diff options
author | Yurii Zubrytskyi <zyy@google.com> | 2020-12-21 16:51:45 -0800 |
---|---|---|
committer | Paul Lawrence <paullawrence@google.com> | 2021-01-06 16:57:22 +0000 |
commit | d749bc24dcdeef98baf3555c0768150576c1bd0f (patch) | |
tree | 36114993c0ba33832d5be8f3a658f7675a4d6dee | |
parent | 9046dcfedf7bb21f18b2fdd21ced1255529a79aa (diff) |
ANDROID: Incremental fs: fix .blocks_written
.blocks_writen file handling was missing some operations:
SELinux xattr handlers, safety checks for it being a
pseudo file etc.
This CL generalizes pseudo file handling so that all such
files work in a generic way and next time it should be
easier to add all operations at once.
Bug: 175823975
Test: incfs_tests pass
Change-Id: Id2b1936018c81c62c8ab4cdbaa8827e2679b513f
Signed-off-by: Yurii Zubrytskyi <zyy@google.com>
Signed-off-by: Paul Lawrence <paullawrence@google.com>
-rw-r--r-- | fs/incfs/data_mgmt.c | 5 | ||||
-rw-r--r-- | fs/incfs/data_mgmt.h | 8 | ||||
-rw-r--r-- | fs/incfs/pseudo_files.c | 378 | ||||
-rw-r--r-- | fs/incfs/pseudo_files.h | 5 | ||||
-rw-r--r-- | fs/incfs/vfs.c | 33 |
5 files changed, 207 insertions, 222 deletions
diff --git a/fs/incfs/data_mgmt.c b/fs/incfs/data_mgmt.c index 657f1ae6b9c8..df3b24b2d428 100644 --- a/fs/incfs/data_mgmt.c +++ b/fs/incfs/data_mgmt.c @@ -123,6 +123,7 @@ int incfs_realloc_mount_info(struct mount_info *mi, void incfs_free_mount_info(struct mount_info *mi) { + int i; if (!mi) return; @@ -136,8 +137,8 @@ void incfs_free_mount_info(struct mount_info *mi) mutex_destroy(&mi->mi_zstd_workspace_mutex); put_cred(mi->mi_owner); kfree(mi->mi_log.rl_ring_buf); - kfree(mi->log_xattr); - kfree(mi->pending_read_xattr); + for (i = 0; i < ARRAY_SIZE(mi->pseudo_file_xattr); ++i) + kfree(mi->pseudo_file_xattr[i].data); kfree(mi->mi_per_uid_read_timeouts); kfree(mi); } diff --git a/fs/incfs/data_mgmt.h b/fs/incfs/data_mgmt.h index 76b16999f854..77e0f557f4e4 100644 --- a/fs/incfs/data_mgmt.h +++ b/fs/incfs/data_mgmt.h @@ -20,6 +20,7 @@ #include <uapi/linux/incrementalfs.h> #include "internal.h" +#include "pseudo_files.h" #define SEGMENTS_PER_FILE 3 @@ -151,11 +152,8 @@ struct mount_info { /* Temporary buffer for read logger. */ struct read_log mi_log; - void *log_xattr; - size_t log_xattr_size; - - void *pending_read_xattr; - size_t pending_read_xattr_size; + /* SELinux needs special xattrs on our pseudo files */ + struct mem_range pseudo_file_xattr[PSEUDO_FILE_COUNT]; /* A queue of waiters who want to be notified about blocks_written */ wait_queue_head_t mi_blocks_written_notif_wq; diff --git a/fs/incfs/pseudo_files.c b/fs/incfs/pseudo_files.c index c9e29e209754..e9e46ee9a17d 100644 --- a/fs/incfs/pseudo_files.c +++ b/fs/incfs/pseudo_files.c @@ -19,149 +19,15 @@ #include "integrity.h" #include "vfs.h" -#define INCFS_PENDING_READS_INODE 2 -#define INCFS_LOG_INODE 3 -#define INCFS_BLOCKS_WRITTEN_INODE 4 #define READ_WRITE_FILE_MODE 0666 -/******************************************************************************* - * .log pseudo file definition - ******************************************************************************/ -static const char log_file_name[] = INCFS_LOG_FILENAME; -static const struct mem_range log_file_name_range = { - .data = (u8 *)log_file_name, - .len = ARRAY_SIZE(log_file_name) - 1 -}; - -/* State of an open .log file, unique for each file descriptor. */ -struct log_file_state { - struct read_log_state state; -}; - -static ssize_t log_read(struct file *f, char __user *buf, size_t len, - loff_t *ppos) -{ - struct log_file_state *log_state = f->private_data; - struct mount_info *mi = get_mount_info(file_superblock(f)); - int total_reads_collected = 0; - int rl_size; - ssize_t result = 0; - bool report_uid; - unsigned long page = 0; - struct incfs_pending_read_info *reads_buf = NULL; - struct incfs_pending_read_info2 *reads_buf2 = NULL; - size_t record_size; - ssize_t reads_to_collect; - ssize_t reads_per_page; - - if (!mi) - return -EFAULT; - - report_uid = mi->mi_options.report_uid; - record_size = report_uid ? sizeof(*reads_buf2) : sizeof(*reads_buf); - reads_to_collect = len / record_size; - reads_per_page = PAGE_SIZE / record_size; - - rl_size = READ_ONCE(mi->mi_log.rl_size); - if (rl_size == 0) - return 0; - - page = __get_free_page(GFP_NOFS); - if (!page) - return -ENOMEM; - - if (report_uid) - reads_buf2 = (struct incfs_pending_read_info2 *) page; - else - reads_buf = (struct incfs_pending_read_info *) page; - - reads_to_collect = min_t(ssize_t, rl_size, reads_to_collect); - while (reads_to_collect > 0) { - struct read_log_state next_state; - int reads_collected; - - memcpy(&next_state, &log_state->state, sizeof(next_state)); - reads_collected = incfs_collect_logged_reads( - mi, &next_state, reads_buf, reads_buf2, - min_t(ssize_t, reads_to_collect, reads_per_page)); - if (reads_collected <= 0) { - result = total_reads_collected ? - total_reads_collected * record_size : - reads_collected; - goto out; - } - if (copy_to_user(buf, (void *) page, - reads_collected * record_size)) { - result = total_reads_collected ? - total_reads_collected * record_size : - -EFAULT; - goto out; - } - - memcpy(&log_state->state, &next_state, sizeof(next_state)); - total_reads_collected += reads_collected; - buf += reads_collected * record_size; - reads_to_collect -= reads_collected; - } - - result = total_reads_collected * record_size; - *ppos = 0; -out: - free_page(page); - return result; -} - -static __poll_t log_poll(struct file *file, poll_table *wait) -{ - struct log_file_state *log_state = file->private_data; - struct mount_info *mi = get_mount_info(file_superblock(file)); - int count; - __poll_t ret = 0; - - poll_wait(file, &mi->mi_log.ml_notif_wq, wait); - count = incfs_get_uncollected_logs_count(mi, &log_state->state); - if (count >= mi->mi_options.read_log_wakeup_count) - ret = EPOLLIN | EPOLLRDNORM; - - return ret; -} - -static int log_open(struct inode *inode, struct file *file) -{ - struct log_file_state *log_state = NULL; - struct mount_info *mi = get_mount_info(file_superblock(file)); - - log_state = kzalloc(sizeof(*log_state), GFP_NOFS); - if (!log_state) - return -ENOMEM; - - log_state->state = incfs_get_log_state(mi); - file->private_data = log_state; - return 0; -} - -static int log_release(struct inode *inode, struct file *file) -{ - kfree(file->private_data); - return 0; -} - -static const struct file_operations incfs_log_file_ops = { - .read = log_read, - .poll = log_poll, - .open = log_open, - .release = log_release, - .llseek = noop_llseek, -}; +static bool is_pseudo_filename(struct mem_range name); /******************************************************************************* * .pending_reads pseudo file definition ******************************************************************************/ +#define INCFS_PENDING_READS_INODE 2 static const char pending_reads_file_name[] = INCFS_PENDING_READS_FILENAME; -static const struct mem_range pending_reads_file_name_range = { - .data = (u8 *)pending_reads_file_name, - .len = ARRAY_SIZE(pending_reads_file_name) - 1 -}; /* State of an open .pending_reads file, unique for each file descriptor. */ struct pending_reads_state { @@ -344,16 +210,6 @@ static bool incfs_equal_ranges(struct mem_range lhs, struct mem_range rhs) return memcmp(lhs.data, rhs.data, lhs.len) == 0; } -static bool is_pseudo_filename(struct mem_range name) -{ - if (incfs_equal_ranges(pending_reads_file_name_range, name)) - return true; - if (incfs_equal_ranges(log_file_name_range, name)) - return true; - - return false; -} - static int validate_name(char *file_name) { struct mem_range name = range(file_name, strlen(file_name)); @@ -1059,7 +915,7 @@ static long pending_reads_dispatch_ioctl(struct file *f, unsigned int req, } } -static const struct file_operations incfs_pending_read_file_ops = { +static const struct file_operations incfs_pending_reads_file_ops = { .read = pending_reads_read, .poll = pending_reads_poll, .open = pending_reads_open, @@ -1070,13 +926,137 @@ static const struct file_operations incfs_pending_read_file_ops = { }; /******************************************************************************* + * .log pseudo file definition + ******************************************************************************/ +#define INCFS_LOG_INODE 3 +static const char log_file_name[] = INCFS_LOG_FILENAME; + +/* State of an open .log file, unique for each file descriptor. */ +struct log_file_state { + struct read_log_state state; +}; + +static ssize_t log_read(struct file *f, char __user *buf, size_t len, + loff_t *ppos) +{ + struct log_file_state *log_state = f->private_data; + struct mount_info *mi = get_mount_info(file_superblock(f)); + int total_reads_collected = 0; + int rl_size; + ssize_t result = 0; + bool report_uid; + unsigned long page = 0; + struct incfs_pending_read_info *reads_buf = NULL; + struct incfs_pending_read_info2 *reads_buf2 = NULL; + size_t record_size; + ssize_t reads_to_collect; + ssize_t reads_per_page; + + if (!mi) + return -EFAULT; + + report_uid = mi->mi_options.report_uid; + record_size = report_uid ? sizeof(*reads_buf2) : sizeof(*reads_buf); + reads_to_collect = len / record_size; + reads_per_page = PAGE_SIZE / record_size; + + rl_size = READ_ONCE(mi->mi_log.rl_size); + if (rl_size == 0) + return 0; + + page = __get_free_page(GFP_NOFS); + if (!page) + return -ENOMEM; + + if (report_uid) + reads_buf2 = (struct incfs_pending_read_info2 *)page; + else + reads_buf = (struct incfs_pending_read_info *)page; + + reads_to_collect = min_t(ssize_t, rl_size, reads_to_collect); + while (reads_to_collect > 0) { + struct read_log_state next_state; + int reads_collected; + + memcpy(&next_state, &log_state->state, sizeof(next_state)); + reads_collected = incfs_collect_logged_reads( + mi, &next_state, reads_buf, reads_buf2, + min_t(ssize_t, reads_to_collect, reads_per_page)); + if (reads_collected <= 0) { + result = total_reads_collected ? + total_reads_collected * record_size : + reads_collected; + goto out; + } + if (copy_to_user(buf, (void *)page, + reads_collected * record_size)) { + result = total_reads_collected ? + total_reads_collected * record_size : + -EFAULT; + goto out; + } + + memcpy(&log_state->state, &next_state, sizeof(next_state)); + total_reads_collected += reads_collected; + buf += reads_collected * record_size; + reads_to_collect -= reads_collected; + } + + result = total_reads_collected * record_size; + *ppos = 0; +out: + free_page(page); + return result; +} + +static __poll_t log_poll(struct file *file, poll_table *wait) +{ + struct log_file_state *log_state = file->private_data; + struct mount_info *mi = get_mount_info(file_superblock(file)); + int count; + __poll_t ret = 0; + + poll_wait(file, &mi->mi_log.ml_notif_wq, wait); + count = incfs_get_uncollected_logs_count(mi, &log_state->state); + if (count >= mi->mi_options.read_log_wakeup_count) + ret = EPOLLIN | EPOLLRDNORM; + + return ret; +} + +static int log_open(struct inode *inode, struct file *file) +{ + struct log_file_state *log_state = NULL; + struct mount_info *mi = get_mount_info(file_superblock(file)); + + log_state = kzalloc(sizeof(*log_state), GFP_NOFS); + if (!log_state) + return -ENOMEM; + + log_state->state = incfs_get_log_state(mi); + file->private_data = log_state; + return 0; +} + +static int log_release(struct inode *inode, struct file *file) +{ + kfree(file->private_data); + return 0; +} + +static const struct file_operations incfs_log_file_ops = { + .read = log_read, + .poll = log_poll, + .open = log_open, + .release = log_release, + .llseek = noop_llseek, +}; + +/******************************************************************************* * .blocks_written pseudo file definition ******************************************************************************/ +#define INCFS_BLOCKS_WRITTEN_INODE 4 static const char blocks_written_file_name[] = INCFS_BLOCKS_WRITTEN_FILENAME; -static const struct mem_range blocks_written_file_name_range = { - .data = (u8 *)blocks_written_file_name, - .len = ARRAY_SIZE(blocks_written_file_name) - 1 -}; /* State of an open .blocks_written file, unique for each file descriptor. */ struct blocks_written_file_state { @@ -1156,8 +1136,44 @@ static const struct file_operations incfs_blocks_written_file_ops = { /******************************************************************************* * Generic inode lookup functionality ******************************************************************************/ + +const struct mem_range incfs_pseudo_file_names[] = { + { .data = (u8 *)pending_reads_file_name, + .len = ARRAY_SIZE(pending_reads_file_name) - 1 }, + { .data = (u8 *)log_file_name, .len = ARRAY_SIZE(log_file_name) - 1 }, + { .data = (u8 *)blocks_written_file_name, + .len = ARRAY_SIZE(blocks_written_file_name) - 1 } +}; + +const unsigned long incfs_pseudo_file_inodes[] = { INCFS_PENDING_READS_INODE, + INCFS_LOG_INODE, + INCFS_BLOCKS_WRITTEN_INODE }; + +static const struct file_operations *const pseudo_file_operations[] = { + &incfs_pending_reads_file_ops, &incfs_log_file_ops, + &incfs_blocks_written_file_ops +}; + +static bool is_pseudo_filename(struct mem_range name) +{ + int i = 0; + + for (; i < ARRAY_SIZE(incfs_pseudo_file_names); ++i) + if (incfs_equal_ranges(incfs_pseudo_file_names[i], name)) + return true; + return false; +} + static bool get_pseudo_inode(int ino, struct inode *inode) { + int i = 0; + + for (; i < ARRAY_SIZE(incfs_pseudo_file_inodes); ++i) + if (ino == incfs_pseudo_file_inodes[i]) + break; + if (i == ARRAY_SIZE(incfs_pseudo_file_inodes)) + return false; + inode->i_ctime = (struct timespec64){}; inode->i_mtime = inode->i_ctime; inode->i_atime = inode->i_ctime; @@ -1166,23 +1182,8 @@ static bool get_pseudo_inode(int ino, struct inode *inode) inode->i_private = NULL; inode_init_owner(inode, NULL, S_IFREG | READ_WRITE_FILE_MODE); inode->i_op = &incfs_file_inode_ops; - - switch (ino) { - case INCFS_PENDING_READS_INODE: - inode->i_fop = &incfs_pending_read_file_ops; - return true; - - case INCFS_LOG_INODE: - inode->i_fop = &incfs_log_file_ops; - return true; - - case INCFS_BLOCKS_WRITTEN_INODE: - inode->i_fop = &incfs_blocks_written_file_ops; - return true; - - default: - return false; - } + inode->i_fop = pseudo_file_operations[i]; + return true; } struct inode_search { @@ -1230,16 +1231,16 @@ int dir_lookup_pseudo_files(struct super_block *sb, struct dentry *dentry) range((u8 *)dentry->d_name.name, dentry->d_name.len); unsigned long ino; struct inode *inode; + int i = 0; - if (incfs_equal_ranges(pending_reads_file_name_range, name_range)) - ino = INCFS_PENDING_READS_INODE; - else if (incfs_equal_ranges(log_file_name_range, name_range)) - ino = INCFS_LOG_INODE; - else if (incfs_equal_ranges(blocks_written_file_name_range, name_range)) - ino = INCFS_BLOCKS_WRITTEN_INODE; - else + for (; i < ARRAY_SIZE(incfs_pseudo_file_names); ++i) + if (incfs_equal_ranges(incfs_pseudo_file_names[i], name_range)) + break; + if (i == ARRAY_SIZE(incfs_pseudo_file_names)) return -ENOENT; + ino = incfs_pseudo_file_inodes[i]; + inode = fetch_inode(sb, ino); if (IS_ERR(inode)) return PTR_ERR(inode); @@ -1250,32 +1251,15 @@ int dir_lookup_pseudo_files(struct super_block *sb, struct dentry *dentry) int emit_pseudo_files(struct dir_context *ctx) { - if (ctx->pos == 0) { - if (!dir_emit(ctx, pending_reads_file_name, - ARRAY_SIZE(pending_reads_file_name) - 1, - INCFS_PENDING_READS_INODE, DT_REG)) - return -EINVAL; + loff_t i = ctx->pos; - ctx->pos++; - } - - if (ctx->pos == 1) { - if (!dir_emit(ctx, log_file_name, - ARRAY_SIZE(log_file_name) - 1, - INCFS_LOG_INODE, DT_REG)) + for (; i < ARRAY_SIZE(incfs_pseudo_file_names); ++i) { + if (!dir_emit(ctx, incfs_pseudo_file_names[i].data, + incfs_pseudo_file_names[i].len, + incfs_pseudo_file_inodes[i], DT_REG)) return -EINVAL; ctx->pos++; } - - if (ctx->pos == 2) { - if (!dir_emit(ctx, blocks_written_file_name, - ARRAY_SIZE(blocks_written_file_name) - 1, - INCFS_BLOCKS_WRITTEN_INODE, DT_REG)) - return -EINVAL; - - ctx->pos++; - } - return 0; } diff --git a/fs/incfs/pseudo_files.h b/fs/incfs/pseudo_files.h index 358bcabfe49a..188721837253 100644 --- a/fs/incfs/pseudo_files.h +++ b/fs/incfs/pseudo_files.h @@ -6,9 +6,14 @@ #ifndef _INCFS_PSEUDO_FILES_H #define _INCFS_PSEUDO_FILES_H +#include "internal.h" + #define PSEUDO_FILE_COUNT 3 #define INCFS_START_INO_RANGE 10 +extern const struct mem_range incfs_pseudo_file_names[PSEUDO_FILE_COUNT]; +extern const unsigned long incfs_pseudo_file_inodes[PSEUDO_FILE_COUNT]; + int dir_lookup_pseudo_files(struct super_block *sb, struct dentry *dentry); int emit_pseudo_files(struct dir_context *ctx); diff --git a/fs/incfs/vfs.c b/fs/incfs/vfs.c index f0a544b7e431..4208831198ad 100644 --- a/fs/incfs/vfs.c +++ b/fs/incfs/vfs.c @@ -1434,6 +1434,7 @@ static ssize_t incfs_getxattr(struct dentry *d, const char *name, struct mount_info *mi = get_mount_info(d->d_sb); char *stored_value; size_t stored_size; + int i; if (di && di->backing_path.dentry) return vfs_getxattr(di->backing_path.dentry, name, value, size); @@ -1441,16 +1442,14 @@ static ssize_t incfs_getxattr(struct dentry *d, const char *name, if (strcmp(name, "security.selinux")) return -ENODATA; - if (!strcmp(d->d_iname, INCFS_PENDING_READS_FILENAME)) { - stored_value = mi->pending_read_xattr; - stored_size = mi->pending_read_xattr_size; - } else if (!strcmp(d->d_iname, INCFS_LOG_FILENAME)) { - stored_value = mi->log_xattr; - stored_size = mi->log_xattr_size; - } else { + for (i = 0; i < PSEUDO_FILE_COUNT; ++i) + if (!strcmp(d->d_iname, incfs_pseudo_file_names[i].data)) + break; + if (i == PSEUDO_FILE_COUNT) return -ENODATA; - } + stored_value = mi->pseudo_file_xattr[i].data; + stored_size = mi->pseudo_file_xattr[i].len; if (!stored_value) return -ENODATA; @@ -1459,7 +1458,6 @@ static ssize_t incfs_getxattr(struct dentry *d, const char *name, memcpy(value, stored_value, stored_size); return stored_size; - } @@ -1468,8 +1466,9 @@ static ssize_t incfs_setxattr(struct dentry *d, const char *name, { struct dentry_info *di = get_incfs_dentry(d); struct mount_info *mi = get_mount_info(d->d_sb); - void **stored_value; + u8 **stored_value; size_t *stored_size; + int i; if (di && di->backing_path.dentry) return vfs_setxattr(di->backing_path.dentry, name, value, size, @@ -1481,16 +1480,14 @@ static ssize_t incfs_setxattr(struct dentry *d, const char *name, if (size > INCFS_MAX_FILE_ATTR_SIZE) return -E2BIG; - if (!strcmp(d->d_iname, INCFS_PENDING_READS_FILENAME)) { - stored_value = &mi->pending_read_xattr; - stored_size = &mi->pending_read_xattr_size; - } else if (!strcmp(d->d_iname, INCFS_LOG_FILENAME)) { - stored_value = &mi->log_xattr; - stored_size = &mi->log_xattr_size; - } else { + for (i = 0; i < PSEUDO_FILE_COUNT; ++i) + if (!strcmp(d->d_iname, incfs_pseudo_file_names[i].data)) + break; + if (i == PSEUDO_FILE_COUNT) return -ENODATA; - } + stored_value = &mi->pseudo_file_xattr[i].data; + stored_size = &mi->pseudo_file_xattr[i].len; kfree (*stored_value); *stored_value = kzalloc(size, GFP_NOFS); if (!*stored_value) |