mirror of
https://github.com/Dasharo/linux.git
synced 2026-03-06 15:25:10 -08:00
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:
426
Documentation/filesystems/ubifs-authentication.md
Normal file
426
Documentation/filesystems/ubifs-authentication.md
Normal 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
|
||||
@@ -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
|
||||
|
||||
@@ -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*
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
502
fs/ubifs/auth.c
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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);
|
||||
|
||||
@@ -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))
|
||||
|
||||
148
fs/ubifs/io.c
148
fs/ubifs/io.c
@@ -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
@@ -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);
|
||||
|
||||
184
fs/ubifs/lpt.c
184
fs/ubifs/lpt.c
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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),
|
||||
|
||||
211
fs/ubifs/sb.c
211
fs/ubifs/sb.c
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user