Make encryption action an argument to mkdir
FscryptSetDirectoryPolicy no longer tries to infer the action from the
filename. Well mostly; it still assumes top-level directories in /data
should be encrypted unless the mkdir arguments say otherwise, but
it warns.
Bug: 26641735
Test: boot, check log messages
Change-Id: Id6d2cea7fb856f17323897d85cf6190c981b443c
diff --git a/init/util.cpp b/init/util.cpp
index 40db838..ada9e78 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -434,6 +434,142 @@
return {};
}
+static FscryptAction FscryptInferAction(const std::string& dir) {
+ const std::string prefix = "/data/";
+
+ if (!android::base::StartsWith(dir, prefix)) {
+ return FscryptAction::kNone;
+ }
+
+ // Special-case /data/media/obb per b/64566063
+ if (dir == "/data/media/obb") {
+ // Try to set policy on this directory, but if it is non-empty this may fail.
+ return FscryptAction::kAttempt;
+ }
+
+ // Only set policy on first level /data directories
+ // To make this less restrictive, consider using a policy file.
+ // However this is overkill for as long as the policy is simply
+ // to apply a global policy to all /data folders created via makedir
+ if (dir.find_first_of('/', prefix.size()) != std::string::npos) {
+ return FscryptAction::kNone;
+ }
+
+ // Special case various directories that must not be encrypted,
+ // often because their subdirectories must be encrypted.
+ // This isn't a nice way to do this, see b/26641735
+ std::vector<std::string> directories_to_exclude = {
+ "lost+found", "system_ce", "system_de", "misc_ce", "misc_de",
+ "vendor_ce", "vendor_de", "media", "data", "user",
+ "user_de", "apex", "preloads", "app-staging", "gsi",
+ };
+ for (const auto& d : directories_to_exclude) {
+ if ((prefix + d) == dir) {
+ return FscryptAction::kNone;
+ }
+ }
+ // Empty these directories if policy setting fails.
+ std::vector<std::string> wipe_on_failure = {
+ "rollback", "rollback-observer", // b/139193659
+ };
+ for (const auto& d : wipe_on_failure) {
+ if ((prefix + d) == dir) {
+ return FscryptAction::kDeleteIfNecessary;
+ }
+ }
+ return FscryptAction::kRequire;
+}
+
+Result<MkdirOptions> ParseMkdir(const std::vector<std::string>& args) {
+ mode_t mode = 0755;
+ Result<uid_t> uid = -1;
+ Result<gid_t> gid = -1;
+ FscryptAction fscrypt_inferred_action = FscryptInferAction(args[1]);
+ FscryptAction fscrypt_action = fscrypt_inferred_action;
+ std::string ref_option = "ref";
+ bool set_option_encryption = false;
+ bool set_option_key = false;
+
+ for (size_t i = 2; i < args.size(); i++) {
+ switch (i) {
+ case 2:
+ mode = std::strtoul(args[2].c_str(), 0, 8);
+ break;
+ case 3:
+ uid = DecodeUid(args[3]);
+ if (!uid) {
+ return Error()
+ << "Unable to decode UID for '" << args[3] << "': " << uid.error();
+ }
+ break;
+ case 4:
+ gid = DecodeUid(args[4]);
+ if (!gid) {
+ return Error()
+ << "Unable to decode GID for '" << args[4] << "': " << gid.error();
+ }
+ break;
+ default:
+ auto parts = android::base::Split(args[i], "=");
+ if (parts.size() != 2) {
+ return Error() << "Can't parse option: '" << args[i] << "'";
+ }
+ auto optname = parts[0];
+ auto optval = parts[1];
+ if (optname == "encryption") {
+ if (set_option_encryption) {
+ return Error() << "Duplicated option: '" << optname << "'";
+ }
+ if (optval == "Require") {
+ fscrypt_action = FscryptAction::kRequire;
+ } else if (optval == "None") {
+ fscrypt_action = FscryptAction::kNone;
+ } else if (optval == "Attempt") {
+ fscrypt_action = FscryptAction::kAttempt;
+ } else if (optval == "DeleteIfNecessary") {
+ fscrypt_action = FscryptAction::kDeleteIfNecessary;
+ } else {
+ return Error() << "Unknown encryption option: '" << optval << "'";
+ }
+ set_option_encryption = true;
+ } else if (optname == "key") {
+ if (set_option_key) {
+ return Error() << "Duplicated option: '" << optname << "'";
+ }
+ if (optval == "ref" || optval == "per_boot_ref") {
+ ref_option = optval;
+ } else {
+ return Error() << "Unknown key option: '" << optval << "'";
+ }
+ set_option_key = true;
+ } else {
+ return Error() << "Unknown option: '" << args[i] << "'";
+ }
+ }
+ }
+ if (set_option_key && fscrypt_action == FscryptAction::kNone) {
+ return Error() << "Key option set but encryption action is none";
+ }
+ const std::string prefix = "/data/";
+ if (StartsWith(args[1], prefix) &&
+ args[1].find_first_of('/', prefix.size()) == std::string::npos) {
+ if (!set_option_encryption) {
+ LOG(WARNING) << "Top-level directory needs encryption action, eg mkdir " << args[1]
+ << " <mode> <uid> <gid> encryption=Require";
+ }
+ if (fscrypt_action == FscryptAction::kNone) {
+ LOG(INFO) << "Not setting encryption policy on: " << args[1];
+ }
+ }
+ if (fscrypt_action != fscrypt_inferred_action) {
+ LOG(WARNING) << "Inferred action different from explicit one, expected "
+ << static_cast<int>(fscrypt_inferred_action) << " but got "
+ << static_cast<int>(fscrypt_action);
+ }
+
+ return MkdirOptions{args[1], mode, *uid, *gid, fscrypt_action, ref_option};
+}
+
Result<std::pair<int, std::vector<std::string>>> ParseRestorecon(
const std::vector<std::string>& args) {
struct flag_type {