mc/vfs/extfs.c

1412 lines
35 KiB
C
Raw Normal View History

1998-02-27 07:54:42 +03:00
/* Virtual File System: External file system.
Copyright (C) 1995 The Free Software Foundation
Written by: 1995 Jakub Jelinek
Rewritten by: 1998 Pavel Machek
Additional changes by: 1999 Andrew T. Veliath
1998-02-27 07:54:42 +03:00
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public License
as published by the Free Software Foundation; either version 2 of
the License, or (at your option) any later version.
1998-02-27 07:54:42 +03:00
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Library General Public License for more details.
1998-02-27 07:54:42 +03:00
You should have received a copy of the GNU Library General Public
License along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
1998-02-27 07:54:42 +03:00
/* Namespace: init_extfs */
1998-10-13 02:07:53 +04:00
1998-02-27 07:54:42 +03:00
#include <config.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <signal.h>
#ifdef HAVE_SYS_WAIT_H
#include <sys/wait.h>
#endif
#include <errno.h>
#include "../src/global.h"
#include "../src/tty.h" /* enable/disable interrupt key */
#include "../src/wtools.h" /* message() */
#include "../src/main.h" /* print_vfs_message */
#include "utilvfs.h"
2003-06-22 13:18:12 +04:00
#include "../src/execute.h" /* For shell_execute */
1998-02-27 07:54:42 +03:00
#include "vfs.h"
#include "vfs-impl.h"
#include "gc.h" /* vfs_rmstamp */
1998-02-27 07:54:42 +03:00
#undef ERRNOR
#define ERRNOR(x,y) do { my_errno = x; return y; } while(0)
struct inode {
nlink_t nlink;
struct entry *first_in_subdir; /* only used if this is a directory */
struct entry *last_in_subdir;
ino_t inode; /* This is inode # */
dev_t dev; /* This is an internal identification of the extfs archive */
struct archive *archive; /* And this is an archive structure */
dev_t rdev;
mode_t mode;
uid_t uid;
gid_t gid;
off_t size;
time_t mtime;
char *linkname;
time_t atime;
time_t ctime;
char *local_filename;
};
struct entry {
struct entry *next_in_dir;
struct entry *dir;
char *name;
struct inode *inode;
};
struct pseudofile {
struct archive *archive;
unsigned int has_changed:1;
int local_handle;
struct entry *entry;
};
struct archive {
int fstype;
char *name;
char *local_name;
struct stat local_stat;
dev_t rdev;
int fd_usage;
ino_t inode_counter;
struct entry *root_entry;
struct archive *next;
};
static struct entry *extfs_find_entry (struct entry *dir, char *name,
int make_dirs, int make_file);
static int extfs_which (struct vfs_class *me, const char *path);
static void extfs_remove_entry (struct entry *e);
static void extfs_free (vfsid id);
static struct vfs_class vfs_extfs_ops;
static struct archive *first_archive = NULL;
static int my_errno = 0;
1998-02-27 07:54:42 +03:00
#define MAXEXTFS 32
static char *extfs_prefixes [MAXEXTFS];
static char extfs_need_archive [MAXEXTFS];
static int extfs_no = 0;
static void
extfs_fill_names (struct vfs_class *me, fill_names_f func)
1998-02-27 07:54:42 +03:00
{
struct archive *a = first_archive;
1998-02-27 07:54:42 +03:00
char *name;
(void) me;
while (a) {
name =
g_strconcat (a->name ? a->name : "", "#",
extfs_prefixes[a->fstype], (char *) NULL);
(*func) (name);
g_free (name);
1998-02-27 07:54:42 +03:00
a = a->next;
}
}
static void extfs_make_dots (struct entry *ent)
1998-02-27 07:54:42 +03:00
{
struct entry *entry = g_new (struct entry, 1);
struct entry *parentry = ent->dir;
struct inode *inode = ent->inode, *parent;
1998-02-27 07:54:42 +03:00
parent = (parentry != NULL) ? parentry->inode : NULL;
entry->name = g_strdup (".");
1998-02-27 07:54:42 +03:00
entry->inode = inode;
entry->dir = ent;
inode->local_filename = NULL;
inode->first_in_subdir = entry;
inode->nlink++;
entry->next_in_dir = g_new (struct entry, 1);
entry = entry->next_in_dir;
entry->name = g_strdup ("..");
1998-02-27 07:54:42 +03:00
inode->last_in_subdir = entry;
entry->next_in_dir = NULL;
if (parent != NULL) {
entry->inode = parent;
entry->dir = parentry;
parent->nlink++;
} else {
entry->inode = inode;
entry->dir = ent;
inode->nlink++;
}
}
static struct entry *extfs_generate_entry (struct archive *archive,
const char *name, struct entry *parentry, mode_t mode)
1998-02-27 07:54:42 +03:00
{
mode_t myumask;
struct inode *inode, *parent;
struct entry *entry;
1998-02-27 07:54:42 +03:00
parent = (parentry != NULL) ? parentry->inode : NULL;
entry = g_new (struct entry, 1);
1998-02-27 07:54:42 +03:00
entry->name = g_strdup (name);
1998-02-27 07:54:42 +03:00
entry->next_in_dir = NULL;
entry->dir = parentry;
if (parent != NULL) {
parent->last_in_subdir->next_in_dir = entry;
parent->last_in_subdir = entry;
}
inode = g_new (struct inode, 1);
1998-02-27 07:54:42 +03:00
entry->inode = inode;
inode->local_filename = NULL;
inode->linkname = NULL;
inode->last_in_subdir = NULL;
inode->inode = (archive->inode_counter)++;
1998-02-27 07:54:42 +03:00
inode->dev = archive->rdev;
inode->archive = archive;
myumask = umask (022);
umask (myumask);
inode->mode = mode & ~myumask;
mode = inode->mode;
inode->rdev = 0;
inode->uid = getuid ();
inode->gid = getgid ();
inode->size = 0;
inode->mtime = time (NULL);
inode->atime = inode->mtime;
inode->ctime = inode->mtime;
inode->nlink = 1;
if (S_ISDIR (mode))
extfs_make_dots (entry);
1998-02-27 07:54:42 +03:00
return entry;
}
static void extfs_free_entries (struct entry *entry)
1998-02-27 07:54:42 +03:00
{
(void) entry;
1998-02-27 07:54:42 +03:00
return;
}
static void extfs_free_archive (struct archive *archive)
1998-02-27 07:54:42 +03:00
{
extfs_free_entries (archive->root_entry);
1998-02-27 07:54:42 +03:00
if (archive->local_name != NULL) {
struct stat my;
mc_stat (archive->local_name, &my);
mc_ungetlocalcopy (archive->name, archive->local_name,
archive->local_stat.st_mtime != my.st_mtime);
g_free(archive->local_name);
1998-02-27 07:54:42 +03:00
}
g_free (archive->name);
g_free (archive);
1998-02-27 07:54:42 +03:00
}
static FILE *
extfs_open_archive (int fstype, const char *name, struct archive **pparc)
1998-02-27 07:54:42 +03:00
{
static dev_t archive_counter = 0;
1998-02-27 07:54:42 +03:00
FILE *result;
mode_t mode;
char *cmd;
char *mc_extfsdir;
1998-02-27 07:54:42 +03:00
struct stat mystat;
struct archive *current_archive;
struct entry *root_entry;
1998-02-27 07:54:42 +03:00
char *local_name = NULL, *tmp = 0;
int uses_archive = extfs_need_archive[fstype];
if (uses_archive) {
1998-02-27 07:54:42 +03:00
if (mc_stat (name, &mystat) == -1)
return NULL;
if (!vfs_file_is_local (name)) {
local_name = mc_getlocalcopy (name);
if (local_name == NULL)
return NULL;
}
tmp = name_quote (name, 0);
1998-02-27 07:54:42 +03:00
}
mc_extfsdir = concat_dir_and_file (mc_home, "extfs" PATH_SEP_STR);
cmd =
g_strconcat (mc_extfsdir, extfs_prefixes[fstype], " list ",
local_name ? local_name : tmp, (char *) NULL);
g_free (tmp);
2000-01-13 18:15:42 +03:00
g_free (mc_extfsdir);
open_error_pipe ();
1998-02-27 07:54:42 +03:00
result = popen (cmd, "r");
g_free (cmd);
1998-02-27 07:54:42 +03:00
if (result == NULL) {
close_error_pipe (1, NULL);
if (local_name) {
mc_ungetlocalcopy (name, local_name, 0);
g_free(local_name);
}
return NULL;
}
#ifdef ___QNXNTO__
setvbuf (result, NULL, _IONBF, 0);
#endif
current_archive = g_new (struct archive, 1);
1998-02-27 07:54:42 +03:00
current_archive->fstype = fstype;
current_archive->name = name ? g_strdup (name) : NULL;
1998-02-27 07:54:42 +03:00
current_archive->local_name = local_name;
1998-02-27 07:54:42 +03:00
if (local_name != NULL)
mc_stat (local_name, &current_archive->local_stat);
current_archive->inode_counter = 0;
1998-02-27 07:54:42 +03:00
current_archive->fd_usage = 0;
current_archive->rdev = archive_counter++;
1998-02-27 07:54:42 +03:00
current_archive->next = first_archive;
first_archive = current_archive;
mode = mystat.st_mode & 07777;
1998-02-27 07:54:42 +03:00
if (mode & 0400)
mode |= 0100;
1998-02-27 07:54:42 +03:00
if (mode & 0040)
mode |= 0010;
1998-02-27 07:54:42 +03:00
if (mode & 0004)
mode |= 0001;
1998-02-27 07:54:42 +03:00
mode |= S_IFDIR;
root_entry = extfs_generate_entry (current_archive, "/", NULL, mode);
root_entry->inode->uid = mystat.st_uid;
root_entry->inode->gid = mystat.st_gid;
root_entry->inode->atime = mystat.st_atime;
root_entry->inode->ctime = mystat.st_ctime;
root_entry->inode->mtime = mystat.st_mtime;
1998-02-27 07:54:42 +03:00
current_archive->root_entry = root_entry;
1998-02-27 07:54:42 +03:00
*pparc = current_archive;
return result;
}
/*
* Main loop for reading an archive.
* Return 0 on success, -1 on error.
1998-02-27 07:54:42 +03:00
*/
static int
extfs_read_archive (int fstype, const char *name, struct archive **pparc)
1998-02-27 07:54:42 +03:00
{
FILE *extfsd;
char *buffer;
struct archive *current_archive;
char *current_file_name, *current_link_name;
if ((extfsd =
extfs_open_archive (fstype, name, &current_archive)) == NULL) {
message (1, MSG_ERROR, _("Cannot open %s archive\n%s"),
extfs_prefixes[fstype], name);
1998-02-27 07:54:42 +03:00
return -1;
}
buffer = g_malloc (4096);
1998-02-27 07:54:42 +03:00
while (fgets (buffer, 4096, extfsd) != NULL) {
struct stat hstat;
current_link_name = NULL;
if (vfs_parse_ls_lga
(buffer, &hstat, &current_file_name, &current_link_name)) {
struct entry *entry, *pent;
struct inode *inode;
char *p, *q, *cfn = current_file_name;
if (*cfn) {
if (*cfn == '/')
cfn++;
p = strchr (cfn, 0);
if (p != cfn && *(p - 1) == '/')
*(p - 1) = 0;
p = strrchr (cfn, '/');
if (p == NULL) {
p = cfn;
q = strchr (cfn, 0);
} else {
*(p++) = 0;
q = cfn;
}
if (S_ISDIR (hstat.st_mode)
&& (!strcmp (p, ".") || !strcmp (p, "..")))
goto read_extfs_continue;
pent =
extfs_find_entry (current_archive->root_entry, q, 1,
0);
if (pent == NULL) {
/* FIXME: Should clean everything one day */
g_free (buffer);
1998-02-27 07:54:42 +03:00
pclose (extfsd);
close_error_pipe (1, _("Inconsistent extfs archive"));
return -1;
}
entry = g_new (struct entry, 1);
entry->name = g_strdup (p);
1998-02-27 07:54:42 +03:00
entry->next_in_dir = NULL;
entry->dir = pent;
if (pent->inode->last_in_subdir) {
pent->inode->last_in_subdir->next_in_dir = entry;
pent->inode->last_in_subdir = entry;
1998-02-27 07:54:42 +03:00
}
if (!S_ISLNK (hstat.st_mode) && current_link_name != NULL) {
pent =
extfs_find_entry (current_archive->root_entry,
current_link_name, 0, 0);
if (pent == NULL) {
/* FIXME: Should clean everything one day */
g_free (buffer);
1998-02-27 07:54:42 +03:00
pclose (extfsd);
close_error_pipe (1,
_("Inconsistent extfs archive"));
return -1;
} else {
1998-02-27 07:54:42 +03:00
entry->inode = pent->inode;
pent->inode->nlink++;
}
} else {
inode = g_new (struct inode, 1);
1998-02-27 07:54:42 +03:00
entry->inode = inode;
inode->local_filename = NULL;
inode->inode = (current_archive->inode_counter)++;
1998-02-27 07:54:42 +03:00
inode->nlink = 1;
inode->dev = current_archive->rdev;
inode->archive = current_archive;
inode->mode = hstat.st_mode;
#ifdef HAVE_STRUCT_STAT_ST_RDEV
1998-02-27 07:54:42 +03:00
inode->rdev = hstat.st_rdev;
#else
inode->rdev = 0;
#endif
1998-02-27 07:54:42 +03:00
inode->uid = hstat.st_uid;
inode->gid = hstat.st_gid;
inode->size = hstat.st_size;
inode->mtime = hstat.st_mtime;
inode->atime = hstat.st_atime;
inode->ctime = hstat.st_ctime;
inode->first_in_subdir = NULL;
inode->last_in_subdir = NULL;
if (current_link_name != NULL
&& S_ISLNK (hstat.st_mode)) {
inode->linkname = current_link_name;
current_link_name = NULL;
1998-02-27 07:54:42 +03:00
} else {
if (S_ISLNK (hstat.st_mode))
inode->mode &= ~S_IFLNK; /* You *DON'T* want to do this always */
inode->linkname = NULL;
1998-02-27 07:54:42 +03:00
}
if (S_ISDIR (hstat.st_mode))
extfs_make_dots (entry);
}
}
read_extfs_continue:
g_free (current_file_name);
g_free (current_link_name);
}
1998-02-27 07:54:42 +03:00
}
g_free (buffer);
/* Check if extfs 'list' returned 0 */
if (pclose (extfsd) != 0) {
extfs_free (current_archive);
close_error_pipe (1, _("Inconsistent extfs archive"));
return -1;
}
close_error_pipe (1, NULL);
1998-02-27 07:54:42 +03:00
*pparc = current_archive;
return 0;
}
/*
* Dissect the path and create corresponding superblock. Note that inname
* can be changed and the result may point inside the original string.
*/
static char *
extfs_get_path_mangle (struct vfs_class *me, char *inname, struct archive **archive,
int do_not_open)
1998-02-27 07:54:42 +03:00
{
char *local, *op;
const char *archive_name;
1998-02-27 07:54:42 +03:00
int result = -1;
struct archive *parc;
1998-02-27 07:54:42 +03:00
int fstype;
archive_name = inname;
vfs_split (inname, &local, &op);
fstype = extfs_which (me, op);
if (fstype == -1)
return NULL;
if (!local)
local = inname + strlen (inname);
1998-02-27 07:54:42 +03:00
/*
* All filesystems should have some local archive, at least
* it can be '/'.
1998-02-27 07:54:42 +03:00
*/
for (parc = first_archive; parc != NULL; parc = parc->next)
if (parc->name) {
if (!strcmp (parc->name, archive_name)) {
vfs_stamp (&vfs_extfs_ops, (vfsid) parc);
1998-02-27 07:54:42 +03:00
goto return_success;
}
}
result =
do_not_open ? -1 : extfs_read_archive (fstype, archive_name,
&parc);
if (result == -1)
ERRNOR (EIO, NULL);
return_success:
1998-02-27 07:54:42 +03:00
*archive = parc;
return local;
}
/*
* Dissect the path and create corresponding superblock.
* The result should be freed.
*/
static char *
extfs_get_path (struct vfs_class *me, const char *inname, struct archive **archive,
int do_not_open)
{
char *buf = g_strdup (inname);
char *res = extfs_get_path_mangle (me, buf, archive, do_not_open);
char *res2 = NULL;
if (res)
res2 = g_strdup (res);
g_free (buf);
return res2;
}
/* Return allocated path (without leading slash) inside the archive */
static char *extfs_get_path_from_entry (struct entry *entry)
1998-02-27 07:54:42 +03:00
{
struct list {
struct list *next;
char *name;
} *head, *p;
char *localpath;
size_t len;
for (len = 0, head = 0; entry->dir; entry = entry->dir) {
p = g_new (struct list, 1);
1998-02-27 07:54:42 +03:00
p->next = head;
p->name = entry->name;
head = p;
len += strlen (entry->name) + 1;
}
if (len == 0)
2000-01-13 18:15:42 +03:00
return g_strdup ("");
1998-02-27 07:54:42 +03:00
localpath = g_malloc (len);
1998-02-27 07:54:42 +03:00
*localpath = '\0';
2000-01-13 18:15:42 +03:00
while (head) {
1998-02-27 07:54:42 +03:00
strcat (localpath, head->name);
if (head->next)
strcat (localpath, "/");
p = head;
head = head->next;
g_free (p);
1998-02-27 07:54:42 +03:00
}
return (localpath);
}
struct loop_protect {
struct entry *entry;
struct loop_protect *next;
1998-02-27 07:54:42 +03:00
};
static int errloop;
static int notadir;
static struct entry *
extfs_find_entry_int (struct entry *dir, char *name,
struct loop_protect *list, int make_dirs, int make_file);
1998-02-27 07:54:42 +03:00
static struct entry *
extfs_resolve_symlinks_int (struct entry *entry,
struct loop_protect *list)
1998-02-27 07:54:42 +03:00
{
struct entry *pent;
struct loop_protect *looping;
1998-02-27 07:54:42 +03:00
if (!S_ISLNK (entry->inode->mode))
return entry;
for (looping = list; looping != NULL; looping = looping->next)
if (entry == looping->entry) { /* Here we protect us against symlink looping */
errloop = 1;
return NULL;
}
looping = g_new (struct loop_protect, 1);
1998-02-27 07:54:42 +03:00
looping->entry = entry;
looping->next = list;
pent = extfs_find_entry_int (entry->dir, entry->inode->linkname, looping, 0, 0);
g_free (looping);
1998-02-27 07:54:42 +03:00
if (pent == NULL)
my_errno = ENOENT;
1998-02-27 07:54:42 +03:00
return pent;
}
static struct entry *extfs_resolve_symlinks (struct entry *entry)
1998-02-27 07:54:42 +03:00
{
struct entry *res;
1998-02-27 07:54:42 +03:00
errloop = 0;
notadir = 0;
res = extfs_resolve_symlinks_int (entry, NULL);
1998-02-27 07:54:42 +03:00
if (res == NULL) {
if (errloop)
my_errno = ELOOP;
1998-02-27 07:54:42 +03:00
else if (notadir)
my_errno = ENOTDIR;
1998-02-27 07:54:42 +03:00
}
return res;
}
static const char *
extfs_get_archive_name (struct archive *archive)
1998-02-27 07:54:42 +03:00
{
const char *archive_name;
1998-02-27 07:54:42 +03:00
if (archive->local_name)
archive_name = archive->local_name;
else
archive_name = archive->name;
if (!archive_name || !*archive_name)
return "no_archive_name";
else
return archive_name;
}
/* Don't pass localname as NULL */
static int
extfs_cmd (const char *extfs_cmd, struct archive *archive,
struct entry *entry, const char *localname)
{
char *file;
char *quoted_file;
char *quoted_localname;
char *archive_name;
char *mc_extfsdir;
char *cmd;
int retval;
file = extfs_get_path_from_entry (entry);
quoted_file = name_quote (file, 0);
g_free (file);
archive_name = name_quote (extfs_get_archive_name (archive), 0);
quoted_localname = name_quote (localname, 0);
mc_extfsdir = concat_dir_and_file (mc_home, "extfs" PATH_SEP_STR);
cmd = g_strconcat (mc_extfsdir, extfs_prefixes[archive->fstype],
extfs_cmd, archive_name, " ", quoted_file, " ",
quoted_localname, (char *) NULL);
g_free (quoted_file);
g_free (quoted_localname);
g_free (mc_extfsdir);
g_free (archive_name);
open_error_pipe ();
retval = my_system (EXECUTE_AS_SHELL, shell, cmd);
g_free (cmd);
close_error_pipe (1, NULL);
return retval;
}
static void
extfs_run (struct vfs_class *me, const char *file)
1998-02-27 07:54:42 +03:00
{
struct archive *archive = NULL;
char *p, *q, *archive_name, *mc_extfsdir;
char *cmd;
1998-02-27 07:54:42 +03:00
if ((p = extfs_get_path (me, file, &archive, 0)) == NULL)
1998-02-27 07:54:42 +03:00
return;
q = name_quote (p, 0);
g_free (p);
archive_name = name_quote (extfs_get_archive_name (archive), 0);
mc_extfsdir = concat_dir_and_file (mc_home, "extfs" PATH_SEP_STR);
cmd = g_strconcat (mc_extfsdir, extfs_prefixes[archive->fstype],
" run ", archive_name, " ", q, (char *) NULL);
g_free (mc_extfsdir);
g_free (archive_name);
g_free (q);
shell_execute (cmd, 0);
g_free (cmd);
1998-02-27 07:54:42 +03:00
}
static void *
extfs_open (struct vfs_class *me, const char *file, int flags, int mode)
1998-02-27 07:54:42 +03:00
{
struct pseudofile *extfs_info;
struct archive *archive = NULL;
char *q;
struct entry *entry;
1998-02-27 07:54:42 +03:00
int local_handle;
int created = 0;
if ((q = extfs_get_path (me, file, &archive, 0)) == NULL)
1998-02-27 07:54:42 +03:00
return NULL;
entry = extfs_find_entry (archive->root_entry, q, 0, 0);
if (entry == NULL && (flags & O_CREAT)) {
/* Create new entry */
entry = extfs_find_entry (archive->root_entry, q, 0, 1);
created = (entry != NULL);
}
g_free (q);
if (entry == NULL)
return NULL;
if ((entry = extfs_resolve_symlinks (entry)) == NULL)
1998-02-27 07:54:42 +03:00
return NULL;
if (S_ISDIR (entry->inode->mode))
ERRNOR (EISDIR, NULL);
1998-02-27 07:54:42 +03:00
if (entry->inode->local_filename == NULL) {
char *local_filename;
local_handle = vfs_mkstemps (&local_filename, "extfs", entry->name);
if (local_handle == -1)
return NULL;
close (local_handle);
if (!created && !(flags & O_TRUNC)
&& extfs_cmd (" copyout ", archive, entry, local_filename)) {
unlink (local_filename);
free (local_filename);
my_errno = EIO;
return NULL;
}
entry->inode->local_filename = local_filename;
1998-02-27 07:54:42 +03:00
}
local_handle =
open (entry->inode->local_filename, NO_LINEAR (flags), mode);
if (local_handle == -1)
ERRNOR (EIO, NULL);
extfs_info = g_new (struct pseudofile, 1);
1998-02-27 07:54:42 +03:00
extfs_info->archive = archive;
extfs_info->entry = entry;
extfs_info->has_changed = created;
1998-02-27 07:54:42 +03:00
extfs_info->local_handle = local_handle;
/* i.e. we had no open files and now we have one */
vfs_rmstamp (&vfs_extfs_ops, (vfsid) archive);
1998-02-27 07:54:42 +03:00
archive->fd_usage++;
return extfs_info;
}
static int extfs_read (void *data, char *buffer, int count)
{
struct pseudofile *file = (struct pseudofile *)data;
1998-02-27 07:54:42 +03:00
return read (file->local_handle, buffer, count);
}
static int
extfs_close (void *data)
1998-02-27 07:54:42 +03:00
{
struct pseudofile *file;
int errno_code = 0;
file = (struct pseudofile *) data;
1998-02-27 07:54:42 +03:00
close (file->local_handle);
/* Commit the file if it has changed */
if (file->has_changed) {
if (extfs_cmd
(" copyin ", file->archive, file->entry,
file->entry->inode->local_filename))
1998-02-27 07:54:42 +03:00
errno_code = EIO;
2000-01-13 18:15:42 +03:00
{
struct stat file_status;
if (stat (file->entry->inode->local_filename, &file_status) !=
0)
errno_code = EIO;
2000-01-13 18:15:42 +03:00
else
file->entry->inode->size = file_status.st_size;
}
file->entry->inode->mtime = time (NULL);
1998-02-27 07:54:42 +03:00
}
1998-02-27 07:54:42 +03:00
file->archive->fd_usage--;
if (!file->archive->fd_usage)
vfs_stamp_create (&vfs_extfs_ops, file->archive);
g_free (data);
if (errno_code)
ERRNOR (EIO, -1);
return 0;
1998-02-27 07:54:42 +03:00
}
#define RECORDSIZE 512
static struct entry*
extfs_find_entry_int (struct entry *dir, char *name,
struct loop_protect *list, int make_dirs, int make_file)
{
struct entry *pent, *pdir;
char *p, *q, *name_end;
char c;
if (*name == '/') { /* Handle absolute paths */
name++;
dir = dir->inode->archive->root_entry;
}
pent = dir;
p = name;
name_end = name + strlen (name);
q = strchr (p, '/');
c = '/';
if (!q)
q = strchr (p, 0);
for (; pent != NULL && c && *p; ){
c = *q;
*q = 0;
if (strcmp (p, ".")){
if (!strcmp (p, ".."))
pent = pent->dir;
else {
if ((pent = extfs_resolve_symlinks_int (pent, list))==NULL){
*q = c;
return NULL;
}
if (!S_ISDIR (pent->inode->mode)){
*q = c;
notadir = 1;
return NULL;
}
pdir = pent;
for (pent = pent->inode->first_in_subdir; pent; pent = pent->next_in_dir)
/* Hack: I keep the original semanthic unless
q+1 would break in the strchr */
if (!strcmp (pent->name, p)){
if (q + 1 > name_end){
*q = c;
notadir = !S_ISDIR (pent->inode->mode);
return pent;
}
break;
}
/* When we load archive, we create automagically
* non-existant directories
*/
if (pent == NULL && make_dirs) {
pent = extfs_generate_entry (dir->inode->archive, p, pdir, S_IFDIR | 0777);
}
if (pent == NULL && make_file) {
pent = extfs_generate_entry (dir->inode->archive, p, pdir, S_IFREG | 0666);
}
}
}
/* Next iteration */
*q = c;
p = q + 1;
q = strchr (p, '/');
if (!q)
q = strchr (p, 0);
}
if (pent == NULL)
my_errno = ENOENT;
return pent;
}
static struct entry *extfs_find_entry (struct entry *dir, char *name, int make_dirs, int make_file)
{
struct entry *res;
errloop = 0;
notadir = 0;
res = extfs_find_entry_int (dir, name, NULL, make_dirs, make_file);
if (res == NULL) {
if (errloop)
my_errno = ELOOP;
else if (notadir)
my_errno = ENOTDIR;
}
return res;
}
static int extfs_errno (struct vfs_class *me)
{
(void) me;
return my_errno;
}
static void * extfs_opendir (struct vfs_class *me, const char *dirname)
{
struct archive *archive = NULL;
char *q;
struct entry *entry;
struct entry **info;
if ((q = extfs_get_path (me, dirname, &archive, 0)) == NULL)
return NULL;
entry = extfs_find_entry (archive->root_entry, q, 0, 0);
g_free (q);
if (entry == NULL)
return NULL;
if ((entry = extfs_resolve_symlinks (entry)) == NULL)
return NULL;
if (!S_ISDIR (entry->inode->mode)) ERRNOR (ENOTDIR, NULL);
info = g_new (struct entry *, 2);
info[0] = entry->inode->first_in_subdir;
info[1] = entry->inode->first_in_subdir;
return info;
}
static void * extfs_readdir(void *data)
{
static union vfs_dirent dir;
struct entry **info = (struct entry **) data;
if (!*info)
return NULL;
g_strlcpy(dir.dent.d_name, (*info)->name, MC_MAXPATHLEN);
compute_namelen(&dir.dent);
*info = (*info)->next_in_dir;
return (void *) &dir;
}
static int extfs_closedir (void *data)
{
g_free (data);
return 0;
}
static void extfs_stat_move (struct stat *buf, const struct inode *inode)
{
buf->st_dev = inode->dev;
buf->st_ino = inode->inode;
buf->st_mode = inode->mode;
buf->st_nlink = inode->nlink;
buf->st_uid = inode->uid;
buf->st_gid = inode->gid;
#ifdef HAVE_STRUCT_STAT_ST_RDEV
buf->st_rdev = inode->rdev;
#endif
buf->st_size = inode->size;
#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
buf->st_blksize = RECORDSIZE;
#endif
#ifdef HAVE_STRUCT_STAT_ST_BLOCKS
buf->st_blocks = (inode->size + RECORDSIZE - 1) / RECORDSIZE;
#endif
buf->st_atime = inode->atime;
buf->st_mtime = inode->mtime;
buf->st_ctime = inode->ctime;
}
static int
extfs_internal_stat (struct vfs_class *me, const char *path, struct stat *buf,
int resolve)
{
struct archive *archive;
char *q;
struct entry *entry;
char *path2 = g_strdup (path);
int result = -1;
if ((q = extfs_get_path_mangle (me, path2, &archive, 0)) == NULL)
goto cleanup;
entry = extfs_find_entry (archive->root_entry, q, 0, 0);
if (entry == NULL)
goto cleanup;
if (resolve && (entry = extfs_resolve_symlinks (entry)) == NULL)
goto cleanup;
extfs_stat_move (buf, entry->inode);
result = 0;
cleanup:
g_free (path2);
return result;
}
static int extfs_stat (struct vfs_class *me, const char *path, struct stat *buf)
{
return extfs_internal_stat (me, path, buf, 1);
}
static int extfs_lstat (struct vfs_class *me, const char *path, struct stat *buf)
{
return extfs_internal_stat (me, path, buf, 0);
}
static int extfs_fstat (void *data, struct stat *buf)
{
struct pseudofile *file = (struct pseudofile *)data;
extfs_stat_move (buf, file->entry->inode);
return 0;
}
static int
extfs_readlink (struct vfs_class *me, const char *path, char *buf, size_t size)
{
struct archive *archive;
char *q;
size_t len;
struct entry *entry;
char *mpath = g_strdup (path);
int result = -1;
if ((q = extfs_get_path_mangle (me, mpath, &archive, 0)) == NULL)
goto cleanup;
entry = extfs_find_entry (archive->root_entry, q, 0, 0);
if (entry == NULL)
goto cleanup;
if (!S_ISLNK (entry->inode->mode)) {
me->verrno = EINVAL;
goto cleanup;
}
len = strlen (entry->inode->linkname);
if (size < len)
len = size;
/* readlink() does not append a NUL character to buf */
memcpy (buf, entry->inode->linkname, result = len);
cleanup:
g_free (mpath);
return result;
}
1998-02-27 07:54:42 +03:00
static int extfs_chmod (struct vfs_class *me, const char *path, int mode)
1998-02-27 07:54:42 +03:00
{
(void) me;
(void) path;
(void) mode;
1998-02-27 07:54:42 +03:00
return 0;
}
static int extfs_write (void *data, const char *buf, int nbyte)
1998-02-27 07:54:42 +03:00
{
struct pseudofile *file = (struct pseudofile *)data;
1998-02-27 07:54:42 +03:00
file->has_changed = 1;
return write (file->local_handle, buf, nbyte);
}
static int extfs_unlink (struct vfs_class *me, const char *file)
{
struct archive *archive;
char *q, *mpath = g_strdup (file);
struct entry *entry;
int result = -1;
if ((q = extfs_get_path_mangle (me, mpath, &archive, 0)) == NULL)
goto cleanup;
entry = extfs_find_entry (archive->root_entry, q, 0, 0);
if (entry == NULL)
goto cleanup;
if ((entry = extfs_resolve_symlinks (entry)) == NULL)
goto cleanup;
if (S_ISDIR (entry->inode->mode)) {
me->verrno = EISDIR;
goto cleanup;
}
if (extfs_cmd (" rm ", archive, entry, "")){
2000-01-13 18:15:42 +03:00
my_errno = EIO;
goto cleanup;
}
extfs_remove_entry (entry);
result = 0;
cleanup:
g_free (mpath);
return result;
}
static int extfs_mkdir (struct vfs_class *me, const char *path, mode_t mode)
{
struct archive *archive;
char *q, *mpath = g_strdup(path);
struct entry *entry;
int result = -1;
(void) mode;
if ((q = extfs_get_path_mangle (me, mpath, &archive, 0)) == NULL)
goto cleanup;
entry = extfs_find_entry (archive->root_entry, q, 0, 0);
if (entry != NULL) {
me->verrno = EEXIST;
goto cleanup;
}
entry = extfs_find_entry (archive->root_entry, q, 1, 0);
if (entry == NULL)
goto cleanup;
if ((entry = extfs_resolve_symlinks (entry)) == NULL)
goto cleanup;
if (!S_ISDIR (entry->inode->mode)) {
me->verrno = ENOTDIR;
goto cleanup;
}
if (extfs_cmd (" mkdir ", archive, entry, "")){
2000-01-13 18:15:42 +03:00
my_errno = EIO;
extfs_remove_entry (entry);
goto cleanup;
}
result = 0;
cleanup:
g_free (mpath);
return result;
}
static int extfs_rmdir (struct vfs_class *me, const char *path)
{
struct archive *archive;
char *q, *mpath = g_strdup(path);
struct entry *entry;
int result = -1;
if ((q = extfs_get_path_mangle (me, mpath, &archive, 0)) == NULL)
goto cleanup;
entry = extfs_find_entry (archive->root_entry, q, 0, 0);
if (entry == NULL)
goto cleanup;
if ((entry = extfs_resolve_symlinks (entry)) == NULL)
goto cleanup;
if (!S_ISDIR (entry->inode->mode)) {
me->verrno = ENOTDIR;
goto cleanup;
}
if (extfs_cmd (" rmdir ", archive, entry, "")){
2000-01-13 18:15:42 +03:00
my_errno = EIO;
goto cleanup;
}
extfs_remove_entry (entry);
result = 0;
cleanup:
g_free (mpath);
return result;
}
static int
extfs_chdir (struct vfs_class *me, const char *path)
1998-02-27 07:54:42 +03:00
{
struct archive *archive = NULL;
2000-01-13 18:15:42 +03:00
char *q;
struct entry *entry;
1998-02-27 07:54:42 +03:00
my_errno = ENOTDIR;
if ((q = extfs_get_path (me, path, &archive, 0)) == NULL)
1998-02-27 07:54:42 +03:00
return -1;
entry = extfs_find_entry (archive->root_entry, q, 0, 0);
g_free (q);
if (!entry)
return -1;
entry = extfs_resolve_symlinks (entry);
if ((!entry) || (!S_ISDIR (entry->inode->mode)))
return -1;
my_errno = 0;
1998-02-27 07:54:42 +03:00
return 0;
}
static int extfs_lseek (void *data, off_t offset, int whence)
{
struct pseudofile *file = (struct pseudofile *) data;
1998-02-27 07:54:42 +03:00
return lseek (file->local_handle, offset, whence);
}
static vfsid
extfs_getid (struct vfs_class *me, const char *path)
1998-02-27 07:54:42 +03:00
{
struct archive *archive = NULL;
char *p;
1998-02-27 07:54:42 +03:00
if (!(p = extfs_get_path (me, path, &archive, 1)))
return NULL;
g_free (p);
return (vfsid) archive;
1998-02-27 07:54:42 +03:00
}
static int extfs_nothingisopen (vfsid id)
{
if (((struct archive *)id)->fd_usage <= 0)
1998-02-27 07:54:42 +03:00
return 1;
2000-01-13 18:15:42 +03:00
return 0;
1998-02-27 07:54:42 +03:00
}
static void extfs_remove_entry (struct entry *e)
{
int i = --(e->inode->nlink);
struct entry *pe, *ent, *prev;
if (S_ISDIR (e->inode->mode) && e->inode->first_in_subdir != NULL) {
struct entry *f = e->inode->first_in_subdir;
e->inode->first_in_subdir = NULL;
extfs_remove_entry (f);
}
pe = e->dir;
if (e == pe->inode->first_in_subdir)
2000-01-13 18:15:42 +03:00
pe->inode->first_in_subdir = e->next_in_dir;
prev = NULL;
for (ent = pe->inode->first_in_subdir; ent && ent->next_in_dir;
ent = ent->next_in_dir)
if (e == ent->next_in_dir) {
2000-01-13 18:15:42 +03:00
prev = ent;
break;
}
if (prev)
2000-01-13 18:15:42 +03:00
prev->next_in_dir = e->next_in_dir;
if (e == pe->inode->last_in_subdir)
2000-01-13 18:15:42 +03:00
pe->inode->last_in_subdir = prev;
if (i <= 0) {
if (e->inode->local_filename != NULL) {
unlink (e->inode->local_filename);
free (e->inode->local_filename);
}
g_free (e->inode->linkname);
g_free (e->inode);
}
g_free (e->name);
g_free (e);
}
static void extfs_free_entry (struct entry *e)
1998-02-27 07:54:42 +03:00
{
int i = --(e->inode->nlink);
if (S_ISDIR (e->inode->mode) && e->inode->first_in_subdir != NULL) {
struct entry *f = e->inode->first_in_subdir;
1998-02-27 07:54:42 +03:00
e->inode->first_in_subdir = NULL;
extfs_free_entry (f);
1998-02-27 07:54:42 +03:00
}
if (i <= 0) {
if (e->inode->local_filename != NULL) {
unlink (e->inode->local_filename);
free (e->inode->local_filename);
1998-02-27 07:54:42 +03:00
}
g_free (e->inode->linkname);
g_free (e->inode);
1998-02-27 07:54:42 +03:00
}
if (e->next_in_dir != NULL)
extfs_free_entry (e->next_in_dir);
g_free (e->name);
g_free (e);
1998-02-27 07:54:42 +03:00
}
static void extfs_free (vfsid id)
{
struct archive *parc;
struct archive *archive = (struct archive *)id;
1998-02-27 07:54:42 +03:00
extfs_free_entry (archive->root_entry);
1998-02-27 07:54:42 +03:00
if (archive == first_archive) {
first_archive = archive->next;
} else {
for (parc = first_archive; parc != NULL; parc = parc->next)
if (parc->next == archive)
break;
if (parc != NULL)
parc->next = archive->next;
}
extfs_free_archive (archive);
1998-02-27 07:54:42 +03:00
}
static char *
extfs_getlocalcopy (struct vfs_class *me, const char *path)
1998-02-27 07:54:42 +03:00
{
struct pseudofile *fp =
(struct pseudofile *) extfs_open (me, path, O_RDONLY, 0);
1998-02-27 07:54:42 +03:00
char *p;
1998-02-27 07:54:42 +03:00
if (fp == NULL)
return NULL;
1998-02-27 07:54:42 +03:00
if (fp->entry->inode->local_filename == NULL) {
extfs_close ((void *) fp);
return NULL;
1998-02-27 07:54:42 +03:00
}
p = g_strdup (fp->entry->inode->local_filename);
1998-02-27 07:54:42 +03:00
fp->archive->fd_usage++;
extfs_close ((void *) fp);
return p;
}
static int
extfs_ungetlocalcopy (struct vfs_class *me, const char *path,
const char *local, int has_changed)
1998-02-27 07:54:42 +03:00
{
struct pseudofile *fp =
(struct pseudofile *) extfs_open (me, path, O_RDONLY, 0);
1998-02-27 07:54:42 +03:00
if (fp == NULL)
return 0;
1998-02-27 07:54:42 +03:00
if (!strcmp (fp->entry->inode->local_filename, local)) {
fp->archive->fd_usage--;
fp->has_changed |= has_changed;
extfs_close ((void *) fp);
return 0;
1998-02-27 07:54:42 +03:00
} else {
/* Should not happen */
extfs_close ((void *) fp);
return 0;
1998-02-27 07:54:42 +03:00
}
}
static int extfs_init (struct vfs_class *me)
1998-02-27 07:54:42 +03:00
{
FILE *cfg;
char *mc_extfsini;
char key[256];
(void) me;
mc_extfsini = concat_dir_and_file (mc_home, "extfs" PATH_SEP_STR "extfs.ini");
cfg = fopen (mc_extfsini, "r");
/* We may not use vfs_die() message or message or similar,
* UI is not initialized at this time and message would not
* appear on screen. */
if (!cfg) {
fprintf (stderr, _("Warning: file %s not found\n"), mc_extfsini);
g_free (mc_extfsini);
return 0;
}
1998-02-27 07:54:42 +03:00
extfs_no = 0;
while (extfs_no < MAXEXTFS && fgets (key, sizeof (key), cfg)) {
char *c;
1998-02-27 07:54:42 +03:00
/* Handle those with a trailing ':', those flag that the
* file system does not require an archive to work
*/
if (*key == '[') {
fprintf(stderr, "Warning: You need to update your %s file.\n",
mc_extfsini);
fclose(cfg);
g_free (mc_extfsini);
return 0;
}
if (*key == '#' || *key == '\n')
continue;
1998-02-27 07:54:42 +03:00
if ((c = strchr (key, '\n'))){
*c-- = 0;
} else { /* Last line without newline or strlen (key) > 255 */
These are a bunch of changes to fix CORBA and session management. They are almost complete (i.e. to handle all nitty gritty cases), but they seem to be working OK right now. SM should be much more stable now. Please tell me if you find any weird behavior - Federico 1999-03-30 Federico Mena Quintero <federico@nuclecu.unam.mx> * gdesktop-icon.c (desktop_icon_realize): Remove the WM_CLIENT_LEADER property from icon windows so that window managers will not store SM information for them. * gnome-open-dialog.c: Added missing #includes. * gdesktop-init.c (desktop_init_at): Removed an unused variable. * gdesktop.h: Added some missing prototypes. * gmain.h: Added some missing prototypes. * Makefile.in: Added gsession.[ch] to the list of sources. * gmain.c (create_panels): Consider whether we have a CORBA server and session management. * gdesktop.c: #include "gdesktop-init.h" * gdesktop.c: Added a missing cast to GNOME_DIALOG. * gmain.c (create_panels): Removed the run_desktop global variable. * glayout.c (create_container): Set the wmclass of the panel to include its unique ID. * gsession.[ch]: New file with the functions that deal with session management. * glayout.c (gnome_exit): Use session_set_restart(). * gcorba.c (corba_init): Now returns an int with an error value. (corba_init_server): Initialize the server properly. Fixed all the object implementation code. (corba_create_window): New function used to create a window with the CORBA server. * gmain.c (gnome_check_super_user): Now the check for running as root is done here. There should be no GUI code in src/. 1999-03-30 Federico Mena Quintero <federico@nuclecu.unam.mx> * dlg.c (dlg_run_done): Do not call the callback of a NULL current widget. * setup.h: Added missing prototype for setup_init(). * filegui.c (check_progress_buttons): Added a missing return value. * dlg.c (remove_widget): Added a missing return value. * main.c: Removed the global directory_list variable. Removed the main_corba_register_server() function. * main.h: Removed the global run_desktop variable. * panel.h: Now the panel structure has a unique numerical ID used for session management. * screen.c (panel_new): Maintain a unique ID for each panel. * main.c (maybe_display_linksdir): Handle display of the desktop init dir here. (main): Call gnome_check_super_user(). (init_corba_with_args): Call corba_init_server(). * main.c (init_corba_with_args): Do CORBA initialization here. Also removed the global force_activation option. 1999-03-30 Federico Mena Quintero <federico@nuclecu.unam.mx> * vfs.c (vfs_add_current_stamps): Only do stamping of the panels if they exist. * mcserv.c: #include <sys/wait.h> (get_client): Put `#ifdef __EMX__' around an otherwise-unused variable. * utilvfs.c (vfs_split_url): Fix NULL <-> 0 confusion when comparing characters. * ftpfs.c (retrieve_dir): Removed unused variable dot_dot_found. * extfs.c (extfs_init): Assign `key' to c, not `&key'.
1999-03-30 10:09:56 +04:00
c = &key [strlen (key) - 1];
}
These are a bunch of changes to fix CORBA and session management. They are almost complete (i.e. to handle all nitty gritty cases), but they seem to be working OK right now. SM should be much more stable now. Please tell me if you find any weird behavior - Federico 1999-03-30 Federico Mena Quintero <federico@nuclecu.unam.mx> * gdesktop-icon.c (desktop_icon_realize): Remove the WM_CLIENT_LEADER property from icon windows so that window managers will not store SM information for them. * gnome-open-dialog.c: Added missing #includes. * gdesktop-init.c (desktop_init_at): Removed an unused variable. * gdesktop.h: Added some missing prototypes. * gmain.h: Added some missing prototypes. * Makefile.in: Added gsession.[ch] to the list of sources. * gmain.c (create_panels): Consider whether we have a CORBA server and session management. * gdesktop.c: #include "gdesktop-init.h" * gdesktop.c: Added a missing cast to GNOME_DIALOG. * gmain.c (create_panels): Removed the run_desktop global variable. * glayout.c (create_container): Set the wmclass of the panel to include its unique ID. * gsession.[ch]: New file with the functions that deal with session management. * glayout.c (gnome_exit): Use session_set_restart(). * gcorba.c (corba_init): Now returns an int with an error value. (corba_init_server): Initialize the server properly. Fixed all the object implementation code. (corba_create_window): New function used to create a window with the CORBA server. * gmain.c (gnome_check_super_user): Now the check for running as root is done here. There should be no GUI code in src/. 1999-03-30 Federico Mena Quintero <federico@nuclecu.unam.mx> * dlg.c (dlg_run_done): Do not call the callback of a NULL current widget. * setup.h: Added missing prototype for setup_init(). * filegui.c (check_progress_buttons): Added a missing return value. * dlg.c (remove_widget): Added a missing return value. * main.c: Removed the global directory_list variable. Removed the main_corba_register_server() function. * main.h: Removed the global run_desktop variable. * panel.h: Now the panel structure has a unique numerical ID used for session management. * screen.c (panel_new): Maintain a unique ID for each panel. * main.c (maybe_display_linksdir): Handle display of the desktop init dir here. (main): Call gnome_check_super_user(). (init_corba_with_args): Call corba_init_server(). * main.c (init_corba_with_args): Do CORBA initialization here. Also removed the global force_activation option. 1999-03-30 Federico Mena Quintero <federico@nuclecu.unam.mx> * vfs.c (vfs_add_current_stamps): Only do stamping of the panels if they exist. * mcserv.c: #include <sys/wait.h> (get_client): Put `#ifdef __EMX__' around an otherwise-unused variable. * utilvfs.c (vfs_split_url): Fix NULL <-> 0 confusion when comparing characters. * ftpfs.c (retrieve_dir): Removed unused variable dot_dot_found. * extfs.c (extfs_init): Assign `key' to c, not `&key'.
1999-03-30 10:09:56 +04:00
extfs_need_archive [extfs_no] = !(*c == ':');
if (*c == ':')
*c = 0;
if (!(*key))
continue;
1998-02-27 07:54:42 +03:00
extfs_prefixes [extfs_no++] = g_strdup (key);
1998-02-27 07:54:42 +03:00
}
fclose(cfg);
g_free (mc_extfsini);
return 1;
1998-02-27 07:54:42 +03:00
}
static int extfs_which (struct vfs_class *me, const char *path)
1998-02-27 07:54:42 +03:00
{
int i;
1998-02-27 07:54:42 +03:00
(void) me;
for (i = 0; i < extfs_no; i++)
if (!strcmp (path, extfs_prefixes [i]))
1998-02-27 07:54:42 +03:00
return i;
return -1;
}
static void extfs_done (struct vfs_class *me)
1998-02-27 07:54:42 +03:00
{
int i;
(void) me;
for (i = 0; i < extfs_no; i++ )
g_free (extfs_prefixes [i]);
1998-02-27 07:54:42 +03:00
extfs_no = 0;
}
static int
extfs_setctl (struct vfs_class *me, const char *path, int ctlop, void *arg)
1998-10-13 02:07:53 +04:00
{
(void) arg;
if (ctlop == VFS_SETCTL_RUN) {
extfs_run (me, path);
1998-10-13 02:07:53 +04:00
return 1;
}
return 0;
}
void
init_extfs (void)
{
vfs_extfs_ops.name = "extfs";
vfs_extfs_ops.init = extfs_init;
vfs_extfs_ops.done = extfs_done;
vfs_extfs_ops.fill_names = extfs_fill_names;
vfs_extfs_ops.which = extfs_which;
vfs_extfs_ops.open = extfs_open;
vfs_extfs_ops.close = extfs_close;
vfs_extfs_ops.read = extfs_read;
vfs_extfs_ops.write = extfs_write;
vfs_extfs_ops.opendir = extfs_opendir;
vfs_extfs_ops.readdir = extfs_readdir;
vfs_extfs_ops.closedir = extfs_closedir;
vfs_extfs_ops.stat = extfs_stat;
vfs_extfs_ops.lstat = extfs_lstat;
vfs_extfs_ops.fstat = extfs_fstat;
vfs_extfs_ops.chmod = extfs_chmod;
vfs_extfs_ops.readlink = extfs_readlink;
vfs_extfs_ops.unlink = extfs_unlink;
vfs_extfs_ops.chdir = extfs_chdir;
vfs_extfs_ops.ferrno = extfs_errno;
vfs_extfs_ops.lseek = extfs_lseek;
vfs_extfs_ops.getid = extfs_getid;
vfs_extfs_ops.nothingisopen = extfs_nothingisopen;
vfs_extfs_ops.free = extfs_free;
vfs_extfs_ops.getlocalcopy = extfs_getlocalcopy;
vfs_extfs_ops.ungetlocalcopy = extfs_ungetlocalcopy;
vfs_extfs_ops.mkdir = extfs_mkdir;
vfs_extfs_ops.rmdir = extfs_rmdir;
vfs_extfs_ops.setctl = extfs_setctl;
vfs_register_class (&vfs_extfs_ops);
}