• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "base/file_util.h"
6 
7 #include <dirent.h>
8 #include <errno.h>
9 #include <fcntl.h>
10 #include <fnmatch.h>
11 #include <libgen.h>
12 #include <limits.h>
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <sys/errno.h>
17 #include <sys/mman.h>
18 #include <sys/param.h>
19 #include <sys/stat.h>
20 #include <sys/time.h>
21 #include <sys/types.h>
22 #include <time.h>
23 #include <unistd.h>
24 
25 #if defined(OS_MACOSX)
26 #include <AvailabilityMacros.h>
27 #elif !defined(ANDROID)
28 #include <glib.h>
29 #endif
30 
31 #include <fstream>
32 
33 #include "base/basictypes.h"
34 #include "base/eintr_wrapper.h"
35 #include "base/file_path.h"
36 #include "base/logging.h"
37 #include "base/memory/scoped_ptr.h"
38 #include "base/memory/singleton.h"
39 #include "base/string_util.h"
40 #include "base/sys_string_conversions.h"
41 #include "base/threading/thread_restrictions.h"
42 #include "base/time.h"
43 #include "base/utf_string_conversions.h"
44 
45 namespace file_util {
46 
47 namespace {
48 
49 // Helper for NormalizeFilePath(), defined below.
RealPath(const FilePath & path,FilePath * real_path)50 bool RealPath(const FilePath& path, FilePath* real_path) {
51   base::ThreadRestrictions::AssertIOAllowed();  // For realpath().
52   FilePath::CharType buf[PATH_MAX];
53   if (!realpath(path.value().c_str(), buf))
54     return false;
55 
56   *real_path = FilePath(buf);
57   return true;
58 }
59 
60 }  // namespace
61 
62 #if defined(OS_OPENBSD) || defined(OS_FREEBSD) || \
63     (defined(OS_MACOSX) && \
64      MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5)
65 typedef struct stat stat_wrapper_t;
CallStat(const char * path,stat_wrapper_t * sb)66 static int CallStat(const char *path, stat_wrapper_t *sb) {
67   base::ThreadRestrictions::AssertIOAllowed();
68   return stat(path, sb);
69 }
70 #else
71 typedef struct stat64 stat_wrapper_t;
CallStat(const char * path,stat_wrapper_t * sb)72 static int CallStat(const char *path, stat_wrapper_t *sb) {
73   base::ThreadRestrictions::AssertIOAllowed();
74   return stat64(path, sb);
75 }
76 #endif
77 
78 
79 #if defined(GOOGLE_CHROME_BUILD)
80 static const char* kTempFileName = ".com.google.chrome.XXXXXX";
81 #else
82 static const char* kTempFileName = ".org.chromium.XXXXXX";
83 #endif
84 
AbsolutePath(FilePath * path)85 bool AbsolutePath(FilePath* path) {
86   base::ThreadRestrictions::AssertIOAllowed();  // For realpath().
87   char full_path[PATH_MAX];
88   if (realpath(path->value().c_str(), full_path) == NULL)
89     return false;
90   *path = FilePath(full_path);
91   return true;
92 }
93 
CountFilesCreatedAfter(const FilePath & path,const base::Time & comparison_time)94 int CountFilesCreatedAfter(const FilePath& path,
95                            const base::Time& comparison_time) {
96   base::ThreadRestrictions::AssertIOAllowed();
97   int file_count = 0;
98 
99   DIR* dir = opendir(path.value().c_str());
100   if (dir) {
101 #if !defined(OS_LINUX) && !defined(OS_MACOSX) && !defined(OS_FREEBSD) && \
102     !defined(OS_OPENBSD) && !defined(OS_SOLARIS)
103   #error Port warning: depending on the definition of struct dirent, \
104          additional space for pathname may be needed
105 #endif
106     struct dirent ent_buf;
107     struct dirent* ent;
108     while (readdir_r(dir, &ent_buf, &ent) == 0 && ent) {
109       if ((strcmp(ent->d_name, ".") == 0) ||
110           (strcmp(ent->d_name, "..") == 0))
111         continue;
112 
113       stat_wrapper_t st;
114       int test = CallStat(path.Append(ent->d_name).value().c_str(), &st);
115       if (test != 0) {
116         PLOG(ERROR) << "stat64 failed";
117         continue;
118       }
119       // Here, we use Time::TimeT(), which discards microseconds. This
120       // means that files which are newer than |comparison_time| may
121       // be considered older. If we don't discard microseconds, it
122       // introduces another issue. Suppose the following case:
123       //
124       // 1. Get |comparison_time| by Time::Now() and the value is 10.1 (secs).
125       // 2. Create a file and the current time is 10.3 (secs).
126       //
127       // As POSIX doesn't have microsecond precision for |st_ctime|,
128       // the creation time of the file created in the step 2 is 10 and
129       // the file is considered older than |comparison_time|. After
130       // all, we may have to accept either of the two issues: 1. files
131       // which are older than |comparison_time| are considered newer
132       // (current implementation) 2. files newer than
133       // |comparison_time| are considered older.
134       if (static_cast<time_t>(st.st_ctime) >= comparison_time.ToTimeT())
135         ++file_count;
136     }
137     closedir(dir);
138   }
139   return file_count;
140 }
141 
142 // TODO(erikkay): The Windows version of this accepts paths like "foo/bar/*"
143 // which works both with and without the recursive flag.  I'm not sure we need
144 // that functionality. If not, remove from file_util_win.cc, otherwise add it
145 // here.
Delete(const FilePath & path,bool recursive)146 bool Delete(const FilePath& path, bool recursive) {
147   base::ThreadRestrictions::AssertIOAllowed();
148   const char* path_str = path.value().c_str();
149   stat_wrapper_t file_info;
150   int test = CallStat(path_str, &file_info);
151   if (test != 0) {
152     // The Windows version defines this condition as success.
153     bool ret = (errno == ENOENT || errno == ENOTDIR);
154     return ret;
155   }
156   if (!S_ISDIR(file_info.st_mode))
157     return (unlink(path_str) == 0);
158   if (!recursive)
159     return (rmdir(path_str) == 0);
160 
161   bool success = true;
162   std::stack<std::string> directories;
163   directories.push(path.value());
164   FileEnumerator traversal(path, true, static_cast<FileEnumerator::FILE_TYPE>(
165         FileEnumerator::FILES | FileEnumerator::DIRECTORIES |
166         FileEnumerator::SHOW_SYM_LINKS));
167   for (FilePath current = traversal.Next(); success && !current.empty();
168        current = traversal.Next()) {
169     FileEnumerator::FindInfo info;
170     traversal.GetFindInfo(&info);
171 
172     if (S_ISDIR(info.stat.st_mode))
173       directories.push(current.value());
174     else
175       success = (unlink(current.value().c_str()) == 0);
176   }
177 
178   while (success && !directories.empty()) {
179     FilePath dir = FilePath(directories.top());
180     directories.pop();
181     success = (rmdir(dir.value().c_str()) == 0);
182   }
183   return success;
184 }
185 
Move(const FilePath & from_path,const FilePath & to_path)186 bool Move(const FilePath& from_path, const FilePath& to_path) {
187   base::ThreadRestrictions::AssertIOAllowed();
188   // Windows compatibility: if to_path exists, from_path and to_path
189   // must be the same type, either both files, or both directories.
190   stat_wrapper_t to_file_info;
191   if (CallStat(to_path.value().c_str(), &to_file_info) == 0) {
192     stat_wrapper_t from_file_info;
193     if (CallStat(from_path.value().c_str(), &from_file_info) == 0) {
194       if (S_ISDIR(to_file_info.st_mode) != S_ISDIR(from_file_info.st_mode))
195         return false;
196     } else {
197       return false;
198     }
199   }
200 
201   if (rename(from_path.value().c_str(), to_path.value().c_str()) == 0)
202     return true;
203 
204   if (!CopyDirectory(from_path, to_path, true))
205     return false;
206 
207   Delete(from_path, true);
208   return true;
209 }
210 
ReplaceFile(const FilePath & from_path,const FilePath & to_path)211 bool ReplaceFile(const FilePath& from_path, const FilePath& to_path) {
212   base::ThreadRestrictions::AssertIOAllowed();
213   return (rename(from_path.value().c_str(), to_path.value().c_str()) == 0);
214 }
215 
CopyDirectory(const FilePath & from_path,const FilePath & to_path,bool recursive)216 bool CopyDirectory(const FilePath& from_path,
217                    const FilePath& to_path,
218                    bool recursive) {
219   base::ThreadRestrictions::AssertIOAllowed();
220   // Some old callers of CopyDirectory want it to support wildcards.
221   // After some discussion, we decided to fix those callers.
222   // Break loudly here if anyone tries to do this.
223   // TODO(evanm): remove this once we're sure it's ok.
224   DCHECK(to_path.value().find('*') == std::string::npos);
225   DCHECK(from_path.value().find('*') == std::string::npos);
226 
227   char top_dir[PATH_MAX];
228   if (base::strlcpy(top_dir, from_path.value().c_str(),
229                     arraysize(top_dir)) >= arraysize(top_dir)) {
230     return false;
231   }
232 
233   // This function does not properly handle destinations within the source
234   FilePath real_to_path = to_path;
235   if (PathExists(real_to_path)) {
236     if (!AbsolutePath(&real_to_path))
237       return false;
238   } else {
239     real_to_path = real_to_path.DirName();
240     if (!AbsolutePath(&real_to_path))
241       return false;
242   }
243   FilePath real_from_path = from_path;
244   if (!AbsolutePath(&real_from_path))
245     return false;
246   if (real_to_path.value().size() >= real_from_path.value().size() &&
247       real_to_path.value().compare(0, real_from_path.value().size(),
248       real_from_path.value()) == 0)
249     return false;
250 
251   bool success = true;
252   FileEnumerator::FILE_TYPE traverse_type =
253       static_cast<FileEnumerator::FILE_TYPE>(FileEnumerator::FILES |
254       FileEnumerator::SHOW_SYM_LINKS);
255   if (recursive)
256     traverse_type = static_cast<FileEnumerator::FILE_TYPE>(
257         traverse_type | FileEnumerator::DIRECTORIES);
258   FileEnumerator traversal(from_path, recursive, traverse_type);
259 
260   // We have to mimic windows behavior here. |to_path| may not exist yet,
261   // start the loop with |to_path|.
262   FileEnumerator::FindInfo info;
263   FilePath current = from_path;
264   if (stat(from_path.value().c_str(), &info.stat) < 0) {
265     LOG(ERROR) << "CopyDirectory() couldn't stat source directory: " <<
266         from_path.value() << " errno = " << errno;
267     success = false;
268   }
269   struct stat to_path_stat;
270   FilePath from_path_base = from_path;
271   if (recursive && stat(to_path.value().c_str(), &to_path_stat) == 0 &&
272       S_ISDIR(to_path_stat.st_mode)) {
273     // If the destination already exists and is a directory, then the
274     // top level of source needs to be copied.
275     from_path_base = from_path.DirName();
276   }
277 
278   // The Windows version of this function assumes that non-recursive calls
279   // will always have a directory for from_path.
280   DCHECK(recursive || S_ISDIR(info.stat.st_mode));
281 
282   while (success && !current.empty()) {
283     // current is the source path, including from_path, so paste
284     // the suffix after from_path onto to_path to create the target_path.
285     std::string suffix(&current.value().c_str()[from_path_base.value().size()]);
286     // Strip the leading '/' (if any).
287     if (!suffix.empty()) {
288       DCHECK_EQ('/', suffix[0]);
289       suffix.erase(0, 1);
290     }
291     const FilePath target_path = to_path.Append(suffix);
292 
293     if (S_ISDIR(info.stat.st_mode)) {
294       if (mkdir(target_path.value().c_str(), info.stat.st_mode & 01777) != 0 &&
295           errno != EEXIST) {
296         LOG(ERROR) << "CopyDirectory() couldn't create directory: " <<
297             target_path.value() << " errno = " << errno;
298         success = false;
299       }
300     } else if (S_ISREG(info.stat.st_mode)) {
301       if (!CopyFile(current, target_path)) {
302         LOG(ERROR) << "CopyDirectory() couldn't create file: " <<
303             target_path.value();
304         success = false;
305       }
306     } else {
307       LOG(WARNING) << "CopyDirectory() skipping non-regular file: " <<
308           current.value();
309     }
310 
311     current = traversal.Next();
312     traversal.GetFindInfo(&info);
313   }
314 
315   return success;
316 }
317 
PathExists(const FilePath & path)318 bool PathExists(const FilePath& path) {
319   base::ThreadRestrictions::AssertIOAllowed();
320   return access(path.value().c_str(), F_OK) == 0;
321 }
322 
PathIsWritable(const FilePath & path)323 bool PathIsWritable(const FilePath& path) {
324   base::ThreadRestrictions::AssertIOAllowed();
325   return access(path.value().c_str(), W_OK) == 0;
326 }
327 
DirectoryExists(const FilePath & path)328 bool DirectoryExists(const FilePath& path) {
329   base::ThreadRestrictions::AssertIOAllowed();
330   stat_wrapper_t file_info;
331   if (CallStat(path.value().c_str(), &file_info) == 0)
332     return S_ISDIR(file_info.st_mode);
333   return false;
334 }
335 
336 // TODO(erikkay): implement
337 #if 0
338 bool GetFileCreationLocalTimeFromHandle(int fd,
339                                         LPSYSTEMTIME creation_time) {
340   if (!file_handle)
341     return false;
342 
343   FILETIME utc_filetime;
344   if (!GetFileTime(file_handle, &utc_filetime, NULL, NULL))
345     return false;
346 
347   FILETIME local_filetime;
348   if (!FileTimeToLocalFileTime(&utc_filetime, &local_filetime))
349     return false;
350 
351   return !!FileTimeToSystemTime(&local_filetime, creation_time);
352 }
353 
354 bool GetFileCreationLocalTime(const std::string& filename,
355                               LPSYSTEMTIME creation_time) {
356   ScopedHandle file_handle(
357       CreateFile(filename.c_str(), GENERIC_READ,
358                  FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
359                  OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL));
360   return GetFileCreationLocalTimeFromHandle(file_handle.Get(), creation_time);
361 }
362 #endif
363 
ReadFromFD(int fd,char * buffer,size_t bytes)364 bool ReadFromFD(int fd, char* buffer, size_t bytes) {
365   size_t total_read = 0;
366   while (total_read < bytes) {
367     ssize_t bytes_read =
368         HANDLE_EINTR(read(fd, buffer + total_read, bytes - total_read));
369     if (bytes_read <= 0)
370       break;
371     total_read += bytes_read;
372   }
373   return total_read == bytes;
374 }
375 
CreateSymbolicLink(const FilePath & target_path,const FilePath & symlink_path)376 bool CreateSymbolicLink(const FilePath& target_path,
377                         const FilePath& symlink_path) {
378   DCHECK(!symlink_path.empty());
379   DCHECK(!target_path.empty());
380   return ::symlink(target_path.value().c_str(),
381                    symlink_path.value().c_str()) != -1;
382 }
383 
ReadSymbolicLink(const FilePath & symlink_path,FilePath * target_path)384 bool ReadSymbolicLink(const FilePath& symlink_path,
385                       FilePath* target_path) {
386   DCHECK(!symlink_path.empty());
387   DCHECK(target_path);
388   char buf[PATH_MAX];
389   ssize_t count = ::readlink(symlink_path.value().c_str(), buf, arraysize(buf));
390 
391   if (count <= 0) {
392     target_path->clear();
393     return false;
394   }
395 
396   *target_path = FilePath(FilePath::StringType(buf, count));
397   return true;
398 }
399 
400 // Creates and opens a temporary file in |directory|, returning the
401 // file descriptor. |path| is set to the temporary file path.
402 // This function does NOT unlink() the file.
CreateAndOpenFdForTemporaryFile(FilePath directory,FilePath * path)403 int CreateAndOpenFdForTemporaryFile(FilePath directory, FilePath* path) {
404   base::ThreadRestrictions::AssertIOAllowed();  // For call to mkstemp().
405   *path = directory.Append(kTempFileName);
406   const std::string& tmpdir_string = path->value();
407   // this should be OK since mkstemp just replaces characters in place
408   char* buffer = const_cast<char*>(tmpdir_string.c_str());
409 
410   return HANDLE_EINTR(mkstemp(buffer));
411 }
412 
CreateTemporaryFile(FilePath * path)413 bool CreateTemporaryFile(FilePath* path) {
414   base::ThreadRestrictions::AssertIOAllowed();  // For call to close().
415   FilePath directory;
416   if (!GetTempDir(&directory))
417     return false;
418   int fd = CreateAndOpenFdForTemporaryFile(directory, path);
419   if (fd < 0)
420     return false;
421   ignore_result(HANDLE_EINTR(close(fd)));
422   return true;
423 }
424 
CreateAndOpenTemporaryShmemFile(FilePath * path)425 FILE* CreateAndOpenTemporaryShmemFile(FilePath* path) {
426   FilePath directory;
427   if (!GetShmemTempDir(&directory))
428     return NULL;
429 
430   return CreateAndOpenTemporaryFileInDir(directory, path);
431 }
432 
CreateAndOpenTemporaryFileInDir(const FilePath & dir,FilePath * path)433 FILE* CreateAndOpenTemporaryFileInDir(const FilePath& dir, FilePath* path) {
434   int fd = CreateAndOpenFdForTemporaryFile(dir, path);
435   if (fd < 0)
436     return NULL;
437 
438   FILE* file = fdopen(fd, "a+");
439   if (!file)
440     ignore_result(HANDLE_EINTR(close(fd)));
441   return file;
442 }
443 
CreateTemporaryFileInDir(const FilePath & dir,FilePath * temp_file)444 bool CreateTemporaryFileInDir(const FilePath& dir, FilePath* temp_file) {
445   base::ThreadRestrictions::AssertIOAllowed();  // For call to close().
446   int fd = CreateAndOpenFdForTemporaryFile(dir, temp_file);
447   return ((fd >= 0) && !HANDLE_EINTR(close(fd)));
448 }
449 
CreateTemporaryDirInDirImpl(const FilePath & base_dir,const FilePath::StringType & name_tmpl,FilePath * new_dir)450 static bool CreateTemporaryDirInDirImpl(const FilePath& base_dir,
451                                         const FilePath::StringType& name_tmpl,
452                                         FilePath* new_dir) {
453   base::ThreadRestrictions::AssertIOAllowed();  // For call to mkdtemp().
454   CHECK(name_tmpl.find("XXXXXX") != FilePath::StringType::npos)
455     << "Directory name template must contain \"XXXXXX\".";
456 
457   FilePath sub_dir = base_dir.Append(name_tmpl);
458   std::string sub_dir_string = sub_dir.value();
459 
460   // this should be OK since mkdtemp just replaces characters in place
461   char* buffer = const_cast<char*>(sub_dir_string.c_str());
462   char* dtemp = mkdtemp(buffer);
463   if (!dtemp) {
464     DPLOG(ERROR) << "mkdtemp";
465     return false;
466   }
467   *new_dir = FilePath(dtemp);
468   return true;
469 }
470 
CreateTemporaryDirInDir(const FilePath & base_dir,const FilePath::StringType & prefix,FilePath * new_dir)471 bool CreateTemporaryDirInDir(const FilePath& base_dir,
472                              const FilePath::StringType& prefix,
473                              FilePath* new_dir) {
474   FilePath::StringType mkdtemp_template = prefix;
475   mkdtemp_template.append(FILE_PATH_LITERAL("XXXXXX"));
476   return CreateTemporaryDirInDirImpl(base_dir, mkdtemp_template, new_dir);
477 }
478 
CreateNewTempDirectory(const FilePath::StringType & prefix,FilePath * new_temp_path)479 bool CreateNewTempDirectory(const FilePath::StringType& prefix,
480                             FilePath* new_temp_path) {
481   FilePath tmpdir;
482   if (!GetTempDir(&tmpdir))
483     return false;
484 
485   return CreateTemporaryDirInDirImpl(tmpdir, kTempFileName, new_temp_path);
486 }
487 
CreateDirectory(const FilePath & full_path)488 bool CreateDirectory(const FilePath& full_path) {
489   base::ThreadRestrictions::AssertIOAllowed();  // For call to mkdir().
490   std::vector<FilePath> subpaths;
491 
492   // Collect a list of all parent directories.
493   FilePath last_path = full_path;
494   subpaths.push_back(full_path);
495   for (FilePath path = full_path.DirName();
496        path.value() != last_path.value(); path = path.DirName()) {
497     subpaths.push_back(path);
498     last_path = path;
499   }
500 
501   // Iterate through the parents and create the missing ones.
502   for (std::vector<FilePath>::reverse_iterator i = subpaths.rbegin();
503        i != subpaths.rend(); ++i) {
504     if (DirectoryExists(*i))
505       continue;
506     if (mkdir(i->value().c_str(), 0700) == 0)
507       continue;
508     // Mkdir failed, but it might have failed with EEXIST, or some other error
509     // due to the the directory appearing out of thin air. This can occur if
510     // two processes are trying to create the same file system tree at the same
511     // time. Check to see if it exists and make sure it is a directory.
512     if (!DirectoryExists(*i))
513       return false;
514   }
515   return true;
516 }
517 
GetFileInfo(const FilePath & file_path,base::PlatformFileInfo * results)518 bool GetFileInfo(const FilePath& file_path, base::PlatformFileInfo* results) {
519   stat_wrapper_t file_info;
520   if (CallStat(file_path.value().c_str(), &file_info) != 0)
521     return false;
522   results->is_directory = S_ISDIR(file_info.st_mode);
523   results->size = file_info.st_size;
524   results->last_modified = base::Time::FromTimeT(file_info.st_mtime);
525   results->last_accessed = base::Time::FromTimeT(file_info.st_atime);
526   results->creation_time = base::Time::FromTimeT(file_info.st_ctime);
527   return true;
528 }
529 
GetInode(const FilePath & path,ino_t * inode)530 bool GetInode(const FilePath& path, ino_t* inode) {
531   base::ThreadRestrictions::AssertIOAllowed();  // For call to stat().
532   struct stat buffer;
533   int result = stat(path.value().c_str(), &buffer);
534   if (result < 0)
535     return false;
536 
537   *inode = buffer.st_ino;
538   return true;
539 }
540 
OpenFile(const std::string & filename,const char * mode)541 FILE* OpenFile(const std::string& filename, const char* mode) {
542   return OpenFile(FilePath(filename), mode);
543 }
544 
OpenFile(const FilePath & filename,const char * mode)545 FILE* OpenFile(const FilePath& filename, const char* mode) {
546   base::ThreadRestrictions::AssertIOAllowed();
547   FILE* result = NULL;
548   do {
549     result = fopen(filename.value().c_str(), mode);
550   } while (!result && errno == EINTR);
551   return result;
552 }
553 
ReadFile(const FilePath & filename,char * data,int size)554 int ReadFile(const FilePath& filename, char* data, int size) {
555   base::ThreadRestrictions::AssertIOAllowed();
556   int fd = HANDLE_EINTR(open(filename.value().c_str(), O_RDONLY));
557   if (fd < 0)
558     return -1;
559 
560   ssize_t bytes_read = HANDLE_EINTR(read(fd, data, size));
561   if (int ret = HANDLE_EINTR(close(fd)) < 0)
562     return ret;
563   return bytes_read;
564 }
565 
WriteFile(const FilePath & filename,const char * data,int size)566 int WriteFile(const FilePath& filename, const char* data, int size) {
567   base::ThreadRestrictions::AssertIOAllowed();
568   int fd = HANDLE_EINTR(creat(filename.value().c_str(), 0666));
569   if (fd < 0)
570     return -1;
571 
572   int bytes_written = WriteFileDescriptor(fd, data, size);
573   if (int ret = HANDLE_EINTR(close(fd)) < 0)
574     return ret;
575   return bytes_written;
576 }
577 
WriteFileDescriptor(const int fd,const char * data,int size)578 int WriteFileDescriptor(const int fd, const char* data, int size) {
579   // Allow for partial writes.
580   ssize_t bytes_written_total = 0;
581   for (ssize_t bytes_written_partial = 0; bytes_written_total < size;
582        bytes_written_total += bytes_written_partial) {
583     bytes_written_partial =
584         HANDLE_EINTR(write(fd, data + bytes_written_total,
585                            size - bytes_written_total));
586     if (bytes_written_partial < 0)
587       return -1;
588   }
589 
590   return bytes_written_total;
591 }
592 
593 // Gets the current working directory for the process.
GetCurrentDirectory(FilePath * dir)594 bool GetCurrentDirectory(FilePath* dir) {
595   // getcwd can return ENOENT, which implies it checks against the disk.
596   base::ThreadRestrictions::AssertIOAllowed();
597 
598   char system_buffer[PATH_MAX] = "";
599   if (!getcwd(system_buffer, sizeof(system_buffer))) {
600     NOTREACHED();
601     return false;
602   }
603   *dir = FilePath(system_buffer);
604   return true;
605 }
606 
607 // Sets the current working directory for the process.
SetCurrentDirectory(const FilePath & path)608 bool SetCurrentDirectory(const FilePath& path) {
609   base::ThreadRestrictions::AssertIOAllowed();
610   int ret = chdir(path.value().c_str());
611   return !ret;
612 }
613 
614 ///////////////////////////////////////////////
615 // FileEnumerator
616 
FileEnumerator(const FilePath & root_path,bool recursive,FileEnumerator::FILE_TYPE file_type)617 FileEnumerator::FileEnumerator(const FilePath& root_path,
618                                bool recursive,
619                                FileEnumerator::FILE_TYPE file_type)
620     : current_directory_entry_(0),
621       root_path_(root_path),
622       recursive_(recursive),
623       file_type_(file_type) {
624   // INCLUDE_DOT_DOT must not be specified if recursive.
625   DCHECK(!(recursive && (INCLUDE_DOT_DOT & file_type_)));
626   pending_paths_.push(root_path);
627 }
628 
FileEnumerator(const FilePath & root_path,bool recursive,FileEnumerator::FILE_TYPE file_type,const FilePath::StringType & pattern)629 FileEnumerator::FileEnumerator(const FilePath& root_path,
630                                bool recursive,
631                                FileEnumerator::FILE_TYPE file_type,
632                                const FilePath::StringType& pattern)
633     : current_directory_entry_(0),
634       root_path_(root_path),
635       recursive_(recursive),
636       file_type_(file_type),
637       pattern_(root_path.Append(pattern).value()) {
638   // INCLUDE_DOT_DOT must not be specified if recursive.
639   DCHECK(!(recursive && (INCLUDE_DOT_DOT & file_type_)));
640   // The Windows version of this code appends the pattern to the root_path,
641   // potentially only matching against items in the top-most directory.
642   // Do the same here.
643   if (pattern.empty())
644     pattern_ = FilePath::StringType();
645   pending_paths_.push(root_path);
646 }
647 
~FileEnumerator()648 FileEnumerator::~FileEnumerator() {
649 }
650 
Next()651 FilePath FileEnumerator::Next() {
652   ++current_directory_entry_;
653 
654   // While we've exhausted the entries in the current directory, do the next
655   while (current_directory_entry_ >= directory_entries_.size()) {
656     if (pending_paths_.empty())
657       return FilePath();
658 
659     root_path_ = pending_paths_.top();
660     root_path_ = root_path_.StripTrailingSeparators();
661     pending_paths_.pop();
662 
663     std::vector<DirectoryEntryInfo> entries;
664     if (!ReadDirectory(&entries, root_path_, file_type_ & SHOW_SYM_LINKS))
665       continue;
666 
667     directory_entries_.clear();
668     current_directory_entry_ = 0;
669     for (std::vector<DirectoryEntryInfo>::const_iterator
670         i = entries.begin(); i != entries.end(); ++i) {
671       FilePath full_path = root_path_.Append(i->filename);
672       if (ShouldSkip(full_path))
673         continue;
674 
675       if (pattern_.size() &&
676           fnmatch(pattern_.c_str(), full_path.value().c_str(), FNM_NOESCAPE))
677         continue;
678 
679       if (recursive_ && S_ISDIR(i->stat.st_mode))
680         pending_paths_.push(full_path);
681 
682       if ((S_ISDIR(i->stat.st_mode) && (file_type_ & DIRECTORIES)) ||
683           (!S_ISDIR(i->stat.st_mode) && (file_type_ & FILES)))
684         directory_entries_.push_back(*i);
685     }
686   }
687 
688   return root_path_.Append(directory_entries_[current_directory_entry_
689       ].filename);
690 }
691 
GetFindInfo(FindInfo * info)692 void FileEnumerator::GetFindInfo(FindInfo* info) {
693   DCHECK(info);
694 
695   if (current_directory_entry_ >= directory_entries_.size())
696     return;
697 
698   DirectoryEntryInfo* cur_entry = &directory_entries_[current_directory_entry_];
699   memcpy(&(info->stat), &(cur_entry->stat), sizeof(info->stat));
700   info->filename.assign(cur_entry->filename.value());
701 }
702 
IsDirectory(const FindInfo & info)703 bool FileEnumerator::IsDirectory(const FindInfo& info) {
704   return S_ISDIR(info.stat.st_mode);
705 }
706 
707 // static
GetFilename(const FindInfo & find_info)708 FilePath FileEnumerator::GetFilename(const FindInfo& find_info) {
709   return FilePath(find_info.filename);
710 }
711 
ReadDirectory(std::vector<DirectoryEntryInfo> * entries,const FilePath & source,bool show_links)712 bool FileEnumerator::ReadDirectory(std::vector<DirectoryEntryInfo>* entries,
713                                    const FilePath& source, bool show_links) {
714   base::ThreadRestrictions::AssertIOAllowed();
715   DIR* dir = opendir(source.value().c_str());
716   if (!dir)
717     return false;
718 
719 #if !defined(OS_LINUX) && !defined(OS_MACOSX) && !defined(OS_FREEBSD) && \
720     !defined(OS_OPENBSD) && !defined(OS_SOLARIS)
721   #error Port warning: depending on the definition of struct dirent, \
722          additional space for pathname may be needed
723 #endif
724 
725   struct dirent dent_buf;
726   struct dirent* dent;
727   while (readdir_r(dir, &dent_buf, &dent) == 0 && dent) {
728     DirectoryEntryInfo info;
729     info.filename = FilePath(dent->d_name);
730 
731     FilePath full_name = source.Append(dent->d_name);
732     int ret;
733     if (show_links)
734       ret = lstat(full_name.value().c_str(), &info.stat);
735     else
736       ret = stat(full_name.value().c_str(), &info.stat);
737     if (ret < 0) {
738       // Print the stat() error message unless it was ENOENT and we're
739       // following symlinks.
740       if (!(errno == ENOENT && !show_links)) {
741         PLOG(ERROR) << "Couldn't stat "
742                     << source.Append(dent->d_name).value();
743       }
744       memset(&info.stat, 0, sizeof(info.stat));
745     }
746     entries->push_back(info);
747   }
748 
749   closedir(dir);
750   return true;
751 }
752 
753 ///////////////////////////////////////////////
754 // MemoryMappedFile
755 
MemoryMappedFile()756 MemoryMappedFile::MemoryMappedFile()
757     : file_(base::kInvalidPlatformFileValue),
758       data_(NULL),
759       length_(0) {
760 }
761 
MapFileToMemoryInternal()762 bool MemoryMappedFile::MapFileToMemoryInternal() {
763   base::ThreadRestrictions::AssertIOAllowed();
764 
765   struct stat file_stat;
766   if (fstat(file_, &file_stat) == base::kInvalidPlatformFileValue) {
767     LOG(ERROR) << "Couldn't fstat " << file_ << ", errno " << errno;
768     return false;
769   }
770   length_ = file_stat.st_size;
771 
772   data_ = static_cast<uint8*>(
773       mmap(NULL, length_, PROT_READ, MAP_SHARED, file_, 0));
774   if (data_ == MAP_FAILED)
775     LOG(ERROR) << "Couldn't mmap " << file_ << ", errno " << errno;
776 
777   return data_ != MAP_FAILED;
778 }
779 
CloseHandles()780 void MemoryMappedFile::CloseHandles() {
781   base::ThreadRestrictions::AssertIOAllowed();
782 
783   if (data_ != NULL)
784     munmap(data_, length_);
785   if (file_ != base::kInvalidPlatformFileValue)
786     ignore_result(HANDLE_EINTR(close(file_)));
787 
788   data_ = NULL;
789   length_ = 0;
790   file_ = base::kInvalidPlatformFileValue;
791 }
792 
HasFileBeenModifiedSince(const FileEnumerator::FindInfo & find_info,const base::Time & cutoff_time)793 bool HasFileBeenModifiedSince(const FileEnumerator::FindInfo& find_info,
794                               const base::Time& cutoff_time) {
795   return static_cast<time_t>(find_info.stat.st_mtime) >= cutoff_time.ToTimeT();
796 }
797 
NormalizeFilePath(const FilePath & path,FilePath * normalized_path)798 bool NormalizeFilePath(const FilePath& path, FilePath* normalized_path) {
799   FilePath real_path_result;
800   if (!RealPath(path, &real_path_result))
801     return false;
802 
803   // To be consistant with windows, fail if |real_path_result| is a
804   // directory.
805   stat_wrapper_t file_info;
806   if (CallStat(real_path_result.value().c_str(), &file_info) != 0 ||
807       S_ISDIR(file_info.st_mode))
808     return false;
809 
810   *normalized_path = real_path_result;
811   return true;
812 }
813 
814 #if !defined(OS_MACOSX)
GetTempDir(FilePath * path)815 bool GetTempDir(FilePath* path) {
816   const char* tmp = getenv("TMPDIR");
817   if (tmp)
818     *path = FilePath(tmp);
819   else
820     *path = FilePath("/tmp");
821   return true;
822 }
823 
GetShmemTempDir(FilePath * path)824 bool GetShmemTempDir(FilePath* path) {
825   *path = FilePath("/dev/shm");
826   return true;
827 }
828 
GetHomeDir()829 FilePath GetHomeDir() {
830 #ifndef ANDROID
831   const char* home_dir = getenv("HOME");
832   if (home_dir && home_dir[0])
833     return FilePath(home_dir);
834 
835   // g_get_home_dir calls getpwent, which can fall through to LDAP calls.
836   base::ThreadRestrictions::AssertIOAllowed();
837 
838   home_dir = g_get_home_dir();
839   if (home_dir && home_dir[0])
840     return FilePath(home_dir);
841 
842   FilePath rv;
843   if (file_util::GetTempDir(&rv))
844     return rv;
845 #endif
846   // Last resort.
847   return FilePath("/tmp");
848 }
849 
CopyFile(const FilePath & from_path,const FilePath & to_path)850 bool CopyFile(const FilePath& from_path, const FilePath& to_path) {
851   base::ThreadRestrictions::AssertIOAllowed();
852   int infile = HANDLE_EINTR(open(from_path.value().c_str(), O_RDONLY));
853   if (infile < 0)
854     return false;
855 
856   int outfile = HANDLE_EINTR(creat(to_path.value().c_str(), 0666));
857   if (outfile < 0) {
858     ignore_result(HANDLE_EINTR(close(infile)));
859     return false;
860   }
861 
862   const size_t kBufferSize = 32768;
863   std::vector<char> buffer(kBufferSize);
864   bool result = true;
865 
866   while (result) {
867     ssize_t bytes_read = HANDLE_EINTR(read(infile, &buffer[0], buffer.size()));
868     if (bytes_read < 0) {
869       result = false;
870       break;
871     }
872     if (bytes_read == 0)
873       break;
874     // Allow for partial writes
875     ssize_t bytes_written_per_read = 0;
876     do {
877       ssize_t bytes_written_partial = HANDLE_EINTR(write(
878           outfile,
879           &buffer[bytes_written_per_read],
880           bytes_read - bytes_written_per_read));
881       if (bytes_written_partial < 0) {
882         result = false;
883         break;
884       }
885       bytes_written_per_read += bytes_written_partial;
886     } while (bytes_written_per_read < bytes_read);
887   }
888 
889   if (HANDLE_EINTR(close(infile)) < 0)
890     result = false;
891   if (HANDLE_EINTR(close(outfile)) < 0)
892     result = false;
893 
894   return result;
895 }
896 #endif  // defined(OS_MACOSX)
897 
898 }  // namespace file_util
899