/* * Maintained by Jeff Garzik * * Copyright 2006-2007 Red Hat, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, write to * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. * */ #define FUSE_USE_VERSION 26 #define _BSD_SOURCE #include "dbfs-config.h" #include #include #include #include #include #include #include #include #include #include "dbfs.h" int debugging = 1; static void dbfs_fill_attr(const struct dbfs_inode *ino, struct stat *st) { memset(st, 0, sizeof(*st)); st->st_dev = 1; st->st_ino = GUINT64_FROM_LE(ino->raw_inode->ino); st->st_mode = GUINT32_FROM_LE(ino->raw_inode->mode); st->st_nlink = GUINT32_FROM_LE(ino->raw_inode->nlink); st->st_uid = GUINT32_FROM_LE(ino->raw_inode->uid); st->st_gid = GUINT32_FROM_LE(ino->raw_inode->gid); st->st_rdev = GUINT64_FROM_LE(ino->raw_inode->rdev); st->st_size = GUINT64_FROM_LE(ino->raw_inode->size); st->st_blksize = 512; st->st_blocks = GUINT64_FROM_LE(ino->raw_inode->size) / 512ULL; st->st_atime = GUINT64_FROM_LE(ino->raw_inode->atime); st->st_mtime = GUINT64_FROM_LE(ino->raw_inode->mtime); st->st_ctime = GUINT64_FROM_LE(ino->raw_inode->ctime); if (debugging) syslog(LOG_DEBUG, "fill_attr: ino %lu, mode %u, uid %u, " "gid %u, size %lu, mtime %lu", st->st_ino, st->st_mode, st->st_uid, st->st_gid, st->st_size, st->st_mtime); } static void dbfs_fill_ent(const struct dbfs_inode *ino, struct fuse_entry_param *ent) { memset(ent, 0, sizeof(*ent)); ent->ino = GUINT64_FROM_LE(ino->raw_inode->ino); ent->generation = GUINT64_FROM_LE(ino->raw_inode->version); dbfs_fill_attr(ino, &ent->attr); /* these timeouts are just a guess */ ent->attr_timeout = 2.0; ent->entry_timeout = 2.0; } static void dbfs_reply_ino(fuse_req_t req, struct dbfs_inode *ino) { struct fuse_entry_param ent; dbfs_fill_ent(ino, &ent); fuse_reply_entry(req, &ent); dbfs_inode_free(ino); } static void dbfs_op_init(void *userdata, struct fuse_conn_info *conn) { struct dbfs *fs; int rc; if (debugging) syslog(LOG_DEBUG, "ENTER dbfs_op_init"); fs = dbfs_new(); rc = dbfs_open(fs, DB_RECOVER | DB_CREATE, DB_CREATE, "dbfs", TRUE); if (rc) { syslog(LOG_ERR, "dbfs_open failed"); abort(); /* TODO: improve */ } gfs = fs; syslog(LOG_INFO, PACKAGE_STRING " initialized"); } static void dbfs_op_destroy(void *userdata) { struct dbfs *fs = gfs; if (debugging) syslog(LOG_DEBUG, "ENTER dbfs_op_destroy"); dbfs_close(fs); dbfs_free(fs); gfs = NULL; if (debugging) syslog(LOG_DEBUG, "EXIT dbfs_op_destroy"); } static void dbfs_op_lookup(fuse_req_t req, fuse_ino_t parent, const char *name) { guint64 ino_n; struct dbfs_inode *ino; int rc; DB_TXN *txn; unsigned long long ino_pr; if (debugging) syslog(LOG_DEBUG, "ENTER dbfs_op_lookup, name=='%s'", name); rc = gfs->env->txn_begin(gfs->env, NULL, &txn, 0); if (rc) { fuse_reply_err(req, rc); return; } /* lookup inode in parent directory */ rc = dbfs_dir_lookup(txn, parent, name, &ino_n); if (rc) goto err_out; rc = dbfs_inode_read(txn, ino_n, &ino); if (rc) goto err_out; rc = txn->commit(txn, 0); if (rc) { dbfs_inode_free(ino); fuse_reply_err(req, rc); return; } /* send reply */ ino_pr = GUINT64_FROM_LE(ino->raw_inode->ino); dbfs_reply_ino(req, ino); if (debugging) syslog(LOG_DEBUG, "EXIT dbfs_op_lookup, ino==%Lu", ino_pr); return; err_out: txn->abort(txn); fuse_reply_err(req, -rc); if (debugging) syslog(LOG_DEBUG, "EXIT dbfs_op_lookup, rc==%d", -rc); } static void dbfs_op_getattr(fuse_req_t req, fuse_ino_t ino_n, struct fuse_file_info *fi) { struct dbfs_inode *ino; struct stat st; int rc; DB_TXN *txn; if (debugging) syslog(LOG_DEBUG, "ENTER dbfs_op_getattr, ino==%lu", ino_n); rc = gfs->env->txn_begin(gfs->env, NULL, &txn, 0); if (rc) { fuse_reply_err(req, rc); return; } /* read inode from database */ rc = dbfs_inode_read(txn, ino_n, &ino); if (rc) { rc = ENOENT; goto err_out_txn; } rc = txn->commit(txn, 0); if (rc) { rc = -rc; goto err_out; } /* fill in stat buf, taking care to convert from * little endian to native endian */ dbfs_fill_attr(ino, &st); /* send result back to FUSE */ fuse_reply_attr(req, &st, 2.0); dbfs_inode_free(ino); if (debugging) syslog(LOG_DEBUG, "EXIT dbfs_op_getattr, ino==%Lu", (unsigned long long) st.st_ino); return; err_out_txn: txn->abort(txn); err_out: fuse_reply_err(req, rc); if (debugging) syslog(LOG_DEBUG, "EXIT dbfs_op_getattr, rc==%d", rc); } static void dbfs_op_setattr(fuse_req_t req, fuse_ino_t ino_n, struct stat *attr, int to_set, struct fuse_file_info *fi) { struct dbfs_inode *ino; struct stat st; int rc, dirty = 0; DB_TXN *txn; if (debugging) syslog(LOG_DEBUG, "ENTER dbfs_op_setattr, ino==%lu", ino_n); rc = gfs->env->txn_begin(gfs->env, NULL, &txn, 0); if (rc) { rc = -rc; goto err_out; } /* read inode from database */ rc = dbfs_inode_read(txn, ino_n, &ino); if (rc) goto err_out_txn; if (to_set & FUSE_SET_ATTR_MODE) { ino->raw_inode->mode = GUINT32_TO_LE(attr->st_mode); dirty = 1; } if (to_set & FUSE_SET_ATTR_UID) { ino->raw_inode->uid = GUINT32_TO_LE(attr->st_uid); dirty = 1; } if (to_set & FUSE_SET_ATTR_GID) { ino->raw_inode->gid = GUINT32_TO_LE(attr->st_gid); dirty = 1; } if (to_set & FUSE_SET_ATTR_SIZE) { rc = dbfs_inode_resize(txn, ino, attr->st_size); if (rc) goto err_out_free; ino->raw_inode->size = GUINT64_TO_LE(attr->st_size); dirty = 1; } if (to_set & FUSE_SET_ATTR_ATIME) { ino->raw_inode->atime = GUINT64_TO_LE(attr->st_atime); dirty = 1; } if (to_set & FUSE_SET_ATTR_MTIME) { ino->raw_inode->mtime = GUINT64_TO_LE(attr->st_mtime); dirty = 1; } if (dirty) { rc = dbfs_inode_write(txn, ino); if (rc) goto err_out_free; } rc = txn->commit(txn, 0); if (rc) { rc = -rc; txn = NULL; goto err_out_free; } dbfs_fill_attr(ino, &st); dbfs_inode_free(ino); fuse_reply_attr(req, &st, 2.0); if (debugging) syslog(LOG_DEBUG, "EXIT dbfs_op_setattr, ino==%Lu", (unsigned long long) st.st_ino); return; err_out_free: dbfs_inode_free(ino); err_out_txn: if (txn) txn->abort(txn); err_out: fuse_reply_err(req, -rc); if (debugging) syslog(LOG_DEBUG, "EXIT dbfs_op_setattr, rc==%d", -rc); } static void dbfs_op_readlink(fuse_req_t req, fuse_ino_t ino) { int rc; DBT val; char *s; DB_TXN *txn; if (debugging) syslog(LOG_DEBUG, "ENTER dbfs_op_readlink, ino==%lu", ino); rc = gfs->env->txn_begin(gfs->env, NULL, &txn, 0); if (rc) { rc = -rc; goto err_out; } /* read link from database */ rc = dbfs_symlink_read(txn, ino, &val); if (rc) goto err_out_txn; rc = txn->commit(txn, 0); if (rc) { rc = -rc; free(val.data); goto err_out; } /* send reply; use g_strndup to append a trailing null */ s = g_strndup(val.data, val.size); fuse_reply_readlink(req, s); free(val.data); if (debugging) syslog(LOG_DEBUG, "EXIT dbfs_op_readlink, linktext=='%s'", s); g_free(s); return; err_out_txn: txn->abort(txn); err_out: fuse_reply_err(req, -rc); if (debugging) syslog(LOG_DEBUG, "EXIT dbfs_op_readlink, rc==%d", -rc); } static int dbfs_mode_validate(mode_t mode) { unsigned int ifmt = mode & S_IFMT; int rc = 0; if (S_ISREG(mode)) { if (ifmt & ~S_IFREG) rc = -EINVAL; } else if (S_ISDIR(mode)) { if (ifmt & ~S_IFDIR) rc = -EINVAL; } else if (S_ISCHR(mode)) { if (ifmt & ~S_IFCHR) rc = -EINVAL; } else if (S_ISBLK(mode)) { if (ifmt & ~S_IFBLK) rc = -EINVAL; } else if (S_ISFIFO(mode)) { if (ifmt & ~S_IFIFO) rc = -EINVAL; } else if (S_ISLNK(mode)) rc = -EINVAL; else if (S_ISSOCK(mode)) { if (ifmt & ~S_IFSOCK) rc = -EINVAL; } else rc = -EINVAL; return rc; } static void dbfs_op_mknod(fuse_req_t req, fuse_ino_t parent, const char *name, mode_t mode, dev_t rdev) { struct dbfs_inode *ino; int rc; DB_TXN *txn; unsigned long long ino_pr; if (debugging) syslog(LOG_DEBUG, "ENTER dbfs_op_mknod, name='%s'", name); rc = gfs->env->txn_begin(gfs->env, NULL, &txn, 0); if (rc) { rc = -rc; goto err_out; } rc = dbfs_mode_validate(mode); if (rc) goto err_out_txn; /* these have separate inode-creation hooks */ if (S_ISDIR(mode) || S_ISLNK(mode)) { rc = -EINVAL; goto err_out_txn; } rc = dbfs_mknod(txn, parent, name, mode, rdev, &ino); if (rc) goto err_out_txn; rc = txn->commit(txn, 0); if (rc) { dbfs_inode_free(ino); rc = -rc; goto err_out; } ino_pr = GUINT64_FROM_LE(ino->raw_inode->ino); dbfs_reply_ino(req, ino); if (debugging) syslog(LOG_DEBUG, "EXIT dbfs_op_mknod, ino==%Lu", ino_pr); return; err_out_txn: txn->abort(txn); err_out: fuse_reply_err(req, -rc); if (debugging) syslog(LOG_DEBUG, "EXIT dbfs_op_mknod, rc==%d", -rc); } static void dbfs_op_mkdir(fuse_req_t req, fuse_ino_t parent, const char *name, mode_t mode) { struct dbfs_inode *ino; int rc; DB_TXN *txn; unsigned long long ino_pr; if (debugging) syslog(LOG_DEBUG, "ENTER dbfs_op_mkdir, name=='%s'", name); rc = gfs->env->txn_begin(gfs->env, NULL, &txn, 0); if (rc) { rc = -rc; goto err_out; } mode &= ALLPERMS; mode |= S_IFDIR; rc = dbfs_mknod(txn, parent, name, mode, 0, &ino); if (rc) goto err_out_txn; rc = txn->commit(txn, 0); if (rc) { dbfs_inode_free(ino); rc = -rc; goto err_out; } ino_pr = GUINT64_FROM_LE(ino->raw_inode->ino); dbfs_reply_ino(req, ino); if (debugging) syslog(LOG_DEBUG, "EXIT dbfs_op_mkdir, ino==%Lu", ino_pr); return; err_out_txn: txn->abort(txn); err_out: fuse_reply_err(req, -rc); if (debugging) syslog(LOG_DEBUG, "EXIT dbfs_op_mkdir, rc==%d", -rc); } static void dbfs_op_unlink(fuse_req_t req, fuse_ino_t parent, const char *name) { int rc; DB_TXN *txn; if (debugging) syslog(LOG_DEBUG, "ENTER dbfs_op_unlink, name=='%s'", name); rc = gfs->env->txn_begin(gfs->env, NULL, &txn, 0); if (rc) { rc = -rc; goto out; } rc = dbfs_unlink(txn, parent, name, 0); if (rc) goto err_out; rc = txn->commit(txn, 0); if (rc) { rc = -rc; goto out; } out: fuse_reply_err(req, -rc); if (debugging) syslog(LOG_DEBUG, "EXIT dbfs_op_unlink, rc==%d", -rc); return; err_out: txn->abort(txn); goto out; } static void dbfs_op_link(fuse_req_t req, fuse_ino_t ino_n, fuse_ino_t parent, const char *newname) { struct dbfs_inode *ino; int rc; DB_TXN *txn; unsigned long long ino_pr; if (debugging) syslog(LOG_DEBUG, "ENTER dbfs_op_link, ino==%lu, newname=='%s'", ino_n, newname); rc = gfs->env->txn_begin(gfs->env, NULL, &txn, 0); if (rc) { rc = -rc; goto err_out; } /* read inode from database */ rc = dbfs_inode_read(txn, ino_n, &ino); if (rc) { rc = -ENOENT; goto err_out_txn; } /* attempt to create hard link */ rc = dbfs_link(txn, ino, ino_n, parent, newname); if (rc) goto err_out_ino; rc = txn->commit(txn, 0); if (rc) { dbfs_inode_free(ino); rc = -rc; goto err_out; } ino_pr = GUINT64_FROM_LE(ino->raw_inode->ino); dbfs_reply_ino(req, ino); if (debugging) syslog(LOG_DEBUG, "EXIT dbfs_op_link, ino==%Lu", ino_pr); return; err_out_ino: dbfs_inode_free(ino); err_out_txn: txn->abort(txn); err_out: fuse_reply_err(req, -rc); if (debugging) syslog(LOG_DEBUG, "EXIT dbfs_op_link, rc==%d", -rc); } static void dbfs_op_open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) { if (debugging) syslog(LOG_DEBUG, "ENTER dbfs_op_open, ino==%lu", ino); fi->direct_io = 0; fi->keep_cache = 1; fuse_reply_open(req, fi); } static void dbfs_op_read(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi) { void *buf = NULL; int rc, rc2; DB_TXN *txn; if (debugging) syslog(LOG_DEBUG, "ENTER dbfs_op_read, ino==%lu", ino); rc = gfs->env->txn_begin(gfs->env, NULL, &txn, 0); if (rc) { rc = -rc; goto err_out; } rc = dbfs_read(txn, ino, off, size, &buf); if (rc < 0) goto err_out_txn; rc2 = txn->commit(txn, 0); if (rc2) { rc = -rc2; goto err_out; } fuse_reply_buf(req, buf, rc); free(buf); if (debugging) syslog(LOG_DEBUG, "EXIT dbfs_op_read, rc==%d", rc); return; err_out_txn: txn->abort(txn); err_out: fuse_reply_err(req, -rc); if (debugging) syslog(LOG_DEBUG, "EXIT dbfs_op_read, rc==%d", -rc); } static void dbfs_op_write(fuse_req_t req, fuse_ino_t ino, const char *buf, size_t size, off_t off, struct fuse_file_info *fi) { int rc, rc2; DB_TXN *txn; if (debugging) syslog(LOG_DEBUG, "ENTER dbfs_op_write, ino==%lu", ino); rc = gfs->env->txn_begin(gfs->env, NULL, &txn, 0); if (rc) { rc = -rc; goto err_out; } rc = dbfs_write(txn, ino, off, buf, size); if (rc < 0) goto err_out_txn; rc2 = txn->commit(txn, 0); if (rc2) { rc = -rc2; goto err_out; } fuse_reply_write(req, rc); if (debugging) syslog(LOG_DEBUG, "EXIT dbfs_op_write, rc==%d", rc); return; err_out_txn: txn->abort(txn); err_out: fuse_reply_err(req, -rc); if (debugging) syslog(LOG_DEBUG, "EXIT dbfs_op_write, error, rc==%d", -rc); } static int dbfs_chk_empty(struct dbfs_dirent *de, void *userdata) { if ((GUINT16_FROM_LE(de->namelen) == 1) && (!memcmp(de->name, ".", 1))) return 0; if ((GUINT16_FROM_LE(de->namelen) == 2) && (!memcmp(de->name, "..", 2))) return 0; return ENOTEMPTY; } static void dbfs_op_rmdir(fuse_req_t req, fuse_ino_t parent, const char *name) { guint64 ino_n; int rc; DBT val; DB_TXN *txn; if (debugging) syslog(LOG_DEBUG, "ENTER dbfs_op_rmdir, name=='%s'", name); rc = gfs->env->txn_begin(gfs->env, NULL, &txn, 0); if (rc) { rc = -rc; goto out; } /* get inode number associated with name */ rc = dbfs_dir_lookup(txn, parent, name, &ino_n); if (rc) goto out_txn; /* read dir associated with name */ rc = dbfs_dir_read(txn, ino_n, &val); if (rc) goto out_txn; /* make sure dir only contains "." and ".." */ rc = dbfs_dir_foreach(val.data, dbfs_chk_empty, NULL); free(val.data); /* if dbfs_chk_empty() returns non-zero, dir is not empty */ if (rc) goto out_txn; /* dir is empty, go ahead and unlink */ rc = dbfs_unlink(txn, parent, name, DBFS_UNLINK_DIR); if (rc) goto out_txn; rc = txn->commit(txn, 0); if (rc) { rc = -rc; goto out; } fuse_reply_err(req, 0); if (debugging) syslog(LOG_DEBUG, "EXIT dbfs_op_rmdir, rc==0"); return; out_txn: txn->abort(txn); out: fuse_reply_err(req, -rc); if (debugging) syslog(LOG_DEBUG, "EXIT dbfs_op_rmdir, rc==%d", -rc); } static void dbfs_op_symlink(fuse_req_t req, const char *link, fuse_ino_t parent, const char *name) { struct dbfs_inode *ino; int rc; DB_TXN *txn; unsigned long long ino_pr; if (debugging) syslog(LOG_DEBUG, "ENTER dbfs_op_symlink, name=='%s'", name); if (!g_utf8_validate(link, -1, NULL)) { rc = -EINVAL; goto err_out; } rc = gfs->env->txn_begin(gfs->env, NULL, &txn, 0); if (rc) { rc = -rc; goto err_out; } rc = dbfs_mknod(txn, parent, name, S_IFLNK | S_IRWXU | S_IRWXG | S_IRWXO, 0, &ino); if (rc) goto err_out_txn; rc = dbfs_symlink_write(txn, GUINT64_FROM_LE(ino->raw_inode->ino), link); if (rc) goto err_out_ino; rc = txn->commit(txn, 0); if (rc) { dbfs_inode_free(ino); rc = -rc; goto err_out; } ino_pr = GUINT64_FROM_LE(ino->raw_inode->ino); dbfs_reply_ino(req, ino); if (debugging) syslog(LOG_DEBUG, "EXIT dbfs_op_symlink, ino==%Lu", ino_pr); return; err_out_ino: dbfs_inode_free(ino); err_out_txn: txn->abort(txn); err_out: fuse_reply_err(req, -rc); if (debugging) syslog(LOG_DEBUG, "EXIT dbfs_op_symlink, rc==%d", -rc); } static void dbfs_op_rename(fuse_req_t req, fuse_ino_t parent, const char *name, fuse_ino_t newparent, const char *newname) { int rc; DB_TXN *txn; if (debugging) syslog(LOG_DEBUG, "ENTER dbfs_op_rename, name=='%s', newname=='%s'", name, newname); rc = gfs->env->txn_begin(gfs->env, NULL, &txn, 0); if (rc) { rc = -rc; goto out; } rc = dbfs_rename(txn, parent, name, newparent, newname); if (rc) goto out_txn; rc = txn->commit(txn, 0); if (rc) { rc = -rc; goto out; } fuse_reply_err(req, 0); return; out_txn: txn->abort(txn); out: fuse_reply_err(req, -rc); if (debugging) syslog(LOG_DEBUG, "EXIT dbfs_op_rename, rc==%d", -rc); } static void dbfs_op_fsync (fuse_req_t req, fuse_ino_t ino, int datasync, struct fuse_file_info *fi) { if (debugging) syslog(LOG_DEBUG, "ENTER dbfs_op_fsync, ino==%lu", ino); /* DB should have already sync'd our data for us */ fuse_reply_err(req, 0); } static void dbfs_op_opendir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) { DBT val; int rc; DB_TXN *txn; if (debugging) syslog(LOG_DEBUG, "ENTER dbfs_op_opendir, ino==%lu", ino); rc = gfs->env->txn_begin(gfs->env, NULL, &txn, 0); if (rc) { rc = -rc; goto err_out; } /* read directory from database */ rc = dbfs_dir_read(txn, ino, &val); if (rc) goto err_out_txn; rc = txn->commit(txn, 0); if (rc) { rc = -rc; goto err_out; } /* save for later use */ fi->fh = (uint64_t) (unsigned long) val.data; /* send reply */ fuse_reply_open(req, fi); if (debugging) syslog(LOG_DEBUG, "EXIT dbfs_op_opendir"); return; err_out_txn: txn->abort(txn); err_out: fuse_reply_err(req, -rc); if (debugging) syslog(LOG_DEBUG, "EXIT dbfs_op_opendir, rc==%d", -rc); } struct dirbuf { char *p; size_t size; }; struct dirbuf_iter { struct dirbuf db; fuse_req_t req; }; /* stock function copied from FUSE template (hello_ll.c) */ static void dirbuf_add(fuse_req_t req, struct dirbuf *b, const char *name, fuse_ino_t ino) { struct stat stbuf; size_t oldsize = b->size; b->size += fuse_add_direntry(req, NULL, 0, name, NULL, 0); b->p = (char *) realloc(b->p, b->size); memset(&stbuf, 0, sizeof(stbuf)); stbuf.st_ino = ino; fuse_add_direntry(req, b->p + oldsize, b->size - oldsize, name, &stbuf, b->size); } #ifndef min #define min(x, y) ((x) < (y) ? (x) : (y)) #endif /* stock function copied from FUSE template */ static int reply_buf_limited(fuse_req_t req, const void *buf, size_t bufsize, off_t off, size_t maxsize) { if (off < bufsize) return fuse_reply_buf(req, buf + off, min(bufsize - off, maxsize)); else return fuse_reply_buf(req, NULL, 0); } static int dbfs_fill_dirbuf(struct dbfs_dirent *de, void *userdata) { struct dirbuf_iter *di = userdata; struct dirbuf *b = &di->db; char *s; /* add dirent to buffer; use g_strndup solely to append nul */ s = g_strndup(de->name, GUINT16_FROM_LE(de->namelen)); dirbuf_add(di->req, b, s, GUINT64_FROM_LE(de->ino)); free(s); return 0; } static void dbfs_op_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi) { struct dirbuf_iter di; void *p; if (debugging) syslog(LOG_DEBUG, "ENTER dbfs_op_readdir, ino==%lu", ino); /* grab directory contents stored by opendir */ p = (void *) (unsigned long) fi->fh; /* iterate through each dirent, filling dirbuf */ memset(&di, 0, sizeof(di)); dbfs_dir_foreach(p, dbfs_fill_dirbuf, &di); /* send reply */ reply_buf_limited(req, di.db.p, di.db.size, off, size); free(di.db.p); if (debugging) syslog(LOG_DEBUG, "EXIT dbfs_op_readdir"); } static void dbfs_op_releasedir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) { void *p = (void *) (unsigned long) fi->fh; if (debugging) syslog(LOG_DEBUG, "ENTER dbfs_op_releasedir, ino==%lu", ino); /* release directory contents */ free(p); fuse_reply_err(req, 0); } static void dbfs_op_fsyncdir (fuse_req_t req, fuse_ino_t ino, int datasync, struct fuse_file_info *fi) { if (debugging) syslog(LOG_DEBUG, "ENTER dbfs_op_fsyncdir, ino==%lu", ino); /* DB should have already sync'd our data for us */ fuse_reply_err(req, 0); } #define COPY(x) f.f_##x = st.f_##x static void dbfs_op_statfs(fuse_req_t req, fuse_ino_t ino) { struct statvfs f; struct statfs st; if (debugging) syslog(LOG_DEBUG, "ENTER dbfs_op_statfs"); if (statfs(gfs->home, &st) < 0) { fuse_reply_err(req, errno); return; } memset(&f, 0, sizeof(f)); COPY(bsize); f.f_frsize = 512; COPY(blocks); COPY(bfree); COPY(bavail); f.f_files = 0xfffffff; f.f_ffree = 0xffffff; f.f_favail = 0xffffff; f.f_fsid = 0xdeadbeef; f.f_flag = 0; f.f_namemax = DBFS_FILENAME_MAX; fuse_reply_statfs(req, &f); } #undef COPY static void dbfs_op_setxattr(fuse_req_t req, fuse_ino_t ino, const char *name, const char *value, size_t size, int flags) { int rc; DB_TXN *txn; if (debugging) syslog(LOG_DEBUG, "ENTER dbfs_op_setxattr, name=='%s'", name); rc = gfs->env->txn_begin(gfs->env, NULL, &txn, 0); if (rc) { rc = -rc; goto err_out; } rc = dbfs_xattr_set(txn, ino, name, value, size, flags); if (rc) goto err_out_txn; rc = txn->commit(txn, 0); if (rc) { rc = -rc; goto err_out; } fuse_reply_err(req, 0); return; err_out_txn: txn->abort(txn); err_out: fuse_reply_err(req, -rc); if (debugging) syslog(LOG_DEBUG, "EXIT dbfs_op_setxattr, rc==%d", -rc); } static void dbfs_op_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name, size_t size) { void *buf = NULL; size_t buflen = 0; int rc; DB_TXN *txn; if (debugging) syslog(LOG_DEBUG, "ENTER dbfs_op_getxattr, name=='%s'", name); rc = gfs->env->txn_begin(gfs->env, NULL, &txn, 0); if (rc) { rc = -rc; goto err_out; } rc = dbfs_xattr_get(txn, ino, name, &buf, &buflen); if (rc) goto err_out_txn; rc = txn->commit(txn, 0); if (rc) { rc = -rc; goto err_out; } if (size == 0) fuse_reply_xattr(req, buflen); else if (buflen <= size) fuse_reply_buf(req, buf, buflen); else { rc = -ERANGE; goto err_out; } free(buf); return; err_out_txn: txn->abort(txn); err_out: fuse_reply_err(req, -rc); if (debugging) syslog(LOG_DEBUG, "EXIT dbfs_op_getxattr, rc==%d", -rc); } static void dbfs_op_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size) { int rc; void *buf; size_t buflen; DB_TXN *txn; if (debugging) syslog(LOG_DEBUG, "ENTER dbfs_op_listxattr, ino==%lu", ino); rc = gfs->env->txn_begin(gfs->env, NULL, &txn, 0); if (rc) { rc = -rc; goto err_out; } rc = dbfs_xattr_list(txn, ino, &buf, &buflen); if (rc < 0) goto err_out_txn; rc = txn->commit(txn, 0); if (rc) { rc = -rc; goto err_out; } if (size == 0) fuse_reply_xattr(req, buflen); else if (size < buflen) fuse_reply_err(req, ERANGE); else fuse_reply_buf(req, buf, buflen); free(buf); return; err_out_txn: txn->abort(txn); err_out: fuse_reply_err(req, -rc); if (debugging) syslog(LOG_DEBUG, "EXIT dbfs_op_listxattr, rc==%d", -rc); } static void dbfs_op_removexattr(fuse_req_t req, fuse_ino_t ino, const char *name) { int rc; DB_TXN *txn; if (debugging) syslog(LOG_DEBUG, "ENTER dbfs_op_removexattr, ino==%lu, name=='%s'", ino, name); rc = gfs->env->txn_begin(gfs->env, NULL, &txn, 0); if (rc) { rc = -rc; goto err_out; } rc = dbfs_xattr_remove(txn, ino, name, TRUE); if (rc) goto err_out_txn; rc = txn->commit(txn, 0); if (rc) { rc = -rc; goto err_out; } fuse_reply_err(req, 0); return; err_out_txn: txn->abort(txn); err_out: fuse_reply_err(req, -rc); if (debugging) syslog(LOG_DEBUG, "EXIT dbfs_op_removexattr, rc==%d", -rc); } static void dbfs_op_access(fuse_req_t req, fuse_ino_t ino_n, int mask) { struct dbfs_inode *ino; const struct fuse_ctx *ctx; int rc; guint32 mode, uid, gid; DB_TXN *txn; if (debugging) syslog(LOG_DEBUG, "ENTER dbfs_op_access, ino==%lu", ino_n); ctx = fuse_req_ctx(req); g_assert(ctx != NULL); rc = gfs->env->txn_begin(gfs->env, NULL, &txn, 0); if (rc) { rc = -rc; goto out; } rc = dbfs_inode_read(txn, ino_n, &ino); if (rc) goto out_txn; rc = txn->commit(txn, 0); if (rc) { dbfs_inode_free(ino); rc = -rc; goto out; } mode = GUINT32_FROM_LE(ino->raw_inode->mode); uid = GUINT32_FROM_LE(ino->raw_inode->uid); gid = GUINT32_FROM_LE(ino->raw_inode->gid); if (uid == ctx->uid) mode >>= 8; else if (gid == ctx->gid) mode >>= 4; rc = 0; if ((mask & R_OK) && (!(mode & S_IROTH))) rc = -EACCES; if ((mask & W_OK) && (!(mode & S_IWOTH))) rc = -EACCES; if ((mask & X_OK) && (!(mode & S_IXOTH))) rc = -EACCES; dbfs_inode_free(ino); out: fuse_reply_err(req, -rc); if (debugging) syslog(LOG_DEBUG, "EXIT dbfs_op_access, rc==%d", -rc); return; out_txn: txn->abort(txn); goto out; } static struct fuse_lowlevel_ops dbfs_ops = { .init = dbfs_op_init, .destroy = dbfs_op_destroy, .lookup = dbfs_op_lookup, .forget = NULL, .getattr = dbfs_op_getattr, .setattr = dbfs_op_setattr, .readlink = dbfs_op_readlink, .mknod = dbfs_op_mknod, .mkdir = dbfs_op_mkdir, .unlink = dbfs_op_unlink, .rmdir = dbfs_op_rmdir, .symlink = dbfs_op_symlink, .rename = dbfs_op_rename, .link = dbfs_op_link, .open = dbfs_op_open, .read = dbfs_op_read, .write = dbfs_op_write, .flush = NULL, .release = NULL, .fsync = dbfs_op_fsync, .opendir = dbfs_op_opendir, .readdir = dbfs_op_readdir, .releasedir = dbfs_op_releasedir, .fsyncdir = dbfs_op_fsyncdir, .statfs = dbfs_op_statfs, .setxattr = dbfs_op_setxattr, .getxattr = dbfs_op_getxattr, .listxattr = dbfs_op_listxattr, .removexattr = dbfs_op_removexattr, .access = dbfs_op_access, .create = NULL, }; /* stock main() from FUSE example */ int main(int argc, char *argv[]) { struct fuse_args args = FUSE_ARGS_INIT(argc, argv); struct fuse_chan *ch; char *mountpoint; int err = -1; openlog("dbfs", LOG_PID, LOG_LOCAL4); if (fuse_parse_cmdline(&args, &mountpoint, NULL, NULL) != -1 && (ch = fuse_mount(mountpoint, &args)) != NULL) { struct fuse_session *se; se = fuse_lowlevel_new(&args, &dbfs_ops, sizeof(dbfs_ops), NULL); if (se != NULL) { if (fuse_set_signal_handlers(se) != -1) { fuse_session_add_chan(se, ch); err = fuse_session_loop(se); fuse_remove_signal_handlers(se); fuse_session_remove_chan(ch); } fuse_session_destroy(se); } fuse_unmount(mountpoint, ch); } fuse_opt_free_args(&args); return err ? 1 : 0; }