Ticket #3176: sftpfs: fix segfault when trying to view a file.
[midnight-commander.git] / src / vfs / sftpfs / file.c
blob47bc95fe50681b1e3a9cfa44a4c26388b600f879
1 /* Virtual File System: SFTP file system.
2 The internal functions: files
4 Copyright (C) 2011-2014
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;
63 int flags;
64 mode_t mode;
66 file_handler_data = (sftpfs_file_handler_data_t *) file_handler->data;
67 flags = file_handler_data->flags;
68 mode = file_handler_data->mode;
70 sftpfs_close_file (file_handler, error);
71 if (error == NULL || *error == NULL)
72 sftpfs_open_file (file_handler, flags, mode, error);
75 /* --------------------------------------------------------------------------------------------- */
76 /*** public functions ****************************************************************************/
77 /* --------------------------------------------------------------------------------------------- */
78 /**
79 * Open new SFTP file.
81 * @param file_handler the file handler data
82 * @param flags flags (see man 2 open)
83 * @param mode mode (see man 2 open)
84 * @param error pointer to the error handler
85 * @return TRUE if connection was created successfully, FALSE otherwise
88 gboolean
89 sftpfs_open_file (vfs_file_handler_t * file_handler, int flags, mode_t mode, GError ** error)
91 unsigned long sftp_open_flags = 0;
92 int sftp_open_mode = 0;
93 gboolean do_append = FALSE;
94 sftpfs_file_handler_data_t *file_handler_data;
95 sftpfs_super_data_t *super_data;
96 char *name;
98 (void) mode;
100 name = vfs_s_fullpath (&sftpfs_class, file_handler->ino);
101 if (name == NULL)
102 return FALSE;
104 super_data = (sftpfs_super_data_t *) file_handler->ino->super->data;
105 file_handler_data = g_new0 (sftpfs_file_handler_data_t, 1);
107 if ((flags & O_CREAT) != 0 || (flags & O_WRONLY) != 0)
109 sftp_open_flags = (flags & O_WRONLY) != 0 ? LIBSSH2_FXF_WRITE : 0;
110 sftp_open_flags |= (flags & O_CREAT) != 0 ? LIBSSH2_FXF_CREAT : 0;
111 if ((flags & O_APPEND) != 0)
113 sftp_open_flags |= LIBSSH2_FXF_APPEND;
114 do_append = TRUE;
116 sftp_open_flags |= (flags & O_TRUNC) != 0 ? LIBSSH2_FXF_TRUNC : 0;
118 sftp_open_mode = LIBSSH2_SFTP_S_IRUSR |
119 LIBSSH2_SFTP_S_IWUSR | LIBSSH2_SFTP_S_IRGRP | LIBSSH2_SFTP_S_IROTH;
121 else
122 sftp_open_flags = LIBSSH2_FXF_READ;
124 while (TRUE)
126 int libssh_errno;
128 file_handler_data->handle =
129 libssh2_sftp_open (super_data->sftp_session, sftpfs_fix_filename (name),
130 sftp_open_flags, sftp_open_mode);
132 if (file_handler_data->handle != NULL)
133 break;
135 libssh_errno = libssh2_session_last_errno (super_data->session);
136 if (libssh_errno != LIBSSH2_ERROR_EAGAIN)
138 sftpfs_ssherror_to_gliberror (super_data, libssh_errno, error);
139 g_free (name);
140 g_free (file_handler_data);
141 return FALSE;
145 g_free (name);
147 file_handler_data->flags = flags;
148 file_handler_data->mode = mode;
149 file_handler->data = file_handler_data;
151 if (do_append)
153 struct stat file_info;
155 if (sftpfs_fstat (file_handler, &file_info, error) == 0)
156 libssh2_sftp_seek64 (file_handler_data->handle, file_info.st_size);
158 return TRUE;
161 /* --------------------------------------------------------------------------------------------- */
163 * Stats the file specified by the file descriptor.
165 * @param data file data handler
166 * @param buf buffer for store stat-info
167 * @param error pointer to the error handler
168 * @return 0 if success, negative value otherwise
172 sftpfs_fstat (void *data, struct stat *buf, GError ** error)
174 int res;
175 LIBSSH2_SFTP_ATTRIBUTES attrs;
176 vfs_file_handler_t *fh = (vfs_file_handler_t *) data;
177 sftpfs_file_handler_data_t *sftpfs_fh = fh->data;
178 struct vfs_s_super *super = fh->ino->super;
179 sftpfs_super_data_t *super_data = (sftpfs_super_data_t *) super->data;
181 if (sftpfs_fh->handle == NULL)
182 return -1;
186 res = libssh2_sftp_fstat_ex (sftpfs_fh->handle, &attrs, 0);
187 if (res >= 0)
188 break;
190 if (res != LIBSSH2_ERROR_EAGAIN)
192 sftpfs_ssherror_to_gliberror (super_data, res, error);
193 return -1;
196 sftpfs_waitsocket (super_data, error);
197 if (error != NULL && *error != NULL)
198 return -1;
200 while (res == LIBSSH2_ERROR_EAGAIN);
202 if ((attrs.flags & LIBSSH2_SFTP_ATTR_UIDGID) != 0)
204 buf->st_uid = attrs.uid;
205 buf->st_gid = attrs.gid;
208 if ((attrs.flags & LIBSSH2_SFTP_ATTR_ACMODTIME) != 0)
210 buf->st_atime = attrs.atime;
211 buf->st_mtime = attrs.mtime;
212 buf->st_ctime = attrs.mtime;
215 if ((attrs.flags & LIBSSH2_SFTP_ATTR_SIZE) != 0)
216 buf->st_size = attrs.filesize;
218 if ((attrs.flags & LIBSSH2_SFTP_ATTR_PERMISSIONS) != 0)
219 buf->st_mode = attrs.permissions;
221 return 0;
224 /* --------------------------------------------------------------------------------------------- */
226 * Read up to 'count' bytes from the file descriptor 'file_handler' to the buffer starting at 'buffer'.
228 * @param file_handler file data handler
229 * @param buffer buffer for data
230 * @param count data size
231 * @param error pointer to the error handler
233 * @return 0 on success, negative value otherwise
236 ssize_t
237 sftpfs_read_file (vfs_file_handler_t * file_handler, char *buffer, size_t count, GError ** error)
239 ssize_t rc;
240 sftpfs_file_handler_data_t *file_handler_data;
241 sftpfs_super_data_t *super_data;
243 if (file_handler == NULL || file_handler->data == NULL)
245 g_set_error (error, MC_ERROR, -1, _("sftp: No file handler data present for reading file"));
246 return -1;
249 file_handler_data = file_handler->data;
250 super_data = (sftpfs_super_data_t *) file_handler->ino->super->data;
254 rc = libssh2_sftp_read (file_handler_data->handle, buffer, count);
255 if (rc >= 0)
256 break;
258 if (rc != LIBSSH2_ERROR_EAGAIN)
260 sftpfs_ssherror_to_gliberror (super_data, rc, error);
261 return -1;
264 sftpfs_waitsocket (super_data, error);
265 if (error != NULL && *error != NULL)
266 return -1;
268 while (rc == LIBSSH2_ERROR_EAGAIN);
270 file_handler->pos = (off_t) libssh2_sftp_tell64 (file_handler_data->handle);
272 return rc;
275 /* --------------------------------------------------------------------------------------------- */
278 * Write up to 'count' bytes from the buffer starting at 'buffer' to the descriptor 'file_handler'.
280 * @param file_handler file data handler
281 * @param buffer buffer for data
282 * @param count data size
283 * @param error pointer to the error handler
285 * @return 0 on success, negative value otherwise
288 ssize_t
289 sftpfs_write_file (vfs_file_handler_t * file_handler, const char *buffer, size_t count,
290 GError ** error)
292 ssize_t rc;
293 sftpfs_file_handler_data_t *file_handler_data;
294 sftpfs_super_data_t *super_data;
296 file_handler_data = (sftpfs_file_handler_data_t *) file_handler->data;
297 super_data = (sftpfs_super_data_t *) file_handler->ino->super->data;
299 file_handler->pos = (off_t) libssh2_sftp_tell64 (file_handler_data->handle);
303 rc = libssh2_sftp_write (file_handler_data->handle, buffer, count);
304 if (rc >= 0)
305 break;
307 if (rc != LIBSSH2_ERROR_EAGAIN)
309 sftpfs_ssherror_to_gliberror (super_data, rc, error);
310 return -1;
313 sftpfs_waitsocket (super_data, error);
314 if (error != NULL && *error != NULL)
315 return -1;
317 while (rc == LIBSSH2_ERROR_EAGAIN);
319 return rc;
322 /* --------------------------------------------------------------------------------------------- */
325 * Close a file descriptor.
327 * @param file_handler file data handler
328 * @param error pointer to the error handler
330 * @return 0 on success, negative value otherwise
334 sftpfs_close_file (vfs_file_handler_t * file_handler, GError ** error)
336 sftpfs_file_handler_data_t *file_handler_data;
338 (void) error;
340 file_handler_data = (sftpfs_file_handler_data_t *) file_handler->data;
341 if (file_handler_data == NULL)
342 return -1;
344 libssh2_sftp_close (file_handler_data->handle);
346 g_free (file_handler_data);
347 return 0;
350 /* --------------------------------------------------------------------------------------------- */
353 * Reposition the offset of the open file associated with the file descriptor.
355 * @param file_handler file data handler
356 * @param offset file offset
357 * @param whence method of seek (at begin, at current, at end)
358 * @param error pointer to the error handler
360 * @return 0 on success, negative value otherwise
363 off_t
364 sftpfs_lseek (vfs_file_handler_t * file_handler, off_t offset, int whence, GError ** error)
366 sftpfs_file_handler_data_t *file_handler_data;
368 switch (whence)
370 case SEEK_SET:
371 /* Need reopen file because:
372 "You MUST NOT seek during writing or reading a file with SFTP, as the internals use
373 outstanding packets and changing the "file position" during transit will results in
374 badness." */
375 if (file_handler->pos > offset || offset == 0)
377 sftpfs_reopen (file_handler, error);
378 if (error != NULL && *error != NULL)
379 return 0;
381 file_handler->pos = offset;
382 break;
383 case SEEK_CUR:
384 file_handler->pos += offset;
385 break;
386 case SEEK_END:
387 if (file_handler->pos > file_handler->ino->st.st_size - offset)
389 sftpfs_reopen (file_handler, error);
390 if (error != NULL && *error != NULL)
391 return 0;
393 file_handler->pos = file_handler->ino->st.st_size - offset;
394 break;
397 file_handler_data = (sftpfs_file_handler_data_t *) file_handler->data;
399 libssh2_sftp_seek64 (file_handler_data->handle, file_handler->pos);
400 file_handler->pos = (off_t) libssh2_sftp_tell64 (file_handler_data->handle);
402 return file_handler->pos;
405 /* --------------------------------------------------------------------------------------------- */