* Added Directory::CreateFile() which can be implemented by file systems to

support file creation.
* Extended open() and open_from() to support O_CREAT to create files.
  open_from() has got an optional "permissions" parameter for that purpose.
* Fixed errno. It would crash when being used. Also changed the POSIX functions
  to return their error code via errno as expected.
* Added writev().
* FAT file system:
  - Added support for reading long file names.
  - Added support for creating files (8.3 name only) and writing to them.
  - Enabled scanning partitions with it.
* Boot loader menu:
  - Enabled the "Reboot" menu item unconditionally.
  - Added "Save syslog from previous session" menu item to the debug menu.
    Currently saving the syslog to FAT32 volumes is supported.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@35882 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Ingo Weinhold 2010-03-16 17:29:58 +00:00
parent d1367a37cc
commit 085cf27b40
18 changed files with 1298 additions and 233 deletions

View File

@ -61,6 +61,9 @@ class Directory : public Node {
virtual status_t GetNextNode(void *cookie, Node **_node) = 0;
virtual status_t Rewind(void *cookie) = 0;
virtual bool IsEmpty() = 0;
virtual status_t CreateFile(const char *name, mode_t permissions,
Node **_node);
};
/** The console based nodes don't need cookies for I/O, they
@ -103,7 +106,8 @@ extern status_t register_boot_file_system(Directory *directory);
extern Directory *get_boot_file_system(stage2_args *args);
extern status_t mount_file_systems(stage2_args *args);
extern int open_node(Node *node, int mode);
extern int open_from(Directory *directory, const char *path, int mode);
extern int open_from(Directory *directory, const char *path, int mode,
mode_t permissions = 0);
extern Node *get_node_from(int fd);

View File

@ -20,6 +20,7 @@ UsePrivateHeaders shared storage ;
BOOT_SUPPORT_FILE_SYSTEM_BFS
BOOT_SUPPORT_FILE_SYSTEM_TARFS
#BOOT_SUPPORT_FILE_MAP_DISK
BOOT_SUPPORT_FILE_SYSTEM_FAT
;
# Add architecture specific partition/file system modules

View File

@ -6,11 +6,12 @@
#include "File.h"
#include <util/kernel_cpp.h>
#include <errno.h>
#include <sys/stat.h>
#include <unistd.h>
#include <util/kernel_cpp.h>
namespace FFS {
@ -18,7 +19,7 @@ class Stream {
public:
Stream(int device, FileBlock &node);
~Stream();
status_t InitCheck();
ssize_t ReadAt(off_t offset, uint8 *buffer, size_t size);
@ -55,35 +56,35 @@ Stream::~Stream()
}
status_t
status_t
Stream::InitCheck()
{
return fBlock.BlockData() != NULL ? B_OK : B_NO_MEMORY;
}
int32
int32
Stream::BlockOffset(off_t offset) const
{
return offset % fNode.BlockSize();
}
int32
int32
Stream::BlockIndex(off_t offset) const
{
return (offset % (fNode.BlockSize() * fNode.NumDataBlocks())) / fNode.BlockSize();
}
int32
int32
Stream::ExtensionBlockOffset(off_t offset) const
{
return offset / (fNode.BlockSize() * fNode.NumDataBlocks());
}
status_t
status_t
Stream::ReadNextExtension()
{
int32 next;
@ -99,7 +100,7 @@ Stream::ReadNextExtension()
}
ssize_t
ssize_t
Stream::ReadAt(off_t offset, uint8 *buffer, size_t size)
{
if (offset < 0)
@ -139,8 +140,8 @@ Stream::ReadAt(off_t offset, uint8 *buffer, size_t size)
ssize_t bytesRead = read_pos(fDevice, block * fNode.BlockSize() + blockOffset,
buffer, toRead);
if (bytesRead < B_OK)
return bytesRead;
if (bytesRead < 0)
return errno;
bytesLeft -= bytesRead;
buffer += bytesRead;
@ -172,7 +173,7 @@ File::~File()
}
status_t
status_t
File::InitCheck()
{
if (!fNode.IsFile())
@ -182,7 +183,7 @@ File::InitCheck()
}
status_t
status_t
File::Open(void **_cookie, int mode)
{
Stream *stream = new(nothrow) Stream(fVolume.Device(), fNode);
@ -199,7 +200,7 @@ File::Open(void **_cookie, int mode)
}
status_t
status_t
File::Close(void *cookie)
{
Stream *stream = (Stream *)cookie;
@ -209,7 +210,7 @@ File::Close(void *cookie)
}
ssize_t
ssize_t
File::ReadAt(void *cookie, off_t pos, void *buffer, size_t bufferSize)
{
Stream *stream = (Stream *)cookie;
@ -220,28 +221,28 @@ File::ReadAt(void *cookie, off_t pos, void *buffer, size_t bufferSize)
}
ssize_t
ssize_t
File::WriteAt(void *cookie, off_t pos, const void *buffer, size_t bufferSize)
{
return EROFS;
}
status_t
status_t
File::GetName(char *nameBuffer, size_t bufferSize) const
{
return fNode.GetName(nameBuffer, bufferSize);
}
int32
int32
File::Type() const
{
return S_IFREG;
}
off_t
off_t
File::Size() const
{
return fNode.Size();

View File

@ -8,16 +8,14 @@
#include "CachedBlock.h"
#include "Stream.h"
#include "Directory.h"
#include "File.h"
#include <util/kernel_cpp.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <util/kernel_cpp.h>
using namespace FATFS;
@ -47,3 +45,61 @@ CachedBlock::~CachedBlock()
}
uint8 *
CachedBlock::SetTo(off_t block)
{
status_t error = SetTo(block, READ);
return error == B_OK ? fBlock : NULL;
}
status_t
CachedBlock::SetTo(off_t blockNumber, uint32 flags)
{
if (fBlock == NULL) {
fBlock = (uint8*)malloc(BlockSize());
if (fBlock == NULL)
return B_NO_MEMORY;
}
if (blockNumber != fBlockNumber)
flags |= FORCE;
fBlockNumber = blockNumber;
status_t error = B_OK;
if ((flags & READ) != 0) {
if ((flags & FORCE) != 0) {
ssize_t bytesRead = read_pos(fVolume.Device(),
fBlockNumber << BlockShift(), fBlock, BlockSize());
if (bytesRead < 0)
error = bytesRead;
else if (bytesRead < (ssize_t)BlockSize())
error = B_ERROR;
}
} else if ((flags & CLEAR) != 0)
memset(fBlock, 0, BlockSize());
if (error != B_OK)
fBlockNumber = -1;
return error;
}
status_t
CachedBlock::Flush()
{
if (fBlockNumber < 0)
return B_BAD_VALUE;
ssize_t written = write_pos(fVolume.Device(), fBlockNumber << BlockShift(),
fBlock, BlockSize());
if (written < 0)
return errno;
if (written != (ssize_t)BlockSize())
return B_ERROR;
return B_OK;
}

View File

@ -8,6 +8,7 @@
#ifndef CACHED_BLOCK_H
#define CACHED_BLOCK_H
#include <SupportDefs.h>
#include <sys/stat.h>
#include <stdlib.h>
@ -15,15 +16,25 @@
#include "Volume.h"
namespace FATFS {
class CachedBlock {
public:
enum {
READ = 0x01,
CLEAR = 0x02,
FORCE = 0x04,
};
public:
CachedBlock(Volume &volume);
CachedBlock(Volume &volume, off_t block);
~CachedBlock();
uint8 *SetTo(off_t block);
status_t SetTo(off_t blockNumber, uint32 flags);
status_t Flush();
void Unset();
@ -46,25 +57,6 @@ CachedBlock::Unset()
}
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;
}
} // namespace FATFS

View File

@ -5,16 +5,21 @@
#include "Directory.h"
#include "Volume.h"
#include "File.h"
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <new>
#include <StorageDefs.h>
#include <util/kernel_cpp.h>
#include <string.h>
#include <unistd.h>
#include <stdint.h>
#include <stdio.h>
#include "CachedBlock.h"
#include "File.h"
#include "Volume.h"
//#define TRACE(x) dprintf x
#define TRACE(x) do {} while (0)
@ -27,9 +32,12 @@ struct dir_entry {
const char *Extension() const { return fExt; };
uint8 Flags() const { return fFlags; };
uint32 Cluster(int32 fatBits) const;
void SetCluster(uint32 cluster, int32 fatBits);
uint32 Size() const { return B_LENDIAN_TO_HOST_INT32(fSize); };
void SetSize(uint32 size);
bool IsFile() const;
bool IsDir() const;
char fName[8];
char fExt[3];
uint8 fFlags;
@ -55,6 +63,23 @@ dir_entry::Cluster(int32 fatBits) const
return c;
}
void
dir_entry::SetCluster(uint32 cluster, int32 fatBits)
{
fClusterLSB = B_HOST_TO_LENDIAN_INT16((uint16)cluster);
if (fatBits == 32)
fClusterMSB = B_HOST_TO_LENDIAN_INT16(cluster >> 16);
}
void
dir_entry::SetSize(uint32 size)
{
fSize = B_HOST_TO_LENDIAN_INT32(size);
}
bool
dir_entry::IsFile() const
{
@ -70,16 +95,211 @@ dir_entry::IsDir() const
struct dir_cookie {
enum {
MAX_UTF16_NAME_LENGTH = 255
};
int32 index;
struct dir_entry entry;
off_t entryOffset;
uint16 nameBuffer[MAX_UTF16_NAME_LENGTH];
uint32 nameLength;
off_t Offset() const { return index * sizeof(struct dir_entry); }
char* Name() { return (char*)nameBuffer; }
void ResetName();
bool AddNameChars(const uint16* chars, uint32 count);
bool ConvertNameToUTF8();
void Set8_3Name(const char* baseName, const char* extension);
};
Directory::Directory(Volume &volume, uint32 cluster, const char *name)
void
dir_cookie::ResetName()
{
nameLength = 0;
}
bool
dir_cookie::AddNameChars(const uint16* chars, uint32 count)
{
// If there is a null character, we ignore it and all subsequent characters.
for (uint32 i = 0; i < count; i++) {
if (chars[i] == 0) {
count = i;
break;
}
}
if (count > 0) {
if (count > (MAX_UTF16_NAME_LENGTH - nameLength))
return false;
nameLength += count;
memcpy(nameBuffer + (MAX_UTF16_NAME_LENGTH - nameLength),
chars, count * 2);
}
return true;
}
bool
dir_cookie::ConvertNameToUTF8()
{
char name[B_FILE_NAME_LENGTH];
uint32 nameOffset = 0;
const uint16* utf16 = nameBuffer + (MAX_UTF16_NAME_LENGTH - nameLength);
for (uint32 i = 0; i < nameLength; i++) {
uint8 utf8[4];
uint32 count;
uint16 c = B_LENDIAN_TO_HOST_INT16(utf16[i]);
if (c < 0x80) {
utf8[0] = c;
count = 1;
} else if (c < 0xff80) {
utf8[0] = 0xc0 | (c >> 6);
utf8[1] = 0x80 | (c & 0x3f);
count = 2;
} else if ((c & 0xfc00) != 0xd800) {
utf8[0] = 0xe0 | (c >> 12);
utf8[1] = 0x80 | ((c >> 6) & 0x3f);
utf8[2] = 0x80 | (c & 0x3f);
count = 3;
} else {
// surrogate pair
if (i + 1 >= nameLength)
return false;
uint16 c2 = B_LENDIAN_TO_HOST_INT16(utf16[++i]);
if ((c2 & 0xfc00) != 0xdc00)
return false;
uint32 value = ((c - 0xd7c0) << 10) | (c2 & 0x3ff);
utf8[0] = 0xf0 | (value >> 18);
utf8[1] = 0x80 | ((value >> 12) & 0x3f);
utf8[2] = 0x80 | ((value >> 6) & 0x3f);
utf8[3] = 0x80 | (value & 0x3f);
count = 4;
}
if (nameOffset + count >= sizeof(name))
return false;
memcpy(name + nameOffset, utf8, count);
nameOffset += count;
}
name[nameOffset] = '\0';
strlcpy(Name(), name, sizeof(nameBuffer));
return true;
}
void
dir_cookie::Set8_3Name(const char* baseName, const char* extension)
{
// trim base name
uint32 baseNameLength = 8;
while (baseNameLength > 0 && baseName[baseNameLength - 1] == ' ')
baseNameLength--;
// trim extension
uint32 extensionLength = 3;
while (extensionLength > 0 && extension[extensionLength - 1] == ' ')
extensionLength--;
// compose the name
char* name = Name();
memcpy(name, baseName, baseNameLength);
if (extensionLength > 0) {
name[baseNameLength] = '.';
memcpy(name + baseNameLength + 1, extension, extensionLength);
name[baseNameLength + 1 + extensionLength] = '\0';
} else
name[baseNameLength] = '\0';
}
// #pragma mark -
static bool
is_valid_8_3_file_name_char(char c)
{
if ((uint8)c >= 128)
return true;
if ((c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9'))
return true;
return strchr("*!#$%&'()-@^_`{}~ ", c) != NULL;
}
static bool
check_valid_8_3_file_name(const char* name, const char*& _baseName,
uint32& _baseNameLength, const char*& _extension, uint32& _extensionLength)
{
// check length of base name and extension
size_t nameLength = strlen(name);
const char* extension = strchr(name, '.');
size_t baseNameLength;
size_t extensionLength;
if (extension != NULL) {
baseNameLength = extension - name;
extensionLength = nameLength - baseNameLength - 1;
if (extensionLength > 0)
extension++;
else
extension = NULL;
} else {
baseNameLength = nameLength;
extensionLength = 0;
}
// trim trailing space
while (baseNameLength > 0 && name[baseNameLength - 1] == ' ')
baseNameLength--;
while (extensionLength > 0 && extension[extensionLength - 1] == ' ')
extensionLength--;
if (baseNameLength == 0 || baseNameLength > 8 || extensionLength > 3)
return false;
// check the chars
for (size_t i = 0; i < baseNameLength; i++) {
if (!is_valid_8_3_file_name_char(name[i]))
return false;
}
for (size_t i = 0; i < extensionLength; i++) {
if (!is_valid_8_3_file_name_char(extension[i]))
return false;
}
_baseName = name;
_baseNameLength = baseNameLength;
_extension = extension;
_extensionLength = extensionLength;
return true;
}
// #pragma mark - Directory
Directory::Directory(Volume &volume, off_t dirEntryOffset, uint32 cluster,
const char *name)
:
fVolume(volume),
fStream(volume, cluster, UINT32_MAX, name)
fStream(volume, cluster, UINT32_MAX, name),
fDirEntryOffset(dirEntryOffset)
{
TRACE(("FASFS::Directory::(, %lu, %s)\n", cluster, name));
}
@ -91,7 +311,7 @@ Directory::~Directory()
}
status_t
status_t
Directory::InitCheck()
{
status_t err;
@ -102,7 +322,7 @@ Directory::InitCheck()
}
status_t
status_t
Directory::Open(void **_cookie, int mode)
{
TRACE(("FASFS::Directory::%s(, %d)\n", __FUNCTION__, mode));
@ -113,13 +333,14 @@ Directory::Open(void **_cookie, int mode)
return B_NO_MEMORY;
c->index = -1;
c->entryOffset = 0;
*_cookie = (void *)c;
return B_OK;
}
status_t
status_t
Directory::Close(void *cookie)
{
TRACE(("FASFS::Directory::%s()\n", __FUNCTION__));
@ -138,67 +359,40 @@ Directory::Lookup(const char *name, bool traverseLinks)
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;
c->entryOffset = 0;
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;
TRACE(("FASFS::Directory::%s: %s <> '%s'\n", __FUNCTION__,
name, c->Name()));
if (strcasecmp(name, c->Name()) == 0) {
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);
return new File(fVolume, c->entryOffset,
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 new Directory(fVolume, c->entryOffset,
c->entry.Cluster(fVolume.FatBits()), name);
}
return NULL;
}
status_t
status_t
Directory::GetNextEntry(void *cookie, char *name, size_t size)
{
TRACE(("FASFS::Directory::%s()\n", __FUNCTION__));
@ -209,14 +403,12 @@ Directory::GetNextEntry(void *cookie, char *name, size_t size)
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));
strlcpy(name, c->Name(), size);
return B_OK;
}
status_t
status_t
Directory::GetNextNode(void *cookie, Node **_node)
{
return B_ERROR;
@ -229,6 +421,7 @@ Directory::Rewind(void *cookie)
TRACE(("FASFS::Directory::%s()\n", __FUNCTION__));
struct dir_cookie *c = (struct dir_cookie *)cookie;
c->index = -1;
c->entryOffset = 0;
return B_OK;
}
@ -241,6 +434,7 @@ Directory::IsEmpty()
struct dir_cookie cookie;
struct dir_cookie *c = &cookie;
c->index = -1;
c->entryOffset = 0;
if (GetNextEntry(c) == B_OK)
return false;
return true;
@ -264,36 +458,222 @@ Directory::Inode() const
return fStream.FirstCluster() << 16;
}
status_t
status_t
Directory::CreateFile(const char* name, mode_t permissions, Node** _node)
{
if (Node* node = Lookup(name, false)) {
node->Release();
return B_FILE_EXISTS;
}
// We only support 8.3 file names ATM.
const char* baseName;
const char* extension;
uint32 baseNameLength;
uint32 extensionLength;
if (!check_valid_8_3_file_name(name, baseName, baseNameLength, extension,
extensionLength)) {
return B_UNSUPPORTED;
}
// prepare a directory entry for the new file
dir_entry entry;
memset(entry.fName, ' ', 11);
// clear both base name and extension
memcpy(entry.fName, baseName, baseNameLength);
if (extensionLength > 0)
memcpy(entry.fExt, extension, extensionLength);
entry.fFlags = 0;
entry.fReserved1 = 0;
entry.fCreateTime10ms = 199;
entry.fCreateTime = B_HOST_TO_LENDIAN_INT16((23 << 11) | (59 << 5) | 29);
// 23:59:59.9
entry.fCreateDate = B_HOST_TO_LENDIAN_INT16((127 << 9) | (12 << 5) | 31);
// 2107-12-31
entry.fAccessDate = entry.fCreateDate;
entry.fClusterMSB = 0;
entry.fModifiedTime = entry.fCreateTime;
entry.fModifiedDate = entry.fCreateDate;
entry.fClusterLSB = 0;
entry.fSize = 0;
// add the entry to the directory
off_t entryOffset;
status_t error = _AddEntry(entry, entryOffset);
if (error != B_OK)
return error;
// create a File object
File* file = new(std::nothrow) File(fVolume, entryOffset,
entry.Cluster(fVolume.FatBits()), entry.Size(), name);
if (file == NULL)
return B_NO_MEMORY;
*_node = file;
return B_OK;
}
/*static*/ status_t
Directory::UpdateDirEntry(Volume& volume, off_t dirEntryOffset,
uint32 firstCluster, uint32 size)
{
if (dirEntryOffset == 0)
return B_BAD_VALUE;
CachedBlock cachedBlock(volume);
off_t block = volume.ToBlock(dirEntryOffset);
status_t error = cachedBlock.SetTo(block, CachedBlock::READ);
if (error != B_OK)
return error;
dir_entry* entry = (dir_entry*)(cachedBlock.Block()
+ dirEntryOffset % volume.BlockSize());
entry->SetCluster(firstCluster, volume.FatBits());
entry->SetSize(size);
return cachedBlock.Flush();
}
status_t
Directory::GetNextEntry(void *cookie, uint8 mask, uint8 match)
{
TRACE(("FASFS::Directory::%s(, %02x, %02x)\n", __FUNCTION__, mask, match));
struct dir_cookie *c = (struct dir_cookie *)cookie;
bool hasLongName = false;
bool longNameValid = false;
do {
c->index++;
size_t len = sizeof(c->entry);
if (fStream.ReadAt(c->Offset(), (uint8 *)&c->entry, &len) < B_OK)
if (fStream.ReadAt(c->Offset(), (uint8 *)&c->entry, &len,
&c->entryOffset) != B_OK || len != sizeof(c->entry)) {
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
if (c->entry.Flags() == 0x0f) { // LFN entry
uint8* nameEntry = (uint8*)&c->entry;
if ((*nameEntry & 0x40) != 0) {
c->ResetName();
hasLongName = true;
longNameValid = true;
}
uint16 nameChars[13];
memcpy(nameChars, nameEntry + 0x01, 10);
memcpy(nameChars + 5, nameEntry + 0x0e, 12);
memcpy(nameChars + 11, nameEntry + 0x1c, 4);
longNameValid |= c->AddNameChars(nameChars, 13);
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__,
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)
if ((c->entry.Flags() & mask) == match) {
if (longNameValid)
longNameValid = c->ConvertNameToUTF8();
if (!longNameValid) {
// copy 8.3 name to name buffer
c->Set8_3Name(c->entry.BaseName(), c->entry.Extension());
}
break;
}
} while (true);
TRACE(("FATFS::Directory::%s: '%8.8s.%3.3s'\n", __FUNCTION__,
c->entry.BaseName(), c->entry.Extension()));
return B_OK;
}
status_t
Directory::_AddEntry(dir_entry& entry, off_t& _entryOffset)
{
off_t dirSize = _GetStreamSize();
if (dirSize < 0)
return dirSize;
uint32 firstCluster = fStream.FirstCluster();
// First null-terminate the new entry list, so we don't leave the list in
// a broken state, if writing the actual entry fails. We only need to do
// that when the entry is not at the end of a cluster.
if ((dirSize + sizeof(entry)) % fVolume.ClusterSize() != 0) {
// TODO: Rather zero the complete remainder of the cluster?
size_t size = 1;
char terminator = 0;
status_t error = fStream.WriteAt(dirSize + sizeof(entry), &terminator,
&size);
if (error != B_OK)
return error;
if (size != 1)
return B_ERROR;
}
// write the entry
size_t size = sizeof(entry);
status_t error = fStream.WriteAt(dirSize, &entry, &size, &_entryOffset);
if (error != B_OK)
return error;
if (size != sizeof(entry))
return B_ERROR;
// TODO: Undo changes!
fStream.SetSize(dirSize + sizeof(entry));
// If the directory cluster has changed (which should only happen, if the
// directory was empty before), we need to adjust the directory entry.
if (firstCluster != fStream.FirstCluster()) {
error = UpdateDirEntry(fVolume, fDirEntryOffset, fStream.FirstCluster(),
0);
if (error != B_OK)
return error;
// TODO: Undo changes!
}
return B_OK;
}
off_t
Directory::_GetStreamSize()
{
off_t size = fStream.Size();
if (size != UINT32_MAX)
return size;
// iterate to the end of the directory
size = 0;
while (true) {
dir_entry entry;
size_t entrySize = sizeof(entry);
status_t error = fStream.ReadAt(size, &entry, &entrySize);
if (error != B_OK)
return error;
if (entrySize != sizeof(entry) || entry.fName[0] == 0)
break;
size += sizeof(entry);
}
fStream.SetSize(size);
return size;
}
} // namespace FATFS

