一个300行的文件系统(基于linux6.6)

本文围绕基于Linux6.6的最小文件系统展开。原示例在新版本编译报错,处理后加载ko挂载文件系统,发现创建多文件和长文件名时文件属性显示异常。经分析找到问题原因,最终给出修改版本,不过该文件系统仍存在一些待解决的bug。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、背景

其实这是一个很久就看过的文章,但是由于是在微信上看到的,下来一直没有机会试一下,久而久之就遗忘了,最近在知乎上又看到这篇(当然是剽窃的,知乎最近这种抄别人劳作的风气越來越重了,本来想举报一下,结果又要侵权本人提供证据,被侵权的文章也不是我写的,我也就没法提供证据举报了)

原文链接(感谢作者提供了一个非常好的例子):

300来行代码带你实现一个能跑的最小Linux文件系统

二、基于linux6.6的更新

由于原作者的示例是基于一个比较老的linux 内核版本,在新版本上有较多编译报错,大部分是结构体的变化,比较大一点的是tinyfs_readdir 接口的变化

版本一:

(这一版本只是处理了编译报错相关的问题,主体部分未做修改)

tinyfs.h

#define MAXLEN 8
#define MAX_FILES 32
#define MAX_BLOCKSIZE 512

//define the dir
struct dir_entry {
    char filename[MAXLEN];
    uint8_t idx;
};

//define the file block
struct file_blk {
    uint8_t busy;
    mode_t mode;
    uint8_t idx;

    union {
        uint8_t file_size;
        uint8_t dir_children;
    };
    char data[0];
};

tinyfs.c

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/uaccess.h>

#include "tinyfs.h"

struct file_blk block[MAX_FILES+1];

int curr_count = 0;

static int get_block(void)
{
    int i;
    for( i = 2; i < MAX_FILES; i++) {
        if (!block[i].busy) {
            block[i].busy = 1;
            return i;
        }
    }

    return -1;
}

static struct inode_operations tinyfs_inode_ops;

static int tinyfs_readdir(struct file *filp, struct dir_context *ctx)
{

    loff_t pos;
    struct file_blk  *blk;
    struct dir_entry *entry;
	struct dentry *dentry = filp->f_path.dentry;

    int i;

	if (!dir_emit_dots(filp, ctx))
		return 0;

    pos = filp->f_pos;

    if (pos)
        return 0;

    blk = (struct file_blk*)dentry->d_inode->i_private;

    if (!S_ISDIR(blk->mode)) {
        return -ENOTDIR;
    }

    //loop get one dir included file name
    entry = (struct dir_entry *)&blk->data[0];
    for (i = 0; i < blk->dir_children; i++) {
        if (!dir_emit(ctx, entry[i].filename, strlen(entry[i].filename),
			      entry[i].idx,
			      DT_UNKNOWN))
			break;
        filp->f_pos += sizeof(struct dir_entry);
        pos += sizeof(struct dir_entry);

    }

	return 0;
}

ssize_t tinyfs_read(struct file *filp, char __user *buf, size_t len, loff_t *ppos)
{
    struct file_blk *blk;
    char *buffer;

    blk = (struct file_blk*)filp->f_path.dentry->d_inode->i_private;

    if (*ppos >= blk->file_size)
        return 0;

    buffer = (char *)&blk->data[0];
    len = min((size_t)blk->file_size, len);

    if (copy_to_user(buf, buffer, len)) {
        return -EFAULT;
    }
    *ppos += len;

    return len;
}

ssize_t tinyfs_write(struct file *filp, const char __user *buf, size_t len, loff_t *ppos)
{
    struct file_blk *blk;
    char *buffer;

    blk = filp->f_path.dentry->d_inode->i_private;

    buffer = (char *)&blk->data[0];
    buffer += *ppos;

    if (copy_from_user(buffer, buf, len)) {
        return -EFAULT;
    }

    *ppos += len;
    blk->file_size = *ppos;

    return len;
}

const struct file_operations tinyfs_file_operations = {
    .read = tinyfs_read,
    .write = tinyfs_write,
};

static const struct file_operations tinyfs_dir_operations = {
    .owner = THIS_MODULE,
    .read		= generic_read_dir,
    .iterate_shared	= tinyfs_readdir,
};

