Added enhancements from Sergei which he attached to #241.
[midnight-commander.git] / vfs / extfs.c
blob03bbf254fc9efe5b8cb8adec65b0a1d011ab7ad6
1 /* Virtual File System: External file system.
2 Copyright (C) 1995, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
3 2006, 2007 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 /* Namespace: init_extfs */
25 #include <config.h>
26 #include <stdio.h>
27 #include <ctype.h>
28 #include <string.h>
29 #include <stdlib.h>
30 #include <sys/types.h>
31 #include <sys/stat.h>
32 #include <unistd.h>
33 #include <signal.h>
34 #ifdef HAVE_SYS_WAIT_H
35 #include <sys/wait.h>
36 #endif
37 #include <errno.h>
39 #include <mhl/string.h>
41 #include "../src/global.h"
42 #include "../src/tty.h" /* enable/disable interrupt key */
43 #include "../src/wtools.h" /* message() */
44 #include "../src/main.h" /* print_vfs_message */
45 #include "utilvfs.h"
46 #include "../src/execute.h" /* For shell_execute */
47 #include "vfs.h"
48 #include "vfs-impl.h"
49 #include "gc.h" /* vfs_rmstamp */
51 #undef ERRNOR
52 #define ERRNOR(x,y) do { my_errno = x; return y; } while(0)
54 struct inode {
55 nlink_t nlink;
56 struct entry *first_in_subdir; /* only used if this is a directory */
57 struct entry *last_in_subdir;
58 ino_t inode; /* This is inode # */
59 dev_t dev; /* This is an internal identification of the extfs archive */
60 struct archive *archive; /* And this is an archive structure */
61 dev_t rdev;
62 mode_t mode;
63 uid_t uid;
64 gid_t gid;
65 off_t size;
66 time_t mtime;
67 char *linkname;
68 time_t atime;
69 time_t ctime;
70 char *local_filename;
73 struct entry {
74 struct entry *next_in_dir;
75 struct entry *dir;
76 char *name;
77 struct inode *inode;
80 struct pseudofile {
81 struct archive *archive;
82 unsigned int has_changed:1;
83 int local_handle;
84 struct entry *entry;
87 struct archive {
88 int fstype;
89 char *name;
90 char *local_name;
91 struct stat local_stat;
92 dev_t rdev;
93 int fd_usage;
94 ino_t inode_counter;
95 struct entry *root_entry;
96 struct archive *next;
99 static struct entry *extfs_find_entry (struct entry *dir, char *name,
100 int make_dirs, int make_file);
101 static int extfs_which (struct vfs_class *me, const char *path);
102 static void extfs_remove_entry (struct entry *e);
103 static void extfs_free (vfsid id);
104 static void extfs_free_entry (struct entry *e);
106 static struct vfs_class vfs_extfs_ops;
107 static struct archive *first_archive = NULL;
108 static int my_errno = 0;
110 #define MAXEXTFS 32
111 static char *extfs_prefixes [MAXEXTFS];
112 static char extfs_need_archive [MAXEXTFS];
113 static int extfs_no = 0;
115 static void
116 extfs_fill_names (struct vfs_class *me, fill_names_f func)
118 struct archive *a = first_archive;
119 char *name;
121 (void) me;
123 while (a) {
124 name =
125 g_strconcat (a->name ? a->name : "", "#",
126 extfs_prefixes[a->fstype], (char *) NULL);
127 (*func) (name);
128 g_free (name);
129 a = a->next;
133 static void extfs_make_dots (struct entry *ent)
135 struct entry *entry = g_new (struct entry, 1);
136 struct entry *parentry = ent->dir;
137 struct inode *inode = ent->inode, *parent;
139 parent = (parentry != NULL) ? parentry->inode : NULL;
140 entry->name = g_strdup (".");
141 entry->inode = inode;
142 entry->dir = ent;
143 inode->local_filename = NULL;
144 inode->first_in_subdir = entry;
145 inode->nlink++;
146 entry->next_in_dir = g_new (struct entry, 1);
147 entry = entry->next_in_dir;
148 entry->name = g_strdup ("..");
149 inode->last_in_subdir = entry;
150 entry->next_in_dir = NULL;
151 if (parent != NULL) {
152 entry->inode = parent;
153 entry->dir = parentry;
154 parent->nlink++;
155 } else {
156 entry->inode = inode;
157 entry->dir = ent;
158 inode->nlink++;
162 static struct entry *extfs_generate_entry (struct archive *archive,
163 const char *name, struct entry *parentry, mode_t mode)
165 mode_t myumask;
166 struct inode *inode, *parent;
167 struct entry *entry;
169 parent = (parentry != NULL) ? parentry->inode : NULL;
170 entry = g_new (struct entry, 1);
172 entry->name = g_strdup (name);
173 entry->next_in_dir = NULL;
174 entry->dir = parentry;
175 if (parent != NULL) {
176 parent->last_in_subdir->next_in_dir = entry;
177 parent->last_in_subdir = entry;
179 inode = g_new (struct inode, 1);
180 entry->inode = inode;
181 inode->local_filename = NULL;
182 inode->linkname = NULL;
183 inode->last_in_subdir = NULL;
184 inode->inode = (archive->inode_counter)++;
185 inode->dev = archive->rdev;
186 inode->archive = archive;
187 myumask = umask (022);
188 umask (myumask);
189 inode->mode = mode & ~myumask;
190 mode = inode->mode;
191 inode->rdev = 0;
192 inode->uid = getuid ();
193 inode->gid = getgid ();
194 inode->size = 0;
195 inode->mtime = time (NULL);
196 inode->atime = inode->mtime;
197 inode->ctime = inode->mtime;
198 inode->nlink = 1;
199 if (S_ISDIR (mode))
200 extfs_make_dots (entry);
201 return entry;
204 #if 0
205 static void extfs_free_entries (struct entry *entry)
207 (void) entry;
208 return;
210 #endif
212 static void extfs_free_archive (struct archive *archive)
214 extfs_free_entry (archive->root_entry);
215 if (archive->local_name != NULL) {
216 struct stat my;
218 mc_stat (archive->local_name, &my);
219 mc_ungetlocalcopy (archive->name, archive->local_name,
220 archive->local_stat.st_mtime != my.st_mtime);
221 g_free(archive->local_name);
223 g_free (archive->name);
224 g_free (archive);
227 static FILE *
228 extfs_open_archive (int fstype, const char *name, struct archive **pparc)
230 static dev_t archive_counter = 0;
231 FILE *result;
232 mode_t mode;
233 char *cmd;
234 char *mc_extfsdir;
235 struct stat mystat;
236 struct archive *current_archive;
237 struct entry *root_entry;
238 char *local_name = NULL, *tmp = 0;
239 int uses_archive = extfs_need_archive[fstype];
241 if (uses_archive) {
242 if (mc_stat (name, &mystat) == -1)
243 return NULL;
244 if (!vfs_file_is_local (name)) {
245 local_name = mc_getlocalcopy (name);
246 if (local_name == NULL)
247 return NULL;
249 tmp = name_quote (name, 0);
252 mc_extfsdir = mhl_str_dir_plus_file (mc_home, "extfs" PATH_SEP_STR);
253 cmd =
254 g_strconcat (mc_extfsdir, extfs_prefixes[fstype], " list ",
255 local_name ? local_name : tmp, (char *) NULL);
256 g_free (tmp);
257 g_free (mc_extfsdir);
258 open_error_pipe ();
259 result = popen (cmd, "r");
260 g_free (cmd);
261 if (result == NULL) {
262 close_error_pipe (1, NULL);
263 if (local_name) {
264 mc_ungetlocalcopy (name, local_name, 0);
265 g_free(local_name);
267 return NULL;
269 #ifdef ___QNXNTO__
270 setvbuf (result, NULL, _IONBF, 0);
271 #endif
273 current_archive = g_new (struct archive, 1);
274 current_archive->fstype = fstype;
275 current_archive->name = name ? g_strdup (name) : NULL;
276 current_archive->local_name = local_name;
278 if (local_name != NULL)
279 mc_stat (local_name, &current_archive->local_stat);
280 current_archive->inode_counter = 0;
281 current_archive->fd_usage = 0;
282 current_archive->rdev = archive_counter++;
283 current_archive->next = first_archive;
284 first_archive = current_archive;
285 mode = mystat.st_mode & 07777;
286 if (mode & 0400)
287 mode |= 0100;
288 if (mode & 0040)
289 mode |= 0010;
290 if (mode & 0004)
291 mode |= 0001;
292 mode |= S_IFDIR;
293 root_entry = extfs_generate_entry (current_archive, "/", NULL, mode);
294 root_entry->inode->uid = mystat.st_uid;
295 root_entry->inode->gid = mystat.st_gid;
296 root_entry->inode->atime = mystat.st_atime;
297 root_entry->inode->ctime = mystat.st_ctime;
298 root_entry->inode->mtime = mystat.st_mtime;
299 current_archive->root_entry = root_entry;
301 *pparc = current_archive;
303 return result;
307 * Main loop for reading an archive.
308 * Return 0 on success, -1 on error.
310 static int
311 extfs_read_archive (int fstype, const char *name, struct archive **pparc)
313 FILE *extfsd;
314 char *buffer;
315 struct archive *current_archive;
316 char *current_file_name, *current_link_name;
318 if ((extfsd =
319 extfs_open_archive (fstype, name, &current_archive)) == NULL) {
320 message (1, MSG_ERROR, _("Cannot open %s archive\n%s"),
321 extfs_prefixes[fstype], name);
322 return -1;
325 buffer = g_malloc (4096);
326 while (fgets (buffer, 4096, extfsd) != NULL) {
327 struct stat hstat;
329 current_link_name = NULL;
330 if (vfs_parse_ls_lga
331 (buffer, &hstat, &current_file_name, &current_link_name)) {
332 struct entry *entry, *pent;
333 struct inode *inode;
334 char *p, *q, *cfn = current_file_name;
336 if (*cfn) {
337 if (*cfn == '/')
338 cfn++;
339 p = strchr (cfn, 0);
340 if (p != cfn && *(p - 1) == '/')
341 *(p - 1) = 0;
342 p = strrchr (cfn, '/');
343 if (p == NULL) {
344 p = cfn;
345 q = strchr (cfn, 0);
346 } else {
347 *(p++) = 0;
348 q = cfn;
350 if (S_ISDIR (hstat.st_mode)
351 && (!strcmp (p, ".") || !strcmp (p, "..")))
352 goto read_extfs_continue;
353 pent =
354 extfs_find_entry (current_archive->root_entry, q, 1,
356 if (pent == NULL) {
357 /* FIXME: Should clean everything one day */
358 g_free (buffer);
359 pclose (extfsd);
360 close_error_pipe (1, _("Inconsistent extfs archive"));
361 return -1;
363 entry = g_new (struct entry, 1);
364 entry->name = g_strdup (p);
365 entry->next_in_dir = NULL;
366 entry->dir = pent;
367 if (pent->inode->last_in_subdir) {
368 pent->inode->last_in_subdir->next_in_dir = entry;
369 pent->inode->last_in_subdir = entry;
371 if (!S_ISLNK (hstat.st_mode) && current_link_name != NULL) {
372 pent =
373 extfs_find_entry (current_archive->root_entry,
374 current_link_name, 0, 0);
375 if (pent == NULL) {
376 /* FIXME: Should clean everything one day */
377 g_free (buffer);
378 pclose (extfsd);
379 close_error_pipe (1,
380 _("Inconsistent extfs archive"));
381 return -1;
382 } else {
383 entry->inode = pent->inode;
384 pent->inode->nlink++;
386 } else {
387 inode = g_new (struct inode, 1);
388 entry->inode = inode;
389 inode->local_filename = NULL;
390 inode->inode = (current_archive->inode_counter)++;
391 inode->nlink = 1;
392 inode->dev = current_archive->rdev;
393 inode->archive = current_archive;
394 inode->mode = hstat.st_mode;
395 #ifdef HAVE_STRUCT_STAT_ST_RDEV
396 inode->rdev = hstat.st_rdev;
397 #else
398 inode->rdev = 0;
399 #endif
400 inode->uid = hstat.st_uid;
401 inode->gid = hstat.st_gid;
402 inode->size = hstat.st_size;
403 inode->mtime = hstat.st_mtime;
404 inode->atime = hstat.st_atime;
405 inode->ctime = hstat.st_ctime;
406 inode->first_in_subdir = NULL;
407 inode->last_in_subdir = NULL;
408 if (current_link_name != NULL
409 && S_ISLNK (hstat.st_mode)) {
410 inode->linkname = current_link_name;
411 current_link_name = NULL;
412 } else {
413 if (S_ISLNK (hstat.st_mode))
414 inode->mode &= ~S_IFLNK; /* You *DON'T* want to do this always */
415 inode->linkname = NULL;
417 if (S_ISDIR (hstat.st_mode))
418 extfs_make_dots (entry);
421 read_extfs_continue:
422 g_free (current_file_name);
423 g_free (current_link_name);
426 g_free (buffer);
428 /* Check if extfs 'list' returned 0 */
429 if (pclose (extfsd) != 0) {
430 extfs_free (current_archive);
431 close_error_pipe (1, _("Inconsistent extfs archive"));
432 return -1;
435 close_error_pipe (1, NULL);
436 *pparc = current_archive;
437 return 0;
441 * Dissect the path and create corresponding superblock. Note that inname
442 * can be changed and the result may point inside the original string.
444 static char *
445 extfs_get_path_mangle (struct vfs_class *me, char *inname, struct archive **archive,
446 int do_not_open)
448 char *local, *op;
449 const char *archive_name;
450 int result = -1;
451 struct archive *parc;
452 int fstype;
454 archive_name = inname;
455 vfs_split (inname, &local, &op);
457 fstype = extfs_which (me, op);
459 if (fstype == -1)
460 return NULL;
461 if (!local)
462 local = inname + strlen (inname);
465 * All filesystems should have some local archive, at least
466 * it can be '/'.
468 for (parc = first_archive; parc != NULL; parc = parc->next)
469 if (parc->name) {
470 if (!strcmp (parc->name, archive_name)) {
471 vfs_stamp (&vfs_extfs_ops, (vfsid) parc);
472 goto return_success;
476 result =
477 do_not_open ? -1 : extfs_read_archive (fstype, archive_name,
478 &parc);
479 if (result == -1)
480 ERRNOR (EIO, NULL);
482 return_success:
483 *archive = parc;
484 return local;
489 * Dissect the path and create corresponding superblock.
490 * The result should be freed.
492 static char *
493 extfs_get_path (struct vfs_class *me, const char *inname, struct archive **archive,
494 int do_not_open)
496 char *buf = g_strdup (inname);
497 char *res = extfs_get_path_mangle (me, buf, archive, do_not_open);
498 char *res2 = NULL;
499 if (res)
500 res2 = g_strdup (res);
501 g_free (buf);
502 return res2;
506 /* Return allocated path (without leading slash) inside the archive */
507 static char *extfs_get_path_from_entry (struct entry *entry)
509 struct list {
510 struct list *next;
511 char *name;
512 } *head, *p;
513 char *localpath;
514 size_t len;
516 for (len = 0, head = 0; entry->dir; entry = entry->dir) {
517 p = g_new (struct list, 1);
518 p->next = head;
519 p->name = entry->name;
520 head = p;
521 len += strlen (entry->name) + 1;
524 if (len == 0)
525 return g_strdup ("");
527 localpath = g_malloc (len);
528 *localpath = '\0';
529 while (head) {
530 strcat (localpath, head->name);
531 if (head->next)
532 strcat (localpath, "/");
533 p = head;
534 head = head->next;
535 g_free (p);
537 return (localpath);
541 struct loop_protect {
542 struct entry *entry;
543 struct loop_protect *next;
545 static int errloop;
546 static int notadir;
548 static struct entry *
549 extfs_find_entry_int (struct entry *dir, char *name,
550 struct loop_protect *list, int make_dirs, int make_file);
552 static struct entry *
553 extfs_resolve_symlinks_int (struct entry *entry,
554 struct loop_protect *list)
556 struct entry *pent;
557 struct loop_protect *looping;
559 if (!S_ISLNK (entry->inode->mode))
560 return entry;
561 for (looping = list; looping != NULL; looping = looping->next)
562 if (entry == looping->entry) { /* Here we protect us against symlink looping */
563 errloop = 1;
564 return NULL;
566 looping = g_new (struct loop_protect, 1);
567 looping->entry = entry;
568 looping->next = list;
569 pent = extfs_find_entry_int (entry->dir, entry->inode->linkname, looping, 0, 0);
570 g_free (looping);
571 if (pent == NULL)
572 my_errno = ENOENT;
573 return pent;
576 static struct entry *extfs_resolve_symlinks (struct entry *entry)
578 struct entry *res;
580 errloop = 0;
581 notadir = 0;
582 res = extfs_resolve_symlinks_int (entry, NULL);
583 if (res == NULL) {
584 if (errloop)
585 my_errno = ELOOP;
586 else if (notadir)
587 my_errno = ENOTDIR;
589 return res;
592 static const char *
593 extfs_get_archive_name (struct archive *archive)
595 const char *archive_name;
597 if (archive->local_name)
598 archive_name = archive->local_name;
599 else
600 archive_name = archive->name;
602 if (!archive_name || !*archive_name)
603 return "no_archive_name";
604 else
605 return archive_name;
608 /* Don't pass localname as NULL */
609 static int
610 extfs_cmd (const char *extfs_cmd, struct archive *archive,
611 struct entry *entry, const char *localname)
613 char *file;
614 char *quoted_file;
615 char *quoted_localname;
616 char *archive_name;
617 char *mc_extfsdir;
618 char *cmd;
619 int retval;
621 file = extfs_get_path_from_entry (entry);
622 quoted_file = name_quote (file, 0);
623 g_free (file);
624 archive_name = name_quote (extfs_get_archive_name (archive), 0);
625 quoted_localname = name_quote (localname, 0);
627 mc_extfsdir = mhl_str_dir_plus_file (mc_home, "extfs" PATH_SEP_STR);
628 cmd = g_strconcat (mc_extfsdir, extfs_prefixes[archive->fstype],
629 extfs_cmd, archive_name, " ", quoted_file, " ",
630 quoted_localname, (char *) NULL);
631 g_free (quoted_file);
632 g_free (quoted_localname);
633 g_free (mc_extfsdir);
634 g_free (archive_name);
636 open_error_pipe ();
637 retval = my_system (EXECUTE_AS_SHELL, shell, cmd);
638 g_free (cmd);
639 close_error_pipe (1, NULL);
640 return retval;
643 static void
644 extfs_run (struct vfs_class *me, const char *file)
646 struct archive *archive = NULL;
647 char *p, *q, *archive_name, *mc_extfsdir;
648 char *cmd;
650 if ((p = extfs_get_path (me, file, &archive, 0)) == NULL)
651 return;
652 q = name_quote (p, 0);
653 g_free (p);
655 archive_name = name_quote (extfs_get_archive_name (archive), 0);
656 mc_extfsdir = mhl_str_dir_plus_file (mc_home, "extfs" PATH_SEP_STR);
657 cmd = g_strconcat (mc_extfsdir, extfs_prefixes[archive->fstype],
658 " run ", archive_name, " ", q, (char *) NULL);
659 g_free (mc_extfsdir);
660 g_free (archive_name);
661 g_free (q);
662 shell_execute (cmd, 0);
663 g_free (cmd);
666 static void *
667 extfs_open (struct vfs_class *me, const char *file, int flags, int mode)
669 struct pseudofile *extfs_info;
670 struct archive *archive = NULL;
671 char *q;
672 struct entry *entry;
673 int local_handle;
674 int created = 0;
676 if ((q = extfs_get_path (me, file, &archive, 0)) == NULL)
677 return NULL;
678 entry = extfs_find_entry (archive->root_entry, q, 0, 0);
679 if (entry == NULL && (flags & O_CREAT)) {
680 /* Create new entry */
681 entry = extfs_find_entry (archive->root_entry, q, 0, 1);
682 created = (entry != NULL);
685 g_free (q);
686 if (entry == NULL)
687 return NULL;
688 if ((entry = extfs_resolve_symlinks (entry)) == NULL)
689 return NULL;
691 if (S_ISDIR (entry->inode->mode))
692 ERRNOR (EISDIR, NULL);
694 if (entry->inode->local_filename == NULL) {
695 char *local_filename;
697 local_handle = vfs_mkstemps (&local_filename, "extfs", entry->name);
699 if (local_handle == -1)
700 return NULL;
701 close (local_handle);
703 if (!created && !(flags & O_TRUNC)
704 && extfs_cmd (" copyout ", archive, entry, local_filename)) {
705 unlink (local_filename);
706 free (local_filename);
707 my_errno = EIO;
708 return NULL;
710 entry->inode->local_filename = local_filename;
713 local_handle =
714 open (entry->inode->local_filename, NO_LINEAR (flags), mode);
715 if (local_handle == -1)
716 ERRNOR (EIO, NULL);
718 extfs_info = g_new (struct pseudofile, 1);
719 extfs_info->archive = archive;
720 extfs_info->entry = entry;
721 extfs_info->has_changed = created;
722 extfs_info->local_handle = local_handle;
724 /* i.e. we had no open files and now we have one */
725 vfs_rmstamp (&vfs_extfs_ops, (vfsid) archive);
726 archive->fd_usage++;
727 return extfs_info;
730 static int extfs_read (void *data, char *buffer, int count)
732 struct pseudofile *file = (struct pseudofile *)data;
734 return read (file->local_handle, buffer, count);
737 static int
738 extfs_close (void *data)
740 struct pseudofile *file;
741 int errno_code = 0;
742 file = (struct pseudofile *) data;
744 close (file->local_handle);
746 /* Commit the file if it has changed */
747 if (file->has_changed) {
748 if (extfs_cmd
749 (" copyin ", file->archive, file->entry,
750 file->entry->inode->local_filename))
751 errno_code = EIO;
753 struct stat file_status;
754 if (stat (file->entry->inode->local_filename, &file_status) !=
756 errno_code = EIO;
757 else
758 file->entry->inode->size = file_status.st_size;
761 file->entry->inode->mtime = time (NULL);
764 file->archive->fd_usage--;
765 if (!file->archive->fd_usage)
766 vfs_stamp_create (&vfs_extfs_ops, file->archive);
768 g_free (data);
769 if (errno_code)
770 ERRNOR (EIO, -1);
771 return 0;
774 #define RECORDSIZE 512
776 static struct entry*
777 extfs_find_entry_int (struct entry *dir, char *name,
778 struct loop_protect *list, int make_dirs, int make_file)
780 struct entry *pent, *pdir;
781 char *p, *q, *name_end;
782 char c;
784 if (*name == '/') { /* Handle absolute paths */
785 name++;
786 dir = dir->inode->archive->root_entry;
789 pent = dir;
790 p = name;
791 name_end = name + strlen (name);
792 q = strchr (p, '/');
793 c = '/';
794 if (!q)
795 q = strchr (p, 0);
797 for (; pent != NULL && c && *p; ){
798 c = *q;
799 *q = 0;
801 if (strcmp (p, ".")){
802 if (!strcmp (p, ".."))
803 pent = pent->dir;
804 else {
805 if ((pent = extfs_resolve_symlinks_int (pent, list))==NULL){
806 *q = c;
807 return NULL;
809 if (!S_ISDIR (pent->inode->mode)){
810 *q = c;
811 notadir = 1;
812 return NULL;
814 pdir = pent;
815 for (pent = pent->inode->first_in_subdir; pent; pent = pent->next_in_dir)
816 /* Hack: I keep the original semanthic unless
817 q+1 would break in the strchr */
818 if (!strcmp (pent->name, p)){
819 if (q + 1 > name_end){
820 *q = c;
821 notadir = !S_ISDIR (pent->inode->mode);
822 return pent;
824 break;
827 /* When we load archive, we create automagically
828 * non-existant directories
830 if (pent == NULL && make_dirs) {
831 pent = extfs_generate_entry (dir->inode->archive, p, pdir, S_IFDIR | 0777);
833 if (pent == NULL && make_file) {
834 pent = extfs_generate_entry (dir->inode->archive, p, pdir, S_IFREG | 0666);
838 /* Next iteration */
839 *q = c;
840 p = q + 1;
841 q = strchr (p, '/');
842 if (!q)
843 q = strchr (p, 0);
845 if (pent == NULL)
846 my_errno = ENOENT;
847 return pent;
850 static struct entry *extfs_find_entry (struct entry *dir, char *name, int make_dirs, int make_file)
852 struct entry *res;
854 errloop = 0;
855 notadir = 0;
856 res = extfs_find_entry_int (dir, name, NULL, make_dirs, make_file);
857 if (res == NULL) {
858 if (errloop)
859 my_errno = ELOOP;
860 else if (notadir)
861 my_errno = ENOTDIR;
863 return res;
867 static int extfs_errno (struct vfs_class *me)
869 (void) me;
871 return my_errno;
874 static void * extfs_opendir (struct vfs_class *me, const char *dirname)
876 struct archive *archive = NULL;
877 char *q;
878 struct entry *entry;
879 struct entry **info;
881 if ((q = extfs_get_path (me, dirname, &archive, 0)) == NULL)
882 return NULL;
883 entry = extfs_find_entry (archive->root_entry, q, 0, 0);
884 g_free (q);
885 if (entry == NULL)
886 return NULL;
887 if ((entry = extfs_resolve_symlinks (entry)) == NULL)
888 return NULL;
889 if (!S_ISDIR (entry->inode->mode)) ERRNOR (ENOTDIR, NULL);
891 info = g_new (struct entry *, 2);
892 info[0] = entry->inode->first_in_subdir;
893 info[1] = entry->inode->first_in_subdir;
895 return info;
898 static void * extfs_readdir(void *data)
900 static union vfs_dirent dir;
901 struct entry **info = (struct entry **) data;
903 if (!*info)
904 return NULL;
906 g_strlcpy(dir.dent.d_name, (*info)->name, MC_MAXPATHLEN);
908 compute_namelen(&dir.dent);
909 *info = (*info)->next_in_dir;
911 return (void *) &dir;
914 static int extfs_closedir (void *data)
916 g_free (data);
917 return 0;
920 static void extfs_stat_move (struct stat *buf, const struct inode *inode)
922 buf->st_dev = inode->dev;
923 buf->st_ino = inode->inode;
924 buf->st_mode = inode->mode;
925 buf->st_nlink = inode->nlink;
926 buf->st_uid = inode->uid;
927 buf->st_gid = inode->gid;
928 #ifdef HAVE_STRUCT_STAT_ST_RDEV
929 buf->st_rdev = inode->rdev;
930 #endif
931 buf->st_size = inode->size;
932 #ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
933 buf->st_blksize = RECORDSIZE;
934 #endif
935 #ifdef HAVE_STRUCT_STAT_ST_BLOCKS
936 buf->st_blocks = (inode->size + RECORDSIZE - 1) / RECORDSIZE;
937 #endif
938 buf->st_atime = inode->atime;
939 buf->st_mtime = inode->mtime;
940 buf->st_ctime = inode->ctime;
943 static int
944 extfs_internal_stat (struct vfs_class *me, const char *path, struct stat *buf,
945 int resolve)
947 struct archive *archive;
948 char *q;
949 struct entry *entry;
950 char *path2 = g_strdup (path);
951 int result = -1;
953 if ((q = extfs_get_path_mangle (me, path2, &archive, 0)) == NULL)
954 goto cleanup;
955 entry = extfs_find_entry (archive->root_entry, q, 0, 0);
956 if (entry == NULL)
957 goto cleanup;
958 if (resolve && (entry = extfs_resolve_symlinks (entry)) == NULL)
959 goto cleanup;
960 extfs_stat_move (buf, entry->inode);
961 result = 0;
962 cleanup:
963 g_free (path2);
964 return result;
967 static int extfs_stat (struct vfs_class *me, const char *path, struct stat *buf)
969 return extfs_internal_stat (me, path, buf, 1);
972 static int extfs_lstat (struct vfs_class *me, const char *path, struct stat *buf)
974 return extfs_internal_stat (me, path, buf, 0);
977 static int extfs_fstat (void *data, struct stat *buf)
979 struct pseudofile *file = (struct pseudofile *)data;
981 extfs_stat_move (buf, file->entry->inode);
982 return 0;
985 static int
986 extfs_readlink (struct vfs_class *me, const char *path, char *buf, size_t size)
988 struct archive *archive;
989 char *q;
990 size_t len;
991 struct entry *entry;
992 char *mpath = g_strdup (path);
993 int result = -1;
995 if ((q = extfs_get_path_mangle (me, mpath, &archive, 0)) == NULL)
996 goto cleanup;
997 entry = extfs_find_entry (archive->root_entry, q, 0, 0);
998 if (entry == NULL)
999 goto cleanup;
1000 if (!S_ISLNK (entry->inode->mode)) {
1001 me->verrno = EINVAL;
1002 goto cleanup;
1004 len = strlen (entry->inode->linkname);
1005 if (size < len)
1006 len = size;
1007 /* readlink() does not append a NUL character to buf */
1008 memcpy (buf, entry->inode->linkname, result = len);
1009 cleanup:
1010 g_free (mpath);
1011 return result;
1014 static int extfs_chmod (struct vfs_class *me, const char *path, int mode)
1016 (void) me;
1017 (void) path;
1018 (void) mode;
1019 return 0;
1022 static int extfs_write (void *data, const char *buf, int nbyte)
1024 struct pseudofile *file = (struct pseudofile *)data;
1026 file->has_changed = 1;
1027 return write (file->local_handle, buf, nbyte);
1030 static int extfs_unlink (struct vfs_class *me, const char *file)
1032 struct archive *archive;
1033 char *q, *mpath = g_strdup (file);
1034 struct entry *entry;
1035 int result = -1;
1037 if ((q = extfs_get_path_mangle (me, mpath, &archive, 0)) == NULL)
1038 goto cleanup;
1039 entry = extfs_find_entry (archive->root_entry, q, 0, 0);
1040 if (entry == NULL)
1041 goto cleanup;
1042 if ((entry = extfs_resolve_symlinks (entry)) == NULL)
1043 goto cleanup;
1044 if (S_ISDIR (entry->inode->mode)) {
1045 me->verrno = EISDIR;
1046 goto cleanup;
1048 if (extfs_cmd (" rm ", archive, entry, "")){
1049 my_errno = EIO;
1050 goto cleanup;
1052 extfs_remove_entry (entry);
1053 result = 0;
1054 cleanup:
1055 g_free (mpath);
1056 return result;
1059 static int extfs_mkdir (struct vfs_class *me, const char *path, mode_t mode)
1061 struct archive *archive;
1062 char *q, *mpath = g_strdup(path);
1063 struct entry *entry;
1064 int result = -1;
1066 (void) mode;
1068 if ((q = extfs_get_path_mangle (me, mpath, &archive, 0)) == NULL)
1069 goto cleanup;
1070 entry = extfs_find_entry (archive->root_entry, q, 0, 0);
1071 if (entry != NULL) {
1072 me->verrno = EEXIST;
1073 goto cleanup;
1075 entry = extfs_find_entry (archive->root_entry, q, 1, 0);
1076 if (entry == NULL)
1077 goto cleanup;
1078 if ((entry = extfs_resolve_symlinks (entry)) == NULL)
1079 goto cleanup;
1080 if (!S_ISDIR (entry->inode->mode)) {
1081 me->verrno = ENOTDIR;
1082 goto cleanup;
1085 if (extfs_cmd (" mkdir ", archive, entry, "")){
1086 my_errno = EIO;
1087 extfs_remove_entry (entry);
1088 goto cleanup;
1090 result = 0;
1091 cleanup:
1092 g_free (mpath);
1093 return result;
1096 static int extfs_rmdir (struct vfs_class *me, const char *path)
1098 struct archive *archive;
1099 char *q, *mpath = g_strdup(path);
1100 struct entry *entry;
1101 int result = -1;
1103 if ((q = extfs_get_path_mangle (me, mpath, &archive, 0)) == NULL)
1104 goto cleanup;
1105 entry = extfs_find_entry (archive->root_entry, q, 0, 0);
1106 if (entry == NULL)
1107 goto cleanup;
1108 if ((entry = extfs_resolve_symlinks (entry)) == NULL)
1109 goto cleanup;
1110 if (!S_ISDIR (entry->inode->mode)) {
1111 me->verrno = ENOTDIR;
1112 goto cleanup;
1115 if (extfs_cmd (" rmdir ", archive, entry, "")){
1116 my_errno = EIO;
1117 goto cleanup;
1119 extfs_remove_entry (entry);
1120 result = 0;
1121 cleanup:
1122 g_free (mpath);
1123 return result;
1126 static int
1127 extfs_chdir (struct vfs_class *me, const char *path)
1129 struct archive *archive = NULL;
1130 char *q;
1131 struct entry *entry;
1133 my_errno = ENOTDIR;
1134 if ((q = extfs_get_path (me, path, &archive, 0)) == NULL)
1135 return -1;
1136 entry = extfs_find_entry (archive->root_entry, q, 0, 0);
1137 g_free (q);
1138 if (!entry)
1139 return -1;
1140 entry = extfs_resolve_symlinks (entry);
1141 if ((!entry) || (!S_ISDIR (entry->inode->mode)))
1142 return -1;
1143 my_errno = 0;
1144 return 0;
1147 static int extfs_lseek (void *data, off_t offset, int whence)
1149 struct pseudofile *file = (struct pseudofile *) data;
1151 return lseek (file->local_handle, offset, whence);
1154 static vfsid
1155 extfs_getid (struct vfs_class *me, const char *path)
1157 struct archive *archive = NULL;
1158 char *p;
1160 if (!(p = extfs_get_path (me, path, &archive, 1)))
1161 return NULL;
1162 g_free (p);
1163 return (vfsid) archive;
1166 static int extfs_nothingisopen (vfsid id)
1168 if (((struct archive *)id)->fd_usage <= 0)
1169 return 1;
1170 return 0;
1173 static void extfs_remove_entry (struct entry *e)
1175 int i = --(e->inode->nlink);
1176 struct entry *pe, *ent, *prev;
1178 if (S_ISDIR (e->inode->mode) && e->inode->first_in_subdir != NULL) {
1179 struct entry *f = e->inode->first_in_subdir;
1180 e->inode->first_in_subdir = NULL;
1181 extfs_remove_entry (f);
1183 pe = e->dir;
1184 if (e == pe->inode->first_in_subdir)
1185 pe->inode->first_in_subdir = e->next_in_dir;
1187 prev = NULL;
1188 for (ent = pe->inode->first_in_subdir; ent && ent->next_in_dir;
1189 ent = ent->next_in_dir)
1190 if (e == ent->next_in_dir) {
1191 prev = ent;
1192 break;
1194 if (prev)
1195 prev->next_in_dir = e->next_in_dir;
1196 if (e == pe->inode->last_in_subdir)
1197 pe->inode->last_in_subdir = prev;
1199 if (i <= 0) {
1200 if (e->inode->local_filename != NULL) {
1201 unlink (e->inode->local_filename);
1202 free (e->inode->local_filename);
1204 g_free (e->inode->linkname);
1205 g_free (e->inode);
1208 g_free (e->name);
1209 g_free (e);
1212 static void extfs_free_entry (struct entry *e)
1214 int i = --(e->inode->nlink);
1215 if (S_ISDIR (e->inode->mode) && e->inode->first_in_subdir != NULL) {
1216 struct entry *f = e->inode->first_in_subdir;
1218 e->inode->first_in_subdir = NULL;
1219 extfs_free_entry (f);
1221 if (i <= 0) {
1222 if (e->inode->local_filename != NULL) {
1223 unlink (e->inode->local_filename);
1224 free (e->inode->local_filename);
1226 g_free (e->inode->linkname);
1227 g_free (e->inode);
1229 if (e->next_in_dir != NULL)
1230 extfs_free_entry (e->next_in_dir);
1231 g_free (e->name);
1232 g_free (e);
1235 static void extfs_free (vfsid id)
1237 struct archive *parc;
1238 struct archive *archive = (struct archive *)id;
1240 if (archive == first_archive) {
1241 first_archive = archive->next;
1242 } else {
1243 for (parc = first_archive; parc != NULL; parc = parc->next)
1244 if (parc->next == archive) {
1245 parc->next = archive->next;
1246 break;
1249 extfs_free_archive (archive);
1252 static char *
1253 extfs_getlocalcopy (struct vfs_class *me, const char *path)
1255 struct pseudofile *fp =
1256 (struct pseudofile *) extfs_open (me, path, O_RDONLY, 0);
1257 char *p;
1259 if (fp == NULL)
1260 return NULL;
1261 if (fp->entry->inode->local_filename == NULL) {
1262 extfs_close ((void *) fp);
1263 return NULL;
1265 p = g_strdup (fp->entry->inode->local_filename);
1266 fp->archive->fd_usage++;
1267 extfs_close ((void *) fp);
1268 return p;
1271 static int
1272 extfs_ungetlocalcopy (struct vfs_class *me, const char *path,
1273 const char *local, int has_changed)
1275 struct pseudofile *fp =
1276 (struct pseudofile *) extfs_open (me, path, O_RDONLY, 0);
1278 if (fp == NULL)
1279 return 0;
1280 if (!strcmp (fp->entry->inode->local_filename, local)) {
1281 fp->archive->fd_usage--;
1282 fp->has_changed |= has_changed;
1283 extfs_close ((void *) fp);
1284 return 0;
1285 } else {
1286 /* Should not happen */
1287 extfs_close ((void *) fp);
1288 return 0;
1293 static int extfs_init (struct vfs_class *me)
1295 FILE *cfg;
1296 char *mc_extfsini;
1297 char key[256];
1299 (void) me;
1301 mc_extfsini = mhl_str_dir_plus_file (mc_home, "extfs" PATH_SEP_STR "extfs.ini");
1302 cfg = fopen (mc_extfsini, "r");
1304 /* We may not use vfs_die() message or message or similar,
1305 * UI is not initialized at this time and message would not
1306 * appear on screen. */
1307 if (!cfg) {
1308 fprintf (stderr, _("Warning: file %s not found\n"), mc_extfsini);
1309 g_free (mc_extfsini);
1310 return 0;
1313 extfs_no = 0;
1314 while (extfs_no < MAXEXTFS && fgets (key, sizeof (key), cfg)) {
1315 char *c;
1317 /* Handle those with a trailing ':', those flag that the
1318 * file system does not require an archive to work
1321 if (*key == '[') {
1322 fprintf(stderr, "Warning: You need to update your %s file.\n",
1323 mc_extfsini);
1324 fclose(cfg);
1325 g_free (mc_extfsini);
1326 return 0;
1328 if (*key == '#' || *key == '\n')
1329 continue;
1331 if ((c = strchr (key, '\n'))){
1332 *c-- = 0;
1333 } else { /* Last line without newline or strlen (key) > 255 */
1334 c = &key [strlen (key) - 1];
1336 extfs_need_archive [extfs_no] = !(*c == ':');
1337 if (*c == ':')
1338 *c = 0;
1339 if (!(*key))
1340 continue;
1342 extfs_prefixes [extfs_no++] = g_strdup (key);
1344 fclose(cfg);
1345 g_free (mc_extfsini);
1346 return 1;
1349 static int extfs_which (struct vfs_class *me, const char *path)
1351 int i;
1353 (void) me;
1355 for (i = 0; i < extfs_no; i++)
1356 if (!strcmp (path, extfs_prefixes [i]))
1357 return i;
1358 return -1;
1361 static void extfs_done (struct vfs_class *me)
1363 int i;
1364 struct archive *ar;
1366 (void) me;
1368 for (ar = first_archive; ar != NULL;) {
1369 extfs_free ((vfsid) ar);
1370 ar = first_archive;
1373 for (i = 0; i < extfs_no; i++ )
1374 g_free (extfs_prefixes [i]);
1375 extfs_no = 0;
1378 static int
1379 extfs_setctl (struct vfs_class *me, const char *path, int ctlop, void *arg)
1381 (void) arg;
1383 if (ctlop == VFS_SETCTL_RUN) {
1384 extfs_run (me, path);
1385 return 1;
1387 return 0;
1390 void
1391 init_extfs (void)
1393 vfs_extfs_ops.name = "extfs";
1394 vfs_extfs_ops.init = extfs_init;
1395 vfs_extfs_ops.done = extfs_done;
1396 vfs_extfs_ops.fill_names = extfs_fill_names;
1397 vfs_extfs_ops.which = extfs_which;
1398 vfs_extfs_ops.open = extfs_open;
1399 vfs_extfs_ops.close = extfs_close;
1400 vfs_extfs_ops.read = extfs_read;
1401 vfs_extfs_ops.write = extfs_write;
1402 vfs_extfs_ops.opendir = extfs_opendir;
1403 vfs_extfs_ops.readdir = extfs_readdir;
1404 vfs_extfs_ops.closedir = extfs_closedir;
1405 vfs_extfs_ops.stat = extfs_stat;
1406 vfs_extfs_ops.lstat = extfs_lstat;
1407 vfs_extfs_ops.fstat = extfs_fstat;
1408 vfs_extfs_ops.chmod = extfs_chmod;
1409 vfs_extfs_ops.readlink = extfs_readlink;
1410 vfs_extfs_ops.unlink = extfs_unlink;
1411 vfs_extfs_ops.chdir = extfs_chdir;
1412 vfs_extfs_ops.ferrno = extfs_errno;
1413 vfs_extfs_ops.lseek = extfs_lseek;
1414 vfs_extfs_ops.getid = extfs_getid;
1415 vfs_extfs_ops.nothingisopen = extfs_nothingisopen;
1416 vfs_extfs_ops.free = extfs_free;
1417 vfs_extfs_ops.getlocalcopy = extfs_getlocalcopy;
1418 vfs_extfs_ops.ungetlocalcopy = extfs_ungetlocalcopy;
1419 vfs_extfs_ops.mkdir = extfs_mkdir;
1420 vfs_extfs_ops.rmdir = extfs_rmdir;
1421 vfs_extfs_ops.setctl = extfs_setctl;
1422 vfs_register_class (&vfs_extfs_ops);