Completely removed MHL stuff
[midnight-commander.git] / vfs / extfs.c
blobe4a36d568cabf6f582fc54cb40ff13270bc875ce
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 "../src/global.h"
40 #include "../src/tty.h" /* enable/disable interrupt key */
41 #include "../src/wtools.h" /* message() */
42 #include "../src/main.h" /* print_vfs_message */
43 #include "utilvfs.h"
44 #include "../src/execute.h" /* For shell_execute */
45 #include "vfs.h"
46 #include "vfs-impl.h"
47 #include "gc.h" /* vfs_rmstamp */
49 #undef ERRNOR
50 #define ERRNOR(x,y) do { my_errno = x; return y; } while(0)
52 struct inode {
53 nlink_t nlink;
54 struct entry *first_in_subdir; /* only used if this is a directory */
55 struct entry *last_in_subdir;
56 ino_t inode; /* This is inode # */
57 dev_t dev; /* This is an internal identification of the extfs archive */
58 struct archive *archive; /* And this is an archive structure */
59 dev_t rdev;
60 mode_t mode;
61 uid_t uid;
62 gid_t gid;
63 off_t size;
64 time_t mtime;
65 char *linkname;
66 time_t atime;
67 time_t ctime;
68 char *local_filename;
71 struct entry {
72 struct entry *next_in_dir;
73 struct entry *dir;
74 char *name;
75 struct inode *inode;
78 struct pseudofile {
79 struct archive *archive;
80 unsigned int has_changed:1;
81 int local_handle;
82 struct entry *entry;
85 struct archive {
86 int fstype;
87 char *name;
88 char *local_name;
89 struct stat local_stat;
90 dev_t rdev;
91 int fd_usage;
92 ino_t inode_counter;
93 struct entry *root_entry;
94 struct archive *next;
97 static struct entry *extfs_find_entry (struct entry *dir, char *name,
98 int make_dirs, int make_file);
99 static int extfs_which (struct vfs_class *me, const char *path);
100 static void extfs_remove_entry (struct entry *e);
101 static void extfs_free (vfsid id);
102 static void extfs_free_entry (struct entry *e);
104 static struct vfs_class vfs_extfs_ops;
105 static struct archive *first_archive = NULL;
106 static int my_errno = 0;
108 #define MAXEXTFS 32
109 static char *extfs_prefixes [MAXEXTFS];
110 static char extfs_need_archive [MAXEXTFS];
111 static int extfs_no = 0;
113 static void
114 extfs_fill_names (struct vfs_class *me, fill_names_f func)
116 struct archive *a = first_archive;
117 char *name;
119 (void) me;
121 while (a) {
122 name =
123 g_strconcat (a->name ? a->name : "", "#",
124 extfs_prefixes[a->fstype], (char *) NULL);
125 (*func) (name);
126 g_free (name);
127 a = a->next;
131 static void extfs_make_dots (struct entry *ent)
133 struct entry *entry = g_new (struct entry, 1);
134 struct entry *parentry = ent->dir;
135 struct inode *inode = ent->inode, *parent;
137 parent = (parentry != NULL) ? parentry->inode : NULL;
138 entry->name = g_strdup (".");
139 entry->inode = inode;
140 entry->dir = ent;
141 inode->local_filename = NULL;
142 inode->first_in_subdir = entry;
143 inode->nlink++;
144 entry->next_in_dir = g_new (struct entry, 1);
145 entry = entry->next_in_dir;
146 entry->name = g_strdup ("..");
147 inode->last_in_subdir = entry;
148 entry->next_in_dir = NULL;
149 if (parent != NULL) {
150 entry->inode = parent;
151 entry->dir = parentry;
152 parent->nlink++;
153 } else {
154 entry->inode = inode;
155 entry->dir = ent;
156 inode->nlink++;
160 static struct entry *extfs_generate_entry (struct archive *archive,
161 const char *name, struct entry *parentry, mode_t mode)
163 mode_t myumask;
164 struct inode *inode, *parent;
165 struct entry *entry;
167 parent = (parentry != NULL) ? parentry->inode : NULL;
168 entry = g_new (struct entry, 1);
170 entry->name = g_strdup (name);
171 entry->next_in_dir = NULL;
172 entry->dir = parentry;
173 if (parent != NULL) {
174 parent->last_in_subdir->next_in_dir = entry;
175 parent->last_in_subdir = entry;
177 inode = g_new (struct inode, 1);
178 entry->inode = inode;
179 inode->local_filename = NULL;
180 inode->linkname = NULL;
181 inode->last_in_subdir = NULL;
182 inode->inode = (archive->inode_counter)++;
183 inode->dev = archive->rdev;
184 inode->archive = archive;
185 myumask = umask (022);
186 umask (myumask);
187 inode->mode = mode & ~myumask;
188 mode = inode->mode;
189 inode->rdev = 0;
190 inode->uid = getuid ();
191 inode->gid = getgid ();
192 inode->size = 0;
193 inode->mtime = time (NULL);
194 inode->atime = inode->mtime;
195 inode->ctime = inode->mtime;
196 inode->nlink = 1;
197 if (S_ISDIR (mode))
198 extfs_make_dots (entry);
199 return entry;
202 #if 0
203 static void extfs_free_entries (struct entry *entry)
205 (void) entry;
206 return;
208 #endif
210 static void extfs_free_archive (struct archive *archive)
212 extfs_free_entry (archive->root_entry);
213 if (archive->local_name != NULL) {
214 struct stat my;
216 mc_stat (archive->local_name, &my);
217 mc_ungetlocalcopy (archive->name, archive->local_name,
218 archive->local_stat.st_mtime != my.st_mtime);
219 g_free(archive->local_name);
221 g_free (archive->name);
222 g_free (archive);
225 static FILE *
226 extfs_open_archive (int fstype, const char *name, struct archive **pparc)
228 static dev_t archive_counter = 0;
229 FILE *result;
230 mode_t mode;
231 char *cmd;
232 char *mc_extfsdir;
233 struct stat mystat;
234 struct archive *current_archive;
235 struct entry *root_entry;
236 char *local_name = NULL, *tmp = 0;
237 int uses_archive = extfs_need_archive[fstype];
239 if (uses_archive) {
240 if (mc_stat (name, &mystat) == -1)
241 return NULL;
242 if (!vfs_file_is_local (name)) {
243 local_name = mc_getlocalcopy (name);
244 if (local_name == NULL)
245 return NULL;
247 tmp = name_quote (name, 0);
250 mc_extfsdir = concat_dir_and_file (mc_home, "extfs" PATH_SEP_STR);
251 cmd =
252 g_strconcat (mc_extfsdir, extfs_prefixes[fstype], " list ",
253 local_name ? local_name : tmp, (char *) NULL);
254 g_free (tmp);
255 g_free (mc_extfsdir);
256 open_error_pipe ();
257 result = popen (cmd, "r");
258 g_free (cmd);
259 if (result == NULL) {
260 close_error_pipe (D_ERROR, NULL);
261 if (local_name) {
262 mc_ungetlocalcopy (name, local_name, 0);
263 g_free(local_name);
265 return NULL;
267 #ifdef ___QNXNTO__
268 setvbuf (result, NULL, _IONBF, 0);
269 #endif
271 current_archive = g_new (struct archive, 1);
272 current_archive->fstype = fstype;
273 current_archive->name = name ? g_strdup (name) : NULL;
274 current_archive->local_name = local_name;
276 if (local_name != NULL)
277 mc_stat (local_name, &current_archive->local_stat);
278 current_archive->inode_counter = 0;
279 current_archive->fd_usage = 0;
280 current_archive->rdev = archive_counter++;
281 current_archive->next = first_archive;
282 first_archive = current_archive;
283 mode = mystat.st_mode & 07777;
284 if (mode & 0400)
285 mode |= 0100;
286 if (mode & 0040)
287 mode |= 0010;
288 if (mode & 0004)
289 mode |= 0001;
290 mode |= S_IFDIR;
291 root_entry = extfs_generate_entry (current_archive, "/", NULL, mode);
292 root_entry->inode->uid = mystat.st_uid;
293 root_entry->inode->gid = mystat.st_gid;
294 root_entry->inode->atime = mystat.st_atime;
295 root_entry->inode->ctime = mystat.st_ctime;
296 root_entry->inode->mtime = mystat.st_mtime;
297 current_archive->root_entry = root_entry;
299 *pparc = current_archive;
301 return result;
305 * Main loop for reading an archive.
306 * Return 0 on success, -1 on error.
308 static int
309 extfs_read_archive (int fstype, const char *name, struct archive **pparc)
311 FILE *extfsd;
312 char *buffer;
313 struct archive *current_archive;
314 char *current_file_name, *current_link_name;
316 if ((extfsd =
317 extfs_open_archive (fstype, name, &current_archive)) == NULL) {
318 message (D_ERROR, MSG_ERROR, _("Cannot open %s archive\n%s"),
319 extfs_prefixes[fstype], name);
320 return -1;
323 buffer = g_malloc (4096);
324 while (fgets (buffer, 4096, extfsd) != NULL) {
325 struct stat hstat;
327 current_link_name = NULL;
328 if (vfs_parse_ls_lga
329 (buffer, &hstat, &current_file_name, &current_link_name)) {
330 struct entry *entry, *pent;
331 struct inode *inode;
332 char *p, *q, *cfn = current_file_name;
334 if (*cfn) {
335 if (*cfn == '/')
336 cfn++;
337 p = strchr (cfn, 0);
338 if (p != cfn && *(p - 1) == '/')
339 *(p - 1) = 0;
340 p = strrchr (cfn, '/');
341 if (p == NULL) {
342 p = cfn;
343 q = strchr (cfn, 0);
344 } else {
345 *(p++) = 0;
346 q = cfn;
348 if (S_ISDIR (hstat.st_mode)
349 && (!strcmp (p, ".") || !strcmp (p, "..")))
350 goto read_extfs_continue;
351 pent =
352 extfs_find_entry (current_archive->root_entry, q, 1,
354 if (pent == NULL) {
355 /* FIXME: Should clean everything one day */
356 g_free (buffer);
357 pclose (extfsd);
358 close_error_pipe (D_ERROR, _("Inconsistent extfs archive"));
359 return -1;
361 entry = g_new (struct entry, 1);
362 entry->name = g_strdup (p);
363 entry->next_in_dir = NULL;
364 entry->dir = pent;
365 if (pent->inode->last_in_subdir) {
366 pent->inode->last_in_subdir->next_in_dir = entry;
367 pent->inode->last_in_subdir = entry;
369 if (!S_ISLNK (hstat.st_mode) && current_link_name != NULL) {
370 pent =
371 extfs_find_entry (current_archive->root_entry,
372 current_link_name, 0, 0);
373 if (pent == NULL) {
374 /* FIXME: Should clean everything one day */
375 g_free (buffer);
376 pclose (extfsd);
377 close_error_pipe (D_ERROR,
378 _("Inconsistent extfs archive"));
379 return -1;
380 } else {
381 entry->inode = pent->inode;
382 pent->inode->nlink++;
384 } else {
385 inode = g_new (struct inode, 1);
386 entry->inode = inode;
387 inode->local_filename = NULL;
388 inode->inode = (current_archive->inode_counter)++;
389 inode->nlink = 1;
390 inode->dev = current_archive->rdev;
391 inode->archive = current_archive;
392 inode->mode = hstat.st_mode;
393 #ifdef HAVE_STRUCT_STAT_ST_RDEV
394 inode->rdev = hstat.st_rdev;
395 #else
396 inode->rdev = 0;
397 #endif
398 inode->uid = hstat.st_uid;
399 inode->gid = hstat.st_gid;
400 inode->size = hstat.st_size;
401 inode->mtime = hstat.st_mtime;
402 inode->atime = hstat.st_atime;
403 inode->ctime = hstat.st_ctime;
404 inode->first_in_subdir = NULL;
405 inode->last_in_subdir = NULL;
406 if (current_link_name != NULL
407 && S_ISLNK (hstat.st_mode)) {
408 inode->linkname = current_link_name;
409 current_link_name = NULL;
410 } else {
411 if (S_ISLNK (hstat.st_mode))
412 inode->mode &= ~S_IFLNK; /* You *DON'T* want to do this always */
413 inode->linkname = NULL;
415 if (S_ISDIR (hstat.st_mode))
416 extfs_make_dots (entry);
419 read_extfs_continue:
420 g_free (current_file_name);
421 g_free (current_link_name);
424 g_free (buffer);
426 /* Check if extfs 'list' returned 0 */
427 if (pclose (extfsd) != 0) {
428 extfs_free (current_archive);
429 close_error_pipe (D_ERROR, _("Inconsistent extfs archive"));
430 return -1;
433 close_error_pipe (D_ERROR, NULL);
434 *pparc = current_archive;
435 return 0;
439 * Dissect the path and create corresponding superblock. Note that inname
440 * can be changed and the result may point inside the original string.
442 static char *
443 extfs_get_path_mangle (struct vfs_class *me, char *inname, struct archive **archive,
444 int do_not_open)
446 char *local, *op;
447 const char *archive_name;
448 int result = -1;
449 struct archive *parc;
450 int fstype;
452 archive_name = inname;
453 vfs_split (inname, &local, &op);
455 fstype = extfs_which (me, op);
457 if (fstype == -1)
458 return NULL;
459 if (!local)
460 local = inname + strlen (inname);
463 * All filesystems should have some local archive, at least
464 * it can be '/'.
466 for (parc = first_archive; parc != NULL; parc = parc->next)
467 if (parc->name) {
468 if (!strcmp (parc->name, archive_name)) {
469 vfs_stamp (&vfs_extfs_ops, (vfsid) parc);
470 goto return_success;
474 result =
475 do_not_open ? -1 : extfs_read_archive (fstype, archive_name,
476 &parc);
477 if (result == -1)
478 ERRNOR (EIO, NULL);
480 return_success:
481 *archive = parc;
482 return local;
487 * Dissect the path and create corresponding superblock.
488 * The result should be freed.
490 static char *
491 extfs_get_path (struct vfs_class *me, const char *inname, struct archive **archive,
492 int do_not_open)
494 char *buf = g_strdup (inname);
495 char *res = extfs_get_path_mangle (me, buf, archive, do_not_open);
496 char *res2 = NULL;
497 if (res)
498 res2 = g_strdup (res);
499 g_free (buf);
500 return res2;
504 /* Return allocated path (without leading slash) inside the archive */
505 static char *extfs_get_path_from_entry (struct entry *entry)
507 struct list {
508 struct list *next;
509 char *name;
510 } *head, *p;
511 char *localpath;
512 size_t len;
514 for (len = 0, head = 0; entry->dir; entry = entry->dir) {
515 p = g_new (struct list, 1);
516 p->next = head;
517 p->name = entry->name;
518 head = p;
519 len += strlen (entry->name) + 1;
522 if (len == 0)
523 return g_strdup ("");
525 localpath = g_malloc (len);
526 *localpath = '\0';
527 while (head) {
528 strcat (localpath, head->name);
529 if (head->next)
530 strcat (localpath, "/");
531 p = head;
532 head = head->next;
533 g_free (p);
535 return (localpath);
539 struct loop_protect {
540 struct entry *entry;
541 struct loop_protect *next;
543 static int errloop;
544 static int notadir;
546 static struct entry *
547 extfs_find_entry_int (struct entry *dir, char *name,
548 struct loop_protect *list, int make_dirs, int make_file);
550 static struct entry *
551 extfs_resolve_symlinks_int (struct entry *entry,
552 struct loop_protect *list)
554 struct entry *pent;
555 struct loop_protect *looping;
557 if (!S_ISLNK (entry->inode->mode))
558 return entry;
559 for (looping = list; looping != NULL; looping = looping->next)
560 if (entry == looping->entry) { /* Here we protect us against symlink looping */
561 errloop = 1;
562 return NULL;
564 looping = g_new (struct loop_protect, 1);
565 looping->entry = entry;
566 looping->next = list;
567 pent = extfs_find_entry_int (entry->dir, entry->inode->linkname, looping, 0, 0);
568 g_free (looping);
569 if (pent == NULL)
570 my_errno = ENOENT;
571 return pent;
574 static struct entry *extfs_resolve_symlinks (struct entry *entry)
576 struct entry *res;
578 errloop = 0;
579 notadir = 0;
580 res = extfs_resolve_symlinks_int (entry, NULL);
581 if (res == NULL) {
582 if (errloop)
583 my_errno = ELOOP;
584 else if (notadir)
585 my_errno = ENOTDIR;
587 return res;
590 static const char *
591 extfs_get_archive_name (struct archive *archive)
593 const char *archive_name;
595 if (archive->local_name)
596 archive_name = archive->local_name;
597 else
598 archive_name = archive->name;
600 if (!archive_name || !*archive_name)
601 return "no_archive_name";
602 else
603 return archive_name;
606 /* Don't pass localname as NULL */
607 static int
608 extfs_cmd (const char *extfs_cmd, struct archive *archive,
609 struct entry *entry, const char *localname)
611 char *file;
612 char *quoted_file;
613 char *quoted_localname;
614 char *archive_name;
615 char *mc_extfsdir;
616 char *cmd;
617 int retval;
619 file = extfs_get_path_from_entry (entry);
620 quoted_file = name_quote (file, 0);
621 g_free (file);
622 archive_name = name_quote (extfs_get_archive_name (archive), 0);
623 quoted_localname = name_quote (localname, 0);
625 mc_extfsdir = concat_dir_and_file (mc_home, "extfs" PATH_SEP_STR);
626 cmd = g_strconcat (mc_extfsdir, extfs_prefixes[archive->fstype],
627 extfs_cmd, archive_name, " ", quoted_file, " ",
628 quoted_localname, (char *) NULL);
629 g_free (quoted_file);
630 g_free (quoted_localname);
631 g_free (mc_extfsdir);
632 g_free (archive_name);
634 open_error_pipe ();
635 retval = my_system (EXECUTE_AS_SHELL, shell, cmd);
636 g_free (cmd);
637 close_error_pipe (D_ERROR, NULL);
638 return retval;
641 static void
642 extfs_run (struct vfs_class *me, const char *file)
644 struct archive *archive = NULL;
645 char *p, *q, *archive_name, *mc_extfsdir;
646 char *cmd;
648 if ((p = extfs_get_path (me, file, &archive, 0)) == NULL)
649 return;
650 q = name_quote (p, 0);
651 g_free (p);
653 archive_name = name_quote (extfs_get_archive_name (archive), 0);
654 mc_extfsdir = concat_dir_and_file (mc_home, "extfs" PATH_SEP_STR);
655 cmd = g_strconcat (mc_extfsdir, extfs_prefixes[archive->fstype],
656 " run ", archive_name, " ", q, (char *) NULL);
657 g_free (mc_extfsdir);
658 g_free (archive_name);
659 g_free (q);
660 shell_execute (cmd, 0);
661 g_free (cmd);
664 static void *
665 extfs_open (struct vfs_class *me, const char *file, int flags, int mode)
667 struct pseudofile *extfs_info;
668 struct archive *archive = NULL;
669 char *q;
670 struct entry *entry;
671 int local_handle;
672 int created = 0;
674 if ((q = extfs_get_path (me, file, &archive, 0)) == NULL)
675 return NULL;
676 entry = extfs_find_entry (archive->root_entry, q, 0, 0);
677 if (entry == NULL && (flags & O_CREAT)) {
678 /* Create new entry */
679 entry = extfs_find_entry (archive->root_entry, q, 0, 1);
680 created = (entry != NULL);
683 g_free (q);
684 if (entry == NULL)
685 return NULL;
686 if ((entry = extfs_resolve_symlinks (entry)) == NULL)
687 return NULL;
689 if (S_ISDIR (entry->inode->mode))
690 ERRNOR (EISDIR, NULL);
692 if (entry->inode->local_filename == NULL) {
693 char *local_filename;
695 local_handle = vfs_mkstemps (&local_filename, "extfs", entry->name);
697 if (local_handle == -1)
698 return NULL;
699 close (local_handle);
701 if (!created && !(flags & O_TRUNC)
702 && extfs_cmd (" copyout ", archive, entry, local_filename)) {
703 unlink (local_filename);
704 free (local_filename);
705 my_errno = EIO;
706 return NULL;
708 entry->inode->local_filename = local_filename;
711 local_handle =
712 open (entry->inode->local_filename, NO_LINEAR (flags), mode);
713 if (local_handle == -1)
714 ERRNOR (EIO, NULL);
716 extfs_info = g_new (struct pseudofile, 1);
717 extfs_info->archive = archive;
718 extfs_info->entry = entry;
719 extfs_info->has_changed = created;
720 extfs_info->local_handle = local_handle;
722 /* i.e. we had no open files and now we have one */
723 vfs_rmstamp (&vfs_extfs_ops, (vfsid) archive);
724 archive->fd_usage++;
725 return extfs_info;
728 static ssize_t extfs_read (void *data, char *buffer, int count)
730 struct pseudofile *file = (struct pseudofile *)data;
732 return read (file->local_handle, buffer, count);
735 static int
736 extfs_close (void *data)
738 struct pseudofile *file;
739 int errno_code = 0;
740 file = (struct pseudofile *) data;
742 close (file->local_handle);
744 /* Commit the file if it has changed */
745 if (file->has_changed) {
746 if (extfs_cmd
747 (" copyin ", file->archive, file->entry,
748 file->entry->inode->local_filename))
749 errno_code = EIO;
751 struct stat file_status;
752 if (stat (file->entry->inode->local_filename, &file_status) !=
754 errno_code = EIO;
755 else
756 file->entry->inode->size = file_status.st_size;
759 file->entry->inode->mtime = time (NULL);
762 file->archive->fd_usage--;
763 if (!file->archive->fd_usage)
764 vfs_stamp_create (&vfs_extfs_ops, file->archive);
766 g_free (data);
767 if (errno_code)
768 ERRNOR (EIO, -1);
769 return 0;
772 #define RECORDSIZE 512
774 static struct entry*
775 extfs_find_entry_int (struct entry *dir, char *name,
776 struct loop_protect *list, int make_dirs, int make_file)
778 struct entry *pent, *pdir;
779 char *p, *q, *name_end;
780 char c;
782 if (*name == '/') { /* Handle absolute paths */
783 name++;
784 dir = dir->inode->archive->root_entry;
787 pent = dir;
788 p = name;
789 name_end = name + strlen (name);
790 q = strchr (p, '/');
791 c = '/';
792 if (!q)
793 q = strchr (p, 0);
795 for (; pent != NULL && c && *p; ){
796 c = *q;
797 *q = 0;
799 if (strcmp (p, ".")){
800 if (!strcmp (p, ".."))
801 pent = pent->dir;
802 else {
803 if ((pent = extfs_resolve_symlinks_int (pent, list))==NULL){
804 *q = c;
805 return NULL;
807 if (!S_ISDIR (pent->inode->mode)){
808 *q = c;
809 notadir = 1;
810 return NULL;
812 pdir = pent;
813 for (pent = pent->inode->first_in_subdir; pent; pent = pent->next_in_dir)
814 /* Hack: I keep the original semanthic unless
815 q+1 would break in the strchr */
816 if (!strcmp (pent->name, p)){
817 if (q + 1 > name_end){
818 *q = c;
819 notadir = !S_ISDIR (pent->inode->mode);
820 return pent;
822 break;
825 /* When we load archive, we create automagically
826 * non-existant directories
828 if (pent == NULL && make_dirs) {
829 pent = extfs_generate_entry (dir->inode->archive, p, pdir, S_IFDIR | 0777);
831 if (pent == NULL && make_file) {
832 pent = extfs_generate_entry (dir->inode->archive, p, pdir, S_IFREG | 0666);
836 /* Next iteration */
837 *q = c;
838 p = q + 1;
839 q = strchr (p, '/');
840 if (!q)
841 q = strchr (p, 0);
843 if (pent == NULL)
844 my_errno = ENOENT;
845 return pent;
848 static struct entry *extfs_find_entry (struct entry *dir, char *name, int make_dirs, int make_file)
850 struct entry *res;
852 errloop = 0;
853 notadir = 0;
854 res = extfs_find_entry_int (dir, name, NULL, make_dirs, make_file);
855 if (res == NULL) {
856 if (errloop)
857 my_errno = ELOOP;
858 else if (notadir)
859 my_errno = ENOTDIR;
861 return res;
865 static int extfs_errno (struct vfs_class *me)
867 (void) me;
869 return my_errno;
872 static void * extfs_opendir (struct vfs_class *me, const char *dirname)
874 struct archive *archive = NULL;
875 char *q;
876 struct entry *entry;
877 struct entry **info;
879 if ((q = extfs_get_path (me, dirname, &archive, 0)) == NULL)
880 return NULL;
881 entry = extfs_find_entry (archive->root_entry, q, 0, 0);
882 g_free (q);
883 if (entry == NULL)
884 return NULL;
885 if ((entry = extfs_resolve_symlinks (entry)) == NULL)
886 return NULL;
887 if (!S_ISDIR (entry->inode->mode)) ERRNOR (ENOTDIR, NULL);
889 info = g_new (struct entry *, 2);
890 info[0] = entry->inode->first_in_subdir;
891 info[1] = entry->inode->first_in_subdir;
893 return info;
896 static void * extfs_readdir(void *data)
898 static union vfs_dirent dir;
899 struct entry **info = (struct entry **) data;
901 if (!*info)
902 return NULL;
904 g_strlcpy(dir.dent.d_name, (*info)->name, MC_MAXPATHLEN);
906 compute_namelen(&dir.dent);
907 *info = (*info)->next_in_dir;
909 return (void *) &dir;
912 static int extfs_closedir (void *data)
914 g_free (data);
915 return 0;
918 static void extfs_stat_move (struct stat *buf, const struct inode *inode)
920 buf->st_dev = inode->dev;
921 buf->st_ino = inode->inode;
922 buf->st_mode = inode->mode;
923 buf->st_nlink = inode->nlink;
924 buf->st_uid = inode->uid;
925 buf->st_gid = inode->gid;
926 #ifdef HAVE_STRUCT_STAT_ST_RDEV
927 buf->st_rdev = inode->rdev;
928 #endif
929 buf->st_size = inode->size;
930 #ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
931 buf->st_blksize = RECORDSIZE;
932 #endif
933 #ifdef HAVE_STRUCT_STAT_ST_BLOCKS
934 buf->st_blocks = (inode->size + RECORDSIZE - 1) / RECORDSIZE;
935 #endif
936 buf->st_atime = inode->atime;
937 buf->st_mtime = inode->mtime;
938 buf->st_ctime = inode->ctime;
941 static int
942 extfs_internal_stat (struct vfs_class *me, const char *path, struct stat *buf,
943 int resolve)
945 struct archive *archive;
946 char *q;
947 struct entry *entry;
948 char *path2 = g_strdup (path);
949 int result = -1;
951 if ((q = extfs_get_path_mangle (me, path2, &archive, 0)) == NULL)
952 goto cleanup;
953 entry = extfs_find_entry (archive->root_entry, q, 0, 0);
954 if (entry == NULL)
955 goto cleanup;
956 if (resolve && (entry = extfs_resolve_symlinks (entry)) == NULL)
957 goto cleanup;
958 extfs_stat_move (buf, entry->inode);
959 result = 0;
960 cleanup:
961 g_free (path2);
962 return result;
965 static int extfs_stat (struct vfs_class *me, const char *path, struct stat *buf)
967 return extfs_internal_stat (me, path, buf, 1);
970 static int extfs_lstat (struct vfs_class *me, const char *path, struct stat *buf)
972 return extfs_internal_stat (me, path, buf, 0);
975 static int extfs_fstat (void *data, struct stat *buf)
977 struct pseudofile *file = (struct pseudofile *)data;
979 extfs_stat_move (buf, file->entry->inode);
980 return 0;
983 static int
984 extfs_readlink (struct vfs_class *me, const char *path, char *buf, size_t size)
986 struct archive *archive;
987 char *q;
988 size_t len;
989 struct entry *entry;
990 char *mpath = g_strdup (path);
991 int result = -1;
993 if ((q = extfs_get_path_mangle (me, mpath, &archive, 0)) == NULL)
994 goto cleanup;
995 entry = extfs_find_entry (archive->root_entry, q, 0, 0);
996 if (entry == NULL)
997 goto cleanup;
998 if (!S_ISLNK (entry->inode->mode)) {
999 me->verrno = EINVAL;
1000 goto cleanup;
1002 len = strlen (entry->inode->linkname);
1003 if (size < len)
1004 len = size;
1005 /* readlink() does not append a NUL character to buf */
1006 memcpy (buf, entry->inode->linkname, result = len);
1007 cleanup:
1008 g_free (mpath);
1009 return result;
1012 static int extfs_chmod (struct vfs_class *me, const char *path, int mode)
1014 (void) me;
1015 (void) path;
1016 (void) mode;
1017 return 0;
1020 static ssize_t extfs_write (void *data, const char *buf, int nbyte)
1022 struct pseudofile *file = (struct pseudofile *)data;
1024 file->has_changed = 1;
1025 return write (file->local_handle, buf, nbyte);
1028 static int extfs_unlink (struct vfs_class *me, const char *file)
1030 struct archive *archive;
1031 char *q, *mpath = g_strdup (file);
1032 struct entry *entry;
1033 int result = -1;
1035 if ((q = extfs_get_path_mangle (me, mpath, &archive, 0)) == NULL)
1036 goto cleanup;
1037 entry = extfs_find_entry (archive->root_entry, q, 0, 0);
1038 if (entry == NULL)
1039 goto cleanup;
1040 if ((entry = extfs_resolve_symlinks (entry)) == NULL)
1041 goto cleanup;
1042 if (S_ISDIR (entry->inode->mode)) {
1043 me->verrno = EISDIR;
1044 goto cleanup;
1046 if (extfs_cmd (" rm ", archive, entry, "")){
1047 my_errno = EIO;
1048 goto cleanup;
1050 extfs_remove_entry (entry);
1051 result = 0;
1052 cleanup:
1053 g_free (mpath);
1054 return result;
1057 static int extfs_mkdir (struct vfs_class *me, const char *path, mode_t mode)
1059 struct archive *archive;
1060 char *q, *mpath = g_strdup(path);
1061 struct entry *entry;
1062 int result = -1;
1064 (void) mode;
1066 if ((q = extfs_get_path_mangle (me, mpath, &archive, 0)) == NULL)
1067 goto cleanup;
1068 entry = extfs_find_entry (archive->root_entry, q, 0, 0);
1069 if (entry != NULL) {
1070 me->verrno = EEXIST;
1071 goto cleanup;
1073 entry = extfs_find_entry (archive->root_entry, q, 1, 0);
1074 if (entry == NULL)
1075 goto cleanup;
1076 if ((entry = extfs_resolve_symlinks (entry)) == NULL)
1077 goto cleanup;
1078 if (!S_ISDIR (entry->inode->mode)) {
1079 me->verrno = ENOTDIR;
1080 goto cleanup;
1083 if (extfs_cmd (" mkdir ", archive, entry, "")){
1084 my_errno = EIO;
1085 extfs_remove_entry (entry);
1086 goto cleanup;
1088 result = 0;
1089 cleanup:
1090 g_free (mpath);
1091 return result;
1094 static int extfs_rmdir (struct vfs_class *me, const char *path)
1096 struct archive *archive;
1097 char *q, *mpath = g_strdup(path);
1098 struct entry *entry;
1099 int result = -1;
1101 if ((q = extfs_get_path_mangle (me, mpath, &archive, 0)) == NULL)
1102 goto cleanup;
1103 entry = extfs_find_entry (archive->root_entry, q, 0, 0);
1104 if (entry == NULL)
1105 goto cleanup;
1106 if ((entry = extfs_resolve_symlinks (entry)) == NULL)
1107 goto cleanup;
1108 if (!S_ISDIR (entry->inode->mode)) {
1109 me->verrno = ENOTDIR;
1110 goto cleanup;
1113 if (extfs_cmd (" rmdir ", archive, entry, "")){
1114 my_errno = EIO;
1115 goto cleanup;
1117 extfs_remove_entry (entry);
1118 result = 0;
1119 cleanup:
1120 g_free (mpath);
1121 return result;
1124 static int
1125 extfs_chdir (struct vfs_class *me, const char *path)
1127 struct archive *archive = NULL;
1128 char *q;
1129 struct entry *entry;
1131 my_errno = ENOTDIR;
1132 if ((q = extfs_get_path (me, path, &archive, 0)) == NULL)
1133 return -1;
1134 entry = extfs_find_entry (archive->root_entry, q, 0, 0);
1135 g_free (q);
1136 if (!entry)
1137 return -1;
1138 entry = extfs_resolve_symlinks (entry);
1139 if ((!entry) || (!S_ISDIR (entry->inode->mode)))
1140 return -1;
1141 my_errno = 0;
1142 return 0;
1145 static off_t extfs_lseek (void *data, off_t offset, int whence)
1147 struct pseudofile *file = (struct pseudofile *) data;
1149 return lseek (file->local_handle, offset, whence);
1152 static vfsid
1153 extfs_getid (struct vfs_class *me, const char *path)
1155 struct archive *archive = NULL;
1156 char *p;
1158 if (!(p = extfs_get_path (me, path, &archive, 1)))
1159 return NULL;
1160 g_free (p);
1161 return (vfsid) archive;
1164 static int extfs_nothingisopen (vfsid id)
1166 if (((struct archive *)id)->fd_usage <= 0)
1167 return 1;
1168 return 0;
1171 static void extfs_remove_entry (struct entry *e)
1173 int i = --(e->inode->nlink);
1174 struct entry *pe, *ent, *prev;
1176 if (S_ISDIR (e->inode->mode) && e->inode->first_in_subdir != NULL) {
1177 struct entry *f = e->inode->first_in_subdir;
1178 e->inode->first_in_subdir = NULL;
1179 extfs_remove_entry (f);
1181 pe = e->dir;
1182 if (e == pe->inode->first_in_subdir)
1183 pe->inode->first_in_subdir = e->next_in_dir;
1185 prev = NULL;
1186 for (ent = pe->inode->first_in_subdir; ent && ent->next_in_dir;
1187 ent = ent->next_in_dir)
1188 if (e == ent->next_in_dir) {
1189 prev = ent;
1190 break;
1192 if (prev)
1193 prev->next_in_dir = e->next_in_dir;
1194 if (e == pe->inode->last_in_subdir)
1195 pe->inode->last_in_subdir = prev;
1197 if (i <= 0) {
1198 if (e->inode->local_filename != NULL) {
1199 unlink (e->inode->local_filename);
1200 free (e->inode->local_filename);
1202 g_free (e->inode->linkname);
1203 g_free (e->inode);
1206 g_free (e->name);
1207 g_free (e);
1210 static void extfs_free_entry (struct entry *e)
1212 int i = --(e->inode->nlink);
1213 if (S_ISDIR (e->inode->mode) && e->inode->first_in_subdir != NULL) {
1214 struct entry *f = e->inode->first_in_subdir;
1216 e->inode->first_in_subdir = NULL;
1217 extfs_free_entry (f);
1219 if (i <= 0) {
1220 if (e->inode->local_filename != NULL) {
1221 unlink (e->inode->local_filename);
1222 free (e->inode->local_filename);
1224 g_free (e->inode->linkname);
1225 g_free (e->inode);
1227 if (e->next_in_dir != NULL)
1228 extfs_free_entry (e->next_in_dir);
1229 g_free (e->name);
1230 g_free (e);
1233 static void extfs_free (vfsid id)
1235 struct archive *parc;
1236 struct archive *archive = (struct archive *)id;
1238 if (archive == first_archive) {
1239 first_archive = archive->next;
1240 } else {
1241 for (parc = first_archive; parc != NULL; parc = parc->next)
1242 if (parc->next == archive) {
1243 parc->next = archive->next;
1244 break;
1247 extfs_free_archive (archive);
1250 static char *
1251 extfs_getlocalcopy (struct vfs_class *me, const char *path)
1253 struct pseudofile *fp =
1254 (struct pseudofile *) extfs_open (me, path, O_RDONLY, 0);
1255 char *p;
1257 if (fp == NULL)
1258 return NULL;
1259 if (fp->entry->inode->local_filename == NULL) {
1260 extfs_close ((void *) fp);
1261 return NULL;
1263 p = g_strdup (fp->entry->inode->local_filename);
1264 fp->archive->fd_usage++;
1265 extfs_close ((void *) fp);
1266 return p;
1269 static int
1270 extfs_ungetlocalcopy (struct vfs_class *me, const char *path,
1271 const char *local, int has_changed)
1273 struct pseudofile *fp =
1274 (struct pseudofile *) extfs_open (me, path, O_RDONLY, 0);
1276 if (fp == NULL)
1277 return 0;
1278 if (!strcmp (fp->entry->inode->local_filename, local)) {
1279 fp->archive->fd_usage--;
1280 fp->has_changed |= has_changed;
1281 extfs_close ((void *) fp);
1282 return 0;
1283 } else {
1284 /* Should not happen */
1285 extfs_close ((void *) fp);
1286 return 0;
1291 static int extfs_init (struct vfs_class *me)
1293 FILE *cfg;
1294 char *mc_extfsini;
1295 char key[256];
1297 (void) me;
1299 mc_extfsini = concat_dir_and_file (mc_home, "extfs" PATH_SEP_STR "extfs.ini");
1300 cfg = fopen (mc_extfsini, "r");
1302 /* We may not use vfs_die() message or message or similar,
1303 * UI is not initialized at this time and message would not
1304 * appear on screen. */
1305 if (!cfg) {
1306 fprintf (stderr, _("Warning: file %s not found\n"), mc_extfsini);
1307 g_free (mc_extfsini);
1308 return 0;
1311 extfs_no = 0;
1312 while (extfs_no < MAXEXTFS && fgets (key, sizeof (key), cfg)) {
1313 char *c;
1315 /* Handle those with a trailing ':', those flag that the
1316 * file system does not require an archive to work
1319 if (*key == '[') {
1320 fprintf(stderr, "Warning: You need to update your %s file.\n",
1321 mc_extfsini);
1322 fclose(cfg);
1323 g_free (mc_extfsini);
1324 return 0;
1326 if (*key == '#' || *key == '\n')
1327 continue;
1329 if ((c = strchr (key, '\n'))){
1330 *c-- = 0;
1331 } else { /* Last line without newline or strlen (key) > 255 */
1332 c = &key [strlen (key) - 1];
1334 extfs_need_archive [extfs_no] = !(*c == ':');
1335 if (*c == ':')
1336 *c = 0;
1337 if (!(*key))
1338 continue;
1340 extfs_prefixes [extfs_no++] = g_strdup (key);
1342 fclose(cfg);
1343 g_free (mc_extfsini);
1344 return 1;
1347 static int extfs_which (struct vfs_class *me, const char *path)
1349 int i;
1351 (void) me;
1353 for (i = 0; i < extfs_no; i++)
1354 if (!strcmp (path, extfs_prefixes [i]))
1355 return i;
1356 return -1;
1359 static void extfs_done (struct vfs_class *me)
1361 int i;
1362 struct archive *ar;
1364 (void) me;
1366 for (ar = first_archive; ar != NULL;) {
1367 extfs_free ((vfsid) ar);
1368 ar = first_archive;
1371 for (i = 0; i < extfs_no; i++ )
1372 g_free (extfs_prefixes [i]);
1373 extfs_no = 0;
1376 static int
1377 extfs_setctl (struct vfs_class *me, const char *path, int ctlop, void *arg)
1379 (void) arg;
1381 if (ctlop == VFS_SETCTL_RUN) {
1382 extfs_run (me, path);
1383 return 1;
1385 return 0;
1388 void
1389 init_extfs (void)
1391 vfs_extfs_ops.name = "extfs";
1392 vfs_extfs_ops.init = extfs_init;
1393 vfs_extfs_ops.done = extfs_done;
1394 vfs_extfs_ops.fill_names = extfs_fill_names;
1395 vfs_extfs_ops.which = extfs_which;
1396 vfs_extfs_ops.open = extfs_open;
1397 vfs_extfs_ops.close = extfs_close;
1398 vfs_extfs_ops.read = extfs_read;
1399 vfs_extfs_ops.write = extfs_write;
1400 vfs_extfs_ops.opendir = extfs_opendir;
1401 vfs_extfs_ops.readdir = extfs_readdir;
1402 vfs_extfs_ops.closedir = extfs_closedir;
1403 vfs_extfs_ops.stat = extfs_stat;
1404 vfs_extfs_ops.lstat = extfs_lstat;
1405 vfs_extfs_ops.fstat = extfs_fstat;
1406 vfs_extfs_ops.chmod = extfs_chmod;
1407 vfs_extfs_ops.readlink = extfs_readlink;
1408 vfs_extfs_ops.unlink = extfs_unlink;
1409 vfs_extfs_ops.chdir = extfs_chdir;
1410 vfs_extfs_ops.ferrno = extfs_errno;
1411 vfs_extfs_ops.lseek = extfs_lseek;
1412 vfs_extfs_ops.getid = extfs_getid;
1413 vfs_extfs_ops.nothingisopen = extfs_nothingisopen;
1414 vfs_extfs_ops.free = extfs_free;
1415 vfs_extfs_ops.getlocalcopy = extfs_getlocalcopy;
1416 vfs_extfs_ops.ungetlocalcopy = extfs_ungetlocalcopy;
1417 vfs_extfs_ops.mkdir = extfs_mkdir;
1418 vfs_extfs_ops.rmdir = extfs_rmdir;
1419 vfs_extfs_ops.setctl = extfs_setctl;
1420 vfs_register_class (&vfs_extfs_ops);