Add timeout for fsck on untrusted media
Certain setups of cards can take a long time to fsck.
This adds a timeout to avoid angering the watchdog
Bug: 195615825
Test: Mount removable storage with ~30K folders,
obeserve timeout in logs
Change-Id: I8b6e2658cf7024645f976599851bbee0557745ca
diff --git a/Utils.cpp b/Utils.cpp
index f9f3058..97a71a7 100644
--- a/Utils.cpp
+++ b/Utils.cpp
@@ -754,7 +754,53 @@
return OK;
}
-pid_t ForkExecvpAsync(const std::vector<std::string>& args) {
+status_t ForkExecvpTimeout(const std::vector<std::string>& args, std::chrono::seconds timeout,
+ char* context) {
+ int status;
+
+ pid_t wait_timeout_pid = fork();
+ if (wait_timeout_pid == 0) {
+ pid_t pid = ForkExecvpAsync(args, context);
+ if (pid == -1) {
+ _exit(EXIT_FAILURE);
+ }
+ pid_t timer_pid = fork();
+ if (timer_pid == 0) {
+ sleep(timeout.count());
+ _exit(ETIMEDOUT);
+ }
+ if (timer_pid == -1) {
+ PLOG(ERROR) << "fork in ForkExecvpAsync_timeout";
+ kill(pid, SIGTERM);
+ _exit(EXIT_FAILURE);
+ }
+ pid_t finished = wait(&status);
+ if (finished == pid) {
+ kill(timer_pid, SIGTERM);
+ } else {
+ kill(pid, SIGTERM);
+ }
+ if (!WIFEXITED(status)) {
+ _exit(ECHILD);
+ }
+ _exit(WEXITSTATUS(status));
+ }
+ if (waitpid(wait_timeout_pid, &status, 0) == -1) {
+ PLOG(ERROR) << "waitpid in ForkExecvpAsync_timeout";
+ return -errno;
+ }
+ if (!WIFEXITED(status)) {
+ LOG(ERROR) << "Process did not exit normally, status: " << status;
+ return -ECHILD;
+ }
+ if (WEXITSTATUS(status)) {
+ LOG(ERROR) << "Process exited with code: " << WEXITSTATUS(status);
+ return WEXITSTATUS(status);
+ }
+ return OK;
+}
+
+pid_t ForkExecvpAsync(const std::vector<std::string>& args, char* context) {
auto argv = ConvertToArgv(args);
pid_t pid = fork();
@@ -762,6 +808,12 @@
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);
+ if (context) {
+ if (setexeccon(context)) {
+ LOG(ERROR) << "Failed to setexeccon in ForkExecvpAsync";
+ abort();
+ }
+ }
execvp(argv[0], const_cast<char**>(argv.data()));
PLOG(ERROR) << "exec in ForkExecvpAsync";
diff --git a/Utils.h b/Utils.h
index bb6615c..3cca60b 100644
--- a/Utils.h
+++ b/Utils.h
@@ -37,6 +37,8 @@
static const char* kVoldAppDataIsolationEnabled = "persist.sys.vold_app_data_isolation_enabled";
static const char* kExternalStorageSdcardfs = "external_storage.sdcardfs.enabled";
+static constexpr std::chrono::seconds kUntrustedFsckSleepTime(45);
+
/* SELinux contexts used depending on the block device type */
extern char* sBlkidContext;
extern char* sBlkidUntrustedContext;
@@ -106,8 +108,10 @@
/* Returns either WEXITSTATUS() status, or a negative errno */
status_t ForkExecvp(const std::vector<std::string>& args,
std::vector<std::string>* output = nullptr, char* context = nullptr);
+status_t ForkExecvpTimeout(const std::vector<std::string>& args, std::chrono::seconds timeout,
+ char* context = nullptr);
-pid_t ForkExecvpAsync(const std::vector<std::string>& args);
+pid_t ForkExecvpAsync(const std::vector<std::string>& args, char* context = nullptr);
/* Gets block device size in bytes */
status_t GetBlockDevSize(int fd, uint64_t* size);
diff --git a/fs/Exfat.cpp b/fs/Exfat.cpp
index 7782dd3..c8b19e0 100644
--- a/fs/Exfat.cpp
+++ b/fs/Exfat.cpp
@@ -44,7 +44,7 @@
cmd.push_back("-y");
cmd.push_back(source);
- int rc = ForkExecvp(cmd, nullptr, sFsckUntrustedContext);
+ int rc = ForkExecvpTimeout(cmd, kUntrustedFsckSleepTime, sFsckUntrustedContext);
if (rc == 0) {
LOG(INFO) << "Check OK";
return 0;
diff --git a/fs/Vfat.cpp b/fs/Vfat.cpp
index 4f1e982..f3f04d8 100644
--- a/fs/Vfat.cpp
+++ b/fs/Vfat.cpp
@@ -68,10 +68,9 @@
cmd.push_back(source);
// Fat devices are currently always untrusted
- rc = ForkExecvp(cmd, nullptr, sFsckUntrustedContext);
-
+ rc = ForkExecvpTimeout(cmd, kUntrustedFsckSleepTime, sFsckUntrustedContext);
if (rc < 0) {
- LOG(ERROR) << "Filesystem check failed due to logwrap error";
+ LOG(ERROR) << "Filesystem check failed due to fork error";
errno = EIO;
return -1;
}
@@ -81,6 +80,10 @@
LOG(INFO) << "Filesystem check completed OK";
return 0;
+ case 1:
+ LOG(INFO) << "Failed to check filesystem";
+ return -1;
+
case 2:
LOG(ERROR) << "Filesystem check failed (not a FAT filesystem)";
errno = ENODATA;
@@ -100,6 +103,11 @@
errno = ENODATA;
return -1;
+ case ETIMEDOUT:
+ LOG(ERROR) << "Filesystem check timed out";
+ errno = ETIMEDOUT;
+ return -1;
+
default:
LOG(ERROR) << "Filesystem check failed (unknown exit code " << rc << ")";
errno = EIO;