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 <dirent.h> 6#include <errno.h> 7#include <limits.h> 8#include <sys/stat.h> 9#include <sys/types.h> 10#include <unistd.h> 11 12#include <fbl/alloc_checker.h> 13#include <fbl/auto_call.h> 14#include <fbl/ref_ptr.h> 15#include <fbl/unique_ptr.h> 16#include <fuzz-utils/path.h> 17#include <lib/fdio/debug.h> 18#include <zircon/errors.h> 19#include <zircon/status.h> 20 21#define ZXDEBUG 0 22 23namespace fuzzing { 24 25// Public methods 26 27Path::Path() { 28 fbl::AllocChecker ac; 29 path_ = AdoptRef(new (&ac) PathBuffer()); 30 ZX_ASSERT(ac.check()); 31 Reset(); 32} 33 34Path::Path(fbl::RefPtr<PathBuffer> path) : path_(path), length_(path->buffer_.length()) {} 35 36Path::Path(const Path& other) : path_(other.path_), length_(other.length_) {} 37 38Path::~Path() {} 39 40fbl::String Path::Join(const char* relpath) const { 41 ZX_DEBUG_ASSERT(relpath); 42 43 fbl::StringBuffer<PATH_MAX> abspath; 44 abspath.Append(c_str(), length_ - 1); 45 46 // Add each path segment 47 const char* p = relpath; 48 const char* sep; 49 while (p && (sep = strchr(p, '/'))) { 50 // Skip repeated slashes 51 if (p != sep) { 52 abspath.Append('/'); 53 abspath.Append(p, sep - p); 54 } 55 p = sep + 1; 56 } 57 if (*p) { 58 abspath.Append('/'); 59 abspath.Append(p); 60 } 61 62 return fbl::move(abspath); 63} 64 65zx_status_t Path::GetSize(const char* relpath, size_t* out) const { 66 fbl::String abspath = Join(relpath); 67 struct stat buf; 68 if (stat(abspath.c_str(), &buf) != 0) { 69 xprintf("Failed to get status for '%s': %s\n", abspath.c_str(), strerror(errno)); 70 return ZX_ERR_IO; 71 } 72 *out = buf.st_size; 73 return ZX_OK; 74} 75 76fbl::unique_ptr<StringList> Path::List() const { 77 fbl::AllocChecker ac; 78 fbl::unique_ptr<StringList> list(new (&ac) StringList()); 79 ZX_ASSERT(ac.check()); 80 81 DIR* dir = opendir(c_str()); 82 if (!dir) { 83 return fbl::move(list); 84 } 85 auto close_dir = fbl::MakeAutoCall([&dir]() { closedir(dir); }); 86 87 struct dirent* ent; 88 while ((ent = readdir(dir))) { 89 if (strcmp(".", ent->d_name) != 0) { 90 list->push_back(ent->d_name); 91 } 92 } 93 return fbl::move(list); 94} 95 96zx_status_t Path::Ensure(const char* relpath) { 97 ZX_DEBUG_ASSERT(relpath); 98 zx_status_t rc; 99 100 // First check if already exists 101 fbl::String abspath = fbl::move(Join(relpath)); 102 struct stat buf; 103 if (stat(abspath.c_str(), &buf) == 0 && S_ISDIR(buf.st_mode)) { 104 return ZX_OK; 105 } 106 107 // Now recursively create the parent directories 108 const char* sep = strrchr(relpath, '/'); 109 if (sep) { 110 fbl::String prefix(relpath, sep - relpath); 111 if ((rc = Ensure(prefix.c_str())) != ZX_OK) { 112 xprintf("Failed to ensure parent directory: %s\n", zx_status_get_string(rc)); 113 return rc; 114 } 115 } 116 117 // Finally, create the last directory 118 if (mkdir(abspath.c_str(), 0777) != 0) { 119 xprintf("Failed to make directory '%s': %s.\n", abspath.c_str(), strerror(errno)); 120 return ZX_ERR_IO; 121 } 122 return ZX_OK; 123} 124 125zx_status_t Path::Push(const char* relpath) { 126 ZX_DEBUG_ASSERT(relpath); 127 if (*relpath == '\0') { 128 xprintf("Can't push empty path.\n"); 129 return ZX_ERR_INVALID_ARGS; 130 } 131 fbl::String abspath = fbl::move(Join(relpath)); 132 struct stat buf; 133 if (stat(abspath.c_str(), &buf) != 0) { 134 xprintf("Failed to get status for '%s': %s\n", abspath.c_str(), strerror(errno)); 135 return ZX_ERR_IO; 136 } 137 if (!S_ISDIR(buf.st_mode)) { 138 xprintf("Not a directory: %s\n", abspath.c_str()); 139 return ZX_ERR_NOT_DIR; 140 } 141 142 fbl::AllocChecker ac; 143 fbl::unique_ptr<Path> cloned(new (&ac) Path(path_)); 144 ZX_ASSERT(ac.check()); 145 146 cloned->parent_.swap(parent_); 147 parent_.swap(cloned); 148 path_->buffer_.Clear(); 149 path_->buffer_.Append(abspath); 150 path_->buffer_.Append('/'); 151 length_ = path_->buffer_.length(); 152 153 return ZX_OK; 154} 155 156void Path::Pop() { 157 if (!parent_) { 158 return; 159 } 160 length_ = parent_->length_; 161 path_->buffer_.Resize(length_); 162 fbl::unique_ptr<Path> parent; 163 parent.swap(parent_->parent_); 164 parent_.swap(parent); 165} 166 167zx_status_t Path::Remove(const char* relpath) { 168 ZX_DEBUG_ASSERT(relpath); 169 zx_status_t rc; 170 171 fbl::String abspath = fbl::move(Join(relpath)); 172 struct stat buf; 173 if (stat(abspath.c_str(), &buf) != 0) { 174 // Ignore missing files 175 if (errno != ENOENT) { 176 xprintf("Failed to get status for '%s': %s\n", abspath.c_str(), strerror(errno)); 177 return ZX_ERR_IO; 178 } 179 180 } else if (S_ISDIR(buf.st_mode)) { 181 // Recursively remove directories 182 if ((rc = Push(relpath)) != ZX_OK) { 183 xprintf("Failed to push subdirectory: %s\n", zx_status_get_string(rc)); 184 return rc; 185 } 186 auto pop = fbl::MakeAutoCall([this]() { Pop(); }); 187 188 auto names = List(); 189 for (const char* name = names->first(); name; name = names->next()) { 190 if ((rc = Remove(name)) != ZX_OK) { 191 xprintf("Failed to remove subdirectory: %s\n", zx_status_get_string(rc)); 192 return rc; 193 } 194 } 195 if (rmdir(c_str()) != 0) { 196 xprintf("Failed to remove directory '%s': %s\n", c_str(), strerror(errno)); 197 return ZX_ERR_IO; 198 } 199 return ZX_OK; 200 201 } else { 202 // Remove file 203 if (unlink(abspath.c_str()) != 0) { 204 xprintf("Failed to unlink '%s': %s\n", abspath.c_str(), strerror(errno)); 205 return ZX_ERR_IO; 206 } 207 } 208 209 return ZX_OK; 210} 211 212zx_status_t Path::Rename(const char* old_relpath, const char* new_relpath) { 213 fbl::String old_abspath = fbl::move(Join(old_relpath)); 214 fbl::String new_abspath = fbl::move(Join(new_relpath)); 215 if (rename(old_abspath.c_str(), new_abspath.c_str()) != 0) { 216 xprintf("Failed to rename '%s' to '%s': %s.\n", old_abspath.c_str(), new_abspath.c_str(), 217 strerror(errno)); 218 return ZX_ERR_IO; 219 } 220 return ZX_OK; 221} 222 223void Path::Reset() { 224 parent_.reset(); 225 path_->buffer_.Clear(); 226 path_->buffer_.Append("/"); 227 length_ = path_->buffer_.length(); 228} 229 230} // namespace fuzzing 231