| Index: runtime/bin/directory_fuchsia.cc
|
| diff --git a/runtime/bin/directory_fuchsia.cc b/runtime/bin/directory_fuchsia.cc
|
| index 9259bd019164403da02b95544835ac2cd9e01138..c9311712ced530c4dc2fb36186b764dbd91a13e2 100644
|
| --- a/runtime/bin/directory_fuchsia.cc
|
| +++ b/runtime/bin/directory_fuchsia.cc
|
| @@ -126,91 +126,61 @@ ListType DirectoryListingEntry::Next(DirectoryListing* listing) {
|
| done_ = true;
|
| return kListError;
|
| }
|
| - switch (entry->d_type) {
|
| - case DT_DIR:
|
| + // TODO(MG-450): When entry->d_type is filled out correctly, we can avoid
|
| + // this call to stat().
|
| + struct stat64 entry_info;
|
| + int stat_success;
|
| + stat_success = NO_RETRY_EXPECTED(
|
| + lstat64(listing->path_buffer().AsString(), &entry_info));
|
| + if (stat_success == -1) {
|
| + perror("lstat64 failed: ");
|
| + return kListError;
|
| + }
|
| + if (listing->follow_links() && S_ISLNK(entry_info.st_mode)) {
|
| + // Check to see if we are in a loop created by a symbolic link.
|
| + LinkList current_link = {entry_info.st_dev, entry_info.st_ino, link_};
|
| + LinkList* previous = link_;
|
| + while (previous != NULL) {
|
| + if ((previous->dev == current_link.dev) &&
|
| + (previous->ino == current_link.ino)) {
|
| + // Report the looping link as a link, rather than following it.
|
| + return kListLink;
|
| + }
|
| + previous = previous->next;
|
| + }
|
| + stat_success = NO_RETRY_EXPECTED(
|
| + stat64(listing->path_buffer().AsString(), &entry_info));
|
| + if (stat_success == -1) {
|
| + perror("lstat64 failed");
|
| + // Report a broken link as a link, even if follow_links is true.
|
| + return kListLink;
|
| + }
|
| + if (S_ISDIR(entry_info.st_mode)) {
|
| + // Recurse into the subdirectory with current_link added to the
|
| + // linked list of seen file system links.
|
| + link_ = new LinkList(current_link);
|
| if ((strcmp(entry->d_name, ".") == 0) ||
|
| (strcmp(entry->d_name, "..") == 0)) {
|
| return Next(listing);
|
| }
|
| return kListDirectory;
|
| - case DT_BLK:
|
| - case DT_CHR:
|
| - case DT_FIFO:
|
| - case DT_SOCK:
|
| - case DT_REG:
|
| - return kListFile;
|
| - case DT_LNK:
|
| - if (!listing->follow_links()) {
|
| - return kListLink;
|
| - }
|
| - // Else fall through to next case.
|
| - // Fall through.
|
| - case DT_UNKNOWN: {
|
| - // On some file systems the entry type is not determined by
|
| - // readdir. For those and for links we use stat to determine
|
| - // the actual entry type. Notice that stat returns the type of
|
| - // the file pointed to.
|
| - struct stat64 entry_info;
|
| - int stat_success;
|
| - stat_success = NO_RETRY_EXPECTED(
|
| - lstat64(listing->path_buffer().AsString(), &entry_info));
|
| - if (stat_success == -1) {
|
| - perror("lstat64 failed: ");
|
| - return kListError;
|
| - }
|
| - if (listing->follow_links() && S_ISLNK(entry_info.st_mode)) {
|
| - // Check to see if we are in a loop created by a symbolic link.
|
| - LinkList current_link = {entry_info.st_dev, entry_info.st_ino, link_};
|
| - LinkList* previous = link_;
|
| - while (previous != NULL) {
|
| - if ((previous->dev == current_link.dev) &&
|
| - (previous->ino == current_link.ino)) {
|
| - // Report the looping link as a link, rather than following it.
|
| - return kListLink;
|
| - }
|
| - previous = previous->next;
|
| - }
|
| - stat_success = NO_RETRY_EXPECTED(
|
| - stat64(listing->path_buffer().AsString(), &entry_info));
|
| - if (stat_success == -1) {
|
| - perror("lstat64 failed");
|
| - // Report a broken link as a link, even if follow_links is true.
|
| - return kListLink;
|
| - }
|
| - if (S_ISDIR(entry_info.st_mode)) {
|
| - // Recurse into the subdirectory with current_link added to the
|
| - // linked list of seen file system links.
|
| - link_ = new LinkList(current_link);
|
| - if ((strcmp(entry->d_name, ".") == 0) ||
|
| - (strcmp(entry->d_name, "..") == 0)) {
|
| - return Next(listing);
|
| - }
|
| - return kListDirectory;
|
| - }
|
| - }
|
| - if (S_ISDIR(entry_info.st_mode)) {
|
| - if ((strcmp(entry->d_name, ".") == 0) ||
|
| - (strcmp(entry->d_name, "..") == 0)) {
|
| - return Next(listing);
|
| - }
|
| - return kListDirectory;
|
| - } else if (S_ISREG(entry_info.st_mode) || S_ISCHR(entry_info.st_mode) ||
|
| - S_ISBLK(entry_info.st_mode) ||
|
| - S_ISFIFO(entry_info.st_mode) ||
|
| - S_ISSOCK(entry_info.st_mode)) {
|
| - return kListFile;
|
| - } else if (S_ISLNK(entry_info.st_mode)) {
|
| - return kListLink;
|
| - } else {
|
| - FATAL1("Unexpected st_mode: %d\n", entry_info.st_mode);
|
| - return kListError;
|
| - }
|
| }
|
| -
|
| - default:
|
| - // We should have covered all the bases. If not, let's get an error.
|
| - FATAL1("Unexpected d_type: %d\n", entry->d_type);
|
| - return kListError;
|
| + }
|
| + if (S_ISDIR(entry_info.st_mode)) {
|
| + if ((strcmp(entry->d_name, ".") == 0) ||
|
| + (strcmp(entry->d_name, "..") == 0)) {
|
| + return Next(listing);
|
| + }
|
| + return kListDirectory;
|
| + } else if (S_ISREG(entry_info.st_mode) || S_ISCHR(entry_info.st_mode) ||
|
| + S_ISBLK(entry_info.st_mode) || S_ISFIFO(entry_info.st_mode) ||
|
| + S_ISSOCK(entry_info.st_mode)) {
|
| + return kListFile;
|
| + } else if (S_ISLNK(entry_info.st_mode)) {
|
| + return kListLink;
|
| + } else {
|
| + FATAL1("Unexpected st_mode: %d\n", entry_info.st_mode);
|
| + return kListError;
|
| }
|
| }
|
| done_ = true;
|
| @@ -336,10 +306,7 @@ const char* Directory::CreateTemp(const char* prefix) {
|
| // Pattern has overflowed.
|
| return NULL;
|
| }
|
| - char* result;
|
| - do {
|
| - result = mkdtemp(path.AsString());
|
| - } while ((result == NULL) && (errno == EINTR));
|
| + char* result = mkdtemp(path.AsString());
|
| if (result == NULL) {
|
| return NULL;
|
| }
|
| @@ -347,6 +314,98 @@ const char* Directory::CreateTemp(const char* prefix) {
|
| }
|
|
|
|
|
| +static bool DeleteRecursively(PathBuffer* path);
|
| +
|
| +
|
| +static bool DeleteFile(char* file_name, PathBuffer* path) {
|
| + return path->Add(file_name) &&
|
| + (NO_RETRY_EXPECTED(unlink(path->AsString())) == 0);
|
| +}
|
| +
|
| +
|
| +static bool DeleteDir(char* dir_name, PathBuffer* path) {
|
| + if ((strcmp(dir_name, ".") == 0) || (strcmp(dir_name, "..") == 0)) {
|
| + return true;
|
| + }
|
| + return path->Add(dir_name) && DeleteRecursively(path);
|
| +}
|
| +
|
| +
|
| +static bool DeleteRecursively(PathBuffer* path) {
|
| + // Do not recurse into links for deletion. Instead delete the link.
|
| + // If it's a file, delete it.
|
| + struct stat64 st;
|
| + if (NO_RETRY_EXPECTED(lstat64(path->AsString(), &st)) == -1) {
|
| + return false;
|
| + } else if (!S_ISDIR(st.st_mode)) {
|
| + return NO_RETRY_EXPECTED(unlink(path->AsString())) == 0;
|
| + }
|
| +
|
| + if (!path->Add(File::PathSeparator())) {
|
| + return false;
|
| + }
|
| +
|
| + // Not a link. Attempt to open as a directory and recurse into the
|
| + // directory.
|
| + DIR* dir_pointer = opendir(path->AsString());
|
| + if (dir_pointer == NULL) {
|
| + return false;
|
| + }
|
| +
|
| + // Iterate the directory and delete all files and directories.
|
| + int path_length = path->length();
|
| + while (true) {
|
| + // In case `readdir()` returns `NULL` we distinguish between end-of-stream
|
| + // and error by looking if `errno` was updated.
|
| + errno = 0;
|
| + // In glibc 2.24+, readdir_r is deprecated.
|
| + // According to the man page for readdir:
|
| + // "readdir(3) is not required to be thread-safe. However, in modern
|
| + // implementations (including the glibc implementation), concurrent calls to
|
| + // readdir(3) that specify different directory streams are thread-safe."
|
| + dirent* entry = readdir(dir_pointer);
|
| + if (entry == NULL) {
|
| + // Failed to read next directory entry.
|
| + if (errno != 0) {
|
| + break;
|
| + }
|
| + // End of directory.
|
| + return (NO_RETRY_EXPECTED(closedir(dir_pointer)) == 0) &&
|
| + (NO_RETRY_EXPECTED(remove(path->AsString())) == 0);
|
| + }
|
| + bool ok = false;
|
| + if (!path->Add(entry->d_name)) {
|
| + break;
|
| + }
|
| + // TODO(MG-450): When entry->d_type is filled out correctly, we can avoid
|
| + // this call to stat().
|
| + struct stat64 entry_info;
|
| + if (NO_RETRY_EXPECTED(lstat64(path->AsString(), &entry_info)) == -1) {
|
| + break;
|
| + }
|
| + path->Reset(path_length);
|
| + if (S_ISDIR(entry_info.st_mode)) {
|
| + ok = DeleteDir(entry->d_name, path);
|
| + } else {
|
| + // Treat links as files. This will delete the link which is
|
| + // what we want no matter if the link target is a file or a
|
| + // directory.
|
| + ok = DeleteFile(entry->d_name, path);
|
| + }
|
| + if (!ok) {
|
| + break;
|
| + }
|
| + path->Reset(path_length);
|
| + }
|
| + // Only happens if there was an error.
|
| + ASSERT(errno != 0);
|
| + int err = errno;
|
| + VOID_NO_RETRY_EXPECTED(closedir(dir_pointer));
|
| + errno = err;
|
| + return false;
|
| +}
|
| +
|
| +
|
| bool Directory::Delete(const char* dir_name, bool recursive) {
|
| if (!recursive) {
|
| if ((File::GetType(dir_name, false) == File::kIsLink) &&
|
| @@ -355,10 +414,11 @@ bool Directory::Delete(const char* dir_name, bool recursive) {
|
| }
|
| return NO_RETRY_EXPECTED(rmdir(dir_name)) == 0;
|
| } else {
|
| - // TODO(MG-416): After the issue is addressed, this can use the same code
|
| - // as on Linux, etc.
|
| - UNIMPLEMENTED();
|
| - return false;
|
| + PathBuffer path;
|
| + if (!path.Add(dir_name)) {
|
| + return false;
|
| + }
|
| + return DeleteRecursively(&path);
|
| }
|
| }
|
|
|
|
|