* 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:
@@ -61,6 +61,9 @@ class Directory : public Node {
|
|||||||
virtual status_t GetNextNode(void *cookie, Node **_node) = 0;
|
virtual status_t GetNextNode(void *cookie, Node **_node) = 0;
|
||||||
virtual status_t Rewind(void *cookie) = 0;
|
virtual status_t Rewind(void *cookie) = 0;
|
||||||
virtual bool IsEmpty() = 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
|
/** 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 Directory *get_boot_file_system(stage2_args *args);
|
||||||
extern status_t mount_file_systems(stage2_args *args);
|
extern status_t mount_file_systems(stage2_args *args);
|
||||||
extern int open_node(Node *node, int mode);
|
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);
|
extern Node *get_node_from(int fd);
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ UsePrivateHeaders shared storage ;
|
|||||||
BOOT_SUPPORT_FILE_SYSTEM_BFS
|
BOOT_SUPPORT_FILE_SYSTEM_BFS
|
||||||
BOOT_SUPPORT_FILE_SYSTEM_TARFS
|
BOOT_SUPPORT_FILE_SYSTEM_TARFS
|
||||||
#BOOT_SUPPORT_FILE_MAP_DISK
|
#BOOT_SUPPORT_FILE_MAP_DISK
|
||||||
|
BOOT_SUPPORT_FILE_SYSTEM_FAT
|
||||||
;
|
;
|
||||||
|
|
||||||
# Add architecture specific partition/file system modules
|
# Add architecture specific partition/file system modules
|
||||||
|
|||||||
@@ -6,11 +6,12 @@
|
|||||||
|
|
||||||
#include "File.h"
|
#include "File.h"
|
||||||
|
|
||||||
#include <util/kernel_cpp.h>
|
#include <errno.h>
|
||||||
|
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include <util/kernel_cpp.h>
|
||||||
|
|
||||||
|
|
||||||
namespace FFS {
|
namespace FFS {
|
||||||
|
|
||||||
@@ -18,7 +19,7 @@ class Stream {
|
|||||||
public:
|
public:
|
||||||
Stream(int device, FileBlock &node);
|
Stream(int device, FileBlock &node);
|
||||||
~Stream();
|
~Stream();
|
||||||
|
|
||||||
status_t InitCheck();
|
status_t InitCheck();
|
||||||
ssize_t ReadAt(off_t offset, uint8 *buffer, size_t size);
|
ssize_t ReadAt(off_t offset, uint8 *buffer, size_t size);
|
||||||
|
|
||||||
@@ -55,35 +56,35 @@ Stream::~Stream()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
Stream::InitCheck()
|
Stream::InitCheck()
|
||||||
{
|
{
|
||||||
return fBlock.BlockData() != NULL ? B_OK : B_NO_MEMORY;
|
return fBlock.BlockData() != NULL ? B_OK : B_NO_MEMORY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int32
|
int32
|
||||||
Stream::BlockOffset(off_t offset) const
|
Stream::BlockOffset(off_t offset) const
|
||||||
{
|
{
|
||||||
return offset % fNode.BlockSize();
|
return offset % fNode.BlockSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int32
|
int32
|
||||||
Stream::BlockIndex(off_t offset) const
|
Stream::BlockIndex(off_t offset) const
|
||||||
{
|
{
|
||||||
return (offset % (fNode.BlockSize() * fNode.NumDataBlocks())) / fNode.BlockSize();
|
return (offset % (fNode.BlockSize() * fNode.NumDataBlocks())) / fNode.BlockSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int32
|
int32
|
||||||
Stream::ExtensionBlockOffset(off_t offset) const
|
Stream::ExtensionBlockOffset(off_t offset) const
|
||||||
{
|
{
|
||||||
return offset / (fNode.BlockSize() * fNode.NumDataBlocks());
|
return offset / (fNode.BlockSize() * fNode.NumDataBlocks());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
Stream::ReadNextExtension()
|
Stream::ReadNextExtension()
|
||||||
{
|
{
|
||||||
int32 next;
|
int32 next;
|
||||||
@@ -99,7 +100,7 @@ Stream::ReadNextExtension()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
ssize_t
|
ssize_t
|
||||||
Stream::ReadAt(off_t offset, uint8 *buffer, size_t size)
|
Stream::ReadAt(off_t offset, uint8 *buffer, size_t size)
|
||||||
{
|
{
|
||||||
if (offset < 0)
|
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,
|
ssize_t bytesRead = read_pos(fDevice, block * fNode.BlockSize() + blockOffset,
|
||||||
buffer, toRead);
|
buffer, toRead);
|
||||||
if (bytesRead < B_OK)
|
if (bytesRead < 0)
|
||||||
return bytesRead;
|
return errno;
|
||||||
|
|
||||||
bytesLeft -= bytesRead;
|
bytesLeft -= bytesRead;
|
||||||
buffer += bytesRead;
|
buffer += bytesRead;
|
||||||
@@ -172,7 +173,7 @@ File::~File()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
File::InitCheck()
|
File::InitCheck()
|
||||||
{
|
{
|
||||||
if (!fNode.IsFile())
|
if (!fNode.IsFile())
|
||||||
@@ -182,7 +183,7 @@ File::InitCheck()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
File::Open(void **_cookie, int mode)
|
File::Open(void **_cookie, int mode)
|
||||||
{
|
{
|
||||||
Stream *stream = new(nothrow) Stream(fVolume.Device(), fNode);
|
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)
|
File::Close(void *cookie)
|
||||||
{
|
{
|
||||||
Stream *stream = (Stream *)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)
|
File::ReadAt(void *cookie, off_t pos, void *buffer, size_t bufferSize)
|
||||||
{
|
{
|
||||||
Stream *stream = (Stream *)cookie;
|
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)
|
File::WriteAt(void *cookie, off_t pos, const void *buffer, size_t bufferSize)
|
||||||
{
|
{
|
||||||
return EROFS;
|
return EROFS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
File::GetName(char *nameBuffer, size_t bufferSize) const
|
File::GetName(char *nameBuffer, size_t bufferSize) const
|
||||||
{
|
{
|
||||||
return fNode.GetName(nameBuffer, bufferSize);
|
return fNode.GetName(nameBuffer, bufferSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int32
|
int32
|
||||||
File::Type() const
|
File::Type() const
|
||||||
{
|
{
|
||||||
return S_IFREG;
|
return S_IFREG;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
off_t
|
off_t
|
||||||
File::Size() const
|
File::Size() const
|
||||||
{
|
{
|
||||||
return fNode.Size();
|
return fNode.Size();
|
||||||
|
|||||||
@@ -8,16 +8,14 @@
|
|||||||
|
|
||||||
|
|
||||||
#include "CachedBlock.h"
|
#include "CachedBlock.h"
|
||||||
#include "Stream.h"
|
|
||||||
#include "Directory.h"
|
|
||||||
#include "File.h"
|
|
||||||
|
|
||||||
#include <util/kernel_cpp.h>
|
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include <util/kernel_cpp.h>
|
||||||
|
|
||||||
|
|
||||||
using namespace FATFS;
|
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
|
#ifndef CACHED_BLOCK_H
|
||||||
#define CACHED_BLOCK_H
|
#define CACHED_BLOCK_H
|
||||||
|
|
||||||
|
|
||||||
#include <SupportDefs.h>
|
#include <SupportDefs.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
@@ -15,15 +16,25 @@
|
|||||||
|
|
||||||
#include "Volume.h"
|
#include "Volume.h"
|
||||||
|
|
||||||
|
|
||||||
namespace FATFS {
|
namespace FATFS {
|
||||||
|
|
||||||
class CachedBlock {
|
class CachedBlock {
|
||||||
|
public:
|
||||||
|
enum {
|
||||||
|
READ = 0x01,
|
||||||
|
CLEAR = 0x02,
|
||||||
|
FORCE = 0x04,
|
||||||
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
CachedBlock(Volume &volume);
|
CachedBlock(Volume &volume);
|
||||||
CachedBlock(Volume &volume, off_t block);
|
CachedBlock(Volume &volume, off_t block);
|
||||||
~CachedBlock();
|
~CachedBlock();
|
||||||
|
|
||||||
uint8 *SetTo(off_t block);
|
uint8 *SetTo(off_t block);
|
||||||
|
status_t SetTo(off_t blockNumber, uint32 flags);
|
||||||
|
status_t Flush();
|
||||||
|
|
||||||
void Unset();
|
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
|
} // namespace FATFS
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -5,16 +5,21 @@
|
|||||||
|
|
||||||
|
|
||||||
#include "Directory.h"
|
#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 <StorageDefs.h>
|
||||||
#include <util/kernel_cpp.h>
|
#include <util/kernel_cpp.h>
|
||||||
|
|
||||||
#include <string.h>
|
#include "CachedBlock.h"
|
||||||
#include <unistd.h>
|
#include "File.h"
|
||||||
#include <stdint.h>
|
#include "Volume.h"
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
//#define TRACE(x) dprintf x
|
//#define TRACE(x) dprintf x
|
||||||
#define TRACE(x) do {} while (0)
|
#define TRACE(x) do {} while (0)
|
||||||
@@ -27,9 +32,12 @@ struct dir_entry {
|
|||||||
const char *Extension() const { return fExt; };
|
const char *Extension() const { return fExt; };
|
||||||
uint8 Flags() const { return fFlags; };
|
uint8 Flags() const { return fFlags; };
|
||||||
uint32 Cluster(int32 fatBits) const;
|
uint32 Cluster(int32 fatBits) const;
|
||||||
|
void SetCluster(uint32 cluster, int32 fatBits);
|
||||||
uint32 Size() const { return B_LENDIAN_TO_HOST_INT32(fSize); };
|
uint32 Size() const { return B_LENDIAN_TO_HOST_INT32(fSize); };
|
||||||
|
void SetSize(uint32 size);
|
||||||
bool IsFile() const;
|
bool IsFile() const;
|
||||||
bool IsDir() const;
|
bool IsDir() const;
|
||||||
|
|
||||||
char fName[8];
|
char fName[8];
|
||||||
char fExt[3];
|
char fExt[3];
|
||||||
uint8 fFlags;
|
uint8 fFlags;
|
||||||
@@ -55,6 +63,23 @@ dir_entry::Cluster(int32 fatBits) const
|
|||||||
return c;
|
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
|
bool
|
||||||
dir_entry::IsFile() const
|
dir_entry::IsFile() const
|
||||||
{
|
{
|
||||||
@@ -70,16 +95,211 @@ dir_entry::IsDir() const
|
|||||||
|
|
||||||
|
|
||||||
struct dir_cookie {
|
struct dir_cookie {
|
||||||
|
enum {
|
||||||
|
MAX_UTF16_NAME_LENGTH = 255
|
||||||
|
};
|
||||||
|
|
||||||
int32 index;
|
int32 index;
|
||||||
struct dir_entry entry;
|
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); }
|
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),
|
fVolume(volume),
|
||||||
fStream(volume, cluster, UINT32_MAX, name)
|
fStream(volume, cluster, UINT32_MAX, name),
|
||||||
|
fDirEntryOffset(dirEntryOffset)
|
||||||
{
|
{
|
||||||
TRACE(("FASFS::Directory::(, %lu, %s)\n", cluster, name));
|
TRACE(("FASFS::Directory::(, %lu, %s)\n", cluster, name));
|
||||||
}
|
}
|
||||||
@@ -91,7 +311,7 @@ Directory::~Directory()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
Directory::InitCheck()
|
Directory::InitCheck()
|
||||||
{
|
{
|
||||||
status_t err;
|
status_t err;
|
||||||
@@ -102,7 +322,7 @@ Directory::InitCheck()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
Directory::Open(void **_cookie, int mode)
|
Directory::Open(void **_cookie, int mode)
|
||||||
{
|
{
|
||||||
TRACE(("FASFS::Directory::%s(, %d)\n", __FUNCTION__, mode));
|
TRACE(("FASFS::Directory::%s(, %d)\n", __FUNCTION__, mode));
|
||||||
@@ -113,13 +333,14 @@ Directory::Open(void **_cookie, int mode)
|
|||||||
return B_NO_MEMORY;
|
return B_NO_MEMORY;
|
||||||
|
|
||||||
c->index = -1;
|
c->index = -1;
|
||||||
|
c->entryOffset = 0;
|
||||||
|
|
||||||
*_cookie = (void *)c;
|
*_cookie = (void *)c;
|
||||||
return B_OK;
|
return B_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
Directory::Close(void *cookie)
|
Directory::Close(void *cookie)
|
||||||
{
|
{
|
||||||
TRACE(("FASFS::Directory::%s()\n", __FUNCTION__));
|
TRACE(("FASFS::Directory::%s()\n", __FUNCTION__));
|
||||||
@@ -138,67 +359,40 @@ Directory::Lookup(const char *name, bool traverseLinks)
|
|||||||
Acquire();
|
Acquire();
|
||||||
return this;
|
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;
|
status_t err;
|
||||||
struct dir_cookie cookie;
|
struct dir_cookie cookie;
|
||||||
struct dir_cookie *c = &cookie;
|
struct dir_cookie *c = &cookie;
|
||||||
c->index = -1;
|
c->index = -1;
|
||||||
|
c->entryOffset = 0;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
err = GetNextEntry(c);
|
err = GetNextEntry(c);
|
||||||
if (err < B_OK)
|
if (err < B_OK)
|
||||||
return NULL;
|
return NULL;
|
||||||
TRACE(("FASFS::Directory::%s: %s <> '%8.8s.%3.3s'\n", __FUNCTION__,
|
TRACE(("FASFS::Directory::%s: %s <> '%s'\n", __FUNCTION__,
|
||||||
name, c->entry.BaseName(), c->entry.Extension()));
|
name, c->Name()));
|
||||||
int i;
|
if (strcasecmp(name, c->Name()) == 0) {
|
||||||
for (i = 0; i < FATFS_BASENAME_LENGTH; i++)
|
TRACE(("GOT IT!\n"));
|
||||||
if (c->entry.BaseName()[i] == ' ')
|
break;
|
||||||
break;
|
}
|
||||||
int nlen = MIN(i,FATFS_BASENAME_LENGTH);
|
|
||||||
for (i = 0; i < FATFS_EXTNAME_LENGTH; i++)
|
|
||||||
if (c->entry.Extension()[i] == ' ')
|
|
||||||
break;
|
|
||||||
int elen = MIN(i,FATFS_EXTNAME_LENGTH);
|
|
||||||
if (nlen != baselen)
|
|
||||||
continue;
|
|
||||||
if (elen != extlen)
|
|
||||||
continue;
|
|
||||||
if (strncasecmp(name, c->entry.BaseName(), nlen))
|
|
||||||
continue;
|
|
||||||
if (strncasecmp(ext, c->entry.Extension(), elen))
|
|
||||||
continue;
|
|
||||||
TRACE(("GOT IT!\n"));
|
|
||||||
break;
|
|
||||||
} while (true);
|
} while (true);
|
||||||
|
|
||||||
if (c->entry.IsFile()) {
|
if (c->entry.IsFile()) {
|
||||||
TRACE(("IS FILE\n"));
|
TRACE(("IS FILE\n"));
|
||||||
return new File(fVolume, c->entry.Cluster(fVolume.FatBits()),
|
return new File(fVolume, c->entryOffset,
|
||||||
c->entry.Size(), name);
|
c->entry.Cluster(fVolume.FatBits()), c->entry.Size(), name);
|
||||||
}
|
}
|
||||||
if (c->entry.IsDir()) {
|
if (c->entry.IsDir()) {
|
||||||
TRACE(("IS DIR\n"));
|
TRACE(("IS DIR\n"));
|
||||||
return new Directory(fVolume, c->entry.Cluster(fVolume.FatBits()),
|
return new Directory(fVolume, c->entryOffset,
|
||||||
name);
|
c->entry.Cluster(fVolume.FatBits()), name);
|
||||||
}
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
Directory::GetNextEntry(void *cookie, char *name, size_t size)
|
Directory::GetNextEntry(void *cookie, char *name, size_t size)
|
||||||
{
|
{
|
||||||
TRACE(("FASFS::Directory::%s()\n", __FUNCTION__));
|
TRACE(("FASFS::Directory::%s()\n", __FUNCTION__));
|
||||||
@@ -209,14 +403,12 @@ Directory::GetNextEntry(void *cookie, char *name, size_t size)
|
|||||||
if (err < B_OK)
|
if (err < B_OK)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
strlcpy(name, c->entry.fName, MIN(size, FATFS_BASENAME_LENGTH));
|
strlcpy(name, c->Name(), size);
|
||||||
strlcpy(name, ".", size);
|
|
||||||
strlcpy(name, c->entry.fExt, MIN(size, FATFS_EXTNAME_LENGTH));
|
|
||||||
return B_OK;
|
return B_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
Directory::GetNextNode(void *cookie, Node **_node)
|
Directory::GetNextNode(void *cookie, Node **_node)
|
||||||
{
|
{
|
||||||
return B_ERROR;
|
return B_ERROR;
|
||||||
@@ -229,6 +421,7 @@ Directory::Rewind(void *cookie)
|
|||||||
TRACE(("FASFS::Directory::%s()\n", __FUNCTION__));
|
TRACE(("FASFS::Directory::%s()\n", __FUNCTION__));
|
||||||
struct dir_cookie *c = (struct dir_cookie *)cookie;
|
struct dir_cookie *c = (struct dir_cookie *)cookie;
|
||||||
c->index = -1;
|
c->index = -1;
|
||||||
|
c->entryOffset = 0;
|
||||||
|
|
||||||
return B_OK;
|
return B_OK;
|
||||||
}
|
}
|
||||||
@@ -241,6 +434,7 @@ Directory::IsEmpty()
|
|||||||
struct dir_cookie cookie;
|
struct dir_cookie cookie;
|
||||||
struct dir_cookie *c = &cookie;
|
struct dir_cookie *c = &cookie;
|
||||||
c->index = -1;
|
c->index = -1;
|
||||||
|
c->entryOffset = 0;
|
||||||
if (GetNextEntry(c) == B_OK)
|
if (GetNextEntry(c) == B_OK)
|
||||||
return false;
|
return false;
|
||||||
return true;
|
return true;
|
||||||
@@ -264,36 +458,222 @@ Directory::Inode() const
|
|||||||
return fStream.FirstCluster() << 16;
|
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)
|
Directory::GetNextEntry(void *cookie, uint8 mask, uint8 match)
|
||||||
{
|
{
|
||||||
TRACE(("FASFS::Directory::%s(, %02x, %02x)\n", __FUNCTION__, mask, match));
|
TRACE(("FASFS::Directory::%s(, %02x, %02x)\n", __FUNCTION__, mask, match));
|
||||||
struct dir_cookie *c = (struct dir_cookie *)cookie;
|
struct dir_cookie *c = (struct dir_cookie *)cookie;
|
||||||
|
|
||||||
|
bool hasLongName = false;
|
||||||
|
bool longNameValid = false;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
c->index++;
|
c->index++;
|
||||||
size_t len = sizeof(c->entry);
|
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;
|
return B_ENTRY_NOT_FOUND;
|
||||||
|
}
|
||||||
|
|
||||||
TRACE(("FASFS::Directory::%s: got one entry\n", __FUNCTION__));
|
TRACE(("FASFS::Directory::%s: got one entry\n", __FUNCTION__));
|
||||||
if ((uint8)c->entry.fName[0] == 0x00) // last one
|
if ((uint8)c->entry.fName[0] == 0x00) // last one
|
||||||
return B_ENTRY_NOT_FOUND;
|
return B_ENTRY_NOT_FOUND;
|
||||||
if ((uint8)c->entry.fName[0] == 0xe5) // deleted
|
if ((uint8)c->entry.fName[0] == 0xe5) // deleted
|
||||||
continue;
|
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;
|
continue;
|
||||||
|
}
|
||||||
if ((c->entry.Flags() & (FAT_VOLUME|FAT_SUBDIR)) == FAT_VOLUME) {
|
if ((c->entry.Flags() & (FAT_VOLUME|FAT_SUBDIR)) == FAT_VOLUME) {
|
||||||
// TODO handle Volume name (set fVolume's name)
|
// TODO handle Volume name (set fVolume's name)
|
||||||
continue;
|
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()));
|
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;
|
break;
|
||||||
|
}
|
||||||
} while (true);
|
} while (true);
|
||||||
TRACE(("FATFS::Directory::%s: '%8.8s.%3.3s'\n", __FUNCTION__,
|
TRACE(("FATFS::Directory::%s: '%8.8s.%3.3s'\n", __FUNCTION__,
|
||||||
c->entry.BaseName(), c->entry.Extension()));
|
c->entry.BaseName(), c->entry.Extension()));
|
||||||
return B_OK;
|
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
|
} // namespace FATFS
|
||||||
|
|||||||
@@ -14,16 +14,20 @@
|
|||||||
|
|
||||||
namespace FATFS {
|
namespace FATFS {
|
||||||
|
|
||||||
|
struct dir_entry;
|
||||||
class Volume;
|
class Volume;
|
||||||
|
|
||||||
class Directory : public ::Directory {
|
class Directory : public ::Directory {
|
||||||
public:
|
public:
|
||||||
Directory();
|
Directory();
|
||||||
Directory(Volume &volume, uint32 cluster, const char *name);
|
Directory(Volume &volume, off_t dirEntryOffset, uint32 cluster,
|
||||||
|
const char *name);
|
||||||
virtual ~Directory();
|
virtual ~Directory();
|
||||||
|
|
||||||
status_t InitCheck();
|
status_t InitCheck();
|
||||||
|
|
||||||
|
off_t DirEntryOffset() const { return fDirEntryOffset; }
|
||||||
|
|
||||||
virtual status_t Open(void **_cookie, int mode);
|
virtual status_t Open(void **_cookie, int mode);
|
||||||
virtual status_t Close(void *cookie);
|
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 status_t GetName(char *name, size_t size) const;
|
||||||
virtual ino_t Inode() 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:
|
private:
|
||||||
status_t GetNextEntry(void *cookie,
|
status_t GetNextEntry(void *cookie,
|
||||||
uint8 mask = FAT_VOLUME, uint8 match = 0);
|
uint8 mask = FAT_VOLUME, uint8 match = 0);
|
||||||
|
status_t _AddEntry(dir_entry& entry, off_t& _entryOffset);
|
||||||
|
off_t _GetStreamSize();
|
||||||
|
|
||||||
Volume &fVolume;
|
Volume &fVolume;
|
||||||
Stream fStream;
|
Stream fStream;
|
||||||
|
off_t fDirEntryOffset;
|
||||||
|
|
||||||
typedef ::Directory _inherited;
|
typedef ::Directory _inherited;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,11 +6,13 @@
|
|||||||
|
|
||||||
#include "File.h"
|
#include "File.h"
|
||||||
|
|
||||||
#include <util/kernel_cpp.h>
|
|
||||||
|
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include <util/kernel_cpp.h>
|
||||||
|
|
||||||
|
#include "Directory.h"
|
||||||
|
|
||||||
|
|
||||||
//#define TRACE(x) dprintf x
|
//#define TRACE(x) dprintf x
|
||||||
#define TRACE(x) do {} while (0)
|
#define TRACE(x) do {} while (0)
|
||||||
@@ -19,10 +21,12 @@
|
|||||||
namespace FATFS {
|
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),
|
fVolume(volume),
|
||||||
fStream(volume, cluster, size, name)
|
fStream(volume, cluster, size, name),
|
||||||
|
fDirEntryOffset(dirEntryOffset)
|
||||||
{
|
{
|
||||||
TRACE(("FATFS::File::()\n"));
|
TRACE(("FATFS::File::()\n"));
|
||||||
}
|
}
|
||||||
@@ -51,14 +55,14 @@ File::Open(void **_cookie, int mode)
|
|||||||
if (fStream.InitCheck() < B_OK)
|
if (fStream.InitCheck() < B_OK)
|
||||||
return fStream.InitCheck();
|
return fStream.InitCheck();
|
||||||
|
|
||||||
return B_OK;
|
return Node::Open(_cookie, mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
File::Close(void *cookie)
|
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));
|
TRACE(("FATFS::File::%s(, %Ld,, %d)\n", __FUNCTION__, pos, bufferSize));
|
||||||
status_t err;
|
status_t err;
|
||||||
err = fStream.ReadAt(pos, (uint8 *)buffer, &bufferSize);
|
err = fStream.ReadAt(pos, buffer, &bufferSize);
|
||||||
if (err < B_OK)
|
if (err < B_OK)
|
||||||
return err;
|
return err;
|
||||||
return bufferSize;
|
return bufferSize;
|
||||||
@@ -77,7 +81,25 @@ 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)
|
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
|
File::Type() const
|
||||||
{
|
{
|
||||||
return S_IFREG;
|
return S_IFREG;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
off_t
|
off_t
|
||||||
File::Size() const
|
File::Size() const
|
||||||
{
|
{
|
||||||
return fStream.Size();
|
return fStream.Size();
|
||||||
|
|||||||
@@ -16,11 +16,14 @@ namespace FATFS {
|
|||||||
|
|
||||||
class File : public Node {
|
class File : public Node {
|
||||||
public:
|
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();
|
virtual ~File();
|
||||||
|
|
||||||
status_t InitCheck();
|
status_t InitCheck();
|
||||||
|
|
||||||
|
off_t DirEntryOffset() const { return fDirEntryOffset; }
|
||||||
|
|
||||||
virtual status_t Open(void **_cookie, int mode);
|
virtual status_t Open(void **_cookie, int mode);
|
||||||
virtual status_t Close(void *cookie);
|
virtual status_t Close(void *cookie);
|
||||||
|
|
||||||
@@ -37,6 +40,7 @@ class File : public Node {
|
|||||||
Volume &fVolume;
|
Volume &fVolume;
|
||||||
//FileBlock fNode;
|
//FileBlock fNode;
|
||||||
Stream fStream;
|
Stream fStream;
|
||||||
|
off_t fDirEntryOffset;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace FATFS
|
} // namespace FATFS
|
||||||
|
|||||||
@@ -8,17 +8,20 @@
|
|||||||
|
|
||||||
|
|
||||||
#include "Stream.h"
|
#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 <stdint.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <unistd.h>
|
|
||||||
#include <string.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) dprintf x
|
||||||
#define TRACE(x) do {} while (0)
|
#define TRACE(x) do {} while (0)
|
||||||
@@ -104,11 +107,11 @@ Stream::GetFileMap(struct file_map_run *runs, int32 *count)
|
|||||||
|
|
||||||
|
|
||||||
status_t
|
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));
|
//TRACE(("FATFS::Stream::%s(%Ld,,)\n", __FUNCTION__, pos));
|
||||||
uint32 index = (uint32)(pos / fVolume.ClusterSize());
|
uint32 index = (uint32)(pos / fVolume.ClusterSize());
|
||||||
if (pos >= fSize || index >= fClusterCount)
|
if (pos > fSize || index >= fClusterCount)
|
||||||
return B_BAD_VALUE;
|
return B_BAD_VALUE;
|
||||||
|
|
||||||
uint32 cluster = 0;
|
uint32 cluster = 0;
|
||||||
@@ -147,23 +150,94 @@ Stream::FindBlock(off_t pos, off_t &block, off_t &offset)
|
|||||||
fClusterMapCacheLast++;
|
fClusterMapCacheLast++;
|
||||||
fClusterMapCacheLast %= CLUSTER_MAP_CACHE_SIZE;
|
fClusterMapCacheLast %= CLUSTER_MAP_CACHE_SIZE;
|
||||||
|
|
||||||
//cluster = fClusters[index];
|
_cluster = cluster;
|
||||||
// convert to position
|
|
||||||
offset = fVolume.ToOffset(cluster);
|
|
||||||
offset += (pos %= fVolume.ClusterSize());
|
|
||||||
// convert to block + offset
|
|
||||||
block = fVolume.ToBlock(offset);
|
|
||||||
offset %= fVolume.BlockSize();
|
|
||||||
//TRACE(("FATFS::Stream::FindBlock: %Ld:%Ld\n", block, offset));
|
|
||||||
return B_OK;
|
return B_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
status_t
|
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));
|
TRACE(("FATFS::Stream::%s(%Ld, )\n", __FUNCTION__, pos));
|
||||||
|
|
||||||
|
uint8* buffer = (uint8*)_buffer;
|
||||||
|
|
||||||
// set/check boundaries for pos/length
|
// set/check boundaries for pos/length
|
||||||
if (pos < 0)
|
if (pos < 0)
|
||||||
return B_BAD_VALUE;
|
return B_BAD_VALUE;
|
||||||
@@ -193,6 +267,9 @@ Stream::ReadAt(off_t pos, uint8 *buffer, size_t *_length)
|
|||||||
return B_BAD_VALUE;
|
return B_BAD_VALUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (diskOffset != NULL)
|
||||||
|
*diskOffset = fVolume.BlockToOffset(num) + offset;
|
||||||
|
|
||||||
uint32 bytesRead = 0;
|
uint32 bytesRead = 0;
|
||||||
uint32 blockSize = fVolume.BlockSize();
|
uint32 blockSize = fVolume.BlockSize();
|
||||||
uint8 *block;
|
uint8 *block;
|
||||||
@@ -248,7 +325,7 @@ Stream::ReadAt(off_t pos, uint8 *buffer, size_t *_length)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (read_pos(fVolume.Device(), fVolume.ToOffset(num),
|
if (read_pos(fVolume.Device(), fVolume.BlockToOffset(num),
|
||||||
buffer + bytesRead, fVolume.BlockSize()) < B_OK) {
|
buffer + bytesRead, fVolume.BlockSize()) < B_OK) {
|
||||||
*_length = bytesRead;
|
*_length = bytesRead;
|
||||||
return B_BAD_VALUE;
|
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
|
status_t
|
||||||
Stream::BuildClusterList()
|
Stream::BuildClusterList()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -34,11 +34,20 @@ class Stream {
|
|||||||
off_t Size() const { return fSize; }
|
off_t Size() const { return fSize; }
|
||||||
uint32 FirstCluster() const { return fFirstCluster; }
|
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:
|
private:
|
||||||
status_t BuildClusterList();
|
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);
|
status_t FindBlock(off_t pos, off_t &block, off_t &offset);
|
||||||
|
|
||||||
Volume &fVolume;
|
Volume &fVolume;
|
||||||
uint32 fFirstCluster;
|
uint32 fFirstCluster;
|
||||||
uint32 fClusterCount;
|
uint32 fClusterCount;
|
||||||
|
|||||||
@@ -65,10 +65,10 @@ Volume::Volume(boot::Partition *partition)
|
|||||||
// check the signature
|
// check the signature
|
||||||
if (((buf[0x1fe] != 0x55) || (buf[0x1ff] != 0xaa)) && (buf[0x15] == 0xf8))
|
if (((buf[0x1fe] != 0x55) || (buf[0x1ff] != 0xaa)) && (buf[0x15] == 0xf8))
|
||||||
goto err1;
|
goto err1;
|
||||||
|
|
||||||
if (!memcmp(buf+3, "NTFS ", 8) || !memcmp(buf+3, "HPFS ", 8))
|
if (!memcmp(buf+3, "NTFS ", 8) || !memcmp(buf+3, "HPFS ", 8))
|
||||||
goto err1;
|
goto err1;
|
||||||
|
|
||||||
TRACE(("%s: signature ok\n", __FUNCTION__));
|
TRACE(("%s: signature ok\n", __FUNCTION__));
|
||||||
fBytesPerSector = read16(buf,0xb);
|
fBytesPerSector = read16(buf,0xb);
|
||||||
switch (fBytesPerSector) {
|
switch (fBytesPerSector) {
|
||||||
@@ -85,7 +85,7 @@ Volume::Volume(boot::Partition *partition)
|
|||||||
goto err1;
|
goto err1;
|
||||||
}
|
}
|
||||||
TRACE(("%s: block shift %d\n", __FUNCTION__, fBlockShift));
|
TRACE(("%s: block shift %d\n", __FUNCTION__, fBlockShift));
|
||||||
|
|
||||||
fSectorsPerCluster = buf[0xd];
|
fSectorsPerCluster = buf[0xd];
|
||||||
switch (fSectorsPerCluster) {
|
switch (fSectorsPerCluster) {
|
||||||
case 1: case 2: case 4: case 8:
|
case 1: case 2: case 4: case 8:
|
||||||
@@ -101,12 +101,12 @@ Volume::Volume(boot::Partition *partition)
|
|||||||
fClusterShift += 1;
|
fClusterShift += 1;
|
||||||
}
|
}
|
||||||
TRACE(("%s: cluster shift %d\n", __FUNCTION__, fClusterShift));
|
TRACE(("%s: cluster shift %d\n", __FUNCTION__, fClusterShift));
|
||||||
|
|
||||||
fReservedSectors = read16(buf,0xe);
|
fReservedSectors = read16(buf,0xe);
|
||||||
fFatCount = buf[0x10];
|
fFatCount = buf[0x10];
|
||||||
if ((fFatCount == 0) || (fFatCount > 8))
|
if ((fFatCount == 0) || (fFatCount > 8))
|
||||||
goto err1;
|
goto err1;
|
||||||
|
|
||||||
fMediaDesc = buf[0x15];
|
fMediaDesc = buf[0x15];
|
||||||
if ((fMediaDesc != 0xf0) && (fMediaDesc < 0xf8))
|
if ((fMediaDesc != 0xf0) && (fMediaDesc < 0xf8))
|
||||||
goto err1;
|
goto err1;
|
||||||
@@ -124,32 +124,36 @@ Volume::Volume(boot::Partition *partition)
|
|||||||
fRootDirCluster = read32(buf,0x2c);
|
fRootDirCluster = read32(buf,0x2c);
|
||||||
if (fRootDirCluster >= fTotalClusters)
|
if (fRootDirCluster >= fTotalClusters)
|
||||||
goto err1;
|
goto err1;
|
||||||
|
|
||||||
|
fFSInfoSector = read16(buf, 0x30);
|
||||||
|
if (fFSInfoSector < 1 || fFSInfoSector > fTotalSectors)
|
||||||
|
goto err1;
|
||||||
} else {
|
} else {
|
||||||
// FAT12/16
|
// FAT12/16
|
||||||
// XXX:FIXME
|
// XXX:FIXME
|
||||||
fFatBits = 16;
|
fFatBits = 16;
|
||||||
goto err1;
|
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));
|
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));
|
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));
|
fReservedSectors, fMaxRootEntries, fMediaDesc, fSectorsPerFat));
|
||||||
|
|
||||||
|
|
||||||
//if (fTotalSectors > partition->sectors_per_track * partition->cylinder_count * partition->head_count)
|
//if (fTotalSectors > partition->sectors_per_track * partition->cylinder_count * partition->head_count)
|
||||||
if ((off_t)fTotalSectors * fBytesPerSector > partition->size)
|
if ((off_t)fTotalSectors * fBytesPerSector > partition->size)
|
||||||
goto err1;
|
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));
|
fFatBits, fRootDirCluster));
|
||||||
|
|
||||||
fRoot = new Directory(*this, fRootDirCluster, "/");
|
fRoot = new Directory(*this, 0, fRootDirCluster, "/");
|
||||||
return;
|
return;
|
||||||
|
|
||||||
err1:
|
err1:
|
||||||
dprintf("fatfs: cannot mount (bad superblock ?)\n");
|
TRACE("fatfs: cannot mount (bad superblock ?)\n");
|
||||||
// XXX !? this triple-faults in QEMU ..
|
// XXX !? this triple-faults in QEMU ..
|
||||||
//delete fCachedBlock;
|
//delete fCachedBlock;
|
||||||
}
|
}
|
||||||
@@ -163,7 +167,7 @@ Volume::~Volume()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
Volume::InitCheck()
|
Volume::InitCheck()
|
||||||
{
|
{
|
||||||
if (fCachedBlock == NULL)
|
if (fCachedBlock == NULL)
|
||||||
@@ -175,7 +179,7 @@ Volume::InitCheck()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
Volume::GetName(char *name, size_t size) const
|
Volume::GetName(char *name, size_t size) const
|
||||||
{
|
{
|
||||||
//TODO: WRITEME
|
//TODO: WRITEME
|
||||||
@@ -184,12 +188,12 @@ Volume::GetName(char *name, size_t size) const
|
|||||||
|
|
||||||
|
|
||||||
off_t
|
off_t
|
||||||
Volume::ToOffset(uint32 cluster) const
|
Volume::ClusterToOffset(uint32 cluster) const
|
||||||
{
|
{
|
||||||
return (fDataStart << SectorShift()) + ((cluster - 2) << ClusterShift());
|
return (fDataStart << SectorShift()) + ((cluster - 2) << ClusterShift());
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32
|
uint32
|
||||||
Volume::NextCluster(uint32 cluster, uint32 skip)
|
Volume::NextCluster(uint32 cluster, uint32 skip)
|
||||||
{
|
{
|
||||||
//TRACE(("%s(%d, %d)\n", __FUNCTION__, cluster, skip));
|
//TRACE(("%s(%d, %d)\n", __FUNCTION__, cluster, skip));
|
||||||
@@ -221,7 +225,7 @@ again:
|
|||||||
return InvalidClusterID();
|
return InvalidClusterID();
|
||||||
|
|
||||||
offset %= BlockSize();
|
offset %= BlockSize();
|
||||||
|
|
||||||
switch (fFatBits) {
|
switch (fFatBits) {
|
||||||
case 32:
|
case 32:
|
||||||
next = read32(buf, offset);
|
next = read32(buf, offset);
|
||||||
@@ -241,7 +245,7 @@ again:
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool
|
bool
|
||||||
Volume::IsValidCluster(uint32 cluster) const
|
Volume::IsValidCluster(uint32 cluster) const
|
||||||
{
|
{
|
||||||
if (cluster > 1 && cluster < fTotalClusters)
|
if (cluster > 1 && cluster < fTotalClusters)
|
||||||
@@ -255,10 +259,141 @@ Volume::IsLastCluster(uint32 cluster) const
|
|||||||
{
|
{
|
||||||
if (cluster >= fTotalClusters && ((cluster & 0xff8) == 0xff8))
|
if (cluster >= fTotalClusters && ((cluster & 0xff8) == 0xff8))
|
||||||
return true;
|
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 -
|
// #pragma mark -
|
||||||
|
|
||||||
|
|||||||
@@ -43,17 +43,24 @@ class Volume {
|
|||||||
int32 NumBlocks() const { return (int32)((off_t)fTotalSectors * fBytesPerSector / fBlockSize); }
|
int32 NumBlocks() const { return (int32)((off_t)fTotalSectors * fBytesPerSector / fBlockSize); }
|
||||||
int32 NumSectors() const { return fTotalSectors; }
|
int32 NumSectors() const { return fTotalSectors; }
|
||||||
int32 NumClusters() const { return fTotalClusters; }
|
int32 NumClusters() const { return fTotalClusters; }
|
||||||
|
|
||||||
uint32 NextCluster(uint32 cluster, uint32 skip=0);
|
uint32 NextCluster(uint32 cluster, uint32 skip=0);
|
||||||
bool IsValidCluster(uint32 cluster) const;
|
bool IsValidCluster(uint32 cluster) const;
|
||||||
bool IsLastCluster(uint32 cluster) const;
|
bool IsLastCluster(uint32 cluster) const;
|
||||||
uint32 InvalidClusterID() const { return (1 << fFatBits) - 1; }
|
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(); }
|
// 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(); }
|
uint32 ToBlock(off_t offset) const { return offset >> BlockShift(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
status_t _UpdateCluster(uint32 cluster, uint32 value);
|
||||||
|
status_t _ClusterAllocated(uint32 cluster);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
int fDevice;
|
int fDevice;
|
||||||
@@ -75,7 +82,8 @@ class Volume {
|
|||||||
uint32 fDataStart;
|
uint32 fDataStart;
|
||||||
uint32 fTotalClusters;
|
uint32 fTotalClusters;
|
||||||
uint32 fRootDirCluster;
|
uint32 fRootDirCluster;
|
||||||
|
uint16 fFSInfoSector;
|
||||||
|
|
||||||
CachedBlock *fCachedBlock;
|
CachedBlock *fCachedBlock;
|
||||||
Directory *fRoot;
|
Directory *fRoot;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -27,6 +27,12 @@ class Volume;
|
|||||||
#define read16(buffer,off) \
|
#define read16(buffer,off) \
|
||||||
B_LENDIAN_TO_HOST_INT16(*(uint16 *)&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 {
|
enum name_lengths {
|
||||||
FATFS_BASENAME_LENGTH = 8,
|
FATFS_BASENAME_LENGTH = 8,
|
||||||
FATFS_EXTNAME_LENGTH = 3,
|
FATFS_EXTNAME_LENGTH = 3,
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
#include "menu.h"
|
#include "menu.h"
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
@@ -16,6 +17,7 @@
|
|||||||
#include <boot/stage2.h>
|
#include <boot/stage2.h>
|
||||||
#include <boot/vfs.h>
|
#include <boot/vfs.h>
|
||||||
#include <boot/platform.h>
|
#include <boot/platform.h>
|
||||||
|
#include <boot/platform/generic/text_console.h>
|
||||||
#include <boot/stdio.h>
|
#include <boot/stdio.h>
|
||||||
#include <safemode.h>
|
#include <safemode.h>
|
||||||
#include <util/kernel_cpp.h>
|
#include <util/kernel_cpp.h>
|
||||||
@@ -359,6 +361,57 @@ Menu::Run()
|
|||||||
// #pragma mark -
|
// #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
|
static bool
|
||||||
user_menu_boot_volume(Menu *menu, MenuItem *item)
|
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
|
static bool
|
||||||
debug_menu_toggle_debug_syslog(Menu *menu, MenuItem *item)
|
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 *
|
static Menu *
|
||||||
add_boot_volume_menu(Directory *bootVolume)
|
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 *
|
static Menu *
|
||||||
add_debug_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;
|
MenuItem *item;
|
||||||
|
|
||||||
#if DEBUG_SPINLOCK_LATENCIES
|
#if DEBUG_SPINLOCK_LATENCIES
|
||||||
@@ -558,6 +749,11 @@ add_debug_menu()
|
|||||||
item->SetType(MENU_ITEM_NO_CHOICE);
|
item->SetType(MENU_ITEM_NO_CHOICE);
|
||||||
item->SetHelpText(
|
item->SetHelpText(
|
||||||
"Displays the syslog from the previous Haiku session.");
|
"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();
|
menu->AddSeparatorItem();
|
||||||
@@ -625,10 +821,9 @@ user_menu(Directory **_bootVolume)
|
|||||||
platform_add_menus(menu);
|
platform_add_menus(menu);
|
||||||
|
|
||||||
menu->AddSeparatorItem();
|
menu->AddSeparatorItem();
|
||||||
if (*_bootVolume == NULL) {
|
|
||||||
menu->AddItem(item = new(nothrow) MenuItem("Reboot"));
|
menu->AddItem(item = new(nothrow) MenuItem("Reboot"));
|
||||||
item->SetTarget(user_menu_reboot);
|
item->SetTarget(user_menu_reboot);
|
||||||
}
|
|
||||||
|
|
||||||
menu->AddItem(item = new(nothrow) MenuItem("Continue booting"));
|
menu->AddItem(item = new(nothrow) MenuItem("Continue booting"));
|
||||||
if (*_bootVolume == NULL) {
|
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/FileMapDisk.h>
|
||||||
#include <boot/partitions.h>
|
|
||||||
#include <boot/vfs.h>
|
|
||||||
#include <boot/platform.h>
|
#include <boot/platform.h>
|
||||||
#include <boot/stage2.h>
|
#include <boot/stage2.h>
|
||||||
#include <boot/stdio.h>
|
#include <boot/stdio.h>
|
||||||
|
#include <boot/vfs.h>
|
||||||
#include <ddm_modules.h>
|
#include <ddm_modules.h>
|
||||||
#include <util/kernel_cpp.h>
|
#include <util/kernel_cpp.h>
|
||||||
|
|
||||||
#include <unistd.h>
|
#include "RootFileSystem.h"
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
using namespace boot;
|
using namespace boot;
|
||||||
|
|
||||||
@@ -161,7 +164,8 @@ Partition::ReadAt(void *cookie, off_t position, void *buffer, size_t bufferSize)
|
|||||||
if (position + bufferSize > this->size)
|
if (position + bufferSize > this->size)
|
||||||
bufferSize = this->size - position;
|
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)
|
if (position + bufferSize > this->size)
|
||||||
bufferSize = this->size - position;
|
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)
|
if (_fileSystem)
|
||||||
*_fileSystem = fileSystem;
|
*_fileSystem = fileSystem;
|
||||||
|
|
||||||
// remember the module name that mounted us
|
// remember the module that mounted us
|
||||||
fModuleName = module->module_name;
|
fModuleName = module->module_name;
|
||||||
|
this->content_type = module->pretty_name;
|
||||||
|
|
||||||
fIsFileSystem = true;
|
fIsFileSystem = true;
|
||||||
|
|
||||||
@@ -386,7 +393,7 @@ Partition::Scan(bool mountFileSystems, bool isBootDevice)
|
|||||||
delete child;
|
delete child;
|
||||||
}
|
}
|
||||||
|
|
||||||
// remember the module name that identified us
|
// remember the name of the module that identified us
|
||||||
fModuleName = bestModule->module.name;
|
fModuleName = bestModule->module.name;
|
||||||
|
|
||||||
return B_OK;
|
return B_OK;
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
#include <boot/stdio.h>
|
#include <boot/stdio.h>
|
||||||
#include <util/kernel_cpp.h>
|
#include <util/kernel_cpp.h>
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
@@ -19,8 +20,16 @@
|
|||||||
//extern FILE *stdout;
|
//extern FILE *stdout;
|
||||||
//extern FILE *stdin;
|
//extern FILE *stdin;
|
||||||
|
|
||||||
|
|
||||||
|
#undef errno
|
||||||
int errno;
|
int errno;
|
||||||
int *_errnop = &errno;
|
|
||||||
|
|
||||||
|
int*
|
||||||
|
_errnop(void)
|
||||||
|
{
|
||||||
|
return &errno;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
int
|
int
|
||||||
|
|||||||
+125
-56
@@ -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 <StorageDefs.h>
|
||||||
#include <boot/vfs.h>
|
|
||||||
#include <boot/platform.h>
|
#include <boot/platform.h>
|
||||||
#include <boot/partitions.h>
|
#include <boot/partitions.h>
|
||||||
#include <boot/stdio.h>
|
#include <boot/stdio.h>
|
||||||
#include <boot/stage2.h>
|
#include <boot/stage2.h>
|
||||||
#include <util/kernel_cpp.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;
|
using namespace boot;
|
||||||
|
|
||||||
@@ -92,7 +98,7 @@ Node::Close(void *cookie)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
Node::GetName(char *nameBuffer, size_t bufferSize) const
|
Node::GetName(char *nameBuffer, size_t bufferSize) const
|
||||||
{
|
{
|
||||||
return B_ERROR;
|
return B_ERROR;
|
||||||
@@ -106,14 +112,14 @@ Node::GetFileMap(struct file_map_run *runs, int32 *count)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int32
|
int32
|
||||||
Node::Type() const
|
Node::Type() const
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
off_t
|
off_t
|
||||||
Node::Size() const
|
Node::Size() const
|
||||||
{
|
{
|
||||||
return 0LL;
|
return 0LL;
|
||||||
@@ -127,7 +133,7 @@ Node::Inode() const
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
Node::Acquire()
|
Node::Acquire()
|
||||||
{
|
{
|
||||||
fRefCount++;
|
fRefCount++;
|
||||||
@@ -136,7 +142,7 @@ Node::Acquire()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
Node::Release()
|
Node::Release()
|
||||||
{
|
{
|
||||||
TRACE(("%p::Release(), fRefCount = %ld\n", this, fRefCount));
|
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)
|
Directory::ReadAt(void *cookie, off_t pos, void *buffer, size_t bufferSize)
|
||||||
{
|
{
|
||||||
return B_ERROR;
|
return B_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
ssize_t
|
ssize_t
|
||||||
Directory::WriteAt(void *cookie, off_t pos, const void *buffer, size_t bufferSize)
|
Directory::WriteAt(void *cookie, off_t pos, const void *buffer, size_t bufferSize)
|
||||||
{
|
{
|
||||||
return B_ERROR;
|
return B_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int32
|
int32
|
||||||
Directory::Type() const
|
Directory::Type() const
|
||||||
{
|
{
|
||||||
return S_IFDIR;
|
return S_IFDIR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
Directory::CreateFile(const char *name, mode_t permissions, Node **_node)
|
||||||
|
{
|
||||||
|
return EROFS;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// #pragma mark -
|
// #pragma mark -
|
||||||
|
|
||||||
|
|
||||||
@@ -273,7 +286,7 @@ Descriptor::~Descriptor()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
ssize_t
|
ssize_t
|
||||||
Descriptor::Read(void *buffer, size_t bufferSize)
|
Descriptor::Read(void *buffer, size_t bufferSize)
|
||||||
{
|
{
|
||||||
ssize_t bytesRead = fNode->ReadAt(fCookie, fOffset, buffer, 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)
|
Descriptor::ReadAt(off_t pos, void *buffer, size_t bufferSize)
|
||||||
{
|
{
|
||||||
return fNode->ReadAt(fCookie, pos, buffer, bufferSize);
|
return fNode->ReadAt(fCookie, pos, buffer, bufferSize);
|
||||||
@@ -359,7 +372,7 @@ status_t
|
|||||||
register_boot_file_system(Directory *volume)
|
register_boot_file_system(Directory *volume)
|
||||||
{
|
{
|
||||||
gRoot->AddLink("boot", volume);
|
gRoot->AddLink("boot", volume);
|
||||||
|
|
||||||
Partition *partition;
|
Partition *partition;
|
||||||
status_t status = gRoot->GetPartitionFor(volume, &partition);
|
status_t status = gRoot->GetPartitionFor(volume, &partition);
|
||||||
if (status != B_OK) {
|
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
|
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();
|
directory->Acquire();
|
||||||
// balance Acquire()/Release() calls
|
// balance Acquire()/Release() calls
|
||||||
|
|
||||||
@@ -584,7 +592,7 @@ open_node(Node *node, int mode)
|
|||||||
return B_ERROR;
|
return B_ERROR;
|
||||||
|
|
||||||
// get free descriptor
|
// get free descriptor
|
||||||
|
|
||||||
int fd = 0;
|
int fd = 0;
|
||||||
for (; fd < MAX_VFS_DESCRIPTORS; fd++) {
|
for (; fd < MAX_VFS_DESCRIPTORS; fd++) {
|
||||||
if (sDescriptors[fd] == NULL)
|
if (sDescriptors[fd] == NULL)
|
||||||
@@ -596,7 +604,7 @@ open_node(Node *node, int mode)
|
|||||||
TRACE(("got descriptor %d for node %p\n", fd, node));
|
TRACE(("got descriptor %d for node %p\n", fd, node));
|
||||||
|
|
||||||
// we got a free descriptor entry, now try to open the node
|
// we got a free descriptor entry, now try to open the node
|
||||||
|
|
||||||
void *cookie;
|
void *cookie;
|
||||||
status_t status = node->Open(&cookie, mode);
|
status_t status = node->Open(&cookie, mode);
|
||||||
if (status < B_OK)
|
if (status < B_OK)
|
||||||
@@ -619,10 +627,10 @@ dup(int fd)
|
|||||||
{
|
{
|
||||||
Descriptor *descriptor = get_descriptor(fd);
|
Descriptor *descriptor = get_descriptor(fd);
|
||||||
if (descriptor == NULL)
|
if (descriptor == NULL)
|
||||||
return B_FILE_ERROR;
|
RETURN_AND_SET_ERRNO(B_FILE_ERROR);
|
||||||
|
|
||||||
descriptor->Acquire();
|
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);
|
Descriptor *descriptor = get_descriptor(fd);
|
||||||
if (descriptor == NULL)
|
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);
|
Descriptor *descriptor = get_descriptor(fd);
|
||||||
if (descriptor == NULL)
|
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);
|
Descriptor *descriptor = get_descriptor(fd);
|
||||||
if (descriptor == NULL)
|
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);
|
Descriptor *descriptor = get_descriptor(fd);
|
||||||
if (descriptor == NULL)
|
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
|
int
|
||||||
open(const char *name, int mode, ...)
|
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?))
|
// we always start at the top (there is no notion of a current directory (yet?))
|
||||||
if (name[0] == '/')
|
RETURN_AND_SET_ERRNO(open_from(gRoot, name, mode, permissions));
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int
|
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] == '/') {
|
if (name[0] == '/') {
|
||||||
// ignore the directory and start from root if we are asked to do that
|
// 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++;
|
name++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
char path[B_PATH_NAME_LENGTH];
|
||||||
|
if (strlcpy(path, name, sizeof(path)) >= sizeof(path))
|
||||||
|
return B_NAME_TOO_LONG;
|
||||||
|
|
||||||
Node *node;
|
Node *node;
|
||||||
if (get_node_for_path(directory, name, &node) < B_OK)
|
status_t error = get_node_for_path(directory, path, &node);
|
||||||
return B_ENTRY_NOT_FOUND;
|
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);
|
int fd = open_node(node, mode);
|
||||||
|
|
||||||
@@ -729,13 +798,13 @@ close(int fd)
|
|||||||
{
|
{
|
||||||
Descriptor *descriptor = get_descriptor(fd);
|
Descriptor *descriptor = get_descriptor(fd);
|
||||||
if (descriptor == NULL)
|
if (descriptor == NULL)
|
||||||
return B_FILE_ERROR;
|
RETURN_AND_SET_ERRNO(B_FILE_ERROR);
|
||||||
|
|
||||||
status_t status = descriptor->Release();
|
status_t status = descriptor->Release();
|
||||||
if (!descriptor->RefCount())
|
if (!descriptor->RefCount())
|
||||||
free_descriptor(fd);
|
free_descriptor(fd);
|
||||||
|
|
||||||
return status;
|
RETURN_AND_SET_ERRNO(status);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -748,11 +817,11 @@ fstat(int fd, struct stat *stat)
|
|||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
if (stat == NULL)
|
if (stat == NULL)
|
||||||
return B_BAD_VALUE;
|
RETURN_AND_SET_ERRNO(B_BAD_VALUE);
|
||||||
|
|
||||||
Descriptor *descriptor = get_descriptor(fd);
|
Descriptor *descriptor = get_descriptor(fd);
|
||||||
if (descriptor == NULL)
|
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));
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user