This commit is contained in:
Miklos Szeredi
2001-10-28 19:44:14 +00:00
parent 90d8bef61c
commit 85c74fcdfd
15 changed files with 1235 additions and 624 deletions
+12 -21
View File
@@ -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
View File
@@ -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, &param, 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, &param, 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;
}
+125
View File
@@ -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;
}
+37
View File
@@ -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);
+37 -47
View File
@@ -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)
+16
View File
@@ -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
View File
@@ -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 = &param.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
View File
@@ -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
View File
@@ -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
+66
View File
@@ -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,
};
View File
+16
View File
@@ -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
View File
@@ -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);
}
+33
View File
@@ -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
View File
@@ -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;
}