* main.c (handle_args): Don't call mc_get_current_wd().
[midnight-commander.git] / vfs / extfs.c
blob17e814cca8d9e62f26083b2a7185e1612dacf046
1 /* Virtual File System: External file system.
2 Copyright (C) 1995 The Free Software Foundation
4 Written by: 1995 Jakub Jelinek
5 Rewritten by: 1998 Pavel Machek
6 Additional changes by: 1999 Andrew T. Veliath
8 $Id$
10 This program is free software; you can redistribute it and/or
11 modify it under the terms of the GNU Library General Public License
12 as published by the Free Software Foundation; either version 2 of
13 the License, or (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU Library General Public License for more details.
20 You should have received a copy of the GNU Library General Public
21 License along with this program; if not, write to the Free Software
22 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
24 /* Namespace: exports only vfs_extfs_ops */
26 #include <config.h>
27 #include <stdio.h>
28 #include <ctype.h>
29 #include <string.h>
30 #include <stdlib.h>
31 #include <sys/types.h>
32 #include <sys/stat.h>
33 #include <fcntl.h>
34 #include <unistd.h>
35 #include <signal.h>
36 #ifdef HAVE_SYS_WAIT_H
37 #include <sys/wait.h>
38 #endif
39 #include <errno.h>
40 #include "utilvfs.h"
41 #include "../src/dialog.h"
42 #include "../src/main.h" /* For shell_execute */
43 #include "xdirentry.h"
44 #include "vfs.h"
45 #include "extfs.h"
47 #undef ERRNOR
48 #define ERRNOR(x,y) do { my_errno = x; return y; } while(0)
50 struct inode {
51 nlink_t nlink;
52 struct entry *first_in_subdir; /* only used if this is a directory */
53 struct entry *last_in_subdir;
54 ino_t inode; /* This is inode # */
55 dev_t dev; /* This is an internal identification of the extfs archive */
56 struct archive *archive; /* And this is an archive structure */
57 dev_t rdev;
58 mode_t mode;
59 uid_t uid;
60 gid_t gid;
61 int size;
62 time_t mtime;
63 char linkflag;
64 char *linkname;
65 time_t atime;
66 time_t ctime;
67 char *local_filename;
70 struct entry {
71 struct entry *next_in_dir;
72 struct entry *dir;
73 char *name;
74 struct inode *inode;
77 struct pseudofile {
78 struct archive *archive;
79 unsigned int has_changed:1;
80 int local_handle;
81 struct entry *entry;
84 static struct entry *
85 find_entry (struct entry *dir, char *name, int make_dirs, int make_file);
86 static int extfs_which (vfs *me, char *path);
87 static void remove_entry (struct entry *e);
89 static struct archive *first_archive = NULL;
90 static int my_errno = 0;
91 static struct stat hstat; /* Stat struct corresponding */
93 #define MAXEXTFS 32
94 static char *extfs_prefixes [MAXEXTFS];
95 static char extfs_need_archive [MAXEXTFS];
96 static int extfs_no = 0;
98 static void extfs_fill_names (vfs *me, void (*func)(char *))
100 struct archive *a = first_archive;
101 char *name;
103 while (a){
104 name = g_strconcat (a->name ? a->name : "",
105 "#", extfs_prefixes [a->fstype],
106 PATH_SEP_STR, a->current_dir->name, NULL);
107 (*func)(name);
108 g_free (name);
109 a = a->next;
113 static void make_dot_doubledot (struct entry *ent)
115 struct entry *entry = g_new (struct entry, 1);
116 struct entry *parentry = ent->dir;
117 struct inode *inode = ent->inode, *parent;
119 parent = (parentry != NULL) ? parentry->inode : NULL;
120 entry->name = g_strdup (".");
121 entry->inode = inode;
122 entry->dir = ent;
123 inode->local_filename = NULL;
124 inode->first_in_subdir = entry;
125 inode->last_in_subdir = entry;
126 inode->nlink++;
127 entry->next_in_dir = g_new (struct entry, 1);
128 entry=entry->next_in_dir;
129 entry->name = g_strdup ("..");
130 inode->last_in_subdir = entry;
131 entry->next_in_dir = NULL;
132 if (parent != NULL) {
133 entry->inode = parent;
134 entry->dir = parentry;
135 parent->nlink++;
136 } else {
137 entry->inode = inode;
138 entry->dir = ent;
139 inode->nlink++;
143 static struct entry *generate_entry (struct archive *archive,
144 char *name, struct entry *parentry, mode_t mode)
146 mode_t myumask;
147 struct inode *inode, *parent;
148 struct entry *entry;
150 parent = (parentry != NULL) ? parentry->inode : NULL;
151 entry = g_new (struct entry, 1);
153 entry->name = g_strdup (name);
154 entry->next_in_dir = NULL;
155 entry->dir = parentry;
156 if (parent != NULL) {
157 parent->last_in_subdir->next_in_dir = entry;
158 parent->last_in_subdir = entry;
160 inode = g_new (struct inode, 1);
161 entry->inode = inode;
162 inode->local_filename = NULL;
163 inode->linkname = 0;
164 inode->inode = (archive->__inode_counter)++;
165 inode->dev = archive->rdev;
166 inode->archive = archive;
167 myumask = umask (022);
168 umask (myumask);
169 inode->mode = mode & ~myumask;
170 mode = inode->mode;
171 inode->rdev = 0;
172 inode->uid = getuid ();
173 inode->gid = getgid ();
174 inode->size = 0;
175 inode->mtime = time (NULL);
176 inode->atime = inode->mtime;
177 inode->ctime = inode->mtime;
178 inode->nlink = 1;
179 if (S_ISDIR (mode))
180 make_dot_doubledot (entry);
181 return entry;
184 static void free_entries (struct entry *entry)
186 return;
189 static void free_archive (struct archive *archive)
191 free_entries (archive->root_entry);
192 if (archive->local_name != NULL) {
193 struct stat my;
195 mc_stat (archive->local_name, &my);
196 mc_ungetlocalcopy (archive->name, archive->local_name,
197 archive->local_stat.st_mtime != my.st_mtime);
198 /* ungetlocalcopy frees local_name for us */
200 if (archive->name)
201 g_free (archive->name);
202 g_free (archive);
205 static FILE *open_archive (int fstype, char *name, struct archive **pparc)
207 static dev_t __extfs_no = 0;
208 FILE *result;
209 mode_t mode;
210 char *cmd;
211 char *mc_extfsdir;
212 struct stat mystat;
213 struct archive *current_archive;
214 struct entry *root_entry;
215 char *local_name = NULL, *tmp = 0;
216 int uses_archive = extfs_need_archive [fstype];
218 if (uses_archive){
219 if (mc_stat (name, &mystat) == -1)
220 return NULL;
221 if (!vfs_file_is_local (name)) {
222 local_name = mc_getlocalcopy (name);
223 if (local_name == NULL)
224 return NULL;
226 tmp = name_quote (name, 0);
229 #if 0
230 /* Sorry, what is this good for? */
231 if (uses_archive == EFS_NEED_ARG){
232 tmp = name_quote (name, 0);
234 #endif
236 mc_extfsdir = concat_dir_and_file (mc_home, "extfs/");
237 cmd = g_strconcat (mc_extfsdir, extfs_prefixes [fstype],
238 " list ", local_name ? local_name : tmp, NULL);
239 if (tmp)
240 g_free (tmp);
241 g_free (mc_extfsdir);
242 result = popen (cmd, "r");
243 g_free (cmd);
244 if (result == NULL) {
245 if (local_name != NULL && uses_archive)
246 mc_ungetlocalcopy (name, local_name, 0);
247 return NULL;
250 current_archive = g_new (struct archive, 1);
251 current_archive->fstype = fstype;
252 current_archive->name = name ? g_strdup (name): name;
253 current_archive->local_name = local_name;
255 if (local_name != NULL)
256 mc_stat (local_name, &current_archive->local_stat);
257 current_archive->__inode_counter = 0;
258 current_archive->fd_usage = 0;
259 current_archive->extfsstat = mystat;
260 current_archive->rdev = __extfs_no++;
261 current_archive->next = first_archive;
262 first_archive = current_archive;
263 mode = current_archive->extfsstat.st_mode & 07777;
264 if (mode & 0400)
265 mode |= 0100;
266 if (mode & 0040)
267 mode |= 0010;
268 if (mode & 0004)
269 mode |= 0001;
270 mode |= S_IFDIR;
271 root_entry = generate_entry (current_archive, "/", NULL, mode);
272 root_entry->inode->uid = current_archive->extfsstat.st_uid;
273 root_entry->inode->gid = current_archive->extfsstat.st_gid;
274 root_entry->inode->atime = current_archive->extfsstat.st_atime;
275 root_entry->inode->ctime = current_archive->extfsstat.st_ctime;
276 root_entry->inode->mtime = current_archive->extfsstat.st_mtime;
277 current_archive->root_entry = root_entry;
278 current_archive->current_dir = root_entry;
280 *pparc = current_archive;
282 return result;
286 * Main loop for reading an archive.
287 * Returns 0 on success, -1 on error.
289 static int read_archive (int fstype, char *name, struct archive **pparc)
291 FILE *extfsd;
292 char *buffer;
293 struct archive *current_archive;
294 char *current_file_name, *current_link_name;
297 if ((extfsd = open_archive (fstype, name, &current_archive)) == NULL) {
298 message_3s (1, MSG_ERROR, _("Couldn't open %s archive\n%s"),
299 extfs_prefixes [fstype], name);
300 return -1;
303 buffer = g_malloc (4096);
304 while (fgets (buffer, 4096, extfsd) != NULL) {
305 current_link_name = NULL;
306 if (vfs_parse_ls_lga (buffer, &hstat, &current_file_name, &current_link_name)) {
307 struct entry *entry, *pent;
308 struct inode *inode;
309 char *p, *q, *cfn = current_file_name;
311 if (*cfn) {
312 if (*cfn == '/')
313 cfn++;
314 p = strchr (cfn, 0);
315 if (p != cfn && *(p - 1) == '/')
316 *(p - 1) = 0;
317 p = strrchr (cfn, '/');
318 if (p == NULL) {
319 p = cfn;
320 q = strchr (cfn, 0);
321 } else {
322 *(p++) = 0;
323 q = cfn;
325 if (S_ISDIR (hstat.st_mode) &&
326 (!strcmp (p, ".") || !strcmp (p, "..")))
327 goto read_extfs_continue;
328 pent = find_entry (current_archive->root_entry, q, 1, 0) ;
329 if (pent == NULL) {
330 message_1s (1, MSG_ERROR, _("Inconsistent extfs archive"));
331 /* FIXME: Should clean everything one day */
332 g_free (buffer);
333 pclose (extfsd);
334 return -1;
336 entry = g_new (struct entry, 1);
337 entry->name = g_strdup (p);
338 entry->next_in_dir = NULL;
339 entry->dir = pent;
340 if (pent != NULL) {
341 if (pent->inode->last_in_subdir){
342 pent->inode->last_in_subdir->next_in_dir = entry;
343 pent->inode->last_in_subdir = entry;
346 if (!S_ISLNK (hstat.st_mode) && current_link_name != NULL) {
347 pent = find_entry (current_archive->root_entry, current_link_name, 0, 0);
348 if (pent == NULL) {
349 message_1s (1, MSG_ERROR, _("Inconsistent extfs archive"));
350 /* FIXME: Should clean everything one day */
351 g_free (buffer);
352 pclose (extfsd);
353 return -1;
354 } else {
355 entry->inode = pent->inode;
356 pent->inode->nlink++;
358 } else {
359 inode = g_new (struct inode, 1);
360 entry->inode = inode;
361 inode->local_filename = NULL;
362 inode->inode = (current_archive->__inode_counter)++;
363 inode->nlink = 1;
364 inode->dev = current_archive->rdev;
365 inode->archive = current_archive;
366 inode->mode = hstat.st_mode;
367 #ifdef HAVE_ST_RDEV
368 inode->rdev = hstat.st_rdev;
369 #else
370 inode->rdev = 0;
371 #endif
372 inode->uid = hstat.st_uid;
373 inode->gid = hstat.st_gid;
374 inode->size = hstat.st_size;
375 inode->mtime = hstat.st_mtime;
376 inode->atime = hstat.st_atime;
377 inode->ctime = hstat.st_ctime;
378 if (current_link_name != NULL && S_ISLNK (hstat.st_mode)) {
379 inode->linkname = current_link_name;
380 current_link_name = NULL;
381 } else {
382 if (S_ISLNK( hstat.st_mode))
383 inode->mode &= ~S_IFLNK; /* You *DON'T* want to do this always */
384 inode->linkname = NULL;
386 if (S_ISDIR (hstat.st_mode))
387 make_dot_doubledot (entry);
390 read_extfs_continue:
391 g_free (current_file_name);
392 if (current_link_name != NULL)
393 g_free (current_link_name);
396 pclose (extfsd);
397 #ifdef SCO_FLAVOR
398 waitpid(-1,NULL,WNOHANG);
399 #endif /* SCO_FLAVOR */
400 *pparc = current_archive;
401 g_free (buffer);
402 return 0;
405 static char *get_path (char *inname, struct archive **archive, int is_dir,
406 int do_not_open);
408 /* Returns path inside argument. Returned char* is inside inname, which is mangled
409 * by this operation (so you must not free it's return value)
411 static char *get_path_mangle (char *inname, struct archive **archive, int is_dir,
412 int do_not_open)
414 char *local, *archive_name, *op;
415 int result = -1;
416 struct archive *parc;
417 struct vfs_stamping *parent;
418 vfs *v;
419 int fstype;
421 archive_name = inname;
422 vfs_split( inname, &local, &op );
423 fstype = extfs_which( NULL, op ); /* FIXME: we really should pass
424 self pointer. But as we know that extfs_which does not touch vfs
425 *me, it does not matter for now */
426 if (fstype == -1)
427 return NULL;
428 if (!local)
429 local = "";
431 /* All filesystems should have some local archive, at least
432 * it can be '/'.
434 * Actually, we should implement an alias mechanism that would
435 * translate: "a:" to "dos:a.
438 for (parc = first_archive; parc != NULL; parc = parc->next)
439 if (parc->name) {
440 if (!strcmp (parc->name, archive_name)) {
441 struct stat *s=&(parc->extfsstat);
442 if (vfs_uid && (!(s->st_mode & 0004)))
443 if ((s->st_gid != vfs_gid) || !(s->st_mode & 0040))
444 if ((s->st_uid != vfs_uid) || !(s->st_mode & 0400))
445 return NULL;
446 /* This is not too secure - in some cases (/#mtools) files created
447 under user a are probably visible to everyone else since / usually
448 has permissions 755 */
449 vfs_stamp (&vfs_extfs_ops, (vfsid) parc);
450 goto return_success;
454 result = do_not_open ? -1 : read_archive (fstype, archive_name, &parc);
455 if (result == -1) ERRNOR (EIO, NULL);
457 if (archive_name){
458 v = vfs_type (archive_name);
459 if (v == &vfs_local_ops) {
460 parent = NULL;
461 } else {
462 parent = g_new (struct vfs_stamping, 1);
463 parent->v = v;
464 parent->next = 0;
465 parent->id = (*v->getid) (v, archive_name, &(parent->parent));
467 vfs_add_noncurrent_stamps (&vfs_extfs_ops, (vfsid) parc, parent);
468 vfs_rm_parents (parent);
470 return_success:
471 *archive = parc;
472 return local;
475 /* Returns allocated path (without leading slash) inside the archive */
476 static char *get_path_from_entry (struct entry *entry)
478 struct list {
479 struct list *next;
480 char *name;
481 } *head, *p;
482 char *localpath;
483 size_t len;
485 for (len = 0, head = 0; entry->dir; entry = entry->dir) {
486 p = g_new (struct list, 1);
487 p->next = head;
488 p->name = entry->name;
489 head = p;
490 len += strlen (entry->name) + 1;
493 if (len == 0)
494 return g_strdup ("");
496 localpath = g_malloc (len);
497 *localpath = '\0';
498 while (head) {
499 strcat (localpath, head->name);
500 if (head->next)
501 strcat (localpath, "/");
502 p = head;
503 head = head->next;
504 g_free (p);
506 return (localpath);
510 struct loop_protect {
511 struct entry *entry;
512 struct loop_protect *next;
514 static int errloop;
515 static int notadir;
517 static struct entry *
518 __find_entry (struct entry *dir, char *name,
519 struct loop_protect *list, int make_dirs, int make_file);
521 static struct entry *
522 __resolve_symlinks (struct entry *entry,
523 struct loop_protect *list)
525 struct entry *pent;
526 struct loop_protect *looping;
528 if (!S_ISLNK (entry->inode->mode))
529 return entry;
530 for (looping = list; looping != NULL; looping = looping->next)
531 if (entry == looping->entry) { /* Here we protect us against symlink looping */
532 errloop = 1;
533 return NULL;
535 looping = g_new (struct loop_protect, 1);
536 looping->entry = entry;
537 looping->next = list;
538 pent = __find_entry (entry->dir, entry->inode->linkname, looping, 0, 0);
539 g_free (looping);
540 if (pent == NULL)
541 my_errno = ENOENT;
542 return pent;
545 static struct entry *my_resolve_symlinks (struct entry *entry)
547 struct entry *res;
549 errloop = 0;
550 notadir = 0;
551 res = __resolve_symlinks (entry, NULL);
552 if (res == NULL) {
553 if (errloop)
554 my_errno = ELOOP;
555 else if (notadir)
556 my_errno = ENOTDIR;
558 return res;
561 static char *get_archive_name (struct archive *archive)
563 char *archive_name;
565 if (archive->local_name)
566 archive_name = archive->local_name;
567 else
568 archive_name = archive->name;
570 if (!archive_name || !*archive_name)
571 return "no_archive_name";
572 else
573 return archive_name;
576 static void extfs_run (char *file)
578 struct archive *archive;
579 char *p, *q, *archive_name, *mc_extfsdir;
580 char *cmd;
582 if ((p = get_path (file, &archive, 0, 0)) == NULL)
583 return;
584 q = name_quote (p, 0);
585 g_free (p);
587 archive_name = name_quote (get_archive_name(archive), 0);
588 mc_extfsdir = concat_dir_and_file (mc_home, "extfs/");
589 cmd = g_strconcat (mc_extfsdir, extfs_prefixes [archive->fstype],
590 " run ", archive_name, " ", q, NULL);
591 g_free (mc_extfsdir);
592 g_free (archive_name);
593 g_free (q);
594 #ifndef VFS_STANDALONE
595 shell_execute(cmd, 0);
596 #else
597 vfs_die( "shell_execute: implement me!" );
598 #endif
599 g_free(cmd);
602 static void *extfs_open (vfs *me, char *file, int flags, int mode)
604 struct pseudofile *extfs_info;
605 struct archive *archive;
606 char *q;
607 char *mc_extfsdir;
608 struct entry *entry;
609 int local_handle;
610 int created = 0;
612 if ((q = get_path_mangle (file, &archive, 0, 0)) == NULL)
613 return NULL;
614 entry = find_entry (archive->root_entry, q, 0, 0);
615 if (entry == NULL && (flags & O_CREAT)) {
616 /* Create new entry */
617 entry = find_entry (archive->root_entry, q, 0, 1);
618 created = (entry != NULL);
620 if (entry == NULL)
621 return NULL;
622 if ((entry = my_resolve_symlinks (entry)) == NULL)
623 return NULL;
624 if (S_ISDIR (entry->inode->mode)) ERRNOR (EISDIR, NULL);
625 if (entry->inode->local_filename == NULL) {
626 char *cmd;
627 char *archive_name, *p;
630 int handle;
631 handle = mc_mkstemps (&entry->inode->local_filename, "extfs", NULL);
633 if (handle == -1)
634 return NULL;
635 close(handle);
637 p = get_path_from_entry (entry);
638 q = name_quote (p, 0);
639 g_free (p);
640 archive_name = name_quote (get_archive_name (archive), 0);
642 mc_extfsdir = concat_dir_and_file (mc_home, "extfs/");
643 cmd = g_strconcat (mc_extfsdir, extfs_prefixes [archive->fstype],
644 " copyout ",
645 archive_name,
646 " ", q, " ", entry->inode->local_filename, NULL);
647 g_free (q);
648 g_free (mc_extfsdir);
649 g_free (archive_name);
650 if (my_system (EXECUTE_AS_SHELL | EXECUTE_SETUID | EXECUTE_WAIT, shell, cmd) && !created){
651 free (entry->inode->local_filename);
652 entry->inode->local_filename = NULL;
653 g_free (cmd);
654 my_errno = EIO;
655 return NULL;
657 g_free (cmd);
660 local_handle = open (entry->inode->local_filename, NO_LINEAR(flags),
661 mode);
662 if (local_handle == -1) ERRNOR (EIO, NULL);
664 extfs_info = g_new (struct pseudofile, 1);
665 extfs_info->archive = archive;
666 extfs_info->entry = entry;
667 extfs_info->has_changed = created;
668 extfs_info->local_handle = local_handle;
670 /* i.e. we had no open files and now we have one */
671 vfs_rmstamp (&vfs_extfs_ops, (vfsid) archive, 1);
672 archive->fd_usage++;
673 return extfs_info;
676 static int extfs_read (void *data, char *buffer, int count)
678 struct pseudofile *file = (struct pseudofile *)data;
680 return read (file->local_handle, buffer, count);
683 static int extfs_close (void *data)
685 struct pseudofile *file;
686 int errno_code = 0;
687 file = (struct pseudofile *)data;
689 close (file->local_handle);
691 /* Commit the file if it has changed */
692 if (file->has_changed){
693 struct archive *archive;
694 char *archive_name, *file_name;
695 char *cmd;
696 char *mc_extfsdir;
697 char *p;
699 archive = file->archive;
700 archive_name = name_quote (get_archive_name (archive), 0);
701 p = get_path_from_entry (file->entry);
702 file_name = name_quote (p, 0);
703 g_free (p);
705 mc_extfsdir = concat_dir_and_file (mc_home, "extfs/");
706 cmd = g_strconcat (mc_extfsdir,
707 extfs_prefixes [archive->fstype],
708 " copyin ", archive_name, " ",
709 file_name, " ",
710 file->entry->inode->local_filename, NULL);
711 g_free (archive_name);
712 g_free (file_name);
713 g_free (mc_extfsdir);
714 if (my_system (EXECUTE_AS_SHELL | EXECUTE_SETUID | EXECUTE_WAIT, shell, cmd))
715 errno_code = EIO;
716 g_free (cmd);
718 struct stat file_status;
719 if (stat(file->entry->inode->local_filename,&file_status) != 0)
720 errno_code = EIO;
721 else
722 file->entry->inode->size = file_status.st_size;
725 file->entry->inode->mtime = time (NULL);
728 file->archive->fd_usage--;
729 if (!file->archive->fd_usage) {
730 struct vfs_stamping *parent;
731 vfs *v;
733 if (!file->archive->name || !*file->archive->name || (v = vfs_type (file->archive->name)) == &vfs_local_ops) {
734 parent = NULL;
735 } else {
736 parent = g_new (struct vfs_stamping, 1);
737 parent->v = v;
738 parent->next = 0;
739 parent->id = (*v->getid) (v, file->archive->name, &(parent->parent));
741 vfs_add_noncurrent_stamps (&vfs_extfs_ops, (vfsid) (file->archive), parent);
742 vfs_rm_parents (parent);
745 g_free (data);
746 if (errno_code) ERRNOR (EIO, -1);
747 return 0;
750 #define RECORDSIZE 512
752 static char *get_path (char *inname, struct archive **archive, int is_dir,
753 int do_not_open)
755 char *buf = g_strdup (inname);
756 char *res = get_path_mangle( buf, archive, is_dir, do_not_open );
757 char *res2 = NULL;
758 if (res)
759 res2 = g_strdup(res);
760 g_free(buf);
761 return res2;
764 static struct entry*
765 __find_entry (struct entry *dir, char *name,
766 struct loop_protect *list, int make_dirs, int make_file)
768 struct entry *pent, *pdir;
769 char *p, *q, *name_end;
770 char c;
772 if (*name == '/') { /* Handle absolute paths */
773 name++;
774 dir = dir->inode->archive->root_entry;
777 pent = dir;
778 p = name;
779 name_end = name + strlen (name);
780 q = strchr (p, '/');
781 c = '/';
782 if (!q)
783 q = strchr (p, 0);
785 for (; pent != NULL && c && *p; ){
786 c = *q;
787 *q = 0;
789 if (strcmp (p, ".")){
790 if (!strcmp (p, ".."))
791 pent = pent->dir;
792 else {
793 if ((pent = __resolve_symlinks (pent, list))==NULL){
794 *q = c;
795 return NULL;
797 if (!S_ISDIR (pent->inode->mode)){
798 *q = c;
799 notadir = 1;
800 return NULL;
802 pdir = pent;
803 for (pent = pent->inode->first_in_subdir; pent; pent = pent->next_in_dir)
804 /* Hack: I keep the original semanthic unless
805 q+1 would break in the strchr */
806 if (!strcmp (pent->name, p)){
807 if (q + 1 > name_end){
808 *q = c;
809 notadir = !S_ISDIR (pent->inode->mode);
810 return pent;
812 break;
815 /* When we load archive, we create automagically
816 * non-existant directories
818 if (pent == NULL && make_dirs) {
819 pent = generate_entry (dir->inode->archive, p, pdir, S_IFDIR | 0777);
821 if (pent == NULL && make_file) {
822 pent = generate_entry (dir->inode->archive, p, pdir, 0777);
826 /* Next iteration */
827 *q = c;
828 p = q + 1;
829 q = strchr (p, '/');
830 if (!q)
831 q = strchr (p, 0);
833 if (pent == NULL)
834 my_errno = ENOENT;
835 return pent;
838 static struct entry *find_entry (struct entry *dir, char *name, int make_dirs, int make_file)
840 struct entry *res;
842 errloop = 0;
843 notadir = 0;
844 res = __find_entry (dir, name, NULL, make_dirs, make_file);
845 if (res == NULL) {
846 if (errloop)
847 my_errno = ELOOP;
848 else if (notadir)
849 my_errno = ENOTDIR;
851 return res;
855 static int s_errno (vfs *me)
857 return my_errno;
860 static void * s_opendir (vfs *me, char *dirname)
862 struct archive *archive;
863 char *q;
864 struct entry *entry;
865 struct entry **info;
867 if ((q = get_path_mangle (dirname, &archive, 1, 0)) == NULL)
868 return NULL;
869 entry = find_entry (archive->root_entry, q, 0, 0);
870 if (entry == NULL)
871 return NULL;
872 if ((entry = my_resolve_symlinks (entry)) == NULL)
873 return NULL;
874 if (!S_ISDIR (entry->inode->mode)) ERRNOR (ENOTDIR, NULL);
876 info = g_new (struct entry *, 2);
877 info[0] = entry->inode->first_in_subdir;
878 info[1] = entry->inode->first_in_subdir;
880 return info;
883 static void * s_readdir (void *data)
885 static struct {
886 struct dirent dir;
887 #ifdef NEED_EXTRA_DIRENT_BUFFER
888 char extra_buffer [MC_MAXPATHLEN];
889 #endif
890 } dir;
892 struct entry **info = (struct entry **) data;
894 if (!*info)
895 return NULL;
897 strcpy (&(dir.dir.d_name [0]), (*info)->name);
899 #ifndef DIRENT_LENGTH_COMPUTED
900 dir.d_namlen = strlen (dir.dir.d_name);
901 #endif
902 *info = (*info)->next_in_dir;
904 return (void *)&dir;
907 static int s_telldir (void *data)
909 struct entry **info = (struct entry **) data;
910 struct entry *cur;
911 int num = 0;
913 cur = info[1];
914 while (cur!=NULL) {
915 if (cur == info[0]) return num;
916 num++;
917 cur = cur->next_in_dir;
919 return -1;
922 static void s_seekdir (void *data, int offset)
924 struct entry **info = (struct entry **) data;
925 int i;
926 info[0] = info[1];
927 for (i=0; i<offset; i++)
928 s_readdir( data );
931 static int s_closedir (void *data)
933 g_free (data);
934 return 0;
937 static void stat_move( struct stat *buf, struct inode *inode )
939 buf->st_dev = inode->dev;
940 buf->st_ino = inode->inode;
941 buf->st_mode = inode->mode;
942 buf->st_nlink = inode->nlink;
943 buf->st_uid = inode->uid;
944 buf->st_gid = inode->gid;
945 #ifdef HAVE_ST_RDEV
946 buf->st_rdev = inode->rdev;
947 #endif
948 buf->st_size = inode->size;
949 #ifdef HAVE_ST_BLKSIZE
950 buf->st_blksize = RECORDSIZE;
951 #endif
952 #ifdef HAVE_ST_BLOCKS
953 buf->st_blocks = (inode->size + RECORDSIZE - 1) / RECORDSIZE;
954 #endif
955 buf->st_atime = inode->atime;
956 buf->st_mtime = inode->mtime;
957 buf->st_ctime = inode->ctime;
960 static int s_internal_stat (char *path, struct stat *buf, int resolve)
962 struct archive *archive;
963 char *q;
964 struct entry *entry;
965 struct inode *inode;
967 if ((q = get_path_mangle (path, &archive, 0, 0)) == NULL)
968 return -1;
969 entry = find_entry (archive->root_entry, q, 0, 0);
970 if (entry == NULL)
971 return -1;
972 if (resolve && (entry = my_resolve_symlinks (entry)) == NULL)
973 return -1;
974 inode = entry->inode;
975 stat_move( buf, inode );
976 return 0;
979 static int s_stat (vfs *me, char *path, struct stat *buf)
981 return s_internal_stat (path, buf, 1);
984 static int s_lstat (vfs *me, char *path, struct stat *buf)
986 return s_internal_stat (path, buf, 0);
989 static int s_fstat (void *data, struct stat *buf)
991 struct pseudofile *file = (struct pseudofile *)data;
992 struct inode *inode;
994 inode = file->entry->inode;
995 stat_move( buf, inode );
996 return 0;
999 static int s_readlink (vfs *me, char *path, char *buf, int size)
1001 struct archive *archive;
1002 char *q;
1003 int i;
1004 struct entry *entry;
1006 if ((q = get_path_mangle (path, &archive, 0, 0)) == NULL)
1007 return -1;
1008 entry = find_entry (archive->root_entry, q, 0, 0);
1009 if (entry == NULL)
1010 return -1;
1011 if (!S_ISLNK (entry->inode->mode)) ERRNOR (EINVAL, -1);
1012 if (size > (i = strlen (entry->inode->linkname))) {
1013 size = i;
1015 strncpy (buf, entry->inode->linkname, i);
1016 return i;
1019 static int extfs_chmod (vfs *me, char *path, int mode)
1021 return 0;
1024 static int extfs_write (void *data, char *buf, int nbyte)
1026 struct pseudofile *file = (struct pseudofile *)data;
1028 file->has_changed = 1;
1029 return write (file->local_handle, buf, nbyte);
1032 static int extfs_unlink (vfs *me, char *file)
1034 struct archive *archive;
1035 char *q;
1036 char *mc_extfsdir;
1037 struct entry *entry;
1038 char *cmd;
1039 char *archive_name, *p;
1041 if ((q = get_path_mangle (file, &archive, 0, 0)) == NULL)
1042 return -1;
1043 entry = find_entry (archive->root_entry, q, 0, 0);
1044 if (entry == NULL)
1045 return -1;
1046 if ((entry = my_resolve_symlinks (entry)) == NULL)
1047 return -1;
1048 if (S_ISDIR (entry->inode->mode)) ERRNOR (EISDIR, -1);
1050 p = get_path_from_entry (entry);
1051 q = name_quote (p, 0);
1052 g_free (p);
1053 archive_name = name_quote (get_archive_name (archive), 0);
1055 mc_extfsdir = concat_dir_and_file (mc_home, "extfs/");
1056 cmd = g_strconcat (mc_extfsdir, extfs_prefixes [archive->fstype],
1057 " rm ", archive_name, " ", q, NULL);
1058 g_free (q);
1059 g_free (mc_extfsdir);
1060 g_free (archive_name);
1061 if (my_system (EXECUTE_AS_SHELL | EXECUTE_SETUID | EXECUTE_WAIT, shell, cmd)){
1062 g_free (cmd);
1063 my_errno = EIO;
1064 return -1;
1066 g_free (cmd);
1067 remove_entry (entry);
1069 return 0;
1072 static int extfs_mkdir (vfs *me, char *path, mode_t mode)
1074 struct archive *archive;
1075 char *q;
1076 char *mc_extfsdir;
1077 struct entry *entry;
1078 char *cmd;
1079 char *archive_name, *p;
1081 if ((q = get_path_mangle (path, &archive, 0, 0)) == NULL)
1082 return -1;
1083 entry = find_entry (archive->root_entry, q, 0, 0);
1084 if (entry != NULL) ERRNOR (EEXIST, -1);
1085 entry = find_entry (archive->root_entry, q, 1, 0);
1086 if (entry == NULL)
1087 return -1;
1088 if ((entry = my_resolve_symlinks (entry)) == NULL)
1089 return -1;
1090 if (!S_ISDIR (entry->inode->mode)) ERRNOR (ENOTDIR, -1);
1092 p = get_path_from_entry (entry);
1093 q = name_quote (p, 0);
1094 g_free (p);
1095 archive_name = name_quote (get_archive_name (archive), 0);
1097 mc_extfsdir = concat_dir_and_file (mc_home, "extfs/");
1098 cmd = g_strconcat (mc_extfsdir, extfs_prefixes [archive->fstype],
1099 " mkdir ", archive_name, " ", q, NULL);
1100 g_free (q);
1101 g_free (mc_extfsdir);
1102 g_free (archive_name);
1103 if (my_system (EXECUTE_AS_SHELL | EXECUTE_SETUID | EXECUTE_WAIT, shell, cmd)){
1104 g_free (cmd);
1105 my_errno = EIO;
1106 remove_entry (entry);
1107 return -1;
1109 g_free (cmd);
1111 return 0;
1114 static int extfs_rmdir (vfs *me, char *path)
1116 struct archive *archive;
1117 char *q;
1118 char *mc_extfsdir;
1119 struct entry *entry;
1120 char *cmd;
1121 char *archive_name, *p;
1123 if ((q = get_path_mangle (path, &archive, 0, 0)) == NULL)
1124 return -1;
1125 entry = find_entry (archive->root_entry, q, 0, 0);
1126 if (entry == NULL)
1127 return -1;
1128 if ((entry = my_resolve_symlinks (entry)) == NULL)
1129 return -1;
1130 if (!S_ISDIR (entry->inode->mode)) ERRNOR (ENOTDIR, -1);
1132 p = get_path_from_entry (entry);
1133 q = name_quote (p, 0);
1134 g_free (p);
1135 archive_name = name_quote (get_archive_name (archive), 0);
1137 mc_extfsdir = concat_dir_and_file (mc_home, "extfs/");
1138 cmd = g_strconcat (mc_extfsdir, extfs_prefixes [archive->fstype],
1139 " rmdir ", archive_name, " ", q, NULL);
1140 g_free (q);
1141 g_free (mc_extfsdir);
1142 g_free (archive_name);
1143 if (my_system (EXECUTE_AS_SHELL | EXECUTE_SETUID | EXECUTE_WAIT, shell, cmd)){
1144 g_free (cmd);
1145 my_errno = EIO;
1146 return -1;
1148 g_free (cmd);
1149 remove_entry (entry);
1151 return 0;
1154 static int extfs_chdir (vfs *me, char *path)
1156 struct archive *archive;
1157 char *q;
1158 struct entry *entry;
1160 my_errno = ENOTDIR;
1161 if ((q = get_path_mangle (path, &archive, 1, 0)) == NULL)
1162 return -1;
1163 entry = find_entry (archive->root_entry, q, 0, 0);
1164 if (!entry)
1165 return -1;
1166 entry = my_resolve_symlinks (entry);
1167 if ((!entry) || (!S_ISDIR (entry->inode->mode)))
1168 return -1;
1169 entry->inode->archive->current_dir = entry;
1170 my_errno = 0;
1171 return 0;
1174 static int extfs_lseek (void *data, off_t offset, int whence)
1176 struct pseudofile *file = (struct pseudofile *) data;
1178 return lseek (file->local_handle, offset, whence);
1181 static vfsid extfs_getid (vfs *me, char *path, struct vfs_stamping **parent)
1183 struct archive *archive;
1184 vfs *v;
1185 vfsid id;
1186 struct vfs_stamping *par;
1187 char *p;
1189 *parent = NULL;
1190 if (!(p = get_path (path, &archive, 1, 1)))
1191 return (vfsid) -1;
1192 g_free(p);
1193 if (archive->name){
1194 v = vfs_type (archive->name);
1195 id = (*v->getid) (v, archive->name, &par);
1196 if (id != (vfsid)-1) {
1197 *parent = g_new (struct vfs_stamping, 1);
1198 (*parent)->v = v;
1199 (*parent)->id = id;
1200 (*parent)->parent = par;
1201 (*parent)->next = NULL;
1204 return (vfsid) archive;
1207 static int extfs_nothingisopen (vfsid id)
1209 if (((struct archive *)id)->fd_usage <= 0)
1210 return 1;
1211 return 0;
1214 static void remove_entry (struct entry *e)
1216 int i = --(e->inode->nlink);
1217 struct entry *pe, *ent, *prev;
1219 if (S_ISDIR (e->inode->mode) && e->inode->first_in_subdir != NULL) {
1220 struct entry *f = e->inode->first_in_subdir;
1221 e->inode->first_in_subdir = NULL;
1222 remove_entry (f);
1224 pe = e->dir;
1225 if (e == pe->inode->first_in_subdir)
1226 pe->inode->first_in_subdir = e->next_in_dir;
1228 prev = NULL;
1229 for (ent = pe->inode->first_in_subdir; ent && ent->next_in_dir;
1230 ent = ent->next_in_dir)
1231 if (e == ent->next_in_dir) {
1232 prev = ent;
1233 break;
1235 if (prev)
1236 prev->next_in_dir = e->next_in_dir;
1237 if (e == pe->inode->last_in_subdir)
1238 pe->inode->last_in_subdir = prev;
1240 if (i <= 0) {
1241 if (e->inode->local_filename != NULL) {
1242 unlink (e->inode->local_filename);
1243 free (e->inode->local_filename);
1245 if (e->inode->linkname != NULL)
1246 g_free (e->inode->linkname);
1247 g_free (e->inode);
1250 g_free (e->name);
1251 g_free (e);
1254 static void free_entry (struct entry *e)
1256 int i = --(e->inode->nlink);
1257 if (S_ISDIR (e->inode->mode) && e->inode->first_in_subdir != NULL) {
1258 struct entry *f = e->inode->first_in_subdir;
1260 e->inode->first_in_subdir = NULL;
1261 free_entry (f);
1263 if (i <= 0) {
1264 if (e->inode->local_filename != NULL) {
1265 unlink (e->inode->local_filename);
1266 free (e->inode->local_filename);
1268 if (e->inode->linkname != NULL)
1269 g_free (e->inode->linkname);
1270 g_free (e->inode);
1272 if (e->next_in_dir != NULL)
1273 free_entry (e->next_in_dir);
1274 g_free (e->name);
1275 g_free (e);
1278 static void extfs_free (vfsid id)
1280 struct archive *parc;
1281 struct archive *archive = (struct archive *)id;
1283 free_entry (archive->root_entry);
1284 if (archive == first_archive) {
1285 first_archive = archive->next;
1286 } else {
1287 for (parc = first_archive; parc != NULL; parc = parc->next)
1288 if (parc->next == archive)
1289 break;
1290 if (parc != NULL)
1291 parc->next = archive->next;
1293 free_archive (archive);
1296 static char *extfs_getlocalcopy (vfs *me, char *path)
1298 struct pseudofile *fp =
1299 (struct pseudofile *) extfs_open (me, path, O_RDONLY, 0);
1300 char *p;
1302 if (fp == NULL)
1303 return NULL;
1304 if (fp->entry->inode->local_filename == NULL) {
1305 extfs_close ((void *) fp);
1306 return NULL;
1308 p = g_strdup (fp->entry->inode->local_filename);
1309 fp->archive->fd_usage++;
1310 extfs_close ((void *) fp);
1311 return p;
1314 static int extfs_ungetlocalcopy (vfs *me, char *path, char *local, int has_changed)
1316 struct pseudofile *fp =
1317 (struct pseudofile *) extfs_open (me, path, O_RDONLY, 0);
1319 if (fp == NULL)
1320 return 0;
1321 if (!strcmp (fp->entry->inode->local_filename, local)) {
1322 fp->archive->fd_usage--;
1323 fp->has_changed |= has_changed;
1324 extfs_close ((void *) fp);
1325 return 0;
1326 } else {
1327 /* Should not happen */
1328 extfs_close ((void *) fp);
1329 return mc_def_ungetlocalcopy (me, path, local, has_changed);
1334 #include "../src/profile.h"
1335 static int extfs_init (vfs *me)
1337 FILE *cfg;
1338 char *mc_extfsini;
1340 mc_extfsini = concat_dir_and_file (mc_home, "extfs/extfs.ini");
1341 cfg = fopen (mc_extfsini, "r");
1343 if (!cfg) {
1344 fprintf(stderr, _("Warning: file %s not found\n"), mc_extfsini);
1345 g_free (mc_extfsini);
1346 return 0;
1349 extfs_no = 0;
1350 while ( extfs_no < MAXEXTFS ) {
1351 char key[256];
1352 char *c;
1354 if (!fgets( key, sizeof (key)-1, cfg ))
1355 break;
1357 /* Handle those with a trailing ':', those flag that the
1358 * file system does not require an archive to work
1361 if (*key == '[') {
1362 /* We may not use vfs_die() message or message_1s or similar,
1363 * UI is not initialized at this time and message would not
1364 * appear on screen. */
1365 fprintf(stderr, "Warning: You need to update your %s file.\n",
1366 mc_extfsini);
1367 fclose(cfg);
1368 g_free (mc_extfsini);
1369 return 0;
1371 if (*key == '#')
1372 continue;
1374 if ((c = strchr (key, '\n'))){
1375 *c = 0;
1376 c = &key [strlen (key) - 1];
1377 } else {
1378 c = key;
1380 extfs_need_archive [extfs_no] = !(*c == ':');
1381 if (*c == ':')
1382 *c = 0;
1383 if (!(*key))
1384 continue;
1386 extfs_prefixes [extfs_no] = g_strdup (key);
1387 extfs_no++;
1389 fclose(cfg);
1390 g_free (mc_extfsini);
1391 return 1;
1394 /* Do NOT use me argument in this function */
1395 static int extfs_which (vfs *me, char *path)
1397 int i;
1399 for (i = 0; i < extfs_no; i++)
1400 if (!strcmp (path, extfs_prefixes [i]))
1401 return i;
1402 return -1;
1405 static void extfs_done (vfs *me)
1407 int i;
1409 for (i = 0; i < extfs_no; i++ )
1410 g_free (extfs_prefixes [i]);
1411 extfs_no = 0;
1414 static int extfs_setctl (vfs *me, char *path, int ctlop, char *arg)
1416 if (ctlop == MCCTL_EXTFS_RUN) {
1417 extfs_run (path);
1418 return 1;
1420 return 0;
1423 vfs vfs_extfs_ops = {
1424 NULL, /* This is place of next pointer */
1425 "extfs",
1426 F_EXEC, /* flags */
1427 NULL, /* prefix */
1428 NULL, /* data */
1429 0, /* errno */
1430 extfs_init,
1431 extfs_done,
1432 extfs_fill_names,
1433 extfs_which,
1435 extfs_open,
1436 extfs_close,
1437 extfs_read,
1438 extfs_write,
1440 s_opendir,
1441 s_readdir,
1442 s_closedir,
1443 s_telldir,
1444 s_seekdir,
1446 s_stat,
1447 s_lstat,
1448 s_fstat,
1450 extfs_chmod, /* chmod ... strange, returns success? */
1451 NULL,
1452 NULL,
1454 s_readlink,
1456 NULL, /* symlink */
1457 NULL,
1458 extfs_unlink,
1460 NULL,
1461 extfs_chdir,
1462 s_errno,
1463 extfs_lseek,
1464 NULL,
1466 extfs_getid,
1467 extfs_nothingisopen,
1468 extfs_free,
1470 extfs_getlocalcopy,
1471 extfs_ungetlocalcopy,
1473 extfs_mkdir, /* mkdir */
1474 extfs_rmdir,
1475 NULL,
1476 extfs_setctl
1478 MMAPNULL