added new parameter mcview_eof into ini-file
[free-mc.git] / vfs / extfs.c
blobd30774e7ae2917a4fa5cd8df11d5362193d75117
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 *str_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 str_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 g_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);
724 if (local_handle == -1) {
725 /* file exists(may be). Need to drop O_CREAT flag and truncate file content */
726 flags = ~O_CREAT & (NO_LINEAR (flags)|O_TRUNC);
727 local_handle = open (entry->inode->local_filename, flags , mode);
730 if (local_handle == -1)
731 ERRNOR (EIO, NULL);
733 extfs_info = g_new (struct pseudofile, 1);
734 extfs_info->archive = archive;
735 extfs_info->entry = entry;
736 extfs_info->has_changed = created;
737 extfs_info->local_handle = local_handle;
739 /* i.e. we had no open files and now we have one */
740 vfs_rmstamp (&vfs_extfs_ops, (vfsid) archive);
741 archive->fd_usage++;
742 return extfs_info;
745 static ssize_t extfs_read (void *data, char *buffer, int count)
747 struct pseudofile *file = (struct pseudofile *)data;
749 return read (file->local_handle, buffer, count);
752 static int
753 extfs_close (void *data)
755 struct pseudofile *file;
756 int errno_code = 0;
757 file = (struct pseudofile *) data;
759 close (file->local_handle);
761 /* Commit the file if it has changed */
762 if (file->has_changed) {
763 if (extfs_cmd
764 (" copyin ", file->archive, file->entry,
765 file->entry->inode->local_filename))
766 errno_code = EIO;
768 struct stat file_status;
769 if (stat (file->entry->inode->local_filename, &file_status) !=
771 errno_code = EIO;
772 else
773 file->entry->inode->size = file_status.st_size;
776 file->entry->inode->mtime = time (NULL);
779 file->archive->fd_usage--;
780 if (!file->archive->fd_usage)
781 vfs_stamp_create (&vfs_extfs_ops, file->archive);
783 g_free (data);
784 if (errno_code)
785 ERRNOR (EIO, -1);
786 return 0;
789 #define RECORDSIZE 512
791 static struct entry*
792 extfs_find_entry_int (struct entry *dir, char *name,
793 struct loop_protect *list, int make_dirs, int make_file)
795 struct entry *pent, *pdir;
796 char *p, *q, *name_end;
797 char c;
799 if (*name == '/') { /* Handle absolute paths */
800 name++;
801 dir = dir->inode->archive->root_entry;
804 pent = dir;
805 p = name;
806 name_end = name + strlen (name);
807 q = strchr (p, '/');
808 c = '/';
809 if (!q)
810 q = strchr (p, 0);
812 for (; pent != NULL && c && *p; ){
813 c = *q;
814 *q = 0;
816 if (strcmp (p, ".")){
817 if (!strcmp (p, ".."))
818 pent = pent->dir;
819 else {
820 if ((pent = extfs_resolve_symlinks_int (pent, list))==NULL){
821 *q = c;
822 return NULL;
824 if (!S_ISDIR (pent->inode->mode)){
825 *q = c;
826 notadir = 1;
827 return NULL;
829 pdir = pent;
830 for (pent = pent->inode->first_in_subdir; pent; pent = pent->next_in_dir)
831 /* Hack: I keep the original semanthic unless
832 q+1 would break in the strchr */
833 if (!strcmp (pent->name, p)){
834 if (q + 1 > name_end){
835 *q = c;
836 notadir = !S_ISDIR (pent->inode->mode);
837 return pent;
839 break;
842 /* When we load archive, we create automagically
843 * non-existant directories
845 if (pent == NULL && make_dirs) {
846 pent = extfs_generate_entry (dir->inode->archive, p, pdir, S_IFDIR | 0777);
848 if (pent == NULL && make_file) {
849 pent = extfs_generate_entry (dir->inode->archive, p, pdir, S_IFREG | 0666);
853 /* Next iteration */
854 *q = c;
855 p = q + 1;
856 q = strchr (p, '/');
857 if (!q)
858 q = strchr (p, 0);
860 if (pent == NULL)
861 my_errno = ENOENT;
862 return pent;
865 static struct entry *extfs_find_entry (struct entry *dir, char *name, int make_dirs, int make_file)
867 struct entry *res;
869 errloop = 0;
870 notadir = 0;
871 res = extfs_find_entry_int (dir, name, NULL, make_dirs, make_file);
872 if (res == NULL) {
873 if (errloop)
874 my_errno = ELOOP;
875 else if (notadir)
876 my_errno = ENOTDIR;
878 return res;
882 static int extfs_errno (struct vfs_class *me)
884 (void) me;
886 return my_errno;
889 static void * extfs_opendir (struct vfs_class *me, const char *dirname)
891 struct archive *archive = NULL;
892 char *q;
893 struct entry *entry;
894 struct entry **info;
896 if ((q = extfs_get_path (me, dirname, &archive, 0)) == NULL)
897 return NULL;
898 entry = extfs_find_entry (archive->root_entry, q, 0, 0);
899 g_free (q);
900 if (entry == NULL)
901 return NULL;
902 if ((entry = extfs_resolve_symlinks (entry)) == NULL)
903 return NULL;
904 if (!S_ISDIR (entry->inode->mode)) ERRNOR (ENOTDIR, NULL);
906 info = g_new (struct entry *, 2);
907 info[0] = entry->inode->first_in_subdir;
908 info[1] = entry->inode->first_in_subdir;
910 return info;
913 static void * extfs_readdir(void *data)
915 static union vfs_dirent dir;
916 struct entry **info = (struct entry **) data;
918 if (!*info)
919 return NULL;
921 g_strlcpy(dir.dent.d_name, (*info)->name, MC_MAXPATHLEN);
923 compute_namelen(&dir.dent);
924 *info = (*info)->next_in_dir;
926 return (void *) &dir;
929 static int extfs_closedir (void *data)
931 g_free (data);
932 return 0;
935 static void extfs_stat_move (struct stat *buf, const struct inode *inode)
937 buf->st_dev = inode->dev;
938 buf->st_ino = inode->inode;
939 buf->st_mode = inode->mode;
940 buf->st_nlink = inode->nlink;
941 buf->st_uid = inode->uid;
942 buf->st_gid = inode->gid;
943 #ifdef HAVE_STRUCT_STAT_ST_RDEV
944 buf->st_rdev = inode->rdev;
945 #endif
946 buf->st_size = inode->size;
947 #ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
948 buf->st_blksize = RECORDSIZE;
949 #endif
950 #ifdef HAVE_STRUCT_STAT_ST_BLOCKS
951 buf->st_blocks = (inode->size + RECORDSIZE - 1) / RECORDSIZE;
952 #endif
953 buf->st_atime = inode->atime;
954 buf->st_mtime = inode->mtime;
955 buf->st_ctime = inode->ctime;
958 static int
959 extfs_internal_stat (struct vfs_class *me, const char *path, struct stat *buf,
960 int resolve)
962 struct archive *archive;
963 char *q;
964 struct entry *entry;
965 char *path2 = g_strdup (path);
966 int result = -1;
968 if ((q = extfs_get_path_mangle (me, path2, &archive, 0)) == NULL)
969 goto cleanup;
970 entry = extfs_find_entry (archive->root_entry, q, 0, 0);
971 if (entry == NULL)
972 goto cleanup;
973 if (resolve && (entry = extfs_resolve_symlinks (entry)) == NULL)
974 goto cleanup;
975 extfs_stat_move (buf, entry->inode);
976 result = 0;
977 cleanup:
978 g_free (path2);
979 return result;
982 static int extfs_stat (struct vfs_class *me, const char *path, struct stat *buf)
984 return extfs_internal_stat (me, path, buf, 1);
987 static int extfs_lstat (struct vfs_class *me, const char *path, struct stat *buf)
989 return extfs_internal_stat (me, path, buf, 0);
992 static int extfs_fstat (void *data, struct stat *buf)
994 struct pseudofile *file = (struct pseudofile *)data;
996 extfs_stat_move (buf, file->entry->inode);
997 return 0;
1000 static int
1001 extfs_readlink (struct vfs_class *me, const char *path, char *buf, size_t size)
1003 struct archive *archive;
1004 char *q;
1005 size_t len;
1006 struct entry *entry;
1007 char *mpath = g_strdup (path);
1008 int result = -1;
1010 if ((q = extfs_get_path_mangle (me, mpath, &archive, 0)) == NULL)
1011 goto cleanup;
1012 entry = extfs_find_entry (archive->root_entry, q, 0, 0);
1013 if (entry == NULL)
1014 goto cleanup;
1015 if (!S_ISLNK (entry->inode->mode)) {
1016 me->verrno = EINVAL;
1017 goto cleanup;
1019 len = strlen (entry->inode->linkname);
1020 if (size < len)
1021 len = size;
1022 /* readlink() does not append a NUL character to buf */
1023 memcpy (buf, entry->inode->linkname, result = len);
1024 cleanup:
1025 g_free (mpath);
1026 return result;
1029 static int extfs_chown (struct vfs_class *me, const char *path, int owner, int group)
1031 (void) me;
1032 (void) path;
1033 (void) owner;
1034 (void) group;
1035 return 0;
1038 static int extfs_chmod (struct vfs_class *me, const char *path, int mode)
1040 (void) me;
1041 (void) path;
1042 (void) mode;
1043 return 0;
1046 static ssize_t extfs_write (void *data, const char *buf, int nbyte)
1048 struct pseudofile *file = (struct pseudofile *)data;
1050 file->has_changed = 1;
1051 return write (file->local_handle, buf, nbyte);
1054 static int extfs_unlink (struct vfs_class *me, const char *file)
1056 struct archive *archive;
1057 char *q, *mpath = g_strdup (file);
1058 struct entry *entry;
1059 int result = -1;
1061 if ((q = extfs_get_path_mangle (me, mpath, &archive, 0)) == NULL)
1062 goto cleanup;
1063 entry = extfs_find_entry (archive->root_entry, q, 0, 0);
1064 if (entry == NULL)
1065 goto cleanup;
1066 if ((entry = extfs_resolve_symlinks (entry)) == NULL)
1067 goto cleanup;
1068 if (S_ISDIR (entry->inode->mode)) {
1069 me->verrno = EISDIR;
1070 goto cleanup;
1072 if (extfs_cmd (" rm ", archive, entry, "")){
1073 my_errno = EIO;
1074 goto cleanup;
1076 extfs_remove_entry (entry);
1077 result = 0;
1078 cleanup:
1079 g_free (mpath);
1080 return result;
1083 static int extfs_mkdir (struct vfs_class *me, const char *path, mode_t mode)
1085 struct archive *archive;
1086 char *q, *mpath = g_strdup(path);
1087 struct entry *entry;
1088 int result = -1;
1090 (void) mode;
1092 if ((q = extfs_get_path_mangle (me, mpath, &archive, 0)) == NULL)
1093 goto cleanup;
1094 entry = extfs_find_entry (archive->root_entry, q, 0, 0);
1095 if (entry != NULL) {
1096 me->verrno = EEXIST;
1097 goto cleanup;
1099 entry = extfs_find_entry (archive->root_entry, q, 1, 0);
1100 if (entry == NULL)
1101 goto cleanup;
1102 if ((entry = extfs_resolve_symlinks (entry)) == NULL)
1103 goto cleanup;
1104 if (!S_ISDIR (entry->inode->mode)) {
1105 me->verrno = ENOTDIR;
1106 goto cleanup;
1109 if (extfs_cmd (" mkdir ", archive, entry, "")){
1110 my_errno = EIO;
1111 extfs_remove_entry (entry);
1112 goto cleanup;
1114 result = 0;
1115 cleanup:
1116 g_free (mpath);
1117 return result;
1120 static int extfs_rmdir (struct vfs_class *me, const char *path)
1122 struct archive *archive;
1123 char *q, *mpath = g_strdup(path);
1124 struct entry *entry;
1125 int result = -1;
1127 if ((q = extfs_get_path_mangle (me, mpath, &archive, 0)) == NULL)
1128 goto cleanup;
1129 entry = extfs_find_entry (archive->root_entry, q, 0, 0);
1130 if (entry == NULL)
1131 goto cleanup;
1132 if ((entry = extfs_resolve_symlinks (entry)) == NULL)
1133 goto cleanup;
1134 if (!S_ISDIR (entry->inode->mode)) {
1135 me->verrno = ENOTDIR;
1136 goto cleanup;
1139 if (extfs_cmd (" rmdir ", archive, entry, "")){
1140 my_errno = EIO;
1141 goto cleanup;
1143 extfs_remove_entry (entry);
1144 result = 0;
1145 cleanup:
1146 g_free (mpath);
1147 return result;
1150 static int
1151 extfs_chdir (struct vfs_class *me, const char *path)
1153 struct archive *archive = NULL;
1154 char *q;
1155 struct entry *entry;
1157 my_errno = ENOTDIR;
1158 if ((q = extfs_get_path (me, path, &archive, 0)) == NULL)
1159 return -1;
1160 entry = extfs_find_entry (archive->root_entry, q, 0, 0);
1161 g_free (q);
1162 if (!entry)
1163 return -1;
1164 entry = extfs_resolve_symlinks (entry);
1165 if ((!entry) || (!S_ISDIR (entry->inode->mode)))
1166 return -1;
1167 my_errno = 0;
1168 return 0;
1171 static off_t extfs_lseek (void *data, off_t offset, int whence)
1173 struct pseudofile *file = (struct pseudofile *) data;
1175 return lseek (file->local_handle, offset, whence);
1178 static vfsid
1179 extfs_getid (struct vfs_class *me, const char *path)
1181 struct archive *archive = NULL;
1182 char *p;
1184 if (!(p = extfs_get_path (me, path, &archive, 1)))
1185 return NULL;
1186 g_free (p);
1187 return (vfsid) archive;
1190 static int extfs_nothingisopen (vfsid id)
1192 if (((struct archive *)id)->fd_usage <= 0)
1193 return 1;
1194 return 0;
1197 static void extfs_remove_entry (struct entry *e)
1199 int i = --(e->inode->nlink);
1200 struct entry *pe, *ent, *prev;
1202 if (S_ISDIR (e->inode->mode) && e->inode->first_in_subdir != NULL) {
1203 struct entry *f = e->inode->first_in_subdir;
1204 e->inode->first_in_subdir = NULL;
1205 extfs_remove_entry (f);
1207 pe = e->dir;
1208 if (e == pe->inode->first_in_subdir)
1209 pe->inode->first_in_subdir = e->next_in_dir;
1211 prev = NULL;
1212 for (ent = pe->inode->first_in_subdir; ent && ent->next_in_dir;
1213 ent = ent->next_in_dir)
1214 if (e == ent->next_in_dir) {
1215 prev = ent;
1216 break;
1218 if (prev)
1219 prev->next_in_dir = e->next_in_dir;
1220 if (e == pe->inode->last_in_subdir)
1221 pe->inode->last_in_subdir = prev;
1223 if (i <= 0) {
1224 if (e->inode->local_filename != NULL) {
1225 unlink (e->inode->local_filename);
1226 g_free (e->inode->local_filename);
1228 g_free (e->inode->linkname);
1229 g_free (e->inode);
1232 g_free (e->name);
1233 g_free (e);
1236 static void extfs_free_entry (struct entry *e)
1238 int i = --(e->inode->nlink);
1239 if (S_ISDIR (e->inode->mode) && e->inode->first_in_subdir != NULL) {
1240 struct entry *f = e->inode->first_in_subdir;
1242 e->inode->first_in_subdir = NULL;
1243 extfs_free_entry (f);
1245 if (i <= 0) {
1246 if (e->inode->local_filename != NULL) {
1247 unlink (e->inode->local_filename);
1248 g_free (e->inode->local_filename);
1250 g_free (e->inode->linkname);
1251 g_free (e->inode);
1253 if (e->next_in_dir != NULL)
1254 extfs_free_entry (e->next_in_dir);
1255 g_free (e->name);
1256 g_free (e);
1259 static void extfs_free (vfsid id)
1261 struct archive *parc;
1262 struct archive *archive = (struct archive *)id;
1264 if (archive == first_archive) {
1265 first_archive = archive->next;
1266 } else {
1267 for (parc = first_archive; parc != NULL; parc = parc->next)
1268 if (parc->next == archive) {
1269 parc->next = archive->next;
1270 break;
1273 extfs_free_archive (archive);
1276 static char *
1277 extfs_getlocalcopy (struct vfs_class *me, const char *path)
1279 struct pseudofile *fp =
1280 (struct pseudofile *) extfs_open (me, path, O_RDONLY, 0);
1281 char *p;
1283 if (fp == NULL)
1284 return NULL;
1285 if (fp->entry->inode->local_filename == NULL) {
1286 extfs_close ((void *) fp);
1287 return NULL;
1289 p = g_strdup (fp->entry->inode->local_filename);
1290 fp->archive->fd_usage++;
1291 extfs_close ((void *) fp);
1292 return p;
1295 static int
1296 extfs_ungetlocalcopy (struct vfs_class *me, const char *path,
1297 const char *local, int has_changed)
1299 struct pseudofile *fp =
1300 (struct pseudofile *) extfs_open (me, path, O_RDONLY, 0);
1302 if (fp == NULL)
1303 return 0;
1304 if (!strcmp (fp->entry->inode->local_filename, local)) {
1305 fp->archive->fd_usage--;
1306 fp->has_changed |= has_changed;
1307 extfs_close ((void *) fp);
1308 return 0;
1309 } else {
1310 /* Should not happen */
1311 extfs_close ((void *) fp);
1312 return 0;
1317 static int extfs_init (struct vfs_class *me)
1319 FILE *cfg;
1320 char *mc_extfsini;
1321 char key[256];
1323 (void) me;
1325 mc_extfsini = concat_dir_and_file (mc_home, "extfs" PATH_SEP_STR "extfs.ini");
1326 cfg = fopen (mc_extfsini, "r");
1328 /* We may not use vfs_die() message or message or similar,
1329 * UI is not initialized at this time and message would not
1330 * appear on screen. */
1331 if (!cfg) {
1332 fprintf (stderr, _("Warning: file %s not found\n"), mc_extfsini);
1333 g_free (mc_extfsini);
1334 return 0;
1337 extfs_no = 0;
1338 while (extfs_no < MAXEXTFS && fgets (key, sizeof (key), cfg)) {
1339 char *c;
1341 /* Handle those with a trailing ':', those flag that the
1342 * file system does not require an archive to work
1345 if (*key == '[') {
1346 fprintf(stderr, "Warning: You need to update your %s file.\n",
1347 mc_extfsini);
1348 fclose(cfg);
1349 g_free (mc_extfsini);
1350 return 0;
1352 if (*key == '#' || *key == '\n')
1353 continue;
1355 if ((c = strchr (key, '\n'))){
1356 *c-- = 0;
1357 } else { /* Last line without newline or strlen (key) > 255 */
1358 c = &key [strlen (key) - 1];
1360 extfs_need_archive [extfs_no] = !(*c == ':');
1361 if (*c == ':')
1362 *c = 0;
1363 if (!(*key))
1364 continue;
1366 extfs_prefixes [extfs_no++] = g_strdup (key);
1368 fclose(cfg);
1369 g_free (mc_extfsini);
1370 return 1;
1373 static int extfs_which (struct vfs_class *me, const char *path)
1375 int i;
1377 (void) me;
1379 for (i = 0; i < extfs_no; i++)
1380 if (!strcmp (path, extfs_prefixes [i]))
1381 return i;
1382 return -1;
1385 static void extfs_done (struct vfs_class *me)
1387 int i;
1388 struct archive *ar;
1390 (void) me;
1392 for (ar = first_archive; ar != NULL;) {
1393 extfs_free ((vfsid) ar);
1394 ar = first_archive;
1397 for (i = 0; i < extfs_no; i++ )
1398 g_free (extfs_prefixes [i]);
1399 extfs_no = 0;
1402 static int
1403 extfs_setctl (struct vfs_class *me, const char *path, int ctlop, void *arg)
1405 (void) arg;
1407 if (ctlop == VFS_SETCTL_RUN) {
1408 extfs_run (me, path);
1409 return 1;
1411 return 0;
1414 void
1415 init_extfs (void)
1417 vfs_extfs_ops.name = "extfs";
1418 vfs_extfs_ops.init = extfs_init;
1419 vfs_extfs_ops.done = extfs_done;
1420 vfs_extfs_ops.fill_names = extfs_fill_names;
1421 vfs_extfs_ops.which = extfs_which;
1422 vfs_extfs_ops.open = extfs_open;
1423 vfs_extfs_ops.close = extfs_close;
1424 vfs_extfs_ops.read = extfs_read;
1425 vfs_extfs_ops.write = extfs_write;
1426 vfs_extfs_ops.opendir = extfs_opendir;
1427 vfs_extfs_ops.readdir = extfs_readdir;
1428 vfs_extfs_ops.closedir = extfs_closedir;
1429 vfs_extfs_ops.stat = extfs_stat;
1430 vfs_extfs_ops.lstat = extfs_lstat;
1431 vfs_extfs_ops.fstat = extfs_fstat;
1432 vfs_extfs_ops.chmod = extfs_chmod;
1433 vfs_extfs_ops.chown = extfs_chown;
1434 vfs_extfs_ops.readlink = extfs_readlink;
1435 vfs_extfs_ops.unlink = extfs_unlink;
1436 vfs_extfs_ops.chdir = extfs_chdir;
1437 vfs_extfs_ops.ferrno = extfs_errno;
1438 vfs_extfs_ops.lseek = extfs_lseek;
1439 vfs_extfs_ops.getid = extfs_getid;
1440 vfs_extfs_ops.nothingisopen = extfs_nothingisopen;
1441 vfs_extfs_ops.free = extfs_free;
1442 vfs_extfs_ops.getlocalcopy = extfs_getlocalcopy;
1443 vfs_extfs_ops.ungetlocalcopy = extfs_ungetlocalcopy;
1444 vfs_extfs_ops.mkdir = extfs_mkdir;
1445 vfs_extfs_ops.rmdir = extfs_rmdir;
1446 vfs_extfs_ops.setctl = extfs_setctl;
1447 vfs_register_class (&vfs_extfs_ops);