//create file
static int tinyfs_do_create(struct mnt_idmap *idmap, struct inode *dir, struct dentry *dentry, umode_t mode)
{
    struct inode *inode;
    struct super_block *sb;
    struct dir_entry *entry;
    struct file_blk *blk, *pblk;
    int idx;

    sb = dir->i_sb;

    if (curr_count >= MAX_FILES) {
        return -ENOSPC;
    }

    if (!S_ISDIR(mode) && !S_ISREG(mode)) {
        return -EINVAL;
    }

    inode = new_inode(sb);

    if (!inode) {
        return -ENOMEM;
    }

    inode->i_sb = sb;
    inode->i_op = &tinyfs_inode_ops;
    inode->i_atime = inode->i_mtime = inode_set_ctime_current(inode);

    idx = get_block(); //get free block save new file

    blk = &block[idx];
    inode->i_ino = idx;
    blk->mode = mode;
    curr_count ++;

    if (S_ISDIR(mode)) {
        blk->dir_children = 0;
        inode->i_fop = &tinyfs_dir_operations;
    } else if (S_ISREG(mode)) {
        blk->file_size = 0;
        inode->i_fop = &tinyfs_file_operations;
    }

    inode->i_private = blk;
    pblk = (struct file_blk *)dir->i_private;

    entry = (struct dir_entry *)&pblk->data[0];
    entry += pblk->dir_children;
    pblk->dir_children ++;

    entry->idx = idx;
    strcpy(entry->filename, dentry->d_name.name);

    inode_init_owner(&nop_mnt_idmap, inode, dir, mode);
    d_add(dentry, inode);

    return 0;
}


static int tinyfs_mkdir(struct mnt_idmap *idmap, struct inode *dir, struct dentry *dentry, umode_t mode)
{
    return tinyfs_do_create(&nop_mnt_idmap, dir, dentry, S_IFDIR | mode);
}

static int tinyfs_create(struct mnt_idmap *idmap, struct inode *dir, struct dentry *dentry, umode_t mode, bool excl)
{
    return tinyfs_do_create(&nop_mnt_idmap, dir, dentry, mode);
}

static struct inode * tinyfs_iget(struct super_block *sb, int idx)
{
    struct inode *inode;
    struct file_blk *blk;

    inode = new_inode(sb);
    inode->i_ino = idx;
    inode->i_sb = sb;
    inode->i_op = &tinyfs_inode_ops;

    blk = &block[idx];

    if (S_ISDIR(blk->mode))
        inode->i_fop = &tinyfs_dir_operations;
    else if (S_ISREG(blk->mode))
        inode->i_fop = &tinyfs_file_operations;

    inode->i_atime = inode->i_mtime  = inode_set_ctime_current(inode);
    inode->i_private = blk;

    return inode;
}

struct dentry *tinyfs_lookup(struct inode *parent_inode, struct dentry *child_dentry, unsigned int flags)
{
    struct super_block *sb = parent_inode->i_sb;
    struct file_blk *blk;
    struct dir_entry *entry;
    int i;

    blk = (struct file_blk *)parent_inode->i_private;
    entry = (struct dir_entry *)&blk->data[0];
    for (i = 0; i < blk->dir_children; i++) {
        if (!strcmp(entry[i].filename, child_dentry->d_name.name)) {
            struct inode *inode = tinyfs_iget(sb, entry[i].idx);
            struct file_blk *inner = (struct file_blk*)inode->i_private;
            inode_init_owner(&nop_mnt_idmap, inode, parent_inode, inner->mode);
            d_add(child_dentry, inode);
            return NULL;
        }
    }

    return NULL;
}

int tinyfs_rmdir(struct inode *dir, struct dentry *dentry)
{
    struct inode *inode = dentry->d_inode;
    struct file_blk *blk = (struct file_blk*) inode->i_private;

    blk->busy = 0;
    return simple_rmdir(dir, dentry);
}

int tinyfs_unlink(struct inode *dir, struct dentry *dentry)
{
    int i;
    struct inode *inode = dentry->d_inode;
    struct file_blk *blk = (struct file_blk *)inode->i_private;
    struct file_blk *pblk = (struct file_blk *)dir->i_private;
    struct dir_entry *entry;

    entry = (struct dir_entry*)&pblk->data[0];
    for (i = 0; i < pblk->dir_children; i++) {
        if (!strcmp(entry[i].filename, dentry->d_name.name)) {
            int j;
            for (j = i; j < pblk->dir_children - 1; j++) {
                memcpy(&entry[j], &entry[j+1], sizeof(struct dir_entry));
            }
            pblk->dir_children --;
            break;
        }
    }

    blk->busy = 0;
    return simple_unlink(dir, dentry);
}

