1 /** 2 * object_id.c - Processing of object ids 3 * 4 * This module is part of ntfs-3g library 5 * 6 * Copyright (c) 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 "object_id.h" 59 #include "logging.h" 60 #include "misc.h" 61 62 /* 63 * Endianness considerations 64 * 65 * According to RFC 4122, GUIDs should be printed with the most 66 * significant byte first, and the six fields be compared individually 67 * for ordering. RFC 4122 does not define the internal representation. 68 * 69 * Here we always copy disk images with no endianness change, 70 * and, for indexing, GUIDs are compared as if they were a sequence 71 * of four unsigned 32 bit integers. 72 * 73 * --------------------- begin from RFC 4122 ---------------------- 74 * Consider each field of the UUID to be an unsigned integer as shown 75 * in the table in section Section 4.1.2. Then, to compare a pair of 76 * UUIDs, arithmetically compare the corresponding fields from each 77 * UUID in order of significance and according to their data type. 78 * Two UUIDs are equal if and only if all the corresponding fields 79 * are equal. 80 * 81 * UUIDs, as defined in this document, can also be ordered 82 * lexicographically. For a pair of UUIDs, the first one follows the 83 * second if the most significant field in which the UUIDs differ is 84 * greater for the first UUID. The second precedes the first if the 85 * most significant field in which the UUIDs differ is greater for 86 * the second UUID. 87 * 88 * The fields are encoded as 16 octets, with the sizes and order of the 89 * fields defined above, and with each field encoded with the Most 90 * Significant Byte first (known as network byte order). Note that the 91 * field names, particularly for multiplexed fields, follow historical 92 * practice. 93 * 94 * 0 1 2 3 95 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 96 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 97 * | time_low | 98 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 99 * | time_mid | time_hi_and_version | 100 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 101 * |clk_seq_hi_res | clk_seq_low | node (0-1) | 102 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 103 * | node (2-5) | 104 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 105 * 106 * ---------------------- end from RFC 4122 ----------------------- 107 */ 108 109 typedef struct { 110 union { 111 /* alignment may be needed to evaluate collations */ 112 u32 alignment; 113 GUID guid; 114 } object_id; 115 } OBJECT_ID_INDEX_KEY; 116 117 typedef struct { 118 le64 file_id; 119 GUID birth_volume_id; 120 GUID birth_object_id; 121 GUID domain_id; 122 } OBJECT_ID_INDEX_DATA; // known as OBJ_ID_INDEX_DATA 123 124 struct OBJECT_ID_INDEX { /* index entry in $Extend/$ObjId */ 125 INDEX_ENTRY_HEADER header; 126 OBJECT_ID_INDEX_KEY key; 127 OBJECT_ID_INDEX_DATA data; 128 } ; 129 130 static ntfschar objid_index_name[] = { const_cpu_to_le16('$'), 131 const_cpu_to_le16('O') }; 132 #ifdef HAVE_SETXATTR /* extended attributes interface required */ 133 134 /* 135 * Set the index for a new object id 136 * 137 * Returns 0 if success 138 * -1 if failure, explained by errno 139 */ 140 141 static int set_object_id_index(ntfs_inode *ni, ntfs_index_context *xo, 142 const OBJECT_ID_ATTR *object_id) 143 { 144 struct OBJECT_ID_INDEX indx; 145 u64 file_id_cpu; 146 le64 file_id; 147 le16 seqn; 148 149 seqn = ni->mrec->sequence_number; 150 file_id_cpu = MK_MREF(ni->mft_no,le16_to_cpu(seqn)); 151 file_id = cpu_to_le64(file_id_cpu); 152 indx.header.data_offset = const_cpu_to_le16( 153 sizeof(INDEX_ENTRY_HEADER) 154 + sizeof(OBJECT_ID_INDEX_KEY)); 155 indx.header.data_length = const_cpu_to_le16( 156 sizeof(OBJECT_ID_INDEX_DATA)); 157 indx.header.reservedV = const_cpu_to_le32(0); 158 indx.header.length = const_cpu_to_le16( 159 sizeof(struct OBJECT_ID_INDEX)); 160 indx.header.key_length = const_cpu_to_le16( 161 sizeof(OBJECT_ID_INDEX_KEY)); 162 indx.header.flags = const_cpu_to_le16(0); 163 indx.header.reserved = const_cpu_to_le16(0); 164 165 memcpy(&indx.key.object_id,object_id,sizeof(GUID)); 166 167 indx.data.file_id = file_id; 168 memcpy(&indx.data.birth_volume_id, 169 &object_id->birth_volume_id,sizeof(GUID)); 170 memcpy(&indx.data.birth_object_id, 171 &object_id->birth_object_id,sizeof(GUID)); 172 memcpy(&indx.data.domain_id, 173 &object_id->domain_id,sizeof(GUID)); 174 ntfs_index_ctx_reinit(xo); 175 return (ntfs_ie_add(xo,(INDEX_ENTRY*)&indx)); 176 } 177 178 #endif /* HAVE_SETXATTR */ 179 180 /* 181 * Open the $Extend/$ObjId file and its index 182 * 183 * Return the index context if opened 184 * or NULL if an error occurred (errno tells why) 185 * 186 * The index has to be freed and inode closed when not needed any more. 187 */ 188 189 static ntfs_index_context *open_object_id_index(ntfs_volume *vol) 190 { 191 u64 inum; 192 ntfs_inode *ni; 193 ntfs_inode *dir_ni; 194 ntfs_index_context *xo; 195 196 /* do not use path_name_to inode - could reopen root */ 197 dir_ni = ntfs_inode_open(vol, FILE_Extend); 198 ni = (ntfs_inode*)NULL; 199 if (dir_ni) { 200 inum = ntfs_inode_lookup_by_mbsname(dir_ni,"$ObjId"); 201 if (inum != (u64)-1) 202 ni = ntfs_inode_open(vol, inum); 203 ntfs_inode_close(dir_ni); 204 } 205 if (ni) { 206 xo = ntfs_index_ctx_get(ni, objid_index_name, 2); 207 if (!xo) { 208 ntfs_inode_close(ni); 209 } 210 } else 211 xo = (ntfs_index_context*)NULL; 212 return (xo); 213 } 214 215 #ifdef HAVE_SETXATTR /* extended attributes interface required */ 216 217 /* 218 * Merge object_id data stored in the index into 219 * a full object_id struct. 220 * 221 * returns 0 if merging successful 222 * -1 if no data could be merged. This is generally not an error 223 */ 224 225 static int merge_index_data(ntfs_inode *ni, 226 const OBJECT_ID_ATTR *objectid_attr, 227 OBJECT_ID_ATTR *full_objectid) 228 { 229 OBJECT_ID_INDEX_KEY key; 230 struct OBJECT_ID_INDEX *entry; 231 ntfs_index_context *xo; 232 ntfs_inode *xoni; 233 int res; 234 235 res = -1; 236 xo = open_object_id_index(ni->vol); 237 if (xo) { 238 memcpy(&key.object_id,objectid_attr,sizeof(GUID)); 239 if (!ntfs_index_lookup(&key, 240 sizeof(OBJECT_ID_INDEX_KEY), xo)) { 241 entry = (struct OBJECT_ID_INDEX*)xo->entry; 242 /* make sure inode numbers match */ 243 if (entry 244 && (MREF(le64_to_cpu(entry->data.file_id)) 245 == ni->mft_no)) { 246 memcpy(&full_objectid->birth_volume_id, 247 &entry->data.birth_volume_id, 248 sizeof(GUID)); 249 memcpy(&full_objectid->birth_object_id, 250 &entry->data.birth_object_id, 251 sizeof(GUID)); 252 memcpy(&full_objectid->domain_id, 253 &entry->data.domain_id, 254 sizeof(GUID)); 255 res = 0; 256 } 257 } 258 xoni = xo->ni; 259 ntfs_index_ctx_put(xo); 260 ntfs_inode_close(xoni); 261 } 262 return (res); 263 } 264 265 #endif /* HAVE_SETXATTR */ 266 267 /* 268 * Remove an object id index entry if attribute present 269 * 270 * Returns the size of existing object id 271 * (the existing object_d is returned) 272 * -1 if failure, explained by errno 273 */ 274 275 static int remove_object_id_index(ntfs_attr *na, ntfs_index_context *xo, 276 OBJECT_ID_ATTR *old_attr) 277 { 278 OBJECT_ID_INDEX_KEY key; 279 struct OBJECT_ID_INDEX *entry; 280 s64 size; 281 int ret; 282 283 ret = na->data_size; 284 if (ret) { 285 /* read the existing object id attribute */ 286 size = ntfs_attr_pread(na, 0, sizeof(GUID), old_attr); 287 if (size >= (s64)sizeof(GUID)) { 288 memcpy(&key.object_id, 289 &old_attr->object_id,sizeof(GUID)); 290 if (!ntfs_index_lookup(&key, 291 sizeof(OBJECT_ID_INDEX_KEY), xo)) { 292 entry = (struct OBJECT_ID_INDEX*)xo->entry; 293 memcpy(&old_attr->birth_volume_id, 294 &entry->data.birth_volume_id, 295 sizeof(GUID)); 296 memcpy(&old_attr->birth_object_id, 297 &entry->data.birth_object_id, 298 sizeof(GUID)); 299 memcpy(&old_attr->domain_id, 300 &entry->data.domain_id, 301 sizeof(GUID)); 302 if (ntfs_index_rm(xo)) 303 ret = -1; 304 } 305 } else { 306 ret = -1; 307 errno = ENODATA; 308 } 309 } 310 return (ret); 311 } 312 313 #ifdef HAVE_SETXATTR /* extended attributes interface required */ 314 315 /* 316 * Update the object id and index 317 * 318 * The object_id attribute should have been created and the 319 * non-duplication of the GUID should have been checked before. 320 * 321 * Returns 0 if success 322 * -1 if failure, explained by errno 323 * If could not remove the existing index, nothing is done, 324 * If could not write the new data, no index entry is inserted 325 * If failed to insert the index, data is removed 326 */ 327 328 static int update_object_id(ntfs_inode *ni, ntfs_index_context *xo, 329 const OBJECT_ID_ATTR *value, size_t size) 330 { 331 OBJECT_ID_ATTR old_attr; 332 ntfs_attr *na; 333 int oldsize; 334 int written; 335 int res; 336 337 res = 0; 338 339 na = ntfs_attr_open(ni, AT_OBJECT_ID, AT_UNNAMED, 0); 340 if (na) { 341 342 /* remove the existing index entry */ 343 oldsize = remove_object_id_index(na,xo,&old_attr); 344 if (oldsize < 0) 345 res = -1; 346 else { 347 /* resize attribute */ 348 res = ntfs_attr_truncate(na, (s64)sizeof(GUID)); 349 /* write the object_id in attribute */ 350 if (!res && value) { 351 written = (int)ntfs_attr_pwrite(na, 352 (s64)0, (s64)sizeof(GUID), 353 &value->object_id); 354 if (written != (s64)sizeof(GUID)) { 355 ntfs_log_error("Failed to update " 356 "object id\n"); 357 errno = EIO; 358 res = -1; 359 } 360 } 361 /* write index part if provided */ 362 if (!res 363 && ((size < sizeof(OBJECT_ID_ATTR)) 364 || set_object_id_index(ni,xo,value))) { 365 /* 366 * If cannot index, try to remove the object 367 * id and log the error. There will be an 368 * inconsistency if removal fails. 369 */ 370 ntfs_attr_rm(na); 371 ntfs_log_error("Failed to index object id." 372 " Possible corruption.\n"); 373 } 374 } 375 ntfs_attr_close(na); 376 NInoSetDirty(ni); 377 } else 378 res = -1; 379 return (res); 380 } 381 382 /* 383 * Add a (dummy) object id to an inode if it does not exist 384 * 385 * returns 0 if attribute was inserted (or already present) 386 * -1 if adding failed (explained by errno) 387 */ 388 389 static int add_object_id(ntfs_inode *ni, int flags) 390 { 391 int res; 392 u8 dummy; 393 394 res = -1; /* default return */ 395 if (!ntfs_attr_exist(ni,AT_OBJECT_ID, AT_UNNAMED,0)) { 396 if (!(flags & XATTR_REPLACE)) { 397 /* 398 * no object id attribute : add one, 399 * apparently, this does not feed the new value in 400 * Note : NTFS version must be >= 3 401 */ 402 if (ni->vol->major_ver >= 3) { 403 res = ntfs_attr_add(ni, AT_OBJECT_ID, 404 AT_UNNAMED, 0, &dummy, (s64)0); 405 NInoSetDirty(ni); 406 } else 407 errno = EOPNOTSUPP; 408 } else 409 errno = ENODATA; 410 } else { 411 if (flags & XATTR_CREATE) 412 errno = EEXIST; 413 else 414 res = 0; 415 } 416 return (res); 417 } 418 419 #endif /* HAVE_SETXATTR */ 420 421 /* 422 * Delete an object_id index entry 423 * 424 * Returns 0 if success 425 * -1 if failure, explained by errno 426 */ 427 428 int ntfs_delete_object_id_index(ntfs_inode *ni) 429 { 430 ntfs_index_context *xo; 431 ntfs_inode *xoni; 432 ntfs_attr *na; 433 OBJECT_ID_ATTR old_attr; 434 int res; 435 436 res = 0; 437 na = ntfs_attr_open(ni, AT_OBJECT_ID, AT_UNNAMED, 0); 438 if (na) { 439 /* 440 * read the existing object id 441 * and un-index it 442 */ 443 xo = open_object_id_index(ni->vol); 444 if (xo) { 445 if (remove_object_id_index(na,xo,&old_attr) < 0) 446 res = -1; 447 xoni = xo->ni; 448 ntfs_index_entry_mark_dirty(xo); 449 NInoSetDirty(xoni); 450 ntfs_index_ctx_put(xo); 451 ntfs_inode_close(xoni); 452 } 453 ntfs_attr_close(na); 454 } 455 return (res); 456 } 457 458 #ifdef HAVE_SETXATTR /* extended attributes interface required */ 459 460 /* 461 * Get the ntfs object id into an extended attribute 462 * 463 * If present, the object_id from the attribute and the GUIDs 464 * from the index are returned (formatted as OBJECT_ID_ATTR) 465 * 466 * Returns the global size (can be 0, 16 or 64) 467 * and the buffer is updated if it is long enough 468 */ 469 470 int ntfs_get_ntfs_object_id(ntfs_inode *ni, char *value, size_t size) 471 { 472 OBJECT_ID_ATTR full_objectid; 473 OBJECT_ID_ATTR *objectid_attr; 474 s64 attr_size; 475 int full_size; 476 477 full_size = 0; /* default to no data and some error to be defined */ 478 if (ni) { 479 objectid_attr = (OBJECT_ID_ATTR*)ntfs_attr_readall(ni, 480 AT_OBJECT_ID,(ntfschar*)NULL, 0, &attr_size); 481 if (objectid_attr) { 482 /* restrict to only GUID present in attr */ 483 if (attr_size == sizeof(GUID)) { 484 memcpy(&full_objectid.object_id, 485 objectid_attr,sizeof(GUID)); 486 full_size = sizeof(GUID); 487 /* get data from index, if any */ 488 if (!merge_index_data(ni, objectid_attr, 489 &full_objectid)) { 490 full_size = sizeof(OBJECT_ID_ATTR); 491 } 492 if (full_size <= (s64)size) { 493 if (value) 494 memcpy(value,&full_objectid, 495 full_size); 496 else 497 errno = EINVAL; 498 } 499 } else { 500 /* unexpected size, better return unsupported */ 501 errno = EOPNOTSUPP; 502 full_size = 0; 503 } 504 free(objectid_attr); 505 } else 506 errno = ENODATA; 507 } 508 return (full_size ? (int)full_size : -errno); 509 } 510 511 /* 512 * Set the object id from an extended attribute 513 * 514 * If the size is 64, the attribute and index are set. 515 * else if the size is not less than 16 only the attribute is set. 516 * The object id index is set accordingly. 517 * 518 * Returns 0, or -1 if there is a problem 519 */ 520 521 int ntfs_set_ntfs_object_id(ntfs_inode *ni, 522 const char *value, size_t size, int flags) 523 { 524 OBJECT_ID_INDEX_KEY key; 525 ntfs_inode *xoni; 526 ntfs_index_context *xo; 527 int res; 528 529 res = 0; 530 if (ni && value && (size >= sizeof(GUID))) { 531 xo = open_object_id_index(ni->vol); 532 if (xo) { 533 /* make sure the GUID was not used somewhere */ 534 memcpy(&key.object_id, value, sizeof(GUID)); 535 if (ntfs_index_lookup(&key, 536 sizeof(OBJECT_ID_INDEX_KEY), xo)) { 537 ntfs_index_ctx_reinit(xo); 538 res = add_object_id(ni, flags); 539 if (!res) { 540 /* update value and index */ 541 res = update_object_id(ni,xo, 542 (const OBJECT_ID_ATTR*)value, 543 size); 544 } 545 } else { 546 /* GUID is present elsewhere */ 547 res = -1; 548 errno = EEXIST; 549 } 550 xoni = xo->ni; 551 ntfs_index_entry_mark_dirty(xo); 552 NInoSetDirty(xoni); 553 ntfs_index_ctx_put(xo); 554 ntfs_inode_close(xoni); 555 } else { 556 res = -1; 557 } 558 } else { 559 errno = EINVAL; 560 res = -1; 561 } 562 return (res ? -1 : 0); 563 } 564 565 /* 566 * Remove the object id 567 * 568 * Returns 0, or -1 if there is a problem 569 */ 570 571 int ntfs_remove_ntfs_object_id(ntfs_inode *ni) 572 { 573 int res; 574 int olderrno; 575 ntfs_attr *na; 576 ntfs_inode *xoni; 577 ntfs_index_context *xo; 578 int oldsize; 579 OBJECT_ID_ATTR old_attr; 580 581 res = 0; 582 if (ni) { 583 /* 584 * open and delete the object id 585 */ 586 na = ntfs_attr_open(ni, AT_OBJECT_ID, 587 AT_UNNAMED,0); 588 if (na) { 589 /* first remove index (old object id needed) */ 590 xo = open_object_id_index(ni->vol); 591 if (xo) { 592 oldsize = remove_object_id_index(na,xo, 593 &old_attr); 594 if (oldsize < 0) { 595 res = -1; 596 } else { 597 /* now remove attribute */ 598 res = ntfs_attr_rm(na); 599 if (res 600 && (oldsize > (int)sizeof(GUID))) { 601 /* 602 * If we could not remove the 603 * attribute, try to restore the 604 * index and log the error. There 605 * will be an inconsistency if 606 * the reindexing fails. 607 */ 608 set_object_id_index(ni, xo, 609 &old_attr); 610 ntfs_log_error( 611 "Failed to remove object id." 612 " Possible corruption.\n"); 613 } 614 } 615 616 xoni = xo->ni; 617 ntfs_index_entry_mark_dirty(xo); 618 NInoSetDirty(xoni); 619 ntfs_index_ctx_put(xo); 620 ntfs_inode_close(xoni); 621 } 622 olderrno = errno; 623 ntfs_attr_close(na); 624 /* avoid errno pollution */ 625 if (errno == ENOENT) 626 errno = olderrno; 627 } else { 628 errno = ENODATA; 629 res = -1; 630 } 631 NInoSetDirty(ni); 632 } else { 633 errno = EINVAL; 634 res = -1; 635 } 636 return (res ? -1 : 0); 637 } 638 639 #endif /* HAVE_SETXATTR */ 640