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