379 lines
7.6 KiB
C
379 lines
7.6 KiB
C
|
// SPDX-License-Identifier: GPL-2.0
|
||
|
/*
|
||
|
* Copyright 2018 Google LLC
|
||
|
*/
|
||
|
#include <dirent.h>
|
||
|
#include <errno.h>
|
||
|
#include <fcntl.h>
|
||
|
#include <poll.h>
|
||
|
#include <stdio.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <string.h>
|
||
|
#include <unistd.h>
|
||
|
|
||
|
#include <sys/ioctl.h>
|
||
|
#include <sys/mount.h>
|
||
|
#include <sys/stat.h>
|
||
|
#include <sys/types.h>
|
||
|
|
||
|
#include <openssl/sha.h>
|
||
|
#include <openssl/md5.h>
|
||
|
|
||
|
#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);
|
||
|
}
|