| // Copyright (c) 2012 The Chromium OS Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "shill/key_file_store.h" |
| |
| #include <map> |
| |
| #include <base/file_util.h> |
| #include <base/string_number_conversions.h> |
| #include <fcntl.h> |
| #include <sys/stat.h> |
| #include <sys/types.h> |
| |
| #include "shill/key_value_store.h" |
| #include "shill/logging.h" |
| |
| using std::map; |
| using std::set; |
| using std::string; |
| using std::vector; |
| |
| namespace shill { |
| |
| const char KeyFileStore::kCorruptSuffix[] = ".corrupted"; |
| |
| KeyFileStore::KeyFileStore(GLib *glib) |
| : glib_(glib), |
| crypto_(glib), |
| key_file_(NULL) {} |
| |
| KeyFileStore::~KeyFileStore() { |
| ReleaseKeyFile(); |
| } |
| |
| void KeyFileStore::ReleaseKeyFile() { |
| if (key_file_) { |
| glib_->KeyFileFree(key_file_); |
| key_file_ = NULL; |
| } |
| } |
| |
| bool KeyFileStore::IsNonEmpty() const { |
| int64 file_size = 0; |
| return file_util::GetFileSize(path_, &file_size) && file_size != 0; |
| } |
| |
| bool KeyFileStore::Open() { |
| CHECK(!path_.empty()); |
| CHECK(!key_file_); |
| crypto_.Init(); |
| key_file_ = glib_->KeyFileNew(); |
| if (!IsNonEmpty()) { |
| LOG(INFO) << "Creating a new key file at " << path_.value(); |
| return true; |
| } |
| GError *error = NULL; |
| if (glib_->KeyFileLoadFromFile( |
| key_file_, |
| path_.value().c_str(), |
| static_cast<GKeyFileFlags>(G_KEY_FILE_KEEP_COMMENTS | |
| G_KEY_FILE_KEEP_TRANSLATIONS), |
| &error)) { |
| return true; |
| } |
| LOG(ERROR) << "Failed to load key file from " << path_.value() << ": " |
| << glib_->ConvertErrorToMessage(error); |
| ReleaseKeyFile(); |
| return false; |
| } |
| |
| bool KeyFileStore::Close() { |
| bool success = Flush(); |
| ReleaseKeyFile(); |
| return success; |
| } |
| |
| bool KeyFileStore::Flush() { |
| CHECK(key_file_); |
| GError *error = NULL; |
| gsize length = 0; |
| gchar *data = glib_->KeyFileToData(key_file_, &length, &error); |
| |
| bool success = true; |
| if (path_.empty()) { |
| LOG(ERROR) << "Empty key file path."; |
| success = false; |
| } |
| if (success && (!data || error)) { |
| LOG(ERROR) << "Failed to convert key file to string: " |
| << glib_->ConvertErrorToMessage(error); |
| success = false; |
| } |
| if (success) { |
| // The profile file must be accessible by the owner only. |
| int fd = creat(path_.value().c_str(), S_IRUSR | S_IWUSR); |
| if (fd < 0) { |
| LOG(ERROR) << "Failed to create key file " << path_.value(); |
| success = false; |
| } else { |
| int written = file_util::WriteFileDescriptor(fd, data, length); |
| if (written < 0 || (static_cast<gsize>(written) != length)) { |
| LOG(ERROR) << "Failed to store key file: " << path_.value(); |
| success = false; |
| } |
| close(fd); |
| } |
| } |
| glib_->Free(data); |
| return success; |
| } |
| |
| bool KeyFileStore::MarkAsCorrupted() { |
| LOG(INFO) << "In " << __func__ << " for " << path_.value(); |
| if (path_.empty()) { |
| LOG(ERROR) << "Empty key file path."; |
| return false; |
| } |
| string corrupted_path = path_.value() + kCorruptSuffix; |
| int ret = rename(path_.value().c_str(), corrupted_path.c_str()); |
| if (ret != 0) { |
| PLOG(ERROR) << "File rename failed"; |
| return false; |
| } |
| return true; |
| } |
| |
| set<string> KeyFileStore::GetGroups() const { |
| CHECK(key_file_); |
| gsize length = 0; |
| gchar **groups = glib_->KeyFileGetGroups(key_file_, &length); |
| if (!groups) { |
| LOG(ERROR) << "Unable to obtain groups."; |
| return set<string>(); |
| } |
| set<string> group_set(groups, groups + length); |
| glib_->Strfreev(groups); |
| return group_set; |
| } |
| |
| // Returns a set so that caller can easily test whether a particular group |
| // is contained within this collection. |
| set<string> KeyFileStore::GetGroupsWithKey(const string &key) const { |
| set<string> groups = GetGroups(); |
| set<string> groups_with_key; |
| for (set<string>::iterator it = groups.begin(); it != groups.end(); ++it) { |
| if (glib_->KeyFileHasKey(key_file_, (*it).c_str(), key.c_str(), NULL)) { |
| groups_with_key.insert(*it); |
| } |
| } |
| return groups_with_key; |
| } |
| |
| set<string> KeyFileStore::GetGroupsWithProperties( |
| const KeyValueStore &properties) const { |
| set<string> groups = GetGroups(); |
| set<string> groups_with_properties; |
| for (set<string>::iterator it = groups.begin(); it != groups.end(); ++it) { |
| if (DoesGroupMatchProperties(*it, properties)) { |
| groups_with_properties.insert(*it); |
| } |
| } |
| return groups_with_properties; |
| } |
| |
| bool KeyFileStore::ContainsGroup(const string &group) const { |
| CHECK(key_file_); |
| return glib_->KeyFileHasGroup(key_file_, group.c_str()); |
| } |
| |
| bool KeyFileStore::DeleteKey(const string &group, const string &key) { |
| CHECK(key_file_); |
| GError *error = NULL; |
| glib_->KeyFileRemoveKey(key_file_, group.c_str(), key.c_str(), &error); |
| if (error && error->code != G_KEY_FILE_ERROR_KEY_NOT_FOUND) { |
| LOG(ERROR) << "Failed to delete (" << group << ":" << key << "): " |
| << glib_->ConvertErrorToMessage(error); |
| return false; |
| } |
| return true; |
| } |
| |
| bool KeyFileStore::DeleteGroup(const string &group) { |
| CHECK(key_file_); |
| GError *error = NULL; |
| glib_->KeyFileRemoveGroup(key_file_, group.c_str(), &error); |
| if (error && error->code != G_KEY_FILE_ERROR_GROUP_NOT_FOUND) { |
| LOG(ERROR) << "Failed to delete group " << group << ": " |
| << glib_->ConvertErrorToMessage(error); |
| return false; |
| } |
| return true; |
| } |
| |
| bool KeyFileStore::SetHeader(const string &header) { |
| GError *error = NULL; |
| glib_->KeyFileSetComment(key_file_, NULL, NULL, header.c_str(), &error); |
| if (error) { |
| LOG(ERROR) << "Failed to to set header: " |
| << glib_->ConvertErrorToMessage(error); |
| return false; |
| } |
| return true; |
| } |
| |
| bool KeyFileStore::GetString(const string &group, |
| const string &key, |
| string *value) const { |
| CHECK(key_file_); |
| GError *error = NULL; |
| gchar *data = |
| glib_->KeyFileGetString(key_file_, group.c_str(), key.c_str(), &error); |
| if (!data) { |
| string s = glib_->ConvertErrorToMessage(error); |
| SLOG(Storage, 10) << "Failed to lookup (" << group << ":" << key << "): " |
| << s; |
| return false; |
| } |
| if (value) { |
| *value = data; |
| } |
| glib_->Free(data); |
| return true; |
| } |
| |
| bool KeyFileStore::SetString(const string &group, |
| const string &key, |
| const string &value) { |
| CHECK(key_file_); |
| glib_->KeyFileSetString(key_file_, group.c_str(), key.c_str(), value.c_str()); |
| return true; |
| } |
| |
| bool KeyFileStore::GetBool(const string &group, |
| const string &key, |
| bool *value) const { |
| CHECK(key_file_); |
| GError *error = NULL; |
| gboolean data = |
| glib_->KeyFileGetBoolean(key_file_, group.c_str(), key.c_str(), &error); |
| if (error) { |
| string s = glib_->ConvertErrorToMessage(error); |
| SLOG(Storage, 10) << "Failed to lookup (" << group << ":" << key << "): " |
| << s; |
| return false; |
| } |
| if (value) { |
| *value = data; |
| } |
| return true; |
| } |
| |
| bool KeyFileStore::SetBool(const string &group, const string &key, bool value) { |
| CHECK(key_file_); |
| glib_->KeyFileSetBoolean(key_file_, |
| group.c_str(), |
| key.c_str(), |
| value ? TRUE : FALSE); |
| return true; |
| } |
| |
| bool KeyFileStore::GetInt( |
| const string &group, const string &key, int *value) const { |
| CHECK(key_file_); |
| GError *error = NULL; |
| gint data = |
| glib_->KeyFileGetInteger(key_file_, group.c_str(), key.c_str(), &error); |
| if (error) { |
| string s = glib_->ConvertErrorToMessage(error); |
| SLOG(Storage, 10) << "Failed to lookup (" << group << ":" << key << "): " |
| << s; |
| return false; |
| } |
| if (value) { |
| *value = data; |
| } |
| return true; |
| } |
| |
| bool KeyFileStore::SetInt(const string &group, const string &key, int value) { |
| CHECK(key_file_); |
| glib_->KeyFileSetInteger(key_file_, group.c_str(), key.c_str(), value); |
| return true; |
| } |
| |
| bool KeyFileStore::GetUint64( |
| const string &group, const string &key, uint64 *value) const { |
| // Read the value in as a string and then convert to uint64 because glib's |
| // g_key_file_set_uint64 appears not to work correctly on 32-bit platforms |
| // in unit tests. |
| string data_string; |
| if (!GetString(group, key, &data_string)) { |
| return false; |
| } |
| |
| uint64 data; |
| if (!base::StringToUint64(data_string, &data)) { |
| SLOG(Storage, 10) << "Failed to convert (" << group << ":" << key << "): " |
| << "string to uint64 conversion failed"; |
| return false; |
| } |
| |
| if (value) { |
| *value = data; |
| } |
| |
| return true; |
| } |
| |
| bool KeyFileStore::SetUint64( |
| const string &group, const string &key, uint64 value) { |
| // Convert the value to a string first, then save the value because glib's |
| // g_key_file_get_uint64 appears not to work on 32-bit platforms in our |
| // unit tests. |
| return SetString(group, key, base::Uint64ToString(value)); |
| } |
| |
| bool KeyFileStore::GetStringList(const string &group, |
| const string &key, |
| vector<string> *value) const { |
| CHECK(key_file_); |
| gsize length = 0; |
| GError *error = NULL; |
| gchar **data = glib_->KeyFileGetStringList(key_file_, |
| group.c_str(), |
| key.c_str(), |
| &length, |
| &error); |
| if (!data) { |
| string s = glib_->ConvertErrorToMessage(error); |
| SLOG(Storage, 10) << "Failed to lookup (" << group << ":" << key << "): " |
| << s; |
| return false; |
| } |
| if (value) { |
| value->assign(data, data + length); |
| } |
| glib_->Strfreev(data); |
| return true; |
| } |
| |
| bool KeyFileStore::SetStringList(const string &group, |
| const string &key, |
| const vector<string> &value) { |
| CHECK(key_file_); |
| vector<const char *> list; |
| for (vector<string>::const_iterator it = value.begin(); |
| it != value.end(); ++it) { |
| list.push_back(it->c_str()); |
| } |
| glib_->KeyFileSetStringList(key_file_, |
| group.c_str(), |
| key.c_str(), |
| list.data(), |
| list.size()); |
| return true; |
| } |
| |
| bool KeyFileStore::GetCryptedString(const string &group, |
| const string &key, |
| string *value) { |
| if (!GetString(group, key, value)) { |
| return false; |
| } |
| if (value) { |
| *value = crypto_.Decrypt(*value); |
| } |
| return true; |
| } |
| |
| bool KeyFileStore::SetCryptedString(const string &group, |
| const string &key, |
| const string &value) { |
| return SetString(group, key, crypto_.Encrypt(value)); |
| } |
| |
| bool KeyFileStore::DoesGroupMatchProperties( |
| const string &group, const KeyValueStore &properties) const { |
| map<string, bool>::const_iterator bool_it; |
| for (bool_it = properties.bool_properties().begin(); |
| bool_it != properties.bool_properties().end(); |
| ++bool_it) { |
| bool value; |
| if (!GetBool(group, bool_it->first, &value) || |
| value != bool_it->second) { |
| return false; |
| } |
| } |
| map<string, int32>::const_iterator int_it; |
| for (int_it = properties.int_properties().begin(); |
| int_it != properties.int_properties().end(); |
| ++int_it) { |
| int value; |
| if (!GetInt(group, int_it->first, &value) || |
| value != int_it->second) { |
| return false; |
| } |
| } |
| map<string, string>::const_iterator string_it; |
| for (string_it = properties.string_properties().begin(); |
| string_it != properties.string_properties().end(); |
| ++string_it) { |
| string value; |
| if (!GetString(group, string_it->first, &value) || |
| value != string_it->second) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| } // namespace shill |