FAILED: patch "[PATCH] ubifs: Handle re-linking of inodes correctly while recovery" failed to apply to 4.9-stable tree
Subsystems (2)
[email protected]
maintainerThe patch below does not apply to the 4.9-stable tree.
If someone wants it applied there, or to any other stable or longterm tree, then please email the backport, including the original git commit id to <[email protected]>.
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
From e58725d51fa8da9133f3f1c54170aa2e43056b91 Mon Sep 17 00:00:00 2001
From: Richard Weinberger <[email protected]>
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.
fs/ubifs/replay.c +37 -0
@@ -212,6 +212,38 @@ static int trun_remove_range(struct ubifs_info *c, struct replay_entry *r)return ubifs_tnc_remove_range (c, &min_key, &max_key); } +/** +* 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 (c, rino->deletion); +ubifs_assert (c,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 (c, r->sqnum >= rino->sqnum); +if (key_inum (c, &r->key) ==key_inum (c, &rino->key)) +return r->deletion ==0 ; + + } + +ubifs_assert (c,0 ); +return false; +} +/** * apply_replay_entry - apply a replay entry to the TNC. * @c: UBIFS file-system description object
@@ -239,6 +271,11 @@ static int apply_replay_entry(struct ubifs_info *c, struct replay_entry *r) {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 ; }