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"
51 #include "lib/mcconfig.h"
53 #include "lib/widget.h" /* message() */
55 #include "src/main.h" /* shell */
56 #include "src/execute.h" /* For shell_execute */
58 #include "lib/vfs/vfs.h"
59 #include "lib/vfs/utilvfs.h"
60 #include "lib/vfs/gc.h" /* vfs_rmstamp */
64 /*** global variables ****************************************************************************/
66 GArray
*extfs_plugins
= NULL
;
68 /*** file scope macro definitions ****************************************************************/
71 #define ERRNOR(x,y) do { my_errno = x; return y; } while(0)
73 #define RECORDSIZE 512
75 /*** file scope type declarations ****************************************************************/
80 struct entry
*first_in_subdir
; /* only used if this is a directory */
81 struct entry
*last_in_subdir
;
82 ino_t inode
; /* This is inode # */
83 dev_t dev
; /* This is an internal identification of the extfs archive */
84 struct archive
*archive
; /* And this is an archive structure */
99 struct entry
*next_in_dir
;
107 struct archive
*archive
;
108 gboolean has_changed
;
118 struct stat local_stat
;
122 struct entry
*root_entry
;
123 struct archive
*next
;
130 gboolean need_archive
;
131 } extfs_plugin_info_t
;
133 /*** file scope variables ************************************************************************/
135 static gboolean errloop
;
136 static gboolean notadir
;
138 static struct vfs_class vfs_extfs_ops
;
139 static struct archive
*first_archive
= NULL
;
140 static int my_errno
= 0;
142 /*** file scope functions ************************************************************************/
143 /* --------------------------------------------------------------------------------------------- */
145 static void extfs_remove_entry (struct entry
*e
);
146 static void extfs_free (vfsid id
);
147 static void extfs_free_entry (struct entry
*e
);
148 static struct entry
*extfs_resolve_symlinks_int (struct entry
*entry
, GSList
* list
);
150 /* --------------------------------------------------------------------------------------------- */
153 extfs_make_dots (struct entry
*ent
)
155 struct entry
*entry
= g_new (struct entry
, 1);
156 struct entry
*parentry
= ent
->dir
;
157 struct inode
*inode
= ent
->inode
, *parent
;
159 parent
= (parentry
!= NULL
) ? parentry
->inode
: NULL
;
160 entry
->name
= g_strdup (".");
161 entry
->inode
= inode
;
163 inode
->local_filename
= NULL
;
164 inode
->first_in_subdir
= entry
;
167 entry
->next_in_dir
= g_new (struct entry
, 1);
168 entry
= entry
->next_in_dir
;
169 entry
->name
= g_strdup ("..");
170 inode
->last_in_subdir
= entry
;
171 entry
->next_in_dir
= NULL
;
174 entry
->inode
= parent
;
175 entry
->dir
= parentry
;
180 entry
->inode
= inode
;
186 /* --------------------------------------------------------------------------------------------- */
188 static struct entry
*
189 extfs_generate_entry (struct archive
*archive
,
190 const char *name
, struct entry
*parentry
, mode_t mode
)
193 struct inode
*inode
, *parent
;
196 parent
= (parentry
!= NULL
) ? parentry
->inode
: NULL
;
197 entry
= g_new (struct entry
, 1);
199 entry
->name
= g_strdup (name
);
200 entry
->next_in_dir
= NULL
;
201 entry
->dir
= parentry
;
204 parent
->last_in_subdir
->next_in_dir
= entry
;
205 parent
->last_in_subdir
= entry
;
207 inode
= g_new (struct inode
, 1);
208 entry
->inode
= inode
;
209 inode
->local_filename
= NULL
;
210 inode
->linkname
= NULL
;
211 inode
->last_in_subdir
= NULL
;
212 inode
->inode
= (archive
->inode_counter
)++;
213 inode
->dev
= archive
->rdev
;
214 inode
->archive
= archive
;
215 myumask
= umask (022);
217 inode
->mode
= mode
& ~myumask
;
220 inode
->uid
= getuid ();
221 inode
->gid
= getgid ();
223 inode
->mtime
= time (NULL
);
224 inode
->atime
= inode
->mtime
;
225 inode
->ctime
= inode
->mtime
;
228 extfs_make_dots (entry
);
232 /* --------------------------------------------------------------------------------------------- */
234 static struct entry
*
235 extfs_find_entry_int (struct entry
*dir
, char *name
, GSList
* list
,
236 gboolean make_dirs
, gboolean make_file
)
238 struct entry
*pent
, *pdir
;
239 char *p
, *q
, *name_end
;
242 if (g_path_is_absolute (name
))
244 /* Handle absolute paths */
245 name
= (char *) g_path_skip_root (name
);
246 dir
= dir
->inode
->archive
->root_entry
;
251 name_end
= name
+ strlen (name
);
253 q
= strchr (p
, PATH_SEP
);
255 q
= strchr (p
, '\0');
257 while ((pent
!= NULL
) && (c
!= '\0') && (*p
!= '\0'))
262 if (strcmp (p
, ".") != 0)
264 if (strcmp (p
, "..") == 0)
268 pent
= extfs_resolve_symlinks_int (pent
, list
);
274 if (!S_ISDIR (pent
->inode
->mode
))
282 for (pent
= pent
->inode
->first_in_subdir
; pent
!= NULL
; pent
= pent
->next_in_dir
)
283 /* Hack: I keep the original semanthic unless
284 q+1 would break in the strchr */
285 if (strcmp (pent
->name
, p
) == 0)
287 if (q
+ 1 > name_end
)
290 notadir
= !S_ISDIR (pent
->inode
->mode
);
296 /* When we load archive, we create automagically
297 * non-existant directories
299 if (pent
== NULL
&& make_dirs
)
300 pent
= extfs_generate_entry (dir
->inode
->archive
, p
, pdir
, S_IFDIR
| 0777);
301 if (pent
== NULL
&& make_file
)
302 pent
= extfs_generate_entry (dir
->inode
->archive
, p
, pdir
, S_IFREG
| 0666);
308 q
= strchr (p
, PATH_SEP
);
310 q
= strchr (p
, '\0');
317 /* --------------------------------------------------------------------------------------------- */
319 static struct entry
*
320 extfs_find_entry (struct entry
*dir
, char *name
, gboolean make_dirs
, gboolean make_file
)
327 res
= extfs_find_entry_int (dir
, name
, NULL
, make_dirs
, make_file
);
338 /* --------------------------------------------------------------------------------------------- */
341 extfs_fill_names (struct vfs_class
*me
, fill_names_f func
)
343 struct archive
*a
= first_archive
;
349 extfs_plugin_info_t
*info
;
352 info
= &g_array_index (extfs_plugins
, extfs_plugin_info_t
, a
->fstype
);
354 g_strconcat (a
->name
? a
->name
: "", "/", info
->prefix
, VFS_PATH_URL_DELIMITER
,
362 /* --------------------------------------------------------------------------------------------- */
365 extfs_free_archive (struct archive
*archive
)
367 extfs_free_entry (archive
->root_entry
);
368 if (archive
->local_name
!= NULL
)
372 mc_stat (archive
->local_name
, &my
);
373 mc_ungetlocalcopy (archive
->name
, archive
->local_name
,
374 archive
->local_stat
.st_mtime
!= my
.st_mtime
);
375 g_free (archive
->local_name
);
377 g_free (archive
->name
);
381 /* --------------------------------------------------------------------------------------------- */
384 extfs_open_archive (int fstype
, const char *name
, struct archive
**pparc
)
386 const extfs_plugin_info_t
*info
;
387 static dev_t archive_counter
= 0;
392 struct archive
*current_archive
;
393 struct entry
*root_entry
;
394 char *local_name
= NULL
, *tmp
= NULL
;
396 info
= &g_array_index (extfs_plugins
, extfs_plugin_info_t
, fstype
);
398 if (info
->need_archive
)
400 vfs_path_t
*vpath
= vfs_path_from_str (name
);
401 if (mc_stat (name
, &mystat
) == -1)
404 if (!vfs_file_is_local (vpath
))
406 local_name
= mc_getlocalcopy (name
);
407 if (local_name
== NULL
)
410 vfs_path_free (vpath
);
412 tmp
= name_quote (name
, 0);
415 cmd
= g_strconcat (info
->path
, info
->prefix
, " list ",
416 local_name
!= NULL
? local_name
: tmp
, (char *) NULL
);
420 result
= popen (cmd
, "r");
424 close_error_pipe (D_ERROR
, NULL
);
425 if (local_name
!= NULL
)
427 mc_ungetlocalcopy (name
, local_name
, 0);
434 setvbuf (result
, NULL
, _IONBF
, 0);
437 current_archive
= g_new (struct archive
, 1);
438 current_archive
->fstype
= fstype
;
439 current_archive
->name
= name
? g_strdup (name
) : NULL
;
440 current_archive
->local_name
= local_name
;
442 if (local_name
!= NULL
)
443 mc_stat (local_name
, ¤t_archive
->local_stat
);
444 current_archive
->inode_counter
= 0;
445 current_archive
->fd_usage
= 0;
446 current_archive
->rdev
= archive_counter
++;
447 current_archive
->next
= first_archive
;
448 first_archive
= current_archive
;
449 mode
= mystat
.st_mode
& 07777;
457 root_entry
= extfs_generate_entry (current_archive
, PATH_SEP_STR
, NULL
, mode
);
458 root_entry
->inode
->uid
= mystat
.st_uid
;
459 root_entry
->inode
->gid
= mystat
.st_gid
;
460 root_entry
->inode
->atime
= mystat
.st_atime
;
461 root_entry
->inode
->ctime
= mystat
.st_ctime
;
462 root_entry
->inode
->mtime
= mystat
.st_mtime
;
463 current_archive
->root_entry
= root_entry
;
465 *pparc
= current_archive
;
470 /* --------------------------------------------------------------------------------------------- */
472 * Main loop for reading an archive.
473 * Return 0 on success, -1 on error.
477 extfs_read_archive (int fstype
, const char *name
, struct archive
**pparc
)
480 const extfs_plugin_info_t
*info
;
482 struct archive
*current_archive
;
483 char *current_file_name
, *current_link_name
;
485 info
= &g_array_index (extfs_plugins
, extfs_plugin_info_t
, fstype
);
487 extfsd
= extfs_open_archive (fstype
, name
, ¤t_archive
);
491 message (D_ERROR
, MSG_ERROR
, _("Cannot open %s archive\n%s"), info
->prefix
, name
);
495 buffer
= g_malloc (BUF_4K
);
496 while (fgets (buffer
, BUF_4K
, extfsd
) != NULL
)
500 current_link_name
= NULL
;
501 if (vfs_parse_ls_lga (buffer
, &hstat
, ¤t_file_name
, ¤t_link_name
))
503 struct entry
*entry
, *pent
;
505 char *p
, *q
, *cfn
= current_file_name
;
509 if (*cfn
== PATH_SEP
)
511 p
= strchr (cfn
, '\0');
512 if (p
!= cfn
&& *(p
- 1) == PATH_SEP
)
514 p
= strrchr (cfn
, PATH_SEP
);
518 q
= strchr (cfn
, '\0');
525 if (S_ISDIR (hstat
.st_mode
) && (strcmp (p
, ".") == 0 || strcmp (p
, "..") == 0))
526 goto read_extfs_continue
;
527 pent
= extfs_find_entry (current_archive
->root_entry
, q
, TRUE
, FALSE
);
530 /* FIXME: Should clean everything one day */
533 close_error_pipe (D_ERROR
, _("Inconsistent extfs archive"));
536 entry
= g_new (struct entry
, 1);
537 entry
->name
= g_strdup (p
);
538 entry
->next_in_dir
= NULL
;
540 if (pent
->inode
->last_in_subdir
)
542 pent
->inode
->last_in_subdir
->next_in_dir
= entry
;
543 pent
->inode
->last_in_subdir
= entry
;
545 if (!S_ISLNK (hstat
.st_mode
) && (current_link_name
!= NULL
))
547 pent
= extfs_find_entry (current_archive
->root_entry
,
548 current_link_name
, FALSE
, FALSE
);
551 /* FIXME: Should clean everything one day */
554 close_error_pipe (D_ERROR
, _("Inconsistent extfs archive"));
558 entry
->inode
= pent
->inode
;
559 pent
->inode
->nlink
++;
563 inode
= g_new (struct inode
, 1);
564 entry
->inode
= inode
;
565 inode
->local_filename
= NULL
;
566 inode
->inode
= (current_archive
->inode_counter
)++;
568 inode
->dev
= current_archive
->rdev
;
569 inode
->archive
= current_archive
;
570 inode
->mode
= hstat
.st_mode
;
571 #ifdef HAVE_STRUCT_STAT_ST_RDEV
572 inode
->rdev
= hstat
.st_rdev
;
576 inode
->uid
= hstat
.st_uid
;
577 inode
->gid
= hstat
.st_gid
;
578 inode
->size
= hstat
.st_size
;
579 inode
->mtime
= hstat
.st_mtime
;
580 inode
->atime
= hstat
.st_atime
;
581 inode
->ctime
= hstat
.st_ctime
;
582 inode
->first_in_subdir
= NULL
;
583 inode
->last_in_subdir
= NULL
;
584 if (current_link_name
!= NULL
&& S_ISLNK (hstat
.st_mode
))
586 inode
->linkname
= current_link_name
;
587 current_link_name
= NULL
;
591 if (S_ISLNK (hstat
.st_mode
))
592 inode
->mode
&= ~S_IFLNK
; /* You *DON'T* want to do this always */
593 inode
->linkname
= NULL
;
595 if (S_ISDIR (hstat
.st_mode
))
596 extfs_make_dots (entry
);
600 g_free (current_file_name
);
601 g_free (current_link_name
);
606 /* Check if extfs 'list' returned 0 */
607 if (pclose (extfsd
) != 0)
609 extfs_free (current_archive
);
610 close_error_pipe (D_ERROR
, _("Inconsistent extfs archive"));
614 close_error_pipe (D_ERROR
, NULL
);
615 *pparc
= current_archive
;
619 /* --------------------------------------------------------------------------------------------- */
622 extfs_which (struct vfs_class
*me
, const char *path
)
629 path_len
= strlen (path
);
631 for (i
= 0; i
< extfs_plugins
->len
; i
++)
633 extfs_plugin_info_t
*info
;
635 info
= &g_array_index (extfs_plugins
, extfs_plugin_info_t
, i
);
637 if ((strncmp (path
, info
->prefix
, path_len
) == 0)
638 && ((info
->prefix
[path_len
] == '\0') || (info
->prefix
[path_len
] == '+')))
644 /* --------------------------------------------------------------------------------------------- */
646 * Dissect the path and create corresponding superblock. Note that inname
647 * can be changed and the result may point inside the original string.
651 extfs_get_path_mangle (const vfs_path_t
* vpath
, struct archive
**archive
, gboolean do_not_open
)
655 struct archive
*parc
;
657 vfs_path_element_t
*path_element
;
659 path_element
= vfs_path_get_by_index (vpath
, -1);
661 archive_name
= vfs_path_to_str_elements_count (vpath
, -1);
663 fstype
= extfs_which (path_element
->class, path_element
->vfs_prefix
);
667 g_free (archive_name
);
672 * All filesystems should have some local archive, at least
673 * it can be PATH_SEP ('/').
675 for (parc
= first_archive
; parc
!= NULL
; parc
= parc
->next
)
676 if (parc
->name
!= NULL
)
678 if (strcmp (parc
->name
, archive_name
) == 0)
680 vfs_stamp (&vfs_extfs_ops
, (vfsid
) parc
);
685 result
= do_not_open
? -1 : extfs_read_archive (fstype
, archive_name
, &parc
);
688 path_element
->class->verrno
= EIO
;
689 g_free (archive_name
);
695 g_free (archive_name
);
696 return path_element
->path
;
699 /* --------------------------------------------------------------------------------------------- */
701 * Dissect the path and create corresponding superblock.
702 * The result should be freed.
706 extfs_get_path (const vfs_path_t
* vpath
, struct archive
**archive
, gboolean do_not_open
)
708 return g_strdup (extfs_get_path_mangle (vpath
, archive
, do_not_open
));
711 /* --------------------------------------------------------------------------------------------- */
712 /* Return allocated path (without leading slash) inside the archive */
715 extfs_get_path_from_entry (struct entry
*entry
)
719 localpath
= g_string_new ("");
721 while (entry
->dir
!= NULL
)
723 g_string_prepend (localpath
, entry
->name
);
724 if (entry
->dir
->dir
!= NULL
)
725 g_string_prepend_c (localpath
, PATH_SEP
);
729 return g_string_free (localpath
, FALSE
);
732 /* --------------------------------------------------------------------------------------------- */
734 static struct entry
*
735 extfs_resolve_symlinks_int (struct entry
*entry
, GSList
* list
)
737 struct entry
*pent
= NULL
;
739 if (!S_ISLNK (entry
->inode
->mode
))
742 if (g_slist_find (list
, entry
) != NULL
)
744 /* Here we protect us against symlink looping */
751 looping
= g_slist_prepend (list
, entry
);
752 pent
= extfs_find_entry_int (entry
->dir
, entry
->inode
->linkname
, looping
, FALSE
, FALSE
);
753 looping
= g_slist_delete_link (looping
, looping
);
762 /* --------------------------------------------------------------------------------------------- */
764 static struct entry
*
765 extfs_resolve_symlinks (struct entry
*entry
)
771 res
= extfs_resolve_symlinks_int (entry
, NULL
);
782 /* --------------------------------------------------------------------------------------------- */
785 extfs_get_archive_name (struct archive
*archive
)
787 const char *archive_name
;
789 if (archive
->local_name
)
790 archive_name
= archive
->local_name
;
792 archive_name
= archive
->name
;
794 if (!archive_name
|| !*archive_name
)
795 return "no_archive_name";
800 /* --------------------------------------------------------------------------------------------- */
801 /** Don't pass localname as NULL */
804 extfs_cmd (const char *str_extfs_cmd
, struct archive
*archive
,
805 struct entry
*entry
, const char *localname
)
809 char *quoted_localname
;
811 const extfs_plugin_info_t
*info
;
815 file
= extfs_get_path_from_entry (entry
);
816 quoted_file
= name_quote (file
, 0);
819 archive_name
= name_quote (extfs_get_archive_name (archive
), 0);
820 quoted_localname
= name_quote (localname
, 0);
821 info
= &g_array_index (extfs_plugins
, extfs_plugin_info_t
, archive
->fstype
);
822 cmd
= g_strconcat (info
->path
, info
->prefix
, str_extfs_cmd
,
823 archive_name
, " ", quoted_file
, " ", quoted_localname
, (char *) NULL
);
824 g_free (quoted_file
);
825 g_free (quoted_localname
);
826 g_free (archive_name
);
829 retval
= my_system (EXECUTE_AS_SHELL
, shell
, cmd
);
831 close_error_pipe (D_ERROR
, NULL
);
835 /* --------------------------------------------------------------------------------------------- */
838 extfs_run (const vfs_path_t
* vpath
)
840 struct archive
*archive
= NULL
;
841 char *p
, *q
, *archive_name
;
843 const extfs_plugin_info_t
*info
;
845 p
= extfs_get_path (vpath
, &archive
, FALSE
);
848 q
= name_quote (p
, 0);
851 archive_name
= name_quote (extfs_get_archive_name (archive
), 0);
852 info
= &g_array_index (extfs_plugins
, extfs_plugin_info_t
, archive
->fstype
);
853 cmd
= g_strconcat (info
->path
, info
->prefix
, " run ", archive_name
, " ", q
, (char *) NULL
);
854 g_free (archive_name
);
856 shell_execute (cmd
, 0);
860 /* --------------------------------------------------------------------------------------------- */
863 extfs_open (const vfs_path_t
* vpath
, int flags
, mode_t mode
)
865 struct pseudofile
*extfs_info
;
866 struct archive
*archive
= NULL
;
870 gboolean created
= FALSE
;
872 q
= extfs_get_path (vpath
, &archive
, FALSE
);
875 entry
= extfs_find_entry (archive
->root_entry
, q
, FALSE
, FALSE
);
876 if ((entry
== NULL
) && ((flags
& O_CREAT
) != 0))
878 /* Create new entry */
879 entry
= extfs_find_entry (archive
->root_entry
, q
, FALSE
, TRUE
);
880 created
= (entry
!= NULL
);
886 entry
= extfs_resolve_symlinks (entry
);
890 if (S_ISDIR (entry
->inode
->mode
))
891 ERRNOR (EISDIR
, NULL
);
893 if (entry
->inode
->local_filename
== NULL
)
895 char *local_filename
;
897 local_handle
= vfs_mkstemps (&local_filename
, "extfs", entry
->name
);
899 if (local_handle
== -1)
901 close (local_handle
);
903 if (!created
&& ((flags
& O_TRUNC
) == 0)
904 && extfs_cmd (" copyout ", archive
, entry
, local_filename
))
906 unlink (local_filename
);
907 g_free (local_filename
);
911 entry
->inode
->local_filename
= local_filename
;
914 local_handle
= open (entry
->inode
->local_filename
, NO_LINEAR (flags
), mode
);
916 if (local_handle
== -1)
918 /* file exists(may be). Need to drop O_CREAT flag and truncate file content */
919 flags
= ~O_CREAT
& (NO_LINEAR (flags
) | O_TRUNC
);
920 local_handle
= open (entry
->inode
->local_filename
, flags
, mode
);
923 if (local_handle
== -1)
926 extfs_info
= g_new (struct pseudofile
, 1);
927 extfs_info
->archive
= archive
;
928 extfs_info
->entry
= entry
;
929 extfs_info
->has_changed
= created
;
930 extfs_info
->local_handle
= local_handle
;
932 /* i.e. we had no open files and now we have one */
933 vfs_rmstamp (&vfs_extfs_ops
, (vfsid
) archive
);
938 /* --------------------------------------------------------------------------------------------- */
941 extfs_read (void *data
, char *buffer
, size_t count
)
943 struct pseudofile
*file
= (struct pseudofile
*) data
;
945 return read (file
->local_handle
, buffer
, count
);
948 /* --------------------------------------------------------------------------------------------- */
951 extfs_close (void *data
)
953 struct pseudofile
*file
;
955 file
= (struct pseudofile
*) data
;
957 close (file
->local_handle
);
959 /* Commit the file if it has changed */
960 if (file
->has_changed
)
962 struct stat file_status
;
964 if (extfs_cmd (" copyin ", file
->archive
, file
->entry
, file
->entry
->inode
->local_filename
))
967 if (stat (file
->entry
->inode
->local_filename
, &file_status
) != 0)
970 file
->entry
->inode
->size
= file_status
.st_size
;
972 file
->entry
->inode
->mtime
= time (NULL
);
975 if (--file
->archive
->fd_usage
== 0)
976 vfs_stamp_create (&vfs_extfs_ops
, file
->archive
);
984 /* --------------------------------------------------------------------------------------------- */
987 extfs_errno (struct vfs_class
*me
)
993 /* --------------------------------------------------------------------------------------------- */
996 extfs_opendir (const vfs_path_t
* vpath
)
998 struct archive
*archive
= NULL
;
1000 struct entry
*entry
;
1001 struct entry
**info
;
1003 q
= extfs_get_path (vpath
, &archive
, FALSE
);
1006 entry
= extfs_find_entry (archive
->root_entry
, q
, FALSE
, FALSE
);
1010 entry
= extfs_resolve_symlinks (entry
);
1013 if (!S_ISDIR (entry
->inode
->mode
))
1014 ERRNOR (ENOTDIR
, NULL
);
1016 info
= g_new (struct entry
*, 2);
1017 info
[0] = entry
->inode
->first_in_subdir
;
1018 info
[1] = entry
->inode
->first_in_subdir
;
1023 /* --------------------------------------------------------------------------------------------- */
1026 extfs_readdir (void *data
)
1028 static union vfs_dirent dir
;
1029 struct entry
**info
= (struct entry
**) data
;
1034 g_strlcpy (dir
.dent
.d_name
, (*info
)->name
, MC_MAXPATHLEN
);
1036 compute_namelen (&dir
.dent
);
1037 *info
= (*info
)->next_in_dir
;
1039 return (void *) &dir
;
1042 /* --------------------------------------------------------------------------------------------- */
1045 extfs_closedir (void *data
)
1051 /* --------------------------------------------------------------------------------------------- */
1054 extfs_stat_move (struct stat
*buf
, const struct inode
*inode
)
1056 buf
->st_dev
= inode
->dev
;
1057 buf
->st_ino
= inode
->inode
;
1058 buf
->st_mode
= inode
->mode
;
1059 buf
->st_nlink
= inode
->nlink
;
1060 buf
->st_uid
= inode
->uid
;
1061 buf
->st_gid
= inode
->gid
;
1062 #ifdef HAVE_STRUCT_STAT_ST_RDEV
1063 buf
->st_rdev
= inode
->rdev
;
1065 buf
->st_size
= inode
->size
;
1066 #ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
1067 buf
->st_blksize
= RECORDSIZE
;
1069 #ifdef HAVE_STRUCT_STAT_ST_BLOCKS
1070 buf
->st_blocks
= (inode
->size
+ RECORDSIZE
- 1) / RECORDSIZE
;
1072 buf
->st_atime
= inode
->atime
;
1073 buf
->st_mtime
= inode
->mtime
;
1074 buf
->st_ctime
= inode
->ctime
;
1077 /* --------------------------------------------------------------------------------------------- */
1080 extfs_internal_stat (const vfs_path_t
* vpath
, struct stat
*buf
, gboolean resolve
)
1082 struct archive
*archive
;
1084 struct entry
*entry
;
1087 q
= extfs_get_path_mangle (vpath
, &archive
, FALSE
);
1090 entry
= extfs_find_entry (archive
->root_entry
, q
, FALSE
, FALSE
);
1095 entry
= extfs_resolve_symlinks (entry
);
1099 extfs_stat_move (buf
, entry
->inode
);
1105 /* --------------------------------------------------------------------------------------------- */
1108 extfs_stat (const vfs_path_t
* vpath
, struct stat
*buf
)
1110 return extfs_internal_stat (vpath
, buf
, TRUE
);
1113 /* --------------------------------------------------------------------------------------------- */
1116 extfs_lstat (const vfs_path_t
* vpath
, struct stat
*buf
)
1118 return extfs_internal_stat (vpath
, buf
, FALSE
);
1121 /* --------------------------------------------------------------------------------------------- */
1124 extfs_fstat (void *data
, struct stat
*buf
)
1126 struct pseudofile
*file
= (struct pseudofile
*) data
;
1128 extfs_stat_move (buf
, file
->entry
->inode
);
1132 /* --------------------------------------------------------------------------------------------- */
1135 extfs_readlink (const vfs_path_t
* vpath
, char *buf
, size_t size
)
1137 struct archive
*archive
;
1140 struct entry
*entry
;
1143 q
= extfs_get_path_mangle (vpath
, &archive
, FALSE
);
1146 entry
= extfs_find_entry (archive
->root_entry
, q
, FALSE
, FALSE
);
1149 if (!S_ISLNK (entry
->inode
->mode
))
1151 vfs_path_element_t
*path_element
;
1152 path_element
= vfs_path_get_by_index (vpath
, -1);
1153 path_element
->class->verrno
= EINVAL
;
1156 len
= strlen (entry
->inode
->linkname
);
1159 /* readlink() does not append a NUL character to buf */
1161 memcpy (buf
, entry
->inode
->linkname
, result
);
1166 /* --------------------------------------------------------------------------------------------- */
1169 extfs_chown (const vfs_path_t
* vpath
, uid_t owner
, gid_t group
)
1177 /* --------------------------------------------------------------------------------------------- */
1180 extfs_chmod (const vfs_path_t
* vpath
, mode_t mode
)
1187 /* --------------------------------------------------------------------------------------------- */
1190 extfs_write (void *data
, const char *buf
, size_t nbyte
)
1192 struct pseudofile
*file
= (struct pseudofile
*) data
;
1194 file
->has_changed
= TRUE
;
1195 return write (file
->local_handle
, buf
, nbyte
);
1198 /* --------------------------------------------------------------------------------------------- */
1201 extfs_unlink (const vfs_path_t
* vpath
)
1203 struct archive
*archive
;
1205 struct entry
*entry
;
1208 q
= extfs_get_path_mangle (vpath
, &archive
, FALSE
);
1211 entry
= extfs_find_entry (archive
->root_entry
, q
, FALSE
, FALSE
);
1214 entry
= extfs_resolve_symlinks (entry
);
1217 if (S_ISDIR (entry
->inode
->mode
))
1219 vfs_path_element_t
*path_element
= vfs_path_get_by_index (vpath
, -1);
1220 path_element
->class->verrno
= EISDIR
;
1223 if (extfs_cmd (" rm ", archive
, entry
, ""))
1228 extfs_remove_entry (entry
);
1234 /* --------------------------------------------------------------------------------------------- */
1237 extfs_mkdir (const vfs_path_t
* vpath
, mode_t mode
)
1239 struct archive
*archive
;
1241 struct entry
*entry
;
1243 vfs_path_element_t
*path_element
= vfs_path_get_by_index (vpath
, -1);
1247 q
= extfs_get_path_mangle (vpath
, &archive
, FALSE
);
1250 entry
= extfs_find_entry (archive
->root_entry
, q
, FALSE
, FALSE
);
1253 path_element
->class->verrno
= EEXIST
;
1256 entry
= extfs_find_entry (archive
->root_entry
, q
, TRUE
, FALSE
);
1259 entry
= extfs_resolve_symlinks (entry
);
1262 if (!S_ISDIR (entry
->inode
->mode
))
1264 path_element
->class->verrno
= ENOTDIR
;
1268 if (extfs_cmd (" mkdir ", archive
, entry
, ""))
1271 extfs_remove_entry (entry
);
1279 /* --------------------------------------------------------------------------------------------- */
1282 extfs_rmdir (const vfs_path_t
* vpath
)
1284 struct archive
*archive
;
1286 struct entry
*entry
;
1289 q
= extfs_get_path_mangle (vpath
, &archive
, FALSE
);
1292 entry
= extfs_find_entry (archive
->root_entry
, q
, FALSE
, FALSE
);
1295 entry
= extfs_resolve_symlinks (entry
);
1298 if (!S_ISDIR (entry
->inode
->mode
))
1300 vfs_path_element_t
*path_element
= vfs_path_get_by_index (vpath
, -1);
1301 path_element
->class->verrno
= ENOTDIR
;
1305 if (extfs_cmd (" rmdir ", archive
, entry
, ""))
1310 extfs_remove_entry (entry
);
1316 /* --------------------------------------------------------------------------------------------- */
1319 extfs_chdir (const vfs_path_t
* vpath
)
1321 struct archive
*archive
= NULL
;
1323 struct entry
*entry
;
1326 q
= extfs_get_path (vpath
, &archive
, FALSE
);
1329 entry
= extfs_find_entry (archive
->root_entry
, q
, FALSE
, FALSE
);
1333 entry
= extfs_resolve_symlinks (entry
);
1334 if ((entry
== NULL
) || (!S_ISDIR (entry
->inode
->mode
)))
1340 /* --------------------------------------------------------------------------------------------- */
1343 extfs_lseek (void *data
, off_t offset
, int whence
)
1345 struct pseudofile
*file
= (struct pseudofile
*) data
;
1347 return lseek (file
->local_handle
, offset
, whence
);
1350 /* --------------------------------------------------------------------------------------------- */
1353 extfs_getid (const vfs_path_t
* vpath
)
1355 struct archive
*archive
= NULL
;
1358 p
= extfs_get_path (vpath
, &archive
, TRUE
);
1362 return (vfsid
) archive
;
1365 /* --------------------------------------------------------------------------------------------- */
1368 extfs_nothingisopen (vfsid id
)
1370 return (((struct archive
*) id
)->fd_usage
<= 0);
1373 /* --------------------------------------------------------------------------------------------- */
1376 extfs_remove_entry (struct entry
*e
)
1378 int i
= --e
->inode
->nlink
;
1379 struct entry
*pe
, *ent
, *prev
;
1381 if (S_ISDIR (e
->inode
->mode
) && e
->inode
->first_in_subdir
!= NULL
)
1383 struct entry
*f
= e
->inode
->first_in_subdir
;
1384 e
->inode
->first_in_subdir
= NULL
;
1385 extfs_remove_entry (f
);
1388 if (e
== pe
->inode
->first_in_subdir
)
1389 pe
->inode
->first_in_subdir
= e
->next_in_dir
;
1392 for (ent
= pe
->inode
->first_in_subdir
; ent
&& ent
->next_in_dir
; ent
= ent
->next_in_dir
)
1393 if (e
== ent
->next_in_dir
)
1399 prev
->next_in_dir
= e
->next_in_dir
;
1400 if (e
== pe
->inode
->last_in_subdir
)
1401 pe
->inode
->last_in_subdir
= prev
;
1405 if (e
->inode
->local_filename
!= NULL
)
1407 unlink (e
->inode
->local_filename
);
1408 g_free (e
->inode
->local_filename
);
1410 g_free (e
->inode
->linkname
);
1418 /* --------------------------------------------------------------------------------------------- */
1421 extfs_free_entry (struct entry
*e
)
1423 int i
= --e
->inode
->nlink
;
1425 if (S_ISDIR (e
->inode
->mode
) && e
->inode
->first_in_subdir
!= NULL
)
1427 struct entry
*f
= e
->inode
->first_in_subdir
;
1429 e
->inode
->first_in_subdir
= NULL
;
1430 extfs_free_entry (f
);
1434 if (e
->inode
->local_filename
!= NULL
)
1436 unlink (e
->inode
->local_filename
);
1437 g_free (e
->inode
->local_filename
);
1439 g_free (e
->inode
->linkname
);
1442 if (e
->next_in_dir
!= NULL
)
1443 extfs_free_entry (e
->next_in_dir
);
1448 /* --------------------------------------------------------------------------------------------- */
1451 extfs_free (vfsid id
)
1453 struct archive
*archive
= (struct archive
*) id
;
1455 if (archive
== first_archive
)
1457 first_archive
= archive
->next
;
1461 struct archive
*parc
;
1462 for (parc
= first_archive
; parc
!= NULL
; parc
= parc
->next
)
1463 if (parc
->next
== archive
)
1465 parc
->next
= archive
->next
;
1469 extfs_free_archive (archive
);
1472 /* --------------------------------------------------------------------------------------------- */
1475 extfs_getlocalcopy (const vfs_path_t
* vpath
)
1477 struct pseudofile
*fp
;
1480 fp
= (struct pseudofile
*) extfs_open (vpath
, O_RDONLY
, 0);
1483 if (fp
->entry
->inode
->local_filename
== NULL
)
1485 extfs_close ((void *) fp
);
1488 p
= g_strdup (fp
->entry
->inode
->local_filename
);
1489 fp
->archive
->fd_usage
++;
1490 extfs_close ((void *) fp
);
1494 /* --------------------------------------------------------------------------------------------- */
1497 extfs_ungetlocalcopy (const vfs_path_t
* vpath
, const char *local
, int has_changed
)
1499 struct pseudofile
*fp
;
1501 fp
= (struct pseudofile
*) extfs_open (vpath
, O_RDONLY
, 0);
1505 if (strcmp (fp
->entry
->inode
->local_filename
, local
) == 0)
1507 fp
->archive
->fd_usage
--;
1508 if (has_changed
!= 0)
1509 fp
->has_changed
= TRUE
;
1510 extfs_close ((void *) fp
);
1515 /* Should not happen */
1516 extfs_close ((void *) fp
);
1521 /* --------------------------------------------------------------------------------------------- */
1524 extfs_get_plugins (const char *where
, gboolean silent
)
1528 const char *filename
;
1530 dirname
= g_build_path (PATH_SEP_STR
, where
, MC_EXTFS_DIR
, (char *) NULL
);
1531 dir
= g_dir_open (dirname
, 0, NULL
);
1533 /* We may not use vfs_die() message or message or similar,
1534 * UI is not initialized at this time and message would not
1535 * appear on screen. */
1539 fprintf (stderr
, _("Warning: cannot open %s directory\n"), dirname
);
1544 if (extfs_plugins
== NULL
)
1545 extfs_plugins
= g_array_sized_new (FALSE
, TRUE
, sizeof (extfs_plugin_info_t
), 32);
1547 while ((filename
= g_dir_read_name (dir
)) != NULL
)
1549 char fullname
[MC_MAXPATHLEN
];
1552 g_snprintf (fullname
, sizeof (fullname
), "%s" PATH_SEP_STR
"%s", dirname
, filename
);
1554 if ((stat (fullname
, &s
) == 0)
1555 && S_ISREG (s
.st_mode
) && !S_ISDIR (s
.st_mode
)
1556 && (((s
.st_mode
& S_IXOTH
) != 0) ||
1557 ((s
.st_mode
& S_IXUSR
) != 0) || ((s
.st_mode
& S_IXGRP
) != 0)))
1561 f
= open (fullname
, O_RDONLY
);
1566 extfs_plugin_info_t info
;
1567 gboolean found
= FALSE
;
1571 /* Handle those with a trailing '+', those flag that the
1572 * file system does not require an archive to work
1574 len
= strlen (filename
);
1575 info
.need_archive
= (filename
[len
- 1] != '+');
1576 info
.path
= g_strconcat (dirname
, PATH_SEP_STR
, (char *) NULL
);
1577 info
.prefix
= g_strdup (filename
);
1579 /* prepare to compare file names without trailing '+' */
1580 if (!info
.need_archive
)
1581 info
.prefix
[len
- 1] = '\0';
1583 /* don't overload already found plugin */
1584 for (i
= 0; i
< extfs_plugins
->len
; i
++)
1586 extfs_plugin_info_t
*p
;
1588 p
= &g_array_index (extfs_plugins
, extfs_plugin_info_t
, i
);
1590 /* 2 files with same names cannot be in a directory */
1591 if ((strcmp (info
.path
, p
->path
) != 0)
1592 && (strcmp (info
.prefix
, p
->prefix
) == 0))
1602 g_free (info
.prefix
);
1606 /* restore file name */
1607 if (!info
.need_archive
)
1608 info
.prefix
[len
- 1] = '+';
1609 g_array_append_val (extfs_plugins
, info
);
1621 /* --------------------------------------------------------------------------------------------- */
1624 extfs_init (struct vfs_class
*me
)
1630 /* 1st: scan user directory */
1631 d1
= extfs_get_plugins (mc_config_get_data_path (), TRUE
); /* silent about user dir */
1632 /* 2nd: scan system dir */
1633 d2
= extfs_get_plugins (LIBEXECDIR
, d1
);
1635 return (d1
|| d2
? 1 : 0);
1638 /* --------------------------------------------------------------------------------------------- */
1641 extfs_done (struct vfs_class
*me
)
1648 for (ar
= first_archive
; ar
!= NULL
;)
1650 extfs_free ((vfsid
) ar
);
1654 for (i
= 0; i
< extfs_plugins
->len
; i
++)
1656 extfs_plugin_info_t
*info
;
1658 info
= &g_array_index (extfs_plugins
, extfs_plugin_info_t
, i
);
1659 g_free (info
->path
);
1660 g_free (info
->prefix
);
1663 if (extfs_plugins
!= NULL
)
1664 g_array_free (extfs_plugins
, TRUE
);
1667 /* --------------------------------------------------------------------------------------------- */
1670 extfs_setctl (const vfs_path_t
* vpath
, int ctlop
, void *arg
)
1674 if (ctlop
== VFS_SETCTL_RUN
)
1682 /* --------------------------------------------------------------------------------------------- */
1683 /*** public functions ****************************************************************************/
1684 /* --------------------------------------------------------------------------------------------- */
1689 vfs_extfs_ops
.name
= "extfs";
1690 vfs_extfs_ops
.init
= extfs_init
;
1691 vfs_extfs_ops
.done
= extfs_done
;
1692 vfs_extfs_ops
.fill_names
= extfs_fill_names
;
1693 vfs_extfs_ops
.which
= extfs_which
;
1694 vfs_extfs_ops
.open
= extfs_open
;
1695 vfs_extfs_ops
.close
= extfs_close
;
1696 vfs_extfs_ops
.read
= extfs_read
;
1697 vfs_extfs_ops
.write
= extfs_write
;
1698 vfs_extfs_ops
.opendir
= extfs_opendir
;
1699 vfs_extfs_ops
.readdir
= extfs_readdir
;
1700 vfs_extfs_ops
.closedir
= extfs_closedir
;
1701 vfs_extfs_ops
.stat
= extfs_stat
;
1702 vfs_extfs_ops
.lstat
= extfs_lstat
;
1703 vfs_extfs_ops
.fstat
= extfs_fstat
;
1704 vfs_extfs_ops
.chmod
= extfs_chmod
;
1705 vfs_extfs_ops
.chown
= extfs_chown
;
1706 vfs_extfs_ops
.readlink
= extfs_readlink
;
1707 vfs_extfs_ops
.unlink
= extfs_unlink
;
1708 vfs_extfs_ops
.chdir
= extfs_chdir
;
1709 vfs_extfs_ops
.ferrno
= extfs_errno
;
1710 vfs_extfs_ops
.lseek
= extfs_lseek
;
1711 vfs_extfs_ops
.getid
= extfs_getid
;
1712 vfs_extfs_ops
.nothingisopen
= extfs_nothingisopen
;
1713 vfs_extfs_ops
.free
= extfs_free
;
1714 vfs_extfs_ops
.getlocalcopy
= extfs_getlocalcopy
;
1715 vfs_extfs_ops
.ungetlocalcopy
= extfs_ungetlocalcopy
;
1716 vfs_extfs_ops
.mkdir
= extfs_mkdir
;
1717 vfs_extfs_ops
.rmdir
= extfs_rmdir
;
1718 vfs_extfs_ops
.setctl
= extfs_setctl
;
1719 vfs_register_class (&vfs_extfs_ops
);
1722 /* --------------------------------------------------------------------------------------------- */