static struct inode_operations tinyfs_inode_ops = {
    .create = tinyfs_create,
    .lookup = tinyfs_lookup,
    .mkdir = tinyfs_mkdir,
    .rmdir = tinyfs_rmdir,
    .unlink = tinyfs_unlink,
};


int tinyfs_fill_super(struct super_block *sb, void *data, int silent)
{
    struct inode *root_inode;
    umode_t mode = S_IFDIR;

    root_inode = new_inode(sb);
    root_inode->i_ino = 1;
    inode_init_owner(&nop_mnt_idmap, root_inode, NULL, mode);
    root_inode->i_sb = sb;
    root_inode->i_op = &tinyfs_inode_ops;
    root_inode->i_fop = &tinyfs_dir_operations;
    root_inode->i_atime = root_inode->i_mtime = inode_set_ctime_current(root_inode);

    block[1].mode = mode;
    block[1].dir_children = 0;
    block[1].idx = 1;
    block[1].busy = 1;
    root_inode->i_private = &block[1];

    sb->s_root = d_make_root(root_inode);
    curr_count ++;

    return 0;
}

static struct dentry *tinyfs_mount(struct file_system_type *fs_type, int flags, const char* dev_name, void *data)
{
    return mount_nodev(fs_type, flags, data, tinyfs_fill_super);
}

static void tinyfs_kill_superblock(struct super_block *sb)
{
    kill_anon_super(sb);
}

struct file_system_type tinyfs_fs_type = {
    .owner = THIS_MODULE,
    .name = "tinyfs",
    .mount = tinyfs_mount,
    .kill_sb = tinyfs_kill_superblock,
};

static int tinyfs_init(void)
{
    int ret;

    memset(block, 0, sizeof(block));
    ret = register_filesystem(&tinyfs_fs_type);

    if (ret)
        pr_info("register tinyfs filesystem failed\n");

    return ret;
}

static void tinyfs_exit(void)
{
    unregister_filesystem(&tinyfs_fs_type);
}

module_init(tinyfs_init);
module_exit(tinyfs_exit);

MODULE_LICENSE("GPL");

三、加载ko后的演示及发现的问题

1.加载ko,挂载文件系统

/driver # insmod tinyfs.ko
/driver # mkdir tinydir
/driver # mount -t tinyfs none tinydir/

2、发现的问题,创建多个文件时,ls -l文件属性显示异常

3、创建长名字的文件ls -al 显示怪异,不符合预期

/driver/tinydir # touch 1.txt
/driver/tinydir # ls
1.txt
/driver/tinydir # touch 2.txt
/driver/tinydir # ls
1.txt  2.txt
/driver/tinydir # touch 45666.txt
/driver/tinydir # ls
1.txt      2.txt?     45666.txt
/driver/tinydir # ls -al
total 4
d---------    1 0        0                0 Dec  9 13:12 .
drwxr-xr-x    3 0        0             4096 Dec  9 12:46 ..
-rw-r--r--    1 0        0                0 Dec  9 13:12 1.txt
?r--rwSr--    1 0        0                0 Dec  9 13:12 2.txt?
-rw-r--r--    1 0        0                0 Dec  9 13:12 45666.txt
/driver/tinydir # touch 34455.txt
/driver/tinydir # ls
1.txt               34455.txt
2.txt?              45666.txt34455.txt

最后创建一个34455.txt,最后显示的部分都和45666.txt连到一起了

4、问题分析

还是debug,不清楚怎么debug ko的可以参考我之前的文章:

qemu 单步调试linux driver-CSDN博客

打断点在tinyfs_readdir 函数中,发现问题(idx应该是block的索引,正常按创建文件的顺序一次增加,从2~32, 但是实际上这里看到都已经越界覆盖了)

原因:struct file_blk 结构体中, 字段char data[0] 是当一个指针在使用,但是又没有分配空间,无论在文件内容存储,还是存储dir的filename信息都会向后覆盖block,导致后面存放的文件属性block 被踩坏

