Fix directory-listing, directory-deletion and file deletion, when links are involved.

BUG=https://code.google.com/p/dart/issues/detail?id=4928,https://code.google.com/p/dart/issues/detail?id=9278,https://code.google.com/p/dart/issues/detail?id=9504

Review URL: https://codereview.chromium.org//13578003

git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@20929 260f80e4-7a28-3924-810f-c04153c831b5
This commit is contained in:
ajohnsen@google.com
2013-04-04 17:30:17 +00:00
parent 6514936a7a
commit 578331c57e
7 changed files with 151 additions and 19 deletions
+5 -2
View File
@@ -169,6 +169,9 @@ static bool ListRecursively(PathBuffer* path,
int stat_success;
if (follow_links) {
stat_success = TEMP_FAILURE_RETRY(stat(path->data, &entry_info));
if (stat_success == -1) {
stat_success = TEMP_FAILURE_RETRY(lstat(path->data, &entry_info));
}
} else {
stat_success = TEMP_FAILURE_RETRY(lstat(path->data, &entry_info));
}
@@ -189,7 +192,6 @@ static bool ListRecursively(PathBuffer* path,
path,
listing) && success;
} else if (S_ISLNK(entry_info.st_mode)) {
ASSERT(!follow_links);
success = HandleLink(entry.d_name,
path,
listing) && success;
@@ -232,7 +234,6 @@ static bool DeleteDir(char* dir_name,
static bool DeleteRecursively(PathBuffer* path) {
if (!path->Add(File::PathSeparator())) return false;
// Do not recurse into links for deletion. Instead delete the link.
struct stat st;
if (TEMP_FAILURE_RETRY(lstat(path->data, &st)) == -1) {
@@ -241,6 +242,8 @@ static bool DeleteRecursively(PathBuffer* path) {
return (remove(path->data) == 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;
+5 -2
View File
@@ -169,6 +169,9 @@ static bool ListRecursively(PathBuffer* path,
int stat_success;
if (follow_links) {
stat_success = TEMP_FAILURE_RETRY(stat(path->data, &entry_info));
if (stat_success == -1) {
stat_success = TEMP_FAILURE_RETRY(lstat(path->data, &entry_info));
}
} else {
stat_success = TEMP_FAILURE_RETRY(lstat(path->data, &entry_info));
}
@@ -189,7 +192,6 @@ static bool ListRecursively(PathBuffer* path,
path,
listing) && success;
} else if (S_ISLNK(entry_info.st_mode)) {
ASSERT(!follow_links);
success = HandleLink(entry.d_name,
path,
listing) && success;
@@ -232,7 +234,6 @@ static bool DeleteDir(char* dir_name,
static bool DeleteRecursively(PathBuffer* path) {
if (!path->Add(File::PathSeparator())) return false;
// Do not recurse into links for deletion. Instead delete the link.
struct stat st;
if (TEMP_FAILURE_RETRY(lstat(path->data, &st)) == -1) {
@@ -241,6 +242,8 @@ static bool DeleteRecursively(PathBuffer* path) {
return (remove(path->data) == 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;
+6 -3
View File
@@ -169,6 +169,9 @@ static bool ListRecursively(PathBuffer* path,
int stat_success;
if (follow_links) {
stat_success = TEMP_FAILURE_RETRY(stat(path->data, &entry_info));
if (stat_success == -1) {
stat_success = TEMP_FAILURE_RETRY(lstat(path->data, &entry_info));
}
} else {
stat_success = TEMP_FAILURE_RETRY(lstat(path->data, &entry_info));
}
@@ -189,7 +192,6 @@ static bool ListRecursively(PathBuffer* path,
path,
listing) && success;
} else if (S_ISLNK(entry_info.st_mode)) {
ASSERT(!follow_links);
success = HandleLink(entry.d_name,
path,
listing) && success;
@@ -232,8 +234,7 @@ static bool DeleteDir(char* dir_name,
static bool DeleteRecursively(PathBuffer* path) {
if (!path->Add(File::PathSeparator())) return false;
// Do not recurse into links for deletion. Instead delete the link.
// Do not recurse into links for deletion. Instead delete the link.
struct stat st;
if (TEMP_FAILURE_RETRY(lstat(path->data, &st)) == -1) {
return false;
@@ -241,6 +242,8 @@ static bool DeleteRecursively(PathBuffer* path) {
return (remove(path->data) == 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;
+19 -3
View File
@@ -118,9 +118,25 @@ static bool HandleEntry(LPWIN32_FIND_DATAW find_file_data,
bool follow_links,
DirectoryListing* listing) {
DWORD attributes = find_file_data->dwFileAttributes;
if (!follow_links && (attributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0) {
return HandleLink(find_file_data->cFileName, path, listing);
} else if ((attributes & FILE_ATTRIBUTE_DIRECTORY) != 0) {
if ((attributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0) {
if (!follow_links) {
return HandleLink(find_file_data->cFileName, path, listing);
}
// Attempt to list the directory, to see if it's a valid link or not.
int path_length = path->length;
if (!path->Add(find_file_data->cFileName)) return false;
if (!path->Add(L"\\*")) return false;
WIN32_FIND_DATAW tmp_file_data;
HANDLE find_handle = FindFirstFileW(path->data, &tmp_file_data);
path->Reset(path_length);
if (find_handle == INVALID_HANDLE_VALUE) {
// Invalid handle, report as (broken) link.
return HandleLink(find_file_data->cFileName, path, listing);
} else {
FindClose(find_handle);
}
}
if ((attributes & FILE_ATTRIBUTE_DIRECTORY) != 0) {
return HandleDir(find_file_data->cFileName,
path,
recursive,
+12 -5
View File
@@ -259,12 +259,19 @@ bool File::CreateLink(const char* utf8_name, const char* utf8_target) {
bool File::Delete(const char* name) {
const wchar_t* system_name = StringUtils::Utf8ToWide(name);
int status = _wremove(system_name);
free(const_cast<wchar_t*>(system_name));
if (status == -1) {
return false;
DWORD attributes = GetFileAttributesW(system_name);
if ((attributes != INVALID_FILE_ATTRIBUTES) &&
(attributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0) {
// It's a junction(link), delete it.
return RemoveDirectoryW(system_name) != 0;
} else {
int status = _wremove(system_name);
free(const_cast<wchar_t*>(system_name));
if (status == -1) {
return false;
}
return true;
}
return true;
}
+97
View File
@@ -244,6 +244,98 @@ class DirectoryTest {
Expect.isFalse(d.existsSync());
}
static void testDeleteLinkSync() {
Directory tmp = new Directory("").createTempSync();
var path = "${tmp.path}${Platform.pathSeparator}";
Directory d = new Directory("${path}target");
d.createSync();
Link l = new Link("${path}symlink");
l.createSync("${path}target");
Expect.isTrue(d.existsSync());
Expect.isTrue(l.existsSync());
new Directory(l.path).deleteSync(recursive: true);
Expect.isTrue(d.existsSync());
Expect.isFalse(l.existsSync());
d.deleteSync();
Expect.isFalse(d.existsSync());
tmp.deleteSync();
}
static void testDeleteLinkAsFileSync() {
Directory tmp = new Directory("").createTempSync();
var path = "${tmp.path}${Platform.pathSeparator}";
Directory d = new Directory("${path}target");
d.createSync();
Link l = new Link("${path}symlink");
l.createSync("${path}target");
Expect.isTrue(d.existsSync());
Expect.isTrue(l.existsSync());
new Link(l.path).deleteSync();
Expect.isTrue(d.existsSync());
Expect.isFalse(l.existsSync());
d.deleteSync();
Expect.isFalse(d.existsSync());
tmp.deleteSync();
}
static void testDeleteBrokenLinkAsFileSync() {
Directory tmp = new Directory("").createTempSync();
var path = "${tmp.path}${Platform.pathSeparator}";
Directory d = new Directory("${path}target");
d.createSync();
Link l = new Link("${path}symlink");
l.createSync("${path}target");
d.deleteSync();
Expect.isFalse(d.existsSync());
Expect.isTrue(l.existsSync());
new Link(l.path).deleteSync();
Expect.isFalse(l.existsSync());
Expect.isFalse(d.existsSync());
tmp.deleteSync();
}
static void testListBrokenLinkSync() {
Directory tmp = new Directory("").createTempSync();
var path = "${tmp.path}${Platform.pathSeparator}";
Directory d = new Directory("${path}target");
d.createSync();
Link l = new Link("${path}symlink");
l.createSync("${path}target");
d.deleteSync();
int count = 0;
tmp.list(followLinks: true).listen(
(file) {
count++;
Expect.isTrue(file is Link);
},
onDone: () {
Expect.equals(1, count);
l.deleteSync();
tmp.deleteSync();
});
}
static void testListLinkSync() {
Directory tmp = new Directory("").createTempSync();
var path = "${tmp.path}${Platform.pathSeparator}";
Directory d = new Directory("${path}target");
d.createSync();
Link l = new Link("${path}symlink");
l.createSync("${path}target");
int count = 0;
tmp.list(followLinks: true).listen(
(file) {
count++;
Expect.isTrue(file is Directory);
},
onDone: () {
Expect.equals(2, count);
l.deleteSync();
d.deleteSync();
tmp.deleteSync();
});
}
static void testCreateTemp([String template = ""]) {
var port = new ReceivePort();
Directory dir = new Directory(template);
@@ -314,6 +406,11 @@ class DirectoryTest {
testDeleteTooLongNameSync();
testExistsCreateDelete();
testExistsCreateDeleteSync();
testDeleteLinkSync();
testDeleteLinkAsFileSync();
testDeleteBrokenLinkAsFileSync();
testListBrokenLinkSync();
testListLinkSync();
testCreateTemp();
testCreateDeleteTemp();
testCurrent();
@@ -206,15 +206,17 @@ testDirectoryListingBrokenLink() {
var doesNotExist = 'this_thing_does_not_exist';
new File(x).createSync();
createLink(doesNotExist, link, () {
Expect.throws(() => temp.listSync(recursive: true),
(e) => e is DirectoryIOException);
temp.listSync(recursive: true); // No exceptions.
var files = [];
var dirs = [];
var links = [];
var errors = [];
temp.list(recursive: true).listen(
(entity) {
if (entity is File) {
files.add(entity.path);
} else if (entity is Link) {
links.add(entity.path);
} else {
Expect.isTrue(entity is Directory);
dirs.add(entity.path);
@@ -224,9 +226,10 @@ testDirectoryListingBrokenLink() {
onDone: () {
Expect.equals(1, files.length);
Expect.isTrue(files[0].endsWith(x));
Expect.equals(1, links.length);
Expect.isTrue(links[0].endsWith(link));
Expect.equals(0, dirs.length);
Expect.equals(1, errors.length);
Expect.isTrue(errors[0].toString().contains(link));
Expect.equals(0, errors.length);
temp.deleteSync(recursive: true);
keepAlive.close();
});