[Author: zork]
[google-gears.git] / gears / base / common / file_posix.cc
blob80dcd0beb9ee2309689cbf8d6cd497d3a8a4e360
1 // Copyright 2007, Google Inc.
2 //
3 // Redistribution and use in source and binary forms, with or without
4 // modification, are permitted provided that the following conditions are met:
5 //
6 // 1. Redistributions of source code must retain the above copyright notice,
7 // this list of conditions and the following disclaimer.
8 // 2. Redistributions in binary form must reproduce the above copyright notice,
9 // this list of conditions and the following disclaimer in the documentation
10 // and/or other materials provided with the distribution.
11 // 3. Neither the name of Google Inc. nor the names of its contributors may be
12 // used to endorse or promote products derived from this software without
13 // specific prior written permission.
15 // THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
16 // WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
17 // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
18 // EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
19 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
21 // OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
22 // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
23 // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
24 // ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 // Implementation of the File class for use on POSIX compliant platforms.
27 // Some methods implementations are browser neutral and can be found
28 // in file.cc.
30 #ifdef WIN32
31 // Currently all non-win32 gears targets are POSIX compliant.
32 #else
33 #include <dirent.h>
34 #include <fcntl.h>
35 #include <sys/types.h>
36 #include <sys/stat.h>
37 #include <unistd.h>
38 #include <cassert>
39 #include <cerrno>
40 #include <cstdio>
41 #include <cstring>
42 #include <string>
43 #include <vector>
44 #include "gears/base/common/file.h"
45 #include "gears/base/common/paths.h"
46 #include "gears/base/common/scoped_token.h"
47 #include "gears/base/common/string_utils.h"
49 // paths.h defines a char16 version of this, but we use UTF8 internally here
50 // and POSIX systems are consistent in this regard.
51 static const char kPathSeparatorUTF8 = '/';
53 // Wraps filename, "is file a directory?" pair.
54 class DirEntry : public std::pair<std::string, bool> {
55 public:
56 DirEntry(const std::string &filename, bool is_dir) :
57 std::pair<std::string,bool>(filename, is_dir) {}
59 const std::string &Filename() const { return first; }
60 bool IsDirectory() const { return second; }
62 typedef std::vector<DirEntry> DirContentsVector;
63 typedef DirContentsVector::const_iterator DirContentsVectorConstIterator;
65 // Places the direct contents of the 'path' directory into results. This
66 // method is not recursive.
67 // 'results' may not be NULL.
69 // Returns false on error in which case 'results' isn't modified.
70 static bool ReadDir(const std::string16 &path, DirContentsVector *results) {
71 std::string path_utf8;
72 if (!String16ToUTF8(path.c_str(), &path_utf8)) {
73 return false;
75 DIR *the_dir = opendir(path_utf8.c_str());
76 if (the_dir == NULL) {
77 return false;
80 DirContentsVector local_dir_contents;
81 bool error_reading_dir_contents = false;
83 // Zero errno - as a NULL return value from readdir() can mean we're done
84 // reading a directory OR that an error has occured, so our only way of
85 // knowing the difference is via errno.
86 // The need to do this arose from a unit test failing - readdir() does not
87 // zero errno itself (at least under OSX).
89 // To clarify, the error case we're solving here, is when we reach this point
90 // with errno != 0. Then when readdir() returns NULL inside the loop to
91 // signify that it's finished reading the directory - when we check errno we
92 // will find a non-zero value and mistakenly think that readdir() has failed.
94 // We do not need to move this inside the loop, because no change will be
95 // made to errno inside the loop if there are no errors, and if there are
96 // we want to bail immediately.
97 errno = 0;
98 while (true) {
99 struct dirent *dir_info = readdir(the_dir);
100 if (dir_info == NULL) {
101 if (errno != 0) {
102 error_reading_dir_contents = true;
104 break; // Reached end of directory contents.
106 if ((strcmp(dir_info->d_name, "..") == 0) ||
107 (strcmp(dir_info->d_name, ".") == 0)) {
108 continue; // Skip parent and current directories.
111 std::string filename(dir_info->d_name);
112 bool is_dir = (dir_info->d_type == DT_DIR);
113 local_dir_contents.push_back(DirEntry(filename, is_dir));
116 if (closedir(the_dir) != 0) {
117 return false;
120 if (error_reading_dir_contents) {
121 return false;
123 results->swap(local_dir_contents);
124 return true;
127 // Scoped file.
128 class CloseFileFunctor {
129 public:
130 void operator()(FILE *file) const {
131 if (file) { fclose(file); }
134 typedef scoped_token<FILE *, CloseFileFunctor> ScopedFile;
136 bool File::CreateNewFile(const char16 *path) {
137 std::string path_utf8;
138 if (!String16ToUTF8(path, &path_utf8)) {
139 return false;
142 // Create new file with permission 0600, fail if the file already exists.
143 int fd = open(path_utf8.c_str(), O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
144 if (fd < 0) {
145 return false;
148 if (close(fd) != 0) {
149 return false;
151 return true;
154 // Retrieves the stat() struct for the given file.
155 static bool StatFile(const char16 *path, struct stat *stat_data) {
156 std::string path_utf8;
157 if (!String16ToUTF8(path, &path_utf8)) {
158 return false;
161 struct stat tmp;
162 if (stat(path_utf8.c_str(), &tmp) != 0) {
163 return false;
165 *stat_data = tmp;
166 return true;
169 bool File::Exists(const char16 *full_filepath) {
170 struct stat stat_data;
171 if (!StatFile(full_filepath, &stat_data)) {
172 return false;
175 return S_ISREG(stat_data.st_mode);
178 bool File::DirectoryExists(const char16 *full_dirpath) {
179 struct stat stat_data;
180 if (!StatFile(full_dirpath, &stat_data)) {
181 return false;
184 return S_ISDIR(stat_data.st_mode);
187 int64 File::GetFileSize(const char16 *full_filepath) {
188 struct stat stat_data;
189 if (!StatFile(full_filepath, &stat_data)) {
190 return 0;
192 return static_cast<int64>(stat_data.st_size);
195 int File::ReadFileSegmentToBuffer(const char16 *full_filepath,
196 uint8* destination,
197 int max_bytes,
198 int64 position) {
199 if (max_bytes <= 0 || position < 0) {
200 return 0;
203 std::string file_path_utf8;
204 if (!String16ToUTF8(full_filepath, &file_path_utf8)) {
205 return 0;
207 ScopedFile scoped_file(fopen(file_path_utf8.c_str(), "rb"));
208 if (scoped_file.get() == NULL) {
209 return 0;
212 if (position != 0 && fseek(scoped_file.get(), position, SEEK_SET) != 0) {
213 return 0;
216 size_t bytes_read = fread(destination, 1, max_bytes, scoped_file.get());
218 if (ferror(scoped_file.get()) || fclose(scoped_file.release()) != 0) {
219 return 0;
222 return bytes_read;
225 bool File::ReadFileToVector(const char16 *full_filepath,
226 std::vector<uint8> *data) {
227 // Get file size.
228 struct stat stat_data;
229 if (!StatFile(full_filepath, &stat_data)) {
230 return false;
232 size_t file_size = stat_data.st_size;
234 std::string file_path_utf8;
235 if (!String16ToUTF8(full_filepath, &file_path_utf8)) {
236 return false;
238 ScopedFile scoped_file(fopen(file_path_utf8.c_str(), "rb"));
239 if (scoped_file.get() == NULL) {
240 return false;
243 std::vector<uint8> local_data;
244 local_data.resize(file_size);
245 if (local_data.size() != file_size) {
246 return false;
249 // Read Data.
250 size_t bytes_read = fread(&(local_data)[0], 1, file_size, scoped_file.get());
252 if (bytes_read < file_size) {
253 return false;
254 } else if (ferror(scoped_file.get())) {
255 return false;
258 if (fclose(scoped_file.release()) != 0) {
259 return false;
262 data->swap(local_data);
263 return true;
266 bool File::WriteVectorToFile(const char16 *full_filepath,
267 const std::vector<uint8> *data) {
268 const uint8 *first_byte = data->size() ? &(data->at(0)) : NULL;
269 return WriteBytesToFile(full_filepath, first_byte, data->size());
272 bool File::WriteBytesToFile(const char16 *full_filepath, const uint8 *buf,
273 int length) {
274 std::string file_path_utf8;
275 if (!String16ToUTF8(full_filepath, &file_path_utf8)) {
276 return false;
279 // Don't create a file if it doesn't exist already.
280 ScopedFile scoped_file(fopen(file_path_utf8.c_str(), "rb+"));
281 if (scoped_file.get() == NULL) {
282 return false;
285 // Erase current contents of file.
286 if (ftruncate(fileno(scoped_file.get()), 0) != 0) {
287 return false;
290 // Behave the same as other platforms - don't fail on write of 0 bytes
291 // if file exists.
292 if (length > 0) {
293 if (fwrite(buf, 1, length, scoped_file.get()) !=
294 static_cast<size_t>(length)) {
295 return false;
299 if (fclose(scoped_file.release()) != 0) {
300 return false;
303 return true;
306 bool File::Delete(const char16 *full_filepath) {
307 std::string path_utf8;
308 if (!String16ToUTF8(full_filepath, &path_utf8)) {
309 return false;
312 return unlink(path_utf8.c_str()) == 0;
315 int File::GetDirectoryFileCount(const char16 *full_dirpath) {
316 std::string16 the_dir(full_dirpath);
317 DirContentsVector dir_contents;
318 bool success = ReadDir(the_dir, &dir_contents);
319 if (!success) {
320 return 0;
321 } else {
322 return dir_contents.size();
326 bool File::CreateNewTempDirectory(std::string16 *full_filepath) {
327 char temp_dir_template[] = "/tmp/" PRODUCT_SHORT_NAME_ASCII "TempXXXXXX";
329 // mkdtemp() creates the directory with permissions set to 0700.
330 if (mkdtemp(temp_dir_template) == NULL) {
331 return false;
334 if (!UTF8ToString16(temp_dir_template, full_filepath)) {
335 return false;
337 return true;
340 bool File::RecursivelyCreateDir(const char16 *full_dirpath) {
341 // If directory already exists, no need to do anything.
342 if(File::DirectoryExists(full_dirpath)) {
343 return true;
346 File::PathComponents path_components;
347 File::SplitPath(std::string16(full_dirpath), &path_components);
349 std::string16 long_path;
350 // Recursively create directories.
351 for (File::PathComponents::const_iterator it = path_components.begin();
352 it != path_components.end();
353 ++it) {
354 // '//', '.' & '..' shouldn't be present in the path, but if they are fail
355 // hard!
356 if (it->empty() || *it == STRING16(L".") || *it == STRING16(L"..")) {
357 assert("Badly formed pathname" == NULL);
358 return false;
361 long_path = long_path + kPathSeparator + *it;
363 std::string path_utf8;
364 if (!String16ToUTF8(long_path.c_str(), &path_utf8)) {
365 return false;
368 // Create directory with permissions set to 0700.
369 if (mkdir(path_utf8.c_str(), S_IRUSR | S_IWUSR | S_IXUSR) != 0) {
370 // Any error value other than "directory already exists" is considered
371 // fatal.
372 if (errno != EEXIST) {
373 return false;
374 } else if (!File::DirectoryExists(long_path.c_str())) {
375 return false; // We've collided with a file having the same name.
380 return true;
383 // Recursive function for use by DeleteRecursively.
384 static bool DeleteRecursivelyImpl(const std::string &del_path) {
385 std::string16 del_path_utf16;
386 if (!UTF8ToString16(del_path.c_str(), &del_path_utf16)) {
387 return false;
389 DirContentsVector dir_contents;
390 if (!ReadDir(del_path_utf16, &dir_contents)) {
391 return false;
394 for (DirContentsVectorConstIterator it = dir_contents.begin();
395 it != dir_contents.end();
396 ++it) {
397 std::string path_component_to_delete = del_path + kPathSeparatorUTF8 +
398 it->Filename();
399 if (it->IsDirectory()) {
400 if (!DeleteRecursivelyImpl(path_component_to_delete)) {
401 return false;
403 } else {
404 if (unlink(path_component_to_delete.c_str()) != 0) {
405 return false;
410 if (rmdir(del_path.c_str()) != 0) {
411 return false;
414 return true;
417 bool File::DeleteRecursively(const char16 *full_dirpath) {
418 std::string dir_to_delete;
419 if (!String16ToUTF8(full_dirpath, &dir_to_delete)) {
420 return false;
423 // We can only operate on a directory.
424 if(!File::DirectoryExists(full_dirpath)) {
425 return false;
428 // Cut off trailing slash from directory name if any.
429 std::string path_sep(&kPathSeparatorUTF8, 1);
430 if (EndsWith(dir_to_delete, path_sep)) {
431 dir_to_delete.erase(dir_to_delete.end()-1);
434 return DeleteRecursivelyImpl(dir_to_delete);
437 #endif // !WIN32