From 2cf4975b4b706bcfb741d06d0e8deb8a4783a3f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Revol?= Date: Wed, 15 Oct 2008 23:54:04 +0000 Subject: [PATCH] Add FAT32 support. This allows the bootloader to find a BFS image file (currently named BEOS\IMAGE.BE) and start booting for it, until the kernel tries to mount the boot partition. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@28156 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- headers/private/kernel/boot/partitions.h | 2 + src/system/boot/Jamfile | 1 + src/system/boot/loader/Jamfile | 1 + src/system/boot/loader/file_systems/Jamfile | 1 + .../loader/file_systems/fat/CachedBlock.cpp | 74 +++++ .../loader/file_systems/fat/CachedBlock.h | 41 +++ .../loader/file_systems/fat/Directory.cpp | 298 +++++++++++++++++ .../boot/loader/file_systems/fat/Directory.h | 51 +++ .../boot/loader/file_systems/fat/File.cpp | 110 +++++++ .../boot/loader/file_systems/fat/File.h | 43 +++ .../boot/loader/file_systems/fat/Jamfile | 24 ++ .../boot/loader/file_systems/fat/Stream.cpp | 268 ++++++++++++++++ .../boot/loader/file_systems/fat/Stream.h | 57 ++++ .../boot/loader/file_systems/fat/Volume.cpp | 299 ++++++++++++++++++ .../boot/loader/file_systems/fat/Volume.h | 85 +++++ .../boot/loader/file_systems/fat/fatfs.cpp | 49 +++ .../boot/loader/file_systems/fat/fatfs.h | 42 +++ 17 files changed, 1446 insertions(+) create mode 100644 src/system/boot/loader/file_systems/fat/CachedBlock.cpp create mode 100644 src/system/boot/loader/file_systems/fat/CachedBlock.h create mode 100644 src/system/boot/loader/file_systems/fat/Directory.cpp create mode 100644 src/system/boot/loader/file_systems/fat/Directory.h create mode 100644 src/system/boot/loader/file_systems/fat/File.cpp create mode 100644 src/system/boot/loader/file_systems/fat/File.h create mode 100644 src/system/boot/loader/file_systems/fat/Jamfile create mode 100644 src/system/boot/loader/file_systems/fat/Stream.cpp create mode 100644 src/system/boot/loader/file_systems/fat/Stream.h create mode 100644 src/system/boot/loader/file_systems/fat/Volume.cpp create mode 100644 src/system/boot/loader/file_systems/fat/Volume.h create mode 100644 src/system/boot/loader/file_systems/fat/fatfs.cpp create mode 100644 src/system/boot/loader/file_systems/fat/fatfs.h diff --git a/headers/private/kernel/boot/partitions.h b/headers/private/kernel/boot/partitions.h index 731ca42f27..f1a65358b0 100644 --- a/headers/private/kernel/boot/partitions.h +++ b/headers/private/kernel/boot/partitions.h @@ -93,6 +93,8 @@ struct file_system_module_info { }; extern file_system_module_info gBFSFileSystemModule; +extern file_system_module_info gFATFileSystemModule; +extern file_system_module_info gHFSPlusFileSystemModule; extern file_system_module_info gAmigaFFSFileSystemModule; extern file_system_module_info gTarFileSystemModule; diff --git a/src/system/boot/Jamfile b/src/system/boot/Jamfile index a50ce59cc1..380c4b7941 100644 --- a/src/system/boot/Jamfile +++ b/src/system/boot/Jamfile @@ -35,6 +35,7 @@ KernelLd boot_loader_$(TARGET_BOOT_PLATFORM) : boot_bfs.a boot_amiga_ffs.a boot_tarfs.a + boot_fatfs.a libz.a diff --git a/src/system/boot/loader/Jamfile b/src/system/boot/loader/Jamfile index 5f434d4900..7987966f54 100644 --- a/src/system/boot/loader/Jamfile +++ b/src/system/boot/loader/Jamfile @@ -20,6 +20,7 @@ UsePrivateHeaders shared storage ; BOOT_SUPPORT_PARTITION_INTEL BOOT_SUPPORT_FILE_SYSTEM_BFS + BOOT_SUPPORT_FILE_SYSTEM_FAT BOOT_SUPPORT_FILE_SYSTEM_AMIGA_FFS BOOT_SUPPORT_FILE_SYSTEM_TARFS diff --git a/src/system/boot/loader/file_systems/Jamfile b/src/system/boot/loader/file_systems/Jamfile index c0ef9333f3..49109c75ec 100644 --- a/src/system/boot/loader/file_systems/Jamfile +++ b/src/system/boot/loader/file_systems/Jamfile @@ -2,5 +2,6 @@ SubDir HAIKU_TOP src system boot loader file_systems ; SubInclude HAIKU_TOP src system boot loader file_systems amiga_ffs ; SubInclude HAIKU_TOP src system boot loader file_systems bfs ; +SubInclude HAIKU_TOP src system boot loader file_systems fat ; SubInclude HAIKU_TOP src system boot loader file_systems hfs_plus ; SubInclude HAIKU_TOP src system boot loader file_systems tarfs ; diff --git a/src/system/boot/loader/file_systems/fat/CachedBlock.cpp b/src/system/boot/loader/file_systems/fat/CachedBlock.cpp new file mode 100644 index 0000000000..44616c6eba --- /dev/null +++ b/src/system/boot/loader/file_systems/fat/CachedBlock.cpp @@ -0,0 +1,74 @@ +/* + * Copyright 2008, Haiku, Inc. + * Distributed under the terms of the MIT License. + * + * Authors: + * François Revol + */ + + +#include "CachedBlock.h" +#include "Stream.h" +#include "Directory.h" +#include "File.h" + +#include + +#include +#include +#include + + +using namespace FATFS; + + +CachedBlock::CachedBlock(Volume &volume) + : + fVolume(volume), + fBlockNumber(-1LL), + fBlock(NULL) +{ +} + + +CachedBlock::CachedBlock(Volume &volume, off_t block) + : + fVolume(volume), + fBlockNumber(-1LL), + fBlock(NULL) +{ + SetTo(block); +} + + +CachedBlock::~CachedBlock() +{ + free(fBlock); +} + + +inline void +CachedBlock::Unset() +{ + fBlockNumber = -1; +} + + +inline uint8 * +CachedBlock::SetTo(off_t block) +{ + if (block == fBlockNumber) + return fBlock; + if (fBlock == NULL) { + fBlock = (uint8 *)malloc(BlockSize()); + if (fBlock == NULL) + return NULL; + } + + fBlockNumber = block; + if (read_pos(fVolume.Device(), block << BlockShift(), fBlock, BlockSize()) < (ssize_t)BlockSize()) + return NULL; + + return fBlock; +} + diff --git a/src/system/boot/loader/file_systems/fat/CachedBlock.h b/src/system/boot/loader/file_systems/fat/CachedBlock.h new file mode 100644 index 0000000000..510e644e1d --- /dev/null +++ b/src/system/boot/loader/file_systems/fat/CachedBlock.h @@ -0,0 +1,41 @@ +/* + * Copyright 2008, Haiku, Inc. + * Distributed under the terms of the MIT License. + * + * Authors: + * François Revol + */ +#ifndef CACHED_BLOCK_H +#define CACHED_BLOCK_H + +#include +#include + +#include "Volume.h" + +namespace FATFS { + +class CachedBlock { + public: + CachedBlock(Volume &volume); + CachedBlock(Volume &volume, off_t block); + ~CachedBlock(); + + uint8 *SetTo(off_t block); + + void Unset(); + + uint8 *Block() const { return fBlock; } + off_t BlockNumber() const { return fBlockNumber; } + uint32 BlockSize() const { return fVolume.BlockSize(); } + uint32 BlockShift() const { return fVolume.BlockShift(); } + + private: + Volume &fVolume; + off_t fBlockNumber; + uint8 *fBlock; +}; + +} // namespace FATFS + +#endif /* CACHED_BLOCK_H */ diff --git a/src/system/boot/loader/file_systems/fat/Directory.cpp b/src/system/boot/loader/file_systems/fat/Directory.cpp new file mode 100644 index 0000000000..55cf45d45b --- /dev/null +++ b/src/system/boot/loader/file_systems/fat/Directory.cpp @@ -0,0 +1,298 @@ +/* +** 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 +#include + +#define TRACE(x) dprintf x + +namespace FATFS { + +struct dir_entry { + void *Buffer() const { return (void *)fName; }; + const char *BaseName() const { return fName; }; + const char *Extension() const { return fExt; }; + uint8 Flags() const { return fFlags; }; + uint32 Cluster(int32 fatBits) const; + uint32 Size() const { return B_LENDIAN_TO_HOST_INT32(fSize); }; + bool IsFile() const; + bool IsDir() const; + char fName[8]; + char fExt[3]; + uint8 fFlags; + uint8 fReserved1; + uint8 fCreateTime10ms; + uint16 fCreateTime; + uint16 fCreateDate; + uint16 fAccessDate; + uint16 fClusterMSB; + uint16 fModifiedTime; + uint16 fModifiedDate; + uint16 fClusterLSB; + uint32 fSize; +} _PACKED; + + +uint32 +dir_entry::Cluster(int32 fatBits) const +{ + uint32 c = B_LENDIAN_TO_HOST_INT16(fClusterLSB); + if (fatBits == 32) + c += ((uint32)B_LENDIAN_TO_HOST_INT16(fClusterMSB) << 16); + return c; +} + +bool +dir_entry::IsFile() const +{ + return ((Flags() & (FAT_VOLUME|FAT_SUBDIR)) == 0); +} + + +bool +dir_entry::IsDir() const +{ + return ((Flags() & (FAT_VOLUME|FAT_SUBDIR)) == FAT_SUBDIR); +} + + +struct dir_cookie { + int32 index; + struct dir_entry entry; + off_t Offset() const { return index * sizeof(struct dir_entry); } +}; + + +Directory::Directory(Volume &volume, uint32 cluster, const char *name) + : + fVolume(volume), + fStream(volume, cluster, UINT32_MAX, name) +{ + TRACE(("FASFS::Directory::(, %lu, %s)\n", cluster, name)); +} + + +Directory::~Directory() +{ + TRACE(("FASFS::Directory::~()\n")); +} + + +status_t +Directory::InitCheck() +{ + status_t err; + err = fStream.InitCheck(); + if (err < B_OK) + return err; + return B_OK; +} + + +status_t +Directory::Open(void **_cookie, int mode) +{ + TRACE(("FASFS::Directory::%s(, %d)\n", __FUNCTION__, mode)); + _inherited::Open(_cookie, mode); + + struct dir_cookie *c = new struct dir_cookie; + if (c == NULL) + return B_NO_MEMORY; + + c->index = -1; + + *_cookie = (void *)c; + return B_OK; +} + + +status_t +Directory::Close(void *cookie) +{ + TRACE(("FASFS::Directory::%s()\n", __FUNCTION__)); + _inherited::Close(cookie); + + delete (struct dir_cookie *)cookie; + return B_OK; +} + + +Node * +Directory::Lookup(const char *name, bool traverseLinks) +{ + TRACE(("FASFS::Directory::%s('%s', %d)\n", __FUNCTION__, name, traverseLinks)); + if (!strcmp(name, ".")) { + Acquire(); + return this; + } + char *dot = strchr(name, '.'); + int baselen = strlen(name); + if (baselen > FATFS_BASENAME_LENGTH) // !? + return NULL; + char *ext = NULL; + int extlen = 0; + if (dot) { + baselen = dot - name; + ext = dot + 1; + if (strlen(ext) > FATFS_EXTNAME_LENGTH) // !? + return NULL; + extlen = strlen(ext); + } + + status_t err; + struct dir_cookie cookie; + struct dir_cookie *c = &cookie; + c->index = -1; + + do { + err = GetNextEntry(c); + if (err < B_OK) + return NULL; + TRACE(("FASFS::Directory::%s: %s <> '%8.8s.%3.3s'\n", __FUNCTION__, + name, c->entry.BaseName(), c->entry.Extension())); + int i; + for (i = 0; i < FATFS_BASENAME_LENGTH; i++) + if (c->entry.BaseName()[i] == ' ') + break; + int nlen = MIN(i,FATFS_BASENAME_LENGTH); + for (i = 0; i < FATFS_EXTNAME_LENGTH; i++) + if (c->entry.Extension()[i] == ' ') + break; + int elen = MIN(i,FATFS_EXTNAME_LENGTH); + if (nlen != baselen) + continue; + if (elen != extlen) + continue; + if (strncasecmp(name, c->entry.BaseName(), nlen)) + continue; + if (strncasecmp(ext, c->entry.Extension(), elen)) + continue; + TRACE(("GOT IT!\n")); + break; + } while (true); + + if (c->entry.IsFile()) { + TRACE(("IS FILE\n")); + return new File(fVolume, c->entry.Cluster(fVolume.FatBits()), + c->entry.Size(), name); + } + if (c->entry.IsDir()) { + TRACE(("IS DIR\n")); + return new Directory(fVolume, c->entry.Cluster(fVolume.FatBits()), + name); + } + return NULL; +} + + +status_t +Directory::GetNextEntry(void *cookie, char *name, size_t size) +{ + TRACE(("FASFS::Directory::%s()\n", __FUNCTION__)); + struct dir_cookie *c = (struct dir_cookie *)cookie; + status_t err; + + err = GetNextEntry(cookie); + if (err < B_OK) + return err; + + strlcpy(name, c->entry.fName, MIN(size, FATFS_BASENAME_LENGTH)); + strlcpy(name, ".", size); + strlcpy(name, c->entry.fExt, MIN(size, FATFS_EXTNAME_LENGTH)); + return B_OK; +} + + +status_t +Directory::GetNextNode(void *cookie, Node **_node) +{ + return B_ERROR; +} + + +status_t +Directory::Rewind(void *cookie) +{ + TRACE(("FASFS::Directory::%s()\n", __FUNCTION__)); + struct dir_cookie *c = (struct dir_cookie *)cookie; + c->index = -1; + + return B_OK; +} + + +bool +Directory::IsEmpty() +{ + TRACE(("FASFS::Directory::%s()\n", __FUNCTION__)); + struct dir_cookie cookie; + struct dir_cookie *c = &cookie; + c->index = -1; + if (GetNextEntry(c) == B_OK) + return false; + return true; +} + + +status_t +Directory::GetName(char *name, size_t size) const +{ + TRACE(("FASFS::Directory::%s()\n", __FUNCTION__)); + if (this == fVolume.Root()) + return fVolume.GetName(name, size); + return fStream.GetName(name, size); +} + + +ino_t +Directory::Inode() const +{ + TRACE(("FASFS::Directory::%s()\n", __FUNCTION__)); + return fStream.FirstCluster() << 16; +} + +status_t +Directory::GetNextEntry(void *cookie, uint8 mask, uint8 match = 0) +{ + TRACE(("FASFS::Directory::%s(, %02x, %02x)\n", __FUNCTION__, mask, match)); + struct dir_cookie *c = (struct dir_cookie *)cookie; + + do { + c->index++; + size_t len = sizeof(c->entry); + if (fStream.ReadAt(c->Offset(), (uint8 *)&c->entry, &len) < B_OK) + return B_ENTRY_NOT_FOUND; + TRACE(("FASFS::Directory::%s: got one entry\n", __FUNCTION__)); + if ((uint8)c->entry.fName[0] == 0x00) // last one + return B_ENTRY_NOT_FOUND; + if ((uint8)c->entry.fName[0] == 0xe5) // deleted + continue; + if (c->entry.Flags() == 0x0f) // LFN entry + continue; + if (c->entry.Flags() & (FAT_VOLUME|FAT_SUBDIR) == FAT_VOLUME) { + // TODO handle Volume name (set fVolume's name) + continue; + } + TRACE(("FASFS::Directory::%s: checking '%8.8s.%3.3s', %02x\n", __FUNCTION__, + c->entry.BaseName(), c->entry.Extension(), c->entry.Flags())); + if ((c->entry.Flags() & mask) == match) + break; + } while (true); + TRACE(("FATFS::Directory::%s: '%8.8s.%3.3s'\n", __FUNCTION__, + c->entry.BaseName(), c->entry.Extension())); + return B_OK; +} + +} // namespace FATFS diff --git a/src/system/boot/loader/file_systems/fat/Directory.h b/src/system/boot/loader/file_systems/fat/Directory.h new file mode 100644 index 0000000000..ea5fb3b87f --- /dev/null +++ b/src/system/boot/loader/file_systems/fat/Directory.h @@ -0,0 +1,51 @@ +/* +** 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 "fatfs.h" +#include "Stream.h" + +#include + + +namespace FATFS { + +class Volume; + +class Directory : public ::Directory { + public: + Directory(); + Directory(Volume &volume, uint32 cluster, const char *name); + 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 bool IsEmpty(); + + virtual status_t GetName(char *name, size_t size) const; + virtual ino_t Inode() const; + + private: + status_t GetNextEntry(void *cookie, + uint8 mask = FAT_VOLUME, uint8 match = 0); + Volume &fVolume; + Stream fStream; + + typedef ::Directory _inherited; +}; + +} // namespace FATFS + +#endif /* DIRECTORY_H */ diff --git a/src/system/boot/loader/file_systems/fat/File.cpp b/src/system/boot/loader/file_systems/fat/File.cpp new file mode 100644 index 0000000000..1d585d2475 --- /dev/null +++ b/src/system/boot/loader/file_systems/fat/File.cpp @@ -0,0 +1,110 @@ +/* +** 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 + + +#define TRACE(x) dprintf x + + +namespace FATFS { + + +File::File(Volume &volume, uint32 cluster, off_t size, const char *name) + : + fVolume(volume), + fStream(volume, cluster, size, name) +{ + TRACE(("FATFS::File::()\n")); +} + + +File::~File() +{ + TRACE(("FATFS::File::~()\n")); +} + + +status_t +File::InitCheck() +{ + if (!fStream.InitCheck() < B_OK) + return fStream.InitCheck(); + + return B_OK; +} + + +status_t +File::Open(void **_cookie, int mode) +{ + TRACE(("FATFS::File::%s(, %d)\n", __FUNCTION__, mode)); + if (fStream.InitCheck() < B_OK) + return fStream.InitCheck(); + + return B_OK; +} + + +status_t +File::Close(void *cookie) +{ + return B_OK; +} + + +ssize_t +File::ReadAt(void *cookie, off_t pos, void *buffer, size_t bufferSize) +{ + TRACE(("FATFS::File::%s(, %Ld,, %d)\n", __FUNCTION__, pos, bufferSize)); + status_t err; + err = fStream.ReadAt(pos, (uint8 *)buffer, &bufferSize); + if (err < B_OK) + return err; + return bufferSize; +} + + +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 fStream.GetName(nameBuffer, bufferSize); +} + + +int32 +File::Type() const +{ + return S_IFREG; +} + + +off_t +File::Size() const +{ + return fStream.Size(); +} + + +ino_t +File::Inode() const +{ + return fStream.FirstCluster() << 16; +} + +} // namespace FATFS diff --git a/src/system/boot/loader/file_systems/fat/File.h b/src/system/boot/loader/file_systems/fat/File.h new file mode 100644 index 0000000000..61cb92498d --- /dev/null +++ b/src/system/boot/loader/file_systems/fat/File.h @@ -0,0 +1,43 @@ +/* +** 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 "Stream.h" +#include "Volume.h" + + +namespace FATFS { + +class File : public Node { + public: + File(Volume &volume, uint32 cluster, off_t size, const char *name); + virtual ~File(); + + status_t InitCheck(); + + virtual status_t Open(void **_cookie, int mode); + virtual status_t Close(void *cookie); + + 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; + virtual ino_t Inode() const; + + private: + Volume &fVolume; + //FileBlock fNode; + Stream fStream; +}; + +} // namespace FATFS + +#endif /* FILE_H */ diff --git a/src/system/boot/loader/file_systems/fat/Jamfile b/src/system/boot/loader/file_systems/fat/Jamfile new file mode 100644 index 0000000000..69aee03778 --- /dev/null +++ b/src/system/boot/loader/file_systems/fat/Jamfile @@ -0,0 +1,24 @@ +SubDir HAIKU_TOP src system boot loader file_systems fat ; + +#UsePrivateHeaders [ FDirName kernel boot platform $(TARGET_BOOT_PLATFORM) ] ; +#UsePrivateHeaders [ FDirName kernel disk_device_manager ] ; +#UsePrivateHeaders [ FDirName storage ] ; +UsePrivateKernelHeaders ; +UsePrivateHeaders shared storage ; + +#SubDirHdrs $(HAIKU_TOP) src add-ons kernel file_systems fat ; + +local defines = [ FDefines _BOOT_MODE ] ; + +SubDirCcFlags $(defines) ; +SubDirC++Flags -fno-rtti $(defines) ; + +KernelStaticLibrary boot_fatfs : + fatfs.cpp + Volume.cpp + CachedBlock.cpp + Directory.cpp + File.cpp + Stream.cpp + : -fno-pic + ; diff --git a/src/system/boot/loader/file_systems/fat/Stream.cpp b/src/system/boot/loader/file_systems/fat/Stream.cpp new file mode 100644 index 0000000000..c2212fcfa2 --- /dev/null +++ b/src/system/boot/loader/file_systems/fat/Stream.cpp @@ -0,0 +1,268 @@ +/* + * Copyright 2008, Haiku, Inc. + * Distributed under the terms of the MIT License. + * + * Authors: + * François Revol + */ + + +#include "Stream.h" +#include "CachedBlock.h" +#include "Directory.h" +#include "File.h" + +#include + +#include +#include +#include +#include + +#define TRACE(x) dprintf x + + +using namespace FATFS; + + +Stream::Stream(Volume &volume, uint32 chain, off_t size, const char *name) + : + fVolume(volume), + fFirstCluster(chain), + //fClusters(NULL), + fClusterMapCacheLast(0), + fSize(size) +{ + TRACE(("FATFS::Stream::(, %d, %Ld, %s)\n", chain, size, name)); + fName[FATFS_NAME_LENGTH] = '\0'; + strlcpy(fName, name, FATFS_NAME_LENGTH+1); + fClusterCount = (fSize + fVolume.ClusterSize() - 1) / fVolume.ClusterSize(); + if (size == UINT32_MAX) + fClusterCount = 10; // ? + for (int i = 0; i < CLUSTER_MAP_CACHE_SIZE; i++) { + fClusterMapCache[i].block = -1; + fClusterMapCache[i].cluster = fVolume.InvalidClusterID(); + } +} + + +Stream::~Stream() +{ + TRACE(("FATFS::Stream::~()\n")); +} + + +status_t +Stream::InitCheck() +{ + if (fSize && !fVolume.IsValidCluster(fFirstCluster)) + return B_BAD_VALUE; + return B_OK; +} + + +status_t +Stream::GetName(char *nameBuffer, size_t bufferSize) const +{ + return strlcpy(nameBuffer, fName, bufferSize); +} + + +status_t +Stream::FindBlock(off_t pos, off_t &block, off_t &offset) +{ + //TRACE(("FATFS::Stream::%s(%Ld,,)\n", __FUNCTION__, pos)); + uint32 index = (uint32)(pos / fVolume.ClusterSize()); + uint32 cluster; + if (pos >= fSize) + return B_BAD_VALUE; + if (index >= fClusterCount) + return B_BAD_VALUE; + + bool found = false; + int i; + for (i = 0; i < CLUSTER_MAP_CACHE_SIZE; i++) { + if (fClusterMapCache[i].block == index) { + cluster = fClusterMapCache[i].cluster; + found = true; + break; + } + } + if (!found) { +#if 1 + uint32 count = (fSize + fVolume.ClusterSize() - 1) / fVolume.ClusterSize(); + cluster = fFirstCluster; + if (fSize == UINT32_MAX) // it's a directory, try a large enough value + count = 10; + for (i = 0; i < index && fVolume.IsValidCluster(cluster); i++) { + if (fVolume.IsLastCluster(cluster)) + break; + //TRACE(("FATFS::Stream::%s: [%d] = %d\n", __FUNCTION__, i, cluster)); + cluster = fVolume.NextCluster(cluster); + //TRACE(("FATFS::Stream::%s: %04lx ?\n", __FUNCTION__, cluster)); + } +#endif +#if 0 + cluster = fVolume.NextCluster(cluster, index); +#endif + } + if (!fVolume.IsValidCluster(cluster)) + return ENOENT; + fClusterMapCache[fClusterMapCacheLast].block = index; + fClusterMapCache[fClusterMapCacheLast].cluster = cluster; + fClusterMapCacheLast++; + fClusterMapCacheLast %= CLUSTER_MAP_CACHE_SIZE; + + //cluster = fClusters[index]; + // convert to position + offset = fVolume.ToOffset(cluster); + offset += (pos %= fVolume.ClusterSize()); + // convert to block + offset + block = fVolume.ToBlock(offset); + offset %= fVolume.BlockSize(); + //TRACE(("FATFS::Stream::FindBlock: %Ld:%Ld\n", block, offset)); + return B_OK; +} + + +status_t +Stream::ReadAt(off_t pos, uint8 *buffer, size_t *_length) +{ + TRACE(("FATFS::Stream::%s(%Ld, )\n", __FUNCTION__, pos)); + status_t err; + // set/check boundaries for pos/length + + if (pos < 0) + return B_BAD_VALUE; + if (pos >= fSize) { + *_length = 0; + return B_NO_ERROR; + } + +#if 0 + // lazily build the cluster list + if (!fClusters) { + err = BuildClusterList(); + if (err < B_OK) + return err; + } +#endif + + size_t length = *_length; + + if (pos + length > fSize) + length = fSize - pos; + + off_t num; // block number + off_t offset; + if (FindBlock(pos, num, offset) < B_OK) { + *_length = 0; + return B_BAD_VALUE; + } + + uint32 bytesRead = 0; + uint32 blockSize = fVolume.BlockSize(); + uint32 blockShift = fVolume.BlockShift(); + uint8 *block; + + // the first block_run we read could not be aligned to the block_size boundary + // (read partial block at the beginning) + + // pos % block_size == (pos - offset) % block_size, offset % block_size == 0 + if (pos % blockSize != 0) { + CachedBlock cached(fVolume, num); + if ((block = cached.Block()) == NULL) { + *_length = 0; + return B_BAD_VALUE; + } + + bytesRead = blockSize - (pos % blockSize); + if (length < bytesRead) + bytesRead = length; + + memcpy(buffer, block + (pos % blockSize), bytesRead); + pos += bytesRead; + + length -= bytesRead; + if (length == 0) { + *_length = bytesRead; + return B_OK; + } + + if (FindBlock(pos, num, offset) < B_OK) { + *_length = bytesRead; + return B_BAD_VALUE; + } + } + + // the first block_run is already filled in at this point + // read the following complete blocks using cached_read(), + // the last partial block is read using the generic Cache class + + bool partial = false; + + while (length > 0) { + // offset is the offset to the current pos in the block_run + + if (length < blockSize) { + CachedBlock cached(fVolume, num); + if ((block = cached.Block()) == NULL) { + *_length = bytesRead; + return B_BAD_VALUE; + } + memcpy(buffer + bytesRead, block, length); + bytesRead += length; + partial = true; + break; + } + + if (read_pos(fVolume.Device(), fVolume.ToOffset(num), + buffer + bytesRead, fVolume.BlockSize()) < B_OK) { + *_length = bytesRead; + return B_BAD_VALUE; + } + + int32 bytes = fVolume.BlockSize(); + length -= bytes; + bytesRead += bytes; + if (length == 0) + break; + + pos += bytes; + + if (FindBlock(pos, num, offset) < B_OK) { + *_length = bytesRead; + return B_BAD_VALUE; + } + } + + *_length = bytesRead; + return B_NO_ERROR; +} + +status_t +Stream::BuildClusterList() +{ +#if 0 + TRACE(("FATFS::Stream::%s()\n", __FUNCTION__)); + uint32 count = (fSize + fVolume.ClusterSize() - 1) / fVolume.ClusterSize(); + uint32 c = fFirstCluster; + int i; + + if (fSize == UINT32_MAX) // it's a directory, try a large enough value + count = 10; + //fClusters = (uint32 *)malloc(count * sizeof(uint32)); + for (i = 0; i < count && fVolume.IsValidCluster(c); i++) { + if (fVolume.IsLastCluster(c)) + break; + TRACE(("FATFS::Stream::%s: [%d] = %d\n", __FUNCTION__, i, c)); + fClusters[i] = c; + c = fVolume.NextCluster(c); + TRACE(("FATFS::Stream::%s: %04lx ?\n", __FUNCTION__, c)); + // XXX: try to realloc() for dirs maybe ? + } + fClusterCount = i; + TRACE(("FATFS::Stream::%s: %d clusters in chain\n", __FUNCTION__, i)); +#endif + return B_OK; +} diff --git a/src/system/boot/loader/file_systems/fat/Stream.h b/src/system/boot/loader/file_systems/fat/Stream.h new file mode 100644 index 0000000000..8796a25ca6 --- /dev/null +++ b/src/system/boot/loader/file_systems/fat/Stream.h @@ -0,0 +1,57 @@ +/* + * Copyright 2008, Haiku, Inc. + * Distributed under the terms of the MIT License. + * + * Authors: + * François Revol + */ +#ifndef STREAM_H +#define STREAM_H + +#include "fatfs.h" +#include "Volume.h" + +#include + +#define CLUSTER_MAP_CACHE_SIZE 50 + +class Node; + + +namespace FATFS { + +class Stream { + public: + Stream(Volume &volume, uint32 chain, off_t size, const char *name); + ~Stream(); + + status_t InitCheck(); + Volume &GetVolume() const { return fVolume; } + + status_t GetName(char *nameBuffer, size_t bufferSize) const; + off_t Size() const { return fSize; } + uint32 FirstCluster() const { return fFirstCluster; } + + status_t ReadAt(off_t pos, uint8 *buffer, size_t *length); + + private: + status_t BuildClusterList(); + status_t FindBlock(off_t pos, off_t &block, off_t &offset); + Volume &fVolume; + uint32 fFirstCluster; + uint32 fClusterCount; + //uint32 *fClusters; // [fClusterCount] + struct { + off_t block; + uint32 cluster; + } fClusterMapCache[CLUSTER_MAP_CACHE_SIZE]; + int fClusterMapCacheLast; + off_t fSize; + // we cache the name here, since FAT doesn't have inodes, + // let alone names inside. + char fName[FATFS_NAME_LENGTH+1]; +}; + +} // namespace FATFS + +#endif /* STREAM_H */ diff --git a/src/system/boot/loader/file_systems/fat/Volume.cpp b/src/system/boot/loader/file_systems/fat/Volume.cpp new file mode 100644 index 0000000000..5212d45bb4 --- /dev/null +++ b/src/system/boot/loader/file_systems/fat/Volume.cpp @@ -0,0 +1,299 @@ +/* +** 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 "CachedBlock.h" + +#include +#include +#include + +#include +#include +#include +#include +#include + +#define TRACE(x) dprintf x + +using namespace FATFS; + + +Volume::Volume(boot::Partition *partition) + : + fRoot(NULL) +{ + TRACE(("%s()\n", __FUNCTION__)); + if ((fDevice = open_node(partition, O_RDONLY)) < B_OK) + return; + + fCachedBlock = new CachedBlock(*this); + if (fCachedBlock == NULL) + return; + + uint8 *buf; + /* = (char *)malloc(4096); + if (buf == NULL) + return;*/ + + fBlockSize = partition->block_size; + switch (fBlockSize) { + case 0x200: + fBlockShift = 9; + break; + case 0x400: + fBlockShift = 10; + break; + case 0x800: + fBlockShift = 11; + break; + default: + goto err1; + } + TRACE(("%s: reading bootsector\n", __FUNCTION__)); + // read boot sector + buf = fCachedBlock->SetTo(0); + if (buf == NULL) + goto err1; + + TRACE(("%s: checking signature\n", __FUNCTION__)); + // check the signature + if (((buf[0x1fe] != 0x55) || (buf[0x1ff] != 0xaa)) && (buf[0x15] == 0xf8)) + goto err1; + + if (!memcmp(buf+3, "NTFS ", 8) || !memcmp(buf+3, "HPFS ", 8)) + goto err1; + + TRACE(("%s: signature ok\n", __FUNCTION__)); + fBytesPerSector = read16(buf,0xb); + switch (fBytesPerSector) { + case 0x200: + fSectorShift = 9; + break; + case 0x400: + fSectorShift = 10; + break; + case 0x800: + fSectorShift = 11; + break; + default: + goto err1; + } + TRACE(("%s: block shift %d\n", __FUNCTION__, fBlockShift)); + + fSectorsPerCluster = buf[0xd]; + switch (fSectorsPerCluster) { + case 1: case 2: case 4: case 8: + case 0x10: case 0x20: case 0x40: case 0x80: + break; + default: + goto err1; + } + TRACE(("%s: sect/cluster %d\n", __FUNCTION__, fSectorsPerCluster)); + fClusterShift = fSectorShift; + for (uint32 spc = fSectorsPerCluster; !(spc & 0x01); ) { + spc >>= 1; + fClusterShift += 1; + } + TRACE(("%s: cluster shift %d\n", __FUNCTION__, fClusterShift)); + + fReservedSectors = read16(buf,0xe); + fFatCount = buf[0x10]; + if ((fFatCount == 0) || (fFatCount > 8)) + goto err1; + + fMediaDesc = buf[0x15]; + if ((fMediaDesc != 0xf0) && (fMediaDesc < 0xf8)) + goto err1; + + fSectorsPerFat = read16(buf,0x16); + if (fSectorsPerFat == 0) { + // FAT32 + fFatBits = 32; + fSectorsPerFat = read32(buf,0x24); + fTotalSectors = read32(buf,0x20); + bool lFatMirrored = !(buf[0x28] & 0x80); + fActiveFat = (lFatMirrored) ? (buf[0x28] & 0xf) : 0; + fDataStart = fReservedSectors + fFatCount * fSectorsPerFat; + fTotalClusters = (fTotalSectors - fDataStart) / fSectorsPerCluster; + fRootDirCluster = read32(buf,0x2c); + if (fRootDirCluster >= fTotalClusters) + goto err1; + } else { + // FAT12/16 + // XXX:FIXME + fFatBits = 16; + goto err1; + } + TRACE(("%s: block size %d, sector size %d, sectors/cluster %d\n", __FUNCTION__, + fBlockSize, fBytesPerSector, fSectorsPerCluster)); + TRACE(("%s: block shift %d, sector shift %d, cluster shift %d\n", __FUNCTION__, + fBlockShift, fSectorShift, fClusterShift)); + TRACE(("%s: reserved %d, max root entries %d, media %02x, sectors/fat %d\n", __FUNCTION__, + fReservedSectors, fMaxRootEntries, fMediaDesc, fSectorsPerFat)); + + + //if (fTotalSectors > partition->sectors_per_track * partition->cylinder_count * partition->head_count) + if ((off_t)fTotalSectors * fBytesPerSector > partition->size) + goto err1; + + TRACE(("%s: found fat%d filesystem, root dir at cluster %d\n", __FUNCTION__, + fFatBits, fRootDirCluster)); + + fRoot = new Directory(*this, fRootDirCluster, "/"); + return; + +err1: + dprintf("fatfs: cannot mount (bad superblock ?)\n"); + // XXX !? this triple-faults in QEMU .. + //delete fCachedBlock; +} + + +Volume::~Volume() +{ + delete fRoot; + delete fCachedBlock; + close(fDevice); +} + + +status_t +Volume::InitCheck() +{ + if (fCachedBlock == NULL) + return B_ERROR; + if (fRoot == NULL) + return B_ERROR; + + return B_OK; +} + + +status_t +Volume::GetName(char *name, size_t size) const +{ + //TODO: WRITEME + return strlcpy(name, "UNKNOWN", size); +} + + +off_t +Volume::ToOffset(uint32 cluster) const +{ + return (fDataStart << SectorShift()) + ((cluster - 2) << ClusterShift()); +} + +uint32 +Volume::NextCluster(uint32 cluster, uint32 skip) +{ + //TRACE(("%s(%d, %d)\n", __FUNCTION__, cluster, skip)); + // lookup the FAT for next cluster in chain + off_t offset; + uint8 *buf; + int32 next; + int fatBytes = (FatBits() + 7) / 8; + + switch (fFatBits) { + case 32: + case 16: + break; + //XXX handle FAT12 + default: + return InvalidClusterID(); + } + +again: + offset = fBytesPerSector * fReservedSectors; + //offset += fActiveFat * fTotalClusters * fFatBits / 8; + offset += cluster * fatBytes; + + if (!IsValidCluster(cluster)) + return InvalidClusterID(); + + buf = fCachedBlock->SetTo(ToBlock(offset)); + if (!buf) + return InvalidClusterID(); + + offset %= BlockSize(); + + switch (fFatBits) { + case 32: + next = read32(buf, offset); + next &= 0x0fffffff; + break; + case 16: + next = read16(buf, offset); + break; + default: + return InvalidClusterID(); + } + if (skip--) { + cluster = next; + goto again; + } + return next; +} + + +bool +Volume::IsValidCluster(uint32 cluster) const +{ + if (cluster > 1 && cluster < fTotalClusters) + return true; + return false; +} + + +bool +Volume::IsLastCluster(uint32 cluster) const +{ + if (cluster >= fTotalClusters && (cluster & 0xff8 == 0xff8)) + return true; + return false; +} + + + +// #pragma mark - + + +float +dosfs_identify_file_system(boot::Partition *partition) +{ + TRACE(("%s()\n", __FUNCTION__)); + Volume volume(partition); + + return volume.InitCheck() < B_OK ? 0 : 0.8; +} + + +static status_t +dosfs_get_file_system(boot::Partition *partition, ::Directory **_root) +{ + TRACE(("%s()\n", __FUNCTION__)); + 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 gFATFileSystemModule = { + "file_systems/dosfs/v1", + kPartitionTypeFAT32, // XXX:FIXME: FAT16 too ? + dosfs_identify_file_system, + dosfs_get_file_system +}; + diff --git a/src/system/boot/loader/file_systems/fat/Volume.h b/src/system/boot/loader/file_systems/fat/Volume.h new file mode 100644 index 0000000000..1e5f753249 --- /dev/null +++ b/src/system/boot/loader/file_systems/fat/Volume.h @@ -0,0 +1,85 @@ +/* +** 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 "fatfs.h" + +#include + +namespace boot { + class Partition; +} + + +namespace FATFS { + +class CachedBlock; +class Directory; + +class Volume { + public: + Volume(boot::Partition *partition); + ~Volume(); + + status_t InitCheck(); + status_t GetName(char *name, size_t size) const; + + int Device() const { return fDevice; } + Directory *Root() { return fRoot; } + int32 FatBits() const { return fFatBits; } + uint32 DataStart() const { return fDataStart; } + + int32 BlockSize() const { return fBlockSize; } + int32 ClusterSize() const { return fSectorsPerCluster * fBytesPerSector; } + + int32 BlockShift() const { return fBlockShift; } + int32 SectorShift() const { return fSectorShift; } + int32 ClusterShift() const { return fClusterShift; } + + int32 NumBlocks() const { return (int32)((off_t)fTotalSectors * fBytesPerSector / fBlockSize); } + int32 NumSectors() const { return fTotalSectors; } + int32 NumClusters() const { return fTotalClusters; } + + uint32 NextCluster(uint32 cluster, uint32 skip=0); + bool IsValidCluster(uint32 cluster) const; + bool IsLastCluster(uint32 cluster) const; + uint32 InvalidClusterID() const { return (1 << fFatBits) - 1; } + + off_t ToOffset(uint32 cluster) const; +// uint32 ToCluster(off_t offset) const { return offset >> ClusterShift(); } + off_t ToOffset(off_t block) const { return block << BlockShift(); } + uint32 ToBlock(off_t offset) const { return offset >> BlockShift(); } + + + protected: + int fDevice; + int32 fBlockShift; + int32 fSectorShift; + int32 fClusterShift; + uint32 fBlockSize; + // from the boot/fsinfo sectors + uint32 fBytesPerSector; + uint32 fSectorsPerCluster; + uint32 fReservedSectors; + uint8 fMediaDesc; + uint32 fSectorsPerFat; + uint32 fTotalSectors; + uint8 fFatCount; + uint16 fMaxRootEntries; + uint8 fActiveFat; + uint8 fFatBits; + uint32 fDataStart; + uint32 fTotalClusters; + uint32 fRootDirCluster; + + CachedBlock *fCachedBlock; + Directory *fRoot; +}; + +} // namespace FATFS + +#endif /* VOLUME_H */ diff --git a/src/system/boot/loader/file_systems/fat/fatfs.cpp b/src/system/boot/loader/file_systems/fat/fatfs.cpp new file mode 100644 index 0000000000..074476f3e1 --- /dev/null +++ b/src/system/boot/loader/file_systems/fat/fatfs.cpp @@ -0,0 +1,49 @@ +/* +** Copyright 2003, Axel Dörfler, axeld@pinc-software.de. All rights reserved. +** Distributed under the terms of the OpenBeOS License. +*/ + + +#include "fatfs.h" + +#include +#include +#include + +#include +#include +#include +#include +#include + + +using namespace FATFS; + +#if 0 +status_t +FATFS::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; +} + +#endif + diff --git a/src/system/boot/loader/file_systems/fat/fatfs.h b/src/system/boot/loader/file_systems/fat/fatfs.h new file mode 100644 index 0000000000..26aef2984b --- /dev/null +++ b/src/system/boot/loader/file_systems/fat/fatfs.h @@ -0,0 +1,42 @@ +/* +** Copyright 2003, Axel Dörfler, axeld@pinc-software.de. All rights reserved. +** Distributed under the terms of the OpenBeOS License. +*/ +#ifndef FATFS_H +#define FATFS_H + + +#include +#include + +namespace FATFS { + +class Volume; + +// mode bits +#define FAT_READ_ONLY 1 +#define FAT_HIDDEN 2 +#define FAT_SYSTEM 4 +#define FAT_VOLUME 8 +#define FAT_SUBDIR 16 +#define FAT_ARCHIVE 32 + +#define read32(buffer,off) \ + B_LENDIAN_TO_HOST_INT32(*(uint32 *)&buffer[off]) + +#define read16(buffer,off) \ + B_LENDIAN_TO_HOST_INT16(*(uint16 *)&buffer[off]) + +enum name_lengths { + FATFS_BASENAME_LENGTH = 8, + FATFS_EXTNAME_LENGTH = 3, + FATFS_NAME_LENGTH = 12, +}; + +status_t get_root_block(int fDevice, char *buffer, int32 blockSize, off_t partitionSize); + + +} // namespace FATFS + +#endif /* FATFS_H */ +