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
reparse_name_is_valid(size_t size,size_t name_off,u16 len)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 */
ntfs_reparse_target_to_nls(struct ntfs_volume * vol,const __le16 * uname,u16 ulen,char ** target)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 */
valid_reparse_buffer(struct ntfs_inode * ni,const struct reparse_point * reparse_attr,size_t size,size_t payload_min_len)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 */
valid_reparse_data(struct ntfs_inode * ni,const struct reparse_point * reparse_attr,size_t size)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
ntfs_reparse_tag_mode(__le32 reparse_tag)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 */
ntfs_parse_reparse(struct ntfs_inode * ni,unsigned int * mode)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
ntfs_reparse_tag_dt_types(struct ntfs_volume * vol,unsigned long mref)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 DT_UNKNOWN;
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
ntfs_is_drive_letter(const char * target)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 */
ntfs_translate_symlink_path(struct dentry * dentry,const char * target,char ** translated)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 */
set_reparse_index(struct ntfs_inode * ni,struct ntfs_index_context * xr,__le32 reparse_tag)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 */
remove_reparse_index(struct inode * rp,struct ntfs_index_context * xr,__le32 * preparse_tag)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 */
open_reparse_index(struct ntfs_volume * vol)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 */
update_reparse_data(struct ntfs_inode * ni,struct ntfs_index_context * xr,char * value,size_t size)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 err = set_reparse_index(ni, xr,
698 ((const struct reparse_point *)value)->reparse_tag);
699 if (err && oldsize > 0) {
700 /*
701 * If cannot index, try to remove the reparse
702 * data and log the error. There will be an
703 * inconsistency if removal fails.
704 */
705 ntfs_attr_rm(rp_ni);
706 ntfs_error(ni->vol->sb,
707 "Failed to index reparse data. Possible corruption.\n");
708 }
709
710 mark_mft_record_dirty(ni);
711 put_rp_inode:
712 iput(rp_inode);
713
714 return err;
715 }
716
717 /*
718 * Delete a reparse index entry
719 */
ntfs_delete_reparse_index(struct ntfs_inode * ni)720 int ntfs_delete_reparse_index(struct ntfs_inode *ni)
721 {
722 struct inode *vi;
723 struct ntfs_index_context *xr;
724 struct ntfs_inode *xrni;
725 __le32 reparse_tag;
726 int err = 0;
727
728 if (!(ni->flags & FILE_ATTR_REPARSE_POINT))
729 return 0;
730
731 vi = ntfs_attr_iget(VFS_I(ni), AT_REPARSE_POINT, AT_UNNAMED, 0);
732 if (IS_ERR(vi))
733 return PTR_ERR(vi);
734
735 /*
736 * read the existing reparse data (the tag is enough)
737 * and un-index it
738 */
739 xr = open_reparse_index(ni->vol);
740 if (xr) {
741 xrni = xr->idx_ni;
742 mutex_lock_nested(&xrni->mrec_lock, NTFS_EXTEND_MUTEX_PARENT);
743 err = remove_reparse_index(vi, xr, &reparse_tag);
744 if (err < 0) {
745 ntfs_index_ctx_put(xr);
746 mutex_unlock(&xrni->mrec_lock);
747 iput(VFS_I(xrni));
748 goto out;
749 }
750 mark_mft_record_dirty(xrni);
751 ntfs_index_ctx_put(xr);
752 mutex_unlock(&xrni->mrec_lock);
753 iput(VFS_I(xrni));
754 }
755
756 ni->flags &= ~FILE_ATTR_REPARSE_POINT;
757 NInoSetFileNameDirty(ni);
758 mark_mft_record_dirty(ni);
759
760 out:
761 iput(vi);
762 return err;
763 }
764
765 /*
766 * Set the reparse data from an extended attribute
767 */
ntfs_set_ntfs_reparse_data(struct ntfs_inode * ni,char * value,size_t size)768 static int ntfs_set_ntfs_reparse_data(struct ntfs_inode *ni, char *value, size_t size)
769 {
770 int err = 0;
771 struct ntfs_inode *xrni;
772 struct ntfs_index_context *xr;
773
774 if (!ni)
775 return -EINVAL;
776
777 /*
778 * reparse data compatibily with EA is not checked
779 * any more, it is required by Windows 10, but may
780 * lead to problems with earlier versions.
781 */
782 if (valid_reparse_data(ni, (const struct reparse_point *)value, size) == false)
783 return -EINVAL;
784
785 xr = open_reparse_index(ni->vol);
786 if (!xr)
787 return -EINVAL;
788 xrni = xr->idx_ni;
789
790 if (!ntfs_attr_exist(ni, AT_REPARSE_POINT, AT_UNNAMED, 0)) {
791 struct reparse_point rp = {0, };
792
793 /*
794 * no reparse data attribute : add one,
795 * apparently, this does not feed the new value in
796 * Note : NTFS version must be >= 3
797 */
798 if (ni->vol->major_ver < 3) {
799 err = -EOPNOTSUPP;
800 ntfs_index_ctx_put(xr);
801 goto out;
802 }
803
804 err = ntfs_attr_add(ni, AT_REPARSE_POINT, AT_UNNAMED, 0, (u8 *)&rp, sizeof(rp));
805 if (err) {
806 ntfs_index_ctx_put(xr);
807 goto out;
808 }
809 ni->flags |= FILE_ATTR_REPARSE_POINT;
810 NInoSetFileNameDirty(ni);
811 mark_mft_record_dirty(ni);
812 }
813
814 /* update value and index */
815 mutex_lock_nested(&xrni->mrec_lock, NTFS_EXTEND_MUTEX_PARENT);
816 err = update_reparse_data(ni, xr, value, size);
817 if (err) {
818 ni->flags &= ~FILE_ATTR_REPARSE_POINT;
819 NInoSetFileNameDirty(ni);
820 mark_mft_record_dirty(ni);
821 }
822 ntfs_index_ctx_put(xr);
823 mutex_unlock(&xrni->mrec_lock);
824
825 out:
826 if (!err)
827 mark_mft_record_dirty(xrni);
828 iput(VFS_I(xrni));
829
830 return err;
831 }
832
833 /*
834 * Set reparse data for a WSL type symlink
835 */
ntfs_reparse_set_wsl_symlink(struct ntfs_inode * ni,const char * target,int target_len)836 int ntfs_reparse_set_wsl_symlink(struct ntfs_inode *ni,
837 const char *target, int target_len)
838 {
839 int err = 0;
840 int reparse_len;
841 struct reparse_point *reparse;
842 struct wsl_link_reparse_data *data;
843
844 reparse_len = sizeof(struct reparse_point) + sizeof(data->type) +
845 target_len;
846 reparse = kvzalloc(reparse_len, GFP_NOFS);
847 if (!reparse)
848 return -ENOMEM;
849
850 ni->target = kstrdup(target, GFP_NOFS);
851 if (!ni->target) {
852 kvfree(reparse);
853 return -ENOMEM;
854 }
855
856 data = (struct wsl_link_reparse_data *)reparse->reparse_data;
857 reparse->reparse_tag = IO_REPARSE_TAG_LX_SYMLINK;
858 reparse->reparse_data_length =
859 cpu_to_le16(sizeof(data->type) + target_len);
860 reparse->reserved = 0;
861 data->type = cpu_to_le32(2);
862 memcpy(data->link, target, target_len);
863 err = ntfs_set_ntfs_reparse_data(ni,
864 (char *)reparse, reparse_len);
865 kvfree(reparse);
866 if (err) {
867 kfree(ni->target);
868 ni->target = NULL;
869 } else {
870 ni->reparse_tag = IO_REPARSE_TAG_LX_SYMLINK;
871 ni->reparse_flags = 0;
872 }
873 return err;
874 }
875
ntfs_reparse_set_native_symlink(struct ntfs_inode * ni,const char * target,int target_len)876 int ntfs_reparse_set_native_symlink(struct ntfs_inode *ni,
877 const char *target, int target_len)
878 {
879 int err = 0;
880 bool is_absolute, prt_sub_shared = true;
881 char *sub_name = NULL;
882 char *prt_name = NULL;
883 __le16 *sub_name_utf16 = NULL;
884 __le16 *prt_name_utf16 = NULL;
885 int sub_len, prt_len;
886 int total_data_len, total_reparse_len;
887 struct reparse_point *reparse = NULL;
888 struct symlink_reparse_data *data;
889 int i;
890
891 /* Determine if target is absolute (starts with drive letter like C:/ or C:\) */
892 is_absolute = target_len > 2 &&
893 ntfs_is_drive_letter(target) &&
894 (target[2] == '/' || target[2] == '\\');
895
896
897 /* Normalize and prepare NLS paths */
898 prt_name = kstrdup(target, GFP_NOFS);
899 if (!prt_name)
900 return -ENOMEM;
901
902 /* Replace '/' with '\' */
903 for (i = 0; i < target_len; i++) {
904 if (prt_name[i] == '/')
905 prt_name[i] = '\\';
906 }
907
908 if (is_absolute) {
909 /* Prepend '\??\' to Substitutename */
910 sub_name = kmalloc(target_len + 5, GFP_NOFS);
911 if (!sub_name) {
912 err = -ENOMEM;
913 goto out;
914 }
915 snprintf(sub_name, target_len + 5, "\\??\\%s", prt_name);
916 prt_sub_shared = false;
917 } else {
918 /* For relative symlinks (including absolute paths without drive letters),
919 * SubstituteName and PrintName are identical.
920 */
921 sub_name = prt_name;
922 }
923
924 /* Convert NLS paths to UTF-16 */
925 sub_len = ntfs_nlstoucs(ni->vol, sub_name, strlen(sub_name),
926 &sub_name_utf16, PATH_MAX);
927 if (sub_len < 0) {
928 err = sub_len;
929 goto out;
930 }
931
932 prt_len = ntfs_nlstoucs(ni->vol, prt_name, strlen(prt_name),
933 &prt_name_utf16, PATH_MAX);
934 if (prt_len < 0) {
935 err = prt_len;
936 goto out;
937 }
938
939 /* Check for buffer size limits */
940 total_data_len = sizeof(struct symlink_reparse_data) +
941 (sub_len + prt_len) * sizeof(__le16);
942 if (total_data_len > 16384) { /* 16KB max reparse tag size */
943 err = -EFBIG;
944 goto out;
945 }
946
947 total_reparse_len = sizeof(struct reparse_point) + total_data_len;
948 reparse = kvzalloc(total_reparse_len, GFP_NOFS);
949 if (!reparse) {
950 err = -ENOMEM;
951 goto out;
952 }
953
954 /* Pack fields in reparse buffer */
955 reparse->reparse_tag = IO_REPARSE_TAG_SYMLINK;
956 reparse->reparse_data_length = cpu_to_le16(total_data_len);
957 reparse->reserved = 0;
958
959 data = (struct symlink_reparse_data *)reparse->reparse_data;
960 data->substitute_name_offset = 0;
961 data->substitute_name_length = cpu_to_le16(sub_len * sizeof(__le16));
962 data->print_name_offset = data->substitute_name_length;
963 data->print_name_length = cpu_to_le16(prt_len * sizeof(__le16));
964 data->flags = is_absolute ? 0 : cpu_to_le32(SYMLINK_FLAG_RELATIVE);
965
966 /* Copy names to path_buffer */
967 memcpy(data->path_buffer, sub_name_utf16, sub_len * sizeof(__le16));
968 memcpy(data->path_buffer + sub_len, prt_name_utf16, prt_len * sizeof(__le16));
969
970 err = ntfs_set_ntfs_reparse_data(ni, (char *)reparse, total_reparse_len);
971 if (!err) {
972 strreplace(sub_name, '\\', '/');
973 ni->target = sub_name;
974 sub_name = NULL;
975 if (prt_sub_shared)
976 prt_name = NULL;
977 ni->reparse_tag = IO_REPARSE_TAG_SYMLINK;
978 ni->reparse_flags = is_absolute ? 0 :
979 cpu_to_le32(SYMLINK_FLAG_RELATIVE);
980 }
981
982 out:
983 kfree(prt_name);
984 if (!prt_sub_shared)
985 kfree(sub_name);
986 kvfree(sub_name_utf16);
987 kvfree(prt_name_utf16);
988 kvfree(reparse);
989 return err;
990 }
991
992 /*
993 * Set reparse data for a WSL special file other than a symlink
994 * (socket, fifo, character or block device)
995 */
ntfs_reparse_set_wsl_not_symlink(struct ntfs_inode * ni,mode_t mode)996 int ntfs_reparse_set_wsl_not_symlink(struct ntfs_inode *ni, mode_t mode)
997 {
998 int err;
999 int len;
1000 int reparse_len;
1001 __le32 reparse_tag;
1002 struct reparse_point *reparse;
1003
1004 len = 0;
1005 if (S_ISSOCK(mode))
1006 reparse_tag = IO_REPARSE_TAG_AF_UNIX;
1007 else if (S_ISFIFO(mode))
1008 reparse_tag = IO_REPARSE_TAG_LX_FIFO;
1009 else if (S_ISCHR(mode))
1010 reparse_tag = IO_REPARSE_TAG_LX_CHR;
1011 else if (S_ISBLK(mode))
1012 reparse_tag = IO_REPARSE_TAG_LX_BLK;
1013 else
1014 return -EOPNOTSUPP;
1015
1016 reparse_len = sizeof(struct reparse_point) + len;
1017 reparse = kvzalloc(reparse_len, GFP_NOFS);
1018 if (!reparse)
1019 err = -ENOMEM;
1020 else {
1021 reparse->reparse_tag = reparse_tag;
1022 reparse->reparse_data_length = cpu_to_le16(len);
1023 reparse->reserved = cpu_to_le16(0);
1024 err = ntfs_set_ntfs_reparse_data(ni, (char *)reparse,
1025 reparse_len);
1026 kvfree(reparse);
1027 if (!err) {
1028 ni->reparse_tag = reparse_tag;
1029 ni->reparse_flags = 0;
1030 }
1031 }
1032
1033 return err;
1034 }
1035