1 // Copyright 2019 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include <lib/inspect/block.h>
6 #include <lib/inspect/snapshot.h>
7 
8 namespace inspect {
9 
10 using internal::Block;
11 
Snapshot(fbl::Array<uint8_t> buffer)12 Snapshot::Snapshot(fbl::Array<uint8_t> buffer) : buffer_(std::move(buffer)) {}
13 
Create(zx::vmo vmo,Snapshot * out_snapshot)14 zx_status_t Snapshot::Create(zx::vmo vmo, Snapshot* out_snapshot) {
15     return Snapshot::Create(std::move(vmo), kDefaultOptions, out_snapshot);
16 }
17 
Create(zx::vmo vmo,Options options,Snapshot * out_snapshot)18 zx_status_t Snapshot::Create(zx::vmo vmo, Options options, Snapshot* out_snapshot) {
19     return Snapshot::Create(std::move(vmo), std::move(options), nullptr, out_snapshot);
20 }
21 
Create(zx::vmo vmo,Options options,ReadObserver read_observer,Snapshot * out_snapshot)22 zx_status_t Snapshot::Create(zx::vmo vmo, Options options, ReadObserver read_observer,
23                              Snapshot* out_snapshot) {
24     size_t tries_left = options.read_attempts;
25 
26     zx_status_t status;
27     fbl::Array<uint8_t> buffer;
28 
29     while (tries_left-- > 0) {
30         size_t size;
31         status = vmo.get_size(&size);
32         if (status != ZX_OK) {
33             return status;
34         }
35         if (size < sizeof(internal::Block)) {
36             return ZX_ERR_OUT_OF_RANGE;
37         }
38         if (buffer.size() != size) {
39             buffer.reset(new uint8_t[size], size);
40         }
41 
42         status = Snapshot::Read(vmo, sizeof(internal::Block), buffer.begin());
43         if (status != ZX_OK) {
44             return status;
45         }
46         if (read_observer) {
47             read_observer(buffer.begin(), sizeof(internal::Block));
48         }
49 
50         uint64_t generation;
51         status = Snapshot::ParseHeader(buffer.begin(), &generation);
52         if (status != ZX_OK) {
53             return status;
54         }
55 
56         if (!options.skip_consistency_check && generation % 2 != 0) {
57             continue;
58         }
59 
60         status = Snapshot::Read(vmo, size, buffer.begin());
61         if (status != ZX_OK) {
62             return status;
63         }
64         if (read_observer) {
65             read_observer(buffer.begin(), sizeof(size));
66         }
67 
68         uint64_t new_generation;
69         status = Snapshot::ParseHeader(buffer.begin(), &new_generation);
70         if (status != ZX_OK) {
71           return status;
72         }
73         if (!options.skip_consistency_check && generation != new_generation) {
74             continue;
75         }
76 
77         size_t new_size;
78         if (vmo.get_size(&new_size) != ZX_OK) {
79             return ZX_ERR_INTERNAL;
80         }
81         if (new_size != size) {
82             continue;
83         }
84 
85         *out_snapshot = Snapshot(std::move(buffer));
86 
87         return ZX_OK;
88     }
89 
90     return ZX_ERR_INTERNAL;
91 }
92 
Read(zx::vmo & vmo,size_t size,uint8_t * buffer)93 zx_status_t Snapshot::Read(zx::vmo& vmo, size_t size, uint8_t* buffer) {
94     memset(buffer, 0, size);
95     return vmo.read(buffer, 0, size);
96 }
97 
ParseHeader(uint8_t * buffer,uint64_t * out_generation_count)98 zx_status_t Snapshot::ParseHeader(uint8_t* buffer, uint64_t* out_generation_count) {
99     Block* block = reinterpret_cast<Block*>(buffer);
100     if (memcmp(&block->header_data[4], kMagicNumber, 4) != 0) {
101         return ZX_ERR_INTERNAL;
102     }
103     *out_generation_count = block->payload.u64;
104     return ZX_OK;
105 }
106 
GetBlock(internal::BlockIndex index) const107 internal::Block* Snapshot::GetBlock(internal::BlockIndex index) const {
108     if (index >= IndexForOffset(buffer_.size())) {
109         return nullptr;
110     }
111     return reinterpret_cast<internal::Block*>(buffer_.begin() + index * kMinOrderSize);
112 }
113 
114 } // namespace inspect
115