This pull request contains updates for UBIFS:
- Full filesystem authentication feature, UBIFS is now able to have the whole filesystem structure authenticated plus user data encrypted and authenticated. - Minor cleanups -----BEGIN PGP SIGNATURE----- iQJKBAABCAA0FiEEdgfidid8lnn52cLTZvlZhesYu8EFAlvaF2IWHHJpY2hhcmRA c2lnbWEtc3Rhci5hdAAKCRBm+VmF6xi7wUb/D/0Z/jN80LtxoIlQzmfoBnVSnaXv BDvdDFHTwV+zu4XCvUyJzBnwzNjDxNK2XD5hAgiqCoTk5sr4KUi5+zfft5XMW40w T1m5mQNhjwmcI/J/5m2gSHbOSB8Hkc0HIybknS+5ZJDa1OZUkxejLcmpK5Wk+bxp Ak1cOn5GIJKRQMrUudhySkQaBe0DnNmHSACePSb5AYGlnRy6eJ26ANR2mU7PFg1V NBVbOQjMrYIV9qq9m+vtTNsLXidcaRf474fg7lshodmDBISy9g83Oq8FaPzYTJVJ rkvdsRzCrXeApSH2LJ8Gb1AvIAlvJa2Va+anXh8NrSBySfzTKrIPtIONkpF7zxOC 8naZcRNvTqWcMfaTKGK+SGWUqGlHxdGOo5NkkKrn0jsO6HJ8kYAXKFGx65MsiCLv xPlKc543ZLSscw3JJqLXVoXr2hmwhUHMJwwaPngFmdgm88bog62feUgFpYOU/1dj 1s2+q3jSUqfuS4oInjAmeX/Yq9dss/6dMo73ikbekIGRtijUfCMBWFyINdE0oWPu ZUdOOifYrozIG7wWEo6ZzCI1PIyPvYfKcXVMWimPmu9Xi5AnbCDMQmPYVF5YMM0R jexN9gVyFQQjz940reFJi0EkIJjwCycWLWft6P6cLDc/rRUUP4ibNYv3JL8WvhHn Eb9V6InXhcyuX4eopA== =lq2m -----END PGP SIGNATURE----- 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:
commit
42bd06e93d
25 changed files with 2436 additions and 310 deletions
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
|
@ -0,0 +1,502 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* This file is part of UBIFS.
|
||||
*
|
||||
* Copyright (C) 2018 Pengutronix, Sascha Hauer <s.hauer@pengutronix.de>
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file implements various helper functions for UBIFS authentication support
|
||||
*/
|
||||
|
||||
#include <linux/crypto.h>
|
||||
#include <crypto/hash.h>
|
||||
#include <crypto/sha.h>
|
||||
#include <crypto/algapi.h>
|
||||
#include <keys/user-type.h>
|
||||
|
||||
#include "ubifs.h"
|
||||
|
||||
/**
|
||||
* ubifs_node_calc_hash - calculate the hash of a UBIFS node
|
||||
* @c: UBIFS file-system description object
|
||||
* @node: the node to calculate a hash for
|
||||
* @hash: the returned hash
|
||||
*
|
||||
* Returns 0 for success or a negative error code otherwise.
|
||||
*/
|
||||
int __ubifs_node_calc_hash(const struct ubifs_info *c, const void *node,
|
||||
u8 *hash)
|
||||
{
|
||||
const struct ubifs_ch *ch = node;
|
||||
SHASH_DESC_ON_STACK(shash, c->hash_tfm);
|
||||
int err;
|
||||
|
||||
shash->tfm = c->hash_tfm;
|
||||
shash->flags = CRYPTO_TFM_REQ_MAY_SLEEP;
|
||||
|
||||
err = crypto_shash_digest(shash, node, le32_to_cpu(ch->len), hash);
|
||||
if (err < 0)
|
||||
return err;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ubifs_hash_calc_hmac - calculate a HMAC from a hash
|
||||
* @c: UBIFS file-system description object
|
||||
* @hash: the node to calculate a HMAC for
|
||||
* @hmac: the returned HMAC
|
||||
*
|
||||
* Returns 0 for success or a negative error code otherwise.
|
||||
*/
|
||||
static int ubifs_hash_calc_hmac(const struct ubifs_info *c, const u8 *hash,
|
||||
u8 *hmac)
|
||||
{
|
||||
SHASH_DESC_ON_STACK(shash, c->hmac_tfm);
|
||||
int err;
|
||||
|
||||
shash->tfm = c->hmac_tfm;
|
||||
shash->flags = CRYPTO_TFM_REQ_MAY_SLEEP;
|
||||
|
||||
err = crypto_shash_digest(shash, hash, c->hash_len, hmac);
|
||||
if (err < 0)
|
||||
return err;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ubifs_prepare_auth_node - Prepare an authentication node
|
||||
* @c: UBIFS file-system description object
|
||||
* @node: the node to calculate a hash for
|
||||
* @hash: input hash of previous nodes
|
||||
*
|
||||
* This function prepares an authentication node for writing onto flash.
|
||||
* It creates a HMAC from the given input hash and writes it to the node.
|
||||
*
|
||||
* Returns 0 for success or a negative error code otherwise.
|
||||
*/
|
||||
int ubifs_prepare_auth_node(struct ubifs_info *c, void *node,
|
||||
struct shash_desc *inhash)
|
||||
{
|
||||
SHASH_DESC_ON_STACK(hash_desc, c->hash_tfm);
|
||||
struct ubifs_auth_node *auth = node;
|
||||
u8 *hash;
|
||||
int err;
|
||||
|
||||
hash = kmalloc(crypto_shash_descsize(c->hash_tfm), GFP_NOFS);
|
||||
if (!hash)
|
||||
return -ENOMEM;
|
||||
|
||||
hash_desc->tfm = c->hash_tfm;
|
||||
hash_desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP;
|
||||
ubifs_shash_copy_state(c, inhash, hash_desc);
|
||||
|
||||
err = crypto_shash_final(hash_desc, hash);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
err = ubifs_hash_calc_hmac(c, hash, auth->hmac);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
auth->ch.node_type = UBIFS_AUTH_NODE;
|
||||
ubifs_prepare_node(c, auth, ubifs_auth_node_sz(c), 0);
|
||||
|
||||
err = 0;
|
||||
out:
|
||||
kfree(hash);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static struct shash_desc *ubifs_get_desc(const struct ubifs_info *c,
|
||||
struct crypto_shash *tfm)
|
||||
{
|
||||
struct shash_desc *desc;
|
||||
int err;
|
||||
|
||||
if (!ubifs_authenticated(c))
|
||||
return NULL;
|
||||
|
||||
desc = kmalloc(sizeof(*desc) + crypto_shash_descsize(tfm), GFP_KERNEL);
|
||||
if (!desc)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
desc->tfm = tfm;
|
||||
desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP;
|
||||
|
||||
err = crypto_shash_init(desc);
|
||||
if (err) {
|
||||
kfree(desc);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
return desc;
|
||||
}
|
||||
|
||||
/**
|
||||
* __ubifs_hash_get_desc - get a descriptor suitable for hashing a node
|
||||
* @c: UBIFS file-system description object
|
||||
*
|
||||
* This function returns a descriptor suitable for hashing a node. Free after use
|
||||
* with kfree.
|
||||
*/
|
||||
struct shash_desc *__ubifs_hash_get_desc(const struct ubifs_info *c)
|
||||
{
|
||||
return ubifs_get_desc(c, c->hash_tfm);
|
||||
}
|
||||
|
||||
/**
|
||||
* __ubifs_shash_final - finalize shash
|
||||
* @c: UBIFS file-system description object
|
||||
* @desc: the descriptor
|
||||
* @out: the output hash
|
||||
*
|
||||
* Simple wrapper around crypto_shash_final(), safe to be called with
|
||||
* disabled authentication.
|
||||
*/
|
||||
int __ubifs_shash_final(const struct ubifs_info *c, struct shash_desc *desc,
|
||||
u8 *out)
|
||||
{
|
||||
if (ubifs_authenticated(c))
|
||||
return crypto_shash_final(desc, out);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ubifs_bad_hash - Report hash mismatches
|
||||
* @c: UBIFS file-system description object
|
||||
* @node: the node
|
||||
* @hash: the expected hash
|
||||
* @lnum: the LEB @node was read from
|
||||
* @offs: offset in LEB @node was read from
|
||||
*
|
||||
* This function reports a hash mismatch when a node has a different hash than
|
||||
* expected.
|
||||
*/
|
||||
void ubifs_bad_hash(const struct ubifs_info *c, const void *node, const u8 *hash,
|
||||
int lnum, int offs)
|
||||
{
|
||||
int len = min(c->hash_len, 20);
|
||||
int cropped = len != c->hash_len;
|
||||
const char *cont = cropped ? "..." : "";
|
||||
|
||||
u8 calc[UBIFS_HASH_ARR_SZ];
|
||||
|
||||
__ubifs_node_calc_hash(c, node, calc);
|
||||
|
||||
ubifs_err(c, "hash mismatch on node at LEB %d:%d", lnum, offs);
|
||||
ubifs_err(c, "hash expected: %*ph%s", len, hash, cont);
|
||||
ubifs_err(c, "hash calculated: %*ph%s", len, calc, cont);
|
||||
}
|
||||
|
||||
/**
|
||||
* __ubifs_node_check_hash - check the hash of a node against given hash
|
||||
* @c: UBIFS file-system description object
|
||||
* @node: the node
|
||||
* @expected: the expected hash
|
||||
*
|
||||
* This function calculates a hash over a node and compares it to the given hash.
|
||||
* Returns 0 if both hashes are equal or authentication is disabled, otherwise a
|
||||
* negative error code is returned.
|
||||
*/
|
||||
int __ubifs_node_check_hash(const struct ubifs_info *c, const void *node,
|
||||
const u8 *expected)
|
||||
{
|
||||
u8 calc[UBIFS_HASH_ARR_SZ];
|
||||
int err;
|
||||
|
||||
err = __ubifs_node_calc_hash(c, node, calc);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (ubifs_check_hash(c, expected, calc))
|
||||
return -EPERM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ubifs_init_authentication - initialize UBIFS authentication support
|
||||
* @c: UBIFS file-system description object
|
||||
*
|
||||
* This function returns 0 for success or a negative error code otherwise.
|
||||
*/
|
||||
int ubifs_init_authentication(struct ubifs_info *c)
|
||||
{
|
||||
struct key *keyring_key;
|
||||
const struct user_key_payload *ukp;
|
||||
int err;
|
||||
char hmac_name[CRYPTO_MAX_ALG_NAME];
|
||||
|
||||
if (!c->auth_hash_name) {
|
||||
ubifs_err(c, "authentication hash name needed with authentication");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
c->auth_hash_algo = match_string(hash_algo_name, HASH_ALGO__LAST,
|
||||
c->auth_hash_name);
|
||||
if ((int)c->auth_hash_algo < 0) {
|
||||
ubifs_err(c, "Unknown hash algo %s specified",
|
||||
c->auth_hash_name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
snprintf(hmac_name, CRYPTO_MAX_ALG_NAME, "hmac(%s)",
|
||||
c->auth_hash_name);
|
||||
|
||||
keyring_key = request_key(&key_type_logon, c->auth_key_name, NULL);
|
||||
|
||||
if (IS_ERR(keyring_key)) {
|
||||
ubifs_err(c, "Failed to request key: %ld",
|
||||
PTR_ERR(keyring_key));
|
||||
return PTR_ERR(keyring_key);
|
||||
}
|
||||
|
||||
down_read(&keyring_key->sem);
|
||||
|
||||
if (keyring_key->type != &key_type_logon) {
|
||||
ubifs_err(c, "key type must be logon");
|
||||
err = -ENOKEY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ukp = user_key_payload_locked(keyring_key);
|
||||
if (!ukp) {
|
||||
/* key was revoked before we acquired its semaphore */
|
||||
err = -EKEYREVOKED;
|
||||
goto out;
|
||||
}
|
||||
|
||||
c->hash_tfm = crypto_alloc_shash(c->auth_hash_name, 0,
|
||||
CRYPTO_ALG_ASYNC);
|
||||
if (IS_ERR(c->hash_tfm)) {
|
||||
err = PTR_ERR(c->hash_tfm);
|
||||
ubifs_err(c, "Can not allocate %s: %d",
|
||||
c->auth_hash_name, err);
|
||||
goto out;
|
||||
}
|
||||
|
||||
c->hash_len = crypto_shash_digestsize(c->hash_tfm);
|
||||
if (c->hash_len > UBIFS_HASH_ARR_SZ) {
|
||||
ubifs_err(c, "hash %s is bigger than maximum allowed hash size (%d > %d)",
|
||||
c->auth_hash_name, c->hash_len, UBIFS_HASH_ARR_SZ);
|
||||
err = -EINVAL;
|
||||
goto out_free_hash;
|
||||
}
|
||||
|
||||
c->hmac_tfm = crypto_alloc_shash(hmac_name, 0, CRYPTO_ALG_ASYNC);
|
||||
if (IS_ERR(c->hmac_tfm)) {
|
||||
err = PTR_ERR(c->hmac_tfm);
|
||||
ubifs_err(c, "Can not allocate %s: %d", hmac_name, err);
|
||||
goto out_free_hash;
|
||||
}
|
||||
|
||||
c->hmac_desc_len = crypto_shash_digestsize(c->hmac_tfm);
|
||||
if (c->hmac_desc_len > UBIFS_HMAC_ARR_SZ) {
|
||||
ubifs_err(c, "hmac %s is bigger than maximum allowed hmac size (%d > %d)",
|
||||
hmac_name, c->hmac_desc_len, UBIFS_HMAC_ARR_SZ);
|
||||
err = -EINVAL;
|
||||
goto out_free_hash;
|
||||
}
|
||||
|
||||
err = crypto_shash_setkey(c->hmac_tfm, ukp->data, ukp->datalen);
|
||||
if (err)
|
||||
goto out_free_hmac;
|
||||
|
||||
c->authenticated = true;
|
||||
|
||||
c->log_hash = ubifs_hash_get_desc(c);
|
||||
if (IS_ERR(c->log_hash))
|
||||
goto out_free_hmac;
|
||||
|
||||
err = 0;
|
||||
|
||||
out_free_hmac:
|
||||
if (err)
|
||||
crypto_free_shash(c->hmac_tfm);
|
||||
out_free_hash:
|
||||
if (err)
|
||||
crypto_free_shash(c->hash_tfm);
|
||||
out:
|
||||
up_read(&keyring_key->sem);
|
||||
key_put(keyring_key);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* __ubifs_exit_authentication - release resource
|
||||
* @c: UBIFS file-system description object
|
||||
*
|
||||
* This function releases the authentication related resources.
|
||||
*/
|
||||
void __ubifs_exit_authentication(struct ubifs_info *c)
|
||||
{
|
||||
if (!ubifs_authenticated(c))
|
||||
return;
|
||||
|
||||
crypto_free_shash(c->hmac_tfm);
|
||||
crypto_free_shash(c->hash_tfm);
|
||||
kfree(c->log_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* ubifs_node_calc_hmac - calculate the HMAC of a UBIFS node
|
||||
* @c: UBIFS file-system description object
|
||||
* @node: the node to insert a HMAC into.
|
||||
* @len: the length of the node
|
||||
* @ofs_hmac: the offset in the node where the HMAC is inserted
|
||||
* @hmac: returned HMAC
|
||||
*
|
||||
* This function calculates a HMAC of a UBIFS node. The HMAC is expected to be
|
||||
* embedded into the node, so this area is not covered by the HMAC. Also not
|
||||
* covered is the UBIFS_NODE_MAGIC and the CRC of the node.
|
||||
*/
|
||||
static int ubifs_node_calc_hmac(const struct ubifs_info *c, const void *node,
|
||||
int len, int ofs_hmac, void *hmac)
|
||||
{
|
||||
SHASH_DESC_ON_STACK(shash, c->hmac_tfm);
|
||||
int hmac_len = c->hmac_desc_len;
|
||||
int err;
|
||||
|
||||
ubifs_assert(c, ofs_hmac > 8);
|
||||
ubifs_assert(c, ofs_hmac + hmac_len < len);
|
||||
|
||||
shash->tfm = c->hmac_tfm;
|
||||
shash->flags = CRYPTO_TFM_REQ_MAY_SLEEP;
|
||||
|
||||
err = crypto_shash_init(shash);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* behind common node header CRC up to HMAC begin */
|
||||
err = crypto_shash_update(shash, node + 8, ofs_hmac - 8);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/* behind HMAC, if any */
|
||||
if (len - ofs_hmac - hmac_len > 0) {
|
||||
err = crypto_shash_update(shash, node + ofs_hmac + hmac_len,
|
||||
len - ofs_hmac - hmac_len);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
return crypto_shash_final(shash, hmac);
|
||||
}
|
||||
|
||||
/**
|
||||
* __ubifs_node_insert_hmac - insert a HMAC into a UBIFS node
|
||||
* @c: UBIFS file-system description object
|
||||
* @node: the node to insert a HMAC into.
|
||||
* @len: the length of the node
|
||||
* @ofs_hmac: the offset in the node where the HMAC is inserted
|
||||
*
|
||||
* This function inserts a HMAC at offset @ofs_hmac into the node given in
|
||||
* @node.
|
||||
*
|
||||
* This function returns 0 for success or a negative error code otherwise.
|
||||
*/
|
||||
int __ubifs_node_insert_hmac(const struct ubifs_info *c, void *node, int len,
|
||||
int ofs_hmac)
|
||||
{
|
||||
return ubifs_node_calc_hmac(c, node, len, ofs_hmac, node + ofs_hmac);
|
||||
}
|
||||
|
||||
/**
|
||||
* __ubifs_node_verify_hmac - verify the HMAC of UBIFS node
|
||||
* @c: UBIFS file-system description object
|
||||
* @node: the node to insert a HMAC into.
|
||||
* @len: the length of the node
|
||||
* @ofs_hmac: the offset in the node where the HMAC is inserted
|
||||
*
|
||||
* This function verifies the HMAC at offset @ofs_hmac of the node given in
|
||||
* @node. Returns 0 if successful or a negative error code otherwise.
|
||||
*/
|
||||
int __ubifs_node_verify_hmac(const struct ubifs_info *c, const void *node,
|
||||
int len, int ofs_hmac)
|
||||
{
|
||||
int hmac_len = c->hmac_desc_len;
|
||||
u8 *hmac;
|
||||
int err;
|
||||
|
||||
hmac = kmalloc(hmac_len, GFP_NOFS);
|
||||
if (!hmac)
|
||||
return -ENOMEM;
|
||||
|
||||
err = ubifs_node_calc_hmac(c, node, len, ofs_hmac, hmac);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = crypto_memneq(hmac, node + ofs_hmac, hmac_len);
|
||||
|
||||
kfree(hmac);
|
||||
|
||||
if (!err)
|
||||
return 0;
|
||||
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
int __ubifs_shash_copy_state(const struct ubifs_info *c, struct shash_desc *src,
|
||||
struct shash_desc *target)
|
||||
{
|
||||
u8 *state;
|
||||
int err;
|
||||
|
||||
state = kmalloc(crypto_shash_descsize(src->tfm), GFP_NOFS);
|
||||
if (!state)
|
||||
return -ENOMEM;
|
||||
|
||||
err = crypto_shash_export(src, state);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
err = crypto_shash_import(target, state);
|
||||
|
||||
out:
|
||||
kfree(state);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* ubifs_hmac_wkm - Create a HMAC of the well known message
|
||||
* @c: UBIFS file-system description object
|
||||
* @hmac: The HMAC of the well known message
|
||||
*
|
||||
* This function creates a HMAC of a well known message. This is used
|
||||
* to check if the provided key is suitable to authenticate a UBIFS
|
||||
* image. This is only a convenience to the user to provide a better
|
||||
* error message when the wrong key is provided.
|
||||
*
|
||||
* This function returns 0 for success or a negative error code otherwise.
|
||||
*/
|
||||
int ubifs_hmac_wkm(struct ubifs_info *c, u8 *hmac)
|
||||
{
|
||||
SHASH_DESC_ON_STACK(shash, c->hmac_tfm);
|
||||
int err;
|
||||
const char well_known_message[] = "UBIFS";
|
||||
|
||||
if (!ubifs_authenticated(c))
|
||||
return 0;
|
||||
|
||||
shash->tfm = c->hmac_tfm;
|
||||
shash->flags = CRYPTO_TFM_REQ_MAY_SLEEP;
|
||||
|
||||
err = crypto_shash_init(shash);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = crypto_shash_update(shash, well_known_message,
|
||||
sizeof(well_known_message) - 1);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = crypto_shash_final(shash, hmac);
|
||||
if (err)
|
||||
return err;
|
||||
return 0;
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -90,6 +90,12 @@ static inline void zero_trun_node_unused(struct ubifs_trun_node *trun)
|
|||
memset(trun->padding, 0, 12);
|
||||
}
|
||||
|
||||
static void ubifs_add_auth_dirt(struct ubifs_info *c, int lnum)
|
||||
{
|
||||
if (ubifs_authenticated(c))
|
||||
ubifs_add_dirt(c, lnum, ubifs_auth_node_sz(c));
|
||||
}
|
||||
|
||||
/**
|
||||
* reserve_space - reserve space in the journal.
|
||||
* @c: UBIFS file-system description object
|
||||
|
@ -228,34 +234,33 @@ out_return:
|
|||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* write_node - write node to a journal head.
|
||||
* @c: UBIFS file-system description object
|
||||
* @jhead: journal head
|
||||
* @node: node to write
|
||||
* @len: node length
|
||||
* @lnum: LEB number written is returned here
|
||||
* @offs: offset written is returned here
|
||||
*
|
||||
* This function writes a node to reserved space of journal head @jhead.
|
||||
* Returns zero in case of success and a negative error code in case of
|
||||
* failure.
|
||||
*/
|
||||
static int write_node(struct ubifs_info *c, int jhead, void *node, int len,
|
||||
int *lnum, int *offs)
|
||||
static int ubifs_hash_nodes(struct ubifs_info *c, void *node,
|
||||
int len, struct shash_desc *hash)
|
||||
{
|
||||
struct ubifs_wbuf *wbuf = &c->jheads[jhead].wbuf;
|
||||
int auth_node_size = ubifs_auth_node_sz(c);
|
||||
int err;
|
||||
|
||||
ubifs_assert(c, jhead != GCHD);
|
||||
while (1) {
|
||||
const struct ubifs_ch *ch = node;
|
||||
int nodelen = le32_to_cpu(ch->len);
|
||||
|
||||
*lnum = c->jheads[jhead].wbuf.lnum;
|
||||
*offs = c->jheads[jhead].wbuf.offs + c->jheads[jhead].wbuf.used;
|
||||
ubifs_assert(c, len >= auth_node_size);
|
||||
|
||||
dbg_jnl("jhead %s, LEB %d:%d, len %d",
|
||||
dbg_jhead(jhead), *lnum, *offs, len);
|
||||
ubifs_prepare_node(c, node, len, 0);
|
||||
if (len == auth_node_size)
|
||||
break;
|
||||
|
||||
return ubifs_wbuf_write_nolock(wbuf, node, len);
|
||||
ubifs_assert(c, len > nodelen);
|
||||
ubifs_assert(c, ch->magic == cpu_to_le32(UBIFS_NODE_MAGIC));
|
||||
|
||||
err = ubifs_shash_update(c, hash, (void *)node, nodelen);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
node += ALIGN(nodelen, 8);
|
||||
len -= ALIGN(nodelen, 8);
|
||||
}
|
||||
|
||||
return ubifs_prepare_auth_node(c, node, hash);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -268,9 +273,9 @@ static int write_node(struct ubifs_info *c, int jhead, void *node, int len,
|
|||
* @offs: offset written is returned here
|
||||
* @sync: non-zero if the write-buffer has to by synchronized
|
||||
*
|
||||
* This function is the same as 'write_node()' but it does not assume the
|
||||
* buffer it is writing is a node, so it does not prepare it (which means
|
||||
* initializing common header and calculating CRC).
|
||||
* This function writes data to the reserved space of journal head @jhead.
|
||||
* Returns zero in case of success and a negative error code in case of
|
||||
* failure.
|
||||
*/
|
||||
static int write_head(struct ubifs_info *c, int jhead, void *buf, int len,
|
||||
int *lnum, int *offs, int sync)
|
||||
|
@ -285,6 +290,12 @@ static int write_head(struct ubifs_info *c, int jhead, void *buf, int len,
|
|||
dbg_jnl("jhead %s, LEB %d:%d, len %d",
|
||||
dbg_jhead(jhead), *lnum, *offs, len);
|
||||
|
||||
if (ubifs_authenticated(c)) {
|
||||
err = ubifs_hash_nodes(c, buf, len, c->jheads[jhead].log_hash);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
err = ubifs_wbuf_write_nolock(wbuf, buf, len);
|
||||
if (err)
|
||||
return err;
|
||||
|
@ -548,6 +559,9 @@ int ubifs_jnl_update(struct ubifs_info *c, const struct inode *dir,
|
|||
struct ubifs_dent_node *dent;
|
||||
struct ubifs_ino_node *ino;
|
||||
union ubifs_key dent_key, ino_key;
|
||||
u8 hash_dent[UBIFS_HASH_ARR_SZ];
|
||||
u8 hash_ino[UBIFS_HASH_ARR_SZ];
|
||||
u8 hash_ino_host[UBIFS_HASH_ARR_SZ];
|
||||
|
||||
ubifs_assert(c, mutex_is_locked(&host_ui->ui_mutex));
|
||||
|
||||
|
@ -570,7 +584,10 @@ int ubifs_jnl_update(struct ubifs_info *c, const struct inode *dir,
|
|||
|
||||
len = aligned_dlen + aligned_ilen + UBIFS_INO_NODE_SZ;
|
||||
/* Make sure to also account for extended attributes */
|
||||
len += host_ui->data_len;
|
||||
if (ubifs_authenticated(c))
|
||||
len += ALIGN(host_ui->data_len, 8) + ubifs_auth_node_sz(c);
|
||||
else
|
||||
len += host_ui->data_len;
|
||||
|
||||
dent = kzalloc(len, GFP_NOFS);
|
||||
if (!dent)
|
||||
|
@ -602,11 +619,21 @@ int ubifs_jnl_update(struct ubifs_info *c, const struct inode *dir,
|
|||
|
||||
zero_dent_node_unused(dent);
|
||||
ubifs_prep_grp_node(c, dent, dlen, 0);
|
||||
err = ubifs_node_calc_hash(c, dent, hash_dent);
|
||||
if (err)
|
||||
goto out_release;
|
||||
|
||||
ino = (void *)dent + aligned_dlen;
|
||||
pack_inode(c, ino, inode, 0);
|
||||
err = ubifs_node_calc_hash(c, ino, hash_ino);
|
||||
if (err)
|
||||
goto out_release;
|
||||
|
||||
ino = (void *)ino + aligned_ilen;
|
||||
pack_inode(c, ino, dir, 1);
|
||||
err = ubifs_node_calc_hash(c, ino, hash_ino_host);
|
||||
if (err)
|
||||
goto out_release;
|
||||
|
||||
if (last_reference) {
|
||||
err = ubifs_add_orphan(c, inode->i_ino);
|
||||
|
@ -628,6 +655,7 @@ int ubifs_jnl_update(struct ubifs_info *c, const struct inode *dir,
|
|||
}
|
||||
release_head(c, BASEHD);
|
||||
kfree(dent);
|
||||
ubifs_add_auth_dirt(c, lnum);
|
||||
|
||||
if (deletion) {
|
||||
if (nm->hash)
|
||||
|
@ -638,7 +666,8 @@ int ubifs_jnl_update(struct ubifs_info *c, const struct inode *dir,
|
|||
goto out_ro;
|
||||
err = ubifs_add_dirt(c, lnum, dlen);
|
||||
} else
|
||||
err = ubifs_tnc_add_nm(c, &dent_key, lnum, dent_offs, dlen, nm);
|
||||
err = ubifs_tnc_add_nm(c, &dent_key, lnum, dent_offs, dlen,
|
||||
hash_dent, nm);
|
||||
if (err)
|
||||
goto out_ro;
|
||||
|
||||
|
@ -650,14 +679,14 @@ int ubifs_jnl_update(struct ubifs_info *c, const struct inode *dir,
|
|||
*/
|
||||
ino_key_init(c, &ino_key, inode->i_ino);
|
||||
ino_offs = dent_offs + aligned_dlen;
|
||||
err = ubifs_tnc_add(c, &ino_key, lnum, ino_offs, ilen);
|
||||
err = ubifs_tnc_add(c, &ino_key, lnum, ino_offs, ilen, hash_ino);
|
||||
if (err)
|
||||
goto out_ro;
|
||||
|
||||
ino_key_init(c, &ino_key, dir->i_ino);
|
||||
ino_offs += aligned_ilen;
|
||||
err = ubifs_tnc_add(c, &ino_key, lnum, ino_offs,
|
||||
UBIFS_INO_NODE_SZ + host_ui->data_len);
|
||||
UBIFS_INO_NODE_SZ + host_ui->data_len, hash_ino_host);
|
||||
if (err)
|
||||
goto out_ro;
|
||||
|
||||
|
@ -706,10 +735,12 @@ int ubifs_jnl_write_data(struct ubifs_info *c, const struct inode *inode,
|
|||
const union ubifs_key *key, const void *buf, int len)
|
||||
{
|
||||
struct ubifs_data_node *data;
|
||||
int err, lnum, offs, compr_type, out_len, compr_len;
|
||||
int err, lnum, offs, compr_type, out_len, compr_len, auth_len;
|
||||
int dlen = COMPRESSED_DATA_NODE_BUF_SZ, allocated = 1;
|
||||
int write_len;
|
||||
struct ubifs_inode *ui = ubifs_inode(inode);
|
||||
bool encrypted = ubifs_crypt_is_encrypted(inode);
|
||||
u8 hash[UBIFS_HASH_ARR_SZ];
|
||||
|
||||
dbg_jnlk(key, "ino %lu, blk %u, len %d, key ",
|
||||
(unsigned long)key_inum(c, key), key_block(c, key), len);
|
||||
|
@ -718,7 +749,9 @@ int ubifs_jnl_write_data(struct ubifs_info *c, const struct inode *inode,
|
|||
if (encrypted)
|
||||
dlen += UBIFS_CIPHER_BLOCK_SIZE;
|
||||
|
||||
data = kmalloc(dlen, GFP_NOFS | __GFP_NOWARN);
|
||||
auth_len = ubifs_auth_node_sz(c);
|
||||
|
||||
data = kmalloc(dlen + auth_len, GFP_NOFS | __GFP_NOWARN);
|
||||
if (!data) {
|
||||
/*
|
||||
* Fall-back to the write reserve buffer. Note, we might be
|
||||
|
@ -757,20 +790,33 @@ int ubifs_jnl_write_data(struct ubifs_info *c, const struct inode *inode,
|
|||
}
|
||||
|
||||
dlen = UBIFS_DATA_NODE_SZ + out_len;
|
||||
if (ubifs_authenticated(c))
|
||||
write_len = ALIGN(dlen, 8) + auth_len;
|
||||
else
|
||||
write_len = dlen;
|
||||
|
||||
data->compr_type = cpu_to_le16(compr_type);
|
||||
|
||||
/* Make reservation before allocating sequence numbers */
|
||||
err = make_reservation(c, DATAHD, dlen);
|
||||
err = make_reservation(c, DATAHD, write_len);
|
||||
if (err)
|
||||
goto out_free;
|
||||
|
||||
err = write_node(c, DATAHD, data, dlen, &lnum, &offs);
|
||||
ubifs_prepare_node(c, data, dlen, 0);
|
||||
err = write_head(c, DATAHD, data, write_len, &lnum, &offs, 0);
|
||||
if (err)
|
||||
goto out_release;
|
||||
|
||||
err = ubifs_node_calc_hash(c, data, hash);
|
||||
if (err)
|
||||
goto out_release;
|
||||
|
||||
ubifs_wbuf_add_ino_nolock(&c->jheads[DATAHD].wbuf, key_inum(c, key));
|
||||
release_head(c, DATAHD);
|
||||
|
||||
err = ubifs_tnc_add(c, key, lnum, offs, dlen);
|
||||
ubifs_add_auth_dirt(c, lnum);
|
||||
|
||||
err = ubifs_tnc_add(c, key, lnum, offs, dlen, hash);
|
||||
if (err)
|
||||
goto out_ro;
|
||||
|
||||
|
@ -808,7 +854,9 @@ int ubifs_jnl_write_inode(struct ubifs_info *c, const struct inode *inode)
|
|||
int err, lnum, offs;
|
||||
struct ubifs_ino_node *ino;
|
||||
struct ubifs_inode *ui = ubifs_inode(inode);
|
||||
int sync = 0, len = UBIFS_INO_NODE_SZ, last_reference = !inode->i_nlink;
|
||||
int sync = 0, write_len, ilen = UBIFS_INO_NODE_SZ;
|
||||
int last_reference = !inode->i_nlink;
|
||||
u8 hash[UBIFS_HASH_ARR_SZ];
|
||||
|
||||
dbg_jnl("ino %lu, nlink %u", inode->i_ino, inode->i_nlink);
|
||||
|
||||
|
@ -817,20 +865,30 @@ int ubifs_jnl_write_inode(struct ubifs_info *c, const struct inode *inode)
|
|||
* need to synchronize the write-buffer either.
|
||||
*/
|
||||
if (!last_reference) {
|
||||
len += ui->data_len;
|
||||
ilen += ui->data_len;
|
||||
sync = IS_SYNC(inode);
|
||||
}
|
||||
ino = kmalloc(len, GFP_NOFS);
|
||||
|
||||
if (ubifs_authenticated(c))
|
||||
write_len = ALIGN(ilen, 8) + ubifs_auth_node_sz(c);
|
||||
else
|
||||
write_len = ilen;
|
||||
|
||||
ino = kmalloc(write_len, GFP_NOFS);
|
||||
if (!ino)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Make reservation before allocating sequence numbers */
|
||||
err = make_reservation(c, BASEHD, len);
|
||||
err = make_reservation(c, BASEHD, write_len);
|
||||
if (err)
|
||||
goto out_free;
|
||||
|
||||
pack_inode(c, ino, inode, 1);
|
||||
err = write_head(c, BASEHD, ino, len, &lnum, &offs, sync);
|
||||
err = ubifs_node_calc_hash(c, ino, hash);
|
||||
if (err)
|
||||
goto out_release;
|
||||
|
||||
err = write_head(c, BASEHD, ino, write_len, &lnum, &offs, sync);
|
||||
if (err)
|
||||
goto out_release;
|
||||
if (!sync)
|
||||
|
@ -838,17 +896,19 @@ int ubifs_jnl_write_inode(struct ubifs_info *c, const struct inode *inode)
|
|||
inode->i_ino);
|
||||
release_head(c, BASEHD);
|
||||
|
||||
ubifs_add_auth_dirt(c, lnum);
|
||||
|
||||
if (last_reference) {
|
||||
err = ubifs_tnc_remove_ino(c, inode->i_ino);
|
||||
if (err)
|
||||
goto out_ro;
|
||||
ubifs_delete_orphan(c, inode->i_ino);
|
||||
err = ubifs_add_dirt(c, lnum, len);
|
||||
err = ubifs_add_dirt(c, lnum, ilen);
|
||||
} else {
|
||||
union ubifs_key key;
|
||||
|
||||
ino_key_init(c, &key, inode->i_ino);
|
||||
err = ubifs_tnc_add(c, &key, lnum, offs, len);
|
||||
err = ubifs_tnc_add(c, &key, lnum, offs, ilen, hash);
|
||||
}
|
||||
if (err)
|
||||
goto out_ro;
|
||||
|
@ -958,6 +1018,10 @@ int ubifs_jnl_xrename(struct ubifs_info *c, const struct inode *fst_dir,
|
|||
int aligned_dlen1, aligned_dlen2;
|
||||
int twoparents = (fst_dir != snd_dir);
|
||||
void *p;
|
||||
u8 hash_dent1[UBIFS_HASH_ARR_SZ];
|
||||
u8 hash_dent2[UBIFS_HASH_ARR_SZ];
|
||||
u8 hash_p1[UBIFS_HASH_ARR_SZ];
|
||||
u8 hash_p2[UBIFS_HASH_ARR_SZ];
|
||||
|
||||
ubifs_assert(c, ubifs_inode(fst_dir)->data_len == 0);
|
||||
ubifs_assert(c, ubifs_inode(snd_dir)->data_len == 0);
|
||||
|
@ -973,6 +1037,8 @@ int ubifs_jnl_xrename(struct ubifs_info *c, const struct inode *fst_dir,
|
|||
if (twoparents)
|
||||
len += plen;
|
||||
|
||||
len += ubifs_auth_node_sz(c);
|
||||
|
||||
dent1 = kzalloc(len, GFP_NOFS);
|
||||
if (!dent1)
|
||||
return -ENOMEM;
|
||||
|
@ -993,6 +1059,9 @@ int ubifs_jnl_xrename(struct ubifs_info *c, const struct inode *fst_dir,
|
|||
set_dent_cookie(c, dent1);
|
||||
zero_dent_node_unused(dent1);
|
||||
ubifs_prep_grp_node(c, dent1, dlen1, 0);
|
||||
err = ubifs_node_calc_hash(c, dent1, hash_dent1);
|
||||
if (err)
|
||||
goto out_release;
|
||||
|
||||
/* Make new dent for 2nd entry */
|
||||
dent2 = (void *)dent1 + aligned_dlen1;
|
||||
|
@ -1006,14 +1075,26 @@ int ubifs_jnl_xrename(struct ubifs_info *c, const struct inode *fst_dir,
|
|||
set_dent_cookie(c, dent2);
|
||||
zero_dent_node_unused(dent2);
|
||||
ubifs_prep_grp_node(c, dent2, dlen2, 0);
|
||||
err = ubifs_node_calc_hash(c, dent2, hash_dent2);
|
||||
if (err)
|
||||
goto out_release;
|
||||
|
||||
p = (void *)dent2 + aligned_dlen2;
|
||||
if (!twoparents)
|
||||
if (!twoparents) {
|
||||
pack_inode(c, p, fst_dir, 1);
|
||||
else {
|
||||
err = ubifs_node_calc_hash(c, p, hash_p1);
|
||||
if (err)
|
||||
goto out_release;
|
||||
} else {
|
||||
pack_inode(c, p, fst_dir, 0);
|
||||
err = ubifs_node_calc_hash(c, p, hash_p1);
|
||||
if (err)
|
||||
goto out_release;
|
||||
p += ALIGN(plen, 8);
|
||||
pack_inode(c, p, snd_dir, 1);
|
||||
err = ubifs_node_calc_hash(c, p, hash_p2);
|
||||
if (err)
|
||||
goto out_release;
|
||||
}
|
||||
|
||||
err = write_head(c, BASEHD, dent1, len, &lnum, &offs, sync);
|
||||
|
@ -1027,28 +1108,30 @@ int ubifs_jnl_xrename(struct ubifs_info *c, const struct inode *fst_dir,
|
|||
}
|
||||
release_head(c, BASEHD);
|
||||
|
||||
ubifs_add_auth_dirt(c, lnum);
|
||||
|
||||
dent_key_init(c, &key, snd_dir->i_ino, snd_nm);
|
||||
err = ubifs_tnc_add_nm(c, &key, lnum, offs, dlen1, snd_nm);
|
||||
err = ubifs_tnc_add_nm(c, &key, lnum, offs, dlen1, hash_dent1, snd_nm);
|
||||
if (err)
|
||||
goto out_ro;
|
||||
|
||||
offs += aligned_dlen1;
|
||||
dent_key_init(c, &key, fst_dir->i_ino, fst_nm);
|
||||
err = ubifs_tnc_add_nm(c, &key, lnum, offs, dlen2, fst_nm);
|
||||
err = ubifs_tnc_add_nm(c, &key, lnum, offs, dlen2, hash_dent2, fst_nm);
|
||||
if (err)
|
||||
goto out_ro;
|
||||
|
||||
offs += aligned_dlen2;
|
||||
|
||||
ino_key_init(c, &key, fst_dir->i_ino);
|
||||
err = ubifs_tnc_add(c, &key, lnum, offs, plen);
|
||||
err = ubifs_tnc_add(c, &key, lnum, offs, plen, hash_p1);
|
||||
if (err)
|
||||
goto out_ro;
|
||||
|
||||
if (twoparents) {
|
||||
offs += ALIGN(plen, 8);
|
||||
ino_key_init(c, &key, snd_dir->i_ino);
|
||||
err = ubifs_tnc_add(c, &key, lnum, offs, plen);
|
||||
err = ubifs_tnc_add(c, &key, lnum, offs, plen, hash_p2);
|
||||
if (err)
|
||||
goto out_ro;
|
||||
}
|
||||
|
@ -1101,6 +1184,11 @@ int ubifs_jnl_rename(struct ubifs_info *c, const struct inode *old_dir,
|
|||
int last_reference = !!(new_inode && new_inode->i_nlink == 0);
|
||||
int move = (old_dir != new_dir);
|
||||
struct ubifs_inode *uninitialized_var(new_ui);
|
||||
u8 hash_old_dir[UBIFS_HASH_ARR_SZ];
|
||||
u8 hash_new_dir[UBIFS_HASH_ARR_SZ];
|
||||
u8 hash_new_inode[UBIFS_HASH_ARR_SZ];
|
||||
u8 hash_dent1[UBIFS_HASH_ARR_SZ];
|
||||
u8 hash_dent2[UBIFS_HASH_ARR_SZ];
|
||||
|
||||
ubifs_assert(c, ubifs_inode(old_dir)->data_len == 0);
|
||||
ubifs_assert(c, ubifs_inode(new_dir)->data_len == 0);
|
||||
|
@ -1123,6 +1211,9 @@ int ubifs_jnl_rename(struct ubifs_info *c, const struct inode *old_dir,
|
|||
len = aligned_dlen1 + aligned_dlen2 + ALIGN(ilen, 8) + ALIGN(plen, 8);
|
||||
if (move)
|
||||
len += plen;
|
||||
|
||||
len += ubifs_auth_node_sz(c);
|
||||
|
||||
dent = kzalloc(len, GFP_NOFS);
|
||||
if (!dent)
|
||||
return -ENOMEM;
|
||||
|
@ -1143,6 +1234,9 @@ int ubifs_jnl_rename(struct ubifs_info *c, const struct inode *old_dir,
|
|||
set_dent_cookie(c, dent);
|
||||
zero_dent_node_unused(dent);
|
||||
ubifs_prep_grp_node(c, dent, dlen1, 0);
|
||||
err = ubifs_node_calc_hash(c, dent, hash_dent1);
|
||||
if (err)
|
||||
goto out_release;
|
||||
|
||||
dent2 = (void *)dent + aligned_dlen1;
|
||||
dent2->ch.node_type = UBIFS_DENT_NODE;
|
||||
|
@ -1162,19 +1256,36 @@ int ubifs_jnl_rename(struct ubifs_info *c, const struct inode *old_dir,
|
|||
set_dent_cookie(c, dent2);
|
||||
zero_dent_node_unused(dent2);
|
||||
ubifs_prep_grp_node(c, dent2, dlen2, 0);
|
||||
err = ubifs_node_calc_hash(c, dent2, hash_dent2);
|
||||
if (err)
|
||||
goto out_release;
|
||||
|
||||
p = (void *)dent2 + aligned_dlen2;
|
||||
if (new_inode) {
|
||||
pack_inode(c, p, new_inode, 0);
|
||||
err = ubifs_node_calc_hash(c, p, hash_new_inode);
|
||||
if (err)
|
||||
goto out_release;
|
||||
|
||||
p += ALIGN(ilen, 8);
|
||||
}
|
||||
|
||||
if (!move)
|
||||
if (!move) {
|
||||
pack_inode(c, p, old_dir, 1);
|
||||
else {
|
||||
err = ubifs_node_calc_hash(c, p, hash_old_dir);
|
||||
if (err)
|
||||
goto out_release;
|
||||
} else {
|
||||
pack_inode(c, p, old_dir, 0);
|
||||
err = ubifs_node_calc_hash(c, p, hash_old_dir);
|
||||
if (err)
|
||||
goto out_release;
|
||||
|
||||
p += ALIGN(plen, 8);
|
||||
pack_inode(c, p, new_dir, 1);
|
||||
err = ubifs_node_calc_hash(c, p, hash_new_dir);
|
||||
if (err)
|
||||
goto out_release;
|
||||
}
|
||||
|
||||
if (last_reference) {
|
||||
|
@ -1200,15 +1311,17 @@ int ubifs_jnl_rename(struct ubifs_info *c, const struct inode *old_dir,
|
|||
}
|
||||
release_head(c, BASEHD);
|
||||
|
||||
ubifs_add_auth_dirt(c, lnum);
|
||||
|
||||
dent_key_init(c, &key, new_dir->i_ino, new_nm);
|
||||
err = ubifs_tnc_add_nm(c, &key, lnum, offs, dlen1, new_nm);
|
||||
err = ubifs_tnc_add_nm(c, &key, lnum, offs, dlen1, hash_dent1, new_nm);
|
||||
if (err)
|
||||
goto out_ro;
|
||||
|
||||
offs += aligned_dlen1;
|
||||
if (whiteout) {
|
||||
dent_key_init(c, &key, old_dir->i_ino, old_nm);
|
||||
err = ubifs_tnc_add_nm(c, &key, lnum, offs, dlen2, old_nm);
|
||||
err = ubifs_tnc_add_nm(c, &key, lnum, offs, dlen2, hash_dent2, old_nm);
|
||||
if (err)
|
||||
goto out_ro;
|
||||
|
||||
|
@ -1227,21 +1340,21 @@ int ubifs_jnl_rename(struct ubifs_info *c, const struct inode *old_dir,
|
|||
offs += aligned_dlen2;
|
||||
if (new_inode) {
|
||||
ino_key_init(c, &key, new_inode->i_ino);
|
||||
err = ubifs_tnc_add(c, &key, lnum, offs, ilen);
|
||||
err = ubifs_tnc_add(c, &key, lnum, offs, ilen, hash_new_inode);
|
||||
if (err)
|
||||
goto out_ro;
|
||||
offs += ALIGN(ilen, 8);
|
||||
}
|
||||
|
||||
ino_key_init(c, &key, old_dir->i_ino);
|
||||
err = ubifs_tnc_add(c, &key, lnum, offs, plen);
|
||||
err = ubifs_tnc_add(c, &key, lnum, offs, plen, hash_old_dir);
|
||||
if (err)
|
||||
goto out_ro;
|
||||
|
||||
if (move) {
|
||||
offs += ALIGN(plen, 8);
|
||||
ino_key_init(c, &key, new_dir->i_ino);
|
||||
err = ubifs_tnc_add(c, &key, lnum, offs, plen);
|
||||
err = ubifs_tnc_add(c, &key, lnum, offs, plen, hash_new_dir);
|
||||
if (err)
|
||||
goto out_ro;
|
||||
}
|
||||
|
@ -1360,6 +1473,8 @@ int ubifs_jnl_truncate(struct ubifs_info *c, const struct inode *inode,
|
|||
struct ubifs_inode *ui = ubifs_inode(inode);
|
||||
ino_t inum = inode->i_ino;
|
||||
unsigned int blk;
|
||||
u8 hash_ino[UBIFS_HASH_ARR_SZ];
|
||||
u8 hash_dn[UBIFS_HASH_ARR_SZ];
|
||||
|
||||
dbg_jnl("ino %lu, size %lld -> %lld",
|
||||
(unsigned long)inum, old_size, new_size);
|
||||
|
@ -1369,6 +1484,9 @@ int ubifs_jnl_truncate(struct ubifs_info *c, const struct inode *inode,
|
|||
|
||||
sz = UBIFS_TRUN_NODE_SZ + UBIFS_INO_NODE_SZ +
|
||||
UBIFS_MAX_DATA_NODE_SZ * WORST_COMPR_FACTOR;
|
||||
|
||||
sz += ubifs_auth_node_sz(c);
|
||||
|
||||
ino = kmalloc(sz, GFP_NOFS);
|
||||
if (!ino)
|
||||
return -ENOMEM;
|
||||
|
@ -1414,16 +1532,28 @@ int ubifs_jnl_truncate(struct ubifs_info *c, const struct inode *inode,
|
|||
|
||||
/* Must make reservation before allocating sequence numbers */
|
||||
len = UBIFS_TRUN_NODE_SZ + UBIFS_INO_NODE_SZ;
|
||||
if (dlen)
|
||||
|
||||
if (ubifs_authenticated(c))
|
||||
len += ALIGN(dlen, 8) + ubifs_auth_node_sz(c);
|
||||
else
|
||||
len += dlen;
|
||||
|
||||
err = make_reservation(c, BASEHD, len);
|
||||
if (err)
|
||||
goto out_free;
|
||||
|
||||
pack_inode(c, ino, inode, 0);
|
||||
err = ubifs_node_calc_hash(c, ino, hash_ino);
|
||||
if (err)
|
||||
goto out_release;
|
||||
|
||||
ubifs_prep_grp_node(c, trun, UBIFS_TRUN_NODE_SZ, dlen ? 0 : 1);
|
||||
if (dlen)
|
||||
if (dlen) {
|
||||
ubifs_prep_grp_node(c, dn, dlen, 1);
|
||||
err = ubifs_node_calc_hash(c, dn, hash_dn);
|
||||
if (err)
|
||||
goto out_release;
|
||||
}
|
||||
|
||||
err = write_head(c, BASEHD, ino, len, &lnum, &offs, sync);
|
||||
if (err)
|
||||
|
@ -1432,15 +1562,17 @@ int ubifs_jnl_truncate(struct ubifs_info *c, const struct inode *inode,
|
|||
ubifs_wbuf_add_ino_nolock(&c->jheads[BASEHD].wbuf, inum);
|
||||
release_head(c, BASEHD);
|
||||
|
||||
ubifs_add_auth_dirt(c, lnum);
|
||||
|
||||
if (dlen) {
|
||||
sz = offs + UBIFS_INO_NODE_SZ + UBIFS_TRUN_NODE_SZ;
|
||||
err = ubifs_tnc_add(c, &key, lnum, sz, dlen);
|
||||
err = ubifs_tnc_add(c, &key, lnum, sz, dlen, hash_dn);
|
||||
if (err)
|
||||
goto out_ro;
|
||||
}
|
||||
|
||||
ino_key_init(c, &key, inum);
|
||||
err = ubifs_tnc_add(c, &key, lnum, offs, UBIFS_INO_NODE_SZ);
|
||||
err = ubifs_tnc_add(c, &key, lnum, offs, UBIFS_INO_NODE_SZ, hash_ino);
|
||||
if (err)
|
||||
goto out_ro;
|
||||
|
||||
|
@ -1495,12 +1627,13 @@ int ubifs_jnl_delete_xattr(struct ubifs_info *c, const struct inode *host,
|
|||
const struct inode *inode,
|
||||
const struct fscrypt_name *nm)
|
||||
{
|
||||
int err, xlen, hlen, len, lnum, xent_offs, aligned_xlen;
|
||||
int err, xlen, hlen, len, lnum, xent_offs, aligned_xlen, write_len;
|
||||
struct ubifs_dent_node *xent;
|
||||
struct ubifs_ino_node *ino;
|
||||
union ubifs_key xent_key, key1, key2;
|
||||
int sync = IS_DIRSYNC(host);
|
||||
struct ubifs_inode *host_ui = ubifs_inode(host);
|
||||
u8 hash[UBIFS_HASH_ARR_SZ];
|
||||
|
||||
ubifs_assert(c, inode->i_nlink == 0);
|
||||
ubifs_assert(c, mutex_is_locked(&host_ui->ui_mutex));
|
||||
|
@ -1514,12 +1647,14 @@ int ubifs_jnl_delete_xattr(struct ubifs_info *c, const struct inode *host,
|
|||
hlen = host_ui->data_len + UBIFS_INO_NODE_SZ;
|
||||
len = aligned_xlen + UBIFS_INO_NODE_SZ + ALIGN(hlen, 8);
|
||||
|
||||
xent = kzalloc(len, GFP_NOFS);
|
||||
write_len = len + ubifs_auth_node_sz(c);
|
||||
|
||||
xent = kzalloc(write_len, GFP_NOFS);
|
||||
if (!xent)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Make reservation before allocating sequence numbers */
|
||||
err = make_reservation(c, BASEHD, len);
|
||||
err = make_reservation(c, BASEHD, write_len);
|
||||
if (err) {
|
||||
kfree(xent);
|
||||
return err;
|
||||
|
@ -1540,11 +1675,16 @@ int ubifs_jnl_delete_xattr(struct ubifs_info *c, const struct inode *host,
|
|||
pack_inode(c, ino, inode, 0);
|
||||
ino = (void *)ino + UBIFS_INO_NODE_SZ;
|
||||
pack_inode(c, ino, host, 1);
|
||||
err = ubifs_node_calc_hash(c, ino, hash);
|
||||
if (err)
|
||||
goto out_release;
|
||||
|
||||
err = write_head(c, BASEHD, xent, len, &lnum, &xent_offs, sync);
|
||||
err = write_head(c, BASEHD, xent, write_len, &lnum, &xent_offs, sync);
|
||||
if (!sync && !err)
|
||||
ubifs_wbuf_add_ino_nolock(&c->jheads[BASEHD].wbuf, host->i_ino);
|
||||
release_head(c, BASEHD);
|
||||
|
||||
ubifs_add_auth_dirt(c, lnum);
|
||||
kfree(xent);
|
||||
if (err)
|
||||
goto out_ro;
|
||||
|
@ -1572,7 +1712,7 @@ int ubifs_jnl_delete_xattr(struct ubifs_info *c, const struct inode *host,
|
|||
|
||||
/* And update TNC with the new host inode position */
|
||||
ino_key_init(c, &key1, host->i_ino);
|
||||
err = ubifs_tnc_add(c, &key1, lnum, xent_offs + len - hlen, hlen);
|
||||
err = ubifs_tnc_add(c, &key1, lnum, xent_offs + len - hlen, hlen, hash);
|
||||
if (err)
|
||||
goto out_ro;
|
||||
|
||||
|
@ -1583,6 +1723,9 @@ int ubifs_jnl_delete_xattr(struct ubifs_info *c, const struct inode *host,
|
|||
mark_inode_clean(c, host_ui);
|
||||
return 0;
|
||||
|
||||
out_release:
|
||||
kfree(xent);
|
||||
release_head(c, BASEHD);
|
||||
out_ro:
|
||||
ubifs_ro_mode(c, err);
|
||||
finish_reservation(c);
|
||||
|
@ -1610,6 +1753,8 @@ int ubifs_jnl_change_xattr(struct ubifs_info *c, const struct inode *inode,
|
|||
struct ubifs_ino_node *ino;
|
||||
union ubifs_key key;
|
||||
int sync = IS_DIRSYNC(host);
|
||||
u8 hash_host[UBIFS_HASH_ARR_SZ];
|
||||
u8 hash[UBIFS_HASH_ARR_SZ];
|
||||
|
||||
dbg_jnl("ino %lu, ino %lu", host->i_ino, inode->i_ino);
|
||||
ubifs_assert(c, host->i_nlink > 0);
|
||||
|
@ -1621,6 +1766,8 @@ int ubifs_jnl_change_xattr(struct ubifs_info *c, const struct inode *inode,
|
|||
aligned_len1 = ALIGN(len1, 8);
|
||||
aligned_len = aligned_len1 + ALIGN(len2, 8);
|
||||
|
||||
aligned_len += ubifs_auth_node_sz(c);
|
||||
|
||||
ino = kzalloc(aligned_len, GFP_NOFS);
|
||||
if (!ino)
|
||||
return -ENOMEM;
|
||||
|
@ -1631,7 +1778,13 @@ int ubifs_jnl_change_xattr(struct ubifs_info *c, const struct inode *inode,
|
|||
goto out_free;
|
||||
|
||||
pack_inode(c, ino, host, 0);
|
||||
err = ubifs_node_calc_hash(c, ino, hash_host);
|
||||
if (err)
|
||||
goto out_release;
|
||||
pack_inode(c, (void *)ino + aligned_len1, inode, 1);
|
||||
err = ubifs_node_calc_hash(c, (void *)ino + aligned_len1, hash);
|
||||
if (err)
|
||||
goto out_release;
|
||||
|
||||
err = write_head(c, BASEHD, ino, aligned_len, &lnum, &offs, 0);
|
||||
if (!sync && !err) {
|
||||
|
@ -1644,13 +1797,15 @@ int ubifs_jnl_change_xattr(struct ubifs_info *c, const struct inode *inode,
|
|||
if (err)
|
||||
goto out_ro;
|
||||
|
||||
ubifs_add_auth_dirt(c, lnum);
|
||||
|
||||
ino_key_init(c, &key, host->i_ino);
|
||||
err = ubifs_tnc_add(c, &key, lnum, offs, len1);
|
||||
err = ubifs_tnc_add(c, &key, lnum, offs, len1, hash_host);
|
||||
if (err)
|
||||
goto out_ro;
|
||||
|
||||
ino_key_init(c, &key, inode->i_ino);
|
||||
err = ubifs_tnc_add(c, &key, lnum, offs + aligned_len1, len2);
|
||||
err = ubifs_tnc_add(c, &key, lnum, offs + aligned_len1, len2, hash);
|
||||
if (err)
|
||||
goto out_ro;
|
||||
|
||||
|
@ -1662,6 +1817,8 @@ int ubifs_jnl_change_xattr(struct ubifs_info *c, const struct inode *inode,
|
|||
kfree(ino);
|
||||
return 0;
|
||||
|
||||
out_release:
|
||||
release_head(c, BASEHD);
|
||||
out_ro:
|
||||
ubifs_ro_mode(c, err);
|
||||
finish_reservation(c);
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@
|
|||
#include "ubifs.h"
|
||||
|
||||
static int try_read_node(const struct ubifs_info *c, void *buf, int type,
|
||||
int len, int lnum, int offs);
|
||||
struct ubifs_zbranch *zbr);
|
||||
static int fallible_read_node(struct ubifs_info *c, const union ubifs_key *key,
|
||||
struct ubifs_zbranch *zbr, void *node);
|
||||
|
||||
|
@ -433,9 +433,7 @@ static int tnc_read_hashed_node(struct ubifs_info *c, struct ubifs_zbranch *zbr,
|
|||
* @c: UBIFS file-system description object
|
||||
* @buf: buffer to read to
|
||||
* @type: node type
|
||||
* @len: node length (not aligned)
|
||||
* @lnum: LEB number of node to read
|
||||
* @offs: offset of node to read
|
||||
* @zbr: the zbranch describing the node to read
|
||||
*
|
||||
* This function tries to read a node of known type and length, checks it and
|
||||
* stores it in @buf. This function returns %1 if a node is present and %0 if
|
||||
|
@ -453,8 +451,11 @@ static int tnc_read_hashed_node(struct ubifs_info *c, struct ubifs_zbranch *zbr,
|
|||
* journal nodes may potentially be corrupted, so checking is required.
|
||||
*/
|
||||
static int try_read_node(const struct ubifs_info *c, void *buf, int type,
|
||||
int len, int lnum, int offs)
|
||||
struct ubifs_zbranch *zbr)
|
||||
{
|
||||
int len = zbr->len;
|
||||
int lnum = zbr->lnum;
|
||||
int offs = zbr->offs;
|
||||
int err, node_len;
|
||||
struct ubifs_ch *ch = buf;
|
||||
uint32_t crc, node_crc;
|
||||
|
@ -487,6 +488,12 @@ static int try_read_node(const struct ubifs_info *c, void *buf, int type,
|
|||
if (crc != node_crc)
|
||||
return 0;
|
||||
|
||||
err = ubifs_node_check_hash(c, buf, zbr->hash);
|
||||
if (err) {
|
||||
ubifs_bad_hash(c, buf, zbr->hash, lnum, offs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -507,8 +514,7 @@ static int fallible_read_node(struct ubifs_info *c, const union ubifs_key *key,
|
|||
|
||||
dbg_tnck(key, "LEB %d:%d, key ", zbr->lnum, zbr->offs);
|
||||
|
||||
ret = try_read_node(c, node, key_type(c, key), zbr->len, zbr->lnum,
|
||||
zbr->offs);
|
||||
ret = try_read_node(c, node, key_type(c, key), zbr);
|
||||
if (ret == 1) {
|
||||
union ubifs_key node_key;
|
||||
struct ubifs_dent_node *dent = node;
|
||||
|
@ -1713,6 +1719,12 @@ static int validate_data_node(struct ubifs_info *c, void *buf,
|
|||
goto out;
|
||||
}
|
||||
|
||||
err = ubifs_node_check_hash(c, buf, zbr->hash);
|
||||
if (err) {
|
||||
ubifs_bad_hash(c, buf, zbr->hash, zbr->lnum, zbr->offs);
|
||||
return err;
|
||||
}
|
||||
|
||||
len = le32_to_cpu(ch->len);
|
||||
if (len != zbr->len) {
|
||||
ubifs_err(c, "bad node length %d, expected %d", len, zbr->len);
|
||||
|
@ -2260,13 +2272,14 @@ do_split:
|
|||
* @lnum: LEB number of node
|
||||
* @offs: node offset
|
||||
* @len: node length
|
||||
* @hash: The hash over the node
|
||||
*
|
||||
* This function adds a node with key @key to TNC. The node may be new or it may
|
||||
* obsolete some existing one. Returns %0 on success or negative error code on
|
||||
* failure.
|
||||
*/
|
||||
int ubifs_tnc_add(struct ubifs_info *c, const union ubifs_key *key, int lnum,
|
||||
int offs, int len)
|
||||
int offs, int len, const u8 *hash)
|
||||
{
|
||||
int found, n, err = 0;
|
||||
struct ubifs_znode *znode;
|
||||
|
@ -2281,6 +2294,7 @@ int ubifs_tnc_add(struct ubifs_info *c, const union ubifs_key *key, int lnum,
|
|||
zbr.lnum = lnum;
|
||||
zbr.offs = offs;
|
||||
zbr.len = len;
|
||||
ubifs_copy_hash(c, hash, zbr.hash);
|
||||
key_copy(c, key, &zbr.key);
|
||||
err = tnc_insert(c, znode, &zbr, n + 1);
|
||||
} else if (found == 1) {
|
||||
|
@ -2291,6 +2305,7 @@ int ubifs_tnc_add(struct ubifs_info *c, const union ubifs_key *key, int lnum,
|
|||
zbr->lnum = lnum;
|
||||
zbr->offs = offs;
|
||||
zbr->len = len;
|
||||
ubifs_copy_hash(c, hash, zbr->hash);
|
||||
} else
|
||||
err = found;
|
||||
if (!err)
|
||||
|
@ -2392,13 +2407,14 @@ out_unlock:
|
|||
* @lnum: LEB number of node
|
||||
* @offs: node offset
|
||||
* @len: node length
|
||||
* @hash: The hash over the node
|
||||
* @nm: node name
|
||||
*
|
||||
* This is the same as 'ubifs_tnc_add()' but it should be used with keys which
|
||||
* may have collisions, like directory entry keys.
|
||||
*/
|
||||
int ubifs_tnc_add_nm(struct ubifs_info *c, const union ubifs_key *key,
|
||||
int lnum, int offs, int len,
|
||||
int lnum, int offs, int len, const u8 *hash,
|
||||
const struct fscrypt_name *nm)
|
||||
{
|
||||
int found, n, err = 0;
|
||||
|
@ -2441,6 +2457,7 @@ int ubifs_tnc_add_nm(struct ubifs_info *c, const union ubifs_key *key,
|
|||
zbr->lnum = lnum;
|
||||
zbr->offs = offs;
|
||||
zbr->len = len;
|
||||
ubifs_copy_hash(c, hash, zbr->hash);
|
||||
goto out_unlock;
|
||||
}
|
||||
}
|
||||
|
@ -2452,6 +2469,7 @@ int ubifs_tnc_add_nm(struct ubifs_info *c, const union ubifs_key *key,
|
|||
zbr.lnum = lnum;
|
||||
zbr.offs = offs;
|
||||
zbr.len = len;
|
||||
ubifs_copy_hash(c, hash, zbr.hash);
|
||||
key_copy(c, key, &zbr.key);
|
||||
err = tnc_insert(c, znode, &zbr, n + 1);
|
||||
if (err)
|
||||
|
|
|
@ -38,6 +38,7 @@ static int make_idx_node(struct ubifs_info *c, struct ubifs_idx_node *idx,
|
|||
struct ubifs_znode *znode, int lnum, int offs, int len)
|
||||
{
|
||||
struct ubifs_znode *zp;
|
||||
u8 hash[UBIFS_HASH_ARR_SZ];
|
||||
int i, err;
|
||||
|
||||
/* Make index node */
|
||||
|
@ -52,6 +53,7 @@ static int make_idx_node(struct ubifs_info *c, struct ubifs_idx_node *idx,
|
|||
br->lnum = cpu_to_le32(zbr->lnum);
|
||||
br->offs = cpu_to_le32(zbr->offs);
|
||||
br->len = cpu_to_le32(zbr->len);
|
||||
ubifs_copy_hash(c, zbr->hash, ubifs_branch_hash(c, br));
|
||||
if (!zbr->lnum || !zbr->len) {
|
||||
ubifs_err(c, "bad ref in znode");
|
||||
ubifs_dump_znode(c, znode);
|
||||
|
@ -62,6 +64,7 @@ static int make_idx_node(struct ubifs_info *c, struct ubifs_idx_node *idx,
|
|||
}
|
||||
}
|
||||
ubifs_prepare_node(c, idx, len, 0);
|
||||
ubifs_node_calc_hash(c, idx, hash);
|
||||
|
||||
znode->lnum = lnum;
|
||||
znode->offs = offs;
|
||||
|
@ -78,10 +81,12 @@ static int make_idx_node(struct ubifs_info *c, struct ubifs_idx_node *idx,
|
|||
zbr->lnum = lnum;
|
||||
zbr->offs = offs;
|
||||
zbr->len = len;
|
||||
ubifs_copy_hash(c, hash, zbr->hash);
|
||||
} else {
|
||||
c->zroot.lnum = lnum;
|
||||
c->zroot.offs = offs;
|
||||
c->zroot.len = len;
|
||||
ubifs_copy_hash(c, hash, c->zroot.hash);
|
||||
}
|
||||
c->calc_idx_sz += ALIGN(len, 8);
|
||||
|
||||
|
@ -647,6 +652,8 @@ static int get_znodes_to_commit(struct ubifs_info *c)
|
|||
znode->cnext = c->cnext;
|
||||
break;
|
||||
}
|
||||
znode->cparent = znode->parent;
|
||||
znode->ciip = znode->iip;
|
||||
znode->cnext = cnext;
|
||||
znode = cnext;
|
||||
cnt += 1;
|
||||
|
@ -840,6 +847,8 @@ static int write_index(struct ubifs_info *c)
|
|||
}
|
||||
|
||||
while (1) {
|
||||
u8 hash[UBIFS_HASH_ARR_SZ];
|
||||
|
||||
cond_resched();
|
||||
|
||||
znode = cnext;
|
||||
|
@ -857,6 +866,7 @@ static int write_index(struct ubifs_info *c)
|
|||
br->lnum = cpu_to_le32(zbr->lnum);
|
||||
br->offs = cpu_to_le32(zbr->offs);
|
||||
br->len = cpu_to_le32(zbr->len);
|
||||
ubifs_copy_hash(c, zbr->hash, ubifs_branch_hash(c, br));
|
||||
if (!zbr->lnum || !zbr->len) {
|
||||
ubifs_err(c, "bad ref in znode");
|
||||
ubifs_dump_znode(c, znode);
|
||||
|
@ -868,6 +878,23 @@ static int write_index(struct ubifs_info *c)
|
|||
}
|
||||
len = ubifs_idx_node_sz(c, znode->child_cnt);
|
||||
ubifs_prepare_node(c, idx, len, 0);
|
||||
ubifs_node_calc_hash(c, idx, hash);
|
||||
|
||||
mutex_lock(&c->tnc_mutex);
|
||||
|
||||
if (znode->cparent)
|
||||
ubifs_copy_hash(c, hash,
|
||||
znode->cparent->zbranch[znode->ciip].hash);
|
||||
|
||||
if (znode->parent) {
|
||||
if (!ubifs_zn_obsolete(znode))
|
||||
ubifs_copy_hash(c, hash,
|
||||
znode->parent->zbranch[znode->iip].hash);
|
||||
} else {
|
||||
ubifs_copy_hash(c, hash, c->zroot.hash);
|
||||
}
|
||||
|
||||
mutex_unlock(&c->tnc_mutex);
|
||||
|
||||
/* Determine the index node position */
|
||||
if (lnum == -1) {
|
||||
|
|
|
@ -265,9 +265,7 @@ long ubifs_destroy_tnc_subtree(const struct ubifs_info *c,
|
|||
/**
|
||||
* read_znode - read an indexing node from flash and fill znode.
|
||||
* @c: UBIFS file-system description object
|
||||
* @lnum: LEB of the indexing node to read
|
||||
* @offs: node offset
|
||||
* @len: node length
|
||||
* @zzbr: the zbranch describing the node to read
|
||||
* @znode: znode to read to
|
||||
*
|
||||
* This function reads an indexing node from the flash media and fills znode
|
||||
|
@ -276,9 +274,12 @@ long ubifs_destroy_tnc_subtree(const struct ubifs_info *c,
|
|||
* is wrong with it, this function prints complaint messages and returns
|
||||
* %-EINVAL.
|
||||
*/
|
||||
static int read_znode(struct ubifs_info *c, int lnum, int offs, int len,
|
||||
static int read_znode(struct ubifs_info *c, struct ubifs_zbranch *zzbr,
|
||||
struct ubifs_znode *znode)
|
||||
{
|
||||
int lnum = zzbr->lnum;
|
||||
int offs = zzbr->offs;
|
||||
int len = zzbr->len;
|
||||
int i, err, type, cmp;
|
||||
struct ubifs_idx_node *idx;
|
||||
|
||||
|
@ -292,6 +293,12 @@ static int read_znode(struct ubifs_info *c, int lnum, int offs, int len,
|
|||
return err;
|
||||
}
|
||||
|
||||
err = ubifs_node_check_hash(c, idx, zzbr->hash);
|
||||
if (err) {
|
||||
ubifs_bad_hash(c, idx, zzbr->hash, lnum, offs);
|
||||
return err;
|
||||
}
|
||||
|
||||
znode->child_cnt = le16_to_cpu(idx->child_cnt);
|
||||
znode->level = le16_to_cpu(idx->level);
|
||||
|
||||
|
@ -308,13 +315,14 @@ static int read_znode(struct ubifs_info *c, int lnum, int offs, int len,
|
|||
}
|
||||
|
||||
for (i = 0; i < znode->child_cnt; i++) {
|
||||
const struct ubifs_branch *br = ubifs_idx_branch(c, idx, i);
|
||||
struct ubifs_branch *br = ubifs_idx_branch(c, idx, i);
|
||||
struct ubifs_zbranch *zbr = &znode->zbranch[i];
|
||||
|
||||
key_read(c, &br->key, &zbr->key);
|
||||
zbr->lnum = le32_to_cpu(br->lnum);
|
||||
zbr->offs = le32_to_cpu(br->offs);
|
||||
zbr->len = le32_to_cpu(br->len);
|
||||
ubifs_copy_hash(c, ubifs_branch_hash(c, br), zbr->hash);
|
||||
zbr->znode = NULL;
|
||||
|
||||
/* Validate branch */
|
||||
|
@ -425,7 +433,7 @@ struct ubifs_znode *ubifs_load_znode(struct ubifs_info *c,
|
|||
if (!znode)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
err = read_znode(c, zbr->lnum, zbr->offs, zbr->len, znode);
|
||||
err = read_znode(c, zbr, znode);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
|
@ -496,5 +504,11 @@ int ubifs_tnc_read_node(struct ubifs_info *c, struct ubifs_zbranch *zbr,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
err = ubifs_node_check_hash(c, node, zbr->hash);
|
||||
if (err) {
|
||||
ubifs_bad_hash(c, node, zbr->hash, zbr->lnum, zbr->offs);
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -286,6 +286,7 @@ enum {
|
|||
#define UBIFS_IDX_NODE_SZ sizeof(struct ubifs_idx_node)
|
||||
#define UBIFS_CS_NODE_SZ sizeof(struct ubifs_cs_node)
|
||||
#define UBIFS_ORPH_NODE_SZ sizeof(struct ubifs_orph_node)
|
||||
#define UBIFS_AUTH_NODE_SZ sizeof(struct ubifs_auth_node)
|
||||
/* Extended attribute entry nodes are identical to directory entry nodes */
|
||||
#define UBIFS_XENT_NODE_SZ UBIFS_DENT_NODE_SZ
|
||||
/* Only this does not have to be multiple of 8 bytes */
|
||||
|
@ -300,6 +301,12 @@ enum {
|
|||
/* The largest UBIFS node */
|
||||
#define UBIFS_MAX_NODE_SZ UBIFS_MAX_INO_NODE_SZ
|
||||
|
||||
/* The maxmimum size of a hash, enough for sha512 */
|
||||
#define UBIFS_MAX_HASH_LEN 64
|
||||
|
||||
/* The maxmimum size of a hmac, enough for hmac(sha512) */
|
||||
#define UBIFS_MAX_HMAC_LEN 64
|
||||
|
||||
/*
|
||||
* xattr name of UBIFS encryption context, we don't use a prefix
|
||||
* nor a long name to not waste space on the flash.
|
||||
|
@ -365,6 +372,7 @@ enum {
|
|||
* UBIFS_IDX_NODE: index node
|
||||
* UBIFS_CS_NODE: commit start node
|
||||
* UBIFS_ORPH_NODE: orphan node
|
||||
* UBIFS_AUTH_NODE: authentication node
|
||||
* UBIFS_NODE_TYPES_CNT: count of supported node types
|
||||
*
|
||||
* Note, we index arrays by these numbers, so keep them low and contiguous.
|
||||
|
@ -384,6 +392,7 @@ enum {
|
|||
UBIFS_IDX_NODE,
|
||||
UBIFS_CS_NODE,
|
||||
UBIFS_ORPH_NODE,
|
||||
UBIFS_AUTH_NODE,
|
||||
UBIFS_NODE_TYPES_CNT,
|
||||
};
|
||||
|
||||
|
@ -421,15 +430,19 @@ enum {
|
|||
* UBIFS_FLG_DOUBLE_HASH: store a 32bit cookie in directory entry nodes to
|
||||
* support 64bit cookies for lookups by hash
|
||||
* UBIFS_FLG_ENCRYPTION: this filesystem contains encrypted files
|
||||
* UBIFS_FLG_AUTHENTICATION: this filesystem contains hashes for authentication
|
||||
*/
|
||||
enum {
|
||||
UBIFS_FLG_BIGLPT = 0x02,
|
||||
UBIFS_FLG_SPACE_FIXUP = 0x04,
|
||||
UBIFS_FLG_DOUBLE_HASH = 0x08,
|
||||
UBIFS_FLG_ENCRYPTION = 0x10,
|
||||
UBIFS_FLG_AUTHENTICATION = 0x20,
|
||||
};
|
||||
|
||||
#define UBIFS_FLG_MASK (UBIFS_FLG_BIGLPT|UBIFS_FLG_SPACE_FIXUP|UBIFS_FLG_DOUBLE_HASH|UBIFS_FLG_ENCRYPTION)
|
||||
#define UBIFS_FLG_MASK (UBIFS_FLG_BIGLPT | UBIFS_FLG_SPACE_FIXUP | \
|
||||
UBIFS_FLG_DOUBLE_HASH | UBIFS_FLG_ENCRYPTION | \
|
||||
UBIFS_FLG_AUTHENTICATION)
|
||||
|
||||
/**
|
||||
* struct ubifs_ch - common header node.
|
||||
|
@ -633,6 +646,10 @@ struct ubifs_pad_node {
|
|||
* @time_gran: time granularity in nanoseconds
|
||||
* @uuid: UUID generated when the file system image was created
|
||||
* @ro_compat_version: UBIFS R/O compatibility version
|
||||
* @hmac: HMAC to authenticate the superblock node
|
||||
* @hmac_wkm: HMAC of a well known message (the string "UBIFS") as a convenience
|
||||
* to the user to check if the correct key is passed.
|
||||
* @hash_algo: The hash algo used for this filesystem (one of enum hash_algo)
|
||||
*/
|
||||
struct ubifs_sb_node {
|
||||
struct ubifs_ch ch;
|
||||
|
@ -660,7 +677,10 @@ struct ubifs_sb_node {
|
|||
__le32 time_gran;
|
||||
__u8 uuid[16];
|
||||
__le32 ro_compat_version;
|
||||
__u8 padding2[3968];
|
||||
__u8 hmac[UBIFS_MAX_HMAC_LEN];
|
||||
__u8 hmac_wkm[UBIFS_MAX_HMAC_LEN];
|
||||
__le16 hash_algo;
|
||||
__u8 padding2[3838];
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
|
@ -695,6 +715,9 @@ struct ubifs_sb_node {
|
|||
* @empty_lebs: number of empty logical eraseblocks
|
||||
* @idx_lebs: number of indexing logical eraseblocks
|
||||
* @leb_cnt: count of LEBs used by file-system
|
||||
* @hash_root_idx: the hash of the root index node
|
||||
* @hash_lpt: the hash of the LPT
|
||||
* @hmac: HMAC to authenticate the master node
|
||||
* @padding: reserved for future, zeroes
|
||||
*/
|
||||
struct ubifs_mst_node {
|
||||
|
@ -727,7 +750,10 @@ struct ubifs_mst_node {
|
|||
__le32 empty_lebs;
|
||||
__le32 idx_lebs;
|
||||
__le32 leb_cnt;
|
||||
__u8 padding[344];
|
||||
__u8 hash_root_idx[UBIFS_MAX_HASH_LEN];
|
||||
__u8 hash_lpt[UBIFS_MAX_HASH_LEN];
|
||||
__u8 hmac[UBIFS_MAX_HMAC_LEN];
|
||||
__u8 padding[152];
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
|
@ -746,12 +772,26 @@ struct ubifs_ref_node {
|
|||
__u8 padding[28];
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* struct ubifs_auth_node - node for authenticating other nodes
|
||||
* @ch: common header
|
||||
* @hmac: The HMAC
|
||||
*/
|
||||
struct ubifs_auth_node {
|
||||
struct ubifs_ch ch;
|
||||
__u8 hmac[];
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* struct ubifs_branch - key/reference/length branch
|
||||
* @lnum: LEB number of the target node
|
||||
* @offs: offset within @lnum
|
||||
* @len: target node length
|
||||
* @key: key
|
||||
*
|
||||
* In an authenticated UBIFS we have the hash of the referenced node after @key.
|
||||
* This can't be added to the struct type definition because @key is a
|
||||
* dynamically sized element already.
|
||||
*/
|
||||
struct ubifs_branch {
|
||||
__le32 lnum;
|
||||
|
|
253
fs/ubifs/ubifs.h
253
fs/ubifs/ubifs.h
|
@ -39,6 +39,9 @@
|
|||
#include <linux/security.h>
|
||||
#include <linux/xattr.h>
|
||||
#include <linux/random.h>
|
||||
#include <crypto/hash_info.h>
|
||||
#include <crypto/hash.h>
|
||||
#include <crypto/algapi.h>
|
||||
|
||||
#define __FS_HAS_ENCRYPTION IS_ENABLED(CONFIG_UBIFS_FS_ENCRYPTION)
|
||||
#include <linux/fscrypt.h>
|
||||
|
@ -157,6 +160,14 @@
|
|||
/* Maximum number of data nodes to bulk-read */
|
||||
#define UBIFS_MAX_BULK_READ 32
|
||||
|
||||
#ifdef CONFIG_UBIFS_FS_AUTHENTICATION
|
||||
#define UBIFS_HASH_ARR_SZ UBIFS_MAX_HASH_LEN
|
||||
#define UBIFS_HMAC_ARR_SZ UBIFS_MAX_HMAC_LEN
|
||||
#else
|
||||
#define UBIFS_HASH_ARR_SZ 0
|
||||
#define UBIFS_HMAC_ARR_SZ 0
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Lockdep classes for UBIFS inode @ui_mutex.
|
||||
*/
|
||||
|
@ -706,6 +717,7 @@ struct ubifs_wbuf {
|
|||
* @jhead: journal head number this bud belongs to
|
||||
* @list: link in the list buds belonging to the same journal head
|
||||
* @rb: link in the tree of all buds
|
||||
* @log_hash: the log hash from the commit start node up to this bud
|
||||
*/
|
||||
struct ubifs_bud {
|
||||
int lnum;
|
||||
|
@ -713,6 +725,7 @@ struct ubifs_bud {
|
|||
int jhead;
|
||||
struct list_head list;
|
||||
struct rb_node rb;
|
||||
struct shash_desc *log_hash;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -720,6 +733,7 @@ struct ubifs_bud {
|
|||
* @wbuf: head's write-buffer
|
||||
* @buds_list: list of bud LEBs belonging to this journal head
|
||||
* @grouped: non-zero if UBIFS groups nodes when writing to this journal head
|
||||
* @log_hash: the log hash from the commit start node up to this journal head
|
||||
*
|
||||
* Note, the @buds list is protected by the @c->buds_lock.
|
||||
*/
|
||||
|
@ -727,6 +741,7 @@ struct ubifs_jhead {
|
|||
struct ubifs_wbuf wbuf;
|
||||
struct list_head buds_list;
|
||||
unsigned int grouped:1;
|
||||
struct shash_desc *log_hash;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -736,6 +751,7 @@ struct ubifs_jhead {
|
|||
* @lnum: LEB number of the target node (indexing node or data node)
|
||||
* @offs: target node offset within @lnum
|
||||
* @len: target node length
|
||||
* @hash: the hash of the target node
|
||||
*/
|
||||
struct ubifs_zbranch {
|
||||
union ubifs_key key;
|
||||
|
@ -746,12 +762,15 @@ struct ubifs_zbranch {
|
|||
int lnum;
|
||||
int offs;
|
||||
int len;
|
||||
u8 hash[UBIFS_HASH_ARR_SZ];
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ubifs_znode - in-memory representation of an indexing node.
|
||||
* @parent: parent znode or NULL if it is the root
|
||||
* @cnext: next znode to commit
|
||||
* @cparent: parent node for this commit
|
||||
* @ciip: index in cparent's zbranch array
|
||||
* @flags: znode flags (%DIRTY_ZNODE, %COW_ZNODE or %OBSOLETE_ZNODE)
|
||||
* @time: last access time (seconds)
|
||||
* @level: level of the entry in the TNC tree
|
||||
|
@ -769,6 +788,8 @@ struct ubifs_zbranch {
|
|||
struct ubifs_znode {
|
||||
struct ubifs_znode *parent;
|
||||
struct ubifs_znode *cnext;
|
||||
struct ubifs_znode *cparent;
|
||||
int ciip;
|
||||
unsigned long flags;
|
||||
time64_t time;
|
||||
int level;
|
||||
|
@ -983,6 +1004,7 @@ struct ubifs_debug_info;
|
|||
* struct ubifs_info - UBIFS file-system description data structure
|
||||
* (per-superblock).
|
||||
* @vfs_sb: VFS @struct super_block object
|
||||
* @sup_node: The super block node as read from the device
|
||||
*
|
||||
* @highest_inum: highest used inode number
|
||||
* @max_sqnum: current global sequence number
|
||||
|
@ -1028,6 +1050,7 @@ struct ubifs_debug_info;
|
|||
* @default_compr: default compression algorithm (%UBIFS_COMPR_LZO, etc)
|
||||
* @rw_incompat: the media is not R/W compatible
|
||||
* @assert_action: action to take when a ubifs_assert() fails
|
||||
* @authenticated: flag indigating the FS is mounted in authenticated mode
|
||||
*
|
||||
* @tnc_mutex: protects the Tree Node Cache (TNC), @zroot, @cnext, @enext, and
|
||||
* @calc_idx_sz
|
||||
|
@ -1075,6 +1098,7 @@ struct ubifs_debug_info;
|
|||
* @key_hash: direntry key hash function
|
||||
* @key_fmt: key format
|
||||
* @key_len: key length
|
||||
* @hash_len: The length of the index node hashes
|
||||
* @fanout: fanout of the index tree (number of links per indexing node)
|
||||
*
|
||||
* @min_io_size: minimal input/output unit size
|
||||
|
@ -1210,6 +1234,15 @@ struct ubifs_debug_info;
|
|||
* @rp_uid: reserved pool user ID
|
||||
* @rp_gid: reserved pool group ID
|
||||
*
|
||||
* @hash_tfm: the hash transformation used for hashing nodes
|
||||
* @hmac_tfm: the HMAC transformation for this filesystem
|
||||
* @hmac_desc_len: length of the HMAC used for authentication
|
||||
* @auth_key_name: the authentication key name
|
||||
* @auth_hash_name: the name of the hash algorithm used for authentication
|
||||
* @auth_hash_algo: the authentication hash used for this fs
|
||||
* @log_hash: the log hash from the commit start node up to the latest reference
|
||||
* node.
|
||||
*
|
||||
* @empty: %1 if the UBI device is empty
|
||||
* @need_recovery: %1 if the file-system needs recovery
|
||||
* @replaying: %1 during journal replay
|
||||
|
@ -1230,6 +1263,7 @@ struct ubifs_debug_info;
|
|||
*/
|
||||
struct ubifs_info {
|
||||
struct super_block *vfs_sb;
|
||||
struct ubifs_sb_node *sup_node;
|
||||
|
||||
ino_t highest_inum;
|
||||
unsigned long long max_sqnum;
|
||||
|
@ -1270,6 +1304,7 @@ struct ubifs_info {
|
|||
unsigned int default_compr:2;
|
||||
unsigned int rw_incompat:1;
|
||||
unsigned int assert_action:2;
|
||||
unsigned int authenticated:1;
|
||||
|
||||
struct mutex tnc_mutex;
|
||||
struct ubifs_zbranch zroot;
|
||||
|
@ -1314,6 +1349,7 @@ struct ubifs_info {
|
|||
uint32_t (*key_hash)(const char *str, int len);
|
||||
int key_fmt;
|
||||
int key_len;
|
||||
int hash_len;
|
||||
int fanout;
|
||||
|
||||
int min_io_size;
|
||||
|
@ -1441,6 +1477,15 @@ struct ubifs_info {
|
|||
kuid_t rp_uid;
|
||||
kgid_t rp_gid;
|
||||
|
||||
struct crypto_shash *hash_tfm;
|
||||
struct crypto_shash *hmac_tfm;
|
||||
int hmac_desc_len;
|
||||
char *auth_key_name;
|
||||
char *auth_hash_name;
|
||||
enum hash_algo auth_hash_algo;
|
||||
|
||||
struct shash_desc *log_hash;
|
||||
|
||||
/* The below fields are used only during mounting and re-mounting */
|
||||
unsigned int empty:1;
|
||||
unsigned int need_recovery:1;
|
||||
|
@ -1471,6 +1516,195 @@ extern const struct inode_operations ubifs_dir_inode_operations;
|
|||
extern const struct inode_operations ubifs_symlink_inode_operations;
|
||||
extern struct ubifs_compressor *ubifs_compressors[UBIFS_COMPR_TYPES_CNT];
|
||||
|
||||
/* auth.c */
|
||||
static inline int ubifs_authenticated(const struct ubifs_info *c)
|
||||
{
|
||||
return (IS_ENABLED(CONFIG_UBIFS_FS_AUTHENTICATION)) && c->authenticated;
|
||||
}
|
||||
|
||||
struct shash_desc *__ubifs_hash_get_desc(const struct ubifs_info *c);
|
||||
static inline struct shash_desc *ubifs_hash_get_desc(const struct ubifs_info *c)
|
||||
{
|
||||
return ubifs_authenticated(c) ? __ubifs_hash_get_desc(c) : NULL;
|
||||
}
|
||||
|
||||
static inline int ubifs_shash_init(const struct ubifs_info *c,
|
||||
struct shash_desc *desc)
|
||||
{
|
||||
if (ubifs_authenticated(c))
|
||||
return crypto_shash_init(desc);
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int ubifs_shash_update(const struct ubifs_info *c,
|
||||
struct shash_desc *desc, const void *buf,
|
||||
unsigned int len)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
if (ubifs_authenticated(c)) {
|
||||
err = crypto_shash_update(desc, buf, len);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int ubifs_shash_final(const struct ubifs_info *c,
|
||||
struct shash_desc *desc, u8 *out)
|
||||
{
|
||||
return ubifs_authenticated(c) ? crypto_shash_final(desc, out) : 0;
|
||||
}
|
||||
|
||||
int __ubifs_node_calc_hash(const struct ubifs_info *c, const void *buf,
|
||||
u8 *hash);
|
||||
static inline int ubifs_node_calc_hash(const struct ubifs_info *c,
|
||||
const void *buf, u8 *hash)
|
||||
{
|
||||
if (ubifs_authenticated(c))
|
||||
return __ubifs_node_calc_hash(c, buf, hash);
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ubifs_prepare_auth_node(struct ubifs_info *c, void *node,
|
||||
struct shash_desc *inhash);
|
||||
|
||||
/**
|
||||
* ubifs_check_hash - compare two hashes
|
||||
* @c: UBIFS file-system description object
|
||||
* @expected: first hash
|
||||
* @got: second hash
|
||||
*
|
||||
* Compare two hashes @expected and @got. Returns 0 when they are equal, a
|
||||
* negative error code otherwise.
|
||||
*/
|
||||
static inline int ubifs_check_hash(const struct ubifs_info *c,
|
||||
const u8 *expected, const u8 *got)
|
||||
{
|
||||
return crypto_memneq(expected, got, c->hash_len);
|
||||
}
|
||||
|
||||
/**
|
||||
* ubifs_check_hmac - compare two HMACs
|
||||
* @c: UBIFS file-system description object
|
||||
* @expected: first HMAC
|
||||
* @got: second HMAC
|
||||
*
|
||||
* Compare two hashes @expected and @got. Returns 0 when they are equal, a
|
||||
* negative error code otherwise.
|
||||
*/
|
||||
static inline int ubifs_check_hmac(const struct ubifs_info *c,
|
||||
const u8 *expected, const u8 *got)
|
||||
{
|
||||
return crypto_memneq(expected, got, c->hmac_desc_len);
|
||||
}
|
||||
|
||||
void ubifs_bad_hash(const struct ubifs_info *c, const void *node,
|
||||
const u8 *hash, int lnum, int offs);
|
||||
|
||||
int __ubifs_node_check_hash(const struct ubifs_info *c, const void *buf,
|
||||
const u8 *expected);
|
||||
static inline int ubifs_node_check_hash(const struct ubifs_info *c,
|
||||
const void *buf, const u8 *expected)
|
||||
{
|
||||
if (ubifs_authenticated(c))
|
||||
return __ubifs_node_check_hash(c, buf, expected);
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ubifs_init_authentication(struct ubifs_info *c);
|
||||
void __ubifs_exit_authentication(struct ubifs_info *c);
|
||||
static inline void ubifs_exit_authentication(struct ubifs_info *c)
|
||||
{
|
||||
if (ubifs_authenticated(c))
|
||||
__ubifs_exit_authentication(c);
|
||||
}
|
||||
|
||||
/**
|
||||
* ubifs_branch_hash - returns a pointer to the hash of a branch
|
||||
* @c: UBIFS file-system description object
|
||||
* @br: branch to get the hash from
|
||||
*
|
||||
* This returns a pointer to the hash of a branch. Since the key already is a
|
||||
* dynamically sized object we cannot use a struct member here.
|
||||
*/
|
||||
static inline u8 *ubifs_branch_hash(struct ubifs_info *c,
|
||||
struct ubifs_branch *br)
|
||||
{
|
||||
return (void *)br + sizeof(*br) + c->key_len;
|
||||
}
|
||||
|
||||
/**
|
||||
* ubifs_copy_hash - copy a hash
|
||||
* @c: UBIFS file-system description object
|
||||
* @from: source hash
|
||||
* @to: destination hash
|
||||
*
|
||||
* With authentication this copies a hash, otherwise does nothing.
|
||||
*/
|
||||
static inline void ubifs_copy_hash(const struct ubifs_info *c, const u8 *from,
|
||||
u8 *to)
|
||||
{
|
||||
if (ubifs_authenticated(c))
|
||||
memcpy(to, from, c->hash_len);
|
||||
}
|
||||
|
||||
int __ubifs_node_insert_hmac(const struct ubifs_info *c, void *buf,
|
||||
int len, int ofs_hmac);
|
||||
static inline int ubifs_node_insert_hmac(const struct ubifs_info *c, void *buf,
|
||||
int len, int ofs_hmac)
|
||||
{
|
||||
if (ubifs_authenticated(c))
|
||||
return __ubifs_node_insert_hmac(c, buf, len, ofs_hmac);
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __ubifs_node_verify_hmac(const struct ubifs_info *c, const void *buf,
|
||||
int len, int ofs_hmac);
|
||||
static inline int ubifs_node_verify_hmac(const struct ubifs_info *c,
|
||||
const void *buf, int len, int ofs_hmac)
|
||||
{
|
||||
if (ubifs_authenticated(c))
|
||||
return __ubifs_node_verify_hmac(c, buf, len, ofs_hmac);
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ubifs_auth_node_sz - returns the size of an authentication node
|
||||
* @c: UBIFS file-system description object
|
||||
*
|
||||
* This function returns the size of an authentication node which can
|
||||
* be 0 for unauthenticated filesystems or the real size of an auth node
|
||||
* authentication is enabled.
|
||||
*/
|
||||
static inline int ubifs_auth_node_sz(const struct ubifs_info *c)
|
||||
{
|
||||
if (ubifs_authenticated(c))
|
||||
return sizeof(struct ubifs_auth_node) + c->hmac_desc_len;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ubifs_hmac_wkm(struct ubifs_info *c, u8 *hmac);
|
||||
|
||||
int __ubifs_shash_copy_state(const struct ubifs_info *c, struct shash_desc *src,
|
||||
struct shash_desc *target);
|
||||
static inline int ubifs_shash_copy_state(const struct ubifs_info *c,
|
||||
struct shash_desc *src,
|
||||
struct shash_desc *target)
|
||||
{
|
||||
if (ubifs_authenticated(c))
|
||||
return __ubifs_shash_copy_state(c, src, target);
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* io.c */
|
||||
void ubifs_ro_mode(struct ubifs_info *c, int err);
|
||||
int ubifs_leb_read(const struct ubifs_info *c, int lnum, void *buf, int offs,
|
||||
|
@ -1490,9 +1724,15 @@ int ubifs_read_node_wbuf(struct ubifs_wbuf *wbuf, void *buf, int type, int len,
|
|||
int lnum, int offs);
|
||||
int ubifs_write_node(struct ubifs_info *c, void *node, int len, int lnum,
|
||||
int offs);
|
||||
int ubifs_write_node_hmac(struct ubifs_info *c, void *buf, int len, int lnum,
|
||||
int offs, int hmac_offs);
|
||||
int ubifs_check_node(const struct ubifs_info *c, const void *buf, int lnum,
|
||||
int offs, int quiet, int must_chk_crc);
|
||||
void ubifs_init_node(struct ubifs_info *c, void *buf, int len, int pad);
|
||||
void ubifs_crc_node(struct ubifs_info *c, void *buf, int len);
|
||||
void ubifs_prepare_node(struct ubifs_info *c, void *buf, int len, int pad);
|
||||
int ubifs_prepare_node_hmac(struct ubifs_info *c, void *node, int len,
|
||||
int hmac_offs, int pad);
|
||||
void ubifs_prep_grp_node(struct ubifs_info *c, void *node, int len, int last);
|
||||
int ubifs_io_init(struct ubifs_info *c);
|
||||
void ubifs_pad(const struct ubifs_info *c, void *buf, int pad);
|
||||
|
@ -1592,11 +1832,12 @@ int ubifs_tnc_lookup_dh(struct ubifs_info *c, const union ubifs_key *key,
|
|||
int ubifs_tnc_locate(struct ubifs_info *c, const union ubifs_key *key,
|
||||
void *node, int *lnum, int *offs);
|
||||
int ubifs_tnc_add(struct ubifs_info *c, const union ubifs_key *key, int lnum,
|
||||
int offs, int len);
|
||||
int offs, int len, const u8 *hash);
|
||||
int ubifs_tnc_replace(struct ubifs_info *c, const union ubifs_key *key,
|
||||
int old_lnum, int old_offs, int lnum, int offs, int len);
|
||||
int ubifs_tnc_add_nm(struct ubifs_info *c, const union ubifs_key *key,
|
||||
int lnum, int offs, int len, const struct fscrypt_name *nm);
|
||||
int lnum, int offs, int len, const u8 *hash,
|
||||
const struct fscrypt_name *nm);
|
||||
int ubifs_tnc_remove(struct ubifs_info *c, const union ubifs_key *key);
|
||||
int ubifs_tnc_remove_nm(struct ubifs_info *c, const union ubifs_key *key,
|
||||
const struct fscrypt_name *nm);
|
||||
|
@ -1659,12 +1900,12 @@ int ubifs_gc_should_commit(struct ubifs_info *c);
|
|||
void ubifs_wait_for_commit(struct ubifs_info *c);
|
||||
|
||||
/* master.c */
|
||||
int ubifs_compare_master_node(struct ubifs_info *c, void *m1, void *m2);
|
||||
int ubifs_read_master(struct ubifs_info *c);
|
||||
int ubifs_write_master(struct ubifs_info *c);
|
||||
|
||||
/* sb.c */
|
||||
int ubifs_read_superblock(struct ubifs_info *c);
|
||||
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 ubifs_fixup_free_space(struct ubifs_info *c);
|
||||
int ubifs_enable_encryption(struct ubifs_info *c);
|
||||
|
@ -1693,7 +1934,7 @@ int ubifs_clear_orphans(struct ubifs_info *c);
|
|||
/* lpt.c */
|
||||
int ubifs_calc_lpt_geom(struct ubifs_info *c);
|
||||
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 ubifs_lpt_init(struct ubifs_info *c, int rd, int wr);
|
||||
struct ubifs_lprops *ubifs_lpt_lookup(struct ubifs_info *c, int lnum);
|
||||
struct ubifs_lprops *ubifs_lpt_lookup_dirty(struct ubifs_info *c, int lnum);
|
||||
|
@ -1712,6 +1953,7 @@ struct ubifs_pnode *ubifs_get_pnode(struct ubifs_info *c,
|
|||
struct ubifs_nnode *parent, int iip);
|
||||
struct ubifs_nnode *ubifs_get_nnode(struct ubifs_info *c,
|
||||
struct ubifs_nnode *parent, int iip);
|
||||
struct ubifs_pnode *ubifs_pnode_lookup(struct ubifs_info *c, int i);
|
||||
int ubifs_read_nnode(struct ubifs_info *c, struct ubifs_nnode *parent, int iip);
|
||||
void ubifs_add_lpt_dirt(struct ubifs_info *c, int lnum, int dirty);
|
||||
void ubifs_add_nnode_dirt(struct ubifs_info *c, struct ubifs_nnode *nnode);
|
||||
|
@ -1720,6 +1962,7 @@ struct ubifs_nnode *ubifs_first_nnode(struct ubifs_info *c, int *hght);
|
|||
/* Needed only in debugging code in lpt_commit.c */
|
||||
int ubifs_unpack_nnode(const struct ubifs_info *c, void *buf,
|
||||
struct ubifs_nnode *nnode);
|
||||
int ubifs_lpt_calc_hash(struct ubifs_info *c, u8 *hash);
|
||||
|
||||
/* lpt_commit.c */
|
||||
int ubifs_lpt_start_commit(struct ubifs_info *c);
|
||||
|
@ -1807,7 +2050,7 @@ int ubifs_clean_lebs(struct ubifs_info *c, void *sbuf);
|
|||
int ubifs_rcvry_gc_commit(struct ubifs_info *c);
|
||||
int ubifs_recover_size_accum(struct ubifs_info *c, union ubifs_key *key,
|
||||
int deletion, loff_t new_size);
|
||||
int ubifs_recover_size(struct ubifs_info *c);
|
||||
int ubifs_recover_size(struct ubifs_info *c, bool in_place);
|
||||
void ubifs_destroy_size_tree(struct ubifs_info *c);
|
||||
|
||||
/* ioctl.c */
|
||||
|
|
Loading…
Reference in a new issue