(sftpfs_fstat): use sftpfs_attr_to_stat().
[midnight-commander.git] / src / vfs / sftpfs / file.c
blob84bd9bd08236a524402ef67e4a97d439baf7ed0c
1 /* Virtual File System: SFTP file system.
2 The internal functions: files
4 Copyright (C) 2011-2017
5 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"
33 #include "lib/util.h"
35 #include "internal.h"
37 /*** global variables ****************************************************************************/
39 /*** file scope macro definitions ****************************************************************/
41 /*** file scope type declarations ****************************************************************/
43 typedef struct
45 LIBSSH2_SFTP_HANDLE *handle;
46 int flags;
47 mode_t mode;
48 } sftpfs_file_handler_data_t;
50 /*** file scope variables ************************************************************************/
52 /*** file scope functions ************************************************************************/
53 /* --------------------------------------------------------------------------------------------- */
54 /**
55 * Reopen file by file handle.
57 * @param file_handler the file handler data
58 * @param mcerror pointer to the error handler
60 static void
61 sftpfs_reopen (vfs_file_handler_t * file_handler, GError ** mcerror)
63 sftpfs_file_handler_data_t *file_handler_data;
64 int flags;
65 mode_t mode;
67 g_return_if_fail (mcerror == NULL || *mcerror == NULL);
69 file_handler_data = (sftpfs_file_handler_data_t *) file_handler->data;
70 flags = file_handler_data->flags;
71 mode = file_handler_data->mode;
73 sftpfs_close_file (file_handler, mcerror);
74 sftpfs_open_file (file_handler, flags, mode, mcerror);
77 /* --------------------------------------------------------------------------------------------- */
78 /*** public functions ****************************************************************************/
79 /* --------------------------------------------------------------------------------------------- */
80 /**
81 * Open new SFTP file.
83 * @param file_handler the file handler data
84 * @param flags flags (see man 2 open)
85 * @param mode mode (see man 2 open)
86 * @param mcerror pointer to the error handler
87 * @return TRUE if connection was created successfully, FALSE otherwise
90 gboolean
91 sftpfs_open_file (vfs_file_handler_t * file_handler, int flags, mode_t mode, GError ** mcerror)
93 unsigned long sftp_open_flags = 0;
94 int sftp_open_mode = 0;
95 gboolean do_append = FALSE;
96 sftpfs_file_handler_data_t *file_handler_data;
97 sftpfs_super_data_t *super_data;
98 char *name;
100 (void) mode;
101 mc_return_val_if_error (mcerror, FALSE);
103 name = vfs_s_fullpath (&sftpfs_class, file_handler->ino);
104 if (name == NULL)
105 return FALSE;
107 super_data = (sftpfs_super_data_t *) file_handler->ino->super->data;
108 file_handler_data = g_new0 (sftpfs_file_handler_data_t, 1);
110 if ((flags & O_CREAT) != 0 || (flags & O_WRONLY) != 0)
112 sftp_open_flags = (flags & O_WRONLY) != 0 ? LIBSSH2_FXF_WRITE : 0;
113 sftp_open_flags |= (flags & O_CREAT) != 0 ? LIBSSH2_FXF_CREAT : 0;
114 if ((flags & O_APPEND) != 0)
116 sftp_open_flags |= LIBSSH2_FXF_APPEND;
117 do_append = TRUE;
119 sftp_open_flags |= (flags & O_TRUNC) != 0 ? LIBSSH2_FXF_TRUNC : 0;
121 sftp_open_mode = LIBSSH2_SFTP_S_IRUSR |
122 LIBSSH2_SFTP_S_IWUSR | LIBSSH2_SFTP_S_IRGRP | LIBSSH2_SFTP_S_IROTH;
124 else
125 sftp_open_flags = LIBSSH2_FXF_READ;
127 while (TRUE)
129 const char *fixfname;
130 unsigned int fixfname_len = 0;
131 int libssh_errno;
133 fixfname = sftpfs_fix_filename (name, &fixfname_len);
135 file_handler_data->handle =
136 libssh2_sftp_open_ex (super_data->sftp_session, fixfname, fixfname_len, sftp_open_flags,
137 sftp_open_mode, LIBSSH2_SFTP_OPENFILE);
138 if (file_handler_data->handle != NULL)
139 break;
141 libssh_errno = libssh2_session_last_errno (super_data->session);
142 if (libssh_errno != LIBSSH2_ERROR_EAGAIN)
144 sftpfs_ssherror_to_gliberror (super_data, libssh_errno, mcerror);
145 g_free (name);
146 g_free (file_handler_data);
147 return FALSE;
151 g_free (name);
153 file_handler_data->flags = flags;
154 file_handler_data->mode = mode;
155 file_handler->data = file_handler_data;
157 if (do_append)
159 struct stat file_info = {
160 .st_dev = 0
162 /* In case of
164 struct stat file_info = { 0 };
166 gcc < 4.7 [1] generates the following:
168 error: missing initializer [-Werror=missing-field-initializers]
169 error: (near initialization for 'file_info.st_dev') [-Werror=missing-field-initializers]
171 [1] http://stackoverflow.com/questions/13373695/how-to-remove-the-warning-in-gcc-4-6-missing-initializer-wmissing-field-initi/27461062#27461062
174 if (sftpfs_fstat (file_handler, &file_info, mcerror) == 0)
175 libssh2_sftp_seek64 (file_handler_data->handle, file_info.st_size);
177 return TRUE;
180 /* --------------------------------------------------------------------------------------------- */
182 * Stats the file specified by the file descriptor.
184 * @param data file data handler
185 * @param buf buffer for store stat-info
186 * @param mcerror pointer to the error handler
187 * @return 0 if success, negative value otherwise
191 sftpfs_fstat (void *data, struct stat *buf, GError ** mcerror)
193 int res;
194 LIBSSH2_SFTP_ATTRIBUTES attrs;
195 vfs_file_handler_t *fh = (vfs_file_handler_t *) data;
196 sftpfs_file_handler_data_t *sftpfs_fh = fh->data;
197 struct vfs_s_super *super = fh->ino->super;
198 sftpfs_super_data_t *super_data = (sftpfs_super_data_t *) super->data;
200 mc_return_val_if_error (mcerror, -1);
202 if (sftpfs_fh->handle == NULL)
203 return -1;
207 res = libssh2_sftp_fstat_ex (sftpfs_fh->handle, &attrs, 0);
208 if (res >= 0)
209 break;
211 if (res != LIBSSH2_ERROR_EAGAIN)
213 sftpfs_ssherror_to_gliberror (super_data, res, mcerror);
214 return -1;
217 sftpfs_waitsocket (super_data, mcerror);
218 mc_return_val_if_error (mcerror, -1);
220 while (res == LIBSSH2_ERROR_EAGAIN);
222 sftpfs_attr_to_stat (&attrs, buf);
224 return 0;
227 /* --------------------------------------------------------------------------------------------- */
229 * Read up to 'count' bytes from the file descriptor 'file_handler' to the buffer starting at 'buffer'.
231 * @param file_handler file data handler
232 * @param buffer buffer for data
233 * @param count data size
234 * @param mcerror pointer to the error handler
236 * @return 0 on success, negative value otherwise
239 ssize_t
240 sftpfs_read_file (vfs_file_handler_t * file_handler, char *buffer, size_t count, GError ** mcerror)
242 ssize_t rc;
243 sftpfs_file_handler_data_t *file_handler_data;
244 sftpfs_super_data_t *super_data;
246 mc_return_val_if_error (mcerror, -1);
248 if (file_handler == NULL || file_handler->data == NULL)
250 mc_propagate_error (mcerror, 0, "%s",
251 _("sftp: No file handler data present for reading file"));
252 return -1;
255 file_handler_data = file_handler->data;
256 super_data = (sftpfs_super_data_t *) file_handler->ino->super->data;
260 rc = libssh2_sftp_read (file_handler_data->handle, buffer, count);
261 if (rc >= 0)
262 break;
264 if (rc != LIBSSH2_ERROR_EAGAIN)
266 sftpfs_ssherror_to_gliberror (super_data, rc, mcerror);
267 return -1;
270 sftpfs_waitsocket (super_data, mcerror);
271 mc_return_val_if_error (mcerror, -1);
273 while (rc == LIBSSH2_ERROR_EAGAIN);
275 file_handler->pos = (off_t) libssh2_sftp_tell64 (file_handler_data->handle);
277 return rc;
280 /* --------------------------------------------------------------------------------------------- */
283 * Write up to 'count' bytes from the buffer starting at 'buffer' to the descriptor 'file_handler'.
285 * @param file_handler file data handler
286 * @param buffer buffer for data
287 * @param count data size
288 * @param mcerror pointer to the error handler
290 * @return 0 on success, negative value otherwise
293 ssize_t
294 sftpfs_write_file (vfs_file_handler_t * file_handler, const char *buffer, size_t count,
295 GError ** mcerror)
297 ssize_t rc;
298 sftpfs_file_handler_data_t *file_handler_data;
299 sftpfs_super_data_t *super_data;
301 mc_return_val_if_error (mcerror, -1);
303 file_handler_data = (sftpfs_file_handler_data_t *) file_handler->data;
304 super_data = (sftpfs_super_data_t *) file_handler->ino->super->data;
306 file_handler->pos = (off_t) libssh2_sftp_tell64 (file_handler_data->handle);
310 rc = libssh2_sftp_write (file_handler_data->handle, buffer, count);
311 if (rc >= 0)
312 break;
314 if (rc != LIBSSH2_ERROR_EAGAIN)
316 sftpfs_ssherror_to_gliberror (super_data, rc, mcerror);
317 return -1;
320 sftpfs_waitsocket (super_data, mcerror);
321 mc_return_val_if_error (mcerror, -1);
323 while (rc == LIBSSH2_ERROR_EAGAIN);
325 return rc;
328 /* --------------------------------------------------------------------------------------------- */
331 * Close a file descriptor.
333 * @param file_handler file data handler
334 * @param mcerror pointer to the error handler
336 * @return 0 on success, negative value otherwise
340 sftpfs_close_file (vfs_file_handler_t * file_handler, GError ** mcerror)
342 sftpfs_file_handler_data_t *file_handler_data;
344 mc_return_val_if_error (mcerror, -1);
346 file_handler_data = (sftpfs_file_handler_data_t *) file_handler->data;
347 if (file_handler_data == NULL)
348 return -1;
350 libssh2_sftp_close (file_handler_data->handle);
352 g_free (file_handler_data);
353 return 0;
356 /* --------------------------------------------------------------------------------------------- */
359 * Reposition the offset of the open file associated with the file descriptor.
361 * @param file_handler file data handler
362 * @param offset file offset
363 * @param whence method of seek (at begin, at current, at end)
364 * @param mcerror pointer to the error handler
366 * @return 0 on success, negative value otherwise
369 off_t
370 sftpfs_lseek (vfs_file_handler_t * file_handler, off_t offset, int whence, GError ** mcerror)
372 sftpfs_file_handler_data_t *file_handler_data;
374 mc_return_val_if_error (mcerror, 0);
376 switch (whence)
378 case SEEK_SET:
379 /* Need reopen file because:
380 "You MUST NOT seek during writing or reading a file with SFTP, as the internals use
381 outstanding packets and changing the "file position" during transit will results in
382 badness." */
383 if (file_handler->pos > offset || offset == 0)
385 sftpfs_reopen (file_handler, mcerror);
386 mc_return_val_if_error (mcerror, 0);
388 file_handler->pos = offset;
389 break;
390 case SEEK_CUR:
391 file_handler->pos += offset;
392 break;
393 case SEEK_END:
394 if (file_handler->pos > file_handler->ino->st.st_size - offset)
396 sftpfs_reopen (file_handler, mcerror);
397 mc_return_val_if_error (mcerror, 0);
399 file_handler->pos = file_handler->ino->st.st_size - offset;
400 break;
401 default:
402 break;
405 file_handler_data = (sftpfs_file_handler_data_t *) file_handler->data;
407 libssh2_sftp_seek64 (file_handler_data->handle, file_handler->pos);
408 file_handler->pos = (off_t) libssh2_sftp_tell64 (file_handler_data->handle);
410 return file_handler->pos;
413 /* --------------------------------------------------------------------------------------------- */