Commit 10dde05b authored by Vincent Whitchurch's avatar Vincent Whitchurch Committed by Linus Torvalds
Browse files

squashfs: add option to panic on errors

Add an errors=panic mount option to make squashfs trigger a panic when
errors are encountered, similar to several other filesystems.  This allows
a kernel dump to be saved using which the corruption can be analysed and
debugged.

Inspired by a pre-fs_context patch by Anton Eliasson.

Link: https://lkml.kernel.org/r/20210527125019.14511-1-vincent.whitchurch@axis.com


Signed-off-by: default avatarVincent Whitchurch <vincent.whitchurch@axis.com>
Signed-off-by: default avatarPhillip Lougher <phillip@squashfs.org.uk>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent d98e4d95
Loading
Loading
Loading
Loading
+4 −1
Original line number Diff line number Diff line
@@ -226,8 +226,11 @@ int squashfs_read_data(struct super_block *sb, u64 index, int length,
	bio_free_pages(bio);
	bio_put(bio);
out:
	if (res < 0)
	if (res < 0) {
		ERROR("Failed to read block 0x%llx: %d\n", index, res);
		if (msblk->panic_on_errors)
			panic("squashfs read failed");
	}

	return res;
}
+1 −0
Original line number Diff line number Diff line
@@ -65,5 +65,6 @@ struct squashfs_sb_info {
	unsigned int				fragments;
	int					xattr_ids;
	unsigned int				ids;
	bool					panic_on_errors;
};
#endif
+86 −0
Original line number Diff line number Diff line
@@ -18,9 +18,11 @@

#include <linux/fs.h>
#include <linux/fs_context.h>
#include <linux/fs_parser.h>
#include <linux/vfs.h>
#include <linux/slab.h>
#include <linux/mutex.h>
#include <linux/seq_file.h>
#include <linux/pagemap.h>
#include <linux/init.h>
#include <linux/module.h>
@@ -37,6 +39,51 @@
static struct file_system_type squashfs_fs_type;
static const struct super_operations squashfs_super_ops;

enum Opt_errors {
	Opt_errors_continue,
	Opt_errors_panic,
};

enum squashfs_param {
	Opt_errors,
};

struct squashfs_mount_opts {
	enum Opt_errors errors;
};

static const struct constant_table squashfs_param_errors[] = {
	{"continue",   Opt_errors_continue },
	{"panic",      Opt_errors_panic },
	{}
};

static const struct fs_parameter_spec squashfs_fs_parameters[] = {
	fsparam_enum("errors", Opt_errors, squashfs_param_errors),
	{}
};

static int squashfs_parse_param(struct fs_context *fc, struct fs_parameter *param)
{
	struct squashfs_mount_opts *opts = fc->fs_private;
	struct fs_parse_result result;
	int opt;

	opt = fs_parse(fc, squashfs_fs_parameters, param, &result);
	if (opt < 0)
		return opt;

	switch (opt) {
	case Opt_errors:
		opts->errors = result.uint_32;
		break;
	default:
		return -EINVAL;
	}

	return 0;
}

static const struct squashfs_decompressor *supported_squashfs_filesystem(
	struct fs_context *fc,
	short major, short minor, short id)
@@ -67,6 +114,7 @@ static const struct squashfs_decompressor *supported_squashfs_filesystem(

static int squashfs_fill_super(struct super_block *sb, struct fs_context *fc)
{
	struct squashfs_mount_opts *opts = fc->fs_private;
	struct squashfs_sb_info *msblk;
	struct squashfs_super_block *sblk = NULL;
	struct inode *root;
@@ -85,6 +133,8 @@ static int squashfs_fill_super(struct super_block *sb, struct fs_context *fc)
	}
	msblk = sb->s_fs_info;

	msblk->panic_on_errors = (opts->errors == Opt_errors_panic);

	msblk->devblksize = sb_min_blocksize(sb, SQUASHFS_DEVBLK_SIZE);
	msblk->devblksize_log2 = ffz(~msblk->devblksize);

@@ -350,18 +400,52 @@ static int squashfs_get_tree(struct fs_context *fc)

static int squashfs_reconfigure(struct fs_context *fc)
{
	struct super_block *sb = fc->root->d_sb;
	struct squashfs_sb_info *msblk = sb->s_fs_info;
	struct squashfs_mount_opts *opts = fc->fs_private;

	sync_filesystem(fc->root->d_sb);
	fc->sb_flags |= SB_RDONLY;

	msblk->panic_on_errors = (opts->errors == Opt_errors_panic);

	return 0;
}

static void squashfs_free_fs_context(struct fs_context *fc)
{
	kfree(fc->fs_private);
}

static const struct fs_context_operations squashfs_context_ops = {
	.get_tree	= squashfs_get_tree,
	.free		= squashfs_free_fs_context,
	.parse_param	= squashfs_parse_param,
	.reconfigure	= squashfs_reconfigure,
};

static int squashfs_show_options(struct seq_file *s, struct dentry *root)
{
	struct super_block *sb = root->d_sb;
	struct squashfs_sb_info *msblk = sb->s_fs_info;

	if (msblk->panic_on_errors)
		seq_puts(s, ",errors=panic");
	else
		seq_puts(s, ",errors=continue");

	return 0;
}

static int squashfs_init_fs_context(struct fs_context *fc)
{
	struct squashfs_mount_opts *opts;

	opts = kzalloc(sizeof(*opts), GFP_KERNEL);
	if (!opts)
		return -ENOMEM;

	fc->fs_private = opts;
	fc->ops = &squashfs_context_ops;
	return 0;
}
@@ -481,6 +565,7 @@ static struct file_system_type squashfs_fs_type = {
	.owner = THIS_MODULE,
	.name = "squashfs",
	.init_fs_context = squashfs_init_fs_context,
	.parameters = squashfs_fs_parameters,
	.kill_sb = kill_block_super,
	.fs_flags = FS_REQUIRES_DEV
};
@@ -491,6 +576,7 @@ static const struct super_operations squashfs_super_ops = {
	.free_inode = squashfs_free_inode,
	.statfs = squashfs_statfs,
	.put_super = squashfs_put_super,
	.show_options = squashfs_show_options,
};

module_init(init_squashfs_fs);