[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:
Jim McDonough 2013-09-21 10:36:10 -05:00 committed by Steve French
parent 9ae6cf606a
commit 74d290da47
3 changed files with 43 additions and 6 deletions

View File

@ -1268,6 +1268,7 @@ struct dfs_info3_param {
#define CIFS_FATTR_DELETE_PENDING 0x2 #define CIFS_FATTR_DELETE_PENDING 0x2
#define CIFS_FATTR_NEED_REVAL 0x4 #define CIFS_FATTR_NEED_REVAL 0x4
#define CIFS_FATTR_INO_COLLISION 0x8 #define CIFS_FATTR_INO_COLLISION 0x8
#define CIFS_FATTR_UNKNOWN_NLINK 0x10
struct cifs_fattr { struct cifs_fattr {
u32 cf_flags; u32 cf_flags;

View File

@ -120,6 +120,33 @@ cifs_revalidate_cache(struct inode *inode, struct cifs_fattr *fattr)
cifs_i->invalid_mapping = true; 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 */ /* populate an inode with info from a cifs_fattr struct */
void void
cifs_fattr_to_inode(struct inode *inode, struct cifs_fattr *fattr) 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_mtime = fattr->cf_mtime;
inode->i_ctime = fattr->cf_ctime; inode->i_ctime = fattr->cf_ctime;
inode->i_rdev = fattr->cf_rdev; 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_uid = fattr->cf_uid;
inode->i_gid = fattr->cf_gid; 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_bytes = le64_to_cpu(info->AllocationSize);
fattr->cf_createtime = le64_to_cpu(info->CreationTime); fattr->cf_createtime = le64_to_cpu(info->CreationTime);
fattr->cf_nlink = le32_to_cpu(info->NumberOfLinks);
if (fattr->cf_cifsattrs & ATTR_DIRECTORY) { if (fattr->cf_cifsattrs & ATTR_DIRECTORY) {
fattr->cf_mode = S_IFDIR | cifs_sb->mnt_dir_mode; fattr->cf_mode = S_IFDIR | cifs_sb->mnt_dir_mode;
fattr->cf_dtype = DT_DIR; 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 * Server can return wrong NumberOfLinks value for directories
* when Unix extensions are disabled - fake it. * 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) { } else if (fattr->cf_cifsattrs & ATTR_REPARSE) {
fattr->cf_mode = S_IFLNK; fattr->cf_mode = S_IFLNK;
fattr->cf_dtype = DT_LNK; 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) if (fattr->cf_cifsattrs & ATTR_READONLY)
fattr->cf_mode &= ~(S_IWUGO); fattr->cf_mode &= ~(S_IWUGO);
fattr->cf_nlink = le32_to_cpu(info->NumberOfLinks); /*
if (fattr->cf_nlink < 1) { * Don't accept zero nlink from non-unix servers unless
cifs_dbg(1, "replacing bogus file nlink value %u\n", * 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);
fattr->cf_nlink = 1; fattr->cf_flags |= CIFS_FATTR_UNKNOWN_NLINK;
} }
} }

View File

@ -180,6 +180,9 @@ cifs_fill_common_info(struct cifs_fattr *fattr, struct cifs_sb_info *cifs_sb)
fattr->cf_dtype = DT_REG; 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) if (fattr->cf_cifsattrs & ATTR_READONLY)
fattr->cf_mode &= ~S_IWUGO; fattr->cf_mode &= ~S_IWUGO;