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