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:
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user