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-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
  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     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
 180 sftpfs_waitsocket (sftpfs_super_t * super, int sftp_res, GError ** mcerror)
     /* [previous][next][first][last][top][bottom][index][help]  */
 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
 196 sftpfs_is_sftp_error (LIBSSH2_SFTP * sftp_session, int sftp_res, int sftp_error)
     /* [previous][next][first][last][top][bottom][index][help]  */
 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
 212 sftpfs_ssherror_to_gliberror (sftpfs_super_t * super, int libssh_errno, GError ** mcerror)
     /* [previous][next][first][last][top][bottom][index][help]  */
 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 *
 239 sftpfs_fix_filename (const char *file_name)
     /* [previous][next][first][last][top][bottom][index][help]  */
 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
 248 sftpfs_attr_to_stat (const LIBSSH2_SFTP_ATTRIBUTES * attrs, struct stat *s)
     /* [previous][next][first][last][top][bottom][index][help]  */
 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
 287 sftpfs_lstat (const vfs_path_t * vpath, struct stat *buf, GError ** mcerror)
     /* [previous][next][first][last][top][bottom][index][help]  */
 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
 315 sftpfs_stat (const vfs_path_t * vpath, struct stat *buf, GError ** mcerror)
     /* [previous][next][first][last][top][bottom][index][help]  */
 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
 345 sftpfs_readlink (const vfs_path_t * vpath, char *buf, size_t size, GError ** mcerror)
     /* [previous][next][first][last][top][bottom][index][help]  */
 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
 385 sftpfs_symlink (const vfs_path_t * vpath1, const vfs_path_t * vpath2, GError ** mcerror)
     /* [previous][next][first][last][top][bottom][index][help]  */
 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 char *path1;
 391     size_t path1_len;
 392     const GString *ctmp_path;
 393     char *tmp_path;
 394     unsigned int tmp_path_len;
 395     int res;
 396 
 397     if (!sftpfs_op_init (&super, &path_element2, vpath2, mcerror))
 398         return -1;
 399 
 400     ctmp_path = sftpfs_fix_filename (path_element2->path);
 401     tmp_path = g_strndup (ctmp_path->str, ctmp_path->len);
 402     tmp_path_len = ctmp_path->len;
 403 
 404     path_element1 = vfs_path_get_by_index (vpath1, -1);
 405     path1 = path_element1->path;
 406     path1_len = strlen (path1);
 407 
 408     do
 409     {
 410         res =
 411             libssh2_sftp_symlink_ex (super->sftp_session, path1, path1_len, tmp_path, tmp_path_len,
 412                                      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
 440 sftpfs_utime (const vfs_path_t * vpath, time_t atime, time_t mtime, GError ** mcerror)
     /* [previous][next][first][last][top][bottom][index][help]  */
 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.flags = LIBSSH2_SFTP_ATTR_ACMODTIME;
 452     attrs.atime = atime;
 453     attrs.mtime = mtime;
 454 
 455     do
 456     {
 457         const GString *fixfname;
 458 
 459         fixfname = sftpfs_fix_filename (path_element->path);
 460 
 461         res =
 462             libssh2_sftp_stat_ex (super->sftp_session, fixfname->str, fixfname->len,
 463                                   LIBSSH2_SFTP_SETSTAT, &attrs);
 464         if (res >= 0)
 465             break;
 466 
 467         if (sftpfs_is_sftp_error (super->sftp_session, res, LIBSSH2_FX_NO_SUCH_FILE))
 468             return -ENOENT;
 469 
 470         if (sftpfs_is_sftp_error (super->sftp_session, res, LIBSSH2_FX_FAILURE))
 471         {
 472             res = 0;            /* need something like ftpfs_ignore_chattr_errors */
 473             break;
 474         }
 475 
 476         if (!sftpfs_waitsocket (super, res, mcerror))
 477             return -1;
 478     }
 479     while (res == LIBSSH2_ERROR_EAGAIN);
 480 
 481     return res;
 482 }
 483 
 484 /* --------------------------------------------------------------------------------------------- */
 485 /**
 486  * Changes the permissions of the file.
 487  *
 488  * @param vpath   path to file or directory
 489  * @param mode    mode (see man 2 open)
 490  * @param mcerror pointer to error object
 491  * @return 0 if success, negative value otherwise
 492  */
 493 
 494 int
 495 sftpfs_chmod (const vfs_path_t * vpath, mode_t mode, GError ** mcerror)
     /* [previous][next][first][last][top][bottom][index][help]  */
 496 {
 497     sftpfs_super_t *super = NULL;
 498     const vfs_path_element_t *path_element = NULL;
 499     LIBSSH2_SFTP_ATTRIBUTES attrs;
 500     int res;
 501 
 502     res = sftpfs_stat_init (&super, &path_element, vpath, mcerror, LIBSSH2_SFTP_LSTAT, &attrs);
 503     if (res < 0)
 504         return res;
 505 
 506     attrs.flags = LIBSSH2_SFTP_ATTR_PERMISSIONS;
 507     attrs.permissions = mode;
 508 
 509     do
 510     {
 511         const GString *fixfname;
 512 
 513         fixfname = sftpfs_fix_filename (path_element->path);
 514 
 515         res =
 516             libssh2_sftp_stat_ex (super->sftp_session, fixfname->str, fixfname->len,
 517                                   LIBSSH2_SFTP_SETSTAT, &attrs);
 518         if (res >= 0)
 519             break;
 520 
 521         if (sftpfs_is_sftp_error (super->sftp_session, res, LIBSSH2_FX_NO_SUCH_FILE))
 522             return -ENOENT;
 523 
 524         if (sftpfs_is_sftp_error (super->sftp_session, res, LIBSSH2_FX_FAILURE))
 525         {
 526             res = 0;            /* need something like ftpfs_ignore_chattr_errors */
 527             break;
 528         }
 529 
 530         if (!sftpfs_waitsocket (super, res, mcerror))
 531             return -1;
 532     }
 533     while (res == LIBSSH2_ERROR_EAGAIN);
 534 
 535     return res;
 536 }
 537 
 538 /* --------------------------------------------------------------------------------------------- */
 539 /**
 540  * Delete a name from the file system.
 541  *
 542  * @param vpath   path to file or directory
 543  * @param mcerror pointer to error object
 544  * @return 0 if success, negative value otherwise
 545  */
 546 
 547 int
 548 sftpfs_unlink (const vfs_path_t * vpath, GError ** mcerror)
     /* [previous][next][first][last][top][bottom][index][help]  */
 549 {
 550     sftpfs_super_t *super = NULL;
 551     const vfs_path_element_t *path_element = NULL;
 552     int res;
 553 
 554     if (!sftpfs_op_init (&super, &path_element, vpath, mcerror))
 555         return -1;
 556 
 557     do
 558     {
 559         const GString *fixfname;
 560 
 561         fixfname = sftpfs_fix_filename (path_element->path);
 562 
 563         res = libssh2_sftp_unlink_ex (super->sftp_session, fixfname->str, fixfname->len);
 564         if (res >= 0)
 565             break;
 566 
 567         if (!sftpfs_waitsocket (super, res, mcerror))
 568             return -1;
 569     }
 570     while (res == LIBSSH2_ERROR_EAGAIN);
 571 
 572     return res;
 573 }
 574 
 575 /* --------------------------------------------------------------------------------------------- */
 576 /**
 577  * Rename a file, moving it between directories if required.
 578  *
 579  * @param vpath1   path to source file or directory
 580  * @param vpath2   path to destination file or directory
 581  * @param mcerror  pointer to error object
 582  * @return 0 if success, negative value otherwise
 583  */
 584 
 585 int
 586 sftpfs_rename (const vfs_path_t * vpath1, const vfs_path_t * vpath2, GError ** mcerror)
     /* [previous][next][first][last][top][bottom][index][help]  */
 587 {
 588     sftpfs_super_t *super = NULL;
 589     const vfs_path_element_t *path_element1;
 590     const vfs_path_element_t *path_element2 = NULL;
 591     const GString *ctmp_path;
 592     char *tmp_path;
 593     unsigned int tmp_path_len;
 594     int res;
 595 
 596     if (!sftpfs_op_init (&super, &path_element2, vpath2, mcerror))
 597         return -1;
 598 
 599     ctmp_path = sftpfs_fix_filename (path_element2->path);
 600     tmp_path = g_strndup (ctmp_path->str, ctmp_path->len);
 601     tmp_path_len = ctmp_path->len;
 602 
 603     path_element1 = vfs_path_get_by_index (vpath1, -1);
 604 
 605     do
 606     {
 607         const GString *fixfname;
 608 
 609         fixfname = sftpfs_fix_filename (path_element1->path);
 610 
 611         res =
 612             libssh2_sftp_rename_ex (super->sftp_session, fixfname->str, fixfname->len, tmp_path,
 613                                     tmp_path_len, LIBSSH2_SFTP_SYMLINK);
 614         if (res >= 0)
 615             break;
 616 
 617         if (!sftpfs_waitsocket (super, res, mcerror))
 618         {
 619             g_free (tmp_path);
 620             return -1;
 621         }
 622     }
 623     while (res == LIBSSH2_ERROR_EAGAIN);
 624     g_free (tmp_path);
 625 
 626     return 0;
 627 }
 628 
 629 /* --------------------------------------------------------------------------------------------- */

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