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-2019
   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 char *fixfname;
 153         unsigned int fixfname_len;
 154 
 155         fixfname = sftpfs_fix_filename ((*path_element)->path, &fixfname_len);
 156 
 157         res = libssh2_sftp_stat_ex ((*super)->sftp_session, fixfname, fixfname_len,
 158                                     stat_type, attrs);
 159         if (res >= 0)
 160             break;
 161 
 162         if (sftpfs_is_sftp_error ((*super)->sftp_session, res, LIBSSH2_FX_PERMISSION_DENIED))
 163             return -EACCES;
 164 
 165         if (sftpfs_is_sftp_error ((*super)->sftp_session, res, LIBSSH2_FX_NO_SUCH_FILE))
 166             return -ENOENT;
 167 
 168         if (!sftpfs_waitsocket (*super, res, mcerror))
 169             return -1;
 170     }
 171     while (res == LIBSSH2_ERROR_EAGAIN);
 172 
 173     return res;
 174 }
 175 
 176 /* --------------------------------------------------------------------------------------------- */
 177 /*** public functions ****************************************************************************/
 178 /* --------------------------------------------------------------------------------------------- */
 179 
 180 gboolean
 181 sftpfs_waitsocket (sftpfs_super_t * super, int sftp_res, GError ** mcerror)
     /* [previous][next][first][last][top][bottom][index][help]  */
 182 {
 183     if (sftp_res != LIBSSH2_ERROR_EAGAIN)
 184     {
 185         sftpfs_ssherror_to_gliberror (super, sftp_res, mcerror);
 186         return FALSE;
 187     }
 188 
 189     sftpfs_internal_waitsocket (super, mcerror);
 190 
 191     return (mcerror == NULL || *mcerror == NULL);
 192 }
 193 
 194 /* --------------------------------------------------------------------------------------------- */
 195 
 196 gboolean
 197 sftpfs_is_sftp_error (LIBSSH2_SFTP * sftp_session, int sftp_res, int sftp_error)
     /* [previous][next][first][last][top][bottom][index][help]  */
 198 {
 199     return (sftp_res == LIBSSH2_ERROR_SFTP_PROTOCOL &&
 200             libssh2_sftp_last_error (sftp_session) == (unsigned long) sftp_error);
 201 }
 202 
 203 /* --------------------------------------------------------------------------------------------- */
 204 /**
 205  * Convert libssh error to GError object.
 206  *
 207  * @param super   extra data for SFTP connection
 208  * @param libssh_errno errno from libssh
 209  * @param mcerror      pointer to the error object
 210  */
 211 
 212 void
 213 sftpfs_ssherror_to_gliberror (sftpfs_super_t * super, int libssh_errno, GError ** mcerror)
     /* [previous][next][first][last][top][bottom][index][help]  */
 214 {
 215     char *err = NULL;
 216     int err_len;
 217 
 218     mc_return_if_error (mcerror);
 219 
 220     libssh2_session_last_error (super->session, &err, &err_len, 1);
 221     if (libssh_errno == LIBSSH2_ERROR_SFTP_PROTOCOL && super->sftp_session != NULL)
 222         mc_propagate_error (mcerror, libssh_errno, "%s %lu", err,
 223                             libssh2_sftp_last_error (super->sftp_session));
 224     else
 225         mc_propagate_error (mcerror, libssh_errno, "%s", err);
 226     g_free (err);
 227 }
 228 
 229 /* --------------------------------------------------------------------------------------------- */
 230 /**
 231  * Fix filename for SFTP operations: add leading slash to file name.
 232  *
 233  * @param file_name file name
 234  * @param length length of returned string
 235  *
 236  * @return pointer to string that contains the file name with leading slash
 237  */
 238 
 239 const char *
 240 sftpfs_fix_filename (const char *file_name, unsigned int *length)
     /* [previous][next][first][last][top][bottom][index][help]  */
 241 {
 242     g_string_printf (sftpfs_filename_buffer, "%c%s", PATH_SEP, file_name);
 243     *length = sftpfs_filename_buffer->len;
 244     return sftpfs_filename_buffer->str;
 245 }
 246 
 247 /* --------------------------------------------------------------------------------------------- */
 248 
 249 void
 250 sftpfs_attr_to_stat (const LIBSSH2_SFTP_ATTRIBUTES * attrs, struct stat *s)
     /* [previous][next][first][last][top][bottom][index][help]  */
 251 {
 252     if ((attrs->flags & LIBSSH2_SFTP_ATTR_UIDGID) != 0)
 253     {
 254         s->st_uid = attrs->uid;
 255         s->st_gid = attrs->gid;
 256     }
 257 
 258     if ((attrs->flags & LIBSSH2_SFTP_ATTR_ACMODTIME) != 0)
 259     {
 260         s->st_atime = attrs->atime;
 261         s->st_mtime = attrs->mtime;
 262         s->st_ctime = attrs->mtime;
 263 #ifdef HAVE_STRUCT_STAT_ST_MTIM
 264         s->st_atim.tv_nsec = s->st_mtim.tv_nsec = s->st_ctim.tv_nsec = 0;
 265 #endif
 266     }
 267 
 268     if ((attrs->flags & LIBSSH2_SFTP_ATTR_SIZE) != 0)
 269     {
 270         s->st_size = attrs->filesize;
 271         sftpfs_blksize (s);
 272     }
 273 
 274     if ((attrs->flags & LIBSSH2_SFTP_ATTR_PERMISSIONS) != 0)
 275         s->st_mode = attrs->permissions;
 276 }
 277 
 278 /* --------------------------------------------------------------------------------------------- */
 279 /**
 280  * Getting information about a symbolic link.
 281  *
 282  * @param vpath   path to file, directory or symbolic link
 283  * @param buf     buffer for store stat-info
 284  * @param mcerror pointer to error object
 285  * @return 0 if success, negative value otherwise
 286  */
 287 
 288 int
 289 sftpfs_lstat (const vfs_path_t * vpath, struct stat *buf, GError ** mcerror)
     /* [previous][next][first][last][top][bottom][index][help]  */
 290 {
 291     sftpfs_super_t *super = NULL;
 292     const vfs_path_element_t *path_element = NULL;
 293     LIBSSH2_SFTP_ATTRIBUTES attrs;
 294     int res;
 295 
 296     res = sftpfs_stat_init (&super, &path_element, vpath, mcerror, LIBSSH2_SFTP_LSTAT, &attrs);
 297     if (res >= 0)
 298     {
 299         sftpfs_attr_to_stat (&attrs, buf);
 300         res = 0;
 301     }
 302 
 303     return res;
 304 }
 305 
 306 /* --------------------------------------------------------------------------------------------- */
 307 /**
 308  * Getting information about a file or directory.
 309  *
 310  * @param vpath   path to file or directory
 311  * @param buf     buffer for store stat-info
 312  * @param mcerror pointer to error object
 313  * @return 0 if success, negative value otherwise
 314  */
 315 
 316 int
 317 sftpfs_stat (const vfs_path_t * vpath, struct stat *buf, GError ** mcerror)
     /* [previous][next][first][last][top][bottom][index][help]  */
 318 {
 319     sftpfs_super_t *super = NULL;
 320     const vfs_path_element_t *path_element = NULL;
 321     LIBSSH2_SFTP_ATTRIBUTES attrs;
 322     int res;
 323 
 324     res = sftpfs_stat_init (&super, &path_element, vpath, mcerror, LIBSSH2_SFTP_STAT, &attrs);
 325     if (res >= 0)
 326     {
 327         buf->st_nlink = 1;
 328         sftpfs_attr_to_stat (&attrs, buf);
 329         res = 0;
 330     }
 331 
 332     return res;
 333 }
 334 
 335 /* --------------------------------------------------------------------------------------------- */
 336 /**
 337  * Read value of a symbolic link.
 338  *
 339  * @param vpath   path to file or directory
 340  * @param buf     buffer for store stat-info
 341  * @param size    buffer size
 342  * @param mcerror pointer to error object
 343  * @return 0 if success, negative value otherwise
 344  */
 345 
 346 int
 347 sftpfs_readlink (const vfs_path_t * vpath, char *buf, size_t size, GError ** mcerror)
     /* [previous][next][first][last][top][bottom][index][help]  */
 348 {
 349     sftpfs_super_t *super = NULL;
 350     const vfs_path_element_t *path_element = NULL;
 351     int res;
 352 
 353     if (!sftpfs_op_init (&super, &path_element, vpath, mcerror))
 354         return -1;
 355 
 356     do
 357     {
 358         const char *fixfname;
 359         unsigned int fixfname_len = 0;
 360 
 361         fixfname = sftpfs_fix_filename (path_element->path, &fixfname_len);
 362 
 363         res =
 364             libssh2_sftp_symlink_ex (super->sftp_session, fixfname, fixfname_len, buf, size,
 365                                      LIBSSH2_SFTP_READLINK);
 366         if (res >= 0)
 367             break;
 368 
 369         if (!sftpfs_waitsocket (super, res, mcerror))
 370             return -1;
 371     }
 372     while (res == LIBSSH2_ERROR_EAGAIN);
 373 
 374     return res;
 375 }
 376 
 377 /* --------------------------------------------------------------------------------------------- */
 378 /**
 379  * Create symlink to file or directory
 380  *
 381  * @param vpath1  path to file or directory
 382  * @param vpath2  path to symlink
 383  * @param mcerror pointer to error object
 384  * @return 0 if success, negative value otherwise
 385  */
 386 
 387 int
 388 sftpfs_symlink (const vfs_path_t * vpath1, const vfs_path_t * vpath2, GError ** mcerror)
     /* [previous][next][first][last][top][bottom][index][help]  */
 389 {
 390     sftpfs_super_t *super = NULL;
 391     const vfs_path_element_t *path_element1;
 392     const vfs_path_element_t *path_element2 = NULL;
 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     tmp_path = (char *) sftpfs_fix_filename (path_element2->path, &tmp_path_len);
 401     tmp_path = g_strndup (tmp_path, tmp_path_len);
 402 
 403     path_element1 = vfs_path_get_by_index (vpath1, -1);
 404 
 405     do
 406     {
 407         const char *fixfname;
 408         unsigned int fixfname_len = 0;
 409 
 410         fixfname = sftpfs_fix_filename (path_element1->path, &fixfname_len);
 411 
 412         res =
 413             libssh2_sftp_symlink_ex (super->sftp_session, fixfname, fixfname_len, tmp_path,
 414                                      tmp_path_len, LIBSSH2_SFTP_SYMLINK);
 415         if (res >= 0)
 416             break;
 417 
 418         if (!sftpfs_waitsocket (super, res, mcerror))
 419         {
 420             g_free (tmp_path);
 421             return -1;
 422         }
 423     }
 424     while (res == LIBSSH2_ERROR_EAGAIN);
 425     g_free (tmp_path);
 426 
 427     return 0;
 428 }
 429 
 430 /* --------------------------------------------------------------------------------------------- */
 431 /**
 432  * Changes the times of the file.
 433  *
 434  * @param vpath   path to file or directory
 435  * @param atime   access time
 436  * @param mtime   modification time
 437  * @param mcerror pointer to error object
 438  * @return 0 if success, negative value otherwise
 439  */
 440 
 441 int
 442 sftpfs_utime (const vfs_path_t * vpath, time_t atime, time_t mtime, GError ** mcerror)
     /* [previous][next][first][last][top][bottom][index][help]  */
 443 {
 444     sftpfs_super_t *super = NULL;
 445     const vfs_path_element_t *path_element = NULL;
 446     LIBSSH2_SFTP_ATTRIBUTES attrs;
 447     int res;
 448 
 449     res = sftpfs_stat_init (&super, &path_element, vpath, mcerror, LIBSSH2_SFTP_LSTAT, &attrs);
 450     if (res < 0)
 451         return res;
 452 
 453     attrs.atime = atime;
 454     attrs.mtime = mtime;
 455 
 456     do
 457     {
 458         const char *fixfname;
 459         unsigned int fixfname_len = 0;
 460 
 461         fixfname = sftpfs_fix_filename (path_element->path, &fixfname_len);
 462 
 463         res =
 464             libssh2_sftp_stat_ex (super->sftp_session, fixfname, fixfname_len,
 465                                   LIBSSH2_SFTP_SETSTAT, &attrs);
 466         if (res >= 0)
 467             break;
 468 
 469         if (sftpfs_is_sftp_error (super->sftp_session, res, LIBSSH2_FX_NO_SUCH_FILE))
 470             return -ENOENT;
 471 
 472         if (sftpfs_is_sftp_error (super->sftp_session, res, LIBSSH2_FX_FAILURE))
 473         {
 474             res = 0;            /* need something like ftpfs_ignore_chattr_errors */
 475             break;
 476         }
 477 
 478         if (!sftpfs_waitsocket (super, res, mcerror))
 479             return -1;
 480     }
 481     while (res == LIBSSH2_ERROR_EAGAIN);
 482 
 483     return res;
 484 }
 485 
 486 /* --------------------------------------------------------------------------------------------- */
 487 /**
 488  * Changes the permissions of the file.
 489  *
 490  * @param vpath   path to file or directory
 491  * @param mode    mode (see man 2 open)
 492  * @param mcerror pointer to error object
 493  * @return 0 if success, negative value otherwise
 494  */
 495 
 496 int
 497 sftpfs_chmod (const vfs_path_t * vpath, mode_t mode, GError ** mcerror)
     /* [previous][next][first][last][top][bottom][index][help]  */
 498 {
 499     sftpfs_super_t *super = NULL;
 500     const vfs_path_element_t *path_element = NULL;
 501     LIBSSH2_SFTP_ATTRIBUTES attrs;
 502     int res;
 503 
 504     res = sftpfs_stat_init (&super, &path_element, vpath, mcerror, LIBSSH2_SFTP_LSTAT, &attrs);
 505     if (res < 0)
 506         return res;
 507 
 508     attrs.permissions = mode;
 509 
 510     do
 511     {
 512         const char *fixfname;
 513         unsigned int fixfname_len = 0;
 514 
 515         fixfname = sftpfs_fix_filename (path_element->path, &fixfname_len);
 516 
 517         res =
 518             libssh2_sftp_stat_ex (super->sftp_session, fixfname, fixfname_len,
 519                                   LIBSSH2_SFTP_SETSTAT, &attrs);
 520         if (res >= 0)
 521             break;
 522 
 523         if (sftpfs_is_sftp_error (super->sftp_session, res, LIBSSH2_FX_NO_SUCH_FILE))
 524             return -ENOENT;
 525 
 526         if (sftpfs_is_sftp_error (super->sftp_session, res, LIBSSH2_FX_FAILURE))
 527         {
 528             res = 0;            /* need something like ftpfs_ignore_chattr_errors */
 529             break;
 530         }
 531 
 532         if (!sftpfs_waitsocket (super, res, mcerror))
 533             return -1;
 534     }
 535     while (res == LIBSSH2_ERROR_EAGAIN);
 536 
 537     return res;
 538 }
 539 
 540 /* --------------------------------------------------------------------------------------------- */
 541 /**
 542  * Delete a name from the file system.
 543  *
 544  * @param vpath   path to file or directory
 545  * @param mcerror pointer to error object
 546  * @return 0 if success, negative value otherwise
 547  */
 548 
 549 int
 550 sftpfs_unlink (const vfs_path_t * vpath, GError ** mcerror)
     /* [previous][next][first][last][top][bottom][index][help]  */
 551 {
 552     sftpfs_super_t *super = NULL;
 553     const vfs_path_element_t *path_element = NULL;
 554     int res;
 555 
 556     if (!sftpfs_op_init (&super, &path_element, vpath, mcerror))
 557         return -1;
 558 
 559     do
 560     {
 561         const char *fixfname;
 562         unsigned int fixfname_len = 0;
 563 
 564         fixfname = sftpfs_fix_filename (path_element->path, &fixfname_len);
 565 
 566         res = libssh2_sftp_unlink_ex (super->sftp_session, fixfname, fixfname_len);
 567         if (res >= 0)
 568             break;
 569 
 570         if (!sftpfs_waitsocket (super, res, mcerror))
 571             return -1;
 572     }
 573     while (res == LIBSSH2_ERROR_EAGAIN);
 574 
 575     return res;
 576 }
 577 
 578 /* --------------------------------------------------------------------------------------------- */
 579 /**
 580  * Rename a file, moving it between directories if required.
 581  *
 582  * @param vpath1   path to source file or directory
 583  * @param vpath2   path to destination file or directory
 584  * @param mcerror  pointer to error object
 585  * @return 0 if success, negative value otherwise
 586  */
 587 
 588 int
 589 sftpfs_rename (const vfs_path_t * vpath1, const vfs_path_t * vpath2, GError ** mcerror)
     /* [previous][next][first][last][top][bottom][index][help]  */
 590 {
 591     sftpfs_super_t *super = NULL;
 592     const vfs_path_element_t *path_element1;
 593     const vfs_path_element_t *path_element2 = NULL;
 594     char *tmp_path;
 595     unsigned int tmp_path_len;
 596     int res;
 597 
 598     if (!sftpfs_op_init (&super, &path_element2, vpath2, mcerror))
 599         return -1;
 600 
 601     tmp_path = (char *) sftpfs_fix_filename (path_element2->path, &tmp_path_len);
 602     tmp_path = g_strndup (tmp_path, tmp_path_len);
 603 
 604     path_element1 = vfs_path_get_by_index (vpath1, -1);
 605 
 606     do
 607     {
 608         const char *fixfname;
 609         unsigned int fixfname_len = 0;
 610 
 611         fixfname = sftpfs_fix_filename (path_element1->path, &fixfname_len);
 612 
 613         res =
 614             libssh2_sftp_rename_ex (super->sftp_session, fixfname, fixfname_len, tmp_path,
 615                                     tmp_path_len, LIBSSH2_SFTP_SYMLINK);
 616         if (res >= 0)
 617             break;
 618 
 619         if (!sftpfs_waitsocket (super, res, mcerror))
 620         {
 621             g_free (tmp_path);
 622             return -1;
 623         }
 624     }
 625     while (res == LIBSSH2_ERROR_EAGAIN);
 626     g_free (tmp_path);
 627 
 628     return 0;
 629 }
 630 
 631 /* --------------------------------------------------------------------------------------------- */

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