Shaka Packager SDK
file.cc
1 // Copyright 2014 Google Inc. All rights reserved.
2 //
3 // Use of this source code is governed by a BSD-style
4 // license that can be found in the LICENSE file or at
5 // https://developers.google.com/open-source/licenses/bsd
6 
7 #include "packager/file/file.h"
8 
9 #include <gflags/gflags.h>
10 #include <inttypes.h>
11 #include <algorithm>
12 #include <memory>
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 
25 DEFINE_uint64(io_cache_size,
26  32ULL << 20,
27  "Size of the threaded I/O cache, in bytes. Specify 0 to disable "
28  "threaded I/O.");
29 DEFINE_uint64(io_block_size,
30  1ULL << 16,
31  "Size of the block size used for threaded I/O, in bytes.");
32 
33 // Needed for Windows weirdness which somewhere defines CopyFile as CopyFileW.
34 #ifdef CopyFile
35 #undef CopyFile
36 #endif // CopyFile
37 
38 namespace shaka {
39 
40 const char* kCallbackFilePrefix = "callback://";
41 const char* kLocalFilePrefix = "file://";
42 const char* kMemoryFilePrefix = "memory://";
43 const char* kUdpFilePrefix = "udp://";
44 
45 namespace {
46 
47 typedef File* (*FileFactoryFunction)(const char* file_name, const char* mode);
48 typedef bool (*FileDeleteFunction)(const char* file_name);
49 typedef bool (*FileAtomicWriteFunction)(const char* file_name,
50  const std::string& contents);
51 
52 struct FileTypeInfo {
53  const char* type;
54  const FileFactoryFunction factory_function;
55  const FileDeleteFunction delete_function;
56  const FileAtomicWriteFunction atomic_write_function;
57 };
58 
59 File* CreateCallbackFile(const char* file_name, const char* mode) {
60  return new CallbackFile(file_name, mode);
61 }
62 
63 File* CreateLocalFile(const char* file_name, const char* mode) {
64  return new LocalFile(file_name, mode);
65 }
66 
67 bool DeleteLocalFile(const char* file_name) {
68  return LocalFile::Delete(file_name);
69 }
70 
71 bool WriteLocalFileAtomically(const char* file_name,
72  const std::string& contents) {
73  const base::FilePath file_path = base::FilePath::FromUTF8Unsafe(file_name);
74  const std::string dir_name = file_path.DirName().AsUTF8Unsafe();
75  std::string temp_file_name;
76  if (!TempFilePath(dir_name, &temp_file_name))
77  return false;
78  if (!File::WriteStringToFile(temp_file_name.c_str(), contents))
79  return false;
80  base::File::Error replace_file_error = base::File::FILE_OK;
81  if (!base::ReplaceFile(base::FilePath::FromUTF8Unsafe(temp_file_name),
82  file_path, &replace_file_error)) {
83  LOG(ERROR) << "Failed to replace file '" << file_name << "' with '"
84  << temp_file_name << "', error: " << replace_file_error;
85  return false;
86  }
87  return true;
88 }
89 
90 File* CreateUdpFile(const char* file_name, const char* mode) {
91  if (strcmp(mode, "r")) {
92  NOTIMPLEMENTED() << "UdpFile only supports read (receive) mode.";
93  return NULL;
94  }
95  return new UdpFile(file_name);
96 }
97 
98 File* CreateMemoryFile(const char* file_name, const char* mode) {
99  return new MemoryFile(file_name, mode);
100 }
101 
102 bool DeleteMemoryFile(const char* file_name) {
103  MemoryFile::Delete(file_name);
104  return true;
105 }
106 
107 static const FileTypeInfo kFileTypeInfo[] = {
108  {
109  kLocalFilePrefix,
110  &CreateLocalFile,
111  &DeleteLocalFile,
112  &WriteLocalFileAtomically,
113  },
114  {kUdpFilePrefix, &CreateUdpFile, nullptr, nullptr},
115  {kMemoryFilePrefix, &CreateMemoryFile, &DeleteMemoryFile, nullptr},
116  {kCallbackFilePrefix, &CreateCallbackFile, nullptr, nullptr},
117 };
118 
119 base::StringPiece GetFileTypePrefix(base::StringPiece file_name) {
120  size_t pos = file_name.find("://");
121  return (pos == std::string::npos) ? "" : file_name.substr(0, pos + 3);
122 }
123 
124 const FileTypeInfo* GetFileTypeInfo(base::StringPiece file_name,
125  base::StringPiece* real_file_name) {
126  base::StringPiece file_type_prefix = GetFileTypePrefix(file_name);
127  for (const FileTypeInfo& file_type : kFileTypeInfo) {
128  if (file_type_prefix == file_type.type) {
129  *real_file_name = file_name.substr(file_type_prefix.size());
130  return &file_type;
131  }
132  }
133  // Otherwise we default to the first file type, which is LocalFile.
134  *real_file_name = file_name;
135  return &kFileTypeInfo[0];
136 }
137 
138 } // namespace
139 
140 File* File::Create(const char* file_name, const char* mode) {
141  std::unique_ptr<File, FileCloser> internal_file(
142  CreateInternalFile(file_name, mode));
143 
144  base::StringPiece file_type_prefix = GetFileTypePrefix(file_name);
145  if (file_type_prefix == kMemoryFilePrefix ||
146  file_type_prefix == kCallbackFilePrefix) {
147  // Disable caching for memory and callback files.
148  return internal_file.release();
149  }
150 
151  if (FLAGS_io_cache_size) {
152  // Enable threaded I/O for "r", "w", and "a" modes only.
153  if (!strcmp(mode, "r")) {
154  return new ThreadedIoFile(std::move(internal_file),
155  ThreadedIoFile::kInputMode, FLAGS_io_cache_size,
156  FLAGS_io_block_size);
157  } else if (!strcmp(mode, "w") || !strcmp(mode, "a")) {
158  return new ThreadedIoFile(std::move(internal_file),
159  ThreadedIoFile::kOutputMode,
160  FLAGS_io_cache_size, FLAGS_io_block_size);
161  }
162  }
163 
164  // Threaded I/O is disabled.
165  DLOG(WARNING) << "Threaded I/O is disabled. Performance may be decreased.";
166  return internal_file.release();
167 }
168 
169 File* File::CreateInternalFile(const char* file_name, const char* mode) {
170  base::StringPiece real_file_name;
171  const FileTypeInfo* file_type = GetFileTypeInfo(file_name, &real_file_name);
172  DCHECK(file_type);
173  return file_type->factory_function(real_file_name.data(), mode);
174 }
175 
176 File* File::Open(const char* file_name, const char* mode) {
177  File* file = File::Create(file_name, mode);
178  if (!file)
179  return NULL;
180  if (!file->Open()) {
181  delete file;
182  return NULL;
183  }
184  return file;
185 }
186 
187 File* File::OpenWithNoBuffering(const char* file_name, const char* mode) {
188  File* file = File::CreateInternalFile(file_name, mode);
189  if (!file)
190  return NULL;
191  if (!file->Open()) {
192  delete file;
193  return NULL;
194  }
195  return file;
196 }
197 
198 bool File::Delete(const char* file_name) {
199  base::StringPiece real_file_name;
200  const FileTypeInfo* file_type = GetFileTypeInfo(file_name, &real_file_name);
201  DCHECK(file_type);
202  return file_type->delete_function
203  ? file_type->delete_function(real_file_name.data())
204  : false;
205 }
206 
207 int64_t File::GetFileSize(const char* file_name) {
208  File* file = File::Open(file_name, "r");
209  if (!file)
210  return -1;
211  int64_t res = file->Size();
212  file->Close();
213  return res;
214 }
215 
216 bool File::ReadFileToString(const char* file_name, std::string* contents) {
217  DCHECK(contents);
218 
219  File* file = File::Open(file_name, "r");
220  if (!file)
221  return false;
222 
223  const size_t kBufferSize = 0x40000; // 256KB.
224  std::unique_ptr<char[]> buf(new char[kBufferSize]);
225 
226  int64_t len;
227  while ((len = file->Read(buf.get(), kBufferSize)) > 0)
228  contents->append(buf.get(), len);
229 
230  file->Close();
231  return len == 0;
232 }
233 
234 bool File::WriteStringToFile(const char* file_name,
235  const std::string& contents) {
236  std::unique_ptr<File, FileCloser> file(File::Open(file_name, "w"));
237  if (!file) {
238  LOG(ERROR) << "Failed to open file " << file_name;
239  return false;
240  }
241  int64_t bytes_written = file->Write(contents.data(), contents.size());
242  if (bytes_written < 0) {
243  LOG(ERROR) << "Failed to write to file '" << file_name << "' ("
244  << bytes_written << ").";
245  return false;
246  }
247  if (static_cast<size_t>(bytes_written) != contents.size()) {
248  LOG(ERROR) << "Failed to write the whole file to " << file_name
249  << ". Wrote " << bytes_written << " but expecting "
250  << contents.size() << " bytes.";
251  return false;
252  }
253  if (!file.release()->Close()) {
254  LOG(ERROR)
255  << "Failed to close file '" << file_name
256  << "', possibly file permission issue or running out of disk space.";
257  return false;
258  }
259  return true;
260 }
261 
262 bool File::WriteFileAtomically(const char* file_name,
263  const std::string& contents) {
264  base::StringPiece real_file_name;
265  const FileTypeInfo* file_type = GetFileTypeInfo(file_name, &real_file_name);
266  DCHECK(file_type);
267  if (file_type->atomic_write_function)
268  return file_type->atomic_write_function(real_file_name.data(), contents);
269 
270  // Provide a default implementation which may not be atomic unfortunately.
271 
272  // Skip the warning message for memory files, which is meant for testing
273  // anyway..
274  if (strncmp(file_name, kMemoryFilePrefix, strlen(kMemoryFilePrefix)) != 0) {
275  LOG(WARNING) << "Writing to " << file_name
276  << " is not guaranteed to be atomic.";
277  }
278  return WriteStringToFile(file_name, contents);
279 }
280 
281 bool File::Copy(const char* from_file_name, const char* to_file_name) {
282  std::string content;
283  if (!ReadFileToString(from_file_name, &content)) {
284  LOG(ERROR) << "Failed to open file " << from_file_name;
285  return false;
286  }
287 
288  std::unique_ptr<File, FileCloser> output_file(File::Open(to_file_name, "w"));
289  if (!output_file) {
290  LOG(ERROR) << "Failed to write to " << to_file_name;
291  return false;
292  }
293 
294  uint64_t bytes_left = content.size();
295  uint64_t total_bytes_written = 0;
296  const char* content_cstr = content.c_str();
297  while (bytes_left > total_bytes_written) {
298  const int64_t bytes_written =
299  output_file->Write(content_cstr + total_bytes_written, bytes_left);
300  if (bytes_written < 0) {
301  LOG(ERROR) << "Failure while writing to " << to_file_name;
302  return false;
303  }
304 
305  total_bytes_written += bytes_written;
306  }
307  if (!output_file.release()->Close()) {
308  LOG(ERROR)
309  << "Failed to close file '" << to_file_name
310  << "', possibly file permission issue or running out of disk space.";
311  return false;
312  }
313  return true;
314 }
315 
316 int64_t File::CopyFile(File* source, File* destination) {
317  return CopyFile(source, destination, kWholeFile);
318 }
319 
320 int64_t File::CopyFile(File* source, File* destination, int64_t max_copy) {
321  DCHECK(source);
322  DCHECK(destination);
323  if (max_copy < 0)
324  max_copy = std::numeric_limits<int64_t>::max();
325 
326  const int64_t kBufferSize = 0x40000; // 256KB.
327  std::unique_ptr<uint8_t[]> buffer(new uint8_t[kBufferSize]);
328  int64_t bytes_copied = 0;
329  while (bytes_copied < max_copy) {
330  const int64_t size = std::min(kBufferSize, max_copy - bytes_copied);
331  const int64_t bytes_read = source->Read(buffer.get(), size);
332  if (bytes_read < 0)
333  return bytes_read;
334  if (bytes_read == 0)
335  break;
336 
337  int64_t total_bytes_written = 0;
338  while (total_bytes_written < bytes_read) {
339  const int64_t bytes_written = destination->Write(
340  buffer.get() + total_bytes_written, bytes_read - total_bytes_written);
341  if (bytes_written < 0)
342  return bytes_written;
343 
344  total_bytes_written += bytes_written;
345  }
346 
347  DCHECK_EQ(total_bytes_written, bytes_read);
348  bytes_copied += bytes_read;
349  }
350 
351  return bytes_copied;
352 }
353 
355  const BufferCallbackParams& callback_params,
356  const std::string& name) {
357  if (name.empty())
358  return "";
359  return base::StringPrintf("%s%" PRIdPTR "/%s", kCallbackFilePrefix,
360  reinterpret_cast<intptr_t>(&callback_params),
361  name.c_str());
362 }
363 
364 bool File::ParseCallbackFileName(const std::string& callback_file_name,
365  const BufferCallbackParams** callback_params,
366  std::string* name) {
367  size_t pos = callback_file_name.find("/");
368  int64_t callback_address = 0;
369  if (pos == std::string::npos ||
370  !base::StringToInt64(callback_file_name.substr(0, pos),
371  &callback_address)) {
372  LOG(ERROR) << "Expecting CallbackFile with name like "
373  "'<callback address>/<entity name>', but seeing "
374  << callback_file_name;
375  return false;
376  }
377  *callback_params = reinterpret_cast<BufferCallbackParams*>(callback_address);
378  *name = callback_file_name.substr(pos + 1);
379  return true;
380 }
381 
382 } // namespace shaka
virtual int64_t Write(const void *buffer, uint64_t length)=0
static void Delete(const std::string &file_name)
Definition: memory_file.cc:190
static File * Open(const char *file_name, const char *mode)
Definition: file.cc:176
static bool Delete(const char *file_name)
Definition: file.cc:198
Define an abstract file interface.
Definition: file.h:26
static bool Copy(const char *from_file_name, const char *to_file_name)
Definition: file.cc:281
static int64_t CopyFile(File *source, File *destination)
Definition: file.cc:316
static bool ReadFileToString(const char *file_name, std::string *contents)
Definition: file.cc:216
virtual int64_t Read(void *buffer, uint64_t length)=0
bool TempFilePath(const std::string &temp_dir, std::string *temp_file_path)
Definition: file_util.cc:38
All the methods that are virtual are virtual for mocking.
static int64_t GetFileSize(const char *file_name)
Definition: file.cc:207
virtual bool Close()=0
virtual int64_t Size()=0
static bool WriteStringToFile(const char *file_name, const std::string &contents)
Definition: file.cc:234
static std::string MakeCallbackFileName(const BufferCallbackParams &callback_params, const std::string &name)
Definition: file.cc:354
static bool Delete(const char *file_name)
Definition: local_file.cc:199
static File * OpenWithNoBuffering(const char *file_name, const char *mode)
Definition: file.cc:187
static bool WriteFileAtomically(const char *file_name, const std::string &contents)
Definition: file.cc:262
static bool ParseCallbackFileName(const std::string &callback_file_name, const BufferCallbackParams **callback_params, std::string *name)
Definition: file.cc:364
virtual bool Open()=0
Internal open. Should not be used directly.
Buffer callback params.