blob: 4d9e797531bdfc7e7e1d34181d3736a87490ae4b [file] [log] [blame]
[email protected]b2e97292008-09-02 18:20:341// 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
[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]4b7743de2009-04-21 01:50:3917#include <sys/types.h>
[email protected]b2e97292008-09-02 18:20:3418#include <time.h>
[email protected]4b7743de2009-04-21 01:50:3919#include <unistd.h>
[email protected]b2e97292008-09-02 18:20:3420
[email protected]3224dcd2009-09-16 17:31:2521#if defined(OS_MACOSX)
22#include <AvailabilityMacros.h>
23#endif
24
[email protected]b2e97292008-09-02 18:20:3425#include <fstream>
26
27#include "base/basictypes.h"
[email protected]157c61b2009-05-01 21:37:3128#include "base/eintr_wrapper.h"
[email protected]640517f2008-10-30 23:54:0429#include "base/file_path.h"
[email protected]807bc042009-07-15 01:32:0230#include "base/lock.h"
[email protected]b2e97292008-09-02 18:20:3431#include "base/logging.h"
[email protected]807bc042009-07-15 01:32:0232#include "base/scoped_ptr.h"
33#include "base/singleton.h"
[email protected]b2e97292008-09-02 18:20:3434#include "base/string_util.h"
[email protected]807bc042009-07-15 01:32:0235#include "base/sys_string_conversions.h"
[email protected]4b7743de2009-04-21 01:50:3936#include "base/time.h"
[email protected]047a03f2009-10-07 02:10:2037#include "base/utf_string_conversions.h"
[email protected]172c5502009-06-24 03:29:2638
[email protected]b2e97292008-09-02 18:20:3439namespace file_util {
40
[email protected]3224dcd2009-09-16 17:31:2541#if defined(OS_FREEBSD) || \
42 (defined(OS_MACOSX) && \
43 MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5)
[email protected]fb66f9d2009-09-07 16:39:4644typedef 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
[email protected]22a087f2009-03-17 19:17:4356#if defined(GOOGLE_CHROME_BUILD)
[email protected]392264c2008-11-11 00:01:3857static const char* kTempFileName = "com.google.chrome.XXXXXX";
[email protected]22a087f2009-03-17 19:17:4358#else
59static const char* kTempFileName = "org.chromium.XXXXXX";
60#endif
[email protected]778e8c52008-09-11 17:36:2361
[email protected]b2e97292008-09-02 18:20:3462std::wstring GetDirectoryFromPath(const std::wstring& path) {
63 if (EndsWithSeparator(path)) {
[email protected]a97f2b42009-04-21 23:15:4564 std::wstring dir = path;
65 TrimTrailingSeparator(&dir);
66 return dir;
[email protected]b2e97292008-09-02 18:20:3467 } 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}
[email protected]9e51af92009-02-04 00:58:3973
[email protected]640517f2008-10-30 23:54:0474bool AbsolutePath(FilePath* path) {
[email protected]b2e97292008-09-02 18:20:3475 char full_path[PATH_MAX];
[email protected]640517f2008-10-30 23:54:0476 if (realpath(path->value().c_str(), full_path) == NULL)
[email protected]b2e97292008-09-02 18:20:3477 return false;
[email protected]640517f2008-10-30 23:54:0478 *path = FilePath(full_path);
[email protected]b2e97292008-09-02 18:20:3479 return true;
80}
81
[email protected]4b7743de2009-04-21 01:50:3982int 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) {
[email protected]fb66f9d2009-09-07 16:39:4688#if !defined(OS_LINUX) && !defined(OS_MACOSX) && !defined(OS_FREEBSD)
[email protected]49930c3a2009-08-06 21:23:0789 #error Depending on the definition of struct dirent, additional space for \
90 pathname may be needed
91#endif
[email protected]2126577b2009-04-23 15:05:1992 struct dirent ent_buf;
[email protected]4b7743de2009-04-21 01:50:3993 struct dirent* ent;
[email protected]2126577b2009-04-23 15:05:1994 while (readdir_r(dir, &ent_buf, &ent) == 0 && ent) {
[email protected]4b7743de2009-04-21 01:50:3995 if ((strcmp(ent->d_name, ".") == 0) ||
96 (strcmp(ent->d_name, "..") == 0))
97 continue;
98
[email protected]fb66f9d2009-09-07 16:39:4699 stat_wrapper_t st;
100 int test = CallStat(path.Append(ent->d_name).value().c_str(), &st);
[email protected]4b7743de2009-04-21 01:50:39101 if (test != 0) {
102 LOG(ERROR) << "stat64 failed: " << strerror(errno);
103 continue;
104 }
[email protected]2126577b2009-04-23 15:05:19105 // 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.
[email protected]4b7743de2009-04-21 01:50:39120 if (st.st_ctime >= comparison_time.ToTimeT())
121 ++file_count;
122 }
123 closedir(dir);
124 }
125 return file_count;
126}
127
[email protected]b2e97292008-09-02 18:20:34128// 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.
[email protected]640517f2008-10-30 23:54:04132bool Delete(const FilePath& path, bool recursive) {
133 const char* path_str = path.value().c_str();
[email protected]fb66f9d2009-09-07 16:39:46134 stat_wrapper_t file_info;
135 int test = CallStat(path_str, &file_info);
[email protected]b2e97292008-09-02 18:20:34136 if (test != 0) {
137 // The Windows version defines this condition as success.
[email protected]9e51af92009-02-04 00:58:39138 bool ret = (errno == ENOENT || errno == ENOTDIR);
[email protected]b2e97292008-09-02 18:20:34139 return ret;
140 }
141 if (!S_ISDIR(file_info.st_mode))
[email protected]640517f2008-10-30 23:54:04142 return (unlink(path_str) == 0);
[email protected]b2e97292008-09-02 18:20:34143 if (!recursive)
[email protected]640517f2008-10-30 23:54:04144 return (rmdir(path_str) == 0);
[email protected]b2e97292008-09-02 18:20:34145
146 bool success = true;
[email protected]49930c3a2009-08-06 21:23:07147 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);
[email protected]21dec3872008-09-18 19:15:54161 }
[email protected]49930c3a2009-08-06 21:23:07162
163 while (success && !directories.empty()) {
164 FilePath dir = FilePath(directories.top());
165 directories.pop();
166 success = (rmdir(dir.value().c_str()) == 0);
[email protected]b2e97292008-09-02 18:20:34167 }
[email protected]49930c3a2009-08-06 21:23:07168
[email protected]b2e97292008-09-02 18:20:34169 return success;
170}
171
[email protected]640517f2008-10-30 23:54:04172bool Move(const FilePath& from_path, const FilePath& to_path) {
[email protected]cc7948a2009-03-13 20:01:43173 if (rename(from_path.value().c_str(), to_path.value().c_str()) == 0)
174 return true;
175
176 if (!CopyDirectory(from_path, to_path, true))
177 return false;
178
179 Delete(from_path, true);
180 return true;
[email protected]b2e97292008-09-02 18:20:34181}
182
[email protected]c5866dca2009-05-19 17:21:07183bool ReplaceFile(const FilePath& from_path, const FilePath& to_path) {
184 return (rename(from_path.value().c_str(), to_path.value().c_str()) == 0);
185}
186
[email protected]640517f2008-10-30 23:54:04187bool CopyDirectory(const FilePath& from_path,
188 const FilePath& to_path,
[email protected]21dec3872008-09-18 19:15:54189 bool recursive) {
[email protected]21dec3872008-09-18 19:15:54190 // Some old callers of CopyDirectory want it to support wildcards.
191 // After some discussion, we decided to fix those callers.
192 // Break loudly here if anyone tries to do this.
193 // TODO(evanm): remove this once we're sure it's ok.
[email protected]640517f2008-10-30 23:54:04194 DCHECK(to_path.value().find('*') == std::string::npos);
195 DCHECK(from_path.value().find('*') == std::string::npos);
[email protected]21dec3872008-09-18 19:15:54196
197 char top_dir[PATH_MAX];
[email protected]640517f2008-10-30 23:54:04198 if (base::strlcpy(top_dir, from_path.value().c_str(),
[email protected]21dec3872008-09-18 19:15:54199 arraysize(top_dir)) >= arraysize(top_dir)) {
200 return false;
201 }
202
[email protected]49930c3a2009-08-06 21:23:07203 // This function does not properly handle destinations within the source
204 FilePath real_to_path = to_path;
205 if (PathExists(real_to_path)) {
206 if (!AbsolutePath(&real_to_path))
207 return false;
208 } else {
209 real_to_path = real_to_path.DirName();
210 if (!AbsolutePath(&real_to_path))
211 return false;
212 }
213 FilePath real_from_path = from_path;
214 if (!AbsolutePath(&real_from_path))
[email protected]21dec3872008-09-18 19:15:54215 return false;
[email protected]49930c3a2009-08-06 21:23:07216 if (real_to_path.value().size() >= real_from_path.value().size() &&
217 real_to_path.value().compare(0, real_from_path.value().size(),
218 real_from_path.value()) == 0)
219 return false;
220
221 bool success = true;
222 FileEnumerator::FILE_TYPE traverse_type =
223 static_cast<FileEnumerator::FILE_TYPE>(FileEnumerator::FILES |
224 FileEnumerator::SHOW_SYM_LINKS);
225 if (recursive)
226 traverse_type = static_cast<FileEnumerator::FILE_TYPE>(
227 traverse_type | FileEnumerator::DIRECTORIES);
228 FileEnumerator traversal(from_path, recursive, traverse_type);
229
[email protected]abbc5732009-10-13 17:57:27230 // We have to mimic windows behavior here. |to_path| may not exist yet,
231 // start the loop with |to_path|. If this is a recursive copy and
232 // the destination already exists, we have to copy the source directory
233 // as well.
[email protected]49930c3a2009-08-06 21:23:07234 FileEnumerator::FindInfo info;
235 FilePath current = from_path;
[email protected]abbc5732009-10-13 17:57:27236 FilePath from_path_base = from_path;
237 if (recursive && stat(to_path.value().c_str(), &info.stat) == 0) {
238 // If the destination already exists, then the top level of source
239 // needs to be copied.
240 from_path_base = from_path.DirName();
241 }
[email protected]49930c3a2009-08-06 21:23:07242 if (stat(from_path.value().c_str(), &info.stat) < 0) {
243 LOG(ERROR) << "CopyDirectory() couldn't stat source directory: " <<
244 from_path.value() << " errno = " << errno;
245 success = false;
[email protected]21dec3872008-09-18 19:15:54246 }
247
[email protected]49930c3a2009-08-06 21:23:07248 while (success && !current.empty()) {
249 // current is the source path, including from_path, so paste
[email protected]21dec3872008-09-18 19:15:54250 // the suffix after from_path onto to_path to create the target_path.
[email protected]abbc5732009-10-13 17:57:27251 std::string suffix(&current.value().c_str()[from_path_base.value().size()]);
[email protected]ca0209612009-01-13 18:57:46252 // Strip the leading '/' (if any).
253 if (!suffix.empty()) {
[email protected]6ae340f2009-03-06 09:56:28254 DCHECK_EQ('/', suffix[0]);
[email protected]ca0209612009-01-13 18:57:46255 suffix.erase(0, 1);
256 }
257 const FilePath target_path = to_path.Append(suffix);
[email protected]21dec3872008-09-18 19:15:54258
[email protected]49930c3a2009-08-06 21:23:07259 if (S_ISDIR(info.stat.st_mode)) {
260 if (mkdir(target_path.value().c_str(), info.stat.st_mode & 01777) != 0 &&
261 errno != EEXIST) {
262 LOG(ERROR) << "CopyDirectory() couldn't create directory: " <<
263 target_path.value() << " errno = " << errno;
264 success = false;
265 }
266 } else if (S_ISREG(info.stat.st_mode)) {
267 if (!CopyFile(current, target_path)) {
268 LOG(ERROR) << "CopyDirectory() couldn't create file: " <<
269 target_path.value();
270 success = false;
271 }
272 } else {
273 LOG(WARNING) << "CopyDirectory() skipping non-regular file: " <<
274 current.value();
[email protected]21dec3872008-09-18 19:15:54275 }
[email protected]21dec3872008-09-18 19:15:54276
[email protected]49930c3a2009-08-06 21:23:07277 current = traversal.Next();
278 traversal.GetFindInfo(&info);
[email protected]21dec3872008-09-18 19:15:54279 }
280
[email protected]49930c3a2009-08-06 21:23:07281 return success;
[email protected]b2e97292008-09-02 18:20:34282}
283
[email protected]640517f2008-10-30 23:54:04284bool PathExists(const FilePath& path) {
[email protected]fb66f9d2009-09-07 16:39:46285 stat_wrapper_t file_info;
286 return CallStat(path.value().c_str(), &file_info) == 0;
[email protected]b2e97292008-09-02 18:20:34287}
288
[email protected]7e1fde6a2008-12-23 20:20:10289bool PathIsWritable(const FilePath& path) {
290 FilePath test_path(path);
[email protected]fb66f9d2009-09-07 16:39:46291 stat_wrapper_t file_info;
292 if (CallStat(test_path.value().c_str(), &file_info) != 0) {
[email protected]7e1fde6a2008-12-23 20:20:10293 // If the path doesn't exist, test the parent dir.
294 test_path = test_path.DirName();
295 // If the parent dir doesn't exist, then return false (the path is not
296 // directly writable).
[email protected]fb66f9d2009-09-07 16:39:46297 if (CallStat(test_path.value().c_str(), &file_info) != 0)
[email protected]7e1fde6a2008-12-23 20:20:10298 return false;
299 }
300 if (S_IWOTH & file_info.st_mode)
301 return true;
302 if (getegid() == file_info.st_gid && (S_IWGRP & file_info.st_mode))
303 return true;
304 if (geteuid() == file_info.st_uid && (S_IWUSR & file_info.st_mode))
305 return true;
306 return false;
307}
308
[email protected]640517f2008-10-30 23:54:04309bool DirectoryExists(const FilePath& path) {
[email protected]fb66f9d2009-09-07 16:39:46310 stat_wrapper_t file_info;
311 if (CallStat(path.value().c_str(), &file_info) == 0)
[email protected]806b9c62008-09-11 16:09:11312 return S_ISDIR(file_info.st_mode);
313 return false;
314}
315
[email protected]b2e97292008-09-02 18:20:34316// TODO(erikkay): implement
317#if 0
318bool GetFileCreationLocalTimeFromHandle(int fd,
319 LPSYSTEMTIME creation_time) {
320 if (!file_handle)
321 return false;
[email protected]9e51af92009-02-04 00:58:39322
[email protected]b2e97292008-09-02 18:20:34323 FILETIME utc_filetime;
324 if (!GetFileTime(file_handle, &utc_filetime, NULL, NULL))
325 return false;
[email protected]9e51af92009-02-04 00:58:39326
[email protected]b2e97292008-09-02 18:20:34327 FILETIME local_filetime;
328 if (!FileTimeToLocalFileTime(&utc_filetime, &local_filetime))
329 return false;
[email protected]9e51af92009-02-04 00:58:39330
[email protected]b2e97292008-09-02 18:20:34331 return !!FileTimeToSystemTime(&local_filetime, creation_time);
332}
333
334bool GetFileCreationLocalTime(const std::string& filename,
335 LPSYSTEMTIME creation_time) {
336 ScopedHandle file_handle(
[email protected]9e51af92009-02-04 00:58:39337 CreateFile(filename.c_str(), GENERIC_READ,
[email protected]b2e97292008-09-02 18:20:34338 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
339 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL));
340 return GetFileCreationLocalTimeFromHandle(file_handle.Get(), creation_time);
341}
342#endif
343
[email protected]45301492009-04-23 12:38:08344bool ReadFromFD(int fd, char* buffer, size_t bytes) {
345 size_t total_read = 0;
346 while (total_read < bytes) {
[email protected]157c61b2009-05-01 21:37:31347 ssize_t bytes_read =
348 HANDLE_EINTR(read(fd, buffer + total_read, bytes - total_read));
349 if (bytes_read <= 0)
[email protected]45301492009-04-23 12:38:08350 break;
[email protected]157c61b2009-05-01 21:37:31351 total_read += bytes_read;
[email protected]45301492009-04-23 12:38:08352 }
353 return total_read == bytes;
354}
355
[email protected]9e51af92009-02-04 00:58:39356// Creates and opens a temporary file in |directory|, returning the
[email protected]16eac0a72009-09-11 17:33:50357// file descriptor. |path| is set to the temporary file path.
358// This function does NOT unlink() the file.
[email protected]9e51af92009-02-04 00:58:39359int CreateAndOpenFdForTemporaryFile(FilePath directory, FilePath* path) {
360 *path = directory.Append(kTempFileName);
361 const std::string& tmpdir_string = path->value();
[email protected]778e8c52008-09-11 17:36:23362 // this should be OK since mkstemp just replaces characters in place
363 char* buffer = const_cast<char*>(tmpdir_string.c_str());
[email protected]392264c2008-11-11 00:01:38364
[email protected]9e51af92009-02-04 00:58:39365 return mkstemp(buffer);
366}
367
[email protected]33edeab2009-08-18 16:07:55368bool CreateTemporaryFile(FilePath* path) {
[email protected]9e51af92009-02-04 00:58:39369 FilePath directory;
370 if (!GetTempDir(&directory))
371 return false;
372 int fd = CreateAndOpenFdForTemporaryFile(directory, path);
[email protected]b2e97292008-09-02 18:20:34373 if (fd < 0)
374 return false;
[email protected]392264c2008-11-11 00:01:38375 close(fd);
[email protected]b2e97292008-09-02 18:20:34376 return true;
377}
378
[email protected]9e51af92009-02-04 00:58:39379FILE* CreateAndOpenTemporaryShmemFile(FilePath* path) {
380 FilePath directory;
381 if (!GetShmemTempDir(&directory))
382 return false;
383
[email protected]6faa0e0d2009-04-28 06:50:36384 return CreateAndOpenTemporaryFileInDir(directory, path);
385}
386
387FILE* CreateAndOpenTemporaryFileInDir(const FilePath& dir, FilePath* path) {
388 int fd = CreateAndOpenFdForTemporaryFile(dir, path);
[email protected]9e51af92009-02-04 00:58:39389 if (fd < 0)
390 return NULL;
391
[email protected]6faa0e0d2009-04-28 06:50:36392 return fdopen(fd, "a+");
[email protected]9e51af92009-02-04 00:58:39393}
[email protected]6445c402009-09-11 20:06:27394
395bool CreateTemporaryFileInDir(const FilePath& dir, FilePath* temp_file) {
396 int fd = CreateAndOpenFdForTemporaryFile(dir, temp_file);
397 return ((fd >= 0) && !close(fd));
[email protected]9ccbb372008-10-10 18:50:32398}
399
[email protected]7e1fde6a2008-12-23 20:20:10400bool CreateNewTempDirectory(const FilePath::StringType& prefix,
401 FilePath* new_temp_path) {
[email protected]392264c2008-11-11 00:01:38402 FilePath tmpdir;
[email protected]b2e97292008-09-02 18:20:34403 if (!GetTempDir(&tmpdir))
404 return false;
[email protected]392264c2008-11-11 00:01:38405 tmpdir = tmpdir.Append(kTempFileName);
406 std::string tmpdir_string = tmpdir.value();
[email protected]b2e97292008-09-02 18:20:34407 // this should be OK since mkdtemp just replaces characters in place
[email protected]778e8c52008-09-11 17:36:23408 char* buffer = const_cast<char*>(tmpdir_string.c_str());
[email protected]b2e97292008-09-02 18:20:34409 char* dtemp = mkdtemp(buffer);
410 if (!dtemp)
411 return false;
[email protected]7e1fde6a2008-12-23 20:20:10412 *new_temp_path = FilePath(dtemp);
[email protected]b2e97292008-09-02 18:20:34413 return true;
414}
415
[email protected]640517f2008-10-30 23:54:04416bool CreateDirectory(const FilePath& full_path) {
417 std::vector<FilePath> subpaths;
418
419 // Collect a list of all parent directories.
420 FilePath last_path = full_path;
421 subpaths.push_back(full_path);
422 for (FilePath path = full_path.DirName();
423 path.value() != last_path.value(); path = path.DirName()) {
424 subpaths.push_back(path);
425 last_path = path;
426 }
427
428 // Iterate through the parents and create the missing ones.
429 for (std::vector<FilePath>::reverse_iterator i = subpaths.rbegin();
430 i != subpaths.rend(); ++i) {
431 if (!DirectoryExists(*i)) {
[email protected]39a1a062009-05-12 20:12:24432 if (mkdir(i->value().c_str(), 0700) != 0)
[email protected]b2e97292008-09-02 18:20:34433 return false;
434 }
435 }
436 return true;
437}
438
[email protected]eac0709a2008-11-04 21:00:46439bool GetFileInfo(const FilePath& file_path, FileInfo* results) {
[email protected]fb66f9d2009-09-07 16:39:46440 stat_wrapper_t file_info;
441 if (CallStat(file_path.value().c_str(), &file_info) != 0)
[email protected]b2e97292008-09-02 18:20:34442 return false;
[email protected]f5e3da4d2008-09-26 01:04:08443 results->is_directory = S_ISDIR(file_info.st_mode);
444 results->size = file_info.st_size;
[email protected]5ef323892009-07-24 16:13:53445 results->last_modified = base::Time::FromTimeT(file_info.st_mtime);
[email protected]b2e97292008-09-02 18:20:34446 return true;
447}
448
[email protected]825003f2009-05-14 17:49:23449bool GetInode(const FilePath& path, ino_t* inode) {
450 struct stat buffer;
451 int result = stat(path.value().c_str(), &buffer);
452 if (result < 0)
453 return false;
454
455 *inode = buffer.st_ino;
456 return true;
457}
458
[email protected]836f1342008-10-01 17:40:13459FILE* OpenFile(const std::string& filename, const char* mode) {
[email protected]a9cd2a652008-11-17 21:01:19460 return OpenFile(FilePath(filename), mode);
[email protected]836f1342008-10-01 17:40:13461}
462
[email protected]a9cd2a652008-11-17 21:01:19463FILE* OpenFile(const FilePath& filename, const char* mode) {
464 return fopen(filename.value().c_str(), mode);
[email protected]836f1342008-10-01 17:40:13465}
466
[email protected]c870c762009-01-28 05:47:15467int ReadFile(const FilePath& filename, char* data, int size) {
468 int fd = open(filename.value().c_str(), O_RDONLY);
[email protected]b2e97292008-09-02 18:20:34469 if (fd < 0)
470 return -1;
[email protected]a9cd2a652008-11-17 21:01:19471
[email protected]157c61b2009-05-01 21:37:31472 int ret_value = HANDLE_EINTR(read(fd, data, size));
473 HANDLE_EINTR(close(fd));
[email protected]b2e97292008-09-02 18:20:34474 return ret_value;
475}
476
[email protected]c870c762009-01-28 05:47:15477int WriteFile(const FilePath& filename, const char* data, int size) {
478 int fd = creat(filename.value().c_str(), 0666);
[email protected]b2e97292008-09-02 18:20:34479 if (fd < 0)
480 return -1;
[email protected]778e8c52008-09-11 17:36:23481
[email protected]fbea0232009-09-16 00:29:22482 int rv = WriteFileDescriptor(fd, data, size);
[email protected]157c61b2009-05-01 21:37:31483 HANDLE_EINTR(close(fd));
[email protected]fbea0232009-09-16 00:29:22484 return rv;
485}
486
487int WriteFileDescriptor(const int fd, const char* data, int size) {
488 // Allow for partial writes.
489 ssize_t bytes_written_total = 0;
490 for (ssize_t bytes_written_partial = 0; bytes_written_total < size;
491 bytes_written_total += bytes_written_partial) {
492 bytes_written_partial =
493 HANDLE_EINTR(write(fd, data + bytes_written_total,
494 size - bytes_written_total));
495 if (bytes_written_partial < 0)
496 return -1;
497 }
498
[email protected]778e8c52008-09-11 17:36:23499 return bytes_written_total;
[email protected]b2e97292008-09-02 18:20:34500}
501
502// Gets the current working directory for the process.
[email protected]640517f2008-10-30 23:54:04503bool GetCurrentDirectory(FilePath* dir) {
[email protected]b2e97292008-09-02 18:20:34504 char system_buffer[PATH_MAX] = "";
[email protected]640517f2008-10-30 23:54:04505 if (!getcwd(system_buffer, sizeof(system_buffer))) {
506 NOTREACHED();
507 return false;
508 }
509 *dir = FilePath(system_buffer);
[email protected]b2e97292008-09-02 18:20:34510 return true;
511}
512
513// Sets the current working directory for the process.
[email protected]a9cd2a652008-11-17 21:01:19514bool SetCurrentDirectory(const FilePath& path) {
515 int ret = chdir(path.value().c_str());
516 return !ret;
[email protected]b2e97292008-09-02 18:20:34517}
[email protected]a9cd2a652008-11-17 21:01:19518
[email protected]7856bb82008-12-12 23:43:03519///////////////////////////////////////////////
520// FileEnumerator
521
[email protected]0b733222008-12-11 14:55:12522FileEnumerator::FileEnumerator(const FilePath& root_path,
[email protected]b2e97292008-09-02 18:20:34523 bool recursive,
524 FileEnumerator::FILE_TYPE file_type)
[email protected]49930c3a2009-08-06 21:23:07525 : root_path_(root_path),
526 recursive_(recursive),
[email protected]b2e97292008-09-02 18:20:34527 file_type_(file_type),
528 is_in_find_op_(false),
[email protected]49930c3a2009-08-06 21:23:07529 current_directory_entry_(0) {
[email protected]8199b3a2009-06-09 05:57:38530 // INCLUDE_DOT_DOT must not be specified if recursive.
531 DCHECK(!(recursive && (INCLUDE_DOT_DOT & file_type_)));
[email protected]b2e97292008-09-02 18:20:34532 pending_paths_.push(root_path);
533}
534
[email protected]0b733222008-12-11 14:55:12535FileEnumerator::FileEnumerator(const FilePath& root_path,
[email protected]b2e97292008-09-02 18:20:34536 bool recursive,
537 FileEnumerator::FILE_TYPE file_type,
[email protected]0b733222008-12-11 14:55:12538 const FilePath::StringType& pattern)
[email protected]49930c3a2009-08-06 21:23:07539 : root_path_(root_path),
540 recursive_(recursive),
[email protected]b2e97292008-09-02 18:20:34541 file_type_(file_type),
[email protected]49930c3a2009-08-06 21:23:07542 pattern_(root_path.Append(pattern)),
[email protected]b2e97292008-09-02 18:20:34543 is_in_find_op_(false),
[email protected]49930c3a2009-08-06 21:23:07544 current_directory_entry_(0) {
[email protected]8199b3a2009-06-09 05:57:38545 // INCLUDE_DOT_DOT must not be specified if recursive.
546 DCHECK(!(recursive && (INCLUDE_DOT_DOT & file_type_)));
[email protected]49930c3a2009-08-06 21:23:07547 // The Windows version of this code appends the pattern to the root_path,
548 // potentially only matching against items in the top-most directory.
549 // Do the same here.
550 if (pattern.size() == 0)
551 pattern_ = FilePath();
[email protected]b2e97292008-09-02 18:20:34552 pending_paths_.push(root_path);
553}
[email protected]a9cd2a652008-11-17 21:01:19554
[email protected]b2e97292008-09-02 18:20:34555FileEnumerator::~FileEnumerator() {
[email protected]b2e97292008-09-02 18:20:34556}
557
[email protected]13ef7c02008-11-20 22:30:13558void FileEnumerator::GetFindInfo(FindInfo* info) {
559 DCHECK(info);
560
[email protected]49930c3a2009-08-06 21:23:07561 if (current_directory_entry_ >= directory_entries_.size())
[email protected]13ef7c02008-11-20 22:30:13562 return;
563
[email protected]49930c3a2009-08-06 21:23:07564 DirectoryEntryInfo* cur_entry = &directory_entries_[current_directory_entry_];
565 memcpy(&(info->stat), &(cur_entry->stat), sizeof(info->stat));
566 info->filename.assign(cur_entry->filename.value());
[email protected]13ef7c02008-11-20 22:30:13567}
568
[email protected]0b733222008-12-11 14:55:12569FilePath FileEnumerator::Next() {
[email protected]49930c3a2009-08-06 21:23:07570 ++current_directory_entry_;
571
572 // While we've exhausted the entries in the current directory, do the next
573 while (current_directory_entry_ >= directory_entries_.size()) {
[email protected]b2e97292008-09-02 18:20:34574 if (pending_paths_.empty())
[email protected]0b733222008-12-11 14:55:12575 return FilePath();
[email protected]13ef7c02008-11-20 22:30:13576
[email protected]b2e97292008-09-02 18:20:34577 root_path_ = pending_paths_.top();
[email protected]0b733222008-12-11 14:55:12578 root_path_ = root_path_.StripTrailingSeparators();
[email protected]b2e97292008-09-02 18:20:34579 pending_paths_.pop();
[email protected]13ef7c02008-11-20 22:30:13580
[email protected]49930c3a2009-08-06 21:23:07581 std::vector<DirectoryEntryInfo> entries;
582 if (!ReadDirectory(&entries, root_path_, file_type_ & SHOW_SYM_LINKS))
583 continue;
[email protected]13ef7c02008-11-20 22:30:13584
[email protected]49930c3a2009-08-06 21:23:07585 directory_entries_.clear();
586 current_directory_entry_ = 0;
587 for (std::vector<DirectoryEntryInfo>::const_iterator
588 i = entries.begin(); i != entries.end(); ++i) {
589 FilePath full_path = root_path_.Append(i->filename);
590 if (ShouldSkip(full_path))
591 continue;
[email protected]13ef7c02008-11-20 22:30:13592
[email protected]49930c3a2009-08-06 21:23:07593 if (pattern_.value().size() &&
594 fnmatch(pattern_.value().c_str(), full_path.value().c_str(),
595 FNM_NOESCAPE))
596 continue;
597
598 if (recursive_ && S_ISDIR(i->stat.st_mode))
599 pending_paths_.push(full_path);
600
601 if ((S_ISDIR(i->stat.st_mode) && (file_type_ & DIRECTORIES)) ||
602 (!S_ISDIR(i->stat.st_mode) && (file_type_ & FILES)))
603 directory_entries_.push_back(*i);
[email protected]b2e97292008-09-02 18:20:34604 }
605 }
[email protected]13ef7c02008-11-20 22:30:13606
[email protected]49930c3a2009-08-06 21:23:07607 return root_path_.Append(directory_entries_[current_directory_entry_
608 ].filename);
609}
[email protected]8199b3a2009-06-09 05:57:38610
[email protected]49930c3a2009-08-06 21:23:07611bool FileEnumerator::ReadDirectory(std::vector<DirectoryEntryInfo>* entries,
612 const FilePath& source, bool show_links) {
613 DIR* dir = opendir(source.value().c_str());
614 if (!dir)
615 return false;
616
[email protected]fb66f9d2009-09-07 16:39:46617#if !defined(OS_LINUX) && !defined(OS_MACOSX) && !defined(OS_FREEBSD)
[email protected]49930c3a2009-08-06 21:23:07618 #error Depending on the definition of struct dirent, additional space for \
619 pathname may be needed
620#endif
621 struct dirent dent_buf;
622 struct dirent* dent;
623 while (readdir_r(dir, &dent_buf, &dent) == 0 && dent) {
624 DirectoryEntryInfo info;
[email protected]49930c3a2009-08-06 21:23:07625 info.filename = FilePath(dent->d_name);
[email protected]2237f5d72009-09-03 22:39:34626
627 FilePath full_name = source.Append(dent->d_name);
628 int ret;
[email protected]49930c3a2009-08-06 21:23:07629 if (show_links)
[email protected]2237f5d72009-09-03 22:39:34630 ret = lstat(full_name.value().c_str(), &info.stat);
[email protected]49930c3a2009-08-06 21:23:07631 else
[email protected]2237f5d72009-09-03 22:39:34632 ret = stat(full_name.value().c_str(), &info.stat);
633 if (ret < 0) {
634 // Print the stat() error message unless it was ENOENT and we're
635 // following symlinks.
636 if (!(ret == ENOENT && !show_links)) {
637 LOG(ERROR) << "Couldn't stat "
638 << source.Append(dent->d_name).value() << ": "
639 << strerror(errno);
640 }
[email protected]49930c3a2009-08-06 21:23:07641 memset(&info.stat, 0, sizeof(info.stat));
[email protected]8199b3a2009-06-09 05:57:38642 }
[email protected]49930c3a2009-08-06 21:23:07643 entries->push_back(info);
[email protected]b2e97292008-09-02 18:20:34644 }
[email protected]49930c3a2009-08-06 21:23:07645
646 closedir(dir);
647 return true;
648}
649
[email protected]7856bb82008-12-12 23:43:03650///////////////////////////////////////////////
651// MemoryMappedFile
652
653MemoryMappedFile::MemoryMappedFile()
654 : file_(-1),
655 data_(NULL),
656 length_(0) {
657}
658
659bool MemoryMappedFile::MapFileToMemory(const FilePath& file_name) {
[email protected]cc8f1462009-06-12 17:36:55660 file_ = open(file_name.value().c_str(), O_RDONLY);
661
[email protected]4883a4e2009-06-06 19:59:36662 if (file_ == -1) {
663 LOG(ERROR) << "Couldn't open " << file_name.value();
[email protected]7856bb82008-12-12 23:43:03664 return false;
[email protected]4883a4e2009-06-06 19:59:36665 }
[email protected]7856bb82008-12-12 23:43:03666
667 struct stat file_stat;
[email protected]4883a4e2009-06-06 19:59:36668 if (fstat(file_, &file_stat) == -1) {
669 LOG(ERROR) << "Couldn't fstat " << file_name.value() << ", errno " << errno;
[email protected]7856bb82008-12-12 23:43:03670 return false;
[email protected]4883a4e2009-06-06 19:59:36671 }
[email protected]7856bb82008-12-12 23:43:03672 length_ = file_stat.st_size;
673
674 data_ = static_cast<uint8*>(
675 mmap(NULL, length_, PROT_READ, MAP_SHARED, file_, 0));
676 if (data_ == MAP_FAILED)
[email protected]4883a4e2009-06-06 19:59:36677 LOG(ERROR) << "Couldn't mmap " << file_name.value() << ", errno " << errno;
678
679 return data_ != MAP_FAILED;
[email protected]7856bb82008-12-12 23:43:03680}
681
682void MemoryMappedFile::CloseHandles() {
683 if (data_ != NULL)
684 munmap(data_, length_);
685 if (file_ != -1)
686 close(file_);
687
688 data_ = NULL;
689 length_ = 0;
690 file_ = -1;
691}
[email protected]13ef7c02008-11-20 22:30:13692
[email protected]b2e97292008-09-02 18:20:34693} // namespace file_util