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 
  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 /*** forward declarations (file scope functions) *************************************************/
  52 
  53 /*** file scope variables ************************************************************************/
  54 
  55 /* --------------------------------------------------------------------------------------------- */
  56 /*** file scope functions ************************************************************************/
  57 /* --------------------------------------------------------------------------------------------- */
  58 
  59 /* Adjust block size and number of blocks */
  60 
  61 static void
  62 sftpfs_blksize (struct stat *s)
     /* [previous][next][first][last][top][bottom][index][help]  */
  63 {
  64 #ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
  65     s->st_blksize = LIBSSH2_CHANNEL_WINDOW_DEFAULT;     /* FIXME */
  66 #endif /* HAVE_STRUCT_STAT_ST_BLKSIZE */
  67     vfs_adjust_stat (s);
  68 }
  69 
  70 /* --------------------------------------------------------------------------------------------- */
  71 /**
  72  * Awaiting for any activity on socket.
  73  *
  74  * @param super extra data for SFTP connection
  75  * @param mcerror    pointer to the error object
  76  * @return 0 if success, negative value otherwise
  77  */
  78 
  79 static int
  80 sftpfs_internal_waitsocket (sftpfs_super_t * super, GError ** mcerror)
     /* [previous][next][first][last][top][bottom][index][help]  */
  81 {
  82     struct timeval timeout = { 10, 0 };
  83     fd_set fd;
  84     fd_set *writefd = NULL;
  85     fd_set *readfd = NULL;
  86     int dir, ret;
  87 
  88     mc_return_val_if_error (mcerror, -1);
  89 
  90     FD_ZERO (&fd);
  91     FD_SET (super->socket_handle, &fd);
  92 
  93     /* now make sure we wait in the correct direction */
  94     dir = libssh2_session_block_directions (super->session);
  95 
  96     if ((dir & LIBSSH2_SESSION_BLOCK_INBOUND) != 0)
  97         readfd = &fd;
  98 
  99     if ((dir & LIBSSH2_SESSION_BLOCK_OUTBOUND) != 0)
 100         writefd = &fd;
 101 
 102     ret = select (super->socket_handle + 1, readfd, writefd, NULL, &timeout);
 103     if (ret < 0)
 104     {
 105         int my_errno = errno;
 106 
 107         mc_propagate_error (mcerror, my_errno, _("sftp: socket error: %s"),
 108                             unix_error_string (my_errno));
 109     }
 110 
 111     return ret;
 112 }
 113 
 114 /* --------------------------------------------------------------------------------------------- */
 115 
 116 static int
 117 sftpfs_stat_init (sftpfs_super_t ** super, const vfs_path_element_t ** path_element,
     /* [previous][next][first][last][top][bottom][index][help]  */
 118                   const vfs_path_t * vpath, GError ** mcerror, int stat_type,
 119                   LIBSSH2_SFTP_ATTRIBUTES * attrs)
 120 {
 121     const GString *fixfname;
 122     int res;
 123 
 124     if (!sftpfs_op_init (super, path_element, vpath, mcerror))
 125         return -1;
 126 
 127     fixfname = sftpfs_fix_filename ((*path_element)->path);
 128 
 129     do
 130     {
 131         res = libssh2_sftp_stat_ex ((*super)->sftp_session, fixfname->str, fixfname->len,
 132                                     stat_type, attrs);
 133         if (res >= 0)
 134             break;
 135 
 136         if (sftpfs_is_sftp_error ((*super)->sftp_session, res, LIBSSH2_FX_PERMISSION_DENIED))
 137             return -EACCES;
 138 
 139         if (sftpfs_is_sftp_error ((*super)->sftp_session, res, LIBSSH2_FX_NO_SUCH_FILE))
 140             return -ENOENT;
 141 
 142         if (!sftpfs_waitsocket (*super, res, mcerror))
 143             return -1;
 144     }
 145     while (res == LIBSSH2_ERROR_EAGAIN);
 146 
 147     return res;
 148 }
 149 
 150 /* --------------------------------------------------------------------------------------------- */
 151 /*** public functions ****************************************************************************/
 152 /* --------------------------------------------------------------------------------------------- */
 153 
 154 gboolean
 155 sftpfs_waitsocket (sftpfs_super_t * super, int sftp_res, GError ** mcerror)
     /* [previous][next][first][last][top][bottom][index][help]  */
 156 {
 157     if (sftp_res != LIBSSH2_ERROR_EAGAIN)
 158     {
 159         sftpfs_ssherror_to_gliberror (super, sftp_res, mcerror);
 160         return FALSE;
 161     }
 162 
 163     sftpfs_internal_waitsocket (super, mcerror);
 164 
 165     return (mcerror == NULL || *mcerror == NULL);
 166 }
 167 
 168 /* --------------------------------------------------------------------------------------------- */
 169 
 170 gboolean
 171 sftpfs_is_sftp_error (LIBSSH2_SFTP * sftp_session, int sftp_res, int sftp_error)
     /* [previous][next][first][last][top][bottom][index][help]  */
 172 {
 173     return (sftp_res == LIBSSH2_ERROR_SFTP_PROTOCOL &&
 174             libssh2_sftp_last_error (sftp_session) == (unsigned long) sftp_error);
 175 }
 176 
 177 /* --------------------------------------------------------------------------------------------- */
 178 /**
 179  * Convert libssh error to GError object.
 180  *
 181  * @param super   extra data for SFTP connection
 182  * @param libssh_errno errno from libssh
 183  * @param mcerror      pointer to the error object
 184  */
 185 
 186 void
 187 sftpfs_ssherror_to_gliberror (sftpfs_super_t * super, int libssh_errno, GError ** mcerror)
     /* [previous][next][first][last][top][bottom][index][help]  */
 188 {
 189     char *err = NULL;
 190     int err_len;
 191 
 192     mc_return_if_error (mcerror);
 193 
 194     libssh2_session_last_error (super->session, &err, &err_len, 1);
 195     if (libssh_errno == LIBSSH2_ERROR_SFTP_PROTOCOL && super->sftp_session != NULL)
 196         mc_propagate_error (mcerror, libssh_errno, "%s %lu", err,
 197                             libssh2_sftp_last_error (super->sftp_session));
 198     else
 199         mc_propagate_error (mcerror, libssh_errno, "%s", err);
 200     g_free (err);
 201 }
 202 
 203 /* --------------------------------------------------------------------------------------------- */
 204 /**
 205  * Fix filename for SFTP operations: add leading slash to file name.
 206  *
 207  * @param file_name file name
 208  * @param length length of returned string
 209  *
 210  * @return pointer to string that contains the file name with leading slash
 211  */
 212 
 213 const GString *
 214 sftpfs_fix_filename (const char *file_name)
     /* [previous][next][first][last][top][bottom][index][help]  */
 215 {
 216     g_string_printf (sftpfs_filename_buffer, "%c%s", PATH_SEP, file_name);
 217     return sftpfs_filename_buffer;
 218 }
 219 
 220 /* --------------------------------------------------------------------------------------------- */
 221 
 222 gboolean
 223 sftpfs_op_init (sftpfs_super_t ** super, const vfs_path_element_t ** path_element,
     /* [previous][next][first][last][top][bottom][index][help]  */
 224                 const vfs_path_t * vpath, GError ** mcerror)
 225 {
 226     struct vfs_s_super *lc_super = NULL;
 227 
 228     mc_return_val_if_error (mcerror, FALSE);
 229 
 230     if (vfs_s_get_path (vpath, &lc_super, 0) == NULL)
 231         return FALSE;
 232 
 233     if (lc_super == NULL)
 234         return FALSE;
 235 
 236     *super = SFTP_SUPER (lc_super);
 237     if ((*super)->sftp_session == NULL)
 238         return FALSE;
 239 
 240     *path_element = vfs_path_get_by_index (vpath, -1);
 241 
 242     return TRUE;
 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     const GString *fixfname;
 350     int res;
 351 
 352     if (!sftpfs_op_init (&super, &path_element, vpath, mcerror))
 353         return -1;
 354 
 355     fixfname = sftpfs_fix_filename (path_element->path);
 356 
 357     do
 358     {
 359         res =
 360             libssh2_sftp_symlink_ex (super->sftp_session, fixfname->str, fixfname->len, buf, size,
 361                                      LIBSSH2_SFTP_READLINK);
 362         if (res >= 0)
 363             break;
 364 
 365         if (!sftpfs_waitsocket (super, res, mcerror))
 366             return -1;
 367     }
 368     while (res == LIBSSH2_ERROR_EAGAIN);
 369 
 370     return res;
 371 }
 372 
 373 /* --------------------------------------------------------------------------------------------- */
 374 /**
 375  * Create symlink to file or directory
 376  *
 377  * @param vpath1  path to file or directory
 378  * @param vpath2  path to symlink
 379  * @param mcerror pointer to error object
 380  * @return 0 if success, negative value otherwise
 381  */
 382 
 383 int
 384 sftpfs_symlink (const vfs_path_t * vpath1, const vfs_path_t * vpath2, GError ** mcerror)
     /* [previous][next][first][last][top][bottom][index][help]  */
 385 {
 386     sftpfs_super_t *super = NULL;
 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     path1 = vfs_path_get_last_path_str (vpath1);
 403     path1_len = strlen (path1);
 404 
 405     do
 406     {
 407         res =
 408             libssh2_sftp_symlink_ex (super->sftp_session, path1, path1_len, tmp_path, tmp_path_len,
 409                                      LIBSSH2_SFTP_SYMLINK);
 410         if (res >= 0)
 411             break;
 412 
 413         if (!sftpfs_waitsocket (super, res, mcerror))
 414         {
 415             g_free (tmp_path);
 416             return -1;
 417         }
 418     }
 419     while (res == LIBSSH2_ERROR_EAGAIN);
 420     g_free (tmp_path);
 421 
 422     return 0;
 423 }
 424 
 425 /* --------------------------------------------------------------------------------------------- */
 426 /**
 427  * Changes the times of the file.
 428  *
 429  * @param vpath   path to file or directory
 430  * @param atime   access time
 431  * @param mtime   modification time
 432  * @param mcerror pointer to error object
 433  * @return 0 if success, negative value otherwise
 434  */
 435 
 436 int
 437 sftpfs_utime (const vfs_path_t * vpath, time_t atime, time_t mtime, GError ** mcerror)
     /* [previous][next][first][last][top][bottom][index][help]  */
 438 {
 439     sftpfs_super_t *super = NULL;
 440     const vfs_path_element_t *path_element = NULL;
 441     LIBSSH2_SFTP_ATTRIBUTES attrs;
 442     const GString *fixfname;
 443     int res;
 444 
 445     res = sftpfs_stat_init (&super, &path_element, vpath, mcerror, LIBSSH2_SFTP_LSTAT, &attrs);
 446     if (res < 0)
 447         return res;
 448 
 449     attrs.flags = LIBSSH2_SFTP_ATTR_ACMODTIME;
 450     attrs.atime = atime;
 451     attrs.mtime = mtime;
 452 
 453     fixfname = sftpfs_fix_filename (path_element->path);
 454 
 455     do
 456     {
 457         res =
 458             libssh2_sftp_stat_ex (super->sftp_session, fixfname->str, fixfname->len,
 459                                   LIBSSH2_SFTP_SETSTAT, &attrs);
 460         if (res >= 0)
 461             break;
 462 
 463         if (sftpfs_is_sftp_error (super->sftp_session, res, LIBSSH2_FX_NO_SUCH_FILE))
 464             return -ENOENT;
 465 
 466         if (sftpfs_is_sftp_error (super->sftp_session, res, LIBSSH2_FX_FAILURE))
 467         {
 468             res = 0;            /* need something like ftpfs_ignore_chattr_errors */
 469             break;
 470         }
 471 
 472         if (!sftpfs_waitsocket (super, res, mcerror))
 473             return -1;
 474     }
 475     while (res == LIBSSH2_ERROR_EAGAIN);
 476 
 477     return res;
 478 }
 479 
 480 /* --------------------------------------------------------------------------------------------- */
 481 /**
 482  * Changes the permissions of the file.
 483  *
 484  * @param vpath   path to file or directory
 485  * @param mode    mode (see man 2 open)
 486  * @param mcerror pointer to error object
 487  * @return 0 if success, negative value otherwise
 488  */
 489 
 490 int
 491 sftpfs_chmod (const vfs_path_t * vpath, mode_t mode, GError ** mcerror)
     /* [previous][next][first][last][top][bottom][index][help]  */
 492 {
 493     sftpfs_super_t *super = NULL;
 494     const vfs_path_element_t *path_element = NULL;
 495     LIBSSH2_SFTP_ATTRIBUTES attrs;
 496     const GString *fixfname;
 497     int res;
 498 
 499     res = sftpfs_stat_init (&super, &path_element, vpath, mcerror, LIBSSH2_SFTP_LSTAT, &attrs);
 500     if (res < 0)
 501         return res;
 502 
 503     attrs.flags = LIBSSH2_SFTP_ATTR_PERMISSIONS;
 504     attrs.permissions = mode;
 505 
 506     fixfname = sftpfs_fix_filename (path_element->path);
 507 
 508     do
 509     {
 510         res =
 511             libssh2_sftp_stat_ex (super->sftp_session, fixfname->str, fixfname->len,
 512                                   LIBSSH2_SFTP_SETSTAT, &attrs);
 513         if (res >= 0)
 514             break;
 515 
 516         if (sftpfs_is_sftp_error (super->sftp_session, res, LIBSSH2_FX_NO_SUCH_FILE))
 517             return -ENOENT;
 518 
 519         if (sftpfs_is_sftp_error (super->sftp_session, res, LIBSSH2_FX_FAILURE))
 520         {
 521             res = 0;            /* need something like ftpfs_ignore_chattr_errors */
 522             break;
 523         }
 524 
 525         if (!sftpfs_waitsocket (super, res, mcerror))
 526             return -1;
 527     }
 528     while (res == LIBSSH2_ERROR_EAGAIN);
 529 
 530     return res;
 531 }
 532 
 533 /* --------------------------------------------------------------------------------------------- */
 534 /**
 535  * Delete a name from the file system.
 536  *
 537  * @param vpath   path to file or directory
 538  * @param mcerror pointer to error object
 539  * @return 0 if success, negative value otherwise
 540  */
 541 
 542 int
 543 sftpfs_unlink (const vfs_path_t * vpath, GError ** mcerror)
     /* [previous][next][first][last][top][bottom][index][help]  */
 544 {
 545     sftpfs_super_t *super = NULL;
 546     const vfs_path_element_t *path_element = NULL;
 547     const GString *fixfname;
 548     int res;
 549 
 550     if (!sftpfs_op_init (&super, &path_element, vpath, mcerror))
 551         return -1;
 552 
 553     fixfname = sftpfs_fix_filename (path_element->path);
 554 
 555     do
 556     {
 557         res = libssh2_sftp_unlink_ex (super->sftp_session, fixfname->str, fixfname->len);
 558         if (res >= 0)
 559             break;
 560 
 561         if (!sftpfs_waitsocket (super, res, mcerror))
 562             return -1;
 563     }
 564     while (res == LIBSSH2_ERROR_EAGAIN);
 565 
 566     return res;
 567 }
 568 
 569 /* --------------------------------------------------------------------------------------------- */
 570 /**
 571  * Rename a file, moving it between directories if required.
 572  *
 573  * @param vpath1   path to source file or directory
 574  * @param vpath2   path to destination file or directory
 575  * @param mcerror  pointer to error object
 576  * @return 0 if success, negative value otherwise
 577  */
 578 
 579 int
 580 sftpfs_rename (const vfs_path_t * vpath1, const vfs_path_t * vpath2, GError ** mcerror)
     /* [previous][next][first][last][top][bottom][index][help]  */
 581 {
 582     sftpfs_super_t *super = NULL;
 583     const char *path1;
 584     const vfs_path_element_t *path_element2 = NULL;
 585     const GString *ctmp_path;
 586     char *tmp_path;
 587     unsigned int tmp_path_len;
 588     const GString *fixfname;
 589     int res;
 590 
 591     if (!sftpfs_op_init (&super, &path_element2, vpath2, mcerror))
 592         return -1;
 593 
 594     ctmp_path = sftpfs_fix_filename (path_element2->path);
 595     tmp_path = g_strndup (ctmp_path->str, ctmp_path->len);
 596     tmp_path_len = ctmp_path->len;
 597 
 598     path1 = vfs_path_get_last_path_str (vpath1);
 599     fixfname = sftpfs_fix_filename (path1);
 600 
 601     do
 602     {
 603         res =
 604             libssh2_sftp_rename_ex (super->sftp_session, fixfname->str, fixfname->len, tmp_path,
 605                                     tmp_path_len, LIBSSH2_SFTP_SYMLINK);
 606         if (res >= 0)
 607             break;
 608 
 609         if (!sftpfs_waitsocket (super, res, mcerror))
 610         {
 611             g_free (tmp_path);
 612             return -1;
 613         }
 614     }
 615     while (res == LIBSSH2_ERROR_EAGAIN);
 616     g_free (tmp_path);
 617 
 618     return 0;
 619 }
 620 
 621 /* --------------------------------------------------------------------------------------------- */

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