| OLD | NEW |
| 1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 #include "platform/globals.h" | 5 #include "platform/globals.h" |
| 6 #if defined(HOST_OS_FUCHSIA) | 6 #if defined(HOST_OS_FUCHSIA) |
| 7 | 7 |
| 8 #include "bin/directory.h" | 8 #include "bin/directory.h" |
| 9 | 9 |
| 10 #include <dirent.h> // NOLINT | 10 #include <dirent.h> // NOLINT |
| 11 #include <errno.h> // NOLINT | 11 #include <errno.h> // NOLINT |
| 12 #include <stdlib.h> // NOLINT | 12 #include <fcntl.h> // NOLINT |
| 13 #include <string.h> // NOLINT | 13 #include <mxio/namespace.h> // NOLINT |
| 14 #include <sys/param.h> // NOLINT | 14 #include <stdlib.h> // NOLINT |
| 15 #include <sys/stat.h> // NOLINT | 15 #include <string.h> // NOLINT |
| 16 #include <unistd.h> // NOLINT | 16 #include <sys/param.h> // NOLINT |
| 17 #include <sys/stat.h> // NOLINT |
| 18 #include <unistd.h> // NOLINT |
| 17 | 19 |
| 20 #include "bin/crypto.h" |
| 18 #include "bin/dartutils.h" | 21 #include "bin/dartutils.h" |
| 22 #include "bin/fdutils.h" |
| 19 #include "bin/file.h" | 23 #include "bin/file.h" |
| 24 #include "bin/namespace.h" |
| 20 #include "bin/platform.h" | 25 #include "bin/platform.h" |
| 21 #include "platform/signal_blocker.h" | 26 #include "platform/signal_blocker.h" |
| 22 | 27 |
| 23 namespace dart { | 28 namespace dart { |
| 24 namespace bin { | 29 namespace bin { |
| 25 | 30 |
| 26 PathBuffer::PathBuffer() : length_(0) { | 31 PathBuffer::PathBuffer() : length_(0) { |
| 27 data_ = calloc(PATH_MAX + 1, sizeof(char)); // NOLINT | 32 data_ = calloc(PATH_MAX + 1, sizeof(char)); // NOLINT |
| 28 } | 33 } |
| 29 | 34 |
| (...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 79 dev_t dev; | 84 dev_t dev; |
| 80 ino64_t ino; | 85 ino64_t ino; |
| 81 LinkList* next; | 86 LinkList* next; |
| 82 }; | 87 }; |
| 83 | 88 |
| 84 ListType DirectoryListingEntry::Next(DirectoryListing* listing) { | 89 ListType DirectoryListingEntry::Next(DirectoryListing* listing) { |
| 85 if (done_) { | 90 if (done_) { |
| 86 return kListDone; | 91 return kListDone; |
| 87 } | 92 } |
| 88 | 93 |
| 89 if (lister_ == 0) { | 94 if (fd_ == -1) { |
| 90 lister_ = | 95 ASSERT(lister_ == 0); |
| 91 reinterpret_cast<intptr_t>(opendir(listing->path_buffer().AsString())); | 96 NamespaceScope ns(listing->namespc(), listing->path_buffer().AsString()); |
| 92 if (lister_ == 0) { | 97 const int listingfd = |
| 93 perror("opendir failed: "); | 98 TEMP_FAILURE_RETRY(openat64(ns.fd(), ns.path(), O_DIRECTORY)); |
| 99 if (listingfd < 0) { |
| 94 done_ = true; | 100 done_ = true; |
| 95 return kListError; | 101 return kListError; |
| 96 } | 102 } |
| 103 fd_ = listingfd; |
| 104 } |
| 105 |
| 106 if (lister_ == 0) { |
| 107 do { |
| 108 lister_ = reinterpret_cast<intptr_t>(fdopendir(fd_)); |
| 109 } while ((lister_ == 0) && (errno == EINTR)); |
| 110 if (lister_ == 0) { |
| 111 done_ = true; |
| 112 return kListError; |
| 113 } |
| 97 if (parent_ != NULL) { | 114 if (parent_ != NULL) { |
| 98 if (!listing->path_buffer().Add(File::PathSeparator())) { | 115 if (!listing->path_buffer().Add(File::PathSeparator())) { |
| 99 return kListError; | 116 return kListError; |
| 100 } | 117 } |
| 101 } | 118 } |
| 102 path_length_ = listing->path_buffer().length(); | 119 path_length_ = listing->path_buffer().length(); |
| 103 } | 120 } |
| 104 // Reset. | 121 // Reset. |
| 105 listing->path_buffer().Reset(path_length_); | 122 listing->path_buffer().Reset(path_length_); |
| 106 ResetLink(); | 123 ResetLink(); |
| 107 | 124 |
| 108 // Iterate the directory and post the directories and files to the | 125 // Iterate the directory and post the directories and files to the |
| 109 // ports. | 126 // ports. |
| 110 errno = 0; | 127 errno = 0; |
| 111 dirent* entry = readdir(reinterpret_cast<DIR*>(lister_)); | 128 dirent* entry = readdir(reinterpret_cast<DIR*>(lister_)); |
| 112 if (entry != NULL) { | 129 if (entry != NULL) { |
| 113 if (!listing->path_buffer().Add(entry->d_name)) { | 130 if (!listing->path_buffer().Add(entry->d_name)) { |
| 114 done_ = true; | 131 done_ = true; |
| 115 return kListError; | 132 return kListError; |
| 116 } | 133 } |
| 117 // TODO(MG-450): When entry->d_type is filled out correctly, we can avoid | 134 switch (entry->d_type) { |
| 118 // this call to stat(). | 135 case DT_DIR: |
| 119 struct stat64 entry_info; | |
| 120 int stat_success; | |
| 121 stat_success = NO_RETRY_EXPECTED( | |
| 122 lstat64(listing->path_buffer().AsString(), &entry_info)); | |
| 123 if (stat_success == -1) { | |
| 124 perror("lstat64 failed: "); | |
| 125 return kListError; | |
| 126 } | |
| 127 if (listing->follow_links() && S_ISLNK(entry_info.st_mode)) { | |
| 128 // Check to see if we are in a loop created by a symbolic link. | |
| 129 LinkList current_link = {entry_info.st_dev, entry_info.st_ino, link_}; | |
| 130 LinkList* previous = link_; | |
| 131 while (previous != NULL) { | |
| 132 if ((previous->dev == current_link.dev) && | |
| 133 (previous->ino == current_link.ino)) { | |
| 134 // Report the looping link as a link, rather than following it. | |
| 135 return kListLink; | |
| 136 } | |
| 137 previous = previous->next; | |
| 138 } | |
| 139 stat_success = NO_RETRY_EXPECTED( | |
| 140 stat64(listing->path_buffer().AsString(), &entry_info)); | |
| 141 if (stat_success == -1) { | |
| 142 perror("lstat64 failed"); | |
| 143 // Report a broken link as a link, even if follow_links is true. | |
| 144 return kListLink; | |
| 145 } | |
| 146 if (S_ISDIR(entry_info.st_mode)) { | |
| 147 // Recurse into the subdirectory with current_link added to the | |
| 148 // linked list of seen file system links. | |
| 149 link_ = new LinkList(current_link); | |
| 150 if ((strcmp(entry->d_name, ".") == 0) || | 136 if ((strcmp(entry->d_name, ".") == 0) || |
| 151 (strcmp(entry->d_name, "..") == 0)) { | 137 (strcmp(entry->d_name, "..") == 0)) { |
| 152 return Next(listing); | 138 return Next(listing); |
| 153 } | 139 } |
| 154 return kListDirectory; | 140 return kListDirectory; |
| 141 case DT_BLK: |
| 142 case DT_CHR: |
| 143 case DT_FIFO: |
| 144 case DT_SOCK: |
| 145 case DT_REG: |
| 146 return kListFile; |
| 147 case DT_LNK: |
| 148 if (!listing->follow_links()) { |
| 149 return kListLink; |
| 150 } |
| 151 // Else fall through to next case. |
| 152 // Fall through. |
| 153 case DT_UNKNOWN: { |
| 154 // On some file systems the entry type is not determined by |
| 155 // readdir. For those and for links we use stat to determine |
| 156 // the actual entry type. Notice that stat returns the type of |
| 157 // the file pointed to. |
| 158 NamespaceScope ns(listing->namespc(), |
| 159 listing->path_buffer().AsString()); |
| 160 struct stat64 entry_info; |
| 161 int stat_success; |
| 162 stat_success = TEMP_FAILURE_RETRY( |
| 163 fstatat64(ns.fd(), ns.path(), &entry_info, AT_SYMLINK_NOFOLLOW)); |
| 164 if (stat_success == -1) { |
| 165 return kListError; |
| 166 } |
| 167 if (listing->follow_links() && S_ISLNK(entry_info.st_mode)) { |
| 168 // Check to see if we are in a loop created by a symbolic link. |
| 169 LinkList current_link = {entry_info.st_dev, entry_info.st_ino, link_}; |
| 170 LinkList* previous = link_; |
| 171 while (previous != NULL) { |
| 172 if ((previous->dev == current_link.dev) && |
| 173 (previous->ino == current_link.ino)) { |
| 174 // Report the looping link as a link, rather than following it. |
| 175 return kListLink; |
| 176 } |
| 177 previous = previous->next; |
| 178 } |
| 179 stat_success = |
| 180 TEMP_FAILURE_RETRY(fstatat64(ns.fd(), ns.path(), &entry_info, 0)); |
| 181 if (stat_success == -1) { |
| 182 // Report a broken link as a link, even if follow_links is true. |
| 183 return kListLink; |
| 184 } |
| 185 if (S_ISDIR(entry_info.st_mode)) { |
| 186 // Recurse into the subdirectory with current_link added to the |
| 187 // linked list of seen file system links. |
| 188 link_ = new LinkList(current_link); |
| 189 if ((strcmp(entry->d_name, ".") == 0) || |
| 190 (strcmp(entry->d_name, "..") == 0)) { |
| 191 return Next(listing); |
| 192 } |
| 193 return kListDirectory; |
| 194 } |
| 195 } |
| 196 if (S_ISDIR(entry_info.st_mode)) { |
| 197 if ((strcmp(entry->d_name, ".") == 0) || |
| 198 (strcmp(entry->d_name, "..") == 0)) { |
| 199 return Next(listing); |
| 200 } |
| 201 return kListDirectory; |
| 202 } else if (S_ISREG(entry_info.st_mode) || S_ISCHR(entry_info.st_mode) || |
| 203 S_ISBLK(entry_info.st_mode) || |
| 204 S_ISFIFO(entry_info.st_mode) || |
| 205 S_ISSOCK(entry_info.st_mode)) { |
| 206 return kListFile; |
| 207 } else if (S_ISLNK(entry_info.st_mode)) { |
| 208 return kListLink; |
| 209 } else { |
| 210 FATAL1("Unexpected st_mode: %d\n", entry_info.st_mode); |
| 211 return kListError; |
| 212 } |
| 155 } | 213 } |
| 156 } | 214 |
| 157 if (S_ISDIR(entry_info.st_mode)) { | 215 default: |
| 158 if ((strcmp(entry->d_name, ".") == 0) || | 216 // We should have covered all the bases. If not, let's get an error. |
| 159 (strcmp(entry->d_name, "..") == 0)) { | 217 FATAL1("Unexpected d_type: %d\n", entry->d_type); |
| 160 return Next(listing); | 218 return kListError; |
| 161 } | |
| 162 return kListDirectory; | |
| 163 } else if (S_ISREG(entry_info.st_mode) || S_ISCHR(entry_info.st_mode) || | |
| 164 S_ISBLK(entry_info.st_mode) || S_ISFIFO(entry_info.st_mode) || | |
| 165 S_ISSOCK(entry_info.st_mode)) { | |
| 166 return kListFile; | |
| 167 } else if (S_ISLNK(entry_info.st_mode)) { | |
| 168 return kListLink; | |
| 169 } else { | |
| 170 FATAL1("Unexpected st_mode: %d\n", entry_info.st_mode); | |
| 171 return kListError; | |
| 172 } | 219 } |
| 173 } | 220 } |
| 174 done_ = true; | 221 done_ = true; |
| 175 | 222 |
| 176 if (errno != 0) { | 223 if (errno != 0) { |
| 177 return kListError; | 224 return kListError; |
| 178 } | 225 } |
| 179 | 226 |
| 180 return kListDone; | 227 return kListDone; |
| 181 } | 228 } |
| 182 | 229 |
| 183 DirectoryListingEntry::~DirectoryListingEntry() { | 230 DirectoryListingEntry::~DirectoryListingEntry() { |
| 184 ResetLink(); | 231 ResetLink(); |
| 185 if (lister_ != 0) { | 232 if (lister_ != 0) { |
| 233 // This also closes fd_. |
| 186 VOID_NO_RETRY_EXPECTED(closedir(reinterpret_cast<DIR*>(lister_))); | 234 VOID_NO_RETRY_EXPECTED(closedir(reinterpret_cast<DIR*>(lister_))); |
| 187 } | 235 } |
| 188 } | 236 } |
| 189 | 237 |
| 190 void DirectoryListingEntry::ResetLink() { | 238 void DirectoryListingEntry::ResetLink() { |
| 191 if ((link_ != NULL) && ((parent_ == NULL) || (parent_->link_ != link_))) { | 239 if ((link_ != NULL) && ((parent_ == NULL) || (parent_->link_ != link_))) { |
| 192 delete link_; | 240 delete link_; |
| 193 link_ = NULL; | 241 link_ = NULL; |
| 194 } | 242 } |
| 195 if (parent_ != NULL) { | 243 if (parent_ != NULL) { |
| 196 link_ = parent_->link_; | 244 link_ = parent_->link_; |
| 197 } | 245 } |
| 198 } | 246 } |
| 199 | 247 |
| 200 Directory::ExistsResult Directory::Exists(const char* dir_name) { | 248 Directory::ExistsResult Directory::Exists(Namespace* namespc, |
| 201 struct stat entry_info; | 249 const char* dir_name) { |
| 202 int success = NO_RETRY_EXPECTED(stat(dir_name, &entry_info)); | 250 NamespaceScope ns(namespc, dir_name); |
| 251 struct stat64 entry_info; |
| 252 const int success = |
| 253 TEMP_FAILURE_RETRY(fstatat64(ns.fd(), ns.path(), &entry_info, 0)); |
| 203 if (success == 0) { | 254 if (success == 0) { |
| 204 if (S_ISDIR(entry_info.st_mode)) { | 255 if (S_ISDIR(entry_info.st_mode)) { |
| 205 return EXISTS; | 256 return EXISTS; |
| 206 } else { | 257 } else { |
| 207 // An OSError may be constructed based on the return value of this | 258 // An OSError may be constructed based on the return value of this |
| 208 // function, so set errno to something that makes sense. | 259 // function, so set errno to something that makes sense. |
| 209 errno = ENOTDIR; | 260 errno = ENOTDIR; |
| 210 return DOES_NOT_EXIST; | 261 return DOES_NOT_EXIST; |
| 211 } | 262 } |
| 212 } else { | 263 } else { |
| 213 if ((errno == EACCES) || (errno == EBADF) || (errno == EFAULT) || | 264 if ((errno == EACCES) || (errno == EBADF) || (errno == EFAULT) || |
| 214 (errno == ENOMEM) || (errno == EOVERFLOW)) { | 265 (errno == ENOMEM) || (errno == EOVERFLOW)) { |
| 215 // Search permissions denied for one of the directories in the | 266 // Search permissions denied for one of the directories in the |
| 216 // path or a low level error occured. We do not know if the | 267 // path or a low level error occured. We do not know if the |
| 217 // directory exists. | 268 // directory exists. |
| 218 return UNKNOWN; | 269 return UNKNOWN; |
| 219 } | 270 } |
| 220 ASSERT((errno == ELOOP) || (errno == ENAMETOOLONG) || (errno == ENOENT) || | 271 ASSERT((errno == ELOOP) || (errno == ENAMETOOLONG) || (errno == ENOENT) || |
| 221 (errno == ENOTDIR)); | 272 (errno == ENOTDIR)); |
| 222 return DOES_NOT_EXIST; | 273 return DOES_NOT_EXIST; |
| 223 } | 274 } |
| 224 } | 275 } |
| 225 | 276 |
| 226 char* Directory::CurrentNoScope() { | 277 char* Directory::CurrentNoScope() { |
| 227 return getcwd(NULL, 0); | 278 return getcwd(NULL, 0); |
| 228 } | 279 } |
| 229 | 280 |
| 230 const char* Directory::Current() { | 281 bool Directory::Create(Namespace* namespc, const char* dir_name) { |
| 231 char buffer[PATH_MAX]; | 282 NamespaceScope ns(namespc, dir_name); |
| 232 if (getcwd(buffer, PATH_MAX) == NULL) { | |
| 233 return NULL; | |
| 234 } | |
| 235 return DartUtils::ScopedCopyCString(buffer); | |
| 236 } | |
| 237 | |
| 238 bool Directory::SetCurrent(const char* path) { | |
| 239 return (NO_RETRY_EXPECTED(chdir(path)) == 0); | |
| 240 } | |
| 241 | |
| 242 bool Directory::Create(const char* dir_name) { | |
| 243 // Create the directory with the permissions specified by the | 283 // Create the directory with the permissions specified by the |
| 244 // process umask. | 284 // process umask. |
| 245 int result = NO_RETRY_EXPECTED(mkdir(dir_name, 0777)); | 285 const int result = NO_RETRY_EXPECTED(mkdirat(ns.fd(), ns.path(), 0777)); |
| 246 // If the directory already exists, treat it as a success. | 286 // If the directory already exists, treat it as a success. |
| 247 if ((result == -1) && (errno == EEXIST)) { | 287 if ((result == -1) && (errno == EEXIST)) { |
| 248 return (Exists(dir_name) == EXISTS); | 288 return (Exists(namespc, dir_name) == EXISTS); |
| 249 } | 289 } |
| 250 return (result == 0); | 290 return (result == 0); |
| 251 } | 291 } |
| 252 | 292 |
| 253 const char* Directory::SystemTemp() { | 293 const char* Directory::SystemTemp(Namespace* namespc) { |
| 254 PathBuffer path; | 294 PathBuffer path; |
| 255 const char* temp_dir = getenv("TMPDIR"); | 295 const char* temp_dir = getenv("TMPDIR"); |
| 256 if (temp_dir == NULL) { | 296 if (temp_dir == NULL) { |
| 257 temp_dir = getenv("TMP"); | 297 temp_dir = getenv("TMP"); |
| 258 } | 298 } |
| 259 if (temp_dir == NULL) { | 299 if (temp_dir == NULL) { |
| 260 temp_dir = "/tmp"; | 300 temp_dir = "/tmp"; |
| 261 } | 301 } |
| 262 if (!path.Add(temp_dir)) { | 302 NamespaceScope ns(namespc, temp_dir); |
| 303 if (!path.Add(ns.path())) { |
| 263 return NULL; | 304 return NULL; |
| 264 } | 305 } |
| 265 | 306 |
| 266 // Remove any trailing slash. | 307 // Remove any trailing slash. |
| 267 char* result = path.AsString(); | 308 char* result = path.AsString(); |
| 268 int length = strlen(result); | 309 int length = strlen(result); |
| 269 if ((length > 1) && (result[length - 1] == '/')) { | 310 if ((length > 1) && (result[length - 1] == '/')) { |
| 270 result[length - 1] = '\0'; | 311 result[length - 1] = '\0'; |
| 271 } | 312 } |
| 272 return path.AsScopedString(); | 313 return path.AsScopedString(); |
| 273 } | 314 } |
| 274 | 315 |
| 275 const char* Directory::CreateTemp(const char* prefix) { | 316 // Returns a new, unused directory name, adding characters to the end |
| 276 // Returns a new, unused directory name, adding characters to the end | 317 // of prefix. Creates the directory with the permissions specified |
| 277 // of prefix. Creates the directory with the permissions specified | 318 // by the process umask. |
| 278 // by the process umask. | 319 // The return value is Dart_ScopeAllocated. |
| 279 // The return value is Dart_ScopeAllocated. | 320 const char* Directory::CreateTemp(Namespace* namespc, const char* prefix) { |
| 280 PathBuffer path; | 321 PathBuffer path; |
| 322 const int firstchar = 'A'; |
| 323 const int numchars = 'Z' - 'A' + 1; |
| 324 uint8_t random_bytes[7]; |
| 325 |
| 326 // mkdtemp doesn't have an "at" variant, so we have to simulate it. |
| 281 if (!path.Add(prefix)) { | 327 if (!path.Add(prefix)) { |
| 282 return NULL; | 328 return NULL; |
| 283 } | 329 } |
| 284 if (!path.Add("XXXXXX")) { | 330 intptr_t prefix_length = path.length(); |
| 285 // Pattern has overflowed. | 331 while (true) { |
| 286 return NULL; | 332 Crypto::GetRandomBytes(6, random_bytes); |
| 333 for (intptr_t i = 0; i < 6; i++) { |
| 334 random_bytes[i] = (random_bytes[i] % numchars) + firstchar; |
| 335 } |
| 336 random_bytes[6] = '\0'; |
| 337 if (!path.Add(reinterpret_cast<char*>(random_bytes))) { |
| 338 return NULL; |
| 339 } |
| 340 NamespaceScope ns(namespc, path.AsString()); |
| 341 const int result = NO_RETRY_EXPECTED(mkdirat(ns.fd(), ns.path(), 0777)); |
| 342 if (result == 0) { |
| 343 return path.AsScopedString(); |
| 344 } else if (errno == EEXIST) { |
| 345 path.Reset(prefix_length); |
| 346 } else { |
| 347 return NULL; |
| 348 } |
| 287 } | 349 } |
| 288 char* result = mkdtemp(path.AsString()); | |
| 289 if (result == NULL) { | |
| 290 return NULL; | |
| 291 } | |
| 292 return path.AsScopedString(); | |
| 293 } | 350 } |
| 294 | 351 |
| 295 static bool DeleteRecursively(PathBuffer* path); | 352 static bool DeleteRecursively(int dirfd, PathBuffer* path); |
| 296 | 353 |
| 297 static bool DeleteFile(char* file_name, PathBuffer* path) { | 354 static bool DeleteFile(int dirfd, char* file_name, PathBuffer* path) { |
| 298 return path->Add(file_name) && | 355 return path->Add(file_name) && |
| 299 (NO_RETRY_EXPECTED(unlink(path->AsString())) == 0); | 356 (NO_RETRY_EXPECTED(unlinkat(dirfd, path->AsString(), 0)) == 0); |
| 300 } | 357 } |
| 301 | 358 |
| 302 static bool DeleteDir(char* dir_name, PathBuffer* path) { | 359 static bool DeleteDir(int dirfd, char* dir_name, PathBuffer* path) { |
| 303 if ((strcmp(dir_name, ".") == 0) || (strcmp(dir_name, "..") == 0)) { | 360 if ((strcmp(dir_name, ".") == 0) || (strcmp(dir_name, "..") == 0)) { |
| 304 return true; | 361 return true; |
| 305 } | 362 } |
| 306 return path->Add(dir_name) && DeleteRecursively(path); | 363 return path->Add(dir_name) && DeleteRecursively(dirfd, path); |
| 307 } | 364 } |
| 308 | 365 |
| 309 static bool DeleteRecursively(PathBuffer* path) { | 366 static bool DeleteRecursively(int dirfd, PathBuffer* path) { |
| 310 // Do not recurse into links for deletion. Instead delete the link. | 367 // Do not recurse into links for deletion. Instead delete the link. |
| 311 // If it's a file, delete it. | 368 // If it's a file, delete it. |
| 312 struct stat64 st; | 369 struct stat64 st; |
| 313 if (NO_RETRY_EXPECTED(lstat64(path->AsString(), &st)) == -1) { | 370 if (TEMP_FAILURE_RETRY( |
| 371 fstatat64(dirfd, path->AsString(), &st, AT_SYMLINK_NOFOLLOW)) == -1) { |
| 314 return false; | 372 return false; |
| 315 } else if (!S_ISDIR(st.st_mode)) { | 373 } else if (!S_ISDIR(st.st_mode)) { |
| 316 return NO_RETRY_EXPECTED(unlink(path->AsString())) == 0; | 374 return (NO_RETRY_EXPECTED(unlinkat(dirfd, path->AsString(), 0)) == 0); |
| 317 } | 375 } |
| 318 | 376 |
| 319 if (!path->Add(File::PathSeparator())) { | 377 if (!path->Add(File::PathSeparator())) { |
| 320 return false; | 378 return false; |
| 321 } | 379 } |
| 322 | 380 |
| 323 // Not a link. Attempt to open as a directory and recurse into the | 381 // Not a link. Attempt to open as a directory and recurse into the |
| 324 // directory. | 382 // directory. |
| 325 DIR* dir_pointer = opendir(path->AsString()); | 383 const int fd = |
| 384 TEMP_FAILURE_RETRY(openat64(dirfd, path->AsString(), O_DIRECTORY)); |
| 385 if (fd < 0) { |
| 386 return false; |
| 387 } |
| 388 DIR* dir_pointer; |
| 389 do { |
| 390 dir_pointer = fdopendir(fd); |
| 391 } while ((dir_pointer == NULL) && (errno == EINTR)); |
| 326 if (dir_pointer == NULL) { | 392 if (dir_pointer == NULL) { |
| 393 FDUtils::SaveErrorAndClose(fd); |
| 327 return false; | 394 return false; |
| 328 } | 395 } |
| 329 | 396 |
| 330 // Iterate the directory and delete all files and directories. | 397 // Iterate the directory and delete all files and directories. |
| 331 int path_length = path->length(); | 398 int path_length = path->length(); |
| 332 while (true) { | 399 while (true) { |
| 333 // In case `readdir()` returns `NULL` we distinguish between end-of-stream | 400 // In case `readdir()` returns `NULL` we distinguish between end-of-stream |
| 334 // and error by looking if `errno` was updated. | 401 // and error by looking if `errno` was updated. |
| 335 errno = 0; | 402 errno = 0; |
| 336 // In glibc 2.24+, readdir_r is deprecated. | 403 // In glibc 2.24+, readdir_r is deprecated. |
| 337 // According to the man page for readdir: | 404 // According to the man page for readdir: |
| 338 // "readdir(3) is not required to be thread-safe. However, in modern | 405 // "readdir(3) is not required to be thread-safe. However, in modern |
| 339 // implementations (including the glibc implementation), concurrent calls to | 406 // implementations (including the glibc implementation), concurrent calls to |
| 340 // readdir(3) that specify different directory streams are thread-safe." | 407 // readdir(3) that specify different directory streams are thread-safe." |
| 341 dirent* entry = readdir(dir_pointer); | 408 dirent* entry = readdir(dir_pointer); |
| 342 if (entry == NULL) { | 409 if (entry == NULL) { |
| 343 // Failed to read next directory entry. | 410 // Failed to read next directory entry. |
| 344 if (errno != 0) { | 411 if (errno != 0) { |
| 345 break; | 412 break; |
| 346 } | 413 } |
| 347 // End of directory. | 414 // End of directory. |
| 348 return (NO_RETRY_EXPECTED(closedir(dir_pointer)) == 0) && | 415 int status = NO_RETRY_EXPECTED(closedir(dir_pointer)); |
| 349 (NO_RETRY_EXPECTED(remove(path->AsString())) == 0); | 416 FDUtils::SaveErrorAndClose(fd); |
| 417 if (status != 0) { |
| 418 return false; |
| 419 } |
| 420 status = |
| 421 NO_RETRY_EXPECTED(unlinkat(dirfd, path->AsString(), AT_REMOVEDIR)); |
| 422 return status == 0; |
| 350 } | 423 } |
| 351 bool ok = false; | 424 bool ok = false; |
| 352 if (!path->Add(entry->d_name)) { | 425 switch (entry->d_type) { |
| 353 break; | 426 case DT_DIR: |
| 354 } | 427 ok = DeleteDir(dirfd, entry->d_name, path); |
| 355 // TODO(MG-450): When entry->d_type is filled out correctly, we can avoid | 428 break; |
| 356 // this call to stat(). | 429 case DT_BLK: |
| 357 struct stat64 entry_info; | 430 case DT_CHR: |
| 358 if (NO_RETRY_EXPECTED(lstat64(path->AsString(), &entry_info)) == -1) { | 431 case DT_FIFO: |
| 359 break; | 432 case DT_SOCK: |
| 360 } | 433 case DT_REG: |
| 361 path->Reset(path_length); | 434 case DT_LNK: |
| 362 if (S_ISDIR(entry_info.st_mode)) { | 435 // Treat all links as files. This will delete the link which |
| 363 ok = DeleteDir(entry->d_name, path); | 436 // is what we want no matter if the link target is a file or a |
| 364 } else { | 437 // directory. |
| 365 // Treat links as files. This will delete the link which is | 438 ok = DeleteFile(dirfd, entry->d_name, path); |
| 366 // what we want no matter if the link target is a file or a | 439 break; |
| 367 // directory. | 440 case DT_UNKNOWN: { |
| 368 ok = DeleteFile(entry->d_name, path); | 441 if (!path->Add(entry->d_name)) { |
| 442 break; |
| 443 } |
| 444 // On some file systems the entry type is not determined by |
| 445 // readdir. For those we use lstat to determine the entry |
| 446 // type. |
| 447 struct stat64 entry_info; |
| 448 if (TEMP_FAILURE_RETRY(fstatat64(dirfd, path->AsString(), &entry_info, |
| 449 AT_SYMLINK_NOFOLLOW)) == -1) { |
| 450 break; |
| 451 } |
| 452 path->Reset(path_length); |
| 453 if (S_ISDIR(entry_info.st_mode)) { |
| 454 ok = DeleteDir(dirfd, entry->d_name, path); |
| 455 } else { |
| 456 // Treat links as files. This will delete the link which is |
| 457 // what we want no matter if the link target is a file or a |
| 458 // directory. |
| 459 ok = DeleteFile(dirfd, entry->d_name, path); |
| 460 } |
| 461 break; |
| 462 } |
| 463 default: |
| 464 // We should have covered all the bases. If not, let's get an error. |
| 465 FATAL1("Unexpected d_type: %d\n", entry->d_type); |
| 466 break; |
| 369 } | 467 } |
| 370 if (!ok) { | 468 if (!ok) { |
| 371 break; | 469 break; |
| 372 } | 470 } |
| 373 path->Reset(path_length); | 471 path->Reset(path_length); |
| 374 } | 472 } |
| 375 // Only happens if there was an error. | 473 // Only happens if an error. |
| 376 ASSERT(errno != 0); | 474 ASSERT(errno != 0); |
| 377 int err = errno; | 475 int err = errno; |
| 378 VOID_NO_RETRY_EXPECTED(closedir(dir_pointer)); | 476 VOID_NO_RETRY_EXPECTED(closedir(dir_pointer)); |
| 477 FDUtils::SaveErrorAndClose(fd); |
| 379 errno = err; | 478 errno = err; |
| 380 return false; | 479 return false; |
| 381 } | 480 } |
| 382 | 481 |
| 383 bool Directory::Delete(const char* dir_name, bool recursive) { | 482 bool Directory::Delete(Namespace* namespc, |
| 483 const char* dir_name, |
| 484 bool recursive) { |
| 485 NamespaceScope ns(namespc, dir_name); |
| 384 if (!recursive) { | 486 if (!recursive) { |
| 385 if ((File::GetType(dir_name, false) == File::kIsLink) && | 487 if ((File::GetType(namespc, dir_name, false) == File::kIsLink) && |
| 386 (File::GetType(dir_name, true) == File::kIsDirectory)) { | 488 (File::GetType(namespc, dir_name, true) == File::kIsDirectory)) { |
| 387 return NO_RETRY_EXPECTED(unlink(dir_name)) == 0; | 489 return NO_RETRY_EXPECTED(unlinkat(ns.fd(), ns.path(), 0)) == 0; |
| 388 } | 490 } |
| 389 return NO_RETRY_EXPECTED(rmdir(dir_name)) == 0; | 491 return NO_RETRY_EXPECTED(unlinkat(ns.fd(), ns.path(), AT_REMOVEDIR)) == 0; |
| 390 } else { | 492 } else { |
| 391 PathBuffer path; | 493 PathBuffer path; |
| 392 if (!path.Add(dir_name)) { | 494 if (!path.Add(ns.path())) { |
| 393 return false; | 495 return false; |
| 394 } | 496 } |
| 395 return DeleteRecursively(&path); | 497 return DeleteRecursively(ns.fd(), &path); |
| 396 } | 498 } |
| 397 } | 499 } |
| 398 | 500 |
| 399 bool Directory::Rename(const char* path, const char* new_path) { | 501 bool Directory::Rename(Namespace* namespc, |
| 400 ExistsResult exists = Exists(path); | 502 const char* old_path, |
| 503 const char* new_path) { |
| 504 ExistsResult exists = Exists(namespc, old_path); |
| 401 if (exists != EXISTS) { | 505 if (exists != EXISTS) { |
| 402 return false; | 506 return false; |
| 403 } | 507 } |
| 404 return (NO_RETRY_EXPECTED(rename(path, new_path)) == 0); | 508 NamespaceScope oldns(namespc, old_path); |
| 509 NamespaceScope newns(namespc, new_path); |
| 510 return (NO_RETRY_EXPECTED(renameat(oldns.fd(), oldns.path(), newns.fd(), |
| 511 newns.path())) == 0); |
| 405 } | 512 } |
| 406 | 513 |
| 407 } // namespace bin | 514 } // namespace bin |
| 408 } // namespace dart | 515 } // namespace dart |
| 409 | 516 |
| 410 #endif // defined(HOST_OS_FUCHSIA) | 517 #endif // defined(HOST_OS_FUCHSIA) |
| OLD | NEW |