1 // Copyright 2007, Google Inc.
3 // Redistribution and use in source and binary forms, with or without
4 // modification, are permitted provided that the following conditions are met:
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
31 // Currently all non-win32 gears targets are POSIX compliant.
35 #include <sys/types.h>
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> {
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
)) {
75 DIR *the_dir
= opendir(path_utf8
.c_str());
76 if (the_dir
== NULL
) {
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.
99 struct dirent
*dir_info
= readdir(the_dir
);
100 if (dir_info
== NULL
) {
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) {
120 if (error_reading_dir_contents
) {
123 results
->swap(local_dir_contents
);
128 class CloseFileFunctor
{
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
)) {
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
);
148 if (close(fd
) != 0) {
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
)) {
162 if (stat(path_utf8
.c_str(), &tmp
) != 0) {
169 bool File::Exists(const char16
*full_filepath
) {
170 struct stat stat_data
;
171 if (!StatFile(full_filepath
, &stat_data
)) {
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
)) {
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
)) {
192 return static_cast<int64
>(stat_data
.st_size
);
195 int File::ReadFileSegmentToBuffer(const char16
*full_filepath
,
199 if (max_bytes
<= 0 || position
< 0) {
203 std::string file_path_utf8
;
204 if (!String16ToUTF8(full_filepath
, &file_path_utf8
)) {
207 ScopedFile
scoped_file(fopen(file_path_utf8
.c_str(), "rb"));
208 if (scoped_file
.get() == NULL
) {
212 if (position
!= 0 && fseek(scoped_file
.get(), position
, SEEK_SET
) != 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) {
225 bool File::ReadFileToVector(const char16
*full_filepath
,
226 std::vector
<uint8
> *data
) {
228 struct stat stat_data
;
229 if (!StatFile(full_filepath
, &stat_data
)) {
232 size_t file_size
= stat_data
.st_size
;
234 std::string file_path_utf8
;
235 if (!String16ToUTF8(full_filepath
, &file_path_utf8
)) {
238 ScopedFile
scoped_file(fopen(file_path_utf8
.c_str(), "rb"));
239 if (scoped_file
.get() == NULL
) {
243 std::vector
<uint8
> local_data
;
244 local_data
.resize(file_size
);
245 if (local_data
.size() != file_size
) {
250 size_t bytes_read
= fread(&(local_data
)[0], 1, file_size
, scoped_file
.get());
252 if (bytes_read
< file_size
) {
254 } else if (ferror(scoped_file
.get())) {
258 if (fclose(scoped_file
.release()) != 0) {
262 data
->swap(local_data
);
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
,
274 std::string file_path_utf8
;
275 if (!String16ToUTF8(full_filepath
, &file_path_utf8
)) {
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
) {
285 // Erase current contents of file.
286 if (ftruncate(fileno(scoped_file
.get()), 0) != 0) {
290 // Behave the same as other platforms - don't fail on write of 0 bytes
293 if (fwrite(buf
, 1, length
, scoped_file
.get()) !=
294 static_cast<size_t>(length
)) {
299 if (fclose(scoped_file
.release()) != 0) {
306 bool File::Delete(const char16
*full_filepath
) {
307 std::string path_utf8
;
308 if (!String16ToUTF8(full_filepath
, &path_utf8
)) {
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
);
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
) {
334 if (!UTF8ToString16(temp_dir_template
, full_filepath
)) {
340 bool File::RecursivelyCreateDir(const char16
*full_dirpath
) {
341 // If directory already exists, no need to do anything.
342 if(File::DirectoryExists(full_dirpath
)) {
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();
354 // '//', '.' & '..' shouldn't be present in the path, but if they are fail
356 if (it
->empty() || *it
== STRING16(L
".") || *it
== STRING16(L
"..")) {
357 assert("Badly formed pathname" == NULL
);
361 long_path
= long_path
+ kPathSeparator
+ *it
;
363 std::string path_utf8
;
364 if (!String16ToUTF8(long_path
.c_str(), &path_utf8
)) {
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
372 if (errno
!= EEXIST
) {
374 } else if (!File::DirectoryExists(long_path
.c_str())) {
375 return false; // We've collided with a file having the same name.
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
)) {
389 DirContentsVector dir_contents
;
390 if (!ReadDir(del_path_utf16
, &dir_contents
)) {
394 for (DirContentsVectorConstIterator it
= dir_contents
.begin();
395 it
!= dir_contents
.end();
397 std::string path_component_to_delete
= del_path
+ kPathSeparatorUTF8
+
399 if (it
->IsDirectory()) {
400 if (!DeleteRecursivelyImpl(path_component_to_delete
)) {
404 if (unlink(path_component_to_delete
.c_str()) != 0) {
410 if (rmdir(del_path
.c_str()) != 0) {
417 bool File::DeleteRecursively(const char16
*full_dirpath
) {
418 std::string dir_to_delete
;
419 if (!String16ToUTF8(full_dirpath
, &dir_to_delete
)) {
423 // We can only operate on a directory.
424 if(!File::DirectoryExists(full_dirpath
)) {
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
);