1// SPDX-License-Identifier: GPL-2.0-only
2/*
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
17int smb2_create_reparse_symlink(const unsigned int xid, struct inode *inode,
18				struct dentry *dentry, struct cifs_tcon *tcon,
19				const char *full_path, const char *symname)
20{
21	struct reparse_symlink_data_buffer *buf = NULL;
22	struct cifs_open_info_data data;
23	struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
24	struct inode *new;
25	struct kvec iov;
26	__le16 *path;
27	char *sym, sep = CIFS_DIR_SEP(cifs_sb);
28	u16 len, plen;
29	int rc = 0;
30
31	sym = kstrdup(symname, GFP_KERNEL);
32	if (!sym)
33		return -ENOMEM;
34
35	data = (struct cifs_open_info_data) {
36		.reparse_point = true,
37		.reparse = { .tag = IO_REPARSE_TAG_SYMLINK, },
38		.symlink_target = sym,
39	};
40
41	convert_delimiter(sym, sep);
42	path = cifs_convert_path_to_utf16(sym, cifs_sb);
43	if (!path) {
44		rc = -ENOMEM;
45		goto out;
46	}
47
48	plen = 2 * UniStrnlen((wchar_t *)path, PATH_MAX);
49	len = sizeof(*buf) + plen * 2;
50	buf = kzalloc(len, GFP_KERNEL);
51	if (!buf) {
52		rc = -ENOMEM;
53		goto out;
54	}
55
56	buf->ReparseTag = cpu_to_le32(IO_REPARSE_TAG_SYMLINK);
57	buf->ReparseDataLength = cpu_to_le16(len - sizeof(struct reparse_data_buffer));
58	buf->SubstituteNameOffset = cpu_to_le16(plen);
59	buf->SubstituteNameLength = cpu_to_le16(plen);
60	memcpy(&buf->PathBuffer[plen], path, plen);
61	buf->PrintNameOffset = 0;
62	buf->PrintNameLength = cpu_to_le16(plen);
63	memcpy(buf->PathBuffer, path, plen);
64	buf->Flags = cpu_to_le32(*symname != '/' ? SYMLINK_FLAG_RELATIVE : 0);
65	if (*sym != sep)
66		buf->Flags = cpu_to_le32(SYMLINK_FLAG_RELATIVE);
67
68	convert_delimiter(sym, '/');
69	iov.iov_base = buf;
70	iov.iov_len = len;
71	new = smb2_get_reparse_inode(&data, inode->i_sb, xid,
72				     tcon, full_path, &iov, NULL);
73	if (!IS_ERR(new))
74		d_instantiate(dentry, new);
75	else
76		rc = PTR_ERR(new);
77out:
78	kfree(path);
79	cifs_free_open_info(&data);
80	kfree(buf);
81	return rc;
82}
83
84static int nfs_set_reparse_buf(struct reparse_posix_data *buf,
85			       mode_t mode, dev_t dev,
86			       struct kvec *iov)
87{
88	u64 type;
89	u16 len, dlen;
90
91	len = sizeof(*buf);
92
93	switch ((type = reparse_mode_nfs_type(mode))) {
94	case NFS_SPECFILE_BLK:
95	case NFS_SPECFILE_CHR:
96		dlen = sizeof(__le64);
97		break;
98	case NFS_SPECFILE_FIFO:
99	case NFS_SPECFILE_SOCK:
100		dlen = 0;
101		break;
102	default:
103		return -EOPNOTSUPP;
104	}
105
106	buf->ReparseTag = cpu_to_le32(IO_REPARSE_TAG_NFS);
107	buf->Reserved = 0;
108	buf->InodeType = cpu_to_le64(type);
109	buf->ReparseDataLength = cpu_to_le16(len + dlen -
110					     sizeof(struct reparse_data_buffer));
111	*(__le64 *)buf->DataBuffer = cpu_to_le64(((u64)MAJOR(dev) << 32) |
112						 MINOR(dev));
113	iov->iov_base = buf;
114	iov->iov_len = len + dlen;
115	return 0;
116}
117
118static int mknod_nfs(unsigned int xid, struct inode *inode,
119		     struct dentry *dentry, struct cifs_tcon *tcon,
120		     const char *full_path, umode_t mode, dev_t dev)
121{
122	struct cifs_open_info_data data;
123	struct reparse_posix_data *p;
124	struct inode *new;
125	struct kvec iov;
126	__u8 buf[sizeof(*p) + sizeof(__le64)];
127	int rc;
128
129	p = (struct reparse_posix_data *)buf;
130	rc = nfs_set_reparse_buf(p, mode, dev, &iov);
131	if (rc)
132		return rc;
133
134	data = (struct cifs_open_info_data) {
135		.reparse_point = true,
136		.reparse = { .tag = IO_REPARSE_TAG_NFS, .posix = p, },
137	};
138
139	new = smb2_get_reparse_inode(&data, inode->i_sb, xid,
140				     tcon, full_path, &iov, NULL);
141	if (!IS_ERR(new))
142		d_instantiate(dentry, new);
143	else
144		rc = PTR_ERR(new);
145	cifs_free_open_info(&data);
146	return rc;
147}
148
149static int wsl_set_reparse_buf(struct reparse_data_buffer *buf,
150			       mode_t mode, struct kvec *iov)
151{
152	u32 tag;
153
154	switch ((tag = reparse_mode_wsl_tag(mode))) {
155	case IO_REPARSE_TAG_LX_BLK:
156	case IO_REPARSE_TAG_LX_CHR:
157	case IO_REPARSE_TAG_LX_FIFO:
158	case IO_REPARSE_TAG_AF_UNIX:
159		break;
160	default:
161		return -EOPNOTSUPP;
162	}
163
164	buf->ReparseTag = cpu_to_le32(tag);
165	buf->Reserved = 0;
166	buf->ReparseDataLength = 0;
167	iov->iov_base = buf;
168	iov->iov_len = sizeof(*buf);
169	return 0;
170}
171
172static struct smb2_create_ea_ctx *ea_create_context(u32 dlen, size_t *cc_len)
173{
174	struct smb2_create_ea_ctx *cc;
175
176	*cc_len = round_up(sizeof(*cc) + dlen, 8);
177	cc = kzalloc(*cc_len, GFP_KERNEL);
178	if (!cc)
179		return ERR_PTR(-ENOMEM);
180
181	cc->ctx.NameOffset = cpu_to_le16(offsetof(struct smb2_create_ea_ctx,
182						  name));
183	cc->ctx.NameLength = cpu_to_le16(4);
184	memcpy(cc->name, SMB2_CREATE_EA_BUFFER, strlen(SMB2_CREATE_EA_BUFFER));
185	cc->ctx.DataOffset = cpu_to_le16(offsetof(struct smb2_create_ea_ctx, ea));
186	cc->ctx.DataLength = cpu_to_le32(dlen);
187	return cc;
188}
189
190struct wsl_xattr {
191	const char	*name;
192	__le64		value;
193	u16		size;
194	u32		next;
195};
196
197static int wsl_set_xattrs(struct inode *inode, umode_t _mode,
198			  dev_t _dev, struct kvec *iov)
199{
200	struct smb2_file_full_ea_info *ea;
201	struct smb2_create_ea_ctx *cc;
202	struct smb3_fs_context *ctx = CIFS_SB(inode->i_sb)->ctx;
203	__le64 uid = cpu_to_le64(from_kuid(current_user_ns(), ctx->linux_uid));
204	__le64 gid = cpu_to_le64(from_kgid(current_user_ns(), ctx->linux_gid));
205	__le64 dev = cpu_to_le64(((u64)MINOR(_dev) << 32) | MAJOR(_dev));
206	__le64 mode = cpu_to_le64(_mode);
207	struct wsl_xattr xattrs[] = {
208		{ .name = SMB2_WSL_XATTR_UID,  .value = uid,  .size = SMB2_WSL_XATTR_UID_SIZE, },
209		{ .name = SMB2_WSL_XATTR_GID,  .value = gid,  .size = SMB2_WSL_XATTR_GID_SIZE, },
210		{ .name = SMB2_WSL_XATTR_MODE, .value = mode, .size = SMB2_WSL_XATTR_MODE_SIZE, },
211		{ .name = SMB2_WSL_XATTR_DEV,  .value = dev, .size = SMB2_WSL_XATTR_DEV_SIZE, },
212	};
213	size_t cc_len;
214	u32 dlen = 0, next = 0;
215	int i, num_xattrs;
216	u8 name_size = SMB2_WSL_XATTR_NAME_LEN + 1;
217
218	memset(iov, 0, sizeof(*iov));
219
220	/* Exclude $LXDEV xattr for sockets and fifos */
221	if (S_ISSOCK(_mode) || S_ISFIFO(_mode))
222		num_xattrs = ARRAY_SIZE(xattrs) - 1;
223	else
224		num_xattrs = ARRAY_SIZE(xattrs);
225
226	for (i = 0; i < num_xattrs; i++) {
227		xattrs[i].next = ALIGN(sizeof(*ea) + name_size +
228				       xattrs[i].size, 4);
229		dlen += xattrs[i].next;
230	}
231
232	cc = ea_create_context(dlen, &cc_len);
233	if (IS_ERR(cc))
234		return PTR_ERR(cc);
235
236	ea = &cc->ea;
237	for (i = 0; i < num_xattrs; i++) {
238		ea = (void *)((u8 *)ea + next);
239		next = xattrs[i].next;
240		ea->next_entry_offset = cpu_to_le32(next);
241
242		ea->ea_name_length = name_size - 1;
243		ea->ea_value_length = cpu_to_le16(xattrs[i].size);
244		memcpy(ea->ea_data, xattrs[i].name, name_size);
245		memcpy(&ea->ea_data[name_size],
246		       &xattrs[i].value, xattrs[i].size);
247	}
248	ea->next_entry_offset = 0;
249
250	iov->iov_base = cc;
251	iov->iov_len = cc_len;
252	return 0;
253}
254
255static int mknod_wsl(unsigned int xid, struct inode *inode,
256		     struct dentry *dentry, struct cifs_tcon *tcon,
257		     const char *full_path, umode_t mode, dev_t dev)
258{
259	struct cifs_open_info_data data;
260	struct reparse_data_buffer buf;
261	struct smb2_create_ea_ctx *cc;
262	struct inode *new;
263	unsigned int len;
264	struct kvec reparse_iov, xattr_iov;
265	int rc;
266
267	rc = wsl_set_reparse_buf(&buf, mode, &reparse_iov);
268	if (rc)
269		return rc;
270
271	rc = wsl_set_xattrs(inode, mode, dev, &xattr_iov);
272	if (rc)
273		return rc;
274
275	data = (struct cifs_open_info_data) {
276		.reparse_point = true,
277		.reparse = { .tag = le32_to_cpu(buf.ReparseTag), .buf = &buf, },
278	};
279
280	cc = xattr_iov.iov_base;
281	len = le32_to_cpu(cc->ctx.DataLength);
282	memcpy(data.wsl.eas, &cc->ea, len);
283	data.wsl.eas_len = len;
284
285	new = smb2_get_reparse_inode(&data, inode->i_sb,
286				     xid, tcon, full_path,
287				     &reparse_iov, &xattr_iov);
288	if (!IS_ERR(new))
289		d_instantiate(dentry, new);
290	else
291		rc = PTR_ERR(new);
292	cifs_free_open_info(&data);
293	kfree(xattr_iov.iov_base);
294	return rc;
295}
296
297int smb2_mknod_reparse(unsigned int xid, struct inode *inode,
298		       struct dentry *dentry, struct cifs_tcon *tcon,
299		       const char *full_path, umode_t mode, dev_t dev)
300{
301	struct smb3_fs_context *ctx = CIFS_SB(inode->i_sb)->ctx;
302	int rc = -EOPNOTSUPP;
303
304	switch (ctx->reparse_type) {
305	case CIFS_REPARSE_TYPE_NFS:
306		rc = mknod_nfs(xid, inode, dentry, tcon, full_path, mode, dev);
307		break;
308	case CIFS_REPARSE_TYPE_WSL:
309		rc = mknod_wsl(xid, inode, dentry, tcon, full_path, mode, dev);
310		break;
311	}
312	return rc;
313}
314
315/* See MS-FSCC 2.1.2.6 for the 'NFS' style reparse tags */
316static int parse_reparse_posix(struct reparse_posix_data *buf,
317			       struct cifs_sb_info *cifs_sb,
318			       struct cifs_open_info_data *data)
319{
320	unsigned int len;
321	u64 type;
322
323	switch ((type = le64_to_cpu(buf->InodeType))) {
324	case NFS_SPECFILE_LNK:
325		len = le16_to_cpu(buf->ReparseDataLength);
326		data->symlink_target = cifs_strndup_from_utf16(buf->DataBuffer,
327							       len, true,
328							       cifs_sb->local_nls);
329		if (!data->symlink_target)
330			return -ENOMEM;
331		convert_delimiter(data->symlink_target, '/');
332		cifs_dbg(FYI, "%s: target path: %s\n",
333			 __func__, data->symlink_target);
334		break;
335	case NFS_SPECFILE_CHR:
336	case NFS_SPECFILE_BLK:
337	case NFS_SPECFILE_FIFO:
338	case NFS_SPECFILE_SOCK:
339		break;
340	default:
341		cifs_dbg(VFS, "%s: unhandled inode type: 0x%llx\n",
342			 __func__, type);
343		return -EOPNOTSUPP;
344	}
345	return 0;
346}
347
348static int parse_reparse_symlink(struct reparse_symlink_data_buffer *sym,
349				 u32 plen, bool unicode,
350				 struct cifs_sb_info *cifs_sb,
351				 struct cifs_open_info_data *data)
352{
353	unsigned int len;
354	unsigned int offs;
355
356	/* We handle Symbolic Link reparse tag here. See: MS-FSCC 2.1.2.4 */
357
358	offs = le16_to_cpu(sym->SubstituteNameOffset);
359	len = le16_to_cpu(sym->SubstituteNameLength);
360	if (offs + 20 > plen || offs + len + 20 > plen) {
361		cifs_dbg(VFS, "srv returned malformed symlink buffer\n");
362		return -EIO;
363	}
364
365	data->symlink_target = cifs_strndup_from_utf16(sym->PathBuffer + offs,
366						       len, unicode,
367						       cifs_sb->local_nls);
368	if (!data->symlink_target)
369		return -ENOMEM;
370
371	convert_delimiter(data->symlink_target, '/');
372	cifs_dbg(FYI, "%s: target path: %s\n", __func__, data->symlink_target);
373
374	return 0;
375}
376
377int parse_reparse_point(struct reparse_data_buffer *buf,
378			u32 plen, struct cifs_sb_info *cifs_sb,
379			bool unicode, struct cifs_open_info_data *data)
380{
381	data->reparse.buf = buf;
382
383	/* See MS-FSCC 2.1.2 */
384	switch (le32_to_cpu(buf->ReparseTag)) {
385	case IO_REPARSE_TAG_NFS:
386		return parse_reparse_posix((struct reparse_posix_data *)buf,
387					   cifs_sb, data);
388	case IO_REPARSE_TAG_SYMLINK:
389		return parse_reparse_symlink(
390			(struct reparse_symlink_data_buffer *)buf,
391			plen, unicode, cifs_sb, data);
392	case IO_REPARSE_TAG_LX_SYMLINK:
393	case IO_REPARSE_TAG_AF_UNIX:
394	case IO_REPARSE_TAG_LX_FIFO:
395	case IO_REPARSE_TAG_LX_CHR:
396	case IO_REPARSE_TAG_LX_BLK:
397		return 0;
398	default:
399		cifs_dbg(VFS, "%s: unhandled reparse tag: 0x%08x\n",
400			 __func__, le32_to_cpu(buf->ReparseTag));
401		return -EOPNOTSUPP;
402	}
403}
404
405int smb2_parse_reparse_point(struct cifs_sb_info *cifs_sb,
406			     struct kvec *rsp_iov,
407			     struct cifs_open_info_data *data)
408{
409	struct reparse_data_buffer *buf;
410	struct smb2_ioctl_rsp *io = rsp_iov->iov_base;
411	u32 plen = le32_to_cpu(io->OutputCount);
412
413	buf = (struct reparse_data_buffer *)((u8 *)io +
414					     le32_to_cpu(io->OutputOffset));
415	return parse_reparse_point(buf, plen, cifs_sb, true, data);
416}
417
418static void wsl_to_fattr(struct cifs_open_info_data *data,
419			 struct cifs_sb_info *cifs_sb,
420			 u32 tag, struct cifs_fattr *fattr)
421{
422	struct smb2_file_full_ea_info *ea;
423	u32 next = 0;
424
425	switch (tag) {
426	case IO_REPARSE_TAG_LX_SYMLINK:
427		fattr->cf_mode |= S_IFLNK;
428		break;
429	case IO_REPARSE_TAG_LX_FIFO:
430		fattr->cf_mode |= S_IFIFO;
431		break;
432	case IO_REPARSE_TAG_AF_UNIX:
433		fattr->cf_mode |= S_IFSOCK;
434		break;
435	case IO_REPARSE_TAG_LX_CHR:
436		fattr->cf_mode |= S_IFCHR;
437		break;
438	case IO_REPARSE_TAG_LX_BLK:
439		fattr->cf_mode |= S_IFBLK;
440		break;
441	}
442
443	if (!data->wsl.eas_len)
444		goto out;
445
446	ea = (struct smb2_file_full_ea_info *)data->wsl.eas;
447	do {
448		const char *name;
449		void *v;
450		u8 nlen;
451
452		ea = (void *)((u8 *)ea + next);
453		next = le32_to_cpu(ea->next_entry_offset);
454		if (!le16_to_cpu(ea->ea_value_length))
455			continue;
456
457		name = ea->ea_data;
458		nlen = ea->ea_name_length;
459		v = (void *)((u8 *)ea->ea_data + ea->ea_name_length + 1);
460
461		if (!strncmp(name, SMB2_WSL_XATTR_UID, nlen))
462			fattr->cf_uid = wsl_make_kuid(cifs_sb, v);
463		else if (!strncmp(name, SMB2_WSL_XATTR_GID, nlen))
464			fattr->cf_gid = wsl_make_kgid(cifs_sb, v);
465		else if (!strncmp(name, SMB2_WSL_XATTR_MODE, nlen))
466			fattr->cf_mode = (umode_t)le32_to_cpu(*(__le32 *)v);
467		else if (!strncmp(name, SMB2_WSL_XATTR_DEV, nlen))
468			fattr->cf_rdev = wsl_mkdev(v);
469	} while (next);
470out:
471	fattr->cf_dtype = S_DT(fattr->cf_mode);
472}
473
474bool cifs_reparse_point_to_fattr(struct cifs_sb_info *cifs_sb,
475				 struct cifs_fattr *fattr,
476				 struct cifs_open_info_data *data)
477{
478	struct reparse_posix_data *buf = data->reparse.posix;
479	u32 tag = data->reparse.tag;
480
481	if (tag == IO_REPARSE_TAG_NFS && buf) {
482		switch (le64_to_cpu(buf->InodeType)) {
483		case NFS_SPECFILE_CHR:
484			fattr->cf_mode |= S_IFCHR;
485			fattr->cf_rdev = reparse_nfs_mkdev(buf);
486			break;
487		case NFS_SPECFILE_BLK:
488			fattr->cf_mode |= S_IFBLK;
489			fattr->cf_rdev = reparse_nfs_mkdev(buf);
490			break;
491		case NFS_SPECFILE_FIFO:
492			fattr->cf_mode |= S_IFIFO;
493			break;
494		case NFS_SPECFILE_SOCK:
495			fattr->cf_mode |= S_IFSOCK;
496			break;
497		case NFS_SPECFILE_LNK:
498			fattr->cf_mode |= S_IFLNK;
499			break;
500		default:
501			WARN_ON_ONCE(1);
502			return false;
503		}
504		goto out;
505	}
506
507	switch (tag) {
508	case IO_REPARSE_TAG_DFS:
509	case IO_REPARSE_TAG_DFSR:
510	case IO_REPARSE_TAG_MOUNT_POINT:
511		/* See cifs_create_junction_fattr() */
512		fattr->cf_mode = S_IFDIR | 0711;
513		break;
514	case IO_REPARSE_TAG_LX_SYMLINK:
515	case IO_REPARSE_TAG_LX_FIFO:
516	case IO_REPARSE_TAG_AF_UNIX:
517	case IO_REPARSE_TAG_LX_CHR:
518	case IO_REPARSE_TAG_LX_BLK:
519		wsl_to_fattr(data, cifs_sb, tag, fattr);
520		break;
521	case 0: /* SMB1 symlink */
522	case IO_REPARSE_TAG_SYMLINK:
523	case IO_REPARSE_TAG_NFS:
524		fattr->cf_mode |= S_IFLNK;
525		break;
526	default:
527		return false;
528	}
529out:
530	fattr->cf_dtype = S_DT(fattr->cf_mode);
531	return true;
532}
533