[linux-cifs-client,3/3] cifs: add cifs_iget_unix and have readdir codepath use it
diff mbox

Message ID 1238936961-6979-4-git-send-email-jlayton@redhat.com
State New, archived
Headers show

Commit Message

Jeff Layton April 5, 2009, 1:09 p.m. UTC
Add a cifs_iget_unix function similar to cifs_iget_unix_basic. This
will ID inodes based on the UniqueId value in a FILE_INFO_UNIX struct.

Signed-off-by: Jeff Layton <jlayton@redhat.com>
---
 fs/cifs/cifsproto.h |    4 ++
 fs/cifs/inode.c     |   18 ++++++
 fs/cifs/readdir.c   |  147 +++++++++++++++++++++++++++++++++++++-------------
 3 files changed, 131 insertions(+), 38 deletions(-)

Patch
diff mbox

diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h
index 51dfd51..53b79ad 100644
--- a/fs/cifs/cifsproto.h
+++ b/fs/cifs/cifsproto.h
@@ -100,9 +100,13 @@  extern int cifs_posix_open(char *full_path, struct inode **pinode,
 			   int *poplock, __u16 *pnetfid, int xid);
 extern void posix_fill_in_inode(struct inode *tmp_inode,
 				FILE_UNIX_BASIC_INFO *pData);
+extern void unix_fill_in_inode(struct inode *tmp_inode,
+				FILE_UNIX_INFO *pData);
 extern struct inode *cifs_new_inode(struct super_block *sb, __u64 *inum);
 extern struct inode *cifs_iget_unix_basic(struct super_block *sb,
 				FILE_UNIX_BASIC_INFO *info);
+extern struct inode *cifs_iget_unix(struct super_block *sb,
+				FILE_UNIX_INFO *info);
 extern int cifs_get_inode_info(struct inode **pinode,
 			const unsigned char *search_path,
 			FILE_ALL_INFO *pfile_info,
diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c
index c30c318..ac7336a 100644
--- a/fs/cifs/inode.c
+++ b/fs/cifs/inode.c
@@ -759,6 +759,24 @@  cifs_iget_unix_basic(struct super_block *sb, FILE_UNIX_BASIC_INFO *info)
 	return inode;
 }
 
+struct inode *
+cifs_iget_unix(struct super_block *sb, FILE_UNIX_INFO *info)
+{
+	struct inode *inode;
+
+	inode = cifs_iget_locked(sb, le64_to_cpu(info->UniqueId));
+	if (!inode)
+		return ERR_PTR(-ENOMEM);
+
+	/* update inode since we have info in hand */
+	unix_fill_in_inode(inode, info);
+
+	if (inode->i_state & I_NEW)
+		unlock_new_inode(inode);
+
+	return inode;
+}
+
 /* gets root inode */
 struct inode *cifs_root_iget(struct super_block *sb, unsigned long ino)
 {
diff --git a/fs/cifs/readdir.c b/fs/cifs/readdir.c
index 1a8be62..4b59ccb 100644
--- a/fs/cifs/readdir.c
+++ b/fs/cifs/readdir.c
@@ -56,6 +56,80 @@  static inline void dump_cifs_file_struct(struct file *file, char *label)
 }
 #endif /* DEBUG2 */
 
