Commit 45e72402 authored by Paulo Alcantara's avatar Paulo Alcantara Committed by Steve French
Browse files

smb: client: set correct file type from NFS reparse points



Handle all file types in NFS reparse points as specified in MS-FSCC
2.1.2.6 Network File System (NFS) Reparse Data Buffer.

The client is now able to set all file types based on the parsed NFS
reparse point, which used to support only symlinks.  This works for
SMB1+.

Before patch:

$ mount.cifs //srv/share /mnt -o ...
$ ls -l /mnt
ls: cannot access 'block': Operation not supported
ls: cannot access 'char': Operation not supported
ls: cannot access 'fifo': Operation not supported
ls: cannot access 'sock': Operation not supported
total 1
l????????? ? ?    ?    ?            ? block
l????????? ? ?    ?    ?            ? char
-rwxr-xr-x 1 root root 5 Nov 18 23:22 f0
l????????? ? ?    ?    ?            ? fifo
l--------- 1 root root 0 Nov 18 23:23 link -> f0
l????????? ? ?    ?    ?            ? sock

After patch:

$ mount.cifs //srv/share /mnt -o ...
$ ls -l /mnt
total 1
brwxr-xr-x 1 root root  123,  123 Nov 18 00:34 block
crwxr-xr-x 1 root root 1234, 1234 Nov 18 00:33 char
-rwxr-xr-x 1 root root          5 Nov 18 23:22 f0
prwxr-xr-x 1 root root          0 Nov 18 23:23 fifo
lrwxr-xr-x 1 root root          0 Nov 18 23:23 link -> f0
srwxr-xr-x 1 root root          0 Nov 19  2023 sock

Signed-off-by: default avatarPaulo Alcantara (SUSE) <pc@manguebit.com>
Signed-off-by: default avatarSteve French <stfrench@microsoft.com>
parent 539aad7f
Loading
Loading
Loading
Loading
+7 −1
Original line number Diff line number Diff line
@@ -191,7 +191,13 @@ struct cifs_open_info_data {
		bool reparse_point;
		bool symlink;
	};
	__u32 reparse_tag;
	struct {
		__u32 tag;
		union {
			struct reparse_data_buffer *buf;
			struct reparse_posix_data *posix;
		};
	} reparse;
	char *symlink_target;
	union {
		struct smb2_file_all_info fi;
+1 −1
Original line number Diff line number Diff line
@@ -1509,7 +1509,7 @@ struct reparse_posix_data {
	__le16	ReparseDataLength;
	__u16	Reserved;
	__le64	InodeType; /* LNK, FIFO, CHR etc. */
	char	PathBuffer[];
	__u8	DataBuffer[];
} __attribute__((packed));

struct cifs_quota_data {
+2 −2
Original line number Diff line number Diff line
@@ -210,7 +210,7 @@ int cifs_get_inode_info(struct inode **inode, const char *full_path,
			const struct cifs_fid *fid);
bool cifs_reparse_point_to_fattr(struct cifs_sb_info *cifs_sb,
				 struct cifs_fattr *fattr,
				 u32 tag);
				 struct cifs_open_info_data *data);
extern int smb311_posix_get_inode_info(struct inode **pinode, const char *search_path,
			struct super_block *sb, unsigned int xid);
extern int cifs_get_inode_info_unix(struct inode **pinode,
@@ -667,7 +667,7 @@ char *extract_hostname(const char *unc);
char *extract_sharename(const char *unc);
int parse_reparse_point(struct reparse_data_buffer *buf,
			u32 plen, struct cifs_sb_info *cifs_sb,
			bool unicode, char **target_path);
			bool unicode, struct cifs_open_info_data *data);

#ifdef CONFIG_CIFS_DFS_UPCALL
static inline int get_dfs_path(const unsigned int xid, struct cifs_ses *ses,
+46 −5
Original line number Diff line number Diff line
@@ -721,10 +721,51 @@ static void smb311_posix_info_to_fattr(struct cifs_fattr *fattr,
		fattr->cf_mode, fattr->cf_uniqueid, fattr->cf_nlink);
}

static inline dev_t nfs_mkdev(struct reparse_posix_data *buf)
{
	u64 v = le64_to_cpu(*(__le64 *)buf->DataBuffer);

	return MKDEV(v >> 32, v & 0xffffffff);
}

bool cifs_reparse_point_to_fattr(struct cifs_sb_info *cifs_sb,
				 struct cifs_fattr *fattr,
				 u32 tag)
				 struct cifs_open_info_data *data)
{
	struct reparse_posix_data *buf = data->reparse.posix;
	u32 tag = data->reparse.tag;

