Re: [PATCH] cifs: Allow mounts for paths for restricted intermediate paths

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

 



On Thu, 21 Nov 2013 11:16:20 -0600
Shirish Pargaonkar <shirishpargaonkar@xxxxxxxxx> wrote:

> On Tue, Nov 19, 2013 at 2:59 PM, Jeff Layton <jlayton@xxxxxxxxxx> wrote:
> > On Tue, 19 Nov 2013 10:33:33 -0600
> > shirishpargaonkar@xxxxxxxxx wrote:
> >
> >> From: Shirish Pargaonkar <shirishpargaonkar@xxxxxxxxx>
> >>
> >>
> >> Allow cifs mounts for a prefixpath with intermediate paths without access,
> >> so as to continue with the shared superblock model.
> >>
> >> For the intermediate path entries that do not allow access, create "placeholder"
> >> inodes and instantiate dentries with those inodes.
> >> If and when such a path entry becomes accessible it is filled with correct
> >> info.
> >>
> >
> > I'm unclear on this last bit...
> >
> > How exactly do they end up being filled with the correct info once that
> > happens?
> 
> I was thinking when the access permissions for this directory change
> on the server.
> 
> For example,  if we had two mounts with shared superblocks
> /mnt_m/a/b
> /mnt_n
> where a was inaccessible.  If the permissions change on a such that
> it becomes accessible, stat /mnt_n/a will fetch correct info.
> 

Right, but the dentry is already set at that point to use your fake
inode, and it has children so we can't just toss it out of the cache
and create a new one.

