ru.po: Corrections from Evgeny Bulgakov <bgav@netvision.net.il>
[midnight-commander.git] / vfs / extfs.c
blob782a0fe08fb91fd1a7cbafbd4c473df0a6f60df5
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., 675 Mass Ave, Cambridge, MA 02139, 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 <fcntl.h>
32 #include <unistd.h>
33 #include <signal.h>
34 #ifdef HAVE_SYS_WAIT_H
35 #include <sys/wait.h>
36 #endif
37 #include <errno.h>
38 #ifdef SCO_FLAVOR
39 #include <sys/timeb.h> /* alex: for struct timeb definition */
40 #endif /* SCO_FLAVOR */
41 #include <time.h>
42 #include "utilvfs.h"
43 #include "../src/dialog.h"
44 #include "../src/main.h" /* For shell_execute */
45 #include "vfs.h"
46 #include "extfs.h"
48 #define ERRNOR(x,y) do { my_errno = x; return y; } while(0)
50 static struct entry *
51 find_entry (struct entry *dir, char *name, int make_dirs, int make_file);
52 static int extfs_which (vfs *me, char *path);
53 static void remove_entry (struct entry *e);
55 static struct archive *first_archive = NULL;
56 static int my_errno = 0;
57 static struct stat hstat; /* Stat struct corresponding */
59 #define MAXEXTFS 32
60 static char *extfs_prefixes [MAXEXTFS];
61 static char extfs_need_archive [MAXEXTFS];
62 static int extfs_no = 0;
64 static void extfs_fill_names (vfs *me, void (*func)(char *))
66 struct archive *a = first_archive;
67 char *name;
69 while (a){
70 name = g_strconcat (extfs_prefixes [a->fstype], "#",
71 (a->name ? a->name : ""), "/",
72 a->current_dir->name, NULL);
73 (*func)(name);
74 g_free (name);
75 a = a->next;
79 static void make_dot_doubledot (struct entry *ent)
81 struct entry *entry = g_new (struct entry, 1);
82 struct entry *parentry = ent->dir;
83 struct inode *inode = ent->inode, *parent;
85 parent = (parentry != NULL) ? parentry->inode : NULL;
86 entry->name = g_strdup (".");
87 entry->has_changed = 0;
88 entry->inode = inode;
89 entry->dir = ent;
90 inode->local_filename = NULL;
91 inode->first_in_subdir = entry;
92 inode->last_in_subdir = entry;
93 inode->nlink++;
94 entry->next_in_dir = g_new (struct entry, 1);
95 entry=entry->next_in_dir;
96 entry->name = g_strdup ("..");
97 entry->has_changed = 0;
98 inode->last_in_subdir = entry;
99 entry->next_in_dir = NULL;
100 if (parent != NULL) {
101 entry->inode = parent;
102 entry->dir = parentry;
103 parent->nlink++;
104 } else {
105 entry->inode = inode;
106 entry->dir = ent;
107 inode->nlink++;
111 static struct entry *generate_entry (struct archive *archive,
112 char *name, struct entry *parentry, mode_t mode)
114 mode_t myumask;
115 struct inode *inode, *parent;
116 struct entry *entry;
118 parent = (parentry != NULL) ? parentry->inode : NULL;
119 entry = g_new (struct entry, 1);
121 entry->name = g_strdup (name);
122 entry->has_changed = 0;
123 entry->next_in_dir = NULL;
124 entry->dir = parentry;
125 if (parent != NULL) {
126 parent->last_in_subdir->next_in_dir = entry;
127 parent->last_in_subdir = entry;
129 inode = g_new (struct inode, 1);
130 entry->inode = inode;
131 inode->has_changed = 0;
132 inode->local_filename = NULL;
133 inode->linkname = 0;
134 inode->inode = (archive->__inode_counter)++;
135 inode->dev = archive->rdev;
136 inode->archive = archive;
137 myumask = umask (022);
138 umask (myumask);
139 inode->mode = mode & ~myumask;
140 mode = inode->mode;
141 inode->rdev = 0;
142 inode->uid = getuid ();
143 inode->gid = getgid ();
144 inode->size = 0;
145 inode->mtime = time (NULL);
146 inode->atime = inode->mtime;
147 inode->ctime = inode->mtime;
148 inode->nlink = 1;
149 if (S_ISDIR (mode))
150 make_dot_doubledot (entry);
151 return entry;
154 static void free_entries (struct entry *entry)
156 return;
159 static void free_archive (struct archive *archive)
161 free_entries (archive->root_entry);
162 if (archive->local_name != NULL) {
163 struct stat my;
165 mc_stat (archive->local_name, &my);
166 mc_ungetlocalcopy (archive->name, archive->local_name,
167 archive->local_stat.st_mtime != my.st_mtime);
168 /* ungetlocalcopy frees local_name for us */
170 if (archive->name)
171 g_free (archive->name);
172 g_free (archive);
175 static FILE *open_archive (int fstype, char *name, struct archive **pparc)
177 static dev_t __extfs_no = 0;
178 FILE *result;
179 mode_t mode;
180 char *cmd;
181 char *mc_extfsdir;
182 struct stat mystat;
183 struct archive *current_archive;
184 struct entry *root_entry;
185 char *local_name = NULL, *tmp = 0;
186 int uses_archive = extfs_need_archive [fstype];
188 if (uses_archive){
189 if (mc_stat (name, &mystat) == -1)
190 return NULL;
191 if (!vfs_file_is_local (name)) {
192 local_name = mc_getlocalcopy (name);
193 if (local_name == NULL)
194 return NULL;
196 tmp = name_quote (name, 0);
199 #if 0
200 /* Sorry, what is this good for? */
201 if (uses_archive == EFS_NEED_ARG){
202 tmp = name_quote (name, 0);
204 #endif
206 mc_extfsdir = concat_dir_and_file (mc_home, "extfs/");
207 cmd = g_strconcat (mc_extfsdir, extfs_prefixes [fstype],
208 " list ", local_name ? local_name : tmp, NULL);
209 if (tmp)
210 g_free (tmp);
211 result = popen (cmd, "r");
212 g_free (cmd);
213 g_free (mc_extfsdir);
214 if (result == NULL) {
215 if (local_name != NULL && uses_archive)
216 mc_ungetlocalcopy (name, local_name, 0);
217 return NULL;
220 current_archive = g_new (struct archive, 1);
221 current_archive->fstype = fstype;
222 current_archive->name = name ? g_strdup (name): name;
223 current_archive->local_name = local_name;
225 if (local_name != NULL)
226 mc_stat (local_name, &current_archive->local_stat);
227 current_archive->__inode_counter = 0;
228 current_archive->fd_usage = 0;
229 current_archive->extfsstat = mystat;
230 current_archive->rdev = __extfs_no++;
231 current_archive->next = first_archive;
232 first_archive = current_archive;
233 mode = current_archive->extfsstat.st_mode & 07777;
234 if (mode & 0400)
235 mode |= 0100;
236 if (mode & 0040)
237 mode |= 0010;
238 if (mode & 0004)
239 mode |= 0001;
240 mode |= S_IFDIR;
241 root_entry = generate_entry (current_archive, "/", NULL, mode);
242 root_entry->inode->uid = current_archive->extfsstat.st_uid;
243 root_entry->inode->gid = current_archive->extfsstat.st_gid;
244 root_entry->inode->atime = current_archive->extfsstat.st_atime;
245 root_entry->inode->ctime = current_archive->extfsstat.st_ctime;
246 root_entry->inode->mtime = current_archive->extfsstat.st_mtime;
247 current_archive->root_entry = root_entry;
248 current_archive->current_dir = root_entry;
250 *pparc = current_archive;
252 return result;
256 * Main loop for reading an archive.
257 * Returns 0 on success, -1 on error.
259 static int read_archive (int fstype, char *name, struct archive **pparc)
261 FILE *extfsd;
262 char *buffer;
263 struct archive *current_archive;
264 char *current_file_name, *current_link_name;
267 if ((extfsd = open_archive (fstype, name, &current_archive)) == NULL) {
268 message_3s (1, MSG_ERROR, _("Couldn't open %s archive\n%s"),
269 extfs_prefixes [fstype], name);
270 return -1;
273 buffer = g_malloc (4096);
274 while (fgets (buffer, 4096, extfsd) != NULL) {
275 current_link_name = NULL;
276 if (vfs_parse_ls_lga (buffer, &hstat, &current_file_name, &current_link_name)) {
277 struct entry *entry, *pent;
278 struct inode *inode;
279 char *p, *q, *cfn = current_file_name;
281 if (*cfn) {
282 if (*cfn == '/')
283 cfn++;
284 p = strchr (cfn, 0);
285 if (p != NULL && p != cfn && *(p - 1) == '/')
286 *(p - 1) = 0;
287 p = strrchr (cfn, '/');
288 if (p == NULL) {
289 p = cfn;
290 q = strchr (cfn, 0);
291 } else {
292 *(p++) = 0;
293 q = cfn;
295 if (S_ISDIR (hstat.st_mode) &&
296 (!strcmp (p, ".") || !strcmp (p, "..")))
297 goto read_extfs_continue;
298 pent = find_entry (current_archive->root_entry, q, 1, 0) ;
299 if (pent == NULL) {
300 message_1s (1, MSG_ERROR, _("Inconsistent extfs archive"));
301 /* FIXME: Should clean everything one day */
302 g_free (buffer);
303 pclose (extfsd);
304 return -1;
306 entry = g_new (struct entry, 1);
307 entry->name = g_strdup (p);
308 entry->has_changed = 0;
309 entry->next_in_dir = NULL;
310 entry->dir = pent;
311 if (pent != NULL) {
312 if (pent->inode->last_in_subdir){
313 pent->inode->last_in_subdir->next_in_dir = entry;
314 pent->inode->last_in_subdir = entry;
317 if (!S_ISLNK (hstat.st_mode) && current_link_name != NULL) {
318 pent = find_entry (current_archive->root_entry, current_link_name, 0, 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;
325 } else {
326 entry->inode = pent->inode;
327 pent->inode->nlink++;
329 } else {
330 inode = g_new (struct inode, 1);
331 entry->inode = inode;
332 inode->local_filename = NULL;
333 inode->has_changed = 0;
334 inode->inode = (current_archive->__inode_counter)++;
335 inode->nlink = 1;
336 inode->dev = current_archive->rdev;
337 inode->archive = current_archive;
338 inode->mode = hstat.st_mode;
339 #ifdef HAVE_ST_RDEV
340 inode->rdev = hstat.st_rdev;
341 #else
342 inode->rdev = 0;
343 #endif
344 inode->uid = hstat.st_uid;
345 inode->gid = hstat.st_gid;
346 inode->size = hstat.st_size;
347 inode->mtime = hstat.st_mtime;
348 inode->atime = hstat.st_atime;
349 inode->ctime = hstat.st_ctime;
350 if (current_link_name != NULL && S_ISLNK (hstat.st_mode)) {
351 inode->linkname = current_link_name;
352 current_link_name = NULL;
353 } else {
354 if (S_ISLNK( hstat.st_mode))
355 inode->mode &= ~S_IFLNK; /* You *DON'T* want to do this always */
356 inode->linkname = NULL;
358 if (S_ISDIR (hstat.st_mode))
359 make_dot_doubledot (entry);
362 read_extfs_continue:
363 g_free (current_file_name);
364 if (current_link_name != NULL)
365 g_free (current_link_name);
368 pclose (extfsd);
369 #ifdef SCO_FLAVOR
370 waitpid(-1,NULL,WNOHANG);
371 #endif /* SCO_FLAVOR */
372 *pparc = current_archive;
373 g_free (buffer);
374 return 0;
377 static char *get_path (char *inname, struct archive **archive, int is_dir,
378 int do_not_open);
380 /* Returns path inside argument. Returned char* is inside inname, which is mangled
381 * by this operation (so you must not free it's return value)
383 static char *get_path_mangle (char *inname, struct archive **archive, int is_dir,
384 int do_not_open)
386 char *local, *archive_name, *op;
387 int result = -1;
388 struct archive *parc;
389 struct vfs_stamping *parent;
390 vfs *v;
391 int fstype;
393 archive_name = inname;
394 vfs_split( inname, &local, &op );
395 fstype = extfs_which( NULL, op ); /* FIXME: we really should pass
396 self pointer. But as we know that extfs_which does not touch vfs
397 *me, it does not matter for now */
398 if (!local)
399 local = "";
400 if (fstype == -1)
401 return NULL;
403 /* All filesystems should have some local archive, at least
404 * it can be '/'.
406 * Actually, we should implement an alias mechanism that would
407 * translate: "a:" to "dos:a.
410 for (parc = first_archive; parc != NULL; parc = parc->next)
411 if (parc->name) {
412 if (!strcmp (parc->name, archive_name)) {
413 struct stat *s=&(parc->extfsstat);
414 if (vfs_uid && (!(s->st_mode & 0004)))
415 if ((s->st_gid != vfs_gid) || !(s->st_mode & 0040))
416 if ((s->st_uid != vfs_uid) || !(s->st_mode & 0400))
417 return NULL;
418 /* This is not too secure - in some cases (/#mtools) files created
419 under user a are probably visible to everyone else since / usually
420 has permissions 755 */
421 vfs_stamp (&vfs_extfs_ops, (vfsid) parc);
422 goto return_success;
426 result = do_not_open ? -1 : read_archive (fstype, archive_name, &parc);
427 if (result == -1) ERRNOR (EIO, NULL);
429 if (archive_name){
430 v = vfs_type (archive_name);
431 if (v == &vfs_local_ops) {
432 parent = NULL;
433 } else {
434 parent = g_new (struct vfs_stamping, 1);
435 parent->v = v;
436 parent->next = 0;
437 parent->id = (*v->getid) (v, archive_name, &(parent->parent));
439 vfs_add_noncurrent_stamps (&vfs_extfs_ops, (vfsid) parc, parent);
440 vfs_rm_parents (parent);
442 return_success:
443 *archive = parc;
444 return local;
447 /* Returns allocated path (without leading slash) inside the archive */
448 static char *get_path_from_entry (struct entry *entry)
450 struct list {
451 struct list *next;
452 char *name;
453 } *head, *p;
454 char *localpath;
455 size_t len;
457 for (len = 0, head = 0; entry->dir; entry = entry->dir) {
458 p = g_new (struct list, 1);
459 p->next = head;
460 p->name = entry->name;
461 head = p;
462 len += strlen (entry->name) + 1;
465 if (len == 0)
466 return g_strdup ("");
468 localpath = g_malloc (len);
469 *localpath = '\0';
470 for ( ; head; ) {
471 strcat (localpath, head->name);
472 if (head->next)
473 strcat (localpath, "/");
474 p = head;
475 head = head->next;
476 g_free (p);
478 return (localpath);
482 struct loop_protect {
483 struct entry *entry;
484 struct loop_protect *next;
486 static int errloop;
487 static int notadir;
489 static struct entry *
490 __find_entry (struct entry *dir, char *name,
491 struct loop_protect *list, int make_dirs, int make_file);
493 static struct entry *
494 __resolve_symlinks (struct entry *entry,
495 struct loop_protect *list)
497 struct entry *pent;
498 struct loop_protect *looping;
500 if (!S_ISLNK (entry->inode->mode))
501 return entry;
502 for (looping = list; looping != NULL; looping = looping->next)
503 if (entry == looping->entry) { /* Here we protect us against symlink looping */
504 errloop = 1;
505 return NULL;
507 looping = g_new (struct loop_protect, 1);
508 looping->entry = entry;
509 looping->next = list;
510 pent = __find_entry (entry->dir, entry->inode->linkname, looping, 0, 0);
511 g_free (looping);
512 if (pent == NULL)
513 my_errno = ENOENT;
514 return pent;
517 static struct entry *my_resolve_symlinks (struct entry *entry)
519 struct entry *res;
521 errloop = 0;
522 notadir = 0;
523 res = __resolve_symlinks (entry, NULL);
524 if (res == NULL) {
525 if (errloop)
526 my_errno = ELOOP;
527 else if (notadir)
528 my_errno = ENOTDIR;
530 return res;
533 struct pseudofile {
534 struct archive *archive;
535 unsigned int has_changed:1;
536 int local_handle;
537 struct entry *entry;
540 static char *get_archive_name (struct archive *archive)
542 char *archive_name;
544 if (archive->local_name)
545 archive_name = archive->local_name;
546 else
547 archive_name = archive->name;
549 if (!archive_name || !*archive_name)
550 return "no_archive_name";
551 else
552 return archive_name;
555 /* FIXME: we really should not have non-static procedures - it
556 * pollutes namespace. */
558 void extfs_run (char *file)
560 struct archive *archive;
561 char *p, *q, *archive_name, *mc_extfsdir;
562 char *cmd;
564 if ((p = get_path (file, &archive, 0, 0)) == NULL)
565 return;
566 q = name_quote (p, 0);
568 archive_name = name_quote (get_archive_name(archive), 0);
569 mc_extfsdir = concat_dir_and_file (mc_home, "extfs/");
570 cmd = g_strconcat (mc_extfsdir, extfs_prefixes [archive->fstype],
571 " run ", archive_name, " ", q, NULL);
572 g_free (mc_extfsdir);
573 g_free (archive_name);
574 g_free (q);
575 #ifndef VFS_STANDALONE
576 shell_execute(cmd, 0);
577 #else
578 vfs_die( "shell_execute: implement me!" );
579 #endif
580 g_free(cmd);
581 g_free(p);
584 static void *extfs_open (vfs *me, char *file, int flags, int mode)
586 struct pseudofile *extfs_info;
587 struct archive *archive;
588 char *q;
589 char *mc_extfsdir;
590 struct entry *entry;
591 int local_handle;
592 const int do_create = (flags & O_ACCMODE) != O_RDONLY;
594 if ((q = get_path_mangle (file, &archive, 0, 0)) == NULL)
595 return NULL;
596 entry = find_entry (archive->root_entry, q, 0, do_create);
597 if (entry == NULL)
598 return NULL;
599 if ((entry = my_resolve_symlinks (entry)) == NULL)
600 return NULL;
601 if (S_ISDIR (entry->inode->mode)) ERRNOR (EISDIR, NULL);
602 if (entry->inode->local_filename == NULL) {
603 char *cmd;
604 char *archive_name, *p;
606 entry->inode->local_filename = tempnam (NULL, "extfs");
608 int handle;
610 handle = open(entry->inode->local_filename, O_RDWR | O_CREAT | O_EXCL, 0600);
611 if (handle == -1)
612 return NULL;
613 close(handle);
615 p = get_path_from_entry (entry);
616 q = name_quote (p, 0);
617 g_free (p);
618 archive_name = name_quote (get_archive_name (archive), 0);
620 mc_extfsdir = concat_dir_and_file (mc_home, "extfs/");
621 cmd = g_strconcat (mc_extfsdir, extfs_prefixes [archive->fstype],
622 " copyout ",
623 archive_name,
624 " ", q, " ", entry->inode->local_filename, NULL);
625 g_free (q);
626 g_free (mc_extfsdir);
627 g_free (archive_name);
628 if (my_system (EXECUTE_AS_SHELL | EXECUTE_SETUID | EXECUTE_WAIT, shell, cmd) && !do_create){
629 free (entry->inode->local_filename);
630 entry->inode->local_filename = NULL;
631 g_free (cmd);
632 my_errno = EIO;
633 return NULL;
635 g_free (cmd);
638 local_handle = open (entry->inode->local_filename, flags, mode);
639 if (local_handle == -1) ERRNOR (EIO, NULL);
641 extfs_info = g_new (struct pseudofile, 1);
642 extfs_info->archive = archive;
643 extfs_info->entry = entry;
644 extfs_info->has_changed = 0;
645 extfs_info->local_handle = local_handle;
647 /* i.e. we had no open files and now we have one */
648 vfs_rmstamp (&vfs_extfs_ops, (vfsid) archive, 1);
649 archive->fd_usage++;
650 return extfs_info;
653 static int extfs_read (void *data, char *buffer, int count)
655 struct pseudofile *file = (struct pseudofile *)data;
657 return read (file->local_handle, buffer, count);
660 static int extfs_close (void *data)
662 struct pseudofile *file;
663 int errno_code = 0;
664 file = (struct pseudofile *)data;
666 close (file->local_handle);
668 /* Commit the file if it has changed */
669 if (file->has_changed){
670 struct archive *archive;
671 char *archive_name, *file_name;
672 char *cmd;
673 char *mc_extfsdir;
674 char *p;
676 archive = file->archive;
677 archive_name = name_quote (get_archive_name (archive), 0);
678 p = get_path_from_entry (file->entry);
679 file_name = name_quote (p, 0);
680 g_free (p);
682 mc_extfsdir = concat_dir_and_file (mc_home, "extfs/");
683 cmd = g_strconcat (mc_extfsdir,
684 extfs_prefixes [archive->fstype],
685 " copyin ", archive_name, " ",
686 file_name, " ",
687 file->entry->inode->local_filename, NULL);
688 g_free (archive_name);
689 g_free (file_name);
690 if (my_system (EXECUTE_AS_SHELL | EXECUTE_SETUID | EXECUTE_WAIT, shell, cmd))
691 errno_code = EIO;
692 g_free (cmd);
693 g_free (mc_extfsdir);
695 struct stat file_status;
696 if( stat(file->entry->inode->local_filename,&file_status) != 0 )
697 errno_code = EIO;
698 else file->entry->inode->size = file_status.st_size;
701 file->entry->inode->mtime = time (NULL);
704 file->archive->fd_usage--;
705 if (!file->archive->fd_usage) {
706 struct vfs_stamping *parent;
707 vfs *v;
709 if (!file->archive->name || !*file->archive->name || (v = vfs_type (file->archive->name)) == &vfs_local_ops) {
710 parent = NULL;
711 } else {
712 parent = g_new (struct vfs_stamping, 1);
713 parent->v = v;
714 parent->next = 0;
715 parent->id = (*v->getid) (v, file->archive->name, &(parent->parent));
717 vfs_add_noncurrent_stamps (&vfs_extfs_ops, (vfsid) (file->archive), parent);
718 vfs_rm_parents (parent);
721 g_free (data);
722 if (errno_code) ERRNOR (EIO, -1);
723 return 0;
726 #define RECORDSIZE 512
727 #include "shared_tar_ext.c"
729 static int extfs_chmod (vfs *me, char *path, int mode)
731 return 0;
734 static int extfs_write (void *data, char *buf, int nbyte)
736 struct pseudofile *file = (struct pseudofile *)data;
738 file->has_changed = 1;
739 return write (file->local_handle, buf, nbyte);
742 static int extfs_unlink (vfs *me, char *file)
744 struct archive *archive;
745 char *q;
746 char *mc_extfsdir;
747 struct entry *entry;
748 char *cmd;
749 char *archive_name, *p;
751 if ((q = get_path_mangle (file, &archive, 0, 0)) == NULL)
752 return -1;
753 entry = find_entry (archive->root_entry, q, 0, 0);
754 if (entry == NULL)
755 return -1;
756 if ((entry = my_resolve_symlinks (entry)) == NULL)
757 return -1;
758 if (S_ISDIR (entry->inode->mode)) ERRNOR (EISDIR, -1);
760 p = get_path_from_entry (entry);
761 q = name_quote (p, 0);
762 g_free (p);
763 archive_name = name_quote (get_archive_name (archive), 0);
765 mc_extfsdir = concat_dir_and_file (mc_home, "extfs/");
766 cmd = g_strconcat (mc_extfsdir, extfs_prefixes [archive->fstype],
767 " rm ", archive_name, " ", q, NULL);
768 g_free (q);
769 g_free (mc_extfsdir);
770 g_free (archive_name);
771 if (my_system (EXECUTE_AS_SHELL | EXECUTE_SETUID | EXECUTE_WAIT, shell, cmd)){
772 g_free (cmd);
773 my_errno = EIO;
774 return -1;
776 g_free (cmd);
777 remove_entry (entry);
779 return 0;
782 static int extfs_mkdir (vfs *me, char *path, mode_t mode)
784 struct archive *archive;
785 char *q;
786 char *mc_extfsdir;
787 struct entry *entry;
788 char *cmd;
789 char *archive_name, *p;
791 if ((q = get_path_mangle (path, &archive, 0, 0)) == NULL)
792 return -1;
793 entry = find_entry (archive->root_entry, q, 0, 0);
794 if (entry != NULL) ERRNOR (EEXIST, -1);
795 entry = find_entry (archive->root_entry, q, 1, 0);
796 if (entry == NULL)
797 return -1;
798 if ((entry = my_resolve_symlinks (entry)) == NULL)
799 return -1;
800 if (!S_ISDIR (entry->inode->mode)) ERRNOR (ENOTDIR, -1);
802 p = get_path_from_entry (entry);
803 q = name_quote (p, 0);
804 g_free (p);
805 archive_name = name_quote (get_archive_name (archive), 0);
807 mc_extfsdir = concat_dir_and_file (mc_home, "extfs/");
808 cmd = g_strconcat (mc_extfsdir, extfs_prefixes [archive->fstype],
809 " mkdir ", archive_name, " ", q, NULL);
810 g_free (q);
811 g_free (mc_extfsdir);
812 g_free (archive_name);
813 if (my_system (EXECUTE_AS_SHELL | EXECUTE_SETUID | EXECUTE_WAIT, shell, cmd)){
814 g_free (cmd);
815 my_errno = EIO;
816 remove_entry (entry);
817 return -1;
819 g_free (cmd);
821 return 0;
824 static int extfs_rmdir (vfs *me, char *path)
826 struct archive *archive;
827 char *q;
828 char *mc_extfsdir;
829 struct entry *entry;
830 char *cmd;
831 char *archive_name, *p;
833 if ((q = get_path_mangle (path, &archive, 0, 0)) == NULL)
834 return -1;
835 entry = find_entry (archive->root_entry, q, 0, 0);
836 if (entry == NULL)
837 return -1;
838 if ((entry = my_resolve_symlinks (entry)) == NULL)
839 return -1;
840 if (!S_ISDIR (entry->inode->mode)) ERRNOR (ENOTDIR, -1);
842 p = get_path_from_entry (entry);
843 q = name_quote (p, 0);
844 g_free (p);
845 archive_name = name_quote (get_archive_name (archive), 0);
847 mc_extfsdir = concat_dir_and_file (mc_home, "extfs/");
848 cmd = g_strconcat (mc_extfsdir, extfs_prefixes [archive->fstype],
849 " rmdir ", archive_name, " ", q, NULL);
850 g_free (q);
851 g_free (mc_extfsdir);
852 g_free (archive_name);
853 if (my_system (EXECUTE_AS_SHELL | EXECUTE_SETUID | EXECUTE_WAIT, shell, cmd)){
854 g_free (cmd);
855 my_errno = EIO;
856 return -1;
858 g_free (cmd);
859 remove_entry (entry);
861 return 0;
864 static int extfs_chdir (vfs *me, char *path)
866 struct archive *archive;
867 char *q, *res;
868 struct entry *entry;
870 my_errno = ENOTDIR;
871 if ((q = get_path_mangle (path, &archive, 1, 0)) == NULL)
872 return -1;
873 entry = find_entry (archive->root_entry, q, 0, 0);
874 if (!entry)
875 return -1;
876 entry = my_resolve_symlinks (entry);
877 if ((!entry) || (!S_ISDIR (entry->inode->mode)))
878 return -1;
879 entry->inode->archive->current_dir = entry;
880 res = g_strconcat (
881 entry->inode->archive->name, "#", extfs_prefixes [entry->inode->archive->fstype],
882 "/", q, NULL);
883 my_errno = 0;
884 return 0;
887 static int extfs_lseek (void *data, off_t offset, int whence)
889 struct pseudofile *file = (struct pseudofile *) data;
891 return lseek (file->local_handle, offset, whence);
894 static vfsid extfs_getid (vfs *me, char *path, struct vfs_stamping **parent)
896 struct archive *archive;
897 vfs *v;
898 vfsid id;
899 struct vfs_stamping *par;
900 char *p;
902 *parent = NULL;
903 if (!(p = get_path (path, &archive, 1, 1)))
904 return (vfsid) -1;
905 g_free(p);
906 if (archive->name){
907 v = vfs_type (archive->name);
908 id = (*v->getid) (v, archive->name, &par);
909 if (id != (vfsid)-1) {
910 *parent = g_new (struct vfs_stamping, 1);
911 (*parent)->v = v;
912 (*parent)->id = id;
913 (*parent)->parent = par;
914 (*parent)->next = NULL;
917 return (vfsid) archive;
920 static int extfs_nothingisopen (vfsid id)
922 if (((struct archive *)id)->fd_usage <= 0)
923 return 1;
924 else
925 return 0;
928 static void remove_entry (struct entry *e)
930 int i = --(e->inode->nlink);
931 struct entry *pe, *ent, *prev;
933 if (S_ISDIR (e->inode->mode) && e->inode->first_in_subdir != NULL) {
934 struct entry *f = e->inode->first_in_subdir;
935 e->inode->first_in_subdir = NULL;
936 remove_entry (f);
938 pe = e->dir;
939 if (e == pe->inode->first_in_subdir)
940 pe->inode->first_in_subdir = e->next_in_dir;
942 prev = NULL;
943 for (ent = pe->inode->first_in_subdir; ent && ent->next_in_dir;
944 ent = ent->next_in_dir)
945 if (e == ent->next_in_dir) {
946 prev = ent;
947 break;
949 if (prev)
950 prev->next_in_dir = e->next_in_dir;
951 if (e == pe->inode->last_in_subdir)
952 pe->inode->last_in_subdir = prev;
954 if (i <= 0) {
955 if (e->inode->local_filename != NULL) {
956 unlink (e->inode->local_filename);
957 free (e->inode->local_filename);
959 if (e->inode->linkname != NULL)
960 g_free (e->inode->linkname);
961 g_free (e->inode);
964 g_free (e->name);
965 g_free (e);
968 static void free_entry (struct entry *e)
970 int i = --(e->inode->nlink);
971 if (S_ISDIR (e->inode->mode) && e->inode->first_in_subdir != NULL) {
972 struct entry *f = e->inode->first_in_subdir;
974 e->inode->first_in_subdir = NULL;
975 free_entry (f);
977 if (i <= 0) {
978 if (e->inode->local_filename != NULL) {
979 unlink (e->inode->local_filename);
980 free (e->inode->local_filename);
982 if (e->inode->linkname != NULL)
983 g_free (e->inode->linkname);
984 g_free (e->inode);
986 if (e->next_in_dir != NULL)
987 free_entry (e->next_in_dir);
988 g_free (e->name);
989 g_free (e);
992 static void extfs_free (vfsid id)
994 struct archive *parc;
995 struct archive *archive = (struct archive *)id;
997 free_entry (archive->root_entry);
998 if (archive == first_archive) {
999 first_archive = archive->next;
1000 } else {
1001 for (parc = first_archive; parc != NULL; parc = parc->next)
1002 if (parc->next == archive)
1003 break;
1004 if (parc != NULL)
1005 parc->next = archive->next;
1007 free_archive (archive);
1010 static char *extfs_getlocalcopy (vfs *me, char *path)
1012 struct pseudofile *fp =
1013 (struct pseudofile *) extfs_open (me, path, O_RDONLY, 0);
1014 char *p;
1016 if (fp == NULL)
1017 return NULL;
1018 if (fp->entry->inode->local_filename == NULL) {
1019 extfs_close ((void *) fp);
1020 return NULL;
1022 p = g_strdup (fp->entry->inode->local_filename);
1023 fp->archive->fd_usage++;
1024 extfs_close ((void *) fp);
1025 return p;
1028 static void extfs_ungetlocalcopy (vfs *me, char *path, char *local, int has_changed)
1030 struct pseudofile *fp =
1031 (struct pseudofile *) extfs_open (me, path, O_WRONLY, 0);
1033 if (fp == NULL)
1034 return;
1035 if (!strcmp (fp->entry->inode->local_filename, local)) {
1036 fp->entry->inode->has_changed = has_changed;
1037 fp->archive->fd_usage--;
1038 extfs_close ((void *) fp);
1039 return;
1040 } else {
1041 /* Should not happen */
1042 extfs_close ((void *) fp);
1043 mc_def_ungetlocalcopy (me, path, local, has_changed);
1048 #include "../src/profile.h"
1049 static int extfs_init (vfs *me)
1051 FILE *cfg;
1052 char *mc_extfsini;
1054 mc_extfsini = concat_dir_and_file (mc_home, "extfs/extfs.ini");
1055 cfg = fopen (mc_extfsini, "r");
1056 g_free (mc_extfsini);
1058 if (!cfg) {
1059 fprintf( stderr, "Warning: " LIBDIR "extfs/extfs.ini not found\n" );
1060 return 0;
1063 extfs_no = 0;
1064 while ( extfs_no < MAXEXTFS ) {
1065 char key[256];
1066 char *c;
1068 if (!fgets( key, sizeof (key)-1, cfg ))
1069 break;
1071 /* Handle those with a trailing ':', those flag that the
1072 * file system does not require an archive to work
1075 if (*key == '[') {
1076 /* We may not use vfs_die() message or message_1s or similar,
1077 * UI is not initialized at this time and message would not
1078 * appear on screen. */
1079 fprintf( stderr, "Warning: You need to update your " LIBDIR "extfs/extfs.ini file.\n" );
1080 fclose(cfg);
1081 return 0;
1083 if (*key == '#')
1084 continue;
1086 if ((c = strchr (key, '\n'))){
1087 *c = 0;
1088 c = &key [strlen (key) - 1];
1089 } else {
1090 c = key;
1092 extfs_need_archive [extfs_no] = !(*c == ':');
1093 if (*c == ':')
1094 *c = 0;
1095 if (!(*key))
1096 continue;
1098 extfs_prefixes [extfs_no] = g_strdup (key);
1099 extfs_no++;
1101 fclose(cfg);
1102 return 1;
1105 /* Do NOT use me argument in this function */
1106 static int extfs_which (vfs *me, char *path)
1108 int i;
1110 for (i = 0; i < extfs_no; i++)
1111 if (!strcmp (path, extfs_prefixes [i]))
1112 return i;
1113 return -1;
1116 static void extfs_done (vfs *me)
1118 int i;
1120 for (i = 0; i < extfs_no; i++ )
1121 g_free (extfs_prefixes [i]);
1122 extfs_no = 0;
1125 static int extfs_setctl (vfs *me, char *path, int ctlop, char *arg)
1127 if (ctlop == MCCTL_EXTFS_RUN) {
1128 extfs_run (path);
1129 return 1;
1131 return 0;
1134 vfs vfs_extfs_ops = {
1135 NULL, /* This is place of next pointer */
1136 "Extended filesystems",
1137 F_EXEC, /* flags */
1138 NULL, /* prefix */
1139 NULL, /* data */
1140 0, /* errno */
1141 extfs_init,
1142 extfs_done,
1143 extfs_fill_names,
1144 extfs_which,
1146 extfs_open,
1147 extfs_close,
1148 extfs_read,
1149 extfs_write,
1151 s_opendir,
1152 s_readdir,
1153 s_closedir,
1154 s_telldir,
1155 s_seekdir,
1157 s_stat,
1158 s_lstat,
1159 s_fstat,
1161 extfs_chmod, /* chmod ... strange, returns success? */
1162 NULL,
1163 NULL,
1165 s_readlink,
1167 NULL, /* symlink */
1168 NULL,
1169 extfs_unlink,
1171 NULL,
1172 extfs_chdir,
1173 s_errno,
1174 extfs_lseek,
1175 NULL,
1177 extfs_getid,
1178 extfs_nothingisopen,
1179 extfs_free,
1181 extfs_getlocalcopy,
1182 extfs_ungetlocalcopy,
1184 extfs_mkdir, /* mkdir */
1185 extfs_rmdir,
1186 NULL,
1187 extfs_setctl
1189 MMAPNULL