Following prototypes of functions was changed in VFS-module API:
[midnight-commander.git] / src / vfs / extfs / extfs.c
blob8dde4109bc1b6e5e9706520153e5c8af81fd3007
1 /* Virtual File System: External file system.
2 Copyright (C) 1995, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
3 2006, 2007, 2009 Free Software Foundation, Inc.
5 Written by: 1995 Jakub Jelinek
6 Rewritten by: 1998 Pavel Machek
7 Additional changes by: 1999 Andrew T. Veliath
9 This program is free software; you can redistribute it and/or
10 modify it under the terms of the GNU Library General Public License
11 as published by the Free Software Foundation; either version 2 of
12 the License, or (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU Library General Public License for more details.
19 You should have received a copy of the GNU Library General Public
20 License along with this program; if not, write to the Free Software
21 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
23 /**
24 * \file
25 * \brief Source: Virtual File System: External file system
26 * \author Jakub Jelinek
27 * \author Pavel Machek
28 * \author Andrew T. Veliath
29 * \date 1995, 1998, 1999
32 /* Namespace: init_extfs */
34 #include <config.h>
36 #include <stdio.h>
37 #include <ctype.h>
38 #include <string.h>
39 #include <stdlib.h>
40 #include <sys/types.h>
41 #include <sys/stat.h>
42 #include <unistd.h>
43 #include <signal.h>
44 #include <fcntl.h>
45 #include <errno.h>
46 #include <sys/wait.h>
47 #include <unistd.h>
49 #include "lib/global.h"
50 #include "lib/fileloc.h"
51 #include "lib/mcconfig.h"
52 #include "lib/util.h"
53 #include "lib/widget.h" /* message() */
55 #include "src/main.h" /* shell */
56 #include "src/execute.h" /* For shell_execute */
58 #include "lib/vfs/vfs.h"
59 #include "lib/vfs/utilvfs.h"
60 #include "lib/vfs/gc.h" /* vfs_rmstamp */
62 #include "extfs.h"
64 /*** global variables ****************************************************************************/
66 GArray *extfs_plugins = NULL;
68 /*** file scope macro definitions ****************************************************************/
70 #undef ERRNOR
71 #define ERRNOR(x,y) do { my_errno = x; return y; } while(0)
73 #define RECORDSIZE 512
75 /*** file scope type declarations ****************************************************************/
77 struct inode
79 nlink_t nlink;
80 struct entry *first_in_subdir; /* only used if this is a directory */
81 struct entry *last_in_subdir;
82 ino_t inode; /* This is inode # */
83 dev_t dev; /* This is an internal identification of the extfs archive */
84 struct archive *archive; /* And this is an archive structure */
85 dev_t rdev;
86 mode_t mode;
87 uid_t uid;
88 gid_t gid;
89 off_t size;
90 time_t mtime;
91 char *linkname;
92 time_t atime;
93 time_t ctime;
94 char *local_filename;
97 struct entry
99 struct entry *next_in_dir;
100 struct entry *dir;
101 char *name;
102 struct inode *inode;
105 struct pseudofile
107 struct archive *archive;
108 gboolean has_changed;
109 int local_handle;
110 struct entry *entry;
113 struct archive
115 int fstype;
116 char *name;
117 char *local_name;
118 struct stat local_stat;
119 dev_t rdev;
120 int fd_usage;
121 ino_t inode_counter;
122 struct entry *root_entry;
123 struct archive *next;
126 typedef struct
128 char *path;
129 char *prefix;
130 gboolean need_archive;
131 } extfs_plugin_info_t;
133 /*** file scope variables ************************************************************************/
135 static gboolean errloop;
136 static gboolean notadir;
138 static struct vfs_class vfs_extfs_ops;
139 static struct archive *first_archive = NULL;
140 static int my_errno = 0;
142 /*** file scope functions ************************************************************************/
143 /* --------------------------------------------------------------------------------------------- */
145 static void extfs_remove_entry (struct entry *e);
146 static void extfs_free (vfsid id);
147 static void extfs_free_entry (struct entry *e);
148 static struct entry *extfs_resolve_symlinks_int (struct entry *entry, GSList * list);
150 /* --------------------------------------------------------------------------------------------- */
152 static void
153 extfs_make_dots (struct entry *ent)
155 struct entry *entry = g_new (struct entry, 1);
156 struct entry *parentry = ent->dir;
157 struct inode *inode = ent->inode, *parent;
159 parent = (parentry != NULL) ? parentry->inode : NULL;
160 entry->name = g_strdup (".");
161 entry->inode = inode;
162 entry->dir = ent;
163 inode->local_filename = NULL;
164 inode->first_in_subdir = entry;
165 inode->nlink++;
167 entry->next_in_dir = g_new (struct entry, 1);
168 entry = entry->next_in_dir;
169 entry->name = g_strdup ("..");
170 inode->last_in_subdir = entry;
171 entry->next_in_dir = NULL;
172 if (parent != NULL)
174 entry->inode = parent;
175 entry->dir = parentry;
176 parent->nlink++;
178 else
180 entry->inode = inode;
181 entry->dir = ent;
182 inode->nlink++;
186 /* --------------------------------------------------------------------------------------------- */
188 static struct entry *
189 extfs_generate_entry (struct archive *archive,
190 const char *name, struct entry *parentry, mode_t mode)
192 mode_t myumask;
193 struct inode *inode, *parent;
194 struct entry *entry;
196 parent = (parentry != NULL) ? parentry->inode : NULL;
197 entry = g_new (struct entry, 1);
199 entry->name = g_strdup (name);
200 entry->next_in_dir = NULL;
201 entry->dir = parentry;
202 if (parent != NULL)
204 parent->last_in_subdir->next_in_dir = entry;
205 parent->last_in_subdir = entry;
207 inode = g_new (struct inode, 1);
208 entry->inode = inode;
209 inode->local_filename = NULL;
210 inode->linkname = NULL;
211 inode->last_in_subdir = NULL;
212 inode->inode = (archive->inode_counter)++;
213 inode->dev = archive->rdev;
214 inode->archive = archive;
215 myumask = umask (022);
216 umask (myumask);
217 inode->mode = mode & ~myumask;
218 mode = inode->mode;
219 inode->rdev = 0;
220 inode->uid = getuid ();
221 inode->gid = getgid ();
222 inode->size = 0;
223 inode->mtime = time (NULL);
224 inode->atime = inode->mtime;
225 inode->ctime = inode->mtime;
226 inode->nlink = 1;
227 if (S_ISDIR (mode))
228 extfs_make_dots (entry);
229 return entry;
232 /* --------------------------------------------------------------------------------------------- */
234 static struct entry *
235 extfs_find_entry_int (struct entry *dir, char *name, GSList * list,
236 gboolean make_dirs, gboolean make_file)
238 struct entry *pent, *pdir;
239 char *p, *q, *name_end;
240 char c = PATH_SEP;
242 if (g_path_is_absolute (name))
244 /* Handle absolute paths */
245 name = (char *) g_path_skip_root (name);
246 dir = dir->inode->archive->root_entry;
249 pent = dir;
250 p = name;
251 name_end = name + strlen (name);
253 q = strchr (p, PATH_SEP);
254 if (q == '\0')
255 q = strchr (p, '\0');
257 while ((pent != NULL) && (c != '\0') && (*p != '\0'))
259 c = *q;
260 *q = '\0';
262 if (strcmp (p, ".") != 0)
264 if (strcmp (p, "..") == 0)
265 pent = pent->dir;
266 else
268 pent = extfs_resolve_symlinks_int (pent, list);
269 if (pent == NULL)
271 *q = c;
272 return NULL;
274 if (!S_ISDIR (pent->inode->mode))
276 *q = c;
277 notadir = TRUE;
278 return NULL;
281 pdir = pent;
282 for (pent = pent->inode->first_in_subdir; pent != NULL; pent = pent->next_in_dir)
283 /* Hack: I keep the original semanthic unless
284 q+1 would break in the strchr */
285 if (strcmp (pent->name, p) == 0)
287 if (q + 1 > name_end)
289 *q = c;
290 notadir = !S_ISDIR (pent->inode->mode);
291 return pent;
293 break;
296 /* When we load archive, we create automagically
297 * non-existant directories
299 if (pent == NULL && make_dirs)
300 pent = extfs_generate_entry (dir->inode->archive, p, pdir, S_IFDIR | 0777);
301 if (pent == NULL && make_file)
302 pent = extfs_generate_entry (dir->inode->archive, p, pdir, S_IFREG | 0666);
305 /* Next iteration */
306 *q = c;
307 p = q + 1;
308 q = strchr (p, PATH_SEP);
309 if (q == '\0')
310 q = strchr (p, '\0');
312 if (pent == NULL)
313 my_errno = ENOENT;
314 return pent;
317 /* --------------------------------------------------------------------------------------------- */
319 static struct entry *
320 extfs_find_entry (struct entry *dir, char *name, gboolean make_dirs, gboolean make_file)
322 struct entry *res;
324 errloop = FALSE;
325 notadir = FALSE;
327 res = extfs_find_entry_int (dir, name, NULL, make_dirs, make_file);
328 if (res == NULL)
330 if (errloop)
331 my_errno = ELOOP;
332 else if (notadir)
333 my_errno = ENOTDIR;
335 return res;
338 /* --------------------------------------------------------------------------------------------- */
340 static void
341 extfs_fill_names (struct vfs_class *me, fill_names_f func)
343 struct archive *a = first_archive;
345 (void) me;
347 while (a != NULL)
349 extfs_plugin_info_t *info;
350 char *name;
352 info = &g_array_index (extfs_plugins, extfs_plugin_info_t, a->fstype);
353 name = g_strconcat (a->name ? a->name : "", "#", info->prefix, (char *) NULL);
354 func (name);
355 g_free (name);
356 a = a->next;
360 /* --------------------------------------------------------------------------------------------- */
362 static void
363 extfs_free_archive (struct archive *archive)
365 extfs_free_entry (archive->root_entry);
366 if (archive->local_name != NULL)
368 struct stat my;
370 mc_stat (archive->local_name, &my);
371 mc_ungetlocalcopy (archive->name, archive->local_name,
372 archive->local_stat.st_mtime != my.st_mtime);
373 g_free (archive->local_name);
375 g_free (archive->name);
376 g_free (archive);
379 /* --------------------------------------------------------------------------------------------- */
381 static FILE *
382 extfs_open_archive (int fstype, const char *name, struct archive **pparc)
384 const extfs_plugin_info_t *info;
385 static dev_t archive_counter = 0;
386 FILE *result;
387 mode_t mode;
388 char *cmd;
389 struct stat mystat;
390 struct archive *current_archive;
391 struct entry *root_entry;
392 char *local_name = NULL, *tmp = NULL;
394 info = &g_array_index (extfs_plugins, extfs_plugin_info_t, fstype);
396 if (info->need_archive)
398 if (mc_stat (name, &mystat) == -1)
399 return NULL;
401 if (!vfs_file_is_local (name))
403 local_name = mc_getlocalcopy (name);
404 if (local_name == NULL)
405 return NULL;
408 tmp = name_quote (name, 0);
411 cmd = g_strconcat (info->path, info->prefix, " list ",
412 local_name != NULL ? local_name : tmp, (char *) NULL);
413 g_free (tmp);
415 open_error_pipe ();
416 result = popen (cmd, "r");
417 g_free (cmd);
418 if (result == NULL)
420 close_error_pipe (D_ERROR, NULL);
421 if (local_name != NULL)
423 mc_ungetlocalcopy (name, local_name, 0);
424 g_free (local_name);
426 return NULL;
429 #ifdef ___QNXNTO__
430 setvbuf (result, NULL, _IONBF, 0);
431 #endif
433 current_archive = g_new (struct archive, 1);
434 current_archive->fstype = fstype;
435 current_archive->name = name ? g_strdup (name) : NULL;
436 current_archive->local_name = local_name;
438 if (local_name != NULL)
439 mc_stat (local_name, &current_archive->local_stat);
440 current_archive->inode_counter = 0;
441 current_archive->fd_usage = 0;
442 current_archive->rdev = archive_counter++;
443 current_archive->next = first_archive;
444 first_archive = current_archive;
445 mode = mystat.st_mode & 07777;
446 if (mode & 0400)
447 mode |= 0100;
448 if (mode & 0040)
449 mode |= 0010;
450 if (mode & 0004)
451 mode |= 0001;
452 mode |= S_IFDIR;
453 root_entry = extfs_generate_entry (current_archive, PATH_SEP_STR, NULL, mode);
454 root_entry->inode->uid = mystat.st_uid;
455 root_entry->inode->gid = mystat.st_gid;
456 root_entry->inode->atime = mystat.st_atime;
457 root_entry->inode->ctime = mystat.st_ctime;
458 root_entry->inode->mtime = mystat.st_mtime;
459 current_archive->root_entry = root_entry;
461 *pparc = current_archive;
463 return result;
466 /* --------------------------------------------------------------------------------------------- */
468 * Main loop for reading an archive.
469 * Return 0 on success, -1 on error.
472 static int
473 extfs_read_archive (int fstype, const char *name, struct archive **pparc)
475 FILE *extfsd;
476 const extfs_plugin_info_t *info;
477 char *buffer;
478 struct archive *current_archive;
479 char *current_file_name, *current_link_name;
481 info = &g_array_index (extfs_plugins, extfs_plugin_info_t, fstype);
483 extfsd = extfs_open_archive (fstype, name, &current_archive);
485 if (extfsd == NULL)
487 message (D_ERROR, MSG_ERROR, _("Cannot open %s archive\n%s"), info->prefix, name);
488 return -1;
491 buffer = g_malloc (BUF_4K);
492 while (fgets (buffer, BUF_4K, extfsd) != NULL)
494 struct stat hstat;
496 current_link_name = NULL;
497 if (vfs_parse_ls_lga (buffer, &hstat, &current_file_name, &current_link_name))
499 struct entry *entry, *pent;
500 struct inode *inode;
501 char *p, *q, *cfn = current_file_name;
503 if (*cfn != '\0')
505 if (*cfn == PATH_SEP)
506 cfn++;
507 p = strchr (cfn, '\0');
508 if (p != cfn && *(p - 1) == PATH_SEP)
509 *(p - 1) = '\0';
510 p = strrchr (cfn, PATH_SEP);
511 if (p == NULL)
513 p = cfn;
514 q = strchr (cfn, '\0');
516 else
518 *(p++) = '\0';
519 q = cfn;
521 if (S_ISDIR (hstat.st_mode) && (strcmp (p, ".") == 0 || strcmp (p, "..") == 0))
522 goto read_extfs_continue;
523 pent = extfs_find_entry (current_archive->root_entry, q, TRUE, FALSE);
524 if (pent == NULL)
526 /* FIXME: Should clean everything one day */
527 g_free (buffer);
528 pclose (extfsd);
529 close_error_pipe (D_ERROR, _("Inconsistent extfs archive"));
530 return -1;
532 entry = g_new (struct entry, 1);
533 entry->name = g_strdup (p);
534 entry->next_in_dir = NULL;
535 entry->dir = pent;
536 if (pent->inode->last_in_subdir)
538 pent->inode->last_in_subdir->next_in_dir = entry;
539 pent->inode->last_in_subdir = entry;
541 if (!S_ISLNK (hstat.st_mode) && (current_link_name != NULL))
543 pent = extfs_find_entry (current_archive->root_entry,
544 current_link_name, FALSE, FALSE);
545 if (pent == NULL)
547 /* FIXME: Should clean everything one day */
548 g_free (buffer);
549 pclose (extfsd);
550 close_error_pipe (D_ERROR, _("Inconsistent extfs archive"));
551 return -1;
554 entry->inode = pent->inode;
555 pent->inode->nlink++;
557 else
559 inode = g_new (struct inode, 1);
560 entry->inode = inode;
561 inode->local_filename = NULL;
562 inode->inode = (current_archive->inode_counter)++;
563 inode->nlink = 1;
564 inode->dev = current_archive->rdev;
565 inode->archive = current_archive;
566 inode->mode = hstat.st_mode;
567 #ifdef HAVE_STRUCT_STAT_ST_RDEV
568 inode->rdev = hstat.st_rdev;
569 #else
570 inode->rdev = 0;
571 #endif
572 inode->uid = hstat.st_uid;
573 inode->gid = hstat.st_gid;
574 inode->size = hstat.st_size;
575 inode->mtime = hstat.st_mtime;
576 inode->atime = hstat.st_atime;
577 inode->ctime = hstat.st_ctime;
578 inode->first_in_subdir = NULL;
579 inode->last_in_subdir = NULL;
580 if (current_link_name != NULL && S_ISLNK (hstat.st_mode))
582 inode->linkname = current_link_name;
583 current_link_name = NULL;
585 else
587 if (S_ISLNK (hstat.st_mode))
588 inode->mode &= ~S_IFLNK; /* You *DON'T* want to do this always */
589 inode->linkname = NULL;
591 if (S_ISDIR (hstat.st_mode))
592 extfs_make_dots (entry);
595 read_extfs_continue:
596 g_free (current_file_name);
597 g_free (current_link_name);
600 g_free (buffer);
602 /* Check if extfs 'list' returned 0 */
603 if (pclose (extfsd) != 0)
605 extfs_free (current_archive);
606 close_error_pipe (D_ERROR, _("Inconsistent extfs archive"));
607 return -1;
610 close_error_pipe (D_ERROR, NULL);
611 *pparc = current_archive;
612 return 0;
615 /* --------------------------------------------------------------------------------------------- */
617 static int
618 extfs_which (struct vfs_class *me, const char *path)
620 size_t path_len;
621 size_t i;
623 (void) me;
625 path_len = strlen (path);
627 for (i = 0; i < extfs_plugins->len; i++)
629 extfs_plugin_info_t *info;
631 info = &g_array_index (extfs_plugins, extfs_plugin_info_t, i);
633 if ((strncmp (path, info->prefix, path_len) == 0)
634 && ((info->prefix[path_len] == '\0') || (info->prefix[path_len] == '+')))
635 return i;
637 return -1;
640 /* --------------------------------------------------------------------------------------------- */
642 * Dissect the path and create corresponding superblock. Note that inname
643 * can be changed and the result may point inside the original string.
646 static char *
647 extfs_get_path_mangle (struct vfs_class *me, char *inname, struct archive **archive,
648 gboolean do_not_open)
650 char *local, *op;
651 const char *archive_name;
652 int result = -1;
653 struct archive *parc;
654 int fstype;
656 archive_name = inname;
657 vfs_split (inname, &local, &op);
659 fstype = extfs_which (me, op);
661 if (fstype == -1)
662 return NULL;
663 if (local == NULL)
664 local = inname + strlen (inname);
667 * All filesystems should have some local archive, at least
668 * it can be PATH_SEP ('/').
670 for (parc = first_archive; parc != NULL; parc = parc->next)
671 if (parc->name != NULL)
673 if (strcmp (parc->name, archive_name) == 0)
675 vfs_stamp (&vfs_extfs_ops, (vfsid) parc);
676 goto return_success;
680 result = do_not_open ? -1 : extfs_read_archive (fstype, archive_name, &parc);
681 if (result == -1)
682 ERRNOR (EIO, NULL);
684 return_success:
685 *archive = parc;
686 return local;
689 /* --------------------------------------------------------------------------------------------- */
691 * Dissect the path and create corresponding superblock.
692 * The result should be freed.
695 static char *
696 extfs_get_path (struct vfs_class *me, const char *inname,
697 struct archive **archive, gboolean do_not_open)
699 char *buf, *res, *res2;
701 buf = g_strdup (inname);
702 res = extfs_get_path_mangle (me, buf, archive, do_not_open);
703 res2 = g_strdup (res);
704 g_free (buf);
705 return res2;
708 /* --------------------------------------------------------------------------------------------- */
709 /* Return allocated path (without leading slash) inside the archive */
711 static char *
712 extfs_get_path_from_entry (struct entry *entry)
714 GString *localpath;
716 localpath = g_string_new ("");
718 while (entry->dir != NULL)
720 g_string_prepend (localpath, entry->name);
721 if (entry->dir->dir != NULL)
722 g_string_prepend_c (localpath, PATH_SEP);
723 entry = entry->dir;
726 return g_string_free (localpath, FALSE);
729 /* --------------------------------------------------------------------------------------------- */
731 static struct entry *
732 extfs_resolve_symlinks_int (struct entry *entry, GSList * list)
734 struct entry *pent = NULL;
736 if (!S_ISLNK (entry->inode->mode))
737 return entry;
739 if (g_slist_find (list, entry) != NULL)
741 /* Here we protect us against symlink looping */
742 errloop = TRUE;
744 else
746 GSList *looping;
748 looping = g_slist_prepend (list, entry);
749 pent = extfs_find_entry_int (entry->dir, entry->inode->linkname, looping, FALSE, FALSE);
750 looping = g_slist_delete_link (looping, looping);
752 if (pent == NULL)
753 my_errno = ENOENT;
756 return pent;
759 /* --------------------------------------------------------------------------------------------- */
761 static struct entry *
762 extfs_resolve_symlinks (struct entry *entry)
764 struct entry *res;
766 errloop = FALSE;
767 notadir = FALSE;
768 res = extfs_resolve_symlinks_int (entry, NULL);
769 if (res == NULL)
771 if (errloop)
772 my_errno = ELOOP;
773 else if (notadir)
774 my_errno = ENOTDIR;
776 return res;
779 /* --------------------------------------------------------------------------------------------- */
781 static const char *
782 extfs_get_archive_name (struct archive *archive)
784 const char *archive_name;
786 if (archive->local_name)
787 archive_name = archive->local_name;
788 else
789 archive_name = archive->name;
791 if (!archive_name || !*archive_name)
792 return "no_archive_name";
793 else
794 return archive_name;
797 /* --------------------------------------------------------------------------------------------- */
798 /** Don't pass localname as NULL */
800 static int
801 extfs_cmd (const char *str_extfs_cmd, struct archive *archive,
802 struct entry *entry, const char *localname)
804 char *file;
805 char *quoted_file;
806 char *quoted_localname;
807 char *archive_name;
808 const extfs_plugin_info_t *info;
809 char *cmd;
810 int retval;
812 file = extfs_get_path_from_entry (entry);
813 quoted_file = name_quote (file, 0);
814 g_free (file);
816 archive_name = name_quote (extfs_get_archive_name (archive), 0);
817 quoted_localname = name_quote (localname, 0);
818 info = &g_array_index (extfs_plugins, extfs_plugin_info_t, archive->fstype);
819 cmd = g_strconcat (info->path, info->prefix, str_extfs_cmd,
820 archive_name, " ", quoted_file, " ", quoted_localname, (char *) NULL);
821 g_free (quoted_file);
822 g_free (quoted_localname);
823 g_free (archive_name);
825 open_error_pipe ();
826 retval = my_system (EXECUTE_AS_SHELL, shell, cmd);
827 g_free (cmd);
828 close_error_pipe (D_ERROR, NULL);
829 return retval;
832 /* --------------------------------------------------------------------------------------------- */
834 static void
835 extfs_run (struct vfs_class *me, const char *file)
837 struct archive *archive = NULL;
838 char *p, *q, *archive_name;
839 char *cmd;
840 const extfs_plugin_info_t *info;
842 p = extfs_get_path (me, file, &archive, FALSE);
843 if (p == NULL)
844 return;
845 q = name_quote (p, 0);
846 g_free (p);
848 archive_name = name_quote (extfs_get_archive_name (archive), 0);
849 info = &g_array_index (extfs_plugins, extfs_plugin_info_t, archive->fstype);
850 cmd = g_strconcat (info->path, info->prefix, " run ", archive_name, " ", q, (char *) NULL);
851 g_free (archive_name);
852 g_free (q);
853 shell_execute (cmd, 0);
854 g_free (cmd);
857 /* --------------------------------------------------------------------------------------------- */
859 static void *
860 extfs_open (const vfs_path_t * vpath, int flags, mode_t mode)
862 struct pseudofile *extfs_info;
863 struct archive *archive = NULL;
864 char *q;
865 struct entry *entry;
866 int local_handle;
867 gboolean created = FALSE;
868 vfs_path_element_t *path_element = vfs_path_get_by_index (vpath, vfs_path_length (vpath) - 1);
870 q = extfs_get_path (path_element->class, vpath->unparsed, &archive, FALSE);
871 if (q == NULL)
872 return NULL;
873 entry = extfs_find_entry (archive->root_entry, q, FALSE, FALSE);
874 if ((entry == NULL) && ((flags & O_CREAT) != 0))
876 /* Create new entry */
877 entry = extfs_find_entry (archive->root_entry, q, FALSE, TRUE);
878 created = (entry != NULL);
881 g_free (q);
882 if (entry == NULL)
883 return NULL;
884 entry = extfs_resolve_symlinks (entry);
885 if (entry == NULL)
886 return NULL;
888 if (S_ISDIR (entry->inode->mode))
889 ERRNOR (EISDIR, NULL);
891 if (entry->inode->local_filename == NULL)
893 char *local_filename;
895 local_handle = vfs_mkstemps (&local_filename, "extfs", entry->name);
897 if (local_handle == -1)
898 return NULL;
899 close (local_handle);
901 if (!created && ((flags & O_TRUNC) == 0)
902 && extfs_cmd (" copyout ", archive, entry, local_filename))
904 unlink (local_filename);
905 g_free (local_filename);
906 my_errno = EIO;
907 return NULL;
909 entry->inode->local_filename = local_filename;
912 local_handle = open (entry->inode->local_filename, NO_LINEAR (flags), mode);
914 if (local_handle == -1)
916 /* file exists(may be). Need to drop O_CREAT flag and truncate file content */
917 flags = ~O_CREAT & (NO_LINEAR (flags) | O_TRUNC);
918 local_handle = open (entry->inode->local_filename, flags, mode);
921 if (local_handle == -1)
922 ERRNOR (EIO, NULL);
924 extfs_info = g_new (struct pseudofile, 1);
925 extfs_info->archive = archive;
926 extfs_info->entry = entry;
927 extfs_info->has_changed = created;
928 extfs_info->local_handle = local_handle;
930 /* i.e. we had no open files and now we have one */
931 vfs_rmstamp (&vfs_extfs_ops, (vfsid) archive);
932 archive->fd_usage++;
933 return extfs_info;
936 /* --------------------------------------------------------------------------------------------- */
938 static ssize_t
939 extfs_read (void *data, char *buffer, size_t count)
941 struct pseudofile *file = (struct pseudofile *) data;
943 return read (file->local_handle, buffer, count);
946 /* --------------------------------------------------------------------------------------------- */
948 static int
949 extfs_close (void *data)
951 struct pseudofile *file;
952 int errno_code = 0;
953 file = (struct pseudofile *) data;
955 close (file->local_handle);
957 /* Commit the file if it has changed */
958 if (file->has_changed)
960 struct stat file_status;
962 if (extfs_cmd (" copyin ", file->archive, file->entry, file->entry->inode->local_filename))
963 errno_code = EIO;
965 if (stat (file->entry->inode->local_filename, &file_status) != 0)
966 errno_code = EIO;
967 else
968 file->entry->inode->size = file_status.st_size;
970 file->entry->inode->mtime = time (NULL);
973 if (--file->archive->fd_usage == 0)
974 vfs_stamp_create (&vfs_extfs_ops, file->archive);
976 g_free (data);
977 if (errno_code != 0)
978 ERRNOR (EIO, -1);
979 return 0;
982 /* --------------------------------------------------------------------------------------------- */
984 static int
985 extfs_errno (struct vfs_class *me)
987 (void) me;
988 return my_errno;
991 /* --------------------------------------------------------------------------------------------- */
993 static void *
994 extfs_opendir (const vfs_path_t * vpath)
996 struct archive *archive = NULL;
997 char *q;
998 struct entry *entry;
999 struct entry **info;
1000 vfs_path_element_t *path_element = vfs_path_get_by_index (vpath, vfs_path_length (vpath) - 1);
1002 q = extfs_get_path (path_element->class, vpath->unparsed, &archive, FALSE);
1003 if (q == NULL)
1004 return NULL;
1005 entry = extfs_find_entry (archive->root_entry, q, FALSE, FALSE);
1006 g_free (q);
1007 if (entry == NULL)
1008 return NULL;
1009 entry = extfs_resolve_symlinks (entry);
1010 if (entry == NULL)
1011 return NULL;
1012 if (!S_ISDIR (entry->inode->mode))
1013 ERRNOR (ENOTDIR, NULL);
1015 info = g_new (struct entry *, 2);
1016 info[0] = entry->inode->first_in_subdir;
1017 info[1] = entry->inode->first_in_subdir;
1019 return info;
1022 /* --------------------------------------------------------------------------------------------- */
1024 static void *
1025 extfs_readdir (void *data)
1027 static union vfs_dirent dir;
1028 struct entry **info = (struct entry **) data;
1030 if (*info == NULL)
1031 return NULL;
1033 g_strlcpy (dir.dent.d_name, (*info)->name, MC_MAXPATHLEN);
1035 compute_namelen (&dir.dent);
1036 *info = (*info)->next_in_dir;
1038 return (void *) &dir;
1041 /* --------------------------------------------------------------------------------------------- */
1043 static int
1044 extfs_closedir (void *data)
1046 g_free (data);
1047 return 0;
1050 /* --------------------------------------------------------------------------------------------- */
1052 static void
1053 extfs_stat_move (struct stat *buf, const struct inode *inode)
1055 buf->st_dev = inode->dev;
1056 buf->st_ino = inode->inode;
1057 buf->st_mode = inode->mode;
1058 buf->st_nlink = inode->nlink;
1059 buf->st_uid = inode->uid;
1060 buf->st_gid = inode->gid;
1061 #ifdef HAVE_STRUCT_STAT_ST_RDEV
1062 buf->st_rdev = inode->rdev;
1063 #endif
1064 buf->st_size = inode->size;
1065 #ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
1066 buf->st_blksize = RECORDSIZE;
1067 #endif
1068 #ifdef HAVE_STRUCT_STAT_ST_BLOCKS
1069 buf->st_blocks = (inode->size + RECORDSIZE - 1) / RECORDSIZE;
1070 #endif
1071 buf->st_atime = inode->atime;
1072 buf->st_mtime = inode->mtime;
1073 buf->st_ctime = inode->ctime;
1076 /* --------------------------------------------------------------------------------------------- */
1078 static int
1079 extfs_internal_stat (const vfs_path_t * vpath, struct stat *buf, gboolean resolve)
1081 struct archive *archive;
1082 char *q, *mpath;
1083 struct entry *entry;
1084 int result = -1;
1085 vfs_path_element_t *path_element;
1087 mpath = g_strdup (vpath->unparsed);
1089 path_element = vfs_path_get_by_index (vpath, vfs_path_length (vpath) - 1);
1090 q = extfs_get_path_mangle (path_element->class, mpath, &archive, FALSE);
1091 if (q == NULL)
1092 goto cleanup;
1093 entry = extfs_find_entry (archive->root_entry, q, FALSE, FALSE);
1094 if (entry == NULL)
1095 goto cleanup;
1096 if (resolve)
1098 entry = extfs_resolve_symlinks (entry);
1099 if (entry == NULL)
1100 goto cleanup;
1102 extfs_stat_move (buf, entry->inode);
1103 result = 0;
1104 cleanup:
1105 g_free (mpath);
1106 return result;
1109 /* --------------------------------------------------------------------------------------------- */
1111 static int
1112 extfs_stat (const vfs_path_t * vpath, struct stat *buf)
1114 return extfs_internal_stat (vpath, buf, TRUE);
1117 /* --------------------------------------------------------------------------------------------- */
1119 static int
1120 extfs_lstat (const vfs_path_t * vpath, struct stat *buf)
1122 return extfs_internal_stat (vpath, buf, FALSE);
1125 /* --------------------------------------------------------------------------------------------- */
1127 static int
1128 extfs_fstat (void *data, struct stat *buf)
1130 struct pseudofile *file = (struct pseudofile *) data;
1132 extfs_stat_move (buf, file->entry->inode);
1133 return 0;
1136 /* --------------------------------------------------------------------------------------------- */
1138 static int
1139 extfs_readlink (const vfs_path_t * vpath, char *buf, size_t size)
1141 struct archive *archive;
1142 char *q, *mpath;
1143 size_t len;
1144 struct entry *entry;
1145 int result = -1;
1146 vfs_path_element_t *path_element;
1148 path_element = vfs_path_get_by_index (vpath, vfs_path_length (vpath) - 1);
1150 mpath = g_strdup (vpath->unparsed);
1152 q = extfs_get_path_mangle (path_element->class, mpath, &archive, FALSE);
1153 if (q == NULL)
1154 goto cleanup;
1155 entry = extfs_find_entry (archive->root_entry, q, FALSE, FALSE);
1156 if (entry == NULL)
1157 goto cleanup;
1158 if (!S_ISLNK (entry->inode->mode))
1160 path_element->class->verrno = EINVAL;
1161 goto cleanup;
1163 len = strlen (entry->inode->linkname);
1164 if (size < len)
1165 len = size;
1166 /* readlink() does not append a NUL character to buf */
1167 result = len;
1168 memcpy (buf, entry->inode->linkname, result);
1169 cleanup:
1170 g_free (mpath);
1171 return result;
1174 /* --------------------------------------------------------------------------------------------- */
1176 static int
1177 extfs_chown (const vfs_path_t * vpath, uid_t owner, gid_t group)
1179 (void) vpath;
1180 (void) owner;
1181 (void) group;
1182 return 0;
1185 /* --------------------------------------------------------------------------------------------- */
1187 static int
1188 extfs_chmod (const vfs_path_t * vpath, int mode)
1190 (void) vpath;
1191 (void) mode;
1192 return 0;
1195 /* --------------------------------------------------------------------------------------------- */
1197 static ssize_t
1198 extfs_write (void *data, const char *buf, size_t nbyte)
1200 struct pseudofile *file = (struct pseudofile *) data;
1202 file->has_changed = TRUE;
1203 return write (file->local_handle, buf, nbyte);
1206 /* --------------------------------------------------------------------------------------------- */
1208 static int
1209 extfs_unlink (const vfs_path_t * vpath)
1211 struct archive *archive;
1212 char *q, *mpath;
1213 struct entry *entry;
1214 int result = -1;
1215 vfs_path_element_t *path_element = vfs_path_get_by_index (vpath, vfs_path_length (vpath) - 1);
1217 mpath = g_strdup (vpath->unparsed);
1219 q = extfs_get_path_mangle (path_element->class, mpath, &archive, FALSE);
1220 if (q == NULL)
1221 goto cleanup;
1222 entry = extfs_find_entry (archive->root_entry, q, FALSE, FALSE);
1223 if (entry == NULL)
1224 goto cleanup;
1225 entry = extfs_resolve_symlinks (entry);
1226 if (entry == NULL)
1227 goto cleanup;
1228 if (S_ISDIR (entry->inode->mode))
1230 path_element->class->verrno = EISDIR;
1231 goto cleanup;
1233 if (extfs_cmd (" rm ", archive, entry, ""))
1235 my_errno = EIO;
1236 goto cleanup;
1238 extfs_remove_entry (entry);
1239 result = 0;
1240 cleanup:
1241 g_free (mpath);
1242 return result;
1245 /* --------------------------------------------------------------------------------------------- */
1247 static int
1248 extfs_mkdir (const vfs_path_t * vpath, mode_t mode)
1250 struct archive *archive;
1251 char *q, *mpath;
1252 struct entry *entry;
1253 int result = -1;
1254 vfs_path_element_t *path_element = vfs_path_get_by_index (vpath, vfs_path_length (vpath) - 1);
1256 (void) mode;
1258 mpath = g_strdup (vpath->unparsed);
1260 q = extfs_get_path_mangle (path_element->class, mpath, &archive, FALSE);
1261 if (q == NULL)
1262 goto cleanup;
1263 entry = extfs_find_entry (archive->root_entry, q, FALSE, FALSE);
1264 if (entry != NULL)
1266 path_element->class->verrno = EEXIST;
1267 goto cleanup;
1269 entry = extfs_find_entry (archive->root_entry, q, TRUE, FALSE);
1270 if (entry == NULL)
1271 goto cleanup;
1272 entry = extfs_resolve_symlinks (entry);
1273 if (entry == NULL)
1274 goto cleanup;
1275 if (!S_ISDIR (entry->inode->mode))
1277 path_element->class->verrno = ENOTDIR;
1278 goto cleanup;
1281 if (extfs_cmd (" mkdir ", archive, entry, ""))
1283 my_errno = EIO;
1284 extfs_remove_entry (entry);
1285 goto cleanup;
1287 result = 0;
1288 cleanup:
1289 g_free (mpath);
1290 return result;
1293 /* --------------------------------------------------------------------------------------------- */
1295 static int
1296 extfs_rmdir (const vfs_path_t * vpath)
1298 struct archive *archive;
1299 char *q, *mpath;
1300 struct entry *entry;
1301 int result = -1;
1302 vfs_path_element_t *path_element = vfs_path_get_by_index (vpath, vfs_path_length (vpath) - 1);
1304 mpath = g_strdup (vpath->unparsed);
1306 q = extfs_get_path_mangle (path_element->class, mpath, &archive, FALSE);
1307 if (q == NULL)
1308 goto cleanup;
1309 entry = extfs_find_entry (archive->root_entry, q, FALSE, FALSE);
1310 if (entry == NULL)
1311 goto cleanup;
1312 entry = extfs_resolve_symlinks (entry);
1313 if (entry == NULL)
1314 goto cleanup;
1315 if (!S_ISDIR (entry->inode->mode))
1317 path_element->class->verrno = ENOTDIR;
1318 goto cleanup;
1321 if (extfs_cmd (" rmdir ", archive, entry, ""))
1323 my_errno = EIO;
1324 goto cleanup;
1326 extfs_remove_entry (entry);
1327 result = 0;
1328 cleanup:
1329 g_free (mpath);
1330 return result;
1333 /* --------------------------------------------------------------------------------------------- */
1335 static int
1336 extfs_chdir (const vfs_path_t * vpath)
1338 struct archive *archive = NULL;
1339 char *q;
1340 struct entry *entry;
1341 vfs_path_element_t *path_element = vfs_path_get_by_index (vpath, vfs_path_length (vpath) - 1);
1343 my_errno = ENOTDIR;
1344 q = extfs_get_path (path_element->class, vpath->unparsed, &archive, FALSE);
1345 if (q == NULL)
1346 return -1;
1347 entry = extfs_find_entry (archive->root_entry, q, FALSE, FALSE);
1348 g_free (q);
1349 if (entry == NULL)
1350 return -1;
1351 entry = extfs_resolve_symlinks (entry);
1352 if ((entry == NULL) || (!S_ISDIR (entry->inode->mode)))
1353 return -1;
1354 my_errno = 0;
1355 return 0;
1358 /* --------------------------------------------------------------------------------------------- */
1360 static off_t
1361 extfs_lseek (void *data, off_t offset, int whence)
1363 struct pseudofile *file = (struct pseudofile *) data;
1365 return lseek (file->local_handle, offset, whence);
1368 /* --------------------------------------------------------------------------------------------- */
1370 static vfsid
1371 extfs_getid (const vfs_path_t * vpath)
1373 struct archive *archive = NULL;
1374 char *p;
1375 vfs_path_element_t *path_element = vfs_path_get_by_index (vpath, vfs_path_length (vpath) - 1);
1377 p = extfs_get_path (path_element->class, vpath->unparsed, &archive, TRUE);
1378 if (p == NULL)
1379 return NULL;
1380 g_free (p);
1381 return (vfsid) archive;
1384 /* --------------------------------------------------------------------------------------------- */
1386 static int
1387 extfs_nothingisopen (vfsid id)
1389 return (((struct archive *) id)->fd_usage <= 0);
1392 /* --------------------------------------------------------------------------------------------- */
1394 static void
1395 extfs_remove_entry (struct entry *e)
1397 int i = --e->inode->nlink;
1398 struct entry *pe, *ent, *prev;
1400 if (S_ISDIR (e->inode->mode) && e->inode->first_in_subdir != NULL)
1402 struct entry *f = e->inode->first_in_subdir;
1403 e->inode->first_in_subdir = NULL;
1404 extfs_remove_entry (f);
1406 pe = e->dir;
1407 if (e == pe->inode->first_in_subdir)
1408 pe->inode->first_in_subdir = e->next_in_dir;
1410 prev = NULL;
1411 for (ent = pe->inode->first_in_subdir; ent && ent->next_in_dir; ent = ent->next_in_dir)
1412 if (e == ent->next_in_dir)
1414 prev = ent;
1415 break;
1417 if (prev)
1418 prev->next_in_dir = e->next_in_dir;
1419 if (e == pe->inode->last_in_subdir)
1420 pe->inode->last_in_subdir = prev;
1422 if (i <= 0)
1424 if (e->inode->local_filename != NULL)
1426 unlink (e->inode->local_filename);
1427 g_free (e->inode->local_filename);
1429 g_free (e->inode->linkname);
1430 g_free (e->inode);
1433 g_free (e->name);
1434 g_free (e);
1437 /* --------------------------------------------------------------------------------------------- */
1439 static void
1440 extfs_free_entry (struct entry *e)
1442 int i = --e->inode->nlink;
1444 if (S_ISDIR (e->inode->mode) && e->inode->first_in_subdir != NULL)
1446 struct entry *f = e->inode->first_in_subdir;
1448 e->inode->first_in_subdir = NULL;
1449 extfs_free_entry (f);
1451 if (i <= 0)
1453 if (e->inode->local_filename != NULL)
1455 unlink (e->inode->local_filename);
1456 g_free (e->inode->local_filename);
1458 g_free (e->inode->linkname);
1459 g_free (e->inode);
1461 if (e->next_in_dir != NULL)
1462 extfs_free_entry (e->next_in_dir);
1463 g_free (e->name);
1464 g_free (e);
1467 /* --------------------------------------------------------------------------------------------- */
1469 static void
1470 extfs_free (vfsid id)
1472 struct archive *archive = (struct archive *) id;
1474 if (archive == first_archive)
1476 first_archive = archive->next;
1478 else
1480 struct archive *parc;
1481 for (parc = first_archive; parc != NULL; parc = parc->next)
1482 if (parc->next == archive)
1484 parc->next = archive->next;
1485 break;
1488 extfs_free_archive (archive);
1491 /* --------------------------------------------------------------------------------------------- */
1493 static char *
1494 extfs_getlocalcopy (const vfs_path_t * vpath)
1496 struct pseudofile *fp;
1497 char *p;
1499 fp = (struct pseudofile *) extfs_open (vpath, O_RDONLY, 0);
1500 if (fp == NULL)
1501 return NULL;
1502 if (fp->entry->inode->local_filename == NULL)
1504 extfs_close ((void *) fp);
1505 return NULL;
1507 p = g_strdup (fp->entry->inode->local_filename);
1508 fp->archive->fd_usage++;
1509 extfs_close ((void *) fp);
1510 return p;
1513 /* --------------------------------------------------------------------------------------------- */
1515 static int
1516 extfs_ungetlocalcopy (const vfs_path_t * vpath, const char *local, int has_changed)
1518 struct pseudofile *fp;
1520 fp = (struct pseudofile *) extfs_open (vpath, O_RDONLY, 0);
1521 if (fp == NULL)
1522 return 0;
1524 if (strcmp (fp->entry->inode->local_filename, local) == 0)
1526 fp->archive->fd_usage--;
1527 if (has_changed != 0)
1528 fp->has_changed = TRUE;
1529 extfs_close ((void *) fp);
1530 return 0;
1532 else
1534 /* Should not happen */
1535 extfs_close ((void *) fp);
1536 return 0;
1540 /* --------------------------------------------------------------------------------------------- */
1542 static gboolean
1543 extfs_get_plugins (const char *where, gboolean silent)
1545 char *dirname;
1546 GDir *dir;
1547 const char *filename;
1549 dirname = g_build_path (PATH_SEP_STR, where, MC_EXTFS_DIR, (char *) NULL);
1550 dir = g_dir_open (dirname, 0, NULL);
1552 /* We may not use vfs_die() message or message or similar,
1553 * UI is not initialized at this time and message would not
1554 * appear on screen. */
1555 if (dir == NULL)
1557 if (!silent)
1558 fprintf (stderr, _("Warning: cannot open %s directory\n"), dirname);
1559 g_free (dirname);
1560 return FALSE;
1563 if (extfs_plugins == NULL)
1564 extfs_plugins = g_array_sized_new (FALSE, TRUE, sizeof (extfs_plugin_info_t), 32);
1566 while ((filename = g_dir_read_name (dir)) != NULL)
1568 char fullname[MC_MAXPATHLEN];
1569 struct stat s;
1571 g_snprintf (fullname, sizeof (fullname), "%s" PATH_SEP_STR "%s", dirname, filename);
1573 if ((stat (fullname, &s) == 0)
1574 && S_ISREG (s.st_mode) && !S_ISDIR (s.st_mode)
1575 && (((s.st_mode & S_IXOTH) != 0) ||
1576 ((s.st_mode & S_IXUSR) != 0) || ((s.st_mode & S_IXGRP) != 0)))
1578 int f;
1580 f = open (fullname, O_RDONLY);
1582 if (f > 0)
1584 size_t len, i;
1585 extfs_plugin_info_t info;
1586 gboolean found = FALSE;
1588 close (f);
1590 /* Handle those with a trailing '+', those flag that the
1591 * file system does not require an archive to work
1593 len = strlen (filename);
1594 info.need_archive = (filename[len - 1] != '+');
1595 info.path = g_strconcat (dirname, PATH_SEP_STR, (char *) NULL);
1596 info.prefix = g_strdup (filename);
1598 /* prepare to compare file names without trailing '+' */
1599 if (!info.need_archive)
1600 info.prefix[len - 1] = '\0';
1602 /* don't overload already found plugin */
1603 for (i = 0; i < extfs_plugins->len; i++)
1605 extfs_plugin_info_t *p;
1607 p = &g_array_index (extfs_plugins, extfs_plugin_info_t, i);
1609 /* 2 files with same names cannot be in a directory */
1610 if ((strcmp (info.path, p->path) != 0)
1611 && (strcmp (info.prefix, p->prefix) == 0))
1613 found = TRUE;
1614 break;
1618 if (found)
1620 g_free (info.path);
1621 g_free (info.prefix);
1623 else
1625 /* restore file name */
1626 if (!info.need_archive)
1627 info.prefix[len - 1] = '+';
1628 g_array_append_val (extfs_plugins, info);
1634 g_dir_close (dir);
1635 g_free (dirname);
1637 return TRUE;
1640 /* --------------------------------------------------------------------------------------------- */
1642 static int
1643 extfs_init (struct vfs_class *me)
1645 gboolean d1, d2;
1647 (void) me;
1649 /* 1st: scan user directory */
1650 d1 = extfs_get_plugins (mc_config_get_data_path (), TRUE); /* silent about user dir */
1651 /* 2nd: scan system dir */
1652 d2 = extfs_get_plugins (LIBEXECDIR, d1);
1654 return (d1 || d2 ? 1 : 0);
1657 /* --------------------------------------------------------------------------------------------- */
1659 static void
1660 extfs_done (struct vfs_class *me)
1662 size_t i;
1663 struct archive *ar;
1665 (void) me;
1667 for (ar = first_archive; ar != NULL;)
1669 extfs_free ((vfsid) ar);
1670 ar = first_archive;
1673 for (i = 0; i < extfs_plugins->len; i++)
1675 extfs_plugin_info_t *info;
1677 info = &g_array_index (extfs_plugins, extfs_plugin_info_t, i);
1678 g_free (info->path);
1679 g_free (info->prefix);
1682 if (extfs_plugins != NULL)
1683 g_array_free (extfs_plugins, TRUE);
1686 /* --------------------------------------------------------------------------------------------- */
1688 static int
1689 extfs_setctl (const vfs_path_t * vpath, int ctlop, void *arg)
1691 vfs_path_element_t *path_element = vfs_path_get_by_index (vpath, vfs_path_length (vpath) - 1);
1693 (void) arg;
1695 if (ctlop == VFS_SETCTL_RUN)
1697 extfs_run (path_element->class, vpath->unparsed);
1698 return 1;
1700 return 0;
1703 /* --------------------------------------------------------------------------------------------- */
1704 /*** public functions ****************************************************************************/
1705 /* --------------------------------------------------------------------------------------------- */
1707 void
1708 init_extfs (void)
1710 vfs_extfs_ops.name = "extfs";
1711 vfs_extfs_ops.init = extfs_init;
1712 vfs_extfs_ops.done = extfs_done;
1713 vfs_extfs_ops.fill_names = extfs_fill_names;
1714 vfs_extfs_ops.which = extfs_which;
1715 vfs_extfs_ops.open = extfs_open;
1716 vfs_extfs_ops.close = extfs_close;
1717 vfs_extfs_ops.read = extfs_read;
1718 vfs_extfs_ops.write = extfs_write;
1719 vfs_extfs_ops.opendir = extfs_opendir;
1720 vfs_extfs_ops.readdir = extfs_readdir;
1721 vfs_extfs_ops.closedir = extfs_closedir;
1722 vfs_extfs_ops.stat = extfs_stat;
1723 vfs_extfs_ops.lstat = extfs_lstat;
1724 vfs_extfs_ops.fstat = extfs_fstat;
1725 vfs_extfs_ops.chmod = extfs_chmod;
1726 vfs_extfs_ops.chown = extfs_chown;
1727 vfs_extfs_ops.readlink = extfs_readlink;
1728 vfs_extfs_ops.unlink = extfs_unlink;
1729 vfs_extfs_ops.chdir = extfs_chdir;
1730 vfs_extfs_ops.ferrno = extfs_errno;
1731 vfs_extfs_ops.lseek = extfs_lseek;
1732 vfs_extfs_ops.getid = extfs_getid;
1733 vfs_extfs_ops.nothingisopen = extfs_nothingisopen;
1734 vfs_extfs_ops.free = extfs_free;
1735 vfs_extfs_ops.getlocalcopy = extfs_getlocalcopy;
1736 vfs_extfs_ops.ungetlocalcopy = extfs_ungetlocalcopy;
1737 vfs_extfs_ops.mkdir = extfs_mkdir;
1738 vfs_extfs_ops.rmdir = extfs_rmdir;
1739 vfs_extfs_ops.setctl = extfs_setctl;
1740 vfs_register_class (&vfs_extfs_ops);
1743 /* --------------------------------------------------------------------------------------------- */