mirror of
https://github.com/torvalds/linux.git
synced 2024-11-24 05:02:12 +00:00
CIFS: refactor cifs_get_inode_info()
Make logic of cifs_get_inode() much clearer by moving code to sub functions and adding comments. Document the steps this function does. cifs_get_inode_info() gets and updates a file inode metadata from its file path. * If caller already has raw info data from server they can pass it. * If inode already exists (just need to update) caller can pass it. Step 1: get raw data from server if none was passed Step 2: parse raw data into intermediate internal cifs_fattr struct Step 3: set fattr uniqueid which is later used for inode number. This can sometime be done from raw data Step 4: tweak fattr according to mount options (file_mode, acl to mode bits, uid, gid, etc) Step 5: update or create inode from final fattr struct * add is_smb1_server() helper * add is_inode_cache_good() helper * move SMB1-backupcreds-getinfo-retry to separate func cifs_backup_query_path_info(). * move set-uniqueid code to separate func cifs_set_fattr_ino() * don't clobber uniqueid from backup cred retry * fix some probable corner cases memleaks Signed-off-by: Aurelien Aptel <aaptel@suse.com> Signed-off-by: Steve French <stfrench@microsoft.com>
This commit is contained in:
parent
f6a6bf7c4d
commit
b8f7442bc4
@ -1967,4 +1967,10 @@ extern struct smb_version_values smb302_values;
|
||||
#define ALT_SMB311_VERSION_STRING "3.11"
|
||||
extern struct smb_version_operations smb311_operations;
|
||||
extern struct smb_version_values smb311_values;
|
||||
|
||||
static inline bool is_smb1_server(struct TCP_Server_Info *server)
|
||||
{
|
||||
return strcmp(server->vals->version_string, SMB1_VERSION_STRING) == 0;
|
||||
}
|
||||
|
||||
#endif /* _CIFS_GLOB_H */
|
||||
|
352
fs/cifs/inode.c
352
fs/cifs/inode.c
@ -727,22 +727,138 @@ static __u64 simple_hashstr(const char *str)
|
||||
return hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* cifs_backup_query_path_info - SMB1 fallback code to get ino
|
||||
*
|
||||
* Fallback code to get file metadata when we don't have access to
|
||||
* @full_path (EACCESS) and have backup creds.
|
||||
*
|
||||
* @data will be set to search info result buffer
|
||||
* @resp_buf will be set to cifs resp buf and needs to be freed with
|
||||
* cifs_buf_release() when done with @data.
|
||||
*/
|
||||
static int
|
||||
cifs_backup_query_path_info(int xid,
|
||||
struct cifs_tcon *tcon,
|
||||
struct super_block *sb,
|
||||
const char *full_path,
|
||||
void **resp_buf,
|
||||
FILE_ALL_INFO **data)
|
||||
{
|
||||
struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
|
||||
struct cifs_search_info info = {0};
|
||||
u16 flags;
|
||||
int rc;
|
||||
|
||||
*resp_buf = NULL;
|
||||
info.endOfSearch = false;
|
||||
if (tcon->unix_ext)
|
||||
info.info_level = SMB_FIND_FILE_UNIX;
|
||||
else if ((tcon->ses->capabilities &
|
||||
tcon->ses->server->vals->cap_nt_find) == 0)
|
||||
info.info_level = SMB_FIND_FILE_INFO_STANDARD;
|
||||
else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM)
|
||||
info.info_level = SMB_FIND_FILE_ID_FULL_DIR_INFO;
|
||||
else /* no srvino useful for fallback to some netapp */
|
||||
info.info_level = SMB_FIND_FILE_DIRECTORY_INFO;
|
||||
|
||||
flags = CIFS_SEARCH_CLOSE_ALWAYS |
|
||||
CIFS_SEARCH_CLOSE_AT_END |
|
||||
CIFS_SEARCH_BACKUP_SEARCH;
|
||||
|
||||
rc = CIFSFindFirst(xid, tcon, full_path,
|
||||
cifs_sb, NULL, flags, &info, false);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
*resp_buf = (void *)info.ntwrk_buf_start;
|
||||
*data = (FILE_ALL_INFO *)info.srch_entries_start;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
cifs_set_fattr_ino(int xid,
|
||||
struct cifs_tcon *tcon,
|
||||
struct super_block *sb,
|
||||
struct inode **inode,
|
||||
const char *full_path,
|
||||
FILE_ALL_INFO *data,
|
||||
struct cifs_fattr *fattr)
|
||||
{
|
||||
struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
|
||||
struct TCP_Server_Info *server = tcon->ses->server;
|
||||
int rc;
|
||||
|
||||
if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM)) {
|
||||
if (*inode)
|
||||
fattr->cf_uniqueid = CIFS_I(*inode)->uniqueid;
|
||||
else
|
||||
fattr->cf_uniqueid = iunique(sb, ROOT_I);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* If we have an inode pass a NULL tcon to ensure we don't
|
||||
* make a round trip to the server. This only works for SMB2+.
|
||||
*/
|
||||
rc = server->ops->get_srv_inum(xid,
|
||||
*inode ? NULL : tcon,
|
||||
cifs_sb, full_path,
|
||||
&fattr->cf_uniqueid,
|
||||
data);
|
||||
if (rc) {
|
||||
/*
|
||||
* If that fails reuse existing ino or generate one
|
||||
* and disable server ones
|
||||
*/
|
||||
if (*inode)
|
||||
fattr->cf_uniqueid = CIFS_I(*inode)->uniqueid;
|
||||
else {
|
||||
fattr->cf_uniqueid = iunique(sb, ROOT_I);
|
||||
cifs_autodisable_serverino(cifs_sb);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/* If no errors, check for zero root inode (invalid) */
|
||||
if (fattr->cf_uniqueid == 0 && strlen(full_path) == 0) {
|
||||
cifs_dbg(FYI, "Invalid (0) inodenum\n");
|
||||
if (*inode) {
|
||||
/* reuse */
|
||||
fattr->cf_uniqueid = CIFS_I(*inode)->uniqueid;
|
||||
} else {
|
||||
/* make an ino by hashing the UNC */
|
||||
fattr->cf_flags |= CIFS_FATTR_FAKE_ROOT_INO;
|
||||
fattr->cf_uniqueid = simple_hashstr(tcon->treeName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool is_inode_cache_good(struct inode *ino)
|
||||
{
|
||||
return ino && CIFS_CACHE_READ(CIFS_I(ino)) && CIFS_I(ino)->time != 0;
|
||||
}
|
||||
|
||||
int
|
||||
cifs_get_inode_info(struct inode **inode, const char *full_path,
|
||||
FILE_ALL_INFO *data, struct super_block *sb, int xid,
|
||||
cifs_get_inode_info(struct inode **inode,
|
||||
const char *full_path,
|
||||
FILE_ALL_INFO *in_data,
|
||||
struct super_block *sb, int xid,
|
||||
const struct cifs_fid *fid)
|
||||
{
|
||||
__u16 srchflgs;
|
||||
int rc = 0, tmprc = ENOSYS;
|
||||
|
||||
struct cifs_tcon *tcon;
|
||||
struct TCP_Server_Info *server;
|
||||
struct tcon_link *tlink;
|
||||
struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
|
||||
char *buf = NULL;
|
||||
bool adjust_tz = false;
|
||||
struct cifs_fattr fattr;
|
||||
struct cifs_search_info *srchinf = NULL;
|
||||
struct cifs_fattr fattr = {0};
|
||||
bool symlink = false;
|
||||
FILE_ALL_INFO *data = in_data;
|
||||
FILE_ALL_INFO *tmp_data = NULL;
|
||||
void *smb1_backup_rsp_buf = NULL;
|
||||
int rc = 0;
|
||||
int tmprc = 0;
|
||||
|
||||
tlink = cifs_sb_tlink(cifs_sb);
|
||||
if (IS_ERR(tlink))
|
||||
@ -750,142 +866,88 @@ cifs_get_inode_info(struct inode **inode, const char *full_path,
|
||||
tcon = tlink_tcon(tlink);
|
||||
server = tcon->ses->server;
|
||||
|
||||
cifs_dbg(FYI, "Getting info on %s\n", full_path);
|
||||
/*
|
||||
* 1. Fetch file metadata if not provided (data)
|
||||
*/
|
||||
|
||||
if ((data == NULL) && (*inode != NULL)) {
|
||||
if (CIFS_CACHE_READ(CIFS_I(*inode)) &&
|
||||
CIFS_I(*inode)->time != 0) {
|
||||
if (!data) {
|
||||
if (is_inode_cache_good(*inode)) {
|
||||
cifs_dbg(FYI, "No need to revalidate cached inode sizes\n");
|
||||
goto cgii_exit;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
/* if inode info is not passed, get it from server */
|
||||
if (data == NULL) {
|
||||
if (!server->ops->query_path_info) {
|
||||
rc = -ENOSYS;
|
||||
goto cgii_exit;
|
||||
}
|
||||
buf = kmalloc(sizeof(FILE_ALL_INFO), GFP_KERNEL);
|
||||
if (buf == NULL) {
|
||||
tmp_data = kmalloc(sizeof(FILE_ALL_INFO), GFP_KERNEL);
|
||||
if (!tmp_data) {
|
||||
rc = -ENOMEM;
|
||||
goto cgii_exit;
|
||||
goto out;
|
||||
}
|
||||
data = (FILE_ALL_INFO *)buf;
|
||||
rc = server->ops->query_path_info(xid, tcon, cifs_sb, full_path,
|
||||
data, &adjust_tz, &symlink);
|
||||
rc = server->ops->query_path_info(xid, tcon, cifs_sb,
|
||||
full_path, tmp_data,
|
||||
&adjust_tz, &symlink);
|
||||
data = tmp_data;
|
||||
}
|
||||
|
||||
if (!rc) {
|
||||
cifs_all_info_to_fattr(&fattr, data, sb, adjust_tz,
|
||||
symlink);
|
||||
} else if (rc == -EREMOTE) {
|
||||
cifs_create_dfs_fattr(&fattr, sb);
|
||||
rc = 0;
|
||||
} else if ((rc == -EACCES) && backup_cred(cifs_sb) &&
|
||||
(strcmp(server->vals->version_string, SMB1_VERSION_STRING)
|
||||
== 0)) {
|
||||
/*
|
||||
* For SMB2 and later the backup intent flag is already
|
||||
* sent if needed on open and there is no path based
|
||||
* FindFirst operation to use to retry with
|
||||
*/
|
||||
|
||||
srchinf = kzalloc(sizeof(struct cifs_search_info),
|
||||
GFP_KERNEL);
|
||||
if (srchinf == NULL) {
|
||||
rc = -ENOMEM;
|
||||
goto cgii_exit;
|
||||
}
|
||||
|
||||
srchinf->endOfSearch = false;
|
||||
if (tcon->unix_ext)
|
||||
srchinf->info_level = SMB_FIND_FILE_UNIX;
|
||||
else if ((tcon->ses->capabilities &
|
||||
tcon->ses->server->vals->cap_nt_find) == 0)
|
||||
srchinf->info_level = SMB_FIND_FILE_INFO_STANDARD;
|
||||
else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM)
|
||||
srchinf->info_level = SMB_FIND_FILE_ID_FULL_DIR_INFO;
|
||||
else /* no srvino useful for fallback to some netapp */
|
||||
srchinf->info_level = SMB_FIND_FILE_DIRECTORY_INFO;
|
||||
|
||||
srchflgs = CIFS_SEARCH_CLOSE_ALWAYS |
|
||||
CIFS_SEARCH_CLOSE_AT_END |
|
||||
CIFS_SEARCH_BACKUP_SEARCH;
|
||||
|
||||
rc = CIFSFindFirst(xid, tcon, full_path,
|
||||
cifs_sb, NULL, srchflgs, srchinf, false);
|
||||
if (!rc) {
|
||||
data = (FILE_ALL_INFO *)srchinf->srch_entries_start;
|
||||
|
||||
cifs_dir_info_to_fattr(&fattr,
|
||||
(FILE_DIRECTORY_INFO *)data, cifs_sb);
|
||||
fattr.cf_uniqueid = le64_to_cpu(
|
||||
((SEARCH_ID_FULL_DIR_INFO *)data)->UniqueId);
|
||||
|
||||
cifs_buf_release(srchinf->ntwrk_buf_start);
|
||||
}
|
||||
kfree(srchinf);
|
||||
if (rc)
|
||||
goto cgii_exit;
|
||||
} else
|
||||
goto cgii_exit;
|
||||
|
||||
/*
|
||||
* If an inode wasn't passed in, then get the inode number
|
||||
*
|
||||
* Is an i_ino of zero legal? Can we use that to check if the server
|
||||
* supports returning inode numbers? Are there other sanity checks we
|
||||
* can use to ensure that the server is really filling in that field?
|
||||
* 2. Convert it to internal cifs metadata (fattr)
|
||||
*/
|
||||
if (*inode == NULL) {
|
||||
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) {
|
||||
if (server->ops->get_srv_inum)
|
||||
tmprc = server->ops->get_srv_inum(xid,
|
||||
tcon, cifs_sb, full_path,
|
||||
&fattr.cf_uniqueid, data);
|
||||
if (tmprc) {
|
||||
cifs_dbg(FYI, "GetSrvInodeNum rc %d\n",
|
||||
tmprc);
|
||||
fattr.cf_uniqueid = iunique(sb, ROOT_I);
|
||||
cifs_autodisable_serverino(cifs_sb);
|
||||
} else if ((fattr.cf_uniqueid == 0) &&
|
||||
strlen(full_path) == 0) {
|
||||
/* some servers ret bad root ino ie 0 */
|
||||
cifs_dbg(FYI, "Invalid (0) inodenum\n");
|
||||
fattr.cf_flags |=
|
||||
CIFS_FATTR_FAKE_ROOT_INO;
|
||||
fattr.cf_uniqueid =
|
||||
simple_hashstr(tcon->treeName);
|
||||
}
|
||||
} else
|
||||
fattr.cf_uniqueid = iunique(sb, ROOT_I);
|
||||
} else {
|
||||
if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM)
|
||||
&& server->ops->get_srv_inum) {
|
||||
/*
|
||||
* Pass a NULL tcon to ensure we don't make a round
|
||||
* trip to the server. This only works for SMB2+.
|
||||
*/
|
||||
tmprc = server->ops->get_srv_inum(xid,
|
||||
NULL, cifs_sb, full_path,
|
||||
&fattr.cf_uniqueid, data);
|
||||
if (tmprc)
|
||||
fattr.cf_uniqueid = CIFS_I(*inode)->uniqueid;
|
||||
else if ((fattr.cf_uniqueid == 0) &&
|
||||
strlen(full_path) == 0) {
|
||||
/*
|
||||
* Reuse existing root inode num since
|
||||
* inum zero for root causes ls of . and .. to
|
||||
* not be returned
|
||||
*/
|
||||
cifs_dbg(FYI, "Srv ret 0 inode num for root\n");
|
||||
fattr.cf_uniqueid = CIFS_I(*inode)->uniqueid;
|
||||
}
|
||||
} else
|
||||
fattr.cf_uniqueid = CIFS_I(*inode)->uniqueid;
|
||||
|
||||
switch (rc) {
|
||||
case 0:
|
||||
cifs_all_info_to_fattr(&fattr, data, sb, adjust_tz, symlink);
|
||||
break;
|
||||
case -EREMOTE:
|
||||
/* DFS link, no metadata available on this server */
|
||||
cifs_create_dfs_fattr(&fattr, sb);
|
||||
rc = 0;
|
||||
break;
|
||||
case -EACCES:
|
||||
/*
|
||||
* perm errors, try again with backup flags if possible
|
||||
*
|
||||
* For SMB2 and later the backup intent flag
|
||||
* is already sent if needed on open and there
|
||||
* is no path based FindFirst operation to use
|
||||
* to retry with
|
||||
*/
|
||||
if (backup_cred(cifs_sb) && is_smb1_server(server)) {
|
||||
/* for easier reading */
|
||||
FILE_DIRECTORY_INFO *fdi;
|
||||
SEARCH_ID_FULL_DIR_INFO *si;
|
||||
|
||||
rc = cifs_backup_query_path_info(xid, tcon, sb,
|
||||
full_path,
|
||||
&smb1_backup_rsp_buf,
|
||||
&data);
|
||||
if (rc)
|
||||
goto out;
|
||||
|
||||
fdi = (FILE_DIRECTORY_INFO *)data;
|
||||
si = (SEARCH_ID_FULL_DIR_INFO *)data;
|
||||
|
||||
cifs_dir_info_to_fattr(&fattr, fdi, cifs_sb);
|
||||
fattr.cf_uniqueid = le64_to_cpu(si->UniqueId);
|
||||
/* uniqueid set, skip get inum step */
|
||||
goto handle_mnt_opt;
|
||||
} else {
|
||||
/* nothing we can do, bail out */
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
cifs_dbg(FYI, "%s: unhandled err rc %d\n", __func__, rc);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* 3. Get or update inode number (fattr.cf_uniqueid)
|
||||
*/
|
||||
|
||||
cifs_set_fattr_ino(xid, tcon, sb, inode, full_path, data, &fattr);
|
||||
|
||||
/*
|
||||
* 4. Tweak fattr based on mount options
|
||||
*/
|
||||
|
||||
handle_mnt_opt:
|
||||
/* query for SFU type info if supported and needed */
|
||||
if (fattr.cf_cifsattrs & ATTR_SYSTEM &&
|
||||
cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) {
|
||||
@ -900,16 +962,15 @@ cifs_get_inode_info(struct inode **inode, const char *full_path,
|
||||
full_path, fid);
|
||||
if (rc) {
|
||||
cifs_dbg(FYI, "%s: Get mode from SID failed. rc=%d\n",
|
||||
__func__, rc);
|
||||
goto cgii_exit;
|
||||
__func__, rc);
|
||||
goto out;
|
||||
}
|
||||
} else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) {
|
||||
rc = cifs_acl_to_fattr(cifs_sb, &fattr, *inode, false,
|
||||
full_path, fid);
|
||||
if (rc) {
|
||||
full_path, fid); if (rc) {
|
||||
cifs_dbg(FYI, "%s: Getting ACL failed with error: %d\n",
|
||||
__func__, rc);
|
||||
goto cgii_exit;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
@ -925,6 +986,10 @@ cifs_get_inode_info(struct inode **inode, const char *full_path,
|
||||
cifs_dbg(FYI, "check_mf_symlink: %d\n", tmprc);
|
||||
}
|
||||
|
||||
/*
|
||||
* 5. Update inode with final fattr data
|
||||
*/
|
||||
|
||||
if (!*inode) {
|
||||
*inode = cifs_iget(sb, &fattr);
|
||||
if (!*inode)
|
||||
@ -937,7 +1002,7 @@ cifs_get_inode_info(struct inode **inode, const char *full_path,
|
||||
CIFS_I(*inode)->uniqueid != fattr.cf_uniqueid)) {
|
||||
CIFS_I(*inode)->time = 0; /* force reval */
|
||||
rc = -ESTALE;
|
||||
goto cgii_exit;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* if filetype is different, return error */
|
||||
@ -945,18 +1010,15 @@ cifs_get_inode_info(struct inode **inode, const char *full_path,
|
||||
(fattr.cf_mode & S_IFMT))) {
|
||||
CIFS_I(*inode)->time = 0; /* force reval */
|
||||
rc = -ESTALE;
|
||||
goto cgii_exit;
|
||||
goto out;
|
||||
}
|
||||
|
||||
cifs_fattr_to_inode(*inode, &fattr);
|
||||
}
|
||||
|
||||
cgii_exit:
|
||||
if ((*inode) && ((*inode)->i_ino == 0))
|
||||
cifs_dbg(FYI, "inode number of zero returned\n");
|
||||
|
||||
kfree(buf);
|
||||
out:
|
||||
cifs_buf_release(smb1_backup_rsp_buf);
|
||||
cifs_put_tlink(tlink);
|
||||
kfree(tmp_data);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user