1 /* Virtual File System switch code
2 Copyright (C) 1995 The Free Software Foundation
4 Written by: 1995 Miguel de Icaza
8 This program is free software; you can redistribute it and/or
9 modify it under the terms of the GNU Library General Public License
10 as published by the Free Software Foundation; either version 2 of
11 the License, or (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU Library General Public License for more details.
18 You should have received a copy of the GNU Library General Public
19 License along with this program; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
22 /* Warning: funtions like extfs_lstat() have right to destroy any
23 * strings you pass to them. This is acutally ok as you g_strdup what
24 * you are passing to them, anyway; still, beware. */
26 /* Namespace: exports *many* functions with vfs_ prefix; exports
27 parse_ls_lga and friends which do not have that prefix. */
32 #include <stdlib.h> /* For atol() */
36 #include <sys/types.h>
38 #include <ctype.h> /* is_digit() */
48 /* They keep track of the current directory */
49 static struct vfs_class
*current_vfs
;
50 static char *current_dir
;
54 struct vfs_class
*vclass
;
58 static GSList
*vfs_openfiles
;
59 #define VFS_FIRST_HANDLE 100
61 static struct vfs_class
*localfs_class
;
63 /* Create new VFS handle and put it to the list */
65 vfs_new_handle (struct vfs_class
*vclass
, void *fsinfo
)
67 static int vfs_handle_counter
= VFS_FIRST_HANDLE
;
68 struct vfs_openfile
*h
;
70 h
= g_new (struct vfs_openfile
, 1);
71 h
->handle
= vfs_handle_counter
++;
74 vfs_openfiles
= g_slist_prepend (vfs_openfiles
, h
);
78 /* Function to match handle, passed to g_slist_find_custom() */
80 vfs_cmp_handle (gconstpointer a
, gconstpointer b
)
84 return ((struct vfs_openfile
*) a
)->handle
!= (int) b
;
87 /* Find VFS class by file handle */
88 static inline struct vfs_class
*
92 struct vfs_openfile
*h
;
94 l
= g_slist_find_custom (vfs_openfiles
, (void *) handle
,
98 h
= (struct vfs_openfile
*) l
->data
;
104 /* Find private file data by file handle */
106 vfs_info (int handle
)
109 struct vfs_openfile
*h
;
111 l
= g_slist_find_custom (vfs_openfiles
, (void *) handle
,
115 h
= (struct vfs_openfile
*) l
->data
;
121 /* Free open file data for given file handle */
123 vfs_free_handle (int handle
)
127 l
= g_slist_find_custom (vfs_openfiles
, (void *) handle
,
129 vfs_openfiles
= g_slist_delete_link (vfs_openfiles
, l
);
132 static struct vfs_class
*vfs_list
;
135 vfs_register_class (struct vfs_class
*vfs
)
137 if (vfs
->init
) /* vfs has own initialization function */
138 if (!(*vfs
->init
)(vfs
)) /* but it failed */
141 vfs
->next
= vfs_list
;
147 /* Return VFS class for the given prefix */
148 static struct vfs_class
*
149 vfs_prefix_to_class (char *prefix
)
151 struct vfs_class
*vfs
;
153 for (vfs
= vfs_list
; vfs
; vfs
= vfs
->next
) {
155 if ((*vfs
->which
) (vfs
, prefix
) == -1)
160 && !strncmp (prefix
, vfs
->prefix
, strlen (vfs
->prefix
)))
166 /* Strip known vfs suffixes from a filename (possible improvement: strip
167 suffix from last path component).
168 Returns a malloced string which has to be freed. */
170 vfs_strip_suffix_from_filename (const char *filename
)
172 struct vfs_class
*vfs
;
177 vfs_die ("vfs_strip_suffix_from_path got NULL: impossible");
179 p
= g_strdup (filename
);
180 if (!(semi
= strrchr (p
, '#')))
183 /* Avoid last class (localfs) that would accept any prefix */
184 for (vfs
= vfs_list
; vfs
->next
; vfs
= vfs
->next
) {
186 if ((*vfs
->which
) (vfs
, semi
+ 1) == -1)
188 *semi
= '\0'; /* Found valid suffix */
192 && !strncmp (semi
+ 1, vfs
->prefix
, strlen (vfs
->prefix
))) {
193 *semi
= '\0'; /* Found valid suffix */
201 path_magic (const char *path
)
205 if (!stat(path
, &buf
))
212 * Splits path '/p1#op/inpath' into inpath,op; returns which vfs it is.
213 * What is left in path is p1. You still want to g_free(path), you DON'T
214 * want to free neither *inpath nor *op
217 vfs_split (char *path
, char **inpath
, char **op
)
221 struct vfs_class
*ret
;
224 vfs_die("Cannot split NULL");
226 semi
= strrchr (path
, '#');
227 if (!semi
|| !path_magic(path
))
230 slash
= strchr (semi
, PATH_SEP
);
242 if ((ret
= vfs_prefix_to_class (semi
+1))){
246 *inpath
= slash
? slash
+ 1 : NULL
;
253 ret
= vfs_split (path
, inpath
, op
);
258 static struct vfs_class
*
259 _vfs_get_class (const char *path
)
263 struct vfs_class
*ret
;
265 g_return_val_if_fail(path
, NULL
);
267 semi
= strrchr (path
, '#');
268 if (!semi
|| !path_magic (path
))
271 slash
= strchr (semi
, PATH_SEP
);
276 ret
= vfs_prefix_to_class (semi
+1);
281 ret
= _vfs_get_class (path
);
288 vfs_get_class (const char *path
)
290 struct vfs_class
*vfs
;
292 vfs
= _vfs_get_class(path
);
301 ferrno (struct vfs_class
*vfs
)
303 return vfs
->ferrno
? (*vfs
->ferrno
)(vfs
) : E_UNKNOWN
;
304 /* Hope that error message is obscure enough ;-) */
308 mc_open (const char *filename
, int flags
, ...)
314 char *file
= vfs_canon (filename
);
315 struct vfs_class
*vfs
= vfs_get_class (file
);
317 /* Get the mode flag */ /* FIXME: should look if O_CREAT is present */
318 va_start (ap
, flags
);
319 mode
= va_arg (ap
, int);
328 info
= (*vfs
->open
) (vfs
, file
, flags
, mode
); /* open must be supported */
331 errno
= ferrno (vfs
);
335 return vfs_new_handle (vfs
, info
);
339 #define MC_NAMEOP(name, inarg, callarg) \
340 int mc_##name inarg \
342 struct vfs_class *vfs; \
344 char *mpath = vfs_canon (path); \
345 vfs = vfs_get_class (mpath); \
346 result = vfs->name ? (*vfs->name)callarg : -1; \
349 errno = vfs->name ? ferrno (vfs) : E_NOTSUPP; \
353 MC_NAMEOP (chmod
, (const char *path
, int mode
), (vfs
, path
, mode
))
354 MC_NAMEOP (chown
, (const char *path
, int owner
, int group
), (vfs
, path
, owner
, group
))
355 MC_NAMEOP (utime
, (const char *path
, struct utimbuf
*times
), (vfs
, path
, times
))
356 MC_NAMEOP (readlink
, (const char *path
, char *buf
, int bufsiz
), (vfs
, path
, buf
, bufsiz
))
357 MC_NAMEOP (unlink
, (const char *path
), (vfs
, path
))
358 MC_NAMEOP (symlink
, (const char *name1
, const char *path
), (vfs
, name1
, path
))
359 MC_NAMEOP (mkdir
, (const char *path
, mode_t mode
), (vfs
, path
, mode
))
360 MC_NAMEOP (rmdir
, (const char *path
), (vfs
, path
))
361 MC_NAMEOP (mknod
, (const char *path
, int mode
, int dev
), (vfs
, path
, mode
, dev
))
364 #define MC_HANDLEOP(name, inarg, callarg) \
365 int mc_##name inarg \
367 struct vfs_class *vfs; \
371 vfs = vfs_op (handle); \
372 result = vfs->name ? (*vfs->name)callarg : -1; \
374 errno = vfs->name ? ferrno (vfs) : E_NOTSUPP; \
378 MC_HANDLEOP(read
, (int handle
, char *buffer
, int count
), (vfs_info (handle
), buffer
, count
) )
379 MC_HANDLEOP (write
, (int handle
, const char *buf
, int nbyte
), (vfs_info (handle
), buf
, nbyte
))
382 #define MC_RENAMEOP(name) \
383 int mc_##name (const char *fname1, const char *fname2) \
385 struct vfs_class *vfs; \
387 char *name2, *name1 = vfs_canon (fname1); \
388 vfs = vfs_get_class (name1); \
389 name2 = vfs_canon (fname2); \
390 if (vfs != vfs_get_class (name2)){ \
396 result = vfs->name ? (*vfs->name)(vfs, name1, name2) : -1; \
400 errno = vfs->name ? ferrno (vfs) : E_NOTSUPP; \
409 mc_ctl (int handle
, int ctlop
, void *arg
)
411 struct vfs_class
*vfs
= vfs_op (handle
);
413 return vfs
->ctl
? (*vfs
->ctl
)(vfs_info (handle
), ctlop
, arg
) : 0;
417 mc_setctl (const char *path
, int ctlop
, void *arg
)
419 struct vfs_class
*vfs
;
424 vfs_die("You don't want to pass NULL to mc_setctl.");
426 mpath
= vfs_canon (path
);
427 vfs
= vfs_get_class (mpath
);
428 result
= vfs
->setctl
? (*vfs
->setctl
)(vfs
, mpath
, ctlop
, arg
) : 0;
434 mc_close (int handle
)
436 struct vfs_class
*vfs
;
439 if (handle
== -1 || !vfs_info (handle
))
442 vfs
= vfs_op (handle
);
444 return close (handle
);
447 vfs_die ("VFS must support close.\n");
448 result
= (*vfs
->close
)(vfs_info (handle
));
449 vfs_free_handle (handle
);
451 errno
= ferrno (vfs
);
457 mc_opendir (const char *dirname
)
459 int handle
, *handlep
;
461 struct vfs_class
*vfs
;
464 dname
= vfs_canon (dirname
);
465 vfs
= vfs_get_class (dname
);
467 info
= vfs
->opendir
? (*vfs
->opendir
)(vfs
, dname
) : NULL
;
470 errno
= vfs
->opendir
? ferrno (vfs
) : E_NOTSUPP
;
473 handle
= vfs_new_handle (vfs
, info
);
475 handlep
= g_new (int, 1);
477 return (DIR *) handlep
;
481 mc_readdir (DIR *dirp
)
484 struct vfs_class
*vfs
;
485 struct dirent
*result
= NULL
;
491 handle
= *(int *) dirp
;
492 vfs
= vfs_op (handle
);
494 result
= (*vfs
->readdir
) (vfs_info (handle
));
496 errno
= vfs
->readdir
? ferrno (vfs
) : E_NOTSUPP
;
501 mc_closedir (DIR *dirp
)
503 int handle
= *(int *) dirp
;
504 struct vfs_class
*vfs
= vfs_op (handle
);
507 result
= vfs
->closedir
? (*vfs
->closedir
)(vfs_info (handle
)) : -1;
508 vfs_free_handle (handle
);
513 int mc_stat (const char *filename
, struct stat
*buf
) {
514 struct vfs_class
*vfs
;
517 path
= vfs_canon (filename
); vfs
= vfs_get_class (path
);
518 result
= vfs
->stat
? (*vfs
->stat
) (vfs
, path
, buf
) : -1;
521 errno
= vfs
->name
? ferrno (vfs
) : E_NOTSUPP
;
525 int mc_lstat (const char *filename
, struct stat
*buf
) {
526 struct vfs_class
*vfs
;
529 path
= vfs_canon (filename
); vfs
= vfs_get_class (path
);
530 result
= vfs
->lstat
? (*vfs
->lstat
) (vfs
, path
, buf
) : -1;
533 errno
= vfs
->name
? ferrno (vfs
) : E_NOTSUPP
;
537 int mc_fstat (int handle
, struct stat
*buf
) {
538 struct vfs_class
*vfs
;
543 vfs
= vfs_op (handle
);
544 result
= vfs
->fstat
? (*vfs
->fstat
) (vfs_info (handle
), buf
) : -1;
546 errno
= vfs
->name
? ferrno (vfs
) : E_NOTSUPP
;
551 * Return current directory. If it's local, reread the current directory
552 * from the OS. You must g_strdup whatever this function returns.
558 struct stat my_stat
, my_stat2
;
560 if (!_vfs_get_class (current_dir
)) {
561 p
= g_get_current_dir ();
562 if (!p
) /* One of the directories in the path is not readable */
565 /* Otherwise check if it is O.K. to use the current_dir */
566 if (!cd_symlinks
|| mc_stat (p
, &my_stat
)
567 || mc_stat (current_dir
, &my_stat2
)
568 || my_stat
.st_ino
!= my_stat2
.st_ino
569 || my_stat
.st_dev
!= my_stat2
.st_dev
) {
570 g_free (current_dir
);
573 } /* Otherwise we return current_dir below */
582 current_dir
= g_strdup (PATH_SEP_STR
);
585 if (strlen (current_dir
) > MC_MAXPATHLEN
- 2)
586 vfs_die ("Current dir too long.\n");
588 current_vfs
= vfs_get_class (current_dir
);
592 * Return current directory. If it's local, reread the current directory
593 * from the OS. Put directory to the provided buffer.
596 mc_get_current_wd (char *buffer
, int size
)
598 const char *cwd
= _vfs_get_cwd ();
600 g_strlcpy (buffer
, cwd
, size
- 1);
601 buffer
[size
- 1] = 0;
606 * Return current directory without any OS calls.
609 vfs_get_current_dir (void)
614 off_t
mc_lseek (int fd
, off_t offset
, int whence
)
616 struct vfs_class
*vfs
;
623 result
= vfs
->lseek
? (*vfs
->lseek
)(vfs_info (fd
), offset
, whence
) : -1;
625 errno
= vfs
->lseek
? ferrno (vfs
) : E_NOTSUPP
;
630 * remove //, /./ and /../, local should point to big enough buffer
633 #define ISSLASH(a) (!a || (a == '/'))
636 vfs_canon (const char *path
)
639 vfs_die("Cannot canonicalize NULL");
641 /* Relative to current directory */
642 if (*path
!= PATH_SEP
){
643 char *local
, *result
;
645 local
= concat_dir_and_file (current_dir
, path
);
647 result
= vfs_canon (local
);
653 * So we have path of following form:
654 * /p1/p2#op/.././././p3#op/p4. Good luck.
657 char *result
= g_strdup (path
);
658 canonicalize_pathname (result
);
665 * Return 0 on success, -1 on failure.
668 mc_chdir (const char *path
)
671 struct vfs_class
*old_vfs
, *new_vfs
;
675 new_dir
= vfs_canon (path
);
676 new_vfs
= vfs_get_class (new_dir
);
680 result
= (*new_vfs
->chdir
) (new_vfs
, new_dir
);
683 errno
= ferrno (new_vfs
);
688 old_vfsid
= vfs_getid (current_vfs
, current_dir
);
689 old_vfs
= current_vfs
;
691 /* Actually change directory */
692 g_free (current_dir
);
693 current_dir
= new_dir
;
694 current_vfs
= new_vfs
;
696 /* This function uses the new current_dir implicitly */
697 vfs_stamp_create (old_vfs
, old_vfsid
);
699 /* Sometimes we assume no trailing slash on cwd */
702 p
= strchr (current_dir
, 0) - 1;
703 if (*p
== PATH_SEP
&& p
> current_dir
)
710 /* Return 1 is the current VFS class is local */
712 vfs_current_is_local (void)
714 return (current_vfs
->flags
& VFSF_LOCAL
) != 0;
717 /* Return flags of the VFS class of the given filename */
719 vfs_file_class_flags (const char *filename
)
721 struct vfs_class
*vfs
;
724 fname
= vfs_canon (filename
);
725 vfs
= vfs_get_class (fname
);
731 static struct mc_mmapping
{
734 struct vfs_class
*vfs
;
735 struct mc_mmapping
*next
;
736 } *mc_mmaparray
= NULL
;
739 mc_mmap (caddr_t addr
, size_t len
, int prot
, int flags
, int fd
, off_t offset
)
741 struct vfs_class
*vfs
;
743 struct mc_mmapping
*mcm
;
749 result
= vfs
->mmap
? (*vfs
->mmap
)(vfs
, addr
, len
, prot
, flags
, vfs_info (fd
), offset
) : (caddr_t
)-1;
750 if (result
== (caddr_t
)-1){
751 errno
= ferrno (vfs
);
754 mcm
=g_new (struct mc_mmapping
, 1);
756 mcm
->vfs_info
= vfs_info (fd
);
758 mcm
->next
= mc_mmaparray
;
764 mc_munmap (caddr_t addr
, size_t len
)
766 struct mc_mmapping
*mcm
, *mcm2
= NULL
;
768 for (mcm
= mc_mmaparray
; mcm
!= NULL
; mcm2
= mcm
, mcm
= mcm
->next
){
769 if (mcm
->addr
== addr
){
771 mc_mmaparray
= mcm
->next
;
773 mcm2
->next
= mcm
->next
;
774 if (mcm
->vfs
->munmap
)
775 (*mcm
->vfs
->munmap
)(mcm
->vfs
, addr
, len
, mcm
->vfs_info
);
786 mc_def_getlocalcopy (struct vfs_class
*vfs
, const char *filename
)
793 fdin
= mc_open (filename
, O_RDONLY
| O_LINEAR
);
797 fdout
= vfs_mkstemps (&tmp
, "vfs", filename
);
801 while ((i
= mc_read (fdin
, buffer
, sizeof (buffer
))) > 0) {
802 if (write (fdout
, buffer
, i
) != i
)
811 if (close (fdout
) == -1)
814 if (mc_stat (filename
, &mystat
) != -1) {
815 chmod (tmp
, mystat
.st_mode
);
829 mc_getlocalcopy (const char *pathname
)
832 char *path
= vfs_canon (pathname
);
833 struct vfs_class
*vfs
= vfs_get_class (path
);
835 result
= vfs
->getlocalcopy
? (*vfs
->getlocalcopy
)(vfs
, path
) :
836 mc_def_getlocalcopy (vfs
, path
);
839 errno
= ferrno (vfs
);
844 mc_def_ungetlocalcopy (struct vfs_class
*vfs
, const char *filename
,
845 const char *local
, int has_changed
)
847 int fdin
= -1, fdout
= -1, i
;
854 fdin
= open (local
, O_RDONLY
);
857 fdout
= mc_open (filename
, O_WRONLY
| O_TRUNC
);
860 while ((i
= read (fdin
, buffer
, sizeof (buffer
))) > 0) {
861 if (mc_write (fdout
, buffer
, i
) != i
)
867 if (close (fdin
) == -1) {
872 if (mc_close (fdout
) == -1) {
881 message (1, _("Changes to file lost"), "%s", filename
);
891 mc_ungetlocalcopy (const char *pathname
, const char *local
, int has_changed
)
893 int return_value
= 0;
894 char *path
= vfs_canon (pathname
);
895 struct vfs_class
*vfs
= vfs_get_class (path
);
897 return_value
= vfs
->ungetlocalcopy
?
898 (*vfs
->ungetlocalcopy
)(vfs
, path
, local
, has_changed
) :
899 mc_def_ungetlocalcopy (vfs
, path
, local
, has_changed
);
908 /* localfs needs to be the first one */
910 /* fallback value for vfs_get_class() */
911 localfs_class
= vfs_list
;
920 #endif /* USE_EXT2FSLIB */
928 #endif /* WITH_SMBFS */
931 #endif /* WITH_SMBFS */
932 #endif /* USE_NETCODE */
940 struct vfs_class
*vfs
;
945 g_free (current_dir
);
947 for (vfs
= vfs_list
; vfs
; vfs
= vfs
->next
)
951 g_slist_free (vfs_openfiles
);
955 * These ones grab information from the VFS
956 * and handles them to an upper layer
959 vfs_fill_names (fill_names_f func
)
961 struct vfs_class
*vfs
;
963 for (vfs
=vfs_list
; vfs
; vfs
=vfs
->next
)
965 (*vfs
->fill_names
) (vfs
, func
);
969 * Returns vfs path corresponding to given url. If passed string is
970 * not recognized as url, g_strdup(url) is returned.
973 vfs_translate_url (const char *url
)
975 if (strncmp (url
, "ftp://", 6) == 0)
976 return g_strconcat ("/#ftp:", url
+ 6, NULL
);
977 else if (strncmp (url
, "a:", 2) == 0)
978 return g_strdup ("/#a");
980 return g_strdup (url
);
983 int vfs_file_is_local (const char *filename
)
985 return vfs_file_class_flags (filename
) & VFSF_LOCAL
;