文件名字异常的的bug原因:

结构体定义:

struct dir_entry {

char filename[MAXLEN]; //maxlen为8, 创建文件名时不能超过;

uint8_t idx;

};

使用的地方在

tinyfs_do_create

-->strcpy(entry->filename, dentry->d_name.name);//换成strncpy就好了

四、最终修改版本

针对前面的struct file_blk中data[0]的问题有两种方式,一种是在使用前分配,删除时释放,另外一种就是我现在用的增加一个结构体数组,做了一些长度限制(一个目录下只能存在4个文件或目录项)。

tinyfs.h

#define MAXLEN 8
#define MAX_FILES 32
#define MAX_BLOCKSIZE 512
#define MAX_SUBDIR_FILES 4  //one dir can include max file size

//define the dir node info
struct dir_entry {
    char filename[MAXLEN];
    uint8_t idx;
};

//just same count char as dir node size
#define FILE_BUF_SIZ  (sizeof(struct dir_entry) * MAX_SUBDIR_FILES)

//define the file block
struct file_blk {
    uint8_t busy;
    mode_t mode;
    uint8_t idx;

    union {
        uint8_t file_size;
        uint8_t dir_children;
    };

    union {//用作目录时,最大记录的条目数,不超过4个; 用作文件时,这里就是文件的buffer大小
        struct dir_entry dir_data[MAX_SUBDIR_FILES]; //one dir can exit 4 file
        char file_data[FILE_BUF_SIZ];
    };
};

tinyfs.c

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/uaccess.h>

#include "tinyfs.h"

#define tinyfs_dbg pr_debug
#define tinyfs_err pr_err

struct file_blk block[MAX_FILES+1];

int curr_count = 0;

static int get_block(void)
{
    int i;
    for( i = 2; i < MAX_FILES; i++) {
        if (!block[i].busy) {
            block[i].busy = 1;
            return i;
        }
    }

    return -1;
}

static struct inode_operations tinyfs_inode_ops;

/*
 * read the entries from a directory
 */
static int tinyfs_readdir(struct file *filp, struct dir_context *ctx)
{

    loff_t pos;
    struct file_blk  *blk;
    struct dir_entry *entry;
    struct dentry *dentry = filp->f_path.dentry;

    int i;

    if (!dir_emit_dots(filp, ctx))
        return 0;

    pos = filp->f_pos;

    if (pos)
        return 0;

    blk = (struct file_blk*)dentry->d_inode->i_private;

    if (!S_ISDIR(blk->mode)) {
        return -ENOTDIR;
    }

    //loop get one dir included file name
    entry = (struct dir_entry *)blk->dir_data;
    for (i = 0; i < blk->dir_children; i++) {
        if (!dir_emit(ctx, entry[i].filename, strlen(entry[i].filename),
                    entry[i].idx,
                    DT_UNKNOWN))
            break;
        filp->f_pos += sizeof(struct dir_entry);
        pos += sizeof(struct dir_entry);
    }

    return 0;
}

ssize_t tinyfs_read(struct file *filp, char __user *buf, size_t len, loff_t *ppos)
{
    struct file_blk *blk;
    char *buffer;

    blk = (struct file_blk*)filp->f_path.dentry->d_inode->i_private;

    if (*ppos >= blk->file_size)
        return 0;

    buffer = (char *)blk->file_data;
    len = min((size_t)blk->file_size, len);

    if (copy_to_user(buf, buffer, len)) {
        return -EFAULT;
    }
    *ppos += len;

    return len;
}

ssize_t tinyfs_write(struct file *filp, const char __user *buf, size_t len, loff_t *ppos)
{
    struct file_blk *blk;
    char *buffer;

    blk = filp->f_path.dentry->d_inode->i_private;

    buffer = (char *)blk->file_data;
    buffer += *ppos;

    if (copy_from_user(buffer, buf, len)) {
        return -EFAULT;
    }

    *ppos += len;
    blk->file_size = *ppos;

    return len;
}

const struct file_operations tinyfs_file_operations = {
    .read = tinyfs_read,
    .write = tinyfs_write,
};

static const struct file_operations tinyfs_dir_operations = {
    .owner = THIS_MODULE,
    .read = generic_read_dir,
    .iterate_shared	= tinyfs_readdir,
};

