* add 'Whole words' option into the viewer 'Search' dialog
[midnight-commander.git] / vfs / extfs.c
blob9cd00a1462e115ce97bbd6bbf23b3665c9fe1118
1 /* Virtual File System: External file system.
2 Copyright (C) 1995, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
3 2006, 2007, 2009 Free Software Foundation, Inc.
5 Written by: 1995 Jakub Jelinek
6 Rewritten by: 1998 Pavel Machek
7 Additional changes by: 1999 Andrew T. Veliath
9 This program is free software; you can redistribute it and/or
10 modify it under the terms of the GNU Library General Public License
11 as published by the Free Software Foundation; either version 2 of
12 the License, or (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU Library General Public License for more details.
19 You should have received a copy of the GNU Library General Public
20 License along with this program; if not, write to the Free Software
21 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
23 /**
24 * \file
25 * \brief Source: Virtual File System: External file system
26 * \author Jakub Jelinek
27 * \author Pavel Machek
28 * \author Andrew T. Veliath
29 * \date 1995, 1998, 1999
32 /* Namespace: init_extfs */
34 #include <config.h>
36 #include <stdio.h>
37 #include <ctype.h>
38 #include <string.h>
39 #include <stdlib.h>
40 #include <sys/types.h>
41 #include <sys/stat.h>
42 #include <unistd.h>
43 #include <signal.h>
44 #include <fcntl.h>
45 #include <errno.h>
46 #include <sys/wait.h>
48 #include "../src/global.h"
50 #include "../src/wtools.h" /* message() */
51 #include "../src/main.h" /* print_vfs_message */
52 #include "../src/execute.h" /* For shell_execute */
54 #include "utilvfs.h"
55 #include "vfs.h"
56 #include "vfs-impl.h"
57 #include "gc.h" /* vfs_rmstamp */
59 #undef ERRNOR
60 #define ERRNOR(x,y) do { my_errno = x; return y; } while(0)
62 struct inode {
63 nlink_t nlink;
64 struct entry *first_in_subdir; /* only used if this is a directory */
65 struct entry *last_in_subdir;
66 ino_t inode; /* This is inode # */
67 dev_t dev; /* This is an internal identification of the extfs archive */
68 struct archive *archive; /* And this is an archive structure */
69 dev_t rdev;
70 mode_t mode;
71 uid_t uid;
72 gid_t gid;
73 off_t size;
74 time_t mtime;
75 char *linkname;
76 time_t atime;
77 time_t ctime;
78 char *local_filename;
81 struct entry {
82 struct entry *next_in_dir;
83 struct entry *dir;
84 char *name;
85 struct inode *inode;
88 struct pseudofile {
89 struct archive *archive;
90 unsigned int has_changed:1;
91 int local_handle;
92 struct entry *entry;
95 struct archive {
96 int fstype;
97 char *name;
98 char *local_name;
99 struct stat local_stat;
100 dev_t rdev;
101 int fd_usage;
102 ino_t inode_counter;
103 struct entry *root_entry;
104 struct archive *next;
107 static struct entry *extfs_find_entry (struct entry *dir, char *name,
108 int make_dirs, int make_file);
109 static int extfs_which (struct vfs_class *me, const char *path);
110 static void extfs_remove_entry (struct entry *e);
111 static void extfs_free (vfsid id);
112 static void extfs_free_entry (struct entry *e);
114 static struct vfs_class vfs_extfs_ops;
115 static struct archive *first_archive = NULL;
116 static int my_errno = 0;
118 #define MAXEXTFS 32
119 static char *extfs_prefixes [MAXEXTFS];
120 static char extfs_need_archive [MAXEXTFS];
121 static int extfs_no = 0;
123 static void
124 extfs_fill_names (struct vfs_class *me, fill_names_f func)
126 struct archive *a = first_archive;
127 char *name;
129 (void) me;
131 while (a) {
132 name =
133 g_strconcat (a->name ? a->name : "", "#",
134 extfs_prefixes[a->fstype], (char *) NULL);
135 (*func) (name);
136 g_free (name);
137 a = a->next;
141 static void extfs_make_dots (struct entry *ent)
143 struct entry *entry = g_new (struct entry, 1);
144 struct entry *parentry = ent->dir;
145 struct inode *inode = ent->inode, *parent;
147 parent = (parentry != NULL) ? parentry->inode : NULL;
148 entry->name = g_strdup (".");
149 entry->inode = inode;
150 entry->dir = ent;
151 inode->local_filename = NULL;
152 inode->first_in_subdir = entry;
153 inode->nlink++;
154 entry->next_in_dir = g_new (struct entry, 1);
155 entry = entry->next_in_dir;
156 entry->name = g_strdup ("..");
157 inode->last_in_subdir = entry;
158 entry->next_in_dir = NULL;
159 if (parent != NULL) {
160 entry->inode = parent;
161 entry->dir = parentry;
162 parent->nlink++;
163 } else {
164 entry->inode = inode;
165 entry->dir = ent;
166 inode->nlink++;
170 static struct entry *extfs_generate_entry (struct archive *archive,
171 const char *name, struct entry *parentry, mode_t mode)
173 mode_t myumask;
174 struct inode *inode, *parent;
175 struct entry *entry;
177 parent = (parentry != NULL) ? parentry->inode : NULL;
178 entry = g_new (struct entry, 1);
180 entry->name = g_strdup (name);
181 entry->next_in_dir = NULL;
182 entry->dir = parentry;
183 if (parent != NULL) {
184 parent->last_in_subdir->next_in_dir = entry;
185 parent->last_in_subdir = entry;
187 inode = g_new (struct inode, 1);
188 entry->inode = inode;
189 inode->local_filename = NULL;
190 inode->linkname = NULL;
191 inode->last_in_subdir = NULL;
192 inode->inode = (archive->inode_counter)++;
193 inode->dev = archive->rdev;
194 inode->archive = archive;
195 myumask = umask (022);
196 umask (myumask);
197 inode->mode = mode & ~myumask;
198 mode = inode->mode;
199 inode->rdev = 0;
200 inode->uid = getuid ();
201 inode->gid = getgid ();
202 inode->size = 0;
203 inode->mtime = time (NULL);
204 inode->atime = inode->mtime;
205 inode->ctime = inode->mtime;
206 inode->nlink = 1;
207 if (S_ISDIR (mode))
208 extfs_make_dots (entry);
209 return entry;
212 #if 0
213 static void extfs_free_entries (struct entry *entry)
215 (void) entry;
216 return;
218 #endif
220 static void extfs_free_archive (struct archive *archive)
222 extfs_free_entry (archive->root_entry);
223 if (archive->local_name != NULL) {
224 struct stat my;
226 mc_stat (archive->local_name, &my);
227 mc_ungetlocalcopy (archive->name, archive->local_name,
228 archive->local_stat.st_mtime != my.st_mtime);
229 g_free(archive->local_name);
231 g_free (archive->name);
232 g_free (archive);
235 static FILE *
236 extfs_open_archive (int fstype, const char *name, struct archive **pparc)
238 static dev_t archive_counter = 0;
239 FILE *result;
240 mode_t mode;
241 char *cmd;
242 char *mc_extfsdir;
243 struct stat mystat;
244 struct archive *current_archive;
245 struct entry *root_entry;
246 char *local_name = NULL, *tmp = 0;
247 int uses_archive = extfs_need_archive[fstype];
249 if (uses_archive) {
250 if (mc_stat (name, &mystat) == -1)
251 return NULL;
252 if (!vfs_file_is_local (name)) {
253 local_name = mc_getlocalcopy (name);
254 if (local_name == NULL)
255 return NULL;
257 tmp = name_quote (name, 0);
260 mc_extfsdir = concat_dir_and_file (mc_home_alt, "extfs" PATH_SEP_STR);
261 cmd =
262 g_strconcat (mc_extfsdir, extfs_prefixes[fstype], " list ",
263 local_name ? local_name : tmp, (char *) NULL);
264 g_free (tmp);
265 g_free (mc_extfsdir);
266 open_error_pipe ();
267 result = popen (cmd, "r");
268 g_free (cmd);
269 if (result == NULL) {
270 close_error_pipe (D_ERROR, NULL);
271 if (local_name) {
272 mc_ungetlocalcopy (name, local_name, 0);
273 g_free(local_name);
275 return NULL;
277 #ifdef ___QNXNTO__
278 setvbuf (result, NULL, _IONBF, 0);
279 #endif
281 current_archive = g_new (struct archive, 1);
282 current_archive->fstype = fstype;
283 current_archive->name = name ? g_strdup (name) : NULL;
284 current_archive->local_name = local_name;
286 if (local_name != NULL)
287 mc_stat (local_name, &current_archive->local_stat);
288 current_archive->inode_counter = 0;
289 current_archive->fd_usage = 0;
290 current_archive->rdev = archive_counter++;
291 current_archive->next = first_archive;
292 first_archive = current_archive;
293 mode = mystat.st_mode & 07777;
294 if (mode & 0400)
295 mode |= 0100;
296 if (mode & 0040)
297 mode |= 0010;
298 if (mode & 0004)
299 mode |= 0001;
300 mode |= S_IFDIR;
301 root_entry = extfs_generate_entry (current_archive, "/", NULL, mode);
302 root_entry->inode->uid = mystat.st_uid;
303 root_entry->inode->gid = mystat.st_gid;
304 root_entry->inode->atime = mystat.st_atime;
305 root_entry->inode->ctime = mystat.st_ctime;
306 root_entry->inode->mtime = mystat.st_mtime;
307 current_archive->root_entry = root_entry;
309 *pparc = current_archive;
311 return result;
315 * Main loop for reading an archive.
316 * Return 0 on success, -1 on error.
318 static int
319 extfs_read_archive (int fstype, const char *name, struct archive **pparc)
321 FILE *extfsd;
322 char *buffer;
323 struct archive *current_archive;
324 char *current_file_name, *current_link_name;
326 if ((extfsd =
327 extfs_open_archive (fstype, name, &current_archive)) == NULL) {
328 message (D_ERROR, MSG_ERROR, _("Cannot open %s archive\n%s"),
329 extfs_prefixes[fstype], name);
330 return -1;
333 buffer = g_malloc (4096);
334 while (fgets (buffer, 4096, extfsd) != NULL) {
335 struct stat hstat;
337 current_link_name = NULL;
338 if (vfs_parse_ls_lga
339 (buffer, &hstat, &current_file_name, &current_link_name)) {
340 struct entry *entry, *pent;
341 struct inode *inode;
342 char *p, *q, *cfn = current_file_name;
344 if (*cfn) {
345 if (*cfn == '/')
346 cfn++;
347 p = strchr (cfn, 0);
348 if (p != cfn && *(p - 1) == '/')
349 *(p - 1) = 0;
350 p = strrchr (cfn, '/');
351 if (p == NULL) {
352 p = cfn;
353 q = strchr (cfn, 0);
354 } else {
355 *(p++) = 0;
356 q = cfn;
358 if (S_ISDIR (hstat.st_mode)
359 && (!strcmp (p, ".") || !strcmp (p, "..")))
360 goto read_extfs_continue;
361 pent =
362 extfs_find_entry (current_archive->root_entry, q, 1,
364 if (pent == NULL) {
365 /* FIXME: Should clean everything one day */
366 g_free (buffer);
367 pclose (extfsd);
368 close_error_pipe (D_ERROR, _("Inconsistent extfs archive"));
369 return -1;
371 entry = g_new (struct entry, 1);
372 entry->name = g_strdup (p);
373 entry->next_in_dir = NULL;
374 entry->dir = pent;
375 if (pent->inode->last_in_subdir) {
376 pent->inode->last_in_subdir->next_in_dir = entry;
377 pent->inode->last_in_subdir = entry;
379 if (!S_ISLNK (hstat.st_mode) && current_link_name != NULL) {
380 pent =
381 extfs_find_entry (current_archive->root_entry,
382 current_link_name, 0, 0);
383 if (pent == NULL) {
384 /* FIXME: Should clean everything one day */
385 g_free (buffer);
386 pclose (extfsd);
387 close_error_pipe (D_ERROR,
388 _("Inconsistent extfs archive"));
389 return -1;
390 } else {
391 entry->inode = pent->inode;
392 pent->inode->nlink++;
394 } else {
395 inode = g_new (struct inode, 1);
396 entry->inode = inode;
397 inode->local_filename = NULL;
398 inode->inode = (current_archive->inode_counter)++;
399 inode->nlink = 1;
400 inode->dev = current_archive->rdev;
401 inode->archive = current_archive;
402 inode->mode = hstat.st_mode;
403 #ifdef HAVE_STRUCT_STAT_ST_RDEV
404 inode->rdev = hstat.st_rdev;
405 #else
406 inode->rdev = 0;
407 #endif
408 inode->uid = hstat.st_uid;
409 inode->gid = hstat.st_gid;
410 inode->size = hstat.st_size;
411 inode->mtime = hstat.st_mtime;
412 inode->atime = hstat.st_atime;
413 inode->ctime = hstat.st_ctime;
414 inode->first_in_subdir = NULL;
415 inode->last_in_subdir = NULL;
416 if (current_link_name != NULL
417 && S_ISLNK (hstat.st_mode)) {
418 inode->linkname = current_link_name;
419 current_link_name = NULL;
420 } else {
421 if (S_ISLNK (hstat.st_mode))
422 inode->mode &= ~S_IFLNK; /* You *DON'T* want to do this always */
423 inode->linkname = NULL;
425 if (S_ISDIR (hstat.st_mode))
426 extfs_make_dots (entry);
429 read_extfs_continue:
430 g_free (current_file_name);
431 g_free (current_link_name);
434 g_free (buffer);
436 /* Check if extfs 'list' returned 0 */
437 if (pclose (extfsd) != 0) {
438 extfs_free (current_archive);
439 close_error_pipe (D_ERROR, _("Inconsistent extfs archive"));
440 return -1;
443 close_error_pipe (D_ERROR, NULL);
444 *pparc = current_archive;
445 return 0;
449 * Dissect the path and create corresponding superblock. Note that inname
450 * can be changed and the result may point inside the original string.
452 static char *
453 extfs_get_path_mangle (struct vfs_class *me, char *inname, struct archive **archive,
454 int do_not_open)
456 char *local, *op;
457 const char *archive_name;
458 int result = -1;
459 struct archive *parc;
460 int fstype;
462 archive_name = inname;
463 vfs_split (inname, &local, &op);
465 fstype = extfs_which (me, op);
467 if (fstype == -1)
468 return NULL;
469 if (!local)
470 local = inname + strlen (inname);
473 * All filesystems should have some local archive, at least
474 * it can be '/'.
476 for (parc = first_archive; parc != NULL; parc = parc->next)
477 if (parc->name) {
478 if (!strcmp (parc->name, archive_name)) {
479 vfs_stamp (&vfs_extfs_ops, (vfsid) parc);
480 goto return_success;
484 result =
485 do_not_open ? -1 : extfs_read_archive (fstype, archive_name,
486 &parc);
487 if (result == -1)
488 ERRNOR (EIO, NULL);
490 return_success:
491 *archive = parc;
492 return local;
497 * Dissect the path and create corresponding superblock.
498 * The result should be freed.
500 static char *
501 extfs_get_path (struct vfs_class *me, const char *inname, struct archive **archive,
502 int do_not_open)
504 char *buf = g_strdup (inname);
505 char *res = extfs_get_path_mangle (me, buf, archive, do_not_open);
506 char *res2 = NULL;
507 if (res)
508 res2 = g_strdup (res);
509 g_free (buf);
510 return res2;
514 /* Return allocated path (without leading slash) inside the archive */
515 static char *extfs_get_path_from_entry (struct entry *entry)
517 struct list {
518 struct list *next;
519 char *name;
520 } *head, *p;
521 char *localpath;
522 size_t len;
524 for (len = 0, head = 0; entry->dir; entry = entry->dir) {
525 p = g_new (struct list, 1);
526 p->next = head;
527 p->name = entry->name;
528 head = p;
529 len += strlen (entry->name) + 1;
532 if (len == 0)
533 return g_strdup ("");
535 localpath = g_malloc (len);
536 *localpath = '\0';
537 while (head) {
538 strcat (localpath, head->name);
539 if (head->next)
540 strcat (localpath, "/");
541 p = head;
542 head = head->next;
543 g_free (p);
545 return (localpath);
549 struct loop_protect {
550 struct entry *entry;
551 struct loop_protect *next;
553 static int errloop;
554 static int notadir;
556 static struct entry *
557 extfs_find_entry_int (struct entry *dir, char *name,
558 struct loop_protect *list, int make_dirs, int make_file);
560 static struct entry *
561 extfs_resolve_symlinks_int (struct entry *entry,
562 struct loop_protect *list)
564 struct entry *pent;
565 struct loop_protect *looping;
567 if (!S_ISLNK (entry->inode->mode))
568 return entry;
569 for (looping = list; looping != NULL; looping = looping->next)
570 if (entry == looping->entry) { /* Here we protect us against symlink looping */
571 errloop = 1;
572 return NULL;
574 looping = g_new (struct loop_protect, 1);
575 looping->entry = entry;
576 looping->next = list;
577 pent = extfs_find_entry_int (entry->dir, entry->inode->linkname, looping, 0, 0);
578 g_free (looping);
579 if (pent == NULL)
580 my_errno = ENOENT;
581 return pent;
584 static struct entry *extfs_resolve_symlinks (struct entry *entry)
586 struct entry *res;
588 errloop = 0;
589 notadir = 0;
590 res = extfs_resolve_symlinks_int (entry, NULL);
591 if (res == NULL) {
592 if (errloop)
593 my_errno = ELOOP;
594 else if (notadir)
595 my_errno = ENOTDIR;
597 return res;
600 static const char *
601 extfs_get_archive_name (struct archive *archive)
603 const char *archive_name;
605 if (archive->local_name)
606 archive_name = archive->local_name;
607 else
608 archive_name = archive->name;
610 if (!archive_name || !*archive_name)
611 return "no_archive_name";
612 else
613 return archive_name;
616 /* Don't pass localname as NULL */
617 static int
618 extfs_cmd (const char *extfs_cmd, struct archive *archive,
619 struct entry *entry, const char *localname)
621 char *file;
622 char *quoted_file;
623 char *quoted_localname;
624 char *archive_name;
625 char *mc_extfsdir;
626 char *cmd;
627 int retval;
629 file = extfs_get_path_from_entry (entry);
630 quoted_file = name_quote (file, 0);
631 g_free (file);
632 archive_name = name_quote (extfs_get_archive_name (archive), 0);
633 quoted_localname = name_quote (localname, 0);
635 mc_extfsdir = concat_dir_and_file (mc_home_alt, "extfs" PATH_SEP_STR);
636 cmd = g_strconcat (mc_extfsdir, extfs_prefixes[archive->fstype],
637 extfs_cmd, archive_name, " ", quoted_file, " ",
638 quoted_localname, (char *) NULL);
639 g_free (quoted_file);
640 g_free (quoted_localname);
641 g_free (mc_extfsdir);
642 g_free (archive_name);
644 open_error_pipe ();
645 retval = my_system (EXECUTE_AS_SHELL, shell, cmd);
646 g_free (cmd);
647 close_error_pipe (D_ERROR, NULL);
648 return retval;
651 static void
652 extfs_run (struct vfs_class *me, const char *file)
654 struct archive *archive = NULL;
655 char *p, *q, *archive_name, *mc_extfsdir;
656 char *cmd;
658 if ((p = extfs_get_path (me, file, &archive, 0)) == NULL)
659 return;
660 q = name_quote (p, 0);
661 g_free (p);
663 archive_name = name_quote (extfs_get_archive_name (archive), 0);
664 mc_extfsdir = concat_dir_and_file (mc_home_alt, "extfs" PATH_SEP_STR);
665 cmd = g_strconcat (mc_extfsdir, extfs_prefixes[archive->fstype],
666 " run ", archive_name, " ", q, (char *) NULL);
667 g_free (mc_extfsdir);
668 g_free (archive_name);
669 g_free (q);
670 shell_execute (cmd, 0);
671 g_free (cmd);
674 static void *
675 extfs_open (struct vfs_class *me, const char *file, int flags, int mode)
677 struct pseudofile *extfs_info;
678 struct archive *archive = NULL;
679 char *q;
680 struct entry *entry;
681 int local_handle;
682 int created = 0;
684 if ((q = extfs_get_path (me, file, &archive, 0)) == NULL)
685 return NULL;
686 entry = extfs_find_entry (archive->root_entry, q, 0, 0);
687 if (entry == NULL && (flags & O_CREAT)) {
688 /* Create new entry */
689 entry = extfs_find_entry (archive->root_entry, q, 0, 1);
690 created = (entry != NULL);
693 g_free (q);
694 if (entry == NULL)
695 return NULL;
696 if ((entry = extfs_resolve_symlinks (entry)) == NULL)
697 return NULL;
699 if (S_ISDIR (entry->inode->mode))
700 ERRNOR (EISDIR, NULL);
702 if (entry->inode->local_filename == NULL) {
703 char *local_filename;
705 local_handle = vfs_mkstemps (&local_filename, "extfs", entry->name);
707 if (local_handle == -1)
708 return NULL;
709 close (local_handle);
711 if (!created && !(flags & O_TRUNC)
712 && extfs_cmd (" copyout ", archive, entry, local_filename)) {
713 unlink (local_filename);
714 free (local_filename);
715 my_errno = EIO;
716 return NULL;
718 entry->inode->local_filename = local_filename;
721 local_handle =
722 open (entry->inode->local_filename, NO_LINEAR (flags), mode);
723 if (local_handle == -1)
724 ERRNOR (EIO, NULL);
726 extfs_info = g_new (struct pseudofile, 1);
727 extfs_info->archive = archive;
728 extfs_info->entry = entry;
729 extfs_info->has_changed = created;
730 extfs_info->local_handle = local_handle;
732 /* i.e. we had no open files and now we have one */
733 vfs_rmstamp (&vfs_extfs_ops, (vfsid) archive);
734 archive->fd_usage++;
735 return extfs_info;
738 static ssize_t extfs_read (void *data, char *buffer, int count)
740 struct pseudofile *file = (struct pseudofile *)data;
742 return read (file->local_handle, buffer, count);
745 static int
746 extfs_close (void *data)
748 struct pseudofile *file;
749 int errno_code = 0;
750 file = (struct pseudofile *) data;
752 close (file->local_handle);
754 /* Commit the file if it has changed */
755 if (file->has_changed) {
756 if (extfs_cmd
757 (" copyin ", file->archive, file->entry,
758 file->entry->inode->local_filename))
759 errno_code = EIO;
761 struct stat file_status;
762 if (stat (file->entry->inode->local_filename, &file_status) !=
764 errno_code = EIO;
765 else
766 file->entry->inode->size = file_status.st_size;
769 file->entry->inode->mtime = time (NULL);
772 file->archive->fd_usage--;
773 if (!file->archive->fd_usage)
774 vfs_stamp_create (&vfs_extfs_ops, file->archive);
776 g_free (data);
777 if (errno_code)
778 ERRNOR (EIO, -1);
779 return 0;
782 #define RECORDSIZE 512
784 static struct entry*
785 extfs_find_entry_int (struct entry *dir, char *name,
786 struct loop_protect *list, int make_dirs, int make_file)
788 struct entry *pent, *pdir;
789 char *p, *q, *name_end;
790 char c;
792 if (*name == '/') { /* Handle absolute paths */
793 name++;
794 dir = dir->inode->archive->root_entry;
797 pent = dir;
798 p = name;
799 name_end = name + strlen (name);
800 q = strchr (p, '/');
801 c = '/';
802 if (!q)
803 q = strchr (p, 0);
805 for (; pent != NULL && c && *p; ){
806 c = *q;
807 *q = 0;
809 if (strcmp (p, ".")){
810 if (!strcmp (p, ".."))
811 pent = pent->dir;
812 else {
813 if ((pent = extfs_resolve_symlinks_int (pent, list))==NULL){
814 *q = c;
815 return NULL;
817 if (!S_ISDIR (pent->inode->mode)){
818 *q = c;
819 notadir = 1;
820 return NULL;
822 pdir = pent;
823 for (pent = pent->inode->first_in_subdir; pent; pent = pent->next_in_dir)
824 /* Hack: I keep the original semanthic unless
825 q+1 would break in the strchr */
826 if (!strcmp (pent->name, p)){
827 if (q + 1 > name_end){
828 *q = c;
829 notadir = !S_ISDIR (pent->inode->mode);
830 return pent;
832 break;
835 /* When we load archive, we create automagically
836 * non-existant directories
838 if (pent == NULL && make_dirs) {
839 pent = extfs_generate_entry (dir->inode->archive, p, pdir, S_IFDIR | 0777);
841 if (pent == NULL && make_file) {
842 pent = extfs_generate_entry (dir->inode->archive, p, pdir, S_IFREG | 0666);
846 /* Next iteration */
847 *q = c;
848 p = q + 1;
849 q = strchr (p, '/');
850 if (!q)
851 q = strchr (p, 0);
853 if (pent == NULL)
854 my_errno = ENOENT;
855 return pent;
858 static struct entry *extfs_find_entry (struct entry *dir, char *name, int make_dirs, int make_file)
860 struct entry *res;
862 errloop = 0;
863 notadir = 0;
864 res = extfs_find_entry_int (dir, name, NULL, make_dirs, make_file);
865 if (res == NULL) {
866 if (errloop)
867 my_errno = ELOOP;
868 else if (notadir)
869 my_errno = ENOTDIR;
871 return res;
875 static int extfs_errno (struct vfs_class *me)
877 (void) me;
879 return my_errno;
882 static void * extfs_opendir (struct vfs_class *me, const char *dirname)
884 struct archive *archive = NULL;
885 char *q;
886 struct entry *entry;
887 struct entry **info;
889 if ((q = extfs_get_path (me, dirname, &archive, 0)) == NULL)
890 return NULL;
891 entry = extfs_find_entry (archive->root_entry, q, 0, 0);
892 g_free (q);
893 if (entry == NULL)
894 return NULL;
895 if ((entry = extfs_resolve_symlinks (entry)) == NULL)
896 return NULL;
897 if (!S_ISDIR (entry->inode->mode)) ERRNOR (ENOTDIR, NULL);
899 info = g_new (struct entry *, 2);
900 info[0] = entry->inode->first_in_subdir;
901 info[1] = entry->inode->first_in_subdir;
903 return info;
906 static void * extfs_readdir(void *data)
908 static union vfs_dirent dir;
909 struct entry **info = (struct entry **) data;
911 if (!*info)
912 return NULL;
914 g_strlcpy(dir.dent.d_name, (*info)->name, MC_MAXPATHLEN);
916 compute_namelen(&dir.dent);
917 *info = (*info)->next_in_dir;
919 return (void *) &dir;
922 static int extfs_closedir (void *data)
924 g_free (data);
925 return 0;
928 static void extfs_stat_move (struct stat *buf, const struct inode *inode)
930 buf->st_dev = inode->dev;
931 buf->st_ino = inode->inode;
932 buf->st_mode = inode->mode;
933 buf->st_nlink = inode->nlink;
934 buf->st_uid = inode->uid;
935 buf->st_gid = inode->gid;
936 #ifdef HAVE_STRUCT_STAT_ST_RDEV
937 buf->st_rdev = inode->rdev;
938 #endif
939 buf->st_size = inode->size;
940 #ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
941 buf->st_blksize = RECORDSIZE;
942 #endif
943 #ifdef HAVE_STRUCT_STAT_ST_BLOCKS
944 buf->st_blocks = (inode->size + RECORDSIZE - 1) / RECORDSIZE;
945 #endif
946 buf->st_atime = inode->atime;
947 buf->st_mtime = inode->mtime;
948 buf->st_ctime = inode->ctime;
951 static int
952 extfs_internal_stat (struct vfs_class *me, const char *path, struct stat *buf,
953 int resolve)
955 struct archive *archive;
956 char *q;
957 struct entry *entry;
958 char *path2 = g_strdup (path);
959 int result = -1;
961 if ((q = extfs_get_path_mangle (me, path2, &archive, 0)) == NULL)
962 goto cleanup;
963 entry = extfs_find_entry (archive->root_entry, q, 0, 0);
964 if (entry == NULL)
965 goto cleanup;
966 if (resolve && (entry = extfs_resolve_symlinks (entry)) == NULL)
967 goto cleanup;
968 extfs_stat_move (buf, entry->inode);
969 result = 0;
970 cleanup:
971 g_free (path2);
972 return result;
975 static int extfs_stat (struct vfs_class *me, const char *path, struct stat *buf)
977 return extfs_internal_stat (me, path, buf, 1);
980 static int extfs_lstat (struct vfs_class *me, const char *path, struct stat *buf)
982 return extfs_internal_stat (me, path, buf, 0);
985 static int extfs_fstat (void *data, struct stat *buf)
987 struct pseudofile *file = (struct pseudofile *)data;
989 extfs_stat_move (buf, file->entry->inode);
990 return 0;
993 static int
994 extfs_readlink (struct vfs_class *me, const char *path, char *buf, size_t size)
996 struct archive *archive;
997 char *q;
998 size_t len;
999 struct entry *entry;
1000 char *mpath = g_strdup (path);
1001 int result = -1;
1003 if ((q = extfs_get_path_mangle (me, mpath, &archive, 0)) == NULL)
1004 goto cleanup;
1005 entry = extfs_find_entry (archive->root_entry, q, 0, 0);
1006 if (entry == NULL)
1007 goto cleanup;
1008 if (!S_ISLNK (entry->inode->mode)) {
1009 me->verrno = EINVAL;
1010 goto cleanup;
1012 len = strlen (entry->inode->linkname);
1013 if (size < len)
1014 len = size;
1015 /* readlink() does not append a NUL character to buf */
1016 memcpy (buf, entry->inode->linkname, result = len);
1017 cleanup:
1018 g_free (mpath);
1019 return result;
1022 static int extfs_chmod (struct vfs_class *me, const char *path, int mode)
1024 (void) me;
1025 (void) path;
1026 (void) mode;
1027 return 0;
1030 static ssize_t extfs_write (void *data, const char *buf, int nbyte)
1032 struct pseudofile *file = (struct pseudofile *)data;
1034 file->has_changed = 1;
1035 return write (file->local_handle, buf, nbyte);
1038 static int extfs_unlink (struct vfs_class *me, const char *file)
1040 struct archive *archive;
1041 char *q, *mpath = g_strdup (file);
1042 struct entry *entry;
1043 int result = -1;
1045 if ((q = extfs_get_path_mangle (me, mpath, &archive, 0)) == NULL)
1046 goto cleanup;
1047 entry = extfs_find_entry (archive->root_entry, q, 0, 0);
1048 if (entry == NULL)
1049 goto cleanup;
1050 if ((entry = extfs_resolve_symlinks (entry)) == NULL)
1051 goto cleanup;
1052 if (S_ISDIR (entry->inode->mode)) {
1053 me->verrno = EISDIR;
1054 goto cleanup;
1056 if (extfs_cmd (" rm ", archive, entry, "")){
1057 my_errno = EIO;
1058 goto cleanup;
1060 extfs_remove_entry (entry);
1061 result = 0;
1062 cleanup:
1063 g_free (mpath);
1064 return result;
1067 static int extfs_mkdir (struct vfs_class *me, const char *path, mode_t mode)
1069 struct archive *archive;
1070 char *q, *mpath = g_strdup(path);
1071 struct entry *entry;
1072 int result = -1;
1074 (void) mode;
1076 if ((q = extfs_get_path_mangle (me, mpath, &archive, 0)) == NULL)
1077 goto cleanup;
1078 entry = extfs_find_entry (archive->root_entry, q, 0, 0);
1079 if (entry != NULL) {
1080 me->verrno = EEXIST;
1081 goto cleanup;
1083 entry = extfs_find_entry (archive->root_entry, q, 1, 0);
1084 if (entry == NULL)
1085 goto cleanup;
1086 if ((entry = extfs_resolve_symlinks (entry)) == NULL)
1087 goto cleanup;
1088 if (!S_ISDIR (entry->inode->mode)) {
1089 me->verrno = ENOTDIR;
1090 goto cleanup;
1093 if (extfs_cmd (" mkdir ", archive, entry, "")){
1094 my_errno = EIO;
1095 extfs_remove_entry (entry);
1096 goto cleanup;
1098 result = 0;
1099 cleanup:
1100 g_free (mpath);
1101 return result;
1104 static int extfs_rmdir (struct vfs_class *me, const char *path)
1106 struct archive *archive;
1107 char *q, *mpath = g_strdup(path);
1108 struct entry *entry;
1109 int result = -1;
1111 if ((q = extfs_get_path_mangle (me, mpath, &archive, 0)) == NULL)
1112 goto cleanup;
1113 entry = extfs_find_entry (archive->root_entry, q, 0, 0);
1114 if (entry == NULL)
1115 goto cleanup;
1116 if ((entry = extfs_resolve_symlinks (entry)) == NULL)
1117 goto cleanup;
1118 if (!S_ISDIR (entry->inode->mode)) {
1119 me->verrno = ENOTDIR;
1120 goto cleanup;
1123 if (extfs_cmd (" rmdir ", archive, entry, "")){
1124 my_errno = EIO;
1125 goto cleanup;
1127 extfs_remove_entry (entry);
1128 result = 0;
1129 cleanup:
1130 g_free (mpath);
1131 return result;
1134 static int
1135 extfs_chdir (struct vfs_class *me, const char *path)
1137 struct archive *archive = NULL;
1138 char *q;
1139 struct entry *entry;
1141 my_errno = ENOTDIR;
1142 if ((q = extfs_get_path (me, path, &archive, 0)) == NULL)
1143 return -1;
1144 entry = extfs_find_entry (archive->root_entry, q, 0, 0);
1145 g_free (q);
1146 if (!entry)
1147 return -1;
1148 entry = extfs_resolve_symlinks (entry);
1149 if ((!entry) || (!S_ISDIR (entry->inode->mode)))
1150 return -1;
1151 my_errno = 0;
1152 return 0;
1155 static off_t extfs_lseek (void *data, off_t offset, int whence)
1157 struct pseudofile *file = (struct pseudofile *) data;
1159 return lseek (file->local_handle, offset, whence);
1162 static vfsid
1163 extfs_getid (struct vfs_class *me, const char *path)
1165 struct archive *archive = NULL;
1166 char *p;
1168 if (!(p = extfs_get_path (me, path, &archive, 1)))
1169 return NULL;
1170 g_free (p);
1171 return (vfsid) archive;
1174 static int extfs_nothingisopen (vfsid id)
1176 if (((struct archive *)id)->fd_usage <= 0)
1177 return 1;
1178 return 0;
1181 static void extfs_remove_entry (struct entry *e)
1183 int i = --(e->inode->nlink);
1184 struct entry *pe, *ent, *prev;
1186 if (S_ISDIR (e->inode->mode) && e->inode->first_in_subdir != NULL) {
1187 struct entry *f = e->inode->first_in_subdir;
1188 e->inode->first_in_subdir = NULL;
1189 extfs_remove_entry (f);
1191 pe = e->dir;
1192 if (e == pe->inode->first_in_subdir)
1193 pe->inode->first_in_subdir = e->next_in_dir;
1195 prev = NULL;
1196 for (ent = pe->inode->first_in_subdir; ent && ent->next_in_dir;
1197 ent = ent->next_in_dir)
1198 if (e == ent->next_in_dir) {
1199 prev = ent;
1200 break;
1202 if (prev)
1203 prev->next_in_dir = e->next_in_dir;
1204 if (e == pe->inode->last_in_subdir)
1205 pe->inode->last_in_subdir = prev;
1207 if (i <= 0) {
1208 if (e->inode->local_filename != NULL) {
1209 unlink (e->inode->local_filename);
1210 free (e->inode->local_filename);
1212 g_free (e->inode->linkname);
1213 g_free (e->inode);
1216 g_free (e->name);
1217 g_free (e);
1220 static void extfs_free_entry (struct entry *e)
1222 int i = --(e->inode->nlink);
1223 if (S_ISDIR (e->inode->mode) && e->inode->first_in_subdir != NULL) {
1224 struct entry *f = e->inode->first_in_subdir;
1226 e->inode->first_in_subdir = NULL;
1227 extfs_free_entry (f);
1229 if (i <= 0) {
1230 if (e->inode->local_filename != NULL) {
1231 unlink (e->inode->local_filename);
1232 free (e->inode->local_filename);
1234 g_free (e->inode->linkname);
1235 g_free (e->inode);
1237 if (e->next_in_dir != NULL)
1238 extfs_free_entry (e->next_in_dir);
1239 g_free (e->name);
1240 g_free (e);
1243 static void extfs_free (vfsid id)
1245 struct archive *parc;
1246 struct archive *archive = (struct archive *)id;
1248 if (archive == first_archive) {
1249 first_archive = archive->next;
1250 } else {
1251 for (parc = first_archive; parc != NULL; parc = parc->next)
1252 if (parc->next == archive) {
1253 parc->next = archive->next;
1254 break;
1257 extfs_free_archive (archive);
1260 static char *
1261 extfs_getlocalcopy (struct vfs_class *me, const char *path)
1263 struct pseudofile *fp =
1264 (struct pseudofile *) extfs_open (me, path, O_RDONLY, 0);
1265 char *p;
1267 if (fp == NULL)
1268 return NULL;
1269 if (fp->entry->inode->local_filename == NULL) {
1270 extfs_close ((void *) fp);
1271 return NULL;
1273 p = g_strdup (fp->entry->inode->local_filename);
1274 fp->archive->fd_usage++;
1275 extfs_close ((void *) fp);
1276 return p;
1279 static int
1280 extfs_ungetlocalcopy (struct vfs_class *me, const char *path,
1281 const char *local, int has_changed)
1283 struct pseudofile *fp =
1284 (struct pseudofile *) extfs_open (me, path, O_RDONLY, 0);
1286 if (fp == NULL)
1287 return 0;
1288 if (!strcmp (fp->entry->inode->local_filename, local)) {
1289 fp->archive->fd_usage--;
1290 fp->has_changed |= has_changed;
1291 extfs_close ((void *) fp);
1292 return 0;
1293 } else {
1294 /* Should not happen */
1295 extfs_close ((void *) fp);
1296 return 0;
1301 static int extfs_init (struct vfs_class *me)
1303 FILE *cfg;
1304 char *mc_extfsini;
1305 char key[256];
1307 (void) me;
1309 mc_extfsini = concat_dir_and_file (mc_home, "extfs" PATH_SEP_STR "extfs.ini");
1310 cfg = fopen (mc_extfsini, "r");
1312 /* We may not use vfs_die() message or message or similar,
1313 * UI is not initialized at this time and message would not
1314 * appear on screen. */
1315 if (!cfg) {
1316 fprintf (stderr, _("Warning: file %s not found\n"), mc_extfsini);
1317 g_free (mc_extfsini);
1318 return 0;
1321 extfs_no = 0;
1322 while (extfs_no < MAXEXTFS && fgets (key, sizeof (key), cfg)) {
1323 char *c;
1325 /* Handle those with a trailing ':', those flag that the
1326 * file system does not require an archive to work
1329 if (*key == '[') {
1330 fprintf(stderr, "Warning: You need to update your %s file.\n",
1331 mc_extfsini);
1332 fclose(cfg);
1333 g_free (mc_extfsini);
1334 return 0;
1336 if (*key == '#' || *key == '\n')
1337 continue;
1339 if ((c = strchr (key, '\n'))){
1340 *c-- = 0;
1341 } else { /* Last line without newline or strlen (key) > 255 */
1342 c = &key [strlen (key) - 1];
1344 extfs_need_archive [extfs_no] = !(*c == ':');
1345 if (*c == ':')
1346 *c = 0;
1347 if (!(*key))
1348 continue;
1350 extfs_prefixes [extfs_no++] = g_strdup (key);
1352 fclose(cfg);
1353 g_free (mc_extfsini);
1354 return 1;
1357 static int extfs_which (struct vfs_class *me, const char *path)
1359 int i;
1361 (void) me;
1363 for (i = 0; i < extfs_no; i++)
1364 if (!strcmp (path, extfs_prefixes [i]))
1365 return i;
1366 return -1;
1369 static void extfs_done (struct vfs_class *me)
1371 int i;
1372 struct archive *ar;
1374 (void) me;
1376 for (ar = first_archive; ar != NULL;) {
1377 extfs_free ((vfsid) ar);
1378 ar = first_archive;
1381 for (i = 0; i < extfs_no; i++ )
1382 g_free (extfs_prefixes [i]);
1383 extfs_no = 0;
1386 static int
1387 extfs_setctl (struct vfs_class *me, const char *path, int ctlop, void *arg)
1389 (void) arg;
1391 if (ctlop == VFS_SETCTL_RUN) {
1392 extfs_run (me, path);
1393 return 1;
1395 return 0;
1398 void
1399 init_extfs (void)
1401 vfs_extfs_ops.name = "extfs";
1402 vfs_extfs_ops.init = extfs_init;
1403 vfs_extfs_ops.done = extfs_done;
1404 vfs_extfs_ops.fill_names = extfs_fill_names;
1405 vfs_extfs_ops.which = extfs_which;
1406 vfs_extfs_ops.open = extfs_open;
1407 vfs_extfs_ops.close = extfs_close;
1408 vfs_extfs_ops.read = extfs_read;
1409 vfs_extfs_ops.write = extfs_write;
1410 vfs_extfs_ops.opendir = extfs_opendir;
1411 vfs_extfs_ops.readdir = extfs_readdir;
1412 vfs_extfs_ops.closedir = extfs_closedir;
1413 vfs_extfs_ops.stat = extfs_stat;
1414 vfs_extfs_ops.lstat = extfs_lstat;
1415 vfs_extfs_ops.fstat = extfs_fstat;
1416 vfs_extfs_ops.chmod = extfs_chmod;
1417 vfs_extfs_ops.readlink = extfs_readlink;
1418 vfs_extfs_ops.unlink = extfs_unlink;
1419 vfs_extfs_ops.chdir = extfs_chdir;
1420 vfs_extfs_ops.ferrno = extfs_errno;
1421 vfs_extfs_ops.lseek = extfs_lseek;
1422 vfs_extfs_ops.getid = extfs_getid;
1423 vfs_extfs_ops.nothingisopen = extfs_nothingisopen;
1424 vfs_extfs_ops.free = extfs_free;
1425 vfs_extfs_ops.getlocalcopy = extfs_getlocalcopy;
1426 vfs_extfs_ops.ungetlocalcopy = extfs_ungetlocalcopy;
1427 vfs_extfs_ops.mkdir = extfs_mkdir;
1428 vfs_extfs_ops.rmdir = extfs_rmdir;
1429 vfs_extfs_ops.setctl = extfs_setctl;
1430 vfs_register_class (&vfs_extfs_ops);