Re: cifs and win2k8

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

 



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;
 	} 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) {
 		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

--
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