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

Commit ad7415f1 authored by Treehugger Robot's avatar Treehugger Robot Committed by Gerrit Code Review
Browse files

Merge changes I14df050f,I55c30f78,I269eae0f

* changes:
  Frameworks: Refactor FileDescriptorTable errors
  Frameworks: Add app name to zygote abort
  Frameworks: Refactor zygote abort code
parents c2493ee1 183a5d3c
Loading
Loading
Loading
Loading
+136 −78
Original line number Diff line number Diff line
@@ -71,6 +71,9 @@ using android::String8;
using android::base::StringPrintf;
using android::base::WriteStringToFile;

#define CREATE_ERROR(...) StringPrintf("%s:%d: ", __FILE__, __LINE__). \
                              append(StringPrintf(__VA_ARGS__))

static pid_t gSystemServerPid = 0;

static const char kZygoteClassName[] = "com/android/internal/os/Zygote";
@@ -186,30 +189,32 @@ static void UnsetChldSignalHandler() {

// Calls POSIX setgroups() using the int[] object as an argument.
// A NULL argument is tolerated.
static void SetGids(JNIEnv* env, jintArray javaGids) {
static bool SetGids(JNIEnv* env, jintArray javaGids, std::string* error_msg) {
  if (javaGids == NULL) {
    return;
    return true;
  }

  ScopedIntArrayRO gids(env, javaGids);
  if (gids.get() == NULL) {
    RuntimeAbort(env, __LINE__, "Getting gids int array failed");
    *error_msg = CREATE_ERROR("Getting gids int array failed");
    return false;
  }
  int rc = setgroups(gids.size(), reinterpret_cast<const gid_t*>(&gids[0]));
  if (rc == -1) {
    std::ostringstream oss;
    oss << "setgroups failed: " << strerror(errno) << ", gids.size=" << gids.size();
    RuntimeAbort(env, __LINE__, oss.str().c_str());
    *error_msg = CREATE_ERROR("setgroups failed: %s, gids.size=%zu", strerror(errno), gids.size());
    return false;
  }

  return true;
}

// Sets the resource limits via setrlimit(2) for the values in the
// two-dimensional array of integers that's passed in. The second dimension
// contains a tuple of length 3: (resource, rlim_cur, rlim_max). NULL is
// treated as an empty array.
static void SetRLimits(JNIEnv* env, jobjectArray javaRlimits) {
static bool SetRLimits(JNIEnv* env, jobjectArray javaRlimits, std::string* error_msg) {
  if (javaRlimits == NULL) {
    return;
    return true;
  }

  rlimit rlim;
@@ -219,7 +224,8 @@ static void SetRLimits(JNIEnv* env, jobjectArray javaRlimits) {
    ScopedLocalRef<jobject> javaRlimitObject(env, env->GetObjectArrayElement(javaRlimits, i));
    ScopedIntArrayRO javaRlimit(env, reinterpret_cast<jintArray>(javaRlimitObject.get()));
    if (javaRlimit.size() != 3) {
      RuntimeAbort(env, __LINE__, "rlimits array must have a second dimension of size 3");
      *error_msg = CREATE_ERROR("rlimits array must have a second dimension of size 3");
      return false;
    }

    rlim.rlim_cur = javaRlimit[1];
@@ -227,11 +233,13 @@ static void SetRLimits(JNIEnv* env, jobjectArray javaRlimits) {

    int rc = setrlimit(javaRlimit[0], &rlim);
    if (rc == -1) {
      ALOGE("setrlimit(%d, {%ld, %ld}) failed", javaRlimit[0], rlim.rlim_cur,
      *error_msg = CREATE_ERROR("setrlimit(%d, {%ld, %ld}) failed", javaRlimit[0], rlim.rlim_cur,
            rlim.rlim_max);
      RuntimeAbort(env, __LINE__, "setrlimit failed");
      return false;
    }
  }

  return true;
}

// The debug malloc library needs to know whether it's the zygote or a child.
@@ -259,14 +267,16 @@ static void SetUpSeccompFilter(uid_t uid) {
  }
}

static void EnableKeepCapabilities(JNIEnv* env) {
static bool EnableKeepCapabilities(std::string* error_msg) {
  int rc = prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0);
  if (rc == -1) {
    RuntimeAbort(env, __LINE__, "prctl(PR_SET_KEEPCAPS) failed");
    *error_msg = CREATE_ERROR("prctl(PR_SET_KEEPCAPS) failed: %s", strerror(errno));
    return false;
  }
  return true;
}

static void DropCapabilitiesBoundingSet(JNIEnv* env) {
static bool DropCapabilitiesBoundingSet(std::string* error_msg) {
  for (int i = 0; prctl(PR_CAPBSET_READ, i, 0, 0, 0) >= 0; i++) {
    int rc = prctl(PR_CAPBSET_DROP, i, 0, 0, 0);
    if (rc == -1) {
@@ -274,14 +284,15 @@ static void DropCapabilitiesBoundingSet(JNIEnv* env) {
        ALOGE("prctl(PR_CAPBSET_DROP) failed with EINVAL. Please verify "
              "your kernel is compiled with file capabilities support");
      } else {
        ALOGE("prctl(PR_CAPBSET_DROP, %d) failed: %s", i, strerror(errno));
        RuntimeAbort(env, __LINE__, "prctl(PR_CAPBSET_DROP) failed");
        *error_msg = CREATE_ERROR("prctl(PR_CAPBSET_DROP, %d) failed: %s", i, strerror(errno));
        return false;
      }
    }
  }
  return true;
}

static void SetInheritable(JNIEnv* env, uint64_t inheritable) {
static bool SetInheritable(uint64_t inheritable, std::string* error_msg) {
  __user_cap_header_struct capheader;
  memset(&capheader, 0, sizeof(capheader));
  capheader.version = _LINUX_CAPABILITY_VERSION_3;
@@ -289,21 +300,23 @@ static void SetInheritable(JNIEnv* env, uint64_t inheritable) {

  __user_cap_data_struct capdata[2];
  if (capget(&capheader, &capdata[0]) == -1) {
    ALOGE("capget failed: %s", strerror(errno));
    RuntimeAbort(env, __LINE__, "capget failed");
    *error_msg = CREATE_ERROR("capget failed: %s", strerror(errno));
    return false;
  }

  capdata[0].inheritable = inheritable;
  capdata[1].inheritable = inheritable >> 32;

  if (capset(&capheader, &capdata[0]) == -1) {
    ALOGE("capset(inh=%" PRIx64 ") failed: %s", inheritable, strerror(errno));
    RuntimeAbort(env, __LINE__, "capset failed");
    *error_msg = CREATE_ERROR("capset(inh=%" PRIx64 ") failed: %s", inheritable, strerror(errno));
    return false;
  }

  return true;
}

static void SetCapabilities(JNIEnv* env, uint64_t permitted, uint64_t effective,
                            uint64_t inheritable) {
static bool SetCapabilities(uint64_t permitted, uint64_t effective, uint64_t inheritable,
                            std::string* error_msg) {
  __user_cap_header_struct capheader;
  memset(&capheader, 0, sizeof(capheader));
  capheader.version = _LINUX_CAPABILITY_VERSION_3;
@@ -319,18 +332,20 @@ static void SetCapabilities(JNIEnv* env, uint64_t permitted, uint64_t effective,
  capdata[1].inheritable = inheritable >> 32;

  if (capset(&capheader, &capdata[0]) == -1) {
    ALOGE("capset(perm=%" PRIx64 ", eff=%" PRIx64 ", inh=%" PRIx64 ") failed: %s", permitted,
          effective, inheritable, strerror(errno));
    RuntimeAbort(env, __LINE__, "capset failed");
    *error_msg = CREATE_ERROR("capset(perm=%" PRIx64 ", eff=%" PRIx64 ", inh=%" PRIx64 ") "
                              "failed: %s", permitted, effective, inheritable, strerror(errno));
    return false;
  }
  return true;
}

static void SetSchedulerPolicy(JNIEnv* env) {
static bool SetSchedulerPolicy(std::string* error_msg) {
  errno = -set_sched_policy(0, SP_DEFAULT);
  if (errno != 0) {
    ALOGE("set_sched_policy(0, SP_DEFAULT) failed");
    RuntimeAbort(env, __LINE__, "set_sched_policy(0, SP_DEFAULT) failed");
    *error_msg = CREATE_ERROR("set_sched_policy(0, SP_DEFAULT) failed: %s", strerror(errno));
    return false;
  }
  return true;
}

static int UnmountTree(const char* path) {
@@ -364,7 +379,7 @@ static int UnmountTree(const char* path) {
// Create a private mount namespace and bind mount appropriate emulated
// storage for the given user.
static bool MountEmulatedStorage(uid_t uid, jint mount_mode,
        bool force_mount_namespace) {
        bool force_mount_namespace, std::string* error_msg) {
    // See storage config details at http://source.android.com/tech/storage/

    String8 storageSource;
@@ -381,7 +396,7 @@ static bool MountEmulatedStorage(uid_t uid, jint mount_mode,

    // Create a second private mount namespace for our process
    if (unshare(CLONE_NEWNS) == -1) {
        ALOGW("Failed to unshare(): %s", strerror(errno));
        *error_msg = CREATE_ERROR("Failed to unshare(): %s", strerror(errno));
        return false;
    }

@@ -392,7 +407,9 @@ static bool MountEmulatedStorage(uid_t uid, jint mount_mode,

    if (TEMP_FAILURE_RETRY(mount(storageSource.string(), "/storage",
            NULL, MS_BIND | MS_REC | MS_SLAVE, NULL)) == -1) {
        ALOGW("Failed to mount %s to /storage: %s", storageSource.string(), strerror(errno));
        *error_msg = CREATE_ERROR("Failed to mount %s to /storage: %s",
                                  storageSource.string(),
                                  strerror(errno));
        return false;
    }

@@ -400,11 +417,14 @@ static bool MountEmulatedStorage(uid_t uid, jint mount_mode,
    userid_t user_id = multiuser_get_user_id(uid);
    const String8 userSource(String8::format("/mnt/user/%d", user_id));
    if (fs_prepare_dir(userSource.string(), 0751, 0, 0) == -1) {
        *error_msg = CREATE_ERROR("fs_prepare_dir failed on %s", userSource.string());
        return false;
    }
    if (TEMP_FAILURE_RETRY(mount(userSource.string(), "/storage/self",
            NULL, MS_BIND, NULL)) == -1) {
        ALOGW("Failed to mount %s to /storage/self: %s", userSource.string(), strerror(errno));
        *error_msg = CREATE_ERROR("Failed to mount %s to /storage/self: %s",
                                  userSource.string(),
                                  strerror(errno));
        return false;
    }

@@ -436,31 +456,32 @@ static bool NeedsNoRandomizeWorkaround() {
// descriptor (if any) is closed via dup2(), replacing it with a valid
// (open) descriptor to /dev/null.

static void DetachDescriptors(JNIEnv* env, jintArray fdsToClose) {
static bool DetachDescriptors(JNIEnv* env, jintArray fdsToClose, std::string* error_msg) {
  if (!fdsToClose) {
    return;
    return true;
  }
  jsize count = env->GetArrayLength(fdsToClose);
  ScopedIntArrayRO ar(env, fdsToClose);
  if (ar.get() == NULL) {
      RuntimeAbort(env, __LINE__, "Bad fd array");
    *error_msg = "Bad fd array";
    return false;
  }
  jsize i;
  int devnull;
  for (i = 0; i < count; i++) {
    devnull = open("/dev/null", O_RDWR);
    if (devnull < 0) {
      ALOGE("Failed to open /dev/null: %s", strerror(errno));
      RuntimeAbort(env, __LINE__, "Failed to open /dev/null");
      continue;
      *error_msg = std::string("Failed to open /dev/null: ").append(strerror(errno));
      return false;
    }
    ALOGV("Switching descriptor %d to /dev/null: %s", ar[i], strerror(errno));
    if (dup2(devnull, ar[i]) < 0) {
      ALOGE("Failed dup2() on descriptor %d: %s", ar[i], strerror(errno));
      RuntimeAbort(env, __LINE__, "Failed dup2()");
      *error_msg = StringPrintf("Failed dup2() on descriptor %d: %s", ar[i], strerror(errno));
      return false;
    }
    close(devnull);
  }
  return true;
}

void SetThreadName(const char* thread_name) {
@@ -495,20 +516,23 @@ void SetThreadName(const char* thread_name) {
// The list of open zygote file descriptors.
static FileDescriptorTable* gOpenFdTable = NULL;

static void FillFileDescriptorVector(JNIEnv* env,
static bool FillFileDescriptorVector(JNIEnv* env,
                                     jintArray java_fds,
                                     std::vector<int>* fds) {
                                     std::vector<int>* fds,
                                     std::string* error_msg) {
  CHECK(fds != nullptr);
  if (java_fds != nullptr) {
    ScopedIntArrayRO ar(env, java_fds);
    if (ar.get() == nullptr) {
      RuntimeAbort(env, __LINE__, "Bad fd array");
      *error_msg = "Bad fd array";
      return false;
    }
    fds->reserve(ar.size());
    for (size_t i = 0; i < ar.size(); ++i) {
      fds->push_back(ar[i]);
    }
  }
  return true;
}

// Utility routine to fork zygote and specialize the child process.
@@ -526,32 +550,53 @@ static pid_t ForkAndSpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArra
  sigemptyset(&sigchld);
  sigaddset(&sigchld, SIGCHLD);

  auto fail_fn = [env, java_se_name, is_system_server](const std::string& msg)
      __attribute__ ((noreturn)) {
    const char* se_name_c_str = nullptr;
    std::unique_ptr<ScopedUtfChars> se_name;
    if (java_se_name != nullptr) {
      se_name.reset(new ScopedUtfChars(env, java_se_name));
      se_name_c_str = se_name->c_str();
    }
    if (se_name_c_str == nullptr && is_system_server) {
      se_name_c_str = "system_server";
    }
    const std::string& error_msg = (se_name_c_str == nullptr)
        ? msg
        : StringPrintf("(%s) %s", se_name_c_str, msg.c_str());
    env->FatalError(error_msg.c_str());
    __builtin_unreachable();
  };

  // Temporarily block SIGCHLD during forks. The SIGCHLD handler might
  // log, which would result in the logging FDs we close being reopened.
  // This would cause failures because the FDs are not whitelisted.
  //
  // Note that the zygote process is single threaded at this point.
  if (sigprocmask(SIG_BLOCK, &sigchld, nullptr) == -1) {
    ALOGE("sigprocmask(SIG_SETMASK, { SIGCHLD }) failed: %s", strerror(errno));
    RuntimeAbort(env, __LINE__, "Call to sigprocmask(SIG_BLOCK, { SIGCHLD }) failed.");
    fail_fn(CREATE_ERROR("sigprocmask(SIG_SETMASK, { SIGCHLD }) failed: %s", strerror(errno)));
  }

  // Close any logging related FDs before we start evaluating the list of
  // file descriptors.
  __android_log_close();

  std::string error_msg;

  // If this is the first fork for this zygote, create the open FD table.
  // If it isn't, we just need to check whether the list of open files has
  // changed (and it shouldn't in the normal case).
  std::vector<int> fds_to_ignore;
  FillFileDescriptorVector(env, fdsToIgnore, &fds_to_ignore);
  if (!FillFileDescriptorVector(env, fdsToIgnore, &fds_to_ignore, &error_msg)) {
    fail_fn(error_msg);
  }
  if (gOpenFdTable == NULL) {
    gOpenFdTable = FileDescriptorTable::Create(fds_to_ignore);
    gOpenFdTable = FileDescriptorTable::Create(fds_to_ignore, &error_msg);
    if (gOpenFdTable == NULL) {
      RuntimeAbort(env, __LINE__, "Unable to construct file descriptor table.");
      fail_fn(error_msg);
    }
  } else if (!gOpenFdTable->Restat(fds_to_ignore)) {
    RuntimeAbort(env, __LINE__, "Unable to restat file descriptor table.");
  } else if (!gOpenFdTable->Restat(fds_to_ignore, &error_msg)) {
    fail_fn(error_msg);
  }

  pid_t pid = fork();
@@ -560,17 +605,18 @@ static pid_t ForkAndSpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArra
    PreApplicationInit();

    // Clean up any descriptors which must be closed immediately
    DetachDescriptors(env, fdsToClose);
    if (!DetachDescriptors(env, fdsToClose, &error_msg)) {
      fail_fn(error_msg);
    }

    // Re-open all remaining open file descriptors so that they aren't shared
    // with the zygote across a fork.
    if (!gOpenFdTable->ReopenOrDetach()) {
      RuntimeAbort(env, __LINE__, "Unable to reopen whitelisted descriptors.");
    if (!gOpenFdTable->ReopenOrDetach(&error_msg)) {
      fail_fn(error_msg);
    }

    if (sigprocmask(SIG_UNBLOCK, &sigchld, nullptr) == -1) {
      ALOGE("sigprocmask(SIG_SETMASK, { SIGCHLD }) failed: %s", strerror(errno));
      RuntimeAbort(env, __LINE__, "Call to sigprocmask(SIG_UNBLOCK, { SIGCHLD }) failed.");
      fail_fn(CREATE_ERROR("sigprocmask(SIG_SETMASK, { SIGCHLD }) failed: %s", strerror(errno)));
    }

    // Must be called when the new process still has CAP_SYS_ADMIN.  The other alternative is to
@@ -580,11 +626,17 @@ static pid_t ForkAndSpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArra

    // Keep capabilities across UID change, unless we're staying root.
    if (uid != 0) {
      EnableKeepCapabilities(env);
      if (!EnableKeepCapabilities(&error_msg)) {
        fail_fn(error_msg);
      }
    }

    SetInheritable(env, permittedCapabilities);
    DropCapabilitiesBoundingSet(env);
    if (!SetInheritable(permittedCapabilities, &error_msg)) {
      fail_fn(error_msg);
    }
    if (!DropCapabilitiesBoundingSet(&error_msg)) {
      fail_fn(error_msg);
    }

    bool use_native_bridge = !is_system_server && (instructionSet != NULL)
        && android::NativeBridgeAvailable();
@@ -601,8 +653,8 @@ static pid_t ForkAndSpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArra
      ALOGW("Native bridge will not be used because dataDir == NULL.");
    }

    if (!MountEmulatedStorage(uid, mount_external, use_native_bridge)) {
      ALOGW("Failed to mount emulated storage: %s", strerror(errno));
    if (!MountEmulatedStorage(uid, mount_external, use_native_bridge, &error_msg)) {
      ALOGW("Failed to mount emulated storage: %s (%s)", error_msg.c_str(), strerror(errno));
      if (errno == ENOTCONN || errno == EROFS) {
        // When device is actively encrypting, we get ENOTCONN here
        // since FUSE was mounted before the framework restarted.
@@ -610,7 +662,7 @@ static pid_t ForkAndSpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArra
        // FUSE hasn't been created yet by init.
        // In either case, continue without external storage.
      } else {
        RuntimeAbort(env, __LINE__, "Cannot continue without emulated storage");
        fail_fn(error_msg);
      }
    }

@@ -625,9 +677,14 @@ static pid_t ForkAndSpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArra
        }
    }

    SetGids(env, javaGids);
    std::string error_msg;
    if (!SetGids(env, javaGids, &error_msg)) {
      fail_fn(error_msg);
    }

    SetRLimits(env, javaRlimits);
    if (!SetRLimits(env, javaRlimits, &error_msg)) {
      fail_fn(error_msg);
    }

    if (use_native_bridge) {
      ScopedUtfChars isa_string(env, instructionSet);
@@ -637,14 +694,12 @@ static pid_t ForkAndSpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArra

    int rc = setresgid(gid, gid, gid);
    if (rc == -1) {
      ALOGE("setresgid(%d) failed: %s", gid, strerror(errno));
      RuntimeAbort(env, __LINE__, "setresgid failed");
      fail_fn(CREATE_ERROR("setresgid(%d) failed: %s", gid, strerror(errno)));
    }

    rc = setresuid(uid, uid, uid);
    if (rc == -1) {
      ALOGE("setresuid(%d) failed: %s", uid, strerror(errno));
      RuntimeAbort(env, __LINE__, "setresuid failed");
      fail_fn(CREATE_ERROR("setresuid(%d) failed: %s", uid, strerror(errno)));
    }

    if (NeedsNoRandomizeWorkaround()) {
@@ -656,9 +711,14 @@ static pid_t ForkAndSpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArra
        }
    }

    SetCapabilities(env, permittedCapabilities, effectiveCapabilities, permittedCapabilities);
    if (!SetCapabilities(permittedCapabilities, effectiveCapabilities, permittedCapabilities,
                         &error_msg)) {
      fail_fn(error_msg);
    }

    SetSchedulerPolicy(env);
    if (!SetSchedulerPolicy(&error_msg)) {
      fail_fn(error_msg);
    }

    const char* se_info_c_str = NULL;
    ScopedUtfChars* se_info = NULL;
@@ -666,7 +726,7 @@ static pid_t ForkAndSpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArra
        se_info = new ScopedUtfChars(env, java_se_info);
        se_info_c_str = se_info->c_str();
        if (se_info_c_str == NULL) {
          RuntimeAbort(env, __LINE__, "se_info_c_str == NULL");
          fail_fn("se_info_c_str == NULL");
        }
    }
    const char* se_name_c_str = NULL;
@@ -675,14 +735,13 @@ static pid_t ForkAndSpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArra
        se_name = new ScopedUtfChars(env, java_se_name);
        se_name_c_str = se_name->c_str();
        if (se_name_c_str == NULL) {
          RuntimeAbort(env, __LINE__, "se_name_c_str == NULL");
          fail_fn("se_name_c_str == NULL");
        }
    }
    rc = selinux_android_setcontext(uid, is_system_server, se_info_c_str, se_name_c_str);
    if (rc == -1) {
      ALOGE("selinux_android_setcontext(%d, %d, \"%s\", \"%s\") failed", uid,
            is_system_server, se_info_c_str, se_name_c_str);
      RuntimeAbort(env, __LINE__, "selinux_android_setcontext failed");
      fail_fn(CREATE_ERROR("selinux_android_setcontext(%d, %d, \"%s\", \"%s\") failed", uid,
            is_system_server, se_info_c_str, se_name_c_str));
    }

    // Make it easier to debug audit logs by setting the main thread's name to the
@@ -703,15 +762,14 @@ static pid_t ForkAndSpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArra
    env->CallStaticVoidMethod(gZygoteClass, gCallPostForkChildHooks, runtime_flags,
                              is_system_server, is_child_zygote, instructionSet);
    if (env->ExceptionCheck()) {
      RuntimeAbort(env, __LINE__, "Error calling post fork hooks.");
      fail_fn("Error calling post fork hooks.");
    }
  } else if (pid > 0) {
    // the parent process

    // We blocked SIGCHLD prior to a fork, we unblock it here.
    if (sigprocmask(SIG_UNBLOCK, &sigchld, nullptr) == -1) {
      ALOGE("sigprocmask(SIG_SETMASK, { SIGCHLD }) failed: %s", strerror(errno));
      RuntimeAbort(env, __LINE__, "Call to sigprocmask(SIG_UNBLOCK, { SIGCHLD }) failed.");
      fail_fn(CREATE_ERROR("sigprocmask(SIG_SETMASK, { SIGCHLD }) failed: %s", strerror(errno)));
    }
  }
  return pid;
+122 −43

File changed.

Preview size limit exceeded, changes collapsed.

+7 −47
Original line number Diff line number Diff line
@@ -28,6 +28,8 @@

#include <android-base/macros.h>

class FileDescriptorInfo;

// 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
@@ -66,49 +68,6 @@ class FileDescriptorWhitelist {
  DISALLOW_COPY_AND_ASSIGN(FileDescriptorWhitelist);
};

// Keeps track of all relevant information (flags, offset etc.) of an
// 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);

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

  bool ReopenOrDetach() const;

  const int fd;
  const struct stat stat;
  const std::string file_path;
  const int open_flags;
  const int fd_flags;
  const int fs_flags;
  const off_t offset;
  const bool is_sock;

 private:
  FileDescriptorInfo(int fd);

  FileDescriptorInfo(struct stat stat, const std::string& file_path, int fd, int open_flags,
                     int fd_flags, int fs_flags, off_t offset);

  // Returns the locally-bound name of the socket |fd|. Returns true
  // iff. all of the following hold :
  //
  // - the socket's sa_family is AF_UNIX.
  // - the length of the path is greater than zero (i.e, not an unnamed socket).
  // - the first byte of the path isn't zero (i.e, not a socket with an abstract
  //   address).
  static bool GetSocketName(const int fd, std::string* result);

  bool DetachSocket() const;

  DISALLOW_COPY_AND_ASSIGN(FileDescriptorInfo);
};

// A FileDescriptorTable is a collection of FileDescriptorInfo objects
// keyed by their FDs.
class FileDescriptorTable {
@@ -116,19 +75,20 @@ class FileDescriptorTable {
  // Creates a new FileDescriptorTable. This function scans
  // /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);
  static FileDescriptorTable* Create(const std::vector<int>& fds_to_ignore,
                                     std::string* error_msg);

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

  // 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();
  bool ReopenOrDetach(std::string* error_msg);

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

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

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