Skip to content

Commit d275566

Browse files
zichanggcommit-bot@chromium.org
authored andcommitted
Fix Directory.list from aborting on Linux
Directory.list will throw fatal error if a symbolic link points to an unexpected file type with follow_link being set. This cl will make list() issuing the link itself regardless of whether follow_link is set. Closes: dart-lang/sdk#43176 Change-Id: I16f0e9a0ec68c9d48e60f1fbe58820ebe4b066eb Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/160705 Commit-Queue: Zichang Guo <zichangguo@google.com> Reviewed-by: Lasse R.H. Nielsen <lrn@google.com>
1 parent a6db069 commit d275566

File tree

6 files changed

+66
-34
lines changed

6 files changed

+66
-34
lines changed

runtime/bin/directory_android.cc

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -172,8 +172,12 @@ ListType DirectoryListingEntry::Next(DirectoryListing* listing) {
172172
}
173173
stat_success =
174174
TEMP_FAILURE_RETRY(fstatat(ns.fd(), ns.path(), &entry_info, 0));
175-
if (stat_success == -1) {
175+
if (stat_success == -1 || (S_IFMT & entry_info.st_mode) == 0) {
176176
// Report a broken link as a link, even if follow_links is true.
177+
// A symbolic link can potentially point to an anon_inode. For
178+
// example, an epoll file descriptor will have a symbolic link whose
179+
// content is the string anon_inode:[eventpoll]. In this case, the
180+
// target doesn't belong to any regular file catogory.
177181
return kListLink;
178182
}
179183
if (S_ISDIR(entry_info.st_mode)) {
@@ -193,16 +197,12 @@ ListType DirectoryListingEntry::Next(DirectoryListing* listing) {
193197
return Next(listing);
194198
}
195199
return kListDirectory;
196-
} else if (S_ISREG(entry_info.st_mode) || S_ISCHR(entry_info.st_mode) ||
197-
S_ISBLK(entry_info.st_mode) ||
198-
S_ISFIFO(entry_info.st_mode) ||
199-
S_ISSOCK(entry_info.st_mode)) {
200-
return kListFile;
201200
} else if (S_ISLNK(entry_info.st_mode)) {
202201
return kListLink;
203202
} else {
204-
FATAL1("Unexpected st_mode: %d\n", entry_info.st_mode);
205-
return kListError;
203+
// Regular files, character devices, block devices, fifos, sockets and
204+
// unknown types are all considered as files.
205+
return kListFile;
206206
}
207207
}
208208

runtime/bin/directory_fuchsia.cc

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -178,8 +178,12 @@ ListType DirectoryListingEntry::Next(DirectoryListing* listing) {
178178
}
179179
stat_success =
180180
TEMP_FAILURE_RETRY(fstatat(ns.fd(), ns.path(), &entry_info, 0));
181-
if (stat_success == -1) {
181+
if (stat_success == -1 || (S_IFMT & entry_info.st_mode) == 0) {
182182
// Report a broken link as a link, even if follow_links is true.
183+
// A symbolic link can potentially point to an anon_inode. For
184+
// example, an epoll file descriptor will have a symbolic link whose
185+
// content is the string anon_inode:[eventpoll]. In this case, the
186+
// target doesn't belong to any regular file catogory.
183187
return kListLink;
184188
}
185189
if (S_ISDIR(entry_info.st_mode)) {
@@ -199,16 +203,12 @@ ListType DirectoryListingEntry::Next(DirectoryListing* listing) {
199203
return Next(listing);
200204
}
201205
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;
207206
} else if (S_ISLNK(entry_info.st_mode)) {
208207
return kListLink;
209208
} else {
210-
FATAL1("Unexpected st_mode: %d\n", entry_info.st_mode);
211-
return kListError;
209+
// Regular files, character devices, block devices, fifos, sockets and
210+
// unknown types are all considerred as files.
211+
return kListFile;
212212
}
213213
}
214214

