7 #include "packager/file/file.h"
9 #include <gflags/gflags.h>
13 #include "packager/base/files/file_util.h"
14 #include "packager/base/logging.h"
15 #include "packager/base/strings/string_number_conversions.h"
16 #include "packager/base/strings/string_piece.h"
17 #include "packager/base/strings/stringprintf.h"
18 #include "packager/file/callback_file.h"
19 #include "packager/file/file_util.h"
20 #include "packager/file/local_file.h"
21 #include "packager/file/memory_file.h"
22 #include "packager/file/threaded_io_file.h"
23 #include "packager/file/udp_file.h"
24 #include "packager/file/http_file.h"
26 DEFINE_uint64(io_cache_size,
28 "Size of the threaded I/O cache, in bytes. Specify 0 to disable "
30 DEFINE_uint64(io_block_size,
32 "Size of the block size used for threaded I/O, in bytes.");
41 const char* kCallbackFilePrefix =
"callback://";
42 const char* kLocalFilePrefix =
"file://";
43 const char* kMemoryFilePrefix =
"memory://";
44 const char* kUdpFilePrefix =
"udp://";
45 const char* kHttpFilePrefix =
"http://";
46 const char* kHttpsFilePrefix =
"https://";
51 typedef File* (*FileFactoryFunction)(
const char* file_name,
const char* mode);
52 typedef bool (*FileDeleteFunction)(
const char* file_name);
53 typedef bool (*FileAtomicWriteFunction)(
const char* file_name,
54 const std::string& contents);
58 const FileFactoryFunction factory_function;
59 const FileDeleteFunction delete_function;
60 const FileAtomicWriteFunction atomic_write_function;
63 File* CreateCallbackFile(
const char* file_name,
const char* mode) {
64 return new CallbackFile(file_name, mode);
67 File* CreateLocalFile(
const char* file_name,
const char* mode) {
68 return new LocalFile(file_name, mode);
71 bool DeleteLocalFile(
const char* file_name) {
75 bool WriteLocalFileAtomically(
const char* file_name,
76 const std::string& contents) {
77 const base::FilePath file_path = base::FilePath::FromUTF8Unsafe(file_name);
78 const std::string dir_name = file_path.DirName().AsUTF8Unsafe();
79 std::string temp_file_name;
84 base::File::Error replace_file_error = base::File::FILE_OK;
85 if (!base::ReplaceFile(base::FilePath::FromUTF8Unsafe(temp_file_name),
86 file_path, &replace_file_error)) {
87 LOG(ERROR) <<
"Failed to replace file '" << file_name <<
"' with '"
88 << temp_file_name <<
"', error: " << replace_file_error;
94 File* CreateUdpFile(
const char* file_name,
const char* mode) {
95 if (strcmp(mode,
"r")) {
96 NOTIMPLEMENTED() <<
"UdpFile only supports read (receive) mode.";
99 return new UdpFile(file_name);
102 File* CreateHttpsFile(
const char* file_name,
const char* mode) {
103 return new HttpFile(HttpMethod::kPut, std::string(
"https://") + file_name);
106 File* CreateHttpFile(
const char* file_name,
const char* mode) {
107 return new HttpFile(HttpMethod::kPut, std::string(
"http://") + file_name);
110 File* CreateMemoryFile(
const char* file_name,
const char* mode) {
111 return new MemoryFile(file_name, mode);
114 bool DeleteMemoryFile(
const char* file_name) {
119 static const FileTypeInfo kFileTypeInfo[] = {
124 &WriteLocalFileAtomically,
126 {kUdpFilePrefix, &CreateUdpFile,
nullptr,
nullptr},
127 {kMemoryFilePrefix, &CreateMemoryFile, &DeleteMemoryFile,
nullptr},
128 {kCallbackFilePrefix, &CreateCallbackFile,
nullptr,
nullptr},
129 {kHttpFilePrefix, &CreateHttpFile,
nullptr,
nullptr},
130 {kHttpsFilePrefix, &CreateHttpsFile,
nullptr,
nullptr},
133 base::StringPiece GetFileTypePrefix(base::StringPiece file_name) {
134 size_t pos = file_name.find(
"://");
135 return (pos == std::string::npos) ?
"" : file_name.substr(0, pos + 3);
138 const FileTypeInfo* GetFileTypeInfo(base::StringPiece file_name,
139 base::StringPiece* real_file_name) {
140 base::StringPiece file_type_prefix = GetFileTypePrefix(file_name);
141 for (
const FileTypeInfo& file_type : kFileTypeInfo) {
142 if (file_type_prefix == file_type.type) {
143 *real_file_name = file_name.substr(file_type_prefix.size());
148 *real_file_name = file_name;
149 return &kFileTypeInfo[0];
154 File* File::Create(
const char* file_name,
const char* mode) {
155 std::unique_ptr<File, FileCloser> internal_file(
158 base::StringPiece file_type_prefix = GetFileTypePrefix(
file_name);
159 if (file_type_prefix == kMemoryFilePrefix ||
160 file_type_prefix == kCallbackFilePrefix) {
162 return internal_file.release();
165 if (FLAGS_io_cache_size) {
167 if (!strcmp(mode,
"r")) {
168 return new ThreadedIoFile(std::move(internal_file),
169 ThreadedIoFile::kInputMode, FLAGS_io_cache_size,
170 FLAGS_io_block_size);
171 }
else if (!strcmp(mode,
"w") || !strcmp(mode,
"a")) {
172 return new ThreadedIoFile(std::move(internal_file),
173 ThreadedIoFile::kOutputMode,
174 FLAGS_io_cache_size, FLAGS_io_block_size);
179 DLOG(WARNING) <<
"Threaded I/O is disabled. Performance may be decreased.";
180 return internal_file.release();
183 File* File::CreateInternalFile(
const char* file_name,
const char* mode) {
184 base::StringPiece real_file_name;
185 const FileTypeInfo* file_type = GetFileTypeInfo(
file_name, &real_file_name);
187 return file_type->factory_function(real_file_name.data(), mode);
213 base::StringPiece real_file_name;
214 const FileTypeInfo* file_type = GetFileTypeInfo(
file_name, &real_file_name);
216 return file_type->delete_function
217 ? file_type->delete_function(real_file_name.data())
225 int64_t res = file->
Size();
237 const size_t kBufferSize = 0x40000;
238 std::unique_ptr<char[]> buf(
new char[kBufferSize]);
241 while ((len = file->
Read(buf.get(), kBufferSize)) > 0)
242 contents->append(buf.get(), len);
249 const std::string& contents) {
250 VLOG(2) <<
"File::WriteStringToFile: " <<
file_name;
253 LOG(ERROR) <<
"Failed to open file " <<
file_name;
256 int64_t bytes_written = file->Write(contents.data(), contents.size());
257 if (bytes_written < 0) {
258 LOG(ERROR) <<
"Failed to write to file '" <<
file_name <<
"' ("
259 << bytes_written <<
").";
262 if (
static_cast<size_t>(bytes_written) != contents.size()) {
263 LOG(ERROR) <<
"Failed to write the whole file to " <<
file_name
264 <<
". Wrote " << bytes_written <<
" but expecting "
265 << contents.size() <<
" bytes.";
268 if (!file.release()->Close()) {
271 <<
"', possibly file permission issue or running out of disk space.";
278 const std::string& contents) {
279 VLOG(2) <<
"File::WriteFileAtomically: " <<
file_name;
280 base::StringPiece real_file_name;
281 const FileTypeInfo* file_type = GetFileTypeInfo(
file_name, &real_file_name);
283 if (file_type->atomic_write_function)
284 return file_type->atomic_write_function(real_file_name.data(), contents);
291 if (strncmp(
file_name, kMemoryFilePrefix, strlen(kMemoryFilePrefix)) != 0
292 && strncmp(
file_name, kHttpFilePrefix, strlen(kHttpFilePrefix)) != 0
293 && strncmp(
file_name, kHttpsFilePrefix, strlen(kHttpsFilePrefix)) != 0) {
294 LOG(WARNING) <<
"Writing to " <<
file_name
295 <<
" is not guaranteed to be atomic.";
300 bool File::Copy(
const char* from_file_name,
const char* to_file_name) {
302 VLOG(2) <<
"File::Copy from " << from_file_name <<
" to " << to_file_name;
304 LOG(ERROR) <<
"Failed to open file " << from_file_name;
308 std::unique_ptr<File, FileCloser> output_file(
File::Open(to_file_name,
"w"));
310 LOG(ERROR) <<
"Failed to write to " << to_file_name;
314 uint64_t bytes_left = content.size();
315 uint64_t total_bytes_written = 0;
316 const char* content_cstr = content.c_str();
317 while (bytes_left > total_bytes_written) {
318 const int64_t bytes_written =
319 output_file->Write(content_cstr + total_bytes_written, bytes_left);
320 if (bytes_written < 0) {
321 LOG(ERROR) <<
"Failure while writing to " << to_file_name;
325 total_bytes_written += bytes_written;
327 if (!output_file.release()->Close()) {
329 <<
"Failed to close file '" << to_file_name
330 <<
"', possibly file permission issue or running out of disk space.";
337 return CopyFile(source, destination, kWholeFile);
344 max_copy = std::numeric_limits<int64_t>::max();
346 VLOG(2) <<
"File::CopyFile from " << source->
file_name() <<
" to "
349 const int64_t kBufferSize = 0x40000;
350 std::unique_ptr<uint8_t[]> buffer(
new uint8_t[kBufferSize]);
351 int64_t bytes_copied = 0;
352 while (bytes_copied < max_copy) {
353 const int64_t size = std::min(kBufferSize, max_copy - bytes_copied);
354 const int64_t bytes_read = source->
Read(buffer.get(), size);
360 int64_t total_bytes_written = 0;
361 while (total_bytes_written < bytes_read) {
362 const int64_t bytes_written = destination->
Write(
363 buffer.get() + total_bytes_written, bytes_read - total_bytes_written);
364 if (bytes_written < 0)
365 return bytes_written;
367 total_bytes_written += bytes_written;
370 DCHECK_EQ(total_bytes_written, bytes_read);
371 bytes_copied += bytes_read;
378 base::StringPiece real_file_name;
379 const FileTypeInfo* file_type = GetFileTypeInfo(
file_name, &real_file_name);
381 if (file_type->type != kLocalFilePrefix)
384 const base::FilePath file_path(
385 base::FilePath::FromUTF8Unsafe(real_file_name));
386 const DWORD fileattr = GetFileAttributes(file_path.value().c_str());
387 if (fileattr == INVALID_FILE_ATTRIBUTES) {
388 LOG(ERROR) <<
"Failed to GetFileAttributes of " << file_path.value();
391 return (fileattr & FILE_ATTRIBUTE_DIRECTORY) == 0;
394 if (stat(real_file_name.data(), &info) != 0) {
395 LOG(ERROR) <<
"Failed to run stat on " << real_file_name;
398 return S_ISREG(info.st_mode);
404 const std::string& name) {
407 return base::StringPrintf(
"%s%" PRIdPTR
"/%s", kCallbackFilePrefix,
408 reinterpret_cast<intptr_t
>(&callback_params),
415 size_t pos = callback_file_name.find(
"/");
416 int64_t callback_address = 0;
417 if (pos == std::string::npos ||
418 !base::StringToInt64(callback_file_name.substr(0, pos),
419 &callback_address)) {
420 LOG(ERROR) <<
"Expecting CallbackFile with name like "
421 "'<callback address>/<entity name>', but seeing "
422 << callback_file_name;
426 *name = callback_file_name.substr(pos + 1);
Define an abstract file interface.
static File * OpenWithNoBuffering(const char *file_name, const char *mode)
static bool Delete(const char *file_name)
static bool WriteStringToFile(const char *file_name, const std::string &contents)
const std::string & file_name() const
static bool WriteFileAtomically(const char *file_name, const std::string &contents)
virtual bool Open()=0
Internal open. Should not be used directly.
virtual int64_t Read(void *buffer, uint64_t length)=0
static std::string MakeCallbackFileName(const BufferCallbackParams &callback_params, const std::string &name)
static int64_t CopyFile(File *source, File *destination)
virtual int64_t Write(const void *buffer, uint64_t length)=0
static bool IsLocalRegularFile(const char *file_name)
static File * Open(const char *file_name, const char *mode)
static int64_t GetFileSize(const char *file_name)
static bool ReadFileToString(const char *file_name, std::string *contents)
static bool ParseCallbackFileName(const std::string &callback_file_name, const BufferCallbackParams **callback_params, std::string *name)
static bool Copy(const char *from_file_name, const char *to_file_name)
static bool Delete(const char *file_name)
static void Delete(const std::string &file_name)
All the methods that are virtual are virtual for mocking.
bool TempFilePath(const std::string &temp_dir, std::string *temp_file_path)