root/src/vfs/sftpfs/internal.c

/* [previous][next][first][last][top][bottom][index][help]  */

DEFINITIONS

This source file includes following definitions.
  1. sftpfs_blksize
  2. sftpfs_internal_waitsocket
  3. sftpfs_stat_init
  4. sftpfs_waitsocket
  5. sftpfs_is_sftp_error
  6. sftpfs_ssherror_to_gliberror
  7. sftpfs_fix_filename
  8. sftpfs_op_init
  9. sftpfs_attr_to_stat
  10. sftpfs_lstat
  11. sftpfs_stat
  12. sftpfs_readlink
  13. sftpfs_symlink
  14. sftpfs_utime
  15. sftpfs_chmod
  16. sftpfs_unlink
  17. sftpfs_rename

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

/* [previous][next][first][last][top][bottom][index][help]  */