1 /* Virtual File System: SFTP file system.
4 Copyright (C) 2011-2024
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 #ifdef HAVE_SYS_SELECT_H
31 #include <sys/select.h>
34 #include <sys/types.h>
38 #include "lib/global.h"
40 #include "lib/vfs/utilvfs.h"
44 /*** global variables ****************************************************************************/
46 GString
*sftpfs_filename_buffer
= NULL
;
48 /*** file scope macro definitions ****************************************************************/
50 /*** file scope type declarations ****************************************************************/
52 /*** forward declarations (file scope functions) *************************************************/
54 /*** file scope variables ************************************************************************/
56 /* --------------------------------------------------------------------------------------------- */
57 /*** file scope functions ************************************************************************/
58 /* --------------------------------------------------------------------------------------------- */
60 /* Adjust block size and number of blocks */
63 sftpfs_blksize (struct stat
*s
)
65 #ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
66 s
->st_blksize
= LIBSSH2_CHANNEL_WINDOW_DEFAULT
; /* FIXME */
67 #endif /* HAVE_STRUCT_STAT_ST_BLKSIZE */
71 /* --------------------------------------------------------------------------------------------- */
73 * Awaiting for any activity on socket.
75 * @param super extra data for SFTP connection
76 * @param mcerror pointer to the error object
77 * @return 0 if success, negative value otherwise
81 sftpfs_internal_waitsocket (sftpfs_super_t
*super
, GError
**mcerror
)
83 struct timeval timeout
= { 10, 0 };
85 fd_set
*writefd
= NULL
;
86 fd_set
*readfd
= NULL
;
89 mc_return_val_if_error (mcerror
, -1);
92 FD_SET (super
->socket_handle
, &fd
);
94 /* now make sure we wait in the correct direction */
95 dir
= libssh2_session_block_directions (super
->session
);
97 if ((dir
& LIBSSH2_SESSION_BLOCK_INBOUND
) != 0)
100 if ((dir
& LIBSSH2_SESSION_BLOCK_OUTBOUND
) != 0)
103 ret
= select (super
->socket_handle
+ 1, readfd
, writefd
, NULL
, &timeout
);
106 int my_errno
= errno
;
108 mc_propagate_error (mcerror
, my_errno
, _("sftp: socket error: %s"),
109 unix_error_string (my_errno
));
115 /* --------------------------------------------------------------------------------------------- */
118 sftpfs_stat_init (sftpfs_super_t
**super
, const vfs_path_element_t
**path_element
,
119 const vfs_path_t
*vpath
, GError
**mcerror
, int stat_type
,
120 LIBSSH2_SFTP_ATTRIBUTES
*attrs
)
122 const GString
*fixfname
;
125 if (!sftpfs_op_init (super
, path_element
, vpath
, mcerror
))
128 fixfname
= sftpfs_fix_filename ((*path_element
)->path
);
132 res
= libssh2_sftp_stat_ex ((*super
)->sftp_session
, fixfname
->str
, fixfname
->len
,
137 if (sftpfs_is_sftp_error ((*super
)->sftp_session
, res
, LIBSSH2_FX_PERMISSION_DENIED
))
140 if (sftpfs_is_sftp_error ((*super
)->sftp_session
, res
, LIBSSH2_FX_NO_SUCH_FILE
))
143 if (!sftpfs_waitsocket (*super
, res
, mcerror
))
146 while (res
== LIBSSH2_ERROR_EAGAIN
);
151 /* --------------------------------------------------------------------------------------------- */
152 /*** public functions ****************************************************************************/
153 /* --------------------------------------------------------------------------------------------- */
156 sftpfs_waitsocket (sftpfs_super_t
*super
, int sftp_res
, GError
**mcerror
)
158 if (sftp_res
!= LIBSSH2_ERROR_EAGAIN
)
160 sftpfs_ssherror_to_gliberror (super
, sftp_res
, mcerror
);
164 sftpfs_internal_waitsocket (super
, mcerror
);
166 return (mcerror
== NULL
|| *mcerror
== NULL
);
169 /* --------------------------------------------------------------------------------------------- */
172 sftpfs_is_sftp_error (LIBSSH2_SFTP
*sftp_session
, int sftp_res
, int sftp_error
)
174 return (sftp_res
== LIBSSH2_ERROR_SFTP_PROTOCOL
&&
175 libssh2_sftp_last_error (sftp_session
) == (unsigned long) sftp_error
);
178 /* --------------------------------------------------------------------------------------------- */
180 * Convert libssh error to GError object.
182 * @param super extra data for SFTP connection
183 * @param libssh_errno errno from libssh
184 * @param mcerror pointer to the error object
188 sftpfs_ssherror_to_gliberror (sftpfs_super_t
*super
, int libssh_errno
, GError
**mcerror
)
193 mc_return_if_error (mcerror
);
195 libssh2_session_last_error (super
->session
, &err
, &err_len
, 1);
196 if (libssh_errno
== LIBSSH2_ERROR_SFTP_PROTOCOL
&& super
->sftp_session
!= NULL
)
197 mc_propagate_error (mcerror
, libssh_errno
, "%s %lu", err
,
198 libssh2_sftp_last_error (super
->sftp_session
));
200 mc_propagate_error (mcerror
, libssh_errno
, "%s", err
);
204 /* --------------------------------------------------------------------------------------------- */
206 * Fix filename for SFTP operations: add leading slash to file name.
208 * @param file_name file name
209 * @param length length of returned string
211 * @return pointer to string that contains the file name with leading slash
215 sftpfs_fix_filename (const char *file_name
)
217 g_string_printf (sftpfs_filename_buffer
, "%c%s", PATH_SEP
, file_name
);
218 return sftpfs_filename_buffer
;
221 /* --------------------------------------------------------------------------------------------- */
224 sftpfs_op_init (sftpfs_super_t
**super
, const vfs_path_element_t
**path_element
,
225 const vfs_path_t
*vpath
, GError
**mcerror
)
227 struct vfs_s_super
*lc_super
= NULL
;
229 mc_return_val_if_error (mcerror
, FALSE
);
231 if (vfs_s_get_path (vpath
, &lc_super
, 0) == NULL
)
234 if (lc_super
== NULL
)
237 *super
= SFTP_SUPER (lc_super
);
238 if ((*super
)->sftp_session
== NULL
)
241 *path_element
= vfs_path_get_by_index (vpath
, -1);
246 /* --------------------------------------------------------------------------------------------- */
249 sftpfs_attr_to_stat (const LIBSSH2_SFTP_ATTRIBUTES
*attrs
, struct stat
*s
)
251 if ((attrs
->flags
& LIBSSH2_SFTP_ATTR_UIDGID
) != 0)
253 s
->st_uid
= attrs
->uid
;
254 s
->st_gid
= attrs
->gid
;
257 if ((attrs
->flags
& LIBSSH2_SFTP_ATTR_ACMODTIME
) != 0)
259 vfs_zero_stat_times (s
);
260 s
->st_atime
= attrs
->atime
;
261 s
->st_mtime
= attrs
->mtime
;
262 s
->st_ctime
= attrs
->mtime
;
265 if ((attrs
->flags
& LIBSSH2_SFTP_ATTR_SIZE
) != 0)
267 s
->st_size
= attrs
->filesize
;
271 if ((attrs
->flags
& LIBSSH2_SFTP_ATTR_PERMISSIONS
) != 0)
272 s
->st_mode
= attrs
->permissions
;
275 /* --------------------------------------------------------------------------------------------- */
277 * Getting information about a symbolic link.
279 * @param vpath path to file, directory or symbolic link
280 * @param buf buffer for store stat-info
281 * @param mcerror pointer to error object
282 * @return 0 if success, negative value otherwise
286 sftpfs_lstat (const vfs_path_t
*vpath
, struct stat
*buf
, GError
**mcerror
)
288 sftpfs_super_t
*super
= NULL
;
289 const vfs_path_element_t
*path_element
= NULL
;
290 LIBSSH2_SFTP_ATTRIBUTES attrs
;
293 res
= sftpfs_stat_init (&super
, &path_element
, vpath
, mcerror
, LIBSSH2_SFTP_LSTAT
, &attrs
);
296 sftpfs_attr_to_stat (&attrs
, buf
);
303 /* --------------------------------------------------------------------------------------------- */
305 * Getting information about a file or directory.
307 * @param vpath path to file or directory
308 * @param buf buffer for store stat-info
309 * @param mcerror pointer to error object
310 * @return 0 if success, negative value otherwise
314 sftpfs_stat (const vfs_path_t
*vpath
, struct stat
*buf
, GError
**mcerror
)
316 sftpfs_super_t
*super
= NULL
;
317 const vfs_path_element_t
*path_element
= NULL
;
318 LIBSSH2_SFTP_ATTRIBUTES attrs
;
321 res
= sftpfs_stat_init (&super
, &path_element
, vpath
, mcerror
, LIBSSH2_SFTP_STAT
, &attrs
);
325 sftpfs_attr_to_stat (&attrs
, buf
);
332 /* --------------------------------------------------------------------------------------------- */
334 * Read value of a symbolic link.
336 * @param vpath path to file or directory
337 * @param buf buffer for store stat-info
338 * @param size buffer size
339 * @param mcerror pointer to error object
340 * @return 0 if success, negative value otherwise
344 sftpfs_readlink (const vfs_path_t
*vpath
, char *buf
, size_t size
, GError
**mcerror
)
346 sftpfs_super_t
*super
= NULL
;
347 const vfs_path_element_t
*path_element
= NULL
;
348 const GString
*fixfname
;
351 if (!sftpfs_op_init (&super
, &path_element
, vpath
, mcerror
))
354 fixfname
= sftpfs_fix_filename (path_element
->path
);
359 libssh2_sftp_symlink_ex (super
->sftp_session
, fixfname
->str
, fixfname
->len
, buf
, size
,
360 LIBSSH2_SFTP_READLINK
);
364 if (!sftpfs_waitsocket (super
, res
, mcerror
))
367 while (res
== LIBSSH2_ERROR_EAGAIN
);
372 /* --------------------------------------------------------------------------------------------- */
374 * Create symlink to file or directory
376 * @param vpath1 path to file or directory
377 * @param vpath2 path to symlink
378 * @param mcerror pointer to error object
379 * @return 0 if success, negative value otherwise
383 sftpfs_symlink (const vfs_path_t
*vpath1
, const vfs_path_t
*vpath2
, GError
**mcerror
)
385 sftpfs_super_t
*super
= NULL
;
386 const vfs_path_element_t
*path_element2
= NULL
;
389 const GString
*ctmp_path
;
391 unsigned int tmp_path_len
;
394 if (!sftpfs_op_init (&super
, &path_element2
, vpath2
, mcerror
))
397 ctmp_path
= sftpfs_fix_filename (path_element2
->path
);
398 tmp_path
= g_strndup (ctmp_path
->str
, ctmp_path
->len
);
399 tmp_path_len
= ctmp_path
->len
;
401 path1
= vfs_path_get_last_path_str (vpath1
);
402 path1_len
= strlen (path1
);
407 libssh2_sftp_symlink_ex (super
->sftp_session
, path1
, path1_len
, tmp_path
, tmp_path_len
,
408 LIBSSH2_SFTP_SYMLINK
);
412 if (!sftpfs_waitsocket (super
, res
, mcerror
))
418 while (res
== LIBSSH2_ERROR_EAGAIN
);
424 /* --------------------------------------------------------------------------------------------- */
426 * Changes the times of the file.
428 * @param vpath path to file or directory
429 * @param atime access time
430 * @param mtime modification time
431 * @param mcerror pointer to error object
432 * @return 0 if success, negative value otherwise
436 sftpfs_utime (const vfs_path_t
*vpath
, time_t atime
, time_t mtime
, GError
**mcerror
)
438 sftpfs_super_t
*super
= NULL
;
439 const vfs_path_element_t
*path_element
= NULL
;
440 LIBSSH2_SFTP_ATTRIBUTES attrs
;
441 const GString
*fixfname
;
444 res
= sftpfs_stat_init (&super
, &path_element
, vpath
, mcerror
, LIBSSH2_SFTP_LSTAT
, &attrs
);
448 attrs
.flags
= LIBSSH2_SFTP_ATTR_ACMODTIME
;
452 fixfname
= sftpfs_fix_filename (path_element
->path
);
457 libssh2_sftp_stat_ex (super
->sftp_session
, fixfname
->str
, fixfname
->len
,
458 LIBSSH2_SFTP_SETSTAT
, &attrs
);
462 if (sftpfs_is_sftp_error (super
->sftp_session
, res
, LIBSSH2_FX_NO_SUCH_FILE
))
465 if (sftpfs_is_sftp_error (super
->sftp_session
, res
, LIBSSH2_FX_FAILURE
))
467 res
= 0; /* need something like ftpfs_ignore_chattr_errors */
471 if (!sftpfs_waitsocket (super
, res
, mcerror
))
474 while (res
== LIBSSH2_ERROR_EAGAIN
);
479 /* --------------------------------------------------------------------------------------------- */
481 * Changes the permissions of the file.
483 * @param vpath path to file or directory
484 * @param mode mode (see man 2 open)
485 * @param mcerror pointer to error object
486 * @return 0 if success, negative value otherwise
490 sftpfs_chmod (const vfs_path_t
*vpath
, mode_t mode
, GError
**mcerror
)
492 sftpfs_super_t
*super
= NULL
;
493 const vfs_path_element_t
*path_element
= NULL
;
494 LIBSSH2_SFTP_ATTRIBUTES attrs
;
495 const GString
*fixfname
;
498 res
= sftpfs_stat_init (&super
, &path_element
, vpath
, mcerror
, LIBSSH2_SFTP_LSTAT
, &attrs
);
502 attrs
.flags
= LIBSSH2_SFTP_ATTR_PERMISSIONS
;
503 attrs
.permissions
= mode
;
505 fixfname
= sftpfs_fix_filename (path_element
->path
);
510 libssh2_sftp_stat_ex (super
->sftp_session
, fixfname
->str
, fixfname
->len
,
511 LIBSSH2_SFTP_SETSTAT
, &attrs
);
515 if (sftpfs_is_sftp_error (super
->sftp_session
, res
, LIBSSH2_FX_NO_SUCH_FILE
))
518 if (sftpfs_is_sftp_error (super
->sftp_session
, res
, LIBSSH2_FX_FAILURE
))
520 res
= 0; /* need something like ftpfs_ignore_chattr_errors */
524 if (!sftpfs_waitsocket (super
, res
, mcerror
))
527 while (res
== LIBSSH2_ERROR_EAGAIN
);
532 /* --------------------------------------------------------------------------------------------- */
534 * Delete a name from the file system.
536 * @param vpath path to file or directory
537 * @param mcerror pointer to error object
538 * @return 0 if success, negative value otherwise
542 sftpfs_unlink (const vfs_path_t
*vpath
, GError
**mcerror
)
544 sftpfs_super_t
*super
= NULL
;
545 const vfs_path_element_t
*path_element
= NULL
;
546 const GString
*fixfname
;
549 if (!sftpfs_op_init (&super
, &path_element
, vpath
, mcerror
))
552 fixfname
= sftpfs_fix_filename (path_element
->path
);
556 res
= libssh2_sftp_unlink_ex (super
->sftp_session
, fixfname
->str
, fixfname
->len
);
560 if (!sftpfs_waitsocket (super
, res
, mcerror
))
563 while (res
== LIBSSH2_ERROR_EAGAIN
);
568 /* --------------------------------------------------------------------------------------------- */
570 * Rename a file, moving it between directories if required.
572 * @param vpath1 path to source file or directory
573 * @param vpath2 path to destination file or directory
574 * @param mcerror pointer to error object
575 * @return 0 if success, negative value otherwise
579 sftpfs_rename (const vfs_path_t
*vpath1
, const vfs_path_t
*vpath2
, GError
**mcerror
)
581 sftpfs_super_t
*super
= NULL
;
583 const vfs_path_element_t
*path_element2
= NULL
;
584 const GString
*ctmp_path
;
586 unsigned int tmp_path_len
;
587 const GString
*fixfname
;
590 if (!sftpfs_op_init (&super
, &path_element2
, vpath2
, mcerror
))
593 ctmp_path
= sftpfs_fix_filename (path_element2
->path
);
594 tmp_path
= g_strndup (ctmp_path
->str
, ctmp_path
->len
);
595 tmp_path_len
= ctmp_path
->len
;
597 path1
= vfs_path_get_last_path_str (vpath1
);
598 fixfname
= sftpfs_fix_filename (path1
);
603 libssh2_sftp_rename_ex (super
->sftp_session
, fixfname
->str
, fixfname
->len
, tmp_path
,
604 tmp_path_len
, LIBSSH2_SFTP_SYMLINK
);
608 if (!sftpfs_waitsocket (super
, res
, mcerror
))
614 while (res
== LIBSSH2_ERROR_EAGAIN
);
620 /* --------------------------------------------------------------------------------------------- */