static int tinyfs_do_create(struct mnt_idmap *idmap, struct inode *dir, struct dentry *dentry, umode_t mode)
{
    struct inode *inode;
    struct super_block *sb;
    struct dir_entry *entry;
    struct file_blk *blk, *pblk;
    int idx;

    sb = dir->i_sb;

    if (curr_count >= MAX_FILES) {
        return -ENOSPC;
    }

    if (!S_ISDIR(mode) && !S_ISREG(mode)) {
        return -EINVAL;
    }

    pblk = (struct file_blk *)dir->i_private;

    //dir include file|dir count can't over MAX_SUBDIR_FILES
    if (pblk->dir_children >= MAX_SUBDIR_FILES) {
        tinyfs_err("one dir max include file cnt should less than %d\n", MAX_SUBDIR_FILES);
        return -ENOSPC;
    }

    inode = new_inode(sb);

    if (!inode) {
        return -ENOMEM;
    }

    inode->i_sb = sb;
    inode->i_op = &tinyfs_inode_ops;
    inode->i_atime = inode->i_mtime = inode_set_ctime_current(inode);

    idx = get_block(); //get free block save new file

    blk = &block[idx];
    inode->i_ino = idx;
    blk->mode = mode;
    curr_count ++;

    if (S_ISDIR(mode)) {
        blk->dir_children = 0;
        inode->i_fop = &tinyfs_dir_operations;
    } else if (S_ISREG(mode)) {
        blk->file_size = 0;
        inode->i_fop = &tinyfs_file_operations;
    }

    inode->i_private = blk;

    //need check the dir inode is what?
    tinyfs_dbg("%s dir inode %llx", __func__, dir);

    entry = &pblk->dir_data[pblk->dir_children];
    pblk->dir_children ++;

    entry->idx = idx;
    strncpy(entry->filename, dentry->d_name.name, MAXLEN-1);
    entry->filename[MAXLEN-1] = 0;

    inode_init_owner(&nop_mnt_idmap, inode, dir, mode);
    d_add(dentry, inode);

    return 0;
}

static int tinyfs_mkdir(struct mnt_idmap *idmap, struct inode *dir, struct dentry *dentry, umode_t mode)
{
    return tinyfs_do_create(&nop_mnt_idmap, dir, dentry, S_IFDIR | mode);
}

static int tinyfs_create(struct mnt_idmap *idmap, struct inode *dir, struct dentry *dentry, umode_t mode, bool excl)
{
    return tinyfs_do_create(&nop_mnt_idmap, dir, dentry, mode);
}

static struct inode * tinyfs_iget(struct super_block *sb, int idx)
{
    struct inode *inode;
    struct file_blk *blk;

    inode = new_inode(sb);
    inode->i_ino = idx;
    inode->i_sb = sb;
    inode->i_op = &tinyfs_inode_ops;

    blk = &block[idx];

    if (S_ISDIR(blk->mode))
        inode->i_fop = &tinyfs_dir_operations;
    else if (S_ISREG(blk->mode))
        inode->i_fop = &tinyfs_file_operations;

    inode->i_atime = inode->i_mtime = inode_set_ctime_current(inode);
    inode->i_private = blk;

    return inode;
}

struct dentry *tinyfs_lookup(struct inode *parent_inode, struct dentry *child_dentry, unsigned int flags)
{
    struct super_block *sb = parent_inode->i_sb;
    struct file_blk *blk;
    struct dir_entry *entry;
    int i;

    blk = (struct file_blk *)parent_inode->i_private;
    entry = (struct dir_entry *)blk->dir_data;
    for (i = 0; i < blk->dir_children; i++) {
        if (!strcmp(entry[i].filename, child_dentry->d_name.name)) {
            struct inode *inode = tinyfs_iget(sb, entry[i].idx);
            struct file_blk *inner = (struct file_blk*)inode->i_private;
            inode_init_owner(&nop_mnt_idmap, inode, parent_inode, inner->mode);
            d_add(child_dentry, inode);
            return NULL;
        }
    }

    return NULL;
}

int tinyfs_rmdir(struct inode *dir, struct dentry *dentry)
{
    struct inode *inode = dentry->d_inode;
    struct file_blk *blk = (struct file_blk*) inode->i_private;

    blk->busy = 0;
    return simple_rmdir(dir, dentry);
}

