blob: e1ed5416e0127ceab3ab959310ed74e27bc048b7 [file] [log] [blame]
Narayan Kamathc5f27a72016-08-19 13:45:24 +01001/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <string>
18#include <unordered_map>
19#include <set>
20#include <vector>
21#include <algorithm>
22
Jakub Adamek1c15c632016-09-23 09:07:11 +010023#include <android-base/strings.h>
Narayan Kamathc5f27a72016-08-19 13:45:24 +010024#include <dirent.h>
25#include <fcntl.h>
26#include <grp.h>
27#include <inttypes.h>
28#include <stdlib.h>
Narayan Kamath3764a262016-08-30 15:36:19 +010029#include <sys/socket.h>
Narayan Kamathc5f27a72016-08-19 13:45:24 +010030#include <sys/stat.h>
31#include <sys/types.h>
Narayan Kamath3764a262016-08-30 15:36:19 +010032#include <sys/un.h>
Narayan Kamathc5f27a72016-08-19 13:45:24 +010033#include <unistd.h>
34
35#include <cutils/log.h>
36#include "JNIHelp.h"
37#include "ScopedPrimitiveArray.h"
38
Narayan Kamath3764a262016-08-30 15:36:19 +010039// Whitelist of open paths that the zygote is allowed to keep open.
40//
41// In addition to the paths listed here, all files ending with
42// ".jar" under /system/framework" are whitelisted. See
43// FileDescriptorInfo::IsWhitelisted for the canonical definition.
44//
45// If the whitelisted path is associated with a regular file or a
46// character device, the file is reopened after a fork with the same
47// offset and mode. If the whilelisted path is associated with a
48// AF_UNIX socket, the socket will refer to /dev/null after each
49// fork, and all operations on it will fail.
Narayan Kamathc5f27a72016-08-19 13:45:24 +010050static const char* kPathWhitelist[] = {
51 "/dev/null",
Narayan Kamath3764a262016-08-30 15:36:19 +010052 "/dev/socket/zygote",
53 "/dev/socket/zygote_secondary",
Narayan Kamathc5f27a72016-08-19 13:45:24 +010054 "/sys/kernel/debug/tracing/trace_marker",
55 "/system/framework/framework-res.apk",
56 "/dev/urandom",
Adrian Salido8977e422016-08-30 12:51:55 -070057 "/dev/ion",
58 "/dev/dri/renderD129", // Fixes b/31172436
Narayan Kamathc5f27a72016-08-19 13:45:24 +010059};
60
61static const char* kFdPath = "/proc/self/fd";
62
63// Keeps track of all relevant information (flags, offset etc.) of an
64// open zygote file descriptor.
65class FileDescriptorInfo {
66 public:
67 // Create a FileDescriptorInfo for a given file descriptor. Returns
68 // |NULL| if an error occurred.
69 static FileDescriptorInfo* createFromFd(int fd) {
70 struct stat f_stat;
71 // This should never happen; the zygote should always have the right set
72 // of permissions required to stat all its open files.
73 if (TEMP_FAILURE_RETRY(fstat(fd, &f_stat)) == -1) {
74 ALOGE("Unable to stat fd %d : %s", fd, strerror(errno));
75 return NULL;
76 }
77
Narayan Kamathc5f27a72016-08-19 13:45:24 +010078 if (S_ISSOCK(f_stat.st_mode)) {
Narayan Kamath3764a262016-08-30 15:36:19 +010079 std::string socket_name;
80 if (!GetSocketName(fd, &socket_name)) {
81 return NULL;
82 }
83
84 if (!IsWhitelisted(socket_name)) {
85 ALOGE("Socket name not whitelisted : %s (fd=%d)", socket_name.c_str(), fd);
86 return NULL;
87 }
88
Narayan Kamathc5f27a72016-08-19 13:45:24 +010089 return new FileDescriptorInfo(fd);
90 }
91
92 // We only handle whitelisted regular files and character devices. Whitelisted
93 // character devices must provide a guarantee of sensible behaviour when
94 // reopened.
95 //
96 // S_ISDIR : Not supported. (We could if we wanted to, but it's unused).
97 // S_ISLINK : Not supported.
98 // S_ISBLK : Not supported.
99 // S_ISFIFO : Not supported. Note that the zygote uses pipes to communicate
100 // with the child process across forks but those should have been closed
101 // before we got to this point.
102 if (!S_ISCHR(f_stat.st_mode) && !S_ISREG(f_stat.st_mode)) {
103 ALOGE("Unsupported st_mode %d", f_stat.st_mode);
104 return NULL;
105 }
106
107 std::string file_path;
108 if (!Readlink(fd, &file_path)) {
109 return NULL;
110 }
111
112 if (!IsWhitelisted(file_path)) {
113 ALOGE("Not whitelisted : %s", file_path.c_str());
114 return NULL;
115 }
116
117 // File descriptor flags : currently on FD_CLOEXEC. We can set these
118 // using F_SETFD - we're single threaded at this point of execution so
119 // there won't be any races.
120 const int fd_flags = TEMP_FAILURE_RETRY(fcntl(fd, F_GETFD));
121 if (fd_flags == -1) {
122 ALOGE("Failed fcntl(%d, F_GETFD) : %s", fd, strerror(errno));
123 return NULL;
124 }
125
126 // File status flags :
127 // - File access mode : (O_RDONLY, O_WRONLY...) we'll pass these through
128 // to the open() call.
129 //
130 // - File creation flags : (O_CREAT, O_EXCL...) - there's not much we can
131 // do about these, since the file has already been created. We shall ignore
132 // them here.
133 //
134 // - Other flags : We'll have to set these via F_SETFL. On linux, F_SETFL
135 // can only set O_APPEND, O_ASYNC, O_DIRECT, O_NOATIME, and O_NONBLOCK.
136 // In particular, it can't set O_SYNC and O_DSYNC. We'll have to test for
137 // their presence and pass them in to open().
138 int fs_flags = TEMP_FAILURE_RETRY(fcntl(fd, F_GETFL));
139 if (fs_flags == -1) {
140 ALOGE("Failed fcntl(%d, F_GETFL) : %s", fd, strerror(errno));
141 return NULL;
142 }
143
144 // File offset : Ignore the offset for non seekable files.
145 const off_t offset = TEMP_FAILURE_RETRY(lseek64(fd, 0, SEEK_CUR));
146
147 // We pass the flags that open accepts to open, and use F_SETFL for
148 // the rest of them.
149 static const int kOpenFlags = (O_RDONLY | O_WRONLY | O_RDWR | O_DSYNC | O_SYNC);
150 int open_flags = fs_flags & (kOpenFlags);
151 fs_flags = fs_flags & (~(kOpenFlags));
152
153 return new FileDescriptorInfo(f_stat, file_path, fd, open_flags, fd_flags, fs_flags, offset);
154 }
155
156 // Checks whether the file descriptor associated with this object
157 // refers to the same description.
158 bool Restat() const {
159 struct stat f_stat;
160 if (TEMP_FAILURE_RETRY(fstat(fd, &f_stat)) == -1) {
161 return false;
162 }
163
164 return f_stat.st_ino == stat.st_ino && f_stat.st_dev == stat.st_dev;
165 }
166
Narayan Kamath3764a262016-08-30 15:36:19 +0100167 bool ReopenOrDetach() const {
Narayan Kamathc5f27a72016-08-19 13:45:24 +0100168 if (is_sock) {
Narayan Kamath3764a262016-08-30 15:36:19 +0100169 return DetachSocket();
Narayan Kamathc5f27a72016-08-19 13:45:24 +0100170 }
171
172 // NOTE: This might happen if the file was unlinked after being opened.
173 // It's a common pattern in the case of temporary files and the like but
174 // we should not allow such usage from the zygote.
175 const int new_fd = TEMP_FAILURE_RETRY(open(file_path.c_str(), open_flags));
176
177 if (new_fd == -1) {
178 ALOGE("Failed open(%s, %d) : %s", file_path.c_str(), open_flags, strerror(errno));
179 return false;
180 }
181
182 if (TEMP_FAILURE_RETRY(fcntl(new_fd, F_SETFD, fd_flags)) == -1) {
183 close(new_fd);
184 ALOGE("Failed fcntl(%d, F_SETFD, %x) : %s", new_fd, fd_flags, strerror(errno));
185 return false;
186 }
187
188 if (TEMP_FAILURE_RETRY(fcntl(new_fd, F_SETFL, fs_flags)) == -1) {
189 close(new_fd);
190 ALOGE("Failed fcntl(%d, F_SETFL, %x) : %s", new_fd, fs_flags, strerror(errno));
191 return false;
192 }
193
194 if (offset != -1 && TEMP_FAILURE_RETRY(lseek64(new_fd, offset, SEEK_SET)) == -1) {
195 close(new_fd);
196 ALOGE("Failed lseek64(%d, SEEK_SET) : %s", new_fd, strerror(errno));
197 return false;
198 }
199
200 if (TEMP_FAILURE_RETRY(dup2(new_fd, fd)) == -1) {
201 close(new_fd);
202 ALOGE("Failed dup2(%d, %d) : %s", fd, new_fd, strerror(errno));
203 return false;
204 }
205
Narayan Kamath3764a262016-08-30 15:36:19 +0100206 close(new_fd);
Narayan Kamathc5f27a72016-08-19 13:45:24 +0100207
208 return true;
209 }
210
211 const int fd;
212 const struct stat stat;
213 const std::string file_path;
214 const int open_flags;
215 const int fd_flags;
216 const int fs_flags;
217 const off_t offset;
218 const bool is_sock;
219
220 private:
221 FileDescriptorInfo(int fd) :
222 fd(fd),
223 stat(),
224 open_flags(0),
225 fd_flags(0),
226 fs_flags(0),
227 offset(0),
228 is_sock(true) {
229 }
230
231 FileDescriptorInfo(struct stat stat, const std::string& file_path, int fd, int open_flags,
232 int fd_flags, int fs_flags, off_t offset) :
233 fd(fd),
234 stat(stat),
235 file_path(file_path),
236 open_flags(open_flags),
237 fd_flags(fd_flags),
238 fs_flags(fs_flags),
239 offset(offset),
240 is_sock(false) {
241 }
242
Narayan Kamath9087f332016-09-23 09:07:11 +0100243 static bool StartsWith(const std::string& str, const std::string& prefix) {
244 return str.compare(0, prefix.size(), prefix) == 0;
245 }
246
247 static bool EndsWith(const std::string& str, const std::string& suffix) {
248 if (suffix.size() > str.size()) {
249 return false;
250 }
251
252 return str.compare(str.size() - suffix.size(), suffix.size(), suffix) == 0;
253 }
254
Narayan Kamathc5f27a72016-08-19 13:45:24 +0100255 // Returns true iff. a given path is whitelisted. A path is whitelisted
256 // if it belongs to the whitelist (see kPathWhitelist) or if it's a path
Jakub Adamek1c15c632016-09-23 09:07:11 +0100257 // under /system/framework that ends with ".jar" or if it is a system
258 // framework overlay.
Narayan Kamathc5f27a72016-08-19 13:45:24 +0100259 static bool IsWhitelisted(const std::string& path) {
260 for (size_t i = 0; i < (sizeof(kPathWhitelist) / sizeof(kPathWhitelist[0])); ++i) {
261 if (kPathWhitelist[i] == path) {
262 return true;
263 }
264 }
265
Narayan Kamath9087f332016-09-23 09:07:11 +0100266 static const std::string kFrameworksPrefix = "/system/framework/";
267 static const std::string kJarSuffix = ".jar";
268 if (StartsWith(path, kFrameworksPrefix) && EndsWith(path, kJarSuffix)) {
Narayan Kamathc5f27a72016-08-19 13:45:24 +0100269 return true;
270 }
Jakub Adamek1c15c632016-09-23 09:07:11 +0100271
272 // Whitelist files needed for Runtime Resource Overlay, like these:
273 // /system/vendor/overlay/framework-res.apk
Narayan Kamath9087f332016-09-23 09:07:11 +0100274 // /system/vendor/overlay-subdir/pg/framework-res.apk
Jakub Adamek1c15c632016-09-23 09:07:11 +0100275 // /data/resource-cache/system@vendor@overlay@framework-res.apk@idmap
Narayan Kamath9087f332016-09-23 09:07:11 +0100276 // /data/resource-cache/system@vendor@overlay-subdir@pg@framework-res.apk@idmap
277 // See AssetManager.cpp for more details on overlay-subdir.
278 static const std::string kOverlayDir = "/system/vendor/overlay/";
279 static const std::string kVendorOverlayDir = "/vendor/overlay";
280 static const std::string kOverlaySubdir = "/system/vendor/overlay-subdir/";
281 static const std::string kApkSuffix = ".apk";
Jakub Adamek1c15c632016-09-23 09:07:11 +0100282
Narayan Kamath9087f332016-09-23 09:07:11 +0100283 if ((StartsWith(path, kOverlayDir) || StartsWith(path, kOverlaySubdir)
284 || StartsWith(path, kVendorOverlayDir))
285 && EndsWith(path, kApkSuffix)
Jakub Adamek1c15c632016-09-23 09:07:11 +0100286 && path.find("/../") == std::string::npos) {
287 return true;
288 }
289
Narayan Kamath9087f332016-09-23 09:07:11 +0100290 static const std::string kOverlayIdmapPrefix = "/data/resource-cache/";
291 static const std::string kOverlayIdmapSuffix = ".apk@idmap";
292 if (StartsWith(path, kOverlayIdmapPrefix) && EndsWith(path, kOverlayIdmapSuffix)
293 && path.find("/../") == std::string::npos) {
Jakub Adamek1c15c632016-09-23 09:07:11 +0100294 return true;
295 }
296
Narayan Kamath5e2f7c62016-11-07 19:59:29 +0000297 // All regular files that are placed under this path are whitelisted automatically.
298 static const std::string kZygoteWhitelistPath = "/vendor/zygote_whitelist/";
299 if (StartsWith(path, kZygoteWhitelistPath) && path.find("/../") == std::string::npos) {
300 return true;
301 }
302
Narayan Kamathc5f27a72016-08-19 13:45:24 +0100303 return false;
304 }
305
306 // TODO: Call android::base::Readlink instead of copying the code here.
307 static bool Readlink(const int fd, std::string* result) {
308 char path[64];
309 snprintf(path, sizeof(path), "/proc/self/fd/%d", fd);
310
311 // Code copied from android::base::Readlink starts here :
312
313 // Annoyingly, the readlink system call returns EINVAL for a zero-sized buffer,
314 // and truncates to whatever size you do supply, so it can't be used to query.
315 // We could call lstat first, but that would introduce a race condition that
316 // we couldn't detect.
317 // ext2 and ext4 both have PAGE_SIZE limitations, so we assume that here.
318 char buf[4096];
319 ssize_t len = readlink(path, buf, sizeof(buf));
320 if (len == -1) return false;
321
322 result->assign(buf, len);
323 return true;
324 }
325
Narayan Kamath3764a262016-08-30 15:36:19 +0100326 // Returns the locally-bound name of the socket |fd|. Returns true
327 // iff. all of the following hold :
328 //
329 // - the socket's sa_family is AF_UNIX.
330 // - the length of the path is greater than zero (i.e, not an unnamed socket).
331 // - the first byte of the path isn't zero (i.e, not a socket with an abstract
332 // address).
333 static bool GetSocketName(const int fd, std::string* result) {
334 sockaddr_storage ss;
335 sockaddr* addr = reinterpret_cast<sockaddr*>(&ss);
336 socklen_t addr_len = sizeof(ss);
337
338 if (TEMP_FAILURE_RETRY(getsockname(fd, addr, &addr_len)) == -1) {
339 ALOGE("Failed getsockname(%d) : %s", fd, strerror(errno));
340 return false;
341 }
342
343 if (addr->sa_family != AF_UNIX) {
344 ALOGE("Unsupported socket (fd=%d) with family %d", fd, addr->sa_family);
345 return false;
346 }
347
348 const sockaddr_un* unix_addr = reinterpret_cast<const sockaddr_un*>(&ss);
349
350 size_t path_len = addr_len - offsetof(struct sockaddr_un, sun_path);
351 // This is an unnamed local socket, we do not accept it.
352 if (path_len == 0) {
353 ALOGE("Unsupported AF_UNIX socket (fd=%d) with empty path.", fd);
354 return false;
355 }
356
357 // This is a local socket with an abstract address, we do not accept it.
358 if (unix_addr->sun_path[0] == '\0') {
359 ALOGE("Unsupported AF_UNIX socket (fd=%d) with abstract address.", fd);
360 return false;
361 }
362
363 // If we're here, sun_path must refer to a null terminated filesystem
364 // pathname (man 7 unix). Remove the terminator before assigning it to an
365 // std::string.
366 if (unix_addr->sun_path[path_len - 1] == '\0') {
367 --path_len;
368 }
369
370 result->assign(unix_addr->sun_path, path_len);
371 return true;
372 }
373
374 bool DetachSocket() const {
375 const int dev_null_fd = open("/dev/null", O_RDWR);
376 if (dev_null_fd < 0) {
377 ALOGE("Failed to open /dev/null : %s", strerror(errno));
378 return false;
379 }
380
381 if (dup2(dev_null_fd, fd) == -1) {
382 ALOGE("Failed dup2 on socket descriptor %d : %s", fd, strerror(errno));
383 return false;
384 }
385
386 if (close(dev_null_fd) == -1) {
387 ALOGE("Failed close(%d) : %s", dev_null_fd, strerror(errno));
388 return false;
389 }
390
391 return true;
392 }
393
Narayan Kamathc5f27a72016-08-19 13:45:24 +0100394 DISALLOW_COPY_AND_ASSIGN(FileDescriptorInfo);
395};
396
397// A FileDescriptorTable is a collection of FileDescriptorInfo objects
398// keyed by their FDs.
399class FileDescriptorTable {
400 public:
401 // Creates a new FileDescriptorTable. This function scans
402 // /proc/self/fd for the list of open file descriptors and collects
403 // information about them. Returns NULL if an error occurs.
404 static FileDescriptorTable* Create() {
405 DIR* d = opendir(kFdPath);
406 if (d == NULL) {
407 ALOGE("Unable to open directory %s: %s", kFdPath, strerror(errno));
408 return NULL;
409 }
410 int dir_fd = dirfd(d);
411 dirent* e;
412
413 std::unordered_map<int, FileDescriptorInfo*> open_fd_map;
414 while ((e = readdir(d)) != NULL) {
415 const int fd = ParseFd(e, dir_fd);
416 if (fd == -1) {
417 continue;
418 }
419
420 FileDescriptorInfo* info = FileDescriptorInfo::createFromFd(fd);
421 if (info == NULL) {
422 if (closedir(d) == -1) {
423 ALOGE("Unable to close directory : %s", strerror(errno));
424 }
425 return NULL;
426 }
427 open_fd_map[fd] = info;
428 }
429
430 if (closedir(d) == -1) {
431 ALOGE("Unable to close directory : %s", strerror(errno));
432 return NULL;
433 }
434 return new FileDescriptorTable(open_fd_map);
435 }
436
437 bool Restat() {
438 std::set<int> open_fds;
439
440 // First get the list of open descriptors.
441 DIR* d = opendir(kFdPath);
442 if (d == NULL) {
443 ALOGE("Unable to open directory %s: %s", kFdPath, strerror(errno));
444 return false;
445 }
446
447 int dir_fd = dirfd(d);
448 dirent* e;
449 while ((e = readdir(d)) != NULL) {
450 const int fd = ParseFd(e, dir_fd);
451 if (fd == -1) {
452 continue;
453 }
454
455 open_fds.insert(fd);
456 }
457
458 if (closedir(d) == -1) {
459 ALOGE("Unable to close directory : %s", strerror(errno));
460 return false;
461 }
462
463 return RestatInternal(open_fds);
464 }
465
466 // Reopens all file descriptors that are contained in the table. Returns true
Narayan Kamath3764a262016-08-30 15:36:19 +0100467 // if all descriptors were successfully re-opened or detached, and false if an
468 // error occurred.
469 bool ReopenOrDetach() {
Narayan Kamathc5f27a72016-08-19 13:45:24 +0100470 std::unordered_map<int, FileDescriptorInfo*>::const_iterator it;
471 for (it = open_fd_map_.begin(); it != open_fd_map_.end(); ++it) {
472 const FileDescriptorInfo* info = it->second;
Narayan Kamath3764a262016-08-30 15:36:19 +0100473 if (info == NULL || !info->ReopenOrDetach()) {
Narayan Kamathc5f27a72016-08-19 13:45:24 +0100474 return false;
475 }
476 }
477
478 return true;
479 }
480
481 private:
482 FileDescriptorTable(const std::unordered_map<int, FileDescriptorInfo*>& map)
483 : open_fd_map_(map) {
484 }
485
486 bool RestatInternal(std::set<int>& open_fds) {
487 bool error = false;
488
489 // Iterate through the list of file descriptors we've already recorded
490 // and check whether :
491 //
492 // (a) they continue to be open.
493 // (b) they refer to the same file.
Narayan Kamath0b76d6a2016-09-07 13:14:40 +0100494 std::unordered_map<int, FileDescriptorInfo*>::iterator it = open_fd_map_.begin();
495 while (it != open_fd_map_.end()) {
Narayan Kamathc5f27a72016-08-19 13:45:24 +0100496 std::set<int>::const_iterator element = open_fds.find(it->first);
497 if (element == open_fds.end()) {
498 // The entry from the file descriptor table is no longer in the list
499 // of open files. We warn about this condition and remove it from
500 // the list of FDs under consideration.
501 //
502 // TODO(narayan): This will be an error in a future android release.
503 // error = true;
Narayan Kamath3764a262016-08-30 15:36:19 +0100504 // ALOGW("Zygote closed file descriptor %d.", it->first);
Narayan Kamath0b76d6a2016-09-07 13:14:40 +0100505 it = open_fd_map_.erase(it);
Narayan Kamathc5f27a72016-08-19 13:45:24 +0100506 } else {
507 // The entry from the file descriptor table is still open. Restat
508 // it and check whether it refers to the same file.
Narayan Kamathc5f27a72016-08-19 13:45:24 +0100509 const bool same_file = it->second->Restat();
510 if (!same_file) {
511 // The file descriptor refers to a different description. We must
512 // update our entry in the table.
513 delete it->second;
514 it->second = FileDescriptorInfo::createFromFd(*element);
515 if (it->second == NULL) {
516 // The descriptor no longer no longer refers to a whitelisted file.
517 // We flag an error and remove it from the list of files we're
518 // tracking.
519 error = true;
Narayan Kamath0b76d6a2016-09-07 13:14:40 +0100520 it = open_fd_map_.erase(it);
521 } else {
522 // Successfully restatted the file, move on to the next open FD.
523 ++it;
Narayan Kamathc5f27a72016-08-19 13:45:24 +0100524 }
525 } else {
Narayan Kamath0b76d6a2016-09-07 13:14:40 +0100526 // It's the same file. Nothing to do here. Move on to the next open
527 // FD.
528 ++it;
Narayan Kamathc5f27a72016-08-19 13:45:24 +0100529 }
Narayan Kamath0b76d6a2016-09-07 13:14:40 +0100530
531 // Finally, remove the FD from the set of open_fds. We do this last because
532 // |element| will not remain valid after a call to erase.
533 open_fds.erase(element);
Narayan Kamathc5f27a72016-08-19 13:45:24 +0100534 }
535 }
536
537 if (open_fds.size() > 0) {
538 // The zygote has opened new file descriptors since our last inspection.
539 // We warn about this condition and add them to our table.
540 //
541 // TODO(narayan): This will be an error in a future android release.
542 // error = true;
Narayan Kamath3764a262016-08-30 15:36:19 +0100543 // ALOGW("Zygote opened %zd new file descriptor(s).", open_fds.size());
Narayan Kamathc5f27a72016-08-19 13:45:24 +0100544
545 // TODO(narayan): This code will be removed in a future android release.
546 std::set<int>::const_iterator it;
547 for (it = open_fds.begin(); it != open_fds.end(); ++it) {
548 const int fd = (*it);
549 FileDescriptorInfo* info = FileDescriptorInfo::createFromFd(fd);
550 if (info == NULL) {
551 // A newly opened file is not on the whitelist. Flag an error and
552 // continue.
553 error = true;
554 } else {
555 // Track the newly opened file.
556 open_fd_map_[fd] = info;
557 }
558 }
559 }
560
561 return !error;
562 }
563
564 static int ParseFd(dirent* e, int dir_fd) {
565 char* end;
566 const int fd = strtol(e->d_name, &end, 10);
567 if ((*end) != '\0') {
568 return -1;
569 }
570
571 // Don't bother with the standard input/output/error, they're handled
572 // specially post-fork anyway.
573 if (fd <= STDERR_FILENO || fd == dir_fd) {
574 return -1;
575 }
576
577 return fd;
578 }
579
580 // Invariant: All values in this unordered_map are non-NULL.
581 std::unordered_map<int, FileDescriptorInfo*> open_fd_map_;
582
583 DISALLOW_COPY_AND_ASSIGN(FileDescriptorTable);
584};