+static unsigned int
+cifs_dentry_type_unix(u32 type)
+{
+	switch (type) {
+	case UNIX_FILE:
+		return DT_REG;
+	case UNIX_SYMLINK:
+		return DT_LNK;
+	case UNIX_DIR:
+		return DT_DIR;
+	case UNIX_CHARDEV:
+		return DT_CHR;
+	case UNIX_BLOCKDEV:
+		return DT_BLK;
+	case UNIX_FIFO:
+		return DT_FIFO;
+	case UNIX_SOCKET:
+		return DT_SOCK;
+	default:
+		/* safest to just call it a file */
+		cFYI(1, ("unknown inode type %d", type));
+		return DT_REG;
+	}
+}
+
+/*
+ * Find the dentry that matches "name". If there isn't one, create
+ * one. If it's a negative dentry, then drop it and recreate it.
+ */
+static struct dentry *
+cifs_readdir_lookup_unix(struct dentry *parent, struct qstr *name,
+			 FILE_UNIX_INFO *info)
+{
+	struct dentry *dentry, *alias;
+	struct inode *inode;
+	struct super_block *sb = parent->d_inode->i_sb;
+
+	cFYI(1, ("For %s", name->name));
+
+	dentry = d_lookup(parent, name);
+	if (dentry) {
+		/* BB: check for inode number change */
+		if (dentry->d_inode != NULL)
+			return dentry;
+		d_drop(dentry);
+		dput(dentry);
+	}
+
+	dentry = d_alloc(parent, name);
+	if (dentry == NULL)
+		return NULL;
+
+	inode = cifs_iget_unix(sb, info);
+	if (IS_ERR(inode)) {
+		dput(dentry);
+		return NULL;
+	}
+
+	if (CIFS_SB(sb)->tcon->nocase)
+		dentry->d_op = &cifs_ci_dentry_ops;
+	else
+		dentry->d_op = &cifs_dentry_ops;
+
+	alias = d_materialise_unique(dentry, inode);
+	if (alias != NULL) {
+		dput(dentry);
+		if (IS_ERR(alias))
+			return NULL;
+		dentry = alias;
+	}
+
+	return dentry;
+}
+
 /* Returns 1 if new inode created, 2 if both dentry and inode were */
 /* Might check in the future if inode number changed so we can rehash inode */
 static int
