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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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