Ticket #1535: SFTP support
[midnight-commander.git] / src / vfs / sftpfs / file.c
blob94ec70d4a04dbb31a731c3f95418290cb60c1f95
1 /* Virtual File System: SFTP file system.
2 The internal functions: files
4 Copyright (C) 2011
5 The Free Software Foundation, Inc.
7 Written by:
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/>.
27 #include <config.h>
29 #include <libssh2.h>
30 #include <libssh2_sftp.h>
32 #include "lib/global.h"
34 #include "internal.h"
36 /*** global variables ****************************************************************************/
38 /*** file scope macro definitions ****************************************************************/
40 /*** file scope type declarations ****************************************************************/
42 typedef struct
44 LIBSSH2_SFTP_HANDLE *handle;
45 int flags;
46 mode_t mode;
47 } sftpfs_file_handler_data_t;
49 /*** file scope variables ************************************************************************/
51 /*** file scope functions ************************************************************************/
52 /* --------------------------------------------------------------------------------------------- */
53 /**
54 * Reopen file by file handle.
56 * @param file_handler the file handler data
57 * @param error pointer to the error handler
59 static void
60 sftpfs_reopen (vfs_file_handler_t * file_handler, GError ** error)
62 sftpfs_file_handler_data_t *file_handler_data;
64 file_handler_data = (sftpfs_file_handler_data_t *) file_handler->data;
66 sftpfs_close_file (file_handler, error);
67 if (error == NULL || *error == NULL)
68 sftpfs_open_file (file_handler, file_handler_data->flags, file_handler_data->mode, error);
71 /* --------------------------------------------------------------------------------------------- */
72 /*** public functions ****************************************************************************/
73 /* --------------------------------------------------------------------------------------------- */
74 /**
75 * Open new SFTP file.
77 * @param file_handler the file handler data
78 * @param flags flags (see man 2 open)
79 * @param mode mode (see man 2 open)
80 * @param error pointer to the error handler
81 * @return TRUE if connection was created successfully, FALSE otherwise
84 gboolean
85 sftpfs_open_file (vfs_file_handler_t * file_handler, int flags, mode_t mode, GError ** error)
87 unsigned long sftp_open_flags = 0;
88 int sftp_open_mode = 0;
89 sftpfs_file_handler_data_t *file_handler_data;
90 sftpfs_super_data_t *super_data;
91 char *name;
93 (void) mode;
95 name = vfs_s_fullpath (&sftpfs_class, file_handler->ino);
96 if (name == NULL)
97 return FALSE;
99 super_data = (sftpfs_super_data_t *) file_handler->ino->super->data;
100 file_handler_data = g_new0 (sftpfs_file_handler_data_t, 1);
102 if ((flags & O_CREAT) != 0 || (flags & O_WRONLY) != 0)
104 sftp_open_flags = (flags & O_WRONLY) != 0 ? LIBSSH2_FXF_WRITE : 0;
105 sftp_open_flags |= (flags & O_CREAT) != 0 ? LIBSSH2_FXF_CREAT : 0;
106 sftp_open_flags |= (flags & O_APPEND) != 0 ? LIBSSH2_FXF_APPEND : 0;
107 sftp_open_flags |= (flags & O_TRUNC) != 0 ? LIBSSH2_FXF_TRUNC : 0;
109 sftp_open_mode = LIBSSH2_SFTP_S_IRUSR |
110 LIBSSH2_SFTP_S_IWUSR | LIBSSH2_SFTP_S_IRGRP | LIBSSH2_SFTP_S_IROTH;
112 else
113 sftp_open_flags = LIBSSH2_FXF_READ;
115 while (TRUE)
117 int libssh_errno;
119 file_handler_data->handle =
120 libssh2_sftp_open (super_data->sftp_session, sftpfs_fix_filename (name),
121 sftp_open_flags, sftp_open_mode);
123 if (file_handler_data->handle != NULL)
124 break;
126 libssh_errno = libssh2_session_last_errno (super_data->session);
127 if (libssh_errno != LIBSSH2_ERROR_EAGAIN)
129 sftpfs_ssherror_to_gliberror (super_data, libssh_errno, error);
130 g_free (name);
131 return FALSE;
135 g_free (name);
137 file_handler_data->flags = flags;
138 file_handler_data->mode = mode;
139 file_handler->data = file_handler_data;
141 if ((flags & O_APPEND) != 0)
143 struct stat file_info;
145 if (sftpfs_fstat (file_handler, &file_info, error) == 0)
146 libssh2_sftp_seek64 (file_handler_data->handle, file_info.st_size);
148 return TRUE;
151 /* --------------------------------------------------------------------------------------------- */
153 * Stats the file specified by the file descriptor.
155 * @param data file data handler
156 * @param buf buffer for store stat-info
157 * @param error pointer to the error handler
158 * @return 0 if sucess, negative value otherwise
162 sftpfs_fstat (void *data, struct stat *buf, GError ** error)
164 int res;
165 LIBSSH2_SFTP_ATTRIBUTES attrs;
166 vfs_file_handler_t *fh = (vfs_file_handler_t *) data;
167 sftpfs_file_handler_data_t *sftpfs_fh = fh->data;
168 struct vfs_s_super *super = fh->ino->super;
169 sftpfs_super_data_t *super_data = (sftpfs_super_data_t *) super->data;
171 if (sftpfs_fh->handle == NULL)
172 return -1;
176 res = libssh2_sftp_fstat_ex (sftpfs_fh->handle, &attrs, 0);
177 if (res >= 0)
178 break;
180 if (res != LIBSSH2_ERROR_EAGAIN)
182 sftpfs_ssherror_to_gliberror (super_data, res, error);
183 return -1;
186 sftpfs_waitsocket (super_data, error);
187 if (error != NULL && *error != NULL)
188 return -1;
190 while (res == LIBSSH2_ERROR_EAGAIN);
192 if ((attrs.flags & LIBSSH2_SFTP_ATTR_UIDGID) != 0)
194 buf->st_uid = attrs.uid;
195 buf->st_gid = attrs.gid;
198 if ((attrs.flags & LIBSSH2_SFTP_ATTR_ACMODTIME) != 0)
200 buf->st_atime = attrs.atime;
201 buf->st_mtime = attrs.mtime;
202 buf->st_ctime = attrs.mtime;
205 if ((attrs.flags & LIBSSH2_SFTP_ATTR_SIZE) != 0)
206 buf->st_size = attrs.filesize;
208 if ((attrs.flags & LIBSSH2_SFTP_ATTR_PERMISSIONS) != 0)
209 buf->st_mode = attrs.permissions;
211 return 0;
214 /* --------------------------------------------------------------------------------------------- */
216 * Read up to 'count' bytes from the file descriptor 'file_handler' to the buffer starting at 'buffer'.
218 * @param data file data handler
219 * @param buffer buffer for data
220 * @param count data size
221 * @param error pointer to the error handler
222 * @return 0 if sucess, negative value otherwise
225 ssize_t
226 sftpfs_read_file (vfs_file_handler_t * file_handler, char *buffer, size_t count, GError ** error)
228 ssize_t rc;
229 sftpfs_file_handler_data_t *file_handler_data;
230 sftpfs_super_data_t *super_data;
232 if (file_handler == NULL || file_handler->data == NULL)
234 g_set_error (error, MC_ERROR, -1, _("sftp: No file handler data present for reading file"));
235 return -1;
238 file_handler_data = file_handler->data;
239 super_data = (sftpfs_super_data_t *) file_handler->ino->super->data;
243 rc = libssh2_sftp_read (file_handler_data->handle, buffer, count);
244 if (rc >= 0)
245 break;
247 if (rc != LIBSSH2_ERROR_EAGAIN)
249 sftpfs_ssherror_to_gliberror (super_data, rc, error);
250 return -1;
253 sftpfs_waitsocket (super_data, error);
254 if (error != NULL && *error != NULL)
255 return -1;
257 while (rc == LIBSSH2_ERROR_EAGAIN);
259 file_handler->pos = (off_t) libssh2_sftp_tell64 (file_handler_data->handle);
261 return rc;
264 /* --------------------------------------------------------------------------------------------- */
266 * Write up to 'count' bytes from the buffer starting at 'buffer' to the descriptor 'file_handler'.
268 * @param data file data handler
269 * @param buffer buffer for data
270 * @param count data size
271 * @param error pointer to the error handler
272 * @return 0 if sucess, negative value otherwise
275 ssize_t
276 sftpfs_write_file (vfs_file_handler_t * file_handler, const char *buffer, size_t count,
277 GError ** error)
279 ssize_t rc;
280 sftpfs_file_handler_data_t *file_handler_data;
281 sftpfs_super_data_t *super_data;
283 file_handler_data = (sftpfs_file_handler_data_t *) file_handler->data;
284 super_data = (sftpfs_super_data_t *) file_handler->ino->super->data;
286 file_handler->pos = (off_t) libssh2_sftp_tell64 (file_handler_data->handle);
290 rc = libssh2_sftp_write (file_handler_data->handle, buffer, count);
291 if (rc >= 0)
292 break;
294 if (rc != LIBSSH2_ERROR_EAGAIN)
296 sftpfs_ssherror_to_gliberror (super_data, rc, error);
297 return -1;
300 sftpfs_waitsocket (super_data, error);
301 if (error != NULL && *error != NULL)
302 return -1;
304 while (rc == LIBSSH2_ERROR_EAGAIN);
306 return rc;
309 /* --------------------------------------------------------------------------------------------- */
311 * Close a file descriptor.
313 * @param data file data handler
314 * @param error pointer to the error handler
315 * @return 0 if sucess, negative value otherwise
319 sftpfs_close_file (vfs_file_handler_t * file_handler, GError ** error)
321 sftpfs_file_handler_data_t *file_handler_data;
323 (void) error;
325 file_handler_data = (sftpfs_file_handler_data_t *) file_handler->data;
326 if (file_handler_data == NULL)
327 return -1;
329 libssh2_sftp_close (file_handler_data->handle);
331 g_free (file_handler_data);
332 return 0;
335 /* --------------------------------------------------------------------------------------------- */
337 * Reposition the offset of the open file associated with the file descriptor.
339 * @param data file data handler
340 * @param offset file offset
341 * @param whence method of seek (at begin, at current, at end)
342 * @param error pointer to the error handler
343 * @return 0 if sucess, negative value otherwise
346 off_t
347 sftpfs_lseek (vfs_file_handler_t * file_handler, off_t offset, int whence, GError ** error)
349 sftpfs_file_handler_data_t *file_handler_data;
351 file_handler_data = (sftpfs_file_handler_data_t *) file_handler->data;
353 switch (whence)
355 case SEEK_SET:
356 /* Need reopen file because:
357 "You MUST NOT seek during writing or reading a file with SFTP, as the internals use
358 outstanding packets and changing the "file position" during transit will results in
359 badness." */
360 if (file_handler->pos > offset || offset == 0)
362 sftpfs_reopen (file_handler, error);
363 if (error != NULL && *error != NULL)
364 return 0;
366 file_handler->pos = offset;
367 break;
368 case SEEK_CUR:
369 file_handler->pos += offset;
370 break;
371 case SEEK_END:
372 if (file_handler->pos > file_handler->ino->st.st_size - offset)
374 sftpfs_reopen (file_handler, error);
375 if (error != NULL && *error != NULL)
376 return 0;
378 file_handler->pos = file_handler->ino->st.st_size - offset;
379 break;
382 libssh2_sftp_seek64 (file_handler_data->handle, file_handler->pos);
383 file_handler->pos = (off_t) libssh2_sftp_tell64 (file_handler_data->handle);
385 return file_handler->pos;
388 /* --------------------------------------------------------------------------------------------- */