* extfs.c (extfs_init): Fix possible off-by-one buffer underflow
[midnight-commander.git] / vfs / extfs.c
blob66f2cdd71df4d14c7022c7e73733da5f797f1425
1 /* Virtual File System: External file system.
2 Copyright (C) 1995 The Free Software Foundation
4 Written by: 1995 Jakub Jelinek
5 Rewritten by: 1998 Pavel Machek
6 Additional changes by: 1999 Andrew T. Veliath
8 This program is free software; you can redistribute it and/or
9 modify it under the terms of the GNU Library General Public License
10 as published by the Free Software Foundation; either version 2 of
11 the License, or (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU Library General Public License for more details.
18 You should have received a copy of the GNU Library General Public
19 License along with this program; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
22 /* Namespace: init_extfs */
24 #include <config.h>
25 #include <stdio.h>
26 #include <ctype.h>
27 #include <string.h>
28 #include <stdlib.h>
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 #include <unistd.h>
32 #include <signal.h>
33 #ifdef HAVE_SYS_WAIT_H
34 #include <sys/wait.h>
35 #endif
36 #include <errno.h>
37 #include "utilvfs.h"
38 #include "../src/execute.h" /* For shell_execute */
39 #include "vfs.h"
40 #include "gc.h" /* vfs_rmstamp */
42 #undef ERRNOR
43 #define ERRNOR(x,y) do { my_errno = x; return y; } while(0)
45 struct inode {
46 nlink_t nlink;
47 struct entry *first_in_subdir; /* only used if this is a directory */
48 struct entry *last_in_subdir;
49 ino_t inode; /* This is inode # */
50 dev_t dev; /* This is an internal identification of the extfs archive */
51 struct archive *archive; /* And this is an archive structure */
52 dev_t rdev;
53 mode_t mode;
54 uid_t uid;
55 gid_t gid;
56 int size;
57 time_t mtime;
58 char linkflag;
59 char *linkname;
60 time_t atime;
61 time_t ctime;
62 char *local_filename;
65 struct entry {
66 struct entry *next_in_dir;
67 struct entry *dir;
68 char *name;
69 struct inode *inode;
72 struct pseudofile {
73 struct archive *archive;
74 unsigned int has_changed:1;
75 int local_handle;
76 struct entry *entry;
79 struct archive {
80 int fstype;
81 char *name;
82 char *local_name;
83 struct stat local_stat;
84 dev_t rdev;
85 int fd_usage;
86 ino_t inode_counter;
87 struct entry *root_entry;
88 struct archive *next;
91 static struct entry *extfs_find_entry (struct entry *dir, char *name,
92 int make_dirs, int make_file);
93 static int extfs_which (struct vfs_class *me, char *path);
94 static void extfs_remove_entry (struct entry *e);
95 static void extfs_free (vfsid id);
97 static struct vfs_class vfs_extfs_ops;
98 static struct archive *first_archive = NULL;
99 static int my_errno = 0;
101 #define MAXEXTFS 32
102 static char *extfs_prefixes [MAXEXTFS];
103 static char extfs_need_archive [MAXEXTFS];
104 static int extfs_no = 0;
106 static void
107 extfs_fill_names (struct vfs_class *me, void (*func) (char *))
109 struct archive *a = first_archive;
110 char *name;
112 while (a) {
113 name =
114 g_strconcat (a->name ? a->name : "", "#",
115 extfs_prefixes[a->fstype], NULL);
116 (*func) (name);
117 g_free (name);
118 a = a->next;
122 static void extfs_make_dots (struct entry *ent)
124 struct entry *entry = g_new (struct entry, 1);
125 struct entry *parentry = ent->dir;
126 struct inode *inode = ent->inode, *parent;
128 parent = (parentry != NULL) ? parentry->inode : NULL;
129 entry->name = g_strdup (".");
130 entry->inode = inode;
131 entry->dir = ent;
132 inode->local_filename = NULL;
133 inode->first_in_subdir = entry;
134 inode->last_in_subdir = entry;
135 inode->nlink++;
136 entry->next_in_dir = g_new (struct entry, 1);
137 entry=entry->next_in_dir;
138 entry->name = g_strdup ("..");
139 inode->last_in_subdir = entry;
140 entry->next_in_dir = NULL;
141 if (parent != NULL) {
142 entry->inode = parent;
143 entry->dir = parentry;
144 parent->nlink++;
145 } else {
146 entry->inode = inode;
147 entry->dir = ent;
148 inode->nlink++;
152 static struct entry *extfs_generate_entry (struct archive *archive,
153 char *name, struct entry *parentry, mode_t mode)
155 mode_t myumask;
156 struct inode *inode, *parent;
157 struct entry *entry;
159 parent = (parentry != NULL) ? parentry->inode : NULL;
160 entry = g_new (struct entry, 1);
162 entry->name = g_strdup (name);
163 entry->next_in_dir = NULL;
164 entry->dir = parentry;
165 if (parent != NULL) {
166 parent->last_in_subdir->next_in_dir = entry;
167 parent->last_in_subdir = entry;
169 inode = g_new (struct inode, 1);
170 entry->inode = inode;
171 inode->local_filename = NULL;
172 inode->linkname = 0;
173 inode->inode = (archive->inode_counter)++;
174 inode->dev = archive->rdev;
175 inode->archive = archive;
176 myumask = umask (022);
177 umask (myumask);
178 inode->mode = mode & ~myumask;
179 mode = inode->mode;
180 inode->rdev = 0;
181 inode->uid = getuid ();
182 inode->gid = getgid ();
183 inode->size = 0;
184 inode->mtime = time (NULL);
185 inode->atime = inode->mtime;
186 inode->ctime = inode->mtime;
187 inode->nlink = 1;
188 if (S_ISDIR (mode))
189 extfs_make_dots (entry);
190 return entry;
193 static void extfs_free_entries (struct entry *entry)
195 return;
198 static void extfs_free_archive (struct archive *archive)
200 extfs_free_entries (archive->root_entry);
201 if (archive->local_name != NULL) {
202 struct stat my;
204 mc_stat (archive->local_name, &my);
205 mc_ungetlocalcopy (archive->name, archive->local_name,
206 archive->local_stat.st_mtime != my.st_mtime);
207 /* mc_ungetlocalcopy() frees local_name for us */
209 if (archive->name)
210 g_free (archive->name);
211 g_free (archive);
214 static FILE *
215 extfs_open_archive (int fstype, const char *name, struct archive **pparc)
217 static dev_t archive_counter = 0;
218 FILE *result;
219 mode_t mode;
220 char *cmd;
221 char *mc_extfsdir;
222 struct stat mystat;
223 struct archive *current_archive;
224 struct entry *root_entry;
225 char *local_name = NULL, *tmp = 0;
226 int uses_archive = extfs_need_archive[fstype];
228 if (uses_archive) {
229 if (mc_stat (name, &mystat) == -1)
230 return NULL;
231 if (!vfs_file_is_local (name)) {
232 local_name = mc_getlocalcopy (name);
233 if (local_name == NULL)
234 return NULL;
236 tmp = name_quote (name, 0);
239 mc_extfsdir = concat_dir_and_file (mc_home, "extfs" PATH_SEP_STR);
240 cmd =
241 g_strconcat (mc_extfsdir, extfs_prefixes[fstype], " list ",
242 local_name ? local_name : tmp, NULL);
243 if (tmp)
244 g_free (tmp);
245 g_free (mc_extfsdir);
246 open_error_pipe ();
247 result = popen (cmd, "r");
248 g_free (cmd);
249 if (result == NULL) {
250 close_error_pipe (1, NULL);
251 if (local_name)
252 mc_ungetlocalcopy (name, local_name, 0);
253 return NULL;
256 current_archive = g_new (struct archive, 1);
257 current_archive->fstype = fstype;
258 current_archive->name = name ? g_strdup (name) : NULL;
259 current_archive->local_name = local_name;
261 if (local_name != NULL)
262 mc_stat (local_name, &current_archive->local_stat);
263 current_archive->inode_counter = 0;
264 current_archive->fd_usage = 0;
265 current_archive->rdev = archive_counter++;
266 current_archive->next = first_archive;
267 first_archive = current_archive;
268 mode = mystat.st_mode & 07777;
269 if (mode & 0400)
270 mode |= 0100;
271 if (mode & 0040)
272 mode |= 0010;
273 if (mode & 0004)
274 mode |= 0001;
275 mode |= S_IFDIR;
276 root_entry = extfs_generate_entry (current_archive, "/", NULL, mode);
277 root_entry->inode->uid = mystat.st_uid;
278 root_entry->inode->gid = mystat.st_gid;
279 root_entry->inode->atime = mystat.st_atime;
280 root_entry->inode->ctime = mystat.st_ctime;
281 root_entry->inode->mtime = mystat.st_mtime;
282 current_archive->root_entry = root_entry;
284 *pparc = current_archive;
286 return result;
290 * Main loop for reading an archive.
291 * Return 0 on success, -1 on error.
293 static int
294 extfs_read_archive (int fstype, const char *name, struct archive **pparc)
296 FILE *extfsd;
297 char *buffer;
298 struct archive *current_archive;
299 char *current_file_name, *current_link_name;
301 if ((extfsd =
302 extfs_open_archive (fstype, name, &current_archive)) == NULL) {
303 message (1, MSG_ERROR, _("Cannot open %s archive\n%s"),
304 extfs_prefixes[fstype], name);
305 return -1;
308 buffer = g_malloc (4096);
309 while (fgets (buffer, 4096, extfsd) != NULL) {
310 struct stat hstat;
312 current_link_name = NULL;
313 if (vfs_parse_ls_lga
314 (buffer, &hstat, &current_file_name, &current_link_name)) {
315 struct entry *entry, *pent;
316 struct inode *inode;
317 char *p, *q, *cfn = current_file_name;
319 if (*cfn) {
320 if (*cfn == '/')
321 cfn++;
322 p = strchr (cfn, 0);
323 if (p != cfn && *(p - 1) == '/')
324 *(p - 1) = 0;
325 p = strrchr (cfn, '/');
326 if (p == NULL) {
327 p = cfn;
328 q = strchr (cfn, 0);
329 } else {
330 *(p++) = 0;
331 q = cfn;
333 if (S_ISDIR (hstat.st_mode)
334 && (!strcmp (p, ".") || !strcmp (p, "..")))
335 goto read_extfs_continue;
336 pent =
337 extfs_find_entry (current_archive->root_entry, q, 1,
339 if (pent == NULL) {
340 /* FIXME: Should clean everything one day */
341 g_free (buffer);
342 pclose (extfsd);
343 close_error_pipe (1, _("Inconsistent extfs archive"));
344 return -1;
346 entry = g_new (struct entry, 1);
347 entry->name = g_strdup (p);
348 entry->next_in_dir = NULL;
349 entry->dir = pent;
350 if (pent != NULL) {
351 if (pent->inode->last_in_subdir) {
352 pent->inode->last_in_subdir->next_in_dir = entry;
353 pent->inode->last_in_subdir = entry;
356 if (!S_ISLNK (hstat.st_mode) && current_link_name != NULL) {
357 pent =
358 extfs_find_entry (current_archive->root_entry,
359 current_link_name, 0, 0);
360 if (pent == NULL) {
361 /* FIXME: Should clean everything one day */
362 g_free (buffer);
363 pclose (extfsd);
364 close_error_pipe (1,
365 _("Inconsistent extfs archive"));
366 return -1;
367 } else {
368 entry->inode = pent->inode;
369 pent->inode->nlink++;
371 } else {
372 inode = g_new (struct inode, 1);
373 entry->inode = inode;
374 inode->local_filename = NULL;
375 inode->inode = (current_archive->inode_counter)++;
376 inode->nlink = 1;
377 inode->dev = current_archive->rdev;
378 inode->archive = current_archive;
379 inode->mode = hstat.st_mode;
380 #ifdef HAVE_STRUCT_STAT_ST_RDEV
381 inode->rdev = hstat.st_rdev;
382 #else
383 inode->rdev = 0;
384 #endif
385 inode->uid = hstat.st_uid;
386 inode->gid = hstat.st_gid;
387 inode->size = hstat.st_size;
388 inode->mtime = hstat.st_mtime;
389 inode->atime = hstat.st_atime;
390 inode->ctime = hstat.st_ctime;
391 if (current_link_name != NULL
392 && S_ISLNK (hstat.st_mode)) {
393 inode->linkname = current_link_name;
394 current_link_name = NULL;
395 } else {
396 if (S_ISLNK (hstat.st_mode))
397 inode->mode &= ~S_IFLNK; /* You *DON'T* want to do this always */
398 inode->linkname = NULL;
400 if (S_ISDIR (hstat.st_mode))
401 extfs_make_dots (entry);
404 read_extfs_continue:
405 g_free (current_file_name);
406 if (current_link_name != NULL)
407 g_free (current_link_name);
411 /* Check if extfs 'list' returned 0 */
412 if (pclose (extfsd) != 0) {
413 g_free (buffer);
414 extfs_free (current_archive);
415 close_error_pipe (1, _("Inconsistent extfs archive"));
416 return -1;
419 close_error_pipe (1, NULL);
420 *pparc = current_archive;
421 g_free (buffer);
422 return 0;
426 * Dissect the path and create corresponding superblock. Note that inname
427 * can be changed and the result may point inside the original string.
429 static char *
430 extfs_get_path_mangle (char *inname, struct archive **archive, int is_dir,
431 int do_not_open)
433 char *local, *op;
434 const char *archive_name;
435 int result = -1;
436 struct archive *parc;
437 int fstype;
439 archive_name = inname;
440 vfs_split (inname, &local, &op);
443 * FIXME: we really should pass self pointer. But as we know that
444 * extfs_which does not touch struct vfs_class *me, it does not matter for now
446 fstype = extfs_which (NULL, op);
448 if (fstype == -1)
449 return NULL;
450 if (!local)
451 local = "";
454 * All filesystems should have some local archive, at least
455 * it can be '/'.
457 for (parc = first_archive; parc != NULL; parc = parc->next)
458 if (parc->name) {
459 if (!strcmp (parc->name, archive_name)) {
460 vfs_stamp (&vfs_extfs_ops, (vfsid) parc);
461 goto return_success;
465 result =
466 do_not_open ? -1 : extfs_read_archive (fstype, archive_name,
467 &parc);
468 if (result == -1)
469 ERRNOR (EIO, NULL);
471 return_success:
472 *archive = parc;
473 return local;
478 * Dissect the path and create corresponding superblock.
479 * The result should be freed.
481 static char *
482 extfs_get_path (const char *inname, struct archive **archive, int is_dir,
483 int do_not_open)
485 char *buf = g_strdup (inname);
486 char *res = extfs_get_path_mangle (buf, archive, is_dir, do_not_open);
487 char *res2 = NULL;
488 if (res)
489 res2 = g_strdup (res);
490 g_free (buf);
491 return res2;
495 /* Return allocated path (without leading slash) inside the archive */
496 static char *extfs_get_path_from_entry (struct entry *entry)
498 struct list {
499 struct list *next;
500 char *name;
501 } *head, *p;
502 char *localpath;
503 size_t len;
505 for (len = 0, head = 0; entry->dir; entry = entry->dir) {
506 p = g_new (struct list, 1);
507 p->next = head;
508 p->name = entry->name;
509 head = p;
510 len += strlen (entry->name) + 1;
513 if (len == 0)
514 return g_strdup ("");
516 localpath = g_malloc (len);
517 *localpath = '\0';
518 while (head) {
519 strcat (localpath, head->name);
520 if (head->next)
521 strcat (localpath, "/");
522 p = head;
523 head = head->next;
524 g_free (p);
526 return (localpath);
530 struct loop_protect {
531 struct entry *entry;
532 struct loop_protect *next;
534 static int errloop;
535 static int notadir;
537 static struct entry *
538 extfs_find_entry_int (struct entry *dir, char *name,
539 struct loop_protect *list, int make_dirs, int make_file);
541 static struct entry *
542 extfs_resolve_symlinks_int (struct entry *entry,
543 struct loop_protect *list)
545 struct entry *pent;
546 struct loop_protect *looping;
548 if (!S_ISLNK (entry->inode->mode))
549 return entry;
550 for (looping = list; looping != NULL; looping = looping->next)
551 if (entry == looping->entry) { /* Here we protect us against symlink looping */
552 errloop = 1;
553 return NULL;
555 looping = g_new (struct loop_protect, 1);
556 looping->entry = entry;
557 looping->next = list;
558 pent = extfs_find_entry_int (entry->dir, entry->inode->linkname, looping, 0, 0);
559 g_free (looping);
560 if (pent == NULL)
561 my_errno = ENOENT;
562 return pent;
565 static struct entry *extfs_resolve_symlinks (struct entry *entry)
567 struct entry *res;
569 errloop = 0;
570 notadir = 0;
571 res = extfs_resolve_symlinks_int (entry, NULL);
572 if (res == NULL) {
573 if (errloop)
574 my_errno = ELOOP;
575 else if (notadir)
576 my_errno = ENOTDIR;
578 return res;
581 static char *extfs_get_archive_name (struct archive *archive)
583 char *archive_name;
585 if (archive->local_name)
586 archive_name = archive->local_name;
587 else
588 archive_name = archive->name;
590 if (!archive_name || !*archive_name)
591 return "no_archive_name";
592 else
593 return archive_name;
596 /* Don't pass localname as NULL */
597 static int
598 extfs_cmd (const char *extfs_cmd, struct archive *archive,
599 struct entry *entry, const char *localname)
601 char *file;
602 char *quoted_file;
603 char *quoted_localname;
604 char *archive_name;
605 char *mc_extfsdir;
606 char *cmd;
607 int retval;
609 file = extfs_get_path_from_entry (entry);
610 quoted_file = name_quote (file, 0);
611 g_free (file);
612 archive_name = name_quote (extfs_get_archive_name (archive), 0);
613 quoted_localname = name_quote (localname, 0);
615 mc_extfsdir = concat_dir_and_file (mc_home, "extfs" PATH_SEP_STR);
616 cmd = g_strconcat (mc_extfsdir, extfs_prefixes[archive->fstype],
617 extfs_cmd, archive_name, " ", quoted_file, " ",
618 quoted_localname, NULL);
619 g_free (quoted_file);
620 g_free (quoted_localname);
621 g_free (mc_extfsdir);
622 g_free (archive_name);
624 open_error_pipe ();
625 retval = my_system (EXECUTE_AS_SHELL, shell, cmd);
626 g_free (cmd);
627 close_error_pipe (1, NULL);
628 return retval;
631 static void
632 extfs_run (char *file)
634 struct archive *archive;
635 char *p, *q, *archive_name, *mc_extfsdir;
636 char *cmd;
638 if ((p = extfs_get_path (file, &archive, 0, 0)) == NULL)
639 return;
640 q = name_quote (p, 0);
641 g_free (p);
643 archive_name = name_quote (extfs_get_archive_name (archive), 0);
644 mc_extfsdir = concat_dir_and_file (mc_home, "extfs" PATH_SEP_STR);
645 cmd = g_strconcat (mc_extfsdir, extfs_prefixes[archive->fstype],
646 " run ", archive_name, " ", q, NULL);
647 g_free (mc_extfsdir);
648 g_free (archive_name);
649 g_free (q);
650 shell_execute (cmd, 0);
651 g_free (cmd);
654 static void *
655 extfs_open (struct vfs_class *me, const char *file, int flags, int mode)
657 struct pseudofile *extfs_info;
658 struct archive *archive;
659 char *q;
660 struct entry *entry;
661 int local_handle;
662 int created = 0;
664 if ((q = extfs_get_path (file, &archive, 0, 0)) == NULL)
665 return NULL;
666 entry = extfs_find_entry (archive->root_entry, q, 0, 0);
667 if (entry == NULL && (flags & O_CREAT)) {
668 /* Create new entry */
669 entry = extfs_find_entry (archive->root_entry, q, 0, 1);
670 created = (entry != NULL);
673 g_free (q);
674 if (entry == NULL)
675 return NULL;
676 if ((entry = extfs_resolve_symlinks (entry)) == NULL)
677 return NULL;
679 if (S_ISDIR (entry->inode->mode))
680 ERRNOR (EISDIR, NULL);
682 if (entry->inode->local_filename == NULL) {
683 char *local_filename;
685 local_handle = vfs_mkstemps (&local_filename, "extfs", entry->name);
687 if (local_handle == -1)
688 return NULL;
689 close (local_handle);
691 if (!created && !(flags & O_TRUNC)
692 && extfs_cmd (" copyout ", archive, entry, local_filename)) {
693 unlink (local_filename);
694 free (local_filename);
695 my_errno = EIO;
696 return NULL;
698 entry->inode->local_filename = local_filename;
701 local_handle =
702 open (entry->inode->local_filename, NO_LINEAR (flags), mode);
703 if (local_handle == -1)
704 ERRNOR (EIO, NULL);
706 extfs_info = g_new (struct pseudofile, 1);
707 extfs_info->archive = archive;
708 extfs_info->entry = entry;
709 extfs_info->has_changed = created;
710 extfs_info->local_handle = local_handle;
712 /* i.e. we had no open files and now we have one */
713 vfs_rmstamp (&vfs_extfs_ops, (vfsid) archive);
714 archive->fd_usage++;
715 return extfs_info;
718 static int extfs_read (void *data, char *buffer, int count)
720 struct pseudofile *file = (struct pseudofile *)data;
722 return read (file->local_handle, buffer, count);
725 static int
726 extfs_close (void *data)
728 struct pseudofile *file;
729 int errno_code = 0;
730 file = (struct pseudofile *) data;
732 close (file->local_handle);
734 /* Commit the file if it has changed */
735 if (file->has_changed) {
736 if (extfs_cmd
737 (" copyin ", file->archive, file->entry,
738 file->entry->inode->local_filename))
739 errno_code = EIO;
741 struct stat file_status;
742 if (stat (file->entry->inode->local_filename, &file_status) !=
744 errno_code = EIO;
745 else
746 file->entry->inode->size = file_status.st_size;
749 file->entry->inode->mtime = time (NULL);
752 file->archive->fd_usage--;
753 if (!file->archive->fd_usage)
754 vfs_stamp_create (&vfs_extfs_ops, file->archive);
756 g_free (data);
757 if (errno_code)
758 ERRNOR (EIO, -1);
759 return 0;
762 #define RECORDSIZE 512
764 static struct entry*
765 extfs_find_entry_int (struct entry *dir, char *name,
766 struct loop_protect *list, int make_dirs, int make_file)
768 struct entry *pent, *pdir;
769 char *p, *q, *name_end;
770 char c;
772 if (*name == '/') { /* Handle absolute paths */
773 name++;
774 dir = dir->inode->archive->root_entry;
777 pent = dir;
778 p = name;
779 name_end = name + strlen (name);
780 q = strchr (p, '/');
781 c = '/';
782 if (!q)
783 q = strchr (p, 0);
785 for (; pent != NULL && c && *p; ){
786 c = *q;
787 *q = 0;
789 if (strcmp (p, ".")){
790 if (!strcmp (p, ".."))
791 pent = pent->dir;
792 else {
793 if ((pent = extfs_resolve_symlinks_int (pent, list))==NULL){
794 *q = c;
795 return NULL;
797 if (!S_ISDIR (pent->inode->mode)){
798 *q = c;
799 notadir = 1;
800 return NULL;
802 pdir = pent;
803 for (pent = pent->inode->first_in_subdir; pent; pent = pent->next_in_dir)
804 /* Hack: I keep the original semanthic unless
805 q+1 would break in the strchr */
806 if (!strcmp (pent->name, p)){
807 if (q + 1 > name_end){
808 *q = c;
809 notadir = !S_ISDIR (pent->inode->mode);
810 return pent;
812 break;
815 /* When we load archive, we create automagically
816 * non-existant directories
818 if (pent == NULL && make_dirs) {
819 pent = extfs_generate_entry (dir->inode->archive, p, pdir, S_IFDIR | 0777);
821 if (pent == NULL && make_file) {
822 pent = extfs_generate_entry (dir->inode->archive, p, pdir, S_IFREG | 0666);
826 /* Next iteration */
827 *q = c;
828 p = q + 1;
829 q = strchr (p, '/');
830 if (!q)
831 q = strchr (p, 0);
833 if (pent == NULL)
834 my_errno = ENOENT;
835 return pent;
838 static struct entry *extfs_find_entry (struct entry *dir, char *name, int make_dirs, int make_file)
840 struct entry *res;
842 errloop = 0;
843 notadir = 0;
844 res = extfs_find_entry_int (dir, name, NULL, make_dirs, make_file);
845 if (res == NULL) {
846 if (errloop)
847 my_errno = ELOOP;
848 else if (notadir)
849 my_errno = ENOTDIR;
851 return res;
855 static int extfs_errno (struct vfs_class *me)
857 return my_errno;
860 static void * extfs_opendir (struct vfs_class *me, const char *dirname)
862 struct archive *archive;
863 char *q;
864 struct entry *entry;
865 struct entry **info;
867 if ((q = extfs_get_path (dirname, &archive, 1, 0)) == NULL)
868 return NULL;
869 entry = extfs_find_entry (archive->root_entry, q, 0, 0);
870 g_free (q);
871 if (entry == NULL)
872 return NULL;
873 if ((entry = extfs_resolve_symlinks (entry)) == NULL)
874 return NULL;
875 if (!S_ISDIR (entry->inode->mode)) ERRNOR (ENOTDIR, NULL);
877 info = g_new (struct entry *, 2);
878 info[0] = entry->inode->first_in_subdir;
879 info[1] = entry->inode->first_in_subdir;
881 return info;
884 static void * extfs_readdir(void *data)
886 static union vfs_dirent dir;
887 struct entry **info = (struct entry **) data;
889 if (!*info)
890 return NULL;
892 strncpy(dir.dent.d_name, (*info)->name, MC_MAXPATHLEN);
893 dir.dent.d_name[MC_MAXPATHLEN] = 0;
895 compute_namelen(&dir.dent);
896 *info = (*info)->next_in_dir;
898 return (void *) &dir;
901 static int extfs_closedir (void *data)
903 g_free (data);
904 return 0;
907 static void extfs_stat_move( struct stat *buf, struct inode *inode )
909 buf->st_dev = inode->dev;
910 buf->st_ino = inode->inode;
911 buf->st_mode = inode->mode;
912 buf->st_nlink = inode->nlink;
913 buf->st_uid = inode->uid;
914 buf->st_gid = inode->gid;
915 #ifdef HAVE_STRUCT_STAT_ST_RDEV
916 buf->st_rdev = inode->rdev;
917 #endif
918 buf->st_size = inode->size;
919 #ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
920 buf->st_blksize = RECORDSIZE;
921 #endif
922 #ifdef HAVE_STRUCT_STAT_ST_BLOCKS
923 buf->st_blocks = (inode->size + RECORDSIZE - 1) / RECORDSIZE;
924 #endif
925 buf->st_atime = inode->atime;
926 buf->st_mtime = inode->mtime;
927 buf->st_ctime = inode->ctime;
930 static int extfs_internal_stat (char *path, struct stat *buf, int resolve)
932 struct archive *archive;
933 char *q;
934 struct entry *entry;
935 struct inode *inode;
937 if ((q = extfs_get_path_mangle (path, &archive, 0, 0)) == NULL)
938 return -1;
939 entry = extfs_find_entry (archive->root_entry, q, 0, 0);
940 if (entry == NULL)
941 return -1;
942 if (resolve && (entry = extfs_resolve_symlinks (entry)) == NULL)
943 return -1;
944 inode = entry->inode;
945 extfs_stat_move( buf, inode );
946 return 0;
949 static int extfs_stat (struct vfs_class *me, char *path, struct stat *buf)
951 return extfs_internal_stat (path, buf, 1);
954 static int extfs_lstat (struct vfs_class *me, char *path, struct stat *buf)
956 return extfs_internal_stat (path, buf, 0);
959 static int extfs_fstat (void *data, struct stat *buf)
961 struct pseudofile *file = (struct pseudofile *)data;
962 struct inode *inode;
964 inode = file->entry->inode;
965 extfs_stat_move( buf, inode );
966 return 0;
969 static int
970 extfs_readlink (struct vfs_class *me, char *path, char *buf, int size)
972 struct archive *archive;
973 char *q;
974 int i;
975 struct entry *entry;
977 if ((q = extfs_get_path_mangle (path, &archive, 0, 0)) == NULL)
978 return -1;
979 entry = extfs_find_entry (archive->root_entry, q, 0, 0);
980 if (entry == NULL)
981 return -1;
982 if (!S_ISLNK (entry->inode->mode))
983 ERRNOR (EINVAL, -1);
984 if (size < (i = strlen (entry->inode->linkname))) {
985 i = size;
987 strncpy (buf, entry->inode->linkname, i);
988 return i;
991 static int extfs_chmod (struct vfs_class *me, char *path, int mode)
993 return 0;
996 static int extfs_write (void *data, char *buf, int nbyte)
998 struct pseudofile *file = (struct pseudofile *)data;
1000 file->has_changed = 1;
1001 return write (file->local_handle, buf, nbyte);
1004 static int extfs_unlink (struct vfs_class *me, char *file)
1006 struct archive *archive;
1007 char *q;
1008 struct entry *entry;
1010 if ((q = extfs_get_path_mangle (file, &archive, 0, 0)) == NULL)
1011 return -1;
1012 entry = extfs_find_entry (archive->root_entry, q, 0, 0);
1013 if (entry == NULL)
1014 return -1;
1015 if ((entry = extfs_resolve_symlinks (entry)) == NULL)
1016 return -1;
1017 if (S_ISDIR (entry->inode->mode)) ERRNOR (EISDIR, -1);
1019 if (extfs_cmd (" rm ", archive, entry, "")){
1020 my_errno = EIO;
1021 return -1;
1023 extfs_remove_entry (entry);
1025 return 0;
1028 static int extfs_mkdir (struct vfs_class *me, char *path, mode_t mode)
1030 struct archive *archive;
1031 char *q;
1032 struct entry *entry;
1034 if ((q = extfs_get_path_mangle (path, &archive, 0, 0)) == NULL)
1035 return -1;
1036 entry = extfs_find_entry (archive->root_entry, q, 0, 0);
1037 if (entry != NULL) ERRNOR (EEXIST, -1);
1038 entry = extfs_find_entry (archive->root_entry, q, 1, 0);
1039 if (entry == NULL)
1040 return -1;
1041 if ((entry = extfs_resolve_symlinks (entry)) == NULL)
1042 return -1;
1043 if (!S_ISDIR (entry->inode->mode)) ERRNOR (ENOTDIR, -1);
1045 if (extfs_cmd (" mkdir ", archive, entry, "")){
1046 my_errno = EIO;
1047 extfs_remove_entry (entry);
1048 return -1;
1051 return 0;
1054 static int extfs_rmdir (struct vfs_class *me, char *path)
1056 struct archive *archive;
1057 char *q;
1058 struct entry *entry;
1060 if ((q = extfs_get_path_mangle (path, &archive, 0, 0)) == NULL)
1061 return -1;
1062 entry = extfs_find_entry (archive->root_entry, q, 0, 0);
1063 if (entry == NULL)
1064 return -1;
1065 if ((entry = extfs_resolve_symlinks (entry)) == NULL)
1066 return -1;
1067 if (!S_ISDIR (entry->inode->mode)) ERRNOR (ENOTDIR, -1);
1069 if (extfs_cmd (" rmdir ", archive, entry, "")){
1070 my_errno = EIO;
1071 return -1;
1073 extfs_remove_entry (entry);
1075 return 0;
1078 static int
1079 extfs_chdir (struct vfs_class *me, const char *path)
1081 struct archive *archive;
1082 char *q;
1083 struct entry *entry;
1085 my_errno = ENOTDIR;
1086 if ((q = extfs_get_path (path, &archive, 1, 0)) == NULL)
1087 return -1;
1088 entry = extfs_find_entry (archive->root_entry, q, 0, 0);
1089 g_free (q);
1090 if (!entry)
1091 return -1;
1092 entry = extfs_resolve_symlinks (entry);
1093 if ((!entry) || (!S_ISDIR (entry->inode->mode)))
1094 return -1;
1095 my_errno = 0;
1096 return 0;
1099 static int extfs_lseek (void *data, off_t offset, int whence)
1101 struct pseudofile *file = (struct pseudofile *) data;
1103 return lseek (file->local_handle, offset, whence);
1106 static vfsid
1107 extfs_getid (struct vfs_class *me, const char *path)
1109 struct archive *archive;
1110 char *p;
1112 if (!(p = extfs_get_path (path, &archive, 1, 1)))
1113 return NULL;
1114 g_free (p);
1115 return (vfsid) archive;
1118 static int extfs_nothingisopen (vfsid id)
1120 if (((struct archive *)id)->fd_usage <= 0)
1121 return 1;
1122 return 0;
1125 static void extfs_remove_entry (struct entry *e)
1127 int i = --(e->inode->nlink);
1128 struct entry *pe, *ent, *prev;
1130 if (S_ISDIR (e->inode->mode) && e->inode->first_in_subdir != NULL) {
1131 struct entry *f = e->inode->first_in_subdir;
1132 e->inode->first_in_subdir = NULL;
1133 extfs_remove_entry (f);
1135 pe = e->dir;
1136 if (e == pe->inode->first_in_subdir)
1137 pe->inode->first_in_subdir = e->next_in_dir;
1139 prev = NULL;
1140 for (ent = pe->inode->first_in_subdir; ent && ent->next_in_dir;
1141 ent = ent->next_in_dir)
1142 if (e == ent->next_in_dir) {
1143 prev = ent;
1144 break;
1146 if (prev)
1147 prev->next_in_dir = e->next_in_dir;
1148 if (e == pe->inode->last_in_subdir)
1149 pe->inode->last_in_subdir = prev;
1151 if (i <= 0) {
1152 if (e->inode->local_filename != NULL) {
1153 unlink (e->inode->local_filename);
1154 free (e->inode->local_filename);
1156 if (e->inode->linkname != NULL)
1157 g_free (e->inode->linkname);
1158 g_free (e->inode);
1161 g_free (e->name);
1162 g_free (e);
1165 static void extfs_free_entry (struct entry *e)
1167 int i = --(e->inode->nlink);
1168 if (S_ISDIR (e->inode->mode) && e->inode->first_in_subdir != NULL) {
1169 struct entry *f = e->inode->first_in_subdir;
1171 e->inode->first_in_subdir = NULL;
1172 extfs_free_entry (f);
1174 if (i <= 0) {
1175 if (e->inode->local_filename != NULL) {
1176 unlink (e->inode->local_filename);
1177 free (e->inode->local_filename);
1179 if (e->inode->linkname != NULL)
1180 g_free (e->inode->linkname);
1181 g_free (e->inode);
1183 if (e->next_in_dir != NULL)
1184 extfs_free_entry (e->next_in_dir);
1185 g_free (e->name);
1186 g_free (e);
1189 static void extfs_free (vfsid id)
1191 struct archive *parc;
1192 struct archive *archive = (struct archive *)id;
1194 extfs_free_entry (archive->root_entry);
1195 if (archive == first_archive) {
1196 first_archive = archive->next;
1197 } else {
1198 for (parc = first_archive; parc != NULL; parc = parc->next)
1199 if (parc->next == archive)
1200 break;
1201 if (parc != NULL)
1202 parc->next = archive->next;
1204 extfs_free_archive (archive);
1207 static char *
1208 extfs_getlocalcopy (struct vfs_class *me, const char *path)
1210 struct pseudofile *fp =
1211 (struct pseudofile *) extfs_open (me, path, O_RDONLY, 0);
1212 char *p;
1214 if (fp == NULL)
1215 return NULL;
1216 if (fp->entry->inode->local_filename == NULL) {
1217 extfs_close ((void *) fp);
1218 return NULL;
1220 p = g_strdup (fp->entry->inode->local_filename);
1221 fp->archive->fd_usage++;
1222 extfs_close ((void *) fp);
1223 return p;
1226 static int
1227 extfs_ungetlocalcopy (struct vfs_class *me, const char *path,
1228 const char *local, int has_changed)
1230 struct pseudofile *fp =
1231 (struct pseudofile *) extfs_open (me, path, O_RDONLY, 0);
1233 if (fp == NULL)
1234 return 0;
1235 if (!strcmp (fp->entry->inode->local_filename, local)) {
1236 fp->archive->fd_usage--;
1237 fp->has_changed |= has_changed;
1238 extfs_close ((void *) fp);
1239 return 0;
1240 } else {
1241 /* Should not happen */
1242 extfs_close ((void *) fp);
1243 return 0;
1248 static int extfs_init (struct vfs_class *me)
1250 FILE *cfg;
1251 char *mc_extfsini;
1252 char key[256];
1254 mc_extfsini = concat_dir_and_file (mc_home, "extfs" PATH_SEP_STR "extfs.ini");
1255 cfg = fopen (mc_extfsini, "r");
1257 /* We may not use vfs_die() message or message or similar,
1258 * UI is not initialized at this time and message would not
1259 * appear on screen. */
1260 if (!cfg) {
1261 fprintf (stderr, _("Warning: file %s not found\n"), mc_extfsini);
1262 g_free (mc_extfsini);
1263 return 0;
1266 extfs_no = 0;
1267 while (extfs_no < MAXEXTFS && fgets (key, sizeof (key), cfg)) {
1268 char *c;
1270 /* Handle those with a trailing ':', those flag that the
1271 * file system does not require an archive to work
1274 if (*key == '[') {
1275 fprintf(stderr, "Warning: You need to update your %s file.\n",
1276 mc_extfsini);
1277 fclose(cfg);
1278 g_free (mc_extfsini);
1279 return 0;
1281 if (*key == '#' || *key == '\n')
1282 continue;
1284 if ((c = strchr (key, '\n'))){
1285 *c-- = 0;
1286 } else { /* Last line without newline or strlen (key) > 255 */
1287 c = &key [strlen (key) - 1];
1289 extfs_need_archive [extfs_no] = !(*c == ':');
1290 if (*c == ':')
1291 *c = 0;
1292 if (!(*key))
1293 continue;
1295 extfs_prefixes [extfs_no++] = g_strdup (key);
1297 fclose(cfg);
1298 g_free (mc_extfsini);
1299 return 1;
1302 /* Do NOT use me argument in this function */
1303 static int extfs_which (struct vfs_class *me, char *path)
1305 int i;
1307 for (i = 0; i < extfs_no; i++)
1308 if (!strcmp (path, extfs_prefixes [i]))
1309 return i;
1310 return -1;
1313 static void extfs_done (struct vfs_class *me)
1315 int i;
1317 for (i = 0; i < extfs_no; i++ )
1318 g_free (extfs_prefixes [i]);
1319 extfs_no = 0;
1322 static int
1323 extfs_setctl (struct vfs_class *me, char *path, int ctlop, void *arg)
1325 if (ctlop == VFS_SETCTL_RUN) {
1326 extfs_run (path);
1327 return 1;
1329 return 0;
1332 void
1333 init_extfs (void)
1335 vfs_extfs_ops.name = "extfs";
1336 vfs_extfs_ops.init = extfs_init;
1337 vfs_extfs_ops.done = extfs_done;
1338 vfs_extfs_ops.fill_names = extfs_fill_names;
1339 vfs_extfs_ops.which = extfs_which;
1340 vfs_extfs_ops.open = extfs_open;
1341 vfs_extfs_ops.close = extfs_close;
1342 vfs_extfs_ops.read = extfs_read;
1343 vfs_extfs_ops.write = extfs_write;
1344 vfs_extfs_ops.opendir = extfs_opendir;
1345 vfs_extfs_ops.readdir = extfs_readdir;
1346 vfs_extfs_ops.closedir = extfs_closedir;
1347 vfs_extfs_ops.stat = extfs_stat;
1348 vfs_extfs_ops.lstat = extfs_lstat;
1349 vfs_extfs_ops.fstat = extfs_fstat;
1350 vfs_extfs_ops.chmod = extfs_chmod;
1351 vfs_extfs_ops.readlink = extfs_readlink;
1352 vfs_extfs_ops.unlink = extfs_unlink;
1353 vfs_extfs_ops.chdir = extfs_chdir;
1354 vfs_extfs_ops.ferrno = extfs_errno;
1355 vfs_extfs_ops.lseek = extfs_lseek;
1356 vfs_extfs_ops.getid = extfs_getid;
1357 vfs_extfs_ops.nothingisopen = extfs_nothingisopen;
1358 vfs_extfs_ops.free = extfs_free;
1359 vfs_extfs_ops.getlocalcopy = extfs_getlocalcopy;
1360 vfs_extfs_ops.ungetlocalcopy = extfs_ungetlocalcopy;
1361 vfs_extfs_ops.mkdir = extfs_mkdir;
1362 vfs_extfs_ops.rmdir = extfs_rmdir;
1363 vfs_extfs_ops.setctl = extfs_setctl;
1364 vfs_register_class (&vfs_extfs_ops);