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