[CIFS] Provide sane values for nlink
Since we don't get info about the number of links from the readdir linfo levels, stat() will return 0 for st_nlink, and in particular, samba re-exported shares will show directories as files (as samba is keying off st_nlink before evaluating how to set the dos modebits) when doing a dir or ls. Copy nlink to the inode, unless it wasn't provided. Provide sane values if we don't have an existing one and none was provided. Signed-off-by: Jim McDonough <jmcd@samba.org> Reviewed-by: Jeff Layton <jlayton@redhat.com> Reviewed-by: David Disseldorp <ddiss@samba.org> Signed-off-by: Steve French <smfrench@gmail.com>
This commit is contained in:
Родитель
9ae6cf606a
Коммит
74d290da47
|
@ -1268,6 +1268,7 @@ struct dfs_info3_param {
|
|||
#define CIFS_FATTR_DELETE_PENDING 0x2
|
||||
#define CIFS_FATTR_NEED_REVAL 0x4
|
||||
#define CIFS_FATTR_INO_COLLISION 0x8
|
||||
#define CIFS_FATTR_UNKNOWN_NLINK 0x10
|
||||
|
||||
struct cifs_fattr {
|
||||
u32 cf_flags;
|
||||
|
|
|
@ -120,6 +120,33 @@ cifs_revalidate_cache(struct inode *inode, struct cifs_fattr *fattr)
|
|||
cifs_i->invalid_mapping = true;
|
||||
}
|
||||
|
||||
/*
|
||||
* copy nlink to the inode, unless it wasn't provided. Provide
|
||||
* sane values if we don't have an existing one and none was provided
|
||||
*/
|
||||
static void
|
||||
cifs_nlink_fattr_to_inode(struct inode *inode, struct cifs_fattr *fattr)
|
||||
{
|
||||
/*
|
||||
* if we're in a situation where we can't trust what we
|
||||
* got from the server (readdir, some non-unix cases)
|
||||
* fake reasonable values
|
||||
*/
|
||||
if (fattr->cf_flags & CIFS_FATTR_UNKNOWN_NLINK) {
|
||||
/* only provide fake values on a new inode */
|
||||
if (inode->i_state & I_NEW) {
|
||||
if (fattr->cf_cifsattrs & ATTR_DIRECTORY)
|
||||
set_nlink(inode, 2);
|
||||
else
|
||||
set_nlink(inode, 1);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/* we trust the server, so update it */
|
||||
set_nlink(inode, fattr->cf_nlink);
|
||||
}
|
||||
|
||||
/* populate an inode with info from a cifs_fattr struct */
|
||||
void
|
||||
cifs_fattr_to_inode(struct inode *inode, struct cifs_fattr *fattr)
|
||||
|
@ -134,7 +161,7 @@ cifs_fattr_to_inode(struct inode *inode, struct cifs_fattr *fattr)
|
|||
inode->i_mtime = fattr->cf_mtime;
|
||||
inode->i_ctime = fattr->cf_ctime;
|
||||
inode->i_rdev = fattr->cf_rdev;
|
||||
set_nlink(inode, fattr->cf_nlink);
|
||||
cifs_nlink_fattr_to_inode(inode, fattr);
|
||||
inode->i_uid = fattr->cf_uid;
|
||||
inode->i_gid = fattr->cf_gid;
|
||||
|
||||
|
@ -541,6 +568,7 @@ cifs_all_info_to_fattr(struct cifs_fattr *fattr, FILE_ALL_INFO *info,
|
|||
fattr->cf_bytes = le64_to_cpu(info->AllocationSize);
|
||||
fattr->cf_createtime = le64_to_cpu(info->CreationTime);
|
||||
|
||||
fattr->cf_nlink = le32_to_cpu(info->NumberOfLinks);
|
||||
if (fattr->cf_cifsattrs & ATTR_DIRECTORY) {
|
||||
fattr->cf_mode = S_IFDIR | cifs_sb->mnt_dir_mode;
|
||||
fattr->cf_dtype = DT_DIR;
|
||||
|
@ -548,7 +576,8 @@ cifs_all_info_to_fattr(struct cifs_fattr *fattr, FILE_ALL_INFO *info,
|
|||
* Server can return wrong NumberOfLinks value for directories
|
||||
* when Unix extensions are disabled - fake it.
|
||||
*/
|
||||
fattr->cf_nlink = 2;
|
||||
if (!tcon->unix_ext)
|
||||
fattr->cf_flags |= CIFS_FATTR_UNKNOWN_NLINK;
|
||||
} else if (fattr->cf_cifsattrs & ATTR_REPARSE) {
|
||||
fattr->cf_mode = S_IFLNK;
|
||||
fattr->cf_dtype = DT_LNK;
|
||||
|
@ -561,11 +590,15 @@ cifs_all_info_to_fattr(struct cifs_fattr *fattr, FILE_ALL_INFO *info,
|
|||
if (fattr->cf_cifsattrs & ATTR_READONLY)
|
||||
fattr->cf_mode &= ~(S_IWUGO);
|
||||
|
||||
fattr->cf_nlink = le32_to_cpu(info->NumberOfLinks);
|
||||
if (fattr->cf_nlink < 1) {
|
||||
cifs_dbg(1, "replacing bogus file nlink value %u\n",
|
||||
/*
|
||||
* Don't accept zero nlink from non-unix servers unless
|
||||
* delete is pending. Instead mark it as unknown.
|
||||
*/
|
||||
if ((fattr->cf_nlink < 1) && !tcon->unix_ext &&
|
||||
!info->DeletePending) {
|
||||
cifs_dbg(1, "bogus file nlink value %u\n",
|
||||
fattr->cf_nlink);
|
||||
fattr->cf_nlink = 1;
|
||||
fattr->cf_flags |= CIFS_FATTR_UNKNOWN_NLINK;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -180,6 +180,9 @@ cifs_fill_common_info(struct cifs_fattr *fattr, struct cifs_sb_info *cifs_sb)
|
|||
fattr->cf_dtype = DT_REG;
|
||||
}
|
||||
|
||||
/* non-unix readdir doesn't provide nlink */
|
||||
fattr->cf_flags |= CIFS_FATTR_UNKNOWN_NLINK;
|
||||
|
||||
if (fattr->cf_cifsattrs & ATTR_READONLY)
|
||||
fattr->cf_mode &= ~S_IWUGO;
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче