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_op_init
  4. sftpfs_stat_init
  5. sftpfs_waitsocket
  6. sftpfs_is_sftp_error
  7. sftpfs_ssherror_to_gliberror
  8. sftpfs_fix_filename
  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-2022
   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
  60 sftpfs_blksize (struct stat *s)
     /* [previous][next][first][last][top][bottom][index][help]  */
  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
  78 sftpfs_internal_waitsocket (sftpfs_super_t * super, GError ** mcerror)
     /* [previous][next][first][last][top][bottom][index][help]  */
  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
 115 sftpfs_op_init (sftpfs_super_t ** super, const vfs_path_element_t ** path_element,
     /* [previous][next][first][last][top][bottom][index][help]  */
 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
 141 sftpfs_stat_init (sftpfs_super_t ** super, const vfs_path_element_t ** path_element,
     /* [previous][next][first][last][top][bottom][index][help]  */
 142                   const vfs_path_t * vpath, GError ** mcerror, int stat_type,
 143                   LIBSSH2_SFTP_ATTRIBUTES * attrs)
 144 {
 145     const GString *fixfname;
 146     int res;
 147 
 148     if (!sftpfs_op_init (super, path_element, vpath, mcerror))
 149         return -1;
 150 
 151     fixfname = sftpfs_fix_filename ((*path_element)->path);
 152 
 153     do
 154     {
 155         res = libssh2_sftp_stat_ex ((*super)->sftp_session, fixfname->str, fixfname->len,
 156                                     stat_type, attrs);
 157         if (res >= 0)
 158             break;
 159 
 160         if (sftpfs_is_sftp_error ((*super)->sftp_session, res, LIBSSH2_FX_PERMISSION_DENIED))
 161             return -EACCES;
 162 
 163         if (sftpfs_is_sftp_error ((*super)->sftp_session, res, LIBSSH2_FX_NO_SUCH_FILE))
 164             return -ENOENT;
 165 
 166         if (!sftpfs_waitsocket (*super, res, mcerror))
 167             return -1;
 168     }
 169     while (res == LIBSSH2_ERROR_EAGAIN);
 170 
 171     return res;
 172 }
 173 
 174 /* --------------------------------------------------------------------------------------------- */
 175 /*** public functions ****************************************************************************/
 176 /* --------------------------------------------------------------------------------------------- */
 177 
 178 gboolean
 179 sftpfs_waitsocket (sftpfs_super_t * super, int sftp_res, GError ** mcerror)
     /* [previous][next][first][last][top][bottom][index][help]  */
 180 {
 181     if (sftp_res != LIBSSH2_ERROR_EAGAIN)
 182     {
 183         sftpfs_ssherror_to_gliberror (super, sftp_res, mcerror);
 184         return FALSE;
 185     }
 186 
 187     sftpfs_internal_waitsocket (super, mcerror);
 188 
 189     return (mcerror == NULL || *mcerror == NULL);
 190 }
 191 
 192 /* --------------------------------------------------------------------------------------------- */
 193 
 194 gboolean
 195 sftpfs_is_sftp_error (LIBSSH2_SFTP * sftp_session, int sftp_res, int sftp_error)
     /* [previous][next][first][last][top][bottom][index][help]  */
 196 {
 197     return (sftp_res == LIBSSH2_ERROR_SFTP_PROTOCOL &&
 198             libssh2_sftp_last_error (sftp_session) == (unsigned long) sftp_error);
 199 }
 200 
 201 /* --------------------------------------------------------------------------------------------- */
 202 /**
 203  * Convert libssh error to GError object.
 204  *
 205  * @param super   extra data for SFTP connection
 206  * @param libssh_errno errno from libssh
 207  * @param mcerror      pointer to the error object
 208  */
 209 
 210 void
 211 sftpfs_ssherror_to_gliberror (sftpfs_super_t * super, int libssh_errno, GError ** mcerror)
     /* [previous][next][first][last][top][bottom][index][help]  */
 212 {
 213     char *err = NULL;
 214     int err_len;
 215 
 216     mc_return_if_error (mcerror);
 217 
 218     libssh2_session_last_error (super->session, &err, &err_len, 1);
 219     if (libssh_errno == LIBSSH2_ERROR_SFTP_PROTOCOL && super->sftp_session != NULL)
 220         mc_propagate_error (mcerror, libssh_errno, "%s %lu", err,
 221                             libssh2_sftp_last_error (super->sftp_session));
 222     else
 223         mc_propagate_error (mcerror, libssh_errno, "%s", err);
 224     g_free (err);
 225 }
 226 
 227 /* --------------------------------------------------------------------------------------------- */
 228 /**
 229  * Fix filename for SFTP operations: add leading slash to file name.
 230  *
 231  * @param file_name file name
 232  * @param length length of returned string
 233  *
 234  * @return pointer to string that contains the file name with leading slash
 235  */
 236 
 237 const GString *
 238 sftpfs_fix_filename (const char *file_name)
     /* [previous][next][first][last][top][bottom][index][help]  */
 239 {
 240     g_string_printf (sftpfs_filename_buffer, "%c%s", PATH_SEP, file_name);
 241     return sftpfs_filename_buffer;
 242 }
 243 
 244 /* --------------------------------------------------------------------------------------------- */
 245 
 246 void
 247 sftpfs_attr_to_stat (const LIBSSH2_SFTP_ATTRIBUTES * attrs, struct stat *s)
     /* [previous][next][first][last][top][bottom][index][help]  */
 248 {
 249     if ((attrs->flags & LIBSSH2_SFTP_ATTR_UIDGID) != 0)
 250     {
 251         s->st_uid = attrs->uid;
 252         s->st_gid = attrs->gid;
 253     }
 254 
 255     if ((attrs->flags & LIBSSH2_SFTP_ATTR_ACMODTIME) != 0)
 256     {
 257         s->st_atime = attrs->atime;
 258         s->st_mtime = attrs->mtime;
 259         s->st_ctime = attrs->mtime;
 260 #ifdef HAVE_STRUCT_STAT_ST_MTIM
 261         s->st_atim.tv_nsec = s->st_mtim.tv_nsec = s->st_ctim.tv_nsec = 0;
 262 #endif
 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_element1;
 387     const vfs_path_element_t *path_element2 = NULL;
 388     const char *path1;
 389     size_t path1_len;
 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     path1 = path_element1->path;
 404     path1_len = strlen (path1);
 405 
 406     do
 407     {
 408         res =
 409             libssh2_sftp_symlink_ex (super->sftp_session, path1, path1_len, tmp_path, tmp_path_len,
 410                                      LIBSSH2_SFTP_SYMLINK);
 411         if (res >= 0)
 412             break;
 413 
 414         if (!sftpfs_waitsocket (super, res, mcerror))
 415         {
 416             g_free (tmp_path);
 417             return -1;
 418         }
 419     }
 420     while (res == LIBSSH2_ERROR_EAGAIN);
 421     g_free (tmp_path);
 422 
 423     return 0;
 424 }
 425 
 426 /* --------------------------------------------------------------------------------------------- */
 427 /**
 428  * Changes the times of the file.
 429  *
 430  * @param vpath   path to file or directory
 431  * @param atime   access time
 432  * @param mtime   modification time
 433  * @param mcerror pointer to error object
 434  * @return 0 if success, negative value otherwise
 435  */
 436 
 437 int
 438 sftpfs_utime (const vfs_path_t * vpath, time_t atime, time_t mtime, GError ** mcerror)
     /* [previous][next][first][last][top][bottom][index][help]  */
 439 {
 440     sftpfs_super_t *super = NULL;
 441     const vfs_path_element_t *path_element = NULL;
 442     LIBSSH2_SFTP_ATTRIBUTES attrs;
 443     const GString *fixfname;
 444     int res;
 445 
 446     res = sftpfs_stat_init (&super, &path_element, vpath, mcerror, LIBSSH2_SFTP_LSTAT, &attrs);
 447     if (res < 0)
 448         return res;
 449 
 450     attrs.flags = LIBSSH2_SFTP_ATTR_ACMODTIME;
 451     attrs.atime = atime;
 452     attrs.mtime = mtime;
 453 
 454     fixfname = sftpfs_fix_filename (path_element->path);
 455 
 456     do
 457     {
 458         res =
 459             libssh2_sftp_stat_ex (super->sftp_session, fixfname->str, fixfname->len,
 460                                   LIBSSH2_SFTP_SETSTAT, &attrs);
 461         if (res >= 0)
 462             break;
 463 
 464         if (sftpfs_is_sftp_error (super->sftp_session, res, LIBSSH2_FX_NO_SUCH_FILE))
 465             return -ENOENT;
 466 
 467         if (sftpfs_is_sftp_error (super->sftp_session, res, LIBSSH2_FX_FAILURE))
 468         {
 469             res = 0;            /* need something like ftpfs_ignore_chattr_errors */
 470             break;
 471         }
 472 
 473         if (!sftpfs_waitsocket (super, res, mcerror))
 474             return -1;
 475     }
 476     while (res == LIBSSH2_ERROR_EAGAIN);
 477 
 478     return res;
 479 }
 480 
 481 /* --------------------------------------------------------------------------------------------- */
 482 /**
 483  * Changes the permissions of the file.
 484  *
 485  * @param vpath   path to file or directory
 486  * @param mode    mode (see man 2 open)
 487  * @param mcerror pointer to error object
 488  * @return 0 if success, negative value otherwise
 489  */
 490 
 491 int
 492 sftpfs_chmod (const vfs_path_t * vpath, mode_t mode, GError ** mcerror)
     /* [previous][next][first][last][top][bottom][index][help]  */
 493 {
 494     sftpfs_super_t *super = NULL;
 495     const vfs_path_element_t *path_element = NULL;
 496     LIBSSH2_SFTP_ATTRIBUTES attrs;
 497     const GString *fixfname;
 498     int res;
 499 
 500     res = sftpfs_stat_init (&super, &path_element, vpath, mcerror, LIBSSH2_SFTP_LSTAT, &attrs);
 501     if (res < 0)
 502         return res;
 503 
 504     attrs.flags = LIBSSH2_SFTP_ATTR_PERMISSIONS;
 505     attrs.permissions = mode;
 506 
 507     fixfname = sftpfs_fix_filename (path_element->path);
 508 
 509     do
 510     {
 511         res =
 512             libssh2_sftp_stat_ex (super->sftp_session, fixfname->str, fixfname->len,
 513                                   LIBSSH2_SFTP_SETSTAT, &attrs);
 514         if (res >= 0)
 515             break;
 516 
 517         if (sftpfs_is_sftp_error (super->sftp_session, res, LIBSSH2_FX_NO_SUCH_FILE))
 518             return -ENOENT;
 519 
 520         if (sftpfs_is_sftp_error (super->sftp_session, res, LIBSSH2_FX_FAILURE))
 521         {
 522             res = 0;            /* need something like ftpfs_ignore_chattr_errors */
 523             break;
 524         }
 525 
 526         if (!sftpfs_waitsocket (super, res, mcerror))
 527             return -1;
 528     }
 529     while (res == LIBSSH2_ERROR_EAGAIN);
 530 
 531     return res;
 532 }
 533 
 534 /* --------------------------------------------------------------------------------------------- */
 535 /**
 536  * Delete a name from the file system.
 537  *
 538  * @param vpath   path to file or directory
 539  * @param mcerror pointer to error object
 540  * @return 0 if success, negative value otherwise
 541  */
 542 
 543 int
 544 sftpfs_unlink (const vfs_path_t * vpath, GError ** mcerror)
     /* [previous][next][first][last][top][bottom][index][help]  */
 545 {
 546     sftpfs_super_t *super = NULL;
 547     const vfs_path_element_t *path_element = NULL;
 548     const GString *fixfname;
 549     int res;
 550 
 551     if (!sftpfs_op_init (&super, &path_element, vpath, mcerror))
 552         return -1;
 553 
 554     fixfname = sftpfs_fix_filename (path_element->path);
 555 
 556     do
 557     {
 558         res = libssh2_sftp_unlink_ex (super->sftp_session, fixfname->str, fixfname->len);
 559         if (res >= 0)
 560             break;
 561 
 562         if (!sftpfs_waitsocket (super, res, mcerror))
 563             return -1;
 564     }
 565     while (res == LIBSSH2_ERROR_EAGAIN);
 566 
 567     return res;
 568 }
 569 
 570 /* --------------------------------------------------------------------------------------------- */
 571 /**
 572  * Rename a file, moving it between directories if required.
 573  *
 574  * @param vpath1   path to source file or directory
 575  * @param vpath2   path to destination file or directory
 576  * @param mcerror  pointer to error object
 577  * @return 0 if success, negative value otherwise
 578  */
 579 
 580 int
 581 sftpfs_rename (const vfs_path_t * vpath1, const vfs_path_t * vpath2, GError ** mcerror)
     /* [previous][next][first][last][top][bottom][index][help]  */
 582 {
 583     sftpfs_super_t *super = NULL;
 584     const vfs_path_element_t *path_element1;
 585     const vfs_path_element_t *path_element2 = NULL;
 586     const GString *ctmp_path;
 587     char *tmp_path;
 588     unsigned int tmp_path_len;
 589     const GString *fixfname;
 590     int res;
 591 
 592     if (!sftpfs_op_init (&super, &path_element2, vpath2, mcerror))
 593         return -1;
 594 
 595     ctmp_path = sftpfs_fix_filename (path_element2->path);
 596     tmp_path = g_strndup (ctmp_path->str, ctmp_path->len);
 597     tmp_path_len = ctmp_path->len;
 598 
 599     path_element1 = vfs_path_get_by_index (vpath1, -1);
 600 
 601     fixfname = sftpfs_fix_filename (path_element1->path);
 602 
 603     do
 604     {
 605         res =
 606             libssh2_sftp_rename_ex (super->sftp_session, fixfname->str, fixfname->len, tmp_path,
 607                                     tmp_path_len, LIBSSH2_SFTP_SYMLINK);
 608         if (res >= 0)
 609             break;
 610 
 611         if (!sftpfs_waitsocket (super, res, mcerror))
 612         {
 613             g_free (tmp_path);
 614             return -1;
 615         }
 616     }
 617     while (res == LIBSSH2_ERROR_EAGAIN);
 618     g_free (tmp_path);
 619 
 620     return 0;
 621 }
 622 
 623 /* --------------------------------------------------------------------------------------------- */

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