mc/vfs/extfs.c

1188 lines
31 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
$Id$
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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
1998-02-27 07:54:42 +03:00
1998-10-13 02:07:53 +04:00
/* Namespace: exports only vfs_extfs_ops */
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 <fcntl.h>
#include <unistd.h>
#include <signal.h>
#ifdef HAVE_SYS_WAIT_H
#include <sys/wait.h>
#endif
#include <errno.h>
#include "utilvfs.h"
#include "../src/dialog.h"
1998-02-27 07:54:42 +03:00
#include "../src/main.h" /* For shell_execute */
#include "xdirentry.h"
1998-02-27 07:54:42 +03:00
#include "vfs.h"
#include "extfs.h"
#undef ERRNOR
#define ERRNOR(x,y) do { my_errno = x; return y; } while(0)
static struct entry *
find_entry (struct entry *dir, char *name, int make_dirs, int make_file);
static int extfs_which (vfs *me, char *path);
static void remove_entry (struct entry *e);
static struct archive *first_archive = NULL;
static int my_errno = 0;
1998-02-27 07:54:42 +03:00
static struct stat hstat; /* Stat struct corresponding */
#define MAXEXTFS 32
static char *extfs_prefixes [MAXEXTFS];
static char extfs_need_archive [MAXEXTFS];
static int extfs_no = 0;
1998-10-13 02:07:53 +04:00
static void extfs_fill_names (vfs *me, void (*func)(char *))
1998-02-27 07:54:42 +03:00
{
struct archive *a = first_archive;
1998-02-27 07:54:42 +03:00
char *name;
while (a){
name = g_strconcat (extfs_prefixes [a->fstype], "#",
1998-02-27 07:54:42 +03:00
(a->name ? a->name : ""), "/",
a->current_dir->name, NULL);
1998-02-27 07:54:42 +03:00
(*func)(name);
g_free (name);
1998-02-27 07:54:42 +03:00
a = a->next;
}
}
static void make_dot_doubledot (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->has_changed = 0;
entry->inode = inode;
entry->dir = ent;
inode->local_filename = NULL;
inode->first_in_subdir = entry;
inode->last_in_subdir = entry;
inode->nlink++;
entry->next_in_dir = g_new (struct entry, 1);
1998-02-27 07:54:42 +03:00
entry=entry->next_in_dir;
entry->name = g_strdup ("..");
1998-02-27 07:54:42 +03:00
entry->has_changed = 0;
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 *generate_entry (struct archive *archive,
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->has_changed = 0;
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->has_changed = 0;
inode->local_filename = NULL;
inode->linkname = 0;
inode->inode = (archive->__inode_counter)++;
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))
make_dot_doubledot (entry);
return entry;
}
static void free_entries (struct entry *entry)
1998-02-27 07:54:42 +03:00
{
return;
}
static void free_archive (struct archive *archive)
1998-02-27 07:54:42 +03:00
{
free_entries (archive->root_entry);
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);
/* ungetlocalcopy frees local_name for us */
}
if (archive->name)
g_free (archive->name);
g_free (archive);
1998-02-27 07:54:42 +03:00
}
static FILE *open_archive (int fstype, char *name, struct archive **pparc)
1998-02-27 07:54:42 +03:00
{
static dev_t __extfs_no = 0;
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];
1998-02-27 07:54:42 +03:00
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);
}
#if 0
/* Sorry, what is this good for? */
1998-02-27 07:54:42 +03:00
if (uses_archive == EFS_NEED_ARG){
tmp = name_quote (name, 0);
}
#endif
1998-02-27 07:54:42 +03:00
mc_extfsdir = concat_dir_and_file (mc_home, "extfs/");
cmd = g_strconcat (mc_extfsdir, extfs_prefixes [fstype],
" list ", local_name ? local_name : tmp, NULL);
1998-02-27 07:54:42 +03:00
if (tmp)
g_free (tmp);
2000-01-13 18:15:42 +03:00
g_free (mc_extfsdir);
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) {
if (local_name != NULL && uses_archive)
mc_ungetlocalcopy (name, local_name, 0);
return NULL;
}
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): name;
1998-02-27 07:54:42 +03:00
current_archive->local_name = local_name;
if (local_name != NULL)
mc_stat (local_name, &current_archive->local_stat);
current_archive->__inode_counter = 0;
current_archive->fd_usage = 0;
current_archive->extfsstat = mystat;
current_archive->rdev = __extfs_no++;
current_archive->next = first_archive;
first_archive = current_archive;
mode = current_archive->extfsstat.st_mode & 07777;
if (mode & 0400)
mode |= 0100;
if (mode & 0040)
mode |= 0010;
if (mode & 0004)
mode |= 0001;
mode |= S_IFDIR;
root_entry = generate_entry (current_archive, "/", NULL, mode);
root_entry->inode->uid = current_archive->extfsstat.st_uid;
root_entry->inode->gid = current_archive->extfsstat.st_gid;
root_entry->inode->atime = current_archive->extfsstat.st_atime;
root_entry->inode->ctime = current_archive->extfsstat.st_ctime;
root_entry->inode->mtime = current_archive->extfsstat.st_mtime;
current_archive->root_entry = root_entry;
current_archive->current_dir = root_entry;
*pparc = current_archive;
return result;
}
/*
* Main loop for reading an archive.
* Returns 0 on success, -1 on error.
*/
1998-10-13 02:07:53 +04:00
static int read_archive (int fstype, 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;
1998-02-27 07:54:42 +03:00
if ((extfsd = open_archive (fstype, name, &current_archive)) == NULL) {
message_3s (1, MSG_ERROR, _("Couldn't open %s archive\n%s"),
1998-02-27 07:54:42 +03:00
extfs_prefixes [fstype], name);
return -1;
}
buffer = g_malloc (4096);
1998-02-27 07:54:42 +03:00
while (fgets (buffer, 4096, extfsd) != NULL) {
current_link_name = NULL;
1998-10-13 02:07:53 +04:00
if (vfs_parse_ls_lga (buffer, &hstat, &current_file_name, &current_link_name)) {
struct entry *entry, *pent;
struct inode *inode;
1998-02-27 07:54:42 +03:00
char *p, *q, *cfn = current_file_name;
if (*cfn) {
if (*cfn == '/')
cfn++;
p = strchr (cfn, 0);
2000-01-13 18:15:42 +03:00
if (p != cfn && *(p - 1) == '/')
1998-02-27 07:54:42 +03:00
*(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 = find_entry (current_archive->root_entry, q, 1, 0) ;
1998-02-27 07:54:42 +03:00
if (pent == NULL) {
message_1s (1, MSG_ERROR, _("Inconsistent extfs archive"));
1998-02-27 07:54:42 +03:00
/* FIXME: Should clean everything one day */
g_free (buffer);
1998-02-27 07:54:42 +03:00
pclose (extfsd);
return -1;
}
entry = g_new (struct entry, 1);
entry->name = g_strdup (p);
1998-02-27 07:54:42 +03:00
entry->has_changed = 0;
entry->next_in_dir = NULL;
entry->dir = pent;
if (pent != NULL) {
if (pent->inode->last_in_subdir){
pent->inode->last_in_subdir->next_in_dir = entry;
pent->inode->last_in_subdir = entry;
}
}
if (!S_ISLNK (hstat.st_mode) && current_link_name != NULL) {
pent = find_entry (current_archive->root_entry, current_link_name, 0, 0);
1998-02-27 07:54:42 +03:00
if (pent == NULL) {
message_1s (1, MSG_ERROR, _("Inconsistent extfs archive"));
1998-02-27 07:54:42 +03:00
/* FIXME: Should clean everything one day */
g_free (buffer);
1998-02-27 07:54:42 +03:00
pclose (extfsd);
return -1;
} else {
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->has_changed = 0;
inode->inode = (current_archive->__inode_counter)++;
inode->nlink = 1;
inode->dev = current_archive->rdev;
inode->archive = current_archive;
inode->mode = hstat.st_mode;
#ifdef HAVE_ST_RDEV
inode->rdev = hstat.st_rdev;
#else
inode->rdev = 0;
#endif
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;
if (current_link_name != NULL && S_ISLNK (hstat.st_mode)) {
inode->linkname = current_link_name;
current_link_name = NULL;
} else {
if (S_ISLNK( hstat.st_mode))
inode->mode &= ~S_IFLNK; /* You *DON'T* want to do this always */
1998-02-27 07:54:42 +03:00
inode->linkname = NULL;
}
if (S_ISDIR (hstat.st_mode))
make_dot_doubledot (entry);
}
}
read_extfs_continue:
g_free (current_file_name);
1998-02-27 07:54:42 +03:00
if (current_link_name != NULL)
g_free (current_link_name);
1998-02-27 07:54:42 +03:00
}
}
pclose (extfsd);
#ifdef SCO_FLAVOR
waitpid(-1,NULL,WNOHANG);
1998-02-27 07:54:42 +03:00
#endif /* SCO_FLAVOR */
*pparc = current_archive;
g_free (buffer);
1998-02-27 07:54:42 +03:00
return 0;
}
static char *get_path (char *inname, struct archive **archive, int is_dir,
int do_not_open);
/* Returns path inside argument. Returned char* is inside inname, which is mangled
* by this operation (so you must not free it's return value)
*/
static char *get_path_mangle (char *inname, struct archive **archive, int is_dir,
1998-02-27 07:54:42 +03:00
int do_not_open)
{
char *local, *archive_name, *op;
1998-02-27 07:54:42 +03:00
int result = -1;
struct archive *parc;
1998-02-27 07:54:42 +03:00
struct vfs_stamping *parent;
vfs *v;
int fstype;
archive_name = inname;
vfs_split( inname, &local, &op );
fstype = extfs_which( NULL, op ); /* FIXME: we really should pass
self pointer. But as we know that extfs_which does not touch vfs
*me, it does not matter for now */
if (fstype == -1)
return NULL;
if (!local)
local = "";
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
*
* Actually, we should implement an alias mechanism that would
* translate: "a:" to "dos:a.
*
*/
for (parc = first_archive; parc != NULL; parc = parc->next)
if (parc->name) {
if (!strcmp (parc->name, archive_name)) {
struct stat *s=&(parc->extfsstat);
if (vfs_uid && (!(s->st_mode & 0004)))
if ((s->st_gid != vfs_gid) || !(s->st_mode & 0040))
if ((s->st_uid != vfs_uid) || !(s->st_mode & 0400))
return NULL;
/* This is not too secure - in some cases (/#mtools) files created
under user a are probably visible to everyone else since / usually
has permissions 755 */
1998-10-13 02:07:53 +04:00
vfs_stamp (&vfs_extfs_ops, (vfsid) parc);
1998-02-27 07:54:42 +03:00
goto return_success;
}
}
result = do_not_open ? -1 : read_archive (fstype, archive_name, &parc);
if (result == -1) ERRNOR (EIO, NULL);
1998-02-27 07:54:42 +03:00
if (archive_name){
v = vfs_type (archive_name);
1998-10-13 02:07:53 +04:00
if (v == &vfs_local_ops) {
1998-02-27 07:54:42 +03:00
parent = NULL;
} else {
parent = g_new (struct vfs_stamping, 1);
1998-02-27 07:54:42 +03:00
parent->v = v;
parent->next = 0;
parent->id = (*v->getid) (v, archive_name, &(parent->parent));
1998-02-27 07:54:42 +03:00
}
1998-10-13 02:07:53 +04:00
vfs_add_noncurrent_stamps (&vfs_extfs_ops, (vfsid) parc, parent);
1998-02-27 07:54:42 +03:00
vfs_rm_parents (parent);
}
return_success:
*archive = parc;
return local;
}
/* Returns allocated path (without leading slash) inside the archive */
static char *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 *
__find_entry (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 *
__resolve_symlinks (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 = __find_entry (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 *my_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 = __resolve_symlinks (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;
}
struct pseudofile {
struct archive *archive;
1998-02-27 07:54:42 +03:00
unsigned int has_changed:1;
int local_handle;
struct entry *entry;
1998-02-27 07:54:42 +03:00
};
static char *get_archive_name (struct archive *archive)
1998-02-27 07:54:42 +03:00
{
char *archive_name;
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;
}
1998-10-13 02:07:53 +04:00
/* FIXME: we really should not have non-static procedures - it
* pollutes namespace. */
1998-02-27 07:54:42 +03:00
void extfs_run (char *file)
{
struct archive *archive;
char *p, *q, *archive_name, *mc_extfsdir;
char *cmd;
1998-02-27 07:54:42 +03:00
if ((p = get_path (file, &archive, 0, 0)) == NULL)
1998-02-27 07:54:42 +03:00
return;
q = name_quote (p, 0);
g_free (p);
1998-02-27 07:54:42 +03:00
archive_name = name_quote (get_archive_name(archive), 0);
mc_extfsdir = concat_dir_and_file (mc_home, "extfs/");
cmd = g_strconcat (mc_extfsdir, extfs_prefixes [archive->fstype],
" run ", archive_name, " ", q, NULL);
g_free (mc_extfsdir);
g_free (archive_name);
g_free (q);
#ifndef VFS_STANDALONE
1998-02-27 07:54:42 +03:00
shell_execute(cmd, 0);
#else
vfs_die( "shell_execute: implement me!" );
#endif
g_free(cmd);
1998-02-27 07:54:42 +03:00
}
static void *extfs_open (vfs *me, char *file, int flags, int mode)
1998-02-27 07:54:42 +03:00
{
struct pseudofile *extfs_info;
struct archive *archive;
char *q;
char *mc_extfsdir;
struct entry *entry;
1998-02-27 07:54:42 +03:00
int local_handle;
const int do_create = (flags & O_ACCMODE) != O_RDONLY;
if ((q = get_path_mangle (file, &archive, 0, 0)) == NULL)
1998-02-27 07:54:42 +03:00
return NULL;
entry = find_entry (archive->root_entry, q, 0, do_create);
if (entry == NULL)
1998-02-27 07:54:42 +03:00
return NULL;
if ((entry = my_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 *cmd;
char *archive_name, *p;
1998-02-27 07:54:42 +03:00
entry->inode->local_filename = g_tempnam (NULL, "extfs");
{
int handle;
handle = open(entry->inode->local_filename, O_RDWR | O_CREAT | O_EXCL, 0600);
if (handle == -1)
2000-01-13 18:15:42 +03:00
return NULL;
close(handle);
}
p = get_path_from_entry (entry);
1998-02-27 07:54:42 +03:00
q = name_quote (p, 0);
g_free (p);
1998-02-27 07:54:42 +03:00
archive_name = name_quote (get_archive_name (archive), 0);
mc_extfsdir = concat_dir_and_file (mc_home, "extfs/");
cmd = g_strconcat (mc_extfsdir, extfs_prefixes [archive->fstype],
" copyout ",
1998-02-27 07:54:42 +03:00
archive_name,
" ", q, " ", entry->inode->local_filename, NULL);
g_free (q);
g_free (mc_extfsdir);
g_free (archive_name);
1999-01-11 06:49:18 +03:00
if (my_system (EXECUTE_AS_SHELL | EXECUTE_SETUID | EXECUTE_WAIT, shell, cmd) && !do_create){
free (entry->inode->local_filename);
1998-02-27 07:54:42 +03:00
entry->inode->local_filename = NULL;
g_free (cmd);
my_errno = EIO;
1998-02-27 07:54:42 +03:00
return NULL;
}
g_free (cmd);
1998-02-27 07:54:42 +03:00
}
local_handle = open (entry->inode->local_filename, flags, mode);
if (local_handle == -1) ERRNOR (EIO, NULL);
1998-02-27 07:54:42 +03:00
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 = 0;
extfs_info->local_handle = local_handle;
/* i.e. we had no open files and now we have one */
1998-10-13 02:07:53 +04:00
vfs_rmstamp (&vfs_extfs_ops, (vfsid) archive, 1);
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)
{
struct pseudofile *file;
1998-02-27 07:54:42 +03:00
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){
struct archive *archive;
1998-02-27 07:54:42 +03:00
char *archive_name, *file_name;
char *cmd;
char *mc_extfsdir;
1998-02-27 07:54:42 +03:00
char *p;
archive = file->archive;
archive_name = name_quote (get_archive_name (archive), 0);
p = get_path_from_entry (file->entry);
1998-02-27 07:54:42 +03:00
file_name = name_quote (p, 0);
g_free (p);
1998-02-27 07:54:42 +03:00
mc_extfsdir = concat_dir_and_file (mc_home, "extfs/");
cmd = g_strconcat (mc_extfsdir,
1998-02-27 07:54:42 +03:00
extfs_prefixes [archive->fstype],
" copyin ", archive_name, " ",
file_name, " ",
file->entry->inode->local_filename, NULL);
g_free (archive_name);
g_free (file_name);
2000-01-13 18:15:42 +03:00
g_free (mc_extfsdir);
1999-01-11 06:49:18 +03:00
if (my_system (EXECUTE_AS_SHELL | EXECUTE_SETUID | EXECUTE_WAIT, shell, cmd))
1998-02-27 07:54:42 +03:00
errno_code = EIO;
g_free (cmd);
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;
else
file->entry->inode->size = file_status.st_size;
}
file->entry->inode->mtime = time (NULL);
1998-02-27 07:54:42 +03:00
}
file->archive->fd_usage--;
if (!file->archive->fd_usage) {
struct vfs_stamping *parent;
vfs *v;
1998-10-13 02:07:53 +04:00
if (!file->archive->name || !*file->archive->name || (v = vfs_type (file->archive->name)) == &vfs_local_ops) {
1998-02-27 07:54:42 +03:00
parent = NULL;
} else {
parent = g_new (struct vfs_stamping, 1);
1998-02-27 07:54:42 +03:00
parent->v = v;
parent->next = 0;
parent->id = (*v->getid) (v, file->archive->name, &(parent->parent));
1998-02-27 07:54:42 +03:00
}
1998-10-13 02:07:53 +04:00
vfs_add_noncurrent_stamps (&vfs_extfs_ops, (vfsid) (file->archive), parent);
1998-02-27 07:54:42 +03:00
vfs_rm_parents (parent);
}
g_free (data);
if (errno_code) ERRNOR (EIO, -1);
return 0;
1998-02-27 07:54:42 +03:00
}
#define RECORDSIZE 512
#include "shared_tar_ext.c"
1998-02-27 07:54:42 +03:00
static int extfs_chmod (vfs *me, char *path, int mode)
1998-02-27 07:54:42 +03:00
{
return 0;
}
static int extfs_write (void *data, char *buf, int nbyte)
{
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 (vfs *me, char *file)
{
struct archive *archive;
char *q;
char *mc_extfsdir;
struct entry *entry;
char *cmd;
char *archive_name, *p;
if ((q = get_path_mangle (file, &archive, 0, 0)) == NULL)
return -1;
entry = find_entry (archive->root_entry, q, 0, 0);
if (entry == NULL)
return -1;
if ((entry = my_resolve_symlinks (entry)) == NULL)
return -1;
if (S_ISDIR (entry->inode->mode)) ERRNOR (EISDIR, -1);
p = get_path_from_entry (entry);
q = name_quote (p, 0);
g_free (p);
archive_name = name_quote (get_archive_name (archive), 0);
mc_extfsdir = concat_dir_and_file (mc_home, "extfs/");
cmd = g_strconcat (mc_extfsdir, extfs_prefixes [archive->fstype],
" rm ", archive_name, " ", q, NULL);
g_free (q);
g_free (mc_extfsdir);
g_free (archive_name);
if (my_system (EXECUTE_AS_SHELL | EXECUTE_SETUID | EXECUTE_WAIT, shell, cmd)){
2000-01-13 18:15:42 +03:00
g_free (cmd);
my_errno = EIO;
return -1;
}
g_free (cmd);
remove_entry (entry);
return 0;
}
static int extfs_mkdir (vfs *me, char *path, mode_t mode)
{
struct archive *archive;
char *q;
char *mc_extfsdir;
struct entry *entry;
char *cmd;
char *archive_name, *p;
if ((q = get_path_mangle (path, &archive, 0, 0)) == NULL)
2000-01-13 18:15:42 +03:00
return -1;
entry = find_entry (archive->root_entry, q, 0, 0);
if (entry != NULL) ERRNOR (EEXIST, -1);
entry = find_entry (archive->root_entry, q, 1, 0);
if (entry == NULL)
2000-01-13 18:15:42 +03:00
return -1;
if ((entry = my_resolve_symlinks (entry)) == NULL)
return -1;
if (!S_ISDIR (entry->inode->mode)) ERRNOR (ENOTDIR, -1);
p = get_path_from_entry (entry);
q = name_quote (p, 0);
g_free (p);
archive_name = name_quote (get_archive_name (archive), 0);
mc_extfsdir = concat_dir_and_file (mc_home, "extfs/");
cmd = g_strconcat (mc_extfsdir, extfs_prefixes [archive->fstype],
" mkdir ", archive_name, " ", q, NULL);
g_free (q);
g_free (mc_extfsdir);
g_free (archive_name);
if (my_system (EXECUTE_AS_SHELL | EXECUTE_SETUID | EXECUTE_WAIT, shell, cmd)){
2000-01-13 18:15:42 +03:00
g_free (cmd);
my_errno = EIO;
remove_entry (entry);
return -1;
}
g_free (cmd);
return 0;
}
static int extfs_rmdir (vfs *me, char *path)
{
struct archive *archive;
char *q;
char *mc_extfsdir;
struct entry *entry;
char *cmd;
char *archive_name, *p;
if ((q = get_path_mangle (path, &archive, 0, 0)) == NULL)
return -1;
entry = find_entry (archive->root_entry, q, 0, 0);
if (entry == NULL)
return -1;
if ((entry = my_resolve_symlinks (entry)) == NULL)
return -1;
if (!S_ISDIR (entry->inode->mode)) ERRNOR (ENOTDIR, -1);
p = get_path_from_entry (entry);
q = name_quote (p, 0);
g_free (p);
archive_name = name_quote (get_archive_name (archive), 0);
mc_extfsdir = concat_dir_and_file (mc_home, "extfs/");
cmd = g_strconcat (mc_extfsdir, extfs_prefixes [archive->fstype],
" rmdir ", archive_name, " ", q, NULL);
g_free (q);
g_free (mc_extfsdir);
g_free (archive_name);
if (my_system (EXECUTE_AS_SHELL | EXECUTE_SETUID | EXECUTE_WAIT, shell, cmd)){
2000-01-13 18:15:42 +03:00
g_free (cmd);
my_errno = EIO;
return -1;
}
g_free (cmd);
remove_entry (entry);
return 0;
}
static int extfs_chdir (vfs *me, char *path)
1998-02-27 07:54:42 +03:00
{
struct archive *archive;
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 = get_path_mangle (path, &archive, 1, 0)) == NULL)
1998-02-27 07:54:42 +03:00
return -1;
entry = find_entry (archive->root_entry, q, 0, 0);
if (!entry)
1998-02-27 07:54:42 +03:00
return -1;
entry = my_resolve_symlinks (entry);
if ((!entry) || (!S_ISDIR (entry->inode->mode)))
1998-02-27 07:54:42 +03:00
return -1;
entry->inode->archive->current_dir = entry;
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 (vfs *me, char *path, struct vfs_stamping **parent)
1998-02-27 07:54:42 +03:00
{
struct archive *archive;
1998-02-27 07:54:42 +03:00
vfs *v;
vfsid id;
struct vfs_stamping *par;
char *p;
1998-02-27 07:54:42 +03:00
*parent = NULL;
if (!(p = get_path (path, &archive, 1, 1)))
1998-02-27 07:54:42 +03:00
return (vfsid) -1;
g_free(p);
1998-02-27 07:54:42 +03:00
if (archive->name){
v = vfs_type (archive->name);
id = (*v->getid) (v, archive->name, &par);
1998-02-27 07:54:42 +03:00
if (id != (vfsid)-1) {
*parent = g_new (struct vfs_stamping, 1);
1998-02-27 07:54:42 +03:00
(*parent)->v = v;
(*parent)->id = id;
(*parent)->parent = par;
(*parent)->next = NULL;
}
}
return (vfsid) archive;
}
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 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;
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);
}
if (e->inode->linkname != NULL)
g_free (e->inode->linkname);
g_free (e->inode);
}
g_free (e->name);
g_free (e);
}
static void 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;
free_entry (f);
}
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
}
if (e->inode->linkname != NULL)
g_free (e->inode->linkname);
g_free (e->inode);
1998-02-27 07:54:42 +03:00
}
if (e->next_in_dir != NULL)
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
free_entry (archive->root_entry);
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;
}
free_archive (archive);
}
static char *extfs_getlocalcopy (vfs *me, 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;
if (fp == NULL)
return NULL;
if (fp->entry->inode->local_filename == NULL) {
extfs_close ((void *) fp);
return NULL;
}
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 (vfs *me, char *path, char *local, int has_changed)
1998-02-27 07:54:42 +03:00
{
struct pseudofile *fp =
(struct pseudofile *) extfs_open (me, path, O_WRONLY, 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->entry->inode->has_changed = has_changed;
fp->archive->fd_usage--;
extfs_close ((void *) fp);
return 0;
1998-02-27 07:54:42 +03:00
} else {
/* Should not happen */
extfs_close ((void *) fp);
return mc_def_ungetlocalcopy (me, path, local, has_changed);
1998-02-27 07:54:42 +03:00
}
}
#include "../src/profile.h"
static int extfs_init (vfs *me)
1998-02-27 07:54:42 +03:00
{
FILE *cfg;
char *mc_extfsini;
mc_extfsini = concat_dir_and_file (mc_home, "extfs/extfs.ini");
cfg = fopen (mc_extfsini, "r");
g_free (mc_extfsini);
if (!cfg) {
fprintf( stderr, "Warning: " LIBDIR "extfs/extfs.ini not found\n" );
return 0;
}
1998-02-27 07:54:42 +03:00
extfs_no = 0;
while ( extfs_no < MAXEXTFS ) {
char key[256];
char *c;
if (!fgets( key, sizeof (key)-1, cfg ))
break;
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 == '[') {
/* We may not use vfs_die() message or message_1s or similar,
* UI is not initialized at this time and message would not
* appear on screen. */
fprintf( stderr, "Warning: You need to update your " LIBDIR "extfs/extfs.ini file.\n" );
fclose(cfg);
return 0;
}
if (*key == '#')
continue;
1998-02-27 07:54:42 +03:00
if ((c = strchr (key, '\n'))){
*c = 0;
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];
} else {
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;
}
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);
extfs_no++;
1998-02-27 07:54:42 +03:00
}
fclose(cfg);
return 1;
1998-02-27 07:54:42 +03:00
}
/* Do NOT use me argument in this function */
static int extfs_which (vfs *me, char *path)
1998-02-27 07:54:42 +03:00
{
int i;
1998-02-27 07:54:42 +03:00
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 (vfs *me)
1998-02-27 07:54:42 +03:00
{
int i;
for (i = 0; i < extfs_no; i++ )
g_free (extfs_prefixes [i]);
1998-02-27 07:54:42 +03:00
extfs_no = 0;
}
1998-10-13 02:07:53 +04:00
static int extfs_setctl (vfs *me, char *path, int ctlop, char *arg)
{
if (ctlop == MCCTL_EXTFS_RUN) {
extfs_run (path);
return 1;
}
return 0;
}
vfs vfs_extfs_ops = {
NULL, /* This is place of next pointer */
"Extended filesystems",
F_EXEC, /* flags */
NULL, /* prefix */
NULL, /* data */
0, /* errno */
extfs_init,
extfs_done,
extfs_fill_names,
extfs_which,
extfs_open,
extfs_close,
extfs_read,
extfs_write,
s_opendir,
s_readdir,
s_closedir,
s_telldir,
s_seekdir,
s_stat,
s_lstat,
s_fstat,
extfs_chmod, /* chmod ... strange, returns success? */
NULL,
NULL,
s_readlink,
NULL, /* symlink */
NULL,
extfs_unlink,
NULL,
extfs_chdir,
s_errno,
extfs_lseek,
NULL,
extfs_getid,
extfs_nothingisopen,
extfs_free,
extfs_getlocalcopy,
extfs_ungetlocalcopy,
extfs_mkdir, /* mkdir */
extfs_rmdir,
NULL,
1998-10-13 02:07:53 +04:00
extfs_setctl
MMAPNULL
};