2 Virtual File System: External file system.
4 Copyright (C) 1995, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
6 The Free Software Foundation, Inc.
11 Andrew T. Veliath, 1999
13 This file is part of the Midnight Commander.
15 The Midnight Commander is free software: you can redistribute it
16 and/or modify it under the terms of the GNU General Public License as
17 published by the Free Software Foundation, either version 3 of the License,
18 or (at your option) any later version.
20 The Midnight Commander is distributed in the hope that it will be useful,
21 but WITHOUT ANY WARRANTY; without even the implied warranty of
22 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 GNU General Public License for more details.
25 You should have received a copy of the GNU General Public License
26 along with this program. If not, see <http://www.gnu.org/licenses/>.
31 * \brief Source: Virtual File System: External file system
32 * \author Jakub Jelinek
33 * \author Pavel Machek
34 * \author Andrew T. Veliath
35 * \date 1995, 1998, 1999
38 /* Namespace: init_extfs */
46 #include <sys/types.h>
55 #include "lib/global.h"
56 #include "lib/fileloc.h"
57 #include "lib/mcconfig.h"
59 #include "lib/widget.h" /* message() */
61 #include "src/main.h" /* shell */
62 #include "src/execute.h" /* For shell_execute */
64 #include "lib/vfs/vfs.h"
65 #include "lib/vfs/utilvfs.h"
66 #include "lib/vfs/gc.h" /* vfs_rmstamp */
70 /*** global variables ****************************************************************************/
72 GArray
*extfs_plugins
= NULL
;
74 /*** file scope macro definitions ****************************************************************/
77 #define ERRNOR(x,y) do { my_errno = x; return y; } while(0)
79 #define RECORDSIZE 512
81 /*** file scope type declarations ****************************************************************/
86 struct entry
*first_in_subdir
; /* only used if this is a directory */
87 struct entry
*last_in_subdir
;
88 ino_t inode
; /* This is inode # */
89 dev_t dev
; /* This is an internal identification of the extfs archive */
90 struct archive
*archive
; /* And this is an archive structure */
100 char *local_filename
;
105 struct entry
*next_in_dir
;
113 struct archive
*archive
;
114 gboolean has_changed
;
124 struct stat local_stat
;
128 struct entry
*root_entry
;
129 struct archive
*next
;
136 gboolean need_archive
;
137 } extfs_plugin_info_t
;
139 /*** file scope variables ************************************************************************/
141 static gboolean errloop
;
142 static gboolean notadir
;
144 static struct vfs_class vfs_extfs_ops
;
145 static struct archive
*first_archive
= NULL
;
146 static int my_errno
= 0;
148 /*** file scope functions ************************************************************************/
149 /* --------------------------------------------------------------------------------------------- */
151 static void extfs_remove_entry (struct entry
*e
);
152 static void extfs_free (vfsid id
);
153 static void extfs_free_entry (struct entry
*e
);
154 static struct entry
*extfs_resolve_symlinks_int (struct entry
*entry
, GSList
* list
);
156 /* --------------------------------------------------------------------------------------------- */
159 extfs_make_dots (struct entry
*ent
)
161 struct entry
*entry
= g_new (struct entry
, 1);
162 struct entry
*parentry
= ent
->dir
;
163 struct inode
*inode
= ent
->inode
, *parent
;
165 parent
= (parentry
!= NULL
) ? parentry
->inode
: NULL
;
166 entry
->name
= g_strdup (".");
167 entry
->inode
= inode
;
169 inode
->local_filename
= NULL
;
170 inode
->first_in_subdir
= entry
;
173 entry
->next_in_dir
= g_new (struct entry
, 1);
174 entry
= entry
->next_in_dir
;
175 entry
->name
= g_strdup ("..");
176 inode
->last_in_subdir
= entry
;
177 entry
->next_in_dir
= NULL
;
180 entry
->inode
= parent
;
181 entry
->dir
= parentry
;
186 entry
->inode
= inode
;
192 /* --------------------------------------------------------------------------------------------- */
194 static struct entry
*
195 extfs_generate_entry (struct archive
*archive
,
196 const char *name
, struct entry
*parentry
, mode_t mode
)
199 struct inode
*inode
, *parent
;
202 parent
= (parentry
!= NULL
) ? parentry
->inode
: NULL
;
203 entry
= g_new (struct entry
, 1);
205 entry
->name
= g_strdup (name
);
206 entry
->next_in_dir
= NULL
;
207 entry
->dir
= parentry
;
210 parent
->last_in_subdir
->next_in_dir
= entry
;
211 parent
->last_in_subdir
= entry
;
213 inode
= g_new (struct inode
, 1);
214 entry
->inode
= inode
;
215 inode
->local_filename
= NULL
;
216 inode
->linkname
= NULL
;
217 inode
->last_in_subdir
= NULL
;
218 inode
->inode
= (archive
->inode_counter
)++;
219 inode
->dev
= archive
->rdev
;
220 inode
->archive
= archive
;
221 myumask
= umask (022);
223 inode
->mode
= mode
& ~myumask
;
226 inode
->uid
= getuid ();
227 inode
->gid
= getgid ();
229 inode
->mtime
= time (NULL
);
230 inode
->atime
= inode
->mtime
;
231 inode
->ctime
= inode
->mtime
;
234 extfs_make_dots (entry
);
238 /* --------------------------------------------------------------------------------------------- */
240 static struct entry
*
241 extfs_find_entry_int (struct entry
*dir
, char *name
, GSList
* list
,
242 gboolean make_dirs
, gboolean make_file
)
244 struct entry
*pent
, *pdir
;
245 char *p
, *q
, *name_end
;
248 if (g_path_is_absolute (name
))
250 /* Handle absolute paths */
251 name
= (char *) g_path_skip_root (name
);
252 dir
= dir
->inode
->archive
->root_entry
;
257 name_end
= name
+ strlen (name
);
259 q
= strchr (p
, PATH_SEP
);
261 q
= strchr (p
, '\0');
263 while ((pent
!= NULL
) && (c
!= '\0') && (*p
!= '\0'))
268 if (strcmp (p
, ".") != 0)
270 if (strcmp (p
, "..") == 0)
274 pent
= extfs_resolve_symlinks_int (pent
, list
);
280 if (!S_ISDIR (pent
->inode
->mode
))
288 for (pent
= pent
->inode
->first_in_subdir
; pent
!= NULL
; pent
= pent
->next_in_dir
)
289 /* Hack: I keep the original semanthic unless
290 q+1 would break in the strchr */
291 if (strcmp (pent
->name
, p
) == 0)
293 if (q
+ 1 > name_end
)
296 notadir
= !S_ISDIR (pent
->inode
->mode
);
302 /* When we load archive, we create automagically
303 * non-existant directories
305 if (pent
== NULL
&& make_dirs
)
306 pent
= extfs_generate_entry (dir
->inode
->archive
, p
, pdir
, S_IFDIR
| 0777);
307 if (pent
== NULL
&& make_file
)
308 pent
= extfs_generate_entry (dir
->inode
->archive
, p
, pdir
, S_IFREG
| 0666);
314 q
= strchr (p
, PATH_SEP
);
316 q
= strchr (p
, '\0');
323 /* --------------------------------------------------------------------------------------------- */
325 static struct entry
*
326 extfs_find_entry (struct entry
*dir
, char *name
, gboolean make_dirs
, gboolean make_file
)
333 res
= extfs_find_entry_int (dir
, name
, NULL
, make_dirs
, make_file
);
344 /* --------------------------------------------------------------------------------------------- */
347 extfs_fill_names (struct vfs_class
*me
, fill_names_f func
)
349 struct archive
*a
= first_archive
;
355 extfs_plugin_info_t
*info
;
358 info
= &g_array_index (extfs_plugins
, extfs_plugin_info_t
, a
->fstype
);
360 g_strconcat (a
->name
? a
->name
: "", "/", info
->prefix
, VFS_PATH_URL_DELIMITER
,
368 /* --------------------------------------------------------------------------------------------- */
371 extfs_free_archive (struct archive
*archive
)
373 extfs_free_entry (archive
->root_entry
);
374 if (archive
->local_name
!= NULL
)
377 vfs_path_t
*local_name_vpath
, *name_vpath
;
379 local_name_vpath
= vfs_path_from_str (archive
->local_name
);
380 name_vpath
= vfs_path_from_str (archive
->local_name
);
381 mc_stat (local_name_vpath
, &my
);
382 mc_ungetlocalcopy (name_vpath
, local_name_vpath
,
383 archive
->local_stat
.st_mtime
!= my
.st_mtime
);
384 vfs_path_free (local_name_vpath
);
385 vfs_path_free (name_vpath
);
386 g_free (archive
->local_name
);
388 g_free (archive
->name
);
392 /* --------------------------------------------------------------------------------------------- */
395 extfs_open_archive (int fstype
, const char *name
, struct archive
**pparc
)
397 const extfs_plugin_info_t
*info
;
398 static dev_t archive_counter
= 0;
403 struct archive
*current_archive
;
404 struct entry
*root_entry
;
406 vfs_path_t
*local_name_vpath
= NULL
;
407 vfs_path_t
*name_vpath
;
409 name_vpath
= vfs_path_from_str (name
);
410 info
= &g_array_index (extfs_plugins
, extfs_plugin_info_t
, fstype
);
412 if (info
->need_archive
)
414 if (mc_stat (name_vpath
, &mystat
) == -1)
417 if (!vfs_file_is_local (name_vpath
))
419 local_name_vpath
= mc_getlocalcopy (name_vpath
);
420 if (local_name_vpath
== NULL
)
424 tmp
= name_quote ( vfs_path_get_last_path_str (name_vpath
), 0);
427 cmd
= g_strconcat (info
->path
, info
->prefix
, " list ",
428 vfs_path_get_last_path_str (local_name_vpath
) != NULL
?
429 vfs_path_get_last_path_str (local_name_vpath
) : tmp
, (char *) NULL
);
433 result
= popen (cmd
, "r");
437 close_error_pipe (D_ERROR
, NULL
);
438 if (local_name_vpath
!= NULL
)
440 mc_ungetlocalcopy (name_vpath
, local_name_vpath
, FALSE
);
441 vfs_path_free (local_name_vpath
);
447 setvbuf (result
, NULL
, _IONBF
, 0);
450 current_archive
= g_new (struct archive
, 1);
451 current_archive
->fstype
= fstype
;
452 current_archive
->name
= (name
!= NULL
) ? g_strdup (name
) : NULL
;
453 current_archive
->local_name
= g_strdup (vfs_path_get_last_path_str (local_name_vpath
));
455 if (local_name_vpath
!= NULL
)
457 mc_stat (local_name_vpath
, ¤t_archive
->local_stat
);
458 vfs_path_free (local_name_vpath
);
460 current_archive
->inode_counter
= 0;
461 current_archive
->fd_usage
= 0;
462 current_archive
->rdev
= archive_counter
++;
463 current_archive
->next
= first_archive
;
464 first_archive
= current_archive
;
465 mode
= mystat
.st_mode
& 07777;
473 root_entry
= extfs_generate_entry (current_archive
, PATH_SEP_STR
, NULL
, mode
);
474 root_entry
->inode
->uid
= mystat
.st_uid
;
475 root_entry
->inode
->gid
= mystat
.st_gid
;
476 root_entry
->inode
->atime
= mystat
.st_atime
;
477 root_entry
->inode
->ctime
= mystat
.st_ctime
;
478 root_entry
->inode
->mtime
= mystat
.st_mtime
;
479 current_archive
->root_entry
= root_entry
;
481 *pparc
= current_archive
;
484 vfs_path_free (name_vpath
);
488 /* --------------------------------------------------------------------------------------------- */
490 * Main loop for reading an archive.
491 * Return 0 on success, -1 on error.
495 extfs_read_archive (int fstype
, const char *name
, struct archive
**pparc
)
498 const extfs_plugin_info_t
*info
;
500 struct archive
*current_archive
;
501 char *current_file_name
, *current_link_name
;
503 info
= &g_array_index (extfs_plugins
, extfs_plugin_info_t
, fstype
);
505 extfsd
= extfs_open_archive (fstype
, name
, ¤t_archive
);
509 message (D_ERROR
, MSG_ERROR
, _("Cannot open %s archive\n%s"), info
->prefix
, name
);
513 buffer
= g_malloc (BUF_4K
);
514 while (fgets (buffer
, BUF_4K
, extfsd
) != NULL
)
518 current_link_name
= NULL
;
519 if (vfs_parse_ls_lga (buffer
, &hstat
, ¤t_file_name
, ¤t_link_name
, NULL
))
521 struct entry
*entry
, *pent
;
523 char *p
, *q
, *cfn
= current_file_name
;
527 if (*cfn
== PATH_SEP
)
529 p
= strchr (cfn
, '\0');
530 if (p
!= cfn
&& *(p
- 1) == PATH_SEP
)
532 p
= strrchr (cfn
, PATH_SEP
);
536 q
= strchr (cfn
, '\0');
543 if (S_ISDIR (hstat
.st_mode
) && (strcmp (p
, ".") == 0 || strcmp (p
, "..") == 0))
544 goto read_extfs_continue
;
545 pent
= extfs_find_entry (current_archive
->root_entry
, q
, TRUE
, FALSE
);
548 /* FIXME: Should clean everything one day */
551 close_error_pipe (D_ERROR
, _("Inconsistent extfs archive"));
554 entry
= g_new (struct entry
, 1);
555 entry
->name
= g_strdup (p
);
556 entry
->next_in_dir
= NULL
;
558 if (pent
->inode
->last_in_subdir
)
560 pent
->inode
->last_in_subdir
->next_in_dir
= entry
;
561 pent
->inode
->last_in_subdir
= entry
;
563 if (!S_ISLNK (hstat
.st_mode
) && (current_link_name
!= NULL
))
565 pent
= extfs_find_entry (current_archive
->root_entry
,
566 current_link_name
, FALSE
, FALSE
);
569 /* FIXME: Should clean everything one day */
572 close_error_pipe (D_ERROR
, _("Inconsistent extfs archive"));
576 entry
->inode
= pent
->inode
;
577 pent
->inode
->nlink
++;
581 inode
= g_new (struct inode
, 1);
582 entry
->inode
= inode
;
583 inode
->local_filename
= NULL
;
584 inode
->inode
= (current_archive
->inode_counter
)++;
586 inode
->dev
= current_archive
->rdev
;
587 inode
->archive
= current_archive
;
588 inode
->mode
= hstat
.st_mode
;
589 #ifdef HAVE_STRUCT_STAT_ST_RDEV
590 inode
->rdev
= hstat
.st_rdev
;
594 inode
->uid
= hstat
.st_uid
;
595 inode
->gid
= hstat
.st_gid
;
596 inode
->size
= hstat
.st_size
;
597 inode
->mtime
= hstat
.st_mtime
;
598 inode
->atime
= hstat
.st_atime
;
599 inode
->ctime
= hstat
.st_ctime
;
600 inode
->first_in_subdir
= NULL
;
601 inode
->last_in_subdir
= NULL
;
602 if (current_link_name
!= NULL
&& S_ISLNK (hstat
.st_mode
))
604 inode
->linkname
= current_link_name
;
605 current_link_name
= NULL
;
609 if (S_ISLNK (hstat
.st_mode
))
610 inode
->mode
&= ~S_IFLNK
; /* You *DON'T* want to do this always */
611 inode
->linkname
= NULL
;
613 if (S_ISDIR (hstat
.st_mode
))
614 extfs_make_dots (entry
);
618 g_free (current_file_name
);
619 g_free (current_link_name
);
624 /* Check if extfs 'list' returned 0 */
625 if (pclose (extfsd
) != 0)
627 extfs_free (current_archive
);
628 close_error_pipe (D_ERROR
, _("Inconsistent extfs archive"));
632 close_error_pipe (D_ERROR
, NULL
);
633 *pparc
= current_archive
;
637 /* --------------------------------------------------------------------------------------------- */
640 extfs_which (struct vfs_class
*me
, const char *path
)
647 path_len
= strlen (path
);
649 for (i
= 0; i
< extfs_plugins
->len
; i
++)
651 extfs_plugin_info_t
*info
;
653 info
= &g_array_index (extfs_plugins
, extfs_plugin_info_t
, i
);
655 if ((strncmp (path
, info
->prefix
, path_len
) == 0)
656 && ((info
->prefix
[path_len
] == '\0') || (info
->prefix
[path_len
] == '+')))
662 /* --------------------------------------------------------------------------------------------- */
664 * Dissect the path and create corresponding superblock. Note that inname
665 * can be changed and the result may point inside the original string.
669 extfs_get_path_mangle (const vfs_path_t
* vpath
, struct archive
**archive
, gboolean do_not_open
)
673 struct archive
*parc
;
675 const vfs_path_element_t
*path_element
;
677 path_element
= vfs_path_get_by_index (vpath
, -1);
679 archive_name
= vfs_path_to_str_elements_count (vpath
, -1);
681 fstype
= extfs_which (path_element
->class, path_element
->vfs_prefix
);
685 g_free (archive_name
);
690 * All filesystems should have some local archive, at least
691 * it can be PATH_SEP ('/').
693 for (parc
= first_archive
; parc
!= NULL
; parc
= parc
->next
)
694 if (parc
->name
!= NULL
)
696 if (strcmp (parc
->name
, archive_name
) == 0)
698 vfs_stamp (&vfs_extfs_ops
, (vfsid
) parc
);
703 result
= do_not_open
? -1 : extfs_read_archive (fstype
, archive_name
, &parc
);
706 path_element
->class->verrno
= EIO
;
707 g_free (archive_name
);
713 g_free (archive_name
);
714 return path_element
->path
;
717 /* --------------------------------------------------------------------------------------------- */
719 * Dissect the path and create corresponding superblock.
720 * The result should be freed.
724 extfs_get_path (const vfs_path_t
* vpath
, struct archive
**archive
, gboolean do_not_open
)
726 return g_strdup (extfs_get_path_mangle (vpath
, archive
, do_not_open
));
729 /* --------------------------------------------------------------------------------------------- */
730 /* Return allocated path (without leading slash) inside the archive */
733 extfs_get_path_from_entry (struct entry
*entry
)
737 localpath
= g_string_new ("");
739 while (entry
->dir
!= NULL
)
741 g_string_prepend (localpath
, entry
->name
);
742 if (entry
->dir
->dir
!= NULL
)
743 g_string_prepend_c (localpath
, PATH_SEP
);
747 return g_string_free (localpath
, FALSE
);
750 /* --------------------------------------------------------------------------------------------- */
752 static struct entry
*
753 extfs_resolve_symlinks_int (struct entry
*entry
, GSList
* list
)
755 struct entry
*pent
= NULL
;
757 if (!S_ISLNK (entry
->inode
->mode
))
760 if (g_slist_find (list
, entry
) != NULL
)
762 /* Here we protect us against symlink looping */
769 looping
= g_slist_prepend (list
, entry
);
770 pent
= extfs_find_entry_int (entry
->dir
, entry
->inode
->linkname
, looping
, FALSE
, FALSE
);
771 looping
= g_slist_delete_link (looping
, looping
);
780 /* --------------------------------------------------------------------------------------------- */
782 static struct entry
*
783 extfs_resolve_symlinks (struct entry
*entry
)
789 res
= extfs_resolve_symlinks_int (entry
, NULL
);
800 /* --------------------------------------------------------------------------------------------- */
803 extfs_get_archive_name (struct archive
*archive
)
805 const char *archive_name
;
807 if (archive
->local_name
)
808 archive_name
= archive
->local_name
;
810 archive_name
= archive
->name
;
812 if (!archive_name
|| !*archive_name
)
813 return g_strdup ("no_archive_name");
818 const vfs_path_element_t
*path_element
;
820 vpath
= vfs_path_from_str (archive_name
);
821 path_element
= vfs_path_get_by_index (vpath
, -1);
822 ret_str
= g_strdup (path_element
->path
);
823 vfs_path_free (vpath
);
828 /* --------------------------------------------------------------------------------------------- */
829 /** Don't pass localname as NULL */
832 extfs_cmd (const char *str_extfs_cmd
, struct archive
*archive
,
833 struct entry
*entry
, const char *localname
)
837 char *quoted_localname
;
838 char *archive_name
, *quoted_archive_name
;
839 const extfs_plugin_info_t
*info
;
843 file
= extfs_get_path_from_entry (entry
);
844 quoted_file
= name_quote (file
, 0);
847 archive_name
= extfs_get_archive_name (archive
);
848 quoted_archive_name
= name_quote (archive_name
, 0);
849 g_free (archive_name
);
850 quoted_localname
= name_quote (localname
, 0);
851 info
= &g_array_index (extfs_plugins
, extfs_plugin_info_t
, archive
->fstype
);
852 cmd
= g_strconcat (info
->path
, info
->prefix
, str_extfs_cmd
,
853 quoted_archive_name
, " ", quoted_file
, " ", quoted_localname
, (char *) NULL
);
854 g_free (quoted_file
);
855 g_free (quoted_localname
);
856 g_free (quoted_archive_name
);
859 retval
= my_system (EXECUTE_AS_SHELL
, shell
, cmd
);
861 close_error_pipe (D_ERROR
, NULL
);
865 /* --------------------------------------------------------------------------------------------- */
868 extfs_run (const vfs_path_t
* vpath
)
870 struct archive
*archive
= NULL
;
871 char *p
, *q
, *archive_name
, *quoted_archive_name
;
873 const extfs_plugin_info_t
*info
;
875 p
= extfs_get_path (vpath
, &archive
, FALSE
);
878 q
= name_quote (p
, 0);
881 archive_name
= extfs_get_archive_name (archive
);
882 quoted_archive_name
= name_quote (archive_name
, 0);
883 g_free (archive_name
);
884 info
= &g_array_index (extfs_plugins
, extfs_plugin_info_t
, archive
->fstype
);
886 g_strconcat (info
->path
, info
->prefix
, " run ", quoted_archive_name
, " ", q
, (char *) NULL
);
887 g_free (quoted_archive_name
);
889 shell_execute (cmd
, 0);
893 /* --------------------------------------------------------------------------------------------- */
896 extfs_open (const vfs_path_t
* vpath
, int flags
, mode_t mode
)
898 struct pseudofile
*extfs_info
;
899 struct archive
*archive
= NULL
;
903 gboolean created
= FALSE
;
905 q
= extfs_get_path (vpath
, &archive
, FALSE
);
908 entry
= extfs_find_entry (archive
->root_entry
, q
, FALSE
, FALSE
);
909 if ((entry
== NULL
) && ((flags
& O_CREAT
) != 0))
911 /* Create new entry */
912 entry
= extfs_find_entry (archive
->root_entry
, q
, FALSE
, TRUE
);
913 created
= (entry
!= NULL
);
919 entry
= extfs_resolve_symlinks (entry
);
923 if (S_ISDIR (entry
->inode
->mode
))
924 ERRNOR (EISDIR
, NULL
);
926 if (entry
->inode
->local_filename
== NULL
)
928 vfs_path_t
*local_filename_vpath
;
929 const char *local_filename
;
931 local_handle
= vfs_mkstemps (&local_filename_vpath
, "extfs", entry
->name
);
933 if (local_handle
== -1)
935 close (local_handle
);
936 local_filename
= vfs_path_get_by_index (local_filename_vpath
, -1)->path
;
938 if (!created
&& ((flags
& O_TRUNC
) == 0)
939 && extfs_cmd (" copyout ", archive
, entry
, local_filename
))
941 unlink (local_filename
);
942 vfs_path_free (local_filename_vpath
);
946 entry
->inode
->local_filename
= g_strdup (local_filename
);
947 vfs_path_free (local_filename_vpath
);
950 local_handle
= open (entry
->inode
->local_filename
, NO_LINEAR (flags
), mode
);
952 if (local_handle
== -1)
954 /* file exists(may be). Need to drop O_CREAT flag and truncate file content */
955 flags
= ~O_CREAT
& (NO_LINEAR (flags
) | O_TRUNC
);
956 local_handle
= open (entry
->inode
->local_filename
, flags
, mode
);
959 if (local_handle
== -1)
962 extfs_info
= g_new (struct pseudofile
, 1);
963 extfs_info
->archive
= archive
;
964 extfs_info
->entry
= entry
;
965 extfs_info
->has_changed
= created
;
966 extfs_info
->local_handle
= local_handle
;
968 /* i.e. we had no open files and now we have one */
969 vfs_rmstamp (&vfs_extfs_ops
, (vfsid
) archive
);
974 /* --------------------------------------------------------------------------------------------- */
977 extfs_read (void *data
, char *buffer
, size_t count
)
979 struct pseudofile
*file
= (struct pseudofile
*) data
;
981 return read (file
->local_handle
, buffer
, count
);
984 /* --------------------------------------------------------------------------------------------- */
987 extfs_close (void *data
)
989 struct pseudofile
*file
;
991 file
= (struct pseudofile
*) data
;
993 close (file
->local_handle
);
995 /* Commit the file if it has changed */
996 if (file
->has_changed
)
998 struct stat file_status
;
1000 if (extfs_cmd (" copyin ", file
->archive
, file
->entry
, file
->entry
->inode
->local_filename
))
1003 if (stat (file
->entry
->inode
->local_filename
, &file_status
) != 0)
1006 file
->entry
->inode
->size
= file_status
.st_size
;
1008 file
->entry
->inode
->mtime
= time (NULL
);
1011 if (--file
->archive
->fd_usage
== 0)
1012 vfs_stamp_create (&vfs_extfs_ops
, file
->archive
);
1015 if (errno_code
!= 0)
1020 /* --------------------------------------------------------------------------------------------- */
1023 extfs_errno (struct vfs_class
*me
)
1029 /* --------------------------------------------------------------------------------------------- */
1032 extfs_opendir (const vfs_path_t
* vpath
)
1034 struct archive
*archive
= NULL
;
1036 struct entry
*entry
;
1037 struct entry
**info
;
1039 q
= extfs_get_path (vpath
, &archive
, FALSE
);
1042 entry
= extfs_find_entry (archive
->root_entry
, q
, FALSE
, FALSE
);
1046 entry
= extfs_resolve_symlinks (entry
);
1049 if (!S_ISDIR (entry
->inode
->mode
))
1050 ERRNOR (ENOTDIR
, NULL
);
1052 info
= g_new (struct entry
*, 2);
1053 info
[0] = entry
->inode
->first_in_subdir
;
1054 info
[1] = entry
->inode
->first_in_subdir
;
1059 /* --------------------------------------------------------------------------------------------- */
1062 extfs_readdir (void *data
)
1064 static union vfs_dirent dir
;
1065 struct entry
**info
= (struct entry
**) data
;
1070 g_strlcpy (dir
.dent
.d_name
, (*info
)->name
, MC_MAXPATHLEN
);
1072 compute_namelen (&dir
.dent
);
1073 *info
= (*info
)->next_in_dir
;
1075 return (void *) &dir
;
1078 /* --------------------------------------------------------------------------------------------- */
1081 extfs_closedir (void *data
)
1087 /* --------------------------------------------------------------------------------------------- */
1090 extfs_stat_move (struct stat
*buf
, const struct inode
*inode
)
1092 buf
->st_dev
= inode
->dev
;
1093 buf
->st_ino
= inode
->inode
;
1094 buf
->st_mode
= inode
->mode
;
1095 buf
->st_nlink
= inode
->nlink
;
1096 buf
->st_uid
= inode
->uid
;
1097 buf
->st_gid
= inode
->gid
;
1098 #ifdef HAVE_STRUCT_STAT_ST_RDEV
1099 buf
->st_rdev
= inode
->rdev
;
1101 buf
->st_size
= inode
->size
;
1102 #ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
1103 buf
->st_blksize
= RECORDSIZE
;
1105 #ifdef HAVE_STRUCT_STAT_ST_BLOCKS
1106 buf
->st_blocks
= (inode
->size
+ RECORDSIZE
- 1) / RECORDSIZE
;
1108 buf
->st_atime
= inode
->atime
;
1109 buf
->st_mtime
= inode
->mtime
;
1110 buf
->st_ctime
= inode
->ctime
;
1113 /* --------------------------------------------------------------------------------------------- */
1116 extfs_internal_stat (const vfs_path_t
* vpath
, struct stat
*buf
, gboolean resolve
)
1118 struct archive
*archive
;
1120 struct entry
*entry
;
1123 q
= extfs_get_path_mangle (vpath
, &archive
, FALSE
);
1126 entry
= extfs_find_entry (archive
->root_entry
, q
, FALSE
, FALSE
);
1131 entry
= extfs_resolve_symlinks (entry
);
1135 extfs_stat_move (buf
, entry
->inode
);
1141 /* --------------------------------------------------------------------------------------------- */
1144 extfs_stat (const vfs_path_t
* vpath
, struct stat
*buf
)
1146 return extfs_internal_stat (vpath
, buf
, TRUE
);
1149 /* --------------------------------------------------------------------------------------------- */
1152 extfs_lstat (const vfs_path_t
* vpath
, struct stat
*buf
)
1154 return extfs_internal_stat (vpath
, buf
, FALSE
);
1157 /* --------------------------------------------------------------------------------------------- */
1160 extfs_fstat (void *data
, struct stat
*buf
)
1162 struct pseudofile
*file
= (struct pseudofile
*) data
;
1164 extfs_stat_move (buf
, file
->entry
->inode
);
1168 /* --------------------------------------------------------------------------------------------- */
1171 extfs_readlink (const vfs_path_t
* vpath
, char *buf
, size_t size
)
1173 struct archive
*archive
;
1176 struct entry
*entry
;
1179 q
= extfs_get_path_mangle (vpath
, &archive
, FALSE
);
1182 entry
= extfs_find_entry (archive
->root_entry
, q
, FALSE
, FALSE
);
1185 if (!S_ISLNK (entry
->inode
->mode
))
1187 const vfs_path_element_t
*path_element
;
1189 path_element
= vfs_path_get_by_index (vpath
, -1);
1190 path_element
->class->verrno
= EINVAL
;
1193 len
= strlen (entry
->inode
->linkname
);
1196 /* readlink() does not append a NUL character to buf */
1198 memcpy (buf
, entry
->inode
->linkname
, result
);
1203 /* --------------------------------------------------------------------------------------------- */
1206 extfs_chown (const vfs_path_t
* vpath
, uid_t owner
, gid_t group
)
1214 /* --------------------------------------------------------------------------------------------- */
1217 extfs_chmod (const vfs_path_t
* vpath
, mode_t mode
)
1224 /* --------------------------------------------------------------------------------------------- */
1227 extfs_write (void *data
, const char *buf
, size_t nbyte
)
1229 struct pseudofile
*file
= (struct pseudofile
*) data
;
1231 file
->has_changed
= TRUE
;
1232 return write (file
->local_handle
, buf
, nbyte
);
1235 /* --------------------------------------------------------------------------------------------- */
1238 extfs_unlink (const vfs_path_t
* vpath
)
1240 struct archive
*archive
;
1242 struct entry
*entry
;
1245 q
= extfs_get_path_mangle (vpath
, &archive
, FALSE
);
1248 entry
= extfs_find_entry (archive
->root_entry
, q
, FALSE
, FALSE
);
1251 entry
= extfs_resolve_symlinks (entry
);
1254 if (S_ISDIR (entry
->inode
->mode
))
1256 const vfs_path_element_t
*path_element
;
1258 path_element
= vfs_path_get_by_index (vpath
, -1);
1259 path_element
->class->verrno
= EISDIR
;
1262 if (extfs_cmd (" rm ", archive
, entry
, ""))
1267 extfs_remove_entry (entry
);
1273 /* --------------------------------------------------------------------------------------------- */
1276 extfs_mkdir (const vfs_path_t
* vpath
, mode_t mode
)
1278 struct archive
*archive
;
1280 struct entry
*entry
;
1282 const vfs_path_element_t
*path_element
;
1286 path_element
= vfs_path_get_by_index (vpath
, -1);
1287 q
= extfs_get_path_mangle (vpath
, &archive
, FALSE
);
1290 entry
= extfs_find_entry (archive
->root_entry
, q
, FALSE
, FALSE
);
1293 path_element
->class->verrno
= EEXIST
;
1296 entry
= extfs_find_entry (archive
->root_entry
, q
, TRUE
, FALSE
);
1299 entry
= extfs_resolve_symlinks (entry
);
1302 if (!S_ISDIR (entry
->inode
->mode
))
1304 path_element
->class->verrno
= ENOTDIR
;
1308 if (extfs_cmd (" mkdir ", archive
, entry
, ""))
1311 extfs_remove_entry (entry
);
1319 /* --------------------------------------------------------------------------------------------- */
1322 extfs_rmdir (const vfs_path_t
* vpath
)
1324 struct archive
*archive
;
1326 struct entry
*entry
;
1329 q
= extfs_get_path_mangle (vpath
, &archive
, FALSE
);
1332 entry
= extfs_find_entry (archive
->root_entry
, q
, FALSE
, FALSE
);
1335 entry
= extfs_resolve_symlinks (entry
);
1338 if (!S_ISDIR (entry
->inode
->mode
))
1340 const vfs_path_element_t
*path_element
;
1342 path_element
= vfs_path_get_by_index (vpath
, -1);
1343 path_element
->class->verrno
= ENOTDIR
;
1347 if (extfs_cmd (" rmdir ", archive
, entry
, ""))
1352 extfs_remove_entry (entry
);
1358 /* --------------------------------------------------------------------------------------------- */
1361 extfs_chdir (const vfs_path_t
* vpath
)
1363 struct archive
*archive
= NULL
;
1365 struct entry
*entry
;
1368 q
= extfs_get_path (vpath
, &archive
, FALSE
);
1371 entry
= extfs_find_entry (archive
->root_entry
, q
, FALSE
, FALSE
);
1375 entry
= extfs_resolve_symlinks (entry
);
1376 if ((entry
== NULL
) || (!S_ISDIR (entry
->inode
->mode
)))
1382 /* --------------------------------------------------------------------------------------------- */
1385 extfs_lseek (void *data
, off_t offset
, int whence
)
1387 struct pseudofile
*file
= (struct pseudofile
*) data
;
1389 return lseek (file
->local_handle
, offset
, whence
);
1392 /* --------------------------------------------------------------------------------------------- */
1395 extfs_getid (const vfs_path_t
* vpath
)
1397 struct archive
*archive
= NULL
;
1400 p
= extfs_get_path (vpath
, &archive
, TRUE
);
1404 return (vfsid
) archive
;
1407 /* --------------------------------------------------------------------------------------------- */
1410 extfs_nothingisopen (vfsid id
)
1412 return (((struct archive
*) id
)->fd_usage
<= 0);
1415 /* --------------------------------------------------------------------------------------------- */
1418 extfs_remove_entry (struct entry
*e
)
1420 int i
= --e
->inode
->nlink
;
1421 struct entry
*pe
, *ent
, *prev
;
1423 if (S_ISDIR (e
->inode
->mode
) && e
->inode
->first_in_subdir
!= NULL
)
1425 struct entry
*f
= e
->inode
->first_in_subdir
;
1426 e
->inode
->first_in_subdir
= NULL
;
1427 extfs_remove_entry (f
);
1430 if (e
== pe
->inode
->first_in_subdir
)
1431 pe
->inode
->first_in_subdir
= e
->next_in_dir
;
1434 for (ent
= pe
->inode
->first_in_subdir
; ent
&& ent
->next_in_dir
; ent
= ent
->next_in_dir
)
1435 if (e
== ent
->next_in_dir
)
1441 prev
->next_in_dir
= e
->next_in_dir
;
1442 if (e
== pe
->inode
->last_in_subdir
)
1443 pe
->inode
->last_in_subdir
= prev
;
1447 if (e
->inode
->local_filename
!= NULL
)
1449 unlink (e
->inode
->local_filename
);
1450 g_free (e
->inode
->local_filename
);
1452 g_free (e
->inode
->linkname
);
1460 /* --------------------------------------------------------------------------------------------- */
1463 extfs_free_entry (struct entry
*e
)
1465 int i
= --e
->inode
->nlink
;
1467 if (S_ISDIR (e
->inode
->mode
) && e
->inode
->first_in_subdir
!= NULL
)
1469 struct entry
*f
= e
->inode
->first_in_subdir
;
1471 e
->inode
->first_in_subdir
= NULL
;
1472 extfs_free_entry (f
);
1476 if (e
->inode
->local_filename
!= NULL
)
1478 unlink (e
->inode
->local_filename
);
1479 g_free (e
->inode
->local_filename
);
1481 g_free (e
->inode
->linkname
);
1484 if (e
->next_in_dir
!= NULL
)
1485 extfs_free_entry (e
->next_in_dir
);
1490 /* --------------------------------------------------------------------------------------------- */
1493 extfs_free (vfsid id
)
1495 struct archive
*archive
= (struct archive
*) id
;
1497 if (archive
== first_archive
)
1499 first_archive
= archive
->next
;
1503 struct archive
*parc
;
1504 for (parc
= first_archive
; parc
!= NULL
; parc
= parc
->next
)
1505 if (parc
->next
== archive
)
1507 parc
->next
= archive
->next
;
1511 extfs_free_archive (archive
);
1514 /* --------------------------------------------------------------------------------------------- */
1517 extfs_getlocalcopy (const vfs_path_t
* vpath
)
1519 struct pseudofile
*fp
;
1522 fp
= (struct pseudofile
*) extfs_open (vpath
, O_RDONLY
, 0);
1525 if (fp
->entry
->inode
->local_filename
== NULL
)
1527 extfs_close ((void *) fp
);
1530 p
= vfs_path_from_str (fp
->entry
->inode
->local_filename
);
1531 fp
->archive
->fd_usage
++;
1532 extfs_close ((void *) fp
);
1536 /* --------------------------------------------------------------------------------------------- */
1539 extfs_ungetlocalcopy (const vfs_path_t
* vpath
, const vfs_path_t
* local
, gboolean has_changed
)
1541 struct pseudofile
*fp
;
1543 fp
= (struct pseudofile
*) extfs_open (vpath
, O_RDONLY
, 0);
1547 if (strcmp (fp
->entry
->inode
->local_filename
, vfs_path_get_last_path_str (local
)) == 0)
1549 fp
->archive
->fd_usage
--;
1551 fp
->has_changed
= TRUE
;
1552 extfs_close ((void *) fp
);
1557 /* Should not happen */
1558 extfs_close ((void *) fp
);
1563 /* --------------------------------------------------------------------------------------------- */
1566 extfs_get_plugins (const char *where
, gboolean silent
)
1570 const char *filename
;
1572 dirname
= g_build_path (PATH_SEP_STR
, where
, MC_EXTFS_DIR
, (char *) NULL
);
1573 dir
= g_dir_open (dirname
, 0, NULL
);
1575 /* We may not use vfs_die() message or message or similar,
1576 * UI is not initialized at this time and message would not
1577 * appear on screen. */
1581 fprintf (stderr
, _("Warning: cannot open %s directory\n"), dirname
);
1586 if (extfs_plugins
== NULL
)
1587 extfs_plugins
= g_array_sized_new (FALSE
, TRUE
, sizeof (extfs_plugin_info_t
), 32);
1589 while ((filename
= g_dir_read_name (dir
)) != NULL
)
1591 char fullname
[MC_MAXPATHLEN
];
1594 g_snprintf (fullname
, sizeof (fullname
), "%s" PATH_SEP_STR
"%s", dirname
, filename
);
1596 if ((stat (fullname
, &s
) == 0)
1597 && S_ISREG (s
.st_mode
) && !S_ISDIR (s
.st_mode
)
1598 && (((s
.st_mode
& S_IXOTH
) != 0) ||
1599 ((s
.st_mode
& S_IXUSR
) != 0) || ((s
.st_mode
& S_IXGRP
) != 0)))
1603 f
= open (fullname
, O_RDONLY
);
1608 extfs_plugin_info_t info
;
1609 gboolean found
= FALSE
;
1613 /* Handle those with a trailing '+', those flag that the
1614 * file system does not require an archive to work
1616 len
= strlen (filename
);
1617 info
.need_archive
= (filename
[len
- 1] != '+');
1618 info
.path
= g_strconcat (dirname
, PATH_SEP_STR
, (char *) NULL
);
1619 info
.prefix
= g_strdup (filename
);
1621 /* prepare to compare file names without trailing '+' */
1622 if (!info
.need_archive
)
1623 info
.prefix
[len
- 1] = '\0';
1625 /* don't overload already found plugin */
1626 for (i
= 0; i
< extfs_plugins
->len
; i
++)
1628 extfs_plugin_info_t
*p
;
1630 p
= &g_array_index (extfs_plugins
, extfs_plugin_info_t
, i
);
1632 /* 2 files with same names cannot be in a directory */
1633 if ((strcmp (info
.path
, p
->path
) != 0)
1634 && (strcmp (info
.prefix
, p
->prefix
) == 0))
1644 g_free (info
.prefix
);
1648 /* restore file name */
1649 if (!info
.need_archive
)
1650 info
.prefix
[len
- 1] = '+';
1651 g_array_append_val (extfs_plugins
, info
);
1663 /* --------------------------------------------------------------------------------------------- */
1666 extfs_init (struct vfs_class
*me
)
1672 /* 1st: scan user directory */
1673 d1
= extfs_get_plugins (mc_config_get_data_path (), TRUE
); /* silent about user dir */
1674 /* 2nd: scan system dir */
1675 d2
= extfs_get_plugins (LIBEXECDIR
, d1
);
1677 return (d1
|| d2
? 1 : 0);
1680 /* --------------------------------------------------------------------------------------------- */
1683 extfs_done (struct vfs_class
*me
)
1690 for (ar
= first_archive
; ar
!= NULL
;)
1692 extfs_free ((vfsid
) ar
);
1696 for (i
= 0; i
< extfs_plugins
->len
; i
++)
1698 extfs_plugin_info_t
*info
;
1700 info
= &g_array_index (extfs_plugins
, extfs_plugin_info_t
, i
);
1701 g_free (info
->path
);
1702 g_free (info
->prefix
);
1705 if (extfs_plugins
!= NULL
)
1706 g_array_free (extfs_plugins
, TRUE
);
1709 /* --------------------------------------------------------------------------------------------- */
1712 extfs_setctl (const vfs_path_t
* vpath
, int ctlop
, void *arg
)
1716 if (ctlop
== VFS_SETCTL_RUN
)
1724 /* --------------------------------------------------------------------------------------------- */
1725 /*** public functions ****************************************************************************/
1726 /* --------------------------------------------------------------------------------------------- */
1731 vfs_extfs_ops
.name
= "extfs";
1732 vfs_extfs_ops
.init
= extfs_init
;
1733 vfs_extfs_ops
.done
= extfs_done
;
1734 vfs_extfs_ops
.fill_names
= extfs_fill_names
;
1735 vfs_extfs_ops
.which
= extfs_which
;
1736 vfs_extfs_ops
.open
= extfs_open
;
1737 vfs_extfs_ops
.close
= extfs_close
;
1738 vfs_extfs_ops
.read
= extfs_read
;
1739 vfs_extfs_ops
.write
= extfs_write
;
1740 vfs_extfs_ops
.opendir
= extfs_opendir
;
1741 vfs_extfs_ops
.readdir
= extfs_readdir
;
1742 vfs_extfs_ops
.closedir
= extfs_closedir
;
1743 vfs_extfs_ops
.stat
= extfs_stat
;
1744 vfs_extfs_ops
.lstat
= extfs_lstat
;
1745 vfs_extfs_ops
.fstat
= extfs_fstat
;
1746 vfs_extfs_ops
.chmod
= extfs_chmod
;
1747 vfs_extfs_ops
.chown
= extfs_chown
;
1748 vfs_extfs_ops
.readlink
= extfs_readlink
;
1749 vfs_extfs_ops
.unlink
= extfs_unlink
;
1750 vfs_extfs_ops
.chdir
= extfs_chdir
;
1751 vfs_extfs_ops
.ferrno
= extfs_errno
;
1752 vfs_extfs_ops
.lseek
= extfs_lseek
;
1753 vfs_extfs_ops
.getid
= extfs_getid
;
1754 vfs_extfs_ops
.nothingisopen
= extfs_nothingisopen
;
1755 vfs_extfs_ops
.free
= extfs_free
;
1756 vfs_extfs_ops
.getlocalcopy
= extfs_getlocalcopy
;
1757 vfs_extfs_ops
.ungetlocalcopy
= extfs_ungetlocalcopy
;
1758 vfs_extfs_ops
.mkdir
= extfs_mkdir
;
1759 vfs_extfs_ops
.rmdir
= extfs_rmdir
;
1760 vfs_extfs_ops
.setctl
= extfs_setctl
;
1761 vfs_register_class (&vfs_extfs_ops
);
1764 /* --------------------------------------------------------------------------------------------- */