1 /* Virtual File System switch code
2 Copyright (C) 1995 The Free Software Foundation
4 Written by: 1995 Miguel de Icaza
10 This program is free software; you can redistribute it and/or
11 modify it under the terms of the GNU Library General Public License
12 as published by the Free Software Foundation; either version 2 of
13 the License, or (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU Library General Public License for more details.
20 You should have received a copy of the GNU Library General Public
21 License along with this program; if not, write to the Free Software
22 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
24 /* Warning: funtions like extfs_lstat() have right to destroy any
25 * strings you pass to them. This is acutally ok as you g_strdup what
26 * you are passing to them, anyway; still, beware. */
28 /* Namespace: exports *many* functions with vfs_ prefix; exports
29 parse_ls_lga and friends which do not have that prefix. */
38 #include <stdlib.h> /* For atol() */
42 #include <sys/types.h>
48 #include "../src/dir.h"
49 #include "../src/main.h"
50 #ifndef VFS_STANDALONE
51 #include "../src/panel.h"
52 #include "../src/key.h" /* Required for the async alarm handler */
53 #include "../src/layout.h" /* For get_panel_widget and get_other_index */
54 #include "../src/dialog.h"
57 #include "xdirentry.h"
59 #include "extfs.h" /* FIXME: we should not know anything about our modules */
69 extern int get_other_type (void);
71 int vfs_timeout
= 60; /* VFS timeout in seconds */
72 int vfs_flags
= 0; /* Flags */
74 extern int cd_symlinks
; /* Defined in main.c */
76 /* They keep track of the current directory */
77 static vfs
*current_vfs
= &vfs_local_ops
;
78 static char *current_dir
= NULL
;
81 * FIXME: this is broken. It depends on mc not crossing border on month!
83 static int current_mday
;
84 static int current_mon
;
85 static int current_year
;
90 /* FIXME: Open files managed by the vfs layer, should be dynamical */
91 #define MAX_VFS_FILES 100
96 } vfs_file_table
[MAX_VFS_FILES
];
103 /* 0, 1, 2 are reserved file descriptors, while (DIR *) 0 means error */
104 for (i
= 3; i
< MAX_VFS_FILES
; i
++){
105 if (!vfs_file_table
[i
].fs_info
)
109 vfs_die ("No more virtual file handles");
113 /* vfs_local_ops needs to be the first one */
114 static vfs
*vfs_list
= &vfs_local_ops
;
117 vfs_register (vfs
*vfs
)
120 vfs_die("You can not register NULL.");
122 if (vfs
->init
) /* vfs has own initialization function */
123 if (!(*vfs
->init
)(vfs
)) /* but it failed */
126 vfs
->next
= vfs_list
;
133 vfs_type_from_op (char *path
)
138 vfs_die ("vfs_type_from_op got NULL: impossible");
140 for (vfs
= vfs_list
; vfs
!= &vfs_local_ops
; vfs
= vfs
->next
){
142 if ((*vfs
->which
) (vfs
, path
) == -1)
146 if (!strncmp (path
, vfs
->prefix
, strlen (vfs
->prefix
)))
149 return NULL
; /* shut up stupid gcc */
152 /* Strip known vfs suffixes from a filename (possible improvement: strip
153 suffix from last path component).
154 Returns a malloced string which has to be freed. */
156 vfs_strip_suffix_from_filename (const char *filename
)
163 vfs_die("vfs_strip_suffix_from_path got NULL: impossible");
165 p
= g_strdup (filename
);
166 if (!(semi
= strrchr (p
, '#')))
169 for (vfs
= vfs_list
; vfs
!= &vfs_local_ops
; vfs
= vfs
->next
){
171 if ((*vfs
->which
) (vfs
, semi
+ 1) == -1)
173 *semi
= '\0'; /* Found valid suffix */
176 if (!strncmp (semi
+ 1, vfs
->prefix
, strlen (vfs
->prefix
))) {
177 *semi
= '\0'; /* Found valid suffix */
185 path_magic (const char *path
)
189 if (vfs_flags
& FL_ALWAYS_MAGIC
)
192 if (!stat(path
, &buf
))
199 * Splits path '/p1#op/inpath' into inpath,op; returns which vfs it is.
200 * What is left in path is p1. You still want to g_free(path), you DON'T
201 * want to free neither *inpath nor *op
204 vfs_split (char *path
, char **inpath
, char **op
)
211 vfs_die("Can not split NULL");
213 semi
= strrchr (path
, '#');
214 if (!semi
|| !path_magic(path
))
217 slash
= strchr (semi
, PATH_SEP
);
229 if ((ret
= vfs_type_from_op (semi
+1))){
233 *inpath
= slash
? slash
+ 1 : NULL
;
240 ret
= vfs_split (path
, inpath
, op
);
246 vfs_rosplit (char *path
)
252 g_return_val_if_fail(path
, NULL
);
254 semi
= strrchr (path
, '#');
255 if (!semi
|| !path_magic (path
))
258 slash
= strchr (semi
, PATH_SEP
);
263 ret
= vfs_type_from_op (semi
+1);
264 if (!ret
&& (vfs_flags
& FL_NO_LOCALHASH
))
270 ret
= vfs_rosplit (path
);
277 vfs_type (char *path
)
281 vfs
= vfs_rosplit(path
);
284 vfs
= &vfs_local_ops
;
289 static struct vfs_stamping
*stamps
;
292 * Returns the number of seconds remaining to the vfs timeout
294 * FIXME: currently this is set to 10 seconds. We should compute this.
299 return stamps
? 10 : 0;
303 vfs_addstamp (vfs
*v
, vfsid id
, struct vfs_stamping
*parent
)
305 if (v
!= &vfs_local_ops
&& id
!= (vfsid
)-1){
306 struct vfs_stamping
*stamp
;
307 struct vfs_stamping
*last_stamp
= NULL
;
309 for (stamp
= stamps
; stamp
!= NULL
; stamp
= stamp
->next
) {
310 if (stamp
->v
== v
&& stamp
->id
== id
){
311 gettimeofday(&(stamp
->time
), NULL
);
316 stamp
= g_new (struct vfs_stamping
, 1);
320 struct vfs_stamping
*st
= stamp
;
322 st
->parent
= g_new (struct vfs_stamping
, 1);
323 *st
->parent
= *parent
;
324 parent
= parent
->parent
;
332 gettimeofday (&(stamp
->time
), NULL
);
337 last_stamp
->next
= stamp
;
339 /* Add first element */
346 vfs_stamp (vfs
*v
, vfsid id
)
348 struct vfs_stamping
*stamp
;
350 for (stamp
= stamps
; stamp
!= NULL
; stamp
= stamp
->next
)
351 if (stamp
->v
== v
&& stamp
->id
== id
){
353 gettimeofday (&(stamp
->time
), NULL
);
354 if (stamp
->parent
!= NULL
)
355 vfs_stamp (stamp
->parent
->v
, stamp
->parent
->id
);
362 vfs_rm_parents (struct vfs_stamping
*stamp
)
364 struct vfs_stamping
*parent
;
367 parent
= stamp
->parent
;
374 vfs_rmstamp (vfs
*v
, vfsid id
, int removeparents
)
376 struct vfs_stamping
*stamp
, *st1
;
378 for (stamp
= stamps
, st1
= NULL
; stamp
!= NULL
; st1
= stamp
, stamp
= stamp
->next
)
379 if (stamp
->v
== v
&& stamp
->id
== id
){
380 if (stamp
->parent
!= NULL
){
382 vfs_rmstamp (stamp
->parent
->v
, stamp
->parent
->id
, 1);
383 vfs_rm_parents (stamp
->parent
);
386 stamps
= stamp
->next
;
388 st1
->next
= stamp
->next
;
399 return vfs
->ferrno
? (*vfs
->ferrno
)(vfs
) : E_UNKNOWN
;
400 /* Hope that error message is obscure enough ;-) */
404 mc_open (const char *filename
, int flags
, ...)
411 char *file
= vfs_canon (filename
);
412 vfs
*vfs
= vfs_type (file
);
414 /* Get the mode flag */ /* FIXME: should look if O_CREAT is present */
415 va_start (ap
, flags
);
416 mode
= va_arg (ap
, int);
424 info
= (*vfs
->open
) (vfs
, file
, flags
, mode
); /* open must be supported */
427 errno
= ferrno (vfs
);
430 handle
= get_bucket ();
431 vfs_file_table
[handle
].fs_info
= info
;
432 vfs_file_table
[handle
].operations
= vfs
;
437 #define vfs_op(handle) vfs_file_table [handle].operations
438 #define vfs_info(handle) vfs_file_table [handle].fs_info
439 #define vfs_free_bucket(handle) vfs_info(handle) = 0;
441 #define MC_OP(name, inarg, callarg, pre, post) \
442 int mc_##name inarg \
448 result = vfs->name ? (*vfs->name)callarg : -1; \
451 errno = vfs->name ? ferrno (vfs) : E_NOTSUPP; \
455 #define MC_NAMEOP(name, inarg, callarg) \
456 MC_OP (name, inarg, callarg, path = vfs_canon (path); vfs = vfs_type (path);, g_free (path); )
457 #define MC_HANDLEOP(name, inarg, callarg) \
458 MC_OP (name, inarg, callarg, if (handle == -1) return -1; vfs = vfs_op (handle);, )
460 MC_HANDLEOP(read
, (int handle
, char *buffer
, int count
), (vfs_info (handle
), buffer
, count
) )
463 mc_ctl (int handle
, int ctlop
, int arg
)
465 vfs
*vfs
= vfs_op (handle
);
467 return vfs
->ctl
? (*vfs
->ctl
)(vfs_info (handle
), ctlop
, arg
) : 0;
471 mc_setctl (char *path
, int ctlop
, char *arg
)
477 vfs_die("You don't want to pass NULL to mc_setctl.");
479 path
= vfs_canon (path
);
480 vfs
= vfs_type (path
);
481 result
= vfs
->setctl
? (*vfs
->setctl
)(vfs
, path
, ctlop
, arg
) : 0;
487 mc_close (int handle
)
492 if (handle
== -1 || !vfs_info (handle
))
495 vfs
= vfs_op (handle
);
497 return close (handle
);
500 vfs_die ("VFS must support close.\n");
501 result
= (*vfs
->close
)(vfs_info (handle
));
502 vfs_free_bucket (handle
);
504 errno
= ferrno (vfs
);
510 mc_opendir (char *dirname
)
512 int handle
, *handlep
;
516 dirname
= vfs_canon (dirname
);
517 vfs
= vfs_type (dirname
);
519 info
= vfs
->opendir
? (*vfs
->opendir
)(vfs
, dirname
) : NULL
;
522 errno
= vfs
->opendir
? ferrno (vfs
) : E_NOTSUPP
;
525 handle
= get_bucket ();
526 vfs_file_table
[handle
].fs_info
= info
;
527 vfs_file_table
[handle
].operations
= vfs
;
529 handlep
= g_new (int, 1);
531 return (DIR *) handlep
;
534 /* This should strip the non needed part of a path name */
535 #define vfs_name(x) x
538 mc_seekdir (DIR *dirp
, int offset
)
547 handle
= *(int *) dirp
;
548 vfs
= vfs_op (handle
);
550 (*vfs
->seekdir
) (vfs_info (handle
), offset
);
555 #define MC_DIROP(name, type, onerr ) \
556 type mc_##name (DIR *dirp) \
566 handle = *(int *) dirp; \
567 vfs = vfs_op (handle); \
568 result = vfs->name ? (*vfs->name) (vfs_info (handle)) : onerr; \
569 if (result == onerr) \
570 errno = vfs->name ? ferrno(vfs) : E_NOTSUPP; \
574 MC_DIROP (readdir
, struct dirent
*, NULL
)
575 MC_DIROP (telldir
, int, -1)
578 mc_closedir (DIR *dirp
)
580 int handle
= *(int *) dirp
;
581 vfs
*vfs
= vfs_op (handle
);
584 result
= vfs
->closedir
? (*vfs
->closedir
)(vfs_info (handle
)) : -1;
585 vfs_free_bucket (handle
);
590 int mc_stat (char *path
, struct stat
*buf
) {
594 path
= vfs_canon (path
); vfs
= vfs_type (path
);
595 result
= vfs
->stat
? (*vfs
->stat
) (vfs
, vfs_name (path
), buf
) : -1;
598 errno
= vfs
->name
? ferrno (vfs
) : E_NOTSUPP
;
602 int mc_lstat (char *path
, struct stat
*buf
) {
606 path
= vfs_canon (path
); vfs
= vfs_type (path
);
607 result
= vfs
->lstat
? (*vfs
->lstat
) (vfs
, vfs_name (path
), buf
) : -1;
610 errno
= vfs
->name
? ferrno (vfs
) : E_NOTSUPP
;
614 int mc_fstat (int handle
, struct stat
*buf
) {
620 vfs
= vfs_op (handle
);
621 result
= vfs
->fstat
? (*vfs
->fstat
) (vfs_info (handle
), buf
) : -1;
623 errno
= vfs
->name
? ferrno (vfs
) : E_NOTSUPP
;
628 * You must g_strdup whatever this function returns.
635 struct stat my_stat
, my_stat2
;
637 if (!vfs_rosplit (current_dir
)){
638 p
= g_get_current_dir ();
639 if (!p
) /* One of the directories in the path is not readable */
642 /* Otherwise check if it is O.K. to use the current_dir */
644 mc_stat (p
, &my_stat
) ||
645 mc_stat (current_dir
, &my_stat2
) ||
646 my_stat
.st_ino
!= my_stat2
.st_ino
||
647 my_stat
.st_dev
!= my_stat2
.st_dev
){
648 g_free (current_dir
);
651 } /* Otherwise we return current_dir below */
658 mc_get_current_wd (char *buffer
, int size
)
660 const char *cwd
= mc_return_cwd();
662 strncpy (buffer
, cwd
, size
);
666 MC_NAMEOP (chmod
, (char *path
, int mode
), (vfs
, vfs_name (path
), mode
))
667 MC_NAMEOP (chown
, (char *path
, int owner
, int group
), (vfs
, vfs_name (path
), owner
, group
))
668 MC_NAMEOP (utime
, (char *path
, struct utimbuf
*times
), (vfs
, vfs_name (path
), times
))
669 MC_NAMEOP (readlink
, (char *path
, char *buf
, int bufsiz
), (vfs
, vfs_name (path
), buf
, bufsiz
))
670 MC_NAMEOP (unlink
, (char *path
), (vfs
, vfs_name (path
)))
671 MC_NAMEOP (symlink
, (char *name1
, char *path
), (vfs
, vfs_name (name1
), vfs_name (path
)))
673 #define MC_RENAMEOP(name) \
674 int mc_##name (const char *fname1, const char *fname2) \
679 char *name2, *name1 = vfs_canon (fname1); \
680 vfs = vfs_type (name1); \
681 name2 = vfs_canon (fname2); \
682 if (vfs != vfs_type (name2)){ \
689 result = vfs->name ? (*vfs->name)(vfs, vfs_name (name1), vfs_name (name2)) : -1; \
693 errno = vfs->name ? ferrno (vfs) : E_NOTSUPP; \
700 MC_HANDLEOP (write
, (int handle
, char *buf
, int nbyte
), (vfs_info (handle
), buf
, nbyte
))
702 off_t
mc_lseek (int fd
, off_t offset
, int whence
)
711 result
= vfs
->lseek
? (*vfs
->lseek
)(vfs_info (fd
), offset
, whence
) : -1;
713 errno
= vfs
->lseek
? ferrno (vfs
) : E_NOTSUPP
;
718 * remove //, /./ and /../, local should point to big enough buffer
721 #define ISSLASH(a) (!a || (a == '/'))
724 vfs_canon (const char *path
)
727 vfs_die("Cannot canonicalize NULL");
729 /* Tilde expansion */
731 char *local
, *result
;
733 local
= tilde_expand (path
);
735 result
= vfs_canon (local
);
741 /* Relative to current directory */
742 if (*path
!= PATH_SEP
){
743 char *local
, *result
;
745 local
= concat_dir_and_file (current_dir
, path
);
747 result
= vfs_canon (local
);
753 * So we have path of following form:
754 * /p1/p2#op/.././././p3#op/p4. Good luck.
757 char *result
= g_strdup (path
);
758 canonicalize_pathname (result
);
764 vfs_ncs_getid (vfs
*nvfs
, char *dir
, struct vfs_stamping
**par
)
768 dir
= concat_dir_and_file (dir
, "");
770 nvfsid
= (*nvfs
->getid
)(nvfs
, dir
, par
);
777 is_parent (vfs
* nvfs
, vfsid nvfsid
, struct vfs_stamping
*parent
)
779 struct vfs_stamping
*stamp
;
781 for (stamp
= parent
; stamp
; stamp
= stamp
->parent
)
782 if (stamp
->v
== nvfs
&& stamp
->id
== nvfsid
)
785 return (stamp
? 1 : 0);
789 vfs_add_noncurrent_stamps (vfs
* oldvfs
, vfsid oldvfsid
, struct vfs_stamping
*parent
)
791 #ifndef VFS_STANDALONE
792 vfs
*nvfs
, *n2vfs
, *n3vfs
;
793 vfsid nvfsid
, n2vfsid
, n3vfsid
;
794 struct vfs_stamping
*par
, *stamp
;
797 /* FIXME: As soon as we convert to multiple panels, this stuff
798 has to change. It works like this: We do not time out the
799 vfs's which are current in any panel and on the other
800 side we add the old directory with all its parents which
801 are not in any panel (if we find such one, we stop adding
802 parents to the time-outing structure. */
804 /* There are three directories we have to take care of: current_dir,
805 cpanel->cwd and opanel->cwd. Athough most of the time either
806 current_dir and cpanel->cwd or current_dir and opanel->cwd are the
807 same, it's possible that all three are different -- Norbert */
812 nvfs
= vfs_type (current_dir
);
813 nvfsid
= vfs_ncs_getid (nvfs
, current_dir
, &par
);
814 vfs_rmstamp (nvfs
, nvfsid
, 1);
816 f
= is_parent (oldvfs
, oldvfsid
, par
);
817 vfs_rm_parents (par
);
818 if ((nvfs
== oldvfs
&& nvfsid
== oldvfsid
) || oldvfsid
== (vfsid
*)-1 || f
){
822 if (get_current_type () == view_listing
){
823 n2vfs
= vfs_type (cpanel
->cwd
);
824 n2vfsid
= vfs_ncs_getid (n2vfs
, cpanel
->cwd
, &par
);
825 f
= is_parent (oldvfs
, oldvfsid
, par
);
826 vfs_rm_parents (par
);
827 if ((n2vfs
== oldvfs
&& n2vfsid
== oldvfsid
) || f
)
831 n2vfsid
= (vfs
*) -1;
834 if (get_other_type () == view_listing
){
835 n3vfs
= vfs_type (opanel
->cwd
);
836 n3vfsid
= vfs_ncs_getid (n3vfs
, opanel
->cwd
, &par
);
837 f
= is_parent (oldvfs
, oldvfsid
, par
);
838 vfs_rm_parents (par
);
839 if ((n3vfs
== oldvfs
&& n3vfsid
== oldvfsid
) || f
)
846 if ((*oldvfs
->nothingisopen
) (oldvfsid
)){
847 if (oldvfs
== &vfs_extfs_ops
&& ((extfs_archive
*) oldvfsid
)->name
== 0){
848 /* Free the resources immediatly when we leave a mtools fs
849 ('cd a:') instead of waiting for the vfs-timeout */
850 (oldvfs
->free
) (oldvfsid
);
852 vfs_addstamp (oldvfs
, oldvfsid
, parent
);
853 for (stamp
= parent
; stamp
!= NULL
; stamp
= stamp
->parent
){
854 if ((stamp
->v
== nvfs
&& stamp
->id
== nvfsid
) ||
855 (stamp
->v
== n2vfs
&& stamp
->id
== n2vfsid
) ||
856 (stamp
->v
== n3vfs
&& stamp
->id
== n3vfsid
) ||
857 stamp
->id
== (vfsid
) - 1 ||
858 !(*stamp
->v
->nothingisopen
) (stamp
->id
))
860 if (stamp
->v
== &vfs_extfs_ops
&& ((extfs_archive
*) stamp
->id
)->name
== 0){
861 (stamp
->v
->free
) (stamp
->id
);
862 vfs_rmstamp (stamp
->v
, stamp
->id
, 0);
864 vfs_addstamp (stamp
->v
, stamp
->id
, stamp
->parent
);
868 vfs_addstamp (oldvfs
, oldvfsid
, parent
);
873 vfs_stamp_path (char *path
)
877 struct vfs_stamping
*par
, *stamp
;
879 vfs
= vfs_type (path
);
880 id
= vfs_ncs_getid (vfs
, path
, &par
);
881 vfs_addstamp (vfs
, id
, par
);
883 for (stamp
= par
; stamp
!= NULL
; stamp
= stamp
->parent
)
884 vfs_addstamp (stamp
->v
, stamp
->id
, stamp
->parent
);
885 vfs_rm_parents (par
);
888 #ifndef VFS_STANDALONE
890 vfs_add_current_stamps (void)
892 vfs_stamp_path (current_dir
);
895 if (get_current_type () == view_listing
)
896 vfs_stamp_path (cpanel
->cwd
);
900 if (get_other_type () == view_listing
)
901 vfs_stamp_path (opanel
->cwd
);
906 /* This function is really broken */
908 mc_chdir (char *path
)
915 struct vfs_stamping
*parent
;
917 a
= current_dir
; /* Save a copy for case of failure */
918 current_dir
= vfs_canon (path
);
919 current_vfs
= vfs_type (current_dir
);
920 b
= g_strdup (current_dir
);
921 result
= (*current_vfs
->chdir
) ? (*current_vfs
->chdir
)(current_vfs
, vfs_name (b
)) : -1;
924 errno
= ferrno (current_vfs
);
925 g_free (current_dir
);
926 current_vfs
= vfs_type (a
);
929 oldvfs
= vfs_type (a
);
930 oldvfsid
= vfs_ncs_getid (oldvfs
, a
, &parent
);
932 vfs_add_noncurrent_stamps (oldvfs
, oldvfsid
, parent
);
933 vfs_rm_parents (parent
);
937 p
= strchr (current_dir
, 0) - 1;
938 if (*p
== PATH_SEP
&& p
> current_dir
)
939 *p
= 0; /* Sometimes we assume no trailing slash on cwd */
945 vfs_current_is_local (void)
947 return current_vfs
== &vfs_local_ops
;
951 /* External world should not do differences between VFS-s */
953 vfs_current_is_extfs (void)
955 return current_vfs
== &vfs_extfs_ops
;
959 vfs_current_is_tarfs (void)
961 return current_vfs
== &vfs_tarfs_ops
;
965 vfs_current_is_cpiofs (void)
967 return current_vfs
== &vfs_cpiofs_ops
;
972 vfs_file_is_local (const char *file
)
974 char *filename
= vfs_canon (file
);
975 vfs
*vfs
= vfs_type (filename
);
978 return vfs
== &vfs_local_ops
;
982 vfs_file_is_ftp (char *filename
)
987 filename
= vfs_canon (filename
);
988 vfs
= vfs_type (filename
);
990 return vfs
== &vfs_ftpfs_ops
;
997 vfs_file_is_smb (char *filename
)
1003 filename
= vfs_canon (filename
);
1004 vfs
= vfs_type (filename
);
1006 return vfs
== &vfs_smbfs_ops
;
1007 #endif /* USE_NETCODE */
1008 #endif /* WITH_SMBFS */
1012 char *vfs_get_current_dir (void)
1017 static void vfs_setup_wd (void)
1019 current_dir
= g_strdup (PATH_SEP_STR
);
1020 if (!(vfs_flags
& FL_NO_CWDSETUP
))
1023 if (strlen(current_dir
)>MC_MAXPATHLEN
-2)
1024 vfs_die ("Current dir too long.\n");
1027 MC_NAMEOP (mkdir
, (char *path
, mode_t mode
), (vfs
, vfs_name (path
), mode
))
1028 MC_NAMEOP (rmdir
, (char *path
), (vfs
, vfs_name (path
)))
1029 MC_NAMEOP (mknod
, (char *path
, int mode
, int dev
), (vfs
, vfs_name (path
), mode
, dev
))
1032 static struct mc_mmapping
{
1036 struct mc_mmapping
*next
;
1037 } *mc_mmaparray
= NULL
;
1040 mc_mmap (caddr_t addr
, size_t len
, int prot
, int flags
, int fd
, off_t offset
)
1044 struct mc_mmapping
*mcm
;
1047 return (caddr_t
) -1;
1050 result
= vfs
->mmap
? (*vfs
->mmap
)(vfs
, addr
, len
, prot
, flags
, vfs_info (fd
), offset
) : (caddr_t
)-1;
1051 if (result
== (caddr_t
)-1){
1052 errno
= ferrno (vfs
);
1055 mcm
=g_new (struct mc_mmapping
, 1);
1057 mcm
->vfs_info
= vfs_info (fd
);
1059 mcm
->next
= mc_mmaparray
;
1065 mc_munmap (caddr_t addr
, size_t len
)
1067 struct mc_mmapping
*mcm
, *mcm2
= NULL
;
1069 for (mcm
= mc_mmaparray
; mcm
!= NULL
; mcm2
= mcm
, mcm
= mcm
->next
){
1070 if (mcm
->addr
== addr
){
1072 mc_mmaparray
= mcm
->next
;
1074 mcm2
->next
= mcm
->next
;
1075 if (mcm
->vfs
->munmap
)
1076 (*mcm
->vfs
->munmap
)(mcm
->vfs
, addr
, len
, mcm
->vfs_info
);
1087 mc_def_getlocalcopy (vfs
*vfs
, char *filename
)
1096 fdin
= mc_open (filename
, O_RDONLY
);
1100 /* Try to preserve existing extension */
1101 for (ptr
= filename
+ strlen(filename
) - 1; ptr
>= filename
; ptr
--) {
1107 if (!isalnum((unsigned char) *ptr
))
1111 fdout
= mc_mkstemps (&tmp
, "mclocalcopy", ext
);
1114 while ((i
= mc_read (fdin
, buffer
, sizeof (buffer
))) > 0){
1115 if (write (fdout
, buffer
, i
) != i
)
1120 i
= mc_close (fdin
);
1124 if (close (fdout
)==-1)
1127 if (mc_stat (filename
, &mystat
) != -1){
1128 chmod (tmp
, mystat
.st_mode
);
1133 if (fdout
) close(fdout
);
1134 if (fdin
) mc_close (fdin
);
1140 mc_getlocalcopy (const char *pathname
)
1143 char *path
= vfs_canon (pathname
);
1144 vfs
*vfs
= vfs_type (path
);
1146 result
= vfs
->getlocalcopy
? (*vfs
->getlocalcopy
)(vfs
, vfs_name (path
)) :
1147 mc_def_getlocalcopy (vfs
, vfs_name (path
));
1150 errno
= ferrno (vfs
);
1155 mc_def_ungetlocalcopy (vfs
*vfs
, char *filename
, char *local
, int has_changed
)
1156 { /* Dijkstra probably hates me... But he should teach me how to do this nicely. */
1157 int fdin
= -1, fdout
= -1, i
;
1161 fdin
= open (local
, O_RDONLY
);
1164 fdout
= mc_open (filename
, O_WRONLY
| O_TRUNC
);
1167 while ((i
= read (fdin
, buffer
, sizeof (buffer
))) > 0){
1168 if (mc_write (fdout
, buffer
, i
) != i
)
1174 if (close (fdin
)==-1) {
1179 if (mc_close (fdout
)==-1) {
1189 message_1s (1, _("Changes to file lost"), filename
);
1190 if (fdout
!=-1) mc_close(fdout
);
1191 if (fdin
!=-1) close(fdin
);
1198 mc_ungetlocalcopy (const char *pathname
, char *local
, int has_changed
)
1200 int return_value
= 0;
1201 char *path
= vfs_canon (pathname
);
1202 vfs
*vfs
= vfs_type (path
);
1204 return_value
= vfs
->ungetlocalcopy
?
1205 (*vfs
->ungetlocalcopy
)(vfs
, vfs_name (path
), local
, has_changed
) :
1206 mc_def_ungetlocalcopy (vfs
, vfs_name (path
), local
, has_changed
);
1208 return return_value
;
1212 * Hmm, as timeout is minute or so, do we need to care about usecs?
1215 timeoutcmp (struct timeval
*t1
, struct timeval
*t2
)
1217 return ((t1
->tv_sec
< t2
->tv_sec
)
1218 || ((t1
->tv_sec
== t2
->tv_sec
) && (t1
->tv_usec
<= t2
->tv_usec
)));
1221 /* This is called from timeout handler with now = 0, or can be called
1222 with now = 1 to force freeing all filesystems that are not in use */
1225 vfs_expire (int now
)
1227 static int locked
= 0;
1228 struct timeval time
;
1229 struct vfs_stamping
*stamp
, *st
;
1231 /* Avoid recursive invocation, e.g. when one of the free functions
1237 gettimeofday (&time
, NULL
);
1238 time
.tv_sec
-= vfs_timeout
;
1240 for (stamp
= stamps
; stamp
!= NULL
;){
1241 if (now
|| (timeoutcmp (&stamp
->time
, &time
))){
1243 (*stamp
->v
->free
) (stamp
->id
);
1244 vfs_rmstamp (stamp
->v
, stamp
->id
, 0);
1247 stamp
= stamp
->next
;
1253 vfs_timeout_handler (void)
1261 time_t current_time
;
1264 memset (vfs_file_table
, 0, sizeof (vfs_file_table
));
1265 current_time
= time (NULL
);
1266 t
= localtime (¤t_time
);
1267 current_mday
= t
->tm_mday
;
1268 current_mon
= t
->tm_mon
;
1269 current_year
= t
->tm_year
;
1271 /* We do not want to register vfs_local_ops */
1275 vfs_register (&vfs_ftpfs_ops
);
1277 vfs_register (&vfs_smbfs_ops
);
1280 vfs_register (&vfs_mcfs_ops
);
1284 vfs_register (&vfs_fish_ops
);
1285 vfs_register (&vfs_extfs_ops
);
1286 vfs_register (&vfs_sfs_ops
);
1287 vfs_register (&vfs_tarfs_ops
);
1288 vfs_register (&vfs_cpiofs_ops
);
1290 #ifdef USE_EXT2FSLIB
1291 vfs_register (&vfs_undelfs_ops
);
1298 vfs_free_resources (char *path
)
1302 struct vfs_stamping
*parent
;
1304 vfs
= vfs_type (path
);
1305 vid
= vfs_ncs_getid (vfs
, path
, &parent
);
1306 if (vid
!= (vfsid
) -1)
1308 vfs_rm_parents (parent
);
1312 /* Shutdown a vfs given a path name */
1314 vfs_shut_path (char *p
)
1317 struct vfs_stamping
*par
;
1319 the_vfs
= vfs_type (p
);
1320 vfs_ncs_getid (the_vfs
, p
, &par
);
1321 (*par
->v
->free
)(par
->id
);
1322 vfs_rm_parents (par
);
1329 struct vfs_stamping
*stamp
, *st
;
1332 for (stamp
= stamps
, stamps
= 0; stamp
!= NULL
;){
1333 (*stamp
->v
->free
)(stamp
->id
);
1340 vfs_rmstamp (stamps
->v
, stamps
->id
, 1);
1343 g_free (current_dir
);
1345 for (vfs
=vfs_list
; vfs
; vfs
=vfs
->next
)
1351 * These ones grab information from the VFS
1352 * and handles them to an upper layer
1355 vfs_fill_names (void (*func
)(char *))
1359 for (vfs
=vfs_list
; vfs
; vfs
=vfs
->next
)
1360 if (vfs
->fill_names
)
1361 (*vfs
->fill_names
) (vfs
, func
);
1364 /* Following stuff (parse_ls_lga) is used by ftpfs and extfs */
1367 static char *columns
[MAXCOLS
]; /* Points to the string in column n */
1368 static int column_ptr
[MAXCOLS
]; /* Index from 0 to the starting positions of the columns */
1371 vfs_split_text (char *p
)
1376 memset (columns
, 0, sizeof (columns
));
1378 for (numcols
= 0; *p
&& numcols
< MAXCOLS
; numcols
++){
1379 while (*p
== ' ' || *p
== '\r' || *p
== '\n'){
1383 columns
[numcols
] = p
;
1384 column_ptr
[numcols
] = p
- original
;
1385 while (*p
&& *p
!= ' ' && *p
!= '\r' && *p
!= '\n')
1394 char *column
= columns
[idx
];
1396 if (!column
|| column
[0] < '0' || column
[0] > '9')
1403 is_dos_date (char *str
)
1408 if (strlen (str
) == 8 && str
[2] == str
[5]
1409 && strchr ("\\-/", (int) str
[2]) != NULL
)
1416 is_week (char *str
, struct tm
*tim
)
1418 static const char *week
= "SunMonTueWedThuFriSat";
1424 if ((pos
= strstr (week
, str
)) != NULL
) {
1426 tim
->tm_wday
= (pos
- week
) / 3;
1433 is_month (char *str
, struct tm
*tim
)
1435 static const char *month
= "JanFebMarAprMayJunJulAugSepOctNovDec";
1441 if ((pos
= strstr (month
, str
)) != NULL
) {
1443 tim
->tm_mon
= (pos
- month
) / 3;
1450 is_time (char *str
, struct tm
*tim
)
1457 if ((p
= strchr (str
, ':')) && (p2
= strrchr (str
, ':'))) {
1460 (str
, "%2d:%2d:%2d", &tim
->tm_hour
, &tim
->tm_min
,
1464 if (sscanf (str
, "%2d:%2d", &tim
->tm_hour
, &tim
->tm_min
) != 2)
1473 static int is_year (char *str
, struct tm
*tim
)
1480 if (strchr (str
, ':'))
1483 if (strlen (str
) != 4)
1486 if (sscanf (str
, "%ld", &year
) != 1)
1489 if (year
< 1900 || year
> 3000)
1492 tim
->tm_year
= (int) (year
- 1900);
1498 * FIXME: this is broken. Consider following entry:
1499 * -rwx------ 1 root root 1 Aug 31 10:04 2904 1234
1500 * where "2904 1234" is filename. Well, this code decodes it as year :-(.
1504 vfs_parse_filetype (char c
)
1507 case 'd': return S_IFDIR
;
1508 case 'b': return S_IFBLK
;
1509 case 'c': return S_IFCHR
;
1510 case 'l': return S_IFLNK
;
1511 case 's': /* Socket */
1515 /* If not supported, we fall through to IFIFO */
1518 case 'D': /* Solaris door */
1524 case 'p': return S_IFIFO
;
1525 case 'm': case 'n': /* Don't know what these are :-) */
1526 case '-': case '?': return S_IFREG
;
1531 int vfs_parse_filemode (const char *p
)
1532 { /* converts rw-rw-rw- into 0666 */
1535 case 'r': res
|= 0400; break;
1540 case 'w': res
|= 0200; break;
1545 case 'x': res
|= 0100; break;
1546 case 's': res
|= 0100 | S_ISUID
; break;
1547 case 'S': res
|= S_ISUID
; break;
1552 case 'r': res
|= 0040; break;
1557 case 'w': res
|= 0020; break;
1562 case 'x': res
|= 0010; break;
1563 case 's': res
|= 0010 | S_ISGID
; break;
1564 case 'l': /* Solaris produces these */
1565 case 'S': res
|= S_ISGID
; break;
1570 case 'r': res
|= 0004; break;
1575 case 'w': res
|= 0002; break;
1580 case 'x': res
|= 0001; break;
1581 case 't': res
|= 0001 | S_ISVTX
; break;
1582 case 'T': res
|= S_ISVTX
; break;
1589 int vfs_parse_filedate(int idx
, time_t *t
)
1590 { /* This thing parses from idx in columns[] array */
1597 /* Let's setup default time values */
1598 tim
.tm_year
= current_year
;
1599 tim
.tm_mon
= current_mon
;
1600 tim
.tm_mday
= current_mday
;
1604 tim
.tm_isdst
= -1; /* Let mktime() try to guess correct dst offset */
1606 p
= columns
[idx
++];
1608 /* We eat weekday name in case of extfs */
1609 if(is_week(p
, &tim
))
1610 p
= columns
[idx
++];
1613 if(is_month(p
, &tim
)){
1614 /* And we expect, it followed by day number */
1616 tim
.tm_mday
= (int)atol (columns
[idx
++]);
1618 return 0; /* No day */
1621 /* We usually expect:
1624 But in case of extfs we allow these date formats:
1627 Wek Mon DD hh:mm:ss YYYY
1629 where Mon is Jan-Dec, DD, MM, YY two digit day, month, year,
1630 YYYY four digit year, hh, mm, ss two digit hour, minute or second. */
1632 /* Here just this special case with MM-DD-YY */
1633 if (is_dos_date(p
)){
1636 if(sscanf(p
, "%2d-%2d-%2d", &d
[0], &d
[1], &d
[2]) == 3){
1637 /* We expect to get:
1643 /* Hmm... maybe, next time :)*/
1645 /* At last, MM-DD-YY */
1646 d
[0]--; /* Months are zerobased */
1656 return 0; /* sscanf failed */
1658 return 0; /* unsupported format */
1661 /* Here we expect to find time and/or year */
1664 if(is_time(columns
[idx
], &tim
) || (got_year
= is_year(columns
[idx
], &tim
))) {
1667 /* This is a special case for ctime() or Mon DD YYYY hh:mm */
1668 if(is_num (idx
) && (columns
[idx
+1][0]) &&
1669 ((got_year
|= is_year(columns
[idx
], &tim
)) || is_time(columns
[idx
], &tim
)))
1670 idx
++; /* time & year or reverse */
1671 } /* only time or date */
1674 return 0; /* Nor time or date */
1677 * If the date is less than 6 months in the past, it is shown without year
1678 * other dates in the past or future are shown with year but without time
1679 * This does not check for years before 1900 ... I don't know, how
1680 * to represent them at all
1683 current_mon
< 6 && current_mon
< tim
.tm_mon
&&
1684 tim
.tm_mon
- current_mon
>= 6)
1688 if ((*t
= mktime(&tim
)) < 0)
1694 vfs_parse_ls_lga (const char *p
, struct stat
*s
, char **filename
, char **linkname
)
1696 int idx
, idx2
, num_cols
;
1698 char *p_copy
= NULL
;
1700 const char *line
= p
;
1702 if (strncmp (p
, "total", 5) == 0)
1705 if ((i
= vfs_parse_filetype(*(p
++))) == -1)
1709 if (*p
== ' ') /* Notwell 4 */
1712 if (strlen (p
) <= 8 || p
[8] != ']')
1714 /* Should parse here the Notwell permissions :) */
1715 if (S_ISDIR (s
->st_mode
))
1716 s
->st_mode
|= (S_IRUSR
| S_IRGRP
| S_IROTH
| S_IWUSR
| S_IXUSR
| S_IXGRP
| S_IXOTH
);
1718 s
->st_mode
|= (S_IRUSR
| S_IRGRP
| S_IROTH
| S_IWUSR
);
1721 if ((i
= vfs_parse_filemode(p
)) == -1)
1726 /* This is for an extra ACL attribute (HP-UX) */
1731 p_copy
= g_strdup(p
);
1732 num_cols
= vfs_split_text (p_copy
);
1734 s
->st_nlink
= atol (columns
[0]);
1735 if (s
->st_nlink
<= 0)
1739 s
->st_uid
= finduid (columns
[1]);
1741 s
->st_uid
= (uid_t
) atol (columns
[1]);
1743 /* Mhm, the ls -lg did not produce a group field */
1744 for (idx
= 3; idx
<= 5; idx
++)
1745 if (is_month(columns
[idx
], NULL
) || is_week(columns
[idx
], NULL
) || is_dos_date(columns
[idx
]))
1748 if (idx
== 6 || (idx
== 5 && !S_ISCHR (s
->st_mode
) && !S_ISBLK (s
->st_mode
)))
1751 /* We don't have gid */
1752 if (idx
== 3 || (idx
== 4 && (S_ISCHR(s
->st_mode
) || S_ISBLK (s
->st_mode
))))
1755 /* We have gid field */
1757 s
->st_gid
= (gid_t
) atol (columns
[2]);
1759 s
->st_gid
= findgid (columns
[2]);
1763 /* This is device */
1764 if (S_ISCHR (s
->st_mode
) || S_ISBLK (s
->st_mode
)){
1767 if (!is_num (idx2
) || sscanf(columns
[idx2
], " %d,", &maj
) != 1)
1770 if (!is_num (++idx2
) || sscanf(columns
[idx2
], " %d", &min
) != 1)
1774 s
->st_rdev
= ((maj
& 0xff) << 8) | (min
& 0xffff00ff);
1779 /* Common file size */
1783 s
->st_size
= (size_t) atol (columns
[idx2
]);
1789 idx
= vfs_parse_filedate(idx
, &s
->st_mtime
);
1792 /* Use resulting time value */
1793 s
->st_atime
= s
->st_ctime
= s
->st_mtime
;
1795 /* These variables must be initialized by vfs_s_new_inode () */
1799 #ifdef HAVE_ST_BLKSIZE
1800 s
->st_blksize
= 512;
1802 #ifdef HAVE_ST_BLOCKS
1803 s
->st_blocks
= (s
->st_size
+ 511) / 512;
1806 for (i
= idx
+ 1, idx2
= 0; i
< num_cols
; i
++ )
1807 if (strcmp (columns
[i
], "->") == 0){
1812 if (((S_ISLNK (s
->st_mode
) ||
1813 (num_cols
== idx
+ 3 && s
->st_nlink
> 1))) /* Maybe a hardlink? (in extfs) */
1817 *filename
= g_strndup (p
+ column_ptr
[idx
], column_ptr
[idx2
] - column_ptr
[idx
] - 1);
1820 t
= g_strdup (p
+ column_ptr
[idx2
+1]);
1824 /* Extract the filename from the string copy, not from the columns
1825 * this way we have a chance of entering hidden directories like ". ."
1829 *filename = g_strdup (columns [idx++]);
1832 t
= g_strdup (p
+ column_ptr
[idx
]);
1841 if ((--p
> 0) && (t
[p
] == '\r' || t
[p
] == '\n'))
1843 if ((--p
> 0) && (t
[p
] == '\r' || t
[p
] == '\n'))
1852 static int errorcount
= 0;
1854 if (++errorcount
< 5) {
1855 message_1s (1, _("Could not parse:"), (p_copy
&& *p_copy
) ? p_copy
: line
);
1856 } else if (errorcount
== 5)
1857 message_1s (1, _(" Error "), _("More parsing errors will be ignored."));
1867 message_1s (1, _("Internal error:"), m
);
1872 vfs_print_stats (const char *fs_name
, const char *action
, const char *file_name
, off_t have
, off_t need
)
1874 static char *i18n_percent_transf_format
= NULL
, *i18n_transf_format
= NULL
;
1876 if (i18n_percent_transf_format
== NULL
) {
1877 i18n_percent_transf_format
= _("%s: %s: %s %3d%% (%lu bytes transferred)");
1878 i18n_transf_format
= _("%s: %s: %s %lu bytes transferred");
1882 print_vfs_message (i18n_percent_transf_format
, fs_name
, action
,
1883 file_name
, (int)((double)have
*100/need
), (unsigned long) have
);
1885 print_vfs_message (i18n_transf_format
,
1886 fs_name
, action
, file_name
, (unsigned long) have
);
1889 #ifndef VFS_STANDALONE
1891 vfs_get_password (char *msg
)
1893 return (char *) input_dialog (msg
, _("Password:"), "");
1898 * Returns vfs path corresponding to given url. If passed string is
1899 * not recognized as url, g_strdup(url) is returned.
1902 vfs_translate_url (char *url
)
1904 if (strncmp (url
, "ftp://", 6) == 0)
1905 return g_strconcat ("/#ftp:", url
+ 6, NULL
);
1906 else if (strncmp (url
, "a:", 2) == 0)
1907 return g_strdup ("/#a");
1909 return g_strdup (url
);