diff --git a/src/tests/add-ons/kernel/file_systems/udf/r5/AllocationDescriptorList.h b/src/tests/add-ons/kernel/file_systems/udf/r5/AllocationDescriptorList.h new file mode 100644 index 0000000000..a5e114f4a0 --- /dev/null +++ b/src/tests/add-ons/kernel/file_systems/udf/r5/AllocationDescriptorList.h @@ -0,0 +1,261 @@ +//---------------------------------------------------------------------- +// This software is part of the OpenBeOS distribution and is covered +// by the OpenBeOS license. +// +// Copyright (c) 2003 Tyler Dauwalder, tyler@dauwalder.net +//--------------------------------------------------------------------- +#ifndef _UDF_ALLOCATION_DESCRIPTOR_LIST_H +#define _UDF_ALLOCATION_DESCRIPTOR_LIST_H + +/*! \file AllocationDescriptorList.h +*/ + +#include "kernel_cpp.h" +#include "UdfDebug.h" + +#include "UdfStructures.h" +#include "Icb.h" +#include "Volume.h" + +namespace Udf { + +/*! \brief Common interface for dealing with the three standard + forms of allocation descriptors used in UDF icbs. + + The \c Accessor class is an allocation descriptor accessor class + for the allocation scheme of interest. Instances of it should be + passable by value, and should define the following public members: + - typedef DescriptorType; + - inline uint8 GetType(DescriptorType &descriptor); + - inline uint32 GetBlock(DescriptorType &descriptor); + - inline uint16 GetPartition(DescriptorType &descriptor); + - inline uint32 GetLength(DescriptorType &descriptor); +*/ +template +class AllocationDescriptorList { +private: + typedef typename Accessor::DescriptorType Descriptor; +public: + AllocationDescriptorList(Icb *icb, Accessor accessor = Accessor()) + : fIcb(icb) + , fVolume(icb->GetVolume()) + , fIcbDescriptors(reinterpret_cast(icb->AllocationDescriptors())) + , fIcbDescriptorsSize(icb->AllocationDescriptorsSize()) + , fAdditionalDescriptors(icb->GetVolume()) + , fReadFromIcb(true) + , fAccessor(accessor) + , fDescriptorIndex(0) + , fDescriptorNumber(0) + , fBlockIndex(0) + { + DEBUG_INIT("AllocationDescriptorList<>"); + _WalkContinuationChain(_CurrentDescriptor()); + } + + /*! \brief Finds the extent for the given address in the stream, + returning it in the address pointed to by \a blockRun. + + \param start The byte address of interest + \param extent The extent containing the stream address given + by \c start. + \param isEmpty If set to true, indicates that the given extent is unrecorded + and thus its contents should be interpreted as all zeros. + */ + status_t FindExtent(off_t start, long_address *extent, bool *isEmpty) { + DEBUG_INIT_ETC("AllocationDescriptorList<>", + ("start: %Ld, extent: %p, isEmpty: %p", start, extent, isEmpty)); + off_t startBlock = start >> fVolume->BlockShift(); + + // This should never have to happen, as FindExtent is only called by + // Icb::_Read() sequentially as a file read is performed, but you + // never know. :-) + if (startBlock < _BlockIndex()) + _Rewind(); + + status_t error = B_OK; + while (true) { + Descriptor *descriptor = _CurrentDescriptor(); + if (descriptor) { + if (_BlockIndex() <= startBlock + && startBlock < _BlockIndex()+fAccessor.GetLength(*descriptor)) + { + // The start block is somewhere in this extent, so return + // the applicable tail end portion. + off_t offset = startBlock - _BlockIndex(); + extent->set_block(fAccessor.GetBlock(*descriptor)+offset); + extent->set_partition(fAccessor.GetPartition(*descriptor)); + extent->set_length(fAccessor.GetLength(*descriptor)-(offset*fVolume->BlockSize())); + extent->set_type(fAccessor.GetType(*descriptor)); + break; + } else { + _MoveToNextDescriptor(); + } + } else { + PRINT(("Descriptor #%ld found NULL\n", _DescriptorNumber())); + error = B_ERROR; + break; + } + } + RETURN(error); + } + +private: + + Descriptor* _CurrentDescriptor() const { + DEBUG_INIT("AllocationDescriptorList<>"); + PRINT(("(_DescriptorIndex()+1)*sizeof(Descriptor) = %ld\n", (_DescriptorIndex()+1)*sizeof(Descriptor))); + PRINT(("_DescriptorArraySize() = %ld\n", _DescriptorArraySize())); + PRINT(("_DescriptorArray() = %p\n", _DescriptorArray())); + return ((_DescriptorIndex()+1)*sizeof(Descriptor) <= _DescriptorArraySize()) + ? &(_DescriptorArray()[_DescriptorIndex()]) + : NULL; + } + + status_t _MoveToNextDescriptor() { + DEBUG_INIT("AllocationDescriptorList<>"); + + Descriptor* descriptor = _CurrentDescriptor(); + if (!descriptor) { + RETURN(B_ENTRY_NOT_FOUND); + } else { + // Increment our indices and get the next descriptor + // from this extent. + fBlockIndex += fAccessor.GetLength(*descriptor); + fDescriptorIndex++; + fDescriptorNumber++; + descriptor = _CurrentDescriptor(); + + // If no such descriptor exists, we've run out of + // descriptors in this extent, and we're done. The + // next time _CurrentDescriptor() is called, it will + // return NULL, signifying this. Otherwise, we have to + // see if the new descriptor identifies the next extent + // of allocation descriptors, in which case we have to + // load up the appropriate extent (guaranteed to be at + // most one block in length by UDF-2.01 5.1 and UDF-2.01 + // 2.3.11). + _WalkContinuationChain(descriptor); + } + + + RETURN(B_ERROR); + } + + void _WalkContinuationChain(Descriptor *descriptor) { + DEBUG_INIT_ETC("AllocationDescriptorList<>", + ("descriptor: %p", descriptor)); + if (descriptor && fAccessor.GetType(*descriptor) == EXTENT_TYPE_CONTINUATION) { + // Load the new block, make sure we're not trying + // to read from the icb descriptors anymore, and + // reset the descriptor index. + fAdditionalDescriptors.SetTo(fAccessor, *descriptor); + fReadFromIcb = false; + fDescriptorIndex = 0; + + // Make sure that the first descriptor in this extent isn't + // another continuation. That would be stupid, but not + // technically illegal. + _WalkContinuationChain(_CurrentDescriptor()); + + } + + + } + + void _Rewind() { + fDescriptorIndex = 0; + fDescriptorNumber = 0; + fReadFromIcb = true; + } + + Descriptor *_DescriptorArray() const { + return fReadFromIcb + ? fIcbDescriptors + : reinterpret_cast(fAdditionalDescriptors.Block()); + } + + size_t _DescriptorArraySize() const { + return fReadFromIcb ? fIcbDescriptorsSize : fAdditionalDescriptors.BlockSize(); + } + + int32 _DescriptorIndex() const { + return fDescriptorIndex; + } + + int32 _DescriptorNumber() const { + return fDescriptorNumber; + } + + off_t _BlockIndex() const { + return fBlockIndex; + } + + Icb *fIcb; + Volume *fVolume; + Descriptor *fIcbDescriptors; + int32 fIcbDescriptorsSize; + CachedBlock fAdditionalDescriptors; + bool fReadFromIcb; + + Accessor fAccessor; + int32 fDescriptorIndex; + int32 fDescriptorNumber; + off_t fBlockIndex; + +}; + +// Accessors + +class ShortDescriptorAccessor { +public: + ShortDescriptorAccessor(uint16 partition) + : fPartition(partition) + { + } + + typedef short_address DescriptorType; + + inline uint8 GetType(DescriptorType &descriptor) const { + return descriptor.type(); + } + + inline uint32 GetBlock(DescriptorType &descriptor) const { + return descriptor.block(); + } + + inline uint16 GetPartition(DescriptorType &descriptor) const { + return fPartition; + } + + inline uint32 GetLength(DescriptorType &descriptor) const { + return descriptor.length(); + } +private: + uint16 fPartition; +}; + +class LongDescriptorAccessor { +public: + typedef long_address DescriptorType; + + inline uint8 GetType(DescriptorType &descriptor) const { + return descriptor.type(); + } + + inline uint32 GetBlock(DescriptorType &descriptor) const { + return descriptor.block(); + } + + inline uint16 GetPartition(DescriptorType &descriptor) const { + return descriptor.partition(); + } + + inline uint32 GetLength(DescriptorType &descriptor) const { + return descriptor.length(); + } +}; + + +}; // namespace Udf + +#endif // _UDF_ALLOCATION_DESCRIPTOR_LIST_H diff --git a/src/tests/add-ons/kernel/file_systems/udf/r5/Array.h b/src/tests/add-ons/kernel/file_systems/udf/r5/Array.h new file mode 100644 index 0000000000..cc9a25c7ba --- /dev/null +++ b/src/tests/add-ons/kernel/file_systems/udf/r5/Array.h @@ -0,0 +1,93 @@ +//---------------------------------------------------------------------- +// This software is part of the OpenBeOS distribution and is covered +// by the OpenBeOS license. +// +// Copyright (c) 2003 Tyler Dauwalder, tyler@dauwalder.net +//--------------------------------------------------------------------- + +#ifndef _UDF_ARRAY_H +#define _UDF_ARRAY_H + +#include "kernel_cpp.h" + +#include "SupportDefs.h" +#include "UdfDebug.h" + +namespace Udf { + +/*! \brief Slightly more typesafe static array type than built-in arrays, + with array length information stored implicitly (i.e. consuming no + physical space in the actual struct) via the \c arrayLength template + parameter. +*/ +template +struct array { +public: + void dump() const { + for (uint32 i = 0; i < arrayLength; i++) + data[i].print(); + } + + uint32 length() const { return arrayLength; } + uint32 size() const { return arrayLength * sizeof(DataType); } + + // This doesn't appear to work. I don't know why. + DataType operator[] (int index) const { return data[index]; } + + DataType data[arrayLength]; +}; + +/*! \brief \c uint8 specialization of the \c array template struct. +*/ +template +struct array { + void dump() const + { + const uint8 bytesPerRow = 8; + char classname[40]; + sprintf(classname, "array", arrayLength); + + DUMP_INIT(classname); + + for (uint32 i = 0; i < arrayLength; i++) { + if (i % bytesPerRow == 0) + PRINT(("[%ld:%ld]: ", i, i+bytesPerRow-1)); + SIMPLE_PRINT(("0x%.2x ", data[i])); + if ((i+1) % bytesPerRow == 0 || i+1 == arrayLength) + SIMPLE_PRINT(("\n")); + } + } + uint32 length() const { return arrayLength; } + uint32 size() const { return arrayLength; } + uint8 data[arrayLength]; +}; + +/*! \brief \c char specialization of the \c array template struct. +*/ +template +struct array { + void dump() const + { + const uint8 bytesPerRow = 8; + char classname[40]; + sprintf(classname, "array", arrayLength); + + DUMP_INIT(classname); + + for (uint32 i = 0; i < arrayLength; i++) { + if (i % bytesPerRow == 0) + PRINT(("[%ld:%ld]: ", i, i+bytesPerRow-1)); + SIMPLE_PRINT(("0x%.2x ", data[i])); + if ((i+1) % bytesPerRow == 0 || i+1 == arrayLength) + SIMPLE_PRINT(("\n")); + } + } + uint32 length() const { return arrayLength; } + uint32 size() const { return arrayLength; } + uint8 data[arrayLength]; +}; + + +}; // namespace UDF + +#endif // _UDF_ARRAY_H diff --git a/src/tests/add-ons/kernel/file_systems/udf/r5/CachedBlock.h b/src/tests/add-ons/kernel/file_systems/udf/r5/CachedBlock.h new file mode 100644 index 0000000000..b59369926a --- /dev/null +++ b/src/tests/add-ons/kernel/file_systems/udf/r5/CachedBlock.h @@ -0,0 +1,159 @@ +//---------------------------------------------------------------------- +// This software is part of the OpenBeOS distribution and is covered +// by the OpenBeOS license. +// +// Copyright (c) 2003 Tyler Dauwalder, tyler@dauwalder.net +// Based on the CachedBlock class from OpenBFS, +// Copyright (c) 2002 Axel Dörfler, axeld@pinc-software.de +//--------------------------------------------------------------------- +#ifndef _UDF_CACHED_BLOCK_H +#define _UDF_CACHED_BLOCK_H + +/*! \file CachedBlock.h + + Based on the CachedBlock class from OpenBFS, written by + Axel Dörfler, axeld@pinc-software.de +*/ + +#ifdef COMPILE_FOR_R5 +extern "C" { +#endif + #include "fsproto.h" +#ifdef COMPILE_FOR_R5 +} +#endif + +extern "C" { + #ifndef _IMPEXP_KERNEL + # define _IMPEXP_KERNEL + #endif + + #include "lock.h" + #include "cache.h" +} + +#include "kernel_cpp.h" +#include "UdfDebug.h" + +#include "UdfStructures.h" +#include "Volume.h" + +namespace Udf { + +class CachedBlock { + public: + CachedBlock(Volume *volume); + CachedBlock(Volume *volume, off_t block, bool empty = false); + CachedBlock(CachedBlock *cached); + ~CachedBlock(); + + inline void Keep(); + inline void Unset(); + inline uint8 *SetTo(off_t block, bool empty = false); + inline uint8 *SetTo(long_address address, bool empty = false); + template + inline uint8* SetTo(Accessor &accessor, Descriptor &descriptor, + bool empty = false); + + uint8 *Block() const { return fBlock; } + off_t BlockNumber() const { return fBlockNumber; } + uint32 BlockSize() const { return fVolume->BlockSize(); } + uint32 BlockShift() const { return fVolume->BlockShift(); } + + private: + CachedBlock(const CachedBlock &); // unimplemented + CachedBlock &operator=(const CachedBlock &); // unimplemented + + protected: + Volume *fVolume; + off_t fBlockNumber; + uint8 *fBlock; +}; + +inline +CachedBlock::CachedBlock(Volume *volume) + : + fVolume(volume), + fBlock(NULL) +{ +} + +inline +CachedBlock::CachedBlock(Volume *volume, off_t block, bool empty = false) + : + fVolume(volume), + fBlock(NULL) +{ + SetTo(block, empty); +} + +inline +CachedBlock::CachedBlock(CachedBlock *cached) + : fVolume(cached->fVolume) + , fBlockNumber(cached->BlockNumber()) + , fBlock(cached->fBlock) +{ + cached->Keep(); +} + +inline +CachedBlock::~CachedBlock() +{ + Unset(); +} + +inline void +CachedBlock::Keep() +{ + fBlock = NULL; +} + +inline void +CachedBlock::Unset() +{ + DEBUG_INIT("CachedBlock"); + if (fBlock) { + PRINT(("releasing block #%Ld\n", BlockNumber())); + release_block(fVolume->Device(), fBlockNumber); + } else { + PRINT(("no block to release\n")); + } +} + +inline uint8 * +CachedBlock::SetTo(off_t block, bool empty = false) +{ + DEBUG_INIT_ETC("CachedBlock", ("block: %Ld, empty: %s", + block, (empty ? "true" : "false"))); + Unset(); + fBlockNumber = block; + PRINT(("getting block #%Ld\n", block)); + return fBlock = empty ? (uint8 *)get_empty_block(fVolume->Device(), block, BlockSize()) + : (uint8 *)get_block(fVolume->Device(), block, BlockSize()); +} + +inline uint8 * +CachedBlock::SetTo(long_address address, bool empty = false) +{ + off_t block; + if (fVolume->MapBlock(address, &block) == B_OK) + return SetTo(block, empty); + else + return NULL; +} + +template +inline uint8* +CachedBlock::SetTo(Accessor &accessor, Descriptor &descriptor, bool empty = false) +{ + // Make a long_address out of the descriptor and call it a day + long_address address; + address.set_to(accessor.GetBlock(descriptor), + accessor.GetPartition(descriptor)); + return SetTo(address, empty); +} + +}; // namespace Udf + +#endif // _UDF_CACHED_BLOCK_H + diff --git a/src/tests/add-ons/kernel/file_systems/udf/r5/DString.cpp b/src/tests/add-ons/kernel/file_systems/udf/r5/DString.cpp new file mode 100644 index 0000000000..b9497b6c1b --- /dev/null +++ b/src/tests/add-ons/kernel/file_systems/udf/r5/DString.cpp @@ -0,0 +1,114 @@ +#include "DString.h" + +#include + +using namespace Udf; + +/*! \brief Creates a useless, empty string object. +*/ +DString::DString() + : fString(NULL) + , fLength(0) +{ +} + +/*! \brief Create a new DString object that is a copy of \a ref. +*/ +DString::DString(const DString &ref) + : fString(NULL) + , fLength(0) +{ + SetTo(ref); +} + +/*! \brief Creates a new DString \a fieldLength bytes long that contains + at most the first \c (fieldLength-1) bytes of \a string.Cs0(). +*/ +DString::DString(const Udf::String &string, uint8 fieldLength) + : fString(NULL) + , fLength(0) +{ + SetTo(string, fieldLength); +} + +/*! \brief Creates a new DString \a fieldLength bytes long that contains + at most the first \c (fieldLength-1) bytes of the Cs0 representation + of the NULL-terminated UTF8 string \a utf8. +*/ +DString::DString(const char *utf8, uint8 fieldLength) + : fString(NULL) + , fLength(0) +{ + SetTo(utf8, fieldLength); +} + +void +DString::SetTo(const DString &ref) +{ + _Clear(); + if (ref.Length() > 0) { + fString = new(nothrow) uint8[ref.Length()]; + if (fString) { + fLength = ref.Length(); + memcpy(fString, ref.String(), fLength); + } + } +} + +/*! \brief Sets the DString be \a fieldLength bytes long and contain + at most the first \c (fieldLength-1) bytes of \a string.Cs0(). +*/ +void +DString::SetTo(const Udf::String &string, uint8 fieldLength) +{ + _Clear(); + if (fieldLength > 0) { + // Allocate our string + fString = new(nothrow) uint8[fieldLength]; + status_t error = fString ? B_OK : B_NO_MEMORY; + if (!error) { + // Figure out how many bytes to copy + uint32 sourceLength = string.Cs0Length(); + if (sourceLength > 0) { + uint8 destLength = sourceLength > uint8(fieldLength-1) + ? uint8(fieldLength-1) + : uint8(sourceLength); + // If the source string is 16-bit unicode, make sure any dangling + // half-character at the end of the string is not copied + if (string.Cs0()[1] == '\x10' && destLength > 0 && destLength % 2 == 0) + destLength--; + // Copy + memcpy(fString, string.Cs0(), destLength); + // Zero any characters between the end of the string and + // the terminating string length character + if (destLength < fieldLength-1) + memset(&fString[destLength], 0, fieldLength-1-destLength); + // Write the string length to the last character in the field + fString[fieldLength-1] = destLength; + } else { + // Empty strings are to contain all zeros + memset(fString, 0, fieldLength); + } + } + } +} + +/*! \brief Sets the DString be \a fieldLength bytes long and contain + at most the first \c (fieldLength-1) bytes of the Cs0 representation + of the NULL-terminated UTF8 string \a utf8. +*/ +void +DString::SetTo(const char *utf8, uint8 fieldLength) +{ + Udf::String string(utf8); + SetTo(string, fieldLength); +} + +void +DString::_Clear() +{ + DEBUG_INIT("DString"); + delete [] fString; + fString = NULL; + fLength = 0; +} diff --git a/src/tests/add-ons/kernel/file_systems/udf/r5/DString.h b/src/tests/add-ons/kernel/file_systems/udf/r5/DString.h new file mode 100644 index 0000000000..c996202ae2 --- /dev/null +++ b/src/tests/add-ons/kernel/file_systems/udf/r5/DString.h @@ -0,0 +1,47 @@ +//---------------------------------------------------------------------- +// This software is part of the OpenBeOS distribution and is covered +// by the OpenBeOS license. +// +// Copyright (c) 2003 Tyler Dauwalder, tyler@dauwalder.net +//--------------------------------------------------------------------- + +#ifndef _D_STRING_H +#define _D_STRING_H + +#include "kernel_cpp.h" +#include "UdfString.h" +#include "UdfDebug.h" + +namespace Udf { + +/*! \brief Fixed-length d-string class that takes a Udf::String as input + and provides a properly formatted ECMA-167 d-string of the given + field length as ouput. + + For d-string info, see: ECMA-167 1/7.2.12, UDF-2.50 2.1.3 +*/ +class DString { +public: + DString(); + DString(const DString &ref); + DString(const Udf::String &string, uint8 fieldLength); + DString(const char *utf8, uint8 fieldLength); + + void SetTo(const DString &ref); + void SetTo(const Udf::String &string, uint8 fieldLength); + void SetTo(const char *utf8, uint8 fieldLength); + + const uint8* String() const { return fString; } + uint8 Length() const { return fLength; } +private: + void _Clear(); + + uint8 *fString; + uint8 fLength; +}; + +}; // namespace UDF + + + +#endif // _D_STRING_H diff --git a/src/tests/add-ons/kernel/file_systems/udf/r5/DirectoryIterator.cpp b/src/tests/add-ons/kernel/file_systems/udf/r5/DirectoryIterator.cpp new file mode 100644 index 0000000000..64fdfcaf4a --- /dev/null +++ b/src/tests/add-ons/kernel/file_systems/udf/r5/DirectoryIterator.cpp @@ -0,0 +1,105 @@ +//---------------------------------------------------------------------- +// This software is part of the OpenBeOS distribution and is covered +// by the OpenBeOS license. +// +// Copyright (c) 2003 Tyler Dauwalder, tyler@dauwalder.net +//--------------------------------------------------------------------- + +/*! \file DirectoryIterator.cpp +*/ + +#include "DirectoryIterator.h" + +#include +#include + +#include "Icb.h" + +#include "UdfString.h" +#include "Utils.h" + +using namespace Udf; + +status_t +DirectoryIterator::GetNextEntry(char *name, uint32 *length, vnode_id *id) +{ + DEBUG_INIT_ETC("DirectoryIterator", + ("name: %p, length: %p, id: %p", name, length, id)); + + if (!id || !name || !length) + return B_BAD_VALUE; + + PRINT(("fPosition: %Ld\n", fPosition)); + PRINT(("Parent()->Length(): %Ld\n", Parent()->Length())); + + + status_t error = B_OK; + if (fAtBeginning) { + sprintf(name, "."); + *length = 2; + *id = Parent()->Id(); + fAtBeginning = false; + } else { + + if (uint64(fPosition) >= Parent()->Length()) + RETURN(B_ENTRY_NOT_FOUND); + + uint8 data[kMaxFileIdSize]; + file_id_descriptor *entry = reinterpret_cast(data); + + uint32 block = 0; + off_t offset = fPosition; + + size_t entryLength = kMaxFileIdSize; + // First read in the static portion of the file id descriptor, + // then, based on the information therein, read in the variable + // length tail portion as well. + error = Parent()->Read(offset, entry, &entryLength, &block); + if (!error && entryLength >= sizeof(file_id_descriptor) && entry->tag().init_check(block) == B_OK) { + PDUMP(entry); + offset += entry->total_length(); + + if (entry->is_parent()) { + sprintf(name, ".."); + *length = 3; + } else { + String string(entry->id(), entry->id_length()); + PRINT(("id == `%s'\n", string.Utf8())); + DUMP(entry->icb()); + sprintf(name, "%s", string.Utf8()); + *length = string.Utf8Length(); + } + *id = to_vnode_id(entry->icb()); + } + + if (!error) + fPosition = offset; + } + + RETURN(error); +} + +/* \brief Rewinds the iterator to point to the first + entry in the directory. +*/ +void +DirectoryIterator::Rewind() +{ + fPosition = 0; + fAtBeginning = true; +} + +DirectoryIterator::DirectoryIterator(Icb *parent) + : fParent(parent) + , fPosition(0) + , fAtBeginning(true) +{ +} + +void +DirectoryIterator::Invalidate() +{ + fParent = NULL; +} + + diff --git a/src/tests/add-ons/kernel/file_systems/udf/r5/DirectoryIterator.h b/src/tests/add-ons/kernel/file_systems/udf/r5/DirectoryIterator.h new file mode 100644 index 0000000000..7b8c2dd7db --- /dev/null +++ b/src/tests/add-ons/kernel/file_systems/udf/r5/DirectoryIterator.h @@ -0,0 +1,54 @@ +//---------------------------------------------------------------------- +// This software is part of the OpenBeOS distribution and is covered +// by the OpenBeOS license. +// +// Copyright (c) 2003 Tyler Dauwalder, tyler@dauwalder.net +//--------------------------------------------------------------------- +#ifndef _UDF_DIRECTORY_ITERATOR_H +#define _UDF_DIRECTORY_ITERATOR_H + +/*! \file DirectoryIterator.h +*/ + +#ifndef _IMPEXP_KERNEL +# define _IMPEXP_KERNEL +#endif +#ifdef COMPILE_FOR_R5 +extern "C" { +#endif + #include "fsproto.h" +#ifdef COMPILE_FOR_R5 +} +#endif + +#include "kernel_cpp.h" +#include "UdfDebug.h" + +namespace Udf { + +class Icb; + +class DirectoryIterator { +public: + + status_t GetNextEntry(char *name, uint32 *length, vnode_id *id); + void Rewind(); + + Icb* Parent() { return fParent; } + const Icb* Parent() const { return fParent; } + +private: + friend class Icb; + + DirectoryIterator(); // unimplemented + DirectoryIterator(Icb *parent); // called by Icb::GetDirectoryIterator() + void Invalidate(); // called by Icb::~Icb() + + Icb *fParent; + off_t fPosition; + bool fAtBeginning; +}; + +}; // namespace Udf + +#endif // _UDF_DIRECTORY_ITERATOR_H diff --git a/src/tests/add-ons/kernel/file_systems/udf/r5/Icb.cpp b/src/tests/add-ons/kernel/file_systems/udf/r5/Icb.cpp new file mode 100644 index 0000000000..6860188c46 --- /dev/null +++ b/src/tests/add-ons/kernel/file_systems/udf/r5/Icb.cpp @@ -0,0 +1,171 @@ +//---------------------------------------------------------------------- +// This software is part of the OpenBeOS distribution and is covered +// by the OpenBeOS license. +// +// Copyright (c) 2003 Tyler Dauwalder, tyler@dauwalder.net +//--------------------------------------------------------------------- +#include "Icb.h" + +#include "time.h" + +#include "AllocationDescriptorList.h" +#include "DirectoryIterator.h" +#include "Utils.h" +#include "Volume.h" + +using namespace Udf; + +Icb::Icb(Volume *volume, long_address address) + : fVolume(volume) + , fData(volume) + , fInitStatus(B_NO_INIT) + , fId(to_vnode_id(address)) + , fFileEntry(&fData) + , fExtendedEntry(&fData) +{ + DEBUG_INIT_ETC("Icb", ("volume: %p, address(block: %ld, " + "partition: %d, length: %ld)", volume, address.block(), + address.partition(), address.length())); + status_t error = volume ? B_OK : B_BAD_VALUE; + if (!error) { + off_t block; + error = fVolume->MapBlock(address, &block); + if (!error) { + icb_header *header = reinterpret_cast(fData.SetTo(block)); + if (header->tag().id() == TAGID_FILE_ENTRY) { + file_icb_entry *entry = reinterpret_cast(header); + PDUMP(entry); + (void)entry; // warning death + } else if (header->tag().id() == TAGID_EXTENDED_FILE_ENTRY) { + extended_file_icb_entry *entry = reinterpret_cast(header); + PDUMP(entry); + (void)entry; // warning death + } else { + PDUMP(header); + } + error = header->tag().init_check(address.block()); + } + } + fInitStatus = error; + PRINT(("result: 0x%lx, `%s'\n", error, strerror(error))); +} + +status_t +Icb::InitCheck() +{ + return fInitStatus; +} + +time_t +Icb::AccessTime() +{ + return make_time(FileEntry()->access_date_and_time()); +} + +time_t +Icb::ModificationTime() +{ + return make_time(FileEntry()->modification_date_and_time()); +} + +status_t +Icb::Read(off_t pos, void *buffer, size_t *length, uint32 *block) +{ + DEBUG_INIT_ETC("Icb", + ("pos: %Ld, buffer: %p, length: (%p)->%ld", pos, buffer, length, (length ? *length : 0))); + + if (!buffer || !length || pos < 0) + RETURN(B_BAD_VALUE); + + if (uint64(pos) >= Length()) { + *length = 0; + return B_OK; + } + + 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) +{ + status_t error = iterator ? B_OK : B_BAD_VALUE; + + if (!error) { + *iterator = new(nothrow) DirectoryIterator(this); + if (*iterator) { + error = fIteratorList.PushBack(*iterator); + } else { + error = B_NO_MEMORY; + } + } + + return error; +} + +status_t +Icb::Find(const char *filename, vnode_id *id) +{ + DEBUG_INIT_ETC("Icb", + ("filename: `%s', id: %p", filename, id)); + + if (!filename || !id) + RETURN(B_BAD_VALUE); + + DirectoryIterator *i; + status_t error = GetDirectoryIterator(&i); + if (!error) { + 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 { + error = B_ENTRY_NOT_FOUND; + } + } + + RETURN(error); +} diff --git a/src/tests/add-ons/kernel/file_systems/udf/r5/Icb.h b/src/tests/add-ons/kernel/file_systems/udf/r5/Icb.h new file mode 100644 index 0000000000..d340f8330a --- /dev/null +++ b/src/tests/add-ons/kernel/file_systems/udf/r5/Icb.h @@ -0,0 +1,321 @@ +//---------------------------------------------------------------------- +// This software is part of the OpenBeOS distribution and is covered +// by the OpenBeOS license. +// +// Copyright (c) 2003 Tyler Dauwalder, tyler@dauwalder.net +//--------------------------------------------------------------------- +#ifndef _UDF_ICB_H +#define _UDF_ICB_H + +/*! \file Icb.h +*/ + +#ifndef _IMPEXP_KERNEL +# define _IMPEXP_KERNEL +#endif +#ifdef COMPILE_FOR_R5 +extern "C" { +#endif + #include "fsproto.h" +#ifdef COMPILE_FOR_R5 +} +#endif + +#include "kernel_cpp.h" +#include "UdfDebug.h" + +#include "CachedBlock.h" +#include "UdfStructures.h" +#include "SinglyLinkedList.h" + +namespace Udf { + +class DirectoryIterator; +class Volume; + +/*! \brief Abstract interface to file entry structure members + that are not commonly accessible through 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, long_address address); + status_t InitCheck(); + vnode_id Id() { return fId; } + + // categorization + uint8 Type() { return IcbTag().file_type(); } + bool IsFile() { return InitCheck() == B_OK && Type() == ICB_TYPE_REGULAR_FILE; } + bool IsDirectory() { return InitCheck() == B_OK + && (Type() == ICB_TYPE_DIRECTORY || Type() == ICB_TYPE_STREAM_DIRECTORY); } + + 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; } + time_t AccessTime(); + time_t ModificationTime(); + + 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 + + descriptor_tag & Tag() { return (reinterpret_cast(fData.Block()))->tag(); } + icb_entry_tag& IcbTag() { return (reinterpret_cast(fData.Block()))->icb_tag(); } + AbstractFileEntry* AbstractEntry() { + DEBUG_INIT("Icb"); + return (Tag().id() == TAGID_EXTENDED_FILE_ENTRY) +// ? reinterpret_cast(fData.Block()) +// : reinterpret_cast(fData.Block())); + ? &fExtendedEntry + : &fFileEntry; + } + file_icb_entry* FileEntry() { return (reinterpret_cast(fData.Block())); } + 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; + CachedBlock fData; + status_t fInitStatus; + vnode_id fId; + SinglyLinkedList fIteratorList; + /* [zooey]: gcc-2.95.3 requires the explicit namespace here, otherwise + it complains about a syntax error(!). This is most probably a bug. */ + Udf::FileEntry fFileEntry; + Udf::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("Icb", ("list: %p, pos: %Ld, 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 error = B_OK; + uint8 *buffer = reinterpret_cast(_buffer); + bool isFirstBlock = true; + + while (bytesLeft > 0 && !error) { + + PRINT(("pos: %Ld\n", pos)); + PRINT(("bytesLeft: %ld\n", bytesLeft)); + + long_address extent; + bool isEmpty = false; + error = list.FindExtent(pos, &extent, &isEmpty); + if (!error) { + PRINT(("found extent for offset %Ld: (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())); + error = B_ERROR; + break; + } + + if (!error) { + // 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; + error = volume->MapBlock(extent, &diskBlock); + if (!error) { + 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; + error = volume->MapBlock(extent, &diskBlock); + if (!error) { + PRINT(("reading %ld bytes from disk block %Ld using cached_read()\n", + readLength, diskBlock)); + error = cached_read(volume->Device(), diskBlock, buffer, + readLength >> volume->BlockShift(), + volume->BlockSize()); + } + } + + if (!error) { + 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: %Ld\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; + error = volume->MapBlock(extent, &diskBlock); + if (!error) { + PRINT(("reading %ld bytes from disk block %Ld using get_block()\n", + partialLength, diskBlock)); + uint8 *data = (uint8*)get_block(volume->Device(), diskBlock, volume->BlockSize()); + error = data ? B_OK : B_BAD_DATA; + if (!error) { + memcpy(buffer, data+partialOffset, partialLength); + release_block(volume->Device(), diskBlock); + } + } + } + + if (!error) { + bytesLeft -= partialLength; + bytesRead += partialLength; + pos += partialLength; + buffer += partialLength; + } + } + } + } else { + PRINT(("error finding extent for offset %Ld: 0x%lx, `%s'", pos, + error, strerror(error))); + break; + } + } + + *length = bytesRead; + + RETURN(error); +} + +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 diff --git a/src/tests/add-ons/kernel/file_systems/udf/r5/Jamfile b/src/tests/add-ons/kernel/file_systems/udf/r5/Jamfile new file mode 100644 index 0000000000..be8ffa3469 --- /dev/null +++ b/src/tests/add-ons/kernel/file_systems/udf/r5/Jamfile @@ -0,0 +1,69 @@ +SubDir HAIKU_TOP src add-ons kernel file_systems udf ; + +SetSubDirSupportedPlatformsBeOSCompatible ; +SubDirC++Flags -fno-rtti ; + +# save original optimization level +oldOPTIM = $(OPTIM) ; + +# set some additional defines +{ + local defines = + KEEP_WRONG_DIRENT_RECLEN + ; + + defines += COMPILE_FOR_R5 ; + + if $(DEBUG) = 0 { + # the gcc on BeOS doesn't compile BFS correctly with -O2 or more + OPTIM = -O1 ; + } + + defines = [ FDefines $(defines) ] ; + SubDirCcFlags $(defines) -Wall -Wno-multichar ; + SubDirC++Flags $(defines) -Wall -Wno-multichar ; +} + +UsePrivateHeaders kernel ; # For kernel_cpp.cpp +UsePrivateHeaders [ FDirName kernel util ] ; # For all the UDF source files + +KernelAddon udf : kernel file_systems : + kernel_cpp.cpp + udf.cpp + + DirectoryIterator.cpp + DString.cpp + Icb.cpp + MetadataPartition.cpp + PhysicalPartition.cpp + Recognition.cpp + SparablePartition.cpp + UdfDebug.cpp + UdfString.cpp + UdfStructures.cpp + Utils.cpp + VirtualPartition.cpp + Volume.cpp +; + +SEARCH on [ FGristFiles + kernel_cpp.cpp + ] = [ FDirName $(HAIKU_TOP) src system kernel util ] ; + + +rule InstallUDF +{ + Depends $(<) : $(>) ; +} + +actions ignore InstallUDF +{ + cp $(>) /boot/home/config/add-ons/kernel/file_systems/ +} + +InstallUDF install : udf ; + +# restore original optimization level +OPTIM = $(oldOPTIM) ; + +SubInclude HAIKU_TOP src add-ons kernel file_systems udf drive_setup_addon ; diff --git a/src/tests/add-ons/kernel/file_systems/udf/r5/MemoryChunk.h b/src/tests/add-ons/kernel/file_systems/udf/r5/MemoryChunk.h new file mode 100644 index 0000000000..f05467867a --- /dev/null +++ b/src/tests/add-ons/kernel/file_systems/udf/r5/MemoryChunk.h @@ -0,0 +1,72 @@ +//---------------------------------------------------------------------- +// This software is part of the OpenBeOS distribution and is covered +// by the OpenBeOS license. +// +// Copyright (c) 2003 Tyler Dauwalder, tyler@dauwalder.net +//--------------------------------------------------------------------- + +#ifndef _UDF_MEMORY_CHUNK_H +#define _UDF_MEMORY_CHUNK_H + +#include + +#include "kernel_cpp.h" + +namespace Udf { + +/*! Simple class to encapsulate the boring details of allocating + and deallocating a chunk of memory. + + The main use for this class is cleanly and simply allocating + arbitrary chunks of data on the stack. +*/ +class MemoryChunk { +public: + MemoryChunk(uint32 blockSize) + : fSize(blockSize) + , fData(malloc(blockSize)) + , fOwnsData(true) + { + } + + MemoryChunk(uint32 blockSize, void *blockData) + : fSize(blockSize) + , fData(blockData) + , fOwnsData(false) + { + } + + ~MemoryChunk() + { + if (fOwnsData) + free(Data()); + } + + uint32 Size() { return fSize; } + void* Data() { return fData; } + status_t InitCheck() { return Data() ? B_OK : B_NO_MEMORY; } + +private: + MemoryChunk(); + MemoryChunk(const MemoryChunk&); + MemoryChunk& operator=(const MemoryChunk&); + + uint32 fSize; + void *fData; + bool fOwnsData; +}; + +template +class StaticMemoryChunk { +public: + uint32 Size() { return size; } + void* Data() { return reinterpret_cast(fData); } + status_t InitCheck() { return B_OK; } + +private: + uint8 fData[size]; +}; + +}; // namespace Udf + +#endif // _UDF_MEMORY_CHUNK_H diff --git a/src/tests/add-ons/kernel/file_systems/udf/r5/MetadataPartition.cpp b/src/tests/add-ons/kernel/file_systems/udf/r5/MetadataPartition.cpp new file mode 100644 index 0000000000..b7fc00eb4f --- /dev/null +++ b/src/tests/add-ons/kernel/file_systems/udf/r5/MetadataPartition.cpp @@ -0,0 +1,44 @@ +#include "MetadataPartition.h" + +#define B_NOT_IMPLEMENTED B_ERROR + +using namespace Udf; + +/*! \brief Creates a new MetadataPartition object. +*/ +MetadataPartition::MetadataPartition(Partition &parentPartition, + uint32 metadataFileLocation, + uint32 metadataMirrorFileLocation, + uint32 metadataBitmapFileLocation, + uint32 allocationUnitSize, + uint16 alignmentUnitSize, + bool metadataIsDuplicated) + : fParentPartition(parentPartition) + , fAllocationUnitSize(allocationUnitSize) + , fAlignmentUnitSize(alignmentUnitSize) + , fMetadataIsDuplicated(metadataIsDuplicated) + , fInitStatus(B_NO_INIT) +{ +} + +/*! \brief Destroys the MetadataPartition object. +*/ +MetadataPartition::~MetadataPartition() +{ +} + +/*! \brief Maps the given logical block to a physical block on disc. +*/ +status_t +MetadataPartition::MapBlock(uint32 logicalBlock, off_t &physicalBlock) +{ + return B_NOT_IMPLEMENTED; +} + +/*! Returns the initialization status of the object. +*/ +status_t +MetadataPartition::InitCheck() +{ + return fInitStatus; +} diff --git a/src/tests/add-ons/kernel/file_systems/udf/r5/MetadataPartition.h b/src/tests/add-ons/kernel/file_systems/udf/r5/MetadataPartition.h new file mode 100644 index 0000000000..4c0dd10377 --- /dev/null +++ b/src/tests/add-ons/kernel/file_systems/udf/r5/MetadataPartition.h @@ -0,0 +1,51 @@ +//---------------------------------------------------------------------- +// This software is part of the OpenBeOS distribution and is covered +// by the OpenBeOS license. +// +// Copyright (c) 2003 Tyler Dauwalder, tyler@dauwalder.net +//--------------------------------------------------------------------- +#ifndef _UDF_METADATA_PARTITION_H +#define _UDF_METADATA_PARTITION_H + +/*! \file MetadataPartition.h +*/ + +#include + +#include "Partition.h" +#include "UdfDebug.h" + +namespace Udf { + +/*! \brief Type 2 metadata partition + + Metadata partitions allow for clustering of metadata (ICBs, directory + contents, etc.) and also provide the option of metadata duplication. + + See also UDF-2.50 2.2.10, UDF-2.50 2.2.13 +*/ +class MetadataPartition : public Partition { +public: + MetadataPartition(Partition &parentPartition, uint32 metadataFileLocation, + uint32 metadataMirrorFileLocation, uint32 metadataBitmapFileLocation, + uint32 allocationUnitSize, uint16 alignmentUnitSize, + bool metadataIsDuplicated); + virtual ~MetadataPartition(); + virtual status_t MapBlock(uint32 logicalBlock, off_t &physicalBlock); + + status_t InitCheck(); + + uint32 AllocationUnitSize() const { return fAllocationUnitSize; } + uint16 AlignmentUnitSize() const { return fAlignmentUnitSize; } + uint32 MetadataIsDuplicated() const { return fMetadataIsDuplicated; } +private: + Partition &fParentPartition; + uint32 fAllocationUnitSize; + uint16 fAlignmentUnitSize; + bool fMetadataIsDuplicated; + status_t fInitStatus; +}; + +}; // namespace Udf + +#endif // _UDF_METADATA_PARTITION_H diff --git a/src/tests/add-ons/kernel/file_systems/udf/r5/Partition.h b/src/tests/add-ons/kernel/file_systems/udf/r5/Partition.h new file mode 100644 index 0000000000..33cc94f5ca --- /dev/null +++ b/src/tests/add-ons/kernel/file_systems/udf/r5/Partition.h @@ -0,0 +1,29 @@ +//---------------------------------------------------------------------- +// This software is part of the OpenBeOS distribution and is covered +// by the OpenBeOS license. +// +// Copyright (c) 2003 Tyler Dauwalder, tyler@dauwalder.net +//--------------------------------------------------------------------- +#ifndef _UDF_PARTITION_H +#define _UDF_PARTITION_H + +/*! \file Partition.h +*/ + +#include + +namespace Udf { + +/*! \brief Abstract base class for various UDF partition types. +*/ +class Partition { +public: + virtual ~Partition() {} + virtual status_t MapBlock(uint32 logicalBlock, off_t &physicalBlock) = 0; +// virtual status_t MapExtent(uint32 logicalBlock, uint32 logicalLength, +// uint32 &physicalBlock, uint32 &physicalLength) = 0; +}; + +}; // namespace Udf + +#endif // _UDF_PARTITION_H diff --git a/src/tests/add-ons/kernel/file_systems/udf/r5/PhysicalPartition.cpp b/src/tests/add-ons/kernel/file_systems/udf/r5/PhysicalPartition.cpp new file mode 100644 index 0000000000..6950c9674d --- /dev/null +++ b/src/tests/add-ons/kernel/file_systems/udf/r5/PhysicalPartition.cpp @@ -0,0 +1,39 @@ +#include "PhysicalPartition.h" + +#define B_NOT_IMPLEMENTED B_ERROR + +using namespace Udf; + +/*! \brief Creates a new PhysicalPartition object. +*/ +PhysicalPartition::PhysicalPartition(uint16 number, uint32 start, uint32 length) + : fNumber(number) + , fStart(start) + , fLength(length) +{ +} + +/*! \brief Destroys the PhysicalPartition object. +*/ +PhysicalPartition::~PhysicalPartition() +{ +} + +/*! \brief Maps the given logical block to a physical block on disc. + + The given logical block is simply treated as an offset from the + start of the physical partition. +*/ +status_t +PhysicalPartition::MapBlock(uint32 logicalBlock, off_t &physicalBlock) +{ + DEBUG_INIT_ETC("PhysicalPartition", ("%ld", logicalBlock)); + if (logicalBlock >= fLength) { + PRINT(("invalid logical block: %ld, length: %ld\n", logicalBlock, fLength)); + return B_BAD_ADDRESS; + } else { + physicalBlock = fStart + logicalBlock; + PRINT(("mapped %ld to %Ld\n", logicalBlock, physicalBlock)); + return B_OK; + } +} diff --git a/src/tests/add-ons/kernel/file_systems/udf/r5/PhysicalPartition.h b/src/tests/add-ons/kernel/file_systems/udf/r5/PhysicalPartition.h new file mode 100644 index 0000000000..baff3bdb75 --- /dev/null +++ b/src/tests/add-ons/kernel/file_systems/udf/r5/PhysicalPartition.h @@ -0,0 +1,44 @@ +//---------------------------------------------------------------------- +// This software is part of the OpenBeOS distribution and is covered +// by the OpenBeOS license. +// +// Copyright (c) 2003 Tyler Dauwalder, tyler@dauwalder.net +//--------------------------------------------------------------------- +#ifndef _UDF_PHYSICAL_PARTITION_H +#define _UDF_PHYSICAL_PARTITION_H + +/*! \file PhysicalPartition.h +*/ + +#include + +#include "Partition.h" +#include "UdfDebug.h" + +namespace Udf { + +/*! \brief Standard type 1 physical partition + + PhysicalPartitions map logical block numbers directly to physical + block numbers. + + See also: ECMA-167 10.7.2 +*/ +class PhysicalPartition : public Partition { +public: + PhysicalPartition(uint16 number, uint32 start, uint32 length); + virtual ~PhysicalPartition(); + virtual status_t MapBlock(uint32 logicalBlock, off_t &physicalBlock); + + uint16 Number() const { return fNumber; } + uint32 Start() const { return fStart; } + uint32 Length() const { return fLength; } +private: + uint16 fNumber; + uint32 fStart; + uint32 fLength; +}; + +}; // namespace Udf + +#endif // _UDF_PHYSICAL_PARTITION_H diff --git a/src/tests/add-ons/kernel/file_systems/udf/r5/Recognition.cpp b/src/tests/add-ons/kernel/file_systems/udf/r5/Recognition.cpp new file mode 100644 index 0000000000..2b994d02a5 --- /dev/null +++ b/src/tests/add-ons/kernel/file_systems/udf/r5/Recognition.cpp @@ -0,0 +1,549 @@ +#include "Recognition.h" + +#include "UdfString.h" +#include "MemoryChunk.h" +#include "Utils.h" + +using namespace Udf; + +//------------------------------------------------------------------------------ +// forward declarations +//------------------------------------------------------------------------------ + +static status_t +walk_volume_recognition_sequence(int device, off_t offset, + uint32 blockSize, + uint32 blockShift); +static status_t +walk_anchor_volume_descriptor_sequences(int device, off_t offset, off_t length, + uint32 blockSize, uint32 blockShift, + logical_volume_descriptor &logicalVolumeDescriptor, + partition_descriptor partitionDescriptors[], + uint8 &partitionDescriptorCount); +static status_t +walk_volume_descriptor_sequence(extent_address descriptorSequence, + int device, uint32 blockSize, uint32 blockShift, + logical_volume_descriptor &logicalVolumeDescriptor, + partition_descriptor partitionDescriptors[], + uint8 &partitionDescriptorCount); + +static status_t +walk_integrity_sequence(int device, uint32 blockSize, uint32 blockShift, + extent_address descriptorSequence, uint32 sequenceNumber = 0); + +//------------------------------------------------------------------------------ +// externally visible functions +//------------------------------------------------------------------------------ + +status_t +Udf::udf_recognize(int device, off_t offset, off_t length, uint32 blockSize, + uint32 &blockShift, logical_volume_descriptor &logicalVolumeDescriptor, + partition_descriptor partitionDescriptors[], + uint8 &partitionDescriptorCount) +{ + DEBUG_INIT_ETC(NULL, ("device: %d, offset: %Ld, length: %Ld, " + "blockSize: %ld, [...descriptors, etc...]", device, offset, + length, blockSize)); + + // Check the block size + status_t error = get_block_shift(blockSize, blockShift); + if (!error) { + PRINT(("blockShift: %ld\n", blockShift)); + + // Check for a valid volume recognition sequence + error = walk_volume_recognition_sequence(device, offset, blockSize, blockShift); + + // Now hunt down a volume descriptor sequence from one of + // the anchor volume pointers (if there are any). + if (!error) { + error = walk_anchor_volume_descriptor_sequences(device, offset, length, + blockSize, blockShift, + logicalVolumeDescriptor, + partitionDescriptors, + partitionDescriptorCount); + } + + // Now walk the integrity sequence and make sure the last integrity + // descriptor is a closed descriptor + if (!error) { + error = walk_integrity_sequence(device, blockSize, blockShift, + logicalVolumeDescriptor.integrity_sequence_extent()); + } + } else { + PRINT(("Block size must be a positive power of two! (blockSize = %ld)\n", blockSize)); + } + + RETURN(error); +} + +status_t +Udf::udf_recognize(int device, off_t offset, off_t length, uint32 blockSize, + char *volumeName) +{ + DEBUG_INIT_ETC(NULL, ("device: %d, offset: %Ld, length: %Ld, " + "blockSize: %ld, volumeName: %p", device, offset, length, + blockSize, volumeName)); + logical_volume_descriptor logicalVolumeDescriptor; + partition_descriptor partitionDescriptors[Udf::kMaxPartitionDescriptors]; + uint8 partitionDescriptorCount; + uint32 blockShift; + status_t error = udf_recognize(device, offset, length, blockSize, blockShift, + logicalVolumeDescriptor, partitionDescriptors, + partitionDescriptorCount); + if (!error && volumeName) { + String name(logicalVolumeDescriptor.logical_volume_identifier()); + strcpy(volumeName, name.Utf8()); + } + RETURN(error); +} + +//------------------------------------------------------------------------------ +// local functions +//------------------------------------------------------------------------------ + +static +status_t +walk_volume_recognition_sequence(int device, off_t offset, uint32 blockSize, uint32 blockShift) +{ + DEBUG_INIT(NULL); + // vrs starts at block 16. Each volume structure descriptor (vsd) + // should be one block long. We're expecting to find 0 or more iso9660 + // vsd's followed by some ECMA-167 vsd's. + MemoryChunk chunk(blockSize); + status_t error = chunk.InitCheck(); + if (!error) { + bool foundISO = false; + bool foundExtended = false; + bool foundECMA167 = false; + bool foundECMA168 = false; + bool foundBoot = false; + for (uint32 block = 16; true; block++) { + PRINT(("block %ld: ", block)) + off_t address = (offset + block) << blockShift; + ssize_t bytesRead = read_pos(device, address, chunk.Data(), blockSize); + if (bytesRead == (ssize_t)blockSize) + { + volume_structure_descriptor_header* descriptor = + reinterpret_cast(chunk.Data()); + if (descriptor->id_matches(kVSDID_ISO)) { + SIMPLE_PRINT(("found ISO9660 descriptor\n")); + foundISO = true; + } else if (descriptor->id_matches(kVSDID_BEA)) { + SIMPLE_PRINT(("found BEA descriptor\n")); + foundExtended = true; + } else if (descriptor->id_matches(kVSDID_TEA)) { + SIMPLE_PRINT(("found TEA descriptor\n")); + foundExtended = true; + } else if (descriptor->id_matches(kVSDID_ECMA167_2)) { + SIMPLE_PRINT(("found ECMA-167 rev 2 descriptor\n")); + foundECMA167 = true; + } else if (descriptor->id_matches(kVSDID_ECMA167_3)) { + SIMPLE_PRINT(("found ECMA-167 rev 3 descriptor\n")); + foundECMA167 = true; + } else if (descriptor->id_matches(kVSDID_BOOT)) { + SIMPLE_PRINT(("found boot descriptor\n")); + foundBoot = true; + } else if (descriptor->id_matches(kVSDID_ECMA168)) { + SIMPLE_PRINT(("found ECMA-168 descriptor\n")); + foundECMA168 = true; + } else { + SIMPLE_PRINT(("found invalid descriptor, id = `%.5s'\n", descriptor->id)); + break; + } + } else { + SIMPLE_PRINT(("read_pos(pos:%Ld, len:%ld) failed with: 0x%lx\n", address, + blockSize, bytesRead)); + break; + } + } + + // If we find an ECMA-167 descriptor, OR if we find a beginning + // or terminating extended area descriptor with NO ECMA-168 + // descriptors, we return B_OK to signal that we should go + // looking for valid anchors. + error = foundECMA167 || (foundExtended && !foundECMA168) ? B_OK : B_ERROR; + } + + RETURN(error); +} + +static +status_t +walk_anchor_volume_descriptor_sequences(int device, off_t offset, off_t length, + uint32 blockSize, uint32 blockShift, + logical_volume_descriptor &logicalVolumeDescriptor, + partition_descriptor partitionDescriptors[], + uint8 &partitionDescriptorCount) +{ + DEBUG_INIT(NULL); + const uint8 avds_location_count = 4; + const off_t avds_locations[avds_location_count] = { + 256, + length-1-256, + length-1, + 512, + }; + bool found_vds = false; + for (int32 i = 0; i < avds_location_count; i++) { + off_t block = avds_locations[i]; + off_t address = (offset + block) << blockShift; + MemoryChunk chunk(blockSize); + anchor_volume_descriptor *anchor = NULL; + + status_t anchorErr = chunk.InitCheck(); + if (!anchorErr) { + ssize_t bytesRead = read_pos(device, address, chunk.Data(), blockSize); + anchorErr = bytesRead == (ssize_t)blockSize ? B_OK : B_IO_ERROR; + if (anchorErr) { + PRINT(("block %Ld: read_pos(pos:%Ld, len:%ld) failed with error 0x%lx\n", + block, address, blockSize, bytesRead)); + } + } + if (!anchorErr) { + anchor = reinterpret_cast(chunk.Data()); + anchorErr = anchor->tag().init_check(block+offset); + if (anchorErr) { + PRINT(("block %Ld: invalid anchor\n", block)); + } else { + PRINT(("block %Ld: valid anchor\n", block)); + } + } + if (!anchorErr) { + PRINT(("block %Ld: anchor:\n", block)); + PDUMP(anchor); + // Found an avds, so try the main sequence first, then + // the reserve sequence if the main one fails. + anchorErr = walk_volume_descriptor_sequence(anchor->main_vds(), device, + blockSize, blockShift, + logicalVolumeDescriptor, + partitionDescriptors, + partitionDescriptorCount); + if (anchorErr) + anchorErr = walk_volume_descriptor_sequence(anchor->reserve_vds(), device, + blockSize, blockShift, + logicalVolumeDescriptor, + partitionDescriptors, + partitionDescriptorCount); + } + if (!anchorErr) { + PRINT(("block %Ld: found valid vds\n", avds_locations[i])); + found_vds = true; + break; + } else { + // Both failed, so loop around and try another avds + PRINT(("block %Ld: vds search failed\n", avds_locations[i])); + } + } + status_t error = found_vds ? B_OK : B_ERROR; + RETURN(error); +} + +static +status_t +walk_volume_descriptor_sequence(extent_address descriptorSequence, + int device, uint32 blockSize, uint32 blockShift, + logical_volume_descriptor &logicalVolumeDescriptor, + partition_descriptor partitionDescriptors[], + uint8 &partitionDescriptorCount) +{ + DEBUG_INIT_ETC(NULL, ("descriptorSequence.loc:%ld, descriptorSequence.len:%ld", + descriptorSequence.location(), descriptorSequence.length())); + uint32 count = descriptorSequence.length() >> blockShift; + + bool foundLogicalVolumeDescriptor = false; + bool foundUnallocatedSpaceDescriptor = false; + bool foundUdfImplementationUseDescriptor = false; + uint8 uniquePartitions = 0; + status_t error = B_OK; + + for (uint32 i = 0; i < count; i++) + { + off_t block = descriptorSequence.location()+i; + off_t address = block << blockShift; + MemoryChunk chunk(blockSize); + descriptor_tag *tag = NULL; + + PRINT(("descriptor #%ld (block %Ld):\n", i, block)); + + status_t loopError = chunk.InitCheck(); + if (!loopError) { + ssize_t bytesRead = read_pos(device, address, chunk.Data(), blockSize); + loopError = bytesRead == (ssize_t)blockSize ? B_OK : B_IO_ERROR; + if (loopError) { + PRINT(("block %Ld: read_pos(pos:%Ld, len:%ld) failed with error 0x%lx\n", + block, address, blockSize, bytesRead)); + } + } + if (!loopError) { + tag = reinterpret_cast(chunk.Data()); + loopError = tag->init_check(block); + } + if (!loopError) { + // Now decide what type of descriptor we have + switch (tag->id()) { + case TAGID_UNDEFINED: + break; + + case TAGID_PRIMARY_VOLUME_DESCRIPTOR: + { + primary_volume_descriptor *primary = reinterpret_cast(tag); + PDUMP(primary); + (void)primary; // kill the warning + break; + } + + case TAGID_ANCHOR_VOLUME_DESCRIPTOR_POINTER: + break; + + case TAGID_VOLUME_DESCRIPTOR_POINTER: + break; + + case TAGID_IMPLEMENTATION_USE_VOLUME_DESCRIPTOR: + { + implementation_use_descriptor *impUse = reinterpret_cast(tag); + PDUMP(impUse); + // Check for a matching implementation id string (note that the + // revision version is not checked) + if (impUse->tag().init_check(block) == B_OK + && impUse->implementation_id().matches(kLogicalVolumeInfoId201)) + { + foundUdfImplementationUseDescriptor = true; + } + break; + } + + case TAGID_PARTITION_DESCRIPTOR: + { + partition_descriptor *partition = reinterpret_cast(tag); + PDUMP(partition); + if (partition->tag().init_check(block) == B_OK) { + // Check for a previously discovered partition descriptor with + // the same number as this partition. If found, keep the one with + // the higher vds number. + bool foundDuplicate = false; + int num; + for (num = 0; num < uniquePartitions; num++) { + if (partitionDescriptors[num].partition_number() + == partition->partition_number()) + { + foundDuplicate = true; + if (partitionDescriptors[num].vds_number() + < partition->vds_number()) + { + partitionDescriptors[num] = *partition; + PRINT(("Replacing previous partition #%d (vds_number: %ld) with " + "new partition #%d (vds_number: %ld)\n", + partitionDescriptors[num].partition_number(), + partitionDescriptors[num].vds_number(), + partition->partition_number(), + partition->vds_number())); + } + break; + } + } + // If we didn't find a duplicate, see if we have any open descriptor + // spaces left. + if (!foundDuplicate) { + if (num < Udf::kMaxPartitionDescriptors) { + // At least one more partition descriptor allowed + partitionDescriptors[num] = *partition; + uniquePartitions++; + PRINT(("Adding partition #%d (vds_number: %ld)\n", + partition->partition_number(), + partition->vds_number())); + } else { + // We've found more than kMaxPartitionDescriptor uniquely- + // numbered partitions. So, search through the partitions + // we already have again, this time just looking for a + // partition with a lower vds number. If we find one, + // replace it with this one. If we don't, scream bloody + // murder. + bool foundReplacement = false; + for (int j = 0; j < uniquePartitions; j++) { + if (partitionDescriptors[j].vds_number() + < partition->vds_number()) + { + foundReplacement = true; + partitionDescriptors[j] = *partition; + PRINT(("Replacing partition #%d (vds_number: %ld) " + "with partition #%d (vds_number: %ld)\n", + partitionDescriptors[j].partition_number(), + partitionDescriptors[j].vds_number(), + partition->partition_number(), + partition->vds_number())); + break; + } + } + if (!foundReplacement) { + PRINT(("Found more than kMaxPartitionDescriptors == %d " + "unique partition descriptors!\n", + kMaxPartitionDescriptors)); + error = B_BAD_VALUE; + break; + } + } + } + } + break; + } + + case TAGID_LOGICAL_VOLUME_DESCRIPTOR: + { + logical_volume_descriptor *logical = reinterpret_cast(tag); + PDUMP(logical); + if (foundLogicalVolumeDescriptor) { + // Keep the vd with the highest vds_number + if (logicalVolumeDescriptor.vds_number() < logical->vds_number()) + logicalVolumeDescriptor = *logical; + } else { + logicalVolumeDescriptor = *logical; + foundLogicalVolumeDescriptor = true; + } + break; + } + + case TAGID_UNALLOCATED_SPACE_DESCRIPTOR: + { + unallocated_space_descriptor *unallocated = reinterpret_cast(tag); + PDUMP(unallocated); + foundUnallocatedSpaceDescriptor = true; + (void)unallocated; // kill the warning + break; + } + + case TAGID_TERMINATING_DESCRIPTOR: + { + terminating_descriptor *terminating = reinterpret_cast(tag); + PDUMP(terminating); + (void)terminating; // kill the warning + break; + } + + case TAGID_LOGICAL_VOLUME_INTEGRITY_DESCRIPTOR: + // Not found in this descriptor sequence + break; + + default: + break; + + } + } + } + + PRINT(("found %d unique partition%s\n", uniquePartitions, + (uniquePartitions == 1 ? "" : "s"))); + + if (!error && !foundUdfImplementationUseDescriptor) { + INFORM(("WARNING: no valid udf implementation use descriptor found\n")); + } + if (!error) + error = foundLogicalVolumeDescriptor + && foundUnallocatedSpaceDescriptor + ? B_OK : B_ERROR; + if (!error) + error = uniquePartitions >= 1 ? B_OK : B_ERROR; + if (!error) + partitionDescriptorCount = uniquePartitions; + + RETURN(error); +} + +/*! \brief Walks the integrity sequence in the extent given by \a descriptorSequence. + + \return + - \c B_OK: Success. the sequence was terminated by a valid, closed + integrity descriptor. + - \c B_ENTRY_NOT_FOUND: The sequence was empty. + - (other error code): The sequence was non-empty and did not end in a valid, + closed integrity descriptor. +*/ +static status_t +walk_integrity_sequence(int device, uint32 blockSize, uint32 blockShift, + extent_address descriptorSequence, uint32 sequenceNumber) +{ + DEBUG_INIT_ETC(NULL, ("descriptorSequence.loc:%ld, descriptorSequence.len:%ld", + descriptorSequence.location(), descriptorSequence.length())); + uint32 count = descriptorSequence.length() >> blockShift; + + bool lastDescriptorWasClosed = false; + uint16 highestMinimumUDFReadRevision = 0x0000; + status_t error = count > 0 ? B_OK : B_ENTRY_NOT_FOUND; + for (uint32 i = 0; error == B_OK && i < count; i++) + { + off_t block = descriptorSequence.location()+i; + off_t address = block << blockShift; + MemoryChunk chunk(blockSize); + descriptor_tag *tag = NULL; + + PRINT(("integrity descriptor #%ld:%ld (block %Ld):\n", sequenceNumber, i, block)); + + status_t loopError = chunk.InitCheck(); + if (!loopError) { + ssize_t bytesRead = read_pos(device, address, chunk.Data(), blockSize); + loopError = check_size_error(bytesRead, blockSize); + if (loopError) { + PRINT(("block %Ld: read_pos(pos:%Ld, len:%ld) failed with error 0x%lx\n", + block, address, blockSize, bytesRead)); + } + } + if (!loopError) { + tag = reinterpret_cast(chunk.Data()); + loopError = tag->init_check(block); + } + if (!loopError) { + // Check the descriptor type and see if it's closed. + loopError = tag->id() == TAGID_LOGICAL_VOLUME_INTEGRITY_DESCRIPTOR + ? B_OK : B_BAD_DATA; + if (!loopError) { + logical_volume_integrity_descriptor *descriptor = + reinterpret_cast(chunk.Data()); + PDUMP(descriptor); + lastDescriptorWasClosed = descriptor->integrity_type() == INTEGRITY_CLOSED; + if (lastDescriptorWasClosed) { + uint16 minimumRevision = descriptor->minimum_udf_read_revision(); + if (minimumRevision > highestMinimumUDFReadRevision) { + highestMinimumUDFReadRevision = minimumRevision; + } else if (minimumRevision < highestMinimumUDFReadRevision) { + INFORM(("WARNING: found decreasing minimum udf read revision in integrity " + "sequence (last highest: 0x%04x, current: 0x%04x); using higher " + "revision.\n", highestMinimumUDFReadRevision, minimumRevision)); + } + } + + // Check a continuation extent if necessary. Note that this effectively + // ends our search through this extent + extent_address &next = descriptor->next_integrity_extent(); + if (next.length() > 0) { + status_t nextError = walk_integrity_sequence(device, blockSize, blockShift, + next, sequenceNumber+1); + if (nextError && nextError != B_ENTRY_NOT_FOUND) { + // Continuation proved invalid + error = nextError; + break; + } else { + // Either the continuation was valid or empty; either way, + // we're done searching. + break; + } + } + } else { + PDUMP(tag); + } + } + // If we hit an error on the first item, consider the extent empty, + // otherwise just break out of the loop and assume part of the + // extent is unrecorded + if (loopError) { + if (i == 0) + error = B_ENTRY_NOT_FOUND; + else + break; + } + } + if (!error) + error = lastDescriptorWasClosed ? B_OK : B_BAD_DATA; + if (!error) + error = highestMinimumUDFReadRevision <= UDF_MAX_READ_REVISION ? B_OK : B_ERROR; + RETURN(error); +} + diff --git a/src/tests/add-ons/kernel/file_systems/udf/r5/Recognition.h b/src/tests/add-ons/kernel/file_systems/udf/r5/Recognition.h new file mode 100644 index 0000000000..9595abc02f --- /dev/null +++ b/src/tests/add-ons/kernel/file_systems/udf/r5/Recognition.h @@ -0,0 +1,28 @@ +//---------------------------------------------------------------------- +// This software is part of the OpenBeOS distribution and is covered +// by the OpenBeOS license. +// +// Copyright (c) 2003 Tyler Dauwalder, tyler@dauwalder.net +//--------------------------------------------------------------------- +#ifndef _UDF_RECOGNITION_H +#define _UDF_RECOGNITION_H + +/*! \file Recognition.h +*/ + +#include "UdfStructures.h" +#include "UdfDebug.h" + +namespace Udf { + +status_t udf_recognize(int device, off_t offset, off_t length, + uint32 blockSize, uint32 &blockShift, + logical_volume_descriptor &logicalVolumeDescriptor, + partition_descriptor partitionDescriptors[], + uint8 &partitionDescriptorCount); +status_t udf_recognize(int device, off_t offset, off_t length, + uint32 blockSize, char *volumeName); + +} // namespace Udf + +#endif // _UDF_RECOGNITION_H diff --git a/src/tests/add-ons/kernel/file_systems/udf/r5/SparablePartition.cpp b/src/tests/add-ons/kernel/file_systems/udf/r5/SparablePartition.cpp new file mode 100644 index 0000000000..dd71f2f35c --- /dev/null +++ b/src/tests/add-ons/kernel/file_systems/udf/r5/SparablePartition.cpp @@ -0,0 +1,67 @@ +#include "SparablePartition.h" + +#define B_NOT_IMPLEMENTED B_ERROR + +using namespace Udf; + +/*! \brief Creates a new SparablePartition object. +*/ +SparablePartition::SparablePartition(uint16 number, uint32 start, uint32 length, + uint16 packetLength, uint8 tableCount, + uint32 *tableLocations) + : fNumber(number) + , fStart(start) + , fLength(length) + , fPacketLength(packetLength) + , fTableCount(tableCount) + , fInitStatus(B_NO_INIT) +{ + status_t error = (0 < TableCount() && TableCount() <= kMaxSparingTableCount) + ? B_OK : B_BAD_VALUE; + if (!error) { + for (uint8 i = 0; i < TableCount(); i++) + fTableLocations[i] = tableLocations[i]; + } + if (!error) + fInitStatus = B_OK; +} + +/*! \brief Destroys the SparablePartition object. +*/ +SparablePartition::~SparablePartition() +{ +} + +/*! \brief Maps the given logical block to a physical block on disc. + + The sparing tables are first checked to see if the logical block has + been remapped from a defective location to a non-defective one. If + not, the given logical block is then simply treated as an offset from + the start of the physical partition. +*/ +status_t +SparablePartition::MapBlock(uint32 logicalBlock, off_t &physicalBlock) +{ + status_t error = InitCheck(); + if (!error) { + if (logicalBlock >= fLength) + error = B_BAD_ADDRESS; + else { + // Check for the logical block in the sparing tables. If not + // found, map directly to physical space. + + //physicalBlock = fStart + logicalBlock; + //return B_OK; + error = B_ERROR; + } + } + return error; +} + +/*! Returns the initialization status of the object. +*/ +status_t +SparablePartition::InitCheck() +{ + return fInitStatus; +} diff --git a/src/tests/add-ons/kernel/file_systems/udf/r5/SparablePartition.h b/src/tests/add-ons/kernel/file_systems/udf/r5/SparablePartition.h new file mode 100644 index 0000000000..8957642b1e --- /dev/null +++ b/src/tests/add-ons/kernel/file_systems/udf/r5/SparablePartition.h @@ -0,0 +1,63 @@ +//---------------------------------------------------------------------- +// This software is part of the OpenBeOS distribution and is covered +// by the OpenBeOS license. +// +// Copyright (c) 2003 Tyler Dauwalder, tyler@dauwalder.net +//--------------------------------------------------------------------- +#ifndef _UDF_SPARABLE_PARTITION_H +#define _UDF_SPARABLE_PARTITION_H + +/*! \file SparablePartition.h +*/ + +#include + +#include "UdfStructures.h" +#include "Partition.h" +#include "UdfDebug.h" + +namespace Udf { + +/*! \brief Type 2 sparable partition + + Sparable partitions provide a defect-managed partition + space for media that does not implicitly provide defect management, + such as CD-RW. Arbitrary packets of blocks in the sparable partition + may be transparently remapped to other locations on disc should the + original locations become defective. + + Per UDF-2.01 2.2.11, sparable partitions shall be recorded only on + disk/drive systems that do not perform defect management. + + See also UDF-2.01 2.2.9, UDF-2.01 2.2.11 +*/ +class SparablePartition : public Partition { +public: + SparablePartition(uint16 number, uint32 start, uint32 length, uint16 packetLength, + uint8 tableCount, uint32 *tableLocations); + virtual ~SparablePartition(); + virtual status_t MapBlock(uint32 logicalBlock, off_t &physicalBlock); + + status_t InitCheck(); + + uint16 Number() const { return fNumber; } + uint32 Start() const { return fStart; } + uint32 Length() const { return fLength; } + uint32 PacketLength() const { return fPacketLength; } + uint8 TableCount() const { return fTableCount; } + + //! Maximum number of redundant sparing tables per SparablePartition + static const uint8 kMaxSparingTableCount = UDF_MAX_SPARING_TABLE_COUNT; +private: + uint16 fNumber; + uint32 fStart; + uint32 fLength; + uint32 fPacketLength; + uint8 fTableCount; + uint32 fTableLocations[kMaxSparingTableCount]; + status_t fInitStatus; +}; + +}; // namespace Udf + +#endif // _UDF_SPARABLE_PARTITION_H diff --git a/src/tests/add-ons/kernel/file_systems/udf/r5/UdfDebug.cpp b/src/tests/add-ons/kernel/file_systems/udf/r5/UdfDebug.cpp new file mode 100644 index 0000000000..af2f299081 --- /dev/null +++ b/src/tests/add-ons/kernel/file_systems/udf/r5/UdfDebug.cpp @@ -0,0 +1,344 @@ +//---------------------------------------------------------------------- +// This software is part of the OpenBeOS distribution and is covered +// by the OpenBeOS license. +// +// This version copyright (c) 2003 Tyler Dauwalder, tyler@dauwalder.net +// Initial version copyright (c) 2002 Axel Dörfler, axeld@pinc-software.de +//---------------------------------------------------------------------- + +/*! \file Debug.cpp + + Support code for handy debugging macros. +*/ + +#include "UdfDebug.h" + +#include +#include +#include + +//---------------------------------------------------------------------- +// Long-winded overview of the debug output macros: +//---------------------------------------------------------------------- +/*! \def DEBUG_INIT() + \brief Increases the indentation level, prints out the enclosing function's + name, and creates a \c DebugHelper object on the stack to automatically + decrease the indentation level upon function exit. + + This macro should be called at the very beginning of any function in + which you wish to use any of the other debugging macros. + + If DEBUG is undefined, does nothing. +*/ +//---------------------------------------------------------------------- +/*! \def PRINT(x) + \brief Prints out the enclosing function's name followed by the contents + of \a x at the current indentation level. + + \param x A printf-style format string enclosed in an extra set of parenteses, + e.g. PRINT(("%d\n", 0)); + + If DEBUG is undefined, does nothing. +*/ +//---------------------------------------------------------------------- +/*! \def LPRINT(x) + \brief Identical to \c PRINT(x), except that the line number in the source + file at which the macro is invoked is also printed. + + \param x A printf-style format string enclosed in an extra set of parenteses, + e.g. PRINT(("%d\n", 0)); + + If DEBUG is undefined, does nothing. +*/ +//---------------------------------------------------------------------- +/*! \def SIMPLE_PRINT(x) + \brief Directly prints the contents of \a x with no extra formatting or + information included (just like a straight \c printf() call). + + \param x A printf-style format string enclosed in an extra set of parenteses, + e.g. PRINT(("%d\n", 0)); + + If DEBUG is undefined, does nothing. +*/ +//---------------------------------------------------------------------- +/*! \def PRINT_INDENT() + \brief Prints out enough indentation characters to indent the current line + to the current indentation level (assuming the cursor was flush left to + begin with...). + + This function is called by the other \c *PRINT* macros, and isn't really + intended for general consumption, but you might find it useful. + + If DEBUG is undefined, does nothing. +*/ +//---------------------------------------------------------------------- +/*! \def REPORT_ERROR(error) + \brief Calls \c LPRINT(x) with a format string listing the error + code in \c error (assumed to be a \c status_t value) and the + corresponding text error code returned by a call to \c strerror(). + + This function is called by the \c RETURN* macros, and isn't really + intended for general consumption, but you might find it useful. + + \param error A \c status_t error code to report. + + If DEBUG is undefined, does nothing. +*/ +//---------------------------------------------------------------------- +/*! \def RETURN_ERROR(error) + \brief Calls \c REPORT_ERROR(error) if error is a an error code (i.e. + negative), otherwise remains silent. In either case, the enclosing + function is then exited with a call to \c "return error;". + + \param error A \c status_t error code to report (if negative) and return. + + If DEBUG is undefined, silently returns the value in \c error. +*/ +//---------------------------------------------------------------------- +/*! \def RETURN(error) + \brief Prints out a description of the error code being returned + (which, in this case, may be either "erroneous" or "successful") + and then exits the enclosing function with a call to \c "return error;". + + \param error A \c status_t error code to report and return. + + If DEBUG is undefined, silently returns the value in \c error. +*/ +//---------------------------------------------------------------------- +/*! \def FATAL(x) + \brief Prints out a fatal error message. + + This one's still a work in progress... + + \param x A printf-style format string enclosed in an extra set of parenteses, + e.g. PRINT(("%d\n", 0)); + + If DEBUG is undefined, does nothing. +*/ +//---------------------------------------------------------------------- +/*! \def INFORM(x) + \brief Directly prints the contents of \a x with no extra formatting or + information included (just like a straight \c printf() call). Does so + whether \c DEBUG is defined or not. + + \param x A printf-style format string enclosed in an extra set of parenteses, + e.g. PRINT(("%d\n", 0)); + + I'll say it again: Prints its output regardless to DEBUG being defined or + undefined. +*/ +//---------------------------------------------------------------------- +/*! \def DBG(x) + \brief If debug is defined, \a x is passed along to the code and + executed unmodified. If \c DEBUG is undefined, the contents of + \a x disappear into the ether. + + \param x Damn near anything resembling valid C\C++. +*/ +//---------------------------------------------------------------------- +/*! \def DIE(x) + \brief Drops the user into the appropriate debugger (user or kernel) + after printing out the handy message bundled in the parenthesee + enclosed printf-style format string found in \a x. + + \param x A printf-style format string enclosed in an extra set of parenteses, + e.g. PRINT(("%d\n", 0)); +*/ + + +//---------------------------------------------------------------------- +// declarations +//---------------------------------------------------------------------- + +static void indent(uint8 tabCount); +static void unindent(uint8 tabCount); +#if !_KERNEL_MODE + static int32 get_tls_handle(); +#endif + +//! Used to keep the tls handle from being allocated more than once. +vint32 tls_spinlock = 0; + +/*! \brief Used to flag whether the tls handle has been allocated yet. + + Not sure if this really needs to be \c volatile or not... +*/ +volatile bool tls_handle_initialized = false; + +//! The tls handle of the tls var used to store indentation info. +int32 tls_handle = 0; + +//---------------------------------------------------------------------- +// public functions +//---------------------------------------------------------------------- + +/*! \brief Returns the current debug indentation level for the + current thread. + + NOTE: indentation is currently unsupported for R5::kernelland due + to lack of thread local storage support. +*/ +int32 +_get_debug_indent_level() +{ +#if !_KERNEL_MODE + return (int32)tls_get(get_tls_handle()); +#else + return 1; +#endif +} + +//---------------------------------------------------------------------- +// static functions +//---------------------------------------------------------------------- + +/*! \brief Increases the current debug indentation level for + the current thread by 1. +*/ +void +indent(uint8 tabCount) +{ +#if !_KERNEL_MODE + tls_set(get_tls_handle(), (void*)(_get_debug_indent_level()+tabCount)); +#endif +} + +/*! \brief Decreases the current debug indentation level for + the current thread by 1. +*/ +void +unindent(uint8 tabCount) +{ +#if !_KERNEL_MODE + tls_set(get_tls_handle(), (void*)(_get_debug_indent_level()-tabCount)); +#endif +} + +#if !_KERNEL_MODE +/*! \brief Returns the thread local storage handle used to store + indentation information, allocating the handle first if + necessary. +*/ +int32 +get_tls_handle() +{ + // Init the tls handle if this is the first call. + if (!tls_handle_initialized) { + if (atomic_or(&tls_spinlock, 1) == 0) { + // First one in gets to init + tls_handle = tls_allocate(); + tls_handle_initialized = true; + atomic_and(&tls_spinlock, 0); + } else { + // All others must wait patiently + while (!tls_handle_initialized) { + snooze(1); + } + } + } + return tls_handle; +} +#endif + +/*! \brief Helper class for initializing the debugging output + file. + + Note that this hummer isn't threadsafe, but it doesn't really + matter for our concerns, since the worst it'll result in is + a dangling file descriptor, and that would be in the case of + two or more volumes being mounted almost simultaneously... + not too big of a worry. +*/ +class DebugOutputFile { +public: + DebugOutputFile(const char *filename = NULL) + : fFile(-1) + { + Init(filename); + } + + ~DebugOutputFile() { + if (fFile >= 0) + close(fFile); + } + + void Init(const char *filename) { + if (fFile < 0 && filename) + fFile = open(filename, O_RDWR | O_CREAT | O_TRUNC); + } + + int File() const { return fFile; } +private: + int fFile; +}; + +DebugOutputFile *out = NULL; + +/*! \brief It doesn't appear that the constructor for the global + \c out variable is called when built as an R5 filesystem add-on, + so this function needs to be called in udf_mount to let the + magic happen. +*/ +void initialize_debugger(const char *filename) +{ +#if DEBUG_TO_FILE + if (!out) { + out = new(nothrow) DebugOutputFile(filename); + dbg_printf("out was NULL!\n"); + } else { + DebugOutputFile *temp = out; + out = new(nothrow) DebugOutputFile(filename); + dbg_printf("out was %p!\n", temp); + } +#endif +} + +// dbg_printf, stolen from Ingo's ReiserFS::Debug.cpp. +void +dbg_printf(const char *format,...) +{ +#if DEBUG_TO_FILE + if (!out) + return; + + char buffer[1024]; + va_list args; + va_start(args, format); + // no vsnprintf() on PPC + #if defined(__INTEL__) && !_KERNEL_MODE + vsnprintf(buffer, sizeof(buffer) - 1, format, args); + #else + vsprintf(buffer, format, args); + #endif + va_end(args); + buffer[sizeof(buffer) - 1] = '\0'; + write(out->File(), buffer, strlen(buffer)); +#endif +} + +//---------------------------------------------------------------------- +// DebugHelper +//---------------------------------------------------------------------- + +/*! \brief Increases the current indentation level. +*/ +DebugHelper::DebugHelper(const char *className, uint8 tabCount) + : fTabCount(tabCount) + , fClassName(NULL) +{ + indent(fTabCount); + if (className) { + fClassName = (char*)malloc(strlen(className)+1); + if (fClassName) + strcpy(fClassName, className); + } +} + +/*! \brief Decreases the current indentation level. +*/ +DebugHelper::~DebugHelper() +{ + unindent(fTabCount); + free(fClassName); +} + diff --git a/src/tests/add-ons/kernel/file_systems/udf/r5/UdfDebug.h b/src/tests/add-ons/kernel/file_systems/udf/r5/UdfDebug.h new file mode 100644 index 0000000000..7cf907e2a3 --- /dev/null +++ b/src/tests/add-ons/kernel/file_systems/udf/r5/UdfDebug.h @@ -0,0 +1,240 @@ +//---------------------------------------------------------------------- +// This software is part of the OpenBeOS distribution and is covered +// by the OpenBeOS license. +// +// This version copyright (c) 2003 Tyler Dauwalder, tyler@dauwalder.net +// Initial version copyright (c) 2002 Axel Dörfler, axeld@pinc-software.de +// dbg_printf() function copyright (c) 2003 Ingo Weinhold, bonefish@cs.tu-berlin.edu +//---------------------------------------------------------------------- +#ifndef _UDF_DEBUG_H +#define _UDF_DEBUG_H + +/*! \file Debug.h + + Handy debugging macros. +*/ + +#include + +#include +#ifdef DEBUG +//# include +#endif +#include + + +#define DEBUG_TO_FILE 0 + +# include +#if DEBUG_TO_FILE +//# include +# include +extern "C" int vsprintf(char *s, const char *format, va_list arg); +# include +# define __out dbg_printf + void dbg_printf(const char *format,...); + void initialize_debugger(const char *filename); +#else +# if !_KERNEL_MODE +//# include +# define __out printf +# else +//# include +# define __out dprintf +# endif +# include +# include +//# define __out printf +#endif + +#include "kernel_cpp.h" + +class DebugHelper; + +int32 _get_debug_indent_level(); + +/*! \brief Helper class that is allocated on the stack by + the \c DEBUG_INIT() macro. On creation, it increases the + current indentation level by the amount specified via its + constructor's \c tabCount parameter; on destruction, it + decreases it. +*/ +class DebugHelper +{ +public: + DebugHelper(const char *className = NULL, uint8 tabCount = 1); + ~DebugHelper(); + + uint8 TabCount() const { return fTabCount; } + const char* ClassName() const { return fClassName; } + +private: + uint8 fTabCount; + char *fClassName; +}; + +//---------------------------------------------------------------------- +// NOTE: See Debug.cpp for complete descriptions of the following +// debug macros. +//---------------------------------------------------------------------- + + +//---------------------------------------------------------------------- +// DEBUG-independent macros +//---------------------------------------------------------------------- +#define INFORM(x) { __out("udf: "); __out x; } +#if !_KERNEL_MODE +# define DIE(x) debugger x +#else +# define DIE(x) kernel_debugger x +#endif + +//---------------------------------------------------------------------- +// DEBUG-dependent macros +//---------------------------------------------------------------------- +#ifdef DEBUG + #if DEBUG_TO_FILE + #define INITIALIZE_DEBUGGING_OUTPUT_FILE(filename) initialize_debugger(filename); + #else + #define INITIALIZE_DEBUGGING_OUTPUT_FILE(filename) ; + #endif + + #define DEBUG_INIT_SILENT(className) \ + DebugHelper _debugHelper(className, 2); + + #define DEBUG_INIT(className) \ + DEBUG_INIT_SILENT(className); \ + PRINT(("\n")); + + #define DEBUG_INIT_ETC(className, arguments) \ + DEBUG_INIT_SILENT(className) \ + { \ + PRINT_INDENT(); \ + if (_debugHelper.ClassName()) { \ + __out("udf: %s::%s(", \ + _debugHelper.ClassName(), __FUNCTION__); \ + } else { \ + __out("udf: %s(", __FUNCTION__); \ + } \ + __out arguments; \ + __out("):\n"); \ + } + + #define DUMP_INIT(className) \ + DEBUG_INIT_SILENT(className); + + #define PRINT(x) { \ + { \ + PRINT_INDENT(); \ + if (_debugHelper.ClassName()) { \ + __out("udf: %s::%s(): ", \ + _debugHelper.ClassName(), __FUNCTION__); \ + } else { \ + __out("udf: %s(): ", __FUNCTION__); \ + } \ + __out x; \ + } \ + } + + #define LPRINT(x) { \ + { \ + PRINT_INDENT(); \ + if (_debugHelper.ClassName()) { \ + __out("udf: %s::%s(): line %d: ", \ + _debugHelper.ClassName(), __FUNCTION__, __LINE__); \ + } else { \ + __out("udf: %s(): line %d: ", \ + __FUNCTION__, __LINE__); \ + } \ + __out x; \ + } \ + } + + #define SIMPLE_PRINT(x) { \ + { \ + __out x; \ + } \ + } + + #define PRINT_INDENT() { \ + { \ + int32 _level = _get_debug_indent_level(); \ + for (int32 i = 0; i < _level-_debugHelper.TabCount(); i++) { \ + __out(" "); \ + } \ + } \ + } + + #define PRINT_DIVIDER() \ + PRINT_INDENT(); \ + SIMPLE_PRINT(("------------------------------------------------------------\n")); + + #define DUMP(object) \ + { \ + (object).dump(); \ + } + + #define PDUMP(objectPointer) \ + { \ + (objectPointer)->dump(); \ + } + + #define REPORT_ERROR(error) { \ + LPRINT(("returning error 0x%lx, `%s'\n", error, strerror(error))); \ + } + + #define RETURN_ERROR(error) { \ + status_t _status = error; \ + if (_status < (status_t)B_OK) \ + REPORT_ERROR(_status); \ + return _status; \ + } + + #define RETURN(error) { \ + status_t _status = error; \ + if (_status < (status_t)B_OK) { \ + REPORT_ERROR(_status); \ + } else if (_status == (status_t)B_OK) { \ + LPRINT(("returning B_OK\n")); \ + } else { \ + LPRINT(("returning 0x%lx = %ld\n", _status, _status)); \ + } \ + return _status; \ + } + + #define FATAL(x) { \ + PRINT(("fatal error: ")); SIMPLE_PRINT(x); \ + } + + #define DBG(x) x ; + +#else // ifdef DEBUG + #define INITIALIZE_DEBUGGING_OUTPUT_FILE(filename) ; + #define DEBUG_INIT_SILENT(className) ; + #define DEBUG_INIT(className) ; + #define DEBUG_INIT_ETC(className, arguments) ; + #define DUMP_INIT(className) ; + #define PRINT(x) ; + #define LPRINT(x) ; + #define SIMPLE_PRINT(x) ; + #define PRINT_INDENT(x) ; + #define PRINT_DIVIDER() ; + #define DUMP(object) ; + #define PDUMP(objectPointer) ; + #define REPORT_ERROR(status) ; + #define RETURN_ERROR(status) return status; + #define RETURN(status) return status; + #define FATAL(x) { __out("udf: fatal error: "); __out x; } + #define DBG(x) ; + #define DUMP(x) ; +#endif // ifdef DEBUG else + +#define TRACE(x) DBG(dprintf x) + +// These macros turn on or off extensive and generally unnecessary +// debugging output regarding table of contents parsing +//#define WARN(x) (dprintf x) +//#define WARN(x) +#define WARN(x) DBG(dprintf x) + +#endif // _UDF_DEBUG_H diff --git a/src/tests/add-ons/kernel/file_systems/udf/r5/UdfString.cpp b/src/tests/add-ons/kernel/file_systems/udf/r5/UdfString.cpp new file mode 100644 index 0000000000..a071db0dee --- /dev/null +++ b/src/tests/add-ons/kernel/file_systems/udf/r5/UdfString.cpp @@ -0,0 +1,312 @@ +#include "UdfString.h" + +#include "ByteOrder.h" + + +/*! \brief Converts the given unicode character to utf8. + + \param c The unicode character. + \param out Pointer to a C-string of at least 4 characters + long into which the output utf8 characters will + be written. The string that is pointed to will + be incremented to reflect the number of characters + written, i.e. if \a out initially points to a pointer + to the first character in string named \c str, and + the function writes 4 characters to \c str, then + upon returning, out will point to a pointer to + the fifth character in \c str. +*/ +static +void +unicode_to_utf8(uint32 c, char **out) +{ + char *s = *out; + + if (c < 0x80) + *(s++) = c; + else if (c < 0x800) { + *(s++) = 0xc0 | (c>>6); + *(s++) = 0x80 | (c & 0x3f); + } else if (c < 0x10000) { + *(s++) = 0xe0 | (c>>12); + *(s++) = 0x80 | ((c>>6) & 0x3f); + *(s++) = 0x80 | (c & 0x3f); + } else if (c <= 0x10ffff) { + *(s++) = 0xf0 | (c>>18); + *(s++) = 0x80 | ((c>>12) & 0x3f); + *(s++) = 0x80 | ((c>>6) & 0x3f); + *(s++) = 0x80 | (c & 0x3f); + } + *out = s; +} + +/*! \brief Converts the given utf8 character to 4-byte unicode. + + \param in Pointer to a C-String from which utf8 characters + will be read. *in will be incremented to reflect + the number of characters read, similarly to the + \c out parameter for Udf::unicode_to_utf8(). + + \return The 4-byte unicode character, or **in if passed an + invalid character, or 0 if passed any NULL pointers. +*/ +static +uint32 +utf8_to_unicode(const char **in) +{ + if (!in) + return 0; + uint8 *bytes = (uint8 *)*in; + if (!bytes) + return 0; + + int32 length; + uint8 mask = 0x1f; + + switch (bytes[0] & 0xf0) { + case 0xc0: + case 0xd0: length = 2; break; + case 0xe0: length = 3; break; + case 0xf0: + mask = 0x0f; + length = 4; + break; + default: + // valid 1-byte character + // and invalid characters + (*in)++; + return bytes[0]; + } + uint32 c = bytes[0] & mask; + int32 i = 1; + for (;i < length && (bytes[i] & 0x80) > 0;i++) + c = (c << 6) | (bytes[i] & 0x3f); + + if (i < length) { + // invalid character + (*in)++; + return (uint32)bytes[0]; + } + *in += length; + return c; +} + +using namespace Udf; + +/*! \brief Creates an empty string object. +*/ +String::String() + : fCs0String(NULL) + , fUtf8String(NULL) +{ +} + +/*! \brief Creates a new String object from the given Utf8 string. +*/ +String::String(const char *utf8) + : fCs0String(NULL) + , fUtf8String(NULL) +{ + SetTo(utf8); +} + +/*! \brief Creates a new String object from the given Cs0 string. +*/ +String::String(const char *cs0, uint32 length) + : fCs0String(NULL) + , fUtf8String(NULL) +{ + SetTo(cs0, length); +} + +String::~String() +{ + DEBUG_INIT("String"); + + _Clear(); +} + +/*! \brief Assignment from a Utf8 string. +*/ +void +String::SetTo(const char *utf8) +{ + DEBUG_INIT_ETC("String", ("utf8: `%s', strlen(utf8): %ld", utf8, + utf8 ? strlen(utf8) : 0)); + _Clear(); + if (!utf8) { + PRINT(("passed NULL utf8 string\n")); + return; + } + uint32 length = strlen(utf8); + // First copy the utf8 string + fUtf8String = new(nothrow) char[length+1]; + if (!fUtf8String){ + PRINT(("new fUtf8String[%ld] allocation failed\n", length+1)); + return; + } + memcpy(fUtf8String, utf8, length+1); + // Next convert to raw 4-byte unicode. Then we'll do some + // analysis to figure out if we have any invalid characters, + // and whether we can get away with compressed 8-bit unicode, + // or have to use burly 16-bit unicode. + uint32 *raw = new(nothrow) uint32[length]; + if (!raw) { + PRINT(("new uint32 raw[%ld] temporary string allocation failed\n", length)); + _Clear(); + return; + } + const char *in = utf8; + uint32 rawLength = 0; + for (uint32 i = 0; i < length && uint32(in-utf8) < length; i++, rawLength++) + raw[i] = utf8_to_unicode(&in); + // Check for invalids. + uint32 mask = 0xffff0000; + for (uint32 i = 0; i < rawLength; i++) { + if (raw[i] & mask) { + PRINT(("WARNING: utf8 string contained a multi-byte sequence which " + "was converted into a unicode character larger than 16-bits; " + "character will be converted to an underscore character for " + "safety.\n")); + raw[i] = '_'; + } + } + // See if we can get away with 8-bit compressed unicode + mask = 0xffffff00; + bool canUse8bit = true; + for (uint32 i = 0; i < rawLength; i++) { + if (raw[i] & mask) { + canUse8bit = false; + break; + } + } + // Build our cs0 string + if (canUse8bit) { + fCs0Length = rawLength+1; + fCs0String = new(nothrow) char[fCs0Length]; + if (fCs0String) { + fCs0String[0] = '\x08'; // 8-bit compressed unicode + for (uint32 i = 0; i < rawLength; i++) + fCs0String[i+1] = raw[i] % 256; + } else { + PRINT(("new fCs0String[%ld] allocation failed\n", fCs0Length)); + _Clear(); + return; + } + } else { + fCs0Length = rawLength*2+1; + fCs0String = new(nothrow) char[fCs0Length]; + if (fCs0String) { + uint32 pos = 0; + fCs0String[pos++] = '\x10'; // 16-bit unicode + for (uint32 i = 0; i < rawLength; i++) { + // 16-bit unicode chars must be written big endian + uint16 value = uint16(raw[i]); + uint8 high = uint8(value >> 8 & 0xff); + uint8 low = uint8(value & 0xff); + fCs0String[pos++] = high; + fCs0String[pos++] = low; + } + } else { + PRINT(("new fCs0String[%ld] allocation failed\n", fCs0Length)); + _Clear(); + return; + } + } + // Clean up + delete [] raw; + raw = NULL; +} + +/*! \brief Assignment from a Cs0 string. +*/ +void +String::SetTo(const char *cs0, uint32 length) +{ + DEBUG_INIT_ETC("String", ("cs0: %p, length: %ld", cs0, length)); + + _Clear(); + if (length == 0) + return; + if (!cs0) { + PRINT(("passed NULL cs0 string\n")); + return; + } + + // First copy the Cs0 string and length + fCs0String = new(nothrow) char[length]; + if (fCs0String) { + memcpy(fCs0String, cs0, length); + fCs0Length = length; + } else { + PRINT(("new fCs0String[%ld] allocation failed\n", length)); + return; + } + + // Now convert to utf8 + + // The first byte of the CS0 string is the compression ID. + // - 8: 1 byte characters + // - 16: 2 byte, big endian characters + // - 254: "CS0 expansion is empty and unique", 1 byte characters + // - 255: "CS0 expansion is empty and unique", 2 byte, big endian characters + PRINT(("compression ID: %d\n", cs0[0])); + switch (reinterpret_cast(cs0)[0]) { + case 8: + case 254: + { + const uint8 *inputString = reinterpret_cast(&(cs0[1])); + int32 maxLength = length-1; // Max length of input string in uint8 characters + int32 allocationLength = maxLength*2+1; // Need at most 2 utf8 chars per uint8 char + fUtf8String = new(nothrow) char[allocationLength]; + if (fUtf8String) { + char *outputString = fUtf8String; + + for (int32 i = 0; i < maxLength && inputString[i]; i++) { + unicode_to_utf8(inputString[i], &outputString); + } + outputString[0] = 0; + } else { + PRINT(("new fUtf8String[%ld] allocation failed\n", allocationLength)); + } + + break; + } + + case 16: + case 255: + { + const uint16 *inputString = reinterpret_cast(&(cs0[1])); + int32 maxLength = (length-1) / 2; // Max length of input string in uint16 characters + int32 allocationLength = maxLength*3+1; // Need at most 3 utf8 chars per uint16 char + fUtf8String = new(nothrow) char[allocationLength]; + if (fUtf8String) { + char *outputString = fUtf8String; + + for (int32 i = 0; i < maxLength && inputString[i]; i++) { + unicode_to_utf8(B_BENDIAN_TO_HOST_INT16(inputString[i]), &outputString); + } + outputString[0] = 0; + } else { + PRINT(("new fUtf8String[%ld] allocation failed\n", allocationLength)); + } + + break; + } + + default: + PRINT(("invalid compression id!\n")); + break; + } +} + +void +String::_Clear() +{ + DEBUG_INIT("String"); + + delete [] fCs0String; + fCs0String = NULL; + delete [] fUtf8String; + fUtf8String = NULL; +} diff --git a/src/tests/add-ons/kernel/file_systems/udf/r5/UdfString.h b/src/tests/add-ons/kernel/file_systems/udf/r5/UdfString.h new file mode 100644 index 0000000000..903a95e573 --- /dev/null +++ b/src/tests/add-ons/kernel/file_systems/udf/r5/UdfString.h @@ -0,0 +1,106 @@ +//---------------------------------------------------------------------- +// This software is part of the OpenBeOS distribution and is covered +// by the OpenBeOS license. +// +// Copyright (c) 2003 Tyler Dauwalder, tyler@dauwalder.net +//--------------------------------------------------------------------- + +#ifndef _UDF_STRING_H +#define _UDF_STRING_H + +#include + +#include "kernel_cpp.h" + +#include "Array.h" +#include "UdfDebug.h" + +namespace Udf { + +/*! \brief String class that takes as input either a UTF8 string or a + CS0 unicode string and then provides access to said string in both + formats. + + For CS0 info, see: ECMA-167 1/7.2.2 (not very helpful), UDF-2.01 2.1.1 +*/ +class String { +public: + String(); + String(const char *utf8); + String(const char *cs0, uint32 length); + template + String(const array &dString); + ~String(); + + void SetTo(const char *utf8); + void SetTo(const char *cs0, uint32 length); + template + void SetTo(const array &dString); + + template + String& operator=(const array &dString); + + const char* Cs0() const { return fCs0String; } + const char* Utf8() const { return fUtf8String; } + uint32 Cs0Length() const { return fCs0Length; } + uint32 Utf8Length() const { return fUtf8String ? strlen(fUtf8String) : 0; } + +private: + void _Clear(); + + char *fCs0String; + uint32 fCs0Length; + char *fUtf8String; +}; + +/*! \brief Creates a new String object from the given d-string. +*/ +template +String::String(const array &dString) + : fCs0String(NULL) + , fUtf8String(NULL) +{ + DEBUG_INIT_ETC("String", ("dString.length(): %ld", dString.length())); + + SetTo(dString); +} + +/*! \brief Assignment from a d-string. + + The last byte of a d-string specifies the data length of the + enclosed Cs0 string. +*/ +template +void +String::SetTo(const array &dString) +{ + uint8 dataLength = dString.length() == 0 + ? 0 + : reinterpret_cast(dString.data)[dString.length()-1]; + if (dataLength == 0 + || dataLength == 1 /* technically illegal, but... */) + { + SetTo(NULL); + } else { + if (dataLength > dString.length()-1) + dataLength = dString.length()-1; + SetTo(reinterpret_cast(dString.data), dataLength); + } +} + +/*! \brief Assignment from a d-string. +*/ +template +String& +String::operator=(const array &dString) +{ + SetTo(dString); + return *this; +} + + +}; // namespace UDF + + + +#endif // _UDF_STRING_H diff --git a/src/tests/add-ons/kernel/file_systems/udf/r5/UdfStructures.cpp b/src/tests/add-ons/kernel/file_systems/udf/r5/UdfStructures.cpp new file mode 100644 index 0000000000..4f94c74945 --- /dev/null +++ b/src/tests/add-ons/kernel/file_systems/udf/r5/UdfStructures.cpp @@ -0,0 +1,1169 @@ +//---------------------------------------------------------------------- +// This software is part of the OpenBeOS distribution and is covered +// by the OpenBeOS license. +// +// Copyright (c) 2003 Tyler Dauwalder, tyler@dauwalder.net +//---------------------------------------------------------------------- + +/*! \file UdfStructures.cpp + + UDF on-disk data structure definitions +*/ + +#include "UdfStructures.h" + +#include + +#include "UdfString.h" +#include "Utils.h" + +using namespace Udf; + +//---------------------------------------------------------------------- +// Constants +//---------------------------------------------------------------------- + +const charspec Udf::kCs0CharacterSet(0, "OSTA Compressed Unicode"); +//const charspec kCs0Charspec = { _character_set_type: 0, +// _character_set_info: "OSTA Compressed Unicode" +// "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" +// "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" +// }; + +// Volume structure descriptor ids +const char* Udf::kVSDID_BEA = "BEA01"; +const char* Udf::kVSDID_TEA = "TEA01"; +const char* Udf::kVSDID_BOOT = "BOOT2"; +const char* Udf::kVSDID_ISO = "CD001"; +const char* Udf::kVSDID_ECMA167_2 = "NSR02"; +const char* Udf::kVSDID_ECMA167_3 = "NSR03"; +const char* Udf::kVSDID_ECMA168 = "CDW02"; + +// entity_ids +const entity_id Udf::kMetadataPartitionMapId(0, "*UDF Metadata Partition"); +const entity_id Udf::kSparablePartitionMapId(0, "*UDF Sparable Partition"); +const entity_id Udf::kVirtualPartitionMapId(0, "*UDF Virtual Partition"); +const entity_id Udf::kImplementationId(0, "*OpenBeOS UDF", implementation_id_suffix(OS_BEOS, BEOS_GENERIC)); +const entity_id Udf::kPartitionContentsId1xx(0, "+NSR02"); +const entity_id Udf::kPartitionContentsId2xx(0, "+NSR03"); +const entity_id Udf::kLogicalVolumeInfoId150(0, "*UDF LV Info", udf_id_suffix(0x0150, OS_BEOS, BEOS_GENERIC)); +const entity_id Udf::kLogicalVolumeInfoId201(0, "*UDF LV Info", udf_id_suffix(0x0201, OS_BEOS, BEOS_GENERIC)); +const entity_id Udf::kDomainId150(0, "*OSTA UDF Compliant", domain_id_suffix(0x0150, + DF_HARD_WRITE_PROTECT)); +const entity_id Udf::kDomainId201(0, "*OSTA UDF Compliant", domain_id_suffix(0x0201, + DF_HARD_WRITE_PROTECT)); + +//! crc 010041 table, as generated by crc_table.cpp +const uint16 Udf::kCrcTable[256] = { + 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, + 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, + 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6, + 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de, + 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485, + 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d, + 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4, + 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc, + 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823, + 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b, + 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12, + 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a, + 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41, + 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49, + 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70, + 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78, + 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f, + 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067, + 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e, + 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256, + 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d, + 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, + 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c, + 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634, + 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab, + 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3, + 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a, + 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92, + 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9, + 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1, + 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8, + 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0 +}; + +const uint32 Udf::kLogicalVolumeDescriptorBaseSize = sizeof(logical_volume_descriptor) + - (UDF_MAX_PARTITION_MAPS + * UDF_MAX_PARTITION_MAP_SIZE); + + +//---------------------------------------------------------------------- +// Helper functions +//---------------------------------------------------------------------- + +const char *Udf::tag_id_to_string(tag_id id) +{ + switch (id) { + case TAGID_UNDEFINED: + return "undefined"; + + case TAGID_PRIMARY_VOLUME_DESCRIPTOR: + return "primary volume descriptor"; + case TAGID_ANCHOR_VOLUME_DESCRIPTOR_POINTER: + return "anchor volume descriptor pointer"; + case TAGID_VOLUME_DESCRIPTOR_POINTER: + return "volume descriptor pointer"; + case TAGID_IMPLEMENTATION_USE_VOLUME_DESCRIPTOR: + return "implementation use volume descriptor"; + case TAGID_PARTITION_DESCRIPTOR: + return "partition descriptor"; + case TAGID_LOGICAL_VOLUME_DESCRIPTOR: + return "logical volume descriptor"; + case TAGID_UNALLOCATED_SPACE_DESCRIPTOR: + return "unallocated space descriptor"; + case TAGID_TERMINATING_DESCRIPTOR: + return "terminating descriptor"; + case TAGID_LOGICAL_VOLUME_INTEGRITY_DESCRIPTOR: + return "logical volume integrity descriptor"; + + case TAGID_FILE_SET_DESCRIPTOR: + return "file set descriptor"; + case TAGID_FILE_ID_DESCRIPTOR: + return "file identifier descriptor"; + case TAGID_ALLOCATION_EXTENT_DESCRIPTOR: + return "allocation extent descriptor"; + case TAGID_INDIRECT_ENTRY: + return "indirect entry"; + case TAGID_TERMINAL_ENTRY: + return "terminal entry"; + case TAGID_FILE_ENTRY: + return "file entry"; + case TAGID_EXTENDED_ATTRIBUTE_HEADER_DESCRIPTOR: + return "extended attribute header descriptor"; + case TAGID_UNALLOCATED_SPACE_ENTRY: + return "unallocated space entry"; + case TAGID_SPACE_BITMAP_DESCRIPTOR: + return "space bitmap descriptor"; + case TAGID_PARTITION_INTEGRITY_ENTRY: + return "partition integrity entry"; + case TAGID_EXTENDED_FILE_ENTRY: + return "extended file entry"; + + default: + if (TAGID_CUSTOM_START <= id && id <= TAGID_CUSTOM_END) + return "custom"; + return "reserved"; + } +} + + +//---------------------------------------------------------------------- +// volume_structure_descriptor_header +//---------------------------------------------------------------------- + +volume_structure_descriptor_header::volume_structure_descriptor_header(uint8 type, const char *_id, uint8 version) + : type(type) + , version(version) +{ + memcpy(id, _id, 5); +} + + +/*! \brief Returns true if the given \a id matches the header's id. +*/ +bool +volume_structure_descriptor_header::id_matches(const char *id) +{ + return strncmp(this->id, id, 5) == 0; +} + + +//---------------------------------------------------------------------- +// charspec +//---------------------------------------------------------------------- + +charspec::charspec(uint8 type, const char *info) +{ + set_character_set_type(type); + set_character_set_info(info); +} + +void +charspec::dump() const +{ + DUMP_INIT("charspec"); + PRINT(("character_set_type: %d\n", character_set_type())); + PRINT(("character_set_info: `%s'\n", character_set_info())); +} + +void +charspec::set_character_set_info(const char *info) +{ + memset(_character_set_info, 0, 63); + if (info) + strncpy(_character_set_info, info, 63); +} + +//---------------------------------------------------------------------- +// timestamp +//---------------------------------------------------------------------- + +#if _KERNEL_MODE +static +int +get_month_length(int month, int year) +{ + if (0 <= month && month < 12 && year >= 1970) { + const int monthLengths[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; + int result = monthLengths[month]; + if (month == 1 && ((year - 1968) % 4 == 0)) + result++; + return result; + } else { + DEBUG_INIT_ETC(NULL, ("month: %d, year: %d", month, year)); + PRINT(("Invalid month or year! Returning 0\n")); + return 0; + } +} +#endif + +timestamp::timestamp(time_t time) +{ +#if !_KERNEL_MODE + // Is it me, or is localtime() broken? + tm *local = localtime(&time); + if (local) { + set_microsecond(0); + set_hundred_microsecond(0); + set_centisecond(0); + set_second(local->tm_sec); + set_minute(local->tm_min); + set_hour(local->tm_hour); + set_day(local->tm_mday); + set_month(local->tm_mon+1); + set_year(local->tm_year+1900); + set_type(1); + set_timezone(local->tm_gmtoff / 60); + } else { + _clear(); + } +#else // no localtime() in the R5 kernel... + // real_time_clock() is returning the time offset by -16 hours. + // Considering I'm -8 hours from GMT, this doesn't really make + // sense. For the moment I'm offsetting it manually here, but + // I'm not sure what the freaking deal is, and unfortunately, + // localtime() appears to be broken... + time += 16 * 60 * 60; + + set_microsecond(0); + set_hundred_microsecond(0); + set_centisecond(0); + set_second(time % 60); + time = time / 60; // convert to minutes + set_minute(time % 60); + time = time / 60; // convert to hours + set_hour(time % 24); + time = time / 24; // convert to days + + // From here we start at time == 0 and count up + // by days until we figure out what the day, month, + // and year are. + int year = 0; + int month = 0; + time_t clock = 0; + for (clock = 0; + clock + get_month_length(month, year+1970) < time; + clock += get_month_length(month, year+1970)) + { + month++; + if (month == 12) { + year++; + month = 0; + } + } + int day = time - clock; + set_day(day); + set_month(month+1); + set_year(year+1970); + set_type(1); + set_timezone(-2047); // -2047 == no timezone specified +#endif +} + +void +timestamp::dump() const +{ + DUMP_INIT("timestamp"); + PRINT(("type: %d\n", type())); + PRINT(("timezone: %d\n", timezone())); + PRINT(("year: %d\n", year())); + PRINT(("month: %d\n", month())); + PRINT(("day: %d\n", day())); + PRINT(("hour: %d\n", hour())); + PRINT(("minute: %d\n", minute())); + PRINT(("second: %d\n", second())); + PRINT(("centisecond: %d\n", centisecond())); + PRINT(("hundred_microsecond: %d\n", hundred_microsecond())); + PRINT(("microsecond: %d\n", microsecond())); +} + +void +timestamp::_clear() +{ + set_microsecond(0); + set_hundred_microsecond(0); + set_centisecond(0); + set_second(0); + set_minute(0); + set_hour(0); + set_day(0); + set_month(0); + set_year(0); + set_type(0); + set_timezone(0); +} + +//---------------------------------------------------------------------- +// udf_id_suffix +//---------------------------------------------------------------------- + +udf_id_suffix::udf_id_suffix(uint16 udfRevision, uint8 os_class, + uint8 os_identifier) + : _udf_revision(udfRevision) + , _os_class(os_class) + , _os_identifier(os_identifier) +{ + memset(_reserved.data, 0, _reserved.size()); +} + +//---------------------------------------------------------------------- +// implementation_id_suffix +//---------------------------------------------------------------------- + +implementation_id_suffix::implementation_id_suffix(uint8 os_class, + uint8 os_identifier) + : _os_class(os_class) + , _os_identifier(os_identifier) +{ + memset(_implementation_use.data, 0, _implementation_use.size()); +} + +//---------------------------------------------------------------------- +// domain_id_suffix +//---------------------------------------------------------------------- + +domain_id_suffix::domain_id_suffix(uint16 udfRevision, uint8 domainFlags) + : _udf_revision(udfRevision) + , _domain_flags(domainFlags) +{ + memset(_reserved.data, 0, _reserved.size()); +} + +//---------------------------------------------------------------------- +// entity_id +//---------------------------------------------------------------------- + +entity_id::entity_id(uint8 flags, char *identifier, uint8 *identifier_suffix) + : _flags(flags) +{ + memset(_identifier, 0, kIdentifierLength); + if (identifier) + strncpy(_identifier, identifier, kIdentifierLength); + if (identifier_suffix) + memcpy(_identifier_suffix.data, identifier_suffix, kIdentifierSuffixLength); + else + memset(_identifier_suffix.data, 0, kIdentifierSuffixLength); +} + +entity_id::entity_id(uint8 flags, char *identifier, + const udf_id_suffix &suffix) + : _flags(flags) +{ + memset(_identifier, 0, kIdentifierLength); + if (identifier) + strncpy(_identifier, identifier, kIdentifierLength); + memcpy(_identifier_suffix.data, &suffix, kIdentifierSuffixLength); +} + +entity_id::entity_id(uint8 flags, char *identifier, + const implementation_id_suffix &suffix) + : _flags(flags) +{ + memset(_identifier, 0, kIdentifierLength); + if (identifier) + strncpy(_identifier, identifier, kIdentifierLength); + memcpy(_identifier_suffix.data, &suffix, kIdentifierSuffixLength); +} + +entity_id::entity_id(uint8 flags, char *identifier, + const domain_id_suffix &suffix) + : _flags(flags) +{ + memset(_identifier, 0, kIdentifierLength); + if (identifier) + strncpy(_identifier, identifier, kIdentifierLength); + memcpy(_identifier_suffix.data, &suffix, kIdentifierSuffixLength); +} + +void +entity_id::dump() const +{ + DUMP_INIT("entity_id"); + PRINT(("flags: %d\n", flags())); + PRINT(("identifier: `%.23s'\n", identifier())); + PRINT(("identifier_suffix:\n")); + DUMP(identifier_suffix()); +} + +bool +entity_id::matches(const entity_id &id) const +{ + bool result = true; + for (int i = 0; i < entity_id::kIdentifierLength; i++) { + if (identifier()[i] != id.identifier()[i]) { + result = false; + break; + } + } + return result; +} + +//---------------------------------------------------------------------- +// extent_address +//---------------------------------------------------------------------- + +extent_address::extent_address(uint32 location, uint32 length) +{ + set_location(location); + set_length(length); +} + +void +extent_address::dump() const +{ + DUMP_INIT("extent_address"); + PRINT(("length: %ld\n", length())); + PRINT(("location: %ld\n", location())); +} + +//---------------------------------------------------------------------- +// logical_block_address +//---------------------------------------------------------------------- + +void +logical_block_address::dump() const +{ + DUMP_INIT("logical_block_address"); + PRINT(("block: %ld\n", block())); + PRINT(("partition: %d\n", partition())); +} + +logical_block_address::logical_block_address(uint16 partition, uint32 block) +{ + set_partition(partition); + set_block(block); +} + +//---------------------------------------------------------------------- +// long_address +//---------------------------------------------------------------------- + +long_address::long_address(uint16 partition, uint32 block, uint32 length, + uint8 type) +{ + set_partition(partition); + set_block(block); + set_length(length); + set_type(type); + memset(_implementation_use.data, 0, _implementation_use.size()); +} + +void +long_address::dump() const +{ + DUMP_INIT("long_address"); + PRINT(("length: %ld\n", length())); + PRINT(("block: %ld\n", block())); + PRINT(("partition: %d\n", partition())); + PRINT(("implementation_use:\n")); + DUMP(implementation_use()); +} + +//---------------------------------------------------------------------- +// descriptor_tag +//---------------------------------------------------------------------- + +void +descriptor_tag::dump() const +{ + DUMP_INIT("descriptor_tag"); + PRINT(("id: %d (%s)\n", id(), tag_id_to_string(tag_id(id())))); + PRINT(("version: %d\n", version())); + PRINT(("checksum: %d\n", checksum())); + PRINT(("serial_number: %d\n", serial_number())); + PRINT(("crc: %d\n", crc())); + PRINT(("crc_length: %d\n", crc_length())); + PRINT(("location: %ld\n", location())); +} + + +/*! \brief Calculates the tag's CRC, verifies the tag's checksum, and + verifies the tag's location on the medium. + + Note that this function makes the assumption that the descriptor_tag + is the first data member in a larger descriptor structure, the remainder + of which immediately follows the descriptor_tag itself in memory. This + is generally a safe assumption, as long as the entire descriptor (and + not the its tag) is read in before init_check() is called. If this is + not the case, it's best to call this function with a \a calculateCrc + value of false, to keep from trying to calculate a crc value on invalid + and possibly unowned memory. + + \param block The block location of this descriptor as taken from the + corresponding allocation descriptor. If the address specifies + a block in a partition, the partition block is the desired + location, not the mapped physical disk block. + \param calculateCrc Whether or not to perform the crc calculation + on the descriptor data following the tag. +*/ +status_t +descriptor_tag::init_check(uint32 block, bool calculateCrc) +{ + DEBUG_INIT_ETC("descriptor_tag", ("location: %ld, calculateCrc: %s", + block, bool_to_string(calculateCrc))); + PRINT(("location (paramater) == %ld\n", block)); + PRINT(("location (in structure) == %ld\n", location())); + if (calculateCrc) { + PRINT(("crc (calculated) == %d\n", + Udf::calculate_crc(reinterpret_cast(this)+sizeof(descriptor_tag), + crc_length()))) + } else { + PRINT(("crc (calculated) == (not calculated)\n")); + } + PRINT(("crc (in structure) == %d\n", crc())); + PRINT(("crc_length (in structure) == %d\n", crc_length())); + // location + status_t error = (block == location()) ? B_OK : B_NO_INIT; + // checksum + if (!error) { + uint32 sum = 0; + for (int i = 0; i <= 3; i++) + sum += reinterpret_cast(this)[i]; + for (int i = 5; i <= 15; i++) + sum += reinterpret_cast(this)[i]; + error = sum % 256 == checksum() ? B_OK : B_NO_INIT; + } + // crc + if (!error && calculateCrc) { + uint16 _crc = Udf::calculate_crc(reinterpret_cast(this) + + sizeof(descriptor_tag), crc_length()); + error = _crc == crc() ? B_OK : B_NO_INIT; + } + RETURN(error); +} + +//---------------------------------------------------------------------- +// primary_volume_descriptor +//---------------------------------------------------------------------- + +void +primary_volume_descriptor::dump() const +{ + DUMP_INIT("primary_volume_descriptor"); + + String string; + + PRINT(("tag:\n")); + DUMP(tag()); + PRINT(("vds_number: %ld\n", vds_number())); + PRINT(("primary_volume_descriptor_number: %ld\n", primary_volume_descriptor_number())); + string = volume_identifier(); + PRINT(("volume_identifier: `%s'\n", string.Utf8())); + PRINT(("volume_sequence_number: %d\n", volume_sequence_number())); + PRINT(("max_volume_sequence_number: %d\n", max_volume_sequence_number())); + PRINT(("interchange_level: %d\n", interchange_level())); + PRINT(("max_interchange_level: %d\n", max_interchange_level())); + PRINT(("character_set_list: %ld\n", character_set_list())); + PRINT(("max_character_set_list: %ld\n", max_character_set_list())); + string = volume_set_identifier(); + PRINT(("volume_set_identifier: `%s'\n", string.Utf8())); + PRINT(("descriptor_character_set:\n")); + DUMP(descriptor_character_set()); + PRINT(("explanatory_character_set:\n")); + DUMP(explanatory_character_set()); + PRINT(("volume_abstract:\n")); + DUMP(volume_abstract()); + PRINT(("volume_copyright_notice:\n")); + DUMP(volume_copyright_notice()); + PRINT(("application_id:\n")); + DUMP(application_id()); + PRINT(("recording_date_and_time:\n")); + DUMP(recording_date_and_time()); + PRINT(("implementation_id:\n")); + DUMP(implementation_id()); + PRINT(("implementation_use:\n")); + DUMP(implementation_use()); + PRINT(("predecessor_vds_location: %ld\n", + predecessor_volume_descriptor_sequence_location())); + PRINT(("flags: %d\n", flags())); +} + + +//---------------------------------------------------------------------- +// anchor_volume_descriptor_pointer +//---------------------------------------------------------------------- + +void +anchor_volume_descriptor::dump() const +{ + DUMP_INIT("anchor_volume_descriptor"); + PRINT(("tag:\n")); + DUMP(tag()); + PRINT(("main_vds:\n")); + DUMP(main_vds()); + PRINT(("reserve_vds:\n")); + DUMP(reserve_vds()); +} + +//---------------------------------------------------------------------- +// logical_volume_info +//---------------------------------------------------------------------- + +void +logical_volume_info::dump() const +{ + String string; + DUMP_INIT("logical_volume_information"); + PRINT(("character_set:\n")); + DUMP(character_set()); + string = logical_volume_id(); + PRINT(("logical_volume_id: `%s'\n", string.Utf8())); + for (uint32 i = 0; i < _logical_volume_info.length(); i++) { + string = _logical_volume_info[i]; + PRINT(("logical_volume_info #%ld: %s\n", i, string.Utf8())); + } + PRINT(("implementation_id:\n")); + DUMP(implementation_id()); + PRINT(("implementation_use:\n")); + DUMP(implementation_use()); +} + +//---------------------------------------------------------------------- +// implementation_use_descriptor +//---------------------------------------------------------------------- + +void +implementation_use_descriptor::dump() const +{ + DUMP_INIT("implementation_use_descriptor"); + PRINT(("tag:\n")); + DUMP(tag()); + PRINT(("vds_number: %ld\n", vds_number())); + PRINT(("implementation_id:\n")); + DUMP(implementation_id()); + PRINT(("implementation_use: XXX\n")); + DUMP(implementation_use()); +} + +//---------------------------------------------------------------------- +// partition_descriptor +//---------------------------------------------------------------------- + +const uint8 Udf::kMaxPartitionDescriptors = 2; + +void +partition_descriptor::dump() const +{ + DUMP_INIT("partition_descriptor"); + PRINT(("tag:\n")); + DUMP(tag()); + PRINT(("vds_number: %ld\n", vds_number())); + PRINT(("partition_flags: %d\n", partition_flags())); + PRINT(("partition_flags.allocated: %s\n", allocated() ? "true" : "false")); + PRINT(("partition_number: %d\n", partition_number())); + PRINT(("partition_contents:\n")); + DUMP(partition_contents()); + PRINT(("partition_contents_use: XXX\n")); + DUMP(partition_contents_use()); + PRINT(("access_type: %ld\n", access_type())); + PRINT(("start: %ld\n", start())); + PRINT(("length: %ld\n", length())); + PRINT(("implementation_id:\n")); + DUMP(implementation_id()); + PRINT(("implementation_use: XXX\n")); + DUMP(implementation_use()); +} + +//---------------------------------------------------------------------- +// logical_volume_descriptor +//---------------------------------------------------------------------- + +void +logical_volume_descriptor::dump() const +{ + DUMP_INIT("logical_volume_descriptor"); + PRINT(("tag:\n")); + DUMP(tag()); + PRINT(("vds_number: %ld\n", vds_number())); + PRINT(("character_set:\n")); + DUMP(character_set()); + String string(logical_volume_identifier()); + PRINT(("logical_volume_identifier: `%s'\n", string.Utf8())); + PRINT(("logical_block_size: %ld\n", logical_block_size())); + PRINT(("domain_id:\n")); + DUMP(domain_id()); + PRINT(("logical_volume_contents_use:\n")); + DUMP(logical_volume_contents_use()); + PRINT(("file_set_address:\n")); + DUMP(file_set_address()); + PRINT(("map_table_length: %ld\n", map_table_length())); + PRINT(("partition_map_count: %ld\n", partition_map_count())); + PRINT(("implementation_id:\n")); + DUMP(implementation_id()); + PRINT(("implementation_use:\n")); + DUMP(implementation_use()); + PRINT(("integrity_sequence_extent:\n")); + DUMP(integrity_sequence_extent()); +// PRINT(("partition_maps:\n")); + const uint8 *maps = partition_maps(); + int offset = 0; + for (uint i = 0; i < partition_map_count(); i++) { + PRINT(("partition_map #%d:\n", i)); + uint8 type = maps[offset]; + uint8 length = maps[offset+1]; + PRINT((" type: %d\n", type)); + PRINT((" length: %d\n", length)); + switch (type) { + case 1: + for (int j = 0; j < length-2; j++) + PRINT((" data[%d]: %d\n", j, maps[offset+2+j])); + break; + case 2: { + PRINT((" partition_number: %d\n", *reinterpret_cast(&(maps[offset+38])))); + PRINT((" entity_id:\n")); + const entity_id *id = reinterpret_cast(&(maps[offset+4])); + if (id) // To kill warning when DEBUG==0 + PDUMP(id); + break; + } + } + offset += maps[offset+1]; + } + // \todo dump partition_maps +} + + +logical_volume_descriptor& +logical_volume_descriptor::operator=(const logical_volume_descriptor &rhs) +{ + _tag = rhs._tag; + _vds_number = rhs._vds_number; + _character_set = rhs._character_set; + _logical_volume_identifier = rhs._logical_volume_identifier; + _logical_block_size = rhs._logical_block_size; + _domain_id = rhs._domain_id; + _logical_volume_contents_use = rhs._logical_volume_contents_use; + _map_table_length = rhs._map_table_length; + _partition_map_count = rhs._partition_map_count; + _implementation_id = rhs._implementation_id; + _implementation_use = rhs._implementation_use; + _integrity_sequence_extent = rhs._integrity_sequence_extent; + // copy the partition maps one by one + uint8 *lhsMaps = partition_maps(); + const uint8 *rhsMaps = rhs.partition_maps(); + int offset = 0; + for (uint8 i = 0; i < rhs.partition_map_count(); i++) { + uint8 length = rhsMaps[offset+1]; + memcpy(&lhsMaps[offset], &rhsMaps[offset], length); + offset += length; + } + return *this; +} + + +//---------------------------------------------------------------------- +// physical_partition_map +//---------------------------------------------------------------------- + +void +physical_partition_map::dump() +{ + DUMP_INIT("physical_partition_map"); + PRINT(("type: %d\n", type())); + PRINT(("length: %d\n", length())); + PRINT(("volume_sequence_number: %d\n", volume_sequence_number())); + PRINT(("partition_number: %d\n", partition_number())); +} + +//---------------------------------------------------------------------- +// sparable_partition_map +//---------------------------------------------------------------------- + +void +sparable_partition_map::dump() +{ + DUMP_INIT("sparable_partition_map"); + PRINT(("type: %d\n", type())); + PRINT(("length: %d\n", length())); + PRINT(("partition_type_id:")); + DUMP(partition_type_id()); + PRINT(("volume_sequence_number: %d\n", volume_sequence_number())); + PRINT(("partition_number: %d\n", partition_number())); + PRINT(("sparing_table_count: %d\n", sparing_table_count())); + PRINT(("sparing_table_size: %ld\n", sparing_table_size())); + PRINT(("sparing_table_locations:")); + for (uint8 i = 0; i < sparing_table_count(); i++) + PRINT((" %d: %ld\n", i, sparing_table_location(i))); +} + +//---------------------------------------------------------------------- +// unallocated_space_descriptor +//---------------------------------------------------------------------- + +void +unallocated_space_descriptor::dump() const +{ + DUMP_INIT("unallocated_space_descriptor"); + PRINT(("tag:\n")); + DUMP(tag()); + PRINT(("vds_number: %ld\n", vds_number())); + PRINT(("allocation_descriptor_count: %ld\n", allocation_descriptor_count())); + // \todo dump alloc_descriptors +} + + +//---------------------------------------------------------------------- +// terminating_descriptor +//---------------------------------------------------------------------- + +void +terminating_descriptor::dump() const +{ + DUMP_INIT("terminating_descriptor"); + PRINT(("tag:\n")); + DUMP(tag()); +} + +//---------------------------------------------------------------------- +// file_set_descriptor +//---------------------------------------------------------------------- + +void +file_set_descriptor::dump() const +{ + DUMP_INIT("file_set_descriptor"); + PRINT(("tag:\n")); + DUMP(tag()); + PRINT(("recording_date_and_time:\n")); + DUMP(recording_date_and_time()); + PRINT(("interchange_level: %d\n", interchange_level())); + PRINT(("max_interchange_level: %d\n", max_interchange_level())); + PRINT(("character_set_list: %ld\n", character_set_list())); + PRINT(("max_character_set_list: %ld\n", max_character_set_list())); + PRINT(("file_set_number: %ld\n", file_set_number())); + PRINT(("file_set_descriptor_number: %ld\n", file_set_descriptor_number())); + PRINT(("logical_volume_id_character_set:\n")); + DUMP(logical_volume_id_character_set()); + PRINT(("logical_volume_id:\n")); + DUMP(logical_volume_id()); + PRINT(("file_set_id_character_set:\n")); + DUMP(file_set_id_character_set()); + PRINT(("file_set_id:\n")); + DUMP(file_set_id()); + PRINT(("copyright_file_id:\n")); + DUMP(copyright_file_id()); + PRINT(("abstract_file_id:\n")); + DUMP(abstract_file_id()); + PRINT(("root_directory_icb:\n")); + DUMP(root_directory_icb()); + PRINT(("domain_id:\n")); + DUMP(domain_id()); + PRINT(("next_extent:\n")); + DUMP(next_extent()); + PRINT(("system_stream_directory_icb:\n")); + DUMP(system_stream_directory_icb()); +} + +//---------------------------------------------------------------------- +// logical_volume_integrity_descriptor +//---------------------------------------------------------------------- + +void +logical_volume_integrity_descriptor::dump() const +{ + DUMP_INIT("logical_volume_integrity_descriptor"); + PRINT(("tag:\n")); + DUMP(tag()); + PRINT(("recording_time:\n")); + DUMP(recording_time()); + PRINT(("integrity_type: ")); + switch (integrity_type()) { + case INTEGRITY_OPEN: + SIMPLE_PRINT(("open\n")); + break; + case INTEGRITY_CLOSED: + SIMPLE_PRINT(("closed\n")); + break; + default: + SIMPLE_PRINT(("invalid integrity type (%ld)", integrity_type())); + break; + } + PRINT(("next_integrity_extent:\n")); + DUMP(next_integrity_extent()); + PRINT(("logical_volume_contents_use:\n")); + DUMP(logical_volume_contents_use()); + PRINT(("next_unique_id: %Ld\n", next_unique_id())); + PRINT(("partition_count: %ld\n", partition_count())); + PRINT(("implementation_use_length: %ld\n", implementation_use_length())); + if (partition_count() > 0) { + PRINT(("free_space_table:\n")); + for (uint32 i = 0; i < partition_count(); i++) { + PRINT(("partition %ld: %ld free blocks\n", i, free_space_table()[i])); + } + PRINT(("size_table:\n")); + for (uint32 i = 0; i < partition_count(); i++) { + PRINT(("partition %ld: %ld blocks large\n", i, size_table()[i])); + } + } + + if (implementation_use_length() >= minimum_implementation_use_length) { + PRINT(("implementation_id:\n")); + DUMP(implementation_id()); + PRINT(("file_count: %ld\n", file_count())); + PRINT(("directory_count: %ld\n", directory_count())); + PRINT(("minimum_udf_read_revision: 0x%04x\n", minimum_udf_read_revision())); + PRINT(("minimum_udf_write_revision: 0x%04x\n", minimum_udf_write_revision())); + PRINT(("maximum_udf_write_revision: 0x%04x\n", maximum_udf_write_revision())); + } else { + PRINT(("NOTE: implementation_use() field of insufficient length to contain \n")); + PRINT((" appropriate UDF-2.50 2.2.6.4 fields.\n")); + } +} + +//---------------------------------------------------------------------- +// file_id_descriptor +//---------------------------------------------------------------------- + +void +file_id_descriptor::dump() const +{ + DUMP_INIT("file_id_descriptor"); + PRINT(("tag:\n")); + DUMP(tag()); + PRINT(("version_number: %d\n", version_number())); + PRINT(("may_be_hidden: %d\n", may_be_hidden())); + PRINT(("is_directory: %d\n", is_directory())); + PRINT(("is_deleted: %d\n", is_deleted())); + PRINT(("is_parent: %d\n", is_parent())); + PRINT(("is_metadata_stream: %d\n", is_metadata_stream())); + PRINT(("id_length: %d\n", id_length())); + PRINT(("icb:\n")); + DUMP(icb()); + PRINT(("implementation_use_length: %d\n", is_parent())); + String fileId(id()); + PRINT(("id: `%s'", fileId.Utf8())); +} + +//---------------------------------------------------------------------- +// icb_entry_tag +//---------------------------------------------------------------------- + +void +icb_entry_tag::dump() const +{ + DUMP_INIT("icb_entry_tag"); + PRINT(("prior_entries: %ld\n", prior_recorded_number_of_direct_entries())); + PRINT(("strategy_type: %d\n", strategy_type())); + PRINT(("strategy_parameters:\n")); + DUMP(strategy_parameters()); + PRINT(("entry_count: %d\n", entry_count())); + PRINT(("file_type: %d\n", file_type())); + PRINT(("parent_icb_location:\n")); + DUMP(parent_icb_location()); + PRINT(("all_flags: %d\n", flags())); + +/* + uint32 prior_recorded_number_of_direct_entries; + uint16 strategy_type; + array strategy_parameters; + uint16 entry_count; + uint8 reserved; + uint8 file_type; + logical_block_address parent_icb_location; + union { + uint16 all_flags; + struct { + uint16 descriptor_flags:3, + if_directory_then_sort:1, //!< To be set to 0 per UDF-2.01 2.3.5.4 + non_relocatable:1, + archive:1, + setuid:1, + setgid:1, + sticky:1, + contiguous:1, + system:1, + transformed:1, + multi_version:1, //!< To be set to 0 per UDF-2.01 2.3.5.4 + is_stream:1, + reserved_icb_entry_flags:2; + } flags; + }; + +*/ + +} + +//---------------------------------------------------------------------- +// icb_header +//---------------------------------------------------------------------- + +void +icb_header::dump() const +{ + DUMP_INIT("icb_header"); + + PRINT(("tag:\n")); + DUMP(tag()); + PRINT(("icb_tag:\n")); + DUMP(icb_tag()); + +} + +//---------------------------------------------------------------------- +// file_icb_entry +//---------------------------------------------------------------------- + +long_address file_icb_entry::_dummy_stream_directory_icb; + +void +file_icb_entry::dump() const +{ + DUMP_INIT("file_icb_entry"); + + PRINT(("tag:\n")); + DUMP(tag()); + PRINT(("icb_tag:\n")); + DUMP(icb_tag()); + + PRINT(("uid: %lu, 0x%lx\n", uid(), uid())); + PRINT(("gid: %lu, 0x%lx\n", gid(), gid())); + PRINT(("permissions: %ld, 0x%lx\n", permissions(), permissions())); + PRINT(("file_link_count: %d\n", file_link_count())); + PRINT(("record_format: %d\n", record_format())); + PRINT(("record_display_attributes: %d\n", record_display_attributes())); + PRINT(("record_length: %d\n", record_length())); + PRINT(("information_length: %Ld\n", information_length())); + PRINT(("logical_blocks_recorded: %Ld\n", logical_blocks_recorded())); + PRINT(("access_date_and_time:\n")); + DUMP(access_date_and_time()); + PRINT(("modification_date_and_time:\n")); + DUMP(modification_date_and_time()); + PRINT(("attribute_date_and_time:\n")); + DUMP(attribute_date_and_time()); + PRINT(("checkpoint: %ld\n", checkpoint())); + + PRINT(("extended_attribute_icb:\n")); + DUMP(extended_attribute_icb()); + PRINT(("implementation_id:\n")); + DUMP(implementation_id()); + + PRINT(("unique_id: %Ld\n", unique_id())); + PRINT(("extended_attributes_length: %ld\n", extended_attributes_length())); + PRINT(("allocation_descriptors_length: %ld\n", allocation_descriptors_length())); + + PRINT(("allocation_descriptors:\n")); + switch (icb_tag().descriptor_flags()) { + case ICB_DESCRIPTOR_TYPE_SHORT: + PRINT((" short descriptors...\n")); + break; + case ICB_DESCRIPTOR_TYPE_LONG: + { + const long_address *address = reinterpret_cast(allocation_descriptors()); + for (uint32 length = allocation_descriptors_length(); + length >= sizeof(long_address); + length -= sizeof(long_address), address++) + { + PDUMP(address); + } + break; + } + case ICB_DESCRIPTOR_TYPE_EXTENDED: + PRINT((" extended descriptors...\n")); + break; + case ICB_DESCRIPTOR_TYPE_EMBEDDED: + PRINT((" embedded descriptors...\n")); + break; + default: + PRINT((" invalid descriptors type\n")); + break; + } +} + +//---------------------------------------------------------------------- +// extended_file_icb_entry +//---------------------------------------------------------------------- + +void +extended_file_icb_entry::dump() const +{ + DUMP_INIT("extended_file_icb_entry"); + + PRINT(("tag:\n")); + DUMP(tag()); + PRINT(("icb_tag:\n")); + DUMP(icb_tag()); + + PRINT(("uid: %lu, 0x%lx\n", uid(), uid())); + PRINT(("gid: %lu, 0x%lx\n", gid(), gid())); + PRINT(("permissions: %ld, 0x%lx\n", permissions(), permissions())); + PRINT(("file_link_count: %d\n", file_link_count())); + PRINT(("record_format: %d\n", record_format())); + PRINT(("record_display_attributes: %d\n", record_display_attributes())); + PRINT(("record_length: %ld\n", record_length())); + PRINT(("information_length: %Ld\n", information_length())); + PRINT(("logical_blocks_recorded: %Ld\n", logical_blocks_recorded())); + PRINT(("access_date_and_time:\n")); + DUMP(access_date_and_time()); + PRINT(("modification_date_and_time:\n")); + DUMP(modification_date_and_time()); + PRINT(("creation_date_and_time:\n")); + DUMP(creation_date_and_time()); + PRINT(("attribute_date_and_time:\n")); + DUMP(attribute_date_and_time()); + PRINT(("checkpoint: %ld\n", checkpoint())); + + PRINT(("extended_attribute_icb:\n")); + DUMP(extended_attribute_icb()); + PRINT(("stream_directory_icb:\n")); + DUMP(stream_directory_icb()); + PRINT(("implementation_id:\n")); + DUMP(implementation_id()); + + PRINT(("unique_id: %Ld\n", unique_id())); + PRINT(("extended_attributes_length: %ld\n", extended_attributes_length())); + PRINT(("allocation_descriptors_length: %ld\n", allocation_descriptors_length())); + + PRINT(("allocation_descriptors:\n")); + switch (icb_tag().descriptor_flags()) { + case ICB_DESCRIPTOR_TYPE_SHORT: + PRINT((" short descriptors...\n")); + break; + case ICB_DESCRIPTOR_TYPE_LONG: + { + const long_address *address = reinterpret_cast(allocation_descriptors()); + for (uint32 length = allocation_descriptors_length(); + length >= sizeof(long_address); + length -= sizeof(long_address), address++) + { + PDUMP(address); + } + break; + } + case ICB_DESCRIPTOR_TYPE_EXTENDED: + PRINT((" extended descriptors...\n")); + break; + case ICB_DESCRIPTOR_TYPE_EMBEDDED: + PRINT((" embedded descriptors...\n")); + break; + default: + PRINT((" invalid descriptors type\n")); + break; + } +} diff --git a/src/tests/add-ons/kernel/file_systems/udf/r5/UdfStructures.h b/src/tests/add-ons/kernel/file_systems/udf/r5/UdfStructures.h new file mode 100644 index 0000000000..d76f70224f --- /dev/null +++ b/src/tests/add-ons/kernel/file_systems/udf/r5/UdfStructures.h @@ -0,0 +1,2204 @@ +//---------------------------------------------------------------------- +// This software is part of the OpenBeOS distribution and is covered +// by the OpenBeOS license. +// +// Copyright (c) 2003 Tyler Dauwalder, tyler@dauwalder.net +//--------------------------------------------------------------------- +#ifndef _UDF_DISK_STRUCTURES_H +#define _UDF_DISK_STRUCTURES_H + +#include + +#include +#include + +#include "kernel_cpp.h" +#include "UdfDebug.h" + +#include "Array.h" + +/*! \file UdfStructures.h + + \brief UDF on-disk data structure declarations + + UDF is a specialization of the ECMA-167 standard. For the most part, + ECMA-167 structures are used by UDF with special restrictions. In a + few instances, UDF introduces its own structures to augment those + supplied by ECMA-167; those structures are clearly marked. + + For UDF info: http://www.osta.org + For ECMA info: http://www.ecma-international.org + + For lack of a better place to store this info, the structures that + are allowed to have length greater than the logical block size are + as follows (other length restrictions may be found in UDF-2.01 5.1): + - \c logical_volume_descriptor + - \c unallocated_space_descriptor + - \c logical_volume_integrity_descriptor + - \c space_bitmap_descriptor + + Other links of interest: + - Philips UDF verifier + - Possible test disc image generator (?) +*/ + +namespace Udf { + +//---------------------------------------------------------------------- +// ECMA-167 Part 1 +//---------------------------------------------------------------------- + +/*! \brief Character set specifications + + The character_set_info field shall be set to the ASCII string + "OSTA Compressed Unicode" (padded right with NULL chars). + + See also: ECMA 167 1/7.2.1, UDF-2.01 2.1.2 +*/ +struct charspec { +public: + charspec(uint8 type = 0, const char *info = NULL); + + void dump() const; + + uint8 character_set_type() const { return _character_set_type; } + const char* character_set_info() const { return _character_set_info; } + char* character_set_info() { return _character_set_info; } + + void set_character_set_type(uint8 type) { _character_set_type = type; } + void set_character_set_info(const char *info); +private: + uint8 _character_set_type; //!< to be set to 0 to indicate CS0 + char _character_set_info[63]; //!< "OSTA Compressed Unicode" +} __attribute__((packed)); + +extern const charspec kCs0CharacterSet; + +/*! \brief Date and time stamp + + See also: ECMA 167 1/7.3, UDF-2.01 2.1.4 +*/ +class timestamp { +private: + union type_and_timezone_accessor { + uint16 type_and_timezone; + struct { + uint16 timezone:12, + type:4; + } bits; + }; + +public: + timestamp() { _clear(); } + timestamp(time_t time); + + void dump() const; + + // Get functions + uint16 type_and_timezone() const { return B_LENDIAN_TO_HOST_INT16(_type_and_timezone); } + uint8 type() const { + type_and_timezone_accessor t; + t.type_and_timezone = type_and_timezone(); + return t.bits.type; + } + int16 timezone() const { + type_and_timezone_accessor t; + t.type_and_timezone = type_and_timezone(); + int16 result = t.bits.timezone; + // Fill the lefmost bits with ones if timezone is negative + result <<= 4; + result >>= 4; + return result; + } + uint16 year() const { return B_LENDIAN_TO_HOST_INT16(_year); } + uint8 month() const { return _month; } + uint8 day() const { return _day; } + uint8 hour() const { return _hour; } + uint8 minute() const { return _minute; } + uint8 second() const { return _second; } + uint8 centisecond() const { return _centisecond; } + uint8 hundred_microsecond() const { return _hundred_microsecond; } + uint8 microsecond() const { return _microsecond; } + + // Set functions + void set_type_and_timezone(uint16 type_and_timezone) { _type_and_timezone = B_HOST_TO_LENDIAN_INT16(type_and_timezone); } + void set_type(uint8 type) { + type_and_timezone_accessor t; + t.type_and_timezone = type_and_timezone(); + t.bits.type = type; + set_type_and_timezone(t.type_and_timezone); + } + void set_timezone(int16 tz) { + type_and_timezone_accessor t; + t.type_and_timezone = type_and_timezone(); + t.bits.timezone = tz; + set_type_and_timezone(t.type_and_timezone); + } + void set_year(uint16 year) { _year = B_HOST_TO_LENDIAN_INT16(year); } + void set_month(uint8 month) { _month = month; } + void set_day(uint8 day) { _day = day; } + void set_hour(uint8 hour) { _hour = hour; } + void set_minute(uint8 minute) { _minute = minute; } + void set_second(uint8 second) { _second = second; } + void set_centisecond(uint8 centisecond) { _centisecond = centisecond; } + void set_hundred_microsecond(uint8 hundred_microsecond) { _hundred_microsecond = hundred_microsecond; } + void set_microsecond(uint8 microsecond) { _microsecond = microsecond; } +private: + void _clear(); + + uint16 _type_and_timezone; + uint16 _year; + uint8 _month; + uint8 _day; + uint8 _hour; + uint8 _minute; + uint8 _second; + uint8 _centisecond; + uint8 _hundred_microsecond; + uint8 _microsecond; + +} __attribute__((packed)); + + +/*! \brief UDF ID Identify Suffix + + See also: UDF 2.50 2.1.5.3 +*/ +struct udf_id_suffix { +public: + udf_id_suffix(uint16 udfRevision, uint8 os_class, uint8 os_identifier); + + //! Note that revision 2.50 is denoted by 0x0250. + uint16 udf_revision() const { return _udf_revision; } + uint8 os_class() const { return _os_class; } + uint8 os_identifier() const { return _os_identifier; } + + void set_os_class(uint8 os_class) { _os_class = os_class; } + void set_os_identifier(uint8 identifier) { _os_identifier = identifier; } +private: + uint16 _udf_revision; + uint8 _os_class; + uint8 _os_identifier; + array _reserved; +}; + +/*! \brief Implementation ID Identify Suffix + + See also: UDF 2.50 2.1.5.3 +*/ +struct implementation_id_suffix { +public: + implementation_id_suffix(uint8 os_class, uint8 os_identifier); + + uint8 os_class() const { return _os_class; } + uint8 os_identifier() const { return _os_identifier; } + + void set_os_class(uint8 os_class) { _os_class = os_class; } + void set_os_identifier(uint8 identifier) { _os_identifier = identifier; } +private: + uint8 _os_class; + uint8 _os_identifier; + array _implementation_use; +}; + +/*! \brief Operating system classes for implementation_id_suffixes + + See also: Udf 2.50 6.3 +*/ +enum { + OS_UNDEFINED = 0, + OS_DOS, + OS_OS2, + OS_MACOS, + OS_UNIX, + OS_WIN9X, + OS_WINNT, + OS_OS400, + OS_BEOS, + OS_WINCE +}; + +/*! \brief BeOS operating system classes identifiers for implementation_id_suffixes + + See also: Udf 2.50 6.3 +*/ +enum { + BEOS_GENERIC = 0, + BEOS_OPENBEOS = 1 // not part of the standard, but perhaps someday. :-) +}; + +/*! \brief Domain ID Identify Suffix + + See also: UDF 2.50 2.1.5.3 +*/ +struct domain_id_suffix { +public: + domain_id_suffix(uint16 udfRevision, uint8 domainFlags); + + //! Note that revision 2.50 is denoted by 0x0250. + uint16 udf_revision() const { return _udf_revision; } + uint8 domain_flags() const { return _domain_flags; } + + void set_udf_revision(uint16 revision) { _udf_revision = B_HOST_TO_LENDIAN_INT16(revision); } + void set_domain_flags(uint8 flags) { _domain_flags = flags; } +private: + uint16 _udf_revision; + uint8 _domain_flags; + array _reserved; +}; + +/*! \brief Domain flags + + See also: UDF 2.50 2.1.5.3 +*/ +enum { + DF_HARD_WRITE_PROTECT = 0x01, + DF_SOFT_WRITE_PROTECT = 0x02 +}; + +/*! \brief Identifier used to designate the implementation responsible + for writing associated data structures on the medium. + + See also: ECMA 167 1/7.4, UDF 2.01 2.1.5 +*/ +struct entity_id { +public: + static const int kIdentifierLength = 23; + static const int kIdentifierSuffixLength = 8; + + entity_id(uint8 flags = 0, char *identifier = NULL, + uint8 *identifier_suffix = NULL); + entity_id(uint8 flags, char *identifier, + const udf_id_suffix &suffix); + entity_id(uint8 flags, char *identifier, + const implementation_id_suffix &suffix); + entity_id(uint8 flags, char *identifier, + const domain_id_suffix &suffix); + + void dump() const; + bool matches(const entity_id &id) const; + + // Get functions + uint8 flags() const { return _flags; } + const char* identifier() const { return _identifier; } + char* identifier() { return _identifier; } + const array& identifier_suffix() const { return _identifier_suffix; } + array& identifier_suffix() { return _identifier_suffix; } + + // Set functions + void set_flags(uint8 flags) { _flags = flags; } +private: + uint8 _flags; + char _identifier[kIdentifierLength]; + array _identifier_suffix; +} __attribute__((packed)); + +extern const entity_id kMetadataPartitionMapId; +extern const entity_id kSparablePartitionMapId; +extern const entity_id kVirtualPartitionMapId; +extern const entity_id kImplementationId; +extern const entity_id kPartitionContentsId1xx; +extern const entity_id kPartitionContentsId2xx; +extern const entity_id kUdfId; +extern const entity_id kLogicalVolumeInfoId150; +extern const entity_id kLogicalVolumeInfoId201; +extern const entity_id kDomainId150; +extern const entity_id kDomainId201; + +//---------------------------------------------------------------------- +// ECMA-167 Part 2 +//---------------------------------------------------------------------- + + +/*! \brief Header for volume structure descriptors + + Each descriptor consumes an entire block. All unused trailing + bytes in the descriptor should be set to 0. + + The following descriptors contain no more information than + that contained in the header: + + - BEA01: + - type: 0 + - id: "BEA01" + - version: 1 + + - TEA01: + - type: 0 + - id: "TEA01" + - version: 1 + + - NSR03: + - type: 0 + - id: "NSR03" + - version: 1 + + See also: ECMA 167 2/9.1 +*/ +struct volume_structure_descriptor_header { +public: + volume_structure_descriptor_header(uint8 type, const char *id, uint8 version); + + uint8 type; + char id[5]; + uint8 version; + + bool id_matches(const char *id); +} __attribute__((packed)); + +// Volume structure descriptor ids +extern const char* kVSDID_BEA; +extern const char* kVSDID_TEA; +extern const char* kVSDID_BOOT; +extern const char* kVSDID_ISO; +extern const char* kVSDID_ECMA167_2; +extern const char* kVSDID_ECMA167_3; +extern const char* kVSDID_ECMA168; + +//---------------------------------------------------------------------- +// ECMA-167 Part 3 +//---------------------------------------------------------------------- + + +/*! \brief Location and length of a contiguous chunk of data on the volume. + + \c _location is an absolute block address. + + See also: ECMA 167 3/7.1 +*/ +struct extent_address { +public: + extent_address(uint32 location = 0, uint32 length = 0); + + void dump() const; + + uint32 length() const { return B_LENDIAN_TO_HOST_INT32(_length); } + uint32 location() const { return B_LENDIAN_TO_HOST_INT32(_location); } + + void set_length(int32 length) { _length = B_HOST_TO_LENDIAN_INT32(length); } + void set_location(int32 location) { _location = B_HOST_TO_LENDIAN_INT32(location); } +private: + uint32 _length; + uint32 _location; +} __attribute__((packed)); + + +/*! \brief Location of a logical block within a logical volume. + + See also: ECMA 167 4/7.1 +*/ +struct logical_block_address { +public: + void dump() const; + logical_block_address(uint16 partition = 0, uint32 block = 0); + + uint32 block() const { return B_LENDIAN_TO_HOST_INT32(_block); } + uint16 partition() const { return B_LENDIAN_TO_HOST_INT16(_partition); } + + void set_block(uint32 block) { _block = B_HOST_TO_LENDIAN_INT32(block); } + void set_partition(uint16 partition) { _partition = B_HOST_TO_LENDIAN_INT16(partition); } + +private: + uint32 _block; //!< Block location relative to start of corresponding partition + uint16 _partition; //!< Numeric partition id within logical volume +} __attribute__((packed)); + +/*! \brief Extent types used in short_address, long_address, + and extended_address. + + See also: ECMA-167 4/14.14.1.1 +*/ +enum extent_type { + EXTENT_TYPE_RECORDED = 0, //!< Allocated and recorded + EXTENT_TYPE_ALLOCATED, //!< Allocated but unrecorded + EXTENT_TYPE_UNALLOCATED, //!< Unallocated and unrecorded + EXTENT_TYPE_CONTINUATION, //!< Specifies next extent of descriptors +}; + + +/*! \brief Allocation descriptor. + + See also: ECMA 167 4/14.14.1 +*/ +struct short_address { +private: + union type_and_length_accessor { + uint32 type_and_length; + struct { + uint32 length:30, + type:2; +// uint32 type:2, +// length:30; + } bits; + }; + +public: + void dump() const; + + uint8 type() const { + type_and_length_accessor t; + t.type_and_length = type_and_length(); + return t.bits.type; + } + uint32 length() const { + type_and_length_accessor t; + t.type_and_length = type_and_length(); + return t.bits.length; + } + uint32 block() const { return B_LENDIAN_TO_HOST_INT32(_block); } + + void set_type(uint8 type) { + type_and_length_accessor t; + t.type_and_length = type_and_length(); + t.bits.type = type; + set_type_and_length(t.type_and_length); + } + void set_length(uint32 length) { + type_and_length_accessor t; + t.type_and_length = type_and_length(); + t.bits.length = length; + set_type_and_length(t.type_and_length); + } + void set_block(uint32 block) { _block = B_HOST_TO_LENDIAN_INT32(block); } +private: + uint32 type_and_length() const { return B_LENDIAN_TO_HOST_INT32(_type_and_length); } + void set_type_and_length(uint32 value) { _type_and_length = B_HOST_TO_LENDIAN_INT32(value); } + + uint32 _type_and_length; + uint32 _block; +} __attribute__((packed)); + + +/*! \brief Allocation descriptor w/ 6 byte implementation use field. + + See also: ECMA 167 4/14.14.2 +*/ +struct long_address { +private: + union type_and_length_accessor { + uint32 type_and_length; + struct { + uint32 length:30, + type:2; + } bits; + }; + +public: + long_address(uint16 partition = 0, uint32 block = 0, uint32 length = 0, + uint8 type = 0); + + void dump() const; + + uint8 type() const { + type_and_length_accessor t; + t.type_and_length = type_and_length(); + return t.bits.type; + } + uint32 length() const { + type_and_length_accessor t; + t.type_and_length = type_and_length(); + return t.bits.length; + } + + uint32 block() const { return _location.block(); } + uint16 partition() const { return _location.partition(); } + + const array& implementation_use() const { return _implementation_use; } + array& implementation_use() { return _implementation_use; } + + uint16 flags() const { return B_LENDIAN_TO_HOST_INT16(_accessor().flags); } + uint32 unique_id() const { return B_LENDIAN_TO_HOST_INT32(_accessor().unique_id); } + + void set_type(uint8 type) { + type_and_length_accessor t; + t.type_and_length = type_and_length(); + t.bits.type = type; + set_type_and_length(t.type_and_length); + } + void set_length(uint32 length) { + type_and_length_accessor t; + t.type_and_length = type_and_length(); + t.bits.length = length; + set_type_and_length(t.type_and_length); + } + void set_block(uint32 block) { _location.set_block(block); } + void set_partition(uint16 partition) { _location.set_partition(partition); } + + void set_flags(uint16 flags) { _accessor().flags = B_HOST_TO_LENDIAN_INT16(flags); } + void set_unique_id(uint32 id) { _accessor().unique_id = B_HOST_TO_LENDIAN_INT32(id); } + + void set_to(uint32 block, uint16 partition, uint32 length = 1, + uint8 type = EXTENT_TYPE_RECORDED, uint16 flags = 0, uint32 unique_id = 0) + { + set_block(block); + set_partition(partition); + set_length(length); + set_type(type); + set_flags(flags); + set_unique_id(unique_id); + } + +private: + //! See UDF-2.50 2.3.4.3 + struct _implementation_use_accessor { + uint16 flags; + uint32 unique_id; + } __attribute__((packed)); + + _implementation_use_accessor& _accessor() { return + *reinterpret_cast<_implementation_use_accessor*>(implementation_use().data); } + const _implementation_use_accessor& _accessor() const { return + *reinterpret_cast(implementation_use().data); } + + uint32 type_and_length() const { return B_LENDIAN_TO_HOST_INT32(_type_and_length); } + void set_type_and_length(uint32 value) { _type_and_length = B_HOST_TO_LENDIAN_INT32(value); } + + uint32 _type_and_length; + logical_block_address _location; + array _implementation_use; +} __attribute__((packed)); + +/*! \brief Common tag found at the beginning of most udf descriptor structures. + + For error checking, \c descriptor_tag structures have: + - The disk location of the tag redundantly stored in the tag itself + - A checksum value for the tag + - A CRC value and length + + See also: ECMA 167 1/7.2, UDF 2.01 2.2.1, UDF 2.01 2.3.1 +*/ +struct descriptor_tag { +public: + void dump() const; + + status_t init_check(uint32 block, bool calculateCrc = true); + + uint16 id() const { return B_LENDIAN_TO_HOST_INT16(_id); } + uint16 version() const { return B_LENDIAN_TO_HOST_INT16(_version); } + uint8 checksum() const { return _checksum; } + uint16 serial_number() const { return B_LENDIAN_TO_HOST_INT16(_serial_number); } + uint16 crc() const { return B_LENDIAN_TO_HOST_INT16(_crc); } + uint16 crc_length() const { return B_LENDIAN_TO_HOST_INT16(_crc_length); } + uint32 location() const { return B_LENDIAN_TO_HOST_INT32(_location); } + + void set_id(uint16 id) { _id = B_HOST_TO_LENDIAN_INT16(id); } + void set_version(uint16 version) { _version = B_HOST_TO_LENDIAN_INT16(version); } + void set_checksum(uint8 checksum) { _checksum = checksum; } + void set_serial_number(uint16 serial_number) { _serial_number = B_HOST_TO_LENDIAN_INT16(serial_number); } + void set_crc(uint16 crc) { _crc = B_HOST_TO_LENDIAN_INT16(crc); } + void set_crc_length(uint16 crc_length) { _crc_length = B_HOST_TO_LENDIAN_INT16(crc_length); } + void set_location(uint32 location) { _location = B_HOST_TO_LENDIAN_INT32(location); } + + /*! \brief Calculates and sets the crc length, crc checksumm, and + checksum for the tag. + + This function should not be called until all member variables in + the descriptor_tag's enclosing descriptor and all member variables + in the descriptor_tag itself other than crc_length, crc, and checksum + have been set (since the checksum is based off of values in the + descriptor_tag, and the crc is based off the values in and the + size of the enclosing descriptor). + + \param The tag's enclosing descriptor. + \param The size of the tag's enclosing descriptor (including the + tag); only necessary if different from sizeof(Descriptor). + */ + template + void + set_checksums(Descriptor &descriptor, uint16 size = sizeof(Descriptor)) + { + + // check that this tag is actually owned by + // the given descriptor + if (this == &descriptor.tag()) + { + // crc_length, based off provided descriptor + set_crc_length(size - sizeof(descriptor_tag)); + // crc + uint16 crc = Udf::calculate_crc(reinterpret_cast(this) + + sizeof(descriptor_tag), crc_length()); + set_crc(crc); + // checksum (which depends on the other two values) + uint32 sum = 0; + for (int i = 0; i <= 3; i++) + sum += reinterpret_cast(this)[i]; + for (int i = 5; i <= 15; i++) + sum += reinterpret_cast(this)[i]; + set_checksum(sum % 256); + } + } +private: + uint16 _id; + uint16 _version; + uint8 _checksum; //!< Sum modulo 256 of bytes 0-3 and 5-15 of this struct. + uint8 _reserved; //!< Set to #00. + uint16 _serial_number; + uint16 _crc; //!< May be 0 if \c crc_length field is 0. + /*! \brief Length of the data chunk used to calculate CRC. + + If 0, no CRC was calculated, and the \c crc field must be 0. + + According to UDF-2.01 2.3.1.2, the CRC shall be calculated for all descriptors + unless otherwise noted, and this field shall be set to: + + (descriptor length) - (descriptor tag length) + */ + uint16 _crc_length; + /*! \brief Address of this tag within its partition (for error checking). + + For virtually addressed structures (i.e. those accessed thru a VAT), this + shall be the virtual address, not the physical or logical address. + */ + uint32 _location; + +} __attribute__((packed)); + + +/*! \c descriptor_tag ::id values +*/ +enum tag_id { + TAGID_UNDEFINED = 0, + + // ECMA 167, PART 3 + TAGID_PRIMARY_VOLUME_DESCRIPTOR, + TAGID_ANCHOR_VOLUME_DESCRIPTOR_POINTER, + TAGID_VOLUME_DESCRIPTOR_POINTER, + TAGID_IMPLEMENTATION_USE_VOLUME_DESCRIPTOR, + TAGID_PARTITION_DESCRIPTOR, + TAGID_LOGICAL_VOLUME_DESCRIPTOR, + TAGID_UNALLOCATED_SPACE_DESCRIPTOR, + TAGID_TERMINATING_DESCRIPTOR, + TAGID_LOGICAL_VOLUME_INTEGRITY_DESCRIPTOR, + + TAGID_CUSTOM_START = 65280, + TAGID_CUSTOM_END = 65535, + + // ECMA 167, PART 4 + TAGID_FILE_SET_DESCRIPTOR = 256, + TAGID_FILE_ID_DESCRIPTOR, + TAGID_ALLOCATION_EXTENT_DESCRIPTOR, + TAGID_INDIRECT_ENTRY, + TAGID_TERMINAL_ENTRY, + TAGID_FILE_ENTRY, + TAGID_EXTENDED_ATTRIBUTE_HEADER_DESCRIPTOR, + TAGID_UNALLOCATED_SPACE_ENTRY, + TAGID_SPACE_BITMAP_DESCRIPTOR, + TAGID_PARTITION_INTEGRITY_ENTRY, + TAGID_EXTENDED_FILE_ENTRY, +}; + +const char *tag_id_to_string(tag_id id); + +extern const uint16 kCrcTable[256]; + +/*! \brief Primary volume descriptor +*/ +struct primary_volume_descriptor { +public: + void dump() const; + + // Get functions + const descriptor_tag & tag() const { return _tag; } + descriptor_tag & tag() { return _tag; } + + uint32 vds_number() const { return B_LENDIAN_TO_HOST_INT32(_vds_number); } + uint32 primary_volume_descriptor_number() const { return B_LENDIAN_TO_HOST_INT32(_primary_volume_descriptor_number); } + + const array& volume_identifier() const { return _volume_identifier; } + array& volume_identifier() { return _volume_identifier; } + + uint16 volume_sequence_number() const { return B_LENDIAN_TO_HOST_INT16(_volume_sequence_number); } + uint16 max_volume_sequence_number() const { return B_LENDIAN_TO_HOST_INT16(_max_volume_sequence_number); } + uint16 interchange_level() const { return B_LENDIAN_TO_HOST_INT16(_interchange_level); } + uint16 max_interchange_level() const { return B_LENDIAN_TO_HOST_INT16(_max_interchange_level); } + uint32 character_set_list() const { return B_LENDIAN_TO_HOST_INT32(_character_set_list); } + uint32 max_character_set_list() const { return B_LENDIAN_TO_HOST_INT32(_max_character_set_list); } + + const array& volume_set_identifier() const { return _volume_set_identifier; } + array& volume_set_identifier() { return _volume_set_identifier; } + + const charspec& descriptor_character_set() const { return _descriptor_character_set; } + charspec& descriptor_character_set() { return _descriptor_character_set; } + + const charspec& explanatory_character_set() const { return _explanatory_character_set; } + charspec& explanatory_character_set() { return _explanatory_character_set; } + + const extent_address& volume_abstract() const { return _volume_abstract; } + extent_address& volume_abstract() { return _volume_abstract; } + const extent_address& volume_copyright_notice() const { return _volume_copyright_notice; } + extent_address& volume_copyright_notice() { return _volume_copyright_notice; } + + const entity_id& application_id() const { return _application_id; } + entity_id& application_id() { return _application_id; } + + const timestamp& recording_date_and_time() const { return _recording_date_and_time; } + timestamp& recording_date_and_time() { return _recording_date_and_time; } + + const entity_id& implementation_id() const { return _implementation_id; } + entity_id& implementation_id() { return _implementation_id; } + + const array& implementation_use() const { return _implementation_use; } + array& implementation_use() { return _implementation_use; } + + uint32 predecessor_volume_descriptor_sequence_location() const + { return B_LENDIAN_TO_HOST_INT32(_predecessor_volume_descriptor_sequence_location); } + uint16 flags() const { return B_LENDIAN_TO_HOST_INT16(_flags); } + + const array& reserved() const { return _reserved; } + array& reserved() { return _reserved; } + + // Set functions + void set_vds_number(uint32 number) + { _vds_number = B_HOST_TO_LENDIAN_INT32(number); } + void set_primary_volume_descriptor_number(uint32 number) + { _primary_volume_descriptor_number = B_HOST_TO_LENDIAN_INT32(number); } + void set_volume_sequence_number(uint16 number) + { _volume_sequence_number = B_HOST_TO_LENDIAN_INT16(number); } + void set_max_volume_sequence_number(uint16 number) + { _max_volume_sequence_number = B_HOST_TO_LENDIAN_INT16(number); } + void set_interchange_level(uint16 level) + { _interchange_level = B_HOST_TO_LENDIAN_INT16(level); } + void set_max_interchange_level(uint16 level) + { _max_interchange_level = B_HOST_TO_LENDIAN_INT16(level); } + void set_character_set_list(uint32 list) + { _character_set_list = B_HOST_TO_LENDIAN_INT32(list); } + void set_max_character_set_list(uint32 list) + { _max_character_set_list = B_HOST_TO_LENDIAN_INT32(list); } + void set_predecessor_volume_descriptor_sequence_location(uint32 location) + { _predecessor_volume_descriptor_sequence_location = B_HOST_TO_LENDIAN_INT32(location); } + void set_flags(uint16 flags) + { _flags = B_HOST_TO_LENDIAN_INT16(flags); } + +private: + descriptor_tag _tag; + uint32 _vds_number; + uint32 _primary_volume_descriptor_number; + array _volume_identifier; + uint16 _volume_sequence_number; + uint16 _max_volume_sequence_number; + uint16 _interchange_level; //!< to be set to 3 if part of multivolume set, 2 otherwise + uint16 _max_interchange_level; //!< to be set to 3 unless otherwise directed by user + uint32 _character_set_list; + uint32 _max_character_set_list; + array _volume_set_identifier; + + /*! \brief Identifies the character set for the \c volume_identifier + and \c volume_set_identifier fields. + + To be set to CS0. + */ + charspec _descriptor_character_set; + + /*! \brief Identifies the character set used in the \c volume_abstract + and \c volume_copyright_notice extents. + + To be set to CS0. + */ + charspec _explanatory_character_set; + + extent_address _volume_abstract; + extent_address _volume_copyright_notice; + + entity_id _application_id; + timestamp _recording_date_and_time; + entity_id _implementation_id; + array _implementation_use; + uint32 _predecessor_volume_descriptor_sequence_location; + uint16 _flags; + array _reserved; + +} __attribute__((packed)); + + +/*! \brief Anchor Volume Descriptor Pointer + + vd recorded at preset locations in the partition, used as a reference + point to the main vd sequences + + According to UDF 2.01, an avdp shall be recorded in at least 2 of + the 3 following locations, where N is the last recordable sector + of the partition: + - 256 + - (N - 256) + - N + + See also: ECMA 167 3/10.2, UDF-2.01 2.2.3 +*/ +struct anchor_volume_descriptor { +public: + anchor_volume_descriptor() { memset(_reserved.data, 0, _reserved.size()); } + void dump() const; + + descriptor_tag & tag() { return _tag; } + const descriptor_tag & tag() const { return _tag; } + + extent_address& main_vds() { return _main_vds; } + const extent_address& main_vds() const { return _main_vds; } + + extent_address& reserve_vds() { return _reserve_vds; } + const extent_address& reserve_vds() const { return _reserve_vds; } +private: + descriptor_tag _tag; + extent_address _main_vds; //!< min length of 16 sectors + extent_address _reserve_vds; //!< min length of 16 sectors + array _reserved; +} __attribute__((packed)); + + +/*! \brief Volume Descriptor Pointer + + Used to chain extents of volume descriptor sequences together. + + See also: ECMA 167 3/10.3 +*/ +struct descriptor_pointer { + descriptor_tag tag; + uint32 vds_number; + extent_address next; +} __attribute__((packed)); + + +/*! \brief UDF Implementation Use Volume Descriptor struct found in + implementation_use() field of implementation_use_descriptor when + said descriptor's implementation_id() field specifies "*UDF LV Info" + + See also: UDF 2.50 2.2.7 +*/ +struct logical_volume_info { +public: + void dump() const; + + charspec& character_set() { return _character_set; } + const charspec& character_set() const { return _character_set; } + + array& logical_volume_id() { return _logical_volume_id; } + const array& logical_volume_id() const { return _logical_volume_id; } + + array& logical_volume_info_1() { return _logical_volume_info.data[0]; } + const array& logical_volume_info_1() const { return _logical_volume_info.data[0]; } + + array& logical_volume_info_2() { return _logical_volume_info.data[1]; } + const array& logical_volume_info_2() const { return _logical_volume_info.data[1]; } + + array& logical_volume_info_3() { return _logical_volume_info.data[2]; } + const array& logical_volume_info_3() const { return _logical_volume_info.data[2]; } + + entity_id& implementation_id() { return _implementation_id; } + const entity_id& implementation_id() const { return _implementation_id; } + + array& implementation_use() { return _implementation_use; } + const array& implementation_use() const { return _implementation_use; } +private: + charspec _character_set; + array _logical_volume_id; // d-string + array, 3> _logical_volume_info; // d-strings + entity_id _implementation_id; + array _implementation_use; +} __attribute__((packed)); + +/*! \brief Implementation Use Volume Descriptor + + See also: ECMA 167 3/10.4 +*/ +struct implementation_use_descriptor { +public: + void dump() const; + + // Get functions + const descriptor_tag & tag() const { return _tag; } + descriptor_tag & tag() { return _tag; } + + uint32 vds_number() const { return B_LENDIAN_TO_HOST_INT32(_vds_number); } + + const entity_id& implementation_id() const { return _implementation_id; } + entity_id& implementation_id() { return _implementation_id; } + + const array& implementation_use() const { return _implementation_use; } + array& implementation_use() { return _implementation_use; } + + // Only valid if implementation_id() returns Udf::kLogicalVolumeInfoId. + logical_volume_info& info() { return *reinterpret_cast(_implementation_use.data); } + const logical_volume_info& info() const { return *reinterpret_cast(_implementation_use.data); } + + // Set functions + void set_vds_number(uint32 number) { _vds_number = B_HOST_TO_LENDIAN_INT32(number); } +private: + descriptor_tag _tag; + uint32 _vds_number; + entity_id _implementation_id; + array _implementation_use; +} __attribute__((packed)); + + +/*! \brief Maximum number of partition descriptors to be found in volume + descriptor sequence, per UDF-2.50 +*/ +extern const uint8 kMaxPartitionDescriptors; +#define UDF_MAX_PARTITION_MAPS 2 +#define UDF_MAX_PARTITION_MAP_SIZE 64 + +/*! \brief Partition Descriptor + + See also: ECMA 167 3/10.5 +*/ +struct partition_descriptor { +private: + union partition_flags_accessor { + uint16 partition_flags; + struct { + uint16 allocated:1, + reserved:15; + } bits; + }; + +public: + void dump() const; + + // Get functions + const descriptor_tag & tag() const { return _tag; } + descriptor_tag & tag() { return _tag; } + + uint32 vds_number() const { return B_LENDIAN_TO_HOST_INT32(_vds_number); } + uint16 partition_flags() const { return B_LENDIAN_TO_HOST_INT16(_partition_flags); } + bool allocated() const { + partition_flags_accessor f; + f.partition_flags = partition_flags(); + return f.bits.allocated; + } + uint16 partition_number() const { return B_LENDIAN_TO_HOST_INT16(_partition_number); } + + const entity_id& partition_contents() const { return _partition_contents; } + entity_id& partition_contents() { return _partition_contents; } + + const array& partition_contents_use() const { return _partition_contents_use; } + array& partition_contents_use() { return _partition_contents_use; } + + uint32 access_type() const { return B_LENDIAN_TO_HOST_INT32(_access_type); } + uint32 start() const { return B_LENDIAN_TO_HOST_INT32(_start); } + uint32 length() const { return B_LENDIAN_TO_HOST_INT32(_length); } + + const entity_id& implementation_id() const { return _implementation_id; } + entity_id& implementation_id() { return _implementation_id; } + + const array& implementation_use() const { return _implementation_use; } + array& implementation_use() { return _implementation_use; } + + const array& reserved() const { return _reserved; } + array& reserved() { return _reserved; } + + // Set functions + void set_vds_number(uint32 number) { _vds_number = B_HOST_TO_LENDIAN_INT32(number); } + void set_partition_flags(uint16 flags) { _partition_flags = B_HOST_TO_LENDIAN_INT16(flags); } + void set_allocated(bool allocated) { + partition_flags_accessor f; + f.partition_flags = partition_flags(); + f.bits.allocated = allocated; + set_partition_flags(f.partition_flags); + } + void set_partition_number(uint16 number) { _partition_number = B_HOST_TO_LENDIAN_INT16(number); } + void set_access_type(uint32 type) { _access_type = B_HOST_TO_LENDIAN_INT32(type); } + void set_start(uint32 start) { _start = B_HOST_TO_LENDIAN_INT32(start); } + void set_length(uint32 length) { _length = B_HOST_TO_LENDIAN_INT32(length); } + +private: + descriptor_tag _tag; + uint32 _vds_number; + /*! Bit 0: If 0, shall mean volume space has not been allocated. If 1, + shall mean volume space has been allocated. + */ + uint16 _partition_flags; + uint16 _partition_number; + + /*! - "+NSR03" Volume recorded according to ECMA-167, i.e. UDF + - "+CD001" Volume recorded according to ECMA-119, i.e. iso9660 + - "+FDC01" Volume recorded according to ECMA-107 + - "+CDW02" Volume recorded according to ECMA-168 + */ + entity_id _partition_contents; + array _partition_contents_use; + + /*! See \c partition_access_type enum + */ + uint32 _access_type; + uint32 _start; + uint32 _length; + entity_id _implementation_id; + array _implementation_use; + array _reserved; +} __attribute__((packed)); + + +enum partition_access_type { + ACCESS_UNSPECIFIED, + ACCESS_READ_ONLY, + ACCESS_WRITE_ONCE, + ACCESS_REWRITABLE, + ACCESS_OVERWRITABLE, +}; + + +/*! \brief Logical volume descriptor + + See also: ECMA 167 3/10.6, UDF-2.01 2.2.4 +*/ +struct logical_volume_descriptor { + void dump() const; + + // Get functions + const descriptor_tag & tag() const { return _tag; } + descriptor_tag & tag() { return _tag; } + + uint32 vds_number() const { return B_LENDIAN_TO_HOST_INT32(_vds_number); } + + const charspec& character_set() const { return _character_set; } + charspec& character_set() { return _character_set; } + + const array& logical_volume_identifier() const { return _logical_volume_identifier; } + array& logical_volume_identifier() { return _logical_volume_identifier; } + + uint32 logical_block_size() const { return B_LENDIAN_TO_HOST_INT32(_logical_block_size); } + + const entity_id& domain_id() const { return _domain_id; } + entity_id& domain_id() { return _domain_id; } + + const array& logical_volume_contents_use() const { return _logical_volume_contents_use; } + array& logical_volume_contents_use() { return _logical_volume_contents_use; } + + const long_address& file_set_address() const { return *reinterpret_cast(&_logical_volume_contents_use); } + long_address& file_set_address() { return *reinterpret_cast(&_logical_volume_contents_use); } + + uint32 map_table_length() const { return B_LENDIAN_TO_HOST_INT32(_map_table_length); } + uint32 partition_map_count() const { return B_LENDIAN_TO_HOST_INT32(_partition_map_count); } + + const entity_id& implementation_id() const { return _implementation_id; } + entity_id& implementation_id() { return _implementation_id; } + + const array& implementation_use() const { return _implementation_use; } + array& implementation_use() { return _implementation_use; } + + const extent_address& integrity_sequence_extent() const { return _integrity_sequence_extent; } + extent_address& integrity_sequence_extent() { return _integrity_sequence_extent; } + + const uint8* partition_maps() const { return _partition_maps; } + uint8* partition_maps() { return _partition_maps; } + + // Set functions + void set_vds_number(uint32 number) { _vds_number = B_HOST_TO_LENDIAN_INT32(number); } + void set_logical_block_size(uint32 size) { _logical_block_size = B_HOST_TO_LENDIAN_INT32(size); } + + void set_map_table_length(uint32 length) { _map_table_length = B_HOST_TO_LENDIAN_INT32(length); } + void set_partition_map_count(uint32 count) { _partition_map_count = B_HOST_TO_LENDIAN_INT32(count); } + + // Other functions + logical_volume_descriptor& operator=(const logical_volume_descriptor &rhs); + +private: + descriptor_tag _tag; + uint32 _vds_number; + + /*! \brief Identifies the character set for the + \c logical_volume_identifier field. + + To be set to CS0. + */ + charspec _character_set; + array _logical_volume_identifier; + uint32 _logical_block_size; + + /*! \brief To be set to 0 or "*OSTA UDF Compliant". See UDF specs. + */ + entity_id _domain_id; + + /*! \brief For UDF, shall contain a \c long_address which identifies + the location of the logical volume's first file set. + */ + array _logical_volume_contents_use; + + uint32 _map_table_length; + uint32 _partition_map_count; + entity_id _implementation_id; + array _implementation_use; + + /*! \brief Logical volume integrity sequence location. + + For re/overwritable media, shall be a min of 8KB in length. + For WORM media, shall be quite frickin large, as a new volume + must be added to the set if the extent fills up (since you + can't chain lvis's I guess). + */ + extent_address _integrity_sequence_extent; + + /*! \brief Restricted to maps of type 1 for normal maps and + UDF type 2 for virtual maps or maps on systems not supporting + defect management. + + Note that we actually allocate memory for the partition maps + here due to the fact that we allocate logical_volume_descriptor + objects on the stack sometimes. + + See UDF-2.01 2.2.8, 2.2.9 + */ + uint8 _partition_maps[UDF_MAX_PARTITION_MAPS * UDF_MAX_PARTITION_MAP_SIZE]; +} __attribute__((packed)); + +//! Base size (excluding partition maps) of lvd +extern const uint32 kLogicalVolumeDescriptorBaseSize; + +/*! \brief (Mostly) common portion of various partition maps + + See also: ECMA-167 3/10.7.1 +*/ +struct partition_map_header { +public: + uint8 type() const { return _type; } + uint8 length() const { return _length; } + uint8 *map_data() { return _map_data; } + const uint8 *map_data() const { return _map_data; } + + entity_id& partition_type_id() + { return *reinterpret_cast(&_map_data[2]); } + const entity_id& partition_type_id() const + { return *reinterpret_cast(&_map_data[2]); } + + void set_type(uint8 type) { _type = type; } + void set_length(uint8 length) { _length = length; } +private: + uint8 _type; + uint8 _length; + uint8 _map_data[0]; +};// __attribute__((packed)); + + +/*! \brief Physical partition map (i.e. ECMA-167 Type 1 partition map) + + See also: ECMA-167 3/10.7.2 +*/ +struct physical_partition_map { +public: + void dump(); + + uint8 type() const { return _type; } + uint8 length() const { return _length; } + + uint16 volume_sequence_number() const { + return B_LENDIAN_TO_HOST_INT16(_volume_sequence_number); } + uint16 partition_number() const { + return B_LENDIAN_TO_HOST_INT16(_partition_number); } + + void set_type(uint8 type) { _type = type; } + void set_length(uint8 length) { _length = length; } + void set_volume_sequence_number(uint16 number) { + _volume_sequence_number = B_HOST_TO_LENDIAN_INT16(number); } + void set_partition_number(uint16 number) { + _partition_number = B_HOST_TO_LENDIAN_INT16(number); } +private: + uint8 _type; + uint8 _length; + uint16 _volume_sequence_number; + uint16 _partition_number; +} __attribute__((packed)); + + +/* ----UDF Specific---- */ +/*! \brief Virtual partition map + + Note that this map is a customization of the ECMA-167 + type 2 partition map. + + See also: UDF-2.01 2.2.8 +*/ +struct virtual_partition_map { + uint8 type; + uint8 length; + uint8 reserved1[2]; + + /*! - flags: 0 + - identifier: "*UDF Virtual Partition" + - identifier_suffix: per UDF-2.01 2.1.5.3 + */ + entity_id partition_type_id; + uint16 volume_sequence_number; + + /*! corresponding type 1 partition map in same logical volume + */ + uint16 partition_number; + uint8 reserved2[24]; +} __attribute__((packed)); + + +/*! \brief Maximum number of redundant sparing tables found in + sparable_partition_map structures. +*/ +#define UDF_MAX_SPARING_TABLE_COUNT 4 + +/* ----UDF Specific---- */ +/*! \brief Sparable partition map + + Note that this map is a customization of the ECMA-167 + type 2 partition map. + + See also: UDF-2.01 2.2.9 +*/ +struct sparable_partition_map { +public: + void dump(); + + uint8 type() const { return _type; } + uint8 length() const { return _length; } + + entity_id& partition_type_id() { return _partition_type_id; } + const entity_id& partition_type_id() const { return _partition_type_id; } + + uint16 volume_sequence_number() const { + return B_LENDIAN_TO_HOST_INT16(_volume_sequence_number); } + uint16 partition_number() const { + return B_LENDIAN_TO_HOST_INT16(_partition_number); } + uint16 packet_length() const { + return B_LENDIAN_TO_HOST_INT16(_packet_length); } + uint8 sparing_table_count() const { return _sparing_table_count; } + uint32 sparing_table_size() const { + return B_LENDIAN_TO_HOST_INT32(_sparing_table_size); } + uint32 sparing_table_location(uint8 index) const { + return B_LENDIAN_TO_HOST_INT32(_sparing_table_locations[index]); } + + + void set_type(uint8 type) { _type = type; } + void set_length(uint8 length) { _length = length; } + void set_volume_sequence_number(uint16 number) { + _volume_sequence_number = B_HOST_TO_LENDIAN_INT16(number); } + void set_partition_number(uint16 number) { + _partition_number = B_HOST_TO_LENDIAN_INT16(number); } + void set_packet_length(uint16 length) { + _packet_length = B_HOST_TO_LENDIAN_INT16(length); } + void set_sparing_table_count(uint8 count) { + _sparing_table_count = count; } + void set_sparing_table_size(uint32 size) { + _sparing_table_size = B_HOST_TO_LENDIAN_INT32(size); } + void set_sparing_table_location(uint8 index, uint32 location) { + _sparing_table_locations[index] = B_HOST_TO_LENDIAN_INT32(location); } +private: + uint8 _type; + uint8 _length; + uint8 _reserved1[2]; + + /*! - flags: 0 + - identifier: "*UDF Sparable Partition" + - identifier_suffix: per UDF-2.01 2.1.5.3 + */ + entity_id _partition_type_id; + uint16 _volume_sequence_number; + + //! partition number of corresponding partition descriptor + uint16 _partition_number; + uint16 _packet_length; + uint8 _sparing_table_count; + uint8 _reserved2; + uint32 _sparing_table_size; + uint32 _sparing_table_locations[UDF_MAX_SPARING_TABLE_COUNT]; +} __attribute__((packed)); + + +/* ----UDF Specific---- */ +/*! \brief Metadata partition map + + Note that this map is a customization of the ECMA-167 + type 2 partition map. + + See also: UDF-2.50 2.2.10 +*/ +struct metadata_partition_map { + uint8 type; + uint8 length; + uint8 reserved1[2]; + + /*! - flags: 0 + - identifier: "*UDF Metadata Partition" + - identifier_suffix: per UDF-2.50 2.1.5 + */ + entity_id partition_type_id; + uint16 volume_sequence_number; + + /*! corresponding type 1 or type 2 sparable partition + map in same logical volume + */ + uint16 partition_number; + uint8 reserved2[24]; +} __attribute__((packed)); + + +/*! \brief Unallocated space descriptor + + See also: ECMA-167 3/10.8 +*/ +struct unallocated_space_descriptor { + void dump() const; + + // Get functions + const descriptor_tag & tag() const { return _tag; } + descriptor_tag & tag() { return _tag; } + uint32 vds_number() const { return B_LENDIAN_TO_HOST_INT32(_vds_number); } + uint32 allocation_descriptor_count() const { return B_LENDIAN_TO_HOST_INT32(_allocation_descriptor_count); } + extent_address* allocation_descriptors() { return _allocation_descriptors; } + + // Set functions + void set_vds_number(uint32 number) { _vds_number = B_HOST_TO_LENDIAN_INT32(number); } + void set_allocation_descriptor_count(uint32 count) { _allocation_descriptor_count = B_HOST_TO_LENDIAN_INT32(count); } +private: + descriptor_tag _tag; + uint32 _vds_number; + uint32 _allocation_descriptor_count; + extent_address _allocation_descriptors[0]; +} __attribute__((packed)); + + +/*! \brief Terminating descriptor + + See also: ECMA-167 3/10.9 +*/ +struct terminating_descriptor { + terminating_descriptor() { memset(_reserved.data, 0, _reserved.size()); } + void dump() const; + + // Get functions + const descriptor_tag & tag() const { return _tag; } + descriptor_tag & tag() { return _tag; } +private: + descriptor_tag _tag; + array _reserved; +} __attribute__((packed)); + + +/*! \brief Logical volume integrity descriptor + + See also: ECMA-167 3/10.10, UDF-2.50 2.2.6 +*/ +struct logical_volume_integrity_descriptor { +public: + static const uint32 minimum_implementation_use_length = 46; + + void dump() const; + uint32 descriptor_size() const { return sizeof(*this)+implementation_use_length() + + partition_count()*sizeof(uint32)*2; } + + descriptor_tag& tag() { return _tag; } + const descriptor_tag& tag() const { return _tag; } + + timestamp& recording_time() { return _recording_time; } + const timestamp& recording_time() const { return _recording_time; } + + uint32 integrity_type() const { return B_LENDIAN_TO_HOST_INT32(_integrity_type); } + + extent_address& next_integrity_extent() { return _next_integrity_extent; } + const extent_address& next_integrity_extent() const { return _next_integrity_extent; } + + array& logical_volume_contents_use() { return _logical_volume_contents_use; } + const array& logical_volume_contents_use() const { return _logical_volume_contents_use; } + + // next_unique_id() field is actually stored in the logical_volume_contents_use() + // field, per UDF-2.50 3.2.1 + uint64 next_unique_id() const { return B_LENDIAN_TO_HOST_INT64(_next_unique_id()); } + + uint32 partition_count() const { return B_LENDIAN_TO_HOST_INT32(_partition_count); } + uint32 implementation_use_length() const { return B_LENDIAN_TO_HOST_INT32(_implementation_use_length); } + + /*! \todo double-check the pointer arithmetic here. */ + uint32* free_space_table() { return reinterpret_cast(reinterpret_cast(this)+80); } + const uint32* free_space_table() const { return reinterpret_cast(reinterpret_cast(this)+80); } + uint32* size_table() { return reinterpret_cast(reinterpret_cast(free_space_table())+partition_count()*sizeof(uint32)); } + const uint32* size_table() const { return reinterpret_cast(reinterpret_cast(free_space_table())+partition_count()*sizeof(uint32)); } + uint8* implementation_use() { return reinterpret_cast(reinterpret_cast(size_table())+partition_count()*sizeof(uint32)); } + const uint8* implementation_use() const { return reinterpret_cast(reinterpret_cast(size_table())+partition_count()*sizeof(uint32)); } + + // accessors for fields stored in implementation_use() field per UDF-2.50 2.2.6.4 + entity_id& implementation_id() { return _accessor().id; } + const entity_id& implementation_id() const { return _accessor().id; } + uint32 file_count() const { return B_LENDIAN_TO_HOST_INT32(_accessor().file_count); } + uint32 directory_count() const { return B_LENDIAN_TO_HOST_INT32(_accessor().directory_count); } + uint16 minimum_udf_read_revision() const { return B_LENDIAN_TO_HOST_INT16(_accessor().minimum_udf_read_revision); } + uint16 minimum_udf_write_revision() const { return B_LENDIAN_TO_HOST_INT16(_accessor().minimum_udf_write_revision); } + uint16 maximum_udf_write_revision() const { return B_LENDIAN_TO_HOST_INT16(_accessor().maximum_udf_write_revision); } + + // set functions + void set_integrity_type(uint32 type) { _integrity_type = B_HOST_TO_LENDIAN_INT32(type); } + void set_next_unique_id(uint64 id) { _next_unique_id() = B_HOST_TO_LENDIAN_INT64(id); } + void set_partition_count(uint32 count) { _partition_count = B_HOST_TO_LENDIAN_INT32(count); } + void set_implementation_use_length(uint32 length) { _implementation_use_length = B_HOST_TO_LENDIAN_INT32(length); } + + // set functions for fields stored in implementation_use() field per UDF-2.50 2.2.6.4 + void set_file_count(uint32 count) { _accessor().file_count = B_HOST_TO_LENDIAN_INT32(count); } + void set_directory_count(uint32 count) { _accessor().directory_count = B_HOST_TO_LENDIAN_INT32(count); } + void set_minimum_udf_read_revision(uint16 revision) { _accessor().minimum_udf_read_revision = B_HOST_TO_LENDIAN_INT16(revision); } + void set_minimum_udf_write_revision(uint16 revision) { _accessor().minimum_udf_write_revision = B_HOST_TO_LENDIAN_INT16(revision); } + void set_maximum_udf_write_revision(uint16 revision) { _accessor().maximum_udf_write_revision = B_HOST_TO_LENDIAN_INT16(revision); } + +private: + struct _lvid_implementation_use_accessor { + entity_id id; + uint32 file_count; + uint32 directory_count; + uint16 minimum_udf_read_revision; + uint16 minimum_udf_write_revision; + uint16 maximum_udf_write_revision; + }; + + _lvid_implementation_use_accessor& _accessor() { + return *reinterpret_cast<_lvid_implementation_use_accessor*>(implementation_use()); + } + const _lvid_implementation_use_accessor& _accessor() const { + return *reinterpret_cast(implementation_use()); + } + + uint64& _next_unique_id() { return *reinterpret_cast(logical_volume_contents_use().data); } + const uint64& _next_unique_id() const { return *reinterpret_cast(logical_volume_contents_use().data); } + + descriptor_tag _tag; + timestamp _recording_time; + uint32 _integrity_type; + extent_address _next_integrity_extent; + array _logical_volume_contents_use; + uint32 _partition_count; + uint32 _implementation_use_length; + +} __attribute__((packed)); + +/*! \brief Logical volume integrity types +*/ +enum { + INTEGRITY_OPEN = 0, + INTEGRITY_CLOSED = 1, +}; + +/*! \brief Highest currently supported UDF read revision. +*/ +#define UDF_MAX_READ_REVISION 0x0201 + +//---------------------------------------------------------------------- +// ECMA-167 Part 4 +//---------------------------------------------------------------------- + + + +/*! \brief File set descriptor + + Contains all the pertinent info about a file set (i.e. a hierarchy of files) + + According to UDF-2.01, only one file set descriptor shall be recorded, + except on WORM media, where the following rules apply: + - Multiple file sets are allowed only on WORM media + - The default file set shall be the one with highest value \c file_set_number field. + - Only the default file set may be flagged as writeable. All others shall be + flagged as "hard write protect". + - No writeable file set may reference metadata structures which are referenced + (directly or indirectly) by any other file set. Writeable file sets may, however, + reference actual file data extents that are also referenced by other file sets. +*/ +struct file_set_descriptor { + void dump() const; + + // Get functions + const descriptor_tag & tag() const { return _tag; } + descriptor_tag & tag() { return _tag; } + + const timestamp& recording_date_and_time() const { return _recording_date_and_time; } + timestamp& recording_date_and_time() { return _recording_date_and_time; } + + uint16 interchange_level() const { return B_LENDIAN_TO_HOST_INT16(_interchange_level); } + uint16 max_interchange_level() const { return B_LENDIAN_TO_HOST_INT16(_max_interchange_level); } + uint32 character_set_list() const { return B_LENDIAN_TO_HOST_INT32(_character_set_list); } + uint32 max_character_set_list() const { return B_LENDIAN_TO_HOST_INT32(_max_character_set_list); } + uint32 file_set_number() const { return B_LENDIAN_TO_HOST_INT32(_file_set_number); } + uint32 file_set_descriptor_number() const { return B_LENDIAN_TO_HOST_INT32(_file_set_descriptor_number); } + + const charspec& logical_volume_id_character_set() const { return _logical_volume_id_character_set; } + charspec& logical_volume_id_character_set() { return _logical_volume_id_character_set; } + + const array& logical_volume_id() const { return _logical_volume_id; } + array& logical_volume_id() { return _logical_volume_id; } + + const charspec& file_set_id_character_set() const { return _file_set_id_character_set; } + charspec& file_set_id_character_set() { return _file_set_id_character_set; } + + const array& file_set_id() const { return _file_set_id; } + array& file_set_id() { return _file_set_id; } + + const array& copyright_file_id() const { return _copyright_file_id; } + array& copyright_file_id() { return _copyright_file_id; } + + const array& abstract_file_id() const { return _abstract_file_id; } + array& abstract_file_id() { return _abstract_file_id; } + + const long_address& root_directory_icb() const { return _root_directory_icb; } + long_address& root_directory_icb() { return _root_directory_icb; } + + const entity_id& domain_id() const { return _domain_id; } + entity_id& domain_id() { return _domain_id; } + + const long_address& next_extent() const { return _next_extent; } + long_address& next_extent() { return _next_extent; } + + const long_address& system_stream_directory_icb() const { return _system_stream_directory_icb; } + long_address& system_stream_directory_icb() { return _system_stream_directory_icb; } + + const array& reserved() const { return _reserved; } + array& reserved() { return _reserved; } + + // Set functions + void set_interchange_level(uint16 level) { _interchange_level = B_HOST_TO_LENDIAN_INT16(level); } + void set_max_interchange_level(uint16 level) { _max_interchange_level = B_HOST_TO_LENDIAN_INT16(level); } + void set_character_set_list(uint32 list) { _character_set_list = B_HOST_TO_LENDIAN_INT32(list); } + void set_max_character_set_list(uint32 list) { _max_character_set_list = B_HOST_TO_LENDIAN_INT32(list); } + void set_file_set_number(uint32 number) { _file_set_number = B_HOST_TO_LENDIAN_INT32(number); } + void set_file_set_descriptor_number(uint32 number) { _file_set_descriptor_number = B_HOST_TO_LENDIAN_INT32(number); } +private: + descriptor_tag _tag; + timestamp _recording_date_and_time; + uint16 _interchange_level; //!< To be set to 3 (see UDF-2.01 2.3.2.1) + uint16 _max_interchange_level; //!< To be set to 3 (see UDF-2.01 2.3.2.2) + uint32 _character_set_list; + uint32 _max_character_set_list; + uint32 _file_set_number; + uint32 _file_set_descriptor_number; + charspec _logical_volume_id_character_set; //!< To be set to kCSOCharspec + array _logical_volume_id; + charspec _file_set_id_character_set; + array _file_set_id; + array _copyright_file_id; + array _abstract_file_id; + long_address _root_directory_icb; + entity_id _domain_id; + long_address _next_extent; + long_address _system_stream_directory_icb; + array _reserved; +} __attribute__((packed)); + + +/*! \brief Partition header descriptor + + Contains references to unallocated and freed space data structures. + + Note that unallocated space is space ready to be written with no + preprocessing. Freed space is space needing preprocessing (i.e. + a special write pass) before use. + + Per UDF-2.01 2.3.3, the use of tables or bitmaps shall be consistent, + i.e. only one type or the other shall be used, not both. + + To indicate disuse of a certain field, the fields of the allocation + descriptor shall all be set to 0. + + See also: ECMA-167 4/14.3, UDF-2.01 2.2.3 +*/ +struct partition_header_descriptor { + long_address unallocated_space_table; + long_address unallocated_space_bitmap; + /*! Unused, per UDF-2.01 2.2.3 */ + long_address partition_integrity_table; + long_address freed_space_table; + long_address freed_space_bitmap; + uint8 reserved[88]; +} __attribute__((packed)); + +#define kMaxFileIdSize (sizeof(file_id_descriptor)+512+3) + +/*! \brief File identifier descriptor + + Identifies the name of a file entry, and the location of its corresponding + ICB. + + See also: ECMA-167 4/14.4, UDF-2.01 2.3.4 + + \todo Check pointer arithmetic +*/ +struct file_id_descriptor { +public: + uint32 descriptor_size() const { return total_length(); } + void dump() const; + + descriptor_tag & tag() { return _tag; } + const descriptor_tag & tag() const { return _tag; } + + uint16 version_number() const { return B_LENDIAN_TO_HOST_INT16(_version_number); } + + uint8 characteristics() const { return _characteristics; } + + bool may_be_hidden() const { + characteristics_accessor c; + c.all = characteristics(); + return c.bits.may_be_hidden; + } + + bool is_directory() const { + characteristics_accessor c; + c.all = characteristics(); + return c.bits.is_directory; + } + + bool is_deleted() const { + characteristics_accessor c; + c.all = characteristics(); + return c.bits.is_deleted; + } + + bool is_parent() const { + characteristics_accessor c; + c.all = characteristics(); + return c.bits.is_parent; + } + + bool is_metadata_stream() const { + characteristics_accessor c; + c.all = characteristics(); + return c.bits.is_metadata_stream; + } + + uint8 id_length() const { return _id_length; } + + long_address& icb() { return _icb; } + const long_address& icb() const { return _icb; } + + uint16 implementation_use_length() const { return B_LENDIAN_TO_HOST_INT16(_implementation_use_length); } + + /*! If implementation_use_length is greater than 0, the first 32 + bytes of implementation_use() shall be an entity_id identifying + the implementation that generated the rest of the data in the + implementation_use() field. + */ + uint8* implementation_use() { return ((uint8*)this)+(38); } + char* id() { return ((char*)this)+(38)+implementation_use_length(); } + const char* id() const { return ((const char*)this)+(38)+implementation_use_length(); } + + uint16 structure_length() const { return (38) + id_length() + implementation_use_length(); } + uint16 padding_length() const { return ((structure_length()+3)/4)*4 - structure_length(); } + uint16 total_length() const { return structure_length() + padding_length(); } + + // Set functions + void set_version_number(uint16 number) { _version_number = B_HOST_TO_LENDIAN_INT16(number); } + + void set_characteristics(uint8 characteristics) { _characteristics = characteristics; } + + void set_may_be_hidden(bool how) { + characteristics_accessor c; + c.all = characteristics(); + c.bits.may_be_hidden = how; + set_characteristics(c.all); + } + + void set_is_directory(bool how) { + characteristics_accessor c; + c.all = characteristics(); + c.bits.is_directory = how; + set_characteristics(c.all); + } + + void set_is_deleted(bool how) { + characteristics_accessor c; + c.all = characteristics(); + c.bits.is_deleted = how; + set_characteristics(c.all); + } + + void set_is_parent(bool how) { + characteristics_accessor c; + c.all = characteristics(); + c.bits.is_parent = how; + set_characteristics(c.all); + } + + void set_is_metadata_stream(bool how) { + characteristics_accessor c; + c.all = characteristics(); + c.bits.is_metadata_stream = how; + set_characteristics(c.all); + } + + + void set_id_length(uint8 id_length) { _id_length = id_length; } + void set_implementation_use_length(uint16 implementation_use_length) { _implementation_use_length = B_HOST_TO_LENDIAN_INT16(implementation_use_length); } + + + +private: + union characteristics_accessor { + uint8 all; + struct { + uint8 may_be_hidden:1, + is_directory:1, + is_deleted:1, + is_parent:1, + is_metadata_stream:1, + reserved_characteristics:3; + } bits; + }; + + descriptor_tag _tag; + /*! According to ECMA-167: 1 <= valid version_number <= 32767, 32768 <= reserved <= 65535. + + However, according to UDF-2.01, there shall be exactly one version of + a file, and it shall be 1. + */ + uint16 _version_number; + /*! \todo Check UDF-2.01 2.3.4.2 for some more restrictions. */ + uint8 _characteristics; + uint8 _id_length; + long_address _icb; + uint16 _implementation_use_length; +} __attribute__((packed)); + + +/*! \brief Allocation extent descriptor + + See also: ECMA-167 4/14.5 +*/ +struct allocation_extent_descriptor { + descriptor_tag tag; + uint32 previous_allocation_extent_location; + uint32 length_of_allocation_descriptors; + + /*! \todo Check that this is really how things work: */ + uint8* allocation_descriptors() { return (uint8*)(reinterpret_cast(this)+sizeof(allocation_extent_descriptor)); } +} __attribute__((packed)); + + +/*! \brief icb_tag::file_type values + + See also ECMA-167 4/14.6.6 +*/ +enum icb_file_types { + ICB_TYPE_UNSPECIFIED = 0, + ICB_TYPE_UNALLOCATED_SPACE_ENTRY, + ICB_TYPE_PARTITION_INTEGRITY_ENTRY, + ICB_TYPE_INDIRECT_ENTRY, + ICB_TYPE_DIRECTORY, + ICB_TYPE_REGULAR_FILE, + ICB_TYPE_BLOCK_SPECIAL_DEVICE, + ICB_TYPE_CHARACTER_SPECIAL_DEVICE, + ICB_TYPE_EXTENDED_ATTRIBUTES_FILE, + ICB_TYPE_FIFO, + ICB_TYPE_ISSOCK, + ICB_TYPE_TERMINAL, + ICB_TYPE_SYMLINK, + ICB_TYPE_STREAM_DIRECTORY, + + ICB_TYPE_RESERVED_START = 14, + ICB_TYPE_RESERVED_END = 247, + + ICB_TYPE_CUSTOM_START = 248, + ICB_TYPE_CUSTOM_END = 255, +}; + +/*! \brief idb_entry_tag::_flags::descriptor_flags() values + + See also ECMA-167 4/14.6.8 +*/ +enum icb_descriptor_types { + ICB_DESCRIPTOR_TYPE_SHORT = 0, + ICB_DESCRIPTOR_TYPE_LONG, + ICB_DESCRIPTOR_TYPE_EXTENDED, + ICB_DESCRIPTOR_TYPE_EMBEDDED, +}; + +/*! \brief idb_entry_tag::strategy_type() values + + See also UDF-2.50 2.3.5.1 +*/ +enum icb_strategy_types { + ICB_STRATEGY_SINGLE = 4, + ICB_STRATEGY_LINKED_LIST = 4096 +}; + +/*! \brief ICB entry tag + + Common tag found in all ICB entries (in addition to, and immediately following, + the descriptor tag). + + See also: ECMA-167 4/14.6, UDF-2.01 2.3.5 +*/ +struct icb_entry_tag { +public: + union flags_accessor { + uint16 all_flags; + struct { + uint16 descriptor_flags:3, + if_directory_then_sort:1, //!< To be set to 0 per UDF-2.01 2.3.5.4 + non_relocatable:1, + archive:1, + setuid:1, + setgid:1, + sticky:1, + contiguous:1, + system:1, + transformed:1, + multi_version:1, //!< To be set to 0 per UDF-2.01 2.3.5.4 + is_stream:1, + reserved_icb_entry_flags:2; + } flags; + }; + +public: + void dump() const; + + uint32 prior_recorded_number_of_direct_entries() const { return B_LENDIAN_TO_HOST_INT32(_prior_recorded_number_of_direct_entries); } + uint16 strategy_type() const { return B_LENDIAN_TO_HOST_INT16(_strategy_type); } + + array& strategy_parameters() { return _strategy_parameters; } + const array& strategy_parameters() const { return _strategy_parameters; } + + uint16 entry_count() const { return B_LENDIAN_TO_HOST_INT16(_entry_count); } + uint8& reserved() { return _reserved; } + uint8 file_type() const { return _file_type; } + logical_block_address& parent_icb_location() { return _parent_icb_location; } + const logical_block_address& parent_icb_location() const { return _parent_icb_location; } + + uint16 flags() const { return B_LENDIAN_TO_HOST_INT16(_flags); } + flags_accessor& flags_access() { return *reinterpret_cast(&_flags); } + + // flags accessor functions + uint8 descriptor_flags() const { + flags_accessor f; + f.all_flags = flags(); + return f.flags.descriptor_flags; + } +/* void set_descriptor_flags(uint8 value) { + flags_accessor f; + f.all_flags = flags(); + f.flags.descriptor_flags = value; + set_flags +*/ + + void set_prior_recorded_number_of_direct_entries(uint32 entries) { _prior_recorded_number_of_direct_entries = B_LENDIAN_TO_HOST_INT32(entries); } + void set_strategy_type(uint16 type) { _strategy_type = B_HOST_TO_LENDIAN_INT16(type); } + + void set_entry_count(uint16 count) { _entry_count = B_LENDIAN_TO_HOST_INT16(count); } + void set_file_type(uint8 type) { _file_type = type; } + + void set_flags(uint16 flags) { _flags = B_LENDIAN_TO_HOST_INT16(flags); } + +private: + uint32 _prior_recorded_number_of_direct_entries; + /*! Per UDF-2.01 2.3.5.1, only strategy types 4 and 4096 shall be supported. + + \todo Describe strategy types here. + */ + uint16 _strategy_type; + array _strategy_parameters; + uint16 _entry_count; + uint8 _reserved; + /*! \brief icb_file_type value identifying the type of this icb entry */ + uint8 _file_type; + logical_block_address _parent_icb_location; + uint16 _flags; +} __attribute__((packed)); + +/*! \brief Header portion of an ICB entry. +*/ +struct icb_header { +public: + void dump() const; + + descriptor_tag &tag() { return _tag; } + const descriptor_tag &tag() const { return _tag; } + + icb_entry_tag &icb_tag() { return _icb_tag; } + const icb_entry_tag &icb_tag() const { return _icb_tag; } +private: + descriptor_tag _tag; + icb_entry_tag _icb_tag; +}; + +/*! \brief Indirect ICB entry +*/ +struct indirect_icb_entry { + descriptor_tag tag; + icb_entry_tag icb_tag; + long_address indirect_icb; +} __attribute__((packed)); + + +/*! \brief Terminal ICB entry +*/ +struct terminal_icb_entry { + descriptor_tag tag; + icb_entry_tag icb_tag; +} __attribute__((packed)); + +enum permissions { + OTHER_EXECUTE = 0x0001, + OTHER_WRITE = 0x0002, + OTHER_READ = 0x0004, + OTHER_ATTRIBUTES = 0x0008, + OTHER_DELETE = 0x0010, + GROUP_EXECUTE = 0x0020, + GROUP_WRITE = 0x0040, + GROUP_READ = 0x0080, + GROUP_ATTRIBUTES = 0x0100, + GROUP_DELETE = 0x0200, + USER_EXECUTE = 0x0400, + USER_WRITE = 0x0800, + USER_READ = 0x1000, + USER_ATTRIBUTES = 0x2000, + USER_DELETE = 0x4000, +}; + +/*! \brief File ICB entry + + See also: ECMA-167 4/14.9 + + \todo Check pointer math. +*/ +struct file_icb_entry { + void dump() const; + uint32 descriptor_size() const { return sizeof(*this)+extended_attributes_length() + +allocation_descriptors_length(); } + const char* descriptor_name() const { return "file_icb_entry"; } + + // get functions + descriptor_tag & tag() { return _tag; } + const descriptor_tag & tag() const { return _tag; } + + icb_entry_tag& icb_tag() { return _icb_tag; } + const icb_entry_tag& icb_tag() const { return _icb_tag; } + + uint32 uid() const { return B_LENDIAN_TO_HOST_INT32(_uid); } + uint32 gid() const { return B_LENDIAN_TO_HOST_INT32(_gid); } + uint32 permissions() const { return B_LENDIAN_TO_HOST_INT32(_permissions); } + uint16 file_link_count() const { return B_LENDIAN_TO_HOST_INT16(_file_link_count); } + uint8 record_format() const { return _record_format; } + uint8 record_display_attributes() const { return _record_display_attributes; } + uint8 record_length() const { return _record_length; } + uint64 information_length() const { return B_LENDIAN_TO_HOST_INT64(_information_length); } + uint64 logical_blocks_recorded() const { return B_LENDIAN_TO_HOST_INT64(_logical_blocks_recorded); } + + timestamp& access_date_and_time() { return _access_date_and_time; } + const timestamp& access_date_and_time() const { return _access_date_and_time; } + + timestamp& modification_date_and_time() { return _modification_date_and_time; } + const timestamp& modification_date_and_time() const { return _modification_date_and_time; } + + timestamp& attribute_date_and_time() { return _attribute_date_and_time; } + const timestamp& attribute_date_and_time() const { return _attribute_date_and_time; } + + uint32 checkpoint() const { return B_LENDIAN_TO_HOST_INT32(_checkpoint); } + + long_address& extended_attribute_icb() { return _extended_attribute_icb; } + const long_address& extended_attribute_icb() const { return _extended_attribute_icb; } + + entity_id& implementation_id() { return _implementation_id; } + const entity_id& implementation_id() const { return _implementation_id; } + + uint64 unique_id() const { return B_LENDIAN_TO_HOST_INT64(_unique_id); } + uint32 extended_attributes_length() const { return B_LENDIAN_TO_HOST_INT32(_extended_attributes_length); } + uint32 allocation_descriptors_length() const { return B_LENDIAN_TO_HOST_INT32(_allocation_descriptors_length); } + + uint8* extended_attributes() { return _end(); } + const uint8* extended_attributes() const { return _end(); } + uint8* allocation_descriptors() { return _end()+extended_attributes_length(); } + const uint8* allocation_descriptors() const { return _end()+extended_attributes_length(); } + + // set functions + void set_uid(uint32 uid) { _uid = B_HOST_TO_LENDIAN_INT32(uid); } + void set_gid(uint32 gid) { _gid = B_HOST_TO_LENDIAN_INT32(gid); } + void set_permissions(uint32 permissions) { _permissions = B_HOST_TO_LENDIAN_INT32(permissions); } + + void set_file_link_count(uint16 count) { _file_link_count = B_HOST_TO_LENDIAN_INT16(count); } + void set_record_format(uint8 format) { _record_format = format; } + void set_record_display_attributes(uint8 attributes) { _record_display_attributes = attributes; } + void set_record_length(uint8 length) { _record_length = length; } + + void set_information_length(uint64 length) { _information_length = B_HOST_TO_LENDIAN_INT64(length); } + void set_logical_blocks_recorded(uint64 blocks) { _logical_blocks_recorded = B_HOST_TO_LENDIAN_INT64(blocks); } + + void set_checkpoint(uint32 checkpoint) { _checkpoint = B_HOST_TO_LENDIAN_INT32(checkpoint); } + + void set_unique_id(uint64 id) { _unique_id = B_HOST_TO_LENDIAN_INT64(id); } + + void set_extended_attributes_length(uint32 length) { _extended_attributes_length = B_HOST_TO_LENDIAN_INT32(length); } + void set_allocation_descriptors_length(uint32 length) { _allocation_descriptors_length = B_HOST_TO_LENDIAN_INT32(length); } + + // extended_file_icb_entry compatability functions + timestamp& creation_date_and_time() { return _attribute_date_and_time; } + const timestamp& creation_date_and_time() const { return _attribute_date_and_time; } + + + void set_object_size(uint64 size) { } + void set_reserved(uint32 reserved) { } + long_address& stream_directory_icb() { return _dummy_stream_directory_icb; } + const long_address& stream_directory_icb() const { return _dummy_stream_directory_icb; } + + +private: + static const uint32 _descriptor_length = 176; + static long_address _dummy_stream_directory_icb; + uint8* _end() { return reinterpret_cast(this)+_descriptor_length; } + const uint8* _end() const { return reinterpret_cast(this)+_descriptor_length; } + + descriptor_tag _tag; + icb_entry_tag _icb_tag; + uint32 _uid; + uint32 _gid; + /*! \todo List perms in comment and add handy union thingy */ + uint32 _permissions; + /*! Identifies the number of file identifier descriptors referencing + this icb. + */ + uint16 _file_link_count; + uint8 _record_format; //!< To be set to 0 per UDF-2.01 2.3.6.1 + uint8 _record_display_attributes; //!< To be set to 0 per UDF-2.01 2.3.6.2 + uint8 _record_length; //!< To be set to 0 per UDF-2.01 2.3.6.3 + uint64 _information_length; + uint64 _logical_blocks_recorded; //!< To be 0 for files and dirs with embedded data + timestamp _access_date_and_time; + timestamp _modification_date_and_time; + + // NOTE: data members following this point in the descriptor are in + // different locations in extended file entries + + timestamp _attribute_date_and_time; + /*! \brief Initially 1, may be incremented upon user request. */ + uint32 _checkpoint; + long_address _extended_attribute_icb; + entity_id _implementation_id; + /*! \brief The unique id identifying this file entry + + The id of the root directory of a file set shall be 0. + + \todo Detail the system specific requirements for unique ids from UDF-2.01 + */ + uint64 _unique_id; + uint32 _extended_attributes_length; + uint32 _allocation_descriptors_length; + +}; + + +/*! \brief Extended file ICB entry + + See also: ECMA-167 4/14.17 + + \todo Check pointer math. +*/ +struct extended_file_icb_entry { + void dump() const; + uint32 descriptor_size() const { return sizeof(*this)+extended_attributes_length() + +allocation_descriptors_length(); } + const char* descriptor_name() const { return "extended_file_icb_entry"; } + + // get functions + descriptor_tag & tag() { return _tag; } + const descriptor_tag & tag() const { return _tag; } + + icb_entry_tag& icb_tag() { return _icb_tag; } + const icb_entry_tag& icb_tag() const { return _icb_tag; } + + uint32 uid() const { return B_LENDIAN_TO_HOST_INT32(_uid); } + uint32 gid() const { return B_LENDIAN_TO_HOST_INT32(_gid); } + uint32 permissions() const { return B_LENDIAN_TO_HOST_INT32(_permissions); } + uint16 file_link_count() const { return B_LENDIAN_TO_HOST_INT16(_file_link_count); } + uint8 record_format() const { return _record_format; } + uint8 record_display_attributes() const { return _record_display_attributes; } + uint32 record_length() const { return _record_length; } + uint64 information_length() const { return B_LENDIAN_TO_HOST_INT64(_information_length); } + uint64 object_size() const { return B_LENDIAN_TO_HOST_INT64(_object_size); } + uint64 logical_blocks_recorded() const { return B_LENDIAN_TO_HOST_INT64(_logical_blocks_recorded); } + + timestamp& access_date_and_time() { return _access_date_and_time; } + const timestamp& access_date_and_time() const { return _access_date_and_time; } + + timestamp& modification_date_and_time() { return _modification_date_and_time; } + const timestamp& modification_date_and_time() const { return _modification_date_and_time; } + + timestamp& creation_date_and_time() { return _creation_date_and_time; } + const timestamp& creation_date_and_time() const { return _creation_date_and_time; } + + timestamp& attribute_date_and_time() { return _attribute_date_and_time; } + const timestamp& attribute_date_and_time() const { return _attribute_date_and_time; } + + uint32 checkpoint() const { return B_LENDIAN_TO_HOST_INT32(_checkpoint); } + + long_address& extended_attribute_icb() { return _extended_attribute_icb; } + const long_address& extended_attribute_icb() const { return _extended_attribute_icb; } + + long_address& stream_directory_icb() { return _stream_directory_icb; } + const long_address& stream_directory_icb() const { return _stream_directory_icb; } + + entity_id& implementation_id() { return _implementation_id; } + const entity_id& implementation_id() const { return _implementation_id; } + + uint64 unique_id() const { return B_LENDIAN_TO_HOST_INT64(_unique_id); } + uint32 extended_attributes_length() const { return B_LENDIAN_TO_HOST_INT32(_extended_attributes_length); } + uint32 allocation_descriptors_length() const { return B_LENDIAN_TO_HOST_INT32(_allocation_descriptors_length); } + + uint8* extended_attributes() { return _end(); } + const uint8* extended_attributes() const { return _end(); } + uint8* allocation_descriptors() { return _end()+extended_attributes_length(); } + const uint8* allocation_descriptors() const { return _end()+extended_attributes_length(); } + + // set functions + void set_uid(uint32 uid) { _uid = B_HOST_TO_LENDIAN_INT32(uid); } + void set_gid(uint32 gid) { _gid = B_HOST_TO_LENDIAN_INT32(gid); } + void set_permissions(uint32 permissions) { _permissions = B_HOST_TO_LENDIAN_INT32(permissions); } + + void set_file_link_count(uint16 count) { _file_link_count = B_HOST_TO_LENDIAN_INT16(count); } + void set_record_format(uint8 format) { _record_format = format; } + void set_record_display_attributes(uint8 attributes) { _record_display_attributes = attributes; } + void set_record_length(uint32 length) { _record_length = B_HOST_TO_LENDIAN_INT32(length); } + + void set_information_length(uint64 length) { _information_length = B_HOST_TO_LENDIAN_INT64(length); } + void set_object_size(uint64 size) { _object_size = B_HOST_TO_LENDIAN_INT64(size); } + void set_logical_blocks_recorded(uint64 blocks) { _logical_blocks_recorded = B_HOST_TO_LENDIAN_INT64(blocks); } + + void set_checkpoint(uint32 checkpoint) { _checkpoint = B_HOST_TO_LENDIAN_INT32(checkpoint); } + void set_reserved(uint32 reserved) { _reserved = B_HOST_TO_LENDIAN_INT32(reserved); } + + void set_unique_id(uint64 id) { _unique_id = B_HOST_TO_LENDIAN_INT64(id); } + + void set_extended_attributes_length(uint32 length) { _extended_attributes_length = B_HOST_TO_LENDIAN_INT32(length); } + void set_allocation_descriptors_length(uint32 length) { _allocation_descriptors_length = B_HOST_TO_LENDIAN_INT32(length); } + +private: + static const uint32 _descriptor_length = 216; + uint8* _end() { return reinterpret_cast(this)+_descriptor_length; } + const uint8* _end() const { return reinterpret_cast(this)+_descriptor_length; } + + descriptor_tag _tag; + icb_entry_tag _icb_tag; + uint32 _uid; + uint32 _gid; + /*! \todo List perms in comment and add handy union thingy */ + uint32 _permissions; + /*! Identifies the number of file identifier descriptors referencing + this icb. + */ + uint16 _file_link_count; + uint8 _record_format; //!< To be set to 0 per UDF-2.01 2.3.6.1 + uint8 _record_display_attributes; //!< To be set to 0 per UDF-2.01 2.3.6.2 + uint32 _record_length; //!< To be set to 0 per UDF-2.01 2.3.6.3 + uint64 _information_length; + uint64 _object_size; + uint64 _logical_blocks_recorded; //!< To be 0 for files and dirs with embedded data + timestamp _access_date_and_time; + timestamp _modification_date_and_time; + timestamp _creation_date_and_time; // <== EXTENDED FILE ENTRY ONLY + timestamp _attribute_date_and_time; + /*! \brief Initially 1, may be incremented upon user request. */ + uint32 _checkpoint; + uint32 _reserved; // <== EXTENDED FILE ENTRY ONLY + long_address _extended_attribute_icb; + long_address _stream_directory_icb; // <== EXTENDED FILE ENTRY ONLY + entity_id _implementation_id; + /*! \brief The unique id identifying this file entry + + The id of the root directory of a file set shall be 0. + + \todo Detail the system specific requirements for unique ids from UDF-2.01 3.2.1.1 + */ + uint64 _unique_id; + uint32 _extended_attributes_length; + uint32 _allocation_descriptors_length; + +}; + + +}; // namespace Udf + +#endif // _UDF_DISK_STRUCTURES_H + diff --git a/src/tests/add-ons/kernel/file_systems/udf/r5/Utils.cpp b/src/tests/add-ons/kernel/file_systems/udf/r5/Utils.cpp new file mode 100644 index 0000000000..7a76361260 --- /dev/null +++ b/src/tests/add-ons/kernel/file_systems/udf/r5/Utils.cpp @@ -0,0 +1,173 @@ +//---------------------------------------------------------------------- +// This software is part of the OpenBeOS distribution and is covered +// by the OpenBeOS license. +// +// Copyright (c) 2003 Tyler Dauwalder, tyler@dauwalder.net +//--------------------------------------------------------------------- + +/*! \file Utils.cpp + + Miscellaneous Udf utility functions. +*/ + +#include "Utils.h" + +extern "C" { + #ifndef _IMPEXP_KERNEL + # define _IMPEXP_KERNEL + #endif + + extern int32 timezone_offset; +} + + +namespace Udf { + +long_address +to_long_address(vnode_id id, uint32 length) +{ + DEBUG_INIT_ETC(NULL, ("vnode_id: %Ld (0x%Lx), length: %ld", id, id, length)); + long_address result; + result.set_block((id >> 16) & 0xffffffff); + result.set_partition(id & 0xffff); + result.set_length(length); + DUMP(result); + return result; +} + +vnode_id +to_vnode_id(long_address address) +{ + DEBUG_INIT(NULL); + vnode_id result = address.block(); + result <<= 16; + result |= address.partition(); + PRINT(("block: %ld, 0x%lx\n", address.block(), address.block())); + PRINT(("partition: %d, 0x%x\n", address.partition(), address.partition())); + PRINT(("length: %ld, 0x%lx\n", address.length(), address.length())); + PRINT(("vnode_id: %Ld, 0x%Lx\n", result, result)); + return result; +} + +time_t +make_time(timestamp ×tamp) +{ + DEBUG_INIT_ETC(NULL, ("timestamp: (tnt: 0x%x, type: %d, timezone: %d = 0x%x, year: %d, " + "month: %d, day: %d, hour: %d, minute: %d, second: %d)", timestamp.type_and_timezone(), + timestamp.type(), timestamp.timezone(), + timestamp.timezone(),timestamp.year(), + timestamp.month(), timestamp.day(), timestamp.hour(), timestamp.minute(), timestamp.second())); + + time_t result = 0; + + if (timestamp.year() >= 1970) { + const int monthLengths[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; + + int year = timestamp.year(); + int month = timestamp.month(); + int day = timestamp.day(); + int hour = timestamp.hour(); + int minute = timestamp.minute(); + int second = timestamp.second(); + + // Range check the timezone offset, then round it down + // to the nearest hour, since no one I know treats timezones + // with a per-minute granularity, and none of the other OSes + // I've looked at appear to either. + int timezone_offset = timestamp.timezone(); + if (-1440 > timezone_offset || timezone_offset > 1440) + timezone_offset = 0; + timezone_offset -= timezone_offset % 60; + + int previousLeapYears = (year - 1968) / 4; + bool isLeapYear = (year - 1968) % 4 == 0; + if (isLeapYear) + --previousLeapYears; + + // Years to days + result = (year - 1970) * 365 + previousLeapYears; + // Months to days + for (int i = 0; i < month-1; i++) { + result += monthLengths[i]; + } + if (month > 2 && isLeapYear) + ++result; + // Days to hours + result = (result + day - 1) * 24; + // Hours to minutes + result = (result + hour) * 60 + timezone_offset; + // Minutes to seconds + result = (result + minute) * 60 + second; + } + + return result; +} + +/*! \brief Calculates the block shift amount for the given + block size, which must be a positive power of 2. +*/ +status_t +Udf::get_block_shift(uint32 blockSize, uint32 &blockShift) +{ + if (blockSize == 0) + return B_BAD_VALUE; + uint32 bitCount = 0; + uint32 result = 0; + for (int i = 0; i < 32; i++) { + // Zero out all bits except bit i + uint32 block = blockSize & (uint32(1) << i); + if (block) { + if (++bitCount > 1) { + return B_BAD_VALUE; + } else { + result = i; + } + } + } + blockShift = result; + return B_OK; +} + +/*! \brief Returns "true" if \a value is true, "false" otherwise. +*/ +const char* +Udf::bool_to_string(bool value) +{ + return value ? "true" : "false"; +} + +/*! \brief Takes an overloaded ssize_t return value like those returned + by BFile::Read() and friends, as well as an expected number of bytes, + and returns B_OK if the byte counts match, or the appropriate error + code otherwise. +*/ +status_t +Udf::check_size_error(ssize_t bytesReturned, ssize_t bytesExpected) +{ + return bytesReturned == bytesExpected + ? B_OK + : (bytesReturned >= 0 ? B_IO_ERROR : status_t(bytesReturned)); +} + +/*! \brief Calculates the UDF crc checksum for the given byte stream. + + Based on crc code from UDF-2.50 6.5, as permitted. + + \param data Pointer to the byte stream. + \param length Length of the byte stream in bytes. + + \return The crc checksum, or 0 if an error occurred. +*/ +uint16 +Udf::calculate_crc(uint8 *data, uint16 length) +{ + uint16 crc = 0; + if (data) { + for ( ; length > 0; length--, data++) + crc = Udf::kCrcTable[(crc >> 8 ^ *data) & 0xff] ^ (crc << 8); + } + return crc; +} + +} // namespace Udf + diff --git a/src/tests/add-ons/kernel/file_systems/udf/r5/Utils.h b/src/tests/add-ons/kernel/file_systems/udf/r5/Utils.h new file mode 100644 index 0000000000..325287ddaa --- /dev/null +++ b/src/tests/add-ons/kernel/file_systems/udf/r5/Utils.h @@ -0,0 +1,47 @@ +//---------------------------------------------------------------------- +// This software is part of the OpenBeOS distribution and is covered +// by the OpenBeOS license. +// +// Copyright (c) 2003 Tyler Dauwalder, tyler@dauwalder.net +//--------------------------------------------------------------------- +#ifndef _UDF_UTILS_H +#define _UDF_UTILS_H + +/*! \file Utils.h + + Miscellaneous Udf utility functions. +*/ + +#ifndef _IMPEXP_KERNEL +# define _IMPEXP_KERNEL +#endif +#ifdef COMPILE_FOR_R5 +extern "C" { +#endif + #include "fsproto.h" +#ifdef COMPILE_FOR_R5 +} +#endif + +#include "UdfStructures.h" + +namespace Udf { + +long_address to_long_address(vnode_id id, uint32 length = 0); + +vnode_id to_vnode_id(long_address address); + +time_t make_time(timestamp ×tamp); + +status_t get_block_shift(uint32 blockSize, uint32 &blockShift); + +const char* bool_to_string(bool value); + +status_t check_size_error(ssize_t bytesReturned, ssize_t bytesExpected); + +uint16 calculate_crc(uint8 *data, uint16 length); + +} // namespace Udf + +#endif // _UDF_UTILS_H + diff --git a/src/tests/add-ons/kernel/file_systems/udf/r5/VirtualPartition.cpp b/src/tests/add-ons/kernel/file_systems/udf/r5/VirtualPartition.cpp new file mode 100644 index 0000000000..d5216060ec --- /dev/null +++ b/src/tests/add-ons/kernel/file_systems/udf/r5/VirtualPartition.cpp @@ -0,0 +1,44 @@ +#include "VirtualPartition.h" + +#define B_NOT_IMPLEMENTED B_ERROR + +using namespace Udf; + +/*! \brief Creates a new VirtualPartition object. + + VirtualPartition objects require a valid VAT to be found on disc. This involves + looking up the last recorded sector on the disc (via the "READ CD RECORDED + CAPACITY" SCSI-MMC call (code 0x25)), which should contain the file entry for + the VAT. Once found, the VAT can be loaded and accessed like a normal file. +*/ +VirtualPartition::VirtualPartition(PhysicalPartition &physicalPartition) + : fPhysicalPartition(physicalPartition) +{ + // Find VAT +} + +/*! \brief Destroys the VirtualPartition object. +*/ +VirtualPartition::~VirtualPartition() +{ +} + +/*! \brief Maps the given logical block to a physical block on disc. + + The given logical block is indexed into the VAT. If a corresponding + mapped block exists, that block is mapped to a physical block via the + VirtualPartition object's physical partition. +*/ +status_t +VirtualPartition::MapBlock(uint32 logicalBlock, off_t &physicalBlock) +{ + return B_NOT_IMPLEMENTED; +} + +/*! Returns the initialization status of the object. +*/ +status_t +VirtualPartition::InitCheck() +{ + return B_NO_INIT; +} diff --git a/src/tests/add-ons/kernel/file_systems/udf/r5/VirtualPartition.h b/src/tests/add-ons/kernel/file_systems/udf/r5/VirtualPartition.h new file mode 100644 index 0000000000..5870f412ca --- /dev/null +++ b/src/tests/add-ons/kernel/file_systems/udf/r5/VirtualPartition.h @@ -0,0 +1,46 @@ +//---------------------------------------------------------------------- +// This software is part of the OpenBeOS distribution and is covered +// by the OpenBeOS license. +// +// Copyright (c) 2003 Tyler Dauwalder, tyler@dauwalder.net +//--------------------------------------------------------------------- +#ifndef _UDF_VIRTUAL_PARTITION_H +#define _UDF_VIRTUAL_PARTITION_H + +/*! \file VirtualPartition.h +*/ + +#include + +#include "Partition.h" +#include "PhysicalPartition.h" +#include "UdfDebug.h" + +namespace Udf { + +/*! \brief Type 2 virtual partition + + VirtualPartitions add an extra layer of indirection between logical + block numbers and physical block numbers, allowing the underlying + physical block numbers to be changed without changing the original + references to (virtual) logical block numbers. + + Note that VirtualPartitions should be found only on sequentially written + media such as CD-R, per UDF-2.01 2.2.10. + + See also UDF-2.01 2.2.8, UDF-2.01 2.2.10 +*/ +class VirtualPartition : public Partition { +public: + VirtualPartition(PhysicalPartition &physicalPartition); + virtual ~VirtualPartition(); + virtual status_t MapBlock(uint32 logicalBlock, off_t &physicalBlock); + + status_t InitCheck(); +private: + PhysicalPartition fPhysicalPartition; +}; + +}; // namespace Udf + +#endif // _UDF_VIRTUAL_PARTITION_H diff --git a/src/tests/add-ons/kernel/file_systems/udf/r5/Volume.cpp b/src/tests/add-ons/kernel/file_systems/udf/r5/Volume.cpp new file mode 100644 index 0000000000..1f11d980c0 --- /dev/null +++ b/src/tests/add-ons/kernel/file_systems/udf/r5/Volume.cpp @@ -0,0 +1,349 @@ +//---------------------------------------------------------------------- +// This software is part of the OpenBeOS distribution and is covered +// by the OpenBeOS license. +// +// Copyright (c) 2003 Tyler Dauwalder, tyler@dauwalder.net +// Mad props to Axel Dörfler and his BFS implementation, from which +// this UDF implementation draws much influence (and a little code :-P). +//---------------------------------------------------------------------- +#include "Volume.h" + +#include "Icb.h" +#include "MemoryChunk.h" +#include "PhysicalPartition.h" +#include "Recognition.h" + +using namespace Udf; + +/*! \brief Creates an unmounted volume with the given id. +*/ +Volume::Volume(nspace_id id) + : fId(id) + , fDevice(-1) + , fMounted(false) + , fOffset(0) + , fLength(0) + , fBlockSize(0) + , fBlockShift(0) + , fRootIcb(NULL) +{ + for (int i = 0; i < UDF_MAX_PARTITION_MAPS; i++) + fPartitions[i] = NULL; +} + +Volume::~Volume() +{ + _Unset(); +} + +/*! \brief Attempts to mount the given device. + + \param volumeStart The block on the given device whereat the volume begins. + \param volumeLength The block length of the volume on the given device. +*/ +status_t +Volume::Mount(const char *deviceName, off_t offset, off_t length, + uint32 blockSize, uint32 flags) +{ + DEBUG_INIT_ETC("Volume", + ("deviceName: `%s', offset: %Ld, length: %Ld, blockSize: %ld, " + "flags: %ld", deviceName, offset, length, blockSize, flags)); + if (!deviceName) + RETURN(B_BAD_VALUE); + if (Mounted()) { + // Already mounted, thank you for asking + RETURN(B_BUSY); + } + + // Open the device read only + int device = open(deviceName, O_RDONLY); + if (device < B_OK) + RETURN(device); + + status_t error = B_OK; + + // If the device is actually a normal file, try to disable the cache + // for the file in the parent filesystem +#if _KERNEL_MODE + struct stat stat; + error = fstat(device, &stat) < 0 ? B_ERROR : B_OK; + if (!error) { + if (stat.st_mode & S_IFREG && ioctl(device, IOCTL_FILE_UNCACHED_IO, NULL) < 0) { + DIE(("Unable to disable cache of underlying file system.\n")); + } + } +#endif + + logical_volume_descriptor logicalVolumeDescriptor; + partition_descriptor partitionDescriptors[Udf::kMaxPartitionDescriptors]; + uint8 partitionDescriptorCount; + uint32 blockShift; + + // Run through the volume recognition and descriptor sequences to + // see if we have a potentially valid UDF volume on our hands + error = udf_recognize(device, offset, length, blockSize, blockShift, + logicalVolumeDescriptor, partitionDescriptors, + partitionDescriptorCount); + + // Set up the block cache + if (!error) + error = init_cache_for_device(device, length); + + int physicalCount = 0; + int virtualCount = 0; + int sparableCount = 0; + int metadataCount = 0; + + // Set up the partitions + if (!error) { + // Set up physical and sparable partitions first + int offset = 0; + for (uint8 i = 0; i < logicalVolumeDescriptor.partition_map_count() + && !error; i++) + { + uint8 *maps = logicalVolumeDescriptor.partition_maps(); + partition_map_header *header = + reinterpret_cast(maps+offset); + PRINT(("partition map %d (type %d):\n", i, header->type())); + if (header->type() == 1) { + PRINT(("map type: physical\n")); + physical_partition_map* map = + reinterpret_cast(header); + // Find the corresponding partition descriptor + partition_descriptor *descriptor = NULL; + for (uint8 j = 0; j < partitionDescriptorCount; j++) { + if (map->partition_number() == + partitionDescriptors[j].partition_number()) + { + descriptor = &partitionDescriptors[j]; + break; + } + } + // Create and add the partition + if (descriptor) { + PhysicalPartition *partition = new(nothrow) PhysicalPartition( + map->partition_number(), + descriptor->start(), + descriptor->length()); + error = partition ? B_OK : B_NO_MEMORY; + if (!error) { + PRINT(("Adding PhysicalPartition(number: %d, start: %ld, " + "length: %ld)\n", map->partition_number(), + descriptor->start(), descriptor->length())); + error = _SetPartition(i, partition); + if (!error) + physicalCount++; + } + } else { + PRINT(("no matching partition descriptor found!\n")); + error = B_ERROR; + } + } else if (header->type() == 2) { + // Figure out what kind of type 2 partition map we have based + // on the type identifier + const entity_id &typeId = header->partition_type_id(); + DUMP(typeId); + DUMP(kSparablePartitionMapId); + if (typeId.matches(kVirtualPartitionMapId)) { + PRINT(("map type: virtual\n")); + virtual_partition_map* map = + reinterpret_cast(header); + virtualCount++; + (void)map; // kill the warning for now + } else if (typeId.matches(kSparablePartitionMapId)) { + PRINT(("map type: sparable\n")); + sparable_partition_map* map = + reinterpret_cast(header); + sparableCount++; + (void)map; // kill the warning for now + } else if (typeId.matches(kMetadataPartitionMapId)) { + PRINT(("map type: metadata\n")); + metadata_partition_map* map = + reinterpret_cast(header); + metadataCount++; + (void)map; // kill the warning for now + } else { + PRINT(("map type: unrecognized (`%.23s')\n", + typeId.identifier())); + error = B_ERROR; + } + } else { + PRINT(("Invalid partition type %d found!\n", header->type())); + error = B_ERROR; + } + offset += header->length(); + } + } + + // Do some checking as to what sorts of partitions we've actually found. + if (!error) { + error = (physicalCount == 1 && virtualCount == 0 + && sparableCount == 0 && metadataCount == 0) + || (physicalCount == 2 && virtualCount == 0 + && sparableCount == 0 && metadataCount == 0) + ? B_OK : B_ERROR; + if (error) { + PRINT(("Invalid partition layout found:\n")); + PRINT((" physical partitions: %d\n", physicalCount)); + PRINT((" virtual partitions: %d\n", virtualCount)); + PRINT((" sparable partitions: %d\n", sparableCount)); + PRINT((" metadata partitions: %d\n", metadataCount)); + } + } + + // We're now going to start creating Icb's, which will expect + // certain parts of the volume to be initialized properly. Thus, + // we initialize those parts here. + if (!error) { + fDevice = device; + fOffset = offset; + fLength = length; + fBlockSize = blockSize; + fBlockShift = blockShift; + } + + // At this point we've found a valid set of volume descriptors and + // our partitions are all set up. We now need to investigate the file + // set descriptor pointed to by the logical volume descriptor. + if (!error) { + MemoryChunk chunk(logicalVolumeDescriptor.file_set_address().length()); + + error = chunk.InitCheck(); + + if (!error) { + off_t address; + // Read in the file set descriptor + error = MapBlock(logicalVolumeDescriptor.file_set_address(), + &address); + if (!error) + address <<= blockShift; + if (!error) { + ssize_t bytesRead = read_pos(device, address, chunk.Data(), + blockSize); + if (bytesRead != ssize_t(blockSize)) { + error = B_IO_ERROR; + PRINT(("read_pos(pos:%Ld, len:%ld) failed with: 0x%lx\n", + address, blockSize, bytesRead)); + } + } + // See if it's valid, and if so, create the root icb + if (!error) { + file_set_descriptor *fileSet = + reinterpret_cast(chunk.Data()); + PDUMP(fileSet); + error = fileSet->tag().id() == TAGID_FILE_SET_DESCRIPTOR + ? B_OK : B_ERROR; + if (!error) + error = fileSet->tag().init_check( + logicalVolumeDescriptor.file_set_address().block()); + if (!error) { + PDUMP(fileSet); + fRootIcb = new(nothrow) Icb(this, fileSet->root_directory_icb()); + error = fRootIcb ? fRootIcb->InitCheck() : B_NO_MEMORY; + } + if (!error) { + error = new_vnode(Id(), RootIcb()->Id(), (void*)RootIcb()); + if (error) { + PRINT(("Error creating vnode for root icb! " + "error = 0x%lx, `%s'\n", error, + strerror(error))); + // Clean up the icb we created, since _Unset() + // won't do this for us. + delete fRootIcb; + fRootIcb = NULL; + } + } + } + } + } + + // If we've made it this far, we're good to go; set the volume + // name and then flag that we're mounted. On the other hand, if + // an error occurred, we need to clean things up. + if (!error) { + fName.SetTo(logicalVolumeDescriptor.logical_volume_identifier()); + fMounted = true; + } else { + _Unset(); + } + + RETURN(error); +} + +const char* +Volume::Name() const { + return fName.Utf8(); +} + +/*! \brief Maps the given logical block to a physical block. +*/ +status_t +Volume::MapBlock(long_address address, off_t *mappedBlock) +{ + DEBUG_INIT_ETC("Volume", + ("partition: %d, block: %ld, mappedBlock: %p", + address.partition(), address.block(), mappedBlock)); + status_t error = mappedBlock ? B_OK : B_BAD_VALUE; + if (!error) { + Partition *partition = _GetPartition(address.partition()); + error = partition ? B_OK : B_BAD_ADDRESS; + if (!error) + error = partition->MapBlock(address.block(), *mappedBlock); + } + RETURN(error); +} + +/*! \brief Unsets the volume and deletes any partitions. + + Does *not* delete the root icb object. +*/ +void +Volume::_Unset() +{ + DEBUG_INIT("Volume"); + fId = 0; + if (fDevice >= 0) { + remove_cached_device_blocks(fDevice, NO_WRITES); + close(fDevice); + } + fDevice = -1; + fMounted = false; + fOffset = 0; + fLength = 0; + fBlockSize = 0; + fBlockShift = 0; + fName.SetTo("", 0); + // delete our partitions + for (int i = 0; i < UDF_MAX_PARTITION_MAPS; i++) + _SetPartition(i, NULL); +} + +/*! \brief Sets the partition associated with the given number after + deleting any previously associated partition. + + \param number The partition number (should be the same as the index + into the lvd's partition map array). + \param partition The new partition (may be NULL). +*/ +status_t +Volume::_SetPartition(uint number, Partition *partition) +{ + status_t error = number < UDF_MAX_PARTITION_MAPS + ? B_OK : B_BAD_VALUE; + if (!error) { + delete fPartitions[number]; + fPartitions[number] = partition; + } + return error; +} + +/*! \brief Returns the partition associated with the given number, or + NULL if no such partition exists or the number is invalid. +*/ +Udf::Partition* +Volume::_GetPartition(uint number) +{ + return (number < UDF_MAX_PARTITION_MAPS) + ? fPartitions[number] : NULL; +} + diff --git a/src/tests/add-ons/kernel/file_systems/udf/r5/Volume.h b/src/tests/add-ons/kernel/file_systems/udf/r5/Volume.h new file mode 100644 index 0000000000..54fef27a40 --- /dev/null +++ b/src/tests/add-ons/kernel/file_systems/udf/r5/Volume.h @@ -0,0 +1,90 @@ +//---------------------------------------------------------------------- +// This software is part of the OpenBeOS distribution and is covered +// by the OpenBeOS license. +// +// Copyright (c) 2003 Tyler Dauwalder, tyler@dauwalder.net +// Mad props to Axel Dörfler and his BFS implementation, from which +// this UDF implementation draws much influence (and a little code :-P). +//--------------------------------------------------------------------- +#ifndef _UDF_VOLUME_H +#define _UDF_VOLUME_H + +/*! \file Volume.h +*/ + +#include +#ifndef _IMPEXP_KERNEL +# define _IMPEXP_KERNEL +#endif + +#ifdef COMPILE_FOR_R5 +extern "C" { +#endif + #include "fsproto.h" +#ifdef COMPILE_FOR_R5 +} +#endif + +#include "kernel_cpp.h" +#include "UdfDebug.h" + +#include "UdfString.h" +#include "UdfStructures.h" +#include "Partition.h" + +namespace Udf { + +class Icb; + +class Volume { +public: + // Construction/destruction + Volume(nspace_id id); + ~Volume(); + + // Mounting/unmounting + status_t Mount(const char *deviceName, off_t offset, off_t length, + uint32 blockSize, uint32 flags); + status_t Unmount(); + + // Address mapping + status_t MapBlock(long_address address, off_t *mappedBlock); + status_t MapExtent(long_address logicalExtent, extent_address &physicalExtent); + + // Miscellaneous info + const char *Name() const; + int Device() const { return fDevice; } + nspace_id Id() const { return fId; } + off_t Offset() const { return fOffset; } + off_t Length() const { return fLength; } + uint32 BlockSize() const { return fBlockSize; } + uint32 BlockShift() const { return fBlockShift; } + bool Mounted() const { return fMounted; } + Icb* RootIcb() { return fRootIcb; } + +private: + Volume(); // unimplemented + Volume(const Volume &ref); // unimplemented + Volume& operator=(const Volume &ref); // unimplemented + + void _Unset(); + + status_t _SetPartition(uint number, Partition *partition); + Partition* _GetPartition(uint number); + +private: + nspace_id fId; + int fDevice; + bool fMounted; + off_t fOffset; + off_t fLength; + uint32 fBlockSize; + uint32 fBlockShift; + Partition *fPartitions[UDF_MAX_PARTITION_MAPS]; + Icb *fRootIcb; // Destroyed by vfs via callback to release_node() + String fName; +}; + +}; // namespace Udf + +#endif // _UDF_VOLUME_H diff --git a/src/tests/add-ons/kernel/file_systems/udf/r5/cache.h b/src/tests/add-ons/kernel/file_systems/udf/r5/cache.h new file mode 100644 index 0000000000..a0e913840e --- /dev/null +++ b/src/tests/add-ons/kernel/file_systems/udf/r5/cache.h @@ -0,0 +1,108 @@ +/* + Copyright 1999-2001, Be Incorporated. All Rights Reserved. + This file may be used under the terms of the Be Sample Code License. +*/ + +#ifndef _CACHE_H_ +#define _CACHE_H_ + +#include + +typedef struct hash_ent { + int dev; + off_t bnum; + off_t hash_val; + void *data; + struct hash_ent *next; +} hash_ent; + + +typedef struct hash_table { + hash_ent **table; + int max; + int mask; /* == max - 1 */ + int num_elements; +} hash_table; + + +#define HT_DEFAULT_MAX 128 + + +typedef struct cache_ent { + int dev; + off_t block_num; + int bsize; + volatile int flags; + + void *data; + void *clone; /* copy of data by set_block_info() */ + int lock; + + void (*func)(off_t bnum, size_t num_blocks, void *arg); + off_t logged_bnum; + void *arg; + + struct cache_ent *next, /* points toward mru end of list */ + *prev; /* points toward lru end of list */ + +} cache_ent; + +#define CE_NORMAL 0x0000 /* a nice clean pristine page */ +#define CE_DIRTY 0x0002 /* needs to be written to disk */ +#define CE_BUSY 0x0004 /* this block has i/o happening, don't touch it */ + + +typedef struct cache_ent_list { + cache_ent *lru; /* tail of the list */ + cache_ent *mru; /* head of the list */ +} cache_ent_list; + + +typedef struct block_cache { + struct lock lock; + int flags; + int cur_blocks; + int max_blocks; + hash_table ht; + + cache_ent_list normal, /* list of "normal" blocks (clean & dirty) */ + locked; /* list of clean and locked blocks */ +} block_cache; + +#if 0 /* XXXdbg -- need to deal with write through caches */ +#define DC_WRITE_THROUGH 0x0001 /* cache is write-through (for floppies) */ +#endif + +#define ALLOW_WRITES 1 +#define NO_WRITES 0 + +extern _IMPEXP_KERNEL int init_block_cache(int max_blocks, int flags); +extern _IMPEXP_KERNEL void shutdown_block_cache(void); + +extern _IMPEXP_KERNEL void force_cache_flush(int dev, int prefer_log_blocks); +extern _IMPEXP_KERNEL int flush_blocks(int dev, off_t bnum, int nblocks); +extern _IMPEXP_KERNEL int flush_device(int dev, int warn_locked); + +extern _IMPEXP_KERNEL int init_cache_for_device(int fd, off_t max_blocks); +extern _IMPEXP_KERNEL int remove_cached_device_blocks(int dev, int allow_write); + +extern _IMPEXP_KERNEL void *get_block(int dev, off_t bnum, int bsize); +extern _IMPEXP_KERNEL void *get_empty_block(int dev, off_t bnum, int bsize); +extern _IMPEXP_KERNEL int release_block(int dev, off_t bnum); +extern _IMPEXP_KERNEL int mark_blocks_dirty(int dev, off_t bnum, int nblocks); + + +extern _IMPEXP_KERNEL int cached_read(int dev, off_t bnum, void *data, off_t num_blocks, int bsize); +extern _IMPEXP_KERNEL int cached_write(int dev, off_t bnum, const void *data, + off_t num_blocks, int bsize); +extern _IMPEXP_KERNEL int cached_write_locked(int dev, off_t bnum, const void *data, + off_t num_blocks, int bsize); +extern _IMPEXP_KERNEL int set_blocks_info(int dev, off_t *blocks, int nblocks, + void (*func)(off_t bnum, size_t nblocks, void *arg), + void *arg); + + +extern _IMPEXP_KERNEL size_t read_phys_blocks (int fd, off_t bnum, void *data, uint num_blocks, int bsize); +extern _IMPEXP_KERNEL size_t write_phys_blocks(int fd, off_t bnum, void *data, uint num_blocks, int bsize); + +#endif /* _CACHE_H_ */ diff --git a/src/tests/add-ons/kernel/file_systems/udf/r5/crc_table.cpp b/src/tests/add-ons/kernel/file_systems/udf/r5/crc_table.cpp new file mode 100644 index 0000000000..269f446e59 --- /dev/null +++ b/src/tests/add-ons/kernel/file_systems/udf/r5/crc_table.cpp @@ -0,0 +1,53 @@ +//---------------------------------------------------------------------- +// This software is part of the OpenBeOS distribution and is covered +// by the OpenBeOS license. +// +// Copyright (c) 2003 Tyler Dauwalder, tyler@dauwalder.net +//--------------------------------------------------------------------- + +/*! \file crc_table.cpp + + Standalone program to generate the CRC table used for calculating + UDF tag id CRC values. + + This code based off of crc code in UDF-2.50 specs, as permitted. + See UDF-2.50 6.5 for more information. +*/ + +#include + +int +main(int argc, char *argv[]) { + ulong crc, poly; + + if (argc != 2) { + fprintf(stderr, "USAGE: crc_table \n"); + return 0; + } + + sscanf(argv[1], "%lo", &poly); + if (poly & 0xffff0000) { + fprintf(stderr, "ERROR: polynomial is too large, sucka.\n"); + return 0; + } + + printf("//! CRC 0%o table, as generated by crc_table.cpp\n", poly); + printf("static uint16 crc_table[256] = { \n"); + for (int n = 0; n < 256; n++) { + if (n%8 == 0) + printf(" "); + crc = n << 8; + for (int i = 0; i < 8; i++) { + if (crc & 0x8000) + crc = (crc << 1) ^ poly; + else + crc <<= 1; + crc &= 0xffff; + } + printf("0x%04x%s ", crc, (n != 255 ? "," : "")); + if (n%8 == 7) + printf("\n"); + } + printf("};\n"); + return 0; +} diff --git a/src/tests/add-ons/kernel/file_systems/udf/r5/drive_setup_addon/Jamfile b/src/tests/add-ons/kernel/file_systems/udf/r5/drive_setup_addon/Jamfile new file mode 100644 index 0000000000..ece2906377 --- /dev/null +++ b/src/tests/add-ons/kernel/file_systems/udf/r5/drive_setup_addon/Jamfile @@ -0,0 +1,64 @@ +SubDir HAIKU_TOP src add-ons kernel file_systems udf drive_setup_addon ; + +# save original optimization level +oldOPTIM = $(OPTIM) ; + +# set some additional defines +{ + local defines = + DRIVE_SETUP_ADDON + ; + + if $(COMPILE_FOR_R5) { + defines += COMPILE_FOR_R5 ; + } + + if $(DEBUG) { + #defines += DEBUG ; + } else { + # the gcc on BeOS doesn't compile BFS correctly with -O2 or more + OPTIM = -O1 ; + } + + defines = [ FDefines $(defines) ] ; + SubDirCcFlags $(defines) -Wall -Wno-multichar ; + SubDirC++Flags $(defines) -Wall -Wno-multichar ; +} + +UsePrivateHeaders [ FDirName kernel util ] ; +SubDirHdrs [ FDirName $(HAIKU_TOP) src add-ons kernel file_systems udf ] ; + +# Note that the add-on is named "i-udf-ds" to put it alphabetically +# before the standard iso9660 add-on, thus giving it first dibs at +# iso9660/UDF hybrid discs. +Addon i-udf-ds : [ FDirName drive_setup fs ] : + udf-ds.cpp + Recognition.cpp + UdfDebug.cpp + UdfString.cpp + UdfStructures.cpp + Utils.cpp + + : false + : +; + +SEARCH on [ FGristFiles + Recognition.cpp UdfDebug.cpp UdfString.cpp UdfStructures.cpp Utils.cpp + ] = [ FDirName $(HAIKU_TOP) src add-ons kernel file_systems udf ] ; + +rule InstallUDFDS +{ + Depends $(<) : $(>) ; +} + +actions ignore InstallUDFDS +{ + cp $(>) /boot/beos/system/add-ons/drive_setup/fs/ +} + +InstallUDFDS install : i-udf-ds ; + +# restore original optimization level +OPTIM = $(oldOPTIM) ; + diff --git a/src/tests/add-ons/kernel/file_systems/udf/r5/drive_setup_addon/udf-ds.cpp b/src/tests/add-ons/kernel/file_systems/udf/r5/drive_setup_addon/udf-ds.cpp new file mode 100644 index 0000000000..239355de73 --- /dev/null +++ b/src/tests/add-ons/kernel/file_systems/udf/r5/drive_setup_addon/udf-ds.cpp @@ -0,0 +1,63 @@ +//---------------------------------------------------------------------- +// This software is part of the OpenBeOS distribution and is covered +// by the OpenBeOS license. +// +// Copyright (c) 2003 Tyler Dauwalder, tyler@dauwalder.net +//--------------------------------------------------------------------- + +/*! \file DriveSetupAddon.cpp + + \brief UDF DriveSetup add-on for R5. + + The interface implemented here is detailed in the Be Newsletter, + Volume II, Issue 23, "Getting Mounted". Thanks to Ingo Weinhold + for digging that up. :-) +*/ + +#include "UdfDebug.h" +#include "Recognition.h" + +struct partition_data { + char partition_name[B_FILE_NAME_LENGTH]; + char partition_type[B_FILE_NAME_LENGTH]; + char file_system_short_name[B_FILE_NAME_LENGTH]; + char file_system_long_name[B_FILE_NAME_LENGTH]; + char volume_name[B_FILE_NAME_LENGTH]; + char mounted_at[B_FILE_NAME_LENGTH]; + uint32 logical_block_size; + uint64 offset; // in logical blocks from start of session + uint64 blocks; + bool hidden; //"non-file system" partition + bool reserved1; + uint32 reserved2; +}; + +extern "C" bool ds_fs_id(partition_data*, int32, uint64, int32); + +bool +ds_fs_id(partition_data *data, int32 device, uint64 sessionOffset, + int32 blockSize) +{ + DEBUG_INIT_ETC(NULL, ("%p, %ld, %Lu, %ld", data, + device, sessionOffset, blockSize)); + + if (!data || device < 0) + return false; + + bool result = false; + + char name[256]; + // Udf volume names are at most 63 2-byte unicode chars, so 256 UTF-8 + // chars should cover us. + + status_t error = Udf::udf_recognize(device, (data->offset + sessionOffset), data->blocks, blockSize, name); + if (!error) { + strcpy(data->file_system_short_name, "udf"); + strcpy(data->file_system_long_name, "Universal Disk Format"); + strcpy(data->volume_name, name); + result = true; + } + + return result; +} + diff --git a/src/tests/add-ons/kernel/file_systems/udf/r5/fsproto.h b/src/tests/add-ons/kernel/file_systems/udf/r5/fsproto.h new file mode 100644 index 0000000000..818ffcdd9d --- /dev/null +++ b/src/tests/add-ons/kernel/file_systems/udf/r5/fsproto.h @@ -0,0 +1,261 @@ +/* + Copyright 1999-2001, Be Incorporated. All Rights Reserved. + This file may be used under the terms of the Be Sample Code License. +*/ + +#ifndef _FSPROTO_H +#define _FSPROTO_H + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +typedef dev_t nspace_id; +typedef ino_t vnode_id; + +/* + * PUBLIC PART OF THE FILE SYSTEM PROTOCOL + */ + +#define WSTAT_MODE 0x0001 +#define WSTAT_UID 0x0002 +#define WSTAT_GID 0x0004 +#define WSTAT_SIZE 0x0008 +#define WSTAT_ATIME 0x0010 +#define WSTAT_MTIME 0x0020 +#define WSTAT_CRTIME 0x0040 + +#define WFSSTAT_NAME 0x0001 + +#define B_ENTRY_CREATED 1 +#define B_ENTRY_REMOVED 2 +#define B_ENTRY_MOVED 3 +#define B_STAT_CHANGED 4 +#define B_ATTR_CHANGED 5 +#define B_DEVICE_MOUNTED 6 +#define B_DEVICE_UNMOUNTED 7 + +#define B_STOP_WATCHING 0x0000 +#define B_WATCH_NAME 0x0001 +#define B_WATCH_STAT 0x0002 +#define B_WATCH_ATTR 0x0004 +#define B_WATCH_DIRECTORY 0x0008 + +#define SELECT_READ 1 +#define SELECT_WRITE 2 +#define SELECT_EXCEPTION 3 + +#define B_CUR_FS_API_VERSION 2 + +#define IOCTL_FILE_UNCACHED_IO 10000 +#define IOCTL_CREATE_TIME 10002 +#define IOCTL_MODIFIED_TIME 10003 + +struct attr_info; +struct index_info; + +typedef int op_read_vnode(void *ns, vnode_id vnid, char r, void **node); +typedef int op_write_vnode(void *ns, void *node, char r); +typedef int op_remove_vnode(void *ns, void *node, char r); +typedef int op_secure_vnode(void *ns, void *node); +typedef int op_wake_vnode(void *ns, void *node); +typedef int op_suspend_vnode(void *ns, void *node); + +typedef int op_walk(void *ns, void *base, const char *file, char **newpath, + vnode_id *vnid); + +typedef int op_access(void *ns, void *node, int mode); + +typedef int op_create(void *ns, void *dir, const char *name, + int omode, int perms, vnode_id *vnid, void **cookie); +typedef int op_mkdir(void *ns, void *dir, const char *name, int perms); +typedef int op_symlink(void *ns, void *dir, const char *name, + const char *path); +typedef int op_link(void *ns, void *dir, const char *name, void *node); + +typedef int op_rename(void *ns, void *olddir, const char *oldname, + void *newdir, const char *newname); +typedef int op_unlink(void *ns, void *dir, const char *name); +typedef int op_rmdir(void *ns, void *dir, const char *name); + +typedef int op_readlink(void *ns, void *node, char *buf, size_t *bufsize); + +typedef int op_opendir(void *ns, void *node, void **cookie); +typedef int op_closedir(void *ns, void *node, void *cookie); +typedef int op_rewinddir(void *ns, void *node, void *cookie); +typedef int op_readdir(void *ns, void *node, void *cookie, long *num, + struct dirent *buf, size_t bufsize); + +typedef int op_open(void *ns, void *node, int omode, void **cookie); +typedef int op_close(void *ns, void *node, void *cookie); +typedef int op_free_cookie(void *ns, void *node, void *cookie); +typedef int op_read(void *ns, void *node, void *cookie, off_t pos, void *buf, + size_t *len); +typedef int op_write(void *ns, void *node, void *cookie, off_t pos, + const void *buf, size_t *len); +typedef int op_readv(void *ns, void *node, void *cookie, off_t pos, const iovec *vec, + size_t count, size_t *len); +typedef int op_writev(void *ns, void *node, void *cookie, off_t pos, const iovec *vec, + size_t count, size_t *len); +typedef int op_ioctl(void *ns, void *node, void *cookie, int cmd, void *buf, + size_t len); +typedef int op_setflags(void *ns, void *node, void *cookie, int flags); + +typedef int op_rstat(void *ns, void *node, struct stat *); +typedef int op_wstat(void *ns, void *node, struct stat *, long mask); +typedef int op_fsync(void *ns, void *node); + +typedef int op_select(void *ns, void *node, void *cookie, uint8 event, + uint32 ref, selectsync *sync); +typedef int op_deselect(void *ns, void *node, void *cookie, uint8 event, + selectsync *sync); + +typedef int op_initialize(const char *devname, void *parms, size_t len); +typedef int op_mount(nspace_id nsid, const char *devname, ulong flags, + void *parms, size_t len, void **data, vnode_id *vnid); +typedef int op_unmount(void *ns); +typedef int op_sync(void *ns); +typedef int op_rfsstat(void *ns, struct fs_info *); +typedef int op_wfsstat(void *ns, struct fs_info *, long mask); + + +typedef int op_open_attrdir(void *ns, void *node, void **cookie); +typedef int op_close_attrdir(void *ns, void *node, void *cookie); +typedef int op_rewind_attrdir(void *ns, void *node, void *cookie); +typedef int op_read_attrdir(void *ns, void *node, void *cookie, long *num, + struct dirent *buf, size_t bufsize); +typedef int op_remove_attr(void *ns, void *node, const char *name); +typedef int op_rename_attr(void *ns, void *node, const char *oldname, + const char *newname); +typedef int op_stat_attr(void *ns, void *node, const char *name, + struct attr_info *buf); + +typedef int op_write_attr(void *ns, void *node, const char *name, int type, + const void *buf, size_t *len, off_t pos); +typedef int op_read_attr(void *ns, void *node, const char *name, int type, + void *buf, size_t *len, off_t pos); + +typedef int op_open_indexdir(void *ns, void **cookie); +typedef int op_close_indexdir(void *ns, void *cookie); +typedef int op_rewind_indexdir(void *ns, void *cookie); +typedef int op_read_indexdir(void *ns, void *cookie, long *num, + struct dirent *buf, size_t bufsize); +typedef int op_create_index(void *ns, const char *name, int type, int flags); +typedef int op_remove_index(void *ns, const char *name); +typedef int op_rename_index(void *ns, const char *oldname, + const char *newname); +typedef int op_stat_index(void *ns, const char *name, struct index_info *buf); + +typedef int op_open_query(void *ns, const char *query, ulong flags, + port_id port, long token, void **cookie); +typedef int op_close_query(void *ns, void *cookie); +typedef int op_read_query(void *ns, void *cookie, long *num, + struct dirent *buf, size_t bufsize); + +typedef struct vnode_ops { + op_read_vnode (*read_vnode); + op_write_vnode (*write_vnode); + op_remove_vnode (*remove_vnode); + op_secure_vnode (*secure_vnode); + op_walk (*walk); + op_access (*access); + op_create (*create); + op_mkdir (*mkdir); + op_symlink (*symlink); + op_link (*link); + op_rename (*rename); + op_unlink (*unlink); + op_rmdir (*rmdir); + op_readlink (*readlink); + op_opendir (*opendir); + op_closedir (*closedir); + op_free_cookie (*free_dircookie); + op_rewinddir (*rewinddir); + op_readdir (*readdir); + op_open (*open); + op_close (*close); + op_free_cookie (*free_cookie); + op_read (*read); + op_write (*write); + op_readv (*readv); + op_writev (*writev); + op_ioctl (*ioctl); + op_setflags (*setflags); + op_rstat (*rstat); + op_wstat (*wstat); + op_fsync (*fsync); + op_initialize (*initialize); + op_mount (*mount); + op_unmount (*unmount); + op_sync (*sync); + op_rfsstat (*rfsstat); + op_wfsstat (*wfsstat); + op_select (*select); + op_deselect (*deselect); + op_open_indexdir (*open_indexdir); + op_close_indexdir (*close_indexdir); + op_free_cookie (*free_indexdircookie); + op_rewind_indexdir (*rewind_indexdir); + op_read_indexdir (*read_indexdir); + op_create_index (*create_index); + op_remove_index (*remove_index); + op_rename_index (*rename_index); + op_stat_index (*stat_index); + op_open_attrdir (*open_attrdir); + op_close_attrdir (*close_attrdir); + op_free_cookie (*free_attrdircookie); + op_rewind_attrdir (*rewind_attrdir); + op_read_attrdir (*read_attrdir); + op_write_attr (*write_attr); + op_read_attr (*read_attr); + op_remove_attr (*remove_attr); + op_rename_attr (*rename_attr); + op_stat_attr (*stat_attr); + op_open_query (*open_query); + op_close_query (*close_query); + op_free_cookie (*free_querycookie); + op_read_query (*read_query); + // for Dano compatibility only + op_wake_vnode (*wake_vnode); + op_suspend_vnode (*suspend_vnode); +} vnode_ops; + +#ifdef __cplusplus +extern "C" { +#endif + +extern _IMPEXP_KERNEL int new_path(const char *path, char **copy); +extern _IMPEXP_KERNEL void free_path(char *p); + +extern _IMPEXP_KERNEL int notify_listener(int op, nspace_id nsid, + vnode_id vnida, vnode_id vnidb, + vnode_id vnidc, const char *name); +extern _IMPEXP_KERNEL int send_notification(port_id port, long token, + ulong what, long op, nspace_id nsida, + nspace_id nsidb, vnode_id vnida, + vnode_id vnidb, vnode_id vnidc, + const char *name); +extern _IMPEXP_KERNEL int get_vnode(nspace_id nsid, vnode_id vnid, void **data); +extern _IMPEXP_KERNEL int put_vnode(nspace_id nsid, vnode_id vnid); +extern _IMPEXP_KERNEL int new_vnode(nspace_id nsid, vnode_id vnid, void *data); +extern _IMPEXP_KERNEL int remove_vnode(nspace_id nsid, vnode_id vnid); +extern _IMPEXP_KERNEL int unremove_vnode(nspace_id nsid, vnode_id vnid); +extern _IMPEXP_KERNEL int is_vnode_removed(nspace_id nsid, vnode_id vnid); + +#ifdef __cplusplus +} +#endif + +extern _EXPORT vnode_ops fs_entry; +extern _EXPORT int32 api_version; + +#endif diff --git a/src/tests/add-ons/kernel/file_systems/udf/r5/lock.h b/src/tests/add-ons/kernel/file_systems/udf/r5/lock.h new file mode 100644 index 0000000000..b05adaa21b --- /dev/null +++ b/src/tests/add-ons/kernel/file_systems/udf/r5/lock.h @@ -0,0 +1,47 @@ +/* + Copyright 1999-2001, Be Incorporated. All Rights Reserved. + This file may be used under the terms of the Be Sample Code License. +*/ + +#ifndef _LOCK_H +#define _LOCK_H + +#include + +#include + +#ifdef __cplusplus + extern "C" { +#else + typedef struct lock lock; + typedef struct mlock mlock; +#endif + + +struct lock { + sem_id s; + long c; +}; + +struct mlock { + sem_id s; +}; + +extern _IMPEXP_KERNEL int new_lock(lock *l, const char *name); +extern _IMPEXP_KERNEL int free_lock(lock *l); + +#define LOCK(l) if (atomic_add(&l.c, -1) <= 0) acquire_sem(l.s); +#define UNLOCK(l) if (atomic_add(&l.c, 1) < 0) release_sem(l.s); + +extern _IMPEXP_KERNEL int new_mlock(mlock *l, long c, const char *name); +extern _IMPEXP_KERNEL int free_mlock(mlock *l); + +#define LOCKM(l,cnt) acquire_sem_etc(l.s, cnt, 0, 0) +#define UNLOCKM(l,cnt) release_sem_etc(l.s, cnt, 0) + + +#ifdef __cplusplus + } // extern "C" +#endif + +#endif diff --git a/src/tests/add-ons/kernel/file_systems/udf/r5/udf.cpp b/src/tests/add-ons/kernel/file_systems/udf/r5/udf.cpp new file mode 100644 index 0000000000..a6c0386077 --- /dev/null +++ b/src/tests/add-ons/kernel/file_systems/udf/r5/udf.cpp @@ -0,0 +1,1034 @@ +//---------------------------------------------------------------------- +// This software is part of the OpenBeOS distribution and is covered +// by the OpenBeOS license. +// +// Copyright (c) 2003 Tyler Dauwalder, tyler@dauwalder.net +// Mucho respecto to Axel Dörfler and his BFS implementation, from +// which this UDF implementation draws much influence (and a little +// code :-P). +//--------------------------------------------------------------------- + +/*! \file udf.cpp +*/ + +#include "UdfDebug.h" +#include "kernel_cpp.h" + +#include +#include +#include +#include +#include +#include + +// BeOS vnode layer stuff +#include +#ifndef _IMPEXP_KERNEL +# define _IMPEXP_KERNEL +#endif + +#ifdef COMPILE_FOR_R5 +extern "C" { +#endif + #include "fsproto.h" +#ifdef COMPILE_FOR_R5 +} +#endif + +#if !_KERNEL_MODE +# define dprintf printf +#endif + +#include "DirectoryIterator.h" +#include "Icb.h" +#include "Utils.h" +#include "Volume.h" + +extern "C" { + // general/volume stuff + static int udf_mount(nspace_id nsid, const char *device, ulong flags, + void *parms, size_t len, void **data, vnode_id *vnid); + static int udf_unmount(void *_ns); + static int udf_read_fs_stat(void *_ns, struct fs_info *); + static int udf_write_fs_stat(void *ns, struct fs_info *, long mode); + static int udf_initialize(const char *devname, void *parms, size_t len); + + static int udf_sync(void *ns); + + static int udf_read_vnode(void *_ns, vnode_id vnid, char r, void **node); + static int udf_release_vnode(void *_ns, void *_node, char r); + static int udf_remove_vnode(void *ns, void *node, char r); + + static int udf_walk(void *_ns, void *_base, const char *file, + char **newpath, vnode_id *vnid); + + static int udf_ioctl(void *ns, void *node, void *cookie, int cmd, void *buf,size_t len); + static int udf_setflags(void *ns, void *node, void *cookie, int flags); + + static int udf_select(void *ns, void *node, void *cookie, uint8 event, + uint32 ref, selectsync *sync); + static int udf_deselect(void *ns, void *node, void *cookie, uint8 event, + selectsync *sync); + static int udf_fsync(void *ns,void *node); + + // file stuff + static int udf_create(void *ns, void *dir, const char *name, + int perms, int omode, vnode_id *vnid, void **cookie); + static int udf_symlink(void *ns, void *dir, const char *name, + const char *path); + static int udf_link(void *ns, void *dir, const char *name, void *node); + static int udf_unlink(void *ns, void *dir, const char *name); + static int udf_rename(void *ns, void *oldDir, const char *oldName, void *newDir, const char *newName); + + + static int udf_read_stat(void *_ns, void *_node, struct stat *st); + static int udf_write_stat(void *ns, void *node, struct stat *st, long mask); + + static int udf_open(void *_ns, void *_node, int omode, void **cookie); + static int udf_read(void *_ns, void *_node, void *cookie, off_t pos, + void *buf, size_t *len); + static int udf_write(void *ns, void *node, void *cookie, off_t pos, + const void *buf, size_t *len); + static int udf_free_cookie(void *ns, void *node, void *cookie); + static int udf_close(void *ns, void *node, void *cookie); + + static int udf_access(void *_ns, void *_node, int mode); + static int udf_read_link(void *_ns, void *_node, char *buffer, size_t *bufferSize); + + // directory stuff + static int udf_mkdir(void *ns, void *dir, const char *name, int perms); + static int udf_rmdir(void *ns, void *dir, const char *name); + static int udf_open_dir(void *_ns, void *_node, void **cookie); + static int udf_read_dir(void *_ns, void *_node, void *cookie, + long *num, struct dirent *dirent, size_t bufferSize); + static int udf_rewind_dir(void *_ns, void *_node, void *cookie); + static int udf_close_dir(void *_ns, void *_node, void *cookie); + static int udf_free_dir_cookie(void *_ns, void *_node, void *cookie); + + // attribute stuff + static int udf_open_attrdir(void *ns, void *node, void **cookie); + static int udf_close_attrdir(void *ns, void *node, void *cookie); + static int udf_free_attrdir_cookie(void *ns, void *node, void *cookie); + static int udf_rewind_attrdir(void *ns, void *node, void *cookie); + static int udf_read_attrdir(void *ns, void *node, void *cookie, long *num, + struct dirent *buf, size_t bufferSize); + static int udf_remove_attr(void *ns, void *node, const char *name); + static int udf_rename_attr(void *ns, void *node, const char *oldname, + const char *newname); + static int udf_stat_attr(void *ns, void *node, const char *name, + struct attr_info *buf); + static int udf_write_attr(void *ns, void *node, const char *name, int type, + const void *buf, size_t *len, off_t pos); + static int udf_read_attr(void *ns, void *node, const char *name, int type, + void *buf, size_t *len, off_t pos); + + // index stuff + static int udf_open_indexdir(void *ns, void **cookie); + static int udf_close_indexdir(void *ns, void *cookie); + static int udf_free_indexdir_cookie(void *ns, void *node, void *cookie); + static int udf_rewind_indexdir(void *ns, void *cookie); + static int udf_read_indexdir(void *ns, void *cookie, long *num,struct dirent *dirent, + size_t bufferSize); + static int udf_create_index(void *ns, const char *name, int type, int flags); + static int udf_remove_index(void *ns, const char *name); + static int udf_rename_index(void *ns, const char *oldname, const char *newname); + static int udf_stat_index(void *ns, const char *name, struct index_info *indexInfo); + + // query stuff + static int udf_open_query(void *ns, const char *query, ulong flags, + port_id port, long token, void **cookie); + static int udf_close_query(void *ns, void *cookie); + static int udf_free_query_cookie(void *ns, void *node, void *cookie); + static int udf_read_query(void *ns, void *cookie, long *num, + struct dirent *buf, size_t bufsize); + + // dano stuff (for Mr. Dörfler) + static int udf_wake_vnode(void *ns, void *node); + static int udf_suspend_vnode(void *ns, void *node); +}; // end extern "C" + +vnode_ops fs_entry = { + udf_read_vnode, // read vnode + udf_release_vnode, // write vnode + udf_remove_vnode, // remove vnode + NULL, // secure vnode (unused) + udf_walk, // walk + udf_access, // access + udf_create, // create + udf_mkdir, // mkdir + udf_symlink, // symlink + udf_link, // link + udf_rename, // rename + udf_unlink, // unlink + udf_rmdir, // rmdir + udf_read_link, // readlink + udf_open_dir, // opendir + udf_close_dir, // closedir + udf_free_dir_cookie, // free dir cookie + udf_rewind_dir, // rewinddir + udf_read_dir, // readdir + udf_open, // open file + udf_close, // close file + udf_free_cookie, // free cookie + udf_read, // read file + udf_write, // write file + NULL, // readv + NULL, // writev + udf_ioctl, // ioctl + udf_setflags, // setflags file + udf_read_stat, // read stat + udf_write_stat, // write stat + udf_fsync, // fsync + udf_initialize, // initialize + udf_mount, // mount + udf_unmount, // unmount + udf_sync, // sync + udf_read_fs_stat, // read fs stat + udf_write_fs_stat, // write fs stat + udf_select, // select + udf_deselect, // deselect + + NULL,//udf_open_indexdir, // open index dir + NULL,//udf_close_indexdir, // close index dir + NULL,//udf_free_indexdir_cookie, // free index dir cookie + NULL,//udf_rewind_indexdir, // rewind index dir + NULL,//udf_read_indexdir, // read index dir + NULL,//udf_create_index, // create index + NULL,//udf_remove_index, // remove index + NULL,//udf_rename_index, // rename index + NULL,//udf_stat_index, // stat index + + NULL,//udf_open_attrdir, // open attr dir + NULL,//udf_close_attrdir, // close attr dir + NULL,//udf_free_attrdir_cookie, // free attr dir cookie + NULL,//udf_rewind_attrdir, // rewind attr dir + NULL,//udf_read_attrdir, // read attr dir + NULL,//udf_write_attr, // write attr + NULL,//udf_read_attr, // read attr + NULL,//udf_remove_attr, // remove attr + NULL,//udf_rename_attr, // rename attr + NULL,//udf_stat_attr, // stat attr + + NULL,//udf_open_query, // open query + NULL,//udf_close_query, // close query + NULL,//udf_free_query_cookie, // free query cookie + NULL,//udf_read_query, // read query + + udf_wake_vnode, // dano compatibility + udf_suspend_vnode // dano compatibility +}; + +int32 api_version = B_CUR_FS_API_VERSION; + +//---------------------------------------------------------------------- +// General/volume functions +//---------------------------------------------------------------------- + +/*! \brief mount + + \todo I'm using the B_GET_GEOMETRY ioctl() to find out where the end of the + partition is. This won't work for handling multi-session semantics correctly. + To support them correctly in R5 I need either: + - A way to get the proper info (best) + - To ignore trying to find anchor volume descriptor pointers at + locations N-256 and N. (acceptable, perhaps, but not really correct) + Either way we should address this problem properly for OBOS::R1. + \todo Looks like B_GET_GEOMETRY doesn't work on non-device files (i.e. + disk images), so I need to use stat or something else for those + instances. +*/ +int +udf_mount(nspace_id nsid, const char *name, ulong flags, void *parms, + size_t parmsLength, void **volumeCookie, vnode_id *rootID) +{ + INITIALIZE_DEBUGGING_OUTPUT_FILE("/boot/home/Desktop/udf_debug.txt"); + DEBUG_INIT_ETC(NULL, ("name: `%s'", name)); + + status_t error = B_OK; + char *deviceName = (char*)name; + off_t deviceOffset = 0; + off_t deviceSize = 0; // in blocks + Udf::Volume *volume = NULL; + partition_info info; + device_geometry geometry; + + // Here we need to figure out the length of the device, and if we're + // attempting to open a multisession volume, we need to figure out the + // offset into the raw disk at which the volume begins, then open the + // the raw volume itself instead of the fake partition device the + // kernel gives us, since multisession UDF volumes are allowed to access + // the data in their own partition, as well as the data in any partitions + // that precede them physically on the disc. + int device = open(name, O_RDONLY); + error = device < B_OK ? device : B_OK; + if (!error) { + + // First try to treat the device like a special partition device. If that's + // what we have, then we can use the partition_info data to figure out the + // name of the raw device (which we'll open instead), the offset into the + // raw device at which the volume of interest will begin, and the total + // length from the beginning of the raw device that we're allowed to access. + // + // If that fails, then we try to treat the device as an actual raw device, + // and see if we can get the device size with B_GET_GEOMETRY syscall, since + // stat()ing a raw device appears to not work. + // + // Finally, if that also fails, we're probably stuck with trying to mount + // a regular file, so we just stat() it to get the device size. + // + // If that fails, you're just SOL. + + if (ioctl(device, B_GET_PARTITION_INFO, &info) == 0) { + PRINT(("partition_info:\n")); + PRINT((" offset: %Ld\n", info.offset)); + PRINT((" size: %Ld\n", info.size)); + PRINT((" logical_block_size: %ld\n", info.logical_block_size)); + PRINT((" session: %ld\n", info.session)); + PRINT((" partition: %ld\n", info.partition)); + PRINT((" device: `%s'\n", info.device)); + deviceName = info.device; + deviceOffset = info.offset / info.logical_block_size; + deviceSize = deviceOffset + info.size / info.logical_block_size; + } else if (ioctl(device, B_GET_GEOMETRY, &geometry) == 0) { + PRINT(("geometry_info:\n")); + PRINT((" sectors_per_track: %ld\n", geometry.sectors_per_track)); + PRINT((" cylinder_count: %ld\n", geometry.cylinder_count)); + PRINT((" head_count: %ld\n", geometry.head_count)); + deviceOffset = 0; + deviceSize = (off_t)geometry.sectors_per_track + * geometry.cylinder_count * geometry.head_count; + } else { + struct stat stat; + error = fstat(device, &stat) < 0 ? B_ERROR : B_OK; + if (!error) { + PRINT(("stat_info:\n")); + PRINT((" st_size: %Ld\n", stat.st_size)); + deviceOffset = 0; + deviceSize = stat.st_size / 2048; + } + } + // Close the device + close(device); + } + + // Create and mount the volume + if (!error) { + volume = new(nothrow) Udf::Volume(nsid); + error = volume ? B_OK : B_NO_MEMORY; + } + if (!error) { + error = volume->Mount(deviceName, deviceOffset, deviceSize, 2048, flags); + } + + if (!error) { + if (rootID) + *rootID = volume->RootIcb()->Id(); + if (volumeCookie) + *volumeCookie = volume; + } + + RETURN(error); +} + + +int +udf_unmount(void *ns) +{ + DEBUG_INIT(NULL); + Udf::Volume *volume = reinterpret_cast(ns); + delete volume; + RETURN(B_OK); +} + + +int +udf_read_fs_stat(void *ns, struct fs_info *info) +{ + DEBUG_INIT(NULL); + if (ns == NULL || info == NULL) + return B_BAD_VALUE; + + Udf::Volume *volume = reinterpret_cast(ns); + + // File system flags. + info->flags = B_FS_IS_PERSISTENT | B_FS_IS_READONLY; + + info->io_size = 65536; + // whatever is appropriate here? Just use the same value as BFS (and iso9660) for now + + info->block_size = volume->BlockSize(); + info->total_blocks = volume->Length(); + info->free_blocks = 0; + + // Volume name + sprintf(info->volume_name, "%s", volume->Name()); + + // File system name + strcpy(info->fsh_name, "udf"); + + RETURN(B_OK); +} + + +int +udf_write_fs_stat(void *ns, struct fs_info *info, long mask) +{ + DEBUG_INIT(NULL); + // FUNCTION_START(("mask: %ld\n",mask)); + RETURN(B_ERROR); +} + + +int +udf_initialize(const char *deviceName, void *parms, size_t parmsLength) +{ + DEBUG_INIT(NULL); + // FUNCTION_START(("deviceName: %s, parameter len: %ld\n", deviceName, parmsLength)); + RETURN(B_ERROR); +} + + +int +udf_sync(void *ns) +{ + DEBUG_INIT(NULL); + return B_OK; +} + + +int +udf_read_vnode(void *ns, vnode_id id, char reenter, void **node) +{ + DEBUG_INIT_ETC(NULL, ("id: %Ld, reenter: %s", id, (reenter ? "true" : "false"))); + + if (!ns) + RETURN(B_BAD_VALUE); + + Udf::Volume *volume = reinterpret_cast(ns); + + // Convert the given vnode id to an address, and create + // and return a corresponding Icb object for it. + Udf::Icb *icb = new(nothrow) Udf::Icb(volume, Udf::to_long_address(id, volume->BlockSize())); + status_t error = icb ? B_OK : B_NO_MEMORY; + if (!error) { + error = icb->InitCheck(); + if (!error) { + if (node) + *node = reinterpret_cast(icb); + } else { + delete icb; + } + } + + RETURN(error); +} + + +int +udf_release_vnode(void *ns, void *node, char reenter) +{ +// No debug-to-file in release_vnode; can cause a deadlock in +// rare circumstances. +#if !DEBUG_TO_FILE + DEBUG_INIT_ETC(NULL, ("node: %p", node)); +#endif + Udf::Icb *icb = reinterpret_cast(node); + delete icb; +#if !DEBUG_TO_FILE + RETURN(B_OK); +#else + return B_OK; +#endif +} + + +int +udf_remove_vnode(void *ns, void *node, char reenter) +{ + DEBUG_INIT(NULL); + RETURN(B_ERROR); +} + + +int +udf_wake_vnode(void *ns, void *node) +{ + DEBUG_INIT(NULL); + RETURN(B_ERROR); +} + + +int +udf_suspend_vnode(void *ns, void *node) +{ + DEBUG_INIT(NULL); + RETURN(B_ERROR); +} + + +int +udf_walk(void *ns, void *_dir, const char *filename, char **resolvedPath, vnode_id *vnodeId) +{ + DEBUG_INIT_ETC(NULL, ("dir: %p, filename = `%s'", _dir, filename)); + + if (!ns || !_dir || !filename || !vnodeId) + RETURN(B_BAD_VALUE); + + Udf::Volume *volume = reinterpret_cast(ns); + Udf::Icb *dir = reinterpret_cast(_dir); + Udf::Icb *node = NULL; + + status_t error = B_OK; + + if (strcmp(filename, ".") == 0) { + *vnodeId = dir->Id(); + error = get_vnode(volume->Id(), *vnodeId, reinterpret_cast(&node)) == B_OK ? B_OK : B_BAD_VALUE; + } else { + error = dir->Find(filename, vnodeId); + if (!error) { + Udf::Icb *icb; + error = get_vnode(volume->Id(), *vnodeId, reinterpret_cast(&icb)); + } + } + PRINT(("vnodeId: %Ld\n", *vnodeId)); + + + RETURN(error); +} + + +int +udf_ioctl(void *ns, void *node, void *cookie, int cmd, void *buffer, size_t bufferLength) +{ + DEBUG_INIT_ETC(NULL, ("node: %p, cmd: 0x%x, " + "buf: %p, len: %ld\n", node, cmd, buffer, bufferLength)); + // FUNCTION_START(("node: %p, cmd: %d, buf: %p, len: %ld\n", node, cmd, buffer, bufferLength)); + RETURN(B_ERROR); +} + + +int +udf_setflags(void *ns, void *node, void *cookie, int flags) +{ + DEBUG_INIT(NULL); + // FUNCTION_START(("node: %p, flags: %d", node, flags)); + RETURN(B_ERROR); +} + + +int +udf_select(void *ns, void *node, void *cookie, uint8 event, uint32 ref, selectsync *sync) +{ + DEBUG_INIT(NULL); + // FUNCTION_START(("event: %d, ref: %lu, sync: %p\n", event, ref, sync)); + RETURN(B_ERROR); +} + + +int +udf_deselect(void *ns, void *node, void *cookie, uint8 event, selectsync *sync) +{ + DEBUG_INIT(NULL); + RETURN(B_ERROR); +} + + +int +udf_fsync(void *_ns, void *_node) +{ + DEBUG_INIT(NULL); + RETURN(B_ERROR); +} + + +int +udf_read_stat(void *ns, void *node, struct stat *st) +{ + DEBUG_INIT(NULL); + + if (!ns || !node || !st) + RETURN(B_BAD_VALUE); + + Udf::Volume *volume = reinterpret_cast(ns); + Udf::Icb *icb = reinterpret_cast(node); + + st->st_dev = volume->Id(); + st->st_ino = icb->Id(); + st->st_nlink = icb->FileLinkCount(); + st->st_blksize = volume->BlockSize(); + + st->st_uid = icb->Uid(); + st->st_gid = icb->Gid(); + + st->st_mode = icb->Mode(); + PRINT(("mode = 0x%lx\n", uint32(icb->Mode()))); + st->st_size = icb->Length(); + + // File times. For now, treat the modification time as creation + // time as well, since true creation time is an optional extended + // attribute, and supporting EAs is going to be a PITA. ;-) + st->st_atime = icb->AccessTime(); + st->st_mtime = st->st_ctime = st->st_crtime = icb->ModificationTime(); + + PRINT(("stat->st_ino: %Ld\n", st->st_ino)); + + RETURN(B_OK); +} + + +int +udf_write_stat(void *ns, void *node, struct stat *stat, long mask) +{ + DEBUG_INIT(NULL); + RETURN(B_ERROR); +} + + +//---------------------------------------------------------------------- +// File functions +//---------------------------------------------------------------------- + + +int +udf_create(void *ns, void *dir, const char *name, int omode, int mode, + vnode_id *newID, void **newNode) +{ + DEBUG_INIT(NULL); + // FUNCTION_START(("name: `%s\', perms: %d, omode: %d\n", name, mode, omode)); + RETURN(B_ERROR); +} + + +int +udf_symlink(void *ns, void *dir, const char *name, const char *path) +{ + DEBUG_INIT(NULL); + RETURN(B_ERROR); +} + + +int +udf_link(void *ns, void *dir, const char *name, void *node) +{ + DEBUG_INIT(NULL); + // FUNCTION_START(("name = `%s\'\n", name)); + RETURN(B_ERROR); +} + + +int +udf_unlink(void *ns, void *dir, const char *name) +{ + DEBUG_INIT(NULL); + // FUNCTION_START(("name: `%s'\n",name)); + RETURN(B_ERROR); +} + + +int +udf_rename(void *ns, void *oldDir, const char *oldName, void *newDir, const char *newName) +{ + DEBUG_INIT(NULL); + // FUNCTION_START(("oldDir: %p, oldName: `%s', newDir: %p, newName: `%s'\n", oldDir, oldName, newDir, newName)); + RETURN(B_ERROR); +} + +int +udf_open(void *_ns, void *_node, int omode, void **_cookie) +{ + DEBUG_INIT(NULL); + RETURN(B_OK); +} + +int +udf_read(void *_ns, void *_node, void *_cookie, off_t pos, void *buffer, size_t *_length) +{ + DEBUG_INIT(NULL); + Udf::Icb *icb = reinterpret_cast(_node); + +// if (!inode->HasUserAccessableStream()) { +// *_length = 0; +// RETURN_ERROR(B_BAD_VALUE); +// } + + RETURN(icb->Read(pos, buffer, _length)); +} + + +int +udf_write(void *_ns, void *_node, void *_cookie, off_t pos, const void *buffer, size_t *_length) +{ + DEBUG_INIT(NULL); + RETURN(B_ERROR); +} + + +int +udf_close(void *_ns, void *_node, void *_cookie) +{ + DEBUG_INIT(NULL); + RETURN(B_OK); +} + + +int +udf_free_cookie(void *_ns, void *_node, void *_cookie) +{ + DEBUG_INIT(NULL); + RETURN(B_OK); +} + + +int +udf_access(void *_ns, void *_node, int accessMode) +{ + DEBUG_INIT(NULL); + RETURN(B_OK); +} + + +int +udf_read_link(void *_ns, void *_node, char *buffer, size_t *bufferSize) +{ + DEBUG_INIT(NULL); + RETURN(B_ERROR); +} + + +//---------------------------------------------------------------------- +// Directory functions +//---------------------------------------------------------------------- + + +int +udf_mkdir(void *ns, void *dir, const char *name, int mode) +{ + DEBUG_INIT(NULL); + // FUNCTION_START(("name: `%s', perms: %d\n", name, mode)); + RETURN(B_ERROR); +} + + +int +udf_rmdir(void *ns, void *dir, const char *name) +{ + DEBUG_INIT(NULL); + // FUNCTION_START(("name: `%s'\n", name)); + RETURN(B_ERROR); +} + + +int +udf_open_dir(void *ns, void *node, void **cookie) +{ + DEBUG_INIT_ETC(NULL, ("node: %p, cookie: %p", node, cookie)); + + if (!ns || !node || !cookie) + RETURN(B_BAD_VALUE); + + Udf::Icb *dir = reinterpret_cast(node); + + status_t error = B_OK; + + if (dir->IsDirectory()) { + Udf::DirectoryIterator *iterator = NULL; + error = dir->GetDirectoryIterator(&iterator); + if (!error) { + *cookie = reinterpret_cast(iterator); + } else { + PRINT(("Error getting directory iterator: 0x%lx, `%s'\n", error, strerror(error))); + } + } else { + PRINT(("Given icb is not a directory (type: %d)\n", dir->Type())); + error = B_BAD_VALUE; + } + + RETURN(error); +} + + +int +udf_read_dir(void *ns, void *node, void *cookie, long *num, + struct dirent *dirent, size_t bufferSize) +{ + DEBUG_INIT_ETC(NULL, + ("dir: %p, iterator: %p, bufferSize: %ld", node, cookie, bufferSize)); + + if (!ns || !node || !cookie || !num || bufferSize < sizeof(dirent)) + RETURN(B_BAD_VALUE); + + Udf::Volume *volume = reinterpret_cast(ns); + Udf::Icb *dir = reinterpret_cast(node); + Udf::DirectoryIterator *iterator = reinterpret_cast(cookie); + + if (dir != iterator->Parent()) { + PRINT(("Icb does not match parent Icb of given DirectoryIterator! (iterator->Parent = %p)\n", + iterator->Parent())); + return B_BAD_VALUE; + } + + uint32 nameLength = bufferSize - sizeof(dirent) + 1; + + status_t error = iterator->GetNextEntry(dirent->d_name, &nameLength, &(dirent->d_ino)); + if (!error) { + *num = 1; + dirent->d_dev = volume->Id(); + dirent->d_reclen = sizeof(dirent) + nameLength - 1; + } else { + *num = 0; + // Clear the error for end of directory + if (error == B_ENTRY_NOT_FOUND) + error = B_OK; + } + + RETURN(error); +} + + +int +udf_rewind_dir(void *ns, void *node, void *cookie) +{ + DEBUG_INIT_ETC(NULL, + ("dir: %p, iterator: %p", node, cookie)); + + if (!ns || !node || !cookie) + RETURN(B_BAD_VALUE); + + Udf::Icb *dir = reinterpret_cast(node); + Udf::DirectoryIterator *iterator = reinterpret_cast(cookie); + + if (dir != iterator->Parent()) { + PRINT(("Icb does not match parent Icb of given DirectoryIterator! (iterator->Parent = %p)\n", + iterator->Parent())); + return B_BAD_VALUE; + } + + iterator->Rewind(); + + RETURN(B_OK); +} + + +int +udf_close_dir(void *ns, void *node, void *cookie) +{ + DEBUG_INIT(NULL); + RETURN(B_ERROR); +} + + +int +udf_free_dir_cookie(void *ns, void *node, void *cookie) +{ + DEBUG_INIT(NULL); + delete reinterpret_cast(cookie); + RETURN(B_ERROR); +} + + +//---------------------------------------------------------------------- +// Attribute functions +//---------------------------------------------------------------------- + + +int +udf_open_attrdir(void *ns, void *node, void **cookie) +{ + DEBUG_INIT(NULL); + RETURN(B_ERROR); +} + + +int +udf_close_attrdir(void *ns, void *node, void *cookie) +{ + DEBUG_INIT(NULL); + RETURN(B_ERROR); +} + + +int +udf_free_attrdir_cookie(void *ns, void *node, void *_cookie) +{ + DEBUG_INIT(NULL); + RETURN(B_ERROR); +} + + +int +udf_rewind_attrdir(void *_ns, void *_node, void *_cookie) +{ + DEBUG_INIT(NULL); + RETURN(B_ERROR); +} + + +int +udf_read_attrdir(void *_ns, void *node, void *_cookie, long *num, struct dirent *dirent, size_t bufsize) +{ + DEBUG_INIT(NULL); + RETURN(B_ERROR); +} + + +int +udf_remove_attr(void *_ns, void *_node, const char *name) +{ + DEBUG_INIT(NULL); + // FUNCTION_START(("name: `%s'\n",name)); + RETURN(B_ERROR); +} + + +int +udf_rename_attr(void *ns, void *node, const char *oldname, const char *newname) +{ + DEBUG_INIT(NULL); + // FUNCTION_START(("name: `%s',to: `%s'\n", oldname, newname)); + RETURN(B_ERROR); +} + + +int +udf_stat_attr(void *ns, void *_node, const char *name, struct attr_info *attrInfo) +{ + DEBUG_INIT(NULL); + // FUNCTION_START(("name: `%s'\n",name)); + RETURN(B_ERROR); +} + + +int +udf_write_attr(void *_ns, void *_node, const char *name, int type, const void *buffer, + size_t *_length, off_t pos) +{ + DEBUG_INIT(NULL); + // FUNCTION_START(("name: `%s'\n",name)); + RETURN(B_ERROR); +} + + +int +udf_read_attr(void *_ns, void *_node, const char *name, int type, void *buffer, + size_t *_length, off_t pos) +{ + DEBUG_INIT(NULL); + RETURN(B_ERROR); +} + + +//---------------------------------------------------------------------- +// Index functions +//---------------------------------------------------------------------- + + +int +udf_open_indexdir(void *_ns, void **_cookie) +{ + DEBUG_INIT(NULL); + RETURN(B_ERROR); +} + + +int +udf_close_indexdir(void *_ns, void *_cookie) +{ + DEBUG_INIT(NULL); + RETURN(B_ERROR); +} + + +int +udf_free_indexdir_cookie(void *_ns, void *_node, void *_cookie) +{ + DEBUG_INIT(NULL); + RETURN(B_ERROR); +} + + +int +udf_rewind_indexdir(void *_ns, void *_cookie) +{ + DEBUG_INIT(NULL); + RETURN(B_ERROR); +} + + +int +udf_read_indexdir(void *_ns, void *_cookie, long *num, struct dirent *dirent, size_t bufferSize) +{ + DEBUG_INIT(NULL); + RETURN(B_ERROR); +} + + +int +udf_create_index(void *_ns, const char *name, int type, int flags) +{ + DEBUG_INIT(NULL); + // FUNCTION_START(("name: `%s', type: %d, flags: %d\n", name, type, flags)); + RETURN(B_ERROR); +} + + +int +udf_remove_index(void *_ns, const char *name) +{ + DEBUG_INIT(NULL); + RETURN(B_ERROR); +} + + +int +udf_rename_index(void *ns, const char *oldname, const char *newname) +{ + DEBUG_INIT(NULL); + // FUNCTION_START(("from: %s, to: %s\n", oldname, newname)); + RETURN(B_ERROR); +} + + +int +udf_stat_index(void *_ns, const char *name, struct index_info *indexInfo) +{ + DEBUG_INIT(NULL); + // FUNCTION_START(("name: %s\n",name)); + RETURN(B_ERROR); +} + +//---------------------------------------------------------------------- +// Query functions +//---------------------------------------------------------------------- + +int +udf_open_query(void *_ns, const char *queryString, ulong flags, port_id port, + long token, void **cookie) +{ + DEBUG_INIT(NULL); + RETURN(B_ERROR); +} + + +int +udf_close_query(void *ns, void *cookie) +{ + DEBUG_INIT(NULL); + RETURN(B_ERROR); +} + + +int +udf_free_query_cookie(void *ns, void *node, void *cookie) +{ + DEBUG_INIT(NULL); + RETURN(B_ERROR); +} + + +int +udf_read_query(void *ns, void *cookie, long *num, struct dirent *dirent, size_t bufferSize) +{ + DEBUG_INIT(NULL); + RETURN(B_ERROR); +} +