blob: c6069e67061fb89e16b49440c1c24fe29e706c41 [file] [log] [blame]
[email protected]ec3d1452010-02-18 10:02:261// Copyright (c) 2010 The Chromium Authors. All rights reserved.
[email protected]b2e97292008-09-02 18:20:342// 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
[email protected]4b7743de2009-04-21 01:50:397#include <dirent.h>
[email protected]21dec3872008-09-18 19:15:548#include <errno.h>
[email protected]b2e97292008-09-02 18:20:349#include <fcntl.h>
10#include <fnmatch.h>
[email protected]b2e97292008-09-02 18:20:3411#include <libgen.h>
[email protected]836f1342008-10-01 17:40:1312#include <stdio.h>
[email protected]21dec3872008-09-18 19:15:5413#include <string.h>
[email protected]b2e97292008-09-02 18:20:3414#include <sys/errno.h>
[email protected]7856bb82008-12-12 23:43:0315#include <sys/mman.h>
[email protected]b2e97292008-09-02 18:20:3416#include <sys/stat.h>
[email protected]ec3d1452010-02-18 10:02:2617#include <sys/time.h>
[email protected]4b7743de2009-04-21 01:50:3918#include <sys/types.h>
[email protected]b2e97292008-09-02 18:20:3419#include <time.h>
[email protected]4b7743de2009-04-21 01:50:3920#include <unistd.h>
[email protected]b2e97292008-09-02 18:20:3421
[email protected]3224dcd2009-09-16 17:31:2522#if defined(OS_MACOSX)
23#include <AvailabilityMacros.h>
24#endif
25
[email protected]b2e97292008-09-02 18:20:3426#include <fstream>
27
28#include "base/basictypes.h"
[email protected]157c61b2009-05-01 21:37:3129#include "base/eintr_wrapper.h"
[email protected]640517f2008-10-30 23:54:0430#include "base/file_path.h"
[email protected]807bc042009-07-15 01:32:0231#include "base/lock.h"
[email protected]b2e97292008-09-02 18:20:3432#include "base/logging.h"
[email protected]807bc042009-07-15 01:32:0233#include "base/scoped_ptr.h"
34#include "base/singleton.h"
[email protected]b2e97292008-09-02 18:20:3435#include "base/string_util.h"
[email protected]807bc042009-07-15 01:32:0236#include "base/sys_string_conversions.h"
[email protected]4b7743de2009-04-21 01:50:3937#include "base/time.h"
[email protected]047a03f2009-10-07 02:10:2038#include "base/utf_string_conversions.h"
[email protected]172c5502009-06-24 03:29:2639
[email protected]b2e97292008-09-02 18:20:3440namespace file_util {
41
[email protected]3224dcd2009-09-16 17:31:2542#if defined(OS_FREEBSD) || \
43 (defined(OS_MACOSX) && \
44 MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5)
[email protected]fb66f9d2009-09-07 16:39:4645typedef struct stat stat_wrapper_t;
46static int CallStat(const char *path, stat_wrapper_t *sb) {
47 return stat(path, sb);
48}
49#else
50typedef struct stat64 stat_wrapper_t;
51static int CallStat(const char *path, stat_wrapper_t *sb) {
52 return stat64(path, sb);
53}
54#endif
55
56
[email protected]22a087f2009-03-17 19:17:4357#if defined(GOOGLE_CHROME_BUILD)
[email protected]392264c2008-11-11 00:01:3858static const char* kTempFileName = "com.google.chrome.XXXXXX";
[email protected]22a087f2009-03-17 19:17:4359#else
60static const char* kTempFileName = "org.chromium.XXXXXX";
61#endif
[email protected]778e8c52008-09-11 17:36:2362
[email protected]640517f2008-10-30 23:54:0463bool AbsolutePath(FilePath* path) {
[email protected]b2e97292008-09-02 18:20:3464 char full_path[PATH_MAX];
[email protected]640517f2008-10-30 23:54:0465 if (realpath(path->value().c_str(), full_path) == NULL)
[email protected]b2e97292008-09-02 18:20:3466 return false;
[email protected]640517f2008-10-30 23:54:0467 *path = FilePath(full_path);
[email protected]b2e97292008-09-02 18:20:3468 return true;
69}
70
[email protected]4b7743de2009-04-21 01:50:3971int CountFilesCreatedAfter(const FilePath& path,
72 const base::Time& comparison_time) {
73 int file_count = 0;
74
75 DIR* dir = opendir(path.value().c_str());
76 if (dir) {
[email protected]8d578822010-01-25 23:54:5477#if !defined(OS_LINUX) && !defined(OS_MACOSX) && !defined(OS_FREEBSD) && \
[email protected]b5c72b822010-02-18 16:11:3778 !defined(OS_OPENBSD) && !defined(OS_SOLARIS)
[email protected]e43eddf12009-12-29 00:32:5279 #error Port warning: depending on the definition of struct dirent, \
80 additional space for pathname may be needed
[email protected]49930c3a2009-08-06 21:23:0781#endif
[email protected]2126577b2009-04-23 15:05:1982 struct dirent ent_buf;
[email protected]4b7743de2009-04-21 01:50:3983 struct dirent* ent;
[email protected]2126577b2009-04-23 15:05:1984 while (readdir_r(dir, &ent_buf, &ent) == 0 && ent) {
[email protected]4b7743de2009-04-21 01:50:3985 if ((strcmp(ent->d_name, ".") == 0) ||
86 (strcmp(ent->d_name, "..") == 0))
87 continue;
88
[email protected]fb66f9d2009-09-07 16:39:4689 stat_wrapper_t st;
90 int test = CallStat(path.Append(ent->d_name).value().c_str(), &st);
[email protected]4b7743de2009-04-21 01:50:3991 if (test != 0) {
[email protected]57b765672009-10-13 18:27:4092 PLOG(ERROR) << "stat64 failed";
[email protected]4b7743de2009-04-21 01:50:3993 continue;
94 }
[email protected]2126577b2009-04-23 15:05:1995 // Here, we use Time::TimeT(), which discards microseconds. This
96 // means that files which are newer than |comparison_time| may
97 // be considered older. If we don't discard microseconds, it
98 // introduces another issue. Suppose the following case:
99 //
100 // 1. Get |comparison_time| by Time::Now() and the value is 10.1 (secs).
101 // 2. Create a file and the current time is 10.3 (secs).
102 //
103 // As POSIX doesn't have microsecond precision for |st_ctime|,
104 // the creation time of the file created in the step 2 is 10 and
105 // the file is considered older than |comparison_time|. After
106 // all, we may have to accept either of the two issues: 1. files
107 // which are older than |comparison_time| are considered newer
108 // (current implementation) 2. files newer than
109 // |comparison_time| are considered older.
[email protected]4b7743de2009-04-21 01:50:39110 if (st.st_ctime >= comparison_time.ToTimeT())
111 ++file_count;
112 }
113 closedir(dir);
114 }
115 return file_count;
116}
117
[email protected]b2e97292008-09-02 18:20:34118// TODO(erikkay): The Windows version of this accepts paths like "foo/bar/*"
119// which works both with and without the recursive flag. I'm not sure we need
120// that functionality. If not, remove from file_util_win.cc, otherwise add it
121// here.
[email protected]640517f2008-10-30 23:54:04122bool Delete(const FilePath& path, bool recursive) {
123 const char* path_str = path.value().c_str();
[email protected]fb66f9d2009-09-07 16:39:46124 stat_wrapper_t file_info;
125 int test = CallStat(path_str, &file_info);
[email protected]b2e97292008-09-02 18:20:34126 if (test != 0) {
127 // The Windows version defines this condition as success.
[email protected]9e51af92009-02-04 00:58:39128 bool ret = (errno == ENOENT || errno == ENOTDIR);
[email protected]b2e97292008-09-02 18:20:34129 return ret;
130 }
131 if (!S_ISDIR(file_info.st_mode))
[email protected]640517f2008-10-30 23:54:04132 return (unlink(path_str) == 0);
[email protected]b2e97292008-09-02 18:20:34133 if (!recursive)
[email protected]640517f2008-10-30 23:54:04134 return (rmdir(path_str) == 0);
[email protected]b2e97292008-09-02 18:20:34135
136 bool success = true;
[email protected]49930c3a2009-08-06 21:23:07137 std::stack<std::string> directories;
138 directories.push(path.value());
139 FileEnumerator traversal(path, true, static_cast<FileEnumerator::FILE_TYPE>(
140 FileEnumerator::FILES | FileEnumerator::DIRECTORIES |
141 FileEnumerator::SHOW_SYM_LINKS));
142 for (FilePath current = traversal.Next(); success && !current.empty();
143 current = traversal.Next()) {
144 FileEnumerator::FindInfo info;
145 traversal.GetFindInfo(&info);
146
147 if (S_ISDIR(info.stat.st_mode))
148 directories.push(current.value());
149 else
150 success = (unlink(current.value().c_str()) == 0);
[email protected]21dec3872008-09-18 19:15:54151 }
[email protected]49930c3a2009-08-06 21:23:07152
153 while (success && !directories.empty()) {
154 FilePath dir = FilePath(directories.top());
155 directories.pop();
156 success = (rmdir(dir.value().c_str()) == 0);
[email protected]b2e97292008-09-02 18:20:34157 }
[email protected]49930c3a2009-08-06 21:23:07158
[email protected]b2e97292008-09-02 18:20:34159 return success;
160}
161
[email protected]640517f2008-10-30 23:54:04162bool Move(const FilePath& from_path, const FilePath& to_path) {
[email protected]bc6a9012009-10-15 01:11:44163 // Windows compatibility: if to_path exists, from_path and to_path
164 // must be the same type, either both files, or both directories.
165 stat_wrapper_t to_file_info;
166 if (CallStat(to_path.value().c_str(), &to_file_info) == 0) {
167 stat_wrapper_t from_file_info;
168 if (CallStat(from_path.value().c_str(), &from_file_info) == 0) {
169 if (S_ISDIR(to_file_info.st_mode) != S_ISDIR(from_file_info.st_mode))
170 return false;
171 } else {
172 return false;
173 }
174 }
175
[email protected]cc7948a2009-03-13 20:01:43176 if (rename(from_path.value().c_str(), to_path.value().c_str()) == 0)
177 return true;
178
179 if (!CopyDirectory(from_path, to_path, true))
180 return false;
181
182 Delete(from_path, true);
183 return true;
[email protected]b2e97292008-09-02 18:20:34184}
185
[email protected]c5866dca2009-05-19 17:21:07186bool ReplaceFile(const FilePath& from_path, const FilePath& to_path) {
187 return (rename(from_path.value().c_str(), to_path.value().c_str()) == 0);
188}
189
[email protected]640517f2008-10-30 23:54:04190bool CopyDirectory(const FilePath& from_path,
191 const FilePath& to_path,
[email protected]21dec3872008-09-18 19:15:54192 bool recursive) {
[email protected]21dec3872008-09-18 19:15:54193 // Some old callers of CopyDirectory want it to support wildcards.
194 // After some discussion, we decided to fix those callers.
195 // Break loudly here if anyone tries to do this.
196 // TODO(evanm): remove this once we're sure it's ok.
[email protected]640517f2008-10-30 23:54:04197 DCHECK(to_path.value().find('*') == std::string::npos);
198 DCHECK(from_path.value().find('*') == std::string::npos);
[email protected]21dec3872008-09-18 19:15:54199
200 char top_dir[PATH_MAX];
[email protected]640517f2008-10-30 23:54:04201 if (base::strlcpy(top_dir, from_path.value().c_str(),
[email protected]21dec3872008-09-18 19:15:54202 arraysize(top_dir)) >= arraysize(top_dir)) {
203 return false;
204 }
205
[email protected]49930c3a2009-08-06 21:23:07206 // This function does not properly handle destinations within the source
207 FilePath real_to_path = to_path;
208 if (PathExists(real_to_path)) {
209 if (!AbsolutePath(&real_to_path))
210 return false;
211 } else {
212 real_to_path = real_to_path.DirName();
213 if (!AbsolutePath(&real_to_path))
214 return false;
215 }
216 FilePath real_from_path = from_path;
217 if (!AbsolutePath(&real_from_path))
[email protected]21dec3872008-09-18 19:15:54218 return false;
[email protected]49930c3a2009-08-06 21:23:07219 if (real_to_path.value().size() >= real_from_path.value().size() &&
220 real_to_path.value().compare(0, real_from_path.value().size(),
221 real_from_path.value()) == 0)
222 return false;
223
224 bool success = true;
225 FileEnumerator::FILE_TYPE traverse_type =
226 static_cast<FileEnumerator::FILE_TYPE>(FileEnumerator::FILES |
227 FileEnumerator::SHOW_SYM_LINKS);
228 if (recursive)
229 traverse_type = static_cast<FileEnumerator::FILE_TYPE>(
230 traverse_type | FileEnumerator::DIRECTORIES);
231 FileEnumerator traversal(from_path, recursive, traverse_type);
232
[email protected]abbc5732009-10-13 17:57:27233 // We have to mimic windows behavior here. |to_path| may not exist yet,
[email protected]bc6a9012009-10-15 01:11:44234 // start the loop with |to_path|.
[email protected]49930c3a2009-08-06 21:23:07235 FileEnumerator::FindInfo info;
236 FilePath current = from_path;
237 if (stat(from_path.value().c_str(), &info.stat) < 0) {
238 LOG(ERROR) << "CopyDirectory() couldn't stat source directory: " <<
239 from_path.value() << " errno = " << errno;
240 success = false;
[email protected]21dec3872008-09-18 19:15:54241 }
[email protected]bc6a9012009-10-15 01:11:44242 struct stat to_path_stat;
243 FilePath from_path_base = from_path;
244 if (recursive && stat(to_path.value().c_str(), &to_path_stat) == 0 &&
245 S_ISDIR(to_path_stat.st_mode)) {
246 // If the destination already exists and is a directory, then the
247 // top level of source needs to be copied.
248 from_path_base = from_path.DirName();
249 }
250
251 // The Windows version of this function assumes that non-recursive calls
252 // will always have a directory for from_path.
253 DCHECK(recursive || S_ISDIR(info.stat.st_mode));
[email protected]21dec3872008-09-18 19:15:54254
[email protected]49930c3a2009-08-06 21:23:07255 while (success && !current.empty()) {
256 // current is the source path, including from_path, so paste
[email protected]21dec3872008-09-18 19:15:54257 // the suffix after from_path onto to_path to create the target_path.
[email protected]abbc5732009-10-13 17:57:27258 std::string suffix(&current.value().c_str()[from_path_base.value().size()]);
[email protected]ca0209612009-01-13 18:57:46259 // Strip the leading '/' (if any).
260 if (!suffix.empty()) {
[email protected]6ae340f2009-03-06 09:56:28261 DCHECK_EQ('/', suffix[0]);
[email protected]ca0209612009-01-13 18:57:46262 suffix.erase(0, 1);
263 }
264 const FilePath target_path = to_path.Append(suffix);
[email protected]21dec3872008-09-18 19:15:54265
[email protected]49930c3a2009-08-06 21:23:07266 if (S_ISDIR(info.stat.st_mode)) {
267 if (mkdir(target_path.value().c_str(), info.stat.st_mode & 01777) != 0 &&
268 errno != EEXIST) {
269 LOG(ERROR) << "CopyDirectory() couldn't create directory: " <<
270 target_path.value() << " errno = " << errno;
271 success = false;
272 }
273 } else if (S_ISREG(info.stat.st_mode)) {
274 if (!CopyFile(current, target_path)) {
275 LOG(ERROR) << "CopyDirectory() couldn't create file: " <<
276 target_path.value();
277 success = false;
278 }
279 } else {
280 LOG(WARNING) << "CopyDirectory() skipping non-regular file: " <<
281 current.value();
[email protected]21dec3872008-09-18 19:15:54282 }
[email protected]21dec3872008-09-18 19:15:54283
[email protected]49930c3a2009-08-06 21:23:07284 current = traversal.Next();
285 traversal.GetFindInfo(&info);
[email protected]21dec3872008-09-18 19:15:54286 }
287
[email protected]49930c3a2009-08-06 21:23:07288 return success;
[email protected]b2e97292008-09-02 18:20:34289}
290
[email protected]640517f2008-10-30 23:54:04291bool PathExists(const FilePath& path) {
[email protected]fb66f9d2009-09-07 16:39:46292 stat_wrapper_t file_info;
293 return CallStat(path.value().c_str(), &file_info) == 0;
[email protected]b2e97292008-09-02 18:20:34294}
295
[email protected]7e1fde6a2008-12-23 20:20:10296bool PathIsWritable(const FilePath& path) {
297 FilePath test_path(path);
[email protected]fb66f9d2009-09-07 16:39:46298 stat_wrapper_t file_info;
[email protected]5d6018f2010-01-21 17:33:06299 if (CallStat(test_path.value().c_str(), &file_info) != 0)
300 return false;
[email protected]7e1fde6a2008-12-23 20:20:10301 if (S_IWOTH & file_info.st_mode)
302 return true;
303 if (getegid() == file_info.st_gid && (S_IWGRP & file_info.st_mode))
304 return true;
305 if (geteuid() == file_info.st_uid && (S_IWUSR & file_info.st_mode))
306 return true;
307 return false;
308}
309
[email protected]640517f2008-10-30 23:54:04310bool DirectoryExists(const FilePath& path) {
[email protected]fb66f9d2009-09-07 16:39:46311 stat_wrapper_t file_info;
312 if (CallStat(path.value().c_str(), &file_info) == 0)
[email protected]806b9c62008-09-11 16:09:11313 return S_ISDIR(file_info.st_mode);
314 return false;
315}
316
[email protected]b2e97292008-09-02 18:20:34317// TODO(erikkay): implement
318#if 0
319bool GetFileCreationLocalTimeFromHandle(int fd,
320 LPSYSTEMTIME creation_time) {
321 if (!file_handle)
322 return false;
[email protected]9e51af92009-02-04 00:58:39323
[email protected]b2e97292008-09-02 18:20:34324 FILETIME utc_filetime;
325 if (!GetFileTime(file_handle, &utc_filetime, NULL, NULL))
326 return false;
[email protected]9e51af92009-02-04 00:58:39327
[email protected]b2e97292008-09-02 18:20:34328 FILETIME local_filetime;
329 if (!FileTimeToLocalFileTime(&utc_filetime, &local_filetime))
330 return false;
[email protected]9e51af92009-02-04 00:58:39331
[email protected]b2e97292008-09-02 18:20:34332 return !!FileTimeToSystemTime(&local_filetime, creation_time);
333}
334
335bool GetFileCreationLocalTime(const std::string& filename,
336 LPSYSTEMTIME creation_time) {
337 ScopedHandle file_handle(
[email protected]9e51af92009-02-04 00:58:39338 CreateFile(filename.c_str(), GENERIC_READ,
[email protected]b2e97292008-09-02 18:20:34339 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
340 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL));
341 return GetFileCreationLocalTimeFromHandle(file_handle.Get(), creation_time);
342}
343#endif
344
[email protected]45301492009-04-23 12:38:08345bool ReadFromFD(int fd, char* buffer, size_t bytes) {
346 size_t total_read = 0;
347 while (total_read < bytes) {
[email protected]157c61b2009-05-01 21:37:31348 ssize_t bytes_read =
349 HANDLE_EINTR(read(fd, buffer + total_read, bytes - total_read));
350 if (bytes_read <= 0)
[email protected]45301492009-04-23 12:38:08351 break;
[email protected]157c61b2009-05-01 21:37:31352 total_read += bytes_read;
[email protected]45301492009-04-23 12:38:08353 }
354 return total_read == bytes;
355}
356
[email protected]9e51af92009-02-04 00:58:39357// Creates and opens a temporary file in |directory|, returning the
[email protected]16eac0a72009-09-11 17:33:50358// file descriptor. |path| is set to the temporary file path.
359// This function does NOT unlink() the file.
[email protected]9e51af92009-02-04 00:58:39360int CreateAndOpenFdForTemporaryFile(FilePath directory, FilePath* path) {
361 *path = directory.Append(kTempFileName);
362 const std::string& tmpdir_string = path->value();
[email protected]778e8c52008-09-11 17:36:23363 // this should be OK since mkstemp just replaces characters in place
364 char* buffer = const_cast<char*>(tmpdir_string.c_str());
[email protected]392264c2008-11-11 00:01:38365
[email protected]9e51af92009-02-04 00:58:39366 return mkstemp(buffer);
367}
368
[email protected]33edeab2009-08-18 16:07:55369bool CreateTemporaryFile(FilePath* path) {
[email protected]9e51af92009-02-04 00:58:39370 FilePath directory;
371 if (!GetTempDir(&directory))
372 return false;
373 int fd = CreateAndOpenFdForTemporaryFile(directory, path);
[email protected]b2e97292008-09-02 18:20:34374 if (fd < 0)
375 return false;
[email protected]392264c2008-11-11 00:01:38376 close(fd);
[email protected]b2e97292008-09-02 18:20:34377 return true;
378}
379
[email protected]9e51af92009-02-04 00:58:39380FILE* CreateAndOpenTemporaryShmemFile(FilePath* path) {
381 FilePath directory;
382 if (!GetShmemTempDir(&directory))
383 return false;
384
[email protected]6faa0e0d2009-04-28 06:50:36385 return CreateAndOpenTemporaryFileInDir(directory, path);
386}
387
388FILE* CreateAndOpenTemporaryFileInDir(const FilePath& dir, FilePath* path) {
389 int fd = CreateAndOpenFdForTemporaryFile(dir, path);
[email protected]9e51af92009-02-04 00:58:39390 if (fd < 0)
391 return NULL;
392
[email protected]6faa0e0d2009-04-28 06:50:36393 return fdopen(fd, "a+");
[email protected]9e51af92009-02-04 00:58:39394}
[email protected]6445c402009-09-11 20:06:27395
396bool CreateTemporaryFileInDir(const FilePath& dir, FilePath* temp_file) {
397 int fd = CreateAndOpenFdForTemporaryFile(dir, temp_file);
398 return ((fd >= 0) && !close(fd));
[email protected]9ccbb372008-10-10 18:50:32399}
400
[email protected]7e1fde6a2008-12-23 20:20:10401bool CreateNewTempDirectory(const FilePath::StringType& prefix,
402 FilePath* new_temp_path) {
[email protected]392264c2008-11-11 00:01:38403 FilePath tmpdir;
[email protected]b2e97292008-09-02 18:20:34404 if (!GetTempDir(&tmpdir))
405 return false;
[email protected]392264c2008-11-11 00:01:38406 tmpdir = tmpdir.Append(kTempFileName);
407 std::string tmpdir_string = tmpdir.value();
[email protected]b2e97292008-09-02 18:20:34408 // this should be OK since mkdtemp just replaces characters in place
[email protected]778e8c52008-09-11 17:36:23409 char* buffer = const_cast<char*>(tmpdir_string.c_str());
[email protected]b2e97292008-09-02 18:20:34410 char* dtemp = mkdtemp(buffer);
411 if (!dtemp)
412 return false;
[email protected]7e1fde6a2008-12-23 20:20:10413 *new_temp_path = FilePath(dtemp);
[email protected]b2e97292008-09-02 18:20:34414 return true;
415}
416
[email protected]640517f2008-10-30 23:54:04417bool CreateDirectory(const FilePath& full_path) {
418 std::vector<FilePath> subpaths;
419
420 // Collect a list of all parent directories.
421 FilePath last_path = full_path;
422 subpaths.push_back(full_path);
423 for (FilePath path = full_path.DirName();
424 path.value() != last_path.value(); path = path.DirName()) {
425 subpaths.push_back(path);
426 last_path = path;
427 }
428
429 // Iterate through the parents and create the missing ones.
430 for (std::vector<FilePath>::reverse_iterator i = subpaths.rbegin();
431 i != subpaths.rend(); ++i) {
432 if (!DirectoryExists(*i)) {
[email protected]39a1a062009-05-12 20:12:24433 if (mkdir(i->value().c_str(), 0700) != 0)
[email protected]b2e97292008-09-02 18:20:34434 return false;
435 }
436 }
437 return true;
438}
439
[email protected]eac0709a2008-11-04 21:00:46440bool GetFileInfo(const FilePath& file_path, FileInfo* results) {
[email protected]fb66f9d2009-09-07 16:39:46441 stat_wrapper_t file_info;
442 if (CallStat(file_path.value().c_str(), &file_info) != 0)
[email protected]b2e97292008-09-02 18:20:34443 return false;
[email protected]f5e3da4d2008-09-26 01:04:08444 results->is_directory = S_ISDIR(file_info.st_mode);
445 results->size = file_info.st_size;
[email protected]5ef323892009-07-24 16:13:53446 results->last_modified = base::Time::FromTimeT(file_info.st_mtime);
[email protected]b2e97292008-09-02 18:20:34447 return true;
448}
449
[email protected]ec3d1452010-02-18 10:02:26450bool SetLastModifiedTime(const FilePath& file_path, base::Time last_modified) {
451 struct timeval times[2];
452 times[0] = last_modified.ToTimeVal();
453 times[1] = last_modified.ToTimeVal();
454 return (utimes(file_path.value().c_str(), times) == 0);
455}
456
[email protected]825003f2009-05-14 17:49:23457bool GetInode(const FilePath& path, ino_t* inode) {
458 struct stat buffer;
459 int result = stat(path.value().c_str(), &buffer);
460 if (result < 0)
461 return false;
462
463 *inode = buffer.st_ino;
464 return true;
465}
466
[email protected]836f1342008-10-01 17:40:13467FILE* OpenFile(const std::string& filename, const char* mode) {
[email protected]a9cd2a652008-11-17 21:01:19468 return OpenFile(FilePath(filename), mode);
[email protected]836f1342008-10-01 17:40:13469}
470
[email protected]a9cd2a652008-11-17 21:01:19471FILE* OpenFile(const FilePath& filename, const char* mode) {
472 return fopen(filename.value().c_str(), mode);
[email protected]836f1342008-10-01 17:40:13473}
474
[email protected]c870c762009-01-28 05:47:15475int ReadFile(const FilePath& filename, char* data, int size) {
476 int fd = open(filename.value().c_str(), O_RDONLY);
[email protected]b2e97292008-09-02 18:20:34477 if (fd < 0)
478 return -1;
[email protected]a9cd2a652008-11-17 21:01:19479
[email protected]cabe39c2010-02-02 02:28:16480 ssize_t bytes_read = HANDLE_EINTR(read(fd, data, size));
481 if (int ret = HANDLE_EINTR(close(fd)) < 0)
482 return ret;
483 return bytes_read;
[email protected]b2e97292008-09-02 18:20:34484}
485
[email protected]c870c762009-01-28 05:47:15486int WriteFile(const FilePath& filename, const char* data, int size) {
487 int fd = creat(filename.value().c_str(), 0666);
[email protected]b2e97292008-09-02 18:20:34488 if (fd < 0)
489 return -1;
[email protected]778e8c52008-09-11 17:36:23490
[email protected]cabe39c2010-02-02 02:28:16491 int bytes_written = WriteFileDescriptor(fd, data, size);
492 if (int ret = HANDLE_EINTR(close(fd)) < 0)
493 return ret;
494 return bytes_written;
[email protected]fbea0232009-09-16 00:29:22495}
496
497int WriteFileDescriptor(const int fd, const char* data, int size) {
498 // Allow for partial writes.
499 ssize_t bytes_written_total = 0;
500 for (ssize_t bytes_written_partial = 0; bytes_written_total < size;
501 bytes_written_total += bytes_written_partial) {
502 bytes_written_partial =
503 HANDLE_EINTR(write(fd, data + bytes_written_total,
504 size - bytes_written_total));
505 if (bytes_written_partial < 0)
506 return -1;
507 }
508
[email protected]778e8c52008-09-11 17:36:23509 return bytes_written_total;
[email protected]b2e97292008-09-02 18:20:34510}
511
512// Gets the current working directory for the process.
[email protected]640517f2008-10-30 23:54:04513bool GetCurrentDirectory(FilePath* dir) {
[email protected]b2e97292008-09-02 18:20:34514 char system_buffer[PATH_MAX] = "";
[email protected]640517f2008-10-30 23:54:04515 if (!getcwd(system_buffer, sizeof(system_buffer))) {
516 NOTREACHED();
517 return false;
518 }
519 *dir = FilePath(system_buffer);
[email protected]b2e97292008-09-02 18:20:34520 return true;
521}
522
523// Sets the current working directory for the process.
[email protected]a9cd2a652008-11-17 21:01:19524bool SetCurrentDirectory(const FilePath& path) {
525 int ret = chdir(path.value().c_str());
526 return !ret;
[email protected]b2e97292008-09-02 18:20:34527}
[email protected]a9cd2a652008-11-17 21:01:19528
[email protected]7856bb82008-12-12 23:43:03529///////////////////////////////////////////////
530// FileEnumerator
531
[email protected]0b733222008-12-11 14:55:12532FileEnumerator::FileEnumerator(const FilePath& root_path,
[email protected]b2e97292008-09-02 18:20:34533 bool recursive,
534 FileEnumerator::FILE_TYPE file_type)
[email protected]fbf745a2009-12-08 22:05:59535 : current_directory_entry_(0),
536 root_path_(root_path),
[email protected]49930c3a2009-08-06 21:23:07537 recursive_(recursive),
[email protected]b2e97292008-09-02 18:20:34538 file_type_(file_type),
[email protected]fbf745a2009-12-08 22:05:59539 is_in_find_op_(false) {
[email protected]8199b3a2009-06-09 05:57:38540 // INCLUDE_DOT_DOT must not be specified if recursive.
541 DCHECK(!(recursive && (INCLUDE_DOT_DOT & file_type_)));
[email protected]b2e97292008-09-02 18:20:34542 pending_paths_.push(root_path);
543}
544
[email protected]0b733222008-12-11 14:55:12545FileEnumerator::FileEnumerator(const FilePath& root_path,
[email protected]b2e97292008-09-02 18:20:34546 bool recursive,
547 FileEnumerator::FILE_TYPE file_type,
[email protected]0b733222008-12-11 14:55:12548 const FilePath::StringType& pattern)
[email protected]fbf745a2009-12-08 22:05:59549 : current_directory_entry_(0),
550 root_path_(root_path),
[email protected]49930c3a2009-08-06 21:23:07551 recursive_(recursive),
[email protected]b2e97292008-09-02 18:20:34552 file_type_(file_type),
[email protected]fbf745a2009-12-08 22:05:59553 pattern_(root_path.Append(pattern).value()),
554 is_in_find_op_(false) {
[email protected]8199b3a2009-06-09 05:57:38555 // INCLUDE_DOT_DOT must not be specified if recursive.
556 DCHECK(!(recursive && (INCLUDE_DOT_DOT & file_type_)));
[email protected]49930c3a2009-08-06 21:23:07557 // The Windows version of this code appends the pattern to the root_path,
558 // potentially only matching against items in the top-most directory.
559 // Do the same here.
560 if (pattern.size() == 0)
[email protected]fbf745a2009-12-08 22:05:59561 pattern_ = FilePath::StringType();
[email protected]b2e97292008-09-02 18:20:34562 pending_paths_.push(root_path);
563}
[email protected]a9cd2a652008-11-17 21:01:19564
[email protected]b2e97292008-09-02 18:20:34565FileEnumerator::~FileEnumerator() {
[email protected]b2e97292008-09-02 18:20:34566}
567
[email protected]13ef7c02008-11-20 22:30:13568void FileEnumerator::GetFindInfo(FindInfo* info) {
569 DCHECK(info);
570
[email protected]49930c3a2009-08-06 21:23:07571 if (current_directory_entry_ >= directory_entries_.size())
[email protected]13ef7c02008-11-20 22:30:13572 return;
573
[email protected]49930c3a2009-08-06 21:23:07574 DirectoryEntryInfo* cur_entry = &directory_entries_[current_directory_entry_];
575 memcpy(&(info->stat), &(cur_entry->stat), sizeof(info->stat));
576 info->filename.assign(cur_entry->filename.value());
[email protected]13ef7c02008-11-20 22:30:13577}
578
[email protected]9fa8c2f52009-12-11 21:12:29579bool FileEnumerator::IsDirectory(const FindInfo& info) {
580 return S_ISDIR(info.stat.st_mode);
581}
582
[email protected]1525c682010-02-11 23:27:47583// static
584FilePath FileEnumerator::GetFilename(const FindInfo& find_info) {
585 return FilePath(find_info.filename);
586}
587
[email protected]0b733222008-12-11 14:55:12588FilePath FileEnumerator::Next() {
[email protected]49930c3a2009-08-06 21:23:07589 ++current_directory_entry_;
590
591 // While we've exhausted the entries in the current directory, do the next
592 while (current_directory_entry_ >= directory_entries_.size()) {
[email protected]b2e97292008-09-02 18:20:34593 if (pending_paths_.empty())
[email protected]0b733222008-12-11 14:55:12594 return FilePath();
[email protected]13ef7c02008-11-20 22:30:13595
[email protected]b2e97292008-09-02 18:20:34596 root_path_ = pending_paths_.top();
[email protected]0b733222008-12-11 14:55:12597 root_path_ = root_path_.StripTrailingSeparators();
[email protected]b2e97292008-09-02 18:20:34598 pending_paths_.pop();
[email protected]13ef7c02008-11-20 22:30:13599
[email protected]49930c3a2009-08-06 21:23:07600 std::vector<DirectoryEntryInfo> entries;
601 if (!ReadDirectory(&entries, root_path_, file_type_ & SHOW_SYM_LINKS))
602 continue;
[email protected]13ef7c02008-11-20 22:30:13603
[email protected]49930c3a2009-08-06 21:23:07604 directory_entries_.clear();
605 current_directory_entry_ = 0;
606 for (std::vector<DirectoryEntryInfo>::const_iterator
607 i = entries.begin(); i != entries.end(); ++i) {
608 FilePath full_path = root_path_.Append(i->filename);
609 if (ShouldSkip(full_path))
610 continue;
[email protected]13ef7c02008-11-20 22:30:13611
[email protected]fbf745a2009-12-08 22:05:59612 if (pattern_.size() &&
613 fnmatch(pattern_.c_str(), full_path.value().c_str(), FNM_NOESCAPE))
[email protected]49930c3a2009-08-06 21:23:07614 continue;
615
616 if (recursive_ && S_ISDIR(i->stat.st_mode))
617 pending_paths_.push(full_path);
618
619 if ((S_ISDIR(i->stat.st_mode) && (file_type_ & DIRECTORIES)) ||
620 (!S_ISDIR(i->stat.st_mode) && (file_type_ & FILES)))
621 directory_entries_.push_back(*i);
[email protected]b2e97292008-09-02 18:20:34622 }
623 }
[email protected]13ef7c02008-11-20 22:30:13624
[email protected]49930c3a2009-08-06 21:23:07625 return root_path_.Append(directory_entries_[current_directory_entry_
626 ].filename);
627}
[email protected]8199b3a2009-06-09 05:57:38628
[email protected]49930c3a2009-08-06 21:23:07629bool FileEnumerator::ReadDirectory(std::vector<DirectoryEntryInfo>* entries,
630 const FilePath& source, bool show_links) {
631 DIR* dir = opendir(source.value().c_str());
632 if (!dir)
633 return false;
634
[email protected]8d578822010-01-25 23:54:54635#if !defined(OS_LINUX) && !defined(OS_MACOSX) && !defined(OS_FREEBSD) && \
[email protected]b5c72b822010-02-18 16:11:37636 !defined(OS_OPENBSD) && !defined(OS_SOLARIS)
[email protected]e43eddf12009-12-29 00:32:52637 #error Port warning: depending on the definition of struct dirent, \
638 additional space for pathname may be needed
[email protected]49930c3a2009-08-06 21:23:07639#endif
[email protected]e43eddf12009-12-29 00:32:52640
[email protected]49930c3a2009-08-06 21:23:07641 struct dirent dent_buf;
642 struct dirent* dent;
643 while (readdir_r(dir, &dent_buf, &dent) == 0 && dent) {
644 DirectoryEntryInfo info;
[email protected]49930c3a2009-08-06 21:23:07645 info.filename = FilePath(dent->d_name);
[email protected]2237f5d72009-09-03 22:39:34646
647 FilePath full_name = source.Append(dent->d_name);
648 int ret;
[email protected]49930c3a2009-08-06 21:23:07649 if (show_links)
[email protected]2237f5d72009-09-03 22:39:34650 ret = lstat(full_name.value().c_str(), &info.stat);
[email protected]49930c3a2009-08-06 21:23:07651 else
[email protected]2237f5d72009-09-03 22:39:34652 ret = stat(full_name.value().c_str(), &info.stat);
653 if (ret < 0) {
654 // Print the stat() error message unless it was ENOENT and we're
655 // following symlinks.
656 if (!(ret == ENOENT && !show_links)) {
[email protected]57b765672009-10-13 18:27:40657 PLOG(ERROR) << "Couldn't stat "
658 << source.Append(dent->d_name).value();
[email protected]2237f5d72009-09-03 22:39:34659 }
[email protected]49930c3a2009-08-06 21:23:07660 memset(&info.stat, 0, sizeof(info.stat));
[email protected]8199b3a2009-06-09 05:57:38661 }
[email protected]49930c3a2009-08-06 21:23:07662 entries->push_back(info);
[email protected]b2e97292008-09-02 18:20:34663 }
[email protected]49930c3a2009-08-06 21:23:07664
665 closedir(dir);
666 return true;
667}
668
[email protected]7856bb82008-12-12 23:43:03669///////////////////////////////////////////////
670// MemoryMappedFile
671
672MemoryMappedFile::MemoryMappedFile()
[email protected]cb6037d2009-11-16 22:55:17673 : file_(base::kInvalidPlatformFileValue),
674 data_(NULL),
[email protected]7856bb82008-12-12 23:43:03675 length_(0) {
676}
677
[email protected]85c55dc2009-11-06 03:05:46678bool MemoryMappedFile::MapFileToMemoryInternal() {
[email protected]7856bb82008-12-12 23:43:03679 struct stat file_stat;
[email protected]cb6037d2009-11-16 22:55:17680 if (fstat(file_, &file_stat) == base::kInvalidPlatformFileValue) {
681 LOG(ERROR) << "Couldn't fstat " << file_ << ", errno " << errno;
[email protected]7856bb82008-12-12 23:43:03682 return false;
[email protected]4883a4e2009-06-06 19:59:36683 }
[email protected]7856bb82008-12-12 23:43:03684 length_ = file_stat.st_size;
685
686 data_ = static_cast<uint8*>(
[email protected]cb6037d2009-11-16 22:55:17687 mmap(NULL, length_, PROT_READ, MAP_SHARED, file_, 0));
[email protected]7856bb82008-12-12 23:43:03688 if (data_ == MAP_FAILED)
[email protected]cb6037d2009-11-16 22:55:17689 LOG(ERROR) << "Couldn't mmap " << file_ << ", errno " << errno;
[email protected]4883a4e2009-06-06 19:59:36690
691 return data_ != MAP_FAILED;
[email protected]7856bb82008-12-12 23:43:03692}
693
694void MemoryMappedFile::CloseHandles() {
695 if (data_ != NULL)
696 munmap(data_, length_);
[email protected]cb6037d2009-11-16 22:55:17697 if (file_ != base::kInvalidPlatformFileValue)
698 close(file_);
[email protected]7856bb82008-12-12 23:43:03699
700 data_ = NULL;
701 length_ = 0;
[email protected]cb6037d2009-11-16 22:55:17702 file_ = base::kInvalidPlatformFileValue;
[email protected]7856bb82008-12-12 23:43:03703}
[email protected]13ef7c02008-11-20 22:30:13704
[email protected]35b9be02009-11-26 00:37:06705bool HasFileBeenModifiedSince(const FileEnumerator::FindInfo& find_info,
706 const base::Time& cutoff_time) {
707 return find_info.stat.st_mtime >= cutoff_time.ToTimeT();
708}
709
[email protected]b2e97292008-09-02 18:20:34710} // namespace file_util