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