(sftpfs_waitsocket): handle socket errors.
[midnight-commander.git] / src / vfs / sftpfs / internal.c
blobfe95164a222f6749492dbeb557b34068cbf3c47d
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, ret;
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 ret = select (super_data->socket_handle + 1, readfd, writefd, NULL, &timeout);
173 if (ret < 0)
174 mc_propagate_error (mcerror, 0, _("sftp: socket error: %s"), unix_error_string (errno));
176 return ret;
179 /* --------------------------------------------------------------------------------------------- */
181 void
182 sftpfs_attr_to_stat (const LIBSSH2_SFTP_ATTRIBUTES * attrs, struct stat *s)
184 if ((attrs->flags & LIBSSH2_SFTP_ATTR_UIDGID) != 0)
186 s->st_uid = attrs->uid;
187 s->st_gid = attrs->gid;
190 if ((attrs->flags & LIBSSH2_SFTP_ATTR_ACMODTIME) != 0)
192 s->st_atime = attrs->atime;
193 s->st_mtime = attrs->mtime;
194 s->st_ctime = attrs->mtime;
195 #ifdef HAVE_STRUCT_STAT_ST_MTIM
196 s->st_atim.tv_nsec = s->st_mtim.tv_nsec = s->st_ctim.tv_nsec = 0;
197 #endif
200 if ((attrs->flags & LIBSSH2_SFTP_ATTR_SIZE) != 0)
202 s->st_size = attrs->filesize;
203 sftpfs_blksize (s);
206 if ((attrs->flags & LIBSSH2_SFTP_ATTR_PERMISSIONS) != 0)
207 s->st_mode = attrs->permissions;
210 /* --------------------------------------------------------------------------------------------- */
212 * Getting information about a symbolic link.
214 * @param vpath path to file, directory or symbolic link
215 * @param buf buffer for store stat-info
216 * @param mcerror pointer to error object
217 * @return 0 if success, negative value otherwise
221 sftpfs_lstat (const vfs_path_t * vpath, struct stat *buf, GError ** mcerror)
223 struct vfs_s_super *super;
224 sftpfs_super_data_t *super_data;
225 LIBSSH2_SFTP_ATTRIBUTES attrs;
226 int res;
227 const vfs_path_element_t *path_element;
229 mc_return_val_if_error (mcerror, -1);
231 path_element = vfs_path_get_by_index (vpath, -1);
233 if (vfs_s_get_path (vpath, &super, 0) == NULL)
234 return -1;
236 if (super == NULL)
237 return -1;
239 super_data = (sftpfs_super_data_t *) super->data;
240 if (super_data->sftp_session == NULL)
241 return -1;
245 const char *fixfname;
246 unsigned int fixfname_len = 0;
248 fixfname = sftpfs_fix_filename (path_element->path, &fixfname_len);
250 res =
251 libssh2_sftp_stat_ex (super_data->sftp_session, fixfname, fixfname_len,
252 LIBSSH2_SFTP_LSTAT, &attrs);
253 if (res >= 0)
254 break;
256 if (sftpfs_is_sftp_error (super_data->sftp_session, res, LIBSSH2_FX_PERMISSION_DENIED))
257 return -EACCES;
259 /* perhaps the copy function tries to stat destination file
260 to make sure it's not overwriting anything */
261 if (sftpfs_is_sftp_error (super_data->sftp_session, res, LIBSSH2_FX_NO_SUCH_FILE))
262 return -ENOENT;
264 if (!sftpfs_waitsocket_or_error (super_data, res, mcerror, NULL))
265 return -1;
267 while (res == LIBSSH2_ERROR_EAGAIN);
269 sftpfs_attr_to_stat (&attrs, buf);
271 return 0;
274 /* --------------------------------------------------------------------------------------------- */
276 * Getting information about a file or directory.
278 * @param vpath path to file or directory
279 * @param buf buffer for store stat-info
280 * @param mcerror pointer to error object
281 * @return 0 if success, negative value otherwise
285 sftpfs_stat (const vfs_path_t * vpath, struct stat *buf, GError ** mcerror)
287 struct vfs_s_super *super;
288 sftpfs_super_data_t *super_data;
289 LIBSSH2_SFTP_ATTRIBUTES attrs;
290 int res;
291 const vfs_path_element_t *path_element;
293 mc_return_val_if_error (mcerror, -1);
295 path_element = vfs_path_get_by_index (vpath, -1);
297 if (vfs_s_get_path (vpath, &super, 0) == NULL)
298 return -1;
300 if (super == NULL)
301 return -1;
303 super_data = (sftpfs_super_data_t *) super->data;
304 if (super_data->sftp_session == NULL)
305 return -1;
309 const char *fixfname;
310 unsigned int fixfname_len = 0;
312 fixfname = sftpfs_fix_filename (path_element->path, &fixfname_len);
314 res =
315 libssh2_sftp_stat_ex (super_data->sftp_session, fixfname, fixfname_len,
316 LIBSSH2_SFTP_STAT, &attrs);
317 if (res >= 0)
318 break;
320 if (sftpfs_is_sftp_error (super_data->sftp_session, res, LIBSSH2_FX_PERMISSION_DENIED))
321 return -EACCES;
323 /* perhaps the copy function tries to stat destination file
324 to make sure it's not overwriting anything */
325 if (sftpfs_is_sftp_error (super_data->sftp_session, res, LIBSSH2_FX_NO_SUCH_FILE))
326 return -ENOENT;
328 if (!sftpfs_waitsocket_or_error (super_data, res, mcerror, NULL))
329 return -1;
331 while (res == LIBSSH2_ERROR_EAGAIN);
333 buf->st_nlink = 1;
335 sftpfs_attr_to_stat (&attrs, buf);
337 return 0;
340 /* --------------------------------------------------------------------------------------------- */
342 * Read value of a symbolic link.
344 * @param vpath path to file or directory
345 * @param buf buffer for store stat-info
346 * @param size buffer size
347 * @param mcerror pointer to error object
348 * @return 0 if success, negative value otherwise
352 sftpfs_readlink (const vfs_path_t * vpath, char *buf, size_t size, GError ** mcerror)
354 struct vfs_s_super *super;
355 sftpfs_super_data_t *super_data;
356 int res;
357 const vfs_path_element_t *path_element;
359 mc_return_val_if_error (mcerror, -1);
361 path_element = vfs_path_get_by_index (vpath, -1);
363 if (vfs_s_get_path (vpath, &super, 0) == NULL)
364 return -1;
366 if (super == NULL)
367 return -1;
369 super_data = (sftpfs_super_data_t *) super->data;
370 if (super_data->sftp_session == NULL)
371 return -1;
375 const char *fixfname;
376 unsigned int fixfname_len = 0;
378 fixfname = sftpfs_fix_filename (path_element->path, &fixfname_len);
380 res =
381 libssh2_sftp_symlink_ex (super_data->sftp_session, fixfname, fixfname_len, buf, size,
382 LIBSSH2_SFTP_READLINK);
383 if (res >= 0)
384 break;
386 if (!sftpfs_waitsocket_or_error (super_data, res, mcerror, NULL))
387 return -1;
389 while (res == LIBSSH2_ERROR_EAGAIN);
391 return res;
394 /* --------------------------------------------------------------------------------------------- */
396 * Create symlink to file or directory
398 * @param vpath1 path to file or directory
399 * @param vpath2 path to symlink
400 * @param mcerror pointer to error object
401 * @return 0 if success, negative value otherwise
405 sftpfs_symlink (const vfs_path_t * vpath1, const vfs_path_t * vpath2, GError ** mcerror)
407 struct vfs_s_super *super;
408 sftpfs_super_data_t *super_data;
409 const vfs_path_element_t *path_element1;
410 const vfs_path_element_t *path_element2;
411 char *tmp_path;
412 unsigned int tmp_path_len;
413 int res;
415 mc_return_val_if_error (mcerror, -1);
417 path_element2 = vfs_path_get_by_index (vpath2, -1);
419 if (vfs_s_get_path (vpath2, &super, 0) == NULL)
420 return -1;
422 if (super == NULL)
423 return -1;
425 super_data = (sftpfs_super_data_t *) super->data;
426 if (super_data->sftp_session == NULL)
427 return -1;
429 tmp_path = (char *) sftpfs_fix_filename (path_element2->path, &tmp_path_len);
430 tmp_path = g_strndup (tmp_path, tmp_path_len);
432 path_element1 = vfs_path_get_by_index (vpath1, -1);
436 const char *fixfname;
437 unsigned int fixfname_len = 0;
439 fixfname = sftpfs_fix_filename (path_element1->path, &fixfname_len);
441 res =
442 libssh2_sftp_symlink_ex (super_data->sftp_session, fixfname, fixfname_len, tmp_path,
443 tmp_path_len, LIBSSH2_SFTP_SYMLINK);
444 if (res >= 0)
445 break;
447 if (!sftpfs_waitsocket_or_error (super_data, res, mcerror, tmp_path))
448 return -1;
450 while (res == LIBSSH2_ERROR_EAGAIN);
451 g_free (tmp_path);
453 return 0;
456 /* --------------------------------------------------------------------------------------------- */
458 * Changes the permissions of the file.
460 * @param vpath path to file or directory
461 * @param mode mode (see man 2 open)
462 * @param mcerror pointer to error object
463 * @return 0 if success, negative value otherwise
467 sftpfs_chmod (const vfs_path_t * vpath, mode_t mode, GError ** mcerror)
469 struct vfs_s_super *super;
470 sftpfs_super_data_t *super_data;
471 LIBSSH2_SFTP_ATTRIBUTES attrs;
472 int res;
473 const vfs_path_element_t *path_element;
475 mc_return_val_if_error (mcerror, -1);
477 path_element = vfs_path_get_by_index (vpath, -1);
479 if (vfs_s_get_path (vpath, &super, 0) == NULL)
480 return -1;
482 if (super == NULL)
483 return -1;
485 super_data = (sftpfs_super_data_t *) super->data;
486 if (super_data->sftp_session == NULL)
487 return -1;
491 const char *fixfname;
492 unsigned int fixfname_len = 0;
494 fixfname = sftpfs_fix_filename (path_element->path, &fixfname_len);
496 res =
497 libssh2_sftp_stat_ex (super_data->sftp_session, fixfname, fixfname_len,
498 LIBSSH2_SFTP_LSTAT, &attrs);
499 if (res >= 0)
500 break;
502 if (sftpfs_is_sftp_error (super_data->sftp_session, res, LIBSSH2_FX_PERMISSION_DENIED))
503 return -EACCES;
505 if (sftpfs_is_sftp_error (super_data->sftp_session, res, LIBSSH2_FX_FAILURE))
507 res = 0; /* need something like ftpfs_ignore_chattr_errors */
508 break;
511 if (!sftpfs_waitsocket_or_error (super_data, res, mcerror, NULL))
512 return -1;
514 while (res == LIBSSH2_ERROR_EAGAIN);
516 attrs.permissions = mode;
520 const char *fixfname;
521 unsigned int fixfname_len = 0;
523 fixfname = sftpfs_fix_filename (path_element->path, &fixfname_len);
525 res =
526 libssh2_sftp_stat_ex (super_data->sftp_session, fixfname, fixfname_len,
527 LIBSSH2_SFTP_SETSTAT, &attrs);
528 if (res >= 0)
529 break;
531 if (sftpfs_is_sftp_error (super_data->sftp_session, res, LIBSSH2_FX_NO_SUCH_FILE))
532 return -ENOENT;
534 if (sftpfs_is_sftp_error (super_data->sftp_session, res, LIBSSH2_FX_FAILURE))
536 res = 0; /* need something like ftpfs_ignore_chattr_errors */
537 break;
540 if (!sftpfs_waitsocket_or_error (super_data, res, mcerror, NULL))
541 return -1;
543 while (res == LIBSSH2_ERROR_EAGAIN);
545 return res;
548 /* --------------------------------------------------------------------------------------------- */
550 * Delete a name from the file system.
552 * @param vpath path to file or directory
553 * @param mcerror pointer to error object
554 * @return 0 if success, negative value otherwise
558 sftpfs_unlink (const vfs_path_t * vpath, GError ** mcerror)
560 struct vfs_s_super *super;
561 sftpfs_super_data_t *super_data;
562 int res;
563 const vfs_path_element_t *path_element;
565 mc_return_val_if_error (mcerror, -1);
567 path_element = vfs_path_get_by_index (vpath, -1);
569 if (vfs_s_get_path (vpath, &super, 0) == NULL)
570 return -1;
572 if (super == NULL)
573 return -1;
575 super_data = (sftpfs_super_data_t *) super->data;
576 if (super_data->sftp_session == NULL)
577 return -1;
581 const char *fixfname;
582 unsigned int fixfname_len = 0;
584 fixfname = sftpfs_fix_filename (path_element->path, &fixfname_len);
586 res = libssh2_sftp_unlink_ex (super_data->sftp_session, fixfname, fixfname_len);
587 if (res >= 0)
588 break;
590 if (!sftpfs_waitsocket_or_error (super_data, res, mcerror, NULL))
591 return -1;
593 while (res == LIBSSH2_ERROR_EAGAIN);
595 return res;
598 /* --------------------------------------------------------------------------------------------- */
600 * Rename a file, moving it between directories if required.
602 * @param vpath1 path to source file or directory
603 * @param vpath2 path to destination file or directory
604 * @param mcerror pointer to error object
605 * @return 0 if success, negative value otherwise
609 sftpfs_rename (const vfs_path_t * vpath1, const vfs_path_t * vpath2, GError ** mcerror)
611 struct vfs_s_super *super;
612 sftpfs_super_data_t *super_data;
613 const vfs_path_element_t *path_element1;
614 const vfs_path_element_t *path_element2;
615 char *tmp_path;
616 unsigned int tmp_path_len;
617 int res;
619 mc_return_val_if_error (mcerror, -1);
620 path_element2 = vfs_path_get_by_index (vpath2, -1);
622 if (vfs_s_get_path (vpath2, &super, 0) == NULL)
623 return -1;
625 if (super == NULL)
626 return -1;
628 super_data = (sftpfs_super_data_t *) super->data;
629 if (super_data->sftp_session == NULL)
630 return -1;
632 tmp_path = (char *) sftpfs_fix_filename (path_element2->path, &tmp_path_len);
633 tmp_path = g_strndup (tmp_path, tmp_path_len);
635 path_element1 = vfs_path_get_by_index (vpath1, -1);
639 const char *fixfname;
640 unsigned int fixfname_len = 0;
642 fixfname = sftpfs_fix_filename (path_element1->path, &fixfname_len);
644 res =
645 libssh2_sftp_rename_ex (super_data->sftp_session, fixfname, fixfname_len, tmp_path,
646 tmp_path_len, LIBSSH2_SFTP_SYMLINK);
647 if (res >= 0)
648 break;
650 if (!sftpfs_waitsocket_or_error (super_data, res, mcerror, tmp_path))
651 return -1;
653 while (res == LIBSSH2_ERROR_EAGAIN);
654 g_free (tmp_path);
656 return 0;
659 /* --------------------------------------------------------------------------------------------- */