static int handle_opendir(struct fuse* fuse, struct fuse_handler* handler, const struct fuse_in_header* hdr, const struct fuse_open_in* req) { struct node* node; char path[PATH_MAX]; struct fuse_open_out out; struct dirhandle *h; pthread_mutex_lock(&fuse->lock); node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid, path, sizeof(path)); TRACE("[%d] OPENDIR @ %llx (%s)\n", handler->token, hdr->nodeid, node ? node->name : "?"); pthread_mutex_unlock(&fuse->lock); if (!node) { return -ENOENT; } h = malloc(sizeof(*h)); if (!h) { return -ENOMEM; } TRACE("[%d] OPENDIR %s\n", handler->token, path); h->d = opendir(path); if (!h->d) { free(h); return -errno; } out.fh = ptr_to_id(h); fuse_reply(fuse, hdr->unique, &out, sizeof(out)); return NO_STATUS; }
static int handle_readdir(struct fuse* fuse, struct fuse_handler* handler, const struct fuse_in_header* hdr, const struct fuse_read_in* req) { char buffer[8192]; struct fuse_dirent *fde = (struct fuse_dirent*) buffer; struct dirent *de; struct dirhandle *h = id_to_ptr(req->fh); TRACE("[%d] READDIR %p\n", handler->token, h); if (req->offset == 0) { /* rewinddir() might have been called above us, so rewind here too */ TRACE("[%d] calling rewinddir()\n", handler->token); rewinddir(h->d); } de = readdir(h->d); if (!de) { return 0; } fde->ino = FUSE_UNKNOWN_INO; /* increment the offset so we can detect when rewinddir() seeks back to the beginning */ fde->off = req->offset + 1; fde->type = de->d_type; fde->namelen = strlen(de->d_name); memcpy(fde->name, de->d_name, fde->namelen + 1); fuse_reply(fuse, hdr->unique, fde, FUSE_DIRENT_ALIGN(sizeof(struct fuse_dirent) + fde->namelen)); return NO_STATUS; }
static int handle_read(struct fuse* fuse, struct fuse_handler* handler, const struct fuse_in_header* hdr, const struct fuse_read_in* req) { struct handle *h = id_to_ptr(req->fh); __u64 unique = hdr->unique; __u32 size = req->size; __u64 offset = req->offset; int res; /* Don't access any other fields of hdr or req beyond this point, the read buffer * overlaps the request buffer and will clobber data in the request. This * saves us 128KB per request handler thread at the cost of this scary comment. */ TRACE("[%d] READ %p(%d) %u@%llu\n", handler->token, h, h->fd, size, offset); if (size > sizeof(handler->read_buffer)) { return -EINVAL; } res = pread64(h->fd, handler->read_buffer, size, offset); if (res < 0) { return -errno; } fuse_reply(fuse, unique, handler->read_buffer, res); return NO_STATUS; }
static int handle_statfs(struct fuse* fuse, struct fuse_handler* handler, const struct fuse_in_header* hdr) { char path[PATH_MAX]; struct statfs stat; struct fuse_statfs_out out; int res; pthread_mutex_lock(&fuse->lock); TRACE("[%d] STATFS\n", handler->token); res = get_node_path_locked(&fuse->root, path, sizeof(path)); pthread_mutex_unlock(&fuse->lock); if (res < 0) { return -ENOENT; } if (statfs(fuse->root.name, &stat) < 0) { return -errno; } memset(&out, 0, sizeof(out)); out.st.blocks = stat.f_blocks; out.st.bfree = stat.f_bfree; out.st.bavail = stat.f_bavail; out.st.files = stat.f_files; out.st.ffree = stat.f_ffree; out.st.bsize = stat.f_bsize; out.st.namelen = stat.f_namelen; out.st.frsize = stat.f_frsize; fuse_reply(fuse, hdr->unique, &out, sizeof(out)); return NO_STATUS; }
static int fuse_reply_entry(struct fuse* fuse, __u64 unique, struct node* parent, const char* name, const char* actual_name, const char* path) { struct node* node; struct fuse_entry_out out; struct stat s; if (lstat(path, &s) < 0) { return -errno; } pthread_mutex_lock(&fuse->lock); node = acquire_or_create_child_locked(fuse, parent, name, actual_name); if (!node) { pthread_mutex_unlock(&fuse->lock); return -ENOMEM; } memset(&out, 0, sizeof(out)); attr_from_stat(&out.attr, &s, node->nid); out.attr_valid = 10; out.entry_valid = 10; out.nodeid = node->nid; out.generation = node->gen; pthread_mutex_unlock(&fuse->lock); fuse_reply(fuse, unique, &out, sizeof(out)); return NO_STATUS; }
static int handle_open(void* /* data */, struct fuse_data* fd, const struct fuse_in_header* hdr) { if (hdr->nodeid != PACKAGE_FILE_ID) return -ENOENT; struct fuse_open_out out; memset(&out, 0, sizeof(out)); out.fh = 10; // an arbitrary number; we always use the same handle fuse_reply(fd, hdr->unique, &out, sizeof(out)); return NO_STATUS; }
static int fuse_reply_attr(struct fuse* fuse, __u64 unique, __u64 nid, const char* path) { struct fuse_attr_out out; struct stat s; if (lstat(path, &s) < 0) { return -errno; } memset(&out, 0, sizeof(out)); attr_from_stat(&out.attr, &s, nid); out.attr_valid = 10; fuse_reply(fuse, unique, &out, sizeof(out)); return NO_STATUS; }
static int handle_init(void* data, struct fuse_data* fd, const struct fuse_in_header* hdr) { const struct fuse_init_in* req = data; struct fuse_init_out out; out.major = FUSE_KERNEL_VERSION; out.minor = FUSE_KERNEL_MINOR_VERSION; out.max_readahead = req->max_readahead; out.flags = 0; out.max_background = 32; out.congestion_threshold = 32; out.max_write = 4096; fuse_reply(fd, hdr->unique, &out, sizeof(out)); return NO_STATUS; }
static int handle_getattr(void* /* data */, struct fuse_data* fd, const struct fuse_in_header* hdr) { struct fuse_attr_out out; memset(&out, 0, sizeof(out)); out.attr_valid = 10; if (hdr->nodeid == FUSE_ROOT_ID) { fill_attr(&(out.attr), fd, hdr->nodeid, 4096, S_IFDIR | 0555); } else if (hdr->nodeid == PACKAGE_FILE_ID) { fill_attr(&(out.attr), fd, PACKAGE_FILE_ID, fd->file_size, S_IFREG | 0444); } else { return -ENOENT; } fuse_reply(fd, hdr->unique, &out, sizeof(out)); return NO_STATUS; }
static int handle_init(struct fuse* fuse, struct fuse_handler* handler, const struct fuse_in_header* hdr, const struct fuse_init_in* req) { struct fuse_init_out out; TRACE("[%d] INIT ver=%d.%d maxread=%d flags=%x\n", handler->token, req->major, req->minor, req->max_readahead, req->flags); out.major = FUSE_KERNEL_VERSION; out.minor = FUSE_KERNEL_MINOR_VERSION; out.max_readahead = req->max_readahead; out.flags = FUSE_ATOMIC_O_TRUNC | FUSE_BIG_WRITES; out.max_background = 32; out.congestion_threshold = 32; out.max_write = MAX_WRITE; fuse_reply(fuse, hdr->unique, &out, sizeof(out)); return NO_STATUS; }
static int handle_write(struct fuse* fuse, struct fuse_handler* handler, const struct fuse_in_header* hdr, const struct fuse_write_in* req, const void* buffer) { struct fuse_write_out out; struct handle *h = id_to_ptr(req->fh); int res; TRACE("[%d] WRITE %p(%d) %u@%llu\n", handler->token, h, h->fd, req->size, req->offset); res = pwrite64(h->fd, buffer, req->size, req->offset); if (res < 0) { return -errno; } out.size = res; fuse_reply(fuse, hdr->unique, &out, sizeof(out)); return NO_STATUS; }
static int handle_lookup(void* data, struct fuse_data* fd, const struct fuse_in_header* hdr) { struct fuse_entry_out out; memset(&out, 0, sizeof(out)); out.entry_valid = 10; out.attr_valid = 10; if (strncmp(FUSE_SIDELOAD_HOST_FILENAME, reinterpret_cast<const char*>(data), sizeof(FUSE_SIDELOAD_HOST_FILENAME)) == 0) { out.nodeid = PACKAGE_FILE_ID; out.generation = PACKAGE_FILE_ID; fill_attr(&(out.attr), fd, PACKAGE_FILE_ID, fd->file_size, S_IFREG | 0444); } else { return -ENOENT; } fuse_reply(fd, hdr->unique, &out, sizeof(out)); return NO_STATUS; }
static int handle_init(void* data, struct fuse_data* fd, const struct fuse_in_header* hdr) { const struct fuse_init_in* req = reinterpret_cast<const struct fuse_init_in*>(data); struct fuse_init_out out; size_t fuse_struct_size; /* Kernel 2.6.16 is the first stable kernel with struct fuse_init_out * defined (fuse version 7.6). The structure is the same from 7.6 through * 7.22. Beginning with 7.23, the structure increased in size and added * new parameters. */ if (req->major != FUSE_KERNEL_VERSION || req->minor < 6) { printf("Fuse kernel version mismatch: Kernel version %d.%d, Expected at least %d.6", req->major, req->minor, FUSE_KERNEL_VERSION); return -1; } out.minor = MIN(req->minor, FUSE_KERNEL_MINOR_VERSION); fuse_struct_size = sizeof(out); #if defined(FUSE_COMPAT_22_INIT_OUT_SIZE) /* FUSE_KERNEL_VERSION >= 23. */ /* If the kernel only works on minor revs older than or equal to 22, * then use the older structure size since this code only uses the 7.22 * version of the structure. */ if (req->minor <= 22) { fuse_struct_size = FUSE_COMPAT_22_INIT_OUT_SIZE; } #endif out.major = FUSE_KERNEL_VERSION; out.max_readahead = req->max_readahead; out.flags = 0; out.max_background = 32; out.congestion_threshold = 32; out.max_write = 4096; fuse_reply(fd, hdr->unique, &out, fuse_struct_size); return NO_STATUS; }
void lookup_entry(struct fuse *fuse, struct node *node, const char *name, __u64 unique) { struct fuse_entry_out out; memset(&out, 0, sizeof(out)); node = node_lookup(fuse, node, name, &out.attr); if (!node) { fuse_status(fuse, unique, -ENOENT); return; } node->refcount++; // fprintf(stderr,"ACQUIRE %p (%s) rc=%d\n", node, node->name, node->refcount); out.nodeid = node->nid; out.generation = node->gen; out.entry_valid = 10; out.attr_valid = 10; fuse_reply(fuse, unique, &out, sizeof(out)); }
void handle_fuse_request(struct fuse *fuse, struct fuse_in_header *hdr, void *data, unsigned len) { struct node *node; if ((len < sizeof(*hdr)) || (hdr->len != len)) { ERROR("malformed header\n"); return; } len -= hdr->len; if (hdr->nodeid) { node = lookup_by_inode(fuse, hdr->nodeid); if (!node) { fuse_status(fuse, hdr->unique, -ENOENT); return; } } else { node = 0; } switch (hdr->opcode) { case FUSE_LOOKUP: { /* bytez[] -> entry_out */ TRACE("LOOKUP %llx %s\n", hdr->nodeid, (char*) data); lookup_entry(fuse, node, (char*) data, hdr->unique); return; } case FUSE_FORGET: { struct fuse_forget_in *req = data; TRACE("FORGET %llx (%s) #%lld\n", hdr->nodeid, node->name, req->nlookup); /* no reply */ while (req->nlookup--) node_release(node); return; } case FUSE_GETATTR: { /* getattr_in -> attr_out */ struct fuse_getattr_in *req = data; struct fuse_attr_out out; TRACE("GETATTR flags=%x fh=%llx\n", req->getattr_flags, req->fh); memset(&out, 0, sizeof(out)); node_get_attr(node, &out.attr); out.attr_valid = 10; fuse_reply(fuse, hdr->unique, &out, sizeof(out)); return; } case FUSE_SETATTR: { /* setattr_in -> attr_out */ struct fuse_setattr_in *req = data; struct fuse_attr_out out; char *path, buffer[PATH_BUFFER_SIZE]; int res = 0; struct timespec times[2]; TRACE("SETATTR fh=%llx id=%llx valid=%x\n", req->fh, hdr->nodeid, req->valid); /* XXX: incomplete implementation on purpose. chmod/chown * should NEVER be implemented.*/ path = node_get_path(node, buffer, 0); if (req->valid & FATTR_SIZE) res = truncate(path, req->size); if (res) goto getout; /* Handle changing atime and mtime. If FATTR_ATIME_and FATTR_ATIME_NOW * are both set, then set it to the current time. Else, set it to the * time specified in the request. Same goes for mtime. Use utimensat(2) * as it allows ATIME and MTIME to be changed independently, and has * nanosecond resolution which fuse also has. */ if (req->valid & (FATTR_ATIME | FATTR_MTIME)) { times[0].tv_nsec = UTIME_OMIT; times[1].tv_nsec = UTIME_OMIT; if (req->valid & FATTR_ATIME) { if (req->valid & FATTR_ATIME_NOW) { times[0].tv_nsec = UTIME_NOW; } else { times[0].tv_sec = req->atime; times[0].tv_nsec = req->atimensec; } } if (req->valid & FATTR_MTIME) { if (req->valid & FATTR_MTIME_NOW) { times[1].tv_nsec = UTIME_NOW; } else { times[1].tv_sec = req->mtime; times[1].tv_nsec = req->mtimensec; } } TRACE("Calling utimensat on %s with atime %ld, mtime=%ld\n", path, times[0].tv_sec, times[1].tv_sec); res = utimensat(-1, path, times, 0); } getout: memset(&out, 0, sizeof(out)); node_get_attr(node, &out.attr); out.attr_valid = 10; if (res) fuse_status(fuse, hdr->unique, -errno); else fuse_reply(fuse, hdr->unique, &out, sizeof(out)); return; } // case FUSE_READLINK: // case FUSE_SYMLINK: case FUSE_MKNOD: { /* mknod_in, bytez[] -> entry_out */ struct fuse_mknod_in *req = data; char *path, buffer[PATH_BUFFER_SIZE]; char *name = ((char*) data) + sizeof(*req); int res; TRACE("MKNOD %s @ %llx\n", name, hdr->nodeid); path = node_get_path(node, buffer, name); req->mode = (req->mode & (~0777)) | 0664; res = mknod(path, req->mode, req->rdev); /* XXX perm?*/ if (res < 0) { fuse_status(fuse, hdr->unique, -errno); } else { lookup_entry(fuse, node, name, hdr->unique); } return; } case FUSE_MKDIR: { /* mkdir_in, bytez[] -> entry_out */ struct fuse_mkdir_in *req = data; struct fuse_entry_out out; char *path, buffer[PATH_BUFFER_SIZE]; char *name = ((char*) data) + sizeof(*req); int res; TRACE("MKDIR %s @ %llx 0%o\n", name, hdr->nodeid, req->mode); path = node_get_path(node, buffer, name); req->mode = (req->mode & (~0777)) | 0775; res = mkdir(path, req->mode); if (res < 0) { fuse_status(fuse, hdr->unique, -errno); } else { lookup_entry(fuse, node, name, hdr->unique); } return; } case FUSE_UNLINK: { /* bytez[] -> */ char *path, buffer[PATH_BUFFER_SIZE]; int res; TRACE("UNLINK %s @ %llx\n", (char*) data, hdr->nodeid); path = node_get_path(node, buffer, (char*) data); res = unlink(path); fuse_status(fuse, hdr->unique, res ? -errno : 0); return; } case FUSE_RMDIR: { /* bytez[] -> */ char *path, buffer[PATH_BUFFER_SIZE]; int res; TRACE("RMDIR %s @ %llx\n", (char*) data, hdr->nodeid); path = node_get_path(node, buffer, (char*) data); res = rmdir(path); fuse_status(fuse, hdr->unique, res ? -errno : 0); return; } case FUSE_RENAME: { /* rename_in, oldname, newname -> */ struct fuse_rename_in *req = data; char *oldname = ((char*) data) + sizeof(*req); char *newname = oldname + strlen(oldname) + 1; char *oldpath, oldbuffer[PATH_BUFFER_SIZE]; char *newpath, newbuffer[PATH_BUFFER_SIZE]; struct node *target; struct node *newparent; int res; TRACE("RENAME %s->%s @ %llx\n", oldname, newname, hdr->nodeid); target = lookup_child_by_name(node, oldname); if (!target) { fuse_status(fuse, hdr->unique, -ENOENT); return; } oldpath = node_get_path(node, oldbuffer, oldname); newparent = lookup_by_inode(fuse, req->newdir); if (!newparent) { fuse_status(fuse, hdr->unique, -ENOENT); return; } if (newparent == node) { /* Special case for renaming a file where destination * is same path differing only by case. * In this case we don't want to look for a case insensitive match. * This allows commands like "mv foo FOO" to work as expected. */ newpath = do_node_get_path(newparent, newbuffer, newname, NO_CASE_SENSITIVE_MATCH); } else { newpath = node_get_path(newparent, newbuffer, newname); } if (!remove_child(node, target->nid)) { ERROR("RENAME remove_child not found"); fuse_status(fuse, hdr->unique, -ENOENT); return; } if (!rename_node(target, newname)) { fuse_status(fuse, hdr->unique, -ENOMEM); return; } add_node_to_parent(target, newparent); res = rename(oldpath, newpath); TRACE("RENAME result %d\n", res); fuse_status(fuse, hdr->unique, res ? -errno : 0); return; } // case FUSE_LINK: case FUSE_OPEN: { /* open_in -> open_out */ struct fuse_open_in *req = data; struct fuse_open_out out; char *path, buffer[PATH_BUFFER_SIZE]; struct handle *h; h = malloc(sizeof(*h)); if (!h) { fuse_status(fuse, hdr->unique, -ENOMEM); return; } path = node_get_path(node, buffer, 0); TRACE("OPEN %llx '%s' 0%o fh=%p\n", hdr->nodeid, path, req->flags, h); h->fd = open(path, req->flags); if (h->fd < 0) { ERROR("ERROR\n"); fuse_status(fuse, hdr->unique, -errno); free(h); return; } out.fh = ptr_to_id(h); out.open_flags = 0; out.padding = 0; fuse_reply(fuse, hdr->unique, &out, sizeof(out)); return; } case FUSE_READ: { /* read_in -> byte[] */ char buffer[128 * 1024]; struct fuse_read_in *req = data; struct handle *h = id_to_ptr(req->fh); int res; TRACE("READ %p(%d) %u@%llu\n", h, h->fd, req->size, req->offset); if (req->size > sizeof(buffer)) { fuse_status(fuse, hdr->unique, -EINVAL); return; } res = pread64(h->fd, buffer, req->size, req->offset); if (res < 0) { fuse_status(fuse, hdr->unique, -errno); return; } fuse_reply(fuse, hdr->unique, buffer, res); return; } case FUSE_WRITE: { /* write_in, byte[write_in.size] -> write_out */ struct fuse_write_in *req = data; struct fuse_write_out out; struct handle *h = id_to_ptr(req->fh); int res; TRACE("WRITE %p(%d) %u@%llu\n", h, h->fd, req->size, req->offset); res = pwrite64(h->fd, ((char*) data) + sizeof(*req), req->size, req->offset); if (res < 0) { fuse_status(fuse, hdr->unique, -errno); return; } out.size = res; fuse_reply(fuse, hdr->unique, &out, sizeof(out)); goto oops; } case FUSE_STATFS: { /* getattr_in -> attr_out */ struct statfs stat; struct fuse_statfs_out out; int res; TRACE("STATFS\n"); if (statfs(fuse->root.name, &stat)) { fuse_status(fuse, hdr->unique, -errno); return; } memset(&out, 0, sizeof(out)); out.st.blocks = stat.f_blocks; out.st.bfree = stat.f_bfree; out.st.bavail = stat.f_bavail; out.st.files = stat.f_files; out.st.ffree = stat.f_ffree; out.st.bsize = stat.f_bsize; out.st.namelen = stat.f_namelen; out.st.frsize = stat.f_frsize; fuse_reply(fuse, hdr->unique, &out, sizeof(out)); return; } case FUSE_RELEASE: { /* release_in -> */ struct fuse_release_in *req = data; struct handle *h = id_to_ptr(req->fh); TRACE("RELEASE %p(%d)\n", h, h->fd); close(h->fd); free(h); fuse_status(fuse, hdr->unique, 0); return; } // case FUSE_FSYNC: // case FUSE_SETXATTR: // case FUSE_GETXATTR: // case FUSE_LISTXATTR: // case FUSE_REMOVEXATTR: case FUSE_FLUSH: fuse_status(fuse, hdr->unique, 0); return; case FUSE_OPENDIR: { /* open_in -> open_out */ struct fuse_open_in *req = data; struct fuse_open_out out; char *path, buffer[PATH_BUFFER_SIZE]; struct dirhandle *h; h = malloc(sizeof(*h)); if (!h) { fuse_status(fuse, hdr->unique, -ENOMEM); return; } path = node_get_path(node, buffer, 0); TRACE("OPENDIR %llx '%s'\n", hdr->nodeid, path); h->d = opendir(path); if (h->d == 0) { ERROR("ERROR\n"); fuse_status(fuse, hdr->unique, -errno); free(h); return; } out.fh = ptr_to_id(h); fuse_reply(fuse, hdr->unique, &out, sizeof(out)); return; } case FUSE_READDIR: { struct fuse_read_in *req = data; char buffer[8192]; struct fuse_dirent *fde = (struct fuse_dirent*) buffer; struct dirent *de; struct dirhandle *h = id_to_ptr(req->fh); TRACE("READDIR %p\n", h); if (req->offset == 0) { /* rewinddir() might have been called above us, so rewind here too */ TRACE("calling rewinddir()\n"); rewinddir(h->d); } de = readdir(h->d); if (!de) { fuse_status(fuse, hdr->unique, 0); return; } fde->ino = FUSE_UNKNOWN_INO; /* increment the offset so we can detect when rewinddir() seeks back to the beginning */ fde->off = req->offset + 1; fde->type = de->d_type; fde->namelen = strlen(de->d_name); memcpy(fde->name, de->d_name, fde->namelen + 1); fuse_reply(fuse, hdr->unique, fde, FUSE_DIRENT_ALIGN(sizeof(struct fuse_dirent) + fde->namelen)); return; } case FUSE_RELEASEDIR: { /* release_in -> */ struct fuse_release_in *req = data; struct dirhandle *h = id_to_ptr(req->fh); TRACE("RELEASEDIR %p\n",h); closedir(h->d); free(h); fuse_status(fuse, hdr->unique, 0); return; } // case FUSE_FSYNCDIR: case FUSE_INIT: { /* init_in -> init_out */ struct fuse_init_in *req = data; struct fuse_init_out out; TRACE("INIT ver=%d.%d maxread=%d flags=%x\n", req->major, req->minor, req->max_readahead, req->flags); out.major = FUSE_KERNEL_VERSION; out.minor = FUSE_KERNEL_MINOR_VERSION; out.max_readahead = req->max_readahead; out.flags = FUSE_ATOMIC_O_TRUNC | FUSE_BIG_WRITES; out.max_background = 32; out.congestion_threshold = 32; out.max_write = 256 * 1024; fuse_reply(fuse, hdr->unique, &out, sizeof(out)); return; } default: { struct fuse_out_header h; ERROR("NOTIMPL op=%d uniq=%llx nid=%llx\n", hdr->opcode, hdr->unique, hdr->nodeid); oops: h.len = sizeof(h); h.error = -ENOSYS; h.unique = hdr->unique; write(fuse->fd, &h, sizeof(h)); break; } } }