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