From 3dc4d42634f015dae2df48fdafa76ce22f71f4f0 Mon Sep 17 00:00:00 2001 From: Tyler Dauwalder Date: Thu, 31 Jul 2003 06:59:21 +0000 Subject: [PATCH] Implemented file reading git-svn-id: file:///srv/svn/repos/haiku/trunk/current@4157 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- src/add-ons/kernel/file_systems/udf/Icb.cpp | 103 +++++++- src/add-ons/kernel/file_systems/udf/Icb.h | 259 +++++++++++++++++++- 2 files changed, 346 insertions(+), 16 deletions(-) diff --git a/src/add-ons/kernel/file_systems/udf/Icb.cpp b/src/add-ons/kernel/file_systems/udf/Icb.cpp index f4cbd78405..354c12bd6b 100644 --- a/src/add-ons/kernel/file_systems/udf/Icb.cpp +++ b/src/add-ons/kernel/file_systems/udf/Icb.cpp @@ -1,5 +1,6 @@ #include "Icb.h" +#include "AllocationDescriptorList.h" #include "DirectoryIterator.h" #include "Utils.h" #include "Volume.h" @@ -7,22 +8,24 @@ using namespace Udf; Icb::Icb(Volume *volume, udf_long_address address) - : fVolume(NULL) - , fIcbData(volume ? volume->BlockSize() : 0) + : fVolume(volume) + , fData(volume) , fInitStatus(B_NO_INIT) , fId(to_vnode_id(address)) + , fFileEntry(&fData) + , fExtendedEntry(&fData) { - DEBUG_INIT_ETC(CF_PUBLIC, "Icb", ("Volume*(%p), long_address(block: %ld, partition: %d, length: %ld)", volume, address.block(), address.partition(), address.length())); + DEBUG_INIT_ETC(CF_PUBLIC, "Icb", ("volume: %p, address(block: %ld, " + "partition: %d, length: %ld)", volume, address.block(), + address.partition(), address.length())); status_t err = volume ? B_OK : B_BAD_VALUE; if (!err) { - err = fIcbData.InitCheck(); + off_t block; + err = fVolume->MapBlock(address, &block); if (!err) { - err = volume->Read(address, fIcbData.Size(), fIcbData.Data()); - if (!err) { - udf_icb_header *header = reinterpret_cast(fIcbData.Data()); - PDUMP(header); - err = header->tag().init_check(address.block()); - } + udf_icb_header *header = reinterpret_cast(fData.SetTo(block)); + PDUMP(header); + err = header->tag().init_check(address.block()); } } fInitStatus = err; @@ -34,6 +37,51 @@ Icb::InitCheck() return fInitStatus; } +status_t +Icb::Read(off_t pos, void *buffer, size_t *length, uint32 *block) +{ + DEBUG_INIT_ETC(CF_PUBLIC | CF_HIGH_VOLUME, "Icb", + ("pos: %lld, buffer: %p, length: (%p)->%ld", pos, buffer, length, (length ? *length : 0))); + + if (!buffer || !length || uint64(pos) >= Length()) + RETURN(B_BAD_VALUE); + + switch (IcbTag().descriptor_flags()) { + case ICB_DESCRIPTOR_TYPE_SHORT: { + PRINT(("descriptor type: short\n")); + AllocationDescriptorList list(this, ShortDescriptorAccessor(0)); + RETURN(_Read(list, pos, buffer, length, block)); + break; + } + + case ICB_DESCRIPTOR_TYPE_LONG: { + PRINT(("descriptor type: long\n")); + AllocationDescriptorList list(this); + RETURN(_Read(list, pos, buffer, length, block)); + break; + } + + case ICB_DESCRIPTOR_TYPE_EXTENDED: { + PRINT(("descriptor type: extended\n")); +// AllocationDescriptorList list(this, ExtendedDescriptorAccessor(0)); +// RETURN(_Read(list, pos, buffer, length, block)); + RETURN(B_ERROR); + break; + } + + case ICB_DESCRIPTOR_TYPE_EMBEDDED: { + PRINT(("descriptor type: embedded\n")); + RETURN(B_ERROR); + break; + } + + default: + PRINT(("Invalid icb descriptor flags! (flags = %d)\n", IcbTag().descriptor_flags())); + RETURN(B_BAD_VALUE); + break; + } +} + status_t Icb::GetDirectoryIterator(DirectoryIterator **iterator) { @@ -51,4 +99,37 @@ Icb::GetDirectoryIterator(DirectoryIterator **iterator) return err; } - +status_t +Icb::Find(const char *filename, vnode_id *id) +{ + DEBUG_INIT_ETC(CF_PUBLIC | CF_DIRECTORY_OPS | CF_HIGH_VOLUME, "Icb", + ("filename: `%s', id: %p", filename, id)); + + if (!filename || !id) + RETURN(B_BAD_VALUE); + + DirectoryIterator *i; + status_t err = GetDirectoryIterator(&i); + if (!err) { + vnode_id entryId; + uint32 length = B_FILE_NAME_LENGTH; + char name[B_FILE_NAME_LENGTH]; + + bool foundIt = false; + while (i->GetNextEntry(name, &length, &entryId) == B_OK) + { + if (strcmp(filename, name) == 0) { + foundIt = true; + break; + } + } + + if (foundIt) { + *id = entryId; + } else { + err = B_ENTRY_NOT_FOUND; + } + } + + RETURN(err); +} diff --git a/src/add-ons/kernel/file_systems/udf/Icb.h b/src/add-ons/kernel/file_systems/udf/Icb.h index e3eb5c8e9e..3da853d6a6 100644 --- a/src/add-ons/kernel/file_systems/udf/Icb.h +++ b/src/add-ons/kernel/file_systems/udf/Icb.h @@ -21,8 +21,8 @@ extern "C" { #include "cpp.h" #include "UdfDebug.h" +#include "CachedBlock.h" #include "DiskStructures.h" -#include "MemoryChunk.h" #include "SinglyLinkedList.h" namespace Udf { @@ -30,6 +30,36 @@ namespace Udf { class DirectoryIterator; class Volume; +/*! \brief Abstract interface to file entry structure members + that are not commonly accessible through udf_file_icb_entry(). + + This is necessary, since we can't use virtual functions in + the disk structure structs themselves, since we generally + don't create disk structure objects by calling new, but + rather just cast a chunk of memory read off disk to be + a pointer to the struct of interest (which works fine + for regular functions, but fails miserably for virtuals + due to the vtable not being setup properly). +*/ +class AbstractFileEntry { +public: + virtual uint8* AllocationDescriptors() = 0; + virtual uint32 AllocationDescriptorsLength() = 0; +}; + +template +class FileEntry : public AbstractFileEntry { +public: + FileEntry(CachedBlock *descriptorBlock = NULL); + void SetTo(CachedBlock *descriptorBlock); + virtual uint8* AllocationDescriptors(); + virtual uint32 AllocationDescriptorsLength(); +private: + Descriptor* _Descriptor(); + + CachedBlock *fDescriptorBlock; +}; + class Icb { public: Icb(Volume *volume, udf_long_address address); @@ -42,24 +72,243 @@ public: bool IsDirectory() { return InitCheck() == B_OK && (Type() == ICB_TYPE_DIRECTORY || Type() == ICB_TYPE_STREAM_DIRECTORY); } - // directories + uint32 Uid() { return 0; }//FileEntry()->uid(); } + uint32 Gid() { return 0; } + uint16 FileLinkCount() { return FileEntry()->file_link_count(); } + uint64 Length() { return FileEntry()->information_length(); } + mode_t Mode() { return (IsDirectory() ? S_IFDIR : S_IFREG) | S_IRUSR | S_IRGRP | S_IROTH; } + + uint8 *AllocationDescriptors() { return AbstractEntry()->AllocationDescriptors(); } + uint32 AllocationDescriptorsSize() { return AbstractEntry()->AllocationDescriptorsLength(); } + + status_t Read(off_t pos, void *buffer, size_t *length, uint32 *block = NULL); + + // for directories only status_t GetDirectoryIterator(DirectoryIterator **iterator); + status_t Find(const char *filename, vnode_id *id); + + Volume* GetVolume() const { return fVolume; } private: Icb(); // unimplemented - - udf_icb_entry_tag& IcbTag() { return (reinterpret_cast(fIcbData.Data()))->icb_tag(); } + + udf_tag& Tag() { return (reinterpret_cast(fData.Block()))->tag(); } + udf_icb_entry_tag& IcbTag() { return (reinterpret_cast(fData.Block()))->icb_tag(); } + AbstractFileEntry* AbstractEntry() { + DEBUG_INIT(CF_PRIVATE | CF_HIGH_VOLUME, "Icb"); + return (Tag().id() == TAGID_EXTENDED_FILE_ENTRY) +// ? reinterpret_cast(fData.Block()) +// : reinterpret_cast(fData.Block())); + ? &fExtendedEntry + : &fFileEntry; + } + udf_file_icb_entry* FileEntry() { return (reinterpret_cast(fData.Block())); } + udf_extended_file_icb_entry& ExtendedEntry() { return *(reinterpret_cast(fData.Block())); } + template + status_t _Read(DescriptorList &list, off_t pos, void *buffer, size_t *length, uint32 *block); private: Volume *fVolume; - MemoryChunk fIcbData; + CachedBlock fData; status_t fInitStatus; vnode_id fId; SinglyLinkedList > fIteratorList; + FileEntry fFileEntry; + FileEntry fExtendedEntry; }; +/*! \brief Does the dirty work of reading using the given DescriptorList object + to access the allocation descriptors properly. +*/ +template +status_t +Icb::_Read(DescriptorList &list, off_t pos, void *_buffer, size_t *length, uint32 *block) +{ + DEBUG_INIT_ETC(CF_PRIVATE | CF_HIGH_VOLUME, "Icb", ("list: %p, pos: %lld, buffer: %p, length: (%p)->%ld", + &list, pos, _buffer, length, (length ? *length : 0))); + if (!_buffer || !length) + RETURN(B_BAD_VALUE); + + uint64 bytesLeftInFile = uint64(pos) > Length() ? 0 : Length() - pos; + size_t bytesLeft = (*length >= bytesLeftInFile) ? bytesLeftInFile : *length; + size_t bytesRead = 0; + + Volume *volume = GetVolume(); + status_t err = B_OK; + uint8 *buffer = reinterpret_cast(_buffer); + bool isFirstBlock = true; + + while (bytesLeft > 0 && !err) { + + PRINT(("pos: %lld\n", pos)); + PRINT(("bytesLeft: %ld\n", bytesLeft)); + + udf_long_address extent; + bool isEmpty = false; + err = list.FindExtent(pos, &extent, &isEmpty); + if (!err) { + PRINT(("found extent for offset %lld: (block: %ld, partition: %d, length: %ld, type: %d)\n", + pos, extent.block(), extent.partition(), extent.length(), extent.type())); + + switch (extent.type()) { + case EXTENT_TYPE_RECORDED: + isEmpty = false; + break; + + case EXTENT_TYPE_ALLOCATED: + case EXTENT_TYPE_UNALLOCATED: + isEmpty = true; + break; + + default: + PRINT(("Invalid extent type found: %d\n", extent.type())); + err = B_ERROR; + break; + } + + if (!err) { + // Note the unmapped first block of the total read in + // the block output parameter if provided + if (isFirstBlock) { + isFirstBlock = false; + if (block) + *block = extent.block(); + } + + off_t blockOffset = pos - off_t((pos >> volume->BlockShift()) << volume->BlockShift()); + size_t fullBlocksLeft = bytesLeft >> volume->BlockShift(); + + if (fullBlocksLeft > 0 && blockOffset == 0) { + PRINT(("reading full block (or more)\n")); + // Block aligned and at least one full block left. Read in using + // cached_read() calls. + off_t diskBlock; + err = volume->MapBlock(extent, &diskBlock); + if (!err) { + size_t fullBlockBytesLeft = fullBlocksLeft << volume->BlockShift(); + size_t readLength = fullBlockBytesLeft < extent.length() + ? fullBlockBytesLeft + : extent.length(); + + if (isEmpty) { + PRINT(("reading %ld empty bytes as zeros\n", readLength)); + memset(buffer, 0, readLength); + } else { + off_t diskBlock; + err = volume->MapBlock(extent, &diskBlock); + if (!err) { + PRINT(("reading %ld bytes from disk block %lld using cached_read()\n", + readLength, diskBlock)); + err = cached_read(volume->Device(), diskBlock, buffer, + readLength >> volume->BlockShift(), + volume->BlockSize()); + } + } + + if (!err) { + bytesLeft -= readLength; + bytesRead += readLength; + pos += readLength; + buffer += readLength; + } + } + + } else { + PRINT(("partial block\n")); + off_t partialOffset; + size_t partialLength; + if (blockOffset == 0) { + // Block aligned, but only a partial block's worth remaining. Read + // in remaining bytes of file + partialOffset = 0; + partialLength = bytesLeft; + } else { + // Not block aligned, so just read up to the next block boundary. + partialOffset = blockOffset; + partialLength = volume->BlockSize() - blockOffset; + if (bytesLeft < partialLength) + partialLength = bytesLeft; + } + + PRINT(("partialOffset: %lld\n", partialOffset)); + PRINT(("partialLength: %ld\n", partialLength)); + + if (isEmpty) { + PRINT(("reading %ld empty bytes as zeros\n", partialLength)); + memset(buffer, 0, partialLength); + } else { + off_t diskBlock; + err = volume->MapBlock(extent, &diskBlock); + if (!err) { + PRINT(("reading %ld bytes from disk block %lld using get_block()\n", + partialLength, diskBlock)); + uint8 *data = (uint8*)get_block(volume->Device(), diskBlock, volume->BlockSize()); + err = data ? B_OK : B_BAD_DATA; + if (!err) { + memcpy(buffer, data+partialOffset, partialLength); + release_block(volume->Device(), diskBlock); + } + } + } + + if (!err) { + bytesLeft -= partialLength; + bytesRead += partialLength; + pos += partialLength; + buffer += partialLength; + } + } + } + } else { + PRINT(("error finding extent for offset %lld: 0x%lx, `%s'", pos, + err, strerror(err))); + break; + } + } + + *length = bytesRead; + + RETURN(err); +} + +template +FileEntry::FileEntry(CachedBlock *descriptorBlock) + : fDescriptorBlock(descriptorBlock) +{ +} + +template +void +FileEntry::SetTo(CachedBlock *descriptorBlock) +{ + fDescriptorBlock = descriptorBlock; +} + +template +uint8* +FileEntry::AllocationDescriptors() +{ + Descriptor* descriptor = _Descriptor(); + return descriptor ? descriptor->allocation_descriptors() : NULL; +} + +template +uint32 +FileEntry::AllocationDescriptorsLength() +{ + Descriptor* descriptor = _Descriptor(); + return descriptor ? descriptor->allocation_descriptors_length() : 0; +} + +template +Descriptor* +FileEntry::_Descriptor() +{ + return fDescriptorBlock ? reinterpret_cast(fDescriptorBlock->Block()) : NULL; +} + }; // namespace Udf #endif // _UDF_ICB_H