Merge branch '2121_dir_symlink'
[kaloumi3.git] / lib / vfs / mc-vfs / vfs.c
blob0ed1bdbd8fb2cb81151b309cbd89e2fb79c0a56f
1 /* Virtual File System switch code
2 Copyright (C) 1995, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
3 2007 Free Software Foundation, Inc.
5 Written by: 1995 Miguel de Icaza
6 1995 Jakub Jelinek
7 1998 Pavel Machek
9 This program is free software; you can redistribute it and/or
10 modify it under the terms of the GNU Library General Public License
11 as published by the Free Software Foundation; either version 2 of
12 the License, or (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU Library General Public License for more details.
19 You should have received a copy of the GNU Library General Public
20 License along with this program; if not, write to the Free Software
21 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
23 /**
24 * \file
25 * \brief Source: Virtual File System switch code
26 * \author Miguel de Icaza
27 * \author Jakub Jelinek
28 * \author Pavel Machek
29 * \date 1995, 1998
30 * \warning funtions like extfs_lstat() have right to destroy any
31 * strings you pass to them. This is acutally ok as you g_strdup what
32 * you are passing to them, anyway; still, beware.
34 * Namespace: exports *many* functions with vfs_ prefix; exports
35 * parse_ls_lga and friends which do not have that prefix.
38 #include <config.h>
40 #include <stdio.h>
41 #include <stdlib.h> /* For atol() */
42 #include <stdarg.h>
43 #include <string.h>
44 #include <errno.h>
45 #include <sys/types.h>
46 #include <signal.h>
47 #include <ctype.h> /* is_digit() */
48 #include <fcntl.h>
49 #include <sys/stat.h>
50 #include <unistd.h>
51 #include <dirent.h>
53 #include "lib/global.h"
54 #include "lib/strutil.h"
56 #include "src/wtools.h" /* message() */
57 #include "src/main.h" /* print_vfs_message */
59 #include "utilvfs.h"
60 #include "gc.h"
61 #include "vfs.h"
62 #ifdef USE_NETCODE
63 # include "netutil.h"
64 #endif
65 #include "ftpfs.h"
66 #include "mcfs.h"
67 #include "smbfs.h"
68 #include "local.h"
70 #if defined(_AIX) && !defined(NAME_MAX)
71 # define NAME_MAX FILENAME_MAX
72 #endif
74 /** They keep track of the current directory */
75 static struct vfs_class *current_vfs;
76 static char *current_dir;
78 struct vfs_openfile
80 int handle;
81 struct vfs_class *vclass;
82 void *fsinfo;
85 struct vfs_dirinfo
87 DIR *info;
88 GIConv converter;
92 static GPtrArray *vfs_openfiles;
93 static long vfs_free_handle_list = -1;
94 #define VFS_FIRST_HANDLE 100
96 static struct vfs_class *localfs_class;
97 static GString *vfs_str_buffer;
99 static const char *supported_encodings[] = {
100 "UTF8",
101 "UTF-8",
102 "BIG5",
103 "ASCII",
104 "ISO8859",
105 "ISO-8859",
106 "ISO_8859",
107 "KOI8",
108 "CP852",
109 "CP866",
110 "CP125",
111 NULL
114 /** Create new VFS handle and put it to the list */
115 static int
116 vfs_new_handle (struct vfs_class *vclass, void *fsinfo)
118 struct vfs_openfile *h;
120 h = g_new (struct vfs_openfile, 1);
121 h->fsinfo = fsinfo;
122 h->vclass = vclass;
124 /* Allocate the first free handle */
125 h->handle = vfs_free_handle_list;
126 if (h->handle == -1)
128 /* No free allocated handles, allocate one */
129 h->handle = vfs_openfiles->len;
130 g_ptr_array_add (vfs_openfiles, h);
132 else
134 vfs_free_handle_list = (long) g_ptr_array_index (vfs_openfiles, vfs_free_handle_list);
135 g_ptr_array_index (vfs_openfiles, h->handle) = h;
138 h->handle += VFS_FIRST_HANDLE;
139 return h->handle;
142 /** Find VFS class by file handle */
143 static struct vfs_class *
144 vfs_op (int handle)
146 struct vfs_openfile *h;
148 if (handle < VFS_FIRST_HANDLE || (guint) (handle - VFS_FIRST_HANDLE) >= vfs_openfiles->len)
149 return NULL;
151 h = (struct vfs_openfile *) g_ptr_array_index (vfs_openfiles, handle - VFS_FIRST_HANDLE);
152 if (!h)
153 return NULL;
155 g_assert (h->handle == handle);
157 return h->vclass;
160 /** Find private file data by file handle */
161 static void *
162 vfs_info (int handle)
164 struct vfs_openfile *h;
166 if (handle < VFS_FIRST_HANDLE || (guint) (handle - VFS_FIRST_HANDLE) >= vfs_openfiles->len)
167 return NULL;
169 h = (struct vfs_openfile *) g_ptr_array_index (vfs_openfiles, handle - VFS_FIRST_HANDLE);
170 if (!h)
171 return NULL;
173 g_assert (h->handle == handle);
175 return h->fsinfo;
178 /** Free open file data for given file handle */
179 static void
180 vfs_free_handle (int handle)
182 const int idx = handle - VFS_FIRST_HANDLE;
184 if (handle >= VFS_FIRST_HANDLE && (guint) idx < vfs_openfiles->len)
186 struct vfs_openfile *h;
188 h = (struct vfs_openfile *) g_ptr_array_index (vfs_openfiles, idx);
189 g_free (h);
190 g_ptr_array_index (vfs_openfiles, idx) = (void *) vfs_free_handle_list;
191 vfs_free_handle_list = idx;
195 static struct vfs_class *vfs_list;
198 vfs_register_class (struct vfs_class *vfs)
200 if (vfs->init) /* vfs has own initialization function */
201 if (!(*vfs->init) (vfs)) /* but it failed */
202 return 0;
204 vfs->next = vfs_list;
205 vfs_list = vfs;
207 return 1;
210 /** Return VFS class for the given prefix */
211 static struct vfs_class *
212 vfs_prefix_to_class (char *prefix)
214 struct vfs_class *vfs;
216 /* Avoid last class (localfs) that would accept any prefix */
217 for (vfs = vfs_list; vfs->next; vfs = vfs->next)
219 if (vfs->which)
221 if ((*vfs->which) (vfs, prefix) == -1)
222 continue;
223 return vfs;
225 if (vfs->prefix && !strncmp (prefix, vfs->prefix, strlen (vfs->prefix)))
226 return vfs;
228 return NULL;
231 /** Strip known vfs suffixes from a filename (possible improvement: strip
232 * suffix from last path component).
233 * \return a malloced string which has to be freed.
235 char *
236 vfs_strip_suffix_from_filename (const char *filename)
238 struct vfs_class *vfs;
239 char *semi;
240 char *p;
242 if (!filename)
243 vfs_die ("vfs_strip_suffix_from_path got NULL: impossible");
245 p = g_strdup (filename);
246 if (!(semi = strrchr (p, '#')))
247 return p;
249 /* Avoid last class (localfs) that would accept any prefix */
250 for (vfs = vfs_list; vfs->next; vfs = vfs->next)
252 if (vfs->which)
254 if ((*vfs->which) (vfs, semi + 1) == -1)
255 continue;
256 *semi = '\0'; /* Found valid suffix */
257 return p;
259 if (vfs->prefix && !strncmp (semi + 1, vfs->prefix, strlen (vfs->prefix)))
261 *semi = '\0'; /* Found valid suffix */
262 return p;
265 return p;
268 static int
269 path_magic (const char *path)
271 struct stat buf;
273 if (!stat (path, &buf))
274 return 0;
276 return 1;
280 * Splits path extracting vfs part.
282 * Splits path
283 * \verbatim /p1#op/inpath \endverbatim
284 * into
285 * \verbatim inpath,op; \endverbatim
286 * returns which vfs it is.
287 * What is left in path is p1. You still want to g_free(path), you DON'T
288 * want to free neither *inpath nor *op
290 struct vfs_class *
291 vfs_split (char *path, char **inpath, char **op)
293 char *semi;
294 char *slash;
295 struct vfs_class *ret;
297 if (!path)
298 vfs_die ("Cannot split NULL");
300 semi = strrchr (path, '#');
301 if (!semi || !path_magic (path))
302 return NULL;
304 slash = strchr (semi, PATH_SEP);
305 *semi = 0;
307 if (op)
308 *op = NULL;
310 if (inpath)
311 *inpath = NULL;
313 if (slash)
314 *slash = 0;
316 if ((ret = vfs_prefix_to_class (semi + 1)))
318 if (op)
319 *op = semi + 1;
320 if (inpath)
321 *inpath = slash ? slash + 1 : NULL;
322 return ret;
326 if (slash)
327 *slash = PATH_SEP;
328 ret = vfs_split (path, inpath, op);
329 *semi = '#';
330 return ret;
333 static struct vfs_class *
334 _vfs_get_class (char *path)
336 char *semi;
337 char *slash;
338 struct vfs_class *ret;
340 g_return_val_if_fail (path, NULL);
342 semi = strrchr (path, '#');
343 if (!semi || !path_magic (path))
344 return NULL;
346 slash = strchr (semi, PATH_SEP);
347 *semi = 0;
348 if (slash)
349 *slash = 0;
351 ret = vfs_prefix_to_class (semi + 1);
353 if (slash)
354 *slash = PATH_SEP;
355 if (!ret)
356 ret = _vfs_get_class (path);
358 *semi = '#';
359 return ret;
362 struct vfs_class *
363 vfs_get_class (const char *pathname)
365 struct vfs_class *vfs;
366 char *path = g_strdup (pathname);
368 vfs = _vfs_get_class (path);
369 g_free (path);
371 if (!vfs)
372 vfs = localfs_class;
374 return vfs;
377 const char *
378 vfs_get_encoding (const char *path)
380 static char result[16];
381 char *work;
382 char *semi;
383 char *slash;
385 work = g_strdup (path);
386 semi = g_strrstr (work, "#enc:");
388 if (semi != NULL)
390 semi += 5 * sizeof (char);
391 slash = strchr (semi, PATH_SEP);
392 if (slash != NULL)
393 slash[0] = '\0';
395 g_strlcpy (result, semi, sizeof (result));
396 g_free (work);
397 return result;
399 else
401 g_free (work);
402 return NULL;
406 /* return if encoding can by used in vfs (is ascci full compactible) */
407 /* contains only a few encoding now */
408 static int
409 vfs_supported_enconding (const char *encoding)
411 int t;
412 int result = 0;
414 for (t = 0; supported_encodings[t] != NULL; t++)
416 result += (g_ascii_strncasecmp (encoding, supported_encodings[t],
417 strlen (supported_encodings[t])) == 0);
420 return result;
423 /* now used only by vfs_translate_path, but could be used in other vfs
424 * plugin to automatic detect encoding
425 * path - path to translate
426 * size - how many bytes from path translate
427 * defcnv - convertor, that is used as default, when path does not contain any
428 * #enc: subtring
429 * buffer - used to store result of translation
431 static estr_t
432 _vfs_translate_path (const char *path, int size, GIConv defcnv, GString * buffer)
434 const char *semi;
435 const char *ps;
436 const char *slash;
437 estr_t state = ESTR_SUCCESS;
438 static char encoding[16];
439 GIConv coder;
440 int ms;
442 if (size == 0)
443 return 0;
444 size = (size > 0) ? size : (signed int) strlen (path);
446 /* try found #end: */
447 semi = g_strrstr_len (path, size, "#enc:");
448 if (semi != NULL)
450 /* first must be translated part before #enc: */
451 ms = semi - path;
453 /* remove '/' before #enc */
454 ps = str_cget_prev_char (semi);
455 if (ps[0] == PATH_SEP)
456 ms = ps - path;
458 state = _vfs_translate_path (path, ms, defcnv, buffer);
460 if (state != ESTR_SUCCESS)
461 return state;
462 /* now can be translated part after #enc: */
464 semi += 5;
465 slash = strchr (semi, PATH_SEP);
466 /* ignore slashes after size; */
467 if (slash - path >= size)
468 slash = NULL;
470 ms = (slash != NULL) ? slash - semi : (int) strlen (semi);
471 ms = min ((unsigned int) ms, sizeof (encoding) - 1);
472 /* limit encoding size (ms) to path size (size) */
473 if (semi + ms > path + size)
474 ms = path + size - semi;
475 memcpy (encoding, semi, ms);
476 encoding[ms] = '\0';
478 switch (vfs_supported_enconding (encoding))
480 case 1:
481 coder = str_crt_conv_to (encoding);
482 if (coder != INVALID_CONV)
484 if (slash != NULL)
486 state = str_vfs_convert_to (coder, slash, path + size - slash, buffer);
488 else if (buffer->str[0] == '\0')
490 /* exmaple "/#enc:utf-8" */
491 g_string_append_c (buffer, PATH_SEP);
493 str_close_conv (coder);
494 return state;
496 else
498 errno = EINVAL;
499 return ESTR_FAILURE;
501 break;
502 default:
503 errno = EINVAL;
504 return ESTR_FAILURE;
507 else
509 /* path can be translated whole at once */
510 state = str_vfs_convert_to (defcnv, path, size, buffer);
511 return state;
514 return ESTR_SUCCESS;
517 char *
518 vfs_translate_path (const char *path)
520 estr_t state;
522 g_string_set_size (vfs_str_buffer, 0);
523 state = _vfs_translate_path (path, -1, str_cnv_from_term, vfs_str_buffer);
525 strict version
526 return (state == 0) ? vfs_str_buffer->data : NULL;
528 return (state != ESTR_FAILURE) ? vfs_str_buffer->str : NULL;
531 char *
532 vfs_translate_path_n (const char *path)
534 char *result;
536 result = vfs_translate_path (path);
537 return (result != NULL) ? g_strdup (result) : NULL;
540 char *
541 vfs_canon_and_translate (const char *path)
543 char *canon;
544 char *result;
545 if (path == NULL)
546 canon = g_strdup ("");
547 else
548 canon = vfs_canon (path);
549 result = vfs_translate_path_n (canon);
550 g_free (canon);
551 return result;
554 static int
555 ferrno (struct vfs_class *vfs)
557 return vfs->ferrno ? (*vfs->ferrno) (vfs) : E_UNKNOWN;
558 /* Hope that error message is obscure enough ;-) */
562 mc_open (const char *filename, int flags, ...)
564 int mode;
565 void *info;
566 va_list ap;
568 char *file = vfs_canon_and_translate (filename);
569 if (file != NULL)
571 struct vfs_class *vfs = vfs_get_class (file);
573 /* Get the mode flag */
574 if (flags & O_CREAT)
576 va_start (ap, flags);
577 mode = va_arg (ap, int);
578 va_end (ap);
580 else
581 mode = 0;
583 if (!vfs->open)
585 g_free (file);
586 errno = -EOPNOTSUPP;
587 return -1;
590 info = (*vfs->open) (vfs, file, flags, mode); /* open must be supported */
591 g_free (file);
592 if (!info)
594 errno = ferrno (vfs);
595 return -1;
598 return vfs_new_handle (vfs, info);
600 else
601 return -1;
605 #define MC_NAMEOP(name, inarg, callarg) \
606 int mc_##name inarg \
608 struct vfs_class *vfs; \
609 int result; \
610 char *mpath = vfs_canon_and_translate (path); \
611 if (mpath != NULL) { \
612 vfs = vfs_get_class (mpath); \
613 if (vfs == NULL){ \
614 g_free (mpath); \
615 return -1; \
617 result = vfs->name ? (*vfs->name)callarg : -1; \
618 g_free (mpath); \
619 if (result == -1) \
620 errno = vfs->name ? ferrno (vfs) : E_NOTSUPP; \
621 return result; \
622 } else return -1; \
625 MC_NAMEOP (chmod, (const char *path, mode_t mode), (vfs, mpath, mode))
626 MC_NAMEOP (chown, (const char *path, uid_t owner, gid_t group), (vfs, mpath, owner, group))
627 MC_NAMEOP (utime, (const char *path, struct utimbuf * times), (vfs, mpath, times))
628 MC_NAMEOP (readlink, (const char *path, char *buf, int bufsiz), (vfs, mpath, buf, bufsiz))
629 MC_NAMEOP (unlink, (const char *path), (vfs, mpath))
630 MC_NAMEOP (mkdir, (const char *path, mode_t mode), (vfs, mpath, mode))
631 MC_NAMEOP (rmdir, (const char *path), (vfs, mpath))
632 MC_NAMEOP (mknod, (const char *path, mode_t mode, dev_t dev), (vfs, mpath, mode, dev))
633 int mc_symlink (const char *name1, const char *path)
635 struct vfs_class *vfs;
636 int result;
637 char *mpath;
638 char *lpath;
639 char *tmp;
641 mpath = vfs_canon_and_translate (path);
642 if (mpath != NULL)
644 tmp = g_strdup (name1);
645 lpath = vfs_translate_path_n (tmp);
646 g_free (tmp);
648 if (lpath != NULL)
650 vfs = vfs_get_class (mpath);
651 result = vfs->symlink ? (*vfs->symlink) (vfs, lpath, mpath) : -1;
652 g_free (lpath);
653 g_free (mpath);
655 if (result == -1)
656 errno = vfs->symlink ? ferrno (vfs) : E_NOTSUPP;
657 return result;
659 g_free (mpath);
661 return -1;
664 #define MC_HANDLEOP(name, inarg, callarg) \
665 ssize_t mc_##name inarg \
667 struct vfs_class *vfs; \
668 int result; \
669 if (handle == -1) \
670 return -1; \
671 vfs = vfs_op (handle); \
672 if (vfs == NULL) \
673 return -1; \
674 result = vfs->name ? (*vfs->name)callarg : -1; \
675 if (result == -1) \
676 errno = vfs->name ? ferrno (vfs) : E_NOTSUPP; \
677 return result; \
680 MC_HANDLEOP (read, (int handle, void *buffer, int count), (vfs_info (handle), buffer, count))
681 MC_HANDLEOP (write, (int handle, const void *buf, int nbyte), (vfs_info (handle), buf, nbyte))
682 #define MC_RENAMEOP(name) \
683 int mc_##name (const char *fname1, const char *fname2) \
685 struct vfs_class *vfs; \
686 int result; \
687 char *name2, *name1; \
688 name1 = vfs_canon_and_translate (fname1); \
689 if (name1 != NULL) { \
690 name2 = vfs_canon_and_translate (fname2); \
691 if (name2 != NULL) { \
692 vfs = vfs_get_class (name1); \
693 if (vfs != vfs_get_class (name2)){ \
694 errno = EXDEV; \
695 g_free (name1); \
696 g_free (name2); \
697 return -1; \
699 result = vfs->name ? (*vfs->name)(vfs, name1, name2) : -1; \
700 g_free (name1); \
701 g_free (name2); \
702 if (result == -1) \
703 errno = vfs->name ? ferrno (vfs) : E_NOTSUPP; \
704 return result; \
705 } else { \
706 g_free (name1); \
707 return -1; \
709 } else return -1; \
711 MC_RENAMEOP (link) MC_RENAMEOP (rename)
713 mc_ctl (int handle, int ctlop, void *arg)
715 struct vfs_class *vfs = vfs_op (handle);
717 if (vfs == NULL)
718 return 0;
720 return vfs->ctl ? (*vfs->ctl) (vfs_info (handle), ctlop, arg) : 0;
724 mc_setctl (const char *path, int ctlop, void *arg)
726 struct vfs_class *vfs;
727 int result;
728 char *mpath;
730 if (!path)
731 vfs_die ("You don't want to pass NULL to mc_setctl.");
733 mpath = vfs_canon_and_translate (path);
734 if (mpath != NULL)
736 vfs = vfs_get_class (mpath);
737 result = vfs->setctl ? (*vfs->setctl) (vfs, mpath, ctlop, arg) : 0;
738 g_free (mpath);
739 return result;
741 else
742 return -1;
746 mc_close (int handle)
748 struct vfs_class *vfs;
749 int result;
751 if (handle == -1 || !vfs_info (handle))
752 return -1;
754 vfs = vfs_op (handle);
755 if (vfs == NULL)
756 return -1;
758 if (handle < 3)
759 return close (handle);
761 if (!vfs->close)
762 vfs_die ("VFS must support close.\n");
763 result = (*vfs->close) (vfs_info (handle));
764 vfs_free_handle (handle);
765 if (result == -1)
766 errno = ferrno (vfs);
768 return result;
771 DIR *
772 mc_opendir (const char *dirname)
774 int handle, *handlep;
775 void *info;
776 struct vfs_class *vfs;
777 char *canon;
778 char *dname;
779 struct vfs_dirinfo *dirinfo;
780 const char *encoding;
782 canon = vfs_canon (dirname);
783 dname = vfs_translate_path_n (canon);
785 if (dname != NULL)
787 vfs = vfs_get_class (dname);
788 info = vfs->opendir ? (*vfs->opendir) (vfs, dname) : NULL;
789 g_free (dname);
791 if (info == NULL)
793 errno = vfs->opendir ? ferrno (vfs) : E_NOTSUPP;
794 g_free (canon);
795 return NULL;
798 dirinfo = g_new (struct vfs_dirinfo, 1);
799 dirinfo->info = info;
801 encoding = vfs_get_encoding (canon);
802 g_free (canon);
803 dirinfo->converter = (encoding != NULL) ? str_crt_conv_from (encoding) : str_cnv_from_term;
804 if (dirinfo->converter == INVALID_CONV)
805 dirinfo->converter = str_cnv_from_term;
807 handle = vfs_new_handle (vfs, dirinfo);
809 handlep = g_new (int, 1);
810 *handlep = handle;
811 return (DIR *) handlep;
813 else
815 g_free (canon);
816 return NULL;
820 static struct dirent *mc_readdir_result = NULL;
822 struct dirent *
823 mc_readdir (DIR * dirp)
825 int handle;
826 struct vfs_class *vfs;
827 struct dirent *entry = NULL;
828 struct vfs_dirinfo *dirinfo;
829 estr_t state;
831 if (!mc_readdir_result)
833 /* We can't just allocate struct dirent as (see man dirent.h)
834 * struct dirent has VERY nonnaive semantics of allocating
835 * d_name in it. Moreover, linux's glibc-2.9 allocates dirents _less_,
836 * than 'sizeof (struct dirent)' making full bitwise (sizeof dirent) copy
837 * heap corrupter. So, allocate longliving dirent with at least
838 * (MAXNAMLEN + 1) for d_name in it.
839 * Strictly saying resulting dirent is unusable as we don't adjust internal
840 * structures, holding dirent size. But we don't use it in libc infrastructure.
841 * TODO: to make simpler homemade dirent-alike structure.
843 mc_readdir_result = (struct dirent *) g_malloc (sizeof (struct dirent) + MAXNAMLEN + 1);
846 if (!dirp)
848 errno = EFAULT;
849 return NULL;
851 handle = *(int *) dirp;
853 vfs = vfs_op (handle);
854 if (vfs == NULL)
855 return NULL;
857 dirinfo = vfs_info (handle);
858 if (vfs->readdir)
860 entry = (*vfs->readdir) (dirinfo->info);
861 if (entry == NULL)
862 return NULL;
863 g_string_set_size (vfs_str_buffer, 0);
864 state = str_vfs_convert_from (dirinfo->converter, entry->d_name, vfs_str_buffer);
865 mc_readdir_result->d_ino = entry->d_ino;
866 g_strlcpy (mc_readdir_result->d_name, vfs_str_buffer->str, MAXNAMLEN + 1);
868 if (entry == NULL)
869 errno = vfs->readdir ? ferrno (vfs) : E_NOTSUPP;
870 return (entry != NULL) ? mc_readdir_result : NULL;
874 mc_closedir (DIR * dirp)
876 int handle = *(int *) dirp;
877 struct vfs_class *vfs;
878 int result = -1;
880 vfs = vfs_op (handle);
881 if (vfs != NULL)
883 struct vfs_dirinfo *dirinfo;
885 dirinfo = vfs_info (handle);
886 if (dirinfo->converter != str_cnv_from_term)
887 str_close_conv (dirinfo->converter);
889 result = vfs->closedir ? (*vfs->closedir) (dirinfo->info) : -1;
890 vfs_free_handle (handle);
891 g_free (dirinfo);
893 g_free (dirp);
894 return result;
898 mc_stat (const char *filename, struct stat *buf)
900 struct vfs_class *vfs;
901 int result;
902 char *path;
904 path = vfs_canon_and_translate (filename);
906 if (path == NULL)
907 return -1;
909 vfs = vfs_get_class (path);
911 if (vfs == NULL)
913 g_free (path);
914 return -1;
917 result = vfs->stat ? (*vfs->stat) (vfs, path, buf) : -1;
919 g_free (path);
921 if (result == -1)
922 errno = vfs->name ? ferrno (vfs) : E_NOTSUPP;
923 return result;
927 mc_lstat (const char *filename, struct stat *buf)
929 struct vfs_class *vfs;
930 int result;
931 char *path;
933 path = vfs_canon_and_translate (filename);
935 if (path == NULL)
936 return -1;
938 vfs = vfs_get_class (path);
939 if (vfs == NULL)
941 g_free (path);
942 return -1;
945 result = vfs->lstat ? (*vfs->lstat) (vfs, path, buf) : -1;
946 g_free (path);
947 if (result == -1)
948 errno = vfs->name ? ferrno (vfs) : E_NOTSUPP;
949 return result;
953 mc_fstat (int handle, struct stat *buf)
955 struct vfs_class *vfs;
956 int result;
958 if (handle == -1)
959 return -1;
961 vfs = vfs_op (handle);
962 if (vfs == NULL)
963 return -1;
965 result = vfs->fstat ? (*vfs->fstat) (vfs_info (handle), buf) : -1;
966 if (result == -1)
967 errno = vfs->name ? ferrno (vfs) : E_NOTSUPP;
968 return result;
972 * Return current directory. If it's local, reread the current directory
973 * from the OS. You must g_strdup() whatever this function returns.
975 static const char *
976 _vfs_get_cwd (void)
978 char *trans;
980 trans = vfs_translate_path_n (current_dir);
982 if (_vfs_get_class (trans) == NULL)
984 const char *encoding = vfs_get_encoding (current_dir);
986 if (encoding == NULL)
988 char *tmp;
990 tmp = g_get_current_dir ();
991 if (tmp != NULL)
992 { /* One of the directories in the path is not readable */
993 estr_t state;
994 char *sys_cwd;
996 g_string_set_size (vfs_str_buffer, 0);
997 state = str_vfs_convert_from (str_cnv_from_term, tmp, vfs_str_buffer);
998 g_free (tmp);
1000 sys_cwd = (state == ESTR_SUCCESS) ? g_strdup (vfs_str_buffer->str) : NULL;
1001 if (sys_cwd != NULL)
1003 struct stat my_stat, my_stat2;
1004 /* Check if it is O.K. to use the current_dir */
1005 if (cd_symlinks
1006 && mc_stat (sys_cwd, &my_stat) == 0
1007 && mc_stat (current_dir, &my_stat2) == 0
1008 && my_stat.st_ino == my_stat2.st_ino && my_stat.st_dev == my_stat2.st_dev)
1009 g_free (sys_cwd);
1010 else
1012 g_free (current_dir);
1013 current_dir = sys_cwd;
1020 g_free (trans);
1021 return current_dir;
1024 static void
1025 vfs_setup_wd (void)
1027 current_dir = g_strdup (PATH_SEP_STR);
1028 _vfs_get_cwd ();
1030 if (strlen (current_dir) > MC_MAXPATHLEN - 2)
1031 vfs_die ("Current dir too long.\n");
1033 current_vfs = vfs_get_class (current_dir);
1037 * Return current directory. If it's local, reread the current directory
1038 * from the OS. Put directory to the provided buffer.
1040 char *
1041 mc_get_current_wd (char *buffer, int size)
1043 const char *cwd = _vfs_get_cwd ();
1045 g_strlcpy (buffer, cwd, size);
1046 return buffer;
1050 * Return current directory without any OS calls.
1052 char *
1053 vfs_get_current_dir (void)
1055 return current_dir;
1058 off_t
1059 mc_lseek (int fd, off_t offset, int whence)
1061 struct vfs_class *vfs;
1062 int result;
1064 if (fd == -1)
1065 return -1;
1067 vfs = vfs_op (fd);
1068 if (vfs == NULL)
1069 return -1;
1071 result = vfs->lseek ? (*vfs->lseek) (vfs_info (fd), offset, whence) : -1;
1072 if (result == -1)
1073 errno = vfs->lseek ? ferrno (vfs) : E_NOTSUPP;
1074 return result;
1078 * remove //, /./ and /../
1081 #define ISSLASH(a) (!a || (a == '/'))
1083 char *
1084 vfs_canon (const char *path)
1086 if (!path)
1087 vfs_die ("Cannot canonicalize NULL");
1089 /* Relative to current directory */
1090 if (*path != PATH_SEP)
1092 char *local, *result;
1094 local = concat_dir_and_file (current_dir, path);
1096 result = vfs_canon (local);
1097 g_free (local);
1098 return result;
1102 * So we have path of following form:
1103 * /p1/p2#op/.././././p3#op/p4. Good luck.
1106 char *result = g_strdup (path);
1107 canonicalize_pathname (result);
1108 return result;
1113 * VFS chdir.
1114 * Return 0 on success, -1 on failure.
1117 mc_chdir (const char *path)
1119 char *new_dir;
1120 char *trans_dir;
1121 struct vfs_class *old_vfs, *new_vfs;
1122 vfsid old_vfsid;
1123 int result;
1125 new_dir = vfs_canon (path);
1126 trans_dir = vfs_translate_path_n (new_dir);
1127 if (trans_dir != NULL)
1129 new_vfs = vfs_get_class (trans_dir);
1130 if (!new_vfs->chdir)
1132 g_free (new_dir);
1133 g_free (trans_dir);
1134 return -1;
1137 result = (*new_vfs->chdir) (new_vfs, trans_dir);
1139 if (result == -1)
1141 errno = ferrno (new_vfs);
1142 g_free (new_dir);
1143 g_free (trans_dir);
1144 return -1;
1147 old_vfsid = vfs_getid (current_vfs, current_dir);
1148 old_vfs = current_vfs;
1150 /* Actually change directory */
1151 g_free (current_dir);
1152 current_dir = new_dir;
1153 current_vfs = new_vfs;
1155 /* This function uses the new current_dir implicitly */
1156 vfs_stamp_create (old_vfs, old_vfsid);
1158 /* Sometimes we assume no trailing slash on cwd */
1159 if (*current_dir)
1161 char *p;
1162 p = strchr (current_dir, 0) - 1;
1163 if (*p == PATH_SEP && p > current_dir)
1164 *p = 0;
1167 g_free (trans_dir);
1168 return 0;
1170 else
1172 g_free (new_dir);
1173 return -1;
1177 /* Return 1 is the current VFS class is local */
1179 vfs_current_is_local (void)
1181 return (current_vfs->flags & VFSF_LOCAL) != 0;
1184 /* Return flags of the VFS class of the given filename */
1186 vfs_file_class_flags (const char *filename)
1188 struct vfs_class *vfs;
1189 char *fname;
1191 fname = vfs_canon_and_translate (filename);
1192 if (fname != NULL)
1194 vfs = vfs_get_class (fname);
1195 g_free (fname);
1196 return vfs->flags;
1198 else
1199 return -1;
1202 static char *
1203 mc_def_getlocalcopy (const char *filename)
1205 char *tmp;
1206 int fdin, fdout;
1207 ssize_t i;
1208 char buffer[8192];
1209 struct stat mystat;
1211 fdin = mc_open (filename, O_RDONLY | O_LINEAR);
1212 if (fdin == -1)
1213 return NULL;
1215 fdout = vfs_mkstemps (&tmp, "vfs", filename);
1217 if (fdout == -1)
1218 goto fail;
1219 while ((i = mc_read (fdin, buffer, sizeof (buffer))) > 0)
1221 if (write (fdout, buffer, i) != i)
1222 goto fail;
1224 if (i == -1)
1225 goto fail;
1226 i = mc_close (fdin);
1227 fdin = -1;
1228 if (i == -1)
1229 goto fail;
1230 if (close (fdout) == -1)
1232 fdout = -1;
1233 goto fail;
1236 if (mc_stat (filename, &mystat) != -1)
1238 chmod (tmp, mystat.st_mode);
1240 return tmp;
1242 fail:
1243 if (fdout != -1)
1244 close (fdout);
1245 if (fdin != -1)
1246 mc_close (fdin);
1247 g_free (tmp);
1248 return NULL;
1251 char *
1252 mc_getlocalcopy (const char *pathname)
1254 char *result;
1255 char *path;
1257 path = vfs_canon_and_translate (pathname);
1258 if (path != NULL)
1260 struct vfs_class *vfs = vfs_get_class (path);
1262 result = vfs->getlocalcopy ? (*vfs->getlocalcopy) (vfs, path) : mc_def_getlocalcopy (path);
1263 g_free (path);
1264 if (!result)
1265 errno = ferrno (vfs);
1266 return result;
1268 else
1269 return NULL;
1272 static int
1273 mc_def_ungetlocalcopy (struct vfs_class *vfs, const char *filename,
1274 const char *local, int has_changed)
1276 int fdin = -1, fdout = -1, i;
1277 if (has_changed)
1279 char buffer[8192];
1281 if (!vfs->write)
1282 goto failed;
1284 fdin = open (local, O_RDONLY);
1285 if (fdin == -1)
1286 goto failed;
1287 fdout = mc_open (filename, O_WRONLY | O_TRUNC);
1288 if (fdout == -1)
1289 goto failed;
1290 while ((i = read (fdin, buffer, sizeof (buffer))) > 0)
1292 if (mc_write (fdout, buffer, i) != i)
1293 goto failed;
1295 if (i == -1)
1296 goto failed;
1298 if (close (fdin) == -1)
1300 fdin = -1;
1301 goto failed;
1303 fdin = -1;
1304 if (mc_close (fdout) == -1)
1306 fdout = -1;
1307 goto failed;
1310 unlink (local);
1311 return 0;
1313 failed:
1314 message (D_ERROR, _("Changes to file lost"), "%s", filename);
1315 if (fdout != -1)
1316 mc_close (fdout);
1317 if (fdin != -1)
1318 close (fdin);
1319 unlink (local);
1320 return -1;
1324 mc_ungetlocalcopy (const char *pathname, const char *local, int has_changed)
1326 int return_value = 0;
1327 char *path;
1329 path = vfs_canon_and_translate (pathname);
1330 if (path != NULL)
1332 struct vfs_class *vfs = vfs_get_class (path);
1334 return_value = vfs->ungetlocalcopy ?
1335 (*vfs->ungetlocalcopy) (vfs, path, local, has_changed) :
1336 mc_def_ungetlocalcopy (vfs, path, local, has_changed);
1337 g_free (path);
1338 return return_value;
1340 else
1341 return -1;
1345 void
1346 vfs_init (void)
1348 /* create the VFS handle array */
1349 vfs_openfiles = g_ptr_array_new ();
1351 vfs_str_buffer = g_string_new ("");
1352 /* localfs needs to be the first one */
1353 init_localfs ();
1354 /* fallback value for vfs_get_class() */
1355 localfs_class = vfs_list;
1357 init_extfs ();
1358 init_sfs ();
1359 init_tarfs ();
1360 init_cpiofs ();
1362 #ifdef USE_EXT2FSLIB
1363 init_undelfs ();
1364 #endif /* USE_EXT2FSLIB */
1366 #ifdef USE_NETCODE
1367 init_ftpfs ();
1368 init_fish ();
1369 #ifdef ENABLE_VFS_SMB
1370 init_smbfs ();
1371 #endif /* ENABLE_VFS_SMB */
1372 #ifdef ENABLE_VFS_MCFS
1373 init_mcfs ();
1374 #endif /* ENABLE_VFS_MCFS */
1375 #endif /* USE_NETCODE */
1377 vfs_setup_wd ();
1380 void
1381 vfs_shut (void)
1383 struct vfs_class *vfs;
1385 vfs_gc_done ();
1387 g_free (current_dir);
1389 for (vfs = vfs_list; vfs; vfs = vfs->next)
1390 if (vfs->done)
1391 (*vfs->done) (vfs);
1393 g_ptr_array_free (vfs_openfiles, TRUE);
1394 g_string_free (vfs_str_buffer, TRUE);
1395 g_free (mc_readdir_result);
1399 * These ones grab information from the VFS
1400 * and handles them to an upper layer
1402 void
1403 vfs_fill_names (fill_names_f func)
1405 struct vfs_class *vfs;
1407 for (vfs = vfs_list; vfs; vfs = vfs->next)
1408 if (vfs->fill_names)
1409 (*vfs->fill_names) (vfs, func);
1413 * Returns vfs path corresponding to given url. If passed string is
1414 * not recognized as url, g_strdup(url) is returned.
1417 static const struct
1419 const char *name;
1420 size_t name_len;
1421 const char *substitute;
1422 } url_table[] =
1424 /* *INDENT-OFF* */
1425 { "ftp://", 6, "/#ftp:" },
1426 { "mc://", 5, "/#mc:" },
1427 { "smb://", 6, "/#smb:" },
1428 { "sh://", 5, "/#sh:" },
1429 { "ssh://", 6, "/#sh:" },
1430 { "a:", 2, "/#a" }
1431 /* *INDENT-ON* */
1434 char *
1435 vfs_translate_url (const char *url)
1437 size_t i;
1439 for (i = 0; i < sizeof (url_table) / sizeof (url_table[0]); i++)
1440 if (strncmp (url, url_table[i].name, url_table[i].name_len) == 0)
1441 return g_strconcat (url_table[i].substitute, url + url_table[i].name_len,
1442 (char *) NULL);
1444 return g_strdup (url);
1448 vfs_file_is_local (const char *filename)
1450 return vfs_file_class_flags (filename) & VFSF_LOCAL;