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(¤t.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