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 全局信息 */
struct fuse {
    struct fuse_session *se;
    struct node **name_table; /* 路径名hash表 */
    size_t name_table_size; /* 路径名hash表的长度 */
    struct node **id_table; /* nodeid hash表 */
    size_t id_table_size; /* nodeid hash表的长度 */
    fuse_ino_t ctr;
    unsigned int generation;
    unsigned int hidectr;
    pthread_mutex_t lock;
    struct fuse_config conf;
    int intr_installed;
    struct fuse_fs *fs; /* 文件系统ops信息 */
    int nullpath_ok;
    int curr_ticket;
    struct lock_queue_element *lockq;
};

/* 每个目录项的信息 */
struct node {
    struct node *name_next; /* 名字hash链表 */
    struct node *id_next; /* 索引节点hash链表 */
    fuse_ino_t nodeid; /* 索引节点号 */
    unsigned int generation; /* 用于扩展节点数,inode数可到2^64 */
    int refctr;
    struct node *parent; /* 父目录项信息 */
    char *name; /* 本目录项的名称 */
    uint64_t nlookup;
    int open_count;
    struct timespec stat_updated;
    struct timespec mtime;
    off_t size;
    struct lock *locks;
    unsigned int is_hidden : 1;
    unsigned int cache_valid : 1;
    int treelock;
    int ticket;
};

与路径名查找(fuse_lib_lookup)实现相关的一些函数

/* fuse fs的hash函数 */
static unsigned int name_hash(struct fuse *f, fuse_ino_t parent,
             const char *name)
{
    unsigned int hash = *name;

    if (hash)
        for (name += 1; *name != '\0'; name++)
            hash = (hash << 5) - hash + *name;

    return (hash + parent) % f->name_table_size;
}

/* 通过名字hash查找node信息*/
static struct node *lookup_node(struct fuse *f, fuse_ino_t parent,
                const char *name)
{
    size_t hash = name_hash(f, parent, name);
    struct node *node;

    for (node = f->name_table[hash]; node != NULL; node = node->name_next)
        if (node->parent->nodeid == parent &&
         strcmp(node->name, name) == 0)
            return node;

    return NULL;
}

/* 通过nodeid查找node信息*/
static struct node *get_node_nocheck(struct fuse *f, fuse_ino_t nodeid)
{
    size_t hash = nodeid % f->id_table_size;
    struct node *node;

    for (node = f->id_table[hash]; node != NULL; node = node->id_next)
        if (node->nodeid == nodeid)
            return node;

    return NULL;
}

/* 通过某一个目录项,你想递归获取全局路径 */
static int try_get_path(struct fuse *f, fuse_ino_t nodeid, const char *name,
            char **path, struct node **wnodep, int ticket)
{
    unsigned bufsize = 256;
    char *buf;
    char *s;
    struct node *node;
    struct node *wnode = NULL;
    int err;

    *path = NULL;

    buf = malloc(bufsize);
    if (buf == NULL)
        return -ENOMEM;

    s = buf + bufsize - 1;
    *s = '\0';

    if (name != NULL) {
        s = add_name(&buf, &bufsize, s, name);
        err = -ENOMEM;
        if (s == NULL)
            goto out_free;
    }

    if (wnodep) {
        assert(ticket);
        wnode = lookup_node(f, nodeid, name);
        if (wnode) {
            if (wnode->treelock != 0 ||
             (wnode->ticket && wnode->ticket != ticket)) {
                if (!wnode->ticket)
                    wnode->ticket = ticket;
                err = -EAGAIN;
                goto out_free;
            }
            wnode->treelock = -1;
            wnode->ticket = 0;
        }
    }

    /* 逆向遍历目录树,获取目录项的全局路径 */
    err = 0;
    for (node = get_node(f, nodeid); node->nodeid != FUSE_ROOT_ID;
     node = node->parent) {
        err = -ENOENT;
        if (node->name == NULL || node->parent == NULL)
            goto out_unlock;

        err = -ENOMEM;
         /* 长度不够将自动扩张,默认申请长度为256 */
        s = add_name(&buf, &bufsize, s, node->name);
        if (s == NULL)
            goto out_unlock;

        if (ticket) {
            err = -EAGAIN;
            if (node->treelock == -1 ||
             (node->ticket && node->ticket != ticket))
                goto out_unlock;

            node->treelock++;
            node->ticket = 0;
        }
    }

    if (s[0])
        memmove(buf, s, bufsize - (s - buf));
    else
        strcpy(buf, "/");

    *path = buf;
    if (wnodep)
        *wnodep = wnode;

    return 0;

 out_unlock:
    if (ticket)
        unlock_path(f, nodeid, wnode, node, ticket);
 out_free:
    free(buf);

    return err;
}

