#include "filesystem.h"
#include "disk.h"
struct SuperBlock {
unsigned int magic;
unsigned int inode_count;
unsigned int data_start;
unsigned int version;
};
static struct SuperBlock sb;
static struct Inode inodes[MAX_FILES];
#define INODE_PER_SEC (512 / sizeof(struct Inode))
static int str_eq(const char *a, const char *b)
{
while (*a && *b) {
if (*a != *b) return 0;
a++; b++;
}
return *a == *b;
}
static void str_copy(char *dst, const char *src, int max)
{
int i = 0;
while (*src && i < max - 1) dst[i++] = *src++;
dst[i] = '\0';
}
static void save_inodes(void)
{
unsigned char buf[512];
int i, j, k;
for (i = 0; i < MAX_FILES; i += INODE_PER_SEC) {
for (k = 0; k < 512; k++) buf[k] = 0;
for (j = 0; j < INODE_PER_SEC && (i + j) < MAX_FILES; j++) {
unsigned char *src = (unsigned char *)&inodes[i + j];
unsigned char *dst = buf + j * sizeof(struct Inode);
for (k = 0; k < (int)sizeof(struct Inode); k++) dst[k] = src[k];
}
disk_write_sector(INODE_START + i / INODE_PER_SEC, buf);
}
}
static void load_inodes(void)
{
unsigned char buf[512];
int i, j, k;
for (i = 0; i < MAX_FILES; i += INODE_PER_SEC) {
disk_read_sector(INODE_START + i / INODE_PER_SEC, buf);
for (j = 0; j < INODE_PER_SEC && (i + j) < MAX_FILES; j++) {
unsigned char *src = buf + j * sizeof(struct Inode);
unsigned char *dst = (unsigned char *)&inodes[i + j];
for (k = 0; k < (int)sizeof(struct Inode); k++) dst[k] = src[k];
}
}
}
void fs_init(void)
{
unsigned char buf[512];
struct SuperBlock *p = (struct SuperBlock *)buf;
int i;
disk_read_sector(FS_START_SECTOR, buf);
if (p->magic == FS_MAGIC && p->version == FS_VERSION) {
sb = *p;
load_inodes();
return;
}
sb.magic = FS_MAGIC;
sb.version = FS_VERSION;
sb.inode_count = MAX_FILES;
sb.data_start = DATA_START;
for (i = 0; i < MAX_FILES; i++) { inodes[i].used = 0; inodes[i].owner = 0; inodes[i].mode = 0; }
str_copy(inodes[0].name, "/", MAX_NAME); inodes[0].type = FS_DIR; inodes[0].used = 1; inodes[0].parent = -1; inodes[0].owner = 0; inodes[0].mode = 0755;
str_copy(inodes[1].name, "bin", MAX_NAME); inodes[1].type = FS_DIR; inodes[1].used = 1; inodes[1].parent = 0; inodes[1].owner = 0; inodes[1].mode = 0755;
str_copy(inodes[2].name, "etc", MAX_NAME); inodes[2].type = FS_DIR; inodes[2].used = 1; inodes[2].parent = 0; inodes[2].owner = 0; inodes[2].mode = 0755;
str_copy(inodes[3].name, "home", MAX_NAME); inodes[3].type = FS_DIR; inodes[3].used = 1; inodes[3].parent = 0; inodes[3].owner = 0; inodes[3].mode = 0755;
str_copy(inodes[4].name, "root", MAX_NAME); inodes[4].type = FS_DIR; inodes[4].used = 1; inodes[4].parent = 0; inodes[4].owner = 0; inodes[4].mode = 0700;
str_copy(inodes[5].name, "tmp", MAX_NAME); inodes[5].type = FS_DIR; inodes[5].used = 1; inodes[5].parent = 0; inodes[5].owner = 0; inodes[5].mode = 01777;
str_copy(inodes[6].name, "usr", MAX_NAME); inodes[6].type = FS_DIR; inodes[6].used = 1; inodes[6].parent = 0; inodes[6].owner = 0; inodes[6].mode = 0755;
str_copy(inodes[7].name, "var", MAX_NAME); inodes[7].type = FS_DIR; inodes[7].used = 1; inodes[7].parent = 0; inodes[7].owner = 0; inodes[7].mode = 0755;
str_copy(inodes[8].name, "user", MAX_NAME); inodes[8].type = FS_DIR; inodes[8].used = 1; inodes[8].parent = 3; inodes[8].owner = 1000; inodes[8].mode = 0700;
for (i = 0; i < 512; i++) buf[i] = 0;
{
unsigned char *src = (unsigned char *)&sb;
for (i = 0; i < (int)sizeof(struct SuperBlock); i++) buf[i] = src[i];
}
disk_write_sector(FS_START_SECTOR, buf);
save_inodes();
}
int fs_find_in(int parent, const char *name)
{
int i;
for (i = 0; i < MAX_FILES; i++) {
if (inodes[i].used && inodes[i].parent == parent &&
str_eq(inodes[i].name, name)) return i;
}
return -1;
}
int fs_create(int parent, const char *name, int type)
{
unsigned int mode = (type == FS_DIR) ? 0755 : 0644;
return fs_create_ex(parent, name, type, 0, mode);
}
int fs_create_ex(int parent, const char *name, int type, int owner, unsigned int mode)
{
int i;
if (fs_find_in(parent, name) >= 0) return -1;
for (i = 1; i < MAX_FILES; i++) {
if (!inodes[i].used) {
str_copy(inodes[i].name, name, MAX_NAME);
inodes[i].type = type;
inodes[i].size = 0;
inodes[i].used = 1;
inodes[i].parent = parent;
inodes[i].start_sector = 0;
inodes[i].owner = owner;
inodes[i].mode = mode & 07777;
save_inodes();
return i;
}
}
return -1;
}
int fs_mkdir(int parent, const char *name) { return fs_create(parent, name, FS_DIR); }
int fs_delete(int parent, const char *name)
{
int idx = fs_find_in(parent, name);
if (idx <= 0) return -1;
inodes[idx].used = 0;
save_inodes();
return 0;
}
int fs_cd(int current, const char *path)
{
int cur = current;
const char *p = path;
if (*p == '/') { cur = 0; p++; }
while (*p) {
char name[MAX_NAME];
int i = 0;
while (*p && *p != '/') {
if (i < MAX_NAME - 1) name[i++] = *p;
p++;
}
name[i] = '\0';
if (i == 0) { while (*p == '/') p++; continue; }
if (str_eq(name, ".")) { }
else if (str_eq(name, "..")) { if (cur > 0) cur = inodes[cur].parent; }
else {
int idx = fs_find_in(cur, name);
if (idx < 0 || inodes[idx].type != FS_DIR) return -1;
cur = idx;
}
while (*p == '/') p++;
}
return cur;
}
void fs_get_path(int idx, char *buf)
{
int stack[32], top = 0, i;
if (idx == 0) { buf[0] = '/'; buf[1] = '\0'; return; }
while (idx > 0 && top < 32) { stack[top++] = idx; idx = inodes[idx].parent; }
i = 0;
for (top--; top >= 0; top--) {
buf[i++] = '/';
str_copy(buf + i, inodes[stack[top]].name, MAX_NAME);
while (buf[i]) i++;
}
buf[i] = '\0';
}
static int read_inode(int idx, void *buf, int size)
{
unsigned char sec[512];
unsigned char *p = (unsigned char *)buf;
unsigned int nsec, i;
int j;
if (idx < 0 || idx >= MAX_FILES || !inodes[idx].used) return -1;
if (inodes[idx].type != FS_FILE) return -1;
if (size > (int)inodes[idx].size) size = inodes[idx].size;
if (size <= 0) return 0;
nsec = (size + 511) / 512;
for (i = 0; i < nsec; i++) {
int chunk = (i == nsec - 1) ? (size - (int)i * 512) : 512;
if (disk_read_sector(inodes[idx].start_sector + i, sec) < 0) return -1;
for (j = 0; j < chunk; j++) p[i * 512 + j] = sec[j];
}
return size;
}
static int write_inode(int idx, const void *buf, int size)
{
unsigned char sec[512];
const unsigned char *p = (const unsigned char *)buf;
unsigned int start, nsec, i;
int j;
if (idx < 0 || idx >= MAX_FILES || !inodes[idx].used) return -1;
if (size > MAX_FILE_SIZE) size = MAX_FILE_SIZE;
start = DATA_START;
for (i = 1; i < MAX_FILES; i++) {
if (inodes[i].used && i != (unsigned int)idx && inodes[i].start_sector > 0) {
unsigned int end = inodes[i].start_sector + (inodes[i].size + 511) / 512;
if (end > start) start = end;
}
}
nsec = (size + 511) / 512;
inodes[idx].start_sector = start;
inodes[idx].size = size;
for (i = 0; i < nsec; i++) {
int chunk = (i == nsec - 1) ? (size - (int)i * 512) : 512;
for (j = 0; j < 512; j++) sec[j] = 0;
for (j = 0; j < chunk; j++) sec[j] = p[i * 512 + j];
if (disk_write_sector(start + i, sec) < 0) return -1;
}
save_inodes();
return size;
}
int fs_write(const char *name, const void *buf, int size)
{
int idx = -1, i;
for (i = 1; i < MAX_FILES; i++) {
if (inodes[i].used && str_eq(inodes[i].name, name)) { idx = i; break; }
}
if (idx < 0) return -1;
return write_inode(idx, buf, size);
}
int fs_read(const char *name, void *buf, int size)
{
int idx = -1, i;
for (i = 1; i < MAX_FILES; i++) {
if (inodes[i].used && str_eq(inodes[i].name, name)) { idx = i; break; }
}
if (idx < 0) return -1;
return read_inode(idx, buf, size);
}
int fs_read_at(int parent, const char *name, void *buf, int size)
{
int idx = fs_find_in(parent, name);
if (idx < 0) return -1;
return read_inode(idx, buf, size);
}
int fs_write_at(int parent, const char *name, const void *buf, int size)
{
int idx = fs_find_in(parent, name);
if (idx < 0) return -1;
return write_inode(idx, buf, size);
}
int fs_is_dir(int idx)
{
if (idx < 0 || idx >= MAX_FILES) return 0;
return inodes[idx].used && inodes[idx].type == FS_DIR;
}
int fs_size_of(int idx)
{
if (idx < 0 || idx >= MAX_FILES || !inodes[idx].used) return 0;
return inodes[idx].size;
}
int fs_list(int parent, char names[][MAX_NAME], int max)
{
int i, n = 0;
for (i = 0; i < MAX_FILES && n < max; i++) {
if (inodes[i].used && inodes[i].parent == parent) {
str_copy(names[n], inodes[i].name, MAX_NAME);
n++;
}
}
return n;
}
int fs_get_owner(int idx)
{
if (idx < 0 || idx >= MAX_FILES) return -1;
return inodes[idx].owner;
}
unsigned int fs_get_mode(int idx)
{
if (idx < 0 || idx >= MAX_FILES) return 0;
return inodes[idx].mode;
}
int fs_chmod(int parent, const char *name, unsigned int mode)
{
int idx = fs_find_in(parent, name);
if (idx < 0) return -1;
inodes[idx].mode = mode & 07777;
save_inodes();
return 0;
}
int fs_chown(int parent, const char *name, int uid)
{
int idx = fs_find_in(parent, name);
if (idx < 0) return -1;
inodes[idx].owner = uid;
save_inodes();
return 0;
}