	if (tag == IO_REPARSE_TAG_NFS && buf) {
		switch (le64_to_cpu(buf->InodeType)) {
		case NFS_SPECFILE_CHR:
			fattr->cf_mode |= S_IFCHR | cifs_sb->ctx->file_mode;
			fattr->cf_dtype = DT_CHR;
			fattr->cf_rdev = nfs_mkdev(buf);
			break;
		case NFS_SPECFILE_BLK:
			fattr->cf_mode |= S_IFBLK | cifs_sb->ctx->file_mode;
			fattr->cf_dtype = DT_BLK;
			fattr->cf_rdev = nfs_mkdev(buf);
			break;
		case NFS_SPECFILE_FIFO:
			fattr->cf_mode |= S_IFIFO | cifs_sb->ctx->file_mode;
			fattr->cf_dtype = DT_FIFO;
			break;
		case NFS_SPECFILE_SOCK:
			fattr->cf_mode |= S_IFSOCK | cifs_sb->ctx->file_mode;
			fattr->cf_dtype = DT_SOCK;
			break;
		case NFS_SPECFILE_LNK:
			fattr->cf_mode = S_IFLNK | cifs_sb->ctx->file_mode;
			fattr->cf_dtype = DT_LNK;
			break;
		default:
			WARN_ON_ONCE(1);
			return false;
		}
		return true;
	}

	switch (tag) {
	case IO_REPARSE_TAG_LX_SYMLINK:
		fattr->cf_mode |= S_IFLNK | cifs_sb->ctx->file_mode;
@@ -790,7 +831,7 @@ static void cifs_open_info_to_fattr(struct cifs_fattr *fattr,
	fattr->cf_nlink = le32_to_cpu(info->NumberOfLinks);

	if (cifs_open_data_reparse(data) &&
	    cifs_reparse_point_to_fattr(cifs_sb, fattr, data->reparse_tag))
	    cifs_reparse_point_to_fattr(cifs_sb, fattr, data))
		goto out_reparse;

	if (fattr->cf_cifsattrs & ATTR_DIRECTORY) {
@@ -855,7 +896,7 @@ cifs_get_file_info(struct file *filp)
		data.adjust_tz = false;
		if (data.symlink_target) {
			data.symlink = true;
			data.reparse_tag = IO_REPARSE_TAG_SYMLINK;
			data.reparse.tag = IO_REPARSE_TAG_SYMLINK;
		}
		cifs_open_info_to_fattr(&fattr, &data, inode->i_sb);
		break;
@@ -1024,7 +1065,7 @@ static int reparse_info_to_fattr(struct cifs_open_info_data *data,
	struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
	struct kvec rsp_iov, *iov = NULL;
	int rsp_buftype = CIFS_NO_BUFFER;
	u32 tag = data->reparse_tag;
	u32 tag = data->reparse.tag;
	int rc = 0;

	if (!tag && server->ops->query_reparse_point) {
@@ -1036,7 +1077,7 @@ static int reparse_info_to_fattr(struct cifs_open_info_data *data,
	}

	rc = -EOPNOTSUPP;
	switch ((data->reparse_tag = tag)) {
	switch ((data->reparse.tag = tag)) {
	case 0: /* SMB1 symlink */
		if (server->ops->query_symlink) {
			rc = server->ops->query_symlink(xid, tcon,
+5 −1
Original line number Diff line number Diff line
@@ -153,6 +153,10 @@ static bool reparse_file_needs_reval(const struct cifs_fattr *fattr)
static void
cifs_fill_common_info(struct cifs_fattr *fattr, struct cifs_sb_info *cifs_sb)
{
	struct cifs_open_info_data data = {
		.reparse = { .tag = fattr->cf_cifstag, },
	};

	fattr->cf_uid = cifs_sb->ctx->linux_uid;
	fattr->cf_gid = cifs_sb->ctx->linux_gid;

@@ -165,7 +169,7 @@ cifs_fill_common_info(struct cifs_fattr *fattr, struct cifs_sb_info *cifs_sb)
	 * reasonably map some of them to directories vs. files vs. symlinks
	 */
	if ((fattr->cf_cifsattrs & ATTR_REPARSE) &&
	    cifs_reparse_point_to_fattr(cifs_sb, fattr, fattr->cf_cifstag))
	    cifs_reparse_point_to_fattr(cifs_sb, fattr, &data))
		goto out_reparse;

	if (fattr->cf_cifsattrs & ATTR_DIRECTORY) {
Loading