int tinyfs_unlink(struct inode *dir, struct dentry *dentry)
{
    int i;
    struct inode *inode = dentry->d_inode;
    struct file_blk *blk = (struct file_blk *)inode->i_private;
    struct file_blk *pblk = (struct file_blk *)dir->i_private;
    struct dir_entry *entry;

    entry = (struct dir_entry*)pblk->dir_data;
    for (i = 0; i < pblk->dir_children; i++) {
        if (!strcmp(entry[i].filename, dentry->d_name.name)) {
            int j;
            for (j = i; j < pblk->dir_children - 1; j++) {
                memcpy(&entry[j], &entry[j+1], sizeof(struct dir_entry));
            }
            pblk->dir_children --;
            break;
        }
    }

    blk->busy = 0;
    return simple_unlink(dir, dentry);
}

static struct inode_operations tinyfs_inode_ops = {
    .create = tinyfs_create,
    .lookup = tinyfs_lookup,
    .mkdir = tinyfs_mkdir,
    .rmdir = tinyfs_rmdir,
    .unlink = tinyfs_unlink,
};

int tinyfs_fill_super(struct super_block *sb, void *data, int silent)
{
    struct inode *root_inode;
    umode_t mode = S_IFDIR;

    root_inode = new_inode(sb);
    root_inode->i_ino = 1;
    inode_init_owner(&nop_mnt_idmap, root_inode, NULL, mode);
    root_inode->i_sb = sb;
    root_inode->i_op = &tinyfs_inode_ops;
    root_inode->i_fop = &tinyfs_dir_operations;
    root_inode->i_atime = root_inode->i_mtime = inode_set_ctime_current(root_inode);

    block[1].mode = mode;
    block[1].dir_children = 0;
    block[1].idx = 1;
    block[1].busy = 1;
    root_inode->i_private = &block[1];

    tinyfs_dbg("%s root inode %llx", __func__, root_inode);
    sb->s_root = d_make_root(root_inode);
    curr_count ++;

    return 0;
}

static struct dentry *tinyfs_mount(struct file_system_type *fs_type, int flags, const char* dev_name, void *data)
{
    return mount_nodev(fs_type, flags, data, tinyfs_fill_super);
}

static void tinyfs_kill_superblock(struct super_block *sb)
{
    kill_anon_super(sb);
}

struct file_system_type tinyfs_fs_type = {
    .owner = THIS_MODULE,
    .name = "tinyfs",
    .mount = tinyfs_mount,
    .kill_sb = tinyfs_kill_superblock,
};

static int tinyfs_init(void)
{
    int ret;

    memset(block, 0, sizeof(block));
    ret = register_filesystem(&tinyfs_fs_type);

    if (ret)
        pr_info("register tinyfs filesystem failed\n");

    return ret;
}

static void tinyfs_exit(void)
{
    unregister_filesystem(&tinyfs_fs_type);
}

module_init(tinyfs_init);
module_exit(tinyfs_exit);

MODULE_LICENSE("GPL");

执行效果

/driver/tinydir # touch 1.txt
/driver/tinydir # touch 2.txt
/driver/tinydir # mkdir tinysub
/driver/tinydir # touch 4.txt
/driver/tinydir # ls
1.txt  2.txt  tinysub  4.txt
/driver/tinydir # touch 5.txt  当超出数组长度限制时,提示
[   76.696988] one dir max include file cnt should less than 4   
touch: 5.txt: No space left on device
/driver/tinydir # ls
1.txt  2.txt  tinysub  4.txt
/driver/tinydir # cd tinysub/
/driver/tinydir/tinysub # touch 5.txt
/driver/tinydir/tinysub # cd ..
/driver/tinydir # tree .
.
├── 1.txt
├── 2.txt
├── 4.txt
└── tinysub
    └── 5.txt

上面实验的最小文件系统的结构图,dir和file都是存在block结构体中:

当然这个文件系统还有很多bug,比如文件写入内容的大小控制,删除文件再创建文件的处理(前面tinyfs_do_create下处理entry有点小问题)... ,这些留给感兴趣的同学自己分析处理(主要是我要溜娃了)。

参考:

300来行代码带你实现一个能跑的最小Linux文件系统

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值