diff options
Diffstat (limited to 'fusetrace')
-rw-r--r-- | fusetrace/Makefile | 7 | ||||
-rw-r--r-- | fusetrace/fusetrace_ll.cc | 1184 |
2 files changed, 1191 insertions, 0 deletions
diff --git a/fusetrace/Makefile b/fusetrace/Makefile new file mode 100644 index 000000000..4fd6ed316 --- /dev/null +++ b/fusetrace/Makefile @@ -0,0 +1,7 @@ + +CFLAGS = -g -I../ceph -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE -I$(HOME)/include -L$(HOME)/lib +LIBS = -lfuse +CC = g++ + +fusetrace_ll: fusetrace_ll.cc + $(CC) $(CFLAGS) $(LIBS) $^ -o $@ diff --git a/fusetrace/fusetrace_ll.cc b/fusetrace/fusetrace_ll.cc new file mode 100644 index 000000000..f10c29fd2 --- /dev/null +++ b/fusetrace/fusetrace_ll.cc @@ -0,0 +1,1184 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:4; indent-tabs-mode:t -*- +// vim: ts=8 sw=4 smarttab + +/* + FUSE: Filesystem in Userspace + Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu> + + This program can be distributed under the terms of the GNU GPL. + See the file COPYING. + + gcc -Wall `pkg-config fuse --cflags --libs` -lulockmgr fusexmp_fh.c -o fusexmp_fh +*/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + + +#include <fuse/fuse_lowlevel.h> +#include <ulockmgr.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> +#include <dirent.h> +#include <errno.h> +#include <sys/time.h> +#ifdef HAVE_SETXATTR +#include <sys/xattr.h> +#endif +#include <time.h> + + +#include "include/unordered_map.h" +#include "include/hash_namespace.h" + +#ifndef __LP64__ +CEPH_HASH_NAMESPACE_START + template<> struct hash<uint64_t> { + size_t operator()(uint64_t __x) const { + static hash<uint32_t> H; + return H((__x >> 32) ^ (__x & 0xffffffff)); + } + }; +CEPH_HASH_NAMESPACE_END +#endif + + +#include <iostream> +#include <fstream> +#include <map> +#include <set> +using namespace std; + +#include "common/ceph_mutex.h" + +ceph::mutex trace_lock; +ofstream tracefile; + +#define traceout (tracefile.is_open() ? tracefile : cout) + +char *basedir = 0; +int debug = 0; +bool do_timestamps = true; + +#define dout if (debug) cout + +ceph::mutex lock; + +struct Inode { + struct stat stbuf; + int ref; + set<int> fds; + + map<pair<string,ino_t>,Inode*> parents; + + // if dir, + map<string,Inode*> dentries; + + Inode() : ref(0) {} + + Inode *lookup(const string& dname) { + if (dentries.count(dname)) + return dentries[dname]; + return 0; + } +}; + +Inode *root = 0; +ceph::unordered_map<ino_t, Inode*> inode_map; + +bool make_inode_path(string &buf, Inode *in) +{ + if (!in->parents.empty()) { + if (!make_inode_path(buf, in->parents.begin()->second)) + return false; + buf += "/"; + buf += in->parents.begin()->first.first; + } else { + if (in != root) return false; + assert(in->stbuf.st_ino == 1); + buf = basedir; + buf += "/"; + } + return true; + //dout << "path: " << in->stbuf.st_ino << " -> " << buf << endl; +} + +bool make_inode_path(string &buf, Inode *in, const char *name) +{ + if (!make_inode_path(buf, in)) return false; + buf += "/"; + buf += name; + return true; +} + +bool make_ino_path(string &buf, ino_t ino) +{ + Inode *in = inode_map[ino]; + assert(in); + return make_inode_path(buf, in); +} + +bool make_ino_path(string &buf, ino_t ino, const char *name) +{ + Inode *in = inode_map[ino]; + assert(in); + if (!make_inode_path(buf, in)) + return false; + buf += "/"; + buf += name; + return true; +} + +void remove_dentry(Inode *pin, const string& dname) +{ + dout << "remove_dentry " << pin->stbuf.st_ino << " " << dname << endl; + + Inode *in = pin->lookup(dname); + assert(in); + pin->dentries.erase(dname); + in->parents.erase(pair<string,ino_t>(dname,pin->stbuf.st_ino)); + + dout << "remove_dentry " << pin->stbuf.st_ino << " " << dname + << " ... inode " << in->stbuf.st_ino << " ref " << in->ref + << endl; +} + +void add_dentry(Inode *parent, const string& dname, Inode *in) +{ + dout << "add_dentry " << parent->stbuf.st_ino << " " << dname << " to " << in->stbuf.st_ino << endl; + + if (parent->dentries.count(dname)) + remove_dentry(parent, dname); // e.g., when renaming over another file.. + + parent->dentries[dname] = in; + in->parents[pair<string,ino_t>(dname,parent->stbuf.st_ino)] = parent; +} + +void unlink_inode(Inode *in) +{ + dout << "unlink_inode " << in->stbuf.st_ino << " ref " << in->ref << endl; + + // remove parent links + while (!in->parents.empty()) { + Inode *parent = in->parents.begin()->second; + string dname = in->parents.begin()->first.first; + remove_dentry(parent, dname); + } + + // remove children + while (!in->dentries.empty()) + remove_dentry(in, in->dentries.begin()->first); + + while (!in->fds.empty()) { + int fd = *in->fds.begin(); + ::close(fd); + in->fds.erase(in->fds.begin()); + dout << "remove_inode closeing stray fd " << fd << endl; + } +} + +void remove_inode(Inode *in) +{ + dout << "remove_inode " << in->stbuf.st_ino << " ref " << in->ref << endl; + + unlink_inode(in); + + inode_map.erase(in->stbuf.st_ino); + dout << "remove_inode " << in->stbuf.st_ino << " done" << endl; + delete in; +} + +Inode *add_inode(Inode *parent, const char *name, struct stat *attr) +{ + dout << "add_inode " << parent->stbuf.st_ino << " " << name << " " << attr->st_ino << endl; + + Inode *in; + if (inode_map.count(attr->st_ino)) { + // reuse inode + in = inode_map[attr->st_ino]; + unlink_inode(in); // hrm.. should this close open fds? probably. + dout << "** REUSING INODE **" << endl; + } else { + inode_map[attr->st_ino] = in = new Inode; + } + memcpy(&in->stbuf, attr, sizeof(*attr)); + + string dname(name); + add_dentry(parent, dname, in); + + return in; +} + + +void print_time() +{ + if (do_timestamps) { + struct timeval tv; + gettimeofday(&tv, 0); + traceout << "@" << endl + << tv.tv_sec << endl + << tv.tv_usec << endl; + } +} + + +bool has_perm(int mask, Inode *in, int uid, int gid) +{ + dout << "hash_perm " << uid << "." << gid << " " << oct << mask << " in " << in->stbuf.st_mode + << " " << in->stbuf.st_uid << "." << in->stbuf.st_gid << endl; + if (in->stbuf.st_mode & mask) return true; + if (in->stbuf.st_gid == gid && in->stbuf.st_mode & (mask << 3)) return true; + if (in->stbuf.st_uid == uid && in->stbuf.st_mode & (mask << 6)) return true; + return false; +} + + +static void ft_ll_lookup(fuse_req_t req, fuse_ino_t pino, const char *name) +{ + int res = 0; + + //dout << "lookup " << pino << " " << name << endl; + + struct fuse_entry_param fe; + memset(&fe, 0, sizeof(fe)); + + lock.lock(); + Inode *parent = inode_map[pino]; + assert(parent); + + // check permissions + + string dname(name); + string path; + Inode *in = 0; + if (!has_perm(0001, parent, fuse_req_ctx(req)->uid, fuse_req_ctx(req)->gid)) { + res = EPERM; + } + else if (!make_inode_path(path, parent, name)) { + res = ENOENT; + } else { + in = parent->lookup(dname); + if (in && res == 0) { + // re-stat, for good measure + res = ::lstat(path.c_str(), &in->stbuf); + + // hrm! + if (res != 0) { + dout << "** WEIRD ** lookup on " << pino << " " << name << " inode went away!" << endl; + in = 0; + res = errno; + } + + //dout << "have " << in->stbuf.st_ino << endl; + } else { + in = new Inode; + res = ::lstat(path.c_str(), &in->stbuf); + //dout << "stat " << path << " res = " << res << endl; + if (res == 0) { + inode_map[in->stbuf.st_ino] = in; + add_dentry(parent, dname, in); + } else { + delete in; + in = 0; + res = errno; + } + } + if (in) { + in->ref++; + fe.ino = in->stbuf.st_ino; + memcpy(&fe.attr, &in->stbuf, sizeof(in->stbuf)); + } + } + lock.unlock(); + + trace_lock.lock(); + print_time(); + traceout << "ll_lookup" << endl << pino << endl << name << endl << fe.attr.st_ino << endl; + trace_lock.unlock(); + + if (in) + fuse_reply_entry(req, &fe); + else + fuse_reply_err(req, res); +} + +static void ft_ll_forget(fuse_req_t req, fuse_ino_t ino, long unsigned nlookup) +{ + if (ino != 1) { + std::scoped_lock l{lock}; + Inode *in = inode_map[ino]; + if (in) { + dout << "forget on " << ino << " ref " << in->ref << ", forget " << nlookup << endl; + if (in->ref < nlookup) + dout << "**** BAD **** forget on " << ino << " ref " << in->ref << ", forget " << nlookup << endl; + + in->ref -= nlookup; + if (in->ref <= 0) + remove_inode(in); + } else { + dout << "**** BAD **** forget " << nlookup << " on nonexistent inode " << ino << endl; + } + } + + { + std::scoped_lock l{trace_lock}; + print_time(); + traceout << "ll_forget" << endl << ino << endl << nlookup << endl; + } + + fuse_reply_none(req); +} + +static void ft_ll_getattr(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) +{ + int res = 0; + string path; + int fd = 0; + + Inode *in = 0; + struct stat attr; + + lock.lock(); + in = inode_map[ino]; + if (in->fds.empty()) { + if (!make_inode_path(path, in)) + res = ENOENT; + } else + fd = *in->fds.begin(); + lock.unlock(); + + if (fd > 0) { + res = ::fstat(fd, &attr); + dout << "getattr fstat on fd " << fd << " res " << res << endl; + } else if (res == 0) { + res = ::lstat(path.c_str(), &attr); + dout << "getattr lstat on " << path << " res " << res << endl; + } + if (res < 0) res = errno; + if (ino == 1) attr.st_ino = 1; + + trace_lock.lock(); + print_time(); + traceout << "ll_getattr" << endl << ino << endl; + trace_lock.unlock(); + + if (res == 0) { + lock.lock(); + memcpy(&in->stbuf, &attr, sizeof(attr)); + lock.unlock(); + fuse_reply_attr(req, &attr, 0); + } else + fuse_reply_err(req, res); +} + +static void ft_ll_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr, + int to_set, struct fuse_file_info *fi) +{ + string path; + Inode *in = 0; + int fd = 0; + int res = 0; + + lock.lock(); + in = inode_map[ino]; + if (in->fds.empty() || (to_set & FUSE_SET_ATTR_MTIME)) { + if (!make_inode_path(path, in)) + res = ENOENT; + } else + fd = *in->fds.begin(); + lock.unlock(); + + trace_lock.lock(); + print_time(); + traceout << "ll_setattr" << endl << ino << endl; + traceout << attr->st_mode << endl; + traceout << attr->st_uid << endl << attr->st_gid << endl; + traceout << attr->st_size << endl; + traceout << attr->st_mtime << endl; + traceout << attr->st_atime << endl; + traceout << to_set << endl; + trace_lock.unlock(); + + if (res == 0 && !has_perm(0010, in, fuse_req_ctx(req)->uid, fuse_req_ctx(req)->gid)) { + res = EPERM; + } else if (res == 0) { + if (to_set & FUSE_SET_ATTR_MODE) { + if (fd > 0) + res = ::fchmod(fd, attr->st_mode); + else + res = ::chmod(path.c_str(), attr->st_mode); + } + if (!res && to_set & FUSE_SET_ATTR_UID) { + if (fd > 0) + res = ::fchown(fd, attr->st_uid, attr->st_gid); + else + res = ::chown(path.c_str(), attr->st_uid, attr->st_gid); + } + if (!res && to_set & FUSE_SET_ATTR_SIZE) { + if (fd > 0) + res = ::ftruncate(fd, attr->st_size); + else + res = ::truncate(path.c_str(), attr->st_size); + } + if (!res && to_set & FUSE_SET_ATTR_MTIME) { + struct utimbuf ut; + ut.actime = attr->st_atime; + ut.modtime = attr->st_mtime; + res = ::utime(path.c_str(), &ut); + } + if (res < 0) res = errno; + } + + if (res == 0) { + lock.lock(); + ::lstat(path.c_str(), &in->stbuf); + if (ino == 1) in->stbuf.st_ino = 1; + memcpy(attr, &in->stbuf, sizeof(*attr)); + lock.unlock(); + fuse_reply_attr(req, attr, 0); + } else + fuse_reply_err(req, res); +} + + +static void ft_ll_readlink(fuse_req_t req, fuse_ino_t ino) +{ + string path; + int res = 0; + + lock.lock(); + if (!make_ino_path(path, ino)) + res = ENOENT; + lock.unlock(); + + trace_lock.lock(); + print_time(); + traceout << "ll_readlink" << endl << ino << endl; + trace_lock.unlock(); + + char buf[256]; + if (res == 0) res = readlink(path.c_str(), buf, 255); + if (res < 0) res = errno; + + if (res >= 0) { + buf[res] = 0; + fuse_reply_readlink(req, buf); + } else { + fuse_reply_err(req, res); + } +} + + +static void ft_ll_opendir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) +{ + string path; + int res = 0; + lock.lock(); + Inode *in = inode_map[ino]; + if (!make_inode_path(path, in)) + res = ENOENT; + lock.unlock(); + + DIR *dir = 0; + if (res == 0 && !has_perm(0100, in, fuse_req_ctx(req)->uid, fuse_req_ctx(req)->gid)) + res = EPERM; + else if (res == 0) dir = opendir(path.c_str()); + if (res < 0) res = errno; + + trace_lock.lock(); + print_time(); + traceout << "ll_opendir" << endl << ino << endl << (unsigned long)dir << endl; + trace_lock.unlock(); + + if (dir) { + fi->fh = (long)dir; + fuse_reply_open(req, fi); + } else + fuse_reply_err(req, res); +} + +static void ft_ll_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, + off_t off, struct fuse_file_info *fi) +{ + struct dirent *de; + DIR *dp = (DIR*)fi->fh; + + // buffer + char *buf; + size_t pos = 0; + + buf = new char[size]; + if (!buf) { + fuse_reply_err(req, ENOMEM); + return; + } + + seekdir(dp, off); + while ((de = readdir(dp)) != NULL) { + struct stat st; + memset(&st, 0, sizeof(st)); + st.st_ino = de->d_ino; + st.st_mode = de->d_type << 12; + + size_t entrysize = fuse_add_direntry(req, buf + pos, size - pos, + de->d_name, &st, telldir(dp)); + if (entrysize > size - pos) + break; // didn't fit, done for now. + pos += entrysize; + } + + fuse_reply_buf(req, buf, pos); + delete[] buf; +} + +static void ft_ll_releasedir(fuse_req_t req, fuse_ino_t ino, + struct fuse_file_info *fi) +{ + DIR *dir = (DIR*)fi->fh; + + trace_lock.lock(); + print_time(); + traceout << "ll_releasedir" << endl << (unsigned long)dir << endl; + trace_lock.unlock(); + + closedir(dir); + fuse_reply_err(req, 0); +} + + + +static void ft_ll_mknod(fuse_req_t req, fuse_ino_t parent, const char *name, + mode_t mode, dev_t rdev) +{ + string path; + Inode *pin = 0; + int res = 0; + lock.lock(); + pin = inode_map[parent]; + if (!make_inode_path(path, pin, name)) + res = ENOENT; + lock.unlock(); + + dout << "mknod " << path << endl; + if (res == 0 && !has_perm(0010, pin, fuse_req_ctx(req)->uid, fuse_req_ctx(req)->gid)) + res = EPERM; + else if (res == 0) res = ::mknod(path.c_str(), mode, rdev); + if (res < 0) + res = errno; + else + ::chown(path.c_str(), fuse_req_ctx(req)->uid, fuse_req_ctx(req)->gid); + + struct fuse_entry_param fe; + if (res == 0) { + memset(&fe, 0, sizeof(fe)); + ::lstat(path.c_str(), &fe.attr); + fe.ino = fe.attr.st_ino; + lock.lock(); + Inode *in = add_inode(pin, name, &fe.attr); + in->ref++; + lock.unlock(); + } + + trace_lock.lock(); + print_time(); + traceout << "ll_mknod" << endl << parent << endl << name << endl << mode << endl << rdev << endl; + traceout << (res == 0 ? fe.ino:0) << endl; + trace_lock.unlock(); + + if (res == 0) + fuse_reply_entry(req, &fe); + else + fuse_reply_err(req, res); +} + +static void ft_ll_mkdir(fuse_req_t req, fuse_ino_t parent, const char *name, + mode_t mode) +{ + string path; + Inode *pin = 0; + int res = 0; + lock.lock(); + pin = inode_map[parent]; + if (!make_inode_path(path, pin, name)) + res = ENOENT; + lock.unlock(); + + if (res == 0 && !has_perm(0010, pin, fuse_req_ctx(req)->uid, fuse_req_ctx(req)->gid)) + res = EPERM; + else if (res == 0) res = ::mkdir(path.c_str(), mode); + if (res < 0) + res = errno; + else + ::chown(path.c_str(), fuse_req_ctx(req)->uid, fuse_req_ctx(req)->gid); + + struct fuse_entry_param fe; + if (res == 0) { + memset(&fe, 0, sizeof(fe)); + ::lstat(path.c_str(), &fe.attr); + fe.ino = fe.attr.st_ino; + lock.lock(); + Inode *in = add_inode(pin, name, &fe.attr); + in->ref++; + lock.unlock(); + } + + trace_lock.lock(); + print_time(); + traceout << "ll_mkdir" << endl << parent << endl << name << endl << mode << endl; + traceout << (res == 0 ? fe.ino:0) << endl; + trace_lock.unlock(); + + if (res == 0) + fuse_reply_entry(req, &fe); + else + fuse_reply_err(req, res); +} + +static void ft_ll_symlink(fuse_req_t req, const char *value, fuse_ino_t parent, const char *name) +{ + string path; + Inode *pin = 0; + int res = 0; + + lock.lock(); + pin = inode_map[parent]; + if (!make_inode_path(path, pin, name)) + res = ENOENT; + lock.unlock(); + + if (res == 0 && !has_perm(0010, pin, fuse_req_ctx(req)->uid, fuse_req_ctx(req)->gid)) + res = EPERM; + else if (res == 0) res = ::symlink(value, path.c_str()); + if (res < 0) + res = errno; + else + ::chown(path.c_str(), fuse_req_ctx(req)->uid, fuse_req_ctx(req)->gid); + + struct fuse_entry_param fe; + if (res == 0) { + memset(&fe, 0, sizeof(fe)); + ::lstat(path.c_str(), &fe.attr); + fe.ino = fe.attr.st_ino; + lock.lock(); + Inode *in = add_inode(pin, name, &fe.attr); + in->ref++; + lock.unlock(); + } + + trace_lock.lock(); + print_time(); + traceout << "ll_symlink" << endl << parent << endl << name << endl << value << endl; + traceout << (res == 0 ? fe.ino:0) << endl; + trace_lock.unlock(); + + if (res == 0) + fuse_reply_entry(req, &fe); + else + fuse_reply_err(req, res); +} + +static void ft_ll_create(fuse_req_t req, fuse_ino_t parent, const char *name, + mode_t mode, struct fuse_file_info *fi) +{ + string path; + Inode *pin = 0; + int res = 0; + + lock.lock(); + pin = inode_map[parent]; + if (!make_inode_path(path, pin, name)) + res = ENOENT; + lock.unlock(); + + dout << "create " << path << endl; + int fd = 0; + if (res == 0 && !has_perm(0010, pin, fuse_req_ctx(req)->uid, fuse_req_ctx(req)->gid)) + res = EPERM; + else if (res == 0) { + fd = ::open(path.c_str(), fi->flags|O_CREAT, mode); + if (fd < 0) { + res = errno; + } else { + ::fchown(fd, fuse_req_ctx(req)->uid, fuse_req_ctx(req)->gid); + } + } + + struct fuse_entry_param fe; + memset(&fe, 0, sizeof(fe)); + if (res == 0) { + ::lstat(path.c_str(), &fe.attr); + fe.ino = fe.attr.st_ino; + lock.lock(); + Inode *in = add_inode(pin, name, &fe.attr); + in->ref++; + in->fds.insert(fd); + lock.unlock(); + fi->fh = fd; + } + + trace_lock.lock(); + print_time(); + traceout << "ll_create" << endl + << parent << endl + << name << endl + << mode << endl + << fi->flags << endl + << (res == 0 ? fd:0) << endl + << fe.ino << endl; + trace_lock.unlock(); + + if (res == 0) + fuse_reply_create(req, &fe, fi); + else + fuse_reply_err(req, res); + +} + + +static void ft_ll_statfs(fuse_req_t req, fuse_ino_t ino) +{ + string path; + int res = 0; + if (ino) { + lock.lock(); + if (!make_ino_path(path, ino)) + res = ENOENT; + lock.unlock(); + } else { + path = basedir; + } + + trace_lock.lock(); + print_time(); + traceout << "ll_statfs" << endl << ino << endl; + trace_lock.unlock(); + + struct statvfs stbuf; + if (res == 0) res = statvfs(path.c_str(), &stbuf); + if (res < 0) res = errno; + + if (res == 0) + fuse_reply_statfs(req, &stbuf); + else + fuse_reply_err(req, res); +} + +static void ft_ll_unlink(fuse_req_t req, fuse_ino_t parent, const char *name) +{ + string path; + Inode *pin = 0; + Inode *in = 0; + string dname(name); + int res = 0; + lock.lock(); + pin = inode_map[parent]; + in = pin->lookup(dname); + if (!make_inode_path(path, pin, name)) + res = ENOENT; + lock.unlock(); + + trace_lock.lock(); + print_time(); + traceout << "ll_unlink" << endl << parent << endl << name << endl; + trace_lock.unlock(); + + if (res == 0 && !has_perm(0010, pin, fuse_req_ctx(req)->uid, fuse_req_ctx(req)->gid)) + res = EPERM; + else if (res == 0) { + if (in && in->fds.empty()) { + int fd = ::open(path.c_str(), O_RDWR); + if (fd > 0) + in->fds.insert(fd); // for slow getattrs.. wtf + dout << "unlink opening paranoia fd " << fd << endl; + } + res = ::unlink(path.c_str()); + if (res < 0) res = errno; + } + + if (res == 0) { + // remove from out cache + lock.lock(); + string dname(name); + if (pin->lookup(dname)) + remove_dentry(pin, dname); + lock.unlock(); + fuse_reply_err(req, 0); + } else + fuse_reply_err(req, res); +} + +static void ft_ll_rmdir(fuse_req_t req, fuse_ino_t parent, const char *name) +{ + string path; + Inode *pin = 0; + int res = 0; + + lock.lock(); + pin = inode_map[parent]; + if (!make_inode_path(path, pin, name)) + res = ENOENT; + lock.unlock(); + + trace_lock.lock(); + print_time(); + traceout << "ll_rmdir" << endl << parent << endl << name << endl; + trace_lock.unlock(); + + if (res == 0 && !has_perm(0010, pin, fuse_req_ctx(req)->uid, fuse_req_ctx(req)->gid)) + res = EPERM; + else if (res == 0) res = ::rmdir(path.c_str()); + if (res < 0) res = errno; + + if (res == 0) { + // remove from out cache + lock.lock(); + string dname(name); + if (pin->lookup(dname)) + remove_dentry(pin, dname); + lock.unlock(); + fuse_reply_err(req, 0); + } else + fuse_reply_err(req, res); +} + + +static void ft_ll_rename(fuse_req_t req, fuse_ino_t parent, const char *name, + fuse_ino_t newparent, const char *newname) +{ + string path; + string newpath; + Inode *pin = 0; + Inode *newpin = 0; + int res = 0; + lock.lock(); + pin = inode_map[parent]; + if (!make_inode_path(path, pin, name)) + res = ENOENT; + newpin = inode_map[newparent]; + if (!make_inode_path(newpath, newpin, newname)) + res = ENOENT; + lock.unlock(); + + trace_lock.lock(); + print_time(); + traceout << "ll_rename" << endl + << parent << endl + << name << endl + << newparent << endl + << newname << endl; + trace_lock.unlock(); + + if (res == 0 && (!has_perm(0010, pin, fuse_req_ctx(req)->uid, fuse_req_ctx(req)->gid) || + !has_perm(0010, newpin, fuse_req_ctx(req)->uid, fuse_req_ctx(req)->gid))) + res = EPERM; + else if (res == 0) res = ::rename(path.c_str(), newpath.c_str()); + if (res < 0) res = errno; + + if (res == 0) { + string dname(name); + string newdname(newname); + lock.lock(); + Inode *in = pin->lookup(dname); + if (in) { + add_dentry(newpin, newdname, in); + remove_dentry(pin, dname); + } else { + dout << "hrm, rename didn't have renamed inode.. " << path << " to " << newpath << endl; + } + lock.unlock(); + fuse_reply_err(req, 0); + } else + fuse_reply_err(req, res); +} + +static void ft_ll_link(fuse_req_t req, fuse_ino_t ino, fuse_ino_t newparent, + const char *newname) +{ + string path; + string newpath; + Inode *in = 0; + Inode *newpin = 0; + int res = 0; + lock.lock(); + in = inode_map[ino]; + if (!make_inode_path(path, in)) + res = ENOENT; + + newpin = inode_map[newparent]; + if (!make_inode_path(newpath, newpin, newname)) + res = ENOENT; + lock.unlock(); + + trace_lock.lock(); + print_time(); + traceout << "ll_link" << endl + << ino << endl + << newparent << endl + << newname << endl; + trace_lock.unlock(); + + //cout << "link " << path << " newpath " << newpath << endl; + if (res == 0 && (!has_perm(0010, in, fuse_req_ctx(req)->uid, fuse_req_ctx(req)->gid) || + !has_perm(0010, newpin, fuse_req_ctx(req)->uid, fuse_req_ctx(req)->gid))) + res = EPERM; + else if (res == 0) res = ::link(path.c_str(), newpath.c_str()); + if (res < 0) res = errno; + + if (res == 0) { + struct fuse_entry_param fe; + memset(&fe, 0, sizeof(fe)); + ::lstat(newpath.c_str(), &fe.attr); + + lock.lock(); + string newdname(newname); + add_dentry(newpin, newdname, in); + in->ref++; + memcpy(&in->stbuf, &fe.attr, sizeof(fe.attr)); // re-read, bc we changed the link count + lock.unlock(); + + fe.ino = fe.attr.st_ino; + fuse_reply_entry(req, &fe); + } else + fuse_reply_err(req, res); +} + +static void ft_ll_open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) +{ + string path; + Inode *in = 0; + int res = 0; + + lock.lock(); + in = inode_map[ino]; + if (!make_inode_path(path, in)) + res = ENOENT; + lock.unlock(); + + int want = 0100; + if (fi->flags & O_RDWR) want |= 0010; + if (fi->flags == O_WRONLY) want = 0010; + + int fd = 0; + if (res == 0 && !has_perm(want, in, fuse_req_ctx(req)->uid, fuse_req_ctx(req)->gid)) + res = EPERM; + else if (res == 0) { + fd = ::open(path.c_str(), fi->flags); + if (fd <= 0) res = errno; + } + + trace_lock.lock(); + print_time(); + traceout << "ll_open" << endl + << ino << endl + << fi->flags << endl + << (fd > 0 ? fd:0) << endl; + trace_lock.unlock(); + + if (res == 0) { + lock.lock(); + in->fds.insert(fd); + lock.unlock(); + fi->fh = fd; + fuse_reply_open(req, fi); + } else + fuse_reply_err(req, res); +} + +static void ft_ll_read(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, + struct fuse_file_info *fi) +{ + + char *buf = new char[size]; + int res = ::pread(fi->fh, buf, size, off); + + //cout << "read " << path << " " << off << "~" << size << endl; + trace_lock.lock(); + print_time(); + traceout << "ll_read" << endl + << fi->fh << endl + << off << endl + << size << endl; + trace_lock.unlock(); + + if (res >= 0) + fuse_reply_buf(req, buf, res); + else + fuse_reply_err(req, errno); + delete[] buf; +} + +static void ft_ll_write(fuse_req_t req, fuse_ino_t ino, const char *buf, + size_t size, off_t off, struct fuse_file_info *fi) +{ + int res = ::pwrite(fi->fh, buf, size, off); + + trace_lock.lock(); + print_time(); + traceout << "ll_write" << endl + << fi->fh << endl + << off << endl + << size << endl; + trace_lock.unlock(); + + if (res >= 0) + fuse_reply_write(req, res); + else + fuse_reply_err(req, errno); +} + +static void ft_ll_flush(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) +{ + trace_lock.lock(); + print_time(); + traceout << "ll_flush" << endl << fi->fh << endl; + trace_lock.unlock(); + + int res = ::fdatasync(fi->fh); + //int res = ::close(dup(fi->fh)); + if (res >= 0) + fuse_reply_err(req, 0); + else + fuse_reply_err(req, errno); +} + +static void ft_ll_release(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) +{ + trace_lock.lock(); + print_time(); + traceout << "ll_release" << endl << fi->fh << endl; + trace_lock.unlock(); + + lock.lock(); + Inode *in = inode_map[ino]; + in->fds.erase(fi->fh); + lock.unlock(); + + int res = ::close(fi->fh); + if (res >= 0) + fuse_reply_err(req, 0); + else + fuse_reply_err(req, errno); +} + +static void ft_ll_fsync(fuse_req_t req, fuse_ino_t ino, int datasync, + struct fuse_file_info *fi) +{ + trace_lock.lock(); + print_time(); + traceout << "ll_fsync" << endl << fi->fh << endl; + trace_lock.unlock(); + + int res = ::fsync(fi->fh); + if (res >= 0) + fuse_reply_err(req, 0); + else + fuse_reply_err(req, errno); +} + +static struct fuse_lowlevel_ops ft_ll_oper = { + init: 0, + destroy: 0, + lookup: ft_ll_lookup, + forget: ft_ll_forget, + getattr: ft_ll_getattr, + setattr: ft_ll_setattr, + readlink: ft_ll_readlink, + mknod: ft_ll_mknod, + mkdir: ft_ll_mkdir, + unlink: ft_ll_unlink, + rmdir: ft_ll_rmdir, + symlink: ft_ll_symlink, + rename: ft_ll_rename, + link: ft_ll_link, + open: ft_ll_open, + read: ft_ll_read, + write: ft_ll_write, + flush: ft_ll_flush, + release: ft_ll_release, + fsync: ft_ll_fsync, + opendir: ft_ll_opendir, + readdir: ft_ll_readdir, + releasedir: ft_ll_releasedir, + fsyncdir: 0, + statfs: ft_ll_statfs, + setxattr: 0, + getxattr: 0, + listxattr: 0, + removexattr: 0, + access: 0, + create: ft_ll_create, + getlk: 0, + setlk: 0, + bmap: 0 +}; + +int main(int argc, char *argv[]) +{ + // open trace + + // figure base dir + char *newargv[100]; + int newargc = 0; + for (int i=0; i<argc; i++) { + if (strcmp(argv[i], "--basedir") == 0) { + basedir = argv[++i]; + } else if (strcmp(argv[i], "--timestamps") == 0) { + do_timestamps = atoi(argv[++i]); + } else if (strcmp(argv[i], "--trace") == 0) { + tracefile.open(argv[++i], ios::out|ios::trunc); + if (!tracefile.is_open()) + cerr << "** couldn't open trace file " << argv[i] << endl; + } else if (strcmp(argv[i], "--debug") == 0) { + debug = 1; + } else { + cout << "arg: " << newargc << " " << argv[i] << endl; + newargv[newargc++] = argv[i]; + } + } + newargv[newargc++] = "-o"; + newargv[newargc++] = "allow_other"; + // newargv[newargc++] = "-o"; + // newargv[newargc++] = "default_permissions"; + if (!basedir) return 1; + cout << "basedir is " << basedir << endl; + + // create root ino + root = new Inode; + ::lstat(basedir, &root->stbuf); + root->stbuf.st_ino = 1; + inode_map[1] = root; + root->ref++; + + umask(0); + + // go go gadget fuse + struct fuse_args args = FUSE_ARGS_INIT(newargc, newargv); + struct fuse_chan *ch; + char *mountpoint; + + if (fuse_parse_cmdline(&args, &mountpoint, NULL, NULL) != -1 && + (ch = fuse_mount(mountpoint, &args)) != NULL) { + struct fuse_session *se; + + // init fuse + se = fuse_lowlevel_new(&args, &ft_ll_oper, sizeof(ft_ll_oper), + NULL); + if (se != NULL) { + if (fuse_set_signal_handlers(se) != -1) { + fuse_session_add_chan(se, ch); + if (fuse_session_loop(se) <= -1) { + cout << "Failed fuse_session_loop() call." << endl; + return 1; + } + fuse_remove_signal_handlers(se); + fuse_session_remove_chan(ch); + } + fuse_session_destroy(se); + } + fuse_unmount(mountpoint, ch); + } + fuse_opt_free_args(&args); +} |