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