Fixes of indentation.
[midnight-commander.git] / src / vfs / extfs / extfs.c
blob2bab28644e8716944d586e4e090288a33e8252ab
1 /*
2 Virtual File System: External file system.
4 Copyright (C) 1995, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
5 2006, 2007, 2009, 2011
6 The Free Software Foundation, Inc.
8 Written by:
9 Jakub Jelinek, 1995
10 Pavel Machek, 1998
11 Andrew T. Veliath, 1999
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 <fcntl.h>
51 #include <errno.h>
52 #include <sys/wait.h>
53 #include <unistd.h>
55 #include "lib/global.h"
56 #include "lib/fileloc.h"
57 #include "lib/mcconfig.h"
58 #include "lib/util.h"
59 #include "lib/widget.h" /* message() */
61 #include "src/setup.h" /* shell */
62 #include "src/execute.h" /* For shell_execute */
64 #include "lib/vfs/vfs.h"
65 #include "lib/vfs/utilvfs.h"
66 #include "lib/vfs/gc.h" /* vfs_rmstamp */
68 #include "extfs.h"
70 /*** global variables ****************************************************************************/
72 GArray *extfs_plugins = NULL;
74 /*** file scope macro definitions ****************************************************************/
76 #undef ERRNOR
77 #define ERRNOR(x,y) do { my_errno = x; return y; } while(0)
79 #define RECORDSIZE 512
81 /*** file scope type declarations ****************************************************************/
83 struct inode
85 nlink_t nlink;
86 struct entry *first_in_subdir; /* only used if this is a directory */
87 struct entry *last_in_subdir;
88 ino_t inode; /* This is inode # */
89 dev_t dev; /* This is an internal identification of the extfs archive */
90 struct archive *archive; /* And this is an archive structure */
91 dev_t rdev;
92 mode_t mode;
93 uid_t uid;
94 gid_t gid;
95 off_t size;
96 time_t mtime;
97 char *linkname;
98 time_t atime;
99 time_t ctime;
100 char *local_filename;
103 struct entry
105 struct entry *next_in_dir;
106 struct entry *dir;
107 char *name;
108 struct inode *inode;
111 struct pseudofile
113 struct archive *archive;
114 gboolean has_changed;
115 int local_handle;
116 struct entry *entry;
119 struct archive
121 int fstype;
122 char *name;
123 char *local_name;
124 struct stat local_stat;
125 dev_t rdev;
126 int fd_usage;
127 ino_t inode_counter;
128 struct entry *root_entry;
129 struct archive *next;
132 typedef struct
134 char *path;
135 char *prefix;
136 gboolean need_archive;
137 } extfs_plugin_info_t;
139 /*** file scope variables ************************************************************************/
141 static gboolean errloop;
142 static gboolean notadir;
144 static struct vfs_class vfs_extfs_ops;
145 static struct archive *first_archive = NULL;
146 static int my_errno = 0;
148 /*** file scope functions ************************************************************************/
149 /* --------------------------------------------------------------------------------------------- */
151 static void extfs_remove_entry (struct entry *e);
152 static void extfs_free (vfsid id);
153 static void extfs_free_entry (struct entry *e);
154 static struct entry *extfs_resolve_symlinks_int (struct entry *entry, GSList * list);
156 /* --------------------------------------------------------------------------------------------- */
158 static void
159 extfs_make_dots (struct entry *ent)
161 struct entry *entry = g_new (struct entry, 1);
162 struct entry *parentry = ent->dir;
163 struct inode *inode = ent->inode, *parent;
165 parent = (parentry != NULL) ? parentry->inode : NULL;
166 entry->name = g_strdup (".");
167 entry->inode = inode;
168 entry->dir = ent;
169 inode->local_filename = NULL;
170 inode->first_in_subdir = entry;
171 inode->nlink++;
173 entry->next_in_dir = g_new (struct entry, 1);
174 entry = entry->next_in_dir;
175 entry->name = g_strdup ("..");
176 inode->last_in_subdir = entry;
177 entry->next_in_dir = NULL;
178 if (parent != NULL)
180 entry->inode = parent;
181 entry->dir = parentry;
182 parent->nlink++;
184 else
186 entry->inode = inode;
187 entry->dir = ent;
188 inode->nlink++;
192 /* --------------------------------------------------------------------------------------------- */
194 static struct entry *
195 extfs_generate_entry (struct archive *archive,
196 const char *name, struct entry *parentry, mode_t mode)
198 mode_t myumask;
199 struct inode *inode, *parent;
200 struct entry *entry;
202 parent = (parentry != NULL) ? parentry->inode : NULL;
203 entry = g_new (struct entry, 1);
205 entry->name = g_strdup (name);
206 entry->next_in_dir = NULL;
207 entry->dir = parentry;
208 if (parent != NULL)
210 parent->last_in_subdir->next_in_dir = entry;
211 parent->last_in_subdir = entry;
213 inode = g_new (struct inode, 1);
214 entry->inode = inode;
215 inode->local_filename = NULL;
216 inode->linkname = NULL;
217 inode->last_in_subdir = NULL;
218 inode->inode = (archive->inode_counter)++;
219 inode->dev = archive->rdev;
220 inode->archive = archive;
221 myumask = umask (022);
222 umask (myumask);
223 inode->mode = mode & ~myumask;
224 mode = inode->mode;
225 inode->rdev = 0;
226 inode->uid = getuid ();
227 inode->gid = getgid ();
228 inode->size = 0;
229 inode->mtime = time (NULL);
230 inode->atime = inode->mtime;
231 inode->ctime = inode->mtime;
232 inode->nlink = 1;
233 if (S_ISDIR (mode))
234 extfs_make_dots (entry);
235 return entry;
238 /* --------------------------------------------------------------------------------------------- */
240 static struct entry *
241 extfs_find_entry_int (struct entry *dir, const char *name, GSList * list,
242 gboolean make_dirs, gboolean make_file)
244 struct entry *pent, *pdir;
245 const char *p, *name_end;
246 char *q;
247 char c = PATH_SEP;
249 if (g_path_is_absolute (name))
251 /* Handle absolute paths */
252 name = (char *) g_path_skip_root (name);
253 dir = dir->inode->archive->root_entry;
256 pent = dir;
257 p = name;
258 name_end = name + strlen (name);
260 q = strchr (p, PATH_SEP);
261 if (q == '\0')
262 q = strchr (p, '\0');
264 while ((pent != NULL) && (c != '\0') && (*p != '\0'))
266 c = *q;
267 *q = '\0';
269 if (strcmp (p, ".") != 0)
271 if (strcmp (p, "..") == 0)
272 pent = pent->dir;
273 else
275 pent = extfs_resolve_symlinks_int (pent, list);
276 if (pent == NULL)
278 *q = c;
279 return NULL;
281 if (!S_ISDIR (pent->inode->mode))
283 *q = c;
284 notadir = TRUE;
285 return NULL;
288 pdir = pent;
289 for (pent = pent->inode->first_in_subdir; pent != NULL; pent = pent->next_in_dir)
290 /* Hack: I keep the original semanthic unless
291 q+1 would break in the strchr */
292 if (strcmp (pent->name, p) == 0)
294 if (q + 1 > name_end)
296 *q = c;
297 notadir = !S_ISDIR (pent->inode->mode);
298 return pent;
300 break;
303 /* When we load archive, we create automagically
304 * non-existant directories
306 if (pent == NULL && make_dirs)
307 pent = extfs_generate_entry (dir->inode->archive, p, pdir, S_IFDIR | 0777);
308 if (pent == NULL && make_file)
309 pent = extfs_generate_entry (dir->inode->archive, p, pdir, S_IFREG | 0666);
312 /* Next iteration */
313 *q = c;
314 p = q + 1;
315 q = strchr (p, PATH_SEP);
316 if (q == '\0')
317 q = strchr (p, '\0');
319 if (pent == NULL)
320 my_errno = ENOENT;
321 return pent;
324 /* --------------------------------------------------------------------------------------------- */
326 static struct entry *
327 extfs_find_entry (struct entry *dir, const char *name, gboolean make_dirs, gboolean make_file)
329 struct entry *res;
331 errloop = FALSE;
332 notadir = FALSE;
334 res = extfs_find_entry_int (dir, name, NULL, make_dirs, make_file);
335 if (res == NULL)
337 if (errloop)
338 my_errno = ELOOP;
339 else if (notadir)
340 my_errno = ENOTDIR;
342 return res;
345 /* --------------------------------------------------------------------------------------------- */
347 static void
348 extfs_fill_names (struct vfs_class *me, fill_names_f func)
350 struct archive *a = first_archive;
352 (void) me;
354 while (a != NULL)
356 extfs_plugin_info_t *info;
357 char *name;
359 info = &g_array_index (extfs_plugins, extfs_plugin_info_t, a->fstype);
360 name =
361 g_strconcat (a->name ? a->name : "", "/", info->prefix, VFS_PATH_URL_DELIMITER,
362 (char *) NULL);
363 func (name);
364 g_free (name);
365 a = a->next;
369 /* --------------------------------------------------------------------------------------------- */
371 static void
372 extfs_free_archive (struct archive *archive)
374 extfs_free_entry (archive->root_entry);
375 if (archive->local_name != NULL)
377 struct stat my;
378 vfs_path_t *local_name_vpath, *name_vpath;
380 local_name_vpath = vfs_path_from_str (archive->local_name);
381 name_vpath = vfs_path_from_str (archive->local_name);
382 mc_stat (local_name_vpath, &my);
383 mc_ungetlocalcopy (name_vpath, local_name_vpath,
384 archive->local_stat.st_mtime != my.st_mtime);
385 vfs_path_free (local_name_vpath);
386 vfs_path_free (name_vpath);
387 g_free (archive->local_name);
389 g_free (archive->name);
390 g_free (archive);
393 /* --------------------------------------------------------------------------------------------- */
395 static FILE *
396 extfs_open_archive (int fstype, const char *name, struct archive **pparc)
398 const extfs_plugin_info_t *info;
399 static dev_t archive_counter = 0;
400 FILE *result = NULL;
401 mode_t mode;
402 char *cmd;
403 struct stat mystat;
404 struct archive *current_archive;
405 struct entry *root_entry;
406 char *tmp = NULL;
407 vfs_path_t *local_name_vpath = NULL;
408 vfs_path_t *name_vpath;
410 name_vpath = vfs_path_from_str (name);
411 info = &g_array_index (extfs_plugins, extfs_plugin_info_t, fstype);
413 if (info->need_archive)
415 if (mc_stat (name_vpath, &mystat) == -1)
416 goto ret;
418 if (!vfs_file_is_local (name_vpath))
420 local_name_vpath = mc_getlocalcopy (name_vpath);
421 if (local_name_vpath == NULL)
422 goto ret;
425 tmp = name_quote (vfs_path_get_last_path_str (name_vpath), 0);
428 cmd = g_strconcat (info->path, info->prefix, " list ",
429 vfs_path_get_last_path_str (local_name_vpath) != NULL ?
430 vfs_path_get_last_path_str (local_name_vpath) : tmp, (char *) NULL);
431 g_free (tmp);
433 open_error_pipe ();
434 result = popen (cmd, "r");
435 g_free (cmd);
436 if (result == NULL)
438 close_error_pipe (D_ERROR, NULL);
439 if (local_name_vpath != NULL)
441 mc_ungetlocalcopy (name_vpath, local_name_vpath, FALSE);
442 vfs_path_free (local_name_vpath);
444 goto ret;
447 #ifdef ___QNXNTO__
448 setvbuf (result, NULL, _IONBF, 0);
449 #endif
451 current_archive = g_new (struct archive, 1);
452 current_archive->fstype = fstype;
453 current_archive->name = (name != NULL) ? g_strdup (name) : NULL;
454 current_archive->local_name = g_strdup (vfs_path_get_last_path_str (local_name_vpath));
456 if (local_name_vpath != NULL)
458 mc_stat (local_name_vpath, &current_archive->local_stat);
459 vfs_path_free (local_name_vpath);
461 current_archive->inode_counter = 0;
462 current_archive->fd_usage = 0;
463 current_archive->rdev = archive_counter++;
464 current_archive->next = first_archive;
465 first_archive = current_archive;
466 mode = mystat.st_mode & 07777;
467 if (mode & 0400)
468 mode |= 0100;
469 if (mode & 0040)
470 mode |= 0010;
471 if (mode & 0004)
472 mode |= 0001;
473 mode |= S_IFDIR;
474 root_entry = extfs_generate_entry (current_archive, PATH_SEP_STR, NULL, mode);
475 root_entry->inode->uid = mystat.st_uid;
476 root_entry->inode->gid = mystat.st_gid;
477 root_entry->inode->atime = mystat.st_atime;
478 root_entry->inode->ctime = mystat.st_ctime;
479 root_entry->inode->mtime = mystat.st_mtime;
480 current_archive->root_entry = root_entry;
482 *pparc = current_archive;
484 ret:
485 vfs_path_free (name_vpath);
486 return result;
489 /* --------------------------------------------------------------------------------------------- */
491 * Main loop for reading an archive.
492 * Return 0 on success, -1 on error.
495 static int
496 extfs_read_archive (int fstype, const char *name, struct archive **pparc)
498 FILE *extfsd;
499 const extfs_plugin_info_t *info;
500 char *buffer;
501 struct archive *current_archive;
502 char *current_file_name, *current_link_name;
504 info = &g_array_index (extfs_plugins, extfs_plugin_info_t, fstype);
506 extfsd = extfs_open_archive (fstype, name, &current_archive);
508 if (extfsd == NULL)
510 message (D_ERROR, MSG_ERROR, _("Cannot open %s archive\n%s"), info->prefix, name);
511 return -1;
514 buffer = g_malloc (BUF_4K);
515 while (fgets (buffer, BUF_4K, extfsd) != NULL)
517 struct stat hstat;
519 current_link_name = NULL;
520 if (vfs_parse_ls_lga (buffer, &hstat, &current_file_name, &current_link_name, NULL))
522 struct entry *entry, *pent;
523 struct inode *inode;
524 char *p, *q, *cfn = current_file_name;
526 if (*cfn != '\0')
528 if (*cfn == PATH_SEP)
529 cfn++;
530 p = strchr (cfn, '\0');
531 if (p != cfn && *(p - 1) == PATH_SEP)
532 *(p - 1) = '\0';
533 p = strrchr (cfn, PATH_SEP);
534 if (p == NULL)
536 p = cfn;
537 q = strchr (cfn, '\0');
539 else
541 *(p++) = '\0';
542 q = cfn;
544 if (S_ISDIR (hstat.st_mode) && (strcmp (p, ".") == 0 || strcmp (p, "..") == 0))
545 goto read_extfs_continue;
546 pent = extfs_find_entry (current_archive->root_entry, q, TRUE, FALSE);
547 if (pent == NULL)
549 /* FIXME: Should clean everything one day */
550 g_free (buffer);
551 pclose (extfsd);
552 close_error_pipe (D_ERROR, _("Inconsistent extfs archive"));
553 return -1;
555 entry = g_new (struct entry, 1);
556 entry->name = g_strdup (p);
557 entry->next_in_dir = NULL;
558 entry->dir = pent;
559 if (pent->inode->last_in_subdir)
561 pent->inode->last_in_subdir->next_in_dir = entry;
562 pent->inode->last_in_subdir = entry;
564 if (!S_ISLNK (hstat.st_mode) && (current_link_name != NULL))
566 pent = extfs_find_entry (current_archive->root_entry,
567 current_link_name, FALSE, FALSE);
568 if (pent == NULL)
570 /* FIXME: Should clean everything one day */
571 g_free (buffer);
572 pclose (extfsd);
573 close_error_pipe (D_ERROR, _("Inconsistent extfs archive"));
574 return -1;
577 entry->inode = pent->inode;
578 pent->inode->nlink++;
580 else
582 inode = g_new (struct inode, 1);
583 entry->inode = inode;
584 inode->local_filename = NULL;
585 inode->inode = (current_archive->inode_counter)++;
586 inode->nlink = 1;
587 inode->dev = current_archive->rdev;
588 inode->archive = current_archive;
589 inode->mode = hstat.st_mode;
590 #ifdef HAVE_STRUCT_STAT_ST_RDEV
591 inode->rdev = hstat.st_rdev;
592 #else
593 inode->rdev = 0;
594 #endif
595 inode->uid = hstat.st_uid;
596 inode->gid = hstat.st_gid;
597 inode->size = hstat.st_size;
598 inode->mtime = hstat.st_mtime;
599 inode->atime = hstat.st_atime;
600 inode->ctime = hstat.st_ctime;
601 inode->first_in_subdir = NULL;
602 inode->last_in_subdir = NULL;
603 if (current_link_name != NULL && S_ISLNK (hstat.st_mode))
605 inode->linkname = current_link_name;
606 current_link_name = NULL;
608 else
610 if (S_ISLNK (hstat.st_mode))
611 inode->mode &= ~S_IFLNK; /* You *DON'T* want to do this always */
612 inode->linkname = NULL;
614 if (S_ISDIR (hstat.st_mode))
615 extfs_make_dots (entry);
618 read_extfs_continue:
619 g_free (current_file_name);
620 g_free (current_link_name);
623 g_free (buffer);
625 /* Check if extfs 'list' returned 0 */
626 if (pclose (extfsd) != 0)
628 extfs_free (current_archive);
629 close_error_pipe (D_ERROR, _("Inconsistent extfs archive"));
630 return -1;
633 close_error_pipe (D_ERROR, NULL);
634 *pparc = current_archive;
635 return 0;
638 /* --------------------------------------------------------------------------------------------- */
640 static int
641 extfs_which (struct vfs_class *me, const char *path)
643 size_t path_len;
644 size_t i;
646 (void) me;
648 path_len = strlen (path);
650 for (i = 0; i < extfs_plugins->len; i++)
652 extfs_plugin_info_t *info;
654 info = &g_array_index (extfs_plugins, extfs_plugin_info_t, i);
656 if ((strncmp (path, info->prefix, path_len) == 0)
657 && ((info->prefix[path_len] == '\0') || (info->prefix[path_len] == '+')))
658 return i;
660 return -1;
663 /* --------------------------------------------------------------------------------------------- */
665 * Dissect the path and create corresponding superblock.
667 static const char *
668 extfs_get_path_int (const vfs_path_t * vpath, struct archive **archive, gboolean do_not_open)
670 char *archive_name;
671 int result = -1;
672 struct archive *parc;
673 int fstype;
674 const vfs_path_element_t *path_element;
676 path_element = vfs_path_get_by_index (vpath, -1);
678 fstype = extfs_which (path_element->class, path_element->vfs_prefix);
679 if (fstype == -1)
680 return NULL;
682 archive_name = vfs_path_to_str_elements_count (vpath, -1);
685 * All filesystems should have some local archive, at least
686 * it can be PATH_SEP ('/').
688 for (parc = first_archive; parc != NULL; parc = parc->next)
689 if (parc->name != NULL)
691 if (strcmp (parc->name, archive_name) == 0)
693 vfs_stamp (&vfs_extfs_ops, (vfsid) parc);
694 goto return_success;
698 result = do_not_open ? -1 : extfs_read_archive (fstype, archive_name, &parc);
699 if (result == -1)
701 path_element->class->verrno = EIO;
702 g_free (archive_name);
703 return NULL;
706 return_success:
707 *archive = parc;
708 g_free (archive_name);
709 return path_element->path;
712 /* --------------------------------------------------------------------------------------------- */
714 * Dissect the path and create corresponding superblock.
715 * The result should be freed.
718 static char *
719 extfs_get_path (const vfs_path_t * vpath, struct archive **archive, gboolean do_not_open)
721 return g_strdup (extfs_get_path_int (vpath, archive, do_not_open));
724 /* --------------------------------------------------------------------------------------------- */
725 /* Return allocated path (without leading slash) inside the archive */
727 static char *
728 extfs_get_path_from_entry (struct entry *entry)
730 GString *localpath;
732 localpath = g_string_new ("");
734 while (entry->dir != NULL)
736 g_string_prepend (localpath, entry->name);
737 if (entry->dir->dir != NULL)
738 g_string_prepend_c (localpath, PATH_SEP);
739 entry = entry->dir;
742 return g_string_free (localpath, FALSE);
745 /* --------------------------------------------------------------------------------------------- */
747 static struct entry *
748 extfs_resolve_symlinks_int (struct entry *entry, GSList * list)
750 struct entry *pent = NULL;
752 if (!S_ISLNK (entry->inode->mode))
753 return entry;
755 if (g_slist_find (list, entry) != NULL)
757 /* Here we protect us against symlink looping */
758 errloop = TRUE;
760 else
762 GSList *looping;
764 looping = g_slist_prepend (list, entry);
765 pent = extfs_find_entry_int (entry->dir, entry->inode->linkname, looping, FALSE, FALSE);
766 looping = g_slist_delete_link (looping, looping);
768 if (pent == NULL)
769 my_errno = ENOENT;
772 return pent;
775 /* --------------------------------------------------------------------------------------------- */
777 static struct entry *
778 extfs_resolve_symlinks (struct entry *entry)
780 struct entry *res;
782 errloop = FALSE;
783 notadir = FALSE;
784 res = extfs_resolve_symlinks_int (entry, NULL);
785 if (res == NULL)
787 if (errloop)
788 my_errno = ELOOP;
789 else if (notadir)
790 my_errno = ENOTDIR;
792 return res;
795 /* --------------------------------------------------------------------------------------------- */
797 static char *
798 extfs_get_archive_name (struct archive *archive)
800 const char *archive_name;
802 if (archive->local_name)
803 archive_name = archive->local_name;
804 else
805 archive_name = archive->name;
807 if (!archive_name || !*archive_name)
808 return g_strdup ("no_archive_name");
809 else
811 char *ret_str;
812 vfs_path_t *vpath;
813 const vfs_path_element_t *path_element;
815 vpath = vfs_path_from_str (archive_name);
816 path_element = vfs_path_get_by_index (vpath, -1);
817 ret_str = g_strdup (path_element->path);
818 vfs_path_free (vpath);
819 return ret_str;
823 /* --------------------------------------------------------------------------------------------- */
824 /** Don't pass localname as NULL */
826 static int
827 extfs_cmd (const char *str_extfs_cmd, struct archive *archive,
828 struct entry *entry, const char *localname)
830 char *file;
831 char *quoted_file;
832 char *quoted_localname;
833 char *archive_name, *quoted_archive_name;
834 const extfs_plugin_info_t *info;
835 char *cmd;
836 int retval;
838 file = extfs_get_path_from_entry (entry);
839 quoted_file = name_quote (file, 0);
840 g_free (file);
842 archive_name = extfs_get_archive_name (archive);
843 quoted_archive_name = name_quote (archive_name, 0);
844 g_free (archive_name);
845 quoted_localname = name_quote (localname, 0);
846 info = &g_array_index (extfs_plugins, extfs_plugin_info_t, archive->fstype);
847 cmd = g_strconcat (info->path, info->prefix, str_extfs_cmd,
848 quoted_archive_name, " ", quoted_file, " ", quoted_localname, (char *) NULL);
849 g_free (quoted_file);
850 g_free (quoted_localname);
851 g_free (quoted_archive_name);
853 open_error_pipe ();
854 retval = my_system (EXECUTE_AS_SHELL, shell, cmd);
855 g_free (cmd);
856 close_error_pipe (D_ERROR, NULL);
857 return retval;
860 /* --------------------------------------------------------------------------------------------- */
862 static void
863 extfs_run (const vfs_path_t * vpath)
865 struct archive *archive = NULL;
866 char *p, *q, *archive_name, *quoted_archive_name;
867 char *cmd;
868 const extfs_plugin_info_t *info;
870 p = extfs_get_path (vpath, &archive, FALSE);
871 if (p == NULL)
872 return;
873 q = name_quote (p, 0);
874 g_free (p);
876 archive_name = extfs_get_archive_name (archive);
877 quoted_archive_name = name_quote (archive_name, 0);
878 g_free (archive_name);
879 info = &g_array_index (extfs_plugins, extfs_plugin_info_t, archive->fstype);
880 cmd =
881 g_strconcat (info->path, info->prefix, " run ", quoted_archive_name, " ", q, (char *) NULL);
882 g_free (quoted_archive_name);
883 g_free (q);
884 shell_execute (cmd, 0);
885 g_free (cmd);
888 /* --------------------------------------------------------------------------------------------- */
890 static void *
891 extfs_open (const vfs_path_t * vpath, int flags, mode_t mode)
893 struct pseudofile *extfs_info;
894 struct archive *archive = NULL;
895 char *q;
896 struct entry *entry;
897 int local_handle;
898 gboolean created = FALSE;
900 q = extfs_get_path (vpath, &archive, FALSE);
901 if (q == NULL)
902 return NULL;
903 entry = extfs_find_entry (archive->root_entry, q, FALSE, FALSE);
904 if ((entry == NULL) && ((flags & O_CREAT) != 0))
906 /* Create new entry */
907 entry = extfs_find_entry (archive->root_entry, q, FALSE, TRUE);
908 created = (entry != NULL);
911 g_free (q);
912 if (entry == NULL)
913 return NULL;
914 entry = extfs_resolve_symlinks (entry);
915 if (entry == NULL)
916 return NULL;
918 if (S_ISDIR (entry->inode->mode))
919 ERRNOR (EISDIR, NULL);
921 if (entry->inode->local_filename == NULL)
923 vfs_path_t *local_filename_vpath;
924 const char *local_filename;
926 local_handle = vfs_mkstemps (&local_filename_vpath, "extfs", entry->name);
928 if (local_handle == -1)
929 return NULL;
930 close (local_handle);
931 local_filename = vfs_path_get_by_index (local_filename_vpath, -1)->path;
933 if (!created && ((flags & O_TRUNC) == 0)
934 && extfs_cmd (" copyout ", archive, entry, local_filename))
936 unlink (local_filename);
937 vfs_path_free (local_filename_vpath);
938 my_errno = EIO;
939 return NULL;
941 entry->inode->local_filename = g_strdup (local_filename);
942 vfs_path_free (local_filename_vpath);
945 local_handle = open (entry->inode->local_filename, NO_LINEAR (flags), mode);
947 if (local_handle == -1)
949 /* file exists(may be). Need to drop O_CREAT flag and truncate file content */
950 flags = ~O_CREAT & (NO_LINEAR (flags) | O_TRUNC);
951 local_handle = open (entry->inode->local_filename, flags, mode);
954 if (local_handle == -1)
955 ERRNOR (EIO, NULL);
957 extfs_info = g_new (struct pseudofile, 1);
958 extfs_info->archive = archive;
959 extfs_info->entry = entry;
960 extfs_info->has_changed = created;
961 extfs_info->local_handle = local_handle;
963 /* i.e. we had no open files and now we have one */
964 vfs_rmstamp (&vfs_extfs_ops, (vfsid) archive);
965 archive->fd_usage++;
966 return extfs_info;
969 /* --------------------------------------------------------------------------------------------- */
971 static ssize_t
972 extfs_read (void *data, char *buffer, size_t count)
974 struct pseudofile *file = (struct pseudofile *) data;
976 return read (file->local_handle, buffer, count);
979 /* --------------------------------------------------------------------------------------------- */
981 static int
982 extfs_close (void *data)
984 struct pseudofile *file;
985 int errno_code = 0;
986 file = (struct pseudofile *) data;
988 close (file->local_handle);
990 /* Commit the file if it has changed */
991 if (file->has_changed)
993 struct stat file_status;
995 if (extfs_cmd (" copyin ", file->archive, file->entry, file->entry->inode->local_filename))
996 errno_code = EIO;
998 if (stat (file->entry->inode->local_filename, &file_status) != 0)
999 errno_code = EIO;
1000 else
1001 file->entry->inode->size = file_status.st_size;
1003 file->entry->inode->mtime = time (NULL);
1006 if (--file->archive->fd_usage == 0)
1007 vfs_stamp_create (&vfs_extfs_ops, file->archive);
1009 g_free (data);
1010 if (errno_code != 0)
1011 ERRNOR (EIO, -1);
1012 return 0;
1015 /* --------------------------------------------------------------------------------------------- */
1017 static int
1018 extfs_errno (struct vfs_class *me)
1020 (void) me;
1021 return my_errno;
1024 /* --------------------------------------------------------------------------------------------- */
1026 static void *
1027 extfs_opendir (const vfs_path_t * vpath)
1029 struct archive *archive = NULL;
1030 char *q;
1031 struct entry *entry;
1032 struct entry **info;
1034 q = extfs_get_path (vpath, &archive, FALSE);
1035 if (q == NULL)
1036 return NULL;
1037 entry = extfs_find_entry (archive->root_entry, q, FALSE, FALSE);
1038 g_free (q);
1039 if (entry == NULL)
1040 return NULL;
1041 entry = extfs_resolve_symlinks (entry);
1042 if (entry == NULL)
1043 return NULL;
1044 if (!S_ISDIR (entry->inode->mode))
1045 ERRNOR (ENOTDIR, NULL);
1047 info = g_new (struct entry *, 2);
1048 info[0] = entry->inode->first_in_subdir;
1049 info[1] = entry->inode->first_in_subdir;
1051 return info;
1054 /* --------------------------------------------------------------------------------------------- */
1056 static void *
1057 extfs_readdir (void *data)
1059 static union vfs_dirent dir;
1060 struct entry **info = (struct entry **) data;
1062 if (*info == NULL)
1063 return NULL;
1065 g_strlcpy (dir.dent.d_name, (*info)->name, MC_MAXPATHLEN);
1067 compute_namelen (&dir.dent);
1068 *info = (*info)->next_in_dir;
1070 return (void *) &dir;
1073 /* --------------------------------------------------------------------------------------------- */
1075 static int
1076 extfs_closedir (void *data)
1078 g_free (data);
1079 return 0;
1082 /* --------------------------------------------------------------------------------------------- */
1084 static void
1085 extfs_stat_move (struct stat *buf, const struct inode *inode)
1087 buf->st_dev = inode->dev;
1088 buf->st_ino = inode->inode;
1089 buf->st_mode = inode->mode;
1090 buf->st_nlink = inode->nlink;
1091 buf->st_uid = inode->uid;
1092 buf->st_gid = inode->gid;
1093 #ifdef HAVE_STRUCT_STAT_ST_RDEV
1094 buf->st_rdev = inode->rdev;
1095 #endif
1096 buf->st_size = inode->size;
1097 #ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
1098 buf->st_blksize = RECORDSIZE;
1099 #endif
1100 #ifdef HAVE_STRUCT_STAT_ST_BLOCKS
1101 buf->st_blocks = (inode->size + RECORDSIZE - 1) / RECORDSIZE;
1102 #endif
1103 buf->st_atime = inode->atime;
1104 buf->st_mtime = inode->mtime;
1105 buf->st_ctime = inode->ctime;
1108 /* --------------------------------------------------------------------------------------------- */
1110 static int
1111 extfs_internal_stat (const vfs_path_t * vpath, struct stat *buf, gboolean resolve)
1113 struct archive *archive;
1114 const char *q;
1115 struct entry *entry;
1116 int result = -1;
1118 q = extfs_get_path_int (vpath, &archive, FALSE);
1119 if (q == NULL)
1120 goto cleanup;
1121 entry = extfs_find_entry (archive->root_entry, q, FALSE, FALSE);
1122 if (entry == NULL)
1123 goto cleanup;
1124 if (resolve)
1126 entry = extfs_resolve_symlinks (entry);
1127 if (entry == NULL)
1128 goto cleanup;
1130 extfs_stat_move (buf, entry->inode);
1131 result = 0;
1132 cleanup:
1133 return result;
1136 /* --------------------------------------------------------------------------------------------- */
1138 static int
1139 extfs_stat (const vfs_path_t * vpath, struct stat *buf)
1141 return extfs_internal_stat (vpath, buf, TRUE);
1144 /* --------------------------------------------------------------------------------------------- */
1146 static int
1147 extfs_lstat (const vfs_path_t * vpath, struct stat *buf)
1149 return extfs_internal_stat (vpath, buf, FALSE);
1152 /* --------------------------------------------------------------------------------------------- */
1154 static int
1155 extfs_fstat (void *data, struct stat *buf)
1157 struct pseudofile *file = (struct pseudofile *) data;
1159 extfs_stat_move (buf, file->entry->inode);
1160 return 0;
1163 /* --------------------------------------------------------------------------------------------- */
1165 static int
1166 extfs_readlink (const vfs_path_t * vpath, char *buf, size_t size)
1168 struct archive *archive;
1169 const char *q;
1170 size_t len;
1171 struct entry *entry;
1172 int result = -1;
1174 q = extfs_get_path_int (vpath, &archive, FALSE);
1175 if (q == NULL)
1176 goto cleanup;
1177 entry = extfs_find_entry (archive->root_entry, q, FALSE, FALSE);
1178 if (entry == NULL)
1179 goto cleanup;
1180 if (!S_ISLNK (entry->inode->mode))
1182 const vfs_path_element_t *path_element;
1184 path_element = vfs_path_get_by_index (vpath, -1);
1185 path_element->class->verrno = EINVAL;
1186 goto cleanup;
1188 len = strlen (entry->inode->linkname);
1189 if (size < len)
1190 len = size;
1191 /* readlink() does not append a NUL character to buf */
1192 result = len;
1193 memcpy (buf, entry->inode->linkname, result);
1194 cleanup:
1195 return result;
1198 /* --------------------------------------------------------------------------------------------- */
1200 static int
1201 extfs_chown (const vfs_path_t * vpath, uid_t owner, gid_t group)
1203 (void) vpath;
1204 (void) owner;
1205 (void) group;
1206 return 0;
1209 /* --------------------------------------------------------------------------------------------- */
1211 static int
1212 extfs_chmod (const vfs_path_t * vpath, mode_t mode)
1214 (void) vpath;
1215 (void) mode;
1216 return 0;
1219 /* --------------------------------------------------------------------------------------------- */
1221 static ssize_t
1222 extfs_write (void *data, const char *buf, size_t nbyte)
1224 struct pseudofile *file = (struct pseudofile *) data;
1226 file->has_changed = TRUE;
1227 return write (file->local_handle, buf, nbyte);
1230 /* --------------------------------------------------------------------------------------------- */
1232 static int
1233 extfs_unlink (const vfs_path_t * vpath)
1235 struct archive *archive;
1236 const char *q;
1237 struct entry *entry;
1238 int result = -1;
1240 q = extfs_get_path_int (vpath, &archive, FALSE);
1241 if (q == NULL)
1242 goto cleanup;
1243 entry = extfs_find_entry (archive->root_entry, q, FALSE, FALSE);
1244 if (entry == NULL)
1245 goto cleanup;
1246 entry = extfs_resolve_symlinks (entry);
1247 if (entry == NULL)
1248 goto cleanup;
1249 if (S_ISDIR (entry->inode->mode))
1251 const vfs_path_element_t *path_element;
1253 path_element = vfs_path_get_by_index (vpath, -1);
1254 path_element->class->verrno = EISDIR;
1255 goto cleanup;
1257 if (extfs_cmd (" rm ", archive, entry, ""))
1259 my_errno = EIO;
1260 goto cleanup;
1262 extfs_remove_entry (entry);
1263 result = 0;
1264 cleanup:
1265 return result;
1268 /* --------------------------------------------------------------------------------------------- */
1270 static int
1271 extfs_mkdir (const vfs_path_t * vpath, mode_t mode)
1273 struct archive *archive;
1274 const char *q;
1275 struct entry *entry;
1276 int result = -1;
1277 const vfs_path_element_t *path_element;
1279 (void) mode;
1281 path_element = vfs_path_get_by_index (vpath, -1);
1282 q = extfs_get_path_int (vpath, &archive, FALSE);
1283 if (q == NULL)
1284 goto cleanup;
1285 entry = extfs_find_entry (archive->root_entry, q, FALSE, FALSE);
1286 if (entry != NULL)
1288 path_element->class->verrno = EEXIST;
1289 goto cleanup;
1291 entry = extfs_find_entry (archive->root_entry, q, TRUE, FALSE);
1292 if (entry == NULL)
1293 goto cleanup;
1294 entry = extfs_resolve_symlinks (entry);
1295 if (entry == NULL)
1296 goto cleanup;
1297 if (!S_ISDIR (entry->inode->mode))
1299 path_element->class->verrno = ENOTDIR;
1300 goto cleanup;
1303 if (extfs_cmd (" mkdir ", archive, entry, ""))
1305 my_errno = EIO;
1306 extfs_remove_entry (entry);
1307 goto cleanup;
1309 result = 0;
1310 cleanup:
1311 return result;
1314 /* --------------------------------------------------------------------------------------------- */
1316 static int
1317 extfs_rmdir (const vfs_path_t * vpath)
1319 struct archive *archive;
1320 const char *q;
1321 struct entry *entry;
1322 int result = -1;
1324 q = extfs_get_path_int (vpath, &archive, FALSE);
1325 if (q == NULL)
1326 goto cleanup;
1327 entry = extfs_find_entry (archive->root_entry, q, FALSE, FALSE);
1328 if (entry == NULL)
1329 goto cleanup;
1330 entry = extfs_resolve_symlinks (entry);
1331 if (entry == NULL)
1332 goto cleanup;
1333 if (!S_ISDIR (entry->inode->mode))
1335 const vfs_path_element_t *path_element;
1337 path_element = vfs_path_get_by_index (vpath, -1);
1338 path_element->class->verrno = ENOTDIR;
1339 goto cleanup;
1342 if (extfs_cmd (" rmdir ", archive, entry, ""))
1344 my_errno = EIO;
1345 goto cleanup;
1347 extfs_remove_entry (entry);
1348 result = 0;
1349 cleanup:
1350 return result;
1353 /* --------------------------------------------------------------------------------------------- */
1355 static int
1356 extfs_chdir (const vfs_path_t * vpath)
1358 struct archive *archive = NULL;
1359 char *q;
1360 struct entry *entry;
1362 my_errno = ENOTDIR;
1363 q = extfs_get_path (vpath, &archive, FALSE);
1364 if (q == NULL)
1365 return -1;
1366 entry = extfs_find_entry (archive->root_entry, q, FALSE, FALSE);
1367 g_free (q);
1368 if (entry == NULL)
1369 return -1;
1370 entry = extfs_resolve_symlinks (entry);
1371 if ((entry == NULL) || (!S_ISDIR (entry->inode->mode)))
1372 return -1;
1373 my_errno = 0;
1374 return 0;
1377 /* --------------------------------------------------------------------------------------------- */
1379 static off_t
1380 extfs_lseek (void *data, off_t offset, int whence)
1382 struct pseudofile *file = (struct pseudofile *) data;
1384 return lseek (file->local_handle, offset, whence);
1387 /* --------------------------------------------------------------------------------------------- */
1389 static vfsid
1390 extfs_getid (const vfs_path_t * vpath)
1392 struct archive *archive = NULL;
1393 char *p;
1395 p = extfs_get_path (vpath, &archive, TRUE);
1396 if (p == NULL)
1397 return NULL;
1398 g_free (p);
1399 return (vfsid) archive;
1402 /* --------------------------------------------------------------------------------------------- */
1404 static int
1405 extfs_nothingisopen (vfsid id)
1407 return (((struct archive *) id)->fd_usage <= 0);
1410 /* --------------------------------------------------------------------------------------------- */
1412 static void
1413 extfs_remove_entry (struct entry *e)
1415 int i = --e->inode->nlink;
1416 struct entry *pe, *ent, *prev;
1418 if (S_ISDIR (e->inode->mode) && e->inode->first_in_subdir != NULL)
1420 struct entry *f = e->inode->first_in_subdir;
1421 e->inode->first_in_subdir = NULL;
1422 extfs_remove_entry (f);
1424 pe = e->dir;
1425 if (e == pe->inode->first_in_subdir)
1426 pe->inode->first_in_subdir = e->next_in_dir;
1428 prev = NULL;
1429 for (ent = pe->inode->first_in_subdir; ent && ent->next_in_dir; ent = ent->next_in_dir)
1430 if (e == ent->next_in_dir)
1432 prev = ent;
1433 break;
1435 if (prev)
1436 prev->next_in_dir = e->next_in_dir;
1437 if (e == pe->inode->last_in_subdir)
1438 pe->inode->last_in_subdir = prev;
1440 if (i <= 0)
1442 if (e->inode->local_filename != NULL)
1444 unlink (e->inode->local_filename);
1445 g_free (e->inode->local_filename);
1447 g_free (e->inode->linkname);
1448 g_free (e->inode);
1451 g_free (e->name);
1452 g_free (e);
1455 /* --------------------------------------------------------------------------------------------- */
1457 static void
1458 extfs_free_entry (struct entry *e)
1460 int i = --e->inode->nlink;
1462 if (S_ISDIR (e->inode->mode) && e->inode->first_in_subdir != NULL)
1464 struct entry *f = e->inode->first_in_subdir;
1466 e->inode->first_in_subdir = NULL;
1467 extfs_free_entry (f);
1469 if (i <= 0)
1471 if (e->inode->local_filename != NULL)
1473 unlink (e->inode->local_filename);
1474 g_free (e->inode->local_filename);
1476 g_free (e->inode->linkname);
1477 g_free (e->inode);
1479 if (e->next_in_dir != NULL)
1480 extfs_free_entry (e->next_in_dir);
1481 g_free (e->name);
1482 g_free (e);
1485 /* --------------------------------------------------------------------------------------------- */
1487 static void
1488 extfs_free (vfsid id)
1490 struct archive *archive = (struct archive *) id;
1492 if (archive == first_archive)
1494 first_archive = archive->next;
1496 else
1498 struct archive *parc;
1499 for (parc = first_archive; parc != NULL; parc = parc->next)
1500 if (parc->next == archive)
1502 parc->next = archive->next;
1503 break;
1506 extfs_free_archive (archive);
1509 /* --------------------------------------------------------------------------------------------- */
1511 static vfs_path_t *
1512 extfs_getlocalcopy (const vfs_path_t * vpath)
1514 struct pseudofile *fp;
1515 vfs_path_t *p;
1517 fp = (struct pseudofile *) extfs_open (vpath, O_RDONLY, 0);
1518 if (fp == NULL)
1519 return NULL;
1520 if (fp->entry->inode->local_filename == NULL)
1522 extfs_close ((void *) fp);
1523 return NULL;
1525 p = vfs_path_from_str (fp->entry->inode->local_filename);
1526 fp->archive->fd_usage++;
1527 extfs_close ((void *) fp);
1528 return p;
1531 /* --------------------------------------------------------------------------------------------- */
1533 static int
1534 extfs_ungetlocalcopy (const vfs_path_t * vpath, const vfs_path_t * local, gboolean has_changed)
1536 struct pseudofile *fp;
1538 fp = (struct pseudofile *) extfs_open (vpath, O_RDONLY, 0);
1539 if (fp == NULL)
1540 return 0;
1542 if (strcmp (fp->entry->inode->local_filename, vfs_path_get_last_path_str (local)) == 0)
1544 fp->archive->fd_usage--;
1545 if (has_changed)
1546 fp->has_changed = TRUE;
1547 extfs_close ((void *) fp);
1548 return 0;
1550 else
1552 /* Should not happen */
1553 extfs_close ((void *) fp);
1554 return 0;
1558 /* --------------------------------------------------------------------------------------------- */
1560 static gboolean
1561 extfs_get_plugins (const char *where, gboolean silent)
1563 char *dirname;
1564 GDir *dir;
1565 const char *filename;
1567 dirname = g_build_path (PATH_SEP_STR, where, MC_EXTFS_DIR, (char *) NULL);
1568 dir = g_dir_open (dirname, 0, NULL);
1570 /* We may not use vfs_die() message or message or similar,
1571 * UI is not initialized at this time and message would not
1572 * appear on screen. */
1573 if (dir == NULL)
1575 if (!silent)
1576 fprintf (stderr, _("Warning: cannot open %s directory\n"), dirname);
1577 g_free (dirname);
1578 return FALSE;
1581 if (extfs_plugins == NULL)
1582 extfs_plugins = g_array_sized_new (FALSE, TRUE, sizeof (extfs_plugin_info_t), 32);
1584 while ((filename = g_dir_read_name (dir)) != NULL)
1586 char fullname[MC_MAXPATHLEN];
1587 struct stat s;
1589 g_snprintf (fullname, sizeof (fullname), "%s" PATH_SEP_STR "%s", dirname, filename);
1591 if ((stat (fullname, &s) == 0)
1592 && S_ISREG (s.st_mode) && !S_ISDIR (s.st_mode)
1593 && (((s.st_mode & S_IXOTH) != 0) ||
1594 ((s.st_mode & S_IXUSR) != 0) || ((s.st_mode & S_IXGRP) != 0)))
1596 int f;
1598 f = open (fullname, O_RDONLY);
1600 if (f > 0)
1602 size_t len, i;
1603 extfs_plugin_info_t info;
1604 gboolean found = FALSE;
1606 close (f);
1608 /* Handle those with a trailing '+', those flag that the
1609 * file system does not require an archive to work
1611 len = strlen (filename);
1612 info.need_archive = (filename[len - 1] != '+');
1613 info.path = g_strconcat (dirname, PATH_SEP_STR, (char *) NULL);
1614 info.prefix = g_strdup (filename);
1616 /* prepare to compare file names without trailing '+' */
1617 if (!info.need_archive)
1618 info.prefix[len - 1] = '\0';
1620 /* don't overload already found plugin */
1621 for (i = 0; i < extfs_plugins->len; i++)
1623 extfs_plugin_info_t *p;
1625 p = &g_array_index (extfs_plugins, extfs_plugin_info_t, i);
1627 /* 2 files with same names cannot be in a directory */
1628 if ((strcmp (info.path, p->path) != 0)
1629 && (strcmp (info.prefix, p->prefix) == 0))
1631 found = TRUE;
1632 break;
1636 if (found)
1638 g_free (info.path);
1639 g_free (info.prefix);
1641 else
1643 /* restore file name */
1644 if (!info.need_archive)
1645 info.prefix[len - 1] = '+';
1646 g_array_append_val (extfs_plugins, info);
1652 g_dir_close (dir);
1653 g_free (dirname);
1655 return TRUE;
1658 /* --------------------------------------------------------------------------------------------- */
1660 static int
1661 extfs_init (struct vfs_class *me)
1663 gboolean d1, d2;
1665 (void) me;
1667 /* 1st: scan user directory */
1668 d1 = extfs_get_plugins (mc_config_get_data_path (), TRUE); /* silent about user dir */
1669 /* 2nd: scan system dir */
1670 d2 = extfs_get_plugins (LIBEXECDIR, d1);
1672 return (d1 || d2 ? 1 : 0);
1675 /* --------------------------------------------------------------------------------------------- */
1677 static void
1678 extfs_done (struct vfs_class *me)
1680 size_t i;
1681 struct archive *ar;
1683 (void) me;
1685 for (ar = first_archive; ar != NULL;)
1687 extfs_free ((vfsid) ar);
1688 ar = first_archive;
1691 for (i = 0; i < extfs_plugins->len; i++)
1693 extfs_plugin_info_t *info;
1695 info = &g_array_index (extfs_plugins, extfs_plugin_info_t, i);
1696 g_free (info->path);
1697 g_free (info->prefix);
1700 if (extfs_plugins != NULL)
1701 g_array_free (extfs_plugins, TRUE);
1704 /* --------------------------------------------------------------------------------------------- */
1706 static int
1707 extfs_setctl (const vfs_path_t * vpath, int ctlop, void *arg)
1709 (void) arg;
1711 if (ctlop == VFS_SETCTL_RUN)
1713 extfs_run (vpath);
1714 return 1;
1716 return 0;
1719 /* --------------------------------------------------------------------------------------------- */
1720 /*** public functions ****************************************************************************/
1721 /* --------------------------------------------------------------------------------------------- */
1723 void
1724 init_extfs (void)
1726 vfs_extfs_ops.name = "extfs";
1727 vfs_extfs_ops.init = extfs_init;
1728 vfs_extfs_ops.done = extfs_done;
1729 vfs_extfs_ops.fill_names = extfs_fill_names;
1730 vfs_extfs_ops.which = extfs_which;
1731 vfs_extfs_ops.open = extfs_open;
1732 vfs_extfs_ops.close = extfs_close;
1733 vfs_extfs_ops.read = extfs_read;
1734 vfs_extfs_ops.write = extfs_write;
1735 vfs_extfs_ops.opendir = extfs_opendir;
1736 vfs_extfs_ops.readdir = extfs_readdir;
1737 vfs_extfs_ops.closedir = extfs_closedir;
1738 vfs_extfs_ops.stat = extfs_stat;
1739 vfs_extfs_ops.lstat = extfs_lstat;
1740 vfs_extfs_ops.fstat = extfs_fstat;
1741 vfs_extfs_ops.chmod = extfs_chmod;
1742 vfs_extfs_ops.chown = extfs_chown;
1743 vfs_extfs_ops.readlink = extfs_readlink;
1744 vfs_extfs_ops.unlink = extfs_unlink;
1745 vfs_extfs_ops.chdir = extfs_chdir;
1746 vfs_extfs_ops.ferrno = extfs_errno;
1747 vfs_extfs_ops.lseek = extfs_lseek;
1748 vfs_extfs_ops.getid = extfs_getid;
1749 vfs_extfs_ops.nothingisopen = extfs_nothingisopen;
1750 vfs_extfs_ops.free = extfs_free;
1751 vfs_extfs_ops.getlocalcopy = extfs_getlocalcopy;
1752 vfs_extfs_ops.ungetlocalcopy = extfs_ungetlocalcopy;
1753 vfs_extfs_ops.mkdir = extfs_mkdir;
1754 vfs_extfs_ops.rmdir = extfs_rmdir;
1755 vfs_extfs_ops.setctl = extfs_setctl;
1756 vfs_register_class (&vfs_extfs_ops);
1759 /* --------------------------------------------------------------------------------------------- */