Ticket #2361: VFS URI reimplementation
[midnight-commander.git] / src / vfs / extfs / extfs.c
blob0bc5e14a73e27c5b33d128abaf63e8f9348241e6
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/mcconfig.h"
52 #include "lib/util.h"
53 #include "lib/widget.h" /* message() */
55 #include "src/main.h" /* shell */
56 #include "src/execute.h" /* For shell_execute */
58 #include "lib/vfs/vfs.h"
59 #include "lib/vfs/utilvfs.h"
60 #include "lib/vfs/gc.h" /* vfs_rmstamp */
62 #include "extfs.h"
64 /*** global variables ****************************************************************************/
66 GArray *extfs_plugins = NULL;
68 /*** file scope macro definitions ****************************************************************/
70 #undef ERRNOR
71 #define ERRNOR(x,y) do { my_errno = x; return y; } while(0)
73 #define RECORDSIZE 512
75 /*** file scope type declarations ****************************************************************/
77 struct inode
79 nlink_t nlink;
80 struct entry *first_in_subdir; /* only used if this is a directory */
81 struct entry *last_in_subdir;
82 ino_t inode; /* This is inode # */
83 dev_t dev; /* This is an internal identification of the extfs archive */
84 struct archive *archive; /* And this is an archive structure */
85 dev_t rdev;
86 mode_t mode;
87 uid_t uid;
88 gid_t gid;
89 off_t size;
90 time_t mtime;
91 char *linkname;
92 time_t atime;
93 time_t ctime;
94 char *local_filename;
97 struct entry
99 struct entry *next_in_dir;
100 struct entry *dir;
101 char *name;
102 struct inode *inode;
105 struct pseudofile
107 struct archive *archive;
108 gboolean has_changed;
109 int local_handle;
110 struct entry *entry;
113 struct archive
115 int fstype;
116 char *name;
117 char *local_name;
118 struct stat local_stat;
119 dev_t rdev;
120 int fd_usage;
121 ino_t inode_counter;
122 struct entry *root_entry;
123 struct archive *next;
126 typedef struct
128 char *path;
129 char *prefix;
130 gboolean need_archive;
131 } extfs_plugin_info_t;
133 /*** file scope variables ************************************************************************/
135 static gboolean errloop;
136 static gboolean notadir;
138 static struct vfs_class vfs_extfs_ops;
139 static struct archive *first_archive = NULL;
140 static int my_errno = 0;
142 /*** file scope functions ************************************************************************/
143 /* --------------------------------------------------------------------------------------------- */
145 static void extfs_remove_entry (struct entry *e);
146 static void extfs_free (vfsid id);
147 static void extfs_free_entry (struct entry *e);
148 static struct entry *extfs_resolve_symlinks_int (struct entry *entry, GSList * list);
150 /* --------------------------------------------------------------------------------------------- */
152 static void
153 extfs_make_dots (struct entry *ent)
155 struct entry *entry = g_new (struct entry, 1);
156 struct entry *parentry = ent->dir;
157 struct inode *inode = ent->inode, *parent;
159 parent = (parentry != NULL) ? parentry->inode : NULL;
160 entry->name = g_strdup (".");
161 entry->inode = inode;
162 entry->dir = ent;
163 inode->local_filename = NULL;
164 inode->first_in_subdir = entry;
165 inode->nlink++;
167 entry->next_in_dir = g_new (struct entry, 1);
168 entry = entry->next_in_dir;
169 entry->name = g_strdup ("..");
170 inode->last_in_subdir = entry;
171 entry->next_in_dir = NULL;
172 if (parent != NULL)
174 entry->inode = parent;
175 entry->dir = parentry;
176 parent->nlink++;
178 else
180 entry->inode = inode;
181 entry->dir = ent;
182 inode->nlink++;
186 /* --------------------------------------------------------------------------------------------- */
188 static struct entry *
189 extfs_generate_entry (struct archive *archive,
190 const char *name, struct entry *parentry, mode_t mode)
192 mode_t myumask;
193 struct inode *inode, *parent;
194 struct entry *entry;
196 parent = (parentry != NULL) ? parentry->inode : NULL;
197 entry = g_new (struct entry, 1);
199 entry->name = g_strdup (name);
200 entry->next_in_dir = NULL;
201 entry->dir = parentry;
202 if (parent != NULL)
204 parent->last_in_subdir->next_in_dir = entry;
205 parent->last_in_subdir = entry;
207 inode = g_new (struct inode, 1);
208 entry->inode = inode;
209 inode->local_filename = NULL;
210 inode->linkname = NULL;
211 inode->last_in_subdir = NULL;
212 inode->inode = (archive->inode_counter)++;
213 inode->dev = archive->rdev;
214 inode->archive = archive;
215 myumask = umask (022);
216 umask (myumask);
217 inode->mode = mode & ~myumask;
218 mode = inode->mode;
219 inode->rdev = 0;
220 inode->uid = getuid ();
221 inode->gid = getgid ();
222 inode->size = 0;
223 inode->mtime = time (NULL);
224 inode->atime = inode->mtime;
225 inode->ctime = inode->mtime;
226 inode->nlink = 1;
227 if (S_ISDIR (mode))
228 extfs_make_dots (entry);
229 return entry;
232 /* --------------------------------------------------------------------------------------------- */
234 static struct entry *
235 extfs_find_entry_int (struct entry *dir, char *name, GSList * list,
236 gboolean make_dirs, gboolean make_file)
238 struct entry *pent, *pdir;
239 char *p, *q, *name_end;
240 char c = PATH_SEP;
242 if (g_path_is_absolute (name))
244 /* Handle absolute paths */
245 name = (char *) g_path_skip_root (name);
246 dir = dir->inode->archive->root_entry;
249 pent = dir;
250 p = name;
251 name_end = name + strlen (name);
253 q = strchr (p, PATH_SEP);
254 if (q == '\0')
255 q = strchr (p, '\0');
257 while ((pent != NULL) && (c != '\0') && (*p != '\0'))
259 c = *q;
260 *q = '\0';
262 if (strcmp (p, ".") != 0)
264 if (strcmp (p, "..") == 0)
265 pent = pent->dir;
266 else
268 pent = extfs_resolve_symlinks_int (pent, list);
269 if (pent == NULL)
271 *q = c;
272 return NULL;
274 if (!S_ISDIR (pent->inode->mode))
276 *q = c;
277 notadir = TRUE;
278 return NULL;
281 pdir = pent;
282 for (pent = pent->inode->first_in_subdir; pent != NULL; pent = pent->next_in_dir)
283 /* Hack: I keep the original semanthic unless
284 q+1 would break in the strchr */
285 if (strcmp (pent->name, p) == 0)
287 if (q + 1 > name_end)
289 *q = c;
290 notadir = !S_ISDIR (pent->inode->mode);
291 return pent;
293 break;
296 /* When we load archive, we create automagically
297 * non-existant directories
299 if (pent == NULL && make_dirs)
300 pent = extfs_generate_entry (dir->inode->archive, p, pdir, S_IFDIR | 0777);
301 if (pent == NULL && make_file)
302 pent = extfs_generate_entry (dir->inode->archive, p, pdir, S_IFREG | 0666);
305 /* Next iteration */
306 *q = c;
307 p = q + 1;
308 q = strchr (p, PATH_SEP);
309 if (q == '\0')
310 q = strchr (p, '\0');
312 if (pent == NULL)
313 my_errno = ENOENT;
314 return pent;
317 /* --------------------------------------------------------------------------------------------- */
319 static struct entry *
320 extfs_find_entry (struct entry *dir, char *name, gboolean make_dirs, gboolean make_file)
322 struct entry *res;
324 errloop = FALSE;
325 notadir = FALSE;
327 res = extfs_find_entry_int (dir, name, NULL, make_dirs, make_file);
328 if (res == NULL)
330 if (errloop)
331 my_errno = ELOOP;
332 else if (notadir)
333 my_errno = ENOTDIR;
335 return res;
338 /* --------------------------------------------------------------------------------------------- */
340 static void
341 extfs_fill_names (struct vfs_class *me, fill_names_f func)
343 struct archive *a = first_archive;
345 (void) me;
347 while (a != NULL)
349 extfs_plugin_info_t *info;
350 char *name;
352 info = &g_array_index (extfs_plugins, extfs_plugin_info_t, a->fstype);
353 name =
354 g_strconcat (a->name ? a->name : "", "/", info->prefix, VFS_PATH_URL_DELIMITER,
355 (char *) NULL);
356 func (name);
357 g_free (name);
358 a = a->next;
362 /* --------------------------------------------------------------------------------------------- */
364 static void
365 extfs_free_archive (struct archive *archive)
367 extfs_free_entry (archive->root_entry);
368 if (archive->local_name != NULL)
370 struct stat my;
372 mc_stat (archive->local_name, &my);
373 mc_ungetlocalcopy (archive->name, archive->local_name,
374 archive->local_stat.st_mtime != my.st_mtime);
375 g_free (archive->local_name);
377 g_free (archive->name);
378 g_free (archive);
381 /* --------------------------------------------------------------------------------------------- */
383 static FILE *
384 extfs_open_archive (int fstype, const char *name, struct archive **pparc)
386 const extfs_plugin_info_t *info;
387 static dev_t archive_counter = 0;
388 FILE *result;
389 mode_t mode;
390 char *cmd;
391 struct stat mystat;
392 struct archive *current_archive;
393 struct entry *root_entry;
394 char *local_name = NULL, *tmp = NULL;
396 info = &g_array_index (extfs_plugins, extfs_plugin_info_t, fstype);
398 if (info->need_archive)
400 vfs_path_t *vpath = vfs_path_from_str (name);
401 if (mc_stat (name, &mystat) == -1)
402 return NULL;
404 if (!vfs_file_is_local (vpath))
406 local_name = mc_getlocalcopy (name);
407 if (local_name == NULL)
408 return NULL;
410 vfs_path_free (vpath);
412 tmp = name_quote (name, 0);
415 cmd = g_strconcat (info->path, info->prefix, " list ",
416 local_name != NULL ? local_name : tmp, (char *) NULL);
417 g_free (tmp);
419 open_error_pipe ();
420 result = popen (cmd, "r");
421 g_free (cmd);
422 if (result == NULL)
424 close_error_pipe (D_ERROR, NULL);
425 if (local_name != NULL)
427 mc_ungetlocalcopy (name, local_name, 0);
428 g_free (local_name);
430 return NULL;
433 #ifdef ___QNXNTO__
434 setvbuf (result, NULL, _IONBF, 0);
435 #endif
437 current_archive = g_new (struct archive, 1);
438 current_archive->fstype = fstype;
439 current_archive->name = name ? g_strdup (name) : NULL;
440 current_archive->local_name = local_name;
442 if (local_name != NULL)
443 mc_stat (local_name, &current_archive->local_stat);
444 current_archive->inode_counter = 0;
445 current_archive->fd_usage = 0;
446 current_archive->rdev = archive_counter++;
447 current_archive->next = first_archive;
448 first_archive = current_archive;
449 mode = mystat.st_mode & 07777;
450 if (mode & 0400)
451 mode |= 0100;
452 if (mode & 0040)
453 mode |= 0010;
454 if (mode & 0004)
455 mode |= 0001;
456 mode |= S_IFDIR;
457 root_entry = extfs_generate_entry (current_archive, PATH_SEP_STR, NULL, mode);
458 root_entry->inode->uid = mystat.st_uid;
459 root_entry->inode->gid = mystat.st_gid;
460 root_entry->inode->atime = mystat.st_atime;
461 root_entry->inode->ctime = mystat.st_ctime;
462 root_entry->inode->mtime = mystat.st_mtime;
463 current_archive->root_entry = root_entry;
465 *pparc = current_archive;
467 return result;
470 /* --------------------------------------------------------------------------------------------- */
472 * Main loop for reading an archive.
473 * Return 0 on success, -1 on error.
476 static int
477 extfs_read_archive (int fstype, const char *name, struct archive **pparc)
479 FILE *extfsd;
480 const extfs_plugin_info_t *info;
481 char *buffer;
482 struct archive *current_archive;
483 char *current_file_name, *current_link_name;
485 info = &g_array_index (extfs_plugins, extfs_plugin_info_t, fstype);
487 extfsd = extfs_open_archive (fstype, name, &current_archive);
489 if (extfsd == NULL)
491 message (D_ERROR, MSG_ERROR, _("Cannot open %s archive\n%s"), info->prefix, name);
492 return -1;
495 buffer = g_malloc (BUF_4K);
496 while (fgets (buffer, BUF_4K, extfsd) != NULL)
498 struct stat hstat;
500 current_link_name = NULL;
501 if (vfs_parse_ls_lga (buffer, &hstat, &current_file_name, &current_link_name))
503 struct entry *entry, *pent;
504 struct inode *inode;
505 char *p, *q, *cfn = current_file_name;
507 if (*cfn != '\0')
509 if (*cfn == PATH_SEP)
510 cfn++;
511 p = strchr (cfn, '\0');
512 if (p != cfn && *(p - 1) == PATH_SEP)
513 *(p - 1) = '\0';
514 p = strrchr (cfn, PATH_SEP);
515 if (p == NULL)
517 p = cfn;
518 q = strchr (cfn, '\0');
520 else
522 *(p++) = '\0';
523 q = cfn;
525 if (S_ISDIR (hstat.st_mode) && (strcmp (p, ".") == 0 || strcmp (p, "..") == 0))
526 goto read_extfs_continue;
527 pent = extfs_find_entry (current_archive->root_entry, q, TRUE, FALSE);
528 if (pent == NULL)
530 /* FIXME: Should clean everything one day */
531 g_free (buffer);
532 pclose (extfsd);
533 close_error_pipe (D_ERROR, _("Inconsistent extfs archive"));
534 return -1;
536 entry = g_new (struct entry, 1);
537 entry->name = g_strdup (p);
538 entry->next_in_dir = NULL;
539 entry->dir = pent;
540 if (pent->inode->last_in_subdir)
542 pent->inode->last_in_subdir->next_in_dir = entry;
543 pent->inode->last_in_subdir = entry;
545 if (!S_ISLNK (hstat.st_mode) && (current_link_name != NULL))
547 pent = extfs_find_entry (current_archive->root_entry,
548 current_link_name, FALSE, FALSE);
549 if (pent == NULL)
551 /* FIXME: Should clean everything one day */
552 g_free (buffer);
553 pclose (extfsd);
554 close_error_pipe (D_ERROR, _("Inconsistent extfs archive"));
555 return -1;
558 entry->inode = pent->inode;
559 pent->inode->nlink++;
561 else
563 inode = g_new (struct inode, 1);
564 entry->inode = inode;
565 inode->local_filename = NULL;
566 inode->inode = (current_archive->inode_counter)++;
567 inode->nlink = 1;
568 inode->dev = current_archive->rdev;
569 inode->archive = current_archive;
570 inode->mode = hstat.st_mode;
571 #ifdef HAVE_STRUCT_STAT_ST_RDEV
572 inode->rdev = hstat.st_rdev;
573 #else
574 inode->rdev = 0;
575 #endif
576 inode->uid = hstat.st_uid;
577 inode->gid = hstat.st_gid;
578 inode->size = hstat.st_size;
579 inode->mtime = hstat.st_mtime;
580 inode->atime = hstat.st_atime;
581 inode->ctime = hstat.st_ctime;
582 inode->first_in_subdir = NULL;
583 inode->last_in_subdir = NULL;
584 if (current_link_name != NULL && S_ISLNK (hstat.st_mode))
586 inode->linkname = current_link_name;
587 current_link_name = NULL;
589 else
591 if (S_ISLNK (hstat.st_mode))
592 inode->mode &= ~S_IFLNK; /* You *DON'T* want to do this always */
593 inode->linkname = NULL;
595 if (S_ISDIR (hstat.st_mode))
596 extfs_make_dots (entry);
599 read_extfs_continue:
600 g_free (current_file_name);
601 g_free (current_link_name);
604 g_free (buffer);
606 /* Check if extfs 'list' returned 0 */
607 if (pclose (extfsd) != 0)
609 extfs_free (current_archive);
610 close_error_pipe (D_ERROR, _("Inconsistent extfs archive"));
611 return -1;
614 close_error_pipe (D_ERROR, NULL);
615 *pparc = current_archive;
616 return 0;
619 /* --------------------------------------------------------------------------------------------- */
621 static int
622 extfs_which (struct vfs_class *me, const char *path)
624 size_t path_len;
625 size_t i;
627 (void) me;
629 path_len = strlen (path);
631 for (i = 0; i < extfs_plugins->len; i++)
633 extfs_plugin_info_t *info;
635 info = &g_array_index (extfs_plugins, extfs_plugin_info_t, i);
637 if ((strncmp (path, info->prefix, path_len) == 0)
638 && ((info->prefix[path_len] == '\0') || (info->prefix[path_len] == '+')))
639 return i;
641 return -1;
644 /* --------------------------------------------------------------------------------------------- */
646 * Dissect the path and create corresponding superblock. Note that inname
647 * can be changed and the result may point inside the original string.
650 static char *
651 extfs_get_path_mangle (const vfs_path_t * vpath, struct archive **archive, gboolean do_not_open)
653 char *archive_name;
654 int result = -1;
655 struct archive *parc;
656 int fstype;
657 vfs_path_element_t *path_element;
659 path_element = vfs_path_get_by_index (vpath, -1);
661 archive_name = vfs_path_to_str_elements_count (vpath, -1);
663 fstype = extfs_which (path_element->class, path_element->vfs_prefix);
665 if (fstype == -1)
667 g_free (archive_name);
668 return NULL;
672 * All filesystems should have some local archive, at least
673 * it can be PATH_SEP ('/').
675 for (parc = first_archive; parc != NULL; parc = parc->next)
676 if (parc->name != NULL)
678 if (strcmp (parc->name, archive_name) == 0)
680 vfs_stamp (&vfs_extfs_ops, (vfsid) parc);
681 goto return_success;
685 result = do_not_open ? -1 : extfs_read_archive (fstype, archive_name, &parc);
686 if (result == -1)
688 path_element->class->verrno = EIO;
689 g_free (archive_name);
690 return NULL;
693 return_success:
694 *archive = parc;
695 g_free (archive_name);
696 return path_element->path;
699 /* --------------------------------------------------------------------------------------------- */
701 * Dissect the path and create corresponding superblock.
702 * The result should be freed.
705 static char *
706 extfs_get_path (const vfs_path_t * vpath, struct archive **archive, gboolean do_not_open)
708 return g_strdup (extfs_get_path_mangle (vpath, archive, do_not_open));
711 /* --------------------------------------------------------------------------------------------- */
712 /* Return allocated path (without leading slash) inside the archive */
714 static char *
715 extfs_get_path_from_entry (struct entry *entry)
717 GString *localpath;
719 localpath = g_string_new ("");
721 while (entry->dir != NULL)
723 g_string_prepend (localpath, entry->name);
724 if (entry->dir->dir != NULL)
725 g_string_prepend_c (localpath, PATH_SEP);
726 entry = entry->dir;
729 return g_string_free (localpath, FALSE);
732 /* --------------------------------------------------------------------------------------------- */
734 static struct entry *
735 extfs_resolve_symlinks_int (struct entry *entry, GSList * list)
737 struct entry *pent = NULL;
739 if (!S_ISLNK (entry->inode->mode))
740 return entry;
742 if (g_slist_find (list, entry) != NULL)
744 /* Here we protect us against symlink looping */
745 errloop = TRUE;
747 else
749 GSList *looping;
751 looping = g_slist_prepend (list, entry);
752 pent = extfs_find_entry_int (entry->dir, entry->inode->linkname, looping, FALSE, FALSE);
753 looping = g_slist_delete_link (looping, looping);
755 if (pent == NULL)
756 my_errno = ENOENT;
759 return pent;
762 /* --------------------------------------------------------------------------------------------- */
764 static struct entry *
765 extfs_resolve_symlinks (struct entry *entry)
767 struct entry *res;
769 errloop = FALSE;
770 notadir = FALSE;
771 res = extfs_resolve_symlinks_int (entry, NULL);
772 if (res == NULL)
774 if (errloop)
775 my_errno = ELOOP;
776 else if (notadir)
777 my_errno = ENOTDIR;
779 return res;
782 /* --------------------------------------------------------------------------------------------- */
784 static const char *
785 extfs_get_archive_name (struct archive *archive)
787 const char *archive_name;
789 if (archive->local_name)
790 archive_name = archive->local_name;
791 else
792 archive_name = archive->name;
794 if (!archive_name || !*archive_name)
795 return "no_archive_name";
796 else
797 return archive_name;
800 /* --------------------------------------------------------------------------------------------- */
801 /** Don't pass localname as NULL */
803 static int
804 extfs_cmd (const char *str_extfs_cmd, struct archive *archive,
805 struct entry *entry, const char *localname)
807 char *file;
808 char *quoted_file;
809 char *quoted_localname;
810 char *archive_name;
811 const extfs_plugin_info_t *info;
812 char *cmd;
813 int retval;
815 file = extfs_get_path_from_entry (entry);
816 quoted_file = name_quote (file, 0);
817 g_free (file);
819 archive_name = name_quote (extfs_get_archive_name (archive), 0);
820 quoted_localname = name_quote (localname, 0);
821 info = &g_array_index (extfs_plugins, extfs_plugin_info_t, archive->fstype);
822 cmd = g_strconcat (info->path, info->prefix, str_extfs_cmd,
823 archive_name, " ", quoted_file, " ", quoted_localname, (char *) NULL);
824 g_free (quoted_file);
825 g_free (quoted_localname);
826 g_free (archive_name);
828 open_error_pipe ();
829 retval = my_system (EXECUTE_AS_SHELL, shell, cmd);
830 g_free (cmd);
831 close_error_pipe (D_ERROR, NULL);
832 return retval;
835 /* --------------------------------------------------------------------------------------------- */
837 static void
838 extfs_run (const vfs_path_t * vpath)
840 struct archive *archive = NULL;
841 char *p, *q, *archive_name;
842 char *cmd;
843 const extfs_plugin_info_t *info;
845 p = extfs_get_path (vpath, &archive, FALSE);
846 if (p == NULL)
847 return;
848 q = name_quote (p, 0);
849 g_free (p);
851 archive_name = name_quote (extfs_get_archive_name (archive), 0);
852 info = &g_array_index (extfs_plugins, extfs_plugin_info_t, archive->fstype);
853 cmd = g_strconcat (info->path, info->prefix, " run ", archive_name, " ", q, (char *) NULL);
854 g_free (archive_name);
855 g_free (q);
856 shell_execute (cmd, 0);
857 g_free (cmd);
860 /* --------------------------------------------------------------------------------------------- */
862 static void *
863 extfs_open (const vfs_path_t * vpath, int flags, mode_t mode)
865 struct pseudofile *extfs_info;
866 struct archive *archive = NULL;
867 char *q;
868 struct entry *entry;
869 int local_handle;
870 gboolean created = FALSE;
872 q = extfs_get_path (vpath, &archive, FALSE);
873 if (q == NULL)
874 return NULL;
875 entry = extfs_find_entry (archive->root_entry, q, FALSE, FALSE);
876 if ((entry == NULL) && ((flags & O_CREAT) != 0))
878 /* Create new entry */
879 entry = extfs_find_entry (archive->root_entry, q, FALSE, TRUE);
880 created = (entry != NULL);
883 g_free (q);
884 if (entry == NULL)
885 return NULL;
886 entry = extfs_resolve_symlinks (entry);
887 if (entry == NULL)
888 return NULL;
890 if (S_ISDIR (entry->inode->mode))
891 ERRNOR (EISDIR, NULL);
893 if (entry->inode->local_filename == NULL)
895 char *local_filename;
897 local_handle = vfs_mkstemps (&local_filename, "extfs", entry->name);
899 if (local_handle == -1)
900 return NULL;
901 close (local_handle);
903 if (!created && ((flags & O_TRUNC) == 0)
904 && extfs_cmd (" copyout ", archive, entry, local_filename))
906 unlink (local_filename);
907 g_free (local_filename);
908 my_errno = EIO;
909 return NULL;
911 entry->inode->local_filename = local_filename;
914 local_handle = open (entry->inode->local_filename, NO_LINEAR (flags), mode);
916 if (local_handle == -1)
918 /* file exists(may be). Need to drop O_CREAT flag and truncate file content */
919 flags = ~O_CREAT & (NO_LINEAR (flags) | O_TRUNC);
920 local_handle = open (entry->inode->local_filename, flags, mode);
923 if (local_handle == -1)
924 ERRNOR (EIO, NULL);
926 extfs_info = g_new (struct pseudofile, 1);
927 extfs_info->archive = archive;
928 extfs_info->entry = entry;
929 extfs_info->has_changed = created;
930 extfs_info->local_handle = local_handle;
932 /* i.e. we had no open files and now we have one */
933 vfs_rmstamp (&vfs_extfs_ops, (vfsid) archive);
934 archive->fd_usage++;
935 return extfs_info;
938 /* --------------------------------------------------------------------------------------------- */
940 static ssize_t
941 extfs_read (void *data, char *buffer, size_t count)
943 struct pseudofile *file = (struct pseudofile *) data;
945 return read (file->local_handle, buffer, count);
948 /* --------------------------------------------------------------------------------------------- */
950 static int
951 extfs_close (void *data)
953 struct pseudofile *file;
954 int errno_code = 0;
955 file = (struct pseudofile *) data;
957 close (file->local_handle);
959 /* Commit the file if it has changed */
960 if (file->has_changed)
962 struct stat file_status;
964 if (extfs_cmd (" copyin ", file->archive, file->entry, file->entry->inode->local_filename))
965 errno_code = EIO;
967 if (stat (file->entry->inode->local_filename, &file_status) != 0)
968 errno_code = EIO;
969 else
970 file->entry->inode->size = file_status.st_size;
972 file->entry->inode->mtime = time (NULL);
975 if (--file->archive->fd_usage == 0)
976 vfs_stamp_create (&vfs_extfs_ops, file->archive);
978 g_free (data);
979 if (errno_code != 0)
980 ERRNOR (EIO, -1);
981 return 0;
984 /* --------------------------------------------------------------------------------------------- */
986 static int
987 extfs_errno (struct vfs_class *me)
989 (void) me;
990 return my_errno;
993 /* --------------------------------------------------------------------------------------------- */
995 static void *
996 extfs_opendir (const vfs_path_t * vpath)
998 struct archive *archive = NULL;
999 char *q;
1000 struct entry *entry;
1001 struct entry **info;
1003 q = extfs_get_path (vpath, &archive, FALSE);
1004 if (q == NULL)
1005 return NULL;
1006 entry = extfs_find_entry (archive->root_entry, q, FALSE, FALSE);
1007 g_free (q);
1008 if (entry == NULL)
1009 return NULL;
1010 entry = extfs_resolve_symlinks (entry);
1011 if (entry == NULL)
1012 return NULL;
1013 if (!S_ISDIR (entry->inode->mode))
1014 ERRNOR (ENOTDIR, NULL);
1016 info = g_new (struct entry *, 2);
1017 info[0] = entry->inode->first_in_subdir;
1018 info[1] = entry->inode->first_in_subdir;
1020 return info;
1023 /* --------------------------------------------------------------------------------------------- */
1025 static void *
1026 extfs_readdir (void *data)
1028 static union vfs_dirent dir;
1029 struct entry **info = (struct entry **) data;
1031 if (*info == NULL)
1032 return NULL;
1034 g_strlcpy (dir.dent.d_name, (*info)->name, MC_MAXPATHLEN);
1036 compute_namelen (&dir.dent);
1037 *info = (*info)->next_in_dir;
1039 return (void *) &dir;
1042 /* --------------------------------------------------------------------------------------------- */
1044 static int
1045 extfs_closedir (void *data)
1047 g_free (data);
1048 return 0;
1051 /* --------------------------------------------------------------------------------------------- */
1053 static void
1054 extfs_stat_move (struct stat *buf, const struct inode *inode)
1056 buf->st_dev = inode->dev;
1057 buf->st_ino = inode->inode;
1058 buf->st_mode = inode->mode;
1059 buf->st_nlink = inode->nlink;
1060 buf->st_uid = inode->uid;
1061 buf->st_gid = inode->gid;
1062 #ifdef HAVE_STRUCT_STAT_ST_RDEV
1063 buf->st_rdev = inode->rdev;
1064 #endif
1065 buf->st_size = inode->size;
1066 #ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
1067 buf->st_blksize = RECORDSIZE;
1068 #endif
1069 #ifdef HAVE_STRUCT_STAT_ST_BLOCKS
1070 buf->st_blocks = (inode->size + RECORDSIZE - 1) / RECORDSIZE;
1071 #endif
1072 buf->st_atime = inode->atime;
1073 buf->st_mtime = inode->mtime;
1074 buf->st_ctime = inode->ctime;
1077 /* --------------------------------------------------------------------------------------------- */
1079 static int
1080 extfs_internal_stat (const vfs_path_t * vpath, struct stat *buf, gboolean resolve)
1082 struct archive *archive;
1083 char *q;
1084 struct entry *entry;
1085 int result = -1;
1087 q = extfs_get_path_mangle (vpath, &archive, FALSE);
1088 if (q == NULL)
1089 goto cleanup;
1090 entry = extfs_find_entry (archive->root_entry, q, FALSE, FALSE);
1091 if (entry == NULL)
1092 goto cleanup;
1093 if (resolve)
1095 entry = extfs_resolve_symlinks (entry);
1096 if (entry == NULL)
1097 goto cleanup;
1099 extfs_stat_move (buf, entry->inode);
1100 result = 0;
1101 cleanup:
1102 return result;
1105 /* --------------------------------------------------------------------------------------------- */
1107 static int
1108 extfs_stat (const vfs_path_t * vpath, struct stat *buf)
1110 return extfs_internal_stat (vpath, buf, TRUE);
1113 /* --------------------------------------------------------------------------------------------- */
1115 static int
1116 extfs_lstat (const vfs_path_t * vpath, struct stat *buf)
1118 return extfs_internal_stat (vpath, buf, FALSE);
1121 /* --------------------------------------------------------------------------------------------- */
1123 static int
1124 extfs_fstat (void *data, struct stat *buf)
1126 struct pseudofile *file = (struct pseudofile *) data;
1128 extfs_stat_move (buf, file->entry->inode);
1129 return 0;
1132 /* --------------------------------------------------------------------------------------------- */
1134 static int
1135 extfs_readlink (const vfs_path_t * vpath, char *buf, size_t size)
1137 struct archive *archive;
1138 char *q;
1139 size_t len;
1140 struct entry *entry;
1141 int result = -1;
1143 q = extfs_get_path_mangle (vpath, &archive, FALSE);
1144 if (q == NULL)
1145 goto cleanup;
1146 entry = extfs_find_entry (archive->root_entry, q, FALSE, FALSE);
1147 if (entry == NULL)
1148 goto cleanup;
1149 if (!S_ISLNK (entry->inode->mode))
1151 vfs_path_element_t *path_element;
1152 path_element = vfs_path_get_by_index (vpath, -1);
1153 path_element->class->verrno = EINVAL;
1154 goto cleanup;
1156 len = strlen (entry->inode->linkname);
1157 if (size < len)
1158 len = size;
1159 /* readlink() does not append a NUL character to buf */
1160 result = len;
1161 memcpy (buf, entry->inode->linkname, result);
1162 cleanup:
1163 return result;
1166 /* --------------------------------------------------------------------------------------------- */
1168 static int
1169 extfs_chown (const vfs_path_t * vpath, uid_t owner, gid_t group)
1171 (void) vpath;
1172 (void) owner;
1173 (void) group;
1174 return 0;
1177 /* --------------------------------------------------------------------------------------------- */
1179 static int
1180 extfs_chmod (const vfs_path_t * vpath, mode_t mode)
1182 (void) vpath;
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 (const vfs_path_t * vpath)
1203 struct archive *archive;
1204 char *q;
1205 struct entry *entry;
1206 int result = -1;
1208 q = extfs_get_path_mangle (vpath, &archive, FALSE);
1209 if (q == NULL)
1210 goto cleanup;
1211 entry = extfs_find_entry (archive->root_entry, q, FALSE, FALSE);
1212 if (entry == NULL)
1213 goto cleanup;
1214 entry = extfs_resolve_symlinks (entry);
1215 if (entry == NULL)
1216 goto cleanup;
1217 if (S_ISDIR (entry->inode->mode))
1219 vfs_path_element_t *path_element = vfs_path_get_by_index (vpath, -1);
1220 path_element->class->verrno = EISDIR;
1221 goto cleanup;
1223 if (extfs_cmd (" rm ", archive, entry, ""))
1225 my_errno = EIO;
1226 goto cleanup;
1228 extfs_remove_entry (entry);
1229 result = 0;
1230 cleanup:
1231 return result;
1234 /* --------------------------------------------------------------------------------------------- */
1236 static int
1237 extfs_mkdir (const vfs_path_t * vpath, mode_t mode)
1239 struct archive *archive;
1240 char *q;
1241 struct entry *entry;
1242 int result = -1;
1243 vfs_path_element_t *path_element = vfs_path_get_by_index (vpath, -1);
1245 (void) mode;
1247 q = extfs_get_path_mangle (vpath, &archive, FALSE);
1248 if (q == NULL)
1249 goto cleanup;
1250 entry = extfs_find_entry (archive->root_entry, q, FALSE, FALSE);
1251 if (entry != NULL)
1253 path_element->class->verrno = EEXIST;
1254 goto cleanup;
1256 entry = extfs_find_entry (archive->root_entry, q, TRUE, FALSE);
1257 if (entry == NULL)
1258 goto cleanup;
1259 entry = extfs_resolve_symlinks (entry);
1260 if (entry == NULL)
1261 goto cleanup;
1262 if (!S_ISDIR (entry->inode->mode))
1264 path_element->class->verrno = ENOTDIR;
1265 goto cleanup;
1268 if (extfs_cmd (" mkdir ", archive, entry, ""))
1270 my_errno = EIO;
1271 extfs_remove_entry (entry);
1272 goto cleanup;
1274 result = 0;
1275 cleanup:
1276 return result;
1279 /* --------------------------------------------------------------------------------------------- */
1281 static int
1282 extfs_rmdir (const vfs_path_t * vpath)
1284 struct archive *archive;
1285 char *q;
1286 struct entry *entry;
1287 int result = -1;
1289 q = extfs_get_path_mangle (vpath, &archive, FALSE);
1290 if (q == NULL)
1291 goto cleanup;
1292 entry = extfs_find_entry (archive->root_entry, q, FALSE, FALSE);
1293 if (entry == NULL)
1294 goto cleanup;
1295 entry = extfs_resolve_symlinks (entry);
1296 if (entry == NULL)
1297 goto cleanup;
1298 if (!S_ISDIR (entry->inode->mode))
1300 vfs_path_element_t *path_element = vfs_path_get_by_index (vpath, -1);
1301 path_element->class->verrno = ENOTDIR;
1302 goto cleanup;
1305 if (extfs_cmd (" rmdir ", archive, entry, ""))
1307 my_errno = EIO;
1308 goto cleanup;
1310 extfs_remove_entry (entry);
1311 result = 0;
1312 cleanup:
1313 return result;
1316 /* --------------------------------------------------------------------------------------------- */
1318 static int
1319 extfs_chdir (const vfs_path_t * vpath)
1321 struct archive *archive = NULL;
1322 char *q;
1323 struct entry *entry;
1325 my_errno = ENOTDIR;
1326 q = extfs_get_path (vpath, &archive, FALSE);
1327 if (q == NULL)
1328 return -1;
1329 entry = extfs_find_entry (archive->root_entry, q, FALSE, FALSE);
1330 g_free (q);
1331 if (entry == NULL)
1332 return -1;
1333 entry = extfs_resolve_symlinks (entry);
1334 if ((entry == NULL) || (!S_ISDIR (entry->inode->mode)))
1335 return -1;
1336 my_errno = 0;
1337 return 0;
1340 /* --------------------------------------------------------------------------------------------- */
1342 static off_t
1343 extfs_lseek (void *data, off_t offset, int whence)
1345 struct pseudofile *file = (struct pseudofile *) data;
1347 return lseek (file->local_handle, offset, whence);
1350 /* --------------------------------------------------------------------------------------------- */
1352 static vfsid
1353 extfs_getid (const vfs_path_t * vpath)
1355 struct archive *archive = NULL;
1356 char *p;
1358 p = extfs_get_path (vpath, &archive, TRUE);
1359 if (p == NULL)
1360 return NULL;
1361 g_free (p);
1362 return (vfsid) archive;
1365 /* --------------------------------------------------------------------------------------------- */
1367 static int
1368 extfs_nothingisopen (vfsid id)
1370 return (((struct archive *) id)->fd_usage <= 0);
1373 /* --------------------------------------------------------------------------------------------- */
1375 static void
1376 extfs_remove_entry (struct entry *e)
1378 int i = --e->inode->nlink;
1379 struct entry *pe, *ent, *prev;
1381 if (S_ISDIR (e->inode->mode) && e->inode->first_in_subdir != NULL)
1383 struct entry *f = e->inode->first_in_subdir;
1384 e->inode->first_in_subdir = NULL;
1385 extfs_remove_entry (f);
1387 pe = e->dir;
1388 if (e == pe->inode->first_in_subdir)
1389 pe->inode->first_in_subdir = e->next_in_dir;
1391 prev = NULL;
1392 for (ent = pe->inode->first_in_subdir; ent && ent->next_in_dir; ent = ent->next_in_dir)
1393 if (e == ent->next_in_dir)
1395 prev = ent;
1396 break;
1398 if (prev)
1399 prev->next_in_dir = e->next_in_dir;
1400 if (e == pe->inode->last_in_subdir)
1401 pe->inode->last_in_subdir = prev;
1403 if (i <= 0)
1405 if (e->inode->local_filename != NULL)
1407 unlink (e->inode->local_filename);
1408 g_free (e->inode->local_filename);
1410 g_free (e->inode->linkname);
1411 g_free (e->inode);
1414 g_free (e->name);
1415 g_free (e);
1418 /* --------------------------------------------------------------------------------------------- */
1420 static void
1421 extfs_free_entry (struct entry *e)
1423 int i = --e->inode->nlink;
1425 if (S_ISDIR (e->inode->mode) && e->inode->first_in_subdir != NULL)
1427 struct entry *f = e->inode->first_in_subdir;
1429 e->inode->first_in_subdir = NULL;
1430 extfs_free_entry (f);
1432 if (i <= 0)
1434 if (e->inode->local_filename != NULL)
1436 unlink (e->inode->local_filename);
1437 g_free (e->inode->local_filename);
1439 g_free (e->inode->linkname);
1440 g_free (e->inode);
1442 if (e->next_in_dir != NULL)
1443 extfs_free_entry (e->next_in_dir);
1444 g_free (e->name);
1445 g_free (e);
1448 /* --------------------------------------------------------------------------------------------- */
1450 static void
1451 extfs_free (vfsid id)
1453 struct archive *archive = (struct archive *) id;
1455 if (archive == first_archive)
1457 first_archive = archive->next;
1459 else
1461 struct archive *parc;
1462 for (parc = first_archive; parc != NULL; parc = parc->next)
1463 if (parc->next == archive)
1465 parc->next = archive->next;
1466 break;
1469 extfs_free_archive (archive);
1472 /* --------------------------------------------------------------------------------------------- */
1474 static char *
1475 extfs_getlocalcopy (const vfs_path_t * vpath)
1477 struct pseudofile *fp;
1478 char *p;
1480 fp = (struct pseudofile *) extfs_open (vpath, O_RDONLY, 0);
1481 if (fp == NULL)
1482 return NULL;
1483 if (fp->entry->inode->local_filename == NULL)
1485 extfs_close ((void *) fp);
1486 return NULL;
1488 p = g_strdup (fp->entry->inode->local_filename);
1489 fp->archive->fd_usage++;
1490 extfs_close ((void *) fp);
1491 return p;
1494 /* --------------------------------------------------------------------------------------------- */
1496 static int
1497 extfs_ungetlocalcopy (const vfs_path_t * vpath, const char *local, int has_changed)
1499 struct pseudofile *fp;
1501 fp = (struct pseudofile *) extfs_open (vpath, O_RDONLY, 0);
1502 if (fp == NULL)
1503 return 0;
1505 if (strcmp (fp->entry->inode->local_filename, local) == 0)
1507 fp->archive->fd_usage--;
1508 if (has_changed != 0)
1509 fp->has_changed = TRUE;
1510 extfs_close ((void *) fp);
1511 return 0;
1513 else
1515 /* Should not happen */
1516 extfs_close ((void *) fp);
1517 return 0;
1521 /* --------------------------------------------------------------------------------------------- */
1523 static gboolean
1524 extfs_get_plugins (const char *where, gboolean silent)
1526 char *dirname;
1527 GDir *dir;
1528 const char *filename;
1530 dirname = g_build_path (PATH_SEP_STR, where, MC_EXTFS_DIR, (char *) NULL);
1531 dir = g_dir_open (dirname, 0, NULL);
1533 /* We may not use vfs_die() message or message or similar,
1534 * UI is not initialized at this time and message would not
1535 * appear on screen. */
1536 if (dir == NULL)
1538 if (!silent)
1539 fprintf (stderr, _("Warning: cannot open %s directory\n"), dirname);
1540 g_free (dirname);
1541 return FALSE;
1544 if (extfs_plugins == NULL)
1545 extfs_plugins = g_array_sized_new (FALSE, TRUE, sizeof (extfs_plugin_info_t), 32);
1547 while ((filename = g_dir_read_name (dir)) != NULL)
1549 char fullname[MC_MAXPATHLEN];
1550 struct stat s;
1552 g_snprintf (fullname, sizeof (fullname), "%s" PATH_SEP_STR "%s", dirname, filename);
1554 if ((stat (fullname, &s) == 0)
1555 && S_ISREG (s.st_mode) && !S_ISDIR (s.st_mode)
1556 && (((s.st_mode & S_IXOTH) != 0) ||
1557 ((s.st_mode & S_IXUSR) != 0) || ((s.st_mode & S_IXGRP) != 0)))
1559 int f;
1561 f = open (fullname, O_RDONLY);
1563 if (f > 0)
1565 size_t len, i;
1566 extfs_plugin_info_t info;
1567 gboolean found = FALSE;
1569 close (f);
1571 /* Handle those with a trailing '+', those flag that the
1572 * file system does not require an archive to work
1574 len = strlen (filename);
1575 info.need_archive = (filename[len - 1] != '+');
1576 info.path = g_strconcat (dirname, PATH_SEP_STR, (char *) NULL);
1577 info.prefix = g_strdup (filename);
1579 /* prepare to compare file names without trailing '+' */
1580 if (!info.need_archive)
1581 info.prefix[len - 1] = '\0';
1583 /* don't overload already found plugin */
1584 for (i = 0; i < extfs_plugins->len; i++)
1586 extfs_plugin_info_t *p;
1588 p = &g_array_index (extfs_plugins, extfs_plugin_info_t, i);
1590 /* 2 files with same names cannot be in a directory */
1591 if ((strcmp (info.path, p->path) != 0)
1592 && (strcmp (info.prefix, p->prefix) == 0))
1594 found = TRUE;
1595 break;
1599 if (found)
1601 g_free (info.path);
1602 g_free (info.prefix);
1604 else
1606 /* restore file name */
1607 if (!info.need_archive)
1608 info.prefix[len - 1] = '+';
1609 g_array_append_val (extfs_plugins, info);
1615 g_dir_close (dir);
1616 g_free (dirname);
1618 return TRUE;
1621 /* --------------------------------------------------------------------------------------------- */
1623 static int
1624 extfs_init (struct vfs_class *me)
1626 gboolean d1, d2;
1628 (void) me;
1630 /* 1st: scan user directory */
1631 d1 = extfs_get_plugins (mc_config_get_data_path (), TRUE); /* silent about user dir */
1632 /* 2nd: scan system dir */
1633 d2 = extfs_get_plugins (LIBEXECDIR, d1);
1635 return (d1 || d2 ? 1 : 0);
1638 /* --------------------------------------------------------------------------------------------- */
1640 static void
1641 extfs_done (struct vfs_class *me)
1643 size_t i;
1644 struct archive *ar;
1646 (void) me;
1648 for (ar = first_archive; ar != NULL;)
1650 extfs_free ((vfsid) ar);
1651 ar = first_archive;
1654 for (i = 0; i < extfs_plugins->len; i++)
1656 extfs_plugin_info_t *info;
1658 info = &g_array_index (extfs_plugins, extfs_plugin_info_t, i);
1659 g_free (info->path);
1660 g_free (info->prefix);
1663 if (extfs_plugins != NULL)
1664 g_array_free (extfs_plugins, TRUE);
1667 /* --------------------------------------------------------------------------------------------- */
1669 static int
1670 extfs_setctl (const vfs_path_t * vpath, int ctlop, void *arg)
1672 (void) arg;
1674 if (ctlop == VFS_SETCTL_RUN)
1676 extfs_run (vpath);
1677 return 1;
1679 return 0;
1682 /* --------------------------------------------------------------------------------------------- */
1683 /*** public functions ****************************************************************************/
1684 /* --------------------------------------------------------------------------------------------- */
1686 void
1687 init_extfs (void)
1689 vfs_extfs_ops.name = "extfs";
1690 vfs_extfs_ops.init = extfs_init;
1691 vfs_extfs_ops.done = extfs_done;
1692 vfs_extfs_ops.fill_names = extfs_fill_names;
1693 vfs_extfs_ops.which = extfs_which;
1694 vfs_extfs_ops.open = extfs_open;
1695 vfs_extfs_ops.close = extfs_close;
1696 vfs_extfs_ops.read = extfs_read;
1697 vfs_extfs_ops.write = extfs_write;
1698 vfs_extfs_ops.opendir = extfs_opendir;
1699 vfs_extfs_ops.readdir = extfs_readdir;
1700 vfs_extfs_ops.closedir = extfs_closedir;
1701 vfs_extfs_ops.stat = extfs_stat;
1702 vfs_extfs_ops.lstat = extfs_lstat;
1703 vfs_extfs_ops.fstat = extfs_fstat;
1704 vfs_extfs_ops.chmod = extfs_chmod;
1705 vfs_extfs_ops.chown = extfs_chown;
1706 vfs_extfs_ops.readlink = extfs_readlink;
1707 vfs_extfs_ops.unlink = extfs_unlink;
1708 vfs_extfs_ops.chdir = extfs_chdir;
1709 vfs_extfs_ops.ferrno = extfs_errno;
1710 vfs_extfs_ops.lseek = extfs_lseek;
1711 vfs_extfs_ops.getid = extfs_getid;
1712 vfs_extfs_ops.nothingisopen = extfs_nothingisopen;
1713 vfs_extfs_ops.free = extfs_free;
1714 vfs_extfs_ops.getlocalcopy = extfs_getlocalcopy;
1715 vfs_extfs_ops.ungetlocalcopy = extfs_ungetlocalcopy;
1716 vfs_extfs_ops.mkdir = extfs_mkdir;
1717 vfs_extfs_ops.rmdir = extfs_rmdir;
1718 vfs_extfs_ops.setctl = extfs_setctl;
1719 vfs_register_class (&vfs_extfs_ops);
1722 /* --------------------------------------------------------------------------------------------- */