1 /* Virtual File System: SFTP file system.
2 The internal functions
3
4 Copyright (C) 2011-2021
5 Free Software Foundation, Inc.
6
7 Written by:
8 Ilia Maslakov <il.smind@gmail.com>, 2011
9 Slava Zanko <slavazanko@gmail.com>, 2011, 2012
10
11 This file is part of the Midnight Commander.
12
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.
17
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.
22
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/>.
25 */
26
27 #include <config.h>
28 #include <errno.h>
29
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
37
38 #include "lib/global.h"
39 #include "lib/util.h"
40
41 #include "internal.h"
42
43 /*** global variables ****************************************************************************/
44
45 GString *sftpfs_filename_buffer = NULL;
46
47 /*** file scope macro definitions ****************************************************************/
48
49 /*** file scope type declarations ****************************************************************/
50
51 /*** file scope variables ************************************************************************/
52
53 /* --------------------------------------------------------------------------------------------- */
54 /*** file scope functions ************************************************************************/
55 /* --------------------------------------------------------------------------------------------- */
56
57 /* Adjust block size and number of blocks */
58
59 static void
sftpfs_blksize(struct stat * s)60 sftpfs_blksize (struct stat *s)
61 {
62 #ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
63 s->st_blksize = LIBSSH2_CHANNEL_WINDOW_DEFAULT; /* FIXME */
64 #endif /* HAVE_STRUCT_STAT_ST_BLKSIZE */
65 vfs_adjust_stat (s);
66 }
67
68 /* --------------------------------------------------------------------------------------------- */
69 /**
70 * Awaiting for any activity on socket.
71 *
72 * @param super extra data for SFTP connection
73 * @param mcerror pointer to the error object
74 * @return 0 if success, negative value otherwise
75 */
76
77 static int
sftpfs_internal_waitsocket(sftpfs_super_t * super,GError ** mcerror)78 sftpfs_internal_waitsocket (sftpfs_super_t * super, GError ** mcerror)
79 {
80 struct timeval timeout = { 10, 0 };
81 fd_set fd;
82 fd_set *writefd = NULL;
83 fd_set *readfd = NULL;
84 int dir, ret;
85
86 mc_return_val_if_error (mcerror, -1);
87
88 FD_ZERO (&fd);
89 FD_SET (super->socket_handle, &fd);
90
91 /* now make sure we wait in the correct direction */
92 dir = libssh2_session_block_directions (super->session);
93
94 if ((dir & LIBSSH2_SESSION_BLOCK_INBOUND) != 0)
95 readfd = &fd;
96
97 if ((dir & LIBSSH2_SESSION_BLOCK_OUTBOUND) != 0)
98 writefd = &fd;
99
100 ret = select (super->socket_handle + 1, readfd, writefd, NULL, &timeout);
101 if (ret < 0)
102 {
103 int my_errno = errno;
104
105 mc_propagate_error (mcerror, my_errno, _("sftp: socket error: %s"),
106 unix_error_string (my_errno));
107 }
108
109 return ret;
110 }
111
112 /* --------------------------------------------------------------------------------------------- */
113
114 static gboolean
sftpfs_op_init(sftpfs_super_t ** super,const vfs_path_element_t ** path_element,const vfs_path_t * vpath,GError ** mcerror)115 sftpfs_op_init (sftpfs_super_t ** super, const vfs_path_element_t ** path_element,
116 const vfs_path_t * vpath, GError ** mcerror)
117 {
118 struct vfs_s_super *lc_super = NULL;
119
120 mc_return_val_if_error (mcerror, FALSE);
121
122 if (vfs_s_get_path (vpath, &lc_super, 0) == NULL)
123 return FALSE;
124
125 if (lc_super == NULL)
126 return FALSE;
127
128 *super = SFTP_SUPER (lc_super);
129
130 if ((*super)->sftp_session == NULL)
131 return FALSE;
132
133 *path_element = vfs_path_get_by_index (vpath, -1);
134
135 return TRUE;
136 }
137
138 /* --------------------------------------------------------------------------------------------- */
139
140 static int
sftpfs_stat_init(sftpfs_super_t ** super,const vfs_path_element_t ** path_element,const vfs_path_t * vpath,GError ** mcerror,int stat_type,LIBSSH2_SFTP_ATTRIBUTES * attrs)141 sftpfs_stat_init (sftpfs_super_t ** super, const vfs_path_element_t ** path_element,
142 const vfs_path_t * vpath, GError ** mcerror, int stat_type,
143 LIBSSH2_SFTP_ATTRIBUTES * attrs)
144 {
145 int res;
146
147 if (!sftpfs_op_init (super, path_element, vpath, mcerror))
148 return -1;
149
150 do
151 {
152 const GString *fixfname;
153
154 fixfname = sftpfs_fix_filename ((*path_element)->path);
155
156 res = libssh2_sftp_stat_ex ((*super)->sftp_session, fixfname->str, fixfname->len,
157 stat_type, attrs);
158 if (res >= 0)
159 break;
160
161 if (sftpfs_is_sftp_error ((*super)->sftp_session, res, LIBSSH2_FX_PERMISSION_DENIED))
162 return -EACCES;
163
164 if (sftpfs_is_sftp_error ((*super)->sftp_session, res, LIBSSH2_FX_NO_SUCH_FILE))
165 return -ENOENT;
166
167 if (!sftpfs_waitsocket (*super, res, mcerror))
168 return -1;
169 }
170 while (res == LIBSSH2_ERROR_EAGAIN);
171
172 return res;
173 }
174
175 /* --------------------------------------------------------------------------------------------- */
176 /*** public functions ****************************************************************************/
177 /* --------------------------------------------------------------------------------------------- */
178
179 gboolean
sftpfs_waitsocket(sftpfs_super_t * super,int sftp_res,GError ** mcerror)180 sftpfs_waitsocket (sftpfs_super_t * super, int sftp_res, GError ** mcerror)
181 {
182 if (sftp_res != LIBSSH2_ERROR_EAGAIN)
183 {
184 sftpfs_ssherror_to_gliberror (super, sftp_res, mcerror);
185 return FALSE;
186 }
187
188 sftpfs_internal_waitsocket (super, mcerror);
189
190 return (mcerror == NULL || *mcerror == NULL);
191 }
192
193 /* --------------------------------------------------------------------------------------------- */
194
195 gboolean
sftpfs_is_sftp_error(LIBSSH2_SFTP * sftp_session,int sftp_res,int sftp_error)196 sftpfs_is_sftp_error (LIBSSH2_SFTP * sftp_session, int sftp_res, int sftp_error)
197 {
198 return (sftp_res == LIBSSH2_ERROR_SFTP_PROTOCOL &&
199 libssh2_sftp_last_error (sftp_session) == (unsigned long) sftp_error);
200 }
201
202 /* --------------------------------------------------------------------------------------------- */
203 /**
204 * Convert libssh error to GError object.
205 *
206 * @param super extra data for SFTP connection
207 * @param libssh_errno errno from libssh
208 * @param mcerror pointer to the error object
209 */
210
211 void
sftpfs_ssherror_to_gliberror(sftpfs_super_t * super,int libssh_errno,GError ** mcerror)212 sftpfs_ssherror_to_gliberror (sftpfs_super_t * super, int libssh_errno, GError ** mcerror)
213 {
214 char *err = NULL;
215 int err_len;
216
217 mc_return_if_error (mcerror);
218
219 libssh2_session_last_error (super->session, &err, &err_len, 1);
220 if (libssh_errno == LIBSSH2_ERROR_SFTP_PROTOCOL && super->sftp_session != NULL)
221 mc_propagate_error (mcerror, libssh_errno, "%s %lu", err,
222 libssh2_sftp_last_error (super->sftp_session));
223 else
224 mc_propagate_error (mcerror, libssh_errno, "%s", err);
225 g_free (err);
226 }
227
228 /* --------------------------------------------------------------------------------------------- */
229 /**
230 * Fix filename for SFTP operations: add leading slash to file name.
231 *
232 * @param file_name file name
233 * @param length length of returned string
234 *
235 * @return pointer to string that contains the file name with leading slash
236 */
237
238 const GString *
sftpfs_fix_filename(const char * file_name)239 sftpfs_fix_filename (const char *file_name)
240 {
241 g_string_printf (sftpfs_filename_buffer, "%c%s", PATH_SEP, file_name);
242 return sftpfs_filename_buffer;
243 }
244
245 /* --------------------------------------------------------------------------------------------- */
246
247 void
sftpfs_attr_to_stat(const LIBSSH2_SFTP_ATTRIBUTES * attrs,struct stat * s)248 sftpfs_attr_to_stat (const LIBSSH2_SFTP_ATTRIBUTES * attrs, struct stat *s)
249 {
250 if ((attrs->flags & LIBSSH2_SFTP_ATTR_UIDGID) != 0)
251 {
252 s->st_uid = attrs->uid;
253 s->st_gid = attrs->gid;
254 }
255
256 if ((attrs->flags & LIBSSH2_SFTP_ATTR_ACMODTIME) != 0)
257 {
258 s->st_atime = attrs->atime;
259 s->st_mtime = attrs->mtime;
260 s->st_ctime = attrs->mtime;
261 #ifdef HAVE_STRUCT_STAT_ST_MTIM
262 s->st_atim.tv_nsec = s->st_mtim.tv_nsec = s->st_ctim.tv_nsec = 0;
263 #endif
264 }
265
266 if ((attrs->flags & LIBSSH2_SFTP_ATTR_SIZE) != 0)
267 {
268 s->st_size = attrs->filesize;
269 sftpfs_blksize (s);
270 }
271
272 if ((attrs->flags & LIBSSH2_SFTP_ATTR_PERMISSIONS) != 0)
273 s->st_mode = attrs->permissions;
274 }
275
276 /* --------------------------------------------------------------------------------------------- */
277 /**
278 * Getting information about a symbolic link.
279 *
280 * @param vpath path to file, directory or symbolic link
281 * @param buf buffer for store stat-info
282 * @param mcerror pointer to error object
283 * @return 0 if success, negative value otherwise
284 */
285
286 int
sftpfs_lstat(const vfs_path_t * vpath,struct stat * buf,GError ** mcerror)287 sftpfs_lstat (const vfs_path_t * vpath, struct stat *buf, GError ** mcerror)
288 {
289 sftpfs_super_t *super = NULL;
290 const vfs_path_element_t *path_element = NULL;
291 LIBSSH2_SFTP_ATTRIBUTES attrs;
292 int res;
293
294 res = sftpfs_stat_init (&super, &path_element, vpath, mcerror, LIBSSH2_SFTP_LSTAT, &attrs);
295 if (res >= 0)
296 {
297 sftpfs_attr_to_stat (&attrs, buf);
298 res = 0;
299 }
300
301 return res;
302 }
303
304 /* --------------------------------------------------------------------------------------------- */
305 /**
306 * Getting information about a file or directory.
307 *
308 * @param vpath path to file or directory
309 * @param buf buffer for store stat-info
310 * @param mcerror pointer to error object
311 * @return 0 if success, negative value otherwise
312 */
313
314 int
sftpfs_stat(const vfs_path_t * vpath,struct stat * buf,GError ** mcerror)315 sftpfs_stat (const vfs_path_t * vpath, struct stat *buf, GError ** mcerror)
316 {
317 sftpfs_super_t *super = NULL;
318 const vfs_path_element_t *path_element = NULL;
319 LIBSSH2_SFTP_ATTRIBUTES attrs;
320 int res;
321
322 res = sftpfs_stat_init (&super, &path_element, vpath, mcerror, LIBSSH2_SFTP_STAT, &attrs);
323 if (res >= 0)
324 {
325 buf->st_nlink = 1;
326 sftpfs_attr_to_stat (&attrs, buf);
327 res = 0;
328 }
329
330 return res;
331 }
332
333 /* --------------------------------------------------------------------------------------------- */
334 /**
335 * Read value of a symbolic link.
336 *
337 * @param vpath path to file or directory
338 * @param buf buffer for store stat-info
339 * @param size buffer size
340 * @param mcerror pointer to error object
341 * @return 0 if success, negative value otherwise
342 */
343
344 int
sftpfs_readlink(const vfs_path_t * vpath,char * buf,size_t size,GError ** mcerror)345 sftpfs_readlink (const vfs_path_t * vpath, char *buf, size_t size, GError ** mcerror)
346 {
347 sftpfs_super_t *super = NULL;
348 const vfs_path_element_t *path_element = NULL;
349 int res;
350
351 if (!sftpfs_op_init (&super, &path_element, vpath, mcerror))
352 return -1;
353
354 do
355 {
356 const GString *fixfname;
357
358 fixfname = sftpfs_fix_filename (path_element->path);
359
360 res =
361 libssh2_sftp_symlink_ex (super->sftp_session, fixfname->str, fixfname->len, buf, size,
362 LIBSSH2_SFTP_READLINK);
363 if (res >= 0)
364 break;
365
366 if (!sftpfs_waitsocket (super, res, mcerror))
367 return -1;
368 }
369 while (res == LIBSSH2_ERROR_EAGAIN);
370
371 return res;
372 }
373
374 /* --------------------------------------------------------------------------------------------- */
375 /**
376 * Create symlink to file or directory
377 *
378 * @param vpath1 path to file or directory
379 * @param vpath2 path to symlink
380 * @param mcerror pointer to error object
381 * @return 0 if success, negative value otherwise
382 */
383
384 int
sftpfs_symlink(const vfs_path_t * vpath1,const vfs_path_t * vpath2,GError ** mcerror)385 sftpfs_symlink (const vfs_path_t * vpath1, const vfs_path_t * vpath2, GError ** mcerror)
386 {
387 sftpfs_super_t *super = NULL;
388 const vfs_path_element_t *path_element1;
389 const vfs_path_element_t *path_element2 = NULL;
390 const GString *ctmp_path;
391 char *tmp_path;
392 unsigned int tmp_path_len;
393 int res;
394
395 if (!sftpfs_op_init (&super, &path_element2, vpath2, mcerror))
396 return -1;
397
398 ctmp_path = sftpfs_fix_filename (path_element2->path);
399 tmp_path = g_strndup (ctmp_path->str, ctmp_path->len);
400 tmp_path_len = ctmp_path->len;
401
402 path_element1 = vfs_path_get_by_index (vpath1, -1);
403
404 do
405 {
406 const GString *fixfname;
407
408 fixfname = sftpfs_fix_filename (path_element1->path);
409
410 res =
411 libssh2_sftp_symlink_ex (super->sftp_session, fixfname->str, fixfname->len, tmp_path,
412 tmp_path_len, LIBSSH2_SFTP_SYMLINK);
413 if (res >= 0)
414 break;
415
416 if (!sftpfs_waitsocket (super, res, mcerror))
417 {
418 g_free (tmp_path);
419 return -1;
420 }
421 }
422 while (res == LIBSSH2_ERROR_EAGAIN);
423 g_free (tmp_path);
424
425 return 0;
426 }
427
428 /* --------------------------------------------------------------------------------------------- */
429 /**
430 * Changes the times of the file.
431 *
432 * @param vpath path to file or directory
433 * @param atime access time
434 * @param mtime modification time
435 * @param mcerror pointer to error object
436 * @return 0 if success, negative value otherwise
437 */
438
439 int
sftpfs_utime(const vfs_path_t * vpath,time_t atime,time_t mtime,GError ** mcerror)440 sftpfs_utime (const vfs_path_t * vpath, time_t atime, time_t mtime, GError ** mcerror)
441 {
442 sftpfs_super_t *super = NULL;
443 const vfs_path_element_t *path_element = NULL;
444 LIBSSH2_SFTP_ATTRIBUTES attrs;
445 int res;
446
447 res = sftpfs_stat_init (&super, &path_element, vpath, mcerror, LIBSSH2_SFTP_LSTAT, &attrs);
448 if (res < 0)
449 return res;
450
451 attrs.atime = atime;
452 attrs.mtime = mtime;
453
454 do
455 {
456 const GString *fixfname;
457
458 fixfname = sftpfs_fix_filename (path_element->path);
459
460 res =
461 libssh2_sftp_stat_ex (super->sftp_session, fixfname->str, fixfname->len,
462 LIBSSH2_SFTP_SETSTAT, &attrs);
463 if (res >= 0)
464 break;
465
466 if (sftpfs_is_sftp_error (super->sftp_session, res, LIBSSH2_FX_NO_SUCH_FILE))
467 return -ENOENT;
468
469 if (sftpfs_is_sftp_error (super->sftp_session, res, LIBSSH2_FX_FAILURE))
470 {
471 res = 0; /* need something like ftpfs_ignore_chattr_errors */
472 break;
473 }
474
475 if (!sftpfs_waitsocket (super, res, mcerror))
476 return -1;
477 }
478 while (res == LIBSSH2_ERROR_EAGAIN);
479
480 return res;
481 }
482
483 /* --------------------------------------------------------------------------------------------- */
484 /**
485 * Changes the permissions of the file.
486 *
487 * @param vpath path to file or directory
488 * @param mode mode (see man 2 open)
489 * @param mcerror pointer to error object
490 * @return 0 if success, negative value otherwise
491 */
492
493 int
sftpfs_chmod(const vfs_path_t * vpath,mode_t mode,GError ** mcerror)494 sftpfs_chmod (const vfs_path_t * vpath, mode_t mode, GError ** mcerror)
495 {
496 sftpfs_super_t *super = NULL;
497 const vfs_path_element_t *path_element = NULL;
498 LIBSSH2_SFTP_ATTRIBUTES attrs;
499 int res;
500
501 res = sftpfs_stat_init (&super, &path_element, vpath, mcerror, LIBSSH2_SFTP_LSTAT, &attrs);
502 if (res < 0)
503 return res;
504
505 attrs.permissions = mode;
506
507 do
508 {
509 const GString *fixfname;
510
511 fixfname = sftpfs_fix_filename (path_element->path);
512
513 res =
514 libssh2_sftp_stat_ex (super->sftp_session, fixfname->str, fixfname->len,
515 LIBSSH2_SFTP_SETSTAT, &attrs);
516 if (res >= 0)
517 break;
518
519 if (sftpfs_is_sftp_error (super->sftp_session, res, LIBSSH2_FX_NO_SUCH_FILE))
520 return -ENOENT;
521
522 if (sftpfs_is_sftp_error (super->sftp_session, res, LIBSSH2_FX_FAILURE))
523 {
524 res = 0; /* need something like ftpfs_ignore_chattr_errors */
525 break;
526 }
527
528 if (!sftpfs_waitsocket (super, res, mcerror))
529 return -1;
530 }
531 while (res == LIBSSH2_ERROR_EAGAIN);
532
533 return res;
534 }
535
536 /* --------------------------------------------------------------------------------------------- */
537 /**
538 * Delete a name from the file system.
539 *
540 * @param vpath path to file or directory
541 * @param mcerror pointer to error object
542 * @return 0 if success, negative value otherwise
543 */
544
545 int
sftpfs_unlink(const vfs_path_t * vpath,GError ** mcerror)546 sftpfs_unlink (const vfs_path_t * vpath, GError ** mcerror)
547 {
548 sftpfs_super_t *super = NULL;
549 const vfs_path_element_t *path_element = NULL;
550 int res;
551
552 if (!sftpfs_op_init (&super, &path_element, vpath, mcerror))
553 return -1;
554
555 do
556 {
557 const GString *fixfname;
558
559 fixfname = sftpfs_fix_filename (path_element->path);
560
561 res = libssh2_sftp_unlink_ex (super->sftp_session, fixfname->str, fixfname->len);
562 if (res >= 0)
563 break;
564
565 if (!sftpfs_waitsocket (super, res, mcerror))
566 return -1;
567 }
568 while (res == LIBSSH2_ERROR_EAGAIN);
569
570 return res;
571 }
572
573 /* --------------------------------------------------------------------------------------------- */
574 /**
575 * Rename a file, moving it between directories if required.
576 *
577 * @param vpath1 path to source file or directory
578 * @param vpath2 path to destination file or directory
579 * @param mcerror pointer to error object
580 * @return 0 if success, negative value otherwise
581 */
582
583 int
sftpfs_rename(const vfs_path_t * vpath1,const vfs_path_t * vpath2,GError ** mcerror)584 sftpfs_rename (const vfs_path_t * vpath1, const vfs_path_t * vpath2, GError ** mcerror)
585 {
586 sftpfs_super_t *super = NULL;
587 const vfs_path_element_t *path_element1;
588 const vfs_path_element_t *path_element2 = NULL;
589 const GString *ctmp_path;
590 char *tmp_path;
591 unsigned int tmp_path_len;
592 int res;
593
594 if (!sftpfs_op_init (&super, &path_element2, vpath2, mcerror))
595 return -1;
596
597 ctmp_path = sftpfs_fix_filename (path_element2->path);
598 tmp_path = g_strndup (ctmp_path->str, ctmp_path->len);
599 tmp_path_len = ctmp_path->len;
600
601 path_element1 = vfs_path_get_by_index (vpath1, -1);
602
603 do
604 {
605 const GString *fixfname;
606
607 fixfname = sftpfs_fix_filename (path_element1->path);
608
609 res =
610 libssh2_sftp_rename_ex (super->sftp_session, fixfname->str, fixfname->len, tmp_path,
611 tmp_path_len, LIBSSH2_SFTP_SYMLINK);
612 if (res >= 0)
613 break;
614
615 if (!sftpfs_waitsocket (super, res, mcerror))
616 {
617 g_free (tmp_path);
618 return -1;
619 }
620 }
621 while (res == LIBSSH2_ERROR_EAGAIN);
622 g_free (tmp_path);
623
624 return 0;
625 }
626
627 /* --------------------------------------------------------------------------------------------- */
628