diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index 849f613..35a7a80 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -584,9 +584,12 @@ cifs_get_root(struct smb_vol *vol, struct super_block *sb) char *full_path = NULL; char *s, *p; char sep; + int rc; + unsigned int xid; + struct inode *rinode = NULL; + struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb); - full_path = cifs_build_path_to_root(vol, cifs_sb, - cifs_sb_master_tcon(cifs_sb)); + full_path = cifs_build_path_to_root(vol, cifs_sb, tcon); if (full_path == NULL) return ERR_PTR(-ENOMEM); @@ -627,6 +630,24 @@ cifs_get_root(struct smb_vol *vol, struct super_block *sb) dput(dentry); dentry = child; } while (!IS_ERR(dentry)); + if (IS_ERR(dentry) && (PTR_ERR(dentry) == -EACCES) && *s) { + xid = get_xid(); + if (tcon->unix_ext) { + rc = cifs_get_inode_info_unix(&rinode, full_path, + sb, xid); + } else { + rc = cifs_get_inode_info(&rinode, full_path, NULL, + sb, xid, NULL); + } + free_xid(xid); + + if ((rc == 0) && (rinode != NULL)) { + dentry = d_obtain_alias(rinode); + if (IS_ERR(dentry)) + iput(rinode); + } + } + kfree(full_path); return dentry; } diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c index a514e0a..7cf261f 100644 --- a/fs/cifs/dir.c +++ b/fs/cifs/dir.c @@ -699,6 +699,7 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry, struct cifs_tcon *pTcon; struct inode *newInode = NULL; char *full_path = NULL; + struct dentry *ret = NULL; xid = get_xid(); @@ -745,11 +746,19 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry, } if ((rc == 0) && (newInode != NULL)) { - d_add(direntry, newInode); /* since paths are not looked up by component - the parent directories are presumed to be good here */ - renew_parental_timestamps(direntry); - + ret = d_splice_alias(newInode, direntry); + if (!ret) + renew_parental_timestamps(direntry); + else { + if (!IS_ERR(ret)) { + renew_parental_timestamps(ret); + dput(ret); + goto lookup_out; + } else + rc = PTR_ERR(ret); + } } else if (rc == -ENOENT) { rc = 0; direntry->d_time = jiffies; @@ -761,12 +770,13 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry, /* We special case check for Access Denied - since that is a common return code */ } + ret = ERR_PTR(rc); lookup_out: kfree(full_path); cifs_put_tlink(tlink); free_xid(xid); - return ERR_PTR(rc); + return ret; } static int diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index 36f9ebb..03fdd1d 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c @@ -869,8 +869,10 @@ inode_has_hashed_dentries(struct inode *inode) spin_lock(&inode->i_lock); hlist_for_each_entry(dentry, &inode->i_dentry, d_alias) { if (!d_unhashed(dentry) || IS_ROOT(dentry)) { - spin_unlock(&inode->i_lock); - return true; + if (!(dentry->d_flags & DCACHE_DISCONNECTED)) { + spin_unlock(&inode->i_lock); + return true; + } } } spin_unlock(&inode->i_lock); @@ -922,6 +924,24 @@ retry_iget5_locked: return inode; } +void +fill_phfattr(struct cifs_fattr *cf, struct super_block *sb) +{ +#define CIFS_ROOT_INO 2 /* root inode */ + + struct cifs_sb_info *cifs_sb = CIFS_SB(sb); + + memset(cf, 0, sizeof(*cf)); + cf->cf_uniqueid = CIFS_ROOT_INO; + 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; +} + /* gets root inode */ struct inode *cifs_root_iget(struct super_block *sb) { @@ -930,6 +950,7 @@ struct inode *cifs_root_iget(struct super_block *sb) struct inode *inode = NULL; long rc; struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb); + struct cifs_fattr phfattr; xid = get_xid(); if (tcon->unix_ext) @@ -938,8 +959,16 @@ struct inode *cifs_root_iget(struct super_block *sb) rc = cifs_get_inode_info(&inode, "", NULL, sb, xid, NULL); if (!inode) { - inode = ERR_PTR(rc); - goto out; + if (rc == -EACCES) { + fill_phfattr(&phfattr, sb); + inode = cifs_iget(sb, &phfattr); + if (inode) + rc = 0; + } + if (rc) { + inode = ERR_PTR(rc); + goto out; + } } #ifdef CONFIG_CIFS_FSCACHE