Shaka Packager SDK
local_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/local_file.h"
8 
9 #include <stdio.h>
10 #if defined(OS_WIN)
11 #include <windows.h>
12 #else
13 #include <sys/stat.h>
14 #endif // defined(OS_WIN)
15 #include "packager/base/files/file_path.h"
16 #include "packager/base/files/file_util.h"
17 #include "packager/base/logging.h"
18 
19 namespace shaka {
20 namespace {
21 
22 // Check if the directory |path| exists. Returns false if it does not exist or
23 // it is not a directory. On non-Windows, |mode| will be filled with the file
24 // permission bits on success.
25 bool DirectoryExists(const base::FilePath& path, int* mode) {
26 #if defined(OS_WIN)
27  DWORD fileattr = GetFileAttributes(path.value().c_str());
28  if (fileattr != INVALID_FILE_ATTRIBUTES)
29  return (fileattr & FILE_ATTRIBUTE_DIRECTORY) != 0;
30 #else
31  struct stat info;
32  if (stat(path.value().c_str(), &info) != 0)
33  return false;
34  if (S_ISDIR(info.st_mode)) {
35  const int FILE_PERMISSION_MASK = S_IRWXU | S_IRWXG | S_IRWXO;
36  if (mode)
37  *mode = info.st_mode & FILE_PERMISSION_MASK;
38  return true;
39  }
40 #endif
41  return false;
42 }
43 
44 // Create all the inexistent directories in the path. Returns true on success or
45 // if the directory already exists.
46 bool CreateDirectory(const base::FilePath& full_path) {
47  std::vector<base::FilePath> subpaths;
48 
49  // Collect a list of all parent directories.
50  base::FilePath last_path = full_path;
51  subpaths.push_back(full_path);
52  for (base::FilePath path = full_path.DirName();
53  path.value() != last_path.value(); path = path.DirName()) {
54  subpaths.push_back(path);
55  last_path = path;
56  }
57 
58  // For non-Windows only. File permission for the new directories.
59  // The file permission will be inherited from the last existing directory in
60  // the file path. If none of the directory exists in the path, it is set to
61  // 0755 by default.
62  int mode = 0755;
63 
64  // Iterate through the parents and create the missing ones.
65  for (auto i = subpaths.rbegin(); i != subpaths.rend(); ++i) {
66  if (DirectoryExists(*i, &mode)) {
67  continue;
68  }
69 #if defined(OS_WIN)
70  if (::CreateDirectory(i->value().c_str(), nullptr)) {
71  continue;
72  }
73 #else
74  if (mkdir(i->value().c_str(), mode) == 0) {
75  continue;
76  }
77 #endif
78 
79  // Mkdir failed, but it might have failed with EEXIST, or some other error
80  // due to the the directory appearing out of thin air. This can occur if
81  // two processes are trying to create the same file system tree at the same
82  // time. Check to see if it exists and make sure it is a directory.
83  const auto saved_error_code = ::logging::GetLastSystemErrorCode();
84  if (!DirectoryExists(*i, nullptr)) {
85  LOG(ERROR) << "Failed to create directory " << i->value().c_str()
86  << " ErrorCode " << saved_error_code;
87  return false;
88  }
89  }
90  return true;
91 }
92 
93 } // namespace
94 
95 // Always open files in binary mode.
96 const char kAdditionalFileMode[] = "b";
97 
98 LocalFile::LocalFile(const char* file_name, const char* mode)
99  : File(file_name), file_mode_(mode), internal_file_(NULL) {
100  if (file_mode_.find(kAdditionalFileMode) == std::string::npos)
101  file_mode_ += kAdditionalFileMode;
102 }
103 
105  bool result = true;
106  if (internal_file_) {
107  result = base::CloseFile(internal_file_);
108  internal_file_ = NULL;
109  }
110  delete this;
111  return result;
112 }
113 
114 int64_t LocalFile::Read(void* buffer, uint64_t length) {
115  DCHECK(buffer != NULL);
116  DCHECK(internal_file_ != NULL);
117  size_t bytes_read = fread(buffer, sizeof(char), length, internal_file_);
118  VLOG(2) << "Read " << length << " return " << bytes_read << " error "
119  << ferror(internal_file_);
120  if (bytes_read == 0 && ferror(internal_file_) != 0) {
121  return -1;
122  }
123  return bytes_read;
124 }
125 
126 int64_t LocalFile::Write(const void* buffer, uint64_t length) {
127  DCHECK(buffer != NULL);
128  DCHECK(internal_file_ != NULL);
129  size_t bytes_written = fwrite(buffer, sizeof(char), length, internal_file_);
130  VLOG(2) << "Write " << length << " return " << bytes_written << " error "
131  << ferror(internal_file_);
132  if (bytes_written == 0 && ferror(internal_file_) != 0) {
133  return -1;
134  }
135  return bytes_written;
136 }
137 
138 int64_t LocalFile::Size() {
139  DCHECK(internal_file_ != NULL);
140 
141  // Flush any buffered data, so we get the true file size.
142  if (!Flush()) {
143  LOG(ERROR) << "Cannot flush file.";
144  return -1;
145  }
146 
147  int64_t file_size;
148  if (!base::GetFileSize(base::FilePath::FromUTF8Unsafe(file_name()),
149  &file_size)) {
150  LOG(ERROR) << "Cannot get file size.";
151  return -1;
152  }
153  return file_size;
154 }
155 
157  DCHECK(internal_file_ != NULL);
158  return ((fflush(internal_file_) == 0) && !ferror(internal_file_));
159 }
160 
161 bool LocalFile::Seek(uint64_t position) {
162 #if defined(OS_WIN)
163  return _fseeki64(internal_file_, static_cast<__int64>(position), SEEK_SET) ==
164  0;
165 #else
166  return fseeko(internal_file_, position, SEEK_SET) >= 0;
167 #endif // !defined(OS_WIN)
168 }
169 
170 bool LocalFile::Tell(uint64_t* position) {
171 #if defined(OS_WIN)
172  __int64 offset = _ftelli64(internal_file_);
173 #else
174  off_t offset = ftello(internal_file_);
175 #endif // !defined(OS_WIN)
176  if (offset < 0)
177  return false;
178  *position = static_cast<uint64_t>(offset);
179  return true;
180 }
181 
182 LocalFile::~LocalFile() {}
183 
185  base::FilePath file_path(base::FilePath::FromUTF8Unsafe(file_name()));
186 
187  // Create upper level directories for write mode.
188  if (file_mode_.find("w") != std::string::npos) {
189  // The function returns true if the directories already exist.
190  if (!shaka::CreateDirectory(file_path.DirName())) {
191  return false;
192  }
193  }
194 
195  internal_file_ = base::OpenFile(file_path, file_mode_.c_str());
196  return (internal_file_ != NULL);
197 }
198 
199 bool LocalFile::Delete(const char* file_name) {
200  return base::DeleteFile(base::FilePath::FromUTF8Unsafe(file_name), false);
201 }
202 
203 } // namespace shaka
Define an abstract file interface.
Definition: file.h:27
const std::string & file_name() const
Definition: file.h:95
bool Flush() override
Definition: local_file.cc:156
bool Seek(uint64_t position) override
Definition: local_file.cc:161
int64_t Read(void *buffer, uint64_t length) override
Definition: local_file.cc:114
LocalFile(const char *file_name, const char *mode)
Definition: local_file.cc:98
bool Close() override
Definition: local_file.cc:104
static bool Delete(const char *file_name)
Definition: local_file.cc:199
bool Open() override
Internal open. Should not be used directly.
Definition: local_file.cc:184
int64_t Size() override
Definition: local_file.cc:138
int64_t Write(const void *buffer, uint64_t length) override
Definition: local_file.cc:126
bool Tell(uint64_t *position) override
Definition: local_file.cc:170
All the methods that are virtual are virtual for mocking.