1 /**
2 * reparse.c - Processing of reparse points
3 *
4 * This module is part of ntfs-3g library
5 *
6 * Copyright (c) 2008-2021 Jean-Pierre Andre
7 *
8 * This program/include file is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License as published
10 * by the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program/include file is distributed in the hope that it will be
14 * useful, but WITHOUT ANY WARRANTY; without even the implied warranty
15 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program (in the main directory of the NTFS-3G
20 * distribution in the file COPYING); if not, write to the Free Software
21 * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 */
23
24 #ifdef HAVE_CONFIG_H
25 #include "config.h"
26 #endif
27
28 #ifdef HAVE_STDLIB_H
29 #include <stdlib.h>
30 #endif
31 #ifdef HAVE_ERRNO_H
32 #include <errno.h>
33 #endif
34 #ifdef HAVE_STRING_H
35 #include <string.h>
36 #endif
37 #ifdef HAVE_SYS_STAT_H
38 #include <sys/stat.h>
39 #endif
40 #ifdef MAJOR_IN_MKDEV
41 #include <sys/mkdev.h>
42 #endif
43 #ifdef MAJOR_IN_SYSMACROS
44 #include <sys/sysmacros.h>
45 #endif
46
47 #include "compat.h"
48 #include "types.h"
49 #include "debug.h"
50 #include "layout.h"
51 #include "attrib.h"
52 #include "inode.h"
53 #include "dir.h"
54 #include "volume.h"
55 #include "mft.h"
56 #include "index.h"
57 #include "lcnalloc.h"
58 #include "logging.h"
59 #include "misc.h"
60 #include "reparse.h"
61 #include "xattrs.h"
62 #include "ea.h"
63
64 struct MOUNT_POINT_REPARSE_DATA { /* reparse data for junctions */
65 le16 subst_name_offset;
66 le16 subst_name_length;
67 le16 print_name_offset;
68 le16 print_name_length;
69 char path_buffer[0]; /* above data assume this is char array */
70 } ;
71
72 struct SYMLINK_REPARSE_DATA { /* reparse data for symlinks */
73 le16 subst_name_offset;
74 le16 subst_name_length;
75 le16 print_name_offset;
76 le16 print_name_length;
77 le32 flags; /* 1 for full target, otherwise 0 */
78 char path_buffer[0]; /* above data assume this is char array */
79 } ;
80
81 struct WSL_LINK_REPARSE_DATA {
82 le32 type;
83 char link[0];
84 } ;
85
86 struct REPARSE_INDEX { /* index entry in $Extend/$Reparse */
87 INDEX_ENTRY_HEADER header;
88 REPARSE_INDEX_KEY key;
89 le32 filling;
90 } ;
91
92 static const ntfschar dir_junction_head[] = {
93 const_cpu_to_le16('\\'),
94 const_cpu_to_le16('?'),
95 const_cpu_to_le16('?'),
96 const_cpu_to_le16('\\')
97 } ;
98
99 static const ntfschar vol_junction_head[] = {
100 const_cpu_to_le16('\\'),
101 const_cpu_to_le16('?'),
102 const_cpu_to_le16('?'),
103 const_cpu_to_le16('\\'),
104 const_cpu_to_le16('V'),
105 const_cpu_to_le16('o'),
106 const_cpu_to_le16('l'),
107 const_cpu_to_le16('u'),
108 const_cpu_to_le16('m'),
109 const_cpu_to_le16('e'),
110 const_cpu_to_le16('{'),
111 } ;
112
113 static ntfschar reparse_index_name[] = { const_cpu_to_le16('$'),
114 const_cpu_to_le16('R') };
115
116 static const char mappingdir[] = ".NTFS-3G/";
117
118 /*
119 * Fix a file name with doubtful case in some directory index
120 * and return the name with the casing used in directory.
121 *
122 * Should only be used to translate paths stored with case insensitivity
123 * (such as directory junctions) when no case conflict is expected.
124 * If there some ambiguity, the name which collates first is returned.
125 *
126 * The name is converted to upper case and searched the usual way.
127 * The collation rules for file names are such that we should get the
128 * first candidate if any.
129 */
130
ntfs_fix_file_name(ntfs_inode * dir_ni,ntfschar * uname,int uname_len)131 static u64 ntfs_fix_file_name(ntfs_inode *dir_ni, ntfschar *uname,
132 int uname_len)
133 {
134 ntfs_volume *vol = dir_ni->vol;
135 ntfs_index_context *icx;
136 u64 mref;
137 le64 lemref;
138 int lkup;
139 int olderrno;
140 int i;
141 u32 cpuchar;
142 INDEX_ENTRY *entry;
143 FILE_NAME_ATTR *found;
144 struct {
145 FILE_NAME_ATTR attr;
146 ntfschar file_name[NTFS_MAX_NAME_LEN + 1];
147 } find;
148
149 mref = (u64)-1; /* default return (not found) */
150 icx = ntfs_index_ctx_get(dir_ni, NTFS_INDEX_I30, 4);
151 if (icx) {
152 if (uname_len > NTFS_MAX_NAME_LEN)
153 uname_len = NTFS_MAX_NAME_LEN;
154 find.attr.file_name_length = uname_len;
155 for (i=0; i<uname_len; i++) {
156 cpuchar = le16_to_cpu(uname[i]);
157 /*
158 * We need upper or lower value, whichever is smaller,
159 * but we can only convert to upper case, so we
160 * will fail when searching for an upper case char
161 * whose lower case is smaller (such as umlauted Y)
162 */
163 if ((cpuchar < vol->upcase_len)
164 && (le16_to_cpu(vol->upcase[cpuchar]) < cpuchar))
165 find.attr.file_name[i] = vol->upcase[cpuchar];
166 else
167 find.attr.file_name[i] = uname[i];
168 }
169 olderrno = errno;
170 lkup = ntfs_index_lookup((char*)&find, uname_len, icx);
171 if (errno == ENOENT)
172 errno = olderrno;
173 /*
174 * We generally only get the first matching candidate,
175 * so we still have to check whether this is a real match
176 */
177 if (icx->entry && (icx->entry->ie_flags & INDEX_ENTRY_END))
178 /* get next entry if reaching end of block */
179 entry = ntfs_index_next(icx->entry, icx);
180 else
181 entry = icx->entry;
182 if (entry) {
183 found = &entry->key.file_name;
184 if (lkup
185 && ntfs_names_are_equal(find.attr.file_name,
186 find.attr.file_name_length,
187 found->file_name, found->file_name_length,
188 IGNORE_CASE,
189 vol->upcase, vol->upcase_len))
190 lkup = 0;
191 if (!lkup) {
192 /*
193 * name found :
194 * fix original name and return inode
195 */
196 lemref = entry->indexed_file;
197 mref = le64_to_cpu(lemref);
198 if (NVolCaseSensitive(vol) || !vol->locase) {
199 for (i=0; i<found->file_name_length; i++)
200 uname[i] = found->file_name[i];
201 } else {
202 for (i=0; i<found->file_name_length; i++)
203 uname[i] = vol->locase[le16_to_cpu(found->file_name[i])];
204 }
205 }
206 }
207 ntfs_index_ctx_put(icx);
208 }
209 return (mref);
210 }
211
212 /*
213 * Search for a directory junction or a symbolic link
214 * along the target path, with target defined as a full absolute path
215 *
216 * Returns the path translated to a Linux path
217 * or NULL if the path is not valid
218 */
219
search_absolute(ntfs_volume * vol,ntfschar * path,int count,BOOL isdir)220 static char *search_absolute(ntfs_volume *vol, ntfschar *path,
221 int count, BOOL isdir)
222 {
223 ntfs_inode *ni;
224 u64 inum;
225 char *target;
226 int start;
227 int len;
228
229 target = (char*)NULL; /* default return */
230 ni = ntfs_inode_open(vol, (MFT_REF)FILE_root);
231 if (ni) {
232 start = 0;
233 /*
234 * Examine and translate the path, until we reach either
235 * - the end,
236 * - an unknown item
237 * - a non-directory
238 * - another reparse point,
239 * A reparse point is not dereferenced, it will be
240 * examined later when the translated path is dereferenced,
241 * however the final part of the path will not be adjusted
242 * to correct case.
243 */
244 do {
245 len = 0;
246 while (((start + len) < count)
247 && (path[start + len] != const_cpu_to_le16('\\')))
248 len++;
249 inum = ntfs_fix_file_name(ni, &path[start], len);
250 ntfs_inode_close(ni);
251 ni = (ntfs_inode*)NULL;
252 if (inum != (u64)-1) {
253 inum = MREF(inum);
254 ni = ntfs_inode_open(vol, inum);
255 start += len;
256 if (start < count)
257 path[start++] = const_cpu_to_le16('/');
258 }
259 } while (ni
260 && (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)
261 && !(ni->flags & FILE_ATTR_REPARSE_POINT)
262 && (start < count));
263 if (ni
264 && ((ni->mrec->flags & MFT_RECORD_IS_DIRECTORY ? isdir : !isdir)
265 || (ni->flags & FILE_ATTR_REPARSE_POINT)))
266 if (ntfs_ucstombs(path, count, &target, 0) < 0) {
267 if (target) {
268 free(target);
269 target = (char*)NULL;
270 }
271 }
272 if (ni)
273 ntfs_inode_close(ni);
274 }
275 return (target);
276 }
277
278 /*
279 * Search for a symbolic link along the target path,
280 * with the target defined as a relative path
281 *
282 * Note : the path used to access the current inode, may be
283 * different from the one implied in the target definition,
284 * when an inode has names in several directories.
285 *
286 * Returns the path translated to a Linux path
287 * or NULL if the path is not valid
288 */
289
search_relative(ntfs_inode * ni,ntfschar * path,int count)290 static char *search_relative(ntfs_inode *ni, ntfschar *path, int count)
291 {
292 char *target = (char*)NULL;
293 ntfs_inode *curni;
294 ntfs_inode *newni;
295 u64 inum;
296 int pos;
297 int lth;
298 BOOL ok;
299 BOOL morelinks;
300 int max = 32; /* safety */
301
302 pos = 0;
303 ok = TRUE;
304 morelinks = FALSE;
305 curni = ntfs_dir_parent_inode(ni);
306 /*
307 * Examine and translate the path, until we reach either
308 * - the end,
309 * - an unknown item
310 * - a non-directory
311 * - another reparse point,
312 * A reparse point is not dereferenced, it will be
313 * examined later when the translated path is dereferenced,
314 * however the final part of the path will not be adjusted
315 * to correct case.
316 */
317 while (curni && ok && !morelinks && (pos < (count - 1)) && --max) {
318 if ((count >= (pos + 2))
319 && (path[pos] == const_cpu_to_le16('.'))
320 && (path[pos+1] == const_cpu_to_le16('\\'))) {
321 path[pos+1] = const_cpu_to_le16('/');
322 pos += 2;
323 } else {
324 if ((count >= (pos + 3))
325 && (path[pos] == const_cpu_to_le16('.'))
326 &&(path[pos+1] == const_cpu_to_le16('.'))
327 && (path[pos+2] == const_cpu_to_le16('\\'))) {
328 path[pos+2] = const_cpu_to_le16('/');
329 pos += 3;
330 newni = ntfs_dir_parent_inode(curni);
331 if (curni != ni)
332 ntfs_inode_close(curni);
333 curni = newni;
334 if (!curni)
335 ok = FALSE;
336 } else {
337 lth = 0;
338 while (((pos + lth) < count)
339 && (path[pos + lth] != const_cpu_to_le16('\\')))
340 lth++;
341 if (lth > 0)
342 inum = ntfs_fix_file_name(curni,&path[pos],lth);
343 else
344 inum = (u64)-1;
345 if (!lth
346 || ((curni != ni)
347 && ntfs_inode_close(curni))
348 || (inum == (u64)-1))
349 ok = FALSE;
350 else {
351 curni = ntfs_inode_open(ni->vol, MREF(inum));
352 if (!curni)
353 ok = FALSE;
354 else {
355 if (curni->flags & FILE_ATTR_REPARSE_POINT)
356 morelinks = TRUE;
357 if (ok && ((pos + lth) < count)) {
358 path[pos + lth] = const_cpu_to_le16('/');
359 pos += lth + 1;
360 if (morelinks
361 && ntfs_inode_close(curni))
362 ok = FALSE;
363 } else {
364 pos += lth;
365 if (!morelinks
366 && (ni->mrec->flags ^ curni->mrec->flags)
367 & MFT_RECORD_IS_DIRECTORY)
368 ok = FALSE;
369 if (ntfs_inode_close(curni))
370 ok = FALSE;
371 }
372 }
373 }
374 }
375 }
376 }
377
378 if (ok && (ntfs_ucstombs(path, count, &target, 0) < 0)) {
379 free(target); // needed ?
380 target = (char*)NULL;
381 }
382 return (target);
383 }
384
385 /*
386 * Check whether a drive letter has been defined in .NTFS-3G
387 *
388 * Returns 1 if found,
389 * 0 if not found,
390 * -1 if there was an error (described by errno)
391 */
392
ntfs_drive_letter(ntfs_volume * vol,ntfschar letter)393 static int ntfs_drive_letter(ntfs_volume *vol, ntfschar letter)
394 {
395 char defines[NTFS_MAX_NAME_LEN + 5];
396 char *drive;
397 int ret;
398 int sz;
399 int olderrno;
400 ntfs_inode *ni;
401
402 ret = -1;
403 drive = (char*)NULL;
404 sz = ntfs_ucstombs(&letter, 1, &drive, 0);
405 if (sz > 0) {
406 strcpy(defines,mappingdir);
407 if ((*drive >= 'a') && (*drive <= 'z'))
408 *drive += 'A' - 'a';
409 strcat(defines,drive);
410 strcat(defines,":");
411 olderrno = errno;
412 ni = ntfs_pathname_to_inode(vol, NULL, defines);
413 if (ni && !ntfs_inode_close(ni))
414 ret = 1;
415 else
416 if (errno == ENOENT) {
417 ret = 0;
418 /* avoid errno pollution */
419 errno = olderrno;
420 }
421 }
422 if (drive)
423 free(drive);
424 return (ret);
425 }
426
427 /*
428 * Check whether reparse data describes a valid wsl special file
429 * which is either a socket, a fifo, or a character or block device
430 *
431 * Return zero if valid, otherwise returns a negative error code
432 */
433
ntfs_reparse_check_wsl(ntfs_inode * ni,const REPARSE_POINT * reparse)434 int ntfs_reparse_check_wsl(ntfs_inode *ni, const REPARSE_POINT *reparse)
435 {
436 int res;
437
438 res = -EOPNOTSUPP;
439 switch (reparse->reparse_tag) {
440 case IO_REPARSE_TAG_AF_UNIX :
441 case IO_REPARSE_TAG_LX_FIFO :
442 case IO_REPARSE_TAG_LX_CHR :
443 case IO_REPARSE_TAG_LX_BLK :
444 if (!reparse->reparse_data_length
445 && (ni->flags & FILE_ATTRIBUTE_RECALL_ON_OPEN))
446 res = 0;
447 break;
448 default :
449 break;
450 }
451 if (res)
452 errno = EOPNOTSUPP;
453 return (res);
454 }
455
456 /*
457 * Do some sanity checks on reparse data
458 *
459 * Microsoft reparse points have an 8-byte header whereas
460 * non-Microsoft reparse points have a 24-byte header. In each case,
461 * 'reparse_data_length' must equal the number of non-header bytes.
462 *
463 * If the reparse data looks like a junction point or symbolic
464 * link, more checks can be done.
465 *
466 */
467
valid_reparse_data(ntfs_inode * ni,const REPARSE_POINT * reparse_attr,size_t size)468 static BOOL valid_reparse_data(ntfs_inode *ni,
469 const REPARSE_POINT *reparse_attr, size_t size)
470 {
471 BOOL ok;
472 unsigned int offs;
473 unsigned int lth;
474 const struct MOUNT_POINT_REPARSE_DATA *mount_point_data;
475 const struct SYMLINK_REPARSE_DATA *symlink_data;
476 const struct WSL_LINK_REPARSE_DATA *wsl_reparse_data;
477
478 ok = ni && reparse_attr
479 && (size >= sizeof(REPARSE_POINT))
480 && (reparse_attr->reparse_tag != IO_REPARSE_TAG_RESERVED_ZERO)
481 && (((size_t)le16_to_cpu(reparse_attr->reparse_data_length)
482 + sizeof(REPARSE_POINT)
483 + ((reparse_attr->reparse_tag &
484 IO_REPARSE_TAG_IS_MICROSOFT) ? 0 : sizeof(GUID))) == size);
485 if (ok) {
486 switch (reparse_attr->reparse_tag) {
487 case IO_REPARSE_TAG_MOUNT_POINT :
488 if (size < sizeof(REPARSE_POINT) +
489 sizeof(struct MOUNT_POINT_REPARSE_DATA)) {
490 ok = FALSE;
491 break;
492 }
493 mount_point_data = (const struct MOUNT_POINT_REPARSE_DATA*)
494 reparse_attr->reparse_data;
495 offs = le16_to_cpu(mount_point_data->subst_name_offset);
496 lth = le16_to_cpu(mount_point_data->subst_name_length);
497 /* consistency checks */
498 if (!(ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)
499 || ((size_t)((sizeof(REPARSE_POINT)
500 + sizeof(struct MOUNT_POINT_REPARSE_DATA)
501 + offs + lth)) > size))
502 ok = FALSE;
503 break;
504 case IO_REPARSE_TAG_SYMLINK :
505 if (size < sizeof(REPARSE_POINT) +
506 sizeof(struct SYMLINK_REPARSE_DATA)) {
507 ok = FALSE;
508 break;
509 }
510 symlink_data = (const struct SYMLINK_REPARSE_DATA*)
511 reparse_attr->reparse_data;
512 offs = le16_to_cpu(symlink_data->subst_name_offset);
513 lth = le16_to_cpu(symlink_data->subst_name_length);
514 if ((size_t)((sizeof(REPARSE_POINT)
515 + sizeof(struct SYMLINK_REPARSE_DATA)
516 + offs + lth)) > size)
517 ok = FALSE;
518 break;
519 case IO_REPARSE_TAG_LX_SYMLINK :
520 wsl_reparse_data = (const struct WSL_LINK_REPARSE_DATA*)
521 reparse_attr->reparse_data;
522 if ((le16_to_cpu(reparse_attr->reparse_data_length)
523 <= sizeof(wsl_reparse_data->type))
524 || (wsl_reparse_data->type != const_cpu_to_le32(2)))
525 ok = FALSE;
526 break;
527 case IO_REPARSE_TAG_AF_UNIX :
528 case IO_REPARSE_TAG_LX_FIFO :
529 case IO_REPARSE_TAG_LX_CHR :
530 case IO_REPARSE_TAG_LX_BLK :
531 if (reparse_attr->reparse_data_length
532 || !(ni->flags & FILE_ATTRIBUTE_RECALL_ON_OPEN))
533 ok = FALSE;
534 break;
535 default :
536 break;
537 }
538 }
539 if (!ok)
540 errno = EINVAL;
541 return (ok);
542 }
543
544 /*
545 * Check and translate the target of a junction point or
546 * a full absolute symbolic link.
547 *
548 * A full target definition begins with "\??\" or "\\?\"
549 *
550 * The fully defined target is redefined as a relative link,
551 * - either to the target if found on the same device.
552 * - or into the /.NTFS-3G directory for the user to define
553 * In the first situation, the target is translated to case-sensitive path.
554 *
555 * returns the target converted to a relative symlink
556 * or NULL if there were some problem, as described by errno
557 */
558
ntfs_get_fulllink(ntfs_volume * vol,ntfschar * junction,int count,const char * mnt_point,BOOL isdir)559 static char *ntfs_get_fulllink(ntfs_volume *vol, ntfschar *junction,
560 int count, const char *mnt_point, BOOL isdir)
561 {
562 char *target;
563 char *fulltarget;
564 int sz;
565 char *q;
566 enum { DIR_JUNCTION, VOL_JUNCTION, NO_JUNCTION } kind;
567
568 target = (char*)NULL;
569 fulltarget = (char*)NULL;
570 /*
571 * For a valid directory junction we want \??\x:\
572 * where \ is an individual char and x a non-null char
573 */
574 if ((count >= 7)
575 && !memcmp(junction,dir_junction_head,8)
576 && junction[4]
577 && (junction[5] == const_cpu_to_le16(':'))
578 && (junction[6] == const_cpu_to_le16('\\')))
579 kind = DIR_JUNCTION;
580 else
581 /*
582 * For a valid volume junction we want \\?\Volume{
583 * and a final \ (where \ is an individual char)
584 */
585 if ((count >= 12)
586 && !memcmp(junction,vol_junction_head,22)
587 && (junction[count-1] == const_cpu_to_le16('\\')))
588 kind = VOL_JUNCTION;
589 else
590 kind = NO_JUNCTION;
591 /*
592 * Directory junction with an explicit path and
593 * no specific definition for the drive letter :
594 * try to interpret as a target on the same volume
595 */
596 if ((kind == DIR_JUNCTION)
597 && (count >= 7)
598 && junction[7]
599 && !ntfs_drive_letter(vol, junction[4])) {
600 target = search_absolute(vol,&junction[7],count - 7, isdir);
601 if (target) {
602 fulltarget = (char*)ntfs_malloc(strlen(mnt_point)
603 + strlen(target) + 2);
604 if (fulltarget) {
605 strcpy(fulltarget,mnt_point);
606 strcat(fulltarget,"/");
607 strcat(fulltarget,target);
608 }
609 free(target);
610 }
611 }
612 /*
613 * Volume junctions or directory junctions with
614 * target not found on current volume :
615 * link to /.NTFS-3G/target which the user can
616 * define as a symbolic link to the real target
617 */
618 if (((kind == DIR_JUNCTION) && !fulltarget)
619 || (kind == VOL_JUNCTION)) {
620 sz = ntfs_ucstombs(&junction[4],
621 (kind == VOL_JUNCTION ? count - 5 : count - 4),
622 &target, 0);
623 if ((sz > 0) && target) {
624 /* reverse slashes */
625 for (q=target; *q; q++)
626 if (*q == '\\')
627 *q = '/';
628 /* force uppercase drive letter */
629 if ((target[1] == ':')
630 && (target[0] >= 'a')
631 && (target[0] <= 'z'))
632 target[0] += 'A' - 'a';
633 fulltarget = (char*)ntfs_malloc(strlen(mnt_point)
634 + sizeof(mappingdir) + strlen(target) + 1);
635 if (fulltarget) {
636 strcpy(fulltarget,mnt_point);
637 strcat(fulltarget,"/");
638 strcat(fulltarget,mappingdir);
639 strcat(fulltarget,target);
640 }
641 }
642 if (target)
643 free(target);
644 }
645 return (fulltarget);
646 }
647
648 /*
649 * Check and translate the target of an absolute symbolic link.
650 *
651 * An absolute target definition begins with "\" or "x:\"
652 *
653 * The absolute target is redefined as a relative link,
654 * - either to the target if found on the same device.
655 * - or into the /.NTFS-3G directory for the user to define
656 * In the first situation, the target is translated to case-sensitive path.
657 *
658 * returns the target converted to a relative symlink
659 * or NULL if there were some problem, as described by errno
660 */
661
ntfs_get_abslink(ntfs_volume * vol,ntfschar * junction,int count,const char * mnt_point,BOOL isdir)662 char *ntfs_get_abslink(ntfs_volume *vol, ntfschar *junction, int count,
663 const char *mnt_point __attribute__((unused)),
664 BOOL isdir)
665 {
666 char *target;
667 char *fulltarget;
668 int sz;
669 char *q;
670 enum { FULL_PATH, ABS_PATH, REJECTED_PATH } kind;
671
672 target = (char*)NULL;
673 fulltarget = (char*)NULL;
674 /*
675 * For a full valid path we want x:\
676 * where \ is an individual char and x a non-null char
677 */
678 if ((count >= 3)
679 && junction[0]
680 && (junction[1] == const_cpu_to_le16(':'))
681 && (junction[2] == const_cpu_to_le16('\\')))
682 kind = FULL_PATH;
683 else
684 /*
685 * For an absolute path we want an initial \
686 */
687 if ((count >= 0)
688 && (junction[0] == const_cpu_to_le16('\\')))
689 kind = ABS_PATH;
690 else
691 kind = REJECTED_PATH;
692 /*
693 * Full path, with a drive letter and
694 * no specific definition for the drive letter :
695 * try to interpret as a target on the same volume.
696 * Do the same for an abs path with no drive letter.
697 */
698 if (((kind == FULL_PATH)
699 && (count >= 3)
700 && junction[3]
701 && !ntfs_drive_letter(vol, junction[0]))
702 || (kind == ABS_PATH)) {
703 if (kind == ABS_PATH)
704 target = search_absolute(vol, &junction[1],
705 count - 1, isdir);
706 else
707 target = search_absolute(vol, &junction[3],
708 count - 3, isdir);
709 if (target) {
710 fulltarget = (char*)ntfs_malloc(
711 strlen(vol->abs_mnt_point)
712 + strlen(target) + 2);
713 if (fulltarget) {
714 strcpy(fulltarget,vol->abs_mnt_point);
715 strcat(fulltarget,"/");
716 strcat(fulltarget,target);
717 }
718 free(target);
719 }
720 }
721 /*
722 * full path with target not found on current volume :
723 * link to /.NTFS-3G/target which the user can
724 * define as a symbolic link to the real target
725 */
726 if ((kind == FULL_PATH) && !fulltarget) {
727 sz = ntfs_ucstombs(&junction[0],
728 count,&target, 0);
729 if ((sz > 0) && target) {
730 /* reverse slashes */
731 for (q=target; *q; q++)
732 if (*q == '\\')
733 *q = '/';
734 /* force uppercase drive letter */
735 if ((target[1] == ':')
736 && (target[0] >= 'a')
737 && (target[0] <= 'z'))
738 target[0] += 'A' - 'a';
739 fulltarget = (char*)ntfs_malloc(
740 strlen(vol->abs_mnt_point)
741 + sizeof(mappingdir) + strlen(target) + 1);
742 if (fulltarget) {
743 strcpy(fulltarget,vol->abs_mnt_point);
744 strcat(fulltarget,"/");
745 strcat(fulltarget,mappingdir);
746 strcat(fulltarget,target);
747 }
748 }
749 if (target)
750 free(target);
751 }
752 return (fulltarget);
753 }
754
755 /*
756 * Check and translate the target of a relative symbolic link.
757 *
758 * A relative target definition does not begin with "\"
759 *
760 * The original definition of relative target is kept, it is just
761 * translated to a case-sensitive path.
762 *
763 * returns the target converted to a relative symlink
764 * or NULL if there were some problem, as described by errno
765 */
766
ntfs_get_rellink(ntfs_inode * ni,ntfschar * junction,int count)767 static char *ntfs_get_rellink(ntfs_inode *ni, ntfschar *junction, int count)
768 {
769 char *target;
770
771 target = search_relative(ni,junction,count);
772 return (target);
773 }
774
775 /*
776 * Get the target for a junction point or symbolic link
777 * Should only be called for files or directories with reparse data
778 *
779 * returns the target converted to a relative path, or NULL
780 * if some error occurred, as described by errno
781 * errno is EOPNOTSUPP if the reparse point is not a valid
782 * symbolic link or directory junction
783 */
784
ntfs_make_symlink(ntfs_inode * ni,const char * mnt_point)785 char *ntfs_make_symlink(ntfs_inode *ni, const char *mnt_point)
786 {
787 s64 attr_size = 0;
788 char *target;
789 unsigned int offs;
790 unsigned int lth;
791 ntfs_volume *vol;
792 REPARSE_POINT *reparse_attr;
793 struct MOUNT_POINT_REPARSE_DATA *mount_point_data;
794 struct SYMLINK_REPARSE_DATA *symlink_data;
795 struct WSL_LINK_REPARSE_DATA *wsl_link_data;
796 enum { FULL_TARGET, ABS_TARGET, REL_TARGET } kind;
797 ntfschar *p;
798 BOOL bad;
799 BOOL isdir;
800
801 target = (char*)NULL;
802 bad = TRUE;
803 isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)
804 != const_cpu_to_le16(0);
805 vol = ni->vol;
806 reparse_attr = (REPARSE_POINT*)ntfs_attr_readall(ni,
807 AT_REPARSE_POINT,(ntfschar*)NULL, 0, &attr_size);
808 if (reparse_attr && attr_size
809 && valid_reparse_data(ni, reparse_attr, attr_size)) {
810 switch (reparse_attr->reparse_tag) {
811 case IO_REPARSE_TAG_MOUNT_POINT :
812 mount_point_data = (struct MOUNT_POINT_REPARSE_DATA*)
813 reparse_attr->reparse_data;
814 offs = le16_to_cpu(mount_point_data->subst_name_offset);
815 lth = le16_to_cpu(mount_point_data->subst_name_length);
816 /* reparse data consistency has been checked */
817 target = ntfs_get_fulllink(vol,
818 (ntfschar*)&mount_point_data->path_buffer[offs],
819 lth/2, mnt_point, isdir);
820 if (target)
821 bad = FALSE;
822 break;
823 case IO_REPARSE_TAG_SYMLINK :
824 symlink_data = (struct SYMLINK_REPARSE_DATA*)
825 reparse_attr->reparse_data;
826 offs = le16_to_cpu(symlink_data->subst_name_offset);
827 lth = le16_to_cpu(symlink_data->subst_name_length);
828 p = (ntfschar*)&symlink_data->path_buffer[offs];
829 /*
830 * Predetermine the kind of target,
831 * the called function has to make a full check
832 */
833 if (*p++ == const_cpu_to_le16('\\')) {
834 if ((*p == const_cpu_to_le16('?'))
835 || (*p == const_cpu_to_le16('\\')))
836 kind = FULL_TARGET;
837 else
838 kind = ABS_TARGET;
839 } else
840 if (*p == const_cpu_to_le16(':'))
841 kind = ABS_TARGET;
842 else
843 kind = REL_TARGET;
844 p--;
845 /* reparse data consistency has been checked */
846 switch (kind) {
847 case FULL_TARGET :
848 if (!(symlink_data->flags
849 & const_cpu_to_le32(1))) {
850 target = ntfs_get_fulllink(vol,
851 p, lth/2,
852 mnt_point, isdir);
853 if (target)
854 bad = FALSE;
855 }
856 break;
857 case ABS_TARGET :
858 if (symlink_data->flags
859 & const_cpu_to_le32(1)) {
860 target = ntfs_get_abslink(vol,
861 p, lth/2,
862 mnt_point, isdir);
863 if (target)
864 bad = FALSE;
865 }
866 break;
867 case REL_TARGET :
868 if (symlink_data->flags
869 & const_cpu_to_le32(1)) {
870 target = ntfs_get_rellink(ni,
871 p, lth/2);
872 if (target)
873 bad = FALSE;
874 }
875 break;
876 }
877 break;
878 case IO_REPARSE_TAG_LX_SYMLINK :
879 wsl_link_data = (struct WSL_LINK_REPARSE_DATA*)
880 reparse_attr->reparse_data;
881 if (wsl_link_data->type == const_cpu_to_le32(2)) {
882 lth = le16_to_cpu(
883 reparse_attr->reparse_data_length)
884 - sizeof(wsl_link_data->type);
885 target = (char*)ntfs_malloc(lth + 1);
886 if (target) {
887 memcpy(target, wsl_link_data->link,
888 lth);
889 target[lth] = 0;
890 bad = FALSE;
891 }
892 }
893 break;
894 }
895 free(reparse_attr);
896 }
897 if (bad)
898 errno = EOPNOTSUPP;
899 return (target);
900 }
901
902 /*
903 * Check whether a reparse point looks like a junction point
904 * or a symbolic link.
905 * Should only be called for files or directories with reparse data
906 *
907 * The validity of the target is not checked.
908 */
909
ntfs_possible_symlink(ntfs_inode * ni)910 BOOL ntfs_possible_symlink(ntfs_inode *ni)
911 {
912 s64 attr_size = 0;
913 REPARSE_POINT *reparse_attr;
914 BOOL possible;
915
916 possible = FALSE;
917 reparse_attr = (REPARSE_POINT*)ntfs_attr_readall(ni,
918 AT_REPARSE_POINT,(ntfschar*)NULL, 0, &attr_size);
919 if (reparse_attr && attr_size) {
920 switch (reparse_attr->reparse_tag) {
921 case IO_REPARSE_TAG_MOUNT_POINT :
922 case IO_REPARSE_TAG_SYMLINK :
923 case IO_REPARSE_TAG_LX_SYMLINK :
924 possible = TRUE;
925 default : ;
926 }
927 free(reparse_attr);
928 }
929 return (possible);
930 }
931
932
933 /*
934 * Set the index for new reparse data
935 *
936 * Returns 0 if success
937 * -1 if failure, explained by errno
938 */
939
set_reparse_index(ntfs_inode * ni,ntfs_index_context * xr,le32 reparse_tag)940 static int set_reparse_index(ntfs_inode *ni, ntfs_index_context *xr,
941 le32 reparse_tag)
942 {
943 struct REPARSE_INDEX indx;
944 u64 file_id_cpu;
945 le64 file_id;
946 le16 seqn;
947
948 seqn = ni->mrec->sequence_number;
949 file_id_cpu = MK_MREF(ni->mft_no,le16_to_cpu(seqn));
950 file_id = cpu_to_le64(file_id_cpu);
951 indx.header.data_offset = const_cpu_to_le16(
952 sizeof(INDEX_ENTRY_HEADER)
953 + sizeof(REPARSE_INDEX_KEY));
954 indx.header.data_length = const_cpu_to_le16(0);
955 indx.header.reservedV = const_cpu_to_le32(0);
956 indx.header.length = const_cpu_to_le16(
957 sizeof(struct REPARSE_INDEX));
958 indx.header.key_length = const_cpu_to_le16(
959 sizeof(REPARSE_INDEX_KEY));
960 indx.header.flags = const_cpu_to_le16(0);
961 indx.header.reserved = const_cpu_to_le16(0);
962 indx.key.reparse_tag = reparse_tag;
963 /* danger on processors which require proper alignment ! */
964 memcpy(&indx.key.file_id, &file_id, 8);
965 indx.filling = const_cpu_to_le32(0);
966 ntfs_index_ctx_reinit(xr);
967 return (ntfs_ie_add(xr,(INDEX_ENTRY*)&indx));
968 }
969
970
971 /*
972 * Remove a reparse data index entry if attribute present
973 *
974 * Returns the size of existing reparse data
975 * (the existing reparse tag is returned)
976 * -1 if failure, explained by errno
977 */
978
remove_reparse_index(ntfs_attr * na,ntfs_index_context * xr,le32 * preparse_tag)979 static int remove_reparse_index(ntfs_attr *na, ntfs_index_context *xr,
980 le32 *preparse_tag)
981 {
982 REPARSE_INDEX_KEY key;
983 u64 file_id_cpu;
984 le64 file_id;
985 s64 size;
986 le16 seqn;
987 int ret;
988
989 ret = na->data_size;
990 if (ret) {
991 /* read the existing reparse_tag */
992 size = ntfs_attr_pread(na, 0, 4, preparse_tag);
993 if (size == 4) {
994 seqn = na->ni->mrec->sequence_number;
995 file_id_cpu = MK_MREF(na->ni->mft_no,le16_to_cpu(seqn));
996 file_id = cpu_to_le64(file_id_cpu);
997 key.reparse_tag = *preparse_tag;
998 /* danger on processors which require proper alignment ! */
999 memcpy(&key.file_id, &file_id, 8);
1000 if (!ntfs_index_lookup(&key, sizeof(REPARSE_INDEX_KEY), xr)
1001 && ntfs_index_rm(xr))
1002 ret = -1;
1003 } else {
1004 ret = -1;
1005 errno = ENODATA;
1006 }
1007 }
1008 return (ret);
1009 }
1010
1011 /*
1012 * Open the $Extend/$Reparse file and its index
1013 *
1014 * Return the index context if opened
1015 * or NULL if an error occurred (errno tells why)
1016 *
1017 * The index has to be freed and inode closed when not needed any more.
1018 */
1019
open_reparse_index(ntfs_volume * vol)1020 static ntfs_index_context *open_reparse_index(ntfs_volume *vol)
1021 {
1022 u64 inum;
1023 ntfs_inode *ni;
1024 ntfs_inode *dir_ni;
1025 ntfs_index_context *xr;
1026
1027 /* do not use path_name_to inode - could reopen root */
1028 dir_ni = ntfs_inode_open(vol, FILE_Extend);
1029 ni = (ntfs_inode*)NULL;
1030 if (dir_ni) {
1031 inum = ntfs_inode_lookup_by_mbsname(dir_ni,"$Reparse");
1032 if (inum != (u64)-1)
1033 ni = ntfs_inode_open(vol, inum);
1034 ntfs_inode_close(dir_ni);
1035 }
1036 if (ni) {
1037 xr = ntfs_index_ctx_get(ni, reparse_index_name, 2);
1038 if (!xr) {
1039 ntfs_inode_close(ni);
1040 }
1041 } else
1042 xr = (ntfs_index_context*)NULL;
1043 return (xr);
1044 }
1045
1046
1047 /*
1048 * Update the reparse data and index
1049 *
1050 * The reparse data attribute should have been created, and
1051 * an existing index is expected if there is an existing value.
1052 *
1053 * Returns 0 if success
1054 * -1 if failure, explained by errno
1055 * If could not remove the existing index, nothing is done,
1056 * If could not write the new data, no index entry is inserted
1057 * If failed to insert the index, data is removed
1058 */
1059
update_reparse_data(ntfs_inode * ni,ntfs_index_context * xr,const char * value,size_t size)1060 static int update_reparse_data(ntfs_inode *ni, ntfs_index_context *xr,
1061 const char *value, size_t size)
1062 {
1063 int res;
1064 int written;
1065 int oldsize;
1066 ntfs_attr *na;
1067 le32 reparse_tag;
1068
1069 res = 0;
1070 na = ntfs_attr_open(ni, AT_REPARSE_POINT, AT_UNNAMED, 0);
1071 if (na) {
1072 /* remove the existing reparse data */
1073 oldsize = remove_reparse_index(na,xr,&reparse_tag);
1074 if (oldsize < 0)
1075 res = -1;
1076 else {
1077 /* resize attribute */
1078 res = ntfs_attr_truncate(na, (s64)size);
1079 /* overwrite value if any */
1080 if (!res && value) {
1081 written = (int)ntfs_attr_pwrite(na,
1082 (s64)0, (s64)size, value);
1083 if (written != (s64)size) {
1084 ntfs_log_error("Failed to update "
1085 "reparse data\n");
1086 errno = EIO;
1087 res = -1;
1088 }
1089 }
1090 if (!res
1091 && set_reparse_index(ni,xr,
1092 ((const REPARSE_POINT*)value)->reparse_tag)
1093 && (oldsize > 0)) {
1094 /*
1095 * If cannot index, try to remove the reparse
1096 * data and log the error. There will be an
1097 * inconsistency if removal fails.
1098 */
1099 ntfs_attr_rm(na);
1100 ntfs_log_error("Failed to index reparse data."
1101 " Possible corruption.\n");
1102 }
1103 }
1104 ntfs_attr_close(na);
1105 NInoSetDirty(ni);
1106 } else
1107 res = -1;
1108 return (res);
1109 }
1110
1111
1112 /*
1113 * Delete a reparse index entry
1114 *
1115 * Returns 0 if success
1116 * -1 if failure, explained by errno
1117 */
1118
ntfs_delete_reparse_index(ntfs_inode * ni)1119 int ntfs_delete_reparse_index(ntfs_inode *ni)
1120 {
1121 ntfs_index_context *xr;
1122 ntfs_inode *xrni;
1123 ntfs_attr *na;
1124 le32 reparse_tag;
1125 int res;
1126
1127 res = 0;
1128 na = ntfs_attr_open(ni, AT_REPARSE_POINT, AT_UNNAMED, 0);
1129 if (na) {
1130 /*
1131 * read the existing reparse data (the tag is enough)
1132 * and un-index it
1133 */
1134 xr = open_reparse_index(ni->vol);
1135 if (xr) {
1136 if (remove_reparse_index(na,xr,&reparse_tag) < 0)
1137 res = -1;
1138 xrni = xr->ni;
1139 ntfs_index_entry_mark_dirty(xr);
1140 NInoSetDirty(xrni);
1141 ntfs_index_ctx_put(xr);
1142 ntfs_inode_close(xrni);
1143 }
1144 ntfs_attr_close(na);
1145 }
1146 return (res);
1147 }
1148
1149
1150 /*
1151 * Get the ntfs reparse data into an extended attribute
1152 *
1153 * Returns the reparse data size
1154 * and the buffer is updated if it is long enough
1155 */
1156
ntfs_get_ntfs_reparse_data(ntfs_inode * ni,char * value,size_t size)1157 int ntfs_get_ntfs_reparse_data(ntfs_inode *ni, char *value, size_t size)
1158 {
1159 REPARSE_POINT *reparse_attr;
1160 s64 attr_size;
1161
1162 attr_size = 0; /* default to no data and no error */
1163 if (ni) {
1164 if (ni->flags & FILE_ATTR_REPARSE_POINT) {
1165 reparse_attr = (REPARSE_POINT*)ntfs_attr_readall(ni,
1166 AT_REPARSE_POINT,(ntfschar*)NULL, 0, &attr_size);
1167 if (reparse_attr) {
1168 if (attr_size <= (s64)size) {
1169 if (value)
1170 memcpy(value,reparse_attr,
1171 attr_size);
1172 else
1173 errno = EINVAL;
1174 }
1175 free(reparse_attr);
1176 }
1177 } else
1178 errno = ENODATA;
1179 }
1180 return (attr_size ? (int)attr_size : -errno);
1181 }
1182
1183 /*
1184 * Set the reparse data from an extended attribute
1185 *
1186 * Warning : the new data is not checked
1187 *
1188 * Returns 0, or -1 if there is a problem
1189 */
1190
ntfs_set_ntfs_reparse_data(ntfs_inode * ni,const char * value,size_t size,int flags)1191 int ntfs_set_ntfs_reparse_data(ntfs_inode *ni,
1192 const char *value, size_t size, int flags)
1193 {
1194 int res;
1195 u8 dummy;
1196 ntfs_inode *xrni;
1197 ntfs_index_context *xr;
1198
1199 res = 0;
1200 /*
1201 * reparse data compatibily with EA is not checked
1202 * any more, it is required by Windows 10, but may
1203 * lead to problems with earlier versions.
1204 */
1205 if (ni && valid_reparse_data(ni, (const REPARSE_POINT*)value, size)) {
1206 xr = open_reparse_index(ni->vol);
1207 if (xr) {
1208 if (!ntfs_attr_exist(ni,AT_REPARSE_POINT,
1209 AT_UNNAMED,0)) {
1210 if (!(flags & XATTR_REPLACE)) {
1211 /*
1212 * no reparse data attribute : add one,
1213 * apparently, this does not feed the new value in
1214 * Note : NTFS version must be >= 3
1215 */
1216 if (ni->vol->major_ver >= 3) {
1217 res = ntfs_attr_add(ni,
1218 AT_REPARSE_POINT,
1219 AT_UNNAMED,0,&dummy,
1220 (s64)0);
1221 if (!res) {
1222 ni->flags |=
1223 FILE_ATTR_REPARSE_POINT;
1224 NInoFileNameSetDirty(ni);
1225 }
1226 NInoSetDirty(ni);
1227 } else {
1228 errno = EOPNOTSUPP;
1229 res = -1;
1230 }
1231 } else {
1232 errno = ENODATA;
1233 res = -1;
1234 }
1235 } else {
1236 if (flags & XATTR_CREATE) {
1237 errno = EEXIST;
1238 res = -1;
1239 }
1240 }
1241 if (!res) {
1242 /* update value and index */
1243 res = update_reparse_data(ni,xr,value,size);
1244 }
1245 xrni = xr->ni;
1246 ntfs_index_entry_mark_dirty(xr);
1247 NInoSetDirty(xrni);
1248 ntfs_index_ctx_put(xr);
1249 ntfs_inode_close(xrni);
1250 } else {
1251 res = -1;
1252 }
1253 } else {
1254 errno = EINVAL;
1255 res = -1;
1256 }
1257 return (res ? -1 : 0);
1258 }
1259
1260 /*
1261 * Remove the reparse data
1262 *
1263 * Returns 0, or -1 if there is a problem
1264 */
1265
ntfs_remove_ntfs_reparse_data(ntfs_inode * ni)1266 int ntfs_remove_ntfs_reparse_data(ntfs_inode *ni)
1267 {
1268 int res;
1269 int olderrno;
1270 ntfs_attr *na;
1271 ntfs_inode *xrni;
1272 ntfs_index_context *xr;
1273 le32 reparse_tag;
1274
1275 res = 0;
1276 if (ni) {
1277 /*
1278 * open and delete the reparse data
1279 */
1280 na = ntfs_attr_open(ni, AT_REPARSE_POINT,
1281 AT_UNNAMED,0);
1282 if (na) {
1283 /* first remove index (reparse data needed) */
1284 xr = open_reparse_index(ni->vol);
1285 if (xr) {
1286 if (remove_reparse_index(na,xr,
1287 &reparse_tag) < 0) {
1288 res = -1;
1289 } else {
1290 /* now remove attribute */
1291 res = ntfs_attr_rm(na);
1292 if (!res) {
1293 ni->flags &=
1294 ~FILE_ATTR_REPARSE_POINT;
1295 NInoFileNameSetDirty(ni);
1296 } else {
1297 /*
1298 * If we could not remove the
1299 * attribute, try to restore the
1300 * index and log the error. There
1301 * will be an inconsistency if
1302 * the reindexing fails.
1303 */
1304 set_reparse_index(ni, xr,
1305 reparse_tag);
1306 ntfs_log_error(
1307 "Failed to remove reparse data."
1308 " Possible corruption.\n");
1309 }
1310 }
1311 xrni = xr->ni;
1312 ntfs_index_entry_mark_dirty(xr);
1313 NInoSetDirty(xrni);
1314 ntfs_index_ctx_put(xr);
1315 ntfs_inode_close(xrni);
1316 }
1317 olderrno = errno;
1318 ntfs_attr_close(na);
1319 /* avoid errno pollution */
1320 if (errno == ENOENT)
1321 errno = olderrno;
1322 } else {
1323 errno = ENODATA;
1324 res = -1;
1325 }
1326 NInoSetDirty(ni);
1327 } else {
1328 errno = EINVAL;
1329 res = -1;
1330 }
1331 return (res ? -1 : 0);
1332 }
1333
1334 /*
1335 * Set reparse data for a WSL type symlink
1336 */
1337
ntfs_reparse_set_wsl_symlink(ntfs_inode * ni,const ntfschar * target,int target_len)1338 int ntfs_reparse_set_wsl_symlink(ntfs_inode *ni,
1339 const ntfschar *target, int target_len)
1340 {
1341 int res;
1342 int len;
1343 int reparse_len;
1344 char *utarget;
1345 REPARSE_POINT *reparse;
1346 struct WSL_LINK_REPARSE_DATA *data;
1347
1348 res = -1;
1349 utarget = (char*)NULL;
1350 len = ntfs_ucstombs(target, target_len, &utarget, 0);
1351 if (len > 0) {
1352 reparse_len = sizeof(REPARSE_POINT) + sizeof(data->type) + len;
1353 reparse = (REPARSE_POINT*)malloc(reparse_len);
1354 if (reparse) {
1355 data = (struct WSL_LINK_REPARSE_DATA*)
1356 reparse->reparse_data;
1357 reparse->reparse_tag = IO_REPARSE_TAG_LX_SYMLINK;
1358 reparse->reparse_data_length
1359 = cpu_to_le16(sizeof(data->type) + len);
1360 reparse->reserved = const_cpu_to_le16(0);
1361 data->type = const_cpu_to_le32(2);
1362 memcpy(data->link, utarget, len);
1363 res = ntfs_set_ntfs_reparse_data(ni,
1364 (char*)reparse, reparse_len, 0);
1365 free(reparse);
1366 }
1367 }
1368 free(utarget);
1369 return (res);
1370 }
1371
1372 /*
1373 * Set reparse data for a WSL special file other than a symlink
1374 * (socket, fifo, character or block device)
1375 */
1376
ntfs_reparse_set_wsl_not_symlink(ntfs_inode * ni,mode_t mode)1377 int ntfs_reparse_set_wsl_not_symlink(ntfs_inode *ni, mode_t mode)
1378 {
1379 int res;
1380 int len;
1381 int reparse_len;
1382 le32 reparse_tag;
1383 REPARSE_POINT *reparse;
1384
1385 res = -1;
1386 len = 0;
1387 switch (mode) {
1388 case S_IFSOCK :
1389 reparse_tag = IO_REPARSE_TAG_AF_UNIX;
1390 break;
1391 case S_IFIFO :
1392 reparse_tag = IO_REPARSE_TAG_LX_FIFO;
1393 break;
1394 case S_IFCHR :
1395 reparse_tag = IO_REPARSE_TAG_LX_CHR;
1396 break;
1397 case S_IFBLK :
1398 reparse_tag = IO_REPARSE_TAG_LX_BLK;
1399 break;
1400 default :
1401 len = -1;
1402 errno = EOPNOTSUPP;
1403 break;
1404 }
1405 if (len >= 0) {
1406 reparse_len = sizeof(REPARSE_POINT) + len;
1407 reparse = (REPARSE_POINT*)malloc(reparse_len);
1408 if (reparse) {
1409 reparse->reparse_tag = reparse_tag;
1410 reparse->reparse_data_length = cpu_to_le16(len);
1411 reparse->reserved = const_cpu_to_le16(0);
1412 res = ntfs_set_ntfs_reparse_data(ni,
1413 (char*)reparse, reparse_len, 0);
1414 free(reparse);
1415 }
1416 }
1417 return (res);
1418 }
1419
1420
1421 /*
1422 * Get the reparse data into a buffer
1423 *
1424 * Returns the buffer if the reparse data exists and is valid
1425 * NULL otherwise (with errno set according to the cause).
1426 * When a buffer is returned, it has to be freed by caller.
1427 */
1428
ntfs_get_reparse_point(ntfs_inode * ni)1429 REPARSE_POINT *ntfs_get_reparse_point(ntfs_inode *ni)
1430 {
1431 s64 attr_size = 0;
1432 REPARSE_POINT *reparse_attr;
1433
1434 reparse_attr = (REPARSE_POINT*)NULL;
1435 if (ni) {
1436 reparse_attr = (REPARSE_POINT*)ntfs_attr_readall(ni,
1437 AT_REPARSE_POINT,(ntfschar*)NULL, 0, &attr_size);
1438 if (reparse_attr
1439 && !valid_reparse_data(ni, reparse_attr, attr_size)) {
1440 free(reparse_attr);
1441 reparse_attr = (REPARSE_POINT*)NULL;
1442 errno = EINVAL;
1443 }
1444 } else
1445 errno = EINVAL;
1446 return (reparse_attr);
1447 }
1448