📄 fuse.c
字号:
/* FUSE: Filesystem in Userspace Copyright (C) 2001-2006 Miklos Szeredi <miklos@szeredi.hu> This program can be distributed under the terms of the GNU LGPL. See the file COPYING.LIB*//* For pthread_rwlock_t */#define _GNU_SOURCE#include "fuse_i.h"#include "fuse_lowlevel.h"#include "fuse_opt.h"#include <stdio.h>#include <string.h>#include <stdlib.h>#include <stddef.h>#include <unistd.h>#include <fcntl.h>#include <limits.h>#include <errno.h>#include <assert.h>#include <pthread.h>#include <sys/param.h>#include <sys/uio.h>#define FUSE_MAX_PATH 4096#define FUSE_UNKNOWN_INO 0xffffffffstruct fuse_config { unsigned int uid; unsigned int gid; unsigned int umask; double entry_timeout; double negative_timeout; double attr_timeout; int debug; int hard_remove; int use_ino; int readdir_ino; int set_mode; int set_uid; int set_gid; int direct_io; int kernel_cache;};struct fuse { struct fuse_session *se; struct fuse_operations op; int compat; struct node **name_table; size_t name_table_size; struct node **id_table; size_t id_table_size; fuse_ino_t ctr; unsigned int generation; unsigned int hidectr; pthread_mutex_t lock; pthread_rwlock_t tree_lock; void *user_data; struct fuse_config conf;};struct node { struct node *name_next; struct node *id_next; fuse_ino_t nodeid; unsigned int generation; int refctr; fuse_ino_t parent; char *name; uint64_t nlookup; int open_count; int is_hidden;};struct fuse_dirhandle { pthread_mutex_t lock; struct fuse *fuse; char *contents; int allocated; unsigned len; unsigned size; unsigned needlen; int filled; uint64_t fh; int error; fuse_ino_t nodeid;};static struct fuse_context *(*fuse_getcontext)(void) = NULL;static int fuse_do_open(struct fuse *, char *, struct fuse_file_info *);static void fuse_do_release(struct fuse *, char *, struct fuse_file_info *);static int fuse_do_opendir(struct fuse *, char *, struct fuse_file_info *);static int fuse_do_statfs(struct fuse *, char *, struct statvfs *);#ifndef USE_UCLIBC#define mutex_init(mut) pthread_mutex_init(mut, NULL)#elsestatic void mutex_init(pthread_mutex_t *mut){ pthread_mutexattr_t attr; pthread_mutexattr_init(&attr); pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ADAPTIVE_NP); pthread_mutex_init(mut, &attr); pthread_mutexattr_destroy(&attr);}#endifstatic 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 struct node *get_node(struct fuse *f, fuse_ino_t nodeid){ struct node *node = get_node_nocheck(f, nodeid); if (!node) { fprintf(stderr, "fuse internal error: node %llu not found\n", (unsigned long long) nodeid); abort(); } return node;}static void free_node(struct node *node){ free(node->name); free(node);}static void unhash_id(struct fuse *f, struct node *node){ size_t hash = node->nodeid % f->id_table_size; struct node **nodep = &f->id_table[hash]; for (; *nodep != NULL; nodep = &(*nodep)->id_next) if (*nodep == node) { *nodep = node->id_next; return; }}static void hash_id(struct fuse *f, struct node *node){ size_t hash = node->nodeid % f->id_table_size; node->id_next = f->id_table[hash]; f->id_table[hash] = node;}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;}static void unref_node(struct fuse *f, struct node *node);static void unhash_name(struct fuse *f, struct node *node){ if (node->name) { size_t hash = name_hash(f, node->parent, node->name); struct node **nodep = &f->name_table[hash]; for (; *nodep != NULL; nodep = &(*nodep)->name_next) if (*nodep == node) { *nodep = node->name_next; node->name_next = NULL; unref_node(f, get_node(f, node->parent)); free(node->name); node->name = NULL; node->parent = 0; return; } fprintf(stderr, "fuse internal error: unable to unhash node: %llu\n", (unsigned long long) node->nodeid); abort(); }}static int hash_name(struct fuse *f, struct node *node, fuse_ino_t parent, const char *name){ size_t hash = name_hash(f, parent, name); node->name = strdup(name); if (node->name == NULL) return -1; get_node(f, parent)->refctr ++; node->parent = parent; node->name_next = f->name_table[hash]; f->name_table[hash] = node; return 0;}static void delete_node(struct fuse *f, struct node *node){ if (f->conf.debug) { printf("delete: %llu\n", (unsigned long long) node->nodeid); fflush(stdout); } assert(!node->name); unhash_id(f, node); free_node(node);}static void unref_node(struct fuse *f, struct node *node){ assert(node->refctr > 0); node->refctr --; if (!node->refctr) delete_node(f, node);}static fuse_ino_t next_id(struct fuse *f){ do { f->ctr++; if (!f->ctr) f->generation ++; } while (f->ctr == 0 || get_node_nocheck(f, f->ctr) != NULL); return f->ctr;}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 == parent && strcmp(node->name, name) == 0) return node; return NULL;}static struct node *find_node(struct fuse *f, fuse_ino_t parent, const char *name){ struct node *node; pthread_mutex_lock(&f->lock); node = lookup_node(f, parent, name); if (node == NULL) { node = (struct node *) calloc(1, sizeof(struct node)); if (node == NULL) goto out_err; node->refctr = 1; node->nodeid = next_id(f); node->open_count = 0; node->is_hidden = 0; node->generation = f->generation; if (hash_name(f, node, parent, name) == -1) { free(node); node = NULL; goto out_err; } hash_id(f, node); } node->nlookup ++; out_err: pthread_mutex_unlock(&f->lock); return node;}static char *add_name(char *buf, char *s, const char *name){ size_t len = strlen(name); s -= len; if (s <= buf) { fprintf(stderr, "fuse: path too long: ...%s\n", s + len); return NULL; } strncpy(s, name, len); s--; *s = '/'; return s;}static char *get_path_name(struct fuse *f, fuse_ino_t nodeid, const char *name){ char buf[FUSE_MAX_PATH]; char *s = buf + FUSE_MAX_PATH - 1; struct node *node; *s = '\0'; if (name != NULL) { s = add_name(buf, s, name); if (s == NULL) return NULL; } pthread_mutex_lock(&f->lock); for (node = get_node(f, nodeid); node && node->nodeid != FUSE_ROOT_ID; node = get_node(f, node->parent)) { if (node->name == NULL) { s = NULL; break; } s = add_name(buf, s, node->name); if (s == NULL) break; } pthread_mutex_unlock(&f->lock); if (node == NULL || s == NULL) return NULL; else if (*s == '\0') return strdup("/"); else return strdup(s);}static char *get_path(struct fuse *f, fuse_ino_t nodeid){ return get_path_name(f, nodeid, NULL);}static void forget_node(struct fuse *f, fuse_ino_t nodeid, uint64_t nlookup){ struct node *node; if (nodeid == FUSE_ROOT_ID) return; pthread_mutex_lock(&f->lock); node = get_node(f, nodeid); assert(node->nlookup >= nlookup); node->nlookup -= nlookup; if (!node->nlookup) { unhash_name(f, node); unref_node(f, node); } pthread_mutex_unlock(&f->lock);}static void remove_node(struct fuse *f, fuse_ino_t dir, const char *name){ struct node *node; pthread_mutex_lock(&f->lock); node = lookup_node(f, dir, name); if (node != NULL) unhash_name(f, node); pthread_mutex_unlock(&f->lock);}static int rename_node(struct fuse *f, fuse_ino_t olddir, const char *oldname, fuse_ino_t newdir, const char *newname, int hide){ struct node *node; struct node *newnode; int err = 0; pthread_mutex_lock(&f->lock); node = lookup_node(f, olddir, oldname); newnode = lookup_node(f, newdir, newname); if (node == NULL) goto out; if (newnode != NULL) { if (hide) { fprintf(stderr, "fuse: hidden file got created during hiding\n"); err = -EBUSY; goto out; } unhash_name(f, newnode); } unhash_name(f, node); if (hash_name(f, node, newdir, newname) == -1) { err = -ENOMEM; goto out; } if (hide) node->is_hidden = 1; out: pthread_mutex_unlock(&f->lock); return err;}static void set_stat(struct fuse *f, fuse_ino_t nodeid, struct stat *stbuf){ if (!f->conf.use_ino) stbuf->st_ino = nodeid; if (f->conf.set_mode) stbuf->st_mode = (stbuf->st_mode & S_IFMT) | (0777 & ~f->conf.umask); if (f->conf.set_uid) stbuf->st_uid = f->conf.uid; if (f->conf.set_gid) stbuf->st_gid = f->conf.gid;}static int is_open(struct fuse *f, fuse_ino_t dir, const char *name){ struct node *node; int isopen = 0; pthread_mutex_lock(&f->lock); node = lookup_node(f, dir, name); if (node && node->open_count > 0) isopen = 1; pthread_mutex_unlock(&f->lock); return isopen;}static char *hidden_name(struct fuse *f, fuse_ino_t dir, const char *oldname, char *newname, size_t bufsize){ struct stat buf; struct node *node; struct node *newnode; char *newpath; int res; int failctr = 10; if (!f->op.getattr) return NULL; do { pthread_mutex_lock(&f->lock); node = lookup_node(f, dir, oldname); if (node == NULL) { pthread_mutex_unlock(&f->lock); return NULL; } do { f->hidectr ++; snprintf(newname, bufsize, ".fuse_hidden%08x%08x", (unsigned int) node->nodeid, f->hidectr); newnode = lookup_node(f, dir, newname); } while(newnode); pthread_mutex_unlock(&f->lock); newpath = get_path_name(f, dir, newname); if (!newpath) break; res = f->op.getattr(newpath, &buf); if (res != 0) break; free(newpath); newpath = NULL; } while(--failctr); return newpath;}static int hide_node(struct fuse *f, const char *oldpath, fuse_ino_t dir, const char *oldname){ char newname[64]; char *newpath; int err = -EBUSY; if (f->op.rename && f->op.unlink) { newpath = hidden_name(f, dir, oldname, newname, sizeof(newname)); if (newpath) { int res = f->op.rename(oldpath, newpath); if (res == 0) err = rename_node(f, dir, oldname, dir, newname, 1); free(newpath); } } return err;}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)); if (fi && f->op.fgetattr) res = f->op.fgetattr(path, &e->attr, fi); else res = f->op.getattr(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; set_stat(f, e->ino, &e->attr); if (f->conf.debug) { printf(" NODEID: %lu\n", (unsigned long) e->ino); fflush(stdout); } } } return res;}static struct fuse *req_fuse(fuse_req_t req){ return (struct fuse *) fuse_req_userdata(req);}static struct fuse *req_fuse_prepare(fuse_req_t req){ struct fuse_context *c = fuse_get_context(); const struct fuse_ctx *ctx = fuse_req_ctx(req); c->fuse = req_fuse(req); c->uid = ctx->uid; c->gid = ctx->gid; c->pid = ctx->pid; c->private_data = c->fuse->user_data; return c->fuse;}static inline void reply_err(fuse_req_t req, int err){ /* fuse_reply_err() uses non-negated errno values */ fuse_reply_err(req, -err);}static void reply_entry(fuse_req_t req, const struct fuse_entry_param *e,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -