Changed interface of functions mc_getlocalcopy() and mc_ungetlocalcopy()
[midnight-commander.git] / src / vfs / extfs / extfs.c
blobea61888bf4474c04b00ac2abbd43e034782f52bf
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, const char *name, GSList * list,
242 gboolean make_dirs, gboolean make_file)
244 struct entry *pent, *pdir;
245 const char *p, *name_end;
246 char *q;
247 char c = PATH_SEP;
249 if (g_path_is_absolute (name))
251 /* Handle absolute paths */
252 name = (char *) g_path_skip_root (name);
253 dir = dir->inode->archive->root_entry;
256 pent = dir;
257 p = name;
258 name_end = name + strlen (name);
260 q = strchr (p, PATH_SEP);
261 if (q == '\0')
262 q = strchr (p, '\0');
264 while ((pent != NULL) && (c != '\0') && (*p != '\0'))
266 c = *q;
267 *q = '\0';
269 if (strcmp (p, ".") != 0)
271 if (strcmp (p, "..") == 0)
272 pent = pent->dir;
273 else
275 pent = extfs_resolve_symlinks_int (pent, list);
276 if (pent == NULL)
278 *q = c;
279 return NULL;
281 if (!S_ISDIR (pent->inode->mode))
283 *q = c;
284 notadir = TRUE;
285 return NULL;
288 pdir = pent;
289 for (pent = pent->inode->first_in_subdir; pent != NULL; pent = pent->next_in_dir)
290 /* Hack: I keep the original semanthic unless
291 q+1 would break in the strchr */
292 if (strcmp (pent->name, p) == 0)
294 if (q + 1 > name_end)
296 *q = c;
297 notadir = !S_ISDIR (pent->inode->mode);
298 return pent;
300 break;
303 /* When we load archive, we create automagically
304 * non-existant directories
306 if (pent == NULL && make_dirs)
307 pent = extfs_generate_entry (dir->inode->archive, p, pdir, S_IFDIR | 0777);
308 if (pent == NULL && make_file)
309 pent = extfs_generate_entry (dir->inode->archive, p, pdir, S_IFREG | 0666);
312 /* Next iteration */
313 *q = c;
314 p = q + 1;
315 q = strchr (p, PATH_SEP);
316 if (q == '\0')
317 q = strchr (p, '\0');
319 if (pent == NULL)
320 my_errno = ENOENT;
321 return pent;
324 /* --------------------------------------------------------------------------------------------- */
326 static struct entry *
327 extfs_find_entry (struct entry *dir, const char *name, gboolean make_dirs, gboolean make_file)
329 struct entry *res;
331 errloop = FALSE;
332 notadir = FALSE;
334 res = extfs_find_entry_int (dir, name, NULL, make_dirs, make_file);
335 if (res == NULL)
337 if (errloop)
338 my_errno = ELOOP;
339 else if (notadir)
340 my_errno = ENOTDIR;
342 return res;
345 /* --------------------------------------------------------------------------------------------- */
347 static void
348 extfs_fill_names (struct vfs_class *me, fill_names_f func)
350 struct archive *a = first_archive;
352 (void) me;
354 while (a != NULL)
356 extfs_plugin_info_t *info;
357 char *name;
359 info = &g_array_index (extfs_plugins, extfs_plugin_info_t, a->fstype);
360 name =
361 g_strconcat (a->name ? a->name : "", "/", info->prefix, VFS_PATH_URL_DELIMITER,
362 (char *) NULL);
363 func (name);
364 g_free (name);
365 a = a->next;
369 /* --------------------------------------------------------------------------------------------- */
371 static void
372 extfs_free_archive (struct archive *archive)
374 extfs_free_entry (archive->root_entry);
375 if (archive->local_name != NULL)
377 struct stat my;
378 vfs_path_t *local_name_vpath, *name_vpath;
380 local_name_vpath = vfs_path_from_str (archive->local_name);
381 name_vpath = vfs_path_from_str (archive->local_name);
382 mc_stat (local_name_vpath, &my);
383 mc_ungetlocalcopy (name_vpath, local_name_vpath,
384 archive->local_stat.st_mtime != my.st_mtime);
385 vfs_path_free (local_name_vpath);
386 vfs_path_free (name_vpath);
387 g_free (archive->local_name);
389 g_free (archive->name);
390 g_free (archive);
393 /* --------------------------------------------------------------------------------------------- */
395 static FILE *
396 extfs_open_archive (int fstype, const char *name, struct archive **pparc)
398 const extfs_plugin_info_t *info;
399 static dev_t archive_counter = 0;
400 FILE *result = NULL;
401 mode_t mode;
402 char *cmd;
403 struct stat mystat;
404 struct archive *current_archive;
405 struct entry *root_entry;
406 char *tmp = NULL;
407 vfs_path_t *local_name_vpath = NULL;
408 vfs_path_t *name_vpath;
410 name_vpath = vfs_path_from_str (name);
411 info = &g_array_index (extfs_plugins, extfs_plugin_info_t, fstype);
413 if (info->need_archive)
415 if (mc_stat (name_vpath, &mystat) == -1)
416 goto ret;
418 if (!vfs_file_is_local (name_vpath))
420 local_name_vpath = mc_getlocalcopy (name_vpath);
421 if (local_name_vpath == NULL)
422 goto ret;
425 tmp = name_quote ( vfs_path_get_last_path_str (name_vpath), 0);
428 cmd = g_strconcat (info->path, info->prefix, " list ",
429 vfs_path_get_last_path_str (local_name_vpath) != NULL ?
430 vfs_path_get_last_path_str (local_name_vpath) : tmp, (char *) NULL);
431 g_free (tmp);
433 open_error_pipe ();
434 result = popen (cmd, "r");
435 g_free (cmd);
436 if (result == NULL)
438 close_error_pipe (D_ERROR, NULL);
439 if (local_name_vpath != NULL)
441 mc_ungetlocalcopy (name_vpath, local_name_vpath, FALSE);
442 vfs_path_free (local_name_vpath);
444 goto ret;
447 #ifdef ___QNXNTO__
448 setvbuf (result, NULL, _IONBF, 0);
449 #endif
451 current_archive = g_new (struct archive, 1);
452 current_archive->fstype = fstype;
453 current_archive->name = (name != NULL) ? g_strdup (name) : NULL;
454 current_archive->local_name = g_strdup (vfs_path_get_last_path_str (local_name_vpath));
456 if (local_name_vpath != NULL)
458 mc_stat (local_name_vpath, &current_archive->local_stat);
459 vfs_path_free (local_name_vpath);
461 current_archive->inode_counter = 0;
462 current_archive->fd_usage = 0;
463 current_archive->rdev = archive_counter++;
464 current_archive->next = first_archive;
465 first_archive = current_archive;
466 mode = mystat.st_mode & 07777;
467 if (mode & 0400)
468 mode |= 0100;
469 if (mode & 0040)
470 mode |= 0010;
471 if (mode & 0004)
472 mode |= 0001;
473 mode |= S_IFDIR;
474 root_entry = extfs_generate_entry (current_archive, PATH_SEP_STR, NULL, mode);
475 root_entry->inode->uid = mystat.st_uid;
476 root_entry->inode->gid = mystat.st_gid;
477 root_entry->inode->atime = mystat.st_atime;
478 root_entry->inode->ctime = mystat.st_ctime;
479 root_entry->inode->mtime = mystat.st_mtime;
480 current_archive->root_entry = root_entry;
482 *pparc = current_archive;
484 ret:
485 vfs_path_free (name_vpath);
486 return result;
489 /* --------------------------------------------------------------------------------------------- */
491 * Main loop for reading an archive.
492 * Return 0 on success, -1 on error.
495 static int
496 extfs_read_archive (int fstype, const char *name, struct archive **pparc)
498 FILE *extfsd;
499 const extfs_plugin_info_t *info;
500 char *buffer;
501 struct archive *current_archive;
502 char *current_file_name, *current_link_name;
504 info = &g_array_index (extfs_plugins, extfs_plugin_info_t, fstype);
506 extfsd = extfs_open_archive (fstype, name, &current_archive);
508 if (extfsd == NULL)
510 message (D_ERROR, MSG_ERROR, _("Cannot open %s archive\n%s"), info->prefix, name);
511 return -1;
514 buffer = g_malloc (BUF_4K);
515 while (fgets (buffer, BUF_4K, extfsd) != NULL)
517 struct stat hstat;
519 current_link_name = NULL;
520 if (vfs_parse_ls_lga (buffer, &hstat, &current_file_name, &current_link_name, NULL))
522 struct entry *entry, *pent;
523 struct inode *inode;
524 char *p, *q, *cfn = current_file_name;
526 if (*cfn != '\0')
528 if (*cfn == PATH_SEP)
529 cfn++;
530 p = strchr (cfn, '\0');
531 if (p != cfn && *(p - 1) == PATH_SEP)
532 *(p - 1) = '\0';
533 p = strrchr (cfn, PATH_SEP);
534 if (p == NULL)
536 p = cfn;
537 q = strchr (cfn, '\0');
539 else
541 *(p++) = '\0';
542 q = cfn;
544 if (S_ISDIR (hstat.st_mode) && (strcmp (p, ".") == 0 || strcmp (p, "..") == 0))
545 goto read_extfs_continue;
546 pent = extfs_find_entry (current_archive->root_entry, q, TRUE, FALSE);
547 if (pent == NULL)
549 /* FIXME: Should clean everything one day */
550 g_free (buffer);
551 pclose (extfsd);
552 close_error_pipe (D_ERROR, _("Inconsistent extfs archive"));
553 return -1;
555 entry = g_new (struct entry, 1);
556 entry->name = g_strdup (p);
557 entry->next_in_dir = NULL;
558 entry->dir = pent;
559 if (pent->inode->last_in_subdir)
561 pent->inode->last_in_subdir->next_in_dir = entry;
562 pent->inode->last_in_subdir = entry;
564 if (!S_ISLNK (hstat.st_mode) && (current_link_name != NULL))
566 pent = extfs_find_entry (current_archive->root_entry,
567 current_link_name, FALSE, FALSE);
568 if (pent == NULL)
570 /* FIXME: Should clean everything one day */
571 g_free (buffer);
572 pclose (extfsd);
573 close_error_pipe (D_ERROR, _("Inconsistent extfs archive"));
574 return -1;
577 entry->inode = pent->inode;
578 pent->inode->nlink++;
580 else
582 inode = g_new (struct inode, 1);
583 entry->inode = inode;
584 inode->local_filename = NULL;
585 inode->inode = (current_archive->inode_counter)++;
586 inode->nlink = 1;
587 inode->dev = current_archive->rdev;
588 inode->archive = current_archive;
589 inode->mode = hstat.st_mode;
590 #ifdef HAVE_STRUCT_STAT_ST_RDEV
591 inode->rdev = hstat.st_rdev;
592 #else
593 inode->rdev = 0;
594 #endif
595 inode->uid = hstat.st_uid;
596 inode->gid = hstat.st_gid;
597 inode->size = hstat.st_size;
598 inode->mtime = hstat.st_mtime;
599 inode->atime = hstat.st_atime;
600 inode->ctime = hstat.st_ctime;
601 inode->first_in_subdir = NULL;
602 inode->last_in_subdir = NULL;
603 if (current_link_name != NULL && S_ISLNK (hstat.st_mode))
605 inode->linkname = current_link_name;
606 current_link_name = NULL;
608 else
610 if (S_ISLNK (hstat.st_mode))
611 inode->mode &= ~S_IFLNK; /* You *DON'T* want to do this always */
612 inode->linkname = NULL;
614 if (S_ISDIR (hstat.st_mode))
615 extfs_make_dots (entry);
618 read_extfs_continue:
619 g_free (current_file_name);
620 g_free (current_link_name);
623 g_free (buffer);
625 /* Check if extfs 'list' returned 0 */
626 if (pclose (extfsd) != 0)
628 extfs_free (current_archive);
629 close_error_pipe (D_ERROR, _("Inconsistent extfs archive"));
630 return -1;
633 close_error_pipe (D_ERROR, NULL);
634 *pparc = current_archive;
635 return 0;
638 /* --------------------------------------------------------------------------------------------- */
640 static int
641 extfs_which (struct vfs_class *me, const char *path)
643 size_t path_len;
644 size_t i;
646 (void) me;
648 path_len = strlen (path);
650 for (i = 0; i < extfs_plugins->len; i++)
652 extfs_plugin_info_t *info;
654 info = &g_array_index (extfs_plugins, extfs_plugin_info_t, i);
656 if ((strncmp (path, info->prefix, path_len) == 0)
657 && ((info->prefix[path_len] == '\0') || (info->prefix[path_len] == '+')))
658 return i;
660 return -1;
663 /* --------------------------------------------------------------------------------------------- */
665 * Dissect the path and create corresponding superblock.
667 static const char *
668 extfs_get_path_int (const vfs_path_t * vpath, struct archive **archive, gboolean do_not_open)
670 char *archive_name;
671 int result = -1;
672 struct archive *parc;
673 int fstype;
674 vfs_path_element_t *path_element;
676 path_element = vfs_path_get_by_index (vpath, -1);
678 archive_name = vfs_path_to_str_elements_count (vpath, -1);
680 fstype = extfs_which (path_element->class, path_element->vfs_prefix);
682 if (fstype == -1)
684 g_free (archive_name);
685 return NULL;
689 * All filesystems should have some local archive, at least
690 * it can be PATH_SEP ('/').
692 for (parc = first_archive; parc != NULL; parc = parc->next)
693 if (parc->name != NULL)
695 if (strcmp (parc->name, archive_name) == 0)
697 vfs_stamp (&vfs_extfs_ops, (vfsid) parc);
698 goto return_success;
702 result = do_not_open ? -1 : extfs_read_archive (fstype, archive_name, &parc);
703 if (result == -1)
705 path_element->class->verrno = EIO;
706 g_free (archive_name);
707 return NULL;
710 return_success:
711 *archive = parc;
712 g_free (archive_name);
713 return path_element->path;
716 /* --------------------------------------------------------------------------------------------- */
718 * Dissect the path and create corresponding superblock.
719 * The result should be freed.
722 static char *
723 extfs_get_path (const vfs_path_t * vpath, struct archive **archive, gboolean do_not_open)
725 return g_strdup (extfs_get_path_int (vpath, archive, do_not_open));
728 /* --------------------------------------------------------------------------------------------- */
729 /* Return allocated path (without leading slash) inside the archive */
731 static char *
732 extfs_get_path_from_entry (struct entry *entry)
734 GString *localpath;
736 localpath = g_string_new ("");
738 while (entry->dir != NULL)
740 g_string_prepend (localpath, entry->name);
741 if (entry->dir->dir != NULL)
742 g_string_prepend_c (localpath, PATH_SEP);
743 entry = entry->dir;
746 return g_string_free (localpath, FALSE);
749 /* --------------------------------------------------------------------------------------------- */
751 static struct entry *
752 extfs_resolve_symlinks_int (struct entry *entry, GSList * list)
754 struct entry *pent = NULL;
756 if (!S_ISLNK (entry->inode->mode))
757 return entry;
759 if (g_slist_find (list, entry) != NULL)
761 /* Here we protect us against symlink looping */
762 errloop = TRUE;
764 else
766 GSList *looping;
768 looping = g_slist_prepend (list, entry);
769 pent = extfs_find_entry_int (entry->dir, entry->inode->linkname, looping, FALSE, FALSE);
770 looping = g_slist_delete_link (looping, looping);
772 if (pent == NULL)
773 my_errno = ENOENT;
776 return pent;
779 /* --------------------------------------------------------------------------------------------- */
781 static struct entry *
782 extfs_resolve_symlinks (struct entry *entry)
784 struct entry *res;
786 errloop = FALSE;
787 notadir = FALSE;
788 res = extfs_resolve_symlinks_int (entry, NULL);
789 if (res == NULL)
791 if (errloop)
792 my_errno = ELOOP;
793 else if (notadir)
794 my_errno = ENOTDIR;
796 return res;
799 /* --------------------------------------------------------------------------------------------- */
801 static char *
802 extfs_get_archive_name (struct archive *archive)
804 const char *archive_name;
806 if (archive->local_name)
807 archive_name = archive->local_name;
808 else
809 archive_name = archive->name;
811 if (!archive_name || !*archive_name)
812 return g_strdup ("no_archive_name");
813 else
815 char *ret_str;
816 vfs_path_t *vpath = vfs_path_from_str (archive_name);
817 vfs_path_element_t *path_element = vfs_path_get_by_index (vpath, -1);
818 ret_str = g_strdup (path_element->path);
819 vfs_path_free (vpath);
820 return ret_str;
824 /* --------------------------------------------------------------------------------------------- */
825 /** Don't pass localname as NULL */
827 static int
828 extfs_cmd (const char *str_extfs_cmd, struct archive *archive,
829 struct entry *entry, const char *localname)
831 char *file;
832 char *quoted_file;
833 char *quoted_localname;
834 char *archive_name, *quoted_archive_name;
835 const extfs_plugin_info_t *info;
836 char *cmd;
837 int retval;
839 file = extfs_get_path_from_entry (entry);
840 quoted_file = name_quote (file, 0);
841 g_free (file);
843 archive_name = extfs_get_archive_name (archive);
844 quoted_archive_name = name_quote (archive_name, 0);
845 g_free (archive_name);
846 quoted_localname = name_quote (localname, 0);
847 info = &g_array_index (extfs_plugins, extfs_plugin_info_t, archive->fstype);
848 cmd = g_strconcat (info->path, info->prefix, str_extfs_cmd,
849 quoted_archive_name, " ", quoted_file, " ", quoted_localname, (char *) NULL);
850 g_free (quoted_file);
851 g_free (quoted_localname);
852 g_free (quoted_archive_name);
854 open_error_pipe ();
855 retval = my_system (EXECUTE_AS_SHELL, shell, cmd);
856 g_free (cmd);
857 close_error_pipe (D_ERROR, NULL);
858 return retval;
861 /* --------------------------------------------------------------------------------------------- */
863 static void
864 extfs_run (const vfs_path_t * vpath)
866 struct archive *archive = NULL;
867 char *p, *q, *archive_name, *quoted_archive_name;
868 char *cmd;
869 const extfs_plugin_info_t *info;
871 p = extfs_get_path (vpath, &archive, FALSE);
872 if (p == NULL)
873 return;
874 q = name_quote (p, 0);
875 g_free (p);
877 archive_name = extfs_get_archive_name (archive);
878 quoted_archive_name = name_quote (archive_name, 0);
879 g_free (archive_name);
880 info = &g_array_index (extfs_plugins, extfs_plugin_info_t, archive->fstype);
881 cmd =
882 g_strconcat (info->path, info->prefix, " run ", quoted_archive_name, " ", q, (char *) NULL);
883 g_free (quoted_archive_name);
884 g_free (q);
885 shell_execute (cmd, 0);
886 g_free (cmd);
889 /* --------------------------------------------------------------------------------------------- */
891 static void *
892 extfs_open (const vfs_path_t * vpath, int flags, mode_t mode)
894 struct pseudofile *extfs_info;
895 struct archive *archive = NULL;
896 char *q;
897 struct entry *entry;
898 int local_handle;
899 gboolean created = FALSE;
901 q = extfs_get_path (vpath, &archive, FALSE);
902 if (q == NULL)
903 return NULL;
904 entry = extfs_find_entry (archive->root_entry, q, FALSE, FALSE);
905 if ((entry == NULL) && ((flags & O_CREAT) != 0))
907 /* Create new entry */
908 entry = extfs_find_entry (archive->root_entry, q, FALSE, TRUE);
909 created = (entry != NULL);
912 g_free (q);
913 if (entry == NULL)
914 return NULL;
915 entry = extfs_resolve_symlinks (entry);
916 if (entry == NULL)
917 return NULL;
919 if (S_ISDIR (entry->inode->mode))
920 ERRNOR (EISDIR, NULL);
922 if (entry->inode->local_filename == NULL)
924 vfs_path_t *local_filename_vpath;
925 char *local_filename;
927 local_handle = vfs_mkstemps (&local_filename_vpath, "extfs", entry->name);
929 if (local_handle == -1)
930 return NULL;
931 close (local_handle);
932 local_filename = vfs_path_get_by_index (local_filename_vpath, -1)->path;
934 if (!created && ((flags & O_TRUNC) == 0)
935 && extfs_cmd (" copyout ", archive, entry, local_filename))
937 unlink (local_filename);
938 vfs_path_free (local_filename_vpath);
939 my_errno = EIO;
940 return NULL;
942 entry->inode->local_filename = g_strdup (local_filename);
943 vfs_path_free (local_filename_vpath);
946 local_handle = open (entry->inode->local_filename, NO_LINEAR (flags), mode);
948 if (local_handle == -1)
950 /* file exists(may be). Need to drop O_CREAT flag and truncate file content */
951 flags = ~O_CREAT & (NO_LINEAR (flags) | O_TRUNC);
952 local_handle = open (entry->inode->local_filename, flags, mode);
955 if (local_handle == -1)
956 ERRNOR (EIO, NULL);
958 extfs_info = g_new (struct pseudofile, 1);
959 extfs_info->archive = archive;
960 extfs_info->entry = entry;
961 extfs_info->has_changed = created;
962 extfs_info->local_handle = local_handle;
964 /* i.e. we had no open files and now we have one */
965 vfs_rmstamp (&vfs_extfs_ops, (vfsid) archive);
966 archive->fd_usage++;
967 return extfs_info;
970 /* --------------------------------------------------------------------------------------------- */
972 static ssize_t
973 extfs_read (void *data, char *buffer, size_t count)
975 struct pseudofile *file = (struct pseudofile *) data;
977 return read (file->local_handle, buffer, count);
980 /* --------------------------------------------------------------------------------------------- */
982 static int
983 extfs_close (void *data)
985 struct pseudofile *file;
986 int errno_code = 0;
987 file = (struct pseudofile *) data;
989 close (file->local_handle);
991 /* Commit the file if it has changed */
992 if (file->has_changed)
994 struct stat file_status;
996 if (extfs_cmd (" copyin ", file->archive, file->entry, file->entry->inode->local_filename))
997 errno_code = EIO;
999 if (stat (file->entry->inode->local_filename, &file_status) != 0)
1000 errno_code = EIO;
1001 else
1002 file->entry->inode->size = file_status.st_size;
1004 file->entry->inode->mtime = time (NULL);
1007 if (--file->archive->fd_usage == 0)
1008 vfs_stamp_create (&vfs_extfs_ops, file->archive);
1010 g_free (data);
1011 if (errno_code != 0)
1012 ERRNOR (EIO, -1);
1013 return 0;
1016 /* --------------------------------------------------------------------------------------------- */
1018 static int
1019 extfs_errno (struct vfs_class *me)
1021 (void) me;
1022 return my_errno;
1025 /* --------------------------------------------------------------------------------------------- */
1027 static void *
1028 extfs_opendir (const vfs_path_t * vpath)
1030 struct archive *archive = NULL;
1031 char *q;
1032 struct entry *entry;
1033 struct entry **info;
1035 q = extfs_get_path (vpath, &archive, FALSE);
1036 if (q == NULL)
1037 return NULL;
1038 entry = extfs_find_entry (archive->root_entry, q, FALSE, FALSE);
1039 g_free (q);
1040 if (entry == NULL)
1041 return NULL;
1042 entry = extfs_resolve_symlinks (entry);
1043 if (entry == NULL)
1044 return NULL;
1045 if (!S_ISDIR (entry->inode->mode))
1046 ERRNOR (ENOTDIR, NULL);
1048 info = g_new (struct entry *, 2);
1049 info[0] = entry->inode->first_in_subdir;
1050 info[1] = entry->inode->first_in_subdir;
1052 return info;
1055 /* --------------------------------------------------------------------------------------------- */
1057 static void *
1058 extfs_readdir (void *data)
1060 static union vfs_dirent dir;
1061 struct entry **info = (struct entry **) data;
1063 if (*info == NULL)
1064 return NULL;
1066 g_strlcpy (dir.dent.d_name, (*info)->name, MC_MAXPATHLEN);
1068 compute_namelen (&dir.dent);
1069 *info = (*info)->next_in_dir;
1071 return (void *) &dir;
1074 /* --------------------------------------------------------------------------------------------- */
1076 static int
1077 extfs_closedir (void *data)
1079 g_free (data);
1080 return 0;
1083 /* --------------------------------------------------------------------------------------------- */
1085 static void
1086 extfs_stat_move (struct stat *buf, const struct inode *inode)
1088 buf->st_dev = inode->dev;
1089 buf->st_ino = inode->inode;
1090 buf->st_mode = inode->mode;
1091 buf->st_nlink = inode->nlink;
1092 buf->st_uid = inode->uid;
1093 buf->st_gid = inode->gid;
1094 #ifdef HAVE_STRUCT_STAT_ST_RDEV
1095 buf->st_rdev = inode->rdev;
1096 #endif
1097 buf->st_size = inode->size;
1098 #ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
1099 buf->st_blksize = RECORDSIZE;
1100 #endif
1101 #ifdef HAVE_STRUCT_STAT_ST_BLOCKS
1102 buf->st_blocks = (inode->size + RECORDSIZE - 1) / RECORDSIZE;
1103 #endif
1104 buf->st_atime = inode->atime;
1105 buf->st_mtime = inode->mtime;
1106 buf->st_ctime = inode->ctime;
1109 /* --------------------------------------------------------------------------------------------- */
1111 static int
1112 extfs_internal_stat (const vfs_path_t * vpath, struct stat *buf, gboolean resolve)
1114 struct archive *archive;
1115 const char *q;
1116 struct entry *entry;
1117 int result = -1;
1119 q = extfs_get_path_int (vpath, &archive, FALSE);
1120 if (q == NULL)
1121 goto cleanup;
1122 entry = extfs_find_entry (archive->root_entry, q, FALSE, FALSE);
1123 if (entry == NULL)
1124 goto cleanup;
1125 if (resolve)
1127 entry = extfs_resolve_symlinks (entry);
1128 if (entry == NULL)
1129 goto cleanup;
1131 extfs_stat_move (buf, entry->inode);
1132 result = 0;
1133 cleanup:
1134 return result;
1137 /* --------------------------------------------------------------------------------------------- */
1139 static int
1140 extfs_stat (const vfs_path_t * vpath, struct stat *buf)
1142 return extfs_internal_stat (vpath, buf, TRUE);
1145 /* --------------------------------------------------------------------------------------------- */
1147 static int
1148 extfs_lstat (const vfs_path_t * vpath, struct stat *buf)
1150 return extfs_internal_stat (vpath, buf, FALSE);
1153 /* --------------------------------------------------------------------------------------------- */
1155 static int
1156 extfs_fstat (void *data, struct stat *buf)
1158 struct pseudofile *file = (struct pseudofile *) data;
1160 extfs_stat_move (buf, file->entry->inode);
1161 return 0;
1164 /* --------------------------------------------------------------------------------------------- */
1166 static int
1167 extfs_readlink (const vfs_path_t * vpath, char *buf, size_t size)
1169 struct archive *archive;
1170 const char *q;
1171 size_t len;
1172 struct entry *entry;
1173 int result = -1;
1175 q = extfs_get_path_int (vpath, &archive, FALSE);
1176 if (q == NULL)
1177 goto cleanup;
1178 entry = extfs_find_entry (archive->root_entry, q, FALSE, FALSE);
1179 if (entry == NULL)
1180 goto cleanup;
1181 if (!S_ISLNK (entry->inode->mode))
1183 vfs_path_element_t *path_element;
1184 path_element = vfs_path_get_by_index (vpath, -1);
1185 path_element->class->verrno = EINVAL;
1186 goto cleanup;
1188 len = strlen (entry->inode->linkname);
1189 if (size < len)
1190 len = size;
1191 /* readlink() does not append a NUL character to buf */
1192 result = len;
1193 memcpy (buf, entry->inode->linkname, result);
1194 cleanup:
1195 return result;
1198 /* --------------------------------------------------------------------------------------------- */
1200 static int
1201 extfs_chown (const vfs_path_t * vpath, uid_t owner, gid_t group)
1203 (void) vpath;
1204 (void) owner;
1205 (void) group;
1206 return 0;
1209 /* --------------------------------------------------------------------------------------------- */
1211 static int
1212 extfs_chmod (const vfs_path_t * vpath, mode_t mode)
1214 (void) vpath;
1215 (void) mode;
1216 return 0;
1219 /* --------------------------------------------------------------------------------------------- */
1221 static ssize_t
1222 extfs_write (void *data, const char *buf, size_t nbyte)
1224 struct pseudofile *file = (struct pseudofile *) data;
1226 file->has_changed = TRUE;
1227 return write (file->local_handle, buf, nbyte);
1230 /* --------------------------------------------------------------------------------------------- */
1232 static int
1233 extfs_unlink (const vfs_path_t * vpath)
1235 struct archive *archive;
1236 const char *q;
1237 struct entry *entry;
1238 int result = -1;
1240 q = extfs_get_path_int (vpath, &archive, FALSE);
1241 if (q == NULL)
1242 goto cleanup;
1243 entry = extfs_find_entry (archive->root_entry, q, FALSE, FALSE);
1244 if (entry == NULL)
1245 goto cleanup;
1246 entry = extfs_resolve_symlinks (entry);
1247 if (entry == NULL)
1248 goto cleanup;
1249 if (S_ISDIR (entry->inode->mode))
1251 vfs_path_element_t *path_element = vfs_path_get_by_index (vpath, -1);
1252 path_element->class->verrno = EISDIR;
1253 goto cleanup;
1255 if (extfs_cmd (" rm ", archive, entry, ""))
1257 my_errno = EIO;
1258 goto cleanup;
1260 extfs_remove_entry (entry);
1261 result = 0;
1262 cleanup:
1263 return result;
1266 /* --------------------------------------------------------------------------------------------- */
1268 static int
1269 extfs_mkdir (const vfs_path_t * vpath, mode_t mode)
1271 struct archive *archive;
1272 const char *q;
1273 struct entry *entry;
1274 int result = -1;
1275 vfs_path_element_t *path_element = vfs_path_get_by_index (vpath, -1);
1277 (void) mode;
1279 q = extfs_get_path_int (vpath, &archive, FALSE);
1280 if (q == NULL)
1281 goto cleanup;
1282 entry = extfs_find_entry (archive->root_entry, q, FALSE, FALSE);
1283 if (entry != NULL)
1285 path_element->class->verrno = EEXIST;
1286 goto cleanup;
1288 entry = extfs_find_entry (archive->root_entry, q, TRUE, FALSE);
1289 if (entry == NULL)
1290 goto cleanup;
1291 entry = extfs_resolve_symlinks (entry);
1292 if (entry == NULL)
1293 goto cleanup;
1294 if (!S_ISDIR (entry->inode->mode))
1296 path_element->class->verrno = ENOTDIR;
1297 goto cleanup;
1300 if (extfs_cmd (" mkdir ", archive, entry, ""))
1302 my_errno = EIO;
1303 extfs_remove_entry (entry);
1304 goto cleanup;
1306 result = 0;
1307 cleanup:
1308 return result;
1311 /* --------------------------------------------------------------------------------------------- */
1313 static int
1314 extfs_rmdir (const vfs_path_t * vpath)
1316 struct archive *archive;
1317 const char *q;
1318 struct entry *entry;
1319 int result = -1;
1321 q = extfs_get_path_int (vpath, &archive, FALSE);
1322 if (q == NULL)
1323 goto cleanup;
1324 entry = extfs_find_entry (archive->root_entry, q, FALSE, FALSE);
1325 if (entry == NULL)
1326 goto cleanup;
1327 entry = extfs_resolve_symlinks (entry);
1328 if (entry == NULL)
1329 goto cleanup;
1330 if (!S_ISDIR (entry->inode->mode))
1332 vfs_path_element_t *path_element = vfs_path_get_by_index (vpath, -1);
1333 path_element->class->verrno = ENOTDIR;
1334 goto cleanup;
1337 if (extfs_cmd (" rmdir ", archive, entry, ""))
1339 my_errno = EIO;
1340 goto cleanup;
1342 extfs_remove_entry (entry);
1343 result = 0;
1344 cleanup:
1345 return result;
1348 /* --------------------------------------------------------------------------------------------- */
1350 static int
1351 extfs_chdir (const vfs_path_t * vpath)
1353 struct archive *archive = NULL;
1354 char *q;
1355 struct entry *entry;
1357 my_errno = ENOTDIR;
1358 q = extfs_get_path (vpath, &archive, FALSE);
1359 if (q == NULL)
1360 return -1;
1361 entry = extfs_find_entry (archive->root_entry, q, FALSE, FALSE);
1362 g_free (q);
1363 if (entry == NULL)
1364 return -1;
1365 entry = extfs_resolve_symlinks (entry);
1366 if ((entry == NULL) || (!S_ISDIR (entry->inode->mode)))
1367 return -1;
1368 my_errno = 0;
1369 return 0;
1372 /* --------------------------------------------------------------------------------------------- */
1374 static off_t
1375 extfs_lseek (void *data, off_t offset, int whence)
1377 struct pseudofile *file = (struct pseudofile *) data;
1379 return lseek (file->local_handle, offset, whence);
1382 /* --------------------------------------------------------------------------------------------- */
1384 static vfsid
1385 extfs_getid (const vfs_path_t * vpath)
1387 struct archive *archive = NULL;
1388 char *p;
1390 p = extfs_get_path (vpath, &archive, TRUE);
1391 if (p == NULL)
1392 return NULL;
1393 g_free (p);
1394 return (vfsid) archive;
1397 /* --------------------------------------------------------------------------------------------- */
1399 static int
1400 extfs_nothingisopen (vfsid id)
1402 return (((struct archive *) id)->fd_usage <= 0);
1405 /* --------------------------------------------------------------------------------------------- */
1407 static void
1408 extfs_remove_entry (struct entry *e)
1410 int i = --e->inode->nlink;
1411 struct entry *pe, *ent, *prev;
1413 if (S_ISDIR (e->inode->mode) && e->inode->first_in_subdir != NULL)
1415 struct entry *f = e->inode->first_in_subdir;
1416 e->inode->first_in_subdir = NULL;
1417 extfs_remove_entry (f);
1419 pe = e->dir;
1420 if (e == pe->inode->first_in_subdir)
1421 pe->inode->first_in_subdir = e->next_in_dir;
1423 prev = NULL;
1424 for (ent = pe->inode->first_in_subdir; ent && ent->next_in_dir; ent = ent->next_in_dir)
1425 if (e == ent->next_in_dir)
1427 prev = ent;
1428 break;
1430 if (prev)
1431 prev->next_in_dir = e->next_in_dir;
1432 if (e == pe->inode->last_in_subdir)
1433 pe->inode->last_in_subdir = prev;
1435 if (i <= 0)
1437 if (e->inode->local_filename != NULL)
1439 unlink (e->inode->local_filename);
1440 g_free (e->inode->local_filename);
1442 g_free (e->inode->linkname);
1443 g_free (e->inode);
1446 g_free (e->name);
1447 g_free (e);
1450 /* --------------------------------------------------------------------------------------------- */
1452 static void
1453 extfs_free_entry (struct entry *e)
1455 int i = --e->inode->nlink;
1457 if (S_ISDIR (e->inode->mode) && e->inode->first_in_subdir != NULL)
1459 struct entry *f = e->inode->first_in_subdir;
1461 e->inode->first_in_subdir = NULL;
1462 extfs_free_entry (f);
1464 if (i <= 0)
1466 if (e->inode->local_filename != NULL)
1468 unlink (e->inode->local_filename);
1469 g_free (e->inode->local_filename);
1471 g_free (e->inode->linkname);
1472 g_free (e->inode);
1474 if (e->next_in_dir != NULL)
1475 extfs_free_entry (e->next_in_dir);
1476 g_free (e->name);
1477 g_free (e);
1480 /* --------------------------------------------------------------------------------------------- */
1482 static void
1483 extfs_free (vfsid id)
1485 struct archive *archive = (struct archive *) id;
1487 if (archive == first_archive)
1489 first_archive = archive->next;
1491 else
1493 struct archive *parc;
1494 for (parc = first_archive; parc != NULL; parc = parc->next)
1495 if (parc->next == archive)
1497 parc->next = archive->next;
1498 break;
1501 extfs_free_archive (archive);
1504 /* --------------------------------------------------------------------------------------------- */
1506 static vfs_path_t *
1507 extfs_getlocalcopy (const vfs_path_t * vpath)
1509 struct pseudofile *fp;
1510 vfs_path_t *p;
1512 fp = (struct pseudofile *) extfs_open (vpath, O_RDONLY, 0);
1513 if (fp == NULL)
1514 return NULL;
1515 if (fp->entry->inode->local_filename == NULL)
1517 extfs_close ((void *) fp);
1518 return NULL;
1520 p = vfs_path_from_str (fp->entry->inode->local_filename);
1521 fp->archive->fd_usage++;
1522 extfs_close ((void *) fp);
1523 return p;
1526 /* --------------------------------------------------------------------------------------------- */
1528 static int
1529 extfs_ungetlocalcopy (const vfs_path_t * vpath, const vfs_path_t * local, gboolean has_changed)
1531 struct pseudofile *fp;
1533 fp = (struct pseudofile *) extfs_open (vpath, O_RDONLY, 0);
1534 if (fp == NULL)
1535 return 0;
1537 if (strcmp (fp->entry->inode->local_filename, vfs_path_get_last_path_str (local)) == 0)
1539 fp->archive->fd_usage--;
1540 if (has_changed)
1541 fp->has_changed = TRUE;
1542 extfs_close ((void *) fp);
1543 return 0;
1545 else
1547 /* Should not happen */
1548 extfs_close ((void *) fp);
1549 return 0;
1553 /* --------------------------------------------------------------------------------------------- */
1555 static gboolean
1556 extfs_get_plugins (const char *where, gboolean silent)
1558 char *dirname;
1559 GDir *dir;
1560 const char *filename;
1562 dirname = g_build_path (PATH_SEP_STR, where, MC_EXTFS_DIR, (char *) NULL);
1563 dir = g_dir_open (dirname, 0, NULL);
1565 /* We may not use vfs_die() message or message or similar,
1566 * UI is not initialized at this time and message would not
1567 * appear on screen. */
1568 if (dir == NULL)
1570 if (!silent)
1571 fprintf (stderr, _("Warning: cannot open %s directory\n"), dirname);
1572 g_free (dirname);
1573 return FALSE;
1576 if (extfs_plugins == NULL)
1577 extfs_plugins = g_array_sized_new (FALSE, TRUE, sizeof (extfs_plugin_info_t), 32);
1579 while ((filename = g_dir_read_name (dir)) != NULL)
1581 char fullname[MC_MAXPATHLEN];
1582 struct stat s;
1584 g_snprintf (fullname, sizeof (fullname), "%s" PATH_SEP_STR "%s", dirname, filename);
1586 if ((stat (fullname, &s) == 0)
1587 && S_ISREG (s.st_mode) && !S_ISDIR (s.st_mode)
1588 && (((s.st_mode & S_IXOTH) != 0) ||
1589 ((s.st_mode & S_IXUSR) != 0) || ((s.st_mode & S_IXGRP) != 0)))
1591 int f;
1593 f = open (fullname, O_RDONLY);
1595 if (f > 0)
1597 size_t len, i;
1598 extfs_plugin_info_t info;
1599 gboolean found = FALSE;
1601 close (f);
1603 /* Handle those with a trailing '+', those flag that the
1604 * file system does not require an archive to work
1606 len = strlen (filename);
1607 info.need_archive = (filename[len - 1] != '+');
1608 info.path = g_strconcat (dirname, PATH_SEP_STR, (char *) NULL);
1609 info.prefix = g_strdup (filename);
1611 /* prepare to compare file names without trailing '+' */
1612 if (!info.need_archive)
1613 info.prefix[len - 1] = '\0';
1615 /* don't overload already found plugin */
1616 for (i = 0; i < extfs_plugins->len; i++)
1618 extfs_plugin_info_t *p;
1620 p = &g_array_index (extfs_plugins, extfs_plugin_info_t, i);
1622 /* 2 files with same names cannot be in a directory */
1623 if ((strcmp (info.path, p->path) != 0)
1624 && (strcmp (info.prefix, p->prefix) == 0))
1626 found = TRUE;
1627 break;
1631 if (found)
1633 g_free (info.path);
1634 g_free (info.prefix);
1636 else
1638 /* restore file name */
1639 if (!info.need_archive)
1640 info.prefix[len - 1] = '+';
1641 g_array_append_val (extfs_plugins, info);
1647 g_dir_close (dir);
1648 g_free (dirname);
1650 return TRUE;
1653 /* --------------------------------------------------------------------------------------------- */
1655 static int
1656 extfs_init (struct vfs_class *me)
1658 gboolean d1, d2;
1660 (void) me;
1662 /* 1st: scan user directory */
1663 d1 = extfs_get_plugins (mc_config_get_data_path (), TRUE); /* silent about user dir */
1664 /* 2nd: scan system dir */
1665 d2 = extfs_get_plugins (LIBEXECDIR, d1);
1667 return (d1 || d2 ? 1 : 0);
1670 /* --------------------------------------------------------------------------------------------- */
1672 static void
1673 extfs_done (struct vfs_class *me)
1675 size_t i;
1676 struct archive *ar;
1678 (void) me;
1680 for (ar = first_archive; ar != NULL;)
1682 extfs_free ((vfsid) ar);
1683 ar = first_archive;
1686 for (i = 0; i < extfs_plugins->len; i++)
1688 extfs_plugin_info_t *info;
1690 info = &g_array_index (extfs_plugins, extfs_plugin_info_t, i);
1691 g_free (info->path);
1692 g_free (info->prefix);
1695 if (extfs_plugins != NULL)
1696 g_array_free (extfs_plugins, TRUE);
1699 /* --------------------------------------------------------------------------------------------- */
1701 static int
1702 extfs_setctl (const vfs_path_t * vpath, int ctlop, void *arg)
1704 (void) arg;
1706 if (ctlop == VFS_SETCTL_RUN)
1708 extfs_run (vpath);
1709 return 1;
1711 return 0;
1714 /* --------------------------------------------------------------------------------------------- */
1715 /*** public functions ****************************************************************************/
1716 /* --------------------------------------------------------------------------------------------- */
1718 void
1719 init_extfs (void)
1721 vfs_extfs_ops.name = "extfs";
1722 vfs_extfs_ops.init = extfs_init;
1723 vfs_extfs_ops.done = extfs_done;
1724 vfs_extfs_ops.fill_names = extfs_fill_names;
1725 vfs_extfs_ops.which = extfs_which;
1726 vfs_extfs_ops.open = extfs_open;
1727 vfs_extfs_ops.close = extfs_close;
1728 vfs_extfs_ops.read = extfs_read;
1729 vfs_extfs_ops.write = extfs_write;
1730 vfs_extfs_ops.opendir = extfs_opendir;
1731 vfs_extfs_ops.readdir = extfs_readdir;
1732 vfs_extfs_ops.closedir = extfs_closedir;
1733 vfs_extfs_ops.stat = extfs_stat;
1734 vfs_extfs_ops.lstat = extfs_lstat;
1735 vfs_extfs_ops.fstat = extfs_fstat;
1736 vfs_extfs_ops.chmod = extfs_chmod;
1737 vfs_extfs_ops.chown = extfs_chown;
1738 vfs_extfs_ops.readlink = extfs_readlink;
1739 vfs_extfs_ops.unlink = extfs_unlink;
1740 vfs_extfs_ops.chdir = extfs_chdir;
1741 vfs_extfs_ops.ferrno = extfs_errno;
1742 vfs_extfs_ops.lseek = extfs_lseek;
1743 vfs_extfs_ops.getid = extfs_getid;
1744 vfs_extfs_ops.nothingisopen = extfs_nothingisopen;
1745 vfs_extfs_ops.free = extfs_free;
1746 vfs_extfs_ops.getlocalcopy = extfs_getlocalcopy;
1747 vfs_extfs_ops.ungetlocalcopy = extfs_ungetlocalcopy;
1748 vfs_extfs_ops.mkdir = extfs_mkdir;
1749 vfs_extfs_ops.rmdir = extfs_rmdir;
1750 vfs_extfs_ops.setctl = extfs_setctl;
1751 vfs_register_class (&vfs_extfs_ops);
1754 /* --------------------------------------------------------------------------------------------- */