x
diff --git a/fusepro.c b/fusepro.c
index 4f593e7..ff998bf 100644
--- a/fusepro.c
+++ b/fusepro.c
@@ -1,11 +1,17 @@
-#include <fuse.h>
+#ifdef linux
+/* For pread()/pwrite() */
+#define _XOPEN_SOURCE 500
+#endif
+#include <fuse.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <dirent.h>
#include <errno.h>
#include <signal.h>
+#include <utime.h>
+#include <fcntl.h>
static struct fuse *pro_fuse;
@@ -53,7 +59,7 @@
return res;
}
-static int pro_mknod(const char *path, int mode, int rdev)
+static int pro_mknod(const char *path, mode_t mode, dev_t rdev)
{
int res;
@@ -64,18 +70,7 @@
return 0;
}
-static int pro_symlink(const char *from, const char *to)
-{
- int res;
-
- res = symlink(from, to);
- if(res == -1)
- return -errno;
-
- return 0;
-}
-
-static int pro_mkdir(const char *path, int mode)
+static int pro_mkdir(const char *path, mode_t mode)
{
int res;
@@ -108,6 +103,17 @@
return 0;
}
+static int pro_symlink(const char *from, const char *to)
+{
+ int res;
+
+ res = symlink(from, to);
+ if(res == -1)
+ return -errno;
+
+ return 0;
+}
+
static int pro_rename(const char *from, const char *to)
{
int res;
@@ -130,6 +136,80 @@
return 0;
}
+static int pro_chmod(const char *path, mode_t mode)
+{
+ int res;
+
+ res = chmod(path, mode);
+ if(res == -1)
+ return -errno;
+
+ return 0;
+}
+
+static int pro_chown(const char *path, uid_t uid, gid_t gid)
+{
+ int res;
+
+ res = chown(path, uid, gid);
+ if(res == -1)
+ return -errno;
+
+ return 0;
+}
+
+static int pro_truncate(const char *path, off_t size)
+{
+ int res;
+
+ res = truncate(path, size);
+ if(res == -1)
+ return -errno;
+
+ return 0;
+}
+
+static int pro_utime(const char *path, struct utimbuf *buf)
+{
+ int res;
+
+ res = utime(path, buf);
+ if(res == -1)
+ return -errno;
+
+ return 0;
+}
+
+
+static int pro_open(const char *path, int flags)
+{
+ int res;
+
+ res = open(path, flags);
+ if(res == -1)
+ return -errno;
+
+ close(res);
+ return 0;
+}
+
+static int pro_pread(const char *path, char *buf, size_t size, off_t offset)
+{
+ int fd;
+ int res;
+
+ fd = open(path, 0);
+ if(fd == -1)
+ return -errno;
+
+ res = pread(fd, buf, size, offset);
+ if(res == -1)
+ res = -errno;
+
+ close(fd);
+ return res;
+}
+
static void exit_handler()
{
exit(0);
@@ -176,10 +256,17 @@
rmdir: pro_rmdir,
rename: pro_rename,
link: pro_link,
+ chmod: pro_chmod,
+ chown: pro_chown,
+ truncate: pro_truncate,
+ utime: pro_utime,
+ open: pro_open,
+ pread: pro_pread,
};
int main(int argc, char *argv[])
{
+ int res;
if(argc != 2) {
fprintf(stderr, "usage: %s mount_dir\n", argv[0]);
exit(1);
@@ -189,7 +276,10 @@
atexit(cleanup);
pro_fuse = fuse_new();
- fuse_mount(pro_fuse, argv[1]);
+ res = fuse_mount(pro_fuse, argv[1]);
+ if(res == -1)
+ exit(1);
+
fuse_set_operations(pro_fuse, &pro_oper);
fuse_loop(pro_fuse);
diff --git a/include/fuse.h b/include/fuse.h
index e18f606..bdc96a6 100644
--- a/include/fuse.h
+++ b/include/fuse.h
@@ -10,24 +10,30 @@
#include <sys/types.h>
#include <sys/stat.h>
+#include <utime.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 (*getattr) (const char *path, struct stat *stbuf);
int (*readlink) (const char *path, char *buf, size_t size);
- int (*getdir) (const char *path, struct fuse_dh *h, dirfiller_t filler);
- int (*mknod) (const char *path, int mode, int rdev);
- int (*mkdir) (const char *path, int mode);
- int (*unlink) (const char *path);
- int (*rmdir) (const char *path);
- int (*rename) (const char *from, const char *to);
- int (*symlink) (const char *from, const char *to);
- int (*link) (const char *from, const char *to);
+ int (*getdir) (const char *path, struct fuse_dh *h, dirfiller_t filler);
+ int (*mknod) (const char *path, mode_t mode, dev_t rdev);
+ int (*mkdir) (const char *path, mode_t mode);
+ int (*unlink) (const char *path);
+ int (*rmdir) (const char *path);
+ int (*symlink) (const char *from, const char *to);
+ int (*rename) (const char *from, const char *to);
+ int (*link) (const char *from, const char *to);
+ int (*chmod) (const char *path, mode_t mode);
+ int (*chown) (const char *path, uid_t uid, gid_t gid);
+ int (*truncate) (const char *path, off_t size);
+ int (*utime) (const char *path, struct utimbuf *buf);
+ int (*open) (const char *path, int flags);
+ int (*pread) (const char *path, char *buf, size_t size, off_t offset);
};
struct fuse *fuse_new();
diff --git a/include/linux/fuse.h b/include/linux/fuse.h
index 64a4af3..5fe008c 100644
--- a/include/linux/fuse.h
+++ b/include/linux/fuse.h
@@ -19,11 +19,11 @@
#define FUSE_ROOT_INO 1
struct fuse_attr {
- unsigned short mode;
- unsigned short nlink;
- unsigned short uid;
- unsigned short gid;
- unsigned short rdev;
+ unsigned int mode;
+ unsigned int nlink;
+ unsigned int uid;
+ unsigned int gid;
+ unsigned int rdev;
unsigned long long size;
unsigned long blksize;
unsigned long blocks;
@@ -32,19 +32,28 @@
unsigned long ctime;
};
+#define FATTR_MODE (1 << 0)
+#define FATTR_UID (1 << 1)
+#define FATTR_GID (1 << 2)
+#define FATTR_SIZE (1 << 3)
+#define FATTR_UTIME (1 << 4)
+
enum fuse_opcode {
FUSE_LOOKUP = 1,
FUSE_FORGET,
FUSE_GETATTR,
+ FUSE_SETATTR,
FUSE_READLINK,
+ FUSE_SYMLINK,
FUSE_GETDIR,
FUSE_MKNOD,
FUSE_MKDIR,
- FUSE_SYMLINK,
FUSE_UNLINK,
FUSE_RMDIR,
FUSE_RENAME,
FUSE_LINK,
+ FUSE_OPEN,
+ FUSE_READ,
};
/* Conservative buffer size for the client */
@@ -90,6 +99,20 @@
char name[1];
};
+struct fuse_setattr_in {
+ struct fuse_attr attr;
+ unsigned int valid;
+};
+
+struct fuse_open_in {
+ unsigned int flags;
+};
+
+struct fuse_read_in {
+ unsigned long long offset;
+ unsigned int size;
+};
+
struct fuse_in_header {
int unique;
enum fuse_opcode opcode;
diff --git a/kernel/Makefile b/kernel/Makefile
index f7769d5..4646162 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -5,7 +5,7 @@
all: fuse.o
-fuse_objs = dev.o inode.o dir.o util.o
+fuse_objs = dev.o inode.o dir.o file.o util.o
fuse.o: $(fuse_objs)
ld -r -o fuse.o $(fuse_objs)
diff --git a/kernel/dev.c b/kernel/dev.c
index d32589d..6a8d373 100644
--- a/kernel/dev.c
+++ b/kernel/dev.c
@@ -87,7 +87,7 @@
{
struct fuse_req *req;
- req = kmalloc(sizeof(*req), GFP_KERNEL);
+ req = kmalloc(sizeof(*req), GFP_NOFS);
if(!req)
return NULL;
@@ -98,7 +98,7 @@
req->out = NULL;
req->insize = IHSIZE + inp->argsize;
- req->in = kmalloc(req->insize, GFP_KERNEL);
+ req->in = kmalloc(req->insize, GFP_NOFS);
if(!req->in) {
request_free(req);
return NULL;
@@ -188,7 +188,7 @@
loff_t *off)
{
int ret;
- struct fuse_conn *fc = file->private_data;
+ struct fuse_conn *fc = DEV_FC(file);
struct fuse_req *req;
char *tmpbuf;
unsigned int size;
@@ -249,7 +249,7 @@
size_t nbytes, loff_t *off)
{
ssize_t ret;
- struct fuse_conn *fc = file->private_data;
+ struct fuse_conn *fc = DEV_FC(file);
struct fuse_req *req;
char *tmpbuf;
struct fuse_out_header *oh;
@@ -302,7 +302,7 @@
static unsigned int fuse_dev_poll(struct file *file, poll_table *wait)
{
- struct fuse_conn *fc = file->private_data;
+ struct fuse_conn *fc = DEV_FC(file);
unsigned int mask = POLLOUT | POLLWRNORM;
if(!fc->sb)
@@ -370,7 +370,7 @@
static int fuse_dev_release(struct inode *inode, struct file *file)
{
- struct fuse_conn *fc = file->private_data;
+ struct fuse_conn *fc = DEV_FC(file);
spin_lock(&fuse_lock);
fc->file = NULL;
diff --git a/kernel/dir.c b/kernel/dir.c
index b23def7..1dac0c8 100644
--- a/kernel/dir.c
+++ b/kernel/dir.c
@@ -10,9 +10,8 @@
#include <linux/module.h>
#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/file.h>
#include <linux/slab.h>
+#include <linux/file.h>
static struct inode_operations fuse_dir_inode_operations;
static struct inode_operations fuse_file_inode_operations;
@@ -20,13 +19,12 @@
static struct inode_operations fuse_special_inode_operations;
static struct file_operations fuse_dir_operations;
-static struct file_operations fuse_file_operations;
static struct dentry_operations fuse_dentry_opertations;
static void change_attributes(struct inode *inode, struct fuse_attr *attr)
{
- inode->i_mode = attr->mode;
+ inode->i_mode = (inode->i_mode & S_IFMT) + (attr->mode & 07777);
inode->i_nlink = attr->nlink;
inode->i_uid = attr->uid;
inode->i_gid = attr->gid;
@@ -38,29 +36,46 @@
inode->i_ctime = attr->ctime;
}
-void fuse_init_inode(struct inode *inode, struct fuse_attr *attr)
+static void fuse_init_inode(struct inode *inode, int mode, int rdev)
{
- change_attributes(inode, attr);
-
+ inode->i_mode = mode & S_IFMT;
if(S_ISREG(inode->i_mode)) {
inode->i_op = &fuse_file_inode_operations;
- inode->i_fop = &fuse_file_operations;
+ fuse_init_file_inode(inode);
}
else if(S_ISDIR(inode->i_mode)) {
inode->i_op = &fuse_dir_inode_operations;
inode->i_fop = &fuse_dir_operations;
}
- else if(S_ISLNK(inode->i_mode))
+ else if(S_ISLNK(inode->i_mode)) {
inode->i_op = &fuse_symlink_inode_operations;
+ }
else {
inode->i_op = &fuse_special_inode_operations;
- init_special_inode(inode, inode->i_mode, attr->rdev);
+ init_special_inode(inode, inode->i_mode, rdev);
}
+ inode->u.generic_ip = inode;
+}
+
+struct inode *fuse_iget(struct super_block *sb, ino_t ino,
+ struct fuse_attr *attr)
+{
+ struct inode *inode;
+
+ inode = iget(sb, ino);
+ if(inode) {
+ if(!inode->u.generic_ip)
+ fuse_init_inode(inode, attr->mode, attr->rdev);
+
+ change_attributes(inode, attr);
+ }
+
+ return inode;
}
static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry)
{
- struct fuse_conn *fc = dir->i_sb->u.generic_sbp;
+ struct fuse_conn *fc = INO_FC(dir);
struct fuse_in in = FUSE_IN_INIT;
struct fuse_out out = FUSE_OUT_INIT;
struct fuse_lookup_out arg;
@@ -76,11 +91,9 @@
inode = NULL;
if(!out.h.error) {
- inode = iget(dir->i_sb, arg.ino);
+ inode = fuse_iget(dir->i_sb, arg.ino, &arg.attr);
if(!inode)
return ERR_PTR(-ENOMEM);
-
- fuse_init_inode(inode, &arg.attr);
}
else if(out.h.error != -ENOENT)
return ERR_PTR(out.h.error);
@@ -94,7 +107,7 @@
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_conn *fc = INO_FC(dir);
struct fuse_in in = FUSE_IN_INIT;
struct fuse_out out = FUSE_OUT_INIT;
struct fuse_mknod_in *inarg;
@@ -123,11 +136,10 @@
if(out.h.error)
return out.h.error;
- inode = iget(dir->i_sb, outarg.ino);
+ inode = fuse_iget(dir->i_sb, outarg.ino, &outarg.attr);
if(!inode)
return -ENOMEM;
-
- fuse_init_inode(inode, &outarg.attr);
+
d_instantiate(entry, inode);
return 0;
@@ -141,7 +153,7 @@
static int fuse_mkdir(struct inode *dir, struct dentry *entry, int mode)
{
- struct fuse_conn *fc = dir->i_sb->u.generic_sbp;
+ struct fuse_conn *fc = INO_FC(dir);
struct fuse_in in = FUSE_IN_INIT;
struct fuse_out out = FUSE_OUT_INIT;
struct fuse_mkdir_in *inarg;
@@ -168,7 +180,7 @@
static int fuse_symlink(struct inode *dir, struct dentry *entry,
const char *link)
{
- struct fuse_conn *fc = dir->i_sb->u.generic_sbp;
+ struct fuse_conn *fc = INO_FC(dir);
struct fuse_in in = FUSE_IN_INIT;
struct fuse_out out = FUSE_OUT_INIT;
char *inarg;
@@ -195,7 +207,7 @@
static int fuse_remove(struct inode *dir, struct dentry *entry,
enum fuse_opcode op)
{
- struct fuse_conn *fc = dir->i_sb->u.generic_sbp;
+ struct fuse_conn *fc = INO_FC(dir);
struct fuse_in in = FUSE_IN_INIT;
struct fuse_out out = FUSE_OUT_INIT;
@@ -220,7 +232,7 @@
static int fuse_rename(struct inode *olddir, struct dentry *oldent,
struct inode *newdir, struct dentry *newent)
{
- struct fuse_conn *fc = olddir->i_sb->u.generic_sbp;
+ struct fuse_conn *fc = INO_FC(olddir);
struct fuse_in in = FUSE_IN_INIT;
struct fuse_out out = FUSE_OUT_INIT;
struct fuse_rename_in *inarg;
@@ -252,7 +264,7 @@
struct dentry *newent)
{
struct inode *inode = entry->d_inode;
- struct fuse_conn *fc = inode->i_sb->u.generic_sbp;
+ struct fuse_conn *fc = INO_FC(inode);
struct fuse_in in = FUSE_IN_INIT;
struct fuse_out out = FUSE_OUT_INIT;
struct fuse_link_in *inarg;
@@ -286,7 +298,7 @@
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_conn *fc = INO_FC(inode);
struct fuse_in in = FUSE_IN_INIT;
struct fuse_out out = FUSE_OUT_INIT;
struct fuse_getattr_out arg;
@@ -357,7 +369,7 @@
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_conn *fc = INO_FC(inode);
struct fuse_in in = FUSE_IN_INIT;
struct fuse_out out = FUSE_OUT_INIT;
unsigned long page;
@@ -417,7 +429,7 @@
static int fuse_dir_open(struct inode *inode, struct file *file)
{
- struct fuse_conn *fc = inode->i_sb->u.generic_sbp;
+ struct fuse_conn *fc = INO_FC(inode);
struct fuse_in in = FUSE_IN_INIT;
struct fuse_out out = FUSE_OUT_INIT;
struct fuse_getdir_out outarg;
@@ -453,15 +465,56 @@
static int fuse_dir_release(struct inode *inode, struct file *file)
{
struct file *cfile = file->private_data;
-
- if(!cfile)
- BUG();
-
fput(cfile);
return 0;
}
+static unsigned int iattr_to_fattr(struct iattr *iattr,
+ struct fuse_attr *fattr)
+{
+ unsigned int ivalid = iattr->ia_valid;
+ unsigned int fvalid = 0;
+
+ memset(fattr, 0, sizeof(*fattr));
+
+ if(ivalid & ATTR_MODE)
+ fvalid |= FATTR_MODE, fattr->mode = iattr->ia_mode;
+ if(ivalid & ATTR_UID)
+ fvalid |= FATTR_UID, fattr->uid = iattr->ia_uid;
+ if(ivalid & ATTR_GID)
+ fvalid |= FATTR_GID, fattr->gid = iattr->ia_gid;
+ if(ivalid & ATTR_SIZE)
+ fvalid |= FATTR_SIZE, fattr->size = iattr->ia_size;
+ /* You can only _set_ these together (they may change by themselves) */
+ if((ivalid & (ATTR_ATIME | ATTR_MTIME)) == (ATTR_ATIME | ATTR_MTIME)) {
+ fvalid |= FATTR_UTIME;
+ fattr->atime = iattr->ia_atime;
+ fattr->mtime = iattr->ia_mtime;
+ }
+
+ return fvalid;
+}
+
+static int fuse_setattr(struct dentry *entry, struct iattr *attr)
+{
+ struct inode *inode = entry->d_inode;
+ struct fuse_conn *fc = INO_FC(inode);
+ struct fuse_in in = FUSE_IN_INIT;
+ struct fuse_out out = FUSE_OUT_INIT;
+ struct fuse_setattr_in arg;
+
+ arg.valid = iattr_to_fattr(attr, &arg.attr);
+
+ in.h.opcode = FUSE_SETATTR;
+ in.h.ino = inode->i_ino;
+ in.argsize = sizeof(arg);
+ in.arg = &arg;
+ request_send(fc, &in, &out);
+
+ return out.h.error;
+}
+
static int fuse_dentry_revalidate(struct dentry *entry, int flags)
{
if(!entry->d_inode || !(flags & LOOKUP_CONTINUE))
@@ -481,9 +534,7 @@
rmdir: fuse_rmdir,
rename: fuse_rename,
link: fuse_link,
-#if 0
setattr: fuse_setattr,
-#endif
permission: fuse_permission,
revalidate: fuse_revalidate,
};
@@ -496,20 +547,20 @@
};
static struct inode_operations fuse_file_inode_operations = {
+ setattr: fuse_setattr,
permission: fuse_permission,
revalidate: fuse_revalidate,
};
static struct inode_operations fuse_special_inode_operations = {
+ setattr: fuse_setattr,
permission: fuse_permission,
revalidate: fuse_revalidate,
};
-static struct file_operations fuse_file_operations = {
-};
-
static struct inode_operations fuse_symlink_inode_operations =
{
+ setattr: fuse_setattr,
readlink: fuse_readlink,
follow_link: fuse_follow_link,
revalidate: fuse_revalidate,
diff --git a/kernel/file.c b/kernel/file.c
new file mode 100644
index 0000000..df3a863
--- /dev/null
+++ b/kernel/file.c
@@ -0,0 +1,88 @@
+/*
+ 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/module.h>
+#include <linux/kernel.h>
+#include <linux/pagemap.h>
+
+
+static int fuse_open(struct inode *inode, struct file *file)
+{
+ struct fuse_conn *fc = INO_FC(inode);
+ struct fuse_in in = FUSE_IN_INIT;
+ struct fuse_out out = FUSE_OUT_INIT;
+ struct fuse_open_in arg;
+
+ arg.flags = file->f_flags & ~O_EXCL;
+ in.h.opcode = FUSE_OPEN;
+ in.h.ino = inode->i_ino;
+ in.argsize = sizeof(arg);
+ in.arg = &arg;
+ request_send(fc, &in, &out);
+
+ return out.h.error;
+}
+
+static int fuse_readpage(struct file *file, struct page *page)
+{
+ struct inode *inode = page->mapping->host;
+ struct fuse_conn *fc = INO_FC(inode);
+ struct fuse_in in = FUSE_IN_INIT;
+ struct fuse_out out = FUSE_OUT_INIT;
+ struct fuse_read_in arg;
+ char *buffer;
+
+ buffer = kmap(page);
+
+ arg.offset = page->index << PAGE_CACHE_SHIFT;
+ arg.size = PAGE_CACHE_SIZE;
+
+ in.h.opcode = FUSE_READ;
+ in.h.ino = inode->i_ino;
+ in.argsize = sizeof(arg);
+ in.arg = &arg;
+ out.argsize = PAGE_CACHE_SIZE;
+ out.argvar = 1;
+ out.arg = buffer;
+
+ request_send(fc, &in, &out);
+ if(!out.h.error) {
+ if(out.argsize < PAGE_CACHE_SIZE)
+ memset(buffer + out.argsize, 0,
+ PAGE_CACHE_SIZE - out.argsize);
+ SetPageUptodate(page);
+ }
+
+ kunmap(page);
+ UnlockPage(page);
+
+ return out.h.error;
+}
+
+static struct file_operations fuse_file_operations = {
+ open: fuse_open,
+ read: generic_file_read,
+};
+
+static struct address_space_operations fuse_file_aops = {
+ readpage: fuse_readpage,
+};
+
+void fuse_init_file_inode(struct inode *inode)
+{
+ inode->i_fop = &fuse_file_operations;
+ inode->i_data.a_ops = &fuse_file_aops;
+}
+
+/*
+ * Local Variables:
+ * indent-tabs-mode: t
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/kernel/fuse_i.h b/kernel/fuse_i.h
index de0a9e6..aaa395d 100644
--- a/kernel/fuse_i.h
+++ b/kernel/fuse_i.h
@@ -82,6 +82,9 @@
};
+#define INO_FC(inode) ((struct fuse_conn *) (inode)->i_sb->u.generic_sbp)
+#define DEV_FC(file) ((struct fuse_conn *) (file)->private_data)
+
struct fuse_in {
struct fuse_in_header h;
unsigned int argsize;
@@ -111,9 +114,16 @@
/**
- * Initialize inode
+ * Get a filled in inode
*/
-void fuse_init_inode(struct inode *inode, struct fuse_attr *attr);
+struct inode *fuse_iget(struct super_block *sb, ino_t ino,
+ struct fuse_attr *attr);
+
+
+/**
+ * Initialise operations on regular file
+ */
+void fuse_init_file_inode(struct inode *inode);
/**
* Check if the connection can be released, and if yes, then free the
diff --git a/kernel/inode.c b/kernel/inode.c
index 702ef57..ed4f760 100644
--- a/kernel/inode.c
+++ b/kernel/inode.c
@@ -39,7 +39,7 @@
unsigned long *tmp;
spin_unlock(&fuse_lock);
- tmp = kmalloc(sizeof(unsigned long) * MAX_CLEARED, GFP_KERNEL);
+ tmp = kmalloc(sizeof(unsigned long) * MAX_CLEARED, GFP_NOFS);
spin_lock(&fuse_lock);
if(!fc->file || fc->cleared != NULL)
@@ -72,7 +72,7 @@
static void fuse_clear_inode(struct inode *inode)
{
- struct fuse_conn *fc = inode->i_sb->u.generic_sbp;
+ struct fuse_conn *fc = INO_FC(inode);
unsigned long *forget;
spin_lock(&fuse_lock);
@@ -139,17 +139,11 @@
static struct inode *get_root_inode(struct super_block *sb)
{
- struct inode *root;
+ struct fuse_attr attr;
+ memset(&attr, 0, sizeof(attr));
- root = iget(sb, 1);
- if(root) {
- struct fuse_attr attr;
- memset(&attr, 0, sizeof(attr));
- attr.mode = S_IFDIR;
- fuse_init_inode(root, &attr);
- }
-
- return root;
+ attr.mode = S_IFDIR;
+ return fuse_iget(sb, 1, &attr);
}
static struct super_block *fuse_read_super(struct super_block *sb,
diff --git a/lib/fuse.c b/lib/fuse.c
index f033e04..cd919ed 100644
--- a/lib/fuse.c
+++ b/lib/fuse.c
@@ -27,11 +27,12 @@
strcmp(node1->name, node2->name) == 0;
}
-static struct node *new_node(fino_t parent, const char *name)
+static struct node *new_node(fino_t parent, const char *name, int mode)
{
struct node *node = g_new0(struct node, 1);
node->name = g_strdup(name);
node->parent = parent;
+ node->mode = mode;
return node;
}
@@ -63,20 +64,41 @@
return g_hash_table_lookup(f->nametab, &tmp);
}
-static fino_t find_node(struct fuse *f, fino_t parent, char *name, int create)
+static void unhash_node(struct fuse *f, struct node *node)
+{
+ g_hash_table_remove(f->nametab, node);
+ g_free(node->name);
+ node->parent = 0;
+ node->name = NULL;
+}
+
+static fino_t find_node(struct fuse *f, fino_t parent, char *name, int mode)
+{
+ struct node *node;
+ mode &= S_IFMT;
+
+ node = lookup_node(f, parent, name);
+ if(node != NULL) {
+ if(node->mode == mode)
+ return get_ino(node);
+
+ unhash_node(f, node);
+ }
+
+ node = new_node(parent, name, mode);
+ g_hash_table_insert(f->nametab, node, node);
+ return get_ino(node);
+}
+
+static fino_t find_node_dir(struct fuse *f, fino_t parent, char *name)
{
struct node *node;
node = lookup_node(f, parent, name);
if(node != NULL)
return get_ino(node);
-
- if(!create)
+ else
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)
@@ -91,6 +113,10 @@
struct node *node;
for(; ino != FUSE_ROOT_INO; ino = node->parent) {
node = get_node(ino);
+ if(node->name == NULL) {
+ g_string_free(s, TRUE);
+ return NULL;
+ }
g_string_prepend(s, node->name);
g_string_prepend_c(s, '/');
}
@@ -104,32 +130,42 @@
static char *get_path_name(fino_t ino, const char *name)
{
- char *path = get_path(ino);
- char *path2 = g_strconcat(path, "/", name, NULL);
+ char *path;
+ char *path2;
+
+ path = get_path(ino);
+ if(path == NULL)
+ return NULL;
+
+ path2 = g_strconcat(path, "/", name, NULL);
g_free(path);
return path2;
}
-static void remove_node(struct fuse *f, fino_t ino)
+static void destroy_node(struct fuse *f, fino_t ino)
{
struct node *node = get_node(ino);
- g_hash_table_remove(f->nametab, node);
+ unhash_node(f, node);
free_node(node);
}
+static void remove_node(struct fuse *f, fino_t dir, const char *name)
+{
+ struct node *node = lookup_node(f, dir, name);
+ assert(node != NULL);
+ unhash_node(f, node);
+}
+
static void rename_node(struct fuse *f, fino_t olddir, const char *oldname,
fino_t newdir, const char *newname)
{
struct node *node = lookup_node(f, olddir, oldname);
struct node *newnode = lookup_node(f, newdir, newname);
-
assert(node != NULL);
- /* The overwritten node is left to dangle until deleted */
if(newnode != NULL)
- g_hash_table_remove(f->nametab, newnode);
+ unhash_node(f, newnode);
- /* The renamed node is not freed, since it's pointer is the key */
g_hash_table_remove(f->nametab, node);
g_free(node->name);
node->name = g_strdup(newname);
@@ -159,7 +195,7 @@
size_t reclen;
size_t res;
- dirent.ino = find_node(dh->fuse, dh->dir, name, 0);
+ dirent.ino = find_node_dir(dh->fuse, dh->dir, name);
dirent.namelen = strlen(name);
strncpy(dirent.name, name, sizeof(dirent.name));
dirent.type = type;
@@ -213,14 +249,17 @@
struct stat buf;
struct fuse_lookup_out arg;
+ res = -ENOENT;
path = get_path_name(in->ino, name);
- res = -ENOSYS;
- if(f->op.getattr)
- res = f->op.getattr(path, &buf);
- g_free(path);
+ if(path != NULL) {
+ res = -ENOSYS;
+ if(f->op.getattr)
+ res = f->op.getattr(path, &buf);
+ g_free(path);
+ }
if(res == 0) {
convert_stat(&buf, &arg.attr);
- arg.ino = find_node(f, in->ino, name, 1);
+ arg.ino = find_node(f, in->ino, name, arg.attr.mode);
}
send_reply(f, in, res, &arg, sizeof(arg));
}
@@ -230,7 +269,7 @@
size_t i;
for(i = 0; i < num; i++)
- remove_node(f, inos[i]);
+ destroy_node(f, inos[i]);
}
static void do_getattr(struct fuse *f, struct fuse_in_header *in)
@@ -240,30 +279,78 @@
struct stat buf;
struct fuse_getattr_out arg;
+ res = -ENOENT;
path = get_path(in->ino);
- res = -ENOSYS;
- if(f->op.getattr)
- res = f->op.getattr(path, &buf);
- g_free(path);
+ if(path != NULL) {
+ res = -ENOSYS;
+ if(f->op.getattr)
+ res = f->op.getattr(path, &buf);
+ g_free(path);
+ }
if(res == 0)
convert_stat(&buf, &arg.attr);
send_reply(f, in, res, &arg, sizeof(arg));
}
+static void do_setattr(struct fuse *f, struct fuse_in_header *in,
+ struct fuse_setattr_in *arg)
+{
+ int res;
+ char *path;
+ int valid = arg->valid;
+ struct fuse_attr *attr = &arg->attr;
+
+ res = -ENOENT;
+ path = get_path(in->ino);
+ if(path != NULL) {
+ res = 0;
+ if(!res && (valid & FATTR_MODE)) {
+ res = -ENOSYS;
+ if(f->op.chmod)
+ res = f->op.chmod(path, attr->mode);
+ }
+ if(!res && (valid & (FATTR_UID | FATTR_GID))) {
+ uid_t uid = (valid & FATTR_UID) ? attr->uid : (uid_t) -1;
+ gid_t gid = (valid & FATTR_GID) ? attr->gid : (gid_t) -1;
+
+ res = -ENOSYS;
+ if(f->op.chown)
+ res = f->op.chown(path, uid, gid);
+ }
+ if(!res && (valid & FATTR_SIZE)) {
+ res = -ENOSYS;
+ if(f->op.truncate)
+ res = f->op.truncate(path, attr->size);
+ }
+ if(!res && (valid & FATTR_UTIME)) {
+ struct utimbuf buf;
+ buf.actime = attr->atime;
+ buf.modtime = attr->mtime;
+ res = -ENOSYS;
+ if(f->op.utime)
+ res = f->op.utime(path, &buf);
+ }
+ g_free(path);
+ }
+ send_reply(f, in, res, NULL, 0);
+}
+
static void do_readlink(struct fuse *f, struct fuse_in_header *in)
{
int res;
char link[PATH_MAX + 1];
char *path;
- path = get_path(in->ino);
- res = -ENOSYS;
- if(f->op.readlink)
- res = f->op.readlink(path, link, sizeof(link));
- g_free(path);
-
- send_reply(f, in, res, link, res == 0 ? strlen(link) : 0);
+ res = -ENOENT;
+ path = get_path(in->ino);
+ if(path != NULL) {
+ res = -ENOSYS;
+ if(f->op.readlink)
+ res = f->op.readlink(path, link, sizeof(link));
+ g_free(path);
+ }
+ send_reply(f, in, res, link, !res ? strlen(link) : 0);
}
static void do_getdir(struct fuse *f, struct fuse_in_header *in)
@@ -277,12 +364,14 @@
dh.fp = tmpfile();
dh.dir = in->ino;
+ res = -ENOENT;
path = get_path(in->ino);
- res = -ENOSYS;
- if(f->op.getdir)
- res = f->op.getdir(path, &dh, (dirfiller_t) fill_dir);
- g_free(path);
-
+ if(path != NULL) {
+ res = -ENOSYS;
+ if(f->op.getdir)
+ res = f->op.getdir(path, &dh, (dirfiller_t) fill_dir);
+ g_free(path);
+ }
fflush(dh.fp);
arg.fd = fileno(dh.fp);
send_reply(f, in, res, &arg, sizeof(arg));
@@ -297,17 +386,20 @@
struct fuse_mknod_out outarg;
struct stat buf;
+ res = -ENOENT;
path = get_path_name(in->ino, inarg->name);
- res = -ENOSYS;
- if(f->op.mknod && f->op.getattr) {
- res = f->op.mknod(path, inarg->mode, inarg->rdev);
- if(res == 0)
- res = f->op.getattr(path, &buf);
+ if(path != NULL) {
+ res = -ENOSYS;
+ if(f->op.mknod && f->op.getattr) {
+ res = f->op.mknod(path, inarg->mode, inarg->rdev);
+ if(res == 0)
+ res = f->op.getattr(path, &buf);
+ }
+ g_free(path);
}
- g_free(path);
if(res == 0) {
convert_stat(&buf, &outarg.attr);
- outarg.ino = find_node(f, in->ino, inarg->name, 1);
+ outarg.ino = find_node(f, in->ino, inarg->name, outarg.attr.mode);
}
send_reply(f, in, res, &outarg, sizeof(outarg));
@@ -319,11 +411,14 @@
int res;
char *path;
+ res = -ENOENT;
path = get_path_name(in->ino, inarg->name);
- res = -ENOSYS;
- if(f->op.mkdir)
- res = f->op.mkdir(path, inarg->mode);
- g_free(path);
+ if(path != NULL) {
+ res = -ENOSYS;
+ if(f->op.mkdir)
+ res = f->op.mkdir(path, inarg->mode);
+ g_free(path);
+ }
send_reply(f, in, res, NULL, 0);
}
@@ -332,17 +427,22 @@
int res;
char *path;
+ res = -ENOENT;
path = get_path_name(in->ino, name);
- res = -ENOSYS;
- if(in->opcode == FUSE_UNLINK) {
- if(f->op.unlink)
- res = f->op.unlink(path);
+ if(path != NULL) {
+ res = -ENOSYS;
+ if(in->opcode == FUSE_UNLINK) {
+ if(f->op.unlink)
+ res = f->op.unlink(path);
+ }
+ else {
+ if(f->op.rmdir)
+ res = f->op.rmdir(path);
+ }
+ g_free(path);
}
- else {
- if(f->op.rmdir)
- res = f->op.rmdir(path);
- }
- g_free(path);
+ if(res == 0)
+ remove_node(f, in->ino, name);
send_reply(f, in, res, NULL, 0);
}
@@ -352,11 +452,14 @@
int res;
char *path;
+ res = -ENOENT;
path = get_path_name(in->ino, name);
- res = -ENOSYS;
- if(f->op.symlink)
- res = f->op.symlink(link, path);
- g_free(path);
+ if(path != NULL) {
+ res = -ENOSYS;
+ if(f->op.symlink)
+ res = f->op.symlink(link, path);
+ g_free(path);
+ }
send_reply(f, in, res, NULL, 0);
}
@@ -368,31 +471,90 @@
fino_t newdir = inarg->newdir;
char *oldname = inarg->names;
char *newname = inarg->names + strlen(oldname) + 1;
- char *oldpath = get_path_name(olddir, oldname);
- char *newpath = get_path_name(newdir, newname);
+ char *oldpath;
+ char *newpath;
- res = -ENOSYS;
- if(f->op.rename)
- res = f->op.rename(oldpath, newpath);
- if(res == 0)
- rename_node(f, olddir, oldname, newdir, newname);
+ res = -ENOENT;
+ oldpath = get_path_name(olddir, oldname);
+ if(oldpath != NULL) {
+ newpath = get_path_name(newdir, newname);
+ if(newpath != NULL) {
+ res = -ENOSYS;
+ if(f->op.rename)
+ res = f->op.rename(oldpath, newpath);
+ if(res == 0)
+ rename_node(f, olddir, oldname, newdir, newname);
+ g_free(newpath);
+ }
+ g_free(oldpath);
+ }
send_reply(f, in, res, NULL, 0);
}
static void do_link(struct fuse *f, struct fuse_in_header *in,
- struct fuse_link_in *inarg)
+ struct fuse_link_in *arg)
{
int res;
- char *oldpath = get_path(in->ino);
- char *newpath = get_path_name(inarg->newdir, inarg->name);
+ char *oldpath;
+ char *newpath;
- res = -ENOSYS;
- if(f->op.link)
- res = f->op.link(oldpath, newpath);
-
+ res = -ENOENT;
+ oldpath = get_path(in->ino);
+ if(oldpath != NULL) {
+ newpath = get_path_name(arg->newdir, arg->name);
+ if(newpath != NULL) {
+ res = -ENOSYS;
+ if(f->op.link)
+ res = f->op.link(oldpath, newpath);
+ g_free(newpath);
+ }
+ g_free(oldpath);
+ }
send_reply(f, in, res, NULL, 0);
}
+static void do_open(struct fuse *f, struct fuse_in_header *in,
+ struct fuse_open_in *arg)
+{
+ int res;
+ char *path;
+
+ res = -ENOENT;
+ path = get_path(in->ino);
+ if(path != NULL) {
+ res = -ENOSYS;
+ if(f->op.open)
+ res = f->op.open(path, arg->flags);
+ g_free(path);
+ }
+ send_reply(f, in, res, NULL, 0);
+}
+
+static void do_read(struct fuse *f, struct fuse_in_header *in,
+ struct fuse_read_in *arg)
+{
+ int res;
+ char *path;
+ char *buf = g_malloc(arg->size);
+ size_t size;
+
+ path = get_path(in->ino);
+ if(path != NULL) {
+ res = -ENOSYS;
+ if(f->op.pread)
+ res = f->op.pread(path, buf, arg->size, arg->offset);
+ g_free(path);
+ }
+
+ size = 0;
+ if(res > 0) {
+ size = res;
+ res = 0;
+ }
+
+ send_reply(f, in, res, buf, size);
+ g_free(buf);
+}
void fuse_loop(struct fuse *f)
{
@@ -435,6 +597,10 @@
do_getattr(f, in);
break;
+ case FUSE_SETATTR:
+ do_setattr(f, in, (struct fuse_setattr_in *) inarg);
+ break;
+
case FUSE_READLINK:
do_readlink(f, in);
break;
@@ -469,6 +635,14 @@
do_link(f, in, (struct fuse_link_in *) inarg);
break;
+ case FUSE_OPEN:
+ do_open(f, in, (struct fuse_open_in *) inarg);
+ break;
+
+ case FUSE_READ:
+ do_read(f, in, (struct fuse_read_in *) inarg);
+ break;
+
default:
fprintf(stderr, "Operation %i not implemented\n", in->opcode);
/* No need to send reply to async requests */
diff --git a/lib/fuse_i.h b/lib/fuse_i.h
index b3b907b..2d030ae 100644
--- a/lib/fuse_i.h
+++ b/lib/fuse_i.h
@@ -17,6 +17,7 @@
struct node {
char *name;
fino_t parent;
+ int mode;
};
struct fuse {
diff --git a/lib/mount.c b/lib/mount.c
index ace3c65..48d9d45 100644
--- a/lib/mount.c
+++ b/lib/mount.c
@@ -103,7 +103,6 @@
if(f->dir != NULL)
return 0;
- f->dir = g_strdup(dir);
f->fd = open(dev, O_RDWR);
if(f->fd == -1) {
perror(dev);
@@ -115,6 +114,7 @@
return -1;
add_mntent(dev, dir, type);
+ f->dir = g_strdup(dir);
return 0;
}