Commit cc0bad75 authored by Jeff Layton's avatar Jeff Layton Committed by Steve French

cifs: add new cifs_iget function and convert unix codepath to use it

cifs: add new cifs_iget function and convert unix codepath to use it

In order to unify some codepaths, introduce a common cifs_fattr struct
for storing inode attributes. The different codepaths (unix, legacy,
normal, etc...) can fill out this struct with inode info. It can then be
passed as an arg to a common set of routines to get and update inodes.

Add a new cifs_iget function that uses iget5_locked to identify inodes.
This will compare inodes based on the uniqueid value in a cifs_fattr
struct.

Rather than filling out an already-created inode, have
cifs_get_inode_info_unix instead fill out cifs_fattr and hand that off
to cifs_iget. cifs_iget can then properly look for hardlinked inodes.

On the readdir side, add a new cifs_readdir_lookup function that spawns
populated dentries. Redefine FILE_UNIX_INFO so that it's basically a
FILE_UNIX_BASIC_INFO that has a few fields wrapped around it. This
allows us to more easily use the same function for filling out the fattr
as the non-readdir codepath.

With this, we should then have proper hardlink detection and can
eventually get rid of some nasty CIFS-specific hacks for handing them.
Signed-off-by: default avatarJeff Layton <jlayton@redhat.com>
Reviewed-by: default avatarChristoph Hellwig <hch@lst.de>
Signed-off-by: default avatarSteve French <sfrench@us.ibm.com>
parent d960eea9
......@@ -24,6 +24,19 @@
#define ROOT_I 2
/*
* ino_t is 32-bits on 32-bit arch. We have to squash the 64-bit value down
* so that it will fit.
*/
static inline ino_t
cifs_uniqueid_to_ino_t(u64 fileid)
{
ino_t ino = (ino_t) fileid;
if (sizeof(ino_t) < sizeof(u64))
ino ^= fileid >> (sizeof(u64)-sizeof(ino_t)) * 8;
return ino;
}
extern struct file_system_type cifs_fs_type;
extern const struct address_space_operations cifs_addr_ops;
extern const struct address_space_operations cifs_addr_ops_smallbuf;
......
......@@ -371,6 +371,7 @@ struct cifsInodeInfo {
bool oplockPending:1;
bool delete_pending:1; /* DELETE_ON_CLOSE is set */
u64 server_eof; /* current file size on server */
u64 uniqueid; /* server inode number */
struct inode vfs_inode;
};
......@@ -472,6 +473,30 @@ struct dfs_info3_param {
char *node_name;
};
/*
* common struct for holding inode info when searching for or updating an
* inode with new info
*/
#define CIFS_FATTR_DFS_REFERRAL 0x1
struct cifs_fattr {
u32 cf_flags;
u32 cf_cifsattrs;
u64 cf_uniqueid;
u64 cf_eof;
u64 cf_bytes;
uid_t cf_uid;
gid_t cf_gid;
umode_t cf_mode;
dev_t cf_rdev;
unsigned int cf_nlink;
unsigned int cf_dtype;
struct timespec cf_atime;
struct timespec cf_mtime;
struct timespec cf_ctime;
};
static inline void free_dfs_info_param(struct dfs_info3_param *param)
{
if (param) {
......
......@@ -2328,19 +2328,7 @@ struct file_attrib_tag {
typedef struct {
__le32 NextEntryOffset;
__u32 ResumeKey; /* as with FileIndex - no need to convert */
__le64 EndOfFile;
__le64 NumOfBytes;
__le64 LastStatusChange; /*SNIA specs DCE time for the 3 time fields */
__le64 LastAccessTime;
__le64 LastModificationTime;
__le64 Uid;
__le64 Gid;
__le32 Type;
__le64 DevMajor;
__le64 DevMinor;
__le64 UniqueId;
__le64 Permissions;
__le64 Nlinks;
FILE_UNIX_BASIC_INFO basic;
char FileName[1];
} __attribute__((packed)) FILE_UNIX_INFO; /* level 0x202 */
......
......@@ -98,9 +98,14 @@ extern struct timespec cnvrtDosUnixTm(__le16 le_date, __le16 le_time,
extern int cifs_posix_open(char *full_path, struct inode **pinode,
struct super_block *sb, int mode, int oflags,
int *poplock, __u16 *pnetfid, int xid);
extern void posix_fill_in_inode(struct inode *tmp_inode,
FILE_UNIX_BASIC_INFO *pData, int isNewInode);
extern void cifs_unix_basic_to_fattr(struct cifs_fattr *fattr,
FILE_UNIX_BASIC_INFO *info,
struct cifs_sb_info *cifs_sb);
extern void cifs_fattr_to_inode(struct inode *inode, struct cifs_fattr *fattr);
extern struct inode *cifs_new_inode(struct super_block *sb, __u64 *inum);
extern struct inode *cifs_iget(struct super_block *sb,
struct cifs_fattr *fattr);
extern int cifs_get_inode_info(struct inode **pinode,
const unsigned char *search_path,
FILE_ALL_INFO *pfile_info,
......
......@@ -188,6 +188,7 @@ int cifs_posix_open(char *full_path, struct inode **pinode,
FILE_UNIX_BASIC_INFO *presp_data;
__u32 posix_flags = 0;
struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
struct cifs_fattr fattr;
cFYI(1, ("posix open %s", full_path));
......@@ -236,22 +237,21 @@ int cifs_posix_open(char *full_path, struct inode **pinode,
if (presp_data->Type == cpu_to_le32(-1))
goto posix_open_ret; /* open ok, caller does qpathinfo */
/* get new inode and set it up */
if (!pinode)
goto posix_open_ret; /* caller does not need info */
if (*pinode == NULL) {
__u64 unique_id = le64_to_cpu(presp_data->UniqueId);
*pinode = cifs_new_inode(sb, &unique_id);
}
/* else an inode was passed in. Update its info, don't create one */
cifs_unix_basic_to_fattr(&fattr, presp_data, cifs_sb);
/* We do not need to close the file if new_inode fails since
the caller will retry qpathinfo as long as inode is null */
if (*pinode == NULL)
/* get new inode and set it up */
if (*pinode == NULL) {
*pinode = cifs_iget(sb, &fattr);
if (!*pinode) {
rc = -ENOMEM;
goto posix_open_ret;
posix_fill_in_inode(*pinode, presp_data, 1);
}
} else {
cifs_fattr_to_inode(*pinode, &fattr);
}
cifs_fill_fileinfo(*pinode, *pnetfid, cifs_sb->tcon, write_only);
......
This diff is collapsed.
......@@ -63,6 +63,55 @@ 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.
*/
static struct dentry *
cifs_readdir_lookup(struct dentry *parent, struct qstr *name,
struct cifs_fattr *fattr)
{
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) {
/* FIXME: check for inode number changes? */
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(sb, fattr);
if (!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
......@@ -76,7 +125,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
......@@ -299,140 +347,6 @@ 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)
{
loff_t local_size;
struct timespec local_mtime;
struct cifsInodeInfo *cifsInfo = CIFS_I(tmp_inode);
struct cifs_sb_info *cifs_sb = CIFS_SB(tmp_inode->i_sb);
__u32 type = le32_to_cpu(pfindData->Type);
__u64 num_of_bytes = le64_to_cpu(pfindData->NumOfBytes);
__u64 end_of_file = le64_to_cpu(pfindData->EndOfFile);
cifsInfo->time = jiffies;
atomic_inc(&cifsInfo->inUse);
/* save mtime and size */
local_mtime = tmp_inode->i_mtime;
local_size = tmp_inode->i_size;
tmp_inode->i_atime =
cifs_NTtimeToUnix(pfindData->LastAccessTime);
tmp_inode->i_mtime =
cifs_NTtimeToUnix(pfindData->LastModificationTime);
tmp_inode->i_ctime =
cifs_NTtimeToUnix(pfindData->LastStatusChange);
tmp_inode->i_mode = le64_to_cpu(pfindData->Permissions);
/* since we set the inode type below we need to mask off type
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));
}
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_UID)
tmp_inode->i_uid = cifs_sb->mnt_uid;
else
tmp_inode->i_uid = le64_to_cpu(pfindData->Uid);
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_GID)
tmp_inode->i_gid = cifs_sb->mnt_gid;
else
tmp_inode->i_gid = le64_to_cpu(pfindData->Gid);
tmp_inode->i_nlink = le64_to_cpu(pfindData->Nlinks);
cifsInfo->server_eof = end_of_file;
spin_lock(&tmp_inode->i_lock);
if (is_size_safe_to_change(cifsInfo, end_of_file)) {
/* can not safely change the file size here if the
client is writing to it due to potential races */
i_size_write(tmp_inode, end_of_file);
/* 512 bytes (2**9) is the fake blocksize that must be used */
/* for this calculation, not the real blocksize */
tmp_inode->i_blocks = (512 - 1 + num_of_bytes) >> 9;
}
spin_unlock(&tmp_inode->i_lock);
if (S_ISREG(tmp_inode->i_mode)) {
cFYI(1, ("File inode"));
tmp_inode->i_op = &cifs_file_inode_ops;
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DIRECT_IO) {
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_BRL)
tmp_inode->i_fop = &cifs_file_direct_nobrl_ops;
else
tmp_inode->i_fop = &cifs_file_direct_ops;
} else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_BRL)
tmp_inode->i_fop = &cifs_file_nobrl_ops;
else
tmp_inode->i_fop = &cifs_file_ops;
if ((cifs_sb->tcon) && (cifs_sb->tcon->ses) &&
(cifs_sb->tcon->ses->server->maxBuf <
PAGE_CACHE_SIZE + MAX_CIFS_HDR_SIZE))
tmp_inode->i_data.a_ops = &cifs_addr_ops_smallbuf;
else
tmp_inode->i_data.a_ops = &cifs_addr_ops;
if (isNewInode)
return; /* No sense invalidating pages for new inode
since we have not started caching readahead
file data for it yet */
if (timespec_equal(&tmp_inode->i_mtime, &local_mtime) &&
(local_size == tmp_inode->i_size)) {
cFYI(1, ("inode exists but unchanged"));
} else {
/* file may have changed on server */
cFYI(1, ("invalidate inode, readdir detected change"));
invalidate_remote_inode(tmp_inode);
}
} else if (S_ISDIR(tmp_inode->i_mode)) {
cFYI(1, ("Directory inode"));
tmp_inode->i_op = &cifs_dir_inode_ops;
tmp_inode->i_fop = &cifs_dir_ops;
} else if (S_ISLNK(tmp_inode->i_mode)) {
cFYI(1, ("Symbolic Link inode"));
tmp_inode->i_op = &cifs_symlink_inode_ops;
/* tmp_inode->i_fop = *//* do not need to set to anything */
} else {
cFYI(1, ("Special inode"));
init_special_inode(tmp_inode, tmp_inode->i_mode,
tmp_inode->i_rdev);
}
}
/* BB eventually need to add the following helper function to
resolve NT_STATUS_STOPPED_ON_SYMLINK return code when
we try to do FindFirst on (NTFS) directory symlinks */
......@@ -872,7 +786,7 @@ static int cifs_get_name_from_search_buf(struct qstr *pqst,
len = strnlen(filename, PATH_MAX);
}
*pinum = le64_to_cpu(pFindData->UniqueId);
*pinum = le64_to_cpu(pFindData->basic.UniqueId);
} else if (level == SMB_FIND_FILE_DIRECTORY_INFO) {
FILE_DIRECTORY_INFO *pFindData =
(FILE_DIRECTORY_INFO *)current_entry;
......@@ -934,9 +848,11 @@ static int cifs_filldir(char *pfindEntry, struct file *file, filldir_t filldir,
struct cifsFileInfo *pCifsF;
unsigned int obj_type;
__u64 inum;
ino_t ino;
struct cifs_sb_info *cifs_sb;
struct inode *tmp_inode;
struct dentry *tmp_dentry;
struct cifs_fattr fattr;
/* get filename and len into qstring */
/* get dentry */
......@@ -967,39 +883,49 @@ static int cifs_filldir(char *pfindEntry, struct file *file, filldir_t filldir,
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);
if (pCifsF->srch_inf.info_level == SMB_FIND_FILE_UNIX) {
cifs_unix_basic_to_fattr(&fattr,
&((FILE_UNIX_INFO *) pfindEntry)->basic,
cifs_sb);
tmp_dentry = cifs_readdir_lookup(file->f_dentry, &qstring,
&fattr);
obj_type = fattr.cf_dtype;
ino = cifs_uniqueid_to_ino_t(fattr.cf_uniqueid);
} 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);
rc = construct_dentry(&qstring, file, &tmp_inode,
&tmp_dentry, NULL);
if ((tmp_inode == NULL) || (tmp_dentry == NULL))
return -ENOMEM;
if ((tmp_inode == NULL) || (tmp_dentry == NULL)) {
rc = -ENOMEM;
goto out;
}
/* 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);
* 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 /* NT */, pfindEntry, &obj_type, rc);
fill_in_inode(tmp_inode, 1, pfindEntry, &obj_type, rc);
if (rc) /* new inode - needs to be tied to dentry */ {
/* new inode - needs to be tied to dentry */
if (rc) {
d_instantiate(tmp_dentry, tmp_inode);
if (rc == 2)
d_rehash(tmp_dentry);
}
ino = cifs_uniqueid_to_ino_t(tmp_inode->i_ino);
}
rc = filldir(direntry, qstring.name, qstring.len, file->f_pos,
tmp_inode->i_ino, obj_type);
ino, obj_type);
if (rc) {
cFYI(1, ("filldir rc = %d", rc));
/* we can not return filldir errors to the caller
......@@ -1008,6 +934,7 @@ static int cifs_filldir(char *pfindEntry, struct file *file, filldir_t filldir,
rc = -EOVERFLOW;
}
out:
dput(tmp_dentry);
return rc;
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment