diff --git a/src/tests/system/kernel/file_corruption/checksumfs.h b/src/tests/system/kernel/file_corruption/checksumfs.h index b76b680d87..13ce788903 100644 --- a/src/tests/system/kernel/file_corruption/checksumfs.h +++ b/src/tests/system/kernel/file_corruption/checksumfs.h @@ -38,4 +38,34 @@ struct checksumfs_super_block { } _PACKED; +struct checksumfs_node { + uint32 mode; // node type + permissions + uint32 attributeType; // attribute type (attributes only) + uint32 uid; // owning user ID + uint32 gid; // owning group ID + uint64 creationTime; // in ns since the epoche + uint64 modificationTime; // + uint64 changeTime; // + uint64 hardLinks; // number of references to the node + uint64 content; // block index of the content (0 if empty) + uint64 size; // content size in bytes + uint64 parentDirectory; // block index of the parent directory + // (directories and attributes only) + uint64 attributeDirectory; // block index of the attribute directory (0 if + // empty) +} _PACKED; + + +struct checksumfs_dir_entry_block { + uint16 entryCount; + uint16 nameEnds[0]; // end (in-block) offsets of the names, + // e.g. nameEnds[0] == length of first name + // char names[]; // string of all (unterminated) names, + // directly follows the nameEnds array + // ... + // uint64 nodes[]; + // node array ends at the end of the block +}; + + #endif // CHECK_SUM_FS_H diff --git a/src/tests/system/kernel/file_corruption/fs/BlockAllocator.h b/src/tests/system/kernel/file_corruption/fs/BlockAllocator.h index 45f2d60c3a..394227333b 100644 --- a/src/tests/system/kernel/file_corruption/fs/BlockAllocator.h +++ b/src/tests/system/kernel/file_corruption/fs/BlockAllocator.h @@ -58,4 +58,46 @@ private: }; +class AllocatedBlock { +public: + AllocatedBlock(BlockAllocator* allocator) + : + fAllocator(allocator), + fIndex(0) + { + } + + ~AllocatedBlock() + { + if (fIndex > 0) + fAllocator->Free(fIndex, 1); + } + + uint64 Index() const + { + return fIndex; + } + + status_t Allocate(uint64 baseHint = 0) + { + uint64 allocatedBlocks; + status_t error = fAllocator->Allocate(0, 1, fIndex, allocatedBlocks); + if (error != B_OK) + fIndex = 0; + return error; + } + + uint64 Detach() + { + uint64 index = fIndex; + fIndex = 0; + return index; + } + +private: + BlockAllocator* fAllocator; + uint64 fIndex; +}; + + #endif // BLOCK_ALLOCATOR_H diff --git a/src/tests/system/kernel/file_corruption/fs/DebugSupport.h b/src/tests/system/kernel/file_corruption/fs/DebugSupport.h new file mode 100644 index 0000000000..dffdecf020 --- /dev/null +++ b/src/tests/system/kernel/file_corruption/fs/DebugSupport.h @@ -0,0 +1,7 @@ +/* + * Copyright 2010, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ + + +#include "packagefs/DebugSupport.h" diff --git a/src/tests/system/kernel/file_corruption/fs/Directory.cpp b/src/tests/system/kernel/file_corruption/fs/Directory.cpp new file mode 100644 index 0000000000..b234527d70 --- /dev/null +++ b/src/tests/system/kernel/file_corruption/fs/Directory.cpp @@ -0,0 +1,27 @@ +/* + * Copyright 2010, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ + + +#include "Directory.h" + + +Directory::Directory(Volume* volume, uint64 blockIndex, + const checksumfs_node& nodeData) + : + Node(volume, blockIndex, nodeData) +{ +} + + +Directory::Directory(Volume* volume, uint64 blockIndex, mode_t mode) + : + Node(volume, blockIndex, mode) +{ +} + + +Directory::~Directory() +{ +} diff --git a/src/tests/system/kernel/file_corruption/fs/Directory.h b/src/tests/system/kernel/file_corruption/fs/Directory.h new file mode 100644 index 0000000000..d44b334fd6 --- /dev/null +++ b/src/tests/system/kernel/file_corruption/fs/Directory.h @@ -0,0 +1,22 @@ +/* + * Copyright 2010, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ +#ifndef DIRECTORY_H +#define DIRECTORY_H + + +#include "Node.h" + + +class Directory : public Node { +public: + Directory(Volume* volume, uint64 blockIndex, + const checksumfs_node& nodeData); + Directory(Volume* volume, uint64 blockIndex, + mode_t mode); + virtual ~Directory(); +}; + + +#endif // DIRECTORY_H diff --git a/src/tests/system/kernel/file_corruption/fs/Jamfile b/src/tests/system/kernel/file_corruption/fs/Jamfile index 9b3a448658..245cbf1d65 100644 --- a/src/tests/system/kernel/file_corruption/fs/Jamfile +++ b/src/tests/system/kernel/file_corruption/fs/Jamfile @@ -6,17 +6,37 @@ UsePrivateHeaders shared ; UseHeaders [ FDirName $(SUBDIR) $(DOTDOT) ] ; +UseHeaders [ FDirName $(HAIKU_TOP) src add-ons kernel file_systems ] ; + # We use packagefs's DebugSupport.h header, but we don't want to add that + # header to avoid clashes with other headers. -KernelAddon checksumfs : + +DEFINES += DEBUG_APP="\\\"checksumfs\\\"" ; + + +HAIKU_CHECKSUM_FS_SOURCES = BlockAllocator.cpp checksumfs.cpp + Directory.cpp + Node.cpp SuperBlock.cpp Volume.cpp +; + +KernelAddon checksumfs : + $(HAIKU_CHECKSUM_FS_SOURCES) + + # from packagefs + DebugSupport.cpp # from src/kits/shared SHA256.cpp ; +SEARCH on [ FGristFiles DebugSupport.cpp ] + = [ FDirName $(HAIKU_TOP) src add-ons kernel file_systems packagefs ] ; SEARCH on [ FGristFiles SHA256.cpp ] = [ FDirName $(HAIKU_TOP) src kits shared ] ; + +HaikuSubInclude userland ; diff --git a/src/tests/system/kernel/file_corruption/fs/Node.cpp b/src/tests/system/kernel/file_corruption/fs/Node.cpp new file mode 100644 index 0000000000..ec77f88401 --- /dev/null +++ b/src/tests/system/kernel/file_corruption/fs/Node.cpp @@ -0,0 +1,76 @@ +/* + * Copyright 2010, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ + + +#include "Node.h" + +#include +#include + +#include "Block.h" + + +Node::Node(Volume* volume, uint64 blockIndex, const checksumfs_node& nodeData) + : + fVolume(volume), + fBlockIndex(blockIndex), + fNode(nodeData), + fNodeDataDirty(false) +{ + _Init(); +} + + +Node::Node(Volume* volume, uint64 blockIndex, mode_t mode) + : + fVolume(volume), + fBlockIndex(blockIndex), + fNodeDataDirty(true) +{ + _Init(); + + memset(&fNode, 0, sizeof(fNode)); + + fNode.mode = mode; + + // set the times + timeval time; + gettimeofday(&time, NULL); + + fNode.creationTime = (uint64)time.tv_sec * 1000000000 + + (uint64)time.tv_usec * 1000; + fNode.modificationTime = fNode.creationTime; + fNode.changeTime = fNode.creationTime; +} + + +Node::~Node() +{ + rw_lock_destroy(&fLock); +} + + +status_t +Node::Flush() +{ + if (!fNodeDataDirty) + return B_OK; + + Block block; + if (!block.GetWritable(fVolume, fBlockIndex)) + return B_ERROR; + + memcpy(block.Data(), &fNode, sizeof(fNode)); + + fNodeDataDirty = false; + return B_OK; +} + + +void +Node::_Init() +{ + rw_lock_init(&fLock, "checkfs node"); +} diff --git a/src/tests/system/kernel/file_corruption/fs/Node.h b/src/tests/system/kernel/file_corruption/fs/Node.h new file mode 100644 index 0000000000..453393be52 --- /dev/null +++ b/src/tests/system/kernel/file_corruption/fs/Node.h @@ -0,0 +1,114 @@ +/* + * Copyright 2010, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ +#ifndef NODE_H +#define NODE_H + + +#include + +#include + +#include "checksumfs.h" + + +class Volume; + + +class Node { +public: + Node(Volume* volume, uint64 blockIndex, + const checksumfs_node& nodeData); + Node(Volume* volume, uint64 blockIndex, + mode_t mode); + virtual ~Node(); + + inline Volume* GetVolume() const { return fVolume; } + inline uint64 BlockIndex() const { return fBlockIndex; } + inline uint32 Mode() const { return fNode.mode; } + inline uint64 ParentDirectory() const; + inline uint32 HardLinks() const { return fNode.hardLinks; } + inline uint32 UID() const { return fNode.uid; } + inline uint32 GID() const { return fNode.gid; } + inline uint64 Size() const { return fNode.size; } + inline uint64 CreationTime() const; + inline uint64 ModificationTime() const; + inline uint64 ChangeTime() const; + + inline bool ReadLock(); + inline void ReadUnlock(); + inline bool WriteLock(); + inline void WriteUnlock(); + + status_t Flush(); + +private: + void _Init(); + +private: + rw_lock fLock; + Volume* fVolume; + uint64 fBlockIndex; + checksumfs_node fNode; + bool fNodeDataDirty; +}; + + +uint64 +Node::ParentDirectory() const +{ + return fNode.parentDirectory; +} + + +uint64 +Node::CreationTime() const +{ + return fNode.creationTime; +} + + +uint64 +Node::ModificationTime() const +{ + return fNode.modificationTime; +} + + +uint64 +Node::ChangeTime() const +{ + return fNode.changeTime; +} + + +bool +Node::ReadLock() +{ + return rw_lock_read_lock(&fLock) == B_OK; +} + + +void +Node::ReadUnlock() +{ + rw_lock_read_unlock(&fLock); +} + + +bool +Node::WriteLock() +{ + return rw_lock_write_lock(&fLock) == B_OK; +} + + +void +Node::WriteUnlock() +{ + rw_lock_write_unlock(&fLock); +} + + +#endif // NODE_H diff --git a/src/tests/system/kernel/file_corruption/fs/SuperBlock.cpp b/src/tests/system/kernel/file_corruption/fs/SuperBlock.cpp index e359094c4b..02ad536bee 100644 --- a/src/tests/system/kernel/file_corruption/fs/SuperBlock.cpp +++ b/src/tests/system/kernel/file_corruption/fs/SuperBlock.cpp @@ -9,6 +9,7 @@ #include #include "BlockAllocator.h" +#include "Directory.h" #include "Volume.h" @@ -41,6 +42,6 @@ SuperBlock::Initialize(Volume* volume) totalBlocks = volume->TotalBlocks(); freeBlocks = volume->GetBlockAllocator()->FreeBlocks(); blockBitmap = volume->GetBlockAllocator()->BaseBlock(); -// rootDir = volume->RootDirBlock(); + rootDir = volume->RootDirectory()->BlockIndex(); strlcpy(name, volume->Name(), kCheckSumFSNameLength); } diff --git a/src/tests/system/kernel/file_corruption/fs/SuperBlock.h b/src/tests/system/kernel/file_corruption/fs/SuperBlock.h index 3856da53e0..be4d1cdc28 100644 --- a/src/tests/system/kernel/file_corruption/fs/SuperBlock.h +++ b/src/tests/system/kernel/file_corruption/fs/SuperBlock.h @@ -14,12 +14,12 @@ class Volume; struct SuperBlock : private checksumfs_super_block { public: - uint64 TotalBlocks() const { return totalBlocks; } - uint64 FreeBlocks() const { return freeBlocks; } - uint32 Version() const { return version; } - const char* Name() const { return name; } - uint64 BlockBitmap() const { return blockBitmap; } - uint64 RootDir() const { return rootDir; } + uint64 TotalBlocks() const { return totalBlocks; } + uint64 FreeBlocks() const { return freeBlocks; } + uint32 Version() const { return version; } + const char* Name() const { return name; } + uint64 BlockBitmap() const { return blockBitmap; } + uint64 RootDirectory() const { return rootDir; } bool Check(uint64 totalBlocks) const; void Initialize(Volume* volume); diff --git a/src/tests/system/kernel/file_corruption/fs/Volume.cpp b/src/tests/system/kernel/file_corruption/fs/Volume.cpp index 91df9f068b..ee80024760 100644 --- a/src/tests/system/kernel/file_corruption/fs/Volume.cpp +++ b/src/tests/system/kernel/file_corruption/fs/Volume.cpp @@ -20,23 +20,30 @@ #include "Block.h" #include "BlockAllocator.h" #include "checksumfs.h" +#include "checksumfs_private.h" +#include "DebugSupport.h" +#include "Directory.h" #include "SuperBlock.h" Volume::Volume(uint32 flags) : + fFSVolume(NULL), fFD(-1), - fFlags(flags), + fFlags(B_FS_IS_PERSISTENT + | (flags & B_FS_IS_READONLY != 0 ? B_FS_IS_READONLY : 0)), fBlockCache(NULL), fTotalBlocks(0), fName(NULL), - fBlockAllocator(NULL) + fBlockAllocator(NULL), + fRootDirectory(NULL) { } Volume::~Volume() { + delete fRootDirectory; delete fBlockAllocator; if (fBlockCache != NULL) @@ -62,7 +69,27 @@ Volume::Init(const char* device) if (fstat(fFD, &st) < 0) return errno; - return _Init(st.st_size / B_PAGE_SIZE); + off_t size; + switch (st.st_mode & S_IFMT) { + case S_IFREG: + size = st.st_size; + break; + case S_IFCHR: + case S_IFBLK: + { + device_geometry geometry; + if (ioctl(fFD, B_GET_GEOMETRY, &geometry, sizeof(geometry)) < 0) + return errno; + + size = (off_t)geometry.bytes_per_sector * geometry.sectors_per_track + * geometry.cylinder_count * geometry.head_count; + break; + } + default: + return B_BAD_VALUE; + } + + return _Init(size / B_PAGE_SIZE); } @@ -71,17 +98,68 @@ Volume::Init(int fd, uint64 totalBlocks) { fFD = dup(fd); if (fFD < 0) - return errno; + RETURN_ERROR(errno); return _Init(totalBlocks); } status_t -Volume::Mount() +Volume::Mount(fs_volume* fsVolume) { - // TODO:... - return B_UNSUPPORTED; + fFSVolume = fsVolume; + + // load the superblock + Block block; + if (!block.GetReadable(this, kCheckSumFSSuperBlockOffset / B_PAGE_SIZE)) + RETURN_ERROR(B_ERROR); + + SuperBlock* superBlock = (SuperBlock*)block.Data(); + if (!superBlock->Check(fTotalBlocks)) + RETURN_ERROR(B_BAD_DATA); + + // copy the volume name + fName = strdup(superBlock->Name()); + if (fName == NULL) + RETURN_ERROR(B_NO_MEMORY); + + // init the block allocator + status_t error = fBlockAllocator->Init(superBlock->BlockBitmap(), + superBlock->FreeBlocks()); + if (error != B_OK) + RETURN_ERROR(error); + + // load the root directory + Node* rootNode; + error = ReadNode(superBlock->RootDirectory(), rootNode); + if (error != B_OK) + RETURN_ERROR(error); + + fRootDirectory = dynamic_cast(rootNode); + if (fRootDirectory == NULL) { + delete rootNode; + RETURN_ERROR(B_BAD_DATA); + } + + error = PublishNode(fRootDirectory, 0); + if (error != B_OK) { + delete fRootDirectory; + fRootDirectory = NULL; + return error; + } + + return B_OK; +} + + +void +Volume::Unmount() +{ + status_t error = block_cache_sync(fBlockCache); + if (error != B_OK) { + dprintf("checksumfs: Error: Failed to sync block cache when " + "unmounting!\n"); + } } @@ -96,6 +174,17 @@ Volume::Initialize(const char* name) if (error != B_OK) return error; + // create the root directory + error = CreateDirectory(S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH, + fRootDirectory); + if (error != B_OK) + return error; + + error = fRootDirectory->Flush(); + if (error != B_OK) + return error; + + // write the super block Block block; if (!block.GetZero(this, kCheckSumFSSuperBlockOffset / B_PAGE_SIZE)) return B_ERROR; @@ -109,23 +198,117 @@ Volume::Initialize(const char* name) } +void +Volume::GetInfo(fs_info& info) +{ + info.flags = fFlags; + info.block_size = B_PAGE_SIZE; + info.io_size = B_PAGE_SIZE * 16; // random value + info.total_blocks = fTotalBlocks; + info.free_blocks = fBlockAllocator->FreeBlocks(); + info.total_nodes = 1; // phew, who cares? + info.free_nodes = info.free_blocks; + strlcpy(info.volume_name, fName, sizeof(info.volume_name)); + // TODO: We need locking once we are able to change the name! +} + + +status_t +Volume::PublishNode(Node* node, uint32 flags) +{ + return publish_vnode(fFSVolume, node->BlockIndex(), node, + &gCheckSumFSVnodeOps, node->Mode(), flags); +} + + +status_t +Volume::GetNode(uint64 blockIndex, Node*& _node) +{ + return get_vnode(fFSVolume, blockIndex, (void**)&_node); +} + + +status_t +Volume::PutNode(Node* node) +{ + return put_vnode(fFSVolume, node->BlockIndex()); +} + + +status_t +Volume::ReadNode(uint64 blockIndex, Node*& _node) +{ + if (blockIndex == 0 || blockIndex >= fTotalBlocks) + return B_BAD_VALUE; + + // load the node's block + Block block; + if (!block.GetReadable(this, blockIndex)) + return B_ERROR; + + checksumfs_node* nodeData = (checksumfs_node*)block.Data(); + + // create the Node object + Node* node; + switch (nodeData->mode & S_IFMT) { + // TODO: Don't directly access mode! + case S_IFDIR: + node = new(std::nothrow) Directory(this, blockIndex, *nodeData); + break; + default: + node = new(std::nothrow) Node(this, blockIndex, *nodeData); + break; + } + + if (node == NULL) + return B_NO_MEMORY; + + // TODO: Sanity check the node! + + _node = node; + return B_OK; +} + + +status_t +Volume::CreateDirectory(mode_t mode, Directory*& _directory) +{ + // allocate a free block + AllocatedBlock allocatedBlock(fBlockAllocator); + status_t error = allocatedBlock.Allocate(); + if (error != B_OK) + return error; + + // create the directory + Directory* directory = new(std::nothrow) Directory(this, + allocatedBlock.Index(), (mode & ~(mode_t)S_IFMT) | S_IFDIR); + if (directory == NULL) + return B_NO_MEMORY; + + allocatedBlock.Detach(); + _directory = directory; + + return B_OK; +} + + status_t Volume::_Init(uint64 totalBlocks) { fTotalBlocks = totalBlocks; if (fTotalBlocks * B_PAGE_SIZE < kCheckSumFSMinSize) - return B_BAD_VALUE; + RETURN_ERROR(B_BAD_VALUE); // create a block cache fBlockCache = block_cache_create(fFD, fTotalBlocks, B_PAGE_SIZE, IsReadOnly()); if (fBlockCache == NULL) - return B_NO_MEMORY; + RETURN_ERROR(B_NO_MEMORY); // create the block allocator fBlockAllocator = new(std::nothrow) BlockAllocator(this); if (fBlockAllocator == NULL) - return B_NO_MEMORY; + RETURN_ERROR(B_NO_MEMORY); return B_OK; } diff --git a/src/tests/system/kernel/file_corruption/fs/Volume.h b/src/tests/system/kernel/file_corruption/fs/Volume.h index d79a53bd18..3de563bd89 100644 --- a/src/tests/system/kernel/file_corruption/fs/Volume.h +++ b/src/tests/system/kernel/file_corruption/fs/Volume.h @@ -6,11 +6,14 @@ #define VOLUME_H +#include #include #include class BlockAllocator; +class Directory; +class Node; class Volume { @@ -21,33 +24,51 @@ public: status_t Init(const char* device); status_t Init(int fd, uint64 totalBlocks); - status_t Mount(); + status_t Mount(fs_volume* fsVolume); + void Unmount(); + status_t Initialize(const char* name); + void GetInfo(fs_info& info); + + status_t PublishNode(Node* node, uint32 flags); + status_t GetNode(uint64 blockIndex, Node*& _node); + status_t PutNode(Node* node); + + status_t ReadNode(uint64 blockIndex, Node*& _node); + + status_t CreateDirectory(mode_t mode, + Directory*& _directory); + + inline dev_t ID() const { return fFSVolume->id; } inline bool IsReadOnly() const; inline uint64 TotalBlocks() const { return fTotalBlocks; } inline void* BlockCache() const { return fBlockCache; } inline const char* Name() const { return fName; } inline BlockAllocator* GetBlockAllocator() const { return fBlockAllocator; } + inline Directory* RootDirectory() const + { return fRootDirectory; } private: status_t _Init(uint64 totalBlocks); private: + fs_volume* fFSVolume; int fFD; uint32 fFlags; void* fBlockCache; uint64 fTotalBlocks; char* fName; BlockAllocator* fBlockAllocator; + Directory* fRootDirectory; }; bool Volume::IsReadOnly() const { - return (fFlags & B_MOUNT_READ_ONLY) != 0; + return (fFlags & B_FS_IS_READONLY) != 0; } diff --git a/src/tests/system/kernel/file_corruption/fs/checksumfs.cpp b/src/tests/system/kernel/file_corruption/fs/checksumfs.cpp index 4cab3543ed..6ee54305d0 100644 --- a/src/tests/system/kernel/file_corruption/fs/checksumfs.cpp +++ b/src/tests/system/kernel/file_corruption/fs/checksumfs.cpp @@ -4,6 +4,7 @@ */ +#include #include #include @@ -13,6 +14,9 @@ #include #include "checksumfs.h" +#include "checksumfs_private.h" +#include "DebugSupport.h" +#include "Directory.h" #include "SuperBlock.h" #include "Volume.h" @@ -22,7 +26,15 @@ static const char* const kCheckSumFSModuleName = "file_systems/checksumfs" static const char* const kCheckSumFSShortName = "checksumfs"; -// #pragma mark - +static void +set_timespec(timespec& time, uint64 nanos) +{ + time.tv_sec = nanos / 1000000000; + time.tv_nsec = nanos % 1000000000; +} + + +// #pragma mark - FS operations static float @@ -77,11 +89,27 @@ checksumfs_free_identify_partition_cookie(partition_data* partition, static status_t -checksumfs_mount(fs_volume* volume, const char* device, uint32 flags, +checksumfs_mount(fs_volume* fsVolume, const char* device, uint32 flags, const char* args, ino_t* _rootVnodeID) { - // TODO: Implement! - return B_UNSUPPORTED; + Volume* volume = new(std::nothrow) Volume(flags); + if (volume == NULL) + RETURN_ERROR(B_NO_MEMORY); + ObjectDeleter volumeDeleter(volume); + + status_t error = volume->Init(device); + if (error != B_OK) + RETURN_ERROR(error); + + error = volume->Mount(fsVolume); + if (error != B_OK) + RETURN_ERROR(error); + + fsVolume->private_volume = volumeDeleter.Detach(); + fsVolume->ops = &gCheckSumFSVolumeOps; + *_rootVnodeID = volume->RootDirectory()->BlockIndex(); + + return B_OK; } @@ -129,13 +157,299 @@ checksumfs_initialize(int fd, partition_id partition, const char* name, } +// #pragma mark - volume operations + + +static status_t +checksumfs_unmount(fs_volume* fsVolume) +{ + Volume* volume = (Volume*)fsVolume->private_volume; + volume->Unmount(); + return B_OK; +} + + +static status_t +checksumfs_read_fs_info(fs_volume* fsVolume, struct fs_info* info) +{ + Volume* volume = (Volume*)fsVolume->private_volume; + volume->GetInfo(*info); + return B_OK; +} + + +static status_t +checksumfs_get_vnode(fs_volume* fsVolume, ino_t id, fs_vnode* vnode, + int* _type, uint32* _flags, bool reenter) +{ + Volume* volume = (Volume*)fsVolume->private_volume; + + Node* node; + status_t error = volume->ReadNode(id, node); + if (error != B_OK) + return error; + + vnode->private_node = node; + vnode->ops = &gCheckSumFSVnodeOps; + *_type = node->Mode(); + + return B_OK; +} + + +// #pragma mark - vnode operations + + +static status_t +checksumfs_lookup(fs_volume* fsVolume, fs_vnode* fsDir, const char* name, + ino_t* _id) +{ + Volume* volume = (Volume*)fsVolume->private_volume; + Node* node = (Node*)fsDir->private_node; + + Directory* directory = dynamic_cast(node); + if (directory == NULL) + return B_NOT_A_DIRECTORY; + + uint64 blockIndex; + + if (strcmp(name, ".") == 0) { + blockIndex = directory->BlockIndex(); + } else if (strcmp(name, "..") == 0) { + blockIndex = directory->ParentDirectory(); + } else { + // TODO: Implement! + return B_ENTRY_NOT_FOUND; + } + + // get the node + Node* childNode; + status_t error = volume->GetNode(blockIndex, childNode); + if (error != B_OK) + return error; + + *_id = blockIndex; + return B_OK; +} + + +static status_t +checksumfs_put_vnode(fs_volume* fsVolume, fs_vnode* vnode, bool reenter) +{ + Node* node = (Node*)vnode->private_node; + delete node; + return B_OK; +} + + +// #pragma mark - common operations + + +static status_t +checksumfs_read_stat(fs_volume* fsVolume, fs_vnode* vnode, struct stat* st) +{ + Node* node = (Node*)vnode->private_node; + + st->st_mode = node->Mode(); + st->st_nlink = node->HardLinks(); + st->st_uid = node->UID(); + st->st_gid = node->GID(); + st->st_size = node->Size(); + st->st_blksize = B_PAGE_SIZE * 16; // random number + set_timespec(st->st_mtim, node->ModificationTime()); + set_timespec(st->st_ctim, node->ChangeTime()); + set_timespec(st->st_crtim, node->CreationTime()); + st->st_atim = st->st_ctim; + // we don't support access time + st->st_type = 0; /* attribute/index type */ + st->st_blocks = 1 + (st->st_size + B_PAGE_SIZE - 1) / B_PAGE_SIZE; + // TODO: That does neither count management structures for the content + // nor attributes. + + return B_OK; +} + + +// #pragma mark - file operations + + +struct FileCookie { + int openMode; + + FileCookie(int openMode) + : + openMode(openMode) + { + } +}; + + +static status_t +checksumfs_open(fs_volume* fsVolume, fs_vnode* vnode, int openMode, + void** _cookie) +{ + // TODO: Check permissions! + + FileCookie* cookie = new(std::nothrow) FileCookie(openMode); + if (cookie == NULL) + return B_NO_MEMORY; + + *_cookie = cookie; + return B_OK; +} + + +static status_t +checksumfs_close(fs_volume* fsVolume, fs_vnode* vnode, void* cookie) +{ + return B_OK; +} + + +static status_t +checksumfs_free_cookie(fs_volume* fsVolume, fs_vnode* vnode, void* _cookie) +{ + FileCookie* cookie = (FileCookie*)_cookie; + delete cookie; + return B_OK; +} + + +// #pragma mark - directory operations + + +struct DirCookie { + enum { + DOT, + DOT_DOT, + OTHERS + }; + + Directory* directory; + int iterationState; + + DirCookie(Directory* directory) + : + directory(directory), + iterationState(DOT) + { + } + + status_t ReadNextEntry(struct dirent* buffer, size_t size, + uint32& _countRead) + { + const char* name; + uint64 blockIndex; + + int nextIterationState = OTHERS; + switch (iterationState) { + case DOT: + name = "."; + blockIndex = directory->BlockIndex(); + nextIterationState = DOT_DOT; + break; + case DOT_DOT: + name = ".."; + blockIndex = directory->ParentDirectory(); + break; + default: + // TODO: Implement! + _countRead = 0; + return B_OK; + } + + size_t entrySize = sizeof(dirent) + strlen(name); + if (entrySize > size) + return B_BUFFER_OVERFLOW; + + buffer->d_dev = directory->GetVolume()->ID(); + buffer->d_ino = blockIndex; + buffer->d_reclen = entrySize; + strcpy(buffer->d_name, name); + + iterationState = nextIterationState; + + _countRead = 1; + return B_OK; + } + + void Rewind() + { + iterationState = DOT; + } +}; + + +static status_t +checksumfs_open_dir(fs_volume* fsVolume, fs_vnode* vnode, void** _cookie) +{ + Directory* directory = dynamic_cast((Node*)vnode->private_node); + if (directory == NULL) + return B_NOT_A_DIRECTORY; + + DirCookie* cookie = new(std::nothrow) DirCookie(directory); + if (cookie == NULL) + return B_NO_MEMORY; + + *_cookie = cookie; + return B_OK; +} + + +static status_t +checksumfs_close_dir(fs_volume* fsVolume, fs_vnode* vnode, void* cookie) +{ + return B_OK; +} + + +static status_t +checksumfs_free_dir_cookie(fs_volume* fsVolume, fs_vnode* vnode, void* _cookie) +{ + DirCookie* cookie = (DirCookie*)_cookie; + delete cookie; + return B_OK; +} + + +static status_t +checksumfs_read_dir(fs_volume* fsVolume, fs_vnode* vnode, void* _cookie, + struct dirent* buffer, size_t bufferSize, uint32* _num) +{ + if (*_num == 0) + return B_OK; + + DirCookie* cookie = (DirCookie*)_cookie; + return cookie->ReadNextEntry(buffer, bufferSize, *_num); +} + + +static status_t +checksumfs_rewind_dir(fs_volume* fsVolume, fs_vnode* vnode, void* _cookie) +{ + DirCookie* cookie = (DirCookie*)_cookie; + cookie->Rewind(); + return B_OK; +} + + +// #pragma mark - module + + static status_t checksumfs_std_ops(int32 operation, ...) { switch (operation) { case B_MODULE_INIT: - case B_MODULE_UNINIT: + init_debugging(); + PRINT("checksumfs_std_ops(): B_MODULE_INIT\n"); return B_OK; + + case B_MODULE_UNINIT: + PRINT("checksumfs_std_ops(): B_MODULE_UNINIT\n"); + exit_debugging(); + return B_OK; + default: return B_BAD_VALUE; } @@ -191,3 +505,118 @@ const module_info* modules[] = { (module_info*)&sFSModule, NULL }; + + +fs_volume_ops gCheckSumFSVolumeOps = { + checksumfs_unmount, + + checksumfs_read_fs_info, + NULL, // write_fs_info + NULL, // sync + + checksumfs_get_vnode, + + /* index directory & index operations */ + NULL, // open_index_dir + NULL, // close_index_dir + NULL, // free_index_dir_cookie + NULL, // read_index_dir + NULL, // rewind_index_dir + + NULL, // create_index + NULL, // remove_index + NULL, // read_index_stat + + /* query operations */ + NULL, // open_query + NULL, // close_query + NULL, // free_query_cookie + NULL, // read_query + NULL, // rewind_query + + /* support for FS layers */ + NULL, // all_layers_mounted + NULL, // create_sub_vnode + NULL, // delete_sub_vnode +}; + + +fs_vnode_ops gCheckSumFSVnodeOps = { + /* vnode operations */ + checksumfs_lookup, + NULL, // get_vnode_name + + checksumfs_put_vnode, + NULL, // checksumfs_remove_vnode, + + /* VM file access */ + NULL, // can_page + NULL, // read_pages + NULL, // write_pages + + /* asynchronous I/O */ + NULL, // checksumfs_io, + NULL, // checksumfs_cancel_io, + + /* cache file access */ + NULL, // checksumfs_get_file_map, + + /* common operations */ + NULL, // checksumfs_ioctl, + NULL, // checksumfs_set_flags, + NULL, // checksumfs_select, + NULL, // checksumfs_deselect, + NULL, // checksumfs_fsync, + + NULL, // checksumfs_read_symlink, + NULL, // checksumfs_create_symlink, + + NULL, // checksumfs_link, + NULL, // checksumfs_unlink, + NULL, // checksumfs_rename, + + NULL, // checksumfs_access, + checksumfs_read_stat, + NULL, // checksumfs_write_stat, + + /* file operations */ + NULL, // checksumfs_create, + checksumfs_open, + checksumfs_close, + checksumfs_free_cookie, + NULL, // checksumfs_read, + NULL, // checksumfs_write, + + /* directory operations */ + NULL, // checksumfs_create_dir, + NULL, // checksumfs_remove_dir, + checksumfs_open_dir, + checksumfs_close_dir, + checksumfs_free_dir_cookie, + checksumfs_read_dir, + checksumfs_rewind_dir, + + /* attribute directory operations */ + NULL, // checksumfs_open_attr_dir, + NULL, // checksumfs_close_attr_dir, + NULL, // checksumfs_free_attr_dir_cookie, + NULL, // checksumfs_read_attr_dir, + NULL, // checksumfs_rewind_attr_dir, + + /* attribute operations */ + NULL, // checksumfs_create_attr, + NULL, // checksumfs_open_attr, + NULL, // checksumfs_close_attr, + NULL, // checksumfs_free_attr_cookie, + NULL, // checksumfs_read_attr, + NULL, // checksumfs_write_attr, + + NULL, // checksumfs_read_attr_stat, + NULL, // checksumfs_write_attr_stat, + NULL, // checksumfs_rename_attr, + NULL, // checksumfs_remove_attr, + + /* support for node and FS layers */ + NULL, // create_special_node + NULL // get_super_vnode +}; \ No newline at end of file diff --git a/src/tests/system/kernel/file_corruption/fs/checksumfs_private.h b/src/tests/system/kernel/file_corruption/fs/checksumfs_private.h new file mode 100644 index 0000000000..2837709056 --- /dev/null +++ b/src/tests/system/kernel/file_corruption/fs/checksumfs_private.h @@ -0,0 +1,16 @@ +/* + * Copyright 2010, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ +#ifndef CHECK_SUM_FS_PRIVATE_H +#define CHECK_SUM_FS_PRIVATE_H + + +#include + + +extern fs_volume_ops gCheckSumFSVolumeOps; +extern fs_vnode_ops gCheckSumFSVnodeOps; + + +#endif // CHECK_SUM_FS_PRIVATE_H diff --git a/src/tests/system/kernel/file_corruption/fs/userland/Jamfile b/src/tests/system/kernel/file_corruption/fs/userland/Jamfile new file mode 100644 index 0000000000..15dc07e601 --- /dev/null +++ b/src/tests/system/kernel/file_corruption/fs/userland/Jamfile @@ -0,0 +1,36 @@ +SubDir HAIKU_TOP src tests system kernel file_corruption fs userland ; + + +UsePrivateKernelHeaders ; +UsePrivateHeaders shared ; + +UseHeaders [ FDirName $(SUBDIR) $(DOTDOT) $(DOTDOT) ] ; + +UseHeaders [ FDirName $(HAIKU_TOP) src add-ons kernel file_systems ] ; + # We use packagefs's DebugSupport.h header, but we don't want to add that + # header to avoid clashes with other headers. + + +DEFINES += DEBUG_APP="\\\"checksumfs\\\"" ; + + +SEARCH_SOURCE += [ FDirName $(SUBDIR) $(DOTDOT) ] ; + + +Addon checksumfs : + $(HAIKU_CHECKSUM_FS_SOURCES) + + # from packagefs + DebugSupport.cpp + + # from src/kits/shared + SHA256.cpp + + : libuserlandfs_haiku_kernel.so $(TARGET_LIBSUPC++) +; + + +SEARCH on [ FGristFiles DebugSupport.cpp ] + = [ FDirName $(HAIKU_TOP) src add-ons kernel file_systems packagefs ] ; +SEARCH on [ FGristFiles SHA256.cpp ] + = [ FDirName $(HAIKU_TOP) src kits shared ] ;