Implemented an Amiga FFS reader for the boot loader. Can only traverse

directories right now, reading from files is not yet implemented.


git-svn-id: file:///srv/svn/repos/haiku/trunk/current@4695 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Axel Dörfler 2003-09-15 22:10:10 +00:00
parent 86f2640947
commit 50d9710f0d
10 changed files with 905 additions and 0 deletions

View File

@ -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 ;

View File

@ -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 <StorageDefs.h>
#include <util/kernel_cpp.h>
#include <string.h>
#include <unistd.h>
#include <stdio.h>
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

View File

@ -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 <boot/vfs.h>
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 */

View File

@ -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 <util/kernel_cpp.h>
#include <sys/stat.h>
#include <unistd.h>
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

View File

@ -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 <boot/vfs.h>
#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 */

View File

@ -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
;

View File

@ -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 <boot/partitions.h>
#include <boot/platform.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
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
};

View File

@ -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 <SupportDefs.h>
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 */

View File

@ -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 <boot/partitions.h>
#include <boot/platform.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
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;
}

View File

@ -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 <SupportDefs.h>
#include <ByteOrder.h>
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 */