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
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 /* Warning: funtions like extfs_lstat() have right to destroy any
24 * strings you pass to them. This is acutally ok as you mhl_str_dup what
25 * you are passing to them, anyway; still, beware. */
27 /* Namespace: exports *many* functions with vfs_ prefix; exports
28 parse_ls_lga and friends which do not have that prefix. */
33 #include <stdlib.h> /* For atol() */
37 #include <sys/types.h>
39 #include <ctype.h> /* is_digit() */
41 #include <mhl/string.h>
43 #include "../src/global.h"
44 #include "../src/tty.h" /* enable/disable interrupt key */
45 #include "../src/wtools.h" /* message() */
46 #include "../src/main.h" /* print_vfs_message */
59 /* They keep track of the current directory */
60 static struct vfs_class
*current_vfs
;
61 static char *current_dir
;
65 struct vfs_class
*vclass
;
69 static GSList
*vfs_openfiles
;
70 #define VFS_FIRST_HANDLE 100
72 static struct vfs_class
*localfs_class
;
74 /* Create new VFS handle and put it to the list */
76 vfs_new_handle (struct vfs_class
*vclass
, void *fsinfo
)
78 static int vfs_handle_counter
= VFS_FIRST_HANDLE
;
79 struct vfs_openfile
*h
;
81 h
= g_new (struct vfs_openfile
, 1);
82 h
->handle
= vfs_handle_counter
++;
85 vfs_openfiles
= g_slist_prepend (vfs_openfiles
, h
);
89 /* Function to match handle, passed to g_slist_find_custom() */
91 vfs_cmp_handle (gconstpointer a
, gconstpointer b
)
95 return ((struct vfs_openfile
*) a
)->handle
!= (long) b
;
98 /* Find VFS class by file handle */
99 static inline struct vfs_class
*
103 struct vfs_openfile
*h
;
105 l
= g_slist_find_custom (vfs_openfiles
, (void *) (long) handle
,
109 h
= (struct vfs_openfile
*) l
->data
;
115 /* Find private file data by file handle */
117 vfs_info (int handle
)
120 struct vfs_openfile
*h
;
122 l
= g_slist_find_custom (vfs_openfiles
, (void *) (long) handle
,
126 h
= (struct vfs_openfile
*) l
->data
;
132 /* Free open file data for given file handle */
134 vfs_free_handle (int handle
)
138 l
= g_slist_find_custom (vfs_openfiles
, (void *) (long) handle
,
140 vfs_openfiles
= g_slist_delete_link (vfs_openfiles
, l
);
143 static struct vfs_class
*vfs_list
;
146 vfs_register_class (struct vfs_class
*vfs
)
148 if (vfs
->init
) /* vfs has own initialization function */
149 if (!(*vfs
->init
)(vfs
)) /* but it failed */
152 vfs
->next
= vfs_list
;
158 /* Return VFS class for the given prefix */
159 static struct vfs_class
*
160 vfs_prefix_to_class (char *prefix
)
162 struct vfs_class
*vfs
;
164 /* Avoid last class (localfs) that would accept any prefix */
165 for (vfs
= vfs_list
; vfs
->next
; vfs
= vfs
->next
) {
167 if ((*vfs
->which
) (vfs
, prefix
) == -1)
172 && !strncmp (prefix
, vfs
->prefix
, strlen (vfs
->prefix
)))
178 /* Strip known vfs suffixes from a filename (possible improvement: strip
179 suffix from last path component).
180 Returns a malloced string which has to be freed. */
182 vfs_strip_suffix_from_filename (const char *filename
)
184 struct vfs_class
*vfs
;
189 vfs_die ("vfs_strip_suffix_from_path got NULL: impossible");
191 p
= mhl_str_dup (filename
);
192 if (!(semi
= strrchr (p
, '#')))
195 /* Avoid last class (localfs) that would accept any prefix */
196 for (vfs
= vfs_list
; vfs
->next
; vfs
= vfs
->next
) {
198 if ((*vfs
->which
) (vfs
, semi
+ 1) == -1)
200 *semi
= '\0'; /* Found valid suffix */
204 && !strncmp (semi
+ 1, vfs
->prefix
, strlen (vfs
->prefix
))) {
205 *semi
= '\0'; /* Found valid suffix */
213 path_magic (const char *path
)
217 if (!stat(path
, &buf
))
224 * Splits path '/p1#op/inpath' into inpath,op; returns which vfs it is.
225 * What is left in path is p1. You still want to g_free(path), you DON'T
226 * want to free neither *inpath nor *op
229 vfs_split (char *path
, char **inpath
, char **op
)
233 struct vfs_class
*ret
;
236 vfs_die("Cannot split NULL");
238 semi
= strrchr (path
, '#');
239 if (!semi
|| !path_magic(path
))
242 slash
= strchr (semi
, PATH_SEP
);
254 if ((ret
= vfs_prefix_to_class (semi
+1))){
258 *inpath
= slash
? slash
+ 1 : NULL
;
265 ret
= vfs_split (path
, inpath
, op
);
270 static struct vfs_class
*
271 _vfs_get_class (char *path
)
275 struct vfs_class
*ret
;
277 g_return_val_if_fail(path
, NULL
);
279 semi
= strrchr (path
, '#');
280 if (!semi
|| !path_magic (path
))
283 slash
= strchr (semi
, PATH_SEP
);
288 ret
= vfs_prefix_to_class (semi
+1);
293 ret
= _vfs_get_class (path
);
300 vfs_get_class (const char *pathname
)
302 struct vfs_class
*vfs
;
303 char *path
= mhl_str_dup (pathname
);
305 vfs
= _vfs_get_class (path
);
315 ferrno (struct vfs_class
*vfs
)
317 return vfs
->ferrno
? (*vfs
->ferrno
)(vfs
) : E_UNKNOWN
;
318 /* Hope that error message is obscure enough ;-) */
322 mc_open (const char *filename
, int flags
, ...)
328 char *file
= vfs_canon (filename
);
329 struct vfs_class
*vfs
= vfs_get_class (file
);
331 /* Get the mode flag */
332 if (flags
& O_CREAT
) {
333 va_start (ap
, flags
);
334 mode
= va_arg (ap
, int);
345 info
= (*vfs
->open
) (vfs
, file
, flags
, mode
); /* open must be supported */
348 errno
= ferrno (vfs
);
352 return vfs_new_handle (vfs
, info
);
356 #define MC_NAMEOP(name, inarg, callarg) \
357 int mc_##name inarg \
359 struct vfs_class *vfs; \
361 char *mpath = vfs_canon (path); \
362 vfs = vfs_get_class (mpath); \
363 result = vfs->name ? (*vfs->name)callarg : -1; \
366 errno = vfs->name ? ferrno (vfs) : E_NOTSUPP; \
370 MC_NAMEOP (chmod
, (const char *path
, mode_t mode
), (vfs
, mpath
, mode
))
371 MC_NAMEOP (chown
, (const char *path
, uid_t owner
, gid_t group
), (vfs
, mpath
, owner
, group
))
372 MC_NAMEOP (utime
, (const char *path
, struct utimbuf
*times
), (vfs
, mpath
, times
))
373 MC_NAMEOP (readlink
, (const char *path
, char *buf
, int bufsiz
), (vfs
, mpath
, buf
, bufsiz
))
374 MC_NAMEOP (unlink
, (const char *path
), (vfs
, mpath
))
375 MC_NAMEOP (symlink
, (const char *name1
, const char *path
), (vfs
, name1
, mpath
))
376 MC_NAMEOP (mkdir
, (const char *path
, mode_t mode
), (vfs
, mpath
, mode
))
377 MC_NAMEOP (rmdir
, (const char *path
), (vfs
, mpath
))
378 MC_NAMEOP (mknod
, (const char *path
, mode_t mode
, dev_t dev
), (vfs
, mpath
, mode
, dev
))
381 #define MC_HANDLEOP(name, inarg, callarg) \
382 ssize_t mc_##name inarg \
384 struct vfs_class *vfs; \
388 vfs = vfs_op (handle); \
389 result = vfs->name ? (*vfs->name)callarg : -1; \
391 errno = vfs->name ? ferrno (vfs) : E_NOTSUPP; \
395 MC_HANDLEOP(read
, (int handle
, void *buffer
, int count
), (vfs_info (handle
), buffer
, count
))
396 MC_HANDLEOP(write
, (int handle
, const void *buf
, int nbyte
), (vfs_info (handle
), buf
, nbyte
))
399 #define MC_RENAMEOP(name) \
400 int mc_##name (const char *fname1, const char *fname2) \
402 struct vfs_class *vfs; \
404 char *name2, *name1 = vfs_canon (fname1); \
405 vfs = vfs_get_class (name1); \
406 name2 = vfs_canon (fname2); \
407 if (vfs != vfs_get_class (name2)){ \
413 result = vfs->name ? (*vfs->name)(vfs, name1, name2) : -1; \
417 errno = vfs->name ? ferrno (vfs) : E_NOTSUPP; \
426 mc_ctl (int handle
, int ctlop
, void *arg
)
428 struct vfs_class
*vfs
= vfs_op (handle
);
430 return vfs
->ctl
? (*vfs
->ctl
)(vfs_info (handle
), ctlop
, arg
) : 0;
434 mc_setctl (const char *path
, int ctlop
, void *arg
)
436 struct vfs_class
*vfs
;
441 vfs_die("You don't want to pass NULL to mc_setctl.");
443 mpath
= vfs_canon (path
);
444 vfs
= vfs_get_class (mpath
);
445 result
= vfs
->setctl
? (*vfs
->setctl
)(vfs
, mpath
, ctlop
, arg
) : 0;
451 mc_close (int handle
)
453 struct vfs_class
*vfs
;
456 if (handle
== -1 || !vfs_info (handle
))
459 vfs
= vfs_op (handle
);
461 return close (handle
);
464 vfs_die ("VFS must support close.\n");
465 result
= (*vfs
->close
)(vfs_info (handle
));
466 vfs_free_handle (handle
);
468 errno
= ferrno (vfs
);
474 mc_opendir (const char *dirname
)
476 int handle
, *handlep
;
478 struct vfs_class
*vfs
;
481 dname
= vfs_canon (dirname
);
482 vfs
= vfs_get_class (dname
);
484 info
= vfs
->opendir
? (*vfs
->opendir
)(vfs
, dname
) : NULL
;
487 errno
= vfs
->opendir
? ferrno (vfs
) : E_NOTSUPP
;
490 handle
= vfs_new_handle (vfs
, info
);
492 handlep
= g_new (int, 1);
494 return (DIR *) handlep
;
498 mc_readdir (DIR *dirp
)
501 struct vfs_class
*vfs
;
502 struct dirent
*result
= NULL
;
508 handle
= *(int *) dirp
;
509 vfs
= vfs_op (handle
);
511 result
= (*vfs
->readdir
) (vfs_info (handle
));
513 errno
= vfs
->readdir
? ferrno (vfs
) : E_NOTSUPP
;
518 mc_closedir (DIR *dirp
)
520 int handle
= *(int *) dirp
;
521 struct vfs_class
*vfs
= vfs_op (handle
);
524 result
= vfs
->closedir
? (*vfs
->closedir
)(vfs_info (handle
)) : -1;
525 vfs_free_handle (handle
);
530 int mc_stat (const char *filename
, struct stat
*buf
) {
531 struct vfs_class
*vfs
;
534 path
= vfs_canon (filename
); vfs
= vfs_get_class (path
);
535 result
= vfs
->stat
? (*vfs
->stat
) (vfs
, path
, buf
) : -1;
538 errno
= vfs
->name
? ferrno (vfs
) : E_NOTSUPP
;
542 int mc_lstat (const char *filename
, struct stat
*buf
) {
543 struct vfs_class
*vfs
;
546 path
= vfs_canon (filename
); vfs
= vfs_get_class (path
);
547 result
= vfs
->lstat
? (*vfs
->lstat
) (vfs
, path
, buf
) : -1;
550 errno
= vfs
->name
? ferrno (vfs
) : E_NOTSUPP
;
554 int mc_fstat (int handle
, struct stat
*buf
) {
555 struct vfs_class
*vfs
;
560 vfs
= vfs_op (handle
);
561 result
= vfs
->fstat
? (*vfs
->fstat
) (vfs_info (handle
), buf
) : -1;
563 errno
= vfs
->name
? ferrno (vfs
) : E_NOTSUPP
;
568 * Return current directory. If it's local, reread the current directory
569 * from the OS. You must mhl_str_dup() whatever this function returns.
575 struct stat my_stat
, my_stat2
;
577 if (!_vfs_get_class (current_dir
)) {
578 p
= g_get_current_dir ();
579 if (!p
) /* One of the directories in the path is not readable */
582 /* Otherwise check if it is O.K. to use the current_dir */
583 if (!cd_symlinks
|| mc_stat (p
, &my_stat
)
584 || mc_stat (current_dir
, &my_stat2
)
585 || my_stat
.st_ino
!= my_stat2
.st_ino
586 || my_stat
.st_dev
!= my_stat2
.st_dev
) {
587 g_free (current_dir
);
590 } /* Otherwise we return current_dir below */
599 current_dir
= mhl_str_dup (PATH_SEP_STR
);
602 if (strlen (current_dir
) > MC_MAXPATHLEN
- 2)
603 vfs_die ("Current dir too long.\n");
605 current_vfs
= vfs_get_class (current_dir
);
609 * Return current directory. If it's local, reread the current directory
610 * from the OS. Put directory to the provided buffer.
613 mc_get_current_wd (char *buffer
, int size
)
615 const char *cwd
= _vfs_get_cwd ();
617 g_strlcpy (buffer
, cwd
, size
);
622 * Return current directory without any OS calls.
625 vfs_get_current_dir (void)
630 off_t
mc_lseek (int fd
, off_t offset
, int whence
)
632 struct vfs_class
*vfs
;
639 result
= vfs
->lseek
? (*vfs
->lseek
)(vfs_info (fd
), offset
, whence
) : -1;
641 errno
= vfs
->lseek
? ferrno (vfs
) : E_NOTSUPP
;
646 * remove //, /./ and /../
649 #define ISSLASH(a) (!a || (a == '/'))
652 vfs_canon (const char *path
)
655 vfs_die("Cannot canonicalize NULL");
657 /* Relative to current directory */
658 if (*path
!= PATH_SEP
){
659 char *local
, *result
;
661 local
= mhl_str_dir_plus_file (current_dir
, path
);
663 result
= vfs_canon (local
);
669 * So we have path of following form:
670 * /p1/p2#op/.././././p3#op/p4. Good luck.
673 char *result
= mhl_str_dup (path
);
674 canonicalize_pathname (result
);
681 * Return 0 on success, -1 on failure.
684 mc_chdir (const char *path
)
687 struct vfs_class
*old_vfs
, *new_vfs
;
691 new_dir
= vfs_canon (path
);
692 new_vfs
= vfs_get_class (new_dir
);
693 if (!new_vfs
->chdir
) {
698 result
= (*new_vfs
->chdir
) (new_vfs
, new_dir
);
701 errno
= ferrno (new_vfs
);
706 old_vfsid
= vfs_getid (current_vfs
, current_dir
);
707 old_vfs
= current_vfs
;
709 /* Actually change directory */
710 g_free (current_dir
);
711 current_dir
= new_dir
;
712 current_vfs
= new_vfs
;
714 /* This function uses the new current_dir implicitly */
715 vfs_stamp_create (old_vfs
, old_vfsid
);
717 /* Sometimes we assume no trailing slash on cwd */
720 p
= strchr (current_dir
, 0) - 1;
721 if (*p
== PATH_SEP
&& p
> current_dir
)
728 /* Return 1 is the current VFS class is local */
730 vfs_current_is_local (void)
732 return (current_vfs
->flags
& VFSF_LOCAL
) != 0;
735 /* Return flags of the VFS class of the given filename */
737 vfs_file_class_flags (const char *filename
)
739 struct vfs_class
*vfs
;
742 fname
= vfs_canon (filename
);
743 vfs
= vfs_get_class (fname
);
749 mc_def_getlocalcopy (const char *filename
)
756 fdin
= mc_open (filename
, O_RDONLY
| O_LINEAR
);
760 fdout
= vfs_mkstemps (&tmp
, "vfs", filename
);
764 while ((i
= mc_read (fdin
, buffer
, sizeof (buffer
))) > 0) {
765 if (write (fdout
, buffer
, i
) != i
)
774 if (close (fdout
) == -1) {
779 if (mc_stat (filename
, &mystat
) != -1) {
780 chmod (tmp
, mystat
.st_mode
);
794 mc_getlocalcopy (const char *pathname
)
797 char *path
= vfs_canon (pathname
);
798 struct vfs_class
*vfs
= vfs_get_class (path
);
800 result
= vfs
->getlocalcopy
? (*vfs
->getlocalcopy
)(vfs
, path
) :
801 mc_def_getlocalcopy (path
);
804 errno
= ferrno (vfs
);
809 mc_def_ungetlocalcopy (struct vfs_class
*vfs
, const char *filename
,
810 const char *local
, int has_changed
)
812 int fdin
= -1, fdout
= -1, i
;
819 fdin
= open (local
, O_RDONLY
);
822 fdout
= mc_open (filename
, O_WRONLY
| O_TRUNC
);
825 while ((i
= read (fdin
, buffer
, sizeof (buffer
))) > 0) {
826 if (mc_write (fdout
, buffer
, i
) != i
)
832 if (close (fdin
) == -1) {
837 if (mc_close (fdout
) == -1) {
846 message (D_ERROR
, _("Changes to file lost"), "%s", filename
);
856 mc_ungetlocalcopy (const char *pathname
, const char *local
, int has_changed
)
858 int return_value
= 0;
859 char *path
= vfs_canon (pathname
);
860 struct vfs_class
*vfs
= vfs_get_class (path
);
862 return_value
= vfs
->ungetlocalcopy
?
863 (*vfs
->ungetlocalcopy
)(vfs
, path
, local
, has_changed
) :
864 mc_def_ungetlocalcopy (vfs
, path
, local
, has_changed
);
873 /* localfs needs to be the first one */
875 /* fallback value for vfs_get_class() */
876 localfs_class
= vfs_list
;
885 #endif /* USE_EXT2FSLIB */
893 #endif /* WITH_SMBFS */
896 #endif /* WITH_MCFS */
897 #endif /* USE_NETCODE */
905 struct vfs_class
*vfs
;
909 g_free (current_dir
);
911 for (vfs
= vfs_list
; vfs
; vfs
= vfs
->next
)
915 g_slist_free (vfs_openfiles
);
919 * These ones grab information from the VFS
920 * and handles them to an upper layer
923 vfs_fill_names (fill_names_f func
)
925 struct vfs_class
*vfs
;
927 for (vfs
=vfs_list
; vfs
; vfs
=vfs
->next
)
929 (*vfs
->fill_names
) (vfs
, func
);
933 * Returns vfs path corresponding to given url. If passed string is
934 * not recognized as url, mhl_str_dup(url) is returned.
937 static const struct {
940 const char *substitute
;
941 } url_table
[] = { {"ftp://", 6, "/#ftp:"},
942 {"mc://", 5, "/#mc:"},
943 {"smb://", 6, "/#smb:"},
944 {"sh://", 5, "/#sh:"},
945 {"ssh://", 6, "/#sh:"},
950 vfs_translate_url (const char *url
)
954 for (i
= 0; i
< sizeof (url_table
)/sizeof (url_table
[0]); i
++)
955 if (strncmp (url
, url_table
[i
].name
, url_table
[i
].name_len
) == 0)
956 return g_strconcat (url_table
[i
].substitute
, url
+ url_table
[i
].name_len
, (char*) NULL
);
958 return mhl_str_dup (url
);
961 int vfs_file_is_local (const char *filename
)
963 return vfs_file_class_flags (filename
) & VFSF_LOCAL
;