Commit 42bd06e9 authored by Linus Torvalds's avatar Linus Torvalds
Browse files

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: Ad...
parents 4710e789 84db119f
This diff is collapsed.
......@@ -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
// 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))
......
......@@ -365,20 +365,8 @@ static unsigned long long next_sqnum(struct ubifs_info *c)
return sqnum;
}
/**
* ubifs_prepare_node - prepare node to be written to flash.
* @c: UBIFS file-system description object
* @node: the node to pad
* @len: node length
* @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.
*/
void ubifs_prepare_node(struct ubifs_info *c, void *node, int len, int pad)
void ubifs_init_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);
......@@ -389,8 +377,6 @@ void ubifs_prepare_node(struct ubifs_info *c, void *node, int len, int pad)
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);
......@@ -399,6 +385,68 @@ void ubifs_prepare_node(struct ubifs_info *c, void *node, int len, int 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
* @node: the node to pad
* @len: node length
* @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.
*/
void ubifs_prepare_node(struct ubifs_info *c, void *node, int len, int 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);
}
/**
* ubifs_prep_grp_node - prepare node of a group to be written to flash.
* @c: UBIFS file-system description object
......@@ -849,12 +897,13 @@ int ubifs_wbuf_write_nolock(struct ubifs_wbuf *wbuf, void *buf, int len)
}
/**
* ubifs_write_node - write node to the media.
* 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
......@@ -862,8 +911,8 @@ int ubifs_wbuf_write_nolock(struct ubifs_wbuf *wbuf, void *buf, int len)
* 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(struct ubifs_info *c, void *buf, 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 err, buf_len = ALIGN(len, c->min_io_size);
......@@ -878,7 +927,10 @@ int ubifs_write_node(struct ubifs_info *c, void *buf, int len, int lnum,
if (c->ro_error)
return -EROFS;
ubifs_prepare_node(c, buf, len, 1);
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);
......@@ -886,6 +938,26 @@ int ubifs_write_node(struct ubifs_info *c, void *buf, int len, int lnum,
return err;
}
/**
* ubifs_write_node - 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
*
* 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(struct ubifs_info *c, void *buf, int len, int lnum,
int offs)
{
return ubifs_write_node_hmac(c, buf, len, lnum, offs, -1);
}
/**
* ubifs_read_node_wbuf - read node from the media or write-buffer.
* @wbuf: wbuf to check for un-written data
......
This diff is collapsed.
......@@ -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);
......
......@@ -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 @@ static int fix_size_in_place(struct ubifs_info *c, struct size_entry *e)
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, !e->inode);
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 */
ubifs_assert(c, !(c->ro_mount && in_place));
/*
* We found data that is outside the found inode size,
* fixup the inode size
*/
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),
......
......@@ -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_hmac(c, sup, UBIFS_SB_NODE_SZ, 0, 0,
offsetof(struct ubifs_sb_node, hmac));
if (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);
kfree(cs);
if (err)
return 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 @@ static int validate_sb(struct ubifs_info *c, struct ubifs_sb_node *sup)
* 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 @@ static int fixup_free_space(struct ubifs_info *c)
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;
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment