forked from openwrt/openwrt
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
kernel: fix ubifs loosing O_TMPFILE data after power cut
There was a bug in ubifs related to the O_TMPFILE. When reapplying changes after power cut data could be lost. This problem was exposed by overlayfs and the upstream commit 3a1e819b4e80 ("ovl: store file handle of lower inode on copy up"). This fixes a regression introduced when switching from 4.9 to 4.14. Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
- Loading branch information
Rafał Miłecki
committed
Nov 15, 2018
1 parent
80526d2
commit c6a1bca
Showing
1 changed file
with
89 additions
and
0 deletions.
There are no files selected for viewing
89 changes: 89 additions & 0 deletions
89
...inux/generic/backport-4.14/500-ubifs-Handle-re-linking-of-inodes-correctly-while-re.patch
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
From: Richard Weinberger <richard@nod.at> | ||
Date: Wed, 7 Nov 2018 23:04:43 +0100 | ||
Subject: [PATCH] ubifs: Handle re-linking of inodes correctly while recovery | ||
MIME-Version: 1.0 | ||
Content-Type: text/plain; charset=UTF-8 | ||
Content-Transfer-Encoding: 8bit | ||
|
||
UBIFS's recovery code strictly assumes that a deleted inode will never | ||
come back, therefore it removes all data which belongs to that inode | ||
as soon it faces an inode with link count 0 in the replay list. | ||
Before O_TMPFILE this assumption was perfectly fine. With O_TMPFILE | ||
it can lead to data loss upon a power-cut. | ||
|
||
Consider a journal with entries like: | ||
0: inode X (nlink = 0) /* O_TMPFILE was created */ | ||
1: data for inode X /* Someone writes to the temp file */ | ||
2: inode X (nlink = 0) /* inode was changed, xattr, chmod, … */ | ||
3: inode X (nlink = 1) /* inode was re-linked via linkat() */ | ||
|
||
Upon replay of entry #2 UBIFS will drop all data that belongs to inode X, | ||
this will lead to an empty file after mounting. | ||
|
||
As solution for this problem, scan the replay list for a re-link entry | ||
before dropping data. | ||
|
||
Fixes: 474b93704f32 ("ubifs: Implement O_TMPFILE") | ||
Cc: stable@vger.kernel.org | ||
Cc: Russell Senior <russell@personaltelco.net> | ||
Cc: Rafał Miłecki <zajec5@gmail.com> | ||
Reported-by: Russell Senior <russell@personaltelco.net> | ||
Reported-by: Rafał Miłecki <zajec5@gmail.com> | ||
Signed-off-by: Richard Weinberger <richard@nod.at> | ||
--- | ||
fs/ubifs/replay.c | 37 +++++++++++++++++++++++++++++++++++++ | ||
1 file changed, 37 insertions(+) | ||
|
||
--- a/fs/ubifs/replay.c | ||
+++ b/fs/ubifs/replay.c | ||
@@ -210,6 +210,38 @@ static int trun_remove_range(struct ubif | ||
} | ||
|
||
/** | ||
+ * inode_still_linked - check whether inode in question will be re-linked. | ||
+ * @c: UBIFS file-system description object | ||
+ * @rino: replay entry to test | ||
+ * | ||
+ * O_TMPFILE files can be re-linked, this means link count goes from 0 to 1. | ||
+ * This case needs special care, otherwise all references to the inode will | ||
+ * be removed upon the first replay entry of an inode with link count 0 | ||
+ * is found. | ||
+ */ | ||
+static bool inode_still_linked(struct ubifs_info *c, struct replay_entry *rino) | ||
+{ | ||
+ struct replay_entry *r; | ||
+ | ||
+ ubifs_assert(rino->deletion); | ||
+ ubifs_assert(key_type(c, &rino->key) == UBIFS_INO_KEY); | ||
+ | ||
+ /* | ||
+ * Find the most recent entry for the inode behind @rino and check | ||
+ * whether it is a deletion. | ||
+ */ | ||
+ list_for_each_entry_reverse(r, &c->replay_list, list) { | ||
+ ubifs_assert(r->sqnum >= rino->sqnum); | ||
+ if (key_inum(c, &r->key) == key_inum(c, &rino->key)) | ||
+ return r->deletion == 0; | ||
+ | ||
+ } | ||
+ | ||
+ ubifs_assert(0); | ||
+ return false; | ||
+} | ||
+ | ||
+/** | ||
* apply_replay_entry - apply a replay entry to the TNC. | ||
* @c: UBIFS file-system description object | ||
* @r: replay entry to apply | ||
@@ -239,6 +271,11 @@ static int apply_replay_entry(struct ubi | ||
{ | ||
ino_t inum = key_inum(c, &r->key); | ||
|
||
+ if (inode_still_linked(c, r)) { | ||
+ err = 0; | ||
+ break; | ||
+ } | ||
+ | ||
err = ubifs_tnc_remove_ino(c, inum); | ||
break; | ||
} |