> >
> >> Reference: Samba bugzilla 6950
> >>
> >> Signed-off-by: Shirish Pargaonkar <spargaonkar@xxxxxxxx>
> >>
> >> ---
> >>  fs/cifs/cifsfs.c    | 17 +++++++++++++++--
> >>  fs/cifs/cifsproto.h |  3 +++
> >>  fs/cifs/inode.c     | 16 ++++++++++++++++
> >>  3 files changed, 34 insertions(+), 2 deletions(-)
> >>
> >> diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c
> >> index 849f613..bce185c 100644
> >> --- a/fs/cifs/cifsfs.c
> >> +++ b/fs/cifs/cifsfs.c
> >> @@ -584,6 +584,9 @@ cifs_get_root(struct smb_vol *vol, struct super_block *sb)
> >>       char *full_path = NULL;
> >>       char *s, *p;
> >>       char sep;
> >> +     struct inode *dir, *phinode;
> >> +     struct dentry *child;
> >> +     struct cifs_fattr phfattr;
> >>
> >>       full_path = cifs_build_path_to_root(vol, cifs_sb,
> >>                                           cifs_sb_master_tcon(cifs_sb));
> >> @@ -592,13 +595,13 @@ cifs_get_root(struct smb_vol *vol, struct super_block *sb)
> >>
> >>       cifs_dbg(FYI, "Get root dentry for %s\n", full_path);
> >>
> >> +     fill_phfattr(&phfattr, sb);
> >
> > Hmmm...so this function sets up a cifs_fattr struct, including the
> > cf_uniqueid...which won't be very unique when you reuse that struct
> > several times in the do...while loop below. Don't you need to call it
> > again after you use it once?
> >
> 
> That was silly.   Will fix that.
> 
> >>       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;
> >> +             dir = dentry->d_inode;
> >>
> >>               if (!dir) {
> >>                       dput(dentry);
> >> @@ -626,6 +629,16 @@ cifs_get_root(struct smb_vol *vol, struct super_block *sb)
> >>               mutex_unlock(&dir->i_mutex);
> >>               dput(dentry);
> >>               dentry = child;
> >> +             if (!IS_ERR(dentry)) {
> >> +                     if (*s) {
> >> +                             /* EACCESS on an intermediate dir */
> >> +                             if (!dentry->d_inode) {
> >> +                                     phinode = cifs_iget(sb, &phfattr);
> >> +                                     if (phinode)
> >> +                                             d_instantiate(dentry, phinode);
> >> +                             }
> >> +                     }
> >> +             }
> >
> > This doesn't make any sense to me.
> >
> > The do...while loop here is basically doing lookups and instantiating
> > dentries and inodes down from the root of the mount to the root of the
> > share. The lookup_one_len call is what does the single-level lookup in
> > that directory.
> >
> > Why are you adding special handling when "dentry" is not an error?
> > Would it not make more sense to do so when lookup_one_len does return
> > an error?
> 
> If server returns ENOENT, lookup_one_len will return with a negative
> dentry.  This is the case I was handling.  Need to access EACCESS.
> Not sure why/when server returns ENOENT (object not found) even when
> the (directory) entry exists.
> 

This seems backward to me. The ENOENT case is a case where we should
*fail*, not where we should fake up an inode. That's a pretty strong
indicator that the prefixpath is just bogus.

In the case of something like EACCES then we may want to create a
placeholder inode since that sort of implies that the directory is
present, but that we don't have permissions to fetch the info about it.

> >
> >>       } while (!IS_ERR(dentry));
> >>       kfree(full_path);
> >>       return dentry;
> >> diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h
> >> index aa33976..9a84e5c 100644
> >> --- a/fs/cifs/cifsproto.h
> >> +++ b/fs/cifs/cifsproto.h
> >> @@ -498,4 +498,7 @@ void cifs_writedata_release(struct kref *refcount);
> >>  int open_query_close_cifs_symlink(const unsigned char *path, char *pbuf,
> >>                       unsigned int *pbytes_read, struct cifs_sb_info *cifs_sb,
> >>                       unsigned int xid);
> >> +
> >> +extern void fill_phfattr(struct cifs_fattr *, struct super_block *);
> >> +
> >>  #endif                       /* _CIFSPROTO_H */
> >> diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c
> >> index 36f9ebb..4f5a09a 100644
> >> --- a/fs/cifs/inode.c
> >> +++ b/fs/cifs/inode.c
> >> @@ -322,6 +322,22 @@ cifs_create_dfs_fattr(struct cifs_fattr *fattr, struct super_block *sb)
> >>       fattr->cf_flags |= CIFS_FATTR_DFS_REFERRAL;
> >>  }
> >>
> >> +void
> >> +fill_phfattr(struct cifs_fattr *cf, struct super_block *sb)
> >> +{
> >> +     struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
> >> +
> >> +     memset(cf, 0, sizeof(*cf));
> >> +     cf->cf_uniqueid = iunique(sb, ROOT_I);
> >
> >
> > Also, iunique is only guaranteed to return a number that hasn't yet
> > been used. So suppose we're going to instantiate one of these bogus
> > inodes. We give it the uniqueid of '3'. Then later, once the mount is
> > done we find another (legitimate) inode on the server and it also has a
> > uniqueid of '3'. Now you have a collision. Worse yet, if it's a
> > directory now it looks like we have a hardlinked directory...
> >
> > Once you start using iunique, you can't really go back to using server
> > inode numbers since you can't guarantee that there won't be collisions.
> >
> > One possibility to deal with that would be to tag these entries as
> > bogus and ensure that cifs_iget will never match them. Another
> > possibility might be just to never hash these inodes, but I'm not clear
> > on how kosher that is from a vfs standpoint.
> 
> Looking into this.
> 
> 
> >
> >> +     cf->cf_nlink = 1;
> >> +     cf->cf_atime = CURRENT_TIME;
> >> +     cf->cf_ctime = CURRENT_TIME;
> >> +     cf->cf_mtime = CURRENT_TIME;
> >> +     cf->cf_mode = S_IFDIR | S_IXUGO | S_IRWXU;
> >> +     cf->cf_uid = cifs_sb->mnt_uid;
> >> +     cf->cf_gid = cifs_sb->mnt_gid;
> >> +}
> >> +
> >
> >
> >>  static int
> >>  cifs_get_file_info_unix(struct file *filp)
> >>  {
> >
> >
> > I think the thing to do is to step back and describe (in English) how
> > you envision this working. Then once that's done, we can start
> > discussing what the code should look like...
> >

Again, the above is really the place to start. You've been given a
mount request with a prefixpath that has components to which you don't
have access. What do you do now?

-- 
Jeff Layton <jlayton@xxxxxxxxxx>
--
To unsubscribe from this list: send the line "unsubscribe linux-cifs" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux