From: Miklos Szeredi Date: Tue, 4 Jan 2005 12:45:54 +0000 (+0000) Subject: interrupted request improvements X-Git-Tag: fuse_2_2_pre1~9 X-Git-Url: http://git.maquefel.me/?a=commitdiff_plain;h=0f62d7265ebab7807978ae8a206213d968bea9e4;p=qemu-gpiodev%2Flibfuse.git interrupted request improvements --- diff --git a/ChangeLog b/ChangeLog index 7b46d61..0395c38 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,23 @@ +2005-01-04 Miklos Szeredi + + * KERNEL: if request is interrupted, still keep reference to used + inode(s) and file, so that FORGET and RELEASE are not sent until + userspace finishes the request. + +2004-12-16 Miklos Szeredi + + * KERNEL ABI: update interface to make it independent of type + sizes. This will help on 64 bit architectures which can run + legacy 32 bit applications. + + * KERNEL ABI: add "len" field to request headers. This will allow + sending/receiving requests in multiple chunks. + + * KERNEL: handle file type change more intelligently + + * LIB: "-o debug" option should disable backgrounding (fix by + Fabien Reygrobellet) + 2004-12-13 Miklos Szeredi * KERNEL: invalidate dentry/attributes if interrupted request diff --git a/Filesystems b/Filesystems index 553b714..6e147e5 100644 --- a/Filesystems +++ b/Filesystems @@ -1,6 +1,6 @@ Name: OW -Author: Paul H. Alfille palfille at partners org +Author: Paul H. Alfille / palfille at partners org Homepage: http://owfs.sourceforge.net @@ -12,7 +12,7 @@ Description: ============================================================================== Name: FunFS (status: alpha) -Author: Michael Grigoriev (Net Integration Technologies) mag at luminal org +Author: Michael Grigoriev (Net Integration Technologies) / mag at luminal org Homepage: http://www.luminal.org/wiki/index.php/FunFS/FunFS @@ -24,7 +24,7 @@ Description: ============================================================================== Name: EncFS -Author: Valient Gough vgough at pobox com +Author: Valient Gough / vgough at pobox com Homepage: http://pobox.com/~vgough/encfs.html @@ -37,10 +37,12 @@ Description: ============================================================================== Name: FUSE-J -Author: Peter Levart peter.levart at select-tech si +Author: Peter Levart / peter.levart at select-tech si Download: http://www.select-tech.si/fuse/ +Alternate download: http://www.cl.cam.ac.uk/~tdm25/fuse-j/ + Description: FUSE-J provides Java binding for FUSE. It comes with the @@ -49,7 +51,7 @@ Description: ============================================================================== Name: SMB for FUSE -Author: Vincent Wagelaar vincent at ricardis tudelft nl +Author: Vincent Wagelaar / vincent at ricardis tudelft nl Homepage: http://hannibal.lr-s.tudelft.nl/~vincent/fusesmb/ @@ -61,7 +63,7 @@ Description: ============================================================================== Name: Run-Time-Access -Author: Bob Smith bsmith at linuxtoys org +Author: Bob Smith / bsmith at linuxtoys org Homepage: http://www.runtimeaccess.com @@ -76,7 +78,7 @@ Description: ============================================================================== Name: PhoneBook -Author: David McNab david at rebirthing co nz +Author: David McNab / david at rebirthing co nz Homepage: http://www.freenet.org.nz/phonebook @@ -91,7 +93,7 @@ Description: ============================================================================== Name: KIO Fuse Gateway -Author: Alexander Neundorf neundorf at kde org +Author: Alexander Neundorf / neundorf at kde org Homepage: http://kde.ground.cz/tiki-index.php?page=KIO+Fuse+Gateway @@ -104,7 +106,7 @@ Description: ============================================================================== Name: C# bindings -Author: Valient Gough vgough at pobox com +Author: Valient Gough / vgough at pobox com Homepage: http://pobox.com/~vgough/fuse-csharp.html @@ -117,7 +119,7 @@ Description: ============================================================================== Name: LUFS bridge (alpha) -Author: Miklos Szeredi miklos at szeredi hu +Author: Miklos Szeredi / miklos at szeredi hu Homepage: http://sourceforge.net/project/showfiles.php?group_id=121684&package_id=132803 @@ -130,7 +132,7 @@ Description: ============================================================================== Name: btfs (Bluetooth FileSystemMapping) -Author: Collin R. Mulliner collin at betaversion net +Author: Collin R. Mulliner / collin at betaversion net Homepage: http://www.mulliner.org/bluetooth/btfs.php @@ -144,7 +146,7 @@ Description: ============================================================================== Name: mcachefs -Author: Michael Still mikal at stillhq com +Author: Michael Still / mikal at stillhq com Homepage: http://lists.samba.org/archive/linux/2004-March/010211.html @@ -159,7 +161,7 @@ Description: ============================================================================== Name: Fusedav -Author: Lennart Poettering mzshfrqni at 0pointer de +Author: Lennart Poettering / mzshfrqni at 0pointer de Homepage: http://0pointer.de/lennart/projects/fusedav/ @@ -172,7 +174,7 @@ Description: ============================================================================== Name: RelFS -Author: Vincenzo Ciancia vincenzo_ml at yahoo it +Author: Vincenzo Ciancia / vincenzo_ml at yahoo it Homepage: http://relfs.sourceforge.net/ @@ -186,7 +188,7 @@ Description: ============================================================================== Name: GmailFS -Author: Richard Jones richard at jones name +Author: Richard Jones / richard at jones name Homepage: http://richard.jones.name/google-hacks/gmail-filesystem/gmail-filesystem.html @@ -200,7 +202,7 @@ Description: ============================================================================== Name: DataDraw -Author: Bill Cox bill at viasic com +Author: Bill Cox / bill at viasic com Homepage: http://www.viasic.com/opensource/ @@ -212,7 +214,7 @@ Description: ============================================================================== Name: gphoto2-fuse-fs -Author: Christopher Lester lester at hep phy cam ac uk +Author: Christopher Lester / lester at hep phy cam ac uk Homepage: http://www.hep.phy.cam.ac.uk/~lester/gphoto2-fuse-fs/ @@ -226,7 +228,7 @@ Description: ============================================================================== Name: cvsfs-fuse -Author: Patrick Frank pfrank at gmx de +Author: Patrick Frank / pfrank at gmx de Homepage: http://sourceforge.net/projects/cvsfs @@ -240,7 +242,7 @@ Description: ============================================================================== Name: Wayback (User-level Versioning File System for Linux) -Author: Brian Cornell techie at northwestern edu +Author: Brian Cornell / techie at northwestern edu Homepage: http://wayback.sourceforge.net/ @@ -254,3 +256,18 @@ Description: directory. ============================================================================== +Name: Trivial Rolebased Authorisation & Capability Statemachine (TRACS) + +Author: Rob J Meijer / rmeijer at xs4all nl + +Homepage: http://www.xs4all.nl/~rmeijer/tracs.html + +Description: + + This project is the first spin-off project of the Security Incident + Policy Enforcement System project. In the process of designing a + SIPES, the need was recognized for the implementation of an + authorisation server that provides functionality not provided by any + of the current authorisation solutions. + +============================================================================== diff --git a/kernel/dev.c b/kernel/dev.c index 3f2d85a..bfd2bd7 100644 --- a/kernel/dev.c +++ b/kernel/dev.c @@ -55,15 +55,6 @@ void fuse_request_free(struct fuse_req *req) kmem_cache_free(fuse_req_cachep, req); } -static int get_unique(struct fuse_conn *fc) -{ - fc->reqctr++; - /* zero is special */ - if (fc->reqctr == 0) - fc->reqctr = 1; - return fc->reqctr; -} - #ifdef KERNEL_2_6 static inline void block_sigs(sigset_t *oldset) { @@ -112,6 +103,7 @@ static void __fuse_get_request(struct fuse_req *req) /* Must be called with > 1 refcount */ static void __fuse_put_request(struct fuse_req *req) { + BUG_ON(atomic_read(&req->count) < 2); atomic_dec(&req->count); } @@ -134,7 +126,7 @@ static struct fuse_req *do_get_request(struct fuse_conn *fc) struct fuse_req *fuse_get_request(struct fuse_conn *fc) { - if (down_interruptible(&fc->unused_sem)) + if (down_interruptible(&fc->outstanding_sem)) return NULL; return do_get_request(fc); } @@ -145,7 +137,7 @@ struct fuse_req *fuse_get_request_nonint(struct fuse_conn *fc) sigset_t oldset; block_sigs(&oldset); - intr = down_interruptible(&fc->unused_sem); + intr = down_interruptible(&fc->outstanding_sem); restore_sigs(&oldset); return intr ? NULL : do_get_request(fc); } @@ -154,12 +146,16 @@ void fuse_putback_request(struct fuse_conn *fc, struct fuse_req *req) { if (!req->preallocated) fuse_request_free(req); - else { - spin_lock(&fuse_lock); + + spin_lock(&fuse_lock); + if (req->preallocated) list_add(&req->list, &fc->unused_list); - spin_unlock(&fuse_lock); - up(&fc->unused_sem); - } + + if (fc->outstanding_debt) + fc->outstanding_debt--; + else + up(&fc->outstanding_sem); + spin_unlock(&fuse_lock); } void fuse_put_request(struct fuse_conn *fc, struct fuse_req *req) @@ -175,6 +171,14 @@ static void request_end(struct fuse_conn *fc, struct fuse_req *req) req->finished = 1; putback = atomic_dec_and_test(&req->count); spin_unlock(&fuse_lock); + if (req->background) { + if (req->inode) + iput(req->inode); + if (req->inode2) + iput(req->inode2); + if (req->file) + fput(req->file); + } wake_up(&req->waitq); if (putback) fuse_putback_request(fc, req); @@ -190,9 +194,21 @@ static int request_wait_answer_nonint(struct fuse_req *req) return err; } -/* Called with fuse_lock held. Releases, and then reaquires it. */ -static void request_wait_answer(struct fuse_req *req, int interruptible, - int background) +static void background_request(struct fuse_req *req) +{ + /* Need to get hold of the inode(s) and/or file used in the + request, so FORGET and RELEASE are not sent too early */ + req->background = 1; + if (req->inode) + req->inode = igrab(req->inode); + if (req->inode2) + req->inode2 = igrab(req->inode2); + if (req->file) + get_file(req->file); +} + +/* Called with fuse_lock held. Releases, and then reacquires it. */ +static void request_wait_answer(struct fuse_req *req, int interruptible) { int intr; @@ -212,10 +228,6 @@ static void request_wait_answer(struct fuse_req *req, int interruptible, if (!intr) return; - if (background && !req->sent) { - req->isreply = 0; - return; - } if (!interruptible || req->sent) req->out.h.error = -EINTR; else @@ -232,50 +244,74 @@ static void request_wait_answer(struct fuse_req *req, int interruptible, wait_event(req->waitq, !req->locked); spin_lock(&fuse_lock); } - if (!list_empty(&req->list)) { - /* request is still on one of the lists */ + if (!req->sent && !list_empty(&req->list)) { list_del(&req->list); __fuse_put_request(req); + } else if (req->sent) + background_request(req); +} + +static unsigned len_args(unsigned numargs, struct fuse_arg *args) +{ + unsigned nbytes = 0; + unsigned i; + + for (i = 0; i < numargs; i++) + nbytes += args[i].size; + + return nbytes; +} + +static void queue_request(struct fuse_conn *fc, struct fuse_req *req) +{ + fc->reqctr++; + /* zero is special */ + if (fc->reqctr == 0) + fc->reqctr = 1; + req->in.h.unique = fc->reqctr; + req->in.h.len = sizeof(struct fuse_in_header) + + len_args(req->in.numargs, (struct fuse_arg *) req->in.args); + if (!req->preallocated) { + /* decrease outstanding_sem, but without blocking... */ + if (down_trylock(&fc->outstanding_sem)) + fc->outstanding_debt++; } + list_add_tail(&req->list, &fc->pending); + wake_up(&fc->waitq); } static void request_send_wait(struct fuse_conn *fc, struct fuse_req *req, - int interruptible, int background) + int interruptible) { req->isreply = 1; spin_lock(&fuse_lock); req->out.h.error = -ENOTCONN; if (fc->file) { - req->in.h.unique = get_unique(fc); - list_add_tail(&req->list, &fc->pending); - wake_up(&fc->waitq); + queue_request(fc, req); /* acquire extra reference, since request is still needed after request_end() */ __fuse_get_request(req); - request_wait_answer(req, interruptible, background); + request_wait_answer(req, interruptible); } spin_unlock(&fuse_lock); } void request_send(struct fuse_conn *fc, struct fuse_req *req) { - request_send_wait(fc, req, 1, 0); + request_send_wait(fc, req, 1); } -void request_send_nonint(struct fuse_conn *fc, struct fuse_req *req, - int background) +void request_send_nonint(struct fuse_conn *fc, struct fuse_req *req) { - request_send_wait(fc, req, 0, background); + request_send_wait(fc, req, 0); } -void request_send_noreply(struct fuse_conn *fc, struct fuse_req *req) +void request_send_nowait(struct fuse_conn *fc, struct fuse_req *req) { - req->isreply = 0; spin_lock(&fuse_lock); if (fc->file) { - list_add_tail(&req->list, &fc->pending); - wake_up(&fc->waitq); + queue_request(fc, req); spin_unlock(&fuse_lock); } else { req->out.h.error = -ENOTCONN; @@ -283,6 +319,19 @@ void request_send_noreply(struct fuse_conn *fc, struct fuse_req *req) } } +void request_send_noreply(struct fuse_conn *fc, struct fuse_req *req) +{ + req->isreply = 0; + request_send_nowait(fc, req); +} + +void request_send_background(struct fuse_conn *fc, struct fuse_req *req) +{ + req->isreply = 1; + background_request(req); + request_send_nowait(fc, req); +} + static inline int lock_request(struct fuse_req *req) { int err = 0; @@ -486,17 +535,6 @@ static int fuse_copy_args(struct fuse_copy_state *cs, unsigned numargs, return err; } -static unsigned len_args(unsigned numargs, struct fuse_arg *args) -{ - unsigned nbytes = 0; - unsigned i; - - for (i = 0; i < numargs; i++) - nbytes += args[i].size; - - return nbytes; -} - static void request_wait(struct fuse_conn *fc) { DECLARE_WAITQUEUE(wait, current); @@ -544,8 +582,7 @@ static ssize_t fuse_dev_readv(struct file *file, const struct iovec *iov, spin_unlock(&fuse_lock); in = &req->in; - reqsize = sizeof(struct fuse_in_header); - reqsize += len_args(in->numargs, (struct fuse_arg *) in->args); + reqsize = req->in.h.len; nbytes = fuse_copy_init(&cs, 1, req, iov, nr_segs); err = -EINVAL; if (nbytes >= reqsize) { @@ -652,16 +689,22 @@ static ssize_t fuse_dev_writev(struct file *file, const struct iovec *iov, if (err) goto err_finish; err = -EINVAL; - if (!oh.unique || oh.error <= -1000 || oh.error > 0) + if (!oh.unique || oh.error <= -1000 || oh.error > 0 || + oh.len != nbytes) goto err_finish; spin_lock(&fuse_lock); req = request_find(fc, oh.unique); - err = -ENOENT; + err = -EINVAL; if (!req) goto err_unlock; list_del_init(&req->list); + if (req->interrupted) { + request_end(fc, req); + fuse_copy_finish(&cs); + return -ENOENT; + } req->out.h = oh; req->locked = 1; cs.req = req; diff --git a/kernel/dir.c b/kernel/dir.c index 19aebb1..08427a9 100644 --- a/kernel/dir.c +++ b/kernel/dir.c @@ -17,159 +17,15 @@ #endif #include -static struct inode_operations fuse_dir_inode_operations; -static struct inode_operations fuse_file_inode_operations; -static struct inode_operations fuse_symlink_inode_operations; -static struct file_operations fuse_dir_operations; -static struct dentry_operations fuse_dentry_operations; - -#ifndef KERNEL_2_6 -#define new_decode_dev(x) (x) -#define new_encode_dev(x) (x) -#endif -static void change_attributes(struct inode *inode, struct fuse_attr *attr) -{ - if (S_ISREG(inode->i_mode) && i_size_read(inode) != attr->size) { -#ifdef KERNEL_2_6 - invalidate_inode_pages(inode->i_mapping); -#else - invalidate_inode_pages(inode); -#endif - } - - inode->i_ino = attr->ino; - 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; - i_size_write(inode, attr->size); - inode->i_blksize = PAGE_CACHE_SIZE; - inode->i_blocks = attr->blocks; -#ifdef KERNEL_2_6 - inode->i_atime.tv_sec = attr->atime; - inode->i_atime.tv_nsec = attr->atimensec; - inode->i_mtime.tv_sec = attr->mtime; - inode->i_mtime.tv_nsec = attr->mtimensec; - inode->i_ctime.tv_sec = attr->ctime; - inode->i_ctime.tv_nsec = attr->ctimensec; -#else - inode->i_atime = attr->atime; - inode->i_mtime = attr->mtime; - inode->i_ctime = attr->ctime; -#endif -} - -static void fuse_init_inode(struct inode *inode, struct fuse_attr *attr) -{ - inode->i_mode = attr->mode & S_IFMT; - i_size_write(inode, attr->size); - if (S_ISREG(inode->i_mode)) { - inode->i_op = &fuse_file_inode_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)) { - inode->i_op = &fuse_symlink_inode_operations; - } - else if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode) || - S_ISFIFO(inode->i_mode) || S_ISSOCK(inode->i_mode)) { - inode->i_op = &fuse_file_inode_operations; - init_special_inode(inode, inode->i_mode, - new_decode_dev(attr->rdev)); - } else { - /* Don't let user create weird files */ - inode->i_mode = S_IFREG; - inode->i_op = &fuse_file_inode_operations; - fuse_init_file_inode(inode); - } -} - -#ifdef KERNEL_2_6 -static int fuse_inode_eq(struct inode *inode, void *_nodeidp) -{ - unsigned long nodeid = *(unsigned long *) _nodeidp; - if (get_node_id(inode) == nodeid) - return 1; - else - return 0; -} - -static int fuse_inode_set(struct inode *inode, void *_nodeidp) -{ - unsigned long nodeid = *(unsigned long *) _nodeidp; - get_fuse_inode(inode)->nodeid = nodeid; - return 0; -} - -struct inode *fuse_iget(struct super_block *sb, unsigned long nodeid, - int generation, struct fuse_attr *attr, int version) -{ - struct inode *inode; - struct fuse_conn *fc = get_fuse_conn_super(sb); - - inode = iget5_locked(sb, nodeid, fuse_inode_eq, fuse_inode_set, &nodeid); - if (!inode) - return NULL; - - if ((inode->i_state & I_NEW)) { - inode->i_generation = generation; - inode->i_data.backing_dev_info = &fc->bdi; - fuse_init_inode(inode, attr); - unlock_new_inode(inode); - } - - change_attributes(inode, attr); - inode->i_version = version; - return inode; -} - -struct inode *fuse_ilookup(struct super_block *sb, unsigned long nodeid) +static inline unsigned long time_to_jiffies(unsigned long sec, + unsigned long nsec) { - return ilookup5(sb, nodeid, fuse_inode_eq, &nodeid); -} -#else -static int fuse_inode_eq(struct inode *inode, unsigned long ino, void *_nodeidp){ - unsigned long nodeid = *(unsigned long *) _nodeidp; - if (inode->u.generic_ip && get_node_id(inode) == nodeid) - return 1; - else + /* prevent wrapping of jiffies */ + if (sec + 1 >= LONG_MAX / HZ) return 0; -} - -struct inode *fuse_iget(struct super_block *sb, unsigned long nodeid, - int generation, struct fuse_attr *attr, int version) -{ - struct inode *inode; - - inode = iget4(sb, attr->ino, fuse_inode_eq, &nodeid); - if (!inode) - return NULL; - if (!inode->u.generic_ip) { - get_fuse_inode(inode)->nodeid = nodeid; - inode->u.generic_ip = inode; - inode->i_generation = generation; - fuse_init_inode(inode, attr); - } - - change_attributes(inode, attr); - inode->i_version = version; - return inode; -} - -struct inode *fuse_ilookup(struct super_block *sb, ino_t ino, unsigned long nodeid) -{ - struct inode *inode = iget4(sb, ino, fuse_inode_eq, &nodeid); - if (inode && !inode->u.generic_ip) { - iput(inode); - inode = NULL; - } - return inode; + return jiffies + sec * HZ + nsec / (1000000000 / HZ); } -#endif static void fuse_lookup_init(struct fuse_req *req, struct inode *dir, struct dentry *entry, @@ -177,6 +33,7 @@ static void fuse_lookup_init(struct fuse_req *req, struct inode *dir, { req->in.h.opcode = FUSE_LOOKUP; req->in.h.nodeid = get_node_id(dir); + req->inode = dir; req->in.numargs = 1; req->in.args[0].size = entry->d_name.len + 1; req->in.args[0].value = entry->d_name.name; @@ -185,15 +42,53 @@ static void fuse_lookup_init(struct fuse_req *req, struct inode *dir, req->out.args[0].value = outarg; } -static inline unsigned long time_to_jiffies(unsigned long sec, - unsigned long nsec) +static int fuse_dentry_revalidate(struct dentry *entry, struct nameidata *nd) { - /* prevent wrapping of jiffies */ - if (sec + 1 >= LONG_MAX / HZ) + if (!entry->d_inode || is_bad_inode(entry->d_inode)) return 0; + else if (entry->d_time && time_after(jiffies, entry->d_time)) { + int err; + int version; + struct fuse_entry_out outarg; + struct inode *inode = entry->d_inode; + struct fuse_inode *fi = get_fuse_inode(inode); + struct fuse_conn *fc = get_fuse_conn(inode); + struct fuse_req *req = fuse_get_request_nonint(fc); + if (!req) + return 0; - return jiffies + sec * HZ + nsec / (1000000000 / HZ); + fuse_lookup_init(req, entry->d_parent->d_inode, entry, &outarg); + request_send_nonint(fc, req); + version = req->out.h.unique; + err = req->out.h.error; + fuse_put_request(fc, req); + if (err || outarg.nodeid != get_node_id(inode) || + (outarg.attr.mode ^ inode->i_mode) & S_IFMT) + return 0; + + fuse_change_attributes(inode, &outarg.attr); + inode->i_version = version; + entry->d_time = time_to_jiffies(outarg.entry_valid, + outarg.entry_valid_nsec); + fi->i_time = time_to_jiffies(outarg.attr_valid, + outarg.attr_valid_nsec); + } + return 1; +} +#ifndef KERNEL_2_6 +static int fuse_dentry_revalidate_2_4(struct dentry *entry, int flags) +{ + return fuse_dentry_revalidate(entry, NULL); } +#endif + +static struct dentry_operations fuse_dentry_operations = { +#ifdef KERNEL_2_6 + .d_revalidate = fuse_dentry_revalidate, +#else + .d_revalidate = fuse_dentry_revalidate_2_4, +#endif +}; static int fuse_lookup_iget(struct inode *dir, struct dentry *entry, struct inode **inodep) @@ -207,6 +102,7 @@ static int fuse_lookup_iget(struct inode *dir, struct dentry *entry, if (entry->d_name.len > FUSE_NAME_MAX) return -ENAMETOOLONG; + req = fuse_get_request(fc); if (!req) return -ERESTARTNOINTR; @@ -262,6 +158,7 @@ static int create_new_entry(struct fuse_conn *fc, struct fuse_req *req, int err; req->in.h.nodeid = get_node_id(dir); + req->inode = dir; req->out.numargs = 1; req->out.args[0].size = sizeof(outarg); req->out.args[0].value = &outarg; @@ -377,6 +274,7 @@ static int fuse_unlink(struct inode *dir, struct dentry *entry) req->in.h.opcode = FUSE_UNLINK; req->in.h.nodeid = get_node_id(dir); + req->inode = dir; req->in.numargs = 1; req->in.args[0].size = entry->d_name.len + 1; req->in.args[0].value = entry->d_name.name; @@ -407,6 +305,7 @@ static int fuse_rmdir(struct inode *dir, struct dentry *entry) req->in.h.opcode = FUSE_RMDIR; req->in.h.nodeid = get_node_id(dir); + req->inode = dir; req->in.numargs = 1; req->in.args[0].size = entry->d_name.len + 1; req->in.args[0].value = entry->d_name.name; @@ -435,6 +334,8 @@ static int fuse_rename(struct inode *olddir, struct dentry *oldent, inarg.newdir = get_node_id(newdir); req->in.h.opcode = FUSE_RENAME; req->in.h.nodeid = get_node_id(olddir); + req->inode = olddir; + req->inode2 = newdir; req->in.numargs = 3; req->in.args[0].size = sizeof(inarg); req->in.args[0].value = &inarg; @@ -454,8 +355,7 @@ static int fuse_rename(struct inode *olddir, struct dentry *oldent, rename actually took place. If the invalidation fails (e.g. some process has CWD under the renamed directory), then there can be inconsistency between - the dcache and the real filesystem. But not a lot - can be done about that */ + the dcache and the real filesystem. Tough luck. */ fuse_invalidate_entry(oldent); if (newent->d_inode) fuse_invalidate_entry(newent); @@ -478,6 +378,7 @@ static int fuse_link(struct dentry *entry, struct inode *newdir, memset(&inarg, 0, sizeof(inarg)); inarg.newdir = get_node_id(newdir); req->in.h.opcode = FUSE_LINK; + req->inode2 = newdir; req->in.numargs = 2; req->in.args[0].size = sizeof(inarg); req->in.args[0].value = &inarg; @@ -506,6 +407,7 @@ int fuse_do_getattr(struct inode *inode) req->in.h.opcode = FUSE_GETATTR; req->in.h.nodeid = get_node_id(inode); + req->inode = inode; req->out.numargs = 1; req->out.args[0].size = sizeof(arg); req->out.args[0].value = &arg; @@ -513,10 +415,15 @@ int fuse_do_getattr(struct inode *inode) err = req->out.h.error; fuse_put_request(fc, req); if (!err) { - struct fuse_inode *fi = get_fuse_inode(inode); - change_attributes(inode, &arg.attr); - fi->i_time = time_to_jiffies(arg.attr_valid, - arg.attr_valid_nsec); + if ((inode->i_mode ^ arg.attr.mode) & S_IFMT) { + make_bad_inode(inode); + err = -EIO; + } else { + struct fuse_inode *fi = get_fuse_inode(inode); + fuse_change_attributes(inode, &arg.attr); + fi->i_time = time_to_jiffies(arg.attr_valid, + arg.attr_valid_nsec); + } } return err; } @@ -594,7 +501,7 @@ static int parse_dirfile(char *buf, size_t nbytes, struct file *file, struct fuse_dirent *dirent = (struct fuse_dirent *) buf; size_t reclen = FUSE_DIRENT_SIZE(dirent); int over; - if (dirent->namelen > NAME_MAX) + if (dirent->namelen > FUSE_NAME_MAX) return -EIO; if (reclen > nbytes) break; @@ -640,6 +547,7 @@ static int fuse_getdir(struct file *file) req->in.h.opcode = FUSE_GETDIR; req->in.h.nodeid = get_node_id(inode); + req->inode = inode; req->out.numargs = 1; req->out.args[0].size = sizeof(struct fuse_getdir_out); req->out.args[0].value = &outarg; @@ -694,6 +602,7 @@ static char *read_link(struct dentry *dentry) } req->in.h.opcode = FUSE_READLINK; req->in.h.nodeid = get_node_id(inode); + req->inode = inode; req->out.argvar = 1; req->out.numargs = 1; req->out.args[0].size = PAGE_SIZE - 1; @@ -823,6 +732,7 @@ static int fuse_setattr(struct dentry *entry, struct iattr *attr) inarg.valid = iattr_to_fattr(attr, &inarg.attr); req->in.h.opcode = FUSE_SETATTR; req->in.h.nodeid = get_node_id(inode); + req->inode = inode; req->in.numargs = 1; req->in.args[0].size = sizeof(inarg); req->in.args[0].value = &inarg; @@ -833,57 +743,26 @@ static int fuse_setattr(struct dentry *entry, struct iattr *attr) err = req->out.h.error; fuse_put_request(fc, req); if (!err) { - if (is_truncate) { - loff_t origsize = i_size_read(inode); - i_size_write(inode, outarg.attr.size); - if (origsize > outarg.attr.size) - vmtruncate(inode, outarg.attr.size); + if ((inode->i_mode ^ outarg.attr.mode) & S_IFMT) { + make_bad_inode(inode); + err = -EIO; + } else { + if (is_truncate) { + loff_t origsize = i_size_read(inode); + i_size_write(inode, outarg.attr.size); + if (origsize > outarg.attr.size) + vmtruncate(inode, outarg.attr.size); + } + fuse_change_attributes(inode, &outarg.attr); + fi->i_time = time_to_jiffies(outarg.attr_valid, + outarg.attr_valid_nsec); } - change_attributes(inode, &outarg.attr); - fi->i_time = time_to_jiffies(outarg.attr_valid, - outarg.attr_valid_nsec); } else if (err == -EINTR) fuse_invalidate_attr(inode); return err; } -static int fuse_dentry_revalidate(struct dentry *entry, struct nameidata *nd) -{ - if (!entry->d_inode) - return 0; - else if (entry->d_time && time_after(jiffies, entry->d_time)) { - int err; - int version; - struct fuse_entry_out outarg; - struct inode *inode = entry->d_inode; - struct fuse_inode *fi = get_fuse_inode(inode); - struct fuse_conn *fc = get_fuse_conn(inode); - struct fuse_req *req = fuse_get_request_nonint(fc); - if (!req) - return 0; - - fuse_lookup_init(req, entry->d_parent->d_inode, entry, &outarg); - request_send_nonint(fc, req, 0); - version = req->out.h.unique; - err = req->out.h.error; - fuse_put_request(fc, req); - if (err) - return 0; - - if (outarg.nodeid != get_node_id(inode)) - return 0; - - change_attributes(inode, &outarg.attr); - inode->i_version = version; - entry->d_time = time_to_jiffies(outarg.entry_valid, - outarg.entry_valid_nsec); - fi->i_time = time_to_jiffies(outarg.attr_valid, - outarg.attr_valid_nsec); - } - return 1; -} - #ifdef KERNEL_2_6 static int fuse_getattr(struct vfsmount *mnt, struct dentry *entry, struct kstat *stat) @@ -932,11 +811,6 @@ static int fuse_mknod_2_4(struct inode *dir, struct dentry *entry, int mode, return fuse_mknod(dir, entry, mode, rdev); } -static int fuse_dentry_revalidate_2_4(struct dentry *entry, int flags) -{ - return fuse_dentry_revalidate(entry, NULL); -} - static int fuse_create_2_4(struct inode *dir, struct dentry *entry, int mode) { return fuse_create(dir, entry, mode, NULL); @@ -978,6 +852,7 @@ static int fuse_setxattr(struct dentry *entry, const char *name, inarg.flags = flags; req->in.h.opcode = FUSE_SETXATTR; req->in.h.nodeid = get_node_id(inode); + req->inode = inode; req->in.numargs = 3; req->in.args[0].size = sizeof(inarg); req->in.args[0].value = &inarg; @@ -1016,6 +891,7 @@ static ssize_t fuse_getxattr(struct dentry *entry, const char *name, inarg.size = size; req->in.h.opcode = FUSE_GETXATTR; req->in.h.nodeid = get_node_id(inode); + req->inode = inode; req->in.numargs = 2; req->in.args[0].size = sizeof(inarg); req->in.args[0].value = &inarg; @@ -1065,6 +941,7 @@ static ssize_t fuse_listxattr(struct dentry *entry, char *list, size_t size) inarg.size = size; req->in.h.opcode = FUSE_LISTXATTR; req->in.h.nodeid = get_node_id(inode); + req->inode = inode; req->in.numargs = 1; req->in.args[0].size = sizeof(inarg); req->in.args[0].value = &inarg; @@ -1108,6 +985,7 @@ static int fuse_removexattr(struct dentry *entry, const char *name) req->in.h.opcode = FUSE_REMOVEXATTR; req->in.h.nodeid = get_node_id(inode); + req->inode = inode; req->in.numargs = 1; req->in.args[0].size = strlen(name) + 1; req->in.args[0].value = name; @@ -1157,7 +1035,7 @@ static struct file_operations fuse_dir_operations = { .release = fuse_dir_release, }; -static struct inode_operations fuse_file_inode_operations = { +static struct inode_operations fuse_common_inode_operations = { .setattr = fuse_setattr, #ifdef KERNEL_2_6 .permission = fuse_permission, @@ -1191,10 +1069,18 @@ static struct inode_operations fuse_symlink_inode_operations = { #endif }; -static struct dentry_operations fuse_dentry_operations = { -#ifdef KERNEL_2_6 - .d_revalidate = fuse_dentry_revalidate, -#else - .d_revalidate = fuse_dentry_revalidate_2_4, -#endif -}; +void fuse_init_common(struct inode *inode) +{ + inode->i_op = &fuse_common_inode_operations; +} + +void fuse_init_dir(struct inode *inode) +{ + inode->i_op = &fuse_dir_inode_operations; + inode->i_fop = &fuse_dir_operations; +} + +void fuse_init_symlink(struct inode *inode) +{ + inode->i_op = &fuse_symlink_inode_operations; +} diff --git a/kernel/file.c b/kernel/file.c index aa02651..b7359c5 100644 --- a/kernel/file.c +++ b/kernel/file.c @@ -37,12 +37,9 @@ static int fuse_open(struct inode *inode, struct file *file) return err; } - /* Prevent concurrent unlink or rename */ - down(&inode->i_sem); - err = -ERESTARTSYS; req = fuse_get_request(fc); if (!req) - goto out; + return -ERESTARTSYS; err = -ENOMEM; ff = kmalloc(sizeof(struct fuse_file), GFP_KERNEL); @@ -56,9 +53,10 @@ static int fuse_open(struct inode *inode, struct file *file) } memset(&inarg, 0, sizeof(inarg)); - inarg.flags = file->f_flags & ~O_EXCL; + inarg.flags = file->f_flags & ~(O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC); req->in.h.opcode = FUSE_OPEN; req->in.h.nodeid = get_node_id(inode); + req->inode = inode; req->in.numargs = 1; req->in.args[0].size = sizeof(inarg); req->in.args[0].value = &inarg; @@ -85,8 +83,6 @@ static int fuse_open(struct inode *inode, struct file *file) out_put_request: fuse_put_request(fc, req); - out: - up(&inode->i_sem); return err; } @@ -101,11 +97,11 @@ static int fuse_release(struct inode *inode, struct file *file) inarg->flags = file->f_flags & ~O_EXCL; req->in.h.opcode = FUSE_RELEASE; req->in.h.nodeid = get_node_id(inode); + req->inode = inode; req->in.numargs = 1; req->in.args[0].size = sizeof(struct fuse_release_in); req->in.args[0].value = inarg; - request_send_nonint(fc, req, 1); - fuse_put_request(fc, req); + request_send_background(fc, req); kfree(ff); /* Return value is ignored by VFS */ @@ -132,10 +128,12 @@ static int fuse_flush(struct file *file) inarg.fh = ff->fh; req->in.h.opcode = FUSE_FLUSH; req->in.h.nodeid = get_node_id(inode); + req->inode = inode; + req->file = file; req->in.numargs = 1; req->in.args[0].size = sizeof(inarg); req->in.args[0].value = &inarg; - request_send_nonint(fc, req, 0); + request_send_nonint(fc, req); err = req->out.h.error; fuse_put_request(fc, req); if (err == -ENOSYS) { @@ -163,9 +161,11 @@ static int fuse_fsync(struct file *file, struct dentry *de, int datasync) memset(&inarg, 0, sizeof(inarg)); inarg.fh = ff->fh; - inarg.datasync = datasync; + inarg.fsync_flags = datasync ? 1 : 0; req->in.h.opcode = FUSE_FSYNC; req->in.h.nodeid = get_node_id(inode); + req->inode = inode; + req->file = file; req->in.numargs = 1; req->in.args[0].size = sizeof(inarg); req->in.args[0].value = &inarg; @@ -192,6 +192,8 @@ static ssize_t fuse_send_read(struct fuse_req *req, struct file *file, inarg.size = count; req->in.h.opcode = FUSE_READ; req->in.h.nodeid = get_node_id(inode); + req->inode = inode; + req->file = file; req->in.numargs = 1; req->in.args[0].size = sizeof(struct fuse_read_in); req->in.args[0].value = &inarg; @@ -199,19 +201,19 @@ static ssize_t fuse_send_read(struct fuse_req *req, struct file *file, req->out.argvar = 1; req->out.numargs = 1; req->out.args[0].size = count; - request_send_nonint(fc, req, 0); + request_send_nonint(fc, req); return req->out.args[0].size; } static int fuse_readpage(struct file *file, struct page *page) { - int err; struct inode *inode = page->mapping->host; struct fuse_conn *fc = get_fuse_conn(inode); loff_t pos = (loff_t) page->index << PAGE_CACHE_SHIFT; struct fuse_req *req = fuse_get_request_nonint(fc); + int err = -EINTR; if (!req) - return -EINTR; + goto out; req->out.page_zeroing = 1; req->num_pages = 1; @@ -221,6 +223,7 @@ static int fuse_readpage(struct file *file, struct page *page) fuse_put_request(fc, req); if (!err) SetPageUptodate(page); + out: unlock_page(page); return err; } @@ -261,9 +264,10 @@ static int fuse_readpages_fill(void *_data, struct page *page) (req->num_pages + 1) * PAGE_CACHE_SIZE > fc->max_read || req->pages[req->num_pages - 1]->index + 1 != page->index)) { int err = fuse_send_readpages(req, data->file, inode); - if (err) + if (err) { + unlock_page(page); return err; - + } fuse_reset_request(req); } req->pages[req->num_pages] = page; @@ -278,7 +282,6 @@ static int fuse_readpages(struct file *file, struct address_space *mapping, struct fuse_conn *fc = get_fuse_conn(inode); struct fuse_readpages_data data; int err; - data.file = file; data.inode = inode; data.req = fuse_get_request_nonint(fc); @@ -287,7 +290,7 @@ static int fuse_readpages(struct file *file, struct address_space *mapping, err = read_cache_pages(mapping, pages, fuse_readpages_fill, &data); if (!err && data.req->num_pages) - fuse_send_readpages(data.req, file, inode); + err = fuse_send_readpages(data.req, file, inode); fuse_put_request(fc, data.req); return err; } @@ -400,12 +403,13 @@ static ssize_t fuse_send_write(struct fuse_req *req, struct file *file, struct fuse_write_out outarg; memset(&inarg, 0, sizeof(struct fuse_write_in)); - inarg.writepage = 0; inarg.fh = ff->fh; inarg.offset = pos; inarg.size = count; req->in.h.opcode = FUSE_WRITE; req->in.h.nodeid = get_node_id(inode); + req->inode = inode; + req->file = file; req->in.argpages = 1; req->in.numargs = 2; req->in.args[0].size = sizeof(struct fuse_write_in); @@ -414,7 +418,7 @@ static ssize_t fuse_send_write(struct fuse_req *req, struct file *file, req->out.numargs = 1; req->out.args[0].size = sizeof(struct fuse_write_out); req->out.args[0].value = &outarg; - request_send_nonint(fc, req, 0); + request_send_nonint(fc, req); return outarg.size; } @@ -584,6 +588,7 @@ static ssize_t fuse_file_write(struct file *file, const char __user *buf, if (fc->flags & FUSE_DIRECT_IO) { ssize_t res; + /* Don't allow parallel writes to the same file */ down(&inode->i_sem); res = fuse_direct_io(file, buf, count, ppos, 1); up(&inode->i_sem); diff --git a/kernel/fuse_i.h b/kernel/fuse_i.h index a5660b1..9812f31 100644 --- a/kernel/fuse_i.h +++ b/kernel/fuse_i.h @@ -37,6 +37,8 @@ # define i_size_read(inode) ((inode)->i_size) # define i_size_write(inode, size) do { (inode)->i_size = size; } while(0) # endif +# define new_decode_dev(x) (x) +# define new_encode_dev(x) (x) #endif /* KERNEL_2_6 */ #endif /* FUSE_MAINLINE */ #include @@ -64,10 +66,10 @@ static inline void set_page_dirty_lock(struct page *page) unlock_page(page); } #endif -/* Max number of pages that can be used in a single read request */ +/** Max number of pages that can be used in a single read request */ #define FUSE_MAX_PAGES_PER_REQ 32 -/* If more requests are outstanding, then the operation will block */ +/** If more requests are outstanding, then the operation will block */ #define FUSE_MAX_OUTSTANDING 10 /** If the FUSE_DEFAULT_PERMISSIONS flag is given, the filesystem @@ -99,7 +101,7 @@ static inline void set_page_dirty_lock(struct page *page) struct fuse_inode { /** Unique ID, which identifies the inode between userspace * and kernel */ - unsigned long nodeid; + u64 nodeid; /** The request used for sending the FORGET message */ struct fuse_req *forget_req; @@ -114,7 +116,7 @@ struct fuse_file { struct fuse_req *release_req; /** File handle used by userspace */ - unsigned long fh; + u64 fh; }; /** One input argument of a request */ @@ -189,6 +191,9 @@ struct fuse_req { /** The request was interrupted */ unsigned interrupted:1; + /** Request is sent in the background */ + unsigned background:1; + /** Data is being copied to/from the request */ unsigned locked:1; @@ -221,6 +226,15 @@ struct fuse_req { /** offset of data on first page */ unsigned page_offset; + + /** Inode used in the request */ + struct inode *inode; + + /** Second inode used in the request (or NULL) */ + struct inode *inode2; + + /** File used in the request (or NULL) */ + struct file *file; }; /** @@ -259,7 +273,11 @@ struct fuse_conn { struct list_head processing; /** Controls the maximum number of outstanding requests */ - struct semaphore unused_sem; + struct semaphore outstanding_sem; + + /** This counts the number of outstanding requests if + outstanding_sem would go negative */ + unsigned outstanding_debt; /** The list of unused requests */ struct list_head unused_list; @@ -320,7 +338,7 @@ static inline struct fuse_inode *get_fuse_inode(struct inode *inode) return (struct fuse_inode *) (&inode[1]); } -static inline unsigned long get_node_id(struct inode *inode) +static inline u64 get_node_id(struct inode *inode) { return get_fuse_inode(inode)->nodeid; } @@ -348,15 +366,6 @@ extern spinlock_t fuse_lock; struct inode *fuse_iget(struct super_block *sb, unsigned long nodeid, int generation, struct fuse_attr *attr, int version); -/** - * Lookup an inode by nodeid - */ -#ifdef KERNEL_2_6 -struct inode *fuse_ilookup(struct super_block *sb, unsigned long nodeid); -#else -struct inode *fuse_ilookup(struct super_block *sb, ino_t ino, unsigned long nodeid); -#endif - /** * Send FORGET command */ @@ -364,10 +373,30 @@ void fuse_send_forget(struct fuse_conn *fc, struct fuse_req *req, unsigned long nodeid, int version); /** - * Initialise operations on regular file + * Initialise file operations on a regular file */ void fuse_init_file_inode(struct inode *inode); +/** + * Initialise inode operations on regular files and special files + */ +void fuse_init_common(struct inode *inode); + +/** + * Initialise inode and file operations on a directory + */ +void fuse_init_dir(struct inode *inode); + +/** + * Initialise inode operations on a symlink + */ +void fuse_init_symlink(struct inode *inode); + +/** + * Change attributes of an inode + */ +void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr); + /** * Check if the connection can be released, and if yes, then free the * connection structure @@ -431,18 +460,19 @@ void request_send(struct fuse_conn *fc, struct fuse_req *req); /** * Send a request (synchronous, non-interruptible except by SIGKILL) - * - * If background is non-zero and SIGKILL is received still send - * request asynchronously */ -void request_send_nonint(struct fuse_conn *fc, struct fuse_req *req, - int background); +void request_send_nonint(struct fuse_conn *fc, struct fuse_req *req); /** * Send a request with no reply */ void request_send_noreply(struct fuse_conn *fc, struct fuse_req *req); +/** + * Send a request in the background + */ +void request_send_background(struct fuse_conn *fc, struct fuse_req *req); + /** * Get the attributes of a file */ diff --git a/kernel/fuse_kernel.h b/kernel/fuse_kernel.h index a97c3e8..bad4546 100644 --- a/kernel/fuse_kernel.h +++ b/kernel/fuse_kernel.h @@ -8,6 +8,8 @@ /* This file defines the kernel interface of FUSE */ +#include + /** Version number of this interface */ #define FUSE_KERNEL_VERSION 5 @@ -24,30 +26,30 @@ #define FUSE_MINOR 229 struct fuse_attr { - unsigned long ino; - unsigned int mode; - unsigned int nlink; - unsigned int uid; - unsigned int gid; - unsigned int rdev; - unsigned long long size; - unsigned long blocks; - unsigned long atime; - unsigned long atimensec; - unsigned long mtime; - unsigned long mtimensec; - unsigned long ctime; - unsigned long ctimensec; + __u64 ino; + __u64 size; + __u64 blocks; + __u64 atime; + __u64 mtime; + __u64 ctime; + __u32 atimensec; + __u32 mtimensec; + __u32 ctimensec; + __u32 mode; + __u32 nlink; + __u32 uid; + __u32 gid; + __u32 rdev; }; struct fuse_kstatfs { - unsigned int bsize; - unsigned long long blocks; - unsigned long long bfree; - unsigned long long bavail; - unsigned long long files; - unsigned long long ffree; - unsigned int namelen; + __u64 blocks; + __u64 bfree; + __u64 bavail; + __u64 files; + __u64 ffree; + __u32 bsize; + __u32 namelen; }; #define FATTR_MODE (1 << 0) @@ -94,86 +96,87 @@ enum fuse_opcode { #define FUSE_XATTR_SIZE_MAX 4096 struct fuse_entry_out { - unsigned long nodeid; /* Inode ID */ - unsigned long generation; /* Inode generation: nodeid:gen must - be unique for the fs's lifetime */ - unsigned long entry_valid; /* Cache timeout for the name */ - unsigned long entry_valid_nsec; - unsigned long attr_valid; /* Cache timeout for the attributes */ - unsigned long attr_valid_nsec; + __u64 nodeid; /* Inode ID */ + __u64 generation; /* Inode generation: nodeid:gen must + be unique for the fs's lifetime */ + __u64 entry_valid; /* Cache timeout for the name */ + __u64 attr_valid; /* Cache timeout for the attributes */ + __u32 entry_valid_nsec; + __u32 attr_valid_nsec; struct fuse_attr attr; }; struct fuse_forget_in { - int version; + __u64 version; }; struct fuse_attr_out { - unsigned long attr_valid; /* Cache timeout for the attributes */ - unsigned long attr_valid_nsec; + __u64 attr_valid; /* Cache timeout for the attributes */ + __u32 attr_valid_nsec; + __u32 dummy; struct fuse_attr attr; }; struct fuse_getdir_out { - int fd; + __u32 fd; }; struct fuse_mknod_in { - unsigned int mode; - unsigned int rdev; + __u32 mode; + __u32 rdev; }; struct fuse_mkdir_in { - unsigned int mode; + __u32 mode; }; struct fuse_rename_in { - unsigned long newdir; + __u64 newdir; }; struct fuse_link_in { - unsigned long newdir; + __u64 newdir; }; struct fuse_setattr_in { + __u32 valid; struct fuse_attr attr; - unsigned int valid; }; struct fuse_open_in { - unsigned int flags; + __u32 flags; }; struct fuse_open_out { - unsigned long fh; - unsigned int _open_flags; + __u64 fh; + __u32 open_flags; }; struct fuse_release_in { - unsigned long fh; - unsigned int flags; + __u64 fh; + __u32 flags; }; struct fuse_flush_in { - unsigned long fh; - unsigned int _nref; + __u64 fh; + __u32 flush_flags; }; struct fuse_read_in { - unsigned long fh; - unsigned long long offset; - unsigned int size; + __u64 fh; + __u64 offset; + __u32 size; }; struct fuse_write_in { - int writepage; - unsigned long fh; - unsigned long long offset; - unsigned int size; + __u64 fh; + __u64 offset; + __u32 size; + __u32 write_flags; }; struct fuse_write_out { - unsigned int size; + __u32 size; }; struct fuse_statfs_out { @@ -181,45 +184,47 @@ struct fuse_statfs_out { }; struct fuse_fsync_in { - unsigned long fh; - int datasync; + __u64 fh; + __u32 fsync_flags; }; struct fuse_setxattr_in { - unsigned int size; - unsigned int flags; + __u32 size; + __u32 flags; }; struct fuse_getxattr_in { - unsigned int size; + __u32 size; }; struct fuse_getxattr_out { - unsigned int size; + __u32 size; }; struct fuse_in_header { - int unique; - enum fuse_opcode opcode; - unsigned long nodeid; - unsigned int uid; - unsigned int gid; - unsigned int pid; + __u32 len; + __u32 opcode; + __u64 unique; + __u64 nodeid; + __u32 uid; + __u32 gid; + __u32 pid; }; struct fuse_out_header { - int unique; - int error; + __u32 len; + __s32 error; + __u64 unique; }; struct fuse_dirent { - unsigned long ino; - unsigned short namelen; - unsigned char type; - char name[256]; + __u64 ino; + __u32 namelen; + __u32 type; + char name[0]; }; -#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_NAME_OFFSET ((unsigned) ((struct fuse_dirent *) 0)->name) +#define FUSE_DIRENT_ALIGN(x) (((x) + sizeof(__u64) - 1) & ~(sizeof(__u64) - 1)) #define FUSE_DIRENT_SIZE(d) \ FUSE_DIRENT_ALIGN(FUSE_NAME_OFFSET + (d)->namelen) diff --git a/kernel/inode.c b/kernel/inode.c index ac80b63..eb40b7b 100644 --- a/kernel/inode.c +++ b/kernel/inode.c @@ -24,14 +24,19 @@ #endif static kmem_cache_t *fuse_inode_cachep; +static int mount_count; static int user_allow_other; +static int mount_max = 1000; #ifdef KERNEL_2_6 module_param(user_allow_other, int, 0644); +module_param(mount_max, int, 0644); #else MODULE_PARM(user_allow_other, "i"); +MODULE_PARM(mount_max, "i"); #endif MODULE_PARM_DESC(user_allow_other, "Allow non root user to specify the \"allow_other\" or \"allow_root\" mount options"); +MODULE_PARM_DESC(mount_max, "Maximum number of FUSE mounts allowed, if -1 then unlimited (default: 1000)"); #define FUSE_SUPER_MAGIC 0x65735546 @@ -112,11 +117,155 @@ static void fuse_clear_inode(struct inode *inode) } } +void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr) +{ + if (S_ISREG(inode->i_mode) && i_size_read(inode) != attr->size) +#ifdef KERNEL_2_6 + invalidate_inode_pages(inode->i_mapping); +#else + invalidate_inode_pages(inode); +#endif + + inode->i_ino = attr->ino; + 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; + i_size_write(inode, attr->size); + inode->i_blksize = PAGE_CACHE_SIZE; + inode->i_blocks = attr->blocks; +#ifdef KERNEL_2_6 + inode->i_atime.tv_sec = attr->atime; + inode->i_atime.tv_nsec = attr->atimensec; + inode->i_mtime.tv_sec = attr->mtime; + inode->i_mtime.tv_nsec = attr->mtimensec; + inode->i_ctime.tv_sec = attr->ctime; + inode->i_ctime.tv_nsec = attr->ctimensec; +#else + inode->i_atime = attr->atime; + inode->i_mtime = attr->mtime; + inode->i_ctime = attr->ctime; +#endif +} + +static void fuse_init_inode(struct inode *inode, struct fuse_attr *attr) +{ + inode->i_mode = attr->mode & S_IFMT; + i_size_write(inode, attr->size); + if (S_ISREG(inode->i_mode)) { + fuse_init_common(inode); + fuse_init_file_inode(inode); + } else if (S_ISDIR(inode->i_mode)) + fuse_init_dir(inode); + else if (S_ISLNK(inode->i_mode)) + fuse_init_symlink(inode); + else if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode) || + S_ISFIFO(inode->i_mode) || S_ISSOCK(inode->i_mode)) { + fuse_init_common(inode); + init_special_inode(inode, inode->i_mode, + new_decode_dev(attr->rdev)); + } else { + /* Don't let user create weird files */ + inode->i_mode = S_IFREG; + fuse_init_common(inode); + fuse_init_file_inode(inode); + } +} + +#ifdef KERNEL_2_6 +static int fuse_inode_eq(struct inode *inode, void *_nodeidp) +{ + unsigned long nodeid = *(unsigned long *) _nodeidp; + if (get_node_id(inode) == nodeid) + return 1; + else + return 0; +} + +static int fuse_inode_set(struct inode *inode, void *_nodeidp) +{ + unsigned long nodeid = *(unsigned long *) _nodeidp; + get_fuse_inode(inode)->nodeid = nodeid; + return 0; +} + +struct inode *fuse_iget(struct super_block *sb, unsigned long nodeid, + int generation, struct fuse_attr *attr, int version) +{ + struct inode *inode; + struct fuse_conn *fc = get_fuse_conn_super(sb); + int retried = 0; + + retry: + inode = iget5_locked(sb, nodeid, fuse_inode_eq, fuse_inode_set, &nodeid); + if (!inode) + return NULL; + + if ((inode->i_state & I_NEW)) { + inode->i_generation = generation; + inode->i_data.backing_dev_info = &fc->bdi; + fuse_init_inode(inode, attr); + unlock_new_inode(inode); + } else if ((inode->i_mode ^ attr->mode) & S_IFMT) { + BUG_ON(retried); + /* Inode has changed type, any I/O on the old should fail */ + make_bad_inode(inode); + iput(inode); + retried = 1; + goto retry; + } + + fuse_change_attributes(inode, attr); + inode->i_version = version; + return inode; +} +#else +static int fuse_inode_eq(struct inode *inode, unsigned long ino, void *_nodeidp){ + unsigned long nodeid = *(unsigned long *) _nodeidp; + if (inode->u.generic_ip && get_node_id(inode) == nodeid) + return 1; + else + return 0; +} + +struct inode *fuse_iget(struct super_block *sb, unsigned long nodeid, + int generation, struct fuse_attr *attr, int version) +{ + struct inode *inode; + int retried = 0; + + retry: + inode = iget4(sb, attr->ino, fuse_inode_eq, &nodeid); + if (!inode) + return NULL; + + if (!inode->u.generic_ip) { + get_fuse_inode(inode)->nodeid = nodeid; + inode->u.generic_ip = inode; + inode->i_generation = generation; + fuse_init_inode(inode, attr); + } else if ((inode->i_mode ^ attr->mode) & S_IFMT) { + BUG_ON(retried); + /* Inode has changed type, any I/O on the old should fail */ + remove_inode_hash(inode); + make_bad_inode(inode); + iput(inode); + retried = 1; + goto retry; + } + + fuse_change_attributes(inode, attr); + inode->i_version = version; + return inode; +} +#endif + static void fuse_put_super(struct super_block *sb) { struct fuse_conn *fc = get_fuse_conn_super(sb); spin_lock(&fuse_lock); + mount_count --; fc->sb = NULL; fc->uid = 0; fc->flags = 0; @@ -329,7 +478,7 @@ static struct fuse_conn *new_conn(void) INIT_LIST_HEAD(&fc->pending); INIT_LIST_HEAD(&fc->processing); INIT_LIST_HEAD(&fc->unused_list); - sema_init(&fc->unused_sem, FUSE_MAX_OUTSTANDING); + sema_init(&fc->outstanding_sem, FUSE_MAX_OUTSTANDING); for (i = 0; i < FUSE_MAX_OUTSTANDING; i++) { struct fuse_req *req = fuse_request_alloc(); if (!req) { @@ -392,7 +541,7 @@ static struct dentry *fuse_get_dentry(struct super_block *sb, void *vobjp) if (nodeid == 0) return ERR_PTR(-ESTALE); - inode = fuse_ilookup(sb, nodeid); + inode = ilookup5(sb, nodeid, fuse_inode_eq, &nodeid); if (!inode || inode->i_generation != generation) return ERR_PTR(-ESTALE); @@ -449,12 +598,24 @@ static struct super_operations fuse_super_operations = { .show_options = fuse_show_options, }; +static int inc_mount_count(void) +{ + int success = 0; + spin_lock(&fuse_lock); + mount_count ++; + if (mount_max == -1 || mount_count <= mount_max) + success = 1; + spin_unlock(&fuse_lock); + return success; +} + static int fuse_read_super(struct super_block *sb, void *data, int silent) { struct fuse_conn *fc; struct inode *root; struct fuse_mount_data d; struct file *file; + int err; if (!parse_fuse_opt((char *) data, &d)) return -EINVAL; @@ -493,6 +654,11 @@ static int fuse_read_super(struct super_block *sb, void *data, int silent) *get_fuse_conn_super_p(sb) = fc; + err = -ENFILE; + if (!inc_mount_count() && current->uid != 0) + goto err; + + err = -ENOMEM; root = get_root_inode(sb, d.rootmode); if (root == NULL) goto err; @@ -507,11 +673,12 @@ static int fuse_read_super(struct super_block *sb, void *data, int silent) err: spin_lock(&fuse_lock); + mount_count --; fc->sb = NULL; fuse_release_conn(fc); spin_unlock(&fuse_lock); *get_fuse_conn_super_p(sb) = NULL; - return -EINVAL; + return err; } #ifdef KERNEL_2_6 diff --git a/lib/fuse.c b/lib/fuse.c index 1e120e8..b5be472 100644 --- a/lib/fuse.c +++ b/lib/fuse.c @@ -8,15 +8,31 @@ #include #include "fuse_i.h" +#include "fuse_compat.h" #include "fuse_kernel.h" +#include #include #include #include #include #include +#include +#include #include +/* FUSE flags: */ + +/** Enable debuging output */ +#define FUSE_DEBUG (1 << 1) + +/** If a file is removed but it's still open, don't hide the file but + remove it immediately */ +#define FUSE_HARD_REMOVE (1 << 2) + +/** Use st_ino field in getattr instead of generating inode numbers */ +#define FUSE_USE_INO (1 << 3) + #define FUSE_KERNEL_MINOR_VERSION_NEED 1 #define FUSE_VERSION_FILE_OLD "/proc/fs/fuse/version" #define FUSE_VERSION_FILE_NEW "/sys/fs/fuse/version" @@ -28,6 +44,32 @@ #define ENTRY_REVALIDATE_TIME 1 /* sec */ #define ATTR_REVALIDATE_TIME 1 /* sec */ + +struct node { + struct node *name_next; + struct node *id_next; + nodeid_t nodeid; + unsigned int generation; + int refctr; + nodeid_t parent; + char *name; + uint64_t version; + int open_count; + int is_hidden; +}; + +struct fuse_dirhandle { + struct fuse *fuse; + nodeid_t dir; + FILE *fp; +}; + +struct fuse_cmd { + char *buf; + size_t buflen; +}; + + static struct fuse_context *(*fuse_getcontext)(void) = NULL; static const char *opname(enum fuse_opcode opcode) @@ -61,11 +103,18 @@ static const char *opname(enum fuse_opcode opcode) } } -static inline void dec_avail(struct fuse *f) +static inline void fuse_dec_avail(struct fuse *f) { - pthread_mutex_lock(&f->lock); + pthread_mutex_lock(&f->worker_lock); f->numavail --; - pthread_mutex_unlock(&f->lock); + pthread_mutex_unlock(&f->worker_lock); +} + +static inline void fuse_inc_avail(struct fuse *f) +{ + pthread_mutex_lock(&f->worker_lock); + f->numavail ++; + pthread_mutex_unlock(&f->worker_lock); } static struct node *get_node_nocheck(struct fuse *f, nodeid_t nodeid) @@ -83,18 +132,18 @@ static struct node *get_node_nocheck(struct fuse *f, nodeid_t nodeid) static struct node *get_node(struct fuse *f, nodeid_t nodeid) { struct node *node = get_node_nocheck(f, nodeid); - if (node != NULL) - return node; - - fprintf(stderr, "fuse internal error: inode %lu not found\n", nodeid); - abort(); + if (!node) { + fprintf(stderr, "fuse internal error: node %lu not found\n", + nodeid); + abort(); + } + return node; } -static void hash_id(struct fuse *f, struct node *node) +static void free_node(struct node *node) { - size_t hash = node->nodeid % f->id_table_size; - node->id_next = f->id_table[hash]; - f->id_table[hash] = node; + free(node->name); + free(node); } static void unhash_id(struct fuse *f, struct node *node) @@ -109,20 +158,11 @@ static void unhash_id(struct fuse *f, struct node *node) } } -static nodeid_t next_id(struct fuse *f) -{ - do { - f->ctr++; - if (!f->ctr) - f->generation ++; - } while (f->ctr == 0 || get_node_nocheck(f, f->ctr) != NULL); - return f->ctr; -} - -static void free_node(struct node *node) +static void hash_id(struct fuse *f, struct node *node) { - free(node->name); - free(node); + size_t hash = node->nodeid % f->id_table_size; + node->id_next = f->id_table[hash]; + f->id_table[hash] = node; } static unsigned int name_hash(struct fuse *f, nodeid_t parent, const char *name) @@ -136,36 +176,11 @@ static unsigned int name_hash(struct fuse *f, nodeid_t parent, const char *name) return (hash + parent) % f->name_table_size; } -static struct node *lookup_node(struct fuse *f, nodeid_t parent, - const char *name) -{ - size_t hash = name_hash(f, parent, name); - struct node *node; - - for (node = f->name_table[hash]; node != NULL; node = node->name_next) - if (node->parent == parent && strcmp(node->name, name) == 0) - return node; - - return NULL; -} - -static int hash_name(struct fuse *f, struct node *node, nodeid_t parent, - const char *name) -{ - size_t hash = name_hash(f, parent, name); - node->parent = parent; - node->name = strdup(name); - if (node->name == NULL) - return -1; - - node->name_next = f->name_table[hash]; - f->name_table[hash] = node; - return 0; -} +static void unref_node(struct fuse *f, struct node *node); static void unhash_name(struct fuse *f, struct node *node) { - if (node->name != NULL) { + if (node->name) { size_t hash = name_hash(f, node->parent, node->name); struct node **nodep = &f->name_table[hash]; @@ -173,6 +188,7 @@ static void unhash_name(struct fuse *f, struct node *node) if (*nodep == node) { *nodep = node->name_next; node->name_next = NULL; + unref_node(f, get_node(f, node->parent)); free(node->name); node->name = NULL; node->parent = 0; @@ -184,8 +200,61 @@ static void unhash_name(struct fuse *f, struct node *node) } } +static int hash_name(struct fuse *f, struct node *node, nodeid_t parent, + const char *name) +{ + size_t hash = name_hash(f, parent, name); + node->name = strdup(name); + if (node->name == NULL) + return -1; + + get_node(f, parent)->refctr ++; + node->parent = parent; + node->name_next = f->name_table[hash]; + f->name_table[hash] = node; + return 0; +} + +static void delete_node(struct fuse *f, struct node *node) +{ + assert(!node->name); + unhash_id(f, node); + free_node(node); +} + +static void unref_node(struct fuse *f, struct node *node) +{ + assert(node->refctr > 0); + node->refctr --; + if (!node->refctr) + delete_node(f, node); +} + +static nodeid_t next_id(struct fuse *f) +{ + do { + f->ctr++; + if (!f->ctr) + f->generation ++; + } while (f->ctr == 0 || get_node_nocheck(f, f->ctr) != NULL); + return f->ctr; +} + +static struct node *lookup_node(struct fuse *f, nodeid_t parent, + const char *name) +{ + size_t hash = name_hash(f, parent, name); + struct node *node; + + for (node = f->name_table[hash]; node != NULL; node = node->name_next) + if (node->parent == parent && strcmp(node->name, name) == 0) + return node; + + return NULL; +} + static struct node *find_node(struct fuse *f, nodeid_t parent, char *name, - struct fuse_attr *attr, int version) + struct fuse_attr *attr, uint64_t version) { struct node *node; int mode = attr->mode & S_IFMT; @@ -197,38 +266,27 @@ static struct node *find_node(struct fuse *f, nodeid_t parent, char *name, pthread_mutex_lock(&f->lock); node = lookup_node(f, parent, name); if (node != NULL) { - if (node->mode == mode && node->rdev == rdev && - (!(f->flags & FUSE_USE_INO) || node->ino == attr->ino)) { - if (!(f->flags & FUSE_USE_INO)) - attr->ino = node->nodeid; - - goto out; - } + if (!(f->flags & FUSE_USE_INO)) + attr->ino = node->nodeid; + } else { + node = (struct node *) calloc(1, sizeof(struct node)); + if (node == NULL) + goto out_err; - unhash_name(f, node); + node->refctr = 1; + node->nodeid = next_id(f); + if (!(f->flags & FUSE_USE_INO)) + attr->ino = node->nodeid; + node->open_count = 0; + node->is_hidden = 0; + node->generation = f->generation; + if (hash_name(f, node, parent, name) == -1) { + free(node); + node = NULL; + goto out_err; + } + hash_id(f, node); } - - node = (struct node *) calloc(1, sizeof(struct node)); - if (node == NULL) - goto out_err; - - node->nodeid = next_id(f); - if (!(f->flags & FUSE_USE_INO)) - attr->ino = node->nodeid; - node->mode = mode; - node->rdev = rdev; - node->ino = attr->ino; - node->open_count = 0; - node->is_hidden = 0; - node->generation = f->generation; - if (hash_name(f, node, parent, name) == -1) { - free(node); - node = NULL; - goto out_err; - } - hash_id(f, node); - - out: node->version = version; out_err: pthread_mutex_unlock(&f->lock); @@ -265,8 +323,8 @@ static char *get_path_name(struct fuse *f, nodeid_t nodeid, const char *name) } pthread_mutex_lock(&f->lock); - for (node = get_node(f, nodeid); node->nodeid != FUSE_ROOT_ID; - node = get_node(f, node->parent)) { + for (node = get_node(f, nodeid); node && node->nodeid != FUSE_ROOT_ID; + node = get_node(f, node->parent)) { if (node->name == NULL) { s = NULL; break; @@ -278,7 +336,7 @@ static char *get_path_name(struct fuse *f, nodeid_t nodeid, const char *name) } pthread_mutex_unlock(&f->lock); - if (s == NULL) + if (node == NULL || s == NULL) return NULL; else if (*s == '\0') return strdup("/"); @@ -291,16 +349,16 @@ static char *get_path(struct fuse *f, nodeid_t nodeid) return get_path_name(f, nodeid, NULL); } -static void destroy_node(struct fuse *f, nodeid_t nodeid, int version) +static void forget_node(struct fuse *f, nodeid_t nodeid, uint64_t version) { struct node *node; pthread_mutex_lock(&f->lock); - node = get_node_nocheck(f, nodeid); - if (node && node->version == version && nodeid != FUSE_ROOT_ID) { + node = get_node(f, nodeid); + if (node->version == version && nodeid != FUSE_ROOT_ID) { + node->version = 0; unhash_name(f, node); - unhash_id(f, node); - free_node(node); + unref_node(f, node); } pthread_mutex_unlock(&f->lock); @@ -312,12 +370,8 @@ static void remove_node(struct fuse *f, nodeid_t dir, const char *name) pthread_mutex_lock(&f->lock); node = lookup_node(f, dir, name); - if (node == NULL) { - fprintf(stderr, "fuse internal error: unable to remove node %lu/%s\n", - dir, name); - abort(); - } - unhash_name(f, node); + if (node != NULL) + unhash_name(f, node); pthread_mutex_unlock(&f->lock); } @@ -331,11 +385,8 @@ static int rename_node(struct fuse *f, nodeid_t olddir, const char *oldname, pthread_mutex_lock(&f->lock); node = lookup_node(f, olddir, oldname); newnode = lookup_node(f, newdir, newname); - if (node == NULL) { - fprintf(stderr, "fuse internal error: unable to rename node %lu/%s\n", - olddir, oldname); - abort(); - } + if (node == NULL) + goto out; if (newnode != NULL) { if (hide) { @@ -383,19 +434,28 @@ static void convert_stat(struct stat *stbuf, struct fuse_attr *attr) static int fill_dir(struct fuse_dirhandle *dh, const char *name, int type, ino_t ino) { - struct fuse_dirent dirent; + size_t namelen = strlen(name); + struct fuse_dirent *dirent; size_t reclen; size_t res; + if (namelen > FUSE_NAME_MAX) + namelen = FUSE_NAME_MAX; + + dirent = calloc(1, sizeof(struct fuse_dirent) + namelen + 8); + if (dirent == NULL) + return -ENOMEM; + if ((dh->fuse->flags & FUSE_USE_INO)) - dirent.ino = ino; + dirent->ino = ino; else - dirent.ino = (unsigned long) -1; - 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); + dirent->ino = (unsigned long) -1; + dirent->namelen = namelen; + strncpy(dirent->name, name, namelen); + dirent->type = type; + reclen = FUSE_DIRENT_SIZE(dirent); + res = fwrite(dirent, reclen, 1, dh->fp); + free(dirent); if (res == 0) { perror("fuse: writing directory file"); return -EIO; @@ -403,26 +463,22 @@ static int fill_dir(struct fuse_dirhandle *dh, const char *name, int type, return 0; } -static int send_reply_raw(struct fuse *f, char *outbuf, size_t outsize, - int locked) +static int send_reply_raw(struct fuse *f, char *outbuf, size_t outsize) { int res; + struct fuse_out_header *out = (struct fuse_out_header *) outbuf; + out->len = outsize; if ((f->flags & FUSE_DEBUG)) { - struct fuse_out_header *out = (struct fuse_out_header *) outbuf; - printf(" unique: %i, error: %i (%s), outsize: %i\n", out->unique, - out->error, strerror(-out->error), outsize); + printf(" unique: %llu, error: %i (%s), outsize: %i\n", + out->unique, out->error, strerror(-out->error), outsize); fflush(stdout); } - + /* This needs to be done before the reply, otherwise the scheduler - could play tricks with us, and only let the counter be increased - long after the operation is done */ - if (!locked) - pthread_mutex_lock(&f->lock); - f->numavail ++; - if (!locked) - pthread_mutex_unlock(&f->lock); + could play tricks with us, and only let the counter be + increased long after the operation is done */ + fuse_inc_avail(f); res = write(f->fd, outbuf, outsize); if (res == -1) { @@ -434,8 +490,8 @@ static int send_reply_raw(struct fuse *f, char *outbuf, size_t outsize, return 0; } -static int do_send_reply(struct fuse *f, struct fuse_in_header *in, int error, - void *arg, size_t argsize, int locked) +static int send_reply(struct fuse *f, struct fuse_in_header *in, int error, + void *arg, size_t argsize) { int res; char *outbuf; @@ -463,25 +519,13 @@ static int do_send_reply(struct fuse *f, struct fuse_in_header *in, int error, if (argsize != 0) memcpy(outbuf + sizeof(struct fuse_out_header), arg, argsize); - res = send_reply_raw(f, outbuf, outsize, locked); + res = send_reply_raw(f, outbuf, outsize); free(outbuf); } return res; } -static int send_reply(struct fuse *f, struct fuse_in_header *in, int error, - void *arg, size_t argsize) -{ - return do_send_reply(f, in, error, arg, argsize, 0); -} - -static int send_reply_locked(struct fuse *f, struct fuse_in_header *in, - int error, void *arg, size_t argsize) -{ - return do_send_reply(f, in, error, arg, argsize, 1); -} - static int is_open(struct fuse *f, nodeid_t dir, const char *name) { struct node *node; @@ -511,9 +555,8 @@ static char *hidden_name(struct fuse *f, nodeid_t dir, const char *oldname, pthread_mutex_lock(&f->lock); node = lookup_node(f, dir, oldname); if (node == NULL) { - fprintf(stderr, "fuse internal error: node %lu/%s not found\n", - dir, oldname); - abort(); + pthread_mutex_unlock(&f->lock); + return NULL; } do { f->hidectr ++; @@ -556,8 +599,9 @@ static int hide_node(struct fuse *f, const char *oldpath, nodeid_t dir, return err; } -static int lookup_path(struct fuse *f, nodeid_t nodeid, int version, char *name, - const char *path, struct fuse_entry_out *arg) +static int lookup_path(struct fuse *f, nodeid_t nodeid, uint64_t version, + char *name, const char *path, + struct fuse_entry_out *arg) { int res; struct stat buf; @@ -579,7 +623,7 @@ static int lookup_path(struct fuse *f, nodeid_t nodeid, int version, char *name, arg->attr_valid = ATTR_REVALIDATE_TIME; arg->attr_valid_nsec = 0; if (f->flags & FUSE_DEBUG) { - printf(" NODEID: %li\n", arg->nodeid); + printf(" NODEID: %lu\n", (unsigned long) arg->nodeid); fflush(stdout); } } @@ -608,17 +652,18 @@ static void do_lookup(struct fuse *f, struct fuse_in_header *in, char *name) } res2 = send_reply(f, in, res, &arg, sizeof(arg)); if (res == 0 && res2 == -ENOENT) - destroy_node(f, arg.nodeid, in->unique); + forget_node(f, arg.nodeid, in->unique); } static void do_forget(struct fuse *f, struct fuse_in_header *in, struct fuse_forget_in *arg) { if (f->flags & FUSE_DEBUG) { - printf("FORGET %li/%i\n", in->nodeid, arg->version); + printf("FORGET %lu/%llu\n", (unsigned long) in->nodeid, + arg->version); fflush(stdout); } - destroy_node(f, in->nodeid, arg->version); + forget_node(f, in->nodeid, arg->version); } static void do_getattr(struct fuse *f, struct fuse_in_header *in) @@ -644,10 +689,6 @@ static void do_getattr(struct fuse *f, struct fuse_in_header *in) convert_stat(&buf, &arg.attr); if (!(f->flags & FUSE_USE_INO)) arg.attr.ino = in->nodeid; - else { - struct node *node = get_node(f, in->nodeid); - node->ino = arg.attr.ino; - } } send_reply(f, in, res, &arg, sizeof(arg)); @@ -737,10 +778,6 @@ static void do_setattr(struct fuse *f, struct fuse_in_header *in, convert_stat(&buf, &outarg.attr); if (!(f->flags & FUSE_USE_INO)) outarg.attr.ino = in->nodeid; - else { - struct node *node = get_node(f, in->nodeid); - node->ino = outarg.attr.ino; - } } } } @@ -826,7 +863,7 @@ static void do_mknod(struct fuse *f, struct fuse_in_header *in, } res2 = send_reply(f, in, res, &outarg, sizeof(outarg)); if (res == 0 && res2 == -ENOENT) - destroy_node(f, outarg.nodeid, in->unique); + forget_node(f, outarg.nodeid, in->unique); } static void do_mkdir(struct fuse *f, struct fuse_in_header *in, @@ -855,7 +892,7 @@ static void do_mkdir(struct fuse *f, struct fuse_in_header *in, } res2 = send_reply(f, in, res, &outarg, sizeof(outarg)); if (res == 0 && res2 == -ENOENT) - destroy_node(f, outarg.nodeid, in->unique); + forget_node(f, outarg.nodeid, in->unique); } static void do_unlink(struct fuse *f, struct fuse_in_header *in, char *name) @@ -878,6 +915,7 @@ static void do_unlink(struct fuse *f, struct fuse_in_header *in, char *name) res = f->op.unlink(path); if (res == 0) remove_node(f, in->nodeid, name); + } } free(path); @@ -933,7 +971,7 @@ static void do_symlink(struct fuse *f, struct fuse_in_header *in, char *name, } res2 = send_reply(f, in, res, &outarg, sizeof(outarg)); if (res == 0 && res2 == -ENOENT) - destroy_node(f, outarg.nodeid, in->unique); + forget_node(f, outarg.nodeid, in->unique); } @@ -1008,7 +1046,7 @@ static void do_link(struct fuse *f, struct fuse_in_header *in, } res2 = send_reply(f, in, res, &outarg, sizeof(outarg)); if (res == 0 && res2 == -ENOENT) - destroy_node(f, outarg.nodeid, in->unique); + forget_node(f, outarg.nodeid, in->unique); } static void do_open(struct fuse *f, struct fuse_in_header *in, @@ -1025,40 +1063,36 @@ static void do_open(struct fuse *f, struct fuse_in_header *in, path = get_path(f, in->nodeid); if (path != NULL) { res = -ENOSYS; - if (f->op.open.curr) { + if (f->op.open) { if (!f->compat) - res = f->op.open.curr(path, &fi); + res = f->op.open(path, &fi); else - res = f->op.open.compat2(path, fi.flags); + res = ((struct fuse_operations_compat2 *) &f->op)->open(path, fi.flags); } } if (res == 0) { int res2; - - /* If the request is interrupted the lock must be held until - the cancellation is finished. Otherwise there could be - races with rename/unlink, against which the kernel can't - protect */ - pthread_mutex_lock(&f->lock); outarg.fh = fi.fh; if (f->flags & FUSE_DEBUG) { - printf("OPEN[%lu] flags: 0x%x\n", outarg.fh, arg->flags); + printf("OPEN[%lu] flags: 0x%x\n", fi.fh, arg->flags); fflush(stdout); } - - res2 = send_reply_locked(f, in, res, &outarg, sizeof(outarg)); + + pthread_mutex_lock(&f->lock); + res2 = send_reply(f, in, res, &outarg, sizeof(outarg)); if(res2 == -ENOENT) { /* The open syscall was interrupted, so it must be cancelled */ - if(f->op.release.curr) { + if(f->op.release) { if (!f->compat) - f->op.release.curr(path, &fi); + f->op.release(path, &fi); else - f->op.release.compat2(path, fi.flags); + ((struct fuse_operations_compat2 *) &f->op)->release(path, fi.flags); } - } else - get_node(f, in->nodeid)->open_count ++; + } else { + struct node *node = get_node(f, in->nodeid); + node->open_count ++; + } pthread_mutex_unlock(&f->lock); - } else send_reply(f, in, res, NULL, 0); @@ -1079,7 +1113,7 @@ static void do_flush(struct fuse *f, struct fuse_in_header *in, path = get_path(f, in->nodeid); if (path != NULL) { if (f->flags & FUSE_DEBUG) { - printf("FLUSH[%lu]\n", arg->fh); + printf("FLUSH[%lu]\n", (unsigned long) arg->fh); fflush(stdout); } res = -ENOSYS; @@ -1096,6 +1130,7 @@ static void do_release(struct fuse *f, struct fuse_in_header *in, struct node *node; char *path; struct fuse_file_info fi; + int unlink_hidden; memset(&fi, 0, sizeof(fi)); fi.flags = arg->flags; @@ -1103,28 +1138,29 @@ static void do_release(struct fuse *f, struct fuse_in_header *in, pthread_mutex_lock(&f->lock); node = get_node(f, in->nodeid); + assert(node->open_count > 0); --node->open_count; + unlink_hidden = (node->is_hidden && !node->open_count); pthread_mutex_unlock(&f->lock); path = get_path(f, in->nodeid); - if (path != NULL) { - if (f->flags & FUSE_DEBUG) { - printf("RELEASE[%lu]\n", arg->fh); - fflush(stdout); - } - if (f->op.release.curr) { - if (!f->compat) - f->op.release.curr(path, &fi); - else - f->op.release.compat2(path, fi.flags); - } - - if(node->is_hidden && node->open_count == 0) - /* can now clean up this hidden file */ - f->op.unlink(path); - - free(path); + if (f->flags & FUSE_DEBUG) { + printf("RELEASE[%lu] flags: 0x%x\n", fi.fh, fi.flags); + fflush(stdout); + } + if (f->op.release) { + if (!f->compat) + f->op.release(path ? path : "-", &fi); + else if (path) + ((struct fuse_operations_compat2 *) &f->op)->release(path, fi.flags); } + + if(unlink_hidden && path) + f->op.unlink(path); + + if (path) + free(path); + send_reply(f, in, 0, NULL, 0); } @@ -1150,8 +1186,8 @@ static void do_read(struct fuse *f, struct fuse_in_header *in, path = get_path(f, in->nodeid); if (path != NULL) { if (f->flags & FUSE_DEBUG) { - printf("READ[%lu] %u bytes from %llu\n", arg->fh, arg->size, - arg->offset); + printf("READ[%lu] %u bytes from %llu\n", + (unsigned long) arg->fh, arg->size, arg->offset); fflush(stdout); } @@ -1166,7 +1202,8 @@ static void do_read(struct fuse *f, struct fuse_in_header *in, size = res; res = 0; if (f->flags & FUSE_DEBUG) { - printf(" READ[%lu] %u bytes\n", arg->fh, size); + printf(" READ[%lu] %u bytes\n", (unsigned long) arg->fh, + size); fflush(stdout); } } @@ -1175,7 +1212,7 @@ static void do_read(struct fuse *f, struct fuse_in_header *in, out->error = res; outsize = sizeof(struct fuse_out_header) + size; - send_reply_raw(f, outbuf, outsize, 0); + send_reply_raw(f, outbuf, outsize); free(outbuf); } } @@ -1190,15 +1227,15 @@ static void do_write(struct fuse *f, struct fuse_in_header *in, memset(&fi, 0, sizeof(fi)); fi.fh = arg->fh; - fi.writepage = arg->writepage; + fi.writepage = arg->write_flags & 1; res = -ENOENT; path = get_path(f, in->nodeid); if (path != NULL) { if (f->flags & FUSE_DEBUG) { printf("WRITE%s[%lu] %u bytes to %llu\n", - arg->writepage ? "PAGE" : "", arg->fh, arg->size, - arg->offset); + (arg->write_flags & 1) ? "PAGE" : "", + (unsigned long) arg->fh, arg->size, arg->offset); fflush(stdout); } @@ -1253,13 +1290,13 @@ static void do_statfs(struct fuse *f, struct fuse_in_header *in) struct statfs buf; memset(&buf, 0, sizeof(struct statfs)); - if (f->op.statfs.curr) { + if (f->op.statfs) { if (!f->compat || f->compat > 11) - res = f->op.statfs.curr("/", &buf); + res = f->op.statfs("/", &buf); else { struct fuse_statfs_compat1 compatbuf; memset(&compatbuf, 0, sizeof(struct fuse_statfs_compat1)); - res = f->op.statfs.compat1(&compatbuf); + res = ((struct fuse_operations_compat1 *) &f->op)->statfs(&compatbuf); if (res == 0) convert_statfs_compat(&compatbuf, &buf); } @@ -1287,12 +1324,12 @@ static void do_fsync(struct fuse *f, struct fuse_in_header *in, path = get_path(f, in->nodeid); if (path != NULL) { if (f->flags & FUSE_DEBUG) { - printf("FSYNC[%lu]\n", inarg->fh); + printf("FSYNC[%lu]\n", (unsigned long) inarg->fh); fflush(stdout); } res = -ENOSYS; if (f->op.fsync) - res = f->op.fsync(path, inarg->datasync, &fi); + res = f->op.fsync(path, inarg->fsync_flags & 1, &fi); free(path); } send_reply(f, in, res, NULL, 0); @@ -1355,7 +1392,7 @@ static void do_getxattr_read(struct fuse *f, struct fuse_in_header *in, out->unique = in->unique; out->error = res; - send_reply_raw(f, outbuf, sizeof(struct fuse_out_header) + size, 0); + send_reply_raw(f, outbuf, sizeof(struct fuse_out_header) + size); free(outbuf); } } @@ -1423,7 +1460,7 @@ static void do_listxattr_read(struct fuse *f, struct fuse_in_header *in, out->unique = in->unique; out->error = res; - send_reply_raw(f, outbuf, sizeof(struct fuse_out_header) + size, 0); + send_reply_raw(f, outbuf, sizeof(struct fuse_out_header) + size); free(outbuf); } } @@ -1481,12 +1518,12 @@ void fuse_process_cmd(struct fuse *f, struct fuse_cmd *cmd) size_t argsize; struct fuse_context *ctx = fuse_get_context(); - dec_avail(f); + fuse_dec_avail(f); if ((f->flags & FUSE_DEBUG)) { - printf("unique: %i, opcode: %s (%i), nodeid: %li, insize: %i\n", - in->unique, opname(in->opcode), in->opcode, in->nodeid, - cmd->buflen); + printf("unique: %llu, opcode: %s (%i), nodeid: %lu, insize: %i\n", + in->unique, opname(in->opcode), in->opcode, + (unsigned long) in->nodeid, cmd->buflen); fflush(stdout); } @@ -1790,9 +1827,9 @@ struct fuse *fuse_new_common(int fd, const char *opts, struct fuse *f; struct node *root; - if (sizeof(struct fuse_operations_i) < op_size) { + if (sizeof(struct fuse_operations) < op_size) { fprintf(stderr, "fuse: warning: library too old, some operations may not not work\n"); - op_size = sizeof(struct fuse_operations_i); + op_size = sizeof(struct fuse_operations); } f = (struct fuse *) calloc(1, sizeof(struct fuse)); @@ -1829,12 +1866,14 @@ struct fuse *fuse_new_common(int fd, const char *opts, #ifndef USE_UCLIBC pthread_mutex_init(&f->lock, NULL); + pthread_mutex_init(&f->worker_lock, NULL); #else { pthread_mutexattr_t attr; pthread_mutexattr_init(&attr); pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ADAPTIVE_NP); pthread_mutex_init(&f->lock, &attr); + pthread_mutex_init(&f->worker_lock, &attr); pthread_mutexattr_destroy(&attr); } #endif @@ -1850,8 +1889,6 @@ struct fuse *fuse_new_common(int fd, const char *opts, goto out_free_id_table; } - root->mode = 0; - root->rdev = 0; root->name = strdup("/"); if (root->name == NULL) { fprintf(stderr, "fuse: memory allocation failed\n"); @@ -1861,6 +1898,7 @@ struct fuse *fuse_new_common(int fd, const char *opts, root->parent = 0; root->nodeid = FUSE_ROOT_ID; root->generation = 0; + root->refctr = 1; hash_id(f, root); return f; @@ -1926,6 +1964,7 @@ void fuse_destroy(struct fuse *f) free(f->id_table); free(f->name_table); pthread_mutex_destroy(&f->lock); + pthread_mutex_destroy(&f->worker_lock); free(f); } diff --git a/lib/fuse_i.h b/lib/fuse_i.h index f3c3076..ebe712a 100644 --- a/lib/fuse_i.h +++ b/lib/fuse_i.h @@ -7,83 +7,14 @@ */ #include "fuse.h" -#include "fuse_compat.h" -#include #include -/* FUSE flags: */ - -/** Enable debuging output */ -#define FUSE_DEBUG (1 << 1) - -/** If a file is removed but it's still open, don't hide the file but - remove it immediately */ -#define FUSE_HARD_REMOVE (1 << 2) - -/** Use st_ino field in getattr instead of generating inode numbers */ -#define FUSE_USE_INO (1 << 3) - - typedef unsigned long nodeid_t; -struct node { - struct node *name_next; - struct node *id_next; - nodeid_t nodeid; - unsigned int generation; - nodeid_t parent; - char *name; - int mode; - int rdev; - unsigned long ino; - int version; - int open_count; - int is_hidden; -}; - -struct fuse_operations_i { - int (*getattr) (const char *, struct stat *); - int (*readlink) (const char *, char *, size_t); - int (*getdir) (const char *, fuse_dirh_t, fuse_dirfil_t); - int (*mknod) (const char *, mode_t, dev_t); - int (*mkdir) (const char *, mode_t); - int (*unlink) (const char *); - int (*rmdir) (const char *); - int (*symlink) (const char *, const char *); - int (*rename) (const char *, const char *); - int (*link) (const char *, const char *); - int (*chmod) (const char *, mode_t); - int (*chown) (const char *, uid_t, gid_t); - int (*truncate) (const char *, off_t); - int (*utime) (const char *, struct utimbuf *); - union { - int (*curr) (const char *, struct fuse_file_info *); - int (*compat2) (const char *, int); - } open; - int (*read) (const char *, char *, size_t, off_t, - struct fuse_file_info *); - int (*write) (const char *, const char *, size_t, off_t, - struct fuse_file_info *); - union { - int (*curr) (const char *, struct statfs *); - int (*compat1) (struct fuse_statfs_compat1 *); - } statfs; - int (*flush) (const char *, struct fuse_file_info *); - union { - int (*curr) (const char *, struct fuse_file_info *); - int (*compat2) (const char *, int); - } release; - int (*fsync) (const char *, int, struct fuse_file_info *); - int (*setxattr) (const char *, const char *, const char *, size_t, int); - int (*getxattr) (const char *, const char *, char *, size_t); - int (*listxattr) (const char *, char *, size_t); - int (*removexattr) (const char *, const char *); -}; - struct fuse { int flags; int fd; - struct fuse_operations_i op; + struct fuse_operations op; int compat; struct node **name_table; size_t name_table_size; @@ -93,6 +24,7 @@ struct fuse { unsigned int generation; unsigned int hidectr; pthread_mutex_t lock; + pthread_mutex_t worker_lock; int numworker; int numavail; volatile int exited; @@ -100,17 +32,6 @@ struct fuse { int minorver; }; -struct fuse_dirhandle { - struct fuse *fuse; - nodeid_t dir; - FILE *fp; -}; - -struct fuse_cmd { - char *buf; - size_t buflen; -}; - struct fuse *fuse_new_common(int fd, const char *opts, const struct fuse_operations *op, size_t op_size, int compat); diff --git a/lib/fuse_mt.c b/lib/fuse_mt.c index 94d3bb8..c02d5c4 100644 --- a/lib/fuse_mt.c +++ b/lib/fuse_mt.c @@ -40,9 +40,9 @@ static void *do_work(void *data) ctx = (struct fuse_context *) malloc(sizeof(struct fuse_context)); if (ctx == NULL) { fprintf(stderr, "fuse: failed to allocate fuse context\n"); - pthread_mutex_lock(&f->lock); + pthread_mutex_lock(&f->worker_lock); f->numavail --; - pthread_mutex_unlock(&f->lock); + pthread_mutex_unlock(&f->worker_lock); return NULL; } pthread_setspecific(context_key, ctx); @@ -61,7 +61,7 @@ static void *do_work(void *data) continue; if (f->numavail == 0 && f->numworker < FUSE_MAX_WORKERS) { - pthread_mutex_lock(&f->lock); + pthread_mutex_lock(&f->worker_lock); if (f->numworker < FUSE_MAX_WORKERS) { /* FIXME: threads should be stored in a list instead of an array */ @@ -69,15 +69,15 @@ static void *do_work(void *data) pthread_t *thread_id = &w->threads[f->numworker]; f->numavail ++; f->numworker ++; - pthread_mutex_unlock(&f->lock); + pthread_mutex_unlock(&f->worker_lock); res = start_thread(w, thread_id); if (res == -1) { - pthread_mutex_lock(&f->lock); + pthread_mutex_lock(&f->worker_lock); f->numavail --; - pthread_mutex_unlock(&f->lock); + pthread_mutex_unlock(&f->worker_lock); } } else - pthread_mutex_unlock(&f->lock); + pthread_mutex_unlock(&f->worker_lock); } w->proc(w->f, cmd, w->data); diff --git a/lib/helper.c b/lib/helper.c index c14248d..25a60aa 100644 --- a/lib/helper.c +++ b/lib/helper.c @@ -7,6 +7,7 @@ */ #include "fuse_i.h" +#include "fuse_compat.h" #include #include @@ -90,6 +91,19 @@ static int set_signal_handlers() return 0; } +static int opt_member(const char *opts, const char *opt) +{ + const char *e, *s = opts; + int optlen = strlen(opt); + for (s = opts; s; s = e + 1) { + if(!(e = strchr(s, ','))) + break; + if (e - s == optlen && strncmp(s, opt, optlen) == 0) + return 1; + } + return (s && strcmp(s, opt) == 0); +} + static int add_option_to(const char *opt, char **optp) { unsigned len = strlen(opt); @@ -187,7 +201,6 @@ static int fuse_parse_cmdline(int argc, const char *argv[], char **kernel_opts, res = add_options(lib_opts, kernel_opts, "debug"); if (res == -1) goto err; - *background = 0; break; case 'r': @@ -251,7 +264,6 @@ static int fuse_parse_cmdline(int argc, const char *argv[], char **kernel_opts, free(*mountpoint); return -1; } - static struct fuse *fuse_setup_common(int argc, char *argv[], const struct fuse_operations *op, @@ -286,7 +298,7 @@ static struct fuse *fuse_setup_common(int argc, char *argv[], if (fuse == NULL) goto err_unmount; - if (background) { + if (background && !opt_member(lib_opts, "debug")) { res = daemon(0, 0); if (res == -1) { perror("fuse: failed to daemonize program\n");