「转」300行代码实现Linux文件系统

缘起

从微信公众号上转载一篇有趣的文章: 300来行代码带你实现一个能跑的最小Linux文件系统。 通过这边文章,可以实例演练一下上一篇编译加载Linux内核模块, 进而加深对Linux内核模块的理解。


代码

由于微信公众号上代码排版有些奇特,文章中的代码整理如下(CentOS下可编译运行):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// tinyfs.h
#define MAXLINE 8
#define MAX_FILES 32
#define MAX_BLOCKSIZE 512

// 定义每一个目录项的格式
struct dir_entry {
char filename[MAXLINE];
uint8_t idx;
};

// 定义每一个文件的格式。
struct file_blk {
uint8_t busy;
mode_t mode;
uint8_t idx;

union {
uint8_t file_size;
uint8_t dir_children;
};
char data[0];
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
// 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, void *dirent, filldir_t filldir)
{
loff_t pos;
struct file_blk *blk;
struct dir_entry *entry;
int i;

pos = filp->f_pos;
if (pos)
return 0;

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

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

// 循环获取一个目录的所有文件的文件名
entry = (struct dir_entry *)&blk->data[0];
for (i = 0; i < blk->dir_children; i++) {
filldir(dirent, entry[i].filename, MAXLINE, pos, entry[i].idx, DT_UNKNOWN);
filp->f_pos += sizeof(struct dir_entry);
pos += sizeof(struct dir_entry);
}

return 0;
}

// read实现
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;
}

// write实现
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,
};

const struct file_operations tinyfs_dir_operations = {
.owner = THIS_MODULE,
.readdir = tinyfs_readdir,
};

// 创建文件的实现
static int tinyfs_do_create(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->i_ctime = CURRENT_TIME;

idx = get_block(); // 获取一个空闲的文件块保存新文件
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);

// VFS穿针引线的关键步骤,将VFS的inode链接到链表
inode_init_owner(inode, dir, mode);
d_add(dentry, inode);
return 0;
}

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

static int tinyfs_create(struct inode *dir, struct dentry *dentry, umode_t mode, bool excl)
{
return tinyfs_do_create(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->i_ctime = CURRENT_TIME;
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(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;
int mode = S_IFDIR;
root_inode = new_inode(sb);
root_inode->i_ino = 1;
inode_init_owner(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 = root_inode->i_ctime = CURRENT_TIME;

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)
printk("register tinyfs 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");
1
2
3
4
5
6
7
8
9
# Makefile
# CONFIG_MODULE_SIG=n

obj-m += tinyfs.o
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
all:
make -C $(KERNELDIR) M=$(PWD) modules
clean:
make -C $(KERNELDIR) M=$(PWD) clean

编译&运行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
# ls
Makefile tinyfs.c tinyfs.h
#
# make
#
# ls
Makefile modules.order Module.symvers tinyfs.c tinyfs.h tinyfs.ko tinyfs.mod.c tinyfs.mod.o tinyfs.o
# # 加载内核模块: 文件系统ko, tinyfs.ko
# insmod tinyfs.ko
#
# # 挂在文件系统 tinyfs 到 /mnt 目录下
# ls /mnt/
#
# mount -t tinyfs none /mnt
#
# ls /mnt/
#
# cd /mnt
#
# echo 11111 > ./a
#
# cat ./a
11111
#
# mkdir dir1
# echo 22222 > ./dir1/b
#
# cat ./dir1/b
22222
#
# tree
.
├── a
└── dir1
└── b

1 directory, 2 files
#
# echo 333 > ./dir1/c
#
# cat ./dir1/c
333
#
# tree
.
├── a
└── dir1
├── b
└── c

1 directory, 3 files
#
# rm -f ./dir1/c
#
# tree
.
├── a
└── dir1
└── b

1 directory, 2 files
#
# rm -rf dir1/
#
# tree
.
├── a
└── dir1

1 directory, 1 file
#
# rm -rf dir1/
#
# # 因为我们没有实现删除目录的接口,因此目录无法删除。
# tree
.
├── a
└── dir1

1 directory, 1 file
#
#
#
# # 卸载文件系统 tinyfs文件系统
# cd ~
# umount -f /mnt/
#
# # 卸载tinyfs内核模块
# rmmod tinyfs
#
#