|
@@ -27,13 +27,12 @@
|
|
|
* various local functions of those subsystems.
|
|
|
*/
|
|
|
|
|
|
-#define UBIFS_DBG_PRESERVE_UBI
|
|
|
-
|
|
|
-#include "ubifs.h"
|
|
|
#include <linux/module.h>
|
|
|
-#include <linux/moduleparam.h>
|
|
|
#include <linux/debugfs.h>
|
|
|
#include <linux/math64.h>
|
|
|
+#include <linux/uaccess.h>
|
|
|
+#include <linux/random.h>
|
|
|
+#include "ubifs.h"
|
|
|
|
|
|
#ifdef CONFIG_UBIFS_FS_DEBUG
|
|
|
|
|
@@ -42,15 +41,6 @@ DEFINE_SPINLOCK(dbg_lock);
|
|
|
static char dbg_key_buf0[128];
|
|
|
static char dbg_key_buf1[128];
|
|
|
|
|
|
-unsigned int ubifs_chk_flags;
|
|
|
-unsigned int ubifs_tst_flags;
|
|
|
-
|
|
|
-module_param_named(debug_chks, ubifs_chk_flags, uint, S_IRUGO | S_IWUSR);
|
|
|
-module_param_named(debug_tsts, ubifs_tst_flags, uint, S_IRUGO | S_IWUSR);
|
|
|
-
|
|
|
-MODULE_PARM_DESC(debug_chks, "Debug check flags");
|
|
|
-MODULE_PARM_DESC(debug_tsts, "Debug special test flags");
|
|
|
-
|
|
|
static const char *get_key_fmt(int fmt)
|
|
|
{
|
|
|
switch (fmt) {
|
|
@@ -91,6 +81,28 @@ static const char *get_key_type(int type)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+static const char *get_dent_type(int type)
|
|
|
+{
|
|
|
+ switch (type) {
|
|
|
+ case UBIFS_ITYPE_REG:
|
|
|
+ return "file";
|
|
|
+ case UBIFS_ITYPE_DIR:
|
|
|
+ return "dir";
|
|
|
+ case UBIFS_ITYPE_LNK:
|
|
|
+ return "symlink";
|
|
|
+ case UBIFS_ITYPE_BLK:
|
|
|
+ return "blkdev";
|
|
|
+ case UBIFS_ITYPE_CHR:
|
|
|
+ return "char dev";
|
|
|
+ case UBIFS_ITYPE_FIFO:
|
|
|
+ return "fifo";
|
|
|
+ case UBIFS_ITYPE_SOCK:
|
|
|
+ return "socket";
|
|
|
+ default:
|
|
|
+ return "unknown/invalid type";
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
static void sprintf_key(const struct ubifs_info *c, const union ubifs_key *key,
|
|
|
char *buffer)
|
|
|
{
|
|
@@ -234,9 +246,13 @@ static void dump_ch(const struct ubifs_ch *ch)
|
|
|
printk(KERN_DEBUG "\tlen %u\n", le32_to_cpu(ch->len));
|
|
|
}
|
|
|
|
|
|
-void dbg_dump_inode(const struct ubifs_info *c, const struct inode *inode)
|
|
|
+void dbg_dump_inode(struct ubifs_info *c, const struct inode *inode)
|
|
|
{
|
|
|
const struct ubifs_inode *ui = ubifs_inode(inode);
|
|
|
+ struct qstr nm = { .name = NULL };
|
|
|
+ union ubifs_key key;
|
|
|
+ struct ubifs_dent_node *dent, *pdent = NULL;
|
|
|
+ int count = 2;
|
|
|
|
|
|
printk(KERN_DEBUG "Dump in-memory inode:");
|
|
|
printk(KERN_DEBUG "\tinode %lu\n", inode->i_ino);
|
|
@@ -270,6 +286,32 @@ void dbg_dump_inode(const struct ubifs_info *c, const struct inode *inode)
|
|
|
printk(KERN_DEBUG "\tlast_page_read %lu\n", ui->last_page_read);
|
|
|
printk(KERN_DEBUG "\tread_in_a_row %lu\n", ui->read_in_a_row);
|
|
|
printk(KERN_DEBUG "\tdata_len %d\n", ui->data_len);
|
|
|
+
|
|
|
+ if (!S_ISDIR(inode->i_mode))
|
|
|
+ return;
|
|
|
+
|
|
|
+ printk(KERN_DEBUG "List of directory entries:\n");
|
|
|
+ ubifs_assert(!mutex_is_locked(&c->tnc_mutex));
|
|
|
+
|
|
|
+ lowest_dent_key(c, &key, inode->i_ino);
|
|
|
+ while (1) {
|
|
|
+ dent = ubifs_tnc_next_ent(c, &key, &nm);
|
|
|
+ if (IS_ERR(dent)) {
|
|
|
+ if (PTR_ERR(dent) != -ENOENT)
|
|
|
+ printk(KERN_DEBUG "error %ld\n", PTR_ERR(dent));
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ printk(KERN_DEBUG "\t%d: %s (%s)\n",
|
|
|
+ count++, dent->name, get_dent_type(dent->type));
|
|
|
+
|
|
|
+ nm.name = dent->name;
|
|
|
+ nm.len = le16_to_cpu(dent->nlen);
|
|
|
+ kfree(pdent);
|
|
|
+ pdent = dent;
|
|
|
+ key_read(c, &dent->key, &key);
|
|
|
+ }
|
|
|
+ kfree(pdent);
|
|
|
}
|
|
|
|
|
|
void dbg_dump_node(const struct ubifs_info *c, const void *node)
|
|
@@ -278,7 +320,7 @@ void dbg_dump_node(const struct ubifs_info *c, const void *node)
|
|
|
union ubifs_key key;
|
|
|
const struct ubifs_ch *ch = node;
|
|
|
|
|
|
- if (dbg_failure_mode)
|
|
|
+ if (dbg_is_tst_rcvry(c))
|
|
|
return;
|
|
|
|
|
|
/* If the magic is incorrect, just hexdump the first bytes */
|
|
@@ -834,7 +876,7 @@ void dbg_dump_leb(const struct ubifs_info *c, int lnum)
|
|
|
struct ubifs_scan_node *snod;
|
|
|
void *buf;
|
|
|
|
|
|
- if (dbg_failure_mode)
|
|
|
+ if (dbg_is_tst_rcvry(c))
|
|
|
return;
|
|
|
|
|
|
printk(KERN_DEBUG "(pid %d) start dumping LEB %d\n",
|
|
@@ -1080,6 +1122,7 @@ out:
|
|
|
|
|
|
/**
|
|
|
* dbg_check_synced_i_size - check synchronized inode size.
|
|
|
+ * @c: UBIFS file-system description object
|
|
|
* @inode: inode to check
|
|
|
*
|
|
|
* If inode is clean, synchronized inode size has to be equivalent to current
|
|
@@ -1087,12 +1130,12 @@ out:
|
|
|
* has to be locked). Returns %0 if synchronized inode size if correct, and
|
|
|
* %-EINVAL if not.
|
|
|
*/
|
|
|
-int dbg_check_synced_i_size(struct inode *inode)
|
|
|
+int dbg_check_synced_i_size(const struct ubifs_info *c, struct inode *inode)
|
|
|
{
|
|
|
int err = 0;
|
|
|
struct ubifs_inode *ui = ubifs_inode(inode);
|
|
|
|
|
|
- if (!(ubifs_chk_flags & UBIFS_CHK_GEN))
|
|
|
+ if (!dbg_is_chk_gen(c))
|
|
|
return 0;
|
|
|
if (!S_ISREG(inode->i_mode))
|
|
|
return 0;
|
|
@@ -1125,7 +1168,7 @@ int dbg_check_synced_i_size(struct inode *inode)
|
|
|
* Note, it is good idea to make sure the @dir->i_mutex is locked before
|
|
|
* calling this function.
|
|
|
*/
|
|
|
-int dbg_check_dir_size(struct ubifs_info *c, const struct inode *dir)
|
|
|
+int dbg_check_dir(struct ubifs_info *c, const struct inode *dir)
|
|
|
{
|
|
|
unsigned int nlink = 2;
|
|
|
union ubifs_key key;
|
|
@@ -1133,7 +1176,7 @@ int dbg_check_dir_size(struct ubifs_info *c, const struct inode *dir)
|
|
|
struct qstr nm = { .name = NULL };
|
|
|
loff_t size = UBIFS_INO_NODE_SZ;
|
|
|
|
|
|
- if (!(ubifs_chk_flags & UBIFS_CHK_GEN))
|
|
|
+ if (!dbg_is_chk_gen(c))
|
|
|
return 0;
|
|
|
|
|
|
if (!S_ISDIR(dir->i_mode))
|
|
@@ -1167,12 +1210,14 @@ int dbg_check_dir_size(struct ubifs_info *c, const struct inode *dir)
|
|
|
"but calculated size is %llu", dir->i_ino,
|
|
|
(unsigned long long)i_size_read(dir),
|
|
|
(unsigned long long)size);
|
|
|
+ dbg_dump_inode(c, dir);
|
|
|
dump_stack();
|
|
|
return -EINVAL;
|
|
|
}
|
|
|
if (dir->i_nlink != nlink) {
|
|
|
ubifs_err("directory inode %lu has nlink %u, but calculated "
|
|
|
"nlink is %u", dir->i_ino, dir->i_nlink, nlink);
|
|
|
+ dbg_dump_inode(c, dir);
|
|
|
dump_stack();
|
|
|
return -EINVAL;
|
|
|
}
|
|
@@ -1489,7 +1534,7 @@ int dbg_check_tnc(struct ubifs_info *c, int extra)
|
|
|
long clean_cnt = 0, dirty_cnt = 0;
|
|
|
int err, last;
|
|
|
|
|
|
- if (!(ubifs_chk_flags & UBIFS_CHK_TNC))
|
|
|
+ if (!dbg_is_chk_index(c))
|
|
|
return 0;
|
|
|
|
|
|
ubifs_assert(mutex_is_locked(&c->tnc_mutex));
|
|
@@ -1736,7 +1781,7 @@ int dbg_check_idx_size(struct ubifs_info *c, long long idx_size)
|
|
|
int err;
|
|
|
long long calc = 0;
|
|
|
|
|
|
- if (!(ubifs_chk_flags & UBIFS_CHK_IDX_SZ))
|
|
|
+ if (!dbg_is_chk_index(c))
|
|
|
return 0;
|
|
|
|
|
|
err = dbg_walk_index(c, NULL, add_size, &calc);
|
|
@@ -2312,7 +2357,7 @@ int dbg_check_filesystem(struct ubifs_info *c)
|
|
|
int err;
|
|
|
struct fsck_data fsckd;
|
|
|
|
|
|
- if (!(ubifs_chk_flags & UBIFS_CHK_FS))
|
|
|
+ if (!dbg_is_chk_fs(c))
|
|
|
return 0;
|
|
|
|
|
|
fsckd.inodes = RB_ROOT;
|
|
@@ -2347,7 +2392,7 @@ int dbg_check_data_nodes_order(struct ubifs_info *c, struct list_head *head)
|
|
|
struct list_head *cur;
|
|
|
struct ubifs_scan_node *sa, *sb;
|
|
|
|
|
|
- if (!(ubifs_chk_flags & UBIFS_CHK_GEN))
|
|
|
+ if (!dbg_is_chk_gen(c))
|
|
|
return 0;
|
|
|
|
|
|
for (cur = head->next; cur->next != head; cur = cur->next) {
|
|
@@ -2414,7 +2459,7 @@ int dbg_check_nondata_nodes_order(struct ubifs_info *c, struct list_head *head)
|
|
|
struct list_head *cur;
|
|
|
struct ubifs_scan_node *sa, *sb;
|
|
|
|
|
|
- if (!(ubifs_chk_flags & UBIFS_CHK_GEN))
|
|
|
+ if (!dbg_is_chk_gen(c))
|
|
|
return 0;
|
|
|
|
|
|
for (cur = head->next; cur->next != head; cur = cur->next) {
|
|
@@ -2491,214 +2536,141 @@ error_dump:
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-int dbg_force_in_the_gaps(void)
|
|
|
+static inline int chance(unsigned int n, unsigned int out_of)
|
|
|
{
|
|
|
- if (!(ubifs_chk_flags & UBIFS_CHK_GEN))
|
|
|
- return 0;
|
|
|
+ return !!((random32() % out_of) + 1 <= n);
|
|
|
|
|
|
- return !(random32() & 7);
|
|
|
}
|
|
|
|
|
|
-/* Failure mode for recovery testing */
|
|
|
-
|
|
|
-#define chance(n, d) (simple_rand() <= (n) * 32768LL / (d))
|
|
|
-
|
|
|
-struct failure_mode_info {
|
|
|
- struct list_head list;
|
|
|
- struct ubifs_info *c;
|
|
|
-};
|
|
|
-
|
|
|
-static LIST_HEAD(fmi_list);
|
|
|
-static DEFINE_SPINLOCK(fmi_lock);
|
|
|
-
|
|
|
-static unsigned int next;
|
|
|
-
|
|
|
-static int simple_rand(void)
|
|
|
-{
|
|
|
- if (next == 0)
|
|
|
- next = current->pid;
|
|
|
- next = next * 1103515245 + 12345;
|
|
|
- return (next >> 16) & 32767;
|
|
|
-}
|
|
|
-
|
|
|
-static void failure_mode_init(struct ubifs_info *c)
|
|
|
-{
|
|
|
- struct failure_mode_info *fmi;
|
|
|
-
|
|
|
- fmi = kmalloc(sizeof(struct failure_mode_info), GFP_NOFS);
|
|
|
- if (!fmi) {
|
|
|
- ubifs_err("Failed to register failure mode - no memory");
|
|
|
- return;
|
|
|
- }
|
|
|
- fmi->c = c;
|
|
|
- spin_lock(&fmi_lock);
|
|
|
- list_add_tail(&fmi->list, &fmi_list);
|
|
|
- spin_unlock(&fmi_lock);
|
|
|
-}
|
|
|
-
|
|
|
-static void failure_mode_exit(struct ubifs_info *c)
|
|
|
+static int power_cut_emulated(struct ubifs_info *c, int lnum, int write)
|
|
|
{
|
|
|
- struct failure_mode_info *fmi, *tmp;
|
|
|
-
|
|
|
- spin_lock(&fmi_lock);
|
|
|
- list_for_each_entry_safe(fmi, tmp, &fmi_list, list)
|
|
|
- if (fmi->c == c) {
|
|
|
- list_del(&fmi->list);
|
|
|
- kfree(fmi);
|
|
|
- }
|
|
|
- spin_unlock(&fmi_lock);
|
|
|
-}
|
|
|
-
|
|
|
-static struct ubifs_info *dbg_find_info(struct ubi_volume_desc *desc)
|
|
|
-{
|
|
|
- struct failure_mode_info *fmi;
|
|
|
-
|
|
|
- spin_lock(&fmi_lock);
|
|
|
- list_for_each_entry(fmi, &fmi_list, list)
|
|
|
- if (fmi->c->ubi == desc) {
|
|
|
- struct ubifs_info *c = fmi->c;
|
|
|
-
|
|
|
- spin_unlock(&fmi_lock);
|
|
|
- return c;
|
|
|
- }
|
|
|
- spin_unlock(&fmi_lock);
|
|
|
- return NULL;
|
|
|
-}
|
|
|
-
|
|
|
-static int in_failure_mode(struct ubi_volume_desc *desc)
|
|
|
-{
|
|
|
- struct ubifs_info *c = dbg_find_info(desc);
|
|
|
-
|
|
|
- if (c && dbg_failure_mode)
|
|
|
- return c->dbg->failure_mode;
|
|
|
- return 0;
|
|
|
-}
|
|
|
+ struct ubifs_debug_info *d = c->dbg;
|
|
|
|
|
|
-static int do_fail(struct ubi_volume_desc *desc, int lnum, int write)
|
|
|
-{
|
|
|
- struct ubifs_info *c = dbg_find_info(desc);
|
|
|
- struct ubifs_debug_info *d;
|
|
|
+ ubifs_assert(dbg_is_tst_rcvry(c));
|
|
|
|
|
|
- if (!c || !dbg_failure_mode)
|
|
|
- return 0;
|
|
|
- d = c->dbg;
|
|
|
- if (d->failure_mode)
|
|
|
- return 1;
|
|
|
- if (!d->fail_cnt) {
|
|
|
- /* First call - decide delay to failure */
|
|
|
+ if (!d->pc_cnt) {
|
|
|
+ /* First call - decide delay to the power cut */
|
|
|
if (chance(1, 2)) {
|
|
|
- unsigned int delay = 1 << (simple_rand() >> 11);
|
|
|
+ unsigned long delay;
|
|
|
|
|
|
if (chance(1, 2)) {
|
|
|
- d->fail_delay = 1;
|
|
|
- d->fail_timeout = jiffies +
|
|
|
- msecs_to_jiffies(delay);
|
|
|
- dbg_rcvry("failing after %ums", delay);
|
|
|
+ d->pc_delay = 1;
|
|
|
+ /* Fail withing 1 minute */
|
|
|
+ delay = random32() % 60000;
|
|
|
+ d->pc_timeout = jiffies;
|
|
|
+ d->pc_timeout += msecs_to_jiffies(delay);
|
|
|
+ ubifs_warn("failing after %lums", delay);
|
|
|
} else {
|
|
|
- d->fail_delay = 2;
|
|
|
- d->fail_cnt_max = delay;
|
|
|
- dbg_rcvry("failing after %u calls", delay);
|
|
|
+ d->pc_delay = 2;
|
|
|
+ delay = random32() % 10000;
|
|
|
+ /* Fail within 10000 operations */
|
|
|
+ d->pc_cnt_max = delay;
|
|
|
+ ubifs_warn("failing after %lu calls", delay);
|
|
|
}
|
|
|
}
|
|
|
- d->fail_cnt += 1;
|
|
|
+
|
|
|
+ d->pc_cnt += 1;
|
|
|
}
|
|
|
+
|
|
|
/* Determine if failure delay has expired */
|
|
|
- if (d->fail_delay == 1) {
|
|
|
- if (time_before(jiffies, d->fail_timeout))
|
|
|
+ if (d->pc_delay == 1 && time_before(jiffies, d->pc_timeout))
|
|
|
return 0;
|
|
|
- } else if (d->fail_delay == 2)
|
|
|
- if (d->fail_cnt++ < d->fail_cnt_max)
|
|
|
+ if (d->pc_delay == 2 && d->pc_cnt++ < d->pc_cnt_max)
|
|
|
return 0;
|
|
|
+
|
|
|
if (lnum == UBIFS_SB_LNUM) {
|
|
|
- if (write) {
|
|
|
- if (chance(1, 2))
|
|
|
- return 0;
|
|
|
- } else if (chance(19, 20))
|
|
|
+ if (write && chance(1, 2))
|
|
|
+ return 0;
|
|
|
+ if (chance(19, 20))
|
|
|
return 0;
|
|
|
- dbg_rcvry("failing in super block LEB %d", lnum);
|
|
|
+ ubifs_warn("failing in super block LEB %d", lnum);
|
|
|
} else if (lnum == UBIFS_MST_LNUM || lnum == UBIFS_MST_LNUM + 1) {
|
|
|
if (chance(19, 20))
|
|
|
return 0;
|
|
|
- dbg_rcvry("failing in master LEB %d", lnum);
|
|
|
+ ubifs_warn("failing in master LEB %d", lnum);
|
|
|
} else if (lnum >= UBIFS_LOG_LNUM && lnum <= c->log_last) {
|
|
|
- if (write) {
|
|
|
- if (chance(99, 100))
|
|
|
- return 0;
|
|
|
- } else if (chance(399, 400))
|
|
|
+ if (write && chance(99, 100))
|
|
|
return 0;
|
|
|
- dbg_rcvry("failing in log LEB %d", lnum);
|
|
|
+ if (chance(399, 400))
|
|
|
+ return 0;
|
|
|
+ ubifs_warn("failing in log LEB %d", lnum);
|
|
|
} else if (lnum >= c->lpt_first && lnum <= c->lpt_last) {
|
|
|
- if (write) {
|
|
|
- if (chance(7, 8))
|
|
|
- return 0;
|
|
|
- } else if (chance(19, 20))
|
|
|
+ if (write && chance(7, 8))
|
|
|
return 0;
|
|
|
- dbg_rcvry("failing in LPT LEB %d", lnum);
|
|
|
+ if (chance(19, 20))
|
|
|
+ return 0;
|
|
|
+ ubifs_warn("failing in LPT LEB %d", lnum);
|
|
|
} else if (lnum >= c->orph_first && lnum <= c->orph_last) {
|
|
|
- if (write) {
|
|
|
- if (chance(1, 2))
|
|
|
- return 0;
|
|
|
- } else if (chance(9, 10))
|
|
|
+ if (write && chance(1, 2))
|
|
|
+ return 0;
|
|
|
+ if (chance(9, 10))
|
|
|
return 0;
|
|
|
- dbg_rcvry("failing in orphan LEB %d", lnum);
|
|
|
+ ubifs_warn("failing in orphan LEB %d", lnum);
|
|
|
} else if (lnum == c->ihead_lnum) {
|
|
|
if (chance(99, 100))
|
|
|
return 0;
|
|
|
- dbg_rcvry("failing in index head LEB %d", lnum);
|
|
|
+ ubifs_warn("failing in index head LEB %d", lnum);
|
|
|
} else if (c->jheads && lnum == c->jheads[GCHD].wbuf.lnum) {
|
|
|
if (chance(9, 10))
|
|
|
return 0;
|
|
|
- dbg_rcvry("failing in GC head LEB %d", lnum);
|
|
|
+ ubifs_warn("failing in GC head LEB %d", lnum);
|
|
|
} else if (write && !RB_EMPTY_ROOT(&c->buds) &&
|
|
|
!ubifs_search_bud(c, lnum)) {
|
|
|
if (chance(19, 20))
|
|
|
return 0;
|
|
|
- dbg_rcvry("failing in non-bud LEB %d", lnum);
|
|
|
+ ubifs_warn("failing in non-bud LEB %d", lnum);
|
|
|
} else if (c->cmt_state == COMMIT_RUNNING_BACKGROUND ||
|
|
|
c->cmt_state == COMMIT_RUNNING_REQUIRED) {
|
|
|
if (chance(999, 1000))
|
|
|
return 0;
|
|
|
- dbg_rcvry("failing in bud LEB %d commit running", lnum);
|
|
|
+ ubifs_warn("failing in bud LEB %d commit running", lnum);
|
|
|
} else {
|
|
|
if (chance(9999, 10000))
|
|
|
return 0;
|
|
|
- dbg_rcvry("failing in bud LEB %d commit not running", lnum);
|
|
|
+ ubifs_warn("failing in bud LEB %d commit not running", lnum);
|
|
|
}
|
|
|
- ubifs_err("*** SETTING FAILURE MODE ON (LEB %d) ***", lnum);
|
|
|
- d->failure_mode = 1;
|
|
|
+
|
|
|
+ d->pc_happened = 1;
|
|
|
+ ubifs_warn("========== Power cut emulated ==========");
|
|
|
dump_stack();
|
|
|
return 1;
|
|
|
}
|
|
|
|
|
|
-static void cut_data(const void *buf, int len)
|
|
|
+static void cut_data(const void *buf, unsigned int len)
|
|
|
{
|
|
|
- int flen, i;
|
|
|
+ unsigned int from, to, i, ffs = chance(1, 2);
|
|
|
unsigned char *p = (void *)buf;
|
|
|
|
|
|
- flen = (len * (long long)simple_rand()) >> 15;
|
|
|
- for (i = flen; i < len; i++)
|
|
|
- p[i] = 0xff;
|
|
|
-}
|
|
|
+ from = random32() % (len + 1);
|
|
|
+ if (chance(1, 2))
|
|
|
+ to = random32() % (len - from + 1);
|
|
|
+ else
|
|
|
+ to = len;
|
|
|
|
|
|
-int dbg_leb_read(struct ubi_volume_desc *desc, int lnum, char *buf, int offset,
|
|
|
- int len, int check)
|
|
|
-{
|
|
|
- if (in_failure_mode(desc))
|
|
|
- return -EROFS;
|
|
|
- return ubi_leb_read(desc, lnum, buf, offset, len, check);
|
|
|
+ if (from < to)
|
|
|
+ ubifs_warn("filled bytes %u-%u with %s", from, to - 1,
|
|
|
+ ffs ? "0xFFs" : "random data");
|
|
|
+
|
|
|
+ if (ffs)
|
|
|
+ for (i = from; i < to; i++)
|
|
|
+ p[i] = 0xFF;
|
|
|
+ else
|
|
|
+ for (i = from; i < to; i++)
|
|
|
+ p[i] = random32() % 0x100;
|
|
|
}
|
|
|
|
|
|
-int dbg_leb_write(struct ubi_volume_desc *desc, int lnum, const void *buf,
|
|
|
- int offset, int len, int dtype)
|
|
|
+int dbg_leb_write(struct ubifs_info *c, int lnum, const void *buf,
|
|
|
+ int offs, int len, int dtype)
|
|
|
{
|
|
|
int err, failing;
|
|
|
|
|
|
- if (in_failure_mode(desc))
|
|
|
+ if (c->dbg->pc_happened)
|
|
|
return -EROFS;
|
|
|
- failing = do_fail(desc, lnum, 1);
|
|
|
+
|
|
|
+ failing = power_cut_emulated(c, lnum, 1);
|
|
|
if (failing)
|
|
|
cut_data(buf, len);
|
|
|
- err = ubi_leb_write(desc, lnum, buf, offset, len, dtype);
|
|
|
+ err = ubi_leb_write(c->ubi, lnum, buf, offs, len, dtype);
|
|
|
if (err)
|
|
|
return err;
|
|
|
if (failing)
|
|
@@ -2706,162 +2678,207 @@ int dbg_leb_write(struct ubi_volume_desc *desc, int lnum, const void *buf,
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-int dbg_leb_change(struct ubi_volume_desc *desc, int lnum, const void *buf,
|
|
|
+int dbg_leb_change(struct ubifs_info *c, int lnum, const void *buf,
|
|
|
int len, int dtype)
|
|
|
{
|
|
|
int err;
|
|
|
|
|
|
- if (do_fail(desc, lnum, 1))
|
|
|
+ if (c->dbg->pc_happened)
|
|
|
return -EROFS;
|
|
|
- err = ubi_leb_change(desc, lnum, buf, len, dtype);
|
|
|
+ if (power_cut_emulated(c, lnum, 1))
|
|
|
+ return -EROFS;
|
|
|
+ err = ubi_leb_change(c->ubi, lnum, buf, len, dtype);
|
|
|
if (err)
|
|
|
return err;
|
|
|
- if (do_fail(desc, lnum, 1))
|
|
|
+ if (power_cut_emulated(c, lnum, 1))
|
|
|
return -EROFS;
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-int dbg_leb_erase(struct ubi_volume_desc *desc, int lnum)
|
|
|
+int dbg_leb_unmap(struct ubifs_info *c, int lnum)
|
|
|
{
|
|
|
int err;
|
|
|
|
|
|
- if (do_fail(desc, lnum, 0))
|
|
|
+ if (c->dbg->pc_happened)
|
|
|
+ return -EROFS;
|
|
|
+ if (power_cut_emulated(c, lnum, 0))
|
|
|
return -EROFS;
|
|
|
- err = ubi_leb_erase(desc, lnum);
|
|
|
+ err = ubi_leb_unmap(c->ubi, lnum);
|
|
|
if (err)
|
|
|
return err;
|
|
|
- if (do_fail(desc, lnum, 0))
|
|
|
+ if (power_cut_emulated(c, lnum, 0))
|
|
|
return -EROFS;
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-int dbg_leb_unmap(struct ubi_volume_desc *desc, int lnum)
|
|
|
+int dbg_leb_map(struct ubifs_info *c, int lnum, int dtype)
|
|
|
{
|
|
|
int err;
|
|
|
|
|
|
- if (do_fail(desc, lnum, 0))
|
|
|
+ if (c->dbg->pc_happened)
|
|
|
return -EROFS;
|
|
|
- err = ubi_leb_unmap(desc, lnum);
|
|
|
+ if (power_cut_emulated(c, lnum, 0))
|
|
|
+ return -EROFS;
|
|
|
+ err = ubi_leb_map(c->ubi, lnum, dtype);
|
|
|
if (err)
|
|
|
return err;
|
|
|
- if (do_fail(desc, lnum, 0))
|
|
|
+ if (power_cut_emulated(c, lnum, 0))
|
|
|
return -EROFS;
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-int dbg_is_mapped(struct ubi_volume_desc *desc, int lnum)
|
|
|
-{
|
|
|
- if (in_failure_mode(desc))
|
|
|
- return -EROFS;
|
|
|
- return ubi_is_mapped(desc, lnum);
|
|
|
-}
|
|
|
+/*
|
|
|
+ * Root directory for UBIFS stuff in debugfs. Contains sub-directories which
|
|
|
+ * contain the stuff specific to particular file-system mounts.
|
|
|
+ */
|
|
|
+static struct dentry *dfs_rootdir;
|
|
|
|
|
|
-int dbg_leb_map(struct ubi_volume_desc *desc, int lnum, int dtype)
|
|
|
+static int dfs_file_open(struct inode *inode, struct file *file)
|
|
|
{
|
|
|
- int err;
|
|
|
-
|
|
|
- if (do_fail(desc, lnum, 0))
|
|
|
- return -EROFS;
|
|
|
- err = ubi_leb_map(desc, lnum, dtype);
|
|
|
- if (err)
|
|
|
- return err;
|
|
|
- if (do_fail(desc, lnum, 0))
|
|
|
- return -EROFS;
|
|
|
- return 0;
|
|
|
+ file->private_data = inode->i_private;
|
|
|
+ return nonseekable_open(inode, file);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * ubifs_debugging_init - initialize UBIFS debugging.
|
|
|
- * @c: UBIFS file-system description object
|
|
|
+ * provide_user_output - provide output to the user reading a debugfs file.
|
|
|
+ * @val: boolean value for the answer
|
|
|
+ * @u: the buffer to store the answer at
|
|
|
+ * @count: size of the buffer
|
|
|
+ * @ppos: position in the @u output buffer
|
|
|
*
|
|
|
- * This function initializes debugging-related data for the file system.
|
|
|
- * Returns zero in case of success and a negative error code in case of
|
|
|
+ * This is a simple helper function which stores @val boolean value in the user
|
|
|
+ * buffer when the user reads one of UBIFS debugfs files. Returns amount of
|
|
|
+ * bytes written to @u in case of success and a negative error code in case of
|
|
|
* failure.
|
|
|
*/
|
|
|
-int ubifs_debugging_init(struct ubifs_info *c)
|
|
|
+static int provide_user_output(int val, char __user *u, size_t count,
|
|
|
+ loff_t *ppos)
|
|
|
{
|
|
|
- c->dbg = kzalloc(sizeof(struct ubifs_debug_info), GFP_KERNEL);
|
|
|
- if (!c->dbg)
|
|
|
- return -ENOMEM;
|
|
|
+ char buf[3];
|
|
|
|
|
|
- failure_mode_init(c);
|
|
|
- return 0;
|
|
|
+ if (val)
|
|
|
+ buf[0] = '1';
|
|
|
+ else
|
|
|
+ buf[0] = '0';
|
|
|
+ buf[1] = '\n';
|
|
|
+ buf[2] = 0x00;
|
|
|
+
|
|
|
+ return simple_read_from_buffer(u, count, ppos, buf, 2);
|
|
|
}
|
|
|
|
|
|
-/**
|
|
|
- * ubifs_debugging_exit - free debugging data.
|
|
|
- * @c: UBIFS file-system description object
|
|
|
- */
|
|
|
-void ubifs_debugging_exit(struct ubifs_info *c)
|
|
|
+static ssize_t dfs_file_read(struct file *file, char __user *u, size_t count,
|
|
|
+ loff_t *ppos)
|
|
|
{
|
|
|
- failure_mode_exit(c);
|
|
|
- kfree(c->dbg);
|
|
|
-}
|
|
|
+ struct dentry *dent = file->f_path.dentry;
|
|
|
+ struct ubifs_info *c = file->private_data;
|
|
|
+ struct ubifs_debug_info *d = c->dbg;
|
|
|
+ int val;
|
|
|
+
|
|
|
+ if (dent == d->dfs_chk_gen)
|
|
|
+ val = d->chk_gen;
|
|
|
+ else if (dent == d->dfs_chk_index)
|
|
|
+ val = d->chk_index;
|
|
|
+ else if (dent == d->dfs_chk_orph)
|
|
|
+ val = d->chk_orph;
|
|
|
+ else if (dent == d->dfs_chk_lprops)
|
|
|
+ val = d->chk_lprops;
|
|
|
+ else if (dent == d->dfs_chk_fs)
|
|
|
+ val = d->chk_fs;
|
|
|
+ else if (dent == d->dfs_tst_rcvry)
|
|
|
+ val = d->tst_rcvry;
|
|
|
+ else
|
|
|
+ return -EINVAL;
|
|
|
|
|
|
-/*
|
|
|
- * Root directory for UBIFS stuff in debugfs. Contains sub-directories which
|
|
|
- * contain the stuff specific to particular file-system mounts.
|
|
|
- */
|
|
|
-static struct dentry *dfs_rootdir;
|
|
|
+ return provide_user_output(val, u, count, ppos);
|
|
|
+}
|
|
|
|
|
|
/**
|
|
|
- * dbg_debugfs_init - initialize debugfs file-system.
|
|
|
+ * interpret_user_input - interpret user debugfs file input.
|
|
|
+ * @u: user-provided buffer with the input
|
|
|
+ * @count: buffer size
|
|
|
*
|
|
|
- * UBIFS uses debugfs file-system to expose various debugging knobs to
|
|
|
- * user-space. This function creates "ubifs" directory in the debugfs
|
|
|
- * file-system. Returns zero in case of success and a negative error code in
|
|
|
- * case of failure.
|
|
|
+ * This is a helper function which interpret user input to a boolean UBIFS
|
|
|
+ * debugfs file. Returns %0 or %1 in case of success and a negative error code
|
|
|
+ * in case of failure.
|
|
|
*/
|
|
|
-int dbg_debugfs_init(void)
|
|
|
+static int interpret_user_input(const char __user *u, size_t count)
|
|
|
{
|
|
|
- dfs_rootdir = debugfs_create_dir("ubifs", NULL);
|
|
|
- if (IS_ERR(dfs_rootdir)) {
|
|
|
- int err = PTR_ERR(dfs_rootdir);
|
|
|
- ubifs_err("cannot create \"ubifs\" debugfs directory, "
|
|
|
- "error %d\n", err);
|
|
|
- return err;
|
|
|
- }
|
|
|
+ size_t buf_size;
|
|
|
+ char buf[8];
|
|
|
|
|
|
- return 0;
|
|
|
-}
|
|
|
+ buf_size = min_t(size_t, count, (sizeof(buf) - 1));
|
|
|
+ if (copy_from_user(buf, u, buf_size))
|
|
|
+ return -EFAULT;
|
|
|
|
|
|
-/**
|
|
|
- * dbg_debugfs_exit - remove the "ubifs" directory from debugfs file-system.
|
|
|
- */
|
|
|
-void dbg_debugfs_exit(void)
|
|
|
-{
|
|
|
- debugfs_remove(dfs_rootdir);
|
|
|
-}
|
|
|
+ if (buf[0] == '1')
|
|
|
+ return 1;
|
|
|
+ else if (buf[0] == '0')
|
|
|
+ return 0;
|
|
|
|
|
|
-static int open_debugfs_file(struct inode *inode, struct file *file)
|
|
|
-{
|
|
|
- file->private_data = inode->i_private;
|
|
|
- return nonseekable_open(inode, file);
|
|
|
+ return -EINVAL;
|
|
|
}
|
|
|
|
|
|
-static ssize_t write_debugfs_file(struct file *file, const char __user *buf,
|
|
|
- size_t count, loff_t *ppos)
|
|
|
+static ssize_t dfs_file_write(struct file *file, const char __user *u,
|
|
|
+ size_t count, loff_t *ppos)
|
|
|
{
|
|
|
struct ubifs_info *c = file->private_data;
|
|
|
struct ubifs_debug_info *d = c->dbg;
|
|
|
+ struct dentry *dent = file->f_path.dentry;
|
|
|
+ int val;
|
|
|
|
|
|
- if (file->f_path.dentry == d->dfs_dump_lprops)
|
|
|
+ /*
|
|
|
+ * TODO: this is racy - the file-system might have already been
|
|
|
+ * unmounted and we'd oops in this case. The plan is to fix it with
|
|
|
+ * help of 'iterate_supers_type()' which we should have in v3.0: when
|
|
|
+ * a debugfs opened, we rember FS's UUID in file->private_data. Then
|
|
|
+ * whenever we access the FS via a debugfs file, we iterate all UBIFS
|
|
|
+ * superblocks and fine the one with the same UUID, and take the
|
|
|
+ * locking right.
|
|
|
+ *
|
|
|
+ * The other way to go suggested by Al Viro is to create a separate
|
|
|
+ * 'ubifs-debug' file-system instead.
|
|
|
+ */
|
|
|
+ if (file->f_path.dentry == d->dfs_dump_lprops) {
|
|
|
dbg_dump_lprops(c);
|
|
|
- else if (file->f_path.dentry == d->dfs_dump_budg)
|
|
|
+ return count;
|
|
|
+ }
|
|
|
+ if (file->f_path.dentry == d->dfs_dump_budg) {
|
|
|
dbg_dump_budg(c, &c->bi);
|
|
|
- else if (file->f_path.dentry == d->dfs_dump_tnc) {
|
|
|
+ return count;
|
|
|
+ }
|
|
|
+ if (file->f_path.dentry == d->dfs_dump_tnc) {
|
|
|
mutex_lock(&c->tnc_mutex);
|
|
|
dbg_dump_tnc(c);
|
|
|
mutex_unlock(&c->tnc_mutex);
|
|
|
- } else
|
|
|
+ return count;
|
|
|
+ }
|
|
|
+
|
|
|
+ val = interpret_user_input(u, count);
|
|
|
+ if (val < 0)
|
|
|
+ return val;
|
|
|
+
|
|
|
+ if (dent == d->dfs_chk_gen)
|
|
|
+ d->chk_gen = val;
|
|
|
+ else if (dent == d->dfs_chk_index)
|
|
|
+ d->chk_index = val;
|
|
|
+ else if (dent == d->dfs_chk_orph)
|
|
|
+ d->chk_orph = val;
|
|
|
+ else if (dent == d->dfs_chk_lprops)
|
|
|
+ d->chk_lprops = val;
|
|
|
+ else if (dent == d->dfs_chk_fs)
|
|
|
+ d->chk_fs = val;
|
|
|
+ else if (dent == d->dfs_tst_rcvry)
|
|
|
+ d->tst_rcvry = val;
|
|
|
+ else
|
|
|
return -EINVAL;
|
|
|
|
|
|
return count;
|
|
|
}
|
|
|
|
|
|
static const struct file_operations dfs_fops = {
|
|
|
- .open = open_debugfs_file,
|
|
|
- .write = write_debugfs_file,
|
|
|
+ .open = dfs_file_open,
|
|
|
+ .read = dfs_file_read,
|
|
|
+ .write = dfs_file_write,
|
|
|
.owner = THIS_MODULE,
|
|
|
.llseek = no_llseek,
|
|
|
};
|
|
@@ -2880,12 +2897,20 @@ static const struct file_operations dfs_fops = {
|
|
|
*/
|
|
|
int dbg_debugfs_init_fs(struct ubifs_info *c)
|
|
|
{
|
|
|
- int err;
|
|
|
+ int err, n;
|
|
|
const char *fname;
|
|
|
struct dentry *dent;
|
|
|
struct ubifs_debug_info *d = c->dbg;
|
|
|
|
|
|
- sprintf(d->dfs_dir_name, "ubi%d_%d", c->vi.ubi_num, c->vi.vol_id);
|
|
|
+ n = snprintf(d->dfs_dir_name, UBIFS_DFS_DIR_LEN + 1, UBIFS_DFS_DIR_NAME,
|
|
|
+ c->vi.ubi_num, c->vi.vol_id);
|
|
|
+ if (n == UBIFS_DFS_DIR_LEN) {
|
|
|
+ /* The array size is too small */
|
|
|
+ fname = UBIFS_DFS_DIR_NAME;
|
|
|
+ dent = ERR_PTR(-EINVAL);
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
fname = d->dfs_dir_name;
|
|
|
dent = debugfs_create_dir(fname, dfs_rootdir);
|
|
|
if (IS_ERR_OR_NULL(dent))
|
|
@@ -2910,13 +2935,55 @@ int dbg_debugfs_init_fs(struct ubifs_info *c)
|
|
|
goto out_remove;
|
|
|
d->dfs_dump_tnc = dent;
|
|
|
|
|
|
+ fname = "chk_general";
|
|
|
+ dent = debugfs_create_file(fname, S_IRUSR | S_IWUSR, d->dfs_dir, c,
|
|
|
+ &dfs_fops);
|
|
|
+ if (IS_ERR_OR_NULL(dent))
|
|
|
+ goto out_remove;
|
|
|
+ d->dfs_chk_gen = dent;
|
|
|
+
|
|
|
+ fname = "chk_index";
|
|
|
+ dent = debugfs_create_file(fname, S_IRUSR | S_IWUSR, d->dfs_dir, c,
|
|
|
+ &dfs_fops);
|
|
|
+ if (IS_ERR_OR_NULL(dent))
|
|
|
+ goto out_remove;
|
|
|
+ d->dfs_chk_index = dent;
|
|
|
+
|
|
|
+ fname = "chk_orphans";
|
|
|
+ dent = debugfs_create_file(fname, S_IRUSR | S_IWUSR, d->dfs_dir, c,
|
|
|
+ &dfs_fops);
|
|
|
+ if (IS_ERR_OR_NULL(dent))
|
|
|
+ goto out_remove;
|
|
|
+ d->dfs_chk_orph = dent;
|
|
|
+
|
|
|
+ fname = "chk_lprops";
|
|
|
+ dent = debugfs_create_file(fname, S_IRUSR | S_IWUSR, d->dfs_dir, c,
|
|
|
+ &dfs_fops);
|
|
|
+ if (IS_ERR_OR_NULL(dent))
|
|
|
+ goto out_remove;
|
|
|
+ d->dfs_chk_lprops = dent;
|
|
|
+
|
|
|
+ fname = "chk_fs";
|
|
|
+ dent = debugfs_create_file(fname, S_IRUSR | S_IWUSR, d->dfs_dir, c,
|
|
|
+ &dfs_fops);
|
|
|
+ if (IS_ERR_OR_NULL(dent))
|
|
|
+ goto out_remove;
|
|
|
+ d->dfs_chk_fs = dent;
|
|
|
+
|
|
|
+ fname = "tst_recovery";
|
|
|
+ dent = debugfs_create_file(fname, S_IRUSR | S_IWUSR, d->dfs_dir, c,
|
|
|
+ &dfs_fops);
|
|
|
+ if (IS_ERR_OR_NULL(dent))
|
|
|
+ goto out_remove;
|
|
|
+ d->dfs_tst_rcvry = dent;
|
|
|
+
|
|
|
return 0;
|
|
|
|
|
|
out_remove:
|
|
|
debugfs_remove_recursive(d->dfs_dir);
|
|
|
out:
|
|
|
err = dent ? PTR_ERR(dent) : -ENODEV;
|
|
|
- ubifs_err("cannot create \"%s\" debugfs directory, error %d\n",
|
|
|
+ ubifs_err("cannot create \"%s\" debugfs file or directory, error %d\n",
|
|
|
fname, err);
|
|
|
return err;
|
|
|
}
|
|
@@ -2930,4 +2997,179 @@ void dbg_debugfs_exit_fs(struct ubifs_info *c)
|
|
|
debugfs_remove_recursive(c->dbg->dfs_dir);
|
|
|
}
|
|
|
|
|
|
+struct ubifs_global_debug_info ubifs_dbg;
|
|
|
+
|
|
|
+static struct dentry *dfs_chk_gen;
|
|
|
+static struct dentry *dfs_chk_index;
|
|
|
+static struct dentry *dfs_chk_orph;
|
|
|
+static struct dentry *dfs_chk_lprops;
|
|
|
+static struct dentry *dfs_chk_fs;
|
|
|
+static struct dentry *dfs_tst_rcvry;
|
|
|
+
|
|
|
+static ssize_t dfs_global_file_read(struct file *file, char __user *u,
|
|
|
+ size_t count, loff_t *ppos)
|
|
|
+{
|
|
|
+ struct dentry *dent = file->f_path.dentry;
|
|
|
+ int val;
|
|
|
+
|
|
|
+ if (dent == dfs_chk_gen)
|
|
|
+ val = ubifs_dbg.chk_gen;
|
|
|
+ else if (dent == dfs_chk_index)
|
|
|
+ val = ubifs_dbg.chk_index;
|
|
|
+ else if (dent == dfs_chk_orph)
|
|
|
+ val = ubifs_dbg.chk_orph;
|
|
|
+ else if (dent == dfs_chk_lprops)
|
|
|
+ val = ubifs_dbg.chk_lprops;
|
|
|
+ else if (dent == dfs_chk_fs)
|
|
|
+ val = ubifs_dbg.chk_fs;
|
|
|
+ else if (dent == dfs_tst_rcvry)
|
|
|
+ val = ubifs_dbg.tst_rcvry;
|
|
|
+ else
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ return provide_user_output(val, u, count, ppos);
|
|
|
+}
|
|
|
+
|
|
|
+static ssize_t dfs_global_file_write(struct file *file, const char __user *u,
|
|
|
+ size_t count, loff_t *ppos)
|
|
|
+{
|
|
|
+ struct dentry *dent = file->f_path.dentry;
|
|
|
+ int val;
|
|
|
+
|
|
|
+ val = interpret_user_input(u, count);
|
|
|
+ if (val < 0)
|
|
|
+ return val;
|
|
|
+
|
|
|
+ if (dent == dfs_chk_gen)
|
|
|
+ ubifs_dbg.chk_gen = val;
|
|
|
+ else if (dent == dfs_chk_index)
|
|
|
+ ubifs_dbg.chk_index = val;
|
|
|
+ else if (dent == dfs_chk_orph)
|
|
|
+ ubifs_dbg.chk_orph = val;
|
|
|
+ else if (dent == dfs_chk_lprops)
|
|
|
+ ubifs_dbg.chk_lprops = val;
|
|
|
+ else if (dent == dfs_chk_fs)
|
|
|
+ ubifs_dbg.chk_fs = val;
|
|
|
+ else if (dent == dfs_tst_rcvry)
|
|
|
+ ubifs_dbg.tst_rcvry = val;
|
|
|
+ else
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ return count;
|
|
|
+}
|
|
|
+
|
|
|
+static const struct file_operations dfs_global_fops = {
|
|
|
+ .read = dfs_global_file_read,
|
|
|
+ .write = dfs_global_file_write,
|
|
|
+ .owner = THIS_MODULE,
|
|
|
+ .llseek = no_llseek,
|
|
|
+};
|
|
|
+
|
|
|
+/**
|
|
|
+ * dbg_debugfs_init - initialize debugfs file-system.
|
|
|
+ *
|
|
|
+ * UBIFS uses debugfs file-system to expose various debugging knobs to
|
|
|
+ * user-space. This function creates "ubifs" directory in the debugfs
|
|
|
+ * file-system. Returns zero in case of success and a negative error code in
|
|
|
+ * case of failure.
|
|
|
+ */
|
|
|
+int dbg_debugfs_init(void)
|
|
|
+{
|
|
|
+ int err;
|
|
|
+ const char *fname;
|
|
|
+ struct dentry *dent;
|
|
|
+
|
|
|
+ fname = "ubifs";
|
|
|
+ dent = debugfs_create_dir(fname, NULL);
|
|
|
+ if (IS_ERR_OR_NULL(dent))
|
|
|
+ goto out;
|
|
|
+ dfs_rootdir = dent;
|
|
|
+
|
|
|
+ fname = "chk_general";
|
|
|
+ dent = debugfs_create_file(fname, S_IRUSR | S_IWUSR, dfs_rootdir, NULL,
|
|
|
+ &dfs_global_fops);
|
|
|
+ if (IS_ERR_OR_NULL(dent))
|
|
|
+ goto out_remove;
|
|
|
+ dfs_chk_gen = dent;
|
|
|
+
|
|
|
+ fname = "chk_index";
|
|
|
+ dent = debugfs_create_file(fname, S_IRUSR | S_IWUSR, dfs_rootdir, NULL,
|
|
|
+ &dfs_global_fops);
|
|
|
+ if (IS_ERR_OR_NULL(dent))
|
|
|
+ goto out_remove;
|
|
|
+ dfs_chk_index = dent;
|
|
|
+
|
|
|
+ fname = "chk_orphans";
|
|
|
+ dent = debugfs_create_file(fname, S_IRUSR | S_IWUSR, dfs_rootdir, NULL,
|
|
|
+ &dfs_global_fops);
|
|
|
+ if (IS_ERR_OR_NULL(dent))
|
|
|
+ goto out_remove;
|
|
|
+ dfs_chk_orph = dent;
|
|
|
+
|
|
|
+ fname = "chk_lprops";
|
|
|
+ dent = debugfs_create_file(fname, S_IRUSR | S_IWUSR, dfs_rootdir, NULL,
|
|
|
+ &dfs_global_fops);
|
|
|
+ if (IS_ERR_OR_NULL(dent))
|
|
|
+ goto out_remove;
|
|
|
+ dfs_chk_lprops = dent;
|
|
|
+
|
|
|
+ fname = "chk_fs";
|
|
|
+ dent = debugfs_create_file(fname, S_IRUSR | S_IWUSR, dfs_rootdir, NULL,
|
|
|
+ &dfs_global_fops);
|
|
|
+ if (IS_ERR_OR_NULL(dent))
|
|
|
+ goto out_remove;
|
|
|
+ dfs_chk_fs = dent;
|
|
|
+
|
|
|
+ fname = "tst_recovery";
|
|
|
+ dent = debugfs_create_file(fname, S_IRUSR | S_IWUSR, dfs_rootdir, NULL,
|
|
|
+ &dfs_global_fops);
|
|
|
+ if (IS_ERR_OR_NULL(dent))
|
|
|
+ goto out_remove;
|
|
|
+ dfs_tst_rcvry = dent;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+
|
|
|
+out_remove:
|
|
|
+ debugfs_remove_recursive(dfs_rootdir);
|
|
|
+out:
|
|
|
+ err = dent ? PTR_ERR(dent) : -ENODEV;
|
|
|
+ ubifs_err("cannot create \"%s\" debugfs file or directory, error %d\n",
|
|
|
+ fname, err);
|
|
|
+ return err;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * dbg_debugfs_exit - remove the "ubifs" directory from debugfs file-system.
|
|
|
+ */
|
|
|
+void dbg_debugfs_exit(void)
|
|
|
+{
|
|
|
+ debugfs_remove_recursive(dfs_rootdir);
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * ubifs_debugging_init - initialize UBIFS debugging.
|
|
|
+ * @c: UBIFS file-system description object
|
|
|
+ *
|
|
|
+ * This function initializes debugging-related data for the file system.
|
|
|
+ * Returns zero in case of success and a negative error code in case of
|
|
|
+ * failure.
|
|
|
+ */
|
|
|
+int ubifs_debugging_init(struct ubifs_info *c)
|
|
|
+{
|
|
|
+ c->dbg = kzalloc(sizeof(struct ubifs_debug_info), GFP_KERNEL);
|
|
|
+ if (!c->dbg)
|
|
|
+ return -ENOMEM;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * ubifs_debugging_exit - free debugging data.
|
|
|
+ * @c: UBIFS file-system description object
|
|
|
+ */
|
|
|
+void ubifs_debugging_exit(struct ubifs_info *c)
|
|
|
+{
|
|
|
+ kfree(c->dbg);
|
|
|
+}
|
|
|
+
|
|
|
#endif /* CONFIG_UBIFS_FS_DEBUG */
|