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