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