xref: /linux/fs/ntfs/reparse.c (revision 1b78070aaef63512688aebfbc82365ef9d6660f1)
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Processing of reparse points
4  *
5  * Part of this file is based on code from the NTFS-3G.
6  *
7  * Copyright (c) 2008-2021 Jean-Pierre Andre
8  * Copyright (c) 2025 LG Electronics Co., Ltd.
9  */
10 
11 #include "ntfs.h"
12 #include "layout.h"
13 #include "attrib.h"
14 #include "inode.h"
15 #include "dir.h"
16 #include "volume.h"
17 #include "mft.h"
18 #include "index.h"
19 #include "lcnalloc.h"
20 #include "reparse.h"
21 
22 struct wsl_link_reparse_data {
23 	__le32	type;
24 	char	link[];
25 };
26 
27 struct wof_reparse_data {
28 	__le32 version;
29 	__le32 provider;
30 	__le32 provider_version;
31 	__le32 compression_format;
32 } __packed;
33 
34 #define WOF_CURRENT_VERSION			cpu_to_le32(1)
35 
36 #define WOF_PROVIDER_WIM			cpu_to_le32(1)
37 #define WOF_PROVIDER_FILE			cpu_to_le32(2)
38 
39 #define WOF_PROVIDER_CURRENT_VERSION		cpu_to_le32(1)
40 
41 #define WOF_COMPRESSION_XPRESS4K		cpu_to_le32(0)
42 #define WOF_COMPRESSION_LZX			cpu_to_le32(1)
43 #define WOF_COMPRESSION_XPRESS8K		cpu_to_le32(2)
44 #define WOF_COMPRESSION_XPRESS16K		cpu_to_le32(3)
45 
46 static bool reparse_name_is_valid(size_t size, size_t name_off, u16 len)
47 {
48 	if ((name_off | len) & 1)
49 		return false;
50 
51 	return name_off + len <= size;
52 }
53 
54 /*
55  * Windows-native reparse payloads store pathnames as UTF-16 strings with '\\'
56  * separators. Convert the on-disk UTF-16 target into the mount's NLS and
57  * normalize path separators.
58  */
59 static int ntfs_reparse_target_to_nls(struct ntfs_volume *vol,
60 				      const __le16 *uname, u16 ulen,
61 				      char **target)
62 {
63 	int err, i;
64 
65 	*target = NULL;
66 	ulen >>= 1;
67 	if (!ulen)
68 		return -EINVAL;
69 
70 	if (!uname[ulen - 1])
71 		ulen--;
72 
73 	err = ntfs_ucstonls(vol, uname, ulen, (unsigned char **)target, 0);
74 	if (err < 0) {
75 		ntfs_attr_name_free((unsigned char **)target);
76 		return err;
77 	}
78 
79 	for (i = 0; i < err; i++) {
80 		if ((*target)[i] == '\\')
81 			(*target)[i] = '/';
82 	}
83 
84 	return 0;
85 }
86 
87 /* Index entry in $Extend/$Reparse */
88 struct reparse_index {
89 	struct index_entry_header header;
90 	struct reparse_index_key key;
91 	__le32 filling;
92 };
93 
94 __le16 reparse_index_name[] = {cpu_to_le16('$'), cpu_to_le16('R'), 0};
95 
96 
97 /*
98  * Check if the reparse point attribute buffer is valid.
99  * Returns true if valid, false otherwise.
100  */
101 static bool valid_reparse_buffer(struct ntfs_inode *ni,
102 				 const struct reparse_point *reparse_attr,
103 				 size_t size,
104 				 size_t payload_min_len)
105 {
106 	size_t expected;
107 
108 	if (!ni || !reparse_attr)
109 		return false;
110 
111 	/* Minimum size must cover reparse_point header */
112 	if (size < sizeof(struct reparse_point))
113 		return false;
114 
115 	/* The payload must contain the fixed fields for the current tag. */
116 	if (payload_min_len &&
117 	    le16_to_cpu(reparse_attr->reparse_data_length) < payload_min_len)
118 		return false;
119 
120 	/* Reserved zero tag is invalid */
121 	if (reparse_attr->reparse_tag == IO_REPARSE_TAG_RESERVED_ZERO)
122 		return false;
123 
124 	/* Calculate expected total size */
125 	expected = sizeof(struct reparse_point) +
126 		le16_to_cpu(reparse_attr->reparse_data_length);
127 
128 	/* Add GUID size for non-Microsoft tags */
129 	if (!(reparse_attr->reparse_tag & IO_REPARSE_TAG_IS_MICROSOFT))
130 		expected += sizeof(struct guid);
131 
132 	/* Buffer must exactly match the expected size */
133 	return expected == size;
134 }
135 
136 /*
137  * Do some sanity checks on reparse data
138  *
139  * Microsoft reparse points have an 8-byte header whereas
140  * non-Microsoft reparse points have a 24-byte header.  In each case,
141  * 'reparse_data_length' must equal the number of non-header bytes.
142  *
143  * If the reparse data looks like a junction point or symbolic
144  * link, more checks can be done.
145  */
146 static bool valid_reparse_data(struct ntfs_inode *ni,
147 		const struct reparse_point *reparse_attr, size_t size)
148 {
149 	if (size < sizeof(*reparse_attr))
150 		return false;
151 
152 	switch (reparse_attr->reparse_tag) {
153 	case IO_REPARSE_TAG_MOUNT_POINT:
154 	{
155 		struct mount_point_reparse_data *data;
156 		size_t data_offs;
157 
158 		if (!valid_reparse_buffer(ni, reparse_attr, size, sizeof(*data)))
159 			return false;
160 
161 		data = (struct mount_point_reparse_data *)reparse_attr->reparse_data;
162 		data_offs = offsetof(struct reparse_point, reparse_data) +
163 			offsetof(struct mount_point_reparse_data, path_buffer);
164 
165 		if (!reparse_name_is_valid(size,
166 					   data_offs +
167 					   le16_to_cpu(data->substitute_name_offset),
168 					   le16_to_cpu(data->substitute_name_length)) ||
169 		    !reparse_name_is_valid(size,
170 					   data_offs +
171 					   le16_to_cpu(data->print_name_offset),
172 					   le16_to_cpu(data->print_name_length)))
173 			return false;
174 		break;
175 	}
176 	case IO_REPARSE_TAG_SYMLINK:
177 	{
178 		struct symlink_reparse_data *data;
179 		size_t data_offs;
180 
181 		if (!valid_reparse_buffer(ni, reparse_attr, size,
182 					  sizeof(*data)))
183 			return false;
184 
185 		data = (struct symlink_reparse_data *)reparse_attr->reparse_data;
186 		data_offs = offsetof(struct reparse_point, reparse_data) +
187 			offsetof(struct symlink_reparse_data, path_buffer);
188 
189 		if (!reparse_name_is_valid(size,
190 					   data_offs +
191 					   le16_to_cpu(data->substitute_name_offset),
192 					   le16_to_cpu(data->substitute_name_length)) ||
193 		    !reparse_name_is_valid(size,
194 					   data_offs +
195 					   le16_to_cpu(data->print_name_offset),
196 					   le16_to_cpu(data->print_name_length)))
197 			return false;
198 		break;
199 	}
200 	case IO_REPARSE_TAG_LX_SYMLINK:
201 	{
202 		struct wsl_link_reparse_data *data;
203 
204 		if (!valid_reparse_buffer(ni, reparse_attr, size,
205 					  sizeof(*data)))
206 			return false;
207 
208 		data = (struct wsl_link_reparse_data *)reparse_attr->reparse_data;
209 
210 		if (le16_to_cpu(reparse_attr->reparse_data_length) <= sizeof(data->type) ||
211 		    data->type != cpu_to_le32(2))
212 			return false;
213 		break;
214 	}
215 	case IO_REPARSE_TAG_AF_UNIX:
216 	case IO_REPARSE_TAG_LX_FIFO:
217 	case IO_REPARSE_TAG_LX_CHR:
218 	case IO_REPARSE_TAG_LX_BLK:
219 		if (!valid_reparse_buffer(ni, reparse_attr, size, 0))
220 			return false;
221 		if (le16_to_cpu(reparse_attr->reparse_data_length) ||
222 		    !(ni->flags & FILE_ATTRIBUTE_RECALL_ON_OPEN))
223 			return false;
224 		break;
225 	case IO_REPARSE_TAG_WOF: {
226 		if (!valid_reparse_buffer(ni, reparse_attr, size,
227 					  sizeof(struct wof_reparse_data)))
228 			return false;
229 		break;
230 	}
231 	default:
232 		if (!valid_reparse_buffer(ni, reparse_attr, size, 0))
233 			return false;
234 		break;
235 	}
236 
237 	return true;
238 }
239 
240 static unsigned int ntfs_reparse_tag_mode(__le32 reparse_tag)
241 {
242 	unsigned int mode = 0;
243 
244 	switch (reparse_tag) {
245 	case IO_REPARSE_TAG_MOUNT_POINT:
246 	case IO_REPARSE_TAG_SYMLINK:
247 	case IO_REPARSE_TAG_LX_SYMLINK:
248 		mode = S_IFLNK;
249 		break;
250 	case IO_REPARSE_TAG_AF_UNIX:
251 		mode = S_IFSOCK;
252 		break;
253 	case IO_REPARSE_TAG_LX_FIFO:
254 		mode = S_IFIFO;
255 		break;
256 	case IO_REPARSE_TAG_LX_CHR:
257 		mode = S_IFCHR;
258 		break;
259 	case IO_REPARSE_TAG_LX_BLK:
260 		mode = S_IFBLK;
261 	}
262 
263 	return mode;
264 }
265 
266 /*
267  * Parse reparse point data and initialize its in-memory representation.
268  */
269 int ntfs_parse_reparse(struct ntfs_inode *ni, unsigned int *mode)
270 {
271 	s64 attr_size = 0;
272 	int err = -EINVAL;
273 	unsigned int lth;
274 	struct reparse_point *reparse_attr;
275 
276 	kvfree(ni->target);
277 	ni->target = NULL;
278 	ni->reparse_tag = 0;
279 	ni->reparse_flags = 0;
280 	*mode = 0;
281 
282 	reparse_attr = ntfs_attr_readall(ni, AT_REPARSE_POINT, NULL, 0,
283 					 &attr_size);
284 	if (IS_ERR(reparse_attr)) {
285 		err = PTR_ERR(reparse_attr);
286 		ntfs_error(ni->vol->sb,
287 			   "Failed to read reparse point: %d.", err);
288 		return err;
289 	}
290 	if (!valid_reparse_data(ni, reparse_attr, attr_size)) {
291 		ntfs_error(ni->vol->sb, "Invalid reparse point.");
292 		err = -EFSCORRUPTED;
293 		goto out;
294 	}
295 
296 	switch (reparse_attr->reparse_tag) {
297 	case IO_REPARSE_TAG_MOUNT_POINT:
298 	{
299 		struct mount_point_reparse_data *data =
300 			(struct mount_point_reparse_data *)reparse_attr->reparse_data;
301 		const __le16 *name = (const __le16 *)((u8 *)data->path_buffer +
302 				      le16_to_cpu(data->substitute_name_offset));
303 
304 		err = ntfs_reparse_target_to_nls(ni->vol,
305 						 name,
306 						 le16_to_cpu(data->substitute_name_length),
307 						 &ni->target);
308 		break;
309 	}
310 	case IO_REPARSE_TAG_SYMLINK:
311 	{
312 		struct symlink_reparse_data *data =
313 			(struct symlink_reparse_data *)reparse_attr->reparse_data;
314 		const __le16 *name = (const __le16 *)((u8 *)data->path_buffer +
315 						le16_to_cpu(data->substitute_name_offset));
316 
317 		err = ntfs_reparse_target_to_nls(ni->vol,
318 						 name,
319 						 le16_to_cpu(data->substitute_name_length),
320 						 &ni->target);
321 		if (!err)
322 			ni->reparse_flags = data->flags;
323 		break;
324 	}
325 	case IO_REPARSE_TAG_LX_SYMLINK:
326 	{
327 		struct wsl_link_reparse_data *wsl_link_data =
328 			(struct wsl_link_reparse_data *)reparse_attr->reparse_data;
329 
330 		if (wsl_link_data->type == cpu_to_le32(2)) {
331 			lth = le16_to_cpu(reparse_attr->reparse_data_length) -
332 				  sizeof(wsl_link_data->type);
333 			ni->target = kvzalloc(lth + 1, GFP_NOFS);
334 			if (ni->target) {
335 				memcpy(ni->target, wsl_link_data->link, lth);
336 				ni->target[lth] = 0;
337 				err = 0;
338 			}
339 		}
340 		break;
341 	}
342 	case IO_REPARSE_TAG_WOF:
343 	{
344 #ifdef CONFIG_NTFS_FS_WOF_COMPRESSION
345 		const struct wof_reparse_data *wof_data =
346 			(const struct wof_reparse_data *)reparse_attr->reparse_data;
347 
348 		ni->itype.compressed.block_size_bits = 0;
349 		ni->itype.compressed.block_size = 0;
350 		if (wof_data->version == WOF_CURRENT_VERSION &&
351 		    wof_data->provider == WOF_PROVIDER_FILE &&
352 		    wof_data->provider_version ==
353 			    WOF_PROVIDER_CURRENT_VERSION) {
354 			switch (wof_data->compression_format) {
355 			case WOF_COMPRESSION_XPRESS4K:
356 				ni->itype.compressed.block_size_bits =
357 					12;
358 				break;
359 			case WOF_COMPRESSION_XPRESS8K:
360 				ni->itype.compressed.block_size_bits =
361 					13;
362 				break;
363 			case WOF_COMPRESSION_XPRESS16K:
364 				ni->itype.compressed.block_size_bits =
365 					14;
366 				break;
367 			case WOF_COMPRESSION_LZX:
368 				ni->itype.compressed.block_size_bits =
369 					15;
370 				break;
371 			}
372 		}
373 		if (ni->itype.compressed.block_size_bits)
374 			ni->itype.compressed.block_size =
375 				1
376 				<< ni->itype.compressed.block_size_bits;
377 #endif
378 		NInoSetWofCompressed(ni);
379 		VFS_I(ni)->i_mode &= ~0222;
380 		err = 0;
381 		break;
382 	}
383 	default:
384 		err = 0;
385 	}
386 
387 	if (!err) {
388 		*mode = ntfs_reparse_tag_mode(
389 			reparse_attr->reparse_tag);
390 		ni->reparse_tag = reparse_attr->reparse_tag;
391 	}
392 
393 out:
394 	kvfree(reparse_attr);
395 
396 	return err;
397 }
398 
399 unsigned int ntfs_reparse_tag_dt_types(struct ntfs_volume *vol, unsigned long mref)
400 {
401 	s64 attr_size = 0;
402 	struct reparse_point *reparse_attr;
403 	unsigned int dt_type = DT_UNKNOWN;
404 	struct inode *vi;
405 
406 	vi = ntfs_iget(vol->sb, mref);
407 	if (IS_ERR(vi))
408 		return PTR_ERR(vi);
409 
410 	reparse_attr = (struct reparse_point *)ntfs_attr_readall(NTFS_I(vi),
411 			AT_REPARSE_POINT, NULL, 0, &attr_size);
412 	if (IS_ERR(reparse_attr))
413 		reparse_attr = NULL;
414 
415 	if (reparse_attr && attr_size >= sizeof(*reparse_attr)) {
416 		switch (reparse_attr->reparse_tag) {
417 		case IO_REPARSE_TAG_MOUNT_POINT:
418 		case IO_REPARSE_TAG_SYMLINK:
419 		case IO_REPARSE_TAG_LX_SYMLINK:
420 			dt_type = DT_LNK;
421 			break;
422 		case IO_REPARSE_TAG_AF_UNIX:
423 			dt_type = DT_SOCK;
424 			break;
425 		case IO_REPARSE_TAG_LX_FIFO:
426 			dt_type = DT_FIFO;
427 			break;
428 		case IO_REPARSE_TAG_LX_CHR:
429 			dt_type = DT_CHR;
430 			break;
431 		case IO_REPARSE_TAG_LX_BLK:
432 			dt_type = DT_BLK;
433 		}
434 	}
435 
436 	kvfree(reparse_attr);
437 
438 	iput(vi);
439 	return dt_type;
440 }
441 
442 static bool ntfs_is_drive_letter(const char *target)
443 {
444 	return ((target[0] >= 'A' && target[0] <= 'Z') ||
445 		(target[0] >= 'a' && target[0] <= 'z')) &&
446 		target[1] == ':';
447 }
448 
449 /*
450  * ntfs_translate_symlink_path
451  *
452  * @dentry: dentry of the symlink/junction being resolved
453  * @target: NUL-terminated NLS target string with '\\' already normalized to '/'
454  * @translated: out parameter, set to a newly kmalloc'd relative path on success
455  *
456  * Windows junctions (IO_REPARSE_TAG_MOUNT_POINT) and non-relative symlinks
457  * (IO_REPARSE_TAG_SYMLINK without SYMLINK_FLAG_RELATIVE) store substitute
458  * names such as "/??/C:/foo", "//?/C:/foo", "/foo", or "C:/foo". Linux
459  * cannot continue pathname lookup from those syntaxes, so rewrite them as a
460  * path relative to the symlink's containing directory on this NTFS volume,
461  * anchored at the volume root via "../".
462  *
463  * Note: bind-mounted subtrees of the volume may resolve to unexpected
464  * locations because the computed "../" depth is relative to the NTFS volume
465  * root, not the bind-mounted subtree root.
466  *
467  * Return: 0 on success with *translated set to a newly allocated string the
468  * caller must kfree(); negative errno on failure.
469  */
470 int ntfs_translate_symlink_path(struct dentry *dentry, const char *target,
471 				char **translated)
472 {
473 	char *buf, *link_path, *out, *p;
474 	const char *path, *tail;
475 	unsigned int up_levels = 0;
476 	size_t tail_len, out_len;
477 	int err;
478 
479 	if (!dentry || !target || !translated)
480 		return -EINVAL;
481 
482 	path = target;
483 	/* reject UNC path. */
484 	if (path[0] == '/' && path[1] == '/' &&
485 	    !(path[2] == '?' && path[3] == '/'))
486 		return -EOPNOTSUPP;
487 
488 	/* target starts with "/??/" or "//?/"? */
489 	if ((path[0] == '/' && path[1] == '?' && path[2] == '?' && path[3] == '/') ||
490 	    (path[0] == '/' && path[1] == '/' && path[2] == '?' && path[3] == '/'))
491 		path += 4;
492 
493 	/* target must start with a drive character or '/'. */
494 	if (ntfs_is_drive_letter(path)) {
495 		if (path[2] && path[2] != '/')
496 			return -EOPNOTSUPP;
497 		tail = path + 2;
498 		if (*tail == '/')
499 			tail++;
500 	} else if (*path == '/') {
501 		tail = path + 1;
502 	} else {
503 		return -EOPNOTSUPP;
504 	}
505 
506 	tail_len = strlen(tail);
507 
508 	buf = kmalloc(PATH_MAX, GFP_NOFS);
509 	if (!buf)
510 		return -ENOMEM;
511 
512 	link_path = dentry_path_raw(dentry, buf, PATH_MAX);
513 	if (IS_ERR(link_path)) {
514 		err = PTR_ERR(link_path);
515 		goto out;
516 	}
517 
518 	/* count '/' after the leading slash. */
519 	for (p = link_path + 1; *p; p++)
520 		if (*p == '/')
521 			up_levels++;
522 
523 	/* build "./" + ("../" * up_levels) + tail. */
524 	out_len = 2 + up_levels * 3 + tail_len;
525 	if (out_len >= PATH_MAX) {
526 		err = -ENAMETOOLONG;
527 		goto out;
528 	}
529 
530 	out = kmalloc(out_len + 1, GFP_NOFS);
531 	if (!out) {
532 		err = -ENOMEM;
533 		goto out;
534 	}
535 
536 	memcpy(out, "./", 2);
537 	p = out + 2;
538 	while (up_levels--) {
539 		memcpy(p, "../", 3);
540 		p += 3;
541 	}
542 	memcpy(p, tail, tail_len + 1);
543 
544 	*translated = out;
545 	err = 0;
546 out:
547 	kfree(buf);
548 	return err;
549 }
550 
551 /*
552  * Set the index for new reparse data
553  */
554 static int set_reparse_index(struct ntfs_inode *ni, struct ntfs_index_context *xr,
555 		__le32 reparse_tag)
556 {
557 	struct reparse_index indx;
558 	u64 file_id_cpu;
559 	__le64 file_id;
560 
561 	file_id_cpu = MK_MREF(ni->mft_no, ni->seq_no);
562 	file_id = cpu_to_le64(file_id_cpu);
563 	indx.header.data.vi.data_offset =
564 		cpu_to_le16(sizeof(struct index_entry_header) + sizeof(struct reparse_index_key));
565 	indx.header.data.vi.data_length = 0;
566 	indx.header.data.vi.reservedV = 0;
567 	indx.header.length = cpu_to_le16(sizeof(struct reparse_index));
568 	indx.header.key_length = cpu_to_le16(sizeof(struct reparse_index_key));
569 	indx.header.flags = 0;
570 	indx.header.reserved = 0;
571 	indx.key.reparse_tag = reparse_tag;
572 	/* danger on processors which require proper alignment! */
573 	memcpy(&indx.key.file_id, &file_id, 8);
574 	indx.filling = 0;
575 	ntfs_index_ctx_reinit(xr);
576 
577 	return ntfs_ie_add(xr, (struct index_entry *)&indx);
578 }
579 
580 /*
581  * Remove a reparse data index entry if attribute present
582  */
583 static int remove_reparse_index(struct inode *rp, struct ntfs_index_context *xr,
584 				__le32 *preparse_tag)
585 {
586 	struct reparse_index_key key;
587 	u64 file_id_cpu;
588 	__le64 file_id;
589 	s64 size;
590 	struct ntfs_inode *ni = NTFS_I(rp);
591 	int err = 0, ret = ni->data_size;
592 
593 	if (ni->data_size == 0)
594 		return 0;
595 
596 	/* read the existing reparse_tag */
597 	size = ntfs_inode_attr_pread(rp, 0, 4, (char *)preparse_tag);
598 	if (size != 4)
599 		return -ENODATA;
600 
601 	file_id_cpu = MK_MREF(ni->mft_no, ni->seq_no);
602 	file_id = cpu_to_le64(file_id_cpu);
603 	key.reparse_tag = *preparse_tag;
604 	/* danger on processors which require proper alignment! */
605 	memcpy(&key.file_id, &file_id, 8);
606 	if (!ntfs_index_lookup(&key, sizeof(struct reparse_index_key), xr)) {
607 		err = ntfs_index_rm(xr);
608 		if (err)
609 			ret = err;
610 	}
611 	return ret;
612 }
613 
614 /*
615  * Open the $Extend/$Reparse file and its index
616  */
617 static struct ntfs_index_context *open_reparse_index(struct ntfs_volume *vol)
618 {
619 	struct ntfs_index_context *xr = NULL;
620 	u64 mref;
621 	__le16 *uname;
622 	struct ntfs_name *name = NULL;
623 	int uname_len;
624 	struct inode *vi, *dir_vi;
625 
626 	/* do not use path_name_to inode - could reopen root */
627 	dir_vi = ntfs_iget(vol->sb, FILE_Extend);
628 	if (IS_ERR(dir_vi))
629 		return NULL;
630 
631 	uname_len = ntfs_nlstoucs(vol, "$Reparse", 8, &uname,
632 				  NTFS_MAX_NAME_LEN);
633 	if (uname_len < 0) {
634 		iput(dir_vi);
635 		return NULL;
636 	}
637 
638 	mutex_lock_nested(&NTFS_I(dir_vi)->mrec_lock, NTFS_EXTEND_MUTEX_PARENT);
639 	mref = ntfs_lookup_inode_by_name(NTFS_I(dir_vi), uname, uname_len,
640 					 &name);
641 	mutex_unlock(&NTFS_I(dir_vi)->mrec_lock);
642 	kfree(name);
643 	kmem_cache_free(ntfs_name_cache, uname);
644 	if (IS_ERR_MREF(mref))
645 		goto put_dir_vi;
646 
647 	vi = ntfs_iget(vol->sb, MREF(mref));
648 	if (IS_ERR(vi))
649 		goto put_dir_vi;
650 
651 	xr = ntfs_index_ctx_get(NTFS_I(vi), reparse_index_name, 2);
652 	if (!xr)
653 		iput(vi);
654 put_dir_vi:
655 	iput(dir_vi);
656 	return xr;
657 }
658 
659 
660 /*
661  * Update the reparse data and index
662  *
663  * The reparse data attribute should have been created, and
664  * an existing index is expected if there is an existing value.
665  *
666  */
667 static int update_reparse_data(struct ntfs_inode *ni, struct ntfs_index_context *xr,
668 		char *value, size_t size)
669 {
670 	struct inode *rp_inode;
671 	int err = 0;
672 	s64 written;
673 	int oldsize;
674 	__le32 reparse_tag;
675 	struct ntfs_inode *rp_ni;
676 
677 	rp_inode = ntfs_attr_iget(VFS_I(ni), AT_REPARSE_POINT, AT_UNNAMED, 0);
678 	if (IS_ERR(rp_inode))
679 		return -EINVAL;
680 	rp_ni = NTFS_I(rp_inode);
681 
682 	/* remove the existing reparse data */
683 	oldsize = remove_reparse_index(rp_inode, xr, &reparse_tag);
684 	if (oldsize < 0) {
685 		err = oldsize;
686 		goto put_rp_inode;
687 	}
688 
689 	/* overwrite value if any */
690 	written = ntfs_inode_attr_pwrite(rp_inode, 0, size, value, false);
691 	if (written != size) {
692 		ntfs_error(ni->vol->sb, "Failed to update reparse data\n");
693 		err = -EIO;
694 		goto put_rp_inode;
695 	}
696 
697 	if (set_reparse_index(ni, xr, ((const struct reparse_point *)value)->reparse_tag) &&
698 	    oldsize > 0) {
699 		/*
700 		 * If cannot index, try to remove the reparse
701 		 * data and log the error. There will be an
702 		 * inconsistency if removal fails.
703 		 */
704 		ntfs_attr_rm(rp_ni);
705 		ntfs_error(ni->vol->sb,
706 			   "Failed to index reparse data. Possible corruption.\n");
707 	}
708 
709 	mark_mft_record_dirty(ni);
710 put_rp_inode:
711 	iput(rp_inode);
712 
713 	return err;
714 }
715 
716 /*
717  * Delete a reparse index entry
718  */
719 int ntfs_delete_reparse_index(struct ntfs_inode *ni)
720 {
721 	struct inode *vi;
722 	struct ntfs_index_context *xr;
723 	struct ntfs_inode *xrni;
724 	__le32 reparse_tag;
725 	int err = 0;
726 
727 	if (!(ni->flags & FILE_ATTR_REPARSE_POINT))
728 		return 0;
729 
730 	vi = ntfs_attr_iget(VFS_I(ni), AT_REPARSE_POINT, AT_UNNAMED, 0);
731 	if (IS_ERR(vi))
732 		return PTR_ERR(vi);
733 
734 	/*
735 	 * read the existing reparse data (the tag is enough)
736 	 * and un-index it
737 	 */
738 	xr = open_reparse_index(ni->vol);
739 	if (xr) {
740 		xrni = xr->idx_ni;
741 		mutex_lock_nested(&xrni->mrec_lock, NTFS_EXTEND_MUTEX_PARENT);
742 		err = remove_reparse_index(vi, xr, &reparse_tag);
743 		if (err < 0) {
744 			ntfs_index_ctx_put(xr);
745 			mutex_unlock(&xrni->mrec_lock);
746 			iput(VFS_I(xrni));
747 			goto out;
748 		}
749 		mark_mft_record_dirty(xrni);
750 		ntfs_index_ctx_put(xr);
751 		mutex_unlock(&xrni->mrec_lock);
752 		iput(VFS_I(xrni));
753 	}
754 
755 	ni->flags &= ~FILE_ATTR_REPARSE_POINT;
756 	NInoSetFileNameDirty(ni);
757 	mark_mft_record_dirty(ni);
758 
759 out:
760 	iput(vi);
761 	return err;
762 }
763 
764 /*
765  * Set the reparse data from an extended attribute
766  */
767 static int ntfs_set_ntfs_reparse_data(struct ntfs_inode *ni, char *value, size_t size)
768 {
769 	int err = 0;
770 	struct ntfs_inode *xrni;
771 	struct ntfs_index_context *xr;
772 
773 	if (!ni)
774 		return -EINVAL;
775 
776 	/*
777 	 * reparse data compatibily with EA is not checked
778 	 * any more, it is required by Windows 10, but may
779 	 * lead to problems with earlier versions.
780 	 */
781 	if (valid_reparse_data(ni, (const struct reparse_point *)value, size) == false)
782 		return -EINVAL;
783 
784 	xr = open_reparse_index(ni->vol);
785 	if (!xr)
786 		return -EINVAL;
787 	xrni = xr->idx_ni;
788 
789 	if (!ntfs_attr_exist(ni, AT_REPARSE_POINT, AT_UNNAMED, 0)) {
790 		struct reparse_point rp = {0, };
791 
792 		/*
793 		 * no reparse data attribute : add one,
794 		 * apparently, this does not feed the new value in
795 		 * Note : NTFS version must be >= 3
796 		 */
797 		if (ni->vol->major_ver < 3) {
798 			err = -EOPNOTSUPP;
799 			ntfs_index_ctx_put(xr);
800 			goto out;
801 		}
802 
803 		err = ntfs_attr_add(ni, AT_REPARSE_POINT, AT_UNNAMED, 0, (u8 *)&rp, sizeof(rp));
804 		if (err) {
805 			ntfs_index_ctx_put(xr);
806 			goto out;
807 		}
808 		ni->flags |= FILE_ATTR_REPARSE_POINT;
809 		NInoSetFileNameDirty(ni);
810 		mark_mft_record_dirty(ni);
811 	}
812 
813 	/* update value and index */
814 	mutex_lock_nested(&xrni->mrec_lock, NTFS_EXTEND_MUTEX_PARENT);
815 	err = update_reparse_data(ni, xr, value, size);
816 	if (err) {
817 		ni->flags &= ~FILE_ATTR_REPARSE_POINT;
818 		NInoSetFileNameDirty(ni);
819 		mark_mft_record_dirty(ni);
820 	}
821 	ntfs_index_ctx_put(xr);
822 	mutex_unlock(&xrni->mrec_lock);
823 
824 out:
825 	if (!err)
826 		mark_mft_record_dirty(xrni);
827 	iput(VFS_I(xrni));
828 
829 	return err;
830 }
831 
832 /*
833  * Set reparse data for a WSL type symlink
834  */
835 int ntfs_reparse_set_wsl_symlink(struct ntfs_inode *ni,
836 				 const char *target, int target_len)
837 {
838 	int err = 0;
839 	int reparse_len;
840 	struct reparse_point *reparse;
841 	struct wsl_link_reparse_data *data;
842 
843 	reparse_len = sizeof(struct reparse_point) + sizeof(data->type) +
844 		target_len;
845 	reparse = kvzalloc(reparse_len, GFP_NOFS);
846 	if (!reparse)
847 		return -ENOMEM;
848 
849 	ni->target = kstrdup(target, GFP_NOFS);
850 	if (!ni->target) {
851 		kvfree(reparse);
852 		return -ENOMEM;
853 	}
854 
855 	data = (struct wsl_link_reparse_data *)reparse->reparse_data;
856 	reparse->reparse_tag = IO_REPARSE_TAG_LX_SYMLINK;
857 	reparse->reparse_data_length =
858 		cpu_to_le16(sizeof(data->type) + target_len);
859 	reparse->reserved = 0;
860 	data->type = cpu_to_le32(2);
861 	memcpy(data->link, target, target_len);
862 	err = ntfs_set_ntfs_reparse_data(ni,
863 					 (char *)reparse, reparse_len);
864 	kvfree(reparse);
865 	if (err) {
866 		kfree(ni->target);
867 		ni->target = NULL;
868 	} else {
869 		ni->reparse_tag = IO_REPARSE_TAG_LX_SYMLINK;
870 		ni->reparse_flags = 0;
871 	}
872 	return err;
873 }
874 
875 int ntfs_reparse_set_native_symlink(struct ntfs_inode *ni,
876 				    const char *target, int target_len)
877 {
878 	int err = 0;
879 	bool is_absolute, prt_sub_shared = true;
880 	char *sub_name = NULL;
881 	char *prt_name = NULL;
882 	__le16 *sub_name_utf16 = NULL;
883 	__le16 *prt_name_utf16 = NULL;
884 	int sub_len, prt_len;
885 	int total_data_len, total_reparse_len;
886 	struct reparse_point *reparse = NULL;
887 	struct symlink_reparse_data *data;
888 	int i;
889 
890 	/* Determine if target is absolute (starts with drive letter like C:/ or C:\) */
891 	is_absolute = target_len > 2 &&
892 		ntfs_is_drive_letter(target) &&
893 		(target[2] == '/' || target[2] == '\\');
894 
895 
896 	/* Normalize and prepare NLS paths */
897 	prt_name = kstrdup(target, GFP_NOFS);
898 	if (!prt_name)
899 		return -ENOMEM;
900 
901 	/* Replace '/' with '\' */
902 	for (i = 0; i < target_len; i++) {
903 		if (prt_name[i] == '/')
904 			prt_name[i] = '\\';
905 	}
906 
907 	if (is_absolute) {
908 		/* Prepend '\??\' to Substitutename */
909 		sub_name = kmalloc(target_len + 5, GFP_NOFS);
910 		if (!sub_name) {
911 			err = -ENOMEM;
912 			goto out;
913 		}
914 		snprintf(sub_name, target_len + 5, "\\??\\%s", prt_name);
915 		prt_sub_shared = false;
916 	} else {
917 		/* For relative symlinks (including absolute paths without drive letters),
918 		 * SubstituteName and PrintName are identical.
919 		 */
920 		sub_name = prt_name;
921 	}
922 
923 	/* Convert NLS paths to UTF-16 */
924 	sub_len = ntfs_nlstoucs(ni->vol, sub_name, strlen(sub_name),
925 				&sub_name_utf16, PATH_MAX);
926 	if (sub_len < 0) {
927 		err = sub_len;
928 		goto out;
929 	}
930 
931 	prt_len = ntfs_nlstoucs(ni->vol, prt_name, strlen(prt_name),
932 				&prt_name_utf16, PATH_MAX);
933 	if (prt_len < 0) {
934 		err = prt_len;
935 		goto out;
936 	}
937 
938 	/* Check for buffer size limits */
939 	total_data_len = sizeof(struct symlink_reparse_data) +
940 		(sub_len + prt_len) * sizeof(__le16);
941 	if (total_data_len > 16384) { /* 16KB max reparse tag size */
942 		err = -EFBIG;
943 		goto out;
944 	}
945 
946 	total_reparse_len = sizeof(struct reparse_point) + total_data_len;
947 	reparse = kvzalloc(total_reparse_len, GFP_NOFS);
948 	if (!reparse) {
949 		err = -ENOMEM;
950 		goto out;
951 	}
952 
953 	/* Pack fields in reparse buffer */
954 	reparse->reparse_tag = IO_REPARSE_TAG_SYMLINK;
955 	reparse->reparse_data_length = cpu_to_le16(total_data_len);
956 	reparse->reserved = 0;
957 
958 	data = (struct symlink_reparse_data *)reparse->reparse_data;
959 	data->substitute_name_offset = 0;
960 	data->substitute_name_length = cpu_to_le16(sub_len * sizeof(__le16));
961 	data->print_name_offset = data->substitute_name_length;
962 	data->print_name_length = cpu_to_le16(prt_len * sizeof(__le16));
963 	data->flags = is_absolute ? 0 : cpu_to_le32(SYMLINK_FLAG_RELATIVE);
964 
965 	/* Copy names to path_buffer */
966 	memcpy(data->path_buffer, sub_name_utf16, sub_len * sizeof(__le16));
967 	memcpy(data->path_buffer + sub_len, prt_name_utf16, prt_len * sizeof(__le16));
968 
969 	err = ntfs_set_ntfs_reparse_data(ni, (char *)reparse, total_reparse_len);
970 	if (!err) {
971 		strreplace(sub_name, '\\', '/');
972 		ni->target = sub_name;
973 		sub_name = NULL;
974 		if (prt_sub_shared)
975 			prt_name = NULL;
976 		ni->reparse_tag = IO_REPARSE_TAG_SYMLINK;
977 		ni->reparse_flags = is_absolute ? 0 :
978 			cpu_to_le32(SYMLINK_FLAG_RELATIVE);
979 	}
980 
981 out:
982 	kfree(prt_name);
983 	if (!prt_sub_shared)
984 		kfree(sub_name);
985 	kvfree(sub_name_utf16);
986 	kvfree(prt_name_utf16);
987 	kvfree(reparse);
988 	return err;
989 }
990 
991 /*
992  * Set reparse data for a WSL special file other than a symlink
993  * (socket, fifo, character or block device)
994  */
995 int ntfs_reparse_set_wsl_not_symlink(struct ntfs_inode *ni, mode_t mode)
996 {
997 	int err;
998 	int len;
999 	int reparse_len;
1000 	__le32 reparse_tag;
1001 	struct reparse_point *reparse;
1002 
1003 	len = 0;
1004 	if (S_ISSOCK(mode))
1005 		reparse_tag = IO_REPARSE_TAG_AF_UNIX;
1006 	else if (S_ISFIFO(mode))
1007 		reparse_tag = IO_REPARSE_TAG_LX_FIFO;
1008 	else if (S_ISCHR(mode))
1009 		reparse_tag = IO_REPARSE_TAG_LX_CHR;
1010 	else if (S_ISBLK(mode))
1011 		reparse_tag = IO_REPARSE_TAG_LX_BLK;
1012 	else
1013 		return -EOPNOTSUPP;
1014 
1015 	reparse_len = sizeof(struct reparse_point) + len;
1016 	reparse = kvzalloc(reparse_len, GFP_NOFS);
1017 	if (!reparse)
1018 		err = -ENOMEM;
1019 	else {
1020 		reparse->reparse_tag = reparse_tag;
1021 		reparse->reparse_data_length = cpu_to_le16(len);
1022 		reparse->reserved = cpu_to_le16(0);
1023 		err = ntfs_set_ntfs_reparse_data(ni, (char *)reparse,
1024 						 reparse_len);
1025 		kvfree(reparse);
1026 		if (!err) {
1027 			ni->reparse_tag = reparse_tag;
1028 			ni->reparse_flags = 0;
1029 		}
1030 	}
1031 
1032 	return err;
1033 }
1034