Integrate VFS/ext2 code

This commit is contained in:
Mark
2019-10-25 11:20:30 +03:00
parent 6033a7c2d5
commit 1040973125
29 changed files with 3976 additions and 2 deletions
+12 -1
View File
@@ -47,4 +47,15 @@ OBJS+=$(O)/sys/debug.o \
$(O)/sys/panic.o \
$(O)/sys/errno.o \
$(O)/sys/blk.o \
$(O)/sys/dev.o
$(O)/sys/dev.o \
$(O)/sys/tty.o \
$(O)/sys/chr.o \
$(O)/sys/vfs/fs_class.o \
$(O)/sys/vfs/node.o \
$(O)/sys/vfs/vfs.o \
$(O)/sys/vfs/ext2/ext2alloc.o \
$(O)/sys/vfs/ext2/ext2blk.o \
$(O)/sys/vfs/ext2/ext2.o \
$(O)/sys/vfs/ext2/ext2dir.o \
$(O)/sys/vfs/ext2/ext2vnop.o
DIRS+=$(O)/sys/vfs/ext2
+14
View File
@@ -0,0 +1,14 @@
#pragma once
#include "sys/types.h"
struct chrdev {
char name[64];
void *dev_data;
// TODO: maybe something like flush()
ssize_t (*write) (struct chrdev *chr, const void *buf, size_t pos, size_t lim);
ssize_t (*read) (struct chrdev *chr, void *buf, size_t pos, size_t lim);
};
ssize_t chr_write(struct chrdev *chr, const void *buf, size_t pos, size_t lim);
ssize_t chr_read(struct chrdev *chr, void *buf, size_t pos, size_t lim);
+10
View File
@@ -0,0 +1,10 @@
#pragma once
#include <stdint.h>
struct dirent {
uint32_t d_ino;
uint32_t d_off;
uint16_t d_reclen;
uint8_t d_type;
char d_name[];
};
+147
View File
@@ -0,0 +1,147 @@
#pragma once
#include <stdint.h>
#include "fs.h"
#define EXT2_MAGIC ((uint16_t) 0xEF53)
#define EXT2_SBSIZ 1024
#define EXT2_SBOFF 1024
#define EXT2_ROOTINO 2
#define EXT2_GOOD ((uint16_t) 1)
#define EXT2_BAD ((uint16_t) 2)
#define EXT2_EACT_IGN ((uint16_t) 1)
#define EXT2_EACT_REM ((uint16_t) 2)
#define EXT2_EACT_PAN ((uint16_t) 3)
#define EXT2_TYPE_REG ((uint16_t) 0x8000)
#define EXT2_TYPE_DIR ((uint16_t) 0x4000)
#define EXT2_TYPE_LNK ((uint16_t) 0xA000)
struct ext2_sb {
uint32_t inode_count;
uint32_t block_count;
uint32_t su_reserved;
uint32_t free_block_count;
uint32_t free_inode_count;
uint32_t sb_block_number;
uint32_t block_size_log;
uint32_t frag_size_log;
uint32_t block_group_size_blocks;
uint32_t block_group_size_frags;
uint32_t block_group_size_inodes;
uint32_t last_mount_time;
uint32_t last_mtime;
uint16_t mount_count_since_fsck;
uint16_t mount_max_before_fsck;
uint16_t magic;
uint16_t fs_state;
uint16_t error_action;
uint16_t version_minor;
uint32_t last_fsck_time;
uint32_t os_id;
uint32_t version_major;
uint16_t su_uid;
uint16_t su_gid;
} __attribute__((packed));
struct ext2_extsb {
struct ext2_sb sb;
uint32_t first_non_reserved;
uint16_t inode_struct_size;
uint16_t backup_group_number;
uint32_t optional_features;
uint32_t required_features;
uint32_t ro_required_features;
char fsid[16];
char volname[16];
char last_mount_path[64];
uint32_t compression;
uint8_t prealloc_file_block_number;
uint8_t prealloc_dir_block_number;
uint16_t __un0;
char journal_id[16];
uint32_t journal_inode;
uint32_t journal_dev;
uint32_t orphan_inode_head;
// driver-specific info
uint32_t block_size;
uint32_t block_group_count;
uint32_t block_group_descriptor_table_block;
uint32_t block_group_descriptor_table_size_blocks;
struct ext2_grp_desc *block_group_descriptor_table;
} __attribute__((packed));
struct ext2_grp_desc {
uint32_t block_usage_bitmap_block;
uint32_t inode_usage_bitmap_block;
uint32_t inode_table_block;
uint16_t free_blocks;
uint16_t free_inodes;
uint16_t dir_count;
char __pad[14];
} __attribute__((packed));
struct ext2_inode {
uint16_t type_perm;
uint16_t uid;
uint32_t size_lower;
uint32_t atime;
uint32_t ctime;
uint32_t mtime;
uint32_t dtime;
uint16_t gid;
uint16_t hard_link_count;
uint32_t disk_sector_count;
uint32_t flags;
uint32_t os_value_1;
uint32_t direct_blocks[12];
uint32_t l1_indirect_block;
uint32_t l2_indirect_block;
uint32_t l3_indirect_block;
uint32_t gen_number;
uint32_t acl;
union {
uint32_t dir_acl;
uint32_t size_upper;
};
uint32_t frag_block_addr;
char os_value_2[12];
} __attribute__((packed));
struct ext2_dirent {
uint32_t ino;
uint16_t len;
uint8_t name_len;
uint8_t type_ind;
char name[];
} __attribute__((packed));
void ext2_class_init(void);
enum vnode_type ext2_inode_type(struct ext2_inode *i);
// Implemented in ext2blk.c
int ext2_write_superblock(fs_t *ext2);
int ext2_read_block(fs_t *ext2, uint32_t block_no, void *buf);
int ext2_write_block(fs_t *ext2, uint32_t block_no, const void *buf);
int ext2_read_inode_block(fs_t *ext2, struct ext2_inode *inode, uint32_t index, void *buf);
int ext2_write_inode_block(fs_t *ext2, struct ext2_inode *inode, uint32_t index, const void *buf);
int ext2_read_inode(fs_t *ext2, struct ext2_inode *inode, uint32_t ino);
int ext2_write_inode(fs_t *ext2, const struct ext2_inode *inode, uint32_t ino);
// Implemented in ext2alloc.c
int ext2_alloc_block(fs_t *ext2, uint32_t *block_no);
int ext2_free_block(fs_t *ext2, uint32_t block_no);
int ext2_inode_alloc_block(fs_t *ext2, struct ext2_inode *inode, uint32_t ino, uint32_t index);
int ext2_free_inode_block(fs_t *ext2, struct ext2_inode *inode, uint32_t ino, uint32_t index);
int ext2_free_inode(fs_t *ext2, uint32_t ino);
int ext2_alloc_inode(fs_t *ext2, uint32_t *ino);
// Implemented in ext2dir.c
int ext2_dir_add_inode(fs_t *ext2, vnode_t *dir, const char *name, uint32_t ino);
int ext2_dir_remove_inode(fs_t *ext2, vnode_t *dir, const char *name, uint32_t ino);
extern struct vnode_operations ext2_vnode_ops;
+31
View File
@@ -0,0 +1,31 @@
#pragma once
#define R_OK 4
#define W_OK 2
#define X_OK 1
#define F_OK 0
// O_EXEC is a special one for opening a node for
// execution
#define O_EXEC (1 << 2)
#define O_ACCMODE 00000003
#define O_RDONLY 00000000
#define O_WRONLY 00000001
#define O_RDWR 00000002
#define O_CREAT 00000100
// #define O_EXCL 00000200
// #define O_NOCTTY 00000400
#define O_TRUNC 00001000
#define O_APPEND 00002000
// #define O_NONBLOCK 00004000
// #define O_DSYNC 00010000
// #define FASYNC 00020000
// #define O_DIRECT 00040000
// #define O_LARGEFILE 00100000
#define O_DIRECTORY 00200000
// #define O_NOFOLLOW 00400000
// #define O_NOATIME 01000000
// #define O_CLOEXEC 02000000
+47
View File
@@ -0,0 +1,47 @@
#pragma once
#include "sys/fs/node.h"
#include "sys/blk.h"
// Means that the filesystem has a weird way of storing hierarchical
// nodes and employs a custom mapping algorithm. This ensures that
// at mount time the whole node tree for the filesystem is mapped
// to the VFS tree and the nodes do not get deallocated until the
// FS is unmounted.
// Note: when this flag is on, get_root is not used, as the filesystem
// produces the VFS tree all by itself.
#define FS_NODE_MAPPER (1 << 0)
struct statvfs;
struct vfs_node;
struct fs_class {
char name[256];
int opt;
vnode_t *(*get_root) (fs_t *fs);
int (*mount) (fs_t *fs, const char *opt);
int (*umount) (fs_t *fs);
int (*statvfs) (fs_t *fs, struct statvfs *st);
int (*mapper) (fs_t *fs, struct vfs_node **root);
};
// The actual filesystem instance,
// one per each mount
struct fs {
// Mount details here
vnode_t *mnt_at;
// Block device on which the filesystem resides
struct blkdev *blk;
// Private data structure per-mount
void *fs_private;
struct fs_class *cls;
};
struct fs *fs_create(struct fs_class *cls, struct blkdev *blk, vnode_t *mnt_at);
void fs_release(struct fs *fs);
struct fs_class *fs_class_by_name(const char *name);
int fs_class_register(struct fs_class *cls);
+34
View File
@@ -0,0 +1,34 @@
#pragma once
#include <sys/types.h>
#include <stdint.h>
typedef struct hash_entry {
uint64_t key;
void *value;
struct hash_entry *next;
} hash_entry_t;
typedef struct {
int (*keycmp) (uint64_t, uint64_t);
uint64_t (*keyhsh) (uint64_t);
uint64_t (*keydup) (uint64_t);
void (*keyfree) (uint64_t);
void (*valfree) (void *);
size_t bucket_count;
size_t item_count;
hash_entry_t **buckets;
} hash_t;
void hash_init(hash_t *h, int nb);
void hash_walk(hash_t *h, int (*wlkfn) (hash_entry_t *));
void hash_clear(hash_t *h);
void hash_put(hash_t *h, uint64_t key, void *value);
int hash_del(hash_t *h, uint64_t key);
int hash_get(hash_t *h, uint64_t key, void **value);
// Helper functions
int hash_u64_keycmp(uint64_t v0, uint64_t v1);
uint64_t hash_u64_keyhsh(uint64_t v0);
+7
View File
@@ -0,0 +1,7 @@
#pragma once
typedef struct list_simple {
void *car;
struct list_simple *cdr;
} list_simple_t;
+80
View File
@@ -0,0 +1,80 @@
/** vim: ft=c.doxygen
* @file node.h
* @brief vnode
*/
#pragma once
#include <stdint.h>
#include "sys/types.h"
#include "dirent.h"
#include "stat.h"
struct ofile;
struct vfs_ioctx;
typedef struct vnode vnode_t;
typedef struct fs fs_t;
enum vnode_type {
VN_REG,
VN_DIR,
VN_BLK,
VN_CHR,
VN_LNK
};
/**
* @brief Set of functions implemented by filesystem driver
*/
struct vnode_operations {
// File tree traversal, node instance operations
int (*find) (vnode_t *node, const char *path, vnode_t **res);
void (*destroy) (vnode_t *node);
// Symlink
int (*readlink) (vnode_t *node, char *path);
int (*symlink) (vnode_t *at, struct vfs_ioctx *ctx, const char *name, const char *dst);
// File entry operations
int (*access) (vnode_t *node, uid_t *uid, gid_t *gid, mode_t *mode);
int (*creat) (vnode_t *node, struct vfs_ioctx *ctx, const char *name, mode_t mode, int opt, vnode_t **res);
int (*mkdir) (vnode_t *at, const char *name, mode_t mode);
int (*unlink) (vnode_t *at, vnode_t *vn, const char *name);
int (*stat) (vnode_t *node, struct stat *st);
int (*chmod) (vnode_t *node, mode_t mode);
int (*chown) (vnode_t *node, uid_t uid, gid_t gid);
// Directory access
int (*opendir) (vnode_t *node, int opt);
int (*readdir) (struct ofile *fd);
// File access
int (*open) (vnode_t *node, int opt);
void (*close) (struct ofile *fd);
ssize_t (*read) (struct ofile *fd, void *buf, size_t count);
ssize_t (*write) (struct ofile *fd, const void *buf, size_t count);
int (*truncate) (struct ofile *fd, size_t length);
};
struct vnode {
enum vnode_type type;
uint32_t refcount;
fs_t *fs;
// Private filesystem-specific data (like inode struct)
void *fs_data;
// Private filesystem-specific number (like inode number)
uint32_t fs_number;
/*
* (struct blkdev *) if type == VN_BLK
* (struct chrdev *) if type == VN_CHR
*/
void *dev;
void *tree_node;
struct vnode_operations *op;
};
void vnode_ref(vnode_t *vn);
void vnode_unref(vnode_t *vn);
void vnode_free(vnode_t *vn);
+11
View File
@@ -0,0 +1,11 @@
#pragma once
#include "node.h"
#include <sys/types.h>
struct ofile {
int flags;
vnode_t *vnode;
size_t pos;
// Dirent buffer
char dirent_buf[512];
};
+59
View File
@@ -0,0 +1,59 @@
/** vi: ft=c.doxygen :
* @file stat.h
* @brief File status structure
*/
#pragma once
#include <stdint.h>
#include "sys/types.h"
#define S_IFMT 0170000
#define S_IFSOCK 0140000
#define S_IFLNK 0120000
#define S_IFREG 0100000
#define S_IFBLK 0060000
#define S_IFDIR 0040000
#define S_IFCHR 0020000
#define S_IFIFO 0010000
#define S_ISSOCK(m) ((m & S_IFMT) == S_IFSOCK)
#define S_ISLNK(m) ((m & S_IFMT) == S_IFLNK)
#define S_ISREG(m) ((m & S_IFMT) == S_IFREG)
#define S_ISBLK(m) ((m & S_IFMT) == S_IFBLK)
#define S_ISDIR(m) ((m & S_IFMT) == S_IFDIR)
#define S_ISCHR(m) ((m & S_IFMT) == S_IFCHR)
#define S_ISFIFO(m) ((m & S_IFMT) == S_IFIFO)
#define S_ISUID 04000
#define S_ISGID 02000
#define S_ISVTX 01000
#define S_IRUSR 00400
#define S_IWUSR 00200
#define S_IXUSR 00100
#define S_IRWXU (S_IRUSR | S_IWUSR | S_IXUSR)
#define S_IRGRP 00040
#define S_IWGRP 00020
#define S_IXGRP 00010
#define S_IRWXG (S_IRGRP | S_IWGRP | S_IXGRP)
#define S_IROTH 00004
#define S_IWOTH 00002
#define S_IXOTH 00001
#define S_IRWXO (S_IROTH | S_IWOTH | S_IXOTH)
struct stat {
uint32_t st_dev;
uint32_t st_ino;
mode_t st_mode;
uint32_t st_nlink;
uid_t st_uid;
gid_t st_gid;
uint32_t st_rdev;
uint32_t st_size;
uint32_t st_blksize;
uint32_t st_blocks;
uint32_t st_atime;
uint32_t st_mtime;
uint32_t st_ctime;
};
+22
View File
@@ -0,0 +1,22 @@
/** vi: ft=c.doxygen :
* @file statvfs.h
* @brief Filesystem status information
*/
#pragma once
#include <stdint.h>
typedef uint64_t fsblkcnt_t;
struct statvfs {
uint64_t f_bsize;
uint64_t f_frsize;
fsblkcnt_t f_blocks;
fsblkcnt_t f_bfree;
fsblkcnt_t f_bavail;
fsblkcnt_t f_files;
fsblkcnt_t f_ffree;
fsblkcnt_t f_favail;
uint64_t f_fsid;
uint64_t f_flag;
uint64_t f_namemax;
};
+4
View File
@@ -0,0 +1,4 @@
#pragma once
#include "sys/fs/fs.h"
void tarfs_init(void);
+8
View File
@@ -0,0 +1,8 @@
#pragma once
#include "list.h"
typedef struct tree {
void *value;
struct tree *parent;
list_simple_t *children;
} tree_t;
+150
View File
@@ -0,0 +1,150 @@
/** vi: ft=c.doxygen :
* @file vfs.h
* @brief Virtual filesystem (VFS) core functions
*/
#pragma once
#include "tree.h"
#include "node.h"
#include "ofile.h"
#include "stat.h"
#include "statvfs.h"
/**
* @brief Internal VFS tree node.
* As the actual vnodes are not associated
* with any location in the filesystem tree,
* we use this struct to keep things in order.
*/
struct vfs_node {
char name[256];
// Current vnode unless mnt, otherwise the root node of
// the mounted filesystem
vnode_t *vnode;
// Real vnode if mountpoint
vnode_t *real_vnode;
// Is 1 when node is a mountpoint
int ismount;
// Parent ref
struct vfs_node *parent;
// Linked list of children
struct vfs_node *child;
struct vfs_node *cdr;
};
/**
* @brief Process-specific I/O context details
* @note This will be moved to other headers once
* I integrate the VFS project into my kernel.
*/
struct vfs_ioctx {
// Process' current working directory
vnode_t *cwd_vnode;
uid_t uid;
gid_t gid;
};
/**
* @brief Set process' current working directory
* @param ctx I/O context
* @param cwd New CWD
* @return 0 on success, negative values on error
*/
int vfs_setcwd(struct vfs_ioctx *ctx, const char *cwd);
/**
* @brief Set process' working directory relative to the
* current one.
* @param ctx I/O context
* @param cwd_rel Relative/absolute path
* @return 0 on success, negative values on error
*/
int vfs_chdir(struct vfs_ioctx *ctx, const char *cwd_rel);
/**
* @brief Initialize the VFS
*/
void vfs_init(void);
/**
* @brief Dump the internal filesystem representation
*/
void vfs_dump_tree(void);
/**
* @brief Get a string representation of a path
* inside a filesystem tree.
* @param path The result path
* @param vn vnode
*/
void vfs_vnode_path(char *path, vnode_t *vn);
// Tree node ops
/**
* @brief Free internal VFS node struct
* @param n Tree node
*/
void vfs_node_free(struct vfs_node *n);
/**
* @brief Allocate a new node and associate it with
* a vnode
* @param name Name to be given to the tree node
* @param vn The vnode to bind it to
* @return Non-NULL (struct vfs_node *) on success, NULL otherwise
*/
struct vfs_node *vfs_node_create(const char *name, vnode_t *vn);
/**
* @brief Create a mountpoint in a filesystem tree so that
* a "source" directory actually refers to the root
* node of a "target" filesystem.
* @param ctx I/O context
* @param at Path to create mountpoint at
* @param blk Block device on which a filesystem shall be mounted
* @param fs_name Filesystem name
* @param fs_opt Filesystem mounting options
* @return 0 on success,
* -EACCES if the caller context's UID is not zero,
* -EBUSY if trying to create a mountpoint at a
* directory which is not empty,
* -ENOENT if the directory does not exist
*/
int vfs_mount(struct vfs_ioctx *ctx, const char *at, void *blk, const char *fs_name, const char *fs_opt);
/**
* @brief Destroy a mountpoint in a filesystem tree
* @param ctx I/O context
* @param target Path to unmount
* @return 0 on success,
* -EBUSY if there are any used filesystem objects inside
* the mountpoint,
* -EACCES if the caller context's UID is not zero,
* -ENOENT if the directory does not exist
*/
int vfs_umount(struct vfs_ioctx *ctx, const char *target);
/**
* @brief Read symlink's destination path into a buffer
* @param ctx I/O context
* @param path
*/
int vfs_readlink(struct vfs_ioctx *ctx, const char *path, char *buf);
int vfs_readlinkat(struct vfs_ioctx *ctx, vnode_t *at, const char *path, char *buf);
int vfs_symlink(struct vfs_ioctx *ctx, const char *target, const char *linkpath);
// File ops
int vfs_truncate(struct vfs_ioctx *ctx, struct ofile *fd, size_t length);
int vfs_creat(struct vfs_ioctx *ctx, struct ofile *fd, const char *path, int mode, int opt);
int vfs_open(struct vfs_ioctx *ctx, struct ofile *fd, const char *path, int mode, int opt);
int vfs_open_node(struct vfs_ioctx *ctx, struct ofile *fd, vnode_t *vn, int opt);
void vfs_close(struct vfs_ioctx *ctx, struct ofile *fd);
ssize_t vfs_read(struct vfs_ioctx *ctx, struct ofile *fd, void *buf, size_t count);
ssize_t vfs_write(struct vfs_ioctx *ctx, struct ofile *fd, const void *buf, size_t count);
int vfs_unlink(struct vfs_ioctx *ctx, const char *path);
int vfs_stat(struct vfs_ioctx *ctx, const char *path, struct stat *st);
int vfs_statat(struct vfs_ioctx *ctx, vnode_t *at, const char *path, struct stat *st);
int vfs_chmod(struct vfs_ioctx *ctx, const char *path, mode_t mode);
int vfs_chown(struct vfs_ioctx *ctx, const char *path, uid_t uid, gid_t gid);
int vfs_access(struct vfs_ioctx *ctx, const char *path, int mode);
// Directroy ops
int vfs_mkdir(struct vfs_ioctx *ctx, const char *path, mode_t mode);
struct dirent *vfs_readdir(struct vfs_ioctx *ctx, struct ofile *fd);
int vfs_statvfs(struct vfs_ioctx *ctx, const char *path, struct statvfs *st);
+1
View File
@@ -5,6 +5,7 @@
#pragma once
#include "sys/types.h"
#define MAX(x, y) ((x) > (y) ? (x) : (y))
#define MIN(x, y) ((x) < (y) ? (x) : (y))
void *memcpy(void *restrict dst, const void *restrict src, size_t cnt);
+7
View File
@@ -0,0 +1,7 @@
#pragma once
#include "sys/fs/node.h"
extern vnode_t *tty0;
void tty_buffer_write(int n, char c);
void tty_init(void);
+1 -1
View File
@@ -46,7 +46,7 @@ static ssize_t ahci_blk_read(struct blkdev *blk, void *buf, size_t off, size_t c
// TODO: error?
ahci_sata_read((struct ahci_port_registers *) blk->dev_data, buf, nsect, lba);
return 0;
return count;
}
static int ahci_add_port_dev(struct ahci_port_registers *port) {
+3
View File
@@ -13,6 +13,7 @@
#include "sys/amd64/hw/ps2.h"
#include "sys/panic.h"
#include "sys/assert.h"
#include "sys/fs/vfs.h"
static multiboot_info_t *multiboot_info;
@@ -37,6 +38,8 @@ void kernel_main(struct amd64_loader_data *data) {
amd64_apic_init();
pci_init();
vfs_init();
#if defined(AMD64_SMP)
amd64_smp_init();
#endif
+22
View File
@@ -0,0 +1,22 @@
#include "sys/chr.h"
#include "sys/errno.h"
ssize_t chr_write(struct chrdev *chr, const void *buf, size_t off, size_t count) {
if (!chr) {
return -ENODEV;
}
if (!chr->write) {
return -EROFS;
}
return chr->write(chr, buf, off, count);
}
ssize_t chr_read(struct chrdev *chr, void *buf, size_t off, size_t count) {
if (!chr) {
return -ENODEV;
}
if (!chr->read) {
return -EINVAL;
}
return chr->read(chr, buf, off, count);
}
+108
View File
@@ -0,0 +1,108 @@
#include "sys/tty.h"
#include "sys/chr.h"
#include "sys/errno.h"
#include "sys/debug.h"
#include "sys/panic.h"
#include "sys/string.h"
#include "sys/mm.h"
#define DEV_TTY(n) (n ## ULL)
#define DEV_DATA_TTY(n) ((void *) n ## ULL)
#define DEV_DATA_GETTTY(d) ((uint64_t) (d)->dev_data)
static ssize_t tty_write(struct chrdev *tty, const void *buf, size_t pos, size_t lim);
static ssize_t tty_read(struct chrdev *tty, void *buf, size_t pos, size_t lim);
static struct chrdev _dev_tty0 = {
.name = "tty0",
.dev_data = DEV_DATA_TTY(0),
.write = tty_write,
.read = tty_read
};
static vnode_t _tty0 = {
.type = VN_CHR,
.dev = &_dev_tty0
};
vnode_t *tty0 = &_tty0;
// TODO: this seems very ugly
static char *_tty0_reading_now = NULL;
static size_t _tty0_left = 0;
// This function receives keystrokes from keyboard drivers
void tty_buffer_write(int tty_no, char c) {
asm volatile ("cli");
if (tty_no != 0) {
panic("Not implemented\n");
}
if (!_tty0_left) {
// Ignore keystrokes sent while we're not reading
// This is bad and should be reimplemented by
// storing key data inside a ring buffer
return;
}
// No safety
*_tty0_reading_now++ = c;
--_tty0_left;
}
void tty_init(void) {
}
// TODO: multiple ttys
static ssize_t tty_write(struct chrdev *tty, const void *buf, size_t pos, size_t lim) {
uint64_t tty_no = DEV_DATA_GETTTY(tty);
if (tty_no != DEV_TTY(0)) {
return -EINVAL;
}
// TODO: buffer sanity checks
for (size_t i = 0; i < lim; ++i) {
debugc(DEBUG_INFO, ((const char *) buf)[i]);
}
return lim;
}
static ssize_t tty_read(struct chrdev *tty, void *buf, size_t pos, size_t lim) {
uint64_t tty_no = DEV_DATA_GETTTY(tty);
char ibuf[16];
if (tty_no != DEV_TTY(0)) {
return -EINVAL;
}
if (lim == 0) {
return 0;
}
#define MIN(x, y) ((x) < (y) ? (x) : (y))
size_t read = 0;
while (lim > 0) {
// XXX: This is very ugly
// Better just send the task to some kind of "busy"
// state and then wait
size_t read_now = MIN(16, lim);
_tty0_left = read_now;
_tty0_reading_now = ibuf;
while (1) {
asm volatile ("cli");
if (_tty0_left == 0) {
break;
}
asm volatile ("sti; hlt");
}
// Read 16 byte block
lim -= read_now;
// TODO: memcpy_kernel_to_user
memcpy((void *) ((uintptr_t) buf + read), ibuf, read_now);
read += read_now;
}
return 16;
}
+152
View File
@@ -0,0 +1,152 @@
#include "sys/fs/ext2.h"
#include "sys/fs/vfs.h"
#include "sys/debug.h"
#include "sys/panic.h"
#include "sys/errno.h"
#include "sys/heap.h"
enum vnode_type ext2_inode_type(struct ext2_inode *i) {
uint16_t v = i->type_perm & 0xF000;
switch (v) {
case EXT2_TYPE_DIR:
return VN_DIR;
case EXT2_TYPE_REG:
return VN_REG;
case EXT2_TYPE_LNK:
return VN_LNK;
default:
// fprintf(stderr, "Unknown file type: %x\n", v);
// abort();
panic("Unknown file type: %x\n", v);
return 0;
}
}
static int ext2_fs_mount(fs_t *fs, const char *opt) {
int res;
kdebug("ext2_fs_mount()\n");
struct ext2_extsb *sb;
// ext2's private data is its superblock structure
sb = (struct ext2_extsb *) kmalloc(EXT2_SBSIZ);
fs->fs_private = sb;
// Read the superblock from blkdev
if ((res = blk_read(fs->blk, sb, EXT2_SBOFF, EXT2_SBSIZ)) != EXT2_SBSIZ) {
kfree(fs->fs_private);
kerror("ext2: superblock read failed\n");
return -EINVAL;
}
// Check if superblock is ext2
if (sb->sb.magic != EXT2_MAGIC) {
kfree(sb);
kdebug("ext2: magic mismatch\n");
return -EINVAL;
}
// Check if we have an extended ext2 sb
if (sb->sb.version_major == 0) {
// Initialize params which are missing in non-extended sbs
sb->inode_struct_size = 128;
sb->first_non_reserved = 11;
}
sb->block_size = 1024 << sb->sb.block_size_log;
// Load block group descriptor table
// Get descriptor table size
uint32_t block_group_descriptor_table_length = sb->sb.block_count / sb->sb.block_group_size_blocks;
if (block_group_descriptor_table_length * sb->sb.block_group_size_blocks < sb->sb.block_count) {
++block_group_descriptor_table_length;
}
sb->block_group_count = block_group_descriptor_table_length;
uint32_t block_group_descriptor_table_size_blocks = 32 * block_group_descriptor_table_length /
sb->block_size + 1;
uint32_t block_group_descriptor_table_block = 2;
if (sb->block_size > 1024) {
block_group_descriptor_table_block = 1;
}
sb->block_group_descriptor_table_block = block_group_descriptor_table_block;
sb->block_group_descriptor_table_size_blocks = block_group_descriptor_table_size_blocks;
// Load all block group descriptors into memory
kdebug("Allocating %u bytes for BGDT\n", sb->block_group_descriptor_table_size_blocks * sb->block_size);
sb->block_group_descriptor_table = (struct ext2_grp_desc *) kmalloc(sb->block_group_descriptor_table_size_blocks * sb->block_size);
for (size_t i = 0; i < sb->block_group_descriptor_table_size_blocks; ++i) {
ext2_read_block(fs, i + sb->block_group_descriptor_table_block,
(void *) (((uintptr_t) sb->block_group_descriptor_table) + i * sb->block_size));
}
return 0;
}
static int ext2_fs_umount(fs_t *fs) {
struct ext2_extsb *sb = (struct ext2_extsb *) fs->fs_private;
// Free block group descriptor table
kfree(sb->block_group_descriptor_table);
// Free superblock
kfree(sb);
return 0;
}
static vnode_t *ext2_fs_get_root(fs_t *fs) {
struct ext2_extsb *sb = fs->fs_private;
kdebug("ext2_fs_get_root()\n");
struct ext2_inode *inode = (struct ext2_inode *) kmalloc(sb->inode_struct_size);
// Read root inode (2)
if (ext2_read_inode(fs, inode, EXT2_ROOTINO) != 0) {
kfree(inode);
return NULL;
}
vnode_t *res = (vnode_t *) kmalloc(sizeof(vnode_t));
res->fs = fs;
res->fs_data = inode;
res->fs_number = EXT2_ROOTINO;
res->op = &ext2_vnode_ops;
res->type = ext2_inode_type(inode);
return res;
}
static int ext2_fs_statvfs(fs_t *fs, struct statvfs *st) {
struct ext2_extsb *sb = fs->fs_private;
st->f_blocks = sb->sb.block_count;
st->f_bfree = sb->sb.free_block_count;
st->f_bavail = sb->sb.block_count - sb->sb.su_reserved;
st->f_files = sb->sb.inode_count;
st->f_ffree = sb->sb.free_inode_count;
st->f_favail = sb->sb.inode_count - sb->first_non_reserved + 1;
st->f_bsize = sb->block_size;
st->f_frsize = sb->block_size;
// XXX: put something here
st->f_fsid = 0;
st->f_flag = 0;
st->f_namemax = 256;
return 0;
}
static struct fs_class ext2_class = {
.name = "ext2",
.get_root = ext2_fs_get_root,
.mount = ext2_fs_mount,
.umount = ext2_fs_umount,
.statvfs = ext2_fs_statvfs
};
void ext2_class_init(void) {
fs_class_register(&ext2_class);
}
+319
View File
@@ -0,0 +1,319 @@
// ext2fs block/inode alloc/free
#include "sys/fs/ext2.h"
#include "sys/string.h"
#include "sys/assert.h"
#include "sys/debug.h"
#include "sys/errno.h"
// #include <assert.h>
// #include <stdlib.h>
// #include <errno.h>
// #include <stdio.h>
int ext2_alloc_block(fs_t *ext2, uint32_t *block_no) {
struct ext2_extsb *sb = (struct ext2_extsb *) ext2->fs_private;
char block_buffer[sb->block_size];
uint32_t res_block_no = 0;
uint32_t res_group_no = 0;
uint32_t res_block_no_in_group = 0;
int found = 0;
int res;
for (size_t i = 0; i < sb->block_group_count; ++i) {
if (sb->block_group_descriptor_table[i].free_blocks > 0) {
// Found a free block here
kdebug("Allocating a block in group #%zu\n", i);
if ((res = ext2_read_block(ext2,
sb->block_group_descriptor_table[i].block_usage_bitmap_block,
block_buffer)) < 0) {
return res;
}
for (size_t j = 0; j < sb->block_size / sizeof(uint64_t); ++j) {
uint64_t qw = ((uint64_t *) block_buffer)[j];
if (qw != (uint64_t) -1) {
for (size_t k = 0; k < 64; ++k) {
if (!(qw & (1 << k))) {
res_block_no_in_group = k + j * 64;
res_group_no = i;
// XXX: had to increment the resulting block_no
// because for some reason linux's ext2
// impl hasn't marked #531 as used in one
// case, but it was actually a L1-indirect
// block. So I just had to make it allocate
// #532 instead as a workaround (though
// block numbering should start with 0)
res_block_no = res_block_no_in_group + i * sb->sb.block_group_size_blocks + 1;
found = 1;
break;
}
}
if (found) {
break;
}
}
}
if (found) {
break;
}
}
}
if (!found) {
return -ENOSPC;
}
// Write block usage bitmap
((uint64_t *) block_buffer)[res_block_no_in_group / 64] |= (1 << (res_block_no_in_group % 64));
if ((res = ext2_write_block(ext2,
sb->block_group_descriptor_table[res_group_no].block_usage_bitmap_block,
block_buffer)) < 0) {
return res;
}
// Update BGDT
--sb->block_group_descriptor_table[res_group_no].free_blocks;
for (size_t i = 0; i < sb->block_group_descriptor_table_size_blocks; ++i) {
void *blk_ptr = (void *) (((uintptr_t) sb->block_group_descriptor_table) + i * sb->block_size);
if ((res = ext2_write_block(ext2, sb->block_group_descriptor_table_block + i, blk_ptr)) < 0) {
return res;
}
}
// Update global block count and flush superblock
--sb->sb.free_block_count;
if ((res = ext2_write_superblock(ext2)) < 0) {
return res;
}
*block_no = res_block_no;
kdebug("Allocated block #%u\n", res_block_no);
return 0;
}
int ext2_free_block(fs_t *ext2, uint32_t block_no) {
_assert(block_no);
struct ext2_extsb *sb = (struct ext2_extsb *) ext2->fs_private;
char block_buffer[sb->block_size];
int res;
uint32_t block_group_no = (block_no - 1) / sb->sb.block_group_size_blocks;
uint32_t block_no_in_group = (block_no - 1) % sb->sb.block_group_size_blocks;
// Read block ussge bitmap block
if ((res = ext2_read_block(ext2,
sb->block_group_descriptor_table[block_group_no].block_usage_bitmap_block,
block_buffer)) < 0) {
return res;
}
// Update the bitmap
_assert(((uint64_t *) block_buffer)[block_no_in_group / 64] & (1 << (block_no_in_group % 64)));
((uint64_t *) block_buffer)[block_no_in_group / 64] &= ~(1 << (block_no_in_group % 64));
if ((res = ext2_write_block(ext2,
sb->block_group_descriptor_table[block_group_no].block_usage_bitmap_block,
block_buffer)) < 0) {
return res;
}
// Update BGDT
++sb->block_group_descriptor_table[block_group_no].free_blocks;
for (size_t i = 0; i < sb->block_group_descriptor_table_size_blocks; ++i) {
void *blk_ptr = (void *) (((uintptr_t) sb->block_group_descriptor_table) + i * sb->block_size);
if ((res = ext2_write_block(ext2, sb->block_group_descriptor_table_block + i, blk_ptr)) < 0) {
return res;
}
}
// Update global block count
++sb->sb.free_block_count;
if ((res = ext2_write_superblock(ext2)) < 0) {
return res;
}
kdebug("Freed block #%u\n", block_no);
return 0;
}
int ext2_inode_alloc_block(fs_t *ext2, struct ext2_inode *inode, uint32_t ino, uint32_t index) {
if (index >= 12) {
panic("Not implemented\n");
}
int res;
uint32_t block_no;
// Allocate the block itself
if ((res = ext2_alloc_block(ext2, &block_no)) < 0) {
return res;
}
// Write direct block list entry
inode->direct_blocks[index] = block_no;
// Flush changes to the device
return ext2_write_inode(ext2, inode, ino);
}
int ext2_free_inode_block(fs_t *ext2, struct ext2_inode *inode, uint32_t ino, uint32_t index) {
if (index >= 12) {
panic("Not implemented\n");
}
// All sanity checks regarding whether the block is present
// at all are left to the caller
int res;
uint32_t block_no;
// Get block number
block_no = inode->direct_blocks[index];
// Free the block
if ((res = ext2_free_block(ext2, block_no)) < 0) {
return res;
}
// Write updated inode
inode->direct_blocks[index] = 0;
return ext2_write_inode(ext2, inode, ino);
}
int ext2_free_inode(fs_t *ext2, uint32_t ino) {
_assert(ino);
struct ext2_extsb *sb = (struct ext2_extsb *) ext2->fs_private;
char block_buffer[sb->block_size];
uint32_t ino_block_group_number = (ino - 1) / sb->sb.block_group_size_inodes;
uint32_t ino_inode_index_in_group = (ino - 1) % sb->sb.block_group_size_inodes;
int res;
// Read inode usage block
if ((res = ext2_read_block(ext2,
sb->block_group_descriptor_table[ino_block_group_number].inode_usage_bitmap_block,
block_buffer)) < 0) {
return res;
}
// Remove usage bit
_assert(((uint64_t *) block_buffer)[ino_inode_index_in_group / 64] & (1 << (ino_inode_index_in_group % 64)));
((uint64_t *) block_buffer)[ino_inode_index_in_group / 64] &= ~(1 << (ino_inode_index_in_group % 64));
// Write modified bitmap back
if ((res = ext2_write_block(ext2,
sb->block_group_descriptor_table[ino_block_group_number].inode_usage_bitmap_block,
block_buffer)) < 0) {
return res;
}
// Increment free inode count in BGDT entry and write it back
// TODO: this code is repetitive and maybe should be moved to
// ext2_bgdt_inode_inc/_dec()
++sb->block_group_descriptor_table[ino_block_group_number].free_inodes;
for (size_t i = 0; i < sb->block_group_descriptor_table_size_blocks; ++i) {
void *blk_ptr = (void *) (((uintptr_t) sb->block_group_descriptor_table) + i * sb->block_size);
if ((res = ext2_write_block(ext2, sb->block_group_descriptor_table_block + i, blk_ptr)) < 0) {
return res;
}
}
// Update global inode count
++sb->sb.free_inode_count;
if ((res = ext2_write_superblock(ext2)) < 0) {
return res;
}
kdebug("Freed inode #%u\n", ino);
return 0;
}
int ext2_alloc_inode(fs_t *ext2, uint32_t *ino) {
struct ext2_extsb *sb = (struct ext2_extsb *) ext2->fs_private;
char block_buffer[sb->block_size];
uint32_t res_ino = 0;
uint32_t res_group_no = 0;
uint32_t res_ino_number_in_group = 0;
int res;
// Look through BGDT to find any block groups with free inodes
for (size_t i = 0; i < sb->block_group_count; ++i) {
if (sb->block_group_descriptor_table[i].free_inodes > 0) {
// Found a block group with free inodes
kdebug("Allocating an inode inside block group #%zu\n", i);
// Read inode usage bitmap
if ((res = ext2_read_block(ext2,
sb->block_group_descriptor_table[i].inode_usage_bitmap_block,
block_buffer)) < 0) {
return res;
}
// Find a free bit
// Think this should be fine on amd64
for (size_t j = 0; j < sb->block_size / sizeof(uint64_t); ++j) {
// Get bitmap qword
uint64_t qw = ((uint64_t *) block_buffer)[j];
// If not all bits are set in this qword, find exactly which one
if (qw != ((uint64_t) -1)) {
for (size_t k = 0; k < 64; ++k) {
if (!(qw & (1 << k))) {
res_ino_number_in_group = k + j * 64;
res_group_no = i;
res_ino = res_ino_number_in_group + i * sb->sb.block_group_size_inodes + 1;
break;
}
}
if (res_ino) {
break;
}
}
if (res_ino) {
break;
}
}
}
if (res_ino) {
break;
}
}
if (res_ino == 0) {
return -ENOSPC;
}
// Write updated bitmap
((uint64_t *) block_buffer)[res_ino_number_in_group / 64] |= (1 << (res_ino_number_in_group % 64));
if ((res = ext2_write_block(ext2,
sb->block_group_descriptor_table[res_group_no].inode_usage_bitmap_block,
block_buffer)) < 0) {
return res;
}
// Write updated BGDT
--sb->block_group_descriptor_table[res_group_no].free_inodes;
for (size_t i = 0; i < sb->block_group_descriptor_table_size_blocks; ++i) {
void *blk_ptr = (void *) (((uintptr_t) sb->block_group_descriptor_table) + i * sb->block_size);
if ((res = ext2_write_block(ext2, sb->block_group_descriptor_table_block + i, blk_ptr)) < 0) {
return res;
}
}
// Update global inode count and flush superblock
--sb->sb.free_inode_count;
if ((res = ext2_write_superblock(ext2)) < 0) {
return res;
}
*ino = res_ino;
return 0;
}
+138
View File
@@ -0,0 +1,138 @@
#include "sys/fs/ext2.h"
#include "sys/string.h"
#include "sys/assert.h"
#include "sys/errno.h"
#include "sys/debug.h"
#define ext2_super(e) ((struct ext2_extsb *) (e)->fs_private)
int ext2_write_superblock(fs_t *ext2) {
struct ext2_extsb *sb = (struct ext2_extsb *) ext2->fs_private;
return blk_write(ext2->blk, sb, EXT2_SBOFF, EXT2_SBSIZ);
}
int ext2_read_block(fs_t *ext2, uint32_t block_no, void *buf) {
if (!block_no) {
return -1;
}
//printf("ext2_read_block %u\n", block_no);
int res = blk_read(ext2->blk, buf, block_no * ext2_super(ext2)->block_size, ext2_super(ext2)->block_size);
if (res < 0) {
//fprintf(stderr, "ext2: Failed to read %uth block\n", block_no);
kerror("ext2: Failed to read %uth block\n", block_no);
}
return res;
}
int ext2_write_block(fs_t *ext2, uint32_t block_no, const void *buf) {
if (!block_no) {
return -1;
}
int res = blk_write(ext2->blk, buf, block_no * ext2_super(ext2)->block_size, ext2_super(ext2)->block_size);
if (res < 0) {
//fprintf(stderr, "ext2: Failed to write %uth block\n", block_no);
kerror("ext2: Failed to write %uth block\n", block_no);
}
return res;
}
int ext2_write_inode_block(fs_t *ext2, struct ext2_inode *inode, uint32_t index, const void *buf) {
if (index < 12) {
uint32_t block_number = inode->direct_blocks[index];
return ext2_write_block(ext2, block_number, buf);
} else {
// TODO
//abort();
panic("Not implemented\n");
}
}
int ext2_read_inode_block(fs_t *ext2, struct ext2_inode *inode, uint32_t index, void *buf) {
if (index < 12) {
// Use direct ptrs
uint32_t block_number = inode->direct_blocks[index];
return ext2_read_block(ext2, block_number, buf);
} else {
struct ext2_extsb *sb = (struct ext2_extsb *) ext2->fs_private;
// Use buf as indirection block buffer (I think we're allowed to do so)
if (index < 12 + (sb->block_size / 4)) {
// Single indirection
if (ext2_read_block(ext2, inode->l1_indirect_block, buf) < 0) {
return -EIO;
}
uint32_t block_number = ((uint32_t *) buf)[index - 12];
return ext2_read_block(ext2, block_number, buf);
} else {
// Not implemented yet
return -EIO;
}
}
}
int ext2_read_inode(fs_t *ext2, struct ext2_inode *inode, uint32_t ino) {
struct ext2_extsb *sb = (struct ext2_extsb *) ext2->fs_private;
//printf("ext2_read_inode %d\n", ino);
char inode_block_buffer[sb->block_size];
uint32_t ino_block_group_number = (ino - 1) / sb->sb.block_group_size_inodes;
//printf("inode block group number = %d\n", ino_block_group_number);
uint32_t ino_inode_table_block = sb->block_group_descriptor_table[ino_block_group_number].inode_table_block;
//printf("inode table is at block %d\n", ino_inode_table_block);
uint32_t ino_inode_index_in_group = (ino - 1) % sb->sb.block_group_size_inodes;
//printf("inode entry index in the group = %d\n", ino_inode_index_in_group);
uint32_t ino_inode_block_in_group = (ino_inode_index_in_group * sb->inode_struct_size) / sb->block_size;
//printf("inode entry offset is %d blocks\n", ino_inode_block_in_group);
uint32_t ino_inode_block_number = ino_inode_block_in_group + ino_inode_table_block;
//printf("inode block number is %uth block\n", ino_inode_block_number);
//struct ext2_inode *root_inode_block_inode_table = (struct ext2_inode *) root_inode_block_buf;
if (ext2_read_block(ext2, ino_inode_block_number, inode_block_buffer) < 0) {
kerror("ext2: failed to load inode#%d block\n", ino);
return -1;
}
uint32_t ino_entry_in_block = (ino_inode_index_in_group * sb->inode_struct_size) % sb->block_size;
memcpy(inode, &inode_block_buffer[ino_entry_in_block], sb->inode_struct_size);
return 0;
}
int ext2_write_inode(fs_t *ext2, const struct ext2_inode *inode, uint32_t ino) {
struct ext2_extsb *sb = (struct ext2_extsb *) ext2->fs_private;
//printf("ext2_read_inode %d\n", ino);
char inode_block_buffer[sb->block_size];
int res;
uint32_t ino_block_group_number = (ino - 1) / sb->sb.block_group_size_inodes;
//printf("inode block group number = %d\n", ino_block_group_number);
uint32_t ino_inode_table_block = sb->block_group_descriptor_table[ino_block_group_number].inode_table_block;
//printf("inode table is at block %d\n", ino_inode_table_block);
uint32_t ino_inode_index_in_group = (ino - 1) % sb->sb.block_group_size_inodes;
//printf("inode entry index in the group = %d\n", ino_inode_index_in_group);
uint32_t ino_inode_block_in_group = (ino_inode_index_in_group * sb->inode_struct_size) / sb->block_size;
//printf("inode entry offset is %d blocks\n", ino_inode_block_in_group);
uint32_t ino_inode_block_number = ino_inode_block_in_group + ino_inode_table_block;
//printf("inode block number is %uth block\n", ino_inode_block_number);
// Need to read the block to modify it
if ((res = ext2_read_block(ext2, ino_inode_block_number, inode_block_buffer)) < 0) {
return res;
}
uint32_t ino_entry_in_block = (ino_inode_index_in_group * sb->inode_struct_size) % sb->block_size;
memcpy(&inode_block_buffer[ino_entry_in_block], inode, sb->inode_struct_size);
// Write the block back
if ((res = ext2_write_block(ext2, ino_inode_block_number, inode_block_buffer)) < 0) {
return res;
}
return 0;
}
+177
View File
@@ -0,0 +1,177 @@
// ext2fs directory content operations
#include "sys/fs/ext2.h"
#include "sys/string.h"
#include "sys/assert.h"
#include "sys/debug.h"
#include "sys/errno.h"
// #include <string.h>
// #include <stdlib.h>
// #include <assert.h>
// #include <errno.h>
// #include <stdio.h>
// Add an inode to directory
int ext2_dir_add_inode(fs_t *ext2, vnode_t *dir, const char *name, uint32_t ino) {
struct ext2_extsb *sb = (struct ext2_extsb *) ext2->fs_private;
char block_buffer[sb->block_size];
struct ext2_inode *dir_inode = dir->fs_data;
struct ext2_dirent *current_dirent, *result_dirent;
int res;
size_t req_free = strlen(name) + sizeof(struct ext2_dirent);
// Align up 4 bytes
req_free = (req_free + 3) & ~3;
// Try reading parent dirent blocks to see if any has
// some space to fit our file
size_t dir_size_blocks = (dir_inode->size_lower + sb->block_size - 1) / sb->block_size;
for (size_t i = 0; i < dir_size_blocks; ++i) {
current_dirent = NULL;
result_dirent = NULL;
size_t off = 0;
// Read directory content block
if ((res = ext2_read_inode_block(ext2, dir_inode, i, block_buffer)) < 0) {
return res;
}
// Check if any of the entries can be split to fit our entry
while (off < sb->block_size) {
current_dirent = (struct ext2_dirent *) &block_buffer[off];
if (current_dirent->ino == 0) {
kwarn("ext2: found dirent with ino = 0\n");
}
// Check how much space we need to still store the entry
size_t real_len = current_dirent->name_len + sizeof(struct dirent);
real_len = (real_len + 3) & ~3;
// And check how much is left to fit our entry
if (real_len < current_dirent->len /* Sanity? */ &&
current_dirent->len - real_len >= req_free) {
// Yay, can fit our dirent in there
// Sanity check that we're aligned properly
_assert(((off + real_len) & 3) == 0);
result_dirent = (struct ext2_dirent *) &block_buffer[off + real_len];
result_dirent->len = current_dirent->len - real_len;
result_dirent->name_len = strlen(name);
result_dirent->type_ind = 0;
result_dirent->ino = ino;
strncpy(result_dirent->name, name, result_dirent->name_len);
current_dirent->len = real_len;
if ((res = ext2_write_inode_block(ext2, dir_inode, i, block_buffer)) < 0) {
return res;
}
return 0;
}
off += current_dirent->len;
}
}
dir_inode->size_lower += sb->block_size;
if ((res = ext2_inode_alloc_block(ext2, dir_inode, dir->fs_number, dir_size_blocks)) < 0) {
dir_inode->size_lower -= sb->block_size;
return res;
}
memset(block_buffer, 0, sb->block_size);
current_dirent = (struct ext2_dirent *) block_buffer;
current_dirent->ino = ino;
current_dirent->len = sb->block_size;
current_dirent->name_len = strlen(name);
current_dirent->type_ind = 0;
strncpy(current_dirent->name, name, current_dirent->name_len);
return ext2_write_inode_block(ext2, dir_inode, dir_size_blocks, block_buffer);
}
// Not only free the block itself, but also remove it from index list
static int ext2_free_block_index(fs_t *ext2, struct ext2_inode *inode, uint32_t index, uint32_t ino, size_t sz) {
if (index >= 12) {
// TODO: Implement this
panic("Not implemented\n");
}
int res;
uint32_t block_no = inode->direct_blocks[index];
if ((res = ext2_free_block(ext2, block_no)) < 0) {
return res;
}
// Shift direct indexed blocks
for (uint32_t i = index; i < 11; ++i) {
inode->direct_blocks[i] = inode->direct_blocks[i + 1];
}
// TODO: inode->direct_blocks[11] becomes the first block of indirect block
inode->direct_blocks[11] = 0;
inode->size_lower -= sz;
return ext2_write_inode(ext2, inode, ino);
}
int ext2_dir_remove_inode(fs_t *ext2, vnode_t *dir, const char *name, uint32_t ino) {
struct ext2_extsb *sb = (struct ext2_extsb *) ext2->fs_private;
char block_buffer[sb->block_size];
struct ext2_inode *dir_inode = dir->fs_data;
struct ext2_dirent *current_dirent, *prev_dirent;
int res;
size_t dir_size_blocks = (dir_inode->size_lower + sb->block_size - 1) / sb->block_size;
for (size_t i = 0; i < dir_size_blocks; ++i) {
if ((res = ext2_read_inode_block(ext2, dir_inode, i, block_buffer)) < 0) {
return res;
}
size_t off = 0;
current_dirent = NULL;
prev_dirent = NULL;
while (off < sb->block_size) {
prev_dirent = current_dirent;
current_dirent = (struct ext2_dirent *) &block_buffer[off];
if (current_dirent->ino == 0) {
kwarn("ext2: found dirent with ino = 0\n");
}
if (current_dirent->name_len == strlen(name) &&
!strncmp(current_dirent->name, name, current_dirent->name_len)) {
// Found matching dirent
// Sanity
_assert(current_dirent->ino == ino);
if (current_dirent->len + off >= sb->block_size) {
// It's the last node in the list
if (!prev_dirent) {
return ext2_free_block_index(ext2, dir_inode, i, dir->fs_number, sb->block_size);
}
// Resize the previous node
prev_dirent->len += current_dirent->len;
return ext2_write_inode_block(ext2, dir_inode, i, block_buffer);
} else {
// It's not the last one - relocate the next entry
uint32_t len = current_dirent->len;
struct ext2_dirent *next_dirent = (struct ext2_dirent *) &block_buffer[off + len];
memmove(current_dirent, next_dirent, next_dirent->len);
next_dirent = current_dirent;
next_dirent->len += len;
_assert(((off + len) & 3) == 0);
return ext2_write_inode_block(ext2, dir_inode, i, block_buffer);
}
}
off += current_dirent->len;
}
}
return -EIO;
}
+750
View File
@@ -0,0 +1,750 @@
// ext2fs vnode operations
#include "sys/fs/ext2.h"
#include "sys/fs/node.h"
#include "sys/fs/ofile.h"
#include "sys/fs/fcntl.h"
#include "sys/fs/vfs.h"
#include "sys/string.h"
#include "sys/debug.h"
#include "sys/assert.h"
#include "sys/panic.h"
#include "sys/errno.h"
#include "sys/heap.h"
// #include <string.h>
// #include <stddef.h>
// #include <stdlib.h>
// #include <assert.h>
// #include <stdio.h>
// #include <errno.h>
// Forward declaration of ext2 vnode functions
static int ext2_vnode_find(vnode_t *vn, const char *name, vnode_t **resvn);
static int ext2_vnode_creat(vnode_t *at, struct vfs_ioctx *ctx, const char *name, mode_t mode, int opt, vnode_t **resvn);
static int ext2_vnode_mkdir(vnode_t *at, const char *name, mode_t mode);
static int ext2_vnode_open(vnode_t *vn, int opt);
static int ext2_vnode_opendir(vnode_t *vn, int opt);
static ssize_t ext2_vnode_read(struct ofile *fd, void *buf, size_t count);
static ssize_t ext2_vnode_write(struct ofile *fd, const void *buf, size_t count);
static int ext2_vnode_truncate(struct ofile *fd, size_t length);
static int ext2_vnode_readdir(struct ofile *fd);
static void ext2_vnode_destroy(vnode_t *vn);
static int ext2_vnode_stat(vnode_t *vn, struct stat *st);
static int ext2_vnode_chmod(vnode_t *vn, mode_t mode);
static int ext2_vnode_chown(vnode_t *vn, uid_t uid, gid_t gid);
static int ext2_vnode_unlink(vnode_t *at, vnode_t *vn, const char *name);
static int ext2_vnode_access(vnode_t *vn, uid_t *uid, gid_t *gid, mode_t *mode);
static int ext2_vnode_readlink(vnode_t *vn, char *dst);
static int ext2_vnode_symlink(vnode_t *at, struct vfs_ioctx *ctx, const char *name, const char *dst);
struct vnode_operations ext2_vnode_ops = {
.find = ext2_vnode_find,
.creat = ext2_vnode_creat,
.mkdir = ext2_vnode_mkdir,
.destroy = ext2_vnode_destroy,
.readlink = ext2_vnode_readlink,
.symlink = ext2_vnode_symlink,
.chmod = ext2_vnode_chmod,
.chown = ext2_vnode_chown,
.stat = ext2_vnode_stat,
.unlink = ext2_vnode_unlink,
.access = ext2_vnode_access,
.opendir = ext2_vnode_opendir,
.readdir = ext2_vnode_readdir,
.open = ext2_vnode_open,
.read = ext2_vnode_read,
.write = ext2_vnode_write,
.truncate = ext2_vnode_truncate,
};
//// vnode function implementation
static int ext2_vnode_find(vnode_t *vn, const char *name, vnode_t **res) {
fs_t *ext2 = vn->fs;
struct ext2_extsb *sb = vn->fs->fs_private;
struct ext2_inode *inode = vn->fs_data;
char buffer[sb->block_size];
struct ext2_dirent *dirent = NULL;
size_t block_count = (inode->size_lower + (sb->block_size - 1)) / sb->block_size;
// char ent_name[256];
size_t index = 0;
while (index < block_count) {
// Read directory contents block
if (ext2_read_inode_block(ext2, inode, index, buffer) < 0) {
return -EIO;
}
size_t offset = 0;
while (1) {
dirent = (struct ext2_dirent *) &buffer[offset];
if (!dirent->len) {
break;
}
if (dirent->ino) {
if (strlen(name) == dirent->name_len && !strncmp(dirent->name, name, dirent->name_len)) {
// Found the entry
vnode_t *out = (vnode_t *) kmalloc(sizeof(vnode_t));
out->op = &ext2_vnode_ops;
out->fs = ext2;
struct ext2_inode *result_inode = (struct ext2_inode *) kmalloc(sb->inode_struct_size);
if (ext2_read_inode(ext2, result_inode, dirent->ino) != 0) {
return -EIO;
}
out->fs_data = result_inode;
out->fs_number = dirent->ino;
out->type = ext2_inode_type(result_inode);
*res = out;
//printf("Lookup %s in ino %d = %d\n", name, vn->fs_number, out->fs_number);
return 0;
}
}
offset += dirent->len;
if (offset >= sb->block_size) {
break;
}
}
++index;
}
return -ENOENT;
}
static int ext2_vnode_opendir(vnode_t *vn, int opt) {
_assert(vn->type == VN_DIR);
return 0;
}
static int ext2_vnode_open(vnode_t *vn, int opt) {
_assert(vn->type == VN_REG);
return 0;
}
static int ext2_vnode_mkdir(vnode_t *at, const char *name, mode_t mode) {
fs_t *ext2 = at->fs;
_assert(at->type == VN_DIR);
struct ext2_extsb *sb = (struct ext2_extsb *) ext2->fs_private;
char block_buffer[sb->block_size];
uint32_t new_ino, new_block_no;
int res;
// Allocate a new inode for the directory
if ((res = ext2_alloc_inode(ext2, &new_ino)) != 0) {
kerror("ext2: Failed to allocate an inode\n");
return res;
}
// Allocate a block for "." and ".." entries
if ((res = ext2_alloc_block(ext2, &new_block_no)) < 0) {
kerror("ext2: Failed to allocate a block\n");
return res;
}
struct ext2_inode *ent_inode = (struct ext2_inode *) kmalloc(sb->inode_struct_size);
// Now create an entry in parents dirent list
if ((res = ext2_dir_add_inode(ext2, at, name, new_ino)) < 0) {
return res;
}
// Fill the inode
ent_inode->flags = 0;
ent_inode->dir_acl = 0;
ent_inode->frag_block_addr = 0;
ent_inode->gen_number = 0;
ent_inode->hard_link_count = 1;
ent_inode->acl = 0;
ent_inode->os_value_1 = 0;
memset(ent_inode->os_value_2, 0, sizeof(ent_inode->os_value_2));
// TODO: time support in kernel
ent_inode->atime = 0;
ent_inode->mtime = 0;
ent_inode->ctime = 0;
ent_inode->dtime = 0;
memset(ent_inode->direct_blocks, 0, sizeof(ent_inode->direct_blocks));
ent_inode->direct_blocks[0] = new_block_no;
ent_inode->l1_indirect_block = 0;
ent_inode->l2_indirect_block = 0;
ent_inode->l3_indirect_block = 0;
ent_inode->type_perm = (mode & 0x1FF) | EXT2_TYPE_DIR;
// TODO: obtain these from process context in kernel
ent_inode->uid = 0;
ent_inode->gid = 0;
ent_inode->disk_sector_count = 0;
ent_inode->size_lower = sb->block_size;
memset(block_buffer, 0, sb->block_size);
// "."
struct ext2_dirent *dirent = (struct ext2_dirent *) block_buffer;
dirent->ino = new_ino;
dirent->name_len = 1;
dirent->len = (sizeof(struct ext2_dirent) + 4) & ~3;
dirent->name[0] = '.';
dirent->type_ind = 0;
// ".."
dirent = (struct ext2_dirent *) &block_buffer[dirent->len];
dirent->ino = at->fs_number;
dirent->name_len = 2;
dirent->len = sb->block_size - ((sizeof(struct ext2_dirent) + 4) & ~3);
dirent->name[0] = '.';
dirent->name[1] = '.';
dirent->type_ind = 0;
// Write directory's first block
if ((res = ext2_write_block(ext2, new_block_no, block_buffer)) < 0) {
return res;
}
// Write directory inode
if ((res = ext2_write_inode(ext2, ent_inode, new_ino)) < 0) {
return res;
}
return 0;
}
static int ext2_vnode_creat(vnode_t *at, struct vfs_ioctx *ctx, const char *name, mode_t mode, int opt, vnode_t **resvn) {
fs_t *ext2 = at->fs;
_assert(at->type == VN_DIR);
_assert(/* Don't support making directories like this */ !(mode & O_DIRECTORY));
struct ext2_extsb *sb = (struct ext2_extsb *) ext2->fs_private;
uint32_t new_ino;
int res;
// Allocate new inode number
if ((res = ext2_alloc_inode(ext2, &new_ino)) != 0) {
kerror("Failed to allocate inode\n");
return res;
}
kdebug("Allocated inode %d\n", new_ino);
// Create an inode struct in memory
struct ext2_inode *ent_inode = (struct ext2_inode *) kmalloc(sb->inode_struct_size);
// Now create an entry in parents dirent list
if ((res = ext2_dir_add_inode(ext2, at, name, new_ino)) < 0) {
return res;
}
// Fill the inode
ent_inode->flags = 0;
ent_inode->dir_acl = 0;
ent_inode->frag_block_addr = 0;
ent_inode->gen_number = 0;
ent_inode->hard_link_count = 1;
ent_inode->acl = 0;
ent_inode->os_value_1 = 0;
memset(ent_inode->os_value_2, 0, sizeof(ent_inode->os_value_2));
// TODO: time support in kernel
ent_inode->atime = 0;
ent_inode->mtime = 0;
ent_inode->ctime = 0;
ent_inode->dtime = 0;
memset(ent_inode->direct_blocks, 0, sizeof(ent_inode->direct_blocks));
ent_inode->l1_indirect_block = 0;
ent_inode->l2_indirect_block = 0;
ent_inode->l3_indirect_block = 0;
ent_inode->uid = ctx->uid;
ent_inode->gid = ctx->gid;
// TODO: only regular files can be created this way now
ent_inode->type_perm = (mode & 0x1FF) | (EXT2_TYPE_REG);
ent_inode->disk_sector_count = 0;
ent_inode->size_lower = 0;
// Write the inode
if ((res = ext2_write_inode(ext2, ent_inode, new_ino)) < 0) {
return res;
}
// Create the resulting vnode
vnode_t *vn = (vnode_t *) kmalloc(sizeof(vnode_t));
vn->fs = ext2;
vn->fs_data = ent_inode;
vn->fs_number = new_ino;
vn->op = &ext2_vnode_ops;
vn->type = VN_REG;
*resvn = vn;
return 0;
}
static ssize_t ext2_vnode_read(struct ofile *fd, void *buf, size_t count) {
vnode_t *vn = fd->vnode;
struct ext2_inode *inode = (struct ext2_inode *) vn->fs_data;
struct ext2_extsb *sb = vn->fs->fs_private;
size_t nread = MIN(inode->size_lower - fd->pos, count);
if (nread == 0) {
return -1;
}
size_t block_number = fd->pos / sb->block_size;
size_t nblocks = (nread + sb->block_size - 1) / sb->block_size;
char block_buffer[sb->block_size];
for (size_t i = 0; i < nblocks; ++i) {
if (ext2_read_inode_block(vn->fs, inode, i + block_number, block_buffer) < 0) {
kerror("Failed to read inode %d block #%zu\n", vn->fs_number, i + block_number);
return -EIO;
}
if (i == 0) {
size_t ncpy = MIN(sb->block_size - fd->pos % sb->block_size, nread);
memcpy(buf, block_buffer + fd->pos % sb->block_size, ncpy);
} else {
size_t ncpy = MIN(sb->block_size, nread - sb->block_size * i);
memcpy((void *) (((uintptr_t) buf) + sb->block_size * i), block_buffer, ncpy);
}
}
return nread;
}
static ssize_t ext2_vnode_write(struct ofile *fd, const void *buf, size_t count) {
vnode_t *vn = fd->vnode;
_assert(vn);
struct ext2_inode *inode = (struct ext2_inode *) vn->fs_data;
fs_t *ext2 = vn->fs;
struct ext2_extsb *sb = (struct ext2_extsb *) ext2->fs_private;
char block_buffer[sb->block_size];
int res;
if (fd->pos > inode->size_lower) {
// This shouldn't be possible, yeah?
return -ESPIPE;
}
// How many bytes can we write into the blocks already allocated
size_t size_blocks = (inode->size_lower + sb->block_size - 1) / sb->block_size;
size_t can_write = size_blocks * sb->block_size - inode->size_lower;
size_t current_block = fd->pos / sb->block_size;
size_t written = 0;
size_t remaining = count;
if (can_write) {
size_t can_write_blocks = (can_write + sb->block_size - 1) / sb->block_size;
for (size_t i = 0; i < can_write_blocks; ++i) {
size_t block_index = current_block + i;
size_t pos_in_block = fd->pos % sb->block_size;
size_t need_write = MIN(remaining, sb->block_size - pos_in_block);
kdebug("Write %zuB to block %zu offset %zu\n", need_write, block_index, pos_in_block);
if (need_write == sb->block_size) {
// Can write block without reading it
// TODO: implement this
panic("Not implemented\n");
} else {
// Read the block to change its contents
// and write it back again
if ((res = ext2_read_inode_block(ext2, inode, block_index, block_buffer)) < 0) {
break;
}
memcpy(block_buffer + pos_in_block, (void *) (((uintptr_t) buf) + written), need_write);
if ((res = ext2_write_inode_block(ext2, inode, block_index, block_buffer)) < 0) {
break;
}
}
written += need_write;
fd->pos += need_write;
remaining -= need_write;
}
inode->size_lower = MAX(fd->pos, inode->size_lower);
current_block += can_write_blocks;
}
if (remaining) {
// Need to allocate additional blocks
size_t need_blocks = (remaining + sb->block_size - 1) / sb->block_size;
for (size_t i = 0; i < need_blocks; ++i) {
size_t block_index = current_block + i;
size_t need_write = MIN(remaining, sb->block_size);
// Update the size here so it gets written when the block is allocated
// and inode struct is flushed
inode->size_lower += need_write;
// Allocate a block for the index
if ((res = ext2_inode_alloc_block(ext2, inode, vn->fs_number, block_index)) < 0) {
kerror("Could not allocate a block for writing\n");
break;
}
if (need_write == sb->block_size) {
// TODO: implement this
panic("Not implemented\n");
} else {
// Writing the last block
memcpy(block_buffer, (void *) (((uintptr_t) buf) + written), need_write);
if ((res = ext2_write_inode_block(ext2, inode, block_index, block_buffer)) < 0) {
break;
}
}
written += need_write;
fd->pos += need_write;
remaining -= need_write;
}
} else {
if (written) {
// Flush inode struct to disk - size has changed
ext2_write_inode(ext2, inode, vn->fs_number);
}
}
return written;
}
static int ext2_vnode_truncate(struct ofile *fd, size_t length) {
vnode_t *vn = fd->vnode;
fs_t *ext2 = vn->fs;
struct ext2_inode *inode = (struct ext2_inode *) vn->fs_data;
struct ext2_extsb *sb = vn->fs->fs_private;
int res;
if (length == inode->size_lower) {
// Already good
return 0;
}
size_t was_blocks = (inode->size_lower + sb->block_size - 1) / sb->block_size;
size_t now_blocks = (length + sb->block_size - 1) / sb->block_size;
ssize_t delta_blocks = now_blocks - was_blocks;
if (delta_blocks < 0) {
// Free truncated blocks
// XXX: reverse the loop
for (size_t i = now_blocks; i < was_blocks; ++i) {
// Modify inode right here because ext2_free_inode_block will
// flush these changes to disk so we don't have to write it
// twice
inode->size_lower -= sb->block_size;
if ((res = ext2_free_inode_block(ext2, inode, vn->fs_number, i)) < 0) {
// Put the block size back, couldn't free it
inode->size_lower += sb->block_size;
return res;
}
}
// All the blocks were successfully freed, can set proper file length
if (inode->size_lower != length) {
// If requested size is not block-aligned, we need to write inode
// struct to disk once again
inode->size_lower = length;
if ((res = ext2_write_inode(ext2, inode, vn->fs_number)) < 0) {
return res;
}
}
return 0;
} else {
kerror("Not implemented: upwards truncation (ext2)\n");
return -EINVAL;
}
}
// TODO: replace this with getdents
static int ext2_vnode_readdir(struct ofile *fd) {
vnode_t *vn = fd->vnode;
struct ext2_inode *inode = (struct ext2_inode *) vn->fs_data;
struct ext2_extsb *sb = vn->fs->fs_private;
if (fd->pos >= inode->size_lower) {
return -1;
}
size_t block_number = fd->pos / sb->block_size;
char block_buffer[sb->block_size];
if (ext2_read_inode_block(vn->fs, inode, block_number, block_buffer) < 0) {
return -EIO;
}
size_t block_offset = fd->pos % sb->block_size;
struct ext2_dirent *ext2dir = (struct ext2_dirent *) &block_buffer[block_offset];
if (ext2dir->len == 0) {
// If entry size is zero, guess we're finished - align the fd->pos up to block size
fd->pos = (fd->pos + sb->block_size - 1) / sb->block_size;
return -1;
}
struct dirent *vfsdir = (struct dirent *) fd->dirent_buf;
vfsdir->d_ino = ext2dir->ino;
strncpy(vfsdir->d_name, ext2dir->name, ext2dir->name_len);
vfsdir->d_name[ext2dir->name_len] = 0;
vfsdir->d_reclen = ext2dir->len;
vfsdir->d_type = ext2dir->type_ind;
// Not implemented, I guess
vfsdir->d_off = 0;
fd->pos += ext2dir->len;
return 0;
}
static void ext2_vnode_destroy(vnode_t *vn) {
// Release inode struct
kfree(vn->fs_data);
}
static int ext2_vnode_stat(vnode_t *vn, struct stat *st) {
_assert(vn && vn->fs);
struct ext2_inode *inode = (struct ext2_inode *) vn->fs_data;
_assert(inode);
struct ext2_extsb *sb = (struct ext2_extsb *) vn->fs->fs_private;
_assert(sb);
st->st_atime = inode->atime;
st->st_ctime = inode->ctime;
st->st_mtime = inode->mtime;
st->st_dev = 0; // Not implemented
st->st_rdev = 0; // Not implemented
st->st_gid = inode->gid;
st->st_uid = inode->uid;
st->st_mode = inode->type_perm;
st->st_size = inode->size_lower;
st->st_blocks = (inode->size_lower + sb->block_size - 1) / sb->block_size;
st->st_blksize = sb->block_size;
st->st_nlink = 0;
st->st_ino = vn->fs_number;
return 0;
}
static int ext2_vnode_chmod(vnode_t *vn, mode_t mode) {
_assert(vn && vn->fs && vn->fs_data);
struct ext2_inode *inode = (struct ext2_inode *) vn->fs_data;
// Update only access mode
inode->type_perm &= ~0x1FF;
inode->type_perm |= mode & 0x1FF;
// Write the inode back
return ext2_write_inode(vn->fs, inode, vn->fs_number);
}
static int ext2_vnode_chown(vnode_t *vn, uid_t uid, gid_t gid) {
_assert(vn && vn->fs && vn->fs_data);
struct ext2_inode *inode = (struct ext2_inode *) vn->fs_data;
inode->gid = gid;
inode->uid = uid;
// Write the inode back
return ext2_write_inode(vn->fs, inode, vn->fs_number);
}
static int ext2_vnode_unlink(vnode_t *at, vnode_t *vn, const char *name) {
struct ext2_inode *inode = vn->fs_data;
struct ext2_inode *at_inode = at->fs_data;
fs_t *ext2 = vn->fs;
struct ext2_extsb *sb = (struct ext2_extsb *) ext2->fs_private;
uint32_t ino = vn->fs_number;
int res;
if (vn->type == VN_DIR) {
// Check if the directory we're unlinking has any entries besides
// . and ..
// Can tell this just by looking at the first block
if (inode->size_lower > sb->block_size) {
// Directory size is more than one block - totally
// has something inside
return -EISDIR;
}
char block_buffer[sb->block_size];
size_t off = 0;
if ((res = ext2_read_inode_block(ext2, inode, 0, block_buffer)) < 0) {
return res;
}
while (off < sb->block_size) {
struct ext2_dirent *ent = (struct ext2_dirent *) &block_buffer[off];
if (!ent->ino) {
break;
}
if (ent->name_len == 1 && ent->name[0] == '.') {
off += ent->len;
continue;
}
if (ent->name_len == 2 && ent->name[1] == '.' && ent->name[0] == '.') {
off += ent->len;
continue;
}
return -EISDIR;
}
}
// Free blocks used by the inode - truncate the file to zero
size_t nblocks = (inode->size_lower + sb->block_size - 1) / sb->block_size;
inode->size_lower = nblocks * sb->block_size;
for (ssize_t i = nblocks - 1; i >= 0; --i) {
inode->size_lower -= sb->block_size;
if ((res = ext2_free_inode_block(ext2, inode, ino, i)) < 0) {
return res;
}
}
// inode->size_lower is now 0
_assert(inode->size_lower == 0);
// Free the inode itself
if ((res = ext2_free_inode(ext2, ino)) < 0) {
return res;
}
// Now remove the entry from directory
if ((res = ext2_dir_remove_inode(ext2, at, name, ino)) < 0) {
return res;
}
return 0;
}
static int ext2_vnode_access(vnode_t *vn, uid_t *uid, gid_t *gid, mode_t *mode) {
_assert(vn && vn->fs_data);
struct ext2_inode *inode = vn->fs_data;
*uid = inode->uid;
*gid = inode->gid;
*mode = inode->type_perm & 0x1FF;
return 0;
}
static int ext2_vnode_readlink(vnode_t *vn, char *dst) {
_assert(vn && vn->fs_data);
struct ext2_inode *inode = vn->fs_data;
fs_t *ext2 = vn->fs;
struct ext2_extsb *sb = (struct ext2_extsb *) ext2->fs_private;
if (inode->size_lower >= 60) {
char block_buffer[sb->block_size];
int res;
if ((res = ext2_read_inode_block(ext2, inode, 0, block_buffer)) < 0) {
return res;
}
strncpy(dst, block_buffer, inode->size_lower);
dst[inode->size_lower] = 0;
} else {
const char *src = (const char *) inode->direct_blocks;
strncpy(dst, src, inode->size_lower);
dst[inode->size_lower] = 0;
}
return 0;
}
static int ext2_vnode_symlink(vnode_t *at, struct vfs_ioctx *ctx, const char *name, const char *dst) {
_assert(at && at->fs && at->fs_data);
struct ext2_inode *inode = at->fs_data;
fs_t *ext2 = at->fs;
struct ext2_extsb *sb = (struct ext2_extsb *) ext2->fs_private;
uint32_t new_ino;
int res;
// Allocate new inode number
if ((res = ext2_alloc_inode(ext2, &new_ino)) != 0) {
kerror("Failed to allocate inode\n");
return res;
}
kdebug("Allocated inode %d\n", new_ino);
// Create an inode struct in memory
struct ext2_inode *ent_inode = (struct ext2_inode *) kmalloc(sb->inode_struct_size);
// Now create an entry in parents dirent list
if ((res = ext2_dir_add_inode(ext2, at, name, new_ino)) < 0) {
return res;
}
// Fill the inode
ent_inode->flags = 0;
ent_inode->dir_acl = 0;
ent_inode->frag_block_addr = 0;
ent_inode->gen_number = 0;
ent_inode->hard_link_count = 1;
ent_inode->acl = 0;
ent_inode->os_value_1 = 0;
memset(ent_inode->os_value_2, 0, sizeof(ent_inode->os_value_2));
// TODO: time support in kernel
ent_inode->atime = 0;
ent_inode->mtime = 0;
ent_inode->ctime = 0;
ent_inode->dtime = 0;
ent_inode->size_lower = strlen(dst);
if (ent_inode->size_lower <= 60) {
char *dst_str = (char *) ent_inode->direct_blocks;
memset(dst_str, 0, 60);
strncpy(dst_str, dst, ent_inode->size_lower);
} else {
char block_buffer[sb->block_size];
uint32_t block_no;
if ((res = ext2_alloc_block(ext2, &block_no)) < 0) {
return res;
}
memset(block_buffer, 0, sb->block_size);
strncpy(block_buffer, dst, sb->block_size);
if ((res = ext2_write_block(ext2, block_no, block_buffer)) < 0) {
return res;
}
memset(ent_inode->direct_blocks, 0, sizeof(ent_inode->direct_blocks));
ent_inode->l1_indirect_block = 0;
ent_inode->l2_indirect_block = 0;
ent_inode->l3_indirect_block = 0;
ent_inode->direct_blocks[0] = block_no;
}
ent_inode->uid = ctx->uid;
ent_inode->gid = ctx->gid;
ent_inode->type_perm = 0777 | EXT2_TYPE_LNK;
// Write the inode
if ((res = ext2_write_inode(ext2, ent_inode, new_ino)) < 0) {
return res;
}
return 0;
}
+49
View File
@@ -0,0 +1,49 @@
#include "sys/string.h"
#include "sys/fs/fs.h"
#include "sys/errno.h"
// #include <stddef.h>
// #include <string.h>
// #include <errno.h>
static struct fs_class *fses[10] = { NULL };
static struct fs mounts[10];
struct fs *fs_create(struct fs_class *cls, struct blkdev *blk, vnode_t *at) {
for (size_t i = 0; i < 10; ++i) {
if (mounts[i].cls == NULL) {
mounts[i].cls = cls;
mounts[i].blk= blk;
mounts[i].mnt_at = at;
return &mounts[i];
}
}
return NULL;
}
struct fs_class *fs_class_by_name(const char *name) {
for (size_t i = 0; i < 10; ++i) {
if (!fses[i]) {
break;
}
if (!strcmp(name, fses[i]->name)) {
return fses[i];
}
}
return NULL;
}
int fs_class_register(struct fs_class *cls) {
if (fs_class_by_name(cls->name)) {
return -EEXIST;
}
for (size_t i = 0; i < 10; ++i) {
if (!fses[i]) {
fses[i] = cls;
return 0;
}
}
return -ENOMEM;
}
+122
View File
@@ -0,0 +1,122 @@
#include "sys/fs/node.h"
#include "sys/fs/vfs.h"
#include "sys/fs/fs.h"
#include "sys/debug.h"
#include "sys/assert.h"
#include "sys/heap.h"
#include "sys/string.h"
static void vfs_node_remove(struct vfs_node *node) {
// If node->parent == NULL, we've called this
// function on [root], which should not be possible
if (!node || !node->parent) {
return;
}
struct vfs_node *parent = node->parent;
if (parent->child == node) {
parent->child = node->cdr;
} else {
for (struct vfs_node *it = parent->child; it; it = it->cdr) {
if (it->cdr == node) {
it->cdr = node->cdr;
break;
}
}
// TODO: handle case when for some reason node is absent in
// parent's children list
}
// Decrement refcount for parent, unless it's [root]
if (parent->parent && !parent->child && !parent->vnode->refcount) {
vnode_free(parent->vnode);
}
// Just a wrapper around free()
vfs_node_free(node);
}
void vnode_free(vnode_t *vn) {
_assert(vn && vn->op);
_assert(!vn->refcount);
struct vfs_node *node = vn->tree_node;
struct vfs_node *link_node = NULL;
if (node->ismount) {
return;
}
if (vn->op->destroy) {
// This will free/release underlying fs_data
vn->op->destroy(vn);
}
if (node) {
vfs_node_remove(node);
}
memset(vn, 0, sizeof(vnode_t));
kfree(vn);
}
void vnode_ref(vnode_t *vn) {
// char buf[1024];
// vfs_vnode_path(buf, vn);
// printf("++refcount for %s\n", buf);
_assert(vn);
_assert(vn->fs);
_assert(vn->fs->cls);
// FS_NODE_MAPPER means persistent vnodes
if (vn->fs->cls->opt & FS_NODE_MAPPER) {
return;
}
struct vfs_node *node = (struct vfs_node *) vn->tree_node;
if (node && !node->parent) {
// Don't change refcounter for root nodes
return;
}
++vn->refcount;
}
void vnode_unref(vnode_t *vn) {
_assert(vn);
_assert(vn->fs);
_assert(vn->fs->cls);
// FS_NODE_MAPPER means persistent vnodes
if (vn->fs->cls->opt & FS_NODE_MAPPER) {
return;
}
// TODO: don't free root nodes
char buf[1024];
vfs_vnode_path(buf, vn);
struct vfs_node *node = (struct vfs_node *) vn->tree_node;
if (!node->parent) {
return;
}
if (vn->refcount > 0) {
--vn->refcount;
if (vn->refcount == 0) {
if (node->child) {
return;
}
kdebug("free %s\n", buf);
// Free vnode
vnode_free(vn);
}
} else {
_assert(node->child);
kdebug("--refcount with 0 but child\n");
}
}
+1491
View File
File diff suppressed because it is too large Load Diff