* Implemented a read-only ext2 file system.
* It's not yet complete, doesn't support some ext2 stuff (like files over 4 GB), and might have some other bugs (I only tested it with a single 20 MB ext2 image). * To have a read/write ext2 file system, it would probably make more sense to port GNU sources (like ext2fs lib), and use that. But a small read-only ext2 file sytem doesn't hurt, I think, and I don't know if ext2fs lib would be feasible for kernel use (porting the file system from Linux directly would also be an alternative, but probably more work). git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@26187 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
parent
fdf3fa9fe1
commit
63db34c816
@ -2,6 +2,7 @@ SubDir HAIKU_TOP src add-ons kernel file_systems ;
|
||||
|
||||
SubInclude HAIKU_TOP src add-ons kernel file_systems bfs ;
|
||||
SubInclude HAIKU_TOP src add-ons kernel file_systems cdda ;
|
||||
SubInclude HAIKU_TOP src add-ons kernel file_systems ext2 ;
|
||||
SubInclude HAIKU_TOP src add-ons kernel file_systems fat ;
|
||||
SubInclude HAIKU_TOP src add-ons kernel file_systems googlefs ;
|
||||
SubInclude HAIKU_TOP src add-ons kernel file_systems iso9660 ;
|
||||
|
97
src/add-ons/kernel/file_systems/ext2/CachedBlock.h
Normal file
97
src/add-ons/kernel/file_systems/ext2/CachedBlock.h
Normal file
@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright 2001-2008, Axel Dörfler, axeld@pinc-software.de.
|
||||
* This file may be used under the terms of the MIT License.
|
||||
*/
|
||||
#ifndef CACHED_BLOCK_H
|
||||
#define CACHED_BLOCK_H
|
||||
|
||||
//! interface for the block cache
|
||||
|
||||
#include <fs_cache.h>
|
||||
|
||||
#include "Volume.h"
|
||||
|
||||
|
||||
class CachedBlock {
|
||||
public:
|
||||
CachedBlock(Volume* volume);
|
||||
CachedBlock(Volume* volume, uint32 block);
|
||||
~CachedBlock();
|
||||
|
||||
void Keep();
|
||||
void Unset();
|
||||
|
||||
const uint8* SetTo(uint32 block);
|
||||
|
||||
const uint8* Block() const { return fBlock; }
|
||||
off_t BlockNumber() const { return fBlockNumber; }
|
||||
|
||||
private:
|
||||
CachedBlock(const CachedBlock &);
|
||||
CachedBlock &operator=(const CachedBlock &);
|
||||
// no implementation
|
||||
|
||||
protected:
|
||||
Volume* fVolume;
|
||||
uint32 fBlockNumber;
|
||||
uint8* fBlock;
|
||||
};
|
||||
|
||||
|
||||
// inlines
|
||||
|
||||
|
||||
inline
|
||||
CachedBlock::CachedBlock(Volume* volume)
|
||||
:
|
||||
fVolume(volume),
|
||||
fBlockNumber(0),
|
||||
fBlock(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
inline
|
||||
CachedBlock::CachedBlock(Volume* volume, uint32 block)
|
||||
:
|
||||
fVolume(volume),
|
||||
fBlockNumber(0),
|
||||
fBlock(NULL)
|
||||
{
|
||||
SetTo(block);
|
||||
}
|
||||
|
||||
|
||||
inline
|
||||
CachedBlock::~CachedBlock()
|
||||
{
|
||||
Unset();
|
||||
}
|
||||
|
||||
|
||||
inline void
|
||||
CachedBlock::Keep()
|
||||
{
|
||||
fBlock = NULL;
|
||||
}
|
||||
|
||||
|
||||
inline void
|
||||
CachedBlock::Unset()
|
||||
{
|
||||
if (fBlock != NULL) {
|
||||
block_cache_put(fVolume->BlockCache(), fBlockNumber);
|
||||
fBlock = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
inline const uint8 *
|
||||
CachedBlock::SetTo(uint32 block)
|
||||
{
|
||||
Unset();
|
||||
fBlockNumber = block;
|
||||
return fBlock = (uint8 *)block_cache_get(fVolume->BlockCache(), block);
|
||||
}
|
||||
|
||||
#endif // CACHED_BLOCK_H
|
77
src/add-ons/kernel/file_systems/ext2/DirectoryIterator.cpp
Normal file
77
src/add-ons/kernel/file_systems/ext2/DirectoryIterator.cpp
Normal file
@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright 2008, Axel Dörfler, axeld@pinc-software.de.
|
||||
* This file may be used under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
|
||||
#include "DirectoryIterator.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "Inode.h"
|
||||
|
||||
|
||||
DirectoryIterator::DirectoryIterator(Inode* inode)
|
||||
:
|
||||
fInode(inode),
|
||||
fOffset(0)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
DirectoryIterator::~DirectoryIterator()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
DirectoryIterator::GetNext(char* name, size_t* _nameLength, ino_t* _id)
|
||||
{
|
||||
if (fOffset + sizeof(ext2_dir_entry) >= fInode->Size())
|
||||
return B_ENTRY_NOT_FOUND;
|
||||
|
||||
ext2_dir_entry entry;
|
||||
|
||||
while (true) {
|
||||
size_t length = ext2_dir_entry::MinimumSize();
|
||||
status_t status = fInode->ReadAt(fOffset, (uint8*)&entry, &length);
|
||||
if (status != B_OK)
|
||||
return status;
|
||||
if (length < ext2_dir_entry::MinimumSize() || entry.Length() == 0)
|
||||
return B_ENTRY_NOT_FOUND;
|
||||
if (!entry.IsValid())
|
||||
return B_BAD_DATA;
|
||||
|
||||
if (entry.NameLength() != 0)
|
||||
break;
|
||||
|
||||
fOffset += entry.Length();
|
||||
}
|
||||
|
||||
// read name
|
||||
|
||||
size_t length = entry.NameLength();
|
||||
status_t status = fInode->ReadAt(fOffset + ext2_dir_entry::MinimumSize(),
|
||||
(uint8*)entry.name, &length);
|
||||
if (status == B_OK) {
|
||||
if (*_nameLength < length)
|
||||
length = *_nameLength - 1;
|
||||
memcpy(name, entry.name, length);
|
||||
name[length] = '\0';
|
||||
|
||||
*_id = entry.InodeID();
|
||||
*_nameLength = length;
|
||||
|
||||
fOffset += entry.Length();
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
DirectoryIterator::Rewind()
|
||||
{
|
||||
fOffset = 0;
|
||||
return B_OK;
|
||||
}
|
33
src/add-ons/kernel/file_systems/ext2/DirectoryIterator.h
Normal file
33
src/add-ons/kernel/file_systems/ext2/DirectoryIterator.h
Normal file
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright 2008, Axel Dörfler, axeld@pinc-software.de.
|
||||
* This file may be used under the terms of the MIT License.
|
||||
*/
|
||||
#ifndef DIRECTORY_ITERATOR_H
|
||||
#define DIRECTORY_ITERATOR_H
|
||||
|
||||
|
||||
#include <SupportDefs.h>
|
||||
|
||||
|
||||
class Inode;
|
||||
|
||||
class DirectoryIterator {
|
||||
public:
|
||||
DirectoryIterator(Inode* inode);
|
||||
~DirectoryIterator();
|
||||
|
||||
status_t GetNext(char* name, size_t* _nameLength, ino_t* id);
|
||||
|
||||
status_t Rewind();
|
||||
|
||||
private:
|
||||
DirectoryIterator(const DirectoryIterator&);
|
||||
DirectoryIterator &operator=(const DirectoryIterator&);
|
||||
// no implementation
|
||||
|
||||
private:
|
||||
Inode* fInode;
|
||||
off_t fOffset;
|
||||
};
|
||||
|
||||
#endif // DIRECTORY_ITERATOR_H
|
172
src/add-ons/kernel/file_systems/ext2/Inode.cpp
Normal file
172
src/add-ons/kernel/file_systems/ext2/Inode.cpp
Normal file
@ -0,0 +1,172 @@
|
||||
/*
|
||||
* Copyright 2008, Axel Dörfler, axeld@pinc-software.de.
|
||||
* This file may be used under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
|
||||
#include "Inode.h"
|
||||
|
||||
#include <fs_cache.h>
|
||||
|
||||
#include "CachedBlock.h"
|
||||
|
||||
|
||||
Inode::Inode(Volume* volume, ino_t id)
|
||||
:
|
||||
fVolume(volume),
|
||||
fID(id),
|
||||
fCache(NULL),
|
||||
fMap(NULL),
|
||||
fNode(NULL)
|
||||
{
|
||||
rw_lock_init(&fLock, "ext2 inode");
|
||||
|
||||
uint32 block;
|
||||
if (volume->GetInodeBlock(id, block) == B_OK) {
|
||||
ext2_inode* inodes = (ext2_inode*)block_cache_get(volume->BlockCache(),
|
||||
block);
|
||||
if (inodes != NULL)
|
||||
fNode = inodes + volume->InodeBlockIndex(id);
|
||||
}
|
||||
|
||||
if (fNode != NULL) {
|
||||
fCache = file_cache_create(fVolume->ID(), ID(), Size());
|
||||
fMap = file_map_create(fVolume->ID(), ID(), Size());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Inode::~Inode()
|
||||
{
|
||||
file_cache_delete(FileCache());
|
||||
file_map_delete(Map());
|
||||
|
||||
if (fNode != NULL) {
|
||||
uint32 block;
|
||||
if (fVolume->GetInodeBlock(ID(), block) == B_OK)
|
||||
block_cache_put(fVolume->BlockCache(), block);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
Inode::InitCheck()
|
||||
{
|
||||
return fNode != NULL ? B_OK : B_ERROR;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
Inode::CheckPermissions(int accessMode) const
|
||||
{
|
||||
uid_t user = geteuid();
|
||||
gid_t group = getegid();
|
||||
|
||||
// you never have write access to a read-only volume
|
||||
if (accessMode & W_OK && fVolume->IsReadOnly())
|
||||
return B_READ_ONLY_DEVICE;
|
||||
|
||||
// root users always have full access (but they can't execute files without
|
||||
// any execute permissions set)
|
||||
if (user == 0) {
|
||||
if (!((accessMode & X_OK) != 0 && (Mode() & S_IXUSR) == 0)
|
||||
|| S_ISDIR(Mode()))
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
// shift mode bits, to check directly against accessMode
|
||||
mode_t mode = Mode();
|
||||
if (user == (uid_t)fNode->UserID())
|
||||
mode >>= 6;
|
||||
else if (group == (gid_t)fNode->GroupID())
|
||||
mode >>= 3;
|
||||
|
||||
if (accessMode & ~(mode & S_IRWXO))
|
||||
return B_NOT_ALLOWED;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
Inode::FindBlock(off_t offset, uint32& block)
|
||||
{
|
||||
uint32 perBlock = fVolume->BlockSize() / 4;
|
||||
uint32 perIndirectBlock = perBlock * perBlock;
|
||||
uint32 index = offset >> fVolume->BlockShift();
|
||||
|
||||
if (offset >= Size())
|
||||
return B_ENTRY_NOT_FOUND;
|
||||
|
||||
if (index < EXT2_DIRECT_BLOCKS) {
|
||||
// direct blocks
|
||||
block = B_LENDIAN_TO_HOST_INT32(Node().stream.direct[index]);
|
||||
} else if ((index -= EXT2_DIRECT_BLOCKS) < perBlock) {
|
||||
// indirect blocks
|
||||
CachedBlock cached(fVolume);
|
||||
uint32* indirectBlocks = (uint32*)cached.SetTo(Node().stream.indirect);
|
||||
if (indirectBlocks != NULL)
|
||||
return B_IO_ERROR;
|
||||
|
||||
block = B_LENDIAN_TO_HOST_INT32(indirectBlocks[index]);
|
||||
} else if ((index -= perBlock) < perIndirectBlock) {
|
||||
// double indirect blocks
|
||||
CachedBlock cached(fVolume);
|
||||
uint32* indirectBlocks = (uint32*)cached.SetTo(B_LENDIAN_TO_HOST_INT32(
|
||||
Node().stream.double_indirect));
|
||||
if (indirectBlocks != NULL)
|
||||
return B_IO_ERROR;
|
||||
|
||||
indirectBlocks = (uint32*)cached.SetTo(B_LENDIAN_TO_HOST_INT32(
|
||||
indirectBlocks[index / perBlock]));
|
||||
if (indirectBlocks != NULL)
|
||||
return B_IO_ERROR;
|
||||
|
||||
block = B_LENDIAN_TO_HOST_INT32(indirectBlocks[index & (perBlock - 1)]);
|
||||
} else if ((index -= perIndirectBlock) / perBlock < perIndirectBlock) {
|
||||
// triple indirect blocks
|
||||
CachedBlock cached(fVolume);
|
||||
uint32* indirectBlocks = (uint32*)cached.SetTo(B_LENDIAN_TO_HOST_INT32(
|
||||
Node().stream.triple_indirect));
|
||||
if (indirectBlocks != NULL)
|
||||
return B_IO_ERROR;
|
||||
|
||||
indirectBlocks = (uint32*)cached.SetTo(B_LENDIAN_TO_HOST_INT32(
|
||||
indirectBlocks[index / perIndirectBlock]));
|
||||
if (indirectBlocks != NULL)
|
||||
return B_IO_ERROR;
|
||||
|
||||
indirectBlocks = (uint32*)cached.SetTo(B_LENDIAN_TO_HOST_INT32(
|
||||
indirectBlocks[(index / perBlock) & (perBlock - 1)]));
|
||||
if (indirectBlocks != NULL)
|
||||
return B_IO_ERROR;
|
||||
|
||||
block = B_LENDIAN_TO_HOST_INT32(indirectBlocks[index & (perBlock - 1)]);
|
||||
} else {
|
||||
// outside of the possible data stream
|
||||
dprintf("ext2: block outside datastream!\n");
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
//dprintf("FindBlock(offset %Ld): %lu\n", offset, block);
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
Inode::ReadAt(off_t pos, uint8* buffer, size_t* _length)
|
||||
{
|
||||
size_t length = *_length;
|
||||
|
||||
// set/check boundaries for pos/length
|
||||
if (pos < 0)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
if (pos >= Size() || length == 0) {
|
||||
*_length = 0;
|
||||
return B_NO_ERROR;
|
||||
}
|
||||
|
||||
return file_cache_read(FileCache(), NULL, pos, buffer, _length);
|
||||
}
|
||||
|
71
src/add-ons/kernel/file_systems/ext2/Inode.h
Normal file
71
src/add-ons/kernel/file_systems/ext2/Inode.h
Normal file
@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright 2008, Axel Dörfler, axeld@pinc-software.de.
|
||||
* This file may be used under the terms of the MIT License.
|
||||
*/
|
||||
#ifndef INODE_H
|
||||
#define INODE_H
|
||||
|
||||
|
||||
#include <lock.h>
|
||||
|
||||
#include "ext2.h"
|
||||
#include "Volume.h"
|
||||
|
||||
|
||||
class Inode {
|
||||
public:
|
||||
Inode(Volume* volume, ino_t id);
|
||||
~Inode();
|
||||
|
||||
status_t InitCheck();
|
||||
|
||||
ino_t ID() const { return fID; }
|
||||
|
||||
rw_lock* Lock() { return &fLock; }
|
||||
|
||||
bool IsDirectory() const
|
||||
{ return S_ISDIR(Mode()); }
|
||||
bool IsFile() const
|
||||
{ return S_ISREG(Mode()); }
|
||||
bool IsSymLink() const
|
||||
{ return S_ISLNK(Mode()); }
|
||||
|
||||
status_t CheckPermissions(int accessMode) const;
|
||||
|
||||
mode_t Mode() const { return fNode->Mode(); }
|
||||
int32 Flags() const { return fNode->Flags(); }
|
||||
|
||||
off_t Size() const { return fNode->Size(); }
|
||||
time_t ModificationTime() const
|
||||
{ return fNode->ModificationTime(); }
|
||||
time_t CreationTime() const
|
||||
{ return fNode->CreationTime(); }
|
||||
time_t AccessTime() const
|
||||
{ return fNode->AccessTime(); }
|
||||
|
||||
//::Volume* _Volume() const { return fVolume; }
|
||||
Volume* GetVolume() const { return fVolume; }
|
||||
|
||||
status_t FindBlock(off_t offset, uint32& block);
|
||||
status_t ReadAt(off_t pos, uint8 *buffer, size_t *length);
|
||||
|
||||
ext2_inode& Node() { return *fNode; }
|
||||
|
||||
void* FileCache() const { return fCache; }
|
||||
void* Map() const { return fMap; }
|
||||
|
||||
private:
|
||||
Inode(const Inode&);
|
||||
Inode &operator=(const Inode&);
|
||||
// no implementation
|
||||
|
||||
private:
|
||||
rw_lock fLock;
|
||||
::Volume* fVolume;
|
||||
ino_t fID;
|
||||
void* fCache;
|
||||
void* fMap;
|
||||
ext2_inode* fNode;
|
||||
};
|
||||
|
||||
#endif // INODE_H
|
19
src/add-ons/kernel/file_systems/ext2/Jamfile
Normal file
19
src/add-ons/kernel/file_systems/ext2/Jamfile
Normal file
@ -0,0 +1,19 @@
|
||||
SubDir HAIKU_TOP src add-ons kernel file_systems ext2 ;
|
||||
|
||||
# set some additional defines
|
||||
{
|
||||
SubDirCcFlags -Wall -Wno-multichar ;
|
||||
SubDirC++Flags -Wall -Wno-multichar ;
|
||||
}
|
||||
|
||||
#UsePrivateHeaders [ FDirName kernel disk_device_manager ] ;
|
||||
UsePrivateHeaders shared storage ;
|
||||
UsePrivateKernelHeaders ;
|
||||
|
||||
KernelAddon ext2 :
|
||||
Volume.cpp
|
||||
Inode.cpp
|
||||
DirectoryIterator.cpp
|
||||
|
||||
kernel_interface.cpp
|
||||
;
|
405
src/add-ons/kernel/file_systems/ext2/Volume.cpp
Normal file
405
src/add-ons/kernel/file_systems/ext2/Volume.cpp
Normal file
@ -0,0 +1,405 @@
|
||||
/*
|
||||
* Copyright 2008, Axel Dörfler, axeld@pinc-software.de.
|
||||
* This file may be used under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
//! super block, mounting, etc.
|
||||
|
||||
|
||||
#include "Volume.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <new>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <fs_cache.h>
|
||||
#include <fs_volume.h>
|
||||
|
||||
#include <util/AutoLock.h>
|
||||
|
||||
#include "Inode.h"
|
||||
|
||||
|
||||
class DeviceOpener {
|
||||
public:
|
||||
DeviceOpener(int fd, int mode);
|
||||
DeviceOpener(const char *device, int mode);
|
||||
~DeviceOpener();
|
||||
|
||||
int Open(const char *device, int mode);
|
||||
int Open(int fd, int mode);
|
||||
void *InitCache(off_t numBlocks, uint32 blockSize);
|
||||
void RemoveCache(bool allowWrites);
|
||||
|
||||
void Keep();
|
||||
|
||||
int Device() const { return fDevice; }
|
||||
int Mode() const { return fMode; }
|
||||
bool IsReadOnly() const { return _IsReadOnly(fMode); }
|
||||
|
||||
status_t GetSize(off_t *_size, uint32 *_blockSize = NULL);
|
||||
|
||||
private:
|
||||
static bool _IsReadOnly(int mode)
|
||||
{ return (mode & O_RWMASK) == O_RDONLY;}
|
||||
static bool _IsReadWrite(int mode)
|
||||
{ return (mode & O_RWMASK) == O_RDWR;}
|
||||
|
||||
int fDevice;
|
||||
int fMode;
|
||||
void *fBlockCache;
|
||||
};
|
||||
|
||||
|
||||
DeviceOpener::DeviceOpener(const char *device, int mode)
|
||||
:
|
||||
fBlockCache(NULL)
|
||||
{
|
||||
Open(device, mode);
|
||||
}
|
||||
|
||||
|
||||
DeviceOpener::DeviceOpener(int fd, int mode)
|
||||
:
|
||||
fBlockCache(NULL)
|
||||
{
|
||||
Open(fd, mode);
|
||||
}
|
||||
|
||||
|
||||
DeviceOpener::~DeviceOpener()
|
||||
{
|
||||
if (fDevice >= 0) {
|
||||
RemoveCache(false);
|
||||
close(fDevice);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
DeviceOpener::Open(const char *device, int mode)
|
||||
{
|
||||
fDevice = open(device, mode | O_NOCACHE);
|
||||
if (fDevice < 0)
|
||||
fDevice = errno;
|
||||
|
||||
if (fDevice < 0 && _IsReadWrite(mode)) {
|
||||
// try again to open read-only (don't rely on a specific error code)
|
||||
return Open(device, O_RDONLY | O_NOCACHE);
|
||||
}
|
||||
|
||||
if (fDevice >= 0) {
|
||||
// opening succeeded
|
||||
fMode = mode;
|
||||
if (_IsReadWrite(mode)) {
|
||||
// check out if the device really allows for read/write access
|
||||
device_geometry geometry;
|
||||
if (!ioctl(fDevice, B_GET_GEOMETRY, &geometry)) {
|
||||
if (geometry.read_only) {
|
||||
// reopen device read-only
|
||||
close(fDevice);
|
||||
return Open(device, O_RDONLY | O_NOCACHE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return fDevice;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
DeviceOpener::Open(int fd, int mode)
|
||||
{
|
||||
fDevice = dup(fd);
|
||||
if (fDevice < 0)
|
||||
return errno;
|
||||
|
||||
fMode = mode;
|
||||
|
||||
return fDevice;
|
||||
}
|
||||
|
||||
|
||||
void *
|
||||
DeviceOpener::InitCache(off_t numBlocks, uint32 blockSize)
|
||||
{
|
||||
return fBlockCache = block_cache_create(fDevice, numBlocks, blockSize,
|
||||
IsReadOnly());
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
DeviceOpener::RemoveCache(bool allowWrites)
|
||||
{
|
||||
if (fBlockCache == NULL)
|
||||
return;
|
||||
|
||||
block_cache_delete(fBlockCache, allowWrites);
|
||||
fBlockCache = NULL;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
DeviceOpener::Keep()
|
||||
{
|
||||
fDevice = -1;
|
||||
}
|
||||
|
||||
|
||||
/*! Returns the size of the device in bytes. It uses B_GET_GEOMETRY
|
||||
to compute the size, or fstat() if that failed.
|
||||
*/
|
||||
status_t
|
||||
DeviceOpener::GetSize(off_t *_size, uint32 *_blockSize)
|
||||
{
|
||||
device_geometry geometry;
|
||||
if (ioctl(fDevice, B_GET_GEOMETRY, &geometry) < 0) {
|
||||
// maybe it's just a file
|
||||
struct stat stat;
|
||||
if (fstat(fDevice, &stat) < 0)
|
||||
return B_ERROR;
|
||||
|
||||
if (_size)
|
||||
*_size = stat.st_size;
|
||||
if (_blockSize) // that shouldn't cause us any problems
|
||||
*_blockSize = 512;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
if (_size) {
|
||||
*_size = 1LL * geometry.head_count * geometry.cylinder_count
|
||||
* geometry.sectors_per_track * geometry.bytes_per_sector;
|
||||
}
|
||||
if (_blockSize)
|
||||
*_blockSize = geometry.bytes_per_sector;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark -
|
||||
|
||||
|
||||
bool
|
||||
ext2_super_block::IsValid()
|
||||
{
|
||||
// TODO: check some more values!
|
||||
if (Magic() != (uint32)EXT2_SUPER_BLOCK_MAGIC)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark -
|
||||
|
||||
|
||||
Volume::Volume(fs_volume* volume)
|
||||
:
|
||||
fFSVolume(volume),
|
||||
fFlags(0),
|
||||
fGroupBlocks(NULL),
|
||||
fRootNode(NULL)
|
||||
{
|
||||
mutex_init(&fLock, "ext2 volume");
|
||||
}
|
||||
|
||||
|
||||
Volume::~Volume()
|
||||
{
|
||||
if (fGroupBlocks != NULL) {
|
||||
uint32 blockCount = (fNumGroups + fGroupsPerBlock - 1)
|
||||
/ fGroupsPerBlock;
|
||||
for (uint32 i = 0; i < blockCount; i++) {
|
||||
free(fGroupBlocks[i]);
|
||||
}
|
||||
|
||||
free(fGroupBlocks);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
Volume::IsValidSuperBlock()
|
||||
{
|
||||
return fSuperBlock.IsValid();
|
||||
}
|
||||
|
||||
|
||||
const char*
|
||||
Volume::Name() const
|
||||
{
|
||||
if (fSuperBlock.name[0])
|
||||
return fSuperBlock.name;
|
||||
|
||||
return "Unnamed Ext2 Volume";
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
Volume::Mount(const char* deviceName, uint32 flags)
|
||||
{
|
||||
flags |= B_MOUNT_READ_ONLY;
|
||||
// we only support read-only for now
|
||||
|
||||
DeviceOpener opener(deviceName, (flags & B_MOUNT_READ_ONLY) != 0
|
||||
? O_RDONLY : O_RDWR);
|
||||
fDevice = opener.Device();
|
||||
if (fDevice < B_OK)
|
||||
return fDevice;
|
||||
|
||||
if (opener.IsReadOnly())
|
||||
fFlags |= VOLUME_READ_ONLY;
|
||||
|
||||
// read the super block
|
||||
if (Identify(fDevice, &fSuperBlock) != B_OK) {
|
||||
//FATAL(("invalid super block!\n"));
|
||||
return B_BAD_VALUE;
|
||||
}
|
||||
|
||||
if ((fSuperBlock.IncompatibleFeatures()
|
||||
& EXT2_INCOMPATIBLE_FEATURE_COMPRESSION) != 0) {
|
||||
dprintf("ext2: compression not supported.\n");
|
||||
return B_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
// initialize short hands to the super block (to save byte swapping)
|
||||
fBlockShift = fSuperBlock.BlockShift();
|
||||
fBlockSize = 1UL << fSuperBlock.BlockShift();
|
||||
|
||||
fNumGroups = (fSuperBlock.NumBlocks() - fSuperBlock.FirstDataBlock() - 1)
|
||||
/ fSuperBlock.BlocksPerGroup() + 1;
|
||||
fGroupsPerBlock = fBlockSize / sizeof(ext2_block_group);
|
||||
|
||||
uint32 blockCount = (fNumGroups + fGroupsPerBlock - 1) / fGroupsPerBlock;
|
||||
|
||||
fGroupBlocks = (ext2_block_group**)malloc(blockCount * sizeof(void*));
|
||||
if (fGroupBlocks == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
memset(fGroupBlocks, 0, blockCount * sizeof(void*));
|
||||
fInodesPerBlock = fBlockSize / sizeof(ext2_inode);
|
||||
|
||||
// check if the device size is large enough to hold the file system
|
||||
off_t diskSize;
|
||||
status_t status = opener.GetSize(&diskSize);
|
||||
if (status != B_OK)
|
||||
return status;
|
||||
if (diskSize < (NumBlocks() << BlockShift()))
|
||||
return B_BAD_VALUE;
|
||||
|
||||
if ((fBlockCache = opener.InitCache(NumBlocks(), fBlockSize)) == NULL)
|
||||
return B_ERROR;
|
||||
|
||||
status = get_vnode(fFSVolume, EXT2_ROOT_NODE, (void**)&fRootNode);
|
||||
if (status == B_OK) {
|
||||
// all went fine
|
||||
opener.Keep();
|
||||
return B_OK;
|
||||
} else
|
||||
dprintf("ext2: could not create root node: get_vnode() failed!\n");
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
Volume::Unmount()
|
||||
{
|
||||
//put_vnode(fVolume, ToVnode(Root()));
|
||||
//block_cache_delete(fBlockCache, !IsReadOnly());
|
||||
close(fDevice);
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
Volume::GetInodeBlock(ino_t id, uint32& block)
|
||||
{
|
||||
ext2_block_group* group;
|
||||
status_t status = GetBlockGroup((id - 1) / fSuperBlock.InodesPerGroup(),
|
||||
&group);
|
||||
if (status != B_OK)
|
||||
return status;
|
||||
|
||||
block = group->InodeTable()
|
||||
+ ((id - 1) % fSuperBlock.InodesPerGroup()) / fInodesPerBlock;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
uint32
|
||||
Volume::InodeBlockIndex(ino_t id) const
|
||||
{
|
||||
return ((id - 1) % fSuperBlock.InodesPerGroup()) % fInodesPerBlock;
|
||||
}
|
||||
|
||||
|
||||
off_t
|
||||
Volume::_GroupBlockOffset(uint32 blockIndex)
|
||||
{
|
||||
if ((fSuperBlock.IncompatibleFeatures()
|
||||
& EXT2_INCOMPATIBLE_FEATURE_META_GROUP) == 0
|
||||
|| blockIndex < fSuperBlock.FirstMetaBlockGroup())
|
||||
return EXT2_SUPER_BLOCK_OFFSET + fBlockSize * (1 + blockIndex);
|
||||
|
||||
panic("meta block");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*! Makes the requested block group available.
|
||||
The block groups are loaded on demand, but are kept in memory until the
|
||||
volume is unmounted; therefore we don't use the block cache.
|
||||
*/
|
||||
status_t
|
||||
Volume::GetBlockGroup(int32 index, ext2_block_group** _group)
|
||||
{
|
||||
if (index < 0 || (uint32)index > fNumGroups)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
int32 blockIndex = index / fGroupsPerBlock;
|
||||
|
||||
MutexLocker _(fLock);
|
||||
|
||||
if (fGroupBlocks[blockIndex] == NULL) {
|
||||
ext2_block_group* groupBlock = (ext2_block_group*)malloc(fBlockSize);
|
||||
if (groupBlock == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
ssize_t bytesRead = read_pos(fDevice, _GroupBlockOffset(blockIndex),
|
||||
groupBlock, fBlockSize);
|
||||
if (bytesRead >= B_OK && (uint32)bytesRead != fBlockSize)
|
||||
bytesRead = B_IO_ERROR;
|
||||
if (bytesRead < B_OK) {
|
||||
free(groupBlock);
|
||||
return bytesRead;
|
||||
}
|
||||
|
||||
fGroupBlocks[blockIndex] = groupBlock;
|
||||
}
|
||||
|
||||
*_group = fGroupBlocks[blockIndex] + index % fGroupsPerBlock;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark - Disk scanning and initialization
|
||||
|
||||
|
||||
/*static*/ status_t
|
||||
Volume::Identify(int fd, ext2_super_block* superBlock)
|
||||
{
|
||||
if (read_pos(fd, EXT2_SUPER_BLOCK_OFFSET, superBlock,
|
||||
sizeof(ext2_super_block)) != sizeof(ext2_super_block))
|
||||
return B_IO_ERROR;
|
||||
|
||||
if (!superBlock->IsValid())
|
||||
return B_BAD_VALUE;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
74
src/add-ons/kernel/file_systems/ext2/Volume.h
Normal file
74
src/add-ons/kernel/file_systems/ext2/Volume.h
Normal file
@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright 2008, Axel Dörfler, axeld@pinc-software.de.
|
||||
* This file may be used under the terms of the MIT License.
|
||||
*/
|
||||
#ifndef VOLUME_H
|
||||
#define VOLUME_H
|
||||
|
||||
|
||||
#include <lock.h>
|
||||
|
||||
#include "ext2.h"
|
||||
|
||||
class Inode;
|
||||
|
||||
enum volume_flags {
|
||||
VOLUME_READ_ONLY = 0x0001
|
||||
};
|
||||
|
||||
class Volume {
|
||||
public:
|
||||
Volume(fs_volume* volume);
|
||||
~Volume();
|
||||
|
||||
status_t Mount(const char* device, uint32 flags);
|
||||
status_t Unmount();
|
||||
|
||||
bool IsValidSuperBlock();
|
||||
bool IsReadOnly() const { return fFlags & VOLUME_READ_ONLY; }
|
||||
|
||||
Inode* RootNode() const { return fRootNode; }
|
||||
int Device() const { return fDevice; }
|
||||
|
||||
dev_t ID() const { return fFSVolume ? fFSVolume->id : -1; }
|
||||
fs_volume* FSVolume() const { return fFSVolume; }
|
||||
const char* Name() const;
|
||||
|
||||
off_t NumBlocks() const { return fSuperBlock.NumBlocks(); }
|
||||
off_t FreeBlocks() const { return fSuperBlock.FreeBlocks(); }
|
||||
|
||||
uint32 BlockSize() const { return fBlockSize; }
|
||||
uint32 BlockShift() const { return fBlockShift; }
|
||||
uint32 InodeSize() const { return fSuperBlock.InodeSize(); }
|
||||
ext2_super_block& SuperBlock() { return fSuperBlock; }
|
||||
|
||||
status_t GetInodeBlock(ino_t id, uint32& block);
|
||||
uint32 InodeBlockIndex(ino_t id) const;
|
||||
status_t GetBlockGroup(int32 index, ext2_block_group** _group);
|
||||
|
||||
// cache access
|
||||
void* BlockCache() { return fBlockCache; }
|
||||
|
||||
static status_t Identify(int fd, ext2_super_block* superBlock);
|
||||
|
||||
private:
|
||||
off_t _GroupBlockOffset(uint32 blockIndex);
|
||||
|
||||
private:
|
||||
mutex fLock;
|
||||
fs_volume* fFSVolume;
|
||||
int fDevice;
|
||||
ext2_super_block fSuperBlock;
|
||||
uint32 fFlags;
|
||||
uint32 fBlockSize;
|
||||
uint32 fBlockShift;
|
||||
uint32 fNumGroups;
|
||||
uint32 fGroupsPerBlock;
|
||||
ext2_block_group** fGroupBlocks;
|
||||
uint32 fInodesPerBlock;
|
||||
|
||||
void* fBlockCache;
|
||||
Inode* fRootNode;
|
||||
};
|
||||
|
||||
#endif // VOLUME_H
|
251
src/add-ons/kernel/file_systems/ext2/ext2.h
Normal file
251
src/add-ons/kernel/file_systems/ext2/ext2.h
Normal file
@ -0,0 +1,251 @@
|
||||
/*
|
||||
* Copyright 2008, Axel Dörfler, axeld@pinc-software.de.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
#ifndef EXT2_H
|
||||
#define EXT2_H
|
||||
|
||||
|
||||
#include <ByteOrder.h>
|
||||
#include <fs_interface.h>
|
||||
|
||||
|
||||
#define EXT2_SUPER_BLOCK_OFFSET 1024
|
||||
|
||||
struct ext2_super_block {
|
||||
uint32 num_inodes;
|
||||
uint32 num_blocks;
|
||||
uint32 reserved_blocks;
|
||||
uint32 free_blocks;
|
||||
uint32 free_inodes;
|
||||
uint32 first_data_block;
|
||||
uint32 block_shift;
|
||||
uint32 fragment_shift;
|
||||
uint32 blocks_per_group;
|
||||
uint32 fragments_per_group;
|
||||
uint32 inodes_per_group;
|
||||
uint32 mount_time;
|
||||
uint32 write_time;
|
||||
uint16 mount_count;
|
||||
uint16 max_mount_count;
|
||||
uint16 magic;
|
||||
uint16 state;
|
||||
uint16 error_handling;
|
||||
uint16 minor_revision_level;
|
||||
uint32 last_check_time;
|
||||
uint32 check_interval;
|
||||
uint32 creator_os;
|
||||
uint32 revision_level;
|
||||
uint16 reserved_blocks_uid;
|
||||
uint16 reserved_blocks_gid;
|
||||
uint32 first_inode;
|
||||
uint16 inode_size;
|
||||
uint16 block_group;
|
||||
uint32 compatible_features;
|
||||
uint32 incompatible_features;
|
||||
uint32 readonly_features;
|
||||
uint8 uuid[16];
|
||||
char name[16];
|
||||
char last_mount_point[64];
|
||||
uint32 algorithm_usage_bitmap;
|
||||
uint8 preallocated_blocks;
|
||||
uint8 preallocated_directory_blocks;
|
||||
uint16 _padding;
|
||||
|
||||
// journaling ext3 support
|
||||
uint8 journal_uuid[16];
|
||||
uint32 journal_inode;
|
||||
uint32 journal_device;
|
||||
uint32 last_orphan;
|
||||
uint32 hash_seed[4];
|
||||
uint8 default_hash_version;
|
||||
uint8 _reserved1;
|
||||
uint16 _reserved2;
|
||||
uint32 default_mount_options;
|
||||
uint32 first_meta_block_group;
|
||||
uint32 _reserved3[190];
|
||||
|
||||
uint16 Magic() const { return B_LENDIAN_TO_HOST_INT16(magic); }
|
||||
uint32 BlockShift() const { return B_LENDIAN_TO_HOST_INT32(block_shift) + 10; }
|
||||
uint32 NumInodes() const { return B_LENDIAN_TO_HOST_INT32(num_inodes); }
|
||||
uint32 NumBlocks() const { return B_LENDIAN_TO_HOST_INT32(num_blocks); }
|
||||
uint32 FreeInodes() const { return B_LENDIAN_TO_HOST_INT32(free_inodes); }
|
||||
uint32 FreeBlocks() const { return B_LENDIAN_TO_HOST_INT32(free_blocks); }
|
||||
uint16 InodeSize() const { return B_LENDIAN_TO_HOST_INT16(inode_size); }
|
||||
uint32 FirstDataBlock() const
|
||||
{ return B_LENDIAN_TO_HOST_INT32(first_data_block); }
|
||||
uint32 BlocksPerGroup() const
|
||||
{ return B_LENDIAN_TO_HOST_INT32(blocks_per_group); }
|
||||
uint32 InodesPerGroup() const
|
||||
{ return B_LENDIAN_TO_HOST_INT32(inodes_per_group); }
|
||||
uint32 FirstMetaBlockGroup() const
|
||||
{ return B_LENDIAN_TO_HOST_INT32(first_meta_block_group); }
|
||||
uint32 CompatibleFeatures() const
|
||||
{ return B_LENDIAN_TO_HOST_INT32(compatible_features); }
|
||||
uint32 ReadOnlyFeatures() const
|
||||
{ return B_LENDIAN_TO_HOST_INT32(readonly_features); }
|
||||
uint32 IncompatibleFeatures() const
|
||||
{ return B_LENDIAN_TO_HOST_INT32(incompatible_features); }
|
||||
|
||||
bool IsValid();
|
||||
// implemented in Volume.cpp
|
||||
} _PACKED;
|
||||
|
||||
#define EXT2_OLD_REVISION 0
|
||||
#define EXT2_DYNAMIC_REVISION 1
|
||||
|
||||
// compatible features
|
||||
#define EXT2_FEATURE_DIRECTORY_PREALLOCATION 0x01
|
||||
#define EXT2_FEATURE_IMAGIC_INODES 0x02
|
||||
#define EXT2_FEATURE_HAS_JOURNAL 0x04
|
||||
#define EXT2_FEATURE_EXT_ATTR 0x08
|
||||
#define EXT2_FEATURE_RESIZE_INODE 0x10
|
||||
#define EXT2_FEATURE_DIRECTORY_INDEX 0x20
|
||||
|
||||
// read-only compatible features
|
||||
#define EXT2_READ_ONLY_FEATURE_SPARSE_SUPER 0x01
|
||||
#define EXT2_READ_ONLY_FEATURE_LARGE_FILE 0x02
|
||||
#define EXT2_READ_ONLY_FEATURE_BTREE_DIRECTORY 0x04
|
||||
|
||||
// incompatible features
|
||||
#define EXT2_INCOMPATIBLE_FEATURE_COMPRESSION 0x01
|
||||
#define EXT2_INCOMPATIBLE_FEATURE_FILE_TYPE 0x02
|
||||
#define EXT2_INCOMPATIBLE_FEATURE_RECOVER 0x04
|
||||
#define EXT2_INCOMPATIBLE_FEATURE_JOURNAL 0x08
|
||||
#define EXT2_INCOMPATIBLE_FEATURE_META_GROUP 0x10
|
||||
|
||||
// states
|
||||
#define EXT2_STATE_VALID 0x01
|
||||
#define EXT2_STATE_INVALID 0x02
|
||||
|
||||
struct ext2_block_group {
|
||||
uint32 block_bitmap;
|
||||
uint32 inode_bitmap;
|
||||
uint32 inode_table;
|
||||
uint16 free_blocks;
|
||||
uint16 free_inodes;
|
||||
uint16 used_directories;
|
||||
uint16 _padding;
|
||||
uint32 _reserved[3];
|
||||
|
||||
uint32 InodeTable() const
|
||||
{ return B_LENDIAN_TO_HOST_INT32(inode_table); }
|
||||
};
|
||||
|
||||
#define EXT2_DIRECT_BLOCKS 12
|
||||
#define EXT2_ROOT_NODE 2
|
||||
#define EXT2_SHORT_SYMLINK_LENGTH 60
|
||||
|
||||
struct ext2_inode {
|
||||
uint16 mode;
|
||||
uint16 uid;
|
||||
uint32 size;
|
||||
uint32 access_time;
|
||||
uint32 creation_time;
|
||||
uint32 modification_time;
|
||||
uint32 deletion_time;
|
||||
uint16 gid;
|
||||
uint16 num_links;
|
||||
uint32 num_blocks;
|
||||
uint32 flags;
|
||||
uint32 _reserved1;
|
||||
union {
|
||||
struct data_stream {
|
||||
uint32 direct[EXT2_DIRECT_BLOCKS];
|
||||
uint32 indirect;
|
||||
uint32 double_indirect;
|
||||
uint32 triple_indirect;
|
||||
} stream;
|
||||
char symlink[EXT2_SHORT_SYMLINK_LENGTH];
|
||||
};
|
||||
uint32 generation;
|
||||
uint32 file_access_control;
|
||||
uint32 directory_access_control;
|
||||
uint32 fragment;
|
||||
uint8 fragment_number;
|
||||
uint8 fragment_size;
|
||||
uint16 _padding;
|
||||
uint16 uid_high;
|
||||
uint16 gid_high;
|
||||
uint32 _reserved2;
|
||||
|
||||
uint16 Mode() const { return B_LENDIAN_TO_HOST_INT16(mode); }
|
||||
uint32 Size() const { return B_LENDIAN_TO_HOST_INT32(size); }
|
||||
uint32 Flags() const { return B_LENDIAN_TO_HOST_INT32(flags); }
|
||||
|
||||
time_t AccessTime() const { return B_LENDIAN_TO_HOST_INT32(access_time); }
|
||||
time_t CreationTime() const { return B_LENDIAN_TO_HOST_INT32(creation_time); }
|
||||
time_t ModificationTime() const { return B_LENDIAN_TO_HOST_INT32(modification_time); }
|
||||
time_t DeletionTime() const { return B_LENDIAN_TO_HOST_INT32(deletion_time); }
|
||||
|
||||
uint16 UserID() const
|
||||
{
|
||||
return B_LENDIAN_TO_HOST_INT16(uid)
|
||||
| (B_LENDIAN_TO_HOST_INT16(uid_high) << 16);
|
||||
}
|
||||
|
||||
uint16 GroupID() const
|
||||
{
|
||||
return B_LENDIAN_TO_HOST_INT16(gid)
|
||||
| (B_LENDIAN_TO_HOST_INT16(gid_high) << 16);
|
||||
}
|
||||
} _PACKED;
|
||||
|
||||
#define EXT2_SUPER_BLOCK_MAGIC 0xef53
|
||||
|
||||
// flags
|
||||
#define EXT2_INODE_SECURE_DELETION 0x00000001
|
||||
#define EXT2_INODE_UNDELETE 0x00000002
|
||||
#define EXT2_INODE_COMPRESSED 0x00000004
|
||||
#define EXT2_INODE_SYNCHRONOUS 0x00000008
|
||||
#define EXT2_INODE_IMMUTABLE 0x00000010
|
||||
#define EXT2_INODE_APPEND_ONLY 0x00000020
|
||||
#define EXT2_INODE_NO_DUMP 0x00000040
|
||||
#define EXT2_INODE_NO_ACCESS_TIME 0x00000080
|
||||
#define EXT2_INODE_DIRTY 0x00000100
|
||||
#define EXT2_INODE_COMPRESSED_BLOCKS 0x00000200
|
||||
#define EXT2_INODE_DO_NOT_COMPRESS 0x00000400
|
||||
#define EXT2_INODE_COMPRESSION_ERROR 0x00000800
|
||||
#define EXT2_INODE_BTREE 0x00001000
|
||||
|
||||
#define EXT2_NAME_LENGTH 255
|
||||
|
||||
struct ext2_dir_entry {
|
||||
uint32 inode_id;
|
||||
uint16 length;
|
||||
uint8 name_length;
|
||||
uint8 file_type;
|
||||
char name[EXT2_NAME_LENGTH];
|
||||
|
||||
uint32 InodeID() const { return B_LENDIAN_TO_HOST_INT32(inode_id); }
|
||||
uint16 Length() const { return B_LENDIAN_TO_HOST_INT16(length); }
|
||||
uint8 NameLength() const { return name_length; }
|
||||
uint8 FileType() const { return file_type; }
|
||||
|
||||
bool IsValid() const
|
||||
{
|
||||
return Length() > MinimumSize();
|
||||
// There is no maximum size, as the last entry spans until the
|
||||
// end of the block
|
||||
}
|
||||
|
||||
static size_t MinimumSize()
|
||||
{
|
||||
return sizeof(ext2_dir_entry) - EXT2_NAME_LENGTH;
|
||||
}
|
||||
} _PACKED;
|
||||
|
||||
// file types
|
||||
#define EXT2_TYPE_UNKOWN 0
|
||||
#define EXT2_TYPE_FILE 1
|
||||
#define EXT2_TYPE_DIRECTORY 2
|
||||
#define EXT2_TYPE_CHAR_DEVICE 3
|
||||
#define EXT2_TYPE_BLOCK_DEVICE 4
|
||||
#define EXT2_TYPE_FIFO 5
|
||||
#define EXT2_TYPE_SOCKET 6
|
||||
#define EXT2_TYPE_SYMLINK 7
|
||||
|
||||
extern fs_volume_ops gExt2VolumeOps;
|
||||
extern fs_vnode_ops gExt2VnodeOps;
|
||||
|
||||
#endif // EXT2_H
|
578
src/add-ons/kernel/file_systems/ext2/kernel_interface.cpp
Normal file
578
src/add-ons/kernel/file_systems/ext2/kernel_interface.cpp
Normal file
@ -0,0 +1,578 @@
|
||||
/*
|
||||
* Copyright 2008, Axel Dörfler, axeld@pinc-software.de.
|
||||
* This file may be used under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
|
||||
#include <dirent.h>
|
||||
#include <new>
|
||||
#include <string.h>
|
||||
|
||||
#include <fs_cache.h>
|
||||
#include <fs_info.h>
|
||||
|
||||
#include "DirectoryIterator.h"
|
||||
#include "ext2.h"
|
||||
#include "Inode.h"
|
||||
|
||||
|
||||
#define EXT2_IO_SIZE 65536
|
||||
|
||||
|
||||
struct identify_cookie {
|
||||
ext2_super_block super_block;
|
||||
};
|
||||
|
||||
|
||||
/*! Converts the open mode, the open flags given to bfs_open(), into
|
||||
access modes, e.g. since O_RDONLY requires read access to the
|
||||
file, it will be converted to R_OK.
|
||||
*/
|
||||
int
|
||||
open_mode_to_access(int openMode)
|
||||
{
|
||||
openMode &= O_RWMASK;
|
||||
if (openMode == O_RDONLY)
|
||||
return R_OK;
|
||||
else if (openMode == O_WRONLY)
|
||||
return W_OK;
|
||||
|
||||
return R_OK | W_OK;
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark - Scanning
|
||||
|
||||
|
||||
static float
|
||||
ext2_identify_partition(int fd, partition_data *partition, void **_cookie)
|
||||
{
|
||||
ext2_super_block superBlock;
|
||||
status_t status = Volume::Identify(fd, &superBlock);
|
||||
if (status != B_OK)
|
||||
return status;
|
||||
|
||||
identify_cookie *cookie = new identify_cookie;
|
||||
memcpy(&cookie->super_block, &superBlock, sizeof(ext2_super_block));
|
||||
|
||||
*_cookie = cookie;
|
||||
return 0.8f;
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
ext2_scan_partition(int fd, partition_data *partition, void *_cookie)
|
||||
{
|
||||
identify_cookie *cookie = (identify_cookie *)_cookie;
|
||||
|
||||
partition->status = B_PARTITION_VALID;
|
||||
partition->flags |= B_PARTITION_FILE_SYSTEM;
|
||||
partition->content_size = cookie->super_block.NumBlocks()
|
||||
<< cookie->super_block.BlockShift();
|
||||
partition->block_size = 1UL << cookie->super_block.BlockShift();
|
||||
partition->content_name = strdup(cookie->super_block.name);
|
||||
if (partition->content_name == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ext2_free_identify_partition_cookie(partition_data* partition, void* _cookie)
|
||||
{
|
||||
delete (identify_cookie*)_cookie;
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark -
|
||||
|
||||
|
||||
static status_t
|
||||
ext2_mount(fs_volume* _volume, const char* device, uint32 flags,
|
||||
const char* args, ino_t* _rootID)
|
||||
{
|
||||
Volume* volume = new Volume(_volume);
|
||||
if (volume == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
// TODO: this is a bit hacky: we can't use publish_vnode() to publish
|
||||
// the root node, or else its file cache cannot be created (we could
|
||||
// create it later, though). Therefore we're using get_vnode() in Mount(),
|
||||
// but that requires us to export our volume data before calling it.
|
||||
_volume->private_volume = volume;
|
||||
_volume->ops = &gExt2VolumeOps;
|
||||
|
||||
status_t status = volume->Mount(device, flags);
|
||||
if (status != B_OK) {
|
||||
delete volume;
|
||||
return status;
|
||||
}
|
||||
|
||||
*_rootID = volume->RootNode()->ID();
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
ext2_unmount(fs_volume *_volume)
|
||||
{
|
||||
Volume* volume = (Volume *)_volume->private_volume;
|
||||
|
||||
status_t status = volume->Unmount();
|
||||
delete volume;
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
ext2_read_fs_info(fs_volume* _volume, struct fs_info* info)
|
||||
{
|
||||
Volume* volume = (Volume*)_volume->private_volume;
|
||||
|
||||
// File system flags
|
||||
info->flags = B_FS_IS_PERSISTENT
|
||||
| (volume->IsReadOnly() ? B_FS_IS_READONLY : 0);
|
||||
info->io_size = EXT2_IO_SIZE;
|
||||
info->block_size = volume->BlockSize();
|
||||
info->total_blocks = volume->NumBlocks();
|
||||
info->free_blocks = volume->FreeBlocks();
|
||||
|
||||
// Volume name
|
||||
strlcpy(info->volume_name, volume->Name(), sizeof(info->volume_name));
|
||||
|
||||
// File system name
|
||||
strlcpy(info->fsh_name, "ext2", sizeof(info->fsh_name));
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark -
|
||||
|
||||
|
||||
static status_t
|
||||
ext2_get_vnode(fs_volume* _volume, ino_t id, fs_vnode* _node, int* _type,
|
||||
uint32* _flags, bool reenter)
|
||||
{
|
||||
Volume* volume = (Volume*)_volume->private_volume;
|
||||
|
||||
if (id < 2 || id > volume->NumBlocks()) {
|
||||
dprintf("ext2: inode at %Ld requested!\n", id);
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
Inode* inode = new Inode(volume, id);
|
||||
if (inode == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
status_t status = inode->InitCheck();
|
||||
if (status < B_OK)
|
||||
delete inode;
|
||||
|
||||
if (status == B_OK) {
|
||||
_node->private_node = inode;
|
||||
_node->ops = &gExt2VnodeOps;
|
||||
*_type = inode->Mode();
|
||||
*_flags = 0;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
ext2_put_vnode(fs_volume* _volume, fs_vnode* _node, bool reenter)
|
||||
{
|
||||
delete (Inode*)_node->private_node;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
static bool
|
||||
ext2_can_page(fs_volume* _volume, fs_vnode* _node, void* _cookie)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
ext2_read_pages(fs_volume* _volume, fs_vnode* _node, void* _cookie,
|
||||
off_t pos, const iovec* vecs, size_t count, size_t* _numBytes, bool reenter)
|
||||
{
|
||||
Volume* volume = (Volume*)_volume->private_volume;
|
||||
Inode* inode = (Inode*)_node->private_node;
|
||||
|
||||
if (inode->FileCache() == NULL)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
if (!reenter)
|
||||
rw_lock_read_lock(inode->Lock());
|
||||
|
||||
uint32 vecIndex = 0;
|
||||
size_t vecOffset = 0;
|
||||
size_t bytesLeft = *_numBytes;
|
||||
status_t status;
|
||||
|
||||
while (true) {
|
||||
file_io_vec fileVecs[8];
|
||||
uint32 fileVecCount = 8;
|
||||
|
||||
status = file_map_translate(inode->Map(), pos, bytesLeft, fileVecs,
|
||||
&fileVecCount);
|
||||
if (status != B_OK && status != B_BUFFER_OVERFLOW)
|
||||
break;
|
||||
|
||||
bool bufferOverflow = status == B_BUFFER_OVERFLOW;
|
||||
|
||||
size_t bytes = bytesLeft;
|
||||
status = read_file_io_vec_pages(volume->Device(), fileVecs,
|
||||
fileVecCount, vecs, count, &vecIndex, &vecOffset, &bytes);
|
||||
if (status != B_OK || !bufferOverflow)
|
||||
break;
|
||||
|
||||
pos += bytes;
|
||||
bytesLeft -= bytes;
|
||||
}
|
||||
|
||||
if (!reenter)
|
||||
rw_lock_read_unlock(inode->Lock());
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
ext2_get_file_map(fs_volume* _volume, fs_vnode* _node, off_t offset,
|
||||
size_t size, struct file_io_vec* vecs, size_t* _count)
|
||||
{
|
||||
Volume* volume = (Volume*)_volume->private_volume;
|
||||
Inode* inode = (Inode*)_node->private_node;
|
||||
size_t index = 0, max = *_count;
|
||||
|
||||
while (true) {
|
||||
uint32 block;
|
||||
status_t status = inode->FindBlock(offset, block);
|
||||
if (status != B_OK)
|
||||
return status;
|
||||
|
||||
off_t blockOffset = block << volume->BlockShift();
|
||||
uint32 blockLength = volume->BlockSize();
|
||||
|
||||
if (index > 0 && vecs[index - 1].offset == blockOffset - blockLength) {
|
||||
vecs[index - 1].length += blockLength;
|
||||
} else {
|
||||
if (index >= max) {
|
||||
// we're out of file_io_vecs; let's bail out
|
||||
*_count = index;
|
||||
return B_BUFFER_OVERFLOW;
|
||||
}
|
||||
|
||||
vecs[index].offset = blockOffset;
|
||||
vecs[index].length = blockLength;
|
||||
index++;
|
||||
}
|
||||
|
||||
offset += blockLength;
|
||||
|
||||
if (size <= vecs[index - 1].length || offset >= inode->Size()) {
|
||||
// We're done!
|
||||
*_count = index;
|
||||
return B_OK;
|
||||
}
|
||||
}
|
||||
|
||||
// can never get here
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark -
|
||||
|
||||
|
||||
static status_t
|
||||
ext2_lookup(fs_volume* _volume, fs_vnode* _directory, const char* name,
|
||||
ino_t* _vnodeID)
|
||||
{
|
||||
Volume* volume = (Volume*)_volume->private_volume;
|
||||
Inode* directory = (Inode*)_directory->private_node;
|
||||
|
||||
// check access permissions
|
||||
status_t status = directory->CheckPermissions(X_OK);
|
||||
if (status < B_OK)
|
||||
return status;
|
||||
|
||||
DirectoryIterator iterator(directory);
|
||||
while (true) {
|
||||
char buffer[B_FILE_NAME_LENGTH];
|
||||
size_t length = sizeof(buffer);
|
||||
status = iterator.GetNext(buffer, &length, _vnodeID);
|
||||
if (status != B_OK)
|
||||
return status;
|
||||
|
||||
if (!strcmp(buffer, name))
|
||||
break;
|
||||
}
|
||||
|
||||
return get_vnode(volume->FSVolume(), *_vnodeID, NULL);
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
ext2_read_stat(fs_volume* _volume, fs_vnode* _node, struct stat* stat)
|
||||
{
|
||||
Inode* inode = (Inode*)_node->private_node;
|
||||
const ext2_inode& node = inode->Node();
|
||||
|
||||
stat->st_dev = inode->GetVolume()->ID();
|
||||
stat->st_ino = inode->ID();
|
||||
stat->st_nlink = 1;
|
||||
stat->st_blksize = EXT2_IO_SIZE;
|
||||
|
||||
stat->st_uid = node.UserID();
|
||||
stat->st_gid = node.GroupID();
|
||||
stat->st_mode = node.Mode();
|
||||
stat->st_type = 0;
|
||||
|
||||
stat->st_atime = node.AccessTime();
|
||||
stat->st_mtime = stat->st_ctime = node.ModificationTime();
|
||||
stat->st_crtime = node.CreationTime();
|
||||
|
||||
stat->st_size = inode->Size();
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
ext2_open(fs_volume* _volume, fs_vnode* _node, int openMode, void** _cookie)
|
||||
{
|
||||
Inode* inode = (Inode*)_node->private_node;
|
||||
|
||||
// opening a directory read-only is allowed, although you can't read
|
||||
// any data from it.
|
||||
if (inode->IsDirectory() && (openMode & O_RWMASK) != 0)
|
||||
return B_IS_A_DIRECTORY;
|
||||
|
||||
if ((openMode & O_TRUNC) != 0)
|
||||
return B_READ_ONLY_DEVICE;
|
||||
|
||||
return inode->CheckPermissions(open_mode_to_access(openMode)
|
||||
| (openMode & O_TRUNC ? W_OK : 0));
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
ext2_read(fs_volume *_volume, fs_vnode *_node, void *_cookie, off_t pos,
|
||||
void *buffer, size_t *_length)
|
||||
{
|
||||
Inode *inode = (Inode *)_node->private_node;
|
||||
|
||||
if (!inode->IsFile()) {
|
||||
*_length = 0;
|
||||
return inode->IsDirectory() ? B_IS_A_DIRECTORY : B_BAD_VALUE;
|
||||
}
|
||||
|
||||
return inode->ReadAt(pos, (uint8 *)buffer, _length);
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
ext2_close(fs_volume *_volume, fs_vnode *_node, void *_cookie)
|
||||
{
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
ext2_free_cookie(fs_volume* _volume, fs_vnode* _node, void* cookie)
|
||||
{
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
ext2_access(fs_volume* _volume, fs_vnode* _node, int accessMode)
|
||||
{
|
||||
Inode* inode = (Inode*)_node->private_node;
|
||||
return inode->CheckPermissions(accessMode);
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
ext2_read_link(fs_volume *_volume, fs_vnode *_node, char *buffer,
|
||||
size_t *_bufferSize)
|
||||
{
|
||||
Inode* inode = (Inode*)_node->private_node;
|
||||
|
||||
if (!inode->IsSymLink())
|
||||
return B_BAD_VALUE;
|
||||
|
||||
if (inode->Size() < *_bufferSize)
|
||||
*_bufferSize = inode->Size();
|
||||
|
||||
if (inode->Size() > EXT2_SHORT_SYMLINK_LENGTH)
|
||||
return inode->ReadAt(0, (uint8 *)buffer, _bufferSize);
|
||||
|
||||
memcpy(buffer, inode->Node().symlink, *_bufferSize);
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark - Directory functions
|
||||
|
||||
|
||||
static status_t
|
||||
ext2_open_dir(fs_volume *_volume, fs_vnode *_node, void **_cookie)
|
||||
{
|
||||
Inode *inode = (Inode *)_node->private_node;
|
||||
status_t status = inode->CheckPermissions(R_OK);
|
||||
if (status < B_OK)
|
||||
return status;
|
||||
|
||||
if (!inode->IsDirectory())
|
||||
return B_BAD_VALUE;
|
||||
|
||||
DirectoryIterator* iterator = new(std::nothrow) DirectoryIterator(inode);
|
||||
if (iterator == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
*_cookie = iterator;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
ext2_read_dir(fs_volume *_volume, fs_vnode *_node, void *_cookie,
|
||||
struct dirent *dirent, size_t bufferSize, uint32 *_num)
|
||||
{
|
||||
DirectoryIterator *iterator = (DirectoryIterator *)_cookie;
|
||||
|
||||
size_t length = bufferSize;
|
||||
ino_t id;
|
||||
status_t status = iterator->GetNext(dirent->d_name, &length, &id);
|
||||
if (status == B_ENTRY_NOT_FOUND) {
|
||||
*_num = 0;
|
||||
return B_OK;
|
||||
} else if (status != B_OK)
|
||||
return status;
|
||||
|
||||
Volume* volume = (Volume*)_volume->private_volume;
|
||||
|
||||
dirent->d_dev = volume->ID();
|
||||
dirent->d_ino = id;
|
||||
dirent->d_reclen = sizeof(struct dirent) + length;
|
||||
|
||||
*_num = 1;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
ext2_rewind_dir(fs_volume * /*_volume*/, fs_vnode * /*node*/, void *_cookie)
|
||||
{
|
||||
DirectoryIterator *iterator = (DirectoryIterator *)_cookie;
|
||||
return iterator->Rewind();
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
ext2_close_dir(fs_volume * /*_volume*/, fs_vnode * /*node*/, void * /*_cookie*/)
|
||||
{
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
ext2_free_dir_cookie(fs_volume *_volume, fs_vnode *_node, void *_cookie)
|
||||
{
|
||||
delete (DirectoryIterator *)_cookie;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
fs_volume_ops gExt2VolumeOps = {
|
||||
&ext2_unmount,
|
||||
&ext2_read_fs_info,
|
||||
NULL, // write_fs_info()
|
||||
NULL, // sync()
|
||||
&ext2_get_vnode,
|
||||
};
|
||||
|
||||
fs_vnode_ops gExt2VnodeOps = {
|
||||
/* vnode operations */
|
||||
&ext2_lookup,
|
||||
NULL,
|
||||
&ext2_put_vnode,
|
||||
NULL,
|
||||
|
||||
/* VM file access */
|
||||
&ext2_can_page,
|
||||
&ext2_read_pages,
|
||||
NULL,
|
||||
|
||||
&ext2_get_file_map,
|
||||
|
||||
NULL,
|
||||
NULL,
|
||||
NULL, // fs_select
|
||||
NULL, // fs_deselect
|
||||
NULL,
|
||||
|
||||
&ext2_read_link,
|
||||
NULL,
|
||||
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
|
||||
&ext2_access,
|
||||
&ext2_read_stat,
|
||||
NULL,
|
||||
|
||||
/* file operations */
|
||||
NULL,
|
||||
&ext2_open,
|
||||
&ext2_close,
|
||||
&ext2_free_cookie,
|
||||
&ext2_read,
|
||||
NULL,
|
||||
|
||||
/* directory operations */
|
||||
NULL,
|
||||
NULL,
|
||||
&ext2_open_dir,
|
||||
&ext2_close_dir,
|
||||
&ext2_free_dir_cookie,
|
||||
&ext2_read_dir,
|
||||
&ext2_rewind_dir,
|
||||
|
||||
NULL,
|
||||
};
|
||||
|
||||
static file_system_module_info sExt2FileSystem = {
|
||||
{
|
||||
"file_systems/ext2" B_CURRENT_FS_API_VERSION,
|
||||
0,
|
||||
NULL,
|
||||
},
|
||||
|
||||
"ext2", // short_name
|
||||
"Ext2 File System", // pretty_name
|
||||
0, // DDM flags
|
||||
|
||||
// scanning
|
||||
ext2_identify_partition,
|
||||
ext2_scan_partition,
|
||||
ext2_free_identify_partition_cookie,
|
||||
NULL, // free_partition_content_cookie()
|
||||
|
||||
&ext2_mount,
|
||||
|
||||
NULL,
|
||||
};
|
||||
|
||||
module_info *modules[] = {
|
||||
(module_info *)&sExt2FileSystem,
|
||||
NULL,
|
||||
};
|
Loading…
Reference in New Issue
Block a user