blob: 9db41fe95b196c20e7541f66c9bf23809ade696d [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]1c657852010-04-22 23:28:0513#include <stdlib.h>
[email protected]21dec3872008-09-18 19:15:5414#include <string.h>
[email protected]b2e97292008-09-02 18:20:3415#include <sys/errno.h>
[email protected]7856bb82008-12-12 23:43:0316#include <sys/mman.h>
[email protected]01e2a1f2010-05-12 15:13:5717#include <sys/param.h>
[email protected]b2e97292008-09-02 18:20:3418#include <sys/stat.h>
[email protected]ec3d1452010-02-18 10:02:2619#include <sys/time.h>
[email protected]4b7743de2009-04-21 01:50:3920#include <sys/types.h>
[email protected]b2e97292008-09-02 18:20:3421#include <time.h>
[email protected]4b7743de2009-04-21 01:50:3922#include <unistd.h>
[email protected]b2e97292008-09-02 18:20:3423
[email protected]3224dcd2009-09-16 17:31:2524#if defined(OS_MACOSX)
25#include <AvailabilityMacros.h>
[email protected]1c657852010-04-22 23:28:0526#else
27#include <glib.h>
[email protected]3224dcd2009-09-16 17:31:2528#endif
29
[email protected]b2e97292008-09-02 18:20:3430#include <fstream>
31
32#include "base/basictypes.h"
[email protected]157c61b2009-05-01 21:37:3133#include "base/eintr_wrapper.h"
[email protected]640517f2008-10-30 23:54:0434#include "base/file_path.h"
[email protected]807bc042009-07-15 01:32:0235#include "base/lock.h"
[email protected]b2e97292008-09-02 18:20:3436#include "base/logging.h"
[email protected]807bc042009-07-15 01:32:0237#include "base/scoped_ptr.h"
38#include "base/singleton.h"
[email protected]b2e97292008-09-02 18:20:3439#include "base/string_util.h"
[email protected]807bc042009-07-15 01:32:0240#include "base/sys_string_conversions.h"
[email protected]4b7743de2009-04-21 01:50:3941#include "base/time.h"
[email protected]047a03f2009-10-07 02:10:2042#include "base/utf_string_conversions.h"
[email protected]172c5502009-06-24 03:29:2643
[email protected]b2e97292008-09-02 18:20:3444namespace file_util {
45
[email protected]66700d42010-03-10 07:46:4346#if defined(OS_OPENBSD) || defined(OS_FREEBSD) || \
[email protected]3224dcd2009-09-16 17:31:2547 (defined(OS_MACOSX) && \
48 MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5)
[email protected]fb66f9d2009-09-07 16:39:4649typedef struct stat stat_wrapper_t;
50static int CallStat(const char *path, stat_wrapper_t *sb) {
51 return stat(path, sb);
52}
53#else
54typedef struct stat64 stat_wrapper_t;
55static int CallStat(const char *path, stat_wrapper_t *sb) {
56 return stat64(path, sb);
57}
58#endif
59
60
[email protected]22a087f2009-03-17 19:17:4361#if defined(GOOGLE_CHROME_BUILD)
[email protected]392264c2008-11-11 00:01:3862static const char* kTempFileName = "com.google.chrome.XXXXXX";
[email protected]22a087f2009-03-17 19:17:4363#else
64static const char* kTempFileName = "org.chromium.XXXXXX";
65#endif
[email protected]778e8c52008-09-11 17:36:2366
[email protected]640517f2008-10-30 23:54:0467bool AbsolutePath(FilePath* path) {
[email protected]b2e97292008-09-02 18:20:3468 char full_path[PATH_MAX];
[email protected]640517f2008-10-30 23:54:0469 if (realpath(path->value().c_str(), full_path) == NULL)
[email protected]b2e97292008-09-02 18:20:3470 return false;
[email protected]640517f2008-10-30 23:54:0471 *path = FilePath(full_path);
[email protected]b2e97292008-09-02 18:20:3472 return true;
73}
74
[email protected]4b7743de2009-04-21 01:50:3975int CountFilesCreatedAfter(const FilePath& path,
76 const base::Time& comparison_time) {
77 int file_count = 0;
78
79 DIR* dir = opendir(path.value().c_str());
80 if (dir) {
[email protected]8d578822010-01-25 23:54:5481#if !defined(OS_LINUX) && !defined(OS_MACOSX) && !defined(OS_FREEBSD) && \
[email protected]b5c72b822010-02-18 16:11:3782 !defined(OS_OPENBSD) && !defined(OS_SOLARIS)
[email protected]e43eddf12009-12-29 00:32:5283 #error Port warning: depending on the definition of struct dirent, \
84 additional space for pathname may be needed
[email protected]49930c3a2009-08-06 21:23:0785#endif
[email protected]2126577b2009-04-23 15:05:1986 struct dirent ent_buf;
[email protected]4b7743de2009-04-21 01:50:3987 struct dirent* ent;
[email protected]2126577b2009-04-23 15:05:1988 while (readdir_r(dir, &ent_buf, &ent) == 0 && ent) {
[email protected]4b7743de2009-04-21 01:50:3989 if ((strcmp(ent->d_name, ".") == 0) ||
90 (strcmp(ent->d_name, "..") == 0))
91 continue;
92
[email protected]fb66f9d2009-09-07 16:39:4693 stat_wrapper_t st;
94 int test = CallStat(path.Append(ent->d_name).value().c_str(), &st);
[email protected]4b7743de2009-04-21 01:50:3995 if (test != 0) {
[email protected]57b765672009-10-13 18:27:4096 PLOG(ERROR) << "stat64 failed";
[email protected]4b7743de2009-04-21 01:50:3997 continue;
98 }
[email protected]2126577b2009-04-23 15:05:1999 // Here, we use Time::TimeT(), which discards microseconds. This
100 // means that files which are newer than |comparison_time| may
101 // be considered older. If we don't discard microseconds, it
102 // introduces another issue. Suppose the following case:
103 //
104 // 1. Get |comparison_time| by Time::Now() and the value is 10.1 (secs).
105 // 2. Create a file and the current time is 10.3 (secs).
106 //
107 // As POSIX doesn't have microsecond precision for |st_ctime|,
108 // the creation time of the file created in the step 2 is 10 and
109 // the file is considered older than |comparison_time|. After
110 // all, we may have to accept either of the two issues: 1. files
111 // which are older than |comparison_time| are considered newer
112 // (current implementation) 2. files newer than
113 // |comparison_time| are considered older.
[email protected]4b7743de2009-04-21 01:50:39114 if (st.st_ctime >= comparison_time.ToTimeT())
115 ++file_count;
116 }
117 closedir(dir);
118 }
119 return file_count;
120}
121
[email protected]b2e97292008-09-02 18:20:34122// TODO(erikkay): The Windows version of this accepts paths like "foo/bar/*"
123// which works both with and without the recursive flag. I'm not sure we need
124// that functionality. If not, remove from file_util_win.cc, otherwise add it
125// here.
[email protected]640517f2008-10-30 23:54:04126bool Delete(const FilePath& path, bool recursive) {
127 const char* path_str = path.value().c_str();
[email protected]fb66f9d2009-09-07 16:39:46128 stat_wrapper_t file_info;
129 int test = CallStat(path_str, &file_info);
[email protected]b2e97292008-09-02 18:20:34130 if (test != 0) {
131 // The Windows version defines this condition as success.
[email protected]9e51af92009-02-04 00:58:39132 bool ret = (errno == ENOENT || errno == ENOTDIR);
[email protected]b2e97292008-09-02 18:20:34133 return ret;
134 }
135 if (!S_ISDIR(file_info.st_mode))
[email protected]640517f2008-10-30 23:54:04136 return (unlink(path_str) == 0);
[email protected]b2e97292008-09-02 18:20:34137 if (!recursive)
[email protected]640517f2008-10-30 23:54:04138 return (rmdir(path_str) == 0);
[email protected]b2e97292008-09-02 18:20:34139
140 bool success = true;
[email protected]49930c3a2009-08-06 21:23:07141 std::stack<std::string> directories;
142 directories.push(path.value());
143 FileEnumerator traversal(path, true, static_cast<FileEnumerator::FILE_TYPE>(
144 FileEnumerator::FILES | FileEnumerator::DIRECTORIES |
145 FileEnumerator::SHOW_SYM_LINKS));
146 for (FilePath current = traversal.Next(); success && !current.empty();
147 current = traversal.Next()) {
148 FileEnumerator::FindInfo info;
149 traversal.GetFindInfo(&info);
150
151 if (S_ISDIR(info.stat.st_mode))
152 directories.push(current.value());
153 else
154 success = (unlink(current.value().c_str()) == 0);
[email protected]21dec3872008-09-18 19:15:54155 }
[email protected]49930c3a2009-08-06 21:23:07156
157 while (success && !directories.empty()) {
158 FilePath dir = FilePath(directories.top());
159 directories.pop();
160 success = (rmdir(dir.value().c_str()) == 0);
[email protected]b2e97292008-09-02 18:20:34161 }
[email protected]49930c3a2009-08-06 21:23:07162
[email protected]b2e97292008-09-02 18:20:34163 return success;
164}
165
[email protected]640517f2008-10-30 23:54:04166bool Move(const FilePath& from_path, const FilePath& to_path) {
[email protected]bc6a9012009-10-15 01:11:44167 // Windows compatibility: if to_path exists, from_path and to_path
168 // must be the same type, either both files, or both directories.
169 stat_wrapper_t to_file_info;
170 if (CallStat(to_path.value().c_str(), &to_file_info) == 0) {
171 stat_wrapper_t from_file_info;
172 if (CallStat(from_path.value().c_str(), &from_file_info) == 0) {
173 if (S_ISDIR(to_file_info.st_mode) != S_ISDIR(from_file_info.st_mode))
174 return false;
175 } else {
176 return false;
177 }
178 }
179
[email protected]cc7948a2009-03-13 20:01:43180 if (rename(from_path.value().c_str(), to_path.value().c_str()) == 0)
181 return true;
182
183 if (!CopyDirectory(from_path, to_path, true))
184 return false;
185
186 Delete(from_path, true);
187 return true;
[email protected]b2e97292008-09-02 18:20:34188}
189
[email protected]c5866dca2009-05-19 17:21:07190bool ReplaceFile(const FilePath& from_path, const FilePath& to_path) {
191 return (rename(from_path.value().c_str(), to_path.value().c_str()) == 0);
192}
193
[email protected]640517f2008-10-30 23:54:04194bool CopyDirectory(const FilePath& from_path,
195 const FilePath& to_path,
[email protected]21dec3872008-09-18 19:15:54196 bool recursive) {
[email protected]21dec3872008-09-18 19:15:54197 // Some old callers of CopyDirectory want it to support wildcards.
198 // After some discussion, we decided to fix those callers.
199 // Break loudly here if anyone tries to do this.
200 // TODO(evanm): remove this once we're sure it's ok.
[email protected]640517f2008-10-30 23:54:04201 DCHECK(to_path.value().find('*') == std::string::npos);
202 DCHECK(from_path.value().find('*') == std::string::npos);
[email protected]21dec3872008-09-18 19:15:54203
204 char top_dir[PATH_MAX];
[email protected]640517f2008-10-30 23:54:04205 if (base::strlcpy(top_dir, from_path.value().c_str(),
[email protected]21dec3872008-09-18 19:15:54206 arraysize(top_dir)) >= arraysize(top_dir)) {
207 return false;
208 }
209
[email protected]49930c3a2009-08-06 21:23:07210 // This function does not properly handle destinations within the source
211 FilePath real_to_path = to_path;
212 if (PathExists(real_to_path)) {
213 if (!AbsolutePath(&real_to_path))
214 return false;
215 } else {
216 real_to_path = real_to_path.DirName();
217 if (!AbsolutePath(&real_to_path))
218 return false;
219 }
220 FilePath real_from_path = from_path;
221 if (!AbsolutePath(&real_from_path))
[email protected]21dec3872008-09-18 19:15:54222 return false;
[email protected]49930c3a2009-08-06 21:23:07223 if (real_to_path.value().size() >= real_from_path.value().size() &&
224 real_to_path.value().compare(0, real_from_path.value().size(),
225 real_from_path.value()) == 0)
226 return false;
227
228 bool success = true;
229 FileEnumerator::FILE_TYPE traverse_type =
230 static_cast<FileEnumerator::FILE_TYPE>(FileEnumerator::FILES |
231 FileEnumerator::SHOW_SYM_LINKS);
232 if (recursive)
233 traverse_type = static_cast<FileEnumerator::FILE_TYPE>(
234 traverse_type | FileEnumerator::DIRECTORIES);
235 FileEnumerator traversal(from_path, recursive, traverse_type);
236
[email protected]abbc5732009-10-13 17:57:27237 // We have to mimic windows behavior here. |to_path| may not exist yet,
[email protected]bc6a9012009-10-15 01:11:44238 // start the loop with |to_path|.
[email protected]49930c3a2009-08-06 21:23:07239 FileEnumerator::FindInfo info;
240 FilePath current = from_path;
241 if (stat(from_path.value().c_str(), &info.stat) < 0) {
242 LOG(ERROR) << "CopyDirectory() couldn't stat source directory: " <<
243 from_path.value() << " errno = " << errno;
244 success = false;
[email protected]21dec3872008-09-18 19:15:54245 }
[email protected]bc6a9012009-10-15 01:11:44246 struct stat to_path_stat;
247 FilePath from_path_base = from_path;
248 if (recursive && stat(to_path.value().c_str(), &to_path_stat) == 0 &&
249 S_ISDIR(to_path_stat.st_mode)) {
250 // If the destination already exists and is a directory, then the
251 // top level of source needs to be copied.
252 from_path_base = from_path.DirName();
253 }
254
255 // The Windows version of this function assumes that non-recursive calls
256 // will always have a directory for from_path.
257 DCHECK(recursive || S_ISDIR(info.stat.st_mode));
[email protected]21dec3872008-09-18 19:15:54258
[email protected]49930c3a2009-08-06 21:23:07259 while (success && !current.empty()) {
260 // current is the source path, including from_path, so paste
[email protected]21dec3872008-09-18 19:15:54261 // the suffix after from_path onto to_path to create the target_path.
[email protected]abbc5732009-10-13 17:57:27262 std::string suffix(&current.value().c_str()[from_path_base.value().size()]);
[email protected]ca0209612009-01-13 18:57:46263 // Strip the leading '/' (if any).
264 if (!suffix.empty()) {
[email protected]6ae340f2009-03-06 09:56:28265 DCHECK_EQ('/', suffix[0]);
[email protected]ca0209612009-01-13 18:57:46266 suffix.erase(0, 1);
267 }
268 const FilePath target_path = to_path.Append(suffix);
[email protected]21dec3872008-09-18 19:15:54269
[email protected]49930c3a2009-08-06 21:23:07270 if (S_ISDIR(info.stat.st_mode)) {
271 if (mkdir(target_path.value().c_str(), info.stat.st_mode & 01777) != 0 &&
272 errno != EEXIST) {
273 LOG(ERROR) << "CopyDirectory() couldn't create directory: " <<
274 target_path.value() << " errno = " << errno;
275 success = false;
276 }
277 } else if (S_ISREG(info.stat.st_mode)) {
278 if (!CopyFile(current, target_path)) {
279 LOG(ERROR) << "CopyDirectory() couldn't create file: " <<
280 target_path.value();
281 success = false;
282 }
283 } else {
284 LOG(WARNING) << "CopyDirectory() skipping non-regular file: " <<
285 current.value();
[email protected]21dec3872008-09-18 19:15:54286 }
[email protected]21dec3872008-09-18 19:15:54287
[email protected]49930c3a2009-08-06 21:23:07288 current = traversal.Next();
289 traversal.GetFindInfo(&info);
[email protected]21dec3872008-09-18 19:15:54290 }
291
[email protected]49930c3a2009-08-06 21:23:07292 return success;
[email protected]b2e97292008-09-02 18:20:34293}
294
[email protected]640517f2008-10-30 23:54:04295bool PathExists(const FilePath& path) {
[email protected]fb66f9d2009-09-07 16:39:46296 stat_wrapper_t file_info;
297 return CallStat(path.value().c_str(), &file_info) == 0;
[email protected]b2e97292008-09-02 18:20:34298}
299
[email protected]7e1fde6a2008-12-23 20:20:10300bool PathIsWritable(const FilePath& path) {
301 FilePath test_path(path);
[email protected]fb66f9d2009-09-07 16:39:46302 stat_wrapper_t file_info;
[email protected]5d6018f2010-01-21 17:33:06303 if (CallStat(test_path.value().c_str(), &file_info) != 0)
304 return false;
[email protected]7e1fde6a2008-12-23 20:20:10305 if (S_IWOTH & file_info.st_mode)
306 return true;
307 if (getegid() == file_info.st_gid && (S_IWGRP & file_info.st_mode))
308 return true;
309 if (geteuid() == file_info.st_uid && (S_IWUSR & file_info.st_mode))
310 return true;
311 return false;
312}
313
[email protected]640517f2008-10-30 23:54:04314bool DirectoryExists(const FilePath& path) {
[email protected]fb66f9d2009-09-07 16:39:46315 stat_wrapper_t file_info;
316 if (CallStat(path.value().c_str(), &file_info) == 0)
[email protected]806b9c62008-09-11 16:09:11317 return S_ISDIR(file_info.st_mode);
318 return false;
319}
320
[email protected]b2e97292008-09-02 18:20:34321// TODO(erikkay): implement
322#if 0
323bool GetFileCreationLocalTimeFromHandle(int fd,
324 LPSYSTEMTIME creation_time) {
325 if (!file_handle)
326 return false;
[email protected]9e51af92009-02-04 00:58:39327
[email protected]b2e97292008-09-02 18:20:34328 FILETIME utc_filetime;
329 if (!GetFileTime(file_handle, &utc_filetime, NULL, NULL))
330 return false;
[email protected]9e51af92009-02-04 00:58:39331
[email protected]b2e97292008-09-02 18:20:34332 FILETIME local_filetime;
333 if (!FileTimeToLocalFileTime(&utc_filetime, &local_filetime))
334 return false;
[email protected]9e51af92009-02-04 00:58:39335
[email protected]b2e97292008-09-02 18:20:34336 return !!FileTimeToSystemTime(&local_filetime, creation_time);
337}
338
339bool GetFileCreationLocalTime(const std::string& filename,
340 LPSYSTEMTIME creation_time) {
341 ScopedHandle file_handle(
[email protected]9e51af92009-02-04 00:58:39342 CreateFile(filename.c_str(), GENERIC_READ,
[email protected]b2e97292008-09-02 18:20:34343 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
344 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL));
345 return GetFileCreationLocalTimeFromHandle(file_handle.Get(), creation_time);
346}
347#endif
348
[email protected]45301492009-04-23 12:38:08349bool ReadFromFD(int fd, char* buffer, size_t bytes) {
350 size_t total_read = 0;
351 while (total_read < bytes) {
[email protected]157c61b2009-05-01 21:37:31352 ssize_t bytes_read =
353 HANDLE_EINTR(read(fd, buffer + total_read, bytes - total_read));
354 if (bytes_read <= 0)
[email protected]45301492009-04-23 12:38:08355 break;
[email protected]157c61b2009-05-01 21:37:31356 total_read += bytes_read;
[email protected]45301492009-04-23 12:38:08357 }
358 return total_read == bytes;
359}
360
[email protected]9e51af92009-02-04 00:58:39361// Creates and opens a temporary file in |directory|, returning the
[email protected]16eac0a72009-09-11 17:33:50362// file descriptor. |path| is set to the temporary file path.
363// This function does NOT unlink() the file.
[email protected]9e51af92009-02-04 00:58:39364int CreateAndOpenFdForTemporaryFile(FilePath directory, FilePath* path) {
365 *path = directory.Append(kTempFileName);
366 const std::string& tmpdir_string = path->value();
[email protected]778e8c52008-09-11 17:36:23367 // this should be OK since mkstemp just replaces characters in place
368 char* buffer = const_cast<char*>(tmpdir_string.c_str());
[email protected]392264c2008-11-11 00:01:38369
[email protected]9e51af92009-02-04 00:58:39370 return mkstemp(buffer);
371}
372
[email protected]33edeab2009-08-18 16:07:55373bool CreateTemporaryFile(FilePath* path) {
[email protected]9e51af92009-02-04 00:58:39374 FilePath directory;
375 if (!GetTempDir(&directory))
376 return false;
377 int fd = CreateAndOpenFdForTemporaryFile(directory, path);
[email protected]b2e97292008-09-02 18:20:34378 if (fd < 0)
379 return false;
[email protected]392264c2008-11-11 00:01:38380 close(fd);
[email protected]b2e97292008-09-02 18:20:34381 return true;
382}
383
[email protected]9e51af92009-02-04 00:58:39384FILE* CreateAndOpenTemporaryShmemFile(FilePath* path) {
385 FilePath directory;
386 if (!GetShmemTempDir(&directory))
387 return false;
388
[email protected]6faa0e0d2009-04-28 06:50:36389 return CreateAndOpenTemporaryFileInDir(directory, path);
390}
391
392FILE* CreateAndOpenTemporaryFileInDir(const FilePath& dir, FilePath* path) {
393 int fd = CreateAndOpenFdForTemporaryFile(dir, path);
[email protected]9e51af92009-02-04 00:58:39394 if (fd < 0)
395 return NULL;
396
[email protected]6faa0e0d2009-04-28 06:50:36397 return fdopen(fd, "a+");
[email protected]9e51af92009-02-04 00:58:39398}
[email protected]6445c402009-09-11 20:06:27399
400bool CreateTemporaryFileInDir(const FilePath& dir, FilePath* temp_file) {
401 int fd = CreateAndOpenFdForTemporaryFile(dir, temp_file);
402 return ((fd >= 0) && !close(fd));
[email protected]9ccbb372008-10-10 18:50:32403}
404
[email protected]b0b3abd92010-04-30 17:00:09405static bool CreateTemporaryDirInDirImpl(const FilePath& base_dir,
406 const FilePath::StringType& name_tmpl,
407 FilePath* new_dir) {
408 CHECK(name_tmpl.find("XXXXXX") != FilePath::StringType::npos)
409 << "Directory name template must contain \"XXXXXX\".";
410
411 FilePath sub_dir = base_dir.Append(name_tmpl);
412 std::string sub_dir_string = sub_dir.value();
413
414 // this should be OK since mkdtemp just replaces characters in place
415 char* buffer = const_cast<char*>(sub_dir_string.c_str());
416 char* dtemp = mkdtemp(buffer);
417 if (!dtemp)
418 return false;
419 *new_dir = FilePath(dtemp);
420 return true;
421}
422
423bool CreateTemporaryDirInDir(const FilePath& base_dir,
424 const FilePath::StringType& prefix,
425 FilePath* new_dir) {
426 FilePath::StringType mkdtemp_template = prefix;
427 mkdtemp_template.append(FILE_PATH_LITERAL("XXXXXX"));
428 return CreateTemporaryDirInDirImpl(base_dir, mkdtemp_template, new_dir);
429}
430
[email protected]7e1fde6a2008-12-23 20:20:10431bool CreateNewTempDirectory(const FilePath::StringType& prefix,
432 FilePath* new_temp_path) {
[email protected]392264c2008-11-11 00:01:38433 FilePath tmpdir;
[email protected]b2e97292008-09-02 18:20:34434 if (!GetTempDir(&tmpdir))
435 return false;
[email protected]b0b3abd92010-04-30 17:00:09436
437 return CreateTemporaryDirInDirImpl(tmpdir, kTempFileName, new_temp_path);
[email protected]b2e97292008-09-02 18:20:34438}
439
[email protected]640517f2008-10-30 23:54:04440bool CreateDirectory(const FilePath& full_path) {
441 std::vector<FilePath> subpaths;
442
443 // Collect a list of all parent directories.
444 FilePath last_path = full_path;
445 subpaths.push_back(full_path);
446 for (FilePath path = full_path.DirName();
447 path.value() != last_path.value(); path = path.DirName()) {
448 subpaths.push_back(path);
449 last_path = path;
450 }
451
452 // Iterate through the parents and create the missing ones.
453 for (std::vector<FilePath>::reverse_iterator i = subpaths.rbegin();
454 i != subpaths.rend(); ++i) {
[email protected]0ba86e42010-03-17 21:39:42455 if (DirectoryExists(*i))
456 continue;
457 if (mkdir(i->value().c_str(), 0700) == 0)
458 continue;
459 // Mkdir failed, but it might have failed with EEXIST, or some other error
460 // due to the the directory appearing out of thin air. This can occur if
461 // two processes are trying to create the same file system tree at the same
462 // time. Check to see if it exists and make sure it is a directory.
463 if (!DirectoryExists(*i))
464 return false;
[email protected]b2e97292008-09-02 18:20:34465 }
466 return true;
467}
468
[email protected]eac0709a2008-11-04 21:00:46469bool GetFileInfo(const FilePath& file_path, FileInfo* results) {
[email protected]fb66f9d2009-09-07 16:39:46470 stat_wrapper_t file_info;
471 if (CallStat(file_path.value().c_str(), &file_info) != 0)
[email protected]b2e97292008-09-02 18:20:34472 return false;
[email protected]f5e3da4d2008-09-26 01:04:08473 results->is_directory = S_ISDIR(file_info.st_mode);
474 results->size = file_info.st_size;
[email protected]5ef323892009-07-24 16:13:53475 results->last_modified = base::Time::FromTimeT(file_info.st_mtime);
[email protected]b2e97292008-09-02 18:20:34476 return true;
477}
478
[email protected]ec3d1452010-02-18 10:02:26479bool SetLastModifiedTime(const FilePath& file_path, base::Time last_modified) {
480 struct timeval times[2];
481 times[0] = last_modified.ToTimeVal();
482 times[1] = last_modified.ToTimeVal();
483 return (utimes(file_path.value().c_str(), times) == 0);
484}
485
[email protected]825003f2009-05-14 17:49:23486bool GetInode(const FilePath& path, ino_t* inode) {
487 struct stat buffer;
488 int result = stat(path.value().c_str(), &buffer);
489 if (result < 0)
490 return false;
491
492 *inode = buffer.st_ino;
493 return true;
494}
495
[email protected]836f1342008-10-01 17:40:13496FILE* OpenFile(const std::string& filename, const char* mode) {
[email protected]a9cd2a652008-11-17 21:01:19497 return OpenFile(FilePath(filename), mode);
[email protected]836f1342008-10-01 17:40:13498}
499
[email protected]a9cd2a652008-11-17 21:01:19500FILE* OpenFile(const FilePath& filename, const char* mode) {
501 return fopen(filename.value().c_str(), mode);
[email protected]836f1342008-10-01 17:40:13502}
503
[email protected]c870c762009-01-28 05:47:15504int ReadFile(const FilePath& filename, char* data, int size) {
505 int fd = open(filename.value().c_str(), O_RDONLY);
[email protected]b2e97292008-09-02 18:20:34506 if (fd < 0)
507 return -1;
[email protected]a9cd2a652008-11-17 21:01:19508
[email protected]cabe39c2010-02-02 02:28:16509 ssize_t bytes_read = HANDLE_EINTR(read(fd, data, size));
510 if (int ret = HANDLE_EINTR(close(fd)) < 0)
511 return ret;
512 return bytes_read;
[email protected]b2e97292008-09-02 18:20:34513}
514
[email protected]c870c762009-01-28 05:47:15515int WriteFile(const FilePath& filename, const char* data, int size) {
516 int fd = creat(filename.value().c_str(), 0666);
[email protected]b2e97292008-09-02 18:20:34517 if (fd < 0)
518 return -1;
[email protected]778e8c52008-09-11 17:36:23519
[email protected]cabe39c2010-02-02 02:28:16520 int bytes_written = WriteFileDescriptor(fd, data, size);
521 if (int ret = HANDLE_EINTR(close(fd)) < 0)
522 return ret;
523 return bytes_written;
[email protected]fbea0232009-09-16 00:29:22524}
525
526int WriteFileDescriptor(const int fd, const char* data, int size) {
527 // Allow for partial writes.
528 ssize_t bytes_written_total = 0;
529 for (ssize_t bytes_written_partial = 0; bytes_written_total < size;
530 bytes_written_total += bytes_written_partial) {
531 bytes_written_partial =
532 HANDLE_EINTR(write(fd, data + bytes_written_total,
533 size - bytes_written_total));
534 if (bytes_written_partial < 0)
535 return -1;
536 }
537
[email protected]778e8c52008-09-11 17:36:23538 return bytes_written_total;
[email protected]b2e97292008-09-02 18:20:34539}
540
541// Gets the current working directory for the process.
[email protected]640517f2008-10-30 23:54:04542bool GetCurrentDirectory(FilePath* dir) {
[email protected]b2e97292008-09-02 18:20:34543 char system_buffer[PATH_MAX] = "";
[email protected]640517f2008-10-30 23:54:04544 if (!getcwd(system_buffer, sizeof(system_buffer))) {
545 NOTREACHED();
546 return false;
547 }
548 *dir = FilePath(system_buffer);
[email protected]b2e97292008-09-02 18:20:34549 return true;
550}
551
552// Sets the current working directory for the process.
[email protected]a9cd2a652008-11-17 21:01:19553bool SetCurrentDirectory(const FilePath& path) {
554 int ret = chdir(path.value().c_str());
555 return !ret;
[email protected]b2e97292008-09-02 18:20:34556}
[email protected]a9cd2a652008-11-17 21:01:19557
[email protected]7856bb82008-12-12 23:43:03558///////////////////////////////////////////////
559// FileEnumerator
560
[email protected]0b733222008-12-11 14:55:12561FileEnumerator::FileEnumerator(const FilePath& root_path,
[email protected]b2e97292008-09-02 18:20:34562 bool recursive,
563 FileEnumerator::FILE_TYPE file_type)
[email protected]fbf745a2009-12-08 22:05:59564 : current_directory_entry_(0),
565 root_path_(root_path),
[email protected]49930c3a2009-08-06 21:23:07566 recursive_(recursive),
[email protected]b2e97292008-09-02 18:20:34567 file_type_(file_type),
[email protected]fbf745a2009-12-08 22:05:59568 is_in_find_op_(false) {
[email protected]8199b3a2009-06-09 05:57:38569 // INCLUDE_DOT_DOT must not be specified if recursive.
570 DCHECK(!(recursive && (INCLUDE_DOT_DOT & file_type_)));
[email protected]b2e97292008-09-02 18:20:34571 pending_paths_.push(root_path);
572}
573
[email protected]0b733222008-12-11 14:55:12574FileEnumerator::FileEnumerator(const FilePath& root_path,
[email protected]b2e97292008-09-02 18:20:34575 bool recursive,
576 FileEnumerator::FILE_TYPE file_type,
[email protected]0b733222008-12-11 14:55:12577 const FilePath::StringType& pattern)
[email protected]fbf745a2009-12-08 22:05:59578 : current_directory_entry_(0),
579 root_path_(root_path),
[email protected]49930c3a2009-08-06 21:23:07580 recursive_(recursive),
[email protected]b2e97292008-09-02 18:20:34581 file_type_(file_type),
[email protected]fbf745a2009-12-08 22:05:59582 pattern_(root_path.Append(pattern).value()),
583 is_in_find_op_(false) {
[email protected]8199b3a2009-06-09 05:57:38584 // INCLUDE_DOT_DOT must not be specified if recursive.
585 DCHECK(!(recursive && (INCLUDE_DOT_DOT & file_type_)));
[email protected]49930c3a2009-08-06 21:23:07586 // The Windows version of this code appends the pattern to the root_path,
587 // potentially only matching against items in the top-most directory.
588 // Do the same here.
589 if (pattern.size() == 0)
[email protected]fbf745a2009-12-08 22:05:59590 pattern_ = FilePath::StringType();
[email protected]b2e97292008-09-02 18:20:34591 pending_paths_.push(root_path);
592}
[email protected]a9cd2a652008-11-17 21:01:19593
[email protected]b2e97292008-09-02 18:20:34594FileEnumerator::~FileEnumerator() {
[email protected]b2e97292008-09-02 18:20:34595}
596
[email protected]13ef7c02008-11-20 22:30:13597void FileEnumerator::GetFindInfo(FindInfo* info) {
598 DCHECK(info);
599
[email protected]49930c3a2009-08-06 21:23:07600 if (current_directory_entry_ >= directory_entries_.size())
[email protected]13ef7c02008-11-20 22:30:13601 return;
602
[email protected]49930c3a2009-08-06 21:23:07603 DirectoryEntryInfo* cur_entry = &directory_entries_[current_directory_entry_];
604 memcpy(&(info->stat), &(cur_entry->stat), sizeof(info->stat));
605 info->filename.assign(cur_entry->filename.value());
[email protected]13ef7c02008-11-20 22:30:13606}
607
[email protected]9fa8c2f52009-12-11 21:12:29608bool FileEnumerator::IsDirectory(const FindInfo& info) {
609 return S_ISDIR(info.stat.st_mode);
610}
611
[email protected]1525c682010-02-11 23:27:47612// static
613FilePath FileEnumerator::GetFilename(const FindInfo& find_info) {
614 return FilePath(find_info.filename);
615}
616
[email protected]0b733222008-12-11 14:55:12617FilePath FileEnumerator::Next() {
[email protected]49930c3a2009-08-06 21:23:07618 ++current_directory_entry_;
619
620 // While we've exhausted the entries in the current directory, do the next
621 while (current_directory_entry_ >= directory_entries_.size()) {
[email protected]b2e97292008-09-02 18:20:34622 if (pending_paths_.empty())
[email protected]0b733222008-12-11 14:55:12623 return FilePath();
[email protected]13ef7c02008-11-20 22:30:13624
[email protected]b2e97292008-09-02 18:20:34625 root_path_ = pending_paths_.top();
[email protected]0b733222008-12-11 14:55:12626 root_path_ = root_path_.StripTrailingSeparators();
[email protected]b2e97292008-09-02 18:20:34627 pending_paths_.pop();
[email protected]13ef7c02008-11-20 22:30:13628
[email protected]49930c3a2009-08-06 21:23:07629 std::vector<DirectoryEntryInfo> entries;
630 if (!ReadDirectory(&entries, root_path_, file_type_ & SHOW_SYM_LINKS))
631 continue;
[email protected]13ef7c02008-11-20 22:30:13632
[email protected]49930c3a2009-08-06 21:23:07633 directory_entries_.clear();
634 current_directory_entry_ = 0;
635 for (std::vector<DirectoryEntryInfo>::const_iterator
636 i = entries.begin(); i != entries.end(); ++i) {
637 FilePath full_path = root_path_.Append(i->filename);
638 if (ShouldSkip(full_path))
639 continue;
[email protected]13ef7c02008-11-20 22:30:13640
[email protected]fbf745a2009-12-08 22:05:59641 if (pattern_.size() &&
642 fnmatch(pattern_.c_str(), full_path.value().c_str(), FNM_NOESCAPE))
[email protected]49930c3a2009-08-06 21:23:07643 continue;
644
645 if (recursive_ && S_ISDIR(i->stat.st_mode))
646 pending_paths_.push(full_path);
647
648 if ((S_ISDIR(i->stat.st_mode) && (file_type_ & DIRECTORIES)) ||
649 (!S_ISDIR(i->stat.st_mode) && (file_type_ & FILES)))
650 directory_entries_.push_back(*i);
[email protected]b2e97292008-09-02 18:20:34651 }
652 }
[email protected]13ef7c02008-11-20 22:30:13653
[email protected]49930c3a2009-08-06 21:23:07654 return root_path_.Append(directory_entries_[current_directory_entry_
655 ].filename);
656}
[email protected]8199b3a2009-06-09 05:57:38657
[email protected]49930c3a2009-08-06 21:23:07658bool FileEnumerator::ReadDirectory(std::vector<DirectoryEntryInfo>* entries,
659 const FilePath& source, bool show_links) {
660 DIR* dir = opendir(source.value().c_str());
661 if (!dir)
662 return false;
663
[email protected]8d578822010-01-25 23:54:54664#if !defined(OS_LINUX) && !defined(OS_MACOSX) && !defined(OS_FREEBSD) && \
[email protected]b5c72b822010-02-18 16:11:37665 !defined(OS_OPENBSD) && !defined(OS_SOLARIS)
[email protected]e43eddf12009-12-29 00:32:52666 #error Port warning: depending on the definition of struct dirent, \
667 additional space for pathname may be needed
[email protected]49930c3a2009-08-06 21:23:07668#endif
[email protected]e43eddf12009-12-29 00:32:52669
[email protected]49930c3a2009-08-06 21:23:07670 struct dirent dent_buf;
671 struct dirent* dent;
672 while (readdir_r(dir, &dent_buf, &dent) == 0 && dent) {
673 DirectoryEntryInfo info;
[email protected]49930c3a2009-08-06 21:23:07674 info.filename = FilePath(dent->d_name);
[email protected]2237f5d72009-09-03 22:39:34675
676 FilePath full_name = source.Append(dent->d_name);
677 int ret;
[email protected]49930c3a2009-08-06 21:23:07678 if (show_links)
[email protected]2237f5d72009-09-03 22:39:34679 ret = lstat(full_name.value().c_str(), &info.stat);
[email protected]49930c3a2009-08-06 21:23:07680 else
[email protected]2237f5d72009-09-03 22:39:34681 ret = stat(full_name.value().c_str(), &info.stat);
682 if (ret < 0) {
683 // Print the stat() error message unless it was ENOENT and we're
684 // following symlinks.
[email protected]e131cf52010-03-25 19:10:28685 if (!(errno == ENOENT && !show_links)) {
[email protected]57b765672009-10-13 18:27:40686 PLOG(ERROR) << "Couldn't stat "
687 << source.Append(dent->d_name).value();
[email protected]2237f5d72009-09-03 22:39:34688 }
[email protected]49930c3a2009-08-06 21:23:07689 memset(&info.stat, 0, sizeof(info.stat));
[email protected]8199b3a2009-06-09 05:57:38690 }
[email protected]49930c3a2009-08-06 21:23:07691 entries->push_back(info);
[email protected]b2e97292008-09-02 18:20:34692 }
[email protected]49930c3a2009-08-06 21:23:07693
694 closedir(dir);
695 return true;
696}
697
[email protected]7856bb82008-12-12 23:43:03698///////////////////////////////////////////////
699// MemoryMappedFile
700
701MemoryMappedFile::MemoryMappedFile()
[email protected]cb6037d2009-11-16 22:55:17702 : file_(base::kInvalidPlatformFileValue),
703 data_(NULL),
[email protected]7856bb82008-12-12 23:43:03704 length_(0) {
705}
706
[email protected]85c55dc2009-11-06 03:05:46707bool MemoryMappedFile::MapFileToMemoryInternal() {
[email protected]7856bb82008-12-12 23:43:03708 struct stat file_stat;
[email protected]cb6037d2009-11-16 22:55:17709 if (fstat(file_, &file_stat) == base::kInvalidPlatformFileValue) {
710 LOG(ERROR) << "Couldn't fstat " << file_ << ", errno " << errno;
[email protected]7856bb82008-12-12 23:43:03711 return false;
[email protected]4883a4e2009-06-06 19:59:36712 }
[email protected]7856bb82008-12-12 23:43:03713 length_ = file_stat.st_size;
714
715 data_ = static_cast<uint8*>(
[email protected]cb6037d2009-11-16 22:55:17716 mmap(NULL, length_, PROT_READ, MAP_SHARED, file_, 0));
[email protected]7856bb82008-12-12 23:43:03717 if (data_ == MAP_FAILED)
[email protected]cb6037d2009-11-16 22:55:17718 LOG(ERROR) << "Couldn't mmap " << file_ << ", errno " << errno;
[email protected]4883a4e2009-06-06 19:59:36719
720 return data_ != MAP_FAILED;
[email protected]7856bb82008-12-12 23:43:03721}
722
723void MemoryMappedFile::CloseHandles() {
724 if (data_ != NULL)
725 munmap(data_, length_);
[email protected]cb6037d2009-11-16 22:55:17726 if (file_ != base::kInvalidPlatformFileValue)
727 close(file_);
[email protected]7856bb82008-12-12 23:43:03728
729 data_ = NULL;
730 length_ = 0;
[email protected]cb6037d2009-11-16 22:55:17731 file_ = base::kInvalidPlatformFileValue;
[email protected]7856bb82008-12-12 23:43:03732}
[email protected]13ef7c02008-11-20 22:30:13733
[email protected]35b9be02009-11-26 00:37:06734bool HasFileBeenModifiedSince(const FileEnumerator::FindInfo& find_info,
735 const base::Time& cutoff_time) {
736 return find_info.stat.st_mtime >= cutoff_time.ToTimeT();
737}
738
[email protected]01e2a1f2010-05-12 15:13:57739bool RealPath(const FilePath& path, FilePath* real_path) {
740 FilePath::CharType buf[PATH_MAX];
741 if (!realpath(path.value().c_str(), buf))
742 return false;
743
744 *real_path = FilePath(buf);
745 return true;
746}
747
[email protected]84cb1912010-04-21 21:11:36748#if !defined(OS_MACOSX)
749bool GetTempDir(FilePath* path) {
750 const char* tmp = getenv("TMPDIR");
751 if (tmp)
752 *path = FilePath(tmp);
753 else
754 *path = FilePath("/tmp");
755 return true;
756}
757
758bool GetShmemTempDir(FilePath* path) {
759 *path = FilePath("/dev/shm");
760 return true;
761}
762
[email protected]1c657852010-04-22 23:28:05763FilePath GetHomeDir() {
764 const char* home_dir = getenv("HOME");
765 if (home_dir && home_dir[0])
766 return FilePath(home_dir);
767
768 home_dir = g_get_home_dir();
769 if (home_dir && home_dir[0])
770 return FilePath(home_dir);
771
772 FilePath rv;
773 if (file_util::GetTempDir(&rv))
774 return rv;
775
776 // Last resort.
777 return FilePath("/tmp");
778}
779
[email protected]84cb1912010-04-21 21:11:36780bool CopyFile(const FilePath& from_path, const FilePath& to_path) {
781 int infile = open(from_path.value().c_str(), O_RDONLY);
782 if (infile < 0)
783 return false;
784
785 int outfile = creat(to_path.value().c_str(), 0666);
786 if (outfile < 0) {
787 close(infile);
788 return false;
789 }
790
791 const size_t kBufferSize = 32768;
792 std::vector<char> buffer(kBufferSize);
793 bool result = true;
794
795 while (result) {
796 ssize_t bytes_read = HANDLE_EINTR(read(infile, &buffer[0], buffer.size()));
797 if (bytes_read < 0) {
798 result = false;
799 break;
800 }
801 if (bytes_read == 0)
802 break;
803 // Allow for partial writes
804 ssize_t bytes_written_per_read = 0;
805 do {
806 ssize_t bytes_written_partial = HANDLE_EINTR(write(
807 outfile,
808 &buffer[bytes_written_per_read],
809 bytes_read - bytes_written_per_read));
810 if (bytes_written_partial < 0) {
811 result = false;
812 break;
813 }
814 bytes_written_per_read += bytes_written_partial;
815 } while (bytes_written_per_read < bytes_read);
816 }
817
818 if (HANDLE_EINTR(close(infile)) < 0)
819 result = false;
820 if (HANDLE_EINTR(close(outfile)) < 0)
821 result = false;
822
823 return result;
824}
825#endif // defined(OS_MACOSX)
826
[email protected]b2e97292008-09-02 18:20:34827} // namespace file_util