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