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 = 2; 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 = 3; 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) { 127 icb_header *header = (icb_header *)fData.SetTo(block); 128 if (header->tag().id() == TAGID_FILE_ENTRY) { 129 file_icb_entry *entry = (file_icb_entry *)header; 130 PDUMP(entry); 131 (void)entry; // warning death 132 } else if (header->tag().id() == TAGID_EXTENDED_FILE_ENTRY) { 133 extended_file_icb_entry *entry = (extended_file_icb_entry *)header; 134 PDUMP(entry); 135 (void)entry; // warning death 136 } else { 137 PDUMP(header); 138 } 139 status = header->tag().init_check(address.block()); 140 } 141 142 if (IsFile()) { 143 fFileCache = file_cache_create(fVolume->ID(), fId, Length()); 144 fFileMap = file_map_create(fVolume->ID(), fId, Length()); 145 } 146 147 fInitStatus = status; 148 TRACE(("Icb::Icb: status = 0x%" B_PRIx32 ", `%s'\n", status, 149 strerror(status))); 150 } 151 152 153 Icb::~Icb() 154 { 155 if (fFileCache != NULL) { 156 file_cache_delete(fFileCache); 157 file_map_delete(fFileMap); 158 } 159 } 160 161 162 status_t 163 Icb::GetDirectoryIterator(DirectoryIterator **iterator) 164 { 165 status_t error = iterator ? B_OK : B_BAD_VALUE; 166 167 if (!error) { 168 *iterator = new(std::nothrow) DirectoryIterator(this); 169 if (*iterator) 170 fIteratorList.Add(*iterator); 171 else 172 error = B_NO_MEMORY; 173 } 174 175 return error; 176 } 177 178 179 status_t 180 Icb::InitCheck() 181 { 182 return fInitStatus; 183 } 184 185 186 void 187 Icb::GetAccessTime(struct timespec ×pec) const 188 { 189 timestamp ts; 190 if (_Tag().id() == TAGID_EXTENDED_FILE_ENTRY) 191 ts = _ExtendedEntry()->access_date_and_time(); 192 else 193 ts = _FileEntry()->access_date_and_time(); 194 195 if (decode_time(ts, timespec) != B_OK) { 196 decode_time( 197 fVolume->PrimaryVolumeDescriptor()->recording_date_and_time(), 198 timespec); 199 } 200 } 201 202 203 void 204 Icb::GetModificationTime(struct timespec ×pec) const 205 { 206 timestamp ts; 207 if (_Tag().id() == TAGID_EXTENDED_FILE_ENTRY) 208 ts = _ExtendedEntry()->modification_date_and_time(); 209 else 210 ts = _FileEntry()->modification_date_and_time(); 211 212 if (decode_time(ts, timespec) != B_OK) { 213 decode_time( 214 fVolume->PrimaryVolumeDescriptor()->recording_date_and_time(), 215 timespec); 216 } 217 } 218 219 220 status_t 221 Icb::FindBlock(uint32 logicalBlock, off_t &block, bool &recorded) 222 { 223 off_t pos = logicalBlock << fVolume->BlockShift(); 224 if (uint64(pos) >= Length()) { 225 block = -1; 226 return B_ERROR; 227 } 228 229 DEBUG_INIT_ETC("Icb", ("pos: %" B_PRIdOFF, pos)); 230 231 status_t status = B_OK; 232 long_address extent; 233 bool isEmpty = false; 234 recorded = false; 235 236 switch (_IcbTag().descriptor_flags()) { 237 case ICB_DESCRIPTOR_TYPE_SHORT: 238 { 239 TRACE(("Icb::FindBlock: descriptor type -> short\n")); 240 AllocationDescriptorList<ShortDescriptorAccessor> list(this, 241 ShortDescriptorAccessor(fPartition)); 242 status = list.FindExtent(pos, &extent, &isEmpty); 243 if (status != B_OK) { 244 TRACE_ERROR(("Icb::FindBlock: error finding extent for offset " 245 "%" B_PRIdOFF ". status = 0x%" B_PRIx32 " `%s'\n", pos, status, 246 strerror(status))); 247 } 248 break; 249 } 250 251 case ICB_DESCRIPTOR_TYPE_LONG: 252 { 253 TRACE(("Icb::FindBlock: descriptor type -> long\n")); 254 AllocationDescriptorList<LongDescriptorAccessor> list(this); 255 status = list.FindExtent(pos, &extent, &isEmpty); 256 if (status != B_OK) { 257 TRACE_ERROR(("Icb::FindBlock: error finding extent for offset " 258 "%" B_PRIdOFF ". status = 0x%" B_PRIx32 " `%s'\n", pos, 259 status, strerror(status))); 260 } 261 break; 262 } 263 264 case ICB_DESCRIPTOR_TYPE_EXTENDED: 265 { 266 TRACE(("Icb::FindBlock: descriptor type -> extended\n")); 267 // AllocationDescriptorList<ExtendedDescriptorAccessor> list(this, ExtendedDescriptorAccessor(0)); 268 // RETURN(_Read(list, pos, buffer, length, block)); 269 RETURN(B_ERROR); 270 break; 271 } 272 273 case ICB_DESCRIPTOR_TYPE_EMBEDDED: 274 { 275 TRACE(("Icb::FindBlock: descriptor type: embedded\n")); 276 RETURN(B_ERROR); 277 break; 278 } 279 280 default: 281 TRACE(("Icb::FindBlock: invalid icb descriptor flags! (flags = %d)\n", 282 _IcbTag().descriptor_flags())); 283 RETURN(B_BAD_VALUE); 284 break; 285 } 286 287 if (status == B_OK) { 288 block = extent.block(); 289 recorded = extent.type() == EXTENT_TYPE_RECORDED; 290 TRACE(("Icb::FindBlock: block %" B_PRIdOFF "\n", block)); 291 } 292 return status; 293 } 294 295 296 status_t 297 Icb::Read(off_t pos, void *buffer, size_t *length, uint32 *block) 298 { 299 TRACE(("Icb::Read: pos = %" B_PRIdOFF ", buffer = %p, length = (%p)->%ld\n", 300 pos, buffer, length, (length ? *length : 0))); 301 302 if (!buffer || !length || pos < 0) 303 return B_BAD_VALUE; 304 305 if (uint64(pos) >= Length()) { 306 *length = 0; 307 return B_OK; 308 } 309 310 DEBUG_INIT_ETC("Icb", ("pos: %lld, length: %ld", pos, *length)); 311 312 if (fFileCache != NULL) 313 return file_cache_read(fFileCache, NULL, pos, buffer, length); 314 315 switch (_IcbTag().descriptor_flags()) { 316 case ICB_DESCRIPTOR_TYPE_SHORT: 317 { 318 TRACE(("Icb::Read: descriptor type -> short\n")); 319 AllocationDescriptorList<ShortDescriptorAccessor> list(this, 320 ShortDescriptorAccessor(fPartition)); 321 RETURN(_Read(list, pos, buffer, length, block)); 322 break; 323 } 324 325 case ICB_DESCRIPTOR_TYPE_LONG: 326 { 327 TRACE(("Icb::Read: descriptor type -> long\n")); 328 AllocationDescriptorList<LongDescriptorAccessor> list(this); 329 RETURN(_Read(list, pos, buffer, length, block)); 330 break; 331 } 332 333 case ICB_DESCRIPTOR_TYPE_EXTENDED: 334 { 335 TRACE(("Icb::Read: descriptor type -> extended\n")); 336 // AllocationDescriptorList<ExtendedDescriptorAccessor> list(this, ExtendedDescriptorAccessor(0)); 337 // RETURN(_Read(list, pos, buffer, length, block)); 338 RETURN(B_ERROR); 339 break; 340 } 341 342 case ICB_DESCRIPTOR_TYPE_EMBEDDED: 343 { 344 TRACE(("Icb::Read: descriptor type: embedded\n")); 345 RETURN(B_ERROR); 346 break; 347 } 348 349 default: 350 TRACE(("Icb::Read: invalid icb descriptor flags! (flags = %d)\n", 351 _IcbTag().descriptor_flags())); 352 RETURN(B_BAD_VALUE); 353 break; 354 } 355 } 356 357 358 /*! \brief Does the dirty work of reading using the given DescriptorList object 359 to access the allocation descriptors properly. 360 */ 361 template <class DescriptorList> 362 status_t 363 Icb::_Read(DescriptorList &list, off_t pos, void *_buffer, size_t *length, uint32 *block) 364 { 365 TRACE(("Icb::_Read(): list = %p, pos = %" B_PRIdOFF ", buffer = %p, " 366 "length = %ld\n", &list, pos, _buffer, (length ? *length : 0))); 367 368 uint64 bytesLeftInFile = uint64(pos) > Length() ? 0 : Length() - pos; 369 size_t bytesLeft = (*length >= bytesLeftInFile) ? bytesLeftInFile : *length; 370 size_t bytesRead = 0; 371 372 Volume *volume = GetVolume(); 373 status_t status = B_OK; 374 uint8 *buffer = (uint8 *)_buffer; 375 bool isFirstBlock = true; 376 377 while (bytesLeft > 0) { 378 379 TRACE(("Icb::_Read(): pos: %" B_PRIdOFF ", bytesLeft: %ld\n", pos, 380 bytesLeft)); 381 long_address extent; 382 bool isEmpty = false; 383 status = list.FindExtent(pos, &extent, &isEmpty); 384 if (status != B_OK) { 385 TRACE_ERROR(("Icb::_Read: error finding extent for offset %" 386 B_PRIdOFF ". status = 0x%" B_PRIx32 " `%s'\n", pos, status, 387 strerror(status))); 388 break; 389 } 390 391 TRACE(("Icb::_Read(): found extent for offset %" B_PRIdOFF ": (block: " 392 "%" B_PRIu32 ", partition: %d, length: %" B_PRIu32 ", type: %d)\n", 393 pos, extent.block(), extent.partition(), extent.length(), 394 extent.type())); 395 396 switch (extent.type()) { 397 case EXTENT_TYPE_RECORDED: 398 isEmpty = false; 399 break; 400 401 case EXTENT_TYPE_ALLOCATED: 402 case EXTENT_TYPE_UNALLOCATED: 403 isEmpty = true; 404 break; 405 406 default: 407 TRACE_ERROR(("Icb::_Read(): Invalid extent type found: %d\n", 408 extent.type())); 409 status = B_ERROR; 410 break; 411 } 412 413 if (status != B_OK) 414 break; 415 416 // Note the unmapped first block of the total read in 417 // the block output parameter if provided 418 if (isFirstBlock) { 419 isFirstBlock = false; 420 if (block) 421 *block = extent.block(); 422 } 423 424 off_t blockOffset 425 = pos - off_t((pos >> volume->BlockShift()) << volume->BlockShift()); 426 427 size_t readLength = volume->BlockSize() - blockOffset; 428 if (bytesLeft < readLength) 429 readLength = bytesLeft; 430 if (extent.length() < readLength) 431 readLength = extent.length(); 432 433 TRACE(("Icb::_Read: reading block. offset = %" B_PRIdOFF 434 ", length: %ld\n", blockOffset, readLength)); 435 436 if (isEmpty) { 437 TRACE(("Icb::_Read: reading %ld empty bytes as zeros\n", 438 readLength)); 439 memset(buffer, 0, readLength); 440 } else { 441 off_t diskBlock; 442 status = volume->MapBlock(extent, &diskBlock); 443 if (status != B_OK) { 444 TRACE_ERROR(("Icb::_Read: could not map extent\n")); 445 break; 446 } 447 448 TRACE(("Icb::_Read: %ld bytes from disk block %" B_PRIdOFF " using" 449 " block_cache_get_etc()\n", readLength, diskBlock)); 450 uint8 *data = (uint8*)block_cache_get_etc(volume->BlockCache(), 451 diskBlock, 0, readLength); 452 if (data == NULL) 453 break; 454 memcpy(buffer, data + blockOffset, readLength); 455 block_cache_put(volume->BlockCache(), diskBlock); 456 } 457 458 bytesLeft -= readLength; 459 bytesRead += readLength; 460 pos += readLength; 461 buffer += readLength; 462 } 463 464 *length = bytesRead; 465 466 return status; 467 } 468 469 470 status_t 471 Icb::GetFileMap(off_t offset, size_t size, file_io_vec *vecs, size_t *count) 472 { 473 switch (_IcbTag().descriptor_flags()) { 474 case ICB_DESCRIPTOR_TYPE_SHORT: 475 { 476 AllocationDescriptorList<ShortDescriptorAccessor> list(this, 477 ShortDescriptorAccessor(0)); 478 return _GetFileMap(list, offset, size, vecs, count); 479 } 480 481 case ICB_DESCRIPTOR_TYPE_LONG: 482 { 483 AllocationDescriptorList<LongDescriptorAccessor> list(this); 484 return _GetFileMap(list, offset, size, vecs, count); 485 } 486 487 case ICB_DESCRIPTOR_TYPE_EXTENDED: 488 case ICB_DESCRIPTOR_TYPE_EMBEDDED: 489 default: 490 { 491 // TODO: implement? 492 return B_UNSUPPORTED; 493 } 494 } 495 } 496 497 498 template<class DescriptorList> 499 status_t 500 Icb::_GetFileMap(DescriptorList &list, off_t offset, size_t size, 501 struct file_io_vec *vecs, size_t *count) 502 { 503 size_t index = 0; 504 size_t max = *count; 505 506 while (true) { 507 long_address extent; 508 bool isEmpty = false; 509 status_t status = list.FindExtent(offset, &extent, &isEmpty); 510 if (status != B_OK) 511 return status; 512 513 switch (extent.type()) { 514 case EXTENT_TYPE_RECORDED: 515 isEmpty = false; 516 break; 517 518 case EXTENT_TYPE_ALLOCATED: 519 case EXTENT_TYPE_UNALLOCATED: 520 isEmpty = true; 521 break; 522 523 default: 524 return B_ERROR; 525 } 526 527 if (isEmpty) 528 vecs[index].offset = -1; 529 else { 530 off_t diskBlock; 531 fVolume->MapBlock(extent, &diskBlock); 532 vecs[index].offset = diskBlock << fVolume->BlockShift(); 533 } 534 535 off_t length = extent.length(); 536 vecs[index].length = length; 537 538 offset += length; 539 size -= length; 540 index++; 541 542 if (index >= max || (off_t)size <= vecs[index - 1].length 543 || offset >= (off_t)Length()) { 544 *count = index; 545 return index >= max ? B_BUFFER_OVERFLOW : B_OK; 546 } 547 } 548 549 // can never get here 550 return B_ERROR; 551 } 552 553 554 status_t 555 Icb::Find(const char *filename, ino_t *id) 556 { 557 TRACE(("Icb::Find: filename = `%s', id = %p\n", filename, id)); 558 559 if (!filename || !id) 560 return B_BAD_VALUE; 561 562 DirectoryIterator *i; 563 status_t status = GetDirectoryIterator(&i); 564 if (status != B_OK) 565 return status; 566 567 ino_t entryId; 568 uint32 length = B_FILE_NAME_LENGTH; 569 char name[B_FILE_NAME_LENGTH]; 570 571 bool foundIt = false; 572 while (i->GetNextEntry(name, &length, &entryId) == B_OK) { 573 if (strcmp(filename, name) == 0) { 574 foundIt = true; 575 break; 576 } 577 578 // reset overwritten length 579 length = B_FILE_NAME_LENGTH; 580 } 581 582 if (foundIt) 583 *id = entryId; 584 else 585 status = B_ENTRY_NOT_FOUND; 586 587 return status; 588 } 589