blob: 93960e64ea3b20b09fc3ccb5028b5d9e021be758 [file] [log] [blame]
// Copyright 2016 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 "biod/biod_storage.h"
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <algorithm>
#include <sstream>
#include <utility>
#include <base/files/file_enumerator.h>
#include <base/files/file_path.h>
#include <base/files/file_util.h>
#include <base/files/important_file_writer.h>
#include <base/guid.h>
#include <base/json/json_string_value_serializer.h>
#include <base/message_loop/message_loop.h>
#include <base/strings/string_util.h>
#include <base/values.h>
namespace biod {
using base::FilePath;
namespace {
const char kRootPath[] = "/home/root";
const char kRecordFileName[] = "Record";
const char kBiod[] = "biod";
const char kLabel[] = "label";
const char kRecordId[] = "record_id";
const char kData[] = "data";
}
BiodStorage::BiodStorage(const std::string& biometrics_manager_name,
const ReadRecordsCallback& load_record)
: root_path_(kRootPath),
biometrics_manager_name_(biometrics_manager_name),
load_record_(load_record) {}
void BiodStorage::SetRootPathForTesting(const base::FilePath& root_path) {
root_path_ = root_path;
}
bool BiodStorage::WriteRecord(const BiometricsManager::Record& record,
std::unique_ptr<base::Value> data) {
const std::string& record_id(record.GetId());
base::DictionaryValue record_value;
record_value.SetString(kLabel, record.GetLabel());
record_value.SetString(kRecordId, record_id);
record_value.Set(kData, std::move(data));
std::string json_string;
JSONStringValueSerializer json_serializer(&json_string);
if (!json_serializer.Serialize(record_value)) {
LOG(ERROR) << "Failed to serialize record with id " << record_id
<< " to JSON.";
return false;
}
std::unique_ptr<ScopedUmask> owner_only_umask(new ScopedUmask(~(0700)));
FilePath record_storage_filename = root_path_.Append(record.GetUserId())
.Append(kBiod)
.Append(biometrics_manager_name_)
.Append(kRecordFileName + record_id);
if (!base::CreateDirectory(record_storage_filename.DirName())) {
LOG(ERROR) << "Cannot create directory: "
<< record_storage_filename.DirName().value() << ".";
return false;
}
owner_only_umask.reset(new ScopedUmask(~(0600)));
if (!base::ImportantFileWriter::WriteFileAtomically(record_storage_filename,
json_string)) {
LOG(ERROR) << "Failed to write JSON file: "
<< record_storage_filename.value() << ".";
return false;
}
LOG(INFO) << "Done writing record with id " << record_id
<< " to file successfully. ";
return true;
}
bool BiodStorage::ReadRecords(const std::unordered_set<std::string>& user_ids) {
bool read_records_from_all_users = true;
for (const auto& user_id : user_ids) {
read_records_from_all_users &= ReadRecordsForSingleUser(user_id);
}
return read_records_from_all_users;
}
bool BiodStorage::ReadRecordsForSingleUser(const std::string& user_id) {
FilePath biod_path =
root_path_.Append(user_id).Append(kBiod).Append(biometrics_manager_name_);
base::FileEnumerator enum_records(biod_path,
false,
base::FileEnumerator::FILES,
FILE_PATH_LITERAL("Record*"));
bool read_all_records_successfully = true;
for (FilePath record_path = enum_records.Next(); !record_path.empty();
record_path = enum_records.Next()) {
std::string json_string;
if (!base::ReadFileToString(record_path, &json_string)) {
LOG(ERROR) << "Failed to read the string from " << record_path.value()
<< ".";
read_all_records_successfully = false;
continue;
}
JSONStringValueDeserializer json_deserializer(json_string);
json_deserializer.set_allow_trailing_comma(true);
int error_code;
std::string error_message;
std::unique_ptr<base::Value> record_value(
json_deserializer.Deserialize(&error_code, &error_message));
if (!record_value) {
LOG_IF(ERROR, error_code) << "Error in deserializing JSON from path "
<< record_path.value() << " with code "
<< error_code << ".";
LOG_IF(ERROR, !error_message.empty())
<< "JSON error message: " << error_message << ".";
read_all_records_successfully = false;
continue;
}
base::DictionaryValue* record_dictionary;
if (!record_value->GetAsDictionary(&record_dictionary)) {
LOG(ERROR) << "Cannot cast " << record_path.value()
<< " to a dictionary value.";
read_all_records_successfully = false;
continue;
}
std::string label;
if (!record_dictionary->GetString(kLabel, &label)) {
LOG(ERROR) << "Cannot read label from " << record_path.value() << ".";
read_all_records_successfully = false;
continue;
}
std::string record_id;
if (!(record_dictionary->GetString(kRecordId, &record_id))) {
LOG(ERROR) << "Cannot read record id from " << record_path.value() << ".";
read_all_records_successfully = false;
continue;
}
base::Value* data = nullptr;
if (!(record_dictionary->Get(kData, &data))) {
LOG(ERROR) << "Cannot read data from " << record_path.value() << ".";
read_all_records_successfully = false;
continue;
}
if (!load_record_.Run(user_id, label, record_id, *data)) {
LOG(ERROR) << "Cannot load record from " << record_path.value() << ".";
read_all_records_successfully = false;
continue;
}
}
return read_all_records_successfully;
}
bool BiodStorage::DeleteRecord(const std::string& user_id,
const std::string& record_id) {
FilePath record_storage_filename = root_path_.Append(user_id)
.Append(kBiod)
.Append(biometrics_manager_name_)
.Append(kRecordFileName + record_id);
if (!base::PathExists(record_storage_filename)) {
LOG(INFO) << "Trying to delete record " << record_id
<< " which does not exist on disk.";
return true;
}
if (!base::DeleteFile(record_storage_filename, false)) {
LOG(ERROR) << "Fail to delete record " << record_id << " from disk.";
return false;
}
LOG(INFO) << "Done deleting record " << record_id << " from disk.";
return true;
}
std::string BiodStorage::GenerateNewRecordId() {
std::string record_id(base::GenerateGUID());
// dbus member names only allow '_'
std::replace(record_id.begin(), record_id.end(), '-', '_');
return record_id;
}
} // namespace biod