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