root/src/vfs/sftpfs/file.c

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

DEFINITIONS

This source file includes following definitions.
  1. sftpfs_reopen
  2. sftpfs_file__handle_error
  3. sftpfs_fh_new
  4. sftpfs_open_file
  5. sftpfs_fstat
  6. sftpfs_read_file
  7. sftpfs_write_file
  8. sftpfs_close_file
  9. sftpfs_lseek

   1 /* Virtual File System: SFTP file system.
   2    The internal functions: files
   3 
   4    Copyright (C) 2011-2025
   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 <https://www.gnu.org/licenses/>.
  25  */
  26 
  27 #include <config.h>
  28 
  29 #include <errno.h>  // ENOENT, EACCES
  30 
  31 #include <libssh2.h>
  32 #include <libssh2_sftp.h>
  33 
  34 #include "lib/global.h"
  35 #include "lib/util.h"
  36 
  37 #include "internal.h"
  38 
  39 /*** global variables ****************************************************************************/
  40 
  41 /*** file scope macro definitions ****************************************************************/
  42 
  43 #define SFTP_FILE_HANDLER(a) ((sftpfs_file_handler_t *) a)
  44 
  45 /*** file scope type declarations ****************************************************************/
  46 
  47 typedef struct
  48 {
  49     vfs_file_handler_t base;  // base class
  50 
  51     LIBSSH2_SFTP_HANDLE *handle;
  52     int flags;
  53     mode_t mode;
  54 } sftpfs_file_handler_t;
  55 
  56 /*** forward declarations (file scope functions) *************************************************/
  57 
  58 /*** file scope variables ************************************************************************/
  59 
  60 /* --------------------------------------------------------------------------------------------- */
  61 /*** file scope functions ************************************************************************/
  62 /* --------------------------------------------------------------------------------------------- */
  63 /**
  64  * Reopen file by file handle.
  65  *
  66  * @param fh      the file handler
  67  * @param mcerror pointer to the error handler
  68  */
  69 static void
  70 sftpfs_reopen (vfs_file_handler_t *fh, GError **mcerror)
     /* [previous][next][first][last][top][bottom][index][help]  */
  71 {
  72     sftpfs_file_handler_t *file = SFTP_FILE_HANDLER (fh);
  73     int flags;
  74     mode_t mode;
  75 
  76     g_return_if_fail (mcerror == NULL || *mcerror == NULL);
  77 
  78     flags = file->flags;
  79     mode = file->mode;
  80 
  81     sftpfs_close_file (fh, mcerror);
  82     sftpfs_open_file (fh, flags, mode, mcerror);
  83 }
  84 
  85 /* --------------------------------------------------------------------------------------------- */
  86 
  87 static int
  88 sftpfs_file__handle_error (sftpfs_super_t *super, int sftp_res, GError **mcerror)
     /* [previous][next][first][last][top][bottom][index][help]  */
  89 {
  90     if (sftpfs_is_sftp_error (super->sftp_session, sftp_res, LIBSSH2_FX_PERMISSION_DENIED))
  91         return -EACCES;
  92 
  93     if (sftpfs_is_sftp_error (super->sftp_session, sftp_res, LIBSSH2_FX_NO_SUCH_FILE))
  94         return -ENOENT;
  95 
  96     if (!sftpfs_waitsocket (super, sftp_res, mcerror))
  97         return -1;
  98 
  99     return 0;
 100 }
 101 
 102 /* --------------------------------------------------------------------------------------------- */
 103 /*** public functions ****************************************************************************/
 104 /* --------------------------------------------------------------------------------------------- */
 105 
 106 vfs_file_handler_t *
 107 sftpfs_fh_new (struct vfs_s_inode *ino, gboolean changed)
     /* [previous][next][first][last][top][bottom][index][help]  */
 108 {
 109     sftpfs_file_handler_t *fh;
 110 
 111     fh = g_new0 (sftpfs_file_handler_t, 1);
 112     vfs_s_init_fh (VFS_FILE_HANDLER (fh), ino, changed);
 113 
 114     return VFS_FILE_HANDLER (fh);
 115 }
 116 
 117 /* --------------------------------------------------------------------------------------------- */
 118 /**
 119  * Open new SFTP file.
 120  *
 121  * @param fh      the file handler
 122  * @param flags   flags (see man 2 open)
 123  * @param mode    mode (see man 2 open)
 124  * @param mcerror pointer to the error handler
 125  * @return TRUE if connection was created successfully, FALSE otherwise
 126  */
 127 
 128 gboolean
 129 sftpfs_open_file (vfs_file_handler_t *fh, int flags, mode_t mode, GError **mcerror)
     /* [previous][next][first][last][top][bottom][index][help]  */
 130 {
 131     unsigned long sftp_open_flags = 0;
 132     int sftp_open_mode = 0;
 133     gboolean do_append = FALSE;
 134     sftpfs_file_handler_t *file = SFTP_FILE_HANDLER (fh);
 135     sftpfs_super_t *super = SFTP_SUPER (fh->ino->super);
 136     char *name;
 137     const GString *fixfname;
 138 
 139     (void) mode;
 140     mc_return_val_if_error (mcerror, FALSE);
 141 
 142     name = vfs_s_fullpath (vfs_sftpfs_ops, fh->ino);
 143     if (name == NULL)
 144         return FALSE;
 145 
 146     if ((flags & O_CREAT) != 0 || (flags & O_WRONLY) != 0)
 147     {
 148         sftp_open_flags = (flags & O_WRONLY) != 0 ? LIBSSH2_FXF_WRITE : 0;
 149         sftp_open_flags |= (flags & O_CREAT) != 0 ? LIBSSH2_FXF_CREAT : 0;
 150         if ((flags & O_APPEND) != 0)
 151         {
 152             sftp_open_flags |= LIBSSH2_FXF_APPEND;
 153             do_append = TRUE;
 154         }
 155         sftp_open_flags |= (flags & O_TRUNC) != 0 ? LIBSSH2_FXF_TRUNC : 0;
 156 
 157         sftp_open_mode = LIBSSH2_SFTP_S_IRUSR | LIBSSH2_SFTP_S_IWUSR | LIBSSH2_SFTP_S_IRGRP
 158             | LIBSSH2_SFTP_S_IROTH;
 159     }
 160     else
 161         sftp_open_flags = LIBSSH2_FXF_READ;
 162 
 163     fixfname = sftpfs_fix_filename (name);
 164 
 165     while (TRUE)
 166     {
 167         int libssh_errno;
 168 
 169         file->handle =
 170             libssh2_sftp_open_ex (super->sftp_session, fixfname->str, fixfname->len,
 171                                   sftp_open_flags, sftp_open_mode, LIBSSH2_SFTP_OPENFILE);
 172         if (file->handle != NULL)
 173             break;
 174 
 175         libssh_errno = libssh2_session_last_errno (super->session);
 176         if (libssh_errno != LIBSSH2_ERROR_EAGAIN)
 177         {
 178             sftpfs_ssherror_to_gliberror (super, libssh_errno, mcerror);
 179             g_free (name);
 180             return FALSE;
 181         }
 182     }
 183 
 184     g_free (name);
 185 
 186     file->flags = flags;
 187     file->mode = mode;
 188 
 189     if (do_append)
 190     {
 191         struct stat file_info = {
 192             .st_dev = 0,
 193         };
 194         /* In case of
 195 
 196            struct stat file_info = { 0 };
 197 
 198            gcc < 4.7 [1] generates the following:
 199 
 200            error: missing initializer [-Werror=missing-field-initializers]
 201            error: (near initialization for 'file_info.st_dev') [-Werror=missing-field-initializers]
 202 
 203            [1]
 204            https://stackoverflow.com/questions/13373695/how-to-remove-the-warning-in-gcc-4-6-missing-initializer-wmissing-field-initi/27461062#27461062
 205          */
 206 
 207         if (sftpfs_fstat (fh, &file_info, mcerror) == 0)
 208             libssh2_sftp_seek64 (file->handle, file_info.st_size);
 209     }
 210     return TRUE;
 211 }
 212 
 213 /* --------------------------------------------------------------------------------------------- */
 214 /**
 215  * Stats the file specified by the file descriptor.
 216  *
 217  * @param data    file handler
 218  * @param buf     buffer for store stat-info
 219  * @param mcerror pointer to the error handler
 220  * @return 0 if success, negative value otherwise
 221  */
 222 
 223 int
 224 sftpfs_fstat (void *data, struct stat *buf, GError **mcerror)
     /* [previous][next][first][last][top][bottom][index][help]  */
 225 {
 226     int res;
 227     LIBSSH2_SFTP_ATTRIBUTES attrs;
 228     vfs_file_handler_t *fh = VFS_FILE_HANDLER (data);
 229     sftpfs_file_handler_t *sftpfs_fh = (sftpfs_file_handler_t *) data;
 230     struct vfs_s_super *super = VFS_FILE_HANDLER_SUPER (fh);
 231     sftpfs_super_t *sftpfs_super = SFTP_SUPER (super);
 232 
 233     mc_return_val_if_error (mcerror, -1);
 234 
 235     if (sftpfs_fh->handle == NULL)
 236         return -1;
 237 
 238     do
 239     {
 240         int err;
 241 
 242         res = libssh2_sftp_fstat_ex (sftpfs_fh->handle, &attrs, 0);
 243         if (res >= 0)
 244             break;
 245 
 246         err = sftpfs_file__handle_error (sftpfs_super, res, mcerror);
 247         if (err < 0)
 248             return err;
 249     }
 250     while (res == LIBSSH2_ERROR_EAGAIN);
 251 
 252     sftpfs_attr_to_stat (&attrs, buf);
 253 
 254     return 0;
 255 }
 256 
 257 /* --------------------------------------------------------------------------------------------- */
 258 /**
 259  * Read up to 'count' bytes from the file descriptor 'fh' to the buffer starting at 'buffer'.
 260  *
 261  * @param fh      file handler
 262  * @param buffer  buffer for data
 263  * @param count   data size
 264  * @param mcerror pointer to the error handler
 265  *
 266  * @return 0 on success, negative value otherwise
 267  */
 268 
 269 ssize_t
 270 sftpfs_read_file (vfs_file_handler_t *fh, char *buffer, size_t count, GError **mcerror)
     /* [previous][next][first][last][top][bottom][index][help]  */
 271 {
 272     ssize_t rc;
 273     sftpfs_file_handler_t *file = SFTP_FILE_HANDLER (fh);
 274     sftpfs_super_t *super;
 275 
 276     mc_return_val_if_error (mcerror, -1);
 277 
 278     if (fh == NULL)
 279     {
 280         mc_propagate_error (mcerror, 0, "%s",
 281                             _ ("sftp: No file handler data present for reading file"));
 282         return -1;
 283     }
 284 
 285     super = SFTP_SUPER (VFS_FILE_HANDLER_SUPER (fh));
 286 
 287     do
 288     {
 289         int err;
 290 
 291         rc = libssh2_sftp_read (file->handle, buffer, count);
 292         if (rc >= 0)
 293             break;
 294 
 295         err = sftpfs_file__handle_error (super, (int) rc, mcerror);
 296         if (err < 0)
 297             return err;
 298     }
 299     while (rc == LIBSSH2_ERROR_EAGAIN);
 300 
 301     fh->pos = (off_t) libssh2_sftp_tell64 (file->handle);
 302 
 303     return rc;
 304 }
 305 
 306 /* --------------------------------------------------------------------------------------------- */
 307 
 308 /**
 309  * Write up to 'count' bytes from  the buffer starting at 'buffer' to the descriptor 'fh'.
 310  *
 311  * @param fh      file handler
 312  * @param buffer  buffer for data
 313  * @param count   data size
 314  * @param mcerror pointer to the error handler
 315  *
 316  * @return 0 on success, negative value otherwise
 317  */
 318 
 319 ssize_t
 320 sftpfs_write_file (vfs_file_handler_t *fh, const char *buffer, size_t count, GError **mcerror)
     /* [previous][next][first][last][top][bottom][index][help]  */
 321 {
 322     ssize_t rc;
 323     sftpfs_file_handler_t *file = SFTP_FILE_HANDLER (fh);
 324     sftpfs_super_t *super = SFTP_SUPER (VFS_FILE_HANDLER_SUPER (fh));
 325 
 326     mc_return_val_if_error (mcerror, -1);
 327 
 328     fh->pos = (off_t) libssh2_sftp_tell64 (file->handle);
 329 
 330     do
 331     {
 332         int err;
 333 
 334         rc = libssh2_sftp_write (file->handle, buffer, count);
 335         if (rc >= 0)
 336             break;
 337 
 338         err = sftpfs_file__handle_error (super, (int) rc, mcerror);
 339         if (err < 0)
 340             return err;
 341     }
 342     while (rc == LIBSSH2_ERROR_EAGAIN);
 343 
 344     return rc;
 345 }
 346 
 347 /* --------------------------------------------------------------------------------------------- */
 348 
 349 /**
 350  * Close a file descriptor.
 351  *
 352  * @param fh      file handler
 353  * @param mcerror pointer to the error handler
 354  *
 355  * @return 0 on success, negative value otherwise
 356  */
 357 
 358 int
 359 sftpfs_close_file (vfs_file_handler_t *fh, GError **mcerror)
     /* [previous][next][first][last][top][bottom][index][help]  */
 360 {
 361     int ret;
 362 
 363     mc_return_val_if_error (mcerror, -1);
 364 
 365     ret = libssh2_sftp_close (SFTP_FILE_HANDLER (fh)->handle);
 366 
 367     return ret == 0 ? 0 : -1;
 368 }
 369 
 370 /* --------------------------------------------------------------------------------------------- */
 371 
 372 /**
 373  * Reposition the offset of the open file associated with the file descriptor.
 374  *
 375  * @param fh      file handler
 376  * @param offset  file offset
 377  * @param whence  method of seek (at begin, at current, at end)
 378  * @param mcerror pointer to the error handler
 379  *
 380  * @return 0 on success, negative value otherwise
 381  */
 382 
 383 off_t
 384 sftpfs_lseek (vfs_file_handler_t *fh, off_t offset, int whence, GError **mcerror)
     /* [previous][next][first][last][top][bottom][index][help]  */
 385 {
 386     sftpfs_file_handler_t *file = SFTP_FILE_HANDLER (fh);
 387 
 388     mc_return_val_if_error (mcerror, 0);
 389 
 390     switch (whence)
 391     {
 392     case SEEK_SET:
 393         /* Need reopen file because:
 394            "You MUST NOT seek during writing or reading a file with SFTP, as the internals use
 395            outstanding packets and changing the "file position" during transit will results in
 396            badness." */
 397         if (fh->pos > offset || offset == 0)
 398         {
 399             sftpfs_reopen (fh, mcerror);
 400             mc_return_val_if_error (mcerror, 0);
 401         }
 402         fh->pos = offset;
 403         break;
 404     case SEEK_CUR:
 405         fh->pos += offset;
 406         break;
 407     case SEEK_END:
 408         if (fh->pos > fh->ino->st.st_size - offset)
 409         {
 410             sftpfs_reopen (fh, mcerror);
 411             mc_return_val_if_error (mcerror, 0);
 412         }
 413         fh->pos = fh->ino->st.st_size - offset;
 414         break;
 415     default:
 416         break;
 417     }
 418 
 419     libssh2_sftp_seek64 (file->handle, fh->pos);
 420     fh->pos = (off_t) libssh2_sftp_tell64 (file->handle);
 421 
 422     return fh->pos;
 423 }
 424 
 425 /* --------------------------------------------------------------------------------------------- */

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