1// Copyright 2017 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 <dirent.h>
6#include <errno.h>
7#include <fcntl.h>
8#include <inttypes.h>
9#include <stddef.h>
10#include <stdint.h>
11#include <string.h>
12#include <sys/stat.h>
13#include <sys/types.h>
14#include <threads.h>
15#include <unistd.h>
16
17#include <fbl/algorithm.h>
18#include <fbl/auto_call.h>
19#include <fbl/auto_lock.h>
20#include <fbl/unique_fd.h>
21#include <lib/fdio/debug.h>
22#include <lib/fdio/watcher.h>
23#include <fs-management/fvm.h>
24#include <fs-management/mount.h>
25#include <fs-management/ramdisk.h>
26#include <fvm/fvm.h>
27#include <lib/zx/time.h>
28#include <unittest/unittest.h>
29#include <zircon/assert.h>
30#include <zircon/types.h>
31#include <zxcrypt/volume.h>
32
33#include "test-device.h"
34
35#define ZXDEBUG 0
36
37namespace zxcrypt {
38namespace testing {
39namespace {
40
41// No test step should take longer than this
42const zx::duration kTimeout = zx::sec(3);
43
44// FVM driver library
45const char* kFvmDriver = "/boot/driver/fvm.so";
46
47// Takes a given |result|, e.g. from an ioctl, and translates into a zx_status_t.
48zx_status_t ToStatus(ssize_t result) {
49    return result < 0 ? static_cast<zx_status_t>(result) : ZX_OK;
50}
51
52// Helper function to build error messages
53char* Error(const char* fmt, ...) {
54    static char err[256];
55    va_list ap;
56    va_start(ap, fmt);
57    vsnprintf(err, sizeof(err), fmt, ap);
58    va_end(ap);
59    return err;
60}
61
62// Waits for the given |path| to be opened, opens it, and returns the file descriptor via |out|.
63bool WaitAndOpen(char* path, fbl::unique_fd* out) {
64    BEGIN_HELPER;
65
66    ASSERT_EQ(wait_for_device(path, ZX_SEC(3)), ZX_OK,
67              Error("failed while waiting to bind %s", path));
68    fbl::unique_fd fd(open(path, O_RDWR));
69    ASSERT_TRUE(fd, Error("failed to open %s", path));
70    if (out) {
71        out->swap(fd);
72    }
73
74    END_HELPER;
75}
76
77} // namespace
78
79TestDevice::TestDevice()
80    : block_count_(0), block_size_(0), client_(nullptr), tid_(0), need_join_(false), wake_after_(0),
81      wake_deadline_(0) {
82    memset(ramdisk_path_, 0, sizeof(ramdisk_path_));
83    memset(fvm_part_path_, 0, sizeof(fvm_part_path_));
84    memset(&req_, 0, sizeof(req_));
85}
86
87TestDevice::~TestDevice() {
88    Disconnect();
89    ramdisk_.reset();
90    DestroyRamdisk();
91    if (need_join_) {
92        int res;
93        thrd_join(tid_, &res);
94    }
95}
96
97bool TestDevice::Create(size_t device_size, size_t block_size, bool fvm) {
98    BEGIN_HELPER;
99
100    ASSERT_LT(device_size, SSIZE_MAX);
101    if (fvm) {
102        ASSERT_TRUE(CreateFvmPart(device_size, block_size));
103    } else {
104        ASSERT_TRUE(CreateRamdisk(device_size, block_size));
105    }
106
107// TODO(aarongreen): See ZX-1130. The code below should be enabled when that bug is fixed.
108#if 0
109    crypto::digest::Algorithm digest;
110    switch (version) {
111    case Volume::kAES256_XTS_SHA256:
112        digest = crypto::digest::kSHA256;
113        break;
114    default:
115        digest = crypto::digest::kUninitialized;
116        break;
117    }
118
119    size_t digest_len;
120    key_.Reset();
121    if ((rc = crypto::digest::GetDigestLen(digest, &digest_len)) != ZX_OK ||
122        (rc = key_.Randomize(digest_len)) != ZX_OK) {
123        return rc;
124    }
125#else
126    uint8_t *buf;
127    ASSERT_OK(key_.Allocate(kZx1130KeyLen, &buf));
128    memset(buf, 0, key_.len());
129#endif
130
131    END_HELPER;
132}
133
134bool TestDevice::Bind(Volume::Version version, bool fvm) {
135    BEGIN_HELPER;
136    ASSERT_TRUE(Create(kDeviceSize, kBlockSize, fvm));
137    ASSERT_OK(Volume::Create(parent(), key_));
138    ASSERT_TRUE(Connect());
139    END_HELPER;
140}
141
142bool TestDevice::Rebind() {
143    BEGIN_HELPER;
144    Disconnect();
145
146    ASSERT_OK(ToStatus(ioctl_block_rr_part(ramdisk_.get())));
147    zxcrypt_.reset();
148    fvm_part_.reset();
149
150    ASSERT_TRUE(WaitAndOpen(ramdisk_path_, &ramdisk_), Error("failed to open %s", ramdisk_path_));
151    if (strlen(fvm_part_path_) != 0) {
152        ASSERT_TRUE(WaitAndOpen(fvm_part_path_, &fvm_part_),
153                    Error("failed to open %s", fvm_part_path_));
154    }
155
156    ASSERT_TRUE(Connect());
157    END_HELPER;
158}
159
160bool TestDevice::SleepUntil(uint64_t num, bool deferred) {
161    BEGIN_HELPER;
162    fbl::AutoLock lock(&lock_);
163    ASSERT_EQ(wake_after_, 0);
164    ASSERT_NE(num, 0);
165    wake_after_ = num;
166    wake_deadline_ = zx::deadline_after(kTimeout);
167    ASSERT_EQ(thrd_create(&tid_, TestDevice::WakeThread, this), thrd_success);
168    need_join_ = true;
169    if (deferred) {
170        uint32_t flags = RAMDISK_FLAG_RESUME_ON_WAKE;
171        ASSERT_OK(ToStatus(ioctl_ramdisk_set_flags(ramdisk_.get(), &flags)));
172    }
173    uint64_t sleep_after = 0;
174    ASSERT_OK(ToStatus(ioctl_ramdisk_sleep_after(ramdisk_.get(), &sleep_after)));
175    END_HELPER;
176}
177
178bool TestDevice::WakeUp() {
179    BEGIN_HELPER;
180    if (need_join_) {
181        fbl::AutoLock lock(&lock_);
182        ASSERT_NE(wake_after_, 0);
183        int res;
184        ASSERT_EQ(thrd_join(tid_, &res), thrd_success);
185        need_join_ = false;
186        wake_after_ = 0;
187        EXPECT_EQ(res, 0);
188    }
189    END_HELPER;
190}
191
192int TestDevice::WakeThread(void* arg) {
193    TestDevice* device = static_cast<TestDevice*>(arg);
194    fbl::AutoLock lock(&device->lock_);
195
196    // Always send a wake-up call; even if we failed to go to sleep.
197    auto cleanup = fbl::MakeAutoCall([&] { ioctl_ramdisk_wake_up(device->ramdisk_.get()); });
198
199    // Loop until timeout, |wake_after_| txns received, or error getting counts
200    ramdisk_blk_counts_t counts;
201    ssize_t res;
202    do {
203        zx::nanosleep(zx::deadline_after(zx::msec(100)));
204        if (device->wake_deadline_ < zx::clock::get_monotonic()) {
205            printf("Received %lu of %lu transactions before timing out.\n", counts.received,
206                   device->wake_after_);
207            return ZX_ERR_TIMED_OUT;
208        }
209        if ((res = ioctl_ramdisk_get_blk_counts(device->ramdisk_.get(), &counts)) < 0) {
210            return static_cast<zx_status_t>(res);
211        }
212    } while (counts.received < device->wake_after_);
213    return ZX_OK;
214}
215
216bool TestDevice::ReadFd(zx_off_t off, size_t len) {
217    BEGIN_HELPER;
218    ASSERT_OK(ToStatus(lseek(off)));
219    ASSERT_OK(ToStatus(read(off, len)));
220    ASSERT_EQ(memcmp(as_read_.get() + off, to_write_.get() + off, len), 0);
221    END_HELPER;
222}
223
224bool TestDevice::WriteFd(zx_off_t off, size_t len) {
225    BEGIN_HELPER;
226    ASSERT_OK(ToStatus(lseek(off)));
227    ASSERT_OK(ToStatus(write(off, len)));
228    END_HELPER;
229}
230
231bool TestDevice::ReadVmo(zx_off_t off, size_t len) {
232    BEGIN_HELPER;
233    ASSERT_OK(block_fifo_txn(BLOCKIO_READ, off, len));
234    off *= block_size_;
235    len *= block_size_;
236    ASSERT_OK(vmo_read(off, len));
237    ASSERT_EQ(memcmp(as_read_.get() + off, to_write_.get() + off, len), 0);
238    END_HELPER;
239}
240
241bool TestDevice::WriteVmo(zx_off_t off, size_t len) {
242    BEGIN_HELPER;
243    ASSERT_OK(vmo_write(off * block_size_, len * block_size_));
244    ASSERT_OK(block_fifo_txn(BLOCKIO_WRITE, off, len));
245    END_HELPER;
246}
247
248bool TestDevice::Corrupt(zx_off_t offset) {
249    BEGIN_HELPER;
250    uint8_t block[block_size_];
251    zx_off_t block_off = offset % block_size_;
252    offset -= block_off;
253
254    ASSERT_OK(ToStatus(::lseek(ramdisk_.get(), offset, SEEK_SET)));
255    ASSERT_OK(ToStatus(::read(ramdisk_.get(), block, block_size_)));
256
257    int bit = rand() % 8;
258    uint8_t flip = static_cast<uint8_t>(1U << bit);
259    block[block_off] ^= flip;
260
261    ASSERT_OK(ToStatus(::lseek(ramdisk_.get(), offset, SEEK_SET)));
262    ASSERT_OK(ToStatus(::write(ramdisk_.get(), block, block_size_)));
263    END_HELPER;
264}
265
266// Private methods
267
268bool TestDevice::CreateRamdisk(size_t device_size, size_t block_size) {
269    BEGIN_HELPER;
270
271    fbl::AllocChecker ac;
272    size_t count = fbl::round_up(device_size, block_size) / block_size;
273    to_write_.reset(new (&ac) uint8_t[device_size]);
274    ASSERT_TRUE(ac.check());
275    for (size_t i = 0; i < device_size; ++i) {
276        to_write_[i] = static_cast<uint8_t>(rand());
277    }
278
279    as_read_.reset(new (&ac) uint8_t[device_size]);
280    ASSERT_TRUE(ac.check());
281    memset(as_read_.get(), 0, block_size);
282
283    ASSERT_EQ(create_ramdisk(block_size, count, ramdisk_path_), 0);
284    ramdisk_.reset(open(ramdisk_path_, O_RDWR));
285    ASSERT_TRUE(ramdisk_, Error("failed to open %s", ramdisk_path_));
286
287    block_size_ = block_size;
288    block_count_ = count;
289
290    END_HELPER;
291}
292
293void TestDevice::DestroyRamdisk() {
294    if (strlen(ramdisk_path_) != 0) {
295        destroy_ramdisk(ramdisk_path_);
296        ramdisk_path_[0] = '\0';
297    }
298}
299
300// Creates a ramdisk, formats it, and binds to it.
301bool TestDevice::CreateFvmPart(size_t device_size, size_t block_size) {
302    BEGIN_HELPER;
303
304    // Calculate total size of data + metadata.
305    device_size = fbl::round_up(device_size, FVM_BLOCK_SIZE);
306    size_t old_meta = fvm::MetadataSize(device_size, FVM_BLOCK_SIZE);
307    size_t new_meta = fvm::MetadataSize(old_meta + device_size, FVM_BLOCK_SIZE);
308    while (old_meta != new_meta) {
309        old_meta = new_meta;
310        new_meta = fvm::MetadataSize(old_meta + device_size, FVM_BLOCK_SIZE);
311    }
312    ASSERT_TRUE(CreateRamdisk(device_size + (new_meta * 2), block_size));
313
314    // Format the ramdisk as FVM and bind to it
315    ASSERT_OK(fvm_init(ramdisk_.get(), FVM_BLOCK_SIZE));
316    ASSERT_OK(ToStatus(ioctl_device_bind(ramdisk_.get(), kFvmDriver, strlen(kFvmDriver))));
317
318    char path[PATH_MAX];
319    fbl::unique_fd fvm_fd;
320    snprintf(path, sizeof(path), "%s/fvm", ramdisk_path_);
321    ASSERT_TRUE(WaitAndOpen(path, &fvm_fd));
322
323    // Allocate a FVM partition with the last slice unallocated.
324    alloc_req_t req;
325    memset(&req, 0, sizeof(alloc_req_t));
326    req.slice_count = (kDeviceSize / FVM_BLOCK_SIZE) - 1;
327    memcpy(req.type, zxcrypt_magic, sizeof(zxcrypt_magic));
328    for (uint8_t i = 0; i < GUID_LEN; ++i) {
329        req.guid[i] = i;
330    }
331    snprintf(req.name, NAME_LEN, "data");
332    fvm_part_.reset(fvm_allocate_partition(fvm_fd.get(), &req));
333    ASSERT_TRUE(fvm_part_);
334
335    // Save the topological path for rebinding
336    ASSERT_OK(ToStatus(
337        ioctl_device_get_topo_path(fvm_part_.get(), fvm_part_path_, sizeof(fvm_part_path_))));
338
339    END_HELPER;
340}
341
342bool TestDevice::Connect() {
343    BEGIN_HELPER;
344    ZX_DEBUG_ASSERT(!zxcrypt_);
345
346    ASSERT_OK(Volume::Unlock(parent(), key_, 0, &volume_));
347    ASSERT_OK(volume_->Open(kTimeout, &zxcrypt_));
348
349    block_info_t blk;
350    ASSERT_OK(ToStatus(ioctl_block_get_info(zxcrypt_.get(), &blk)));
351    block_size_ = blk.block_size;
352    block_count_ = blk.block_count;
353
354    zx_handle_t fifo;
355    ASSERT_OK(ToStatus(ioctl_block_get_fifos(zxcrypt_.get(), &fifo)));
356    req_.group = 0;
357    ASSERT_OK(block_fifo_create_client(fifo, &client_));
358
359    // Create the vmo and get a transferable handle to give to the block server
360    ASSERT_OK(zx::vmo::create(size(), 0, &vmo_));
361    zx_handle_t xfer;
362    ASSERT_OK(zx_handle_duplicate(vmo_.get(), ZX_RIGHT_SAME_RIGHTS, &xfer));
363    ASSERT_OK(ToStatus(ioctl_block_attach_vmo(zxcrypt_.get(), &xfer, &req_.vmoid)));
364
365    END_HELPER;
366}
367
368void TestDevice::Disconnect() {
369    if (client_) {
370        memset(&req_, 0, sizeof(req_));
371        block_fifo_release_client(client_);
372        client_ = nullptr;
373    }
374    zxcrypt_.reset();
375    volume_.reset();
376    block_size_ = 0;
377    block_count_ = 0;
378    vmo_.reset();
379}
380
381} // namespace testing
382} // namespace zxcrypt
383