Ticket 1551: Update GPL version from 2 to 3
[midnight-commander.git] / src / vfs / extfs / extfs.c
blob852233f522e31893f84a4ebc1d285ed8f5e5f762
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/main.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, char *name, GSList * list,
242 gboolean make_dirs, gboolean make_file)
244 struct entry *pent, *pdir;
245 char *p, *q, *name_end;
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, 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;
378 mc_stat (archive->local_name, &my);
379 mc_ungetlocalcopy (archive->name, archive->local_name,
380 archive->local_stat.st_mtime != my.st_mtime);
381 g_free (archive->local_name);
383 g_free (archive->name);
384 g_free (archive);
387 /* --------------------------------------------------------------------------------------------- */
389 static FILE *
390 extfs_open_archive (int fstype, const char *name, struct archive **pparc)
392 const extfs_plugin_info_t *info;
393 static dev_t archive_counter = 0;
394 FILE *result;
395 mode_t mode;
396 char *cmd;
397 struct stat mystat;
398 struct archive *current_archive;
399 struct entry *root_entry;
400 char *local_name = NULL, *tmp = NULL;
401 vfs_path_t *vpath;
402 vfs_path_element_t *path_element = NULL;
404 vpath = vfs_path_from_str (name);
405 if (vpath != NULL)
406 path_element = vfs_path_get_by_index (vpath, -1);
408 info = &g_array_index (extfs_plugins, extfs_plugin_info_t, fstype);
410 if (info->need_archive)
412 if (mc_stat (name, &mystat) == -1)
413 return NULL;
415 if (!vfs_file_is_local (vpath))
417 local_name = mc_getlocalcopy (name);
418 if (local_name == NULL)
419 return NULL;
421 tmp = name_quote ((vpath != NULL) ? path_element->path : name, 0);
424 cmd = g_strconcat (info->path, info->prefix, " list ",
425 local_name != NULL ? local_name : tmp, (char *) NULL);
426 g_free (tmp);
428 open_error_pipe ();
429 result = popen (cmd, "r");
430 g_free (cmd);
431 if (result == NULL)
433 close_error_pipe (D_ERROR, NULL);
434 if (local_name != NULL)
436 mc_ungetlocalcopy (name, local_name, 0);
437 g_free (local_name);
439 vfs_path_free (vpath);
440 return NULL;
443 #ifdef ___QNXNTO__
444 setvbuf (result, NULL, _IONBF, 0);
445 #endif
447 current_archive = g_new (struct archive, 1);
448 current_archive->fstype = fstype;
449 current_archive->name = (name != NULL) ? g_strdup (name) : NULL;
450 current_archive->local_name = local_name;
452 if (local_name != NULL)
453 mc_stat (local_name, &current_archive->local_stat);
454 current_archive->inode_counter = 0;
455 current_archive->fd_usage = 0;
456 current_archive->rdev = archive_counter++;
457 current_archive->next = first_archive;
458 first_archive = current_archive;
459 mode = mystat.st_mode & 07777;
460 if (mode & 0400)
461 mode |= 0100;
462 if (mode & 0040)
463 mode |= 0010;
464 if (mode & 0004)
465 mode |= 0001;
466 mode |= S_IFDIR;
467 root_entry = extfs_generate_entry (current_archive, PATH_SEP_STR, NULL, mode);
468 root_entry->inode->uid = mystat.st_uid;
469 root_entry->inode->gid = mystat.st_gid;
470 root_entry->inode->atime = mystat.st_atime;
471 root_entry->inode->ctime = mystat.st_ctime;
472 root_entry->inode->mtime = mystat.st_mtime;
473 current_archive->root_entry = root_entry;
475 *pparc = current_archive;
477 vfs_path_free (vpath);
478 return result;
481 /* --------------------------------------------------------------------------------------------- */
483 * Main loop for reading an archive.
484 * Return 0 on success, -1 on error.
487 static int
488 extfs_read_archive (int fstype, const char *name, struct archive **pparc)
490 FILE *extfsd;
491 const extfs_plugin_info_t *info;
492 char *buffer;
493 struct archive *current_archive;
494 char *current_file_name, *current_link_name;
496 info = &g_array_index (extfs_plugins, extfs_plugin_info_t, fstype);
498 extfsd = extfs_open_archive (fstype, name, &current_archive);
500 if (extfsd == NULL)
502 message (D_ERROR, MSG_ERROR, _("Cannot open %s archive\n%s"), info->prefix, name);
503 return -1;
506 buffer = g_malloc (BUF_4K);
507 while (fgets (buffer, BUF_4K, extfsd) != NULL)
509 struct stat hstat;
511 current_link_name = NULL;
512 if (vfs_parse_ls_lga (buffer, &hstat, &current_file_name, &current_link_name, NULL))
514 struct entry *entry, *pent;
515 struct inode *inode;
516 char *p, *q, *cfn = current_file_name;
518 if (*cfn != '\0')
520 if (*cfn == PATH_SEP)
521 cfn++;
522 p = strchr (cfn, '\0');
523 if (p != cfn && *(p - 1) == PATH_SEP)
524 *(p - 1) = '\0';
525 p = strrchr (cfn, PATH_SEP);
526 if (p == NULL)
528 p = cfn;
529 q = strchr (cfn, '\0');
531 else
533 *(p++) = '\0';
534 q = cfn;
536 if (S_ISDIR (hstat.st_mode) && (strcmp (p, ".") == 0 || strcmp (p, "..") == 0))
537 goto read_extfs_continue;
538 pent = extfs_find_entry (current_archive->root_entry, q, TRUE, FALSE);
539 if (pent == NULL)
541 /* FIXME: Should clean everything one day */
542 g_free (buffer);
543 pclose (extfsd);
544 close_error_pipe (D_ERROR, _("Inconsistent extfs archive"));
545 return -1;
547 entry = g_new (struct entry, 1);
548 entry->name = g_strdup (p);
549 entry->next_in_dir = NULL;
550 entry->dir = pent;
551 if (pent->inode->last_in_subdir)
553 pent->inode->last_in_subdir->next_in_dir = entry;
554 pent->inode->last_in_subdir = entry;
556 if (!S_ISLNK (hstat.st_mode) && (current_link_name != NULL))
558 pent = extfs_find_entry (current_archive->root_entry,
559 current_link_name, FALSE, FALSE);
560 if (pent == NULL)
562 /* FIXME: Should clean everything one day */
563 g_free (buffer);
564 pclose (extfsd);
565 close_error_pipe (D_ERROR, _("Inconsistent extfs archive"));
566 return -1;
569 entry->inode = pent->inode;
570 pent->inode->nlink++;
572 else
574 inode = g_new (struct inode, 1);
575 entry->inode = inode;
576 inode->local_filename = NULL;
577 inode->inode = (current_archive->inode_counter)++;
578 inode->nlink = 1;
579 inode->dev = current_archive->rdev;
580 inode->archive = current_archive;
581 inode->mode = hstat.st_mode;
582 #ifdef HAVE_STRUCT_STAT_ST_RDEV
583 inode->rdev = hstat.st_rdev;
584 #else
585 inode->rdev = 0;
586 #endif
587 inode->uid = hstat.st_uid;
588 inode->gid = hstat.st_gid;
589 inode->size = hstat.st_size;
590 inode->mtime = hstat.st_mtime;
591 inode->atime = hstat.st_atime;
592 inode->ctime = hstat.st_ctime;
593 inode->first_in_subdir = NULL;
594 inode->last_in_subdir = NULL;
595 if (current_link_name != NULL && S_ISLNK (hstat.st_mode))
597 inode->linkname = current_link_name;
598 current_link_name = NULL;
600 else
602 if (S_ISLNK (hstat.st_mode))
603 inode->mode &= ~S_IFLNK; /* You *DON'T* want to do this always */
604 inode->linkname = NULL;
606 if (S_ISDIR (hstat.st_mode))
607 extfs_make_dots (entry);
610 read_extfs_continue:
611 g_free (current_file_name);
612 g_free (current_link_name);
615 g_free (buffer);
617 /* Check if extfs 'list' returned 0 */
618 if (pclose (extfsd) != 0)
620 extfs_free (current_archive);
621 close_error_pipe (D_ERROR, _("Inconsistent extfs archive"));
622 return -1;
625 close_error_pipe (D_ERROR, NULL);
626 *pparc = current_archive;
627 return 0;
630 /* --------------------------------------------------------------------------------------------- */
632 static int
633 extfs_which (struct vfs_class *me, const char *path)
635 size_t path_len;
636 size_t i;
638 (void) me;
640 path_len = strlen (path);
642 for (i = 0; i < extfs_plugins->len; i++)
644 extfs_plugin_info_t *info;
646 info = &g_array_index (extfs_plugins, extfs_plugin_info_t, i);
648 if ((strncmp (path, info->prefix, path_len) == 0)
649 && ((info->prefix[path_len] == '\0') || (info->prefix[path_len] == '+')))
650 return i;
652 return -1;
655 /* --------------------------------------------------------------------------------------------- */
657 * Dissect the path and create corresponding superblock. Note that inname
658 * can be changed and the result may point inside the original string.
661 static char *
662 extfs_get_path_mangle (const vfs_path_t * vpath, struct archive **archive, gboolean do_not_open)
664 char *archive_name;
665 int result = -1;
666 struct archive *parc;
667 int fstype;
668 vfs_path_element_t *path_element;
670 path_element = vfs_path_get_by_index (vpath, -1);
672 archive_name = vfs_path_to_str_elements_count (vpath, -1);
674 fstype = extfs_which (path_element->class, path_element->vfs_prefix);
676 if (fstype == -1)
678 g_free (archive_name);
679 return NULL;
683 * All filesystems should have some local archive, at least
684 * it can be PATH_SEP ('/').
686 for (parc = first_archive; parc != NULL; parc = parc->next)
687 if (parc->name != NULL)
689 if (strcmp (parc->name, archive_name) == 0)
691 vfs_stamp (&vfs_extfs_ops, (vfsid) parc);
692 goto return_success;
696 result = do_not_open ? -1 : extfs_read_archive (fstype, archive_name, &parc);
697 if (result == -1)
699 path_element->class->verrno = EIO;
700 g_free (archive_name);
701 return NULL;
704 return_success:
705 *archive = parc;
706 g_free (archive_name);
707 return path_element->path;
710 /* --------------------------------------------------------------------------------------------- */
712 * Dissect the path and create corresponding superblock.
713 * The result should be freed.
716 static char *
717 extfs_get_path (const vfs_path_t * vpath, struct archive **archive, gboolean do_not_open)
719 return g_strdup (extfs_get_path_mangle (vpath, archive, do_not_open));
722 /* --------------------------------------------------------------------------------------------- */
723 /* Return allocated path (without leading slash) inside the archive */
725 static char *
726 extfs_get_path_from_entry (struct entry *entry)
728 GString *localpath;
730 localpath = g_string_new ("");
732 while (entry->dir != NULL)
734 g_string_prepend (localpath, entry->name);
735 if (entry->dir->dir != NULL)
736 g_string_prepend_c (localpath, PATH_SEP);
737 entry = entry->dir;
740 return g_string_free (localpath, FALSE);
743 /* --------------------------------------------------------------------------------------------- */
745 static struct entry *
746 extfs_resolve_symlinks_int (struct entry *entry, GSList * list)
748 struct entry *pent = NULL;
750 if (!S_ISLNK (entry->inode->mode))
751 return entry;
753 if (g_slist_find (list, entry) != NULL)
755 /* Here we protect us against symlink looping */
756 errloop = TRUE;
758 else
760 GSList *looping;
762 looping = g_slist_prepend (list, entry);
763 pent = extfs_find_entry_int (entry->dir, entry->inode->linkname, looping, FALSE, FALSE);
764 looping = g_slist_delete_link (looping, looping);
766 if (pent == NULL)
767 my_errno = ENOENT;
770 return pent;
773 /* --------------------------------------------------------------------------------------------- */
775 static struct entry *
776 extfs_resolve_symlinks (struct entry *entry)
778 struct entry *res;
780 errloop = FALSE;
781 notadir = FALSE;
782 res = extfs_resolve_symlinks_int (entry, NULL);
783 if (res == NULL)
785 if (errloop)
786 my_errno = ELOOP;
787 else if (notadir)
788 my_errno = ENOTDIR;
790 return res;
793 /* --------------------------------------------------------------------------------------------- */
795 static char *
796 extfs_get_archive_name (struct archive *archive)
798 const char *archive_name;
800 if (archive->local_name)
801 archive_name = archive->local_name;
802 else
803 archive_name = archive->name;
805 if (!archive_name || !*archive_name)
806 return g_strdup ("no_archive_name");
807 else
809 char *ret_str;
810 vfs_path_t *vpath = vfs_path_from_str (archive_name);
811 vfs_path_element_t *path_element = vfs_path_get_by_index (vpath, -1);
812 ret_str = g_strdup (path_element->path);
813 vfs_path_free (vpath);
814 return ret_str;
818 /* --------------------------------------------------------------------------------------------- */
819 /** Don't pass localname as NULL */
821 static int
822 extfs_cmd (const char *str_extfs_cmd, struct archive *archive,
823 struct entry *entry, const char *localname)
825 char *file;
826 char *quoted_file;
827 char *quoted_localname;
828 char *archive_name, *quoted_archive_name;
829 const extfs_plugin_info_t *info;
830 char *cmd;
831 int retval;
833 file = extfs_get_path_from_entry (entry);
834 quoted_file = name_quote (file, 0);
835 g_free (file);
837 archive_name = extfs_get_archive_name (archive);
838 quoted_archive_name = name_quote (archive_name, 0);
839 g_free (archive_name);
840 quoted_localname = name_quote (localname, 0);
841 info = &g_array_index (extfs_plugins, extfs_plugin_info_t, archive->fstype);
842 cmd = g_strconcat (info->path, info->prefix, str_extfs_cmd,
843 quoted_archive_name, " ", quoted_file, " ", quoted_localname, (char *) NULL);
844 g_free (quoted_file);
845 g_free (quoted_localname);
846 g_free (quoted_archive_name);
848 open_error_pipe ();
849 retval = my_system (EXECUTE_AS_SHELL, shell, cmd);
850 g_free (cmd);
851 close_error_pipe (D_ERROR, NULL);
852 return retval;
855 /* --------------------------------------------------------------------------------------------- */
857 static void
858 extfs_run (const vfs_path_t * vpath)
860 struct archive *archive = NULL;
861 char *p, *q, *archive_name, *quoted_archive_name;
862 char *cmd;
863 const extfs_plugin_info_t *info;
865 p = extfs_get_path (vpath, &archive, FALSE);
866 if (p == NULL)
867 return;
868 q = name_quote (p, 0);
869 g_free (p);
871 archive_name = extfs_get_archive_name (archive);
872 quoted_archive_name = name_quote (archive_name, 0);
873 g_free (archive_name);
874 info = &g_array_index (extfs_plugins, extfs_plugin_info_t, archive->fstype);
875 cmd =
876 g_strconcat (info->path, info->prefix, " run ", quoted_archive_name, " ", q, (char *) NULL);
877 g_free (quoted_archive_name);
878 g_free (q);
879 shell_execute (cmd, 0);
880 g_free (cmd);
883 /* --------------------------------------------------------------------------------------------- */
885 static void *
886 extfs_open (const vfs_path_t * vpath, int flags, mode_t mode)
888 struct pseudofile *extfs_info;
889 struct archive *archive = NULL;
890 char *q;
891 struct entry *entry;
892 int local_handle;
893 gboolean created = FALSE;
895 q = extfs_get_path (vpath, &archive, FALSE);
896 if (q == NULL)
897 return NULL;
898 entry = extfs_find_entry (archive->root_entry, q, FALSE, FALSE);
899 if ((entry == NULL) && ((flags & O_CREAT) != 0))
901 /* Create new entry */
902 entry = extfs_find_entry (archive->root_entry, q, FALSE, TRUE);
903 created = (entry != NULL);
906 g_free (q);
907 if (entry == NULL)
908 return NULL;
909 entry = extfs_resolve_symlinks (entry);
910 if (entry == NULL)
911 return NULL;
913 if (S_ISDIR (entry->inode->mode))
914 ERRNOR (EISDIR, NULL);
916 if (entry->inode->local_filename == NULL)
918 char *local_filename;
920 local_handle = vfs_mkstemps (&local_filename, "extfs", entry->name);
922 if (local_handle == -1)
923 return NULL;
924 close (local_handle);
926 if (!created && ((flags & O_TRUNC) == 0)
927 && extfs_cmd (" copyout ", archive, entry, local_filename))
929 unlink (local_filename);
930 g_free (local_filename);
931 my_errno = EIO;
932 return NULL;
934 entry->inode->local_filename = local_filename;
937 local_handle = open (entry->inode->local_filename, NO_LINEAR (flags), mode);
939 if (local_handle == -1)
941 /* file exists(may be). Need to drop O_CREAT flag and truncate file content */
942 flags = ~O_CREAT & (NO_LINEAR (flags) | O_TRUNC);
943 local_handle = open (entry->inode->local_filename, flags, mode);
946 if (local_handle == -1)
947 ERRNOR (EIO, NULL);
949 extfs_info = g_new (struct pseudofile, 1);
950 extfs_info->archive = archive;
951 extfs_info->entry = entry;
952 extfs_info->has_changed = created;
953 extfs_info->local_handle = local_handle;
955 /* i.e. we had no open files and now we have one */
956 vfs_rmstamp (&vfs_extfs_ops, (vfsid) archive);
957 archive->fd_usage++;
958 return extfs_info;
961 /* --------------------------------------------------------------------------------------------- */
963 static ssize_t
964 extfs_read (void *data, char *buffer, size_t count)
966 struct pseudofile *file = (struct pseudofile *) data;
968 return read (file->local_handle, buffer, count);
971 /* --------------------------------------------------------------------------------------------- */
973 static int
974 extfs_close (void *data)
976 struct pseudofile *file;
977 int errno_code = 0;
978 file = (struct pseudofile *) data;
980 close (file->local_handle);
982 /* Commit the file if it has changed */
983 if (file->has_changed)
985 struct stat file_status;
987 if (extfs_cmd (" copyin ", file->archive, file->entry, file->entry->inode->local_filename))
988 errno_code = EIO;
990 if (stat (file->entry->inode->local_filename, &file_status) != 0)
991 errno_code = EIO;
992 else
993 file->entry->inode->size = file_status.st_size;
995 file->entry->inode->mtime = time (NULL);
998 if (--file->archive->fd_usage == 0)
999 vfs_stamp_create (&vfs_extfs_ops, file->archive);
1001 g_free (data);
1002 if (errno_code != 0)
1003 ERRNOR (EIO, -1);
1004 return 0;
1007 /* --------------------------------------------------------------------------------------------- */
1009 static int
1010 extfs_errno (struct vfs_class *me)
1012 (void) me;
1013 return my_errno;
1016 /* --------------------------------------------------------------------------------------------- */
1018 static void *
1019 extfs_opendir (const vfs_path_t * vpath)
1021 struct archive *archive = NULL;
1022 char *q;
1023 struct entry *entry;
1024 struct entry **info;
1026 q = extfs_get_path (vpath, &archive, FALSE);
1027 if (q == NULL)
1028 return NULL;
1029 entry = extfs_find_entry (archive->root_entry, q, FALSE, FALSE);
1030 g_free (q);
1031 if (entry == NULL)
1032 return NULL;
1033 entry = extfs_resolve_symlinks (entry);
1034 if (entry == NULL)
1035 return NULL;
1036 if (!S_ISDIR (entry->inode->mode))
1037 ERRNOR (ENOTDIR, NULL);
1039 info = g_new (struct entry *, 2);
1040 info[0] = entry->inode->first_in_subdir;
1041 info[1] = entry->inode->first_in_subdir;
1043 return info;
1046 /* --------------------------------------------------------------------------------------------- */
1048 static void *
1049 extfs_readdir (void *data)
1051 static union vfs_dirent dir;
1052 struct entry **info = (struct entry **) data;
1054 if (*info == NULL)
1055 return NULL;
1057 g_strlcpy (dir.dent.d_name, (*info)->name, MC_MAXPATHLEN);
1059 compute_namelen (&dir.dent);
1060 *info = (*info)->next_in_dir;
1062 return (void *) &dir;
1065 /* --------------------------------------------------------------------------------------------- */
1067 static int
1068 extfs_closedir (void *data)
1070 g_free (data);
1071 return 0;
1074 /* --------------------------------------------------------------------------------------------- */
1076 static void
1077 extfs_stat_move (struct stat *buf, const struct inode *inode)
1079 buf->st_dev = inode->dev;
1080 buf->st_ino = inode->inode;
1081 buf->st_mode = inode->mode;
1082 buf->st_nlink = inode->nlink;
1083 buf->st_uid = inode->uid;
1084 buf->st_gid = inode->gid;
1085 #ifdef HAVE_STRUCT_STAT_ST_RDEV
1086 buf->st_rdev = inode->rdev;
1087 #endif
1088 buf->st_size = inode->size;
1089 #ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
1090 buf->st_blksize = RECORDSIZE;
1091 #endif
1092 #ifdef HAVE_STRUCT_STAT_ST_BLOCKS
1093 buf->st_blocks = (inode->size + RECORDSIZE - 1) / RECORDSIZE;
1094 #endif
1095 buf->st_atime = inode->atime;
1096 buf->st_mtime = inode->mtime;
1097 buf->st_ctime = inode->ctime;
1100 /* --------------------------------------------------------------------------------------------- */
1102 static int
1103 extfs_internal_stat (const vfs_path_t * vpath, struct stat *buf, gboolean resolve)
1105 struct archive *archive;
1106 char *q;
1107 struct entry *entry;
1108 int result = -1;
1110 q = extfs_get_path_mangle (vpath, &archive, FALSE);
1111 if (q == NULL)
1112 goto cleanup;
1113 entry = extfs_find_entry (archive->root_entry, q, FALSE, FALSE);
1114 if (entry == NULL)
1115 goto cleanup;
1116 if (resolve)
1118 entry = extfs_resolve_symlinks (entry);
1119 if (entry == NULL)
1120 goto cleanup;
1122 extfs_stat_move (buf, entry->inode);
1123 result = 0;
1124 cleanup:
1125 return result;
1128 /* --------------------------------------------------------------------------------------------- */
1130 static int
1131 extfs_stat (const vfs_path_t * vpath, struct stat *buf)
1133 return extfs_internal_stat (vpath, buf, TRUE);
1136 /* --------------------------------------------------------------------------------------------- */
1138 static int
1139 extfs_lstat (const vfs_path_t * vpath, struct stat *buf)
1141 return extfs_internal_stat (vpath, buf, FALSE);
1144 /* --------------------------------------------------------------------------------------------- */
1146 static int
1147 extfs_fstat (void *data, struct stat *buf)
1149 struct pseudofile *file = (struct pseudofile *) data;
1151 extfs_stat_move (buf, file->entry->inode);
1152 return 0;
1155 /* --------------------------------------------------------------------------------------------- */
1157 static int
1158 extfs_readlink (const vfs_path_t * vpath, char *buf, size_t size)
1160 struct archive *archive;
1161 char *q;
1162 size_t len;
1163 struct entry *entry;
1164 int result = -1;
1166 q = extfs_get_path_mangle (vpath, &archive, FALSE);
1167 if (q == NULL)
1168 goto cleanup;
1169 entry = extfs_find_entry (archive->root_entry, q, FALSE, FALSE);
1170 if (entry == NULL)
1171 goto cleanup;
1172 if (!S_ISLNK (entry->inode->mode))
1174 vfs_path_element_t *path_element;
1175 path_element = vfs_path_get_by_index (vpath, -1);
1176 path_element->class->verrno = EINVAL;
1177 goto cleanup;
1179 len = strlen (entry->inode->linkname);
1180 if (size < len)
1181 len = size;
1182 /* readlink() does not append a NUL character to buf */
1183 result = len;
1184 memcpy (buf, entry->inode->linkname, result);
1185 cleanup:
1186 return result;
1189 /* --------------------------------------------------------------------------------------------- */
1191 static int
1192 extfs_chown (const vfs_path_t * vpath, uid_t owner, gid_t group)
1194 (void) vpath;
1195 (void) owner;
1196 (void) group;
1197 return 0;
1200 /* --------------------------------------------------------------------------------------------- */
1202 static int
1203 extfs_chmod (const vfs_path_t * vpath, mode_t mode)
1205 (void) vpath;
1206 (void) mode;
1207 return 0;
1210 /* --------------------------------------------------------------------------------------------- */
1212 static ssize_t
1213 extfs_write (void *data, const char *buf, size_t nbyte)
1215 struct pseudofile *file = (struct pseudofile *) data;
1217 file->has_changed = TRUE;
1218 return write (file->local_handle, buf, nbyte);
1221 /* --------------------------------------------------------------------------------------------- */
1223 static int
1224 extfs_unlink (const vfs_path_t * vpath)
1226 struct archive *archive;
1227 char *q;
1228 struct entry *entry;
1229 int result = -1;
1231 q = extfs_get_path_mangle (vpath, &archive, FALSE);
1232 if (q == NULL)
1233 goto cleanup;
1234 entry = extfs_find_entry (archive->root_entry, q, FALSE, FALSE);
1235 if (entry == NULL)
1236 goto cleanup;
1237 entry = extfs_resolve_symlinks (entry);
1238 if (entry == NULL)
1239 goto cleanup;
1240 if (S_ISDIR (entry->inode->mode))
1242 vfs_path_element_t *path_element = vfs_path_get_by_index (vpath, -1);
1243 path_element->class->verrno = EISDIR;
1244 goto cleanup;
1246 if (extfs_cmd (" rm ", archive, entry, ""))
1248 my_errno = EIO;
1249 goto cleanup;
1251 extfs_remove_entry (entry);
1252 result = 0;
1253 cleanup:
1254 return result;
1257 /* --------------------------------------------------------------------------------------------- */
1259 static int
1260 extfs_mkdir (const vfs_path_t * vpath, mode_t mode)
1262 struct archive *archive;
1263 char *q;
1264 struct entry *entry;
1265 int result = -1;
1266 vfs_path_element_t *path_element = vfs_path_get_by_index (vpath, -1);
1268 (void) mode;
1270 q = extfs_get_path_mangle (vpath, &archive, FALSE);
1271 if (q == NULL)
1272 goto cleanup;
1273 entry = extfs_find_entry (archive->root_entry, q, FALSE, FALSE);
1274 if (entry != NULL)
1276 path_element->class->verrno = EEXIST;
1277 goto cleanup;
1279 entry = extfs_find_entry (archive->root_entry, q, TRUE, FALSE);
1280 if (entry == NULL)
1281 goto cleanup;
1282 entry = extfs_resolve_symlinks (entry);
1283 if (entry == NULL)
1284 goto cleanup;
1285 if (!S_ISDIR (entry->inode->mode))
1287 path_element->class->verrno = ENOTDIR;
1288 goto cleanup;
1291 if (extfs_cmd (" mkdir ", archive, entry, ""))
1293 my_errno = EIO;
1294 extfs_remove_entry (entry);
1295 goto cleanup;
1297 result = 0;
1298 cleanup:
1299 return result;
1302 /* --------------------------------------------------------------------------------------------- */
1304 static int
1305 extfs_rmdir (const vfs_path_t * vpath)
1307 struct archive *archive;
1308 char *q;
1309 struct entry *entry;
1310 int result = -1;
1312 q = extfs_get_path_mangle (vpath, &archive, FALSE);
1313 if (q == NULL)
1314 goto cleanup;
1315 entry = extfs_find_entry (archive->root_entry, q, FALSE, FALSE);
1316 if (entry == NULL)
1317 goto cleanup;
1318 entry = extfs_resolve_symlinks (entry);
1319 if (entry == NULL)
1320 goto cleanup;
1321 if (!S_ISDIR (entry->inode->mode))
1323 vfs_path_element_t *path_element = vfs_path_get_by_index (vpath, -1);
1324 path_element->class->verrno = ENOTDIR;
1325 goto cleanup;
1328 if (extfs_cmd (" rmdir ", archive, entry, ""))
1330 my_errno = EIO;
1331 goto cleanup;
1333 extfs_remove_entry (entry);
1334 result = 0;
1335 cleanup:
1336 return result;
1339 /* --------------------------------------------------------------------------------------------- */
1341 static int
1342 extfs_chdir (const vfs_path_t * vpath)
1344 struct archive *archive = NULL;
1345 char *q;
1346 struct entry *entry;
1348 my_errno = ENOTDIR;
1349 q = extfs_get_path (vpath, &archive, FALSE);
1350 if (q == NULL)
1351 return -1;
1352 entry = extfs_find_entry (archive->root_entry, q, FALSE, FALSE);
1353 g_free (q);
1354 if (entry == NULL)
1355 return -1;
1356 entry = extfs_resolve_symlinks (entry);
1357 if ((entry == NULL) || (!S_ISDIR (entry->inode->mode)))
1358 return -1;
1359 my_errno = 0;
1360 return 0;
1363 /* --------------------------------------------------------------------------------------------- */
1365 static off_t
1366 extfs_lseek (void *data, off_t offset, int whence)
1368 struct pseudofile *file = (struct pseudofile *) data;
1370 return lseek (file->local_handle, offset, whence);
1373 /* --------------------------------------------------------------------------------------------- */
1375 static vfsid
1376 extfs_getid (const vfs_path_t * vpath)
1378 struct archive *archive = NULL;
1379 char *p;
1381 p = extfs_get_path (vpath, &archive, TRUE);
1382 if (p == NULL)
1383 return NULL;
1384 g_free (p);
1385 return (vfsid) archive;
1388 /* --------------------------------------------------------------------------------------------- */
1390 static int
1391 extfs_nothingisopen (vfsid id)
1393 return (((struct archive *) id)->fd_usage <= 0);
1396 /* --------------------------------------------------------------------------------------------- */
1398 static void
1399 extfs_remove_entry (struct entry *e)
1401 int i = --e->inode->nlink;
1402 struct entry *pe, *ent, *prev;
1404 if (S_ISDIR (e->inode->mode) && e->inode->first_in_subdir != NULL)
1406 struct entry *f = e->inode->first_in_subdir;
1407 e->inode->first_in_subdir = NULL;
1408 extfs_remove_entry (f);
1410 pe = e->dir;
1411 if (e == pe->inode->first_in_subdir)
1412 pe->inode->first_in_subdir = e->next_in_dir;
1414 prev = NULL;
1415 for (ent = pe->inode->first_in_subdir; ent && ent->next_in_dir; ent = ent->next_in_dir)
1416 if (e == ent->next_in_dir)
1418 prev = ent;
1419 break;
1421 if (prev)
1422 prev->next_in_dir = e->next_in_dir;
1423 if (e == pe->inode->last_in_subdir)
1424 pe->inode->last_in_subdir = prev;
1426 if (i <= 0)
1428 if (e->inode->local_filename != NULL)
1430 unlink (e->inode->local_filename);
1431 g_free (e->inode->local_filename);
1433 g_free (e->inode->linkname);
1434 g_free (e->inode);
1437 g_free (e->name);
1438 g_free (e);
1441 /* --------------------------------------------------------------------------------------------- */
1443 static void
1444 extfs_free_entry (struct entry *e)
1446 int i = --e->inode->nlink;
1448 if (S_ISDIR (e->inode->mode) && e->inode->first_in_subdir != NULL)
1450 struct entry *f = e->inode->first_in_subdir;
1452 e->inode->first_in_subdir = NULL;
1453 extfs_free_entry (f);
1455 if (i <= 0)
1457 if (e->inode->local_filename != NULL)
1459 unlink (e->inode->local_filename);
1460 g_free (e->inode->local_filename);
1462 g_free (e->inode->linkname);
1463 g_free (e->inode);
1465 if (e->next_in_dir != NULL)
1466 extfs_free_entry (e->next_in_dir);
1467 g_free (e->name);
1468 g_free (e);
1471 /* --------------------------------------------------------------------------------------------- */
1473 static void
1474 extfs_free (vfsid id)
1476 struct archive *archive = (struct archive *) id;
1478 if (archive == first_archive)
1480 first_archive = archive->next;
1482 else
1484 struct archive *parc;
1485 for (parc = first_archive; parc != NULL; parc = parc->next)
1486 if (parc->next == archive)
1488 parc->next = archive->next;
1489 break;
1492 extfs_free_archive (archive);
1495 /* --------------------------------------------------------------------------------------------- */
1497 static char *
1498 extfs_getlocalcopy (const vfs_path_t * vpath)
1500 struct pseudofile *fp;
1501 char *p;
1503 fp = (struct pseudofile *) extfs_open (vpath, O_RDONLY, 0);
1504 if (fp == NULL)
1505 return NULL;
1506 if (fp->entry->inode->local_filename == NULL)
1508 extfs_close ((void *) fp);
1509 return NULL;
1511 p = g_strdup (fp->entry->inode->local_filename);
1512 fp->archive->fd_usage++;
1513 extfs_close ((void *) fp);
1514 return p;
1517 /* --------------------------------------------------------------------------------------------- */
1519 static int
1520 extfs_ungetlocalcopy (const vfs_path_t * vpath, const char *local, int has_changed)
1522 struct pseudofile *fp;
1524 fp = (struct pseudofile *) extfs_open (vpath, O_RDONLY, 0);
1525 if (fp == NULL)
1526 return 0;
1528 if (strcmp (fp->entry->inode->local_filename, local) == 0)
1530 fp->archive->fd_usage--;
1531 if (has_changed != 0)
1532 fp->has_changed = TRUE;
1533 extfs_close ((void *) fp);
1534 return 0;
1536 else
1538 /* Should not happen */
1539 extfs_close ((void *) fp);
1540 return 0;
1544 /* --------------------------------------------------------------------------------------------- */
1546 static gboolean
1547 extfs_get_plugins (const char *where, gboolean silent)
1549 char *dirname;
1550 GDir *dir;
1551 const char *filename;
1553 dirname = g_build_path (PATH_SEP_STR, where, MC_EXTFS_DIR, (char *) NULL);
1554 dir = g_dir_open (dirname, 0, NULL);
1556 /* We may not use vfs_die() message or message or similar,
1557 * UI is not initialized at this time and message would not
1558 * appear on screen. */
1559 if (dir == NULL)
1561 if (!silent)
1562 fprintf (stderr, _("Warning: cannot open %s directory\n"), dirname);
1563 g_free (dirname);
1564 return FALSE;
1567 if (extfs_plugins == NULL)
1568 extfs_plugins = g_array_sized_new (FALSE, TRUE, sizeof (extfs_plugin_info_t), 32);
1570 while ((filename = g_dir_read_name (dir)) != NULL)
1572 char fullname[MC_MAXPATHLEN];
1573 struct stat s;
1575 g_snprintf (fullname, sizeof (fullname), "%s" PATH_SEP_STR "%s", dirname, filename);
1577 if ((stat (fullname, &s) == 0)
1578 && S_ISREG (s.st_mode) && !S_ISDIR (s.st_mode)
1579 && (((s.st_mode & S_IXOTH) != 0) ||
1580 ((s.st_mode & S_IXUSR) != 0) || ((s.st_mode & S_IXGRP) != 0)))
1582 int f;
1584 f = open (fullname, O_RDONLY);
1586 if (f > 0)
1588 size_t len, i;
1589 extfs_plugin_info_t info;
1590 gboolean found = FALSE;
1592 close (f);
1594 /* Handle those with a trailing '+', those flag that the
1595 * file system does not require an archive to work
1597 len = strlen (filename);
1598 info.need_archive = (filename[len - 1] != '+');
1599 info.path = g_strconcat (dirname, PATH_SEP_STR, (char *) NULL);
1600 info.prefix = g_strdup (filename);
1602 /* prepare to compare file names without trailing '+' */
1603 if (!info.need_archive)
1604 info.prefix[len - 1] = '\0';
1606 /* don't overload already found plugin */
1607 for (i = 0; i < extfs_plugins->len; i++)
1609 extfs_plugin_info_t *p;
1611 p = &g_array_index (extfs_plugins, extfs_plugin_info_t, i);
1613 /* 2 files with same names cannot be in a directory */
1614 if ((strcmp (info.path, p->path) != 0)
1615 && (strcmp (info.prefix, p->prefix) == 0))
1617 found = TRUE;
1618 break;
1622 if (found)
1624 g_free (info.path);
1625 g_free (info.prefix);
1627 else
1629 /* restore file name */
1630 if (!info.need_archive)
1631 info.prefix[len - 1] = '+';
1632 g_array_append_val (extfs_plugins, info);
1638 g_dir_close (dir);
1639 g_free (dirname);
1641 return TRUE;
1644 /* --------------------------------------------------------------------------------------------- */
1646 static int
1647 extfs_init (struct vfs_class *me)
1649 gboolean d1, d2;
1651 (void) me;
1653 /* 1st: scan user directory */
1654 d1 = extfs_get_plugins (mc_config_get_data_path (), TRUE); /* silent about user dir */
1655 /* 2nd: scan system dir */
1656 d2 = extfs_get_plugins (LIBEXECDIR, d1);
1658 return (d1 || d2 ? 1 : 0);
1661 /* --------------------------------------------------------------------------------------------- */
1663 static void
1664 extfs_done (struct vfs_class *me)
1666 size_t i;
1667 struct archive *ar;
1669 (void) me;
1671 for (ar = first_archive; ar != NULL;)
1673 extfs_free ((vfsid) ar);
1674 ar = first_archive;
1677 for (i = 0; i < extfs_plugins->len; i++)
1679 extfs_plugin_info_t *info;
1681 info = &g_array_index (extfs_plugins, extfs_plugin_info_t, i);
1682 g_free (info->path);
1683 g_free (info->prefix);
1686 if (extfs_plugins != NULL)
1687 g_array_free (extfs_plugins, TRUE);
1690 /* --------------------------------------------------------------------------------------------- */
1692 static int
1693 extfs_setctl (const vfs_path_t * vpath, int ctlop, void *arg)
1695 (void) arg;
1697 if (ctlop == VFS_SETCTL_RUN)
1699 extfs_run (vpath);
1700 return 1;
1702 return 0;
1705 /* --------------------------------------------------------------------------------------------- */
1706 /*** public functions ****************************************************************************/
1707 /* --------------------------------------------------------------------------------------------- */
1709 void
1710 init_extfs (void)
1712 vfs_extfs_ops.name = "extfs";
1713 vfs_extfs_ops.init = extfs_init;
1714 vfs_extfs_ops.done = extfs_done;
1715 vfs_extfs_ops.fill_names = extfs_fill_names;
1716 vfs_extfs_ops.which = extfs_which;
1717 vfs_extfs_ops.open = extfs_open;
1718 vfs_extfs_ops.close = extfs_close;
1719 vfs_extfs_ops.read = extfs_read;
1720 vfs_extfs_ops.write = extfs_write;
1721 vfs_extfs_ops.opendir = extfs_opendir;
1722 vfs_extfs_ops.readdir = extfs_readdir;
1723 vfs_extfs_ops.closedir = extfs_closedir;
1724 vfs_extfs_ops.stat = extfs_stat;
1725 vfs_extfs_ops.lstat = extfs_lstat;
1726 vfs_extfs_ops.fstat = extfs_fstat;
1727 vfs_extfs_ops.chmod = extfs_chmod;
1728 vfs_extfs_ops.chown = extfs_chown;
1729 vfs_extfs_ops.readlink = extfs_readlink;
1730 vfs_extfs_ops.unlink = extfs_unlink;
1731 vfs_extfs_ops.chdir = extfs_chdir;
1732 vfs_extfs_ops.ferrno = extfs_errno;
1733 vfs_extfs_ops.lseek = extfs_lseek;
1734 vfs_extfs_ops.getid = extfs_getid;
1735 vfs_extfs_ops.nothingisopen = extfs_nothingisopen;
1736 vfs_extfs_ops.free = extfs_free;
1737 vfs_extfs_ops.getlocalcopy = extfs_getlocalcopy;
1738 vfs_extfs_ops.ungetlocalcopy = extfs_ungetlocalcopy;
1739 vfs_extfs_ops.mkdir = extfs_mkdir;
1740 vfs_extfs_ops.rmdir = extfs_rmdir;
1741 vfs_extfs_ops.setctl = extfs_setctl;
1742 vfs_register_class (&vfs_extfs_ops);
1745 /* --------------------------------------------------------------------------------------------- */