diff --git a/src/kernel/boot/loader/file_systems/Jamfile b/src/kernel/boot/loader/file_systems/Jamfile index 96832ce8b2..ef761373ac 100644 --- a/src/kernel/boot/loader/file_systems/Jamfile +++ b/src/kernel/boot/loader/file_systems/Jamfile @@ -1,3 +1,4 @@ SubDir OBOS_TOP src kernel boot loader file_systems ; SubInclude OBOS_TOP src kernel boot loader file_systems bfs ; +SubInclude OBOS_TOP src kernel boot loader file_systems amiga_ffs ; diff --git a/src/kernel/boot/loader/file_systems/amiga_ffs/Directory.cpp b/src/kernel/boot/loader/file_systems/amiga_ffs/Directory.cpp new file mode 100644 index 0000000000..b597dcaec0 --- /dev/null +++ b/src/kernel/boot/loader/file_systems/amiga_ffs/Directory.cpp @@ -0,0 +1,118 @@ +/* +** Copyright 2003, Axel Dörfler, axeld@pinc-software.de. All rights reserved. +** Distributed under the terms of the OpenBeOS License. +*/ + + +#include "Directory.h" +#include "Volume.h" +#include "File.h" + +#include +#include + +#include +#include +#include + + +namespace FFS { + +Directory::Directory(Volume &volume, int32 block) + : + fVolume(volume) +{ + void *data = malloc(volume.BlockSize()); + if (data == NULL) + return; + + if (read_pos(volume.Device(), block * volume.BlockSize(), data, volume.BlockSize()) == volume.BlockSize()) + fNode.SetTo(data, volume.BlockSize()); +} + + +Directory::Directory(Volume &volume, RootBlock &root) + : + fVolume(volume) +{ + fNode.SetTo(root.BlockData(), root.BlockSize()); +} + + +Directory::~Directory() +{ + free(fNode.BlockData()); +} + + +status_t +Directory::InitCheck() +{ + return fNode.ValidateCheckSum(); +} + + +status_t +Directory::Open(void **_cookie, int mode) +{ + *_cookie = (void *)new HashIterator(fVolume.Device(), fNode); + if (*_cookie == NULL) + return B_NO_MEMORY; + + return B_OK; +} + + +status_t +Directory::Close(void *cookie) +{ + delete (HashIterator *)cookie; + return B_OK; +} + + +Node * +Directory::Lookup(const char *name, bool traverseLinks) +{ + return NULL; +} + + +status_t +Directory::GetNextEntry(void *cookie, char *name, size_t size) +{ + HashIterator *iterator = (HashIterator *)cookie; + int32 block; + + NodeBlock *node = iterator->GetNext(block); + if (node == NULL) + return B_ENTRY_NOT_FOUND; + + return node->GetName(name, size); +} + + +status_t +Directory::GetNextNode(void *cookie, Node **_node) +{ + return B_ERROR; +} + + +status_t +Directory::Rewind(void *cookie) +{ + HashIterator *iterator = (HashIterator *)cookie; + iterator->Rewind(); + + return B_OK; +} + + +status_t +Directory::GetName(char *name, size_t size) const +{ + return fNode.GetName(name, size); +} + +} // namespace FFS diff --git a/src/kernel/boot/loader/file_systems/amiga_ffs/Directory.h b/src/kernel/boot/loader/file_systems/amiga_ffs/Directory.h new file mode 100644 index 0000000000..0476dbcd37 --- /dev/null +++ b/src/kernel/boot/loader/file_systems/amiga_ffs/Directory.h @@ -0,0 +1,45 @@ +/* +** Copyright 2003, Axel Dörfler, axeld@pinc-software.de. All rights reserved. +** Distributed under the terms of the OpenBeOS License. +*/ +#ifndef DIRECTORY_H +#define DIRECTORY_H + + +#include "amiga_ffs.h" + +#include + + +namespace FFS { + +class Volume; + +class Directory : public ::Directory { + public: + Directory(); + Directory(Volume &volume, RootBlock &root); + Directory(Volume &volume, int32 block); + virtual ~Directory(); + + status_t InitCheck(); + + virtual status_t Open(void **_cookie, int mode); + virtual status_t Close(void *cookie); + + virtual Node *Lookup(const char *name, bool traverseLinks); + + virtual status_t GetNextEntry(void *cookie, char *nameBuffer, size_t bufferSize); + virtual status_t GetNextNode(void *cookie, Node **_node); + virtual status_t Rewind(void *cookie); + + virtual status_t GetName(char *name, size_t size) const; + + private: + Volume &fVolume; + DirectoryBlock fNode; +}; + +} // namespace FFS + +#endif /* DIRECTORY_H */ diff --git a/src/kernel/boot/loader/file_systems/amiga_ffs/File.cpp b/src/kernel/boot/loader/file_systems/amiga_ffs/File.cpp new file mode 100644 index 0000000000..3513d59cf7 --- /dev/null +++ b/src/kernel/boot/loader/file_systems/amiga_ffs/File.cpp @@ -0,0 +1,71 @@ +/* +** Copyright 2003, Axel Dörfler, axeld@pinc-software.de. All rights reserved. +** Distributed under the terms of the OpenBeOS License. +*/ + + +#include "File.h" + +#include + +#include +#include + + +namespace FFS { + +File::File(Volume &volume, int32 block) +{ +} + + +File::~File() +{ +} + + +status_t +File::InitCheck() +{ + if (!fNode.IsFile()) + return B_BAD_TYPE; + + return fNode.ValidateCheckSum(); +} + + +ssize_t +File::ReadAt(void *cookie, off_t pos, void *buffer, size_t bufferSize) +{ + return B_ERROR; +} + + +ssize_t +File::WriteAt(void *cookie, off_t pos, const void *buffer, size_t bufferSize) +{ + return EROFS; +} + + +status_t +File::GetName(char *nameBuffer, size_t bufferSize) const +{ + return fNode.GetName(nameBuffer, bufferSize); +} + + +int32 +File::Type() const +{ + return S_IFREG; +} + + +off_t +File::Size() const +{ + return fNode.Size(); +} + +} // namespace FFS diff --git a/src/kernel/boot/loader/file_systems/amiga_ffs/File.h b/src/kernel/boot/loader/file_systems/amiga_ffs/File.h new file mode 100644 index 0000000000..ac0fabe303 --- /dev/null +++ b/src/kernel/boot/loader/file_systems/amiga_ffs/File.h @@ -0,0 +1,36 @@ +/* +** Copyright 2003, Axel Dörfler, axeld@pinc-software.de. All rights reserved. +** Distributed under the terms of the OpenBeOS License. +*/ +#ifndef FILE_H +#define FILE_H + + +#include + +#include "Volume.h" + + +namespace FFS { + +class File : public Node { + public: + File(Volume &volume, int32 block); + virtual ~File(); + + status_t InitCheck(); + + virtual ssize_t ReadAt(void *cookie, off_t pos, void *buffer, size_t bufferSize); + virtual ssize_t WriteAt(void *cookie, off_t pos, const void *buffer, size_t bufferSize); + + virtual status_t GetName(char *nameBuffer, size_t bufferSize) const; + virtual int32 Type() const; + virtual off_t Size() const; + + private: + FileBlock fNode; +}; + +} // namespace FFS + +#endif /* FILE_H */ diff --git a/src/kernel/boot/loader/file_systems/amiga_ffs/Jamfile b/src/kernel/boot/loader/file_systems/amiga_ffs/Jamfile new file mode 100644 index 0000000000..0986bb81f6 --- /dev/null +++ b/src/kernel/boot/loader/file_systems/amiga_ffs/Jamfile @@ -0,0 +1,14 @@ +SubDir OBOS_TOP src kernel boot loader file_systems amiga_ffs ; + +UsePrivateHeaders [ FDirName kernel boot platform $(OBOS_BOOT_PLATFORM) ] ; +UsePrivateHeaders [ FDirName kernel disk_device_manager ] ; +UsePrivateHeaders [ FDirName storage ] ; + +#SubDirHdrs $(OBOS_TOP) src add-ons kernel file_systems amiga_ffs ; + +KernelStaticLibrary boot_amiga_ffs : + amiga_ffs.cpp + Volume.cpp + Directory.cpp + File.cpp + ; diff --git a/src/kernel/boot/loader/file_systems/amiga_ffs/Volume.cpp b/src/kernel/boot/loader/file_systems/amiga_ffs/Volume.cpp new file mode 100644 index 0000000000..b505c38c6b --- /dev/null +++ b/src/kernel/boot/loader/file_systems/amiga_ffs/Volume.cpp @@ -0,0 +1,122 @@ +/* +** Copyright 2003, Axel Dörfler, axeld@pinc-software.de. All rights reserved. +** Distributed under the terms of the OpenBeOS License. +*/ + + +#include "Volume.h" +#include "Directory.h" + +#include +#include + +#include +#include +#include +#include +#include + + +using namespace FFS; + + +Volume::Volume(boot::Partition *partition) + : + fRoot(NULL) +{ + if ((fDevice = open_node(partition, O_RDONLY)) < B_OK) + return; + + char *buffer = (char *)malloc(4096); + if (buffer == NULL) + return; + + if (read_pos(fDevice, 0, buffer, sizeof(uint32)) < B_OK) { + free(buffer); + return; + } + + switch (B_BENDIAN_TO_HOST_INT32(*(uint32 *)buffer)) { + case DT_AMIGA_FFS: + case DT_AMIGA_FFS_INTL: + case DT_AMIGA_FFS_DCACHE: + break; + + case DT_AMIGA_OFS: + printf("The Amiga OFS is not yet supported.\n"); + default: + // unsupported file system + free(buffer); + return; + } + + int32 blockSize = partition->block_size; + if (get_root_block(fDevice, buffer, blockSize, partition->size) != B_OK) { + // try to get the root block at different sizes, if the + // block size was incorrectly passed from the partitioning + // system + for (int32 size = 512; size <= 4096; size <<= 1) { + if (get_root_block(fDevice, buffer, size, partition->size) == B_OK) { + blockSize = size; + break; + } else if (size >= 4096) { + puts("Could not find root block\n"); + free(buffer); + return; + } + } + } + + char *newBuffer = (char *)realloc(buffer, blockSize); + // if reallocation fails, we keep the old buffer + if (newBuffer != NULL) + buffer = newBuffer; + + fRootNode.SetTo(buffer, blockSize); + fRoot = new Directory(*this, fRootNode); + // fRoot will free the buffer for us upon destruction +} + + +Volume::~Volume() +{ + delete fRoot; + close(fDevice); +} + + +status_t +Volume::InitCheck() +{ + if (fRoot != NULL) + return fRootNode.ValidateCheckSum(); + + return B_ERROR; +} + + +// #pragma mark - + + +static status_t +amiga_ffs_get_file_system(boot::Partition *partition, ::Directory **_root) +{ + Volume *volume = new Volume(partition); + if (volume == NULL) + return B_NO_MEMORY; + + if (volume->InitCheck() < B_OK) { + delete volume; + return B_ERROR; + } + + *_root = volume->Root(); + return B_OK; +} + + +file_system_module_info gAmigaFFSFileSystemModule = { + kPartitionTypeAmigaFFS, + amiga_ffs_get_file_system +}; + diff --git a/src/kernel/boot/loader/file_systems/amiga_ffs/Volume.h b/src/kernel/boot/loader/file_systems/amiga_ffs/Volume.h new file mode 100644 index 0000000000..48cc573d99 --- /dev/null +++ b/src/kernel/boot/loader/file_systems/amiga_ffs/Volume.h @@ -0,0 +1,41 @@ +/* +** Copyright 2003, Axel Dörfler, axeld@pinc-software.de. All rights reserved. +** Distributed under the terms of the OpenBeOS License. +*/ +#ifndef VOLUME_H +#define VOLUME_H + + +#include "amiga_ffs.h" + +#include + +namespace boot { + class Partition; +} + + +namespace FFS { + +class Directory; + +class Volume { + public: + Volume(boot::Partition *partition); + ~Volume(); + + status_t InitCheck(); + + int Device() const { return fDevice; } + Directory *Root() { return fRoot; } + int32 BlockSize() const { return fRootNode.BlockSize(); } + + protected: + int fDevice; + RootBlock fRootNode; + Directory *fRoot; +}; + +} // namespace FFS + +#endif /* VOLUME_H */ diff --git a/src/kernel/boot/loader/file_systems/amiga_ffs/amiga_ffs.cpp b/src/kernel/boot/loader/file_systems/amiga_ffs/amiga_ffs.cpp new file mode 100644 index 0000000000..501ae8f8c5 --- /dev/null +++ b/src/kernel/boot/loader/file_systems/amiga_ffs/amiga_ffs.cpp @@ -0,0 +1,199 @@ +/* +** Copyright 2003, Axel Dörfler, axeld@pinc-software.de. All rights reserved. +** Distributed under the terms of the OpenBeOS License. +*/ + + +#include "amiga_ffs.h" + +#include +#include + +#include +#include +#include +#include +#include + + +using namespace FFS; + + +class BCPLString { + public: + uint8 Length() { return fLength; } + const char *String() { return (const char *)(&fLength + 1); } + int32 CopyTo(char *name, size_t size); + + private: + uint8 fLength; +}; + + +int32 +BCPLString::CopyTo(char *name, size_t size) +{ + int32 length = size - 1 > Length() ? Length() : size - 1; + + memcpy(name, String(), length); + name[length] = '\0'; + + return length; +} + + +// #pragma mark - + + +status_t +BaseBlock::GetNameBackOffset(int32 offset, char *name, size_t size) const +{ + BCPLString *string = (BCPLString *)&fData[fSize - offset]; + string->CopyTo(name, size); + + return B_OK; +} + + +status_t +BaseBlock::ValidateCheckSum() const +{ + if (fData == NULL) + return B_NO_INIT; + + int32 sum = 0; + for (int32 index = 0; index < fSize; index++) { + sum += Offset(index); + } + + return sum == 0 ? B_OK : B_BAD_DATA; +} + + +// #pragma mark - + + +int32 +DirectoryBlock::HashIndexFor(const char *name) +{ + return 0; +} + + +int32 +DirectoryBlock::HashValueAt(int32 index) const +{ + return index >= HashSize() ? -1 : (int32)B_BENDIAN_TO_HOST_INT32(HashTable()[index]); +} + + +int32 +DirectoryBlock::FirstHashValue(int32 &index) const +{ + index = -1; + return NextHashValue(index); +} + + +int32 +DirectoryBlock::NextHashValue(int32 &index) const +{ + index++; + + int32 value; + while ((value = HashValueAt(index)) == 0) { + if (++index >= HashSize()) + return -1; + } + + return value; +} + + +// #pragma mark - + + +HashIterator::HashIterator(int32 device, DirectoryBlock &directory) + : + fDirectory(directory), + fDevice(device), + fCurrent(0), + fBlock(-1) +{ + fData = (int32 *)malloc(directory.BlockSize()); + fNode.SetTo(directory.BlockData(), directory.BlockSize()); +} + + +HashIterator::~HashIterator() +{ + free(fData); +} + + +NodeBlock * +HashIterator::GetNext(int32 &block) +{ + if (fBlock == -1) { + // first entry + fBlock = fDirectory.FirstHashValue(fCurrent); + } else if (fBlock == 0) { + fBlock = fDirectory.NextHashValue(fCurrent); + } + + if (fBlock == -1) + return NULL; + + block = fBlock; + + if (read_pos(fDevice, fBlock * fNode.BlockSize(), fData, fNode.BlockSize()) < B_OK) + return NULL; + + fNode.SetTo(fData); + if (fNode.ValidateCheckSum() != B_OK) { + dprintf("block at %ld bad checksum.\n", fBlock); + return NULL; + } + + fBlock = fNode.HashChain(); + + return &fNode; +} + + +void +HashIterator::Rewind() +{ + fCurrent = 0; + fBlock = -1; +} + + +// #pragma mark - + + +status_t +FFS::get_root_block(int fDevice, char *buffer, int32 blockSize, off_t partitionSize) +{ + // calculate root block position (it depends on the block size) + + // ToDo: get the number of reserved blocks out of the disk_environment structure?? + // (from the amiga_rdb module) + int32 reservedBlocks = 2; + off_t offset = (((partitionSize / blockSize) - 1 - reservedBlocks) / 2) + reservedBlocks; + // ToDo: this calculation might be incorrect for certain cases. + + if (read_pos(fDevice, offset * blockSize, buffer, blockSize) < B_OK) + return B_ERROR; + + RootBlock root(buffer, blockSize); + if (root.ValidateCheckSum() < B_OK) + return B_BAD_DATA; + + //printf("primary = %ld, secondary = %ld\n", root.PrimaryType(), root.SecondaryType()); + if (!root.IsRootBlock()) + return B_BAD_TYPE; + + return B_OK; +} + diff --git a/src/kernel/boot/loader/file_systems/amiga_ffs/amiga_ffs.h b/src/kernel/boot/loader/file_systems/amiga_ffs/amiga_ffs.h new file mode 100644 index 0000000000..b5b7b798d0 --- /dev/null +++ b/src/kernel/boot/loader/file_systems/amiga_ffs/amiga_ffs.h @@ -0,0 +1,258 @@ +/* +** Copyright 2003, Axel Dörfler, axeld@pinc-software.de. All rights reserved. +** Distributed under the terms of the OpenBeOS License. +*/ +#ifndef AMIGA_FFS_H +#define AMIGA_FFS_H + + +#include +#include + + +namespace FFS { + +/** The base class for all FFS blocks */ + +class BaseBlock{ + public: + BaseBlock() : fData(NULL) {} + BaseBlock(void *data, int32 blockSize) { SetTo(data, blockSize); } + BaseBlock(int32 blockSize) { fSize = blockSize >> 2; } + + void SetTo(void *data, int32 blockSize) { fData = (int32 *)data; fSize = blockSize >> 2; } + void SetTo(void *data) { fData = (int32 *)data; } + + int32 *BlockData() const { return fData; } + int32 BlockSize() const { return fSize << 2; } + int32 LongWords() const { return fSize; } + + int32 PrimaryType() const { return Offset(0); } + int32 SecondaryType() const { return BackOffset(1); } + int32 CheckSum() const { return Offset(5); } + + inline bool IsRootBlock() const; + inline bool IsDirectory() const; + inline bool IsFile() const; + inline bool IsExtensionBlock() const; + inline bool IsDirectoryLink() const; + inline bool IsFileLink() const; + inline bool IsSymbolicLink() const; + + status_t ValidateCheckSum() const; + + protected: + int32 Offset(int16 i) const { return B_BENDIAN_TO_HOST_INT32(fData[i]); } + int32 BackOffset(int16 i) const { return B_BENDIAN_TO_HOST_INT32(fData[fSize - i]); } + + status_t GetNameBackOffset(int32 offset, char *name, size_t size) const; + + private: + int32 fSize; + int32 *fData; +}; + + +/** The base class for all blocks that represent files and directories + * (all blocks that are accessible to the user) + */ + +class NodeBlock : public BaseBlock { + public: + NodeBlock() {} + NodeBlock(int32 blockSize) : BaseBlock(blockSize) {} + NodeBlock(void *data, int32 blockSize) : BaseBlock(data, blockSize) {} + + int32 HeaderKey() const { return Offset(1); } + int32 Protection() const { return BackOffset(48); } + + int32 Days() const { return BackOffset(23); } + int32 Minute() const { return BackOffset(22); } + int32 Ticks() const { return BackOffset(21); } + + status_t GetName(char *name, size_t size) const { return GetNameBackOffset(20, name, size); } + + int32 LinkChain() const { return BackOffset(10); } + int32 HashChain() const { return BackOffset(4); } + + int32 Parent() const { return BackOffset(3); } +}; + + +/** A standard user directory block */ + +class DirectoryBlock : public NodeBlock { + public: + DirectoryBlock() : NodeBlock() {} + DirectoryBlock(int32 blockSize) : NodeBlock(blockSize) {} + DirectoryBlock(void *data, int32 blockSize) : NodeBlock(data, blockSize) {} + + int32 HashIndexFor(const char *name); + + int32 HashValueAt(int32 index) const; + int32 NextHashValue(int32 &index) const; + int32 FirstHashValue(int32 &index) const; + + protected: + int32 *HashTable() const { return BlockData() + 6; } + int32 HashSize() const { return LongWords() - 56; } +}; + + +/** The root block of the device and at the same time the root directory */ + +class RootBlock : public DirectoryBlock { + public: + RootBlock() : DirectoryBlock() {} + RootBlock(int32 blockSize) : DirectoryBlock(blockSize) {} + RootBlock(void *data, int32 blockSize) : DirectoryBlock(data, blockSize) {} + + int32 BitmapFlag() const { return BackOffset(50); } + int32 BitmapExtension() const { return BackOffset(24); } + + int32 VolumeDays() const { return BackOffset(10); } + int32 VolumeMinutes() const { return BackOffset(9); } + int32 VolumeTicks() const { return BackOffset(8); } + + int32 CreationDays() const { return BackOffset(7); } + int32 CreationMinutes() const { return BackOffset(6); } + int32 CreationTicks() const { return BackOffset(5); } +}; + + +/** A standard user file block */ + +class FileBlock : public NodeBlock { + public: + FileBlock() : NodeBlock() {} + FileBlock(int32 blockSize) : NodeBlock(blockSize) {} + FileBlock(void *data, int32 blockSize) : NodeBlock(data, blockSize) {} + + int32 BlockCount() const { return Offset(2); } + int32 FirstData() const { return Offset(4); } + int32 Size() const { return BackOffset(47); } + int32 Extension() const { return BackOffset(2); } +}; + +class ExtensionBlock : public BaseBlock { + public: + ExtensionBlock(int32 blockSize) : BaseBlock(blockSize) {} + + int32 HeaderKey() const { return Offset(1); } + int32 BlockCount() const { return Offset(2); } + + int32 Parent() const { return BackOffset(3); } + int32 NextExtension() const { return BackOffset(2); } +}; + +class HashIterator { + public: + HashIterator(int32 device, DirectoryBlock &node); + ~HashIterator(); + + NodeBlock *GetNext(int32 &block); + void Rewind(); + + private: + DirectoryBlock &fDirectory; + int32 fDevice; + int32 fCurrent; + int32 fBlock; + NodeBlock fNode; + int32 *fData; +}; + +enum primary_types { + PT_SHORT = 2, + PT_DATA = 8, + PT_LIST = 16, +}; + +enum secondary_types { + ST_ROOT = 1, + ST_DIRECTORY = 2, + ST_FILE = -3, + ST_DIRECTORY_LINK = 4, + ST_FILE_LINK = -4, + ST_SOFT_LINK = 3 +}; + +enum dos_types { + DT_AMIGA_OFS = 'DOS\0', + DT_AMIGA_FFS = 'DOS\1', + DT_AMIGA_FFS_INTL = 'DOS\2', + DT_AMIGA_FFS_DCACHE = 'DOS\3', +}; + +enum protection_flags { + FILE_IS_DELETABLE = 1, + FILE_IS_EXECUTABLE = 2, + FILE_IS_READABLE = 4, + FILE_IS_WRITABLE = 8, + FILE_IS_ARCHIVED = 16, + FILE_IS_PURE = 32, + FILE_IS_SCRIPT = 64, + FILE_IS_HOLD = 128, +}; + +enum name_lengths { + FFS_NAME_LENGTH = 30, + COMMENT_LENGTH = 79, +}; + +status_t get_root_block(int fDevice, char *buffer, int32 blockSize, off_t partitionSize); + +// inline methods + +inline bool +BaseBlock::IsRootBlock() const +{ + return PrimaryType() == PT_SHORT && SecondaryType() == ST_ROOT; +} + + +inline bool +BaseBlock::IsDirectory() const +{ + return PrimaryType() == PT_SHORT && SecondaryType() == ST_DIRECTORY; +} + + +inline bool +BaseBlock::IsFile() const +{ + return PrimaryType() == PT_SHORT && SecondaryType() == ST_FILE; +} + + +inline bool +BaseBlock::IsExtensionBlock() const +{ + return PrimaryType() == PT_LIST && SecondaryType() == ST_FILE; +} + + +inline bool +BaseBlock::IsDirectoryLink() const +{ + return PrimaryType() == PT_SHORT && SecondaryType() == ST_DIRECTORY_LINK; +} + + +inline bool +BaseBlock::IsFileLink() const +{ + return PrimaryType() == PT_SHORT && SecondaryType() == ST_FILE_LINK; +} + + +inline bool +BaseBlock::IsSymbolicLink() const +{ + return PrimaryType() == PT_SHORT && SecondaryType() == ST_SOFT_LINK; +} + +} // namespace FFS + +#endif /* AMIGA_FFS_H */ +