2019-05-29 23:57:50 +00:00
|
|
|
|
// SPDX-License-Identifier: GPL-2.0-only
|
2008-07-14 16:08:37 +00:00
|
|
|
|
/*
|
|
|
|
|
* This file is part of UBIFS.
|
|
|
|
|
*
|
|
|
|
|
* Copyright (C) 2006-2008 Nokia Corporation.
|
|
|
|
|
*
|
|
|
|
|
* Authors: Artem Bityutskiy (Битюцкий Артём)
|
|
|
|
|
* Adrian Hunter
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* This file implements UBIFS journal.
|
|
|
|
|
*
|
|
|
|
|
* The journal consists of 2 parts - the log and bud LEBs. The log has fixed
|
|
|
|
|
* length and position, while a bud logical eraseblock is any LEB in the main
|
|
|
|
|
* area. Buds contain file system data - data nodes, inode nodes, etc. The log
|
|
|
|
|
* contains only references to buds and some other stuff like commit
|
|
|
|
|
* start node. The idea is that when we commit the journal, we do
|
|
|
|
|
* not copy the data, the buds just become indexed. Since after the commit the
|
|
|
|
|
* nodes in bud eraseblocks become leaf nodes of the file system index tree, we
|
|
|
|
|
* use term "bud". Analogy is obvious, bud eraseblocks contain nodes which will
|
|
|
|
|
* become leafs in the future.
|
|
|
|
|
*
|
|
|
|
|
* The journal is multi-headed because we want to write data to the journal as
|
|
|
|
|
* optimally as possible. It is nice to have nodes belonging to the same inode
|
|
|
|
|
* in one LEB, so we may write data owned by different inodes to different
|
|
|
|
|
* journal heads, although at present only one data head is used.
|
|
|
|
|
*
|
|
|
|
|
* For recovery reasons, the base head contains all inode nodes, all directory
|
|
|
|
|
* entry nodes and all truncate nodes. This means that the other heads contain
|
|
|
|
|
* only data nodes.
|
|
|
|
|
*
|
|
|
|
|
* Bud LEBs may be half-indexed. For example, if the bud was not full at the
|
|
|
|
|
* time of commit, the bud is retained to continue to be used in the journal,
|
|
|
|
|
* even though the "front" of the LEB is now indexed. In that case, the log
|
|
|
|
|
* reference contains the offset where the bud starts for the purposes of the
|
|
|
|
|
* journal.
|
|
|
|
|
*
|
|
|
|
|
* The journal size has to be limited, because the larger is the journal, the
|
|
|
|
|
* longer it takes to mount UBIFS (scanning the journal) and the more memory it
|
|
|
|
|
* takes (indexing in the TNC).
|
|
|
|
|
*
|
|
|
|
|
* All the journal write operations like 'ubifs_jnl_update()' here, which write
|
|
|
|
|
* multiple UBIFS nodes to the journal at one go, are atomic with respect to
|
|
|
|
|
* unclean reboots. Should the unclean reboot happen, the recovery code drops
|
|
|
|
|
* all the nodes.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include "ubifs.h"
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* zero_ino_node_unused - zero out unused fields of an on-flash inode node.
|
|
|
|
|
* @ino: the inode to zero out
|
|
|
|
|
*/
|
|
|
|
|
static inline void zero_ino_node_unused(struct ubifs_ino_node *ino)
|
|
|
|
|
{
|
|
|
|
|
memset(ino->padding1, 0, 4);
|
|
|
|
|
memset(ino->padding2, 0, 26);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* zero_dent_node_unused - zero out unused fields of an on-flash directory
|
|
|
|
|
* entry node.
|
|
|
|
|
* @dent: the directory entry to zero out
|
|
|
|
|
*/
|
|
|
|
|
static inline void zero_dent_node_unused(struct ubifs_dent_node *dent)
|
|
|
|
|
{
|
|
|
|
|
dent->padding1 = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* zero_trun_node_unused - zero out unused fields of an on-flash truncation
|
|
|
|
|
* node.
|
|
|
|
|
* @trun: the truncation node to zero out
|
|
|
|
|
*/
|
|
|
|
|
static inline void zero_trun_node_unused(struct ubifs_trun_node *trun)
|
|
|
|
|
{
|
|
|
|
|
memset(trun->padding, 0, 12);
|
|
|
|
|
}
|
|
|
|
|
|
ubifs: Add authentication nodes to journal
Nodes that are written to flash can only be authenticated through the
index after the next commit. When a journal replay is necessary the
nodes are not yet referenced by the index and thus can't be
authenticated.
This patch overcomes this situation by creating a hash over all nodes
beginning from the commit start node over the reference node(s) and
the buds themselves. From
time to time we insert authentication nodes. Authentication nodes
contain a HMAC from the current hash state, so that they can be
used to authenticate a journal replay up to the point where the
authentication node is. The hash is continued afterwards
so that theoretically we would only have to check the HMAC of
the last authentication node we find.
Overall we get this picture:
,,,,,,,,
,......,...........................................
,. CS , hash1.----. hash2.----.
,. | , . |hmac . |hmac
,. v , . v . v
,.REF#0,-> bud -> bud -> bud.-> auth -> bud -> bud.-> auth ...
,..|...,...........................................
, | ,
, | ,,,,,,,,,,,,,,,
. | hash3,----.
, | , |hmac
, v , v
, REF#1 -> bud -> bud,-> auth ...
,,,|,,,,,,,,,,,,,,,,,,
v
REF#2 -> ...
|
V
...
Note how hash3 covers CS, REF#0 and REF#1 so that it is not possible to
exchange or skip any reference nodes. Unlike the picture suggests the
auth nodes themselves are not hashed.
With this it is possible for an offline attacker to cut each journal
head or to drop the last reference node(s), but not to skip any journal
heads or to reorder any operations.
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Signed-off-by: Richard Weinberger <richard@nod.at>
2018-09-07 12:36:36 +00:00
|
|
|
|
static void ubifs_add_auth_dirt(struct ubifs_info *c, int lnum)
|
|
|
|
|
{
|
|
|
|
|
if (ubifs_authenticated(c))
|
|
|
|
|
ubifs_add_dirt(c, lnum, ubifs_auth_node_sz(c));
|
|
|
|
|
}
|
|
|
|
|
|
2008-07-14 16:08:37 +00:00
|
|
|
|
/**
|
|
|
|
|
* reserve_space - reserve space in the journal.
|
|
|
|
|
* @c: UBIFS file-system description object
|
|
|
|
|
* @jhead: journal head number
|
|
|
|
|
* @len: node length
|
|
|
|
|
*
|
|
|
|
|
* This function reserves space in journal head @head. If the reservation
|
|
|
|
|
* succeeded, the journal head stays locked and later has to be unlocked using
|
2018-05-14 08:18:15 +00:00
|
|
|
|
* 'release_head()'. Returns zero in case of success, %-EAGAIN if commit has to
|
|
|
|
|
* be done, and other negative error codes in case of other failures.
|
2008-07-14 16:08:37 +00:00
|
|
|
|
*/
|
|
|
|
|
static int reserve_space(struct ubifs_info *c, int jhead, int len)
|
|
|
|
|
{
|
2009-03-03 17:22:53 +00:00
|
|
|
|
int err = 0, err1, retries = 0, avail, lnum, offs, squeeze;
|
2008-07-14 16:08:37 +00:00
|
|
|
|
struct ubifs_wbuf *wbuf = &c->jheads[jhead].wbuf;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Typically, the base head has smaller nodes written to it, so it is
|
|
|
|
|
* better to try to allocate space at the ends of eraseblocks. This is
|
|
|
|
|
* what the squeeze parameter does.
|
|
|
|
|
*/
|
2018-07-12 11:01:57 +00:00
|
|
|
|
ubifs_assert(c, !c->ro_media && !c->ro_mount);
|
2008-07-14 16:08:37 +00:00
|
|
|
|
squeeze = (jhead == BASEHD);
|
|
|
|
|
again:
|
|
|
|
|
mutex_lock_nested(&wbuf->io_mutex, wbuf->jhead);
|
|
|
|
|
|
UBIFS: introduce new flag for RO due to errors
The R/O state may have various reasons:
1. The UBI volume is R/O
2. The FS is mounted R/O
3. The FS switched to R/O mode because of an error
However, in UBIFS we have only one variable which represents cases
1 and 3 - 'c->ro_media'. Indeed, we set this to 1 if we switch to
R/O mode due to an error, and then we test it in many places to
make sure that we stop writing as soon as the error happens.
But this is very unclean. One consequence of this, for example, is
that in 'ubifs_remount_fs()' we use 'c->ro_media' to check whether
we are in R/O mode because on an error, and we print a message
in this case. However, if we are in R/O mode because the media
is R/O, our message is bogus.
This patch introduces new flag - 'c->ro_error' which is set when
we switch to R/O mode because of an error. It also changes all
"if (c->ro_media)" checks to "if (c->ro_error)" checks, because
this is what the checks actually mean. We do not need to check
for 'c->ro_media' because if the UBI volume is in R/O mode, we
do not allow R/W mounting, and now writes can happen. This is
guaranteed by VFS. But it is good to double-check this, so this
patch also adds many "ubifs_assert(!c->ro_media)" checks.
In the 'ubifs_remount_fs()' function this patch makes a bit more
changes - it fixes the error messages as well.
Signed-off-by: Artem Bityutskiy <Artem.Bityutskiy@nokia.com>
2010-09-17 13:44:28 +00:00
|
|
|
|
if (c->ro_error) {
|
2008-07-14 16:08:37 +00:00
|
|
|
|
err = -EROFS;
|
|
|
|
|
goto out_unlock;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
avail = c->leb_size - wbuf->offs - wbuf->used;
|
|
|
|
|
if (wbuf->lnum != -1 && avail >= len)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Write buffer wasn't seek'ed or there is no enough space - look for an
|
|
|
|
|
* LEB with some empty space.
|
|
|
|
|
*/
|
2009-03-03 17:22:53 +00:00
|
|
|
|
lnum = ubifs_find_free_space(c, len, &offs, squeeze);
|
2011-05-15 11:51:54 +00:00
|
|
|
|
if (lnum >= 0)
|
2008-07-14 16:08:37 +00:00
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
|
|
err = lnum;
|
|
|
|
|
if (err != -ENOSPC)
|
|
|
|
|
goto out_unlock;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* No free space, we have to run garbage collector to make
|
|
|
|
|
* some. But the write-buffer mutex has to be unlocked because
|
|
|
|
|
* GC also takes it.
|
|
|
|
|
*/
|
2009-09-15 12:03:51 +00:00
|
|
|
|
dbg_jnl("no free space in jhead %s, run GC", dbg_jhead(jhead));
|
2008-07-14 16:08:37 +00:00
|
|
|
|
mutex_unlock(&wbuf->io_mutex);
|
|
|
|
|
|
|
|
|
|
lnum = ubifs_garbage_collect(c, 0);
|
|
|
|
|
if (lnum < 0) {
|
|
|
|
|
err = lnum;
|
|
|
|
|
if (err != -ENOSPC)
|
|
|
|
|
return err;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* GC could not make a free LEB. But someone else may
|
|
|
|
|
* have allocated new bud for this journal head,
|
|
|
|
|
* because we dropped @wbuf->io_mutex, so try once
|
|
|
|
|
* again.
|
|
|
|
|
*/
|
2009-09-15 12:03:51 +00:00
|
|
|
|
dbg_jnl("GC couldn't make a free LEB for jhead %s",
|
|
|
|
|
dbg_jhead(jhead));
|
2008-07-14 16:08:37 +00:00
|
|
|
|
if (retries++ < 2) {
|
|
|
|
|
dbg_jnl("retry (%d)", retries);
|
|
|
|
|
goto again;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
dbg_jnl("return -ENOSPC");
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
mutex_lock_nested(&wbuf->io_mutex, wbuf->jhead);
|
2009-09-15 12:03:51 +00:00
|
|
|
|
dbg_jnl("got LEB %d for jhead %s", lnum, dbg_jhead(jhead));
|
2008-07-14 16:08:37 +00:00
|
|
|
|
avail = c->leb_size - wbuf->offs - wbuf->used;
|
|
|
|
|
|
|
|
|
|
if (wbuf->lnum != -1 && avail >= len) {
|
|
|
|
|
/*
|
|
|
|
|
* Someone else has switched the journal head and we have
|
2008-10-16 17:02:37 +00:00
|
|
|
|
* enough space now. This happens when more than one process is
|
2008-07-14 16:08:37 +00:00
|
|
|
|
* trying to write to the same journal head at the same time.
|
|
|
|
|
*/
|
|
|
|
|
dbg_jnl("return LEB %d back, already have LEB %d:%d",
|
|
|
|
|
lnum, wbuf->lnum, wbuf->offs + wbuf->used);
|
|
|
|
|
err = ubifs_return_leb(c, lnum);
|
|
|
|
|
if (err)
|
|
|
|
|
goto out_unlock;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
offs = 0;
|
|
|
|
|
|
|
|
|
|
out:
|
2011-05-15 11:51:54 +00:00
|
|
|
|
/*
|
|
|
|
|
* Make sure we synchronize the write-buffer before we add the new bud
|
|
|
|
|
* to the log. Otherwise we may have a power cut after the log
|
|
|
|
|
* reference node for the last bud (@lnum) is written but before the
|
|
|
|
|
* write-buffer data are written to the next-to-last bud
|
|
|
|
|
* (@wbuf->lnum). And the effect would be that the recovery would see
|
|
|
|
|
* that there is corruption in the next-to-last bud.
|
|
|
|
|
*/
|
|
|
|
|
err = ubifs_wbuf_sync_nolock(wbuf);
|
|
|
|
|
if (err)
|
|
|
|
|
goto out_return;
|
|
|
|
|
err = ubifs_add_bud_to_log(c, jhead, lnum, offs);
|
|
|
|
|
if (err)
|
|
|
|
|
goto out_return;
|
2012-05-14 15:55:51 +00:00
|
|
|
|
err = ubifs_wbuf_seek_nolock(wbuf, lnum, offs);
|
2008-07-14 16:08:37 +00:00
|
|
|
|
if (err)
|
|
|
|
|
goto out_unlock;
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
out_unlock:
|
|
|
|
|
mutex_unlock(&wbuf->io_mutex);
|
|
|
|
|
return err;
|
|
|
|
|
|
|
|
|
|
out_return:
|
|
|
|
|
/* An error occurred and the LEB has to be returned to lprops */
|
2018-07-12 11:01:57 +00:00
|
|
|
|
ubifs_assert(c, err < 0);
|
2008-07-14 16:08:37 +00:00
|
|
|
|
err1 = ubifs_return_leb(c, lnum);
|
|
|
|
|
if (err1 && err == -EAGAIN)
|
|
|
|
|
/*
|
|
|
|
|
* Return original error code only if it is not %-EAGAIN,
|
|
|
|
|
* which is not really an error. Otherwise, return the error
|
|
|
|
|
* code of 'ubifs_return_leb()'.
|
|
|
|
|
*/
|
|
|
|
|
err = err1;
|
|
|
|
|
mutex_unlock(&wbuf->io_mutex);
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
ubifs: Add authentication nodes to journal
Nodes that are written to flash can only be authenticated through the
index after the next commit. When a journal replay is necessary the
nodes are not yet referenced by the index and thus can't be
authenticated.
This patch overcomes this situation by creating a hash over all nodes
beginning from the commit start node over the reference node(s) and
the buds themselves. From
time to time we insert authentication nodes. Authentication nodes
contain a HMAC from the current hash state, so that they can be
used to authenticate a journal replay up to the point where the
authentication node is. The hash is continued afterwards
so that theoretically we would only have to check the HMAC of
the last authentication node we find.
Overall we get this picture:
,,,,,,,,
,......,...........................................
,. CS , hash1.----. hash2.----.
,. | , . |hmac . |hmac
,. v , . v . v
,.REF#0,-> bud -> bud -> bud.-> auth -> bud -> bud.-> auth ...
,..|...,...........................................
, | ,
, | ,,,,,,,,,,,,,,,
. | hash3,----.
, | , |hmac
, v , v
, REF#1 -> bud -> bud,-> auth ...
,,,|,,,,,,,,,,,,,,,,,,
v
REF#2 -> ...
|
V
...
Note how hash3 covers CS, REF#0 and REF#1 so that it is not possible to
exchange or skip any reference nodes. Unlike the picture suggests the
auth nodes themselves are not hashed.
With this it is possible for an offline attacker to cut each journal
head or to drop the last reference node(s), but not to skip any journal
heads or to reorder any operations.
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Signed-off-by: Richard Weinberger <richard@nod.at>
2018-09-07 12:36:36 +00:00
|
|
|
|
static int ubifs_hash_nodes(struct ubifs_info *c, void *node,
|
|
|
|
|
int len, struct shash_desc *hash)
|
|
|
|
|
{
|
|
|
|
|
int auth_node_size = ubifs_auth_node_sz(c);
|
|
|
|
|
int err;
|
|
|
|
|
|
|
|
|
|
while (1) {
|
|
|
|
|
const struct ubifs_ch *ch = node;
|
|
|
|
|
int nodelen = le32_to_cpu(ch->len);
|
|
|
|
|
|
|
|
|
|
ubifs_assert(c, len >= auth_node_size);
|
|
|
|
|
|
|
|
|
|
if (len == auth_node_size)
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
ubifs_assert(c, len > nodelen);
|
|
|
|
|
ubifs_assert(c, ch->magic == cpu_to_le32(UBIFS_NODE_MAGIC));
|
|
|
|
|
|
|
|
|
|
err = ubifs_shash_update(c, hash, (void *)node, nodelen);
|
|
|
|
|
if (err)
|
|
|
|
|
return err;
|
|
|
|
|
|
|
|
|
|
node += ALIGN(nodelen, 8);
|
|
|
|
|
len -= ALIGN(nodelen, 8);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ubifs_prepare_auth_node(c, node, hash);
|
|
|
|
|
}
|
|
|
|
|
|
2008-07-14 16:08:37 +00:00
|
|
|
|
/**
|
|
|
|
|
* write_head - write data to a journal head.
|
|
|
|
|
* @c: UBIFS file-system description object
|
|
|
|
|
* @jhead: journal head
|
|
|
|
|
* @buf: buffer to write
|
|
|
|
|
* @len: length to write
|
|
|
|
|
* @lnum: LEB number written is returned here
|
|
|
|
|
* @offs: offset written is returned here
|
|
|
|
|
* @sync: non-zero if the write-buffer has to by synchronized
|
|
|
|
|
*
|
2018-09-07 12:36:28 +00:00
|
|
|
|
* This function writes data to the reserved space of journal head @jhead.
|
|
|
|
|
* Returns zero in case of success and a negative error code in case of
|
|
|
|
|
* failure.
|
2008-07-14 16:08:37 +00:00
|
|
|
|
*/
|
|
|
|
|
static int write_head(struct ubifs_info *c, int jhead, void *buf, int len,
|
|
|
|
|
int *lnum, int *offs, int sync)
|
|
|
|
|
{
|
|
|
|
|
int err;
|
|
|
|
|
struct ubifs_wbuf *wbuf = &c->jheads[jhead].wbuf;
|
|
|
|
|
|
2018-07-12 11:01:57 +00:00
|
|
|
|
ubifs_assert(c, jhead != GCHD);
|
2008-07-14 16:08:37 +00:00
|
|
|
|
|
|
|
|
|
*lnum = c->jheads[jhead].wbuf.lnum;
|
|
|
|
|
*offs = c->jheads[jhead].wbuf.offs + c->jheads[jhead].wbuf.used;
|
2009-09-15 12:03:51 +00:00
|
|
|
|
dbg_jnl("jhead %s, LEB %d:%d, len %d",
|
|
|
|
|
dbg_jhead(jhead), *lnum, *offs, len);
|
2008-07-14 16:08:37 +00:00
|
|
|
|
|
ubifs: Add authentication nodes to journal
Nodes that are written to flash can only be authenticated through the
index after the next commit. When a journal replay is necessary the
nodes are not yet referenced by the index and thus can't be
authenticated.
This patch overcomes this situation by creating a hash over all nodes
beginning from the commit start node over the reference node(s) and
the buds themselves. From
time to time we insert authentication nodes. Authentication nodes
contain a HMAC from the current hash state, so that they can be
used to authenticate a journal replay up to the point where the
authentication node is. The hash is continued afterwards
so that theoretically we would only have to check the HMAC of
the last authentication node we find.
Overall we get this picture:
,,,,,,,,
,......,...........................................
,. CS , hash1.----. hash2.----.
,. | , . |hmac . |hmac
,. v , . v . v
,.REF#0,-> bud -> bud -> bud.-> auth -> bud -> bud.-> auth ...
,..|...,...........................................
, | ,
, | ,,,,,,,,,,,,,,,
. | hash3,----.
, | , |hmac
, v , v
, REF#1 -> bud -> bud,-> auth ...
,,,|,,,,,,,,,,,,,,,,,,
v
REF#2 -> ...
|
V
...
Note how hash3 covers CS, REF#0 and REF#1 so that it is not possible to
exchange or skip any reference nodes. Unlike the picture suggests the
auth nodes themselves are not hashed.
With this it is possible for an offline attacker to cut each journal
head or to drop the last reference node(s), but not to skip any journal
heads or to reorder any operations.
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Signed-off-by: Richard Weinberger <richard@nod.at>
2018-09-07 12:36:36 +00:00
|
|
|
|
if (ubifs_authenticated(c)) {
|
|
|
|
|
err = ubifs_hash_nodes(c, buf, len, c->jheads[jhead].log_hash);
|
|
|
|
|
if (err)
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
2008-07-14 16:08:37 +00:00
|
|
|
|
err = ubifs_wbuf_write_nolock(wbuf, buf, len);
|
|
|
|
|
if (err)
|
|
|
|
|
return err;
|
|
|
|
|
if (sync)
|
|
|
|
|
err = ubifs_wbuf_sync_nolock(wbuf);
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
ubifs: Queue up space reservation tasks if retrying many times
Recently we catched ENOSPC returned by make_reservation() while doing
fsstress on UBIFS, we got following information when it occurred (See
details in Link):
UBIFS error (ubi0:0 pid 3640152): make_reservation [ubifs]: cannot
reserve 112 bytes in jhead 2, error -28
CPU: 2 PID: 3640152 Comm: kworker/u16:2 Tainted: G B W
Hardware name: Hisilicon PhosphorHi1230 EMU (DT)
Workqueue: writeback wb_workfn (flush-ubifs_0_0)
Call trace:
dump_stack+0x114/0x198
make_reservation+0x564/0x610 [ubifs]
ubifs_jnl_write_data+0x328/0x48c [ubifs]
do_writepage+0x2a8/0x3e4 [ubifs]
ubifs_writepage+0x16c/0x374 [ubifs]
generic_writepages+0xb4/0x114
do_writepages+0xcc/0x11c
writeback_sb_inodes+0x2d0/0x564
wb_writeback+0x20c/0x2b4
wb_workfn+0x404/0x510
process_one_work+0x304/0x4ac
worker_thread+0x31c/0x4e4
kthread+0x23c/0x290
Budgeting info: data budget sum 17576, total budget sum 17768
budg_data_growth 4144, budg_dd_growth 13432, budg_idx_growth 192
min_idx_lebs 13, old_idx_sz 988640, uncommitted_idx 0
page_budget 4144, inode_budget 160, dent_budget 312
nospace 0, nospace_rp 0
dark_wm 8192, dead_wm 4096, max_idx_node_sz 192
freeable_cnt 0, calc_idx_sz 988640, idx_gc_cnt 0
dirty_pg_cnt 4, dirty_zn_cnt 0, clean_zn_cnt 4811
gc_lnum 21, ihead_lnum 14
jhead 0 (GC) LEB 16
jhead 1 (base) LEB 34
jhead 2 (data) LEB 23
bud LEB 16
bud LEB 23
bud LEB 34
old bud LEB 33
old bud LEB 31
old bud LEB 15
commit state 4
Budgeting predictions:
available: 33832, outstanding 17576, free 15356
(pid 3640152) start dumping LEB properties
(pid 3640152) Lprops statistics: empty_lebs 3, idx_lebs 11
taken_empty_lebs 1, total_free 1253376, total_dirty 2445736
total_used 3438712, total_dark 65536, total_dead 17248
LEB 15 free 0 dirty 248000 used 5952 (taken)
LEB 16 free 110592 dirty 896 used 142464 (taken, jhead 0 (GC))
LEB 21 free 253952 dirty 0 used 0 (taken, GC LEB)
LEB 23 free 0 dirty 248104 used 5848 (taken, jhead 2 (data))
LEB 29 free 253952 dirty 0 used 0 (empty)
LEB 33 free 0 dirty 253952 used 0 (taken)
LEB 34 free 217088 dirty 36544 used 320 (taken, jhead 1 (base))
LEB 37 free 253952 dirty 0 used 0 (empty)
OTHERS: index lebs, zero-available non-index lebs
According to the budget algorithm, there are 5 LEBs reserved for budget:
three journal heads(16,23,34), 1 GC LEB(21) and 1 deletion LEB(can be
used in make_reservation()). There are 2 empty LEBs used for index nodes,
which is calculated as min_idx_lebs - idx_lebs = 2. In theory, LEB 15
and 33 should be reclaimed as free state after committing, but it is now
in taken state. After looking the realization of reserve_space(), there's
a possible situation:
LEB 15: free 2000 dirty 248000 used 3952 (jhead 2)
LEB 23: free 2000 dirty 248104 used 3848 (bud, taken)
LEB 33: free 2000 dirty 251952 used 0 (bud, taken)
wb_workfn wb_workfn_2
do_writepage // write 3000 bytes
ubifs_jnl_write_data
make_reservation
reserve_space
ubifs_garbage_collect
ubifs_find_dirty_leb // ret ENOSPC, dirty LEBs are taken
nospc_retries++ // 1
ubifs_run_commit
do_commit
LEB 15: free 2000 dirty 248000 used 3952 (jhead 2)
LEB 23: free 2000 dirty 248104 used 3848 (dirty)
LEB 33: free 2000 dirty 251952 used 0 (dirty)
do_writepage // write 2000 bytes for 3 times
ubifs_jnl_write_data
// grabs 15\23\33
LEB 15: free 0 dirty 248000 used 5952 (bud, taken)
LEB 23: free 0 dirty 248104 used 5848 (jhead 2)
LEB 33: free 0 dirty 253952 used 0 (bud, taken)
reserve_space
ubifs_garbage_collect
ubifs_find_dirty_leb // ret ENOSPC, dirty LEBs are taken
if (nospc_retries++ < 2) // false
ubifs_ro_mode !
Fetch a reproducer in Link.
The dirty LEBs could be grabbed by other threads, which fails finding dirty
LEBs of GC in current thread, so make_reservation() could try many times to
invoke GC&&committing, but current realization limits the times of retrying
as 'nospc_retries'(twice).
Fix it by adding a wait queue, start queuing up space reservation tasks
when someone task has retried gc + commit for many times. Then there is
only one task making space reservation at any time, and it can always make
success under the premise of correct budgeting.
Link: https://bugzilla.kernel.org/show_bug.cgi?id=218164
Fixes: 1e51764a3c2a ("UBIFS: add new flash file system")
Signed-off-by: Zhihao Cheng <chengzhihao1@huawei.com>
Signed-off-by: Zhang Yi <yi.zhang@huawei.com>
Signed-off-by: Richard Weinberger <richard@nod.at>
2024-01-22 06:31:03 +00:00
|
|
|
|
/**
|
|
|
|
|
* __queue_and_wait - queue a task and wait until the task is waked up.
|
|
|
|
|
* @c: UBIFS file-system description object
|
|
|
|
|
*
|
|
|
|
|
* This function adds current task in queue and waits until the task is waked
|
|
|
|
|
* up. This function should be called with @c->reserve_space_wq locked.
|
|
|
|
|
*/
|
|
|
|
|
static void __queue_and_wait(struct ubifs_info *c)
|
|
|
|
|
{
|
|
|
|
|
DEFINE_WAIT(wait);
|
|
|
|
|
|
|
|
|
|
__add_wait_queue_entry_tail_exclusive(&c->reserve_space_wq, &wait);
|
|
|
|
|
set_current_state(TASK_UNINTERRUPTIBLE);
|
|
|
|
|
spin_unlock(&c->reserve_space_wq.lock);
|
|
|
|
|
|
|
|
|
|
schedule();
|
|
|
|
|
finish_wait(&c->reserve_space_wq, &wait);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* wait_for_reservation - try queuing current task to wait until waked up.
|
|
|
|
|
* @c: UBIFS file-system description object
|
|
|
|
|
*
|
|
|
|
|
* This function queues current task to wait until waked up, if queuing is
|
|
|
|
|
* started(@c->need_wait_space is not %0). Returns %true if current task is
|
|
|
|
|
* added in queue, otherwise %false is returned.
|
|
|
|
|
*/
|
|
|
|
|
static bool wait_for_reservation(struct ubifs_info *c)
|
|
|
|
|
{
|
|
|
|
|
if (likely(atomic_read(&c->need_wait_space) == 0))
|
|
|
|
|
/* Quick path to check whether queuing is started. */
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
spin_lock(&c->reserve_space_wq.lock);
|
|
|
|
|
if (atomic_read(&c->need_wait_space) == 0) {
|
|
|
|
|
/* Queuing is not started, don't queue current task. */
|
|
|
|
|
spin_unlock(&c->reserve_space_wq.lock);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
__queue_and_wait(c);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* wake_up_reservation - wake up first task in queue or stop queuing.
|
|
|
|
|
* @c: UBIFS file-system description object
|
|
|
|
|
*
|
|
|
|
|
* This function wakes up the first task in queue if it exists, or stops
|
|
|
|
|
* queuing if no tasks in queue.
|
|
|
|
|
*/
|
|
|
|
|
static void wake_up_reservation(struct ubifs_info *c)
|
|
|
|
|
{
|
|
|
|
|
spin_lock(&c->reserve_space_wq.lock);
|
|
|
|
|
if (waitqueue_active(&c->reserve_space_wq))
|
|
|
|
|
wake_up_locked(&c->reserve_space_wq);
|
|
|
|
|
else
|
|
|
|
|
/*
|
|
|
|
|
* Compared with wait_for_reservation(), set @c->need_wait_space
|
|
|
|
|
* under the protection of wait queue lock, which can avoid that
|
|
|
|
|
* @c->need_wait_space is set to 0 after new task queued.
|
|
|
|
|
*/
|
|
|
|
|
atomic_set(&c->need_wait_space, 0);
|
|
|
|
|
spin_unlock(&c->reserve_space_wq.lock);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* wake_up_reservation - add current task in queue or start queuing.
|
|
|
|
|
* @c: UBIFS file-system description object
|
|
|
|
|
*
|
|
|
|
|
* This function starts queuing if queuing is not started, otherwise adds
|
|
|
|
|
* current task in queue.
|
|
|
|
|
*/
|
|
|
|
|
static void add_or_start_queue(struct ubifs_info *c)
|
|
|
|
|
{
|
|
|
|
|
spin_lock(&c->reserve_space_wq.lock);
|
|
|
|
|
if (atomic_cmpxchg(&c->need_wait_space, 0, 1) == 0) {
|
|
|
|
|
/* Starts queuing, task can go on directly. */
|
|
|
|
|
spin_unlock(&c->reserve_space_wq.lock);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* There are at least two tasks have retried more than 32 times
|
|
|
|
|
* at certain point, first task has started queuing, just queue
|
|
|
|
|
* the left tasks.
|
|
|
|
|
*/
|
|
|
|
|
__queue_and_wait(c);
|
|
|
|
|
}
|
|
|
|
|
|
2008-07-14 16:08:37 +00:00
|
|
|
|
/**
|
|
|
|
|
* make_reservation - reserve journal space.
|
|
|
|
|
* @c: UBIFS file-system description object
|
|
|
|
|
* @jhead: journal head
|
|
|
|
|
* @len: how many bytes to reserve
|
|
|
|
|
*
|
|
|
|
|
* This function makes space reservation in journal head @jhead. The function
|
|
|
|
|
* takes the commit lock and locks the journal head, and the caller has to
|
|
|
|
|
* unlock the head and finish the reservation with 'finish_reservation()'.
|
|
|
|
|
* Returns zero in case of success and a negative error code in case of
|
|
|
|
|
* failure.
|
|
|
|
|
*
|
|
|
|
|
* Note, the journal head may be unlocked as soon as the data is written, while
|
|
|
|
|
* the commit lock has to be released after the data has been added to the
|
|
|
|
|
* TNC.
|
|
|
|
|
*/
|
|
|
|
|
static int make_reservation(struct ubifs_info *c, int jhead, int len)
|
|
|
|
|
{
|
|
|
|
|
int err, cmt_retries = 0, nospc_retries = 0;
|
ubifs: Queue up space reservation tasks if retrying many times
Recently we catched ENOSPC returned by make_reservation() while doing
fsstress on UBIFS, we got following information when it occurred (See
details in Link):
UBIFS error (ubi0:0 pid 3640152): make_reservation [ubifs]: cannot
reserve 112 bytes in jhead 2, error -28
CPU: 2 PID: 3640152 Comm: kworker/u16:2 Tainted: G B W
Hardware name: Hisilicon PhosphorHi1230 EMU (DT)
Workqueue: writeback wb_workfn (flush-ubifs_0_0)
Call trace:
dump_stack+0x114/0x198
make_reservation+0x564/0x610 [ubifs]
ubifs_jnl_write_data+0x328/0x48c [ubifs]
do_writepage+0x2a8/0x3e4 [ubifs]
ubifs_writepage+0x16c/0x374 [ubifs]
generic_writepages+0xb4/0x114
do_writepages+0xcc/0x11c
writeback_sb_inodes+0x2d0/0x564
wb_writeback+0x20c/0x2b4
wb_workfn+0x404/0x510
process_one_work+0x304/0x4ac
worker_thread+0x31c/0x4e4
kthread+0x23c/0x290
Budgeting info: data budget sum 17576, total budget sum 17768
budg_data_growth 4144, budg_dd_growth 13432, budg_idx_growth 192
min_idx_lebs 13, old_idx_sz 988640, uncommitted_idx 0
page_budget 4144, inode_budget 160, dent_budget 312
nospace 0, nospace_rp 0
dark_wm 8192, dead_wm 4096, max_idx_node_sz 192
freeable_cnt 0, calc_idx_sz 988640, idx_gc_cnt 0
dirty_pg_cnt 4, dirty_zn_cnt 0, clean_zn_cnt 4811
gc_lnum 21, ihead_lnum 14
jhead 0 (GC) LEB 16
jhead 1 (base) LEB 34
jhead 2 (data) LEB 23
bud LEB 16
bud LEB 23
bud LEB 34
old bud LEB 33
old bud LEB 31
old bud LEB 15
commit state 4
Budgeting predictions:
available: 33832, outstanding 17576, free 15356
(pid 3640152) start dumping LEB properties
(pid 3640152) Lprops statistics: empty_lebs 3, idx_lebs 11
taken_empty_lebs 1, total_free 1253376, total_dirty 2445736
total_used 3438712, total_dark 65536, total_dead 17248
LEB 15 free 0 dirty 248000 used 5952 (taken)
LEB 16 free 110592 dirty 896 used 142464 (taken, jhead 0 (GC))
LEB 21 free 253952 dirty 0 used 0 (taken, GC LEB)
LEB 23 free 0 dirty 248104 used 5848 (taken, jhead 2 (data))
LEB 29 free 253952 dirty 0 used 0 (empty)
LEB 33 free 0 dirty 253952 used 0 (taken)
LEB 34 free 217088 dirty 36544 used 320 (taken, jhead 1 (base))
LEB 37 free 253952 dirty 0 used 0 (empty)
OTHERS: index lebs, zero-available non-index lebs
According to the budget algorithm, there are 5 LEBs reserved for budget:
three journal heads(16,23,34), 1 GC LEB(21) and 1 deletion LEB(can be
used in make_reservation()). There are 2 empty LEBs used for index nodes,
which is calculated as min_idx_lebs - idx_lebs = 2. In theory, LEB 15
and 33 should be reclaimed as free state after committing, but it is now
in taken state. After looking the realization of reserve_space(), there's
a possible situation:
LEB 15: free 2000 dirty 248000 used 3952 (jhead 2)
LEB 23: free 2000 dirty 248104 used 3848 (bud, taken)
LEB 33: free 2000 dirty 251952 used 0 (bud, taken)
wb_workfn wb_workfn_2
do_writepage // write 3000 bytes
ubifs_jnl_write_data
make_reservation
reserve_space
ubifs_garbage_collect
ubifs_find_dirty_leb // ret ENOSPC, dirty LEBs are taken
nospc_retries++ // 1
ubifs_run_commit
do_commit
LEB 15: free 2000 dirty 248000 used 3952 (jhead 2)
LEB 23: free 2000 dirty 248104 used 3848 (dirty)
LEB 33: free 2000 dirty 251952 used 0 (dirty)
do_writepage // write 2000 bytes for 3 times
ubifs_jnl_write_data
// grabs 15\23\33
LEB 15: free 0 dirty 248000 used 5952 (bud, taken)
LEB 23: free 0 dirty 248104 used 5848 (jhead 2)
LEB 33: free 0 dirty 253952 used 0 (bud, taken)
reserve_space
ubifs_garbage_collect
ubifs_find_dirty_leb // ret ENOSPC, dirty LEBs are taken
if (nospc_retries++ < 2) // false
ubifs_ro_mode !
Fetch a reproducer in Link.
The dirty LEBs could be grabbed by other threads, which fails finding dirty
LEBs of GC in current thread, so make_reservation() could try many times to
invoke GC&&committing, but current realization limits the times of retrying
as 'nospc_retries'(twice).
Fix it by adding a wait queue, start queuing up space reservation tasks
when someone task has retried gc + commit for many times. Then there is
only one task making space reservation at any time, and it can always make
success under the premise of correct budgeting.
Link: https://bugzilla.kernel.org/show_bug.cgi?id=218164
Fixes: 1e51764a3c2a ("UBIFS: add new flash file system")
Signed-off-by: Zhihao Cheng <chengzhihao1@huawei.com>
Signed-off-by: Zhang Yi <yi.zhang@huawei.com>
Signed-off-by: Richard Weinberger <richard@nod.at>
2024-01-22 06:31:03 +00:00
|
|
|
|
bool blocked = wait_for_reservation(c);
|
2008-07-14 16:08:37 +00:00
|
|
|
|
|
|
|
|
|
again:
|
|
|
|
|
down_read(&c->commit_sem);
|
|
|
|
|
err = reserve_space(c, jhead, len);
|
ubifs: Queue up space reservation tasks if retrying many times
Recently we catched ENOSPC returned by make_reservation() while doing
fsstress on UBIFS, we got following information when it occurred (See
details in Link):
UBIFS error (ubi0:0 pid 3640152): make_reservation [ubifs]: cannot
reserve 112 bytes in jhead 2, error -28
CPU: 2 PID: 3640152 Comm: kworker/u16:2 Tainted: G B W
Hardware name: Hisilicon PhosphorHi1230 EMU (DT)
Workqueue: writeback wb_workfn (flush-ubifs_0_0)
Call trace:
dump_stack+0x114/0x198
make_reservation+0x564/0x610 [ubifs]
ubifs_jnl_write_data+0x328/0x48c [ubifs]
do_writepage+0x2a8/0x3e4 [ubifs]
ubifs_writepage+0x16c/0x374 [ubifs]
generic_writepages+0xb4/0x114
do_writepages+0xcc/0x11c
writeback_sb_inodes+0x2d0/0x564
wb_writeback+0x20c/0x2b4
wb_workfn+0x404/0x510
process_one_work+0x304/0x4ac
worker_thread+0x31c/0x4e4
kthread+0x23c/0x290
Budgeting info: data budget sum 17576, total budget sum 17768
budg_data_growth 4144, budg_dd_growth 13432, budg_idx_growth 192
min_idx_lebs 13, old_idx_sz 988640, uncommitted_idx 0
page_budget 4144, inode_budget 160, dent_budget 312
nospace 0, nospace_rp 0
dark_wm 8192, dead_wm 4096, max_idx_node_sz 192
freeable_cnt 0, calc_idx_sz 988640, idx_gc_cnt 0
dirty_pg_cnt 4, dirty_zn_cnt 0, clean_zn_cnt 4811
gc_lnum 21, ihead_lnum 14
jhead 0 (GC) LEB 16
jhead 1 (base) LEB 34
jhead 2 (data) LEB 23
bud LEB 16
bud LEB 23
bud LEB 34
old bud LEB 33
old bud LEB 31
old bud LEB 15
commit state 4
Budgeting predictions:
available: 33832, outstanding 17576, free 15356
(pid 3640152) start dumping LEB properties
(pid 3640152) Lprops statistics: empty_lebs 3, idx_lebs 11
taken_empty_lebs 1, total_free 1253376, total_dirty 2445736
total_used 3438712, total_dark 65536, total_dead 17248
LEB 15 free 0 dirty 248000 used 5952 (taken)
LEB 16 free 110592 dirty 896 used 142464 (taken, jhead 0 (GC))
LEB 21 free 253952 dirty 0 used 0 (taken, GC LEB)
LEB 23 free 0 dirty 248104 used 5848 (taken, jhead 2 (data))
LEB 29 free 253952 dirty 0 used 0 (empty)
LEB 33 free 0 dirty 253952 used 0 (taken)
LEB 34 free 217088 dirty 36544 used 320 (taken, jhead 1 (base))
LEB 37 free 253952 dirty 0 used 0 (empty)
OTHERS: index lebs, zero-available non-index lebs
According to the budget algorithm, there are 5 LEBs reserved for budget:
three journal heads(16,23,34), 1 GC LEB(21) and 1 deletion LEB(can be
used in make_reservation()). There are 2 empty LEBs used for index nodes,
which is calculated as min_idx_lebs - idx_lebs = 2. In theory, LEB 15
and 33 should be reclaimed as free state after committing, but it is now
in taken state. After looking the realization of reserve_space(), there's
a possible situation:
LEB 15: free 2000 dirty 248000 used 3952 (jhead 2)
LEB 23: free 2000 dirty 248104 used 3848 (bud, taken)
LEB 33: free 2000 dirty 251952 used 0 (bud, taken)
wb_workfn wb_workfn_2
do_writepage // write 3000 bytes
ubifs_jnl_write_data
make_reservation
reserve_space
ubifs_garbage_collect
ubifs_find_dirty_leb // ret ENOSPC, dirty LEBs are taken
nospc_retries++ // 1
ubifs_run_commit
do_commit
LEB 15: free 2000 dirty 248000 used 3952 (jhead 2)
LEB 23: free 2000 dirty 248104 used 3848 (dirty)
LEB 33: free 2000 dirty 251952 used 0 (dirty)
do_writepage // write 2000 bytes for 3 times
ubifs_jnl_write_data
// grabs 15\23\33
LEB 15: free 0 dirty 248000 used 5952 (bud, taken)
LEB 23: free 0 dirty 248104 used 5848 (jhead 2)
LEB 33: free 0 dirty 253952 used 0 (bud, taken)
reserve_space
ubifs_garbage_collect
ubifs_find_dirty_leb // ret ENOSPC, dirty LEBs are taken
if (nospc_retries++ < 2) // false
ubifs_ro_mode !
Fetch a reproducer in Link.
The dirty LEBs could be grabbed by other threads, which fails finding dirty
LEBs of GC in current thread, so make_reservation() could try many times to
invoke GC&&committing, but current realization limits the times of retrying
as 'nospc_retries'(twice).
Fix it by adding a wait queue, start queuing up space reservation tasks
when someone task has retried gc + commit for many times. Then there is
only one task making space reservation at any time, and it can always make
success under the premise of correct budgeting.
Link: https://bugzilla.kernel.org/show_bug.cgi?id=218164
Fixes: 1e51764a3c2a ("UBIFS: add new flash file system")
Signed-off-by: Zhihao Cheng <chengzhihao1@huawei.com>
Signed-off-by: Zhang Yi <yi.zhang@huawei.com>
Signed-off-by: Richard Weinberger <richard@nod.at>
2024-01-22 06:31:03 +00:00
|
|
|
|
if (!err) {
|
2018-08-13 13:14:45 +00:00
|
|
|
|
/* c->commit_sem will get released via finish_reservation(). */
|
ubifs: Queue up space reservation tasks if retrying many times
Recently we catched ENOSPC returned by make_reservation() while doing
fsstress on UBIFS, we got following information when it occurred (See
details in Link):
UBIFS error (ubi0:0 pid 3640152): make_reservation [ubifs]: cannot
reserve 112 bytes in jhead 2, error -28
CPU: 2 PID: 3640152 Comm: kworker/u16:2 Tainted: G B W
Hardware name: Hisilicon PhosphorHi1230 EMU (DT)
Workqueue: writeback wb_workfn (flush-ubifs_0_0)
Call trace:
dump_stack+0x114/0x198
make_reservation+0x564/0x610 [ubifs]
ubifs_jnl_write_data+0x328/0x48c [ubifs]
do_writepage+0x2a8/0x3e4 [ubifs]
ubifs_writepage+0x16c/0x374 [ubifs]
generic_writepages+0xb4/0x114
do_writepages+0xcc/0x11c
writeback_sb_inodes+0x2d0/0x564
wb_writeback+0x20c/0x2b4
wb_workfn+0x404/0x510
process_one_work+0x304/0x4ac
worker_thread+0x31c/0x4e4
kthread+0x23c/0x290
Budgeting info: data budget sum 17576, total budget sum 17768
budg_data_growth 4144, budg_dd_growth 13432, budg_idx_growth 192
min_idx_lebs 13, old_idx_sz 988640, uncommitted_idx 0
page_budget 4144, inode_budget 160, dent_budget 312
nospace 0, nospace_rp 0
dark_wm 8192, dead_wm 4096, max_idx_node_sz 192
freeable_cnt 0, calc_idx_sz 988640, idx_gc_cnt 0
dirty_pg_cnt 4, dirty_zn_cnt 0, clean_zn_cnt 4811
gc_lnum 21, ihead_lnum 14
jhead 0 (GC) LEB 16
jhead 1 (base) LEB 34
jhead 2 (data) LEB 23
bud LEB 16
bud LEB 23
bud LEB 34
old bud LEB 33
old bud LEB 31
old bud LEB 15
commit state 4
Budgeting predictions:
available: 33832, outstanding 17576, free 15356
(pid 3640152) start dumping LEB properties
(pid 3640152) Lprops statistics: empty_lebs 3, idx_lebs 11
taken_empty_lebs 1, total_free 1253376, total_dirty 2445736
total_used 3438712, total_dark 65536, total_dead 17248
LEB 15 free 0 dirty 248000 used 5952 (taken)
LEB 16 free 110592 dirty 896 used 142464 (taken, jhead 0 (GC))
LEB 21 free 253952 dirty 0 used 0 (taken, GC LEB)
LEB 23 free 0 dirty 248104 used 5848 (taken, jhead 2 (data))
LEB 29 free 253952 dirty 0 used 0 (empty)
LEB 33 free 0 dirty 253952 used 0 (taken)
LEB 34 free 217088 dirty 36544 used 320 (taken, jhead 1 (base))
LEB 37 free 253952 dirty 0 used 0 (empty)
OTHERS: index lebs, zero-available non-index lebs
According to the budget algorithm, there are 5 LEBs reserved for budget:
three journal heads(16,23,34), 1 GC LEB(21) and 1 deletion LEB(can be
used in make_reservation()). There are 2 empty LEBs used for index nodes,
which is calculated as min_idx_lebs - idx_lebs = 2. In theory, LEB 15
and 33 should be reclaimed as free state after committing, but it is now
in taken state. After looking the realization of reserve_space(), there's
a possible situation:
LEB 15: free 2000 dirty 248000 used 3952 (jhead 2)
LEB 23: free 2000 dirty 248104 used 3848 (bud, taken)
LEB 33: free 2000 dirty 251952 used 0 (bud, taken)
wb_workfn wb_workfn_2
do_writepage // write 3000 bytes
ubifs_jnl_write_data
make_reservation
reserve_space
ubifs_garbage_collect
ubifs_find_dirty_leb // ret ENOSPC, dirty LEBs are taken
nospc_retries++ // 1
ubifs_run_commit
do_commit
LEB 15: free 2000 dirty 248000 used 3952 (jhead 2)
LEB 23: free 2000 dirty 248104 used 3848 (dirty)
LEB 33: free 2000 dirty 251952 used 0 (dirty)
do_writepage // write 2000 bytes for 3 times
ubifs_jnl_write_data
// grabs 15\23\33
LEB 15: free 0 dirty 248000 used 5952 (bud, taken)
LEB 23: free 0 dirty 248104 used 5848 (jhead 2)
LEB 33: free 0 dirty 253952 used 0 (bud, taken)
reserve_space
ubifs_garbage_collect
ubifs_find_dirty_leb // ret ENOSPC, dirty LEBs are taken
if (nospc_retries++ < 2) // false
ubifs_ro_mode !
Fetch a reproducer in Link.
The dirty LEBs could be grabbed by other threads, which fails finding dirty
LEBs of GC in current thread, so make_reservation() could try many times to
invoke GC&&committing, but current realization limits the times of retrying
as 'nospc_retries'(twice).
Fix it by adding a wait queue, start queuing up space reservation tasks
when someone task has retried gc + commit for many times. Then there is
only one task making space reservation at any time, and it can always make
success under the premise of correct budgeting.
Link: https://bugzilla.kernel.org/show_bug.cgi?id=218164
Fixes: 1e51764a3c2a ("UBIFS: add new flash file system")
Signed-off-by: Zhihao Cheng <chengzhihao1@huawei.com>
Signed-off-by: Zhang Yi <yi.zhang@huawei.com>
Signed-off-by: Richard Weinberger <richard@nod.at>
2024-01-22 06:31:03 +00:00
|
|
|
|
goto out_wake_up;
|
|
|
|
|
}
|
2008-07-14 16:08:37 +00:00
|
|
|
|
up_read(&c->commit_sem);
|
|
|
|
|
|
|
|
|
|
if (err == -ENOSPC) {
|
|
|
|
|
/*
|
|
|
|
|
* GC could not make any progress. We should try to commit
|
ubifs: Queue up space reservation tasks if retrying many times
Recently we catched ENOSPC returned by make_reservation() while doing
fsstress on UBIFS, we got following information when it occurred (See
details in Link):
UBIFS error (ubi0:0 pid 3640152): make_reservation [ubifs]: cannot
reserve 112 bytes in jhead 2, error -28
CPU: 2 PID: 3640152 Comm: kworker/u16:2 Tainted: G B W
Hardware name: Hisilicon PhosphorHi1230 EMU (DT)
Workqueue: writeback wb_workfn (flush-ubifs_0_0)
Call trace:
dump_stack+0x114/0x198
make_reservation+0x564/0x610 [ubifs]
ubifs_jnl_write_data+0x328/0x48c [ubifs]
do_writepage+0x2a8/0x3e4 [ubifs]
ubifs_writepage+0x16c/0x374 [ubifs]
generic_writepages+0xb4/0x114
do_writepages+0xcc/0x11c
writeback_sb_inodes+0x2d0/0x564
wb_writeback+0x20c/0x2b4
wb_workfn+0x404/0x510
process_one_work+0x304/0x4ac
worker_thread+0x31c/0x4e4
kthread+0x23c/0x290
Budgeting info: data budget sum 17576, total budget sum 17768
budg_data_growth 4144, budg_dd_growth 13432, budg_idx_growth 192
min_idx_lebs 13, old_idx_sz 988640, uncommitted_idx 0
page_budget 4144, inode_budget 160, dent_budget 312
nospace 0, nospace_rp 0
dark_wm 8192, dead_wm 4096, max_idx_node_sz 192
freeable_cnt 0, calc_idx_sz 988640, idx_gc_cnt 0
dirty_pg_cnt 4, dirty_zn_cnt 0, clean_zn_cnt 4811
gc_lnum 21, ihead_lnum 14
jhead 0 (GC) LEB 16
jhead 1 (base) LEB 34
jhead 2 (data) LEB 23
bud LEB 16
bud LEB 23
bud LEB 34
old bud LEB 33
old bud LEB 31
old bud LEB 15
commit state 4
Budgeting predictions:
available: 33832, outstanding 17576, free 15356
(pid 3640152) start dumping LEB properties
(pid 3640152) Lprops statistics: empty_lebs 3, idx_lebs 11
taken_empty_lebs 1, total_free 1253376, total_dirty 2445736
total_used 3438712, total_dark 65536, total_dead 17248
LEB 15 free 0 dirty 248000 used 5952 (taken)
LEB 16 free 110592 dirty 896 used 142464 (taken, jhead 0 (GC))
LEB 21 free 253952 dirty 0 used 0 (taken, GC LEB)
LEB 23 free 0 dirty 248104 used 5848 (taken, jhead 2 (data))
LEB 29 free 253952 dirty 0 used 0 (empty)
LEB 33 free 0 dirty 253952 used 0 (taken)
LEB 34 free 217088 dirty 36544 used 320 (taken, jhead 1 (base))
LEB 37 free 253952 dirty 0 used 0 (empty)
OTHERS: index lebs, zero-available non-index lebs
According to the budget algorithm, there are 5 LEBs reserved for budget:
three journal heads(16,23,34), 1 GC LEB(21) and 1 deletion LEB(can be
used in make_reservation()). There are 2 empty LEBs used for index nodes,
which is calculated as min_idx_lebs - idx_lebs = 2. In theory, LEB 15
and 33 should be reclaimed as free state after committing, but it is now
in taken state. After looking the realization of reserve_space(), there's
a possible situation:
LEB 15: free 2000 dirty 248000 used 3952 (jhead 2)
LEB 23: free 2000 dirty 248104 used 3848 (bud, taken)
LEB 33: free 2000 dirty 251952 used 0 (bud, taken)
wb_workfn wb_workfn_2
do_writepage // write 3000 bytes
ubifs_jnl_write_data
make_reservation
reserve_space
ubifs_garbage_collect
ubifs_find_dirty_leb // ret ENOSPC, dirty LEBs are taken
nospc_retries++ // 1
ubifs_run_commit
do_commit
LEB 15: free 2000 dirty 248000 used 3952 (jhead 2)
LEB 23: free 2000 dirty 248104 used 3848 (dirty)
LEB 33: free 2000 dirty 251952 used 0 (dirty)
do_writepage // write 2000 bytes for 3 times
ubifs_jnl_write_data
// grabs 15\23\33
LEB 15: free 0 dirty 248000 used 5952 (bud, taken)
LEB 23: free 0 dirty 248104 used 5848 (jhead 2)
LEB 33: free 0 dirty 253952 used 0 (bud, taken)
reserve_space
ubifs_garbage_collect
ubifs_find_dirty_leb // ret ENOSPC, dirty LEBs are taken
if (nospc_retries++ < 2) // false
ubifs_ro_mode !
Fetch a reproducer in Link.
The dirty LEBs could be grabbed by other threads, which fails finding dirty
LEBs of GC in current thread, so make_reservation() could try many times to
invoke GC&&committing, but current realization limits the times of retrying
as 'nospc_retries'(twice).
Fix it by adding a wait queue, start queuing up space reservation tasks
when someone task has retried gc + commit for many times. Then there is
only one task making space reservation at any time, and it can always make
success under the premise of correct budgeting.
Link: https://bugzilla.kernel.org/show_bug.cgi?id=218164
Fixes: 1e51764a3c2a ("UBIFS: add new flash file system")
Signed-off-by: Zhihao Cheng <chengzhihao1@huawei.com>
Signed-off-by: Zhang Yi <yi.zhang@huawei.com>
Signed-off-by: Richard Weinberger <richard@nod.at>
2024-01-22 06:31:03 +00:00
|
|
|
|
* because it could make some dirty space and GC would make
|
|
|
|
|
* progress, so make the error -EAGAIN so that the below
|
2008-07-14 16:08:37 +00:00
|
|
|
|
* will commit and re-try.
|
|
|
|
|
*/
|
ubifs: Queue up space reservation tasks if retrying many times
Recently we catched ENOSPC returned by make_reservation() while doing
fsstress on UBIFS, we got following information when it occurred (See
details in Link):
UBIFS error (ubi0:0 pid 3640152): make_reservation [ubifs]: cannot
reserve 112 bytes in jhead 2, error -28
CPU: 2 PID: 3640152 Comm: kworker/u16:2 Tainted: G B W
Hardware name: Hisilicon PhosphorHi1230 EMU (DT)
Workqueue: writeback wb_workfn (flush-ubifs_0_0)
Call trace:
dump_stack+0x114/0x198
make_reservation+0x564/0x610 [ubifs]
ubifs_jnl_write_data+0x328/0x48c [ubifs]
do_writepage+0x2a8/0x3e4 [ubifs]
ubifs_writepage+0x16c/0x374 [ubifs]
generic_writepages+0xb4/0x114
do_writepages+0xcc/0x11c
writeback_sb_inodes+0x2d0/0x564
wb_writeback+0x20c/0x2b4
wb_workfn+0x404/0x510
process_one_work+0x304/0x4ac
worker_thread+0x31c/0x4e4
kthread+0x23c/0x290
Budgeting info: data budget sum 17576, total budget sum 17768
budg_data_growth 4144, budg_dd_growth 13432, budg_idx_growth 192
min_idx_lebs 13, old_idx_sz 988640, uncommitted_idx 0
page_budget 4144, inode_budget 160, dent_budget 312
nospace 0, nospace_rp 0
dark_wm 8192, dead_wm 4096, max_idx_node_sz 192
freeable_cnt 0, calc_idx_sz 988640, idx_gc_cnt 0
dirty_pg_cnt 4, dirty_zn_cnt 0, clean_zn_cnt 4811
gc_lnum 21, ihead_lnum 14
jhead 0 (GC) LEB 16
jhead 1 (base) LEB 34
jhead 2 (data) LEB 23
bud LEB 16
bud LEB 23
bud LEB 34
old bud LEB 33
old bud LEB 31
old bud LEB 15
commit state 4
Budgeting predictions:
available: 33832, outstanding 17576, free 15356
(pid 3640152) start dumping LEB properties
(pid 3640152) Lprops statistics: empty_lebs 3, idx_lebs 11
taken_empty_lebs 1, total_free 1253376, total_dirty 2445736
total_used 3438712, total_dark 65536, total_dead 17248
LEB 15 free 0 dirty 248000 used 5952 (taken)
LEB 16 free 110592 dirty 896 used 142464 (taken, jhead 0 (GC))
LEB 21 free 253952 dirty 0 used 0 (taken, GC LEB)
LEB 23 free 0 dirty 248104 used 5848 (taken, jhead 2 (data))
LEB 29 free 253952 dirty 0 used 0 (empty)
LEB 33 free 0 dirty 253952 used 0 (taken)
LEB 34 free 217088 dirty 36544 used 320 (taken, jhead 1 (base))
LEB 37 free 253952 dirty 0 used 0 (empty)
OTHERS: index lebs, zero-available non-index lebs
According to the budget algorithm, there are 5 LEBs reserved for budget:
three journal heads(16,23,34), 1 GC LEB(21) and 1 deletion LEB(can be
used in make_reservation()). There are 2 empty LEBs used for index nodes,
which is calculated as min_idx_lebs - idx_lebs = 2. In theory, LEB 15
and 33 should be reclaimed as free state after committing, but it is now
in taken state. After looking the realization of reserve_space(), there's
a possible situation:
LEB 15: free 2000 dirty 248000 used 3952 (jhead 2)
LEB 23: free 2000 dirty 248104 used 3848 (bud, taken)
LEB 33: free 2000 dirty 251952 used 0 (bud, taken)
wb_workfn wb_workfn_2
do_writepage // write 3000 bytes
ubifs_jnl_write_data
make_reservation
reserve_space
ubifs_garbage_collect
ubifs_find_dirty_leb // ret ENOSPC, dirty LEBs are taken
nospc_retries++ // 1
ubifs_run_commit
do_commit
LEB 15: free 2000 dirty 248000 used 3952 (jhead 2)
LEB 23: free 2000 dirty 248104 used 3848 (dirty)
LEB 33: free 2000 dirty 251952 used 0 (dirty)
do_writepage // write 2000 bytes for 3 times
ubifs_jnl_write_data
// grabs 15\23\33
LEB 15: free 0 dirty 248000 used 5952 (bud, taken)
LEB 23: free 0 dirty 248104 used 5848 (jhead 2)
LEB 33: free 0 dirty 253952 used 0 (bud, taken)
reserve_space
ubifs_garbage_collect
ubifs_find_dirty_leb // ret ENOSPC, dirty LEBs are taken
if (nospc_retries++ < 2) // false
ubifs_ro_mode !
Fetch a reproducer in Link.
The dirty LEBs could be grabbed by other threads, which fails finding dirty
LEBs of GC in current thread, so make_reservation() could try many times to
invoke GC&&committing, but current realization limits the times of retrying
as 'nospc_retries'(twice).
Fix it by adding a wait queue, start queuing up space reservation tasks
when someone task has retried gc + commit for many times. Then there is
only one task making space reservation at any time, and it can always make
success under the premise of correct budgeting.
Link: https://bugzilla.kernel.org/show_bug.cgi?id=218164
Fixes: 1e51764a3c2a ("UBIFS: add new flash file system")
Signed-off-by: Zhihao Cheng <chengzhihao1@huawei.com>
Signed-off-by: Zhang Yi <yi.zhang@huawei.com>
Signed-off-by: Richard Weinberger <richard@nod.at>
2024-01-22 06:31:03 +00:00
|
|
|
|
nospc_retries++;
|
|
|
|
|
dbg_jnl("no space, retry");
|
|
|
|
|
err = -EAGAIN;
|
2008-07-14 16:08:37 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (err != -EAGAIN)
|
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* -EAGAIN means that the journal is full or too large, or the above
|
|
|
|
|
* code wants to do one commit. Do this and re-try.
|
|
|
|
|
*/
|
|
|
|
|
if (cmt_retries > 128) {
|
|
|
|
|
/*
|
ubifs: Queue up space reservation tasks if retrying many times
Recently we catched ENOSPC returned by make_reservation() while doing
fsstress on UBIFS, we got following information when it occurred (See
details in Link):
UBIFS error (ubi0:0 pid 3640152): make_reservation [ubifs]: cannot
reserve 112 bytes in jhead 2, error -28
CPU: 2 PID: 3640152 Comm: kworker/u16:2 Tainted: G B W
Hardware name: Hisilicon PhosphorHi1230 EMU (DT)
Workqueue: writeback wb_workfn (flush-ubifs_0_0)
Call trace:
dump_stack+0x114/0x198
make_reservation+0x564/0x610 [ubifs]
ubifs_jnl_write_data+0x328/0x48c [ubifs]
do_writepage+0x2a8/0x3e4 [ubifs]
ubifs_writepage+0x16c/0x374 [ubifs]
generic_writepages+0xb4/0x114
do_writepages+0xcc/0x11c
writeback_sb_inodes+0x2d0/0x564
wb_writeback+0x20c/0x2b4
wb_workfn+0x404/0x510
process_one_work+0x304/0x4ac
worker_thread+0x31c/0x4e4
kthread+0x23c/0x290
Budgeting info: data budget sum 17576, total budget sum 17768
budg_data_growth 4144, budg_dd_growth 13432, budg_idx_growth 192
min_idx_lebs 13, old_idx_sz 988640, uncommitted_idx 0
page_budget 4144, inode_budget 160, dent_budget 312
nospace 0, nospace_rp 0
dark_wm 8192, dead_wm 4096, max_idx_node_sz 192
freeable_cnt 0, calc_idx_sz 988640, idx_gc_cnt 0
dirty_pg_cnt 4, dirty_zn_cnt 0, clean_zn_cnt 4811
gc_lnum 21, ihead_lnum 14
jhead 0 (GC) LEB 16
jhead 1 (base) LEB 34
jhead 2 (data) LEB 23
bud LEB 16
bud LEB 23
bud LEB 34
old bud LEB 33
old bud LEB 31
old bud LEB 15
commit state 4
Budgeting predictions:
available: 33832, outstanding 17576, free 15356
(pid 3640152) start dumping LEB properties
(pid 3640152) Lprops statistics: empty_lebs 3, idx_lebs 11
taken_empty_lebs 1, total_free 1253376, total_dirty 2445736
total_used 3438712, total_dark 65536, total_dead 17248
LEB 15 free 0 dirty 248000 used 5952 (taken)
LEB 16 free 110592 dirty 896 used 142464 (taken, jhead 0 (GC))
LEB 21 free 253952 dirty 0 used 0 (taken, GC LEB)
LEB 23 free 0 dirty 248104 used 5848 (taken, jhead 2 (data))
LEB 29 free 253952 dirty 0 used 0 (empty)
LEB 33 free 0 dirty 253952 used 0 (taken)
LEB 34 free 217088 dirty 36544 used 320 (taken, jhead 1 (base))
LEB 37 free 253952 dirty 0 used 0 (empty)
OTHERS: index lebs, zero-available non-index lebs
According to the budget algorithm, there are 5 LEBs reserved for budget:
three journal heads(16,23,34), 1 GC LEB(21) and 1 deletion LEB(can be
used in make_reservation()). There are 2 empty LEBs used for index nodes,
which is calculated as min_idx_lebs - idx_lebs = 2. In theory, LEB 15
and 33 should be reclaimed as free state after committing, but it is now
in taken state. After looking the realization of reserve_space(), there's
a possible situation:
LEB 15: free 2000 dirty 248000 used 3952 (jhead 2)
LEB 23: free 2000 dirty 248104 used 3848 (bud, taken)
LEB 33: free 2000 dirty 251952 used 0 (bud, taken)
wb_workfn wb_workfn_2
do_writepage // write 3000 bytes
ubifs_jnl_write_data
make_reservation
reserve_space
ubifs_garbage_collect
ubifs_find_dirty_leb // ret ENOSPC, dirty LEBs are taken
nospc_retries++ // 1
ubifs_run_commit
do_commit
LEB 15: free 2000 dirty 248000 used 3952 (jhead 2)
LEB 23: free 2000 dirty 248104 used 3848 (dirty)
LEB 33: free 2000 dirty 251952 used 0 (dirty)
do_writepage // write 2000 bytes for 3 times
ubifs_jnl_write_data
// grabs 15\23\33
LEB 15: free 0 dirty 248000 used 5952 (bud, taken)
LEB 23: free 0 dirty 248104 used 5848 (jhead 2)
LEB 33: free 0 dirty 253952 used 0 (bud, taken)
reserve_space
ubifs_garbage_collect
ubifs_find_dirty_leb // ret ENOSPC, dirty LEBs are taken
if (nospc_retries++ < 2) // false
ubifs_ro_mode !
Fetch a reproducer in Link.
The dirty LEBs could be grabbed by other threads, which fails finding dirty
LEBs of GC in current thread, so make_reservation() could try many times to
invoke GC&&committing, but current realization limits the times of retrying
as 'nospc_retries'(twice).
Fix it by adding a wait queue, start queuing up space reservation tasks
when someone task has retried gc + commit for many times. Then there is
only one task making space reservation at any time, and it can always make
success under the premise of correct budgeting.
Link: https://bugzilla.kernel.org/show_bug.cgi?id=218164
Fixes: 1e51764a3c2a ("UBIFS: add new flash file system")
Signed-off-by: Zhihao Cheng <chengzhihao1@huawei.com>
Signed-off-by: Zhang Yi <yi.zhang@huawei.com>
Signed-off-by: Richard Weinberger <richard@nod.at>
2024-01-22 06:31:03 +00:00
|
|
|
|
* This should not happen unless:
|
|
|
|
|
* 1. The journal size limitations are too tough.
|
|
|
|
|
* 2. The budgeting is incorrect. We always have to be able to
|
|
|
|
|
* write to the media, because all operations are budgeted.
|
|
|
|
|
* Deletions are not budgeted, though, but we reserve an
|
|
|
|
|
* extra LEB for them.
|
2008-07-14 16:08:37 +00:00
|
|
|
|
*/
|
ubifs: Queue up space reservation tasks if retrying many times
Recently we catched ENOSPC returned by make_reservation() while doing
fsstress on UBIFS, we got following information when it occurred (See
details in Link):
UBIFS error (ubi0:0 pid 3640152): make_reservation [ubifs]: cannot
reserve 112 bytes in jhead 2, error -28
CPU: 2 PID: 3640152 Comm: kworker/u16:2 Tainted: G B W
Hardware name: Hisilicon PhosphorHi1230 EMU (DT)
Workqueue: writeback wb_workfn (flush-ubifs_0_0)
Call trace:
dump_stack+0x114/0x198
make_reservation+0x564/0x610 [ubifs]
ubifs_jnl_write_data+0x328/0x48c [ubifs]
do_writepage+0x2a8/0x3e4 [ubifs]
ubifs_writepage+0x16c/0x374 [ubifs]
generic_writepages+0xb4/0x114
do_writepages+0xcc/0x11c
writeback_sb_inodes+0x2d0/0x564
wb_writeback+0x20c/0x2b4
wb_workfn+0x404/0x510
process_one_work+0x304/0x4ac
worker_thread+0x31c/0x4e4
kthread+0x23c/0x290
Budgeting info: data budget sum 17576, total budget sum 17768
budg_data_growth 4144, budg_dd_growth 13432, budg_idx_growth 192
min_idx_lebs 13, old_idx_sz 988640, uncommitted_idx 0
page_budget 4144, inode_budget 160, dent_budget 312
nospace 0, nospace_rp 0
dark_wm 8192, dead_wm 4096, max_idx_node_sz 192
freeable_cnt 0, calc_idx_sz 988640, idx_gc_cnt 0
dirty_pg_cnt 4, dirty_zn_cnt 0, clean_zn_cnt 4811
gc_lnum 21, ihead_lnum 14
jhead 0 (GC) LEB 16
jhead 1 (base) LEB 34
jhead 2 (data) LEB 23
bud LEB 16
bud LEB 23
bud LEB 34
old bud LEB 33
old bud LEB 31
old bud LEB 15
commit state 4
Budgeting predictions:
available: 33832, outstanding 17576, free 15356
(pid 3640152) start dumping LEB properties
(pid 3640152) Lprops statistics: empty_lebs 3, idx_lebs 11
taken_empty_lebs 1, total_free 1253376, total_dirty 2445736
total_used 3438712, total_dark 65536, total_dead 17248
LEB 15 free 0 dirty 248000 used 5952 (taken)
LEB 16 free 110592 dirty 896 used 142464 (taken, jhead 0 (GC))
LEB 21 free 253952 dirty 0 used 0 (taken, GC LEB)
LEB 23 free 0 dirty 248104 used 5848 (taken, jhead 2 (data))
LEB 29 free 253952 dirty 0 used 0 (empty)
LEB 33 free 0 dirty 253952 used 0 (taken)
LEB 34 free 217088 dirty 36544 used 320 (taken, jhead 1 (base))
LEB 37 free 253952 dirty 0 used 0 (empty)
OTHERS: index lebs, zero-available non-index lebs
According to the budget algorithm, there are 5 LEBs reserved for budget:
three journal heads(16,23,34), 1 GC LEB(21) and 1 deletion LEB(can be
used in make_reservation()). There are 2 empty LEBs used for index nodes,
which is calculated as min_idx_lebs - idx_lebs = 2. In theory, LEB 15
and 33 should be reclaimed as free state after committing, but it is now
in taken state. After looking the realization of reserve_space(), there's
a possible situation:
LEB 15: free 2000 dirty 248000 used 3952 (jhead 2)
LEB 23: free 2000 dirty 248104 used 3848 (bud, taken)
LEB 33: free 2000 dirty 251952 used 0 (bud, taken)
wb_workfn wb_workfn_2
do_writepage // write 3000 bytes
ubifs_jnl_write_data
make_reservation
reserve_space
ubifs_garbage_collect
ubifs_find_dirty_leb // ret ENOSPC, dirty LEBs are taken
nospc_retries++ // 1
ubifs_run_commit
do_commit
LEB 15: free 2000 dirty 248000 used 3952 (jhead 2)
LEB 23: free 2000 dirty 248104 used 3848 (dirty)
LEB 33: free 2000 dirty 251952 used 0 (dirty)
do_writepage // write 2000 bytes for 3 times
ubifs_jnl_write_data
// grabs 15\23\33
LEB 15: free 0 dirty 248000 used 5952 (bud, taken)
LEB 23: free 0 dirty 248104 used 5848 (jhead 2)
LEB 33: free 0 dirty 253952 used 0 (bud, taken)
reserve_space
ubifs_garbage_collect
ubifs_find_dirty_leb // ret ENOSPC, dirty LEBs are taken
if (nospc_retries++ < 2) // false
ubifs_ro_mode !
Fetch a reproducer in Link.
The dirty LEBs could be grabbed by other threads, which fails finding dirty
LEBs of GC in current thread, so make_reservation() could try many times to
invoke GC&&committing, but current realization limits the times of retrying
as 'nospc_retries'(twice).
Fix it by adding a wait queue, start queuing up space reservation tasks
when someone task has retried gc + commit for many times. Then there is
only one task making space reservation at any time, and it can always make
success under the premise of correct budgeting.
Link: https://bugzilla.kernel.org/show_bug.cgi?id=218164
Fixes: 1e51764a3c2a ("UBIFS: add new flash file system")
Signed-off-by: Zhihao Cheng <chengzhihao1@huawei.com>
Signed-off-by: Zhang Yi <yi.zhang@huawei.com>
Signed-off-by: Richard Weinberger <richard@nod.at>
2024-01-22 06:31:03 +00:00
|
|
|
|
ubifs_err(c, "stuck in space allocation, nospc_retries %d",
|
|
|
|
|
nospc_retries);
|
2008-07-14 16:08:37 +00:00
|
|
|
|
err = -ENOSPC;
|
|
|
|
|
goto out;
|
ubifs: Queue up space reservation tasks if retrying many times
Recently we catched ENOSPC returned by make_reservation() while doing
fsstress on UBIFS, we got following information when it occurred (See
details in Link):
UBIFS error (ubi0:0 pid 3640152): make_reservation [ubifs]: cannot
reserve 112 bytes in jhead 2, error -28
CPU: 2 PID: 3640152 Comm: kworker/u16:2 Tainted: G B W
Hardware name: Hisilicon PhosphorHi1230 EMU (DT)
Workqueue: writeback wb_workfn (flush-ubifs_0_0)
Call trace:
dump_stack+0x114/0x198
make_reservation+0x564/0x610 [ubifs]
ubifs_jnl_write_data+0x328/0x48c [ubifs]
do_writepage+0x2a8/0x3e4 [ubifs]
ubifs_writepage+0x16c/0x374 [ubifs]
generic_writepages+0xb4/0x114
do_writepages+0xcc/0x11c
writeback_sb_inodes+0x2d0/0x564
wb_writeback+0x20c/0x2b4
wb_workfn+0x404/0x510
process_one_work+0x304/0x4ac
worker_thread+0x31c/0x4e4
kthread+0x23c/0x290
Budgeting info: data budget sum 17576, total budget sum 17768
budg_data_growth 4144, budg_dd_growth 13432, budg_idx_growth 192
min_idx_lebs 13, old_idx_sz 988640, uncommitted_idx 0
page_budget 4144, inode_budget 160, dent_budget 312
nospace 0, nospace_rp 0
dark_wm 8192, dead_wm 4096, max_idx_node_sz 192
freeable_cnt 0, calc_idx_sz 988640, idx_gc_cnt 0
dirty_pg_cnt 4, dirty_zn_cnt 0, clean_zn_cnt 4811
gc_lnum 21, ihead_lnum 14
jhead 0 (GC) LEB 16
jhead 1 (base) LEB 34
jhead 2 (data) LEB 23
bud LEB 16
bud LEB 23
bud LEB 34
old bud LEB 33
old bud LEB 31
old bud LEB 15
commit state 4
Budgeting predictions:
available: 33832, outstanding 17576, free 15356
(pid 3640152) start dumping LEB properties
(pid 3640152) Lprops statistics: empty_lebs 3, idx_lebs 11
taken_empty_lebs 1, total_free 1253376, total_dirty 2445736
total_used 3438712, total_dark 65536, total_dead 17248
LEB 15 free 0 dirty 248000 used 5952 (taken)
LEB 16 free 110592 dirty 896 used 142464 (taken, jhead 0 (GC))
LEB 21 free 253952 dirty 0 used 0 (taken, GC LEB)
LEB 23 free 0 dirty 248104 used 5848 (taken, jhead 2 (data))
LEB 29 free 253952 dirty 0 used 0 (empty)
LEB 33 free 0 dirty 253952 used 0 (taken)
LEB 34 free 217088 dirty 36544 used 320 (taken, jhead 1 (base))
LEB 37 free 253952 dirty 0 used 0 (empty)
OTHERS: index lebs, zero-available non-index lebs
According to the budget algorithm, there are 5 LEBs reserved for budget:
three journal heads(16,23,34), 1 GC LEB(21) and 1 deletion LEB(can be
used in make_reservation()). There are 2 empty LEBs used for index nodes,
which is calculated as min_idx_lebs - idx_lebs = 2. In theory, LEB 15
and 33 should be reclaimed as free state after committing, but it is now
in taken state. After looking the realization of reserve_space(), there's
a possible situation:
LEB 15: free 2000 dirty 248000 used 3952 (jhead 2)
LEB 23: free 2000 dirty 248104 used 3848 (bud, taken)
LEB 33: free 2000 dirty 251952 used 0 (bud, taken)
wb_workfn wb_workfn_2
do_writepage // write 3000 bytes
ubifs_jnl_write_data
make_reservation
reserve_space
ubifs_garbage_collect
ubifs_find_dirty_leb // ret ENOSPC, dirty LEBs are taken
nospc_retries++ // 1
ubifs_run_commit
do_commit
LEB 15: free 2000 dirty 248000 used 3952 (jhead 2)
LEB 23: free 2000 dirty 248104 used 3848 (dirty)
LEB 33: free 2000 dirty 251952 used 0 (dirty)
do_writepage // write 2000 bytes for 3 times
ubifs_jnl_write_data
// grabs 15\23\33
LEB 15: free 0 dirty 248000 used 5952 (bud, taken)
LEB 23: free 0 dirty 248104 used 5848 (jhead 2)
LEB 33: free 0 dirty 253952 used 0 (bud, taken)
reserve_space
ubifs_garbage_collect
ubifs_find_dirty_leb // ret ENOSPC, dirty LEBs are taken
if (nospc_retries++ < 2) // false
ubifs_ro_mode !
Fetch a reproducer in Link.
The dirty LEBs could be grabbed by other threads, which fails finding dirty
LEBs of GC in current thread, so make_reservation() could try many times to
invoke GC&&committing, but current realization limits the times of retrying
as 'nospc_retries'(twice).
Fix it by adding a wait queue, start queuing up space reservation tasks
when someone task has retried gc + commit for many times. Then there is
only one task making space reservation at any time, and it can always make
success under the premise of correct budgeting.
Link: https://bugzilla.kernel.org/show_bug.cgi?id=218164
Fixes: 1e51764a3c2a ("UBIFS: add new flash file system")
Signed-off-by: Zhihao Cheng <chengzhihao1@huawei.com>
Signed-off-by: Zhang Yi <yi.zhang@huawei.com>
Signed-off-by: Richard Weinberger <richard@nod.at>
2024-01-22 06:31:03 +00:00
|
|
|
|
} else if (cmt_retries > 32) {
|
|
|
|
|
/*
|
|
|
|
|
* It's almost impossible to happen, unless there are many tasks
|
|
|
|
|
* making reservation concurrently and someone task has retried
|
|
|
|
|
* gc + commit for many times, generated available space during
|
|
|
|
|
* this period are grabbed by other tasks.
|
|
|
|
|
* But if it happens, start queuing up all tasks that will make
|
|
|
|
|
* space reservation, then there is only one task making space
|
|
|
|
|
* reservation at any time, and it can always make success under
|
|
|
|
|
* the premise of correct budgeting.
|
|
|
|
|
*/
|
|
|
|
|
ubifs_warn(c, "too many space allocation cmt_retries (%d) "
|
|
|
|
|
"nospc_retries (%d), start queuing tasks",
|
|
|
|
|
cmt_retries, nospc_retries);
|
|
|
|
|
|
|
|
|
|
if (!blocked) {
|
|
|
|
|
blocked = true;
|
|
|
|
|
add_or_start_queue(c);
|
|
|
|
|
}
|
|
|
|
|
}
|
2008-07-14 16:08:37 +00:00
|
|
|
|
|
|
|
|
|
dbg_jnl("-EAGAIN, commit and retry (retried %d times)",
|
|
|
|
|
cmt_retries);
|
|
|
|
|
cmt_retries += 1;
|
|
|
|
|
|
|
|
|
|
err = ubifs_run_commit(c);
|
|
|
|
|
if (err)
|
ubifs: Queue up space reservation tasks if retrying many times
Recently we catched ENOSPC returned by make_reservation() while doing
fsstress on UBIFS, we got following information when it occurred (See
details in Link):
UBIFS error (ubi0:0 pid 3640152): make_reservation [ubifs]: cannot
reserve 112 bytes in jhead 2, error -28
CPU: 2 PID: 3640152 Comm: kworker/u16:2 Tainted: G B W
Hardware name: Hisilicon PhosphorHi1230 EMU (DT)
Workqueue: writeback wb_workfn (flush-ubifs_0_0)
Call trace:
dump_stack+0x114/0x198
make_reservation+0x564/0x610 [ubifs]
ubifs_jnl_write_data+0x328/0x48c [ubifs]
do_writepage+0x2a8/0x3e4 [ubifs]
ubifs_writepage+0x16c/0x374 [ubifs]
generic_writepages+0xb4/0x114
do_writepages+0xcc/0x11c
writeback_sb_inodes+0x2d0/0x564
wb_writeback+0x20c/0x2b4
wb_workfn+0x404/0x510
process_one_work+0x304/0x4ac
worker_thread+0x31c/0x4e4
kthread+0x23c/0x290
Budgeting info: data budget sum 17576, total budget sum 17768
budg_data_growth 4144, budg_dd_growth 13432, budg_idx_growth 192
min_idx_lebs 13, old_idx_sz 988640, uncommitted_idx 0
page_budget 4144, inode_budget 160, dent_budget 312
nospace 0, nospace_rp 0
dark_wm 8192, dead_wm 4096, max_idx_node_sz 192
freeable_cnt 0, calc_idx_sz 988640, idx_gc_cnt 0
dirty_pg_cnt 4, dirty_zn_cnt 0, clean_zn_cnt 4811
gc_lnum 21, ihead_lnum 14
jhead 0 (GC) LEB 16
jhead 1 (base) LEB 34
jhead 2 (data) LEB 23
bud LEB 16
bud LEB 23
bud LEB 34
old bud LEB 33
old bud LEB 31
old bud LEB 15
commit state 4
Budgeting predictions:
available: 33832, outstanding 17576, free 15356
(pid 3640152) start dumping LEB properties
(pid 3640152) Lprops statistics: empty_lebs 3, idx_lebs 11
taken_empty_lebs 1, total_free 1253376, total_dirty 2445736
total_used 3438712, total_dark 65536, total_dead 17248
LEB 15 free 0 dirty 248000 used 5952 (taken)
LEB 16 free 110592 dirty 896 used 142464 (taken, jhead 0 (GC))
LEB 21 free 253952 dirty 0 used 0 (taken, GC LEB)
LEB 23 free 0 dirty 248104 used 5848 (taken, jhead 2 (data))
LEB 29 free 253952 dirty 0 used 0 (empty)
LEB 33 free 0 dirty 253952 used 0 (taken)
LEB 34 free 217088 dirty 36544 used 320 (taken, jhead 1 (base))
LEB 37 free 253952 dirty 0 used 0 (empty)
OTHERS: index lebs, zero-available non-index lebs
According to the budget algorithm, there are 5 LEBs reserved for budget:
three journal heads(16,23,34), 1 GC LEB(21) and 1 deletion LEB(can be
used in make_reservation()). There are 2 empty LEBs used for index nodes,
which is calculated as min_idx_lebs - idx_lebs = 2. In theory, LEB 15
and 33 should be reclaimed as free state after committing, but it is now
in taken state. After looking the realization of reserve_space(), there's
a possible situation:
LEB 15: free 2000 dirty 248000 used 3952 (jhead 2)
LEB 23: free 2000 dirty 248104 used 3848 (bud, taken)
LEB 33: free 2000 dirty 251952 used 0 (bud, taken)
wb_workfn wb_workfn_2
do_writepage // write 3000 bytes
ubifs_jnl_write_data
make_reservation
reserve_space
ubifs_garbage_collect
ubifs_find_dirty_leb // ret ENOSPC, dirty LEBs are taken
nospc_retries++ // 1
ubifs_run_commit
do_commit
LEB 15: free 2000 dirty 248000 used 3952 (jhead 2)
LEB 23: free 2000 dirty 248104 used 3848 (dirty)
LEB 33: free 2000 dirty 251952 used 0 (dirty)
do_writepage // write 2000 bytes for 3 times
ubifs_jnl_write_data
// grabs 15\23\33
LEB 15: free 0 dirty 248000 used 5952 (bud, taken)
LEB 23: free 0 dirty 248104 used 5848 (jhead 2)
LEB 33: free 0 dirty 253952 used 0 (bud, taken)
reserve_space
ubifs_garbage_collect
ubifs_find_dirty_leb // ret ENOSPC, dirty LEBs are taken
if (nospc_retries++ < 2) // false
ubifs_ro_mode !
Fetch a reproducer in Link.
The dirty LEBs could be grabbed by other threads, which fails finding dirty
LEBs of GC in current thread, so make_reservation() could try many times to
invoke GC&&committing, but current realization limits the times of retrying
as 'nospc_retries'(twice).
Fix it by adding a wait queue, start queuing up space reservation tasks
when someone task has retried gc + commit for many times. Then there is
only one task making space reservation at any time, and it can always make
success under the premise of correct budgeting.
Link: https://bugzilla.kernel.org/show_bug.cgi?id=218164
Fixes: 1e51764a3c2a ("UBIFS: add new flash file system")
Signed-off-by: Zhihao Cheng <chengzhihao1@huawei.com>
Signed-off-by: Zhang Yi <yi.zhang@huawei.com>
Signed-off-by: Richard Weinberger <richard@nod.at>
2024-01-22 06:31:03 +00:00
|
|
|
|
goto out_wake_up;
|
2008-07-14 16:08:37 +00:00
|
|
|
|
goto again;
|
|
|
|
|
|
|
|
|
|
out:
|
UBIFS: extend debug/message capabilities
In the case where we have more than one volumes on different UBI
devices, it may be not that easy to tell which volume prints the
messages. Add ubi number and volume id in ubifs_msg/warn/error
to help debug. These two values are passed by struct ubifs_info.
For those where ubifs_info is not initialized yet, ubifs_* is
replaced by pr_*. For those where ubifs_info is not avaliable,
ubifs_info is passed to the calling function as a const parameter.
The output looks like,
[ 95.444879] UBIFS (ubi0:1): background thread "ubifs_bgt0_1" started, PID 696
[ 95.484688] UBIFS (ubi0:1): UBIFS: mounted UBI device 0, volume 1, name "test1"
[ 95.484694] UBIFS (ubi0:1): LEB size: 126976 bytes (124 KiB), min./max. I/O unit sizes: 2048 bytes/2048 bytes
[ 95.484699] UBIFS (ubi0:1): FS size: 30220288 bytes (28 MiB, 238 LEBs), journal size 1523712 bytes (1 MiB, 12 LEBs)
[ 95.484703] UBIFS (ubi0:1): reserved for root: 1427378 bytes (1393 KiB)
[ 95.484709] UBIFS (ubi0:1): media format: w4/r0 (latest is w4/r0), UUID 40DFFC0E-70BE-4193-8905-F7D6DFE60B17, small LPT model
[ 95.489875] UBIFS (ubi1:0): background thread "ubifs_bgt1_0" started, PID 699
[ 95.529713] UBIFS (ubi1:0): UBIFS: mounted UBI device 1, volume 0, name "test2"
[ 95.529718] UBIFS (ubi1:0): LEB size: 126976 bytes (124 KiB), min./max. I/O unit sizes: 2048 bytes/2048 bytes
[ 95.529724] UBIFS (ubi1:0): FS size: 19808256 bytes (18 MiB, 156 LEBs), journal size 1015809 bytes (0 MiB, 8 LEBs)
[ 95.529727] UBIFS (ubi1:0): reserved for root: 935592 bytes (913 KiB)
[ 95.529733] UBIFS (ubi1:0): media format: w4/r0 (latest is w4/r0), UUID EEB7779D-F419-4CA9-811B-831CAC7233D4, small LPT model
[ 954.264767] UBIFS error (ubi1:0 pid 756): ubifs_read_node: bad node type (255 but expected 6)
[ 954.367030] UBIFS error (ubi1:0 pid 756): ubifs_read_node: bad node at LEB 0:0, LEB mapping status 1
Signed-off-by: Sheng Yong <shengyong1@huawei.com>
Signed-off-by: Artem Bityutskiy <artem.bityutskiy@linux.intel.com>
2015-03-20 10:39:42 +00:00
|
|
|
|
ubifs_err(c, "cannot reserve %d bytes in jhead %d, error %d",
|
2008-07-14 16:08:37 +00:00
|
|
|
|
len, jhead, err);
|
|
|
|
|
if (err == -ENOSPC) {
|
|
|
|
|
/* This are some budgeting problems, print useful information */
|
|
|
|
|
down_write(&c->commit_sem);
|
2012-05-16 16:04:54 +00:00
|
|
|
|
dump_stack();
|
2012-05-16 16:15:56 +00:00
|
|
|
|
ubifs_dump_budg(c, &c->bi);
|
|
|
|
|
ubifs_dump_lprops(c);
|
2008-07-14 16:08:37 +00:00
|
|
|
|
cmt_retries = dbg_check_lprops(c);
|
|
|
|
|
up_write(&c->commit_sem);
|
|
|
|
|
}
|
ubifs: Queue up space reservation tasks if retrying many times
Recently we catched ENOSPC returned by make_reservation() while doing
fsstress on UBIFS, we got following information when it occurred (See
details in Link):
UBIFS error (ubi0:0 pid 3640152): make_reservation [ubifs]: cannot
reserve 112 bytes in jhead 2, error -28
CPU: 2 PID: 3640152 Comm: kworker/u16:2 Tainted: G B W
Hardware name: Hisilicon PhosphorHi1230 EMU (DT)
Workqueue: writeback wb_workfn (flush-ubifs_0_0)
Call trace:
dump_stack+0x114/0x198
make_reservation+0x564/0x610 [ubifs]
ubifs_jnl_write_data+0x328/0x48c [ubifs]
do_writepage+0x2a8/0x3e4 [ubifs]
ubifs_writepage+0x16c/0x374 [ubifs]
generic_writepages+0xb4/0x114
do_writepages+0xcc/0x11c
writeback_sb_inodes+0x2d0/0x564
wb_writeback+0x20c/0x2b4
wb_workfn+0x404/0x510
process_one_work+0x304/0x4ac
worker_thread+0x31c/0x4e4
kthread+0x23c/0x290
Budgeting info: data budget sum 17576, total budget sum 17768
budg_data_growth 4144, budg_dd_growth 13432, budg_idx_growth 192
min_idx_lebs 13, old_idx_sz 988640, uncommitted_idx 0
page_budget 4144, inode_budget 160, dent_budget 312
nospace 0, nospace_rp 0
dark_wm 8192, dead_wm 4096, max_idx_node_sz 192
freeable_cnt 0, calc_idx_sz 988640, idx_gc_cnt 0
dirty_pg_cnt 4, dirty_zn_cnt 0, clean_zn_cnt 4811
gc_lnum 21, ihead_lnum 14
jhead 0 (GC) LEB 16
jhead 1 (base) LEB 34
jhead 2 (data) LEB 23
bud LEB 16
bud LEB 23
bud LEB 34
old bud LEB 33
old bud LEB 31
old bud LEB 15
commit state 4
Budgeting predictions:
available: 33832, outstanding 17576, free 15356
(pid 3640152) start dumping LEB properties
(pid 3640152) Lprops statistics: empty_lebs 3, idx_lebs 11
taken_empty_lebs 1, total_free 1253376, total_dirty 2445736
total_used 3438712, total_dark 65536, total_dead 17248
LEB 15 free 0 dirty 248000 used 5952 (taken)
LEB 16 free 110592 dirty 896 used 142464 (taken, jhead 0 (GC))
LEB 21 free 253952 dirty 0 used 0 (taken, GC LEB)
LEB 23 free 0 dirty 248104 used 5848 (taken, jhead 2 (data))
LEB 29 free 253952 dirty 0 used 0 (empty)
LEB 33 free 0 dirty 253952 used 0 (taken)
LEB 34 free 217088 dirty 36544 used 320 (taken, jhead 1 (base))
LEB 37 free 253952 dirty 0 used 0 (empty)
OTHERS: index lebs, zero-available non-index lebs
According to the budget algorithm, there are 5 LEBs reserved for budget:
three journal heads(16,23,34), 1 GC LEB(21) and 1 deletion LEB(can be
used in make_reservation()). There are 2 empty LEBs used for index nodes,
which is calculated as min_idx_lebs - idx_lebs = 2. In theory, LEB 15
and 33 should be reclaimed as free state after committing, but it is now
in taken state. After looking the realization of reserve_space(), there's
a possible situation:
LEB 15: free 2000 dirty 248000 used 3952 (jhead 2)
LEB 23: free 2000 dirty 248104 used 3848 (bud, taken)
LEB 33: free 2000 dirty 251952 used 0 (bud, taken)
wb_workfn wb_workfn_2
do_writepage // write 3000 bytes
ubifs_jnl_write_data
make_reservation
reserve_space
ubifs_garbage_collect
ubifs_find_dirty_leb // ret ENOSPC, dirty LEBs are taken
nospc_retries++ // 1
ubifs_run_commit
do_commit
LEB 15: free 2000 dirty 248000 used 3952 (jhead 2)
LEB 23: free 2000 dirty 248104 used 3848 (dirty)
LEB 33: free 2000 dirty 251952 used 0 (dirty)
do_writepage // write 2000 bytes for 3 times
ubifs_jnl_write_data
// grabs 15\23\33
LEB 15: free 0 dirty 248000 used 5952 (bud, taken)
LEB 23: free 0 dirty 248104 used 5848 (jhead 2)
LEB 33: free 0 dirty 253952 used 0 (bud, taken)
reserve_space
ubifs_garbage_collect
ubifs_find_dirty_leb // ret ENOSPC, dirty LEBs are taken
if (nospc_retries++ < 2) // false
ubifs_ro_mode !
Fetch a reproducer in Link.
The dirty LEBs could be grabbed by other threads, which fails finding dirty
LEBs of GC in current thread, so make_reservation() could try many times to
invoke GC&&committing, but current realization limits the times of retrying
as 'nospc_retries'(twice).
Fix it by adding a wait queue, start queuing up space reservation tasks
when someone task has retried gc + commit for many times. Then there is
only one task making space reservation at any time, and it can always make
success under the premise of correct budgeting.
Link: https://bugzilla.kernel.org/show_bug.cgi?id=218164
Fixes: 1e51764a3c2a ("UBIFS: add new flash file system")
Signed-off-by: Zhihao Cheng <chengzhihao1@huawei.com>
Signed-off-by: Zhang Yi <yi.zhang@huawei.com>
Signed-off-by: Richard Weinberger <richard@nod.at>
2024-01-22 06:31:03 +00:00
|
|
|
|
out_wake_up:
|
|
|
|
|
if (blocked) {
|
|
|
|
|
/*
|
|
|
|
|
* Only tasks that have ever started queuing or ever been queued
|
|
|
|
|
* can wake up other queued tasks, which can make sure that
|
|
|
|
|
* there is only one task waked up to make space reservation.
|
|
|
|
|
* For example:
|
|
|
|
|
* task A task B task C
|
|
|
|
|
* make_reservation make_reservation
|
|
|
|
|
* reserve_space // 0
|
|
|
|
|
* wake_up_reservation
|
|
|
|
|
* atomic_cmpxchg // 0, start queuing
|
|
|
|
|
* reserve_space
|
|
|
|
|
* wait_for_reservation
|
|
|
|
|
* __queue_and_wait
|
|
|
|
|
* add_wait_queue
|
|
|
|
|
* if (blocked) // false
|
|
|
|
|
* // So that task C won't be waked up to race with task B
|
|
|
|
|
*/
|
|
|
|
|
wake_up_reservation(c);
|
|
|
|
|
}
|
2008-07-14 16:08:37 +00:00
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* release_head - release a journal head.
|
|
|
|
|
* @c: UBIFS file-system description object
|
|
|
|
|
* @jhead: journal head
|
|
|
|
|
*
|
|
|
|
|
* This function releases journal head @jhead which was locked by
|
|
|
|
|
* the 'make_reservation()' function. It has to be called after each successful
|
|
|
|
|
* 'make_reservation()' invocation.
|
|
|
|
|
*/
|
|
|
|
|
static inline void release_head(struct ubifs_info *c, int jhead)
|
|
|
|
|
{
|
|
|
|
|
mutex_unlock(&c->jheads[jhead].wbuf.io_mutex);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* finish_reservation - finish a reservation.
|
|
|
|
|
* @c: UBIFS file-system description object
|
|
|
|
|
*
|
|
|
|
|
* This function finishes journal space reservation. It must be called after
|
|
|
|
|
* 'make_reservation()'.
|
|
|
|
|
*/
|
|
|
|
|
static void finish_reservation(struct ubifs_info *c)
|
|
|
|
|
{
|
|
|
|
|
up_read(&c->commit_sem);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* get_dent_type - translate VFS inode mode to UBIFS directory entry type.
|
|
|
|
|
* @mode: inode mode
|
|
|
|
|
*/
|
|
|
|
|
static int get_dent_type(int mode)
|
|
|
|
|
{
|
|
|
|
|
switch (mode & S_IFMT) {
|
|
|
|
|
case S_IFREG:
|
|
|
|
|
return UBIFS_ITYPE_REG;
|
|
|
|
|
case S_IFDIR:
|
|
|
|
|
return UBIFS_ITYPE_DIR;
|
|
|
|
|
case S_IFLNK:
|
|
|
|
|
return UBIFS_ITYPE_LNK;
|
|
|
|
|
case S_IFBLK:
|
|
|
|
|
return UBIFS_ITYPE_BLK;
|
|
|
|
|
case S_IFCHR:
|
|
|
|
|
return UBIFS_ITYPE_CHR;
|
|
|
|
|
case S_IFIFO:
|
|
|
|
|
return UBIFS_ITYPE_FIFO;
|
|
|
|
|
case S_IFSOCK:
|
|
|
|
|
return UBIFS_ITYPE_SOCK;
|
|
|
|
|
default:
|
|
|
|
|
BUG();
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* pack_inode - pack an inode node.
|
|
|
|
|
* @c: UBIFS file-system description object
|
|
|
|
|
* @ino: buffer in which to pack inode node
|
|
|
|
|
* @inode: inode to pack
|
|
|
|
|
* @last: indicates the last node of the group
|
|
|
|
|
*/
|
|
|
|
|
static void pack_inode(struct ubifs_info *c, struct ubifs_ino_node *ino,
|
2008-07-22 09:19:09 +00:00
|
|
|
|
const struct inode *inode, int last)
|
2008-07-14 16:08:37 +00:00
|
|
|
|
{
|
2008-07-22 09:19:09 +00:00
|
|
|
|
int data_len = 0, last_reference = !inode->i_nlink;
|
2008-07-14 16:08:37 +00:00
|
|
|
|
struct ubifs_inode *ui = ubifs_inode(inode);
|
|
|
|
|
|
|
|
|
|
ino->ch.node_type = UBIFS_INO_NODE;
|
|
|
|
|
ino_key_init_flash(c, &ino->key, inode->i_ino);
|
|
|
|
|
ino->creat_sqnum = cpu_to_le64(ui->creat_sqnum);
|
2023-10-04 18:52:58 +00:00
|
|
|
|
ino->atime_sec = cpu_to_le64(inode_get_atime_sec(inode));
|
|
|
|
|
ino->atime_nsec = cpu_to_le32(inode_get_atime_nsec(inode));
|
|
|
|
|
ino->ctime_sec = cpu_to_le64(inode_get_ctime_sec(inode));
|
|
|
|
|
ino->ctime_nsec = cpu_to_le32(inode_get_ctime_nsec(inode));
|
|
|
|
|
ino->mtime_sec = cpu_to_le64(inode_get_mtime_sec(inode));
|
|
|
|
|
ino->mtime_nsec = cpu_to_le32(inode_get_mtime_nsec(inode));
|
2012-02-07 23:50:56 +00:00
|
|
|
|
ino->uid = cpu_to_le32(i_uid_read(inode));
|
|
|
|
|
ino->gid = cpu_to_le32(i_gid_read(inode));
|
2008-07-14 16:08:37 +00:00
|
|
|
|
ino->mode = cpu_to_le32(inode->i_mode);
|
|
|
|
|
ino->flags = cpu_to_le32(ui->flags);
|
|
|
|
|
ino->size = cpu_to_le64(ui->ui_size);
|
|
|
|
|
ino->nlink = cpu_to_le32(inode->i_nlink);
|
|
|
|
|
ino->compr_type = cpu_to_le16(ui->compr_type);
|
|
|
|
|
ino->data_len = cpu_to_le32(ui->data_len);
|
|
|
|
|
ino->xattr_cnt = cpu_to_le32(ui->xattr_cnt);
|
|
|
|
|
ino->xattr_size = cpu_to_le32(ui->xattr_size);
|
|
|
|
|
ino->xattr_names = cpu_to_le32(ui->xattr_names);
|
|
|
|
|
zero_ino_node_unused(ino);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Drop the attached data if this is a deletion inode, the data is not
|
|
|
|
|
* needed anymore.
|
|
|
|
|
*/
|
|
|
|
|
if (!last_reference) {
|
|
|
|
|
memcpy(ino->data, ui->data, ui->data_len);
|
|
|
|
|
data_len = ui->data_len;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ubifs_prep_grp_node(c, ino, UBIFS_INO_NODE_SZ + data_len, last);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* mark_inode_clean - mark UBIFS inode as clean.
|
|
|
|
|
* @c: UBIFS file-system description object
|
|
|
|
|
* @ui: UBIFS inode to mark as clean
|
|
|
|
|
*
|
|
|
|
|
* This helper function marks UBIFS inode @ui as clean by cleaning the
|
|
|
|
|
* @ui->dirty flag and releasing its budget. Note, VFS may still treat the
|
|
|
|
|
* inode as dirty and try to write it back, but 'ubifs_write_inode()' would
|
|
|
|
|
* just do nothing.
|
|
|
|
|
*/
|
|
|
|
|
static void mark_inode_clean(struct ubifs_info *c, struct ubifs_inode *ui)
|
|
|
|
|
{
|
|
|
|
|
if (ui->dirty)
|
|
|
|
|
ubifs_release_dirty_inode_budget(c, ui);
|
|
|
|
|
ui->dirty = 0;
|
|
|
|
|
}
|
|
|
|
|
|
2016-10-19 13:59:12 +00:00
|
|
|
|
static void set_dent_cookie(struct ubifs_info *c, struct ubifs_dent_node *dent)
|
|
|
|
|
{
|
|
|
|
|
if (c->double_hash)
|
2022-10-05 15:43:22 +00:00
|
|
|
|
dent->cookie = (__force __le32) get_random_u32();
|
2016-10-19 13:59:12 +00:00
|
|
|
|
else
|
|
|
|
|
dent->cookie = 0;
|
|
|
|
|
}
|
|
|
|
|
|
2008-07-14 16:08:37 +00:00
|
|
|
|
/**
|
|
|
|
|
* ubifs_jnl_update - update inode.
|
|
|
|
|
* @c: UBIFS file-system description object
|
|
|
|
|
* @dir: parent inode or host inode in case of extended attributes
|
|
|
|
|
* @nm: directory entry name
|
|
|
|
|
* @inode: inode to update
|
|
|
|
|
* @deletion: indicates a directory entry deletion i.e unlink or rmdir
|
|
|
|
|
* @xent: non-zero if the directory entry is an extended attribute entry
|
|
|
|
|
*
|
|
|
|
|
* This function updates an inode by writing a directory entry (or extended
|
|
|
|
|
* attribute entry), the inode itself, and the parent directory inode (or the
|
|
|
|
|
* host inode) to the journal.
|
|
|
|
|
*
|
|
|
|
|
* The function writes the host inode @dir last, which is important in case of
|
|
|
|
|
* extended attributes. Indeed, then we guarantee that if the host inode gets
|
|
|
|
|
* synchronized (with 'fsync()'), and the write-buffer it sits in gets flushed,
|
|
|
|
|
* the extended attribute inode gets flushed too. And this is exactly what the
|
|
|
|
|
* user expects - synchronizing the host inode synchronizes its extended
|
|
|
|
|
* attributes. Similarly, this guarantees that if @dir is synchronized, its
|
|
|
|
|
* directory entry corresponding to @nm gets synchronized too.
|
|
|
|
|
*
|
|
|
|
|
* If the inode (@inode) or the parent directory (@dir) are synchronous, this
|
|
|
|
|
* function synchronizes the write-buffer.
|
|
|
|
|
*
|
|
|
|
|
* This function marks the @dir and @inode inodes as clean and returns zero on
|
|
|
|
|
* success. In case of failure, a negative error code is returned.
|
|
|
|
|
*/
|
|
|
|
|
int ubifs_jnl_update(struct ubifs_info *c, const struct inode *dir,
|
2016-11-11 21:50:29 +00:00
|
|
|
|
const struct fscrypt_name *nm, const struct inode *inode,
|
2008-07-14 16:08:37 +00:00
|
|
|
|
int deletion, int xent)
|
|
|
|
|
{
|
2020-07-07 12:51:40 +00:00
|
|
|
|
int err, dlen, ilen, len, lnum, ino_offs, dent_offs, orphan_added = 0;
|
2008-07-14 16:08:37 +00:00
|
|
|
|
int aligned_dlen, aligned_ilen, sync = IS_DIRSYNC(dir);
|
|
|
|
|
int last_reference = !!(deletion && inode->i_nlink == 0);
|
|
|
|
|
struct ubifs_inode *ui = ubifs_inode(inode);
|
2014-09-19 09:48:46 +00:00
|
|
|
|
struct ubifs_inode *host_ui = ubifs_inode(dir);
|
2008-07-14 16:08:37 +00:00
|
|
|
|
struct ubifs_dent_node *dent;
|
|
|
|
|
struct ubifs_ino_node *ino;
|
|
|
|
|
union ubifs_key dent_key, ino_key;
|
2018-09-07 12:36:34 +00:00
|
|
|
|
u8 hash_dent[UBIFS_HASH_ARR_SZ];
|
|
|
|
|
u8 hash_ino[UBIFS_HASH_ARR_SZ];
|
|
|
|
|
u8 hash_ino_host[UBIFS_HASH_ARR_SZ];
|
2008-07-14 16:08:37 +00:00
|
|
|
|
|
2018-07-12 11:01:57 +00:00
|
|
|
|
ubifs_assert(c, mutex_is_locked(&host_ui->ui_mutex));
|
2008-07-14 16:08:37 +00:00
|
|
|
|
|
2016-11-11 21:50:29 +00:00
|
|
|
|
dlen = UBIFS_DENT_NODE_SZ + fname_len(nm) + 1;
|
2008-07-14 16:08:37 +00:00
|
|
|
|
ilen = UBIFS_INO_NODE_SZ;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* If the last reference to the inode is being deleted, then there is
|
|
|
|
|
* no need to attach and write inode data, it is being deleted anyway.
|
|
|
|
|
* And if the inode is being deleted, no need to synchronize
|
|
|
|
|
* write-buffer even if the inode is synchronous.
|
|
|
|
|
*/
|
|
|
|
|
if (!last_reference) {
|
|
|
|
|
ilen += ui->data_len;
|
|
|
|
|
sync |= IS_SYNC(inode);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
aligned_dlen = ALIGN(dlen, 8);
|
|
|
|
|
aligned_ilen = ALIGN(ilen, 8);
|
2014-10-31 18:50:28 +00:00
|
|
|
|
|
2008-07-14 16:08:37 +00:00
|
|
|
|
len = aligned_dlen + aligned_ilen + UBIFS_INO_NODE_SZ;
|
2014-10-31 18:50:28 +00:00
|
|
|
|
/* Make sure to also account for extended attributes */
|
ubifs: Add authentication nodes to journal
Nodes that are written to flash can only be authenticated through the
index after the next commit. When a journal replay is necessary the
nodes are not yet referenced by the index and thus can't be
authenticated.
This patch overcomes this situation by creating a hash over all nodes
beginning from the commit start node over the reference node(s) and
the buds themselves. From
time to time we insert authentication nodes. Authentication nodes
contain a HMAC from the current hash state, so that they can be
used to authenticate a journal replay up to the point where the
authentication node is. The hash is continued afterwards
so that theoretically we would only have to check the HMAC of
the last authentication node we find.
Overall we get this picture:
,,,,,,,,
,......,...........................................
,. CS , hash1.----. hash2.----.
,. | , . |hmac . |hmac
,. v , . v . v
,.REF#0,-> bud -> bud -> bud.-> auth -> bud -> bud.-> auth ...
,..|...,...........................................
, | ,
, | ,,,,,,,,,,,,,,,
. | hash3,----.
, | , |hmac
, v , v
, REF#1 -> bud -> bud,-> auth ...
,,,|,,,,,,,,,,,,,,,,,,
v
REF#2 -> ...
|
V
...
Note how hash3 covers CS, REF#0 and REF#1 so that it is not possible to
exchange or skip any reference nodes. Unlike the picture suggests the
auth nodes themselves are not hashed.
With this it is possible for an offline attacker to cut each journal
head or to drop the last reference node(s), but not to skip any journal
heads or to reorder any operations.
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Signed-off-by: Richard Weinberger <richard@nod.at>
2018-09-07 12:36:36 +00:00
|
|
|
|
if (ubifs_authenticated(c))
|
|
|
|
|
len += ALIGN(host_ui->data_len, 8) + ubifs_auth_node_sz(c);
|
|
|
|
|
else
|
|
|
|
|
len += host_ui->data_len;
|
2014-10-31 18:50:28 +00:00
|
|
|
|
|
2017-06-16 14:21:44 +00:00
|
|
|
|
dent = kzalloc(len, GFP_NOFS);
|
2008-07-14 16:08:37 +00:00
|
|
|
|
if (!dent)
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
|
|
/* Make reservation before allocating sequence numbers */
|
|
|
|
|
err = make_reservation(c, BASEHD, len);
|
|
|
|
|
if (err)
|
|
|
|
|
goto out_free;
|
|
|
|
|
|
|
|
|
|
if (!xent) {
|
|
|
|
|
dent->ch.node_type = UBIFS_DENT_NODE;
|
2020-01-20 22:32:00 +00:00
|
|
|
|
if (fname_name(nm) == NULL)
|
2017-05-17 08:36:46 +00:00
|
|
|
|
dent_key_init_hash(c, &dent_key, dir->i_ino, nm->hash);
|
|
|
|
|
else
|
|
|
|
|
dent_key_init(c, &dent_key, dir->i_ino, nm);
|
2008-07-14 16:08:37 +00:00
|
|
|
|
} else {
|
|
|
|
|
dent->ch.node_type = UBIFS_XENT_NODE;
|
|
|
|
|
xent_key_init(c, &dent_key, dir->i_ino, nm);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
key_write(c, &dent_key, dent->key);
|
|
|
|
|
dent->inum = deletion ? 0 : cpu_to_le64(inode->i_ino);
|
|
|
|
|
dent->type = get_dent_type(inode->i_mode);
|
2016-11-11 21:50:29 +00:00
|
|
|
|
dent->nlen = cpu_to_le16(fname_len(nm));
|
|
|
|
|
memcpy(dent->name, fname_name(nm), fname_len(nm));
|
|
|
|
|
dent->name[fname_len(nm)] = '\0';
|
2016-10-19 13:59:12 +00:00
|
|
|
|
set_dent_cookie(c, dent);
|
2016-11-11 21:50:29 +00:00
|
|
|
|
|
2008-07-14 16:08:37 +00:00
|
|
|
|
zero_dent_node_unused(dent);
|
|
|
|
|
ubifs_prep_grp_node(c, dent, dlen, 0);
|
2018-09-07 12:36:34 +00:00
|
|
|
|
err = ubifs_node_calc_hash(c, dent, hash_dent);
|
|
|
|
|
if (err)
|
|
|
|
|
goto out_release;
|
2008-07-14 16:08:37 +00:00
|
|
|
|
|
|
|
|
|
ino = (void *)dent + aligned_dlen;
|
2008-07-22 09:19:09 +00:00
|
|
|
|
pack_inode(c, ino, inode, 0);
|
2018-09-07 12:36:34 +00:00
|
|
|
|
err = ubifs_node_calc_hash(c, ino, hash_ino);
|
|
|
|
|
if (err)
|
|
|
|
|
goto out_release;
|
|
|
|
|
|
2008-07-14 16:08:37 +00:00
|
|
|
|
ino = (void *)ino + aligned_ilen;
|
2008-07-22 09:19:09 +00:00
|
|
|
|
pack_inode(c, ino, dir, 1);
|
2018-09-07 12:36:34 +00:00
|
|
|
|
err = ubifs_node_calc_hash(c, ino, hash_ino_host);
|
|
|
|
|
if (err)
|
|
|
|
|
goto out_release;
|
2008-07-14 16:08:37 +00:00
|
|
|
|
|
|
|
|
|
if (last_reference) {
|
|
|
|
|
err = ubifs_add_orphan(c, inode->i_ino);
|
|
|
|
|
if (err) {
|
|
|
|
|
release_head(c, BASEHD);
|
|
|
|
|
goto out_finish;
|
|
|
|
|
}
|
2008-07-22 10:06:20 +00:00
|
|
|
|
ui->del_cmtno = c->cmt_no;
|
2020-07-07 12:51:40 +00:00
|
|
|
|
orphan_added = 1;
|
2008-07-14 16:08:37 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = write_head(c, BASEHD, dent, len, &lnum, &dent_offs, sync);
|
|
|
|
|
if (err)
|
|
|
|
|
goto out_release;
|
|
|
|
|
if (!sync) {
|
|
|
|
|
struct ubifs_wbuf *wbuf = &c->jheads[BASEHD].wbuf;
|
|
|
|
|
|
|
|
|
|
ubifs_wbuf_add_ino_nolock(wbuf, inode->i_ino);
|
|
|
|
|
ubifs_wbuf_add_ino_nolock(wbuf, dir->i_ino);
|
|
|
|
|
}
|
|
|
|
|
release_head(c, BASEHD);
|
|
|
|
|
kfree(dent);
|
ubifs: Add authentication nodes to journal
Nodes that are written to flash can only be authenticated through the
index after the next commit. When a journal replay is necessary the
nodes are not yet referenced by the index and thus can't be
authenticated.
This patch overcomes this situation by creating a hash over all nodes
beginning from the commit start node over the reference node(s) and
the buds themselves. From
time to time we insert authentication nodes. Authentication nodes
contain a HMAC from the current hash state, so that they can be
used to authenticate a journal replay up to the point where the
authentication node is. The hash is continued afterwards
so that theoretically we would only have to check the HMAC of
the last authentication node we find.
Overall we get this picture:
,,,,,,,,
,......,...........................................
,. CS , hash1.----. hash2.----.
,. | , . |hmac . |hmac
,. v , . v . v
,.REF#0,-> bud -> bud -> bud.-> auth -> bud -> bud.-> auth ...
,..|...,...........................................
, | ,
, | ,,,,,,,,,,,,,,,
. | hash3,----.
, | , |hmac
, v , v
, REF#1 -> bud -> bud,-> auth ...
,,,|,,,,,,,,,,,,,,,,,,
v
REF#2 -> ...
|
V
...
Note how hash3 covers CS, REF#0 and REF#1 so that it is not possible to
exchange or skip any reference nodes. Unlike the picture suggests the
auth nodes themselves are not hashed.
With this it is possible for an offline attacker to cut each journal
head or to drop the last reference node(s), but not to skip any journal
heads or to reorder any operations.
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Signed-off-by: Richard Weinberger <richard@nod.at>
2018-09-07 12:36:36 +00:00
|
|
|
|
ubifs_add_auth_dirt(c, lnum);
|
2008-07-14 16:08:37 +00:00
|
|
|
|
|
|
|
|
|
if (deletion) {
|
2020-01-20 22:32:00 +00:00
|
|
|
|
if (fname_name(nm) == NULL)
|
2017-05-17 08:36:46 +00:00
|
|
|
|
err = ubifs_tnc_remove_dh(c, &dent_key, nm->minor_hash);
|
|
|
|
|
else
|
|
|
|
|
err = ubifs_tnc_remove_nm(c, &dent_key, nm);
|
2008-07-14 16:08:37 +00:00
|
|
|
|
if (err)
|
|
|
|
|
goto out_ro;
|
|
|
|
|
err = ubifs_add_dirt(c, lnum, dlen);
|
|
|
|
|
} else
|
2018-09-07 12:36:34 +00:00
|
|
|
|
err = ubifs_tnc_add_nm(c, &dent_key, lnum, dent_offs, dlen,
|
|
|
|
|
hash_dent, nm);
|
2008-07-14 16:08:37 +00:00
|
|
|
|
if (err)
|
|
|
|
|
goto out_ro;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Note, we do not remove the inode from TNC even if the last reference
|
|
|
|
|
* to it has just been deleted, because the inode may still be opened.
|
|
|
|
|
* Instead, the inode has been added to orphan lists and the orphan
|
|
|
|
|
* subsystem will take further care about it.
|
|
|
|
|
*/
|
|
|
|
|
ino_key_init(c, &ino_key, inode->i_ino);
|
|
|
|
|
ino_offs = dent_offs + aligned_dlen;
|
2018-09-07 12:36:34 +00:00
|
|
|
|
err = ubifs_tnc_add(c, &ino_key, lnum, ino_offs, ilen, hash_ino);
|
2008-07-14 16:08:37 +00:00
|
|
|
|
if (err)
|
|
|
|
|
goto out_ro;
|
|
|
|
|
|
|
|
|
|
ino_key_init(c, &ino_key, dir->i_ino);
|
|
|
|
|
ino_offs += aligned_ilen;
|
2014-10-31 18:50:28 +00:00
|
|
|
|
err = ubifs_tnc_add(c, &ino_key, lnum, ino_offs,
|
2018-09-07 12:36:34 +00:00
|
|
|
|
UBIFS_INO_NODE_SZ + host_ui->data_len, hash_ino_host);
|
2008-07-14 16:08:37 +00:00
|
|
|
|
if (err)
|
|
|
|
|
goto out_ro;
|
|
|
|
|
|
|
|
|
|
finish_reservation(c);
|
|
|
|
|
spin_lock(&ui->ui_lock);
|
|
|
|
|
ui->synced_i_size = ui->ui_size;
|
|
|
|
|
spin_unlock(&ui->ui_lock);
|
2018-06-11 22:52:28 +00:00
|
|
|
|
if (xent) {
|
|
|
|
|
spin_lock(&host_ui->ui_lock);
|
|
|
|
|
host_ui->synced_i_size = host_ui->ui_size;
|
|
|
|
|
spin_unlock(&host_ui->ui_lock);
|
|
|
|
|
}
|
2008-07-14 16:08:37 +00:00
|
|
|
|
mark_inode_clean(c, ui);
|
2014-09-19 09:48:46 +00:00
|
|
|
|
mark_inode_clean(c, host_ui);
|
2008-07-14 16:08:37 +00:00
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
out_finish:
|
|
|
|
|
finish_reservation(c);
|
|
|
|
|
out_free:
|
|
|
|
|
kfree(dent);
|
|
|
|
|
return err;
|
|
|
|
|
|
|
|
|
|
out_release:
|
|
|
|
|
release_head(c, BASEHD);
|
2011-05-31 05:40:40 +00:00
|
|
|
|
kfree(dent);
|
2008-07-14 16:08:37 +00:00
|
|
|
|
out_ro:
|
|
|
|
|
ubifs_ro_mode(c, err);
|
2020-07-07 12:51:40 +00:00
|
|
|
|
if (orphan_added)
|
2008-07-14 16:08:37 +00:00
|
|
|
|
ubifs_delete_orphan(c, inode->i_ino);
|
|
|
|
|
finish_reservation(c);
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* ubifs_jnl_write_data - write a data node to the journal.
|
|
|
|
|
* @c: UBIFS file-system description object
|
|
|
|
|
* @inode: inode the data node belongs to
|
|
|
|
|
* @key: node key
|
|
|
|
|
* @buf: buffer to write
|
|
|
|
|
* @len: data length (must not exceed %UBIFS_BLOCK_SIZE)
|
|
|
|
|
*
|
|
|
|
|
* This function writes a data node to the journal. Returns %0 if the data node
|
|
|
|
|
* was successfully written, and a negative error code in case of failure.
|
|
|
|
|
*/
|
|
|
|
|
int ubifs_jnl_write_data(struct ubifs_info *c, const struct inode *inode,
|
|
|
|
|
const union ubifs_key *key, const void *buf, int len)
|
|
|
|
|
{
|
|
|
|
|
struct ubifs_data_node *data;
|
ubifs: Add authentication nodes to journal
Nodes that are written to flash can only be authenticated through the
index after the next commit. When a journal replay is necessary the
nodes are not yet referenced by the index and thus can't be
authenticated.
This patch overcomes this situation by creating a hash over all nodes
beginning from the commit start node over the reference node(s) and
the buds themselves. From
time to time we insert authentication nodes. Authentication nodes
contain a HMAC from the current hash state, so that they can be
used to authenticate a journal replay up to the point where the
authentication node is. The hash is continued afterwards
so that theoretically we would only have to check the HMAC of
the last authentication node we find.
Overall we get this picture:
,,,,,,,,
,......,...........................................
,. CS , hash1.----. hash2.----.
,. | , . |hmac . |hmac
,. v , . v . v
,.REF#0,-> bud -> bud -> bud.-> auth -> bud -> bud.-> auth ...
,..|...,...........................................
, | ,
, | ,,,,,,,,,,,,,,,
. | hash3,----.
, | , |hmac
, v , v
, REF#1 -> bud -> bud,-> auth ...
,,,|,,,,,,,,,,,,,,,,,,
v
REF#2 -> ...
|
V
...
Note how hash3 covers CS, REF#0 and REF#1 so that it is not possible to
exchange or skip any reference nodes. Unlike the picture suggests the
auth nodes themselves are not hashed.
With this it is possible for an offline attacker to cut each journal
head or to drop the last reference node(s), but not to skip any journal
heads or to reorder any operations.
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Signed-off-by: Richard Weinberger <richard@nod.at>
2018-09-07 12:36:36 +00:00
|
|
|
|
int err, lnum, offs, compr_type, out_len, compr_len, auth_len;
|
2011-03-04 22:55:02 +00:00
|
|
|
|
int dlen = COMPRESSED_DATA_NODE_BUF_SZ, allocated = 1;
|
ubifs: Add authentication nodes to journal
Nodes that are written to flash can only be authenticated through the
index after the next commit. When a journal replay is necessary the
nodes are not yet referenced by the index and thus can't be
authenticated.
This patch overcomes this situation by creating a hash over all nodes
beginning from the commit start node over the reference node(s) and
the buds themselves. From
time to time we insert authentication nodes. Authentication nodes
contain a HMAC from the current hash state, so that they can be
used to authenticate a journal replay up to the point where the
authentication node is. The hash is continued afterwards
so that theoretically we would only have to check the HMAC of
the last authentication node we find.
Overall we get this picture:
,,,,,,,,
,......,...........................................
,. CS , hash1.----. hash2.----.
,. | , . |hmac . |hmac
,. v , . v . v
,.REF#0,-> bud -> bud -> bud.-> auth -> bud -> bud.-> auth ...
,..|...,...........................................
, | ,
, | ,,,,,,,,,,,,,,,
. | hash3,----.
, | , |hmac
, v , v
, REF#1 -> bud -> bud,-> auth ...
,,,|,,,,,,,,,,,,,,,,,,
v
REF#2 -> ...
|
V
...
Note how hash3 covers CS, REF#0 and REF#1 so that it is not possible to
exchange or skip any reference nodes. Unlike the picture suggests the
auth nodes themselves are not hashed.
With this it is possible for an offline attacker to cut each journal
head or to drop the last reference node(s), but not to skip any journal
heads or to reorder any operations.
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Signed-off-by: Richard Weinberger <richard@nod.at>
2018-09-07 12:36:36 +00:00
|
|
|
|
int write_len;
|
2008-07-14 16:08:37 +00:00
|
|
|
|
struct ubifs_inode *ui = ubifs_inode(inode);
|
2019-12-09 21:27:21 +00:00
|
|
|
|
bool encrypted = IS_ENCRYPTED(inode);
|
2018-09-07 12:36:34 +00:00
|
|
|
|
u8 hash[UBIFS_HASH_ARR_SZ];
|
2008-07-14 16:08:37 +00:00
|
|
|
|
|
2012-01-13 10:33:53 +00:00
|
|
|
|
dbg_jnlk(key, "ino %lu, blk %u, len %d, key ",
|
|
|
|
|
(unsigned long)key_inum(c, key), key_block(c, key), len);
|
2018-07-12 11:01:57 +00:00
|
|
|
|
ubifs_assert(c, len <= UBIFS_BLOCK_SIZE);
|
2008-07-14 16:08:37 +00:00
|
|
|
|
|
2016-09-29 20:20:19 +00:00
|
|
|
|
if (encrypted)
|
|
|
|
|
dlen += UBIFS_CIPHER_BLOCK_SIZE;
|
|
|
|
|
|
ubifs: Add authentication nodes to journal
Nodes that are written to flash can only be authenticated through the
index after the next commit. When a journal replay is necessary the
nodes are not yet referenced by the index and thus can't be
authenticated.
This patch overcomes this situation by creating a hash over all nodes
beginning from the commit start node over the reference node(s) and
the buds themselves. From
time to time we insert authentication nodes. Authentication nodes
contain a HMAC from the current hash state, so that they can be
used to authenticate a journal replay up to the point where the
authentication node is. The hash is continued afterwards
so that theoretically we would only have to check the HMAC of
the last authentication node we find.
Overall we get this picture:
,,,,,,,,
,......,...........................................
,. CS , hash1.----. hash2.----.
,. | , . |hmac . |hmac
,. v , . v . v
,.REF#0,-> bud -> bud -> bud.-> auth -> bud -> bud.-> auth ...
,..|...,...........................................
, | ,
, | ,,,,,,,,,,,,,,,
. | hash3,----.
, | , |hmac
, v , v
, REF#1 -> bud -> bud,-> auth ...
,,,|,,,,,,,,,,,,,,,,,,
v
REF#2 -> ...
|
V
...
Note how hash3 covers CS, REF#0 and REF#1 so that it is not possible to
exchange or skip any reference nodes. Unlike the picture suggests the
auth nodes themselves are not hashed.
With this it is possible for an offline attacker to cut each journal
head or to drop the last reference node(s), but not to skip any journal
heads or to reorder any operations.
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Signed-off-by: Richard Weinberger <richard@nod.at>
2018-09-07 12:36:36 +00:00
|
|
|
|
auth_len = ubifs_auth_node_sz(c);
|
|
|
|
|
|
|
|
|
|
data = kmalloc(dlen + auth_len, GFP_NOFS | __GFP_NOWARN);
|
2011-03-04 22:55:02 +00:00
|
|
|
|
if (!data) {
|
|
|
|
|
/*
|
|
|
|
|
* Fall-back to the write reserve buffer. Note, we might be
|
|
|
|
|
* currently on the memory reclaim path, when the kernel is
|
|
|
|
|
* trying to free some memory by writing out dirty pages. The
|
|
|
|
|
* write reserve buffer helps us to guarantee that we are
|
|
|
|
|
* always able to write the data.
|
|
|
|
|
*/
|
|
|
|
|
allocated = 0;
|
|
|
|
|
mutex_lock(&c->write_reserve_mutex);
|
|
|
|
|
data = c->write_reserve_buf;
|
|
|
|
|
}
|
2008-07-14 16:08:37 +00:00
|
|
|
|
|
|
|
|
|
data->ch.node_type = UBIFS_DATA_NODE;
|
|
|
|
|
key_write(c, key, &data->key);
|
|
|
|
|
data->size = cpu_to_le32(len);
|
|
|
|
|
|
2008-12-23 12:39:14 +00:00
|
|
|
|
if (!(ui->flags & UBIFS_COMPR_FL))
|
2008-07-14 16:08:37 +00:00
|
|
|
|
/* Compression is disabled for this inode */
|
|
|
|
|
compr_type = UBIFS_COMPR_NONE;
|
|
|
|
|
else
|
|
|
|
|
compr_type = ui->compr_type;
|
|
|
|
|
|
2016-09-29 20:20:19 +00:00
|
|
|
|
out_len = compr_len = dlen - UBIFS_DATA_NODE_SZ;
|
|
|
|
|
ubifs_compress(c, buf, len, &data->data, &compr_len, &compr_type);
|
2018-07-12 11:01:57 +00:00
|
|
|
|
ubifs_assert(c, compr_len <= UBIFS_BLOCK_SIZE);
|
2016-09-29 20:20:19 +00:00
|
|
|
|
|
|
|
|
|
if (encrypted) {
|
|
|
|
|
err = ubifs_encrypt(inode, data, compr_len, &out_len, key_block(c, key));
|
|
|
|
|
if (err)
|
|
|
|
|
goto out_free;
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
data->compr_size = 0;
|
ubifs: fix unencrypted journal write
Without this, I get the following on reboot:
UBIFS error (ubi1:0 pid 703): ubifs_load_znode: bad target node (type 1) length (8240)
UBIFS error (ubi1:0 pid 703): ubifs_load_znode: have to be in range of 48-4144
UBIFS error (ubi1:0 pid 703): ubifs_load_znode: bad indexing node at LEB 13:11080, error 5
magic 0x6101831
crc 0xb1cb246f
node_type 9 (indexing node)
group_type 0 (no node group)
sqnum 546
len 128
child_cnt 5
level 0
Branches:
0: LEB 14:72088 len 161 key (133, inode)
1: LEB 14:81120 len 160 key (134, inode)
2: LEB 20:26624 len 8240 key (134, data, 0)
3: LEB 14:81280 len 160 key (135, inode)
4: LEB 20:34864 len 8240 key (135, data, 0)
UBIFS warning (ubi1:0 pid 703): ubifs_ro_mode.part.0: switched to read-only mode, error -22
CPU: 0 PID: 703 Comm: mount Not tainted 4.9.0-next-20161213+ #1197
Hardware name: Atmel SAMA5
[<c010d2ac>] (unwind_backtrace) from [<c010b250>] (show_stack+0x10/0x14)
[<c010b250>] (show_stack) from [<c024df94>] (ubifs_jnl_update+0x2e8/0x614)
[<c024df94>] (ubifs_jnl_update) from [<c0254bf8>] (ubifs_mkdir+0x160/0x204)
[<c0254bf8>] (ubifs_mkdir) from [<c01a6030>] (vfs_mkdir+0xb0/0x104)
[<c01a6030>] (vfs_mkdir) from [<c0286070>] (ovl_create_real+0x118/0x248)
[<c0286070>] (ovl_create_real) from [<c0283ed4>] (ovl_fill_super+0x994/0xaf4)
[<c0283ed4>] (ovl_fill_super) from [<c019c394>] (mount_nodev+0x44/0x9c)
[<c019c394>] (mount_nodev) from [<c019c4ac>] (mount_fs+0x14/0xa4)
[<c019c4ac>] (mount_fs) from [<c01b5338>] (vfs_kern_mount+0x4c/0xd4)
[<c01b5338>] (vfs_kern_mount) from [<c01b6b80>] (do_mount+0x154/0xac8)
[<c01b6b80>] (do_mount) from [<c01b782c>] (SyS_mount+0x74/0x9c)
[<c01b782c>] (SyS_mount) from [<c0107f80>] (ret_fast_syscall+0x0/0x3c)
UBIFS error (ubi1:0 pid 703): ubifs_mkdir: cannot create directory, error -22
overlayfs: failed to create directory /mnt/ovl/work/work (errno: 22); mounting read-only
Fixes: 7799953b34d1 ("ubifs: Implement encrypt/decrypt for all IO")
Signed-off-by: Peter Rosin <peda@axentia.se>
Tested-by: Kevin Hilman <khilman@baylibre.com>
Signed-off-by: Richard Weinberger <richard@nod.at>
2017-01-04 08:38:29 +00:00
|
|
|
|
out_len = compr_len;
|
2016-09-29 20:20:19 +00:00
|
|
|
|
}
|
2008-07-14 16:08:37 +00:00
|
|
|
|
|
|
|
|
|
dlen = UBIFS_DATA_NODE_SZ + out_len;
|
ubifs: Add authentication nodes to journal
Nodes that are written to flash can only be authenticated through the
index after the next commit. When a journal replay is necessary the
nodes are not yet referenced by the index and thus can't be
authenticated.
This patch overcomes this situation by creating a hash over all nodes
beginning from the commit start node over the reference node(s) and
the buds themselves. From
time to time we insert authentication nodes. Authentication nodes
contain a HMAC from the current hash state, so that they can be
used to authenticate a journal replay up to the point where the
authentication node is. The hash is continued afterwards
so that theoretically we would only have to check the HMAC of
the last authentication node we find.
Overall we get this picture:
,,,,,,,,
,......,...........................................
,. CS , hash1.----. hash2.----.
,. | , . |hmac . |hmac
,. v , . v . v
,.REF#0,-> bud -> bud -> bud.-> auth -> bud -> bud.-> auth ...
,..|...,...........................................
, | ,
, | ,,,,,,,,,,,,,,,
. | hash3,----.
, | , |hmac
, v , v
, REF#1 -> bud -> bud,-> auth ...
,,,|,,,,,,,,,,,,,,,,,,
v
REF#2 -> ...
|
V
...
Note how hash3 covers CS, REF#0 and REF#1 so that it is not possible to
exchange or skip any reference nodes. Unlike the picture suggests the
auth nodes themselves are not hashed.
With this it is possible for an offline attacker to cut each journal
head or to drop the last reference node(s), but not to skip any journal
heads or to reorder any operations.
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Signed-off-by: Richard Weinberger <richard@nod.at>
2018-09-07 12:36:36 +00:00
|
|
|
|
if (ubifs_authenticated(c))
|
|
|
|
|
write_len = ALIGN(dlen, 8) + auth_len;
|
|
|
|
|
else
|
|
|
|
|
write_len = dlen;
|
|
|
|
|
|
2008-07-14 16:08:37 +00:00
|
|
|
|
data->compr_type = cpu_to_le16(compr_type);
|
|
|
|
|
|
|
|
|
|
/* Make reservation before allocating sequence numbers */
|
ubifs: Add authentication nodes to journal
Nodes that are written to flash can only be authenticated through the
index after the next commit. When a journal replay is necessary the
nodes are not yet referenced by the index and thus can't be
authenticated.
This patch overcomes this situation by creating a hash over all nodes
beginning from the commit start node over the reference node(s) and
the buds themselves. From
time to time we insert authentication nodes. Authentication nodes
contain a HMAC from the current hash state, so that they can be
used to authenticate a journal replay up to the point where the
authentication node is. The hash is continued afterwards
so that theoretically we would only have to check the HMAC of
the last authentication node we find.
Overall we get this picture:
,,,,,,,,
,......,...........................................
,. CS , hash1.----. hash2.----.
,. | , . |hmac . |hmac
,. v , . v . v
,.REF#0,-> bud -> bud -> bud.-> auth -> bud -> bud.-> auth ...
,..|...,...........................................
, | ,
, | ,,,,,,,,,,,,,,,
. | hash3,----.
, | , |hmac
, v , v
, REF#1 -> bud -> bud,-> auth ...
,,,|,,,,,,,,,,,,,,,,,,
v
REF#2 -> ...
|
V
...
Note how hash3 covers CS, REF#0 and REF#1 so that it is not possible to
exchange or skip any reference nodes. Unlike the picture suggests the
auth nodes themselves are not hashed.
With this it is possible for an offline attacker to cut each journal
head or to drop the last reference node(s), but not to skip any journal
heads or to reorder any operations.
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Signed-off-by: Richard Weinberger <richard@nod.at>
2018-09-07 12:36:36 +00:00
|
|
|
|
err = make_reservation(c, DATAHD, write_len);
|
2008-07-14 16:08:37 +00:00
|
|
|
|
if (err)
|
|
|
|
|
goto out_free;
|
|
|
|
|
|
2018-09-07 12:36:28 +00:00
|
|
|
|
ubifs_prepare_node(c, data, dlen, 0);
|
ubifs: Add authentication nodes to journal
Nodes that are written to flash can only be authenticated through the
index after the next commit. When a journal replay is necessary the
nodes are not yet referenced by the index and thus can't be
authenticated.
This patch overcomes this situation by creating a hash over all nodes
beginning from the commit start node over the reference node(s) and
the buds themselves. From
time to time we insert authentication nodes. Authentication nodes
contain a HMAC from the current hash state, so that they can be
used to authenticate a journal replay up to the point where the
authentication node is. The hash is continued afterwards
so that theoretically we would only have to check the HMAC of
the last authentication node we find.
Overall we get this picture:
,,,,,,,,
,......,...........................................
,. CS , hash1.----. hash2.----.
,. | , . |hmac . |hmac
,. v , . v . v
,.REF#0,-> bud -> bud -> bud.-> auth -> bud -> bud.-> auth ...
,..|...,...........................................
, | ,
, | ,,,,,,,,,,,,,,,
. | hash3,----.
, | , |hmac
, v , v
, REF#1 -> bud -> bud,-> auth ...
,,,|,,,,,,,,,,,,,,,,,,
v
REF#2 -> ...
|
V
...
Note how hash3 covers CS, REF#0 and REF#1 so that it is not possible to
exchange or skip any reference nodes. Unlike the picture suggests the
auth nodes themselves are not hashed.
With this it is possible for an offline attacker to cut each journal
head or to drop the last reference node(s), but not to skip any journal
heads or to reorder any operations.
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Signed-off-by: Richard Weinberger <richard@nod.at>
2018-09-07 12:36:36 +00:00
|
|
|
|
err = write_head(c, DATAHD, data, write_len, &lnum, &offs, 0);
|
2008-07-14 16:08:37 +00:00
|
|
|
|
if (err)
|
|
|
|
|
goto out_release;
|
2018-09-07 12:36:34 +00:00
|
|
|
|
|
|
|
|
|
err = ubifs_node_calc_hash(c, data, hash);
|
|
|
|
|
if (err)
|
|
|
|
|
goto out_release;
|
|
|
|
|
|
2008-07-14 16:08:37 +00:00
|
|
|
|
ubifs_wbuf_add_ino_nolock(&c->jheads[DATAHD].wbuf, key_inum(c, key));
|
|
|
|
|
release_head(c, DATAHD);
|
|
|
|
|
|
ubifs: Add authentication nodes to journal
Nodes that are written to flash can only be authenticated through the
index after the next commit. When a journal replay is necessary the
nodes are not yet referenced by the index and thus can't be
authenticated.
This patch overcomes this situation by creating a hash over all nodes
beginning from the commit start node over the reference node(s) and
the buds themselves. From
time to time we insert authentication nodes. Authentication nodes
contain a HMAC from the current hash state, so that they can be
used to authenticate a journal replay up to the point where the
authentication node is. The hash is continued afterwards
so that theoretically we would only have to check the HMAC of
the last authentication node we find.
Overall we get this picture:
,,,,,,,,
,......,...........................................
,. CS , hash1.----. hash2.----.
,. | , . |hmac . |hmac
,. v , . v . v
,.REF#0,-> bud -> bud -> bud.-> auth -> bud -> bud.-> auth ...
,..|...,...........................................
, | ,
, | ,,,,,,,,,,,,,,,
. | hash3,----.
, | , |hmac
, v , v
, REF#1 -> bud -> bud,-> auth ...
,,,|,,,,,,,,,,,,,,,,,,
v
REF#2 -> ...
|
V
...
Note how hash3 covers CS, REF#0 and REF#1 so that it is not possible to
exchange or skip any reference nodes. Unlike the picture suggests the
auth nodes themselves are not hashed.
With this it is possible for an offline attacker to cut each journal
head or to drop the last reference node(s), but not to skip any journal
heads or to reorder any operations.
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Signed-off-by: Richard Weinberger <richard@nod.at>
2018-09-07 12:36:36 +00:00
|
|
|
|
ubifs_add_auth_dirt(c, lnum);
|
|
|
|
|
|
2018-09-07 12:36:34 +00:00
|
|
|
|
err = ubifs_tnc_add(c, key, lnum, offs, dlen, hash);
|
2008-07-14 16:08:37 +00:00
|
|
|
|
if (err)
|
|
|
|
|
goto out_ro;
|
|
|
|
|
|
|
|
|
|
finish_reservation(c);
|
2011-03-04 22:55:02 +00:00
|
|
|
|
if (!allocated)
|
|
|
|
|
mutex_unlock(&c->write_reserve_mutex);
|
|
|
|
|
else
|
|
|
|
|
kfree(data);
|
2008-07-14 16:08:37 +00:00
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
out_release:
|
|
|
|
|
release_head(c, DATAHD);
|
|
|
|
|
out_ro:
|
|
|
|
|
ubifs_ro_mode(c, err);
|
|
|
|
|
finish_reservation(c);
|
|
|
|
|
out_free:
|
2011-03-04 22:55:02 +00:00
|
|
|
|
if (!allocated)
|
|
|
|
|
mutex_unlock(&c->write_reserve_mutex);
|
|
|
|
|
else
|
|
|
|
|
kfree(data);
|
2008-07-14 16:08:37 +00:00
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* ubifs_jnl_write_inode - flush inode to the journal.
|
|
|
|
|
* @c: UBIFS file-system description object
|
|
|
|
|
* @inode: inode to flush
|
|
|
|
|
*
|
|
|
|
|
* This function writes inode @inode to the journal. If the inode is
|
|
|
|
|
* synchronous, it also synchronizes the write-buffer. Returns zero in case of
|
|
|
|
|
* success and a negative error code in case of failure.
|
|
|
|
|
*/
|
2008-07-22 09:06:13 +00:00
|
|
|
|
int ubifs_jnl_write_inode(struct ubifs_info *c, const struct inode *inode)
|
2008-07-14 16:08:37 +00:00
|
|
|
|
{
|
2008-07-22 09:06:13 +00:00
|
|
|
|
int err, lnum, offs;
|
2019-04-04 22:34:36 +00:00
|
|
|
|
struct ubifs_ino_node *ino, *ino_start;
|
2008-07-14 16:08:37 +00:00
|
|
|
|
struct ubifs_inode *ui = ubifs_inode(inode);
|
2019-04-04 22:34:36 +00:00
|
|
|
|
int sync = 0, write_len = 0, ilen = UBIFS_INO_NODE_SZ;
|
ubifs: Add authentication nodes to journal
Nodes that are written to flash can only be authenticated through the
index after the next commit. When a journal replay is necessary the
nodes are not yet referenced by the index and thus can't be
authenticated.
This patch overcomes this situation by creating a hash over all nodes
beginning from the commit start node over the reference node(s) and
the buds themselves. From
time to time we insert authentication nodes. Authentication nodes
contain a HMAC from the current hash state, so that they can be
used to authenticate a journal replay up to the point where the
authentication node is. The hash is continued afterwards
so that theoretically we would only have to check the HMAC of
the last authentication node we find.
Overall we get this picture:
,,,,,,,,
,......,...........................................
,. CS , hash1.----. hash2.----.
,. | , . |hmac . |hmac
,. v , . v . v
,.REF#0,-> bud -> bud -> bud.-> auth -> bud -> bud.-> auth ...
,..|...,...........................................
, | ,
, | ,,,,,,,,,,,,,,,
. | hash3,----.
, | , |hmac
, v , v
, REF#1 -> bud -> bud,-> auth ...
,,,|,,,,,,,,,,,,,,,,,,
v
REF#2 -> ...
|
V
...
Note how hash3 covers CS, REF#0 and REF#1 so that it is not possible to
exchange or skip any reference nodes. Unlike the picture suggests the
auth nodes themselves are not hashed.
With this it is possible for an offline attacker to cut each journal
head or to drop the last reference node(s), but not to skip any journal
heads or to reorder any operations.
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Signed-off-by: Richard Weinberger <richard@nod.at>
2018-09-07 12:36:36 +00:00
|
|
|
|
int last_reference = !inode->i_nlink;
|
2019-04-04 22:34:36 +00:00
|
|
|
|
int kill_xattrs = ui->xattr_cnt && last_reference;
|
2018-09-07 12:36:34 +00:00
|
|
|
|
u8 hash[UBIFS_HASH_ARR_SZ];
|
2008-07-14 16:08:37 +00:00
|
|
|
|
|
2008-07-22 09:06:13 +00:00
|
|
|
|
dbg_jnl("ino %lu, nlink %u", inode->i_ino, inode->i_nlink);
|
2008-07-14 16:08:37 +00:00
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* If the inode is being deleted, do not write the attached data. No
|
|
|
|
|
* need to synchronize the write-buffer either.
|
|
|
|
|
*/
|
2008-07-22 09:06:13 +00:00
|
|
|
|
if (!last_reference) {
|
ubifs: Add authentication nodes to journal
Nodes that are written to flash can only be authenticated through the
index after the next commit. When a journal replay is necessary the
nodes are not yet referenced by the index and thus can't be
authenticated.
This patch overcomes this situation by creating a hash over all nodes
beginning from the commit start node over the reference node(s) and
the buds themselves. From
time to time we insert authentication nodes. Authentication nodes
contain a HMAC from the current hash state, so that they can be
used to authenticate a journal replay up to the point where the
authentication node is. The hash is continued afterwards
so that theoretically we would only have to check the HMAC of
the last authentication node we find.
Overall we get this picture:
,,,,,,,,
,......,...........................................
,. CS , hash1.----. hash2.----.
,. | , . |hmac . |hmac
,. v , . v . v
,.REF#0,-> bud -> bud -> bud.-> auth -> bud -> bud.-> auth ...
,..|...,...........................................
, | ,
, | ,,,,,,,,,,,,,,,
. | hash3,----.
, | , |hmac
, v , v
, REF#1 -> bud -> bud,-> auth ...
,,,|,,,,,,,,,,,,,,,,,,
v
REF#2 -> ...
|
V
...
Note how hash3 covers CS, REF#0 and REF#1 so that it is not possible to
exchange or skip any reference nodes. Unlike the picture suggests the
auth nodes themselves are not hashed.
With this it is possible for an offline attacker to cut each journal
head or to drop the last reference node(s), but not to skip any journal
heads or to reorder any operations.
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Signed-off-by: Richard Weinberger <richard@nod.at>
2018-09-07 12:36:36 +00:00
|
|
|
|
ilen += ui->data_len;
|
2008-07-14 16:08:37 +00:00
|
|
|
|
sync = IS_SYNC(inode);
|
2019-04-04 22:34:36 +00:00
|
|
|
|
} else if (kill_xattrs) {
|
|
|
|
|
write_len += UBIFS_INO_NODE_SZ * ui->xattr_cnt;
|
2008-07-14 16:08:37 +00:00
|
|
|
|
}
|
ubifs: Add authentication nodes to journal
Nodes that are written to flash can only be authenticated through the
index after the next commit. When a journal replay is necessary the
nodes are not yet referenced by the index and thus can't be
authenticated.
This patch overcomes this situation by creating a hash over all nodes
beginning from the commit start node over the reference node(s) and
the buds themselves. From
time to time we insert authentication nodes. Authentication nodes
contain a HMAC from the current hash state, so that they can be
used to authenticate a journal replay up to the point where the
authentication node is. The hash is continued afterwards
so that theoretically we would only have to check the HMAC of
the last authentication node we find.
Overall we get this picture:
,,,,,,,,
,......,...........................................
,. CS , hash1.----. hash2.----.
,. | , . |hmac . |hmac
,. v , . v . v
,.REF#0,-> bud -> bud -> bud.-> auth -> bud -> bud.-> auth ...
,..|...,...........................................
, | ,
, | ,,,,,,,,,,,,,,,
. | hash3,----.
, | , |hmac
, v , v
, REF#1 -> bud -> bud,-> auth ...
,,,|,,,,,,,,,,,,,,,,,,
v
REF#2 -> ...
|
V
...
Note how hash3 covers CS, REF#0 and REF#1 so that it is not possible to
exchange or skip any reference nodes. Unlike the picture suggests the
auth nodes themselves are not hashed.
With this it is possible for an offline attacker to cut each journal
head or to drop the last reference node(s), but not to skip any journal
heads or to reorder any operations.
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Signed-off-by: Richard Weinberger <richard@nod.at>
2018-09-07 12:36:36 +00:00
|
|
|
|
|
|
|
|
|
if (ubifs_authenticated(c))
|
2019-04-04 22:34:36 +00:00
|
|
|
|
write_len += ALIGN(ilen, 8) + ubifs_auth_node_sz(c);
|
ubifs: Add authentication nodes to journal
Nodes that are written to flash can only be authenticated through the
index after the next commit. When a journal replay is necessary the
nodes are not yet referenced by the index and thus can't be
authenticated.
This patch overcomes this situation by creating a hash over all nodes
beginning from the commit start node over the reference node(s) and
the buds themselves. From
time to time we insert authentication nodes. Authentication nodes
contain a HMAC from the current hash state, so that they can be
used to authenticate a journal replay up to the point where the
authentication node is. The hash is continued afterwards
so that theoretically we would only have to check the HMAC of
the last authentication node we find.
Overall we get this picture:
,,,,,,,,
,......,...........................................
,. CS , hash1.----. hash2.----.
,. | , . |hmac . |hmac
,. v , . v . v
,.REF#0,-> bud -> bud -> bud.-> auth -> bud -> bud.-> auth ...
,..|...,...........................................
, | ,
, | ,,,,,,,,,,,,,,,
. | hash3,----.
, | , |hmac
, v , v
, REF#1 -> bud -> bud,-> auth ...
,,,|,,,,,,,,,,,,,,,,,,
v
REF#2 -> ...
|
V
...
Note how hash3 covers CS, REF#0 and REF#1 so that it is not possible to
exchange or skip any reference nodes. Unlike the picture suggests the
auth nodes themselves are not hashed.
With this it is possible for an offline attacker to cut each journal
head or to drop the last reference node(s), but not to skip any journal
heads or to reorder any operations.
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Signed-off-by: Richard Weinberger <richard@nod.at>
2018-09-07 12:36:36 +00:00
|
|
|
|
else
|
2019-04-04 22:34:36 +00:00
|
|
|
|
write_len += ilen;
|
ubifs: Add authentication nodes to journal
Nodes that are written to flash can only be authenticated through the
index after the next commit. When a journal replay is necessary the
nodes are not yet referenced by the index and thus can't be
authenticated.
This patch overcomes this situation by creating a hash over all nodes
beginning from the commit start node over the reference node(s) and
the buds themselves. From
time to time we insert authentication nodes. Authentication nodes
contain a HMAC from the current hash state, so that they can be
used to authenticate a journal replay up to the point where the
authentication node is. The hash is continued afterwards
so that theoretically we would only have to check the HMAC of
the last authentication node we find.
Overall we get this picture:
,,,,,,,,
,......,...........................................
,. CS , hash1.----. hash2.----.
,. | , . |hmac . |hmac
,. v , . v . v
,.REF#0,-> bud -> bud -> bud.-> auth -> bud -> bud.-> auth ...
,..|...,...........................................
, | ,
, | ,,,,,,,,,,,,,,,
. | hash3,----.
, | , |hmac
, v , v
, REF#1 -> bud -> bud,-> auth ...
,,,|,,,,,,,,,,,,,,,,,,
v
REF#2 -> ...
|
V
...
Note how hash3 covers CS, REF#0 and REF#1 so that it is not possible to
exchange or skip any reference nodes. Unlike the picture suggests the
auth nodes themselves are not hashed.
With this it is possible for an offline attacker to cut each journal
head or to drop the last reference node(s), but not to skip any journal
heads or to reorder any operations.
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Signed-off-by: Richard Weinberger <richard@nod.at>
2018-09-07 12:36:36 +00:00
|
|
|
|
|
2019-04-04 22:34:36 +00:00
|
|
|
|
ino_start = ino = kmalloc(write_len, GFP_NOFS);
|
2008-07-14 16:08:37 +00:00
|
|
|
|
if (!ino)
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
|
|
/* Make reservation before allocating sequence numbers */
|
ubifs: Add authentication nodes to journal
Nodes that are written to flash can only be authenticated through the
index after the next commit. When a journal replay is necessary the
nodes are not yet referenced by the index and thus can't be
authenticated.
This patch overcomes this situation by creating a hash over all nodes
beginning from the commit start node over the reference node(s) and
the buds themselves. From
time to time we insert authentication nodes. Authentication nodes
contain a HMAC from the current hash state, so that they can be
used to authenticate a journal replay up to the point where the
authentication node is. The hash is continued afterwards
so that theoretically we would only have to check the HMAC of
the last authentication node we find.
Overall we get this picture:
,,,,,,,,
,......,...........................................
,. CS , hash1.----. hash2.----.
,. | , . |hmac . |hmac
,. v , . v . v
,.REF#0,-> bud -> bud -> bud.-> auth -> bud -> bud.-> auth ...
,..|...,...........................................
, | ,
, | ,,,,,,,,,,,,,,,
. | hash3,----.
, | , |hmac
, v , v
, REF#1 -> bud -> bud,-> auth ...
,,,|,,,,,,,,,,,,,,,,,,
v
REF#2 -> ...
|
V
...
Note how hash3 covers CS, REF#0 and REF#1 so that it is not possible to
exchange or skip any reference nodes. Unlike the picture suggests the
auth nodes themselves are not hashed.
With this it is possible for an offline attacker to cut each journal
head or to drop the last reference node(s), but not to skip any journal
heads or to reorder any operations.
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Signed-off-by: Richard Weinberger <richard@nod.at>
2018-09-07 12:36:36 +00:00
|
|
|
|
err = make_reservation(c, BASEHD, write_len);
|
2008-07-14 16:08:37 +00:00
|
|
|
|
if (err)
|
|
|
|
|
goto out_free;
|
|
|
|
|
|
2019-04-04 22:34:36 +00:00
|
|
|
|
if (kill_xattrs) {
|
|
|
|
|
union ubifs_key key;
|
|
|
|
|
struct fscrypt_name nm = {0};
|
|
|
|
|
struct inode *xino;
|
|
|
|
|
struct ubifs_dent_node *xent, *pxent = NULL;
|
|
|
|
|
|
2021-01-20 13:12:53 +00:00
|
|
|
|
if (ui->xattr_cnt > ubifs_xattr_max_cnt(c)) {
|
2021-05-08 03:33:13 +00:00
|
|
|
|
err = -EPERM;
|
2019-04-04 22:34:38 +00:00
|
|
|
|
ubifs_err(c, "Cannot delete inode, it has too much xattrs!");
|
|
|
|
|
goto out_release;
|
|
|
|
|
}
|
|
|
|
|
|
2019-04-04 22:34:36 +00:00
|
|
|
|
lowest_xent_key(c, &key, inode->i_ino);
|
|
|
|
|
while (1) {
|
|
|
|
|
xent = ubifs_tnc_next_ent(c, &key, &nm);
|
|
|
|
|
if (IS_ERR(xent)) {
|
|
|
|
|
err = PTR_ERR(xent);
|
|
|
|
|
if (err == -ENOENT)
|
|
|
|
|
break;
|
|
|
|
|
|
2020-06-01 09:10:36 +00:00
|
|
|
|
kfree(pxent);
|
2019-04-04 22:34:36 +00:00
|
|
|
|
goto out_release;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fname_name(&nm) = xent->name;
|
|
|
|
|
fname_len(&nm) = le16_to_cpu(xent->nlen);
|
|
|
|
|
|
2019-10-16 10:08:03 +00:00
|
|
|
|
xino = ubifs_iget(c->vfs_sb, le64_to_cpu(xent->inum));
|
2019-04-04 22:34:38 +00:00
|
|
|
|
if (IS_ERR(xino)) {
|
|
|
|
|
err = PTR_ERR(xino);
|
|
|
|
|
ubifs_err(c, "dead directory entry '%s', error %d",
|
|
|
|
|
xent->name, err);
|
|
|
|
|
ubifs_ro_mode(c, err);
|
2020-06-01 09:10:36 +00:00
|
|
|
|
kfree(pxent);
|
2020-03-03 09:40:22 +00:00
|
|
|
|
kfree(xent);
|
2019-04-04 22:34:38 +00:00
|
|
|
|
goto out_release;
|
|
|
|
|
}
|
2019-04-04 22:34:36 +00:00
|
|
|
|
ubifs_assert(c, ubifs_inode(xino)->xattr);
|
|
|
|
|
|
|
|
|
|
clear_nlink(xino);
|
|
|
|
|
pack_inode(c, ino, xino, 0);
|
|
|
|
|
ino = (void *)ino + UBIFS_INO_NODE_SZ;
|
|
|
|
|
iput(xino);
|
|
|
|
|
|
|
|
|
|
kfree(pxent);
|
|
|
|
|
pxent = xent;
|
|
|
|
|
key_read(c, &xent->key, &key);
|
|
|
|
|
}
|
|
|
|
|
kfree(pxent);
|
|
|
|
|
}
|
|
|
|
|
|
2008-07-22 09:19:09 +00:00
|
|
|
|
pack_inode(c, ino, inode, 1);
|
2018-09-07 12:36:34 +00:00
|
|
|
|
err = ubifs_node_calc_hash(c, ino, hash);
|
|
|
|
|
if (err)
|
|
|
|
|
goto out_release;
|
|
|
|
|
|
2019-04-04 22:34:36 +00:00
|
|
|
|
err = write_head(c, BASEHD, ino_start, write_len, &lnum, &offs, sync);
|
2008-07-14 16:08:37 +00:00
|
|
|
|
if (err)
|
|
|
|
|
goto out_release;
|
|
|
|
|
if (!sync)
|
|
|
|
|
ubifs_wbuf_add_ino_nolock(&c->jheads[BASEHD].wbuf,
|
|
|
|
|
inode->i_ino);
|
|
|
|
|
release_head(c, BASEHD);
|
|
|
|
|
|
2008-07-22 09:06:13 +00:00
|
|
|
|
if (last_reference) {
|
2008-07-14 16:08:37 +00:00
|
|
|
|
err = ubifs_tnc_remove_ino(c, inode->i_ino);
|
|
|
|
|
if (err)
|
|
|
|
|
goto out_ro;
|
|
|
|
|
ubifs_delete_orphan(c, inode->i_ino);
|
2019-04-04 22:34:36 +00:00
|
|
|
|
err = ubifs_add_dirt(c, lnum, write_len);
|
2008-07-14 16:08:37 +00:00
|
|
|
|
} else {
|
|
|
|
|
union ubifs_key key;
|
|
|
|
|
|
2020-09-28 18:58:59 +00:00
|
|
|
|
ubifs_add_auth_dirt(c, lnum);
|
|
|
|
|
|
2008-07-14 16:08:37 +00:00
|
|
|
|
ino_key_init(c, &key, inode->i_ino);
|
ubifs: Add authentication nodes to journal
Nodes that are written to flash can only be authenticated through the
index after the next commit. When a journal replay is necessary the
nodes are not yet referenced by the index and thus can't be
authenticated.
This patch overcomes this situation by creating a hash over all nodes
beginning from the commit start node over the reference node(s) and
the buds themselves. From
time to time we insert authentication nodes. Authentication nodes
contain a HMAC from the current hash state, so that they can be
used to authenticate a journal replay up to the point where the
authentication node is. The hash is continued afterwards
so that theoretically we would only have to check the HMAC of
the last authentication node we find.
Overall we get this picture:
,,,,,,,,
,......,...........................................
,. CS , hash1.----. hash2.----.
,. | , . |hmac . |hmac
,. v , . v . v
,.REF#0,-> bud -> bud -> bud.-> auth -> bud -> bud.-> auth ...
,..|...,...........................................
, | ,
, | ,,,,,,,,,,,,,,,
. | hash3,----.
, | , |hmac
, v , v
, REF#1 -> bud -> bud,-> auth ...
,,,|,,,,,,,,,,,,,,,,,,
v
REF#2 -> ...
|
V
...
Note how hash3 covers CS, REF#0 and REF#1 so that it is not possible to
exchange or skip any reference nodes. Unlike the picture suggests the
auth nodes themselves are not hashed.
With this it is possible for an offline attacker to cut each journal
head or to drop the last reference node(s), but not to skip any journal
heads or to reorder any operations.
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Signed-off-by: Richard Weinberger <richard@nod.at>
2018-09-07 12:36:36 +00:00
|
|
|
|
err = ubifs_tnc_add(c, &key, lnum, offs, ilen, hash);
|
2008-07-14 16:08:37 +00:00
|
|
|
|
}
|
|
|
|
|
if (err)
|
|
|
|
|
goto out_ro;
|
|
|
|
|
|
|
|
|
|
finish_reservation(c);
|
|
|
|
|
spin_lock(&ui->ui_lock);
|
|
|
|
|
ui->synced_i_size = ui->ui_size;
|
|
|
|
|
spin_unlock(&ui->ui_lock);
|
2019-04-04 22:34:36 +00:00
|
|
|
|
kfree(ino_start);
|
2008-07-14 16:08:37 +00:00
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
out_release:
|
|
|
|
|
release_head(c, BASEHD);
|
|
|
|
|
out_ro:
|
|
|
|
|
ubifs_ro_mode(c, err);
|
|
|
|
|
finish_reservation(c);
|
|
|
|
|
out_free:
|
2019-04-04 22:34:36 +00:00
|
|
|
|
kfree(ino_start);
|
2008-07-14 16:08:37 +00:00
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
2008-07-22 10:06:20 +00:00
|
|
|
|
/**
|
2008-07-23 12:48:39 +00:00
|
|
|
|
* ubifs_jnl_delete_inode - delete an inode.
|
2008-07-22 10:06:20 +00:00
|
|
|
|
* @c: UBIFS file-system description object
|
|
|
|
|
* @inode: inode to delete
|
|
|
|
|
*
|
|
|
|
|
* This function deletes inode @inode which includes removing it from orphans,
|
|
|
|
|
* deleting it from TNC and, in some cases, writing a deletion inode to the
|
|
|
|
|
* journal.
|
|
|
|
|
*
|
|
|
|
|
* When regular file inodes are unlinked or a directory inode is removed, the
|
2008-07-23 12:48:39 +00:00
|
|
|
|
* 'ubifs_jnl_update()' function writes a corresponding deletion inode and
|
2008-07-22 10:06:20 +00:00
|
|
|
|
* direntry to the media, and adds the inode to orphans. After this, when the
|
|
|
|
|
* last reference to this inode has been dropped, this function is called. In
|
|
|
|
|
* general, it has to write one more deletion inode to the media, because if
|
|
|
|
|
* a commit happened between 'ubifs_jnl_update()' and
|
|
|
|
|
* 'ubifs_jnl_delete_inode()', the deletion inode is not in the journal
|
2008-07-23 12:48:39 +00:00
|
|
|
|
* anymore, and in fact it might not be on the flash anymore, because it might
|
|
|
|
|
* have been garbage-collected already. And for optimization reasons UBIFS does
|
2008-07-22 10:06:20 +00:00
|
|
|
|
* not read the orphan area if it has been unmounted cleanly, so it would have
|
|
|
|
|
* no indication in the journal that there is a deleted inode which has to be
|
|
|
|
|
* removed from TNC.
|
|
|
|
|
*
|
|
|
|
|
* However, if there was no commit between 'ubifs_jnl_update()' and
|
|
|
|
|
* 'ubifs_jnl_delete_inode()', then there is no need to write the deletion
|
2008-07-23 12:48:39 +00:00
|
|
|
|
* inode to the media for the second time. And this is quite a typical case.
|
2008-07-22 10:06:20 +00:00
|
|
|
|
*
|
|
|
|
|
* This function returns zero in case of success and a negative error code in
|
|
|
|
|
* case of failure.
|
|
|
|
|
*/
|
|
|
|
|
int ubifs_jnl_delete_inode(struct ubifs_info *c, const struct inode *inode)
|
|
|
|
|
{
|
|
|
|
|
int err;
|
|
|
|
|
struct ubifs_inode *ui = ubifs_inode(inode);
|
|
|
|
|
|
2018-07-12 11:01:57 +00:00
|
|
|
|
ubifs_assert(c, inode->i_nlink == 0);
|
2008-07-22 10:06:20 +00:00
|
|
|
|
|
2019-04-04 22:34:36 +00:00
|
|
|
|
if (ui->xattr_cnt || ui->del_cmtno != c->cmt_no)
|
|
|
|
|
/* A commit happened for sure or inode hosts xattrs */
|
2008-07-22 10:06:20 +00:00
|
|
|
|
return ubifs_jnl_write_inode(c, inode);
|
|
|
|
|
|
|
|
|
|
down_read(&c->commit_sem);
|
|
|
|
|
/*
|
|
|
|
|
* Check commit number again, because the first test has been done
|
|
|
|
|
* without @c->commit_sem, so a commit might have happened.
|
|
|
|
|
*/
|
|
|
|
|
if (ui->del_cmtno != c->cmt_no) {
|
|
|
|
|
up_read(&c->commit_sem);
|
|
|
|
|
return ubifs_jnl_write_inode(c, inode);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = ubifs_tnc_remove_ino(c, inode->i_ino);
|
|
|
|
|
if (err)
|
|
|
|
|
ubifs_ro_mode(c, err);
|
2008-07-23 13:55:55 +00:00
|
|
|
|
else
|
|
|
|
|
ubifs_delete_orphan(c, inode->i_ino);
|
2008-07-22 10:06:20 +00:00
|
|
|
|
up_read(&c->commit_sem);
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
2016-09-14 20:28:51 +00:00
|
|
|
|
/**
|
|
|
|
|
* ubifs_jnl_xrename - cross rename two directory entries.
|
|
|
|
|
* @c: UBIFS file-system description object
|
|
|
|
|
* @fst_dir: parent inode of 1st directory entry to exchange
|
2016-11-11 21:50:29 +00:00
|
|
|
|
* @fst_inode: 1st inode to exchange
|
|
|
|
|
* @fst_nm: name of 1st inode to exchange
|
2016-09-14 20:28:51 +00:00
|
|
|
|
* @snd_dir: parent inode of 2nd directory entry to exchange
|
2016-11-11 21:50:29 +00:00
|
|
|
|
* @snd_inode: 2nd inode to exchange
|
|
|
|
|
* @snd_nm: name of 2nd inode to exchange
|
2016-09-14 20:28:51 +00:00
|
|
|
|
* @sync: non-zero if the write-buffer has to be synchronized
|
|
|
|
|
*
|
|
|
|
|
* This function implements the cross rename operation which may involve
|
|
|
|
|
* writing 2 inodes and 2 directory entries. It marks the written inodes as clean
|
|
|
|
|
* and returns zero on success. In case of failure, a negative error code is
|
|
|
|
|
* returned.
|
|
|
|
|
*/
|
|
|
|
|
int ubifs_jnl_xrename(struct ubifs_info *c, const struct inode *fst_dir,
|
2016-11-11 21:50:29 +00:00
|
|
|
|
const struct inode *fst_inode,
|
|
|
|
|
const struct fscrypt_name *fst_nm,
|
2016-09-14 20:28:51 +00:00
|
|
|
|
const struct inode *snd_dir,
|
2016-11-11 21:50:29 +00:00
|
|
|
|
const struct inode *snd_inode,
|
|
|
|
|
const struct fscrypt_name *snd_nm, int sync)
|
2016-09-14 20:28:51 +00:00
|
|
|
|
{
|
|
|
|
|
union ubifs_key key;
|
|
|
|
|
struct ubifs_dent_node *dent1, *dent2;
|
|
|
|
|
int err, dlen1, dlen2, lnum, offs, len, plen = UBIFS_INO_NODE_SZ;
|
|
|
|
|
int aligned_dlen1, aligned_dlen2;
|
|
|
|
|
int twoparents = (fst_dir != snd_dir);
|
|
|
|
|
void *p;
|
2018-09-07 12:36:34 +00:00
|
|
|
|
u8 hash_dent1[UBIFS_HASH_ARR_SZ];
|
|
|
|
|
u8 hash_dent2[UBIFS_HASH_ARR_SZ];
|
|
|
|
|
u8 hash_p1[UBIFS_HASH_ARR_SZ];
|
|
|
|
|
u8 hash_p2[UBIFS_HASH_ARR_SZ];
|
2016-09-14 20:28:51 +00:00
|
|
|
|
|
2018-07-12 11:01:57 +00:00
|
|
|
|
ubifs_assert(c, ubifs_inode(fst_dir)->data_len == 0);
|
|
|
|
|
ubifs_assert(c, ubifs_inode(snd_dir)->data_len == 0);
|
|
|
|
|
ubifs_assert(c, mutex_is_locked(&ubifs_inode(fst_dir)->ui_mutex));
|
|
|
|
|
ubifs_assert(c, mutex_is_locked(&ubifs_inode(snd_dir)->ui_mutex));
|
2016-09-14 20:28:51 +00:00
|
|
|
|
|
2016-11-11 21:50:29 +00:00
|
|
|
|
dlen1 = UBIFS_DENT_NODE_SZ + fname_len(snd_nm) + 1;
|
|
|
|
|
dlen2 = UBIFS_DENT_NODE_SZ + fname_len(fst_nm) + 1;
|
2016-09-14 20:28:51 +00:00
|
|
|
|
aligned_dlen1 = ALIGN(dlen1, 8);
|
|
|
|
|
aligned_dlen2 = ALIGN(dlen2, 8);
|
|
|
|
|
|
|
|
|
|
len = aligned_dlen1 + aligned_dlen2 + ALIGN(plen, 8);
|
|
|
|
|
if (twoparents)
|
|
|
|
|
len += plen;
|
|
|
|
|
|
ubifs: Add authentication nodes to journal
Nodes that are written to flash can only be authenticated through the
index after the next commit. When a journal replay is necessary the
nodes are not yet referenced by the index and thus can't be
authenticated.
This patch overcomes this situation by creating a hash over all nodes
beginning from the commit start node over the reference node(s) and
the buds themselves. From
time to time we insert authentication nodes. Authentication nodes
contain a HMAC from the current hash state, so that they can be
used to authenticate a journal replay up to the point where the
authentication node is. The hash is continued afterwards
so that theoretically we would only have to check the HMAC of
the last authentication node we find.
Overall we get this picture:
,,,,,,,,
,......,...........................................
,. CS , hash1.----. hash2.----.
,. | , . |hmac . |hmac
,. v , . v . v
,.REF#0,-> bud -> bud -> bud.-> auth -> bud -> bud.-> auth ...
,..|...,...........................................
, | ,
, | ,,,,,,,,,,,,,,,
. | hash3,----.
, | , |hmac
, v , v
, REF#1 -> bud -> bud,-> auth ...
,,,|,,,,,,,,,,,,,,,,,,
v
REF#2 -> ...
|
V
...
Note how hash3 covers CS, REF#0 and REF#1 so that it is not possible to
exchange or skip any reference nodes. Unlike the picture suggests the
auth nodes themselves are not hashed.
With this it is possible for an offline attacker to cut each journal
head or to drop the last reference node(s), but not to skip any journal
heads or to reorder any operations.
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Signed-off-by: Richard Weinberger <richard@nod.at>
2018-09-07 12:36:36 +00:00
|
|
|
|
len += ubifs_auth_node_sz(c);
|
|
|
|
|
|
2017-06-16 14:21:44 +00:00
|
|
|
|
dent1 = kzalloc(len, GFP_NOFS);
|
2016-09-14 20:28:51 +00:00
|
|
|
|
if (!dent1)
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
|
|
/* Make reservation before allocating sequence numbers */
|
|
|
|
|
err = make_reservation(c, BASEHD, len);
|
|
|
|
|
if (err)
|
|
|
|
|
goto out_free;
|
|
|
|
|
|
|
|
|
|
/* Make new dent for 1st entry */
|
|
|
|
|
dent1->ch.node_type = UBIFS_DENT_NODE;
|
2016-11-11 21:50:29 +00:00
|
|
|
|
dent_key_init_flash(c, &dent1->key, snd_dir->i_ino, snd_nm);
|
2016-09-14 20:28:51 +00:00
|
|
|
|
dent1->inum = cpu_to_le64(fst_inode->i_ino);
|
|
|
|
|
dent1->type = get_dent_type(fst_inode->i_mode);
|
2016-11-11 21:50:29 +00:00
|
|
|
|
dent1->nlen = cpu_to_le16(fname_len(snd_nm));
|
|
|
|
|
memcpy(dent1->name, fname_name(snd_nm), fname_len(snd_nm));
|
|
|
|
|
dent1->name[fname_len(snd_nm)] = '\0';
|
2017-06-26 11:49:04 +00:00
|
|
|
|
set_dent_cookie(c, dent1);
|
2016-09-14 20:28:51 +00:00
|
|
|
|
zero_dent_node_unused(dent1);
|
|
|
|
|
ubifs_prep_grp_node(c, dent1, dlen1, 0);
|
2018-09-07 12:36:34 +00:00
|
|
|
|
err = ubifs_node_calc_hash(c, dent1, hash_dent1);
|
|
|
|
|
if (err)
|
|
|
|
|
goto out_release;
|
2016-09-14 20:28:51 +00:00
|
|
|
|
|
|
|
|
|
/* Make new dent for 2nd entry */
|
|
|
|
|
dent2 = (void *)dent1 + aligned_dlen1;
|
|
|
|
|
dent2->ch.node_type = UBIFS_DENT_NODE;
|
2016-11-11 21:50:29 +00:00
|
|
|
|
dent_key_init_flash(c, &dent2->key, fst_dir->i_ino, fst_nm);
|
2016-09-14 20:28:51 +00:00
|
|
|
|
dent2->inum = cpu_to_le64(snd_inode->i_ino);
|
|
|
|
|
dent2->type = get_dent_type(snd_inode->i_mode);
|
2016-11-11 21:50:29 +00:00
|
|
|
|
dent2->nlen = cpu_to_le16(fname_len(fst_nm));
|
|
|
|
|
memcpy(dent2->name, fname_name(fst_nm), fname_len(fst_nm));
|
|
|
|
|
dent2->name[fname_len(fst_nm)] = '\0';
|
2017-06-26 11:49:04 +00:00
|
|
|
|
set_dent_cookie(c, dent2);
|
2016-09-14 20:28:51 +00:00
|
|
|
|
zero_dent_node_unused(dent2);
|
|
|
|
|
ubifs_prep_grp_node(c, dent2, dlen2, 0);
|
2018-09-07 12:36:34 +00:00
|
|
|
|
err = ubifs_node_calc_hash(c, dent2, hash_dent2);
|
|
|
|
|
if (err)
|
|
|
|
|
goto out_release;
|
2016-09-14 20:28:51 +00:00
|
|
|
|
|
|
|
|
|
p = (void *)dent2 + aligned_dlen2;
|
2018-09-07 12:36:34 +00:00
|
|
|
|
if (!twoparents) {
|
2016-09-14 20:28:51 +00:00
|
|
|
|
pack_inode(c, p, fst_dir, 1);
|
2018-09-07 12:36:34 +00:00
|
|
|
|
err = ubifs_node_calc_hash(c, p, hash_p1);
|
|
|
|
|
if (err)
|
|
|
|
|
goto out_release;
|
|
|
|
|
} else {
|
2016-09-14 20:28:51 +00:00
|
|
|
|
pack_inode(c, p, fst_dir, 0);
|
2018-09-07 12:36:34 +00:00
|
|
|
|
err = ubifs_node_calc_hash(c, p, hash_p1);
|
|
|
|
|
if (err)
|
|
|
|
|
goto out_release;
|
2016-09-14 20:28:51 +00:00
|
|
|
|
p += ALIGN(plen, 8);
|
|
|
|
|
pack_inode(c, p, snd_dir, 1);
|
2018-09-07 12:36:34 +00:00
|
|
|
|
err = ubifs_node_calc_hash(c, p, hash_p2);
|
|
|
|
|
if (err)
|
|
|
|
|
goto out_release;
|
2016-09-14 20:28:51 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = write_head(c, BASEHD, dent1, len, &lnum, &offs, sync);
|
|
|
|
|
if (err)
|
|
|
|
|
goto out_release;
|
|
|
|
|
if (!sync) {
|
|
|
|
|
struct ubifs_wbuf *wbuf = &c->jheads[BASEHD].wbuf;
|
|
|
|
|
|
|
|
|
|
ubifs_wbuf_add_ino_nolock(wbuf, fst_dir->i_ino);
|
|
|
|
|
ubifs_wbuf_add_ino_nolock(wbuf, snd_dir->i_ino);
|
|
|
|
|
}
|
|
|
|
|
release_head(c, BASEHD);
|
|
|
|
|
|
ubifs: Add authentication nodes to journal
Nodes that are written to flash can only be authenticated through the
index after the next commit. When a journal replay is necessary the
nodes are not yet referenced by the index and thus can't be
authenticated.
This patch overcomes this situation by creating a hash over all nodes
beginning from the commit start node over the reference node(s) and
the buds themselves. From
time to time we insert authentication nodes. Authentication nodes
contain a HMAC from the current hash state, so that they can be
used to authenticate a journal replay up to the point where the
authentication node is. The hash is continued afterwards
so that theoretically we would only have to check the HMAC of
the last authentication node we find.
Overall we get this picture:
,,,,,,,,
,......,...........................................
,. CS , hash1.----. hash2.----.
,. | , . |hmac . |hmac
,. v , . v . v
,.REF#0,-> bud -> bud -> bud.-> auth -> bud -> bud.-> auth ...
,..|...,...........................................
, | ,
, | ,,,,,,,,,,,,,,,
. | hash3,----.
, | , |hmac
, v , v
, REF#1 -> bud -> bud,-> auth ...
,,,|,,,,,,,,,,,,,,,,,,
v
REF#2 -> ...
|
V
...
Note how hash3 covers CS, REF#0 and REF#1 so that it is not possible to
exchange or skip any reference nodes. Unlike the picture suggests the
auth nodes themselves are not hashed.
With this it is possible for an offline attacker to cut each journal
head or to drop the last reference node(s), but not to skip any journal
heads or to reorder any operations.
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Signed-off-by: Richard Weinberger <richard@nod.at>
2018-09-07 12:36:36 +00:00
|
|
|
|
ubifs_add_auth_dirt(c, lnum);
|
|
|
|
|
|
2016-11-11 21:50:29 +00:00
|
|
|
|
dent_key_init(c, &key, snd_dir->i_ino, snd_nm);
|
2018-09-07 12:36:34 +00:00
|
|
|
|
err = ubifs_tnc_add_nm(c, &key, lnum, offs, dlen1, hash_dent1, snd_nm);
|
2016-09-14 20:28:51 +00:00
|
|
|
|
if (err)
|
|
|
|
|
goto out_ro;
|
|
|
|
|
|
|
|
|
|
offs += aligned_dlen1;
|
2016-11-11 21:50:29 +00:00
|
|
|
|
dent_key_init(c, &key, fst_dir->i_ino, fst_nm);
|
2018-09-07 12:36:34 +00:00
|
|
|
|
err = ubifs_tnc_add_nm(c, &key, lnum, offs, dlen2, hash_dent2, fst_nm);
|
2016-09-14 20:28:51 +00:00
|
|
|
|
if (err)
|
|
|
|
|
goto out_ro;
|
|
|
|
|
|
|
|
|
|
offs += aligned_dlen2;
|
|
|
|
|
|
|
|
|
|
ino_key_init(c, &key, fst_dir->i_ino);
|
2018-09-07 12:36:34 +00:00
|
|
|
|
err = ubifs_tnc_add(c, &key, lnum, offs, plen, hash_p1);
|
2016-09-14 20:28:51 +00:00
|
|
|
|
if (err)
|
|
|
|
|
goto out_ro;
|
|
|
|
|
|
|
|
|
|
if (twoparents) {
|
|
|
|
|
offs += ALIGN(plen, 8);
|
|
|
|
|
ino_key_init(c, &key, snd_dir->i_ino);
|
2018-09-07 12:36:34 +00:00
|
|
|
|
err = ubifs_tnc_add(c, &key, lnum, offs, plen, hash_p2);
|
2016-09-14 20:28:51 +00:00
|
|
|
|
if (err)
|
|
|
|
|
goto out_ro;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
finish_reservation(c);
|
|
|
|
|
|
|
|
|
|
mark_inode_clean(c, ubifs_inode(fst_dir));
|
|
|
|
|
if (twoparents)
|
|
|
|
|
mark_inode_clean(c, ubifs_inode(snd_dir));
|
|
|
|
|
kfree(dent1);
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
out_release:
|
|
|
|
|
release_head(c, BASEHD);
|
|
|
|
|
out_ro:
|
|
|
|
|
ubifs_ro_mode(c, err);
|
|
|
|
|
finish_reservation(c);
|
|
|
|
|
out_free:
|
|
|
|
|
kfree(dent1);
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
2008-07-14 16:08:37 +00:00
|
|
|
|
/**
|
|
|
|
|
* ubifs_jnl_rename - rename a directory entry.
|
|
|
|
|
* @c: UBIFS file-system description object
|
|
|
|
|
* @old_dir: parent inode of directory entry to rename
|
2022-06-02 06:55:56 +00:00
|
|
|
|
* @old_inode: directory entry's inode to rename
|
|
|
|
|
* @old_nm: name of the old directory entry to rename
|
2008-07-14 16:08:37 +00:00
|
|
|
|
* @new_dir: parent inode of directory entry to rename
|
2022-06-02 06:55:56 +00:00
|
|
|
|
* @new_inode: new directory entry's inode (or directory entry's inode to
|
|
|
|
|
* replace)
|
|
|
|
|
* @new_nm: new name of the new directory entry
|
|
|
|
|
* @whiteout: whiteout inode
|
2008-07-14 16:08:37 +00:00
|
|
|
|
* @sync: non-zero if the write-buffer has to be synchronized
|
|
|
|
|
*
|
|
|
|
|
* This function implements the re-name operation which may involve writing up
|
ubifs: Rename whiteout atomically
Currently, rename whiteout has 3 steps:
1. create tmpfile(which associates old dentry to tmpfile inode) for
whiteout, and store tmpfile to disk
2. link whiteout, associate whiteout inode to old dentry agagin and
store old dentry, old inode, new dentry on disk
3. writeback dirty whiteout inode to disk
Suddenly power-cut or error occurring(eg. ENOSPC returned by budget,
memory allocation failure) during above steps may cause kinds of problems:
Problem 1: ENOSPC returned by whiteout space budget (before step 2),
old dentry will disappear after rename syscall, whiteout file
cannot be found either.
ls dir // we get file, whiteout
rename(dir/file, dir/whiteout, REANME_WHITEOUT)
ENOSPC = ubifs_budget_space(&wht_req) // return
ls dir // empty (no file, no whiteout)
Problem 2: Power-cut happens before step 3, whiteout inode with 'nlink=1'
is not stored on disk, whiteout dentry(old dentry) is written
on disk, whiteout file is lost on next mount (We get "dead
directory entry" after executing 'ls -l' on whiteout file).
Now, we use following 3 steps to finish rename whiteout:
1. create an in-mem inode with 'nlink = 1' as whiteout
2. ubifs_jnl_rename (Write on disk to finish associating old dentry to
whiteout inode, associating new dentry with old inode)
3. iput(whiteout)
Rely writing in-mem inode on disk by ubifs_jnl_rename() to finish rename
whiteout, which avoids middle disk state caused by suddenly power-cut
and error occurring.
Fixes: 9e0a1fff8db56ea ("ubifs: Implement RENAME_WHITEOUT")
Signed-off-by: Zhihao Cheng <chengzhihao1@huawei.com>
Signed-off-by: Richard Weinberger <richard@nod.at>
2021-12-27 03:22:36 +00:00
|
|
|
|
* to 4 inodes(new inode, whiteout inode, old and new parent directory inodes)
|
|
|
|
|
* and 2 directory entries. It marks the written inodes as clean and returns
|
|
|
|
|
* zero on success. In case of failure, a negative error code is returned.
|
2008-07-14 16:08:37 +00:00
|
|
|
|
*/
|
|
|
|
|
int ubifs_jnl_rename(struct ubifs_info *c, const struct inode *old_dir,
|
2016-11-11 21:50:29 +00:00
|
|
|
|
const struct inode *old_inode,
|
|
|
|
|
const struct fscrypt_name *old_nm,
|
2008-07-14 16:08:37 +00:00
|
|
|
|
const struct inode *new_dir,
|
2016-11-11 21:50:29 +00:00
|
|
|
|
const struct inode *new_inode,
|
|
|
|
|
const struct fscrypt_name *new_nm,
|
2016-09-14 20:28:50 +00:00
|
|
|
|
const struct inode *whiteout, int sync)
|
2008-07-14 16:08:37 +00:00
|
|
|
|
{
|
|
|
|
|
void *p;
|
|
|
|
|
union ubifs_key key;
|
|
|
|
|
struct ubifs_dent_node *dent, *dent2;
|
ubifs: Rename whiteout atomically
Currently, rename whiteout has 3 steps:
1. create tmpfile(which associates old dentry to tmpfile inode) for
whiteout, and store tmpfile to disk
2. link whiteout, associate whiteout inode to old dentry agagin and
store old dentry, old inode, new dentry on disk
3. writeback dirty whiteout inode to disk
Suddenly power-cut or error occurring(eg. ENOSPC returned by budget,
memory allocation failure) during above steps may cause kinds of problems:
Problem 1: ENOSPC returned by whiteout space budget (before step 2),
old dentry will disappear after rename syscall, whiteout file
cannot be found either.
ls dir // we get file, whiteout
rename(dir/file, dir/whiteout, REANME_WHITEOUT)
ENOSPC = ubifs_budget_space(&wht_req) // return
ls dir // empty (no file, no whiteout)
Problem 2: Power-cut happens before step 3, whiteout inode with 'nlink=1'
is not stored on disk, whiteout dentry(old dentry) is written
on disk, whiteout file is lost on next mount (We get "dead
directory entry" after executing 'ls -l' on whiteout file).
Now, we use following 3 steps to finish rename whiteout:
1. create an in-mem inode with 'nlink = 1' as whiteout
2. ubifs_jnl_rename (Write on disk to finish associating old dentry to
whiteout inode, associating new dentry with old inode)
3. iput(whiteout)
Rely writing in-mem inode on disk by ubifs_jnl_rename() to finish rename
whiteout, which avoids middle disk state caused by suddenly power-cut
and error occurring.
Fixes: 9e0a1fff8db56ea ("ubifs: Implement RENAME_WHITEOUT")
Signed-off-by: Zhihao Cheng <chengzhihao1@huawei.com>
Signed-off-by: Richard Weinberger <richard@nod.at>
2021-12-27 03:22:36 +00:00
|
|
|
|
int err, dlen1, dlen2, ilen, wlen, lnum, offs, len, orphan_added = 0;
|
2008-07-14 16:08:37 +00:00
|
|
|
|
int aligned_dlen1, aligned_dlen2, plen = UBIFS_INO_NODE_SZ;
|
|
|
|
|
int last_reference = !!(new_inode && new_inode->i_nlink == 0);
|
|
|
|
|
int move = (old_dir != new_dir);
|
ubifs: Rename whiteout atomically
Currently, rename whiteout has 3 steps:
1. create tmpfile(which associates old dentry to tmpfile inode) for
whiteout, and store tmpfile to disk
2. link whiteout, associate whiteout inode to old dentry agagin and
store old dentry, old inode, new dentry on disk
3. writeback dirty whiteout inode to disk
Suddenly power-cut or error occurring(eg. ENOSPC returned by budget,
memory allocation failure) during above steps may cause kinds of problems:
Problem 1: ENOSPC returned by whiteout space budget (before step 2),
old dentry will disappear after rename syscall, whiteout file
cannot be found either.
ls dir // we get file, whiteout
rename(dir/file, dir/whiteout, REANME_WHITEOUT)
ENOSPC = ubifs_budget_space(&wht_req) // return
ls dir // empty (no file, no whiteout)
Problem 2: Power-cut happens before step 3, whiteout inode with 'nlink=1'
is not stored on disk, whiteout dentry(old dentry) is written
on disk, whiteout file is lost on next mount (We get "dead
directory entry" after executing 'ls -l' on whiteout file).
Now, we use following 3 steps to finish rename whiteout:
1. create an in-mem inode with 'nlink = 1' as whiteout
2. ubifs_jnl_rename (Write on disk to finish associating old dentry to
whiteout inode, associating new dentry with old inode)
3. iput(whiteout)
Rely writing in-mem inode on disk by ubifs_jnl_rename() to finish rename
whiteout, which avoids middle disk state caused by suddenly power-cut
and error occurring.
Fixes: 9e0a1fff8db56ea ("ubifs: Implement RENAME_WHITEOUT")
Signed-off-by: Zhihao Cheng <chengzhihao1@huawei.com>
Signed-off-by: Richard Weinberger <richard@nod.at>
2021-12-27 03:22:36 +00:00
|
|
|
|
struct ubifs_inode *new_ui, *whiteout_ui;
|
2018-09-07 12:36:34 +00:00
|
|
|
|
u8 hash_old_dir[UBIFS_HASH_ARR_SZ];
|
|
|
|
|
u8 hash_new_dir[UBIFS_HASH_ARR_SZ];
|
|
|
|
|
u8 hash_new_inode[UBIFS_HASH_ARR_SZ];
|
ubifs: Rename whiteout atomically
Currently, rename whiteout has 3 steps:
1. create tmpfile(which associates old dentry to tmpfile inode) for
whiteout, and store tmpfile to disk
2. link whiteout, associate whiteout inode to old dentry agagin and
store old dentry, old inode, new dentry on disk
3. writeback dirty whiteout inode to disk
Suddenly power-cut or error occurring(eg. ENOSPC returned by budget,
memory allocation failure) during above steps may cause kinds of problems:
Problem 1: ENOSPC returned by whiteout space budget (before step 2),
old dentry will disappear after rename syscall, whiteout file
cannot be found either.
ls dir // we get file, whiteout
rename(dir/file, dir/whiteout, REANME_WHITEOUT)
ENOSPC = ubifs_budget_space(&wht_req) // return
ls dir // empty (no file, no whiteout)
Problem 2: Power-cut happens before step 3, whiteout inode with 'nlink=1'
is not stored on disk, whiteout dentry(old dentry) is written
on disk, whiteout file is lost on next mount (We get "dead
directory entry" after executing 'ls -l' on whiteout file).
Now, we use following 3 steps to finish rename whiteout:
1. create an in-mem inode with 'nlink = 1' as whiteout
2. ubifs_jnl_rename (Write on disk to finish associating old dentry to
whiteout inode, associating new dentry with old inode)
3. iput(whiteout)
Rely writing in-mem inode on disk by ubifs_jnl_rename() to finish rename
whiteout, which avoids middle disk state caused by suddenly power-cut
and error occurring.
Fixes: 9e0a1fff8db56ea ("ubifs: Implement RENAME_WHITEOUT")
Signed-off-by: Zhihao Cheng <chengzhihao1@huawei.com>
Signed-off-by: Richard Weinberger <richard@nod.at>
2021-12-27 03:22:36 +00:00
|
|
|
|
u8 hash_whiteout_inode[UBIFS_HASH_ARR_SZ];
|
2018-09-07 12:36:34 +00:00
|
|
|
|
u8 hash_dent1[UBIFS_HASH_ARR_SZ];
|
|
|
|
|
u8 hash_dent2[UBIFS_HASH_ARR_SZ];
|
2008-07-14 16:08:37 +00:00
|
|
|
|
|
2018-07-12 11:01:57 +00:00
|
|
|
|
ubifs_assert(c, ubifs_inode(old_dir)->data_len == 0);
|
|
|
|
|
ubifs_assert(c, ubifs_inode(new_dir)->data_len == 0);
|
|
|
|
|
ubifs_assert(c, mutex_is_locked(&ubifs_inode(old_dir)->ui_mutex));
|
|
|
|
|
ubifs_assert(c, mutex_is_locked(&ubifs_inode(new_dir)->ui_mutex));
|
2008-07-14 16:08:37 +00:00
|
|
|
|
|
2016-11-11 21:50:29 +00:00
|
|
|
|
dlen1 = UBIFS_DENT_NODE_SZ + fname_len(new_nm) + 1;
|
|
|
|
|
dlen2 = UBIFS_DENT_NODE_SZ + fname_len(old_nm) + 1;
|
2008-07-14 16:08:37 +00:00
|
|
|
|
if (new_inode) {
|
|
|
|
|
new_ui = ubifs_inode(new_inode);
|
2018-07-12 11:01:57 +00:00
|
|
|
|
ubifs_assert(c, mutex_is_locked(&new_ui->ui_mutex));
|
2008-07-14 16:08:37 +00:00
|
|
|
|
ilen = UBIFS_INO_NODE_SZ;
|
|
|
|
|
if (!last_reference)
|
|
|
|
|
ilen += new_ui->data_len;
|
|
|
|
|
} else
|
|
|
|
|
ilen = 0;
|
|
|
|
|
|
ubifs: Rename whiteout atomically
Currently, rename whiteout has 3 steps:
1. create tmpfile(which associates old dentry to tmpfile inode) for
whiteout, and store tmpfile to disk
2. link whiteout, associate whiteout inode to old dentry agagin and
store old dentry, old inode, new dentry on disk
3. writeback dirty whiteout inode to disk
Suddenly power-cut or error occurring(eg. ENOSPC returned by budget,
memory allocation failure) during above steps may cause kinds of problems:
Problem 1: ENOSPC returned by whiteout space budget (before step 2),
old dentry will disappear after rename syscall, whiteout file
cannot be found either.
ls dir // we get file, whiteout
rename(dir/file, dir/whiteout, REANME_WHITEOUT)
ENOSPC = ubifs_budget_space(&wht_req) // return
ls dir // empty (no file, no whiteout)
Problem 2: Power-cut happens before step 3, whiteout inode with 'nlink=1'
is not stored on disk, whiteout dentry(old dentry) is written
on disk, whiteout file is lost on next mount (We get "dead
directory entry" after executing 'ls -l' on whiteout file).
Now, we use following 3 steps to finish rename whiteout:
1. create an in-mem inode with 'nlink = 1' as whiteout
2. ubifs_jnl_rename (Write on disk to finish associating old dentry to
whiteout inode, associating new dentry with old inode)
3. iput(whiteout)
Rely writing in-mem inode on disk by ubifs_jnl_rename() to finish rename
whiteout, which avoids middle disk state caused by suddenly power-cut
and error occurring.
Fixes: 9e0a1fff8db56ea ("ubifs: Implement RENAME_WHITEOUT")
Signed-off-by: Zhihao Cheng <chengzhihao1@huawei.com>
Signed-off-by: Richard Weinberger <richard@nod.at>
2021-12-27 03:22:36 +00:00
|
|
|
|
if (whiteout) {
|
|
|
|
|
whiteout_ui = ubifs_inode(whiteout);
|
|
|
|
|
ubifs_assert(c, mutex_is_locked(&whiteout_ui->ui_mutex));
|
|
|
|
|
ubifs_assert(c, whiteout->i_nlink == 1);
|
|
|
|
|
ubifs_assert(c, !whiteout_ui->dirty);
|
|
|
|
|
wlen = UBIFS_INO_NODE_SZ;
|
|
|
|
|
wlen += whiteout_ui->data_len;
|
|
|
|
|
} else
|
|
|
|
|
wlen = 0;
|
|
|
|
|
|
2008-07-14 16:08:37 +00:00
|
|
|
|
aligned_dlen1 = ALIGN(dlen1, 8);
|
|
|
|
|
aligned_dlen2 = ALIGN(dlen2, 8);
|
ubifs: Rename whiteout atomically
Currently, rename whiteout has 3 steps:
1. create tmpfile(which associates old dentry to tmpfile inode) for
whiteout, and store tmpfile to disk
2. link whiteout, associate whiteout inode to old dentry agagin and
store old dentry, old inode, new dentry on disk
3. writeback dirty whiteout inode to disk
Suddenly power-cut or error occurring(eg. ENOSPC returned by budget,
memory allocation failure) during above steps may cause kinds of problems:
Problem 1: ENOSPC returned by whiteout space budget (before step 2),
old dentry will disappear after rename syscall, whiteout file
cannot be found either.
ls dir // we get file, whiteout
rename(dir/file, dir/whiteout, REANME_WHITEOUT)
ENOSPC = ubifs_budget_space(&wht_req) // return
ls dir // empty (no file, no whiteout)
Problem 2: Power-cut happens before step 3, whiteout inode with 'nlink=1'
is not stored on disk, whiteout dentry(old dentry) is written
on disk, whiteout file is lost on next mount (We get "dead
directory entry" after executing 'ls -l' on whiteout file).
Now, we use following 3 steps to finish rename whiteout:
1. create an in-mem inode with 'nlink = 1' as whiteout
2. ubifs_jnl_rename (Write on disk to finish associating old dentry to
whiteout inode, associating new dentry with old inode)
3. iput(whiteout)
Rely writing in-mem inode on disk by ubifs_jnl_rename() to finish rename
whiteout, which avoids middle disk state caused by suddenly power-cut
and error occurring.
Fixes: 9e0a1fff8db56ea ("ubifs: Implement RENAME_WHITEOUT")
Signed-off-by: Zhihao Cheng <chengzhihao1@huawei.com>
Signed-off-by: Richard Weinberger <richard@nod.at>
2021-12-27 03:22:36 +00:00
|
|
|
|
len = aligned_dlen1 + aligned_dlen2 + ALIGN(ilen, 8) +
|
|
|
|
|
ALIGN(wlen, 8) + ALIGN(plen, 8);
|
2016-09-14 20:28:52 +00:00
|
|
|
|
if (move)
|
2008-07-14 16:08:37 +00:00
|
|
|
|
len += plen;
|
ubifs: Add authentication nodes to journal
Nodes that are written to flash can only be authenticated through the
index after the next commit. When a journal replay is necessary the
nodes are not yet referenced by the index and thus can't be
authenticated.
This patch overcomes this situation by creating a hash over all nodes
beginning from the commit start node over the reference node(s) and
the buds themselves. From
time to time we insert authentication nodes. Authentication nodes
contain a HMAC from the current hash state, so that they can be
used to authenticate a journal replay up to the point where the
authentication node is. The hash is continued afterwards
so that theoretically we would only have to check the HMAC of
the last authentication node we find.
Overall we get this picture:
,,,,,,,,
,......,...........................................
,. CS , hash1.----. hash2.----.
,. | , . |hmac . |hmac
,. v , . v . v
,.REF#0,-> bud -> bud -> bud.-> auth -> bud -> bud.-> auth ...
,..|...,...........................................
, | ,
, | ,,,,,,,,,,,,,,,
. | hash3,----.
, | , |hmac
, v , v
, REF#1 -> bud -> bud,-> auth ...
,,,|,,,,,,,,,,,,,,,,,,
v
REF#2 -> ...
|
V
...
Note how hash3 covers CS, REF#0 and REF#1 so that it is not possible to
exchange or skip any reference nodes. Unlike the picture suggests the
auth nodes themselves are not hashed.
With this it is possible for an offline attacker to cut each journal
head or to drop the last reference node(s), but not to skip any journal
heads or to reorder any operations.
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Signed-off-by: Richard Weinberger <richard@nod.at>
2018-09-07 12:36:36 +00:00
|
|
|
|
|
|
|
|
|
len += ubifs_auth_node_sz(c);
|
|
|
|
|
|
2017-06-16 14:21:44 +00:00
|
|
|
|
dent = kzalloc(len, GFP_NOFS);
|
2008-07-14 16:08:37 +00:00
|
|
|
|
if (!dent)
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
|
|
/* Make reservation before allocating sequence numbers */
|
|
|
|
|
err = make_reservation(c, BASEHD, len);
|
|
|
|
|
if (err)
|
|
|
|
|
goto out_free;
|
|
|
|
|
|
|
|
|
|
/* Make new dent */
|
|
|
|
|
dent->ch.node_type = UBIFS_DENT_NODE;
|
2016-11-11 21:50:29 +00:00
|
|
|
|
dent_key_init_flash(c, &dent->key, new_dir->i_ino, new_nm);
|
2008-07-14 16:08:37 +00:00
|
|
|
|
dent->inum = cpu_to_le64(old_inode->i_ino);
|
|
|
|
|
dent->type = get_dent_type(old_inode->i_mode);
|
2016-11-11 21:50:29 +00:00
|
|
|
|
dent->nlen = cpu_to_le16(fname_len(new_nm));
|
|
|
|
|
memcpy(dent->name, fname_name(new_nm), fname_len(new_nm));
|
|
|
|
|
dent->name[fname_len(new_nm)] = '\0';
|
2016-10-19 13:59:12 +00:00
|
|
|
|
set_dent_cookie(c, dent);
|
2008-07-14 16:08:37 +00:00
|
|
|
|
zero_dent_node_unused(dent);
|
|
|
|
|
ubifs_prep_grp_node(c, dent, dlen1, 0);
|
2018-09-07 12:36:34 +00:00
|
|
|
|
err = ubifs_node_calc_hash(c, dent, hash_dent1);
|
|
|
|
|
if (err)
|
|
|
|
|
goto out_release;
|
2008-07-14 16:08:37 +00:00
|
|
|
|
|
|
|
|
|
dent2 = (void *)dent + aligned_dlen1;
|
|
|
|
|
dent2->ch.node_type = UBIFS_DENT_NODE;
|
2016-11-11 21:50:29 +00:00
|
|
|
|
dent_key_init_flash(c, &dent2->key, old_dir->i_ino, old_nm);
|
2016-09-14 20:28:50 +00:00
|
|
|
|
|
|
|
|
|
if (whiteout) {
|
|
|
|
|
dent2->inum = cpu_to_le64(whiteout->i_ino);
|
|
|
|
|
dent2->type = get_dent_type(whiteout->i_mode);
|
|
|
|
|
} else {
|
|
|
|
|
/* Make deletion dent */
|
|
|
|
|
dent2->inum = 0;
|
|
|
|
|
dent2->type = DT_UNKNOWN;
|
|
|
|
|
}
|
2016-11-11 21:50:29 +00:00
|
|
|
|
dent2->nlen = cpu_to_le16(fname_len(old_nm));
|
|
|
|
|
memcpy(dent2->name, fname_name(old_nm), fname_len(old_nm));
|
|
|
|
|
dent2->name[fname_len(old_nm)] = '\0';
|
2016-10-19 13:59:12 +00:00
|
|
|
|
set_dent_cookie(c, dent2);
|
2008-07-14 16:08:37 +00:00
|
|
|
|
zero_dent_node_unused(dent2);
|
|
|
|
|
ubifs_prep_grp_node(c, dent2, dlen2, 0);
|
2018-09-07 12:36:34 +00:00
|
|
|
|
err = ubifs_node_calc_hash(c, dent2, hash_dent2);
|
|
|
|
|
if (err)
|
|
|
|
|
goto out_release;
|
2008-07-14 16:08:37 +00:00
|
|
|
|
|
|
|
|
|
p = (void *)dent2 + aligned_dlen2;
|
|
|
|
|
if (new_inode) {
|
2008-07-22 09:19:09 +00:00
|
|
|
|
pack_inode(c, p, new_inode, 0);
|
2018-09-07 12:36:34 +00:00
|
|
|
|
err = ubifs_node_calc_hash(c, p, hash_new_inode);
|
|
|
|
|
if (err)
|
|
|
|
|
goto out_release;
|
|
|
|
|
|
2008-07-14 16:08:37 +00:00
|
|
|
|
p += ALIGN(ilen, 8);
|
|
|
|
|
}
|
|
|
|
|
|
ubifs: Rename whiteout atomically
Currently, rename whiteout has 3 steps:
1. create tmpfile(which associates old dentry to tmpfile inode) for
whiteout, and store tmpfile to disk
2. link whiteout, associate whiteout inode to old dentry agagin and
store old dentry, old inode, new dentry on disk
3. writeback dirty whiteout inode to disk
Suddenly power-cut or error occurring(eg. ENOSPC returned by budget,
memory allocation failure) during above steps may cause kinds of problems:
Problem 1: ENOSPC returned by whiteout space budget (before step 2),
old dentry will disappear after rename syscall, whiteout file
cannot be found either.
ls dir // we get file, whiteout
rename(dir/file, dir/whiteout, REANME_WHITEOUT)
ENOSPC = ubifs_budget_space(&wht_req) // return
ls dir // empty (no file, no whiteout)
Problem 2: Power-cut happens before step 3, whiteout inode with 'nlink=1'
is not stored on disk, whiteout dentry(old dentry) is written
on disk, whiteout file is lost on next mount (We get "dead
directory entry" after executing 'ls -l' on whiteout file).
Now, we use following 3 steps to finish rename whiteout:
1. create an in-mem inode with 'nlink = 1' as whiteout
2. ubifs_jnl_rename (Write on disk to finish associating old dentry to
whiteout inode, associating new dentry with old inode)
3. iput(whiteout)
Rely writing in-mem inode on disk by ubifs_jnl_rename() to finish rename
whiteout, which avoids middle disk state caused by suddenly power-cut
and error occurring.
Fixes: 9e0a1fff8db56ea ("ubifs: Implement RENAME_WHITEOUT")
Signed-off-by: Zhihao Cheng <chengzhihao1@huawei.com>
Signed-off-by: Richard Weinberger <richard@nod.at>
2021-12-27 03:22:36 +00:00
|
|
|
|
if (whiteout) {
|
|
|
|
|
pack_inode(c, p, whiteout, 0);
|
|
|
|
|
err = ubifs_node_calc_hash(c, p, hash_whiteout_inode);
|
|
|
|
|
if (err)
|
|
|
|
|
goto out_release;
|
|
|
|
|
|
|
|
|
|
p += ALIGN(wlen, 8);
|
|
|
|
|
}
|
|
|
|
|
|
2018-09-07 12:36:34 +00:00
|
|
|
|
if (!move) {
|
2008-07-22 09:19:09 +00:00
|
|
|
|
pack_inode(c, p, old_dir, 1);
|
2018-09-07 12:36:34 +00:00
|
|
|
|
err = ubifs_node_calc_hash(c, p, hash_old_dir);
|
|
|
|
|
if (err)
|
|
|
|
|
goto out_release;
|
|
|
|
|
} else {
|
2008-07-22 09:19:09 +00:00
|
|
|
|
pack_inode(c, p, old_dir, 0);
|
2018-09-07 12:36:34 +00:00
|
|
|
|
err = ubifs_node_calc_hash(c, p, hash_old_dir);
|
|
|
|
|
if (err)
|
|
|
|
|
goto out_release;
|
|
|
|
|
|
2008-07-14 16:08:37 +00:00
|
|
|
|
p += ALIGN(plen, 8);
|
2008-07-22 09:19:09 +00:00
|
|
|
|
pack_inode(c, p, new_dir, 1);
|
2018-09-07 12:36:34 +00:00
|
|
|
|
err = ubifs_node_calc_hash(c, p, hash_new_dir);
|
|
|
|
|
if (err)
|
|
|
|
|
goto out_release;
|
2008-07-14 16:08:37 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (last_reference) {
|
|
|
|
|
err = ubifs_add_orphan(c, new_inode->i_ino);
|
|
|
|
|
if (err) {
|
|
|
|
|
release_head(c, BASEHD);
|
|
|
|
|
goto out_finish;
|
|
|
|
|
}
|
2008-07-22 10:06:20 +00:00
|
|
|
|
new_ui->del_cmtno = c->cmt_no;
|
2020-07-07 12:51:40 +00:00
|
|
|
|
orphan_added = 1;
|
2008-07-14 16:08:37 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = write_head(c, BASEHD, dent, len, &lnum, &offs, sync);
|
|
|
|
|
if (err)
|
|
|
|
|
goto out_release;
|
|
|
|
|
if (!sync) {
|
|
|
|
|
struct ubifs_wbuf *wbuf = &c->jheads[BASEHD].wbuf;
|
|
|
|
|
|
|
|
|
|
ubifs_wbuf_add_ino_nolock(wbuf, new_dir->i_ino);
|
|
|
|
|
ubifs_wbuf_add_ino_nolock(wbuf, old_dir->i_ino);
|
|
|
|
|
if (new_inode)
|
|
|
|
|
ubifs_wbuf_add_ino_nolock(&c->jheads[BASEHD].wbuf,
|
|
|
|
|
new_inode->i_ino);
|
ubifs: Rename whiteout atomically
Currently, rename whiteout has 3 steps:
1. create tmpfile(which associates old dentry to tmpfile inode) for
whiteout, and store tmpfile to disk
2. link whiteout, associate whiteout inode to old dentry agagin and
store old dentry, old inode, new dentry on disk
3. writeback dirty whiteout inode to disk
Suddenly power-cut or error occurring(eg. ENOSPC returned by budget,
memory allocation failure) during above steps may cause kinds of problems:
Problem 1: ENOSPC returned by whiteout space budget (before step 2),
old dentry will disappear after rename syscall, whiteout file
cannot be found either.
ls dir // we get file, whiteout
rename(dir/file, dir/whiteout, REANME_WHITEOUT)
ENOSPC = ubifs_budget_space(&wht_req) // return
ls dir // empty (no file, no whiteout)
Problem 2: Power-cut happens before step 3, whiteout inode with 'nlink=1'
is not stored on disk, whiteout dentry(old dentry) is written
on disk, whiteout file is lost on next mount (We get "dead
directory entry" after executing 'ls -l' on whiteout file).
Now, we use following 3 steps to finish rename whiteout:
1. create an in-mem inode with 'nlink = 1' as whiteout
2. ubifs_jnl_rename (Write on disk to finish associating old dentry to
whiteout inode, associating new dentry with old inode)
3. iput(whiteout)
Rely writing in-mem inode on disk by ubifs_jnl_rename() to finish rename
whiteout, which avoids middle disk state caused by suddenly power-cut
and error occurring.
Fixes: 9e0a1fff8db56ea ("ubifs: Implement RENAME_WHITEOUT")
Signed-off-by: Zhihao Cheng <chengzhihao1@huawei.com>
Signed-off-by: Richard Weinberger <richard@nod.at>
2021-12-27 03:22:36 +00:00
|
|
|
|
if (whiteout)
|
|
|
|
|
ubifs_wbuf_add_ino_nolock(&c->jheads[BASEHD].wbuf,
|
|
|
|
|
whiteout->i_ino);
|
2008-07-14 16:08:37 +00:00
|
|
|
|
}
|
|
|
|
|
release_head(c, BASEHD);
|
|
|
|
|
|
ubifs: Add authentication nodes to journal
Nodes that are written to flash can only be authenticated through the
index after the next commit. When a journal replay is necessary the
nodes are not yet referenced by the index and thus can't be
authenticated.
This patch overcomes this situation by creating a hash over all nodes
beginning from the commit start node over the reference node(s) and
the buds themselves. From
time to time we insert authentication nodes. Authentication nodes
contain a HMAC from the current hash state, so that they can be
used to authenticate a journal replay up to the point where the
authentication node is. The hash is continued afterwards
so that theoretically we would only have to check the HMAC of
the last authentication node we find.
Overall we get this picture:
,,,,,,,,
,......,...........................................
,. CS , hash1.----. hash2.----.
,. | , . |hmac . |hmac
,. v , . v . v
,.REF#0,-> bud -> bud -> bud.-> auth -> bud -> bud.-> auth ...
,..|...,...........................................
, | ,
, | ,,,,,,,,,,,,,,,
. | hash3,----.
, | , |hmac
, v , v
, REF#1 -> bud -> bud,-> auth ...
,,,|,,,,,,,,,,,,,,,,,,
v
REF#2 -> ...
|
V
...
Note how hash3 covers CS, REF#0 and REF#1 so that it is not possible to
exchange or skip any reference nodes. Unlike the picture suggests the
auth nodes themselves are not hashed.
With this it is possible for an offline attacker to cut each journal
head or to drop the last reference node(s), but not to skip any journal
heads or to reorder any operations.
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Signed-off-by: Richard Weinberger <richard@nod.at>
2018-09-07 12:36:36 +00:00
|
|
|
|
ubifs_add_auth_dirt(c, lnum);
|
|
|
|
|
|
2016-11-11 21:50:29 +00:00
|
|
|
|
dent_key_init(c, &key, new_dir->i_ino, new_nm);
|
2018-09-07 12:36:34 +00:00
|
|
|
|
err = ubifs_tnc_add_nm(c, &key, lnum, offs, dlen1, hash_dent1, new_nm);
|
2008-07-14 16:08:37 +00:00
|
|
|
|
if (err)
|
|
|
|
|
goto out_ro;
|
|
|
|
|
|
2016-09-14 20:28:50 +00:00
|
|
|
|
offs += aligned_dlen1;
|
|
|
|
|
if (whiteout) {
|
2016-11-11 21:50:29 +00:00
|
|
|
|
dent_key_init(c, &key, old_dir->i_ino, old_nm);
|
2018-09-07 12:36:34 +00:00
|
|
|
|
err = ubifs_tnc_add_nm(c, &key, lnum, offs, dlen2, hash_dent2, old_nm);
|
2016-09-14 20:28:50 +00:00
|
|
|
|
if (err)
|
|
|
|
|
goto out_ro;
|
|
|
|
|
} else {
|
|
|
|
|
err = ubifs_add_dirt(c, lnum, dlen2);
|
|
|
|
|
if (err)
|
|
|
|
|
goto out_ro;
|
|
|
|
|
|
2016-11-11 21:50:29 +00:00
|
|
|
|
dent_key_init(c, &key, old_dir->i_ino, old_nm);
|
|
|
|
|
err = ubifs_tnc_remove_nm(c, &key, old_nm);
|
2016-09-14 20:28:50 +00:00
|
|
|
|
if (err)
|
|
|
|
|
goto out_ro;
|
|
|
|
|
}
|
2008-07-14 16:08:37 +00:00
|
|
|
|
|
2016-09-14 20:28:50 +00:00
|
|
|
|
offs += aligned_dlen2;
|
2008-07-14 16:08:37 +00:00
|
|
|
|
if (new_inode) {
|
|
|
|
|
ino_key_init(c, &key, new_inode->i_ino);
|
2018-09-07 12:36:34 +00:00
|
|
|
|
err = ubifs_tnc_add(c, &key, lnum, offs, ilen, hash_new_inode);
|
2008-07-14 16:08:37 +00:00
|
|
|
|
if (err)
|
|
|
|
|
goto out_ro;
|
|
|
|
|
offs += ALIGN(ilen, 8);
|
|
|
|
|
}
|
|
|
|
|
|
ubifs: Rename whiteout atomically
Currently, rename whiteout has 3 steps:
1. create tmpfile(which associates old dentry to tmpfile inode) for
whiteout, and store tmpfile to disk
2. link whiteout, associate whiteout inode to old dentry agagin and
store old dentry, old inode, new dentry on disk
3. writeback dirty whiteout inode to disk
Suddenly power-cut or error occurring(eg. ENOSPC returned by budget,
memory allocation failure) during above steps may cause kinds of problems:
Problem 1: ENOSPC returned by whiteout space budget (before step 2),
old dentry will disappear after rename syscall, whiteout file
cannot be found either.
ls dir // we get file, whiteout
rename(dir/file, dir/whiteout, REANME_WHITEOUT)
ENOSPC = ubifs_budget_space(&wht_req) // return
ls dir // empty (no file, no whiteout)
Problem 2: Power-cut happens before step 3, whiteout inode with 'nlink=1'
is not stored on disk, whiteout dentry(old dentry) is written
on disk, whiteout file is lost on next mount (We get "dead
directory entry" after executing 'ls -l' on whiteout file).
Now, we use following 3 steps to finish rename whiteout:
1. create an in-mem inode with 'nlink = 1' as whiteout
2. ubifs_jnl_rename (Write on disk to finish associating old dentry to
whiteout inode, associating new dentry with old inode)
3. iput(whiteout)
Rely writing in-mem inode on disk by ubifs_jnl_rename() to finish rename
whiteout, which avoids middle disk state caused by suddenly power-cut
and error occurring.
Fixes: 9e0a1fff8db56ea ("ubifs: Implement RENAME_WHITEOUT")
Signed-off-by: Zhihao Cheng <chengzhihao1@huawei.com>
Signed-off-by: Richard Weinberger <richard@nod.at>
2021-12-27 03:22:36 +00:00
|
|
|
|
if (whiteout) {
|
|
|
|
|
ino_key_init(c, &key, whiteout->i_ino);
|
|
|
|
|
err = ubifs_tnc_add(c, &key, lnum, offs, wlen,
|
|
|
|
|
hash_whiteout_inode);
|
|
|
|
|
if (err)
|
|
|
|
|
goto out_ro;
|
|
|
|
|
offs += ALIGN(wlen, 8);
|
|
|
|
|
}
|
|
|
|
|
|
2008-07-14 16:08:37 +00:00
|
|
|
|
ino_key_init(c, &key, old_dir->i_ino);
|
2018-09-07 12:36:34 +00:00
|
|
|
|
err = ubifs_tnc_add(c, &key, lnum, offs, plen, hash_old_dir);
|
2008-07-14 16:08:37 +00:00
|
|
|
|
if (err)
|
|
|
|
|
goto out_ro;
|
|
|
|
|
|
2016-09-14 20:28:52 +00:00
|
|
|
|
if (move) {
|
2008-07-14 16:08:37 +00:00
|
|
|
|
offs += ALIGN(plen, 8);
|
|
|
|
|
ino_key_init(c, &key, new_dir->i_ino);
|
2018-09-07 12:36:34 +00:00
|
|
|
|
err = ubifs_tnc_add(c, &key, lnum, offs, plen, hash_new_dir);
|
2008-07-14 16:08:37 +00:00
|
|
|
|
if (err)
|
|
|
|
|
goto out_ro;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
finish_reservation(c);
|
|
|
|
|
if (new_inode) {
|
|
|
|
|
mark_inode_clean(c, new_ui);
|
|
|
|
|
spin_lock(&new_ui->ui_lock);
|
|
|
|
|
new_ui->synced_i_size = new_ui->ui_size;
|
|
|
|
|
spin_unlock(&new_ui->ui_lock);
|
|
|
|
|
}
|
ubifs: Rename whiteout atomically
Currently, rename whiteout has 3 steps:
1. create tmpfile(which associates old dentry to tmpfile inode) for
whiteout, and store tmpfile to disk
2. link whiteout, associate whiteout inode to old dentry agagin and
store old dentry, old inode, new dentry on disk
3. writeback dirty whiteout inode to disk
Suddenly power-cut or error occurring(eg. ENOSPC returned by budget,
memory allocation failure) during above steps may cause kinds of problems:
Problem 1: ENOSPC returned by whiteout space budget (before step 2),
old dentry will disappear after rename syscall, whiteout file
cannot be found either.
ls dir // we get file, whiteout
rename(dir/file, dir/whiteout, REANME_WHITEOUT)
ENOSPC = ubifs_budget_space(&wht_req) // return
ls dir // empty (no file, no whiteout)
Problem 2: Power-cut happens before step 3, whiteout inode with 'nlink=1'
is not stored on disk, whiteout dentry(old dentry) is written
on disk, whiteout file is lost on next mount (We get "dead
directory entry" after executing 'ls -l' on whiteout file).
Now, we use following 3 steps to finish rename whiteout:
1. create an in-mem inode with 'nlink = 1' as whiteout
2. ubifs_jnl_rename (Write on disk to finish associating old dentry to
whiteout inode, associating new dentry with old inode)
3. iput(whiteout)
Rely writing in-mem inode on disk by ubifs_jnl_rename() to finish rename
whiteout, which avoids middle disk state caused by suddenly power-cut
and error occurring.
Fixes: 9e0a1fff8db56ea ("ubifs: Implement RENAME_WHITEOUT")
Signed-off-by: Zhihao Cheng <chengzhihao1@huawei.com>
Signed-off-by: Richard Weinberger <richard@nod.at>
2021-12-27 03:22:36 +00:00
|
|
|
|
/*
|
|
|
|
|
* No need to mark whiteout inode clean.
|
|
|
|
|
* Whiteout doesn't have non-zero size, no need to update
|
|
|
|
|
* synced_i_size for whiteout_ui.
|
|
|
|
|
*/
|
2008-07-14 16:08:37 +00:00
|
|
|
|
mark_inode_clean(c, ubifs_inode(old_dir));
|
|
|
|
|
if (move)
|
|
|
|
|
mark_inode_clean(c, ubifs_inode(new_dir));
|
|
|
|
|
kfree(dent);
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
out_release:
|
|
|
|
|
release_head(c, BASEHD);
|
|
|
|
|
out_ro:
|
|
|
|
|
ubifs_ro_mode(c, err);
|
2020-07-07 12:51:40 +00:00
|
|
|
|
if (orphan_added)
|
2008-07-14 16:08:37 +00:00
|
|
|
|
ubifs_delete_orphan(c, new_inode->i_ino);
|
|
|
|
|
out_finish:
|
|
|
|
|
finish_reservation(c);
|
|
|
|
|
out_free:
|
|
|
|
|
kfree(dent);
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2016-09-29 20:20:19 +00:00
|
|
|
|
* truncate_data_node - re-compress/encrypt a truncated data node.
|
|
|
|
|
* @c: UBIFS file-system description object
|
2021-06-04 01:45:56 +00:00
|
|
|
|
* @inode: inode which refers to the data node
|
2016-09-29 20:20:19 +00:00
|
|
|
|
* @block: data block number
|
2008-07-14 16:08:37 +00:00
|
|
|
|
* @dn: data node to re-compress
|
|
|
|
|
* @new_len: new length
|
2022-07-09 08:40:32 +00:00
|
|
|
|
* @dn_size: size of the data node @dn in memory
|
2008-07-14 16:08:37 +00:00
|
|
|
|
*
|
|
|
|
|
* This function is used when an inode is truncated and the last data node of
|
2016-09-29 20:20:19 +00:00
|
|
|
|
* the inode has to be re-compressed/encrypted and re-written.
|
2008-07-14 16:08:37 +00:00
|
|
|
|
*/
|
2016-09-29 20:20:19 +00:00
|
|
|
|
static int truncate_data_node(const struct ubifs_info *c, const struct inode *inode,
|
|
|
|
|
unsigned int block, struct ubifs_data_node *dn,
|
2022-07-09 08:40:32 +00:00
|
|
|
|
int *new_len, int dn_size)
|
2008-07-14 16:08:37 +00:00
|
|
|
|
{
|
|
|
|
|
void *buf;
|
2022-07-09 08:40:32 +00:00
|
|
|
|
int err, dlen, compr_type, out_len, data_size;
|
2008-07-14 16:08:37 +00:00
|
|
|
|
|
|
|
|
|
out_len = le32_to_cpu(dn->size);
|
2018-07-02 21:47:13 +00:00
|
|
|
|
buf = kmalloc_array(out_len, WORST_COMPR_FACTOR, GFP_NOFS);
|
2008-07-14 16:08:37 +00:00
|
|
|
|
if (!buf)
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
2022-07-09 08:40:32 +00:00
|
|
|
|
dlen = le32_to_cpu(dn->ch.len) - UBIFS_DATA_NODE_SZ;
|
|
|
|
|
data_size = dn_size - UBIFS_DATA_NODE_SZ;
|
2008-07-14 16:08:37 +00:00
|
|
|
|
compr_type = le16_to_cpu(dn->compr_type);
|
|
|
|
|
|
2019-12-09 21:27:21 +00:00
|
|
|
|
if (IS_ENCRYPTED(inode)) {
|
2016-09-29 20:20:19 +00:00
|
|
|
|
err = ubifs_decrypt(inode, dn, &dlen, block);
|
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
2017-05-17 08:36:45 +00:00
|
|
|
|
if (compr_type == UBIFS_COMPR_NONE) {
|
|
|
|
|
out_len = *new_len;
|
|
|
|
|
} else {
|
2016-09-29 20:20:19 +00:00
|
|
|
|
err = ubifs_decompress(c, &dn->data, dlen, buf, &out_len, compr_type);
|
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
|
|
ubifs_compress(c, buf, *new_len, &dn->data, &out_len, &compr_type);
|
|
|
|
|
}
|
|
|
|
|
|
2019-12-09 21:27:21 +00:00
|
|
|
|
if (IS_ENCRYPTED(inode)) {
|
2022-07-09 08:40:32 +00:00
|
|
|
|
err = ubifs_encrypt(inode, dn, out_len, &data_size, block);
|
2016-09-29 20:20:19 +00:00
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
|
|
|
|
|
2022-07-09 08:40:32 +00:00
|
|
|
|
out_len = data_size;
|
2016-09-29 20:20:19 +00:00
|
|
|
|
} else {
|
|
|
|
|
dn->compr_size = 0;
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-12 11:01:57 +00:00
|
|
|
|
ubifs_assert(c, out_len <= UBIFS_BLOCK_SIZE);
|
2008-07-14 16:08:37 +00:00
|
|
|
|
dn->compr_type = cpu_to_le16(compr_type);
|
|
|
|
|
dn->size = cpu_to_le32(*new_len);
|
|
|
|
|
*new_len = UBIFS_DATA_NODE_SZ + out_len;
|
2016-12-16 13:32:39 +00:00
|
|
|
|
err = 0;
|
2008-07-14 16:08:37 +00:00
|
|
|
|
out:
|
|
|
|
|
kfree(buf);
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* ubifs_jnl_truncate - update the journal for a truncation.
|
|
|
|
|
* @c: UBIFS file-system description object
|
|
|
|
|
* @inode: inode to truncate
|
|
|
|
|
* @old_size: old size
|
|
|
|
|
* @new_size: new size
|
|
|
|
|
*
|
|
|
|
|
* When the size of a file decreases due to truncation, a truncation node is
|
|
|
|
|
* written, the journal tree is updated, and the last data block is re-written
|
|
|
|
|
* if it has been affected. The inode is also updated in order to synchronize
|
|
|
|
|
* the new inode size.
|
|
|
|
|
*
|
|
|
|
|
* This function marks the inode as clean and returns zero on success. In case
|
|
|
|
|
* of failure, a negative error code is returned.
|
|
|
|
|
*/
|
|
|
|
|
int ubifs_jnl_truncate(struct ubifs_info *c, const struct inode *inode,
|
|
|
|
|
loff_t old_size, loff_t new_size)
|
|
|
|
|
{
|
|
|
|
|
union ubifs_key key, to_key;
|
|
|
|
|
struct ubifs_ino_node *ino;
|
|
|
|
|
struct ubifs_trun_node *trun;
|
treewide: Remove uninitialized_var() usage
Using uninitialized_var() is dangerous as it papers over real bugs[1]
(or can in the future), and suppresses unrelated compiler warnings
(e.g. "unused variable"). If the compiler thinks it is uninitialized,
either simply initialize the variable or make compiler changes.
In preparation for removing[2] the[3] macro[4], remove all remaining
needless uses with the following script:
git grep '\buninitialized_var\b' | cut -d: -f1 | sort -u | \
xargs perl -pi -e \
's/\buninitialized_var\(([^\)]+)\)/\1/g;
s:\s*/\* (GCC be quiet|to make compiler happy) \*/$::g;'
drivers/video/fbdev/riva/riva_hw.c was manually tweaked to avoid
pathological white-space.
No outstanding warnings were found building allmodconfig with GCC 9.3.0
for x86_64, i386, arm64, arm, powerpc, powerpc64le, s390x, mips, sparc64,
alpha, and m68k.
[1] https://lore.kernel.org/lkml/20200603174714.192027-1-glider@google.com/
[2] https://lore.kernel.org/lkml/CA+55aFw+Vbj0i=1TGqCR5vQkCzWJ0QxK6CernOU6eedsudAixw@mail.gmail.com/
[3] https://lore.kernel.org/lkml/CA+55aFwgbgqhbp1fkxvRKEpzyR5J8n1vKT1VZdz9knmPuXhOeg@mail.gmail.com/
[4] https://lore.kernel.org/lkml/CA+55aFz2500WfbKXAx8s67wrm9=yVJu65TpLgN_ybYNv0VEOKA@mail.gmail.com/
Reviewed-by: Leon Romanovsky <leonro@mellanox.com> # drivers/infiniband and mlx4/mlx5
Acked-by: Jason Gunthorpe <jgg@mellanox.com> # IB
Acked-by: Kalle Valo <kvalo@codeaurora.org> # wireless drivers
Reviewed-by: Chao Yu <yuchao0@huawei.com> # erofs
Signed-off-by: Kees Cook <keescook@chromium.org>
2020-06-03 20:09:38 +00:00
|
|
|
|
struct ubifs_data_node *dn;
|
2008-07-14 16:08:37 +00:00
|
|
|
|
int err, dlen, len, lnum, offs, bit, sz, sync = IS_SYNC(inode);
|
2022-07-09 08:40:32 +00:00
|
|
|
|
int dn_size;
|
2008-07-14 16:08:37 +00:00
|
|
|
|
struct ubifs_inode *ui = ubifs_inode(inode);
|
|
|
|
|
ino_t inum = inode->i_ino;
|
|
|
|
|
unsigned int blk;
|
2018-09-07 12:36:34 +00:00
|
|
|
|
u8 hash_ino[UBIFS_HASH_ARR_SZ];
|
|
|
|
|
u8 hash_dn[UBIFS_HASH_ARR_SZ];
|
2008-07-14 16:08:37 +00:00
|
|
|
|
|
UBIFS: fix compilation warnings
We print 'ino_t' type using '%lu' printk() placeholder, but this
results in many warnings when compiling for Alpha platform. Fix
this by adding (unsingned long) casts.
Fixes these warnings:
fs/ubifs/journal.c:693: warning: format '%lu' expects type 'long unsigned int', but argument 4 has type 'ino_t'
fs/ubifs/journal.c:1131: warning: format '%lu' expects type 'long unsigned int', but argument 4 has type 'ino_t'
fs/ubifs/dir.c:163: warning: format '%lu' expects type 'long unsigned int', but argument 4 has type 'ino_t'
fs/ubifs/tnc.c:2680: warning: format '%lu' expects type 'long unsigned int', but argument 4 has type 'ino_t'
fs/ubifs/tnc.c:2700: warning: format '%lu' expects type 'long unsigned int', but argument 5 has type 'ino_t'
fs/ubifs/replay.c:1066: warning: format '%lu' expects type 'long unsigned int', but argument 7 has type 'ino_t'
fs/ubifs/orphan.c:108: warning: format '%lu' expects type 'long unsigned int', but argument 4 has type 'ino_t'
fs/ubifs/orphan.c:135: warning: format '%lu' expects type 'long unsigned int', but argument 4 has type 'ino_t'
fs/ubifs/orphan.c:142: warning: format '%lu' expects type 'long unsigned int', but argument 4 has type 'ino_t'
fs/ubifs/orphan.c:154: warning: format '%lu' expects type 'long unsigned int', but argument 4 has type 'ino_t'
fs/ubifs/orphan.c:159: warning: format '%lu' expects type 'long unsigned int', but argument 4 has type 'ino_t'
fs/ubifs/orphan.c:451: warning: format '%lu' expects type 'long unsigned int', but argument 4 has type 'ino_t'
fs/ubifs/orphan.c:539: warning: format '%lu' expects type 'long unsigned int', but argument 4 has type 'ino_t'
fs/ubifs/orphan.c:612: warning: format '%lu' expects type 'long unsigned int', but argument 4 has type 'ino_t'
fs/ubifs/orphan.c:843: warning: format '%lu' expects type 'long unsigned int', but argument 4 has type 'ino_t'
fs/ubifs/orphan.c:856: warning: format '%lu' expects type 'long unsigned int', but argument 4 has type 'ino_t'
fs/ubifs/recovery.c:1438: warning: format '%lu' expects type 'long unsigned int', but argument 4 has type 'ino_t'
fs/ubifs/recovery.c:1443: warning: format '%lu' expects type 'long unsigned int', but argument 4 has type 'ino_t'
fs/ubifs/recovery.c:1475: warning: format '%lu' expects type 'long unsigned int', but argument 4 has type 'ino_t'
fs/ubifs/recovery.c:1495: warning: format '%lu' expects type 'long unsigned int', but argument 4 has type 'ino_t'
fs/ubifs/debug.c:105: warning: format '%lu' expects type 'long unsigned int', but argument 3 has type 'ino_t'
fs/ubifs/debug.c:105: warning: format '%lu' expects type 'long unsigned int', but argument 3 has type 'ino_t'
fs/ubifs/debug.c:110: warning: format '%lu' expects type 'long unsigned int', but argument 3 has type 'ino_t'
fs/ubifs/debug.c:110: warning: format '%lu' expects type 'long unsigned int', but argument 3 has type 'ino_t'
fs/ubifs/debug.c:114: warning: format '%lu' expects type 'long unsigned int', but argument 3 has type 'ino_t'
fs/ubifs/debug.c:114: warning: format '%lu' expects type 'long unsigned int', but argument 3 has type 'ino_t'
fs/ubifs/debug.c:118: warning: format '%lu' expects type 'long unsigned int', but argument 3 has type 'ino_t'
fs/ubifs/debug.c:118: warning: format '%lu' expects type 'long unsigned int', but argument 3 has type 'ino_t'
fs/ubifs/debug.c:1591: warning: format '%lu' expects type 'long unsigned int', but argument 4 has type 'ino_t'
fs/ubifs/debug.c:1671: warning: format '%lu' expects type 'long unsigned int', but argument 4 has type 'ino_t'
fs/ubifs/debug.c:1674: warning: format '%lu' expects type 'long unsigned int', but argument 5 has type 'ino_t'
fs/ubifs/debug.c:1680: warning: format '%lu' expects type 'long unsigned int', but argument 4 has type 'ino_t'
fs/ubifs/debug.c:1699: warning: format '%lu' expects type 'long unsigned int', but argument 5 has type 'ino_t'
fs/ubifs/debug.c:1788: warning: format '%lu' expects type 'long unsigned int', but argument 5 has type 'ino_t'
fs/ubifs/debug.c:1821: warning: format '%lu' expects type 'long unsigned int', but argument 5 has type 'ino_t'
fs/ubifs/debug.c:1833: warning: format '%lu' expects type 'long unsigned int', but argument 5 has type 'ino_t'
fs/ubifs/debug.c:1924: warning: format '%lu' expects type 'long unsigned int', but argument 4 has type 'ino_t'
fs/ubifs/debug.c:1932: warning: format '%lu' expects type 'long unsigned int', but argument 4 has type 'ino_t'
fs/ubifs/debug.c:1938: warning: format '%lu' expects type 'long unsigned int', but argument 4 has type 'ino_t'
fs/ubifs/debug.c:1945: warning: format '%lu' expects type 'long unsigned int', but argument 4 has type 'ino_t'
fs/ubifs/debug.c:1953: warning: format '%lu' expects type 'long unsigned int', but argument 4 has type 'ino_t'
fs/ubifs/debug.c:1960: warning: format '%lu' expects type 'long unsigned int', but argument 4 has type 'ino_t'
fs/ubifs/debug.c:1967: warning: format '%lu' expects type 'long unsigned int', but argument 4 has type 'ino_t'
fs/ubifs/debug.c:1973: warning: format '%lu' expects type 'long unsigned int', but argument 4 has type 'ino_t'
fs/ubifs/debug.c:1988: warning: format '%lu' expects type 'long unsigned int', but argument 4 has type 'ino_t'
fs/ubifs/debug.c:1991: warning: format '%lu' expects type 'long unsigned int', but argument 5 has type 'ino_t'
fs/ubifs/debug.c:2009: warning: format '%lu' expects type 'long unsigned int', but argument 2 has type 'ino_t'
Reported-by: Randy Dunlap <randy.dunlap@oracle.com>
Signed-off-by: Artem Bityutskiy <Artem.Bityutskiy@nokia.com>
2008-10-29 10:08:43 +00:00
|
|
|
|
dbg_jnl("ino %lu, size %lld -> %lld",
|
|
|
|
|
(unsigned long)inum, old_size, new_size);
|
2018-07-12 11:01:57 +00:00
|
|
|
|
ubifs_assert(c, !ui->data_len);
|
|
|
|
|
ubifs_assert(c, S_ISREG(inode->i_mode));
|
|
|
|
|
ubifs_assert(c, mutex_is_locked(&ui->ui_mutex));
|
2008-07-14 16:08:37 +00:00
|
|
|
|
|
2022-07-09 08:40:32 +00:00
|
|
|
|
dn_size = COMPRESSED_DATA_NODE_BUF_SZ;
|
ubifs: Add authentication nodes to journal
Nodes that are written to flash can only be authenticated through the
index after the next commit. When a journal replay is necessary the
nodes are not yet referenced by the index and thus can't be
authenticated.
This patch overcomes this situation by creating a hash over all nodes
beginning from the commit start node over the reference node(s) and
the buds themselves. From
time to time we insert authentication nodes. Authentication nodes
contain a HMAC from the current hash state, so that they can be
used to authenticate a journal replay up to the point where the
authentication node is. The hash is continued afterwards
so that theoretically we would only have to check the HMAC of
the last authentication node we find.
Overall we get this picture:
,,,,,,,,
,......,...........................................
,. CS , hash1.----. hash2.----.
,. | , . |hmac . |hmac
,. v , . v . v
,.REF#0,-> bud -> bud -> bud.-> auth -> bud -> bud.-> auth ...
,..|...,...........................................
, | ,
, | ,,,,,,,,,,,,,,,
. | hash3,----.
, | , |hmac
, v , v
, REF#1 -> bud -> bud,-> auth ...
,,,|,,,,,,,,,,,,,,,,,,
v
REF#2 -> ...
|
V
...
Note how hash3 covers CS, REF#0 and REF#1 so that it is not possible to
exchange or skip any reference nodes. Unlike the picture suggests the
auth nodes themselves are not hashed.
With this it is possible for an offline attacker to cut each journal
head or to drop the last reference node(s), but not to skip any journal
heads or to reorder any operations.
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Signed-off-by: Richard Weinberger <richard@nod.at>
2018-09-07 12:36:36 +00:00
|
|
|
|
|
2022-07-09 08:40:32 +00:00
|
|
|
|
if (IS_ENCRYPTED(inode))
|
|
|
|
|
dn_size += UBIFS_CIPHER_BLOCK_SIZE;
|
|
|
|
|
|
|
|
|
|
sz = UBIFS_TRUN_NODE_SZ + UBIFS_INO_NODE_SZ +
|
|
|
|
|
dn_size + ubifs_auth_node_sz(c);
|
ubifs: Add authentication nodes to journal
Nodes that are written to flash can only be authenticated through the
index after the next commit. When a journal replay is necessary the
nodes are not yet referenced by the index and thus can't be
authenticated.
This patch overcomes this situation by creating a hash over all nodes
beginning from the commit start node over the reference node(s) and
the buds themselves. From
time to time we insert authentication nodes. Authentication nodes
contain a HMAC from the current hash state, so that they can be
used to authenticate a journal replay up to the point where the
authentication node is. The hash is continued afterwards
so that theoretically we would only have to check the HMAC of
the last authentication node we find.
Overall we get this picture:
,,,,,,,,
,......,...........................................
,. CS , hash1.----. hash2.----.
,. | , . |hmac . |hmac
,. v , . v . v
,.REF#0,-> bud -> bud -> bud.-> auth -> bud -> bud.-> auth ...
,..|...,...........................................
, | ,
, | ,,,,,,,,,,,,,,,
. | hash3,----.
, | , |hmac
, v , v
, REF#1 -> bud -> bud,-> auth ...
,,,|,,,,,,,,,,,,,,,,,,
v
REF#2 -> ...
|
V
...
Note how hash3 covers CS, REF#0 and REF#1 so that it is not possible to
exchange or skip any reference nodes. Unlike the picture suggests the
auth nodes themselves are not hashed.
With this it is possible for an offline attacker to cut each journal
head or to drop the last reference node(s), but not to skip any journal
heads or to reorder any operations.
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Signed-off-by: Richard Weinberger <richard@nod.at>
2018-09-07 12:36:36 +00:00
|
|
|
|
|
2008-07-14 16:08:37 +00:00
|
|
|
|
ino = kmalloc(sz, GFP_NOFS);
|
|
|
|
|
if (!ino)
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
|
|
trun = (void *)ino + UBIFS_INO_NODE_SZ;
|
|
|
|
|
trun->ch.node_type = UBIFS_TRUN_NODE;
|
|
|
|
|
trun->inum = cpu_to_le32(inum);
|
|
|
|
|
trun->old_size = cpu_to_le64(old_size);
|
|
|
|
|
trun->new_size = cpu_to_le64(new_size);
|
|
|
|
|
zero_trun_node_unused(trun);
|
|
|
|
|
|
|
|
|
|
dlen = new_size & (UBIFS_BLOCK_SIZE - 1);
|
|
|
|
|
if (dlen) {
|
|
|
|
|
/* Get last data block so it can be truncated */
|
|
|
|
|
dn = (void *)trun + UBIFS_TRUN_NODE_SZ;
|
|
|
|
|
blk = new_size >> UBIFS_BLOCK_SHIFT;
|
|
|
|
|
data_key_init(c, &key, inum, blk);
|
2012-01-13 10:33:53 +00:00
|
|
|
|
dbg_jnlk(&key, "last block key ");
|
2008-07-14 16:08:37 +00:00
|
|
|
|
err = ubifs_tnc_lookup(c, &key, dn);
|
|
|
|
|
if (err == -ENOENT)
|
|
|
|
|
dlen = 0; /* Not found (so it is a hole) */
|
|
|
|
|
else if (err)
|
|
|
|
|
goto out_free;
|
|
|
|
|
else {
|
2018-07-01 21:20:51 +00:00
|
|
|
|
int dn_len = le32_to_cpu(dn->size);
|
|
|
|
|
|
|
|
|
|
if (dn_len <= 0 || dn_len > UBIFS_BLOCK_SIZE) {
|
|
|
|
|
ubifs_err(c, "bad data node (block %u, inode %lu)",
|
|
|
|
|
blk, inode->i_ino);
|
2022-07-09 08:40:32 +00:00
|
|
|
|
ubifs_dump_node(c, dn, dn_size);
|
2023-08-18 09:18:48 +00:00
|
|
|
|
err = -EUCLEAN;
|
2018-07-01 21:20:51 +00:00
|
|
|
|
goto out_free;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (dn_len <= dlen)
|
2008-07-14 16:08:37 +00:00
|
|
|
|
dlen = 0; /* Nothing to do */
|
|
|
|
|
else {
|
2022-07-09 08:40:32 +00:00
|
|
|
|
err = truncate_data_node(c, inode, blk, dn,
|
|
|
|
|
&dlen, dn_size);
|
2016-09-29 20:20:19 +00:00
|
|
|
|
if (err)
|
|
|
|
|
goto out_free;
|
2008-07-14 16:08:37 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Must make reservation before allocating sequence numbers */
|
|
|
|
|
len = UBIFS_TRUN_NODE_SZ + UBIFS_INO_NODE_SZ;
|
ubifs: Add authentication nodes to journal
Nodes that are written to flash can only be authenticated through the
index after the next commit. When a journal replay is necessary the
nodes are not yet referenced by the index and thus can't be
authenticated.
This patch overcomes this situation by creating a hash over all nodes
beginning from the commit start node over the reference node(s) and
the buds themselves. From
time to time we insert authentication nodes. Authentication nodes
contain a HMAC from the current hash state, so that they can be
used to authenticate a journal replay up to the point where the
authentication node is. The hash is continued afterwards
so that theoretically we would only have to check the HMAC of
the last authentication node we find.
Overall we get this picture:
,,,,,,,,
,......,...........................................
,. CS , hash1.----. hash2.----.
,. | , . |hmac . |hmac
,. v , . v . v
,.REF#0,-> bud -> bud -> bud.-> auth -> bud -> bud.-> auth ...
,..|...,...........................................
, | ,
, | ,,,,,,,,,,,,,,,
. | hash3,----.
, | , |hmac
, v , v
, REF#1 -> bud -> bud,-> auth ...
,,,|,,,,,,,,,,,,,,,,,,
v
REF#2 -> ...
|
V
...
Note how hash3 covers CS, REF#0 and REF#1 so that it is not possible to
exchange or skip any reference nodes. Unlike the picture suggests the
auth nodes themselves are not hashed.
With this it is possible for an offline attacker to cut each journal
head or to drop the last reference node(s), but not to skip any journal
heads or to reorder any operations.
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Signed-off-by: Richard Weinberger <richard@nod.at>
2018-09-07 12:36:36 +00:00
|
|
|
|
|
|
|
|
|
if (ubifs_authenticated(c))
|
|
|
|
|
len += ALIGN(dlen, 8) + ubifs_auth_node_sz(c);
|
|
|
|
|
else
|
2008-07-14 16:08:37 +00:00
|
|
|
|
len += dlen;
|
ubifs: Add authentication nodes to journal
Nodes that are written to flash can only be authenticated through the
index after the next commit. When a journal replay is necessary the
nodes are not yet referenced by the index and thus can't be
authenticated.
This patch overcomes this situation by creating a hash over all nodes
beginning from the commit start node over the reference node(s) and
the buds themselves. From
time to time we insert authentication nodes. Authentication nodes
contain a HMAC from the current hash state, so that they can be
used to authenticate a journal replay up to the point where the
authentication node is. The hash is continued afterwards
so that theoretically we would only have to check the HMAC of
the last authentication node we find.
Overall we get this picture:
,,,,,,,,
,......,...........................................
,. CS , hash1.----. hash2.----.
,. | , . |hmac . |hmac
,. v , . v . v
,.REF#0,-> bud -> bud -> bud.-> auth -> bud -> bud.-> auth ...
,..|...,...........................................
, | ,
, | ,,,,,,,,,,,,,,,
. | hash3,----.
, | , |hmac
, v , v
, REF#1 -> bud -> bud,-> auth ...
,,,|,,,,,,,,,,,,,,,,,,
v
REF#2 -> ...
|
V
...
Note how hash3 covers CS, REF#0 and REF#1 so that it is not possible to
exchange or skip any reference nodes. Unlike the picture suggests the
auth nodes themselves are not hashed.
With this it is possible for an offline attacker to cut each journal
head or to drop the last reference node(s), but not to skip any journal
heads or to reorder any operations.
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Signed-off-by: Richard Weinberger <richard@nod.at>
2018-09-07 12:36:36 +00:00
|
|
|
|
|
2008-07-14 16:08:37 +00:00
|
|
|
|
err = make_reservation(c, BASEHD, len);
|
|
|
|
|
if (err)
|
|
|
|
|
goto out_free;
|
|
|
|
|
|
2008-07-22 09:19:09 +00:00
|
|
|
|
pack_inode(c, ino, inode, 0);
|
2018-09-07 12:36:34 +00:00
|
|
|
|
err = ubifs_node_calc_hash(c, ino, hash_ino);
|
|
|
|
|
if (err)
|
|
|
|
|
goto out_release;
|
|
|
|
|
|
2008-07-14 16:08:37 +00:00
|
|
|
|
ubifs_prep_grp_node(c, trun, UBIFS_TRUN_NODE_SZ, dlen ? 0 : 1);
|
2018-09-07 12:36:34 +00:00
|
|
|
|
if (dlen) {
|
2008-07-14 16:08:37 +00:00
|
|
|
|
ubifs_prep_grp_node(c, dn, dlen, 1);
|
2018-09-07 12:36:34 +00:00
|
|
|
|
err = ubifs_node_calc_hash(c, dn, hash_dn);
|
|
|
|
|
if (err)
|
|
|
|
|
goto out_release;
|
|
|
|
|
}
|
2008-07-14 16:08:37 +00:00
|
|
|
|
|
|
|
|
|
err = write_head(c, BASEHD, ino, len, &lnum, &offs, sync);
|
|
|
|
|
if (err)
|
|
|
|
|
goto out_release;
|
|
|
|
|
if (!sync)
|
|
|
|
|
ubifs_wbuf_add_ino_nolock(&c->jheads[BASEHD].wbuf, inum);
|
|
|
|
|
release_head(c, BASEHD);
|
|
|
|
|
|
ubifs: Add authentication nodes to journal
Nodes that are written to flash can only be authenticated through the
index after the next commit. When a journal replay is necessary the
nodes are not yet referenced by the index and thus can't be
authenticated.
This patch overcomes this situation by creating a hash over all nodes
beginning from the commit start node over the reference node(s) and
the buds themselves. From
time to time we insert authentication nodes. Authentication nodes
contain a HMAC from the current hash state, so that they can be
used to authenticate a journal replay up to the point where the
authentication node is. The hash is continued afterwards
so that theoretically we would only have to check the HMAC of
the last authentication node we find.
Overall we get this picture:
,,,,,,,,
,......,...........................................
,. CS , hash1.----. hash2.----.
,. | , . |hmac . |hmac
,. v , . v . v
,.REF#0,-> bud -> bud -> bud.-> auth -> bud -> bud.-> auth ...
,..|...,...........................................
, | ,
, | ,,,,,,,,,,,,,,,
. | hash3,----.
, | , |hmac
, v , v
, REF#1 -> bud -> bud,-> auth ...
,,,|,,,,,,,,,,,,,,,,,,
v
REF#2 -> ...
|
V
...
Note how hash3 covers CS, REF#0 and REF#1 so that it is not possible to
exchange or skip any reference nodes. Unlike the picture suggests the
auth nodes themselves are not hashed.
With this it is possible for an offline attacker to cut each journal
head or to drop the last reference node(s), but not to skip any journal
heads or to reorder any operations.
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Signed-off-by: Richard Weinberger <richard@nod.at>
2018-09-07 12:36:36 +00:00
|
|
|
|
ubifs_add_auth_dirt(c, lnum);
|
|
|
|
|
|
2008-07-14 16:08:37 +00:00
|
|
|
|
if (dlen) {
|
|
|
|
|
sz = offs + UBIFS_INO_NODE_SZ + UBIFS_TRUN_NODE_SZ;
|
2018-09-07 12:36:34 +00:00
|
|
|
|
err = ubifs_tnc_add(c, &key, lnum, sz, dlen, hash_dn);
|
2008-07-14 16:08:37 +00:00
|
|
|
|
if (err)
|
|
|
|
|
goto out_ro;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ino_key_init(c, &key, inum);
|
2018-09-07 12:36:34 +00:00
|
|
|
|
err = ubifs_tnc_add(c, &key, lnum, offs, UBIFS_INO_NODE_SZ, hash_ino);
|
2008-07-14 16:08:37 +00:00
|
|
|
|
if (err)
|
|
|
|
|
goto out_ro;
|
|
|
|
|
|
|
|
|
|
err = ubifs_add_dirt(c, lnum, UBIFS_TRUN_NODE_SZ);
|
|
|
|
|
if (err)
|
|
|
|
|
goto out_ro;
|
|
|
|
|
|
|
|
|
|
bit = new_size & (UBIFS_BLOCK_SIZE - 1);
|
|
|
|
|
blk = (new_size >> UBIFS_BLOCK_SHIFT) + (bit ? 1 : 0);
|
|
|
|
|
data_key_init(c, &key, inum, blk);
|
|
|
|
|
|
|
|
|
|
bit = old_size & (UBIFS_BLOCK_SIZE - 1);
|
2008-12-28 09:34:26 +00:00
|
|
|
|
blk = (old_size >> UBIFS_BLOCK_SHIFT) - (bit ? 0 : 1);
|
2008-07-14 16:08:37 +00:00
|
|
|
|
data_key_init(c, &to_key, inum, blk);
|
|
|
|
|
|
|
|
|
|
err = ubifs_tnc_remove_range(c, &key, &to_key);
|
|
|
|
|
if (err)
|
|
|
|
|
goto out_ro;
|
|
|
|
|
|
|
|
|
|
finish_reservation(c);
|
|
|
|
|
spin_lock(&ui->ui_lock);
|
|
|
|
|
ui->synced_i_size = ui->ui_size;
|
|
|
|
|
spin_unlock(&ui->ui_lock);
|
|
|
|
|
mark_inode_clean(c, ui);
|
|
|
|
|
kfree(ino);
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
out_release:
|
|
|
|
|
release_head(c, BASEHD);
|
|
|
|
|
out_ro:
|
|
|
|
|
ubifs_ro_mode(c, err);
|
|
|
|
|
finish_reservation(c);
|
|
|
|
|
out_free:
|
|
|
|
|
kfree(ino);
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* ubifs_jnl_delete_xattr - delete an extended attribute.
|
|
|
|
|
* @c: UBIFS file-system description object
|
|
|
|
|
* @host: host inode
|
|
|
|
|
* @inode: extended attribute inode
|
|
|
|
|
* @nm: extended attribute entry name
|
|
|
|
|
*
|
|
|
|
|
* This function delete an extended attribute which is very similar to
|
|
|
|
|
* un-linking regular files - it writes a deletion xentry, a deletion inode and
|
|
|
|
|
* updates the target inode. Returns zero in case of success and a negative
|
|
|
|
|
* error code in case of failure.
|
|
|
|
|
*/
|
|
|
|
|
int ubifs_jnl_delete_xattr(struct ubifs_info *c, const struct inode *host,
|
2016-11-11 21:50:29 +00:00
|
|
|
|
const struct inode *inode,
|
|
|
|
|
const struct fscrypt_name *nm)
|
2008-07-14 16:08:37 +00:00
|
|
|
|
{
|
ubifs: Add authentication nodes to journal
Nodes that are written to flash can only be authenticated through the
index after the next commit. When a journal replay is necessary the
nodes are not yet referenced by the index and thus can't be
authenticated.
This patch overcomes this situation by creating a hash over all nodes
beginning from the commit start node over the reference node(s) and
the buds themselves. From
time to time we insert authentication nodes. Authentication nodes
contain a HMAC from the current hash state, so that they can be
used to authenticate a journal replay up to the point where the
authentication node is. The hash is continued afterwards
so that theoretically we would only have to check the HMAC of
the last authentication node we find.
Overall we get this picture:
,,,,,,,,
,......,...........................................
,. CS , hash1.----. hash2.----.
,. | , . |hmac . |hmac
,. v , . v . v
,.REF#0,-> bud -> bud -> bud.-> auth -> bud -> bud.-> auth ...
,..|...,...........................................
, | ,
, | ,,,,,,,,,,,,,,,
. | hash3,----.
, | , |hmac
, v , v
, REF#1 -> bud -> bud,-> auth ...
,,,|,,,,,,,,,,,,,,,,,,
v
REF#2 -> ...
|
V
...
Note how hash3 covers CS, REF#0 and REF#1 so that it is not possible to
exchange or skip any reference nodes. Unlike the picture suggests the
auth nodes themselves are not hashed.
With this it is possible for an offline attacker to cut each journal
head or to drop the last reference node(s), but not to skip any journal
heads or to reorder any operations.
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Signed-off-by: Richard Weinberger <richard@nod.at>
2018-09-07 12:36:36 +00:00
|
|
|
|
int err, xlen, hlen, len, lnum, xent_offs, aligned_xlen, write_len;
|
2008-07-14 16:08:37 +00:00
|
|
|
|
struct ubifs_dent_node *xent;
|
|
|
|
|
struct ubifs_ino_node *ino;
|
|
|
|
|
union ubifs_key xent_key, key1, key2;
|
|
|
|
|
int sync = IS_DIRSYNC(host);
|
|
|
|
|
struct ubifs_inode *host_ui = ubifs_inode(host);
|
2018-09-07 12:36:34 +00:00
|
|
|
|
u8 hash[UBIFS_HASH_ARR_SZ];
|
2008-07-14 16:08:37 +00:00
|
|
|
|
|
2018-07-12 11:01:57 +00:00
|
|
|
|
ubifs_assert(c, inode->i_nlink == 0);
|
|
|
|
|
ubifs_assert(c, mutex_is_locked(&host_ui->ui_mutex));
|
2008-07-14 16:08:37 +00:00
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Since we are deleting the inode, we do not bother to attach any data
|
|
|
|
|
* to it and assume its length is %UBIFS_INO_NODE_SZ.
|
|
|
|
|
*/
|
2016-11-11 21:50:29 +00:00
|
|
|
|
xlen = UBIFS_DENT_NODE_SZ + fname_len(nm) + 1;
|
2008-07-14 16:08:37 +00:00
|
|
|
|
aligned_xlen = ALIGN(xlen, 8);
|
|
|
|
|
hlen = host_ui->data_len + UBIFS_INO_NODE_SZ;
|
|
|
|
|
len = aligned_xlen + UBIFS_INO_NODE_SZ + ALIGN(hlen, 8);
|
|
|
|
|
|
ubifs: Add authentication nodes to journal
Nodes that are written to flash can only be authenticated through the
index after the next commit. When a journal replay is necessary the
nodes are not yet referenced by the index and thus can't be
authenticated.
This patch overcomes this situation by creating a hash over all nodes
beginning from the commit start node over the reference node(s) and
the buds themselves. From
time to time we insert authentication nodes. Authentication nodes
contain a HMAC from the current hash state, so that they can be
used to authenticate a journal replay up to the point where the
authentication node is. The hash is continued afterwards
so that theoretically we would only have to check the HMAC of
the last authentication node we find.
Overall we get this picture:
,,,,,,,,
,......,...........................................
,. CS , hash1.----. hash2.----.
,. | , . |hmac . |hmac
,. v , . v . v
,.REF#0,-> bud -> bud -> bud.-> auth -> bud -> bud.-> auth ...
,..|...,...........................................
, | ,
, | ,,,,,,,,,,,,,,,
. | hash3,----.
, | , |hmac
, v , v
, REF#1 -> bud -> bud,-> auth ...
,,,|,,,,,,,,,,,,,,,,,,
v
REF#2 -> ...
|
V
...
Note how hash3 covers CS, REF#0 and REF#1 so that it is not possible to
exchange or skip any reference nodes. Unlike the picture suggests the
auth nodes themselves are not hashed.
With this it is possible for an offline attacker to cut each journal
head or to drop the last reference node(s), but not to skip any journal
heads or to reorder any operations.
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Signed-off-by: Richard Weinberger <richard@nod.at>
2018-09-07 12:36:36 +00:00
|
|
|
|
write_len = len + ubifs_auth_node_sz(c);
|
|
|
|
|
|
|
|
|
|
xent = kzalloc(write_len, GFP_NOFS);
|
2008-07-14 16:08:37 +00:00
|
|
|
|
if (!xent)
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
|
|
/* Make reservation before allocating sequence numbers */
|
ubifs: Add authentication nodes to journal
Nodes that are written to flash can only be authenticated through the
index after the next commit. When a journal replay is necessary the
nodes are not yet referenced by the index and thus can't be
authenticated.
This patch overcomes this situation by creating a hash over all nodes
beginning from the commit start node over the reference node(s) and
the buds themselves. From
time to time we insert authentication nodes. Authentication nodes
contain a HMAC from the current hash state, so that they can be
used to authenticate a journal replay up to the point where the
authentication node is. The hash is continued afterwards
so that theoretically we would only have to check the HMAC of
the last authentication node we find.
Overall we get this picture:
,,,,,,,,
,......,...........................................
,. CS , hash1.----. hash2.----.
,. | , . |hmac . |hmac
,. v , . v . v
,.REF#0,-> bud -> bud -> bud.-> auth -> bud -> bud.-> auth ...
,..|...,...........................................
, | ,
, | ,,,,,,,,,,,,,,,
. | hash3,----.
, | , |hmac
, v , v
, REF#1 -> bud -> bud,-> auth ...
,,,|,,,,,,,,,,,,,,,,,,
v
REF#2 -> ...
|
V
...
Note how hash3 covers CS, REF#0 and REF#1 so that it is not possible to
exchange or skip any reference nodes. Unlike the picture suggests the
auth nodes themselves are not hashed.
With this it is possible for an offline attacker to cut each journal
head or to drop the last reference node(s), but not to skip any journal
heads or to reorder any operations.
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Signed-off-by: Richard Weinberger <richard@nod.at>
2018-09-07 12:36:36 +00:00
|
|
|
|
err = make_reservation(c, BASEHD, write_len);
|
2008-07-14 16:08:37 +00:00
|
|
|
|
if (err) {
|
|
|
|
|
kfree(xent);
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
xent->ch.node_type = UBIFS_XENT_NODE;
|
|
|
|
|
xent_key_init(c, &xent_key, host->i_ino, nm);
|
|
|
|
|
key_write(c, &xent_key, xent->key);
|
|
|
|
|
xent->inum = 0;
|
|
|
|
|
xent->type = get_dent_type(inode->i_mode);
|
2016-11-11 21:50:29 +00:00
|
|
|
|
xent->nlen = cpu_to_le16(fname_len(nm));
|
|
|
|
|
memcpy(xent->name, fname_name(nm), fname_len(nm));
|
|
|
|
|
xent->name[fname_len(nm)] = '\0';
|
2008-07-14 16:08:37 +00:00
|
|
|
|
zero_dent_node_unused(xent);
|
|
|
|
|
ubifs_prep_grp_node(c, xent, xlen, 0);
|
|
|
|
|
|
|
|
|
|
ino = (void *)xent + aligned_xlen;
|
2008-07-22 09:19:09 +00:00
|
|
|
|
pack_inode(c, ino, inode, 0);
|
2008-07-14 16:08:37 +00:00
|
|
|
|
ino = (void *)ino + UBIFS_INO_NODE_SZ;
|
2008-07-22 09:19:09 +00:00
|
|
|
|
pack_inode(c, ino, host, 1);
|
2018-09-07 12:36:34 +00:00
|
|
|
|
err = ubifs_node_calc_hash(c, ino, hash);
|
|
|
|
|
if (err)
|
|
|
|
|
goto out_release;
|
2008-07-14 16:08:37 +00:00
|
|
|
|
|
ubifs: Add authentication nodes to journal
Nodes that are written to flash can only be authenticated through the
index after the next commit. When a journal replay is necessary the
nodes are not yet referenced by the index and thus can't be
authenticated.
This patch overcomes this situation by creating a hash over all nodes
beginning from the commit start node over the reference node(s) and
the buds themselves. From
time to time we insert authentication nodes. Authentication nodes
contain a HMAC from the current hash state, so that they can be
used to authenticate a journal replay up to the point where the
authentication node is. The hash is continued afterwards
so that theoretically we would only have to check the HMAC of
the last authentication node we find.
Overall we get this picture:
,,,,,,,,
,......,...........................................
,. CS , hash1.----. hash2.----.
,. | , . |hmac . |hmac
,. v , . v . v
,.REF#0,-> bud -> bud -> bud.-> auth -> bud -> bud.-> auth ...
,..|...,...........................................
, | ,
, | ,,,,,,,,,,,,,,,
. | hash3,----.
, | , |hmac
, v , v
, REF#1 -> bud -> bud,-> auth ...
,,,|,,,,,,,,,,,,,,,,,,
v
REF#2 -> ...
|
V
...
Note how hash3 covers CS, REF#0 and REF#1 so that it is not possible to
exchange or skip any reference nodes. Unlike the picture suggests the
auth nodes themselves are not hashed.
With this it is possible for an offline attacker to cut each journal
head or to drop the last reference node(s), but not to skip any journal
heads or to reorder any operations.
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Signed-off-by: Richard Weinberger <richard@nod.at>
2018-09-07 12:36:36 +00:00
|
|
|
|
err = write_head(c, BASEHD, xent, write_len, &lnum, &xent_offs, sync);
|
2008-07-14 16:08:37 +00:00
|
|
|
|
if (!sync && !err)
|
|
|
|
|
ubifs_wbuf_add_ino_nolock(&c->jheads[BASEHD].wbuf, host->i_ino);
|
|
|
|
|
release_head(c, BASEHD);
|
ubifs: Add authentication nodes to journal
Nodes that are written to flash can only be authenticated through the
index after the next commit. When a journal replay is necessary the
nodes are not yet referenced by the index and thus can't be
authenticated.
This patch overcomes this situation by creating a hash over all nodes
beginning from the commit start node over the reference node(s) and
the buds themselves. From
time to time we insert authentication nodes. Authentication nodes
contain a HMAC from the current hash state, so that they can be
used to authenticate a journal replay up to the point where the
authentication node is. The hash is continued afterwards
so that theoretically we would only have to check the HMAC of
the last authentication node we find.
Overall we get this picture:
,,,,,,,,
,......,...........................................
,. CS , hash1.----. hash2.----.
,. | , . |hmac . |hmac
,. v , . v . v
,.REF#0,-> bud -> bud -> bud.-> auth -> bud -> bud.-> auth ...
,..|...,...........................................
, | ,
, | ,,,,,,,,,,,,,,,
. | hash3,----.
, | , |hmac
, v , v
, REF#1 -> bud -> bud,-> auth ...
,,,|,,,,,,,,,,,,,,,,,,
v
REF#2 -> ...
|
V
...
Note how hash3 covers CS, REF#0 and REF#1 so that it is not possible to
exchange or skip any reference nodes. Unlike the picture suggests the
auth nodes themselves are not hashed.
With this it is possible for an offline attacker to cut each journal
head or to drop the last reference node(s), but not to skip any journal
heads or to reorder any operations.
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Signed-off-by: Richard Weinberger <richard@nod.at>
2018-09-07 12:36:36 +00:00
|
|
|
|
|
|
|
|
|
ubifs_add_auth_dirt(c, lnum);
|
2008-07-14 16:08:37 +00:00
|
|
|
|
kfree(xent);
|
|
|
|
|
if (err)
|
|
|
|
|
goto out_ro;
|
|
|
|
|
|
|
|
|
|
/* Remove the extended attribute entry from TNC */
|
|
|
|
|
err = ubifs_tnc_remove_nm(c, &xent_key, nm);
|
|
|
|
|
if (err)
|
|
|
|
|
goto out_ro;
|
|
|
|
|
err = ubifs_add_dirt(c, lnum, xlen);
|
|
|
|
|
if (err)
|
|
|
|
|
goto out_ro;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Remove all nodes belonging to the extended attribute inode from TNC.
|
|
|
|
|
* Well, there actually must be only one node - the inode itself.
|
|
|
|
|
*/
|
|
|
|
|
lowest_ino_key(c, &key1, inode->i_ino);
|
|
|
|
|
highest_ino_key(c, &key2, inode->i_ino);
|
|
|
|
|
err = ubifs_tnc_remove_range(c, &key1, &key2);
|
|
|
|
|
if (err)
|
|
|
|
|
goto out_ro;
|
|
|
|
|
err = ubifs_add_dirt(c, lnum, UBIFS_INO_NODE_SZ);
|
|
|
|
|
if (err)
|
|
|
|
|
goto out_ro;
|
|
|
|
|
|
|
|
|
|
/* And update TNC with the new host inode position */
|
|
|
|
|
ino_key_init(c, &key1, host->i_ino);
|
2018-09-07 12:36:34 +00:00
|
|
|
|
err = ubifs_tnc_add(c, &key1, lnum, xent_offs + len - hlen, hlen, hash);
|
2008-07-14 16:08:37 +00:00
|
|
|
|
if (err)
|
|
|
|
|
goto out_ro;
|
|
|
|
|
|
|
|
|
|
finish_reservation(c);
|
|
|
|
|
spin_lock(&host_ui->ui_lock);
|
|
|
|
|
host_ui->synced_i_size = host_ui->ui_size;
|
|
|
|
|
spin_unlock(&host_ui->ui_lock);
|
|
|
|
|
mark_inode_clean(c, host_ui);
|
|
|
|
|
return 0;
|
|
|
|
|
|
2018-09-07 12:36:34 +00:00
|
|
|
|
out_release:
|
|
|
|
|
kfree(xent);
|
|
|
|
|
release_head(c, BASEHD);
|
2008-07-14 16:08:37 +00:00
|
|
|
|
out_ro:
|
|
|
|
|
ubifs_ro_mode(c, err);
|
|
|
|
|
finish_reservation(c);
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* ubifs_jnl_change_xattr - change an extended attribute.
|
|
|
|
|
* @c: UBIFS file-system description object
|
|
|
|
|
* @inode: extended attribute inode
|
|
|
|
|
* @host: host inode
|
|
|
|
|
*
|
|
|
|
|
* This function writes the updated version of an extended attribute inode and
|
2009-03-20 17:11:12 +00:00
|
|
|
|
* the host inode to the journal (to the base head). The host inode is written
|
2008-07-14 16:08:37 +00:00
|
|
|
|
* after the extended attribute inode in order to guarantee that the extended
|
|
|
|
|
* attribute will be flushed when the inode is synchronized by 'fsync()' and
|
|
|
|
|
* consequently, the write-buffer is synchronized. This function returns zero
|
|
|
|
|
* in case of success and a negative error code in case of failure.
|
|
|
|
|
*/
|
|
|
|
|
int ubifs_jnl_change_xattr(struct ubifs_info *c, const struct inode *inode,
|
|
|
|
|
const struct inode *host)
|
|
|
|
|
{
|
|
|
|
|
int err, len1, len2, aligned_len, aligned_len1, lnum, offs;
|
2008-08-12 13:30:12 +00:00
|
|
|
|
struct ubifs_inode *host_ui = ubifs_inode(host);
|
2008-07-14 16:08:37 +00:00
|
|
|
|
struct ubifs_ino_node *ino;
|
|
|
|
|
union ubifs_key key;
|
|
|
|
|
int sync = IS_DIRSYNC(host);
|
2018-09-07 12:36:34 +00:00
|
|
|
|
u8 hash_host[UBIFS_HASH_ARR_SZ];
|
|
|
|
|
u8 hash[UBIFS_HASH_ARR_SZ];
|
2008-07-14 16:08:37 +00:00
|
|
|
|
|
|
|
|
|
dbg_jnl("ino %lu, ino %lu", host->i_ino, inode->i_ino);
|
2018-07-12 11:01:57 +00:00
|
|
|
|
ubifs_assert(c, inode->i_nlink > 0);
|
|
|
|
|
ubifs_assert(c, mutex_is_locked(&host_ui->ui_mutex));
|
2008-07-14 16:08:37 +00:00
|
|
|
|
|
|
|
|
|
len1 = UBIFS_INO_NODE_SZ + host_ui->data_len;
|
|
|
|
|
len2 = UBIFS_INO_NODE_SZ + ubifs_inode(inode)->data_len;
|
|
|
|
|
aligned_len1 = ALIGN(len1, 8);
|
|
|
|
|
aligned_len = aligned_len1 + ALIGN(len2, 8);
|
|
|
|
|
|
ubifs: Add authentication nodes to journal
Nodes that are written to flash can only be authenticated through the
index after the next commit. When a journal replay is necessary the
nodes are not yet referenced by the index and thus can't be
authenticated.
This patch overcomes this situation by creating a hash over all nodes
beginning from the commit start node over the reference node(s) and
the buds themselves. From
time to time we insert authentication nodes. Authentication nodes
contain a HMAC from the current hash state, so that they can be
used to authenticate a journal replay up to the point where the
authentication node is. The hash is continued afterwards
so that theoretically we would only have to check the HMAC of
the last authentication node we find.
Overall we get this picture:
,,,,,,,,
,......,...........................................
,. CS , hash1.----. hash2.----.
,. | , . |hmac . |hmac
,. v , . v . v
,.REF#0,-> bud -> bud -> bud.-> auth -> bud -> bud.-> auth ...
,..|...,...........................................
, | ,
, | ,,,,,,,,,,,,,,,
. | hash3,----.
, | , |hmac
, v , v
, REF#1 -> bud -> bud,-> auth ...
,,,|,,,,,,,,,,,,,,,,,,
v
REF#2 -> ...
|
V
...
Note how hash3 covers CS, REF#0 and REF#1 so that it is not possible to
exchange or skip any reference nodes. Unlike the picture suggests the
auth nodes themselves are not hashed.
With this it is possible for an offline attacker to cut each journal
head or to drop the last reference node(s), but not to skip any journal
heads or to reorder any operations.
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Signed-off-by: Richard Weinberger <richard@nod.at>
2018-09-07 12:36:36 +00:00
|
|
|
|
aligned_len += ubifs_auth_node_sz(c);
|
|
|
|
|
|
2017-06-16 14:21:44 +00:00
|
|
|
|
ino = kzalloc(aligned_len, GFP_NOFS);
|
2008-07-14 16:08:37 +00:00
|
|
|
|
if (!ino)
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
|
|
/* Make reservation before allocating sequence numbers */
|
|
|
|
|
err = make_reservation(c, BASEHD, aligned_len);
|
|
|
|
|
if (err)
|
|
|
|
|
goto out_free;
|
|
|
|
|
|
2008-07-22 09:19:09 +00:00
|
|
|
|
pack_inode(c, ino, host, 0);
|
2018-09-07 12:36:34 +00:00
|
|
|
|
err = ubifs_node_calc_hash(c, ino, hash_host);
|
|
|
|
|
if (err)
|
|
|
|
|
goto out_release;
|
2008-07-22 09:19:09 +00:00
|
|
|
|
pack_inode(c, (void *)ino + aligned_len1, inode, 1);
|
2018-09-07 12:36:34 +00:00
|
|
|
|
err = ubifs_node_calc_hash(c, (void *)ino + aligned_len1, hash);
|
|
|
|
|
if (err)
|
|
|
|
|
goto out_release;
|
2008-07-14 16:08:37 +00:00
|
|
|
|
|
|
|
|
|
err = write_head(c, BASEHD, ino, aligned_len, &lnum, &offs, 0);
|
|
|
|
|
if (!sync && !err) {
|
|
|
|
|
struct ubifs_wbuf *wbuf = &c->jheads[BASEHD].wbuf;
|
|
|
|
|
|
|
|
|
|
ubifs_wbuf_add_ino_nolock(wbuf, host->i_ino);
|
|
|
|
|
ubifs_wbuf_add_ino_nolock(wbuf, inode->i_ino);
|
|
|
|
|
}
|
|
|
|
|
release_head(c, BASEHD);
|
|
|
|
|
if (err)
|
|
|
|
|
goto out_ro;
|
|
|
|
|
|
ubifs: Add authentication nodes to journal
Nodes that are written to flash can only be authenticated through the
index after the next commit. When a journal replay is necessary the
nodes are not yet referenced by the index and thus can't be
authenticated.
This patch overcomes this situation by creating a hash over all nodes
beginning from the commit start node over the reference node(s) and
the buds themselves. From
time to time we insert authentication nodes. Authentication nodes
contain a HMAC from the current hash state, so that they can be
used to authenticate a journal replay up to the point where the
authentication node is. The hash is continued afterwards
so that theoretically we would only have to check the HMAC of
the last authentication node we find.
Overall we get this picture:
,,,,,,,,
,......,...........................................
,. CS , hash1.----. hash2.----.
,. | , . |hmac . |hmac
,. v , . v . v
,.REF#0,-> bud -> bud -> bud.-> auth -> bud -> bud.-> auth ...
,..|...,...........................................
, | ,
, | ,,,,,,,,,,,,,,,
. | hash3,----.
, | , |hmac
, v , v
, REF#1 -> bud -> bud,-> auth ...
,,,|,,,,,,,,,,,,,,,,,,
v
REF#2 -> ...
|
V
...
Note how hash3 covers CS, REF#0 and REF#1 so that it is not possible to
exchange or skip any reference nodes. Unlike the picture suggests the
auth nodes themselves are not hashed.
With this it is possible for an offline attacker to cut each journal
head or to drop the last reference node(s), but not to skip any journal
heads or to reorder any operations.
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Signed-off-by: Richard Weinberger <richard@nod.at>
2018-09-07 12:36:36 +00:00
|
|
|
|
ubifs_add_auth_dirt(c, lnum);
|
|
|
|
|
|
2008-07-14 16:08:37 +00:00
|
|
|
|
ino_key_init(c, &key, host->i_ino);
|
2018-09-07 12:36:34 +00:00
|
|
|
|
err = ubifs_tnc_add(c, &key, lnum, offs, len1, hash_host);
|
2008-07-14 16:08:37 +00:00
|
|
|
|
if (err)
|
|
|
|
|
goto out_ro;
|
|
|
|
|
|
|
|
|
|
ino_key_init(c, &key, inode->i_ino);
|
2018-09-07 12:36:34 +00:00
|
|
|
|
err = ubifs_tnc_add(c, &key, lnum, offs + aligned_len1, len2, hash);
|
2008-07-14 16:08:37 +00:00
|
|
|
|
if (err)
|
|
|
|
|
goto out_ro;
|
|
|
|
|
|
|
|
|
|
finish_reservation(c);
|
|
|
|
|
spin_lock(&host_ui->ui_lock);
|
|
|
|
|
host_ui->synced_i_size = host_ui->ui_size;
|
|
|
|
|
spin_unlock(&host_ui->ui_lock);
|
|
|
|
|
mark_inode_clean(c, host_ui);
|
|
|
|
|
kfree(ino);
|
|
|
|
|
return 0;
|
|
|
|
|
|
2018-09-07 12:36:34 +00:00
|
|
|
|
out_release:
|
|
|
|
|
release_head(c, BASEHD);
|
2008-07-14 16:08:37 +00:00
|
|
|
|
out_ro:
|
|
|
|
|
ubifs_ro_mode(c, err);
|
|
|
|
|
finish_reservation(c);
|
|
|
|
|
out_free:
|
|
|
|
|
kfree(ino);
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|