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 const char *local_last_path
= NULL
;
525 vfs_path_t
*name_vpath
;
527 memset (&mystat
, 0, sizeof (mystat
));
529 name_vpath
= vfs_path_from_str (name
);
530 info
= &g_array_index (extfs_plugins
, extfs_plugin_info_t
, fstype
);
532 if (info
->need_archive
)
534 if (mc_stat (name_vpath
, &mystat
) == -1)
537 if (!vfs_file_is_local (name_vpath
))
539 local_name_vpath
= mc_getlocalcopy (name_vpath
);
540 if (local_name_vpath
== NULL
)
544 local_last_path
= vfs_path_get_last_path_str (local_name_vpath
);
545 if (local_last_path
== NULL
)
546 tmp
= name_quote (vfs_path_get_last_path_str (name_vpath
), FALSE
);
549 if (local_last_path
!= NULL
)
550 cmd
= g_strconcat (info
->path
, info
->prefix
, " list ", local_last_path
, (char *) NULL
);
551 else if (tmp
!= NULL
)
553 cmd
= g_strconcat (info
->path
, info
->prefix
, " list ", tmp
, (char *) NULL
);
559 result
= mc_popen (cmd
, TRUE
, TRUE
, error
);
565 if (local_name_vpath
!= NULL
)
567 mc_ungetlocalcopy (name_vpath
, local_name_vpath
, FALSE
);
568 vfs_path_free (local_name_vpath
, TRUE
);
573 current_archive
= extfs_super_new (vfs_extfs_ops
, name
, local_name_vpath
, fstype
);
574 current_archive
->rdev
= archive_counter
++;
575 vfs_path_free (local_name_vpath
, TRUE
);
577 mode
= mystat
.st_mode
& 07777;
586 root_entry
= extfs_generate_entry (current_archive
, PATH_SEP_STR
, NULL
, mode
);
587 root_entry
->ino
->st
.st_uid
= mystat
.st_uid
;
588 root_entry
->ino
->st
.st_gid
= mystat
.st_gid
;
589 root_entry
->ino
->st
.st_atime
= mystat
.st_atime
;
590 root_entry
->ino
->st
.st_ctime
= mystat
.st_ctime
;
591 root_entry
->ino
->st
.st_mtime
= mystat
.st_mtime
;
592 root_entry
->ino
->ent
= root_entry
;
593 VFS_SUPER (current_archive
)->root
= root_entry
->ino
;
595 *pparc
= current_archive
;
598 vfs_path_free (name_vpath
, TRUE
);
602 /* --------------------------------------------------------------------------------------------- */
604 * Main loop for reading an archive.
605 * Return 0 on success, -1 on error.
609 extfs_read_archive (mc_pipe_t
* pip
, struct extfs_super_t
*archive
, GError
** error
)
613 GString
*err_msg
= NULL
;
614 GString
*remain_file_name
= NULL
;
618 /* init buffers before call of mc_pread() */
619 pip
->out
.len
= MC_PIPE_BUFSIZE
;
620 pip
->err
.len
= MC_PIPE_BUFSIZE
;
622 mc_pread (pip
, error
);
627 if (pip
->err
.len
> 0)
629 /* join errors/warnings */
631 err_msg
= g_string_new_len (pip
->err
.buf
, pip
->err
.len
);
634 if (err_msg
->str
[err_msg
->len
- 1] != '\n')
635 g_string_append_c (err_msg
, '\n');
636 g_string_append_len (err_msg
, pip
->err
.buf
, pip
->err
.len
);
640 if (pip
->out
.len
== MC_PIPE_STREAM_EOF
)
643 if (pip
->out
.len
== 0)
646 if (pip
->out
.len
== MC_PIPE_ERROR_READ
)
649 while (ret
!= -1 && (buffer
= mc_pstream_get_string (&pip
->out
)) != NULL
)
651 /* handle a \n-separated file list */
653 if (buffer
->str
[buffer
->len
- 1] == '\n')
655 /* entire file name or last chunk */
657 g_string_truncate (buffer
, buffer
->len
- 1);
659 /* join filename chunks */
660 if (remain_file_name
!= NULL
)
662 g_string_append_len (remain_file_name
, buffer
->str
, buffer
->len
);
663 g_string_free (buffer
, TRUE
);
664 buffer
= remain_file_name
;
665 remain_file_name
= NULL
;
670 /* first or middle chunk of file name */
672 if (remain_file_name
== NULL
)
673 remain_file_name
= buffer
;
676 g_string_append_len (remain_file_name
, buffer
->str
, buffer
->len
);
677 g_string_free (buffer
, TRUE
);
683 ret
= extfs_add_file (archive
, buffer
->str
);
685 g_string_free (buffer
, TRUE
);
689 if (remain_file_name
!= NULL
)
690 g_string_free (remain_file_name
, TRUE
);
695 mc_propagate_error (error
, 0, "%s", err_msg
->str
);
697 g_string_free (err_msg
, TRUE
);
703 /* --------------------------------------------------------------------------------------------- */
706 extfs_which (struct vfs_class
*me
, const char *path
)
713 path_len
= strlen (path
);
715 for (i
= 0; i
< extfs_plugins
->len
; i
++)
717 extfs_plugin_info_t
*info
;
719 info
= &g_array_index (extfs_plugins
, extfs_plugin_info_t
, i
);
721 if ((strncmp (path
, info
->prefix
, path_len
) == 0)
722 && ((info
->prefix
[path_len
] == '\0') || (info
->prefix
[path_len
] == '+')))
728 /* --------------------------------------------------------------------------------------------- */
731 extfs_open_and_read_archive (int fstype
, const char *name
, struct extfs_super_t
**archive
)
734 struct extfs_super_t
*a
;
736 GError
*error
= NULL
;
738 pip
= extfs_open_archive (fstype
, name
, archive
, &error
);
744 const extfs_plugin_info_t
*info
;
746 info
= &g_array_index (extfs_plugins
, extfs_plugin_info_t
, fstype
);
747 message (D_ERROR
, MSG_ERROR
, _("Cannot open %s archive\n%s:\n%s"), info
->prefix
, name
,
749 g_error_free (error
);
753 result
= extfs_read_archive (pip
, a
, &error
);
756 VFS_SUPER (a
)->me
->free (VFS_SUPER (a
));
760 message (D_ERROR
, MSG_ERROR
, _("EXTFS virtual file system:\n%s"), error
->message
);
761 g_error_free (error
);
764 mc_pclose (pip
, NULL
);
770 /* --------------------------------------------------------------------------------------------- */
772 * Dissect the path and create corresponding superblock.
775 extfs_get_path (const vfs_path_t
* vpath
, struct extfs_super_t
**archive
, int flags
)
781 const vfs_path_element_t
*path_element
;
782 struct extfs_super_t
*a
= NULL
;
784 path_element
= vfs_path_get_by_index (vpath
, -1);
786 fstype
= extfs_which (path_element
->class, path_element
->vfs_prefix
);
790 archive_name
= vfs_path_to_str_elements_count (vpath
, -1);
792 /* All filesystems should have some local archive, at least it can be PATH_SEP ('/'). */
793 parc
= g_list_find_custom (extfs_subclass
.supers
, archive_name
, extfs_cmp_archive
);
796 a
= EXTFS_SUPER (parc
->data
);
797 vfs_stamp (vfs_extfs_ops
, (vfsid
) a
);
798 g_free (archive_name
);
802 if ((flags
& FL_NO_OPEN
) == 0)
803 result
= extfs_open_and_read_archive (fstype
, archive_name
, &a
);
805 g_free (archive_name
);
809 path_element
->class->verrno
= EIO
;
815 return path_element
->path
;
818 /* --------------------------------------------------------------------------------------------- */
819 /* Return allocated path (without leading slash) inside the archive */
822 extfs_get_path_from_entry (const struct vfs_s_entry
*entry
)
824 const struct vfs_s_entry
*e
;
827 localpath
= g_string_new ("");
829 for (e
= entry
; e
->dir
!= NULL
; e
= e
->dir
->ent
)
831 g_string_prepend (localpath
, e
->name
);
832 if (e
->dir
->ent
->dir
!= NULL
)
833 g_string_prepend_c (localpath
, PATH_SEP
);
836 return g_string_free (localpath
, FALSE
);
839 /* --------------------------------------------------------------------------------------------- */
841 static struct vfs_s_entry
*
842 extfs_resolve_symlinks_int (struct vfs_s_entry
*entry
, GSList
* list
)
844 struct vfs_s_entry
*pent
= NULL
;
846 if (!S_ISLNK (entry
->ino
->st
.st_mode
))
849 if (g_slist_find (list
, entry
) != NULL
)
851 /* Here we protect us against symlink looping */
858 looping
= g_slist_prepend (list
, entry
);
859 pent
= extfs_find_entry_int (entry
->dir
, entry
->ino
->linkname
, looping
, FL_NONE
);
860 looping
= g_slist_delete_link (looping
, looping
);
869 /* --------------------------------------------------------------------------------------------- */
871 static struct vfs_s_entry
*
872 extfs_resolve_symlinks (struct vfs_s_entry
*entry
)
874 struct vfs_s_entry
*res
;
878 res
= extfs_resolve_symlinks_int (entry
, NULL
);
889 /* --------------------------------------------------------------------------------------------- */
892 extfs_get_archive_name (const struct extfs_super_t
*archive
)
894 const char *archive_name
;
896 if (archive
->local_name
!= NULL
)
897 archive_name
= archive
->local_name
;
899 archive_name
= CONST_VFS_SUPER (archive
)->name
;
901 if (archive_name
== NULL
|| *archive_name
== '\0')
902 return g_strdup ("no_archive_name");
907 const vfs_path_element_t
*path_element
;
909 vpath
= vfs_path_from_str (archive_name
);
910 path_element
= vfs_path_get_by_index (vpath
, -1);
911 ret_str
= g_strdup (path_element
->path
);
912 vfs_path_free (vpath
, TRUE
);
917 /* --------------------------------------------------------------------------------------------- */
918 /** Don't pass localname as NULL */
921 extfs_cmd (const char *str_extfs_cmd
, const struct extfs_super_t
*archive
,
922 const struct vfs_s_entry
*entry
, const char *localname
)
926 char *quoted_localname
;
927 char *archive_name
, *quoted_archive_name
;
928 const extfs_plugin_info_t
*info
;
931 GError
*error
= NULL
;
934 file
= extfs_get_path_from_entry (entry
);
935 quoted_file
= name_quote (file
, FALSE
);
938 /* Skip leading "./" (if present) added in name_quote() */
939 file
= extfs_skip_leading_dotslash (quoted_file
);
941 archive_name
= extfs_get_archive_name (archive
);
942 quoted_archive_name
= name_quote (archive_name
, FALSE
);
943 g_free (archive_name
);
944 quoted_localname
= name_quote (localname
, FALSE
);
945 info
= &g_array_index (extfs_plugins
, extfs_plugin_info_t
, archive
->fstype
);
946 cmd
= g_strconcat (info
->path
, info
->prefix
, str_extfs_cmd
,
947 quoted_archive_name
, " ", file
, " ", quoted_localname
, (char *) NULL
);
948 g_free (quoted_file
);
949 g_free (quoted_localname
);
950 g_free (quoted_archive_name
);
952 /* don't read stdout */
953 pip
= mc_popen (cmd
, FALSE
, TRUE
, &error
);
958 message (D_ERROR
, MSG_ERROR
, _("EXTFS virtual file system:\n%s"), error
->message
);
959 g_error_free (error
);
963 pip
->err
.null_term
= TRUE
;
965 mc_pread (pip
, &error
);
968 message (D_ERROR
, MSG_ERROR
, _("EXTFS virtual file system:\n%s"), error
->message
);
969 g_error_free (error
);
972 else if (pip
->err
.len
> 0)
973 message (D_ERROR
, MSG_ERROR
, _("EXTFS virtual file system:\n%s"), pip
->err
.buf
);
975 mc_pclose (pip
, NULL
);
980 /* --------------------------------------------------------------------------------------------- */
983 extfs_run (const vfs_path_t
* vpath
)
985 struct extfs_super_t
*archive
= NULL
;
987 char *q
, *archive_name
, *quoted_archive_name
;
989 const extfs_plugin_info_t
*info
;
991 p
= extfs_get_path (vpath
, &archive
, FL_NONE
);
994 q
= name_quote (p
, FALSE
);
996 archive_name
= extfs_get_archive_name (archive
);
997 quoted_archive_name
= name_quote (archive_name
, FALSE
);
998 g_free (archive_name
);
999 info
= &g_array_index (extfs_plugins
, extfs_plugin_info_t
, archive
->fstype
);
1001 g_strconcat (info
->path
, info
->prefix
, " run ", quoted_archive_name
, " ", q
, (char *) NULL
);
1002 g_free (quoted_archive_name
);
1004 shell_execute (cmd
, 0);
1008 /* --------------------------------------------------------------------------------------------- */
1011 extfs_open (const vfs_path_t
* vpath
, int flags
, mode_t mode
)
1013 vfs_file_handler_t
*extfs_info
;
1014 struct extfs_super_t
*archive
= NULL
;
1016 struct vfs_s_entry
*entry
;
1018 gboolean created
= FALSE
;
1020 q
= extfs_get_path (vpath
, &archive
, FL_NONE
);
1023 entry
= extfs_find_entry (VFS_SUPER (archive
)->root
, q
, FL_NONE
);
1024 if ((entry
== NULL
) && ((flags
& O_CREAT
) != 0))
1026 /* Create new entry */
1027 entry
= extfs_find_entry (VFS_SUPER (archive
)->root
, q
, FL_MKFILE
);
1028 created
= (entry
!= NULL
);
1033 entry
= extfs_resolve_symlinks (entry
);
1037 if (S_ISDIR (entry
->ino
->st
.st_mode
))
1038 ERRNOR (EISDIR
, NULL
);
1040 if (entry
->ino
->localname
== NULL
)
1042 vfs_path_t
*local_filename_vpath
;
1043 const char *local_filename
;
1045 local_handle
= vfs_mkstemps (&local_filename_vpath
, "extfs", entry
->name
);
1047 if (local_handle
== -1)
1049 close (local_handle
);
1050 local_filename
= vfs_path_get_by_index (local_filename_vpath
, -1)->path
;
1052 if (!created
&& ((flags
& O_TRUNC
) == 0)
1053 && extfs_cmd (" copyout ", archive
, entry
, local_filename
))
1055 unlink (local_filename
);
1056 vfs_path_free (local_filename_vpath
, TRUE
);
1060 entry
->ino
->localname
= g_strdup (local_filename
);
1061 vfs_path_free (local_filename_vpath
, TRUE
);
1064 local_handle
= open (entry
->ino
->localname
, NO_LINEAR (flags
), mode
);
1066 if (local_handle
== -1)
1068 /* file exists(may be). Need to drop O_CREAT flag and truncate file content */
1069 flags
= ~O_CREAT
& (NO_LINEAR (flags
) | O_TRUNC
);
1070 local_handle
= open (entry
->ino
->localname
, flags
, mode
);
1073 if (local_handle
== -1)
1076 extfs_info
= g_new (vfs_file_handler_t
, 1);
1077 vfs_s_init_fh (extfs_info
, entry
->ino
, created
);
1078 extfs_info
->handle
= local_handle
;
1080 /* i.e. we had no open files and now we have one */
1081 vfs_rmstamp (vfs_extfs_ops
, (vfsid
) archive
);
1082 VFS_SUPER (archive
)->fd_usage
++;
1086 /* --------------------------------------------------------------------------------------------- */
1089 extfs_read (void *fh
, char *buffer
, size_t count
)
1091 vfs_file_handler_t
*file
= VFS_FILE_HANDLER (fh
);
1093 return read (file
->handle
, buffer
, count
);
1096 /* --------------------------------------------------------------------------------------------- */
1099 extfs_close (void *fh
)
1101 vfs_file_handler_t
*file
= VFS_FILE_HANDLER (fh
);
1104 close (file
->handle
);
1107 /* Commit the file if it has changed */
1110 struct stat file_status
;
1113 (" copyin ", EXTFS_SUPER (VFS_FILE_HANDLER_SUPER (fh
)), file
->ino
->ent
,
1114 file
->ino
->localname
))
1117 if (stat (file
->ino
->localname
, &file_status
) != 0)
1120 file
->ino
->st
.st_size
= file_status
.st_size
;
1122 file
->ino
->st
.st_mtime
= time (NULL
);
1125 if (--VFS_FILE_HANDLER_SUPER (fh
)->fd_usage
== 0)
1126 vfs_stamp_create (vfs_extfs_ops
, VFS_FILE_HANDLER_SUPER (fh
));
1129 if (errno_code
!= 0)
1134 /* --------------------------------------------------------------------------------------------- */
1137 extfs_errno (struct vfs_class
*me
)
1143 /* --------------------------------------------------------------------------------------------- */
1146 extfs_opendir (const vfs_path_t
* vpath
)
1148 struct extfs_super_t
*archive
= NULL
;
1150 struct vfs_s_entry
*entry
;
1153 q
= extfs_get_path (vpath
, &archive
, FL_NONE
);
1156 entry
= extfs_find_entry (VFS_SUPER (archive
)->root
, q
, FL_NONE
);
1159 entry
= extfs_resolve_symlinks (entry
);
1162 if (!S_ISDIR (entry
->ino
->st
.st_mode
))
1163 ERRNOR (ENOTDIR
, NULL
);
1165 info
= g_new (GList
*, 1);
1166 *info
= g_queue_peek_head_link (entry
->ino
->subdir
);
1171 /* --------------------------------------------------------------------------------------------- */
1173 static struct vfs_dirent
*
1174 extfs_readdir (void *data
)
1176 struct vfs_dirent
*dir
;
1177 GList
**info
= (GList
**) data
;
1182 dir
= vfs_dirent_init (NULL
, VFS_ENTRY ((*info
)->data
)->name
, 0); /* FIXME: inode */
1184 *info
= g_list_next (*info
);
1189 /* --------------------------------------------------------------------------------------------- */
1192 extfs_closedir (void *data
)
1198 /* --------------------------------------------------------------------------------------------- */
1202 extfs_stat_move (struct stat
*buf
, const struct vfs_s_inode
*inode
)
1206 #ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
1207 buf
->st_blksize
= RECORDSIZE
;
1209 vfs_adjust_stat (buf
);
1210 #ifdef HAVE_STRUCT_STAT_ST_MTIM
1211 buf
->st_atim
.tv_nsec
= buf
->st_mtim
.tv_nsec
= buf
->st_ctim
.tv_nsec
= 0;
1215 /* --------------------------------------------------------------------------------------------- */
1218 extfs_internal_stat (const vfs_path_t
* vpath
, struct stat
*buf
, gboolean resolve
)
1220 struct extfs_super_t
*archive
;
1222 struct vfs_s_entry
*entry
;
1225 q
= extfs_get_path (vpath
, &archive
, FL_NONE
);
1228 entry
= extfs_find_entry (VFS_SUPER (archive
)->root
, q
, FL_NONE
);
1233 entry
= extfs_resolve_symlinks (entry
);
1237 extfs_stat_move (buf
, entry
->ino
);
1243 /* --------------------------------------------------------------------------------------------- */
1246 extfs_stat (const vfs_path_t
* vpath
, struct stat
*buf
)
1248 return extfs_internal_stat (vpath
, buf
, TRUE
);
1251 /* --------------------------------------------------------------------------------------------- */
1254 extfs_lstat (const vfs_path_t
* vpath
, struct stat
*buf
)
1256 return extfs_internal_stat (vpath
, buf
, FALSE
);
1259 /* --------------------------------------------------------------------------------------------- */
1262 extfs_fstat (void *fh
, struct stat
*buf
)
1264 vfs_file_handler_t
*file
= VFS_FILE_HANDLER (fh
);
1266 extfs_stat_move (buf
, file
->ino
);
1270 /* --------------------------------------------------------------------------------------------- */
1273 extfs_readlink (const vfs_path_t
* vpath
, char *buf
, size_t size
)
1275 struct extfs_super_t
*archive
;
1278 struct vfs_s_entry
*entry
;
1281 q
= extfs_get_path (vpath
, &archive
, FL_NONE
);
1284 entry
= extfs_find_entry (VFS_SUPER (archive
)->root
, q
, FL_NONE
);
1287 if (!S_ISLNK (entry
->ino
->st
.st_mode
))
1289 const vfs_path_element_t
*path_element
;
1291 path_element
= vfs_path_get_by_index (vpath
, -1);
1292 path_element
->class->verrno
= EINVAL
;
1295 len
= strlen (entry
->ino
->linkname
);
1298 /* readlink() does not append a NUL character to buf */
1300 memcpy (buf
, entry
->ino
->linkname
, result
);
1305 /* --------------------------------------------------------------------------------------------- */
1308 extfs_chown (const vfs_path_t
* vpath
, uid_t owner
, gid_t group
)
1316 /* --------------------------------------------------------------------------------------------- */
1319 extfs_chmod (const vfs_path_t
* vpath
, mode_t mode
)
1326 /* --------------------------------------------------------------------------------------------- */
1329 extfs_write (void *fh
, const char *buf
, size_t nbyte
)
1331 vfs_file_handler_t
*file
= VFS_FILE_HANDLER (fh
);
1333 file
->changed
= TRUE
;
1334 return write (file
->handle
, buf
, nbyte
);
1337 /* --------------------------------------------------------------------------------------------- */
1340 extfs_unlink (const vfs_path_t
* vpath
)
1342 struct extfs_super_t
*archive
;
1344 struct vfs_s_entry
*entry
;
1347 q
= extfs_get_path (vpath
, &archive
, FL_NONE
);
1350 entry
= extfs_find_entry (VFS_SUPER (archive
)->root
, q
, FL_NONE
);
1353 entry
= extfs_resolve_symlinks (entry
);
1356 if (S_ISDIR (entry
->ino
->st
.st_mode
))
1358 const vfs_path_element_t
*path_element
;
1360 path_element
= vfs_path_get_by_index (vpath
, -1);
1361 path_element
->class->verrno
= EISDIR
;
1364 if (extfs_cmd (" rm ", archive
, entry
, ""))
1369 vfs_s_free_entry (VFS_SUPER (archive
)->me
, entry
);
1375 /* --------------------------------------------------------------------------------------------- */
1378 extfs_mkdir (const vfs_path_t
* vpath
, mode_t mode
)
1380 struct extfs_super_t
*archive
;
1382 struct vfs_s_entry
*entry
;
1384 const vfs_path_element_t
*path_element
;
1388 path_element
= vfs_path_get_by_index (vpath
, -1);
1389 q
= extfs_get_path (vpath
, &archive
, FL_NONE
);
1392 entry
= extfs_find_entry (VFS_SUPER (archive
)->root
, q
, FL_NONE
);
1395 path_element
->class->verrno
= EEXIST
;
1398 entry
= extfs_find_entry (VFS_SUPER (archive
)->root
, q
, FL_MKDIR
);
1401 entry
= extfs_resolve_symlinks (entry
);
1404 if (!S_ISDIR (entry
->ino
->st
.st_mode
))
1406 path_element
->class->verrno
= ENOTDIR
;
1410 if (extfs_cmd (" mkdir ", archive
, entry
, ""))
1413 vfs_s_free_entry (VFS_SUPER (archive
)->me
, entry
);
1421 /* --------------------------------------------------------------------------------------------- */
1424 extfs_rmdir (const vfs_path_t
* vpath
)
1426 struct extfs_super_t
*archive
;
1428 struct vfs_s_entry
*entry
;
1431 q
= extfs_get_path (vpath
, &archive
, FL_NONE
);
1434 entry
= extfs_find_entry (VFS_SUPER (archive
)->root
, q
, FL_NONE
);
1437 entry
= extfs_resolve_symlinks (entry
);
1440 if (!S_ISDIR (entry
->ino
->st
.st_mode
))
1442 const vfs_path_element_t
*path_element
;
1444 path_element
= vfs_path_get_by_index (vpath
, -1);
1445 path_element
->class->verrno
= ENOTDIR
;
1449 if (extfs_cmd (" rmdir ", archive
, entry
, ""))
1454 vfs_s_free_entry (VFS_SUPER (archive
)->me
, entry
);
1460 /* --------------------------------------------------------------------------------------------- */
1463 extfs_chdir (const vfs_path_t
* vpath
)
1468 data
= extfs_opendir (vpath
);
1471 extfs_closedir (data
);
1476 /* --------------------------------------------------------------------------------------------- */
1479 extfs_lseek (void *fh
, off_t offset
, int whence
)
1481 vfs_file_handler_t
*file
= VFS_FILE_HANDLER (fh
);
1483 return lseek (file
->handle
, offset
, whence
);
1486 /* --------------------------------------------------------------------------------------------- */
1489 extfs_getid (const vfs_path_t
* vpath
)
1491 struct extfs_super_t
*archive
= NULL
;
1494 p
= extfs_get_path (vpath
, &archive
, FL_NO_OPEN
);
1495 return (p
== NULL
? NULL
: (vfsid
) archive
);
1498 /* --------------------------------------------------------------------------------------------- */
1501 extfs_getlocalcopy (const vfs_path_t
* vpath
)
1503 vfs_file_handler_t
*fh
;
1506 fh
= VFS_FILE_HANDLER (extfs_open (vpath
, O_RDONLY
, 0));
1509 if (fh
->ino
->localname
== NULL
)
1511 extfs_close ((void *) fh
);
1514 p
= vfs_path_from_str (fh
->ino
->localname
);
1515 VFS_FILE_HANDLER_SUPER (fh
)->fd_usage
++;
1516 extfs_close ((void *) fh
);
1520 /* --------------------------------------------------------------------------------------------- */
1523 extfs_ungetlocalcopy (const vfs_path_t
* vpath
, const vfs_path_t
* local
, gboolean has_changed
)
1525 vfs_file_handler_t
*fh
;
1527 fh
= VFS_FILE_HANDLER (extfs_open (vpath
, O_RDONLY
, 0));
1531 if (strcmp (fh
->ino
->localname
, vfs_path_get_last_path_str (local
)) == 0)
1533 VFS_FILE_HANDLER_SUPER (fh
)->fd_usage
--;
1536 extfs_close ((void *) fh
);
1541 /* Should not happen */
1542 extfs_close ((void *) fh
);
1547 /* --------------------------------------------------------------------------------------------- */
1550 extfs_get_plugins (const char *where
, gboolean silent
)
1554 const char *filename
;
1556 dirname
= g_build_path (PATH_SEP_STR
, where
, MC_EXTFS_DIR
, (char *) NULL
);
1557 dir
= g_dir_open (dirname
, 0, NULL
);
1559 /* We may not use vfs_die() message or message or similar,
1560 * UI is not initialized at this time and message would not
1561 * appear on screen. */
1565 fprintf (stderr
, _("Warning: cannot open %s directory\n"), dirname
);
1570 if (extfs_plugins
== NULL
)
1571 extfs_plugins
= g_array_sized_new (FALSE
, TRUE
, sizeof (extfs_plugin_info_t
), 32);
1573 while ((filename
= g_dir_read_name (dir
)) != NULL
)
1575 char fullname
[MC_MAXPATHLEN
];
1578 g_snprintf (fullname
, sizeof (fullname
), "%s" PATH_SEP_STR
"%s", dirname
, filename
);
1580 if ((stat (fullname
, &s
) == 0) && S_ISREG (s
.st_mode
) && !S_ISDIR (s
.st_mode
)
1581 && is_exe (s
.st_mode
))
1585 f
= open (fullname
, O_RDONLY
);
1590 extfs_plugin_info_t info
;
1591 gboolean found
= FALSE
;
1595 /* Handle those with a trailing '+', those flag that the
1596 * file system does not require an archive to work
1598 len
= strlen (filename
);
1599 info
.need_archive
= (filename
[len
- 1] != '+');
1600 info
.path
= g_strconcat (dirname
, PATH_SEP_STR
, (char *) NULL
);
1601 info
.prefix
= g_strdup (filename
);
1603 /* prepare to compare file names without trailing '+' */
1604 if (!info
.need_archive
)
1605 info
.prefix
[len
- 1] = '\0';
1607 /* don't overload already found plugin */
1608 for (i
= 0; i
< extfs_plugins
->len
; i
++)
1610 extfs_plugin_info_t
*p
;
1612 p
= &g_array_index (extfs_plugins
, extfs_plugin_info_t
, i
);
1614 /* 2 files with same names cannot be in a directory */
1615 if ((strcmp (info
.path
, p
->path
) != 0)
1616 && (strcmp (info
.prefix
, p
->prefix
) == 0))
1626 g_free (info
.prefix
);
1630 /* restore file name */
1631 if (!info
.need_archive
)
1632 info
.prefix
[len
- 1] = '+';
1633 g_array_append_val (extfs_plugins
, info
);
1645 /* --------------------------------------------------------------------------------------------- */
1648 extfs_init (struct vfs_class
*me
)
1654 /* 1st: scan user directory */
1655 d1
= extfs_get_plugins (mc_config_get_data_path (), TRUE
); /* silent about user dir */
1656 /* 2nd: scan system dir */
1657 d2
= extfs_get_plugins (LIBEXECDIR
, d1
);
1659 return (d1
|| d2
? 1 : 0);
1662 /* --------------------------------------------------------------------------------------------- */
1665 extfs_done (struct vfs_class
*me
)
1669 while (VFS_SUBCLASS (me
)->supers
!= NULL
)
1670 me
->free ((vfsid
) VFS_SUBCLASS (me
)->supers
->data
);
1672 if (extfs_plugins
== NULL
)
1675 for (i
= 0; i
< extfs_plugins
->len
; i
++)
1677 extfs_plugin_info_t
*info
;
1679 info
= &g_array_index (extfs_plugins
, extfs_plugin_info_t
, i
);
1680 g_free (info
->path
);
1681 g_free (info
->prefix
);
1684 g_array_free (extfs_plugins
, TRUE
);
1687 /* --------------------------------------------------------------------------------------------- */
1690 extfs_setctl (const vfs_path_t
* vpath
, int ctlop
, void *arg
)
1694 if (ctlop
== VFS_SETCTL_RUN
)
1702 /* --------------------------------------------------------------------------------------------- */
1703 /*** public functions ****************************************************************************/
1704 /* --------------------------------------------------------------------------------------------- */
1707 vfs_init_extfs (void)
1709 vfs_init_subclass (&extfs_subclass
, "extfs", VFSF_UNKNOWN
, NULL
);
1710 vfs_extfs_ops
->init
= extfs_init
;
1711 vfs_extfs_ops
->done
= extfs_done
;
1712 vfs_extfs_ops
->fill_names
= extfs_fill_names
;
1713 vfs_extfs_ops
->which
= extfs_which
;
1714 vfs_extfs_ops
->open
= extfs_open
;
1715 vfs_extfs_ops
->close
= extfs_close
;
1716 vfs_extfs_ops
->read
= extfs_read
;
1717 vfs_extfs_ops
->write
= extfs_write
;
1718 vfs_extfs_ops
->opendir
= extfs_opendir
;
1719 vfs_extfs_ops
->readdir
= extfs_readdir
;
1720 vfs_extfs_ops
->closedir
= extfs_closedir
;
1721 vfs_extfs_ops
->stat
= extfs_stat
;
1722 vfs_extfs_ops
->lstat
= extfs_lstat
;
1723 vfs_extfs_ops
->fstat
= extfs_fstat
;
1724 vfs_extfs_ops
->chmod
= extfs_chmod
;
1725 vfs_extfs_ops
->chown
= extfs_chown
;
1726 vfs_extfs_ops
->readlink
= extfs_readlink
;
1727 vfs_extfs_ops
->unlink
= extfs_unlink
;
1728 vfs_extfs_ops
->chdir
= extfs_chdir
;
1729 vfs_extfs_ops
->ferrno
= extfs_errno
;
1730 vfs_extfs_ops
->lseek
= extfs_lseek
;
1731 vfs_extfs_ops
->getid
= extfs_getid
;
1732 vfs_extfs_ops
->getlocalcopy
= extfs_getlocalcopy
;
1733 vfs_extfs_ops
->ungetlocalcopy
= extfs_ungetlocalcopy
;
1734 vfs_extfs_ops
->mkdir
= extfs_mkdir
;
1735 vfs_extfs_ops
->rmdir
= extfs_rmdir
;
1736 vfs_extfs_ops
->setctl
= extfs_setctl
;
1737 extfs_subclass
.free_inode
= extfs_free_inode
;
1738 extfs_subclass
.free_archive
= extfs_free_archive
;
1739 vfs_register_class (vfs_extfs_ops
);
1742 /* --------------------------------------------------------------------------------------------- */