* Copyright 2001-2020, Axel Dörfler, axeld@pinc-software.de.
* This file may be used under the terms of the MIT License.
*/
#include "Attribute.h"
#include "CheckVisitor.h"
#include "Debug.h"
#include "Volume.h"
#include "Inode.h"
#include "Index.h"
#include "BPlusTree.h"
#include "Query.h"
#include "ResizeVisitor.h"
#include "bfs_control.h"
#include "bfs_disk_system.h"
#ifndef FS_SHELL
# include <io_requests.h>
# include <util/fs_trim_support.h>
#endif
#define BFS_IO_SIZE 65536
#if defined(BFS_LITTLE_ENDIAN_ONLY)
#define BFS_ENDIAN_SUFFIX ""
#define BFS_ENDIAN_PRETTY_SUFFIX ""
#else
#define BFS_ENDIAN_SUFFIX "_big"
#define BFS_ENDIAN_PRETTY_SUFFIX " (Big Endian)"
#endif
struct identify_cookie {
disk_super_block super_block;
};
extern void fill_stat_buffer(Inode* inode, struct stat& stat);
static void
fill_stat_time(const bfs_inode& node, struct stat& stat)
{
bigtime_t now = real_time_clock_usecs();
stat.st_atim.tv_sec = now / 1000000LL;
stat.st_atim.tv_nsec = (now % 1000000LL) * 1000;
stat.st_mtim.tv_sec = bfs_inode::ToSecs(node.LastModifiedTime());
stat.st_mtim.tv_nsec = bfs_inode::ToNsecs(node.LastModifiedTime());
stat.st_crtim.tv_sec = bfs_inode::ToSecs(node.CreateTime());
stat.st_crtim.tv_nsec = bfs_inode::ToNsecs(node.CreateTime());
bigtime_t changeTime = node.StatusChangeTime();
if (changeTime < node.LastModifiedTime())
stat.st_ctim = stat.st_mtim;
else {
stat.st_ctim.tv_sec = bfs_inode::ToSecs(changeTime);
stat.st_ctim.tv_nsec = bfs_inode::ToNsecs(changeTime);
}
}
void
fill_stat_buffer(Inode* inode, struct stat& stat)
{
const bfs_inode& node = inode->Node();
stat.st_dev = inode->GetVolume()->ID();
stat.st_ino = inode->ID();
stat.st_nlink = 1;
stat.st_blksize = BFS_IO_SIZE;
stat.st_uid = node.UserID();
stat.st_gid = node.GroupID();
stat.st_mode = node.Mode();
stat.st_type = node.Type();
fill_stat_time(node, stat);
if (inode->IsSymLink() && (inode->Flags() & INODE_LONG_SYMLINK) == 0) {
stat.st_size = strlen(node.short_symlink);
} else
stat.st_size = inode->Size();
stat.st_blocks = inode->AllocatedSize() / 512;
}
static status_t
iterative_io_get_vecs_hook(void* cookie, io_request* request, off_t offset,
size_t size, struct file_io_vec* vecs, size_t* _count)
{
Inode* inode = (Inode*)cookie;
return file_map_translate(inode->Map(), offset, size, vecs, _count,
inode->GetVolume()->BlockSize());
}
static status_t
iterative_io_finished_hook(void* cookie, io_request* request, status_t status,
bool partialTransfer, size_t bytesTransferred)
{
Inode* inode = (Inode*)cookie;
rw_lock_read_unlock(&inode->Lock());
put_vnode(inode->GetVolume()->FSVolume(), inode->ID());
return B_OK;
}
static float
bfs_identify_partition(int fd, partition_data* partition, void** _cookie)
{
disk_super_block superBlock;
status_t status = Volume::Identify(fd, &superBlock);
if (status != B_OK)
return -1;
identify_cookie* cookie = new(std::nothrow) identify_cookie;
if (cookie == NULL)
return -1;
memcpy(&cookie->super_block, &superBlock, sizeof(disk_super_block));
*_cookie = cookie;
return 0.85f;
}
static status_t
bfs_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.BlockSize();
partition->block_size = cookie->super_block.BlockSize();
partition->content_name = strdup(cookie->super_block.name);
if (partition->content_name == NULL)
return B_NO_MEMORY;
return B_OK;
}
static void
bfs_free_identify_partition_cookie(partition_data* partition, void* _cookie)
{
identify_cookie* cookie = (identify_cookie*)_cookie;
delete cookie;
}
static status_t
bfs_mount(fs_volume* _volume, const char* device, uint32 flags,
const char* args, ino_t* _rootID)
{
FUNCTION();
Volume* volume = new(std::nothrow) Volume(_volume);
if (volume == NULL)
return B_NO_MEMORY;
status_t status = volume->Mount(device, flags);
if (status != B_OK) {
delete volume;
RETURN_ERROR(status);
}
_volume->private_volume = volume;
_volume->ops = &gBFSVolumeOps;
*_rootID = volume->ToVnode(volume->Root());
INFORM(("mounted \"%s\" (root node at %" B_PRIdINO ", device = %s)\n",
volume->Name(), *_rootID, device));
return B_OK;
}
static status_t
bfs_unmount(fs_volume* _volume)
{
FUNCTION();
Volume* volume = (Volume*)_volume->private_volume;
status_t status = volume->Unmount();
delete volume;
RETURN_ERROR(status);
}
static status_t
bfs_read_fs_stat(fs_volume* _volume, struct fs_info* info)
{
FUNCTION();
Volume* volume = (Volume*)_volume->private_volume;
MutexLocker locker(volume->Lock());
info->flags = B_FS_IS_PERSISTENT | B_FS_HAS_ATTR | B_FS_HAS_MIME
| (volume->IndicesNode() != NULL ? B_FS_HAS_QUERY : 0)
| (volume->IsReadOnly() ? B_FS_IS_READONLY : 0)
| B_FS_SUPPORTS_MONITOR_CHILDREN;
info->io_size = BFS_IO_SIZE;
info->block_size = volume->BlockSize();
info->total_blocks = volume->NumBlocks();
info->free_blocks = volume->FreeBlocks();
strlcpy(info->volume_name, volume->Name(), sizeof(info->volume_name));
strlcpy(info->fsh_name, "bfs", sizeof(info->fsh_name));
return B_OK;
}
static status_t
bfs_write_fs_stat(fs_volume* _volume, const struct fs_info* info, uint32 mask)
{
FUNCTION_START(("mask = %" B_PRId32 "\n", mask));
Volume* volume = (Volume*)_volume->private_volume;
if (volume->IsReadOnly())
return B_READ_ONLY_DEVICE;
MutexLocker locker(volume->Lock());
status_t status = B_BAD_VALUE;
if (mask & FS_WRITE_FSINFO_NAME) {
disk_super_block& superBlock = volume->SuperBlock();
strncpy(superBlock.name, info->volume_name,
sizeof(superBlock.name) - 1);
superBlock.name[sizeof(superBlock.name) - 1] = '\0';
status = volume->WriteSuperBlock();
}
return status;
}
static status_t
bfs_sync(fs_volume* _volume)
{
FUNCTION();
Volume* volume = (Volume*)_volume->private_volume;
return volume->Sync();
}
*/
static status_t
bfs_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 < volume->ToBlock(volume->Log()) + volume->Log().Length()
|| id > volume->NumBlocks()) {
INFORM(("inode at %" B_PRIdINO " requested!\n", id));
return B_ERROR;
}
CachedBlock cached(volume);
status_t status = cached.SetTo(id);
if (status != B_OK) {
FATAL(("could not read inode: %" B_PRIdINO ": %s\n", id,
strerror(status)));
return status;
}
bfs_inode* node = (bfs_inode*)cached.Block();
status = node->InitCheck(volume);
if (status != B_OK) {
if ((node->Flags() & INODE_DELETED) != 0) {
INFORM(("inode at %" B_PRIdINO " is already deleted!\n", id));
} else {
FATAL(("inode at %" B_PRIdINO " could not be read: %s!\n", id,
strerror(status)));
}
return status;
}
Inode* inode = new(std::nothrow) Inode(volume, id);
if (inode == NULL)
return B_NO_MEMORY;
status = inode->InitCheck(false);
if (status != B_OK)
delete inode;
if (status == B_OK) {
_node->private_node = inode;
_node->ops = &gBFSVnodeOps;
*_type = inode->Mode();
*_flags = 0;
}
return status;
}
static status_t
bfs_put_vnode(fs_volume* _volume, fs_vnode* _node, bool reenter)
{
Volume* volume = (Volume*)_volume->private_volume;
Inode* inode = (Inode*)_node->private_node;
if (!volume->IsReadOnly() && !volume->IsCheckingThread()
&& inode->NeedsTrimming()) {
Transaction transaction(volume, inode->BlockNumber());
if (inode->TrimPreallocation(transaction) == B_OK)
transaction.Done();
else if (transaction.HasParent()) {
transaction.Done();
}
}
delete inode;
return B_OK;
}
static status_t
bfs_remove_vnode(fs_volume* _volume, fs_vnode* _node, bool reenter)
{
FUNCTION();
Volume* volume = (Volume*)_volume->private_volume;
Inode* inode = (Inode*)_node->private_node;
Transaction transaction(volume, volume->ToBlock(inode->Parent()));
if ((inode->Flags() & INODE_DONT_FREE_SPACE) != 0) {
delete inode;
return B_OK;
}
ASSERT((inode->Flags() & INODE_DELETED) != 0);
status_t status = inode->Free(transaction);
if (status == B_OK) {
status = transaction.Done();
} else if (transaction.HasParent()) {
status = transaction.Done();
}
volume->RemovedInodes().Remove(inode);
delete inode;
return status;
}
static bool
bfs_can_page(fs_volume* _volume, fs_vnode* _v, void* _cookie)
{
return false;
}
static status_t
bfs_read_pages(fs_volume* _volume, fs_vnode* _node, void* _cookie,
off_t pos, const iovec* vecs, size_t count, size_t* _numBytes)
{
Volume* volume = (Volume*)_volume->private_volume;
Inode* inode = (Inode*)_node->private_node;
if (inode->FileCache() == NULL)
RETURN_ERROR(B_BAD_VALUE);
InodeReadLocker _(inode);
uint32 vecIndex = 0;
size_t vecOffset = 0;
size_t bytesLeft = *_numBytes;
status_t status;
while (true) {
file_io_vec fileVecs[8];
size_t fileVecCount = 8;
status = file_map_translate(inode->Map(), pos, bytesLeft, fileVecs,
&fileVecCount, 0);
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;
}
return status;
}
static status_t
bfs_write_pages(fs_volume* _volume, fs_vnode* _node, void* _cookie,
off_t pos, const iovec* vecs, size_t count, size_t* _numBytes)
{
Volume* volume = (Volume*)_volume->private_volume;
Inode* inode = (Inode*)_node->private_node;
if (volume->IsReadOnly())
return B_READ_ONLY_DEVICE;
if (inode->FileCache() == NULL)
RETURN_ERROR(B_BAD_VALUE);
InodeReadLocker _(inode);
uint32 vecIndex = 0;
size_t vecOffset = 0;
size_t bytesLeft = *_numBytes;
status_t status;
while (true) {
file_io_vec fileVecs[8];
size_t fileVecCount = 8;
status = file_map_translate(inode->Map(), pos, bytesLeft, fileVecs,
&fileVecCount, 0);
if (status != B_OK && status != B_BUFFER_OVERFLOW)
break;
bool bufferOverflow = status == B_BUFFER_OVERFLOW;
size_t bytes = bytesLeft;
status = write_file_io_vec_pages(volume->Device(), fileVecs,
fileVecCount, vecs, count, &vecIndex, &vecOffset, &bytes);
if (status != B_OK || !bufferOverflow)
break;
pos += bytes;
bytesLeft -= bytes;
}
return status;
}
static status_t
bfs_io(fs_volume* _volume, fs_vnode* _node, void* _cookie, io_request* request)
{
#if KDEBUG_RW_LOCK_DEBUG
return B_UNSUPPORTED;
#endif
Volume* volume = (Volume*)_volume->private_volume;
Inode* inode = (Inode*)_node->private_node;
#ifndef FS_SHELL
if (io_request_is_write(request) && volume->IsReadOnly()) {
notify_io_request(request, B_READ_ONLY_DEVICE);
return B_READ_ONLY_DEVICE;
}
#endif
if (inode->FileCache() == NULL) {
#ifndef FS_SHELL
notify_io_request(request, B_BAD_VALUE);
#endif
RETURN_ERROR(B_BAD_VALUE);
}
rw_lock_read_lock(&inode->Lock());
acquire_vnode(_volume, inode->ID());
return do_iterative_fd_io(volume->Device(), request,
iterative_io_get_vecs_hook, iterative_io_finished_hook, inode);
}
static status_t
bfs_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;
int32 blockShift = volume->BlockShift();
uint32 index = 0, max = *_count;
block_run run;
off_t fileOffset;
while (true) {
status_t status = inode->FindBlockRun(offset, run, fileOffset);
if (status != B_OK)
return status;
vecs[index].offset = volume->ToOffset(run) + offset - fileOffset;
vecs[index].length = ((uint32)run.Length() << blockShift)
- offset + fileOffset;
if ((uint64)size <= (uint64)vecs[index].length
|| (uint64)offset + (uint64)vecs[index].length
>= (uint64)inode->Size()) {
if ((uint64)offset + (uint64)vecs[index].length
> (uint64)inode->Size()) {
vecs[index].length = round_up(inode->Size() - offset,
volume->BlockSize());
}
*_count = index + 1;
return B_OK;
}
offset += vecs[index].length;
size -= vecs[index].length;
index++;
if (index >= max) {
*_count = index;
return B_BUFFER_OVERFLOW;
}
}
return B_ERROR;
}
static status_t
bfs_lookup(fs_volume* _volume, fs_vnode* _directory, const char* file,
ino_t* _vnodeID)
{
Volume* volume = (Volume*)_volume->private_volume;
Inode* directory = (Inode*)_directory->private_node;
InodeReadLocker locker(directory);
status_t status = directory->CheckPermissions(X_OK);
if (status != B_OK)
RETURN_ERROR(status);
BPlusTree* tree = directory->Tree();
if (tree == NULL)
RETURN_ERROR(B_BAD_VALUE);
status = tree->Find((uint8*)file, (uint16)strlen(file), _vnodeID);
if (status != B_OK) {
if (status == B_ENTRY_NOT_FOUND)
entry_cache_add_missing(volume->ID(), directory->ID(), file);
return status;
}
entry_cache_add(volume->ID(), directory->ID(), file, *_vnodeID);
locker.Unlock();
Inode* inode;
status = get_vnode(volume->FSVolume(), *_vnodeID, (void**)&inode);
if (status != B_OK) {
REPORT_ERROR(status);
return B_ENTRY_NOT_FOUND;
}
return B_OK;
}
static status_t
bfs_get_vnode_name(fs_volume* _volume, fs_vnode* _node, char* buffer,
size_t bufferSize)
{
Inode* inode = (Inode*)_node->private_node;
return inode->GetName(buffer, bufferSize);
}
static status_t
bfs_ioctl(fs_volume* _volume, fs_vnode* _node, void* _cookie, uint32 cmd,
void* buffer, size_t bufferLength)
{
FUNCTION_START(("node = %p, cmd = %" B_PRIu32 ", buf = %p"
", len = %" B_PRIuSIZE "\n", _node, cmd, buffer, bufferLength));
Volume* volume = (Volume*)_volume->private_volume;
switch (cmd) {
#ifndef FS_SHELL
case B_TRIM_DEVICE:
{
fs_trim_data* trimData;
MemoryDeleter deleter;
status_t status = get_trim_data_from_user(buffer, bufferLength,
deleter, trimData);
if (status != B_OK)
return status;
trimData->trimmed_size = 0;
for (uint32 i = 0; i < trimData->range_count; i++) {
uint64 trimmedSize = 0;
status_t status = volume->Allocator().Trim(
trimData->ranges[i].offset, trimData->ranges[i].size,
trimmedSize);
if (status != B_OK)
return status;
trimData->trimmed_size += trimmedSize;
}
return copy_trim_data_to_user(buffer, trimData);
}
#endif
case BFS_IOCTL_VERSION:
{
uint32 version = 0x10000;
return user_memcpy(buffer, &version, sizeof(uint32));
}
case BFS_IOCTL_START_CHECKING:
{
status_t status = volume->CreateCheckVisitor();
if (status != B_OK)
return status;
CheckVisitor* checker = volume->CheckVisitor();
if (user_memcpy(&checker->Control(), buffer,
sizeof(check_control)) != B_OK) {
return B_BAD_ADDRESS;
}
status = checker->StartBitmapPass();
if (status == B_OK) {
file_cookie* cookie = (file_cookie*)_cookie;
cookie->open_mode |= BFS_OPEN_MODE_CHECKING;
}
return status;
}
case BFS_IOCTL_STOP_CHECKING:
{
CheckVisitor* checker = volume->CheckVisitor();
if (checker == NULL)
return B_NO_INIT;
status_t status = checker->StopChecking();
if (status == B_OK) {
file_cookie* cookie = (file_cookie*)_cookie;
cookie->open_mode &= ~BFS_OPEN_MODE_CHECKING;
status = user_memcpy(buffer, &checker->Control(),
sizeof(check_control));
}
volume->DeleteCheckVisitor();
volume->SetCheckingThread(-1);
return status;
}
case BFS_IOCTL_CHECK_NEXT_NODE:
{
CheckVisitor* checker = volume->CheckVisitor();
if (checker == NULL)
return B_NO_INIT;
volume->SetCheckingThread(find_thread(NULL));
checker->Control().errors = 0;
status_t status = checker->Next();
if (status == B_ENTRY_NOT_FOUND) {
checker->Control().status = B_ENTRY_NOT_FOUND;
if (checker->Pass() == BFS_CHECK_PASS_BITMAP) {
if (checker->WriteBackCheckBitmap() == B_OK)
status = checker->StartIndexPass();
}
}
if (status == B_OK) {
status = user_memcpy(buffer, &checker->Control(),
sizeof(check_control));
}
return status;
}
case BFS_IOCTL_UPDATE_BOOT_BLOCK:
{
update_boot_block update;
if (bufferLength != sizeof(update_boot_block))
return B_BAD_VALUE;
if (user_memcpy(&update, buffer, sizeof(update_boot_block)) != B_OK)
return B_BAD_ADDRESS;
uint32 minOffset = offsetof(disk_super_block, pad_to_block);
if (update.offset < minOffset
|| update.offset >= 512 || update.length > 512 - minOffset
|| update.length + update.offset > 512) {
return B_BAD_VALUE;
}
if (user_memcpy((uint8*)&volume->SuperBlock() + update.offset,
update.data, update.length) != B_OK) {
return B_BAD_ADDRESS;
}
return volume->WriteSuperBlock();
}
case BFS_IOCTL_RESIZE:
{
if (bufferLength != sizeof(uint64))
return B_BAD_VALUE;
uint64 size;
if (user_memcpy((uint8*)&size, buffer, sizeof(uint64)) != B_OK)
return B_BAD_ADDRESS;
ResizeVisitor resizer(volume);
return resizer.Resize(size, -1);
}
#ifdef DEBUG_FRAGMENTER
case 56741:
{
BlockAllocator& allocator = volume->Allocator();
allocator.Fragment();
return B_OK;
}
#endif
#ifdef DEBUG
case 56742:
{
BlockAllocator& allocator = volume->Allocator();
Transaction transaction(volume, 0);
CachedBlock cached(volume);
block_run run;
while (allocator.AllocateBlocks(transaction, 8, 0, 64, 1, run)
== B_OK) {
PRINT(("write block_run(%" B_PRId32 ", %" B_PRIu16
", %" B_PRIu16 ")\n", run.allocation_group, run.start,
run.length));
for (int32 i = 0;i < run.length;i++) {
status_t status = cached.SetToWritable(transaction, run);
if (status == B_OK)
memset(cached.WritableBlock(), 0, volume->BlockSize());
}
}
return B_OK;
}
#endif
}
return B_DEV_INVALID_IOCTL;
}
supports O_APPEND currently, but that should be sufficient
for a file system.
*/
static status_t
bfs_set_flags(fs_volume* _volume, fs_vnode* _node, void* _cookie, int flags)
{
FUNCTION_START(("node = %p, flags = %d", _node, flags));
file_cookie* cookie = (file_cookie*)_cookie;
cookie->open_mode = (cookie->open_mode & ~O_APPEND) | (flags & O_APPEND);
return B_OK;
}
static status_t
bfs_fsync(fs_volume* _volume, fs_vnode* _node, bool dataOnly)
{
FUNCTION();
Inode* inode = (Inode*)_node->private_node;
return inode->Sync();
}
static status_t
bfs_read_stat(fs_volume* _volume, fs_vnode* _node, struct stat* stat)
{
FUNCTION();
Inode* inode = (Inode*)_node->private_node;
fill_stat_buffer(inode, *stat);
return B_OK;
}
static status_t
bfs_write_stat(fs_volume* _volume, fs_vnode* _node, const struct stat* stat,
uint32 mask)
{
FUNCTION();
Volume* volume = (Volume*)_volume->private_volume;
Inode* inode = (Inode*)_node->private_node;
if (volume->IsReadOnly())
return B_READ_ONLY_DEVICE;
bfs_inode& node = inode->Node();
bool updateTime = false;
Transaction transaction(volume, inode->BlockNumber());
inode->WriteLockInTransaction(transaction);
if (check_write_stat_permissions(node.GroupID(), node.UserID(), node.Mode(),
mask, stat) != B_OK)
RETURN_ERROR(B_NOT_ALLOWED);
if ((mask & B_STAT_SIZE) != 0 && inode->Size() != stat->st_size) {
if (inode->IsDirectory())
return B_IS_A_DIRECTORY;
if (!inode->IsFile())
return B_BAD_VALUE;
off_t oldSize = inode->Size();
status_t status = inode->SetFileSize(transaction, stat->st_size);
if (status != B_OK)
return status;
if ((mask & B_STAT_SIZE_INSECURE) == 0) {
rw_lock_write_unlock(&inode->Lock());
inode->FillGapWithZeros(oldSize, inode->Size());
rw_lock_write_lock(&inode->Lock());
}
if (!inode->IsDeleted()) {
Index index(volume);
index.UpdateSize(transaction, inode);
updateTime = true;
}
}
if ((mask & B_STAT_UID) != 0) {
node.uid = HOST_ENDIAN_TO_BFS_INT32(stat->st_uid);
updateTime = true;
}
if ((mask & B_STAT_GID) != 0) {
node.gid = HOST_ENDIAN_TO_BFS_INT32(stat->st_gid);
updateTime = true;
}
if ((mask & B_STAT_MODE) != 0) {
PRINT(("original mode = %u, stat->st_mode = %u\n",
(unsigned int)node.Mode(), (unsigned int)stat->st_mode));
node.mode = HOST_ENDIAN_TO_BFS_INT32((node.Mode() & ~S_IUMSK)
| (stat->st_mode & S_IUMSK));
updateTime = true;
}
if ((mask & B_STAT_CREATION_TIME) != 0) {
node.create_time
= HOST_ENDIAN_TO_BFS_INT64(bfs_inode::ToInode(stat->st_crtim));
}
if ((mask & B_STAT_MODIFICATION_TIME) != 0) {
if (!inode->InLastModifiedIndex()) {
node.last_modified_time
= HOST_ENDIAN_TO_BFS_INT64(bfs_inode::ToInode(stat->st_mtim));
} else if (!inode->IsDeleted()) {
Index index(volume);
index.UpdateLastModified(transaction, inode,
bfs_inode::ToInode(stat->st_mtim));
}
}
if ((mask & B_STAT_CHANGE_TIME) != 0 || updateTime) {
bigtime_t newTime;
if ((mask & B_STAT_CHANGE_TIME) == 0)
newTime = bfs_inode::ToInode(real_time_clock_usecs());
else
newTime = bfs_inode::ToInode(stat->st_ctim);
node.status_change_time = HOST_ENDIAN_TO_BFS_INT64(newTime);
}
status_t status = inode->WriteBack(transaction);
if (status == B_OK)
status = transaction.Done();
if (status == B_OK)
notify_stat_changed(volume->ID(), inode->ParentID(), inode->ID(), mask);
return status;
}
status_t
bfs_create(fs_volume* _volume, fs_vnode* _directory, const char* name,
int openMode, int mode, void** _cookie, ino_t* _vnodeID)
{
FUNCTION_START(("name = \"%s\", perms = %d, openMode = %d\n", name, mode,
openMode));
Volume* volume = (Volume*)_volume->private_volume;
Inode* directory = (Inode*)_directory->private_node;
if (volume->IsReadOnly())
return B_READ_ONLY_DEVICE;
if (!directory->IsDirectory())
RETURN_ERROR(B_BAD_TYPE);
file_cookie* cookie = new(std::nothrow) file_cookie;
if (cookie == NULL)
RETURN_ERROR(B_NO_MEMORY);
cookie->open_mode = openMode;
cookie->last_size = 0;
cookie->last_notification = system_time();
Transaction transaction(volume, directory->BlockNumber());
Inode* inode;
bool created;
status_t status = Inode::Create(transaction, directory, name,
S_FILE | (mode & S_IUMSK), openMode, 0, &created, _vnodeID, &inode);
if (status == B_OK && (openMode & O_NOCACHE) != 0
&& inode->FileCache() != NULL) {
status = file_cache_disable(inode->FileCache());
}
entry_cache_add(volume->ID(), directory->ID(), name, *_vnodeID);
if (status == B_OK)
status = transaction.Done();
if (status == B_OK) {
*_cookie = cookie;
if (created) {
notify_entry_created(volume->ID(), directory->ID(), name,
*_vnodeID);
}
} else {
entry_cache_remove(volume->ID(), directory->ID(), name);
delete cookie;
}
return status;
}
static status_t
bfs_create_symlink(fs_volume* _volume, fs_vnode* _directory, const char* name,
const char* path, int mode)
{
FUNCTION_START(("name = \"%s\", path = \"%s\"\n", name, path));
Volume* volume = (Volume*)_volume->private_volume;
Inode* directory = (Inode*)_directory->private_node;
if (volume->IsReadOnly())
return B_READ_ONLY_DEVICE;
if (!directory->IsDirectory())
RETURN_ERROR(B_BAD_TYPE);
status_t status = directory->CheckPermissions(W_OK);
if (status < B_OK)
RETURN_ERROR(status);
Transaction transaction(volume, directory->BlockNumber());
Inode* link;
off_t id;
status = Inode::Create(transaction, directory, name, S_SYMLINK | 0777,
0, 0, NULL, &id, &link);
if (status < B_OK)
RETURN_ERROR(status);
size_t length = strlen(path);
if (length < SHORT_SYMLINK_NAME_LENGTH) {
strcpy(link->Node().short_symlink, path);
} else {
link->Node().flags |= HOST_ENDIAN_TO_BFS_INT32(INODE_LONG_SYMLINK
| INODE_LOGGED);
link->SetFileCache(file_cache_create(volume->ID(), link->ID(), 0));
link->SetMap(file_map_create(volume->ID(), link->ID(), 0));
status = link->WriteAt(transaction, 0, (const uint8*)path, &length);
}
if (status == B_OK)
status = link->WriteBack(transaction);
publish_vnode(volume->FSVolume(), id, link, &gBFSVnodeOps, link->Mode(), 0);
put_vnode(volume->FSVolume(), id);
if (status == B_OK) {
entry_cache_add(volume->ID(), directory->ID(), name, id);
status = transaction.Done();
if (status == B_OK)
notify_entry_created(volume->ID(), directory->ID(), name, id);
else
entry_cache_remove(volume->ID(), directory->ID(), name);
}
return status;
}
status_t
bfs_link(fs_volume* _volume, fs_vnode* dir, const char* name, fs_vnode* node)
{
FUNCTION_START(("name = \"%s\"\n", name));
return B_UNSUPPORTED;
}
status_t
bfs_unlink(fs_volume* _volume, fs_vnode* _directory, const char* name)
{
FUNCTION_START(("name = \"%s\"\n", name));
if (!strcmp(name, "..") || !strcmp(name, "."))
return B_NOT_ALLOWED;
Volume* volume = (Volume*)_volume->private_volume;
Inode* directory = (Inode*)_directory->private_node;
status_t status = directory->CheckPermissions(W_OK);
if (status < B_OK)
return status;
Transaction transaction(volume, directory->BlockNumber());
off_t id;
status = directory->Remove(transaction, name, &id);
if (status == B_OK) {
entry_cache_remove(volume->ID(), directory->ID(), name);
status = transaction.Done();
if (status == B_OK)
notify_entry_removed(volume->ID(), directory->ID(), name, id);
else
entry_cache_add(volume->ID(), directory->ID(), name, id);
}
return status;
}
status_t
bfs_rename(fs_volume* _volume, fs_vnode* _oldDir, const char* oldName,
fs_vnode* _newDir, const char* newName)
{
FUNCTION_START(("oldDir = %p, oldName = \"%s\", newDir = %p, newName = "
"\"%s\"\n", _oldDir, oldName, _newDir, newName));
Volume* volume = (Volume*)_volume->private_volume;
Inode* oldDirectory = (Inode*)_oldDir->private_node;
Inode* newDirectory = (Inode*)_newDir->private_node;
if (oldDirectory == newDirectory && !strcmp(oldName, newName))
return B_OK;
Transaction transaction(volume, oldDirectory->BlockNumber());
oldDirectory->WriteLockInTransaction(transaction);
if (oldDirectory != newDirectory)
newDirectory->WriteLockInTransaction(transaction);
status_t status = oldDirectory->CheckPermissions(W_OK);
if (status == B_OK)
status = newDirectory->CheckPermissions(W_OK);
if (status != B_OK)
return status;
BPlusTree* tree = oldDirectory->Tree();
if (tree == NULL)
RETURN_ERROR(B_BAD_VALUE);
off_t id;
status = tree->Find((const uint8*)oldName, strlen(oldName), &id);
if (status != B_OK)
RETURN_ERROR(status);
Vnode vnode(volume, id);
Inode* inode;
if (vnode.Get(&inode) != B_OK)
return B_IO_ERROR;
if (oldDirectory != newDirectory) {
ino_t parent = newDirectory->ID();
ino_t root = volume->RootNode()->ID();
while (true) {
if (parent == id)
return B_BAD_VALUE;
else if (parent == root || parent == oldDirectory->ID())
break;
Vnode vnode(volume, parent);
Inode* parentNode;
if (vnode.Get(&parentNode) != B_OK)
return B_ERROR;
parent = volume->ToVnode(parentNode->Parent());
}
}
BPlusTree* newTree = tree;
if (newDirectory != oldDirectory) {
newTree = newDirectory->Tree();
if (newTree == NULL)
RETURN_ERROR(B_BAD_VALUE);
}
status = newTree->Insert(transaction, (const uint8*)newName,
strlen(newName), id);
if (status == B_NAME_IN_USE) {
off_t clobber;
if (newTree->Find((const uint8*)newName, strlen(newName), &clobber)
< B_OK)
return B_NAME_IN_USE;
if (clobber == id)
return B_BAD_VALUE;
Vnode vnode(volume, clobber);
Inode* other;
if (vnode.Get(&other) < B_OK)
return B_NAME_IN_USE;
if (inode->IsDirectory() != other->IsDirectory())
return other->IsDirectory() ? B_IS_A_DIRECTORY : B_NOT_A_DIRECTORY;
status = newDirectory->Remove(transaction, newName, NULL,
other->IsDirectory());
if (status < B_OK)
return status;
entry_cache_remove(volume->ID(), newDirectory->ID(), newName);
notify_entry_removed(volume->ID(), newDirectory->ID(), newName,
clobber);
status = newTree->Insert(transaction, (const uint8*)newName,
strlen(newName), id);
}
if (status != B_OK)
return status;
inode->WriteLockInTransaction(transaction);
volume->UpdateLiveQueriesRenameMove(inode, oldDirectory->ID(), oldName,
newDirectory->ID(), newName);
if (strcmp(oldName, newName)) {
status = inode->SetName(transaction, newName);
if (status == B_OK) {
Index index(volume);
index.UpdateName(transaction, oldName, newName, inode,
false );
}
}
if (status == B_OK) {
status = tree->Remove(transaction, (const uint8*)oldName,
strlen(oldName), id);
if (status == B_OK) {
inode->Parent() = newDirectory->BlockRun();
BPlusTree* movedTree = inode->Tree();
if (oldDirectory != newDirectory
&& inode->IsDirectory()
&& movedTree != NULL) {
status = movedTree->Replace(transaction, (const uint8*)"..",
2, newDirectory->ID());
if (status == B_OK) {
entry_cache_add(volume->ID(), id, "..", newDirectory->ID());
}
}
if (status == B_OK && newDirectory != oldDirectory)
status = oldDirectory->ContainerContentsChanged(transaction);
if (status == B_OK)
status = newDirectory->ContainerContentsChanged(transaction);
if (status == B_OK)
status = inode->WriteBack(transaction);
if (status == B_OK) {
entry_cache_remove(volume->ID(), oldDirectory->ID(), oldName);
entry_cache_add(volume->ID(), newDirectory->ID(), newName, id);
status = transaction.Done();
if (status == B_OK) {
notify_entry_moved(volume->ID(), oldDirectory->ID(),
oldName, newDirectory->ID(), newName, id);
return B_OK;
}
entry_cache_remove(volume->ID(), newDirectory->ID(), newName);
entry_cache_add(volume->ID(), oldDirectory->ID(), oldName, id);
}
}
}
return status;
}
static status_t
bfs_open(fs_volume* _volume, fs_vnode* _node, int openMode, void** _cookie)
{
FUNCTION();
Volume* volume = (Volume*)_volume->private_volume;
Inode* inode = (Inode*)_node->private_node;
if (inode->IsDirectory() && (openMode & O_RWMASK) != O_RDONLY)
return B_IS_A_DIRECTORY;
if ((openMode & O_DIRECTORY) != 0 && !inode->IsDirectory())
return B_NOT_A_DIRECTORY;
status_t status = inode->CheckPermissions(open_mode_to_access(openMode));
if (status != B_OK)
RETURN_ERROR(status);
file_cookie* cookie = new(std::nothrow) file_cookie;
if (cookie == NULL)
RETURN_ERROR(B_NO_MEMORY);
ObjectDeleter<file_cookie> cookieDeleter(cookie);
cookie->open_mode = openMode & BFS_OPEN_MODE_USER_MASK;
cookie->last_size = inode->Size();
cookie->last_notification = system_time();
CObjectDeleter<void, void, file_cache_enable> fileCacheEnabler;
if ((openMode & O_NOCACHE) != 0 && inode->FileCache() != NULL) {
status = file_cache_disable(inode->FileCache());
if (status != B_OK)
return status;
fileCacheEnabler.SetTo(inode->FileCache());
}
if ((openMode & O_TRUNC) != 0) {
if ((openMode & O_RWMASK) == O_RDONLY)
return B_NOT_ALLOWED;
Transaction transaction(volume, inode->BlockNumber());
inode->WriteLockInTransaction(transaction);
status_t status = inode->SetFileSize(transaction, 0);
if (status == B_OK)
status = inode->WriteBack(transaction);
if (status == B_OK)
status = transaction.Done();
if (status != B_OK)
return status;
}
fileCacheEnabler.Detach();
cookieDeleter.Detach();
*_cookie = cookie;
return B_OK;
}
static status_t
bfs_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->HasUserAccessableStream()) {
*_length = 0;
return inode->IsDirectory() ? B_IS_A_DIRECTORY : B_BAD_VALUE;
}
return inode->ReadAt(pos, (uint8*)buffer, _length);
}
static status_t
bfs_write(fs_volume* _volume, fs_vnode* _node, void* _cookie, off_t pos,
const void* buffer, size_t* _length)
{
Volume* volume = (Volume*)_volume->private_volume;
Inode* inode = (Inode*)_node->private_node;
if (volume->IsReadOnly())
return B_READ_ONLY_DEVICE;
if (!inode->HasUserAccessableStream()) {
*_length = 0;
return inode->IsDirectory() ? B_IS_A_DIRECTORY : B_BAD_VALUE;
}
file_cookie* cookie = (file_cookie*)_cookie;
if (cookie->open_mode & O_APPEND)
pos = inode->Size();
Transaction transaction;
status_t status = inode->WriteAt(transaction, pos, (const uint8*)buffer,
_length);
if (status == B_OK)
status = transaction.Done();
if (status == B_OK) {
InodeReadLocker locker(inode);
if (!inode->IsDeleted() && cookie->last_size != inode->Size()
&& system_time() > cookie->last_notification
+ INODE_NOTIFICATION_INTERVAL) {
notify_stat_changed(volume->ID(), inode->ParentID(), inode->ID(),
B_STAT_MODIFICATION_TIME | B_STAT_SIZE | B_STAT_INTERIM_UPDATE);
cookie->last_size = inode->Size();
cookie->last_notification = system_time();
}
}
return status;
}
static status_t
bfs_close(fs_volume* _volume, fs_vnode* _node, void* _cookie)
{
FUNCTION();
return B_OK;
}
static status_t
bfs_free_cookie(fs_volume* _volume, fs_vnode* _node, void* _cookie)
{
FUNCTION();
file_cookie* cookie = (file_cookie*)_cookie;
Volume* volume = (Volume*)_volume->private_volume;
Inode* inode = (Inode*)_node->private_node;
Transaction transaction;
bool needsTrimming = false;
if (!volume->IsReadOnly() && !volume->IsCheckingThread()) {
InodeReadLocker locker(inode);
needsTrimming = inode->NeedsTrimming();
if ((cookie->open_mode & O_RWMASK) != 0
&& !inode->IsDeleted()
&& (needsTrimming
|| inode->OldLastModified() != inode->LastModified()
|| (inode->InSizeIndex()
&& inode->OldSize() != inode->Size()))) {
locker.Unlock();
transaction.Start(volume, inode->BlockNumber());
}
}
status_t status = transaction.IsStarted() ? B_OK : B_ERROR;
if (status == B_OK) {
inode->WriteLockInTransaction(transaction);
bool changedSize = false, changedTime = false;
Index index(volume);
if (needsTrimming) {
status = inode->TrimPreallocation(transaction);
if (status < B_OK) {
FATAL(("Could not trim preallocated blocks: inode %" B_PRIdINO
", transaction %d: %s!\n", inode->ID(),
(int)transaction.ID(), strerror(status)));
status = B_OK;
}
}
if (inode->OldSize() != inode->Size()) {
if (inode->InSizeIndex())
index.UpdateSize(transaction, inode);
changedSize = true;
}
if (inode->OldLastModified() != inode->LastModified()) {
if (inode->InLastModifiedIndex()) {
index.UpdateLastModified(transaction, inode,
inode->LastModified());
}
changedTime = true;
inode->WriteBack(transaction);
}
if (changedSize || changedTime) {
notify_stat_changed(volume->ID(), inode->ParentID(), inode->ID(),
(changedTime ? B_STAT_MODIFICATION_TIME : 0)
| (changedSize ? B_STAT_SIZE : 0));
}
}
if (status == B_OK)
transaction.Done();
if ((cookie->open_mode & BFS_OPEN_MODE_CHECKING) != 0) {
FATAL(("check process was aborted!\n"));
volume->CheckVisitor()->StopChecking();
volume->DeleteCheckVisitor();
}
if ((cookie->open_mode & O_NOCACHE) != 0 && inode->FileCache() != NULL)
file_cache_enable(inode->FileCache());
delete cookie;
return B_OK;
}
is not allowed.
*/
static status_t
bfs_access(fs_volume* _volume, fs_vnode* _node, int accessMode)
{
Inode* inode = (Inode*)_node->private_node;
status_t status = inode->CheckPermissions(accessMode);
if (status < B_OK)
RETURN_ERROR(status);
return B_OK;
}
static status_t
bfs_read_link(fs_volume* _volume, fs_vnode* _node, char* buffer,
size_t* _bufferSize)
{
FUNCTION();
Inode* inode = (Inode*)_node->private_node;
if (!inode->IsSymLink())
RETURN_ERROR(B_BAD_VALUE);
if ((inode->Flags() & INODE_LONG_SYMLINK) != 0) {
status_t status = inode->ReadAt(0, (uint8*)buffer, _bufferSize);
if (status < B_OK)
RETURN_ERROR(status);
*_bufferSize = inode->Size();
return B_OK;
}
size_t linkLength = strlen(inode->Node().short_symlink);
size_t bytesToCopy = min_c(linkLength, *_bufferSize);
*_bufferSize = linkLength;
memcpy(buffer, inode->Node().short_symlink, bytesToCopy);
return B_OK;
}
static status_t
bfs_create_dir(fs_volume* _volume, fs_vnode* _directory, const char* name,
int mode)
{
FUNCTION_START(("name = \"%s\", perms = %d\n", name, mode));
Volume* volume = (Volume*)_volume->private_volume;
Inode* directory = (Inode*)_directory->private_node;
if (volume->IsReadOnly())
return B_READ_ONLY_DEVICE;
if (!directory->IsDirectory())
RETURN_ERROR(B_BAD_TYPE);
status_t status = directory->CheckPermissions(W_OK);
if (status < B_OK)
RETURN_ERROR(status);
Transaction transaction(volume, directory->BlockNumber());
off_t id;
status = Inode::Create(transaction, directory, name,
S_DIRECTORY | (mode & S_IUMSK), 0, 0, NULL, &id);
if (status == B_OK) {
put_vnode(volume->FSVolume(), id);
entry_cache_add(volume->ID(), directory->ID(), name, id);
status = transaction.Done();
if (status == B_OK)
notify_entry_created(volume->ID(), directory->ID(), name, id);
else
entry_cache_remove(volume->ID(), directory->ID(), name);
}
return status;
}
static status_t
bfs_remove_dir(fs_volume* _volume, fs_vnode* _directory, const char* name)
{
FUNCTION_START(("name = \"%s\"\n", name));
Volume* volume = (Volume*)_volume->private_volume;
Inode* directory = (Inode*)_directory->private_node;
Transaction transaction(volume, directory->BlockNumber());
off_t id;
status_t status = directory->Remove(transaction, name, &id, true);
if (status == B_OK) {
entry_cache_remove(volume->ID(), directory->ID(), name);
entry_cache_remove(volume->ID(), id, "..");
status = transaction.Done();
if (status == B_OK)
notify_entry_removed(volume->ID(), directory->ID(), name, id);
else {
entry_cache_add(volume->ID(), directory->ID(), name, id);
entry_cache_add(volume->ID(), id, "..", id);
}
}
return status;
}
bfs_open_dir() is also used by bfs_open_index_dir().
*/
static status_t
bfs_open_dir(fs_volume* _volume, fs_vnode* _node, void** _cookie)
{
FUNCTION();
Inode* inode = (Inode*)_node->private_node;
status_t status = inode->CheckPermissions(R_OK);
if (status < B_OK)
RETURN_ERROR(status);
if (!inode->IsContainer())
RETURN_ERROR(B_NOT_A_DIRECTORY);
BPlusTree* tree = inode->Tree();
if (tree == NULL)
RETURN_ERROR(B_BAD_VALUE);
TreeIterator* iterator = new(std::nothrow) TreeIterator(tree);
if (iterator == NULL)
RETURN_ERROR(B_NO_MEMORY);
*_cookie = iterator;
return B_OK;
}
static status_t
bfs_read_dir(fs_volume* _volume, fs_vnode* _node, void* _cookie,
struct dirent* dirent, size_t bufferSize, uint32* _num)
{
FUNCTION();
TreeIterator* iterator = (TreeIterator*)_cookie;
Volume* volume = (Volume*)_volume->private_volume;
uint32 maxCount = *_num;
uint32 count = 0;
while (count < maxCount && bufferSize > sizeof(struct dirent)) {
ino_t id;
uint16 length;
size_t nameBufferSize = bufferSize - offsetof(struct dirent, d_name);
status_t status = iterator->GetNextEntry(dirent->d_name, &length,
nameBufferSize, &id);
if (status == B_ENTRY_NOT_FOUND)
break;
if (status == B_BUFFER_OVERFLOW) {
if (count == 0)
RETURN_ERROR(B_BUFFER_OVERFLOW);
break;
}
if (status != B_OK)
RETURN_ERROR(status);
dirent->d_dev = volume->ID();
dirent->d_ino = id;
dirent = next_dirent(dirent, length, bufferSize);
count++;
}
*_num = count;
return B_OK;
}
static status_t
bfs_rewind_dir(fs_volume* , fs_vnode* , void* _cookie)
{
FUNCTION();
TreeIterator* iterator = (TreeIterator*)_cookie;
return iterator->Rewind();
}
static status_t
bfs_close_dir(fs_volume* , fs_vnode* , void* )
{
FUNCTION();
return B_OK;
}
static status_t
bfs_free_dir_cookie(fs_volume* _volume, fs_vnode* node, void* _cookie)
{
delete (TreeIterator*)_cookie;
return B_OK;
}
static status_t
bfs_open_attr_dir(fs_volume* _volume, fs_vnode* _node, void** _cookie)
{
Inode* inode = (Inode*)_node->private_node;
FUNCTION();
AttributeIterator* iterator = new(std::nothrow) AttributeIterator(inode);
if (iterator == NULL)
RETURN_ERROR(B_NO_MEMORY);
*_cookie = iterator;
return B_OK;
}
static status_t
bfs_close_attr_dir(fs_volume* _volume, fs_vnode* node, void* cookie)
{
FUNCTION();
return B_OK;
}
static status_t
bfs_free_attr_dir_cookie(fs_volume* _volume, fs_vnode* node, void* _cookie)
{
FUNCTION();
AttributeIterator* iterator = (AttributeIterator*)_cookie;
delete iterator;
return B_OK;
}
static status_t
bfs_rewind_attr_dir(fs_volume* _volume, fs_vnode* _node, void* _cookie)
{
FUNCTION();
AttributeIterator* iterator = (AttributeIterator*)_cookie;
RETURN_ERROR(iterator->Rewind());
}
static status_t
bfs_read_attr_dir(fs_volume* _volume, fs_vnode* node, void* _cookie,
struct dirent* dirent, size_t bufferSize, uint32* _num)
{
FUNCTION();
AttributeIterator* iterator = (AttributeIterator*)_cookie;
uint32 type;
size_t length;
status_t status = iterator->GetNext(dirent->d_name, &length, &type,
&dirent->d_ino);
if (status == B_ENTRY_NOT_FOUND) {
*_num = 0;
return B_OK;
} else if (status != B_OK) {
RETURN_ERROR(status);
}
Volume* volume = (Volume*)_volume->private_volume;
dirent->d_dev = volume->ID();
dirent->d_reclen = offsetof(struct dirent, d_name) + length + 1;
*_num = 1;
return B_OK;
}
static status_t
bfs_create_attr(fs_volume* _volume, fs_vnode* _node, const char* name,
uint32 type, int openMode, void** _cookie)
{
FUNCTION();
Volume* volume = (Volume*)_volume->private_volume;
if (volume->IsReadOnly())
return B_READ_ONLY_DEVICE;
Inode* inode = (Inode*)_node->private_node;
Attribute attribute(inode);
return attribute.Create(name, type, openMode, (attr_cookie**)_cookie);
}
static status_t
bfs_open_attr(fs_volume* _volume, fs_vnode* _node, const char* name,
int openMode, void** _cookie)
{
FUNCTION();
Inode* inode = (Inode*)_node->private_node;
Attribute attribute(inode);
return attribute.Open(name, openMode, (attr_cookie**)_cookie);
}
static status_t
bfs_close_attr(fs_volume* _volume, fs_vnode* _file, void* cookie)
{
return B_OK;
}
static status_t
bfs_free_attr_cookie(fs_volume* _volume, fs_vnode* _file, void* cookie)
{
delete (attr_cookie*)cookie;
return B_OK;
}
static status_t
bfs_read_attr(fs_volume* _volume, fs_vnode* _file, void* _cookie, off_t pos,
void* buffer, size_t* _length)
{
FUNCTION();
attr_cookie* cookie = (attr_cookie*)_cookie;
Inode* inode = (Inode*)_file->private_node;
Attribute attribute(inode, cookie);
return attribute.Read(cookie, pos, (uint8*)buffer, _length);
}
static status_t
bfs_write_attr(fs_volume* _volume, fs_vnode* _file, void* _cookie,
off_t pos, const void* buffer, size_t* _length)
{
FUNCTION();
attr_cookie* cookie = (attr_cookie*)_cookie;
Volume* volume = (Volume*)_volume->private_volume;
Inode* inode = (Inode*)_file->private_node;
Transaction transaction(volume, inode->BlockNumber());
Attribute attribute(inode, cookie);
bool created;
status_t status = attribute.Write(transaction, cookie, pos,
(const uint8*)buffer, _length, &created);
if (status == B_OK) {
status = transaction.Done();
if (status == B_OK) {
notify_attribute_changed(volume->ID(), inode->ParentID(),
inode->ID(), cookie->name,
created ? B_ATTR_CREATED : B_ATTR_CHANGED);
notify_stat_changed(volume->ID(), inode->ParentID(), inode->ID(),
B_STAT_CHANGE_TIME);
}
}
return status;
}
static status_t
bfs_read_attr_stat(fs_volume* _volume, fs_vnode* _file, void* _cookie,
struct stat* stat)
{
FUNCTION();
attr_cookie* cookie = (attr_cookie*)_cookie;
Inode* inode = (Inode*)_file->private_node;
Attribute attribute(inode, cookie);
return attribute.Stat(*stat);
}
static status_t
bfs_write_attr_stat(fs_volume* _volume, fs_vnode* file, void* cookie,
const struct stat* stat, int statMask)
{
return EOPNOTSUPP;
}
static status_t
bfs_rename_attr(fs_volume* _volume, fs_vnode* fromFile, const char* fromName,
fs_vnode* toFile, const char* toName)
{
FUNCTION_START(("name = \"%s\", to = \"%s\"\n", fromName, toName));
return EOPNOTSUPP;
}
static status_t
bfs_remove_attr(fs_volume* _volume, fs_vnode* _node, const char* name)
{
FUNCTION_START(("name = \"%s\"\n", name));
Volume* volume = (Volume*)_volume->private_volume;
Inode* inode = (Inode*)_node->private_node;
status_t status = inode->CheckPermissions(W_OK);
if (status != B_OK)
return status;
Transaction transaction(volume, inode->BlockNumber());
status = inode->RemoveAttribute(transaction, name);
if (status == B_OK)
status = transaction.Done();
if (status == B_OK) {
notify_attribute_changed(volume->ID(), inode->ParentID(), inode->ID(),
name, B_ATTR_REMOVED);
}
return status;
}
status_t
bfs_create_special_node(fs_volume* _volume, fs_vnode* _directory,
const char* name, fs_vnode* subVnode, mode_t mode, uint32 flags,
fs_vnode* _superVnode, ino_t* _nodeID)
{
if (name == NULL)
return B_UNSUPPORTED;
FUNCTION_START(("name = \"%s\", mode = %u, flags = 0x%" B_PRIx32
", subVnode: %p\n", name, (unsigned int)mode, flags, subVnode));
Volume* volume = (Volume*)_volume->private_volume;
Inode* directory = (Inode*)_directory->private_node;
if (volume->IsReadOnly())
return B_READ_ONLY_DEVICE;
if (!directory->IsDirectory())
RETURN_ERROR(B_BAD_TYPE);
status_t status = directory->CheckPermissions(W_OK);
if (status < B_OK)
RETURN_ERROR(status);
Transaction transaction(volume, directory->BlockNumber());
off_t id;
Inode* inode;
status = Inode::Create(transaction, directory, name, mode, O_EXCL, 0, NULL,
&id, &inode, subVnode ? subVnode->ops : NULL, flags);
if (status == B_OK) {
_superVnode->private_node = inode;
_superVnode->ops = &gBFSVnodeOps;
*_nodeID = id;
entry_cache_add(volume->ID(), directory->ID(), name, id);
status = transaction.Done();
if (status == B_OK)
notify_entry_created(volume->ID(), directory->ID(), name, id);
else
entry_cache_remove(volume->ID(), directory->ID(), name);
}
return status;
}
static status_t
bfs_open_index_dir(fs_volume* _volume, void** _cookie)
{
FUNCTION();
Volume* volume = (Volume*)_volume->private_volume;
if (volume->IndicesNode() == NULL) {
RETURN_ERROR(B_ENTRY_NOT_FOUND);
}
fs_vnode indicesNode;
indicesNode.private_node = volume->IndicesNode();
RETURN_ERROR(bfs_open_dir(_volume, &indicesNode, _cookie));
}
static status_t
bfs_close_index_dir(fs_volume* _volume, void* _cookie)
{
FUNCTION();
Volume* volume = (Volume*)_volume->private_volume;
fs_vnode indicesNode;
indicesNode.private_node = volume->IndicesNode();
RETURN_ERROR(bfs_close_dir(_volume, &indicesNode, _cookie));
}
static status_t
bfs_free_index_dir_cookie(fs_volume* _volume, void* _cookie)
{
FUNCTION();
Volume* volume = (Volume*)_volume->private_volume;
fs_vnode indicesNode;
indicesNode.private_node = volume->IndicesNode();
RETURN_ERROR(bfs_free_dir_cookie(_volume, &indicesNode, _cookie));
}
static status_t
bfs_rewind_index_dir(fs_volume* _volume, void* _cookie)
{
FUNCTION();
Volume* volume = (Volume*)_volume->private_volume;
fs_vnode indicesNode;
indicesNode.private_node = volume->IndicesNode();
RETURN_ERROR(bfs_rewind_dir(_volume, &indicesNode, _cookie));
}
static status_t
bfs_read_index_dir(fs_volume* _volume, void* _cookie, struct dirent* dirent,
size_t bufferSize, uint32* _num)
{
FUNCTION();
Volume* volume = (Volume*)_volume->private_volume;
fs_vnode indicesNode;
indicesNode.private_node = volume->IndicesNode();
RETURN_ERROR(bfs_read_dir(_volume, &indicesNode, _cookie, dirent,
bufferSize, _num));
}
static status_t
bfs_create_index(fs_volume* _volume, const char* name, uint32 type,
uint32 flags)
{
FUNCTION_START(("name = \"%s\", type = %" B_PRIu32
", flags = %" B_PRIu32 "\n", name, type, flags));
Volume* volume = (Volume*)_volume->private_volume;
if (volume->IsReadOnly())
return B_READ_ONLY_DEVICE;
if (geteuid() != 0)
return B_NOT_ALLOWED;
Transaction transaction(volume, volume->Indices());
Index index(volume);
status_t status = index.Create(transaction, name, type);
if (status == B_OK)
status = transaction.Done();
RETURN_ERROR(status);
}
static status_t
bfs_remove_index(fs_volume* _volume, const char* name)
{
FUNCTION();
Volume* volume = (Volume*)_volume->private_volume;
if (volume->IsReadOnly())
return B_READ_ONLY_DEVICE;
if (geteuid() != 0)
return B_NOT_ALLOWED;
Inode* indices = volume->IndicesNode();
if (indices == NULL)
return B_ENTRY_NOT_FOUND;
Transaction transaction(volume, volume->Indices());
status_t status = indices->Remove(transaction, name);
if (status == B_OK)
status = transaction.Done();
RETURN_ERROR(status);
}
static status_t
bfs_stat_index(fs_volume* _volume, const char* name, struct stat* stat)
{
FUNCTION_START(("name = %s\n", name));
Volume* volume = (Volume*)_volume->private_volume;
Index index(volume);
status_t status = index.SetTo(name);
if (status < B_OK)
RETURN_ERROR(status);
bfs_inode& node = index.Node()->Node();
stat->st_type = index.Type();
stat->st_mode = node.Mode();
stat->st_size = node.data.Size();
stat->st_blocks = index.Node()->AllocatedSize() / 512;
stat->st_nlink = 1;
stat->st_blksize = 65536;
stat->st_uid = node.UserID();
stat->st_gid = node.GroupID();
fill_stat_time(node, *stat);
return B_OK;
}
static status_t
bfs_open_query(fs_volume* _volume, const char* queryString, uint32 flags,
port_id port, uint32 token, void** _cookie)
{
FUNCTION_START(("bfs_open_query(\"%s\", flags = %" B_PRIu32
", port_id = %" B_PRId32 ", token = %" B_PRIu32 ")\n",
queryString, flags, port, token));
Volume* volume = (Volume*)_volume->private_volume;
Query* query;
status_t error = Query::Create(volume, queryString, flags, port, token, query);
if (error != B_OK)
return error;
*_cookie = (void*)query;
return B_OK;
}
static status_t
bfs_close_query(fs_volume* _volume, void* cookie)
{
FUNCTION();
return B_OK;
}
static status_t
bfs_free_query_cookie(fs_volume* _volume, void* cookie)
{
FUNCTION();
Query* query = (Query*)cookie;
delete query;
return B_OK;
}
static status_t
bfs_read_query(fs_volume* , void* cookie, struct dirent* dirent,
size_t bufferSize, uint32* _num)
{
FUNCTION();
Query* query = (Query*)cookie;
status_t status = query->GetNextEntry(dirent, bufferSize);
if (status == B_OK)
*_num = 1;
else if (status == B_ENTRY_NOT_FOUND)
*_num = 0;
else
return status;
return B_OK;
}
static status_t
bfs_rewind_query(fs_volume* , void* cookie)
{
FUNCTION();
Query* query = (Query*)cookie;
return query->Rewind();
}
static uint32
bfs_get_supported_operations(partition_data* partition, uint32 mask)
{
return B_DISK_SYSTEM_SUPPORTS_INITIALIZING
| B_DISK_SYSTEM_SUPPORTS_CONTENT_NAME
| B_DISK_SYSTEM_SUPPORTS_WRITING;
}
static status_t
bfs_initialize(int fd, partition_id partitionID, const char* name,
const char* parameterString, off_t , disk_job_id job)
{
status_t status = check_volume_name(name);
if (status != B_OK)
return status;
initialize_parameters parameters;
status = parse_initialize_parameters(parameterString, parameters);
if (status != B_OK)
return status;
update_disk_device_job_progress(job, 0);
Volume volume(NULL);
status = volume.Initialize(fd, name, parameters.blockSize,
parameters.flags);
if (status < B_OK) {
INFORM(("Initializing volume failed: %s\n", strerror(status)));
return status;
}
status = scan_partition(partitionID);
if (status != B_OK)
return status;
update_disk_device_job_progress(job, 1);
if (parameters.verbose) {
disk_super_block super = volume.SuperBlock();
INFORM(("Disk was initialized successfully.\n"));
INFORM(("\tname: \"%s\"\n", super.name));
INFORM(("\tnum blocks: %" B_PRIdOFF "\n", super.NumBlocks()));
INFORM(("\tused blocks: %" B_PRIdOFF "\n", super.UsedBlocks()));
INFORM(("\tblock size: %u bytes\n", (unsigned)super.BlockSize()));
INFORM(("\tnum allocation groups: %d\n",
(int)super.AllocationGroups()));
INFORM(("\tallocation group size: %ld blocks\n",
1L << super.AllocationGroupShift()));
INFORM(("\tlog size: %u blocks\n", super.log_blocks.Length()));
}
return B_OK;
}
static status_t
bfs_uninitialize(int fd, partition_id partitionID, off_t partitionSize,
uint32 blockSize, disk_job_id job)
{
if (blockSize == 0)
return B_BAD_VALUE;
update_disk_device_job_progress(job, 0.0);
disk_super_block superBlock;
memset(&superBlock, 0, sizeof(superBlock));
if (write_pos(fd, 512, &superBlock, sizeof(superBlock)) < 0)
return errno;
update_disk_device_job_progress(job, 1.0);
return B_OK;
}
static status_t
bfs_std_ops(int32 op, ...)
{
switch (op) {
case B_MODULE_INIT:
#ifdef BFS_DEBUGGER_COMMANDS
add_debugger_commands();
#endif
return B_OK;
case B_MODULE_UNINIT:
#ifdef BFS_DEBUGGER_COMMANDS
remove_debugger_commands();
#endif
return B_OK;
default:
return B_ERROR;
}
}
fs_volume_ops gBFSVolumeOps = {
&bfs_unmount,
&bfs_read_fs_stat,
&bfs_write_fs_stat,
&bfs_sync,
&bfs_get_vnode,
&bfs_open_index_dir,
&bfs_close_index_dir,
&bfs_free_index_dir_cookie,
&bfs_read_index_dir,
&bfs_rewind_index_dir,
&bfs_create_index,
&bfs_remove_index,
&bfs_stat_index,
&bfs_open_query,
&bfs_close_query,
&bfs_free_query_cookie,
&bfs_read_query,
&bfs_rewind_query,
};
fs_vnode_ops gBFSVnodeOps = {
&bfs_lookup,
&bfs_get_vnode_name,
&bfs_put_vnode,
&bfs_remove_vnode,
&bfs_can_page,
&bfs_read_pages,
&bfs_write_pages,
&bfs_io,
NULL,
&bfs_get_file_map,
&bfs_ioctl,
&bfs_set_flags,
NULL,
NULL,
&bfs_fsync,
&bfs_read_link,
&bfs_create_symlink,
&bfs_link,
&bfs_unlink,
&bfs_rename,
&bfs_access,
&bfs_read_stat,
&bfs_write_stat,
NULL,
&bfs_create,
&bfs_open,
&bfs_close,
&bfs_free_cookie,
&bfs_read,
&bfs_write,
&bfs_create_dir,
&bfs_remove_dir,
&bfs_open_dir,
&bfs_close_dir,
&bfs_free_dir_cookie,
&bfs_read_dir,
&bfs_rewind_dir,
&bfs_open_attr_dir,
&bfs_close_attr_dir,
&bfs_free_attr_dir_cookie,
&bfs_read_attr_dir,
&bfs_rewind_attr_dir,
&bfs_create_attr,
&bfs_open_attr,
&bfs_close_attr,
&bfs_free_attr_cookie,
&bfs_read_attr,
&bfs_write_attr,
&bfs_read_attr_stat,
&bfs_write_attr_stat,
&bfs_rename_attr,
&bfs_remove_attr,
&bfs_create_special_node
};
static file_system_module_info sBeFileSystem = {
{
"file_systems/bfs" BFS_ENDIAN_SUFFIX B_CURRENT_FS_API_VERSION,
0,
bfs_std_ops,
},
"bfs" BFS_ENDIAN_SUFFIX,
"Be File System" BFS_ENDIAN_PRETTY_SUFFIX,
0
| B_DISK_SYSTEM_SUPPORTS_INITIALIZING
| B_DISK_SYSTEM_SUPPORTS_CONTENT_NAME
| B_DISK_SYSTEM_SUPPORTS_WRITING
,
bfs_identify_partition,
bfs_scan_partition,
bfs_free_identify_partition_cookie,
NULL,
&bfs_mount,
&bfs_get_supported_operations,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
bfs_initialize,
bfs_uninitialize
};
module_info* modules[] = {
(module_info*)&sBeFileSystem,
NULL,
};