345a73cf46bdefa2dc5692e6f3d3dc5eddde042c
[openwrt/openwrt.git] / target / linux / generic / backport-4.19 / 500-ubifs-Handle-re-linking-of-inodes-correctly-while-re.patch
1 From: Richard Weinberger <richard@nod.at>
2 Date: Wed, 7 Nov 2018 23:04:43 +0100
3 Subject: [PATCH] ubifs: Handle re-linking of inodes correctly while recovery
4 MIME-Version: 1.0
5 Content-Type: text/plain; charset=UTF-8
6 Content-Transfer-Encoding: 8bit
7
8 UBIFS's recovery code strictly assumes that a deleted inode will never
9 come back, therefore it removes all data which belongs to that inode
10 as soon it faces an inode with link count 0 in the replay list.
11 Before O_TMPFILE this assumption was perfectly fine. With O_TMPFILE
12 it can lead to data loss upon a power-cut.
13
14 Consider a journal with entries like:
15 0: inode X (nlink = 0) /* O_TMPFILE was created */
16 1: data for inode X /* Someone writes to the temp file */
17 2: inode X (nlink = 0) /* inode was changed, xattr, chmod, … */
18 3: inode X (nlink = 1) /* inode was re-linked via linkat() */
19
20 Upon replay of entry #2 UBIFS will drop all data that belongs to inode X,
21 this will lead to an empty file after mounting.
22
23 As solution for this problem, scan the replay list for a re-link entry
24 before dropping data.
25
26 Fixes: 474b93704f32 ("ubifs: Implement O_TMPFILE")
27 Cc: stable@vger.kernel.org
28 Cc: Russell Senior <russell@personaltelco.net>
29 Cc: Rafał Miłecki <zajec5@gmail.com>
30 Reported-by: Russell Senior <russell@personaltelco.net>
31 Reported-by: Rafał Miłecki <zajec5@gmail.com>
32 Signed-off-by: Richard Weinberger <richard@nod.at>
33 ---
34 fs/ubifs/replay.c | 37 +++++++++++++++++++++++++++++++++++++
35 1 file changed, 37 insertions(+)
36
37 --- a/fs/ubifs/replay.c
38 +++ b/fs/ubifs/replay.c
39 @@ -210,6 +210,38 @@ static int trun_remove_range(struct ubif
40 }
41
42 /**
43 + * inode_still_linked - check whether inode in question will be re-linked.
44 + * @c: UBIFS file-system description object
45 + * @rino: replay entry to test
46 + *
47 + * O_TMPFILE files can be re-linked, this means link count goes from 0 to 1.
48 + * This case needs special care, otherwise all references to the inode will
49 + * be removed upon the first replay entry of an inode with link count 0
50 + * is found.
51 + */
52 +static bool inode_still_linked(struct ubifs_info *c, struct replay_entry *rino)
53 +{
54 + struct replay_entry *r;
55 +
56 + ubifs_assert(c, rino->deletion);
57 + ubifs_assert(c, key_type(c, &rino->key) == UBIFS_INO_KEY);
58 +
59 + /*
60 + * Find the most recent entry for the inode behind @rino and check
61 + * whether it is a deletion.
62 + */
63 + list_for_each_entry_reverse(r, &c->replay_list, list) {
64 + ubifs_assert(c, r->sqnum >= rino->sqnum);
65 + if (key_inum(c, &r->key) == key_inum(c, &rino->key))
66 + return r->deletion == 0;
67 +
68 + }
69 +
70 + ubifs_assert(c, 0);
71 + return false;
72 +}
73 +
74 +/**
75 * apply_replay_entry - apply a replay entry to the TNC.
76 * @c: UBIFS file-system description object
77 * @r: replay entry to apply
78 @@ -236,6 +268,11 @@ static int apply_replay_entry(struct ubi
79 {
80 ino_t inum = key_inum(c, &r->key);
81
82 + if (inode_still_linked(c, r)) {
83 + err = 0;
84 + break;
85 + }
86 +
87 err = ubifs_tnc_remove_ino(c, inum);
88 break;
89 }