runtime/bin/directory_linux.cc

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -172,8 +172,12 @@ ListType DirectoryListingEntry::Next(DirectoryListing* listing) {
172172
}
173173
stat_success =
174174
TEMP_FAILURE_RETRY(fstatat64(ns.fd(), ns.path(), &entry_info, 0));
175-
if (stat_success == -1) {
175+
if (stat_success == -1 || (S_IFMT & entry_info.st_mode) == 0) {
176176
// Report a broken link as a link, even if follow_links is true.
177+
// A symbolic link can potentially point to an anon_inode. For
178+
// example, an epoll file descriptor will have a symbolic link whose
179+
// content is the string anon_inode:[eventpoll]. In this case, the
180+
// target doesn't belong to any regular file catogory.
177181
return kListLink;
178182
}
179183
if (S_ISDIR(entry_info.st_mode)) {
@@ -193,16 +197,12 @@ ListType DirectoryListingEntry::Next(DirectoryListing* listing) {
193197
return Next(listing);
194198
}
195199
return kListDirectory;
196-
} else if (S_ISREG(entry_info.st_mode) || S_ISCHR(entry_info.st_mode) ||
197-
S_ISBLK(entry_info.st_mode) ||
198-
S_ISFIFO(entry_info.st_mode) ||
199-
S_ISSOCK(entry_info.st_mode)) {
200-
return kListFile;
201200
} else if (S_ISLNK(entry_info.st_mode)) {
202201
return kListLink;
203202
} else {
204-
FATAL1("Unexpected st_mode: %d\n", entry_info.st_mode);
205-
return kListError;
203+
// Regular files, character devices, block devices, fifos, sockets and
204+
// unknown types are all considered as files.
205+
return kListFile;
206206
}
207207
}
208208

runtime/bin/directory_macos.cc

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -159,8 +159,12 @@ ListType DirectoryListingEntry::Next(DirectoryListing* listing) {
159159
}
160160
stat_success = NO_RETRY_EXPECTED(
161161
stat(listing->path_buffer().AsString(), &entry_info));
162-
if (stat_success == -1) {
162+
if (stat_success == -1 || (S_IFMT & entry_info.st_mode) == 0) {
163163
// Report a broken link as a link, even if follow_links is true.
164+
// A symbolic link can potentially point to an anon_inode. For
165+
// example, an epoll file descriptor will have a symbolic link whose
166+
// content is the string anon_inode:[eventpoll]. In this case, the
167+
// target doesn't belong to any regular file catogory.
164168
return kListLink;
165169
}
166170
if (S_ISDIR(entry_info.st_mode)) {
@@ -180,16 +184,12 @@ ListType DirectoryListingEntry::Next(DirectoryListing* listing) {
180184
return Next(listing);
181185
}
182186
return kListDirectory;
183-
} else if (S_ISREG(entry_info.st_mode) || S_ISCHR(entry_info.st_mode) ||
184-
S_ISBLK(entry_info.st_mode) ||
185-
S_ISFIFO(entry_info.st_mode) ||
186-
S_ISSOCK(entry_info.st_mode)) {
187-
return kListFile;
188187
} else if (S_ISLNK(entry_info.st_mode)) {
189188
return kListLink;
190189
} else {
191-
FATAL1("Unexpected st_mode: %d\n", entry_info.st_mode);
192-
return kListError;
190+
// Regular files, character devices, block devices, fifos, sockets and
191+
// unknown types are all considered as files.
192+
return kListFile;
193193
}
194194
}
195195

tests/standalone/io/directory_list_sync_test.dart

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
import 'dart:io';
66

7-
void main() {
7+
void testList() {
88
File script = new File.fromUri(Platform.script);
99
// tests/standalone/io/../..
1010
Directory startingDir = script.parent.parent.parent;
@@ -13,3 +13,19 @@ void main() {
1313
startingDir.listSync(recursive: true, followLinks: false);
1414
print("Found: ${each.length} entities");
1515
}
16+
17+
// The test is disabled by default, because it requires permission to run.
18+
// e.g. sudo dart directory_list_sync_test.dart
19+
void testAnonymousNodeOnLinux() {
20+
// If a symbolic link points to an anon_inode, which doesn't have a regular
21+
// file type like an epoll file descriptor, list() will return a link.
22+
if (!Platform.isLinux) {
23+
return;
24+
}
25+
Directory('/proc/').listSync(recursive: true);
26+
}
27+
28+
void main() {
29+
testList();
30+
//testAnonymousNodeOnLinux();
31+
}

tests/standalone_2/io/directory_list_sync_test.dart

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
import 'dart:io';
66

7-
void main() {
7+
void testList() {
88
File script = new File.fromUri(Platform.script);
99
// tests/standalone/io/../..
1010
Directory startingDir = script.parent.parent.parent;
@@ -13,3 +13,19 @@ void main() {
1313
startingDir.listSync(recursive: true, followLinks: false);
1414
print("Found: ${each.length} entities");
1515
}
16+
17+
// The test is disabled by default, because it requires permission to run.
18+
// e.g. sudo dart directory_list_sync_test.dart
19+
void testAnonymousNodeOnLinux() {
20+
// If a symbolic link points to an anon_inode, which doesn't have a regular
21+
// file type like an epoll file descriptor, list() will return a link.
22+
if (!Platform.isLinux) {
23+
return;
24+
}
25+
Directory('/proc/').listSync(recursive: true);
26+
}
27+
28+
void main() {
29+
testList();
30+
//testAnonymousNodeOnLinux();
31+
}

0 commit comments

Comments
 (0)