Code refactoring in tests.
[midnight-commander.git] / src / vfs / sftpfs / internal.c
blob856f88ecd9d732451d55a6c7311139cb77b72348
1 /* Virtual File System: SFTP file system.
2 The internal functions
4 Copyright (C) 2011
5 The Free Software Foundation, Inc.
7 Written by:
8 Ilia Maslakov <il.smind@gmail.com>, 2011
9 Slava Zanko <slavazanko@gmail.com>, 2011, 2012
11 This file is part of the Midnight Commander.
13 The Midnight Commander is free software: you can redistribute it
14 and/or modify it under the terms of the GNU General Public License as
15 published by the Free Software Foundation, either version 3 of the License,
16 or (at your option) any later version.
18 The Midnight Commander is distributed in the hope that it will be useful,
19 but WITHOUT ANY WARRANTY; without even the implied warranty of
20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 GNU General Public License for more details.
23 You should have received a copy of the GNU General Public License
24 along with this program. If not, see <http://www.gnu.org/licenses/>.
27 #include <config.h>
28 #include <errno.h>
30 #include "lib/global.h"
32 #include "internal.h"
34 /*** global variables ****************************************************************************/
36 GString *sftpfs_filename_buffer = NULL;
38 /*** file scope macro definitions ****************************************************************/
40 /*** file scope type declarations ****************************************************************/
42 /*** file scope variables ************************************************************************/
44 /*** file scope functions ************************************************************************/
45 /* --------------------------------------------------------------------------------------------- */
47 /* --------------------------------------------------------------------------------------------- */
48 /*** public functions ****************************************************************************/
49 /* --------------------------------------------------------------------------------------------- */
50 /**
51 * Show error message (if error have raised) and cleanup GError object.
53 * @param error pointer to object contains error message
54 * @return TRUE if error message was printed, FALSE otherwise
57 gboolean
58 sftpfs_show_error (GError ** error)
60 if (error == NULL || *error == NULL)
61 return FALSE;
63 vfs_print_message ("(%d) %s", (*error)->code, (*error)->message);
64 g_error_free (*error);
65 *error = NULL;
66 return TRUE;
69 /* --------------------------------------------------------------------------------------------- */
70 /**
71 * Convert libssh error to GError object.
73 * @param super_data extra data for SFTP connection
74 * @param libssh_errno errno from libssh
75 * @param error pointer to the error object
78 void
79 sftpfs_ssherror_to_gliberror (sftpfs_super_data_t * super_data, int libssh_errno, GError ** error)
81 char *err = NULL;
82 int err_len;
84 g_return_if_fail (error == NULL || *error == NULL);
86 libssh2_session_last_error (super_data->session, &err, &err_len, 1);
87 g_set_error (error, MC_ERROR, libssh_errno, "%s", err);
88 g_free (err);
91 /* --------------------------------------------------------------------------------------------- */
92 /**
93 * Fix filename for SFTP operations: add leading slash to file name.
95 * @param file_name file name
96 * @return newly allocated string contains the file name with leading slash
99 const char *
100 sftpfs_fix_filename (const char *file_name)
102 g_string_printf (sftpfs_filename_buffer, "%c%s", PATH_SEP, file_name);
103 return sftpfs_filename_buffer->str;
106 /* --------------------------------------------------------------------------------------------- */
108 * Awaiting for any activity on socket.
110 * @param super_data extra data for SFTP connection
111 * @param error unused
112 * @return 0 if sucess, negative value otherwise
116 sftpfs_waitsocket (sftpfs_super_data_t * super_data, GError ** error)
118 struct timeval timeout = { 10, 0 };
119 fd_set fd;
120 fd_set *writefd = NULL;
121 fd_set *readfd = NULL;
122 int dir;
124 (void) error;
126 FD_ZERO (&fd);
127 FD_SET (super_data->socket_handle, &fd);
129 /* now make sure we wait in the correct direction */
130 dir = libssh2_session_block_directions (super_data->session);
132 if ((dir & LIBSSH2_SESSION_BLOCK_INBOUND) != 0)
133 readfd = &fd;
135 if ((dir & LIBSSH2_SESSION_BLOCK_OUTBOUND) != 0)
136 writefd = &fd;
138 return select (super_data->socket_handle + 1, readfd, writefd, NULL, &timeout);
141 /* --------------------------------------------------------------------------------------------- */
143 * Getting information about a symbolic link.
145 * @param vpath path to file, directory or symbolic link
146 * @param buf buffer for store stat-info
147 * @param error pointer to error object
148 * @return 0 if sucess, negative value otherwise
152 sftpfs_lstat (const vfs_path_t * vpath, struct stat *buf, GError ** error)
154 struct vfs_s_super *super;
155 sftpfs_super_data_t *super_data;
156 LIBSSH2_SFTP_ATTRIBUTES attrs;
157 int res;
158 const vfs_path_element_t *path_element;
160 path_element = vfs_path_get_by_index (vpath, -1);
162 if (vfs_s_get_path (vpath, &super, 0) == NULL)
163 return -1;
165 if (super == NULL)
166 return -1;
168 super_data = (sftpfs_super_data_t *) super->data;
169 if (super_data->sftp_session == NULL)
170 return -1;
174 const char *fixfname;
176 fixfname = sftpfs_fix_filename (path_element->path);
178 res = libssh2_sftp_stat_ex (super_data->sftp_session, fixfname,
179 sftpfs_filename_buffer->len, LIBSSH2_SFTP_LSTAT, &attrs);
180 if (res >= 0)
181 break;
183 if (res != LIBSSH2_ERROR_EAGAIN)
185 sftpfs_ssherror_to_gliberror (super_data, res, error);
186 return -1;
189 sftpfs_waitsocket (super_data, error);
190 if (error != NULL && *error != NULL)
191 return -1;
193 while (res == LIBSSH2_ERROR_EAGAIN);
195 if ((attrs.flags & LIBSSH2_SFTP_ATTR_UIDGID) != 0)
197 buf->st_uid = attrs.uid;
198 buf->st_gid = attrs.gid;
201 if ((attrs.flags & LIBSSH2_SFTP_ATTR_ACMODTIME) != 0)
203 buf->st_atime = attrs.atime;
204 buf->st_mtime = attrs.mtime;
205 buf->st_ctime = attrs.mtime;
208 if ((attrs.flags & LIBSSH2_SFTP_ATTR_SIZE) != 0)
209 buf->st_size = attrs.filesize;
211 if ((attrs.flags & LIBSSH2_SFTP_ATTR_PERMISSIONS) != 0)
212 buf->st_mode = attrs.permissions;
214 return 0;
217 /* --------------------------------------------------------------------------------------------- */
219 * Getting information about a file or directory.
221 * @param vpath path to file or directory
222 * @param buf buffer for store stat-info
223 * @param error pointer to error object
224 * @return 0 if sucess, negative value otherwise
228 sftpfs_stat (const vfs_path_t * vpath, struct stat *buf, GError ** error)
230 struct vfs_s_super *super;
231 sftpfs_super_data_t *super_data;
232 LIBSSH2_SFTP_ATTRIBUTES attrs;
233 int res;
234 const vfs_path_element_t *path_element;
236 path_element = vfs_path_get_by_index (vpath, -1);
238 if (vfs_s_get_path (vpath, &super, 0) == NULL)
239 return -1;
241 if (super == NULL)
242 return -1;
244 super_data = (sftpfs_super_data_t *) super->data;
245 if (super_data->sftp_session == NULL)
246 return -1;
250 const char *fixfname;
252 fixfname = sftpfs_fix_filename (path_element->path);
254 res =
255 libssh2_sftp_stat_ex (super_data->sftp_session,
256 fixfname, sftpfs_filename_buffer->len, LIBSSH2_SFTP_STAT, &attrs);
257 if (res >= 0)
258 break;
260 if (res != LIBSSH2_ERROR_EAGAIN)
262 sftpfs_ssherror_to_gliberror (super_data, res, error);
263 return -1;
266 sftpfs_waitsocket (super_data, error);
267 if (error != NULL && *error != NULL)
268 return -1;
270 while (res == LIBSSH2_ERROR_EAGAIN);
272 buf->st_nlink = 1;
273 if ((attrs.flags & LIBSSH2_SFTP_ATTR_UIDGID) != 0)
275 buf->st_uid = attrs.uid;
276 buf->st_gid = attrs.gid;
279 if ((attrs.flags & LIBSSH2_SFTP_ATTR_ACMODTIME) != 0)
281 buf->st_atime = attrs.atime;
282 buf->st_mtime = attrs.mtime;
283 buf->st_ctime = attrs.mtime;
286 if ((attrs.flags & LIBSSH2_SFTP_ATTR_SIZE) != 0)
287 buf->st_size = attrs.filesize;
289 if ((attrs.flags & LIBSSH2_SFTP_ATTR_PERMISSIONS) != 0)
290 buf->st_mode = attrs.permissions;
292 return 0;
295 /* --------------------------------------------------------------------------------------------- */
297 * Read value of a symbolic link.
299 * @param vpath path to file or directory
300 * @param buf buffer for store stat-info
301 * @param size buffer size
302 * @param error pointer to error object
303 * @return 0 if sucess, negative value otherwise
307 sftpfs_readlink (const vfs_path_t * vpath, char *buf, size_t size, GError ** error)
309 struct vfs_s_super *super;
310 sftpfs_super_data_t *super_data;
311 int res;
312 const vfs_path_element_t *path_element;
314 path_element = vfs_path_get_by_index (vpath, -1);
316 if (vfs_s_get_path (vpath, &super, 0) == NULL)
317 return -1;
319 if (super == NULL)
320 return -1;
322 super_data = (sftpfs_super_data_t *) super->data;
323 if (super_data->sftp_session == NULL)
324 return -1;
328 const char *fixfname;
330 fixfname = sftpfs_fix_filename (path_element->path);
332 res = libssh2_sftp_readlink (super_data->sftp_session, fixfname, buf, size);
333 if (res >= 0)
334 break;
336 if (res != LIBSSH2_ERROR_EAGAIN)
338 sftpfs_ssherror_to_gliberror (super_data, res, error);
339 return -1;
342 sftpfs_waitsocket (super_data, error);
343 if (error != NULL && *error != NULL)
344 return -1;
346 while (res == LIBSSH2_ERROR_EAGAIN);
348 return res;
351 /* --------------------------------------------------------------------------------------------- */
353 * Create symlink to file or directory
355 * @param vpath1 path to file or directory
356 * @param vpath2 path to symlink
357 * @param error pointer to error object
358 * @return 0 if sucess, negative value otherwise
362 sftpfs_symlink (const vfs_path_t * vpath1, const vfs_path_t * vpath2, GError ** error)
364 struct vfs_s_super *super;
365 sftpfs_super_data_t *super_data;
366 const vfs_path_element_t *path_element1;
367 const vfs_path_element_t *path_element2;
368 char *tmp_path;
369 int res;
371 path_element2 = vfs_path_get_by_index (vpath2, -1);
373 if (vfs_s_get_path (vpath2, &super, 0) == NULL)
374 return -1;
376 if (super == NULL)
377 return -1;
379 super_data = (sftpfs_super_data_t *) super->data;
380 if (super_data->sftp_session == NULL)
381 return -1;
383 tmp_path = g_strdup_printf ("%c%s", PATH_SEP, path_element2->path);
384 path_element1 = vfs_path_get_by_index (vpath1, -1);
388 const char *fixfname;
390 fixfname = sftpfs_fix_filename (path_element1->path);
392 res =
393 libssh2_sftp_symlink_ex (super_data->sftp_session,
394 fixfname,
395 sftpfs_filename_buffer->len, tmp_path, strlen (tmp_path),
396 LIBSSH2_SFTP_SYMLINK);
397 if (res >= 0)
398 break;
400 if (res != LIBSSH2_ERROR_EAGAIN)
402 sftpfs_ssherror_to_gliberror (super_data, res, error);
403 g_free (tmp_path);
404 return -1;
407 sftpfs_waitsocket (super_data, error);
408 if (error != NULL && *error != NULL)
410 g_free (tmp_path);
411 return -1;
414 while (res == LIBSSH2_ERROR_EAGAIN);
415 g_free (tmp_path);
417 return 0;
420 /* --------------------------------------------------------------------------------------------- */
422 * Changes the permissions of the file.
424 * @param vpath path to file or directory
425 * @param mode mode (see man 2 open)
426 * @param error pointer to error object
427 * @return 0 if sucess, negative value otherwise
431 sftpfs_chmod (const vfs_path_t * vpath, mode_t mode, GError ** error)
433 struct vfs_s_super *super;
434 sftpfs_super_data_t *super_data;
435 LIBSSH2_SFTP_ATTRIBUTES attrs;
436 int res;
437 const vfs_path_element_t *path_element;
439 path_element = vfs_path_get_by_index (vpath, -1);
441 if (vfs_s_get_path (vpath, &super, 0) == NULL)
442 return -1;
444 if (super == NULL)
445 return -1;
447 super_data = (sftpfs_super_data_t *) super->data;
448 if (super_data->sftp_session == NULL)
449 return -1;
453 const char *fixfname;
455 fixfname = sftpfs_fix_filename (path_element->path);
457 res = libssh2_sftp_stat_ex (super_data->sftp_session, fixfname,
458 sftpfs_filename_buffer->len, LIBSSH2_SFTP_LSTAT, &attrs);
459 if (res >= 0)
460 break;
462 if (res != LIBSSH2_ERROR_EAGAIN)
464 sftpfs_ssherror_to_gliberror (super_data, res, error);
465 return -1;
468 sftpfs_waitsocket (super_data, error);
469 if (error != NULL && *error != NULL)
470 return -1;
472 while (res == LIBSSH2_ERROR_EAGAIN);
474 attrs.permissions = mode;
478 const char *fixfname;
480 fixfname = sftpfs_fix_filename (path_element->path);
482 res = libssh2_sftp_stat_ex (super_data->sftp_session, fixfname,
483 sftpfs_filename_buffer->len, LIBSSH2_SFTP_SETSTAT, &attrs);
484 if (res >= 0)
485 break;
486 else if (res != LIBSSH2_ERROR_EAGAIN)
488 sftpfs_ssherror_to_gliberror (super_data, res, error);
489 return -1;
492 sftpfs_waitsocket (super_data, error);
493 if (error != NULL && *error != NULL)
494 return -1;
496 while (res == LIBSSH2_ERROR_EAGAIN);
497 return res;
500 /* --------------------------------------------------------------------------------------------- */
502 * Delete a name from the file system.
504 * @param vpath path to file or directory
505 * @param error pointer to error object
506 * @return 0 if sucess, negative value otherwise
510 sftpfs_unlink (const vfs_path_t * vpath, GError ** error)
512 struct vfs_s_super *super;
513 sftpfs_super_data_t *super_data;
514 int res;
515 const vfs_path_element_t *path_element;
517 path_element = vfs_path_get_by_index (vpath, -1);
519 if (vfs_s_get_path (vpath, &super, 0) == NULL)
520 return -1;
522 if (super == NULL)
523 return -1;
525 super_data = (sftpfs_super_data_t *) super->data;
526 if (super_data->sftp_session == NULL)
527 return -1;
531 const char *fixfname;
533 fixfname = sftpfs_fix_filename (path_element->path);
535 res =
536 libssh2_sftp_unlink_ex (super_data->sftp_session, fixfname,
537 sftpfs_filename_buffer->len);
538 if (res >= 0)
539 break;
541 if (res != LIBSSH2_ERROR_EAGAIN)
543 sftpfs_ssherror_to_gliberror (super_data, res, error);
544 return -1;
547 sftpfs_waitsocket (super_data, error);
548 if (error != NULL && *error != NULL)
549 return -1;
551 while (res == LIBSSH2_ERROR_EAGAIN);
553 return res;
556 /* --------------------------------------------------------------------------------------------- */
558 * Rename a file, moving it between directories if required.
560 * @param vpath1 path to source file or directory
561 * @param vpath2 path to destination file or directory
562 * @param error pointer to error object
563 * @return 0 if sucess, negative value otherwise
567 sftpfs_rename (const vfs_path_t * vpath1, const vfs_path_t * vpath2, GError ** error)
569 struct vfs_s_super *super;
570 sftpfs_super_data_t *super_data;
571 const vfs_path_element_t *path_element1;
572 const vfs_path_element_t *path_element2;
573 char *tmp_path;
574 int res;
576 path_element2 = vfs_path_get_by_index (vpath2, -1);
578 if (vfs_s_get_path (vpath2, &super, 0) == NULL)
579 return -1;
581 if (super == NULL)
582 return -1;
584 super_data = (sftpfs_super_data_t *) super->data;
585 if (super_data->sftp_session == NULL)
586 return -1;
588 tmp_path = g_strdup_printf ("%c%s", PATH_SEP, path_element2->path);
589 path_element1 = vfs_path_get_by_index (vpath1, -1);
593 const char *fixfname;
595 fixfname = sftpfs_fix_filename (path_element1->path);
597 res = libssh2_sftp_rename_ex
598 (super_data->sftp_session,
599 fixfname,
600 sftpfs_filename_buffer->len, tmp_path, strlen (tmp_path), LIBSSH2_SFTP_SYMLINK);
601 if (res >= 0)
602 break;
604 if (res != LIBSSH2_ERROR_EAGAIN)
606 sftpfs_ssherror_to_gliberror (super_data, res, error);
607 g_free (tmp_path);
608 return -1;
611 sftpfs_waitsocket (super_data, error);
612 if (error != NULL && *error != NULL)
614 g_free (tmp_path);
615 return -1;
618 while (res == LIBSSH2_ERROR_EAGAIN);
619 g_free (tmp_path);
621 return 0;
624 /* --------------------------------------------------------------------------------------------- */