]> Pileus Git - ~andy/linux/blobdiff - fs/exportfs/expfs.c
exportfs: BUG_ON in crazy corner case
[~andy/linux] / fs / exportfs / expfs.c
index 293bc2e47a735807a75eaad424764315172367b6..6d0a7fa9abb3010141f5407e465296645b83855f 100644 (file)
@@ -112,18 +112,14 @@ reconnect_path(struct vfsmount *mnt, struct dentry *target_dir, char *nbuf)
        while (target_dir->d_flags & DCACHE_DISCONNECTED && noprogress++ < 10) {
                struct dentry *pd = find_disconnected_root(target_dir);
 
+               BUG_ON(pd == mnt->mnt_sb->s_root);
+
                if (!IS_ROOT(pd)) {
                        /* must have found a connected parent - great */
                        spin_lock(&pd->d_lock);
                        pd->d_flags &= ~DCACHE_DISCONNECTED;
                        spin_unlock(&pd->d_lock);
                        noprogress = 0;
-               } else if (pd == mnt->mnt_sb->s_root) {
-                       printk(KERN_ERR "export: Eeek filesystem root is not connected, impossible\n");
-                       spin_lock(&pd->d_lock);
-                       pd->d_flags &= ~DCACHE_DISCONNECTED;
-                       spin_unlock(&pd->d_lock);
-                       noprogress = 0;
                } else {
                        /*
                         * We have hit the top of a disconnected path, try to
@@ -215,7 +211,7 @@ struct getdents_callback {
        struct dir_context ctx;
        char *name;             /* name that was found. It already points to a
                                   buffer NAME_MAX+1 is size */
-       unsigned long ino;      /* the inum we are looking for */
+       u64 ino;                /* the inum we are looking for */
        int found;              /* inode matched? */
        int sequence;           /* sequence counter */
 };
@@ -231,7 +227,7 @@ static int filldir_one(void * __buf, const char * name, int len,
        int result = 0;
 
        buf->sequence++;
-       if (buf->ino == ino) {
+       if (buf->ino == ino && len <= NAME_MAX) {
                memcpy(buf->name, name, len);
                buf->name[len] = '\0';
                buf->found = 1;
@@ -255,10 +251,14 @@ static int get_name(const struct path *path, char *name, struct dentry *child)
        struct inode *dir = path->dentry->d_inode;
        int error;
        struct file *file;
+       struct kstat stat;
+       struct path child_path = {
+               .mnt = path->mnt,
+               .dentry = child,
+       };
        struct getdents_callback buffer = {
                .ctx.actor = filldir_one,
                .name = name,
-               .ino = child->d_inode->i_ino
        };
 
        error = -ENOTDIR;
@@ -267,6 +267,16 @@ static int get_name(const struct path *path, char *name, struct dentry *child)
        error = -EINVAL;
        if (!dir->i_fop)
                goto out;
+       /*
+        * inode->i_ino is unsigned long, kstat->ino is u64, so the
+        * former would be insufficient on 32-bit hosts when the
+        * filesystem supports 64-bit inode numbers.  So we need to
+        * actually call ->getattr, not just read i_ino:
+        */
+       error = vfs_getattr_nosec(&child_path, &stat);
+       if (error)
+               return error;
+       buffer.ino = stat.ino;
        /*
         * Open the directory ...
         */