[linux-cifs-client] [PATCH 3/3] cifs: add cifs_iget_unix and have
readdir codepath use it
Jeff Layton
jlayton at redhat.com
Sun Apr 5 13:09:21 GMT 2009
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 at redhat.com>
---
fs/cifs/cifsproto.h | 4 ++
fs/cifs/inode.c | 18 ++++++
fs/cifs/readdir.c | 147 +++++++++++++++++++++++++++++++++++++-------------
3 files changed, 131 insertions(+), 38 deletions(-)
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);
--
1.6.0.6
More information about the linux-cifs-client
mailing list