summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--fs/jffs2/jffs2_fs_sb.h7
-rw-r--r--fs/jffs2/nodemgmt.c42
-rw-r--r--fs/jffs2/super.c17
3 files changed, 66 insertions, 0 deletions
diff --git a/fs/jffs2/jffs2_fs_sb.h b/fs/jffs2/jffs2_fs_sb.h
index 55a0c1dcead..0d00bf26923 100644
--- a/fs/jffs2/jffs2_fs_sb.h
+++ b/fs/jffs2/jffs2_fs_sb.h
@@ -32,6 +32,13 @@ struct jffs2_inodirty;
struct jffs2_mount_opts {
bool override_compr;
unsigned int compr;
+
+ /* The size of the reserved pool. The reserved pool is the JFFS2 flash
+ * space which may only be used by root cannot be used by the other
+ * users. This is implemented simply by means of not allowing the
+ * latter users to write to the file system if the amount if the
+ * available space is less then 'rp_size'. */
+ unsigned int rp_size;
};
/* A struct for the overall file system control. Pointers to
diff --git a/fs/jffs2/nodemgmt.c b/fs/jffs2/nodemgmt.c
index 6784d1e7a7e..0c96eb52c79 100644
--- a/fs/jffs2/nodemgmt.c
+++ b/fs/jffs2/nodemgmt.c
@@ -18,6 +18,37 @@
#include "nodelist.h"
#include "debug.h"
+/*
+ * Check whether the user is allowed to write.
+ */
+static int jffs2_rp_can_write(struct jffs2_sb_info *c)
+{
+ uint32_t avail;
+ struct jffs2_mount_opts *opts = &c->mount_opts;
+
+ avail = c->dirty_size + c->free_size + c->unchecked_size +
+ c->erasing_size - c->resv_blocks_write * c->sector_size
+ - c->nospc_dirty_size;
+
+ if (avail < 2 * opts->rp_size)
+ jffs2_dbg(1, "rpsize %u, dirty_size %u, free_size %u, "
+ "erasing_size %u, unchecked_size %u, "
+ "nr_erasing_blocks %u, avail %u, resrv %u\n",
+ opts->rp_size, c->dirty_size, c->free_size,
+ c->erasing_size, c->unchecked_size,
+ c->nr_erasing_blocks, avail, c->nospc_dirty_size);
+
+ if (avail > opts->rp_size)
+ return 1;
+
+ /* Always allow root */
+ if (capable(CAP_SYS_RESOURCE))
+ return 1;
+
+ jffs2_dbg(1, "forbid writing\n");
+ return 0;
+}
+
/**
* jffs2_reserve_space - request physical space to write nodes to flash
* @c: superblock info
@@ -55,6 +86,15 @@ int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize,
spin_lock(&c->erase_completion_lock);
+ /*
+ * Check if the free space is greater then size of the reserved pool.
+ * If not, only allow root to proceed with writing.
+ */
+ if (prio != ALLOC_DELETION && !jffs2_rp_can_write(c)) {
+ ret = -ENOSPC;
+ goto out;
+ }
+
/* this needs a little more thought (true <tglx> :)) */
while(ret == -EAGAIN) {
while(c->nr_free_blocks + c->nr_erasing_blocks < blocksneeded) {
@@ -158,6 +198,8 @@ int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize,
jffs2_dbg(1, "%s(): ret is %d\n", __func__, ret);
}
}
+
+out:
spin_unlock(&c->erase_completion_lock);
if (!ret)
ret = jffs2_prealloc_raw_node_refs(c, c->nextblock, 1);
diff --git a/fs/jffs2/super.c b/fs/jffs2/super.c
index f9916f312bd..66d44560f75 100644
--- a/fs/jffs2/super.c
+++ b/fs/jffs2/super.c
@@ -105,6 +105,8 @@ static int jffs2_show_options(struct seq_file *s, struct dentry *root)
if (opts->override_compr)
seq_printf(s, ",compr=%s", jffs2_compr_name(opts->compr));
+ if (opts->rp_size)
+ seq_printf(s, ",rp_size=%u", opts->rp_size / 1024);
return 0;
}
@@ -171,15 +173,18 @@ static const struct export_operations jffs2_export_ops = {
* JFFS2 mount options.
*
* Opt_override_compr: override default compressor
+ * Opt_rp_size: size of reserved pool in KiB
* Opt_err: just end of array marker
*/
enum {
Opt_override_compr,
+ Opt_rp_size,
Opt_err,
};
static const match_table_t tokens = {
{Opt_override_compr, "compr=%s"},
+ {Opt_rp_size, "rp_size=%u"},
{Opt_err, NULL},
};
@@ -187,6 +192,7 @@ static int jffs2_parse_options(struct jffs2_sb_info *c, char *data)
{
substring_t args[MAX_OPT_ARGS];
char *p, *name;
+ unsigned int opt;
if (!data)
return 0;
@@ -224,6 +230,17 @@ static int jffs2_parse_options(struct jffs2_sb_info *c, char *data)
kfree(name);
c->mount_opts.override_compr = true;
break;
+ case Opt_rp_size:
+ if (match_int(&args[0], &opt))
+ return -EINVAL;
+ opt *= 1024;
+ if (opt > c->mtd->size) {
+ pr_warn("Too large reserve pool specified, max "
+ "is %llu KB\n", c->mtd->size / 1024);
+ return -EINVAL;
+ }
+ c->mount_opts.rp_size = opt;
+ break;
default:
pr_err("Error: unrecognized mount option '%s' or missing value\n",
p);