summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYurii Zubrytskyi <zyy@google.com>2020-12-21 16:51:45 -0800
committerPaul Lawrence <paullawrence@google.com>2021-01-06 16:57:22 +0000
commitd749bc24dcdeef98baf3555c0768150576c1bd0f (patch)
tree36114993c0ba33832d5be8f3a658f7675a4d6dee
parent9046dcfedf7bb21f18b2fdd21ced1255529a79aa (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.c5
-rw-r--r--fs/incfs/data_mgmt.h8
-rw-r--r--fs/incfs/pseudo_files.c378
-rw-r--r--fs/incfs/pseudo_files.h5
-rw-r--r--fs/incfs/vfs.c33
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)