Changed interface of mc_stat() and mc_lstat() functions
[midnight-commander.git] / lib / vfs / interface.c
blobc41cf29e88407613d306777f7f61992222613f78
1 /*
2 Virtual File System: interface functions
4 Copyright (C) 2011
5 The Free Software Foundation, Inc.
7 Written by:
8 Slava Zanko <slavazanko@gmail.com>, 2011
10 This file is part of the Midnight Commander.
12 The Midnight Commander is free software: you can redistribute it
13 and/or modify it under the terms of the GNU General Public License as
14 published by the Free Software Foundation, either version 3 of the License,
15 or (at your option) any later version.
17 The Midnight Commander is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 GNU General Public License for more details.
22 You should have received a copy of the GNU General Public License
23 along with this program. If not, see <http://www.gnu.org/licenses/>.
26 /**
27 * \file
28 * \brief Source: Virtual File System: path handlers
29 * \author Slava Zanko
30 * \date 2011
34 #include <config.h>
36 #include <stdio.h>
37 #include <stdlib.h> /* For atol() */
38 #include <stdarg.h>
39 #include <string.h>
40 #include <errno.h>
41 #include <sys/types.h>
42 #include <signal.h>
43 #include <ctype.h> /* is_digit() */
44 #include <fcntl.h>
45 #include <sys/stat.h>
46 #include <unistd.h>
47 #include <dirent.h>
49 #include "lib/global.h"
51 #include "lib/widget.h" /* message() */
52 #include "lib/strutil.h" /* str_crt_conv_from() */
54 #include "vfs.h"
55 #include "utilvfs.h"
56 #include "path.h"
57 #include "gc.h"
59 extern GString *vfs_str_buffer;
60 extern struct vfs_class *current_vfs;
62 /*** global variables ****************************************************************************/
64 struct dirent *mc_readdir_result = NULL;
66 /*** file scope macro definitions ****************************************************************/
68 /*** file scope type declarations ****************************************************************/
70 /*** file scope variables ************************************************************************/
72 /*** file scope functions ************************************************************************/
73 /* --------------------------------------------------------------------------------------------- */
75 static char *
76 mc_def_getlocalcopy (const char *filename)
78 char *tmp;
79 int fdin, fdout;
80 ssize_t i;
81 char buffer[8192];
82 struct stat mystat;
83 vfs_path_t *vpath = vfs_path_from_str (filename);
85 fdin = mc_open (filename, O_RDONLY | O_LINEAR);
86 if (fdin == -1)
87 return NULL;
89 fdout = vfs_mkstemps (&tmp, "vfs", filename);
91 if (fdout == -1)
92 goto fail;
93 while ((i = mc_read (fdin, buffer, sizeof (buffer))) > 0)
95 if (write (fdout, buffer, i) != i)
96 goto fail;
98 if (i == -1)
99 goto fail;
100 i = mc_close (fdin);
101 fdin = -1;
102 if (i == -1)
103 goto fail;
104 if (close (fdout) == -1)
106 fdout = -1;
107 goto fail;
110 if (mc_stat (vpath, &mystat) != -1)
111 chmod (tmp, mystat.st_mode);
112 vfs_path_free (vpath);
114 return tmp;
116 fail:
117 vfs_path_free (vpath);
118 if (fdout != -1)
119 close (fdout);
120 if (fdin != -1)
121 mc_close (fdin);
122 g_free (tmp);
123 return NULL;
126 /* --------------------------------------------------------------------------------------------- */
128 static int
129 mc_def_ungetlocalcopy (struct vfs_class *vfs, const char *filename,
130 const char *local, int has_changed)
132 int fdin = -1, fdout = -1;
133 if (has_changed)
135 char buffer[8192];
136 ssize_t i;
138 if (!vfs->write)
139 goto failed;
141 fdin = open (local, O_RDONLY);
142 if (fdin == -1)
143 goto failed;
144 fdout = mc_open (filename, O_WRONLY | O_TRUNC);
145 if (fdout == -1)
146 goto failed;
147 while ((i = read (fdin, buffer, sizeof (buffer))) > 0)
148 if (mc_write (fdout, buffer, (size_t) i) != i)
149 goto failed;
150 if (i == -1)
151 goto failed;
153 if (close (fdin) == -1)
155 fdin = -1;
156 goto failed;
158 fdin = -1;
159 if (mc_close (fdout) == -1)
161 fdout = -1;
162 goto failed;
165 unlink (local);
166 return 0;
168 failed:
169 message (D_ERROR, _("Changes to file lost"), "%s", filename);
170 if (fdout != -1)
171 mc_close (fdout);
172 if (fdin != -1)
173 close (fdin);
174 unlink (local);
175 return -1;
178 /* --------------------------------------------------------------------------------------------- */
179 /*** public functions ****************************************************************************/
180 /* --------------------------------------------------------------------------------------------- */
183 mc_open (const char *filename, int flags, ...)
185 int mode = 0, result = -1;
186 vfs_path_t *vpath;
187 vfs_path_element_t *path_element;
189 vpath = vfs_path_from_str (filename);
190 if (vpath == NULL)
191 return -1;
193 /* Get the mode flag */
194 if (flags & O_CREAT)
196 va_list ap;
197 va_start (ap, flags);
198 mode = va_arg (ap, int);
199 va_end (ap);
202 path_element = vfs_path_get_by_index (vpath, -1);
203 if (vfs_path_element_valid (path_element) && path_element->class->open != NULL)
205 void *info;
206 /* open must be supported */
207 info = path_element->class->open (vpath, flags, mode);
208 if (info == NULL)
209 errno = vfs_ferrno (path_element->class);
210 else
211 result = vfs_new_handle (path_element->class, info);
213 else
214 errno = -EOPNOTSUPP;
216 vfs_path_free (vpath);
217 return result;
220 /* --------------------------------------------------------------------------------------------- */
222 /* *INDENT-OFF* */
224 #define MC_NAMEOP(name, inarg, callarg) \
225 int mc_##name inarg \
227 int result; \
228 vfs_path_t *vpath; \
229 vfs_path_element_t *path_element; \
231 vpath = vfs_path_from_str (path); \
232 if (vpath == NULL) \
233 return -1; \
235 path_element = vfs_path_get_by_index (vpath, -1); \
236 if (!vfs_path_element_valid (path_element)) \
238 vfs_path_free(vpath); \
239 return -1; \
242 result = path_element->class->name != NULL ? path_element->class->name callarg : -1; \
243 if (result == -1) \
244 errno = path_element->class->name != NULL ? vfs_ferrno (path_element->class) : E_NOTSUPP; \
245 vfs_path_free(vpath); \
246 return result; \
249 MC_NAMEOP (chmod, (const char *path, mode_t mode), (vpath, mode))
250 MC_NAMEOP (chown, (const char *path, uid_t owner, gid_t group), (vpath, owner, group))
251 MC_NAMEOP (utime, (const char *path, struct utimbuf * times), (vpath, times))
252 MC_NAMEOP (readlink, (const char *path, char *buf, size_t bufsiz), (vpath, buf, bufsiz))
253 MC_NAMEOP (unlink, (const char *path), (vpath))
254 MC_NAMEOP (mkdir, (const char *path, mode_t mode), (vpath, mode))
255 MC_NAMEOP (rmdir, (const char *path), (vpath))
256 MC_NAMEOP (mknod, (const char *path, mode_t mode, dev_t dev), (vpath, mode, dev))
258 /* *INDENT-ON* */
260 /* --------------------------------------------------------------------------------------------- */
263 mc_symlink (const char *name1, const char *path)
265 int result = -1;
266 vfs_path_t *vpath1, *vpath2;
268 vpath1 = vfs_path_from_str (path);
269 if (vpath1 == NULL)
270 return -1;
272 vpath2 = vfs_path_from_str_flags (name1, VPF_NO_CANON);
274 if (vpath2 != NULL)
276 vfs_path_element_t *path_element = vfs_path_get_by_index (vpath1, -1);
277 if (vfs_path_element_valid (path_element))
279 result =
280 path_element->class->symlink !=
281 NULL ? path_element->class->symlink (vpath2, vpath1) : -1;
283 if (result == -1)
284 errno =
285 path_element->class->symlink !=
286 NULL ? vfs_ferrno (path_element->class) : E_NOTSUPP;
289 vfs_path_free (vpath1);
290 vfs_path_free (vpath2);
291 return result;
294 /* --------------------------------------------------------------------------------------------- */
296 /* *INDENT-OFF* */
298 #define MC_HANDLEOP(name, inarg, callarg) \
299 ssize_t mc_##name inarg \
301 struct vfs_class *vfs; \
302 int result; \
303 if (handle == -1) \
304 return -1; \
305 vfs = vfs_class_find_by_handle (handle); \
306 if (vfs == NULL) \
307 return -1; \
308 result = vfs->name != NULL ? vfs->name callarg : -1; \
309 if (result == -1) \
310 errno = vfs->name != NULL ? vfs_ferrno (vfs) : E_NOTSUPP; \
311 return result; \
314 MC_HANDLEOP (read, (int handle, void *buffer, size_t count), (vfs_class_data_find_by_handle (handle), buffer, count))
315 MC_HANDLEOP (write, (int handle, const void *buf, size_t nbyte), (vfs_class_data_find_by_handle (handle), buf, nbyte))
317 /* --------------------------------------------------------------------------------------------- */
319 #define MC_RENAMEOP(name) \
320 int mc_##name (const char *fname1, const char *fname2) \
322 int result; \
323 vfs_path_t *vpath1, *vpath2; \
324 vfs_path_element_t *path_element1, *path_element2; \
326 vpath1 = vfs_path_from_str (fname1); \
327 if (vpath1 == NULL) \
328 return -1; \
330 vpath2 = vfs_path_from_str (fname2); \
331 if (vpath2 == NULL) \
333 vfs_path_free(vpath1); \
334 return -1; \
336 path_element1 = vfs_path_get_by_index (vpath1, - 1); \
337 path_element2 = vfs_path_get_by_index (vpath2, - 1); \
339 if (!vfs_path_element_valid (path_element1) || !vfs_path_element_valid (path_element2) || \
340 path_element1->class != path_element2->class) \
342 errno = EXDEV; \
343 vfs_path_free(vpath1); \
344 vfs_path_free(vpath2); \
345 return -1; \
348 result = path_element1->class->name != NULL \
349 ? path_element1->class->name (vpath1, vpath2) \
350 : -1; \
351 if (result == -1) \
352 errno = path_element1->class->name != NULL ? vfs_ferrno (path_element1->class) : E_NOTSUPP; \
353 vfs_path_free(vpath1); \
354 vfs_path_free(vpath2); \
355 return result; \
358 MC_RENAMEOP (link)
359 MC_RENAMEOP (rename)
361 /* *INDENT-ON* */
363 /* --------------------------------------------------------------------------------------------- */
366 mc_ctl (int handle, int ctlop, void *arg)
368 struct vfs_class *vfs = vfs_class_find_by_handle (handle);
370 if (vfs == NULL)
371 return 0;
373 return vfs->ctl ? (*vfs->ctl) (vfs_class_data_find_by_handle (handle), ctlop, arg) : 0;
376 /* --------------------------------------------------------------------------------------------- */
379 mc_setctl (const char *path, int ctlop, void *arg)
381 int result = -1;
383 vfs_path_t *vpath;
384 vfs_path_element_t *path_element;
386 vpath = vfs_path_from_str (path);
387 if (vpath == NULL)
388 vfs_die ("You don't want to pass NULL to mc_setctl.");
390 path_element = vfs_path_get_by_index (vpath, -1);
391 if (vfs_path_element_valid (path_element))
392 result =
393 path_element->class->setctl != NULL ? path_element->class->setctl (vpath,
394 ctlop, arg) : 0;
396 vfs_path_free (vpath);
397 return result;
400 /* --------------------------------------------------------------------------------------------- */
403 mc_close (int handle)
405 struct vfs_class *vfs;
406 int result;
408 if (handle == -1 || !vfs_class_data_find_by_handle (handle))
409 return -1;
411 vfs = vfs_class_find_by_handle (handle);
412 if (vfs == NULL)
413 return -1;
415 if (handle < 3)
416 return close (handle);
418 if (!vfs->close)
419 vfs_die ("VFS must support close.\n");
420 result = (*vfs->close) (vfs_class_data_find_by_handle (handle));
421 vfs_free_handle (handle);
422 if (result == -1)
423 errno = vfs_ferrno (vfs);
425 return result;
428 /* --------------------------------------------------------------------------------------------- */
430 DIR *
431 mc_opendir (const char *dirname)
433 int handle, *handlep;
434 void *info;
435 vfs_path_t *vpath;
436 vfs_path_element_t *path_element;
438 vpath = vfs_path_from_str (dirname);
440 if (vpath == NULL)
441 return NULL;
443 path_element = vfs_path_get_by_index (vpath, -1);
445 if (!vfs_path_element_valid (path_element))
447 errno = E_NOTSUPP;
448 vfs_path_free (vpath);
449 return NULL;
452 info = path_element->class->opendir ? (*path_element->class->opendir) (vpath) : NULL;
454 if (info == NULL)
456 errno = path_element->class->opendir ? vfs_ferrno (path_element->class) : E_NOTSUPP;
457 vfs_path_free (vpath);
458 return NULL;
461 path_element->dir.info = info;
463 path_element->dir.converter =
464 (path_element->encoding !=
465 NULL) ? str_crt_conv_from (path_element->encoding) : str_cnv_from_term;
466 if (path_element->dir.converter == INVALID_CONV)
467 path_element->dir.converter = str_cnv_from_term;
469 handle = vfs_new_handle (path_element->class, vfs_path_element_clone (path_element));
471 handlep = g_new (int, 1);
472 *handlep = handle;
473 vfs_path_free (vpath);
474 return (DIR *) handlep;
477 /* --------------------------------------------------------------------------------------------- */
479 struct dirent *
480 mc_readdir (DIR * dirp)
482 int handle;
483 struct vfs_class *vfs;
484 struct dirent *entry = NULL;
485 vfs_path_element_t *vfs_path_element;
486 estr_t state;
488 if (!mc_readdir_result)
490 /* We can't just allocate struct dirent as (see man dirent.h)
491 * struct dirent has VERY nonnaive semantics of allocating
492 * d_name in it. Moreover, linux's glibc-2.9 allocates dirents _less_,
493 * than 'sizeof (struct dirent)' making full bitwise (sizeof dirent) copy
494 * heap corrupter. So, allocate longliving dirent with at least
495 * (MAXNAMLEN + 1) for d_name in it.
496 * Strictly saying resulting dirent is unusable as we don't adjust internal
497 * structures, holding dirent size. But we don't use it in libc infrastructure.
498 * TODO: to make simpler homemade dirent-alike structure.
500 mc_readdir_result = (struct dirent *) g_malloc (sizeof (struct dirent) + MAXNAMLEN + 1);
503 if (!dirp)
505 errno = EFAULT;
506 return NULL;
508 handle = *(int *) dirp;
510 vfs = vfs_class_find_by_handle (handle);
511 if (vfs == NULL)
512 return NULL;
514 vfs_path_element = vfs_class_data_find_by_handle (handle);
515 if (vfs->readdir)
517 entry = (*vfs->readdir) (vfs_path_element->dir.info);
518 if (entry == NULL)
519 return NULL;
520 g_string_set_size (vfs_str_buffer, 0);
521 state =
522 str_vfs_convert_from (vfs_path_element->dir.converter, entry->d_name, vfs_str_buffer);
523 mc_readdir_result->d_ino = entry->d_ino;
524 g_strlcpy (mc_readdir_result->d_name, vfs_str_buffer->str, MAXNAMLEN + 1);
526 if (entry == NULL)
527 errno = vfs->readdir ? vfs_ferrno (vfs) : E_NOTSUPP;
528 return (entry != NULL) ? mc_readdir_result : NULL;
531 /* --------------------------------------------------------------------------------------------- */
534 mc_closedir (DIR * dirp)
536 int handle = *(int *) dirp;
537 struct vfs_class *vfs;
538 int result = -1;
540 vfs = vfs_class_find_by_handle (handle);
541 if (vfs != NULL)
543 vfs_path_element_t *vfs_path_element;
544 vfs_path_element = vfs_class_data_find_by_handle (handle);
545 if (vfs_path_element->dir.converter != str_cnv_from_term)
547 str_close_conv (vfs_path_element->dir.converter);
548 vfs_path_element->dir.converter = INVALID_CONV;
551 result = vfs->closedir ? (*vfs->closedir) (vfs_path_element->dir.info) : -1;
552 vfs_free_handle (handle);
553 vfs_path_element_free (vfs_path_element);
555 g_free (dirp);
556 return result;
559 /* --------------------------------------------------------------------------------------------- */
562 mc_stat (const vfs_path_t * vpath, struct stat *buf)
564 int result = -1;
565 vfs_path_element_t *path_element;
567 if (vpath == NULL)
568 return -1;
570 path_element = vfs_path_get_by_index (vpath, -1);
572 if (vfs_path_element_valid (path_element))
574 result = path_element->class->stat ? (*path_element->class->stat) (vpath, buf) : -1;
575 if (result == -1)
576 errno = path_element->class->name ? vfs_ferrno (path_element->class) : E_NOTSUPP;
579 return result;
582 /* --------------------------------------------------------------------------------------------- */
585 mc_lstat (const vfs_path_t * vpath, struct stat *buf)
587 int result = -1;
588 vfs_path_element_t *path_element;
590 if (vpath == NULL)
591 return -1;
593 path_element = vfs_path_get_by_index (vpath, -1);
595 if (vfs_path_element_valid (path_element))
597 result = path_element->class->lstat ? (*path_element->class->lstat) (vpath, buf) : -1;
598 if (result == -1)
599 errno = path_element->class->name ? vfs_ferrno (path_element->class) : E_NOTSUPP;
602 return result;
605 /* --------------------------------------------------------------------------------------------- */
608 mc_fstat (int handle, struct stat *buf)
610 struct vfs_class *vfs;
611 int result;
613 if (handle == -1)
614 return -1;
616 vfs = vfs_class_find_by_handle (handle);
617 if (vfs == NULL)
618 return -1;
620 result = vfs->fstat ? (*vfs->fstat) (vfs_class_data_find_by_handle (handle), buf) : -1;
621 if (result == -1)
622 errno = vfs->name ? vfs_ferrno (vfs) : E_NOTSUPP;
623 return result;
626 /* --------------------------------------------------------------------------------------------- */
628 * Return current directory. If it's local, reread the current directory
629 * from the OS. Put directory to the provided buffer.
632 char *
633 mc_get_current_wd (char *buffer, size_t size)
635 char *cwd = _vfs_get_cwd ();
637 g_strlcpy (buffer, cwd, size);
638 g_free (cwd);
640 return buffer;
643 /* --------------------------------------------------------------------------------------------- */
645 char *
646 mc_getlocalcopy (const char *pathname)
648 char *result = NULL;
649 vfs_path_t *vpath;
650 vfs_path_element_t *path_element;
652 vpath = vfs_path_from_str (pathname);
653 if (vpath == NULL)
654 return NULL;
656 path_element = vfs_path_get_by_index (vpath, -1);
658 if (vfs_path_element_valid (path_element))
660 result = path_element->class->getlocalcopy != NULL ?
661 path_element->class->getlocalcopy (vpath) : mc_def_getlocalcopy (pathname);
662 if (result == NULL)
663 errno = vfs_ferrno (path_element->class);
665 vfs_path_free (vpath);
666 return result;
669 /* --------------------------------------------------------------------------------------------- */
672 mc_ungetlocalcopy (const char *pathname, const char *local, int has_changed)
674 int return_value = -1;
675 vfs_path_t *vpath;
676 vfs_path_element_t *path_element;
678 vpath = vfs_path_from_str (pathname);
679 if (vpath == NULL)
680 return -1;
682 path_element = vfs_path_get_by_index (vpath, -1);
684 if (vfs_path_element_valid (path_element))
686 return_value = path_element->class->ungetlocalcopy != NULL ?
687 path_element->class->ungetlocalcopy (vpath, local,
688 has_changed) :
689 mc_def_ungetlocalcopy (path_element->class, path_element->path, local, has_changed);
691 vfs_path_free (vpath);
692 return return_value;
695 /* --------------------------------------------------------------------------------------------- */
697 * VFS chdir.
699 * @param vpath VFS-path
701 * @return 0 on success, -1 on failure.
705 mc_chdir (const vfs_path_t * vpath)
707 struct vfs_class *old_vfs;
708 vfsid old_vfsid;
709 int result;
710 vfs_path_element_t *path_element;
712 if (vpath == NULL)
713 return -1;
715 path_element = vfs_path_get_by_index (vpath, -1);
717 if (!vfs_path_element_valid (path_element) || path_element->class->chdir == NULL)
719 return -1;
722 result = (*path_element->class->chdir) (vpath);
724 if (result == -1)
726 errno = vfs_ferrno (path_element->class);
727 return -1;
730 old_vfsid = vfs_getid (vfs_get_raw_current_dir ());
731 old_vfs = current_vfs;
733 /* Actually change directory */
734 vfs_set_raw_current_dir (vfs_path_clone (vpath));
735 current_vfs = path_element->class;
737 /* This function uses the new current_dir implicitly */
738 vfs_stamp_create (old_vfs, old_vfsid);
740 /* Sometimes we assume no trailing slash on cwd */
741 path_element = vfs_path_get_by_index (vfs_get_raw_current_dir (), -1);
742 if (vfs_path_element_valid (path_element) && (*path_element->path != '\0'))
744 char *p;
745 p = strchr (path_element->path, 0) - 1;
746 if (*p == PATH_SEP && p > path_element->path)
747 *p = 0;
749 return 0;
752 /* --------------------------------------------------------------------------------------------- */
754 off_t
755 mc_lseek (int fd, off_t offset, int whence)
757 struct vfs_class *vfs;
758 off_t result;
760 if (fd == -1)
761 return -1;
763 vfs = vfs_class_find_by_handle (fd);
764 if (vfs == NULL)
765 return -1;
767 result = vfs->lseek ? (*vfs->lseek) (vfs_class_data_find_by_handle (fd), offset, whence) : -1;
768 if (result == -1)
769 errno = vfs->lseek ? vfs_ferrno (vfs) : E_NOTSUPP;
770 return result;
773 /* --------------------------------------------------------------------------------------------- */