From: Miklos Szeredi Date: Sat, 30 Sep 2006 12:21:49 +0000 (+0000) Subject: kernel changes X-Git-Tag: fuse_2_6_0_rc2~7 X-Git-Url: http://git.maquefel.me/?a=commitdiff_plain;h=d93a4295c13503e52b2c3a2f6b4473a6d5bf907c;p=qemu-gpiodev%2Flibfuse.git kernel changes --- diff --git a/ChangeLog b/ChangeLog index 57b2d00..a14f301 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,9 +1,28 @@ 2006-09-30 Miklos Szeredi + * kernel changes: + * Drop support for kernels earlier than 2.6.9. Kernel module from previous (2.5.x) release can be used with library from this release + * In fuse_dentry_revalidate() use dget_parent() instead of + dereferencing d_parent, since there's no protection against parent + changing and going away + + * Protect nlookup from concurrent updates + + * In lookup if a directory alias exists but is unused, + then get rid of it, otherwise return -EBUSY. + + * In mkdir if a directory alias exists, return success, but leave + dentry negative. In reality this could happen if a remote rename + immediately followed the mkdir. + + * Don't BUG in fuse_iget() if multiple retries are needed to get a + good inode. This could happen if several lookups are racing for + the same inode. + 2006-09-29 Miklos Szeredi * Fix compilation on 2.6.9. Report from Troy Ayers diff --git a/kernel/Makefile.in b/kernel/Makefile.in index c016d1a..2392fd7 100644 --- a/kernel/Makefile.in +++ b/kernel/Makefile.in @@ -7,7 +7,6 @@ VERSION = @PACKAGE_VERSION@ DISTFILES = Makefile.in configure.ac configure config.h.in ../install-sh \ dev.c dir.c file.c inode.c fuse_i.h fuse_kernel.h control.c -COMPATDISTFILES = compat/parser.c compat/parser.h fusemoduledir = @kmoduledir@/kernel/fs/fuse @@ -42,10 +41,8 @@ distclean: clean maintainer-clean: distclean -distdir: $(DISTFILES) $(COMPATDISTFILES) +distdir: $(DISTFILES) cp -p $(DISTFILES) $(distdir) - mkdir $(distdir)/compat - cp -p $(COMPATDISTFILES) $(distdir)/compat EXTRA_CFLAGS += -DFUSE_VERSION=\"$(VERSION)\" diff --git a/kernel/compat/parser.c b/kernel/compat/parser.c deleted file mode 100644 index fa1d63e..0000000 --- a/kernel/compat/parser.c +++ /dev/null @@ -1,220 +0,0 @@ -/* - * lib/parser.c - simple parser for mount, etc. options. - * - * This source code is licensed under the GNU General Public License, - * Version 2. See the file COPYING for more details. - */ - -#include -#ifdef CONFIG_MODVERSIONS -#define MODVERSIONS -#include -#endif - -#include "parser.h" - -#include -#include -#include -#include - -/** - * match_one: - Determines if a string matches a simple pattern - * @s: the string to examine for presense of the pattern - * @p: the string containing the pattern - * @args: array of %MAX_OPT_ARGS &substring_t elements. Used to return match - * locations. - * - * Description: Determines if the pattern @p is present in string @s. Can only - * match extremely simple token=arg style patterns. If the pattern is found, - * the location(s) of the arguments will be returned in the @args array. - */ -static int match_one(char *s, char *p, substring_t args[]) -{ - char *meta; - int argc = 0; - - if (!p) - return 1; - - while(1) { - int len = -1; - meta = strchr(p, '%'); - if (!meta) - return strcmp(p, s) == 0; - - if (strncmp(p, s, meta-p)) - return 0; - - s += meta - p; - p = meta + 1; - - if (isdigit(*p)) - len = simple_strtoul(p, &p, 10); - else if (*p == '%') { - if (*s++ != '%') - return 0; - continue; - } - - if (argc >= MAX_OPT_ARGS) - return 0; - - args[argc].from = s; - switch (*p++) { - case 's': - if (strlen(s) == 0) - return 0; - else if (len == -1 || len > strlen(s)) - len = strlen(s); - args[argc].to = s + len; - break; - case 'd': - simple_strtol(s, &args[argc].to, 0); - goto num; - case 'u': - simple_strtoul(s, &args[argc].to, 0); - goto num; - case 'o': - simple_strtoul(s, &args[argc].to, 8); - goto num; - case 'x': - simple_strtoul(s, &args[argc].to, 16); - num: - if (args[argc].to == args[argc].from) - return 0; - break; - default: - return 0; - } - s = args[argc].to; - argc++; - } -} - -/** - * match_token: - Find a token (and optional args) in a string - * @s: the string to examine for token/argument pairs - * @table: match_table_t describing the set of allowed option tokens and the - * arguments that may be associated with them. Must be terminated with a - * &struct match_token whose pattern is set to the NULL pointer. - * @args: array of %MAX_OPT_ARGS &substring_t elements. Used to return match - * locations. - * - * Description: Detects which if any of a set of token strings has been passed - * to it. Tokens can include up to MAX_OPT_ARGS instances of basic c-style - * format identifiers which will be taken into account when matching the - * tokens, and whose locations will be returned in the @args array. - */ -int match_token(char *s, match_table_t table, substring_t args[]) -{ - struct match_token *p; - - for (p = table; !match_one(s, p->pattern, args) ; p++) - ; - - return p->token; -} - -/** - * match_number: scan a number in the given base from a substring_t - * @s: substring to be scanned - * @result: resulting integer on success - * @base: base to use when converting string - * - * Description: Given a &substring_t and a base, attempts to parse the substring - * as a number in that base. On success, sets @result to the integer represented - * by the string and returns 0. Returns either -ENOMEM or -EINVAL on failure. - */ -static int match_number(substring_t *s, int *result, int base) -{ - char *endp; - char *buf; - int ret; - - buf = kmalloc(s->to - s->from + 1, GFP_KERNEL); - if (!buf) - return -ENOMEM; - memcpy(buf, s->from, s->to - s->from); - buf[s->to - s->from] = '\0'; - *result = simple_strtol(buf, &endp, base); - ret = 0; - if (endp == buf) - ret = -EINVAL; - kfree(buf); - return ret; -} - -/** - * match_int: - scan a decimal representation of an integer from a substring_t - * @s: substring_t to be scanned - * @result: resulting integer on success - * - * Description: Attempts to parse the &substring_t @s as a decimal integer. On - * success, sets @result to the integer represented by the string and returns 0. - * Returns either -ENOMEM or -EINVAL on failure. - */ -int match_int(substring_t *s, int *result) -{ - return match_number(s, result, 0); -} - -/** - * match_octal: - scan an octal representation of an integer from a substring_t - * @s: substring_t to be scanned - * @result: resulting integer on success - * - * Description: Attempts to parse the &substring_t @s as an octal integer. On - * success, sets @result to the integer represented by the string and returns - * 0. Returns either -ENOMEM or -EINVAL on failure. - */ -int match_octal(substring_t *s, int *result) -{ - return match_number(s, result, 8); -} - -/** - * match_hex: - scan a hex representation of an integer from a substring_t - * @s: substring_t to be scanned - * @result: resulting integer on success - * - * Description: Attempts to parse the &substring_t @s as a hexadecimal integer. - * On success, sets @result to the integer represented by the string and - * returns 0. Returns either -ENOMEM or -EINVAL on failure. - */ -int match_hex(substring_t *s, int *result) -{ - return match_number(s, result, 16); -} - -/** - * match_strcpy: - copies the characters from a substring_t to a string - * @to: string to copy characters to. - * @s: &substring_t to copy - * - * Description: Copies the set of characters represented by the given - * &substring_t @s to the c-style string @to. Caller guarantees that @to is - * large enough to hold the characters of @s. - */ -void match_strcpy(char *to, substring_t *s) -{ - memcpy(to, s->from, s->to - s->from); - to[s->to - s->from] = '\0'; -} - -/** - * match_strdup: - allocate a new string with the contents of a substring_t - * @s: &substring_t to copy - * - * Description: Allocates and returns a string filled with the contents of - * the &substring_t @s. The caller is responsible for freeing the returned - * string with kfree(). - */ -char *match_strdup(substring_t *s) -{ - char *p = kmalloc(s->to - s->from + 1, GFP_KERNEL); - if (p) - match_strcpy(p, s); - return p; -} - diff --git a/kernel/compat/parser.h b/kernel/compat/parser.h deleted file mode 100644 index fa33328..0000000 --- a/kernel/compat/parser.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * linux/include/linux/parser.h - * - * Header for lib/parser.c - * Intended use of these functions is parsing filesystem argument lists, - * but could potentially be used anywhere else that simple option=arg - * parsing is required. - */ - - -/* associates an integer enumerator with a pattern string. */ -struct match_token { - int token; - char *pattern; -}; - -typedef struct match_token match_table_t[]; - -/* Maximum number of arguments that match_token will find in a pattern */ -enum {MAX_OPT_ARGS = 3}; - -/* Describe the location within a string of a substring */ -typedef struct { - char *from; - char *to; -} substring_t; - -int match_token(char *, match_table_t table, substring_t args[]); -int match_int(substring_t *, int *result); -int match_octal(substring_t *, int *result); -int match_hex(substring_t *, int *result); -void match_strcpy(char *, substring_t *); -char *match_strdup(substring_t *); diff --git a/kernel/dir.c b/kernel/dir.c index c238674..c2f7d4e 100644 --- a/kernel/dir.c +++ b/kernel/dir.c @@ -138,9 +138,7 @@ static int fuse_dentry_revalidate(struct dentry *entry, struct nameidata *nd) struct fuse_entry_out outarg; struct fuse_conn *fc; struct fuse_req *req; - - /* Doesn't hurt to "reset" the validity timeout */ - fuse_invalidate_entry_cache(entry); + struct dentry *parent; /* For negative dentries, always do a fresh lookup */ if (!inode) @@ -151,7 +149,13 @@ static int fuse_dentry_revalidate(struct dentry *entry, struct nameidata *nd) if (IS_ERR(req)) return 0; - fuse_lookup_init(req, entry->d_parent->d_inode, entry, &outarg); + parent = dget_parent(entry); + if (!parent->d_inode) { + dput(parent); + return 0; + } + fuse_lookup_init(req, parent->d_inode, entry, &outarg); + dput(parent); request_send(fc, req); err = req->out.h.error; /* Zero nodeid is same as -ENOENT */ @@ -163,7 +167,9 @@ static int fuse_dentry_revalidate(struct dentry *entry, struct nameidata *nd) fuse_send_forget(fc, req, outarg.nodeid, 1); return 0; } + spin_lock(&fc->lock); fi->nlookup ++; + spin_unlock(&fc->lock); } fuse_put_request(fc, req); if (err || (outarg.attr.mode ^ inode->i_mode) & S_IFMT) @@ -175,30 +181,6 @@ static int fuse_dentry_revalidate(struct dentry *entry, struct nameidata *nd) return 1; } -/* - * Check if there's already a hashed alias of this directory inode. - * If yes, then lookup and mkdir must not create a new alias. - */ -static int dir_alias(struct inode *inode) -{ - if (S_ISDIR(inode->i_mode)) { - struct dentry *alias = d_find_alias(inode); -#if defined(FUSE_MAINLINE) - if (alias) { - dput(alias); - return 1; - } -#else - if (alias && !(alias->d_flags & DCACHE_DISCONNECTED)) { - dput(alias); - return 1; - } - dput(alias); -#endif - } - return 0; -} - static int invalid_nodeid(u64 nodeid) { return !nodeid || nodeid == FUSE_ROOT_ID; @@ -220,9 +202,7 @@ static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry, int err; struct fuse_entry_out outarg; struct inode *inode = NULL; -#if !defined(FUSE_MAINLINE) struct dentry *newent; -#endif struct fuse_conn *fc = get_fuse_conn(dir); struct fuse_req *req; @@ -252,26 +232,32 @@ static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry, if (err && err != -ENOENT) return ERR_PTR(err); - if (inode && dir_alias(inode)) { - iput(inode); - return ERR_PTR(-EIO); - } -#if defined(FUSE_MAINLINE) - d_add(entry, inode); -#else - newent = d_splice_alias(inode, entry); + if (inode && S_ISDIR(inode->i_mode)) { + struct dentry *alias; + mutex_lock(&fc->inst_mutex); + alias = d_find_alias(inode); + if (alias && !(alias->d_flags & DCACHE_DISCONNECTED)) { + fuse_invalidate_entry(alias); + dput(alias); + if (!list_empty(&inode->i_dentry)) { + mutex_unlock(&fc->inst_mutex); + iput(inode); + return ERR_PTR(-EBUSY); + } + } else + dput(alias); + newent = d_splice_alias(inode, entry); + mutex_unlock(&fc->inst_mutex); + } else + newent = d_splice_alias(inode, entry); + entry = newent ? newent : entry; -#endif entry->d_op = &fuse_dentry_operations; if (!err) fuse_change_timeout(entry, &outarg); else fuse_invalidate_entry_cache(entry); -#if defined(FUSE_MAINLINE) - return NULL; -#else return newent; -#endif } #ifdef HAVE_LOOKUP_INSTANTIATE_FILP @@ -423,12 +409,23 @@ static int create_new_entry(struct fuse_conn *fc, struct fuse_req *req, } fuse_put_request(fc, req); - if (dir_alias(inode)) { - iput(inode); - return -EIO; - } + if (S_ISDIR(inode->i_mode)) { + struct dentry *alias; + mutex_lock(&fc->inst_mutex); + alias = d_find_alias(inode); + if (alias) { + /* New directory must have moved since mkdir */ + mutex_unlock(&fc->inst_mutex); + dput(alias); + iput(inode); + fuse_invalidate_entry(entry); + return 0; + } + d_instantiate(entry, inode); + mutex_unlock(&fc->inst_mutex); + } else + d_instantiate(entry, inode); - d_instantiate(entry, inode); fuse_change_timeout(entry, &outarg); fuse_invalidate_attr(dir); return 0; diff --git a/kernel/fuse_i.h b/kernel/fuse_i.h index 2515df2..e44dd9a 100644 --- a/kernel/fuse_i.h +++ b/kernel/fuse_i.h @@ -57,8 +57,11 @@ #else #include #define DEFINE_MUTEX(m) DECLARE_MUTEX(m) +#define mutex_init(m) init_MUTEX(m) +#define mutex_destroy(m) do { } while (0) #define mutex_lock(m) down(m) #define mutex_unlock(m) up(m) +#define mutex semaphore #endif #ifndef BUG_ON @@ -97,11 +100,7 @@ extern struct list_head fuse_conn_list; /** Global mutex protecting fuse_conn_list and the control filesystem */ -#ifdef KERNEL_2_6_17_PLUS extern struct mutex fuse_mutex; -#else -extern struct semaphore fuse_mutex; -#endif /** FUSE inode */ struct fuse_inode { @@ -299,6 +298,9 @@ struct fuse_conn { /** Lock protecting accessess to members of this structure */ spinlock_t lock; + /** Mutex protecting against directory alias creation */ + struct mutex inst_mutex; + /** Refcount */ atomic_t count; diff --git a/kernel/inode.c b/kernel/inode.c index 949b456..2d4749e 100644 --- a/kernel/inode.c +++ b/kernel/inode.c @@ -106,8 +106,11 @@ static void fuse_clear_inode(struct inode *inode) if (inode->i_sb->s_flags & MS_ACTIVE) { struct fuse_conn *fc = get_fuse_conn(inode); struct fuse_inode *fi = get_fuse_inode(inode); - fuse_send_forget(fc, fi->forget_req, fi->nodeid, fi->nlookup); - fi->forget_req = NULL; + if (fi->nlookup) { + fuse_send_forget(fc, fi->forget_req, fi->nodeid, + fi->nlookup); + fi->forget_req = NULL; + } } } @@ -182,7 +185,6 @@ struct inode *fuse_iget(struct super_block *sb, unsigned long nodeid, struct inode *inode; struct fuse_inode *fi; 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); @@ -196,16 +198,16 @@ struct inode *fuse_iget(struct super_block *sb, unsigned long nodeid, 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; } fi = get_fuse_inode(inode); + spin_lock(&fc->lock); fi->nlookup ++; + spin_unlock(&fc->lock); fuse_change_attributes(inode, attr); return inode; } @@ -414,6 +416,7 @@ static struct fuse_conn *new_conn(void) fc = kzalloc(sizeof(*fc), GFP_KERNEL); if (fc) { spin_lock_init(&fc->lock); + mutex_init(&fc->inst_mutex); atomic_set(&fc->count, 1); init_waitqueue_head(&fc->waitq); init_waitqueue_head(&fc->blocked_waitq); @@ -433,8 +436,10 @@ static struct fuse_conn *new_conn(void) void fuse_conn_put(struct fuse_conn *fc) { - if (atomic_dec_and_test(&fc->count)) + if (atomic_dec_and_test(&fc->count)) { + mutex_destroy(&fc->inst_mutex); kfree(fc); + } } struct fuse_conn *fuse_conn_get(struct fuse_conn *fc)