在 《Linux下使用fuse编写文件系统(1)、(2)》中,我们在Linux环境上使用fuse编写了oufs文件系统,支持列表、创建和删除文件操作,接下来在该基础上增加读写文件操作:#define FUSE_USE_VERSION 26#include<stdio.h>#include<string.h>#include<errno.h>#include<fuse.h>#define BUFFSIZE 8192static int content_size;static char content[BUFFSIZE];static const char* fname = "hello-world";staticintou_readdir(constchar* path, void* buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info* fi){ return filler(buf, fname, NULL, 0);}staticintou_getattr(constchar* path, struct stat* st){ memset(st, 0, sizeof(struct stat)); if (strcmp(path, "/") == 0) { st->st_mode = 0755 | S_IFDIR; st->st_size = strlen(fname); } else { st->st_mode = 0644 | S_IFREG; st->st_size = content_size; } return 0;}staticintou_read(constchar* path, char* buf, size_t bytes, off_t offset, struct fuse_file_info* fi){ size_t available; if (strcmp(path + 1, fname) != 0) return -ENOENT; if (offset >= content_size) return 0; available = content_size - offset; if (available < bytes) bytes = available; memcpy(buf, content + offset, bytes); return bytes;}staticintou_write(constchar* path, constchar* buf, size_t bytes, off_t offset, struct fuse_file_info* fi){ size_t new_size; if (strcmp(path + 1, fname) != 0) return -ENOENT; new_size = offset + bytes; if (new_size > BUFFSIZE) return -EFBIG; memcpy(content + offset, buf, bytes); if (content_size < new_size) content_size = new_size; return bytes;}staticintou_truncate(constchar* path, off_t size){ if (size > BUFFSIZE) return -EFBIG; if (content_size < size) memset(content + content_size, 0, size - content_size); content_size = size; return 0;}static struct fuse_operations oufs_ops = { .readdir = ou_readdir, .getattr = ou_getattr, .read = ou_read, .write = ou_write, .truncate = ou_truncate,};intmain(int argc, char* argv[]){ return fuse_main(argc, argv, &oufs_ops, NULL);}
我们使用一个全局缓冲区content用来记录hello-world的内容,content_size用来记录缓冲区长度。函数ou_read()和ou_write()分别用来读写hello-world的内容,ou_truncate() 用来改变文件大小。这些函数的功能不具体描述了,manpage 里面说得很详细。
接下来,我们再新增创建、删除目录功能:
#define FUSE_USE_VERSION 26#include<stdio.h>#include<stdlib.h>#include<string.h>#include<errno.h>#include<fuse.h>#include"list.h"#define MAX_NAMELEN 255struct ou_entry { mode_t mode; struct list_node node; char name[MAX_NAMELEN + 1];};static struct list_node entries;staticintou_readdir(constchar* path, void* buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info* fi){ struct list_node* n; if (strcmp(path, "/") != 0) return -EINVAL; filler(buf, ".", NULL, 0); filler(buf, "..", NULL, 0); list_for_each (n, &entries) { struct ou_entry* o = list_entry(n, struct ou_entry, node); filler(buf, o->name, NULL, 0); } return 0;}staticintou_getattr(constchar* path, struct stat* st){ struct list_node* n; memset(st, 0, sizeof(struct stat)); if (strcmp(path, "/") == 0) { st->st_mode = 0755 | S_IFDIR; st->st_nlink = 2; st->st_size = 0; list_for_each (n, &entries) { struct ou_entry* o = list_entry(n, struct ou_entry, node); ++st->st_nlink; st->st_size += strlen(o->name); } return 0; } list_for_each (n, &entries) { struct ou_entry* o = list_entry(n, struct ou_entry, node); if (strcmp(path + 1, o->name) == 0) { st->st_mode = o->mode; st->st_nlink = 2; return 0; } } return -ENOENT;}staticintou_mkdir(constchar* path, mode_t mode){ struct ou_entry* o; struct list_node* n; if (strlen(path + 1) > MAX_NAMELEN) return -ENAMETOOLONG; list_for_each (n, &entries) { o = list_entry(n, struct ou_entry, node); if (strcmp(path + 1, o->name) == 0) return -EEXIST; } o = malloc(sizeof(struct ou_entry)); strcpy(o->name, path + 1); /* skip the leading '/' */ o->mode = mode | S_IFDIR; list_add_prev(&o->node, &entries); return 0;}staticintou_rmdir(constchar* path){ struct list_node *n, *p; if (strcmp(path, "/") != 0) return -EINVAL; list_for_each_safe (n, p, &entries) { struct ou_entry* o = list_entry(n, struct ou_entry, node); if (strcmp(path + 1, o->name) == 0) { __list_del(n); free(o); return 0; } } return -ENOENT;}static struct fuse_operations oufs_ops = { .getattr = ou_getattr, .readdir = ou_readdir, .mkdir = ou_mkdir, .rmdir = ou_rmdir,};intmain(int argc, char* argv[]){ list_init(&entries); return fuse_main(argc, argv, &oufs_ops, NULL);}
为了防止对新建的目录执行ls得到错误的结果,程序里把所有函数的执行目录都限定在根目录下。和创建/删除文件的程序比较一下,除了把create()/unlink()分别替换成mkdir()/rmdir()外其它内容基本没有变化,使用的还是同样的数据结构。在linux vfs的实现中目录的确是被作为一个特殊的文件对待,只是普通文件使用read()/write()访问,而目录使用readdir()/mkdir()访问。