Merge "ext4_utils: move fs_config to libcutils"
diff --git a/ext4_utils/Android.mk b/ext4_utils/Android.mk
index b56451b..9f56cca 100644
--- a/ext4_utils/Android.mk
+++ b/ext4_utils/Android.mk
@@ -55,7 +55,8 @@
 
 libext4_utils_src_files += \
     ext4_crypt.cpp \
-    e4crypt_static.c
+    e4crypt_static.c \
+    unencrypted_properties.cpp
 
 ifneq ($(HOST_OS),windows)
 
diff --git a/ext4_utils/ext4_crypt.cpp b/ext4_utils/ext4_crypt.cpp
index 70ad070..bb57332 100644
--- a/ext4_utils/ext4_crypt.cpp
+++ b/ext4_utils/ext4_crypt.cpp
@@ -12,35 +12,29 @@
 #include <cutils/klog.h>
 #include <cutils/properties.h>
 
-// ext4enc::TODO remove this duplicated const
-static const std::string unencrypted_path = "/unencrypted";
+#include "unencrypted_properties.h"
 
-static std::map<std::string, std::string> s_password_store;
+namespace {
+    std::map<std::string, std::string> s_password_store;
+}
 
 bool e4crypt_non_default_key(const char* dir)
 {
     int type = e4crypt_get_password_type(dir);
+
+    // ext4enc:TODO Use consts, not 1 here
     return type != -1 && type != 1;
 }
 
 int e4crypt_get_password_type(const char* path)
 {
-    auto full_path = std::string() + path + unencrypted_path;
-    if (!std::ifstream(full_path + "/key")) {
+    UnencryptedProperties props(path);
+    if (props.Get<std::string>(properties::key).empty()) {
         KLOG_INFO(TAG, "No master key, so not ext4enc\n");
         return -1;
     }
 
-    std::ifstream type(full_path + "/type");
-    if (!type) {
-        KLOG_INFO(TAG, "No password type so default\n");
-        return 1; // Default
-    }
-
-    int value = 0;
-    type >> value;
-    KLOG_INFO(TAG, "Password type is %d\n", value);
-    return value;
+    return props.Get<int>(properties::type, 1);
 }
 
 int e4crypt_change_password(const char* path, int crypt_type,
@@ -48,18 +42,17 @@
 {
     // ext4enc:TODO Encrypt master key with password securely. Store hash of
     // master key for validation
-    auto full_path = std::string() + path + unencrypted_path;
-    std::ofstream(full_path + "/password") << password;
-    std::ofstream(full_path + "/type") << crypt_type;
-    return 0;
+    UnencryptedProperties props(path);
+    if (   props.Set(properties::password, password)
+        && props.Set(properties::type, crypt_type))
+        return 0;
+    return -1;
 }
 
-int  e4crypt_crypto_complete(const char* path)
+int e4crypt_crypto_complete(const char* path)
 {
     KLOG_INFO(TAG, "ext4 crypto complete called on %s\n", path);
-
-    auto full_path = std::string() + path + unencrypted_path;
-    if (!std::ifstream(full_path + "/key")) {
+    if (UnencryptedProperties(path).Get<std::string>(properties::key).empty()) {
         KLOG_INFO(TAG, "No master key, so not ext4enc\n");
         return -1;
     }
@@ -69,14 +62,13 @@
 
 int e4crypt_check_passwd(const char* path, const char* password)
 {
-    auto full_path = std::string() + path + unencrypted_path;
-    if (!std::ifstream(full_path + "/key")) {
+    UnencryptedProperties props(path);
+    if (props.Get<std::string>(properties::key).empty()) {
         KLOG_INFO(TAG, "No master key, so not ext4enc\n");
         return -1;
     }
 
-    std::string actual_password;
-    std::ifstream(full_path + "/password") >> actual_password;
+    auto actual_password = props.Get<std::string>(properties::password);
 
     if (actual_password == password) {
         s_password_store[path] = password;
diff --git a/ext4_utils/ext4_crypt_init_extensions.cpp b/ext4_utils/ext4_crypt_init_extensions.cpp
index 16d333e..6d73fb2 100644
--- a/ext4_utils/ext4_crypt_init_extensions.cpp
+++ b/ext4_utils/ext4_crypt_init_extensions.cpp
@@ -15,6 +15,8 @@
 #include <cutils/properties.h>
 #include <cutils/sockets.h>
 
+#include "unencrypted_properties.h"
+
 // ext4enc:TODO Include structure from somewhere sensible
 // MUST be in sync with ext4_crypto.c in kernel
 #define EXT4_MAX_KEY_SIZE 76
@@ -24,7 +26,6 @@
         uint32_t size;
 };
 
-static const std::string unencrypted_path = "/unencrypted";
 static const std::string keyring = "@s";
 static const std::string arbitrary_sequence_number = "42";
 
@@ -104,46 +105,40 @@
 {
     // Make sure folder exists. Use make_dir to set selinux permissions.
     KLOG_INFO(TAG, "Creating test device key\n");
-    std::string path = std::string() + dir + unencrypted_path;
-    if (ensure_dir_exists(path.c_str())) {
+    UnencryptedProperties props(dir);
+    if (ensure_dir_exists(props.GetPath().c_str())) {
         KLOG_ERROR(TAG, "Failed to create %s with error %s\n",
-                   path.c_str(), strerror(errno));
+                   props.GetPath().c_str(), strerror(errno));
         return -1;
     }
 
-    // Open key if it exists
-    std::string key_path = path + "/key";
-    std::ifstream key(key_path.c_str(), std::ifstream::binary);
-
-    if (!key.good()) {
-        // Create new key if it doesn't
-        std::ofstream new_key(key_path.c_str(), std::ofstream::binary);
-        if (!new_key) {
-            KLOG_ERROR(TAG, "Failed to open %s\n", key_path.c_str());
-            return -1;
-        }
-
+    if (props.Get<std::string>(properties::key).empty()) {
+        // Create new key since it doesn't already exist
         std::ifstream urandom("/dev/urandom", std::ifstream::binary);
         if (!urandom) {
             KLOG_ERROR(TAG, "Failed to open /dev/urandom\n");
             return -1;
         }
 
-        char key_material[32];
-        urandom.read(key_material, 32);
+        // ext4enc:TODO Don't hardcode 32
+        std::string key_material(32, '\0');
+        urandom.read(&key_material[0], key_material.length());
         if (!urandom) {
             KLOG_ERROR(TAG, "Failed to read random bytes\n");
             return -1;
         }
 
-        new_key.write(key_material, 32);
-        if (!new_key) {
+        if (!props.Set(properties::key, key_material)) {
             KLOG_ERROR(TAG, "Failed to write key material");
             return -1;
         }
     }
 
-    remove((std::string(dir) + "/ref").c_str());
+    if (!props.Remove(properties::ref)) {
+        KLOG_ERROR(TAG, "Failed to remove key ref\n");
+        return -1;
+    }
+
     return 0;
 }
 
@@ -160,7 +155,8 @@
         return -1;
     }
 
-    KLOG_INFO(TAG, "Keyring created wth id %d in process %d\n", device_keyring, getpid());
+    KLOG_INFO(TAG, "Keyring created wth id %d in process %d\n",
+              device_keyring, getpid());
 
     // ext4enc:TODO set correct permissions
     long result = keyctl_setperm(device_keyring, 0x3f3f3f3f);
@@ -174,23 +170,8 @@
 
 int e4crypt_install_key(const char* dir)
 {
-    std::string path = std::string() + dir + unencrypted_path;
-
-    // Open key if it exists
-    std::string key_path = path + "/key";
-    std::ifstream key(key_path.c_str(), std::ifstream::binary);
-    if (!key.good()) {
-        KLOG_ERROR(TAG, "Failed to open key %s\n", key_path.c_str());
-        return -1;
-    }
-
-    char keyblob[256];
-    key.read(keyblob, sizeof(keyblob));
-    std::streamsize keyblob_size = key.gcount();
-    if (keyblob_size <= 0) {
-        KLOG_ERROR(TAG, "Failed to read key data\n");
-        return -1;
-    }
+    UnencryptedProperties props(dir);
+    auto key = props.Get<std::string>(properties::key);
 
     // Get password to decrypt as needed
     if (e4crypt_non_default_key(dir)) {
@@ -225,8 +206,14 @@
 
     // Add key to keyring
     ext4_encryption_key ext4_key = {0, {0}, 0};
-    memcpy(ext4_key.raw, keyblob, keyblob_size);
-    ext4_key.size = keyblob_size;
+    if (key.length() > sizeof(ext4_key.raw)) {
+        KLOG_ERROR(TAG, "Key too long\n");
+        return -1;
+    }
+
+    ext4_key.mode = 0;
+    memcpy(ext4_key.raw, &key[0], key.length());
+    ext4_key.size = key.length();
 
     // ext4enc:TODO Use better reference not 1234567890
     key_serial_t key_id = add_key("logon", "ext4-key:1234567890",
@@ -250,29 +237,30 @@
     }
 
     // Save reference to key so we can set policy later
-    std::ofstream(path + "/ref") << "ext4-key:1234567890";
+    if (!props.Set(properties::ref, "ext4-key:1234567890")) {
+        KLOG_ERROR(TAG, "Cannot save key reference\n");
+        return -1;
+    }
+
     return 0;
 }
 
 int e4crypt_set_directory_policy(const char* dir)
 {
     // Only set policy on first level /data directories
-    // ext4enc:TODO don't hard code /data/
+    // 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 || strncmp(dir, "/data/", 6) || strchr(dir + 6, '/')) {
         return 0;
     }
 
-    std::ifstream ref_file("/data/unencrypted/ref");
-    if (!ref_file) {
-        KLOG_ERROR(TAG, "Cannot open key reference file\n");
-        return -1;
-    }
-
-    std::string ref;
-    std::getline(ref_file, ref);
-    std::string policy = std::string() + keyring + "." + ref;
+    UnencryptedProperties props("/data");
+    std::string ref = props.Get<std::string>(properties::ref);
+    std::string policy = keyring + "." + ref;
     KLOG_INFO(TAG, "Setting policy %s\n", policy.c_str());
-    if (do_policy_set(dir, policy.c_str())) {
+    int result = do_policy_set(dir, policy.c_str());
+    if (result) {
         KLOG_ERROR(TAG, "Setting policy on %s failed!", dir);
         return -1;
     }
diff --git a/ext4_utils/unencrypted_properties.cpp b/ext4_utils/unencrypted_properties.cpp
new file mode 100644
index 0000000..bef7c57
--- /dev/null
+++ b/ext4_utils/unencrypted_properties.cpp
@@ -0,0 +1,86 @@
+#include "unencrypted_properties.h"
+
+#include <sys/stat.h>
+
+namespace properties {
+    const char* key = "key";
+    const char* ref = "ref";
+    const char* type = "type";
+    const char* password = "password";
+}
+
+namespace
+{
+    const char* unencrypted_folder = "unencrypted";
+}
+
+UnencryptedProperties::UnencryptedProperties(const char* device)
+  : folder_(std::string() + device + "/" + unencrypted_folder)
+{
+}
+
+UnencryptedProperties::UnencryptedProperties()
+{
+}
+
+template<> std::string UnencryptedProperties::Get(const char* name,
+                                      std::string default_value)
+{
+    if (!OK()) return default_value;
+    std::ifstream i(folder_ + "/" + name, std::ios::binary);
+    if (!i) {
+        return default_value;
+    }
+
+    i.seekg(0, std::ios::end);
+    int length = i.tellg();
+    i.seekg(0, std::ios::beg);
+    if (length == -1) {
+        return default_value;
+    }
+
+    std::string s(length, 0);
+    i.read(&s[0], length);
+    if (!i) {
+        return default_value;
+    }
+
+    return s;
+}
+
+template<> bool UnencryptedProperties::Set(const char* name, std::string const& value)
+{
+    if (!OK()) return false;
+    std::ofstream o(folder_ + "/" + name, std::ios::binary);
+    o << value;
+    return !o.fail();
+}
+
+UnencryptedProperties UnencryptedProperties::GetChild(const char* name)
+{
+    UnencryptedProperties e4p;
+    if (!OK()) return e4p;
+
+    std::string directory(folder_ + "/" + name);
+    if (mkdir(directory.c_str(), 700) == -1 && errno != EEXIST) {
+        return e4p;
+    }
+
+    e4p.folder_ = directory;
+    return e4p;
+}
+
+bool UnencryptedProperties::Remove(const char* name)
+{
+    if (remove((folder_ + "/" + name).c_str())
+        && errno != ENOENT) {
+        return false;
+    }
+
+    return true;
+}
+
+bool UnencryptedProperties::OK() const
+{
+    return !folder_.empty();
+}
diff --git a/ext4_utils/unencrypted_properties.h b/ext4_utils/unencrypted_properties.h
new file mode 100644
index 0000000..80f41df
--- /dev/null
+++ b/ext4_utils/unencrypted_properties.h
@@ -0,0 +1,70 @@
+#include <string>
+#include <fstream>
+
+// key names for properties we use
+namespace properties {
+    extern const char* key;
+    extern const char* ref;
+    extern const char* type;
+    extern const char* password;
+}
+
+/**
+ * Class to store data on the unencrypted folder of a device.
+ * Note that the folder must exist before this class is constructed.
+ * All names must be valid single level (no '/') file or directory names
+ * Data is organized hierarchically so we can get a child folder
+ */
+class UnencryptedProperties
+{
+public:
+    // Opens properties folder on named device.
+    // If folder does not exist, construction will succeed, but all
+    // getters will return default properties and setters will fail.
+    UnencryptedProperties(const char* device);
+
+    // Get named object. Return default if object does not exist or error.
+    template<typename t> t Get(const char* name, t default_value = t());
+
+    // Set named object. Return true if success, false otherwise
+    template<typename t> bool Set(const char* name, t const& value);
+
+    // Get child properties
+    UnencryptedProperties GetChild(const char* name);
+
+    // Remove named object
+    bool Remove(const char* name);
+
+    // Get path of folder
+    std::string const& GetPath() const {return folder_;}
+private:
+    UnencryptedProperties();
+    bool OK() const;
+    std::string folder_;
+};
+
+
+template<typename t> t UnencryptedProperties::Get(const char* name,
+                                                  t default_value)
+{
+    if (!OK()) return default_value;
+    t value = default_value;
+    std::ifstream(folder_ + "/" + name) >> value;
+    return value;
+}
+
+template<typename t> bool UnencryptedProperties::Set(const char* name,
+                                                     t const& value)
+{
+    if (!OK()) return false;
+    std::ofstream o(folder_ + "/" + name);
+    o << value;
+    return !o.fail();
+}
+
+// Specialized getters/setters for strings
+template<> std::string UnencryptedProperties::Get(const char* name,
+                                      std::string default_value);
+
+template<> bool UnencryptedProperties::Set(const char* name,
+                                           std::string const& value);