(mc_global_t::shell): new member to store user's shell
[midnight-commander.git] / src / vfs / extfs / extfs.c
blob945a36ff473e5342db578be076d5103b2d712a1f
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/execute.h" /* For shell_execute */
63 #include "lib/vfs/vfs.h"
64 #include "lib/vfs/utilvfs.h"
65 #include "lib/vfs/gc.h" /* vfs_rmstamp */
67 #include "extfs.h"
69 /*** global variables ****************************************************************************/
71 GArray *extfs_plugins = NULL;
73 /*** file scope macro definitions ****************************************************************/
75 #undef ERRNOR
76 #define ERRNOR(x,y) do { my_errno = x; return y; } while(0)
78 #define RECORDSIZE 512
80 /*** file scope type declarations ****************************************************************/
82 struct inode
84 nlink_t nlink;
85 struct entry *first_in_subdir; /* only used if this is a directory */
86 struct entry *last_in_subdir;
87 ino_t inode; /* This is inode # */
88 dev_t dev; /* This is an internal identification of the extfs archive */
89 struct archive *archive; /* And this is an archive structure */
90 dev_t rdev;
91 mode_t mode;
92 uid_t uid;
93 gid_t gid;
94 off_t size;
95 time_t mtime;
96 char *linkname;
97 time_t atime;
98 time_t ctime;
99 char *local_filename;
102 struct entry
104 struct entry *next_in_dir;
105 struct entry *dir;
106 char *name;
107 struct inode *inode;
110 struct pseudofile
112 struct archive *archive;
113 gboolean has_changed;
114 int local_handle;
115 struct entry *entry;
118 struct archive
120 int fstype;
121 char *name;
122 char *local_name;
123 struct stat local_stat;
124 dev_t rdev;
125 int fd_usage;
126 ino_t inode_counter;
127 struct entry *root_entry;
128 struct archive *next;
131 typedef struct
133 char *path;
134 char *prefix;
135 gboolean need_archive;
136 } extfs_plugin_info_t;
138 /*** file scope variables ************************************************************************/
140 static gboolean errloop;
141 static gboolean notadir;
143 static struct vfs_class vfs_extfs_ops;
144 static struct archive *first_archive = NULL;
145 static int my_errno = 0;
147 /*** file scope functions ************************************************************************/
148 /* --------------------------------------------------------------------------------------------- */
150 static void extfs_remove_entry (struct entry *e);
151 static void extfs_free (vfsid id);
152 static void extfs_free_entry (struct entry *e);
153 static struct entry *extfs_resolve_symlinks_int (struct entry *entry, GSList * list);
155 /* --------------------------------------------------------------------------------------------- */
157 static void
158 extfs_make_dots (struct entry *ent)
160 struct entry *entry = g_new (struct entry, 1);
161 struct entry *parentry = ent->dir;
162 struct inode *inode = ent->inode, *parent;
164 parent = (parentry != NULL) ? parentry->inode : NULL;
165 entry->name = g_strdup (".");
166 entry->inode = inode;
167 entry->dir = ent;
168 inode->local_filename = NULL;
169 inode->first_in_subdir = entry;
170 inode->nlink++;
172 entry->next_in_dir = g_new (struct entry, 1);
173 entry = entry->next_in_dir;
174 entry->name = g_strdup ("..");
175 inode->last_in_subdir = entry;
176 entry->next_in_dir = NULL;
177 if (parent != NULL)
179 entry->inode = parent;
180 entry->dir = parentry;
181 parent->nlink++;
183 else
185 entry->inode = inode;
186 entry->dir = ent;
187 inode->nlink++;
191 /* --------------------------------------------------------------------------------------------- */
193 static struct entry *
194 extfs_generate_entry (struct archive *archive,
195 const char *name, struct entry *parentry, mode_t mode)
197 mode_t myumask;
198 struct inode *inode, *parent;
199 struct entry *entry;
201 parent = (parentry != NULL) ? parentry->inode : NULL;
202 entry = g_new (struct entry, 1);
204 entry->name = g_strdup (name);
205 entry->next_in_dir = NULL;
206 entry->dir = parentry;
207 if (parent != NULL)
209 parent->last_in_subdir->next_in_dir = entry;
210 parent->last_in_subdir = entry;
212 inode = g_new (struct inode, 1);
213 entry->inode = inode;
214 inode->local_filename = NULL;
215 inode->linkname = NULL;
216 inode->last_in_subdir = NULL;
217 inode->inode = (archive->inode_counter)++;
218 inode->dev = archive->rdev;
219 inode->archive = archive;
220 myumask = umask (022);
221 umask (myumask);
222 inode->mode = mode & ~myumask;
223 mode = inode->mode;
224 inode->rdev = 0;
225 inode->uid = getuid ();
226 inode->gid = getgid ();
227 inode->size = 0;
228 inode->mtime = time (NULL);
229 inode->atime = inode->mtime;
230 inode->ctime = inode->mtime;
231 inode->nlink = 1;
232 if (S_ISDIR (mode))
233 extfs_make_dots (entry);
234 return entry;
237 /* --------------------------------------------------------------------------------------------- */
239 static struct entry *
240 extfs_find_entry_int (struct entry *dir, const char *name, GSList * list,
241 gboolean make_dirs, gboolean make_file)
243 struct entry *pent, *pdir;
244 const char *p, *name_end;
245 char *q;
246 char c = PATH_SEP;
248 if (g_path_is_absolute (name))
250 /* Handle absolute paths */
251 name = (char *) g_path_skip_root (name);
252 dir = dir->inode->archive->root_entry;
255 pent = dir;
256 p = name;
257 name_end = name + strlen (name);
259 q = strchr (p, PATH_SEP);
260 if (q == '\0')
261 q = strchr (p, '\0');
263 while ((pent != NULL) && (c != '\0') && (*p != '\0'))
265 c = *q;
266 *q = '\0';
268 if (strcmp (p, ".") != 0)
270 if (strcmp (p, "..") == 0)
271 pent = pent->dir;
272 else
274 pent = extfs_resolve_symlinks_int (pent, list);
275 if (pent == NULL)
277 *q = c;
278 return NULL;
280 if (!S_ISDIR (pent->inode->mode))
282 *q = c;
283 notadir = TRUE;
284 return NULL;
287 pdir = pent;
288 for (pent = pent->inode->first_in_subdir; pent != NULL; pent = pent->next_in_dir)
289 /* Hack: I keep the original semanthic unless
290 q+1 would break in the strchr */
291 if (strcmp (pent->name, p) == 0)
293 if (q + 1 > name_end)
295 *q = c;
296 notadir = !S_ISDIR (pent->inode->mode);
297 return pent;
299 break;
302 /* When we load archive, we create automagically
303 * non-existant directories
305 if (pent == NULL && make_dirs)
306 pent = extfs_generate_entry (dir->inode->archive, p, pdir, S_IFDIR | 0777);
307 if (pent == NULL && make_file)
308 pent = extfs_generate_entry (dir->inode->archive, p, pdir, S_IFREG | 0666);
311 /* Next iteration */
312 *q = c;
313 p = q + 1;
314 q = strchr (p, PATH_SEP);
315 if (q == '\0')
316 q = strchr (p, '\0');
318 if (pent == NULL)
319 my_errno = ENOENT;
320 return pent;
323 /* --------------------------------------------------------------------------------------------- */
325 static struct entry *
326 extfs_find_entry (struct entry *dir, const char *name, gboolean make_dirs, gboolean make_file)
328 struct entry *res;
330 errloop = FALSE;
331 notadir = FALSE;
333 res = extfs_find_entry_int (dir, name, NULL, make_dirs, make_file);
334 if (res == NULL)
336 if (errloop)
337 my_errno = ELOOP;
338 else if (notadir)
339 my_errno = ENOTDIR;
341 return res;
344 /* --------------------------------------------------------------------------------------------- */
346 static void
347 extfs_fill_names (struct vfs_class *me, fill_names_f func)
349 struct archive *a = first_archive;
351 (void) me;
353 while (a != NULL)
355 extfs_plugin_info_t *info;
356 char *name;
358 info = &g_array_index (extfs_plugins, extfs_plugin_info_t, a->fstype);
359 name =
360 g_strconcat (a->name ? a->name : "", "/", info->prefix, VFS_PATH_URL_DELIMITER,
361 (char *) NULL);
362 func (name);
363 g_free (name);
364 a = a->next;
368 /* --------------------------------------------------------------------------------------------- */
370 static void
371 extfs_free_archive (struct archive *archive)
373 extfs_free_entry (archive->root_entry);
374 if (archive->local_name != NULL)
376 struct stat my;
377 vfs_path_t *local_name_vpath, *name_vpath;
379 local_name_vpath = vfs_path_from_str (archive->local_name);
380 name_vpath = vfs_path_from_str (archive->local_name);
381 mc_stat (local_name_vpath, &my);
382 mc_ungetlocalcopy (name_vpath, local_name_vpath,
383 archive->local_stat.st_mtime != my.st_mtime);
384 vfs_path_free (local_name_vpath);
385 vfs_path_free (name_vpath);
386 g_free (archive->local_name);
388 g_free (archive->name);
389 g_free (archive);
392 /* --------------------------------------------------------------------------------------------- */
394 static FILE *
395 extfs_open_archive (int fstype, const char *name, struct archive **pparc)
397 const extfs_plugin_info_t *info;
398 static dev_t archive_counter = 0;
399 FILE *result = NULL;
400 mode_t mode;
401 char *cmd;
402 struct stat mystat;
403 struct archive *current_archive;
404 struct entry *root_entry;
405 char *tmp = NULL;
406 vfs_path_t *local_name_vpath = NULL;
407 vfs_path_t *name_vpath;
409 name_vpath = vfs_path_from_str (name);
410 info = &g_array_index (extfs_plugins, extfs_plugin_info_t, fstype);
412 if (info->need_archive)
414 if (mc_stat (name_vpath, &mystat) == -1)
415 goto ret;
417 if (!vfs_file_is_local (name_vpath))
419 local_name_vpath = mc_getlocalcopy (name_vpath);
420 if (local_name_vpath == NULL)
421 goto ret;
424 tmp = name_quote (vfs_path_get_last_path_str (name_vpath), 0);
427 cmd = g_strconcat (info->path, info->prefix, " list ",
428 vfs_path_get_last_path_str (local_name_vpath) != NULL ?
429 vfs_path_get_last_path_str (local_name_vpath) : tmp, (char *) NULL);
430 g_free (tmp);
432 open_error_pipe ();
433 result = popen (cmd, "r");
434 g_free (cmd);
435 if (result == NULL)
437 close_error_pipe (D_ERROR, NULL);
438 if (local_name_vpath != NULL)
440 mc_ungetlocalcopy (name_vpath, local_name_vpath, FALSE);
441 vfs_path_free (local_name_vpath);
443 goto ret;
446 #ifdef ___QNXNTO__
447 setvbuf (result, NULL, _IONBF, 0);
448 #endif
450 current_archive = g_new (struct archive, 1);
451 current_archive->fstype = fstype;
452 current_archive->name = (name != NULL) ? g_strdup (name) : NULL;
453 current_archive->local_name = g_strdup (vfs_path_get_last_path_str (local_name_vpath));
455 if (local_name_vpath != NULL)
457 mc_stat (local_name_vpath, &current_archive->local_stat);
458 vfs_path_free (local_name_vpath);
460 current_archive->inode_counter = 0;
461 current_archive->fd_usage = 0;
462 current_archive->rdev = archive_counter++;
463 current_archive->next = first_archive;
464 first_archive = current_archive;
465 mode = mystat.st_mode & 07777;
466 if (mode & 0400)
467 mode |= 0100;
468 if (mode & 0040)
469 mode |= 0010;
470 if (mode & 0004)
471 mode |= 0001;
472 mode |= S_IFDIR;
473 root_entry = extfs_generate_entry (current_archive, PATH_SEP_STR, NULL, mode);
474 root_entry->inode->uid = mystat.st_uid;
475 root_entry->inode->gid = mystat.st_gid;
476 root_entry->inode->atime = mystat.st_atime;
477 root_entry->inode->ctime = mystat.st_ctime;
478 root_entry->inode->mtime = mystat.st_mtime;
479 current_archive->root_entry = root_entry;
481 *pparc = current_archive;
483 ret:
484 vfs_path_free (name_vpath);
485 return result;
488 /* --------------------------------------------------------------------------------------------- */
490 * Main loop for reading an archive.
491 * Return 0 on success, -1 on error.
494 static int
495 extfs_read_archive (int fstype, const char *name, struct archive **pparc)
497 FILE *extfsd;
498 const extfs_plugin_info_t *info;
499 char *buffer;
500 struct archive *current_archive;
501 char *current_file_name, *current_link_name;
503 info = &g_array_index (extfs_plugins, extfs_plugin_info_t, fstype);
505 extfsd = extfs_open_archive (fstype, name, &current_archive);
507 if (extfsd == NULL)
509 message (D_ERROR, MSG_ERROR, _("Cannot open %s archive\n%s"), info->prefix, name);
510 return -1;
513 buffer = g_malloc (BUF_4K);
514 while (fgets (buffer, BUF_4K, extfsd) != NULL)
516 struct stat hstat;
518 current_link_name = NULL;
519 if (vfs_parse_ls_lga (buffer, &hstat, &current_file_name, &current_link_name, NULL))
521 struct entry *entry, *pent;
522 struct inode *inode;
523 char *p, *q, *cfn = current_file_name;
525 if (*cfn != '\0')
527 if (*cfn == PATH_SEP)
528 cfn++;
529 p = strchr (cfn, '\0');
530 if (p != cfn && *(p - 1) == PATH_SEP)
531 *(p - 1) = '\0';
532 p = strrchr (cfn, PATH_SEP);
533 if (p == NULL)
535 p = cfn;
536 q = strchr (cfn, '\0');
538 else
540 *(p++) = '\0';
541 q = cfn;
543 if (S_ISDIR (hstat.st_mode) && (strcmp (p, ".") == 0 || strcmp (p, "..") == 0))
544 goto read_extfs_continue;
545 pent = extfs_find_entry (current_archive->root_entry, q, TRUE, FALSE);
546 if (pent == NULL)
548 /* FIXME: Should clean everything one day */
549 g_free (buffer);
550 pclose (extfsd);
551 close_error_pipe (D_ERROR, _("Inconsistent extfs archive"));
552 return -1;
554 entry = g_new (struct entry, 1);
555 entry->name = g_strdup (p);
556 entry->next_in_dir = NULL;
557 entry->dir = pent;
558 if (pent->inode->last_in_subdir)
560 pent->inode->last_in_subdir->next_in_dir = entry;
561 pent->inode->last_in_subdir = entry;
563 if (!S_ISLNK (hstat.st_mode) && (current_link_name != NULL))
565 pent = extfs_find_entry (current_archive->root_entry,
566 current_link_name, FALSE, FALSE);
567 if (pent == NULL)
569 /* FIXME: Should clean everything one day */
570 g_free (buffer);
571 pclose (extfsd);
572 close_error_pipe (D_ERROR, _("Inconsistent extfs archive"));
573 return -1;
576 entry->inode = pent->inode;
577 pent->inode->nlink++;
579 else
581 inode = g_new (struct inode, 1);
582 entry->inode = inode;
583 inode->local_filename = NULL;
584 inode->inode = (current_archive->inode_counter)++;
585 inode->nlink = 1;
586 inode->dev = current_archive->rdev;
587 inode->archive = current_archive;
588 inode->mode = hstat.st_mode;
589 #ifdef HAVE_STRUCT_STAT_ST_RDEV
590 inode->rdev = hstat.st_rdev;
591 #else
592 inode->rdev = 0;
593 #endif
594 inode->uid = hstat.st_uid;
595 inode->gid = hstat.st_gid;
596 inode->size = hstat.st_size;
597 inode->mtime = hstat.st_mtime;
598 inode->atime = hstat.st_atime;
599 inode->ctime = hstat.st_ctime;
600 inode->first_in_subdir = NULL;
601 inode->last_in_subdir = NULL;
602 if (current_link_name != NULL && S_ISLNK (hstat.st_mode))
604 inode->linkname = current_link_name;
605 current_link_name = NULL;
607 else
609 if (S_ISLNK (hstat.st_mode))
610 inode->mode &= ~S_IFLNK; /* You *DON'T* want to do this always */
611 inode->linkname = NULL;
613 if (S_ISDIR (hstat.st_mode))
614 extfs_make_dots (entry);
617 read_extfs_continue:
618 g_free (current_file_name);
619 g_free (current_link_name);
622 g_free (buffer);
624 /* Check if extfs 'list' returned 0 */
625 if (pclose (extfsd) != 0)
627 extfs_free (current_archive);
628 close_error_pipe (D_ERROR, _("Inconsistent extfs archive"));
629 return -1;
632 close_error_pipe (D_ERROR, NULL);
633 *pparc = current_archive;
634 return 0;
637 /* --------------------------------------------------------------------------------------------- */
639 static int
640 extfs_which (struct vfs_class *me, const char *path)
642 size_t path_len;
643 size_t i;
645 (void) me;
647 path_len = strlen (path);
649 for (i = 0; i < extfs_plugins->len; i++)
651 extfs_plugin_info_t *info;
653 info = &g_array_index (extfs_plugins, extfs_plugin_info_t, i);
655 if ((strncmp (path, info->prefix, path_len) == 0)
656 && ((info->prefix[path_len] == '\0') || (info->prefix[path_len] == '+')))
657 return i;
659 return -1;
662 /* --------------------------------------------------------------------------------------------- */
664 * Dissect the path and create corresponding superblock.
666 static const char *
667 extfs_get_path_int (const vfs_path_t * vpath, struct archive **archive, gboolean do_not_open)
669 char *archive_name;
670 int result = -1;
671 struct archive *parc;
672 int fstype;
673 const vfs_path_element_t *path_element;
675 path_element = vfs_path_get_by_index (vpath, -1);
677 fstype = extfs_which (path_element->class, path_element->vfs_prefix);
678 if (fstype == -1)
679 return NULL;
681 archive_name = vfs_path_to_str_elements_count (vpath, -1);
684 * All filesystems should have some local archive, at least
685 * it can be PATH_SEP ('/').
687 for (parc = first_archive; parc != NULL; parc = parc->next)
688 if (parc->name != NULL)
690 if (strcmp (parc->name, archive_name) == 0)
692 vfs_stamp (&vfs_extfs_ops, (vfsid) parc);
693 goto return_success;
697 result = do_not_open ? -1 : extfs_read_archive (fstype, archive_name, &parc);
698 if (result == -1)
700 path_element->class->verrno = EIO;
701 g_free (archive_name);
702 return NULL;
705 return_success:
706 *archive = parc;
707 g_free (archive_name);
708 return path_element->path;
711 /* --------------------------------------------------------------------------------------------- */
713 * Dissect the path and create corresponding superblock.
714 * The result should be freed.
717 static char *
718 extfs_get_path (const vfs_path_t * vpath, struct archive **archive, gboolean do_not_open)
720 return g_strdup (extfs_get_path_int (vpath, archive, do_not_open));
723 /* --------------------------------------------------------------------------------------------- */
724 /* Return allocated path (without leading slash) inside the archive */
726 static char *
727 extfs_get_path_from_entry (struct entry *entry)
729 GString *localpath;
731 localpath = g_string_new ("");
733 while (entry->dir != NULL)
735 g_string_prepend (localpath, entry->name);
736 if (entry->dir->dir != NULL)
737 g_string_prepend_c (localpath, PATH_SEP);
738 entry = entry->dir;
741 return g_string_free (localpath, FALSE);
744 /* --------------------------------------------------------------------------------------------- */
746 static struct entry *
747 extfs_resolve_symlinks_int (struct entry *entry, GSList * list)
749 struct entry *pent = NULL;
751 if (!S_ISLNK (entry->inode->mode))
752 return entry;
754 if (g_slist_find (list, entry) != NULL)
756 /* Here we protect us against symlink looping */
757 errloop = TRUE;
759 else
761 GSList *looping;
763 looping = g_slist_prepend (list, entry);
764 pent = extfs_find_entry_int (entry->dir, entry->inode->linkname, looping, FALSE, FALSE);
765 looping = g_slist_delete_link (looping, looping);
767 if (pent == NULL)
768 my_errno = ENOENT;
771 return pent;
774 /* --------------------------------------------------------------------------------------------- */
776 static struct entry *
777 extfs_resolve_symlinks (struct entry *entry)
779 struct entry *res;
781 errloop = FALSE;
782 notadir = FALSE;
783 res = extfs_resolve_symlinks_int (entry, NULL);
784 if (res == NULL)
786 if (errloop)
787 my_errno = ELOOP;
788 else if (notadir)
789 my_errno = ENOTDIR;
791 return res;
794 /* --------------------------------------------------------------------------------------------- */
796 static char *
797 extfs_get_archive_name (struct archive *archive)
799 const char *archive_name;
801 if (archive->local_name)
802 archive_name = archive->local_name;
803 else
804 archive_name = archive->name;
806 if (!archive_name || !*archive_name)
807 return g_strdup ("no_archive_name");
808 else
810 char *ret_str;
811 vfs_path_t *vpath;
812 const vfs_path_element_t *path_element;
814 vpath = vfs_path_from_str (archive_name);
815 path_element = vfs_path_get_by_index (vpath, -1);
816 ret_str = g_strdup (path_element->path);
817 vfs_path_free (vpath);
818 return ret_str;
822 /* --------------------------------------------------------------------------------------------- */
823 /** Don't pass localname as NULL */
825 static int
826 extfs_cmd (const char *str_extfs_cmd, struct archive *archive,
827 struct entry *entry, const char *localname)
829 char *file;
830 char *quoted_file;
831 char *quoted_localname;
832 char *archive_name, *quoted_archive_name;
833 const extfs_plugin_info_t *info;
834 char *cmd;
835 int retval;
837 file = extfs_get_path_from_entry (entry);
838 quoted_file = name_quote (file, 0);
839 g_free (file);
841 archive_name = extfs_get_archive_name (archive);
842 quoted_archive_name = name_quote (archive_name, 0);
843 g_free (archive_name);
844 quoted_localname = name_quote (localname, 0);
845 info = &g_array_index (extfs_plugins, extfs_plugin_info_t, archive->fstype);
846 cmd = g_strconcat (info->path, info->prefix, str_extfs_cmd,
847 quoted_archive_name, " ", quoted_file, " ", quoted_localname, (char *) NULL);
848 g_free (quoted_file);
849 g_free (quoted_localname);
850 g_free (quoted_archive_name);
852 open_error_pipe ();
853 retval = my_system (EXECUTE_AS_SHELL, mc_global.tty.shell, cmd);
854 g_free (cmd);
855 close_error_pipe (D_ERROR, NULL);
856 return retval;
859 /* --------------------------------------------------------------------------------------------- */
861 static void
862 extfs_run (const vfs_path_t * vpath)
864 struct archive *archive = NULL;
865 char *p, *q, *archive_name, *quoted_archive_name;
866 char *cmd;
867 const extfs_plugin_info_t *info;
869 p = extfs_get_path (vpath, &archive, FALSE);
870 if (p == NULL)
871 return;
872 q = name_quote (p, 0);
873 g_free (p);
875 archive_name = extfs_get_archive_name (archive);
876 quoted_archive_name = name_quote (archive_name, 0);
877 g_free (archive_name);
878 info = &g_array_index (extfs_plugins, extfs_plugin_info_t, archive->fstype);
879 cmd =
880 g_strconcat (info->path, info->prefix, " run ", quoted_archive_name, " ", q, (char *) NULL);
881 g_free (quoted_archive_name);
882 g_free (q);
883 shell_execute (cmd, 0);
884 g_free (cmd);
887 /* --------------------------------------------------------------------------------------------- */
889 static void *
890 extfs_open (const vfs_path_t * vpath, int flags, mode_t mode)
892 struct pseudofile *extfs_info;
893 struct archive *archive = NULL;
894 char *q;
895 struct entry *entry;
896 int local_handle;
897 gboolean created = FALSE;
899 q = extfs_get_path (vpath, &archive, FALSE);
900 if (q == NULL)
901 return NULL;
902 entry = extfs_find_entry (archive->root_entry, q, FALSE, FALSE);
903 if ((entry == NULL) && ((flags & O_CREAT) != 0))
905 /* Create new entry */
906 entry = extfs_find_entry (archive->root_entry, q, FALSE, TRUE);
907 created = (entry != NULL);
910 g_free (q);
911 if (entry == NULL)
912 return NULL;
913 entry = extfs_resolve_symlinks (entry);
914 if (entry == NULL)
915 return NULL;
917 if (S_ISDIR (entry->inode->mode))
918 ERRNOR (EISDIR, NULL);
920 if (entry->inode->local_filename == NULL)
922 vfs_path_t *local_filename_vpath;
923 const char *local_filename;
925 local_handle = vfs_mkstemps (&local_filename_vpath, "extfs", entry->name);
927 if (local_handle == -1)
928 return NULL;
929 close (local_handle);
930 local_filename = vfs_path_get_by_index (local_filename_vpath, -1)->path;
932 if (!created && ((flags & O_TRUNC) == 0)
933 && extfs_cmd (" copyout ", archive, entry, local_filename))
935 unlink (local_filename);
936 vfs_path_free (local_filename_vpath);
937 my_errno = EIO;
938 return NULL;
940 entry->inode->local_filename = g_strdup (local_filename);
941 vfs_path_free (local_filename_vpath);
944 local_handle = open (entry->inode->local_filename, NO_LINEAR (flags), mode);
946 if (local_handle == -1)
948 /* file exists(may be). Need to drop O_CREAT flag and truncate file content */
949 flags = ~O_CREAT & (NO_LINEAR (flags) | O_TRUNC);
950 local_handle = open (entry->inode->local_filename, flags, mode);
953 if (local_handle == -1)
954 ERRNOR (EIO, NULL);
956 extfs_info = g_new (struct pseudofile, 1);
957 extfs_info->archive = archive;
958 extfs_info->entry = entry;
959 extfs_info->has_changed = created;
960 extfs_info->local_handle = local_handle;
962 /* i.e. we had no open files and now we have one */
963 vfs_rmstamp (&vfs_extfs_ops, (vfsid) archive);
964 archive->fd_usage++;
965 return extfs_info;
968 /* --------------------------------------------------------------------------------------------- */
970 static ssize_t
971 extfs_read (void *data, char *buffer, size_t count)
973 struct pseudofile *file = (struct pseudofile *) data;
975 return read (file->local_handle, buffer, count);
978 /* --------------------------------------------------------------------------------------------- */
980 static int
981 extfs_close (void *data)
983 struct pseudofile *file;
984 int errno_code = 0;
985 file = (struct pseudofile *) data;
987 close (file->local_handle);
989 /* Commit the file if it has changed */
990 if (file->has_changed)
992 struct stat file_status;
994 if (extfs_cmd (" copyin ", file->archive, file->entry, file->entry->inode->local_filename))
995 errno_code = EIO;
997 if (stat (file->entry->inode->local_filename, &file_status) != 0)
998 errno_code = EIO;
999 else
1000 file->entry->inode->size = file_status.st_size;
1002 file->entry->inode->mtime = time (NULL);
1005 if (--file->archive->fd_usage == 0)
1006 vfs_stamp_create (&vfs_extfs_ops, file->archive);
1008 g_free (data);
1009 if (errno_code != 0)
1010 ERRNOR (EIO, -1);
1011 return 0;
1014 /* --------------------------------------------------------------------------------------------- */
1016 static int
1017 extfs_errno (struct vfs_class *me)
1019 (void) me;
1020 return my_errno;
1023 /* --------------------------------------------------------------------------------------------- */
1025 static void *
1026 extfs_opendir (const vfs_path_t * vpath)
1028 struct archive *archive = NULL;
1029 char *q;
1030 struct entry *entry;
1031 struct entry **info;
1033 q = extfs_get_path (vpath, &archive, FALSE);
1034 if (q == NULL)
1035 return NULL;
1036 entry = extfs_find_entry (archive->root_entry, q, FALSE, FALSE);
1037 g_free (q);
1038 if (entry == NULL)
1039 return NULL;
1040 entry = extfs_resolve_symlinks (entry);
1041 if (entry == NULL)
1042 return NULL;
1043 if (!S_ISDIR (entry->inode->mode))
1044 ERRNOR (ENOTDIR, NULL);
1046 info = g_new (struct entry *, 2);
1047 info[0] = entry->inode->first_in_subdir;
1048 info[1] = entry->inode->first_in_subdir;
1050 return info;
1053 /* --------------------------------------------------------------------------------------------- */
1055 static void *
1056 extfs_readdir (void *data)
1058 static union vfs_dirent dir;
1059 struct entry **info = (struct entry **) data;
1061 if (*info == NULL)
1062 return NULL;
1064 g_strlcpy (dir.dent.d_name, (*info)->name, MC_MAXPATHLEN);
1066 compute_namelen (&dir.dent);
1067 *info = (*info)->next_in_dir;
1069 return (void *) &dir;
1072 /* --------------------------------------------------------------------------------------------- */
1074 static int
1075 extfs_closedir (void *data)
1077 g_free (data);
1078 return 0;
1081 /* --------------------------------------------------------------------------------------------- */
1083 static void
1084 extfs_stat_move (struct stat *buf, const struct inode *inode)
1086 buf->st_dev = inode->dev;
1087 buf->st_ino = inode->inode;
1088 buf->st_mode = inode->mode;
1089 buf->st_nlink = inode->nlink;
1090 buf->st_uid = inode->uid;
1091 buf->st_gid = inode->gid;
1092 #ifdef HAVE_STRUCT_STAT_ST_RDEV
1093 buf->st_rdev = inode->rdev;
1094 #endif
1095 buf->st_size = inode->size;
1096 #ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
1097 buf->st_blksize = RECORDSIZE;
1098 #endif
1099 #ifdef HAVE_STRUCT_STAT_ST_BLOCKS
1100 buf->st_blocks = (inode->size + RECORDSIZE - 1) / RECORDSIZE;
1101 #endif
1102 buf->st_atime = inode->atime;
1103 buf->st_mtime = inode->mtime;
1104 buf->st_ctime = inode->ctime;
1107 /* --------------------------------------------------------------------------------------------- */
1109 static int
1110 extfs_internal_stat (const vfs_path_t * vpath, struct stat *buf, gboolean resolve)
1112 struct archive *archive;
1113 const char *q;
1114 struct entry *entry;
1115 int result = -1;
1117 q = extfs_get_path_int (vpath, &archive, FALSE);
1118 if (q == NULL)
1119 goto cleanup;
1120 entry = extfs_find_entry (archive->root_entry, q, FALSE, FALSE);
1121 if (entry == NULL)
1122 goto cleanup;
1123 if (resolve)
1125 entry = extfs_resolve_symlinks (entry);
1126 if (entry == NULL)
1127 goto cleanup;
1129 extfs_stat_move (buf, entry->inode);
1130 result = 0;
1131 cleanup:
1132 return result;
1135 /* --------------------------------------------------------------------------------------------- */
1137 static int
1138 extfs_stat (const vfs_path_t * vpath, struct stat *buf)
1140 return extfs_internal_stat (vpath, buf, TRUE);
1143 /* --------------------------------------------------------------------------------------------- */
1145 static int
1146 extfs_lstat (const vfs_path_t * vpath, struct stat *buf)
1148 return extfs_internal_stat (vpath, buf, FALSE);
1151 /* --------------------------------------------------------------------------------------------- */
1153 static int
1154 extfs_fstat (void *data, struct stat *buf)
1156 struct pseudofile *file = (struct pseudofile *) data;
1158 extfs_stat_move (buf, file->entry->inode);
1159 return 0;
1162 /* --------------------------------------------------------------------------------------------- */
1164 static int
1165 extfs_readlink (const vfs_path_t * vpath, char *buf, size_t size)
1167 struct archive *archive;
1168 const char *q;
1169 size_t len;
1170 struct entry *entry;
1171 int result = -1;
1173 q = extfs_get_path_int (vpath, &archive, FALSE);
1174 if (q == NULL)
1175 goto cleanup;
1176 entry = extfs_find_entry (archive->root_entry, q, FALSE, FALSE);
1177 if (entry == NULL)
1178 goto cleanup;
1179 if (!S_ISLNK (entry->inode->mode))
1181 const vfs_path_element_t *path_element;
1183 path_element = vfs_path_get_by_index (vpath, -1);
1184 path_element->class->verrno = EINVAL;
1185 goto cleanup;
1187 len = strlen (entry->inode->linkname);
1188 if (size < len)
1189 len = size;
1190 /* readlink() does not append a NUL character to buf */
1191 result = len;
1192 memcpy (buf, entry->inode->linkname, result);
1193 cleanup:
1194 return result;
1197 /* --------------------------------------------------------------------------------------------- */
1199 static int
1200 extfs_chown (const vfs_path_t * vpath, uid_t owner, gid_t group)
1202 (void) vpath;
1203 (void) owner;
1204 (void) group;
1205 return 0;
1208 /* --------------------------------------------------------------------------------------------- */
1210 static int
1211 extfs_chmod (const vfs_path_t * vpath, mode_t mode)
1213 (void) vpath;
1214 (void) mode;
1215 return 0;
1218 /* --------------------------------------------------------------------------------------------- */
1220 static ssize_t
1221 extfs_write (void *data, const char *buf, size_t nbyte)
1223 struct pseudofile *file = (struct pseudofile *) data;
1225 file->has_changed = TRUE;
1226 return write (file->local_handle, buf, nbyte);
1229 /* --------------------------------------------------------------------------------------------- */
1231 static int
1232 extfs_unlink (const vfs_path_t * vpath)
1234 struct archive *archive;
1235 const char *q;
1236 struct entry *entry;
1237 int result = -1;
1239 q = extfs_get_path_int (vpath, &archive, FALSE);
1240 if (q == NULL)
1241 goto cleanup;
1242 entry = extfs_find_entry (archive->root_entry, q, FALSE, FALSE);
1243 if (entry == NULL)
1244 goto cleanup;
1245 entry = extfs_resolve_symlinks (entry);
1246 if (entry == NULL)
1247 goto cleanup;
1248 if (S_ISDIR (entry->inode->mode))
1250 const vfs_path_element_t *path_element;
1252 path_element = vfs_path_get_by_index (vpath, -1);
1253 path_element->class->verrno = EISDIR;
1254 goto cleanup;
1256 if (extfs_cmd (" rm ", archive, entry, ""))
1258 my_errno = EIO;
1259 goto cleanup;
1261 extfs_remove_entry (entry);
1262 result = 0;
1263 cleanup:
1264 return result;
1267 /* --------------------------------------------------------------------------------------------- */
1269 static int
1270 extfs_mkdir (const vfs_path_t * vpath, mode_t mode)
1272 struct archive *archive;
1273 const char *q;
1274 struct entry *entry;
1275 int result = -1;
1276 const vfs_path_element_t *path_element;
1278 (void) mode;
1280 path_element = vfs_path_get_by_index (vpath, -1);
1281 q = extfs_get_path_int (vpath, &archive, FALSE);
1282 if (q == NULL)
1283 goto cleanup;
1284 entry = extfs_find_entry (archive->root_entry, q, FALSE, FALSE);
1285 if (entry != NULL)
1287 path_element->class->verrno = EEXIST;
1288 goto cleanup;
1290 entry = extfs_find_entry (archive->root_entry, q, TRUE, FALSE);
1291 if (entry == NULL)
1292 goto cleanup;
1293 entry = extfs_resolve_symlinks (entry);
1294 if (entry == NULL)
1295 goto cleanup;
1296 if (!S_ISDIR (entry->inode->mode))
1298 path_element->class->verrno = ENOTDIR;
1299 goto cleanup;
1302 if (extfs_cmd (" mkdir ", archive, entry, ""))
1304 my_errno = EIO;
1305 extfs_remove_entry (entry);
1306 goto cleanup;
1308 result = 0;
1309 cleanup:
1310 return result;
1313 /* --------------------------------------------------------------------------------------------- */
1315 static int
1316 extfs_rmdir (const vfs_path_t * vpath)
1318 struct archive *archive;
1319 const char *q;
1320 struct entry *entry;
1321 int result = -1;
1323 q = extfs_get_path_int (vpath, &archive, FALSE);
1324 if (q == NULL)
1325 goto cleanup;
1326 entry = extfs_find_entry (archive->root_entry, q, FALSE, FALSE);
1327 if (entry == NULL)
1328 goto cleanup;
1329 entry = extfs_resolve_symlinks (entry);
1330 if (entry == NULL)
1331 goto cleanup;
1332 if (!S_ISDIR (entry->inode->mode))
1334 const vfs_path_element_t *path_element;
1336 path_element = vfs_path_get_by_index (vpath, -1);
1337 path_element->class->verrno = ENOTDIR;
1338 goto cleanup;
1341 if (extfs_cmd (" rmdir ", archive, entry, ""))
1343 my_errno = EIO;
1344 goto cleanup;
1346 extfs_remove_entry (entry);
1347 result = 0;
1348 cleanup:
1349 return result;
1352 /* --------------------------------------------------------------------------------------------- */
1354 static int
1355 extfs_chdir (const vfs_path_t * vpath)
1357 struct archive *archive = NULL;
1358 char *q;
1359 struct entry *entry;
1361 my_errno = ENOTDIR;
1362 q = extfs_get_path (vpath, &archive, FALSE);
1363 if (q == NULL)
1364 return -1;
1365 entry = extfs_find_entry (archive->root_entry, q, FALSE, FALSE);
1366 g_free (q);
1367 if (entry == NULL)
1368 return -1;
1369 entry = extfs_resolve_symlinks (entry);
1370 if ((entry == NULL) || (!S_ISDIR (entry->inode->mode)))
1371 return -1;
1372 my_errno = 0;
1373 return 0;
1376 /* --------------------------------------------------------------------------------------------- */
1378 static off_t
1379 extfs_lseek (void *data, off_t offset, int whence)
1381 struct pseudofile *file = (struct pseudofile *) data;
1383 return lseek (file->local_handle, offset, whence);
1386 /* --------------------------------------------------------------------------------------------- */
1388 static vfsid
1389 extfs_getid (const vfs_path_t * vpath)
1391 struct archive *archive = NULL;
1392 char *p;
1394 p = extfs_get_path (vpath, &archive, TRUE);
1395 if (p == NULL)
1396 return NULL;
1397 g_free (p);
1398 return (vfsid) archive;
1401 /* --------------------------------------------------------------------------------------------- */
1403 static int
1404 extfs_nothingisopen (vfsid id)
1406 return (((struct archive *) id)->fd_usage <= 0);
1409 /* --------------------------------------------------------------------------------------------- */
1411 static void
1412 extfs_remove_entry (struct entry *e)
1414 int i = --e->inode->nlink;
1415 struct entry *pe, *ent, *prev;
1417 if (S_ISDIR (e->inode->mode) && e->inode->first_in_subdir != NULL)
1419 struct entry *f = e->inode->first_in_subdir;
1420 e->inode->first_in_subdir = NULL;
1421 extfs_remove_entry (f);
1423 pe = e->dir;
1424 if (e == pe->inode->first_in_subdir)
1425 pe->inode->first_in_subdir = e->next_in_dir;
1427 prev = NULL;
1428 for (ent = pe->inode->first_in_subdir; ent && ent->next_in_dir; ent = ent->next_in_dir)
1429 if (e == ent->next_in_dir)
1431 prev = ent;
1432 break;
1434 if (prev)
1435 prev->next_in_dir = e->next_in_dir;
1436 if (e == pe->inode->last_in_subdir)
1437 pe->inode->last_in_subdir = prev;
1439 if (i <= 0)
1441 if (e->inode->local_filename != NULL)
1443 unlink (e->inode->local_filename);
1444 g_free (e->inode->local_filename);
1446 g_free (e->inode->linkname);
1447 g_free (e->inode);
1450 g_free (e->name);
1451 g_free (e);
1454 /* --------------------------------------------------------------------------------------------- */
1456 static void
1457 extfs_free_entry (struct entry *e)
1459 int i = --e->inode->nlink;
1461 if (S_ISDIR (e->inode->mode) && e->inode->first_in_subdir != NULL)
1463 struct entry *f = e->inode->first_in_subdir;
1465 e->inode->first_in_subdir = NULL;
1466 extfs_free_entry (f);
1468 if (i <= 0)
1470 if (e->inode->local_filename != NULL)
1472 unlink (e->inode->local_filename);
1473 g_free (e->inode->local_filename);
1475 g_free (e->inode->linkname);
1476 g_free (e->inode);
1478 if (e->next_in_dir != NULL)
1479 extfs_free_entry (e->next_in_dir);
1480 g_free (e->name);
1481 g_free (e);
1484 /* --------------------------------------------------------------------------------------------- */
1486 static void
1487 extfs_free (vfsid id)
1489 struct archive *archive = (struct archive *) id;
1491 if (archive == first_archive)
1493 first_archive = archive->next;
1495 else
1497 struct archive *parc;
1498 for (parc = first_archive; parc != NULL; parc = parc->next)
1499 if (parc->next == archive)
1501 parc->next = archive->next;
1502 break;
1505 extfs_free_archive (archive);
1508 /* --------------------------------------------------------------------------------------------- */
1510 static vfs_path_t *
1511 extfs_getlocalcopy (const vfs_path_t * vpath)
1513 struct pseudofile *fp;
1514 vfs_path_t *p;
1516 fp = (struct pseudofile *) extfs_open (vpath, O_RDONLY, 0);
1517 if (fp == NULL)
1518 return NULL;
1519 if (fp->entry->inode->local_filename == NULL)
1521 extfs_close ((void *) fp);
1522 return NULL;
1524 p = vfs_path_from_str (fp->entry->inode->local_filename);
1525 fp->archive->fd_usage++;
1526 extfs_close ((void *) fp);
1527 return p;
1530 /* --------------------------------------------------------------------------------------------- */
1532 static int
1533 extfs_ungetlocalcopy (const vfs_path_t * vpath, const vfs_path_t * local, gboolean has_changed)
1535 struct pseudofile *fp;
1537 fp = (struct pseudofile *) extfs_open (vpath, O_RDONLY, 0);
1538 if (fp == NULL)
1539 return 0;
1541 if (strcmp (fp->entry->inode->local_filename, vfs_path_get_last_path_str (local)) == 0)
1543 fp->archive->fd_usage--;
1544 if (has_changed)
1545 fp->has_changed = TRUE;
1546 extfs_close ((void *) fp);
1547 return 0;
1549 else
1551 /* Should not happen */
1552 extfs_close ((void *) fp);
1553 return 0;
1557 /* --------------------------------------------------------------------------------------------- */
1559 static gboolean
1560 extfs_get_plugins (const char *where, gboolean silent)
1562 char *dirname;
1563 GDir *dir;
1564 const char *filename;
1566 dirname = g_build_path (PATH_SEP_STR, where, MC_EXTFS_DIR, (char *) NULL);
1567 dir = g_dir_open (dirname, 0, NULL);
1569 /* We may not use vfs_die() message or message or similar,
1570 * UI is not initialized at this time and message would not
1571 * appear on screen. */
1572 if (dir == NULL)
1574 if (!silent)
1575 fprintf (stderr, _("Warning: cannot open %s directory\n"), dirname);
1576 g_free (dirname);
1577 return FALSE;
1580 if (extfs_plugins == NULL)
1581 extfs_plugins = g_array_sized_new (FALSE, TRUE, sizeof (extfs_plugin_info_t), 32);
1583 while ((filename = g_dir_read_name (dir)) != NULL)
1585 char fullname[MC_MAXPATHLEN];
1586 struct stat s;
1588 g_snprintf (fullname, sizeof (fullname), "%s" PATH_SEP_STR "%s", dirname, filename);
1590 if ((stat (fullname, &s) == 0)
1591 && S_ISREG (s.st_mode) && !S_ISDIR (s.st_mode)
1592 && (((s.st_mode & S_IXOTH) != 0) ||
1593 ((s.st_mode & S_IXUSR) != 0) || ((s.st_mode & S_IXGRP) != 0)))
1595 int f;
1597 f = open (fullname, O_RDONLY);
1599 if (f > 0)
1601 size_t len, i;
1602 extfs_plugin_info_t info;
1603 gboolean found = FALSE;
1605 close (f);
1607 /* Handle those with a trailing '+', those flag that the
1608 * file system does not require an archive to work
1610 len = strlen (filename);
1611 info.need_archive = (filename[len - 1] != '+');
1612 info.path = g_strconcat (dirname, PATH_SEP_STR, (char *) NULL);
1613 info.prefix = g_strdup (filename);
1615 /* prepare to compare file names without trailing '+' */
1616 if (!info.need_archive)
1617 info.prefix[len - 1] = '\0';
1619 /* don't overload already found plugin */
1620 for (i = 0; i < extfs_plugins->len; i++)
1622 extfs_plugin_info_t *p;
1624 p = &g_array_index (extfs_plugins, extfs_plugin_info_t, i);
1626 /* 2 files with same names cannot be in a directory */
1627 if ((strcmp (info.path, p->path) != 0)
1628 && (strcmp (info.prefix, p->prefix) == 0))
1630 found = TRUE;
1631 break;
1635 if (found)
1637 g_free (info.path);
1638 g_free (info.prefix);
1640 else
1642 /* restore file name */
1643 if (!info.need_archive)
1644 info.prefix[len - 1] = '+';
1645 g_array_append_val (extfs_plugins, info);
1651 g_dir_close (dir);
1652 g_free (dirname);
1654 return TRUE;
1657 /* --------------------------------------------------------------------------------------------- */
1659 static int
1660 extfs_init (struct vfs_class *me)
1662 gboolean d1, d2;
1664 (void) me;
1666 /* 1st: scan user directory */
1667 d1 = extfs_get_plugins (mc_config_get_data_path (), TRUE); /* silent about user dir */
1668 /* 2nd: scan system dir */
1669 d2 = extfs_get_plugins (LIBEXECDIR, d1);
1671 return (d1 || d2 ? 1 : 0);
1674 /* --------------------------------------------------------------------------------------------- */
1676 static void
1677 extfs_done (struct vfs_class *me)
1679 size_t i;
1680 struct archive *ar;
1682 (void) me;
1684 for (ar = first_archive; ar != NULL;)
1686 extfs_free ((vfsid) ar);
1687 ar = first_archive;
1690 for (i = 0; i < extfs_plugins->len; i++)
1692 extfs_plugin_info_t *info;
1694 info = &g_array_index (extfs_plugins, extfs_plugin_info_t, i);
1695 g_free (info->path);
1696 g_free (info->prefix);
1699 if (extfs_plugins != NULL)
1700 g_array_free (extfs_plugins, TRUE);
1703 /* --------------------------------------------------------------------------------------------- */
1705 static int
1706 extfs_setctl (const vfs_path_t * vpath, int ctlop, void *arg)
1708 (void) arg;
1710 if (ctlop == VFS_SETCTL_RUN)
1712 extfs_run (vpath);
1713 return 1;
1715 return 0;
1718 /* --------------------------------------------------------------------------------------------- */
1719 /*** public functions ****************************************************************************/
1720 /* --------------------------------------------------------------------------------------------- */
1722 void
1723 init_extfs (void)
1725 vfs_extfs_ops.name = "extfs";
1726 vfs_extfs_ops.init = extfs_init;
1727 vfs_extfs_ops.done = extfs_done;
1728 vfs_extfs_ops.fill_names = extfs_fill_names;
1729 vfs_extfs_ops.which = extfs_which;
1730 vfs_extfs_ops.open = extfs_open;
1731 vfs_extfs_ops.close = extfs_close;
1732 vfs_extfs_ops.read = extfs_read;
1733 vfs_extfs_ops.write = extfs_write;
1734 vfs_extfs_ops.opendir = extfs_opendir;
1735 vfs_extfs_ops.readdir = extfs_readdir;
1736 vfs_extfs_ops.closedir = extfs_closedir;
1737 vfs_extfs_ops.stat = extfs_stat;
1738 vfs_extfs_ops.lstat = extfs_lstat;
1739 vfs_extfs_ops.fstat = extfs_fstat;
1740 vfs_extfs_ops.chmod = extfs_chmod;
1741 vfs_extfs_ops.chown = extfs_chown;
1742 vfs_extfs_ops.readlink = extfs_readlink;
1743 vfs_extfs_ops.unlink = extfs_unlink;
1744 vfs_extfs_ops.chdir = extfs_chdir;
1745 vfs_extfs_ops.ferrno = extfs_errno;
1746 vfs_extfs_ops.lseek = extfs_lseek;
1747 vfs_extfs_ops.getid = extfs_getid;
1748 vfs_extfs_ops.nothingisopen = extfs_nothingisopen;
1749 vfs_extfs_ops.free = extfs_free;
1750 vfs_extfs_ops.getlocalcopy = extfs_getlocalcopy;
1751 vfs_extfs_ops.ungetlocalcopy = extfs_ungetlocalcopy;
1752 vfs_extfs_ops.mkdir = extfs_mkdir;
1753 vfs_extfs_ops.rmdir = extfs_rmdir;
1754 vfs_extfs_ops.setctl = extfs_setctl;
1755 vfs_register_class (&vfs_extfs_ops);
1758 /* --------------------------------------------------------------------------------------------- */