diff options
| -rw-r--r-- | fs/ubifs/replay.c | 146 |
1 files changed, 144 insertions, 2 deletions
diff --git a/fs/ubifs/replay.c b/fs/ubifs/replay.c index db489d93439c..75f961c4c044 100644 --- a/fs/ubifs/replay.c +++ b/fs/ubifs/replay.c | |||
| @@ -34,6 +34,8 @@ | |||
| 34 | 34 | ||
| 35 | #include "ubifs.h" | 35 | #include "ubifs.h" |
| 36 | #include <linux/list_sort.h> | 36 | #include <linux/list_sort.h> |
| 37 | #include <crypto/hash.h> | ||
| 38 | #include <crypto/algapi.h> | ||
| 37 | 39 | ||
| 38 | /** | 40 | /** |
| 39 | * struct replay_entry - replay list entry. | 41 | * struct replay_entry - replay list entry. |
| @@ -532,6 +534,105 @@ static int is_last_bud(struct ubifs_info *c, struct ubifs_bud *bud) | |||
| 532 | } | 534 | } |
| 533 | 535 | ||
| 534 | /** | 536 | /** |
| 537 | * authenticate_sleb - authenticate one scan LEB | ||
| 538 | * @c: UBIFS file-system description object | ||
| 539 | * @sleb: the scan LEB to authenticate | ||
| 540 | * @log_hash: | ||
| 541 | * @is_last: if true, this is is the last LEB | ||
| 542 | * | ||
| 543 | * This function iterates over the buds of a single LEB authenticating all buds | ||
| 544 | * with the authentication nodes on this LEB. Authentication nodes are written | ||
| 545 | * after some buds and contain a HMAC covering the authentication node itself | ||
| 546 | * and the buds between the last authentication node and the current | ||
| 547 | * authentication node. It can happen that the last buds cannot be authenticated | ||
| 548 | * because a powercut happened when some nodes were written but not the | ||
| 549 | * corresponding authentication node. This function returns the number of nodes | ||
| 550 | * that could be authenticated or a negative error code. | ||
| 551 | */ | ||
| 552 | static int authenticate_sleb(struct ubifs_info *c, struct ubifs_scan_leb *sleb, | ||
| 553 | struct shash_desc *log_hash, int is_last) | ||
| 554 | { | ||
| 555 | int n_not_auth = 0; | ||
| 556 | struct ubifs_scan_node *snod; | ||
| 557 | int n_nodes = 0; | ||
| 558 | int err; | ||
| 559 | u8 *hash, *hmac; | ||
| 560 | |||
| 561 | if (!ubifs_authenticated(c)) | ||
| 562 | return sleb->nodes_cnt; | ||
| 563 | |||
| 564 | hash = kmalloc(crypto_shash_descsize(c->hash_tfm), GFP_NOFS); | ||
| 565 | hmac = kmalloc(c->hmac_desc_len, GFP_NOFS); | ||
| 566 | if (!hash || !hmac) { | ||
| 567 | err = -ENOMEM; | ||
| 568 | goto out; | ||
| 569 | } | ||
| 570 | |||
| 571 | list_for_each_entry(snod, &sleb->nodes, list) { | ||
| 572 | |||
| 573 | n_nodes++; | ||
| 574 | |||
| 575 | if (snod->type == UBIFS_AUTH_NODE) { | ||
| 576 | struct ubifs_auth_node *auth = snod->node; | ||
| 577 | SHASH_DESC_ON_STACK(hash_desc, c->hash_tfm); | ||
| 578 | SHASH_DESC_ON_STACK(hmac_desc, c->hmac_tfm); | ||
| 579 | |||
| 580 | hash_desc->tfm = c->hash_tfm; | ||
| 581 | hash_desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP; | ||
| 582 | |||
| 583 | ubifs_shash_copy_state(c, log_hash, hash_desc); | ||
| 584 | err = crypto_shash_final(hash_desc, hash); | ||
| 585 | if (err) | ||
| 586 | goto out; | ||
| 587 | |||
| 588 | hmac_desc->tfm = c->hmac_tfm; | ||
| 589 | hmac_desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP; | ||
| 590 | err = crypto_shash_digest(hmac_desc, hash, c->hash_len, | ||
| 591 | hmac); | ||
| 592 | if (err) | ||
| 593 | goto out; | ||
| 594 | |||
| 595 | err = ubifs_check_hmac(c, auth->hmac, hmac); | ||
| 596 | if (err) { | ||
| 597 | err = -EPERM; | ||
| 598 | goto out; | ||
| 599 | } | ||
| 600 | n_not_auth = 0; | ||
| 601 | } else { | ||
| 602 | err = crypto_shash_update(log_hash, snod->node, | ||
| 603 | snod->len); | ||
| 604 | if (err) | ||
| 605 | goto out; | ||
| 606 | n_not_auth++; | ||
| 607 | } | ||
| 608 | } | ||
| 609 | |||
| 610 | /* | ||
| 611 | * A powercut can happen when some nodes were written, but not yet | ||
| 612 | * the corresponding authentication node. This may only happen on | ||
| 613 | * the last bud though. | ||
| 614 | */ | ||
| 615 | if (n_not_auth) { | ||
| 616 | if (is_last) { | ||
| 617 | dbg_mnt("%d unauthenticated nodes found on LEB %d, Ignoring them", | ||
| 618 | n_not_auth, sleb->lnum); | ||
| 619 | err = 0; | ||
| 620 | } else { | ||
| 621 | dbg_mnt("%d unauthenticated nodes found on non-last LEB %d", | ||
| 622 | n_not_auth, sleb->lnum); | ||
| 623 | err = -EPERM; | ||
| 624 | } | ||
| 625 | } else { | ||
| 626 | err = 0; | ||
| 627 | } | ||
| 628 | out: | ||
| 629 | kfree(hash); | ||
| 630 | kfree(hmac); | ||
| 631 | |||
| 632 | return err ? err : n_nodes - n_not_auth; | ||
| 633 | } | ||
| 634 | |||
| 635 | /** | ||
| 535 | * replay_bud - replay a bud logical eraseblock. | 636 | * replay_bud - replay a bud logical eraseblock. |
| 536 | * @c: UBIFS file-system description object | 637 | * @c: UBIFS file-system description object |
| 537 | * @b: bud entry which describes the bud | 638 | * @b: bud entry which describes the bud |
| @@ -544,6 +645,7 @@ static int replay_bud(struct ubifs_info *c, struct bud_entry *b) | |||
| 544 | { | 645 | { |
| 545 | int is_last = is_last_bud(c, b->bud); | 646 | int is_last = is_last_bud(c, b->bud); |
| 546 | int err = 0, used = 0, lnum = b->bud->lnum, offs = b->bud->start; | 647 | int err = 0, used = 0, lnum = b->bud->lnum, offs = b->bud->start; |
| 648 | int n_nodes, n = 0; | ||
| 547 | struct ubifs_scan_leb *sleb; | 649 | struct ubifs_scan_leb *sleb; |
| 548 | struct ubifs_scan_node *snod; | 650 | struct ubifs_scan_node *snod; |
| 549 | 651 | ||
| @@ -563,6 +665,15 @@ static int replay_bud(struct ubifs_info *c, struct bud_entry *b) | |||
| 563 | if (IS_ERR(sleb)) | 665 | if (IS_ERR(sleb)) |
| 564 | return PTR_ERR(sleb); | 666 | return PTR_ERR(sleb); |
| 565 | 667 | ||
| 668 | n_nodes = authenticate_sleb(c, sleb, b->bud->log_hash, is_last); | ||
| 669 | if (n_nodes < 0) { | ||
| 670 | err = n_nodes; | ||
| 671 | goto out; | ||
| 672 | } | ||
| 673 | |||
| 674 | ubifs_shash_copy_state(c, b->bud->log_hash, | ||
| 675 | c->jheads[b->bud->jhead].log_hash); | ||
| 676 | |||
| 566 | /* | 677 | /* |
| 567 | * The bud does not have to start from offset zero - the beginning of | 678 | * The bud does not have to start from offset zero - the beginning of |
| 568 | * the 'lnum' LEB may contain previously committed data. One of the | 679 | * the 'lnum' LEB may contain previously committed data. One of the |
| @@ -676,6 +787,10 @@ static int replay_bud(struct ubifs_info *c, struct bud_entry *b) | |||
| 676 | } | 787 | } |
| 677 | if (err) | 788 | if (err) |
| 678 | goto out; | 789 | goto out; |
| 790 | |||
| 791 | n++; | ||
| 792 | if (n == n_nodes) | ||
| 793 | break; | ||
| 679 | } | 794 | } |
| 680 | 795 | ||
| 681 | ubifs_assert(c, ubifs_search_bud(c, lnum)); | 796 | ubifs_assert(c, ubifs_search_bud(c, lnum)); |
| @@ -754,6 +869,7 @@ static int add_replay_bud(struct ubifs_info *c, int lnum, int offs, int jhead, | |||
| 754 | { | 869 | { |
| 755 | struct ubifs_bud *bud; | 870 | struct ubifs_bud *bud; |
| 756 | struct bud_entry *b; | 871 | struct bud_entry *b; |
| 872 | int err; | ||
| 757 | 873 | ||
| 758 | dbg_mnt("add replay bud LEB %d:%d, head %d", lnum, offs, jhead); | 874 | dbg_mnt("add replay bud LEB %d:%d, head %d", lnum, offs, jhead); |
| 759 | 875 | ||
| @@ -763,13 +879,21 @@ static int add_replay_bud(struct ubifs_info *c, int lnum, int offs, int jhead, | |||
| 763 | 879 | ||
| 764 | b = kmalloc(sizeof(struct bud_entry), GFP_KERNEL); | 880 | b = kmalloc(sizeof(struct bud_entry), GFP_KERNEL); |
| 765 | if (!b) { | 881 | if (!b) { |
| 766 | kfree(bud); | 882 | err = -ENOMEM; |
| 767 | return -ENOMEM; | 883 | goto out; |
| 768 | } | 884 | } |
| 769 | 885 | ||
| 770 | bud->lnum = lnum; | 886 | bud->lnum = lnum; |
| 771 | bud->start = offs; | 887 | bud->start = offs; |
| 772 | bud->jhead = jhead; | 888 | bud->jhead = jhead; |
| 889 | bud->log_hash = ubifs_hash_get_desc(c); | ||
| 890 | if (IS_ERR(bud->log_hash)) { | ||
| 891 | err = PTR_ERR(bud->log_hash); | ||
| 892 | goto out; | ||
| 893 | } | ||
| 894 | |||
| 895 | ubifs_shash_copy_state(c, c->log_hash, bud->log_hash); | ||
| 896 | |||
| 773 | ubifs_add_bud(c, bud); | 897 | ubifs_add_bud(c, bud); |
| 774 | 898 | ||
| 775 | b->bud = bud; | 899 | b->bud = bud; |
| @@ -777,6 +901,11 @@ static int add_replay_bud(struct ubifs_info *c, int lnum, int offs, int jhead, | |||
| 777 | list_add_tail(&b->list, &c->replay_buds); | 901 | list_add_tail(&b->list, &c->replay_buds); |
| 778 | 902 | ||
| 779 | return 0; | 903 | return 0; |
| 904 | out: | ||
| 905 | kfree(bud); | ||
| 906 | kfree(b); | ||
| 907 | |||
| 908 | return err; | ||
| 780 | } | 909 | } |
| 781 | 910 | ||
| 782 | /** | 911 | /** |
| @@ -882,6 +1011,14 @@ static int replay_log_leb(struct ubifs_info *c, int lnum, int offs, void *sbuf) | |||
| 882 | 1011 | ||
| 883 | c->cs_sqnum = le64_to_cpu(node->ch.sqnum); | 1012 | c->cs_sqnum = le64_to_cpu(node->ch.sqnum); |
| 884 | dbg_mnt("commit start sqnum %llu", c->cs_sqnum); | 1013 | dbg_mnt("commit start sqnum %llu", c->cs_sqnum); |
| 1014 | |||
| 1015 | err = ubifs_shash_init(c, c->log_hash); | ||
| 1016 | if (err) | ||
| 1017 | goto out; | ||
| 1018 | |||
| 1019 | err = ubifs_shash_update(c, c->log_hash, node, UBIFS_CS_NODE_SZ); | ||
| 1020 | if (err < 0) | ||
| 1021 | goto out; | ||
| 885 | } | 1022 | } |
| 886 | 1023 | ||
| 887 | if (snod->sqnum < c->cs_sqnum) { | 1024 | if (snod->sqnum < c->cs_sqnum) { |
| @@ -929,6 +1066,11 @@ static int replay_log_leb(struct ubifs_info *c, int lnum, int offs, void *sbuf) | |||
| 929 | if (err) | 1066 | if (err) |
| 930 | goto out_dump; | 1067 | goto out_dump; |
| 931 | 1068 | ||
| 1069 | err = ubifs_shash_update(c, c->log_hash, ref, | ||
| 1070 | UBIFS_REF_NODE_SZ); | ||
| 1071 | if (err) | ||
| 1072 | goto out; | ||
| 1073 | |||
| 932 | err = add_replay_bud(c, le32_to_cpu(ref->lnum), | 1074 | err = add_replay_bud(c, le32_to_cpu(ref->lnum), |
| 933 | le32_to_cpu(ref->offs), | 1075 | le32_to_cpu(ref->offs), |
| 934 | le32_to_cpu(ref->jhead), | 1076 | le32_to_cpu(ref->jhead), |
