1 /*
2  * Copyright (C) 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "checkpoint.h"
18 #include "block_allocator.h"
19 #include "block_cache.h"
20 #include "block_mac.h"
21 #include "debug.h"
22 #include "transaction.h"
23 
24 #define CHECKPOINT_MAGIC (0x0063797473757274) /* trustyc\0 */
25 
26 /**
27  * struct checkpoint - On-disk block containing the checkpoint metadata
28  * @iv:             Initial value used for encrypt/decrypt
29  * @magic:          CHECKPOINT_MAGIC
30  * @files:          Block and mac of checkpointed files tree root node
31  * @free:           Block and mac of checkpointed free set root node. When a
32  *                  checkpoint is active blocks may only be allocated if they
33  *                  are marked as free in both the filesystem free set and this
34  *                  checkpointed free set.
35  */
36 struct checkpoint {
37     struct iv iv;
38     uint64_t magic;
39     struct block_mac files;
40     struct block_mac free;
41 };
42 
43 /**
44  * checkpoint_get_new_block - Get a new, writable copy of the checkpoint block
45  * metadata
46  * @tr:                 Transaction object.
47  * @new_checkpoint_ref: Output pointer to hold the block reference for the new
48  *                      block
49  * @checkpoint_mac:     Pointer to the current checkpoint block mac.
50  * Updated with the block number of the new checkpoint block on success.
51  *
52  * Returns a new, writable copy of the checkpoint metadata block, or %NULL on
53  * failure (tr->failed will be set). The returned pointer should then be passed
54  * to checkpoint_update_roots() after the file tree and free set are finalized.
55  * We have to split this operation in two so that the newly allocated block will
56  * be removed from the free set.
57  *
58  * Caller takes ownership of the returned new, dirty block and is responsible
59  * for releasing @new_checkpoint_ref.
60  */
checkpoint_get_new_block(struct transaction * tr,struct obj_ref * new_checkpoint_ref,struct block_mac * checkpoint_mac)61 struct checkpoint* checkpoint_get_new_block(struct transaction* tr,
62                                             struct obj_ref* new_checkpoint_ref,
63                                             struct block_mac* checkpoint_mac) {
64     data_block_t new_checkpoint_block;
65     struct checkpoint* new_checkpoint;
66 
67     new_checkpoint_block = block_allocate(tr);
68     if (tr->failed) {
69         pr_warn("transaction failed, abort\n");
70         return NULL;
71     }
72     assert(new_checkpoint_block);
73 
74     if (block_mac_valid(tr, checkpoint_mac)) {
75         block_free(tr, block_mac_to_block(tr, checkpoint_mac));
76     }
77     new_checkpoint = block_get_cleared(tr, new_checkpoint_block, false,
78                                        new_checkpoint_ref);
79 
80     block_mac_set_block(tr, checkpoint_mac, new_checkpoint_block);
81 
82     new_checkpoint->magic = CHECKPOINT_MAGIC;
83 
84     return new_checkpoint;
85 }
86 
87 /**
88  * checkpoint_update_roots - Update the files and free blocks of a checkpoint
89  * @tr:             Transaction object.
90  * @new_checkpoint: Pointer to a checkpoint metadata block returned by
91  *                  checkpoint_get_new_block()
92  * @files:          New checkpoint files tree root node.
93  * @free:           New checkpoint free set root node.
94  */
checkpoint_update_roots(struct transaction * tr,struct checkpoint * new_checkpoint,const struct block_mac * files,const struct block_mac * free)95 void checkpoint_update_roots(struct transaction* tr,
96                              struct checkpoint* new_checkpoint,
97                              const struct block_mac* files,
98                              const struct block_mac* free) {
99     new_checkpoint->files = *files;
100     new_checkpoint->free = *free;
101 }
102 
103 /**
104  * checkpoint_read - Initialize root blocks from a checkpoint page
105  * @fs:             File-system to initialize checkpoint state in.
106  * @checkpoint:     Checkpoint root page block and mac. Must be a valid block.
107  * @files:          New checkpoint file tree. May be %NULL.
108  * @free:           New checkpoint free set. May be %NULL.
109  *
110  * Returns %true if the @files and @free nodes were properly populated from the
111  * fields in @checkpoint. Either @files or @free may be %NULL; %NULL out params
112  * will not be set. Returns %false and does not change @files or @free if the
113  * @checkpoint metadata page exists but could not be read.
114  *
115  * Example: checkpoint_read(tr, &tr->fs->checkpoint, &files,
116  *                          &tr->fs->checkpoint_free)
117  */
checkpoint_read(struct transaction * tr,const struct block_mac * checkpoint,struct block_tree * files,struct block_set * free)118 bool checkpoint_read(struct transaction* tr,
119                      const struct block_mac* checkpoint,
120                      struct block_tree* files,
121                      struct block_set* free) {
122     const struct checkpoint* checkpoint_ro;
123     struct obj_ref checkpoint_ro_ref = OBJ_REF_INITIAL_VALUE(checkpoint_ro_ref);
124 
125     assert(block_mac_valid(tr, checkpoint));
126 
127     checkpoint_ro = block_get(tr, checkpoint, NULL, &checkpoint_ro_ref);
128     if (tr->failed) {
129         goto err_block_get;
130     }
131 
132     if (checkpoint_ro->magic != CHECKPOINT_MAGIC) {
133         pr_err("Checkpoint magic mismatch!\n");
134         transaction_fail(tr);
135         goto err_magic_mismatch;
136     }
137 
138     if (files) {
139         files->root = checkpoint_ro->files;
140     }
141     if (free) {
142         free->block_tree.root = checkpoint_ro->free;
143         block_range_clear(&free->initial_range);
144     }
145 
146 err_magic_mismatch:
147     block_put(checkpoint_ro, &checkpoint_ro_ref);
148 err_block_get:
149     return !tr->failed;
150 }
151 
152 /**
153  * checkpoint_commit - Save the current file-system state as a checkpoint
154  * @fs:             File-system to checkpoint.
155  *
156  * Create and commit a checkpoint of the current state of @fs.
157  *
158  * Returns %true if the checkpoint was created and committed successfully,
159  * %false otherwise.
160  */
checkpoint_commit(struct fs * fs)161 bool checkpoint_commit(struct fs* fs) {
162     struct transaction tr;
163     bool success;
164 
165     assert(fs);
166     transaction_init(&tr, fs, true);
167     transaction_complete_etc(&tr, true);
168     success = !tr.failed;
169     if (success) {
170         pr_init("Automatically created a checkpoint for filesystem %s\n",
171                 fs->name);
172     } else {
173         pr_err("Failed to commit checkpoint for filesystem %s\n", fs->name);
174     }
175     transaction_free(&tr);
176     return success;
177 }
178