Integrate VFS/ext2 code
This commit is contained in:
+12
-1
@@ -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
|
||||
|
||||
@@ -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);
|
||||
@@ -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[];
|
||||
};
|
||||
@@ -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;
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
typedef struct list_simple {
|
||||
void *car;
|
||||
struct list_simple *cdr;
|
||||
} list_simple_t;
|
||||
|
||||
@@ -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);
|
||||
@@ -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];
|
||||
};
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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;
|
||||
};
|
||||
@@ -0,0 +1,4 @@
|
||||
#pragma once
|
||||
#include "sys/fs/fs.h"
|
||||
|
||||
void tarfs_init(void);
|
||||
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
#include "list.h"
|
||||
|
||||
typedef struct tree {
|
||||
void *value;
|
||||
struct tree *parent;
|
||||
list_simple_t *children;
|
||||
} tree_t;
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
@@ -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
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user