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

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