diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2019-07-14 17:24:12 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2019-07-14 17:24:12 -0700 |
commit | a318423b61e8c67aa5c0a428540c58439a20baac (patch) | |
tree | 5834b93378b01bde412c49aacdb7aa1021c701ca /fs/ubifs | |
parent | f2772a0e4833d1af1901b6f1a38136fb71d1350c (diff) | |
parent | 8009ce956c3d28022af6b122e50213ad830fc902 (diff) |
Merge tag 'upstream-5.3-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rw/ubifs
Pull UBIFS updates from Richard Weinberger:
- Support for zstd compression
- Support for offline signed filesystems
- Various fixes for regressions
* tag 'upstream-5.3-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rw/ubifs:
ubifs: Don't leak orphans on memory during commit
ubifs: Check link count of inodes when killing orphans.
ubifs: Add support for zstd compression.
ubifs: support offline signed images
ubifs: remove unnecessary check in ubifs_log_start_commit
ubifs: Fix typo of output in get_cs_sqnum
ubifs: Simplify redundant code
ubifs: Correctly use tnc_next() in search_dh_cookie()
Diffstat (limited to 'fs/ubifs')
-rw-r--r-- | fs/ubifs/Kconfig | 13 | ||||
-rw-r--r-- | fs/ubifs/auth.c | 86 | ||||
-rw-r--r-- | fs/ubifs/compress.c | 27 | ||||
-rw-r--r-- | fs/ubifs/log.c | 5 | ||||
-rw-r--r-- | fs/ubifs/master.c | 53 | ||||
-rw-r--r-- | fs/ubifs/orphan.c | 94 | ||||
-rw-r--r-- | fs/ubifs/recovery.c | 2 | ||||
-rw-r--r-- | fs/ubifs/sb.c | 52 | ||||
-rw-r--r-- | fs/ubifs/super.c | 46 | ||||
-rw-r--r-- | fs/ubifs/tnc.c | 16 | ||||
-rw-r--r-- | fs/ubifs/ubifs-media.h | 30 | ||||
-rw-r--r-- | fs/ubifs/ubifs.h | 6 |
12 files changed, 338 insertions, 92 deletions
diff --git a/fs/ubifs/Kconfig b/fs/ubifs/Kconfig index 06c35c64162b..69932bcfa920 100644 --- a/fs/ubifs/Kconfig +++ b/fs/ubifs/Kconfig @@ -6,8 +6,10 @@ config UBIFS_FS select CRYPTO if UBIFS_FS_ADVANCED_COMPR select CRYPTO if UBIFS_FS_LZO select CRYPTO if UBIFS_FS_ZLIB + select CRYPTO if UBIFS_FS_ZSTD select CRYPTO_LZO if UBIFS_FS_LZO select CRYPTO_DEFLATE if UBIFS_FS_ZLIB + select CRYPTO_ZSTD if UBIFS_FS_ZSTD select CRYPTO_HASH_INFO select UBIFS_FS_XATTR if FS_ENCRYPTION depends on MTD_UBI @@ -38,6 +40,14 @@ config UBIFS_FS_ZLIB help Zlib compresses better than LZO but it is slower. Say 'Y' if unsure. +config UBIFS_FS_ZSTD + bool "ZSTD compression support" if UBIFS_FS_ADVANCED_COMPR + depends on UBIFS_FS + default y + help + ZSTD compresses is a big win in speed over Zlib and + in compression ratio over LZO. Say 'Y' if unsure. + config UBIFS_ATIME_SUPPORT bool "Access time support" default n @@ -77,8 +87,9 @@ config UBIFS_FS_SECURITY config UBIFS_FS_AUTHENTICATION bool "UBIFS authentication support" - depends on KEYS + select KEYS select CRYPTO_HMAC + select SYSTEM_DATA_VERIFICATION help Enable authentication support for UBIFS. This feature offers protection against offline changes for both data and metadata of the filesystem. diff --git a/fs/ubifs/auth.c b/fs/ubifs/auth.c index 60f43b93d06e..d9af2de9084a 100644 --- a/fs/ubifs/auth.c +++ b/fs/ubifs/auth.c @@ -10,10 +10,12 @@ */ #include <linux/crypto.h> +#include <linux/verification.h> #include <crypto/hash.h> #include <crypto/sha.h> #include <crypto/algapi.h> #include <keys/user-type.h> +#include <keys/asymmetric-type.h> #include "ubifs.h" @@ -199,6 +201,77 @@ int __ubifs_node_check_hash(const struct ubifs_info *c, const void *node, } /** + * ubifs_sb_verify_signature - verify the signature of a superblock + * @c: UBIFS file-system description object + * @sup: The superblock node + * + * To support offline signed images the superblock can be signed with a + * PKCS#7 signature. The signature is placed directly behind the superblock + * node in an ubifs_sig_node. + * + * Returns 0 when the signature can be successfully verified or a negative + * error code if not. + */ +int ubifs_sb_verify_signature(struct ubifs_info *c, + const struct ubifs_sb_node *sup) +{ + int err; + struct ubifs_scan_leb *sleb; + struct ubifs_scan_node *snod; + const struct ubifs_sig_node *signode; + + sleb = ubifs_scan(c, UBIFS_SB_LNUM, UBIFS_SB_NODE_SZ, c->sbuf, 0); + if (IS_ERR(sleb)) { + err = PTR_ERR(sleb); + return err; + } + + if (sleb->nodes_cnt == 0) { + ubifs_err(c, "Unable to find signature node"); + err = -EINVAL; + goto out_destroy; + } + + snod = list_first_entry(&sleb->nodes, struct ubifs_scan_node, list); + + if (snod->type != UBIFS_SIG_NODE) { + ubifs_err(c, "Signature node is of wrong type"); + err = -EINVAL; + goto out_destroy; + } + + signode = snod->node; + + if (le32_to_cpu(signode->len) > snod->len + sizeof(struct ubifs_sig_node)) { + ubifs_err(c, "invalid signature len %d", le32_to_cpu(signode->len)); + err = -EINVAL; + goto out_destroy; + } + + if (le32_to_cpu(signode->type) != UBIFS_SIGNATURE_TYPE_PKCS7) { + ubifs_err(c, "Signature type %d is not supported\n", + le32_to_cpu(signode->type)); + err = -EINVAL; + goto out_destroy; + } + + err = verify_pkcs7_signature(sup, sizeof(struct ubifs_sb_node), + signode->sig, le32_to_cpu(signode->len), + NULL, VERIFYING_UNSPECIFIED_SIGNATURE, + NULL, NULL); + + if (err) + ubifs_err(c, "Failed to verify signature"); + else + ubifs_msg(c, "Successfully verified super block signature"); + +out_destroy: + ubifs_scan_destroy(sleb); + + return err; +} + +/** * ubifs_init_authentication - initialize UBIFS authentication support * @c: UBIFS file-system description object * @@ -478,3 +551,16 @@ int ubifs_hmac_wkm(struct ubifs_info *c, u8 *hmac) return err; return 0; } + +/* + * ubifs_hmac_zero - test if a HMAC is zero + * @c: UBIFS file-system description object + * @hmac: the HMAC to test + * + * This function tests if a HMAC is zero and returns true if it is + * and false otherwise. + */ +bool ubifs_hmac_zero(struct ubifs_info *c, const u8 *hmac) +{ + return !memchr_inv(hmac, 0, c->hmac_desc_len); +} diff --git a/fs/ubifs/compress.c b/fs/ubifs/compress.c index 99c53ad11e93..3a92e6af69b2 100644 --- a/fs/ubifs/compress.c +++ b/fs/ubifs/compress.c @@ -59,6 +59,24 @@ static struct ubifs_compressor zlib_compr = { }; #endif +#ifdef CONFIG_UBIFS_FS_ZSTD +static DEFINE_MUTEX(zstd_enc_mutex); +static DEFINE_MUTEX(zstd_dec_mutex); + +static struct ubifs_compressor zstd_compr = { + .compr_type = UBIFS_COMPR_ZSTD, + .comp_mutex = &zstd_enc_mutex, + .decomp_mutex = &zstd_dec_mutex, + .name = "zstd", + .capi_name = "zstd", +}; +#else +static struct ubifs_compressor zstd_compr = { + .compr_type = UBIFS_COMPR_ZSTD, + .name = "zstd", +}; +#endif + /* All UBIFS compressors */ struct ubifs_compressor *ubifs_compressors[UBIFS_COMPR_TYPES_CNT]; @@ -216,13 +234,19 @@ int __init ubifs_compressors_init(void) if (err) return err; - err = compr_init(&zlib_compr); + err = compr_init(&zstd_compr); if (err) goto out_lzo; + err = compr_init(&zlib_compr); + if (err) + goto out_zstd; + ubifs_compressors[UBIFS_COMPR_NONE] = &none_compr; return 0; +out_zstd: + compr_exit(&zstd_compr); out_lzo: compr_exit(&lzo_compr); return err; @@ -235,4 +259,5 @@ void ubifs_compressors_exit(void) { compr_exit(&lzo_compr); compr_exit(&zlib_compr); + compr_exit(&zstd_compr); } diff --git a/fs/ubifs/log.c b/fs/ubifs/log.c index cd85d7d4c515..b6ac9c4281ef 100644 --- a/fs/ubifs/log.c +++ b/fs/ubifs/log.c @@ -438,10 +438,7 @@ int ubifs_log_start_commit(struct ubifs_info *c, int *ltail_lnum) *ltail_lnum = c->lhead_lnum; c->lhead_offs += len; - if (c->lhead_offs == c->leb_size) { - c->lhead_lnum = ubifs_next_log_lnum(c, c->lhead_lnum); - c->lhead_offs = 0; - } + ubifs_assert(c, c->lhead_offs < c->leb_size); remove_buds(c); diff --git a/fs/ubifs/master.c b/fs/ubifs/master.c index b42a768709c0..52a85c01397e 100644 --- a/fs/ubifs/master.c +++ b/fs/ubifs/master.c @@ -48,6 +48,39 @@ int ubifs_compare_master_node(struct ubifs_info *c, void *m1, void *m2) return 0; } +/* mst_node_check_hash - Check hash of a master node + * @c: UBIFS file-system description object + * @mst: The master node + * @expected: The expected hash of the master node + * + * This checks the hash of a master node against a given expected hash. + * Note that we have two master nodes on a UBIFS image which have different + * sequence numbers and consequently different CRCs. To be able to match + * both master nodes we exclude the common node header containing the sequence + * number and CRC from the hash. + * + * Returns 0 if the hashes are equal, a negative error code otherwise. + */ +static int mst_node_check_hash(const struct ubifs_info *c, + const struct ubifs_mst_node *mst, + const u8 *expected) +{ + u8 calc[UBIFS_MAX_HASH_LEN]; + const void *node = mst; + + SHASH_DESC_ON_STACK(shash, c->hash_tfm); + + shash->tfm = c->hash_tfm; + + crypto_shash_digest(shash, node + sizeof(struct ubifs_ch), + UBIFS_MST_NODE_SZ - sizeof(struct ubifs_ch), calc); + + if (ubifs_check_hash(c, expected, calc)) + return -EPERM; + + return 0; +} + /** * scan_for_master - search the valid master node. * @c: UBIFS file-system description object @@ -102,14 +135,22 @@ static int scan_for_master(struct ubifs_info *c) 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; + if (ubifs_hmac_zero(c, c->mst_node->hmac)) { + err = mst_node_check_hash(c, c->mst_node, + c->sup_node->hash_mst); + if (err) + ubifs_err(c, "Failed to verify master node hash"); + } else { + 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"); } + if (err) + return -EPERM; + return 0; out: diff --git a/fs/ubifs/orphan.c b/fs/ubifs/orphan.c index cb72688032cd..b52624e28fa1 100644 --- a/fs/ubifs/orphan.c +++ b/fs/ubifs/orphan.c @@ -126,25 +126,11 @@ static void __orphan_drop(struct ubifs_info *c, struct ubifs_orphan *o) kfree(o); } -static void orphan_delete(struct ubifs_info *c, ino_t inum) +static void orphan_delete(struct ubifs_info *c, struct ubifs_orphan *orph) { - struct ubifs_orphan *orph, *child_orph, *tmp_o; - - spin_lock(&c->orphan_lock); - - orph = lookup_orphan(c, inum); - if (!orph) { - spin_unlock(&c->orphan_lock); - ubifs_err(c, "missing orphan ino %lu", (unsigned long)inum); - dump_stack(); - - return; - } - if (orph->del) { spin_unlock(&c->orphan_lock); - dbg_gen("deleted twice ino %lu", - (unsigned long)inum); + dbg_gen("deleted twice ino %lu", orph->inum); return; } @@ -153,19 +139,11 @@ static void orphan_delete(struct ubifs_info *c, ino_t inum) orph->dnext = c->orph_dnext; c->orph_dnext = orph; spin_unlock(&c->orphan_lock); - dbg_gen("delete later ino %lu", - (unsigned long)inum); + dbg_gen("delete later ino %lu", orph->inum); return; } - list_for_each_entry_safe(child_orph, tmp_o, &orph->child_list, child_list) { - list_del(&child_orph->child_list); - __orphan_drop(c, child_orph); - } - __orphan_drop(c, orph); - - spin_unlock(&c->orphan_lock); } /** @@ -223,7 +201,27 @@ int ubifs_add_orphan(struct ubifs_info *c, ino_t inum) */ void ubifs_delete_orphan(struct ubifs_info *c, ino_t inum) { - orphan_delete(c, inum); + struct ubifs_orphan *orph, *child_orph, *tmp_o; + + spin_lock(&c->orphan_lock); + + orph = lookup_orphan(c, inum); + if (!orph) { + spin_unlock(&c->orphan_lock); + ubifs_err(c, "missing orphan ino %lu", (unsigned long)inum); + dump_stack(); + + return; + } + + list_for_each_entry_safe(child_orph, tmp_o, &orph->child_list, child_list) { + list_del(&child_orph->child_list); + orphan_delete(c, child_orph); + } + + orphan_delete(c, orph); + + spin_unlock(&c->orphan_lock); } /** @@ -630,6 +628,7 @@ static int do_kill_orphans(struct ubifs_info *c, struct ubifs_scan_leb *sleb, { struct ubifs_scan_node *snod; struct ubifs_orph_node *orph; + struct ubifs_ino_node *ino = NULL; unsigned long long cmt_no; ino_t inum; int i, n, err, first = 1; @@ -676,23 +675,40 @@ static int do_kill_orphans(struct ubifs_info *c, struct ubifs_scan_leb *sleb, if (first) first = 0; + ino = kmalloc(UBIFS_MAX_INO_NODE_SZ, GFP_NOFS); + if (!ino) + return -ENOMEM; + n = (le32_to_cpu(orph->ch.len) - UBIFS_ORPH_NODE_SZ) >> 3; for (i = 0; i < n; i++) { union ubifs_key key1, key2; inum = le64_to_cpu(orph->inos[i]); - dbg_rcvry("deleting orphaned inode %lu", - (unsigned long)inum); - - lowest_ino_key(c, &key1, inum); - highest_ino_key(c, &key2, inum); - err = ubifs_tnc_remove_range(c, &key1, &key2); + ino_key_init(c, &key1, inum); + err = ubifs_tnc_lookup(c, &key1, ino); if (err) - return err; + goto out_free; + + /* + * Check whether an inode can really get deleted. + * linkat() with O_TMPFILE allows rebirth of an inode. + */ + if (ino->nlink == 0) { + dbg_rcvry("deleting orphaned inode %lu", + (unsigned long)inum); + + lowest_ino_key(c, &key1, inum); + highest_ino_key(c, &key2, inum); + + err = ubifs_tnc_remove_range(c, &key1, &key2); + if (err) + goto out_ro; + } + err = insert_dead_orphan(c, inum); if (err) - return err; + goto out_free; } *last_cmt_no = cmt_no; @@ -704,7 +720,15 @@ static int do_kill_orphans(struct ubifs_info *c, struct ubifs_scan_leb *sleb, *last_flagged = 0; } - return 0; + err = 0; +out_free: + kfree(ino); + return err; + +out_ro: + ubifs_ro_mode(c, err); + kfree(ino); + return err; } /** diff --git a/fs/ubifs/recovery.c b/fs/ubifs/recovery.c index 3fc589881825..f116f7b3f9e5 100644 --- a/fs/ubifs/recovery.c +++ b/fs/ubifs/recovery.c @@ -818,7 +818,7 @@ static int get_cs_sqnum(struct ubifs_info *c, int lnum, int offs, goto out_err; } if (cs_node->ch.node_type != UBIFS_CS_NODE) { - ubifs_err(c, "Node a CS node, type is %d", cs_node->ch.node_type); + ubifs_err(c, "Not a CS node, type is %d", cs_node->ch.node_type); goto out_err; } if (le64_to_cpu(cs_node->cmt_no) != c->cmt_no) { diff --git a/fs/ubifs/sb.c b/fs/ubifs/sb.c index 12c2afdb5804..a551eb3e9b89 100644 --- a/fs/ubifs/sb.c +++ b/fs/ubifs/sb.c @@ -578,17 +578,26 @@ static int authenticate_sb_node(struct ubifs_info *c, 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; + /* + * The super block node can either be authenticated by a HMAC or + * by a signature in a ubifs_sig_node directly following the + * super block node to support offline image creation. + */ + if (ubifs_hmac_zero(c, sup->hmac)) { + err = ubifs_sb_verify_signature(c, sup); + } else { + 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)); } - 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); @@ -744,21 +753,16 @@ int ubifs_read_superblock(struct ubifs_info *c) } /* Automatically increase file system size to the maximum size */ - c->old_leb_cnt = c->leb_cnt; if (c->leb_cnt < c->vi.size && c->leb_cnt < c->max_leb_cnt) { + int old_leb_cnt = c->leb_cnt; + c->leb_cnt = min_t(int, c->max_leb_cnt, c->vi.size); - if (c->ro_mount) - dbg_mnt("Auto resizing (ro) from %d LEBs to %d LEBs", - c->old_leb_cnt, c->leb_cnt); - else { - dbg_mnt("Auto resizing (sb) from %d LEBs to %d LEBs", - c->old_leb_cnt, c->leb_cnt); - sup->leb_cnt = cpu_to_le32(c->leb_cnt); - err = ubifs_write_sb_node(c, sup); - if (err) - goto out; - c->old_leb_cnt = c->leb_cnt; - } + sup->leb_cnt = cpu_to_le32(c->leb_cnt); + + c->superblock_need_write = 1; + + dbg_mnt("Auto resizing from %d LEBs to %d LEBs", + old_leb_cnt, c->leb_cnt); } c->log_bytes = (long long)c->log_lebs * c->leb_size; @@ -916,9 +920,7 @@ int ubifs_fixup_free_space(struct ubifs_info *c) c->space_fixup = 0; sup->flags &= cpu_to_le32(~UBIFS_FLG_SPACE_FIXUP); - err = ubifs_write_sb_node(c, sup); - if (err) - return err; + c->superblock_need_write = 1; ubifs_msg(c, "free space fixup complete"); return err; diff --git a/fs/ubifs/super.c b/fs/ubifs/super.c index fd1977b568f0..2c0803b0ac3a 100644 --- a/fs/ubifs/super.c +++ b/fs/ubifs/super.c @@ -566,6 +566,8 @@ static int init_constants_early(struct ubifs_info *c) 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_SIG_NODE].min_len = UBIFS_SIG_NODE_SZ; + c->ranges[UBIFS_SIG_NODE].max_len = c->leb_size - UBIFS_SB_NODE_SZ; c->ranges[UBIFS_INO_NODE].min_len = UBIFS_INO_NODE_SZ; c->ranges[UBIFS_INO_NODE].max_len = UBIFS_MAX_INO_NODE_SZ; @@ -1043,6 +1045,8 @@ static int ubifs_parse_options(struct ubifs_info *c, char *options, c->mount_opts.compr_type = UBIFS_COMPR_LZO; else if (!strcmp(name, "zlib")) c->mount_opts.compr_type = UBIFS_COMPR_ZLIB; + else if (!strcmp(name, "zstd")) + c->mount_opts.compr_type = UBIFS_COMPR_ZSTD; else { ubifs_err(c, "unknown compressor \"%s\"", name); //FIXME: is c ready? kfree(name); @@ -1296,8 +1300,7 @@ static int mount_ubifs(struct ubifs_info *c) if (err) goto out_free; - sz = ALIGN(c->max_idx_node_sz, c->min_io_size); - sz = ALIGN(sz + c->max_idx_node_sz, c->min_io_size); + sz = ALIGN(c->max_idx_node_sz, c->min_io_size) * 2; c->cbuf = kmalloc(sz, GFP_NOFS); if (!c->cbuf) { err = -ENOMEM; @@ -1360,6 +1363,26 @@ static int mount_ubifs(struct ubifs_info *c) goto out_lpt; } + /* + * Handle offline signed images: Now that the master node is + * written and its validation no longer depends on the hash + * in the superblock, we can update the offline signed + * superblock with a HMAC version, + */ + if (ubifs_authenticated(c) && ubifs_hmac_zero(c, c->sup_node->hmac)) { + err = ubifs_hmac_wkm(c, c->sup_node->hmac_wkm); + if (err) + goto out_lpt; + c->superblock_need_write = 1; + } + + if (!c->ro_mount && c->superblock_need_write) { + err = ubifs_write_sb_node(c, c->sup_node); + if (err) + goto out_lpt; + c->superblock_need_write = 0; + } + err = dbg_check_idx_size(c, c->bi.old_idx_sz); if (err) goto out_lpt; @@ -1642,15 +1665,6 @@ static int ubifs_remount_rw(struct ubifs_info *c) if (err) goto out; - if (c->old_leb_cnt != c->leb_cnt) { - struct ubifs_sb_node *sup = c->sup_node; - - sup->leb_cnt = cpu_to_le32(c->leb_cnt); - err = ubifs_write_sb_node(c, sup); - if (err) - goto out; - } - if (c->need_recovery) { ubifs_msg(c, "completing deferred recovery"); err = ubifs_write_rcvrd_mst_node(c); @@ -1682,6 +1696,16 @@ static int ubifs_remount_rw(struct ubifs_info *c) goto out; } + if (c->superblock_need_write) { + struct ubifs_sb_node *sup = c->sup_node; + + err = ubifs_write_sb_node(c, sup); + if (err) + goto out; + + c->superblock_need_write = 0; + } + c->ileb_buf = vmalloc(c->leb_size); if (!c->ileb_buf) { err = -ENOMEM; diff --git a/fs/ubifs/tnc.c b/fs/ubifs/tnc.c index f5a823cb0e43..e8e7b0e9532e 100644 --- a/fs/ubifs/tnc.c +++ b/fs/ubifs/tnc.c @@ -1158,8 +1158,8 @@ static struct ubifs_znode *dirty_cow_bottom_up(struct ubifs_info *c, * o exact match, i.e. the found zero-level znode contains key @key, then %1 * is returned and slot number of the matched branch is stored in @n; * o not exact match, which means that zero-level znode does not contain - * @key, then %0 is returned and slot number of the closest branch is stored - * in @n; + * @key, then %0 is returned and slot number of the closest branch or %-1 + * is stored in @n; In this case calling tnc_next() is mandatory. * o @key is so small that it is even less than the lowest key of the * leftmost zero-level node, then %0 is returned and %0 is stored in @n. * @@ -1882,13 +1882,19 @@ int ubifs_tnc_lookup_nm(struct ubifs_info *c, const union ubifs_key *key, static int search_dh_cookie(struct ubifs_info *c, const union ubifs_key *key, struct ubifs_dent_node *dent, uint32_t cookie, - struct ubifs_znode **zn, int *n) + struct ubifs_znode **zn, int *n, int exact) { int err; struct ubifs_znode *znode = *zn; struct ubifs_zbranch *zbr; union ubifs_key *dkey; + if (!exact) { + err = tnc_next(c, &znode, n); + if (err) + return err; + } + for (;;) { zbr = &znode->zbranch[*n]; dkey = &zbr->key; @@ -1930,7 +1936,7 @@ static int do_lookup_dh(struct ubifs_info *c, const union ubifs_key *key, if (unlikely(err < 0)) goto out_unlock; - err = search_dh_cookie(c, key, dent, cookie, &znode, &n); + err = search_dh_cookie(c, key, dent, cookie, &znode, &n, err); out_unlock: mutex_unlock(&c->tnc_mutex); @@ -2723,7 +2729,7 @@ int ubifs_tnc_remove_dh(struct ubifs_info *c, const union ubifs_key *key, if (unlikely(err < 0)) goto out_free; - err = search_dh_cookie(c, key, dent, cookie, &znode, &n); + err = search_dh_cookie(c, key, dent, cookie, &znode, &n, err); if (err) goto out_free; } diff --git a/fs/ubifs/ubifs-media.h b/fs/ubifs/ubifs-media.h index 86f0f2be116c..3c9792cbb6ff 100644 --- a/fs/ubifs/ubifs-media.h +++ b/fs/ubifs/ubifs-media.h @@ -275,6 +275,8 @@ enum { #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) +#define UBIFS_SIG_NODE_SZ sizeof(struct ubifs_sig_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 */ @@ -301,6 +303,8 @@ enum { */ #define UBIFS_XATTR_NAME_ENCRYPTION_CONTEXT "c" +/* Type field in ubifs_sig_node */ +#define UBIFS_SIGNATURE_TYPE_PKCS7 1 /* * On-flash inode flags. @@ -336,12 +340,14 @@ enum { * UBIFS_COMPR_NONE: no compression * UBIFS_COMPR_LZO: LZO compression * UBIFS_COMPR_ZLIB: ZLIB compression + * UBIFS_COMPR_ZSTD: ZSTD compression * UBIFS_COMPR_TYPES_CNT: count of supported compression types */ enum { UBIFS_COMPR_NONE, UBIFS_COMPR_LZO, UBIFS_COMPR_ZLIB, + UBIFS_COMPR_ZSTD, UBIFS_COMPR_TYPES_CNT, }; @@ -361,6 +367,7 @@ enum { * UBIFS_CS_NODE: commit start node * UBIFS_ORPH_NODE: orphan node * UBIFS_AUTH_NODE: authentication node + * UBIFS_SIG_NODE: signature node * UBIFS_NODE_TYPES_CNT: count of supported node types * * Note, we index arrays by these numbers, so keep them low and contiguous. @@ -381,6 +388,7 @@ enum { UBIFS_CS_NODE, UBIFS_ORPH_NODE, UBIFS_AUTH_NODE, + UBIFS_SIG_NODE, UBIFS_NODE_TYPES_CNT, }; @@ -638,6 +646,8 @@ struct ubifs_pad_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) + * @hash_mst: hash of the master node, only valid for signed images in which the + * master node does not contain a hmac */ struct ubifs_sb_node { struct ubifs_ch ch; @@ -668,7 +678,8 @@ struct ubifs_sb_node { __u8 hmac[UBIFS_MAX_HMAC_LEN]; __u8 hmac_wkm[UBIFS_MAX_HMAC_LEN]; __le16 hash_algo; - __u8 padding2[3838]; + __u8 hash_mst[UBIFS_MAX_HASH_LEN]; + __u8 padding2[3774]; } __packed; /** @@ -771,6 +782,23 @@ struct ubifs_auth_node { } __packed; /** + * struct ubifs_sig_node - node for signing other nodes + * @ch: common header + * @type: type of the signature, currently only UBIFS_SIGNATURE_TYPE_PKCS7 + * supported + * @len: The length of the signature data + * @padding: reserved for future, zeroes + * @sig: The signature data + */ +struct ubifs_sig_node { + struct ubifs_ch ch; + __le32 type; + __le32 len; + __u8 padding[32]; + __u8 sig[]; +} __packed; + +/** * struct ubifs_branch - key/reference/length branch * @lnum: LEB number of the target node * @offs: offset within @lnum diff --git a/fs/ubifs/ubifs.h b/fs/ubifs/ubifs.h index 745b23e5b406..c55f212dcb75 100644 --- a/fs/ubifs/ubifs.h +++ b/fs/ubifs/ubifs.h @@ -1104,7 +1104,6 @@ struct ubifs_debug_info; * used to store indexing nodes (@leb_size - @max_idx_node_sz) * @leb_cnt: count of logical eraseblocks * @max_leb_cnt: maximum count of logical eraseblocks - * @old_leb_cnt: count of logical eraseblocks before re-size * @ro_media: the underlying UBI volume is read-only * @ro_mount: the file-system was mounted as read-only * @ro_error: UBIFS switched to R/O mode because an error happened @@ -1295,6 +1294,7 @@ struct ubifs_info { unsigned int rw_incompat:1; unsigned int assert_action:2; unsigned int authenticated:1; + unsigned int superblock_need_write:1; struct mutex tnc_mutex; struct ubifs_zbranch zroot; @@ -1352,7 +1352,6 @@ struct ubifs_info { int idx_leb_size; int leb_cnt; int max_leb_cnt; - int old_leb_cnt; unsigned int ro_media:1; unsigned int ro_mount:1; unsigned int ro_error:1; @@ -1680,6 +1679,9 @@ static inline int ubifs_auth_node_sz(const struct ubifs_info *c) else return 0; } +int ubifs_sb_verify_signature(struct ubifs_info *c, + const struct ubifs_sb_node *sup); +bool ubifs_hmac_zero(struct ubifs_info *c, const u8 *hmac); int ubifs_hmac_wkm(struct ubifs_info *c, u8 *hmac); |