Merge tag 'tags/upstream-4.20-rc1' of git://git.infradead.org/linux-ubifs

Pull UBIFS updates from Richard Weinberger:

 - Full filesystem authentication feature, UBIFS is now able to have the
   whole filesystem structure authenticated plus user data encrypted and
   authenticated.

 - Minor cleanups

* tag 'tags/upstream-4.20-rc1' of git://git.infradead.org/linux-ubifs: (26 commits)
  ubifs: Remove unneeded semicolon
  Documentation: ubifs: Add authentication whitepaper
  ubifs: Enable authentication support
  ubifs: Do not update inode size in-place in authenticated mode
  ubifs: Add hashes and HMACs to default filesystem
  ubifs: authentication: Authenticate super block node
  ubifs: Create hash for default LPT
  ubfis: authentication: Authenticate master node
  ubifs: authentication: Authenticate LPT
  ubifs: Authenticate replayed journal
  ubifs: Add auth nodes to garbage collector journal head
  ubifs: Add authentication nodes to journal
  ubifs: authentication: Add hashes to index nodes
  ubifs: Add hashes to the tree node cache
  ubifs: Create functions to embed a HMAC in a node
  ubifs: Add helper functions for authentication support
  ubifs: Add separate functions to init/crc a node
  ubifs: Format changes for authentication support
  ubifs: Store read superblock node
  ubifs: Drop write_node
  ...
This commit is contained in:
Linus Torvalds
2018-11-04 14:46:04 -08:00
25 changed files with 2436 additions and 310 deletions

View File

@@ -0,0 +1,426 @@
% UBIFS Authentication
% sigma star gmbh
% 2018
# Introduction
UBIFS utilizes the fscrypt framework to provide confidentiality for file
contents and file names. This prevents attacks where an attacker is able to
read contents of the filesystem on a single point in time. A classic example
is a lost smartphone where the attacker is unable to read personal data stored
on the device without the filesystem decryption key.
At the current state, UBIFS encryption however does not prevent attacks where
the attacker is able to modify the filesystem contents and the user uses the
device afterwards. In such a scenario an attacker can modify filesystem
contents arbitrarily without the user noticing. One example is to modify a
binary to perform a malicious action when executed [DMC-CBC-ATTACK]. Since
most of the filesystem metadata of UBIFS is stored in plain, this makes it
fairly easy to swap files and replace their contents.
Other full disk encryption systems like dm-crypt cover all filesystem metadata,
which makes such kinds of attacks more complicated, but not impossible.
Especially, if the attacker is given access to the device multiple points in
time. For dm-crypt and other filesystems that build upon the Linux block IO
layer, the dm-integrity or dm-verity subsystems [DM-INTEGRITY, DM-VERITY]
can be used to get full data authentication at the block layer.
These can also be combined with dm-crypt [CRYPTSETUP2].
This document describes an approach to get file contents _and_ full metadata
authentication for UBIFS. Since UBIFS uses fscrypt for file contents and file
name encryption, the authentication system could be tied into fscrypt such that
existing features like key derivation can be utilized. It should however also
be possible to use UBIFS authentication without using encryption.
## MTD, UBI & UBIFS
On Linux, the MTD (Memory Technology Devices) subsystem provides a uniform
interface to access raw flash devices. One of the more prominent subsystems that
work on top of MTD is UBI (Unsorted Block Images). It provides volume management
for flash devices and is thus somewhat similar to LVM for block devices. In
addition, it deals with flash-specific wear-leveling and transparent I/O error
handling. UBI offers logical erase blocks (LEBs) to the layers on top of it
and maps them transparently to physical erase blocks (PEBs) on the flash.
UBIFS is a filesystem for raw flash which operates on top of UBI. Thus, wear
leveling and some flash specifics are left to UBI, while UBIFS focuses on
scalability, performance and recoverability.
+------------+ +*******+ +-----------+ +-----+
| | * UBIFS * | UBI-BLOCK | | ... |
| JFFS/JFFS2 | +*******+ +-----------+ +-----+
| | +-----------------------------+ +-----------+ +-----+
| | | UBI | | MTD-BLOCK | | ... |
+------------+ +-----------------------------+ +-----------+ +-----+
+------------------------------------------------------------------+
| MEMORY TECHNOLOGY DEVICES (MTD) |
+------------------------------------------------------------------+
+-----------------------------+ +--------------------------+ +-----+
| NAND DRIVERS | | NOR DRIVERS | | ... |
+-----------------------------+ +--------------------------+ +-----+
Figure 1: Linux kernel subsystems for dealing with raw flash
Internally, UBIFS maintains multiple data structures which are persisted on
the flash:
- *Index*: an on-flash B+ tree where the leaf nodes contain filesystem data
- *Journal*: an additional data structure to collect FS changes before updating
the on-flash index and reduce flash wear.
- *Tree Node Cache (TNC)*: an in-memory B+ tree that reflects the current FS
state to avoid frequent flash reads. It is basically the in-memory
representation of the index, but contains additional attributes.
- *LEB property tree (LPT)*: an on-flash B+ tree for free space accounting per
UBI LEB.
In the remainder of this section we will cover the on-flash UBIFS data
structures in more detail. The TNC is of less importance here since it is never
persisted onto the flash directly. More details on UBIFS can also be found in
[UBIFS-WP].
### UBIFS Index & Tree Node Cache
Basic on-flash UBIFS entities are called *nodes*. UBIFS knows different types
of nodes. Eg. data nodes (`struct ubifs_data_node`) which store chunks of file
contents or inode nodes (`struct ubifs_ino_node`) which represent VFS inodes.
Almost all types of nodes share a common header (`ubifs_ch`) containing basic
information like node type, node length, a sequence number, etc. (see
`fs/ubifs/ubifs-media.h`in kernel source). Exceptions are entries of the LPT
and some less important node types like padding nodes which are used to pad
unusable content at the end of LEBs.
To avoid re-writing the whole B+ tree on every single change, it is implemented
as *wandering tree*, where only the changed nodes are re-written and previous
versions of them are obsoleted without erasing them right away. As a result,
the index is not stored in a single place on the flash, but *wanders* around
and there are obsolete parts on the flash as long as the LEB containing them is
not reused by UBIFS. To find the most recent version of the index, UBIFS stores
a special node called *master node* into UBI LEB 1 which always points to the
most recent root node of the UBIFS index. For recoverability, the master node
is additionally duplicated to LEB 2. Mounting UBIFS is thus a simple read of
LEB 1 and 2 to get the current master node and from there get the location of
the most recent on-flash index.
The TNC is the in-memory representation of the on-flash index. It contains some
additional runtime attributes per node which are not persisted. One of these is
a dirty-flag which marks nodes that have to be persisted the next time the
index is written onto the flash. The TNC acts as a write-back cache and all
modifications of the on-flash index are done through the TNC. Like other caches,
the TNC does not have to mirror the full index into memory, but reads parts of
it from flash whenever needed. A *commit* is the UBIFS operation of updating the
on-flash filesystem structures like the index. On every commit, the TNC nodes
marked as dirty are written to the flash to update the persisted index.
### Journal
To avoid wearing out the flash, the index is only persisted (*commited*) when
certain conditions are met (eg. `fsync(2)`). The journal is used to record
any changes (in form of inode nodes, data nodes etc.) between commits
of the index. During mount, the journal is read from the flash and replayed
onto the TNC (which will be created on-demand from the on-flash index).
UBIFS reserves a bunch of LEBs just for the journal called *log area*. The
amount of log area LEBs is configured on filesystem creation (using
`mkfs.ubifs`) and stored in the superblock node. The log area contains only
two types of nodes: *reference nodes* and *commit start nodes*. A commit start
node is written whenever an index commit is performed. Reference nodes are
written on every journal update. Each reference node points to the position of
other nodes (inode nodes, data nodes etc.) on the flash that are part of this
journal entry. These nodes are called *buds* and describe the actual filesystem
changes including their data.
The log area is maintained as a ring. Whenever the journal is almost full,
a commit is initiated. This also writes a commit start node so that during
mount, UBIFS will seek for the most recent commit start node and just replay
every reference node after that. Every reference node before the commit start
node will be ignored as they are already part of the on-flash index.
When writing a journal entry, UBIFS first ensures that enough space is
available to write the reference node and buds part of this entry. Then, the
reference node is written and afterwards the buds describing the file changes.
On replay, UBIFS will record every reference node and inspect the location of
the referenced LEBs to discover the buds. If these are corrupt or missing,
UBIFS will attempt to recover them by re-reading the LEB. This is however only
done for the last referenced LEB of the journal. Only this can become corrupt
because of a power cut. If the recovery fails, UBIFS will not mount. An error
for every other LEB will directly cause UBIFS to fail the mount operation.
| ---- LOG AREA ---- | ---------- MAIN AREA ------------ |
-----+------+-----+--------+---- ------+-----+-----+---------------
\ | | | | / / | | | \
/ CS | REF | REF | | \ \ DENT | INO | INO | /
\ | | | | / / | | | \
----+------+-----+--------+--- -------+-----+-----+----------------
| | ^ ^
| | | |
+------------------------+ |
| |
+-------------------------------+
Figure 2: UBIFS flash layout of log area with commit start nodes
(CS) and reference nodes (REF) pointing to main area
containing their buds
### LEB Property Tree/Table
The LEB property tree is used to store per-LEB information. This includes the
LEB type and amount of free and *dirty* (old, obsolete content) space [1] on
the LEB. The type is important, because UBIFS never mixes index nodes with data
nodes on a single LEB and thus each LEB has a specific purpose. This again is
useful for free space calculations. See [UBIFS-WP] for more details.
The LEB property tree again is a B+ tree, but it is much smaller than the
index. Due to its smaller size it is always written as one chunk on every
commit. Thus, saving the LPT is an atomic operation.
[1] Since LEBs can only be appended and never overwritten, there is a
difference between free space ie. the remaining space left on the LEB to be
written to without erasing it and previously written content that is obsolete
but can't be overwritten without erasing the full LEB.
# UBIFS Authentication
This chapter introduces UBIFS authentication which enables UBIFS to verify
the authenticity and integrity of metadata and file contents stored on flash.
## Threat Model
UBIFS authentication enables detection of offline data modification. While it
does not prevent it, it enables (trusted) code to check the integrity and
authenticity of on-flash file contents and filesystem metadata. This covers
attacks where file contents are swapped.
UBIFS authentication will not protect against rollback of full flash contents.
Ie. an attacker can still dump the flash and restore it at a later time without
detection. It will also not protect against partial rollback of individual
index commits. That means that an attacker is able to partially undo changes.
This is possible because UBIFS does not immediately overwrites obsolete
versions of the index tree or the journal, but instead marks them as obsolete
and garbage collection erases them at a later time. An attacker can use this by
erasing parts of the current tree and restoring old versions that are still on
the flash and have not yet been erased. This is possible, because every commit
will always write a new version of the index root node and the master node
without overwriting the previous version. This is further helped by the
wear-leveling operations of UBI which copies contents from one physical
eraseblock to another and does not atomically erase the first eraseblock.
UBIFS authentication does not cover attacks where an attacker is able to
execute code on the device after the authentication key was provided.
Additional measures like secure boot and trusted boot have to be taken to
ensure that only trusted code is executed on a device.
## Authentication
To be able to fully trust data read from flash, all UBIFS data structures
stored on flash are authenticated. That is:
- The index which includes file contents, file metadata like extended
attributes, file length etc.
- The journal which also contains file contents and metadata by recording changes
to the filesystem
- The LPT which stores UBI LEB metadata which UBIFS uses for free space accounting
### Index Authentication
Through UBIFS' concept of a wandering tree, it already takes care of only
updating and persisting changed parts from leaf node up to the root node
of the full B+ tree. This enables us to augment the index nodes of the tree
with a hash over each node's child nodes. As a result, the index basically also
a Merkle tree. Since the leaf nodes of the index contain the actual filesystem
data, the hashes of their parent index nodes thus cover all the file contents
and file metadata. When a file changes, the UBIFS index is updated accordingly
from the leaf nodes up to the root node including the master node. This process
can be hooked to recompute the hash only for each changed node at the same time.
Whenever a file is read, UBIFS can verify the hashes from each leaf node up to
the root node to ensure the node's integrity.
To ensure the authenticity of the whole index, the UBIFS master node stores a
keyed hash (HMAC) over its own contents and a hash of the root node of the index
tree. As mentioned above, the master node is always written to the flash whenever
the index is persisted (ie. on index commit).
Using this approach only UBIFS index nodes and the master node are changed to
include a hash. All other types of nodes will remain unchanged. This reduces
the storage overhead which is precious for users of UBIFS (ie. embedded
devices).
+---------------+
| Master Node |
| (hash) |
+---------------+
|
v
+-------------------+
| Index Node #1 |
| |
| branch0 branchn |
| (hash) (hash) |
+-------------------+
| ... | (fanout: 8)
| |
+-------+ +------+
| |
v v
+-------------------+ +-------------------+
| Index Node #2 | | Index Node #3 |
| | | |
| branch0 branchn | | branch0 branchn |
| (hash) (hash) | | (hash) (hash) |
+-------------------+ +-------------------+
| ... | ... |
v v v
+-----------+ +----------+ +-----------+
| Data Node | | INO Node | | DENT Node |
+-----------+ +----------+ +-----------+
Figure 3: Coverage areas of index node hash and master node HMAC
The most important part for robustness and power-cut safety is to atomically
persist the hash and file contents. Here the existing UBIFS logic for how
changed nodes are persisted is already designed for this purpose such that
UBIFS can safely recover if a power-cut occurs while persisting. Adding
hashes to index nodes does not change this since each hash will be persisted
atomically together with its respective node.
### Journal Authentication
The journal is authenticated too. Since the journal is continuously written
it is necessary to also add authentication information frequently to the
journal so that in case of a powercut not too much data can't be authenticated.
This is done by creating a continuous hash beginning from the commit start node
over the previous reference nodes, the current reference node, and the bud
nodes. From time to time whenever it is suitable authentication nodes are added
between the bud nodes. This new node type contains a HMAC over the current state
of the hash chain. That way a journal can be authenticated up to the last
authentication node. The tail of the journal which may not have a authentication
node cannot be authenticated and is skipped during journal replay.
We get this picture for journal authentication:
,,,,,,,,
,......,...........................................
,. 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
...
Since the hash also includes the reference nodes an attacker cannot reorder or
skip any journal heads for replay. An attacker can only remove bud nodes or
reference nodes from the end of the journal, effectively rewinding the
filesystem at maximum back to the last commit.
The location of the log area is stored in the master node. Since the master
node is authenticated with a HMAC as described above, it is not possible to
tamper with that without detection. The size of the log area is specified when
the filesystem is created using `mkfs.ubifs` and stored in the superblock node.
To avoid tampering with this and other values stored there, a HMAC is added to
the superblock struct. The superblock node is stored in LEB 0 and is only
modified on feature flag or similar changes, but never on file changes.
### LPT Authentication
The location of the LPT root node on the flash is stored in the UBIFS master
node. Since the LPT is written and read atomically on every commit, there is
no need to authenticate individual nodes of the tree. It suffices to
protect the integrity of the full LPT by a simple hash stored in the master
node. Since the master node itself is authenticated, the LPTs authenticity can
be verified by verifying the authenticity of the master node and comparing the
LTP hash stored there with the hash computed from the read on-flash LPT.
## Key Management
For simplicity, UBIFS authentication uses a single key to compute the HMACs
of superblock, master, commit start and reference nodes. This key has to be
available on creation of the filesystem (`mkfs.ubifs`) to authenticate the
superblock node. Further, it has to be available on mount of the filesystem
to verify authenticated nodes and generate new HMACs for changes.
UBIFS authentication is intended to operate side-by-side with UBIFS encryption
(fscrypt) to provide confidentiality and authenticity. Since UBIFS encryption
has a different approach of encryption policies per directory, there can be
multiple fscrypt master keys and there might be folders without encryption.
UBIFS authentication on the other hand has an all-or-nothing approach in the
sense that it either authenticates everything of the filesystem or nothing.
Because of this and because UBIFS authentication should also be usable without
encryption, it does not share the same master key with fscrypt, but manages
a dedicated authentication key.
The API for providing the authentication key has yet to be defined, but the
key can eg. be provided by userspace through a keyring similar to the way it
is currently done in fscrypt. It should however be noted that the current
fscrypt approach has shown its flaws and the userspace API will eventually
change [FSCRYPT-POLICY2].
Nevertheless, it will be possible for a user to provide a single passphrase
or key in userspace that covers UBIFS authentication and encryption. This can
be solved by the corresponding userspace tools which derive a second key for
authentication in addition to the derived fscrypt master key used for
encryption.
To be able to check if the proper key is available on mount, the UBIFS
superblock node will additionally store a hash of the authentication key. This
approach is similar to the approach proposed for fscrypt encryption policy v2
[FSCRYPT-POLICY2].
# Future Extensions
In certain cases where a vendor wants to provide an authenticated filesystem
image to customers, it should be possible to do so without sharing the secret
UBIFS authentication key. Instead, in addition the each HMAC a digital
signature could be stored where the vendor shares the public key alongside the
filesystem image. In case this filesystem has to be modified afterwards,
UBIFS can exchange all digital signatures with HMACs on first mount similar
to the way the IMA/EVM subsystem deals with such situations. The HMAC key
will then have to be provided beforehand in the normal way.
# References
[CRYPTSETUP2] http://www.saout.de/pipermail/dm-crypt/2017-November/005745.html
[DMC-CBC-ATTACK] http://www.jakoblell.com/blog/2013/12/22/practical-malleability-attack-against-cbc-encrypted-luks-partitions/
[DM-INTEGRITY] https://www.kernel.org/doc/Documentation/device-mapper/dm-integrity.txt
[DM-VERITY] https://www.kernel.org/doc/Documentation/device-mapper/verity.txt
[FSCRYPT-POLICY2] https://www.spinics.net/lists/linux-ext4/msg58710.html
[UBIFS-WP] http://www.linux-mtd.infradead.org/doc/ubifs_whitepaper.pdf

