1 /* Virtual File System: External file system.
2 Copyright (C) 1995 The Free Software Foundation
4 Written by: 1995 Jakub Jelinek
5 Rewritten by: 1998 Pavel Machek
6 Additional changes by: 1999 Andrew T. Veliath
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 /* Namespace: exports only vfs_extfs_ops */
31 #include <sys/types.h>
36 #ifdef HAVE_SYS_WAIT_H
41 #include "../src/dialog.h"
42 #include "../src/main.h" /* For shell_execute */
43 #include "xdirentry.h"
48 #define ERRNOR(x,y) do { my_errno = x; return y; } while(0)
52 struct entry
*first_in_subdir
; /* only used if this is a directory */
53 struct entry
*last_in_subdir
;
54 ino_t inode
; /* This is inode # */
55 dev_t dev
; /* This is an internal identification of the extfs archive */
56 struct archive
*archive
; /* And this is an archive structure */
71 struct entry
*next_in_dir
;
78 struct archive
*archive
;
79 unsigned int has_changed
:1;
85 find_entry (struct entry
*dir
, char *name
, int make_dirs
, int make_file
);
86 static int extfs_which (vfs
*me
, char *path
);
87 static void remove_entry (struct entry
*e
);
89 static struct archive
*first_archive
= NULL
;
90 static int my_errno
= 0;
91 static struct stat hstat
; /* Stat struct corresponding */
94 static char *extfs_prefixes
[MAXEXTFS
];
95 static char extfs_need_archive
[MAXEXTFS
];
96 static int extfs_no
= 0;
98 static void extfs_fill_names (vfs
*me
, void (*func
)(char *))
100 struct archive
*a
= first_archive
;
104 name
= g_strconcat (a
->name
? a
->name
: "",
105 "#", extfs_prefixes
[a
->fstype
],
106 PATH_SEP_STR
, a
->current_dir
->name
, NULL
);
113 static void make_dot_doubledot (struct entry
*ent
)
115 struct entry
*entry
= g_new (struct entry
, 1);
116 struct entry
*parentry
= ent
->dir
;
117 struct inode
*inode
= ent
->inode
, *parent
;
119 parent
= (parentry
!= NULL
) ? parentry
->inode
: NULL
;
120 entry
->name
= g_strdup (".");
121 entry
->inode
= inode
;
123 inode
->local_filename
= NULL
;
124 inode
->first_in_subdir
= entry
;
125 inode
->last_in_subdir
= entry
;
127 entry
->next_in_dir
= g_new (struct entry
, 1);
128 entry
=entry
->next_in_dir
;
129 entry
->name
= g_strdup ("..");
130 inode
->last_in_subdir
= entry
;
131 entry
->next_in_dir
= NULL
;
132 if (parent
!= NULL
) {
133 entry
->inode
= parent
;
134 entry
->dir
= parentry
;
137 entry
->inode
= inode
;
143 static struct entry
*generate_entry (struct archive
*archive
,
144 char *name
, struct entry
*parentry
, mode_t mode
)
147 struct inode
*inode
, *parent
;
150 parent
= (parentry
!= NULL
) ? parentry
->inode
: NULL
;
151 entry
= g_new (struct entry
, 1);
153 entry
->name
= g_strdup (name
);
154 entry
->next_in_dir
= NULL
;
155 entry
->dir
= parentry
;
156 if (parent
!= NULL
) {
157 parent
->last_in_subdir
->next_in_dir
= entry
;
158 parent
->last_in_subdir
= entry
;
160 inode
= g_new (struct inode
, 1);
161 entry
->inode
= inode
;
162 inode
->local_filename
= NULL
;
164 inode
->inode
= (archive
->__inode_counter
)++;
165 inode
->dev
= archive
->rdev
;
166 inode
->archive
= archive
;
167 myumask
= umask (022);
169 inode
->mode
= mode
& ~myumask
;
172 inode
->uid
= getuid ();
173 inode
->gid
= getgid ();
175 inode
->mtime
= time (NULL
);
176 inode
->atime
= inode
->mtime
;
177 inode
->ctime
= inode
->mtime
;
180 make_dot_doubledot (entry
);
184 static void free_entries (struct entry
*entry
)
189 static void free_archive (struct archive
*archive
)
191 free_entries (archive
->root_entry
);
192 if (archive
->local_name
!= NULL
) {
195 mc_stat (archive
->local_name
, &my
);
196 mc_ungetlocalcopy (archive
->name
, archive
->local_name
,
197 archive
->local_stat
.st_mtime
!= my
.st_mtime
);
198 /* ungetlocalcopy frees local_name for us */
201 g_free (archive
->name
);
205 static FILE *open_archive (int fstype
, char *name
, struct archive
**pparc
)
207 static dev_t __extfs_no
= 0;
213 struct archive
*current_archive
;
214 struct entry
*root_entry
;
215 char *local_name
= NULL
, *tmp
= 0;
216 int uses_archive
= extfs_need_archive
[fstype
];
219 if (mc_stat (name
, &mystat
) == -1)
221 if (!vfs_file_is_local (name
)) {
222 local_name
= mc_getlocalcopy (name
);
223 if (local_name
== NULL
)
226 tmp
= name_quote (name
, 0);
230 /* Sorry, what is this good for? */
231 if (uses_archive
== EFS_NEED_ARG
){
232 tmp
= name_quote (name
, 0);
236 mc_extfsdir
= concat_dir_and_file (mc_home
, "extfs/");
237 cmd
= g_strconcat (mc_extfsdir
, extfs_prefixes
[fstype
],
238 " list ", local_name
? local_name
: tmp
, NULL
);
241 g_free (mc_extfsdir
);
242 result
= popen (cmd
, "r");
244 if (result
== NULL
) {
245 if (local_name
!= NULL
&& uses_archive
)
246 mc_ungetlocalcopy (name
, local_name
, 0);
250 current_archive
= g_new (struct archive
, 1);
251 current_archive
->fstype
= fstype
;
252 current_archive
->name
= name
? g_strdup (name
): name
;
253 current_archive
->local_name
= local_name
;
255 if (local_name
!= NULL
)
256 mc_stat (local_name
, ¤t_archive
->local_stat
);
257 current_archive
->__inode_counter
= 0;
258 current_archive
->fd_usage
= 0;
259 current_archive
->extfsstat
= mystat
;
260 current_archive
->rdev
= __extfs_no
++;
261 current_archive
->next
= first_archive
;
262 first_archive
= current_archive
;
263 mode
= current_archive
->extfsstat
.st_mode
& 07777;
271 root_entry
= generate_entry (current_archive
, "/", NULL
, mode
);
272 root_entry
->inode
->uid
= current_archive
->extfsstat
.st_uid
;
273 root_entry
->inode
->gid
= current_archive
->extfsstat
.st_gid
;
274 root_entry
->inode
->atime
= current_archive
->extfsstat
.st_atime
;
275 root_entry
->inode
->ctime
= current_archive
->extfsstat
.st_ctime
;
276 root_entry
->inode
->mtime
= current_archive
->extfsstat
.st_mtime
;
277 current_archive
->root_entry
= root_entry
;
278 current_archive
->current_dir
= root_entry
;
280 *pparc
= current_archive
;
286 * Main loop for reading an archive.
287 * Returns 0 on success, -1 on error.
289 static int read_archive (int fstype
, char *name
, struct archive
**pparc
)
293 struct archive
*current_archive
;
294 char *current_file_name
, *current_link_name
;
297 if ((extfsd
= open_archive (fstype
, name
, ¤t_archive
)) == NULL
) {
298 message_3s (1, MSG_ERROR
, _("Couldn't open %s archive\n%s"),
299 extfs_prefixes
[fstype
], name
);
303 buffer
= g_malloc (4096);
304 while (fgets (buffer
, 4096, extfsd
) != NULL
) {
305 current_link_name
= NULL
;
306 if (vfs_parse_ls_lga (buffer
, &hstat
, ¤t_file_name
, ¤t_link_name
)) {
307 struct entry
*entry
, *pent
;
309 char *p
, *q
, *cfn
= current_file_name
;
315 if (p
!= cfn
&& *(p
- 1) == '/')
317 p
= strrchr (cfn
, '/');
325 if (S_ISDIR (hstat
.st_mode
) &&
326 (!strcmp (p
, ".") || !strcmp (p
, "..")))
327 goto read_extfs_continue
;
328 pent
= find_entry (current_archive
->root_entry
, q
, 1, 0) ;
330 message_1s (1, MSG_ERROR
, _("Inconsistent extfs archive"));
331 /* FIXME: Should clean everything one day */
336 entry
= g_new (struct entry
, 1);
337 entry
->name
= g_strdup (p
);
338 entry
->next_in_dir
= NULL
;
341 if (pent
->inode
->last_in_subdir
){
342 pent
->inode
->last_in_subdir
->next_in_dir
= entry
;
343 pent
->inode
->last_in_subdir
= entry
;
346 if (!S_ISLNK (hstat
.st_mode
) && current_link_name
!= NULL
) {
347 pent
= find_entry (current_archive
->root_entry
, current_link_name
, 0, 0);
349 message_1s (1, MSG_ERROR
, _("Inconsistent extfs archive"));
350 /* FIXME: Should clean everything one day */
355 entry
->inode
= pent
->inode
;
356 pent
->inode
->nlink
++;
359 inode
= g_new (struct inode
, 1);
360 entry
->inode
= inode
;
361 inode
->local_filename
= NULL
;
362 inode
->inode
= (current_archive
->__inode_counter
)++;
364 inode
->dev
= current_archive
->rdev
;
365 inode
->archive
= current_archive
;
366 inode
->mode
= hstat
.st_mode
;
368 inode
->rdev
= hstat
.st_rdev
;
372 inode
->uid
= hstat
.st_uid
;
373 inode
->gid
= hstat
.st_gid
;
374 inode
->size
= hstat
.st_size
;
375 inode
->mtime
= hstat
.st_mtime
;
376 inode
->atime
= hstat
.st_atime
;
377 inode
->ctime
= hstat
.st_ctime
;
378 if (current_link_name
!= NULL
&& S_ISLNK (hstat
.st_mode
)) {
379 inode
->linkname
= current_link_name
;
380 current_link_name
= NULL
;
382 if (S_ISLNK( hstat
.st_mode
))
383 inode
->mode
&= ~S_IFLNK
; /* You *DON'T* want to do this always */
384 inode
->linkname
= NULL
;
386 if (S_ISDIR (hstat
.st_mode
))
387 make_dot_doubledot (entry
);
391 g_free (current_file_name
);
392 if (current_link_name
!= NULL
)
393 g_free (current_link_name
);
398 waitpid(-1,NULL
,WNOHANG
);
399 #endif /* SCO_FLAVOR */
400 *pparc
= current_archive
;
405 static char *get_path (char *inname
, struct archive
**archive
, int is_dir
,
408 /* Returns path inside argument. Returned char* is inside inname, which is mangled
409 * by this operation (so you must not free it's return value)
411 static char *get_path_mangle (char *inname
, struct archive
**archive
, int is_dir
,
414 char *local
, *archive_name
, *op
;
416 struct archive
*parc
;
417 struct vfs_stamping
*parent
;
421 archive_name
= inname
;
422 vfs_split( inname
, &local
, &op
);
423 fstype
= extfs_which( NULL
, op
); /* FIXME: we really should pass
424 self pointer. But as we know that extfs_which does not touch vfs
425 *me, it does not matter for now */
431 /* All filesystems should have some local archive, at least
434 * Actually, we should implement an alias mechanism that would
435 * translate: "a:" to "dos:a.
438 for (parc
= first_archive
; parc
!= NULL
; parc
= parc
->next
)
440 if (!strcmp (parc
->name
, archive_name
)) {
441 struct stat
*s
=&(parc
->extfsstat
);
442 if (vfs_uid
&& (!(s
->st_mode
& 0004)))
443 if ((s
->st_gid
!= vfs_gid
) || !(s
->st_mode
& 0040))
444 if ((s
->st_uid
!= vfs_uid
) || !(s
->st_mode
& 0400))
446 /* This is not too secure - in some cases (/#mtools) files created
447 under user a are probably visible to everyone else since / usually
448 has permissions 755 */
449 vfs_stamp (&vfs_extfs_ops
, (vfsid
) parc
);
454 result
= do_not_open
? -1 : read_archive (fstype
, archive_name
, &parc
);
455 if (result
== -1) ERRNOR (EIO
, NULL
);
458 v
= vfs_type (archive_name
);
459 if (v
== &vfs_local_ops
) {
462 parent
= g_new (struct vfs_stamping
, 1);
465 parent
->id
= (*v
->getid
) (v
, archive_name
, &(parent
->parent
));
467 vfs_add_noncurrent_stamps (&vfs_extfs_ops
, (vfsid
) parc
, parent
);
468 vfs_rm_parents (parent
);
475 /* Returns allocated path (without leading slash) inside the archive */
476 static char *get_path_from_entry (struct entry
*entry
)
485 for (len
= 0, head
= 0; entry
->dir
; entry
= entry
->dir
) {
486 p
= g_new (struct list
, 1);
488 p
->name
= entry
->name
;
490 len
+= strlen (entry
->name
) + 1;
494 return g_strdup ("");
496 localpath
= g_malloc (len
);
499 strcat (localpath
, head
->name
);
501 strcat (localpath
, "/");
510 struct loop_protect
{
512 struct loop_protect
*next
;
517 static struct entry
*
518 __find_entry (struct entry
*dir
, char *name
,
519 struct loop_protect
*list
, int make_dirs
, int make_file
);
521 static struct entry
*
522 __resolve_symlinks (struct entry
*entry
,
523 struct loop_protect
*list
)
526 struct loop_protect
*looping
;
528 if (!S_ISLNK (entry
->inode
->mode
))
530 for (looping
= list
; looping
!= NULL
; looping
= looping
->next
)
531 if (entry
== looping
->entry
) { /* Here we protect us against symlink looping */
535 looping
= g_new (struct loop_protect
, 1);
536 looping
->entry
= entry
;
537 looping
->next
= list
;
538 pent
= __find_entry (entry
->dir
, entry
->inode
->linkname
, looping
, 0, 0);
545 static struct entry
*my_resolve_symlinks (struct entry
*entry
)
551 res
= __resolve_symlinks (entry
, NULL
);
561 static char *get_archive_name (struct archive
*archive
)
565 if (archive
->local_name
)
566 archive_name
= archive
->local_name
;
568 archive_name
= archive
->name
;
570 if (!archive_name
|| !*archive_name
)
571 return "no_archive_name";
576 static void extfs_run (char *file
)
578 struct archive
*archive
;
579 char *p
, *q
, *archive_name
, *mc_extfsdir
;
582 if ((p
= get_path (file
, &archive
, 0, 0)) == NULL
)
584 q
= name_quote (p
, 0);
587 archive_name
= name_quote (get_archive_name(archive
), 0);
588 mc_extfsdir
= concat_dir_and_file (mc_home
, "extfs/");
589 cmd
= g_strconcat (mc_extfsdir
, extfs_prefixes
[archive
->fstype
],
590 " run ", archive_name
, " ", q
, NULL
);
591 g_free (mc_extfsdir
);
592 g_free (archive_name
);
594 #ifndef VFS_STANDALONE
595 shell_execute(cmd
, 0);
597 vfs_die( "shell_execute: implement me!" );
602 static void *extfs_open (vfs
*me
, char *file
, int flags
, int mode
)
604 struct pseudofile
*extfs_info
;
605 struct archive
*archive
;
612 if ((q
= get_path_mangle (file
, &archive
, 0, 0)) == NULL
)
614 entry
= find_entry (archive
->root_entry
, q
, 0, 0);
615 if (entry
== NULL
&& (flags
& O_CREAT
)) {
616 /* Create new entry */
617 entry
= find_entry (archive
->root_entry
, q
, 0, 1);
618 created
= (entry
!= NULL
);
622 if ((entry
= my_resolve_symlinks (entry
)) == NULL
)
624 if (S_ISDIR (entry
->inode
->mode
)) ERRNOR (EISDIR
, NULL
);
625 if (entry
->inode
->local_filename
== NULL
) {
627 char *archive_name
, *p
;
631 handle
= mc_mkstemps (&entry
->inode
->local_filename
, "extfs", NULL
);
637 p
= get_path_from_entry (entry
);
638 q
= name_quote (p
, 0);
640 archive_name
= name_quote (get_archive_name (archive
), 0);
642 mc_extfsdir
= concat_dir_and_file (mc_home
, "extfs/");
643 cmd
= g_strconcat (mc_extfsdir
, extfs_prefixes
[archive
->fstype
],
646 " ", q
, " ", entry
->inode
->local_filename
, NULL
);
648 g_free (mc_extfsdir
);
649 g_free (archive_name
);
650 if (my_system (EXECUTE_AS_SHELL
, shell
, cmd
) && !created
){
651 free (entry
->inode
->local_filename
);
652 entry
->inode
->local_filename
= NULL
;
660 local_handle
= open (entry
->inode
->local_filename
, NO_LINEAR(flags
),
662 if (local_handle
== -1) ERRNOR (EIO
, NULL
);
664 extfs_info
= g_new (struct pseudofile
, 1);
665 extfs_info
->archive
= archive
;
666 extfs_info
->entry
= entry
;
667 extfs_info
->has_changed
= created
;
668 extfs_info
->local_handle
= local_handle
;
670 /* i.e. we had no open files and now we have one */
671 vfs_rmstamp (&vfs_extfs_ops
, (vfsid
) archive
, 1);
676 static int extfs_read (void *data
, char *buffer
, int count
)
678 struct pseudofile
*file
= (struct pseudofile
*)data
;
680 return read (file
->local_handle
, buffer
, count
);
683 static int extfs_close (void *data
)
685 struct pseudofile
*file
;
687 file
= (struct pseudofile
*)data
;
689 close (file
->local_handle
);
691 /* Commit the file if it has changed */
692 if (file
->has_changed
){
693 struct archive
*archive
;
694 char *archive_name
, *file_name
;
699 archive
= file
->archive
;
700 archive_name
= name_quote (get_archive_name (archive
), 0);
701 p
= get_path_from_entry (file
->entry
);
702 file_name
= name_quote (p
, 0);
705 mc_extfsdir
= concat_dir_and_file (mc_home
, "extfs/");
706 cmd
= g_strconcat (mc_extfsdir
,
707 extfs_prefixes
[archive
->fstype
],
708 " copyin ", archive_name
, " ",
710 file
->entry
->inode
->local_filename
, NULL
);
711 g_free (archive_name
);
713 g_free (mc_extfsdir
);
714 if (my_system (EXECUTE_AS_SHELL
, shell
, cmd
))
718 struct stat file_status
;
719 if (stat(file
->entry
->inode
->local_filename
,&file_status
) != 0)
722 file
->entry
->inode
->size
= file_status
.st_size
;
725 file
->entry
->inode
->mtime
= time (NULL
);
728 file
->archive
->fd_usage
--;
729 if (!file
->archive
->fd_usage
) {
730 struct vfs_stamping
*parent
;
733 if (!file
->archive
->name
|| !*file
->archive
->name
|| (v
= vfs_type (file
->archive
->name
)) == &vfs_local_ops
) {
736 parent
= g_new (struct vfs_stamping
, 1);
739 parent
->id
= (*v
->getid
) (v
, file
->archive
->name
, &(parent
->parent
));
741 vfs_add_noncurrent_stamps (&vfs_extfs_ops
, (vfsid
) (file
->archive
), parent
);
742 vfs_rm_parents (parent
);
746 if (errno_code
) ERRNOR (EIO
, -1);
750 #define RECORDSIZE 512
752 static char *get_path (char *inname
, struct archive
**archive
, int is_dir
,
755 char *buf
= g_strdup (inname
);
756 char *res
= get_path_mangle( buf
, archive
, is_dir
, do_not_open
);
759 res2
= g_strdup(res
);
765 __find_entry (struct entry
*dir
, char *name
,
766 struct loop_protect
*list
, int make_dirs
, int make_file
)
768 struct entry
*pent
, *pdir
;
769 char *p
, *q
, *name_end
;
772 if (*name
== '/') { /* Handle absolute paths */
774 dir
= dir
->inode
->archive
->root_entry
;
779 name_end
= name
+ strlen (name
);
785 for (; pent
!= NULL
&& c
&& *p
; ){
789 if (strcmp (p
, ".")){
790 if (!strcmp (p
, ".."))
793 if ((pent
= __resolve_symlinks (pent
, list
))==NULL
){
797 if (!S_ISDIR (pent
->inode
->mode
)){
803 for (pent
= pent
->inode
->first_in_subdir
; pent
; pent
= pent
->next_in_dir
)
804 /* Hack: I keep the original semanthic unless
805 q+1 would break in the strchr */
806 if (!strcmp (pent
->name
, p
)){
807 if (q
+ 1 > name_end
){
809 notadir
= !S_ISDIR (pent
->inode
->mode
);
815 /* When we load archive, we create automagically
816 * non-existant directories
818 if (pent
== NULL
&& make_dirs
) {
819 pent
= generate_entry (dir
->inode
->archive
, p
, pdir
, S_IFDIR
| 0777);
821 if (pent
== NULL
&& make_file
) {
822 pent
= generate_entry (dir
->inode
->archive
, p
, pdir
, 0777);
838 static struct entry
*find_entry (struct entry
*dir
, char *name
, int make_dirs
, int make_file
)
844 res
= __find_entry (dir
, name
, NULL
, make_dirs
, make_file
);
855 static int s_errno (vfs
*me
)
860 static void * s_opendir (vfs
*me
, char *dirname
)
862 struct archive
*archive
;
867 if ((q
= get_path_mangle (dirname
, &archive
, 1, 0)) == NULL
)
869 entry
= find_entry (archive
->root_entry
, q
, 0, 0);
872 if ((entry
= my_resolve_symlinks (entry
)) == NULL
)
874 if (!S_ISDIR (entry
->inode
->mode
)) ERRNOR (ENOTDIR
, NULL
);
876 info
= g_new (struct entry
*, 2);
877 info
[0] = entry
->inode
->first_in_subdir
;
878 info
[1] = entry
->inode
->first_in_subdir
;
883 static void * s_readdir(void *data
)
885 static union vfs_dirent dir
;
886 struct entry
**info
= (struct entry
**) data
;
891 strncpy(dir
.dent
.d_name
, (*info
)->name
, MC_MAXPATHLEN
);
892 dir
.dent
.d_name
[MC_MAXPATHLEN
] = 0;
894 compute_namelen(&dir
.dent
);
895 *info
= (*info
)->next_in_dir
;
897 return (void *) &dir
;
900 static int s_telldir (void *data
)
902 struct entry
**info
= (struct entry
**) data
;
908 if (cur
== info
[0]) return num
;
910 cur
= cur
->next_in_dir
;
915 static void s_seekdir (void *data
, int offset
)
917 struct entry
**info
= (struct entry
**) data
;
920 for (i
=0; i
<offset
; i
++)
924 static int s_closedir (void *data
)
930 static void stat_move( struct stat
*buf
, struct inode
*inode
)
932 buf
->st_dev
= inode
->dev
;
933 buf
->st_ino
= inode
->inode
;
934 buf
->st_mode
= inode
->mode
;
935 buf
->st_nlink
= inode
->nlink
;
936 buf
->st_uid
= inode
->uid
;
937 buf
->st_gid
= inode
->gid
;
939 buf
->st_rdev
= inode
->rdev
;
941 buf
->st_size
= inode
->size
;
942 #ifdef HAVE_ST_BLKSIZE
943 buf
->st_blksize
= RECORDSIZE
;
945 #ifdef HAVE_ST_BLOCKS
946 buf
->st_blocks
= (inode
->size
+ RECORDSIZE
- 1) / RECORDSIZE
;
948 buf
->st_atime
= inode
->atime
;
949 buf
->st_mtime
= inode
->mtime
;
950 buf
->st_ctime
= inode
->ctime
;
953 static int s_internal_stat (char *path
, struct stat
*buf
, int resolve
)
955 struct archive
*archive
;
960 if ((q
= get_path_mangle (path
, &archive
, 0, 0)) == NULL
)
962 entry
= find_entry (archive
->root_entry
, q
, 0, 0);
965 if (resolve
&& (entry
= my_resolve_symlinks (entry
)) == NULL
)
967 inode
= entry
->inode
;
968 stat_move( buf
, inode
);
972 static int s_stat (vfs
*me
, char *path
, struct stat
*buf
)
974 return s_internal_stat (path
, buf
, 1);
977 static int s_lstat (vfs
*me
, char *path
, struct stat
*buf
)
979 return s_internal_stat (path
, buf
, 0);
982 static int s_fstat (void *data
, struct stat
*buf
)
984 struct pseudofile
*file
= (struct pseudofile
*)data
;
987 inode
= file
->entry
->inode
;
988 stat_move( buf
, inode
);
992 static int s_readlink (vfs
*me
, char *path
, char *buf
, int size
)
994 struct archive
*archive
;
999 if ((q
= get_path_mangle (path
, &archive
, 0, 0)) == NULL
)
1001 entry
= find_entry (archive
->root_entry
, q
, 0, 0);
1004 if (!S_ISLNK (entry
->inode
->mode
)) ERRNOR (EINVAL
, -1);
1005 if (size
> (i
= strlen (entry
->inode
->linkname
))) {
1008 strncpy (buf
, entry
->inode
->linkname
, i
);
1012 static int extfs_chmod (vfs
*me
, char *path
, int mode
)
1017 static int extfs_write (void *data
, char *buf
, int nbyte
)
1019 struct pseudofile
*file
= (struct pseudofile
*)data
;
1021 file
->has_changed
= 1;
1022 return write (file
->local_handle
, buf
, nbyte
);
1025 static int extfs_unlink (vfs
*me
, char *file
)
1027 struct archive
*archive
;
1030 struct entry
*entry
;
1032 char *archive_name
, *p
;
1034 if ((q
= get_path_mangle (file
, &archive
, 0, 0)) == NULL
)
1036 entry
= find_entry (archive
->root_entry
, q
, 0, 0);
1039 if ((entry
= my_resolve_symlinks (entry
)) == NULL
)
1041 if (S_ISDIR (entry
->inode
->mode
)) ERRNOR (EISDIR
, -1);
1043 p
= get_path_from_entry (entry
);
1044 q
= name_quote (p
, 0);
1046 archive_name
= name_quote (get_archive_name (archive
), 0);
1048 mc_extfsdir
= concat_dir_and_file (mc_home
, "extfs/");
1049 cmd
= g_strconcat (mc_extfsdir
, extfs_prefixes
[archive
->fstype
],
1050 " rm ", archive_name
, " ", q
, NULL
);
1052 g_free (mc_extfsdir
);
1053 g_free (archive_name
);
1054 if (my_system (EXECUTE_AS_SHELL
, shell
, cmd
)){
1060 remove_entry (entry
);
1065 static int extfs_mkdir (vfs
*me
, char *path
, mode_t mode
)
1067 struct archive
*archive
;
1070 struct entry
*entry
;
1072 char *archive_name
, *p
;
1074 if ((q
= get_path_mangle (path
, &archive
, 0, 0)) == NULL
)
1076 entry
= find_entry (archive
->root_entry
, q
, 0, 0);
1077 if (entry
!= NULL
) ERRNOR (EEXIST
, -1);
1078 entry
= find_entry (archive
->root_entry
, q
, 1, 0);
1081 if ((entry
= my_resolve_symlinks (entry
)) == NULL
)
1083 if (!S_ISDIR (entry
->inode
->mode
)) ERRNOR (ENOTDIR
, -1);
1085 p
= get_path_from_entry (entry
);
1086 q
= name_quote (p
, 0);
1088 archive_name
= name_quote (get_archive_name (archive
), 0);
1090 mc_extfsdir
= concat_dir_and_file (mc_home
, "extfs/");
1091 cmd
= g_strconcat (mc_extfsdir
, extfs_prefixes
[archive
->fstype
],
1092 " mkdir ", archive_name
, " ", q
, NULL
);
1094 g_free (mc_extfsdir
);
1095 g_free (archive_name
);
1096 if (my_system (EXECUTE_AS_SHELL
, shell
, cmd
)){
1099 remove_entry (entry
);
1107 static int extfs_rmdir (vfs
*me
, char *path
)
1109 struct archive
*archive
;
1112 struct entry
*entry
;
1114 char *archive_name
, *p
;
1116 if ((q
= get_path_mangle (path
, &archive
, 0, 0)) == NULL
)
1118 entry
= find_entry (archive
->root_entry
, q
, 0, 0);
1121 if ((entry
= my_resolve_symlinks (entry
)) == NULL
)
1123 if (!S_ISDIR (entry
->inode
->mode
)) ERRNOR (ENOTDIR
, -1);
1125 p
= get_path_from_entry (entry
);
1126 q
= name_quote (p
, 0);
1128 archive_name
= name_quote (get_archive_name (archive
), 0);
1130 mc_extfsdir
= concat_dir_and_file (mc_home
, "extfs/");
1131 cmd
= g_strconcat (mc_extfsdir
, extfs_prefixes
[archive
->fstype
],
1132 " rmdir ", archive_name
, " ", q
, NULL
);
1134 g_free (mc_extfsdir
);
1135 g_free (archive_name
);
1136 if (my_system (EXECUTE_AS_SHELL
, shell
, cmd
)){
1142 remove_entry (entry
);
1147 static int extfs_chdir (vfs
*me
, char *path
)
1149 struct archive
*archive
;
1151 struct entry
*entry
;
1154 if ((q
= get_path_mangle (path
, &archive
, 1, 0)) == NULL
)
1156 entry
= find_entry (archive
->root_entry
, q
, 0, 0);
1159 entry
= my_resolve_symlinks (entry
);
1160 if ((!entry
) || (!S_ISDIR (entry
->inode
->mode
)))
1162 entry
->inode
->archive
->current_dir
= entry
;
1167 static int extfs_lseek (void *data
, off_t offset
, int whence
)
1169 struct pseudofile
*file
= (struct pseudofile
*) data
;
1171 return lseek (file
->local_handle
, offset
, whence
);
1174 static vfsid
extfs_getid (vfs
*me
, char *path
, struct vfs_stamping
**parent
)
1176 struct archive
*archive
;
1179 struct vfs_stamping
*par
;
1183 if (!(p
= get_path (path
, &archive
, 1, 1)))
1187 v
= vfs_type (archive
->name
);
1188 id
= (*v
->getid
) (v
, archive
->name
, &par
);
1189 if (id
!= (vfsid
)-1) {
1190 *parent
= g_new (struct vfs_stamping
, 1);
1193 (*parent
)->parent
= par
;
1194 (*parent
)->next
= NULL
;
1197 return (vfsid
) archive
;
1200 static int extfs_nothingisopen (vfsid id
)
1202 if (((struct archive
*)id
)->fd_usage
<= 0)
1207 static void remove_entry (struct entry
*e
)
1209 int i
= --(e
->inode
->nlink
);
1210 struct entry
*pe
, *ent
, *prev
;
1212 if (S_ISDIR (e
->inode
->mode
) && e
->inode
->first_in_subdir
!= NULL
) {
1213 struct entry
*f
= e
->inode
->first_in_subdir
;
1214 e
->inode
->first_in_subdir
= NULL
;
1218 if (e
== pe
->inode
->first_in_subdir
)
1219 pe
->inode
->first_in_subdir
= e
->next_in_dir
;
1222 for (ent
= pe
->inode
->first_in_subdir
; ent
&& ent
->next_in_dir
;
1223 ent
= ent
->next_in_dir
)
1224 if (e
== ent
->next_in_dir
) {
1229 prev
->next_in_dir
= e
->next_in_dir
;
1230 if (e
== pe
->inode
->last_in_subdir
)
1231 pe
->inode
->last_in_subdir
= prev
;
1234 if (e
->inode
->local_filename
!= NULL
) {
1235 unlink (e
->inode
->local_filename
);
1236 free (e
->inode
->local_filename
);
1238 if (e
->inode
->linkname
!= NULL
)
1239 g_free (e
->inode
->linkname
);
1247 static void free_entry (struct entry
*e
)
1249 int i
= --(e
->inode
->nlink
);
1250 if (S_ISDIR (e
->inode
->mode
) && e
->inode
->first_in_subdir
!= NULL
) {
1251 struct entry
*f
= e
->inode
->first_in_subdir
;
1253 e
->inode
->first_in_subdir
= NULL
;
1257 if (e
->inode
->local_filename
!= NULL
) {
1258 unlink (e
->inode
->local_filename
);
1259 free (e
->inode
->local_filename
);
1261 if (e
->inode
->linkname
!= NULL
)
1262 g_free (e
->inode
->linkname
);
1265 if (e
->next_in_dir
!= NULL
)
1266 free_entry (e
->next_in_dir
);
1271 static void extfs_free (vfsid id
)
1273 struct archive
*parc
;
1274 struct archive
*archive
= (struct archive
*)id
;
1276 free_entry (archive
->root_entry
);
1277 if (archive
== first_archive
) {
1278 first_archive
= archive
->next
;
1280 for (parc
= first_archive
; parc
!= NULL
; parc
= parc
->next
)
1281 if (parc
->next
== archive
)
1284 parc
->next
= archive
->next
;
1286 free_archive (archive
);
1289 static char *extfs_getlocalcopy (vfs
*me
, char *path
)
1291 struct pseudofile
*fp
=
1292 (struct pseudofile
*) extfs_open (me
, path
, O_RDONLY
, 0);
1297 if (fp
->entry
->inode
->local_filename
== NULL
) {
1298 extfs_close ((void *) fp
);
1301 p
= g_strdup (fp
->entry
->inode
->local_filename
);
1302 fp
->archive
->fd_usage
++;
1303 extfs_close ((void *) fp
);
1307 static int extfs_ungetlocalcopy (vfs
*me
, char *path
, char *local
, int has_changed
)
1309 struct pseudofile
*fp
=
1310 (struct pseudofile
*) extfs_open (me
, path
, O_RDONLY
, 0);
1314 if (!strcmp (fp
->entry
->inode
->local_filename
, local
)) {
1315 fp
->archive
->fd_usage
--;
1316 fp
->has_changed
|= has_changed
;
1317 extfs_close ((void *) fp
);
1320 /* Should not happen */
1321 extfs_close ((void *) fp
);
1322 return mc_def_ungetlocalcopy (me
, path
, local
, has_changed
);
1327 #include "../src/profile.h"
1328 static int extfs_init (vfs
*me
)
1333 mc_extfsini
= concat_dir_and_file (mc_home
, "extfs/extfs.ini");
1334 cfg
= fopen (mc_extfsini
, "r");
1337 fprintf(stderr
, _("Warning: file %s not found\n"), mc_extfsini
);
1338 g_free (mc_extfsini
);
1343 while ( extfs_no
< MAXEXTFS
) {
1347 if (!fgets( key
, sizeof (key
)-1, cfg
))
1350 /* Handle those with a trailing ':', those flag that the
1351 * file system does not require an archive to work
1355 /* We may not use vfs_die() message or message_1s or similar,
1356 * UI is not initialized at this time and message would not
1357 * appear on screen. */
1358 fprintf(stderr
, "Warning: You need to update your %s file.\n",
1361 g_free (mc_extfsini
);
1367 if ((c
= strchr (key
, '\n'))){
1369 c
= &key
[strlen (key
) - 1];
1373 extfs_need_archive
[extfs_no
] = !(*c
== ':');
1379 extfs_prefixes
[extfs_no
] = g_strdup (key
);
1383 g_free (mc_extfsini
);
1387 /* Do NOT use me argument in this function */
1388 static int extfs_which (vfs
*me
, char *path
)
1392 for (i
= 0; i
< extfs_no
; i
++)
1393 if (!strcmp (path
, extfs_prefixes
[i
]))
1398 static void extfs_done (vfs
*me
)
1402 for (i
= 0; i
< extfs_no
; i
++ )
1403 g_free (extfs_prefixes
[i
]);
1407 static int extfs_setctl (vfs
*me
, char *path
, int ctlop
, char *arg
)
1409 if (ctlop
== MCCTL_EXTFS_RUN
) {
1416 vfs vfs_extfs_ops
= {
1417 NULL
, /* This is place of next pointer */
1443 extfs_chmod
, /* chmod ... strange, returns success? */
1460 extfs_nothingisopen
,
1464 extfs_ungetlocalcopy
,
1466 extfs_mkdir
, /* mkdir */