Implemented hooks write_fs_info(), sync(), set_flags(), fsync(), access().
git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@37595 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -237,6 +237,13 @@ File::Write(off_t pos, const void* buffer, size_t size, size_t& _bytesWritten)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
File::Sync()
|
||||||
|
{
|
||||||
|
return file_cache_sync(fFileCache);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
File::RevertNodeData(const checksumfs_node& nodeData)
|
File::RevertNodeData(const checksumfs_node& nodeData)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ public:
|
|||||||
size_t& _bytesRead);
|
size_t& _bytesRead);
|
||||||
virtual status_t Write(off_t pos, const void* buffer,
|
virtual status_t Write(off_t pos, const void* buffer,
|
||||||
size_t size, size_t& _bytesWritten);
|
size_t size, size_t& _bytesWritten);
|
||||||
|
virtual status_t Sync();
|
||||||
|
|
||||||
virtual void RevertNodeData(const checksumfs_node& nodeData);
|
virtual void RevertNodeData(const checksumfs_node& nodeData);
|
||||||
|
|
||||||
|
|||||||
@@ -105,6 +105,13 @@ Node::Write(off_t pos, const void* buffer, size_t size, size_t& _bytesWritten)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
Node::Sync()
|
||||||
|
{
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
Node::SetParentDirectory(uint32 blockIndex)
|
Node::SetParentDirectory(uint32 blockIndex)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ public:
|
|||||||
size_t& _bytesRead);
|
size_t& _bytesRead);
|
||||||
virtual status_t Write(off_t pos, const void* buffer,
|
virtual status_t Write(off_t pos, const void* buffer,
|
||||||
size_t size, size_t& _bytesWritten);
|
size_t size, size_t& _bytesWritten);
|
||||||
|
virtual status_t Sync();
|
||||||
|
|
||||||
inline const checksumfs_node& NodeData() const { return fNode; }
|
inline const checksumfs_node& NodeData() const { return fNode; }
|
||||||
inline Volume* GetVolume() const { return fVolume; }
|
inline Volume* GetVolume() const { return fVolume; }
|
||||||
|
|||||||
@@ -52,3 +52,10 @@ SuperBlock::SetFreeBlocks(uint64 count)
|
|||||||
{
|
{
|
||||||
freeBlocks = count;
|
freeBlocks = count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
SuperBlock::SetName(const char* name)
|
||||||
|
{
|
||||||
|
strlcpy(this->name, name, kCheckSumFSNameLength);
|
||||||
|
}
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ public:
|
|||||||
void Initialize(Volume* volume);
|
void Initialize(Volume* volume);
|
||||||
|
|
||||||
void SetFreeBlocks(uint64 count);
|
void SetFreeBlocks(uint64 count);
|
||||||
|
void SetName(const char* name);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,9 @@
|
|||||||
|
|
||||||
#include <fs_cache.h>
|
#include <fs_cache.h>
|
||||||
|
|
||||||
|
#include <AutoDeleter.h>
|
||||||
|
#include <util/AutoLock.h>
|
||||||
|
|
||||||
#include "Block.h"
|
#include "Block.h"
|
||||||
#include "BlockAllocator.h"
|
#include "BlockAllocator.h"
|
||||||
#include "checksumfs.h"
|
#include "checksumfs.h"
|
||||||
@@ -40,6 +43,7 @@ Volume::Volume(uint32 flags)
|
|||||||
fBlockAllocator(NULL),
|
fBlockAllocator(NULL),
|
||||||
fRootDirectory(NULL)
|
fRootDirectory(NULL)
|
||||||
{
|
{
|
||||||
|
mutex_init(&fLock, "checksumfs volume");
|
||||||
mutex_init(&fTransactionLock, "checksumfs transaction");
|
mutex_init(&fTransactionLock, "checksumfs transaction");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,6 +62,7 @@ Volume::~Volume()
|
|||||||
free(fName);
|
free(fName);
|
||||||
|
|
||||||
mutex_destroy(&fTransactionLock);
|
mutex_destroy(&fTransactionLock);
|
||||||
|
mutex_destroy(&fLock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -217,6 +222,8 @@ Volume::Initialize(const char* name)
|
|||||||
void
|
void
|
||||||
Volume::GetInfo(fs_info& info)
|
Volume::GetInfo(fs_info& info)
|
||||||
{
|
{
|
||||||
|
MutexLocker locker(fLock);
|
||||||
|
|
||||||
info.flags = fFlags;
|
info.flags = fFlags;
|
||||||
info.block_size = B_PAGE_SIZE;
|
info.block_size = B_PAGE_SIZE;
|
||||||
info.io_size = B_PAGE_SIZE * 16; // random value
|
info.io_size = B_PAGE_SIZE * 16; // random value
|
||||||
@@ -225,7 +232,6 @@ Volume::GetInfo(fs_info& info)
|
|||||||
info.total_nodes = 1; // phew, who cares?
|
info.total_nodes = 1; // phew, who cares?
|
||||||
info.free_nodes = info.free_blocks;
|
info.free_nodes = info.free_blocks;
|
||||||
strlcpy(info.volume_name, fName, sizeof(info.volume_name));
|
strlcpy(info.volume_name, fName, sizeof(info.volume_name));
|
||||||
// TODO: We need locking once we are able to change the name!
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -427,6 +433,53 @@ Volume::DeleteNode(Node* node)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
Volume::SetName(const char* name)
|
||||||
|
{
|
||||||
|
if (name == NULL || strlen(name) > kCheckSumFSNameLength)
|
||||||
|
return B_BAD_VALUE;
|
||||||
|
|
||||||
|
// clone the name
|
||||||
|
char* newName = strdup(name);
|
||||||
|
if (newName == NULL)
|
||||||
|
return B_NO_MEMORY;
|
||||||
|
MemoryDeleter newNameDeleter(newName);
|
||||||
|
|
||||||
|
// start a transaction
|
||||||
|
Transaction transaction(this);
|
||||||
|
status_t error = transaction.Start();
|
||||||
|
if (error != B_OK)
|
||||||
|
return error;
|
||||||
|
|
||||||
|
// we lock the volume now, to keep the locking order (transaction -> volume)
|
||||||
|
MutexLocker locker(fLock);
|
||||||
|
|
||||||
|
// update the super block
|
||||||
|
Block block;
|
||||||
|
if (!block.GetWritable(this, kCheckSumFSSuperBlockOffset / B_PAGE_SIZE,
|
||||||
|
transaction)) {
|
||||||
|
return B_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
SuperBlock* superBlock = (SuperBlock*)block.Data();
|
||||||
|
superBlock->SetName(newName);
|
||||||
|
|
||||||
|
block.Put();
|
||||||
|
|
||||||
|
// commit the transaction
|
||||||
|
error = transaction.Commit();
|
||||||
|
if (error != B_OK)
|
||||||
|
return error;
|
||||||
|
|
||||||
|
// Everything went fine. We can replace the name. Since we still have the
|
||||||
|
// volume lock, there's no race condition.
|
||||||
|
free(fName);
|
||||||
|
fName = (char*)newNameDeleter.Detach();
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
Volume::_Init(uint64 totalBlocks)
|
Volume::_Init(uint64 totalBlocks)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -54,6 +54,8 @@ public:
|
|||||||
SymLink*& _symLink);
|
SymLink*& _symLink);
|
||||||
status_t DeleteNode(Node* node);
|
status_t DeleteNode(Node* node);
|
||||||
|
|
||||||
|
status_t SetName(const char* name);
|
||||||
|
|
||||||
inline void TransactionStarted();
|
inline void TransactionStarted();
|
||||||
inline void TransactionFinished();
|
inline void TransactionFinished();
|
||||||
|
|
||||||
@@ -80,6 +82,7 @@ private:
|
|||||||
char* fName;
|
char* fName;
|
||||||
BlockAllocator* fBlockAllocator;
|
BlockAllocator* fBlockAllocator;
|
||||||
Directory* fRootDirectory;
|
Directory* fRootDirectory;
|
||||||
|
mutex fLock;
|
||||||
mutex fTransactionLock;
|
mutex fTransactionLock;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -36,6 +36,107 @@ static const char* const kCheckSumFSModuleName = "file_systems/checksumfs"
|
|||||||
static const char* const kCheckSumFSShortName = "checksumfs";
|
static const char* const kCheckSumFSShortName = "checksumfs";
|
||||||
|
|
||||||
|
|
||||||
|
// #pragma mark -
|
||||||
|
|
||||||
|
|
||||||
|
struct FileCookie {
|
||||||
|
int openMode;
|
||||||
|
|
||||||
|
FileCookie(int openMode)
|
||||||
|
:
|
||||||
|
openMode(openMode)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct DirCookie {
|
||||||
|
DirCookie(Directory* directory)
|
||||||
|
:
|
||||||
|
fDirectory(directory)
|
||||||
|
{
|
||||||
|
Rewind();
|
||||||
|
}
|
||||||
|
|
||||||
|
Directory* GetDirectory() const
|
||||||
|
{
|
||||||
|
return fDirectory;
|
||||||
|
}
|
||||||
|
|
||||||
|
status_t ReadNextEntry(struct dirent* buffer, size_t size,
|
||||||
|
uint32& _countRead)
|
||||||
|
{
|
||||||
|
const char* name;
|
||||||
|
size_t nameLength;
|
||||||
|
uint64 blockIndex;
|
||||||
|
|
||||||
|
int nextIterationState = OTHERS;
|
||||||
|
switch (fIterationState) {
|
||||||
|
case DOT:
|
||||||
|
name = ".";
|
||||||
|
nameLength = 1;
|
||||||
|
blockIndex = fDirectory->BlockIndex();
|
||||||
|
nextIterationState = DOT_DOT;
|
||||||
|
break;
|
||||||
|
case DOT_DOT:
|
||||||
|
name = "..";
|
||||||
|
nameLength = 2;
|
||||||
|
blockIndex = fDirectory->ParentDirectory();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
status_t error = fDirectory->LookupNextEntry(fEntryName,
|
||||||
|
fEntryName, nameLength, blockIndex);
|
||||||
|
if (error != B_OK) {
|
||||||
|
if (error != B_ENTRY_NOT_FOUND)
|
||||||
|
return error;
|
||||||
|
|
||||||
|
_countRead = 0;
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
name = fEntryName;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t entrySize = sizeof(dirent) + nameLength;
|
||||||
|
if (entrySize > size)
|
||||||
|
return B_BUFFER_OVERFLOW;
|
||||||
|
|
||||||
|
buffer->d_dev = fDirectory->GetVolume()->ID();
|
||||||
|
buffer->d_ino = blockIndex;
|
||||||
|
buffer->d_reclen = entrySize;
|
||||||
|
strcpy(buffer->d_name, name);
|
||||||
|
|
||||||
|
fIterationState = nextIterationState;
|
||||||
|
|
||||||
|
_countRead = 1;
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Rewind()
|
||||||
|
{
|
||||||
|
fIterationState = DOT;
|
||||||
|
fEntryName[0] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
enum {
|
||||||
|
DOT,
|
||||||
|
DOT_DOT,
|
||||||
|
OTHERS
|
||||||
|
};
|
||||||
|
|
||||||
|
Directory* fDirectory;
|
||||||
|
int fIterationState;
|
||||||
|
char fEntryName[kCheckSumFSNameLength + 1];
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// #pragma mark -
|
||||||
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
set_timespec(timespec& time, uint64 nanos)
|
set_timespec(timespec& time, uint64 nanos)
|
||||||
{
|
{
|
||||||
@@ -355,6 +456,31 @@ checksumfs_read_fs_info(fs_volume* fsVolume, struct fs_info* info)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static status_t
|
||||||
|
checksumfs_write_fs_info(fs_volume* fsVolume, const struct fs_info* info,
|
||||||
|
uint32 mask)
|
||||||
|
{
|
||||||
|
Volume* volume = (Volume*)fsVolume->private_volume;
|
||||||
|
|
||||||
|
if ((mask & FS_WRITE_FSINFO_NAME) != 0) {
|
||||||
|
status_t error = volume->SetName(info->volume_name);
|
||||||
|
if (error != B_OK)
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static status_t
|
||||||
|
checksumfs_sync(fs_volume* fsVolume)
|
||||||
|
{
|
||||||
|
Volume* volume = (Volume*)fsVolume->private_volume;
|
||||||
|
|
||||||
|
return block_cache_sync(volume->BlockCache());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static status_t
|
static status_t
|
||||||
checksumfs_get_vnode(fs_volume* fsVolume, ino_t id, fs_vnode* vnode,
|
checksumfs_get_vnode(fs_volume* fsVolume, ino_t id, fs_vnode* vnode,
|
||||||
int* _type, uint32* _flags, bool reenter)
|
int* _type, uint32* _flags, bool reenter)
|
||||||
@@ -507,6 +633,31 @@ checksumfs_get_file_map(fs_volume* fsVolume, fs_vnode* vnode, off_t offset,
|
|||||||
// #pragma mark - common operations
|
// #pragma mark - common operations
|
||||||
|
|
||||||
|
|
||||||
|
static status_t
|
||||||
|
checksumfs_set_flags(fs_volume* fsVolume, fs_vnode* vnode, void* _cookie,
|
||||||
|
int flags)
|
||||||
|
{
|
||||||
|
FileCookie* cookie = (FileCookie*)_cookie;
|
||||||
|
|
||||||
|
cookie->openMode = (cookie->openMode & ~O_APPEND) | (flags & O_APPEND);
|
||||||
|
|
||||||
|
// TODO: Also support O_NOCACHE!
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static status_t
|
||||||
|
checksumfs_fsync(fs_volume* fsVolume, fs_vnode* vnode)
|
||||||
|
{
|
||||||
|
Node* node = (Node*)vnode->private_node;
|
||||||
|
|
||||||
|
NodeReadLocker nodeLocker(node);
|
||||||
|
|
||||||
|
return node->Sync();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static status_t
|
static status_t
|
||||||
checksumfs_read_symlink(fs_volume* fsVolume, fs_vnode* vnode, char* buffer,
|
checksumfs_read_symlink(fs_volume* fsVolume, fs_vnode* vnode, char* buffer,
|
||||||
size_t* _bufferSize)
|
size_t* _bufferSize)
|
||||||
@@ -749,6 +900,17 @@ checksumfs_rename(fs_volume* fsVolume, fs_vnode* fromDir, const char* fromName,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static status_t
|
||||||
|
checksumfs_access(fs_volume* fsVolume, fs_vnode* vnode, int mode)
|
||||||
|
{
|
||||||
|
Node* node = (Node*)vnode->private_node;
|
||||||
|
|
||||||
|
NodeReadLocker nodeLocker(node);
|
||||||
|
|
||||||
|
return check_access(node, mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static status_t
|
static status_t
|
||||||
checksumfs_read_stat(fs_volume* fsVolume, fs_vnode* vnode, struct stat* st)
|
checksumfs_read_stat(fs_volume* fsVolume, fs_vnode* vnode, struct stat* st)
|
||||||
{
|
{
|
||||||
@@ -872,17 +1034,6 @@ checksumfs_write_stat(fs_volume* fsVolume, fs_vnode* vnode,
|
|||||||
// #pragma mark - file operations
|
// #pragma mark - file operations
|
||||||
|
|
||||||
|
|
||||||
struct FileCookie {
|
|
||||||
int openMode;
|
|
||||||
|
|
||||||
FileCookie(int openMode)
|
|
||||||
:
|
|
||||||
openMode(openMode)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/*! Opens the node according to the given open mode (if the permissions allow
|
/*! Opens the node according to the given open mode (if the permissions allow
|
||||||
that) and creates a file cookie.
|
that) and creates a file cookie.
|
||||||
The caller must either pass a \a transaction, which is already started and
|
The caller must either pass a \a transaction, which is already started and
|
||||||
@@ -1187,90 +1338,6 @@ checksumfs_write(fs_volume* fsVolume, fs_vnode* vnode, void* _cookie, off_t pos,
|
|||||||
// #pragma mark - directory operations
|
// #pragma mark - directory operations
|
||||||
|
|
||||||
|
|
||||||
struct DirCookie {
|
|
||||||
DirCookie(Directory* directory)
|
|
||||||
:
|
|
||||||
fDirectory(directory)
|
|
||||||
{
|
|
||||||
Rewind();
|
|
||||||
}
|
|
||||||
|
|
||||||
Directory* GetDirectory() const
|
|
||||||
{
|
|
||||||
return fDirectory;
|
|
||||||
}
|
|
||||||
|
|
||||||
status_t ReadNextEntry(struct dirent* buffer, size_t size,
|
|
||||||
uint32& _countRead)
|
|
||||||
{
|
|
||||||
const char* name;
|
|
||||||
size_t nameLength;
|
|
||||||
uint64 blockIndex;
|
|
||||||
|
|
||||||
int nextIterationState = OTHERS;
|
|
||||||
switch (fIterationState) {
|
|
||||||
case DOT:
|
|
||||||
name = ".";
|
|
||||||
nameLength = 1;
|
|
||||||
blockIndex = fDirectory->BlockIndex();
|
|
||||||
nextIterationState = DOT_DOT;
|
|
||||||
break;
|
|
||||||
case DOT_DOT:
|
|
||||||
name = "..";
|
|
||||||
nameLength = 2;
|
|
||||||
blockIndex = fDirectory->ParentDirectory();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
{
|
|
||||||
status_t error = fDirectory->LookupNextEntry(fEntryName,
|
|
||||||
fEntryName, nameLength, blockIndex);
|
|
||||||
if (error != B_OK) {
|
|
||||||
if (error != B_ENTRY_NOT_FOUND)
|
|
||||||
return error;
|
|
||||||
|
|
||||||
_countRead = 0;
|
|
||||||
return B_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
name = fEntryName;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t entrySize = sizeof(dirent) + nameLength;
|
|
||||||
if (entrySize > size)
|
|
||||||
return B_BUFFER_OVERFLOW;
|
|
||||||
|
|
||||||
buffer->d_dev = fDirectory->GetVolume()->ID();
|
|
||||||
buffer->d_ino = blockIndex;
|
|
||||||
buffer->d_reclen = entrySize;
|
|
||||||
strcpy(buffer->d_name, name);
|
|
||||||
|
|
||||||
fIterationState = nextIterationState;
|
|
||||||
|
|
||||||
_countRead = 1;
|
|
||||||
return B_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Rewind()
|
|
||||||
{
|
|
||||||
fIterationState = DOT;
|
|
||||||
fEntryName[0] = '\0';
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
enum {
|
|
||||||
DOT,
|
|
||||||
DOT_DOT,
|
|
||||||
OTHERS
|
|
||||||
};
|
|
||||||
|
|
||||||
Directory* fDirectory;
|
|
||||||
int fIterationState;
|
|
||||||
char fEntryName[kCheckSumFSNameLength + 1];
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
checksumfs_create_dir(fs_volume* fsVolume, fs_vnode* parent, const char* name,
|
checksumfs_create_dir(fs_volume* fsVolume, fs_vnode* parent, const char* name,
|
||||||
int perms)
|
int perms)
|
||||||
@@ -1469,8 +1536,8 @@ fs_volume_ops gCheckSumFSVolumeOps = {
|
|||||||
checksumfs_unmount,
|
checksumfs_unmount,
|
||||||
|
|
||||||
checksumfs_read_fs_info,
|
checksumfs_read_fs_info,
|
||||||
NULL, // write_fs_info
|
checksumfs_write_fs_info,
|
||||||
NULL, // sync
|
checksumfs_sync,
|
||||||
|
|
||||||
checksumfs_get_vnode,
|
checksumfs_get_vnode,
|
||||||
|
|
||||||
@@ -1521,10 +1588,10 @@ fs_vnode_ops gCheckSumFSVnodeOps = {
|
|||||||
|
|
||||||
/* common operations */
|
/* common operations */
|
||||||
NULL, // checksumfs_ioctl,
|
NULL, // checksumfs_ioctl,
|
||||||
NULL, // checksumfs_set_flags,
|
checksumfs_set_flags,
|
||||||
NULL, // checksumfs_select,
|
NULL, // select
|
||||||
NULL, // checksumfs_deselect,
|
NULL, // deselect
|
||||||
NULL, // checksumfs_fsync,
|
checksumfs_fsync,
|
||||||
|
|
||||||
checksumfs_read_symlink,
|
checksumfs_read_symlink,
|
||||||
checksumfs_create_symlink,
|
checksumfs_create_symlink,
|
||||||
@@ -1533,7 +1600,7 @@ fs_vnode_ops gCheckSumFSVnodeOps = {
|
|||||||
checksumfs_unlink,
|
checksumfs_unlink,
|
||||||
checksumfs_rename,
|
checksumfs_rename,
|
||||||
|
|
||||||
NULL, // checksumfs_access,
|
checksumfs_access,
|
||||||
checksumfs_read_stat,
|
checksumfs_read_stat,
|
||||||
checksumfs_write_stat,
|
checksumfs_write_stat,
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user