View File

@@ -91,6 +91,13 @@ chk_data_crc do not skip checking CRCs on data nodes
compr=none override default compressor and set it to "none"
compr=lzo override default compressor and set it to "lzo"
compr=zlib override default compressor and set it to "zlib"
auth_key= specify the key used for authenticating the filesystem.
Passing this option makes authentication mandatory.
The passed key must be present in the kernel keyring
and must be of type 'logon'
auth_hash_name= The hash algorithm used for authentication. Used for
both hashing and for creating HMACs. Typical values
include "sha256" or "sha512"
Quick usage instructions

View File

@@ -1072,6 +1072,7 @@ static int scan_peb(struct ubi_device *ubi, struct ubi_attach_info *ai,
* be a result of power cut during erasure.
*/
ai->maybe_bad_peb_count += 1;
/* fall through */
case UBI_IO_BAD_HDR:
/*
* If we're facing a bad VID header we have to drop *all*

View File

@@ -1334,8 +1334,10 @@ static int bytes_str_to_int(const char *str)
switch (*endp) {
case 'G':
result *= 1024;
/* fall through */
case 'M':
result *= 1024;
/* fall through */
case 'K':
result *= 1024;
if (endp[1] == 'i' && endp[2] == 'B')

View File

@@ -7,6 +7,7 @@ config UBIFS_FS
select CRYPTO if UBIFS_FS_ZLIB
select CRYPTO_LZO if UBIFS_FS_LZO
select CRYPTO_DEFLATE if UBIFS_FS_ZLIB
select CRYPTO_HASH_INFO
depends on MTD_UBI
help
UBIFS is a file system for flash devices which works on top of UBI.
@@ -85,3 +86,13 @@ config UBIFS_FS_SECURITY
the extended attribute support in advance.
If you are not using a security module, say N.
config UBIFS_FS_AUTHENTICATION
bool "UBIFS authentication support"
select CRYPTO_HMAC
help
Enable authentication support for UBIFS. This feature offers protection
against offline changes for both data and metadata of the filesystem.
If you say yes here you should also select a hashing algorithm such as
sha256, these are not selected automatically since there are many
different options.

View File

@@ -8,3 +8,4 @@ ubifs-y += recovery.o ioctl.o lpt_commit.o tnc_misc.o debug.o
ubifs-y += misc.o
ubifs-$(CONFIG_UBIFS_FS_ENCRYPTION) += crypto.o
ubifs-$(CONFIG_UBIFS_FS_XATTR) += xattr.o
ubifs-$(CONFIG_UBIFS_FS_AUTHENTICATION) += auth.o

502
fs/ubifs/auth.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -165,6 +165,8 @@ const char *dbg_ntype(int type)
return "commit start node";
case UBIFS_ORPH_NODE:
return "orphan node";
case UBIFS_AUTH_NODE:
return "auth node";
default:
return "unknown node";
}
@@ -542,6 +544,10 @@ void ubifs_dump_node(const struct ubifs_info *c, const void *node)
(unsigned long long)le64_to_cpu(orph->inos[i]));
break;
}
case UBIFS_AUTH_NODE:
{
break;
}
default:
pr_err("node type %d was not recognized\n",
(int)ch->node_type);

View File

