1 /* Virtual File System: External file system.
2 Copyright (C) 1995, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
3 2006, 2007, 2009 Free Software Foundation, Inc.
5 Written by: 1995 Jakub Jelinek
6 Rewritten by: 1998 Pavel Machek
7 Additional changes by: 1999 Andrew T. Veliath
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. */
25 * \brief Source: Virtual File System: External file system
26 * \author Jakub Jelinek
27 * \author Pavel Machek
28 * \author Andrew T. Veliath
29 * \date 1995, 1998, 1999
32 /* Namespace: init_extfs */
40 #include <sys/types.h>
48 #include "../src/global.h"
50 #include "../src/wtools.h" /* message() */
51 #include "../src/main.h" /* print_vfs_message */
52 #include "../src/execute.h" /* For shell_execute */
57 #include "gc.h" /* vfs_rmstamp */
60 #define ERRNOR(x,y) do { my_errno = x; return y; } while(0)
64 struct entry
*first_in_subdir
; /* only used if this is a directory */
65 struct entry
*last_in_subdir
;
66 ino_t inode
; /* This is inode # */
67 dev_t dev
; /* This is an internal identification of the extfs archive */
68 struct archive
*archive
; /* And this is an archive structure */
82 struct entry
*next_in_dir
;
89 struct archive
*archive
;
90 unsigned int has_changed
:1;
99 struct stat local_stat
;
103 struct entry
*root_entry
;
104 struct archive
*next
;
107 static struct entry
*extfs_find_entry (struct entry
*dir
, char *name
,
108 int make_dirs
, int make_file
);
109 static int extfs_which (struct vfs_class
*me
, const char *path
);
110 static void extfs_remove_entry (struct entry
*e
);
111 static void extfs_free (vfsid id
);
112 static void extfs_free_entry (struct entry
*e
);
114 static struct vfs_class vfs_extfs_ops
;
115 static struct archive
*first_archive
= NULL
;
116 static int my_errno
= 0;
119 static char *extfs_prefixes
[MAXEXTFS
];
120 static char extfs_need_archive
[MAXEXTFS
];
121 static int extfs_no
= 0;
124 extfs_fill_names (struct vfs_class
*me
, fill_names_f func
)
126 struct archive
*a
= first_archive
;
133 g_strconcat (a
->name
? a
->name
: "", "#",
134 extfs_prefixes
[a
->fstype
], (char *) NULL
);
141 static void extfs_make_dots (struct entry
*ent
)
143 struct entry
*entry
= g_new (struct entry
, 1);
144 struct entry
*parentry
= ent
->dir
;
145 struct inode
*inode
= ent
->inode
, *parent
;
147 parent
= (parentry
!= NULL
) ? parentry
->inode
: NULL
;
148 entry
->name
= g_strdup (".");
149 entry
->inode
= inode
;
151 inode
->local_filename
= NULL
;
152 inode
->first_in_subdir
= entry
;
154 entry
->next_in_dir
= g_new (struct entry
, 1);
155 entry
= entry
->next_in_dir
;
156 entry
->name
= g_strdup ("..");
157 inode
->last_in_subdir
= entry
;
158 entry
->next_in_dir
= NULL
;
159 if (parent
!= NULL
) {
160 entry
->inode
= parent
;
161 entry
->dir
= parentry
;
164 entry
->inode
= inode
;
170 static struct entry
*extfs_generate_entry (struct archive
*archive
,
171 const char *name
, struct entry
*parentry
, mode_t mode
)
174 struct inode
*inode
, *parent
;
177 parent
= (parentry
!= NULL
) ? parentry
->inode
: NULL
;
178 entry
= g_new (struct entry
, 1);
180 entry
->name
= g_strdup (name
);
181 entry
->next_in_dir
= NULL
;
182 entry
->dir
= parentry
;
183 if (parent
!= NULL
) {
184 parent
->last_in_subdir
->next_in_dir
= entry
;
185 parent
->last_in_subdir
= entry
;
187 inode
= g_new (struct inode
, 1);
188 entry
->inode
= inode
;
189 inode
->local_filename
= NULL
;
190 inode
->linkname
= NULL
;
191 inode
->last_in_subdir
= NULL
;
192 inode
->inode
= (archive
->inode_counter
)++;
193 inode
->dev
= archive
->rdev
;
194 inode
->archive
= archive
;
195 myumask
= umask (022);
197 inode
->mode
= mode
& ~myumask
;
200 inode
->uid
= getuid ();
201 inode
->gid
= getgid ();
203 inode
->mtime
= time (NULL
);
204 inode
->atime
= inode
->mtime
;
205 inode
->ctime
= inode
->mtime
;
208 extfs_make_dots (entry
);
213 static void extfs_free_entries (struct entry
*entry
)
220 static void extfs_free_archive (struct archive
*archive
)
222 extfs_free_entry (archive
->root_entry
);
223 if (archive
->local_name
!= NULL
) {
226 mc_stat (archive
->local_name
, &my
);
227 mc_ungetlocalcopy (archive
->name
, archive
->local_name
,
228 archive
->local_stat
.st_mtime
!= my
.st_mtime
);
229 g_free(archive
->local_name
);
231 g_free (archive
->name
);
236 extfs_open_archive (int fstype
, const char *name
, struct archive
**pparc
)
238 static dev_t archive_counter
= 0;
244 struct archive
*current_archive
;
245 struct entry
*root_entry
;
246 char *local_name
= NULL
, *tmp
= 0;
247 int uses_archive
= extfs_need_archive
[fstype
];
250 if (mc_stat (name
, &mystat
) == -1)
252 if (!vfs_file_is_local (name
)) {
253 local_name
= mc_getlocalcopy (name
);
254 if (local_name
== NULL
)
257 tmp
= name_quote (name
, 0);
260 mc_extfsdir
= concat_dir_and_file (mc_home_alt
, "extfs" PATH_SEP_STR
);
262 g_strconcat (mc_extfsdir
, extfs_prefixes
[fstype
], " list ",
263 local_name
? local_name
: tmp
, (char *) NULL
);
265 g_free (mc_extfsdir
);
267 result
= popen (cmd
, "r");
269 if (result
== NULL
) {
270 close_error_pipe (D_ERROR
, NULL
);
272 mc_ungetlocalcopy (name
, local_name
, 0);
278 setvbuf (result
, NULL
, _IONBF
, 0);
281 current_archive
= g_new (struct archive
, 1);
282 current_archive
->fstype
= fstype
;
283 current_archive
->name
= name
? g_strdup (name
) : NULL
;
284 current_archive
->local_name
= local_name
;
286 if (local_name
!= NULL
)
287 mc_stat (local_name
, ¤t_archive
->local_stat
);
288 current_archive
->inode_counter
= 0;
289 current_archive
->fd_usage
= 0;
290 current_archive
->rdev
= archive_counter
++;
291 current_archive
->next
= first_archive
;
292 first_archive
= current_archive
;
293 mode
= mystat
.st_mode
& 07777;
301 root_entry
= extfs_generate_entry (current_archive
, "/", NULL
, mode
);
302 root_entry
->inode
->uid
= mystat
.st_uid
;
303 root_entry
->inode
->gid
= mystat
.st_gid
;
304 root_entry
->inode
->atime
= mystat
.st_atime
;
305 root_entry
->inode
->ctime
= mystat
.st_ctime
;
306 root_entry
->inode
->mtime
= mystat
.st_mtime
;
307 current_archive
->root_entry
= root_entry
;
309 *pparc
= current_archive
;
315 * Main loop for reading an archive.
316 * Return 0 on success, -1 on error.
319 extfs_read_archive (int fstype
, const char *name
, struct archive
**pparc
)
323 struct archive
*current_archive
;
324 char *current_file_name
, *current_link_name
;
327 extfs_open_archive (fstype
, name
, ¤t_archive
)) == NULL
) {
328 message (D_ERROR
, MSG_ERROR
, _("Cannot open %s archive\n%s"),
329 extfs_prefixes
[fstype
], name
);
333 buffer
= g_malloc (4096);
334 while (fgets (buffer
, 4096, extfsd
) != NULL
) {
337 current_link_name
= NULL
;
339 (buffer
, &hstat
, ¤t_file_name
, ¤t_link_name
)) {
340 struct entry
*entry
, *pent
;
342 char *p
, *q
, *cfn
= current_file_name
;
348 if (p
!= cfn
&& *(p
- 1) == '/')
350 p
= strrchr (cfn
, '/');
358 if (S_ISDIR (hstat
.st_mode
)
359 && (!strcmp (p
, ".") || !strcmp (p
, "..")))
360 goto read_extfs_continue
;
362 extfs_find_entry (current_archive
->root_entry
, q
, 1,
365 /* FIXME: Should clean everything one day */
368 close_error_pipe (D_ERROR
, _("Inconsistent extfs archive"));
371 entry
= g_new (struct entry
, 1);
372 entry
->name
= g_strdup (p
);
373 entry
->next_in_dir
= NULL
;
375 if (pent
->inode
->last_in_subdir
) {
376 pent
->inode
->last_in_subdir
->next_in_dir
= entry
;
377 pent
->inode
->last_in_subdir
= entry
;
379 if (!S_ISLNK (hstat
.st_mode
) && current_link_name
!= NULL
) {
381 extfs_find_entry (current_archive
->root_entry
,
382 current_link_name
, 0, 0);
384 /* FIXME: Should clean everything one day */
387 close_error_pipe (D_ERROR
,
388 _("Inconsistent extfs archive"));
391 entry
->inode
= pent
->inode
;
392 pent
->inode
->nlink
++;
395 inode
= g_new (struct inode
, 1);
396 entry
->inode
= inode
;
397 inode
->local_filename
= NULL
;
398 inode
->inode
= (current_archive
->inode_counter
)++;
400 inode
->dev
= current_archive
->rdev
;
401 inode
->archive
= current_archive
;
402 inode
->mode
= hstat
.st_mode
;
403 #ifdef HAVE_STRUCT_STAT_ST_RDEV
404 inode
->rdev
= hstat
.st_rdev
;
408 inode
->uid
= hstat
.st_uid
;
409 inode
->gid
= hstat
.st_gid
;
410 inode
->size
= hstat
.st_size
;
411 inode
->mtime
= hstat
.st_mtime
;
412 inode
->atime
= hstat
.st_atime
;
413 inode
->ctime
= hstat
.st_ctime
;
414 inode
->first_in_subdir
= NULL
;
415 inode
->last_in_subdir
= NULL
;
416 if (current_link_name
!= NULL
417 && S_ISLNK (hstat
.st_mode
)) {
418 inode
->linkname
= current_link_name
;
419 current_link_name
= NULL
;
421 if (S_ISLNK (hstat
.st_mode
))
422 inode
->mode
&= ~S_IFLNK
; /* You *DON'T* want to do this always */
423 inode
->linkname
= NULL
;
425 if (S_ISDIR (hstat
.st_mode
))
426 extfs_make_dots (entry
);
430 g_free (current_file_name
);
431 g_free (current_link_name
);
436 /* Check if extfs 'list' returned 0 */
437 if (pclose (extfsd
) != 0) {
438 extfs_free (current_archive
);
439 close_error_pipe (D_ERROR
, _("Inconsistent extfs archive"));
443 close_error_pipe (D_ERROR
, NULL
);
444 *pparc
= current_archive
;
449 * Dissect the path and create corresponding superblock. Note that inname
450 * can be changed and the result may point inside the original string.
453 extfs_get_path_mangle (struct vfs_class
*me
, char *inname
, struct archive
**archive
,
457 const char *archive_name
;
459 struct archive
*parc
;
462 archive_name
= inname
;
463 vfs_split (inname
, &local
, &op
);
465 fstype
= extfs_which (me
, op
);
470 local
= inname
+ strlen (inname
);
473 * All filesystems should have some local archive, at least
476 for (parc
= first_archive
; parc
!= NULL
; parc
= parc
->next
)
478 if (!strcmp (parc
->name
, archive_name
)) {
479 vfs_stamp (&vfs_extfs_ops
, (vfsid
) parc
);
485 do_not_open
? -1 : extfs_read_archive (fstype
, archive_name
,
497 * Dissect the path and create corresponding superblock.
498 * The result should be freed.
501 extfs_get_path (struct vfs_class
*me
, const char *inname
, struct archive
**archive
,
504 char *buf
= g_strdup (inname
);
505 char *res
= extfs_get_path_mangle (me
, buf
, archive
, do_not_open
);
508 res2
= g_strdup (res
);
514 /* Return allocated path (without leading slash) inside the archive */
515 static char *extfs_get_path_from_entry (struct entry
*entry
)
524 for (len
= 0, head
= 0; entry
->dir
; entry
= entry
->dir
) {
525 p
= g_new (struct list
, 1);
527 p
->name
= entry
->name
;
529 len
+= strlen (entry
->name
) + 1;
533 return g_strdup ("");
535 localpath
= g_malloc (len
);
538 strcat (localpath
, head
->name
);
540 strcat (localpath
, "/");
549 struct loop_protect
{
551 struct loop_protect
*next
;
556 static struct entry
*
557 extfs_find_entry_int (struct entry
*dir
, char *name
,
558 struct loop_protect
*list
, int make_dirs
, int make_file
);
560 static struct entry
*
561 extfs_resolve_symlinks_int (struct entry
*entry
,
562 struct loop_protect
*list
)
565 struct loop_protect
*looping
;
567 if (!S_ISLNK (entry
->inode
->mode
))
569 for (looping
= list
; looping
!= NULL
; looping
= looping
->next
)
570 if (entry
== looping
->entry
) { /* Here we protect us against symlink looping */
574 looping
= g_new (struct loop_protect
, 1);
575 looping
->entry
= entry
;
576 looping
->next
= list
;
577 pent
= extfs_find_entry_int (entry
->dir
, entry
->inode
->linkname
, looping
, 0, 0);
584 static struct entry
*extfs_resolve_symlinks (struct entry
*entry
)
590 res
= extfs_resolve_symlinks_int (entry
, NULL
);
601 extfs_get_archive_name (struct archive
*archive
)
603 const char *archive_name
;
605 if (archive
->local_name
)
606 archive_name
= archive
->local_name
;
608 archive_name
= archive
->name
;
610 if (!archive_name
|| !*archive_name
)
611 return "no_archive_name";
616 /* Don't pass localname as NULL */
618 extfs_cmd (const char *str_extfs_cmd
, struct archive
*archive
,
619 struct entry
*entry
, const char *localname
)
623 char *quoted_localname
;
629 file
= extfs_get_path_from_entry (entry
);
630 quoted_file
= name_quote (file
, 0);
632 archive_name
= name_quote (extfs_get_archive_name (archive
), 0);
633 quoted_localname
= name_quote (localname
, 0);
635 mc_extfsdir
= concat_dir_and_file (mc_home_alt
, "extfs" PATH_SEP_STR
);
636 cmd
= g_strconcat (mc_extfsdir
, extfs_prefixes
[archive
->fstype
],
637 str_extfs_cmd
, archive_name
, " ", quoted_file
, " ",
638 quoted_localname
, (char *) NULL
);
639 g_free (quoted_file
);
640 g_free (quoted_localname
);
641 g_free (mc_extfsdir
);
642 g_free (archive_name
);
645 retval
= my_system (EXECUTE_AS_SHELL
, shell
, cmd
);
647 close_error_pipe (D_ERROR
, NULL
);
652 extfs_run (struct vfs_class
*me
, const char *file
)
654 struct archive
*archive
= NULL
;
655 char *p
, *q
, *archive_name
, *mc_extfsdir
;
658 if ((p
= extfs_get_path (me
, file
, &archive
, 0)) == NULL
)
660 q
= name_quote (p
, 0);
663 archive_name
= name_quote (extfs_get_archive_name (archive
), 0);
664 mc_extfsdir
= concat_dir_and_file (mc_home_alt
, "extfs" PATH_SEP_STR
);
665 cmd
= g_strconcat (mc_extfsdir
, extfs_prefixes
[archive
->fstype
],
666 " run ", archive_name
, " ", q
, (char *) NULL
);
667 g_free (mc_extfsdir
);
668 g_free (archive_name
);
670 shell_execute (cmd
, 0);
675 extfs_open (struct vfs_class
*me
, const char *file
, int flags
, int mode
)
677 struct pseudofile
*extfs_info
;
678 struct archive
*archive
= NULL
;
684 if ((q
= extfs_get_path (me
, file
, &archive
, 0)) == NULL
)
686 entry
= extfs_find_entry (archive
->root_entry
, q
, 0, 0);
687 if (entry
== NULL
&& (flags
& O_CREAT
)) {
688 /* Create new entry */
689 entry
= extfs_find_entry (archive
->root_entry
, q
, 0, 1);
690 created
= (entry
!= NULL
);
696 if ((entry
= extfs_resolve_symlinks (entry
)) == NULL
)
699 if (S_ISDIR (entry
->inode
->mode
))
700 ERRNOR (EISDIR
, NULL
);
702 if (entry
->inode
->local_filename
== NULL
) {
703 char *local_filename
;
705 local_handle
= vfs_mkstemps (&local_filename
, "extfs", entry
->name
);
707 if (local_handle
== -1)
709 close (local_handle
);
711 if (!created
&& !(flags
& O_TRUNC
)
712 && extfs_cmd (" copyout ", archive
, entry
, local_filename
)) {
713 unlink (local_filename
);
714 g_free (local_filename
);
718 entry
->inode
->local_filename
= local_filename
;
722 open (entry
->inode
->local_filename
, NO_LINEAR (flags
), mode
);
724 if (local_handle
== -1) {
725 /* file exists(may be). Need to drop O_CREAT flag and truncate file content */
726 flags
= ~O_CREAT
& (NO_LINEAR (flags
)|O_TRUNC
);
727 local_handle
= open (entry
->inode
->local_filename
, flags
, mode
);
730 if (local_handle
== -1)
733 extfs_info
= g_new (struct pseudofile
, 1);
734 extfs_info
->archive
= archive
;
735 extfs_info
->entry
= entry
;
736 extfs_info
->has_changed
= created
;
737 extfs_info
->local_handle
= local_handle
;
739 /* i.e. we had no open files and now we have one */
740 vfs_rmstamp (&vfs_extfs_ops
, (vfsid
) archive
);
745 static ssize_t
extfs_read (void *data
, char *buffer
, int count
)
747 struct pseudofile
*file
= (struct pseudofile
*)data
;
749 return read (file
->local_handle
, buffer
, count
);
753 extfs_close (void *data
)
755 struct pseudofile
*file
;
757 file
= (struct pseudofile
*) data
;
759 close (file
->local_handle
);
761 /* Commit the file if it has changed */
762 if (file
->has_changed
) {
764 (" copyin ", file
->archive
, file
->entry
,
765 file
->entry
->inode
->local_filename
))
768 struct stat file_status
;
769 if (stat (file
->entry
->inode
->local_filename
, &file_status
) !=
773 file
->entry
->inode
->size
= file_status
.st_size
;
776 file
->entry
->inode
->mtime
= time (NULL
);
779 file
->archive
->fd_usage
--;
780 if (!file
->archive
->fd_usage
)
781 vfs_stamp_create (&vfs_extfs_ops
, file
->archive
);
789 #define RECORDSIZE 512
792 extfs_find_entry_int (struct entry
*dir
, char *name
,
793 struct loop_protect
*list
, int make_dirs
, int make_file
)
795 struct entry
*pent
, *pdir
;
796 char *p
, *q
, *name_end
;
799 if (*name
== '/') { /* Handle absolute paths */
801 dir
= dir
->inode
->archive
->root_entry
;
806 name_end
= name
+ strlen (name
);
812 for (; pent
!= NULL
&& c
&& *p
; ){
816 if (strcmp (p
, ".")){
817 if (!strcmp (p
, ".."))
820 if ((pent
= extfs_resolve_symlinks_int (pent
, list
))==NULL
){
824 if (!S_ISDIR (pent
->inode
->mode
)){
830 for (pent
= pent
->inode
->first_in_subdir
; pent
; pent
= pent
->next_in_dir
)
831 /* Hack: I keep the original semanthic unless
832 q+1 would break in the strchr */
833 if (!strcmp (pent
->name
, p
)){
834 if (q
+ 1 > name_end
){
836 notadir
= !S_ISDIR (pent
->inode
->mode
);
842 /* When we load archive, we create automagically
843 * non-existant directories
845 if (pent
== NULL
&& make_dirs
) {
846 pent
= extfs_generate_entry (dir
->inode
->archive
, p
, pdir
, S_IFDIR
| 0777);
848 if (pent
== NULL
&& make_file
) {
849 pent
= extfs_generate_entry (dir
->inode
->archive
, p
, pdir
, S_IFREG
| 0666);
865 static struct entry
*extfs_find_entry (struct entry
*dir
, char *name
, int make_dirs
, int make_file
)
871 res
= extfs_find_entry_int (dir
, name
, NULL
, make_dirs
, make_file
);
882 static int extfs_errno (struct vfs_class
*me
)
889 static void * extfs_opendir (struct vfs_class
*me
, const char *dirname
)
891 struct archive
*archive
= NULL
;
896 if ((q
= extfs_get_path (me
, dirname
, &archive
, 0)) == NULL
)
898 entry
= extfs_find_entry (archive
->root_entry
, q
, 0, 0);
902 if ((entry
= extfs_resolve_symlinks (entry
)) == NULL
)
904 if (!S_ISDIR (entry
->inode
->mode
)) ERRNOR (ENOTDIR
, NULL
);
906 info
= g_new (struct entry
*, 2);
907 info
[0] = entry
->inode
->first_in_subdir
;
908 info
[1] = entry
->inode
->first_in_subdir
;
913 static void * extfs_readdir(void *data
)
915 static union vfs_dirent dir
;
916 struct entry
**info
= (struct entry
**) data
;
921 g_strlcpy(dir
.dent
.d_name
, (*info
)->name
, MC_MAXPATHLEN
);
923 compute_namelen(&dir
.dent
);
924 *info
= (*info
)->next_in_dir
;
926 return (void *) &dir
;
929 static int extfs_closedir (void *data
)
935 static void extfs_stat_move (struct stat
*buf
, const struct inode
*inode
)
937 buf
->st_dev
= inode
->dev
;
938 buf
->st_ino
= inode
->inode
;
939 buf
->st_mode
= inode
->mode
;
940 buf
->st_nlink
= inode
->nlink
;
941 buf
->st_uid
= inode
->uid
;
942 buf
->st_gid
= inode
->gid
;
943 #ifdef HAVE_STRUCT_STAT_ST_RDEV
944 buf
->st_rdev
= inode
->rdev
;
946 buf
->st_size
= inode
->size
;
947 #ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
948 buf
->st_blksize
= RECORDSIZE
;
950 #ifdef HAVE_STRUCT_STAT_ST_BLOCKS
951 buf
->st_blocks
= (inode
->size
+ RECORDSIZE
- 1) / RECORDSIZE
;
953 buf
->st_atime
= inode
->atime
;
954 buf
->st_mtime
= inode
->mtime
;
955 buf
->st_ctime
= inode
->ctime
;
959 extfs_internal_stat (struct vfs_class
*me
, const char *path
, struct stat
*buf
,
962 struct archive
*archive
;
965 char *path2
= g_strdup (path
);
968 if ((q
= extfs_get_path_mangle (me
, path2
, &archive
, 0)) == NULL
)
970 entry
= extfs_find_entry (archive
->root_entry
, q
, 0, 0);
973 if (resolve
&& (entry
= extfs_resolve_symlinks (entry
)) == NULL
)
975 extfs_stat_move (buf
, entry
->inode
);
982 static int extfs_stat (struct vfs_class
*me
, const char *path
, struct stat
*buf
)
984 return extfs_internal_stat (me
, path
, buf
, 1);
987 static int extfs_lstat (struct vfs_class
*me
, const char *path
, struct stat
*buf
)
989 return extfs_internal_stat (me
, path
, buf
, 0);
992 static int extfs_fstat (void *data
, struct stat
*buf
)
994 struct pseudofile
*file
= (struct pseudofile
*)data
;
996 extfs_stat_move (buf
, file
->entry
->inode
);
1001 extfs_readlink (struct vfs_class
*me
, const char *path
, char *buf
, size_t size
)
1003 struct archive
*archive
;
1006 struct entry
*entry
;
1007 char *mpath
= g_strdup (path
);
1010 if ((q
= extfs_get_path_mangle (me
, mpath
, &archive
, 0)) == NULL
)
1012 entry
= extfs_find_entry (archive
->root_entry
, q
, 0, 0);
1015 if (!S_ISLNK (entry
->inode
->mode
)) {
1016 me
->verrno
= EINVAL
;
1019 len
= strlen (entry
->inode
->linkname
);
1022 /* readlink() does not append a NUL character to buf */
1023 memcpy (buf
, entry
->inode
->linkname
, result
= len
);
1029 static int extfs_chown (struct vfs_class
*me
, const char *path
, int owner
, int group
)
1038 static int extfs_chmod (struct vfs_class
*me
, const char *path
, int mode
)
1046 static ssize_t
extfs_write (void *data
, const char *buf
, int nbyte
)
1048 struct pseudofile
*file
= (struct pseudofile
*)data
;
1050 file
->has_changed
= 1;
1051 return write (file
->local_handle
, buf
, nbyte
);
1054 static int extfs_unlink (struct vfs_class
*me
, const char *file
)
1056 struct archive
*archive
;
1057 char *q
, *mpath
= g_strdup (file
);
1058 struct entry
*entry
;
1061 if ((q
= extfs_get_path_mangle (me
, mpath
, &archive
, 0)) == NULL
)
1063 entry
= extfs_find_entry (archive
->root_entry
, q
, 0, 0);
1066 if ((entry
= extfs_resolve_symlinks (entry
)) == NULL
)
1068 if (S_ISDIR (entry
->inode
->mode
)) {
1069 me
->verrno
= EISDIR
;
1072 if (extfs_cmd (" rm ", archive
, entry
, "")){
1076 extfs_remove_entry (entry
);
1083 static int extfs_mkdir (struct vfs_class
*me
, const char *path
, mode_t mode
)
1085 struct archive
*archive
;
1086 char *q
, *mpath
= g_strdup(path
);
1087 struct entry
*entry
;
1092 if ((q
= extfs_get_path_mangle (me
, mpath
, &archive
, 0)) == NULL
)
1094 entry
= extfs_find_entry (archive
->root_entry
, q
, 0, 0);
1095 if (entry
!= NULL
) {
1096 me
->verrno
= EEXIST
;
1099 entry
= extfs_find_entry (archive
->root_entry
, q
, 1, 0);
1102 if ((entry
= extfs_resolve_symlinks (entry
)) == NULL
)
1104 if (!S_ISDIR (entry
->inode
->mode
)) {
1105 me
->verrno
= ENOTDIR
;
1109 if (extfs_cmd (" mkdir ", archive
, entry
, "")){
1111 extfs_remove_entry (entry
);
1120 static int extfs_rmdir (struct vfs_class
*me
, const char *path
)
1122 struct archive
*archive
;
1123 char *q
, *mpath
= g_strdup(path
);
1124 struct entry
*entry
;
1127 if ((q
= extfs_get_path_mangle (me
, mpath
, &archive
, 0)) == NULL
)
1129 entry
= extfs_find_entry (archive
->root_entry
, q
, 0, 0);
1132 if ((entry
= extfs_resolve_symlinks (entry
)) == NULL
)
1134 if (!S_ISDIR (entry
->inode
->mode
)) {
1135 me
->verrno
= ENOTDIR
;
1139 if (extfs_cmd (" rmdir ", archive
, entry
, "")){
1143 extfs_remove_entry (entry
);
1151 extfs_chdir (struct vfs_class
*me
, const char *path
)
1153 struct archive
*archive
= NULL
;
1155 struct entry
*entry
;
1158 if ((q
= extfs_get_path (me
, path
, &archive
, 0)) == NULL
)
1160 entry
= extfs_find_entry (archive
->root_entry
, q
, 0, 0);
1164 entry
= extfs_resolve_symlinks (entry
);
1165 if ((!entry
) || (!S_ISDIR (entry
->inode
->mode
)))
1171 static off_t
extfs_lseek (void *data
, off_t offset
, int whence
)
1173 struct pseudofile
*file
= (struct pseudofile
*) data
;
1175 return lseek (file
->local_handle
, offset
, whence
);
1179 extfs_getid (struct vfs_class
*me
, const char *path
)
1181 struct archive
*archive
= NULL
;
1184 if (!(p
= extfs_get_path (me
, path
, &archive
, 1)))
1187 return (vfsid
) archive
;
1190 static int extfs_nothingisopen (vfsid id
)
1192 if (((struct archive
*)id
)->fd_usage
<= 0)
1197 static void extfs_remove_entry (struct entry
*e
)
1199 int i
= --(e
->inode
->nlink
);
1200 struct entry
*pe
, *ent
, *prev
;
1202 if (S_ISDIR (e
->inode
->mode
) && e
->inode
->first_in_subdir
!= NULL
) {
1203 struct entry
*f
= e
->inode
->first_in_subdir
;
1204 e
->inode
->first_in_subdir
= NULL
;
1205 extfs_remove_entry (f
);
1208 if (e
== pe
->inode
->first_in_subdir
)
1209 pe
->inode
->first_in_subdir
= e
->next_in_dir
;
1212 for (ent
= pe
->inode
->first_in_subdir
; ent
&& ent
->next_in_dir
;
1213 ent
= ent
->next_in_dir
)
1214 if (e
== ent
->next_in_dir
) {
1219 prev
->next_in_dir
= e
->next_in_dir
;
1220 if (e
== pe
->inode
->last_in_subdir
)
1221 pe
->inode
->last_in_subdir
= prev
;
1224 if (e
->inode
->local_filename
!= NULL
) {
1225 unlink (e
->inode
->local_filename
);
1226 g_free (e
->inode
->local_filename
);
1228 g_free (e
->inode
->linkname
);
1236 static void extfs_free_entry (struct entry
*e
)
1238 int i
= --(e
->inode
->nlink
);
1239 if (S_ISDIR (e
->inode
->mode
) && e
->inode
->first_in_subdir
!= NULL
) {
1240 struct entry
*f
= e
->inode
->first_in_subdir
;
1242 e
->inode
->first_in_subdir
= NULL
;
1243 extfs_free_entry (f
);
1246 if (e
->inode
->local_filename
!= NULL
) {
1247 unlink (e
->inode
->local_filename
);
1248 g_free (e
->inode
->local_filename
);
1250 g_free (e
->inode
->linkname
);
1253 if (e
->next_in_dir
!= NULL
)
1254 extfs_free_entry (e
->next_in_dir
);
1259 static void extfs_free (vfsid id
)
1261 struct archive
*parc
;
1262 struct archive
*archive
= (struct archive
*)id
;
1264 if (archive
== first_archive
) {
1265 first_archive
= archive
->next
;
1267 for (parc
= first_archive
; parc
!= NULL
; parc
= parc
->next
)
1268 if (parc
->next
== archive
) {
1269 parc
->next
= archive
->next
;
1273 extfs_free_archive (archive
);
1277 extfs_getlocalcopy (struct vfs_class
*me
, const char *path
)
1279 struct pseudofile
*fp
=
1280 (struct pseudofile
*) extfs_open (me
, path
, O_RDONLY
, 0);
1285 if (fp
->entry
->inode
->local_filename
== NULL
) {
1286 extfs_close ((void *) fp
);
1289 p
= g_strdup (fp
->entry
->inode
->local_filename
);
1290 fp
->archive
->fd_usage
++;
1291 extfs_close ((void *) fp
);
1296 extfs_ungetlocalcopy (struct vfs_class
*me
, const char *path
,
1297 const char *local
, int has_changed
)
1299 struct pseudofile
*fp
=
1300 (struct pseudofile
*) extfs_open (me
, path
, O_RDONLY
, 0);
1304 if (!strcmp (fp
->entry
->inode
->local_filename
, local
)) {
1305 fp
->archive
->fd_usage
--;
1306 fp
->has_changed
|= has_changed
;
1307 extfs_close ((void *) fp
);
1310 /* Should not happen */
1311 extfs_close ((void *) fp
);
1317 static int extfs_init (struct vfs_class
*me
)
1325 mc_extfsini
= concat_dir_and_file (mc_home
, "extfs" PATH_SEP_STR
"extfs.ini");
1326 cfg
= fopen (mc_extfsini
, "r");
1328 /* We may not use vfs_die() message or message or similar,
1329 * UI is not initialized at this time and message would not
1330 * appear on screen. */
1332 fprintf (stderr
, _("Warning: file %s not found\n"), mc_extfsini
);
1333 g_free (mc_extfsini
);
1338 while (extfs_no
< MAXEXTFS
&& fgets (key
, sizeof (key
), cfg
)) {
1341 /* Handle those with a trailing ':', those flag that the
1342 * file system does not require an archive to work
1346 fprintf(stderr
, "Warning: You need to update your %s file.\n",
1349 g_free (mc_extfsini
);
1352 if (*key
== '#' || *key
== '\n')
1355 if ((c
= strchr (key
, '\n'))){
1357 } else { /* Last line without newline or strlen (key) > 255 */
1358 c
= &key
[strlen (key
) - 1];
1360 extfs_need_archive
[extfs_no
] = !(*c
== ':');
1366 extfs_prefixes
[extfs_no
++] = g_strdup (key
);
1369 g_free (mc_extfsini
);
1373 static int extfs_which (struct vfs_class
*me
, const char *path
)
1379 for (i
= 0; i
< extfs_no
; i
++)
1380 if (!strcmp (path
, extfs_prefixes
[i
]))
1385 static void extfs_done (struct vfs_class
*me
)
1392 for (ar
= first_archive
; ar
!= NULL
;) {
1393 extfs_free ((vfsid
) ar
);
1397 for (i
= 0; i
< extfs_no
; i
++ )
1398 g_free (extfs_prefixes
[i
]);
1403 extfs_setctl (struct vfs_class
*me
, const char *path
, int ctlop
, void *arg
)
1407 if (ctlop
== VFS_SETCTL_RUN
) {
1408 extfs_run (me
, path
);
1417 vfs_extfs_ops
.name
= "extfs";
1418 vfs_extfs_ops
.init
= extfs_init
;
1419 vfs_extfs_ops
.done
= extfs_done
;
1420 vfs_extfs_ops
.fill_names
= extfs_fill_names
;
1421 vfs_extfs_ops
.which
= extfs_which
;
1422 vfs_extfs_ops
.open
= extfs_open
;
1423 vfs_extfs_ops
.close
= extfs_close
;
1424 vfs_extfs_ops
.read
= extfs_read
;
1425 vfs_extfs_ops
.write
= extfs_write
;
1426 vfs_extfs_ops
.opendir
= extfs_opendir
;
1427 vfs_extfs_ops
.readdir
= extfs_readdir
;
1428 vfs_extfs_ops
.closedir
= extfs_closedir
;
1429 vfs_extfs_ops
.stat
= extfs_stat
;
1430 vfs_extfs_ops
.lstat
= extfs_lstat
;
1431 vfs_extfs_ops
.fstat
= extfs_fstat
;
1432 vfs_extfs_ops
.chmod
= extfs_chmod
;
1433 vfs_extfs_ops
.chown
= extfs_chown
;
1434 vfs_extfs_ops
.readlink
= extfs_readlink
;
1435 vfs_extfs_ops
.unlink
= extfs_unlink
;
1436 vfs_extfs_ops
.chdir
= extfs_chdir
;
1437 vfs_extfs_ops
.ferrno
= extfs_errno
;
1438 vfs_extfs_ops
.lseek
= extfs_lseek
;
1439 vfs_extfs_ops
.getid
= extfs_getid
;
1440 vfs_extfs_ops
.nothingisopen
= extfs_nothingisopen
;
1441 vfs_extfs_ops
.free
= extfs_free
;
1442 vfs_extfs_ops
.getlocalcopy
= extfs_getlocalcopy
;
1443 vfs_extfs_ops
.ungetlocalcopy
= extfs_ungetlocalcopy
;
1444 vfs_extfs_ops
.mkdir
= extfs_mkdir
;
1445 vfs_extfs_ops
.rmdir
= extfs_rmdir
;
1446 vfs_extfs_ops
.setctl
= extfs_setctl
;
1447 vfs_register_class (&vfs_extfs_ops
);