2 Virtual File System: External file system.
4 Copyright (C) 1995-2022
5 Free Software Foundation, Inc.
10 Andrew T. Veliath, 1999
11 Slava Zanko <slavazanko@gmail.com>, 2013
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>
53 #include "lib/global.h"
54 #include "lib/fileloc.h"
55 #include "lib/mcconfig.h"
57 #include "lib/widget.h" /* message() */
59 #include "src/execute.h" /* For shell_execute */
61 #include "lib/vfs/vfs.h"
62 #include "lib/vfs/utilvfs.h"
63 #include "lib/vfs/xdirentry.h"
64 #include "lib/vfs/gc.h" /* vfs_rmstamp */
68 /*** global variables ****************************************************************************/
70 /*** file scope macro definitions ****************************************************************/
73 #define ERRNOR(x,y) do { my_errno = x; return y; } while(0)
75 #define RECORDSIZE 512
77 #define EXTFS_SUPER(a) ((struct extfs_super_t *) (a))
79 /*** file scope type declarations ****************************************************************/
83 struct vfs_s_super base
; /* base class */
87 struct stat local_stat
;
95 gboolean need_archive
;
96 } extfs_plugin_info_t
;
98 /*** file scope variables ************************************************************************/
100 static GArray
*extfs_plugins
= NULL
;
102 static gboolean errloop
;
103 static gboolean notadir
;
105 static struct vfs_s_subclass extfs_subclass
;
106 static struct vfs_class
*vfs_extfs_ops
= VFS_CLASS (&extfs_subclass
);
108 static int my_errno
= 0;
110 /* --------------------------------------------------------------------------------------------- */
111 /*** file scope functions ************************************************************************/
112 /* --------------------------------------------------------------------------------------------- */
114 static struct vfs_s_entry
*extfs_resolve_symlinks_int (struct vfs_s_entry
*entry
, GSList
* list
);
116 /* --------------------------------------------------------------------------------------------- */
118 static struct extfs_super_t
*
119 extfs_super_new (struct vfs_class
*me
, const char *name
, const vfs_path_t
* local_name_vpath
,
122 struct extfs_super_t
*super
;
123 struct vfs_s_super
*vsuper
;
125 super
= g_new0 (struct extfs_super_t
, 1);
126 vsuper
= VFS_SUPER (super
);
129 vsuper
->name
= g_strdup (name
);
131 super
->fstype
= fstype
;
133 if (local_name_vpath
!= NULL
)
135 super
->local_name
= g_strdup (vfs_path_get_last_path_str (local_name_vpath
));
136 mc_stat (local_name_vpath
, &super
->local_stat
);
139 VFS_SUBCLASS (me
)->supers
= g_list_prepend (VFS_SUBCLASS (me
)->supers
, super
);
144 /* --------------------------------------------------------------------------------------------- */
146 /* unlike vfs_s_new_entry(), inode->ent is kept */
147 static struct vfs_s_entry
*
148 extfs_entry_new (struct vfs_class
*me
, const char *name
, struct vfs_s_inode
*inode
)
150 struct vfs_s_entry
*entry
;
154 entry
= g_new0 (struct vfs_s_entry
, 1);
156 entry
->name
= g_strdup (name
);
162 /* --------------------------------------------------------------------------------------------- */
165 extfs_fill_name (void *data
, void *user_data
)
167 struct vfs_s_super
*a
= VFS_SUPER (data
);
168 fill_names_f func
= (fill_names_f
) user_data
;
169 extfs_plugin_info_t
*info
;
172 info
= &g_array_index (extfs_plugins
, extfs_plugin_info_t
, EXTFS_SUPER (a
)->fstype
);
174 g_strconcat (a
->name
!= NULL
? a
->name
: "", PATH_SEP_STR
, info
->prefix
,
175 VFS_PATH_URL_DELIMITER
, (char *) NULL
);
180 /* --------------------------------------------------------------------------------------------- */
183 extfs_cmp_archive (const void *a
, const void *b
)
185 const struct vfs_s_super
*ar
= (const struct vfs_s_super
*) a
;
186 const char *archive_name
= (const char *) b
;
188 return (ar
->name
!= NULL
&& strcmp (ar
->name
, archive_name
) == 0) ? 0 : 1;
191 /* --------------------------------------------------------------------------------------------- */
193 static struct vfs_s_entry
*
194 extfs_generate_entry (struct extfs_super_t
*archive
, const char *name
, struct vfs_s_inode
*parent
,
197 struct vfs_class
*me
= VFS_SUPER (archive
)->me
;
200 struct vfs_s_inode
*inode
;
201 struct vfs_s_entry
*entry
;
203 memset (&st
, 0, sizeof (st
));
204 st
.st_ino
= VFS_SUPER (archive
)->ino_usage
++;
205 st
.st_dev
= archive
->rdev
;
206 myumask
= umask (022);
208 st
.st_mode
= mode
& ~myumask
;
209 st
.st_uid
= getuid ();
210 st
.st_gid
= getgid ();
211 st
.st_mtime
= time (NULL
);
212 st
.st_atime
= st
.st_mtime
;
213 st
.st_ctime
= st
.st_mtime
;
216 inode
= vfs_s_new_inode (me
, VFS_SUPER (archive
), &st
);
217 entry
= vfs_s_new_entry (me
, name
, inode
);
219 vfs_s_insert_entry (me
, parent
, entry
);
224 /* --------------------------------------------------------------------------------------------- */
226 static struct vfs_s_entry
*
227 extfs_find_entry_int (struct vfs_s_inode
*dir
, const char *name
, GSList
* list
, int flags
)
229 struct vfs_s_entry
*pent
, *pdir
;
230 const char *p
, *name_end
;
233 struct extfs_super_t
*super
;
235 if (g_path_is_absolute (name
))
237 /* Handle absolute paths */
238 name
= g_path_skip_root (name
);
239 dir
= dir
->super
->root
;
242 super
= EXTFS_SUPER (dir
->super
);
245 name_end
= name
+ strlen (name
);
247 while ((pent
!= NULL
) && (c
!= '\0') && (*p
!= '\0'))
249 q
= strchr (p
, PATH_SEP
);
251 q
= (char *) name_end
;
256 if (DIR_IS_DOTDOT (p
))
257 pent
= pent
->dir
->ent
;
262 pent
= extfs_resolve_symlinks_int (pent
, list
);
269 if (!S_ISDIR (pent
->ino
->st
.st_mode
))
277 pl
= g_queue_find_custom (pent
->ino
->subdir
, p
, vfs_s_entry_compare
);
278 pent
= pl
!= NULL
? VFS_ENTRY (pl
->data
) : NULL
;
279 if (pent
!= NULL
&& q
+ 1 > name_end
)
281 /* Hack: I keep the original semanthic unless q+1 would break in the strchr */
283 notadir
= !S_ISDIR (pent
->ino
->st
.st_mode
);
287 /* When we load archive, we create automagically non-existent directories */
288 if (pent
== NULL
&& (flags
& FL_MKDIR
) != 0)
289 pent
= extfs_generate_entry (super
, p
, pdir
->ino
, S_IFDIR
| 0777);
290 if (pent
== NULL
&& (flags
& FL_MKFILE
) != 0)
291 pent
= extfs_generate_entry (super
, p
, pdir
->ino
, S_IFREG
| 0666);
304 /* --------------------------------------------------------------------------------------------- */
306 static struct vfs_s_entry
*
307 extfs_find_entry (struct vfs_s_inode
*dir
, const char *name
, int flags
)
309 struct vfs_s_entry
*res
;
314 res
= extfs_find_entry_int (dir
, name
, NULL
, flags
);
325 /* --------------------------------------------------------------------------------------------- */
328 extfs_fill_names (struct vfs_class
*me
, fill_names_f func
)
330 g_list_foreach (VFS_SUBCLASS (me
)->supers
, extfs_fill_name
, func
);
333 /* --------------------------------------------------------------------------------------------- */
335 /* Create this function because VFSF_USETMP flag is not used in extfs */
337 extfs_free_inode (struct vfs_class
*me
, struct vfs_s_inode
*ino
)
341 if (ino
->localname
!= NULL
)
343 unlink (ino
->localname
);
344 MC_PTR_FREE (ino
->localname
);
348 /* --------------------------------------------------------------------------------------------- */
351 extfs_free_archive (struct vfs_class
*me
, struct vfs_s_super
*psup
)
353 struct extfs_super_t
*archive
= EXTFS_SUPER (psup
);
357 if (archive
->local_name
!= NULL
)
360 vfs_path_t
*local_name_vpath
, *name_vpath
;
362 local_name_vpath
= vfs_path_from_str (archive
->local_name
);
363 name_vpath
= vfs_path_from_str (psup
->name
);
364 mc_stat (local_name_vpath
, &my
);
365 mc_ungetlocalcopy (name_vpath
, local_name_vpath
,
366 archive
->local_stat
.st_mtime
!= my
.st_mtime
);
367 vfs_path_free (local_name_vpath
, TRUE
);
368 vfs_path_free (name_vpath
, TRUE
);
369 g_free (archive
->local_name
);
373 /* --------------------------------------------------------------------------------------------- */
376 extfs_skip_leading_dotslash (char *s
)
378 /* Skip leading "./" (if present).
379 * Some programs don't understand it:
381 * $ zip file.zip ./-file2.txt file1.txt
382 * adding: -file2.txt (stored 0%)
383 * adding: file1.txt (stored 0%)
384 * $ /usr/lib/mc/extfs.d/uzip copyout file.zip ./-file2.txt ./tmp-file2.txt
385 * caution: filename not matched: ./-file2.txt
387 if (s
[0] == '.' && s
[1] == PATH_SEP
)
393 /* --------------------------------------------------------------------------------------------- */
396 extfs_add_file (struct extfs_super_t
*archive
, const char *file_name
)
398 struct vfs_s_super
*super
= VFS_SUPER (archive
);
400 char *current_file_name
= NULL
, *current_link_name
= NULL
;
403 if (vfs_parse_ls_lga (file_name
, &hstat
, ¤t_file_name
, ¤t_link_name
, NULL
))
405 char *cfn
= current_file_name
;
409 struct vfs_s_entry
*entry
;
410 struct vfs_s_entry
*pent
= NULL
;
411 struct vfs_s_inode
*inode
;
414 cfn
= extfs_skip_leading_dotslash (cfn
);
415 if (IS_PATH_SEP (*cfn
))
417 p
= strchr (cfn
, '\0');
418 if (p
!= cfn
&& IS_PATH_SEP (p
[-1]))
420 p
= strrchr (cfn
, PATH_SEP
);
424 q
= strchr (cfn
, '\0');
434 pent
= extfs_find_entry (super
->root
, q
, FL_MKDIR
);
444 entry
= extfs_entry_new (super
->me
, p
, pent
->ino
);
445 entry
->dir
= pent
->ino
;
446 g_queue_push_tail (pent
->ino
->subdir
, entry
);
450 entry
= extfs_entry_new (super
->me
, p
, super
->root
);
451 entry
->dir
= super
->root
;
452 g_queue_push_tail (super
->root
->subdir
, entry
);
455 if (!S_ISLNK (hstat
.st_mode
) && (current_link_name
!= NULL
))
457 pent
= extfs_find_entry (super
->root
, current_link_name
, FL_NONE
);
464 pent
->ino
->st
.st_nlink
++;
465 entry
->ino
= pent
->ino
;
471 memset (&st
, 0, sizeof (st
));
472 st
.st_ino
= super
->ino_usage
++;
474 st
.st_dev
= archive
->rdev
;
475 st
.st_mode
= hstat
.st_mode
;
476 #ifdef HAVE_STRUCT_STAT_ST_RDEV
477 st
.st_rdev
= hstat
.st_rdev
;
479 st
.st_uid
= hstat
.st_uid
;
480 st
.st_gid
= hstat
.st_gid
;
481 st
.st_size
= hstat
.st_size
;
482 st
.st_mtime
= hstat
.st_mtime
;
483 st
.st_atime
= hstat
.st_atime
;
484 st
.st_ctime
= hstat
.st_ctime
;
486 if (current_link_name
== NULL
&& S_ISLNK (hstat
.st_mode
))
487 st
.st_mode
&= ~S_IFLNK
; /* You *DON'T* want to do this always */
489 inode
= vfs_s_new_inode (super
->me
, super
, &st
);
493 if (current_link_name
!= NULL
&& S_ISLNK (hstat
.st_mode
))
495 inode
->linkname
= current_link_name
;
496 current_link_name
= NULL
;
502 g_free (current_file_name
);
503 g_free (current_link_name
);
509 /* --------------------------------------------------------------------------------------------- */
512 extfs_open_archive (int fstype
, const char *name
, struct extfs_super_t
**pparc
, GError
** error
)
514 const extfs_plugin_info_t
*info
;
515 static dev_t archive_counter
= 0;
516 mc_pipe_t
*result
= NULL
;
520 struct extfs_super_t
*current_archive
;
521 struct vfs_s_entry
*root_entry
;
523 vfs_path_t
*local_name_vpath
= NULL
;
524 vfs_path_t
*name_vpath
;
526 memset (&mystat
, 0, sizeof (mystat
));
528 name_vpath
= vfs_path_from_str (name
);
529 info
= &g_array_index (extfs_plugins
, extfs_plugin_info_t
, fstype
);
531 if (info
->need_archive
)
533 if (mc_stat (name_vpath
, &mystat
) == -1)
536 if (!vfs_file_is_local (name_vpath
))
538 local_name_vpath
= mc_getlocalcopy (name_vpath
);
539 if (local_name_vpath
== NULL
)
543 tmp
= name_quote (vfs_path_get_last_path_str (name_vpath
), FALSE
);
546 cmd
= g_strconcat (info
->path
, info
->prefix
, " list ",
547 vfs_path_get_last_path_str (local_name_vpath
) != NULL
?
548 vfs_path_get_last_path_str (local_name_vpath
) : tmp
, (char *) NULL
);
551 result
= mc_popen (cmd
, TRUE
, TRUE
, error
);
556 if (local_name_vpath
!= NULL
)
558 mc_ungetlocalcopy (name_vpath
, local_name_vpath
, FALSE
);
559 vfs_path_free (local_name_vpath
, TRUE
);
564 current_archive
= extfs_super_new (vfs_extfs_ops
, name
, local_name_vpath
, fstype
);
565 current_archive
->rdev
= archive_counter
++;
566 vfs_path_free (local_name_vpath
, TRUE
);
568 mode
= mystat
.st_mode
& 07777;
577 root_entry
= extfs_generate_entry (current_archive
, PATH_SEP_STR
, NULL
, mode
);
578 root_entry
->ino
->st
.st_uid
= mystat
.st_uid
;
579 root_entry
->ino
->st
.st_gid
= mystat
.st_gid
;
580 root_entry
->ino
->st
.st_atime
= mystat
.st_atime
;
581 root_entry
->ino
->st
.st_ctime
= mystat
.st_ctime
;
582 root_entry
->ino
->st
.st_mtime
= mystat
.st_mtime
;
583 root_entry
->ino
->ent
= root_entry
;
584 VFS_SUPER (current_archive
)->root
= root_entry
->ino
;
586 *pparc
= current_archive
;
589 vfs_path_free (name_vpath
, TRUE
);
593 /* --------------------------------------------------------------------------------------------- */
595 * Main loop for reading an archive.
596 * Return 0 on success, -1 on error.
600 extfs_read_archive (mc_pipe_t
* pip
, struct extfs_super_t
*archive
, GError
** error
)
604 GString
*err_msg
= NULL
;
605 GString
*remain_file_name
= NULL
;
609 /* init buffers before call of mc_pread() */
610 pip
->out
.len
= MC_PIPE_BUFSIZE
;
611 pip
->err
.len
= MC_PIPE_BUFSIZE
;
613 mc_pread (pip
, error
);
618 if (pip
->err
.len
> 0)
620 /* join errors/warnings */
622 err_msg
= g_string_new_len (pip
->err
.buf
, pip
->err
.len
);
625 if (err_msg
->str
[err_msg
->len
- 1] != '\n')
626 g_string_append_c (err_msg
, '\n');
627 g_string_append_len (err_msg
, pip
->err
.buf
, pip
->err
.len
);
631 if (pip
->out
.len
== MC_PIPE_STREAM_EOF
)
634 if (pip
->out
.len
== 0)
637 if (pip
->out
.len
== MC_PIPE_ERROR_READ
)
640 while (ret
!= -1 && (buffer
= mc_pstream_get_string (&pip
->out
)) != NULL
)
642 /* handle a \n-separated file list */
644 if (buffer
->str
[buffer
->len
- 1] == '\n')
646 /* entire file name or last chunk */
648 g_string_truncate (buffer
, buffer
->len
- 1);
650 /* join filename chunks */
651 if (remain_file_name
!= NULL
)
653 g_string_append_len (remain_file_name
, buffer
->str
, buffer
->len
);
654 g_string_free (buffer
, TRUE
);
655 buffer
= remain_file_name
;
656 remain_file_name
= NULL
;
661 /* first or middle chunk of file name */
663 if (remain_file_name
== NULL
)
664 remain_file_name
= buffer
;
667 g_string_append_len (remain_file_name
, buffer
->str
, buffer
->len
);
668 g_string_free (buffer
, TRUE
);
674 ret
= extfs_add_file (archive
, buffer
->str
);
676 g_string_free (buffer
, TRUE
);
680 if (remain_file_name
!= NULL
)
681 g_string_free (remain_file_name
, TRUE
);
686 mc_propagate_error (error
, 0, "%s", err_msg
->str
);
688 g_string_free (err_msg
, TRUE
);
694 /* --------------------------------------------------------------------------------------------- */
697 extfs_which (struct vfs_class
*me
, const char *path
)
704 path_len
= strlen (path
);
706 for (i
= 0; i
< extfs_plugins
->len
; i
++)
708 extfs_plugin_info_t
*info
;
710 info
= &g_array_index (extfs_plugins
, extfs_plugin_info_t
, i
);
712 if ((strncmp (path
, info
->prefix
, path_len
) == 0)
713 && ((info
->prefix
[path_len
] == '\0') || (info
->prefix
[path_len
] == '+')))
719 /* --------------------------------------------------------------------------------------------- */
722 extfs_open_and_read_archive (int fstype
, const char *name
, struct extfs_super_t
**archive
)
725 struct extfs_super_t
*a
;
727 GError
*error
= NULL
;
729 pip
= extfs_open_archive (fstype
, name
, archive
, &error
);
735 const extfs_plugin_info_t
*info
;
737 info
= &g_array_index (extfs_plugins
, extfs_plugin_info_t
, fstype
);
738 message (D_ERROR
, MSG_ERROR
, _("Cannot open %s archive\n%s:\n%s"), info
->prefix
, name
,
740 g_error_free (error
);
744 result
= extfs_read_archive (pip
, a
, &error
);
747 VFS_SUPER (a
)->me
->free (VFS_SUPER (a
));
751 message (D_ERROR
, MSG_ERROR
, _("EXTFS virtual file system:\n%s"), error
->message
);
752 g_error_free (error
);
755 mc_pclose (pip
, NULL
);
761 /* --------------------------------------------------------------------------------------------- */
763 * Dissect the path and create corresponding superblock.
766 extfs_get_path (const vfs_path_t
* vpath
, struct extfs_super_t
**archive
, int flags
)
772 const vfs_path_element_t
*path_element
;
773 struct extfs_super_t
*a
= NULL
;
775 path_element
= vfs_path_get_by_index (vpath
, -1);
777 fstype
= extfs_which (path_element
->class, path_element
->vfs_prefix
);
781 archive_name
= vfs_path_to_str_elements_count (vpath
, -1);
783 /* All filesystems should have some local archive, at least it can be PATH_SEP ('/'). */
784 parc
= g_list_find_custom (extfs_subclass
.supers
, archive_name
, extfs_cmp_archive
);
787 a
= EXTFS_SUPER (parc
->data
);
788 vfs_stamp (vfs_extfs_ops
, (vfsid
) a
);
789 g_free (archive_name
);
793 if ((flags
& FL_NO_OPEN
) == 0)
794 result
= extfs_open_and_read_archive (fstype
, archive_name
, &a
);
796 g_free (archive_name
);
800 path_element
->class->verrno
= EIO
;
806 return path_element
->path
;
809 /* --------------------------------------------------------------------------------------------- */
810 /* Return allocated path (without leading slash) inside the archive */
813 extfs_get_path_from_entry (const struct vfs_s_entry
*entry
)
815 const struct vfs_s_entry
*e
;
818 localpath
= g_string_new ("");
820 for (e
= entry
; e
->dir
!= NULL
; e
= e
->dir
->ent
)
822 g_string_prepend (localpath
, e
->name
);
823 if (e
->dir
->ent
->dir
!= NULL
)
824 g_string_prepend_c (localpath
, PATH_SEP
);
827 return g_string_free (localpath
, FALSE
);
830 /* --------------------------------------------------------------------------------------------- */
832 static struct vfs_s_entry
*
833 extfs_resolve_symlinks_int (struct vfs_s_entry
*entry
, GSList
* list
)
835 struct vfs_s_entry
*pent
= NULL
;
837 if (!S_ISLNK (entry
->ino
->st
.st_mode
))
840 if (g_slist_find (list
, entry
) != NULL
)
842 /* Here we protect us against symlink looping */
849 looping
= g_slist_prepend (list
, entry
);
850 pent
= extfs_find_entry_int (entry
->dir
, entry
->ino
->linkname
, looping
, FL_NONE
);
851 looping
= g_slist_delete_link (looping
, looping
);
860 /* --------------------------------------------------------------------------------------------- */
862 static struct vfs_s_entry
*
863 extfs_resolve_symlinks (struct vfs_s_entry
*entry
)
865 struct vfs_s_entry
*res
;
869 res
= extfs_resolve_symlinks_int (entry
, NULL
);
880 /* --------------------------------------------------------------------------------------------- */
883 extfs_get_archive_name (const struct extfs_super_t
*archive
)
885 const char *archive_name
;
887 if (archive
->local_name
!= NULL
)
888 archive_name
= archive
->local_name
;
890 archive_name
= CONST_VFS_SUPER (archive
)->name
;
892 if (archive_name
== NULL
|| *archive_name
== '\0')
893 return g_strdup ("no_archive_name");
898 const vfs_path_element_t
*path_element
;
900 vpath
= vfs_path_from_str (archive_name
);
901 path_element
= vfs_path_get_by_index (vpath
, -1);
902 ret_str
= g_strdup (path_element
->path
);
903 vfs_path_free (vpath
, TRUE
);
908 /* --------------------------------------------------------------------------------------------- */
909 /** Don't pass localname as NULL */
912 extfs_cmd (const char *str_extfs_cmd
, const struct extfs_super_t
*archive
,
913 const struct vfs_s_entry
*entry
, const char *localname
)
917 char *quoted_localname
;
918 char *archive_name
, *quoted_archive_name
;
919 const extfs_plugin_info_t
*info
;
922 GError
*error
= NULL
;
925 file
= extfs_get_path_from_entry (entry
);
926 quoted_file
= name_quote (file
, FALSE
);
929 /* Skip leading "./" (if present) added in name_quote() */
930 file
= extfs_skip_leading_dotslash (quoted_file
);
932 archive_name
= extfs_get_archive_name (archive
);
933 quoted_archive_name
= name_quote (archive_name
, FALSE
);
934 g_free (archive_name
);
935 quoted_localname
= name_quote (localname
, FALSE
);
936 info
= &g_array_index (extfs_plugins
, extfs_plugin_info_t
, archive
->fstype
);
937 cmd
= g_strconcat (info
->path
, info
->prefix
, str_extfs_cmd
,
938 quoted_archive_name
, " ", file
, " ", quoted_localname
, (char *) NULL
);
939 g_free (quoted_file
);
940 g_free (quoted_localname
);
941 g_free (quoted_archive_name
);
943 /* don't read stdout */
944 pip
= mc_popen (cmd
, FALSE
, TRUE
, &error
);
949 message (D_ERROR
, MSG_ERROR
, _("EXTFS virtual file system:\n%s"), error
->message
);
950 g_error_free (error
);
954 pip
->err
.null_term
= TRUE
;
956 mc_pread (pip
, &error
);
959 message (D_ERROR
, MSG_ERROR
, _("EXTFS virtual file system:\n%s"), error
->message
);
960 g_error_free (error
);
963 else if (pip
->err
.len
> 0)
964 message (D_ERROR
, MSG_ERROR
, _("EXTFS virtual file system:\n%s"), pip
->err
.buf
);
966 mc_pclose (pip
, NULL
);
971 /* --------------------------------------------------------------------------------------------- */
974 extfs_run (const vfs_path_t
* vpath
)
976 struct extfs_super_t
*archive
= NULL
;
978 char *q
, *archive_name
, *quoted_archive_name
;
980 const extfs_plugin_info_t
*info
;
982 p
= extfs_get_path (vpath
, &archive
, FL_NONE
);
985 q
= name_quote (p
, FALSE
);
987 archive_name
= extfs_get_archive_name (archive
);
988 quoted_archive_name
= name_quote (archive_name
, FALSE
);
989 g_free (archive_name
);
990 info
= &g_array_index (extfs_plugins
, extfs_plugin_info_t
, archive
->fstype
);
992 g_strconcat (info
->path
, info
->prefix
, " run ", quoted_archive_name
, " ", q
, (char *) NULL
);
993 g_free (quoted_archive_name
);
995 shell_execute (cmd
, 0);
999 /* --------------------------------------------------------------------------------------------- */
1002 extfs_open (const vfs_path_t
* vpath
, int flags
, mode_t mode
)
1004 vfs_file_handler_t
*extfs_info
;
1005 struct extfs_super_t
*archive
= NULL
;
1007 struct vfs_s_entry
*entry
;
1009 gboolean created
= FALSE
;
1011 q
= extfs_get_path (vpath
, &archive
, FL_NONE
);
1014 entry
= extfs_find_entry (VFS_SUPER (archive
)->root
, q
, FL_NONE
);
1015 if ((entry
== NULL
) && ((flags
& O_CREAT
) != 0))
1017 /* Create new entry */
1018 entry
= extfs_find_entry (VFS_SUPER (archive
)->root
, q
, FL_MKFILE
);
1019 created
= (entry
!= NULL
);
1024 entry
= extfs_resolve_symlinks (entry
);
1028 if (S_ISDIR (entry
->ino
->st
.st_mode
))
1029 ERRNOR (EISDIR
, NULL
);
1031 if (entry
->ino
->localname
== NULL
)
1033 vfs_path_t
*local_filename_vpath
;
1034 const char *local_filename
;
1036 local_handle
= vfs_mkstemps (&local_filename_vpath
, "extfs", entry
->name
);
1038 if (local_handle
== -1)
1040 close (local_handle
);
1041 local_filename
= vfs_path_get_by_index (local_filename_vpath
, -1)->path
;
1043 if (!created
&& ((flags
& O_TRUNC
) == 0)
1044 && extfs_cmd (" copyout ", archive
, entry
, local_filename
))
1046 unlink (local_filename
);
1047 vfs_path_free (local_filename_vpath
, TRUE
);
1051 entry
->ino
->localname
= g_strdup (local_filename
);
1052 vfs_path_free (local_filename_vpath
, TRUE
);
1055 local_handle
= open (entry
->ino
->localname
, NO_LINEAR (flags
), mode
);
1057 if (local_handle
== -1)
1059 /* file exists(may be). Need to drop O_CREAT flag and truncate file content */
1060 flags
= ~O_CREAT
& (NO_LINEAR (flags
) | O_TRUNC
);
1061 local_handle
= open (entry
->ino
->localname
, flags
, mode
);
1064 if (local_handle
== -1)
1067 extfs_info
= g_new (vfs_file_handler_t
, 1);
1068 vfs_s_init_fh (extfs_info
, entry
->ino
, created
);
1069 extfs_info
->handle
= local_handle
;
1071 /* i.e. we had no open files and now we have one */
1072 vfs_rmstamp (vfs_extfs_ops
, (vfsid
) archive
);
1073 VFS_SUPER (archive
)->fd_usage
++;
1077 /* --------------------------------------------------------------------------------------------- */
1080 extfs_read (void *fh
, char *buffer
, size_t count
)
1082 vfs_file_handler_t
*file
= VFS_FILE_HANDLER (fh
);
1084 return read (file
->handle
, buffer
, count
);
1087 /* --------------------------------------------------------------------------------------------- */
1090 extfs_close (void *fh
)
1092 vfs_file_handler_t
*file
= VFS_FILE_HANDLER (fh
);
1095 close (file
->handle
);
1098 /* Commit the file if it has changed */
1101 struct stat file_status
;
1104 (" copyin ", EXTFS_SUPER (VFS_FILE_HANDLER_SUPER (fh
)), file
->ino
->ent
,
1105 file
->ino
->localname
))
1108 if (stat (file
->ino
->localname
, &file_status
) != 0)
1111 file
->ino
->st
.st_size
= file_status
.st_size
;
1113 file
->ino
->st
.st_mtime
= time (NULL
);
1116 if (--VFS_FILE_HANDLER_SUPER (fh
)->fd_usage
== 0)
1117 vfs_stamp_create (vfs_extfs_ops
, VFS_FILE_HANDLER_SUPER (fh
));
1120 if (errno_code
!= 0)
1125 /* --------------------------------------------------------------------------------------------- */
1128 extfs_errno (struct vfs_class
*me
)
1134 /* --------------------------------------------------------------------------------------------- */
1137 extfs_opendir (const vfs_path_t
* vpath
)
1139 struct extfs_super_t
*archive
= NULL
;
1141 struct vfs_s_entry
*entry
;
1144 q
= extfs_get_path (vpath
, &archive
, FL_NONE
);
1147 entry
= extfs_find_entry (VFS_SUPER (archive
)->root
, q
, FL_NONE
);
1150 entry
= extfs_resolve_symlinks (entry
);
1153 if (!S_ISDIR (entry
->ino
->st
.st_mode
))
1154 ERRNOR (ENOTDIR
, NULL
);
1156 info
= g_new (GList
*, 1);
1157 *info
= g_queue_peek_head_link (entry
->ino
->subdir
);
1162 /* --------------------------------------------------------------------------------------------- */
1164 static struct vfs_dirent
*
1165 extfs_readdir (void *data
)
1167 struct vfs_dirent
*dir
;
1168 GList
**info
= (GList
**) data
;
1173 dir
= vfs_dirent_init (NULL
, VFS_ENTRY ((*info
)->data
)->name
, 0); /* FIXME: inode */
1175 *info
= g_list_next (*info
);
1180 /* --------------------------------------------------------------------------------------------- */
1183 extfs_closedir (void *data
)
1189 /* --------------------------------------------------------------------------------------------- */
1193 extfs_stat_move (struct stat
*buf
, const struct vfs_s_inode
*inode
)
1197 #ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
1198 buf
->st_blksize
= RECORDSIZE
;
1200 vfs_adjust_stat (buf
);
1201 #ifdef HAVE_STRUCT_STAT_ST_MTIM
1202 buf
->st_atim
.tv_nsec
= buf
->st_mtim
.tv_nsec
= buf
->st_ctim
.tv_nsec
= 0;
1206 /* --------------------------------------------------------------------------------------------- */
1209 extfs_internal_stat (const vfs_path_t
* vpath
, struct stat
*buf
, gboolean resolve
)
1211 struct extfs_super_t
*archive
;
1213 struct vfs_s_entry
*entry
;
1216 q
= extfs_get_path (vpath
, &archive
, FL_NONE
);
1219 entry
= extfs_find_entry (VFS_SUPER (archive
)->root
, q
, FL_NONE
);
1224 entry
= extfs_resolve_symlinks (entry
);
1228 extfs_stat_move (buf
, entry
->ino
);
1234 /* --------------------------------------------------------------------------------------------- */
1237 extfs_stat (const vfs_path_t
* vpath
, struct stat
*buf
)
1239 return extfs_internal_stat (vpath
, buf
, TRUE
);
1242 /* --------------------------------------------------------------------------------------------- */
1245 extfs_lstat (const vfs_path_t
* vpath
, struct stat
*buf
)
1247 return extfs_internal_stat (vpath
, buf
, FALSE
);
1250 /* --------------------------------------------------------------------------------------------- */
1253 extfs_fstat (void *fh
, struct stat
*buf
)
1255 vfs_file_handler_t
*file
= VFS_FILE_HANDLER (fh
);
1257 extfs_stat_move (buf
, file
->ino
);
1261 /* --------------------------------------------------------------------------------------------- */
1264 extfs_readlink (const vfs_path_t
* vpath
, char *buf
, size_t size
)
1266 struct extfs_super_t
*archive
;
1269 struct vfs_s_entry
*entry
;
1272 q
= extfs_get_path (vpath
, &archive
, FL_NONE
);
1275 entry
= extfs_find_entry (VFS_SUPER (archive
)->root
, q
, FL_NONE
);
1278 if (!S_ISLNK (entry
->ino
->st
.st_mode
))
1280 const vfs_path_element_t
*path_element
;
1282 path_element
= vfs_path_get_by_index (vpath
, -1);
1283 path_element
->class->verrno
= EINVAL
;
1286 len
= strlen (entry
->ino
->linkname
);
1289 /* readlink() does not append a NUL character to buf */
1291 memcpy (buf
, entry
->ino
->linkname
, result
);
1296 /* --------------------------------------------------------------------------------------------- */
1299 extfs_chown (const vfs_path_t
* vpath
, uid_t owner
, gid_t group
)
1307 /* --------------------------------------------------------------------------------------------- */
1310 extfs_chmod (const vfs_path_t
* vpath
, mode_t mode
)
1317 /* --------------------------------------------------------------------------------------------- */
1320 extfs_write (void *fh
, const char *buf
, size_t nbyte
)
1322 vfs_file_handler_t
*file
= VFS_FILE_HANDLER (fh
);
1324 file
->changed
= TRUE
;
1325 return write (file
->handle
, buf
, nbyte
);
1328 /* --------------------------------------------------------------------------------------------- */
1331 extfs_unlink (const vfs_path_t
* vpath
)
1333 struct extfs_super_t
*archive
;
1335 struct vfs_s_entry
*entry
;
1338 q
= extfs_get_path (vpath
, &archive
, FL_NONE
);
1341 entry
= extfs_find_entry (VFS_SUPER (archive
)->root
, q
, FL_NONE
);
1344 entry
= extfs_resolve_symlinks (entry
);
1347 if (S_ISDIR (entry
->ino
->st
.st_mode
))
1349 const vfs_path_element_t
*path_element
;
1351 path_element
= vfs_path_get_by_index (vpath
, -1);
1352 path_element
->class->verrno
= EISDIR
;
1355 if (extfs_cmd (" rm ", archive
, entry
, ""))
1360 vfs_s_free_entry (VFS_SUPER (archive
)->me
, entry
);
1366 /* --------------------------------------------------------------------------------------------- */
1369 extfs_mkdir (const vfs_path_t
* vpath
, mode_t mode
)
1371 struct extfs_super_t
*archive
;
1373 struct vfs_s_entry
*entry
;
1375 const vfs_path_element_t
*path_element
;
1379 path_element
= vfs_path_get_by_index (vpath
, -1);
1380 q
= extfs_get_path (vpath
, &archive
, FL_NONE
);
1383 entry
= extfs_find_entry (VFS_SUPER (archive
)->root
, q
, FL_NONE
);
1386 path_element
->class->verrno
= EEXIST
;
1389 entry
= extfs_find_entry (VFS_SUPER (archive
)->root
, q
, FL_MKDIR
);
1392 entry
= extfs_resolve_symlinks (entry
);
1395 if (!S_ISDIR (entry
->ino
->st
.st_mode
))
1397 path_element
->class->verrno
= ENOTDIR
;
1401 if (extfs_cmd (" mkdir ", archive
, entry
, ""))
1404 vfs_s_free_entry (VFS_SUPER (archive
)->me
, entry
);
1412 /* --------------------------------------------------------------------------------------------- */
1415 extfs_rmdir (const vfs_path_t
* vpath
)
1417 struct extfs_super_t
*archive
;
1419 struct vfs_s_entry
*entry
;
1422 q
= extfs_get_path (vpath
, &archive
, FL_NONE
);
1425 entry
= extfs_find_entry (VFS_SUPER (archive
)->root
, q
, FL_NONE
);
1428 entry
= extfs_resolve_symlinks (entry
);
1431 if (!S_ISDIR (entry
->ino
->st
.st_mode
))
1433 const vfs_path_element_t
*path_element
;
1435 path_element
= vfs_path_get_by_index (vpath
, -1);
1436 path_element
->class->verrno
= ENOTDIR
;
1440 if (extfs_cmd (" rmdir ", archive
, entry
, ""))
1445 vfs_s_free_entry (VFS_SUPER (archive
)->me
, entry
);
1451 /* --------------------------------------------------------------------------------------------- */
1454 extfs_chdir (const vfs_path_t
* vpath
)
1459 data
= extfs_opendir (vpath
);
1462 extfs_closedir (data
);
1467 /* --------------------------------------------------------------------------------------------- */
1470 extfs_lseek (void *fh
, off_t offset
, int whence
)
1472 vfs_file_handler_t
*file
= VFS_FILE_HANDLER (fh
);
1474 return lseek (file
->handle
, offset
, whence
);
1477 /* --------------------------------------------------------------------------------------------- */
1480 extfs_getid (const vfs_path_t
* vpath
)
1482 struct extfs_super_t
*archive
= NULL
;
1485 p
= extfs_get_path (vpath
, &archive
, FL_NO_OPEN
);
1486 return (p
== NULL
? NULL
: (vfsid
) archive
);
1489 /* --------------------------------------------------------------------------------------------- */
1492 extfs_getlocalcopy (const vfs_path_t
* vpath
)
1494 vfs_file_handler_t
*fh
;
1497 fh
= VFS_FILE_HANDLER (extfs_open (vpath
, O_RDONLY
, 0));
1500 if (fh
->ino
->localname
== NULL
)
1502 extfs_close ((void *) fh
);
1505 p
= vfs_path_from_str (fh
->ino
->localname
);
1506 VFS_FILE_HANDLER_SUPER (fh
)->fd_usage
++;
1507 extfs_close ((void *) fh
);
1511 /* --------------------------------------------------------------------------------------------- */
1514 extfs_ungetlocalcopy (const vfs_path_t
* vpath
, const vfs_path_t
* local
, gboolean has_changed
)
1516 vfs_file_handler_t
*fh
;
1518 fh
= VFS_FILE_HANDLER (extfs_open (vpath
, O_RDONLY
, 0));
1522 if (strcmp (fh
->ino
->localname
, vfs_path_get_last_path_str (local
)) == 0)
1524 VFS_FILE_HANDLER_SUPER (fh
)->fd_usage
--;
1527 extfs_close ((void *) fh
);
1532 /* Should not happen */
1533 extfs_close ((void *) fh
);
1538 /* --------------------------------------------------------------------------------------------- */
1541 extfs_get_plugins (const char *where
, gboolean silent
)
1545 const char *filename
;
1547 dirname
= g_build_path (PATH_SEP_STR
, where
, MC_EXTFS_DIR
, (char *) NULL
);
1548 dir
= g_dir_open (dirname
, 0, NULL
);
1550 /* We may not use vfs_die() message or message or similar,
1551 * UI is not initialized at this time and message would not
1552 * appear on screen. */
1556 fprintf (stderr
, _("Warning: cannot open %s directory\n"), dirname
);
1561 if (extfs_plugins
== NULL
)
1562 extfs_plugins
= g_array_sized_new (FALSE
, TRUE
, sizeof (extfs_plugin_info_t
), 32);
1564 while ((filename
= g_dir_read_name (dir
)) != NULL
)
1566 char fullname
[MC_MAXPATHLEN
];
1569 g_snprintf (fullname
, sizeof (fullname
), "%s" PATH_SEP_STR
"%s", dirname
, filename
);
1571 if ((stat (fullname
, &s
) == 0) && S_ISREG (s
.st_mode
) && !S_ISDIR (s
.st_mode
)
1572 && is_exe (s
.st_mode
))
1576 f
= open (fullname
, O_RDONLY
);
1581 extfs_plugin_info_t info
;
1582 gboolean found
= FALSE
;
1586 /* Handle those with a trailing '+', those flag that the
1587 * file system does not require an archive to work
1589 len
= strlen (filename
);
1590 info
.need_archive
= (filename
[len
- 1] != '+');
1591 info
.path
= g_strconcat (dirname
, PATH_SEP_STR
, (char *) NULL
);
1592 info
.prefix
= g_strdup (filename
);
1594 /* prepare to compare file names without trailing '+' */
1595 if (!info
.need_archive
)
1596 info
.prefix
[len
- 1] = '\0';
1598 /* don't overload already found plugin */
1599 for (i
= 0; i
< extfs_plugins
->len
; i
++)
1601 extfs_plugin_info_t
*p
;
1603 p
= &g_array_index (extfs_plugins
, extfs_plugin_info_t
, i
);
1605 /* 2 files with same names cannot be in a directory */
1606 if ((strcmp (info
.path
, p
->path
) != 0)
1607 && (strcmp (info
.prefix
, p
->prefix
) == 0))
1617 g_free (info
.prefix
);
1621 /* restore file name */
1622 if (!info
.need_archive
)
1623 info
.prefix
[len
- 1] = '+';
1624 g_array_append_val (extfs_plugins
, info
);
1636 /* --------------------------------------------------------------------------------------------- */
1639 extfs_init (struct vfs_class
*me
)
1645 /* 1st: scan user directory */
1646 d1
= extfs_get_plugins (mc_config_get_data_path (), TRUE
); /* silent about user dir */
1647 /* 2nd: scan system dir */
1648 d2
= extfs_get_plugins (LIBEXECDIR
, d1
);
1650 return (d1
|| d2
? 1 : 0);
1653 /* --------------------------------------------------------------------------------------------- */
1656 extfs_done (struct vfs_class
*me
)
1660 while (VFS_SUBCLASS (me
)->supers
!= NULL
)
1661 me
->free ((vfsid
) VFS_SUBCLASS (me
)->supers
->data
);
1663 if (extfs_plugins
== NULL
)
1666 for (i
= 0; i
< extfs_plugins
->len
; i
++)
1668 extfs_plugin_info_t
*info
;
1670 info
= &g_array_index (extfs_plugins
, extfs_plugin_info_t
, i
);
1671 g_free (info
->path
);
1672 g_free (info
->prefix
);
1675 g_array_free (extfs_plugins
, TRUE
);
1678 /* --------------------------------------------------------------------------------------------- */
1681 extfs_setctl (const vfs_path_t
* vpath
, int ctlop
, void *arg
)
1685 if (ctlop
== VFS_SETCTL_RUN
)
1693 /* --------------------------------------------------------------------------------------------- */
1694 /*** public functions ****************************************************************************/
1695 /* --------------------------------------------------------------------------------------------- */
1698 vfs_init_extfs (void)
1700 vfs_init_subclass (&extfs_subclass
, "extfs", VFSF_UNKNOWN
, NULL
);
1701 vfs_extfs_ops
->init
= extfs_init
;
1702 vfs_extfs_ops
->done
= extfs_done
;
1703 vfs_extfs_ops
->fill_names
= extfs_fill_names
;
1704 vfs_extfs_ops
->which
= extfs_which
;
1705 vfs_extfs_ops
->open
= extfs_open
;
1706 vfs_extfs_ops
->close
= extfs_close
;
1707 vfs_extfs_ops
->read
= extfs_read
;
1708 vfs_extfs_ops
->write
= extfs_write
;
1709 vfs_extfs_ops
->opendir
= extfs_opendir
;
1710 vfs_extfs_ops
->readdir
= extfs_readdir
;
1711 vfs_extfs_ops
->closedir
= extfs_closedir
;
1712 vfs_extfs_ops
->stat
= extfs_stat
;
1713 vfs_extfs_ops
->lstat
= extfs_lstat
;
1714 vfs_extfs_ops
->fstat
= extfs_fstat
;
1715 vfs_extfs_ops
->chmod
= extfs_chmod
;
1716 vfs_extfs_ops
->chown
= extfs_chown
;
1717 vfs_extfs_ops
->readlink
= extfs_readlink
;
1718 vfs_extfs_ops
->unlink
= extfs_unlink
;
1719 vfs_extfs_ops
->chdir
= extfs_chdir
;
1720 vfs_extfs_ops
->ferrno
= extfs_errno
;
1721 vfs_extfs_ops
->lseek
= extfs_lseek
;
1722 vfs_extfs_ops
->getid
= extfs_getid
;
1723 vfs_extfs_ops
->getlocalcopy
= extfs_getlocalcopy
;
1724 vfs_extfs_ops
->ungetlocalcopy
= extfs_ungetlocalcopy
;
1725 vfs_extfs_ops
->mkdir
= extfs_mkdir
;
1726 vfs_extfs_ops
->rmdir
= extfs_rmdir
;
1727 vfs_extfs_ops
->setctl
= extfs_setctl
;
1728 extfs_subclass
.free_inode
= extfs_free_inode
;
1729 extfs_subclass
.free_archive
= extfs_free_archive
;
1730 vfs_register_class (vfs_extfs_ops
);
1733 /* --------------------------------------------------------------------------------------------- */