2014-02-14 23:21:05 +00:00
|
|
|
// Copyright 2014 Google Inc. All rights reserved.
|
|
|
|
//
|
|
|
|
// Use of this source code is governed by a BSD-style
|
|
|
|
// license that can be found in the LICENSE file or at
|
|
|
|
// https://developers.google.com/open-source/licenses/bsd
|
2013-10-11 21:44:55 +00:00
|
|
|
|
2017-07-10 18:26:22 +00:00
|
|
|
#include "packager/file/file.h"
|
2013-10-11 21:44:55 +00:00
|
|
|
|
2016-07-12 20:48:28 +00:00
|
|
|
#include <gflags/gflags.h>
|
2015-10-28 17:23:08 +00:00
|
|
|
#include <algorithm>
|
2016-08-17 17:41:40 +00:00
|
|
|
#include <memory>
|
2017-06-15 20:00:28 +00:00
|
|
|
#include "packager/base/files/important_file_writer.h"
|
2014-10-01 22:10:21 +00:00
|
|
|
#include "packager/base/logging.h"
|
2017-06-15 20:00:28 +00:00
|
|
|
#include "packager/base/strings/string_piece.h"
|
2017-07-10 18:26:22 +00:00
|
|
|
#include "packager/file/local_file.h"
|
|
|
|
#include "packager/file/memory_file.h"
|
|
|
|
#include "packager/file/threaded_io_file.h"
|
|
|
|
#include "packager/file/udp_file.h"
|
2013-10-11 21:44:55 +00:00
|
|
|
|
2015-03-19 18:28:04 +00:00
|
|
|
DEFINE_uint64(io_cache_size,
|
|
|
|
32ULL << 20,
|
|
|
|
"Size of the threaded I/O cache, in bytes. Specify 0 to disable "
|
|
|
|
"threaded I/O.");
|
|
|
|
DEFINE_uint64(io_block_size,
|
|
|
|
2ULL << 20,
|
|
|
|
"Size of the block size used for threaded I/O, in bytes.");
|
|
|
|
|
2016-08-14 22:28:21 +00:00
|
|
|
// Needed for Windows weirdness which somewhere defines CopyFile as CopyFileW.
|
|
|
|
#ifdef CopyFile
|
|
|
|
#undef CopyFile
|
|
|
|
#endif // CopyFile
|
|
|
|
|
2016-05-20 21:19:33 +00:00
|
|
|
namespace shaka {
|
2013-10-11 21:44:55 +00:00
|
|
|
|
|
|
|
const char* kLocalFilePrefix = "file://";
|
2014-04-30 23:22:15 +00:00
|
|
|
const char* kUdpFilePrefix = "udp://";
|
2015-12-22 00:20:34 +00:00
|
|
|
const char* kMemoryFilePrefix = "memory://";
|
2013-10-11 21:44:55 +00:00
|
|
|
|
2015-03-11 19:18:17 +00:00
|
|
|
namespace {
|
|
|
|
|
2014-01-15 22:29:56 +00:00
|
|
|
typedef File* (*FileFactoryFunction)(const char* file_name, const char* mode);
|
2015-03-11 19:18:17 +00:00
|
|
|
typedef bool (*FileDeleteFunction)(const char* file_name);
|
2017-06-15 20:00:28 +00:00
|
|
|
typedef bool (*FileAtomicWriteFunction)(const char* file_name,
|
|
|
|
const std::string& contents);
|
2013-10-11 21:44:55 +00:00
|
|
|
|
2017-06-15 20:00:28 +00:00
|
|
|
struct FileTypeInfo {
|
2013-10-11 21:44:55 +00:00
|
|
|
const char* type;
|
2014-07-02 16:58:40 +00:00
|
|
|
size_t type_length;
|
2013-10-11 21:44:55 +00:00
|
|
|
const FileFactoryFunction factory_function;
|
2015-03-11 19:18:17 +00:00
|
|
|
const FileDeleteFunction delete_function;
|
2017-06-15 20:00:28 +00:00
|
|
|
const FileAtomicWriteFunction atomic_write_function;
|
2013-10-11 21:44:55 +00:00
|
|
|
};
|
|
|
|
|
2015-03-11 19:18:17 +00:00
|
|
|
File* CreateLocalFile(const char* file_name, const char* mode) {
|
2014-01-15 22:29:56 +00:00
|
|
|
return new LocalFile(file_name, mode);
|
2013-10-11 21:44:55 +00:00
|
|
|
}
|
|
|
|
|
2015-03-11 19:18:17 +00:00
|
|
|
bool DeleteLocalFile(const char* file_name) {
|
|
|
|
return LocalFile::Delete(file_name);
|
|
|
|
}
|
|
|
|
|
2017-06-15 20:00:28 +00:00
|
|
|
bool WriteLocalFileAtomically(const char* file_name,
|
|
|
|
const std::string& contents) {
|
|
|
|
return base::ImportantFileWriter::WriteFileAtomically(
|
|
|
|
base::FilePath::FromUTF8Unsafe(file_name), contents);
|
|
|
|
}
|
|
|
|
|
2015-03-11 19:18:17 +00:00
|
|
|
File* CreateUdpFile(const char* file_name, const char* mode) {
|
2016-08-17 21:42:23 +00:00
|
|
|
if (strcmp(mode, "r")) {
|
2014-04-30 23:22:15 +00:00
|
|
|
NOTIMPLEMENTED() << "UdpFile only supports read (receive) mode.";
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
return new UdpFile(file_name);
|
|
|
|
}
|
|
|
|
|
2015-12-22 00:20:34 +00:00
|
|
|
File* CreateMemoryFile(const char* file_name, const char* mode) {
|
2015-12-22 00:33:55 +00:00
|
|
|
return new MemoryFile(file_name, mode);
|
2015-12-22 00:20:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool DeleteMemoryFile(const char* file_name) {
|
|
|
|
MemoryFile::Delete(file_name);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-06-15 20:00:28 +00:00
|
|
|
static const FileTypeInfo kFileTypeInfo[] = {
|
2017-07-10 18:26:22 +00:00
|
|
|
{
|
|
|
|
kLocalFilePrefix,
|
|
|
|
strlen(kLocalFilePrefix),
|
|
|
|
&CreateLocalFile,
|
|
|
|
&DeleteLocalFile,
|
|
|
|
&WriteLocalFileAtomically,
|
|
|
|
},
|
|
|
|
{kUdpFilePrefix, strlen(kUdpFilePrefix), &CreateUdpFile, nullptr, nullptr},
|
|
|
|
{kMemoryFilePrefix, strlen(kMemoryFilePrefix), &CreateMemoryFile,
|
|
|
|
&DeleteMemoryFile, nullptr},
|
2013-10-11 21:44:55 +00:00
|
|
|
};
|
|
|
|
|
2017-06-15 20:00:28 +00:00
|
|
|
const FileTypeInfo* GetFileTypeInfo(base::StringPiece file_name,
|
|
|
|
base::StringPiece* real_file_name) {
|
|
|
|
for (const FileTypeInfo& file_type : kFileTypeInfo) {
|
|
|
|
if (strncmp(file_type.type, file_name.data(), file_type.type_length) == 0) {
|
|
|
|
*real_file_name = file_name.substr(file_type.type_length);
|
|
|
|
return &file_type;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Otherwise we default to the first file type, which is LocalFile.
|
|
|
|
*real_file_name = file_name;
|
|
|
|
return &kFileTypeInfo[0];
|
|
|
|
}
|
|
|
|
|
2015-03-11 19:18:17 +00:00
|
|
|
} // namespace
|
|
|
|
|
2014-01-15 22:29:56 +00:00
|
|
|
File* File::Create(const char* file_name, const char* mode) {
|
2016-08-17 17:41:40 +00:00
|
|
|
std::unique_ptr<File, FileCloser> internal_file(
|
2015-05-20 17:14:36 +00:00
|
|
|
CreateInternalFile(file_name, mode));
|
2015-03-19 18:28:04 +00:00
|
|
|
|
2015-12-22 00:20:34 +00:00
|
|
|
if (!strncmp(file_name, kMemoryFilePrefix, strlen(kMemoryFilePrefix))) {
|
|
|
|
// Disable caching for memory files.
|
|
|
|
return internal_file.release();
|
|
|
|
}
|
|
|
|
|
2015-03-19 18:28:04 +00:00
|
|
|
if (FLAGS_io_cache_size) {
|
|
|
|
// Enable threaded I/O for "r", "w", and "a" modes only.
|
|
|
|
if (!strcmp(mode, "r")) {
|
2016-08-17 17:41:40 +00:00
|
|
|
return new ThreadedIoFile(std::move(internal_file),
|
|
|
|
ThreadedIoFile::kInputMode, FLAGS_io_cache_size,
|
2015-03-19 18:28:04 +00:00
|
|
|
FLAGS_io_block_size);
|
|
|
|
} else if (!strcmp(mode, "w") || !strcmp(mode, "a")) {
|
2016-08-17 17:41:40 +00:00
|
|
|
return new ThreadedIoFile(std::move(internal_file),
|
2015-03-19 18:28:04 +00:00
|
|
|
ThreadedIoFile::kOutputMode,
|
2016-08-17 17:41:40 +00:00
|
|
|
FLAGS_io_cache_size, FLAGS_io_block_size);
|
2015-03-19 18:28:04 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Threaded I/O is disabled.
|
|
|
|
DLOG(WARNING) << "Threaded I/O is disabled. Performance may be decreased.";
|
|
|
|
return internal_file.release();
|
2013-10-11 21:44:55 +00:00
|
|
|
}
|
|
|
|
|
2015-05-20 17:14:36 +00:00
|
|
|
File* File::CreateInternalFile(const char* file_name, const char* mode) {
|
2017-06-15 20:00:28 +00:00
|
|
|
base::StringPiece real_file_name;
|
|
|
|
const FileTypeInfo* file_type = GetFileTypeInfo(file_name, &real_file_name);
|
|
|
|
DCHECK(file_type);
|
|
|
|
return file_type->factory_function(real_file_name.data(), mode);
|
2015-05-20 17:14:36 +00:00
|
|
|
}
|
|
|
|
|
2014-01-15 22:29:56 +00:00
|
|
|
File* File::Open(const char* file_name, const char* mode) {
|
|
|
|
File* file = File::Create(file_name, mode);
|
|
|
|
if (!file)
|
2013-10-11 21:44:55 +00:00
|
|
|
return NULL;
|
|
|
|
if (!file->Open()) {
|
|
|
|
delete file;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
return file;
|
|
|
|
}
|
|
|
|
|
2015-05-20 17:14:36 +00:00
|
|
|
File* File::OpenWithNoBuffering(const char* file_name, const char* mode) {
|
|
|
|
File* file = File::CreateInternalFile(file_name, mode);
|
|
|
|
if (!file)
|
|
|
|
return NULL;
|
|
|
|
if (!file->Open()) {
|
|
|
|
delete file;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
return file;
|
|
|
|
}
|
|
|
|
|
2015-03-11 19:18:17 +00:00
|
|
|
bool File::Delete(const char* file_name) {
|
2017-06-15 20:00:28 +00:00
|
|
|
base::StringPiece real_file_name;
|
|
|
|
const FileTypeInfo* file_type = GetFileTypeInfo(file_name, &real_file_name);
|
|
|
|
DCHECK(file_type);
|
|
|
|
return file_type->delete_function
|
|
|
|
? file_type->delete_function(real_file_name.data())
|
|
|
|
: false;
|
2015-03-11 19:18:17 +00:00
|
|
|
}
|
|
|
|
|
2014-09-30 21:52:21 +00:00
|
|
|
int64_t File::GetFileSize(const char* file_name) {
|
2014-01-15 22:29:56 +00:00
|
|
|
File* file = File::Open(file_name, "r");
|
|
|
|
if (!file)
|
2013-10-11 21:44:55 +00:00
|
|
|
return -1;
|
2014-09-30 21:52:21 +00:00
|
|
|
int64_t res = file->Size();
|
2014-01-15 22:29:56 +00:00
|
|
|
file->Close();
|
2013-10-11 21:44:55 +00:00
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2014-01-15 22:29:56 +00:00
|
|
|
bool File::ReadFileToString(const char* file_name, std::string* contents) {
|
|
|
|
DCHECK(contents);
|
|
|
|
|
|
|
|
File* file = File::Open(file_name, "r");
|
|
|
|
if (!file)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
const size_t kBufferSize = 0x40000; // 256KB.
|
2016-08-17 17:41:40 +00:00
|
|
|
std::unique_ptr<char[]> buf(new char[kBufferSize]);
|
2014-01-15 22:29:56 +00:00
|
|
|
|
2014-09-30 21:52:21 +00:00
|
|
|
int64_t len;
|
2014-01-15 22:29:56 +00:00
|
|
|
while ((len = file->Read(buf.get(), kBufferSize)) > 0)
|
|
|
|
contents->append(buf.get(), len);
|
|
|
|
|
|
|
|
file->Close();
|
|
|
|
return len == 0;
|
|
|
|
}
|
|
|
|
|
2017-07-10 18:26:22 +00:00
|
|
|
bool File::WriteFileAtomically(const char* file_name,
|
|
|
|
const std::string& contents) {
|
2017-06-15 20:00:28 +00:00
|
|
|
base::StringPiece real_file_name;
|
|
|
|
const FileTypeInfo* file_type = GetFileTypeInfo(file_name, &real_file_name);
|
|
|
|
DCHECK(file_type);
|
|
|
|
if (file_type->atomic_write_function)
|
|
|
|
return file_type->atomic_write_function(real_file_name.data(), contents);
|
|
|
|
|
|
|
|
// Provide a default implementation which may not be atomic unfortunately.
|
|
|
|
|
|
|
|
// Skip the warning message for memory files, which is meant for testing
|
|
|
|
// anyway..
|
|
|
|
if (strncmp(file_name, kMemoryFilePrefix, strlen(kMemoryFilePrefix)) != 0) {
|
|
|
|
LOG(WARNING) << "Writing to " << file_name
|
|
|
|
<< " is not guaranteed to be atomic.";
|
|
|
|
}
|
|
|
|
|
2017-07-10 18:26:22 +00:00
|
|
|
std::unique_ptr<File, FileCloser> file(File::Open(file_name, "w"));
|
2017-06-15 20:00:28 +00:00
|
|
|
if (!file) {
|
|
|
|
LOG(ERROR) << "Failed to open file " << file_name;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
int64_t bytes_written = file->Write(contents.data(), contents.size());
|
|
|
|
if (bytes_written < 0) {
|
|
|
|
LOG(ERROR) << "Failed to write to file '" << file_name << "' ("
|
|
|
|
<< bytes_written << ").";
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (static_cast<size_t>(bytes_written) != contents.size()) {
|
|
|
|
LOG(ERROR) << "Failed to write the whole file to " << file_name
|
|
|
|
<< ". Wrote " << bytes_written << " but expecting "
|
|
|
|
<< contents.size() << " bytes.";
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2015-11-05 01:49:35 +00:00
|
|
|
bool File::Copy(const char* from_file_name, const char* to_file_name) {
|
|
|
|
std::string content;
|
|
|
|
if (!ReadFileToString(from_file_name, &content)) {
|
|
|
|
LOG(ERROR) << "Failed to open file " << from_file_name;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2016-08-17 17:41:40 +00:00
|
|
|
std::unique_ptr<File, FileCloser> output_file(File::Open(to_file_name, "w"));
|
2015-11-05 01:49:35 +00:00
|
|
|
if (!output_file) {
|
|
|
|
LOG(ERROR) << "Failed to write to " << to_file_name;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint64_t bytes_left = content.size();
|
|
|
|
uint64_t total_bytes_written = 0;
|
|
|
|
const char* content_cstr = content.c_str();
|
|
|
|
while (bytes_left > total_bytes_written) {
|
|
|
|
const int64_t bytes_written =
|
|
|
|
output_file->Write(content_cstr + total_bytes_written, bytes_left);
|
|
|
|
if (bytes_written < 0) {
|
|
|
|
LOG(ERROR) << "Failure while writing to " << to_file_name;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
total_bytes_written += bytes_written;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2015-10-28 17:23:08 +00:00
|
|
|
int64_t File::CopyFile(File* source, File* destination) {
|
|
|
|
return CopyFile(source, destination, kWholeFile);
|
|
|
|
}
|
|
|
|
|
|
|
|
int64_t File::CopyFile(File* source, File* destination, int64_t max_copy) {
|
|
|
|
DCHECK(source);
|
|
|
|
DCHECK(destination);
|
|
|
|
if (max_copy < 0)
|
|
|
|
max_copy = std::numeric_limits<int64_t>::max();
|
|
|
|
|
|
|
|
const int64_t kBufferSize = 0x40000; // 256KB.
|
2016-08-17 17:41:40 +00:00
|
|
|
std::unique_ptr<uint8_t[]> buffer(new uint8_t[kBufferSize]);
|
2015-10-28 17:23:08 +00:00
|
|
|
int64_t bytes_copied = 0;
|
|
|
|
while (bytes_copied < max_copy) {
|
|
|
|
const int64_t size = std::min(kBufferSize, max_copy - bytes_copied);
|
|
|
|
const int64_t bytes_read = source->Read(buffer.get(), size);
|
|
|
|
if (bytes_read < 0)
|
|
|
|
return bytes_read;
|
|
|
|
if (bytes_read == 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
int64_t total_bytes_written = 0;
|
|
|
|
while (total_bytes_written < bytes_read) {
|
|
|
|
const int64_t bytes_written = destination->Write(
|
|
|
|
buffer.get() + total_bytes_written, bytes_read - total_bytes_written);
|
|
|
|
if (bytes_written < 0)
|
|
|
|
return bytes_written;
|
|
|
|
|
|
|
|
total_bytes_written += bytes_written;
|
|
|
|
}
|
|
|
|
|
|
|
|
DCHECK_EQ(total_bytes_written, bytes_read);
|
|
|
|
bytes_copied += bytes_read;
|
|
|
|
}
|
|
|
|
|
|
|
|
return bytes_copied;
|
|
|
|
}
|
|
|
|
|
2016-05-20 21:19:33 +00:00
|
|
|
} // namespace shaka
|