Merge changes I14df050f,I55c30f78,I269eae0f
* changes: Frameworks: Refactor FileDescriptorTable errors Frameworks: Add app name to zygote abort Frameworks: Refactor zygote abort code
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -119,14 +119,57 @@ FileDescriptorWhitelist::FileDescriptorWhitelist()
|
||||
|
||||
FileDescriptorWhitelist* FileDescriptorWhitelist::instance_ = nullptr;
|
||||
|
||||
// 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, std::string* error_msg);
|
||||
|
||||
// Checks whether the file descriptor associated with this object
|
||||
// refers to the same description.
|
||||
bool Restat() const;
|
||||
|
||||
bool ReopenOrDetach(std::string* error_msg) 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(std::string* error_msg) const;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(FileDescriptorInfo);
|
||||
};
|
||||
|
||||
// static
|
||||
FileDescriptorInfo* FileDescriptorInfo::CreateFromFd(int fd) {
|
||||
FileDescriptorInfo* FileDescriptorInfo::CreateFromFd(int fd, std::string* error_msg) {
|
||||
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) {
|
||||
PLOG(ERROR) << "Unable to stat fd " << fd;
|
||||
return NULL;
|
||||
*error_msg = android::base::StringPrintf("Unable to stat %d", fd);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const FileDescriptorWhitelist* whitelist = FileDescriptorWhitelist::Get();
|
||||
@@ -134,13 +177,15 @@ FileDescriptorInfo* FileDescriptorInfo::CreateFromFd(int fd) {
|
||||
if (S_ISSOCK(f_stat.st_mode)) {
|
||||
std::string socket_name;
|
||||
if (!GetSocketName(fd, &socket_name)) {
|
||||
return NULL;
|
||||
*error_msg = "Unable to get socket name";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!whitelist->IsAllowed(socket_name)) {
|
||||
LOG(ERROR) << "Socket name not whitelisted : " << socket_name
|
||||
<< " (fd=" << fd << ")";
|
||||
return NULL;
|
||||
*error_msg = android::base::StringPrintf("Socket name not whitelisted : %s (fd=%d)",
|
||||
socket_name.c_str(),
|
||||
fd);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return new FileDescriptorInfo(fd);
|
||||
@@ -157,19 +202,22 @@ FileDescriptorInfo* FileDescriptorInfo::CreateFromFd(int fd) {
|
||||
// with the child process across forks but those should have been closed
|
||||
// before we got to this point.
|
||||
if (!S_ISCHR(f_stat.st_mode) && !S_ISREG(f_stat.st_mode)) {
|
||||
LOG(ERROR) << "Unsupported st_mode " << f_stat.st_mode;
|
||||
return NULL;
|
||||
*error_msg = android::base::StringPrintf("Unsupported st_mode %u", f_stat.st_mode);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
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)) {
|
||||
return NULL;
|
||||
*error_msg = android::base::StringPrintf("Could not read fd link %s: %s",
|
||||
fd_path.c_str(),
|
||||
strerror(errno));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!whitelist->IsAllowed(file_path)) {
|
||||
LOG(ERROR) << "Not whitelisted : " << file_path;
|
||||
return NULL;
|
||||
*error_msg = std::string("Not whitelisted : ").append(file_path);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// File descriptor flags : currently on FD_CLOEXEC. We can set these
|
||||
@@ -177,8 +225,11 @@ FileDescriptorInfo* FileDescriptorInfo::CreateFromFd(int fd) {
|
||||
// there won't be any races.
|
||||
const int fd_flags = TEMP_FAILURE_RETRY(fcntl(fd, F_GETFD));
|
||||
if (fd_flags == -1) {
|
||||
PLOG(ERROR) << "Failed fcntl(" << fd << ", F_GETFD)";
|
||||
return NULL;
|
||||
*error_msg = android::base::StringPrintf("Failed fcntl(%d, F_GETFD) (%s): %s",
|
||||
fd,
|
||||
file_path.c_str(),
|
||||
strerror(errno));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// File status flags :
|
||||
@@ -195,8 +246,11 @@ FileDescriptorInfo* FileDescriptorInfo::CreateFromFd(int fd) {
|
||||
// their presence and pass them in to open().
|
||||
int fs_flags = TEMP_FAILURE_RETRY(fcntl(fd, F_GETFL));
|
||||
if (fs_flags == -1) {
|
||||
PLOG(ERROR) << "Failed fcntl(" << fd << ", F_GETFL)";
|
||||
return NULL;
|
||||
*error_msg = android::base::StringPrintf("Failed fcntl(%d, F_GETFL) (%s): %s",
|
||||
fd,
|
||||
file_path.c_str(),
|
||||
strerror(errno));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// File offset : Ignore the offset for non seekable files.
|
||||
@@ -221,9 +275,9 @@ bool FileDescriptorInfo::Restat() const {
|
||||
return f_stat.st_ino == stat.st_ino && f_stat.st_dev == stat.st_dev;
|
||||
}
|
||||
|
||||
bool FileDescriptorInfo::ReopenOrDetach() const {
|
||||
bool FileDescriptorInfo::ReopenOrDetach(std::string* error_msg) const {
|
||||
if (is_sock) {
|
||||
return DetachSocket();
|
||||
return DetachSocket(error_msg);
|
||||
}
|
||||
|
||||
// NOTE: This might happen if the file was unlinked after being opened.
|
||||
@@ -232,31 +286,49 @@ bool FileDescriptorInfo::ReopenOrDetach() const {
|
||||
const int new_fd = TEMP_FAILURE_RETRY(open(file_path.c_str(), open_flags));
|
||||
|
||||
if (new_fd == -1) {
|
||||
PLOG(ERROR) << "Failed open(" << file_path << ", " << open_flags << ")";
|
||||
*error_msg = android::base::StringPrintf("Failed open(%s, %i): %s",
|
||||
file_path.c_str(),
|
||||
open_flags,
|
||||
strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (TEMP_FAILURE_RETRY(fcntl(new_fd, F_SETFD, fd_flags)) == -1) {
|
||||
close(new_fd);
|
||||
PLOG(ERROR) << "Failed fcntl(" << new_fd << ", F_SETFD, " << fd_flags << ")";
|
||||
*error_msg = android::base::StringPrintf("Failed fcntl(%d, F_SETFD, %d) (%s): %s",
|
||||
new_fd,
|
||||
fd_flags,
|
||||
file_path.c_str(),
|
||||
strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (TEMP_FAILURE_RETRY(fcntl(new_fd, F_SETFL, fs_flags)) == -1) {
|
||||
close(new_fd);
|
||||
PLOG(ERROR) << "Failed fcntl(" << new_fd << ", F_SETFL, " << fs_flags << ")";
|
||||
*error_msg = android::base::StringPrintf("Failed fcntl(%d, F_SETFL, %d) (%s): %s",
|
||||
new_fd,
|
||||
fs_flags,
|
||||
file_path.c_str(),
|
||||
strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (offset != -1 && TEMP_FAILURE_RETRY(lseek64(new_fd, offset, SEEK_SET)) == -1) {
|
||||
close(new_fd);
|
||||
PLOG(ERROR) << "Failed lseek64(" << new_fd << ", SEEK_SET)";
|
||||
*error_msg = android::base::StringPrintf("Failed lseek64(%d, SEEK_SET) (%s): %s",
|
||||
new_fd,
|
||||
file_path.c_str(),
|
||||
strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (TEMP_FAILURE_RETRY(dup2(new_fd, fd)) == -1) {
|
||||
close(new_fd);
|
||||
PLOG(ERROR) << "Failed dup2(" << fd << ", " << new_fd << ")";
|
||||
*error_msg = android::base::StringPrintf("Failed dup2(%d, %d) (%s): %s",
|
||||
fd,
|
||||
new_fd,
|
||||
file_path.c_str(),
|
||||
strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -332,20 +404,22 @@ bool FileDescriptorInfo::GetSocketName(const int fd, std::string* result) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FileDescriptorInfo::DetachSocket() const {
|
||||
bool FileDescriptorInfo::DetachSocket(std::string* error_msg) const {
|
||||
const int dev_null_fd = open("/dev/null", O_RDWR);
|
||||
if (dev_null_fd < 0) {
|
||||
PLOG(ERROR) << "Failed to open /dev/null";
|
||||
*error_msg = std::string("Failed to open /dev/null: ").append(strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (dup2(dev_null_fd, fd) == -1) {
|
||||
PLOG(ERROR) << "Failed dup2 on socket descriptor " << fd;
|
||||
*error_msg = android::base::StringPrintf("Failed dup2 on socket descriptor %d: %s",
|
||||
fd,
|
||||
strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (close(dev_null_fd) == -1) {
|
||||
PLOG(ERROR) << "Failed close(" << dev_null_fd << ")";
|
||||
*error_msg = android::base::StringPrintf("Failed close(%d): %s", dev_null_fd, strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -353,11 +427,12 @@ bool FileDescriptorInfo::DetachSocket() const {
|
||||
}
|
||||
|
||||
// static
|
||||
FileDescriptorTable* FileDescriptorTable::Create(const std::vector<int>& fds_to_ignore) {
|
||||
FileDescriptorTable* FileDescriptorTable::Create(const std::vector<int>& fds_to_ignore,
|
||||
std::string* error_msg) {
|
||||
DIR* d = opendir(kFdPath);
|
||||
if (d == NULL) {
|
||||
PLOG(ERROR) << "Unable to open directory " << std::string(kFdPath);
|
||||
return NULL;
|
||||
if (d == nullptr) {
|
||||
*error_msg = std::string("Unable to open directory ").append(kFdPath);
|
||||
return nullptr;
|
||||
}
|
||||
int dir_fd = dirfd(d);
|
||||
dirent* e;
|
||||
@@ -373,7 +448,7 @@ FileDescriptorTable* FileDescriptorTable::Create(const std::vector<int>& fds_to_
|
||||
continue;
|
||||
}
|
||||
|
||||
FileDescriptorInfo* info = FileDescriptorInfo::CreateFromFd(fd);
|
||||
FileDescriptorInfo* info = FileDescriptorInfo::CreateFromFd(fd, error_msg);
|
||||
if (info == NULL) {
|
||||
if (closedir(d) == -1) {
|
||||
PLOG(ERROR) << "Unable to close directory";
|
||||
@@ -384,19 +459,21 @@ FileDescriptorTable* FileDescriptorTable::Create(const std::vector<int>& fds_to_
|
||||
}
|
||||
|
||||
if (closedir(d) == -1) {
|
||||
PLOG(ERROR) << "Unable to close directory";
|
||||
return NULL;
|
||||
*error_msg = "Unable to close directory";
|
||||
return nullptr;
|
||||
}
|
||||
return new FileDescriptorTable(open_fd_map);
|
||||
}
|
||||
|
||||
bool FileDescriptorTable::Restat(const std::vector<int>& fds_to_ignore) {
|
||||
bool FileDescriptorTable::Restat(const std::vector<int>& fds_to_ignore, std::string* error_msg) {
|
||||
std::set<int> open_fds;
|
||||
|
||||
// First get the list of open descriptors.
|
||||
DIR* d = opendir(kFdPath);
|
||||
if (d == NULL) {
|
||||
PLOG(ERROR) << "Unable to open directory " << std::string(kFdPath);
|
||||
*error_msg = android::base::StringPrintf("Unable to open directory %s: %s",
|
||||
kFdPath,
|
||||
strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -416,21 +493,21 @@ bool FileDescriptorTable::Restat(const std::vector<int>& fds_to_ignore) {
|
||||
}
|
||||
|
||||
if (closedir(d) == -1) {
|
||||
PLOG(ERROR) << "Unable to close directory";
|
||||
*error_msg = android::base::StringPrintf("Unable to close directory: %s", strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
return RestatInternal(open_fds);
|
||||
return RestatInternal(open_fds, 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 FileDescriptorTable::ReopenOrDetach() {
|
||||
bool FileDescriptorTable::ReopenOrDetach(std::string* error_msg) {
|
||||
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()) {
|
||||
if (info == NULL || !info->ReopenOrDetach(error_msg)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -443,7 +520,7 @@ FileDescriptorTable::FileDescriptorTable(
|
||||
: open_fd_map_(map) {
|
||||
}
|
||||
|
||||
bool FileDescriptorTable::RestatInternal(std::set<int>& open_fds) {
|
||||
bool FileDescriptorTable::RestatInternal(std::set<int>& open_fds, std::string* error_msg) {
|
||||
bool error = false;
|
||||
|
||||
// Iterate through the list of file descriptors we've already recorded
|
||||
@@ -451,6 +528,8 @@ bool FileDescriptorTable::RestatInternal(std::set<int>& open_fds) {
|
||||
//
|
||||
// (a) they continue to be open.
|
||||
// (b) they refer to the same file.
|
||||
//
|
||||
// We'll only store the last error message.
|
||||
std::unordered_map<int, FileDescriptorInfo*>::iterator it = open_fd_map_.begin();
|
||||
while (it != open_fd_map_.end()) {
|
||||
std::set<int>::const_iterator element = open_fds.find(it->first);
|
||||
@@ -471,7 +550,7 @@ bool FileDescriptorTable::RestatInternal(std::set<int>& open_fds) {
|
||||
// The file descriptor refers to a different description. We must
|
||||
// update our entry in the table.
|
||||
delete it->second;
|
||||
it->second = FileDescriptorInfo::CreateFromFd(*element);
|
||||
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
|
||||
@@ -506,7 +585,7 @@ bool FileDescriptorTable::RestatInternal(std::set<int>& open_fds) {
|
||||
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);
|
||||
FileDescriptorInfo* info = FileDescriptorInfo::CreateFromFd(fd, error_msg);
|
||||
if (info == NULL) {
|
||||
// A newly opened file is not on the whitelist. Flag an error and
|
||||
// continue.
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user