mc.sh & mc.csh creation fixed...
[midnight-commander.git] / vfs / extfs.c
blob60a98197bb9ad3c81b3db0112a41eee06459c4fb
1 /* Virtual File System: External file system.
2 Copyright (C) 1995 The Free Software Foundation
4 Written by: 1995 Jakub Jelinek
5 Rewritten by: 1998 Pavel Machek
6 Additional changes by: 1999 Andrew T. Veliath
8 This program is free software; you can redistribute it and/or
9 modify it under the terms of the GNU Library General Public License
10 as published by the Free Software Foundation; either version 2 of
11 the License, or (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU Library General Public License for more details.
18 You should have received a copy of the GNU Library General Public
19 License along with this program; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
22 /* Namespace: exports only vfs_extfs_ops */
24 #include <config.h>
25 #include <stdio.h>
26 #include <ctype.h>
27 #include <string.h>
28 #include <stdlib.h>
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 #include <unistd.h>
32 #include <signal.h>
33 #ifdef HAVE_SYS_WAIT_H
34 #include <sys/wait.h>
35 #endif
36 #include <errno.h>
37 #include "utilvfs.h"
38 #include "../src/dialog.h"
39 #include "../src/main.h" /* For shell_execute */
40 #include "xdirentry.h"
41 #include "vfs.h"
42 #include "extfs.h"
44 #undef ERRNOR
45 #define ERRNOR(x,y) do { my_errno = x; return y; } while(0)
47 struct inode {
48 nlink_t nlink;
49 struct entry *first_in_subdir; /* only used if this is a directory */
50 struct entry *last_in_subdir;
51 ino_t inode; /* This is inode # */
52 dev_t dev; /* This is an internal identification of the extfs archive */
53 struct archive *archive; /* And this is an archive structure */
54 dev_t rdev;
55 mode_t mode;
56 uid_t uid;
57 gid_t gid;
58 int size;
59 time_t mtime;
60 char linkflag;
61 char *linkname;
62 time_t atime;
63 time_t ctime;
64 char *local_filename;
67 struct entry {
68 struct entry *next_in_dir;
69 struct entry *dir;
70 char *name;
71 struct inode *inode;
74 struct pseudofile {
75 struct archive *archive;
76 unsigned int has_changed:1;
77 int local_handle;
78 struct entry *entry;
81 static struct entry *
82 find_entry (struct entry *dir, char *name, int make_dirs, int make_file);
83 static int extfs_which (vfs *me, char *path);
84 static void remove_entry (struct entry *e);
86 static struct archive *first_archive = NULL;
87 static int my_errno = 0;
88 static struct stat hstat; /* Stat struct corresponding */
90 #define MAXEXTFS 32
91 static char *extfs_prefixes [MAXEXTFS];
92 static char extfs_need_archive [MAXEXTFS];
93 static int extfs_no = 0;
95 static void extfs_fill_names (vfs *me, void (*func)(char *))
97 struct archive *a = first_archive;
98 char *name;
100 while (a){
101 name = g_strconcat (a->name ? a->name : "",
102 "#", extfs_prefixes [a->fstype],
103 PATH_SEP_STR, a->current_dir->name, NULL);
104 (*func)(name);
105 g_free (name);
106 a = a->next;
110 static void make_dot_doubledot (struct entry *ent)
112 struct entry *entry = g_new (struct entry, 1);
113 struct entry *parentry = ent->dir;
114 struct inode *inode = ent->inode, *parent;
116 parent = (parentry != NULL) ? parentry->inode : NULL;
117 entry->name = g_strdup (".");
118 entry->inode = inode;
119 entry->dir = ent;
120 inode->local_filename = NULL;
121 inode->first_in_subdir = entry;
122 inode->last_in_subdir = entry;
123 inode->nlink++;
124 entry->next_in_dir = g_new (struct entry, 1);
125 entry=entry->next_in_dir;
126 entry->name = g_strdup ("..");
127 inode->last_in_subdir = entry;
128 entry->next_in_dir = NULL;
129 if (parent != NULL) {
130 entry->inode = parent;
131 entry->dir = parentry;
132 parent->nlink++;
133 } else {
134 entry->inode = inode;
135 entry->dir = ent;
136 inode->nlink++;
140 static struct entry *generate_entry (struct archive *archive,
141 char *name, struct entry *parentry, mode_t mode)
143 mode_t myumask;
144 struct inode *inode, *parent;
145 struct entry *entry;
147 parent = (parentry != NULL) ? parentry->inode : NULL;
148 entry = g_new (struct entry, 1);
150 entry->name = g_strdup (name);
151 entry->next_in_dir = NULL;
152 entry->dir = parentry;
153 if (parent != NULL) {
154 parent->last_in_subdir->next_in_dir = entry;
155 parent->last_in_subdir = entry;
157 inode = g_new (struct inode, 1);
158 entry->inode = inode;
159 inode->local_filename = NULL;
160 inode->linkname = 0;
161 inode->inode = (archive->__inode_counter)++;
162 inode->dev = archive->rdev;
163 inode->archive = archive;
164 myumask = umask (022);
165 umask (myumask);
166 inode->mode = mode & ~myumask;
167 mode = inode->mode;
168 inode->rdev = 0;
169 inode->uid = getuid ();
170 inode->gid = getgid ();
171 inode->size = 0;
172 inode->mtime = time (NULL);
173 inode->atime = inode->mtime;
174 inode->ctime = inode->mtime;
175 inode->nlink = 1;
176 if (S_ISDIR (mode))
177 make_dot_doubledot (entry);
178 return entry;
181 static void free_entries (struct entry *entry)
183 return;
186 static void free_archive (struct archive *archive)
188 free_entries (archive->root_entry);
189 if (archive->local_name != NULL) {
190 struct stat my;
192 mc_stat (archive->local_name, &my);
193 mc_ungetlocalcopy (archive->name, archive->local_name,
194 archive->local_stat.st_mtime != my.st_mtime);
195 /* ungetlocalcopy frees local_name for us */
197 if (archive->name)
198 g_free (archive->name);
199 g_free (archive);
202 static FILE *open_archive (int fstype, char *name, struct archive **pparc)
204 static dev_t __extfs_no = 0;
205 FILE *result;
206 mode_t mode;
207 char *cmd;
208 char *mc_extfsdir;
209 struct stat mystat;
210 struct archive *current_archive;
211 struct entry *root_entry;
212 char *local_name = NULL, *tmp = 0;
213 int uses_archive = extfs_need_archive [fstype];
215 if (uses_archive){
216 if (mc_stat (name, &mystat) == -1)
217 return NULL;
218 if (!vfs_file_is_local (name)) {
219 local_name = mc_getlocalcopy (name);
220 if (local_name == NULL)
221 return NULL;
223 tmp = name_quote (name, 0);
226 mc_extfsdir = concat_dir_and_file (mc_home, "extfs/");
227 cmd = g_strconcat (mc_extfsdir, extfs_prefixes [fstype],
228 " list ", local_name ? local_name : tmp, NULL);
229 if (tmp)
230 g_free (tmp);
231 g_free (mc_extfsdir);
232 result = popen (cmd, "r");
233 g_free (cmd);
234 if (result == NULL) {
235 if (local_name != NULL && uses_archive)
236 mc_ungetlocalcopy (name, local_name, 0);
237 return NULL;
240 current_archive = g_new (struct archive, 1);
241 current_archive->fstype = fstype;
242 current_archive->name = name ? g_strdup (name): name;
243 current_archive->local_name = local_name;
245 if (local_name != NULL)
246 mc_stat (local_name, &current_archive->local_stat);
247 current_archive->__inode_counter = 0;
248 current_archive->fd_usage = 0;
249 current_archive->extfsstat = mystat;
250 current_archive->rdev = __extfs_no++;
251 current_archive->next = first_archive;
252 first_archive = current_archive;
253 mode = current_archive->extfsstat.st_mode & 07777;
254 if (mode & 0400)
255 mode |= 0100;
256 if (mode & 0040)
257 mode |= 0010;
258 if (mode & 0004)
259 mode |= 0001;
260 mode |= S_IFDIR;
261 root_entry = generate_entry (current_archive, "/", NULL, mode);
262 root_entry->inode->uid = current_archive->extfsstat.st_uid;
263 root_entry->inode->gid = current_archive->extfsstat.st_gid;
264 root_entry->inode->atime = current_archive->extfsstat.st_atime;
265 root_entry->inode->ctime = current_archive->extfsstat.st_ctime;
266 root_entry->inode->mtime = current_archive->extfsstat.st_mtime;
267 current_archive->root_entry = root_entry;
268 current_archive->current_dir = root_entry;
270 *pparc = current_archive;
272 return result;
276 * Main loop for reading an archive.
277 * Returns 0 on success, -1 on error.
279 static int read_archive (int fstype, char *name, struct archive **pparc)
281 FILE *extfsd;
282 char *buffer;
283 struct archive *current_archive;
284 char *current_file_name, *current_link_name;
287 if ((extfsd = open_archive (fstype, name, &current_archive)) == NULL) {
288 message_3s (1, MSG_ERROR, _("Couldn't open %s archive\n%s"),
289 extfs_prefixes [fstype], name);
290 return -1;
293 buffer = g_malloc (4096);
294 while (fgets (buffer, 4096, extfsd) != NULL) {
295 current_link_name = NULL;
296 if (vfs_parse_ls_lga (buffer, &hstat, &current_file_name, &current_link_name)) {
297 struct entry *entry, *pent;
298 struct inode *inode;
299 char *p, *q, *cfn = current_file_name;
301 if (*cfn) {
302 if (*cfn == '/')
303 cfn++;
304 p = strchr (cfn, 0);
305 if (p != cfn && *(p - 1) == '/')
306 *(p - 1) = 0;
307 p = strrchr (cfn, '/');
308 if (p == NULL) {
309 p = cfn;
310 q = strchr (cfn, 0);
311 } else {
312 *(p++) = 0;
313 q = cfn;
315 if (S_ISDIR (hstat.st_mode) &&
316 (!strcmp (p, ".") || !strcmp (p, "..")))
317 goto read_extfs_continue;
318 pent = find_entry (current_archive->root_entry, q, 1, 0) ;
319 if (pent == NULL) {
320 message_1s (1, MSG_ERROR, _("Inconsistent extfs archive"));
321 /* FIXME: Should clean everything one day */
322 g_free (buffer);
323 pclose (extfsd);
324 return -1;
326 entry = g_new (struct entry, 1);
327 entry->name = g_strdup (p);
328 entry->next_in_dir = NULL;
329 entry->dir = pent;
330 if (pent != NULL) {
331 if (pent->inode->last_in_subdir){
332 pent->inode->last_in_subdir->next_in_dir = entry;
333 pent->inode->last_in_subdir = entry;
336 if (!S_ISLNK (hstat.st_mode) && current_link_name != NULL) {
337 pent = find_entry (current_archive->root_entry, current_link_name, 0, 0);
338 if (pent == NULL) {
339 message_1s (1, MSG_ERROR, _("Inconsistent extfs archive"));
340 /* FIXME: Should clean everything one day */
341 g_free (buffer);
342 pclose (extfsd);
343 return -1;
344 } else {
345 entry->inode = pent->inode;
346 pent->inode->nlink++;
348 } else {
349 inode = g_new (struct inode, 1);
350 entry->inode = inode;
351 inode->local_filename = NULL;
352 inode->inode = (current_archive->__inode_counter)++;
353 inode->nlink = 1;
354 inode->dev = current_archive->rdev;
355 inode->archive = current_archive;
356 inode->mode = hstat.st_mode;
357 #ifdef HAVE_ST_RDEV
358 inode->rdev = hstat.st_rdev;
359 #else
360 inode->rdev = 0;
361 #endif
362 inode->uid = hstat.st_uid;
363 inode->gid = hstat.st_gid;
364 inode->size = hstat.st_size;
365 inode->mtime = hstat.st_mtime;
366 inode->atime = hstat.st_atime;
367 inode->ctime = hstat.st_ctime;
368 if (current_link_name != NULL && S_ISLNK (hstat.st_mode)) {
369 inode->linkname = current_link_name;
370 current_link_name = NULL;
371 } else {
372 if (S_ISLNK( hstat.st_mode))
373 inode->mode &= ~S_IFLNK; /* You *DON'T* want to do this always */
374 inode->linkname = NULL;
376 if (S_ISDIR (hstat.st_mode))
377 make_dot_doubledot (entry);
380 read_extfs_continue:
381 g_free (current_file_name);
382 if (current_link_name != NULL)
383 g_free (current_link_name);
386 pclose (extfsd);
387 #ifdef SCO_FLAVOR
388 waitpid(-1,NULL,WNOHANG);
389 #endif /* SCO_FLAVOR */
390 *pparc = current_archive;
391 g_free (buffer);
392 return 0;
395 static char *get_path (char *inname, struct archive **archive, int is_dir,
396 int do_not_open);
398 /* Returns path inside argument. Returned char* is inside inname, which is mangled
399 * by this operation (so you must not free it's return value)
401 static char *get_path_mangle (char *inname, struct archive **archive, int is_dir,
402 int do_not_open)
404 char *local, *archive_name, *op;
405 int result = -1;
406 struct archive *parc;
407 struct vfs_stamping *parent;
408 vfs *v;
409 int fstype;
411 archive_name = inname;
412 vfs_split( inname, &local, &op );
413 fstype = extfs_which( NULL, op ); /* FIXME: we really should pass
414 self pointer. But as we know that extfs_which does not touch vfs
415 *me, it does not matter for now */
416 if (fstype == -1)
417 return NULL;
418 if (!local)
419 local = "";
421 /* All filesystems should have some local archive, at least
422 * it can be '/'.
424 * Actually, we should implement an alias mechanism that would
425 * translate: "a:" to "dos:a.
428 for (parc = first_archive; parc != NULL; parc = parc->next)
429 if (parc->name) {
430 if (!strcmp (parc->name, archive_name)) {
431 struct stat *s=&(parc->extfsstat);
432 if (vfs_uid && (!(s->st_mode & 0004)))
433 if ((s->st_gid != vfs_gid) || !(s->st_mode & 0040))
434 if ((s->st_uid != vfs_uid) || !(s->st_mode & 0400))
435 return NULL;
436 /* This is not too secure - in some cases (/#mtools) files created
437 under user a are probably visible to everyone else since / usually
438 has permissions 755 */
439 vfs_stamp (&vfs_extfs_ops, (vfsid) parc);
440 goto return_success;
444 result = do_not_open ? -1 : read_archive (fstype, archive_name, &parc);
445 if (result == -1) ERRNOR (EIO, NULL);
447 if (archive_name){
448 v = vfs_type (archive_name);
449 if (v == &vfs_local_ops) {
450 parent = NULL;
451 } else {
452 parent = g_new (struct vfs_stamping, 1);
453 parent->v = v;
454 parent->next = 0;
455 parent->id = (*v->getid) (v, archive_name, &(parent->parent));
457 vfs_add_noncurrent_stamps (&vfs_extfs_ops, (vfsid) parc, parent);
458 vfs_rm_parents (parent);
460 return_success:
461 *archive = parc;
462 return local;
465 /* Returns allocated path (without leading slash) inside the archive */
466 static char *get_path_from_entry (struct entry *entry)
468 struct list {
469 struct list *next;
470 char *name;
471 } *head, *p;
472 char *localpath;
473 size_t len;
475 for (len = 0, head = 0; entry->dir; entry = entry->dir) {
476 p = g_new (struct list, 1);
477 p->next = head;
478 p->name = entry->name;
479 head = p;
480 len += strlen (entry->name) + 1;
483 if (len == 0)
484 return g_strdup ("");
486 localpath = g_malloc (len);
487 *localpath = '\0';
488 while (head) {
489 strcat (localpath, head->name);
490 if (head->next)
491 strcat (localpath, "/");
492 p = head;
493 head = head->next;
494 g_free (p);
496 return (localpath);
500 struct loop_protect {
501 struct entry *entry;
502 struct loop_protect *next;
504 static int errloop;
505 static int notadir;
507 static struct entry *
508 __find_entry (struct entry *dir, char *name,
509 struct loop_protect *list, int make_dirs, int make_file);
511 static struct entry *
512 __resolve_symlinks (struct entry *entry,
513 struct loop_protect *list)
515 struct entry *pent;
516 struct loop_protect *looping;
518 if (!S_ISLNK (entry->inode->mode))
519 return entry;
520 for (looping = list; looping != NULL; looping = looping->next)
521 if (entry == looping->entry) { /* Here we protect us against symlink looping */
522 errloop = 1;
523 return NULL;
525 looping = g_new (struct loop_protect, 1);
526 looping->entry = entry;
527 looping->next = list;
528 pent = __find_entry (entry->dir, entry->inode->linkname, looping, 0, 0);
529 g_free (looping);
530 if (pent == NULL)
531 my_errno = ENOENT;
532 return pent;
535 static struct entry *my_resolve_symlinks (struct entry *entry)
537 struct entry *res;
539 errloop = 0;
540 notadir = 0;
541 res = __resolve_symlinks (entry, NULL);
542 if (res == NULL) {
543 if (errloop)
544 my_errno = ELOOP;
545 else if (notadir)
546 my_errno = ENOTDIR;
548 return res;
551 static char *get_archive_name (struct archive *archive)
553 char *archive_name;
555 if (archive->local_name)
556 archive_name = archive->local_name;
557 else
558 archive_name = archive->name;
560 if (!archive_name || !*archive_name)
561 return "no_archive_name";
562 else
563 return archive_name;
566 static void extfs_run (char *file)
568 struct archive *archive;
569 char *p, *q, *archive_name, *mc_extfsdir;
570 char *cmd;
572 if ((p = get_path (file, &archive, 0, 0)) == NULL)
573 return;
574 q = name_quote (p, 0);
575 g_free (p);
577 archive_name = name_quote (get_archive_name(archive), 0);
578 mc_extfsdir = concat_dir_and_file (mc_home, "extfs/");
579 cmd = g_strconcat (mc_extfsdir, extfs_prefixes [archive->fstype],
580 " run ", archive_name, " ", q, NULL);
581 g_free (mc_extfsdir);
582 g_free (archive_name);
583 g_free (q);
584 shell_execute(cmd, 0);
585 g_free(cmd);
588 static void *extfs_open (vfs *me, char *file, int flags, int mode)
590 struct pseudofile *extfs_info;
591 struct archive *archive;
592 char *q;
593 char *mc_extfsdir;
594 struct entry *entry;
595 int local_handle;
596 int created = 0;
598 if ((q = get_path_mangle (file, &archive, 0, 0)) == NULL)
599 return NULL;
600 entry = find_entry (archive->root_entry, q, 0, 0);
601 if (entry == NULL && (flags & O_CREAT)) {
602 /* Create new entry */
603 entry = find_entry (archive->root_entry, q, 0, 1);
604 created = (entry != NULL);
606 if (entry == NULL)
607 return NULL;
608 if ((entry = my_resolve_symlinks (entry)) == NULL)
609 return NULL;
610 if (S_ISDIR (entry->inode->mode)) ERRNOR (EISDIR, NULL);
611 if (entry->inode->local_filename == NULL) {
612 char *cmd;
613 char *archive_name, *p;
616 int handle;
617 handle = mc_mkstemps (&entry->inode->local_filename, "extfs", NULL);
619 if (handle == -1)
620 return NULL;
621 close(handle);
623 p = get_path_from_entry (entry);
624 q = name_quote (p, 0);
625 g_free (p);
626 archive_name = name_quote (get_archive_name (archive), 0);
628 mc_extfsdir = concat_dir_and_file (mc_home, "extfs/");
629 cmd = g_strconcat (mc_extfsdir, extfs_prefixes [archive->fstype],
630 " copyout ",
631 archive_name,
632 " ", q, " ", entry->inode->local_filename, NULL);
633 g_free (q);
634 g_free (mc_extfsdir);
635 g_free (archive_name);
636 if (my_system (EXECUTE_AS_SHELL, shell, cmd) && !created){
637 free (entry->inode->local_filename);
638 entry->inode->local_filename = NULL;
639 g_free (cmd);
640 my_errno = EIO;
641 return NULL;
643 g_free (cmd);
646 local_handle = open (entry->inode->local_filename, NO_LINEAR(flags),
647 mode);
648 if (local_handle == -1) ERRNOR (EIO, NULL);
650 extfs_info = g_new (struct pseudofile, 1);
651 extfs_info->archive = archive;
652 extfs_info->entry = entry;
653 extfs_info->has_changed = created;
654 extfs_info->local_handle = local_handle;
656 /* i.e. we had no open files and now we have one */
657 vfs_rmstamp (&vfs_extfs_ops, (vfsid) archive, 1);
658 archive->fd_usage++;
659 return extfs_info;
662 static int extfs_read (void *data, char *buffer, int count)
664 struct pseudofile *file = (struct pseudofile *)data;
666 return read (file->local_handle, buffer, count);
669 static int extfs_close (void *data)
671 struct pseudofile *file;
672 int errno_code = 0;
673 file = (struct pseudofile *)data;
675 close (file->local_handle);
677 /* Commit the file if it has changed */
678 if (file->has_changed){
679 struct archive *archive;
680 char *archive_name, *file_name;
681 char *cmd;
682 char *mc_extfsdir;
683 char *p;
685 archive = file->archive;
686 archive_name = name_quote (get_archive_name (archive), 0);
687 p = get_path_from_entry (file->entry);
688 file_name = name_quote (p, 0);
689 g_free (p);
691 mc_extfsdir = concat_dir_and_file (mc_home, "extfs/");
692 cmd = g_strconcat (mc_extfsdir,
693 extfs_prefixes [archive->fstype],
694 " copyin ", archive_name, " ",
695 file_name, " ",
696 file->entry->inode->local_filename, NULL);
697 g_free (archive_name);
698 g_free (file_name);
699 g_free (mc_extfsdir);
700 if (my_system (EXECUTE_AS_SHELL, shell, cmd))
701 errno_code = EIO;
702 g_free (cmd);
704 struct stat file_status;
705 if (stat(file->entry->inode->local_filename,&file_status) != 0)
706 errno_code = EIO;
707 else
708 file->entry->inode->size = file_status.st_size;
711 file->entry->inode->mtime = time (NULL);
714 file->archive->fd_usage--;
715 if (!file->archive->fd_usage) {
716 struct vfs_stamping *parent;
717 vfs *v;
719 if (!file->archive->name || !*file->archive->name || (v = vfs_type (file->archive->name)) == &vfs_local_ops) {
720 parent = NULL;
721 } else {
722 parent = g_new (struct vfs_stamping, 1);
723 parent->v = v;
724 parent->next = 0;
725 parent->id = (*v->getid) (v, file->archive->name, &(parent->parent));
727 vfs_add_noncurrent_stamps (&vfs_extfs_ops, (vfsid) (file->archive), parent);
728 vfs_rm_parents (parent);
731 g_free (data);
732 if (errno_code) ERRNOR (EIO, -1);
733 return 0;
736 #define RECORDSIZE 512
738 static char *get_path (char *inname, struct archive **archive, int is_dir,
739 int do_not_open)
741 char *buf = g_strdup (inname);
742 char *res = get_path_mangle( buf, archive, is_dir, do_not_open );
743 char *res2 = NULL;
744 if (res)
745 res2 = g_strdup(res);
746 g_free(buf);
747 return res2;
750 static struct entry*
751 __find_entry (struct entry *dir, char *name,
752 struct loop_protect *list, int make_dirs, int make_file)
754 struct entry *pent, *pdir;
755 char *p, *q, *name_end;
756 char c;
758 if (*name == '/') { /* Handle absolute paths */
759 name++;
760 dir = dir->inode->archive->root_entry;
763 pent = dir;
764 p = name;
765 name_end = name + strlen (name);
766 q = strchr (p, '/');
767 c = '/';
768 if (!q)
769 q = strchr (p, 0);
771 for (; pent != NULL && c && *p; ){
772 c = *q;
773 *q = 0;
775 if (strcmp (p, ".")){
776 if (!strcmp (p, ".."))
777 pent = pent->dir;
778 else {
779 if ((pent = __resolve_symlinks (pent, list))==NULL){
780 *q = c;
781 return NULL;
783 if (!S_ISDIR (pent->inode->mode)){
784 *q = c;
785 notadir = 1;
786 return NULL;
788 pdir = pent;
789 for (pent = pent->inode->first_in_subdir; pent; pent = pent->next_in_dir)
790 /* Hack: I keep the original semanthic unless
791 q+1 would break in the strchr */
792 if (!strcmp (pent->name, p)){
793 if (q + 1 > name_end){
794 *q = c;
795 notadir = !S_ISDIR (pent->inode->mode);
796 return pent;
798 break;
801 /* When we load archive, we create automagically
802 * non-existant directories
804 if (pent == NULL && make_dirs) {
805 pent = generate_entry (dir->inode->archive, p, pdir, S_IFDIR | 0777);
807 if (pent == NULL && make_file) {
808 pent = generate_entry (dir->inode->archive, p, pdir, 0777);
812 /* Next iteration */
813 *q = c;
814 p = q + 1;
815 q = strchr (p, '/');
816 if (!q)
817 q = strchr (p, 0);
819 if (pent == NULL)
820 my_errno = ENOENT;
821 return pent;
824 static struct entry *find_entry (struct entry *dir, char *name, int make_dirs, int make_file)
826 struct entry *res;
828 errloop = 0;
829 notadir = 0;
830 res = __find_entry (dir, name, NULL, make_dirs, make_file);
831 if (res == NULL) {
832 if (errloop)
833 my_errno = ELOOP;
834 else if (notadir)
835 my_errno = ENOTDIR;
837 return res;
841 static int s_errno (vfs *me)
843 return my_errno;
846 static void * s_opendir (vfs *me, char *dirname)
848 struct archive *archive;
849 char *q;
850 struct entry *entry;
851 struct entry **info;
853 if ((q = get_path_mangle (dirname, &archive, 1, 0)) == NULL)
854 return NULL;
855 entry = find_entry (archive->root_entry, q, 0, 0);
856 if (entry == NULL)
857 return NULL;
858 if ((entry = my_resolve_symlinks (entry)) == NULL)
859 return NULL;
860 if (!S_ISDIR (entry->inode->mode)) ERRNOR (ENOTDIR, NULL);
862 info = g_new (struct entry *, 2);
863 info[0] = entry->inode->first_in_subdir;
864 info[1] = entry->inode->first_in_subdir;
866 return info;
869 static void * s_readdir(void *data)
871 static union vfs_dirent dir;
872 struct entry **info = (struct entry **) data;
874 if (!*info)
875 return NULL;
877 strncpy(dir.dent.d_name, (*info)->name, MC_MAXPATHLEN);
878 dir.dent.d_name[MC_MAXPATHLEN] = 0;
880 compute_namelen(&dir.dent);
881 *info = (*info)->next_in_dir;
883 return (void *) &dir;
886 static int s_telldir (void *data)
888 struct entry **info = (struct entry **) data;
889 struct entry *cur;
890 int num = 0;
892 cur = info[1];
893 while (cur!=NULL) {
894 if (cur == info[0]) return num;
895 num++;
896 cur = cur->next_in_dir;
898 return -1;
901 static void s_seekdir (void *data, int offset)
903 struct entry **info = (struct entry **) data;
904 int i;
905 info[0] = info[1];
906 for (i=0; i<offset; i++)
907 s_readdir( data );
910 static int s_closedir (void *data)
912 g_free (data);
913 return 0;
916 static void stat_move( struct stat *buf, struct inode *inode )
918 buf->st_dev = inode->dev;
919 buf->st_ino = inode->inode;
920 buf->st_mode = inode->mode;
921 buf->st_nlink = inode->nlink;
922 buf->st_uid = inode->uid;
923 buf->st_gid = inode->gid;
924 #ifdef HAVE_ST_RDEV
925 buf->st_rdev = inode->rdev;
926 #endif
927 buf->st_size = inode->size;
928 #ifdef HAVE_ST_BLKSIZE
929 buf->st_blksize = RECORDSIZE;
930 #endif
931 #ifdef HAVE_ST_BLOCKS
932 buf->st_blocks = (inode->size + RECORDSIZE - 1) / RECORDSIZE;
933 #endif
934 buf->st_atime = inode->atime;
935 buf->st_mtime = inode->mtime;
936 buf->st_ctime = inode->ctime;
939 static int s_internal_stat (char *path, struct stat *buf, int resolve)
941 struct archive *archive;
942 char *q;
943 struct entry *entry;
944 struct inode *inode;
946 if ((q = get_path_mangle (path, &archive, 0, 0)) == NULL)
947 return -1;
948 entry = find_entry (archive->root_entry, q, 0, 0);
949 if (entry == NULL)
950 return -1;
951 if (resolve && (entry = my_resolve_symlinks (entry)) == NULL)
952 return -1;
953 inode = entry->inode;
954 stat_move( buf, inode );
955 return 0;
958 static int s_stat (vfs *me, char *path, struct stat *buf)
960 return s_internal_stat (path, buf, 1);
963 static int s_lstat (vfs *me, char *path, struct stat *buf)
965 return s_internal_stat (path, buf, 0);
968 static int s_fstat (void *data, struct stat *buf)
970 struct pseudofile *file = (struct pseudofile *)data;
971 struct inode *inode;
973 inode = file->entry->inode;
974 stat_move( buf, inode );
975 return 0;
978 static int s_readlink (vfs *me, char *path, char *buf, int size)
980 struct archive *archive;
981 char *q;
982 int i;
983 struct entry *entry;
985 if ((q = get_path_mangle (path, &archive, 0, 0)) == NULL)
986 return -1;
987 entry = find_entry (archive->root_entry, q, 0, 0);
988 if (entry == NULL)
989 return -1;
990 if (!S_ISLNK (entry->inode->mode)) ERRNOR (EINVAL, -1);
991 if (size > (i = strlen (entry->inode->linkname))) {
992 size = i;
994 strncpy (buf, entry->inode->linkname, i);
995 return i;
998 static int extfs_chmod (vfs *me, char *path, int mode)
1000 return 0;
1003 static int extfs_write (void *data, char *buf, int nbyte)
1005 struct pseudofile *file = (struct pseudofile *)data;
1007 file->has_changed = 1;
1008 return write (file->local_handle, buf, nbyte);
1011 static int extfs_unlink (vfs *me, char *file)
1013 struct archive *archive;
1014 char *q;
1015 char *mc_extfsdir;
1016 struct entry *entry;
1017 char *cmd;
1018 char *archive_name, *p;
1020 if ((q = get_path_mangle (file, &archive, 0, 0)) == NULL)
1021 return -1;
1022 entry = find_entry (archive->root_entry, q, 0, 0);
1023 if (entry == NULL)
1024 return -1;
1025 if ((entry = my_resolve_symlinks (entry)) == NULL)
1026 return -1;
1027 if (S_ISDIR (entry->inode->mode)) ERRNOR (EISDIR, -1);
1029 p = get_path_from_entry (entry);
1030 q = name_quote (p, 0);
1031 g_free (p);
1032 archive_name = name_quote (get_archive_name (archive), 0);
1034 mc_extfsdir = concat_dir_and_file (mc_home, "extfs/");
1035 cmd = g_strconcat (mc_extfsdir, extfs_prefixes [archive->fstype],
1036 " rm ", archive_name, " ", q, NULL);
1037 g_free (q);
1038 g_free (mc_extfsdir);
1039 g_free (archive_name);
1040 if (my_system (EXECUTE_AS_SHELL, shell, cmd)){
1041 g_free (cmd);
1042 my_errno = EIO;
1043 return -1;
1045 g_free (cmd);
1046 remove_entry (entry);
1048 return 0;
1051 static int extfs_mkdir (vfs *me, char *path, mode_t mode)
1053 struct archive *archive;
1054 char *q;
1055 char *mc_extfsdir;
1056 struct entry *entry;
1057 char *cmd;
1058 char *archive_name, *p;
1060 if ((q = get_path_mangle (path, &archive, 0, 0)) == NULL)
1061 return -1;
1062 entry = find_entry (archive->root_entry, q, 0, 0);
1063 if (entry != NULL) ERRNOR (EEXIST, -1);
1064 entry = find_entry (archive->root_entry, q, 1, 0);
1065 if (entry == NULL)
1066 return -1;
1067 if ((entry = my_resolve_symlinks (entry)) == NULL)
1068 return -1;
1069 if (!S_ISDIR (entry->inode->mode)) ERRNOR (ENOTDIR, -1);
1071 p = get_path_from_entry (entry);
1072 q = name_quote (p, 0);
1073 g_free (p);
1074 archive_name = name_quote (get_archive_name (archive), 0);
1076 mc_extfsdir = concat_dir_and_file (mc_home, "extfs/");
1077 cmd = g_strconcat (mc_extfsdir, extfs_prefixes [archive->fstype],
1078 " mkdir ", archive_name, " ", q, NULL);
1079 g_free (q);
1080 g_free (mc_extfsdir);
1081 g_free (archive_name);
1082 if (my_system (EXECUTE_AS_SHELL, shell, cmd)){
1083 g_free (cmd);
1084 my_errno = EIO;
1085 remove_entry (entry);
1086 return -1;
1088 g_free (cmd);
1090 return 0;
1093 static int extfs_rmdir (vfs *me, char *path)
1095 struct archive *archive;
1096 char *q;
1097 char *mc_extfsdir;
1098 struct entry *entry;
1099 char *cmd;
1100 char *archive_name, *p;
1102 if ((q = get_path_mangle (path, &archive, 0, 0)) == NULL)
1103 return -1;
1104 entry = find_entry (archive->root_entry, q, 0, 0);
1105 if (entry == NULL)
1106 return -1;
1107 if ((entry = my_resolve_symlinks (entry)) == NULL)
1108 return -1;
1109 if (!S_ISDIR (entry->inode->mode)) ERRNOR (ENOTDIR, -1);
1111 p = get_path_from_entry (entry);
1112 q = name_quote (p, 0);
1113 g_free (p);
1114 archive_name = name_quote (get_archive_name (archive), 0);
1116 mc_extfsdir = concat_dir_and_file (mc_home, "extfs/");
1117 cmd = g_strconcat (mc_extfsdir, extfs_prefixes [archive->fstype],
1118 " rmdir ", archive_name, " ", q, NULL);
1119 g_free (q);
1120 g_free (mc_extfsdir);
1121 g_free (archive_name);
1122 if (my_system (EXECUTE_AS_SHELL, shell, cmd)){
1123 g_free (cmd);
1124 my_errno = EIO;
1125 return -1;
1127 g_free (cmd);
1128 remove_entry (entry);
1130 return 0;
1133 static int extfs_chdir (vfs *me, char *path)
1135 struct archive *archive;
1136 char *q;
1137 struct entry *entry;
1139 my_errno = ENOTDIR;
1140 if ((q = get_path_mangle (path, &archive, 1, 0)) == NULL)
1141 return -1;
1142 entry = find_entry (archive->root_entry, q, 0, 0);
1143 if (!entry)
1144 return -1;
1145 entry = my_resolve_symlinks (entry);
1146 if ((!entry) || (!S_ISDIR (entry->inode->mode)))
1147 return -1;
1148 entry->inode->archive->current_dir = entry;
1149 my_errno = 0;
1150 return 0;
1153 static int extfs_lseek (void *data, off_t offset, int whence)
1155 struct pseudofile *file = (struct pseudofile *) data;
1157 return lseek (file->local_handle, offset, whence);
1160 static vfsid extfs_getid (vfs *me, char *path, struct vfs_stamping **parent)
1162 struct archive *archive;
1163 vfs *v;
1164 vfsid id;
1165 struct vfs_stamping *par;
1166 char *p;
1168 *parent = NULL;
1169 if (!(p = get_path (path, &archive, 1, 1)))
1170 return (vfsid) -1;
1171 g_free(p);
1172 if (archive->name){
1173 v = vfs_type (archive->name);
1174 id = (*v->getid) (v, archive->name, &par);
1175 if (id != (vfsid)-1) {
1176 *parent = g_new (struct vfs_stamping, 1);
1177 (*parent)->v = v;
1178 (*parent)->id = id;
1179 (*parent)->parent = par;
1180 (*parent)->next = NULL;
1183 return (vfsid) archive;
1186 static int extfs_nothingisopen (vfsid id)
1188 if (((struct archive *)id)->fd_usage <= 0)
1189 return 1;
1190 return 0;
1193 static void remove_entry (struct entry *e)
1195 int i = --(e->inode->nlink);
1196 struct entry *pe, *ent, *prev;
1198 if (S_ISDIR (e->inode->mode) && e->inode->first_in_subdir != NULL) {
1199 struct entry *f = e->inode->first_in_subdir;
1200 e->inode->first_in_subdir = NULL;
1201 remove_entry (f);
1203 pe = e->dir;
1204 if (e == pe->inode->first_in_subdir)
1205 pe->inode->first_in_subdir = e->next_in_dir;
1207 prev = NULL;
1208 for (ent = pe->inode->first_in_subdir; ent && ent->next_in_dir;
1209 ent = ent->next_in_dir)
1210 if (e == ent->next_in_dir) {
1211 prev = ent;
1212 break;
1214 if (prev)
1215 prev->next_in_dir = e->next_in_dir;
1216 if (e == pe->inode->last_in_subdir)
1217 pe->inode->last_in_subdir = prev;
1219 if (i <= 0) {
1220 if (e->inode->local_filename != NULL) {
1221 unlink (e->inode->local_filename);
1222 free (e->inode->local_filename);
1224 if (e->inode->linkname != NULL)
1225 g_free (e->inode->linkname);
1226 g_free (e->inode);
1229 g_free (e->name);
1230 g_free (e);
1233 static void free_entry (struct entry *e)
1235 int i = --(e->inode->nlink);
1236 if (S_ISDIR (e->inode->mode) && e->inode->first_in_subdir != NULL) {
1237 struct entry *f = e->inode->first_in_subdir;
1239 e->inode->first_in_subdir = NULL;
1240 free_entry (f);
1242 if (i <= 0) {
1243 if (e->inode->local_filename != NULL) {
1244 unlink (e->inode->local_filename);
1245 free (e->inode->local_filename);
1247 if (e->inode->linkname != NULL)
1248 g_free (e->inode->linkname);
1249 g_free (e->inode);
1251 if (e->next_in_dir != NULL)
1252 free_entry (e->next_in_dir);
1253 g_free (e->name);
1254 g_free (e);
1257 static void extfs_free (vfsid id)
1259 struct archive *parc;
1260 struct archive *archive = (struct archive *)id;
1262 free_entry (archive->root_entry);
1263 if (archive == first_archive) {
1264 first_archive = archive->next;
1265 } else {
1266 for (parc = first_archive; parc != NULL; parc = parc->next)
1267 if (parc->next == archive)
1268 break;
1269 if (parc != NULL)
1270 parc->next = archive->next;
1272 free_archive (archive);
1275 static char *extfs_getlocalcopy (vfs *me, char *path)
1277 struct pseudofile *fp =
1278 (struct pseudofile *) extfs_open (me, path, O_RDONLY, 0);
1279 char *p;
1281 if (fp == NULL)
1282 return NULL;
1283 if (fp->entry->inode->local_filename == NULL) {
1284 extfs_close ((void *) fp);
1285 return NULL;
1287 p = g_strdup (fp->entry->inode->local_filename);
1288 fp->archive->fd_usage++;
1289 extfs_close ((void *) fp);
1290 return p;
1293 static int extfs_ungetlocalcopy (vfs *me, char *path, char *local, int has_changed)
1295 struct pseudofile *fp =
1296 (struct pseudofile *) extfs_open (me, path, O_RDONLY, 0);
1298 if (fp == NULL)
1299 return 0;
1300 if (!strcmp (fp->entry->inode->local_filename, local)) {
1301 fp->archive->fd_usage--;
1302 fp->has_changed |= has_changed;
1303 extfs_close ((void *) fp);
1304 return 0;
1305 } else {
1306 /* Should not happen */
1307 extfs_close ((void *) fp);
1308 return mc_def_ungetlocalcopy (me, path, local, has_changed);
1313 #include "../src/profile.h"
1314 static int extfs_init (vfs *me)
1316 FILE *cfg;
1317 char *mc_extfsini;
1319 mc_extfsini = concat_dir_and_file (mc_home, "extfs/extfs.ini");
1320 cfg = fopen (mc_extfsini, "r");
1322 if (!cfg) {
1323 fprintf(stderr, _("Warning: file %s not found\n"), mc_extfsini);
1324 g_free (mc_extfsini);
1325 return 0;
1328 extfs_no = 0;
1329 while ( extfs_no < MAXEXTFS ) {
1330 char key[256];
1331 char *c;
1333 if (!fgets( key, sizeof (key)-1, cfg ))
1334 break;
1336 /* Handle those with a trailing ':', those flag that the
1337 * file system does not require an archive to work
1340 if (*key == '[') {
1341 /* We may not use vfs_die() message or message_1s or similar,
1342 * UI is not initialized at this time and message would not
1343 * appear on screen. */
1344 fprintf(stderr, "Warning: You need to update your %s file.\n",
1345 mc_extfsini);
1346 fclose(cfg);
1347 g_free (mc_extfsini);
1348 return 0;
1350 if (*key == '#')
1351 continue;
1353 if ((c = strchr (key, '\n'))){
1354 *c = 0;
1355 c = &key [strlen (key) - 1];
1356 } else {
1357 c = key;
1359 extfs_need_archive [extfs_no] = !(*c == ':');
1360 if (*c == ':')
1361 *c = 0;
1362 if (!(*key))
1363 continue;
1365 extfs_prefixes [extfs_no] = g_strdup (key);
1366 extfs_no++;
1368 fclose(cfg);
1369 g_free (mc_extfsini);
1370 return 1;
1373 /* Do NOT use me argument in this function */
1374 static int extfs_which (vfs *me, char *path)
1376 int i;
1378 for (i = 0; i < extfs_no; i++)
1379 if (!strcmp (path, extfs_prefixes [i]))
1380 return i;
1381 return -1;
1384 static void extfs_done (vfs *me)
1386 int i;
1388 for (i = 0; i < extfs_no; i++ )
1389 g_free (extfs_prefixes [i]);
1390 extfs_no = 0;
1393 static int extfs_setctl (vfs *me, char *path, int ctlop, char *arg)
1395 if (ctlop == MCCTL_EXTFS_RUN) {
1396 extfs_run (path);
1397 return 1;
1399 return 0;
1402 vfs vfs_extfs_ops = {
1403 NULL, /* This is place of next pointer */
1404 "extfs",
1405 F_EXEC, /* flags */
1406 NULL, /* prefix */
1407 NULL, /* data */
1408 0, /* errno */
1409 extfs_init,
1410 extfs_done,
1411 extfs_fill_names,
1412 extfs_which,
1414 extfs_open,
1415 extfs_close,
1416 extfs_read,
1417 extfs_write,
1419 s_opendir,
1420 s_readdir,
1421 s_closedir,
1422 s_telldir,
1423 s_seekdir,
1425 s_stat,
1426 s_lstat,
1427 s_fstat,
1429 extfs_chmod, /* chmod ... strange, returns success? */
1430 NULL,
1431 NULL,
1433 s_readlink,
1435 NULL, /* symlink */
1436 NULL,
1437 extfs_unlink,
1439 NULL,
1440 extfs_chdir,
1441 s_errno,
1442 extfs_lseek,
1443 NULL,
1445 extfs_getid,
1446 extfs_nothingisopen,
1447 extfs_free,
1449 extfs_getlocalcopy,
1450 extfs_ungetlocalcopy,
1452 extfs_mkdir, /* mkdir */
1453 extfs_rmdir,
1454 NULL,
1455 extfs_setctl
1457 MMAPNULL