kernel changes
authorMiklos Szeredi <miklos@szeredi.hu>
Sat, 30 Sep 2006 12:21:49 +0000 (12:21 +0000)
committerMiklos Szeredi <miklos@szeredi.hu>
Sat, 30 Sep 2006 12:21:49 +0000 (12:21 +0000)
ChangeLog
kernel/Makefile.in
kernel/compat/parser.c [deleted file]
kernel/compat/parser.h [deleted file]
kernel/dir.c
kernel/fuse_i.h
kernel/inode.c

index 57b2d005edcac9cf34341a848d314b5bf622ccc8..a14f3019008947b4a034d5f8c19823daa5e0f047 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,9 +1,28 @@
 2006-09-30  Miklos Szeredi <miklos@szeredi.hu>
 
+       * 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 <miklos@szeredi.hu>
 
        * Fix compilation on 2.6.9.  Report from Troy Ayers
index c016d1a682bce956b743f391b218e57bbd81eaac..2392fd73fa5c2e79cb939365632dc08a87c2618e 100644 (file)
@@ -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 (file)
index fa1d63e..0000000
+++ /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 <linux/config.h>
-#ifdef CONFIG_MODVERSIONS
-#define MODVERSIONS
-#include <linux/modversions.h>
-#endif
-
-#include "parser.h"
-
-#include <linux/ctype.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/string.h>
-
-/**
- * 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 (file)
index fa33328..0000000
+++ /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 *);
index c238674e1185ca8549d6649b392e297b4ef487aa..c2f7d4e62d1d1995041d9f673e04e05feae867a5 100644 (file)
@@ -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;
index 2515df2f77284d8a4711c58352b1268311d5e275..e44dd9a343c3470bb3c7ecdf9b1ef49946583aee 100644 (file)
 #else
 #include <asm/semaphore.h>
 #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
 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;
 
index 949b456c411373f75445dd4ba31e2e269cf41612..2d4749eed69e571319ae287b8d70f046b012a6e8 100644 (file)
@@ -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)