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