Merge branch '4583_mc_ext_ini_fix'
[midnight-commander.git] / src / vfs / sftpfs / internal.c
blobb94668d96f040e771a8556982d5b5341b1e1eeb5
1 /* Virtual File System: SFTP file system.
2 The internal functions
4 Copyright (C) 2011-2024
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 #ifdef HAVE_SYS_SELECT_H
31 #include <sys/select.h>
32 #else
33 #include <sys/time.h>
34 #include <sys/types.h>
35 #include <unistd.h>
36 #endif
38 #include "lib/global.h"
39 #include "lib/util.h"
40 #include "lib/vfs/utilvfs.h"
42 #include "internal.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 */
62 static void
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 */
68 vfs_adjust_stat (s);
71 /* --------------------------------------------------------------------------------------------- */
72 /**
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
80 static int
81 sftpfs_internal_waitsocket (sftpfs_super_t *super, GError **mcerror)
83 struct timeval timeout = { 10, 0 };
84 fd_set fd;
85 fd_set *writefd = NULL;
86 fd_set *readfd = NULL;
87 int dir, ret;
89 mc_return_val_if_error (mcerror, -1);
91 FD_ZERO (&fd);
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)
98 readfd = &fd;
100 if ((dir & LIBSSH2_SESSION_BLOCK_OUTBOUND) != 0)
101 writefd = &fd;
103 ret = select (super->socket_handle + 1, readfd, writefd, NULL, &timeout);
104 if (ret < 0)
106 int my_errno = errno;
108 mc_propagate_error (mcerror, my_errno, _("sftp: socket error: %s"),
109 unix_error_string (my_errno));
112 return ret;
115 /* --------------------------------------------------------------------------------------------- */
117 static int
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;
123 int res;
125 if (!sftpfs_op_init (super, path_element, vpath, mcerror))
126 return -1;
128 fixfname = sftpfs_fix_filename ((*path_element)->path);
132 res = libssh2_sftp_stat_ex ((*super)->sftp_session, fixfname->str, fixfname->len,
133 stat_type, attrs);
134 if (res >= 0)
135 break;
137 if (sftpfs_is_sftp_error ((*super)->sftp_session, res, LIBSSH2_FX_PERMISSION_DENIED))
138 return -EACCES;
140 if (sftpfs_is_sftp_error ((*super)->sftp_session, res, LIBSSH2_FX_NO_SUCH_FILE))
141 return -ENOENT;
143 if (!sftpfs_waitsocket (*super, res, mcerror))
144 return -1;
146 while (res == LIBSSH2_ERROR_EAGAIN);
148 return res;
151 /* --------------------------------------------------------------------------------------------- */
152 /*** public functions ****************************************************************************/
153 /* --------------------------------------------------------------------------------------------- */
155 gboolean
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);
161 return FALSE;
164 sftpfs_internal_waitsocket (super, mcerror);
166 return (mcerror == NULL || *mcerror == NULL);
169 /* --------------------------------------------------------------------------------------------- */
171 gboolean
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
187 void
188 sftpfs_ssherror_to_gliberror (sftpfs_super_t *super, int libssh_errno, GError **mcerror)
190 char *err = NULL;
191 int err_len;
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));
199 else
200 mc_propagate_error (mcerror, libssh_errno, "%s", err);
201 g_free (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
214 const GString *
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 /* --------------------------------------------------------------------------------------------- */
223 gboolean
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)
232 return FALSE;
234 if (lc_super == NULL)
235 return FALSE;
237 *super = SFTP_SUPER (lc_super);
238 if ((*super)->sftp_session == NULL)
239 return FALSE;
241 *path_element = vfs_path_get_by_index (vpath, -1);
243 return TRUE;
246 /* --------------------------------------------------------------------------------------------- */
248 void
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;
268 sftpfs_blksize (s);
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;
291 int res;
293 res = sftpfs_stat_init (&super, &path_element, vpath, mcerror, LIBSSH2_SFTP_LSTAT, &attrs);
294 if (res >= 0)
296 sftpfs_attr_to_stat (&attrs, buf);
297 res = 0;
300 return res;
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;
319 int res;
321 res = sftpfs_stat_init (&super, &path_element, vpath, mcerror, LIBSSH2_SFTP_STAT, &attrs);
322 if (res >= 0)
324 buf->st_nlink = 1;
325 sftpfs_attr_to_stat (&attrs, buf);
326 res = 0;
329 return res;
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;
349 int res;
351 if (!sftpfs_op_init (&super, &path_element, vpath, mcerror))
352 return -1;
354 fixfname = sftpfs_fix_filename (path_element->path);
358 res =
359 libssh2_sftp_symlink_ex (super->sftp_session, fixfname->str, fixfname->len, buf, size,
360 LIBSSH2_SFTP_READLINK);
361 if (res >= 0)
362 break;
364 if (!sftpfs_waitsocket (super, res, mcerror))
365 return -1;
367 while (res == LIBSSH2_ERROR_EAGAIN);
369 return res;
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;
387 const char *path1;
388 size_t path1_len;
389 const GString *ctmp_path;
390 char *tmp_path;
391 unsigned int tmp_path_len;
392 int res;
394 if (!sftpfs_op_init (&super, &path_element2, vpath2, mcerror))
395 return -1;
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);
406 res =
407 libssh2_sftp_symlink_ex (super->sftp_session, path1, path1_len, tmp_path, tmp_path_len,
408 LIBSSH2_SFTP_SYMLINK);
409 if (res >= 0)
410 break;
412 if (!sftpfs_waitsocket (super, res, mcerror))
414 g_free (tmp_path);
415 return -1;
418 while (res == LIBSSH2_ERROR_EAGAIN);
419 g_free (tmp_path);
421 return 0;
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;
442 int res;
444 res = sftpfs_stat_init (&super, &path_element, vpath, mcerror, LIBSSH2_SFTP_LSTAT, &attrs);
445 if (res < 0)
446 return res;
448 attrs.flags = LIBSSH2_SFTP_ATTR_ACMODTIME;
449 attrs.atime = atime;
450 attrs.mtime = mtime;
452 fixfname = sftpfs_fix_filename (path_element->path);
456 res =
457 libssh2_sftp_stat_ex (super->sftp_session, fixfname->str, fixfname->len,
458 LIBSSH2_SFTP_SETSTAT, &attrs);
459 if (res >= 0)
460 break;
462 if (sftpfs_is_sftp_error (super->sftp_session, res, LIBSSH2_FX_NO_SUCH_FILE))
463 return -ENOENT;
465 if (sftpfs_is_sftp_error (super->sftp_session, res, LIBSSH2_FX_FAILURE))
467 res = 0; /* need something like ftpfs_ignore_chattr_errors */
468 break;
471 if (!sftpfs_waitsocket (super, res, mcerror))
472 return -1;
474 while (res == LIBSSH2_ERROR_EAGAIN);
476 return res;
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;
496 int res;
498 res = sftpfs_stat_init (&super, &path_element, vpath, mcerror, LIBSSH2_SFTP_LSTAT, &attrs);
499 if (res < 0)
500 return res;
502 attrs.flags = LIBSSH2_SFTP_ATTR_PERMISSIONS;
503 attrs.permissions = mode;
505 fixfname = sftpfs_fix_filename (path_element->path);
509 res =
510 libssh2_sftp_stat_ex (super->sftp_session, fixfname->str, fixfname->len,
511 LIBSSH2_SFTP_SETSTAT, &attrs);
512 if (res >= 0)
513 break;
515 if (sftpfs_is_sftp_error (super->sftp_session, res, LIBSSH2_FX_NO_SUCH_FILE))
516 return -ENOENT;
518 if (sftpfs_is_sftp_error (super->sftp_session, res, LIBSSH2_FX_FAILURE))
520 res = 0; /* need something like ftpfs_ignore_chattr_errors */
521 break;
524 if (!sftpfs_waitsocket (super, res, mcerror))
525 return -1;
527 while (res == LIBSSH2_ERROR_EAGAIN);
529 return res;
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;
547 int res;
549 if (!sftpfs_op_init (&super, &path_element, vpath, mcerror))
550 return -1;
552 fixfname = sftpfs_fix_filename (path_element->path);
556 res = libssh2_sftp_unlink_ex (super->sftp_session, fixfname->str, fixfname->len);
557 if (res >= 0)
558 break;
560 if (!sftpfs_waitsocket (super, res, mcerror))
561 return -1;
563 while (res == LIBSSH2_ERROR_EAGAIN);
565 return res;
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;
582 const char *path1;
583 const vfs_path_element_t *path_element2 = NULL;
584 const GString *ctmp_path;
585 char *tmp_path;
586 unsigned int tmp_path_len;
587 const GString *fixfname;
588 int res;
590 if (!sftpfs_op_init (&super, &path_element2, vpath2, mcerror))
591 return -1;
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);
602 res =
603 libssh2_sftp_rename_ex (super->sftp_session, fixfname->str, fixfname->len, tmp_path,
604 tmp_path_len, LIBSSH2_SFTP_SYMLINK);
605 if (res >= 0)
606 break;
608 if (!sftpfs_waitsocket (super, res, mcerror))
610 g_free (tmp_path);
611 return -1;
614 while (res == LIBSSH2_ERROR_EAGAIN);
615 g_free (tmp_path);
617 return 0;
620 /* --------------------------------------------------------------------------------------------- */