@@ -254,7 +254,8 @@ static int sort_nodes(struct ubifs_info *c, struct ubifs_scan_leb *sleb,
snod->type == UBIFS_DATA_NODE ||
snod->type == UBIFS_DENT_NODE ||
snod->type == UBIFS_XENT_NODE ||
snod->type == UBIFS_TRUN_NODE);
snod->type == UBIFS_TRUN_NODE ||
snod->type == UBIFS_AUTH_NODE);
if (snod->type != UBIFS_INO_NODE &&
snod->type != UBIFS_DATA_NODE &&
@@ -364,12 +365,13 @@ static int move_nodes(struct ubifs_info *c, struct ubifs_scan_leb *sleb)
/* Write nodes to their new location. Use the first-fit strategy */
while (1) {
int avail;
int avail, moved = 0;
struct ubifs_scan_node *snod, *tmp;
/* Move data nodes */
list_for_each_entry_safe(snod, tmp, &sleb->nodes, list) {
avail = c->leb_size - wbuf->offs - wbuf->used;
avail = c->leb_size - wbuf->offs - wbuf->used -
ubifs_auth_node_sz(c);
if (snod->len > avail)
/*
* Do not skip data nodes in order to optimize
@@ -377,14 +379,21 @@ static int move_nodes(struct ubifs_info *c, struct ubifs_scan_leb *sleb)
*/
break;
err = ubifs_shash_update(c, c->jheads[GCHD].log_hash,
snod->node, snod->len);
if (err)
goto out;
err = move_node(c, sleb, snod, wbuf);
if (err)
goto out;
moved = 1;
}
/* Move non-data nodes */
list_for_each_entry_safe(snod, tmp, &nondata, list) {
avail = c->leb_size - wbuf->offs - wbuf->used;
avail = c->leb_size - wbuf->offs - wbuf->used -
ubifs_auth_node_sz(c);
if (avail < min)
break;
@@ -402,9 +411,41 @@ static int move_nodes(struct ubifs_info *c, struct ubifs_scan_leb *sleb)
continue;
}
err = ubifs_shash_update(c, c->jheads[GCHD].log_hash,
snod->node, snod->len);
if (err)
goto out;
err = move_node(c, sleb, snod, wbuf);
if (err)
goto out;
moved = 1;
}
if (ubifs_authenticated(c) && moved) {
struct ubifs_auth_node *auth;
auth = kmalloc(ubifs_auth_node_sz(c), GFP_NOFS);
if (!auth) {
err = -ENOMEM;
goto out;
}
err = ubifs_prepare_auth_node(c, auth,
c->jheads[GCHD].log_hash);
if (err) {
kfree(auth);
goto out;
}
err = ubifs_wbuf_write_nolock(wbuf, auth,
ubifs_auth_node_sz(c));
if (err) {
kfree(auth);
goto out;
}
ubifs_add_dirt(c, wbuf->lnum, ubifs_auth_node_sz(c));
}
if (list_empty(&sleb->nodes) && list_empty(&nondata))

View File

@@ -365,6 +365,68 @@ static unsigned long long next_sqnum(struct ubifs_info *c)
return sqnum;
}
void ubifs_init_node(struct ubifs_info *c, void *node, int len, int pad)
{
struct ubifs_ch *ch = node;
unsigned long long sqnum = next_sqnum(c);
ubifs_assert(c, len >= UBIFS_CH_SZ);
ch->magic = cpu_to_le32(UBIFS_NODE_MAGIC);
ch->len = cpu_to_le32(len);
ch->group_type = UBIFS_NO_NODE_GROUP;
ch->sqnum = cpu_to_le64(sqnum);
ch->padding[0] = ch->padding[1] = 0;
if (pad) {
len = ALIGN(len, 8);
pad = ALIGN(len, c->min_io_size) - len;
ubifs_pad(c, node + len, pad);
}
}
void ubifs_crc_node(struct ubifs_info *c, void *node, int len)
{
struct ubifs_ch *ch = node;
uint32_t crc;
crc = crc32(UBIFS_CRC32_INIT, node + 8, len - 8);
ch->crc = cpu_to_le32(crc);
}
/**
* ubifs_prepare_node_hmac - prepare node to be written to flash.
* @c: UBIFS file-system description object
* @node: the node to pad
* @len: node length
* @hmac_offs: offset of the HMAC in the node
* @pad: if the buffer has to be padded
*
* This function prepares node at @node to be written to the media - it
* calculates node CRC, fills the common header, and adds proper padding up to
* the next minimum I/O unit if @pad is not zero. if @hmac_offs is positive then
* a HMAC is inserted into the node at the given offset.
*
* This function returns 0 for success or a negative error code otherwise.
*/
int ubifs_prepare_node_hmac(struct ubifs_info *c, void *node, int len,
int hmac_offs, int pad)
{
int err;
ubifs_init_node(c, node, len, pad);
if (hmac_offs > 0) {
err = ubifs_node_insert_hmac(c, node, len, hmac_offs);
if (err)
return err;
}
ubifs_crc_node(c, node, len);
return 0;
}
/**
* ubifs_prepare_node - prepare node to be written to flash.
* @c: UBIFS file-system description object
@@ -378,25 +440,11 @@ static unsigned long long next_sqnum(struct ubifs_info *c)
*/
void ubifs_prepare_node(struct ubifs_info *c, void *node, int len, int pad)
{
uint32_t crc;
struct ubifs_ch *ch = node;
unsigned long long sqnum = next_sqnum(c);
ubifs_assert(c, len >= UBIFS_CH_SZ);
ch->magic = cpu_to_le32(UBIFS_NODE_MAGIC);
ch->len = cpu_to_le32(len);
ch->group_type = UBIFS_NO_NODE_GROUP;
ch->sqnum = cpu_to_le64(sqnum);
ch->padding[0] = ch->padding[1] = 0;
crc = crc32(UBIFS_CRC32_INIT, node + 8, len - 8);
ch->crc = cpu_to_le32(crc);
if (pad) {
len = ALIGN(len, 8);
pad = ALIGN(len, c->min_io_size) - len;
ubifs_pad(c, node + len, pad);
}
/*
* Deliberately ignore return value since this function can only fail
* when a hmac offset is given.
*/
ubifs_prepare_node_hmac(c, node, len, 0, pad);
}
/**
@@ -848,6 +896,48 @@ out:
return err;
}
/**
* ubifs_write_node_hmac - write node to the media.
* @c: UBIFS file-system description object
* @buf: the node to write
* @len: node length
* @lnum: logical eraseblock number
* @offs: offset within the logical eraseblock
* @hmac_offs: offset of the HMAC within the node
*
* This function automatically fills node magic number, assigns sequence
* number, and calculates node CRC checksum. The length of the @buf buffer has
* to be aligned to the minimal I/O unit size. This function automatically
* appends padding node and padding bytes if needed. Returns zero in case of
* success and a negative error code in case of failure.
*/
int ubifs_write_node_hmac(struct ubifs_info *c, void *buf, int len, int lnum,
int offs, int hmac_offs)
{
int err, buf_len = ALIGN(len, c->min_io_size);
dbg_io("LEB %d:%d, %s, length %d (aligned %d)",
lnum, offs, dbg_ntype(((struct ubifs_ch *)buf)->node_type), len,
buf_len);
ubifs_assert(c, lnum >= 0 && lnum < c->leb_cnt && offs >= 0);
ubifs_assert(c, offs % c->min_io_size == 0 && offs < c->leb_size);
ubifs_assert(c, !c->ro_media && !c->ro_mount);
ubifs_assert(c, !c->space_fixup);
if (c->ro_error)
return -EROFS;
err = ubifs_prepare_node_hmac(c, buf, len, hmac_offs, 1);
if (err)
return err;
err = ubifs_leb_write(c, lnum, buf, offs, buf_len);
if (err)
ubifs_dump_node(c, buf);
return err;
}
/**
* ubifs_write_node - write node to the media.
* @c: UBIFS file-system description object
@@ -865,25 +955,7 @@ out:
int ubifs_write_node(struct ubifs_info *c, void *buf, int len, int lnum,
int offs)
{
int err, buf_len = ALIGN(len, c->min_io_size);
dbg_io("LEB %d:%d, %s, length %d (aligned %d)",
lnum, offs, dbg_ntype(((struct ubifs_ch *)buf)->node_type), len,
buf_len);
ubifs_assert(c, lnum >= 0 && lnum < c->leb_cnt && offs >= 0);
ubifs_assert(c, offs % c->min_io_size == 0 && offs < c->leb_size);
ubifs_assert(c, !c->ro_media && !c->ro_mount);
ubifs_assert(c, !c->space_fixup);
if (c->ro_error)
return -EROFS;
ubifs_prepare_node(c, buf, len, 1);
err = ubifs_leb_write(c, lnum, buf, offs, buf_len);
if (err)
ubifs_dump_node(c, buf);
return err;
return ubifs_write_node_hmac(c, buf, len, lnum, offs, -1);
}
/**

File diff suppressed because it is too large Load Diff

View File

@@ -236,6 +236,7 @@ int ubifs_add_bud_to_log(struct ubifs_info *c, int jhead, int lnum, int offs)
bud->lnum = lnum;
bud->start = offs;
bud->jhead = jhead;
bud->log_hash = NULL;
ref->ch.node_type = UBIFS_REF_NODE;
ref->lnum = cpu_to_le32(bud->lnum);
@@ -275,6 +276,14 @@ int ubifs_add_bud_to_log(struct ubifs_info *c, int jhead, int lnum, int offs)
if (err)
goto out_unlock;
err = ubifs_shash_update(c, c->log_hash, ref, UBIFS_REF_NODE_SZ);
if (err)
goto out_unlock;
err = ubifs_shash_copy_state(c, c->log_hash, c->jheads[jhead].log_hash);
if (err)
goto out_unlock;
c->lhead_offs += c->ref_node_alsz;
ubifs_add_bud(c, bud);
@@ -377,6 +386,14 @@ int ubifs_log_start_commit(struct ubifs_info *c, int *ltail_lnum)
cs->cmt_no = cpu_to_le64(c->cmt_no);
ubifs_prepare_node(c, cs, UBIFS_CS_NODE_SZ, 0);
err = ubifs_shash_init(c, c->log_hash);
if (err)
goto out;
err = ubifs_shash_update(c, c->log_hash, cs, UBIFS_CS_NODE_SZ);
if (err < 0)
goto out;
/*
* Note, we do not lock 'c->log_mutex' because this is the commit start
* phase and we are exclusively using the log. And we do not lock
@@ -402,6 +419,12 @@ int ubifs_log_start_commit(struct ubifs_info *c, int *ltail_lnum)
ubifs_prepare_node(c, ref, UBIFS_REF_NODE_SZ, 0);
len += UBIFS_REF_NODE_SZ;
err = ubifs_shash_update(c, c->log_hash, ref,
UBIFS_REF_NODE_SZ);
if (err)
goto out;
ubifs_shash_copy_state(c, c->log_hash, c->jheads[i].log_hash);
}
ubifs_pad(c, buf + len, ALIGN(len, c->min_io_size) - len);
@@ -516,6 +539,7 @@ int ubifs_log_post_commit(struct ubifs_info *c, int old_ltail_lnum)
if (err)
return err;
list_del(&bud->list);
kfree(bud->log_hash);
kfree(bud);
}
mutex_lock(&c->log_mutex);

View File

@@ -604,11 +604,12 @@ static int calc_pnode_num_from_parent(const struct ubifs_info *c,
* @lpt_first: LEB number of first LPT LEB
* @lpt_lebs: number of LEBs for LPT is passed and returned here
* @big_lpt: use big LPT model is passed and returned here
* @hash: hash of the LPT is returned here
*
* This function returns %0 on success and a negative error code on failure.
*/
int ubifs_create_dflt_lpt(struct ubifs_info *c, int *main_lebs, int lpt_first,
int *lpt_lebs, int *big_lpt)
int *lpt_lebs, int *big_lpt, u8 *hash)
{
int lnum, err = 0, node_sz, iopos, i, j, cnt, len, alen, row;
int blnum, boffs, bsz, bcnt;
@@ -617,6 +618,7 @@ int ubifs_create_dflt_lpt(struct ubifs_info *c, int *main_lebs, int lpt_first,
void *buf = NULL, *p;
struct ubifs_lpt_lprops *ltab = NULL;
int *lsave = NULL;
struct shash_desc *desc;
err = calc_dflt_lpt_geom(c, main_lebs, big_lpt);
if (err)
@@ -630,6 +632,10 @@ int ubifs_create_dflt_lpt(struct ubifs_info *c, int *main_lebs, int lpt_first,
/* Needed by 'ubifs_pack_lsave()' */
c->main_first = c->leb_cnt - *main_lebs;
desc = ubifs_hash_get_desc(c);
if (IS_ERR(desc))
return PTR_ERR(desc);
lsave = kmalloc_array(c->lsave_cnt, sizeof(int), GFP_KERNEL);
pnode = kzalloc(sizeof(struct ubifs_pnode), GFP_KERNEL);
nnode = kzalloc(sizeof(struct ubifs_nnode), GFP_KERNEL);
@@ -677,6 +683,10 @@ int ubifs_create_dflt_lpt(struct ubifs_info *c, int *main_lebs, int lpt_first,
/* Add first pnode */
ubifs_pack_pnode(c, p, pnode);
err = ubifs_shash_update(c, desc, p, c->pnode_sz);
if (err)
goto out;
p += c->pnode_sz;
len = c->pnode_sz;
pnode->num += 1;
@@ -711,6 +721,10 @@ int ubifs_create_dflt_lpt(struct ubifs_info *c, int *main_lebs, int lpt_first,
len = 0;
}
ubifs_pack_pnode(c, p, pnode);
err = ubifs_shash_update(c, desc, p, c->pnode_sz);
if (err)
goto out;
p += c->pnode_sz;
len += c->pnode_sz;
/*
@@ -830,6 +844,10 @@ int ubifs_create_dflt_lpt(struct ubifs_info *c, int *main_lebs, int lpt_first,
if (err)
goto out;
err = ubifs_shash_final(c, desc, hash);
if (err)
goto out;
c->nhead_lnum = lnum;
c->nhead_offs = ALIGN(len, c->min_io_size);
@@ -853,6 +871,7 @@ int ubifs_create_dflt_lpt(struct ubifs_info *c, int *main_lebs, int lpt_first,
dbg_lp("LPT lsave is at %d:%d", c->lsave_lnum, c->lsave_offs);
out:
c->ltab = NULL;
kfree(desc);
kfree(lsave);
vfree(ltab);
vfree(buf);
@@ -1439,26 +1458,25 @@ struct ubifs_pnode *ubifs_get_pnode(struct ubifs_info *c,
}
/**
* ubifs_lpt_lookup - lookup LEB properties in the LPT.
* ubifs_pnode_lookup - lookup a pnode in the LPT.
* @c: UBIFS file-system description object
* @lnum: LEB number to lookup
* @i: pnode number (0 to (main_lebs - 1) / UBIFS_LPT_FANOUT)
*
* This function returns a pointer to the LEB properties on success or a
* negative error code on failure.
* This function returns a pointer to the pnode on success or a negative
* error code on failure.
*/
struct ubifs_lprops *ubifs_lpt_lookup(struct ubifs_info *c, int lnum)
struct ubifs_pnode *ubifs_pnode_lookup(struct ubifs_info *c, int i)
{
int err, i, h, iip, shft;
int err, h, iip, shft;
struct ubifs_nnode *nnode;
struct ubifs_pnode *pnode;
if (!c->nroot) {
err = ubifs_read_nnode(c, NULL, 0);
if (err)
return ERR_PTR(err);
}
i <<= UBIFS_LPT_FANOUT_SHIFT;
nnode = c->nroot;
i = lnum - c->main_first;
shft = c->lpt_hght * UBIFS_LPT_FANOUT_SHIFT;
for (h = 1; h < c->lpt_hght; h++) {
iip = ((i >> shft) & (UBIFS_LPT_FANOUT - 1));
@@ -1468,7 +1486,24 @@ struct ubifs_lprops *ubifs_lpt_lookup(struct ubifs_info *c, int lnum)
return ERR_CAST(nnode);
}
iip = ((i >> shft) & (UBIFS_LPT_FANOUT - 1));
pnode = ubifs_get_pnode(c, nnode, iip);
return ubifs_get_pnode(c, nnode, iip);
}
/**
* ubifs_lpt_lookup - lookup LEB properties in the LPT.
* @c: UBIFS file-system description object
* @lnum: LEB number to lookup
*
* This function returns a pointer to the LEB properties on success or a
* negative error code on failure.
*/
struct ubifs_lprops *ubifs_lpt_lookup(struct ubifs_info *c, int lnum)
{
int i, iip;
struct ubifs_pnode *pnode;
i = lnum - c->main_first;
pnode = ubifs_pnode_lookup(c, i >> UBIFS_LPT_FANOUT_SHIFT);
if (IS_ERR(pnode))
return ERR_CAST(pnode);
iip = (i & (UBIFS_LPT_FANOUT - 1));
@@ -1619,6 +1654,131 @@ struct ubifs_lprops *ubifs_lpt_lookup_dirty(struct ubifs_info *c, int lnum)
return &pnode->lprops[iip];
}
/**
* ubifs_lpt_calc_hash - Calculate hash of the LPT pnodes
* @c: UBIFS file-system description object
* @hash: the returned hash of the LPT pnodes
*
* This function iterates over the LPT pnodes and creates a hash over them.
* Returns 0 for success or a negative error code otherwise.
*/
int ubifs_lpt_calc_hash(struct ubifs_info *c, u8 *hash)
{
struct ubifs_nnode *nnode, *nn;
struct ubifs_cnode *cnode;
struct shash_desc *desc;
int iip = 0, i;
int bufsiz = max_t(int, c->nnode_sz, c->pnode_sz);
void *buf;
int err;
if (!ubifs_authenticated(c))
return 0;
desc = ubifs_hash_get_desc(c);
if (IS_ERR(desc))
return PTR_ERR(desc);
buf = kmalloc(bufsiz, GFP_NOFS);
if (!buf) {
err = -ENOMEM;
goto out;
}
if (!c->nroot) {
err = ubifs_read_nnode(c, NULL, 0);
if (err)
return err;
}
cnode = (struct ubifs_cnode *)c->nroot;
while (cnode) {
nnode = cnode->parent;
nn = (struct ubifs_nnode *)cnode;
if (cnode->level > 1) {
while (iip < UBIFS_LPT_FANOUT) {
if (nn->nbranch[iip].lnum == 0) {
/* Go right */
iip++;
continue;
}
nnode = ubifs_get_nnode(c, nn, iip);
if (IS_ERR(nnode)) {
err = PTR_ERR(nnode);
goto out;
}
/* Go down */
iip = 0;
cnode = (struct ubifs_cnode *)nnode;
break;
}
if (iip < UBIFS_LPT_FANOUT)
continue;
} else {
struct ubifs_pnode *pnode;
for (i = 0; i < UBIFS_LPT_FANOUT; i++) {
if (nn->nbranch[i].lnum == 0)
continue;
pnode = ubifs_get_pnode(c, nn, i);
if (IS_ERR(pnode)) {
err = PTR_ERR(pnode);
goto out;
}
ubifs_pack_pnode(c, buf, pnode);
err = ubifs_shash_update(c, desc, buf,
c->pnode_sz);
if (err)
goto out;
}
}
/* Go up and to the right */
iip = cnode->iip + 1;
cnode = (struct ubifs_cnode *)nnode;
}
err = ubifs_shash_final(c, desc, hash);
out:
kfree(desc);
kfree(buf);
return err;
}
/**
* lpt_check_hash - check the hash of the LPT.
* @c: UBIFS file-system description object
*
* This function calculates a hash over all pnodes in the LPT and compares it with
* the hash stored in the master node. Returns %0 on success and a negative error
* code on failure.
*/
static int lpt_check_hash(struct ubifs_info *c)
{
int err;
u8 hash[UBIFS_HASH_ARR_SZ];
if (!ubifs_authenticated(c))
return 0;
err = ubifs_lpt_calc_hash(c, hash);
if (err)
return err;
if (ubifs_check_hash(c, c->mst_node->hash_lpt, hash)) {
err = -EPERM;
ubifs_err(c, "Failed to authenticate LPT");
} else {
err = 0;
}
return err;
}
/**
* lpt_init_rd - initialize the LPT for reading.
* @c: UBIFS file-system description object
@@ -1660,6 +1820,10 @@ static int lpt_init_rd(struct ubifs_info *c)
if (err)
return err;
err = lpt_check_hash(c);
if (err)
return err;
dbg_lp("space_bits %d", c->space_bits);
dbg_lp("lpt_lnum_bits %d", c->lpt_lnum_bits);
dbg_lp("lpt_offs_bits %d", c->lpt_offs_bits);

View File

@@ -618,38 +618,6 @@ static struct ubifs_pnode *next_pnode_to_dirty(struct ubifs_info *c,
return ubifs_get_pnode(c, nnode, iip);
}
/**
* pnode_lookup - lookup a pnode in the LPT.
* @c: UBIFS file-system description object
* @i: pnode number (0 to (main_lebs - 1) / UBIFS_LPT_FANOUT))
*
* This function returns a pointer to the pnode on success or a negative
* error code on failure.
*/
static struct ubifs_pnode *pnode_lookup(struct ubifs_info *c, int i)
{
int err, h, iip, shft;
struct ubifs_nnode *nnode;
if (!c->nroot) {
err = ubifs_read_nnode(c, NULL, 0);
if (err)
return ERR_PTR(err);
}
i <<= UBIFS_LPT_FANOUT_SHIFT;
nnode = c->nroot;
shft = c->lpt_hght * UBIFS_LPT_FANOUT_SHIFT;
for (h = 1; h < c->lpt_hght; h++) {
iip = ((i >> shft) & (UBIFS_LPT_FANOUT - 1));
shft -= UBIFS_LPT_FANOUT_SHIFT;
nnode = ubifs_get_nnode(c, nnode, iip);
if (IS_ERR(nnode))
return ERR_CAST(nnode);
}
iip = ((i >> shft) & (UBIFS_LPT_FANOUT - 1));
return ubifs_get_pnode(c, nnode, iip);
}
/**
* add_pnode_dirt - add dirty space to LPT LEB properties.
* @c: UBIFS file-system description object
@@ -702,7 +670,7 @@ static int make_tree_dirty(struct ubifs_info *c)
{
struct ubifs_pnode *pnode;
pnode = pnode_lookup(c, 0);
pnode = ubifs_pnode_lookup(c, 0);
if (IS_ERR(pnode))
return PTR_ERR(pnode);
@@ -956,7 +924,7 @@ static int make_pnode_dirty(struct ubifs_info *c, int node_num, int lnum,
struct ubifs_pnode *pnode;
struct ubifs_nbranch *branch;
pnode = pnode_lookup(c, node_num);
pnode = ubifs_pnode_lookup(c, node_num);
if (IS_ERR(pnode))
return PTR_ERR(pnode);
branch = &pnode->parent->nbranch[pnode->iip];
@@ -1279,6 +1247,10 @@ int ubifs_lpt_start_commit(struct ubifs_info *c)
if (err)
goto out;
err = ubifs_lpt_calc_hash(c, c->mst_node->hash_lpt);
if (err)
goto out;
/* Copy the LPT's own lprops for end commit to write */
memcpy(c->ltab_cmt, c->ltab,
sizeof(struct ubifs_lpt_lprops) * c->lpt_lebs);
@@ -1558,7 +1530,7 @@ static int dbg_is_pnode_dirty(struct ubifs_info *c, int lnum, int offs)
struct ubifs_nbranch *branch;
cond_resched();
pnode = pnode_lookup(c, i);
pnode = ubifs_pnode_lookup(c, i);
if (IS_ERR(pnode))
return PTR_ERR(pnode);
branch = &pnode->parent->nbranch[pnode->iip];
@@ -1710,7 +1682,7 @@ int dbg_check_ltab(struct ubifs_info *c)
for (i = 0; i < cnt; i++) {
struct ubifs_pnode *pnode;
pnode = pnode_lookup(c, i);
pnode = ubifs_pnode_lookup(c, i);
if (IS_ERR(pnode))
return PTR_ERR(pnode);
cond_resched();

View File

@@ -24,6 +24,42 @@
#include "ubifs.h"
/**
* ubifs_compare_master_node - compare two UBIFS master nodes
* @c: UBIFS file-system description object
* @m1: the first node
* @m2: the second node
*
* This function compares two UBIFS master nodes. Returns 0 if they are equal
* and nonzero if not.
*/
int ubifs_compare_master_node(struct ubifs_info *c, void *m1, void *m2)
{
int ret;
int behind;
int hmac_offs = offsetof(struct ubifs_mst_node, hmac);
/*
* Do not compare the common node header since the sequence number and
* hence the CRC are different.
*/
ret = memcmp(m1 + UBIFS_CH_SZ, m2 + UBIFS_CH_SZ,
hmac_offs - UBIFS_CH_SZ);
if (ret)
return ret;
/*
* Do not compare the embedded HMAC aswell which also must be different
* due to the different common node header.
*/
behind = hmac_offs + UBIFS_MAX_HMAC_LEN;
if (UBIFS_MST_NODE_SZ > behind)
return memcmp(m1 + behind, m2 + behind, UBIFS_MST_NODE_SZ - behind);
return 0;
}
/**
* scan_for_master - search the valid master node.
* @c: UBIFS file-system description object
@@ -37,7 +73,7 @@ static int scan_for_master(struct ubifs_info *c)
{
struct ubifs_scan_leb *sleb;
struct ubifs_scan_node *snod;
int lnum, offs = 0, nodes_cnt;
int lnum, offs = 0, nodes_cnt, err;
lnum = UBIFS_MST_LNUM;
@@ -69,12 +105,23 @@ static int scan_for_master(struct ubifs_info *c)
goto out_dump;
if (snod->offs != offs)
goto out;
if (memcmp((void *)c->mst_node + UBIFS_CH_SZ,
(void *)snod->node + UBIFS_CH_SZ,
UBIFS_MST_NODE_SZ - UBIFS_CH_SZ))
if (ubifs_compare_master_node(c, c->mst_node, snod->node))
goto out;
c->mst_offs = offs;
ubifs_scan_destroy(sleb);
if (!ubifs_authenticated(c))
return 0;
err = ubifs_node_verify_hmac(c, c->mst_node,
sizeof(struct ubifs_mst_node),
offsetof(struct ubifs_mst_node, hmac));
if (err) {
ubifs_err(c, "Failed to verify master node HMAC");
return -EPERM;
}
return 0;
out:
@@ -305,6 +352,8 @@ int ubifs_read_master(struct ubifs_info *c)
c->lst.total_dead = le64_to_cpu(c->mst_node->total_dead);
c->lst.total_dark = le64_to_cpu(c->mst_node->total_dark);
ubifs_copy_hash(c, c->mst_node->hash_root_idx, c->zroot.hash);
c->calc_idx_sz = c->bi.old_idx_sz;
if (c->mst_node->flags & cpu_to_le32(UBIFS_MST_NO_ORPHS))
@@ -378,7 +427,9 @@ int ubifs_write_master(struct ubifs_info *c)
c->mst_offs = offs;
c->mst_node->highest_inum = cpu_to_le64(c->highest_inum);
err = ubifs_write_node(c, c->mst_node, len, lnum, offs);
ubifs_copy_hash(c, c->zroot.hash, c->mst_node->hash_root_idx);
err = ubifs_write_node_hmac(c, c->mst_node, len, lnum, offs,
offsetof(struct ubifs_mst_node, hmac));
if (err)
return err;
@@ -389,7 +440,8 @@ int ubifs_write_master(struct ubifs_info *c)
if (err)
return err;
}
err = ubifs_write_node(c, c->mst_node, len, lnum, offs);
err = ubifs_write_node_hmac(c, c->mst_node, len, lnum, offs,
offsetof(struct ubifs_mst_node, hmac));
return err;
}

View File

@@ -197,7 +197,8 @@ static inline int ubifs_return_leb(struct ubifs_info *c, int lnum)
*/
static inline int ubifs_idx_node_sz(const struct ubifs_info *c, int child_cnt)
{
return UBIFS_IDX_NODE_SZ + (UBIFS_BRANCH_SZ + c->key_len) * child_cnt;
return UBIFS_IDX_NODE_SZ + (UBIFS_BRANCH_SZ + c->key_len + c->hash_len)
* child_cnt;
}
/**
@@ -212,7 +213,7 @@ struct ubifs_branch *ubifs_idx_branch(const struct ubifs_info *c,
int bnum)
{
return (struct ubifs_branch *)((void *)idx->branches +
(UBIFS_BRANCH_SZ + c->key_len) * bnum);
(UBIFS_BRANCH_SZ + c->key_len + c->hash_len) * bnum);
}
/**

View File

@@ -212,7 +212,10 @@ static int write_rcvrd_mst_node(struct ubifs_info *c,
save_flags = mst->flags;
mst->flags |= cpu_to_le32(UBIFS_MST_RCVRY);
ubifs_prepare_node(c, mst, UBIFS_MST_NODE_SZ, 1);
err = ubifs_prepare_node_hmac(c, mst, UBIFS_MST_NODE_SZ,
offsetof(struct ubifs_mst_node, hmac), 1);
if (err)
goto out;
err = ubifs_leb_change(c, lnum, mst, sz);
if (err)
goto out;
@@ -264,9 +267,7 @@ int ubifs_recover_master_node(struct ubifs_info *c)
offs2 = (void *)mst2 - buf2;
if (offs1 == offs2) {
/* Same offset, so must be the same */
if (memcmp((void *)mst1 + UBIFS_CH_SZ,
(void *)mst2 + UBIFS_CH_SZ,
UBIFS_MST_NODE_SZ - UBIFS_CH_SZ))
if (ubifs_compare_master_node(c, mst1, mst2))
goto out_err;
mst = mst1;
} else if (offs2 + sz == offs1) {
@@ -1461,16 +1462,82 @@ out:
return err;
}
/**
* inode_fix_size - fix inode size
* @c: UBIFS file-system description object
* @e: inode size information for recovery
*/
static int inode_fix_size(struct ubifs_info *c, struct size_entry *e)
{
struct inode *inode;
struct ubifs_inode *ui;
int err;
if (c->ro_mount)
ubifs_assert(c, !e->inode);
if (e->inode) {
/* Remounting rw, pick up inode we stored earlier */
inode = e->inode;
} else {
inode = ubifs_iget(c->vfs_sb, e->inum);
if (IS_ERR(inode))
return PTR_ERR(inode);
if (inode->i_size >= e->d_size) {
/*
* The original inode in the index already has a size
* big enough, nothing to do
*/
iput(inode);
return 0;
}
dbg_rcvry("ino %lu size %lld -> %lld",
(unsigned long)e->inum,
inode->i_size, e->d_size);
ui = ubifs_inode(inode);
inode->i_size = e->d_size;
ui->ui_size = e->d_size;
ui->synced_i_size = e->d_size;
e->inode = inode;
}
/*
* In readonly mode just keep the inode pinned in memory until we go
* readwrite. In readwrite mode write the inode to the journal with the
* fixed size.
*/
if (c->ro_mount)
return 0;
err = ubifs_jnl_write_inode(c, inode);
iput(inode);
if (err)
return err;
rb_erase(&e->rb, &c->size_tree);
kfree(e);
return 0;
}
/**
* ubifs_recover_size - recover inode size.
* @c: UBIFS file-system description object
* @in_place: If true, do a in-place size fixup
*
* This function attempts to fix inode size discrepancies identified by the
* 'ubifs_recover_size_accum()' function.
*
* This functions returns %0 on success and a negative error code on failure.
*/
int ubifs_recover_size(struct ubifs_info *c)
int ubifs_recover_size(struct ubifs_info *c, bool in_place)
{
struct rb_node *this = rb_first(&c->size_tree);
@@ -1479,6 +1546,9 @@ int ubifs_recover_size(struct ubifs_info *c)
int err;
e = rb_entry(this, struct size_entry, rb);
this = rb_next(this);
if (!e->exists) {
union ubifs_key key;
@@ -1502,40 +1572,26 @@ int ubifs_recover_size(struct ubifs_info *c)
}
if (e->exists && e->i_size < e->d_size) {
if (c->ro_mount) {
/* Fix the inode size and pin it in memory */
struct inode *inode;
struct ubifs_inode *ui;
ubifs_assert(c, !(c->ro_mount && in_place));
ubifs_assert(c, !e->inode);
/*
* We found data that is outside the found inode size,
* fixup the inode size
*/
inode = ubifs_iget(c->vfs_sb, e->inum);
if (IS_ERR(inode))
return PTR_ERR(inode);
ui = ubifs_inode(inode);
if (inode->i_size < e->d_size) {
dbg_rcvry("ino %lu size %lld -> %lld",
(unsigned long)e->inum,
inode->i_size, e->d_size);
inode->i_size = e->d_size;
ui->ui_size = e->d_size;
ui->synced_i_size = e->d_size;
e->inode = inode;
this = rb_next(this);
continue;
}
iput(inode);
} else {
/* Fix the size in place */
if (in_place) {
err = fix_size_in_place(c, e);
if (err)
return err;
iput(e->inode);
} else {
err = inode_fix_size(c, e);
if (err)
return err;
continue;
}
}
this = rb_next(this);
rb_erase(&e->rb, &c->size_tree);
kfree(e);
}

View File

@@ -34,6 +34,8 @@
#include "ubifs.h"
#include <linux/list_sort.h>
#include <crypto/hash.h>
#include <crypto/algapi.h>
/**
* struct replay_entry - replay list entry.
@@ -56,6 +58,7 @@ struct replay_entry {
int lnum;
int offs;
int len;
u8 hash[UBIFS_HASH_ARR_SZ];
unsigned int deletion:1;
unsigned long long sqnum;
struct list_head list;
@@ -228,7 +231,7 @@ static int apply_replay_entry(struct ubifs_info *c, struct replay_entry *r)
err = ubifs_tnc_remove_nm(c, &r->key, &r->nm);
else
err = ubifs_tnc_add_nm(c, &r->key, r->lnum, r->offs,
r->len, &r->nm);
r->len, r->hash, &r->nm);
} else {
if (r->deletion)
switch (key_type(c, &r->key)) {
@@ -248,7 +251,7 @@ static int apply_replay_entry(struct ubifs_info *c, struct replay_entry *r)
}
else
err = ubifs_tnc_add(c, &r->key, r->lnum, r->offs,
r->len);
r->len, r->hash);
if (err)
return err;
@@ -352,9 +355,9 @@ static void destroy_replay_list(struct ubifs_info *c)
* in case of success and a negative error code in case of failure.
*/
static int insert_node(struct ubifs_info *c, int lnum, int offs, int len,
union ubifs_key *key, unsigned long long sqnum,
int deletion, int *used, loff_t old_size,
loff_t new_size)
const u8 *hash, union ubifs_key *key,
unsigned long long sqnum, int deletion, int *used,
loff_t old_size, loff_t new_size)
{
struct replay_entry *r;
@@ -372,6 +375,7 @@ static int insert_node(struct ubifs_info *c, int lnum, int offs, int len,
r->lnum = lnum;
r->offs = offs;
r->len = len;
ubifs_copy_hash(c, hash, r->hash);
r->deletion = !!deletion;
r->sqnum = sqnum;
key_copy(c, key, &r->key);
@@ -400,8 +404,9 @@ static int insert_node(struct ubifs_info *c, int lnum, int offs, int len,
* negative error code in case of failure.
*/
static int insert_dent(struct ubifs_info *c, int lnum, int offs, int len,
union ubifs_key *key, const char *name, int nlen,
unsigned long long sqnum, int deletion, int *used)
const u8 *hash, union ubifs_key *key,
const char *name, int nlen, unsigned long long sqnum,
int deletion, int *used)
{
struct replay_entry *r;
char *nbuf;
@@ -425,6 +430,7 @@ static int insert_dent(struct ubifs_info *c, int lnum, int offs, int len,
r->lnum = lnum;
r->offs = offs;
r->len = len;
ubifs_copy_hash(c, hash, r->hash);
r->deletion = !!deletion;
r->sqnum = sqnum;
key_copy(c, key, &r->key);
@@ -527,6 +533,105 @@ static int is_last_bud(struct ubifs_info *c, struct ubifs_bud *bud)
return data == 0xFFFFFFFF;
}
/**
* authenticate_sleb - authenticate one scan LEB
* @c: UBIFS file-system description object
* @sleb: the scan LEB to authenticate
* @log_hash:
* @is_last: if true, this is is the last LEB
*
* This function iterates over the buds of a single LEB authenticating all buds
* with the authentication nodes on this LEB. Authentication nodes are written
* after some buds and contain a HMAC covering the authentication node itself
* and the buds between the last authentication node and the current
* authentication node. It can happen that the last buds cannot be authenticated
* because a powercut happened when some nodes were written but not the
* corresponding authentication node. This function returns the number of nodes
* that could be authenticated or a negative error code.
*/
static int authenticate_sleb(struct ubifs_info *c, struct ubifs_scan_leb *sleb,
struct shash_desc *log_hash, int is_last)
{
int n_not_auth = 0;
struct ubifs_scan_node *snod;
int n_nodes = 0;
int err;
u8 *hash, *hmac;
if (!ubifs_authenticated(c))
return sleb->nodes_cnt;
hash = kmalloc(crypto_shash_descsize(c->hash_tfm), GFP_NOFS);
hmac = kmalloc(c->hmac_desc_len, GFP_NOFS);
if (!hash || !hmac) {
err = -ENOMEM;
goto out;
}
list_for_each_entry(snod, &sleb->nodes, list) {
n_nodes++;
if (snod->type == UBIFS_AUTH_NODE) {
struct ubifs_auth_node *auth = snod->node;
SHASH_DESC_ON_STACK(hash_desc, c->hash_tfm);
SHASH_DESC_ON_STACK(hmac_desc, c->hmac_tfm);
hash_desc->tfm = c->hash_tfm;
hash_desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP;
ubifs_shash_copy_state(c, log_hash, hash_desc);
err = crypto_shash_final(hash_desc, hash);
if (err)
goto out;
hmac_desc->tfm = c->hmac_tfm;
hmac_desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP;
err = crypto_shash_digest(hmac_desc, hash, c->hash_len,
hmac);
if (err)
goto out;
err = ubifs_check_hmac(c, auth->hmac, hmac);
if (err) {
err = -EPERM;
goto out;
}
n_not_auth = 0;
} else {
err = crypto_shash_update(log_hash, snod->node,
snod->len);
if (err)
goto out;
n_not_auth++;
}
}
/*
* A powercut can happen when some nodes were written, but not yet
* the corresponding authentication node. This may only happen on
* the last bud though.
*/
if (n_not_auth) {
if (is_last) {
dbg_mnt("%d unauthenticated nodes found on LEB %d, Ignoring them",
n_not_auth, sleb->lnum);
err = 0;
} else {
dbg_mnt("%d unauthenticated nodes found on non-last LEB %d",
n_not_auth, sleb->lnum);
err = -EPERM;
}
} else {
err = 0;
}
out:
kfree(hash);
kfree(hmac);
return err ? err : n_nodes - n_not_auth;
}
/**
* replay_bud - replay a bud logical eraseblock.
* @c: UBIFS file-system description object
@@ -540,6 +645,7 @@ static int replay_bud(struct ubifs_info *c, struct bud_entry *b)
{
int is_last = is_last_bud(c, b->bud);
int err = 0, used = 0, lnum = b->bud->lnum, offs = b->bud->start;
int n_nodes, n = 0;
struct ubifs_scan_leb *sleb;
struct ubifs_scan_node *snod;
@@ -559,6 +665,15 @@ static int replay_bud(struct ubifs_info *c, struct bud_entry *b)
if (IS_ERR(sleb))
return PTR_ERR(sleb);
n_nodes = authenticate_sleb(c, sleb, b->bud->log_hash, is_last);
if (n_nodes < 0) {
err = n_nodes;
goto out;
}
ubifs_shash_copy_state(c, b->bud->log_hash,
c->jheads[b->bud->jhead].log_hash);
/*
* The bud does not have to start from offset zero - the beginning of
* the 'lnum' LEB may contain previously committed data. One of the
@@ -582,6 +697,7 @@ static int replay_bud(struct ubifs_info *c, struct bud_entry *b)
*/
list_for_each_entry(snod, &sleb->nodes, list) {
u8 hash[UBIFS_HASH_ARR_SZ];
int deletion = 0;
cond_resched();
@@ -591,6 +707,8 @@ static int replay_bud(struct ubifs_info *c, struct bud_entry *b)
goto out_dump;
}
ubifs_node_calc_hash(c, snod->node, hash);
if (snod->sqnum > c->max_sqnum)
c->max_sqnum = snod->sqnum;
@@ -602,7 +720,7 @@ static int replay_bud(struct ubifs_info *c, struct bud_entry *b)
if (le32_to_cpu(ino->nlink) == 0)
deletion = 1;
err = insert_node(c, lnum, snod->offs, snod->len,
err = insert_node(c, lnum, snod->offs, snod->len, hash,
&snod->key, snod->sqnum, deletion,
&used, 0, new_size);
break;
@@ -614,7 +732,7 @@ static int replay_bud(struct ubifs_info *c, struct bud_entry *b)
key_block(c, &snod->key) *
UBIFS_BLOCK_SIZE;
err = insert_node(c, lnum, snod->offs, snod->len,
err = insert_node(c, lnum, snod->offs, snod->len, hash,
&snod->key, snod->sqnum, deletion,
&used, 0, new_size);
break;
@@ -628,7 +746,7 @@ static int replay_bud(struct ubifs_info *c, struct bud_entry *b)
if (err)
goto out_dump;
err = insert_dent(c, lnum, snod->offs, snod->len,
err = insert_dent(c, lnum, snod->offs, snod->len, hash,
&snod->key, dent->name,
le16_to_cpu(dent->nlen), snod->sqnum,
!le64_to_cpu(dent->inum), &used);
@@ -654,11 +772,13 @@ static int replay_bud(struct ubifs_info *c, struct bud_entry *b)
* functions which expect nodes to have keys.
*/
trun_key_init(c, &key, le32_to_cpu(trun->inum));
err = insert_node(c, lnum, snod->offs, snod->len,
err = insert_node(c, lnum, snod->offs, snod->len, hash,
&key, snod->sqnum, 1, &used,
old_size, new_size);
break;
}
case UBIFS_AUTH_NODE:
break;
default:
ubifs_err(c, "unexpected node type %d in bud LEB %d:%d",
snod->type, lnum, snod->offs);
@@ -667,6 +787,10 @@ static int replay_bud(struct ubifs_info *c, struct bud_entry *b)
}
if (err)
goto out;
n++;
if (n == n_nodes)
break;
}
ubifs_assert(c, ubifs_search_bud(c, lnum));
@@ -745,6 +869,7 @@ static int add_replay_bud(struct ubifs_info *c, int lnum, int offs, int jhead,
{
struct ubifs_bud *bud;
struct bud_entry *b;
int err;
dbg_mnt("add replay bud LEB %d:%d, head %d", lnum, offs, jhead);
@@ -754,13 +879,21 @@ static int add_replay_bud(struct ubifs_info *c, int lnum, int offs, int jhead,
b = kmalloc(sizeof(struct bud_entry), GFP_KERNEL);
if (!b) {
kfree(bud);
return -ENOMEM;
err = -ENOMEM;
goto out;
}
bud->lnum = lnum;
bud->start = offs;
bud->jhead = jhead;
bud->log_hash = ubifs_hash_get_desc(c);
if (IS_ERR(bud->log_hash)) {
err = PTR_ERR(bud->log_hash);
goto out;
}
ubifs_shash_copy_state(c, c->log_hash, bud->log_hash);
ubifs_add_bud(c, bud);
b->bud = bud;
@@ -768,6 +901,11 @@ static int add_replay_bud(struct ubifs_info *c, int lnum, int offs, int jhead,
list_add_tail(&b->list, &c->replay_buds);
return 0;
out:
kfree(bud);
kfree(b);
return err;
}
/**
@@ -873,6 +1011,14 @@ static int replay_log_leb(struct ubifs_info *c, int lnum, int offs, void *sbuf)
c->cs_sqnum = le64_to_cpu(node->ch.sqnum);
dbg_mnt("commit start sqnum %llu", c->cs_sqnum);
err = ubifs_shash_init(c, c->log_hash);
if (err)
goto out;
err = ubifs_shash_update(c, c->log_hash, node, UBIFS_CS_NODE_SZ);
if (err < 0)
goto out;
}
if (snod->sqnum < c->cs_sqnum) {
@@ -920,6 +1066,11 @@ static int replay_log_leb(struct ubifs_info *c, int lnum, int offs, void *sbuf)
if (err)
goto out_dump;
err = ubifs_shash_update(c, c->log_hash, ref,
UBIFS_REF_NODE_SZ);
if (err)
goto out;
err = add_replay_bud(c, le32_to_cpu(ref->lnum),
le32_to_cpu(ref->offs),
le32_to_cpu(ref->jhead),

View File

@@ -82,10 +82,13 @@ static int create_default_filesystem(struct ubifs_info *c)
int err, tmp, jnl_lebs, log_lebs, max_buds, main_lebs, main_first;
int lpt_lebs, lpt_first, orph_lebs, big_lpt, ino_waste, sup_flags = 0;
int min_leb_cnt = UBIFS_MIN_LEB_CNT;
int idx_node_size;
long long tmp64, main_bytes;
__le64 tmp_le64;
__le32 tmp_le32;
struct timespec64 ts;
u8 hash[UBIFS_HASH_ARR_SZ];
u8 hash_lpt[UBIFS_HASH_ARR_SZ];
/* Some functions called from here depend on the @c->key_len filed */
c->key_len = UBIFS_SK_LEN;
@@ -147,7 +150,7 @@ static int create_default_filesystem(struct ubifs_info *c)
c->lsave_cnt = DEFAULT_LSAVE_CNT;
c->max_leb_cnt = c->leb_cnt;
err = ubifs_create_dflt_lpt(c, &main_lebs, lpt_first, &lpt_lebs,
&big_lpt);
&big_lpt, hash_lpt);
if (err)
return err;
@@ -156,17 +159,35 @@ static int create_default_filesystem(struct ubifs_info *c)
main_first = c->leb_cnt - main_lebs;
sup = kzalloc(ALIGN(UBIFS_SB_NODE_SZ, c->min_io_size), GFP_KERNEL);
mst = kzalloc(c->mst_node_alsz, GFP_KERNEL);
idx_node_size = ubifs_idx_node_sz(c, 1);
idx = kzalloc(ALIGN(tmp, c->min_io_size), GFP_KERNEL);
ino = kzalloc(ALIGN(UBIFS_INO_NODE_SZ, c->min_io_size), GFP_KERNEL);
cs = kzalloc(ALIGN(UBIFS_CS_NODE_SZ, c->min_io_size), GFP_KERNEL);
if (!sup || !mst || !idx || !ino || !cs) {
err = -ENOMEM;
goto out;
}
/* Create default superblock */
tmp = ALIGN(UBIFS_SB_NODE_SZ, c->min_io_size);
sup = kzalloc(tmp, GFP_KERNEL);
if (!sup)
return -ENOMEM;
tmp64 = (long long)max_buds * c->leb_size;
if (big_lpt)
sup_flags |= UBIFS_FLG_BIGLPT;
sup_flags |= UBIFS_FLG_DOUBLE_HASH;
if (ubifs_authenticated(c)) {
sup_flags |= UBIFS_FLG_AUTHENTICATION;
sup->hash_algo = cpu_to_le16(c->auth_hash_algo);
err = ubifs_hmac_wkm(c, sup->hmac_wkm);
if (err)
goto out;
} else {
sup->hash_algo = 0xffff;
}
sup->ch.node_type = UBIFS_SB_NODE;
sup->key_hash = UBIFS_KEY_HASH_R5;
sup->flags = cpu_to_le32(sup_flags);
@@ -197,17 +218,9 @@ static int create_default_filesystem(struct ubifs_info *c)
sup->rp_size = cpu_to_le64(tmp64);
sup->ro_compat_version = cpu_to_le32(UBIFS_RO_COMPAT_VERSION);
err = ubifs_write_node(c, sup, UBIFS_SB_NODE_SZ, 0, 0);
kfree(sup);
if (err)
return err;
dbg_gen("default superblock created at LEB 0:0");
/* Create default master node */
mst = kzalloc(c->mst_node_alsz, GFP_KERNEL);
if (!mst)
return -ENOMEM;
mst->ch.node_type = UBIFS_MST_NODE;
mst->log_lnum = cpu_to_le32(UBIFS_LOG_LNUM);
@@ -233,6 +246,7 @@ static int create_default_filesystem(struct ubifs_info *c)
mst->empty_lebs = cpu_to_le32(main_lebs - 2);
mst->idx_lebs = cpu_to_le32(1);
mst->leb_cnt = cpu_to_le32(c->leb_cnt);
ubifs_copy_hash(c, hash_lpt, mst->hash_lpt);
/* Calculate lprops statistics */
tmp64 = main_bytes;
@@ -253,24 +267,9 @@ static int create_default_filesystem(struct ubifs_info *c)
mst->total_used = cpu_to_le64(UBIFS_INO_NODE_SZ);
err = ubifs_write_node(c, mst, UBIFS_MST_NODE_SZ, UBIFS_MST_LNUM, 0);
if (err) {
kfree(mst);
return err;
}
err = ubifs_write_node(c, mst, UBIFS_MST_NODE_SZ, UBIFS_MST_LNUM + 1,
0);
kfree(mst);
if (err)
return err;
dbg_gen("default master node created at LEB %d:0", UBIFS_MST_LNUM);
/* Create the root indexing node */
tmp = ubifs_idx_node_sz(c, 1);
idx = kzalloc(ALIGN(tmp, c->min_io_size), GFP_KERNEL);
if (!idx)
return -ENOMEM;
c->key_fmt = UBIFS_SIMPLE_KEY_FMT;
c->key_hash = key_r5_hash;
@@ -282,19 +281,11 @@ static int create_default_filesystem(struct ubifs_info *c)
key_write_idx(c, &key, &br->key);
br->lnum = cpu_to_le32(main_first + DEFAULT_DATA_LEB);
br->len = cpu_to_le32(UBIFS_INO_NODE_SZ);
err = ubifs_write_node(c, idx, tmp, main_first + DEFAULT_IDX_LEB, 0);
kfree(idx);
if (err)
return err;
dbg_gen("default root indexing node created LEB %d:0",
main_first + DEFAULT_IDX_LEB);
/* Create default root inode */
tmp = ALIGN(UBIFS_INO_NODE_SZ, c->min_io_size);
ino = kzalloc(tmp, GFP_KERNEL);
if (!ino)
return -ENOMEM;
ino_key_init_flash(c, &ino->key, UBIFS_ROOT_INO);
ino->ch.node_type = UBIFS_INO_NODE;
@@ -317,12 +308,6 @@ static int create_default_filesystem(struct ubifs_info *c)
/* Set compression enabled by default */
ino->flags = cpu_to_le32(UBIFS_COMPR_FL);
err = ubifs_write_node(c, ino, UBIFS_INO_NODE_SZ,
main_first + DEFAULT_DATA_LEB, 0);
kfree(ino);
if (err)
return err;
dbg_gen("root inode created at LEB %d:0",
main_first + DEFAULT_DATA_LEB);
@@ -331,19 +316,54 @@ static int create_default_filesystem(struct ubifs_info *c)
* always the case during normal file-system operation. Write a fake
* commit start node to the log.
*/
tmp = ALIGN(UBIFS_CS_NODE_SZ, c->min_io_size);
cs = kzalloc(tmp, GFP_KERNEL);
if (!cs)
return -ENOMEM;
cs->ch.node_type = UBIFS_CS_NODE;
err = ubifs_write_node(c, cs, UBIFS_CS_NODE_SZ, UBIFS_LOG_LNUM, 0);
kfree(cs);
err = ubifs_write_node_hmac(c, sup, UBIFS_SB_NODE_SZ, 0, 0,
offsetof(struct ubifs_sb_node, hmac));
if (err)
return err;
goto out;
err = ubifs_write_node(c, ino, UBIFS_INO_NODE_SZ,
main_first + DEFAULT_DATA_LEB, 0);
if (err)
goto out;
ubifs_node_calc_hash(c, ino, hash);
ubifs_copy_hash(c, hash, ubifs_branch_hash(c, br));
err = ubifs_write_node(c, idx, idx_node_size, main_first + DEFAULT_IDX_LEB, 0);
if (err)
goto out;
ubifs_node_calc_hash(c, idx, hash);
ubifs_copy_hash(c, hash, mst->hash_root_idx);
err = ubifs_write_node_hmac(c, mst, UBIFS_MST_NODE_SZ, UBIFS_MST_LNUM, 0,
offsetof(struct ubifs_mst_node, hmac));
if (err)
goto out;
err = ubifs_write_node_hmac(c, mst, UBIFS_MST_NODE_SZ, UBIFS_MST_LNUM + 1,
0, offsetof(struct ubifs_mst_node, hmac));
if (err)
goto out;
err = ubifs_write_node(c, cs, UBIFS_CS_NODE_SZ, UBIFS_LOG_LNUM, 0);
if (err)
goto out;
ubifs_msg(c, "default file-system created");
return 0;
err = 0;
out:
kfree(sup);
kfree(mst);
kfree(idx);
kfree(ino);
kfree(cs);
return err;
}
/**
@@ -498,7 +518,7 @@ failed:
* code. Note, the user of this function is responsible of kfree()'ing the
* returned superblock buffer.
*/
struct ubifs_sb_node *ubifs_read_sb_node(struct ubifs_info *c)
static struct ubifs_sb_node *ubifs_read_sb_node(struct ubifs_info *c)
{
struct ubifs_sb_node *sup;
int err;
@@ -517,6 +537,65 @@ struct ubifs_sb_node *ubifs_read_sb_node(struct ubifs_info *c)
return sup;
}
static int authenticate_sb_node(struct ubifs_info *c,
const struct ubifs_sb_node *sup)
{
unsigned int sup_flags = le32_to_cpu(sup->flags);
u8 hmac_wkm[UBIFS_HMAC_ARR_SZ];
int authenticated = !!(sup_flags & UBIFS_FLG_AUTHENTICATION);
int hash_algo;
int err;
if (c->authenticated && !authenticated) {
ubifs_err(c, "authenticated FS forced, but found FS without authentication");
return -EINVAL;
}
if (!c->authenticated && authenticated) {
ubifs_err(c, "authenticated FS found, but no key given");
return -EINVAL;
}
ubifs_msg(c, "Mounting in %sauthenticated mode",
c->authenticated ? "" : "un");
if (!c->authenticated)
return 0;
if (!IS_ENABLED(CONFIG_UBIFS_FS_AUTHENTICATION))
return -EOPNOTSUPP;
hash_algo = le16_to_cpu(sup->hash_algo);
if (hash_algo >= HASH_ALGO__LAST) {
ubifs_err(c, "superblock uses unknown hash algo %d",
hash_algo);
return -EINVAL;
}
if (strcmp(hash_algo_name[hash_algo], c->auth_hash_name)) {
ubifs_err(c, "This filesystem uses %s for hashing,"
" but %s is specified", hash_algo_name[hash_algo],
c->auth_hash_name);
return -EINVAL;
}
err = ubifs_hmac_wkm(c, hmac_wkm);
if (err)
return err;
if (ubifs_check_hmac(c, hmac_wkm, sup->hmac_wkm)) {
ubifs_err(c, "provided key does not fit");
return -ENOKEY;
}
err = ubifs_node_verify_hmac(c, sup, sizeof(*sup),
offsetof(struct ubifs_sb_node, hmac));
if (err)
ubifs_err(c, "Failed to authenticate superblock: %d", err);
return err;
}
/**
* ubifs_write_sb_node - write superblock node.
* @c: UBIFS file-system description object
@@ -527,8 +606,13 @@ struct ubifs_sb_node *ubifs_read_sb_node(struct ubifs_info *c)
int ubifs_write_sb_node(struct ubifs_info *c, struct ubifs_sb_node *sup)
{
int len = ALIGN(UBIFS_SB_NODE_SZ, c->min_io_size);
int err;
err = ubifs_prepare_node_hmac(c, sup, UBIFS_SB_NODE_SZ,
offsetof(struct ubifs_sb_node, hmac), 1);
if (err)
return err;
ubifs_prepare_node(c, sup, UBIFS_SB_NODE_SZ, 1);
return ubifs_leb_change(c, UBIFS_SB_LNUM, sup, len);
}
@@ -555,6 +639,8 @@ int ubifs_read_superblock(struct ubifs_info *c)
if (IS_ERR(sup))
return PTR_ERR(sup);
c->sup_node = sup;
c->fmt_version = le32_to_cpu(sup->fmt_version);
c->ro_compat_version = le32_to_cpu(sup->ro_compat_version);
@@ -603,7 +689,7 @@ int ubifs_read_superblock(struct ubifs_info *c)
c->key_hash = key_test_hash;
c->key_hash_type = UBIFS_KEY_HASH_TEST;
break;
};
}
c->key_fmt = sup->key_fmt;
@@ -640,6 +726,10 @@ int ubifs_read_superblock(struct ubifs_info *c)
c->double_hash = !!(sup_flags & UBIFS_FLG_DOUBLE_HASH);
c->encrypted = !!(sup_flags & UBIFS_FLG_ENCRYPTION);
err = authenticate_sb_node(c, sup);
if (err)
goto out;
if ((sup_flags & ~UBIFS_FLG_MASK) != 0) {
ubifs_err(c, "Unknown feature flags found: %#x",
sup_flags & ~UBIFS_FLG_MASK);
@@ -686,7 +776,6 @@ int ubifs_read_superblock(struct ubifs_info *c)
err = validate_sb(c, sup);
out:
kfree(sup);
return err;
}
@@ -815,7 +904,7 @@ out:
int ubifs_fixup_free_space(struct ubifs_info *c)
{
int err;
struct ubifs_sb_node *sup;
struct ubifs_sb_node *sup = c->sup_node;
ubifs_assert(c, c->space_fixup);
ubifs_assert(c, !c->ro_mount);
@@ -826,16 +915,11 @@ int ubifs_fixup_free_space(struct ubifs_info *c)
if (err)
return err;
sup = ubifs_read_sb_node(c);
if (IS_ERR(sup))
return PTR_ERR(sup);
/* Free-space fixup is no longer required */
c->space_fixup = 0;
sup->flags &= cpu_to_le32(~UBIFS_FLG_SPACE_FIXUP);
err = ubifs_write_sb_node(c, sup);
kfree(sup);
if (err)
return err;
@@ -846,7 +930,7 @@ int ubifs_fixup_free_space(struct ubifs_info *c)
int ubifs_enable_encryption(struct ubifs_info *c)
{
int err;
struct ubifs_sb_node *sup;
struct ubifs_sb_node *sup = c->sup_node;
if (c->encrypted)
return 0;
@@ -859,16 +943,11 @@ int ubifs_enable_encryption(struct ubifs_info *c)
return -EINVAL;
}
sup = ubifs_read_sb_node(c);
if (IS_ERR(sup))
return PTR_ERR(sup);
sup->flags |= cpu_to_le32(UBIFS_FLG_ENCRYPTION);
err = ubifs_write_sb_node(c, sup);
if (!err)
c->encrypted = 1;
kfree(sup);
return err;
}

View File

@@ -579,6 +579,9 @@ static int init_constants_early(struct ubifs_info *c)
c->ranges[UBIFS_REF_NODE].len = UBIFS_REF_NODE_SZ;
c->ranges[UBIFS_TRUN_NODE].len = UBIFS_TRUN_NODE_SZ;
c->ranges[UBIFS_CS_NODE].len = UBIFS_CS_NODE_SZ;
c->ranges[UBIFS_AUTH_NODE].min_len = UBIFS_AUTH_NODE_SZ;
c->ranges[UBIFS_AUTH_NODE].max_len = UBIFS_AUTH_NODE_SZ +
UBIFS_MAX_HMAC_LEN;
c->ranges[UBIFS_INO_NODE].min_len = UBIFS_INO_NODE_SZ;
c->ranges[UBIFS_INO_NODE].max_len = UBIFS_MAX_INO_NODE_SZ;
@@ -816,6 +819,9 @@ static int alloc_wbufs(struct ubifs_info *c)
c->jheads[i].wbuf.sync_callback = &bud_wbuf_callback;
c->jheads[i].wbuf.jhead = i;
c->jheads[i].grouped = 1;
c->jheads[i].log_hash = ubifs_hash_get_desc(c);
if (IS_ERR(c->jheads[i].log_hash))
goto out;
}
/*
@@ -826,6 +832,12 @@ static int alloc_wbufs(struct ubifs_info *c)
c->jheads[GCHD].grouped = 0;
return 0;
out:
while (i--)
kfree(c->jheads[i].log_hash);
return err;
}
/**
@@ -840,6 +852,7 @@ static void free_wbufs(struct ubifs_info *c)
for (i = 0; i < c->jhead_cnt; i++) {
kfree(c->jheads[i].wbuf.buf);
kfree(c->jheads[i].wbuf.inodes);
kfree(c->jheads[i].log_hash);
}
kfree(c->jheads);
c->jheads = NULL;
@@ -924,6 +937,8 @@ static int check_volume_empty(struct ubifs_info *c)
* Opt_no_chk_data_crc: do not check CRCs when reading data nodes
* Opt_override_compr: override default compressor
* Opt_assert: set ubifs_assert() action
* Opt_auth_key: The key name used for authentication
* Opt_auth_hash_name: The hash type used for authentication
* Opt_err: just end of array marker
*/
enum {
@@ -935,6 +950,8 @@ enum {
Opt_no_chk_data_crc,
Opt_override_compr,
Opt_assert,
Opt_auth_key,
Opt_auth_hash_name,
Opt_ignore,
Opt_err,
};
@@ -947,6 +964,8 @@ static const match_table_t tokens = {
{Opt_chk_data_crc, "chk_data_crc"},
{Opt_no_chk_data_crc, "no_chk_data_crc"},
{Opt_override_compr, "compr=%s"},
{Opt_auth_key, "auth_key=%s"},
{Opt_auth_hash_name, "auth_hash_name=%s"},
{Opt_ignore, "ubi=%s"},
{Opt_ignore, "vol=%s"},
{Opt_assert, "assert=%s"},
@@ -1070,6 +1089,16 @@ static int ubifs_parse_options(struct ubifs_info *c, char *options,
kfree(act);
break;
}
case Opt_auth_key:
c->auth_key_name = kstrdup(args[0].from, GFP_KERNEL);
if (!c->auth_key_name)
return -ENOMEM;
break;
case Opt_auth_hash_name:
c->auth_hash_name = kstrdup(args[0].from, GFP_KERNEL);
if (!c->auth_hash_name)
return -ENOMEM;
break;
case Opt_ignore:
break;
default:
@@ -1249,6 +1278,19 @@ static int mount_ubifs(struct ubifs_info *c)
c->mounting = 1;
if (c->auth_key_name) {
if (IS_ENABLED(CONFIG_UBIFS_FS_AUTHENTICATION)) {
err = ubifs_init_authentication(c);
if (err)
goto out_free;
} else {
ubifs_err(c, "auth_key_name, but UBIFS is built without"
" authentication support");
err = -EINVAL;
goto out_free;
}
}
err = ubifs_read_superblock(c);
if (err)
goto out_free;
@@ -1367,12 +1409,21 @@ static int mount_ubifs(struct ubifs_info *c)
}
if (c->need_recovery) {
err = ubifs_recover_size(c);
if (err)
goto out_orphans;
if (!ubifs_authenticated(c)) {
err = ubifs_recover_size(c, true);
if (err)
goto out_orphans;
}
err = ubifs_rcvry_gc_commit(c);
if (err)
goto out_orphans;
if (ubifs_authenticated(c)) {
err = ubifs_recover_size(c, false);
if (err)
goto out_orphans;
}
} else {
err = take_gc_lnum(c);
if (err)
@@ -1391,7 +1442,7 @@ static int mount_ubifs(struct ubifs_info *c)
if (err)
goto out_orphans;
} else if (c->need_recovery) {
err = ubifs_recover_size(c);
err = ubifs_recover_size(c, false);
if (err)
goto out_orphans;
} else {
@@ -1557,7 +1608,10 @@ static void ubifs_umount(struct ubifs_info *c)
free_wbufs(c);
free_orphans(c);
ubifs_lpt_free(c, 0);
ubifs_exit_authentication(c);
kfree(c->auth_key_name);
kfree(c->auth_hash_name);
kfree(c->cbuf);
kfree(c->rcvrd_mst_node);
kfree(c->mst_node);
@@ -1605,16 +1659,10 @@ static int ubifs_remount_rw(struct ubifs_info *c)
goto out;
if (c->old_leb_cnt != c->leb_cnt) {
struct ubifs_sb_node *sup;
struct ubifs_sb_node *sup = c->sup_node;
sup = ubifs_read_sb_node(c);
if (IS_ERR(sup)) {
err = PTR_ERR(sup);
goto out;
}
sup->leb_cnt = cpu_to_le32(c->leb_cnt);
err = ubifs_write_sb_node(c, sup);
kfree(sup);
if (err)
goto out;
}
@@ -1624,9 +1672,11 @@ static int ubifs_remount_rw(struct ubifs_info *c)
err = ubifs_write_rcvrd_mst_node(c);
if (err)
goto out;
err = ubifs_recover_size(c);
if (err)
goto out;
if (!ubifs_authenticated(c)) {
err = ubifs_recover_size(c, true);
if (err)
goto out;
}
err = ubifs_clean_lebs(c, c->sbuf);
if (err)
goto out;
@@ -1692,10 +1742,19 @@ static int ubifs_remount_rw(struct ubifs_info *c)
goto out;
}
if (c->need_recovery)
if (c->need_recovery) {
err = ubifs_rcvry_gc_commit(c);
else
if (err)
goto out;
if (ubifs_authenticated(c)) {
err = ubifs_recover_size(c, false);
if (err)
goto out;
}
} else {
err = ubifs_leb_unmap(c, c->gc_lnum);
}
if (err)
goto out;

Some files were not shown because too many files have changed in this diff Show More