* direntry.c: Use g_strlcpy instead strncpy for fix buffer overrun
[midnight-commander.git] / vfs / extfs.c
blobc9cc702eb47ef686d0b9f0dabdc9a10312b24bbc
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: init_extfs */
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/execute.h" /* For shell_execute */
39 #include "vfs.h"
40 #include "vfs-impl.h"
41 #include "gc.h" /* vfs_rmstamp */
43 #undef ERRNOR
44 #define ERRNOR(x,y) do { my_errno = x; return y; } while(0)
46 struct inode {
47 nlink_t nlink;
48 struct entry *first_in_subdir; /* only used if this is a directory */
49 struct entry *last_in_subdir;
50 ino_t inode; /* This is inode # */
51 dev_t dev; /* This is an internal identification of the extfs archive */
52 struct archive *archive; /* And this is an archive structure */
53 dev_t rdev;
54 mode_t mode;
55 uid_t uid;
56 gid_t gid;
57 int size;
58 time_t mtime;
59 char linkflag;
60 char *linkname;
61 time_t atime;
62 time_t ctime;
63 char *local_filename;
66 struct entry {
67 struct entry *next_in_dir;
68 struct entry *dir;
69 char *name;
70 struct inode *inode;
73 struct pseudofile {
74 struct archive *archive;
75 unsigned int has_changed:1;
76 int local_handle;
77 struct entry *entry;
80 struct archive {
81 int fstype;
82 char *name;
83 char *local_name;
84 struct stat local_stat;
85 dev_t rdev;
86 int fd_usage;
87 ino_t inode_counter;
88 struct entry *root_entry;
89 struct archive *next;
92 static struct entry *extfs_find_entry (struct entry *dir, char *name,
93 int make_dirs, int make_file);
94 static int extfs_which (struct vfs_class *me, const char *path);
95 static void extfs_remove_entry (struct entry *e);
96 static void extfs_free (vfsid id);
98 static struct vfs_class vfs_extfs_ops;
99 static struct archive *first_archive = NULL;
100 static int my_errno = 0;
102 #define MAXEXTFS 32
103 static char *extfs_prefixes [MAXEXTFS];
104 static char extfs_need_archive [MAXEXTFS];
105 static int extfs_no = 0;
107 static void
108 extfs_fill_names (struct vfs_class *me, fill_names_f func)
110 struct archive *a = first_archive;
111 char *name;
113 while (a) {
114 name =
115 g_strconcat (a->name ? a->name : "", "#",
116 extfs_prefixes[a->fstype], NULL);
117 (*func) (name);
118 g_free (name);
119 a = a->next;
123 static void extfs_make_dots (struct entry *ent)
125 struct entry *entry = g_new (struct entry, 1);
126 struct entry *parentry = ent->dir;
127 struct inode *inode = ent->inode, *parent;
129 parent = (parentry != NULL) ? parentry->inode : NULL;
130 entry->name = g_strdup (".");
131 entry->inode = inode;
132 entry->dir = ent;
133 inode->local_filename = NULL;
134 inode->first_in_subdir = entry;
135 inode->last_in_subdir = entry;
136 inode->nlink++;
137 entry->next_in_dir = g_new (struct entry, 1);
138 entry=entry->next_in_dir;
139 entry->name = g_strdup ("..");
140 inode->last_in_subdir = entry;
141 entry->next_in_dir = NULL;
142 if (parent != NULL) {
143 entry->inode = parent;
144 entry->dir = parentry;
145 parent->nlink++;
146 } else {
147 entry->inode = inode;
148 entry->dir = ent;
149 inode->nlink++;
153 static struct entry *extfs_generate_entry (struct archive *archive,
154 char *name, struct entry *parentry, mode_t mode)
156 mode_t myumask;
157 struct inode *inode, *parent;
158 struct entry *entry;
160 parent = (parentry != NULL) ? parentry->inode : NULL;
161 entry = g_new (struct entry, 1);
163 entry->name = g_strdup (name);
164 entry->next_in_dir = NULL;
165 entry->dir = parentry;
166 if (parent != NULL) {
167 parent->last_in_subdir->next_in_dir = entry;
168 parent->last_in_subdir = entry;
170 inode = g_new (struct inode, 1);
171 entry->inode = inode;
172 inode->local_filename = NULL;
173 inode->linkname = 0;
174 inode->inode = (archive->inode_counter)++;
175 inode->dev = archive->rdev;
176 inode->archive = archive;
177 myumask = umask (022);
178 umask (myumask);
179 inode->mode = mode & ~myumask;
180 mode = inode->mode;
181 inode->rdev = 0;
182 inode->uid = getuid ();
183 inode->gid = getgid ();
184 inode->size = 0;
185 inode->mtime = time (NULL);
186 inode->atime = inode->mtime;
187 inode->ctime = inode->mtime;
188 inode->nlink = 1;
189 if (S_ISDIR (mode))
190 extfs_make_dots (entry);
191 return entry;
194 static void extfs_free_entries (struct entry *entry)
196 return;
199 static void extfs_free_archive (struct archive *archive)
201 extfs_free_entries (archive->root_entry);
202 if (archive->local_name != NULL) {
203 struct stat my;
205 mc_stat (archive->local_name, &my);
206 mc_ungetlocalcopy (archive->name, archive->local_name,
207 archive->local_stat.st_mtime != my.st_mtime);
208 g_free(archive->local_name);
210 if (archive->name)
211 g_free (archive->name);
212 g_free (archive);
215 static FILE *
216 extfs_open_archive (int fstype, const char *name, struct archive **pparc)
218 static dev_t archive_counter = 0;
219 FILE *result;
220 mode_t mode;
221 char *cmd;
222 char *mc_extfsdir;
223 struct stat mystat;
224 struct archive *current_archive;
225 struct entry *root_entry;
226 char *local_name = NULL, *tmp = 0;
227 int uses_archive = extfs_need_archive[fstype];
229 if (uses_archive) {
230 if (mc_stat (name, &mystat) == -1)
231 return NULL;
232 if (!vfs_file_is_local (name)) {
233 local_name = mc_getlocalcopy (name);
234 if (local_name == NULL)
235 return NULL;
237 tmp = name_quote (name, 0);
240 mc_extfsdir = concat_dir_and_file (mc_home, "extfs" PATH_SEP_STR);
241 cmd =
242 g_strconcat (mc_extfsdir, extfs_prefixes[fstype], " list ",
243 local_name ? local_name : tmp, NULL);
244 if (tmp)
245 g_free (tmp);
246 g_free (mc_extfsdir);
247 open_error_pipe ();
248 result = popen (cmd, "r");
249 g_free (cmd);
250 if (result == NULL) {
251 close_error_pipe (1, NULL);
252 if (local_name) {
253 mc_ungetlocalcopy (name, local_name, 0);
254 g_free(local_name);
256 return NULL;
259 current_archive = g_new (struct archive, 1);
260 current_archive->fstype = fstype;
261 current_archive->name = name ? g_strdup (name) : NULL;
262 current_archive->local_name = local_name;
264 if (local_name != NULL)
265 mc_stat (local_name, &current_archive->local_stat);
266 current_archive->inode_counter = 0;
267 current_archive->fd_usage = 0;
268 current_archive->rdev = archive_counter++;
269 current_archive->next = first_archive;
270 first_archive = current_archive;
271 mode = mystat.st_mode & 07777;
272 if (mode & 0400)
273 mode |= 0100;
274 if (mode & 0040)
275 mode |= 0010;
276 if (mode & 0004)
277 mode |= 0001;
278 mode |= S_IFDIR;
279 root_entry = extfs_generate_entry (current_archive, "/", NULL, mode);
280 root_entry->inode->uid = mystat.st_uid;
281 root_entry->inode->gid = mystat.st_gid;
282 root_entry->inode->atime = mystat.st_atime;
283 root_entry->inode->ctime = mystat.st_ctime;
284 root_entry->inode->mtime = mystat.st_mtime;
285 current_archive->root_entry = root_entry;
287 *pparc = current_archive;
289 return result;
293 * Main loop for reading an archive.
294 * Return 0 on success, -1 on error.
296 static int
297 extfs_read_archive (int fstype, const char *name, struct archive **pparc)
299 FILE *extfsd;
300 char *buffer;
301 struct archive *current_archive;
302 char *current_file_name, *current_link_name;
304 if ((extfsd =
305 extfs_open_archive (fstype, name, &current_archive)) == NULL) {
306 message (1, MSG_ERROR, _("Cannot open %s archive\n%s"),
307 extfs_prefixes[fstype], name);
308 return -1;
311 buffer = g_malloc (4096);
312 while (fgets (buffer, 4096, extfsd) != NULL) {
313 struct stat hstat;
315 current_link_name = NULL;
316 if (vfs_parse_ls_lga
317 (buffer, &hstat, &current_file_name, &current_link_name)) {
318 struct entry *entry, *pent;
319 struct inode *inode;
320 char *p, *q, *cfn = current_file_name;
322 if (*cfn) {
323 if (*cfn == '/')
324 cfn++;
325 p = strchr (cfn, 0);
326 if (p != cfn && *(p - 1) == '/')
327 *(p - 1) = 0;
328 p = strrchr (cfn, '/');
329 if (p == NULL) {
330 p = cfn;
331 q = strchr (cfn, 0);
332 } else {
333 *(p++) = 0;
334 q = cfn;
336 if (S_ISDIR (hstat.st_mode)
337 && (!strcmp (p, ".") || !strcmp (p, "..")))
338 goto read_extfs_continue;
339 pent =
340 extfs_find_entry (current_archive->root_entry, q, 1,
342 if (pent == NULL) {
343 /* FIXME: Should clean everything one day */
344 g_free (buffer);
345 pclose (extfsd);
346 close_error_pipe (1, _("Inconsistent extfs archive"));
347 return -1;
349 entry = g_new (struct entry, 1);
350 entry->name = g_strdup (p);
351 entry->next_in_dir = NULL;
352 entry->dir = pent;
353 if (pent != NULL) {
354 if (pent->inode->last_in_subdir) {
355 pent->inode->last_in_subdir->next_in_dir = entry;
356 pent->inode->last_in_subdir = entry;
359 if (!S_ISLNK (hstat.st_mode) && current_link_name != NULL) {
360 pent =
361 extfs_find_entry (current_archive->root_entry,
362 current_link_name, 0, 0);
363 if (pent == NULL) {
364 /* FIXME: Should clean everything one day */
365 g_free (buffer);
366 pclose (extfsd);
367 close_error_pipe (1,
368 _("Inconsistent extfs archive"));
369 return -1;
370 } else {
371 entry->inode = pent->inode;
372 pent->inode->nlink++;
374 } else {
375 inode = g_new (struct inode, 1);
376 entry->inode = inode;
377 inode->local_filename = NULL;
378 inode->inode = (current_archive->inode_counter)++;
379 inode->nlink = 1;
380 inode->dev = current_archive->rdev;
381 inode->archive = current_archive;
382 inode->mode = hstat.st_mode;
383 #ifdef HAVE_STRUCT_STAT_ST_RDEV
384 inode->rdev = hstat.st_rdev;
385 #else
386 inode->rdev = 0;
387 #endif
388 inode->uid = hstat.st_uid;
389 inode->gid = hstat.st_gid;
390 inode->size = hstat.st_size;
391 inode->mtime = hstat.st_mtime;
392 inode->atime = hstat.st_atime;
393 inode->ctime = hstat.st_ctime;
394 if (current_link_name != NULL
395 && S_ISLNK (hstat.st_mode)) {
396 inode->linkname = current_link_name;
397 current_link_name = NULL;
398 } else {
399 if (S_ISLNK (hstat.st_mode))
400 inode->mode &= ~S_IFLNK; /* You *DON'T* want to do this always */
401 inode->linkname = NULL;
403 if (S_ISDIR (hstat.st_mode))
404 extfs_make_dots (entry);
407 read_extfs_continue:
408 g_free (current_file_name);
409 if (current_link_name != NULL)
410 g_free (current_link_name);
414 /* Check if extfs 'list' returned 0 */
415 if (pclose (extfsd) != 0) {
416 g_free (buffer);
417 extfs_free (current_archive);
418 close_error_pipe (1, _("Inconsistent extfs archive"));
419 return -1;
422 close_error_pipe (1, NULL);
423 *pparc = current_archive;
424 g_free (buffer);
425 return 0;
429 * Dissect the path and create corresponding superblock. Note that inname
430 * can be changed and the result may point inside the original string.
432 static char *
433 extfs_get_path_mangle (char *inname, struct archive **archive, int is_dir,
434 int do_not_open)
436 char *local, *op;
437 const char *archive_name;
438 int result = -1;
439 struct archive *parc;
440 int fstype;
442 archive_name = inname;
443 vfs_split (inname, &local, &op);
446 * FIXME: we really should pass self pointer. But as we know that
447 * extfs_which does not touch struct vfs_class *me, it does not matter for now
449 fstype = extfs_which (NULL, op);
451 if (fstype == -1)
452 return NULL;
453 if (!local)
454 local = "";
457 * All filesystems should have some local archive, at least
458 * it can be '/'.
460 for (parc = first_archive; parc != NULL; parc = parc->next)
461 if (parc->name) {
462 if (!strcmp (parc->name, archive_name)) {
463 vfs_stamp (&vfs_extfs_ops, (vfsid) parc);
464 goto return_success;
468 result =
469 do_not_open ? -1 : extfs_read_archive (fstype, archive_name,
470 &parc);
471 if (result == -1)
472 ERRNOR (EIO, NULL);
474 return_success:
475 *archive = parc;
476 return local;
481 * Dissect the path and create corresponding superblock.
482 * The result should be freed.
484 static char *
485 extfs_get_path (const char *inname, struct archive **archive, int is_dir,
486 int do_not_open)
488 char *buf = g_strdup (inname);
489 char *res = extfs_get_path_mangle (buf, archive, is_dir, do_not_open);
490 char *res2 = NULL;
491 if (res)
492 res2 = g_strdup (res);
493 g_free (buf);
494 return res2;
498 /* Return allocated path (without leading slash) inside the archive */
499 static char *extfs_get_path_from_entry (struct entry *entry)
501 struct list {
502 struct list *next;
503 char *name;
504 } *head, *p;
505 char *localpath;
506 size_t len;
508 for (len = 0, head = 0; entry->dir; entry = entry->dir) {
509 p = g_new (struct list, 1);
510 p->next = head;
511 p->name = entry->name;
512 head = p;
513 len += strlen (entry->name) + 1;
516 if (len == 0)
517 return g_strdup ("");
519 localpath = g_malloc (len);
520 *localpath = '\0';
521 while (head) {
522 strcat (localpath, head->name);
523 if (head->next)
524 strcat (localpath, "/");
525 p = head;
526 head = head->next;
527 g_free (p);
529 return (localpath);
533 struct loop_protect {
534 struct entry *entry;
535 struct loop_protect *next;
537 static int errloop;
538 static int notadir;
540 static struct entry *
541 extfs_find_entry_int (struct entry *dir, char *name,
542 struct loop_protect *list, int make_dirs, int make_file);
544 static struct entry *
545 extfs_resolve_symlinks_int (struct entry *entry,
546 struct loop_protect *list)
548 struct entry *pent;
549 struct loop_protect *looping;
551 if (!S_ISLNK (entry->inode->mode))
552 return entry;
553 for (looping = list; looping != NULL; looping = looping->next)
554 if (entry == looping->entry) { /* Here we protect us against symlink looping */
555 errloop = 1;
556 return NULL;
558 looping = g_new (struct loop_protect, 1);
559 looping->entry = entry;
560 looping->next = list;
561 pent = extfs_find_entry_int (entry->dir, entry->inode->linkname, looping, 0, 0);
562 g_free (looping);
563 if (pent == NULL)
564 my_errno = ENOENT;
565 return pent;
568 static struct entry *extfs_resolve_symlinks (struct entry *entry)
570 struct entry *res;
572 errloop = 0;
573 notadir = 0;
574 res = extfs_resolve_symlinks_int (entry, NULL);
575 if (res == NULL) {
576 if (errloop)
577 my_errno = ELOOP;
578 else if (notadir)
579 my_errno = ENOTDIR;
581 return res;
584 static char *extfs_get_archive_name (struct archive *archive)
586 char *archive_name;
588 if (archive->local_name)
589 archive_name = archive->local_name;
590 else
591 archive_name = archive->name;
593 if (!archive_name || !*archive_name)
594 return "no_archive_name";
595 else
596 return archive_name;
599 /* Don't pass localname as NULL */
600 static int
601 extfs_cmd (const char *extfs_cmd, struct archive *archive,
602 struct entry *entry, const char *localname)
604 char *file;
605 char *quoted_file;
606 char *quoted_localname;
607 char *archive_name;
608 char *mc_extfsdir;
609 char *cmd;
610 int retval;
612 file = extfs_get_path_from_entry (entry);
613 quoted_file = name_quote (file, 0);
614 g_free (file);
615 archive_name = name_quote (extfs_get_archive_name (archive), 0);
616 quoted_localname = name_quote (localname, 0);
618 mc_extfsdir = concat_dir_and_file (mc_home, "extfs" PATH_SEP_STR);
619 cmd = g_strconcat (mc_extfsdir, extfs_prefixes[archive->fstype],
620 extfs_cmd, archive_name, " ", quoted_file, " ",
621 quoted_localname, NULL);
622 g_free (quoted_file);
623 g_free (quoted_localname);
624 g_free (mc_extfsdir);
625 g_free (archive_name);
627 open_error_pipe ();
628 retval = my_system (EXECUTE_AS_SHELL, shell, cmd);
629 g_free (cmd);
630 close_error_pipe (1, NULL);
631 return retval;
634 static void
635 extfs_run (const char *file)
637 struct archive *archive;
638 char *p, *q, *archive_name, *mc_extfsdir;
639 char *cmd;
641 if ((p = extfs_get_path (file, &archive, 0, 0)) == NULL)
642 return;
643 q = name_quote (p, 0);
644 g_free (p);
646 archive_name = name_quote (extfs_get_archive_name (archive), 0);
647 mc_extfsdir = concat_dir_and_file (mc_home, "extfs" PATH_SEP_STR);
648 cmd = g_strconcat (mc_extfsdir, extfs_prefixes[archive->fstype],
649 " run ", archive_name, " ", q, NULL);
650 g_free (mc_extfsdir);
651 g_free (archive_name);
652 g_free (q);
653 shell_execute (cmd, 0);
654 g_free (cmd);
657 static void *
658 extfs_open (struct vfs_class *me, const char *file, int flags, int mode)
660 struct pseudofile *extfs_info;
661 struct archive *archive;
662 char *q;
663 struct entry *entry;
664 int local_handle;
665 int created = 0;
667 if ((q = extfs_get_path (file, &archive, 0, 0)) == NULL)
668 return NULL;
669 entry = extfs_find_entry (archive->root_entry, q, 0, 0);
670 if (entry == NULL && (flags & O_CREAT)) {
671 /* Create new entry */
672 entry = extfs_find_entry (archive->root_entry, q, 0, 1);
673 created = (entry != NULL);
676 g_free (q);
677 if (entry == NULL)
678 return NULL;
679 if ((entry = extfs_resolve_symlinks (entry)) == NULL)
680 return NULL;
682 if (S_ISDIR (entry->inode->mode))
683 ERRNOR (EISDIR, NULL);
685 if (entry->inode->local_filename == NULL) {
686 char *local_filename;
688 local_handle = vfs_mkstemps (&local_filename, "extfs", entry->name);
690 if (local_handle == -1)
691 return NULL;
692 close (local_handle);
694 if (!created && !(flags & O_TRUNC)
695 && extfs_cmd (" copyout ", archive, entry, local_filename)) {
696 unlink (local_filename);
697 free (local_filename);
698 my_errno = EIO;
699 return NULL;
701 entry->inode->local_filename = local_filename;
704 local_handle =
705 open (entry->inode->local_filename, NO_LINEAR (flags), mode);
706 if (local_handle == -1)
707 ERRNOR (EIO, NULL);
709 extfs_info = g_new (struct pseudofile, 1);
710 extfs_info->archive = archive;
711 extfs_info->entry = entry;
712 extfs_info->has_changed = created;
713 extfs_info->local_handle = local_handle;
715 /* i.e. we had no open files and now we have one */
716 vfs_rmstamp (&vfs_extfs_ops, (vfsid) archive);
717 archive->fd_usage++;
718 return extfs_info;
721 static int extfs_read (void *data, char *buffer, int count)
723 struct pseudofile *file = (struct pseudofile *)data;
725 return read (file->local_handle, buffer, count);
728 static int
729 extfs_close (void *data)
731 struct pseudofile *file;
732 int errno_code = 0;
733 file = (struct pseudofile *) data;
735 close (file->local_handle);
737 /* Commit the file if it has changed */
738 if (file->has_changed) {
739 if (extfs_cmd
740 (" copyin ", file->archive, file->entry,
741 file->entry->inode->local_filename))
742 errno_code = EIO;
744 struct stat file_status;
745 if (stat (file->entry->inode->local_filename, &file_status) !=
747 errno_code = EIO;
748 else
749 file->entry->inode->size = file_status.st_size;
752 file->entry->inode->mtime = time (NULL);
755 file->archive->fd_usage--;
756 if (!file->archive->fd_usage)
757 vfs_stamp_create (&vfs_extfs_ops, file->archive);
759 g_free (data);
760 if (errno_code)
761 ERRNOR (EIO, -1);
762 return 0;
765 #define RECORDSIZE 512
767 static struct entry*
768 extfs_find_entry_int (struct entry *dir, char *name,
769 struct loop_protect *list, int make_dirs, int make_file)
771 struct entry *pent, *pdir;
772 char *p, *q, *name_end;
773 char c;
775 if (*name == '/') { /* Handle absolute paths */
776 name++;
777 dir = dir->inode->archive->root_entry;
780 pent = dir;
781 p = name;
782 name_end = name + strlen (name);
783 q = strchr (p, '/');
784 c = '/';
785 if (!q)
786 q = strchr (p, 0);
788 for (; pent != NULL && c && *p; ){
789 c = *q;
790 *q = 0;
792 if (strcmp (p, ".")){
793 if (!strcmp (p, ".."))
794 pent = pent->dir;
795 else {
796 if ((pent = extfs_resolve_symlinks_int (pent, list))==NULL){
797 *q = c;
798 return NULL;
800 if (!S_ISDIR (pent->inode->mode)){
801 *q = c;
802 notadir = 1;
803 return NULL;
805 pdir = pent;
806 for (pent = pent->inode->first_in_subdir; pent; pent = pent->next_in_dir)
807 /* Hack: I keep the original semanthic unless
808 q+1 would break in the strchr */
809 if (!strcmp (pent->name, p)){
810 if (q + 1 > name_end){
811 *q = c;
812 notadir = !S_ISDIR (pent->inode->mode);
813 return pent;
815 break;
818 /* When we load archive, we create automagically
819 * non-existant directories
821 if (pent == NULL && make_dirs) {
822 pent = extfs_generate_entry (dir->inode->archive, p, pdir, S_IFDIR | 0777);
824 if (pent == NULL && make_file) {
825 pent = extfs_generate_entry (dir->inode->archive, p, pdir, S_IFREG | 0666);
829 /* Next iteration */
830 *q = c;
831 p = q + 1;
832 q = strchr (p, '/');
833 if (!q)
834 q = strchr (p, 0);
836 if (pent == NULL)
837 my_errno = ENOENT;
838 return pent;
841 static struct entry *extfs_find_entry (struct entry *dir, char *name, int make_dirs, int make_file)
843 struct entry *res;
845 errloop = 0;
846 notadir = 0;
847 res = extfs_find_entry_int (dir, name, NULL, make_dirs, make_file);
848 if (res == NULL) {
849 if (errloop)
850 my_errno = ELOOP;
851 else if (notadir)
852 my_errno = ENOTDIR;
854 return res;
858 static int extfs_errno (struct vfs_class *me)
860 return my_errno;
863 static void * extfs_opendir (struct vfs_class *me, const char *dirname)
865 struct archive *archive;
866 char *q;
867 struct entry *entry;
868 struct entry **info;
870 if ((q = extfs_get_path (dirname, &archive, 1, 0)) == NULL)
871 return NULL;
872 entry = extfs_find_entry (archive->root_entry, q, 0, 0);
873 g_free (q);
874 if (entry == NULL)
875 return NULL;
876 if ((entry = extfs_resolve_symlinks (entry)) == NULL)
877 return NULL;
878 if (!S_ISDIR (entry->inode->mode)) ERRNOR (ENOTDIR, NULL);
880 info = g_new (struct entry *, 2);
881 info[0] = entry->inode->first_in_subdir;
882 info[1] = entry->inode->first_in_subdir;
884 return info;
887 static void * extfs_readdir(void *data)
889 static union vfs_dirent dir;
890 struct entry **info = (struct entry **) data;
892 if (!*info)
893 return NULL;
895 g_strlcpy(dir.dent.d_name, (*info)->name, MC_MAXPATHLEN);
897 compute_namelen(&dir.dent);
898 *info = (*info)->next_in_dir;
900 return (void *) &dir;
903 static int extfs_closedir (void *data)
905 g_free (data);
906 return 0;
909 static void extfs_stat_move( struct stat *buf, struct inode *inode )
911 buf->st_dev = inode->dev;
912 buf->st_ino = inode->inode;
913 buf->st_mode = inode->mode;
914 buf->st_nlink = inode->nlink;
915 buf->st_uid = inode->uid;
916 buf->st_gid = inode->gid;
917 #ifdef HAVE_STRUCT_STAT_ST_RDEV
918 buf->st_rdev = inode->rdev;
919 #endif
920 buf->st_size = inode->size;
921 #ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
922 buf->st_blksize = RECORDSIZE;
923 #endif
924 #ifdef HAVE_STRUCT_STAT_ST_BLOCKS
925 buf->st_blocks = (inode->size + RECORDSIZE - 1) / RECORDSIZE;
926 #endif
927 buf->st_atime = inode->atime;
928 buf->st_mtime = inode->mtime;
929 buf->st_ctime = inode->ctime;
932 static int extfs_internal_stat (const char *path, struct stat *buf, int resolve)
934 struct archive *archive;
935 char *q;
936 struct entry *entry;
937 struct inode *inode;
938 char *path2 = g_strdup(path);
939 int result = -1;
941 if ((q = extfs_get_path_mangle (path2, &archive, 0, 0)) == NULL)
942 goto cleanup;
943 entry = extfs_find_entry (archive->root_entry, q, 0, 0);
944 if (entry == NULL)
945 goto cleanup;
946 if (resolve && (entry = extfs_resolve_symlinks (entry)) == NULL)
947 goto cleanup;
948 inode = entry->inode;
949 extfs_stat_move( buf, inode );
950 result = 0;
951 cleanup:
952 g_free (path2);
953 return result;
956 static int extfs_stat (struct vfs_class *me, const char *path, struct stat *buf)
958 return extfs_internal_stat (path, buf, 1);
961 static int extfs_lstat (struct vfs_class *me, const char *path, struct stat *buf)
963 return extfs_internal_stat (path, buf, 0);
966 static int extfs_fstat (void *data, struct stat *buf)
968 struct pseudofile *file = (struct pseudofile *)data;
969 struct inode *inode;
971 inode = file->entry->inode;
972 extfs_stat_move( buf, inode );
973 return 0;
976 static int
977 extfs_readlink (struct vfs_class *me, const char *path, char *buf, int size)
979 struct archive *archive;
980 char *q;
981 int i;
982 struct entry *entry;
983 char *mpath = g_strdup(path);
984 int result = -1;
986 if ((q = extfs_get_path_mangle (mpath, &archive, 0, 0)) == NULL)
987 goto cleanup;
988 entry = extfs_find_entry (archive->root_entry, q, 0, 0);
989 if (entry == NULL)
990 goto cleanup;
991 if (!S_ISLNK (entry->inode->mode)) {
992 me->verrno = EINVAL;
993 goto cleanup;
995 if (size < (i = strlen (entry->inode->linkname))) {
996 i = size;
998 strncpy (buf, entry->inode->linkname, i);
999 result = i;
1000 cleanup:
1001 g_free (mpath);
1002 return result;
1005 static int extfs_chmod (struct vfs_class *me, const char *path, int mode)
1007 return 0;
1010 static int extfs_write (void *data, const char *buf, int nbyte)
1012 struct pseudofile *file = (struct pseudofile *)data;
1014 file->has_changed = 1;
1015 return write (file->local_handle, buf, nbyte);
1018 static int extfs_unlink (struct vfs_class *me, const char *file)
1020 struct archive *archive;
1021 char *q, *mpath = g_strdup(file);
1022 struct entry *entry;
1023 int result = -1;
1025 if ((q = extfs_get_path_mangle (mpath, &archive, 0, 0)) == NULL)
1026 return -1;
1027 entry = extfs_find_entry (archive->root_entry, q, 0, 0);
1028 if (entry == NULL)
1029 goto cleanup;
1030 if ((entry = extfs_resolve_symlinks (entry)) == NULL)
1031 goto cleanup;
1032 if (S_ISDIR (entry->inode->mode)) {
1033 me->verrno = EISDIR;
1034 goto cleanup;
1036 if (extfs_cmd (" rm ", archive, entry, "")){
1037 my_errno = EIO;
1038 goto cleanup;
1040 extfs_remove_entry (entry);
1041 result = 0;
1042 cleanup:
1043 g_free (mpath);
1044 return result;
1047 static int extfs_mkdir (struct vfs_class *me, const char *path, mode_t mode)
1049 struct archive *archive;
1050 char *q, *mpath = g_strdup(path);
1051 struct entry *entry;
1052 int result = -1;
1054 if ((q = extfs_get_path_mangle (mpath, &archive, 0, 0)) == NULL)
1055 goto cleanup;
1056 entry = extfs_find_entry (archive->root_entry, q, 0, 0);
1057 if (entry != NULL) {
1058 me->verrno = EEXIST;
1059 goto cleanup;
1061 entry = extfs_find_entry (archive->root_entry, q, 1, 0);
1062 if (entry == NULL)
1063 goto cleanup;
1064 if ((entry = extfs_resolve_symlinks (entry)) == NULL)
1065 goto cleanup;
1066 if (!S_ISDIR (entry->inode->mode)) {
1067 me->verrno = ENOTDIR;
1068 goto cleanup;
1071 if (extfs_cmd (" mkdir ", archive, entry, "")){
1072 my_errno = EIO;
1073 extfs_remove_entry (entry);
1074 goto cleanup;
1076 result = 0;
1077 cleanup:
1078 g_free (mpath);
1079 return result;
1082 static int extfs_rmdir (struct vfs_class *me, const char *path)
1084 struct archive *archive;
1085 char *q, *mpath = g_strdup(path);
1086 struct entry *entry;
1087 int result = -1;
1089 if ((q = extfs_get_path_mangle (mpath, &archive, 0, 0)) == NULL)
1090 goto cleanup;
1091 entry = extfs_find_entry (archive->root_entry, q, 0, 0);
1092 if (entry == NULL)
1093 goto cleanup;
1094 if ((entry = extfs_resolve_symlinks (entry)) == NULL)
1095 goto cleanup;
1096 if (!S_ISDIR (entry->inode->mode)) {
1097 me->verrno = ENOTDIR;
1098 goto cleanup;
1101 if (extfs_cmd (" rmdir ", archive, entry, "")){
1102 my_errno = EIO;
1103 goto cleanup;
1105 extfs_remove_entry (entry);
1106 result = 0;
1107 cleanup:
1108 g_free (mpath);
1109 return result;
1112 static int
1113 extfs_chdir (struct vfs_class *me, const char *path)
1115 struct archive *archive;
1116 char *q;
1117 struct entry *entry;
1119 my_errno = ENOTDIR;
1120 if ((q = extfs_get_path (path, &archive, 1, 0)) == NULL)
1121 return -1;
1122 entry = extfs_find_entry (archive->root_entry, q, 0, 0);
1123 g_free (q);
1124 if (!entry)
1125 return -1;
1126 entry = extfs_resolve_symlinks (entry);
1127 if ((!entry) || (!S_ISDIR (entry->inode->mode)))
1128 return -1;
1129 my_errno = 0;
1130 return 0;
1133 static int extfs_lseek (void *data, off_t offset, int whence)
1135 struct pseudofile *file = (struct pseudofile *) data;
1137 return lseek (file->local_handle, offset, whence);
1140 static vfsid
1141 extfs_getid (struct vfs_class *me, const char *path)
1143 struct archive *archive;
1144 char *p;
1146 if (!(p = extfs_get_path (path, &archive, 1, 1)))
1147 return NULL;
1148 g_free (p);
1149 return (vfsid) archive;
1152 static int extfs_nothingisopen (vfsid id)
1154 if (((struct archive *)id)->fd_usage <= 0)
1155 return 1;
1156 return 0;
1159 static void extfs_remove_entry (struct entry *e)
1161 int i = --(e->inode->nlink);
1162 struct entry *pe, *ent, *prev;
1164 if (S_ISDIR (e->inode->mode) && e->inode->first_in_subdir != NULL) {
1165 struct entry *f = e->inode->first_in_subdir;
1166 e->inode->first_in_subdir = NULL;
1167 extfs_remove_entry (f);
1169 pe = e->dir;
1170 if (e == pe->inode->first_in_subdir)
1171 pe->inode->first_in_subdir = e->next_in_dir;
1173 prev = NULL;
1174 for (ent = pe->inode->first_in_subdir; ent && ent->next_in_dir;
1175 ent = ent->next_in_dir)
1176 if (e == ent->next_in_dir) {
1177 prev = ent;
1178 break;
1180 if (prev)
1181 prev->next_in_dir = e->next_in_dir;
1182 if (e == pe->inode->last_in_subdir)
1183 pe->inode->last_in_subdir = prev;
1185 if (i <= 0) {
1186 if (e->inode->local_filename != NULL) {
1187 unlink (e->inode->local_filename);
1188 free (e->inode->local_filename);
1190 if (e->inode->linkname != NULL)
1191 g_free (e->inode->linkname);
1192 g_free (e->inode);
1195 g_free (e->name);
1196 g_free (e);
1199 static void extfs_free_entry (struct entry *e)
1201 int i = --(e->inode->nlink);
1202 if (S_ISDIR (e->inode->mode) && e->inode->first_in_subdir != NULL) {
1203 struct entry *f = e->inode->first_in_subdir;
1205 e->inode->first_in_subdir = NULL;
1206 extfs_free_entry (f);
1208 if (i <= 0) {
1209 if (e->inode->local_filename != NULL) {
1210 unlink (e->inode->local_filename);
1211 free (e->inode->local_filename);
1213 if (e->inode->linkname != NULL)
1214 g_free (e->inode->linkname);
1215 g_free (e->inode);
1217 if (e->next_in_dir != NULL)
1218 extfs_free_entry (e->next_in_dir);
1219 g_free (e->name);
1220 g_free (e);
1223 static void extfs_free (vfsid id)
1225 struct archive *parc;
1226 struct archive *archive = (struct archive *)id;
1228 extfs_free_entry (archive->root_entry);
1229 if (archive == first_archive) {
1230 first_archive = archive->next;
1231 } else {
1232 for (parc = first_archive; parc != NULL; parc = parc->next)
1233 if (parc->next == archive)
1234 break;
1235 if (parc != NULL)
1236 parc->next = archive->next;
1238 extfs_free_archive (archive);
1241 static char *
1242 extfs_getlocalcopy (struct vfs_class *me, const char *path)
1244 struct pseudofile *fp =
1245 (struct pseudofile *) extfs_open (me, path, O_RDONLY, 0);
1246 char *p;
1248 if (fp == NULL)
1249 return NULL;
1250 if (fp->entry->inode->local_filename == NULL) {
1251 extfs_close ((void *) fp);
1252 return NULL;
1254 p = g_strdup (fp->entry->inode->local_filename);
1255 fp->archive->fd_usage++;
1256 extfs_close ((void *) fp);
1257 return p;
1260 static int
1261 extfs_ungetlocalcopy (struct vfs_class *me, const char *path,
1262 const char *local, int has_changed)
1264 struct pseudofile *fp =
1265 (struct pseudofile *) extfs_open (me, path, O_RDONLY, 0);
1267 if (fp == NULL)
1268 return 0;
1269 if (!strcmp (fp->entry->inode->local_filename, local)) {
1270 fp->archive->fd_usage--;
1271 fp->has_changed |= has_changed;
1272 extfs_close ((void *) fp);
1273 return 0;
1274 } else {
1275 /* Should not happen */
1276 extfs_close ((void *) fp);
1277 return 0;
1282 static int extfs_init (struct vfs_class *me)
1284 FILE *cfg;
1285 char *mc_extfsini;
1286 char key[256];
1288 mc_extfsini = concat_dir_and_file (mc_home, "extfs" PATH_SEP_STR "extfs.ini");
1289 cfg = fopen (mc_extfsini, "r");
1291 /* We may not use vfs_die() message or message or similar,
1292 * UI is not initialized at this time and message would not
1293 * appear on screen. */
1294 if (!cfg) {
1295 fprintf (stderr, _("Warning: file %s not found\n"), mc_extfsini);
1296 g_free (mc_extfsini);
1297 return 0;
1300 extfs_no = 0;
1301 while (extfs_no < MAXEXTFS && fgets (key, sizeof (key), cfg)) {
1302 char *c;
1304 /* Handle those with a trailing ':', those flag that the
1305 * file system does not require an archive to work
1308 if (*key == '[') {
1309 fprintf(stderr, "Warning: You need to update your %s file.\n",
1310 mc_extfsini);
1311 fclose(cfg);
1312 g_free (mc_extfsini);
1313 return 0;
1315 if (*key == '#' || *key == '\n')
1316 continue;
1318 if ((c = strchr (key, '\n'))){
1319 *c-- = 0;
1320 } else { /* Last line without newline or strlen (key) > 255 */
1321 c = &key [strlen (key) - 1];
1323 extfs_need_archive [extfs_no] = !(*c == ':');
1324 if (*c == ':')
1325 *c = 0;
1326 if (!(*key))
1327 continue;
1329 extfs_prefixes [extfs_no++] = g_strdup (key);
1331 fclose(cfg);
1332 g_free (mc_extfsini);
1333 return 1;
1336 /* Do NOT use me argument in this function */
1337 static int extfs_which (struct vfs_class *me, const char *path)
1339 int i;
1341 for (i = 0; i < extfs_no; i++)
1342 if (!strcmp (path, extfs_prefixes [i]))
1343 return i;
1344 return -1;
1347 static void extfs_done (struct vfs_class *me)
1349 int i;
1351 for (i = 0; i < extfs_no; i++ )
1352 g_free (extfs_prefixes [i]);
1353 extfs_no = 0;
1356 static int
1357 extfs_setctl (struct vfs_class *me, const char *path, int ctlop, void *arg)
1359 if (ctlop == VFS_SETCTL_RUN) {
1360 extfs_run (path);
1361 return 1;
1363 return 0;
1366 void
1367 init_extfs (void)
1369 vfs_extfs_ops.name = "extfs";
1370 vfs_extfs_ops.init = extfs_init;
1371 vfs_extfs_ops.done = extfs_done;
1372 vfs_extfs_ops.fill_names = extfs_fill_names;
1373 vfs_extfs_ops.which = extfs_which;
1374 vfs_extfs_ops.open = extfs_open;
1375 vfs_extfs_ops.close = extfs_close;
1376 vfs_extfs_ops.read = extfs_read;
1377 vfs_extfs_ops.write = extfs_write;
1378 vfs_extfs_ops.opendir = extfs_opendir;
1379 vfs_extfs_ops.readdir = extfs_readdir;
1380 vfs_extfs_ops.closedir = extfs_closedir;
1381 vfs_extfs_ops.stat = extfs_stat;
1382 vfs_extfs_ops.lstat = extfs_lstat;
1383 vfs_extfs_ops.fstat = extfs_fstat;
1384 vfs_extfs_ops.chmod = extfs_chmod;
1385 vfs_extfs_ops.readlink = extfs_readlink;
1386 vfs_extfs_ops.unlink = extfs_unlink;
1387 vfs_extfs_ops.chdir = extfs_chdir;
1388 vfs_extfs_ops.ferrno = extfs_errno;
1389 vfs_extfs_ops.lseek = extfs_lseek;
1390 vfs_extfs_ops.getid = extfs_getid;
1391 vfs_extfs_ops.nothingisopen = extfs_nothingisopen;
1392 vfs_extfs_ops.free = extfs_free;
1393 vfs_extfs_ops.getlocalcopy = extfs_getlocalcopy;
1394 vfs_extfs_ops.ungetlocalcopy = extfs_ungetlocalcopy;
1395 vfs_extfs_ops.mkdir = extfs_mkdir;
1396 vfs_extfs_ops.rmdir = extfs_rmdir;
1397 vfs_extfs_ops.setctl = extfs_setctl;
1398 vfs_register_class (&vfs_extfs_ops);