1 /*
2 * Copyright (C) 2015 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 <assert.h>
18 #include <inttypes.h>
19 #include <lk/macros.h>
20 #include <stdbool.h>
21 #include <stdint.h>
22 #include <stdio.h>
23 #include <string.h>
24
25 #include "block_allocator.h"
26 #include "block_map.h"
27 #include "crypt.h"
28 #include "debug.h"
29 #include "file.h"
30 #include "transaction.h"
31
32 #define BIT_MASK(x) \
33 (((x) >= sizeof(uint64_t) * 8) ? (~0ULL) : ((1ULL << (x)) - 1))
34
35 #define FILE_ENTRY_MAGIC (0x0066797473757274) /* trustyf\0 */
36
37 /**
38 * struct file_entry - On-disk file entry
39 * @iv: initial value used for encrypt/decrypt
40 * @magic: FILE_ENTRY_MAGIC.
41 * @block_map: Block and mac of file block map root node.
42 * @size: File size in bytes.
43 * @reserved: Reserved for future use. Write 0, read ignore.
44 * @path: File path and name.
45 */
46 struct file_entry {
47 struct iv iv;
48 uint64_t magic;
49 struct block_mac block_map;
50 struct file_info info;
51 };
52
53 static bool file_tree_lookup(struct block_mac* block_mac_out,
54 struct transaction* tr,
55 struct block_tree* tree,
56 struct block_tree_path* tree_path,
57 const char* file_path,
58 bool remove);
59
60 /**
61 * path_hash - Convert a path string to hash value
62 * @tr: Transaction object.
63 * @path: String to calculate hash for.
64 *
65 * Return: non-zero hash value that fits in @tr->fs->block_num_size bytes
66 * computed from @path
67 */
path_hash(struct transaction * tr,const char * path)68 static data_block_t path_hash(struct transaction* tr, const char* path) {
69 data_block_t hash = str_hash(path);
70
71 hash &= BIT_MASK(tr->fs->block_num_size * 8);
72 if (!hash) {
73 hash++; /* 0 key is not supported by block_tree */
74 }
75
76 return hash;
77 }
78
79 /**
80 * file_block_map_init - Initialize in-memory block map state from file entry
81 * @tr: Transaction object.
82 * @block_map: Block map object.
83 * @file: Block number and mac of file entry.
84 *
85 * Load file entry at @file and initialize @block_map with root node stored
86 * there.
87 */
file_block_map_init(struct transaction * tr,struct block_map * block_map,const struct block_mac * file)88 void file_block_map_init(struct transaction* tr,
89 struct block_map* block_map,
90 const struct block_mac* file) {
91 const struct file_entry* file_entry_ro;
92 struct obj_ref file_entry_ro_ref = OBJ_REF_INITIAL_VALUE(file_entry_ro_ref);
93
94 if (!block_mac_valid(tr, file)) {
95 goto err;
96 }
97 file_entry_ro = block_get(tr, file, NULL, &file_entry_ro_ref);
98 if (!file_entry_ro) {
99 goto err;
100 }
101 assert(file_entry_ro);
102 block_map_init(tr, block_map, &file_entry_ro->block_map,
103 tr->fs->dev->block_size);
104 block_put(file_entry_ro, &file_entry_ro_ref);
105
106 return;
107
108 err:
109 if (tr->failed) {
110 pr_warn("can't read file entry at %" PRIu64 ", transaction failed\n",
111 block_mac_to_block(tr, file));
112 } else {
113 pr_err("can't read file entry at %" PRIu64 "\n",
114 block_mac_to_block(tr, file));
115 transaction_fail(tr);
116 }
117 }
118
119 /**
120 * file_print - Print information about a file
121 * @tr: Transaction object.
122 * @file: File handle object.
123 */
file_print(struct transaction * tr,const struct storage_file_handle * file)124 void file_print(struct transaction* tr,
125 const struct storage_file_handle* file) {
126 const struct file_entry* file_entry_ro;
127 struct obj_ref file_entry_ro_ref = OBJ_REF_INITIAL_VALUE(file_entry_ro_ref);
128 struct block_map block_map;
129
130 file_block_map_init(tr, &block_map, &file->block_mac);
131 file_entry_ro = block_get(tr, &file->block_mac, NULL, &file_entry_ro_ref);
132 if (!file_entry_ro) {
133 printf("can't read file entry at %" PRIu64 "\n",
134 block_mac_to_block(tr, &file->block_mac));
135 return;
136 }
137 assert(file_entry_ro);
138 printf("file at %" PRIu64 ", path %s, size %" PRIu64 ", hash 0x%" PRIx64
139 ", block map (tree)\n",
140 block_mac_to_block(tr, &file->block_mac), file_entry_ro->info.path,
141 file_entry_ro->info.size, path_hash(tr, file_entry_ro->info.path));
142 block_put(file_entry_ro, &file_entry_ro_ref);
143 block_tree_print(tr, &block_map.tree);
144 }
145
146 /**
147 * files_print - Print information about all committed files
148 * @tr: Transaction object.
149 */
files_print(struct transaction * tr)150 void files_print(struct transaction* tr) {
151 struct block_tree_path path;
152 struct storage_file_handle file;
153
154 printf("%s: files:\n", __func__);
155 block_tree_print(tr, &tr->fs->files);
156
157 block_tree_walk(tr, &tr->fs->files, 0, true, &path);
158 while (block_tree_path_get_key(&path)) {
159 file.block_mac = block_tree_path_get_data_block_mac(&path);
160 file_print(tr, &file);
161 block_tree_path_next(&path);
162 }
163 }
164
165 /**
166 * file_check - Check the file block map for errors
167 * @tr: Transaction object.
168 * @file: File handle object.
169 *
170 * Return: %false if an unrecoverable error was detected in the file block map,
171 * %true if the block map was consistent.
172 */
file_check(struct transaction * tr,const struct storage_file_handle * file)173 bool file_check(struct transaction* tr,
174 const struct storage_file_handle* file) {
175 struct block_map block_map;
176 data_block_t file_block_size = get_file_block_size(tr->fs);
177
178 file_block_map_init(tr, &block_map, &file->block_mac);
179 return block_map_check(tr, &block_map,
180 DIV_ROUND_UP(file->size, file_block_size));
181 }
182
183 /**
184 * file_block_map_update - Update file entry with block_map or size changes
185 * @tr: Transaction object.
186 * @block_map: Block map object.
187 * @file: File handle object.
188 *
189 */
file_block_map_update(struct transaction * tr,struct block_map * block_map,const char * new_path,struct storage_file_handle * file)190 static void file_block_map_update(struct transaction* tr,
191 struct block_map* block_map,
192 const char* new_path,
193 struct storage_file_handle* file) {
194 bool found;
195 data_block_t old_block;
196 data_block_t file_block;
197 data_block_t new_block;
198 struct block_mac block_mac;
199 const struct file_entry* file_entry_ro;
200 struct file_entry* file_entry_rw = NULL;
201 struct obj_ref file_entry_ref = OBJ_REF_INITIAL_VALUE(file_entry_ref);
202 struct obj_ref file_entry_copy_ref = OBJ_REF_INITIAL_VALUE(file_entry_ref);
203 struct block_tree_path tree_path;
204
205 if (tr->failed) {
206 pr_warn("transaction failed, abort\n");
207 return;
208 }
209
210 assert(block_mac_valid(tr, &file->block_mac));
211
212 file_entry_ro = block_get(tr, &file->block_mac, NULL, &file_entry_ref);
213 if (!file_entry_ro) {
214 assert(tr->failed);
215 pr_warn("transaction failed, abort\n");
216 return;
217 }
218 assert(file_entry_ro);
219 found = file_tree_lookup(&block_mac, tr, &tr->files_added, &tree_path,
220 file_entry_ro->info.path, !!new_path);
221 if (!found) {
222 found = file_tree_lookup(&block_mac, tr, &tr->fs->files, &tree_path,
223 file_entry_ro->info.path, false);
224 if (tr->failed) {
225 pr_warn("transaction failed, abort\n");
226 goto err;
227 }
228 assert(found);
229
230 if (new_path) {
231 block_tree_insert_block_mac(tr, &tr->files_removed,
232 block_mac_to_block(tr, &block_mac),
233 block_mac);
234 }
235
236 old_block = block_mac_to_block(tr, &block_mac);
237 file_block = block_mac_to_block(tr, &file->block_mac);
238 if (transaction_block_need_copy(tr, file_block)) {
239 if (tr->failed) {
240 pr_warn("transaction failed, abort\n");
241 goto err;
242 }
243 assert((block_map && block_map->tree.root_block_changed) ||
244 file->size != file_entry_ro->info.size || new_path);
245 assert(old_block == file_block);
246 new_block = block_allocate(tr);
247 if (tr->failed) {
248 pr_warn("transaction failed, abort\n");
249 goto err;
250 }
251 assert(new_block);
252 block_mac_set_block(tr, &block_mac, new_block);
253
254 pr_write("copy file block %" PRIu64 " -> %" PRIu64 "\n", file_block,
255 new_block);
256
257 /*
258 * Use block_get_copy instead of block_move since tr->fs->files
259 * is still used and not updated until file_transaction_complete.
260 */
261 // TODO: fix
262 file_entry_rw = block_get_copy(tr, file_entry_ro, new_block, false,
263 &file_entry_copy_ref);
264 block_put(file_entry_ro, &file_entry_ref);
265 file_entry_ro = file_entry_rw;
266 obj_ref_transfer(&file_entry_ref, &file_entry_copy_ref);
267 if (tr->failed) {
268 pr_warn("transaction failed, abort\n");
269 goto err;
270 }
271 /* TODO: insert mac */
272 block_tree_insert(tr, &tr->files_updated, file_block, new_block);
273 block_free(tr, file_block);
274 file->block_mac = block_mac;
275 }
276 assert(!file_tree_lookup(NULL, tr, &tr->files_added, &tree_path,
277 file_entry_ro->info.path, false));
278
279 /* TODO: get path from insert operation */
280 block_tree_walk(tr, &tr->files_updated, old_block, false, &tree_path);
281 if (tr->failed) {
282 pr_warn("transaction failed, abort\n");
283 goto err;
284 }
285 assert(block_tree_path_get_key(&tree_path) == old_block);
286 block_mac = block_tree_path_get_data_block_mac(&tree_path);
287 assert(block_mac_to_block(tr, &block_mac) ==
288 block_mac_to_block(tr, &file->block_mac));
289 /* TODO: enable after insert mac TODO */
290 // assert(block_mac_eq(tr, &block_mac, &file->block_mac));
291
292 if (new_path) {
293 /* TODO: remove by path or, when possible, skip insert instead */
294 block_tree_remove(tr, &tr->files_updated, old_block,
295 block_mac_to_block(tr, &block_mac));
296 }
297 }
298 if (!file_entry_rw) {
299 file_entry_rw = block_dirty(tr, file_entry_ro, false);
300 }
301
302 if (block_map) {
303 pr_write("file at %" PRIu64 ": update block_map %" PRIu64 " -> %" PRIu64
304 "\n",
305 block_mac_to_block(tr, &block_mac),
306 block_mac_to_block(tr, &file_entry_rw->block_map),
307 block_mac_to_block(tr, &block_map->tree.root));
308
309 file_entry_rw->block_map = block_map->tree.root;
310 }
311
312 file_entry_rw->info.size = file->size;
313 if (new_path) {
314 strcpy(file_entry_rw->info.path, new_path);
315 block_put_dirty(tr, file_entry_rw, &file_entry_ref, &block_mac, NULL);
316 block_tree_insert_block_mac(tr, &tr->files_added,
317 path_hash(tr, new_path), block_mac);
318 if (tr->failed) {
319 pr_warn("transaction failed, abort\n");
320 goto err;
321 }
322 block_mac_copy(tr, &file->block_mac, &block_mac);
323 } else {
324 block_tree_path_put_dirty(tr, &tree_path, tree_path.count,
325 file_entry_rw, &file_entry_ref);
326 /* TODO: add better api */
327 file->block_mac = tree_path.entry[tree_path.count].block_mac;
328 }
329
330 /*
331 * Move to head of list so opening the same file twice in a transaction
332 * commits the changes to the file_handle that was modified. Writing to
333 * both handles is not currently supported.
334 */
335 list_delete(&file->node);
336 list_add_head(&tr->open_files, &file->node);
337
338 return;
339
340 err:
341 if (file_entry_rw) {
342 block_put_dirty_discard(file_entry_rw, &file_entry_ref);
343 } else {
344 block_put(file_entry_ro, &file_entry_ref);
345 }
346 }
347
348 /**
349 * get_file_block_size - Get file data block size
350 * @fs: File system state object.
351 *
352 * Return: Get size of data block returned by file_get_block and
353 * file_get_block_write for @fs.
354 */
get_file_block_size(struct fs * fs)355 size_t get_file_block_size(struct fs* fs) {
356 return fs->dev->block_size - sizeof(struct iv);
357 }
358
359 /**
360 * file_get_block_etc - Helper function to get a file block for read or write
361 * @tr: Transaction object.
362 * @file: File handle object.
363 * @file_block: File block number. 0 based.
364 * @read: %true if data should be read from disk, %false otherwise.
365 * @write: %true if returned data should be writeable, %false otherwise.
366 * @ref: Pointer to store reference in.
367 *
368 * Return: Const block data pointer, or NULL if no valid block is found at
369 * @file_block or if @write is %true and a block could not be allocated.
370 */
file_get_block_etc(struct transaction * tr,struct storage_file_handle * file,data_block_t file_block,bool read,bool write,struct obj_ref * ref)371 static const void* file_get_block_etc(struct transaction* tr,
372 struct storage_file_handle* file,
373 data_block_t file_block,
374 bool read,
375 bool write,
376 struct obj_ref* ref) {
377 bool found = false;
378 const void* data = NULL;
379 struct block_map block_map;
380 struct block_mac block_mac;
381 data_block_t old_disk_block;
382 data_block_t new_block;
383 bool dirty = false;
384
385 if (tr->failed) {
386 pr_warn("transaction failed, ignore\n");
387 goto err;
388 }
389
390 file_block_map_init(tr, &block_map, &file->block_mac);
391 if (tr->failed) {
392 pr_warn("transaction failed, abort\n");
393 goto err;
394 }
395
396 file->used_by_tr = true;
397
398 found = block_map_get(tr, &block_map, file_block, &block_mac);
399 if (found) {
400 if (read) {
401 data = block_get(tr, &block_mac, NULL, ref); /* TODO: pass iv? */
402 } else {
403 data = block_get_no_read(tr, block_mac_to_block(tr, &block_mac),
404 ref);
405 }
406 /* TODO: handle mac mismatch better */
407 }
408
409 old_disk_block = found ? block_mac_to_block(tr, &block_mac) : 0;
410 if (write && (!found || transaction_block_need_copy(tr, old_disk_block))) {
411 new_block = block_allocate(tr);
412 if (tr->failed) {
413 pr_warn("transaction failed, abort\n");
414 goto err;
415 }
416 assert(new_block);
417 block_mac_set_block(tr, &block_mac, new_block);
418
419 if (found) {
420 data = block_move(tr, data, new_block, false);
421 dirty = true;
422 assert(!tr->failed);
423 block_free(tr, old_disk_block);
424 } else {
425 if (read) {
426 assert(write);
427 data = block_get_cleared(tr, new_block, false, ref);
428 dirty = true;
429 } else {
430 data = block_get_no_read(tr, new_block, ref);
431 }
432 }
433 if (tr->failed) {
434 pr_warn("transaction failed, abort\n");
435 goto err;
436 }
437 /* TODO: get new mac */
438 assert(!tr->failed);
439 block_map_set(tr, &block_map, file_block, &block_mac);
440 file_block_map_update(tr, &block_map, NULL, file);
441 if (tr->failed) {
442 pr_warn("transaction failed, abort\n");
443 goto err;
444 }
445 }
446 if (write && !dirty) {
447 data = block_dirty(tr, data, false);
448 }
449 if (!data) {
450 return NULL;
451 }
452 return data + sizeof(struct iv);
453
454 err:
455 if (dirty) {
456 block_put_dirty_discard((void*)data, ref);
457 } else if (data) {
458 block_put(data, ref);
459 }
460 return NULL;
461 }
462
463 /**
464 * file_get_block - Get a file block for read
465 * @tr: Transaction object.
466 * @file: File handle object.
467 * @file_block: File block number. 0 based.
468 * @ref: Pointer to store reference in.
469 *
470 * Return: Const block data pointer, or NULL if no valid block is found at
471 * @file_block.
472 */
file_get_block(struct transaction * tr,struct storage_file_handle * file,data_block_t file_block,struct obj_ref * ref)473 const void* file_get_block(struct transaction* tr,
474 struct storage_file_handle* file,
475 data_block_t file_block,
476 struct obj_ref* ref) {
477 return file_get_block_etc(tr, file, file_block, true, false, ref);
478 }
479
480 /**
481 * file_get_block_write - Helper function to get a file block for write
482 * @tr: Transaction object.
483 * @file: File handle object.
484 * @file_block: File block number. 0 based.
485 * @read: %true if data should be read from disk, %false otherwise.
486 * @ref: Pointer to store reference in.
487 *
488 * Return: Block data pointer, or NULL if a block could not be allocated.
489 */
file_get_block_write(struct transaction * tr,struct storage_file_handle * file,data_block_t file_block,bool read,struct obj_ref * ref)490 void* file_get_block_write(struct transaction* tr,
491 struct storage_file_handle* file,
492 data_block_t file_block,
493 bool read,
494 struct obj_ref* ref) {
495 return (void*)file_get_block_etc(tr, file, file_block, read, true, ref);
496 }
497
498 /**
499 * file_block_put - Release reference to a block returned by file_get_block
500 * @data: File block data pointer
501 * @data_ref: Reference pointer to release
502 */
file_block_put(const void * data,struct obj_ref * data_ref)503 void file_block_put(const void* data, struct obj_ref* data_ref) {
504 block_put(data - sizeof(struct iv), data_ref);
505 }
506
507 /**
508 * file_block_put_dirty - Release reference to a dirty file block
509 * @tr: Transaction
510 * @file: File handle object.
511 * @file_block: File block number. 0 based.
512 * @data: File block data pointer
513 * @data_ref: Reference pointer to release
514 *
515 * Release reference to a file block previously returned by
516 * file_get_block_write, and update mac value in block_mac.
517 */
file_block_put_dirty(struct transaction * tr,struct storage_file_handle * file,data_block_t file_block,void * data,struct obj_ref * data_ref)518 void file_block_put_dirty(struct transaction* tr,
519 struct storage_file_handle* file,
520 data_block_t file_block,
521 void* data,
522 struct obj_ref* data_ref) {
523 struct block_map block_map;
524
525 file_block_map_init(tr, &block_map, &file->block_mac);
526
527 block_map_put_dirty(tr, &block_map, file_block, data - sizeof(struct iv),
528 data_ref);
529
530 file_block_map_update(tr, &block_map, NULL, file);
531 }
532
533 /**
534 * file_get_info - Get a file informaion for read
535 * @tr: Transaction object.
536 * @block_mac: File entry block_mac.
537 * @ref: Pointer to store reference in.
538 *
539 * Return: Const file info pointer, or NULL if no valid block is found at
540 * @block_mac.
541 */
file_get_info(struct transaction * tr,const struct block_mac * block_mac,struct obj_ref * ref)542 const struct file_info* file_get_info(struct transaction* tr,
543 const struct block_mac* block_mac,
544 struct obj_ref* ref) {
545 const struct file_entry* file_entry;
546
547 file_entry = block_get(tr, block_mac, NULL, ref);
548 if (!file_entry) {
549 assert(tr->failed);
550 pr_warn("transaction failed, abort\n");
551 return NULL;
552 }
553 assert(file_entry);
554 assert(file_entry->magic == FILE_ENTRY_MAGIC);
555
556 return &file_entry->info;
557 }
558
559 /**
560 * file_info_put - Release reference to a block returned by file_get_info
561 * @data: File info data pointer
562 * @data_ref: Reference pointer to release
563 */
file_info_put(const struct file_info * data,struct obj_ref * data_ref)564 void file_info_put(const struct file_info* data, struct obj_ref* data_ref) {
565 const struct file_entry* entry = containerof(data, struct file_entry, info);
566 assert(entry->magic == FILE_ENTRY_MAGIC);
567 block_put(entry, data_ref);
568 }
569
570 /**
571 * file_get_size - Get file size
572 * @tr: Transaction
573 * @file: File handle object.
574 * @size: Pointer to return size in.
575 *
576 * Return: %true if @size was set, %false if @size was not set because @file is
577 * invalid or @tr has failed.
578 */
file_get_size(struct transaction * tr,struct storage_file_handle * file,data_block_t * size)579 bool file_get_size(struct transaction* tr,
580 struct storage_file_handle* file,
581 data_block_t* size) {
582 if (tr->failed) {
583 pr_warn("transaction failed, ignore\n");
584 return false;
585 }
586
587 if (!block_mac_valid(tr, &file->block_mac)) {
588 pr_warn("invalid file handle\n");
589 return false;
590 }
591
592 file->used_by_tr = true;
593 *size = file->size;
594
595 return true;
596 }
597
598 /**
599 * file_set_size - Set file size
600 * @tr: Transaction
601 * @file: File handle object.
602 * @size: New file size.
603 *
604 * Set file size and free blocks that are not longer needed. Does not clear any
605 * partial block data.
606 */
file_set_size(struct transaction * tr,struct storage_file_handle * file,data_block_t size)607 void file_set_size(struct transaction* tr,
608 struct storage_file_handle* file,
609 data_block_t size) {
610 data_block_t file_block;
611 struct block_map block_map;
612 size_t file_block_size = get_file_block_size(tr->fs);
613
614 if (tr->failed) {
615 pr_warn("transaction failed, ignore\n");
616 return;
617 }
618
619 file_block_map_init(tr, &block_map, &file->block_mac);
620 if (tr->failed) {
621 pr_warn("transaction failed, abort\n");
622 return;
623 }
624 if (size == file->size) {
625 return;
626 }
627 if (size < file->size) {
628 file_block = DIV_ROUND_UP(size, file_block_size);
629 block_map_truncate(tr, &block_map, file_block);
630 }
631 file->size = size;
632 file_block_map_update(tr, &block_map, NULL, file);
633 }
634
635 /**
636 * file_tree_lookup - Helper function to search for a file in a specific tree
637 * @block_mac_out: Block-mac object to return block number and mac in.
638 * @tr: Transaction object.
639 * @tree: Tree object.
640 * @tree_path: Tree path object.
641 * @file_path: File path string.
642 * @remove: %true if found entry should be removed, %false otherwise.
643 *
644 * Return: %true if @file_path was found in @tree, %false otherwise.
645 */
file_tree_lookup(struct block_mac * block_mac_out,struct transaction * tr,struct block_tree * tree,struct block_tree_path * tree_path,const char * file_path,bool remove)646 static bool file_tree_lookup(struct block_mac* block_mac_out,
647 struct transaction* tr,
648 struct block_tree* tree,
649 struct block_tree_path* tree_path,
650 const char* file_path,
651 bool remove) {
652 struct block_mac block_mac;
653 data_block_t hash = path_hash(tr, file_path);
654 const struct file_entry* file_entry;
655 struct obj_ref file_entry_ref = OBJ_REF_INITIAL_VALUE(file_entry_ref);
656 bool found = false;
657
658 assert(strlen(file_path) < sizeof(file_entry->info.path));
659 assert(sizeof(*file_entry) <= tr->fs->dev->block_size);
660
661 block_tree_walk(tr, tree, hash - 1, false, tree_path);
662 while (block_tree_path_get_key(tree_path) &&
663 block_tree_path_get_key(tree_path) < hash) {
664 block_tree_path_next(tree_path);
665 }
666 while (block_tree_path_get_key(tree_path) == hash) {
667 block_mac = block_tree_path_get_data_block_mac(tree_path);
668 if (!block_mac_to_block(tr, &block_mac)) {
669 if (LOCAL_TRACE >= TRACE_LEVEL_WARNING) {
670 pr_warn("got 0 block pointer for hash %" PRIu64
671 " while looking for %s, hash 0x%" PRIx64 "\n",
672 block_tree_path_get_key(tree_path), file_path, hash);
673 block_tree_print(tr, tree);
674 }
675 block_tree_path_next(tree_path);
676 block_mac = block_tree_path_get_data_block_mac(tree_path);
677 pr_warn("next %" PRIu64 ", hash 0x%" PRIx64 "\n",
678 block_mac_to_block(tr, &block_mac),
679 block_tree_path_get_key(tree_path));
680 }
681 if (tr->failed) {
682 pr_warn("transaction failed, abort\n");
683 return false;
684 }
685 assert(block_mac_to_block(tr, &block_mac));
686 file_entry = block_get(tr, &block_mac, NULL, &file_entry_ref);
687 if (!file_entry) {
688 assert(tr->failed);
689 pr_warn("transaction failed, abort\n");
690 return false;
691 }
692 assert(file_entry);
693 assert(file_entry->magic == FILE_ENTRY_MAGIC);
694 found = !strcmp(file_path, file_entry->info.path);
695
696 pr_read("block %" PRIu64 ", hash 0x%" PRIx64 " match, found %d\n",
697 block_mac_to_block(tr, &block_mac), hash, found);
698
699 block_put(file_entry, &file_entry_ref);
700 if (found) {
701 if (remove) {
702 /* TODO: remove by path */
703 // block_tree_remove_path(tr, tree_path);
704 /* TODO: pass mac */
705 block_tree_remove(tr, tree, hash,
706 block_mac_to_block(tr, &block_mac));
707 }
708 *block_mac_out = block_mac;
709 return true;
710 }
711 block_tree_path_next(tree_path);
712 }
713 if (LOCAL_TRACE >= TRACE_LEVEL_READ) {
714 pr_read("%s: hash 0x%" PRIx64 " does not match 0x%" PRIx64 "\n",
715 __func__, hash, block_tree_path_get_key(tree_path));
716 block_tree_print(tr, tree);
717 }
718 return false;
719 }
720
721 /**
722 * file_create - Helper function to create a new file
723 * @block_mac_out: Block-mac object to return block number and mac in.
724 * @tr: Transaction object.
725 * @path: File path string.
726 *
727 * Create a new file and insert it into &tr->files_added. Caller must make
728 * a file matching @path does not already exist.
729 *
730 * Return: %true if file was created, %false otherwise.
731 */
file_create(struct block_mac * block_mac_out,struct transaction * tr,const char * path)732 static bool file_create(struct block_mac* block_mac_out,
733 struct transaction* tr,
734 const char* path) {
735 data_block_t block;
736 struct file_entry* file_entry;
737 struct obj_ref file_entry_ref = OBJ_REF_INITIAL_VALUE(file_entry_ref);
738 data_block_t hash;
739
740 hash = path_hash(tr, path);
741
742 block = block_allocate(tr);
743
744 pr_write("create file, %s, hash 0x%" PRIx64 ", at block %" PRIu64 "\n",
745 path, hash, block);
746
747 if (tr->failed) {
748 pr_warn("transaction failed, abort\n");
749 goto err;
750 }
751
752 assert(block);
753 block_mac_set_block(tr, block_mac_out, block);
754
755 file_entry = block_get_cleared(tr, block, false, &file_entry_ref);
756 assert(file_entry);
757 file_entry->magic = FILE_ENTRY_MAGIC;
758 strcpy(file_entry->info.path, path);
759 block_put_dirty(tr, file_entry, &file_entry_ref, block_mac_out, NULL);
760 if (tr->failed) {
761 pr_warn("transaction failed, abort\n");
762 goto err;
763 }
764 block_tree_insert_block_mac(tr, &tr->files_added, hash, *block_mac_out);
765 if (tr->failed) {
766 pr_warn("transaction failed, abort\n");
767 goto err;
768 }
769 return true;
770
771 err:
772 return false;
773 }
774
775 /**
776 * file_is_removed - Helper function to check if transaction deletes file
777 * @tr: Transaction object.
778 * @block: Block number for file.
779 *
780 * Return: %true if file is removed in @tr, %false otherwise.
781 */
file_is_removed(struct transaction * tr,data_block_t block)782 static bool file_is_removed(struct transaction* tr, data_block_t block) {
783 struct block_tree_path tree_path;
784
785 block_tree_walk(tr, &tr->files_removed, block, false, &tree_path);
786
787 return block_tree_path_get_key(&tree_path) == block;
788 }
789
790 /**
791 * file_check_updated - Helper function to check if transaction updated file
792 * @tr: Transaction object.
793 * @committed_block_mac: Block-mac object to check.
794 * @block_mac_out: Block-mac object to return block number and mac in
795 * for the current state of the file in @tr.
796 *
797 * Return: %true if file is updated in @tr, %false otherwise.
798 */
file_check_updated(struct transaction * tr,const struct block_mac * committed_block_mac,struct block_mac * block_mac_out)799 static bool file_check_updated(struct transaction* tr,
800 const struct block_mac* committed_block_mac,
801 struct block_mac* block_mac_out) {
802 struct block_tree_path tree_path;
803 data_block_t block = block_mac_to_block(tr, committed_block_mac);
804
805 block_tree_walk(tr, &tr->files_updated, block, false, &tree_path);
806 if (block_tree_path_get_key(&tree_path) == block) {
807 pr_read("%" PRIu64
808 ", already updated in this transaction, use new copy %" PRIu64
809 "\n",
810 block, block_tree_path_get_data(&tree_path));
811 *block_mac_out = block_tree_path_get_data_block_mac(&tree_path);
812 return true;
813 }
814
815 return false;
816 }
817
818 /**
819 * file_lookup_not_removed - Helper function to search for a file
820 * @block_mac_out: Block-mac object to return block number and mac
821 * in for the current state of the file.
822 * @committed_block_mac_out: Block-mac object to return block number and mac
823 * in for the committed state of the file.
824 * @tr: Transaction object.
825 * @tree_path: Tree path object.
826 * @file_path: File path string.
827 *
828 * Helper function to search for a file that existed before @tr was activated.
829 *
830 * Return: %true if @file_path was found in @tr->fs->files but not found in
831 * @tr->files_removed, %false otherwise.
832 */
file_lookup_not_removed(struct block_mac * block_mac_out,struct block_mac * committed_block_mac_out,struct transaction * tr,struct block_tree_path * tree_path,const char * file_path)833 static bool file_lookup_not_removed(struct block_mac* block_mac_out,
834 struct block_mac* committed_block_mac_out,
835 struct transaction* tr,
836 struct block_tree_path* tree_path,
837 const char* file_path) {
838 bool found;
839 struct block_mac block_mac;
840
841 found = file_tree_lookup(&block_mac, tr, &tr->fs->files, tree_path,
842 file_path, false);
843 if (!found || file_is_removed(tr, block_mac_to_block(tr, &block_mac))) {
844 if (found) {
845 pr_read("file %s, %" PRIu64 " in removed\n", file_path,
846 block_mac_to_block(tr, &block_mac));
847 } else {
848 pr_read("file %s not in tr->fs->files\n", file_path);
849 }
850 return false;
851 }
852 *committed_block_mac_out = block_mac;
853
854 block_tree_walk(tr, &tr->files_updated, block_mac_to_block(tr, &block_mac),
855 false, tree_path);
856 if (block_tree_path_get_key(tree_path) ==
857 block_mac_to_block(tr, &block_mac)) {
858 pr_read("file %s, %" PRIu64
859 ", already updated in this transaction, use new copy %" PRIu64
860 "\n",
861 file_path, block_mac_to_block(tr, &block_mac),
862 block_tree_path_get_data(tree_path));
863 block_mac = block_tree_path_get_data_block_mac(tree_path);
864 }
865 pr_read("file %s, %" PRIu64 ", found in tr->fs->files\n", file_path,
866 block_mac_to_block(tr, &block_mac));
867
868 /*
869 * TODO: mark file (or blocks) in_use so other writers to the same file
870 * will cause this transaction to fail. Currently conflicts are only
871 * detected after each transaction writes to the file.
872 */
873
874 *block_mac_out = block_mac;
875 return true;
876 }
877
878 /**
879 * file_find_open - Check if transaction has file open and return it
880 * @tr: Transaction object.
881 * @block_mac: Current block and mac value for open file.
882 *
883 * Return: file handle of open file if file is open in @tr, NULL otherwise.
884 */
file_find_open(struct transaction * tr,const struct block_mac * block_mac)885 static struct storage_file_handle* file_find_open(
886 struct transaction* tr,
887 const struct block_mac* block_mac) {
888 struct storage_file_handle* file;
889
890 list_for_every_entry(&tr->open_files, file, struct storage_file_handle,
891 node) {
892 if (block_mac_same_block(tr, &file->block_mac, block_mac)) {
893 return file;
894 }
895 }
896
897 return NULL;
898 }
899
900 /**
901 * file_iterate - Iterate over all files
902 * @tr: Transaction object.
903 * @start_path: First file to not iterate over.
904 * @added: %false to iterate over committed files, %true to iterate over
905 * uncommitted files added by @tr.
906 * @state: client state object containing function to call for each file.
907 * @allow_repaired: Accept a repaired file system state.
908 *
909 * Return: %FILE_OP_ERR_FS_REPAIRED if the file system has been repaired and
910 * @allow_repaired was false, %FILE_OP_ERR_FAILED if the file system is not
911 * readable or an internal error occurred, and %FILE_OP_SUCCESS otherwise.
912 */
file_iterate(struct transaction * tr,const char * start_path,bool added,struct file_iterate_state * state,bool allow_repaired)913 enum file_op_result file_iterate(struct transaction* tr,
914 const char* start_path,
915 bool added,
916 struct file_iterate_state* state,
917 bool allow_repaired) {
918 struct block_tree_path tree_path;
919 struct block_mac block_mac;
920 struct block_tree* tree = added ? &tr->files_added : &tr->fs->files;
921 bool found;
922 bool stop;
923 bool removed = false;
924
925 assert(tr->fs);
926 if (!fs_is_readable(tr->fs)) {
927 return FILE_OP_ERR_FAILED;
928 }
929 if (!allow_repaired && (tr->repaired || fs_is_repaired(tr->fs))) {
930 return FILE_OP_ERR_FS_REPAIRED;
931 }
932
933 if (start_path == NULL) {
934 block_tree_walk(tr, tree, 0, true, &tree_path);
935 } else {
936 found = file_tree_lookup(&block_mac, tr, tree, &tree_path, start_path,
937 false);
938 if (!found) {
939 return FILE_OP_ERR_FAILED;
940 }
941 block_tree_path_next(&tree_path);
942 }
943 if (tr->failed) {
944 return FILE_OP_ERR_FAILED;
945 }
946
947 while (block_tree_path_get_key(&tree_path)) {
948 block_mac = block_tree_path_get_data_block_mac(&tree_path);
949 if (!added) {
950 removed = file_is_removed(tr, block_mac_to_block(tr, &block_mac));
951 file_check_updated(tr, &block_mac, &block_mac);
952 }
953 if (tr->failed) {
954 return FILE_OP_ERR_FAILED;
955 }
956
957 stop = state->file(state, tr, &block_mac, added, removed);
958
959 if (tr->failed) {
960 return FILE_OP_ERR_FAILED;
961 }
962 if (stop) {
963 return FILE_OP_SUCCESS;
964 }
965 block_tree_path_next(&tree_path);
966 }
967 return FILE_OP_SUCCESS;
968 }
969
970 /**
971 * file_open - Open file
972 * @tr: Transaction object.
973 * @path: Path to find or create file at.
974 * @file: File handle object.
975 * @create: FILE_OPEN_NO_CREATE, FILE_OPEN_CREATE or
976 * FILE_OPEN_CREATE_EXCLUSIVE.
977 * @allow_repaired: Accept a repaired file system state. If the file system has
978 * been repaired, missing files may have previously existed and
979 * are now rolled back.
980 *
981 * Return: %FILE_OP_ERR_FS_REPAIRED if the file system has been repaired and
982 * @allow_repaired was false, &enum file_op_result.FILE_OP_SUCCESS if file was
983 * opened, or an error describing the failure to open the file.
984 */
file_open(struct transaction * tr,const char * path,struct storage_file_handle * file,enum file_create_mode create,bool allow_repaired)985 enum file_op_result file_open(struct transaction* tr,
986 const char* path,
987 struct storage_file_handle* file,
988 enum file_create_mode create,
989 bool allow_repaired) {
990 bool found;
991 struct block_mac block_mac;
992 struct block_mac committed_block_mac =
993 BLOCK_MAC_INITIAL_VALUE(committed_block_mac);
994 struct block_tree_path tree_path;
995 const struct file_entry* file_entry_ro;
996 struct obj_ref file_entry_ref = OBJ_REF_INITIAL_VALUE(file_entry_ref);
997
998 assert(tr->fs);
999 if (!allow_repaired && (tr->repaired || fs_is_repaired(tr->fs))) {
1000 return FILE_OP_ERR_FS_REPAIRED;
1001 }
1002
1003 if (!fs_is_readable(tr->fs)) {
1004 return FILE_OP_ERR_FAILED;
1005 }
1006
1007 found = file_tree_lookup(&block_mac, tr, &tr->files_added, &tree_path, path,
1008 false);
1009 if (found) {
1010 goto found;
1011 }
1012
1013 found = file_lookup_not_removed(&block_mac, &committed_block_mac, tr,
1014 &tree_path, path);
1015 if (found) {
1016 goto found;
1017 }
1018 if (tr->failed) {
1019 return FILE_OP_ERR_FAILED;
1020 }
1021
1022 if (create != FILE_OPEN_NO_CREATE) {
1023 found = file_create(&block_mac, tr, path);
1024 }
1025 if (found) {
1026 goto created;
1027 }
1028 if (tr->failed) {
1029 return FILE_OP_ERR_FAILED;
1030 } else {
1031 return FILE_OP_ERR_NOT_FOUND;
1032 }
1033
1034 found:
1035 if (create == FILE_OPEN_CREATE_EXCLUSIVE) {
1036 return FILE_OP_ERR_EXIST;
1037 }
1038 if (file_find_open(tr, &block_mac)) {
1039 pr_warn("%s already open\n", path);
1040 return FILE_OP_ERR_ALREADY_OPEN;
1041 }
1042 created:
1043 file_entry_ro = block_get(tr, &block_mac, NULL, &file_entry_ref);
1044 if (!file_entry_ro) {
1045 assert(tr->failed);
1046 pr_warn("transaction failed, abort\n");
1047 return FILE_OP_ERR_FAILED;
1048 }
1049 assert(file_entry_ro);
1050 list_add_head(&tr->open_files, &file->node);
1051 file->to_commit_block_mac = committed_block_mac;
1052 file->committed_block_mac = committed_block_mac;
1053 file->block_mac = block_mac;
1054 file->size = file_entry_ro->info.size;
1055 file->used_by_tr = false;
1056 block_put(file_entry_ro, &file_entry_ref);
1057
1058 return FILE_OP_SUCCESS;
1059 }
1060
1061 /**
1062 * file_close - Close file
1063 * @file: File handle object.
1064 *
1065 * Must be called before freeing @file after a successful file_open call.
1066 */
file_close(struct storage_file_handle * file)1067 void file_close(struct storage_file_handle* file) {
1068 list_delete(&file->node);
1069 }
1070
1071 /**
1072 * file_delete - Delete file
1073 * @tr: Transaction object.
1074 * @path: Path to find file at.
1075 * @allow_repaired: Accept a repaired file system state.
1076 *
1077 * Return: %FILE_OP_ERR_FS_REPAIRED if the file system has been repaired and
1078 * @allow_repaired was false, %FILE_OP_SUCCESS if the file existed and was
1079 * deleted, %FILE_OP_ERR_NOT_FOUND if the file did not exist, and
1080 * %FILE_OP_ERR_FAILED if some other error occurred.
1081 */
file_delete(struct transaction * tr,const char * path,bool allow_repaired)1082 enum file_op_result file_delete(struct transaction* tr,
1083 const char* path,
1084 bool allow_repaired) {
1085 bool found;
1086 struct block_mac block_mac;
1087 struct block_mac old_block_mac;
1088 bool in_files = false;
1089 const struct file_entry* file_entry;
1090 struct obj_ref file_entry_ref = OBJ_REF_INITIAL_VALUE(file_entry_ref);
1091 struct block_map block_map = BLOCK_MAP_INITIAL_VALUE(block_map);
1092 struct block_tree_path tree_path;
1093 struct storage_file_handle* open_file;
1094
1095 assert(tr->fs);
1096 if (!fs_is_readable(tr->fs)) {
1097 return FILE_OP_ERR_FAILED;
1098 }
1099 if (!allow_repaired && (tr->repaired || fs_is_repaired(tr->fs))) {
1100 return FILE_OP_ERR_FS_REPAIRED;
1101 }
1102
1103 found = file_tree_lookup(&block_mac, tr, &tr->files_added, &tree_path, path,
1104 true);
1105 if (!found) {
1106 pr_read("file %s not in tr->files_added\n", path);
1107 found = file_lookup_not_removed(&block_mac, &old_block_mac, tr,
1108 &tree_path, path);
1109 if (!found) {
1110 pr_warn("file %s not found\n", path);
1111 return tr->failed ? FILE_OP_ERR_FAILED : FILE_OP_ERR_NOT_FOUND;
1112 }
1113 in_files = true;
1114 }
1115
1116 pr_write("delete file, %s, at block %" PRIu64 "\n", path,
1117 block_mac_to_block(tr, &block_mac));
1118
1119 file_entry = block_get(tr, &block_mac, NULL, &file_entry_ref);
1120 if (!file_entry) {
1121 assert(tr->failed);
1122 pr_warn("transaction failed, abort\n");
1123 return FILE_OP_ERR_FAILED;
1124 }
1125 assert(file_entry);
1126 assert(!strcmp(file_entry->info.path, path));
1127 block_map_init(tr, &block_map, &file_entry->block_map,
1128 tr->fs->dev->block_size);
1129 if (!in_files || !block_mac_same_block(tr, &block_mac, &old_block_mac)) {
1130 block_discard_dirty(file_entry);
1131 }
1132 block_put(file_entry, &file_entry_ref);
1133 if (in_files) {
1134 if (!block_mac_same_block(tr, &block_mac, &old_block_mac)) {
1135 /* TODO: pass mac */
1136 block_tree_remove(tr, &tr->files_updated,
1137 block_mac_to_block(tr, &old_block_mac),
1138 block_mac_to_block(tr, &block_mac));
1139 if (tr->failed) {
1140 pr_warn("transaction failed, abort\n");
1141 return FILE_OP_ERR_FAILED;
1142 }
1143 }
1144 block_tree_insert_block_mac(tr, &tr->files_removed,
1145 block_mac_to_block(tr, &old_block_mac),
1146 old_block_mac);
1147 }
1148 block_free(tr, block_mac_to_block(tr, &block_mac));
1149 block_map_free(tr, &block_map);
1150
1151 open_file = file_find_open(tr, &block_mac);
1152 if (open_file) {
1153 struct block_mac clear = BLOCK_MAC_INITIAL_VALUE(clear);
1154 open_file->block_mac = clear;
1155 open_file->size = 0;
1156 }
1157 return FILE_OP_SUCCESS;
1158 }
1159
1160 /**
1161 * file_move - Move a file to a new path, optionally replacing an existing file
1162 * @tr: Transaction object.
1163 * @file: File handle object.
1164 * @dest_path: Path to move file to.
1165 * @dest_create: FILE_OPEN_NO_CREATE, FILE_OPEN_CREATE or
1166 * FILE_OPEN_CREATE_EXCLUSIVE.
1167 * @allow_repaired: Accept a repaired file system state.
1168 *
1169 * Return: %FILE_OP_SUCCESS if the operation was successful,
1170 * %FILE_OP_ERR_FS_REPAIRED if the file system has been repaired and
1171 * @allow_repaired was false, %FILE_OP_ERR_EXIST if the destination file already
1172 * exists and %FILE_OPEN_CREATE_EXCLUSIVE was requested, %FILE_OP_ERR_NOT_FOUND
1173 * if the destination file did not exist but %FILE_OPEN_NO_CREATE was required,
1174 * and %FILE_OP_ERR_FAILED if some other error occurred.
1175 */
file_move(struct transaction * tr,struct storage_file_handle * file,const char * dest_path,enum file_create_mode dest_create,bool allow_repaired)1176 enum file_op_result file_move(struct transaction* tr,
1177 struct storage_file_handle* file,
1178 const char* dest_path,
1179 enum file_create_mode dest_create,
1180 bool allow_repaired) {
1181 assert(tr->fs);
1182 struct block_mac block_mac;
1183 struct block_mac committed_block_mac;
1184 struct block_tree_path tree_path;
1185 bool dest_found;
1186
1187 if (!fs_is_readable(tr->fs)) {
1188 return FILE_OP_ERR_FAILED;
1189 }
1190 if (!allow_repaired && (tr->repaired || fs_is_repaired(tr->fs))) {
1191 return FILE_OP_ERR_FS_REPAIRED;
1192 }
1193
1194 dest_found = file_tree_lookup(&block_mac, tr, &tr->files_added, &tree_path,
1195 dest_path, false);
1196 if (!dest_found) {
1197 dest_found = file_lookup_not_removed(&block_mac, &committed_block_mac,
1198 tr, &tree_path, dest_path);
1199 }
1200 if (tr->failed) {
1201 pr_warn("transaction failed, abort\n");
1202 return FILE_OP_ERR_FAILED;
1203 }
1204
1205 if (dest_found) {
1206 if (dest_create == FILE_OPEN_CREATE_EXCLUSIVE) {
1207 return FILE_OP_ERR_EXIST;
1208 }
1209 if (block_mac_eq(tr, &file->block_mac, &block_mac)) {
1210 return FILE_OP_SUCCESS;
1211 }
1212 file_delete(tr, dest_path, allow_repaired);
1213 } else {
1214 if (dest_create == FILE_OPEN_NO_CREATE) {
1215 return FILE_OP_ERR_NOT_FOUND;
1216 }
1217 }
1218
1219 if (tr->failed) {
1220 pr_warn("transaction failed, abort\n");
1221 return FILE_OP_ERR_FAILED;
1222 }
1223 file_block_map_update(tr, NULL, dest_path, file);
1224 if (tr->failed) {
1225 pr_warn("transaction failed, abort\n");
1226 return FILE_OP_ERR_FAILED;
1227 }
1228 return FILE_OP_SUCCESS;
1229 }
1230
1231 /**
1232 * file_for_each_open - Call function for every open file in every transaction
1233 * @tr: Transaction object.
1234 * @func: Function to call.
1235 */
file_for_each_open(struct transaction * tr,void (* func)(struct transaction * tr,struct transaction * file_tr,struct storage_file_handle * file))1236 static void file_for_each_open(struct transaction* tr,
1237 void (*func)(struct transaction* tr,
1238 struct transaction* file_tr,
1239 struct storage_file_handle* file)) {
1240 struct transaction* tmp_tr;
1241 struct transaction* other_tr;
1242 struct storage_file_handle* file;
1243
1244 list_for_every_entry_safe(&tr->fs->transactions, other_tr, tmp_tr,
1245 struct transaction, node) {
1246 list_for_every_entry(&other_tr->open_files, file,
1247 struct storage_file_handle, node) {
1248 func(tr, other_tr, file);
1249 }
1250 }
1251 }
1252
1253 /**
1254 * file_restore_to_commit - Restore to_commit state to commited state
1255 * @tr: Current transaction.
1256 * @file_tr: Transaction that @file was found in.
1257 * @file: File handle object.
1258 */
file_restore_to_commit(struct transaction * tr,struct transaction * file_tr,struct storage_file_handle * file)1259 static void file_restore_to_commit(struct transaction* tr,
1260 struct transaction* file_tr,
1261 struct storage_file_handle* file) {
1262 struct block_mac* src = &file->committed_block_mac;
1263 struct block_mac* dest = &file->to_commit_block_mac;
1264 if (block_mac_same_block(tr, src, dest)) {
1265 assert(block_mac_eq(tr, src, dest));
1266 return;
1267 }
1268 pr_write("abort block %" PRIu64 "/%" PRIu64 " -> %" PRIu64 ", size %" PRIu64
1269 " -> %" PRIu64 "\n",
1270 block_mac_to_block(tr, &file->committed_block_mac),
1271 block_mac_to_block(tr, &file->block_mac),
1272 block_mac_to_block(tr, &file->to_commit_block_mac), file->size,
1273 file->to_commit_size);
1274 block_mac_copy(tr, dest, src);
1275 }
1276
1277 /**
1278 * file_apply_to_commit - Apply to_commit state
1279 * @tr: Current transaction.
1280 * @file_tr: Transaction that @file was found in.
1281 * @file: File handle object.
1282 *
1283 * Copies to_commit to commited and current state and fails conflicting
1284 * transactions.
1285 */
file_apply_to_commit(struct transaction * tr,struct transaction * file_tr,struct storage_file_handle * file)1286 static void file_apply_to_commit(struct transaction* tr,
1287 struct transaction* file_tr,
1288 struct storage_file_handle* file) {
1289 struct block_mac* src = &file->to_commit_block_mac;
1290 struct block_mac* dest = &file->committed_block_mac;
1291
1292 if (tr == file_tr) {
1293 file->used_by_tr = false;
1294 }
1295
1296 if (block_mac_same_block(tr, src, dest)) {
1297 assert(block_mac_eq(tr, src, dest));
1298 pr_write("file handle %p, unchanged file at %" PRIu64 "\n", file,
1299 block_mac_to_block(tr, &file->committed_block_mac));
1300 return;
1301 }
1302
1303 if (file_tr != tr) {
1304 if (file->used_by_tr) {
1305 pr_warn("conflict %" PRIu64 " != %" PRIu64 " || used_by_tr %d\n",
1306 block_mac_to_block(tr, &file->committed_block_mac),
1307 block_mac_to_block(tr, &file->block_mac), file->used_by_tr);
1308 assert(!file_tr->failed);
1309 transaction_fail(file_tr);
1310 }
1311 assert(block_mac_same_block(tr, dest, &file->block_mac));
1312 }
1313
1314 pr_write("file handle %p, apply block %" PRIu64 "/%" PRIu64 " -> %" PRIu64
1315 ", size %" PRIu64 " -> %" PRIu64 "\n",
1316 file, block_mac_to_block(tr, &file->committed_block_mac),
1317 block_mac_to_block(tr, &file->block_mac),
1318 block_mac_to_block(tr, &file->to_commit_block_mac), file->size,
1319 file->to_commit_size);
1320
1321 block_mac_copy(tr, dest, src);
1322 if (tr == file_tr) {
1323 assert(block_mac_eq(tr, &file->block_mac, src));
1324 assert(file->size == file->to_commit_size);
1325 } else {
1326 block_mac_copy(tr, &file->block_mac, src);
1327 file->size = file->to_commit_size;
1328 }
1329 }
1330
1331 /**
1332 * file_transaction_complete_failed - Restore open files state
1333 * @tr: Transaction object.
1334 *
1335 * Revert open file changes done by file_transaction_complete.
1336 */
file_transaction_complete_failed(struct transaction * tr)1337 void file_transaction_complete_failed(struct transaction* tr) {
1338 file_for_each_open(tr, file_restore_to_commit);
1339 }
1340
1341 /**
1342 * file_update_block_mac_tr - Update open files with committed changes
1343 * @tr: Current transaction object.
1344 * @other_tr: Transaction object to find files in.
1345 * @old_block_mac: Block and mac of committed file.
1346 * @old_block_no_mac: %true if @old_block_mac->max is invalid.
1347 * @new_block_mac: New block and mac.
1348 * @new_size: New size.
1349 *
1350 * Prepare update of open files referring to the file at @old_block_mac.
1351 */
file_update_block_mac_tr(struct transaction * tr,struct transaction * other_tr,const struct block_mac * old_block_mac,bool old_block_no_mac,const struct block_mac * new_block_mac,data_block_t new_size)1352 static void file_update_block_mac_tr(struct transaction* tr,
1353 struct transaction* other_tr,
1354 const struct block_mac* old_block_mac,
1355 bool old_block_no_mac,
1356 const struct block_mac* new_block_mac,
1357 data_block_t new_size) {
1358 struct storage_file_handle* file;
1359
1360 assert(block_mac_valid(tr, old_block_mac) || other_tr == tr);
1361 list_for_every_entry(&other_tr->open_files, file,
1362 struct storage_file_handle, node) {
1363 if (block_mac_valid(tr, old_block_mac)
1364 ? !block_mac_same_block(tr, &file->committed_block_mac,
1365 old_block_mac)
1366 : !block_mac_same_block(tr, &file->block_mac,
1367 new_block_mac)) {
1368 pr_write("file handle %p, unrelated %" PRIu64 " != %" PRIu64 "\n",
1369 file, block_mac_to_block(tr, &file->committed_block_mac),
1370 block_mac_to_block(tr, new_block_mac));
1371 continue; /*unrelated file */
1372 }
1373 assert(old_block_no_mac || !block_mac_valid(tr, old_block_mac) ||
1374 block_mac_eq(tr, &file->committed_block_mac, old_block_mac));
1375
1376 pr_write("file handle %p, stage block %" PRIu64 "/%" PRIu64
1377 " -> %" PRIu64 ", size %" PRIu64 " -> %" PRIu64 "\n",
1378 file, block_mac_to_block(tr, &file->committed_block_mac),
1379 block_mac_to_block(tr, &file->block_mac),
1380 block_mac_to_block(tr, new_block_mac), file->size, new_size);
1381
1382 block_mac_copy(tr, &file->to_commit_block_mac, new_block_mac);
1383 file->to_commit_size = new_size;
1384 }
1385 }
1386
1387 /**
1388 * file_update_block_mac_all - Update open files with committed changes
1389 * @tr: Transaction object.
1390 * @old_block_mac: Block and mac of committed file.
1391 * @old_block_no_mac: %true if @old_block_mac->max is invalid.
1392 * @new_block_mac: New block and mac.
1393 * @new_size: New size.
1394 *
1395 * Update other open files referring to the same file as @src_file with the
1396 * size, block and mac from @src_file.
1397 */
file_update_block_mac_all(struct transaction * tr,const struct block_mac * old_block_mac,bool old_block_no_mac,const struct block_mac * new_block_mac,data_block_t new_size)1398 static void file_update_block_mac_all(struct transaction* tr,
1399 const struct block_mac* old_block_mac,
1400 bool old_block_no_mac,
1401 const struct block_mac* new_block_mac,
1402 data_block_t new_size) {
1403 struct transaction* tmp_tr;
1404 struct transaction* other_tr;
1405
1406 list_for_every_entry_safe(&tr->fs->transactions, other_tr, tmp_tr,
1407 struct transaction, node) {
1408 file_update_block_mac_tr(tr, other_tr, old_block_mac, old_block_no_mac,
1409 new_block_mac, new_size);
1410 }
1411 }
1412
1413 /**
1414 * file_transaction_complete - Update files
1415 * @tr: Transaction object.
1416 * @new_files_block_mac: Object to return block and mac of updated file tree
1417 *
1418 * Create a copy of @tr->fs->files and apply all file updates from @tr to it.
1419 * Called by transaction_complete, which will update the super-block and
1420 * @tr->fs->files if the transaction succeeds.
1421 */
file_transaction_complete(struct transaction * tr,struct block_mac * new_files_block_mac)1422 void file_transaction_complete(struct transaction* tr,
1423 struct block_mac* new_files_block_mac) {
1424 bool found;
1425 const struct file_entry* file_entry_ro;
1426 struct obj_ref file_entry_ref = OBJ_REF_INITIAL_VALUE(file_entry_ref);
1427 struct block_tree_path tree_path;
1428 struct block_tree_path tmp_tree_path;
1429 struct block_mac old_file;
1430 struct block_mac file;
1431 struct block_tree new_files;
1432 data_block_t hash;
1433 const struct block_mac clear_block_mac = BLOCK_MAC_INITIAL_VALUE(clear);
1434
1435 block_tree_copy(&new_files, &tr->fs->files);
1436
1437 block_tree_walk(tr, &tr->files_updated, 0, true, &tree_path);
1438 while (true) {
1439 file = block_tree_path_get_data_block_mac(&tree_path);
1440 if (!block_mac_to_block(tr, &file)) {
1441 break;
1442 }
1443 file_entry_ro = block_get(tr, &file, NULL, &file_entry_ref);
1444 if (!file_entry_ro) {
1445 assert(tr->failed);
1446 pr_warn("transaction failed, abort\n");
1447 return;
1448 }
1449 assert(file_entry_ro);
1450 block_mac_set_block(tr, &old_file, block_tree_path_get_key(&tree_path));
1451
1452 pr_write("update file at %" PRIu64 " -> %" PRIu64 ", %s\n",
1453 block_mac_to_block(tr, &old_file),
1454 block_mac_to_block(tr, &file), file_entry_ro->info.path);
1455
1456 file_update_block_mac_all(tr, &old_file, true, &file,
1457 file_entry_ro->info.size);
1458
1459 hash = path_hash(tr, file_entry_ro->info.path);
1460
1461 block_put(file_entry_ro, &file_entry_ref);
1462
1463 if (tr->failed) {
1464 pr_warn("transaction failed, abort\n");
1465 return;
1466 }
1467
1468 block_tree_update_block_mac(tr, &new_files, hash, old_file, hash, file);
1469
1470 if (tr->failed) {
1471 pr_warn("transaction failed, abort\n");
1472 return;
1473 }
1474
1475 assert(block_mac_to_block(tr, &old_file) !=
1476 block_mac_to_block(tr, &file));
1477 block_tree_path_next(&tree_path);
1478 }
1479
1480 block_tree_walk(tr, &tr->files_removed, 0, true, &tree_path);
1481 while (true) {
1482 file = block_tree_path_get_data_block_mac(&tree_path);
1483 if (!block_mac_to_block(tr, &file)) {
1484 break;
1485 }
1486 file_entry_ro = block_get(tr, &file, NULL, &file_entry_ref);
1487 if (!file_entry_ro) {
1488 assert(tr->failed);
1489 pr_warn("transaction failed, abort\n");
1490 return;
1491 }
1492 assert(file_entry_ro);
1493
1494 pr_write("delete file at %" PRIu64 ", %s\n",
1495 block_mac_to_block(tr, &file), file_entry_ro->info.path);
1496
1497 file_update_block_mac_all(tr, &file, false, &clear_block_mac, 0);
1498
1499 found = file_tree_lookup(&old_file, tr, &new_files, &tmp_tree_path,
1500 file_entry_ro->info.path, true);
1501 block_put(file_entry_ro, &file_entry_ref);
1502
1503 if (tr->failed) {
1504 pr_warn("transaction failed, abort\n");
1505 return;
1506 }
1507
1508 assert(found);
1509 assert(block_mac_to_block(tr, &old_file) ==
1510 block_mac_to_block(tr, &file));
1511 block_tree_path_next(&tree_path);
1512 }
1513 block_tree_walk(tr, &tr->files_added, 0, true, &tree_path);
1514 while (true) {
1515 file = block_tree_path_get_data_block_mac(&tree_path);
1516 if (!block_mac_valid(tr, &file)) {
1517 break;
1518 }
1519 file_entry_ro = block_get(tr, &file, NULL, &file_entry_ref);
1520 if (!file_entry_ro) {
1521 assert(tr->failed);
1522 pr_warn("transaction failed, abort\n");
1523 return;
1524 }
1525 assert(file_entry_ro);
1526 pr_write("add file at %" PRIu64 ", %s\n", block_mac_to_block(tr, &file),
1527 file_entry_ro->info.path);
1528
1529 if (file_tree_lookup(&old_file, tr, &new_files, &tmp_tree_path,
1530 file_entry_ro->info.path, false)) {
1531 block_put(file_entry_ro, &file_entry_ref);
1532 pr_err("add file at %" PRIu64
1533 ", %s, failed, conflicts with %" PRIu64 "\n",
1534 block_mac_to_block(tr, &file), file_entry_ro->info.path,
1535 block_mac_to_block(tr, &old_file));
1536 transaction_fail(tr);
1537 return;
1538 }
1539
1540 file_update_block_mac_tr(tr, tr, &clear_block_mac, false, &file,
1541 file_entry_ro->info.size);
1542
1543 hash = path_hash(tr, file_entry_ro->info.path);
1544 block_put(file_entry_ro, &file_entry_ref);
1545
1546 if (tr->failed) {
1547 pr_warn("transaction failed, abort\n");
1548 return;
1549 }
1550
1551 block_tree_insert_block_mac(tr, &new_files, hash, file);
1552 if (tr->failed) {
1553 pr_warn("transaction failed, abort\n");
1554 return;
1555 }
1556
1557 block_tree_path_next(&tree_path);
1558 }
1559 *new_files_block_mac = new_files.root;
1560 }
1561
1562 /**
1563 * transaction_changed_file - Check if file was modified by current transaction
1564 * @tr: Transaction object.
1565 * @file: File handle.
1566 *
1567 * Return: %true if @file was modified by @tr, %false otherwise.
1568 */
transaction_changed_file(struct transaction * tr,struct storage_file_handle * file)1569 static bool transaction_changed_file(struct transaction* tr,
1570 struct storage_file_handle* file) {
1571 return !block_mac_same_block(tr, &file->committed_block_mac,
1572 &file->block_mac);
1573 }
1574
1575 /**
1576 * file_transaction_success - Update all file handles modified by transaction
1577 * @tr: Transaction object.
1578 */
file_transaction_success(struct transaction * tr)1579 void file_transaction_success(struct transaction* tr) {
1580 file_for_each_open(tr, file_apply_to_commit);
1581 }
1582
1583 /**
1584 * file_read_size - Read file entry to get file size
1585 * @tr: Transaction object.
1586 * @block_mac: Block and mac of file entry to read.
1587 * @sz: Pointer to store file size in.
1588 *
1589 * Return: %true if @sz was set, %false otherwise.
1590 */
file_read_size(struct transaction * tr,const struct block_mac * block_mac,data_block_t * sz)1591 static bool file_read_size(struct transaction* tr,
1592 const struct block_mac* block_mac,
1593 data_block_t* sz) {
1594 const struct file_entry* file_entry_ro;
1595 struct obj_ref ref = OBJ_REF_INITIAL_VALUE(ref);
1596
1597 if (!block_mac_valid(tr, block_mac)) {
1598 *sz = 0;
1599 return true;
1600 }
1601
1602 file_entry_ro = block_get_no_tr_fail(tr, block_mac, NULL, &ref);
1603 if (!file_entry_ro) {
1604 return false;
1605 }
1606 *sz = file_entry_ro->info.size;
1607 block_put(file_entry_ro, &ref);
1608 return true;
1609 }
1610
1611 /**
1612 * file_transaction_failed - Restore file handles after failed transaction
1613 * @tr: Transaction object.
1614 *
1615 * Revert all file handles modified by @tr to the state of the files before @tr
1616 * was activeted. File handles for files crreated by @tr become invalid.
1617 */
file_transaction_failed(struct transaction * tr)1618 void file_transaction_failed(struct transaction* tr) {
1619 struct storage_file_handle* file;
1620 bool success;
1621
1622 list_for_every_entry(&tr->open_files, file, struct storage_file_handle,
1623 node) {
1624 file->used_by_tr = false;
1625 if (transaction_changed_file(tr, file)) {
1626 file->block_mac = file->committed_block_mac;
1627 success = file_read_size(tr, &file->block_mac, &file->size);
1628 if (!success) {
1629 pr_warn("failed to read block %" PRIu64 ", clear file handle\n",
1630 block_mac_to_block(tr, &file->block_mac));
1631 block_mac_clear(tr, &file->block_mac);
1632 block_mac_clear(tr, &file->committed_block_mac);
1633 file->size = 0;
1634 }
1635 }
1636 }
1637 }
1638
1639 /**
1640 * file_rebuild_free_set - Remove a file's blocks from the input set
1641 * @tr: Transaction object
1642 * @new_free_set: Free set to modify
1643 * @file: File handle whose blocks should be removed from
1644 * @new_free_set
1645 *
1646 * Update @new_free_set by removing from it all blocks referenced by @file, i.e.
1647 * the file metadata block, file block map nodes, and file data blocks. Caller
1648 * must ensure that @new_free_set contained all these referenced blocks before
1649 * calling this function.
1650 */
file_rebuild_free_set(struct transaction * tr,struct block_set * new_free_set,struct storage_file_handle * file)1651 static void file_rebuild_free_set(struct transaction* tr,
1652 struct block_set* new_free_set,
1653 struct storage_file_handle* file) {
1654 struct block_map block_map;
1655 data_block_t block;
1656 struct block_tree_path path;
1657 int i;
1658
1659 block_set_remove_block(tr, new_free_set,
1660 block_mac_to_block(tr, &file->block_mac));
1661
1662 file_block_map_init(tr, &block_map, &file->block_mac);
1663 block_tree_walk(tr, &block_map.tree, 0, true, &path);
1664 while (block_tree_path_get_key(&path)) {
1665 /*
1666 * We only remove a tree node the first time we see it. Because we walk
1667 * the tree in order, the first time we see a node we will visit its
1668 * lowest child i.e. path.entry[i].index == 0. When index > 0 we have
1669 * already visited and removed all parent nodes.
1670 */
1671 for (i = path.count - 1; i >= 0 && path.entry[i].index == 0; i--) {
1672 block = block_mac_to_block(tr, &path.entry[i].block_mac);
1673 block_set_remove_block(tr, new_free_set, block);
1674 }
1675 block_set_remove_block(tr, new_free_set,
1676 block_tree_path_get_data(&path));
1677 block_tree_path_next(&path);
1678 }
1679 }
1680
1681 /**
1682 * files_rebuild_free_set - Rebuild a free set from the files tree
1683 * @tr: Transaction object
1684 * @new_free_set: Free set to modify
1685 * @files_root: Root block and mac of the files tree
1686 *
1687 * Update @new_free_set by removing from it all blocks referenced in the file
1688 * tree rooted at @files_root, i.e. all files tree nodes, file metadata blocks,
1689 * file block map nodes, and file data blocks. Caller must ensure that
1690 * @new_free_set contained all these referenced blocks before calling this
1691 * function.
1692 */
files_rebuild_free_set(struct transaction * tr,struct block_set * new_free_set,struct block_mac * files_root)1693 void files_rebuild_free_set(struct transaction* tr,
1694 struct block_set* new_free_set,
1695 struct block_mac* files_root) {
1696 struct block_tree files;
1697 data_block_t block;
1698 struct block_tree_path path;
1699 int i;
1700 struct storage_file_handle file;
1701
1702 fs_file_tree_init(tr->fs, &files);
1703 block_mac_copy(tr, &files.root, files_root);
1704 block_tree_walk(tr, &files, 0, true, &path);
1705 if (!block_tree_path_get_key(&path)) {
1706 /* No files, so we just need to track the tree root. */
1707 block_set_remove_block(tr, new_free_set,
1708 block_mac_to_block(tr, files_root));
1709 return;
1710 }
1711 while (block_tree_path_get_key(&path)) {
1712 /*
1713 * We only remove a tree node the first time we see it. Because we walk
1714 * the tree in order, the first time we see a node we will visit its
1715 * lowest child i.e. path.entry[i].index == 0. When index > 0 we have
1716 * already visited and removed all parent nodes.
1717 */
1718 for (i = path.count - 1; i >= 0 && path.entry[i].index == 0; i--) {
1719 block = block_mac_to_block(tr, &path.entry[i].block_mac);
1720 block_set_remove_block(tr, new_free_set, block);
1721 }
1722 file.block_mac = block_tree_path_get_data_block_mac(&path);
1723 file_rebuild_free_set(tr, new_free_set, &file);
1724 block_tree_path_next(&path);
1725 }
1726 }
1727