Return values of following functions are constants now:
[midnight-commander.git] / src / vfs / extfs / extfs.c
blob9835cd854f46650f67b4a27362d810faa787ea9d
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;
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. Note that inname
665 * can be changed and the result may point inside the original string.
668 static char *
669 extfs_get_path_mangle (const vfs_path_t * vpath, struct archive **archive, gboolean do_not_open)
671 char *archive_name;
672 int result = -1;
673 struct archive *parc;
674 int fstype;
675 const vfs_path_element_t *path_element;
677 path_element = vfs_path_get_by_index (vpath, -1);
679 archive_name = vfs_path_to_str_elements_count (vpath, -1);
681 fstype = extfs_which (path_element->class, path_element->vfs_prefix);
683 if (fstype == -1)
685 g_free (archive_name);
686 return NULL;
690 * All filesystems should have some local archive, at least
691 * it can be PATH_SEP ('/').
693 for (parc = first_archive; parc != NULL; parc = parc->next)
694 if (parc->name != NULL)
696 if (strcmp (parc->name, archive_name) == 0)
698 vfs_stamp (&vfs_extfs_ops, (vfsid) parc);
699 goto return_success;
703 result = do_not_open ? -1 : extfs_read_archive (fstype, archive_name, &parc);
704 if (result == -1)
706 path_element->class->verrno = EIO;
707 g_free (archive_name);
708 return NULL;
711 return_success:
712 *archive = parc;
713 g_free (archive_name);
714 return path_element->path;
717 /* --------------------------------------------------------------------------------------------- */
719 * Dissect the path and create corresponding superblock.
720 * The result should be freed.
723 static char *
724 extfs_get_path (const vfs_path_t * vpath, struct archive **archive, gboolean do_not_open)
726 return g_strdup (extfs_get_path_mangle (vpath, archive, do_not_open));
729 /* --------------------------------------------------------------------------------------------- */
730 /* Return allocated path (without leading slash) inside the archive */
732 static char *
733 extfs_get_path_from_entry (struct entry *entry)
735 GString *localpath;
737 localpath = g_string_new ("");
739 while (entry->dir != NULL)
741 g_string_prepend (localpath, entry->name);
742 if (entry->dir->dir != NULL)
743 g_string_prepend_c (localpath, PATH_SEP);
744 entry = entry->dir;
747 return g_string_free (localpath, FALSE);
750 /* --------------------------------------------------------------------------------------------- */
752 static struct entry *
753 extfs_resolve_symlinks_int (struct entry *entry, GSList * list)
755 struct entry *pent = NULL;
757 if (!S_ISLNK (entry->inode->mode))
758 return entry;
760 if (g_slist_find (list, entry) != NULL)
762 /* Here we protect us against symlink looping */
763 errloop = TRUE;
765 else
767 GSList *looping;
769 looping = g_slist_prepend (list, entry);
770 pent = extfs_find_entry_int (entry->dir, entry->inode->linkname, looping, FALSE, FALSE);
771 looping = g_slist_delete_link (looping, looping);
773 if (pent == NULL)
774 my_errno = ENOENT;
777 return pent;
780 /* --------------------------------------------------------------------------------------------- */
782 static struct entry *
783 extfs_resolve_symlinks (struct entry *entry)
785 struct entry *res;
787 errloop = FALSE;
788 notadir = FALSE;
789 res = extfs_resolve_symlinks_int (entry, NULL);
790 if (res == NULL)
792 if (errloop)
793 my_errno = ELOOP;
794 else if (notadir)
795 my_errno = ENOTDIR;
797 return res;
800 /* --------------------------------------------------------------------------------------------- */
802 static char *
803 extfs_get_archive_name (struct archive *archive)
805 const char *archive_name;
807 if (archive->local_name)
808 archive_name = archive->local_name;
809 else
810 archive_name = archive->name;
812 if (!archive_name || !*archive_name)
813 return g_strdup ("no_archive_name");
814 else
816 char *ret_str;
817 vfs_path_t *vpath;
818 const vfs_path_element_t *path_element;
820 vpath = vfs_path_from_str (archive_name);
821 path_element = vfs_path_get_by_index (vpath, -1);
822 ret_str = g_strdup (path_element->path);
823 vfs_path_free (vpath);
824 return ret_str;
828 /* --------------------------------------------------------------------------------------------- */
829 /** Don't pass localname as NULL */
831 static int
832 extfs_cmd (const char *str_extfs_cmd, struct archive *archive,
833 struct entry *entry, const char *localname)
835 char *file;
836 char *quoted_file;
837 char *quoted_localname;
838 char *archive_name, *quoted_archive_name;
839 const extfs_plugin_info_t *info;
840 char *cmd;
841 int retval;
843 file = extfs_get_path_from_entry (entry);
844 quoted_file = name_quote (file, 0);
845 g_free (file);
847 archive_name = extfs_get_archive_name (archive);
848 quoted_archive_name = name_quote (archive_name, 0);
849 g_free (archive_name);
850 quoted_localname = name_quote (localname, 0);
851 info = &g_array_index (extfs_plugins, extfs_plugin_info_t, archive->fstype);
852 cmd = g_strconcat (info->path, info->prefix, str_extfs_cmd,
853 quoted_archive_name, " ", quoted_file, " ", quoted_localname, (char *) NULL);
854 g_free (quoted_file);
855 g_free (quoted_localname);
856 g_free (quoted_archive_name);
858 open_error_pipe ();
859 retval = my_system (EXECUTE_AS_SHELL, shell, cmd);
860 g_free (cmd);
861 close_error_pipe (D_ERROR, NULL);
862 return retval;
865 /* --------------------------------------------------------------------------------------------- */
867 static void
868 extfs_run (const vfs_path_t * vpath)
870 struct archive *archive = NULL;
871 char *p, *q, *archive_name, *quoted_archive_name;
872 char *cmd;
873 const extfs_plugin_info_t *info;
875 p = extfs_get_path (vpath, &archive, FALSE);
876 if (p == NULL)
877 return;
878 q = name_quote (p, 0);
879 g_free (p);
881 archive_name = extfs_get_archive_name (archive);
882 quoted_archive_name = name_quote (archive_name, 0);
883 g_free (archive_name);
884 info = &g_array_index (extfs_plugins, extfs_plugin_info_t, archive->fstype);
885 cmd =
886 g_strconcat (info->path, info->prefix, " run ", quoted_archive_name, " ", q, (char *) NULL);
887 g_free (quoted_archive_name);
888 g_free (q);
889 shell_execute (cmd, 0);
890 g_free (cmd);
893 /* --------------------------------------------------------------------------------------------- */
895 static void *
896 extfs_open (const vfs_path_t * vpath, int flags, mode_t mode)
898 struct pseudofile *extfs_info;
899 struct archive *archive = NULL;
900 char *q;
901 struct entry *entry;
902 int local_handle;
903 gboolean created = FALSE;
905 q = extfs_get_path (vpath, &archive, FALSE);
906 if (q == NULL)
907 return NULL;
908 entry = extfs_find_entry (archive->root_entry, q, FALSE, FALSE);
909 if ((entry == NULL) && ((flags & O_CREAT) != 0))
911 /* Create new entry */
912 entry = extfs_find_entry (archive->root_entry, q, FALSE, TRUE);
913 created = (entry != NULL);
916 g_free (q);
917 if (entry == NULL)
918 return NULL;
919 entry = extfs_resolve_symlinks (entry);
920 if (entry == NULL)
921 return NULL;
923 if (S_ISDIR (entry->inode->mode))
924 ERRNOR (EISDIR, NULL);
926 if (entry->inode->local_filename == NULL)
928 vfs_path_t *local_filename_vpath;
929 const char *local_filename;
931 local_handle = vfs_mkstemps (&local_filename_vpath, "extfs", entry->name);
933 if (local_handle == -1)
934 return NULL;
935 close (local_handle);
936 local_filename = vfs_path_get_by_index (local_filename_vpath, -1)->path;
938 if (!created && ((flags & O_TRUNC) == 0)
939 && extfs_cmd (" copyout ", archive, entry, local_filename))
941 unlink (local_filename);
942 vfs_path_free (local_filename_vpath);
943 my_errno = EIO;
944 return NULL;
946 entry->inode->local_filename = g_strdup (local_filename);
947 vfs_path_free (local_filename_vpath);
950 local_handle = open (entry->inode->local_filename, NO_LINEAR (flags), mode);
952 if (local_handle == -1)
954 /* file exists(may be). Need to drop O_CREAT flag and truncate file content */
955 flags = ~O_CREAT & (NO_LINEAR (flags) | O_TRUNC);
956 local_handle = open (entry->inode->local_filename, flags, mode);
959 if (local_handle == -1)
960 ERRNOR (EIO, NULL);
962 extfs_info = g_new (struct pseudofile, 1);
963 extfs_info->archive = archive;
964 extfs_info->entry = entry;
965 extfs_info->has_changed = created;
966 extfs_info->local_handle = local_handle;
968 /* i.e. we had no open files and now we have one */
969 vfs_rmstamp (&vfs_extfs_ops, (vfsid) archive);
970 archive->fd_usage++;
971 return extfs_info;
974 /* --------------------------------------------------------------------------------------------- */
976 static ssize_t
977 extfs_read (void *data, char *buffer, size_t count)
979 struct pseudofile *file = (struct pseudofile *) data;
981 return read (file->local_handle, buffer, count);
984 /* --------------------------------------------------------------------------------------------- */
986 static int
987 extfs_close (void *data)
989 struct pseudofile *file;
990 int errno_code = 0;
991 file = (struct pseudofile *) data;
993 close (file->local_handle);
995 /* Commit the file if it has changed */
996 if (file->has_changed)
998 struct stat file_status;
1000 if (extfs_cmd (" copyin ", file->archive, file->entry, file->entry->inode->local_filename))
1001 errno_code = EIO;
1003 if (stat (file->entry->inode->local_filename, &file_status) != 0)
1004 errno_code = EIO;
1005 else
1006 file->entry->inode->size = file_status.st_size;
1008 file->entry->inode->mtime = time (NULL);
1011 if (--file->archive->fd_usage == 0)
1012 vfs_stamp_create (&vfs_extfs_ops, file->archive);
1014 g_free (data);
1015 if (errno_code != 0)
1016 ERRNOR (EIO, -1);
1017 return 0;
1020 /* --------------------------------------------------------------------------------------------- */
1022 static int
1023 extfs_errno (struct vfs_class *me)
1025 (void) me;
1026 return my_errno;
1029 /* --------------------------------------------------------------------------------------------- */
1031 static void *
1032 extfs_opendir (const vfs_path_t * vpath)
1034 struct archive *archive = NULL;
1035 char *q;
1036 struct entry *entry;
1037 struct entry **info;
1039 q = extfs_get_path (vpath, &archive, FALSE);
1040 if (q == NULL)
1041 return NULL;
1042 entry = extfs_find_entry (archive->root_entry, q, FALSE, FALSE);
1043 g_free (q);
1044 if (entry == NULL)
1045 return NULL;
1046 entry = extfs_resolve_symlinks (entry);
1047 if (entry == NULL)
1048 return NULL;
1049 if (!S_ISDIR (entry->inode->mode))
1050 ERRNOR (ENOTDIR, NULL);
1052 info = g_new (struct entry *, 2);
1053 info[0] = entry->inode->first_in_subdir;
1054 info[1] = entry->inode->first_in_subdir;
1056 return info;
1059 /* --------------------------------------------------------------------------------------------- */
1061 static void *
1062 extfs_readdir (void *data)
1064 static union vfs_dirent dir;
1065 struct entry **info = (struct entry **) data;
1067 if (*info == NULL)
1068 return NULL;
1070 g_strlcpy (dir.dent.d_name, (*info)->name, MC_MAXPATHLEN);
1072 compute_namelen (&dir.dent);
1073 *info = (*info)->next_in_dir;
1075 return (void *) &dir;
1078 /* --------------------------------------------------------------------------------------------- */
1080 static int
1081 extfs_closedir (void *data)
1083 g_free (data);
1084 return 0;
1087 /* --------------------------------------------------------------------------------------------- */
1089 static void
1090 extfs_stat_move (struct stat *buf, const struct inode *inode)
1092 buf->st_dev = inode->dev;
1093 buf->st_ino = inode->inode;
1094 buf->st_mode = inode->mode;
1095 buf->st_nlink = inode->nlink;
1096 buf->st_uid = inode->uid;
1097 buf->st_gid = inode->gid;
1098 #ifdef HAVE_STRUCT_STAT_ST_RDEV
1099 buf->st_rdev = inode->rdev;
1100 #endif
1101 buf->st_size = inode->size;
1102 #ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
1103 buf->st_blksize = RECORDSIZE;
1104 #endif
1105 #ifdef HAVE_STRUCT_STAT_ST_BLOCKS
1106 buf->st_blocks = (inode->size + RECORDSIZE - 1) / RECORDSIZE;
1107 #endif
1108 buf->st_atime = inode->atime;
1109 buf->st_mtime = inode->mtime;
1110 buf->st_ctime = inode->ctime;
1113 /* --------------------------------------------------------------------------------------------- */
1115 static int
1116 extfs_internal_stat (const vfs_path_t * vpath, struct stat *buf, gboolean resolve)
1118 struct archive *archive;
1119 char *q;
1120 struct entry *entry;
1121 int result = -1;
1123 q = extfs_get_path_mangle (vpath, &archive, FALSE);
1124 if (q == NULL)
1125 goto cleanup;
1126 entry = extfs_find_entry (archive->root_entry, q, FALSE, FALSE);
1127 if (entry == NULL)
1128 goto cleanup;
1129 if (resolve)
1131 entry = extfs_resolve_symlinks (entry);
1132 if (entry == NULL)
1133 goto cleanup;
1135 extfs_stat_move (buf, entry->inode);
1136 result = 0;
1137 cleanup:
1138 return result;
1141 /* --------------------------------------------------------------------------------------------- */
1143 static int
1144 extfs_stat (const vfs_path_t * vpath, struct stat *buf)
1146 return extfs_internal_stat (vpath, buf, TRUE);
1149 /* --------------------------------------------------------------------------------------------- */
1151 static int
1152 extfs_lstat (const vfs_path_t * vpath, struct stat *buf)
1154 return extfs_internal_stat (vpath, buf, FALSE);
1157 /* --------------------------------------------------------------------------------------------- */
1159 static int
1160 extfs_fstat (void *data, struct stat *buf)
1162 struct pseudofile *file = (struct pseudofile *) data;
1164 extfs_stat_move (buf, file->entry->inode);
1165 return 0;
1168 /* --------------------------------------------------------------------------------------------- */
1170 static int
1171 extfs_readlink (const vfs_path_t * vpath, char *buf, size_t size)
1173 struct archive *archive;
1174 char *q;
1175 size_t len;
1176 struct entry *entry;
1177 int result = -1;
1179 q = extfs_get_path_mangle (vpath, &archive, FALSE);
1180 if (q == NULL)
1181 goto cleanup;
1182 entry = extfs_find_entry (archive->root_entry, q, FALSE, FALSE);
1183 if (entry == NULL)
1184 goto cleanup;
1185 if (!S_ISLNK (entry->inode->mode))
1187 const vfs_path_element_t *path_element;
1189 path_element = vfs_path_get_by_index (vpath, -1);
1190 path_element->class->verrno = EINVAL;
1191 goto cleanup;
1193 len = strlen (entry->inode->linkname);
1194 if (size < len)
1195 len = size;
1196 /* readlink() does not append a NUL character to buf */
1197 result = len;
1198 memcpy (buf, entry->inode->linkname, result);
1199 cleanup:
1200 return result;
1203 /* --------------------------------------------------------------------------------------------- */
1205 static int
1206 extfs_chown (const vfs_path_t * vpath, uid_t owner, gid_t group)
1208 (void) vpath;
1209 (void) owner;
1210 (void) group;
1211 return 0;
1214 /* --------------------------------------------------------------------------------------------- */
1216 static int
1217 extfs_chmod (const vfs_path_t * vpath, mode_t mode)
1219 (void) vpath;
1220 (void) mode;
1221 return 0;
1224 /* --------------------------------------------------------------------------------------------- */
1226 static ssize_t
1227 extfs_write (void *data, const char *buf, size_t nbyte)
1229 struct pseudofile *file = (struct pseudofile *) data;
1231 file->has_changed = TRUE;
1232 return write (file->local_handle, buf, nbyte);
1235 /* --------------------------------------------------------------------------------------------- */
1237 static int
1238 extfs_unlink (const vfs_path_t * vpath)
1240 struct archive *archive;
1241 char *q;
1242 struct entry *entry;
1243 int result = -1;
1245 q = extfs_get_path_mangle (vpath, &archive, FALSE);
1246 if (q == NULL)
1247 goto cleanup;
1248 entry = extfs_find_entry (archive->root_entry, q, FALSE, FALSE);
1249 if (entry == NULL)
1250 goto cleanup;
1251 entry = extfs_resolve_symlinks (entry);
1252 if (entry == NULL)
1253 goto cleanup;
1254 if (S_ISDIR (entry->inode->mode))
1256 const vfs_path_element_t *path_element;
1258 path_element = vfs_path_get_by_index (vpath, -1);
1259 path_element->class->verrno = EISDIR;
1260 goto cleanup;
1262 if (extfs_cmd (" rm ", archive, entry, ""))
1264 my_errno = EIO;
1265 goto cleanup;
1267 extfs_remove_entry (entry);
1268 result = 0;
1269 cleanup:
1270 return result;
1273 /* --------------------------------------------------------------------------------------------- */
1275 static int
1276 extfs_mkdir (const vfs_path_t * vpath, mode_t mode)
1278 struct archive *archive;
1279 char *q;
1280 struct entry *entry;
1281 int result = -1;
1282 const vfs_path_element_t *path_element;
1284 (void) mode;
1286 path_element = vfs_path_get_by_index (vpath, -1);
1287 q = extfs_get_path_mangle (vpath, &archive, FALSE);
1288 if (q == NULL)
1289 goto cleanup;
1290 entry = extfs_find_entry (archive->root_entry, q, FALSE, FALSE);
1291 if (entry != NULL)
1293 path_element->class->verrno = EEXIST;
1294 goto cleanup;
1296 entry = extfs_find_entry (archive->root_entry, q, TRUE, FALSE);
1297 if (entry == NULL)
1298 goto cleanup;
1299 entry = extfs_resolve_symlinks (entry);
1300 if (entry == NULL)
1301 goto cleanup;
1302 if (!S_ISDIR (entry->inode->mode))
1304 path_element->class->verrno = ENOTDIR;
1305 goto cleanup;
1308 if (extfs_cmd (" mkdir ", archive, entry, ""))
1310 my_errno = EIO;
1311 extfs_remove_entry (entry);
1312 goto cleanup;
1314 result = 0;
1315 cleanup:
1316 return result;
1319 /* --------------------------------------------------------------------------------------------- */
1321 static int
1322 extfs_rmdir (const vfs_path_t * vpath)
1324 struct archive *archive;
1325 char *q;
1326 struct entry *entry;
1327 int result = -1;
1329 q = extfs_get_path_mangle (vpath, &archive, FALSE);
1330 if (q == NULL)
1331 goto cleanup;
1332 entry = extfs_find_entry (archive->root_entry, q, FALSE, FALSE);
1333 if (entry == NULL)
1334 goto cleanup;
1335 entry = extfs_resolve_symlinks (entry);
1336 if (entry == NULL)
1337 goto cleanup;
1338 if (!S_ISDIR (entry->inode->mode))
1340 const vfs_path_element_t *path_element;
1342 path_element = vfs_path_get_by_index (vpath, -1);
1343 path_element->class->verrno = ENOTDIR;
1344 goto cleanup;
1347 if (extfs_cmd (" rmdir ", archive, entry, ""))
1349 my_errno = EIO;
1350 goto cleanup;
1352 extfs_remove_entry (entry);
1353 result = 0;
1354 cleanup:
1355 return result;
1358 /* --------------------------------------------------------------------------------------------- */
1360 static int
1361 extfs_chdir (const vfs_path_t * vpath)
1363 struct archive *archive = NULL;
1364 char *q;
1365 struct entry *entry;
1367 my_errno = ENOTDIR;
1368 q = extfs_get_path (vpath, &archive, FALSE);
1369 if (q == NULL)
1370 return -1;
1371 entry = extfs_find_entry (archive->root_entry, q, FALSE, FALSE);
1372 g_free (q);
1373 if (entry == NULL)
1374 return -1;
1375 entry = extfs_resolve_symlinks (entry);
1376 if ((entry == NULL) || (!S_ISDIR (entry->inode->mode)))
1377 return -1;
1378 my_errno = 0;
1379 return 0;
1382 /* --------------------------------------------------------------------------------------------- */
1384 static off_t
1385 extfs_lseek (void *data, off_t offset, int whence)
1387 struct pseudofile *file = (struct pseudofile *) data;
1389 return lseek (file->local_handle, offset, whence);
1392 /* --------------------------------------------------------------------------------------------- */
1394 static vfsid
1395 extfs_getid (const vfs_path_t * vpath)
1397 struct archive *archive = NULL;
1398 char *p;
1400 p = extfs_get_path (vpath, &archive, TRUE);
1401 if (p == NULL)
1402 return NULL;
1403 g_free (p);
1404 return (vfsid) archive;
1407 /* --------------------------------------------------------------------------------------------- */
1409 static int
1410 extfs_nothingisopen (vfsid id)
1412 return (((struct archive *) id)->fd_usage <= 0);
1415 /* --------------------------------------------------------------------------------------------- */
1417 static void
1418 extfs_remove_entry (struct entry *e)
1420 int i = --e->inode->nlink;
1421 struct entry *pe, *ent, *prev;
1423 if (S_ISDIR (e->inode->mode) && e->inode->first_in_subdir != NULL)
1425 struct entry *f = e->inode->first_in_subdir;
1426 e->inode->first_in_subdir = NULL;
1427 extfs_remove_entry (f);
1429 pe = e->dir;
1430 if (e == pe->inode->first_in_subdir)
1431 pe->inode->first_in_subdir = e->next_in_dir;
1433 prev = NULL;
1434 for (ent = pe->inode->first_in_subdir; ent && ent->next_in_dir; ent = ent->next_in_dir)
1435 if (e == ent->next_in_dir)
1437 prev = ent;
1438 break;
1440 if (prev)
1441 prev->next_in_dir = e->next_in_dir;
1442 if (e == pe->inode->last_in_subdir)
1443 pe->inode->last_in_subdir = prev;
1445 if (i <= 0)
1447 if (e->inode->local_filename != NULL)
1449 unlink (e->inode->local_filename);
1450 g_free (e->inode->local_filename);
1452 g_free (e->inode->linkname);
1453 g_free (e->inode);
1456 g_free (e->name);
1457 g_free (e);
1460 /* --------------------------------------------------------------------------------------------- */
1462 static void
1463 extfs_free_entry (struct entry *e)
1465 int i = --e->inode->nlink;
1467 if (S_ISDIR (e->inode->mode) && e->inode->first_in_subdir != NULL)
1469 struct entry *f = e->inode->first_in_subdir;
1471 e->inode->first_in_subdir = NULL;
1472 extfs_free_entry (f);
1474 if (i <= 0)
1476 if (e->inode->local_filename != NULL)
1478 unlink (e->inode->local_filename);
1479 g_free (e->inode->local_filename);
1481 g_free (e->inode->linkname);
1482 g_free (e->inode);
1484 if (e->next_in_dir != NULL)
1485 extfs_free_entry (e->next_in_dir);
1486 g_free (e->name);
1487 g_free (e);
1490 /* --------------------------------------------------------------------------------------------- */
1492 static void
1493 extfs_free (vfsid id)
1495 struct archive *archive = (struct archive *) id;
1497 if (archive == first_archive)
1499 first_archive = archive->next;
1501 else
1503 struct archive *parc;
1504 for (parc = first_archive; parc != NULL; parc = parc->next)
1505 if (parc->next == archive)
1507 parc->next = archive->next;
1508 break;
1511 extfs_free_archive (archive);
1514 /* --------------------------------------------------------------------------------------------- */
1516 static vfs_path_t *
1517 extfs_getlocalcopy (const vfs_path_t * vpath)
1519 struct pseudofile *fp;
1520 vfs_path_t *p;
1522 fp = (struct pseudofile *) extfs_open (vpath, O_RDONLY, 0);
1523 if (fp == NULL)
1524 return NULL;
1525 if (fp->entry->inode->local_filename == NULL)
1527 extfs_close ((void *) fp);
1528 return NULL;
1530 p = vfs_path_from_str (fp->entry->inode->local_filename);
1531 fp->archive->fd_usage++;
1532 extfs_close ((void *) fp);
1533 return p;
1536 /* --------------------------------------------------------------------------------------------- */
1538 static int
1539 extfs_ungetlocalcopy (const vfs_path_t * vpath, const vfs_path_t * local, gboolean has_changed)
1541 struct pseudofile *fp;
1543 fp = (struct pseudofile *) extfs_open (vpath, O_RDONLY, 0);
1544 if (fp == NULL)
1545 return 0;
1547 if (strcmp (fp->entry->inode->local_filename, vfs_path_get_last_path_str (local)) == 0)
1549 fp->archive->fd_usage--;
1550 if (has_changed)
1551 fp->has_changed = TRUE;
1552 extfs_close ((void *) fp);
1553 return 0;
1555 else
1557 /* Should not happen */
1558 extfs_close ((void *) fp);
1559 return 0;
1563 /* --------------------------------------------------------------------------------------------- */
1565 static gboolean
1566 extfs_get_plugins (const char *where, gboolean silent)
1568 char *dirname;
1569 GDir *dir;
1570 const char *filename;
1572 dirname = g_build_path (PATH_SEP_STR, where, MC_EXTFS_DIR, (char *) NULL);
1573 dir = g_dir_open (dirname, 0, NULL);
1575 /* We may not use vfs_die() message or message or similar,
1576 * UI is not initialized at this time and message would not
1577 * appear on screen. */
1578 if (dir == NULL)
1580 if (!silent)
1581 fprintf (stderr, _("Warning: cannot open %s directory\n"), dirname);
1582 g_free (dirname);
1583 return FALSE;
1586 if (extfs_plugins == NULL)
1587 extfs_plugins = g_array_sized_new (FALSE, TRUE, sizeof (extfs_plugin_info_t), 32);
1589 while ((filename = g_dir_read_name (dir)) != NULL)
1591 char fullname[MC_MAXPATHLEN];
1592 struct stat s;
1594 g_snprintf (fullname, sizeof (fullname), "%s" PATH_SEP_STR "%s", dirname, filename);
1596 if ((stat (fullname, &s) == 0)
1597 && S_ISREG (s.st_mode) && !S_ISDIR (s.st_mode)
1598 && (((s.st_mode & S_IXOTH) != 0) ||
1599 ((s.st_mode & S_IXUSR) != 0) || ((s.st_mode & S_IXGRP) != 0)))
1601 int f;
1603 f = open (fullname, O_RDONLY);
1605 if (f > 0)
1607 size_t len, i;
1608 extfs_plugin_info_t info;
1609 gboolean found = FALSE;
1611 close (f);
1613 /* Handle those with a trailing '+', those flag that the
1614 * file system does not require an archive to work
1616 len = strlen (filename);
1617 info.need_archive = (filename[len - 1] != '+');
1618 info.path = g_strconcat (dirname, PATH_SEP_STR, (char *) NULL);
1619 info.prefix = g_strdup (filename);
1621 /* prepare to compare file names without trailing '+' */
1622 if (!info.need_archive)
1623 info.prefix[len - 1] = '\0';
1625 /* don't overload already found plugin */
1626 for (i = 0; i < extfs_plugins->len; i++)
1628 extfs_plugin_info_t *p;
1630 p = &g_array_index (extfs_plugins, extfs_plugin_info_t, i);
1632 /* 2 files with same names cannot be in a directory */
1633 if ((strcmp (info.path, p->path) != 0)
1634 && (strcmp (info.prefix, p->prefix) == 0))
1636 found = TRUE;
1637 break;
1641 if (found)
1643 g_free (info.path);
1644 g_free (info.prefix);
1646 else
1648 /* restore file name */
1649 if (!info.need_archive)
1650 info.prefix[len - 1] = '+';
1651 g_array_append_val (extfs_plugins, info);
1657 g_dir_close (dir);
1658 g_free (dirname);
1660 return TRUE;
1663 /* --------------------------------------------------------------------------------------------- */
1665 static int
1666 extfs_init (struct vfs_class *me)
1668 gboolean d1, d2;
1670 (void) me;
1672 /* 1st: scan user directory */
1673 d1 = extfs_get_plugins (mc_config_get_data_path (), TRUE); /* silent about user dir */
1674 /* 2nd: scan system dir */
1675 d2 = extfs_get_plugins (LIBEXECDIR, d1);
1677 return (d1 || d2 ? 1 : 0);
1680 /* --------------------------------------------------------------------------------------------- */
1682 static void
1683 extfs_done (struct vfs_class *me)
1685 size_t i;
1686 struct archive *ar;
1688 (void) me;
1690 for (ar = first_archive; ar != NULL;)
1692 extfs_free ((vfsid) ar);
1693 ar = first_archive;
1696 for (i = 0; i < extfs_plugins->len; i++)
1698 extfs_plugin_info_t *info;
1700 info = &g_array_index (extfs_plugins, extfs_plugin_info_t, i);
1701 g_free (info->path);
1702 g_free (info->prefix);
1705 if (extfs_plugins != NULL)
1706 g_array_free (extfs_plugins, TRUE);
1709 /* --------------------------------------------------------------------------------------------- */
1711 static int
1712 extfs_setctl (const vfs_path_t * vpath, int ctlop, void *arg)
1714 (void) arg;
1716 if (ctlop == VFS_SETCTL_RUN)
1718 extfs_run (vpath);
1719 return 1;
1721 return 0;
1724 /* --------------------------------------------------------------------------------------------- */
1725 /*** public functions ****************************************************************************/
1726 /* --------------------------------------------------------------------------------------------- */
1728 void
1729 init_extfs (void)
1731 vfs_extfs_ops.name = "extfs";
1732 vfs_extfs_ops.init = extfs_init;
1733 vfs_extfs_ops.done = extfs_done;
1734 vfs_extfs_ops.fill_names = extfs_fill_names;
1735 vfs_extfs_ops.which = extfs_which;
1736 vfs_extfs_ops.open = extfs_open;
1737 vfs_extfs_ops.close = extfs_close;
1738 vfs_extfs_ops.read = extfs_read;
1739 vfs_extfs_ops.write = extfs_write;
1740 vfs_extfs_ops.opendir = extfs_opendir;
1741 vfs_extfs_ops.readdir = extfs_readdir;
1742 vfs_extfs_ops.closedir = extfs_closedir;
1743 vfs_extfs_ops.stat = extfs_stat;
1744 vfs_extfs_ops.lstat = extfs_lstat;
1745 vfs_extfs_ops.fstat = extfs_fstat;
1746 vfs_extfs_ops.chmod = extfs_chmod;
1747 vfs_extfs_ops.chown = extfs_chown;
1748 vfs_extfs_ops.readlink = extfs_readlink;
1749 vfs_extfs_ops.unlink = extfs_unlink;
1750 vfs_extfs_ops.chdir = extfs_chdir;
1751 vfs_extfs_ops.ferrno = extfs_errno;
1752 vfs_extfs_ops.lseek = extfs_lseek;
1753 vfs_extfs_ops.getid = extfs_getid;
1754 vfs_extfs_ops.nothingisopen = extfs_nothingisopen;
1755 vfs_extfs_ops.free = extfs_free;
1756 vfs_extfs_ops.getlocalcopy = extfs_getlocalcopy;
1757 vfs_extfs_ops.ungetlocalcopy = extfs_ungetlocalcopy;
1758 vfs_extfs_ops.mkdir = extfs_mkdir;
1759 vfs_extfs_ops.rmdir = extfs_rmdir;
1760 vfs_extfs_ops.setctl = extfs_setctl;
1761 vfs_register_class (&vfs_extfs_ops);
1764 /* --------------------------------------------------------------------------------------------- */