Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 74d290da authored by Jim McDonough's avatar Jim McDonough Committed by Steve French
Browse files

[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: default avatarJim McDonough <jmcd@samba.org>
Reviewed-by: default avatarJeff Layton <jlayton@redhat.com>
Reviewed-by: default avatarDavid Disseldorp <ddiss@samba.org>
Signed-off-by: default avatarSteve French <smfrench@gmail.com>
parent 9ae6cf60
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -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;
+39 −6
Original line number Diff line number Diff line
@@ -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;
		}
	}

+3 −0
Original line number Diff line number Diff line
@@ -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;