1// Copyright 2018 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 "fixture.h" 6 7#include <dirent.h> 8#include <errno.h> 9#include <fcntl.h> 10#include <inttypes.h> 11#include <limits.h> 12#include <stdarg.h> 13#include <stdint.h> 14#include <string.h> 15#include <sys/stat.h> 16#include <sys/types.h> 17#include <unistd.h> 18 19#include <fbl/string.h> 20#include <fbl/string_buffer.h> 21#include <fbl/string_printf.h> 22#include <fbl/unique_fd.h> 23#include <unittest/unittest.h> 24#include <zircon/syscalls.h> 25 26namespace fuzzing { 27namespace testing { 28 29// Public methods 30 31Fixture::~Fixture() { 32 Reset(); 33} 34 35fbl::String Fixture::path(const char* fmt, ...) const { 36 va_list ap; 37 va_start(ap, fmt); 38 fbl::String result = path(fmt, ap); 39 va_end(ap); 40 return result; 41} 42 43fbl::String Fixture::path(const char* fmt, va_list ap) const { 44 if (!fmt) { 45 return root_; 46 } 47 48 fbl::StringBuffer<PATH_MAX> buffer; 49 buffer.AppendVPrintf(fmt, ap); 50 fbl::String relpath = buffer.ToString(); 51 if (relpath[0] == '/') { 52 return relpath; 53 } 54 55 buffer.Clear(); 56 buffer.Append(root_); 57 buffer.Append(relpath); 58 59 return buffer.ToString(); 60} 61 62// Protected methods 63 64Fixture::Fixture() {} 65 66bool Fixture::Create() { 67 BEGIN_HELPER; 68 Reset(); 69 70 uint64_t randnum; 71 zx_cprng_draw(&randnum, sizeof(randnum)); 72 root_ = fbl::StringPrintf("/tmp/path-unit-test-%" PRIu64 "/", randnum); 73 ASSERT_TRUE(CreateDirectory(nullptr)); 74 75 END_HELPER; 76} 77 78bool Fixture::CreateFile(const char* pathname, const char* contents) { 79 BEGIN_HELPER; 80 ASSERT_NONNULL(pathname); 81 82 fbl::String local = path(pathname); 83 pathname = local.c_str(); 84 85 const char* sep = strrchr(pathname, '/'); 86 fbl::String basename(pathname, sep - pathname); 87 ASSERT_TRUE(CreateDirectory(basename.c_str())); 88 89 fbl::unique_fd fd(open(pathname, O_RDWR | O_CREAT, 0777)); 90 ASSERT_TRUE(!!fd); 91 if (contents) { 92 ASSERT_GE(write(fd.get(), contents, strlen(contents) + 1), 0); 93 } 94 95 END_HELPER; 96} 97 98bool Fixture::CreateDirectory(const char* pathname) { 99 BEGIN_HELPER; 100 101 fbl::String local = path(pathname); 102 pathname = local.c_str(); 103 104 struct stat buf; 105 if (stat(pathname, &buf) == 0) { 106 ASSERT_TRUE(S_ISDIR(buf.st_mode)); 107 } else { 108 ASSERT_EQ(errno, ENOENT); 109 // Trim trailing slashes 110 size_t len = strlen(pathname); 111 while (len > 0 && pathname[len - 1] == '/') { 112 --len; 113 } 114 local.Set(pathname, len); 115 pathname = local.c_str(); 116 117 // Find last segment 118 const char* sep = strrchr(pathname, '/'); 119 ASSERT_NONNULL(sep); 120 fbl::String basename(pathname, sep - pathname); 121 122 ASSERT_TRUE(CreateDirectory(basename.c_str())); 123 ASSERT_GE(mkdir(pathname, 0777), 0); 124 } 125 126 END_HELPER; 127} 128 129bool Fixture::RemoveDirectory(const char* pathname) { 130 BEGIN_HELPER; 131 ASSERT_NONNULL(pathname); 132 133 char buffer[PATH_MAX]; 134 DIR* dir = opendir(pathname); 135 if (dir) { 136 struct dirent* ent; 137 while ((ent = readdir(dir))) { 138 if (strcmp(".", ent->d_name) == 0) { 139 continue; 140 } 141 snprintf(buffer, sizeof(buffer), "%s/%s", pathname, ent->d_name); 142 if (ent->d_type == DT_DIR) { 143 EXPECT_TRUE(RemoveDirectory(buffer)); 144 } else { 145 EXPECT_GE(unlink(buffer), 0); 146 } 147 } 148 closedir(dir); 149 EXPECT_GE(rmdir(pathname), 0); 150 } 151 152 END_HELPER; 153} 154 155void Fixture::Reset() { 156 if (!root_.empty()) { 157 RemoveDirectory(root_.c_str()); 158 } 159 root_.clear(); 160 unsafe_.clear(); 161} 162 163} // namespace testing 164} // namespace fuzzing 165