Ticket #81: Fixup of extfs.
[midnight-commander.git] / lib / vfs / mc-vfs / extfs.c
blob12c0eeb1ebd1bd1c9b0602c7e294347981456403
1 /* Virtual File System: External file system.
2 Copyright (C) 1995, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
3 2006, 2007, 2009 Free Software Foundation, Inc.
5 Written by: 1995 Jakub Jelinek
6 Rewritten by: 1998 Pavel Machek
7 Additional changes by: 1999 Andrew T. Veliath
9 This program is free software; you can redistribute it and/or
10 modify it under the terms of the GNU Library General Public License
11 as published by the Free Software Foundation; either version 2 of
12 the License, or (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU Library General Public License for more details.
19 You should have received a copy of the GNU Library General Public
20 License along with this program; if not, write to the Free Software
21 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
23 /**
24 * \file
25 * \brief Source: Virtual File System: External file system
26 * \author Jakub Jelinek
27 * \author Pavel Machek
28 * \author Andrew T. Veliath
29 * \date 1995, 1998, 1999
32 /* Namespace: init_extfs */
34 #include <config.h>
36 #include <stdio.h>
37 #include <ctype.h>
38 #include <string.h>
39 #include <stdlib.h>
40 #include <sys/types.h>
41 #include <sys/stat.h>
42 #include <unistd.h>
43 #include <signal.h>
44 #include <fcntl.h>
45 #include <errno.h>
46 #include <sys/wait.h>
47 #include <unistd.h>
49 #include "lib/global.h"
50 #include "lib/fileloc.h"
51 #include "lib/util.h"
52 #include "lib/widget.h" /* message() */
54 #include "src/main.h" /* shell */
55 #include "src/execute.h" /* For shell_execute */
57 #include "vfs-impl.h"
58 #include "utilvfs.h"
59 #include "gc.h" /* vfs_rmstamp */
61 /*** global variables ****************************************************************************/
63 GArray *extfs_plugins = NULL;
65 /*** file scope macro definitions ****************************************************************/
67 #undef ERRNOR
68 #define ERRNOR(x,y) do { my_errno = x; return y; } while(0)
70 #define RECORDSIZE 512
72 /*** file scope type declarations ****************************************************************/
74 struct inode
76 nlink_t nlink;
77 struct entry *first_in_subdir; /* only used if this is a directory */
78 struct entry *last_in_subdir;
79 ino_t inode; /* This is inode # */
80 dev_t dev; /* This is an internal identification of the extfs archive */
81 struct archive *archive; /* And this is an archive structure */
82 dev_t rdev;
83 mode_t mode;
84 uid_t uid;
85 gid_t gid;
86 off_t size;
87 time_t mtime;
88 char *linkname;
89 time_t atime;
90 time_t ctime;
91 char *local_filename;
94 struct entry
96 struct entry *next_in_dir;
97 struct entry *dir;
98 char *name;
99 struct inode *inode;
102 struct pseudofile
104 struct archive *archive;
105 gboolean has_changed;
106 int local_handle;
107 struct entry *entry;
110 struct archive
112 int fstype;
113 char *name;
114 char *local_name;
115 struct stat local_stat;
116 dev_t rdev;
117 int fd_usage;
118 ino_t inode_counter;
119 struct entry *root_entry;
120 struct archive *next;
123 typedef struct
125 char *path;
126 char *prefix;
127 gboolean need_archive;
128 } extfs_plugin_info_t;
130 /*** file scope variables ************************************************************************/
132 static gboolean errloop;
133 static gboolean notadir;
135 static struct vfs_class vfs_extfs_ops;
136 static struct archive *first_archive = NULL;
137 static int my_errno = 0;
139 /*** file scope functions ************************************************************************/
140 /* --------------------------------------------------------------------------------------------- */
142 static void extfs_remove_entry (struct entry *e);
143 static void extfs_free (vfsid id);
144 static void extfs_free_entry (struct entry *e);
145 static struct entry *extfs_resolve_symlinks_int (struct entry *entry, GSList * list);
147 /* --------------------------------------------------------------------------------------------- */
149 static void
150 extfs_make_dots (struct entry *ent)
152 struct entry *entry = g_new (struct entry, 1);
153 struct entry *parentry = ent->dir;
154 struct inode *inode = ent->inode, *parent;
156 parent = (parentry != NULL) ? parentry->inode : NULL;
157 entry->name = g_strdup (".");
158 entry->inode = inode;
159 entry->dir = ent;
160 inode->local_filename = NULL;
161 inode->first_in_subdir = entry;
162 inode->nlink++;
164 entry->next_in_dir = g_new (struct entry, 1);
165 entry = entry->next_in_dir;
166 entry->name = g_strdup ("..");
167 inode->last_in_subdir = entry;
168 entry->next_in_dir = NULL;
169 if (parent != NULL)
171 entry->inode = parent;
172 entry->dir = parentry;
173 parent->nlink++;
175 else
177 entry->inode = inode;
178 entry->dir = ent;
179 inode->nlink++;
183 /* --------------------------------------------------------------------------------------------- */
185 static struct entry *
186 extfs_generate_entry (struct archive *archive,
187 const char *name, struct entry *parentry, mode_t mode)
189 mode_t myumask;
190 struct inode *inode, *parent;
191 struct entry *entry;
193 parent = (parentry != NULL) ? parentry->inode : NULL;
194 entry = g_new (struct entry, 1);
196 entry->name = g_strdup (name);
197 entry->next_in_dir = NULL;
198 entry->dir = parentry;
199 if (parent != NULL)
201 parent->last_in_subdir->next_in_dir = entry;
202 parent->last_in_subdir = entry;
204 inode = g_new (struct inode, 1);
205 entry->inode = inode;
206 inode->local_filename = NULL;
207 inode->linkname = NULL;
208 inode->last_in_subdir = NULL;
209 inode->inode = (archive->inode_counter)++;
210 inode->dev = archive->rdev;
211 inode->archive = archive;
212 myumask = umask (022);
213 umask (myumask);
214 inode->mode = mode & ~myumask;
215 mode = inode->mode;
216 inode->rdev = 0;
217 inode->uid = getuid ();
218 inode->gid = getgid ();
219 inode->size = 0;
220 inode->mtime = time (NULL);
221 inode->atime = inode->mtime;
222 inode->ctime = inode->mtime;
223 inode->nlink = 1;
224 if (S_ISDIR (mode))
225 extfs_make_dots (entry);
226 return entry;
229 /* --------------------------------------------------------------------------------------------- */
231 static struct entry *
232 extfs_find_entry_int (struct entry *dir, char *name, GSList * list,
233 gboolean make_dirs, gboolean make_file)
235 struct entry *pent, *pdir;
236 char *p, *q, *name_end;
237 char c = PATH_SEP;
239 if (g_path_is_absolute (name))
241 /* Handle absolute paths */
242 name = (char *) g_path_skip_root (name);
243 dir = dir->inode->archive->root_entry;
246 pent = dir;
247 p = name;
248 name_end = name + strlen (name);
250 q = strchr (p, PATH_SEP);
251 if (q == '\0')
252 q = strchr (p, '\0');
254 while ((pent != NULL) && (c != '\0') && (*p != '\0'))
256 c = *q;
257 *q = '\0';
259 if (strcmp (p, ".") != 0)
261 if (strcmp (p, "..") == 0)
262 pent = pent->dir;
263 else
265 pent = extfs_resolve_symlinks_int (pent, list);
266 if (pent == NULL)
268 *q = c;
269 return NULL;
271 if (!S_ISDIR (pent->inode->mode))
273 *q = c;
274 notadir = TRUE;
275 return NULL;
278 pdir = pent;
279 for (pent = pent->inode->first_in_subdir; pent != NULL; pent = pent->next_in_dir)
280 /* Hack: I keep the original semanthic unless
281 q+1 would break in the strchr */
282 if (strcmp (pent->name, p) == 0)
284 if (q + 1 > name_end)
286 *q = c;
287 notadir = !S_ISDIR (pent->inode->mode);
288 return pent;
290 break;
293 /* When we load archive, we create automagically
294 * non-existant directories
296 if (pent == NULL && make_dirs)
297 pent = extfs_generate_entry (dir->inode->archive, p, pdir, S_IFDIR | 0777);
298 if (pent == NULL && make_file)
299 pent = extfs_generate_entry (dir->inode->archive, p, pdir, S_IFREG | 0666);
302 /* Next iteration */
303 *q = c;
304 p = q + 1;
305 q = strchr (p, PATH_SEP);
306 if (q == '\0')
307 q = strchr (p, '\0');
309 if (pent == NULL)
310 my_errno = ENOENT;
311 return pent;
314 /* --------------------------------------------------------------------------------------------- */
316 static struct entry *
317 extfs_find_entry (struct entry *dir, char *name, gboolean make_dirs, gboolean make_file)
319 struct entry *res;
321 errloop = FALSE;
322 notadir = FALSE;
324 res = extfs_find_entry_int (dir, name, NULL, make_dirs, make_file);
325 if (res == NULL)
327 if (errloop)
328 my_errno = ELOOP;
329 else if (notadir)
330 my_errno = ENOTDIR;
332 return res;
335 /* --------------------------------------------------------------------------------------------- */
337 static void
338 extfs_fill_names (struct vfs_class *me, fill_names_f func)
340 struct archive *a = first_archive;
342 (void) me;
344 while (a != NULL)
346 extfs_plugin_info_t *info;
347 char *name;
349 info = &g_array_index (extfs_plugins, extfs_plugin_info_t, a->fstype);
350 name = g_strconcat (a->name ? a->name : "", "#", info->prefix, (char *) NULL);
351 func (name);
352 g_free (name);
353 a = a->next;
357 /* --------------------------------------------------------------------------------------------- */
359 static void
360 extfs_free_archive (struct archive *archive)
362 extfs_free_entry (archive->root_entry);
363 if (archive->local_name != NULL)
365 struct stat my;
367 mc_stat (archive->local_name, &my);
368 mc_ungetlocalcopy (archive->name, archive->local_name,
369 archive->local_stat.st_mtime != my.st_mtime);
370 g_free (archive->local_name);
372 g_free (archive->name);
373 g_free (archive);
376 /* --------------------------------------------------------------------------------------------- */
378 static FILE *
379 extfs_open_archive (int fstype, const char *name, struct archive **pparc)
381 const extfs_plugin_info_t *info;
382 static dev_t archive_counter = 0;
383 FILE *result;
384 mode_t mode;
385 char *cmd;
386 struct stat mystat;
387 struct archive *current_archive;
388 struct entry *root_entry;
389 char *local_name = NULL, *tmp = NULL;
391 info = &g_array_index (extfs_plugins, extfs_plugin_info_t, fstype);
393 if (info->need_archive)
395 if (mc_stat (name, &mystat) == -1)
396 return NULL;
398 if (!vfs_file_is_local (name))
400 local_name = mc_getlocalcopy (name);
401 if (local_name == NULL)
402 return NULL;
405 tmp = name_quote (name, 0);
408 cmd = g_strconcat (info->path, info->prefix, " list ",
409 local_name != NULL ? local_name : tmp, (char *) NULL);
410 g_free (tmp);
412 open_error_pipe ();
413 result = popen (cmd, "r");
414 g_free (cmd);
415 if (result == NULL)
417 close_error_pipe (D_ERROR, NULL);
418 if (local_name != NULL)
420 mc_ungetlocalcopy (name, local_name, 0);
421 g_free (local_name);
423 return NULL;
426 #ifdef ___QNXNTO__
427 setvbuf (result, NULL, _IONBF, 0);
428 #endif
430 current_archive = g_new (struct archive, 1);
431 current_archive->fstype = fstype;
432 current_archive->name = name ? g_strdup (name) : NULL;
433 current_archive->local_name = local_name;
435 if (local_name != NULL)
436 mc_stat (local_name, &current_archive->local_stat);
437 current_archive->inode_counter = 0;
438 current_archive->fd_usage = 0;
439 current_archive->rdev = archive_counter++;
440 current_archive->next = first_archive;
441 first_archive = current_archive;
442 mode = mystat.st_mode & 07777;
443 if (mode & 0400)
444 mode |= 0100;
445 if (mode & 0040)
446 mode |= 0010;
447 if (mode & 0004)
448 mode |= 0001;
449 mode |= S_IFDIR;
450 root_entry = extfs_generate_entry (current_archive, PATH_SEP_STR, NULL, mode);
451 root_entry->inode->uid = mystat.st_uid;
452 root_entry->inode->gid = mystat.st_gid;
453 root_entry->inode->atime = mystat.st_atime;
454 root_entry->inode->ctime = mystat.st_ctime;
455 root_entry->inode->mtime = mystat.st_mtime;
456 current_archive->root_entry = root_entry;
458 *pparc = current_archive;
460 return result;
463 /* --------------------------------------------------------------------------------------------- */
465 * Main loop for reading an archive.
466 * Return 0 on success, -1 on error.
469 static int
470 extfs_read_archive (int fstype, const char *name, struct archive **pparc)
472 FILE *extfsd;
473 const extfs_plugin_info_t *info;
474 char *buffer;
475 struct archive *current_archive;
476 char *current_file_name, *current_link_name;
478 info = &g_array_index (extfs_plugins, extfs_plugin_info_t, fstype);
480 extfsd = extfs_open_archive (fstype, name, &current_archive);
482 if (extfsd == NULL)
484 message (D_ERROR, MSG_ERROR, _("Cannot open %s archive\n%s"), info->prefix, name);
485 return -1;
488 buffer = g_malloc (BUF_4K);
489 while (fgets (buffer, BUF_4K, extfsd) != NULL)
491 struct stat hstat;
493 current_link_name = NULL;
494 if (vfs_parse_ls_lga (buffer, &hstat, &current_file_name, &current_link_name, NULL))
496 struct entry *entry, *pent;
497 struct inode *inode;
498 char *p, *q, *cfn = current_file_name;
500 if (*cfn != '\0')
502 if (*cfn == PATH_SEP)
503 cfn++;
504 p = strchr (cfn, '\0');
505 if (p != cfn && *(p - 1) == PATH_SEP)
506 *(p - 1) = '\0';
507 p = strrchr (cfn, PATH_SEP);
508 if (p == NULL)
510 p = cfn;
511 q = strchr (cfn, '\0');
513 else
515 *(p++) = '\0';
516 q = cfn;
518 if (S_ISDIR (hstat.st_mode) && (strcmp (p, ".") == 0 || strcmp (p, "..") == 0))
519 goto read_extfs_continue;
520 pent = extfs_find_entry (current_archive->root_entry, q, TRUE, FALSE);
521 if (pent == NULL)
523 /* FIXME: Should clean everything one day */
524 g_free (buffer);
525 pclose (extfsd);
526 close_error_pipe (D_ERROR, _("Inconsistent extfs archive"));
527 return -1;
529 entry = g_new (struct entry, 1);
530 entry->name = g_strdup (p);
531 entry->next_in_dir = NULL;
532 entry->dir = pent;
533 if (pent->inode->last_in_subdir)
535 pent->inode->last_in_subdir->next_in_dir = entry;
536 pent->inode->last_in_subdir = entry;
538 if (!S_ISLNK (hstat.st_mode) && (current_link_name != NULL))
540 pent = extfs_find_entry (current_archive->root_entry,
541 current_link_name, FALSE, FALSE);
542 if (pent == NULL)
544 /* FIXME: Should clean everything one day */
545 g_free (buffer);
546 pclose (extfsd);
547 close_error_pipe (D_ERROR, _("Inconsistent extfs archive"));
548 return -1;
551 entry->inode = pent->inode;
552 pent->inode->nlink++;
554 else
556 inode = g_new (struct inode, 1);
557 entry->inode = inode;
558 inode->local_filename = NULL;
559 inode->inode = (current_archive->inode_counter)++;
560 inode->nlink = 1;
561 inode->dev = current_archive->rdev;
562 inode->archive = current_archive;
563 inode->mode = hstat.st_mode;
564 #ifdef HAVE_STRUCT_STAT_ST_RDEV
565 inode->rdev = hstat.st_rdev;
566 #else
567 inode->rdev = 0;
568 #endif
569 inode->uid = hstat.st_uid;
570 inode->gid = hstat.st_gid;
571 inode->size = hstat.st_size;
572 inode->mtime = hstat.st_mtime;
573 inode->atime = hstat.st_atime;
574 inode->ctime = hstat.st_ctime;
575 inode->first_in_subdir = NULL;
576 inode->last_in_subdir = NULL;
577 if (current_link_name != NULL && S_ISLNK (hstat.st_mode))
579 inode->linkname = current_link_name;
580 current_link_name = NULL;
582 else
584 if (S_ISLNK (hstat.st_mode))
585 inode->mode &= ~S_IFLNK; /* You *DON'T* want to do this always */
586 inode->linkname = NULL;
588 if (S_ISDIR (hstat.st_mode))
589 extfs_make_dots (entry);
592 read_extfs_continue:
593 g_free (current_file_name);
594 g_free (current_link_name);
597 g_free (buffer);
599 /* Check if extfs 'list' returned 0 */
600 if (pclose (extfsd) != 0)
602 extfs_free (current_archive);
603 close_error_pipe (D_ERROR, _("Inconsistent extfs archive"));
604 return -1;
607 close_error_pipe (D_ERROR, NULL);
608 *pparc = current_archive;
609 return 0;
612 /* --------------------------------------------------------------------------------------------- */
614 static int
615 extfs_which (struct vfs_class *me, const char *path)
617 size_t path_len;
618 size_t i;
620 (void) me;
622 path_len = strlen (path);
624 for (i = 0; i < extfs_plugins->len; i++)
626 extfs_plugin_info_t *info;
628 info = &g_array_index (extfs_plugins, extfs_plugin_info_t, i);
630 if ((strncmp (path, info->prefix, path_len) == 0)
631 && ((info->prefix[path_len] == '\0') || (info->prefix[path_len] == '+')))
632 return i;
634 return -1;
637 /* --------------------------------------------------------------------------------------------- */
639 * Dissect the path and create corresponding superblock. Note that inname
640 * can be changed and the result may point inside the original string.
643 static char *
644 extfs_get_path_mangle (struct vfs_class *me, char *inname, struct archive **archive,
645 gboolean do_not_open)
647 char *local, *op;
648 const char *archive_name;
649 int result = -1;
650 struct archive *parc;
651 int fstype;
653 archive_name = inname;
654 vfs_split (inname, &local, &op);
656 fstype = extfs_which (me, op);
658 if (fstype == -1)
659 return NULL;
660 if (local == NULL)
661 local = inname + strlen (inname);
664 * All filesystems should have some local archive, at least
665 * it can be PATH_SEP ('/').
667 for (parc = first_archive; parc != NULL; parc = parc->next)
668 if (parc->name != NULL)
670 if (strcmp (parc->name, archive_name) == 0)
672 vfs_stamp (&vfs_extfs_ops, (vfsid) parc);
673 goto return_success;
677 result = do_not_open ? -1 : extfs_read_archive (fstype, archive_name, &parc);
678 if (result == -1)
679 ERRNOR (EIO, NULL);
681 return_success:
682 *archive = parc;
683 return local;
686 /* --------------------------------------------------------------------------------------------- */
688 * Dissect the path and create corresponding superblock.
689 * The result should be freed.
692 static char *
693 extfs_get_path (struct vfs_class *me, const char *inname,
694 struct archive **archive, gboolean do_not_open)
696 char *buf, *res, *res2;
698 buf = g_strdup (inname);
699 res = extfs_get_path_mangle (me, buf, archive, do_not_open);
700 res2 = g_strdup (res);
701 g_free (buf);
702 return res2;
705 /* --------------------------------------------------------------------------------------------- */
706 /* Return allocated path (without leading slash) inside the archive */
708 static char *
709 extfs_get_path_from_entry (struct entry *entry)
711 GString *localpath;
713 localpath = g_string_new ("");
715 while (entry->dir != NULL)
717 g_string_prepend (localpath, entry->name);
718 if (entry->dir->dir != NULL)
719 g_string_prepend_c (localpath, PATH_SEP);
720 entry = entry->dir;
723 return g_string_free (localpath, FALSE);
726 /* --------------------------------------------------------------------------------------------- */
728 static struct entry *
729 extfs_resolve_symlinks_int (struct entry *entry, GSList * list)
731 struct entry *pent = NULL;
733 if (!S_ISLNK (entry->inode->mode))
734 return entry;
736 if (g_slist_find (list, entry) != NULL)
738 /* Here we protect us against symlink looping */
739 errloop = TRUE;
741 else
743 GSList *looping;
745 looping = g_slist_prepend (list, entry);
746 pent = extfs_find_entry_int (entry->dir, entry->inode->linkname, looping, FALSE, FALSE);
747 looping = g_slist_delete_link (looping, looping);
749 if (pent == NULL)
750 my_errno = ENOENT;
753 return pent;
756 /* --------------------------------------------------------------------------------------------- */
758 static struct entry *
759 extfs_resolve_symlinks (struct entry *entry)
761 struct entry *res;
763 errloop = FALSE;
764 notadir = FALSE;
765 res = extfs_resolve_symlinks_int (entry, NULL);
766 if (res == NULL)
768 if (errloop)
769 my_errno = ELOOP;
770 else if (notadir)
771 my_errno = ENOTDIR;
773 return res;
776 /* --------------------------------------------------------------------------------------------- */
778 static const char *
779 extfs_get_archive_name (struct archive *archive)
781 const char *archive_name;
783 if (archive->local_name)
784 archive_name = archive->local_name;
785 else
786 archive_name = archive->name;
788 if (!archive_name || !*archive_name)
789 return "no_archive_name";
790 else
791 return archive_name;
794 /* --------------------------------------------------------------------------------------------- */
795 /** Don't pass localname as NULL */
797 static int
798 extfs_cmd (const char *str_extfs_cmd, struct archive *archive,
799 struct entry *entry, const char *localname)
801 char *file;
802 char *quoted_file;
803 char *quoted_localname;
804 char *archive_name;
805 const extfs_plugin_info_t *info;
806 char *cmd;
807 int retval;
809 file = extfs_get_path_from_entry (entry);
810 quoted_file = name_quote (file, 0);
811 g_free (file);
813 archive_name = name_quote (extfs_get_archive_name (archive), 0);
814 quoted_localname = name_quote (localname, 0);
815 info = &g_array_index (extfs_plugins, extfs_plugin_info_t, archive->fstype);
816 cmd = g_strconcat (info->path, info->prefix, str_extfs_cmd,
817 archive_name, " ", quoted_file, " ", quoted_localname, (char *) NULL);
818 g_free (quoted_file);
819 g_free (quoted_localname);
820 g_free (archive_name);
822 open_error_pipe ();
823 retval = my_system (EXECUTE_AS_SHELL, shell, cmd);
824 g_free (cmd);
825 close_error_pipe (D_ERROR, NULL);
826 return retval;
829 /* --------------------------------------------------------------------------------------------- */
831 static void
832 extfs_run (struct vfs_class *me, const char *file)
834 struct archive *archive = NULL;
835 char *p, *q, *archive_name;
836 char *cmd;
837 const extfs_plugin_info_t *info;
839 p = extfs_get_path (me, file, &archive, FALSE);
840 if (p == NULL)
841 return;
842 q = name_quote (p, 0);
843 g_free (p);
845 archive_name = name_quote (extfs_get_archive_name (archive), 0);
846 info = &g_array_index (extfs_plugins, extfs_plugin_info_t, archive->fstype);
847 cmd = g_strconcat (info->path, info->prefix, " run ", archive_name, " ", q, (char *) NULL);
848 g_free (archive_name);
849 g_free (q);
850 shell_execute (cmd, 0);
851 g_free (cmd);
854 /* --------------------------------------------------------------------------------------------- */
856 static void *
857 extfs_open (struct vfs_class *me, const char *file, int flags, mode_t mode)
859 struct pseudofile *extfs_info;
860 struct archive *archive = NULL;
861 char *q;
862 struct entry *entry;
863 int local_handle;
864 gboolean created = FALSE;
866 q = extfs_get_path (me, file, &archive, FALSE);
867 if (q == NULL)
868 return NULL;
869 entry = extfs_find_entry (archive->root_entry, q, FALSE, FALSE);
870 if ((entry == NULL) && ((flags & O_CREAT) != 0))
872 /* Create new entry */
873 entry = extfs_find_entry (archive->root_entry, q, FALSE, TRUE);
874 created = (entry != NULL);
877 g_free (q);
878 if (entry == NULL)
879 return NULL;
880 entry = extfs_resolve_symlinks (entry);
881 if (entry == NULL)
882 return NULL;
884 if (S_ISDIR (entry->inode->mode))
885 ERRNOR (EISDIR, NULL);
887 if (entry->inode->local_filename == NULL)
889 char *local_filename;
891 local_handle = vfs_mkstemps (&local_filename, "extfs", entry->name);
893 if (local_handle == -1)
894 return NULL;
895 close (local_handle);
897 if (!created && ((flags & O_TRUNC) == 0)
898 && extfs_cmd (" copyout ", archive, entry, local_filename))
900 unlink (local_filename);
901 g_free (local_filename);
902 my_errno = EIO;
903 return NULL;
905 entry->inode->local_filename = local_filename;
908 local_handle = open (entry->inode->local_filename, NO_LINEAR (flags), mode);
910 if (local_handle == -1)
912 /* file exists(may be). Need to drop O_CREAT flag and truncate file content */
913 flags = ~O_CREAT & (NO_LINEAR (flags) | O_TRUNC);
914 local_handle = open (entry->inode->local_filename, flags, mode);
917 if (local_handle == -1)
918 ERRNOR (EIO, NULL);
920 extfs_info = g_new (struct pseudofile, 1);
921 extfs_info->archive = archive;
922 extfs_info->entry = entry;
923 extfs_info->has_changed = created;
924 extfs_info->local_handle = local_handle;
926 /* i.e. we had no open files and now we have one */
927 vfs_rmstamp (&vfs_extfs_ops, (vfsid) archive);
928 archive->fd_usage++;
929 return extfs_info;
932 /* --------------------------------------------------------------------------------------------- */
934 static ssize_t
935 extfs_read (void *data, char *buffer, size_t count)
937 struct pseudofile *file = (struct pseudofile *) data;
939 return read (file->local_handle, buffer, count);
942 /* --------------------------------------------------------------------------------------------- */
944 static int
945 extfs_close (void *data)
947 struct pseudofile *file;
948 int errno_code = 0;
949 file = (struct pseudofile *) data;
951 close (file->local_handle);
953 /* Commit the file if it has changed */
954 if (file->has_changed)
956 struct stat file_status;
958 if (extfs_cmd (" copyin ", file->archive, file->entry, file->entry->inode->local_filename))
959 errno_code = EIO;
961 if (stat (file->entry->inode->local_filename, &file_status) != 0)
962 errno_code = EIO;
963 else
964 file->entry->inode->size = file_status.st_size;
966 file->entry->inode->mtime = time (NULL);
969 if (--file->archive->fd_usage == 0)
970 vfs_stamp_create (&vfs_extfs_ops, file->archive);
972 g_free (data);
973 if (errno_code != 0)
974 ERRNOR (EIO, -1);
975 return 0;
978 /* --------------------------------------------------------------------------------------------- */
980 static int
981 extfs_errno (struct vfs_class *me)
983 (void) me;
984 return my_errno;
987 /* --------------------------------------------------------------------------------------------- */
989 static void *
990 extfs_opendir (struct vfs_class *me, const char *dirname)
992 struct archive *archive = NULL;
993 char *q;
994 struct entry *entry;
995 struct entry **info;
997 q = extfs_get_path (me, dirname, &archive, FALSE);
998 if (q == NULL)
999 return NULL;
1000 entry = extfs_find_entry (archive->root_entry, q, FALSE, FALSE);
1001 g_free (q);
1002 if (entry == NULL)
1003 return NULL;
1004 entry = extfs_resolve_symlinks (entry);
1005 if (entry == NULL)
1006 return NULL;
1007 if (!S_ISDIR (entry->inode->mode))
1008 ERRNOR (ENOTDIR, NULL);
1010 info = g_new (struct entry *, 2);
1011 info[0] = entry->inode->first_in_subdir;
1012 info[1] = entry->inode->first_in_subdir;
1014 return info;
1017 /* --------------------------------------------------------------------------------------------- */
1019 static void *
1020 extfs_readdir (void *data)
1022 static union vfs_dirent dir;
1023 struct entry **info = (struct entry **) data;
1025 if (*info == NULL)
1026 return NULL;
1028 g_strlcpy (dir.dent.d_name, (*info)->name, MC_MAXPATHLEN);
1030 compute_namelen (&dir.dent);
1031 *info = (*info)->next_in_dir;
1033 return (void *) &dir;
1036 /* --------------------------------------------------------------------------------------------- */
1038 static int
1039 extfs_closedir (void *data)
1041 g_free (data);
1042 return 0;
1045 /* --------------------------------------------------------------------------------------------- */
1047 static void
1048 extfs_stat_move (struct stat *buf, const struct inode *inode)
1050 buf->st_dev = inode->dev;
1051 buf->st_ino = inode->inode;
1052 buf->st_mode = inode->mode;
1053 buf->st_nlink = inode->nlink;
1054 buf->st_uid = inode->uid;
1055 buf->st_gid = inode->gid;
1056 #ifdef HAVE_STRUCT_STAT_ST_RDEV
1057 buf->st_rdev = inode->rdev;
1058 #endif
1059 buf->st_size = inode->size;
1060 #ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
1061 buf->st_blksize = RECORDSIZE;
1062 #endif
1063 #ifdef HAVE_STRUCT_STAT_ST_BLOCKS
1064 buf->st_blocks = (inode->size + RECORDSIZE - 1) / RECORDSIZE;
1065 #endif
1066 buf->st_atime = inode->atime;
1067 buf->st_mtime = inode->mtime;
1068 buf->st_ctime = inode->ctime;
1071 /* --------------------------------------------------------------------------------------------- */
1073 static int
1074 extfs_internal_stat (struct vfs_class *me, const char *path, struct stat *buf, gboolean resolve)
1076 struct archive *archive;
1077 char *q, *mpath;
1078 struct entry *entry;
1079 int result = -1;
1081 mpath = g_strdup (path);
1083 q = extfs_get_path_mangle (me, mpath, &archive, FALSE);
1084 if (q == NULL)
1085 goto cleanup;
1086 entry = extfs_find_entry (archive->root_entry, q, FALSE, FALSE);
1087 if (entry == NULL)
1088 goto cleanup;
1089 if (resolve)
1091 entry = extfs_resolve_symlinks (entry);
1092 if (entry == NULL)
1093 goto cleanup;
1095 extfs_stat_move (buf, entry->inode);
1096 result = 0;
1097 cleanup:
1098 g_free (mpath);
1099 return result;
1102 /* --------------------------------------------------------------------------------------------- */
1104 static int
1105 extfs_stat (struct vfs_class *me, const char *path, struct stat *buf)
1107 return extfs_internal_stat (me, path, buf, TRUE);
1110 /* --------------------------------------------------------------------------------------------- */
1112 static int
1113 extfs_lstat (struct vfs_class *me, const char *path, struct stat *buf)
1115 return extfs_internal_stat (me, path, buf, FALSE);
1118 /* --------------------------------------------------------------------------------------------- */
1120 static int
1121 extfs_fstat (void *data, struct stat *buf)
1123 struct pseudofile *file = (struct pseudofile *) data;
1125 extfs_stat_move (buf, file->entry->inode);
1126 return 0;
1129 /* --------------------------------------------------------------------------------------------- */
1131 static int
1132 extfs_readlink (struct vfs_class *me, const char *path, char *buf, size_t size)
1134 struct archive *archive;
1135 char *q, *mpath;
1136 size_t len;
1137 struct entry *entry;
1138 int result = -1;
1140 mpath = g_strdup (path);
1142 q = extfs_get_path_mangle (me, mpath, &archive, FALSE);
1143 if (q == NULL)
1144 goto cleanup;
1145 entry = extfs_find_entry (archive->root_entry, q, FALSE, FALSE);
1146 if (entry == NULL)
1147 goto cleanup;
1148 if (!S_ISLNK (entry->inode->mode))
1150 me->verrno = EINVAL;
1151 goto cleanup;
1153 len = strlen (entry->inode->linkname);
1154 if (size < len)
1155 len = size;
1156 /* readlink() does not append a NUL character to buf */
1157 result = len;
1158 memcpy (buf, entry->inode->linkname, result);
1159 cleanup:
1160 g_free (mpath);
1161 return result;
1164 /* --------------------------------------------------------------------------------------------- */
1166 static int
1167 extfs_chown (struct vfs_class *me, const char *path, uid_t owner, gid_t group)
1169 (void) me;
1170 (void) path;
1171 (void) owner;
1172 (void) group;
1173 return 0;
1176 /* --------------------------------------------------------------------------------------------- */
1178 static int
1179 extfs_chmod (struct vfs_class *me, const char *path, int mode)
1181 (void) me;
1182 (void) path;
1183 (void) mode;
1184 return 0;
1187 /* --------------------------------------------------------------------------------------------- */
1189 static ssize_t
1190 extfs_write (void *data, const char *buf, size_t nbyte)
1192 struct pseudofile *file = (struct pseudofile *) data;
1194 file->has_changed = TRUE;
1195 return write (file->local_handle, buf, nbyte);
1198 /* --------------------------------------------------------------------------------------------- */
1200 static int
1201 extfs_unlink (struct vfs_class *me, const char *file)
1203 struct archive *archive;
1204 char *q, *mpath;
1205 struct entry *entry;
1206 int result = -1;
1208 mpath = g_strdup (file);
1210 q = extfs_get_path_mangle (me, mpath, &archive, FALSE);
1211 if (q == NULL)
1212 goto cleanup;
1213 entry = extfs_find_entry (archive->root_entry, q, FALSE, FALSE);
1214 if (entry == NULL)
1215 goto cleanup;
1216 entry = extfs_resolve_symlinks (entry);
1217 if (entry == NULL)
1218 goto cleanup;
1219 if (S_ISDIR (entry->inode->mode))
1221 me->verrno = EISDIR;
1222 goto cleanup;
1224 if (extfs_cmd (" rm ", archive, entry, ""))
1226 my_errno = EIO;
1227 goto cleanup;
1229 extfs_remove_entry (entry);
1230 result = 0;
1231 cleanup:
1232 g_free (mpath);
1233 return result;
1236 /* --------------------------------------------------------------------------------------------- */
1238 static int
1239 extfs_mkdir (struct vfs_class *me, const char *path, mode_t mode)
1241 struct archive *archive;
1242 char *q, *mpath;
1243 struct entry *entry;
1244 int result = -1;
1246 (void) mode;
1248 mpath = g_strdup (path);
1250 q = extfs_get_path_mangle (me, mpath, &archive, FALSE);
1251 if (q == NULL)
1252 goto cleanup;
1253 entry = extfs_find_entry (archive->root_entry, q, FALSE, FALSE);
1254 if (entry != NULL)
1256 me->verrno = EEXIST;
1257 goto cleanup;
1259 entry = extfs_find_entry (archive->root_entry, q, TRUE, FALSE);
1260 if (entry == NULL)
1261 goto cleanup;
1262 entry = extfs_resolve_symlinks (entry);
1263 if (entry == NULL)
1264 goto cleanup;
1265 if (!S_ISDIR (entry->inode->mode))
1267 me->verrno = ENOTDIR;
1268 goto cleanup;
1271 if (extfs_cmd (" mkdir ", archive, entry, ""))
1273 my_errno = EIO;
1274 extfs_remove_entry (entry);
1275 goto cleanup;
1277 result = 0;
1278 cleanup:
1279 g_free (mpath);
1280 return result;
1283 /* --------------------------------------------------------------------------------------------- */
1285 static int
1286 extfs_rmdir (struct vfs_class *me, const char *path)
1288 struct archive *archive;
1289 char *q, *mpath;
1290 struct entry *entry;
1291 int result = -1;
1293 mpath = g_strdup (path);
1295 q = extfs_get_path_mangle (me, mpath, &archive, FALSE);
1296 if (q == NULL)
1297 goto cleanup;
1298 entry = extfs_find_entry (archive->root_entry, q, FALSE, FALSE);
1299 if (entry == NULL)
1300 goto cleanup;
1301 entry = extfs_resolve_symlinks (entry);
1302 if (entry == NULL)
1303 goto cleanup;
1304 if (!S_ISDIR (entry->inode->mode))
1306 me->verrno = ENOTDIR;
1307 goto cleanup;
1310 if (extfs_cmd (" rmdir ", archive, entry, ""))
1312 my_errno = EIO;
1313 goto cleanup;
1315 extfs_remove_entry (entry);
1316 result = 0;
1317 cleanup:
1318 g_free (mpath);
1319 return result;
1322 /* --------------------------------------------------------------------------------------------- */
1324 static int
1325 extfs_chdir (struct vfs_class *me, const char *path)
1327 struct archive *archive = NULL;
1328 char *q;
1329 struct entry *entry;
1331 my_errno = ENOTDIR;
1332 q = extfs_get_path (me, path, &archive, FALSE);
1333 if (q == NULL)
1334 return -1;
1335 entry = extfs_find_entry (archive->root_entry, q, FALSE, FALSE);
1336 g_free (q);
1337 if (entry == NULL)
1338 return -1;
1339 entry = extfs_resolve_symlinks (entry);
1340 if ((entry == NULL) || (!S_ISDIR (entry->inode->mode)))
1341 return -1;
1342 my_errno = 0;
1343 return 0;
1346 /* --------------------------------------------------------------------------------------------- */
1348 static off_t
1349 extfs_lseek (void *data, off_t offset, int whence)
1351 struct pseudofile *file = (struct pseudofile *) data;
1353 return lseek (file->local_handle, offset, whence);
1356 /* --------------------------------------------------------------------------------------------- */
1358 static vfsid
1359 extfs_getid (struct vfs_class *me, const char *path)
1361 struct archive *archive = NULL;
1362 char *p;
1364 p = extfs_get_path (me, path, &archive, TRUE);
1365 if (p == NULL)
1366 return NULL;
1367 g_free (p);
1368 return (vfsid) archive;
1371 /* --------------------------------------------------------------------------------------------- */
1373 static int
1374 extfs_nothingisopen (vfsid id)
1376 return (((struct archive *) id)->fd_usage <= 0);
1379 /* --------------------------------------------------------------------------------------------- */
1381 static void
1382 extfs_remove_entry (struct entry *e)
1384 int i = --e->inode->nlink;
1385 struct entry *pe, *ent, *prev;
1387 if (S_ISDIR (e->inode->mode) && e->inode->first_in_subdir != NULL)
1389 struct entry *f = e->inode->first_in_subdir;
1390 e->inode->first_in_subdir = NULL;
1391 extfs_remove_entry (f);
1393 pe = e->dir;
1394 if (e == pe->inode->first_in_subdir)
1395 pe->inode->first_in_subdir = e->next_in_dir;
1397 prev = NULL;
1398 for (ent = pe->inode->first_in_subdir; ent && ent->next_in_dir; ent = ent->next_in_dir)
1399 if (e == ent->next_in_dir)
1401 prev = ent;
1402 break;
1404 if (prev)
1405 prev->next_in_dir = e->next_in_dir;
1406 if (e == pe->inode->last_in_subdir)
1407 pe->inode->last_in_subdir = prev;
1409 if (i <= 0)
1411 if (e->inode->local_filename != NULL)
1413 unlink (e->inode->local_filename);
1414 g_free (e->inode->local_filename);
1416 g_free (e->inode->linkname);
1417 g_free (e->inode);
1420 g_free (e->name);
1421 g_free (e);
1424 /* --------------------------------------------------------------------------------------------- */
1426 static void
1427 extfs_free_entry (struct entry *e)
1429 int i = --e->inode->nlink;
1431 if (S_ISDIR (e->inode->mode) && e->inode->first_in_subdir != NULL)
1433 struct entry *f = e->inode->first_in_subdir;
1435 e->inode->first_in_subdir = NULL;
1436 extfs_free_entry (f);
1438 if (i <= 0)
1440 if (e->inode->local_filename != NULL)
1442 unlink (e->inode->local_filename);
1443 g_free (e->inode->local_filename);
1445 g_free (e->inode->linkname);
1446 g_free (e->inode);
1448 if (e->next_in_dir != NULL)
1449 extfs_free_entry (e->next_in_dir);
1450 g_free (e->name);
1451 g_free (e);
1454 /* --------------------------------------------------------------------------------------------- */
1456 static void
1457 extfs_free (vfsid id)
1459 struct archive *archive = (struct archive *) id;
1461 if (archive == first_archive)
1463 first_archive = archive->next;
1465 else
1467 struct archive *parc;
1468 for (parc = first_archive; parc != NULL; parc = parc->next)
1469 if (parc->next == archive)
1471 parc->next = archive->next;
1472 break;
1475 extfs_free_archive (archive);
1478 /* --------------------------------------------------------------------------------------------- */
1480 static char *
1481 extfs_getlocalcopy (struct vfs_class *me, const char *path)
1483 struct pseudofile *fp;
1484 char *p;
1486 fp = (struct pseudofile *) extfs_open (me, path, O_RDONLY, 0);
1487 if (fp == NULL)
1488 return NULL;
1489 if (fp->entry->inode->local_filename == NULL)
1491 extfs_close ((void *) fp);
1492 return NULL;
1494 p = g_strdup (fp->entry->inode->local_filename);
1495 fp->archive->fd_usage++;
1496 extfs_close ((void *) fp);
1497 return p;
1500 /* --------------------------------------------------------------------------------------------- */
1502 static int
1503 extfs_ungetlocalcopy (struct vfs_class *me, const char *path, const char *local, int has_changed)
1505 struct pseudofile *fp;
1507 fp = (struct pseudofile *) extfs_open (me, path, O_RDONLY, 0);
1508 if (fp == NULL)
1509 return 0;
1511 if (strcmp (fp->entry->inode->local_filename, local) == 0)
1513 fp->archive->fd_usage--;
1514 if (has_changed != 0)
1515 fp->has_changed = TRUE;
1516 extfs_close ((void *) fp);
1517 return 0;
1519 else
1521 /* Should not happen */
1522 extfs_close ((void *) fp);
1523 return 0;
1527 /* --------------------------------------------------------------------------------------------- */
1529 static gboolean
1530 extfs_get_plugins (const char *where, gboolean silent)
1532 char *dirname;
1533 GDir *dir;
1534 const char *filename;
1536 dirname = g_build_path (PATH_SEP_STR, where, MC_EXTFS_DIR, (char *) NULL);
1537 dir = g_dir_open (dirname, 0, NULL);
1539 /* We may not use vfs_die() message or message or similar,
1540 * UI is not initialized at this time and message would not
1541 * appear on screen. */
1542 if (dir == NULL)
1544 if (!silent)
1545 fprintf (stderr, _("Warning: cannot open %s directory\n"), dirname);
1546 g_free (dirname);
1547 return FALSE;
1550 if (extfs_plugins == NULL)
1551 extfs_plugins = g_array_sized_new (FALSE, TRUE, sizeof (extfs_plugin_info_t), 32);
1553 while ((filename = g_dir_read_name (dir)) != NULL)
1555 char fullname[MC_MAXPATHLEN];
1556 struct stat s;
1558 g_snprintf (fullname, sizeof (fullname), "%s" PATH_SEP_STR "%s", dirname, filename);
1560 if ((stat (fullname, &s) == 0)
1561 && S_ISREG (s.st_mode) && !S_ISDIR (s.st_mode)
1562 && (((s.st_mode & S_IXOTH) != 0) ||
1563 ((s.st_mode & S_IXUSR) != 0) || ((s.st_mode & S_IXGRP) != 0)))
1565 int f;
1567 f = open (fullname, O_RDONLY);
1569 if (f > 0)
1571 size_t len, i;
1572 extfs_plugin_info_t info;
1573 gboolean found = FALSE;
1575 close (f);
1577 /* Handle those with a trailing '+', those flag that the
1578 * file system does not require an archive to work
1580 len = strlen (filename);
1581 info.need_archive = (filename[len - 1] != '+');
1582 info.path = g_strconcat (dirname, PATH_SEP_STR, (char *) NULL);
1583 info.prefix = g_strdup (filename);
1585 /* prepare to compare file names without trailing '+' */
1586 if (!info.need_archive)
1587 info.prefix[len - 1] = '\0';
1589 /* don't overload already found plugin */
1590 for (i = 0; i < extfs_plugins->len; i++)
1592 extfs_plugin_info_t *p;
1594 p = &g_array_index (extfs_plugins, extfs_plugin_info_t, i);
1596 /* 2 files with same names cannot be in a directory */
1597 if ((strcmp (info.path, p->path) != 0)
1598 && (strcmp (info.prefix, p->prefix) == 0))
1600 found = TRUE;
1601 break;
1605 if (found)
1607 g_free (info.path);
1608 g_free (info.prefix);
1610 else
1612 /* restore file name */
1613 if (!info.need_archive)
1614 info.prefix[len - 1] = '+';
1615 g_array_append_val (extfs_plugins, info);
1621 g_dir_close (dir);
1622 g_free (dirname);
1624 return TRUE;
1627 /* --------------------------------------------------------------------------------------------- */
1629 static int
1630 extfs_init (struct vfs_class *me)
1632 gboolean d1, d2;
1633 char *dirname;
1635 (void) me;
1637 /* 1st: scan user directory */
1638 dirname = g_build_path (PATH_SEP_STR, home_dir, MC_USERCONF_DIR, (char *) NULL);
1639 d1 = extfs_get_plugins (dirname, TRUE); /* silent about user dir */
1640 g_free (dirname);
1641 /* 2nd: scan system dir */
1642 d2 = extfs_get_plugins (LIBEXECDIR, d1);
1644 return (d1 || d2 ? 1 : 0);
1647 /* --------------------------------------------------------------------------------------------- */
1649 static void
1650 extfs_done (struct vfs_class *me)
1652 size_t i;
1653 struct archive *ar;
1655 (void) me;
1657 for (ar = first_archive; ar != NULL;)
1659 extfs_free ((vfsid) ar);
1660 ar = first_archive;
1663 for (i = 0; i < extfs_plugins->len; i++)
1665 extfs_plugin_info_t *info;
1667 info = &g_array_index (extfs_plugins, extfs_plugin_info_t, i);
1668 g_free (info->path);
1669 g_free (info->prefix);
1672 if (extfs_plugins != NULL)
1673 g_array_free (extfs_plugins, TRUE);
1676 /* --------------------------------------------------------------------------------------------- */
1678 static int
1679 extfs_setctl (struct vfs_class *me, const char *path, int ctlop, void *arg)
1681 (void) arg;
1683 if (ctlop == VFS_SETCTL_RUN)
1685 extfs_run (me, path);
1686 return 1;
1688 return 0;
1691 /* --------------------------------------------------------------------------------------------- */
1692 /*** public functions ****************************************************************************/
1693 /* --------------------------------------------------------------------------------------------- */
1695 void
1696 init_extfs (void)
1698 vfs_extfs_ops.name = "extfs";
1699 vfs_extfs_ops.init = extfs_init;
1700 vfs_extfs_ops.done = extfs_done;
1701 vfs_extfs_ops.fill_names = extfs_fill_names;
1702 vfs_extfs_ops.which = extfs_which;
1703 vfs_extfs_ops.open = extfs_open;
1704 vfs_extfs_ops.close = extfs_close;
1705 vfs_extfs_ops.read = extfs_read;
1706 vfs_extfs_ops.write = extfs_write;
1707 vfs_extfs_ops.opendir = extfs_opendir;
1708 vfs_extfs_ops.readdir = extfs_readdir;
1709 vfs_extfs_ops.closedir = extfs_closedir;
1710 vfs_extfs_ops.stat = extfs_stat;
1711 vfs_extfs_ops.lstat = extfs_lstat;
1712 vfs_extfs_ops.fstat = extfs_fstat;
1713 vfs_extfs_ops.chmod = extfs_chmod;
1714 vfs_extfs_ops.chown = extfs_chown;
1715 vfs_extfs_ops.readlink = extfs_readlink;
1716 vfs_extfs_ops.unlink = extfs_unlink;
1717 vfs_extfs_ops.chdir = extfs_chdir;
1718 vfs_extfs_ops.ferrno = extfs_errno;
1719 vfs_extfs_ops.lseek = extfs_lseek;
1720 vfs_extfs_ops.getid = extfs_getid;
1721 vfs_extfs_ops.nothingisopen = extfs_nothingisopen;
1722 vfs_extfs_ops.free = extfs_free;
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 vfs_register_class (&vfs_extfs_ops);
1731 /* --------------------------------------------------------------------------------------------- */