sftpfs: refactoring: move handling of socket errors to separate function.
[midnight-commander.git] / src / vfs / sftpfs / internal.c
blob6647298359fe4199907afac45a0e16b319a261e8
1 /* Virtual File System: SFTP file system.
2 The internal functions
4 Copyright (C) 2011-2017
5 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"
31 #include "lib/util.h"
33 #include "internal.h"
35 /*** global variables ****************************************************************************/
37 GString *sftpfs_filename_buffer = NULL;
39 /*** file scope macro definitions ****************************************************************/
41 /*** file scope type declarations ****************************************************************/
43 /*** file scope variables ************************************************************************/
45 /* --------------------------------------------------------------------------------------------- */
46 /*** file scope functions ************************************************************************/
47 /* --------------------------------------------------------------------------------------------- */
49 static gboolean
50 sftpfs_is_sftp_error (LIBSSH2_SFTP * sftp_session, int sftp_res, int sftp_error)
52 return (sftp_res == LIBSSH2_ERROR_SFTP_PROTOCOL &&
53 libssh2_sftp_last_error (sftp_session) == (unsigned long) sftp_error);
56 /* --------------------------------------------------------------------------------------------- */
58 /* Adjust block size and number of blocks */
60 static void
61 sftpfs_blksize (struct stat *s)
63 #ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
64 s->st_blksize = LIBSSH2_CHANNEL_WINDOW_DEFAULT; /* FIXME */
65 #endif /* HAVE_STRUCT_STAT_ST_BLKSIZE */
66 vfs_adjust_stat (s);
69 /* --------------------------------------------------------------------------------------------- */
71 static gboolean
72 sftpfs_waitsocket_or_error (sftpfs_super_data_t * super_data, int res, GError ** mcerror,
73 void *to_free)
75 if (res != LIBSSH2_ERROR_EAGAIN)
77 sftpfs_ssherror_to_gliberror (super_data, res, mcerror);
78 g_free (to_free);
79 return FALSE;
82 sftpfs_waitsocket (super_data, mcerror);
84 if (mcerror != NULL && *mcerror != NULL)
86 g_free (to_free);
87 return FALSE;
90 return TRUE;
93 /* --------------------------------------------------------------------------------------------- */
94 /*** public functions ****************************************************************************/
95 /* --------------------------------------------------------------------------------------------- */
96 /**
97 * Convert libssh error to GError object.
99 * @param super_data extra data for SFTP connection
100 * @param libssh_errno errno from libssh
101 * @param mcerror pointer to the error object
104 void
105 sftpfs_ssherror_to_gliberror (sftpfs_super_data_t * super_data, int libssh_errno, GError ** mcerror)
107 char *err = NULL;
108 int err_len;
110 mc_return_if_error (mcerror);
112 libssh2_session_last_error (super_data->session, &err, &err_len, 1);
113 if (libssh_errno == LIBSSH2_ERROR_SFTP_PROTOCOL && super_data->sftp_session != NULL)
114 mc_propagate_error (mcerror, libssh_errno, "%s %lu", err,
115 libssh2_sftp_last_error (super_data->sftp_session));
116 else
117 mc_propagate_error (mcerror, libssh_errno, "%s", err);
118 g_free (err);
121 /* --------------------------------------------------------------------------------------------- */
123 * Fix filename for SFTP operations: add leading slash to file name.
125 * @param file_name file name
126 * @param length length of returned string
128 * @return pointer to string that contains the file name with leading slash
131 const char *
132 sftpfs_fix_filename (const char *file_name, unsigned int *length)
134 g_string_printf (sftpfs_filename_buffer, "%c%s", PATH_SEP, file_name);
135 *length = sftpfs_filename_buffer->len;
136 return sftpfs_filename_buffer->str;
139 /* --------------------------------------------------------------------------------------------- */
141 * Awaiting for any activity on socket.
143 * @param super_data extra data for SFTP connection
144 * @param mcerror pointer to the error object
145 * @return 0 if success, negative value otherwise
149 sftpfs_waitsocket (sftpfs_super_data_t * super_data, GError ** mcerror)
151 struct timeval timeout = { 10, 0 };
152 fd_set fd;
153 fd_set *writefd = NULL;
154 fd_set *readfd = NULL;
155 int dir;
157 mc_return_val_if_error (mcerror, -1);
159 FD_ZERO (&fd);
160 FD_SET (super_data->socket_handle, &fd);
162 /* now make sure we wait in the correct direction */
163 dir = libssh2_session_block_directions (super_data->session);
165 if ((dir & LIBSSH2_SESSION_BLOCK_INBOUND) != 0)
166 readfd = &fd;
168 if ((dir & LIBSSH2_SESSION_BLOCK_OUTBOUND) != 0)
169 writefd = &fd;
171 return select (super_data->socket_handle + 1, readfd, writefd, NULL, &timeout);
174 /* --------------------------------------------------------------------------------------------- */
176 void
177 sftpfs_attr_to_stat (const LIBSSH2_SFTP_ATTRIBUTES * attrs, struct stat *s)
179 if ((attrs->flags & LIBSSH2_SFTP_ATTR_UIDGID) != 0)
181 s->st_uid = attrs->uid;
182 s->st_gid = attrs->gid;
185 if ((attrs->flags & LIBSSH2_SFTP_ATTR_ACMODTIME) != 0)
187 s->st_atime = attrs->atime;
188 s->st_mtime = attrs->mtime;
189 s->st_ctime = attrs->mtime;
190 #ifdef HAVE_STRUCT_STAT_ST_MTIM
191 s->st_atim.tv_nsec = s->st_mtim.tv_nsec = s->st_ctim.tv_nsec = 0;
192 #endif
195 if ((attrs->flags & LIBSSH2_SFTP_ATTR_SIZE) != 0)
197 s->st_size = attrs->filesize;
198 sftpfs_blksize (s);
201 if ((attrs->flags & LIBSSH2_SFTP_ATTR_PERMISSIONS) != 0)
202 s->st_mode = attrs->permissions;
205 /* --------------------------------------------------------------------------------------------- */
207 * Getting information about a symbolic link.
209 * @param vpath path to file, directory or symbolic link
210 * @param buf buffer for store stat-info
211 * @param mcerror pointer to error object
212 * @return 0 if success, negative value otherwise
216 sftpfs_lstat (const vfs_path_t * vpath, struct stat *buf, GError ** mcerror)
218 struct vfs_s_super *super;
219 sftpfs_super_data_t *super_data;
220 LIBSSH2_SFTP_ATTRIBUTES attrs;
221 int res;
222 const vfs_path_element_t *path_element;
224 mc_return_val_if_error (mcerror, -1);
226 path_element = vfs_path_get_by_index (vpath, -1);
228 if (vfs_s_get_path (vpath, &super, 0) == NULL)
229 return -1;
231 if (super == NULL)
232 return -1;
234 super_data = (sftpfs_super_data_t *) super->data;
235 if (super_data->sftp_session == NULL)
236 return -1;
240 const char *fixfname;
241 unsigned int fixfname_len = 0;
243 fixfname = sftpfs_fix_filename (path_element->path, &fixfname_len);
245 res =
246 libssh2_sftp_stat_ex (super_data->sftp_session, fixfname, fixfname_len,
247 LIBSSH2_SFTP_LSTAT, &attrs);
248 if (res >= 0)
249 break;
251 if (sftpfs_is_sftp_error (super_data->sftp_session, res, LIBSSH2_FX_PERMISSION_DENIED))
252 return -EACCES;
254 /* perhaps the copy function tries to stat destination file
255 to make sure it's not overwriting anything */
256 if (sftpfs_is_sftp_error (super_data->sftp_session, res, LIBSSH2_FX_NO_SUCH_FILE))
257 return -ENOENT;
259 if (!sftpfs_waitsocket_or_error (super_data, res, mcerror, NULL))
260 return -1;
262 while (res == LIBSSH2_ERROR_EAGAIN);
264 sftpfs_attr_to_stat (&attrs, buf);
266 return 0;
269 /* --------------------------------------------------------------------------------------------- */
271 * Getting information about a file or directory.
273 * @param vpath path to file or directory
274 * @param buf buffer for store stat-info
275 * @param mcerror pointer to error object
276 * @return 0 if success, negative value otherwise
280 sftpfs_stat (const vfs_path_t * vpath, struct stat *buf, GError ** mcerror)
282 struct vfs_s_super *super;
283 sftpfs_super_data_t *super_data;
284 LIBSSH2_SFTP_ATTRIBUTES attrs;
285 int res;
286 const vfs_path_element_t *path_element;
288 mc_return_val_if_error (mcerror, -1);
290 path_element = vfs_path_get_by_index (vpath, -1);
292 if (vfs_s_get_path (vpath, &super, 0) == NULL)
293 return -1;
295 if (super == NULL)
296 return -1;
298 super_data = (sftpfs_super_data_t *) super->data;
299 if (super_data->sftp_session == NULL)
300 return -1;
304 const char *fixfname;
305 unsigned int fixfname_len = 0;
307 fixfname = sftpfs_fix_filename (path_element->path, &fixfname_len);
309 res =
310 libssh2_sftp_stat_ex (super_data->sftp_session, fixfname, fixfname_len,
311 LIBSSH2_SFTP_STAT, &attrs);
312 if (res >= 0)
313 break;
315 if (sftpfs_is_sftp_error (super_data->sftp_session, res, LIBSSH2_FX_PERMISSION_DENIED))
316 return -EACCES;
318 /* perhaps the copy function tries to stat destination file
319 to make sure it's not overwriting anything */
320 if (sftpfs_is_sftp_error (super_data->sftp_session, res, LIBSSH2_FX_NO_SUCH_FILE))
321 return -ENOENT;
323 if (!sftpfs_waitsocket_or_error (super_data, res, mcerror, NULL))
324 return -1;
326 while (res == LIBSSH2_ERROR_EAGAIN);
328 buf->st_nlink = 1;
330 sftpfs_attr_to_stat (&attrs, buf);
332 return 0;
335 /* --------------------------------------------------------------------------------------------- */
337 * Read value of a symbolic link.
339 * @param vpath path to file or directory
340 * @param buf buffer for store stat-info
341 * @param size buffer size
342 * @param mcerror pointer to error object
343 * @return 0 if success, negative value otherwise
347 sftpfs_readlink (const vfs_path_t * vpath, char *buf, size_t size, GError ** mcerror)
349 struct vfs_s_super *super;
350 sftpfs_super_data_t *super_data;
351 int res;
352 const vfs_path_element_t *path_element;
354 mc_return_val_if_error (mcerror, -1);
356 path_element = vfs_path_get_by_index (vpath, -1);
358 if (vfs_s_get_path (vpath, &super, 0) == NULL)
359 return -1;
361 if (super == NULL)
362 return -1;
364 super_data = (sftpfs_super_data_t *) super->data;
365 if (super_data->sftp_session == NULL)
366 return -1;
370 const char *fixfname;
371 unsigned int fixfname_len = 0;
373 fixfname = sftpfs_fix_filename (path_element->path, &fixfname_len);
375 res =
376 libssh2_sftp_symlink_ex (super_data->sftp_session, fixfname, fixfname_len, buf, size,
377 LIBSSH2_SFTP_READLINK);
378 if (res >= 0)
379 break;
381 if (!sftpfs_waitsocket_or_error (super_data, res, mcerror, NULL))
382 return -1;
384 while (res == LIBSSH2_ERROR_EAGAIN);
386 return res;
389 /* --------------------------------------------------------------------------------------------- */
391 * Create symlink to file or directory
393 * @param vpath1 path to file or directory
394 * @param vpath2 path to symlink
395 * @param mcerror pointer to error object
396 * @return 0 if success, negative value otherwise
400 sftpfs_symlink (const vfs_path_t * vpath1, const vfs_path_t * vpath2, GError ** mcerror)
402 struct vfs_s_super *super;
403 sftpfs_super_data_t *super_data;
404 const vfs_path_element_t *path_element1;
405 const vfs_path_element_t *path_element2;
406 char *tmp_path;
407 unsigned int tmp_path_len;
408 int res;
410 mc_return_val_if_error (mcerror, -1);
412 path_element2 = vfs_path_get_by_index (vpath2, -1);
414 if (vfs_s_get_path (vpath2, &super, 0) == NULL)
415 return -1;
417 if (super == NULL)
418 return -1;
420 super_data = (sftpfs_super_data_t *) super->data;
421 if (super_data->sftp_session == NULL)
422 return -1;
424 tmp_path = (char *) sftpfs_fix_filename (path_element2->path, &tmp_path_len);
425 tmp_path = g_strndup (tmp_path, tmp_path_len);
427 path_element1 = vfs_path_get_by_index (vpath1, -1);
431 const char *fixfname;
432 unsigned int fixfname_len = 0;
434 fixfname = sftpfs_fix_filename (path_element1->path, &fixfname_len);
436 res =
437 libssh2_sftp_symlink_ex (super_data->sftp_session, fixfname, fixfname_len, tmp_path,
438 tmp_path_len, LIBSSH2_SFTP_SYMLINK);
439 if (res >= 0)
440 break;
442 if (!sftpfs_waitsocket_or_error (super_data, res, mcerror, tmp_path))
443 return -1;
445 while (res == LIBSSH2_ERROR_EAGAIN);
446 g_free (tmp_path);
448 return 0;
451 /* --------------------------------------------------------------------------------------------- */
453 * Changes the permissions of the file.
455 * @param vpath path to file or directory
456 * @param mode mode (see man 2 open)
457 * @param mcerror pointer to error object
458 * @return 0 if success, negative value otherwise
462 sftpfs_chmod (const vfs_path_t * vpath, mode_t mode, GError ** mcerror)
464 struct vfs_s_super *super;
465 sftpfs_super_data_t *super_data;
466 LIBSSH2_SFTP_ATTRIBUTES attrs;
467 int res;
468 const vfs_path_element_t *path_element;
470 mc_return_val_if_error (mcerror, -1);
472 path_element = vfs_path_get_by_index (vpath, -1);
474 if (vfs_s_get_path (vpath, &super, 0) == NULL)
475 return -1;
477 if (super == NULL)
478 return -1;
480 super_data = (sftpfs_super_data_t *) super->data;
481 if (super_data->sftp_session == NULL)
482 return -1;
486 const char *fixfname;
487 unsigned int fixfname_len = 0;
489 fixfname = sftpfs_fix_filename (path_element->path, &fixfname_len);
491 res =
492 libssh2_sftp_stat_ex (super_data->sftp_session, fixfname, fixfname_len,
493 LIBSSH2_SFTP_LSTAT, &attrs);
494 if (res >= 0)
495 break;
497 if (sftpfs_is_sftp_error (super_data->sftp_session, res, LIBSSH2_FX_PERMISSION_DENIED))
498 return -EACCES;
500 if (sftpfs_is_sftp_error (super_data->sftp_session, res, LIBSSH2_FX_FAILURE))
502 res = 0; /* need something like ftpfs_ignore_chattr_errors */
503 break;
506 if (!sftpfs_waitsocket_or_error (super_data, res, mcerror, NULL))
507 return -1;
509 while (res == LIBSSH2_ERROR_EAGAIN);
511 attrs.permissions = mode;
515 const char *fixfname;
516 unsigned int fixfname_len = 0;
518 fixfname = sftpfs_fix_filename (path_element->path, &fixfname_len);
520 res =
521 libssh2_sftp_stat_ex (super_data->sftp_session, fixfname, fixfname_len,
522 LIBSSH2_SFTP_SETSTAT, &attrs);
523 if (res >= 0)
524 break;
526 if (sftpfs_is_sftp_error (super_data->sftp_session, res, LIBSSH2_FX_NO_SUCH_FILE))
527 return -ENOENT;
529 if (sftpfs_is_sftp_error (super_data->sftp_session, res, LIBSSH2_FX_FAILURE))
531 res = 0; /* need something like ftpfs_ignore_chattr_errors */
532 break;
535 if (!sftpfs_waitsocket_or_error (super_data, res, mcerror, NULL))
536 return -1;
538 while (res == LIBSSH2_ERROR_EAGAIN);
540 return res;
543 /* --------------------------------------------------------------------------------------------- */
545 * Delete a name from the file system.
547 * @param vpath path to file or directory
548 * @param mcerror pointer to error object
549 * @return 0 if success, negative value otherwise
553 sftpfs_unlink (const vfs_path_t * vpath, GError ** mcerror)
555 struct vfs_s_super *super;
556 sftpfs_super_data_t *super_data;
557 int res;
558 const vfs_path_element_t *path_element;
560 mc_return_val_if_error (mcerror, -1);
562 path_element = vfs_path_get_by_index (vpath, -1);
564 if (vfs_s_get_path (vpath, &super, 0) == NULL)
565 return -1;
567 if (super == NULL)
568 return -1;
570 super_data = (sftpfs_super_data_t *) super->data;
571 if (super_data->sftp_session == NULL)
572 return -1;
576 const char *fixfname;
577 unsigned int fixfname_len = 0;
579 fixfname = sftpfs_fix_filename (path_element->path, &fixfname_len);
581 res = libssh2_sftp_unlink_ex (super_data->sftp_session, fixfname, fixfname_len);
582 if (res >= 0)
583 break;
585 if (!sftpfs_waitsocket_or_error (super_data, res, mcerror, NULL))
586 return -1;
588 while (res == LIBSSH2_ERROR_EAGAIN);
590 return res;
593 /* --------------------------------------------------------------------------------------------- */
595 * Rename a file, moving it between directories if required.
597 * @param vpath1 path to source file or directory
598 * @param vpath2 path to destination file or directory
599 * @param mcerror pointer to error object
600 * @return 0 if success, negative value otherwise
604 sftpfs_rename (const vfs_path_t * vpath1, const vfs_path_t * vpath2, GError ** mcerror)
606 struct vfs_s_super *super;
607 sftpfs_super_data_t *super_data;
608 const vfs_path_element_t *path_element1;
609 const vfs_path_element_t *path_element2;
610 char *tmp_path;
611 unsigned int tmp_path_len;
612 int res;
614 mc_return_val_if_error (mcerror, -1);
615 path_element2 = vfs_path_get_by_index (vpath2, -1);
617 if (vfs_s_get_path (vpath2, &super, 0) == NULL)
618 return -1;
620 if (super == NULL)
621 return -1;
623 super_data = (sftpfs_super_data_t *) super->data;
624 if (super_data->sftp_session == NULL)
625 return -1;
627 tmp_path = (char *) sftpfs_fix_filename (path_element2->path, &tmp_path_len);
628 tmp_path = g_strndup (tmp_path, tmp_path_len);
630 path_element1 = vfs_path_get_by_index (vpath1, -1);
634 const char *fixfname;
635 unsigned int fixfname_len = 0;
637 fixfname = sftpfs_fix_filename (path_element1->path, &fixfname_len);
639 res =
640 libssh2_sftp_rename_ex (super_data->sftp_session, fixfname, fixfname_len, tmp_path,
641 tmp_path_len, LIBSSH2_SFTP_SYMLINK);
642 if (res >= 0)
643 break;
645 if (!sftpfs_waitsocket_or_error (super_data, res, mcerror, tmp_path))
646 return -1;
648 while (res == LIBSSH2_ERROR_EAGAIN);
649 g_free (tmp_path);
651 return 0;
654 /* --------------------------------------------------------------------------------------------- */