1*288bf522SAndroid Build Coastguard Worker /*
2*288bf522SAndroid Build Coastguard Worker * Copyright (C) 2015 The Android Open Source Project
3*288bf522SAndroid Build Coastguard Worker *
4*288bf522SAndroid Build Coastguard Worker * Licensed under the Apache License, Version 2.0 (the "License");
5*288bf522SAndroid Build Coastguard Worker * you may not use this file except in compliance with the License.
6*288bf522SAndroid Build Coastguard Worker * You may obtain a copy of the License at
7*288bf522SAndroid Build Coastguard Worker *
8*288bf522SAndroid Build Coastguard Worker * http://www.apache.org/licenses/LICENSE-2.0
9*288bf522SAndroid Build Coastguard Worker *
10*288bf522SAndroid Build Coastguard Worker * Unless required by applicable law or agreed to in writing, software
11*288bf522SAndroid Build Coastguard Worker * distributed under the License is distributed on an "AS IS" BASIS,
12*288bf522SAndroid Build Coastguard Worker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*288bf522SAndroid Build Coastguard Worker * See the License for the specific language governing permissions and
14*288bf522SAndroid Build Coastguard Worker * limitations under the License.
15*288bf522SAndroid Build Coastguard Worker */
16*288bf522SAndroid Build Coastguard Worker
17*288bf522SAndroid Build Coastguard Worker #include <fcntl.h>
18*288bf522SAndroid Build Coastguard Worker #include <stdlib.h>
19*288bf522SAndroid Build Coastguard Worker #include <sys/mman.h>
20*288bf522SAndroid Build Coastguard Worker
21*288bf522SAndroid Build Coastguard Worker extern "C" {
22*288bf522SAndroid Build Coastguard Worker #include <fec.h>
23*288bf522SAndroid Build Coastguard Worker }
24*288bf522SAndroid Build Coastguard Worker
25*288bf522SAndroid Build Coastguard Worker #include "fec_private.h"
26*288bf522SAndroid Build Coastguard Worker
27*288bf522SAndroid Build Coastguard Worker using rs_unique_ptr = std::unique_ptr<void, decltype(&free_rs_char)>;
28*288bf522SAndroid Build Coastguard Worker
29*288bf522SAndroid Build Coastguard Worker /* prints a hexdump of `data' using warn(...) */
dump(const char * name,uint64_t value,const uint8_t * data,size_t size)30*288bf522SAndroid Build Coastguard Worker static void dump(const char *name, uint64_t value, const uint8_t *data,
31*288bf522SAndroid Build Coastguard Worker size_t size)
32*288bf522SAndroid Build Coastguard Worker {
33*288bf522SAndroid Build Coastguard Worker const int bytes_per_line = 16;
34*288bf522SAndroid Build Coastguard Worker char hex[bytes_per_line * 3 + 1];
35*288bf522SAndroid Build Coastguard Worker char prn[bytes_per_line + 1];
36*288bf522SAndroid Build Coastguard Worker
37*288bf522SAndroid Build Coastguard Worker warn("%s (%" PRIu64 ") (%zu bytes):", name ? name : "", value, size);
38*288bf522SAndroid Build Coastguard Worker
39*288bf522SAndroid Build Coastguard Worker if (!data) {
40*288bf522SAndroid Build Coastguard Worker warn(" (null)");
41*288bf522SAndroid Build Coastguard Worker return;
42*288bf522SAndroid Build Coastguard Worker }
43*288bf522SAndroid Build Coastguard Worker
44*288bf522SAndroid Build Coastguard Worker for (size_t n = 0; n < size; n += bytes_per_line) {
45*288bf522SAndroid Build Coastguard Worker memset(hex, 0, sizeof(hex));
46*288bf522SAndroid Build Coastguard Worker memset(prn, 0, sizeof(prn));
47*288bf522SAndroid Build Coastguard Worker
48*288bf522SAndroid Build Coastguard Worker for (size_t m = 0; m < bytes_per_line; ++m) {
49*288bf522SAndroid Build Coastguard Worker if (n + m < size) {
50*288bf522SAndroid Build Coastguard Worker ptrdiff_t offset = &hex[m * 3] - hex;
51*288bf522SAndroid Build Coastguard Worker snprintf(hex + offset, sizeof(hex) - offset, "%02x ",
52*288bf522SAndroid Build Coastguard Worker data[n + m]);
53*288bf522SAndroid Build Coastguard Worker
54*288bf522SAndroid Build Coastguard Worker if (isprint(data[n + m])) {
55*288bf522SAndroid Build Coastguard Worker prn[m] = data[n + m];
56*288bf522SAndroid Build Coastguard Worker } else {
57*288bf522SAndroid Build Coastguard Worker prn[m] = '.';
58*288bf522SAndroid Build Coastguard Worker }
59*288bf522SAndroid Build Coastguard Worker } else {
60*288bf522SAndroid Build Coastguard Worker strcpy(&hex[m * 3], " ");
61*288bf522SAndroid Build Coastguard Worker }
62*288bf522SAndroid Build Coastguard Worker }
63*288bf522SAndroid Build Coastguard Worker
64*288bf522SAndroid Build Coastguard Worker warn(" %04zu %s %s", n, hex, prn);
65*288bf522SAndroid Build Coastguard Worker }
66*288bf522SAndroid Build Coastguard Worker }
67*288bf522SAndroid Build Coastguard Worker
68*288bf522SAndroid Build Coastguard Worker /* checks if `offset' is within a corrupted block */
is_erasure(fec_handle * f,uint64_t offset,const uint8_t * data)69*288bf522SAndroid Build Coastguard Worker static inline bool is_erasure(fec_handle *f, uint64_t offset,
70*288bf522SAndroid Build Coastguard Worker const uint8_t *data)
71*288bf522SAndroid Build Coastguard Worker {
72*288bf522SAndroid Build Coastguard Worker if (unlikely(offset >= f->data_size)) {
73*288bf522SAndroid Build Coastguard Worker return false;
74*288bf522SAndroid Build Coastguard Worker }
75*288bf522SAndroid Build Coastguard Worker
76*288bf522SAndroid Build Coastguard Worker /* ideally, we would like to know if a specific byte on this block has
77*288bf522SAndroid Build Coastguard Worker been corrupted, but knowing whether any of them is can be useful as
78*288bf522SAndroid Build Coastguard Worker well, because often the entire block is corrupted */
79*288bf522SAndroid Build Coastguard Worker
80*288bf522SAndroid Build Coastguard Worker uint64_t n = offset / FEC_BLOCKSIZE;
81*288bf522SAndroid Build Coastguard Worker
82*288bf522SAndroid Build Coastguard Worker return !f->hashtree().check_block_hash_with_index(n, data);
83*288bf522SAndroid Build Coastguard Worker }
84*288bf522SAndroid Build Coastguard Worker
85*288bf522SAndroid Build Coastguard Worker /* check if `offset' is within a block expected to contain zeros */
is_zero(fec_handle * f,uint64_t offset)86*288bf522SAndroid Build Coastguard Worker static inline bool is_zero(fec_handle *f, uint64_t offset)
87*288bf522SAndroid Build Coastguard Worker {
88*288bf522SAndroid Build Coastguard Worker auto hashtree = f->hashtree();
89*288bf522SAndroid Build Coastguard Worker
90*288bf522SAndroid Build Coastguard Worker if (hashtree.hash_data.empty() || unlikely(offset >= f->data_size)) {
91*288bf522SAndroid Build Coastguard Worker return false;
92*288bf522SAndroid Build Coastguard Worker }
93*288bf522SAndroid Build Coastguard Worker
94*288bf522SAndroid Build Coastguard Worker uint64_t hash_offset = (offset / FEC_BLOCKSIZE) * SHA256_DIGEST_LENGTH;
95*288bf522SAndroid Build Coastguard Worker
96*288bf522SAndroid Build Coastguard Worker if (unlikely(hash_offset >
97*288bf522SAndroid Build Coastguard Worker hashtree.hash_data.size() - SHA256_DIGEST_LENGTH)) {
98*288bf522SAndroid Build Coastguard Worker return false;
99*288bf522SAndroid Build Coastguard Worker }
100*288bf522SAndroid Build Coastguard Worker
101*288bf522SAndroid Build Coastguard Worker return !memcmp(hashtree.zero_hash.data(), &hashtree.hash_data[hash_offset],
102*288bf522SAndroid Build Coastguard Worker SHA256_DIGEST_LENGTH);
103*288bf522SAndroid Build Coastguard Worker }
104*288bf522SAndroid Build Coastguard Worker
105*288bf522SAndroid Build Coastguard Worker /* reads and decodes a single block starting from `offset', returns the number
106*288bf522SAndroid Build Coastguard Worker of bytes corrected in `errors' */
__ecc_read(fec_handle * f,void * rs,uint8_t * dest,uint64_t offset,bool use_erasures,uint8_t * ecc_data,size_t * errors)107*288bf522SAndroid Build Coastguard Worker static int __ecc_read(fec_handle *f, void *rs, uint8_t *dest, uint64_t offset,
108*288bf522SAndroid Build Coastguard Worker bool use_erasures, uint8_t *ecc_data, size_t *errors)
109*288bf522SAndroid Build Coastguard Worker {
110*288bf522SAndroid Build Coastguard Worker check(offset % FEC_BLOCKSIZE == 0);
111*288bf522SAndroid Build Coastguard Worker ecc_info *e = &f->ecc;
112*288bf522SAndroid Build Coastguard Worker
113*288bf522SAndroid Build Coastguard Worker /* reverse interleaving: calculate the RS block that includes the requested
114*288bf522SAndroid Build Coastguard Worker offset */
115*288bf522SAndroid Build Coastguard Worker uint64_t rsb = offset - (offset / (e->rounds * FEC_BLOCKSIZE)) *
116*288bf522SAndroid Build Coastguard Worker e->rounds * FEC_BLOCKSIZE;
117*288bf522SAndroid Build Coastguard Worker int data_index = -1;
118*288bf522SAndroid Build Coastguard Worker int erasures[e->rsn];
119*288bf522SAndroid Build Coastguard Worker int neras = 0;
120*288bf522SAndroid Build Coastguard Worker
121*288bf522SAndroid Build Coastguard Worker /* verity is required to check for erasures */
122*288bf522SAndroid Build Coastguard Worker check(!use_erasures || !f->hashtree().hash_data.empty());
123*288bf522SAndroid Build Coastguard Worker
124*288bf522SAndroid Build Coastguard Worker for (int i = 0; i < e->rsn; ++i) {
125*288bf522SAndroid Build Coastguard Worker uint64_t interleaved = fec_ecc_interleave(rsb * e->rsn + i, e->rsn,
126*288bf522SAndroid Build Coastguard Worker e->rounds);
127*288bf522SAndroid Build Coastguard Worker
128*288bf522SAndroid Build Coastguard Worker if (interleaved == offset) {
129*288bf522SAndroid Build Coastguard Worker data_index = i;
130*288bf522SAndroid Build Coastguard Worker }
131*288bf522SAndroid Build Coastguard Worker
132*288bf522SAndroid Build Coastguard Worker /* to improve our chances of correcting IO errors, initialize the
133*288bf522SAndroid Build Coastguard Worker buffer to zeros even if we are going to read to it later */
134*288bf522SAndroid Build Coastguard Worker uint8_t bbuf[FEC_BLOCKSIZE] = {0};
135*288bf522SAndroid Build Coastguard Worker
136*288bf522SAndroid Build Coastguard Worker if (likely(interleaved < e->start) && !is_zero(f, interleaved)) {
137*288bf522SAndroid Build Coastguard Worker /* copy raw data to reconstruct the RS block */
138*288bf522SAndroid Build Coastguard Worker if (!raw_pread(f->fd, bbuf, FEC_BLOCKSIZE, interleaved)) {
139*288bf522SAndroid Build Coastguard Worker warn("failed to read: %s", strerror(errno));
140*288bf522SAndroid Build Coastguard Worker
141*288bf522SAndroid Build Coastguard Worker /* treat errors as corruption */
142*288bf522SAndroid Build Coastguard Worker if (use_erasures && neras <= e->roots) {
143*288bf522SAndroid Build Coastguard Worker erasures[neras++] = i;
144*288bf522SAndroid Build Coastguard Worker }
145*288bf522SAndroid Build Coastguard Worker } else if (use_erasures && neras <= e->roots &&
146*288bf522SAndroid Build Coastguard Worker is_erasure(f, interleaved, bbuf)) {
147*288bf522SAndroid Build Coastguard Worker erasures[neras++] = i;
148*288bf522SAndroid Build Coastguard Worker }
149*288bf522SAndroid Build Coastguard Worker }
150*288bf522SAndroid Build Coastguard Worker
151*288bf522SAndroid Build Coastguard Worker for (int j = 0; j < FEC_BLOCKSIZE; ++j) {
152*288bf522SAndroid Build Coastguard Worker ecc_data[j * FEC_RSM + i] = bbuf[j];
153*288bf522SAndroid Build Coastguard Worker }
154*288bf522SAndroid Build Coastguard Worker }
155*288bf522SAndroid Build Coastguard Worker
156*288bf522SAndroid Build Coastguard Worker check(data_index >= 0);
157*288bf522SAndroid Build Coastguard Worker
158*288bf522SAndroid Build Coastguard Worker size_t nerrs = 0;
159*288bf522SAndroid Build Coastguard Worker uint8_t copy[FEC_RSM];
160*288bf522SAndroid Build Coastguard Worker
161*288bf522SAndroid Build Coastguard Worker for (int i = 0; i < FEC_BLOCKSIZE; ++i) {
162*288bf522SAndroid Build Coastguard Worker /* copy parity data */
163*288bf522SAndroid Build Coastguard Worker if (!raw_pread(f->fd, &ecc_data[i * FEC_RSM + e->rsn], e->roots,
164*288bf522SAndroid Build Coastguard Worker e->start + (i + rsb) * e->roots)) {
165*288bf522SAndroid Build Coastguard Worker error("failed to read ecc data: %s", strerror(errno));
166*288bf522SAndroid Build Coastguard Worker return -1;
167*288bf522SAndroid Build Coastguard Worker }
168*288bf522SAndroid Build Coastguard Worker
169*288bf522SAndroid Build Coastguard Worker /* for debugging decoding failures, because decode_rs_char can mangle
170*288bf522SAndroid Build Coastguard Worker ecc_data */
171*288bf522SAndroid Build Coastguard Worker if (unlikely(use_erasures)) {
172*288bf522SAndroid Build Coastguard Worker memcpy(copy, &ecc_data[i * FEC_RSM], FEC_RSM);
173*288bf522SAndroid Build Coastguard Worker }
174*288bf522SAndroid Build Coastguard Worker
175*288bf522SAndroid Build Coastguard Worker /* decode */
176*288bf522SAndroid Build Coastguard Worker int rc = decode_rs_char(rs, &ecc_data[i * FEC_RSM], erasures, neras);
177*288bf522SAndroid Build Coastguard Worker
178*288bf522SAndroid Build Coastguard Worker if (unlikely(rc < 0)) {
179*288bf522SAndroid Build Coastguard Worker if (use_erasures) {
180*288bf522SAndroid Build Coastguard Worker error("RS block %" PRIu64 ": decoding failed (%d erasures)",
181*288bf522SAndroid Build Coastguard Worker rsb, neras);
182*288bf522SAndroid Build Coastguard Worker dump("raw RS block", rsb, copy, FEC_RSM);
183*288bf522SAndroid Build Coastguard Worker } else if (f->hashtree().hash_data.empty()) {
184*288bf522SAndroid Build Coastguard Worker warn("RS block %" PRIu64 ": decoding failed", rsb);
185*288bf522SAndroid Build Coastguard Worker } else {
186*288bf522SAndroid Build Coastguard Worker debug("RS block %" PRIu64 ": decoding failed", rsb);
187*288bf522SAndroid Build Coastguard Worker }
188*288bf522SAndroid Build Coastguard Worker
189*288bf522SAndroid Build Coastguard Worker errno = EIO;
190*288bf522SAndroid Build Coastguard Worker return -1;
191*288bf522SAndroid Build Coastguard Worker } else if (unlikely(rc > 0)) {
192*288bf522SAndroid Build Coastguard Worker check(rc <= (use_erasures ? e->roots : e->roots / 2));
193*288bf522SAndroid Build Coastguard Worker nerrs += rc;
194*288bf522SAndroid Build Coastguard Worker }
195*288bf522SAndroid Build Coastguard Worker
196*288bf522SAndroid Build Coastguard Worker dest[i] = ecc_data[i * FEC_RSM + data_index];
197*288bf522SAndroid Build Coastguard Worker }
198*288bf522SAndroid Build Coastguard Worker
199*288bf522SAndroid Build Coastguard Worker if (nerrs) {
200*288bf522SAndroid Build Coastguard Worker warn("RS block %" PRIu64 ": corrected %zu errors", rsb, nerrs);
201*288bf522SAndroid Build Coastguard Worker *errors += nerrs;
202*288bf522SAndroid Build Coastguard Worker }
203*288bf522SAndroid Build Coastguard Worker
204*288bf522SAndroid Build Coastguard Worker return FEC_BLOCKSIZE;
205*288bf522SAndroid Build Coastguard Worker }
206*288bf522SAndroid Build Coastguard Worker
207*288bf522SAndroid Build Coastguard Worker /* initializes RS decoder and allocates memory for interleaving */
ecc_init(fec_handle * f,rs_unique_ptr & rs,std::unique_ptr<uint8_t[]> & ecc_data)208*288bf522SAndroid Build Coastguard Worker static int ecc_init(fec_handle *f, rs_unique_ptr& rs,
209*288bf522SAndroid Build Coastguard Worker std::unique_ptr<uint8_t[]>& ecc_data)
210*288bf522SAndroid Build Coastguard Worker {
211*288bf522SAndroid Build Coastguard Worker check(f);
212*288bf522SAndroid Build Coastguard Worker
213*288bf522SAndroid Build Coastguard Worker rs.reset(init_rs_char(FEC_PARAMS(f->ecc.roots)));
214*288bf522SAndroid Build Coastguard Worker
215*288bf522SAndroid Build Coastguard Worker if (unlikely(!rs)) {
216*288bf522SAndroid Build Coastguard Worker error("failed to initialize RS");
217*288bf522SAndroid Build Coastguard Worker errno = ENOMEM;
218*288bf522SAndroid Build Coastguard Worker return -1;
219*288bf522SAndroid Build Coastguard Worker }
220*288bf522SAndroid Build Coastguard Worker
221*288bf522SAndroid Build Coastguard Worker ecc_data.reset(new (std::nothrow) uint8_t[FEC_RSM * FEC_BLOCKSIZE]);
222*288bf522SAndroid Build Coastguard Worker
223*288bf522SAndroid Build Coastguard Worker if (unlikely(!ecc_data)) {
224*288bf522SAndroid Build Coastguard Worker error("failed to allocate ecc buffer");
225*288bf522SAndroid Build Coastguard Worker errno = ENOMEM;
226*288bf522SAndroid Build Coastguard Worker return -1;
227*288bf522SAndroid Build Coastguard Worker }
228*288bf522SAndroid Build Coastguard Worker
229*288bf522SAndroid Build Coastguard Worker return 0;
230*288bf522SAndroid Build Coastguard Worker }
231*288bf522SAndroid Build Coastguard Worker
232*288bf522SAndroid Build Coastguard Worker /* reads `count' bytes from `offset' and corrects possible errors without
233*288bf522SAndroid Build Coastguard Worker erasure detection, returning the number of corrected bytes in `errors' */
ecc_read(fec_handle * f,uint8_t * dest,size_t count,uint64_t offset,size_t * errors)234*288bf522SAndroid Build Coastguard Worker static ssize_t ecc_read(fec_handle *f, uint8_t *dest, size_t count,
235*288bf522SAndroid Build Coastguard Worker uint64_t offset, size_t *errors)
236*288bf522SAndroid Build Coastguard Worker {
237*288bf522SAndroid Build Coastguard Worker check(f);
238*288bf522SAndroid Build Coastguard Worker check(dest);
239*288bf522SAndroid Build Coastguard Worker check(offset < f->data_size);
240*288bf522SAndroid Build Coastguard Worker check(offset + count <= f->data_size);
241*288bf522SAndroid Build Coastguard Worker check(errors);
242*288bf522SAndroid Build Coastguard Worker
243*288bf522SAndroid Build Coastguard Worker debug("[%" PRIu64 ", %" PRIu64 ")", offset, offset + count);
244*288bf522SAndroid Build Coastguard Worker
245*288bf522SAndroid Build Coastguard Worker rs_unique_ptr rs(NULL, free_rs_char);
246*288bf522SAndroid Build Coastguard Worker std::unique_ptr<uint8_t[]> ecc_data;
247*288bf522SAndroid Build Coastguard Worker
248*288bf522SAndroid Build Coastguard Worker if (ecc_init(f, rs, ecc_data) == -1) {
249*288bf522SAndroid Build Coastguard Worker return -1;
250*288bf522SAndroid Build Coastguard Worker }
251*288bf522SAndroid Build Coastguard Worker
252*288bf522SAndroid Build Coastguard Worker uint64_t curr = offset / FEC_BLOCKSIZE;
253*288bf522SAndroid Build Coastguard Worker size_t coff = (size_t)(offset - curr * FEC_BLOCKSIZE);
254*288bf522SAndroid Build Coastguard Worker size_t left = count;
255*288bf522SAndroid Build Coastguard Worker
256*288bf522SAndroid Build Coastguard Worker uint8_t data[FEC_BLOCKSIZE];
257*288bf522SAndroid Build Coastguard Worker
258*288bf522SAndroid Build Coastguard Worker while (left > 0) {
259*288bf522SAndroid Build Coastguard Worker /* there's no erasure detection without verity metadata */
260*288bf522SAndroid Build Coastguard Worker if (__ecc_read(f, rs.get(), data, curr * FEC_BLOCKSIZE, false,
261*288bf522SAndroid Build Coastguard Worker ecc_data.get(), errors) == -1) {
262*288bf522SAndroid Build Coastguard Worker return -1;
263*288bf522SAndroid Build Coastguard Worker }
264*288bf522SAndroid Build Coastguard Worker
265*288bf522SAndroid Build Coastguard Worker size_t copy = FEC_BLOCKSIZE - coff;
266*288bf522SAndroid Build Coastguard Worker
267*288bf522SAndroid Build Coastguard Worker if (copy > left) {
268*288bf522SAndroid Build Coastguard Worker copy = left;
269*288bf522SAndroid Build Coastguard Worker }
270*288bf522SAndroid Build Coastguard Worker
271*288bf522SAndroid Build Coastguard Worker memcpy(dest, &data[coff], copy);
272*288bf522SAndroid Build Coastguard Worker
273*288bf522SAndroid Build Coastguard Worker dest += copy;
274*288bf522SAndroid Build Coastguard Worker left -= copy;
275*288bf522SAndroid Build Coastguard Worker coff = 0;
276*288bf522SAndroid Build Coastguard Worker ++curr;
277*288bf522SAndroid Build Coastguard Worker }
278*288bf522SAndroid Build Coastguard Worker
279*288bf522SAndroid Build Coastguard Worker return count;
280*288bf522SAndroid Build Coastguard Worker }
281*288bf522SAndroid Build Coastguard Worker
282*288bf522SAndroid Build Coastguard Worker /* reads `count' bytes from `offset', corrects possible errors with
283*288bf522SAndroid Build Coastguard Worker erasure detection, and verifies the integrity of read data using
284*288bf522SAndroid Build Coastguard Worker verity hash tree; returns the number of corrections in `errors' */
verity_read(fec_handle * f,uint8_t * dest,size_t count,uint64_t offset,size_t * errors)285*288bf522SAndroid Build Coastguard Worker static ssize_t verity_read(fec_handle *f, uint8_t *dest, size_t count,
286*288bf522SAndroid Build Coastguard Worker uint64_t offset, size_t *errors)
287*288bf522SAndroid Build Coastguard Worker {
288*288bf522SAndroid Build Coastguard Worker check(f);
289*288bf522SAndroid Build Coastguard Worker check(dest);
290*288bf522SAndroid Build Coastguard Worker check(offset < f->data_size);
291*288bf522SAndroid Build Coastguard Worker check(offset + count <= f->data_size);
292*288bf522SAndroid Build Coastguard Worker check(!f->hashtree().hash_data.empty());
293*288bf522SAndroid Build Coastguard Worker check(errors);
294*288bf522SAndroid Build Coastguard Worker
295*288bf522SAndroid Build Coastguard Worker debug("[%" PRIu64 ", %" PRIu64 ")", offset, offset + count);
296*288bf522SAndroid Build Coastguard Worker
297*288bf522SAndroid Build Coastguard Worker rs_unique_ptr rs(NULL, free_rs_char);
298*288bf522SAndroid Build Coastguard Worker std::unique_ptr<uint8_t[]> ecc_data;
299*288bf522SAndroid Build Coastguard Worker
300*288bf522SAndroid Build Coastguard Worker if (f->ecc.start && ecc_init(f, rs, ecc_data) == -1) {
301*288bf522SAndroid Build Coastguard Worker return -1;
302*288bf522SAndroid Build Coastguard Worker }
303*288bf522SAndroid Build Coastguard Worker
304*288bf522SAndroid Build Coastguard Worker uint64_t curr = offset / FEC_BLOCKSIZE;
305*288bf522SAndroid Build Coastguard Worker size_t coff = (size_t)(offset - curr * FEC_BLOCKSIZE);
306*288bf522SAndroid Build Coastguard Worker size_t left = count;
307*288bf522SAndroid Build Coastguard Worker uint8_t data[FEC_BLOCKSIZE];
308*288bf522SAndroid Build Coastguard Worker
309*288bf522SAndroid Build Coastguard Worker uint64_t max_hash_block =
310*288bf522SAndroid Build Coastguard Worker (f->hashtree().hash_data.size() - SHA256_DIGEST_LENGTH) /
311*288bf522SAndroid Build Coastguard Worker SHA256_DIGEST_LENGTH;
312*288bf522SAndroid Build Coastguard Worker
313*288bf522SAndroid Build Coastguard Worker while (left > 0) {
314*288bf522SAndroid Build Coastguard Worker check(curr <= max_hash_block);
315*288bf522SAndroid Build Coastguard Worker uint64_t curr_offset = curr * FEC_BLOCKSIZE;
316*288bf522SAndroid Build Coastguard Worker
317*288bf522SAndroid Build Coastguard Worker bool expect_zeros = is_zero(f, curr_offset);
318*288bf522SAndroid Build Coastguard Worker
319*288bf522SAndroid Build Coastguard Worker /* if we are in read-only mode and expect to read a zero block,
320*288bf522SAndroid Build Coastguard Worker skip reading and just return zeros */
321*288bf522SAndroid Build Coastguard Worker if ((f->mode & O_ACCMODE) == O_RDONLY && expect_zeros) {
322*288bf522SAndroid Build Coastguard Worker memset(data, 0, FEC_BLOCKSIZE);
323*288bf522SAndroid Build Coastguard Worker goto valid;
324*288bf522SAndroid Build Coastguard Worker }
325*288bf522SAndroid Build Coastguard Worker
326*288bf522SAndroid Build Coastguard Worker /* copy raw data without error correction */
327*288bf522SAndroid Build Coastguard Worker if (!raw_pread(f->fd, data, FEC_BLOCKSIZE, curr_offset)) {
328*288bf522SAndroid Build Coastguard Worker if (errno == EIO) {
329*288bf522SAndroid Build Coastguard Worker warn("I/O error encounter when reading, attempting to recover using fec");
330*288bf522SAndroid Build Coastguard Worker } else {
331*288bf522SAndroid Build Coastguard Worker error("failed to read: %s", strerror(errno));
332*288bf522SAndroid Build Coastguard Worker return -1;
333*288bf522SAndroid Build Coastguard Worker }
334*288bf522SAndroid Build Coastguard Worker }
335*288bf522SAndroid Build Coastguard Worker
336*288bf522SAndroid Build Coastguard Worker if (likely(f->hashtree().check_block_hash_with_index(curr, data))) {
337*288bf522SAndroid Build Coastguard Worker goto valid;
338*288bf522SAndroid Build Coastguard Worker }
339*288bf522SAndroid Build Coastguard Worker
340*288bf522SAndroid Build Coastguard Worker /* we know the block is supposed to contain zeros, so return zeros
341*288bf522SAndroid Build Coastguard Worker instead of trying to correct it */
342*288bf522SAndroid Build Coastguard Worker if (expect_zeros) {
343*288bf522SAndroid Build Coastguard Worker memset(data, 0, FEC_BLOCKSIZE);
344*288bf522SAndroid Build Coastguard Worker goto corrected;
345*288bf522SAndroid Build Coastguard Worker }
346*288bf522SAndroid Build Coastguard Worker
347*288bf522SAndroid Build Coastguard Worker if (!f->ecc.start) {
348*288bf522SAndroid Build Coastguard Worker /* fatal error without ecc */
349*288bf522SAndroid Build Coastguard Worker error("[%" PRIu64 ", %" PRIu64 "): corrupted block %" PRIu64,
350*288bf522SAndroid Build Coastguard Worker offset, offset + count, curr);
351*288bf522SAndroid Build Coastguard Worker return -1;
352*288bf522SAndroid Build Coastguard Worker } else {
353*288bf522SAndroid Build Coastguard Worker debug("[%" PRIu64 ", %" PRIu64 "): corrupted block %" PRIu64,
354*288bf522SAndroid Build Coastguard Worker offset, offset + count, curr);
355*288bf522SAndroid Build Coastguard Worker }
356*288bf522SAndroid Build Coastguard Worker
357*288bf522SAndroid Build Coastguard Worker /* try to correct without erasures first, because checking for
358*288bf522SAndroid Build Coastguard Worker erasure locations is slower */
359*288bf522SAndroid Build Coastguard Worker if (__ecc_read(f, rs.get(), data, curr_offset, false, ecc_data.get(),
360*288bf522SAndroid Build Coastguard Worker errors) == FEC_BLOCKSIZE &&
361*288bf522SAndroid Build Coastguard Worker f->hashtree().check_block_hash_with_index(curr, data)) {
362*288bf522SAndroid Build Coastguard Worker goto corrected;
363*288bf522SAndroid Build Coastguard Worker }
364*288bf522SAndroid Build Coastguard Worker
365*288bf522SAndroid Build Coastguard Worker /* try to correct with erasures */
366*288bf522SAndroid Build Coastguard Worker if (__ecc_read(f, rs.get(), data, curr_offset, true, ecc_data.get(),
367*288bf522SAndroid Build Coastguard Worker errors) == FEC_BLOCKSIZE &&
368*288bf522SAndroid Build Coastguard Worker f->hashtree().check_block_hash_with_index(curr, data)) {
369*288bf522SAndroid Build Coastguard Worker goto corrected;
370*288bf522SAndroid Build Coastguard Worker }
371*288bf522SAndroid Build Coastguard Worker
372*288bf522SAndroid Build Coastguard Worker error("[%" PRIu64 ", %" PRIu64 "): corrupted block %" PRIu64
373*288bf522SAndroid Build Coastguard Worker " (offset %" PRIu64 ") cannot be recovered",
374*288bf522SAndroid Build Coastguard Worker offset, offset + count, curr, curr_offset);
375*288bf522SAndroid Build Coastguard Worker dump("decoded block", curr, data, FEC_BLOCKSIZE);
376*288bf522SAndroid Build Coastguard Worker
377*288bf522SAndroid Build Coastguard Worker errno = EIO;
378*288bf522SAndroid Build Coastguard Worker return -1;
379*288bf522SAndroid Build Coastguard Worker
380*288bf522SAndroid Build Coastguard Worker corrected:
381*288bf522SAndroid Build Coastguard Worker /* update the corrected block to the file if we are in r/w mode */
382*288bf522SAndroid Build Coastguard Worker if (f->mode & O_RDWR &&
383*288bf522SAndroid Build Coastguard Worker !raw_pwrite(f->fd, data, FEC_BLOCKSIZE, curr_offset)) {
384*288bf522SAndroid Build Coastguard Worker error("failed to write: %s", strerror(errno));
385*288bf522SAndroid Build Coastguard Worker return -1;
386*288bf522SAndroid Build Coastguard Worker }
387*288bf522SAndroid Build Coastguard Worker
388*288bf522SAndroid Build Coastguard Worker valid:
389*288bf522SAndroid Build Coastguard Worker size_t copy = FEC_BLOCKSIZE - coff;
390*288bf522SAndroid Build Coastguard Worker
391*288bf522SAndroid Build Coastguard Worker if (copy > left) {
392*288bf522SAndroid Build Coastguard Worker copy = left;
393*288bf522SAndroid Build Coastguard Worker }
394*288bf522SAndroid Build Coastguard Worker
395*288bf522SAndroid Build Coastguard Worker memcpy(dest, &data[coff], copy);
396*288bf522SAndroid Build Coastguard Worker
397*288bf522SAndroid Build Coastguard Worker dest += copy;
398*288bf522SAndroid Build Coastguard Worker left -= copy;
399*288bf522SAndroid Build Coastguard Worker coff = 0;
400*288bf522SAndroid Build Coastguard Worker ++curr;
401*288bf522SAndroid Build Coastguard Worker }
402*288bf522SAndroid Build Coastguard Worker
403*288bf522SAndroid Build Coastguard Worker return count;
404*288bf522SAndroid Build Coastguard Worker }
405*288bf522SAndroid Build Coastguard Worker
406*288bf522SAndroid Build Coastguard Worker /* sets the internal file position to `offset' relative to `whence' */
fec_seek(struct fec_handle * f,int64_t offset,int whence)407*288bf522SAndroid Build Coastguard Worker int fec_seek(struct fec_handle *f, int64_t offset, int whence)
408*288bf522SAndroid Build Coastguard Worker {
409*288bf522SAndroid Build Coastguard Worker check(f);
410*288bf522SAndroid Build Coastguard Worker
411*288bf522SAndroid Build Coastguard Worker if (whence == SEEK_SET) {
412*288bf522SAndroid Build Coastguard Worker if (offset < 0) {
413*288bf522SAndroid Build Coastguard Worker errno = EOVERFLOW;
414*288bf522SAndroid Build Coastguard Worker return -1;
415*288bf522SAndroid Build Coastguard Worker }
416*288bf522SAndroid Build Coastguard Worker
417*288bf522SAndroid Build Coastguard Worker f->pos = offset;
418*288bf522SAndroid Build Coastguard Worker } else if (whence == SEEK_CUR) {
419*288bf522SAndroid Build Coastguard Worker if (offset < 0 && f->pos < (uint64_t)-offset) {
420*288bf522SAndroid Build Coastguard Worker errno = EOVERFLOW;
421*288bf522SAndroid Build Coastguard Worker return -1;
422*288bf522SAndroid Build Coastguard Worker } else if (offset > 0 && (uint64_t)offset > UINT64_MAX - f->pos) {
423*288bf522SAndroid Build Coastguard Worker errno = EOVERFLOW;
424*288bf522SAndroid Build Coastguard Worker return -1;
425*288bf522SAndroid Build Coastguard Worker }
426*288bf522SAndroid Build Coastguard Worker
427*288bf522SAndroid Build Coastguard Worker f->pos += offset;
428*288bf522SAndroid Build Coastguard Worker } else if (whence == SEEK_END) {
429*288bf522SAndroid Build Coastguard Worker if (offset >= 0) {
430*288bf522SAndroid Build Coastguard Worker errno = ENXIO;
431*288bf522SAndroid Build Coastguard Worker return -1;
432*288bf522SAndroid Build Coastguard Worker } else if ((uint64_t)-offset > f->size) {
433*288bf522SAndroid Build Coastguard Worker errno = EOVERFLOW;
434*288bf522SAndroid Build Coastguard Worker return -1;
435*288bf522SAndroid Build Coastguard Worker }
436*288bf522SAndroid Build Coastguard Worker
437*288bf522SAndroid Build Coastguard Worker f->pos = f->size + offset;
438*288bf522SAndroid Build Coastguard Worker } else {
439*288bf522SAndroid Build Coastguard Worker errno = EINVAL;
440*288bf522SAndroid Build Coastguard Worker return -1;
441*288bf522SAndroid Build Coastguard Worker }
442*288bf522SAndroid Build Coastguard Worker
443*288bf522SAndroid Build Coastguard Worker return 0;
444*288bf522SAndroid Build Coastguard Worker }
445*288bf522SAndroid Build Coastguard Worker
446*288bf522SAndroid Build Coastguard Worker /* reads up to `count' bytes starting from the internal file position using
447*288bf522SAndroid Build Coastguard Worker error correction and integrity validation, if available */
fec_read(struct fec_handle * f,void * buf,size_t count)448*288bf522SAndroid Build Coastguard Worker ssize_t fec_read(struct fec_handle *f, void *buf, size_t count)
449*288bf522SAndroid Build Coastguard Worker {
450*288bf522SAndroid Build Coastguard Worker ssize_t rc = fec_pread(f, buf, count, f->pos);
451*288bf522SAndroid Build Coastguard Worker
452*288bf522SAndroid Build Coastguard Worker if (rc > 0) {
453*288bf522SAndroid Build Coastguard Worker check(f->pos < UINT64_MAX - rc);
454*288bf522SAndroid Build Coastguard Worker f->pos += rc;
455*288bf522SAndroid Build Coastguard Worker }
456*288bf522SAndroid Build Coastguard Worker
457*288bf522SAndroid Build Coastguard Worker return rc;
458*288bf522SAndroid Build Coastguard Worker }
459*288bf522SAndroid Build Coastguard Worker
460*288bf522SAndroid Build Coastguard Worker /* for a file with size `max', returns the number of bytes we can read starting
461*288bf522SAndroid Build Coastguard Worker from `offset', up to `count' bytes */
get_max_count(uint64_t offset,size_t count,uint64_t max)462*288bf522SAndroid Build Coastguard Worker static inline size_t get_max_count(uint64_t offset, size_t count, uint64_t max)
463*288bf522SAndroid Build Coastguard Worker {
464*288bf522SAndroid Build Coastguard Worker if (offset >= max) {
465*288bf522SAndroid Build Coastguard Worker return 0;
466*288bf522SAndroid Build Coastguard Worker } else if (offset > max - count) {
467*288bf522SAndroid Build Coastguard Worker return (size_t)(max - offset);
468*288bf522SAndroid Build Coastguard Worker }
469*288bf522SAndroid Build Coastguard Worker
470*288bf522SAndroid Build Coastguard Worker return count;
471*288bf522SAndroid Build Coastguard Worker }
472*288bf522SAndroid Build Coastguard Worker
473*288bf522SAndroid Build Coastguard Worker /* reads `count' bytes from `f->fd' starting from `offset', and copies the
474*288bf522SAndroid Build Coastguard Worker data to `buf' */
raw_pread(int fd,void * buf,size_t count,uint64_t offset)475*288bf522SAndroid Build Coastguard Worker bool raw_pread(int fd, void *buf, size_t count, uint64_t offset) {
476*288bf522SAndroid Build Coastguard Worker check(buf);
477*288bf522SAndroid Build Coastguard Worker
478*288bf522SAndroid Build Coastguard Worker uint8_t *p = (uint8_t *)buf;
479*288bf522SAndroid Build Coastguard Worker size_t remaining = count;
480*288bf522SAndroid Build Coastguard Worker
481*288bf522SAndroid Build Coastguard Worker while (remaining > 0) {
482*288bf522SAndroid Build Coastguard Worker ssize_t n = TEMP_FAILURE_RETRY(pread64(fd, p, remaining, offset));
483*288bf522SAndroid Build Coastguard Worker
484*288bf522SAndroid Build Coastguard Worker if (n <= 0) {
485*288bf522SAndroid Build Coastguard Worker return false;
486*288bf522SAndroid Build Coastguard Worker }
487*288bf522SAndroid Build Coastguard Worker
488*288bf522SAndroid Build Coastguard Worker p += n;
489*288bf522SAndroid Build Coastguard Worker remaining -= n;
490*288bf522SAndroid Build Coastguard Worker offset += n;
491*288bf522SAndroid Build Coastguard Worker }
492*288bf522SAndroid Build Coastguard Worker
493*288bf522SAndroid Build Coastguard Worker return true;
494*288bf522SAndroid Build Coastguard Worker }
495*288bf522SAndroid Build Coastguard Worker
496*288bf522SAndroid Build Coastguard Worker /* writes `count' bytes from `buf' to `f->fd' to a file position `offset' */
raw_pwrite(int fd,const void * buf,size_t count,uint64_t offset)497*288bf522SAndroid Build Coastguard Worker bool raw_pwrite(int fd, const void *buf, size_t count, uint64_t offset) {
498*288bf522SAndroid Build Coastguard Worker check(buf);
499*288bf522SAndroid Build Coastguard Worker
500*288bf522SAndroid Build Coastguard Worker const uint8_t *p = (const uint8_t *)buf;
501*288bf522SAndroid Build Coastguard Worker size_t remaining = count;
502*288bf522SAndroid Build Coastguard Worker
503*288bf522SAndroid Build Coastguard Worker while (remaining > 0) {
504*288bf522SAndroid Build Coastguard Worker ssize_t n = TEMP_FAILURE_RETRY(pwrite64(fd, p, remaining, offset));
505*288bf522SAndroid Build Coastguard Worker
506*288bf522SAndroid Build Coastguard Worker if (n <= 0) {
507*288bf522SAndroid Build Coastguard Worker return false;
508*288bf522SAndroid Build Coastguard Worker }
509*288bf522SAndroid Build Coastguard Worker
510*288bf522SAndroid Build Coastguard Worker p += n;
511*288bf522SAndroid Build Coastguard Worker remaining -= n;
512*288bf522SAndroid Build Coastguard Worker offset += n;
513*288bf522SAndroid Build Coastguard Worker }
514*288bf522SAndroid Build Coastguard Worker
515*288bf522SAndroid Build Coastguard Worker return true;
516*288bf522SAndroid Build Coastguard Worker }
517*288bf522SAndroid Build Coastguard Worker
518*288bf522SAndroid Build Coastguard Worker /* reads up to `count' bytes starting from `offset' using error correction and
519*288bf522SAndroid Build Coastguard Worker integrity validation, if available */
fec_pread(struct fec_handle * f,void * buf,size_t count,uint64_t offset)520*288bf522SAndroid Build Coastguard Worker ssize_t fec_pread(struct fec_handle *f, void *buf, size_t count,
521*288bf522SAndroid Build Coastguard Worker uint64_t offset)
522*288bf522SAndroid Build Coastguard Worker {
523*288bf522SAndroid Build Coastguard Worker check(f);
524*288bf522SAndroid Build Coastguard Worker check(buf);
525*288bf522SAndroid Build Coastguard Worker
526*288bf522SAndroid Build Coastguard Worker if (unlikely(offset > UINT64_MAX - count)) {
527*288bf522SAndroid Build Coastguard Worker errno = EOVERFLOW;
528*288bf522SAndroid Build Coastguard Worker return -1;
529*288bf522SAndroid Build Coastguard Worker }
530*288bf522SAndroid Build Coastguard Worker
531*288bf522SAndroid Build Coastguard Worker if (!f->hashtree().hash_data.empty()) {
532*288bf522SAndroid Build Coastguard Worker return process(f, (uint8_t *)buf,
533*288bf522SAndroid Build Coastguard Worker get_max_count(offset, count, f->data_size), offset,
534*288bf522SAndroid Build Coastguard Worker verity_read);
535*288bf522SAndroid Build Coastguard Worker } else if (f->ecc.start) {
536*288bf522SAndroid Build Coastguard Worker check(f->ecc.start < f->size);
537*288bf522SAndroid Build Coastguard Worker
538*288bf522SAndroid Build Coastguard Worker count = get_max_count(offset, count, f->data_size);
539*288bf522SAndroid Build Coastguard Worker ssize_t rc = process(f, (uint8_t *)buf, count, offset, ecc_read);
540*288bf522SAndroid Build Coastguard Worker
541*288bf522SAndroid Build Coastguard Worker if (rc >= 0) {
542*288bf522SAndroid Build Coastguard Worker return rc;
543*288bf522SAndroid Build Coastguard Worker }
544*288bf522SAndroid Build Coastguard Worker
545*288bf522SAndroid Build Coastguard Worker /* return raw data if pure ecc read fails; due to interleaving
546*288bf522SAndroid Build Coastguard Worker the specific blocks the caller wants may still be fine */
547*288bf522SAndroid Build Coastguard Worker } else {
548*288bf522SAndroid Build Coastguard Worker count = get_max_count(offset, count, f->size);
549*288bf522SAndroid Build Coastguard Worker }
550*288bf522SAndroid Build Coastguard Worker
551*288bf522SAndroid Build Coastguard Worker if (raw_pread(f->fd, buf, count, offset)) {
552*288bf522SAndroid Build Coastguard Worker return count;
553*288bf522SAndroid Build Coastguard Worker }
554*288bf522SAndroid Build Coastguard Worker
555*288bf522SAndroid Build Coastguard Worker return -1;
556*288bf522SAndroid Build Coastguard Worker }
557