Revert "(extfs_open_archive): fix NULL dereferences."
[midnight-commander.git] / src / vfs / extfs / extfs.c
blobc6c536bae89d403d98bf42df175ac1e211f669bd
1 /*
2 Virtual File System: External file system.
4 Copyright (C) 1995-2022
5 Free Software Foundation, Inc.
7 Written by:
8 Jakub Jelinek, 1995
9 Pavel Machek, 1998
10 Andrew T. Veliath, 1999
11 Slava Zanko <slavazanko@gmail.com>, 2013
13 This file is part of the Midnight Commander.
15 The Midnight Commander is free software: you can redistribute it
16 and/or modify it under the terms of the GNU General Public License as
17 published by the Free Software Foundation, either version 3 of the License,
18 or (at your option) any later version.
20 The Midnight Commander is distributed in the hope that it will be useful,
21 but WITHOUT ANY WARRANTY; without even the implied warranty of
22 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 GNU General Public License for more details.
25 You should have received a copy of the GNU General Public License
26 along with this program. If not, see <http://www.gnu.org/licenses/>.
29 /**
30 * \file
31 * \brief Source: Virtual File System: External file system
32 * \author Jakub Jelinek
33 * \author Pavel Machek
34 * \author Andrew T. Veliath
35 * \date 1995, 1998, 1999
38 /* Namespace: init_extfs */
40 #include <config.h>
42 #include <stdio.h>
43 #include <ctype.h>
44 #include <string.h>
45 #include <stdlib.h>
46 #include <sys/types.h>
47 #include <sys/stat.h>
48 #include <unistd.h>
49 #include <signal.h>
50 #include <errno.h>
51 #include <sys/wait.h>
53 #include "lib/global.h"
54 #include "lib/fileloc.h"
55 #include "lib/mcconfig.h"
56 #include "lib/util.h"
57 #include "lib/widget.h" /* message() */
59 #include "src/execute.h" /* For shell_execute */
61 #include "lib/vfs/vfs.h"
62 #include "lib/vfs/utilvfs.h"
63 #include "lib/vfs/xdirentry.h"
64 #include "lib/vfs/gc.h" /* vfs_rmstamp */
66 #include "extfs.h"
68 /*** global variables ****************************************************************************/
70 /*** file scope macro definitions ****************************************************************/
72 #undef ERRNOR
73 #define ERRNOR(x,y) do { my_errno = x; return y; } while(0)
75 #define RECORDSIZE 512
77 #define EXTFS_SUPER(a) ((struct extfs_super_t *) (a))
79 /*** file scope type declarations ****************************************************************/
81 struct extfs_super_t
83 struct vfs_s_super base; /* base class */
85 int fstype;
86 char *local_name;
87 struct stat local_stat;
88 dev_t rdev;
91 typedef struct
93 char *path;
94 char *prefix;
95 gboolean need_archive;
96 } extfs_plugin_info_t;
98 /*** file scope variables ************************************************************************/
100 static GArray *extfs_plugins = NULL;
102 static gboolean errloop;
103 static gboolean notadir;
105 static struct vfs_s_subclass extfs_subclass;
106 static struct vfs_class *vfs_extfs_ops = VFS_CLASS (&extfs_subclass);
108 static int my_errno = 0;
110 /* --------------------------------------------------------------------------------------------- */
111 /*** file scope functions ************************************************************************/
112 /* --------------------------------------------------------------------------------------------- */
114 static struct vfs_s_entry *extfs_resolve_symlinks_int (struct vfs_s_entry *entry, GSList * list);
116 /* --------------------------------------------------------------------------------------------- */
118 static struct extfs_super_t *
119 extfs_super_new (struct vfs_class *me, const char *name, const vfs_path_t * local_name_vpath,
120 int fstype)
122 struct extfs_super_t *super;
123 struct vfs_s_super *vsuper;
125 super = g_new0 (struct extfs_super_t, 1);
126 vsuper = VFS_SUPER (super);
128 vsuper->me = me;
129 vsuper->name = g_strdup (name);
131 super->fstype = fstype;
133 if (local_name_vpath != NULL)
135 super->local_name = g_strdup (vfs_path_get_last_path_str (local_name_vpath));
136 mc_stat (local_name_vpath, &super->local_stat);
139 VFS_SUBCLASS (me)->supers = g_list_prepend (VFS_SUBCLASS (me)->supers, super);
141 return super;
144 /* --------------------------------------------------------------------------------------------- */
146 /* unlike vfs_s_new_entry(), inode->ent is kept */
147 static struct vfs_s_entry *
148 extfs_entry_new (struct vfs_class *me, const char *name, struct vfs_s_inode *inode)
150 struct vfs_s_entry *entry;
152 (void) me;
154 entry = g_new0 (struct vfs_s_entry, 1);
156 entry->name = g_strdup (name);
157 entry->ino = inode;
159 return entry;
162 /* --------------------------------------------------------------------------------------------- */
164 static void
165 extfs_fill_name (void *data, void *user_data)
167 struct vfs_s_super *a = VFS_SUPER (data);
168 fill_names_f func = (fill_names_f) user_data;
169 extfs_plugin_info_t *info;
170 char *name;
172 info = &g_array_index (extfs_plugins, extfs_plugin_info_t, EXTFS_SUPER (a)->fstype);
173 name =
174 g_strconcat (a->name != NULL ? a->name : "", PATH_SEP_STR, info->prefix,
175 VFS_PATH_URL_DELIMITER, (char *) NULL);
176 func (name);
177 g_free (name);
180 /* --------------------------------------------------------------------------------------------- */
182 static gint
183 extfs_cmp_archive (const void *a, const void *b)
185 const struct vfs_s_super *ar = (const struct vfs_s_super *) a;
186 const char *archive_name = (const char *) b;
188 return (ar->name != NULL && strcmp (ar->name, archive_name) == 0) ? 0 : 1;
191 /* --------------------------------------------------------------------------------------------- */
193 static struct vfs_s_entry *
194 extfs_generate_entry (struct extfs_super_t *archive, const char *name, struct vfs_s_inode *parent,
195 mode_t mode)
197 struct vfs_class *me = VFS_SUPER (archive)->me;
198 struct stat st;
199 mode_t myumask;
200 struct vfs_s_inode *inode;
201 struct vfs_s_entry *entry;
203 memset (&st, 0, sizeof (st));
204 st.st_ino = VFS_SUPER (archive)->ino_usage++;
205 st.st_dev = archive->rdev;
206 myumask = umask (022);
207 umask (myumask);
208 st.st_mode = mode & ~myumask;
209 st.st_uid = getuid ();
210 st.st_gid = getgid ();
211 st.st_mtime = time (NULL);
212 st.st_atime = st.st_mtime;
213 st.st_ctime = st.st_mtime;
214 st.st_nlink = 1;
216 inode = vfs_s_new_inode (me, VFS_SUPER (archive), &st);
217 entry = vfs_s_new_entry (me, name, inode);
218 if (parent != NULL)
219 vfs_s_insert_entry (me, parent, entry);
221 return entry;
224 /* --------------------------------------------------------------------------------------------- */
226 static struct vfs_s_entry *
227 extfs_find_entry_int (struct vfs_s_inode *dir, const char *name, GSList * list, int flags)
229 struct vfs_s_entry *pent, *pdir;
230 const char *p, *name_end;
231 char *q;
232 char c = PATH_SEP;
233 struct extfs_super_t *super;
235 if (g_path_is_absolute (name))
237 /* Handle absolute paths */
238 name = g_path_skip_root (name);
239 dir = dir->super->root;
242 super = EXTFS_SUPER (dir->super);
243 pent = dir->ent;
244 p = name;
245 name_end = name + strlen (name);
247 while ((pent != NULL) && (c != '\0') && (*p != '\0'))
249 q = strchr (p, PATH_SEP);
250 if (q == NULL)
251 q = (char *) name_end;
253 c = *q;
254 *q = '\0';
256 if (DIR_IS_DOTDOT (p))
257 pent = pent->dir->ent;
258 else
260 GList *pl;
262 pent = extfs_resolve_symlinks_int (pent, list);
263 if (pent == NULL)
265 *q = c;
266 return NULL;
269 if (!S_ISDIR (pent->ino->st.st_mode))
271 *q = c;
272 notadir = TRUE;
273 return NULL;
276 pdir = pent;
277 pl = g_queue_find_custom (pent->ino->subdir, p, vfs_s_entry_compare);
278 pent = pl != NULL ? VFS_ENTRY (pl->data) : NULL;
279 if (pent != NULL && q + 1 > name_end)
281 /* Hack: I keep the original semanthic unless q+1 would break in the strchr */
282 *q = c;
283 notadir = !S_ISDIR (pent->ino->st.st_mode);
284 return pent;
287 /* When we load archive, we create automagically non-existent directories */
288 if (pent == NULL && (flags & FL_MKDIR) != 0)
289 pent = extfs_generate_entry (super, p, pdir->ino, S_IFDIR | 0777);
290 if (pent == NULL && (flags & FL_MKFILE) != 0)
291 pent = extfs_generate_entry (super, p, pdir->ino, S_IFREG | 0666);
294 /* Next iteration */
295 *q = c;
296 if (c != '\0')
297 p = q + 1;
299 if (pent == NULL)
300 my_errno = ENOENT;
301 return pent;
304 /* --------------------------------------------------------------------------------------------- */
306 static struct vfs_s_entry *
307 extfs_find_entry (struct vfs_s_inode *dir, const char *name, int flags)
309 struct vfs_s_entry *res;
311 errloop = FALSE;
312 notadir = FALSE;
314 res = extfs_find_entry_int (dir, name, NULL, flags);
315 if (res == NULL)
317 if (errloop)
318 my_errno = ELOOP;
319 else if (notadir)
320 my_errno = ENOTDIR;
322 return res;
325 /* --------------------------------------------------------------------------------------------- */
327 static void
328 extfs_fill_names (struct vfs_class *me, fill_names_f func)
330 g_list_foreach (VFS_SUBCLASS (me)->supers, extfs_fill_name, func);
333 /* --------------------------------------------------------------------------------------------- */
335 /* Create this function because VFSF_USETMP flag is not used in extfs */
336 static void
337 extfs_free_inode (struct vfs_class *me, struct vfs_s_inode *ino)
339 (void) me;
341 if (ino->localname != NULL)
343 unlink (ino->localname);
344 MC_PTR_FREE (ino->localname);
348 /* --------------------------------------------------------------------------------------------- */
350 static void
351 extfs_free_archive (struct vfs_class *me, struct vfs_s_super *psup)
353 struct extfs_super_t *archive = EXTFS_SUPER (psup);
355 (void) me;
357 if (archive->local_name != NULL)
359 struct stat my;
360 vfs_path_t *local_name_vpath, *name_vpath;
362 local_name_vpath = vfs_path_from_str (archive->local_name);
363 name_vpath = vfs_path_from_str (psup->name);
364 mc_stat (local_name_vpath, &my);
365 mc_ungetlocalcopy (name_vpath, local_name_vpath,
366 archive->local_stat.st_mtime != my.st_mtime);
367 vfs_path_free (local_name_vpath, TRUE);
368 vfs_path_free (name_vpath, TRUE);
369 g_free (archive->local_name);
373 /* --------------------------------------------------------------------------------------------- */
375 static inline char *
376 extfs_skip_leading_dotslash (char *s)
378 /* Skip leading "./" (if present).
379 * Some programs don't understand it:
381 * $ zip file.zip ./-file2.txt file1.txt
382 * adding: -file2.txt (stored 0%)
383 * adding: file1.txt (stored 0%)
384 * $ /usr/lib/mc/extfs.d/uzip copyout file.zip ./-file2.txt ./tmp-file2.txt
385 * caution: filename not matched: ./-file2.txt
387 if (s[0] == '.' && s[1] == PATH_SEP)
388 s += 2;
390 return s;
393 /* --------------------------------------------------------------------------------------------- */
395 static int
396 extfs_add_file (struct extfs_super_t *archive, const char *file_name)
398 struct vfs_s_super *super = VFS_SUPER (archive);
399 struct stat hstat;
400 char *current_file_name = NULL, *current_link_name = NULL;
401 int ret = 0;
403 if (vfs_parse_ls_lga (file_name, &hstat, &current_file_name, &current_link_name, NULL))
405 char *cfn = current_file_name;
407 if (*cfn != '\0')
409 struct vfs_s_entry *entry;
410 struct vfs_s_entry *pent = NULL;
411 struct vfs_s_inode *inode;
412 char *p, *q;
414 cfn = extfs_skip_leading_dotslash (cfn);
415 if (IS_PATH_SEP (*cfn))
416 cfn++;
417 p = strchr (cfn, '\0');
418 if (p != cfn && IS_PATH_SEP (p[-1]))
419 p[-1] = '\0';
420 p = strrchr (cfn, PATH_SEP);
421 if (p == NULL)
423 p = cfn;
424 q = strchr (cfn, '\0');
426 else
428 *(p++) = '\0';
429 q = cfn;
432 if (*q != '\0')
434 pent = extfs_find_entry (super->root, q, FL_MKDIR);
435 if (pent == NULL)
437 ret = -1;
438 goto done;
442 if (pent != NULL)
444 entry = extfs_entry_new (super->me, p, pent->ino);
445 entry->dir = pent->ino;
446 g_queue_push_tail (pent->ino->subdir, entry);
448 else
450 entry = extfs_entry_new (super->me, p, super->root);
451 entry->dir = super->root;
452 g_queue_push_tail (super->root->subdir, entry);
455 if (!S_ISLNK (hstat.st_mode) && (current_link_name != NULL))
457 pent = extfs_find_entry (super->root, current_link_name, FL_NONE);
458 if (pent == NULL)
460 ret = -1;
461 goto done;
464 pent->ino->st.st_nlink++;
465 entry->ino = pent->ino;
467 else
469 struct stat st;
471 memset (&st, 0, sizeof (st));
472 st.st_ino = super->ino_usage++;
473 st.st_nlink = 1;
474 st.st_dev = archive->rdev;
475 st.st_mode = hstat.st_mode;
476 #ifdef HAVE_STRUCT_STAT_ST_RDEV
477 st.st_rdev = hstat.st_rdev;
478 #endif
479 st.st_uid = hstat.st_uid;
480 st.st_gid = hstat.st_gid;
481 st.st_size = hstat.st_size;
482 st.st_mtime = hstat.st_mtime;
483 st.st_atime = hstat.st_atime;
484 st.st_ctime = hstat.st_ctime;
486 if (current_link_name == NULL && S_ISLNK (hstat.st_mode))
487 st.st_mode &= ~S_IFLNK; /* You *DON'T* want to do this always */
489 inode = vfs_s_new_inode (super->me, super, &st);
490 inode->ent = entry;
491 entry->ino = inode;
493 if (current_link_name != NULL && S_ISLNK (hstat.st_mode))
495 inode->linkname = current_link_name;
496 current_link_name = NULL;
501 done:
502 g_free (current_file_name);
503 g_free (current_link_name);
506 return ret;
509 /* --------------------------------------------------------------------------------------------- */
511 static mc_pipe_t *
512 extfs_open_archive (int fstype, const char *name, struct extfs_super_t **pparc, GError ** error)
514 const extfs_plugin_info_t *info;
515 static dev_t archive_counter = 0;
516 mc_pipe_t *result = NULL;
517 mode_t mode;
518 char *cmd;
519 struct stat mystat;
520 struct extfs_super_t *current_archive;
521 struct vfs_s_entry *root_entry;
522 char *tmp = NULL;
523 vfs_path_t *local_name_vpath = NULL;
524 vfs_path_t *name_vpath;
526 memset (&mystat, 0, sizeof (mystat));
528 name_vpath = vfs_path_from_str (name);
529 info = &g_array_index (extfs_plugins, extfs_plugin_info_t, fstype);
531 if (info->need_archive)
533 if (mc_stat (name_vpath, &mystat) == -1)
534 goto ret;
536 if (!vfs_file_is_local (name_vpath))
538 local_name_vpath = mc_getlocalcopy (name_vpath);
539 if (local_name_vpath == NULL)
540 goto ret;
543 tmp = name_quote (vfs_path_get_last_path_str (name_vpath), FALSE);
546 cmd = g_strconcat (info->path, info->prefix, " list ",
547 vfs_path_get_last_path_str (local_name_vpath) != NULL ?
548 vfs_path_get_last_path_str (local_name_vpath) : tmp, (char *) NULL);
549 g_free (tmp);
551 result = mc_popen (cmd, TRUE, TRUE, error);
552 g_free (cmd);
554 if (result == NULL)
556 if (local_name_vpath != NULL)
558 mc_ungetlocalcopy (name_vpath, local_name_vpath, FALSE);
559 vfs_path_free (local_name_vpath, TRUE);
561 goto ret;
564 current_archive = extfs_super_new (vfs_extfs_ops, name, local_name_vpath, fstype);
565 current_archive->rdev = archive_counter++;
566 vfs_path_free (local_name_vpath, TRUE);
568 mode = mystat.st_mode & 07777;
569 if (mode & 0400)
570 mode |= 0100;
571 if (mode & 0040)
572 mode |= 0010;
573 if (mode & 0004)
574 mode |= 0001;
575 mode |= S_IFDIR;
577 root_entry = extfs_generate_entry (current_archive, PATH_SEP_STR, NULL, mode);
578 root_entry->ino->st.st_uid = mystat.st_uid;
579 root_entry->ino->st.st_gid = mystat.st_gid;
580 root_entry->ino->st.st_atime = mystat.st_atime;
581 root_entry->ino->st.st_ctime = mystat.st_ctime;
582 root_entry->ino->st.st_mtime = mystat.st_mtime;
583 root_entry->ino->ent = root_entry;
584 VFS_SUPER (current_archive)->root = root_entry->ino;
586 *pparc = current_archive;
588 ret:
589 vfs_path_free (name_vpath, TRUE);
590 return result;
593 /* --------------------------------------------------------------------------------------------- */
595 * Main loop for reading an archive.
596 * Return 0 on success, -1 on error.
599 static int
600 extfs_read_archive (mc_pipe_t * pip, struct extfs_super_t *archive, GError ** error)
602 int ret = 0;
603 GString *buffer;
604 GString *err_msg = NULL;
605 GString *remain_file_name = NULL;
607 while (ret != -1)
609 /* init buffers before call of mc_pread() */
610 pip->out.len = MC_PIPE_BUFSIZE;
611 pip->err.len = MC_PIPE_BUFSIZE;
613 mc_pread (pip, error);
615 if (*error != NULL)
616 return (-1);
618 if (pip->err.len > 0)
620 /* join errors/warnings */
621 if (err_msg == NULL)
622 err_msg = g_string_new_len (pip->err.buf, pip->err.len);
623 else
625 if (err_msg->str[err_msg->len - 1] != '\n')
626 g_string_append_c (err_msg, '\n');
627 g_string_append_len (err_msg, pip->err.buf, pip->err.len);
631 if (pip->out.len == MC_PIPE_STREAM_EOF)
632 break;
634 if (pip->out.len == 0)
635 continue;
637 if (pip->out.len == MC_PIPE_ERROR_READ)
638 return (-1);
640 while (ret != -1 && (buffer = mc_pstream_get_string (&pip->out)) != NULL)
642 /* handle a \n-separated file list */
644 if (buffer->str[buffer->len - 1] == '\n')
646 /* entire file name or last chunk */
648 g_string_truncate (buffer, buffer->len - 1);
650 /* join filename chunks */
651 if (remain_file_name != NULL)
653 g_string_append_len (remain_file_name, buffer->str, buffer->len);
654 g_string_free (buffer, TRUE);
655 buffer = remain_file_name;
656 remain_file_name = NULL;
659 else
661 /* first or middle chunk of file name */
663 if (remain_file_name == NULL)
664 remain_file_name = buffer;
665 else
667 g_string_append_len (remain_file_name, buffer->str, buffer->len);
668 g_string_free (buffer, TRUE);
671 continue;
674 ret = extfs_add_file (archive, buffer->str);
676 g_string_free (buffer, TRUE);
680 if (remain_file_name != NULL)
681 g_string_free (remain_file_name, TRUE);
683 if (err_msg != NULL)
685 if (*error == NULL)
686 mc_propagate_error (error, 0, "%s", err_msg->str);
688 g_string_free (err_msg, TRUE);
691 return ret;
694 /* --------------------------------------------------------------------------------------------- */
696 static int
697 extfs_which (struct vfs_class *me, const char *path)
699 size_t path_len;
700 size_t i;
702 (void) me;
704 path_len = strlen (path);
706 for (i = 0; i < extfs_plugins->len; i++)
708 extfs_plugin_info_t *info;
710 info = &g_array_index (extfs_plugins, extfs_plugin_info_t, i);
712 if ((strncmp (path, info->prefix, path_len) == 0)
713 && ((info->prefix[path_len] == '\0') || (info->prefix[path_len] == '+')))
714 return i;
716 return -1;
719 /* --------------------------------------------------------------------------------------------- */
721 static int
722 extfs_open_and_read_archive (int fstype, const char *name, struct extfs_super_t **archive)
724 int result = -1;
725 struct extfs_super_t *a;
726 mc_pipe_t *pip;
727 GError *error = NULL;
729 pip = extfs_open_archive (fstype, name, archive, &error);
731 a = *archive;
733 if (pip == NULL)
735 const extfs_plugin_info_t *info;
737 info = &g_array_index (extfs_plugins, extfs_plugin_info_t, fstype);
738 message (D_ERROR, MSG_ERROR, _("Cannot open %s archive\n%s:\n%s"), info->prefix, name,
739 error->message);
740 g_error_free (error);
742 else
744 result = extfs_read_archive (pip, a, &error);
746 if (result != 0)
747 VFS_SUPER (a)->me->free (VFS_SUPER (a));
749 if (error != NULL)
751 message (D_ERROR, MSG_ERROR, _("EXTFS virtual file system:\n%s"), error->message);
752 g_error_free (error);
755 mc_pclose (pip, NULL);
758 return result;
761 /* --------------------------------------------------------------------------------------------- */
763 * Dissect the path and create corresponding superblock.
765 static const char *
766 extfs_get_path (const vfs_path_t * vpath, struct extfs_super_t **archive, int flags)
768 char *archive_name;
769 int result = -1;
770 GList *parc;
771 int fstype;
772 const vfs_path_element_t *path_element;
773 struct extfs_super_t *a = NULL;
775 path_element = vfs_path_get_by_index (vpath, -1);
777 fstype = extfs_which (path_element->class, path_element->vfs_prefix);
778 if (fstype == -1)
779 return NULL;
781 archive_name = vfs_path_to_str_elements_count (vpath, -1);
783 /* All filesystems should have some local archive, at least it can be PATH_SEP ('/'). */
784 parc = g_list_find_custom (extfs_subclass.supers, archive_name, extfs_cmp_archive);
785 if (parc != NULL)
787 a = EXTFS_SUPER (parc->data);
788 vfs_stamp (vfs_extfs_ops, (vfsid) a);
789 g_free (archive_name);
791 else
793 if ((flags & FL_NO_OPEN) == 0)
794 result = extfs_open_and_read_archive (fstype, archive_name, &a);
796 g_free (archive_name);
798 if (result == -1)
800 path_element->class->verrno = EIO;
801 return NULL;
805 *archive = a;
806 return path_element->path;
809 /* --------------------------------------------------------------------------------------------- */
810 /* Return allocated path (without leading slash) inside the archive */
812 static char *
813 extfs_get_path_from_entry (const struct vfs_s_entry *entry)
815 const struct vfs_s_entry *e;
816 GString *localpath;
818 localpath = g_string_new ("");
820 for (e = entry; e->dir != NULL; e = e->dir->ent)
822 g_string_prepend (localpath, e->name);
823 if (e->dir->ent->dir != NULL)
824 g_string_prepend_c (localpath, PATH_SEP);
827 return g_string_free (localpath, FALSE);
830 /* --------------------------------------------------------------------------------------------- */
832 static struct vfs_s_entry *
833 extfs_resolve_symlinks_int (struct vfs_s_entry *entry, GSList * list)
835 struct vfs_s_entry *pent = NULL;
837 if (!S_ISLNK (entry->ino->st.st_mode))
838 return entry;
840 if (g_slist_find (list, entry) != NULL)
842 /* Here we protect us against symlink looping */
843 errloop = TRUE;
845 else
847 GSList *looping;
849 looping = g_slist_prepend (list, entry);
850 pent = extfs_find_entry_int (entry->dir, entry->ino->linkname, looping, FL_NONE);
851 looping = g_slist_delete_link (looping, looping);
853 if (pent == NULL)
854 my_errno = ENOENT;
857 return pent;
860 /* --------------------------------------------------------------------------------------------- */
862 static struct vfs_s_entry *
863 extfs_resolve_symlinks (struct vfs_s_entry *entry)
865 struct vfs_s_entry *res;
867 errloop = FALSE;
868 notadir = FALSE;
869 res = extfs_resolve_symlinks_int (entry, NULL);
870 if (res == NULL)
872 if (errloop)
873 my_errno = ELOOP;
874 else if (notadir)
875 my_errno = ENOTDIR;
877 return res;
880 /* --------------------------------------------------------------------------------------------- */
882 static char *
883 extfs_get_archive_name (const struct extfs_super_t *archive)
885 const char *archive_name;
887 if (archive->local_name != NULL)
888 archive_name = archive->local_name;
889 else
890 archive_name = CONST_VFS_SUPER (archive)->name;
892 if (archive_name == NULL || *archive_name == '\0')
893 return g_strdup ("no_archive_name");
894 else
896 char *ret_str;
897 vfs_path_t *vpath;
898 const vfs_path_element_t *path_element;
900 vpath = vfs_path_from_str (archive_name);
901 path_element = vfs_path_get_by_index (vpath, -1);
902 ret_str = g_strdup (path_element->path);
903 vfs_path_free (vpath, TRUE);
904 return ret_str;
908 /* --------------------------------------------------------------------------------------------- */
909 /** Don't pass localname as NULL */
911 static int
912 extfs_cmd (const char *str_extfs_cmd, const struct extfs_super_t *archive,
913 const struct vfs_s_entry *entry, const char *localname)
915 char *file;
916 char *quoted_file;
917 char *quoted_localname;
918 char *archive_name, *quoted_archive_name;
919 const extfs_plugin_info_t *info;
920 char *cmd;
921 int retval = 0;
922 GError *error = NULL;
923 mc_pipe_t *pip;
925 file = extfs_get_path_from_entry (entry);
926 quoted_file = name_quote (file, FALSE);
927 g_free (file);
929 /* Skip leading "./" (if present) added in name_quote() */
930 file = extfs_skip_leading_dotslash (quoted_file);
932 archive_name = extfs_get_archive_name (archive);
933 quoted_archive_name = name_quote (archive_name, FALSE);
934 g_free (archive_name);
935 quoted_localname = name_quote (localname, FALSE);
936 info = &g_array_index (extfs_plugins, extfs_plugin_info_t, archive->fstype);
937 cmd = g_strconcat (info->path, info->prefix, str_extfs_cmd,
938 quoted_archive_name, " ", file, " ", quoted_localname, (char *) NULL);
939 g_free (quoted_file);
940 g_free (quoted_localname);
941 g_free (quoted_archive_name);
943 /* don't read stdout */
944 pip = mc_popen (cmd, FALSE, TRUE, &error);
945 g_free (cmd);
947 if (pip == NULL)
949 message (D_ERROR, MSG_ERROR, _("EXTFS virtual file system:\n%s"), error->message);
950 g_error_free (error);
951 return (-1);
954 pip->err.null_term = TRUE;
956 mc_pread (pip, &error);
957 if (error != NULL)
959 message (D_ERROR, MSG_ERROR, _("EXTFS virtual file system:\n%s"), error->message);
960 g_error_free (error);
961 retval = -1;
963 else if (pip->err.len > 0)
964 message (D_ERROR, MSG_ERROR, _("EXTFS virtual file system:\n%s"), pip->err.buf);
966 mc_pclose (pip, NULL);
968 return retval;
971 /* --------------------------------------------------------------------------------------------- */
973 static void
974 extfs_run (const vfs_path_t * vpath)
976 struct extfs_super_t *archive = NULL;
977 const char *p;
978 char *q, *archive_name, *quoted_archive_name;
979 char *cmd;
980 const extfs_plugin_info_t *info;
982 p = extfs_get_path (vpath, &archive, FL_NONE);
983 if (p == NULL)
984 return;
985 q = name_quote (p, FALSE);
987 archive_name = extfs_get_archive_name (archive);
988 quoted_archive_name = name_quote (archive_name, FALSE);
989 g_free (archive_name);
990 info = &g_array_index (extfs_plugins, extfs_plugin_info_t, archive->fstype);
991 cmd =
992 g_strconcat (info->path, info->prefix, " run ", quoted_archive_name, " ", q, (char *) NULL);
993 g_free (quoted_archive_name);
994 g_free (q);
995 shell_execute (cmd, 0);
996 g_free (cmd);
999 /* --------------------------------------------------------------------------------------------- */
1001 static void *
1002 extfs_open (const vfs_path_t * vpath, int flags, mode_t mode)
1004 vfs_file_handler_t *extfs_info;
1005 struct extfs_super_t *archive = NULL;
1006 const char *q;
1007 struct vfs_s_entry *entry;
1008 int local_handle;
1009 gboolean created = FALSE;
1011 q = extfs_get_path (vpath, &archive, FL_NONE);
1012 if (q == NULL)
1013 return NULL;
1014 entry = extfs_find_entry (VFS_SUPER (archive)->root, q, FL_NONE);
1015 if ((entry == NULL) && ((flags & O_CREAT) != 0))
1017 /* Create new entry */
1018 entry = extfs_find_entry (VFS_SUPER (archive)->root, q, FL_MKFILE);
1019 created = (entry != NULL);
1022 if (entry == NULL)
1023 return NULL;
1024 entry = extfs_resolve_symlinks (entry);
1025 if (entry == NULL)
1026 return NULL;
1028 if (S_ISDIR (entry->ino->st.st_mode))
1029 ERRNOR (EISDIR, NULL);
1031 if (entry->ino->localname == NULL)
1033 vfs_path_t *local_filename_vpath;
1034 const char *local_filename;
1036 local_handle = vfs_mkstemps (&local_filename_vpath, "extfs", entry->name);
1038 if (local_handle == -1)
1039 return NULL;
1040 close (local_handle);
1041 local_filename = vfs_path_get_by_index (local_filename_vpath, -1)->path;
1043 if (!created && ((flags & O_TRUNC) == 0)
1044 && extfs_cmd (" copyout ", archive, entry, local_filename))
1046 unlink (local_filename);
1047 vfs_path_free (local_filename_vpath, TRUE);
1048 my_errno = EIO;
1049 return NULL;
1051 entry->ino->localname = g_strdup (local_filename);
1052 vfs_path_free (local_filename_vpath, TRUE);
1055 local_handle = open (entry->ino->localname, NO_LINEAR (flags), mode);
1057 if (local_handle == -1)
1059 /* file exists(may be). Need to drop O_CREAT flag and truncate file content */
1060 flags = ~O_CREAT & (NO_LINEAR (flags) | O_TRUNC);
1061 local_handle = open (entry->ino->localname, flags, mode);
1064 if (local_handle == -1)
1065 ERRNOR (EIO, NULL);
1067 extfs_info = g_new (vfs_file_handler_t, 1);
1068 vfs_s_init_fh (extfs_info, entry->ino, created);
1069 extfs_info->handle = local_handle;
1071 /* i.e. we had no open files and now we have one */
1072 vfs_rmstamp (vfs_extfs_ops, (vfsid) archive);
1073 VFS_SUPER (archive)->fd_usage++;
1074 return extfs_info;
1077 /* --------------------------------------------------------------------------------------------- */
1079 static ssize_t
1080 extfs_read (void *fh, char *buffer, size_t count)
1082 vfs_file_handler_t *file = VFS_FILE_HANDLER (fh);
1084 return read (file->handle, buffer, count);
1087 /* --------------------------------------------------------------------------------------------- */
1089 static int
1090 extfs_close (void *fh)
1092 vfs_file_handler_t *file = VFS_FILE_HANDLER (fh);
1093 int errno_code = 0;
1095 close (file->handle);
1096 file->handle = -1;
1098 /* Commit the file if it has changed */
1099 if (file->changed)
1101 struct stat file_status;
1103 if (extfs_cmd
1104 (" copyin ", EXTFS_SUPER (VFS_FILE_HANDLER_SUPER (fh)), file->ino->ent,
1105 file->ino->localname))
1106 errno_code = EIO;
1108 if (stat (file->ino->localname, &file_status) != 0)
1109 errno_code = EIO;
1110 else
1111 file->ino->st.st_size = file_status.st_size;
1113 file->ino->st.st_mtime = time (NULL);
1116 if (--VFS_FILE_HANDLER_SUPER (fh)->fd_usage == 0)
1117 vfs_stamp_create (vfs_extfs_ops, VFS_FILE_HANDLER_SUPER (fh));
1119 g_free (fh);
1120 if (errno_code != 0)
1121 ERRNOR (EIO, -1);
1122 return 0;
1125 /* --------------------------------------------------------------------------------------------- */
1127 static int
1128 extfs_errno (struct vfs_class *me)
1130 (void) me;
1131 return my_errno;
1134 /* --------------------------------------------------------------------------------------------- */
1136 static void *
1137 extfs_opendir (const vfs_path_t * vpath)
1139 struct extfs_super_t *archive = NULL;
1140 const char *q;
1141 struct vfs_s_entry *entry;
1142 GList **info;
1144 q = extfs_get_path (vpath, &archive, FL_NONE);
1145 if (q == NULL)
1146 return NULL;
1147 entry = extfs_find_entry (VFS_SUPER (archive)->root, q, FL_NONE);
1148 if (entry == NULL)
1149 return NULL;
1150 entry = extfs_resolve_symlinks (entry);
1151 if (entry == NULL)
1152 return NULL;
1153 if (!S_ISDIR (entry->ino->st.st_mode))
1154 ERRNOR (ENOTDIR, NULL);
1156 info = g_new (GList *, 1);
1157 *info = g_queue_peek_head_link (entry->ino->subdir);
1159 return info;
1162 /* --------------------------------------------------------------------------------------------- */
1164 static struct vfs_dirent *
1165 extfs_readdir (void *data)
1167 struct vfs_dirent *dir;
1168 GList **info = (GList **) data;
1170 if (*info == NULL)
1171 return NULL;
1173 dir = vfs_dirent_init (NULL, VFS_ENTRY ((*info)->data)->name, 0); /* FIXME: inode */
1175 *info = g_list_next (*info);
1177 return dir;
1180 /* --------------------------------------------------------------------------------------------- */
1182 static int
1183 extfs_closedir (void *data)
1185 g_free (data);
1186 return 0;
1189 /* --------------------------------------------------------------------------------------------- */
1192 static void
1193 extfs_stat_move (struct stat *buf, const struct vfs_s_inode *inode)
1195 *buf = inode->st;
1197 #ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
1198 buf->st_blksize = RECORDSIZE;
1199 #endif
1200 vfs_adjust_stat (buf);
1201 #ifdef HAVE_STRUCT_STAT_ST_MTIM
1202 buf->st_atim.tv_nsec = buf->st_mtim.tv_nsec = buf->st_ctim.tv_nsec = 0;
1203 #endif
1206 /* --------------------------------------------------------------------------------------------- */
1208 static int
1209 extfs_internal_stat (const vfs_path_t * vpath, struct stat *buf, gboolean resolve)
1211 struct extfs_super_t *archive;
1212 const char *q;
1213 struct vfs_s_entry *entry;
1214 int result = -1;
1216 q = extfs_get_path (vpath, &archive, FL_NONE);
1217 if (q == NULL)
1218 goto cleanup;
1219 entry = extfs_find_entry (VFS_SUPER (archive)->root, q, FL_NONE);
1220 if (entry == NULL)
1221 goto cleanup;
1222 if (resolve)
1224 entry = extfs_resolve_symlinks (entry);
1225 if (entry == NULL)
1226 goto cleanup;
1228 extfs_stat_move (buf, entry->ino);
1229 result = 0;
1230 cleanup:
1231 return result;
1234 /* --------------------------------------------------------------------------------------------- */
1236 static int
1237 extfs_stat (const vfs_path_t * vpath, struct stat *buf)
1239 return extfs_internal_stat (vpath, buf, TRUE);
1242 /* --------------------------------------------------------------------------------------------- */
1244 static int
1245 extfs_lstat (const vfs_path_t * vpath, struct stat *buf)
1247 return extfs_internal_stat (vpath, buf, FALSE);
1250 /* --------------------------------------------------------------------------------------------- */
1252 static int
1253 extfs_fstat (void *fh, struct stat *buf)
1255 vfs_file_handler_t *file = VFS_FILE_HANDLER (fh);
1257 extfs_stat_move (buf, file->ino);
1258 return 0;
1261 /* --------------------------------------------------------------------------------------------- */
1263 static int
1264 extfs_readlink (const vfs_path_t * vpath, char *buf, size_t size)
1266 struct extfs_super_t *archive;
1267 const char *q;
1268 size_t len;
1269 struct vfs_s_entry *entry;
1270 int result = -1;
1272 q = extfs_get_path (vpath, &archive, FL_NONE);
1273 if (q == NULL)
1274 goto cleanup;
1275 entry = extfs_find_entry (VFS_SUPER (archive)->root, q, FL_NONE);
1276 if (entry == NULL)
1277 goto cleanup;
1278 if (!S_ISLNK (entry->ino->st.st_mode))
1280 const vfs_path_element_t *path_element;
1282 path_element = vfs_path_get_by_index (vpath, -1);
1283 path_element->class->verrno = EINVAL;
1284 goto cleanup;
1286 len = strlen (entry->ino->linkname);
1287 if (size < len)
1288 len = size;
1289 /* readlink() does not append a NUL character to buf */
1290 result = len;
1291 memcpy (buf, entry->ino->linkname, result);
1292 cleanup:
1293 return result;
1296 /* --------------------------------------------------------------------------------------------- */
1298 static int
1299 extfs_chown (const vfs_path_t * vpath, uid_t owner, gid_t group)
1301 (void) vpath;
1302 (void) owner;
1303 (void) group;
1304 return 0;
1307 /* --------------------------------------------------------------------------------------------- */
1309 static int
1310 extfs_chmod (const vfs_path_t * vpath, mode_t mode)
1312 (void) vpath;
1313 (void) mode;
1314 return 0;
1317 /* --------------------------------------------------------------------------------------------- */
1319 static ssize_t
1320 extfs_write (void *fh, const char *buf, size_t nbyte)
1322 vfs_file_handler_t *file = VFS_FILE_HANDLER (fh);
1324 file->changed = TRUE;
1325 return write (file->handle, buf, nbyte);
1328 /* --------------------------------------------------------------------------------------------- */
1330 static int
1331 extfs_unlink (const vfs_path_t * vpath)
1333 struct extfs_super_t *archive;
1334 const char *q;
1335 struct vfs_s_entry *entry;
1336 int result = -1;
1338 q = extfs_get_path (vpath, &archive, FL_NONE);
1339 if (q == NULL)
1340 goto cleanup;
1341 entry = extfs_find_entry (VFS_SUPER (archive)->root, q, FL_NONE);
1342 if (entry == NULL)
1343 goto cleanup;
1344 entry = extfs_resolve_symlinks (entry);
1345 if (entry == NULL)
1346 goto cleanup;
1347 if (S_ISDIR (entry->ino->st.st_mode))
1349 const vfs_path_element_t *path_element;
1351 path_element = vfs_path_get_by_index (vpath, -1);
1352 path_element->class->verrno = EISDIR;
1353 goto cleanup;
1355 if (extfs_cmd (" rm ", archive, entry, ""))
1357 my_errno = EIO;
1358 goto cleanup;
1360 vfs_s_free_entry (VFS_SUPER (archive)->me, entry);
1361 result = 0;
1362 cleanup:
1363 return result;
1366 /* --------------------------------------------------------------------------------------------- */
1368 static int
1369 extfs_mkdir (const vfs_path_t * vpath, mode_t mode)
1371 struct extfs_super_t *archive;
1372 const char *q;
1373 struct vfs_s_entry *entry;
1374 int result = -1;
1375 const vfs_path_element_t *path_element;
1377 (void) mode;
1379 path_element = vfs_path_get_by_index (vpath, -1);
1380 q = extfs_get_path (vpath, &archive, FL_NONE);
1381 if (q == NULL)
1382 goto cleanup;
1383 entry = extfs_find_entry (VFS_SUPER (archive)->root, q, FL_NONE);
1384 if (entry != NULL)
1386 path_element->class->verrno = EEXIST;
1387 goto cleanup;
1389 entry = extfs_find_entry (VFS_SUPER (archive)->root, q, FL_MKDIR);
1390 if (entry == NULL)
1391 goto cleanup;
1392 entry = extfs_resolve_symlinks (entry);
1393 if (entry == NULL)
1394 goto cleanup;
1395 if (!S_ISDIR (entry->ino->st.st_mode))
1397 path_element->class->verrno = ENOTDIR;
1398 goto cleanup;
1401 if (extfs_cmd (" mkdir ", archive, entry, ""))
1403 my_errno = EIO;
1404 vfs_s_free_entry (VFS_SUPER (archive)->me, entry);
1405 goto cleanup;
1407 result = 0;
1408 cleanup:
1409 return result;
1412 /* --------------------------------------------------------------------------------------------- */
1414 static int
1415 extfs_rmdir (const vfs_path_t * vpath)
1417 struct extfs_super_t *archive;
1418 const char *q;
1419 struct vfs_s_entry *entry;
1420 int result = -1;
1422 q = extfs_get_path (vpath, &archive, FL_NONE);
1423 if (q == NULL)
1424 goto cleanup;
1425 entry = extfs_find_entry (VFS_SUPER (archive)->root, q, FL_NONE);
1426 if (entry == NULL)
1427 goto cleanup;
1428 entry = extfs_resolve_symlinks (entry);
1429 if (entry == NULL)
1430 goto cleanup;
1431 if (!S_ISDIR (entry->ino->st.st_mode))
1433 const vfs_path_element_t *path_element;
1435 path_element = vfs_path_get_by_index (vpath, -1);
1436 path_element->class->verrno = ENOTDIR;
1437 goto cleanup;
1440 if (extfs_cmd (" rmdir ", archive, entry, ""))
1442 my_errno = EIO;
1443 goto cleanup;
1445 vfs_s_free_entry (VFS_SUPER (archive)->me, entry);
1446 result = 0;
1447 cleanup:
1448 return result;
1451 /* --------------------------------------------------------------------------------------------- */
1453 static int
1454 extfs_chdir (const vfs_path_t * vpath)
1456 void *data;
1458 my_errno = ENOTDIR;
1459 data = extfs_opendir (vpath);
1460 if (data == NULL)
1461 return (-1);
1462 extfs_closedir (data);
1463 my_errno = 0;
1464 return 0;
1467 /* --------------------------------------------------------------------------------------------- */
1469 static off_t
1470 extfs_lseek (void *fh, off_t offset, int whence)
1472 vfs_file_handler_t *file = VFS_FILE_HANDLER (fh);
1474 return lseek (file->handle, offset, whence);
1477 /* --------------------------------------------------------------------------------------------- */
1479 static vfsid
1480 extfs_getid (const vfs_path_t * vpath)
1482 struct extfs_super_t *archive = NULL;
1483 const char *p;
1485 p = extfs_get_path (vpath, &archive, FL_NO_OPEN);
1486 return (p == NULL ? NULL : (vfsid) archive);
1489 /* --------------------------------------------------------------------------------------------- */
1491 static vfs_path_t *
1492 extfs_getlocalcopy (const vfs_path_t * vpath)
1494 vfs_file_handler_t *fh;
1495 vfs_path_t *p;
1497 fh = VFS_FILE_HANDLER (extfs_open (vpath, O_RDONLY, 0));
1498 if (fh == NULL)
1499 return NULL;
1500 if (fh->ino->localname == NULL)
1502 extfs_close ((void *) fh);
1503 return NULL;
1505 p = vfs_path_from_str (fh->ino->localname);
1506 VFS_FILE_HANDLER_SUPER (fh)->fd_usage++;
1507 extfs_close ((void *) fh);
1508 return p;
1511 /* --------------------------------------------------------------------------------------------- */
1513 static int
1514 extfs_ungetlocalcopy (const vfs_path_t * vpath, const vfs_path_t * local, gboolean has_changed)
1516 vfs_file_handler_t *fh;
1518 fh = VFS_FILE_HANDLER (extfs_open (vpath, O_RDONLY, 0));
1519 if (fh == NULL)
1520 return 0;
1522 if (strcmp (fh->ino->localname, vfs_path_get_last_path_str (local)) == 0)
1524 VFS_FILE_HANDLER_SUPER (fh)->fd_usage--;
1525 if (has_changed)
1526 fh->changed = TRUE;
1527 extfs_close ((void *) fh);
1528 return 0;
1530 else
1532 /* Should not happen */
1533 extfs_close ((void *) fh);
1534 return 0;
1538 /* --------------------------------------------------------------------------------------------- */
1540 static gboolean
1541 extfs_get_plugins (const char *where, gboolean silent)
1543 char *dirname;
1544 GDir *dir;
1545 const char *filename;
1547 dirname = g_build_path (PATH_SEP_STR, where, MC_EXTFS_DIR, (char *) NULL);
1548 dir = g_dir_open (dirname, 0, NULL);
1550 /* We may not use vfs_die() message or message or similar,
1551 * UI is not initialized at this time and message would not
1552 * appear on screen. */
1553 if (dir == NULL)
1555 if (!silent)
1556 fprintf (stderr, _("Warning: cannot open %s directory\n"), dirname);
1557 g_free (dirname);
1558 return FALSE;
1561 if (extfs_plugins == NULL)
1562 extfs_plugins = g_array_sized_new (FALSE, TRUE, sizeof (extfs_plugin_info_t), 32);
1564 while ((filename = g_dir_read_name (dir)) != NULL)
1566 char fullname[MC_MAXPATHLEN];
1567 struct stat s;
1569 g_snprintf (fullname, sizeof (fullname), "%s" PATH_SEP_STR "%s", dirname, filename);
1571 if ((stat (fullname, &s) == 0) && S_ISREG (s.st_mode) && !S_ISDIR (s.st_mode)
1572 && is_exe (s.st_mode))
1574 int f;
1576 f = open (fullname, O_RDONLY);
1578 if (f >= 0)
1580 size_t len, i;
1581 extfs_plugin_info_t info;
1582 gboolean found = FALSE;
1584 close (f);
1586 /* Handle those with a trailing '+', those flag that the
1587 * file system does not require an archive to work
1589 len = strlen (filename);
1590 info.need_archive = (filename[len - 1] != '+');
1591 info.path = g_strconcat (dirname, PATH_SEP_STR, (char *) NULL);
1592 info.prefix = g_strdup (filename);
1594 /* prepare to compare file names without trailing '+' */
1595 if (!info.need_archive)
1596 info.prefix[len - 1] = '\0';
1598 /* don't overload already found plugin */
1599 for (i = 0; i < extfs_plugins->len; i++)
1601 extfs_plugin_info_t *p;
1603 p = &g_array_index (extfs_plugins, extfs_plugin_info_t, i);
1605 /* 2 files with same names cannot be in a directory */
1606 if ((strcmp (info.path, p->path) != 0)
1607 && (strcmp (info.prefix, p->prefix) == 0))
1609 found = TRUE;
1610 break;
1614 if (found)
1616 g_free (info.path);
1617 g_free (info.prefix);
1619 else
1621 /* restore file name */
1622 if (!info.need_archive)
1623 info.prefix[len - 1] = '+';
1624 g_array_append_val (extfs_plugins, info);
1630 g_dir_close (dir);
1631 g_free (dirname);
1633 return TRUE;
1636 /* --------------------------------------------------------------------------------------------- */
1638 static int
1639 extfs_init (struct vfs_class *me)
1641 gboolean d1, d2;
1643 (void) me;
1645 /* 1st: scan user directory */
1646 d1 = extfs_get_plugins (mc_config_get_data_path (), TRUE); /* silent about user dir */
1647 /* 2nd: scan system dir */
1648 d2 = extfs_get_plugins (LIBEXECDIR, d1);
1650 return (d1 || d2 ? 1 : 0);
1653 /* --------------------------------------------------------------------------------------------- */
1655 static void
1656 extfs_done (struct vfs_class *me)
1658 size_t i;
1660 while (VFS_SUBCLASS (me)->supers != NULL)
1661 me->free ((vfsid) VFS_SUBCLASS (me)->supers->data);
1663 if (extfs_plugins == NULL)
1664 return;
1666 for (i = 0; i < extfs_plugins->len; i++)
1668 extfs_plugin_info_t *info;
1670 info = &g_array_index (extfs_plugins, extfs_plugin_info_t, i);
1671 g_free (info->path);
1672 g_free (info->prefix);
1675 g_array_free (extfs_plugins, TRUE);
1678 /* --------------------------------------------------------------------------------------------- */
1680 static int
1681 extfs_setctl (const vfs_path_t * vpath, int ctlop, void *arg)
1683 (void) arg;
1685 if (ctlop == VFS_SETCTL_RUN)
1687 extfs_run (vpath);
1688 return 1;
1690 return 0;
1693 /* --------------------------------------------------------------------------------------------- */
1694 /*** public functions ****************************************************************************/
1695 /* --------------------------------------------------------------------------------------------- */
1697 void
1698 vfs_init_extfs (void)
1700 vfs_init_subclass (&extfs_subclass, "extfs", VFSF_UNKNOWN, NULL);
1701 vfs_extfs_ops->init = extfs_init;
1702 vfs_extfs_ops->done = extfs_done;
1703 vfs_extfs_ops->fill_names = extfs_fill_names;
1704 vfs_extfs_ops->which = extfs_which;
1705 vfs_extfs_ops->open = extfs_open;
1706 vfs_extfs_ops->close = extfs_close;
1707 vfs_extfs_ops->read = extfs_read;
1708 vfs_extfs_ops->write = extfs_write;
1709 vfs_extfs_ops->opendir = extfs_opendir;
1710 vfs_extfs_ops->readdir = extfs_readdir;
1711 vfs_extfs_ops->closedir = extfs_closedir;
1712 vfs_extfs_ops->stat = extfs_stat;
1713 vfs_extfs_ops->lstat = extfs_lstat;
1714 vfs_extfs_ops->fstat = extfs_fstat;
1715 vfs_extfs_ops->chmod = extfs_chmod;
1716 vfs_extfs_ops->chown = extfs_chown;
1717 vfs_extfs_ops->readlink = extfs_readlink;
1718 vfs_extfs_ops->unlink = extfs_unlink;
1719 vfs_extfs_ops->chdir = extfs_chdir;
1720 vfs_extfs_ops->ferrno = extfs_errno;
1721 vfs_extfs_ops->lseek = extfs_lseek;
1722 vfs_extfs_ops->getid = extfs_getid;
1723 vfs_extfs_ops->getlocalcopy = extfs_getlocalcopy;
1724 vfs_extfs_ops->ungetlocalcopy = extfs_ungetlocalcopy;
1725 vfs_extfs_ops->mkdir = extfs_mkdir;
1726 vfs_extfs_ops->rmdir = extfs_rmdir;
1727 vfs_extfs_ops->setctl = extfs_setctl;
1728 extfs_subclass.free_inode = extfs_free_inode;
1729 extfs_subclass.free_archive = extfs_free_archive;
1730 vfs_register_class (vfs_extfs_ops);
1733 /* --------------------------------------------------------------------------------------------- */