387 lines
8.0 KiB
C
387 lines
8.0 KiB
C
|
|
#include <stdbool.h>
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
|
|
#include <sys/kassert.h>
|
|
#include <sys/kmem.h>
|
|
#include <sys/spinlock.h>
|
|
#include <sys/disk.h>
|
|
#include <sys/diskcache.h>
|
|
#include <sys/vfs.h>
|
|
#include <sys/dirent.h>
|
|
#include <sys/thread.h>
|
|
|
|
#include "o2fs.h"
|
|
|
|
VFS *O2FS_Mount(Disk *disk);
|
|
int O2FS_Unmount(VFS *fs);
|
|
int O2FS_GetRoot(VFS *fs, VNode **dn);
|
|
int O2FS_Lookup(VNode *dn, VNode **fn, const char *name);
|
|
int O2FS_Open(VNode *fn);
|
|
int O2FS_Close(VNode *fn);
|
|
int O2FS_Stat(VNode *fn, struct stat *statinfo);
|
|
int O2FS_Read(VNode *fn, void *buf, uint64_t off, uint64_t len);
|
|
int O2FS_Write(VNode *fn, void *buf, uint64_t off, uint64_t len);
|
|
int O2FS_ReadDir(VNode *fn, void *buf, uint64_t len, uint64_t *off);
|
|
|
|
static VFSOp O2FSOperations = {
|
|
.unmount = O2FS_Unmount,
|
|
.getroot = O2FS_GetRoot,
|
|
.lookup = O2FS_Lookup,
|
|
.open = O2FS_Open,
|
|
.close = O2FS_Close,
|
|
.stat = O2FS_Stat,
|
|
.read = O2FS_Read,
|
|
.write = O2FS_Write,
|
|
.readdir = O2FS_ReadDir,
|
|
};
|
|
|
|
VFS *
|
|
O2FS_Mount(Disk *disk)
|
|
{
|
|
int status;
|
|
VFS *fs = VFS_Alloc();
|
|
DiskCacheEntry *entry;
|
|
SuperBlock *sb;
|
|
|
|
ASSERT(sizeof(BDirEntry) == 512);
|
|
|
|
if (!fs)
|
|
return NULL;
|
|
|
|
status = DiskCache_Read(disk, 0, &entry);
|
|
if (status < 0) {
|
|
Alert(o2fs, "O2FS: Disk cache read failed\n");
|
|
return NULL;
|
|
}
|
|
|
|
// Read superblock
|
|
sb = entry->buffer;
|
|
if (memcmp(sb->magic, SUPERBLOCK_MAGIC, 8) != 0) {
|
|
Alert(o2fs, "O2FS: Invalid file system\n");
|
|
DiskCache_Release(entry);
|
|
return NULL;
|
|
}
|
|
if (sb->versionMajor != O2FS_VERSION_MAJOR ||
|
|
sb->versionMinor != O2FS_VERSION_MINOR) {
|
|
Alert(o2fs, "O2FS: Unsupported file system version\n");
|
|
DiskCache_Release(entry);
|
|
return NULL;
|
|
}
|
|
|
|
Log(o2fs, "O2FS: File system mounted\n");
|
|
Log(o2fs, "O2FS: Root @ 0x%llx\n", sb->root.offset);
|
|
|
|
fs->fsptr = entry;
|
|
fs->fsval = sb->root.offset;
|
|
|
|
// Setup VFS structure
|
|
fs->op = &O2FSOperations;
|
|
fs->disk = disk;
|
|
Spinlock_Init(&fs->lock, "O2FS Lock", SPINLOCK_TYPE_NORMAL);
|
|
fs->refCount = 1;
|
|
fs->root = NULL;
|
|
|
|
status = O2FS_GetRoot(fs, &fs->root);
|
|
if (status < 0) {
|
|
Alert(o2fs, "O2FS: Mount failed");
|
|
DiskCache_Release(entry);
|
|
return NULL;
|
|
}
|
|
|
|
return fs;
|
|
}
|
|
|
|
int
|
|
O2FS_Unmount(VFS *fs)
|
|
{
|
|
NOT_IMPLEMENTED();
|
|
return -1;
|
|
}
|
|
|
|
VNode *
|
|
O2FSLoadVNode(VFS *fs, ObjID *objid)
|
|
{
|
|
int status;
|
|
VNode *vn;
|
|
BNode *bn;
|
|
DiskCacheEntry *entry;
|
|
|
|
status = DiskCache_Read(fs->disk, objid->offset, &entry);
|
|
if (status < 0) {
|
|
Alert(o2fs, "O2FS: disk read error\n");
|
|
return NULL;
|
|
}
|
|
|
|
bn = entry->buffer;
|
|
if (memcmp(&bn->magic, BNODE_MAGIC, 8) != 0) {
|
|
Alert(o2fs, "O2FS: bad BNode magic\n");
|
|
DiskCache_Release(entry);
|
|
return NULL;
|
|
}
|
|
if (bn->versionMajor != O2FS_VERSION_MAJOR ||
|
|
bn->versionMinor != O2FS_VERSION_MINOR) {
|
|
Alert(o2fs, "O2FS: unsupported BNode version\n");
|
|
DiskCache_Release(entry);
|
|
return NULL;
|
|
}
|
|
|
|
vn = VNode_Alloc();
|
|
if (!vn) {
|
|
return NULL;
|
|
}
|
|
|
|
vn->op = &O2FSOperations;
|
|
vn->disk = fs->disk;
|
|
Spinlock_Init(&vn->lock, "VNode Lock", SPINLOCK_TYPE_NORMAL);
|
|
vn->refCount = 1;
|
|
vn->fsptr = entry;
|
|
vn->vfs = fs;
|
|
|
|
return vn;
|
|
}
|
|
|
|
void
|
|
O2FSRetainVNode(VNode *vn)
|
|
{
|
|
vn->refCount++;
|
|
}
|
|
|
|
void
|
|
O2FSReleaseVNode(VNode *vn)
|
|
{
|
|
vn->refCount--;
|
|
if (vn->refCount == 0) {
|
|
DiskCache_Release(vn->fsptr);
|
|
Spinlock_Destroy(&vn->lock);
|
|
VNode_Free(vn);
|
|
}
|
|
}
|
|
|
|
int
|
|
O2FS_GetRoot(VFS *fs, VNode **dn)
|
|
{
|
|
int status;
|
|
VNode *vn;
|
|
DiskCacheEntry *entry;
|
|
BNode *bn;
|
|
|
|
if (fs->root) {
|
|
fs->root->refCount++;
|
|
*dn = fs->root;
|
|
return 0;
|
|
}
|
|
|
|
status = DiskCache_Read(fs->disk, fs->fsval, &entry);
|
|
if (status < 0) {
|
|
Alert(o2fs, "O2FS: disk read error\n");
|
|
return status;
|
|
}
|
|
|
|
bn = entry->buffer;
|
|
if (memcmp(&bn->magic, BNODE_MAGIC, 8) != 0) {
|
|
Alert(o2fs, "O2FS: bad BNode magic\n");
|
|
DiskCache_Release(entry);
|
|
return -1;
|
|
}
|
|
if (bn->versionMajor != O2FS_VERSION_MAJOR ||
|
|
bn->versionMinor != O2FS_VERSION_MINOR) {
|
|
Alert(o2fs, "O2FS: unsupported BNode version\n");
|
|
DiskCache_Release(entry);
|
|
return -1;
|
|
}
|
|
|
|
vn = VNode_Alloc();
|
|
vn->op = &O2FSOperations;
|
|
vn->disk = fs->disk;
|
|
Spinlock_Init(&vn->lock, "VNode Lock", SPINLOCK_TYPE_NORMAL);
|
|
vn->refCount = 1;
|
|
vn->fsptr = entry;
|
|
vn->vfs = fs;
|
|
|
|
*dn = vn;
|
|
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
O2FSDumpDirEntry(BDirEntry *entry)
|
|
{
|
|
VLOG(o2fs, "%16s %08llx %08llx\n", entry->name, entry->objId.offset, entry->size);
|
|
}
|
|
|
|
int
|
|
O2FS_Lookup(VNode *dn, VNode **fn, const char *name)
|
|
{
|
|
int status;
|
|
VFS *vfs = dn->vfs;
|
|
DiskCacheEntry *sbEntry = (DiskCacheEntry *)vfs->fsptr;
|
|
SuperBlock *sb = sbEntry->buffer;
|
|
DiskCacheEntry *dirEntry = (DiskCacheEntry *)dn->fsptr;
|
|
BNode *dirBN = dirEntry->buffer;
|
|
uint64_t blocks = (dirBN->size + sb->blockSize - 1) / sb->blockSize;
|
|
uint64_t b;
|
|
|
|
VLOG(o2fs, "O2FS: LOOKUP %lld %d\n", dirBN->size, blocks);
|
|
|
|
for (b = 0; b < blocks; b++) {
|
|
// Read block
|
|
int e;
|
|
int entryPerBlock = sb->blockSize / sizeof(BDirEntry);
|
|
DiskCacheEntry *entry;
|
|
BDirEntry *dir;
|
|
status = DiskCache_Read(dn->disk, dirBN->direct[b].offset, &entry);
|
|
if (status < 0)
|
|
return status;
|
|
|
|
dir = (BDirEntry *)entry->buffer;
|
|
for (e = 0; e < entryPerBlock; e++) {
|
|
if (strcmp((char *)dir[e].magic, BDIR_MAGIC) == 0) {
|
|
O2FSDumpDirEntry(&dir[e]);
|
|
|
|
if (strcmp((char *)dir[e].name, name) == 0) {
|
|
*fn = O2FSLoadVNode(vfs, &dir[e].objId);
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
DiskCache_Release(entry);
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
int
|
|
O2FS_Open(VNode *fn)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
O2FS_Close(VNode *fn)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
O2FS_Stat(VNode *fn, struct stat *statinfo)
|
|
{
|
|
VFS *vfs = fn->vfs;
|
|
DiskCacheEntry *sbEntry = (DiskCacheEntry *)vfs->fsptr;
|
|
SuperBlock *sb = sbEntry->buffer;
|
|
DiskCacheEntry *fileEntry = (DiskCacheEntry *)fn->fsptr;
|
|
BNode *fileBN = fileEntry->buffer;
|
|
|
|
statinfo->st_size = fileBN->size;
|
|
// XXX: support other fields
|
|
statinfo->st_blocks = (fileBN->size + sb->blockSize - 1) / sb->blockSize;
|
|
statinfo->st_blksize = sb->blockSize;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
O2FS_Read(VNode *fn, void *buf, uint64_t off, uint64_t len)
|
|
{
|
|
int status;
|
|
VFS *vfs = fn->vfs;
|
|
DiskCacheEntry *sbEntry = (DiskCacheEntry *)vfs->fsptr;
|
|
SuperBlock *sb = sbEntry->buffer;
|
|
DiskCacheEntry *fileEntry = (DiskCacheEntry *)fn->fsptr;
|
|
BNode *fileBN = fileEntry->buffer;
|
|
uint64_t blocks = (fileBN->size + sb->blockSize - 1) / sb->blockSize;
|
|
uint64_t readBytes = 0;
|
|
|
|
VLOG(o2fs, "O2FS: READ %lld %d\n", fileBN->size, blocks);
|
|
|
|
if (off > fileBN->size) {
|
|
return 0;
|
|
}
|
|
|
|
if (off + len > fileBN->size) {
|
|
len = fileBN->size - off;
|
|
}
|
|
|
|
while (1) {
|
|
uint64_t b = off / sb->blockSize;
|
|
uint64_t bOff = off % sb->blockSize;
|
|
uint64_t bLen;
|
|
DiskCacheEntry *entry;
|
|
|
|
if (bOff + len > sb->blockSize) {
|
|
bLen = sb->blockSize - bOff;
|
|
} else {
|
|
bLen = len;
|
|
}
|
|
|
|
status = DiskCache_Read(fn->disk, fileBN->direct[b].offset, &entry);
|
|
if (status < 0)
|
|
return status;
|
|
|
|
memcpy(buf, entry->buffer + bOff, bLen);
|
|
DiskCache_Release(entry);
|
|
|
|
readBytes += bLen;
|
|
buf += bLen;
|
|
off += bLen;
|
|
len -= bLen;
|
|
|
|
if (len == 0)
|
|
break;
|
|
}
|
|
|
|
return readBytes;
|
|
}
|
|
|
|
int
|
|
O2FS_Write(VNode *fn, void *buf, uint64_t off, uint64_t len)
|
|
{
|
|
NOT_IMPLEMENTED();
|
|
return -EINVAL;
|
|
}
|
|
|
|
int
|
|
O2FS_ReadDir(VNode *fn, void *buf, uint64_t len, uint64_t *off)
|
|
{
|
|
int count = 0;
|
|
int status;
|
|
DiskCacheEntry *fileEntry = (DiskCacheEntry *)fn->fsptr;
|
|
BNode *fileBN = fileEntry->buffer;
|
|
BDirEntry dirEntry;
|
|
struct dirent de;
|
|
|
|
while (len >= sizeof(de)) {
|
|
if (*off == fileBN->size)
|
|
return count;
|
|
if (*off > fileBN->size)
|
|
return -EINVAL;
|
|
|
|
// XXX: Check offset
|
|
|
|
status = O2FS_Read(fn, &dirEntry, *off, sizeof(dirEntry));
|
|
if (status != sizeof(dirEntry)) {
|
|
kprintf("Unexpected error reading directory.");
|
|
return status;
|
|
}
|
|
|
|
// XXX: Validation and fill in all fields
|
|
strcpy(de.d_name, (char *)dirEntry.name);
|
|
|
|
status = CopyOut(&de, (uintptr_t)buf, sizeof(de));
|
|
if (status != 0)
|
|
return status;
|
|
|
|
*off += sizeof(dirEntry);
|
|
buf += sizeof(de);
|
|
len -= sizeof(de);
|
|
|
|
count++;
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|