1 /* Virtual File System: SFTP file system.
4 Copyright (C) 2011-2017
5 Free Software Foundation, Inc.
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/>.
30 #include "lib/global.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 /* --------------------------------------------------------------------------------------------- */
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 */
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 */
69 /* --------------------------------------------------------------------------------------------- */
72 sftpfs_waitsocket_or_error (sftpfs_super_data_t
* super_data
, int res
, GError
** mcerror
,
75 if (res
!= LIBSSH2_ERROR_EAGAIN
)
77 sftpfs_ssherror_to_gliberror (super_data
, res
, mcerror
);
82 sftpfs_waitsocket (super_data
, mcerror
);
84 if (mcerror
!= NULL
&& *mcerror
!= NULL
)
93 /* --------------------------------------------------------------------------------------------- */
94 /*** public functions ****************************************************************************/
95 /* --------------------------------------------------------------------------------------------- */
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
105 sftpfs_ssherror_to_gliberror (sftpfs_super_data_t
* super_data
, int libssh_errno
, GError
** mcerror
)
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
));
117 mc_propagate_error (mcerror
, libssh_errno
, "%s", 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
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 };
153 fd_set
*writefd
= NULL
;
154 fd_set
*readfd
= NULL
;
157 mc_return_val_if_error (mcerror
, -1);
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)
168 if ((dir
& LIBSSH2_SESSION_BLOCK_OUTBOUND
) != 0)
171 ret
= select (super_data
->socket_handle
+ 1, readfd
, writefd
, NULL
, &timeout
);
174 mc_propagate_error (mcerror
, 0, _("sftp: socket error: %s"), unix_error_string (errno
));
179 /* --------------------------------------------------------------------------------------------- */
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;
200 if ((attrs
->flags
& LIBSSH2_SFTP_ATTR_SIZE
) != 0)
202 s
->st_size
= attrs
->filesize
;
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
;
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
)
239 super_data
= (sftpfs_super_data_t
*) super
->data
;
240 if (super_data
->sftp_session
== NULL
)
245 const char *fixfname
;
246 unsigned int fixfname_len
= 0;
248 fixfname
= sftpfs_fix_filename (path_element
->path
, &fixfname_len
);
251 libssh2_sftp_stat_ex (super_data
->sftp_session
, fixfname
, fixfname_len
,
252 LIBSSH2_SFTP_LSTAT
, &attrs
);
256 if (sftpfs_is_sftp_error (super_data
->sftp_session
, res
, LIBSSH2_FX_PERMISSION_DENIED
))
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
))
264 if (!sftpfs_waitsocket_or_error (super_data
, res
, mcerror
, NULL
))
267 while (res
== LIBSSH2_ERROR_EAGAIN
);
269 sftpfs_attr_to_stat (&attrs
, buf
);
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
;
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
)
303 super_data
= (sftpfs_super_data_t
*) super
->data
;
304 if (super_data
->sftp_session
== NULL
)
309 const char *fixfname
;
310 unsigned int fixfname_len
= 0;
312 fixfname
= sftpfs_fix_filename (path_element
->path
, &fixfname_len
);
315 libssh2_sftp_stat_ex (super_data
->sftp_session
, fixfname
, fixfname_len
,
316 LIBSSH2_SFTP_STAT
, &attrs
);
320 if (sftpfs_is_sftp_error (super_data
->sftp_session
, res
, LIBSSH2_FX_PERMISSION_DENIED
))
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
))
328 if (!sftpfs_waitsocket_or_error (super_data
, res
, mcerror
, NULL
))
331 while (res
== LIBSSH2_ERROR_EAGAIN
);
335 sftpfs_attr_to_stat (&attrs
, buf
);
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
;
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
)
369 super_data
= (sftpfs_super_data_t
*) super
->data
;
370 if (super_data
->sftp_session
== NULL
)
375 const char *fixfname
;
376 unsigned int fixfname_len
= 0;
378 fixfname
= sftpfs_fix_filename (path_element
->path
, &fixfname_len
);
381 libssh2_sftp_symlink_ex (super_data
->sftp_session
, fixfname
, fixfname_len
, buf
, size
,
382 LIBSSH2_SFTP_READLINK
);
386 if (!sftpfs_waitsocket_or_error (super_data
, res
, mcerror
, NULL
))
389 while (res
== LIBSSH2_ERROR_EAGAIN
);
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
;
412 unsigned int tmp_path_len
;
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
)
425 super_data
= (sftpfs_super_data_t
*) super
->data
;
426 if (super_data
->sftp_session
== NULL
)
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
);
442 libssh2_sftp_symlink_ex (super_data
->sftp_session
, fixfname
, fixfname_len
, tmp_path
,
443 tmp_path_len
, LIBSSH2_SFTP_SYMLINK
);
447 if (!sftpfs_waitsocket_or_error (super_data
, res
, mcerror
, tmp_path
))
450 while (res
== LIBSSH2_ERROR_EAGAIN
);
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
;
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
)
485 super_data
= (sftpfs_super_data_t
*) super
->data
;
486 if (super_data
->sftp_session
== NULL
)
491 const char *fixfname
;
492 unsigned int fixfname_len
= 0;
494 fixfname
= sftpfs_fix_filename (path_element
->path
, &fixfname_len
);
497 libssh2_sftp_stat_ex (super_data
->sftp_session
, fixfname
, fixfname_len
,
498 LIBSSH2_SFTP_LSTAT
, &attrs
);
502 if (sftpfs_is_sftp_error (super_data
->sftp_session
, res
, LIBSSH2_FX_PERMISSION_DENIED
))
505 if (sftpfs_is_sftp_error (super_data
->sftp_session
, res
, LIBSSH2_FX_FAILURE
))
507 res
= 0; /* need something like ftpfs_ignore_chattr_errors */
511 if (!sftpfs_waitsocket_or_error (super_data
, res
, mcerror
, NULL
))
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
);
526 libssh2_sftp_stat_ex (super_data
->sftp_session
, fixfname
, fixfname_len
,
527 LIBSSH2_SFTP_SETSTAT
, &attrs
);
531 if (sftpfs_is_sftp_error (super_data
->sftp_session
, res
, LIBSSH2_FX_NO_SUCH_FILE
))
534 if (sftpfs_is_sftp_error (super_data
->sftp_session
, res
, LIBSSH2_FX_FAILURE
))
536 res
= 0; /* need something like ftpfs_ignore_chattr_errors */
540 if (!sftpfs_waitsocket_or_error (super_data
, res
, mcerror
, NULL
))
543 while (res
== LIBSSH2_ERROR_EAGAIN
);
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
;
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
)
575 super_data
= (sftpfs_super_data_t
*) super
->data
;
576 if (super_data
->sftp_session
== NULL
)
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
);
590 if (!sftpfs_waitsocket_or_error (super_data
, res
, mcerror
, NULL
))
593 while (res
== LIBSSH2_ERROR_EAGAIN
);
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
;
616 unsigned int tmp_path_len
;
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
)
628 super_data
= (sftpfs_super_data_t
*) super
->data
;
629 if (super_data
->sftp_session
== NULL
)
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
);
645 libssh2_sftp_rename_ex (super_data
->sftp_session
, fixfname
, fixfname_len
, tmp_path
,
646 tmp_path_len
, LIBSSH2_SFTP_SYMLINK
);
650 if (!sftpfs_waitsocket_or_error (super_data
, res
, mcerror
, tmp_path
))
653 while (res
== LIBSSH2_ERROR_EAGAIN
);
659 /* --------------------------------------------------------------------------------------------- */