Re: cifs and win2k8

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



2011/11/10 Jeff Layton <jlayton@xxxxxxxxx>:
> On Tue, 1 Nov 2011 22:56:18 +0300
> Pavel Shilovsky <piastry@xxxxxxxxxxx> wrote:
>
>> 2011/11/1 Pavel Shilovsky <piastry@xxxxxxxxxxx>:
>> > Sorry for so long delay on this. Here is the fixed version of the patch:
>> >
>> > [PATCH] CIFS: Fix NT_STATUS_ACCESS_DENIED for mounts with prefixpath option
>> >
>> > but I found out one use case where it doesn't work right:
>> >
>> > 1) mount //server/share/a/b/ test
>> > 2) mount //server/share/ test2
>> > 3) stat test2/a/b - return wrong inode number (autodisable server inode number happens)
>> >
>> > In the same time if I add "ls test2" before step #3 it works! May be VFS can't
>> > handle negative dentry with children in this case, but I am not sure.
>> >
>> > Reorganize code and make it send qpath info request only for a full
>> > path (//server/share/prefixpath) rather than request for every path
>> > compoment. In this case we end up with negative dentries for all
>> > components except full one and we will instantiate them later if
>> > such a mount is requested.
>> >
>> > Signed-off-by: Pavel Shilovsky <piastry@xxxxxxxxxxx>
>> > ---
>> >  fs/cifs/cifsfs.c   |  138 +++++++++++++++++++++++++++++++--------------------
>> >  fs/cifs/cifsfs.h   |    3 +-
>> >  fs/cifs/cifsglob.h |    6 ++
>> >  fs/cifs/dir.c      |    3 +-
>> >  fs/cifs/inode.c    |    7 ++-
>> >  fs/cifs/readdir.c  |   43 ++++++++++------
>> >  6 files changed, 124 insertions(+), 76 deletions(-)
>> >
>> > diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c
>> > index 8f1fe32..a188be1 100644
>> > --- a/fs/cifs/cifsfs.c
>> > +++ b/fs/cifs/cifsfs.c
>> > @@ -90,14 +90,13 @@ extern mempool_t *cifs_sm_req_poolp;
>> >  extern mempool_t *cifs_req_poolp;
>> >  extern mempool_t *cifs_mid_poolp;
>> >
>> > -static int
>> > +static void
>> >  cifs_read_super(struct super_block *sb)
>> >  {
>> > -       struct inode *inode;
>> > -       struct cifs_sb_info *cifs_sb;
>> > -       int rc = 0;
>> > +       struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
>> >
>> > -       cifs_sb = CIFS_SB(sb);
>> > +       /* BB should we make this contingent on mount parm? */
>> > +       sb->s_flags |= MS_NODIRATIME | MS_NOATIME;
>> >
>> >        if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIXACL)
>> >                sb->s_flags |= MS_POSIXACL;
>> > @@ -115,26 +114,6 @@ cifs_read_super(struct super_block *sb)
>> >        sb->s_bdi = &cifs_sb->bdi;
>> >        sb->s_blocksize = CIFS_MAX_MSGSIZE;
>> >        sb->s_blocksize_bits = 14;      /* default 2**14 = CIFS_MAX_MSGSIZE */
>> > -       inode = cifs_root_iget(sb);
>> > -
>> > -       if (IS_ERR(inode)) {
>> > -               rc = PTR_ERR(inode);
>> > -               inode = NULL;
>> > -               goto out_no_root;
>> > -       }
>> > -
>> > -       sb->s_root = d_alloc_root(inode);
>> > -
>> > -       if (!sb->s_root) {
>> > -               rc = -ENOMEM;
>> > -               goto out_no_root;
>> > -       }
>> > -
>> > -       /* do that *after* d_alloc_root() - we want NULL ->d_op for root here */
>> > -       if (cifs_sb_master_tcon(cifs_sb)->nocase)
>> > -               sb->s_d_op = &cifs_ci_dentry_ops;
>> > -       else
>> > -               sb->s_d_op = &cifs_dentry_ops;
>> >
>> >  #ifdef CONFIG_CIFS_NFSD_EXPORT
>> >        if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) {
>> > @@ -143,14 +122,7 @@ cifs_read_super(struct super_block *sb)
>> >        }
>> >  #endif /* CONFIG_CIFS_NFSD_EXPORT */
>> >
>> > -       return 0;
>> > -
>> > -out_no_root:
>> > -       cERROR(1, "cifs_read_super: get root inode failed");
>> > -       if (inode)
>> > -               iput(inode);
>> > -
>> > -       return rc;
>> > +       sb->s_flags |= MS_ACTIVE;
>> >  }
>> >
>> >  static void cifs_kill_sb(struct super_block *sb)
>> > @@ -528,6 +500,17 @@ static const struct super_operations cifs_super_ops = {
>> >  #endif
>> >  };
>> >
>> > +static struct dentry *
>> > +cifs_alloc_root(struct super_block *sb)
>> > +{
>> > +       struct qstr q;
>> > +
>> > +       q.name = "/";
>> > +       q.len = 1;
>> > +       q.hash = full_name_hash(q.name, q.len);
>> > +       return d_alloc_pseudo(sb, &q);
>> > +}
>> > +
>> >  /*
>> >  * Get root dentry from superblock according to prefix path mount option.
>> >  * Return dentry with refcount + 1 on success and NULL otherwise.
>> > @@ -535,8 +518,10 @@ static const struct super_operations cifs_super_ops = {
>> >  static struct dentry *
>> >  cifs_get_root(struct smb_vol *vol, struct super_block *sb)
>> >  {
>> > -       struct dentry *dentry;
>> > +       struct dentry *dentry, *found;
>> > +       struct inode *inode;
>> >        struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
>> > +       struct qstr q;
>> >        char *full_path = NULL;
>> >        char *s, *p;
>> >        char sep;
>> > @@ -548,20 +533,30 @@ cifs_get_root(struct smb_vol *vol, struct super_block *sb)
>> >
>> >        cFYI(1, "Get root dentry for %s", full_path);
>> >
>> > +       if (!sb->s_root) {
>> > +               sb->s_root = cifs_alloc_root(sb);
>> > +               if (IS_ERR(sb->s_root)) {
>> > +                       dentry = ERR_PTR(-ENOMEM);
>> > +                       goto out;
>> > +               }
>> > +
>> > +               /*
>> > +                * do that *after* cifs_alloc_root() - we want NULL ->d_op for
>> > +                * root here
>> > +                */
>> > +               if (cifs_sb_master_tcon(cifs_sb)->nocase)
>> > +                       sb->s_d_op = &cifs_ci_dentry_ops;
>> > +               else
>> > +                       sb->s_d_op = &cifs_dentry_ops;
>> > +       }
>> > +
>> >        sep = CIFS_DIR_SEP(cifs_sb);
>> >        dentry = dget(sb->s_root);
>> >        p = s = full_path;
>> >
>> >        do {
>> > -               struct inode *dir = dentry->d_inode;
>> >                struct dentry *child;
>> >
>> > -               if (!dir) {
>> > -                       dput(dentry);
>> > -                       dentry = ERR_PTR(-ENOENT);
>> > -                       break;
>> > -               }
>> > -
>> >                /* skip separators */
>> >                while (*s == sep)
>> >                        s++;
>> > @@ -572,12 +567,55 @@ cifs_get_root(struct smb_vol *vol, struct super_block *sb)
>> >                while (*s && *s != sep)
>> >                        s++;
>> >
>> > -               mutex_lock(&dir->i_mutex);
>> > -               child = lookup_one_len(p, dentry, s - p);
>> > -               mutex_unlock(&dir->i_mutex);
>> > +               q.name = p;
>> > +               q.len = s - p;
>> > +               if (dentry->d_flags & DCACHE_OP_HASH)
>> > +                       dentry->d_op->d_hash(dentry, dentry->d_inode, &q);
>> > +               else
>> > +                       q.hash = full_name_hash(q.name, q.len);
>> > +
>> > +               child = d_lookup(dentry, &q);
>> > +               if (!child) {
>> > +                       child = d_alloc(dentry, &q);
>> > +                       if (IS_ERR(child)) {
>> > +                               dput(dentry);
>> > +                               dentry = ERR_CAST(child);
>> > +                               break;
>> > +                       }
>> > +                       d_rehash(child);
>> > +               }
>> >                dput(dentry);
>> >                dentry = child;
>> >        } while (!IS_ERR(dentry));
>> > +
>> > +       if (IS_ERR(dentry))
>> > +               goto out;
>> > +
>> > +       mutex_lock(&cifs_root_mutex);
>> > +
>> > +       if (dentry->d_parent->d_inode)
>> > +               mutex_lock(&dentry->d_parent->d_inode->i_mutex);
>> > +
>> > +       if (!dentry->d_inode) {
>> > +               inode = cifs_mntroot_iget(sb, full_path);
>> > +               if (IS_ERR(inode)) {
>> > +                       dput(dentry);
>> > +                       dentry = ERR_CAST(inode);
>> > +                       goto out;
>> > +               }
>> > +
>> > +               found = d_instantiate_unique(dentry, inode);
>> > +               if (found) {
>> > +                       dput(dentry);
>> > +                       dentry = found;
>> > +               }
>> > +       }
>> > +
>> > +       if (dentry->d_parent->d_inode)
>> > +               mutex_unlock(&dentry->d_parent->d_inode->i_mutex);
>> > +
>> > +       mutex_unlock(&cifs_root_mutex);
>> > +out:
>> >        kfree(full_path);
>> >        return dentry;
>> >  }
>> > @@ -644,16 +682,7 @@ cifs_do_mount(struct file_system_type *fs_type,
>> >                cifs_umount(cifs_sb);
>> >        } else {
>> >                sb->s_flags = flags;
>> > -               /* BB should we make this contingent on mount parm? */
>> > -               sb->s_flags |= MS_NODIRATIME | MS_NOATIME;
>> > -
>> > -               rc = cifs_read_super(sb);
>> > -               if (rc) {
>> > -                       root = ERR_PTR(rc);
>> > -                       goto out_super;
>> > -               }
>> > -
>> > -               sb->s_flags |= MS_ACTIVE;
>> > +               cifs_read_super(sb);
>> >        }
>> >
>> >        root = cifs_get_root(volume_info, sb);
>> > @@ -1112,6 +1141,7 @@ init_cifs(void)
>> >        spin_lock_init(&cifs_tcp_ses_lock);
>> >        spin_lock_init(&cifs_file_list_lock);
>> >        spin_lock_init(&GlobalMid_Lock);
>> > +       mutex_init(&cifs_root_mutex);
>> >
>> >        if (cifs_max_pending < 2) {
>> >                cifs_max_pending = 2;
>> > diff --git a/fs/cifs/cifsfs.h b/fs/cifs/cifsfs.h
>> > index 30ff560..cf28150 100644
>> > --- a/fs/cifs/cifsfs.h
>> > +++ b/fs/cifs/cifsfs.h
>> > @@ -43,7 +43,8 @@ extern const struct address_space_operations cifs_addr_ops_smallbuf;
>> >
>> >  /* Functions related to inodes */
>> >  extern const struct inode_operations cifs_dir_inode_ops;
>> > -extern struct inode *cifs_root_iget(struct super_block *);
>> > +extern struct inode *cifs_mntroot_iget(struct super_block *sb,
>> > +                                      const char *full_path);
>> >  extern int cifs_create(struct inode *, struct dentry *, int,
>> >                       struct nameidata *);
>> >  extern struct dentry *cifs_lookup(struct inode *, struct dentry *,
>> > diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
>> > index 8238aa1..67f6852 100644
>> > --- a/fs/cifs/cifsglob.h
>> > +++ b/fs/cifs/cifsglob.h
>> > @@ -958,6 +958,12 @@ GLOBAL_EXTERN spinlock_t           cifs_tcp_ses_lock;
>> >  */
>> >  GLOBAL_EXTERN spinlock_t       cifs_file_list_lock;
>> >
>> > +/*
>> > + * This lock protects root dentry in cifs_get_root from being instantiated
>> > + * twice.
>> > + */
>> > +GLOBAL_EXTERN struct mutex cifs_root_mutex;
>> > +
>> >  #ifdef CONFIG_CIFS_DNOTIFY_EXPERIMENTAL /* unused temporarily */
>> >  /* Outstanding dir notify requests */
>> >  GLOBAL_EXTERN struct list_head GlobalDnotifyReqList;
>> > diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c
>> > index d7eeb9d..d6b3319 100644
>> > --- a/fs/cifs/dir.c
>> > +++ b/fs/cifs/dir.c
>> > @@ -553,6 +553,7 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry,
>> >
>> >        if (direntry->d_inode != NULL) {
>> >                cFYI(1, "non-NULL inode in lookup");
>> > +               newInode = direntry->d_inode;
>>
>> this and ...
>>
>> >        } else {
>> >                cFYI(1, "NULL inode in lookup");
>> >        }
>> > @@ -596,7 +597,7 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry,
>> >                rc = cifs_get_inode_info(&newInode, full_path, NULL,
>> >                                parent_dir_inode->i_sb, xid, NULL);
>> >
>> > -       if ((rc == 0) && (newInode != NULL)) {
>> > +       if ((rc == 0) && (newInode != NULL) && !direntry->d_inode) {
>>
>> ... this doesn't make sense - I made it for testing purpose only and
>> forgot to remove. Since this patch isn't going to be a candidate to
>> merge, seems no need to respin it.
>>
>> >                d_add(direntry, newInode);
>> >                if (posix_open) {
>> >                        filp = lookup_instantiate_filp(nd, direntry,
>> > diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c
>> > index 2c50bd2..7b7fccd 100644
>> > --- a/fs/cifs/inode.c
>> > +++ b/fs/cifs/inode.c
>> > @@ -878,7 +878,7 @@ retry_iget5_locked:
>> >  }
>> >
>> >  /* gets root inode */
>> > -struct inode *cifs_root_iget(struct super_block *sb)
>> > +struct inode *cifs_mntroot_iget(struct super_block *sb, const char *full_path)
>> >  {
>> >        int xid;
>> >        struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
>> > @@ -888,9 +888,10 @@ struct inode *cifs_root_iget(struct super_block *sb)
>> >
>> >        xid = GetXid();
>> >        if (tcon->unix_ext)
>> > -               rc = cifs_get_inode_info_unix(&inode, "", sb, xid);
>> > +               rc = cifs_get_inode_info_unix(&inode, full_path, sb, xid);
>> >        else
>> > -               rc = cifs_get_inode_info(&inode, "", NULL, sb, xid, NULL);
>> > +               rc = cifs_get_inode_info(&inode, full_path, NULL, sb, xid,
>> > +                                        NULL);
>> >
>> >        if (!inode) {
>> >                inode = ERR_PTR(rc);
>> > diff --git a/fs/cifs/readdir.c b/fs/cifs/readdir.c
>> > index 5de03ec..b2f8851 100644
>> > --- a/fs/cifs/readdir.c
>> > +++ b/fs/cifs/readdir.c
>> > @@ -65,10 +65,8 @@ static inline void dump_cifs_file_struct(struct file *file, char *label)
>> >  }
>> >  #endif /* DEBUG2 */
>> >
>> > -/*
>> > - * Find the dentry that matches "name". If there isn't one, create one. If it's
>> > - * a negative dentry or the uniqueid changed, then drop it and recreate it.
>> > - */
>> > +
>> > +/* Find the dentry that matches "name". If there isn't one, create one. */
>> >  static struct dentry *
>> >  cifs_readdir_lookup(struct dentry *parent, struct qstr *name,
>> >                    struct cifs_fattr *fattr)
>> > @@ -84,32 +82,38 @@ cifs_readdir_lookup(struct dentry *parent, struct qstr *name,
>> >        else
>> >                name->hash = full_name_hash(name->name, name->len);
>> >
>> > +       mutex_lock(&cifs_root_mutex);
>> > +
>> >        dentry = d_lookup(parent, name);
>> >        if (dentry) {
>> >                /* FIXME: check for inode number changes? */
>> >                if (dentry->d_inode != NULL)
>> > -                       return dentry;
>> > -               d_drop(dentry);
>> > -               dput(dentry);
>> > +                       goto out;
>> > +       } else {
>> > +               dentry = d_alloc(parent, name);
>> > +               if (dentry == NULL)
>> > +                       goto out;
>> > +               d_rehash(dentry);
>> >        }
>> >
>> > -       dentry = d_alloc(parent, name);
>> > -       if (dentry == NULL)
>> > -               return NULL;
>> > -
>> >        inode = cifs_iget(sb, fattr);
>> >        if (!inode) {
>> >                dput(dentry);
>> > -               return NULL;
>> > +               dentry = NULL;
>> > +               goto out;
>> >        }
>> >
>> > -       alias = d_materialise_unique(dentry, inode);
>> > +       alias = d_instantiate_unique(dentry, inode);
>> >        if (alias != NULL) {
>> >                dput(dentry);
>> > -               if (IS_ERR(alias))
>> > -                       return NULL;
>> > +               if (IS_ERR(alias)) {
>> > +                       dentry = NULL;
>> > +                       goto out;
>> > +               }
>> >                dentry = alias;
>> >        }
>> > +out:
>> > +       mutex_unlock(&cifs_root_mutex);
>> >
>> >        return dentry;
>> >  }
>> > @@ -708,6 +712,7 @@ int cifs_readdir(struct file *file, void *direntry, filldir_t filldir)
>> >        char *tmp_buf = NULL;
>> >        char *end_of_smb;
>> >        unsigned int max_len;
>> > +       ino_t ino;
>> >
>> >        xid = GetXid();
>> >
>> > @@ -732,8 +737,12 @@ int cifs_readdir(struct file *file, void *direntry, filldir_t filldir)
>> >                }
>> >                file->f_pos++;
>> >        case 1:
>> > -               if (filldir(direntry, "..", 2, file->f_pos,
>> > -                    parent_ino(file->f_path.dentry), DT_DIR) < 0) {
>> > +               if (!file->f_path.dentry->d_parent->d_inode) {
>> > +                       cFYI(1, "parent dir is negative, filling as current");
>> > +                       ino = file->f_path.dentry->d_inode->i_ino;
>> > +               } else
>> > +                       ino = parent_ino(file->f_path.dentry);
>> > +               if (filldir(direntry, "..", 2, file->f_pos, ino, DT_DIR) < 0) {
>> >                        cERROR(1, "Filldir for parent dir failed");
>> >                        rc = -ENOMEM;
>> >                        break;
>> > --
>> > 1.7.1
>> >
>> >
>>
>>
>>
>
> (cc'ing fsdevel since I think we could use more eyes on this scheme)
>
> I'm not a big fan of this patch. It seems to me that this assumes that
> it's ok to have a dentry with a negative dentry as a parent. That makes
> no sense though. Anegative dentry is the result of a lookup that
> returned the equivalent of -ENOENT. I'm very reticent to play that sort
> of game with the dcache code...
>

Jeff, now I agree with you that keeping negative dentries with
childrens is not a good idea. That really breaks some VFS assumptions.

> What may be better is to do something like this:
>
> Go ahead and instantiate positive dentries from the root of the share
> to the root of the vfsmount. However, we would mark those inodes as
> "placeholders" and give them a bogus inode number of some sort (maybe
> even 0?).
>

Yes, I was thinking about this approach too but didn't have enough
time to prototype it. It seems easier to implement than the second one
but now I don't imagine what problems we can faced with going this
way.

-- 
Best regards,
Pavel Shilovsky.
--
To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux Ext4 Filesystem]     [Union Filesystem]     [Filesystem Testing]     [Ceph Users]     [Ecryptfs]     [AutoFS]     [Kernel Newbies]     [Share Photos]     [Security]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux Cachefs]     [Reiser Filesystem]     [Linux RAID]     [Samba]     [Device Mapper]     [CEPH Development]
  Powered by Linux