// SPDX-License-Identifier: GPL-2.0 /* * Copyright 2018 Google LLC */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "utils.h" #ifndef __S_IFREG #define __S_IFREG S_IFREG #endif unsigned int rnd(unsigned int max, unsigned int *seed) { return rand_r(seed) * ((uint64_t)max + 1) / RAND_MAX; } int remove_dir(const char *dir) { int err = rmdir(dir); if (err && errno == ENOTEMPTY) { err = delete_dir_tree(dir); if (err) return err; return 0; } if (err && errno != ENOENT) return -errno; return 0; } int drop_caches(void) { int drop_caches = open("/proc/sys/vm/drop_caches", O_WRONLY | O_CLOEXEC); int i; if (drop_caches == -1) return -errno; i = write(drop_caches, "3", 1); close(drop_caches); if (i != 1) return -errno; return 0; } int mount_fs(const char *mount_dir, const char *backing_dir, int read_timeout_ms) { static const char fs_name[] = INCFS_NAME; char mount_options[512]; int result; snprintf(mount_options, ARRAY_SIZE(mount_options), "read_timeout_ms=%u", read_timeout_ms); result = mount(backing_dir, mount_dir, fs_name, 0, mount_options); if (result != 0) perror("Error mounting fs."); return result; } int mount_fs_opt(const char *mount_dir, const char *backing_dir, const char *opt, bool remount) { static const char fs_name[] = INCFS_NAME; int result; result = mount(backing_dir, mount_dir, fs_name, remount ? MS_REMOUNT : 0, opt); if (result != 0) perror("Error mounting fs."); return result; } struct hash_section { uint32_t algorithm; uint8_t log2_blocksize; uint32_t salt_size; /* no salt */ uint32_t hash_size; uint8_t hash[SHA256_DIGEST_SIZE]; } __packed; struct signature_blob { uint32_t version; uint32_t hash_section_size; struct hash_section hash_section; uint32_t signing_section_size; uint8_t signing_section[]; } __packed; size_t format_signature(void **buf, const char *root_hash, const char *add_data) { size_t size = sizeof(struct signature_blob) + strlen(add_data) + 1; struct signature_blob *sb = malloc(size); *sb = (struct signature_blob){ .version = INCFS_SIGNATURE_VERSION, .hash_section_size = sizeof(struct hash_section), .hash_section = (struct hash_section){ .algorithm = INCFS_HASH_TREE_SHA256, .log2_blocksize = 12, .salt_size = 0, .hash_size = SHA256_DIGEST_SIZE, }, .signing_section_size = sizeof(uint32_t) + strlen(add_data) + 1, }; memcpy(sb->hash_section.hash, root_hash, SHA256_DIGEST_SIZE); memcpy((char *)sb->signing_section, add_data, strlen(add_data) + 1); *buf = sb; return size; } int crypto_emit_file(int fd, const char *dir, const char *filename, incfs_uuid_t *id_out, size_t size, const char *root_hash, const char *add_data) { int mode = __S_IFREG | 0555; void *signature; int error = 0; struct incfs_new_file_args args = { .size = size, .mode = mode, .file_name = ptr_to_u64(filename), .directory_path = ptr_to_u64(dir), .file_attr = 0, .file_attr_len = 0 }; args.signature_size = format_signature(&signature, root_hash, add_data); args.signature_info = ptr_to_u64(signature); md5(filename, strlen(filename), (char *)args.file_id.bytes); if (ioctl(fd, INCFS_IOC_CREATE_FILE, &args) != 0) { error = -errno; goto out; } *id_out = args.file_id; out: free(signature); return error; } int emit_file(int fd, const char *dir, const char *filename, incfs_uuid_t *id_out, size_t size, const char *attr) { int mode = __S_IFREG | 0555; struct incfs_new_file_args args = { .size = size, .mode = mode, .file_name = ptr_to_u64(filename), .directory_path = ptr_to_u64(dir), .signature_info = ptr_to_u64(NULL), .signature_size = 0, .file_attr = ptr_to_u64(attr), .file_attr_len = attr ? strlen(attr) : 0 }; md5(filename, strlen(filename), (char *)args.file_id.bytes); if (ioctl(fd, INCFS_IOC_CREATE_FILE, &args) != 0) return -errno; *id_out = args.file_id; return 0; } int get_file_bmap(int cmd_fd, int ino, unsigned char *buf, int buf_size) { return 0; } int get_file_signature(int fd, unsigned char *buf, int buf_size) { struct incfs_get_file_sig_args args = { .file_signature = ptr_to_u64(buf), .file_signature_buf_size = buf_size }; if (ioctl(fd, INCFS_IOC_READ_FILE_SIGNATURE, &args) == 0) return args.file_signature_len_out; return -errno; } loff_t get_file_size(const char *name) { struct stat st; if (stat(name, &st) == 0) return st.st_size; return -ENOENT; } int open_commands_file(const char *mount_dir) { char cmd_file[255]; int cmd_fd; snprintf(cmd_file, ARRAY_SIZE(cmd_file), "%s/%s", mount_dir, INCFS_PENDING_READS_FILENAME); cmd_fd = open(cmd_file, O_RDONLY | O_CLOEXEC); if (cmd_fd < 0) perror("Can't open commands file"); return cmd_fd; } int open_log_file(const char *mount_dir) { char file[255]; int fd; snprintf(file, ARRAY_SIZE(file), "%s/.log", mount_dir); fd = open(file, O_RDWR | O_CLOEXEC); if (fd < 0) perror("Can't open log file"); return fd; } int open_blocks_written_file(const char *mount_dir) { char file[255]; int fd; snprintf(file, ARRAY_SIZE(file), "%s/%s", mount_dir, INCFS_BLOCKS_WRITTEN_FILENAME); fd = open(file, O_RDONLY | O_CLOEXEC); if (fd < 0) perror("Can't open blocks_written file"); return fd; } int wait_for_pending_reads(int fd, int timeout_ms, struct incfs_pending_read_info *prs, int prs_count) { ssize_t read_res = 0; if (timeout_ms > 0) { int poll_res = 0; struct pollfd pollfd = { .fd = fd, .events = POLLIN }; poll_res = poll(&pollfd, 1, timeout_ms); if (poll_res < 0) return -errno; if (poll_res == 0) return 0; if (!(pollfd.revents | POLLIN)) return 0; } read_res = read(fd, prs, prs_count * sizeof(*prs)); if (read_res < 0) return -errno; return read_res / sizeof(*prs); } int wait_for_pending_reads2(int fd, int timeout_ms, struct incfs_pending_read_info2 *prs, int prs_count) { ssize_t read_res = 0; if (timeout_ms > 0) { int poll_res = 0; struct pollfd pollfd = { .fd = fd, .events = POLLIN }; poll_res = poll(&pollfd, 1, timeout_ms); if (poll_res < 0) return -errno; if (poll_res == 0) return 0; if (!(pollfd.revents | POLLIN)) return 0; } read_res = read(fd, prs, prs_count * sizeof(*prs)); if (read_res < 0) return -errno; return read_res / sizeof(*prs); } char *concat_file_name(const char *dir, const char *file) { char full_name[FILENAME_MAX] = ""; if (snprintf(full_name, ARRAY_SIZE(full_name), "%s/%s", dir, file) < 0) return NULL; return strdup(full_name); } int delete_dir_tree(const char *dir_path) { DIR *dir = NULL; struct dirent *dp; int result = 0; dir = opendir(dir_path); if (!dir) { result = -errno; goto out; } while ((dp = readdir(dir))) { char *full_path; if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) continue; full_path = concat_file_name(dir_path, dp->d_name); if (dp->d_type == DT_DIR) result = delete_dir_tree(full_path); else result = unlink(full_path); free(full_path); if (result) goto out; } out: if (dir) closedir(dir); if (!result) rmdir(dir_path); return result; } void sha256(const char *data, size_t dsize, char *hash) { SHA256_CTX ctx; SHA256_Init(&ctx); SHA256_Update(&ctx, data, dsize); SHA256_Final((unsigned char *)hash, &ctx); } void md5(const char *data, size_t dsize, char *hash) { MD5_CTX ctx; MD5_Init(&ctx); MD5_Update(&ctx, data, dsize); MD5_Final((unsigned char *)hash, &ctx); }