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