blob: 1a05d797ae49290acf0df79b68ef484d9b477cc6 [file] [log] [blame]
mmentovai@google.comaa13be62008-09-03 03:20:34 +09001// Copyright (c) 2006-2008 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
evan@chromium.org37301322009-04-21 10:50:39 +09007#include <dirent.h>
evanm@google.com5c1d39b2008-09-19 04:15:54 +09008#include <errno.h>
mmentovai@google.comaa13be62008-09-03 03:20:34 +09009#include <fcntl.h>
10#include <fnmatch.h>
mmentovai@google.comaa13be62008-09-03 03:20:34 +090011#include <libgen.h>
mark@chromium.orgd1bafc62008-10-02 02:40:13 +090012#include <stdio.h>
evanm@google.com5c1d39b2008-09-19 04:15:54 +090013#include <string.h>
mmentovai@google.comaa13be62008-09-03 03:20:34 +090014#include <sys/errno.h>
estade@chromium.org2c233532008-12-13 08:43:03 +090015#include <sys/mman.h>
mmentovai@google.comaa13be62008-09-03 03:20:34 +090016#include <sys/stat.h>
evan@chromium.org37301322009-04-21 10:50:39 +090017#include <sys/types.h>
mmentovai@google.comaa13be62008-09-03 03:20:34 +090018#include <time.h>
evan@chromium.org37301322009-04-21 10:50:39 +090019#include <unistd.h>
mmentovai@google.comaa13be62008-09-03 03:20:34 +090020
mark@chromium.org0e56c162009-09-17 02:31:25 +090021#if defined(OS_MACOSX)
22#include <AvailabilityMacros.h>
23#endif
24
mmentovai@google.comaa13be62008-09-03 03:20:34 +090025#include <fstream>
26
27#include "base/basictypes.h"
agl@chromium.orgd263ad72009-05-02 06:37:31 +090028#include "base/eintr_wrapper.h"
evanm@google.com874d1672008-10-31 08:54:04 +090029#include "base/file_path.h"
yuzo@chromium.orgc846e452009-07-15 10:32:02 +090030#include "base/lock.h"
mmentovai@google.comaa13be62008-09-03 03:20:34 +090031#include "base/logging.h"
yuzo@chromium.orgc846e452009-07-15 10:32:02 +090032#include "base/scoped_ptr.h"
33#include "base/singleton.h"
mmentovai@google.comaa13be62008-09-03 03:20:34 +090034#include "base/string_util.h"
yuzo@chromium.orgc846e452009-07-15 10:32:02 +090035#include "base/sys_string_conversions.h"
evan@chromium.org37301322009-04-21 10:50:39 +090036#include "base/time.h"
brettw@chromium.org50c94652009-10-07 11:10:20 +090037#include "base/utf_string_conversions.h"
estade@chromium.org868ecbc2009-06-24 12:29:26 +090038
mmentovai@google.comaa13be62008-09-03 03:20:34 +090039namespace file_util {
40
mark@chromium.org0e56c162009-09-17 02:31:25 +090041#if defined(OS_FREEBSD) || \
42 (defined(OS_MACOSX) && \
43 MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5)
benl@chromium.org6b6b2162009-09-08 01:39:46 +090044typedef struct stat stat_wrapper_t;
45static int CallStat(const char *path, stat_wrapper_t *sb) {
46 return stat(path, sb);
47}
48#else
49typedef struct stat64 stat_wrapper_t;
50static int CallStat(const char *path, stat_wrapper_t *sb) {
51 return stat64(path, sb);
52}
53#endif
54
55
estade@chromium.orgf3b73202009-03-18 04:17:43 +090056#if defined(GOOGLE_CHROME_BUILD)
estade@chromium.orgf474a1b2008-11-11 09:01:38 +090057static const char* kTempFileName = "com.google.chrome.XXXXXX";
estade@chromium.orgf3b73202009-03-18 04:17:43 +090058#else
59static const char* kTempFileName = "org.chromium.XXXXXX";
60#endif
mark@chromium.org8ca0d272008-09-12 02:36:23 +090061
mmentovai@google.comaa13be62008-09-03 03:20:34 +090062std::wstring GetDirectoryFromPath(const std::wstring& path) {
63 if (EndsWithSeparator(path)) {
tkent@chromium.orgfce07c72009-10-15 14:00:25 +090064 return FilePath::FromWStringHack(path)
65 .StripTrailingSeparators()
66 .ToWStringHack();
mmentovai@google.comaa13be62008-09-03 03:20:34 +090067 } else {
68 char full_path[PATH_MAX];
69 base::strlcpy(full_path, WideToUTF8(path).c_str(), arraysize(full_path));
70 return UTF8ToWide(dirname(full_path));
71 }
72}
jrg@chromium.orgd505c3a2009-02-04 09:58:39 +090073
evanm@google.com874d1672008-10-31 08:54:04 +090074bool AbsolutePath(FilePath* path) {
mmentovai@google.comaa13be62008-09-03 03:20:34 +090075 char full_path[PATH_MAX];
evanm@google.com874d1672008-10-31 08:54:04 +090076 if (realpath(path->value().c_str(), full_path) == NULL)
mmentovai@google.comaa13be62008-09-03 03:20:34 +090077 return false;
evanm@google.com874d1672008-10-31 08:54:04 +090078 *path = FilePath(full_path);
mmentovai@google.comaa13be62008-09-03 03:20:34 +090079 return true;
80}
81
evan@chromium.org37301322009-04-21 10:50:39 +090082int CountFilesCreatedAfter(const FilePath& path,
83 const base::Time& comparison_time) {
84 int file_count = 0;
85
86 DIR* dir = opendir(path.value().c_str());
87 if (dir) {
benl@chromium.org6b6b2162009-09-08 01:39:46 +090088#if !defined(OS_LINUX) && !defined(OS_MACOSX) && !defined(OS_FREEBSD)
thestig@chromium.org3217c822009-08-07 06:23:07 +090089 #error Depending on the definition of struct dirent, additional space for \
90 pathname may be needed
91#endif
erikkay@google.com8d133f62009-04-24 00:05:19 +090092 struct dirent ent_buf;
evan@chromium.org37301322009-04-21 10:50:39 +090093 struct dirent* ent;
erikkay@google.com8d133f62009-04-24 00:05:19 +090094 while (readdir_r(dir, &ent_buf, &ent) == 0 && ent) {
evan@chromium.org37301322009-04-21 10:50:39 +090095 if ((strcmp(ent->d_name, ".") == 0) ||
96 (strcmp(ent->d_name, "..") == 0))
97 continue;
98
benl@chromium.org6b6b2162009-09-08 01:39:46 +090099 stat_wrapper_t st;
100 int test = CallStat(path.Append(ent->d_name).value().c_str(), &st);
evan@chromium.org37301322009-04-21 10:50:39 +0900101 if (test != 0) {
tschmelcher@chromium.org90a3f8a2009-10-14 03:27:40 +0900102 PLOG(ERROR) << "stat64 failed";
evan@chromium.org37301322009-04-21 10:50:39 +0900103 continue;
104 }
erikkay@google.com8d133f62009-04-24 00:05:19 +0900105 // Here, we use Time::TimeT(), which discards microseconds. This
106 // means that files which are newer than |comparison_time| may
107 // be considered older. If we don't discard microseconds, it
108 // introduces another issue. Suppose the following case:
109 //
110 // 1. Get |comparison_time| by Time::Now() and the value is 10.1 (secs).
111 // 2. Create a file and the current time is 10.3 (secs).
112 //
113 // As POSIX doesn't have microsecond precision for |st_ctime|,
114 // the creation time of the file created in the step 2 is 10 and
115 // the file is considered older than |comparison_time|. After
116 // all, we may have to accept either of the two issues: 1. files
117 // which are older than |comparison_time| are considered newer
118 // (current implementation) 2. files newer than
119 // |comparison_time| are considered older.
evan@chromium.org37301322009-04-21 10:50:39 +0900120 if (st.st_ctime >= comparison_time.ToTimeT())
121 ++file_count;
122 }
123 closedir(dir);
124 }
125 return file_count;
126}
127
mmentovai@google.comaa13be62008-09-03 03:20:34 +0900128// TODO(erikkay): The Windows version of this accepts paths like "foo/bar/*"
129// which works both with and without the recursive flag. I'm not sure we need
130// that functionality. If not, remove from file_util_win.cc, otherwise add it
131// here.
evanm@google.com874d1672008-10-31 08:54:04 +0900132bool Delete(const FilePath& path, bool recursive) {
133 const char* path_str = path.value().c_str();
benl@chromium.org6b6b2162009-09-08 01:39:46 +0900134 stat_wrapper_t file_info;
135 int test = CallStat(path_str, &file_info);
mmentovai@google.comaa13be62008-09-03 03:20:34 +0900136 if (test != 0) {
137 // The Windows version defines this condition as success.
jrg@chromium.orgd505c3a2009-02-04 09:58:39 +0900138 bool ret = (errno == ENOENT || errno == ENOTDIR);
mmentovai@google.comaa13be62008-09-03 03:20:34 +0900139 return ret;
140 }
141 if (!S_ISDIR(file_info.st_mode))
evanm@google.com874d1672008-10-31 08:54:04 +0900142 return (unlink(path_str) == 0);
mmentovai@google.comaa13be62008-09-03 03:20:34 +0900143 if (!recursive)
evanm@google.com874d1672008-10-31 08:54:04 +0900144 return (rmdir(path_str) == 0);
mmentovai@google.comaa13be62008-09-03 03:20:34 +0900145
146 bool success = true;
thestig@chromium.org3217c822009-08-07 06:23:07 +0900147 std::stack<std::string> directories;
148 directories.push(path.value());
149 FileEnumerator traversal(path, true, static_cast<FileEnumerator::FILE_TYPE>(
150 FileEnumerator::FILES | FileEnumerator::DIRECTORIES |
151 FileEnumerator::SHOW_SYM_LINKS));
152 for (FilePath current = traversal.Next(); success && !current.empty();
153 current = traversal.Next()) {
154 FileEnumerator::FindInfo info;
155 traversal.GetFindInfo(&info);
156
157 if (S_ISDIR(info.stat.st_mode))
158 directories.push(current.value());
159 else
160 success = (unlink(current.value().c_str()) == 0);
evanm@google.com5c1d39b2008-09-19 04:15:54 +0900161 }
thestig@chromium.org3217c822009-08-07 06:23:07 +0900162
163 while (success && !directories.empty()) {
164 FilePath dir = FilePath(directories.top());
165 directories.pop();
166 success = (rmdir(dir.value().c_str()) == 0);
mmentovai@google.comaa13be62008-09-03 03:20:34 +0900167 }
thestig@chromium.org3217c822009-08-07 06:23:07 +0900168
mmentovai@google.comaa13be62008-09-03 03:20:34 +0900169 return success;
170}
171
evanm@google.com874d1672008-10-31 08:54:04 +0900172bool Move(const FilePath& from_path, const FilePath& to_path) {
vandebo@chromium.orgc0cf77e2009-10-15 10:11:44 +0900173 // Windows compatibility: if to_path exists, from_path and to_path
174 // must be the same type, either both files, or both directories.
175 stat_wrapper_t to_file_info;
176 if (CallStat(to_path.value().c_str(), &to_file_info) == 0) {
177 stat_wrapper_t from_file_info;
178 if (CallStat(from_path.value().c_str(), &from_file_info) == 0) {
179 if (S_ISDIR(to_file_info.st_mode) != S_ISDIR(from_file_info.st_mode))
180 return false;
181 } else {
182 return false;
183 }
184 }
185
estade@chromium.orga2fd8d82009-03-14 05:01:43 +0900186 if (rename(from_path.value().c_str(), to_path.value().c_str()) == 0)
187 return true;
188
189 if (!CopyDirectory(from_path, to_path, true))
190 return false;
191
192 Delete(from_path, true);
193 return true;
mmentovai@google.comaa13be62008-09-03 03:20:34 +0900194}
195
phajdan.jr@chromium.orgd86bea02009-05-20 02:21:07 +0900196bool ReplaceFile(const FilePath& from_path, const FilePath& to_path) {
197 return (rename(from_path.value().c_str(), to_path.value().c_str()) == 0);
198}
199
evanm@google.com874d1672008-10-31 08:54:04 +0900200bool CopyDirectory(const FilePath& from_path,
201 const FilePath& to_path,
evanm@google.com5c1d39b2008-09-19 04:15:54 +0900202 bool recursive) {
evanm@google.com5c1d39b2008-09-19 04:15:54 +0900203 // Some old callers of CopyDirectory want it to support wildcards.
204 // After some discussion, we decided to fix those callers.
205 // Break loudly here if anyone tries to do this.
206 // TODO(evanm): remove this once we're sure it's ok.
evanm@google.com874d1672008-10-31 08:54:04 +0900207 DCHECK(to_path.value().find('*') == std::string::npos);
208 DCHECK(from_path.value().find('*') == std::string::npos);
evanm@google.com5c1d39b2008-09-19 04:15:54 +0900209
210 char top_dir[PATH_MAX];
evanm@google.com874d1672008-10-31 08:54:04 +0900211 if (base::strlcpy(top_dir, from_path.value().c_str(),
evanm@google.com5c1d39b2008-09-19 04:15:54 +0900212 arraysize(top_dir)) >= arraysize(top_dir)) {
213 return false;
214 }
215
thestig@chromium.org3217c822009-08-07 06:23:07 +0900216 // This function does not properly handle destinations within the source
217 FilePath real_to_path = to_path;
218 if (PathExists(real_to_path)) {
219 if (!AbsolutePath(&real_to_path))
220 return false;
221 } else {
222 real_to_path = real_to_path.DirName();
223 if (!AbsolutePath(&real_to_path))
224 return false;
225 }
226 FilePath real_from_path = from_path;
227 if (!AbsolutePath(&real_from_path))
evanm@google.com5c1d39b2008-09-19 04:15:54 +0900228 return false;
thestig@chromium.org3217c822009-08-07 06:23:07 +0900229 if (real_to_path.value().size() >= real_from_path.value().size() &&
230 real_to_path.value().compare(0, real_from_path.value().size(),
231 real_from_path.value()) == 0)
232 return false;
233
234 bool success = true;
235 FileEnumerator::FILE_TYPE traverse_type =
236 static_cast<FileEnumerator::FILE_TYPE>(FileEnumerator::FILES |
237 FileEnumerator::SHOW_SYM_LINKS);
238 if (recursive)
239 traverse_type = static_cast<FileEnumerator::FILE_TYPE>(
240 traverse_type | FileEnumerator::DIRECTORIES);
241 FileEnumerator traversal(from_path, recursive, traverse_type);
242
vandebo@chromium.org70cf3f12009-10-14 02:57:27 +0900243 // We have to mimic windows behavior here. |to_path| may not exist yet,
vandebo@chromium.orgc0cf77e2009-10-15 10:11:44 +0900244 // start the loop with |to_path|.
thestig@chromium.org3217c822009-08-07 06:23:07 +0900245 FileEnumerator::FindInfo info;
246 FilePath current = from_path;
247 if (stat(from_path.value().c_str(), &info.stat) < 0) {
248 LOG(ERROR) << "CopyDirectory() couldn't stat source directory: " <<
249 from_path.value() << " errno = " << errno;
250 success = false;
evanm@google.com5c1d39b2008-09-19 04:15:54 +0900251 }
vandebo@chromium.orgc0cf77e2009-10-15 10:11:44 +0900252 struct stat to_path_stat;
253 FilePath from_path_base = from_path;
254 if (recursive && stat(to_path.value().c_str(), &to_path_stat) == 0 &&
255 S_ISDIR(to_path_stat.st_mode)) {
256 // If the destination already exists and is a directory, then the
257 // top level of source needs to be copied.
258 from_path_base = from_path.DirName();
259 }
260
261 // The Windows version of this function assumes that non-recursive calls
262 // will always have a directory for from_path.
263 DCHECK(recursive || S_ISDIR(info.stat.st_mode));
evanm@google.com5c1d39b2008-09-19 04:15:54 +0900264
thestig@chromium.org3217c822009-08-07 06:23:07 +0900265 while (success && !current.empty()) {
266 // current is the source path, including from_path, so paste
evanm@google.com5c1d39b2008-09-19 04:15:54 +0900267 // the suffix after from_path onto to_path to create the target_path.
vandebo@chromium.org70cf3f12009-10-14 02:57:27 +0900268 std::string suffix(&current.value().c_str()[from_path_base.value().size()]);
phajdan.jr@chromium.orgecf50752009-01-14 03:57:46 +0900269 // Strip the leading '/' (if any).
270 if (!suffix.empty()) {
phajdan.jr@chromium.org8c9b4512009-03-06 18:56:28 +0900271 DCHECK_EQ('/', suffix[0]);
phajdan.jr@chromium.orgecf50752009-01-14 03:57:46 +0900272 suffix.erase(0, 1);
273 }
274 const FilePath target_path = to_path.Append(suffix);
evanm@google.com5c1d39b2008-09-19 04:15:54 +0900275
thestig@chromium.org3217c822009-08-07 06:23:07 +0900276 if (S_ISDIR(info.stat.st_mode)) {
277 if (mkdir(target_path.value().c_str(), info.stat.st_mode & 01777) != 0 &&
278 errno != EEXIST) {
279 LOG(ERROR) << "CopyDirectory() couldn't create directory: " <<
280 target_path.value() << " errno = " << errno;
281 success = false;
282 }
283 } else if (S_ISREG(info.stat.st_mode)) {
284 if (!CopyFile(current, target_path)) {
285 LOG(ERROR) << "CopyDirectory() couldn't create file: " <<
286 target_path.value();
287 success = false;
288 }
289 } else {
290 LOG(WARNING) << "CopyDirectory() skipping non-regular file: " <<
291 current.value();
evanm@google.com5c1d39b2008-09-19 04:15:54 +0900292 }
evanm@google.com5c1d39b2008-09-19 04:15:54 +0900293
thestig@chromium.org3217c822009-08-07 06:23:07 +0900294 current = traversal.Next();
295 traversal.GetFindInfo(&info);
evanm@google.com5c1d39b2008-09-19 04:15:54 +0900296 }
297
thestig@chromium.org3217c822009-08-07 06:23:07 +0900298 return success;
mmentovai@google.comaa13be62008-09-03 03:20:34 +0900299}
300
evanm@google.com874d1672008-10-31 08:54:04 +0900301bool PathExists(const FilePath& path) {
benl@chromium.org6b6b2162009-09-08 01:39:46 +0900302 stat_wrapper_t file_info;
303 return CallStat(path.value().c_str(), &file_info) == 0;
mmentovai@google.comaa13be62008-09-03 03:20:34 +0900304}
305
erikkay@google.comcce83822008-12-24 05:20:10 +0900306bool PathIsWritable(const FilePath& path) {
307 FilePath test_path(path);
benl@chromium.org6b6b2162009-09-08 01:39:46 +0900308 stat_wrapper_t file_info;
309 if (CallStat(test_path.value().c_str(), &file_info) != 0) {
erikkay@google.comcce83822008-12-24 05:20:10 +0900310 // If the path doesn't exist, test the parent dir.
311 test_path = test_path.DirName();
312 // If the parent dir doesn't exist, then return false (the path is not
313 // directly writable).
benl@chromium.org6b6b2162009-09-08 01:39:46 +0900314 if (CallStat(test_path.value().c_str(), &file_info) != 0)
erikkay@google.comcce83822008-12-24 05:20:10 +0900315 return false;
316 }
317 if (S_IWOTH & file_info.st_mode)
318 return true;
319 if (getegid() == file_info.st_gid && (S_IWGRP & file_info.st_mode))
320 return true;
321 if (geteuid() == file_info.st_uid && (S_IWUSR & file_info.st_mode))
322 return true;
323 return false;
324}
325
evanm@google.com874d1672008-10-31 08:54:04 +0900326bool DirectoryExists(const FilePath& path) {
benl@chromium.org6b6b2162009-09-08 01:39:46 +0900327 stat_wrapper_t file_info;
328 if (CallStat(path.value().c_str(), &file_info) == 0)
mmoss@google.com733df6b2008-09-12 01:09:11 +0900329 return S_ISDIR(file_info.st_mode);
330 return false;
331}
332
mmentovai@google.comaa13be62008-09-03 03:20:34 +0900333// TODO(erikkay): implement
334#if 0
335bool GetFileCreationLocalTimeFromHandle(int fd,
336 LPSYSTEMTIME creation_time) {
337 if (!file_handle)
338 return false;
jrg@chromium.orgd505c3a2009-02-04 09:58:39 +0900339
mmentovai@google.comaa13be62008-09-03 03:20:34 +0900340 FILETIME utc_filetime;
341 if (!GetFileTime(file_handle, &utc_filetime, NULL, NULL))
342 return false;
jrg@chromium.orgd505c3a2009-02-04 09:58:39 +0900343
mmentovai@google.comaa13be62008-09-03 03:20:34 +0900344 FILETIME local_filetime;
345 if (!FileTimeToLocalFileTime(&utc_filetime, &local_filetime))
346 return false;
jrg@chromium.orgd505c3a2009-02-04 09:58:39 +0900347
mmentovai@google.comaa13be62008-09-03 03:20:34 +0900348 return !!FileTimeToSystemTime(&local_filetime, creation_time);
349}
350
351bool GetFileCreationLocalTime(const std::string& filename,
352 LPSYSTEMTIME creation_time) {
353 ScopedHandle file_handle(
jrg@chromium.orgd505c3a2009-02-04 09:58:39 +0900354 CreateFile(filename.c_str(), GENERIC_READ,
mmentovai@google.comaa13be62008-09-03 03:20:34 +0900355 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
356 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL));
357 return GetFileCreationLocalTimeFromHandle(file_handle.Get(), creation_time);
358}
359#endif
360
phajdan.jr@chromium.org23725932009-04-23 21:38:08 +0900361bool ReadFromFD(int fd, char* buffer, size_t bytes) {
362 size_t total_read = 0;
363 while (total_read < bytes) {
agl@chromium.orgd263ad72009-05-02 06:37:31 +0900364 ssize_t bytes_read =
365 HANDLE_EINTR(read(fd, buffer + total_read, bytes - total_read));
366 if (bytes_read <= 0)
phajdan.jr@chromium.org23725932009-04-23 21:38:08 +0900367 break;
agl@chromium.orgd263ad72009-05-02 06:37:31 +0900368 total_read += bytes_read;
phajdan.jr@chromium.org23725932009-04-23 21:38:08 +0900369 }
370 return total_read == bytes;
371}
372
jrg@chromium.orgd505c3a2009-02-04 09:58:39 +0900373// Creates and opens a temporary file in |directory|, returning the
erikkay@chromium.org3a9a6422009-09-12 02:33:50 +0900374// file descriptor. |path| is set to the temporary file path.
375// This function does NOT unlink() the file.
jrg@chromium.orgd505c3a2009-02-04 09:58:39 +0900376int CreateAndOpenFdForTemporaryFile(FilePath directory, FilePath* path) {
377 *path = directory.Append(kTempFileName);
378 const std::string& tmpdir_string = path->value();
mark@chromium.org8ca0d272008-09-12 02:36:23 +0900379 // this should be OK since mkstemp just replaces characters in place
380 char* buffer = const_cast<char*>(tmpdir_string.c_str());
estade@chromium.orgf474a1b2008-11-11 09:01:38 +0900381
jrg@chromium.orgd505c3a2009-02-04 09:58:39 +0900382 return mkstemp(buffer);
383}
384
erikkay@chromium.org18f0dde2009-08-19 01:07:55 +0900385bool CreateTemporaryFile(FilePath* path) {
jrg@chromium.orgd505c3a2009-02-04 09:58:39 +0900386 FilePath directory;
387 if (!GetTempDir(&directory))
388 return false;
389 int fd = CreateAndOpenFdForTemporaryFile(directory, path);
mmentovai@google.comaa13be62008-09-03 03:20:34 +0900390 if (fd < 0)
391 return false;
estade@chromium.orgf474a1b2008-11-11 09:01:38 +0900392 close(fd);
mmentovai@google.comaa13be62008-09-03 03:20:34 +0900393 return true;
394}
395
jrg@chromium.orgd505c3a2009-02-04 09:58:39 +0900396FILE* CreateAndOpenTemporaryShmemFile(FilePath* path) {
397 FilePath directory;
398 if (!GetShmemTempDir(&directory))
399 return false;
400
phajdan.jr@chromium.org8139fe12009-04-28 15:50:36 +0900401 return CreateAndOpenTemporaryFileInDir(directory, path);
402}
403
404FILE* CreateAndOpenTemporaryFileInDir(const FilePath& dir, FilePath* path) {
405 int fd = CreateAndOpenFdForTemporaryFile(dir, path);
jrg@chromium.orgd505c3a2009-02-04 09:58:39 +0900406 if (fd < 0)
407 return NULL;
408
phajdan.jr@chromium.org8139fe12009-04-28 15:50:36 +0900409 return fdopen(fd, "a+");
jrg@chromium.orgd505c3a2009-02-04 09:58:39 +0900410}
dumi@chromium.org13e715d2009-09-12 05:06:27 +0900411
412bool CreateTemporaryFileInDir(const FilePath& dir, FilePath* temp_file) {
413 int fd = CreateAndOpenFdForTemporaryFile(dir, temp_file);
414 return ((fd >= 0) && !close(fd));
jcampan@chromium.orgbf29e602008-10-11 03:50:32 +0900415}
416
erikkay@google.comcce83822008-12-24 05:20:10 +0900417bool CreateNewTempDirectory(const FilePath::StringType& prefix,
418 FilePath* new_temp_path) {
estade@chromium.orgf474a1b2008-11-11 09:01:38 +0900419 FilePath tmpdir;
mmentovai@google.comaa13be62008-09-03 03:20:34 +0900420 if (!GetTempDir(&tmpdir))
421 return false;
estade@chromium.orgf474a1b2008-11-11 09:01:38 +0900422 tmpdir = tmpdir.Append(kTempFileName);
423 std::string tmpdir_string = tmpdir.value();
mmentovai@google.comaa13be62008-09-03 03:20:34 +0900424 // this should be OK since mkdtemp just replaces characters in place
mark@chromium.org8ca0d272008-09-12 02:36:23 +0900425 char* buffer = const_cast<char*>(tmpdir_string.c_str());
mmentovai@google.comaa13be62008-09-03 03:20:34 +0900426 char* dtemp = mkdtemp(buffer);
427 if (!dtemp)
428 return false;
erikkay@google.comcce83822008-12-24 05:20:10 +0900429 *new_temp_path = FilePath(dtemp);
mmentovai@google.comaa13be62008-09-03 03:20:34 +0900430 return true;
431}
432
evanm@google.com874d1672008-10-31 08:54:04 +0900433bool CreateDirectory(const FilePath& full_path) {
434 std::vector<FilePath> subpaths;
435
436 // Collect a list of all parent directories.
437 FilePath last_path = full_path;
438 subpaths.push_back(full_path);
439 for (FilePath path = full_path.DirName();
440 path.value() != last_path.value(); path = path.DirName()) {
441 subpaths.push_back(path);
442 last_path = path;
443 }
444
445 // Iterate through the parents and create the missing ones.
446 for (std::vector<FilePath>::reverse_iterator i = subpaths.rbegin();
447 i != subpaths.rend(); ++i) {
448 if (!DirectoryExists(*i)) {
evan@chromium.orgdff7b7f2009-05-13 05:12:24 +0900449 if (mkdir(i->value().c_str(), 0700) != 0)
mmentovai@google.comaa13be62008-09-03 03:20:34 +0900450 return false;
451 }
452 }
453 return true;
454}
455
dkegel@google.com44982682008-11-05 06:00:46 +0900456bool GetFileInfo(const FilePath& file_path, FileInfo* results) {
benl@chromium.org6b6b2162009-09-08 01:39:46 +0900457 stat_wrapper_t file_info;
458 if (CallStat(file_path.value().c_str(), &file_info) != 0)
mmentovai@google.comaa13be62008-09-03 03:20:34 +0900459 return false;
darin@google.com7f479f22008-09-26 10:04:08 +0900460 results->is_directory = S_ISDIR(file_info.st_mode);
461 results->size = file_info.st_size;
brettw@chromium.orgdbc9b5a2009-07-25 01:13:53 +0900462 results->last_modified = base::Time::FromTimeT(file_info.st_mtime);
mmentovai@google.comaa13be62008-09-03 03:20:34 +0900463 return true;
464}
465
phajdan.jr@chromium.org99aec932009-05-15 02:49:23 +0900466bool GetInode(const FilePath& path, ino_t* inode) {
467 struct stat buffer;
468 int result = stat(path.value().c_str(), &buffer);
469 if (result < 0)
470 return false;
471
472 *inode = buffer.st_ino;
473 return true;
474}
475
mark@chromium.orgd1bafc62008-10-02 02:40:13 +0900476FILE* OpenFile(const std::string& filename, const char* mode) {
estade@chromium.orgb1d358a2008-11-18 06:01:19 +0900477 return OpenFile(FilePath(filename), mode);
mark@chromium.orgd1bafc62008-10-02 02:40:13 +0900478}
479
estade@chromium.orgb1d358a2008-11-18 06:01:19 +0900480FILE* OpenFile(const FilePath& filename, const char* mode) {
481 return fopen(filename.value().c_str(), mode);
mark@chromium.orgd1bafc62008-10-02 02:40:13 +0900482}
483
estade@chromium.org9d32ed82009-01-28 14:47:15 +0900484int ReadFile(const FilePath& filename, char* data, int size) {
485 int fd = open(filename.value().c_str(), O_RDONLY);
mmentovai@google.comaa13be62008-09-03 03:20:34 +0900486 if (fd < 0)
487 return -1;
estade@chromium.orgb1d358a2008-11-18 06:01:19 +0900488
agl@chromium.orgd263ad72009-05-02 06:37:31 +0900489 int ret_value = HANDLE_EINTR(read(fd, data, size));
490 HANDLE_EINTR(close(fd));
mmentovai@google.comaa13be62008-09-03 03:20:34 +0900491 return ret_value;
492}
493
estade@chromium.org9d32ed82009-01-28 14:47:15 +0900494int WriteFile(const FilePath& filename, const char* data, int size) {
495 int fd = creat(filename.value().c_str(), 0666);
mmentovai@google.comaa13be62008-09-03 03:20:34 +0900496 if (fd < 0)
497 return -1;
mark@chromium.org8ca0d272008-09-12 02:36:23 +0900498
estade@chromium.org557da512009-09-16 09:29:22 +0900499 int rv = WriteFileDescriptor(fd, data, size);
agl@chromium.orgd263ad72009-05-02 06:37:31 +0900500 HANDLE_EINTR(close(fd));
estade@chromium.org557da512009-09-16 09:29:22 +0900501 return rv;
502}
503
504int WriteFileDescriptor(const int fd, const char* data, int size) {
505 // Allow for partial writes.
506 ssize_t bytes_written_total = 0;
507 for (ssize_t bytes_written_partial = 0; bytes_written_total < size;
508 bytes_written_total += bytes_written_partial) {
509 bytes_written_partial =
510 HANDLE_EINTR(write(fd, data + bytes_written_total,
511 size - bytes_written_total));
512 if (bytes_written_partial < 0)
513 return -1;
514 }
515
mark@chromium.org8ca0d272008-09-12 02:36:23 +0900516 return bytes_written_total;
mmentovai@google.comaa13be62008-09-03 03:20:34 +0900517}
518
519// Gets the current working directory for the process.
evanm@google.com874d1672008-10-31 08:54:04 +0900520bool GetCurrentDirectory(FilePath* dir) {
mmentovai@google.comaa13be62008-09-03 03:20:34 +0900521 char system_buffer[PATH_MAX] = "";
evanm@google.com874d1672008-10-31 08:54:04 +0900522 if (!getcwd(system_buffer, sizeof(system_buffer))) {
523 NOTREACHED();
524 return false;
525 }
526 *dir = FilePath(system_buffer);
mmentovai@google.comaa13be62008-09-03 03:20:34 +0900527 return true;
528}
529
530// Sets the current working directory for the process.
estade@chromium.orgb1d358a2008-11-18 06:01:19 +0900531bool SetCurrentDirectory(const FilePath& path) {
532 int ret = chdir(path.value().c_str());
533 return !ret;
mmentovai@google.comaa13be62008-09-03 03:20:34 +0900534}
estade@chromium.orgb1d358a2008-11-18 06:01:19 +0900535
estade@chromium.org2c233532008-12-13 08:43:03 +0900536///////////////////////////////////////////////
537// FileEnumerator
538
avi@google.com5cb79352008-12-11 23:55:12 +0900539FileEnumerator::FileEnumerator(const FilePath& root_path,
mmentovai@google.comaa13be62008-09-03 03:20:34 +0900540 bool recursive,
541 FileEnumerator::FILE_TYPE file_type)
thestig@chromium.org3217c822009-08-07 06:23:07 +0900542 : root_path_(root_path),
543 recursive_(recursive),
mmentovai@google.comaa13be62008-09-03 03:20:34 +0900544 file_type_(file_type),
545 is_in_find_op_(false),
thestig@chromium.org3217c822009-08-07 06:23:07 +0900546 current_directory_entry_(0) {
yuzo@chromium.org2da0f822009-06-09 14:57:38 +0900547 // INCLUDE_DOT_DOT must not be specified if recursive.
548 DCHECK(!(recursive && (INCLUDE_DOT_DOT & file_type_)));
mmentovai@google.comaa13be62008-09-03 03:20:34 +0900549 pending_paths_.push(root_path);
550}
551
avi@google.com5cb79352008-12-11 23:55:12 +0900552FileEnumerator::FileEnumerator(const FilePath& root_path,
mmentovai@google.comaa13be62008-09-03 03:20:34 +0900553 bool recursive,
554 FileEnumerator::FILE_TYPE file_type,
avi@google.com5cb79352008-12-11 23:55:12 +0900555 const FilePath::StringType& pattern)
thestig@chromium.org3217c822009-08-07 06:23:07 +0900556 : root_path_(root_path),
557 recursive_(recursive),
mmentovai@google.comaa13be62008-09-03 03:20:34 +0900558 file_type_(file_type),
thestig@chromium.org3217c822009-08-07 06:23:07 +0900559 pattern_(root_path.Append(pattern)),
mmentovai@google.comaa13be62008-09-03 03:20:34 +0900560 is_in_find_op_(false),
thestig@chromium.org3217c822009-08-07 06:23:07 +0900561 current_directory_entry_(0) {
yuzo@chromium.org2da0f822009-06-09 14:57:38 +0900562 // INCLUDE_DOT_DOT must not be specified if recursive.
563 DCHECK(!(recursive && (INCLUDE_DOT_DOT & file_type_)));
thestig@chromium.org3217c822009-08-07 06:23:07 +0900564 // The Windows version of this code appends the pattern to the root_path,
565 // potentially only matching against items in the top-most directory.
566 // Do the same here.
567 if (pattern.size() == 0)
568 pattern_ = FilePath();
mmentovai@google.comaa13be62008-09-03 03:20:34 +0900569 pending_paths_.push(root_path);
570}
estade@chromium.orgb1d358a2008-11-18 06:01:19 +0900571
mmentovai@google.comaa13be62008-09-03 03:20:34 +0900572FileEnumerator::~FileEnumerator() {
mmentovai@google.comaa13be62008-09-03 03:20:34 +0900573}
574
estade@chromium.orgb23e8e62008-11-21 07:30:13 +0900575void FileEnumerator::GetFindInfo(FindInfo* info) {
576 DCHECK(info);
577
thestig@chromium.org3217c822009-08-07 06:23:07 +0900578 if (current_directory_entry_ >= directory_entries_.size())
estade@chromium.orgb23e8e62008-11-21 07:30:13 +0900579 return;
580
thestig@chromium.org3217c822009-08-07 06:23:07 +0900581 DirectoryEntryInfo* cur_entry = &directory_entries_[current_directory_entry_];
582 memcpy(&(info->stat), &(cur_entry->stat), sizeof(info->stat));
583 info->filename.assign(cur_entry->filename.value());
estade@chromium.orgb23e8e62008-11-21 07:30:13 +0900584}
585
avi@google.com5cb79352008-12-11 23:55:12 +0900586FilePath FileEnumerator::Next() {
thestig@chromium.org3217c822009-08-07 06:23:07 +0900587 ++current_directory_entry_;
588
589 // While we've exhausted the entries in the current directory, do the next
590 while (current_directory_entry_ >= directory_entries_.size()) {
mmentovai@google.comaa13be62008-09-03 03:20:34 +0900591 if (pending_paths_.empty())
avi@google.com5cb79352008-12-11 23:55:12 +0900592 return FilePath();
estade@chromium.orgb23e8e62008-11-21 07:30:13 +0900593
mmentovai@google.comaa13be62008-09-03 03:20:34 +0900594 root_path_ = pending_paths_.top();
avi@google.com5cb79352008-12-11 23:55:12 +0900595 root_path_ = root_path_.StripTrailingSeparators();
mmentovai@google.comaa13be62008-09-03 03:20:34 +0900596 pending_paths_.pop();
estade@chromium.orgb23e8e62008-11-21 07:30:13 +0900597
thestig@chromium.org3217c822009-08-07 06:23:07 +0900598 std::vector<DirectoryEntryInfo> entries;
599 if (!ReadDirectory(&entries, root_path_, file_type_ & SHOW_SYM_LINKS))
600 continue;
estade@chromium.orgb23e8e62008-11-21 07:30:13 +0900601
thestig@chromium.org3217c822009-08-07 06:23:07 +0900602 directory_entries_.clear();
603 current_directory_entry_ = 0;
604 for (std::vector<DirectoryEntryInfo>::const_iterator
605 i = entries.begin(); i != entries.end(); ++i) {
606 FilePath full_path = root_path_.Append(i->filename);
607 if (ShouldSkip(full_path))
608 continue;
estade@chromium.orgb23e8e62008-11-21 07:30:13 +0900609
thestig@chromium.org3217c822009-08-07 06:23:07 +0900610 if (pattern_.value().size() &&
611 fnmatch(pattern_.value().c_str(), full_path.value().c_str(),
612 FNM_NOESCAPE))
613 continue;
614
615 if (recursive_ && S_ISDIR(i->stat.st_mode))
616 pending_paths_.push(full_path);
617
618 if ((S_ISDIR(i->stat.st_mode) && (file_type_ & DIRECTORIES)) ||
619 (!S_ISDIR(i->stat.st_mode) && (file_type_ & FILES)))
620 directory_entries_.push_back(*i);
mmentovai@google.comaa13be62008-09-03 03:20:34 +0900621 }
622 }
estade@chromium.orgb23e8e62008-11-21 07:30:13 +0900623
thestig@chromium.org3217c822009-08-07 06:23:07 +0900624 return root_path_.Append(directory_entries_[current_directory_entry_
625 ].filename);
626}
yuzo@chromium.org2da0f822009-06-09 14:57:38 +0900627
thestig@chromium.org3217c822009-08-07 06:23:07 +0900628bool FileEnumerator::ReadDirectory(std::vector<DirectoryEntryInfo>* entries,
629 const FilePath& source, bool show_links) {
630 DIR* dir = opendir(source.value().c_str());
631 if (!dir)
632 return false;
633
benl@chromium.org6b6b2162009-09-08 01:39:46 +0900634#if !defined(OS_LINUX) && !defined(OS_MACOSX) && !defined(OS_FREEBSD)
thestig@chromium.org3217c822009-08-07 06:23:07 +0900635 #error Depending on the definition of struct dirent, additional space for \
636 pathname may be needed
637#endif
638 struct dirent dent_buf;
639 struct dirent* dent;
640 while (readdir_r(dir, &dent_buf, &dent) == 0 && dent) {
641 DirectoryEntryInfo info;
thestig@chromium.org3217c822009-08-07 06:23:07 +0900642 info.filename = FilePath(dent->d_name);
evan@chromium.org1c798372009-09-04 07:39:34 +0900643
644 FilePath full_name = source.Append(dent->d_name);
645 int ret;
thestig@chromium.org3217c822009-08-07 06:23:07 +0900646 if (show_links)
evan@chromium.org1c798372009-09-04 07:39:34 +0900647 ret = lstat(full_name.value().c_str(), &info.stat);
thestig@chromium.org3217c822009-08-07 06:23:07 +0900648 else
evan@chromium.org1c798372009-09-04 07:39:34 +0900649 ret = stat(full_name.value().c_str(), &info.stat);
650 if (ret < 0) {
651 // Print the stat() error message unless it was ENOENT and we're
652 // following symlinks.
653 if (!(ret == ENOENT && !show_links)) {
tschmelcher@chromium.org90a3f8a2009-10-14 03:27:40 +0900654 PLOG(ERROR) << "Couldn't stat "
655 << source.Append(dent->d_name).value();
evan@chromium.org1c798372009-09-04 07:39:34 +0900656 }
thestig@chromium.org3217c822009-08-07 06:23:07 +0900657 memset(&info.stat, 0, sizeof(info.stat));
yuzo@chromium.org2da0f822009-06-09 14:57:38 +0900658 }
thestig@chromium.org3217c822009-08-07 06:23:07 +0900659 entries->push_back(info);
mmentovai@google.comaa13be62008-09-03 03:20:34 +0900660 }
thestig@chromium.org3217c822009-08-07 06:23:07 +0900661
662 closedir(dir);
663 return true;
664}
665
estade@chromium.org2c233532008-12-13 08:43:03 +0900666///////////////////////////////////////////////
667// MemoryMappedFile
668
669MemoryMappedFile::MemoryMappedFile()
estade@chromium.org2131ee22009-11-06 12:05:46 +0900670 : data_(NULL),
estade@chromium.org2c233532008-12-13 08:43:03 +0900671 length_(0) {
672}
673
estade@chromium.org2131ee22009-11-06 12:05:46 +0900674bool MemoryMappedFile::Initialize(const base::FileDescriptor& fd) {
675 if (IsValid())
676 return false;
agl@chromium.org28a12e72009-06-13 02:36:55 +0900677
estade@chromium.org2131ee22009-11-06 12:05:46 +0900678 file_ = fd;
679
680 if (!MapFileToMemoryInternal()) {
681 CloseHandles();
682 return false;
683 }
684
685 return true;
686}
687
688bool MemoryMappedFile::MapFileToMemory(const FilePath& file_name) {
689 file_ = base::FileDescriptor(open(file_name.value().c_str(), O_RDONLY), true);
690
691 if (file_.fd == -1) {
dkegel@google.com44a17202009-06-07 04:59:36 +0900692 LOG(ERROR) << "Couldn't open " << file_name.value();
estade@chromium.org2c233532008-12-13 08:43:03 +0900693 return false;
dkegel@google.com44a17202009-06-07 04:59:36 +0900694 }
estade@chromium.org2c233532008-12-13 08:43:03 +0900695
estade@chromium.org2131ee22009-11-06 12:05:46 +0900696 return MapFileToMemoryInternal();
697}
698
699bool MemoryMappedFile::MapFileToMemoryInternal() {
estade@chromium.org2c233532008-12-13 08:43:03 +0900700 struct stat file_stat;
estade@chromium.org2131ee22009-11-06 12:05:46 +0900701 if (fstat(file_.fd, &file_stat) == -1) {
702 LOG(ERROR) << "Couldn't fstat " << file_.fd << ", errno " << errno;
estade@chromium.org2c233532008-12-13 08:43:03 +0900703 return false;
dkegel@google.com44a17202009-06-07 04:59:36 +0900704 }
estade@chromium.org2c233532008-12-13 08:43:03 +0900705 length_ = file_stat.st_size;
706
707 data_ = static_cast<uint8*>(
estade@chromium.org2131ee22009-11-06 12:05:46 +0900708 mmap(NULL, length_, PROT_READ, MAP_SHARED, file_.fd, 0));
estade@chromium.org2c233532008-12-13 08:43:03 +0900709 if (data_ == MAP_FAILED)
estade@chromium.org2131ee22009-11-06 12:05:46 +0900710 LOG(ERROR) << "Couldn't mmap " << file_.fd << ", errno " << errno;
dkegel@google.com44a17202009-06-07 04:59:36 +0900711
712 return data_ != MAP_FAILED;
estade@chromium.org2c233532008-12-13 08:43:03 +0900713}
714
715void MemoryMappedFile::CloseHandles() {
716 if (data_ != NULL)
717 munmap(data_, length_);
estade@chromium.org2131ee22009-11-06 12:05:46 +0900718 if (file_.auto_close && file_.fd != -1)
719 close(file_.fd);
estade@chromium.org2c233532008-12-13 08:43:03 +0900720
721 data_ = NULL;
722 length_ = 0;
estade@chromium.org2131ee22009-11-06 12:05:46 +0900723 file_ = base::FileDescriptor();
estade@chromium.org2c233532008-12-13 08:43:03 +0900724}
estade@chromium.orgb23e8e62008-11-21 07:30:13 +0900725
mmentovai@google.comaa13be62008-09-03 03:20:34 +0900726} // namespace file_util