* 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:
parent
d1367a37cc
commit
085cf27b40
@ -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);
|
||||
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
{
|
||||
|
@ -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;
|
||||
|
@ -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 -
|
||||
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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,
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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));
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user