1 /* 2 * Copyright 2012, Jérôme Duval, korli@users.berlios.de. 3 * Copyright 2010, Michael Lotz, mmlr@mlotz.ch. 4 * Copyright 2003, Tyler Dauwalder, tyler@dauwalder.net. 5 * Distributed under the terms of the MIT License. 6 */ 7 8 #include "Icb.h" 9 10 #include "time.h" 11 12 #include "AllocationDescriptorList.h" 13 #include "Utils.h" 14 #include "Volume.h" 15 16 #include <file_cache.h> 17 18 19 status_t 20 DirectoryIterator::GetNextEntry(char *name, uint32 *length, ino_t *id) 21 { 22 if (!id || !name || !length) 23 return B_BAD_VALUE; 24 25 TRACE(("DirectoryIterator::GetNextEntry: name = %p, length = %" B_PRIu32 26 ", id = %p, position = %" B_PRIdOFF ", parent length = %" B_PRIu64 27 "\n", name, *length, id, fPosition, Parent()->Length())); 28 29 status_t status = B_OK; 30 if (fAtBeginning) { 31 TRACE(("DirectoryIterator::GetNextEntry: .\n")); 32 sprintf(name, "."); 33 *length = 1; 34 *id = Parent()->Id(); 35 fAtBeginning = false; 36 } else { 37 if (uint64(fPosition) >= Parent()->Length()) { 38 TRACE(("DirectoryIterator::GetNextEntry: end of dir\n")); 39 return B_ENTRY_NOT_FOUND; 40 } 41 42 uint8 data[kMaxFileIdSize]; 43 file_id_descriptor *entry = (file_id_descriptor *)data; 44 45 uint32 block = 0; 46 off_t offset = fPosition; 47 48 size_t entryLength = kMaxFileIdSize; 49 // First read in the static portion of the file id descriptor, 50 // then, based on the information therein, read in the variable 51 // length tail portion as well. 52 status = Parent()->Read(offset, entry, &entryLength, &block); 53 if (!status && entryLength >= sizeof(file_id_descriptor) 54 && entry->tag().init_check(block) == B_OK) { 55 PDUMP(entry); 56 offset += entry->total_length(); 57 58 if (entry->is_parent()) { 59 TRACE(("DirectoryIterator::GetNextEntry: ..\n")); 60 sprintf(name, ".."); 61 *length = 2; 62 } else { 63 UdfString string(entry->id(), entry->id_length()); 64 TRACE(("DirectoryIterator::GetNextEntry: UfdString id == `%s', " 65 "length = %" B_PRIu32 "\n", string.Utf8(), 66 string.Utf8Length())); 67 DUMP(entry->icb()); 68 sprintf(name, "%s", string.Utf8()); 69 *length = string.Utf8Length(); 70 } 71 *id = to_vnode_id(entry->icb()); 72 } 73 74 if (!status) 75 fPosition = offset; 76 } 77 78 return status; 79 } 80 81 82 /* \brief Rewinds the iterator to point to the first entry in the directory. */ 83 void 84 DirectoryIterator::Rewind() 85 { 86 fAtBeginning = true; 87 fPosition = 0; 88 } 89 90 91 // #pragma mark - Private methods 92 93 94 DirectoryIterator::DirectoryIterator(Icb *parent) 95 : 96 fAtBeginning(true), 97 fParent(parent), 98 fPosition(0) 99 { 100 } 101 102 103 Icb::Icb(Volume *volume, long_address address) 104 : 105 fVolume(volume), 106 fData(volume), 107 fInitStatus(B_NO_INIT), 108 fId(to_vnode_id(address)), 109 fPartition(address.partition()), 110 fFileEntry(&fData), 111 fExtendedEntry(&fData), 112 fFileCache(NULL), 113 fFileMap(NULL) 114 { 115 TRACE(("Icb::Icb: volume = %p, address(block = %" B_PRIu32 ", partition = " 116 "%d, length = %" B_PRIu32 ")\n", volume, address.block(), 117 address.partition(), address.length())); 118 119 if (volume == NULL) { 120 fInitStatus = B_BAD_VALUE; 121 return; 122 } 123 124 off_t block; 125 status_t status = fVolume->MapBlock(address, &block); 126 if (status == B_OK) { 127 status = fData.SetTo(block); 128 if (status == B_OK) { 129 icb_header *header = (icb_header *)fData.Block(); 130 if (header->tag().id() == TAGID_FILE_ENTRY) { 131 file_icb_entry *entry = (file_icb_entry *)header; 132 PDUMP(entry); 133 (void)entry; // warning death 134 } else if (header->tag().id() == TAGID_EXTENDED_FILE_ENTRY) { 135 extended_file_icb_entry *entry 136 = (extended_file_icb_entry *)header; 137 PDUMP(entry); 138 (void)entry; // warning death 139 } else { 140 PDUMP(header); 141 } 142 status = header->tag().init_check(address.block()); 143 } 144 } 145 146 if (IsFile()) { 147 fFileCache = file_cache_create(fVolume->ID(), fId, Length()); 148 fFileMap = file_map_create(fVolume->ID(), fId, Length()); 149 } 150 151 fInitStatus = status; 152 TRACE(("Icb::Icb: status = 0x%" B_PRIx32 ", `%s'\n", status, 153 strerror(status))); 154 } 155 156 157 Icb::~Icb() 158 { 159 if (fFileCache != NULL) { 160 file_cache_delete(fFileCache); 161 file_map_delete(fFileMap); 162 } 163 } 164 165 166 status_t 167 Icb::GetDirectoryIterator(DirectoryIterator **iterator) 168 { 169 status_t error = iterator ? B_OK : B_BAD_VALUE; 170 171 if (!error) { 172 *iterator = new(std::nothrow) DirectoryIterator(this); 173 if (*iterator) 174 fIteratorList.Add(*iterator); 175 else 176 error = B_NO_MEMORY; 177 } 178 179 return error; 180 } 181 182 183 status_t 184 Icb::InitCheck() 185 { 186 return fInitStatus; 187 } 188 189 190 void 191 Icb::GetAccessTime(struct timespec ×pec) const 192 { 193 timestamp ts; 194 if (_Tag().id() == TAGID_EXTENDED_FILE_ENTRY) 195 ts = _ExtendedEntry()->access_date_and_time(); 196 else 197 ts = _FileEntry()->access_date_and_time(); 198 199 if (decode_time(ts, timespec) != B_OK) { 200 decode_time( 201 fVolume->PrimaryVolumeDescriptor()->recording_date_and_time(), 202 timespec); 203 } 204 } 205 206 207 void 208 Icb::GetModificationTime(struct timespec ×pec) const 209 { 210 timestamp ts; 211 if (_Tag().id() == TAGID_EXTENDED_FILE_ENTRY) 212 ts = _ExtendedEntry()->modification_date_and_time(); 213 else 214 ts = _FileEntry()->modification_date_and_time(); 215 216 if (decode_time(ts, timespec) != B_OK) { 217 decode_time( 218 fVolume->PrimaryVolumeDescriptor()->recording_date_and_time(), 219 timespec); 220 } 221 } 222 223 224 status_t 225 Icb::FindBlock(uint32 logicalBlock, off_t &block, bool &recorded) 226 { 227 off_t pos = logicalBlock << fVolume->BlockShift(); 228 if (uint64(pos) >= Length()) { 229 block = -1; 230 return B_ERROR; 231 } 232 233 DEBUG_INIT_ETC("Icb", ("pos: %" B_PRIdOFF, pos)); 234 235 status_t status = B_OK; 236 long_address extent; 237 bool isEmpty = false; 238 recorded = false; 239 240 switch (_IcbTag().descriptor_flags()) { 241 case ICB_DESCRIPTOR_TYPE_SHORT: 242 { 243 TRACE(("Icb::FindBlock: descriptor type -> short\n")); 244 AllocationDescriptorList<ShortDescriptorAccessor> list(this, 245 ShortDescriptorAccessor(fPartition)); 246 status = list.FindExtent(pos, &extent, &isEmpty); 247 if (status != B_OK) { 248 TRACE_ERROR(("Icb::FindBlock: error finding extent for offset " 249 "%" B_PRIdOFF ". status = 0x%" B_PRIx32 " `%s'\n", pos, status, 250 strerror(status))); 251 } 252 break; 253 } 254 255 case ICB_DESCRIPTOR_TYPE_LONG: 256 { 257 TRACE(("Icb::FindBlock: descriptor type -> long\n")); 258 AllocationDescriptorList<LongDescriptorAccessor> list(this); 259 status = list.FindExtent(pos, &extent, &isEmpty); 260 if (status != B_OK) { 261 TRACE_ERROR(("Icb::FindBlock: error finding extent for offset " 262 "%" B_PRIdOFF ". status = 0x%" B_PRIx32 " `%s'\n", pos, 263 status, strerror(status))); 264 } 265 break; 266 } 267 268 case ICB_DESCRIPTOR_TYPE_EXTENDED: 269 { 270 TRACE(("Icb::FindBlock: descriptor type -> extended\n")); 271 // AllocationDescriptorList<ExtendedDescriptorAccessor> list(this, ExtendedDescriptorAccessor(0)); 272 // RETURN(_Read(list, pos, buffer, length, block)); 273 RETURN(B_ERROR); 274 break; 275 } 276 277 case ICB_DESCRIPTOR_TYPE_EMBEDDED: 278 { 279 TRACE(("Icb::FindBlock: descriptor type: embedded\n")); 280 RETURN(B_ERROR); 281 break; 282 } 283 284 default: 285 TRACE(("Icb::FindBlock: invalid icb descriptor flags! (flags = %d)\n", 286 _IcbTag().descriptor_flags())); 287 RETURN(B_BAD_VALUE); 288 break; 289 } 290 291 if (status == B_OK) { 292 block = extent.block(); 293 recorded = extent.type() == EXTENT_TYPE_RECORDED; 294 TRACE(("Icb::FindBlock: block %" B_PRIdOFF "\n", block)); 295 } 296 return status; 297 } 298 299 300 status_t 301 Icb::Read(off_t pos, void *buffer, size_t *length, uint32 *block) 302 { 303 TRACE(("Icb::Read: pos = %" B_PRIdOFF ", buffer = %p, length = (%p)->%ld\n", 304 pos, buffer, length, (length ? *length : 0))); 305 306 DEBUG_INIT_ETC("Icb", ("pos: %" B_PRIdOFF " , length: %ld", pos, *length)); 307 308 if (fFileCache != NULL) 309 return file_cache_read(fFileCache, NULL, pos, buffer, length); 310 311 if (!buffer || !length || pos < 0) 312 return B_BAD_VALUE; 313 314 if (uint64(pos) >= Length()) { 315 *length = 0; 316 return B_OK; 317 } 318 319 switch (_IcbTag().descriptor_flags()) { 320 case ICB_DESCRIPTOR_TYPE_SHORT: 321 { 322 TRACE(("Icb::Read: descriptor type -> short\n")); 323 AllocationDescriptorList<ShortDescriptorAccessor> list(this, 324 ShortDescriptorAccessor(fPartition)); 325 RETURN(_Read(list, pos, buffer, length, block)); 326 break; 327 } 328 329 case ICB_DESCRIPTOR_TYPE_LONG: 330 { 331 TRACE(("Icb::Read: descriptor type -> long\n")); 332 AllocationDescriptorList<LongDescriptorAccessor> list(this); 333 RETURN(_Read(list, pos, buffer, length, block)); 334 break; 335 } 336 337 case ICB_DESCRIPTOR_TYPE_EXTENDED: 338 { 339 TRACE(("Icb::Read: descriptor type -> extended\n")); 340 // AllocationDescriptorList<ExtendedDescriptorAccessor> list(this, ExtendedDescriptorAccessor(0)); 341 // RETURN(_Read(list, pos, buffer, length, block)); 342 RETURN(B_ERROR); 343 break; 344 } 345 346 case ICB_DESCRIPTOR_TYPE_EMBEDDED: 347 { 348 TRACE(("Icb::Read: descriptor type: embedded\n")); 349 RETURN(B_ERROR); 350 break; 351 } 352 353 default: 354 TRACE(("Icb::Read: invalid icb descriptor flags! (flags = %d)\n", 355 _IcbTag().descriptor_flags())); 356 RETURN(B_BAD_VALUE); 357 break; 358 } 359 } 360 361 362 /*! \brief Does the dirty work of reading using the given DescriptorList object 363 to access the allocation descriptors properly. 364 */ 365 template <class DescriptorList> 366 status_t 367 Icb::_Read(DescriptorList &list, off_t pos, void *_buffer, size_t *length, uint32 *block) 368 { 369 TRACE(("Icb::_Read(): list = %p, pos = %" B_PRIdOFF ", buffer = %p, " 370 "length = %ld\n", &list, pos, _buffer, (length ? *length : 0))); 371 372 uint64 bytesLeftInFile = uint64(pos) > Length() ? 0 : Length() - pos; 373 size_t bytesLeft = (*length >= bytesLeftInFile) ? bytesLeftInFile : *length; 374 size_t bytesRead = 0; 375 376 Volume *volume = GetVolume(); 377 status_t status = B_OK; 378 uint8 *buffer = (uint8 *)_buffer; 379 bool isFirstBlock = true; 380 381 while (bytesLeft > 0) { 382 383 TRACE(("Icb::_Read(): pos: %" B_PRIdOFF ", bytesLeft: %ld\n", pos, 384 bytesLeft)); 385 long_address extent; 386 bool isEmpty = false; 387 status = list.FindExtent(pos, &extent, &isEmpty); 388 if (status != B_OK) { 389 TRACE_ERROR(("Icb::_Read: error finding extent for offset %" 390 B_PRIdOFF ". status = 0x%" B_PRIx32 " `%s'\n", pos, status, 391 strerror(status))); 392 break; 393 } 394 395 TRACE(("Icb::_Read(): found extent for offset %" B_PRIdOFF ": (block: " 396 "%" B_PRIu32 ", partition: %d, length: %" B_PRIu32 ", type: %d)\n", 397 pos, extent.block(), extent.partition(), extent.length(), 398 extent.type())); 399 400 switch (extent.type()) { 401 case EXTENT_TYPE_RECORDED: 402 isEmpty = false; 403 break; 404 405 case EXTENT_TYPE_ALLOCATED: 406 case EXTENT_TYPE_UNALLOCATED: 407 isEmpty = true; 408 break; 409 410 default: 411 TRACE_ERROR(("Icb::_Read(): Invalid extent type found: %d\n", 412 extent.type())); 413 status = B_ERROR; 414 break; 415 } 416 417 if (status != B_OK) 418 break; 419 420 // Note the unmapped first block of the total read in 421 // the block output parameter if provided 422 if (isFirstBlock) { 423 isFirstBlock = false; 424 if (block) 425 *block = extent.block(); 426 } 427 428 off_t blockOffset 429 = pos - off_t((pos >> volume->BlockShift()) << volume->BlockShift()); 430 431 size_t readLength = volume->BlockSize() - blockOffset; 432 if (bytesLeft < readLength) 433 readLength = bytesLeft; 434 if (extent.length() < readLength) 435 readLength = extent.length(); 436 437 TRACE(("Icb::_Read: reading block. offset = %" B_PRIdOFF 438 ", length: %ld\n", blockOffset, readLength)); 439 440 if (isEmpty) { 441 TRACE(("Icb::_Read: reading %ld empty bytes as zeros\n", 442 readLength)); 443 memset(buffer, 0, readLength); 444 } else { 445 off_t diskBlock; 446 status = volume->MapBlock(extent, &diskBlock); 447 if (status != B_OK) { 448 TRACE_ERROR(("Icb::_Read: could not map extent\n")); 449 break; 450 } 451 452 TRACE(("Icb::_Read: %ld bytes from disk block %" B_PRIdOFF " using" 453 " block_cache_get_etc()\n", readLength, diskBlock)); 454 const uint8 *data; 455 status = block_cache_get_etc(volume->BlockCache(), 456 diskBlock, 0, readLength, (const void**)&data); 457 if (status != B_OK) 458 break; 459 memcpy(buffer, data + blockOffset, readLength); 460 block_cache_put(volume->BlockCache(), diskBlock); 461 } 462 463 bytesLeft -= readLength; 464 bytesRead += readLength; 465 pos += readLength; 466 buffer += readLength; 467 } 468 469 *length = bytesRead; 470 471 return status; 472 } 473 474 475 status_t 476 Icb::GetFileMap(off_t offset, size_t size, file_io_vec *vecs, size_t *count) 477 { 478 switch (_IcbTag().descriptor_flags()) { 479 case ICB_DESCRIPTOR_TYPE_SHORT: 480 { 481 AllocationDescriptorList<ShortDescriptorAccessor> list(this, 482 ShortDescriptorAccessor(0)); 483 return _GetFileMap(list, offset, size, vecs, count); 484 } 485 486 case ICB_DESCRIPTOR_TYPE_LONG: 487 { 488 AllocationDescriptorList<LongDescriptorAccessor> list(this); 489 return _GetFileMap(list, offset, size, vecs, count); 490 } 491 492 case ICB_DESCRIPTOR_TYPE_EXTENDED: 493 case ICB_DESCRIPTOR_TYPE_EMBEDDED: 494 default: 495 { 496 // TODO: implement? 497 return B_UNSUPPORTED; 498 } 499 } 500 } 501 502 503 template<class DescriptorList> 504 status_t 505 Icb::_GetFileMap(DescriptorList &list, off_t offset, size_t size, 506 struct file_io_vec *vecs, size_t *count) 507 { 508 size_t index = 0; 509 size_t max = *count; 510 511 while (true) { 512 long_address extent; 513 bool isEmpty = false; 514 status_t status = list.FindExtent(offset, &extent, &isEmpty); 515 if (status != B_OK) 516 return status; 517 518 switch (extent.type()) { 519 case EXTENT_TYPE_RECORDED: 520 isEmpty = false; 521 break; 522 523 case EXTENT_TYPE_ALLOCATED: 524 case EXTENT_TYPE_UNALLOCATED: 525 isEmpty = true; 526 break; 527 528 default: 529 return B_ERROR; 530 } 531 532 if (isEmpty) 533 vecs[index].offset = -1; 534 else { 535 off_t diskBlock; 536 fVolume->MapBlock(extent, &diskBlock); 537 vecs[index].offset = diskBlock << fVolume->BlockShift(); 538 } 539 540 off_t length = extent.length(); 541 vecs[index].length = length; 542 543 offset += length; 544 size -= length; 545 index++; 546 547 if (index >= max || (off_t)size <= vecs[index - 1].length 548 || offset >= (off_t)Length()) { 549 *count = index; 550 return index >= max ? B_BUFFER_OVERFLOW : B_OK; 551 } 552 } 553 554 // can never get here 555 return B_ERROR; 556 } 557 558 559 status_t 560 Icb::Find(const char *filename, ino_t *id) 561 { 562 TRACE(("Icb::Find: filename = `%s', id = %p\n", filename, id)); 563 564 if (!filename || !id) 565 return B_BAD_VALUE; 566 567 DirectoryIterator *i; 568 status_t status = GetDirectoryIterator(&i); 569 if (status != B_OK) 570 return status; 571 572 ino_t entryId; 573 uint32 length = B_FILE_NAME_LENGTH; 574 char name[B_FILE_NAME_LENGTH]; 575 576 bool foundIt = false; 577 while (i->GetNextEntry(name, &length, &entryId) == B_OK) { 578 if (strcmp(filename, name) == 0) { 579 foundIt = true; 580 break; 581 } 582 583 // reset overwritten length 584 length = B_FILE_NAME_LENGTH; 585 } 586 587 if (foundIt) 588 *id = entryId; 589 else 590 status = B_ENTRY_NOT_FOUND; 591 592 return status; 593 } 594