brintos

brintos / linux-shallow public Read only

0
0
Text · 19.8 KiB · 74abbdf Raw
733 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (c) 2024 Paulo Alcantara <pc@manguebit.com>4 */5 6#include <linux/fs.h>7#include <linux/stat.h>8#include <linux/slab.h>9#include "cifsglob.h"10#include "smb2proto.h"11#include "cifsproto.h"12#include "cifs_unicode.h"13#include "cifs_debug.h"14#include "fs_context.h"15#include "reparse.h"16 17static int detect_directory_symlink_target(struct cifs_sb_info *cifs_sb,18					   const unsigned int xid,19					   const char *full_path,20					   const char *symname,21					   bool *directory);22 23int smb2_create_reparse_symlink(const unsigned int xid, struct inode *inode,24				struct dentry *dentry, struct cifs_tcon *tcon,25				const char *full_path, const char *symname)26{27	struct reparse_symlink_data_buffer *buf = NULL;28	struct cifs_open_info_data data;29	struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);30	struct inode *new;31	struct kvec iov;32	__le16 *path;33	bool directory;34	char *sym, sep = CIFS_DIR_SEP(cifs_sb);35	u16 len, plen;36	int rc = 0;37 38	sym = kstrdup(symname, GFP_KERNEL);39	if (!sym)40		return -ENOMEM;41 42	data = (struct cifs_open_info_data) {43		.reparse_point = true,44		.reparse = { .tag = IO_REPARSE_TAG_SYMLINK, },45		.symlink_target = sym,46	};47 48	convert_delimiter(sym, sep);49	path = cifs_convert_path_to_utf16(sym, cifs_sb);50	if (!path) {51		rc = -ENOMEM;52		goto out;53	}54 55	/*56	 * SMB distinguish between symlink to directory and symlink to file.57	 * They cannot be exchanged (symlink of file type which points to58	 * directory cannot be resolved and vice-versa). Try to detect if59	 * the symlink target could be a directory or not. When detection60	 * fails then treat symlink as a file (non-directory) symlink.61	 */62	directory = false;63	rc = detect_directory_symlink_target(cifs_sb, xid, full_path, symname, &directory);64	if (rc < 0)65		goto out;66 67	plen = 2 * UniStrnlen((wchar_t *)path, PATH_MAX);68	len = sizeof(*buf) + plen * 2;69	buf = kzalloc(len, GFP_KERNEL);70	if (!buf) {71		rc = -ENOMEM;72		goto out;73	}74 75	buf->ReparseTag = cpu_to_le32(IO_REPARSE_TAG_SYMLINK);76	buf->ReparseDataLength = cpu_to_le16(len - sizeof(struct reparse_data_buffer));77	buf->SubstituteNameOffset = cpu_to_le16(plen);78	buf->SubstituteNameLength = cpu_to_le16(plen);79	memcpy(&buf->PathBuffer[plen], path, plen);80	buf->PrintNameOffset = 0;81	buf->PrintNameLength = cpu_to_le16(plen);82	memcpy(buf->PathBuffer, path, plen);83	buf->Flags = cpu_to_le32(*symname != '/' ? SYMLINK_FLAG_RELATIVE : 0);84	if (*sym != sep)85		buf->Flags = cpu_to_le32(SYMLINK_FLAG_RELATIVE);86 87	convert_delimiter(sym, '/');88	iov.iov_base = buf;89	iov.iov_len = len;90	new = smb2_get_reparse_inode(&data, inode->i_sb, xid,91				     tcon, full_path, directory,92				     &iov, NULL);93	if (!IS_ERR(new))94		d_instantiate(dentry, new);95	else96		rc = PTR_ERR(new);97out:98	kfree(path);99	cifs_free_open_info(&data);100	kfree(buf);101	return rc;102}103 104static int detect_directory_symlink_target(struct cifs_sb_info *cifs_sb,105					   const unsigned int xid,106					   const char *full_path,107					   const char *symname,108					   bool *directory)109{110	char sep = CIFS_DIR_SEP(cifs_sb);111	struct cifs_open_parms oparms;112	struct tcon_link *tlink;113	struct cifs_tcon *tcon;114	const char *basename;115	struct cifs_fid fid;116	char *resolved_path;117	int full_path_len;118	int basename_len;119	int symname_len;120	char *path_sep;121	__u32 oplock;122	int open_rc;123 124	/*125	 * First do some simple check. If the original Linux symlink target ends126	 * with slash, or last path component is dot or dot-dot then it is for127	 * sure symlink to the directory.128	 */129	basename = kbasename(symname);130	basename_len = strlen(basename);131	if (basename_len == 0 || /* symname ends with slash */132	    (basename_len == 1 && basename[0] == '.') || /* last component is "." */133	    (basename_len == 2 && basename[0] == '.' && basename[1] == '.')) { /* or ".." */134		*directory = true;135		return 0;136	}137 138	/*139	 * For absolute symlinks it is not possible to determinate140	 * if it should point to directory or file.141	 */142	if (symname[0] == '/') {143		cifs_dbg(FYI,144			 "%s: cannot determinate if the symlink target path '%s' "145			 "is directory or not, creating '%s' as file symlink\n",146			 __func__, symname, full_path);147		return 0;148	}149 150	/*151	 * If it was not detected as directory yet and the symlink is relative152	 * then try to resolve the path on the SMB server, check if the path153	 * exists and determinate if it is a directory or not.154	 */155 156	full_path_len = strlen(full_path);157	symname_len = strlen(symname);158 159	tlink = cifs_sb_tlink(cifs_sb);160	if (IS_ERR(tlink))161		return PTR_ERR(tlink);162 163	resolved_path = kzalloc(full_path_len + symname_len + 1, GFP_KERNEL);164	if (!resolved_path) {165		cifs_put_tlink(tlink);166		return -ENOMEM;167	}168 169	/*170	 * Compose the resolved SMB symlink path from the SMB full path171	 * and Linux target symlink path.172	 */173	memcpy(resolved_path, full_path, full_path_len+1);174	path_sep = strrchr(resolved_path, sep);175	if (path_sep)176		path_sep++;177	else178		path_sep = resolved_path;179	memcpy(path_sep, symname, symname_len+1);180	if (sep == '\\')181		convert_delimiter(path_sep, sep);182 183	tcon = tlink_tcon(tlink);184	oparms = CIFS_OPARMS(cifs_sb, tcon, resolved_path,185			     FILE_READ_ATTRIBUTES, FILE_OPEN, 0, ACL_NO_MODE);186	oparms.fid = &fid;187 188	/* Try to open as a directory (NOT_FILE) */189	oplock = 0;190	oparms.create_options = cifs_create_options(cifs_sb,191						    CREATE_NOT_FILE | OPEN_REPARSE_POINT);192	open_rc = tcon->ses->server->ops->open(xid, &oparms, &oplock, NULL);193	if (open_rc == 0) {194		/* Successful open means that the target path is definitely a directory. */195		*directory = true;196		tcon->ses->server->ops->close(xid, tcon, &fid);197	} else if (open_rc == -ENOTDIR) {198		/* -ENOTDIR means that the target path is definitely a file. */199		*directory = false;200	} else if (open_rc == -ENOENT) {201		/* -ENOENT means that the target path does not exist. */202		cifs_dbg(FYI,203			 "%s: symlink target path '%s' does not exist, "204			 "creating '%s' as file symlink\n",205			 __func__, symname, full_path);206	} else {207		/* Try to open as a file (NOT_DIR) */208		oplock = 0;209		oparms.create_options = cifs_create_options(cifs_sb,210							    CREATE_NOT_DIR | OPEN_REPARSE_POINT);211		open_rc = tcon->ses->server->ops->open(xid, &oparms, &oplock, NULL);212		if (open_rc == 0) {213			/* Successful open means that the target path is definitely a file. */214			*directory = false;215			tcon->ses->server->ops->close(xid, tcon, &fid);216		} else if (open_rc == -EISDIR) {217			/* -EISDIR means that the target path is definitely a directory. */218			*directory = true;219		} else {220			/*221			 * This code branch is called when we do not have a permission to222			 * open the resolved_path or some other client/process denied223			 * opening the resolved_path.224			 *225			 * TODO: Try to use ops->query_dir_first on the parent directory226			 * of resolved_path, search for basename of resolved_path and227			 * check if the ATTR_DIRECTORY is set in fi.Attributes. In some228			 * case this could work also when opening of the path is denied.229			 */230			cifs_dbg(FYI,231				 "%s: cannot determinate if the symlink target path '%s' "232				 "is directory or not, creating '%s' as file symlink\n",233				 __func__, symname, full_path);234		}235	}236 237	kfree(resolved_path);238	cifs_put_tlink(tlink);239	return 0;240}241 242static int nfs_set_reparse_buf(struct reparse_posix_data *buf,243			       mode_t mode, dev_t dev,244			       struct kvec *iov)245{246	u64 type;247	u16 len, dlen;248 249	len = sizeof(*buf);250 251	switch ((type = reparse_mode_nfs_type(mode))) {252	case NFS_SPECFILE_BLK:253	case NFS_SPECFILE_CHR:254		dlen = sizeof(__le64);255		break;256	case NFS_SPECFILE_FIFO:257	case NFS_SPECFILE_SOCK:258		dlen = 0;259		break;260	default:261		return -EOPNOTSUPP;262	}263 264	buf->ReparseTag = cpu_to_le32(IO_REPARSE_TAG_NFS);265	buf->Reserved = 0;266	buf->InodeType = cpu_to_le64(type);267	buf->ReparseDataLength = cpu_to_le16(len + dlen -268					     sizeof(struct reparse_data_buffer));269	*(__le64 *)buf->DataBuffer = cpu_to_le64(((u64)MINOR(dev) << 32) |270						 MAJOR(dev));271	iov->iov_base = buf;272	iov->iov_len = len + dlen;273	return 0;274}275 276static int mknod_nfs(unsigned int xid, struct inode *inode,277		     struct dentry *dentry, struct cifs_tcon *tcon,278		     const char *full_path, umode_t mode, dev_t dev)279{280	struct cifs_open_info_data data;281	struct reparse_posix_data *p;282	struct inode *new;283	struct kvec iov;284	__u8 buf[sizeof(*p) + sizeof(__le64)];285	int rc;286 287	p = (struct reparse_posix_data *)buf;288	rc = nfs_set_reparse_buf(p, mode, dev, &iov);289	if (rc)290		return rc;291 292	data = (struct cifs_open_info_data) {293		.reparse_point = true,294		.reparse = { .tag = IO_REPARSE_TAG_NFS, .posix = p, },295	};296 297	new = smb2_get_reparse_inode(&data, inode->i_sb, xid,298				     tcon, full_path, false, &iov, NULL);299	if (!IS_ERR(new))300		d_instantiate(dentry, new);301	else302		rc = PTR_ERR(new);303	cifs_free_open_info(&data);304	return rc;305}306 307static int wsl_set_reparse_buf(struct reparse_data_buffer *buf,308			       mode_t mode, struct kvec *iov)309{310	u32 tag;311 312	switch ((tag = reparse_mode_wsl_tag(mode))) {313	case IO_REPARSE_TAG_LX_BLK:314	case IO_REPARSE_TAG_LX_CHR:315	case IO_REPARSE_TAG_LX_FIFO:316	case IO_REPARSE_TAG_AF_UNIX:317		break;318	default:319		return -EOPNOTSUPP;320	}321 322	buf->ReparseTag = cpu_to_le32(tag);323	buf->Reserved = 0;324	buf->ReparseDataLength = 0;325	iov->iov_base = buf;326	iov->iov_len = sizeof(*buf);327	return 0;328}329 330static struct smb2_create_ea_ctx *ea_create_context(u32 dlen, size_t *cc_len)331{332	struct smb2_create_ea_ctx *cc;333 334	*cc_len = round_up(sizeof(*cc) + dlen, 8);335	cc = kzalloc(*cc_len, GFP_KERNEL);336	if (!cc)337		return ERR_PTR(-ENOMEM);338 339	cc->ctx.NameOffset = cpu_to_le16(offsetof(struct smb2_create_ea_ctx,340						  name));341	cc->ctx.NameLength = cpu_to_le16(4);342	memcpy(cc->name, SMB2_CREATE_EA_BUFFER, strlen(SMB2_CREATE_EA_BUFFER));343	cc->ctx.DataOffset = cpu_to_le16(offsetof(struct smb2_create_ea_ctx, ea));344	cc->ctx.DataLength = cpu_to_le32(dlen);345	return cc;346}347 348struct wsl_xattr {349	const char	*name;350	__le64		value;351	u16		size;352	u32		next;353};354 355static int wsl_set_xattrs(struct inode *inode, umode_t _mode,356			  dev_t _dev, struct kvec *iov)357{358	struct smb2_file_full_ea_info *ea;359	struct smb2_create_ea_ctx *cc;360	struct smb3_fs_context *ctx = CIFS_SB(inode->i_sb)->ctx;361	__le64 uid = cpu_to_le64(from_kuid(current_user_ns(), ctx->linux_uid));362	__le64 gid = cpu_to_le64(from_kgid(current_user_ns(), ctx->linux_gid));363	__le64 dev = cpu_to_le64(((u64)MINOR(_dev) << 32) | MAJOR(_dev));364	__le64 mode = cpu_to_le64(_mode);365	struct wsl_xattr xattrs[] = {366		{ .name = SMB2_WSL_XATTR_UID,  .value = uid,  .size = SMB2_WSL_XATTR_UID_SIZE, },367		{ .name = SMB2_WSL_XATTR_GID,  .value = gid,  .size = SMB2_WSL_XATTR_GID_SIZE, },368		{ .name = SMB2_WSL_XATTR_MODE, .value = mode, .size = SMB2_WSL_XATTR_MODE_SIZE, },369		{ .name = SMB2_WSL_XATTR_DEV,  .value = dev, .size = SMB2_WSL_XATTR_DEV_SIZE, },370	};371	size_t cc_len;372	u32 dlen = 0, next = 0;373	int i, num_xattrs;374	u8 name_size = SMB2_WSL_XATTR_NAME_LEN + 1;375 376	memset(iov, 0, sizeof(*iov));377 378	/* Exclude $LXDEV xattr for sockets and fifos */379	if (S_ISSOCK(_mode) || S_ISFIFO(_mode))380		num_xattrs = ARRAY_SIZE(xattrs) - 1;381	else382		num_xattrs = ARRAY_SIZE(xattrs);383 384	for (i = 0; i < num_xattrs; i++) {385		xattrs[i].next = ALIGN(sizeof(*ea) + name_size +386				       xattrs[i].size, 4);387		dlen += xattrs[i].next;388	}389 390	cc = ea_create_context(dlen, &cc_len);391	if (IS_ERR(cc))392		return PTR_ERR(cc);393 394	ea = &cc->ea;395	for (i = 0; i < num_xattrs; i++) {396		ea = (void *)((u8 *)ea + next);397		next = xattrs[i].next;398		ea->next_entry_offset = cpu_to_le32(next);399 400		ea->ea_name_length = name_size - 1;401		ea->ea_value_length = cpu_to_le16(xattrs[i].size);402		memcpy(ea->ea_data, xattrs[i].name, name_size);403		memcpy(&ea->ea_data[name_size],404		       &xattrs[i].value, xattrs[i].size);405	}406	ea->next_entry_offset = 0;407 408	iov->iov_base = cc;409	iov->iov_len = cc_len;410	return 0;411}412 413static int mknod_wsl(unsigned int xid, struct inode *inode,414		     struct dentry *dentry, struct cifs_tcon *tcon,415		     const char *full_path, umode_t mode, dev_t dev)416{417	struct cifs_open_info_data data;418	struct reparse_data_buffer buf;419	struct smb2_create_ea_ctx *cc;420	struct inode *new;421	unsigned int len;422	struct kvec reparse_iov, xattr_iov;423	int rc;424 425	rc = wsl_set_reparse_buf(&buf, mode, &reparse_iov);426	if (rc)427		return rc;428 429	rc = wsl_set_xattrs(inode, mode, dev, &xattr_iov);430	if (rc)431		return rc;432 433	data = (struct cifs_open_info_data) {434		.reparse_point = true,435		.reparse = { .tag = le32_to_cpu(buf.ReparseTag), .buf = &buf, },436	};437 438	cc = xattr_iov.iov_base;439	len = le32_to_cpu(cc->ctx.DataLength);440	memcpy(data.wsl.eas, &cc->ea, len);441	data.wsl.eas_len = len;442 443	new = smb2_get_reparse_inode(&data, inode->i_sb,444				     xid, tcon, full_path, false,445				     &reparse_iov, &xattr_iov);446	if (!IS_ERR(new))447		d_instantiate(dentry, new);448	else449		rc = PTR_ERR(new);450	cifs_free_open_info(&data);451	kfree(xattr_iov.iov_base);452	return rc;453}454 455int smb2_mknod_reparse(unsigned int xid, struct inode *inode,456		       struct dentry *dentry, struct cifs_tcon *tcon,457		       const char *full_path, umode_t mode, dev_t dev)458{459	struct smb3_fs_context *ctx = CIFS_SB(inode->i_sb)->ctx;460	int rc = -EOPNOTSUPP;461 462	switch (ctx->reparse_type) {463	case CIFS_REPARSE_TYPE_NFS:464		rc = mknod_nfs(xid, inode, dentry, tcon, full_path, mode, dev);465		break;466	case CIFS_REPARSE_TYPE_WSL:467		rc = mknod_wsl(xid, inode, dentry, tcon, full_path, mode, dev);468		break;469	}470	return rc;471}472 473/* See MS-FSCC 2.1.2.6 for the 'NFS' style reparse tags */474static int parse_reparse_posix(struct reparse_posix_data *buf,475			       struct cifs_sb_info *cifs_sb,476			       struct cifs_open_info_data *data)477{478	unsigned int len;479	u64 type;480 481	len = le16_to_cpu(buf->ReparseDataLength);482	if (len < sizeof(buf->InodeType)) {483		cifs_dbg(VFS, "srv returned malformed nfs buffer\n");484		return -EIO;485	}486 487	len -= sizeof(buf->InodeType);488 489	switch ((type = le64_to_cpu(buf->InodeType))) {490	case NFS_SPECFILE_LNK:491		if (len == 0 || (len % 2)) {492			cifs_dbg(VFS, "srv returned malformed nfs symlink buffer\n");493			return -EIO;494		}495		/*496		 * Check that buffer does not contain UTF-16 null codepoint497		 * because Linux cannot process symlink with null byte.498		 */499		if (UniStrnlen((wchar_t *)buf->DataBuffer, len/2) != len/2) {500			cifs_dbg(VFS, "srv returned null byte in nfs symlink target location\n");501			return -EIO;502		}503		data->symlink_target = cifs_strndup_from_utf16(buf->DataBuffer,504							       len, true,505							       cifs_sb->local_nls);506		if (!data->symlink_target)507			return -ENOMEM;508		cifs_dbg(FYI, "%s: target path: %s\n",509			 __func__, data->symlink_target);510		break;511	case NFS_SPECFILE_CHR:512	case NFS_SPECFILE_BLK:513		/* DataBuffer for block and char devices contains two 32-bit numbers */514		if (len != 8) {515			cifs_dbg(VFS, "srv returned malformed nfs buffer for type: 0x%llx\n", type);516			return -EIO;517		}518		break;519	case NFS_SPECFILE_FIFO:520	case NFS_SPECFILE_SOCK:521		/* DataBuffer for fifos and sockets is empty */522		if (len != 0) {523			cifs_dbg(VFS, "srv returned malformed nfs buffer for type: 0x%llx\n", type);524			return -EIO;525		}526		break;527	default:528		cifs_dbg(VFS, "%s: unhandled inode type: 0x%llx\n",529			 __func__, type);530		return -EOPNOTSUPP;531	}532	return 0;533}534 535static int parse_reparse_symlink(struct reparse_symlink_data_buffer *sym,536				 u32 plen, bool unicode,537				 struct cifs_sb_info *cifs_sb,538				 struct cifs_open_info_data *data)539{540	unsigned int len;541	unsigned int offs;542 543	/* We handle Symbolic Link reparse tag here. See: MS-FSCC 2.1.2.4 */544 545	offs = le16_to_cpu(sym->SubstituteNameOffset);546	len = le16_to_cpu(sym->SubstituteNameLength);547	if (offs + 20 > plen || offs + len + 20 > plen) {548		cifs_dbg(VFS, "srv returned malformed symlink buffer\n");549		return -EIO;550	}551 552	data->symlink_target = cifs_strndup_from_utf16(sym->PathBuffer + offs,553						       len, unicode,554						       cifs_sb->local_nls);555	if (!data->symlink_target)556		return -ENOMEM;557 558	convert_delimiter(data->symlink_target, '/');559	cifs_dbg(FYI, "%s: target path: %s\n", __func__, data->symlink_target);560 561	return 0;562}563 564int parse_reparse_point(struct reparse_data_buffer *buf,565			u32 plen, struct cifs_sb_info *cifs_sb,566			bool unicode, struct cifs_open_info_data *data)567{568	struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb);569 570	data->reparse.buf = buf;571 572	/* See MS-FSCC 2.1.2 */573	switch (le32_to_cpu(buf->ReparseTag)) {574	case IO_REPARSE_TAG_NFS:575		return parse_reparse_posix((struct reparse_posix_data *)buf,576					   cifs_sb, data);577	case IO_REPARSE_TAG_SYMLINK:578		return parse_reparse_symlink(579			(struct reparse_symlink_data_buffer *)buf,580			plen, unicode, cifs_sb, data);581	case IO_REPARSE_TAG_LX_SYMLINK:582	case IO_REPARSE_TAG_AF_UNIX:583	case IO_REPARSE_TAG_LX_FIFO:584	case IO_REPARSE_TAG_LX_CHR:585	case IO_REPARSE_TAG_LX_BLK:586		break;587	default:588		cifs_tcon_dbg(VFS | ONCE, "unhandled reparse tag: 0x%08x\n",589			      le32_to_cpu(buf->ReparseTag));590		break;591	}592	return 0;593}594 595int smb2_parse_reparse_point(struct cifs_sb_info *cifs_sb,596			     struct kvec *rsp_iov,597			     struct cifs_open_info_data *data)598{599	struct reparse_data_buffer *buf;600	struct smb2_ioctl_rsp *io = rsp_iov->iov_base;601	u32 plen = le32_to_cpu(io->OutputCount);602 603	buf = (struct reparse_data_buffer *)((u8 *)io +604					     le32_to_cpu(io->OutputOffset));605	return parse_reparse_point(buf, plen, cifs_sb, true, data);606}607 608static void wsl_to_fattr(struct cifs_open_info_data *data,609			 struct cifs_sb_info *cifs_sb,610			 u32 tag, struct cifs_fattr *fattr)611{612	struct smb2_file_full_ea_info *ea;613	u32 next = 0;614 615	switch (tag) {616	case IO_REPARSE_TAG_LX_SYMLINK:617		fattr->cf_mode |= S_IFLNK;618		break;619	case IO_REPARSE_TAG_LX_FIFO:620		fattr->cf_mode |= S_IFIFO;621		break;622	case IO_REPARSE_TAG_AF_UNIX:623		fattr->cf_mode |= S_IFSOCK;624		break;625	case IO_REPARSE_TAG_LX_CHR:626		fattr->cf_mode |= S_IFCHR;627		break;628	case IO_REPARSE_TAG_LX_BLK:629		fattr->cf_mode |= S_IFBLK;630		break;631	}632 633	if (!data->wsl.eas_len)634		goto out;635 636	ea = (struct smb2_file_full_ea_info *)data->wsl.eas;637	do {638		const char *name;639		void *v;640		u8 nlen;641 642		ea = (void *)((u8 *)ea + next);643		next = le32_to_cpu(ea->next_entry_offset);644		if (!le16_to_cpu(ea->ea_value_length))645			continue;646 647		name = ea->ea_data;648		nlen = ea->ea_name_length;649		v = (void *)((u8 *)ea->ea_data + ea->ea_name_length + 1);650 651		if (!strncmp(name, SMB2_WSL_XATTR_UID, nlen))652			fattr->cf_uid = wsl_make_kuid(cifs_sb, v);653		else if (!strncmp(name, SMB2_WSL_XATTR_GID, nlen))654			fattr->cf_gid = wsl_make_kgid(cifs_sb, v);655		else if (!strncmp(name, SMB2_WSL_XATTR_MODE, nlen))656			fattr->cf_mode = (umode_t)le32_to_cpu(*(__le32 *)v);657		else if (!strncmp(name, SMB2_WSL_XATTR_DEV, nlen))658			fattr->cf_rdev = reparse_mkdev(v);659	} while (next);660out:661	fattr->cf_dtype = S_DT(fattr->cf_mode);662}663 664bool cifs_reparse_point_to_fattr(struct cifs_sb_info *cifs_sb,665				 struct cifs_fattr *fattr,666				 struct cifs_open_info_data *data)667{668	struct reparse_posix_data *buf = data->reparse.posix;669	u32 tag = data->reparse.tag;670 671	if (tag == IO_REPARSE_TAG_NFS && buf) {672		if (le16_to_cpu(buf->ReparseDataLength) < sizeof(buf->InodeType))673			return false;674		switch (le64_to_cpu(buf->InodeType)) {675		case NFS_SPECFILE_CHR:676			if (le16_to_cpu(buf->ReparseDataLength) != sizeof(buf->InodeType) + 8)677				return false;678			fattr->cf_mode |= S_IFCHR;679			fattr->cf_rdev = reparse_mkdev(buf->DataBuffer);680			break;681		case NFS_SPECFILE_BLK:682			if (le16_to_cpu(buf->ReparseDataLength) != sizeof(buf->InodeType) + 8)683				return false;684			fattr->cf_mode |= S_IFBLK;685			fattr->cf_rdev = reparse_mkdev(buf->DataBuffer);686			break;687		case NFS_SPECFILE_FIFO:688			fattr->cf_mode |= S_IFIFO;689			break;690		case NFS_SPECFILE_SOCK:691			fattr->cf_mode |= S_IFSOCK;692			break;693		case NFS_SPECFILE_LNK:694			fattr->cf_mode |= S_IFLNK;695			break;696		default:697			WARN_ON_ONCE(1);698			return false;699		}700		goto out;701	}702 703	switch (tag) {704	case IO_REPARSE_TAG_INTERNAL:705		if (!(fattr->cf_cifsattrs & ATTR_DIRECTORY))706			return false;707		fallthrough;708	case IO_REPARSE_TAG_DFS:709	case IO_REPARSE_TAG_DFSR:710	case IO_REPARSE_TAG_MOUNT_POINT:711		/* See cifs_create_junction_fattr() */712		fattr->cf_mode = S_IFDIR | 0711;713		break;714	case IO_REPARSE_TAG_LX_SYMLINK:715	case IO_REPARSE_TAG_LX_FIFO:716	case IO_REPARSE_TAG_AF_UNIX:717	case IO_REPARSE_TAG_LX_CHR:718	case IO_REPARSE_TAG_LX_BLK:719		wsl_to_fattr(data, cifs_sb, tag, fattr);720		break;721	case 0: /* SMB1 symlink */722	case IO_REPARSE_TAG_SYMLINK:723	case IO_REPARSE_TAG_NFS:724		fattr->cf_mode |= S_IFLNK;725		break;726	default:727		return false;728	}729out:730	fattr->cf_dtype = S_DT(fattr->cf_mode);731	return true;732}733