blob: 62c602eab8665f5be520ea4d12a803633f88cfe3 [file] [log] [blame]
// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "cobalt/storage/virtual_file_system.h"
#include "base/logging.h"
#include "base/synchronization/lock.h"
#include "cobalt/storage/virtual_file.h"
#include "starboard/client_porting/poem/string_poem.h"
namespace cobalt {
namespace storage {
namespace {
// Update this any time the serialization format changes.
const char kVersion[] = "SAV0";
} // namespace
// static
int VirtualFileSystem::GetHeaderVersion(const SerializedHeader& header) {
// Copy the version int to a char buffer to avoid endian issues.
char version[4];
memcpy(version, &header.version, sizeof(version));
if (memcmp(version, kVersion, 3) != 0) {
return -1;
} else if (header.file_size < static_cast<int>(sizeof(SerializedHeader))) {
return -1;
} else {
return version[3] - '0';
}
}
// static
int VirtualFileSystem::GetCurrentVersion() {
COMPILE_ASSERT(sizeof(kVersion) - 1 == 4, Unexpected_version_size);
int version;
memcpy(&version, kVersion, sizeof(version));
return version;
}
VirtualFileSystem::VirtualFileSystem() {}
VirtualFileSystem::~VirtualFileSystem() {
DCHECK(thread_checker_.CalledOnValidThread());
base::AutoLock lock(file_table_lock_);
ClearFileTable();
}
VirtualFile* VirtualFileSystem::Open(const std::string& filename) {
base::AutoLock lock(file_table_lock_);
VirtualFile* result = NULL;
FileTable::iterator it = table_.find(filename);
if (it != table_.end()) {
result = it->second;
} else {
result = new VirtualFile(filename);
table_.insert(it, FileTable::value_type(filename, result));
}
return result;
}
std::vector<std::string> VirtualFileSystem::ListFiles() {
base::AutoLock lock(file_table_lock_);
std::vector<std::string> files;
for (FileTable::iterator it = table_.begin(); it != table_.end(); ++it) {
files.push_back(it->first);
}
return files;
}
void VirtualFileSystem::Delete(const std::string& filename) {
base::AutoLock lock(file_table_lock_);
FileTable::iterator it = table_.find(filename);
if (it != table_.end()) {
delete it->second;
table_.erase(it);
}
}
int VirtualFileSystem::Serialize(uint8* buffer, bool dry_run) {
DCHECK(thread_checker_.CalledOnValidThread());
base::AutoLock lock(file_table_lock_);
uint8* original = buffer;
// We don't know the total size of the file yet, so defer writing the header.
buffer += sizeof(SerializedHeader);
int valid_file_count = 0;
// Serialize each file
for (FileTable::iterator it = table_.begin(); it != table_.end(); ++it) {
if (it->second->Size() == 0) {
continue;
}
int file_bytes = it->second->Serialize(buffer, dry_run);
buffer += file_bytes;
valid_file_count++;
}
const int bytes_written = static_cast<int>(buffer - original);
if (!dry_run) {
// Now we can write the header to the beginning of the buffer.
SerializedHeader header;
header.version = GetCurrentVersion();
header.file_count = valid_file_count;
header.file_size = bytes_written;
memcpy(original, &header, sizeof(SerializedHeader));
}
return bytes_written;
}
bool VirtualFileSystem::Deserialize(const uint8* buffer, int buffer_size_in) {
const uint8* caller_buffer = buffer;
base::AutoLock lock(file_table_lock_);
ClearFileTable();
if (buffer_size_in < 0) {
DLOG(ERROR) << "Buffer size must be positive: " << buffer_size_in
<< " < 0.";
return false;
}
size_t buffer_size = static_cast<size_t>(buffer_size_in);
// The size of the buffer must be checked before copying the beginning of it
// into a SerializedHeader.
if (buffer_size < sizeof(SerializedHeader)) {
DLOG(ERROR) << "Buffer size " << buffer_size
<< " is too small to contain a SerializedHeader of size "
<< sizeof(SerializedHeader) << "; operation aborted.";
return false;
}
// Read in expected number of files
SerializedHeader header;
memcpy(&header, buffer, sizeof(SerializedHeader));
buffer += sizeof(SerializedHeader);
int buffer_version = GetHeaderVersion(header);
if (buffer_version != 0) {
// Note: We would handle old versions here, if necessary.
DLOG(ERROR) << "Attempted to load a different version; operation aborted.";
return false;
} else if (static_cast<size_t>(header.file_size) != buffer_size) {
DLOG(ERROR) << "Buffer size mismatch: " << header.file_size
<< " != " << buffer_size;
return false;
}
for (int i = 0; i < header.file_count; ++i) {
size_t buffer_used = static_cast<size_t>(buffer - caller_buffer);
if (buffer_size < buffer_used) {
DLOG(ERROR) << "Buffer overrun deserializing files";
ClearFileTable();
return false;
}
size_t buffer_remaining = buffer_size - buffer_used;
VirtualFile* file = new VirtualFile("");
int bytes = file->Deserialize(buffer, buffer_remaining);
if (bytes > 0) {
buffer += bytes;
table_[file->name_] = file;
} else {
DLOG(WARNING) << "Failed to deserialize virtual file system.";
delete file;
ClearFileTable();
return false;
}
}
return true;
}
void VirtualFileSystem::ClearFileTable() {
DCHECK(thread_checker_.CalledOnValidThread());
file_table_lock_.AssertAcquired();
for (FileTable::iterator it = table_.begin(); it != table_.end(); ++it) {
delete it->second;
}
table_.clear();
}
} // namespace storage
} // namespace cobalt