http://blog.chinaunix.net/uid-20196318-id-28822.html
fuse提供两种类型的操作接口,fuse lowlevel operations类似于内核文件系统的接口,以inode号作为访问的关键字,而更高级的fuse operations则提供以路径名作为关键字的访问形式,这样即使fuse用户对内核文件系统并不了解,也能编写文件系统程序。
高级的接口,其实是对fuse lowlevel operations的一个封装(相当于实现了一个用户空间文件系统fuse_fs,但其数据都放在内存中),它实现了一组lowlevel operations,并通过hash表来组织目录项,实现inode关键字向路径名关键字的转换(逆向路径名查找,索引节点中需要记录父节点的nodeid),封装的实现在fuse源代码包中fuse.c中实现。
2011年11月23日补充:
以访问/a/b/c为例,在路径名解析的过程中(根node在初始化时加载),能得到各级目录项的nodeid(递增)及node信息,并将这些信息加入node哈希表及名字哈希表,再根据c的nodeid访问c时(fuse_lowlevel_ops),可以反向解析出c的绝对路径,然后将c的绝对路径传递给外部实现的接口(fuse_operations),从而实现低级接口到高级接口的转换。
两个主要的数据结构
/* fuse fs 全局信息 */ |
与路径名查找(fuse_lib_lookup)实现相关的一些函数
/* fuse fs的hash函数 */ |
创建fuse_fs环境的函数
struct fuse *fuse_new(struct fuse_chan *ch, struct fuse_args *args,
const struct fuse_operations *op, size_t op_size,
void *user_data)
{
return fuse_new_common(ch, args, op, op_size, user_data, 0);
}
/* 创建fuse文件系统的全局环境的内部接口 */
struct fuse *fuse_new_common(struct fuse_chan *ch, struct fuse_args *args,
const struct fuse_operations *op,
size_t op_size, void *user_data, int compat)
{
struct fuse *f;
struct node *root;
struct fuse_fs *fs;
struct fuse_lowlevel_ops llop = fuse_path_ops;
if (fuse_create_context_key() == -1)
goto out;
f = (struct fuse *) calloc(1, sizeof(struct fuse));
if (f == NULL) {
fprintf(stderr, "fuse: failed to allocate fuse object\n");
goto out_delete_context_key;
}
fs = fuse_fs_new(op, op_size, user_data); /* 申请fuse fs的空间 */
if (!fs)
goto out_free;
fs->compat = compat;
f->fs = fs;
f->nullpath_ok = fs->op.flag_nullpath_ok;
/* Oh f**k, this is ugly! */
if (!fs->op.lock) {
llop.getlk = NULL;
llop.setlk = NULL;
}
f->conf.entry_timeout = 1.0;
f->conf.attr_timeout = 1.0;
f->conf.negative_timeout = 0.0;
f->conf.intr_signal = FUSE_DEFAULT_INTR_SIGNAL;
if (fuse_opt_parse(args, &f->conf, fuse_lib_opts,
fuse_lib_opt_proc) == -1)
goto out_free_fs;
if (f->conf.modules) {
char *module;
char *next;
for (module = f->conf.modules; module; module = next) {
char *p;
for (p = module; *p && *p != ':'; p++);
next = *p ? p + 1 : NULL;
*p = '\0';
if (module[0] &&
fuse_push_module(f, module, args) == -1)
goto out_free_fs;
}
}
if (!f->conf.ac_attr_timeout_set)
f->conf.ac_attr_timeout = f->conf.attr_timeout;
#ifdef __FreeBSD__
/*
* In FreeBSD, we always use these settings as inode numbers
* are needed to make getcwd(3) work.
*/
f->conf.readdir_ino = 1;
#endif
if (compat && compat <= 25) {
if (fuse_sync_compat_args(args) == -1)
goto out_free_fs;
}
/* 注册fuse fs的llop到lowlevel ops */
f->se = fuse_lowlevel_new_common(args, &llop, sizeof(llop), f);
if (f->se == NULL) {
if (f->conf.help)
fuse_lib_help_modules();
goto out_free_fs;
}
fuse_session_add_chan(f->se, ch);
if (f->conf.debug)
fprintf(stderr, "nullpath_ok: %i\n", f->nullpath_ok);
/* Trace topmost layer by default */
f->fs->debug = f->conf.debug;
f->ctr = 0;
f->generation = 0;
/* hash表长度,空间分配 */
/* FIXME: Dynamic hash table */
f->name_table_size = 14057;
f->name_table = (struct node **)
calloc(1, sizeof(struct node *) * f->name_table_size);
if (f->name_table == NULL) {
fprintf(stderr, "fuse: memory allocation failed\n");
goto out_free_session;
}
f->id_table_size = 14057;
f->id_table = (struct node **)
calloc(1, sizeof(struct node *) * f->id_table_size);
if (f->id_table == NULL) {
fprintf(stderr, "fuse: memory allocation failed\n");
goto out_free_name_table;
}
fuse_mutex_init(&f->lock);
/* 根节点的信息加入hash表,因不会根据名字查找根,故根只加入nodeid hash表
在find_node中,如果name为空,则返回父目录的信息 */
root = (struct node *) calloc(1, sizeof(struct node));
if (root == NULL) {
fprintf(stderr, "fuse: memory allocation failed\n");
goto out_free_id_table;
}
root->name = strdup("/");
if (root->name == NULL) {
fprintf(stderr, "fuse: memory allocation failed\n");
goto out_free_root;
}
if (f->conf.intr &&
fuse_init_intr_signal(f->conf.intr_signal,
&f->intr_installed) == -1)
goto out_free_root_name;
root->parent = NULL;
root->nodeid = FUSE_ROOT_ID;
root->generation = 0;
root->refctr = 1;
root->nlookup = 1;
hash_id(f, root);
return f;
out_free_root_name:
free(root->name);
out_free_root:
free(root);
out_free_id_table:
free(f->id_table);
out_free_name_table:
free(f->name_table);
out_free_session:
fuse_session_destroy(f->se);
out_free_fs:
/* Horrible compatibility hack to stop the destructor from being
called on the filesystem without init being called first */
fs->op.destroy = NULL;
fuse_fs_destroy(f->fs);
free(f->conf.modules);
out_free:
free(f);
out_delete_context_key:
fuse_delete_context_key();
out:
return NULL;
}
struct fuse_fs *fuse_fs_new(const struct fuse_operations *op, size_t op_size,
void *user_data)
{
struct fuse_fs *fs;
if (sizeof(struct fuse_operations) < op_size) {
fprintf(stderr, "fuse: warning: library too old, some operations may not not work\n");
op_size = sizeof(struct fuse_operations);
}
fs = (struct fuse_fs *) calloc(1, sizeof(struct fuse_fs));
if (!fs) {
fprintf(stderr, "fuse: failed to allocate fuse_fs object\n");
return NULL;
}
fs->user_data = user_data;
if (op)
memcpy(&fs->op, op, op_size);
return fs;
}