mirror of
https://github.com/openharmony/third_party_libfuse.git
synced 2026-07-21 00:45:56 -04:00
x
This commit is contained in:
@@ -1,34 +1,25 @@
|
||||
CC = gcc
|
||||
|
||||
KCFLAGS = -O2 -Wall -Wstrict-prototypes -fno-strict-aliasing -pipe
|
||||
KCPPFLAGS = -I /lib/modules/`uname -r`/build/include/ -D__KERNEL__ -DMODULE -D_LOOSE_KERNEL_NAMES
|
||||
|
||||
CFLAGS = -Wall -W -g `glib-config --cflags`
|
||||
LDFLAGS = `glib-config --libs`
|
||||
CPPFLAGS =
|
||||
CPPFLAGS = -Iinclude
|
||||
|
||||
all: fuse.o fusemount
|
||||
|
||||
dev.o: dev.c
|
||||
$(CC) $(KCFLAGS) $(KCPPFLAGS) -c dev.c
|
||||
|
||||
inode.o: inode.c
|
||||
$(CC) $(KCFLAGS) $(KCPPFLAGS) -c inode.c
|
||||
all: kernel/fuse.o fusepro
|
||||
|
||||
dir.o: dir.c
|
||||
$(CC) $(KCFLAGS) $(KCPPFLAGS) -c dir.c
|
||||
kernel/fuse.o: FORCE
|
||||
make -C kernel fuse.o
|
||||
|
||||
util.o: util.c
|
||||
$(CC) $(KCFLAGS) $(KCPPFLAGS) -c util.c
|
||||
lib/libfuse.a: FORCE
|
||||
make -C lib libfuse.a
|
||||
|
||||
fuse_objs = dev.o inode.o dir.o util.o
|
||||
|
||||
fuse.o: $(fuse_objs)
|
||||
ld -r -o fuse.o $(fuse_objs)
|
||||
|
||||
fusemount: fusemount.o
|
||||
fusepro: fusepro.o lib/libfuse.a
|
||||
|
||||
clean:
|
||||
make -C kernel clean
|
||||
make -C lib clean
|
||||
rm -f *.o
|
||||
rm -f fusemount
|
||||
rm -f fusepro
|
||||
rm -f *~
|
||||
|
||||
FORCE:
|
||||
|
||||
-399
@@ -1,399 +0,0 @@
|
||||
/*
|
||||
AVFS: A Virtual File System Library
|
||||
Copyright (C) 1998-2001 Miklos Szeredi (mszeredi@inf.bme.hu)
|
||||
|
||||
This program can be distributed under the terms of the GNU GPL.
|
||||
See the file COPYING.
|
||||
*/
|
||||
|
||||
#include "fuse.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
#include <dirent.h>
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/mount.h>
|
||||
#include <mntent.h>
|
||||
#include <glib.h>
|
||||
|
||||
static char *mount_dir;
|
||||
|
||||
const char *basedir = "/tmp/pro";
|
||||
|
||||
struct node {
|
||||
char *name;
|
||||
unsigned long ino;
|
||||
};
|
||||
|
||||
static GNode *root;
|
||||
static GHashTable *nodetab;
|
||||
static unsigned long inoctr = FUSE_ROOT_INO;
|
||||
|
||||
static GNode *new_node(const char *name, unsigned long ino)
|
||||
{
|
||||
struct node *node = g_new0(struct node, 1);
|
||||
GNode *gn = g_node_new(node);
|
||||
|
||||
node->name = g_strdup(name);
|
||||
node->ino = ino;
|
||||
|
||||
return gn;
|
||||
}
|
||||
|
||||
static unsigned long find_node(unsigned long ino, const char *name)
|
||||
{
|
||||
GNode *cn;
|
||||
GNode *pn = g_hash_table_lookup(nodetab, (gpointer) ino);
|
||||
if(pn == NULL) {
|
||||
fprintf(stderr, "Can't find parent node %li\n", ino);
|
||||
return 0;
|
||||
}
|
||||
|
||||
for(cn = pn->children; cn != NULL; cn = cn->next) {
|
||||
struct node *node = (struct node *) cn->data;
|
||||
if(strcmp(node->name, name) == 0)
|
||||
return node->ino;
|
||||
}
|
||||
|
||||
do inoctr++;
|
||||
while(!inoctr && g_hash_table_lookup(nodetab, (gpointer) ino) != NULL);
|
||||
|
||||
cn = new_node(name, inoctr);
|
||||
g_node_insert(pn, -1, cn);
|
||||
g_hash_table_insert(nodetab, (gpointer) inoctr, cn);
|
||||
|
||||
return inoctr;
|
||||
}
|
||||
|
||||
static char *real_path(unsigned long ino)
|
||||
{
|
||||
GString *s;
|
||||
char *ss;
|
||||
GNode *gn = g_hash_table_lookup(nodetab, (gpointer) ino);
|
||||
|
||||
if(gn == NULL) {
|
||||
fprintf(stderr, "Can't find node %li\n", ino);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
s = g_string_new("");
|
||||
for(; gn != NULL; gn = gn->parent) {
|
||||
g_string_prepend(s, ((struct node *) gn->data)->name);
|
||||
g_string_prepend_c(s, '/');
|
||||
}
|
||||
g_string_prepend(s, basedir);
|
||||
ss = s->str;
|
||||
g_string_free(s, FALSE);
|
||||
|
||||
return ss;
|
||||
}
|
||||
|
||||
static int get_dir(unsigned long dir)
|
||||
{
|
||||
int dirfd;
|
||||
struct fuse_dirent dirent;
|
||||
DIR *dp;
|
||||
struct dirent *de;
|
||||
size_t reclen;
|
||||
char *path;
|
||||
|
||||
path = real_path(dir);
|
||||
if(path == NULL)
|
||||
return -ENOENT;
|
||||
|
||||
dp = opendir(path);
|
||||
g_free(path);
|
||||
if(dp == NULL) {
|
||||
perror(path);
|
||||
return -errno;
|
||||
}
|
||||
dirfd = open("/tmp/dirtmp", O_RDWR | O_TRUNC | O_CREAT, 0600);
|
||||
if(dirfd == -1) {
|
||||
perror("/tmp/dirtmp");
|
||||
exit(1);
|
||||
}
|
||||
while((de = readdir(dp)) != NULL) {
|
||||
unsigned long ino = find_node(dir, de->d_name);
|
||||
assert(ino != 0);
|
||||
|
||||
dirent.ino = ino;
|
||||
dirent.namelen = strlen(de->d_name);
|
||||
assert(dirent.namelen <= NAME_MAX);
|
||||
strcpy(dirent.name, de->d_name);
|
||||
dirent.type = de->d_type;
|
||||
|
||||
reclen = FUSE_DIRENT_SIZE(&dirent);
|
||||
write(dirfd, &dirent, reclen);
|
||||
}
|
||||
closedir(dp);
|
||||
|
||||
return dirfd;
|
||||
}
|
||||
|
||||
static int get_attributes(unsigned long ino, struct fuse_attr *attr)
|
||||
{
|
||||
char *path;
|
||||
struct stat buf;
|
||||
int res;
|
||||
|
||||
path = real_path(ino);
|
||||
if(path == NULL)
|
||||
return -ENOENT;
|
||||
|
||||
res = stat(path, &buf);
|
||||
g_free(path);
|
||||
if(res == -1)
|
||||
return -errno;
|
||||
|
||||
attr->mode = buf.st_mode;
|
||||
attr->nlink = buf.st_nlink;
|
||||
attr->uid = buf.st_uid;
|
||||
attr->gid = buf.st_gid;
|
||||
attr->rdev = buf.st_rdev;
|
||||
attr->size = buf.st_size;
|
||||
attr->blksize = buf.st_blksize;
|
||||
attr->blocks = buf.st_blocks;
|
||||
attr->atime = buf.st_atime;
|
||||
attr->mtime = buf.st_mtime;
|
||||
attr->ctime = buf.st_ctime;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void loop(int devfd)
|
||||
{
|
||||
int res;
|
||||
struct fuse_param param;
|
||||
struct fuse_outparam out;
|
||||
struct fuse_inparam in;
|
||||
int dirfd;
|
||||
|
||||
while(1) {
|
||||
res = read(devfd, ¶m, sizeof(param));
|
||||
if(res == -1) {
|
||||
perror("read");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
printf("unique: %i, opcode: %i\n", param.unique, param.u.i.opcode);
|
||||
|
||||
dirfd = -1;
|
||||
in = param.u.i;
|
||||
switch(in.opcode) {
|
||||
case FUSE_LOOKUP:
|
||||
out.u.lookup.ino = find_node(in.ino, in.u.lookup.name);
|
||||
if(out.u.lookup.ino == 0)
|
||||
out.result = -ENOENT;
|
||||
else
|
||||
out.result = get_attributes(out.u.lookup.ino,
|
||||
&out.u.lookup.attr);
|
||||
break;
|
||||
|
||||
case FUSE_GETATTR:
|
||||
out.result = get_attributes(in.ino, &out.u.getattr.attr);
|
||||
break;
|
||||
|
||||
case FUSE_OPEN:
|
||||
dirfd = get_dir(in.ino);
|
||||
if(dirfd >= 0) {
|
||||
out.u.open.fd = dirfd;
|
||||
out.result = 0;
|
||||
}
|
||||
else
|
||||
out.result = dirfd;
|
||||
break;
|
||||
|
||||
case FUSE_RELEASE:
|
||||
out.result = 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
out.result = -EOPNOTSUPP;
|
||||
}
|
||||
param.u.o = out;
|
||||
|
||||
res = write(devfd, ¶m, sizeof(param));
|
||||
if(res == -1) {
|
||||
perror("write");
|
||||
exit(1);
|
||||
}
|
||||
if(dirfd != -1) {
|
||||
close(dirfd);
|
||||
unlink("/tmp/dirtmp");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int mount_fuse(const char *dev, const char *dir, int devfd)
|
||||
{
|
||||
int res;
|
||||
const char *type;
|
||||
FILE *fd;
|
||||
struct mntent ent;
|
||||
struct fuse_mount_data data;
|
||||
|
||||
data.version = FUSE_MOUNT_VERSION;
|
||||
data.fd = devfd;
|
||||
|
||||
type = "fuse";
|
||||
res = mount(dev, dir, type, MS_MGC_VAL | MS_NOSUID | MS_NODEV, &data);
|
||||
|
||||
if(res == -1) {
|
||||
fprintf(stderr, "mount failed: %s\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
fd = setmntent("/etc/mtab", "a");
|
||||
if(fd == NULL) {
|
||||
fprintf(stderr, "setmntent(\"/etc/mtab\") failed: %s\n",
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
ent.mnt_fsname = (char *) dev;
|
||||
ent.mnt_dir = (char *) dir;
|
||||
ent.mnt_type = (char *) type;
|
||||
ent.mnt_opts = "rw,nosuid,nodev";
|
||||
ent.mnt_freq = 0;
|
||||
ent.mnt_passno = 0;
|
||||
res = addmntent(fd, & ent);
|
||||
if(res != 0)
|
||||
fprintf(stderr, "addmntent() failed: %s\n", strerror(errno));
|
||||
|
||||
endmntent(fd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int unmount_fuse(const char *dir)
|
||||
{
|
||||
int res;
|
||||
FILE *fdold, *fdnew;
|
||||
struct mntent *entp;
|
||||
|
||||
res = umount(dir);
|
||||
|
||||
if(res == -1) {
|
||||
fprintf(stderr, "umount failed: %s\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
fdold = setmntent("/etc/mtab", "r");
|
||||
if(fdold == NULL) {
|
||||
fprintf(stderr, "setmntent(\"/etc/mtab\") failed: %s\n",
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
fdnew = setmntent("/etc/mtab~", "w");
|
||||
if(fdnew == NULL) {
|
||||
fprintf(stderr, "setmntent(\"/etc/mtab~\") failed: %s\n",
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
do {
|
||||
entp = getmntent(fdold);
|
||||
if(entp != NULL) {
|
||||
if(strcmp(entp->mnt_dir, dir) != 0) {
|
||||
res = addmntent(fdnew, entp);
|
||||
if(res != 0) {
|
||||
fprintf(stderr, "addmntent() failed: %s\n",
|
||||
strerror(errno));
|
||||
}
|
||||
}
|
||||
}
|
||||
} while(entp != NULL);
|
||||
|
||||
endmntent(fdold);
|
||||
endmntent(fdnew);
|
||||
|
||||
res = rename("/etc/mtab~", "/etc/mtab");
|
||||
if(res == -1) {
|
||||
fprintf(stderr, "rename(\"/etc/mtab~\", \"/etc/mtab\") failed: %s\n",
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void cleanup()
|
||||
{
|
||||
unmount_fuse(mount_dir);
|
||||
}
|
||||
|
||||
|
||||
void exit_handler()
|
||||
{
|
||||
exit(0);
|
||||
}
|
||||
|
||||
void set_signal_handlers()
|
||||
{
|
||||
struct sigaction sa;
|
||||
|
||||
sa.sa_handler = exit_handler;
|
||||
sigemptyset(&(sa.sa_mask));
|
||||
sa.sa_flags = 0;
|
||||
|
||||
if (sigaction(SIGHUP, &sa, NULL) == -1 ||
|
||||
sigaction(SIGINT, &sa, NULL) == -1 ||
|
||||
sigaction(SIGTERM, &sa, NULL) == -1) {
|
||||
|
||||
perror("Cannot set exit signal handlers");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
sa.sa_handler = SIG_IGN;
|
||||
|
||||
if(sigaction(SIGPIPE, &sa, NULL) == -1) {
|
||||
perror("Cannot set ignored signals");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
const char *dev;
|
||||
int devfd;
|
||||
|
||||
if(argc < 3) {
|
||||
fprintf(stderr, "usage: %s dev dir\n", argv[0]);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
dev = argv[1];
|
||||
mount_dir = argv[2];
|
||||
|
||||
devfd = open(dev, O_RDWR);
|
||||
if(devfd == -1) {
|
||||
fprintf(stderr, "failed to open %s: %s\n", dev, strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
mount_fuse(dev, mount_dir, devfd);
|
||||
|
||||
set_signal_handlers();
|
||||
atexit(cleanup);
|
||||
|
||||
root = new_node("/", FUSE_ROOT_INO);
|
||||
nodetab = g_hash_table_new(NULL, NULL);
|
||||
g_hash_table_insert(nodetab, (gpointer) FUSE_ROOT_INO, root);
|
||||
|
||||
loop(devfd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,125 @@
|
||||
#include <fuse.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
|
||||
static struct fuse *pro_fuse;
|
||||
|
||||
static int pro_getattr(const char *path, struct stat *stbuf)
|
||||
{
|
||||
int res;
|
||||
|
||||
res = lstat(path, stbuf);
|
||||
if(res == -1)
|
||||
return -errno;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pro_readlink(const char *path, char *buf, size_t size)
|
||||
{
|
||||
int res;
|
||||
|
||||
res = readlink(path, buf, size - 1);
|
||||
if(res == -1)
|
||||
return -errno;
|
||||
|
||||
buf[res] = '\0';
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int pro_getdir(const char *path, struct fuse_dh *h, dirfiller_t filler)
|
||||
{
|
||||
DIR *dp;
|
||||
struct dirent *de;
|
||||
int res;
|
||||
|
||||
dp = opendir(path);
|
||||
if(dp == NULL)
|
||||
return -errno;
|
||||
|
||||
while((de = readdir(dp)) != NULL) {
|
||||
res = filler(h, de->d_name, de->d_type);
|
||||
if(res != 0)
|
||||
break;
|
||||
}
|
||||
|
||||
closedir(dp);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int pro_mknod(const char *path, int mode, int rdev)
|
||||
{
|
||||
int res;
|
||||
|
||||
res = mknod(path, mode, rdev);
|
||||
if(res == -1)
|
||||
return -errno;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void exit_handler()
|
||||
{
|
||||
exit(0);
|
||||
}
|
||||
|
||||
static void set_signal_handlers()
|
||||
{
|
||||
struct sigaction sa;
|
||||
|
||||
sa.sa_handler = exit_handler;
|
||||
sigemptyset(&(sa.sa_mask));
|
||||
sa.sa_flags = 0;
|
||||
|
||||
if (sigaction(SIGHUP, &sa, NULL) == -1 ||
|
||||
sigaction(SIGINT, &sa, NULL) == -1 ||
|
||||
sigaction(SIGTERM, &sa, NULL) == -1) {
|
||||
|
||||
perror("Cannot set exit signal handlers");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
sa.sa_handler = SIG_IGN;
|
||||
|
||||
if(sigaction(SIGPIPE, &sa, NULL) == -1) {
|
||||
perror("Cannot set ignored signals");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
static void cleanup()
|
||||
{
|
||||
fuse_unmount(pro_fuse);
|
||||
fuse_destroy(pro_fuse);
|
||||
}
|
||||
|
||||
static struct fuse_operations pro_oper = {
|
||||
getattr: pro_getattr,
|
||||
readlink: pro_readlink,
|
||||
getdir: pro_getdir,
|
||||
mknod: pro_mknod,
|
||||
};
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
if(argc != 2) {
|
||||
fprintf(stderr, "usage: %s mount_dir\n", argv[0]);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
set_signal_handlers();
|
||||
atexit(cleanup);
|
||||
|
||||
pro_fuse = fuse_new();
|
||||
fuse_mount(pro_fuse, argv[1]);
|
||||
fuse_set_operations(pro_fuse, &pro_oper);
|
||||
fuse_loop(pro_fuse);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
FUSE: Filesystem in Userspace
|
||||
Copyright (C) 2001 Miklos Szeredi (mszeredi@inf.bme.hu)
|
||||
|
||||
This program can be distributed under the terms of the GNU GPL.
|
||||
See the file COPYING.
|
||||
*/
|
||||
|
||||
/* This file defines the library interface of FUSE */
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
struct fuse;
|
||||
struct fuse_dh;
|
||||
|
||||
typedef int (*dirfiller_t) (struct fuse_dh *, const char *, int type);
|
||||
|
||||
|
||||
struct fuse_operations {
|
||||
int (*getattr) (const char *path, struct stat *stbuf);
|
||||
int (*readlink) (const char *path, char *buf, size_t size);
|
||||
int (*mknod) (const char *path, int mode, int rdev);
|
||||
int (*getdir) (const char *path, struct fuse_dh *h, dirfiller_t filler);
|
||||
};
|
||||
|
||||
struct fuse *fuse_new();
|
||||
|
||||
int fuse_mount(struct fuse *f, const char *dir);
|
||||
|
||||
void fuse_set_operations(struct fuse *f, const struct fuse_operations *op);
|
||||
|
||||
void fuse_loop(struct fuse *f);
|
||||
|
||||
int fuse_unmount(struct fuse *f);
|
||||
|
||||
void fuse_destroy(struct fuse *f);
|
||||
@@ -6,7 +6,8 @@
|
||||
See the file COPYING.
|
||||
*/
|
||||
|
||||
#include <linux/limits.h>
|
||||
/* This file defines the kernel interface of FUSE */
|
||||
|
||||
|
||||
#define FUSE_MOUNT_VERSION 1
|
||||
|
||||
@@ -18,86 +19,75 @@ struct fuse_mount_data {
|
||||
#define FUSE_ROOT_INO 1
|
||||
|
||||
struct fuse_attr {
|
||||
unsigned short mode;
|
||||
unsigned short nlink;
|
||||
unsigned short uid;
|
||||
unsigned short gid;
|
||||
unsigned short rdev;
|
||||
unsigned long size;
|
||||
unsigned long blksize;
|
||||
unsigned long blocks;
|
||||
unsigned long atime;
|
||||
unsigned long mtime;
|
||||
unsigned long ctime;
|
||||
unsigned short mode;
|
||||
unsigned short nlink;
|
||||
unsigned short uid;
|
||||
unsigned short gid;
|
||||
unsigned short rdev;
|
||||
unsigned long long size;
|
||||
unsigned long blksize;
|
||||
unsigned long blocks;
|
||||
unsigned long atime;
|
||||
unsigned long mtime;
|
||||
unsigned long ctime;
|
||||
};
|
||||
|
||||
enum fuse_opcode {
|
||||
FUSE_LOOKUP,
|
||||
FUSE_LOOKUP = 1,
|
||||
FUSE_FORGET,
|
||||
FUSE_GETATTR,
|
||||
FUSE_READLINK,
|
||||
FUSE_OPEN,
|
||||
FUSE_RELEASE,
|
||||
FUSE_GETDIR,
|
||||
FUSE_MKNOD,
|
||||
};
|
||||
|
||||
/* Conservative buffer size for the client */
|
||||
#define FUSE_MAX_IN 8192
|
||||
|
||||
struct fuse_in_open {
|
||||
int flag;
|
||||
};
|
||||
|
||||
struct fuse_out_open {
|
||||
int fd;
|
||||
};
|
||||
|
||||
struct fuse_in_lookup {
|
||||
char name[NAME_MAX + 1];
|
||||
};
|
||||
|
||||
struct fuse_out_lookup {
|
||||
struct fuse_lookup_out {
|
||||
unsigned long ino;
|
||||
struct fuse_attr attr;
|
||||
};
|
||||
|
||||
struct fuse_out_getattr {
|
||||
struct fuse_getattr_out {
|
||||
struct fuse_attr attr;
|
||||
};
|
||||
|
||||
struct fuse_out_readlink {
|
||||
char link[PATH_MAX + 1];
|
||||
struct fuse_getdir_out {
|
||||
int fd;
|
||||
void *file; /* Used by kernel only */
|
||||
};
|
||||
|
||||
struct fuse_in_common {
|
||||
struct fuse_mknod_in {
|
||||
unsigned short mode;
|
||||
unsigned short rdev;
|
||||
char name[1];
|
||||
};
|
||||
|
||||
struct fuse_mknod_out {
|
||||
unsigned long ino;
|
||||
struct fuse_attr attr;
|
||||
};
|
||||
|
||||
struct fuse_in_header {
|
||||
int unique;
|
||||
enum fuse_opcode opcode;
|
||||
unsigned long ino;
|
||||
};
|
||||
|
||||
struct fuse_out_common {
|
||||
struct fuse_out_header {
|
||||
int unique;
|
||||
int result;
|
||||
};
|
||||
|
||||
struct fuse_in {
|
||||
struct fuse_in_common c;
|
||||
size_t argsize;
|
||||
void *arg;
|
||||
};
|
||||
|
||||
struct fuse_out {
|
||||
struct fuse_out_common c;
|
||||
size_t argsize;
|
||||
void *arg;
|
||||
};
|
||||
|
||||
struct fuse_dirent {
|
||||
unsigned long ino;
|
||||
unsigned short namelen;
|
||||
unsigned char type;
|
||||
char name[NAME_MAX + 1];
|
||||
char name[256];
|
||||
};
|
||||
|
||||
#define FUSE_NAME_OFFSET ((size_t) ((struct fuse_dirent *) 0)->name)
|
||||
#define FUSE_NAME_OFFSET ((unsigned int) ((struct fuse_dirent *) 0)->name)
|
||||
#define FUSE_DIRENT_ALIGN(x) (((x) + sizeof(long) - 1) & ~(sizeof(long) - 1))
|
||||
#define FUSE_DIRENT_SIZE(d) \
|
||||
FUSE_DIRENT_ALIGN(FUSE_NAME_OFFSET + (d)->namelen)
|
||||
@@ -0,0 +1,16 @@
|
||||
CC = gcc
|
||||
CFLAGS = -O2 -Wall -Wstrict-prototypes -fno-strict-aliasing -pipe
|
||||
CPPFLAGS = -I /lib/modules/`uname -r`/build/include/ -D__KERNEL__ -DMODULE -D_LOOSE_KERNEL_NAMES -I../include
|
||||
|
||||
|
||||
all: fuse.o
|
||||
|
||||
fuse_objs = dev.o inode.o dir.o util.o
|
||||
|
||||
fuse.o: $(fuse_objs)
|
||||
ld -r -o fuse.o $(fuse_objs)
|
||||
|
||||
|
||||
clean:
|
||||
rm -f *.o
|
||||
rm -f *~
|
||||
+176
-76
@@ -14,8 +14,8 @@
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/file.h>
|
||||
|
||||
#define ICSIZE sizeof(struct fuse_in_common)
|
||||
#define OCSIZE sizeof(struct fuse_out_common)
|
||||
#define IHSIZE sizeof(struct fuse_in_header)
|
||||
#define OHSIZE sizeof(struct fuse_out_header)
|
||||
|
||||
static struct proc_dir_entry *proc_fs_fuse;
|
||||
struct proc_dir_entry *proc_fuse_dev;
|
||||
@@ -26,7 +26,7 @@ static int request_wait_answer(struct fuse_req *req)
|
||||
DECLARE_WAITQUEUE(wait, current);
|
||||
|
||||
add_wait_queue(&req->waitq, &wait);
|
||||
while(!req->done) {
|
||||
while(!list_empty(&req->list)) {
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
if(signal_pending(current)) {
|
||||
ret = -EINTR;
|
||||
@@ -42,49 +42,123 @@ static int request_wait_answer(struct fuse_req *req)
|
||||
return ret;
|
||||
}
|
||||
|
||||
void request_send(struct fuse_conn *fc, struct fuse_in *in,
|
||||
struct fuse_out *out)
|
||||
static int request_check(struct fuse_req *req, struct fuse_out *outp)
|
||||
{
|
||||
struct fuse_out_header *oh;
|
||||
unsigned int size;
|
||||
|
||||
if(!req->out)
|
||||
return -ECONNABORTED;
|
||||
|
||||
oh = (struct fuse_out_header *) req->out;
|
||||
size = req->outsize - OHSIZE;
|
||||
|
||||
if (oh->result <= -512 || oh->result > 0) {
|
||||
printk("fuse: bad result\n");
|
||||
return -EPROTO;
|
||||
}
|
||||
|
||||
if(size > outp->argsize ||
|
||||
(oh->result == 0 && !outp->argvar && size != outp->argsize) ||
|
||||
(oh->result != 0 && size != 0)) {
|
||||
printk("fuse: invalid argument length: %i (%i)\n", size,
|
||||
req->opcode);
|
||||
return -EPROTO;
|
||||
}
|
||||
|
||||
memcpy(&outp->h, oh, OHSIZE);
|
||||
outp->argsize = size;
|
||||
if(size)
|
||||
memcpy(outp->arg, req->out + OHSIZE, size);
|
||||
|
||||
return oh->result;
|
||||
}
|
||||
|
||||
static void request_free(struct fuse_req *req)
|
||||
{
|
||||
kfree(req->in);
|
||||
kfree(req->out);
|
||||
kfree(req);
|
||||
}
|
||||
|
||||
|
||||
static struct fuse_req *request_new(struct fuse_conn *fc, struct fuse_in *inp,
|
||||
struct fuse_out *outp)
|
||||
{
|
||||
struct fuse_req *req;
|
||||
|
||||
req = kmalloc(sizeof(*req), GFP_KERNEL);
|
||||
if(!req)
|
||||
return NULL;
|
||||
|
||||
if(outp)
|
||||
req->outsize = OHSIZE + outp->argsize;
|
||||
else
|
||||
req->outsize = 0;
|
||||
req->out = NULL;
|
||||
|
||||
req->insize = IHSIZE + inp->argsize;
|
||||
req->in = kmalloc(req->insize, GFP_KERNEL);
|
||||
if(!req->in) {
|
||||
request_free(req);
|
||||
return NULL;
|
||||
}
|
||||
memcpy(req->in, &inp->h, IHSIZE);
|
||||
if(inp->argsize)
|
||||
memcpy(req->in + IHSIZE, inp->arg, inp->argsize);
|
||||
|
||||
req->opcode = inp->h.opcode;
|
||||
init_waitqueue_head(&req->waitq);
|
||||
|
||||
return req;
|
||||
}
|
||||
|
||||
/* If 'outp' is NULL then the request this is asynchronous */
|
||||
void request_send(struct fuse_conn *fc, struct fuse_in *inp,
|
||||
struct fuse_out *outp)
|
||||
{
|
||||
int ret;
|
||||
struct fuse_in_header *ih;
|
||||
struct fuse_req *req;
|
||||
|
||||
req = kmalloc(sizeof(*req), GFP_KERNEL);
|
||||
if(req == NULL) {
|
||||
out->result = -ENOMEM;
|
||||
return;
|
||||
}
|
||||
|
||||
req->in = in;
|
||||
req->out = out;
|
||||
req->done = 0;
|
||||
init_waitqueue_head(&req->waitq);
|
||||
|
||||
spin_lock(&fuse_lock);
|
||||
if(fc->file == NULL) {
|
||||
ret = -ENOTCONN;
|
||||
ret = -ENOMEM;
|
||||
req = request_new(fc, inp, outp);
|
||||
if(!req)
|
||||
goto out;
|
||||
}
|
||||
|
||||
req->in->h.unique = fc->reqctr ++;
|
||||
spin_lock(&fuse_lock);
|
||||
ret = -ENOTCONN;
|
||||
if(fc->file == NULL)
|
||||
goto out_unlock_free;
|
||||
|
||||
ih = (struct fuse_in_header *) req->in;
|
||||
if(outp) {
|
||||
do fc->reqctr++;
|
||||
while(!fc->reqctr);
|
||||
ih->unique = req->unique = fc->reqctr;
|
||||
}
|
||||
else
|
||||
ih->unique = req->unique = 0;
|
||||
|
||||
list_add_tail(&req->list, &fc->pending);
|
||||
fc->outstanding ++;
|
||||
/* FIXME: Wait until the number of outstanding requests drops
|
||||
below a certain level */
|
||||
wake_up(&fc->waitq);
|
||||
|
||||
/* Async reqests are freed in fuse_dev_read() */
|
||||
if(!outp)
|
||||
goto out_unlock;
|
||||
|
||||
ret = request_wait_answer(req);
|
||||
fc->outstanding --;
|
||||
list_del(&req->list);
|
||||
if(!ret)
|
||||
ret = request_check(req, outp);
|
||||
|
||||
out:
|
||||
kfree(req);
|
||||
out_unlock_free:
|
||||
request_free(req);
|
||||
out_unlock:
|
||||
spin_unlock(&fuse_lock);
|
||||
|
||||
if(ret)
|
||||
out->result = ret;
|
||||
else if (out->result <= -512 || out->result > 0) {
|
||||
printk("Bad result from client: %i\n", out->result);
|
||||
out->result = -EPROTO;
|
||||
}
|
||||
out:
|
||||
if(outp)
|
||||
outp->h.result = ret;
|
||||
}
|
||||
|
||||
static int request_wait(struct fuse_conn *fc)
|
||||
@@ -113,31 +187,38 @@ static int request_wait(struct fuse_conn *fc)
|
||||
static ssize_t fuse_dev_read(struct file *file, char *buf, size_t nbytes,
|
||||
loff_t *off)
|
||||
{
|
||||
ssize_t ret;
|
||||
int ret;
|
||||
struct fuse_conn *fc = file->private_data;
|
||||
struct fuse_req *req;
|
||||
size_t size;
|
||||
char *tmpbuf;
|
||||
unsigned int size;
|
||||
|
||||
if(fc->sb == NULL)
|
||||
return -EPERM;
|
||||
|
||||
spin_lock(&fuse_lock);
|
||||
ret = request_wait(fc);
|
||||
if(ret)
|
||||
goto err;
|
||||
|
||||
req = list_entry(fc->pending.next, struct fuse_req, list);
|
||||
size = ICSIZE + req->in->argsize;
|
||||
|
||||
ret = -EPROTO;
|
||||
size = req->insize;
|
||||
if(nbytes < size) {
|
||||
printk("fuse_dev_read: buffer too small (%i)\n", size);
|
||||
printk("fuse_dev_read[%i]: buffer too small\n", fc->id);
|
||||
ret = -EIO;
|
||||
goto err;
|
||||
}
|
||||
|
||||
tmpbuf = req->in;
|
||||
req->in = NULL;
|
||||
|
||||
list_del(&req->list);
|
||||
list_add_tail(&req->list, &fc->processing);
|
||||
if(req->outsize)
|
||||
list_add_tail(&req->list, &fc->processing);
|
||||
else
|
||||
request_free(req);
|
||||
spin_unlock(&fuse_lock);
|
||||
|
||||
if(copy_to_user(buf, &req->in->c, ICSIZE) ||
|
||||
copy_to_user(buf + ICSIZE, req->in->arg, req->in->argsize))
|
||||
if(copy_to_user(buf, tmpbuf, size))
|
||||
return -EFAULT;
|
||||
|
||||
return size;
|
||||
@@ -155,7 +236,7 @@ static struct fuse_req *request_find(struct fuse_conn *fc, unsigned int unique)
|
||||
list_for_each(entry, &fc->processing) {
|
||||
struct fuse_req *tmp;
|
||||
tmp = list_entry(entry, struct fuse_req, list);
|
||||
if(tmp->in->c.unique == unique) {
|
||||
if(tmp->unique == unique) {
|
||||
req = tmp;
|
||||
break;
|
||||
}
|
||||
@@ -167,44 +248,55 @@ static struct fuse_req *request_find(struct fuse_conn *fc, unsigned int unique)
|
||||
static ssize_t fuse_dev_write(struct file *file, const char *buf,
|
||||
size_t nbytes, loff_t *off)
|
||||
{
|
||||
ssize_t ret;
|
||||
struct fuse_conn *fc = file->private_data;
|
||||
struct fuse_req *req;
|
||||
struct fuse_out_common oc;
|
||||
char *tmpbuf;
|
||||
struct fuse_out_header *oh;
|
||||
|
||||
if(nbytes > 2 * PAGE_SIZE) {
|
||||
printk("fuse_dev_write: write is too long\n");
|
||||
return -EPROTO;
|
||||
if(!fc->sb)
|
||||
return -EPERM;
|
||||
|
||||
ret = -EIO;
|
||||
if(nbytes < OHSIZE || nbytes > OHSIZE + PAGE_SIZE) {
|
||||
printk("fuse_dev_write[%i]: write is short or long\n", fc->id);
|
||||
goto out;
|
||||
}
|
||||
|
||||
tmpbuf = (char *) __get_free_pages(GFP_KERNEL, 1);
|
||||
ret = -ENOMEM;
|
||||
tmpbuf = kmalloc(nbytes, GFP_KERNEL);
|
||||
if(!tmpbuf)
|
||||
return -ENOMEM;
|
||||
|
||||
goto out;
|
||||
|
||||
ret = -EFAULT;
|
||||
if(copy_from_user(tmpbuf, buf, nbytes))
|
||||
return -EFAULT;
|
||||
goto out_free;
|
||||
|
||||
spin_lock(&fuse_lock);
|
||||
req = request_find(fc, oc.unique);
|
||||
|
||||
if(req == NULL)
|
||||
printk("fuse_dev_write[%i]: unknown request: %i", fc->id,
|
||||
oc.unique);
|
||||
|
||||
|
||||
|
||||
else {
|
||||
struct fuse_outparam *out = ¶m.u.o;
|
||||
if(req->param.u.i.opcode == FUSE_OPEN && out->result == 0)
|
||||
out->u.open_internal.file = fget(out->u.open.fd);
|
||||
|
||||
req->param = param;
|
||||
req->done = 1;
|
||||
wake_up(&req->waitq);
|
||||
oh = (struct fuse_out_header *) tmpbuf;
|
||||
req = request_find(fc, oh->unique);
|
||||
if(req == NULL) {
|
||||
ret = -ENOENT;
|
||||
goto out_free_unlock;
|
||||
}
|
||||
list_del_init(&req->list);
|
||||
if(req->opcode == FUSE_GETDIR) {
|
||||
/* fget() needs to be done in this context */
|
||||
struct fuse_getdir_out *arg;
|
||||
arg = (struct fuse_getdir_out *) (tmpbuf + OHSIZE);
|
||||
arg->file = fget(arg->fd);
|
||||
}
|
||||
req->out = tmpbuf;
|
||||
req->outsize = nbytes;
|
||||
tmpbuf = NULL;
|
||||
ret = nbytes;
|
||||
wake_up(&req->waitq);
|
||||
out_free_unlock:
|
||||
spin_unlock(&fuse_lock);
|
||||
|
||||
return nbytes;
|
||||
out_free:
|
||||
kfree(tmpbuf);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
@@ -213,6 +305,9 @@ static unsigned int fuse_dev_poll(struct file *file, poll_table *wait)
|
||||
struct fuse_conn *fc = file->private_data;
|
||||
unsigned int mask = POLLOUT | POLLWRNORM;
|
||||
|
||||
if(!fc->sb)
|
||||
return -EPERM;
|
||||
|
||||
poll_wait(file, &fc->waitq, wait);
|
||||
|
||||
spin_lock(&fuse_lock);
|
||||
@@ -235,8 +330,9 @@ static struct fuse_conn *new_conn(void)
|
||||
init_waitqueue_head(&fc->waitq);
|
||||
INIT_LIST_HEAD(&fc->pending);
|
||||
INIT_LIST_HEAD(&fc->processing);
|
||||
fc->outstanding = 0;
|
||||
fc->reqctr = 0;
|
||||
fc->reqctr = 1;
|
||||
fc->cleared = NULL;
|
||||
fc->numcleared = 0;
|
||||
|
||||
spin_lock(&fuse_lock);
|
||||
fc->id = connctr ++;
|
||||
@@ -265,9 +361,10 @@ static void end_requests(struct list_head *head)
|
||||
struct fuse_req *req;
|
||||
req = list_entry(head->next, struct fuse_req, list);
|
||||
list_del_init(&req->list);
|
||||
req->done = 1;
|
||||
req->param.u.o.result = -ECONNABORTED;
|
||||
wake_up(&req->waitq);
|
||||
if(req->outsize)
|
||||
wake_up(&req->waitq);
|
||||
else
|
||||
request_free(req);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -279,6 +376,9 @@ static int fuse_dev_release(struct inode *inode, struct file *file)
|
||||
fc->file = NULL;
|
||||
end_requests(&fc->pending);
|
||||
end_requests(&fc->processing);
|
||||
kfree(fc->cleared);
|
||||
fc->cleared = NULL;
|
||||
fc->numcleared = 0;
|
||||
fuse_release_conn(fc);
|
||||
spin_unlock(&fuse_lock);
|
||||
return 0;
|
||||
+138
-66
@@ -38,36 +38,91 @@ static void init_inode(struct inode *inode, struct fuse_attr *attr)
|
||||
fuse_dir_init(inode);
|
||||
else if(S_ISLNK(inode->i_mode))
|
||||
fuse_symlink_init(inode);
|
||||
else
|
||||
else {
|
||||
fuse_special_init(inode);
|
||||
init_special_inode(inode, inode->i_mode, attr->rdev);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry)
|
||||
{
|
||||
struct fuse_conn *fc = dir->i_sb->u.generic_sbp;
|
||||
struct fuse_inparam in;
|
||||
struct fuse_outparam out;
|
||||
struct fuse_in in = FUSE_IN_INIT;
|
||||
struct fuse_out out = FUSE_OUT_INIT;
|
||||
struct fuse_lookup_out arg;
|
||||
struct inode *inode;
|
||||
|
||||
in.opcode = FUSE_LOOKUP;
|
||||
in.ino = dir->i_ino;
|
||||
strcpy(in.u.lookup.name, entry->d_name.name);
|
||||
|
||||
in.h.opcode = FUSE_LOOKUP;
|
||||
in.h.ino = dir->i_ino;
|
||||
in.argsize = entry->d_name.len + 1;
|
||||
in.arg = entry->d_name.name;
|
||||
out.argsize = sizeof(arg);
|
||||
out.arg = &arg;
|
||||
request_send(fc, &in, &out);
|
||||
|
||||
if(out.result)
|
||||
return ERR_PTR(out.result);
|
||||
if(out.h.result) {
|
||||
/* Negative dentries are not hashed */
|
||||
if(out.h.result == -ENOENT)
|
||||
return NULL;
|
||||
else
|
||||
return ERR_PTR(out.h.result);
|
||||
}
|
||||
|
||||
inode = iget(dir->i_sb, out.u.lookup.ino);
|
||||
inode = iget(dir->i_sb, arg.ino);
|
||||
if(!inode)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
init_inode(inode, &out.u.lookup.attr);
|
||||
|
||||
init_inode(inode, &arg.attr);
|
||||
d_add(entry, inode);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int fuse_mknod(struct inode *dir, struct dentry *entry, int mode,
|
||||
int rdev)
|
||||
{
|
||||
struct fuse_conn *fc = dir->i_sb->u.generic_sbp;
|
||||
struct fuse_in in = FUSE_IN_INIT;
|
||||
struct fuse_out out = FUSE_OUT_INIT;
|
||||
struct fuse_mknod_in *inarg;
|
||||
unsigned int insize;
|
||||
struct fuse_mknod_out outarg;
|
||||
struct inode *inode;
|
||||
|
||||
insize = offsetof(struct fuse_mknod_in, name) + entry->d_name.len + 1;
|
||||
inarg = kmalloc(insize, GFP_KERNEL);
|
||||
if(!inarg)
|
||||
return -ENOMEM;
|
||||
|
||||
inarg->mode = mode;
|
||||
inarg->rdev = rdev;
|
||||
strcpy(inarg->name, entry->d_name.name);
|
||||
|
||||
in.h.opcode = FUSE_MKNOD;
|
||||
in.h.ino = dir->i_ino;
|
||||
in.argsize = insize;
|
||||
in.arg = inarg;
|
||||
out.argsize = sizeof(outarg);
|
||||
out.arg = &outarg;
|
||||
request_send(fc, &in, &out);
|
||||
kfree(inarg);
|
||||
if(out.h.result)
|
||||
return out.h.result;
|
||||
|
||||
inode = iget(dir->i_sb, outarg.ino);
|
||||
if(!inode)
|
||||
return -ENOMEM;
|
||||
|
||||
init_inode(inode, &outarg.attr);
|
||||
d_add(entry, inode);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fuse_create(struct inode *dir, struct dentry *entry, int mode)
|
||||
{
|
||||
return fuse_mknod(dir, entry, mode, 0);
|
||||
}
|
||||
|
||||
static int fuse_permission(struct inode *inode, int mask)
|
||||
{
|
||||
|
||||
@@ -78,18 +133,20 @@ static int fuse_revalidate(struct dentry *dentry)
|
||||
{
|
||||
struct inode *inode = dentry->d_inode;
|
||||
struct fuse_conn *fc = inode->i_sb->u.generic_sbp;
|
||||
struct fuse_inparam in;
|
||||
struct fuse_outparam out;
|
||||
struct fuse_in in = FUSE_IN_INIT;
|
||||
struct fuse_out out = FUSE_OUT_INIT;
|
||||
struct fuse_getattr_out arg;
|
||||
|
||||
in.opcode = FUSE_GETATTR;
|
||||
in.ino = inode->i_ino;
|
||||
|
||||
in.h.opcode = FUSE_GETATTR;
|
||||
in.h.ino = inode->i_ino;
|
||||
out.argsize = sizeof(arg);
|
||||
out.arg = &arg;
|
||||
request_send(fc, &in, &out);
|
||||
|
||||
if(out.result == 0)
|
||||
change_attributes(inode, &out.u.getattr.attr);
|
||||
if(out.h.result == 0)
|
||||
change_attributes(inode, &arg.attr);
|
||||
|
||||
return out.result;
|
||||
return out.h.result;
|
||||
}
|
||||
|
||||
|
||||
@@ -118,7 +175,7 @@ static int fuse_readdir(struct file *file, void *dstbuf, filldir_t filldir)
|
||||
while(nbytes >= FUSE_NAME_OFFSET) {
|
||||
struct fuse_dirent *dirent = (struct fuse_dirent *) p;
|
||||
size_t reclen = FUSE_DIRENT_SIZE(dirent);
|
||||
int err;
|
||||
int over;
|
||||
if(dirent->namelen > NAME_MAX) {
|
||||
printk("fuse_readdir: name too long\n");
|
||||
ret = -EPROTO;
|
||||
@@ -127,16 +184,14 @@ static int fuse_readdir(struct file *file, void *dstbuf, filldir_t filldir)
|
||||
if(reclen > nbytes)
|
||||
break;
|
||||
|
||||
err = filldir(dstbuf, dirent->name, dirent->namelen,
|
||||
over = filldir(dstbuf, dirent->name, dirent->namelen,
|
||||
file->f_pos, dirent->ino, dirent->type);
|
||||
if(err) {
|
||||
ret = err;
|
||||
if(over)
|
||||
break;
|
||||
}
|
||||
|
||||
p += reclen;
|
||||
file->f_pos += reclen;
|
||||
nbytes -= reclen;
|
||||
ret ++;
|
||||
}
|
||||
|
||||
out:
|
||||
@@ -150,34 +205,33 @@ static int read_link(struct dentry *dentry, char **bufp)
|
||||
{
|
||||
struct inode *inode = dentry->d_inode;
|
||||
struct fuse_conn *fc = inode->i_sb->u.generic_sbp;
|
||||
struct fuse_in in;
|
||||
struct fuse_out out;
|
||||
struct fuse_in in = FUSE_IN_INIT;
|
||||
struct fuse_out out = FUSE_OUT_INIT;
|
||||
unsigned long page;
|
||||
|
||||
page = __get_free_page(GFP_KERNEL);
|
||||
if(!page)
|
||||
return -ENOMEM;
|
||||
|
||||
in.c.opcode = FUSE_READLINK;
|
||||
in.c.ino = inode->i_ino;
|
||||
in.argsize = 0;
|
||||
in.h.opcode = FUSE_READLINK;
|
||||
in.h.ino = inode->i_ino;
|
||||
out.arg = (void *) page;
|
||||
out.argsize = PAGE_SIZE;
|
||||
|
||||
out.argsize = PAGE_SIZE - 1;
|
||||
out.argvar = 1;
|
||||
request_send(fc, &in, &out);
|
||||
if(out.c.result) {
|
||||
__free_page(page);
|
||||
return out.c.result;
|
||||
if(out.h.result) {
|
||||
free_page(page);
|
||||
return out.h.result;
|
||||
}
|
||||
|
||||
*bufp = (char *) page;
|
||||
(*bufp)[PAGE_SIZE - 1] = 0;
|
||||
(*bufp)[out.argsize] = '\0';
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void free_link(char *link)
|
||||
{
|
||||
__free_page((unsigned long) link);
|
||||
free_page((unsigned long) link);
|
||||
}
|
||||
|
||||
static int fuse_readlink(struct dentry *dentry, char *buffer, int buflen)
|
||||
@@ -208,29 +262,31 @@ static int fuse_follow_link(struct dentry *dentry, struct nameidata *nd)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int fuse_open(struct inode *inode, struct file *file)
|
||||
static int fuse_dir_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct fuse_conn *fc = inode->i_sb->u.generic_sbp;
|
||||
struct fuse_inparam in;
|
||||
struct fuse_outparam out;
|
||||
struct file *cfile = NULL;
|
||||
|
||||
in.opcode = FUSE_OPEN;
|
||||
in.ino = inode->i_ino;
|
||||
in.u.open.flags = file->f_flags & ~O_EXCL;
|
||||
struct fuse_in in = FUSE_IN_INIT;
|
||||
struct fuse_out out = FUSE_OUT_INIT;
|
||||
struct fuse_getdir_out outarg;
|
||||
|
||||
request_send(fc, &in, &out);
|
||||
if(!(file->f_flags & O_DIRECTORY))
|
||||
return -EISDIR;
|
||||
|
||||
if(out.result == 0) {
|
||||
in.h.opcode = FUSE_GETDIR;
|
||||
in.h.ino = inode->i_ino;
|
||||
out.argsize = sizeof(outarg);
|
||||
out.arg = &outarg;
|
||||
request_send(fc, &in, &out);
|
||||
if(out.h.result == 0) {
|
||||
struct file *cfile = outarg.file;
|
||||
struct inode *inode;
|
||||
cfile = out.u.open_internal.file;
|
||||
if(!cfile) {
|
||||
printk("fuse_open: invalid container file\n");
|
||||
printk("fuse_getdir: invalid file\n");
|
||||
return -EPROTO;
|
||||
}
|
||||
inode = cfile->f_dentry->d_inode;
|
||||
if(!S_ISREG(inode->i_mode)) {
|
||||
printk("fuse_open: container is not a regular file\n");
|
||||
printk("fuse_getdir: not a regular file\n");
|
||||
fput(cfile);
|
||||
return -EPROTO;
|
||||
}
|
||||
@@ -238,28 +294,35 @@ static int fuse_open(struct inode *inode, struct file *file)
|
||||
file->private_data = cfile;
|
||||
}
|
||||
|
||||
return out.result;
|
||||
return out.h.result;
|
||||
}
|
||||
|
||||
static int fuse_release(struct inode *inode, struct file *file)
|
||||
static int fuse_dir_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct fuse_conn *fc = inode->i_sb->u.generic_sbp;
|
||||
struct file *cfile = file->private_data;
|
||||
struct fuse_inparam in;
|
||||
struct fuse_outparam out;
|
||||
|
||||
if(cfile)
|
||||
fput(cfile);
|
||||
if(!cfile)
|
||||
BUG();
|
||||
|
||||
fput(cfile);
|
||||
|
||||
in.opcode = FUSE_RELEASE;
|
||||
request_send(fc, &in, &out);
|
||||
|
||||
return out.result;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct inode_operations fuse_dir_inode_operations =
|
||||
{
|
||||
lookup: fuse_lookup,
|
||||
create: fuse_create,
|
||||
mknod: fuse_mknod,
|
||||
#if 0
|
||||
|
||||
link: fuse_link,
|
||||
unlink: fuse_unlink,
|
||||
symlink: fuse_symlink,
|
||||
mkdir: fuse_mkdir,
|
||||
rmdir: fuse_rmdir,
|
||||
rename: fuse_rename,
|
||||
#endif
|
||||
permission: fuse_permission,
|
||||
revalidate: fuse_revalidate,
|
||||
};
|
||||
@@ -267,12 +330,16 @@ static struct inode_operations fuse_dir_inode_operations =
|
||||
static struct file_operations fuse_dir_operations = {
|
||||
read: generic_read_dir,
|
||||
readdir: fuse_readdir,
|
||||
open: fuse_open,
|
||||
release: fuse_release,
|
||||
open: fuse_dir_open,
|
||||
release: fuse_dir_release,
|
||||
};
|
||||
|
||||
static struct inode_operations fuse_file_inode_operations =
|
||||
{
|
||||
static struct inode_operations fuse_file_inode_operations = {
|
||||
permission: fuse_permission,
|
||||
revalidate: fuse_revalidate,
|
||||
};
|
||||
|
||||
static struct inode_operations fuse_special_inode_operations = {
|
||||
permission: fuse_permission,
|
||||
revalidate: fuse_revalidate,
|
||||
};
|
||||
@@ -304,6 +371,11 @@ void fuse_symlink_init(struct inode *inode)
|
||||
inode->i_op = &fuse_symlink_inode_operations;
|
||||
}
|
||||
|
||||
void fuse_special_init(struct inode *inode)
|
||||
{
|
||||
inode->i_op = &fuse_special_inode_operations;
|
||||
}
|
||||
|
||||
/*
|
||||
* Local Variables:
|
||||
* indent-tabs-mode: t
|
||||
+44
-15
@@ -6,14 +6,15 @@
|
||||
See the file COPYING.
|
||||
*/
|
||||
|
||||
#include "fuse.h"
|
||||
|
||||
#include <linux/fuse.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
#define FUSE_VERSION "0.1"
|
||||
|
||||
#define MAX_CLEARED 256
|
||||
|
||||
/**
|
||||
* A Fuse connection.
|
||||
*
|
||||
@@ -37,8 +38,11 @@ struct fuse_conn {
|
||||
/** The list of requests being processed */
|
||||
struct list_head processing;
|
||||
|
||||
/** The number of outstanding requests */
|
||||
int outstanding;
|
||||
/** The number of cleared inodes */
|
||||
unsigned int numcleared;
|
||||
|
||||
/** The array of cleared inode numbers */
|
||||
unsigned long *cleared;
|
||||
|
||||
/** Connnection number (for debuging) */
|
||||
int id;
|
||||
@@ -54,26 +58,46 @@ struct fuse_req {
|
||||
/** The request list */
|
||||
struct list_head list;
|
||||
|
||||
/** The request input parameters */
|
||||
struct fuse_in *in;
|
||||
/** The request ID */
|
||||
int unique;
|
||||
|
||||
/** The request result */
|
||||
struct fuse_out *out;
|
||||
/** The opcode */
|
||||
enum fuse_opcode opcode;
|
||||
|
||||
/** The request input size */
|
||||
unsigned int insize;
|
||||
|
||||
/** The file returned by open */
|
||||
struct file *file;
|
||||
/** The request input */
|
||||
char *in;
|
||||
|
||||
/** The maximum request output size, if zero, then the request is
|
||||
asynchronous */
|
||||
unsigned int outsize;
|
||||
|
||||
/** The request output */
|
||||
char *out;
|
||||
|
||||
/** The request wait queue */
|
||||
wait_queue_head_t waitq;
|
||||
|
||||
/** True if the request is finished */
|
||||
int done;
|
||||
};
|
||||
|
||||
struct fuse_out_open_internal {
|
||||
file *file;
|
||||
|
||||
struct fuse_in {
|
||||
struct fuse_in_header h;
|
||||
unsigned int argsize;
|
||||
const void *arg;
|
||||
};
|
||||
|
||||
struct fuse_out {
|
||||
struct fuse_out_header h;
|
||||
unsigned int argsize;
|
||||
unsigned int argvar;
|
||||
void *arg;
|
||||
};
|
||||
|
||||
#define FUSE_IN_INIT { {0, 0, 0}, 0, 0 }
|
||||
#define FUSE_OUT_INIT { {0, 0}, 0, 0, 0 }
|
||||
|
||||
|
||||
/**
|
||||
* The proc entry for the client device ("/proc/fs/fuse/dev")
|
||||
@@ -100,6 +124,11 @@ void fuse_file_init(struct inode *inode);
|
||||
*/
|
||||
void fuse_symlink_init(struct inode *inode);
|
||||
|
||||
/**
|
||||
* Fill in the special inode operaions
|
||||
*/
|
||||
void fuse_special_init(struct inode *inode);
|
||||
|
||||
/**
|
||||
* Check if the connection can be released, and if yes, then free the
|
||||
* connection structure
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/file.h>
|
||||
|
||||
#define FUSE_SUPER_MAGIC 0x65735546
|
||||
@@ -20,6 +21,70 @@ static void fuse_read_inode(struct inode *inode)
|
||||
/* No op */
|
||||
}
|
||||
|
||||
static void send_forget(struct fuse_conn *fc, unsigned long *forget,
|
||||
unsigned int numforget)
|
||||
{
|
||||
struct fuse_in in = FUSE_IN_INIT;
|
||||
|
||||
in.h.opcode = FUSE_FORGET;
|
||||
in.h.ino = 0;
|
||||
in.argsize = numforget * sizeof(unsigned long);
|
||||
in.arg = forget;
|
||||
|
||||
request_send(fc, &in, NULL);
|
||||
}
|
||||
|
||||
static int alloc_cleared(struct fuse_conn *fc)
|
||||
{
|
||||
unsigned long *tmp;
|
||||
|
||||
spin_unlock(&fuse_lock);
|
||||
tmp = kmalloc(sizeof(unsigned long) * MAX_CLEARED, GFP_KERNEL);
|
||||
spin_lock(&fuse_lock);
|
||||
|
||||
if(!fc->file || fc->cleared != NULL)
|
||||
kfree(tmp);
|
||||
else if(!tmp)
|
||||
printk("fuse_clear_inode: Cannot allocate memory\n");
|
||||
else
|
||||
fc->cleared = tmp;
|
||||
|
||||
return fc->cleared != NULL;
|
||||
}
|
||||
|
||||
static unsigned long *add_cleared(struct fuse_conn *fc, unsigned long ino)
|
||||
{
|
||||
if(!fc->file || (!fc->cleared && !alloc_cleared(fc)))
|
||||
return NULL;
|
||||
|
||||
fc->cleared[fc->numcleared] = ino;
|
||||
fc->numcleared ++;
|
||||
|
||||
if(fc->numcleared == MAX_CLEARED) {
|
||||
unsigned long *tmp = fc->cleared;
|
||||
fc->cleared = NULL;
|
||||
fc->numcleared = 0;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void fuse_clear_inode(struct inode *inode)
|
||||
{
|
||||
struct fuse_conn *fc = inode->i_sb->u.generic_sbp;
|
||||
unsigned long *forget;
|
||||
|
||||
spin_lock(&fuse_lock);
|
||||
forget = add_cleared(fc, inode->i_ino);
|
||||
spin_unlock(&fuse_lock);
|
||||
|
||||
if(forget) {
|
||||
send_forget(fc, forget, MAX_CLEARED);
|
||||
kfree(forget);
|
||||
}
|
||||
}
|
||||
|
||||
static void fuse_put_super(struct super_block *sb)
|
||||
{
|
||||
struct fuse_conn *fc = sb->u.generic_sbp;
|
||||
@@ -33,6 +98,7 @@ static void fuse_put_super(struct super_block *sb)
|
||||
|
||||
static struct super_operations fuse_super_operations = {
|
||||
read_inode: fuse_read_inode,
|
||||
clear_inode: fuse_clear_inode,
|
||||
put_super: fuse_put_super,
|
||||
};
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
CC = gcc
|
||||
CFLAGS = -Wall -W -g `glib-config --cflags`
|
||||
LDFLAGS = `glib-config --libs`
|
||||
CPPFLAGS = -I../include
|
||||
|
||||
|
||||
all: libfuse.a
|
||||
|
||||
libfuse_objs = mount.o fuse.o
|
||||
|
||||
libfuse.a: $(libfuse_objs)
|
||||
ar cr libfuse.a $(libfuse_objs)
|
||||
|
||||
clean:
|
||||
rm -f *.o *.a
|
||||
rm -f *~
|
||||
+393
@@ -0,0 +1,393 @@
|
||||
/*
|
||||
FUSE: Filesystem in Userspace
|
||||
Copyright (C) 2001 Miklos Szeredi (mszeredi@inf.bme.hu)
|
||||
|
||||
This program can be distributed under the terms of the GNU GPL.
|
||||
See the file COPYING.
|
||||
*/
|
||||
|
||||
#include "fuse_i.h"
|
||||
#include <linux/fuse.h>
|
||||
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
|
||||
|
||||
static guint name_hash(const struct node *node)
|
||||
{
|
||||
return g_str_hash(node->name) ^ node->parent;
|
||||
}
|
||||
|
||||
static gint name_compare(const struct node *node1, const struct node *node2)
|
||||
{
|
||||
return
|
||||
node1->parent == node2->parent &&
|
||||
strcmp(node1->name, node2->name) == 0;
|
||||
}
|
||||
|
||||
static struct node *new_node(fino_t parent, const char *name)
|
||||
{
|
||||
struct node *node = g_new0(struct node, 1);
|
||||
node->name = strdup(name);
|
||||
node->parent = parent;
|
||||
return node;
|
||||
}
|
||||
|
||||
static int free_node(struct node *node)
|
||||
{
|
||||
g_free(node->name);
|
||||
g_free(node);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static inline struct node *get_node(fino_t ino)
|
||||
{
|
||||
return (struct node *) ino;
|
||||
}
|
||||
|
||||
static inline fino_t get_ino(struct node *node)
|
||||
{
|
||||
return (fino_t) node;
|
||||
}
|
||||
|
||||
|
||||
static fino_t find_node(struct fuse *f, fino_t parent, char *name, int create)
|
||||
{
|
||||
struct node *node;
|
||||
struct node tmp;
|
||||
|
||||
tmp.name = name;
|
||||
tmp.parent = parent;
|
||||
|
||||
node = g_hash_table_lookup(f->nametab, &tmp);
|
||||
if(node != NULL)
|
||||
return get_ino(node);
|
||||
|
||||
if(!create)
|
||||
return (fino_t) -1;
|
||||
|
||||
node = new_node(parent, name);
|
||||
g_hash_table_insert(f->nametab, node, node);
|
||||
return get_ino(node);
|
||||
}
|
||||
|
||||
static char *get_path(fino_t ino)
|
||||
{
|
||||
GString *s;
|
||||
char *ss;
|
||||
|
||||
s = g_string_new("");
|
||||
if(ino == FUSE_ROOT_INO)
|
||||
g_string_prepend_c(s, '/');
|
||||
else {
|
||||
struct node *node;
|
||||
for(; ino != FUSE_ROOT_INO; ino = node->parent) {
|
||||
node = get_node(ino);
|
||||
g_string_prepend(s, node->name);
|
||||
g_string_prepend_c(s, '/');
|
||||
}
|
||||
}
|
||||
|
||||
ss = s->str;
|
||||
g_string_free(s, FALSE);
|
||||
|
||||
return ss;
|
||||
}
|
||||
|
||||
static void remove_node(struct fuse *f, fino_t ino)
|
||||
{
|
||||
struct node *node = get_node(ino);
|
||||
g_hash_table_remove(f->nametab, node);
|
||||
free_node(node);
|
||||
}
|
||||
|
||||
|
||||
static void convert_stat(struct stat *stbuf, struct fuse_attr *attr)
|
||||
{
|
||||
attr->mode = stbuf->st_mode;
|
||||
attr->nlink = stbuf->st_nlink;
|
||||
attr->uid = stbuf->st_uid;
|
||||
attr->gid = stbuf->st_gid;
|
||||
attr->rdev = stbuf->st_rdev;
|
||||
attr->size = stbuf->st_size;
|
||||
attr->blksize = stbuf->st_blksize;
|
||||
attr->blocks = stbuf->st_blocks;
|
||||
attr->atime = stbuf->st_atime;
|
||||
attr->mtime = stbuf->st_mtime;
|
||||
attr->ctime = stbuf->st_ctime;
|
||||
}
|
||||
|
||||
static int get_attributes(struct fuse *f, fino_t ino, struct fuse_attr *attr)
|
||||
{
|
||||
char *path;
|
||||
struct stat buf;
|
||||
int res;
|
||||
|
||||
if(f->op.getattr == NULL)
|
||||
return -ENOSYS;
|
||||
|
||||
path = get_path(ino);
|
||||
res = f->op.getattr(path, &buf);
|
||||
g_free(path);
|
||||
if(res == 0)
|
||||
convert_stat(&buf, attr);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int read_link(struct fuse *f, fino_t ino, char *buf, size_t size)
|
||||
{
|
||||
char *path;
|
||||
int res;
|
||||
|
||||
if(f->op.readlink == NULL)
|
||||
return -ENOSYS;
|
||||
|
||||
path = get_path(ino);
|
||||
res = f->op.readlink(path, buf, size);
|
||||
g_free(path);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int fill_dir(struct fuse_dh *dh, char *name, int type)
|
||||
{
|
||||
struct fuse_dirent dirent;
|
||||
size_t reclen;
|
||||
size_t res;
|
||||
|
||||
dirent.ino = find_node(dh->fuse, dh->dir, name, 0);
|
||||
dirent.namelen = strlen(name);
|
||||
strncpy(dirent.name, name, sizeof(dirent.name));
|
||||
dirent.type = type;
|
||||
reclen = FUSE_DIRENT_SIZE(&dirent);
|
||||
res = fwrite(&dirent, reclen, 1, dh->fp);
|
||||
if(res == 0) {
|
||||
perror("writing directory file");
|
||||
return -EIO;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_dir(struct fuse *f, fino_t ino, FILE *fp)
|
||||
{
|
||||
char *path;
|
||||
int res;
|
||||
struct fuse_dh dh;
|
||||
|
||||
if(f->op.getdir == NULL)
|
||||
return -ENOSYS;
|
||||
|
||||
dh.fuse = f;
|
||||
dh.fp = fp;
|
||||
dh.dir = ino;
|
||||
|
||||
path = get_path(ino);
|
||||
res = f->op.getdir(path, &dh, (dirfiller_t) fill_dir);
|
||||
g_free(path);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
static void send_reply(struct fuse *f, struct fuse_in_header *in, int result,
|
||||
void *arg, size_t argsize)
|
||||
{
|
||||
int res;
|
||||
char *outbuf;
|
||||
size_t outsize;
|
||||
struct fuse_out_header *out;
|
||||
|
||||
if(result > 0) {
|
||||
fprintf(stderr, "positive result to operation %i : %i\n", in->opcode,
|
||||
result);
|
||||
result = -ERANGE;
|
||||
}
|
||||
|
||||
if(result != 0)
|
||||
argsize = 0;
|
||||
|
||||
outsize = sizeof(struct fuse_out_header) + argsize;
|
||||
outbuf = (char *) g_malloc(outsize);
|
||||
out = (struct fuse_out_header *) outbuf;
|
||||
out->unique = in->unique;
|
||||
out->result = result;
|
||||
if(argsize != 0)
|
||||
memcpy(outbuf + sizeof(struct fuse_out_header), arg, argsize);
|
||||
|
||||
printf(" unique: %i, result: %i (%s), outsize: %i\n", out->unique,
|
||||
out->result, strerror(-out->result), outsize);
|
||||
|
||||
res = write(f->fd, outbuf, outsize);
|
||||
if(res == -1)
|
||||
perror("writing fuse device");
|
||||
|
||||
g_free(outbuf);
|
||||
}
|
||||
|
||||
static void do_lookup(struct fuse *f, struct fuse_in_header *in, char *name)
|
||||
{
|
||||
int res;
|
||||
struct fuse_lookup_out arg;
|
||||
|
||||
arg.ino = find_node(f, in->ino, name, 1);
|
||||
res = get_attributes(f, arg.ino, &arg.attr);
|
||||
|
||||
send_reply(f, in, res, &arg, sizeof(arg));
|
||||
}
|
||||
|
||||
|
||||
static void do_forget(struct fuse *f, unsigned long *inos, size_t num)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for(i = 0; i < num; i++)
|
||||
remove_node(f, inos[i]);
|
||||
}
|
||||
|
||||
static void do_getattr(struct fuse *f, struct fuse_in_header *in)
|
||||
{
|
||||
int res;
|
||||
struct fuse_getattr_out arg;
|
||||
|
||||
res = get_attributes(f, in->ino, &arg.attr);
|
||||
send_reply(f, in, res, &arg, sizeof(arg));
|
||||
}
|
||||
|
||||
static void do_readlink(struct fuse *f, struct fuse_in_header *in)
|
||||
{
|
||||
int res;
|
||||
char link[PATH_MAX + 1];
|
||||
|
||||
res = read_link(f, in->ino, link, PATH_MAX + 1);
|
||||
send_reply(f, in, res, link, res == 0 ? strlen(link) : 0);
|
||||
}
|
||||
|
||||
static void do_mknod(struct fuse *f, struct fuse_in_header *in,
|
||||
struct fuse_mknod_in *inarg)
|
||||
{
|
||||
int res;
|
||||
struct fuse_mknod_out outarg;
|
||||
|
||||
res = -ENOSYS;
|
||||
if(f->op.mknod != NULL && f->op.getattr != NULL) {
|
||||
char *path;
|
||||
struct stat buf;
|
||||
|
||||
outarg.ino = find_node(f, in->ino, inarg->name, 1);
|
||||
path = get_path(outarg.ino);
|
||||
res = f->op.mknod(path, inarg->mode, inarg->rdev);
|
||||
if(res == 0)
|
||||
res = f->op.getattr(path, &buf);
|
||||
g_free(path);
|
||||
|
||||
if(res == 0)
|
||||
convert_stat(&buf, &outarg.attr);
|
||||
else
|
||||
remove_node(f, outarg.ino);
|
||||
}
|
||||
send_reply(f, in, res, &outarg, sizeof(outarg));
|
||||
}
|
||||
|
||||
static void do_getdir(struct fuse *f, struct fuse_in_header *in)
|
||||
{
|
||||
int res;
|
||||
struct fuse_getdir_out arg;
|
||||
FILE *fp = tmpfile();
|
||||
|
||||
res = get_dir(f, in->ino, fp);
|
||||
fflush(fp);
|
||||
arg.fd = fileno(fp);
|
||||
send_reply(f, in, res, &arg, sizeof(arg));
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
void fuse_loop(struct fuse *f)
|
||||
{
|
||||
int res;
|
||||
char inbuf[FUSE_MAX_IN];
|
||||
struct fuse_in_header *in = (struct fuse_in_header *) inbuf;
|
||||
void *inarg = inbuf + sizeof(struct fuse_in_header);
|
||||
size_t insize;
|
||||
size_t argsize;
|
||||
|
||||
while(1) {
|
||||
res = read(f->fd, inbuf, sizeof(inbuf));
|
||||
if(res == -1) {
|
||||
perror("reading fuse device");
|
||||
continue;
|
||||
}
|
||||
insize = res;
|
||||
|
||||
if(insize < sizeof(struct fuse_in_header)) {
|
||||
fprintf(stderr, "short read on fuse device\n");
|
||||
continue;
|
||||
}
|
||||
printf("unique: %i, opcode: %i, ino: %li, insize: %i (%i)\n",
|
||||
in->unique, in->opcode, in->ino, insize,
|
||||
g_hash_table_size(f->nametab));
|
||||
|
||||
argsize = insize - sizeof(struct fuse_in_header);
|
||||
|
||||
switch(in->opcode) {
|
||||
case FUSE_LOOKUP:
|
||||
do_lookup(f, in, (char *) inarg);
|
||||
break;
|
||||
|
||||
case FUSE_FORGET:
|
||||
do_forget(f, (unsigned long *) inarg,
|
||||
argsize / sizeof(unsigned long));
|
||||
break;
|
||||
|
||||
case FUSE_GETATTR:
|
||||
do_getattr(f, in);
|
||||
break;
|
||||
|
||||
case FUSE_READLINK:
|
||||
do_readlink(f, in);
|
||||
break;
|
||||
|
||||
case FUSE_GETDIR:
|
||||
do_getdir(f, in);
|
||||
break;
|
||||
|
||||
case FUSE_MKNOD:
|
||||
do_mknod(f, in, (struct fuse_mknod_in *) inarg);
|
||||
break;
|
||||
|
||||
default:
|
||||
fprintf(stderr, "Operation %i not implemented\n", in->opcode);
|
||||
/* No need to send reply to async requests */
|
||||
if(in->unique != 0)
|
||||
send_reply(f, in, -ENOSYS, NULL, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct fuse *fuse_new()
|
||||
{
|
||||
struct fuse *f = g_new0(struct fuse, 1);
|
||||
|
||||
f->fd = -1;
|
||||
f->dir = NULL;
|
||||
f->nametab = g_hash_table_new((GHashFunc) name_hash,
|
||||
(GCompareFunc) name_compare);
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
|
||||
void fuse_set_operations(struct fuse *f, const struct fuse_operations *op)
|
||||
{
|
||||
f->op = *op;
|
||||
}
|
||||
|
||||
void fuse_destroy(struct fuse *f)
|
||||
{
|
||||
fuse_unmount(f);
|
||||
g_hash_table_foreach_remove(f->nametab, (GHRFunc) free_node, NULL);
|
||||
g_hash_table_destroy(f->nametab);
|
||||
g_free(f);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
FUSE: Filesystem in Userspace
|
||||
Copyright (C) 2001 Miklos Szeredi (mszeredi@inf.bme.hu)
|
||||
|
||||
This program can be distributed under the terms of the GNU GPL.
|
||||
See the file COPYING.
|
||||
*/
|
||||
|
||||
#include "fuse.h"
|
||||
#include <glib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define FUSE_DEV "/proc/fs/fuse/dev"
|
||||
|
||||
typedef unsigned long fino_t;
|
||||
|
||||
struct node {
|
||||
char *name;
|
||||
fino_t parent;
|
||||
};
|
||||
|
||||
struct fuse {
|
||||
char *dir;
|
||||
int fd;
|
||||
struct fuse_operations op;
|
||||
GHashTable *nametab;
|
||||
};
|
||||
|
||||
struct fuse_dh {
|
||||
struct fuse *fuse;
|
||||
fino_t dir;
|
||||
FILE *fp;
|
||||
};
|
||||
+142
@@ -0,0 +1,142 @@
|
||||
/*
|
||||
FUSE: Filesystem in Userspace
|
||||
Copyright (C) 2001 Miklos Szeredi (mszeredi@inf.bme.hu)
|
||||
|
||||
This program can be distributed under the terms of the GNU GPL.
|
||||
See the file COPYING.
|
||||
*/
|
||||
|
||||
#include "fuse_i.h"
|
||||
#include <linux/fuse.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/mount.h>
|
||||
#include <mntent.h>
|
||||
|
||||
static int do_mount(const char *dev, const char *dir, const char *type, int fd)
|
||||
{
|
||||
int res;
|
||||
struct fuse_mount_data data;
|
||||
|
||||
data.version = FUSE_MOUNT_VERSION;
|
||||
data.fd = fd;
|
||||
|
||||
res = mount(dev, dir, type, MS_MGC_VAL | MS_NOSUID | MS_NODEV, &data);
|
||||
if(res == -1) {
|
||||
perror("mount failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void add_mntent(const char *dev, const char *dir, const char *type)
|
||||
{
|
||||
int res;
|
||||
FILE *fp;
|
||||
struct mntent ent;
|
||||
|
||||
fp = setmntent("/etc/mtab", "a");
|
||||
if(fp == NULL) {
|
||||
perror("setmntent");
|
||||
return;
|
||||
}
|
||||
|
||||
ent.mnt_fsname = (char *) dev;
|
||||
ent.mnt_dir = (char *) dir;
|
||||
ent.mnt_type = (char *) type;
|
||||
ent.mnt_opts = "rw,nosuid,nodev";
|
||||
ent.mnt_freq = 0;
|
||||
ent.mnt_passno = 0;
|
||||
res = addmntent(fp, & ent);
|
||||
if(res != 0)
|
||||
perror("addmntent");
|
||||
|
||||
endmntent(fp);
|
||||
|
||||
}
|
||||
|
||||
static void remove_mntent(const char *dir)
|
||||
{
|
||||
int res;
|
||||
FILE *fdold, *fdnew;
|
||||
struct mntent *entp;
|
||||
|
||||
fdold = setmntent("/etc/mtab", "r");
|
||||
if(fdold == NULL) {
|
||||
perror("/etc/mtab");
|
||||
return;
|
||||
}
|
||||
|
||||
fdnew = setmntent("/etc/mtab~", "w");
|
||||
if(fdnew == NULL) {
|
||||
perror("/etc/mtab~");
|
||||
return;
|
||||
}
|
||||
|
||||
do {
|
||||
entp = getmntent(fdold);
|
||||
if(entp != NULL && strcmp(entp->mnt_dir, dir) != 0) {
|
||||
res = addmntent(fdnew, entp);
|
||||
if(res != 0)
|
||||
perror("addmntent");
|
||||
}
|
||||
} while(entp != NULL);
|
||||
|
||||
endmntent(fdold);
|
||||
endmntent(fdnew);
|
||||
|
||||
res = rename("/etc/mtab~", "/etc/mtab");
|
||||
if(res == -1)
|
||||
perror("renameing /etc/mtab~ to /etc/mtab");
|
||||
}
|
||||
|
||||
int fuse_mount(struct fuse *f, const char *dir)
|
||||
{
|
||||
int res;
|
||||
const char *dev = FUSE_DEV;
|
||||
const char *type = "fuse";
|
||||
|
||||
if(f->dir != NULL)
|
||||
return 0;
|
||||
|
||||
f->dir = g_strdup(dir);
|
||||
f->fd = open(dev, O_RDWR);
|
||||
if(f->fd == -1) {
|
||||
perror(dev);
|
||||
return -1;
|
||||
}
|
||||
|
||||
res = do_mount(dev, dir, type, f->fd);
|
||||
if(res == -1)
|
||||
return -1;
|
||||
|
||||
add_mntent(dev, dir, type);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fuse_unmount(struct fuse *f)
|
||||
{
|
||||
int res;
|
||||
|
||||
if(f->dir == NULL)
|
||||
return 0;
|
||||
|
||||
close(f->fd);
|
||||
f->fd = -1;
|
||||
|
||||
res = umount(f->dir);
|
||||
if(res == -1)
|
||||
perror("umount failed");
|
||||
else
|
||||
remove_mntent(f->dir);
|
||||
|
||||
g_free(f->dir);
|
||||
f->dir = NULL;
|
||||
|
||||
return res;
|
||||
}
|
||||
Reference in New Issue
Block a user