/* 获取下一个nodeid */
static fuse_ino_t next_id(struct fuse *f)
{
    do {
        f->ctr = (f->ctr + 1) & 0xffffffff;
        if (!f->ctr)
            f->generation ++; /* 如果32bit数以用完,将generation加1 */
    } while (f->ctr == 0 || f->ctr == FUSE_UNKNOWN_INO ||
         get_node_nocheck(f, f->ctr) != NULL);
    return f->ctr;
}

/* 获取目录项的fuse_entry_param信息,如果目录项不存在则新建,并加入hash表
lookup、mknod、create等都调用了该方法 */

static int lookup_path(struct fuse *f, fuse_ino_t nodeid,
         const char *name, const char *path,
         struct fuse_entry_param *e, struct fuse_file_info *fi)
{
    int res;

    memset(e, 0, sizeof(struct fuse_entry_param));
    /* 从这里可以看出 如果getattr接口返回错误,则整个过程将会出现问题,故在实现getattr时,即使获取不到文件的信息,也要填充entry的信息 */
    if (fi)
        res = fuse_fs_fgetattr(f->fs, path, &e->attr, fi);
    else
        res = fuse_fs_getattr(f->fs, path, &e->attr);
    if (res == 0) {
        struct node *node;

        node = find_node(f, nodeid, name);
        if (node == NULL)
            res = -ENOMEM;
        else {
            /* 如果分配子目录项不成功,则返回父目录的信息 */
            e->ino = node->nodeid;
            e->generation = node->generation;
            e->entry_timeout = f->conf.entry_timeout;
            e->attr_timeout = f->conf.attr_timeout;
            if (f->conf.auto_cache) {
                pthread_mutex_lock(&f->lock);
                update_stat(node, &e->attr);
                pthread_mutex_unlock(&f->lock);
            }
            set_stat(f, e->ino, &e->attr);
            if (f->conf.debug)
                fprintf(stderr, " NODEID: %lu\n",
                    (unsigned long) e->ino);
        }
    }
    return res;
}

static struct node *find_node(struct fuse *f, fuse_ino_t parent,
             const char *name)
{
    struct node *node;

    pthread_mutex_lock(&f->lock);
    if (!name)
        node = get_node(f, parent);
    else
        node = lookup_node(f, parent, name);
    if (node == NULL) {
        node = (struct node *) calloc(1, sizeof(struct node));
        if (node == NULL)
            goto out_err;

        if (f->conf.noforget)
            node->nlookup = 1;
        node->refctr = 1;
        node->nodeid = next_id(f); /* 选择下一个使用的nodeid */
        node->generation = f->generation;
        node->open_count = 0;
        node->is_hidden = 0;
        node->treelock = 0;
        node->ticket = 0;
        if (hash_name(f, node, parent, name) == -1) { /* 加入路径名hash表 */
            free(node);
            node = NULL;
            goto out_err;
        }
        hash_id(f, node); /* 加入nodeid hash表 */
    }
    node->nlookup ++;
out_err:
    pthread_mutex_unlock(&f->lock);
    return node;
}


创建fuse_fs环境的函数

/* 创建fuse文件系统的全局环境 */
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;
}

 
01-19 12:11