1 /* Virtual File System: SFTP file system.
2 The internal functions: files
4 Copyright (C) 2011-2024
5 Free Software Foundation, Inc.
8 Ilia Maslakov <il.smind@gmail.com>, 2011
9 Slava Zanko <slavazanko@gmail.com>, 2011, 2012
11 This file is part of the Midnight Commander.
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.
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.
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/>.
29 #include <errno.h> /* ENOENT, EACCES */
32 #include <libssh2_sftp.h>
34 #include "lib/global.h"
39 /*** global variables ****************************************************************************/
41 /*** file scope macro definitions ****************************************************************/
43 #define SFTP_FILE_HANDLER(a) ((sftpfs_file_handler_t *) a)
45 /*** file scope type declarations ****************************************************************/
49 vfs_file_handler_t base
; /* base class */
51 LIBSSH2_SFTP_HANDLE
*handle
;
54 } sftpfs_file_handler_t
;
56 /*** forward declarations (file scope functions) *************************************************/
58 /*** file scope variables ************************************************************************/
60 /* --------------------------------------------------------------------------------------------- */
61 /*** file scope functions ************************************************************************/
62 /* --------------------------------------------------------------------------------------------- */
64 * Reopen file by file handle.
66 * @param fh the file handler
67 * @param mcerror pointer to the error handler
70 sftpfs_reopen (vfs_file_handler_t
*fh
, GError
**mcerror
)
72 sftpfs_file_handler_t
*file
= SFTP_FILE_HANDLER (fh
);
76 g_return_if_fail (mcerror
== NULL
|| *mcerror
== NULL
);
81 sftpfs_close_file (fh
, mcerror
);
82 sftpfs_open_file (fh
, flags
, mode
, mcerror
);
85 /* --------------------------------------------------------------------------------------------- */
88 sftpfs_file__handle_error (sftpfs_super_t
*super
, int sftp_res
, GError
**mcerror
)
90 if (sftpfs_is_sftp_error (super
->sftp_session
, sftp_res
, LIBSSH2_FX_PERMISSION_DENIED
))
93 if (sftpfs_is_sftp_error (super
->sftp_session
, sftp_res
, LIBSSH2_FX_NO_SUCH_FILE
))
96 if (!sftpfs_waitsocket (super
, sftp_res
, mcerror
))
102 /* --------------------------------------------------------------------------------------------- */
103 /*** public functions ****************************************************************************/
104 /* --------------------------------------------------------------------------------------------- */
107 sftpfs_fh_new (struct vfs_s_inode
*ino
, gboolean changed
)
109 sftpfs_file_handler_t
*fh
;
111 fh
= g_new0 (sftpfs_file_handler_t
, 1);
112 vfs_s_init_fh (VFS_FILE_HANDLER (fh
), ino
, changed
);
114 return VFS_FILE_HANDLER (fh
);
117 /* --------------------------------------------------------------------------------------------- */
119 * Open new SFTP file.
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
129 sftpfs_open_file (vfs_file_handler_t
*fh
, int flags
, mode_t mode
, GError
**mcerror
)
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
);
137 const GString
*fixfname
;
140 mc_return_val_if_error (mcerror
, FALSE
);
142 name
= vfs_s_fullpath (vfs_sftpfs_ops
, fh
->ino
);
146 if ((flags
& O_CREAT
) != 0 || (flags
& O_WRONLY
) != 0)
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)
152 sftp_open_flags
|= LIBSSH2_FXF_APPEND
;
155 sftp_open_flags
|= (flags
& O_TRUNC
) != 0 ? LIBSSH2_FXF_TRUNC
: 0;
157 sftp_open_mode
= LIBSSH2_SFTP_S_IRUSR
|
158 LIBSSH2_SFTP_S_IWUSR
| LIBSSH2_SFTP_S_IRGRP
| LIBSSH2_SFTP_S_IROTH
;
161 sftp_open_flags
= LIBSSH2_FXF_READ
;
163 fixfname
= sftpfs_fix_filename (name
);
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
)
175 libssh_errno
= libssh2_session_last_errno (super
->session
);
176 if (libssh_errno
!= LIBSSH2_ERROR_EAGAIN
)
178 sftpfs_ssherror_to_gliberror (super
, libssh_errno
, mcerror
);
191 struct stat file_info
= {
196 struct stat file_info = { 0 };
198 gcc < 4.7 [1] generates the following:
200 error: missing initializer [-Werror=missing-field-initializers]
201 error: (near initialization for 'file_info.st_dev') [-Werror=missing-field-initializers]
203 [1] http://stackoverflow.com/questions/13373695/how-to-remove-the-warning-in-gcc-4-6-missing-initializer-wmissing-field-initi/27461062#27461062
206 if (sftpfs_fstat (fh
, &file_info
, mcerror
) == 0)
207 libssh2_sftp_seek64 (file
->handle
, file_info
.st_size
);
212 /* --------------------------------------------------------------------------------------------- */
214 * Stats the file specified by the file descriptor.
216 * @param data file handler
217 * @param buf buffer for store stat-info
218 * @param mcerror pointer to the error handler
219 * @return 0 if success, negative value otherwise
223 sftpfs_fstat (void *data
, struct stat
*buf
, GError
**mcerror
)
226 LIBSSH2_SFTP_ATTRIBUTES attrs
;
227 vfs_file_handler_t
*fh
= VFS_FILE_HANDLER (data
);
228 sftpfs_file_handler_t
*sftpfs_fh
= (sftpfs_file_handler_t
*) data
;
229 struct vfs_s_super
*super
= VFS_FILE_HANDLER_SUPER (fh
);
230 sftpfs_super_t
*sftpfs_super
= SFTP_SUPER (super
);
232 mc_return_val_if_error (mcerror
, -1);
234 if (sftpfs_fh
->handle
== NULL
)
241 res
= libssh2_sftp_fstat_ex (sftpfs_fh
->handle
, &attrs
, 0);
245 err
= sftpfs_file__handle_error (sftpfs_super
, res
, mcerror
);
249 while (res
== LIBSSH2_ERROR_EAGAIN
);
251 sftpfs_attr_to_stat (&attrs
, buf
);
256 /* --------------------------------------------------------------------------------------------- */
258 * Read up to 'count' bytes from the file descriptor 'fh' to the buffer starting at 'buffer'.
260 * @param fh file handler
261 * @param buffer buffer for data
262 * @param count data size
263 * @param mcerror pointer to the error handler
265 * @return 0 on success, negative value otherwise
269 sftpfs_read_file (vfs_file_handler_t
*fh
, char *buffer
, size_t count
, GError
**mcerror
)
272 sftpfs_file_handler_t
*file
= SFTP_FILE_HANDLER (fh
);
273 sftpfs_super_t
*super
;
275 mc_return_val_if_error (mcerror
, -1);
279 mc_propagate_error (mcerror
, 0, "%s",
280 _("sftp: No file handler data present for reading file"));
284 super
= SFTP_SUPER (VFS_FILE_HANDLER_SUPER (fh
));
290 rc
= libssh2_sftp_read (file
->handle
, buffer
, count
);
294 err
= sftpfs_file__handle_error (super
, (int) rc
, mcerror
);
298 while (rc
== LIBSSH2_ERROR_EAGAIN
);
300 fh
->pos
= (off_t
) libssh2_sftp_tell64 (file
->handle
);
305 /* --------------------------------------------------------------------------------------------- */
308 * Write up to 'count' bytes from the buffer starting at 'buffer' to the descriptor 'fh'.
310 * @param fh file handler
311 * @param buffer buffer for data
312 * @param count data size
313 * @param mcerror pointer to the error handler
315 * @return 0 on success, negative value otherwise
319 sftpfs_write_file (vfs_file_handler_t
*fh
, const char *buffer
, size_t count
, GError
**mcerror
)
322 sftpfs_file_handler_t
*file
= SFTP_FILE_HANDLER (fh
);
323 sftpfs_super_t
*super
= SFTP_SUPER (VFS_FILE_HANDLER_SUPER (fh
));
325 mc_return_val_if_error (mcerror
, -1);
327 fh
->pos
= (off_t
) libssh2_sftp_tell64 (file
->handle
);
333 rc
= libssh2_sftp_write (file
->handle
, buffer
, count
);
337 err
= sftpfs_file__handle_error (super
, (int) rc
, mcerror
);
341 while (rc
== LIBSSH2_ERROR_EAGAIN
);
346 /* --------------------------------------------------------------------------------------------- */
349 * Close a file descriptor.
351 * @param fh file handler
352 * @param mcerror pointer to the error handler
354 * @return 0 on success, negative value otherwise
358 sftpfs_close_file (vfs_file_handler_t
*fh
, GError
**mcerror
)
362 mc_return_val_if_error (mcerror
, -1);
364 ret
= libssh2_sftp_close (SFTP_FILE_HANDLER (fh
)->handle
);
366 return ret
== 0 ? 0 : -1;
369 /* --------------------------------------------------------------------------------------------- */
372 * Reposition the offset of the open file associated with the file descriptor.
374 * @param fh file handler
375 * @param offset file offset
376 * @param whence method of seek (at begin, at current, at end)
377 * @param mcerror pointer to the error handler
379 * @return 0 on success, negative value otherwise
383 sftpfs_lseek (vfs_file_handler_t
*fh
, off_t offset
, int whence
, GError
**mcerror
)
385 sftpfs_file_handler_t
*file
= SFTP_FILE_HANDLER (fh
);
387 mc_return_val_if_error (mcerror
, 0);
392 /* Need reopen file because:
393 "You MUST NOT seek during writing or reading a file with SFTP, as the internals use
394 outstanding packets and changing the "file position" during transit will results in
396 if (fh
->pos
> offset
|| offset
== 0)
398 sftpfs_reopen (fh
, mcerror
);
399 mc_return_val_if_error (mcerror
, 0);
407 if (fh
->pos
> fh
->ino
->st
.st_size
- offset
)
409 sftpfs_reopen (fh
, mcerror
);
410 mc_return_val_if_error (mcerror
, 0);
412 fh
->pos
= fh
->ino
->st
.st_size
- offset
;
418 libssh2_sftp_seek64 (file
->handle
, fh
->pos
);
419 fh
->pos
= (off_t
) libssh2_sftp_tell64 (file
->handle
);
424 /* --------------------------------------------------------------------------------------------- */