@@ -69,7 +143,6 @@  construct_dentry(struct qstr *qstring, struct file *file,
 
 	cFYI(1, ("For %s", qstring->name));
 
-	qstring->hash = full_name_hash(qstring->name, qstring->len);
 	tmp_dentry = d_lookup(file->f_path.dentry, qstring);
 	if (tmp_dentry) {
 		/* BB: overwrite old name? i.e. tmp_dentry->d_name and
@@ -304,8 +377,7 @@  static void fill_in_inode(struct inode *tmp_inode, int new_buf_type,
 	}
 }
 
-static void unix_fill_in_inode(struct inode *tmp_inode,
-	FILE_UNIX_INFO *pfindData, unsigned int *pobject_type, int isNewInode)
+void unix_fill_in_inode(struct inode *tmp_inode, FILE_UNIX_INFO *pfindData)
 {
 	loff_t local_size;
 	struct timespec local_mtime;
@@ -335,33 +407,25 @@  static void unix_fill_in_inode(struct inode *tmp_inode,
 	   to avoid strange results if bits above were corrupt */
 	tmp_inode->i_mode &= ~S_IFMT;
 	if (type == UNIX_FILE) {
-		*pobject_type = DT_REG;
 		tmp_inode->i_mode |= S_IFREG;
 	} else if (type == UNIX_SYMLINK) {
-		*pobject_type = DT_LNK;
 		tmp_inode->i_mode |= S_IFLNK;
 	} else if (type == UNIX_DIR) {
-		*pobject_type = DT_DIR;
 		tmp_inode->i_mode |= S_IFDIR;
 	} else if (type == UNIX_CHARDEV) {
-		*pobject_type = DT_CHR;
 		tmp_inode->i_mode |= S_IFCHR;
 		tmp_inode->i_rdev = MKDEV(le64_to_cpu(pfindData->DevMajor),
 				le64_to_cpu(pfindData->DevMinor) & MINORMASK);
 	} else if (type == UNIX_BLOCKDEV) {
-		*pobject_type = DT_BLK;
 		tmp_inode->i_mode |= S_IFBLK;
 		tmp_inode->i_rdev = MKDEV(le64_to_cpu(pfindData->DevMajor),
 				le64_to_cpu(pfindData->DevMinor) & MINORMASK);
 	} else if (type == UNIX_FIFO) {
-		*pobject_type = DT_FIFO;
 		tmp_inode->i_mode |= S_IFIFO;
 	} else if (type == UNIX_SOCKET) {
-		*pobject_type = DT_SOCK;
 		tmp_inode->i_mode |= S_IFSOCK;
 	} else {
 		/* safest to just call it a file */
-		*pobject_type = DT_REG;
 		tmp_inode->i_mode |= S_IFREG;
 		cFYI(1, ("unknown inode type %d", type));
 	}
@@ -410,7 +474,7 @@  static void unix_fill_in_inode(struct inode *tmp_inode,
 		else
 			tmp_inode->i_data.a_ops = &cifs_addr_ops;
 
-		if (isNewInode)
+		if (tmp_inode->i_state & I_NEW)
 			return; /* No sense invalidating pages for new inode
 				   since we have not started caching readahead
 				   file data for it yet */
@@ -939,36 +1003,43 @@  static int cifs_filldir(char *pfindEntry, struct file *file,
 		return rc;
 
 	/* only these two infolevels return valid inode numbers */
-	if (pCifsF->srch_inf.info_level == SMB_FIND_FILE_UNIX ||
-	    pCifsF->srch_inf.info_level == SMB_FIND_FILE_ID_FULL_DIR_INFO)
-		rc = construct_dentry(&qstring, file, &tmp_inode, &tmp_dentry,
-					&inum);
-	else
-		rc = construct_dentry(&qstring, file, &tmp_inode, &tmp_dentry,
-					NULL);
+	if (pCifsF->srch_inf.info_level == SMB_FIND_FILE_UNIX) {
+		FILE_UNIX_INFO *info = (FILE_UNIX_INFO *) pfindEntry;
 
-	if ((tmp_inode == NULL) || (tmp_dentry == NULL))
-		return -ENOMEM;
+		tmp_dentry = cifs_readdir_lookup_unix(file->f_dentry, &qstring,
+						      info);
+		if (!tmp_dentry)
+			return -ENOMEM;
 
-	/* we pass in rc below, indicating whether it is a new inode,
-	   so we can figure out whether to invalidate the inode cached
-	   data if the file has changed */
-	if (pCifsF->srch_inf.info_level == SMB_FIND_FILE_UNIX)
-		unix_fill_in_inode(tmp_inode,
-				   (FILE_UNIX_INFO *)pfindEntry,
-				   &obj_type, rc);
-	else if (pCifsF->srch_inf.info_level == SMB_FIND_FILE_INFO_STANDARD)
-		fill_in_inode(tmp_inode, 0 /* old level 1 buffer type */,
-				pfindEntry, &obj_type, rc);
-	else
-		fill_in_inode(tmp_inode, 1 /* NT */, pfindEntry, &obj_type, rc);
+		obj_type = cifs_dentry_type_unix(le32_to_cpu(info->Type));
+	} else {
+		if (pCifsF->srch_inf.info_level ==
+		    SMB_FIND_FILE_ID_FULL_DIR_INFO)
+			rc = construct_dentry(&qstring, file, &tmp_inode,
+						&tmp_dentry, &inum);
+		else
+			rc = construct_dentry(&qstring, file, &tmp_inode,
+						&tmp_dentry, NULL);
 
-	if (rc) /* new inode - needs to be tied to dentry */ {
-		d_instantiate(tmp_dentry, tmp_inode);
-		if (rc == 2)
-			d_rehash(tmp_dentry);
-	}
+		if ((tmp_inode == NULL) || (tmp_dentry == NULL))
+			return -ENOMEM;
+
+		/* we pass in rc below, indicating whether it is a new inode,
+		 * so we can figure out whether to invalidate the inode cached
+		 * data if the file has changed
+		 */
+		if (pCifsF->srch_inf.info_level == SMB_FIND_FILE_INFO_STANDARD)
+			fill_in_inode(tmp_inode, 0, pfindEntry, &obj_type, rc);
+		else
+			fill_in_inode(tmp_inode, 1, pfindEntry, &obj_type, rc);
 
+		/* new inode - needs to be tied to dentry */
+		if (rc) {
+			d_instantiate(tmp_dentry, tmp_inode);
+			if (rc == 2)
+				d_rehash(tmp_dentry);
+		}
+	}
 
 	rc = filldir(direntry, qstring.name, qstring.len, file->f_pos,
 		     tmp_inode->i_ino, obj_type);