Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit aa1c962d authored by Chris Wailes's avatar Chris Wailes
Browse files

Implemented native functions and types for blastula management.

This patch adds native support for spawning and managing blastula pools,
as well as several code cleanups and modernizations.

Changes includes:
* A function to fork blastulas
* A table for managing blastula-related data
* Functions for adding and removing blastula data from the
aforementioned table
* Switching from NULL to nullptr
* Replacing string-passing error handling with a curried failure
function
* Utility functions for handling managed objects
* JNI functions for blastula pool management

Change-Id: I12cd9f2c87a2e3c00d64b683edf3631e29a51551
Topic: zygot-prefork
Test: make & flash & launch apps & check log for messages
Bug: 68253328
parent e54b7b54
Loading
Loading
Loading
Loading
+34 −13
Original line number Diff line number Diff line
@@ -126,7 +126,7 @@ public final class Zygote {
    private Zygote() {}

    /** Called for some security initialization before any fork. */
    native static void nativeSecurityInit();
    static native void nativeSecurityInit();

    /**
     * Forks a new VM instance.  The current VM must have been started
@@ -184,15 +184,20 @@ public final class Zygote {
        return pid;
    }

    native private static int nativeForkAndSpecialize(int uid, int gid, int[] gids,int runtimeFlags,
            int[][] rlimits, int mountExternal, String seInfo, String niceName, int[] fdsToClose,
            int[] fdsToIgnore, boolean startChildZygote, String instructionSet, String appDataDir,
            String packageName, String[] packagesForUid, String[] visibleVolIds);
    private static native int nativeForkAndSpecialize(int uid, int gid, int[] gids,
            int runtimeFlags, int[][] rlimits, int mountExternal, String seInfo, String niceName,
            int[] fdsToClose, int[] fdsToIgnore, boolean startChildZygote, String instructionSet,
            String appDataDir, String packageName, String[] packagesForUid, String[] visibleVolIds);

    private static native void nativeSpecializeBlastula(int uid, int gid, int[] gids,
            int runtimeFlags, int[][] rlimits, int mountExternal, String seInfo, String niceName,
            boolean startChildZygote, String instructionSet, String appDataDir, String packageName,
            String[] packagesForUid, String[] visibleVolIds);

    /**
     * Called to do any initialization before starting an application.
     */
    native static void nativePreApplicationInit();
    static native void nativePreApplicationInit();

    /**
     * Special method to start the system server process. In addition to the
@@ -223,7 +228,8 @@ public final class Zygote {
        // Resets nice priority for zygote process.
        resetNicePriority();
        int pid = nativeForkSystemServer(
                uid, gid, gids, runtimeFlags, rlimits, permittedCapabilities, effectiveCapabilities);
                uid, gid, gids, runtimeFlags, rlimits,
                permittedCapabilities, effectiveCapabilities);
        // Enable tracing as soon as we enter the system_server.
        if (pid == 0) {
            Trace.setTracingEnabled(true, runtimeFlags);
@@ -232,13 +238,13 @@ public final class Zygote {
        return pid;
    }

    native private static int nativeForkSystemServer(int uid, int gid, int[] gids, int runtimeFlags,
    private static native int nativeForkSystemServer(int uid, int gid, int[] gids, int runtimeFlags,
            int[][] rlimits, long permittedCapabilities, long effectiveCapabilities);

    /**
     * Lets children of the zygote inherit open file descriptors to this path.
     */
    native protected static void nativeAllowFileAcrossFork(String path);
    protected static native void nativeAllowFileAcrossFork(String path);

    /**
     * Installs a seccomp filter that limits setresuid()/setresgid() to the passed-in range
@@ -251,7 +257,22 @@ public final class Zygote {
     * Zygote unmount storage space on initializing.
     * This method is called once.
     */
    native protected static void nativeUnmountStorageOnInit();
    protected static native void nativeUnmountStorageOnInit();

    protected static native void nativeGetSocketFDs(boolean isPrimary);

    private static native int nativeGetBlastulaPoolCount();

    private static native int nativeGetBlastulaPoolEventFD();

    private static native int nativeForkBlastula(int readPipeFD,
                                                 int writePipeFD,
                                                 int[] sessionSocketRawFDs);

    private static native int[] nativeGetBlastulaPipeFDs();

    private static native boolean nativeRemoveBlastulaTableEntry(int blastulaPID);


    private static void callPostForkSystemServerHooks() {
        // SystemServer specific post fork hooks run before child post fork hooks.
+778 −324

File changed.

Preview size limit exceeded, changes collapsed.

+116 −157
Original line number Diff line number Diff line
@@ -136,15 +136,14 @@ FileDescriptorWhitelist* FileDescriptorWhitelist::instance_ = nullptr;
// open zygote file descriptor.
class FileDescriptorInfo {
 public:
  // Create a FileDescriptorInfo for a given file descriptor. Returns
  // |NULL| if an error occurred.
  static FileDescriptorInfo* CreateFromFd(int fd, std::string* error_msg);
  // Create a FileDescriptorInfo for a given file descriptor.
  static FileDescriptorInfo* CreateFromFd(int fd, fail_fn_t fail_fn);

  // Checks whether the file descriptor associated with this object
  // refers to the same description.
  bool Restat() const;
  bool RefersToSameFile() const;

  bool ReopenOrDetach(std::string* error_msg) const;
  void ReopenOrDetach(fail_fn_t fail_fn) const;

  const int fd;
  const struct stat stat;
@@ -170,19 +169,18 @@ class FileDescriptorInfo {
  //   address).
  static bool GetSocketName(const int fd, std::string* result);

  bool DetachSocket(std::string* error_msg) const;
  void DetachSocket(fail_fn_t fail_fn) const;

  DISALLOW_COPY_AND_ASSIGN(FileDescriptorInfo);
};

// static
FileDescriptorInfo* FileDescriptorInfo::CreateFromFd(int fd, std::string* error_msg) {
FileDescriptorInfo* FileDescriptorInfo::CreateFromFd(int fd, fail_fn_t fail_fn) {
  struct stat f_stat;
  // This should never happen; the zygote should always have the right set
  // of permissions required to stat all its open files.
  if (TEMP_FAILURE_RETRY(fstat(fd, &f_stat)) == -1) {
    *error_msg = android::base::StringPrintf("Unable to stat %d", fd);
    return nullptr;
    fail_fn(android::base::StringPrintf("Unable to stat %d", fd));
  }

  const FileDescriptorWhitelist* whitelist = FileDescriptorWhitelist::Get();
@@ -190,15 +188,13 @@ FileDescriptorInfo* FileDescriptorInfo::CreateFromFd(int fd, std::string* error_
  if (S_ISSOCK(f_stat.st_mode)) {
    std::string socket_name;
    if (!GetSocketName(fd, &socket_name)) {
      *error_msg = "Unable to get socket name";
      return nullptr;
      fail_fn("Unable to get socket name");
    }

    if (!whitelist->IsAllowed(socket_name)) {
      *error_msg = android::base::StringPrintf("Socket name not whitelisted : %s (fd=%d)",
      fail_fn(android::base::StringPrintf("Socket name not whitelisted : %s (fd=%d)",
                                          socket_name.c_str(),
                                               fd);
      return nullptr;
                                          fd));
    }

    return new FileDescriptorInfo(fd);
@@ -211,26 +207,35 @@ FileDescriptorInfo* FileDescriptorInfo::CreateFromFd(int fd, std::string* error_
  // S_ISDIR : Not supported. (We could if we wanted to, but it's unused).
  // S_ISLINK : Not supported.
  // S_ISBLK : Not supported.
  // S_ISFIFO : Not supported. Note that the zygote uses pipes to communicate
  // with the child process across forks but those should have been closed
  // before we got to this point.
  // S_ISFIFO : Not supported. Note that the Zygote and blastulas use pipes to
  // communicate with the child processes across forks but those should have been
  // added to the redirection exemption list.
  if (!S_ISCHR(f_stat.st_mode) && !S_ISREG(f_stat.st_mode)) {
    *error_msg = android::base::StringPrintf("Unsupported st_mode %u", f_stat.st_mode);
    return nullptr;
    std::string mode = "Unknown";

    if (S_ISDIR(f_stat.st_mode)) {
      mode = "DIR";
    } else if (S_ISLNK(f_stat.st_mode)) {
      mode = "LINK";
    } else if (S_ISBLK(f_stat.st_mode)) {
      mode = "BLOCK";
    } else if (S_ISFIFO(f_stat.st_mode)) {
      mode = "FIFO";
    }

    fail_fn(android::base::StringPrintf("Unsupported st_mode for FD %d:  %s", fd, mode.c_str()));
  }

  std::string file_path;
  const std::string fd_path = android::base::StringPrintf("/proc/self/fd/%d", fd);
  if (!android::base::Readlink(fd_path, &file_path)) {
    *error_msg = android::base::StringPrintf("Could not read fd link %s: %s",
    fail_fn(android::base::StringPrintf("Could not read fd link %s: %s",
                                        fd_path.c_str(),
                                             strerror(errno));
    return nullptr;
                                        strerror(errno)));
  }

  if (!whitelist->IsAllowed(file_path)) {
    *error_msg = std::string("Not whitelisted : ").append(file_path);
    return nullptr;
    fail_fn(std::string("Not whitelisted : ").append(file_path));
  }

  // File descriptor flags : currently on FD_CLOEXEC. We can set these
@@ -238,11 +243,10 @@ FileDescriptorInfo* FileDescriptorInfo::CreateFromFd(int fd, std::string* error_
  // there won't be any races.
  const int fd_flags = TEMP_FAILURE_RETRY(fcntl(fd, F_GETFD));
  if (fd_flags == -1) {
    *error_msg = android::base::StringPrintf("Failed fcntl(%d, F_GETFD) (%s): %s",
    fail_fn(android::base::StringPrintf("Failed fcntl(%d, F_GETFD) (%s): %s",
                                        fd,
                                        file_path.c_str(),
                                             strerror(errno));
    return nullptr;
                                        strerror(errno)));
  }

  // File status flags :
@@ -259,11 +263,10 @@ FileDescriptorInfo* FileDescriptorInfo::CreateFromFd(int fd, std::string* error_
  //   their presence and pass them in to open().
  int fs_flags = TEMP_FAILURE_RETRY(fcntl(fd, F_GETFL));
  if (fs_flags == -1) {
    *error_msg = android::base::StringPrintf("Failed fcntl(%d, F_GETFL) (%s): %s",
    fail_fn(android::base::StringPrintf("Failed fcntl(%d, F_GETFL) (%s): %s",
                                        fd,
                                        file_path.c_str(),
                                             strerror(errno));
    return nullptr;
                                        strerror(errno)));
  }

  // File offset : Ignore the offset for non seekable files.
@@ -278,7 +281,7 @@ FileDescriptorInfo* FileDescriptorInfo::CreateFromFd(int fd, std::string* error_
  return new FileDescriptorInfo(f_stat, file_path, fd, open_flags, fd_flags, fs_flags, offset);
}

bool FileDescriptorInfo::Restat() const {
bool FileDescriptorInfo::RefersToSameFile() const {
  struct stat f_stat;
  if (TEMP_FAILURE_RETRY(fstat(fd, &f_stat)) == -1) {
    PLOG(ERROR) << "Unable to restat fd " << fd;
@@ -288,9 +291,9 @@ bool FileDescriptorInfo::Restat() const {
  return f_stat.st_ino == stat.st_ino && f_stat.st_dev == stat.st_dev;
}

bool FileDescriptorInfo::ReopenOrDetach(std::string* error_msg) const {
void FileDescriptorInfo::ReopenOrDetach(fail_fn_t fail_fn) const {
  if (is_sock) {
    return DetachSocket(error_msg);
    return DetachSocket(fail_fn);
  }

  // NOTE: This might happen if the file was unlinked after being opened.
@@ -299,57 +302,50 @@ bool FileDescriptorInfo::ReopenOrDetach(std::string* error_msg) const {
  const int new_fd = TEMP_FAILURE_RETRY(open(file_path.c_str(), open_flags));

  if (new_fd == -1) {
    *error_msg = android::base::StringPrintf("Failed open(%s, %i): %s",
    fail_fn(android::base::StringPrintf("Failed open(%s, %i): %s",
                                        file_path.c_str(),
                                        open_flags,
                                             strerror(errno));
    return false;
                                        strerror(errno)));
  }

  if (TEMP_FAILURE_RETRY(fcntl(new_fd, F_SETFD, fd_flags)) == -1) {
    close(new_fd);
    *error_msg = android::base::StringPrintf("Failed fcntl(%d, F_SETFD, %d) (%s): %s",
    fail_fn(android::base::StringPrintf("Failed fcntl(%d, F_SETFD, %d) (%s): %s",
                                        new_fd,
                                        fd_flags,
                                        file_path.c_str(),
                                             strerror(errno));
    return false;
                                        strerror(errno)));
  }

  if (TEMP_FAILURE_RETRY(fcntl(new_fd, F_SETFL, fs_flags)) == -1) {
    close(new_fd);
    *error_msg = android::base::StringPrintf("Failed fcntl(%d, F_SETFL, %d) (%s): %s",
    fail_fn(android::base::StringPrintf("Failed fcntl(%d, F_SETFL, %d) (%s): %s",
                                        new_fd,
                                        fs_flags,
                                        file_path.c_str(),
                                             strerror(errno));
    return false;
                                        strerror(errno)));
  }

  if (offset != -1 && TEMP_FAILURE_RETRY(lseek64(new_fd, offset, SEEK_SET)) == -1) {
    close(new_fd);
    *error_msg = android::base::StringPrintf("Failed lseek64(%d, SEEK_SET) (%s): %s",
    fail_fn(android::base::StringPrintf("Failed lseek64(%d, SEEK_SET) (%s): %s",
                                        new_fd,
                                        file_path.c_str(),
                                             strerror(errno));
    return false;
                                        strerror(errno)));
  }

  int dupFlags = (fd_flags & FD_CLOEXEC) ? O_CLOEXEC : 0;
  if (TEMP_FAILURE_RETRY(dup3(new_fd, fd, dupFlags)) == -1) {
  int dup_flags = (fd_flags & FD_CLOEXEC) ? O_CLOEXEC : 0;
  if (TEMP_FAILURE_RETRY(dup3(new_fd, fd, dup_flags)) == -1) {
    close(new_fd);
    *error_msg = android::base::StringPrintf("Failed dup3(%d, %d, %d) (%s): %s",
    fail_fn(android::base::StringPrintf("Failed dup3(%d, %d, %d) (%s): %s",
                                        fd,
                                        new_fd,
                                             dupFlags,
                                        dup_flags,
                                        file_path.c_str(),
                                             strerror(errno));
    return false;
                                        strerror(errno)));
  }

  close(new_fd);

  return true;
}

FileDescriptorInfo::FileDescriptorInfo(int fd) :
@@ -375,7 +371,6 @@ FileDescriptorInfo::FileDescriptorInfo(struct stat stat, const std::string& file
  is_sock(false) {
}

// static
bool FileDescriptorInfo::GetSocketName(const int fd, std::string* result) {
  sockaddr_storage ss;
  sockaddr* addr = reinterpret_cast<sockaddr*>(&ss);
@@ -419,86 +414,75 @@ bool FileDescriptorInfo::GetSocketName(const int fd, std::string* result) {
  return true;
}

bool FileDescriptorInfo::DetachSocket(std::string* error_msg) const {
void FileDescriptorInfo::DetachSocket(fail_fn_t fail_fn) const {
  const int dev_null_fd = open("/dev/null", O_RDWR);
  if (dev_null_fd < 0) {
    *error_msg = std::string("Failed to open /dev/null: ").append(strerror(errno));
    return false;
    fail_fn(std::string("Failed to open /dev/null: ").append(strerror(errno)));
  }

  if (dup2(dev_null_fd, fd) == -1) {
    *error_msg = android::base::StringPrintf("Failed dup2 on socket descriptor %d: %s",
    fail_fn(android::base::StringPrintf("Failed dup2 on socket descriptor %d: %s",
                                        fd,
                                             strerror(errno));
    return false;
                                        strerror(errno)));
  }

  if (close(dev_null_fd) == -1) {
    *error_msg = android::base::StringPrintf("Failed close(%d): %s", dev_null_fd, strerror(errno));
    return false;
    fail_fn(android::base::StringPrintf("Failed close(%d): %s", dev_null_fd, strerror(errno)));
  }

  return true;
}

// static
FileDescriptorTable* FileDescriptorTable::Create(const std::vector<int>& fds_to_ignore,
                                                 std::string* error_msg) {
  DIR* d = opendir(kFdPath);
  if (d == nullptr) {
    *error_msg = std::string("Unable to open directory ").append(kFdPath);
    return nullptr;
                                                 fail_fn_t fail_fn) {
  DIR* proc_fd_dir = opendir(kFdPath);
  if (proc_fd_dir == nullptr) {
    fail_fn(std::string("Unable to open directory ").append(kFdPath));
  }
  int dir_fd = dirfd(d);
  dirent* e;

  int dir_fd = dirfd(proc_fd_dir);
  dirent* dir_entry;

  std::unordered_map<int, FileDescriptorInfo*> open_fd_map;
  while ((e = readdir(d)) != NULL) {
    const int fd = ParseFd(e, dir_fd);
  while ((dir_entry = readdir(proc_fd_dir)) != nullptr) {
    const int fd = ParseFd(dir_entry, dir_fd);
    if (fd == -1) {
      continue;
    }

    if (std::find(fds_to_ignore.begin(), fds_to_ignore.end(), fd) != fds_to_ignore.end()) {
      LOG(INFO) << "Ignoring open file descriptor " << fd;
      continue;
    }

    FileDescriptorInfo* info = FileDescriptorInfo::CreateFromFd(fd, error_msg);
    if (info == NULL) {
      if (closedir(d) == -1) {
        PLOG(ERROR) << "Unable to close directory";
      }
      return NULL;
    }
    open_fd_map[fd] = info;
    open_fd_map[fd] = FileDescriptorInfo::CreateFromFd(fd, fail_fn);
  }

  if (closedir(d) == -1) {
    *error_msg = "Unable to close directory";
    return nullptr;
  if (closedir(proc_fd_dir) == -1) {
    fail_fn("Unable to close directory");
  }

  return new FileDescriptorTable(open_fd_map);
}

bool FileDescriptorTable::Restat(const std::vector<int>& fds_to_ignore, std::string* error_msg) {
void FileDescriptorTable::Restat(const std::vector<int>& fds_to_ignore, fail_fn_t fail_fn) {
  std::set<int> open_fds;

  // First get the list of open descriptors.
  DIR* d = opendir(kFdPath);
  if (d == NULL) {
    *error_msg = android::base::StringPrintf("Unable to open directory %s: %s",
  DIR* proc_fd_dir = opendir(kFdPath);
  if (proc_fd_dir == nullptr) {
    fail_fn(android::base::StringPrintf("Unable to open directory %s: %s",
                                        kFdPath,
                                             strerror(errno));
    return false;
                                        strerror(errno)));
  }

  int dir_fd = dirfd(d);
  dirent* e;
  while ((e = readdir(d)) != NULL) {
    const int fd = ParseFd(e, dir_fd);
  int dir_fd = dirfd(proc_fd_dir);
  dirent* dir_entry;
  while ((dir_entry = readdir(proc_fd_dir)) != nullptr) {
    const int fd = ParseFd(dir_entry, dir_fd);
    if (fd == -1) {
      continue;
    }

    if (std::find(fds_to_ignore.begin(), fds_to_ignore.end(), fd) != fds_to_ignore.end()) {
      LOG(INFO) << "Ignoring open file descriptor " << fd;
      continue;
@@ -507,27 +491,24 @@ bool FileDescriptorTable::Restat(const std::vector<int>& fds_to_ignore, std::str
    open_fds.insert(fd);
  }

  if (closedir(d) == -1) {
    *error_msg = android::base::StringPrintf("Unable to close directory: %s", strerror(errno));
    return false;
  if (closedir(proc_fd_dir) == -1) {
    fail_fn(android::base::StringPrintf("Unable to close directory: %s", strerror(errno)));
  }

  return RestatInternal(open_fds, error_msg);
  RestatInternal(open_fds, fail_fn);
}

// Reopens all file descriptors that are contained in the table. Returns true
// if all descriptors were successfully re-opened or detached, and false if an
// error occurred.
bool FileDescriptorTable::ReopenOrDetach(std::string* error_msg) {
// Reopens all file descriptors that are contained in the table.
void FileDescriptorTable::ReopenOrDetach(fail_fn_t fail_fn) {
  std::unordered_map<int, FileDescriptorInfo*>::const_iterator it;
  for (it = open_fd_map_.begin(); it != open_fd_map_.end(); ++it) {
    const FileDescriptorInfo* info = it->second;
    if (info == NULL || !info->ReopenOrDetach(error_msg)) {
      return false;
    if (info == nullptr) {
      return;
    } else {
      info->ReopenOrDetach(fail_fn);
    }
  }

  return true;
}

FileDescriptorTable::FileDescriptorTable(
@@ -535,9 +516,7 @@ FileDescriptorTable::FileDescriptorTable(
    : open_fd_map_(map) {
}

bool FileDescriptorTable::RestatInternal(std::set<int>& open_fds, std::string* error_msg) {
  bool error = false;

void FileDescriptorTable::RestatInternal(std::set<int>& open_fds, fail_fn_t fail_fn) {
  // Iterate through the list of file descriptors we've already recorded
  // and check whether :
  //
@@ -560,28 +539,18 @@ bool FileDescriptorTable::RestatInternal(std::set<int>& open_fds, std::string* e
    } else {
      // The entry from the file descriptor table is still open. Restat
      // it and check whether it refers to the same file.
      const bool same_file = it->second->Restat();
      if (!same_file) {
      if (!it->second->RefersToSameFile()) {
        // The file descriptor refers to a different description. We must
        // update our entry in the table.
        delete it->second;
        it->second = FileDescriptorInfo::CreateFromFd(*element, error_msg);
        if (it->second == NULL) {
          // The descriptor no longer no longer refers to a whitelisted file.
          // We flag an error and remove it from the list of files we're
          // tracking.
          error = true;
          it = open_fd_map_.erase(it);
        } else {
          // Successfully restatted the file, move on to the next open FD.
          ++it;
        }
        it->second = FileDescriptorInfo::CreateFromFd(*element, fail_fn);
      } else {
        // It's the same file. Nothing to do here. Move on to the next open
        // FD.
        ++it;
      }

      ++it;

      // Finally, remove the FD from the set of open_fds. We do this last because
      // |element| will not remain valid after a call to erase.
      open_fds.erase(element);
@@ -600,25 +569,15 @@ bool FileDescriptorTable::RestatInternal(std::set<int>& open_fds, std::string* e
    std::set<int>::const_iterator it;
    for (it = open_fds.begin(); it != open_fds.end(); ++it) {
      const int fd = (*it);
      FileDescriptorInfo* info = FileDescriptorInfo::CreateFromFd(fd, error_msg);
      if (info == NULL) {
        // A newly opened file is not on the whitelist. Flag an error and
        // continue.
        error = true;
      } else {
        // Track the newly opened file.
        open_fd_map_[fd] = info;
      open_fd_map_[fd] = FileDescriptorInfo::CreateFromFd(fd, fail_fn);
    }
  }
}

  return !error;
}

// static
int FileDescriptorTable::ParseFd(dirent* e, int dir_fd) {
int FileDescriptorTable::ParseFd(dirent* dir_entry, int dir_fd) {
  char* end;
  const int fd = strtol(e->d_name, &end, 10);
  const int fd = strtol(dir_entry->d_name, &end, 10);
  if ((*end) != '\0') {
    return -1;
  }
+7 −4
Original line number Diff line number Diff line
@@ -30,6 +30,9 @@

class FileDescriptorInfo;

// This type is duplicated in com_android_internal_os_Zygote.cpp
typedef const std::function<void(std::string)>& fail_fn_t;

// Whitelist of open paths that the zygote is allowed to keep open.
//
// In addition to the paths listed in kPathWhitelist in file_utils.cpp, and
@@ -76,19 +79,19 @@ class FileDescriptorTable {
  // /proc/self/fd for the list of open file descriptors and collects
  // information about them. Returns NULL if an error occurs.
  static FileDescriptorTable* Create(const std::vector<int>& fds_to_ignore,
                                     std::string* error_msg);
                                     fail_fn_t fail_fn);

  bool Restat(const std::vector<int>& fds_to_ignore, std::string* error_msg);
  void Restat(const std::vector<int>& fds_to_ignore, fail_fn_t fail_fn);

  // Reopens all file descriptors that are contained in the table. Returns true
  // if all descriptors were successfully re-opened or detached, and false if an
  // error occurred.
  bool ReopenOrDetach(std::string* error_msg);
  void ReopenOrDetach(fail_fn_t fail_fn);

 private:
  explicit FileDescriptorTable(const std::unordered_map<int, FileDescriptorInfo*>& map);

  bool RestatInternal(std::set<int>& open_fds, std::string* error_msg);
  void RestatInternal(std::set<int>& open_fds, fail_fn_t fail_fn);

  static int ParseFd(dirent* e, int dir_fd);