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-2024
   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 #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 /* HAVE_STRUCT_STAT_ST_BLKSIZE */
  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,
 133                                     stat_type, 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 =
 359             libssh2_sftp_symlink_ex (super->sftp_session, fixfname->str, fixfname->len, buf, size,
 360                                      LIBSSH2_SFTP_READLINK);
 361         if (res >= 0)
 362             break;
 363 
 364         if (!sftpfs_waitsocket (super, res, mcerror))
 365             return -1;
 366     }
 367     while (res == LIBSSH2_ERROR_EAGAIN);
 368 
 369     return res;
 370 }
 371 
 372 /* --------------------------------------------------------------------------------------------- */
 373 /**
 374  * Create symlink to file or directory
 375  *
 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
 380  */
 381 
 382 int
 383 sftpfs_symlink (const vfs_path_t *vpath1, const vfs_path_t *vpath2, GError **mcerror)
     /* [previous][next][first][last][top][bottom][index][help]  */
 384 {
 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;
 393 
 394     if (!sftpfs_op_init (&super, &path_element2, vpath2, mcerror))
 395         return -1;
 396 
 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;
 400 
 401     path1 = vfs_path_get_last_path_str (vpath1);
 402     path1_len = strlen (path1);
 403 
 404     do
 405     {
 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;
 411 
 412         if (!sftpfs_waitsocket (super, res, mcerror))
 413         {
 414             g_free (tmp_path);
 415             return -1;
 416         }
 417     }
 418     while (res == LIBSSH2_ERROR_EAGAIN);
 419     g_free (tmp_path);
 420 
 421     return 0;
 422 }
 423 
 424 /* --------------------------------------------------------------------------------------------- */
 425 /**
 426  * Changes the times of the file.
 427  *
 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
 433  */
 434 
 435 int
 436 sftpfs_utime (const vfs_path_t *vpath, time_t atime, time_t mtime, GError **mcerror)
     /* [previous][next][first][last][top][bottom][index][help]  */
 437 {
 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;
 443 
 444     res = sftpfs_stat_init (&super, &path_element, vpath, mcerror, LIBSSH2_SFTP_LSTAT, &attrs);
 445     if (res < 0)
 446         return res;
 447 
 448     attrs.flags = LIBSSH2_SFTP_ATTR_ACMODTIME;
 449     attrs.atime = atime;
 450     attrs.mtime = mtime;
 451 
 452     fixfname = sftpfs_fix_filename (path_element->path);
 453 
 454     do
 455     {
 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;
 461 
 462         if (sftpfs_is_sftp_error (super->sftp_session, res, LIBSSH2_FX_NO_SUCH_FILE))
 463             return -ENOENT;
 464 
 465         if (sftpfs_is_sftp_error (super->sftp_session, res, LIBSSH2_FX_FAILURE))
 466         {
 467             res = 0;            /* need something like ftpfs_ignore_chattr_errors */
 468             break;
 469         }
 470 
 471         if (!sftpfs_waitsocket (super, res, mcerror))
 472             return -1;
 473     }
 474     while (res == LIBSSH2_ERROR_EAGAIN);
 475 
 476     return res;
 477 }
 478 
 479 /* --------------------------------------------------------------------------------------------- */
 480 /**
 481  * Changes the permissions of the file.
 482  *
 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
 487  */
 488 
 489 int
 490 sftpfs_chmod (const vfs_path_t *vpath, mode_t mode, GError **mcerror)
     /* [previous][next][first][last][top][bottom][index][help]  */
 491 {
 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;
 497 
 498     res = sftpfs_stat_init (&super, &path_element, vpath, mcerror, LIBSSH2_SFTP_LSTAT, &attrs);
 499     if (res < 0)
 500         return res;
 501 
 502     attrs.flags = LIBSSH2_SFTP_ATTR_PERMISSIONS;
 503     attrs.permissions = mode;
 504 
 505     fixfname = sftpfs_fix_filename (path_element->path);
 506 
 507     do
 508     {
 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;
 514 
 515         if (sftpfs_is_sftp_error (super->sftp_session, res, LIBSSH2_FX_NO_SUCH_FILE))
 516             return -ENOENT;
 517 
 518         if (sftpfs_is_sftp_error (super->sftp_session, res, LIBSSH2_FX_FAILURE))
 519         {
 520             res = 0;            /* need something like ftpfs_ignore_chattr_errors */
 521             break;
 522         }
 523 
 524         if (!sftpfs_waitsocket (super, res, mcerror))
 525             return -1;
 526     }
 527     while (res == LIBSSH2_ERROR_EAGAIN);
 528 
 529     return res;
 530 }
 531 
 532 /* --------------------------------------------------------------------------------------------- */
 533 /**
 534  * Delete a name from the file system.
 535  *
 536  * @param vpath   path to file or directory
 537  * @param mcerror pointer to error object
 538  * @return 0 if success, negative value otherwise
 539  */
 540 
 541 int
 542 sftpfs_unlink (const vfs_path_t *vpath, GError **mcerror)
     /* [previous][next][first][last][top][bottom][index][help]  */
 543 {
 544     sftpfs_super_t *super = NULL;
 545     const vfs_path_element_t *path_element = NULL;
 546     const GString *fixfname;
 547     int res;
 548 
 549     if (!sftpfs_op_init (&super, &path_element, vpath, mcerror))
 550         return -1;
 551 
 552     fixfname = sftpfs_fix_filename (path_element->path);
 553 
 554     do
 555     {
 556         res = libssh2_sftp_unlink_ex (super->sftp_session, fixfname->str, fixfname->len);
 557         if (res >= 0)
 558             break;
 559 
 560         if (!sftpfs_waitsocket (super, res, mcerror))
 561             return -1;
 562     }
 563     while (res == LIBSSH2_ERROR_EAGAIN);
 564 
 565     return res;
 566 }
 567 
 568 /* --------------------------------------------------------------------------------------------- */
 569 /**
 570  * Rename a file, moving it between directories if required.
 571  *
 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
 576  */
 577 
 578 int
 579 sftpfs_rename (const vfs_path_t *vpath1, const vfs_path_t *vpath2, GError **mcerror)
     /* [previous][next][first][last][top][bottom][index][help]  */
 580 {
 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;
 589 
 590     if (!sftpfs_op_init (&super, &path_element2, vpath2, mcerror))
 591         return -1;
 592 
 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;
 596 
 597     path1 = vfs_path_get_last_path_str (vpath1);
 598     fixfname = sftpfs_fix_filename (path1);
 599 
 600     do
 601     {
 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;
 607 
 608         if (!sftpfs_waitsocket (super, res, mcerror))
 609         {
 610             g_free (tmp_path);
 611             return -1;
 612         }
 613     }
 614     while (res == LIBSSH2_ERROR_EAGAIN);
 615     g_free (tmp_path);
 616 
 617     return 0;
 618 }
 619 
 620 /* --------------------------------------------------------------------------------------------- */

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