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