View File

@ -14,16 +14,20 @@
namespace FATFS {
struct dir_entry;
class Volume;
class Directory : public ::Directory {
public:
Directory();
Directory(Volume &volume, uint32 cluster, const char *name);
Directory(Volume &volume, off_t dirEntryOffset, uint32 cluster,
const char *name);
virtual ~Directory();
status_t InitCheck();
off_t DirEntryOffset() const { return fDirEntryOffset; }
virtual status_t Open(void **_cookie, int mode);
virtual status_t Close(void *cookie);
@ -37,11 +41,21 @@ class Directory : public ::Directory {
virtual status_t GetName(char *name, size_t size) const;
virtual ino_t Inode() const;
virtual status_t CreateFile(const char *name, mode_t permissions,
Node **_node);
static status_t UpdateDirEntry(Volume& volume, off_t dirEntryOffset,
uint32 firstCluster, uint32 size);
private:
status_t GetNextEntry(void *cookie,
status_t GetNextEntry(void *cookie,
uint8 mask = FAT_VOLUME, uint8 match = 0);
status_t _AddEntry(dir_entry& entry, off_t& _entryOffset);
off_t _GetStreamSize();
Volume &fVolume;
Stream fStream;
off_t fDirEntryOffset;
typedef ::Directory _inherited;
};

View File

@ -6,11 +6,13 @@
#include "File.h"
#include <util/kernel_cpp.h>
#include <sys/stat.h>
#include <unistd.h>
#include <util/kernel_cpp.h>
#include "Directory.h"
//#define TRACE(x) dprintf x
#define TRACE(x) do {} while (0)
@ -19,10 +21,12 @@
namespace FATFS {
File::File(Volume &volume, uint32 cluster, off_t size, const char *name)
File::File(Volume &volume, off_t dirEntryOffset, uint32 cluster, off_t size,
const char *name)
:
fVolume(volume),
fStream(volume, cluster, size, name)
fStream(volume, cluster, size, name),
fDirEntryOffset(dirEntryOffset)
{
TRACE(("FATFS::File::()\n"));
}
@ -51,14 +55,14 @@ File::Open(void **_cookie, int mode)
if (fStream.InitCheck() < B_OK)
return fStream.InitCheck();
return B_OK;
return Node::Open(_cookie, mode);
}
status_t
File::Close(void *cookie)
{
return B_OK;
return Node::Close(cookie);
}
@ -67,7 +71,7 @@ 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);
err = fStream.ReadAt(pos, buffer, &bufferSize);
if (err < B_OK)
return err;
return bufferSize;
@ -77,7 +81,25 @@ File::ReadAt(void *cookie, off_t pos, void *buffer, size_t bufferSize)
ssize_t
File::WriteAt(void *cookie, off_t pos, const void *buffer, size_t bufferSize)
{
return EROFS;
off_t streamSize = fStream.Size();
uint32 firstCluster = fStream.FirstCluster();
// write data
size_t written = bufferSize;
status_t error = fStream.WriteAt(pos, buffer, &written);
if (error != B_OK)
return error;
// If the file size has changed, we need to adjust the directory entry.
if (fStream.Size() > streamSize || fStream.FirstCluster() != firstCluster) {
error = Directory::UpdateDirEntry(fVolume, fDirEntryOffset,
fStream.FirstCluster(), fStream.Size());
if (error != B_OK)
return error;
// TODO: Undo the changes!
}
return written;
}
@ -95,14 +117,14 @@ File::GetFileMap(struct file_map_run *runs, int32 *count)
}
int32
int32
File::Type() const
{
return S_IFREG;
}
off_t
off_t
File::Size() const
{
return fStream.Size();

View File

@ -16,11 +16,14 @@ namespace FATFS {
class File : public Node {
public:
File(Volume &volume, uint32 cluster, off_t size, const char *name);
File(Volume &volume, off_t dirEntryOffset, uint32 cluster, off_t size,
const char *name);
virtual ~File();
status_t InitCheck();
off_t DirEntryOffset() const { return fDirEntryOffset; }
virtual status_t Open(void **_cookie, int mode);
virtual status_t Close(void *cookie);
@ -37,6 +40,7 @@ class File : public Node {
Volume &fVolume;
//FileBlock fNode;
Stream fStream;
off_t fDirEntryOffset;
};
} // namespace FATFS

View File

@ -8,17 +8,20 @@
#include "Stream.h"
#include "CachedBlock.h"
#include "Directory.h"
#include "File.h"
#include <util/kernel_cpp.h>
#include <boot/FileMapDisk.h>
#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <unistd.h>
#include <algorithm>
#include <boot/FileMapDisk.h>
#include <util/kernel_cpp.h>
#include "CachedBlock.h"
#include "Directory.h"
#include "File.h"
//#define TRACE(x) dprintf x
#define TRACE(x) do {} while (0)
@ -104,11 +107,11 @@ Stream::GetFileMap(struct file_map_run *runs, int32 *count)
status_t
Stream::FindBlock(off_t pos, off_t &block, off_t &offset)
Stream::_FindCluster(off_t pos, uint32& _cluster)
{
//TRACE(("FATFS::Stream::%s(%Ld,,)\n", __FUNCTION__, pos));
uint32 index = (uint32)(pos / fVolume.ClusterSize());
if (pos >= fSize || index >= fClusterCount)
if (pos > fSize || index >= fClusterCount)
return B_BAD_VALUE;
uint32 cluster = 0;
@ -147,23 +150,94 @@ Stream::FindBlock(off_t pos, off_t &block, off_t &offset)
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));
_cluster = cluster;
return B_OK;
}
status_t
Stream::ReadAt(off_t pos, uint8 *buffer, size_t *_length)
Stream::_FindOrCreateCluster(off_t pos, uint32& _cluster, bool& _added)
{
status_t error = _FindCluster(pos, _cluster);
if (error == B_OK) {
_added = false;
return B_OK;
}
// iterate through the cluster list
uint32 index = (uint32)(pos / fVolume.ClusterSize());
uint32 cluster = fFirstCluster;
uint32 clusterCount = 0;
if (cluster != 0) {
uint32 nextCluster = cluster;
while (clusterCount <= index) {
if (!fVolume.IsValidCluster(nextCluster)
|| fVolume.IsLastCluster(nextCluster)) {
break;
}
cluster = nextCluster;
clusterCount++;
nextCluster = fVolume.NextCluster(nextCluster);
}
}
if (clusterCount > index) {
// the cluster existed after all
_cluster = cluster;
_added = false;
return B_OK;
}
while (clusterCount <= index) {
uint32 newCluster;
error = fVolume.AllocateCluster(cluster, newCluster);
if (error != B_OK)
return error;
if (clusterCount == 0)
fFirstCluster = newCluster;
// TODO: We should support to zero out the new cluster. Maybe make this
// and optional parameter of WriteAt().
cluster = newCluster;
clusterCount++;
}
_cluster = cluster;
_added = true;
return B_OK;
}
status_t
Stream::FindBlock(off_t pos, off_t &block, off_t &offset)
{
uint32 cluster;
status_t error = _FindCluster(pos, cluster);
if (error != B_OK)
return error;
// convert to position
offset = fVolume.ClusterToOffset(cluster);
offset += (pos %= fVolume.ClusterSize());
// convert to block + offset
block = fVolume.ToBlock(offset);
offset %= fVolume.BlockSize();
return B_OK;
}
status_t
Stream::ReadAt(off_t pos, void *_buffer, size_t *_length, off_t *diskOffset)
{
TRACE(("FATFS::Stream::%s(%Ld, )\n", __FUNCTION__, pos));
uint8* buffer = (uint8*)_buffer;
// set/check boundaries for pos/length
if (pos < 0)
return B_BAD_VALUE;
@ -193,6 +267,9 @@ Stream::ReadAt(off_t pos, uint8 *buffer, size_t *_length)
return B_BAD_VALUE;
}
if (diskOffset != NULL)
*diskOffset = fVolume.BlockToOffset(num) + offset;
uint32 bytesRead = 0;
uint32 blockSize = fVolume.BlockSize();
uint8 *block;
@ -248,7 +325,7 @@ Stream::ReadAt(off_t pos, uint8 *buffer, size_t *_length)
break;
}
if (read_pos(fVolume.Device(), fVolume.ToOffset(num),
if (read_pos(fVolume.Device(), fVolume.BlockToOffset(num),
buffer + bytesRead, fVolume.BlockSize()) < B_OK) {
*_length = bytesRead;
return B_BAD_VALUE;
@ -273,6 +350,82 @@ Stream::ReadAt(off_t pos, uint8 *buffer, size_t *_length)
}
status_t
Stream::WriteAt(off_t pos, const void* _buffer, size_t* _length,
off_t* diskOffset)
{
if (pos < 0)
return B_BAD_VALUE;
const uint8* buffer = (const uint8*)_buffer;
size_t length = *_length;
size_t totalWritten = 0;
status_t error = B_OK;
CachedBlock cachedBlock(fVolume);
while (length > 0) {
// get the cluster
uint32 cluster;
bool added;
error = _FindOrCreateCluster(pos, cluster, added);
if (error != B_OK)
break;
// convert to position
off_t inClusterOffset = pos % fVolume.ClusterSize();
off_t offset = fVolume.ClusterToOffset(cluster) + inClusterOffset;
if (diskOffset != NULL) {
*diskOffset = offset;
diskOffset = NULL;
}
// convert to block + offset
off_t block = fVolume.ToBlock(offset);
size_t inBlockOffset = offset % fVolume.BlockSize();
// write
size_t toWrite = std::min(fVolume.BlockSize() - inBlockOffset, length);
if (toWrite == (size_t)fVolume.BlockSize()) {
// write the whole block
ssize_t written = write_pos(fVolume.Device(),
fVolume.BlockToOffset(block), buffer, fVolume.BlockSize());
if (written < 0) {
error = written;
break;
}
if (written != fVolume.BlockSize()) {
error = B_ERROR;
break;
}
} else {
// write a partial block -- need to read it from disk first
error = cachedBlock.SetTo(block, CachedBlock::READ);
if (error != B_OK)
break;
memcpy(cachedBlock.Block() + inBlockOffset, buffer, toWrite);
error = cachedBlock.Flush();
if (error != B_OK)
break;
}
totalWritten += toWrite;
pos += toWrite;
buffer += toWrite;
length -= toWrite;
if (pos > fSize)
fSize = pos;
}
*_length = totalWritten;
return totalWritten > 0 ? B_OK : error;
}
status_t
Stream::BuildClusterList()
{

View File

@ -34,11 +34,20 @@ class Stream {
off_t Size() const { return fSize; }
uint32 FirstCluster() const { return fFirstCluster; }
status_t ReadAt(off_t pos, uint8 *buffer, size_t *length);
void SetSize(off_t size) { fSize = size; }
status_t ReadAt(off_t pos, void *buffer, size_t *length,
off_t *diskOffset = NULL);
status_t WriteAt(off_t pos, const void *buffer, size_t *length,
off_t *diskOffset = NULL);
private:
status_t BuildClusterList();
status_t _FindCluster(off_t pos, uint32& _cluster);
status_t _FindOrCreateCluster(off_t pos, uint32& _cluster,
bool& _added);
status_t FindBlock(off_t pos, off_t &block, off_t &offset);
Volume &fVolume;
uint32 fFirstCluster;
uint32 fClusterCount;

View File

@ -65,10 +65,10 @@ Volume::Volume(boot::Partition *partition)
// 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) {
@ -85,7 +85,7 @@ Volume::Volume(boot::Partition *partition)
goto err1;
}
TRACE(("%s: block shift %d\n", __FUNCTION__, fBlockShift));
fSectorsPerCluster = buf[0xd];
switch (fSectorsPerCluster) {
case 1: case 2: case 4: case 8:
@ -101,12 +101,12 @@ Volume::Volume(boot::Partition *partition)
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;
@ -124,32 +124,36 @@ Volume::Volume(boot::Partition *partition)
fRootDirCluster = read32(buf,0x2c);
if (fRootDirCluster >= fTotalClusters)
goto err1;
fFSInfoSector = read16(buf, 0x30);
if (fFSInfoSector < 1 || fFSInfoSector > fTotalSectors)
goto err1;
} else {
// FAT12/16
// XXX:FIXME
fFatBits = 16;
goto err1;
}
TRACE(("%s: block size %d, sector size %d, sectors/cluster %d\n", __FUNCTION__,
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__,
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__,
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__,
TRACE(("%s: found fat%d filesystem, root dir at cluster %d\n", __FUNCTION__,
fFatBits, fRootDirCluster));
fRoot = new Directory(*this, fRootDirCluster, "/");
fRoot = new Directory(*this, 0, fRootDirCluster, "/");
return;
err1:
dprintf("fatfs: cannot mount (bad superblock ?)\n");
TRACE("fatfs: cannot mount (bad superblock ?)\n");
// XXX !? this triple-faults in QEMU ..
//delete fCachedBlock;
}
@ -163,7 +167,7 @@ Volume::~Volume()
}
status_t
status_t
Volume::InitCheck()
{
if (fCachedBlock == NULL)
@ -175,7 +179,7 @@ Volume::InitCheck()
}
status_t
status_t
Volume::GetName(char *name, size_t size) const
{
//TODO: WRITEME
@ -184,12 +188,12 @@ Volume::GetName(char *name, size_t size) const
off_t
Volume::ToOffset(uint32 cluster) const
Volume::ClusterToOffset(uint32 cluster) const
{
return (fDataStart << SectorShift()) + ((cluster - 2) << ClusterShift());
}
uint32
uint32
Volume::NextCluster(uint32 cluster, uint32 skip)
{
//TRACE(("%s(%d, %d)\n", __FUNCTION__, cluster, skip));
@ -221,7 +225,7 @@ again:
return InvalidClusterID();
offset %= BlockSize();
switch (fFatBits) {
case 32:
next = read32(buf, offset);
@ -241,7 +245,7 @@ again:
}
bool
bool
Volume::IsValidCluster(uint32 cluster) const
{
if (cluster > 1 && cluster < fTotalClusters)
@ -255,10 +259,141 @@ Volume::IsLastCluster(uint32 cluster) const
{
if (cluster >= fTotalClusters && ((cluster & 0xff8) == 0xff8))
return true;
return false;
return false;
}
/*! Allocates a free cluster.
If \a previousCluster is a valid cluster idnex, its chain pointer is
changed to point to the newly allocated cluster.
*/
status_t
Volume::AllocateCluster(uint32 previousCluster, uint32& _newCluster)
{
if (fFatBits != 32)
return B_UNSUPPORTED;
// TODO: Support FAT16 and FAT12.
const int fatBytes = FatBits() / 8;
const uint32 blockOffsetMask = (uint32)BlockSize() - 1;
// Iterate through the FAT to find a free cluster.
off_t offset = fBytesPerSector * fReservedSectors;
offset += 2 * fatBytes;
for (uint32 i = 2; i < fTotalClusters; i++, offset += fatBytes) {
uint8* buffer = fCachedBlock->SetTo(ToBlock(offset));
if (buffer == NULL)
return B_ERROR;
uint32 value = read32(buffer, offset & blockOffsetMask);
if (value == 0) {
// found one -- mark it used (end of file)
status_t error = _UpdateCluster(i, 0x0ffffff8);
if (error != B_OK)
return error;
// If a previous cluster was given, update its list link.
if (IsValidCluster(previousCluster)) {
error = _UpdateCluster(previousCluster, i);
if (error != B_OK) {
_UpdateCluster(i, 0);
return error;
}
}
_ClusterAllocated(i);
_newCluster = i;
return B_OK;
}
}
return B_DEVICE_FULL;
}
status_t
Volume::_UpdateCluster(uint32 cluster, uint32 value)
{
if (fFatBits != 32 && fFatBits != 16)
return B_UNSUPPORTED;
// TODO: Support FAT12.
if (!IsValidCluster(cluster))
return InvalidClusterID();
// get the buffer we need to change
const int fatBytes = FatBits() / 8;
for (uint8 i = 0; i < fFatCount; i++) {
off_t offset
= fBytesPerSector * (fReservedSectors + i * fSectorsPerFat);
offset += cluster * fatBytes;
uint8* buffer = fCachedBlock->SetTo(ToBlock(offset));
if (buffer == NULL)
return InvalidClusterID();
offset %= BlockSize();
// set the value
switch (fFatBits) {
case 32:
*(uint32*)(buffer + offset) = B_HOST_TO_LENDIAN_INT32(value);
break;
case 16:
*(uint16*)(buffer + offset) = B_HOST_TO_LENDIAN_INT16(value);
break;
default:
return InvalidClusterID();
}
// write the block back to disk
status_t error = fCachedBlock->Flush();
if (error != B_OK) {
fCachedBlock->Unset();
return error;
}
}
return B_OK;
}
status_t
Volume::_ClusterAllocated(uint32 cluster)
{
// update the FS info
// exists only for FAT32
if (fFatBits != 32)
return B_OK;
off_t offset = fBytesPerSector * fFSInfoSector;
status_t error = fCachedBlock->SetTo(offset / BlockSize(),
CachedBlock::READ);
if (error != B_OK)
return error;
uint8* buffer = fCachedBlock->Block() + offset % BlockSize();
// update number of free cluster
int32 freeClusters = read32(buffer, 0x1e8);
if (freeClusters != -1)
write32(buffer, 0x1e8, freeClusters - 1);
// update number of most recently allocated cluster
write32(buffer, 0x1ec, cluster);
// write the block back
error = fCachedBlock->Flush();
if (error != B_OK)
fCachedBlock->Unset();
return error;
}
// #pragma mark -

View File

@ -43,17 +43,24 @@ class Volume {
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;
status_t AllocateCluster(uint32 previousCluster,
uint32& _newCluster);
off_t ClusterToOffset(uint32 cluster) const;
// uint32 ToCluster(off_t offset) const { return offset >> ClusterShift(); }
off_t ToOffset(off_t block) const { return block << BlockShift(); }
off_t BlockToOffset(off_t block) const
{ return block << BlockShift(); }
uint32 ToBlock(off_t offset) const { return offset >> BlockShift(); }
private:
status_t _UpdateCluster(uint32 cluster, uint32 value);
status_t _ClusterAllocated(uint32 cluster);
protected:
int fDevice;
@ -75,7 +82,8 @@ class Volume {
uint32 fDataStart;
uint32 fTotalClusters;
uint32 fRootDirCluster;
uint16 fFSInfoSector;
CachedBlock *fCachedBlock;
Directory *fRoot;
};

View File

@ -27,6 +27,12 @@ class Volume;
#define read16(buffer,off) \
B_LENDIAN_TO_HOST_INT16(*(uint16 *)&buffer[off])
#define write32(buffer, off, value) \
*(uint32*)&buffer[off] = B_HOST_TO_LENDIAN_INT32(value)
#define write16(buffer, off, value) \
*(uint16*)&buffer[off] = B_HOST_TO_LENDIAN_INT16(value)
enum name_lengths {
FATFS_BASENAME_LENGTH = 8,
FATFS_EXTNAME_LENGTH = 3,

View File

@ -6,6 +6,7 @@
#include "menu.h"
#include <errno.h>
#include <string.h>
#include <algorithm>
@ -16,6 +17,7 @@
#include <boot/stage2.h>
#include <boot/vfs.h>
#include <boot/platform.h>
#include <boot/platform/generic/text_console.h>
#include <boot/stdio.h>
#include <safemode.h>
#include <util/kernel_cpp.h>
@ -359,6 +361,57 @@ Menu::Run()
// #pragma mark -
static const char*
size_to_string(off_t size, char* buffer, size_t bufferSize)
{
static const char* const kPrefixes[] = { "K", "M", "G", "T", "P", NULL };
int32 nextIndex = 0;
int32 remainder = 0;
while (size >= 1024 && kPrefixes[nextIndex] != NULL) {
remainder = size % 1024;
size /= 1024;
nextIndex++;
if (size < 1024) {
// Compute the decimal remainder and make sure we have at most
// 3 decimal places (or 4 for 1000 <= size <= 1023).
int32 factor;
if (size >= 100)
factor = 100;
else if (size >= 10)
factor = 10;
else
factor = 1;
remainder = (remainder * 1000 + 5 * factor) / 1024;
if (remainder >= 1000) {
size++;
remainder = 0;
} else
remainder /= 10 * factor;
} else
size += (remainder + 512) / 1024;
}
if (remainder == 0) {
snprintf(buffer, bufferSize, "%" B_PRIdOFF, size);
} else {
snprintf(buffer, bufferSize, "%" B_PRIdOFF ".%" B_PRId32, size,
remainder);
}
size_t length = strlen(buffer);
snprintf(buffer + length, bufferSize - length, " %sB",
nextIndex == 0 ? "" : kPrefixes[nextIndex - 1]);
return buffer;
}
// #pragma mark -
static bool
user_menu_boot_volume(Menu *menu, MenuItem *item)
{
@ -412,6 +465,58 @@ debug_menu_display_syslog(Menu *menu, MenuItem *item)
}
static status_t
save_syslog_to_volume(Directory* directory)
{
// find an unused name
char name[16];
bool found = false;
for (int i = 0; i < 99; i++) {
snprintf(name, sizeof(name), "SYSLOG%02d.TXT", i);
Node* node = directory->Lookup(name, false);
if (node == NULL) {
found = true;
break;
}
node->Release();
}
if (!found) {
printf("Failed to find an unused name for the syslog file!\n");
return B_ERROR;
}
printf("Writing syslog to file \"%s\" ...\n", name);
int fd = open_from(directory, name, O_RDWR | O_CREAT | O_EXCL, 0644);
if (fd < 0) {
printf("Failed to create syslog file!\n");
return fd;
}
ring_buffer* syslogBuffer = (ring_buffer*)gKernelArgs.debug_output;
iovec vecs[2];
int32 vecCount = ring_buffer_get_vecs(syslogBuffer, vecs);
if (vecCount > 0) {
size_t toWrite = ring_buffer_readable(syslogBuffer);
ssize_t written = writev(fd, vecs, vecCount);
if (written < 0 || (size_t)written != toWrite) {
printf("Failed to write to the syslog file \"%s\"!\n", name);
close(fd);
return errno;
}
}
close(fd);
printf("Successfully wrote syslog file.\n");
return B_OK;
}
static bool
debug_menu_toggle_debug_syslog(Menu *menu, MenuItem *item)
{
@ -420,6 +525,22 @@ debug_menu_toggle_debug_syslog(Menu *menu, MenuItem *item)
}
static bool
debug_menu_save_syslog(Menu *menu, MenuItem *item)
{
Directory* volume = (Directory*)item->Data();
console_clear_screen();
save_syslog_to_volume(volume);
printf("\nPress any key to continue\n");
console_wait_for_key();
return true;
}
static Menu *
add_boot_volume_menu(Directory *bootVolume)
{
@ -511,10 +632,80 @@ add_safe_mode_menu()
}
static Menu*
add_save_debug_syslog_menu()
{
Menu* menu = new(nothrow) Menu(STANDARD_MENU, "Save syslog to volume ...");
MenuItem* item;
const char* const kHelpText = "Currently only FAT32 volumes are supported. "
"Newly plugged in removable devices are only recognized after "
"rebooting.";
int32 itemsAdded = 0;
void* cookie;
if (gRoot->Open(&cookie, O_RDONLY) == B_OK) {
Node* node;
while (gRoot->GetNextNode(cookie, &node) == B_OK) {
Directory* volume = static_cast<Directory*>(node);
Partition* partition;
if (gRoot->GetPartitionFor(volume, &partition) != B_OK)
continue;
// we support only FAT32 volumes ATM
if (partition->content_type == NULL
|| strcmp(partition->content_type, kPartitionTypeFAT32) != 0) {
continue;
}
char name[B_FILE_NAME_LENGTH];
if (volume->GetName(name, sizeof(name)) != B_OK)
strcpy(name, "unnamed");
// append offset, size, and type to the name
size_t len = strlen(name);
char offsetBuffer[32];
char sizeBuffer[32];
snprintf(name + len, sizeof(name) - len,
" (%s, offset %s, size %s)", partition->content_type,
size_to_string(partition->offset, offsetBuffer,
sizeof(offsetBuffer)),
size_to_string(partition->size, sizeBuffer,
sizeof(sizeBuffer)));
item = new(nothrow) MenuItem(name);
item->SetData(volume);
item->SetTarget(&debug_menu_save_syslog);
item->SetType(MENU_ITEM_NO_CHOICE);
item->SetHelpText(kHelpText);
menu->AddItem(item);
itemsAdded++;
}
gRoot->Close(cookie);
}
if (itemsAdded == 0) {
menu->AddItem(item
= new(nothrow) MenuItem("No supported volumes found"));
item->SetType(MENU_ITEM_NO_CHOICE);
item->SetHelpText(kHelpText);
item->SetEnabled(false);
}
menu->AddSeparatorItem();
menu->AddItem(item = new(nothrow) MenuItem("Return to debug menu"));
item->SetHelpText(kHelpText);
return menu;
}
static Menu *
add_debug_menu()
{
Menu *menu = new(nothrow) Menu(SAFE_MODE_MENU, "Debug Options");
Menu *menu = new(nothrow) Menu(STANDARD_MENU, "Debug Options");
MenuItem *item;
#if DEBUG_SPINLOCK_LATENCIES
@ -558,6 +749,11 @@ add_debug_menu()
item->SetType(MENU_ITEM_NO_CHOICE);
item->SetHelpText(
"Displays the syslog from the previous Haiku session.");
menu->AddItem(item = new(nothrow) MenuItem(
"Save syslog from previous session", add_save_debug_syslog_menu()));
item->SetHelpText("Saves the syslog from the previous Haiku session to "
"disk. Currently only FAT32 volumes are supported.");
}
menu->AddSeparatorItem();
@ -625,10 +821,9 @@ user_menu(Directory **_bootVolume)
platform_add_menus(menu);
menu->AddSeparatorItem();
if (*_bootVolume == NULL) {
menu->AddItem(item = new(nothrow) MenuItem("Reboot"));
item->SetTarget(user_menu_reboot);
}
menu->AddItem(item = new(nothrow) MenuItem("Reboot"));
item->SetTarget(user_menu_reboot);
menu->AddItem(item = new(nothrow) MenuItem("Continue booting"));
if (*_bootVolume == NULL) {

View File

@ -4,19 +4,22 @@
*/
#include "RootFileSystem.h"
#include <boot/partitions.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <boot/FileMapDisk.h>
#include <boot/partitions.h>
#include <boot/vfs.h>
#include <boot/platform.h>
#include <boot/stage2.h>
#include <boot/stdio.h>
#include <boot/vfs.h>
#include <ddm_modules.h>
#include <util/kernel_cpp.h>
#include <unistd.h>
#include <string.h>
#include "RootFileSystem.h"
using namespace boot;
@ -161,7 +164,8 @@ Partition::ReadAt(void *cookie, off_t position, void *buffer, size_t bufferSize)
if (position + bufferSize > this->size)
bufferSize = this->size - position;
return read_pos(fFD, this->offset + position, buffer, bufferSize);
ssize_t result = read_pos(fFD, this->offset + position, buffer, bufferSize);
return result < 0 ? errno : result;
}
@ -177,7 +181,9 @@ Partition::WriteAt(void *cookie, off_t position, const void *buffer,
if (position + bufferSize > this->size)
bufferSize = this->size - position;
return write_pos(fFD, this->offset + position, buffer, bufferSize);
ssize_t result = write_pos(fFD, this->offset + position, buffer,
bufferSize);
return result < 0 ? errno : result;
}
@ -231,8 +237,9 @@ Partition::_Mount(file_system_module_info *module, Directory **_fileSystem)
if (_fileSystem)
*_fileSystem = fileSystem;
// remember the module name that mounted us
// remember the module that mounted us
fModuleName = module->module_name;
this->content_type = module->pretty_name;
fIsFileSystem = true;
@ -386,7 +393,7 @@ Partition::Scan(bool mountFileSystems, bool isBootDevice)
delete child;
}
// remember the module name that identified us
// remember the name of the module that identified us
fModuleName = bestModule->module.name;
return B_OK;

View File

@ -8,6 +8,7 @@
#include <boot/stdio.h>
#include <util/kernel_cpp.h>
#include <errno.h>
#include <stdarg.h>
#include <string.h>
@ -19,8 +20,16 @@
//extern FILE *stdout;
//extern FILE *stdin;
#undef errno
int errno;
int *_errnop = &errno;
int*
_errnop(void)
{
return &errno;
}
int

View File

@ -4,19 +4,25 @@
*/
#include "RootFileSystem.h"
#include <boot/vfs.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <sys/uio.h>
#include <unistd.h>
#include <StorageDefs.h>
#include <boot/vfs.h>
#include <boot/platform.h>
#include <boot/partitions.h>
#include <boot/stdio.h>
#include <boot/stage2.h>
#include <util/kernel_cpp.h>
#include <syscall_utils.h>
#include "RootFileSystem.h"
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
using namespace boot;
@ -92,7 +98,7 @@ Node::Close(void *cookie)
}
status_t
status_t
Node::GetName(char *nameBuffer, size_t bufferSize) const
{
return B_ERROR;
@ -106,14 +112,14 @@ Node::GetFileMap(struct file_map_run *runs, int32 *count)
}
int32
int32
Node::Type() const
{
return 0;
}
off_t
off_t
Node::Size() const
{
return 0LL;
@ -127,7 +133,7 @@ Node::Inode() const
}
status_t
status_t
Node::Acquire()
{
fRefCount++;
@ -136,7 +142,7 @@ Node::Acquire()
}
status_t
status_t
Node::Release()
{
TRACE(("%p::Release(), fRefCount = %ld\n", this, fRefCount));
@ -182,27 +188,34 @@ Directory::Directory()
}
ssize_t
ssize_t
Directory::ReadAt(void *cookie, off_t pos, void *buffer, size_t bufferSize)
{
return B_ERROR;
}
ssize_t
ssize_t
Directory::WriteAt(void *cookie, off_t pos, const void *buffer, size_t bufferSize)
{
return B_ERROR;
}
int32
int32
Directory::Type() const
{
return S_IFDIR;
}
status_t
Directory::CreateFile(const char *name, mode_t permissions, Node **_node)
{
return EROFS;
}
// #pragma mark -
@ -273,7 +286,7 @@ Descriptor::~Descriptor()
}
ssize_t
ssize_t
Descriptor::Read(void *buffer, size_t bufferSize)
{
ssize_t bytesRead = fNode->ReadAt(fCookie, fOffset, buffer, bufferSize);
@ -284,7 +297,7 @@ Descriptor::Read(void *buffer, size_t bufferSize)
}
ssize_t
ssize_t
Descriptor::ReadAt(off_t pos, void *buffer, size_t bufferSize)
{
return fNode->ReadAt(fCookie, pos, buffer, bufferSize);
@ -359,7 +372,7 @@ status_t
register_boot_file_system(Directory *volume)
{
gRoot->AddLink("boot", volume);
Partition *partition;
status_t status = gRoot->GetPartitionFor(volume, &partition);
if (status != B_OK) {
@ -497,17 +510,12 @@ mount_file_systems(stage2_args *args)
}
/*! Resolves \a directory + \a path to a node.
Note that \a path will be modified by the function.
*/
static status_t
get_node_for_path(Directory *directory, const char *pathName, Node **_node)
get_node_for_path(Directory *directory, char *path, Node **_node)
{
char pathBuffer[B_PATH_NAME_LENGTH];
char *path = pathBuffer;
if (pathName == NULL)
return B_BAD_VALUE;
strlcpy(path, pathName, sizeof(pathBuffer));
directory->Acquire();
// balance Acquire()/Release() calls
@ -584,7 +592,7 @@ open_node(Node *node, int mode)
return B_ERROR;
// get free descriptor
int fd = 0;
for (; fd < MAX_VFS_DESCRIPTORS; fd++) {
if (sDescriptors[fd] == NULL)
@ -596,7 +604,7 @@ open_node(Node *node, int mode)
TRACE(("got descriptor %d for node %p\n", fd, node));
// we got a free descriptor entry, now try to open the node
void *cookie;
status_t status = node->Open(&cookie, mode);
if (status < B_OK)
@ -619,10 +627,10 @@ dup(int fd)
{
Descriptor *descriptor = get_descriptor(fd);
if (descriptor == NULL)
return B_FILE_ERROR;
RETURN_AND_SET_ERRNO(B_FILE_ERROR);
descriptor->Acquire();
return fd;
RETURN_AND_SET_ERRNO(fd);
}
@ -631,9 +639,9 @@ read_pos(int fd, off_t offset, void *buffer, size_t bufferSize)
{
Descriptor *descriptor = get_descriptor(fd);
if (descriptor == NULL)
return B_FILE_ERROR;
RETURN_AND_SET_ERRNO(B_FILE_ERROR);
return descriptor->ReadAt(offset, buffer, bufferSize);
RETURN_AND_SET_ERRNO(descriptor->ReadAt(offset, buffer, bufferSize));
}
@ -642,9 +650,9 @@ read(int fd, void *buffer, size_t bufferSize)
{
Descriptor *descriptor = get_descriptor(fd);
if (descriptor == NULL)
return B_FILE_ERROR;
RETURN_AND_SET_ERRNO(B_FILE_ERROR);
return descriptor->Read(buffer, bufferSize);
RETURN_AND_SET_ERRNO(descriptor->Read(buffer, bufferSize));
}
@ -653,9 +661,9 @@ write_pos(int fd, off_t offset, const void *buffer, size_t bufferSize)
{
Descriptor *descriptor = get_descriptor(fd);
if (descriptor == NULL)
return B_FILE_ERROR;
RETURN_AND_SET_ERRNO(B_FILE_ERROR);
return descriptor->WriteAt(offset, buffer, bufferSize);
RETURN_AND_SET_ERRNO(descriptor->WriteAt(offset, buffer, bufferSize));
}
@ -664,32 +672,51 @@ write(int fd, const void *buffer, size_t bufferSize)
{
Descriptor *descriptor = get_descriptor(fd);
if (descriptor == NULL)
return B_FILE_ERROR;
RETURN_AND_SET_ERRNO(B_FILE_ERROR);
return descriptor->Write(buffer, bufferSize);
RETURN_AND_SET_ERRNO(descriptor->Write(buffer, bufferSize));
}
ssize_t
writev(int fd, const struct iovec* vecs, size_t count)
{
size_t totalWritten = 0;
for (size_t i = 0; i < count; i++) {
ssize_t written = write(fd, vecs[i].iov_base, vecs[i].iov_len);
if (written < 0)
return totalWritten == 0 ? written : totalWritten;
totalWritten += written;
if ((size_t)written != vecs[i].iov_len)
break;
}
return totalWritten;
}
int
open(const char *name, int mode, ...)
{
mode_t permissions = 0;
if ((mode & O_CREAT) != 0) {
va_list args;
va_start(args, mode);
permissions = va_arg(args, int) /*& ~__gUmask*/;
// adapt the permissions as required by POSIX
va_end(args);
}
// we always start at the top (there is no notion of a current directory (yet?))
if (name[0] == '/')
name++;
Node *node;
if (get_node_for_path(gRoot, name, &node) < B_OK)
return B_ENTRY_NOT_FOUND;
int fd = open_node(node, mode);
node->Release();
return fd;
RETURN_AND_SET_ERRNO(open_from(gRoot, name, mode, permissions));
}
int
open_from(Directory *directory, const char *name, int mode)
open_from(Directory *directory, const char *name, int mode, mode_t permissions)
{
if (name[0] == '/') {
// ignore the directory and start from root if we are asked to do that
@ -697,9 +724,51 @@ open_from(Directory *directory, const char *name, int mode)
name++;
}
char path[B_PATH_NAME_LENGTH];
if (strlcpy(path, name, sizeof(path)) >= sizeof(path))
return B_NAME_TOO_LONG;
Node *node;
if (get_node_for_path(directory, name, &node) < B_OK)
return B_ENTRY_NOT_FOUND;
status_t error = get_node_for_path(directory, path, &node);
if (error != B_OK) {
if (error != B_ENTRY_NOT_FOUND)
return error;
if ((mode & O_CREAT) == 0)
return B_ENTRY_NOT_FOUND;
// try to resolve the parent directory
strlcpy(path, name, sizeof(path));
if (char* lastSlash = strrchr(path, '/')) {
if (lastSlash[1] == '\0')
return B_ENTRY_NOT_FOUND;
lastSlash = '\0';
name = lastSlash + 1;
// resolve the directory
if (get_node_for_path(directory, path, &node) != B_OK)
return B_ENTRY_NOT_FOUND;
if (node->Type() != S_IFDIR) {
node->Release();
return B_NOT_A_DIRECTORY;
}
directory = static_cast<Directory*>(node);
} else
directory->Acquire();
// create the file
error = directory->CreateFile(name, permissions, &node);
directory->Release();
if (error != B_OK)
return error;
} else if ((mode & O_EXCL) != 0) {
node->Release();
return B_FILE_EXISTS;
}
int fd = open_node(node, mode);
@ -729,13 +798,13 @@ close(int fd)
{
Descriptor *descriptor = get_descriptor(fd);
if (descriptor == NULL)
return B_FILE_ERROR;
RETURN_AND_SET_ERRNO(B_FILE_ERROR);
status_t status = descriptor->Release();
if (!descriptor->RefCount())
free_descriptor(fd);
return status;
RETURN_AND_SET_ERRNO(status);
}
@ -748,11 +817,11 @@ fstat(int fd, struct stat *stat)
#endif
{
if (stat == NULL)
return B_BAD_VALUE;
RETURN_AND_SET_ERRNO(B_BAD_VALUE);
Descriptor *descriptor = get_descriptor(fd);
if (descriptor == NULL)
return B_FILE_ERROR;
RETURN_AND_SET_ERRNO(B_FILE_ERROR);
return descriptor->Stat(*stat);
RETURN_AND_SET_ERRNO(descriptor->Stat(*stat));
}