| From 6729c4bfe520b20559dbbea27db16f8afc544731 Mon Sep 17 00:00:00 2001 |
| From: Richard Weinberger <richard@nod.at> |
| Date: Tue, 10 Jan 2017 11:49:40 +0100 |
| Subject: [PATCH] ubifs: Fix journal replay wrt. xattr nodes |
| |
| commit 1cb51a15b576ee325d527726afff40947218fd5e upstream. |
| |
| When replaying the journal it can happen that a journal entry points to |
| a garbage collected node. |
| This is the case when a power-cut occurred between a garbage collect run |
| and a commit. In such a case nodes have to be read using the failable |
| read functions to detect whether the found node matches what we expect. |
| |
| One corner case was forgotten, when the journal contains an entry to |
| remove an inode all xattrs have to be removed too. UBIFS models xattr |
| like directory entries, so the TNC code iterates over |
| all xattrs of the inode and removes them too. This code re-uses the |
| functions for walking directories and calls ubifs_tnc_next_ent(). |
| ubifs_tnc_next_ent() expects to be used only after the journal and |
| aborts when a node does not match the expected result. This behavior can |
| render an UBIFS volume unmountable after a power-cut when xattrs are |
| used. |
| |
| Fix this issue by using failable read functions in ubifs_tnc_next_ent() |
| too when replaying the journal. |
| Cc: stable@vger.kernel.org |
| Fixes: 1e51764a3c2ac05a ("UBIFS: add new flash file system") |
| Reported-by: Rock Lee <rockdotlee@gmail.com> |
| Reviewed-by: David Gstir <david@sigma-star.at> |
| Signed-off-by: Richard Weinberger <richard@nod.at> |
| Signed-off-by: Paul Gortmaker <paul.gortmaker@windriver.com> |
| |
| diff --git a/fs/ubifs/tnc.c b/fs/ubifs/tnc.c |
| index fa9a20cc60d6..fe5e8d4970ae 100644 |
| --- a/fs/ubifs/tnc.c |
| +++ b/fs/ubifs/tnc.c |
| @@ -34,6 +34,11 @@ |
| #include <linux/slab.h> |
| #include "ubifs.h" |
| |
| +static int try_read_node(const struct ubifs_info *c, void *buf, int type, |
| + int len, int lnum, int offs); |
| +static int fallible_read_node(struct ubifs_info *c, const union ubifs_key *key, |
| + struct ubifs_zbranch *zbr, void *node); |
| + |
| /* |
| * Returned codes of 'matches_name()' and 'fallible_matches_name()' functions. |
| * @NAME_LESS: name corresponding to the first argument is less than second |
| @@ -402,7 +407,19 @@ static int tnc_read_node_nm(struct ubifs_info *c, struct ubifs_zbranch *zbr, |
| return 0; |
| } |
| |
| - err = ubifs_tnc_read_node(c, zbr, node); |
| + if (c->replaying) { |
| + err = fallible_read_node(c, &zbr->key, zbr, node); |
| + /* |
| + * When the node was not found, return -ENOENT, 0 otherwise. |
| + * Negative return codes stay as-is. |
| + */ |
| + if (err == 0) |
| + err = -ENOENT; |
| + else if (err == 1) |
| + err = 0; |
| + } else { |
| + err = ubifs_tnc_read_node(c, zbr, node); |
| + } |
| if (err) |
| return err; |
| |
| @@ -2766,7 +2783,11 @@ struct ubifs_dent_node *ubifs_tnc_next_ent(struct ubifs_info *c, |
| if (nm->name) { |
| if (err) { |
| /* Handle collisions */ |
| - err = resolve_collision(c, key, &znode, &n, nm); |
| + if (c->replaying) |
| + err = fallible_resolve_collision(c, key, &znode, &n, |
| + nm, 0); |
| + else |
| + err = resolve_collision(c, key, &znode, &n, nm); |
| dbg_tnc("rc returned %d, znode %p, n %d", |
| err, znode, n); |
| if (unlikely(err < 0)) |
| -- |
| 2.10.1 |
| |