示例#1
0
static int handle_fuse_request(struct fuse *fuse, struct fuse_handler* handler,
        const struct fuse_in_header *hdr, const void *data, size_t data_len)
{
    switch (hdr->opcode) {
    case FUSE_LOOKUP: { /* bytez[] -> entry_out */
        const char* name = data;
        return handle_lookup(fuse, handler, hdr, name);
    }

    case FUSE_FORGET: {
        const struct fuse_forget_in *req = data;
        return handle_forget(fuse, handler, hdr, req);
    }

    case FUSE_GETATTR: { /* getattr_in -> attr_out */
        const struct fuse_getattr_in *req = data;
        return handle_getattr(fuse, handler, hdr, req);
    }

    case FUSE_SETATTR: { /* setattr_in -> attr_out */
        const struct fuse_setattr_in *req = data;
        return handle_setattr(fuse, handler, hdr, req);
    }

//    case FUSE_READLINK:
//    case FUSE_SYMLINK:
    case FUSE_MKNOD: { /* mknod_in, bytez[] -> entry_out */
        const struct fuse_mknod_in *req = data;
        const char *name = ((const char*) data) + sizeof(*req);
        return handle_mknod(fuse, handler, hdr, req, name);
    }

    case FUSE_MKDIR: { /* mkdir_in, bytez[] -> entry_out */
        const struct fuse_mkdir_in *req = data;
        const char *name = ((const char*) data) + sizeof(*req);
        return handle_mkdir(fuse, handler, hdr, req, name);
    }

    case FUSE_UNLINK: { /* bytez[] -> */
        const char* name = data;
        return handle_unlink(fuse, handler, hdr, name);
    }

    case FUSE_RMDIR: { /* bytez[] -> */
        const char* name = data;
        return handle_rmdir(fuse, handler, hdr, name);
    }

    case FUSE_RENAME: { /* rename_in, oldname, newname ->  */
        const struct fuse_rename_in *req = data;
        const char *old_name = ((const char*) data) + sizeof(*req);
        const char *new_name = old_name + strlen(old_name) + 1;
        return handle_rename(fuse, handler, hdr, req, old_name, new_name);
    }

//    case FUSE_LINK:
    case FUSE_OPEN: { /* open_in -> open_out */
        const struct fuse_open_in *req = data;
        return handle_open(fuse, handler, hdr, req);
    }

    case FUSE_READ: { /* read_in -> byte[] */
        const struct fuse_read_in *req = data;
        return handle_read(fuse, handler, hdr, req);
    }

    case FUSE_WRITE: { /* write_in, byte[write_in.size] -> write_out */
        const struct fuse_write_in *req = data;
        const void* buffer = (const __u8*)data + sizeof(*req);
        return handle_write(fuse, handler, hdr, req, buffer);
    }

    case FUSE_STATFS: { /* getattr_in -> attr_out */
        return handle_statfs(fuse, handler, hdr);
    }

    case FUSE_RELEASE: { /* release_in -> */
        const struct fuse_release_in *req = data;
        return handle_release(fuse, handler, hdr, req);
    }

    case FUSE_FSYNC: {
        const struct fuse_fsync_in *req = data;
        return handle_fsync(fuse, handler, hdr, req);
    }

//    case FUSE_SETXATTR:
//    case FUSE_GETXATTR:
//    case FUSE_LISTXATTR:
//    case FUSE_REMOVEXATTR:
    case FUSE_FLUSH: {
        return handle_flush(fuse, handler, hdr);
    }

    case FUSE_OPENDIR: { /* open_in -> open_out */
        const struct fuse_open_in *req = data;
        return handle_opendir(fuse, handler, hdr, req);
    }

    case FUSE_READDIR: {
        const struct fuse_read_in *req = data;
        return handle_readdir(fuse, handler, hdr, req);
    }

    case FUSE_RELEASEDIR: { /* release_in -> */
        const struct fuse_release_in *req = data;
        return handle_releasedir(fuse, handler, hdr, req);
    }

//    case FUSE_FSYNCDIR:
    case FUSE_INIT: { /* init_in -> init_out */
        const struct fuse_init_in *req = data;
        return handle_init(fuse, handler, hdr, req);
    }

    default: {
        TRACE("[%d] NOTIMPL op=%d uniq=%llx nid=%llx\n",
                handler->token, hdr->opcode, hdr->unique, hdr->nodeid);
        return -ENOSYS;
    }
    }
}
int run_fuse_sideload(struct provider_vtab* vtab, void* cookie,
                      uint64_t file_size, uint32_t block_size)
{
    int result;
    uint64_t mem = 0;
    uint64_t avail = 0;

    // If something's already mounted on our mountpoint, try to remove
    // it.  (Mostly in case of a previous abnormal exit.)
    umount2(FUSE_SIDELOAD_HOST_MOUNTPOINT, MNT_FORCE);

    if (block_size < 1024) {
        fprintf(stderr, "block size (%u) is too small\n", block_size);
        return -1;
    }
    if (block_size > (1<<22)) {   // 4 MiB
        fprintf(stderr, "block size (%u) is too large\n", block_size);
        return -1;
    }

    struct fuse_data fd;
    memset(&fd, 0, sizeof(fd));
    fd.vtab = vtab;
    fd.cookie = cookie;
    fd.file_size = file_size;
    fd.block_size = block_size;
    fd.file_blocks = (file_size == 0) ? 0 : (((file_size-1) / block_size) + 1);

    if (fd.file_blocks > (1<<18)) {
        fprintf(stderr, "file has too many blocks (%u)\n", fd.file_blocks);
        result = -1;
        goto done;
    }

    fd.hashes = (uint8_t*)calloc(fd.file_blocks, SHA256_DIGEST_SIZE);
    if (fd.hashes == NULL) {
        fprintf(stderr, "failed to allocate %d bites for hashes\n",
                fd.file_blocks * SHA256_DIGEST_SIZE);
        result = -1;
        goto done;
    }

    fd.uid = getuid();
    fd.gid = getgid();

    fd.curr_block = -1;
    fd.block_data = (uint8_t*)malloc(block_size);
    if (fd.block_data == NULL) {
        fprintf(stderr, "failed to allocate %d bites for block_data\n", block_size);
        result = -1;
        goto done;
    }
    fd.extra_block = (uint8_t*)malloc(block_size);
    if (fd.extra_block == NULL) {
        fprintf(stderr, "failed to allocate %d bites for extra_block\n", block_size);
        result = -1;
        goto done;
    }

    fd.block_cache_max_size = 0;
    fd.block_cache_size = 0;
    fd.block_cache = NULL;
    mem = free_memory();
    avail = mem - (INSTALL_REQUIRED_MEMORY + fd.file_blocks * sizeof(uint8_t*));
    if (mem > avail) {
        uint32_t max_size = avail / fd.block_size;
        if (max_size > fd.file_blocks) {
            max_size = fd.file_blocks;
        }
        // The cache must be at least 1% of the file size or two blocks,
        // whichever is larger.
        if (max_size >= fd.file_blocks/100 && max_size >= 2) {
            fd.block_cache_max_size = max_size;
            fd.block_cache = (uint8_t**)calloc(fd.file_blocks, sizeof(uint8_t*));
        }
    }

    signal(SIGTERM, sig_term);

    fd.ffd = open("/dev/fuse", O_RDWR);
    if (fd.ffd < 0) {
        perror("open /dev/fuse");
        result = -1;
        goto done;
    }

    char opts[256];
    snprintf(opts, sizeof(opts),
             ("fd=%d,user_id=%d,group_id=%d,max_read=%u,"
              "allow_other,rootmode=040000"),
             fd.ffd, fd.uid, fd.gid, block_size);

    result = mount("/dev/fuse", FUSE_SIDELOAD_HOST_MOUNTPOINT,
                   "fuse", MS_NOSUID | MS_NODEV | MS_RDONLY | MS_NOEXEC, opts);
    if (result < 0) {
        perror("mount");
        goto done;
    }
    uint8_t request_buffer[sizeof(struct fuse_in_header) + PATH_MAX*8];
    while (!terminated) {
        fd_set fds;
        struct timeval tv;
        FD_ZERO(&fds);
        FD_SET(fd.ffd, &fds);
        tv.tv_sec = 1;
        tv.tv_usec = 0;
        int rc = select(fd.ffd+1, &fds, NULL, NULL, &tv);
        if (rc <= 0) {
            continue;
        }
        ssize_t len = TEMP_FAILURE_RETRY(read(fd.ffd, request_buffer, sizeof(request_buffer)));
        if (len < 0) {
            if (errno != EINTR) {
                perror("read request");
                if (errno == ENODEV) {
                    result = -1;
                    break;
                }
            }
            continue;
        }

        if ((size_t)len < sizeof(struct fuse_in_header)) {
            fprintf(stderr, "request too short: len=%zu\n", (size_t)len);
            continue;
        }

        struct fuse_in_header* hdr = (struct fuse_in_header*) request_buffer;
        void* data = request_buffer + sizeof(struct fuse_in_header);

        result = -ENOSYS;

        switch (hdr->opcode) {
             case FUSE_INIT:
                result = handle_init(data, &fd, hdr);
                break;

             case FUSE_LOOKUP:
                result = handle_lookup(data, &fd, hdr);
                break;

            case FUSE_GETATTR:
                result = handle_getattr(data, &fd, hdr);
                break;

            case FUSE_OPEN:
                result = handle_open(data, &fd, hdr);
                break;

            case FUSE_READ:
                result = handle_read(data, &fd, hdr);
                break;

            case FUSE_FLUSH:
                result = handle_flush(data, &fd, hdr);
                break;

            case FUSE_RELEASE:
                result = handle_release(data, &fd, hdr);
                break;

            default:
                fprintf(stderr, "unknown fuse request opcode %d\n", hdr->opcode);
                break;
        }

        if (result != NO_STATUS) {
            struct fuse_out_header outhdr;
            outhdr.len = sizeof(outhdr);
            outhdr.error = result;
            outhdr.unique = hdr->unique;
            TEMP_FAILURE_RETRY(write(fd.ffd, &outhdr, sizeof(outhdr)));
        }
    }

  done:
    fd.vtab->close(fd.cookie);

    result = umount2(FUSE_SIDELOAD_HOST_MOUNTPOINT, MNT_DETACH);
    if (result < 0) {
        printf("fuse_sideload umount failed: %s\n", strerror(errno));
    }

    if (fd.ffd) close(fd.ffd);
    if (fd.block_cache) {
        uint32_t n;
        for (n = 0; n < fd.file_blocks; ++n) {
            free(fd.block_cache[n]);
        }
        free(fd.block_cache);
    }
    free(fd.hashes);
    free(fd.block_data);
    free(fd.extra_block);

    return result;
}
示例#3
0
int run_fuse_sideload(struct provider_vtab* vtab, void* cookie,
                      uint64_t file_size, uint32_t block_size)
{
    int result;

    // If something's already mounted on our mountpoint, try to remove
    // it.  (Mostly in case of a previous abnormal exit.)
    umount2(FUSE_SIDELOAD_HOST_MOUNTPOINT, MNT_FORCE);

    if (block_size < 1024) {
        fprintf(stderr, "block size (%u) is too small\n", block_size);
        return -1;
    }
    if (block_size > (1<<22)) {   // 4 MiB
        fprintf(stderr, "block size (%u) is too large\n", block_size);
        return -1;
    }

    struct fuse_data fd;
    memset(&fd, 0, sizeof(fd));
    fd.vtab = vtab;
    fd.cookie = cookie;
    fd.file_size = file_size;
    fd.block_size = block_size;
    fd.file_blocks = (file_size == 0) ? 0 : (((file_size-1) / block_size) + 1);

    if (fd.file_blocks > (1<<18)) {
        fprintf(stderr, "file has too many blocks (%u)\n", fd.file_blocks);
        result = -1;
        goto done;
    }

    fd.hashes = (uint8_t*)calloc(fd.file_blocks, SHA256_DIGEST_SIZE);
    if (fd.hashes == NULL) {
        fprintf(stderr, "failed to allocate %d bites for hashes\n",
                fd.file_blocks * SHA256_DIGEST_SIZE);
        result = -1;
        goto done;
    }

    fd.uid = getuid();
    fd.gid = getgid();

    fd.curr_block = -1;
    fd.block_data = (uint8_t*)malloc(block_size);
    if (fd.block_data == NULL) {
        fprintf(stderr, "failed to allocate %d bites for block_data\n", block_size);
        result = -1;
        goto done;
    }
    fd.extra_block = (uint8_t*)malloc(block_size);
    if (fd.extra_block == NULL) {
        fprintf(stderr, "failed to allocate %d bites for extra_block\n", block_size);
        result = -1;
        goto done;
    }

    fd.ffd = open("/dev/fuse", O_RDWR);
    if (fd.ffd < 0) {
        perror("open /dev/fuse");
        result = -1;
        goto done;
    }

    char opts[256];
    snprintf(opts, sizeof(opts),
             ("fd=%d,user_id=%d,group_id=%d,max_read=%zu,"
              "allow_other,rootmode=040000"),
             fd.ffd, fd.uid, fd.gid, block_size);

    result = mount("/dev/fuse", FUSE_SIDELOAD_HOST_MOUNTPOINT,
                   "fuse", MS_NOSUID | MS_NODEV | MS_RDONLY | MS_NOEXEC, opts);
    if (result < 0) {
        perror("mount");
        goto done;
    }
    uint8_t request_buffer[sizeof(struct fuse_in_header) + PATH_MAX*8];
    for (;;) {
        ssize_t len = read(fd.ffd, request_buffer, sizeof(request_buffer));
        if (len < 0) {
            if (errno != EINTR) {
                perror("read request");
                if (errno == ENODEV) {
                    result = -1;
                    break;
                }
            }
            continue;
        }

        if ((size_t)len < sizeof(struct fuse_in_header)) {
            fprintf(stderr, "request too short: len=%zu\n", (size_t)len);
            continue;
        }

        struct fuse_in_header* hdr = (struct fuse_in_header*) request_buffer;
        void* data = request_buffer + sizeof(struct fuse_in_header);

        result = -ENOSYS;

        switch (hdr->opcode) {
             case FUSE_INIT:
                result = handle_init(data, &fd, hdr);
                break;

             case FUSE_LOOKUP:
                result = handle_lookup(data, &fd, hdr);
                break;

            case FUSE_GETATTR:
                result = handle_getattr(data, &fd, hdr);
                break;

            case FUSE_OPEN:
                result = handle_open(data, &fd, hdr);
                break;

            case FUSE_READ:
                result = handle_read(data, &fd, hdr);
                break;

            case FUSE_FLUSH:
                result = handle_flush(data, &fd, hdr);
                break;

            case FUSE_RELEASE:
                result = handle_release(data, &fd, hdr);
                break;

            default:
                fprintf(stderr, "unknown fuse request opcode %d\n", hdr->opcode);
                break;
        }

        if (result == NO_STATUS_EXIT) {
            result = 0;
            break;
        }

        if (result != NO_STATUS) {
            struct fuse_out_header outhdr;
            outhdr.len = sizeof(outhdr);
            outhdr.error = result;
            outhdr.unique = hdr->unique;
            write(fd.ffd, &outhdr, sizeof(outhdr));
        }
    }

  done:
    fd.vtab->close(fd.cookie);

    result = umount2(FUSE_SIDELOAD_HOST_MOUNTPOINT, MNT_DETACH);
    if (result < 0) {
        printf("fuse_sideload umount failed: %s\n", strerror(errno));
    }

    if (fd.ffd) close(fd.ffd);
    free(fd.hashes);
    free(fd.block_data);
    free(fd.extra_block);

    return result;
}