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>
49 #include "lib/global.h"
50 #include "lib/fileloc.h"
52 #include "src/wtools.h" /* message() */
53 #include "src/main.h" /* print_vfs_message */
54 #include "src/execute.h" /* For shell_execute */
59 #include "gc.h" /* vfs_rmstamp */
62 #define ERRNOR(x,y) do { my_errno = x; return y; } while(0)
67 struct entry
*first_in_subdir
; /* only used if this is a directory */
68 struct entry
*last_in_subdir
;
69 ino_t inode
; /* This is inode # */
70 dev_t dev
; /* This is an internal identification of the extfs archive */
71 struct archive
*archive
; /* And this is an archive structure */
86 struct entry
*next_in_dir
;
94 struct archive
*archive
;
105 struct stat local_stat
;
109 struct entry
*root_entry
;
110 struct archive
*next
;
117 gboolean need_archive
;
118 } extfs_plugin_info_t
;
120 static gboolean errloop
;
121 static gboolean notadir
;
123 static void extfs_remove_entry (struct entry
*e
);
124 static void extfs_free (vfsid id
);
125 static void extfs_free_entry (struct entry
*e
);
126 static struct entry
*extfs_resolve_symlinks_int (struct entry
*entry
, GSList
* list
);
128 static struct vfs_class vfs_extfs_ops
;
129 static struct archive
*first_archive
= NULL
;
130 static int my_errno
= 0;
132 GArray
*extfs_plugins
= NULL
;
135 extfs_make_dots (struct entry
*ent
)
137 struct entry
*entry
= g_new (struct entry
, 1);
138 struct entry
*parentry
= ent
->dir
;
139 struct inode
*inode
= ent
->inode
, *parent
;
141 parent
= (parentry
!= NULL
) ? parentry
->inode
: NULL
;
142 entry
->name
= g_strdup (".");
143 entry
->inode
= inode
;
145 inode
->local_filename
= NULL
;
146 inode
->first_in_subdir
= entry
;
149 entry
->next_in_dir
= g_new (struct entry
, 1);
150 entry
= entry
->next_in_dir
;
151 entry
->name
= g_strdup ("..");
152 inode
->last_in_subdir
= entry
;
153 entry
->next_in_dir
= NULL
;
156 entry
->inode
= parent
;
157 entry
->dir
= parentry
;
162 entry
->inode
= inode
;
168 static struct entry
*
169 extfs_generate_entry (struct archive
*archive
,
170 const char *name
, struct entry
*parentry
, mode_t mode
)
173 struct inode
*inode
, *parent
;
176 parent
= (parentry
!= NULL
) ? parentry
->inode
: NULL
;
177 entry
= g_new (struct entry
, 1);
179 entry
->name
= g_strdup (name
);
180 entry
->next_in_dir
= NULL
;
181 entry
->dir
= parentry
;
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
);
212 static struct entry
*
213 extfs_find_entry_int (struct entry
*dir
, char *name
, GSList
* list
,
214 gboolean make_dirs
, gboolean make_file
)
216 struct entry
*pent
, *pdir
;
217 char *p
, *q
, *name_end
;
220 if (g_path_is_absolute (name
))
222 /* Handle absolute paths */
223 name
= (char *) g_path_skip_root (name
);
224 dir
= dir
->inode
->archive
->root_entry
;
229 name_end
= name
+ strlen (name
);
231 q
= strchr (p
, PATH_SEP
);
233 q
= strchr (p
, '\0');
235 while ((pent
!= NULL
) && (c
!= '\0') && (*p
!= '\0'))
240 if (strcmp (p
, ".") != 0)
242 if (strcmp (p
, "..") == 0)
246 pent
= extfs_resolve_symlinks_int (pent
, list
);
252 if (!S_ISDIR (pent
->inode
->mode
))
260 for (pent
= pent
->inode
->first_in_subdir
; pent
!= NULL
; pent
= pent
->next_in_dir
)
261 /* Hack: I keep the original semanthic unless
262 q+1 would break in the strchr */
263 if (strcmp (pent
->name
, p
) == 0)
265 if (q
+ 1 > name_end
)
268 notadir
= !S_ISDIR (pent
->inode
->mode
);
274 /* When we load archive, we create automagically
275 * non-existant directories
277 if (pent
== NULL
&& make_dirs
)
278 pent
= extfs_generate_entry (dir
->inode
->archive
, p
, pdir
, S_IFDIR
| 0777);
279 if (pent
== NULL
&& make_file
)
280 pent
= extfs_generate_entry (dir
->inode
->archive
, p
, pdir
, S_IFREG
| 0666);
286 q
= strchr (p
, PATH_SEP
);
288 q
= strchr (p
, '\0');
295 static struct entry
*
296 extfs_find_entry (struct entry
*dir
, char *name
, gboolean make_dirs
, gboolean make_file
)
303 res
= extfs_find_entry_int (dir
, name
, NULL
, make_dirs
, make_file
);
315 extfs_fill_names (struct vfs_class
*me
, fill_names_f func
)
317 struct archive
*a
= first_archive
;
323 extfs_plugin_info_t
*info
;
326 info
= &g_array_index (extfs_plugins
, extfs_plugin_info_t
, a
->fstype
);
327 name
= g_strconcat (a
->name
? a
->name
: "", "#", info
->prefix
, (char *) NULL
);
335 extfs_free_archive (struct archive
*archive
)
337 extfs_free_entry (archive
->root_entry
);
338 if (archive
->local_name
!= NULL
)
342 mc_stat (archive
->local_name
, &my
);
343 mc_ungetlocalcopy (archive
->name
, archive
->local_name
,
344 archive
->local_stat
.st_mtime
!= my
.st_mtime
);
345 g_free (archive
->local_name
);
347 g_free (archive
->name
);
352 extfs_open_archive (int fstype
, const char *name
, struct archive
**pparc
)
354 const extfs_plugin_info_t
*info
;
355 static dev_t archive_counter
= 0;
360 struct archive
*current_archive
;
361 struct entry
*root_entry
;
362 char *local_name
= NULL
, *tmp
= NULL
;
364 info
= &g_array_index (extfs_plugins
, extfs_plugin_info_t
, fstype
);
366 if (info
->need_archive
)
368 if (mc_stat (name
, &mystat
) == -1)
371 if (!vfs_file_is_local (name
))
373 local_name
= mc_getlocalcopy (name
);
374 if (local_name
== NULL
)
378 tmp
= name_quote (name
, 0);
381 cmd
= g_strconcat (info
->path
, info
->prefix
, " list ",
382 local_name
!= NULL
? local_name
: tmp
, (char *) NULL
);
386 result
= popen (cmd
, "r");
390 close_error_pipe (D_ERROR
, NULL
);
391 if (local_name
!= NULL
)
393 mc_ungetlocalcopy (name
, local_name
, 0);
400 setvbuf (result
, NULL
, _IONBF
, 0);
403 current_archive
= g_new (struct archive
, 1);
404 current_archive
->fstype
= fstype
;
405 current_archive
->name
= name
? g_strdup (name
) : NULL
;
406 current_archive
->local_name
= local_name
;
408 if (local_name
!= NULL
)
409 mc_stat (local_name
, ¤t_archive
->local_stat
);
410 current_archive
->inode_counter
= 0;
411 current_archive
->fd_usage
= 0;
412 current_archive
->rdev
= archive_counter
++;
413 current_archive
->next
= first_archive
;
414 first_archive
= current_archive
;
415 mode
= mystat
.st_mode
& 07777;
423 root_entry
= extfs_generate_entry (current_archive
, PATH_SEP_STR
, NULL
, mode
);
424 root_entry
->inode
->uid
= mystat
.st_uid
;
425 root_entry
->inode
->gid
= mystat
.st_gid
;
426 root_entry
->inode
->atime
= mystat
.st_atime
;
427 root_entry
->inode
->ctime
= mystat
.st_ctime
;
428 root_entry
->inode
->mtime
= mystat
.st_mtime
;
429 current_archive
->root_entry
= root_entry
;
431 *pparc
= current_archive
;
437 * Main loop for reading an archive.
438 * Return 0 on success, -1 on error.
441 extfs_read_archive (int fstype
, const char *name
, struct archive
**pparc
)
444 const extfs_plugin_info_t
*info
;
446 struct archive
*current_archive
;
447 char *current_file_name
, *current_link_name
;
449 info
= &g_array_index (extfs_plugins
, extfs_plugin_info_t
, fstype
);
451 extfsd
= extfs_open_archive (fstype
, name
, ¤t_archive
);
455 message (D_ERROR
, MSG_ERROR
, _("Cannot open %s archive\n%s"), info
->prefix
, name
);
459 buffer
= g_malloc (BUF_4K
);
460 while (fgets (buffer
, BUF_4K
, extfsd
) != NULL
)
464 current_link_name
= NULL
;
465 if (vfs_parse_ls_lga (buffer
, &hstat
, ¤t_file_name
, ¤t_link_name
))
467 struct entry
*entry
, *pent
;
469 char *p
, *q
, *cfn
= current_file_name
;
473 if (*cfn
== PATH_SEP
)
475 p
= strchr (cfn
, '\0');
476 if (p
!= cfn
&& *(p
- 1) == PATH_SEP
)
478 p
= strrchr (cfn
, PATH_SEP
);
482 q
= strchr (cfn
, '\0');
489 if (S_ISDIR (hstat
.st_mode
) && (strcmp (p
, ".") == 0 || strcmp (p
, "..") == 0))
490 goto read_extfs_continue
;
491 pent
= extfs_find_entry (current_archive
->root_entry
, q
, TRUE
, FALSE
);
494 /* FIXME: Should clean everything one day */
497 close_error_pipe (D_ERROR
, _("Inconsistent extfs archive"));
500 entry
= g_new (struct entry
, 1);
501 entry
->name
= g_strdup (p
);
502 entry
->next_in_dir
= NULL
;
504 if (pent
->inode
->last_in_subdir
)
506 pent
->inode
->last_in_subdir
->next_in_dir
= entry
;
507 pent
->inode
->last_in_subdir
= entry
;
509 if (!S_ISLNK (hstat
.st_mode
) && (current_link_name
!= NULL
))
511 pent
= extfs_find_entry (current_archive
->root_entry
,
512 current_link_name
, FALSE
, FALSE
);
515 /* FIXME: Should clean everything one day */
518 close_error_pipe (D_ERROR
, _("Inconsistent extfs archive"));
522 entry
->inode
= pent
->inode
;
523 pent
->inode
->nlink
++;
527 inode
= g_new (struct inode
, 1);
528 entry
->inode
= inode
;
529 inode
->local_filename
= NULL
;
530 inode
->inode
= (current_archive
->inode_counter
)++;
532 inode
->dev
= current_archive
->rdev
;
533 inode
->archive
= current_archive
;
534 inode
->mode
= hstat
.st_mode
;
535 #ifdef HAVE_STRUCT_STAT_ST_RDEV
536 inode
->rdev
= hstat
.st_rdev
;
540 inode
->uid
= hstat
.st_uid
;
541 inode
->gid
= hstat
.st_gid
;
542 inode
->size
= hstat
.st_size
;
543 inode
->mtime
= hstat
.st_mtime
;
544 inode
->atime
= hstat
.st_atime
;
545 inode
->ctime
= hstat
.st_ctime
;
546 inode
->first_in_subdir
= NULL
;
547 inode
->last_in_subdir
= NULL
;
548 if (current_link_name
!= NULL
&& S_ISLNK (hstat
.st_mode
))
550 inode
->linkname
= current_link_name
;
551 current_link_name
= NULL
;
555 if (S_ISLNK (hstat
.st_mode
))
556 inode
->mode
&= ~S_IFLNK
; /* You *DON'T* want to do this always */
557 inode
->linkname
= NULL
;
559 if (S_ISDIR (hstat
.st_mode
))
560 extfs_make_dots (entry
);
564 g_free (current_file_name
);
565 g_free (current_link_name
);
570 /* Check if extfs 'list' returned 0 */
571 if (pclose (extfsd
) != 0)
573 extfs_free (current_archive
);
574 close_error_pipe (D_ERROR
, _("Inconsistent extfs archive"));
578 close_error_pipe (D_ERROR
, NULL
);
579 *pparc
= current_archive
;
584 extfs_which (struct vfs_class
*me
, const char *path
)
591 path_len
= strlen (path
);
593 for (i
= 0; i
< extfs_plugins
->len
; i
++)
595 extfs_plugin_info_t
*info
;
597 info
= &g_array_index (extfs_plugins
, extfs_plugin_info_t
, i
);
599 if ((strncmp (path
, info
->prefix
, path_len
) == 0)
600 && ((info
->prefix
[path_len
] == '\0')
601 || (info
->prefix
[path_len
] == '+')))
608 * Dissect the path and create corresponding superblock. Note that inname
609 * can be changed and the result may point inside the original string.
612 extfs_get_path_mangle (struct vfs_class
*me
, char *inname
, struct archive
**archive
,
613 gboolean do_not_open
)
616 const char *archive_name
;
618 struct archive
*parc
;
621 archive_name
= inname
;
622 vfs_split (inname
, &local
, &op
);
624 fstype
= extfs_which (me
, op
);
629 local
= inname
+ strlen (inname
);
632 * All filesystems should have some local archive, at least
633 * it can be PATH_SEP ('/').
635 for (parc
= first_archive
; parc
!= NULL
; parc
= parc
->next
)
636 if (parc
->name
!= NULL
)
638 if (strcmp (parc
->name
, archive_name
) == 0)
640 vfs_stamp (&vfs_extfs_ops
, (vfsid
) parc
);
645 result
= do_not_open
? -1 : extfs_read_archive (fstype
, archive_name
, &parc
);
655 * Dissect the path and create corresponding superblock.
656 * The result should be freed.
659 extfs_get_path (struct vfs_class
*me
, const char *inname
,
660 struct archive
**archive
, gboolean do_not_open
)
662 char *buf
, *res
, *res2
;
664 buf
= g_strdup (inname
);
665 res
= extfs_get_path_mangle (me
, buf
, archive
, do_not_open
);
666 res2
= g_strdup (res
);
671 /* Return allocated path (without leading slash) inside the archive */
673 extfs_get_path_from_entry (struct entry
*entry
)
677 localpath
= g_string_new ("");
679 while (entry
->dir
!= NULL
)
681 g_string_prepend (localpath
, entry
->name
);
682 if (entry
->dir
->dir
!= NULL
)
683 g_string_prepend_c (localpath
, PATH_SEP
);
687 return g_string_free (localpath
, FALSE
);
690 static struct entry
*
691 extfs_resolve_symlinks_int (struct entry
*entry
, GSList
* list
)
693 struct entry
*pent
= NULL
;
695 if (!S_ISLNK (entry
->inode
->mode
))
698 if (g_slist_find (list
, entry
) != NULL
)
700 /* Here we protect us against symlink looping */
707 looping
= g_slist_prepend (list
, entry
);
708 pent
= extfs_find_entry_int (entry
->dir
, entry
->inode
->linkname
, looping
, FALSE
, FALSE
);
709 looping
= g_slist_delete_link (looping
, looping
);
718 static struct entry
*
719 extfs_resolve_symlinks (struct entry
*entry
)
725 res
= extfs_resolve_symlinks_int (entry
, NULL
);
737 extfs_get_archive_name (struct archive
*archive
)
739 const char *archive_name
;
741 if (archive
->local_name
)
742 archive_name
= archive
->local_name
;
744 archive_name
= archive
->name
;
746 if (!archive_name
|| !*archive_name
)
747 return "no_archive_name";
752 /* Don't pass localname as NULL */
754 extfs_cmd (const char *str_extfs_cmd
, struct archive
*archive
,
755 struct entry
*entry
, const char *localname
)
759 char *quoted_localname
;
761 const extfs_plugin_info_t
*info
;
765 file
= extfs_get_path_from_entry (entry
);
766 quoted_file
= name_quote (file
, 0);
769 archive_name
= name_quote (extfs_get_archive_name (archive
), 0);
770 quoted_localname
= name_quote (localname
, 0);
771 info
= &g_array_index (extfs_plugins
, extfs_plugin_info_t
, archive
->fstype
);
772 cmd
= g_strconcat (info
->path
, info
->prefix
, str_extfs_cmd
,
773 archive_name
, " ", quoted_file
, " ", quoted_localname
, (char *) NULL
);
774 g_free (quoted_file
);
775 g_free (quoted_localname
);
776 g_free (archive_name
);
779 retval
= my_system (EXECUTE_AS_SHELL
, shell
, cmd
);
781 close_error_pipe (D_ERROR
, NULL
);
786 extfs_run (struct vfs_class
*me
, const char *file
)
788 struct archive
*archive
= NULL
;
789 char *p
, *q
, *archive_name
;
791 const extfs_plugin_info_t
*info
;
793 p
= extfs_get_path (me
, file
, &archive
, FALSE
);
796 q
= name_quote (p
, 0);
799 archive_name
= name_quote (extfs_get_archive_name (archive
), 0);
800 info
= &g_array_index (extfs_plugins
, extfs_plugin_info_t
, archive
->fstype
);
801 cmd
= g_strconcat (info
->path
, info
->prefix
, " run ", archive_name
, " ", q
, (char *) NULL
);
802 g_free (archive_name
);
804 shell_execute (cmd
, 0);
809 extfs_open (struct vfs_class
*me
, const char *file
, int flags
, int mode
)
811 struct pseudofile
*extfs_info
;
812 struct archive
*archive
= NULL
;
816 gboolean created
= FALSE
;
818 q
= extfs_get_path (me
, file
, &archive
, FALSE
);
821 entry
= extfs_find_entry (archive
->root_entry
, q
, FALSE
, FALSE
);
822 if ((entry
== NULL
) && ((flags
& O_CREAT
) != 0))
824 /* Create new entry */
825 entry
= extfs_find_entry (archive
->root_entry
, q
, FALSE
, TRUE
);
826 created
= (entry
!= NULL
);
832 entry
= extfs_resolve_symlinks (entry
);
836 if (S_ISDIR (entry
->inode
->mode
))
837 ERRNOR (EISDIR
, NULL
);
839 if (entry
->inode
->local_filename
== NULL
)
841 char *local_filename
;
843 local_handle
= vfs_mkstemps (&local_filename
, "extfs", entry
->name
);
845 if (local_handle
== -1)
847 close (local_handle
);
849 if (!created
&& ((flags
& O_TRUNC
) == 0)
850 && extfs_cmd (" copyout ", archive
, entry
, local_filename
))
852 unlink (local_filename
);
853 g_free (local_filename
);
857 entry
->inode
->local_filename
= local_filename
;
860 local_handle
= open (entry
->inode
->local_filename
, NO_LINEAR (flags
), mode
);
862 if (local_handle
== -1)
864 /* file exists(may be). Need to drop O_CREAT flag and truncate file content */
865 flags
= ~O_CREAT
& (NO_LINEAR (flags
) | O_TRUNC
);
866 local_handle
= open (entry
->inode
->local_filename
, flags
, mode
);
869 if (local_handle
== -1)
872 extfs_info
= g_new (struct pseudofile
, 1);
873 extfs_info
->archive
= archive
;
874 extfs_info
->entry
= entry
;
875 extfs_info
->has_changed
= created
;
876 extfs_info
->local_handle
= local_handle
;
878 /* i.e. we had no open files and now we have one */
879 vfs_rmstamp (&vfs_extfs_ops
, (vfsid
) archive
);
885 extfs_read (void *data
, char *buffer
, int count
)
887 struct pseudofile
*file
= (struct pseudofile
*) data
;
889 return read (file
->local_handle
, buffer
, count
);
893 extfs_close (void *data
)
895 struct pseudofile
*file
;
897 file
= (struct pseudofile
*) data
;
899 close (file
->local_handle
);
901 /* Commit the file if it has changed */
902 if (file
->has_changed
)
904 struct stat file_status
;
906 if (extfs_cmd (" copyin ", file
->archive
, file
->entry
, file
->entry
->inode
->local_filename
))
909 if (stat (file
->entry
->inode
->local_filename
, &file_status
) != 0)
912 file
->entry
->inode
->size
= file_status
.st_size
;
914 file
->entry
->inode
->mtime
= time (NULL
);
917 if (--file
->archive
->fd_usage
== 0)
918 vfs_stamp_create (&vfs_extfs_ops
, file
->archive
);
927 extfs_errno (struct vfs_class
*me
)
934 extfs_opendir (struct vfs_class
*me
, const char *dirname
)
936 struct archive
*archive
= NULL
;
941 q
= extfs_get_path (me
, dirname
, &archive
, FALSE
);
944 entry
= extfs_find_entry (archive
->root_entry
, q
, FALSE
, FALSE
);
948 entry
= extfs_resolve_symlinks (entry
);
951 if (!S_ISDIR (entry
->inode
->mode
))
952 ERRNOR (ENOTDIR
, NULL
);
954 info
= g_new (struct entry
*, 2);
955 info
[0] = entry
->inode
->first_in_subdir
;
956 info
[1] = entry
->inode
->first_in_subdir
;
962 extfs_readdir (void *data
)
964 static union vfs_dirent dir
;
965 struct entry
**info
= (struct entry
**) data
;
970 g_strlcpy (dir
.dent
.d_name
, (*info
)->name
, MC_MAXPATHLEN
);
972 compute_namelen (&dir
.dent
);
973 *info
= (*info
)->next_in_dir
;
975 return (void *) &dir
;
979 extfs_closedir (void *data
)
985 #define RECORDSIZE 512
988 extfs_stat_move (struct stat
*buf
, const struct inode
*inode
)
990 buf
->st_dev
= inode
->dev
;
991 buf
->st_ino
= inode
->inode
;
992 buf
->st_mode
= inode
->mode
;
993 buf
->st_nlink
= inode
->nlink
;
994 buf
->st_uid
= inode
->uid
;
995 buf
->st_gid
= inode
->gid
;
996 #ifdef HAVE_STRUCT_STAT_ST_RDEV
997 buf
->st_rdev
= inode
->rdev
;
999 buf
->st_size
= inode
->size
;
1000 #ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
1001 buf
->st_blksize
= RECORDSIZE
;
1003 #ifdef HAVE_STRUCT_STAT_ST_BLOCKS
1004 buf
->st_blocks
= (inode
->size
+ RECORDSIZE
- 1) / RECORDSIZE
;
1006 buf
->st_atime
= inode
->atime
;
1007 buf
->st_mtime
= inode
->mtime
;
1008 buf
->st_ctime
= inode
->ctime
;
1012 extfs_internal_stat (struct vfs_class
*me
, const char *path
, struct stat
*buf
, gboolean resolve
)
1014 struct archive
*archive
;
1016 struct entry
*entry
;
1019 mpath
= g_strdup (path
);
1021 q
= extfs_get_path_mangle (me
, mpath
, &archive
, FALSE
);
1024 entry
= extfs_find_entry (archive
->root_entry
, q
, FALSE
, FALSE
);
1029 entry
= extfs_resolve_symlinks (entry
);
1033 extfs_stat_move (buf
, entry
->inode
);
1041 extfs_stat (struct vfs_class
*me
, const char *path
, struct stat
*buf
)
1043 return extfs_internal_stat (me
, path
, buf
, TRUE
);
1047 extfs_lstat (struct vfs_class
*me
, const char *path
, struct stat
*buf
)
1049 return extfs_internal_stat (me
, path
, buf
, FALSE
);
1053 extfs_fstat (void *data
, struct stat
*buf
)
1055 struct pseudofile
*file
= (struct pseudofile
*) data
;
1057 extfs_stat_move (buf
, file
->entry
->inode
);
1062 extfs_readlink (struct vfs_class
*me
, const char *path
, char *buf
, size_t size
)
1064 struct archive
*archive
;
1067 struct entry
*entry
;
1070 mpath
= g_strdup (path
);
1072 q
= extfs_get_path_mangle (me
, mpath
, &archive
, FALSE
);
1075 entry
= extfs_find_entry (archive
->root_entry
, q
, FALSE
, FALSE
);
1078 if (!S_ISLNK (entry
->inode
->mode
))
1080 me
->verrno
= EINVAL
;
1083 len
= strlen (entry
->inode
->linkname
);
1086 /* readlink() does not append a NUL character to buf */
1088 memcpy (buf
, entry
->inode
->linkname
, result
);
1095 extfs_chown (struct vfs_class
*me
, const char *path
, int owner
, int group
)
1105 extfs_chmod (struct vfs_class
*me
, const char *path
, int mode
)
1114 extfs_write (void *data
, const char *buf
, int nbyte
)
1116 struct pseudofile
*file
= (struct pseudofile
*) data
;
1118 file
->has_changed
= TRUE
;
1119 return write (file
->local_handle
, buf
, nbyte
);
1123 extfs_unlink (struct vfs_class
*me
, const char *file
)
1125 struct archive
*archive
;
1127 struct entry
*entry
;
1130 mpath
= g_strdup (file
);
1132 q
= extfs_get_path_mangle (me
, mpath
, &archive
, FALSE
);
1135 entry
= extfs_find_entry (archive
->root_entry
, q
, FALSE
, FALSE
);
1138 entry
= extfs_resolve_symlinks (entry
);
1141 if (S_ISDIR (entry
->inode
->mode
))
1143 me
->verrno
= EISDIR
;
1146 if (extfs_cmd (" rm ", archive
, entry
, ""))
1151 extfs_remove_entry (entry
);
1159 extfs_mkdir (struct vfs_class
*me
, const char *path
, mode_t mode
)
1161 struct archive
*archive
;
1163 struct entry
*entry
;
1168 mpath
= g_strdup (path
);
1170 q
= extfs_get_path_mangle (me
, mpath
, &archive
, FALSE
);
1173 entry
= extfs_find_entry (archive
->root_entry
, q
, FALSE
, FALSE
);
1176 me
->verrno
= EEXIST
;
1179 entry
= extfs_find_entry (archive
->root_entry
, q
, TRUE
, FALSE
);
1182 entry
= extfs_resolve_symlinks (entry
);
1185 if (!S_ISDIR (entry
->inode
->mode
))
1187 me
->verrno
= ENOTDIR
;
1191 if (extfs_cmd (" mkdir ", archive
, entry
, ""))
1194 extfs_remove_entry (entry
);
1204 extfs_rmdir (struct vfs_class
*me
, const char *path
)
1206 struct archive
*archive
;
1208 struct entry
*entry
;
1211 mpath
= g_strdup (path
);
1213 q
= extfs_get_path_mangle (me
, mpath
, &archive
, FALSE
);
1216 entry
= extfs_find_entry (archive
->root_entry
, q
, FALSE
, FALSE
);
1219 entry
= extfs_resolve_symlinks (entry
);
1222 if (!S_ISDIR (entry
->inode
->mode
))
1224 me
->verrno
= ENOTDIR
;
1228 if (extfs_cmd (" rmdir ", archive
, entry
, ""))
1233 extfs_remove_entry (entry
);
1241 extfs_chdir (struct vfs_class
*me
, const char *path
)
1243 struct archive
*archive
= NULL
;
1245 struct entry
*entry
;
1248 q
= extfs_get_path (me
, path
, &archive
, FALSE
);
1251 entry
= extfs_find_entry (archive
->root_entry
, q
, FALSE
, FALSE
);
1255 entry
= extfs_resolve_symlinks (entry
);
1256 if ((entry
== NULL
) || (!S_ISDIR (entry
->inode
->mode
)))
1263 extfs_lseek (void *data
, off_t offset
, int whence
)
1265 struct pseudofile
*file
= (struct pseudofile
*) data
;
1267 return lseek (file
->local_handle
, offset
, whence
);
1271 extfs_getid (struct vfs_class
*me
, const char *path
)
1273 struct archive
*archive
= NULL
;
1276 p
= extfs_get_path (me
, path
, &archive
, TRUE
);
1280 return (vfsid
) archive
;
1284 extfs_nothingisopen (vfsid id
)
1286 return (((struct archive
*) id
)->fd_usage
<= 0);
1290 extfs_remove_entry (struct entry
*e
)
1292 int i
= --e
->inode
->nlink
;
1293 struct entry
*pe
, *ent
, *prev
;
1295 if (S_ISDIR (e
->inode
->mode
) && e
->inode
->first_in_subdir
!= NULL
)
1297 struct entry
*f
= e
->inode
->first_in_subdir
;
1298 e
->inode
->first_in_subdir
= NULL
;
1299 extfs_remove_entry (f
);
1302 if (e
== pe
->inode
->first_in_subdir
)
1303 pe
->inode
->first_in_subdir
= e
->next_in_dir
;
1306 for (ent
= pe
->inode
->first_in_subdir
; ent
&& ent
->next_in_dir
; ent
= ent
->next_in_dir
)
1307 if (e
== ent
->next_in_dir
)
1313 prev
->next_in_dir
= e
->next_in_dir
;
1314 if (e
== pe
->inode
->last_in_subdir
)
1315 pe
->inode
->last_in_subdir
= prev
;
1319 if (e
->inode
->local_filename
!= NULL
)
1321 unlink (e
->inode
->local_filename
);
1322 g_free (e
->inode
->local_filename
);
1324 g_free (e
->inode
->linkname
);
1333 extfs_free_entry (struct entry
*e
)
1335 int i
= --e
->inode
->nlink
;
1337 if (S_ISDIR (e
->inode
->mode
) && e
->inode
->first_in_subdir
!= NULL
)
1339 struct entry
*f
= e
->inode
->first_in_subdir
;
1341 e
->inode
->first_in_subdir
= NULL
;
1342 extfs_free_entry (f
);
1346 if (e
->inode
->local_filename
!= NULL
)
1348 unlink (e
->inode
->local_filename
);
1349 g_free (e
->inode
->local_filename
);
1351 g_free (e
->inode
->linkname
);
1354 if (e
->next_in_dir
!= NULL
)
1355 extfs_free_entry (e
->next_in_dir
);
1361 extfs_free (vfsid id
)
1363 struct archive
*archive
= (struct archive
*) id
;
1365 if (archive
== first_archive
)
1367 first_archive
= archive
->next
;
1371 struct archive
*parc
;
1372 for (parc
= first_archive
; parc
!= NULL
; parc
= parc
->next
)
1373 if (parc
->next
== archive
)
1375 parc
->next
= archive
->next
;
1379 extfs_free_archive (archive
);
1383 extfs_getlocalcopy (struct vfs_class
*me
, const char *path
)
1385 struct pseudofile
*fp
;
1388 fp
= (struct pseudofile
*) extfs_open (me
, path
, O_RDONLY
, 0);
1391 if (fp
->entry
->inode
->local_filename
== NULL
)
1393 extfs_close ((void *) fp
);
1396 p
= g_strdup (fp
->entry
->inode
->local_filename
);
1397 fp
->archive
->fd_usage
++;
1398 extfs_close ((void *) fp
);
1403 extfs_ungetlocalcopy (struct vfs_class
*me
, const char *path
, const char *local
, int has_changed
)
1405 struct pseudofile
*fp
;
1407 fp
= (struct pseudofile
*) extfs_open (me
, path
, O_RDONLY
, 0);
1411 if (strcmp (fp
->entry
->inode
->local_filename
, local
) == 0)
1413 fp
->archive
->fd_usage
--;
1414 if (has_changed
!= 0)
1415 fp
->has_changed
= TRUE
;
1416 extfs_close ((void *) fp
);
1421 /* Should not happen */
1422 extfs_close ((void *) fp
);
1428 extfs_get_plugins (const char *where
, gboolean silent
)
1432 const char *filename
;
1434 dirname
= g_build_path (PATH_SEP_STR
, where
, MC_EXTFS_DIR
, (char *) NULL
);
1435 dir
= g_dir_open (dirname
, 0, NULL
);
1437 /* We may not use vfs_die() message or message or similar,
1438 * UI is not initialized at this time and message would not
1439 * appear on screen. */
1443 fprintf (stderr
, _("Warning: cannot open %s directory\n"), dirname
);
1448 if (extfs_plugins
== NULL
)
1449 extfs_plugins
= g_array_sized_new (FALSE
, TRUE
, sizeof (extfs_plugin_info_t
), 32);
1451 while ((filename
= g_dir_read_name (dir
)) != NULL
)
1453 char fullname
[MC_MAXPATHLEN
];
1456 g_snprintf (fullname
, sizeof (fullname
), "%s" PATH_SEP_STR
"%s", dirname
, filename
);
1458 if ((stat (fullname
, &s
) == 0)
1459 && S_ISREG (s
.st_mode
) && !S_ISDIR (s
.st_mode
)
1460 && (((s
.st_mode
& S_IXOTH
) != 0) ||
1461 ((s
.st_mode
& S_IXUSR
) != 0) || ((s
.st_mode
& S_IXGRP
) != 0)))
1465 f
= open (fullname
, O_RDONLY
);
1470 extfs_plugin_info_t info
;
1471 gboolean found
= FALSE
;
1475 /* Handle those with a trailing '+', those flag that the
1476 * file system does not require an archive to work
1478 len
= strlen (filename
);
1479 info
.need_archive
= (filename
[len
- 1] != '+');
1480 info
.path
= g_strconcat (dirname
, PATH_SEP_STR
, (char *) NULL
);
1481 info
.prefix
= g_strdup (filename
);
1483 /* prepare to compare file names without trailing '+' */
1484 if (!info
.need_archive
)
1485 info
.prefix
[len
- 1] = '\0';
1487 /* don't overload already found plugin */
1488 for (i
= 0; i
< extfs_plugins
->len
; i
++)
1490 extfs_plugin_info_t
*p
;
1492 p
= &g_array_index (extfs_plugins
, extfs_plugin_info_t
, i
);
1494 /* 2 files with same names cannot be in a directory */
1495 if ((strcmp (info
.path
, p
->path
) != 0)
1496 && (strcmp (info
.prefix
, p
->prefix
) == 0))
1506 g_free (info
.prefix
);
1510 /* restore file name */
1511 if (!info
.need_archive
)
1512 info
.prefix
[len
- 1] = '+';
1513 g_array_append_val (extfs_plugins
, info
);
1527 extfs_init (struct vfs_class
*me
)
1534 /* 1st: scan user directory */
1535 dirname
= g_build_path (PATH_SEP_STR
, home_dir
, MC_USERCONF_DIR
, (char *) NULL
);
1536 d1
= extfs_get_plugins (dirname
, TRUE
); /* silent about user dir */
1538 /* 2nd: scan system dir */
1539 d2
= extfs_get_plugins (LIBEXECDIR
, d1
);
1541 return (d1
|| d2
? 1 : 0);
1545 extfs_done (struct vfs_class
*me
)
1552 for (ar
= first_archive
; ar
!= NULL
;)
1554 extfs_free ((vfsid
) ar
);
1558 for (i
= 0; i
< extfs_plugins
->len
; i
++)
1560 extfs_plugin_info_t
*info
;
1562 info
= &g_array_index (extfs_plugins
, extfs_plugin_info_t
, i
);
1563 g_free (info
->path
);
1564 g_free (info
->prefix
);
1566 g_array_free (extfs_plugins
, TRUE
);
1570 extfs_setctl (struct vfs_class
*me
, const char *path
, int ctlop
, void *arg
)
1574 if (ctlop
== VFS_SETCTL_RUN
)
1576 extfs_run (me
, path
);
1585 vfs_extfs_ops
.name
= "extfs";
1586 vfs_extfs_ops
.init
= extfs_init
;
1587 vfs_extfs_ops
.done
= extfs_done
;
1588 vfs_extfs_ops
.fill_names
= extfs_fill_names
;
1589 vfs_extfs_ops
.which
= extfs_which
;
1590 vfs_extfs_ops
.open
= extfs_open
;
1591 vfs_extfs_ops
.close
= extfs_close
;
1592 vfs_extfs_ops
.read
= extfs_read
;
1593 vfs_extfs_ops
.write
= extfs_write
;
1594 vfs_extfs_ops
.opendir
= extfs_opendir
;
1595 vfs_extfs_ops
.readdir
= extfs_readdir
;
1596 vfs_extfs_ops
.closedir
= extfs_closedir
;
1597 vfs_extfs_ops
.stat
= extfs_stat
;
1598 vfs_extfs_ops
.lstat
= extfs_lstat
;
1599 vfs_extfs_ops
.fstat
= extfs_fstat
;
1600 vfs_extfs_ops
.chmod
= extfs_chmod
;
1601 vfs_extfs_ops
.chown
= extfs_chown
;
1602 vfs_extfs_ops
.readlink
= extfs_readlink
;
1603 vfs_extfs_ops
.unlink
= extfs_unlink
;
1604 vfs_extfs_ops
.chdir
= extfs_chdir
;
1605 vfs_extfs_ops
.ferrno
= extfs_errno
;
1606 vfs_extfs_ops
.lseek
= extfs_lseek
;
1607 vfs_extfs_ops
.getid
= extfs_getid
;
1608 vfs_extfs_ops
.nothingisopen
= extfs_nothingisopen
;
1609 vfs_extfs_ops
.free
= extfs_free
;
1610 vfs_extfs_ops
.getlocalcopy
= extfs_getlocalcopy
;
1611 vfs_extfs_ops
.ungetlocalcopy
= extfs_ungetlocalcopy
;
1612 vfs_extfs_ops
.mkdir
= extfs_mkdir
;
1613 vfs_extfs_ops
.rmdir
= extfs_rmdir
;
1614 vfs_extfs_ops
.setctl
= extfs_setctl
;
1615 vfs_register_class (&vfs_extfs_ops
);