📄 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 "fuse_misc.h"#include <stdio.h>#include <string.h>#include <stdlib.h>#include <stddef.h>#include <unistd.h>#include <time.h>#include <fcntl.h>#include <limits.h>#include <errno.h>#include <signal.h>#include <assert.h>#include <sys/param.h>#include <sys/uio.h>#include <sys/time.h>#define FUSE_MAX_PATH 4096#define FUSE_DEFAULT_INTR_SIGNAL SIGUSR1#define FUSE_UNKNOWN_INO 0xffffffff#define OFFSET_MAX 0x7fffffffffffffffLLstruct fuse_config { unsigned int uid; unsigned int gid; unsigned int umask; double entry_timeout; double negative_timeout; double attr_timeout; double ac_attr_timeout; int ac_attr_timeout_set; 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; int auto_cache; int intr; int intr_signal;};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; int intr_installed;};struct lock { int type; off_t start; off_t end; pid_t pid; uint64_t owner; struct lock *next;};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 timespec stat_updated; struct timespec mtime; off_t size; int cache_valid; struct lock *locks;};struct fuse_dirhandle { pthread_mutex_t lock; struct fuse *fuse; fuse_req_t req; char *contents; int allocated; unsigned len; unsigned size; unsigned needlen; int filled; uint64_t fh; int error; fuse_ino_t nodeid;};struct fuse_context_i { struct fuse_context ctx; fuse_req_t req;};static pthread_key_t fuse_context_key;static pthread_mutex_t fuse_context_lock = PTHREAD_MUTEX_INITIALIZER;static int fuse_context_ref;static int fuse_compat_open(struct fuse *, fuse_req_t, char *, struct fuse_file_info *);static void fuse_compat_release(struct fuse *, fuse_req_t, char *, struct fuse_file_info *);static int fuse_compat_opendir(struct fuse *, fuse_req_t, char *, struct fuse_file_info *);static int fuse_compat_statfs(struct fuse *, fuse_req_t, struct statvfs *);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 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 = (f->ctr + 1) & 0xffffffff; if (!f->ctr) f->generation ++; } while (f->ctr == 0 || f->ctr == FUSE_UNKNOWN_INO || 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 struct fuse *req_fuse(fuse_req_t req){ return (struct fuse *) fuse_req_userdata(req);}static void fuse_intr_sighandler(int sig){ (void) sig; /* Nothing to do */}struct fuse_intr_data { pthread_t id; pthread_cond_t cond; int finished;};static void fuse_interrupt(fuse_req_t req, void *d_){ struct fuse_intr_data *d = d_; struct fuse *f = req_fuse(req); if (d->id == pthread_self()) return; pthread_mutex_lock(&f->lock); while (!d->finished) { struct timeval now; struct timespec timeout; pthread_kill(d->id, f->conf.intr_signal); gettimeofday(&now, NULL); timeout.tv_sec = now.tv_sec + 1; timeout.tv_nsec = now.tv_usec * 1000; pthread_cond_timedwait(&d->cond, &f->lock, &timeout); } pthread_mutex_unlock(&f->lock);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -