blob: e71051eca763f5c51890e10cb04a75d037c8d708 [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]e92dffe2010-05-12 21:36:3912#include <limits.h>
[email protected]836f1342008-10-01 17:40:1313#include <stdio.h>
[email protected]1c657852010-04-22 23:28:0514#include <stdlib.h>
[email protected]21dec3872008-09-18 19:15:5415#include <string.h>
[email protected]b2e97292008-09-02 18:20:3416#include <sys/errno.h>
[email protected]7856bb82008-12-12 23:43:0317#include <sys/mman.h>
[email protected]01e2a1f2010-05-12 15:13:5718#include <sys/param.h>
[email protected]b2e97292008-09-02 18:20:3419#include <sys/stat.h>
[email protected]ec3d1452010-02-18 10:02:2620#include <sys/time.h>
[email protected]4b7743de2009-04-21 01:50:3921#include <sys/types.h>
[email protected]b2e97292008-09-02 18:20:3422#include <time.h>
[email protected]4b7743de2009-04-21 01:50:3923#include <unistd.h>
[email protected]b2e97292008-09-02 18:20:3424
[email protected]3224dcd2009-09-16 17:31:2525#if defined(OS_MACOSX)
26#include <AvailabilityMacros.h>
[email protected]1c657852010-04-22 23:28:0527#else
28#include <glib.h>
[email protected]3224dcd2009-09-16 17:31:2529#endif
30
[email protected]b2e97292008-09-02 18:20:3431#include <fstream>
32
33#include "base/basictypes.h"
[email protected]157c61b2009-05-01 21:37:3134#include "base/eintr_wrapper.h"
[email protected]640517f2008-10-30 23:54:0435#include "base/file_path.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]ba74b0d22010-10-23 05:19:2041#include "base/thread_restrictions.h"
[email protected]4b7743de2009-04-21 01:50:3942#include "base/time.h"
[email protected]047a03f2009-10-07 02:10:2043#include "base/utf_string_conversions.h"
[email protected]172c5502009-06-24 03:29:2644
[email protected]b2e97292008-09-02 18:20:3445namespace file_util {
46
[email protected]6f5f4322010-06-09 22:56:4847namespace {
48
49// Helper for NormalizeFilePath(), defined below.
50bool RealPath(const FilePath& path, FilePath* real_path) {
[email protected]ba74b0d22010-10-23 05:19:2051 base::ThreadRestrictions::AssertIOAllowed(); // For realpath().
[email protected]6f5f4322010-06-09 22:56:4852 FilePath::CharType buf[PATH_MAX];
53 if (!realpath(path.value().c_str(), buf))
54 return false;
55
56 *real_path = FilePath(buf);
57 return true;
58}
59
60} // namespace
61
[email protected]66700d42010-03-10 07:46:4362#if defined(OS_OPENBSD) || defined(OS_FREEBSD) || \
[email protected]3224dcd2009-09-16 17:31:2563 (defined(OS_MACOSX) && \
64 MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5)
[email protected]fb66f9d2009-09-07 16:39:4665typedef struct stat stat_wrapper_t;
66static int CallStat(const char *path, stat_wrapper_t *sb) {
[email protected]ba74b0d22010-10-23 05:19:2067 base::ThreadRestrictions::AssertIOAllowed();
[email protected]fb66f9d2009-09-07 16:39:4668 return stat(path, sb);
69}
70#else
71typedef struct stat64 stat_wrapper_t;
72static int CallStat(const char *path, stat_wrapper_t *sb) {
[email protected]ba74b0d22010-10-23 05:19:2073 base::ThreadRestrictions::AssertIOAllowed();
[email protected]fb66f9d2009-09-07 16:39:4674 return stat64(path, sb);
75}
76#endif
77
78
[email protected]22a087f2009-03-17 19:17:4379#if defined(GOOGLE_CHROME_BUILD)
[email protected]390433f2010-06-10 17:11:2980static const char* kTempFileName = ".com.google.chrome.XXXXXX";
[email protected]22a087f2009-03-17 19:17:4381#else
[email protected]390433f2010-06-10 17:11:2982static const char* kTempFileName = ".org.chromium.XXXXXX";
[email protected]22a087f2009-03-17 19:17:4383#endif
[email protected]778e8c52008-09-11 17:36:2384
[email protected]640517f2008-10-30 23:54:0485bool AbsolutePath(FilePath* path) {
[email protected]ba74b0d22010-10-23 05:19:2086 base::ThreadRestrictions::AssertIOAllowed(); // For realpath().
[email protected]b2e97292008-09-02 18:20:3487 char full_path[PATH_MAX];
[email protected]640517f2008-10-30 23:54:0488 if (realpath(path->value().c_str(), full_path) == NULL)
[email protected]b2e97292008-09-02 18:20:3489 return false;
[email protected]640517f2008-10-30 23:54:0490 *path = FilePath(full_path);
[email protected]b2e97292008-09-02 18:20:3491 return true;
92}
93
[email protected]4b7743de2009-04-21 01:50:3994int CountFilesCreatedAfter(const FilePath& path,
95 const base::Time& comparison_time) {
[email protected]ba74b0d22010-10-23 05:19:2096 base::ThreadRestrictions::AssertIOAllowed();
[email protected]4b7743de2009-04-21 01:50:3997 int file_count = 0;
98
99 DIR* dir = opendir(path.value().c_str());
100 if (dir) {
[email protected]8d578822010-01-25 23:54:54101#if !defined(OS_LINUX) && !defined(OS_MACOSX) && !defined(OS_FREEBSD) && \
[email protected]b5c72b822010-02-18 16:11:37102 !defined(OS_OPENBSD) && !defined(OS_SOLARIS)
[email protected]e43eddf12009-12-29 00:32:52103 #error Port warning: depending on the definition of struct dirent, \
104 additional space for pathname may be needed
[email protected]49930c3a2009-08-06 21:23:07105#endif
[email protected]2126577b2009-04-23 15:05:19106 struct dirent ent_buf;
[email protected]4b7743de2009-04-21 01:50:39107 struct dirent* ent;
[email protected]2126577b2009-04-23 15:05:19108 while (readdir_r(dir, &ent_buf, &ent) == 0 && ent) {
[email protected]4b7743de2009-04-21 01:50:39109 if ((strcmp(ent->d_name, ".") == 0) ||
110 (strcmp(ent->d_name, "..") == 0))
111 continue;
112
[email protected]fb66f9d2009-09-07 16:39:46113 stat_wrapper_t st;
114 int test = CallStat(path.Append(ent->d_name).value().c_str(), &st);
[email protected]4b7743de2009-04-21 01:50:39115 if (test != 0) {
[email protected]57b765672009-10-13 18:27:40116 PLOG(ERROR) << "stat64 failed";
[email protected]4b7743de2009-04-21 01:50:39117 continue;
118 }
[email protected]2126577b2009-04-23 15:05:19119 // Here, we use Time::TimeT(), which discards microseconds. This
120 // means that files which are newer than |comparison_time| may
121 // be considered older. If we don't discard microseconds, it
122 // introduces another issue. Suppose the following case:
123 //
124 // 1. Get |comparison_time| by Time::Now() and the value is 10.1 (secs).
125 // 2. Create a file and the current time is 10.3 (secs).
126 //
127 // As POSIX doesn't have microsecond precision for |st_ctime|,
128 // the creation time of the file created in the step 2 is 10 and
129 // the file is considered older than |comparison_time|. After
130 // all, we may have to accept either of the two issues: 1. files
131 // which are older than |comparison_time| are considered newer
132 // (current implementation) 2. files newer than
133 // |comparison_time| are considered older.
[email protected]e9ad4352010-11-17 11:19:15134 if (static_cast<time_t>(st.st_ctime) >= comparison_time.ToTimeT())
[email protected]4b7743de2009-04-21 01:50:39135 ++file_count;
136 }
137 closedir(dir);
138 }
139 return file_count;
140}
141
[email protected]b2e97292008-09-02 18:20:34142// TODO(erikkay): The Windows version of this accepts paths like "foo/bar/*"
143// which works both with and without the recursive flag. I'm not sure we need
144// that functionality. If not, remove from file_util_win.cc, otherwise add it
145// here.
[email protected]640517f2008-10-30 23:54:04146bool Delete(const FilePath& path, bool recursive) {
[email protected]ba74b0d22010-10-23 05:19:20147 base::ThreadRestrictions::AssertIOAllowed();
[email protected]640517f2008-10-30 23:54:04148 const char* path_str = path.value().c_str();
[email protected]fb66f9d2009-09-07 16:39:46149 stat_wrapper_t file_info;
150 int test = CallStat(path_str, &file_info);
[email protected]b2e97292008-09-02 18:20:34151 if (test != 0) {
152 // The Windows version defines this condition as success.
[email protected]9e51af92009-02-04 00:58:39153 bool ret = (errno == ENOENT || errno == ENOTDIR);
[email protected]b2e97292008-09-02 18:20:34154 return ret;
155 }
156 if (!S_ISDIR(file_info.st_mode))
[email protected]640517f2008-10-30 23:54:04157 return (unlink(path_str) == 0);
[email protected]b2e97292008-09-02 18:20:34158 if (!recursive)
[email protected]640517f2008-10-30 23:54:04159 return (rmdir(path_str) == 0);
[email protected]b2e97292008-09-02 18:20:34160
161 bool success = true;
[email protected]49930c3a2009-08-06 21:23:07162 std::stack<std::string> directories;
163 directories.push(path.value());
164 FileEnumerator traversal(path, true, static_cast<FileEnumerator::FILE_TYPE>(
165 FileEnumerator::FILES | FileEnumerator::DIRECTORIES |
166 FileEnumerator::SHOW_SYM_LINKS));
167 for (FilePath current = traversal.Next(); success && !current.empty();
168 current = traversal.Next()) {
169 FileEnumerator::FindInfo info;
170 traversal.GetFindInfo(&info);
171
172 if (S_ISDIR(info.stat.st_mode))
173 directories.push(current.value());
174 else
175 success = (unlink(current.value().c_str()) == 0);
[email protected]21dec3872008-09-18 19:15:54176 }
[email protected]49930c3a2009-08-06 21:23:07177
178 while (success && !directories.empty()) {
179 FilePath dir = FilePath(directories.top());
180 directories.pop();
181 success = (rmdir(dir.value().c_str()) == 0);
[email protected]b2e97292008-09-02 18:20:34182 }
183 return success;
184}
185
[email protected]640517f2008-10-30 23:54:04186bool Move(const FilePath& from_path, const FilePath& to_path) {
[email protected]ba74b0d22010-10-23 05:19:20187 base::ThreadRestrictions::AssertIOAllowed();
[email protected]bc6a9012009-10-15 01:11:44188 // Windows compatibility: if to_path exists, from_path and to_path
189 // must be the same type, either both files, or both directories.
190 stat_wrapper_t to_file_info;
191 if (CallStat(to_path.value().c_str(), &to_file_info) == 0) {
192 stat_wrapper_t from_file_info;
193 if (CallStat(from_path.value().c_str(), &from_file_info) == 0) {
194 if (S_ISDIR(to_file_info.st_mode) != S_ISDIR(from_file_info.st_mode))
195 return false;
196 } else {
197 return false;
198 }
199 }
200
[email protected]cc7948a2009-03-13 20:01:43201 if (rename(from_path.value().c_str(), to_path.value().c_str()) == 0)
202 return true;
203
204 if (!CopyDirectory(from_path, to_path, true))
205 return false;
206
207 Delete(from_path, true);
208 return true;
[email protected]b2e97292008-09-02 18:20:34209}
210
[email protected]c5866dca2009-05-19 17:21:07211bool ReplaceFile(const FilePath& from_path, const FilePath& to_path) {
[email protected]ba74b0d22010-10-23 05:19:20212 base::ThreadRestrictions::AssertIOAllowed();
[email protected]c5866dca2009-05-19 17:21:07213 return (rename(from_path.value().c_str(), to_path.value().c_str()) == 0);
214}
215
[email protected]640517f2008-10-30 23:54:04216bool CopyDirectory(const FilePath& from_path,
217 const FilePath& to_path,
[email protected]21dec3872008-09-18 19:15:54218 bool recursive) {
[email protected]ba74b0d22010-10-23 05:19:20219 base::ThreadRestrictions::AssertIOAllowed();
[email protected]21dec3872008-09-18 19:15:54220 // Some old callers of CopyDirectory want it to support wildcards.
221 // After some discussion, we decided to fix those callers.
222 // Break loudly here if anyone tries to do this.
223 // TODO(evanm): remove this once we're sure it's ok.
[email protected]640517f2008-10-30 23:54:04224 DCHECK(to_path.value().find('*') == std::string::npos);
225 DCHECK(from_path.value().find('*') == std::string::npos);
[email protected]21dec3872008-09-18 19:15:54226
227 char top_dir[PATH_MAX];
[email protected]640517f2008-10-30 23:54:04228 if (base::strlcpy(top_dir, from_path.value().c_str(),
[email protected]21dec3872008-09-18 19:15:54229 arraysize(top_dir)) >= arraysize(top_dir)) {
230 return false;
231 }
232
[email protected]49930c3a2009-08-06 21:23:07233 // This function does not properly handle destinations within the source
234 FilePath real_to_path = to_path;
235 if (PathExists(real_to_path)) {
236 if (!AbsolutePath(&real_to_path))
237 return false;
238 } else {
239 real_to_path = real_to_path.DirName();
240 if (!AbsolutePath(&real_to_path))
241 return false;
242 }
243 FilePath real_from_path = from_path;
244 if (!AbsolutePath(&real_from_path))
[email protected]21dec3872008-09-18 19:15:54245 return false;
[email protected]49930c3a2009-08-06 21:23:07246 if (real_to_path.value().size() >= real_from_path.value().size() &&
247 real_to_path.value().compare(0, real_from_path.value().size(),
248 real_from_path.value()) == 0)
249 return false;
250
251 bool success = true;
252 FileEnumerator::FILE_TYPE traverse_type =
253 static_cast<FileEnumerator::FILE_TYPE>(FileEnumerator::FILES |
254 FileEnumerator::SHOW_SYM_LINKS);
255 if (recursive)
256 traverse_type = static_cast<FileEnumerator::FILE_TYPE>(
257 traverse_type | FileEnumerator::DIRECTORIES);
258 FileEnumerator traversal(from_path, recursive, traverse_type);
259
[email protected]abbc5732009-10-13 17:57:27260 // We have to mimic windows behavior here. |to_path| may not exist yet,
[email protected]bc6a9012009-10-15 01:11:44261 // start the loop with |to_path|.
[email protected]49930c3a2009-08-06 21:23:07262 FileEnumerator::FindInfo info;
263 FilePath current = from_path;
264 if (stat(from_path.value().c_str(), &info.stat) < 0) {
265 LOG(ERROR) << "CopyDirectory() couldn't stat source directory: " <<
266 from_path.value() << " errno = " << errno;
267 success = false;
[email protected]21dec3872008-09-18 19:15:54268 }
[email protected]bc6a9012009-10-15 01:11:44269 struct stat to_path_stat;
270 FilePath from_path_base = from_path;
271 if (recursive && stat(to_path.value().c_str(), &to_path_stat) == 0 &&
272 S_ISDIR(to_path_stat.st_mode)) {
273 // If the destination already exists and is a directory, then the
274 // top level of source needs to be copied.
275 from_path_base = from_path.DirName();
276 }
277
278 // The Windows version of this function assumes that non-recursive calls
279 // will always have a directory for from_path.
280 DCHECK(recursive || S_ISDIR(info.stat.st_mode));
[email protected]21dec3872008-09-18 19:15:54281
[email protected]49930c3a2009-08-06 21:23:07282 while (success && !current.empty()) {
283 // current is the source path, including from_path, so paste
[email protected]21dec3872008-09-18 19:15:54284 // the suffix after from_path onto to_path to create the target_path.
[email protected]abbc5732009-10-13 17:57:27285 std::string suffix(&current.value().c_str()[from_path_base.value().size()]);
[email protected]ca0209612009-01-13 18:57:46286 // Strip the leading '/' (if any).
287 if (!suffix.empty()) {
[email protected]6ae340f2009-03-06 09:56:28288 DCHECK_EQ('/', suffix[0]);
[email protected]ca0209612009-01-13 18:57:46289 suffix.erase(0, 1);
290 }
291 const FilePath target_path = to_path.Append(suffix);
[email protected]21dec3872008-09-18 19:15:54292
[email protected]49930c3a2009-08-06 21:23:07293 if (S_ISDIR(info.stat.st_mode)) {
294 if (mkdir(target_path.value().c_str(), info.stat.st_mode & 01777) != 0 &&
295 errno != EEXIST) {
296 LOG(ERROR) << "CopyDirectory() couldn't create directory: " <<
297 target_path.value() << " errno = " << errno;
298 success = false;
299 }
300 } else if (S_ISREG(info.stat.st_mode)) {
301 if (!CopyFile(current, target_path)) {
302 LOG(ERROR) << "CopyDirectory() couldn't create file: " <<
303 target_path.value();
304 success = false;
305 }
306 } else {
307 LOG(WARNING) << "CopyDirectory() skipping non-regular file: " <<
308 current.value();
[email protected]21dec3872008-09-18 19:15:54309 }
[email protected]21dec3872008-09-18 19:15:54310
[email protected]49930c3a2009-08-06 21:23:07311 current = traversal.Next();
312 traversal.GetFindInfo(&info);
[email protected]21dec3872008-09-18 19:15:54313 }
314
[email protected]49930c3a2009-08-06 21:23:07315 return success;
[email protected]b2e97292008-09-02 18:20:34316}
317
[email protected]640517f2008-10-30 23:54:04318bool PathExists(const FilePath& path) {
[email protected]ba74b0d22010-10-23 05:19:20319 base::ThreadRestrictions::AssertIOAllowed();
[email protected]ef944e052010-05-17 15:01:22320 return access(path.value().c_str(), F_OK) == 0;
[email protected]b2e97292008-09-02 18:20:34321}
322
[email protected]7e1fde6a2008-12-23 20:20:10323bool PathIsWritable(const FilePath& path) {
[email protected]ba74b0d22010-10-23 05:19:20324 base::ThreadRestrictions::AssertIOAllowed();
[email protected]ef944e052010-05-17 15:01:22325 return access(path.value().c_str(), W_OK) == 0;
[email protected]7e1fde6a2008-12-23 20:20:10326}
327
[email protected]640517f2008-10-30 23:54:04328bool DirectoryExists(const FilePath& path) {
[email protected]ba74b0d22010-10-23 05:19:20329 base::ThreadRestrictions::AssertIOAllowed();
[email protected]fb66f9d2009-09-07 16:39:46330 stat_wrapper_t file_info;
331 if (CallStat(path.value().c_str(), &file_info) == 0)
[email protected]806b9c62008-09-11 16:09:11332 return S_ISDIR(file_info.st_mode);
333 return false;
334}
335
[email protected]b2e97292008-09-02 18:20:34336// TODO(erikkay): implement
337#if 0
338bool GetFileCreationLocalTimeFromHandle(int fd,
339 LPSYSTEMTIME creation_time) {
340 if (!file_handle)
341 return false;
[email protected]9e51af92009-02-04 00:58:39342
[email protected]b2e97292008-09-02 18:20:34343 FILETIME utc_filetime;
344 if (!GetFileTime(file_handle, &utc_filetime, NULL, NULL))
345 return false;
[email protected]9e51af92009-02-04 00:58:39346
[email protected]b2e97292008-09-02 18:20:34347 FILETIME local_filetime;
348 if (!FileTimeToLocalFileTime(&utc_filetime, &local_filetime))
349 return false;
[email protected]9e51af92009-02-04 00:58:39350
[email protected]b2e97292008-09-02 18:20:34351 return !!FileTimeToSystemTime(&local_filetime, creation_time);
352}
353
354bool GetFileCreationLocalTime(const std::string& filename,
355 LPSYSTEMTIME creation_time) {
356 ScopedHandle file_handle(
[email protected]9e51af92009-02-04 00:58:39357 CreateFile(filename.c_str(), GENERIC_READ,
[email protected]b2e97292008-09-02 18:20:34358 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
359 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL));
360 return GetFileCreationLocalTimeFromHandle(file_handle.Get(), creation_time);
361}
362#endif
363
[email protected]45301492009-04-23 12:38:08364bool ReadFromFD(int fd, char* buffer, size_t bytes) {
365 size_t total_read = 0;
366 while (total_read < bytes) {
[email protected]157c61b2009-05-01 21:37:31367 ssize_t bytes_read =
368 HANDLE_EINTR(read(fd, buffer + total_read, bytes - total_read));
369 if (bytes_read <= 0)
[email protected]45301492009-04-23 12:38:08370 break;
[email protected]157c61b2009-05-01 21:37:31371 total_read += bytes_read;
[email protected]45301492009-04-23 12:38:08372 }
373 return total_read == bytes;
374}
375
[email protected]2e733d102010-11-30 00:43:37376bool CreateSymbolicLink(const FilePath& target_path,
377 const FilePath& symlink_path) {
378 DCHECK(!symlink_path.empty());
379 DCHECK(!target_path.empty());
380 return ::symlink(target_path.value().c_str(),
381 symlink_path.value().c_str()) != -1;
382}
383
384bool ReadSymbolicLink(const FilePath& symlink_path,
385 FilePath* target_path) {
386 DCHECK(!symlink_path.empty());
387 DCHECK(target_path);
388 char buf[PATH_MAX];
389 ssize_t count = ::readlink(symlink_path.value().c_str(), buf, arraysize(buf));
390
391 if (count <= 0)
392 return false;
393
394 *target_path = FilePath(FilePath::StringType(buf, count));
395
396 return true;
397}
398
[email protected]9e51af92009-02-04 00:58:39399// Creates and opens a temporary file in |directory|, returning the
[email protected]16eac0a72009-09-11 17:33:50400// file descriptor. |path| is set to the temporary file path.
401// This function does NOT unlink() the file.
[email protected]9e51af92009-02-04 00:58:39402int CreateAndOpenFdForTemporaryFile(FilePath directory, FilePath* path) {
[email protected]ba74b0d22010-10-23 05:19:20403 base::ThreadRestrictions::AssertIOAllowed(); // For call to mkstemp().
[email protected]9e51af92009-02-04 00:58:39404 *path = directory.Append(kTempFileName);
405 const std::string& tmpdir_string = path->value();
[email protected]778e8c52008-09-11 17:36:23406 // this should be OK since mkstemp just replaces characters in place
407 char* buffer = const_cast<char*>(tmpdir_string.c_str());
[email protected]392264c2008-11-11 00:01:38408
[email protected]9e51af92009-02-04 00:58:39409 return mkstemp(buffer);
410}
411
[email protected]33edeab2009-08-18 16:07:55412bool CreateTemporaryFile(FilePath* path) {
[email protected]ba74b0d22010-10-23 05:19:20413 base::ThreadRestrictions::AssertIOAllowed(); // For call to close().
[email protected]9e51af92009-02-04 00:58:39414 FilePath directory;
415 if (!GetTempDir(&directory))
416 return false;
417 int fd = CreateAndOpenFdForTemporaryFile(directory, path);
[email protected]b2e97292008-09-02 18:20:34418 if (fd < 0)
419 return false;
[email protected]392264c2008-11-11 00:01:38420 close(fd);
[email protected]b2e97292008-09-02 18:20:34421 return true;
422}
423
[email protected]9e51af92009-02-04 00:58:39424FILE* CreateAndOpenTemporaryShmemFile(FilePath* path) {
425 FilePath directory;
426 if (!GetShmemTempDir(&directory))
[email protected]628476aa2010-06-10 22:56:23427 return NULL;
[email protected]9e51af92009-02-04 00:58:39428
[email protected]6faa0e0d2009-04-28 06:50:36429 return CreateAndOpenTemporaryFileInDir(directory, path);
430}
431
432FILE* CreateAndOpenTemporaryFileInDir(const FilePath& dir, FilePath* path) {
433 int fd = CreateAndOpenFdForTemporaryFile(dir, path);
[email protected]9e51af92009-02-04 00:58:39434 if (fd < 0)
435 return NULL;
436
[email protected]6faa0e0d2009-04-28 06:50:36437 return fdopen(fd, "a+");
[email protected]9e51af92009-02-04 00:58:39438}
[email protected]6445c402009-09-11 20:06:27439
440bool CreateTemporaryFileInDir(const FilePath& dir, FilePath* temp_file) {
[email protected]ba74b0d22010-10-23 05:19:20441 base::ThreadRestrictions::AssertIOAllowed(); // For call to close().
[email protected]6445c402009-09-11 20:06:27442 int fd = CreateAndOpenFdForTemporaryFile(dir, temp_file);
443 return ((fd >= 0) && !close(fd));
[email protected]9ccbb372008-10-10 18:50:32444}
445
[email protected]b0b3abd92010-04-30 17:00:09446static bool CreateTemporaryDirInDirImpl(const FilePath& base_dir,
447 const FilePath::StringType& name_tmpl,
448 FilePath* new_dir) {
[email protected]ba74b0d22010-10-23 05:19:20449 base::ThreadRestrictions::AssertIOAllowed(); // For call to mkdtemp().
[email protected]b0b3abd92010-04-30 17:00:09450 CHECK(name_tmpl.find("XXXXXX") != FilePath::StringType::npos)
451 << "Directory name template must contain \"XXXXXX\".";
452
453 FilePath sub_dir = base_dir.Append(name_tmpl);
454 std::string sub_dir_string = sub_dir.value();
455
456 // this should be OK since mkdtemp just replaces characters in place
457 char* buffer = const_cast<char*>(sub_dir_string.c_str());
458 char* dtemp = mkdtemp(buffer);
[email protected]3ad035d22010-07-28 21:00:51459 if (!dtemp) {
460 DPLOG(ERROR) << "mkdtemp";
[email protected]b0b3abd92010-04-30 17:00:09461 return false;
[email protected]3ad035d22010-07-28 21:00:51462 }
[email protected]b0b3abd92010-04-30 17:00:09463 *new_dir = FilePath(dtemp);
464 return true;
465}
466
467bool CreateTemporaryDirInDir(const FilePath& base_dir,
468 const FilePath::StringType& prefix,
[email protected]046062e82010-06-30 07:19:11469 FilePath* new_dir) {
[email protected]b0b3abd92010-04-30 17:00:09470 FilePath::StringType mkdtemp_template = prefix;
471 mkdtemp_template.append(FILE_PATH_LITERAL("XXXXXX"));
472 return CreateTemporaryDirInDirImpl(base_dir, mkdtemp_template, new_dir);
473}
474
[email protected]7e1fde6a2008-12-23 20:20:10475bool CreateNewTempDirectory(const FilePath::StringType& prefix,
476 FilePath* new_temp_path) {
[email protected]392264c2008-11-11 00:01:38477 FilePath tmpdir;
[email protected]b2e97292008-09-02 18:20:34478 if (!GetTempDir(&tmpdir))
479 return false;
[email protected]b0b3abd92010-04-30 17:00:09480
481 return CreateTemporaryDirInDirImpl(tmpdir, kTempFileName, new_temp_path);
[email protected]b2e97292008-09-02 18:20:34482}
483
[email protected]640517f2008-10-30 23:54:04484bool CreateDirectory(const FilePath& full_path) {
[email protected]ba74b0d22010-10-23 05:19:20485 base::ThreadRestrictions::AssertIOAllowed(); // For call to mkdir().
[email protected]640517f2008-10-30 23:54:04486 std::vector<FilePath> subpaths;
487
488 // Collect a list of all parent directories.
489 FilePath last_path = full_path;
490 subpaths.push_back(full_path);
491 for (FilePath path = full_path.DirName();
492 path.value() != last_path.value(); path = path.DirName()) {
493 subpaths.push_back(path);
494 last_path = path;
495 }
496
497 // Iterate through the parents and create the missing ones.
498 for (std::vector<FilePath>::reverse_iterator i = subpaths.rbegin();
499 i != subpaths.rend(); ++i) {
[email protected]0ba86e42010-03-17 21:39:42500 if (DirectoryExists(*i))
501 continue;
502 if (mkdir(i->value().c_str(), 0700) == 0)
503 continue;
504 // Mkdir failed, but it might have failed with EEXIST, or some other error
505 // due to the the directory appearing out of thin air. This can occur if
506 // two processes are trying to create the same file system tree at the same
507 // time. Check to see if it exists and make sure it is a directory.
508 if (!DirectoryExists(*i))
509 return false;
[email protected]b2e97292008-09-02 18:20:34510 }
511 return true;
512}
513
[email protected]2f0193c22010-09-03 02:28:37514bool GetFileInfo(const FilePath& file_path, base::PlatformFileInfo* results) {
[email protected]fb66f9d2009-09-07 16:39:46515 stat_wrapper_t file_info;
516 if (CallStat(file_path.value().c_str(), &file_info) != 0)
[email protected]b2e97292008-09-02 18:20:34517 return false;
[email protected]f5e3da4d2008-09-26 01:04:08518 results->is_directory = S_ISDIR(file_info.st_mode);
519 results->size = file_info.st_size;
[email protected]5ef323892009-07-24 16:13:53520 results->last_modified = base::Time::FromTimeT(file_info.st_mtime);
[email protected]2f0193c22010-09-03 02:28:37521 results->last_accessed = base::Time::FromTimeT(file_info.st_atime);
522 results->creation_time = base::Time::FromTimeT(file_info.st_ctime);
[email protected]b2e97292008-09-02 18:20:34523 return true;
524}
525
[email protected]825003f2009-05-14 17:49:23526bool GetInode(const FilePath& path, ino_t* inode) {
[email protected]ba74b0d22010-10-23 05:19:20527 base::ThreadRestrictions::AssertIOAllowed(); // For call to stat().
[email protected]825003f2009-05-14 17:49:23528 struct stat buffer;
529 int result = stat(path.value().c_str(), &buffer);
530 if (result < 0)
531 return false;
532
533 *inode = buffer.st_ino;
534 return true;
535}
536
[email protected]836f1342008-10-01 17:40:13537FILE* OpenFile(const std::string& filename, const char* mode) {
[email protected]a9cd2a652008-11-17 21:01:19538 return OpenFile(FilePath(filename), mode);
[email protected]836f1342008-10-01 17:40:13539}
540
[email protected]a9cd2a652008-11-17 21:01:19541FILE* OpenFile(const FilePath& filename, const char* mode) {
[email protected]ba74b0d22010-10-23 05:19:20542 base::ThreadRestrictions::AssertIOAllowed();
[email protected]a9cd2a652008-11-17 21:01:19543 return fopen(filename.value().c_str(), mode);
[email protected]836f1342008-10-01 17:40:13544}
545
[email protected]c870c762009-01-28 05:47:15546int ReadFile(const FilePath& filename, char* data, int size) {
[email protected]ba74b0d22010-10-23 05:19:20547 base::ThreadRestrictions::AssertIOAllowed();
[email protected]c870c762009-01-28 05:47:15548 int fd = open(filename.value().c_str(), O_RDONLY);
[email protected]b2e97292008-09-02 18:20:34549 if (fd < 0)
550 return -1;
[email protected]a9cd2a652008-11-17 21:01:19551
[email protected]cabe39c2010-02-02 02:28:16552 ssize_t bytes_read = HANDLE_EINTR(read(fd, data, size));
553 if (int ret = HANDLE_EINTR(close(fd)) < 0)
554 return ret;
555 return bytes_read;
[email protected]b2e97292008-09-02 18:20:34556}
557
[email protected]c870c762009-01-28 05:47:15558int WriteFile(const FilePath& filename, const char* data, int size) {
[email protected]ba74b0d22010-10-23 05:19:20559 base::ThreadRestrictions::AssertIOAllowed();
[email protected]c870c762009-01-28 05:47:15560 int fd = creat(filename.value().c_str(), 0666);
[email protected]b2e97292008-09-02 18:20:34561 if (fd < 0)
562 return -1;
[email protected]778e8c52008-09-11 17:36:23563
[email protected]cabe39c2010-02-02 02:28:16564 int bytes_written = WriteFileDescriptor(fd, data, size);
565 if (int ret = HANDLE_EINTR(close(fd)) < 0)
566 return ret;
567 return bytes_written;
[email protected]fbea0232009-09-16 00:29:22568}
569
570int WriteFileDescriptor(const int fd, const char* data, int size) {
571 // Allow for partial writes.
572 ssize_t bytes_written_total = 0;
573 for (ssize_t bytes_written_partial = 0; bytes_written_total < size;
574 bytes_written_total += bytes_written_partial) {
575 bytes_written_partial =
576 HANDLE_EINTR(write(fd, data + bytes_written_total,
577 size - bytes_written_total));
578 if (bytes_written_partial < 0)
579 return -1;
580 }
581
[email protected]778e8c52008-09-11 17:36:23582 return bytes_written_total;
[email protected]b2e97292008-09-02 18:20:34583}
584
585// Gets the current working directory for the process.
[email protected]640517f2008-10-30 23:54:04586bool GetCurrentDirectory(FilePath* dir) {
[email protected]ba74b0d22010-10-23 05:19:20587 // getcwd can return ENOENT, which implies it checks against the disk.
588 base::ThreadRestrictions::AssertIOAllowed();
589
[email protected]b2e97292008-09-02 18:20:34590 char system_buffer[PATH_MAX] = "";
[email protected]640517f2008-10-30 23:54:04591 if (!getcwd(system_buffer, sizeof(system_buffer))) {
592 NOTREACHED();
593 return false;
594 }
595 *dir = FilePath(system_buffer);
[email protected]b2e97292008-09-02 18:20:34596 return true;
597}
598
599// Sets the current working directory for the process.
[email protected]a9cd2a652008-11-17 21:01:19600bool SetCurrentDirectory(const FilePath& path) {
[email protected]ba74b0d22010-10-23 05:19:20601 base::ThreadRestrictions::AssertIOAllowed();
[email protected]a9cd2a652008-11-17 21:01:19602 int ret = chdir(path.value().c_str());
603 return !ret;
[email protected]b2e97292008-09-02 18:20:34604}
[email protected]a9cd2a652008-11-17 21:01:19605
[email protected]7856bb82008-12-12 23:43:03606///////////////////////////////////////////////
607// FileEnumerator
608
[email protected]0b733222008-12-11 14:55:12609FileEnumerator::FileEnumerator(const FilePath& root_path,
[email protected]b2e97292008-09-02 18:20:34610 bool recursive,
611 FileEnumerator::FILE_TYPE file_type)
[email protected]fbf745a2009-12-08 22:05:59612 : current_directory_entry_(0),
613 root_path_(root_path),
[email protected]49930c3a2009-08-06 21:23:07614 recursive_(recursive),
[email protected]a82026e2010-10-14 23:43:29615 file_type_(file_type) {
[email protected]8199b3a2009-06-09 05:57:38616 // INCLUDE_DOT_DOT must not be specified if recursive.
617 DCHECK(!(recursive && (INCLUDE_DOT_DOT & file_type_)));
[email protected]b2e97292008-09-02 18:20:34618 pending_paths_.push(root_path);
619}
620
[email protected]0b733222008-12-11 14:55:12621FileEnumerator::FileEnumerator(const FilePath& root_path,
[email protected]b2e97292008-09-02 18:20:34622 bool recursive,
623 FileEnumerator::FILE_TYPE file_type,
[email protected]0b733222008-12-11 14:55:12624 const FilePath::StringType& pattern)
[email protected]fbf745a2009-12-08 22:05:59625 : current_directory_entry_(0),
626 root_path_(root_path),
[email protected]49930c3a2009-08-06 21:23:07627 recursive_(recursive),
[email protected]b2e97292008-09-02 18:20:34628 file_type_(file_type),
[email protected]a82026e2010-10-14 23:43:29629 pattern_(root_path.Append(pattern).value()) {
[email protected]8199b3a2009-06-09 05:57:38630 // INCLUDE_DOT_DOT must not be specified if recursive.
631 DCHECK(!(recursive && (INCLUDE_DOT_DOT & file_type_)));
[email protected]49930c3a2009-08-06 21:23:07632 // The Windows version of this code appends the pattern to the root_path,
633 // potentially only matching against items in the top-most directory.
634 // Do the same here.
635 if (pattern.size() == 0)
[email protected]fbf745a2009-12-08 22:05:59636 pattern_ = FilePath::StringType();
[email protected]b2e97292008-09-02 18:20:34637 pending_paths_.push(root_path);
638}
[email protected]a9cd2a652008-11-17 21:01:19639
[email protected]b2e97292008-09-02 18:20:34640FileEnumerator::~FileEnumerator() {
[email protected]b2e97292008-09-02 18:20:34641}
642
[email protected]13ef7c02008-11-20 22:30:13643void FileEnumerator::GetFindInfo(FindInfo* info) {
644 DCHECK(info);
645
[email protected]49930c3a2009-08-06 21:23:07646 if (current_directory_entry_ >= directory_entries_.size())
[email protected]13ef7c02008-11-20 22:30:13647 return;
648
[email protected]49930c3a2009-08-06 21:23:07649 DirectoryEntryInfo* cur_entry = &directory_entries_[current_directory_entry_];
650 memcpy(&(info->stat), &(cur_entry->stat), sizeof(info->stat));
651 info->filename.assign(cur_entry->filename.value());
[email protected]13ef7c02008-11-20 22:30:13652}
653
[email protected]9fa8c2f52009-12-11 21:12:29654bool FileEnumerator::IsDirectory(const FindInfo& info) {
655 return S_ISDIR(info.stat.st_mode);
656}
657
[email protected]1525c682010-02-11 23:27:47658// static
659FilePath FileEnumerator::GetFilename(const FindInfo& find_info) {
660 return FilePath(find_info.filename);
661}
662
[email protected]0b733222008-12-11 14:55:12663FilePath FileEnumerator::Next() {
[email protected]49930c3a2009-08-06 21:23:07664 ++current_directory_entry_;
665
666 // While we've exhausted the entries in the current directory, do the next
667 while (current_directory_entry_ >= directory_entries_.size()) {
[email protected]b2e97292008-09-02 18:20:34668 if (pending_paths_.empty())
[email protected]0b733222008-12-11 14:55:12669 return FilePath();
[email protected]13ef7c02008-11-20 22:30:13670
[email protected]b2e97292008-09-02 18:20:34671 root_path_ = pending_paths_.top();
[email protected]0b733222008-12-11 14:55:12672 root_path_ = root_path_.StripTrailingSeparators();
[email protected]b2e97292008-09-02 18:20:34673 pending_paths_.pop();
[email protected]13ef7c02008-11-20 22:30:13674
[email protected]49930c3a2009-08-06 21:23:07675 std::vector<DirectoryEntryInfo> entries;
676 if (!ReadDirectory(&entries, root_path_, file_type_ & SHOW_SYM_LINKS))
677 continue;
[email protected]13ef7c02008-11-20 22:30:13678
[email protected]49930c3a2009-08-06 21:23:07679 directory_entries_.clear();
680 current_directory_entry_ = 0;
681 for (std::vector<DirectoryEntryInfo>::const_iterator
682 i = entries.begin(); i != entries.end(); ++i) {
683 FilePath full_path = root_path_.Append(i->filename);
684 if (ShouldSkip(full_path))
685 continue;
[email protected]13ef7c02008-11-20 22:30:13686
[email protected]fbf745a2009-12-08 22:05:59687 if (pattern_.size() &&
688 fnmatch(pattern_.c_str(), full_path.value().c_str(), FNM_NOESCAPE))
[email protected]49930c3a2009-08-06 21:23:07689 continue;
690
691 if (recursive_ && S_ISDIR(i->stat.st_mode))
692 pending_paths_.push(full_path);
693
694 if ((S_ISDIR(i->stat.st_mode) && (file_type_ & DIRECTORIES)) ||
695 (!S_ISDIR(i->stat.st_mode) && (file_type_ & FILES)))
696 directory_entries_.push_back(*i);
[email protected]b2e97292008-09-02 18:20:34697 }
698 }
[email protected]13ef7c02008-11-20 22:30:13699
[email protected]49930c3a2009-08-06 21:23:07700 return root_path_.Append(directory_entries_[current_directory_entry_
701 ].filename);
702}
[email protected]8199b3a2009-06-09 05:57:38703
[email protected]49930c3a2009-08-06 21:23:07704bool FileEnumerator::ReadDirectory(std::vector<DirectoryEntryInfo>* entries,
705 const FilePath& source, bool show_links) {
[email protected]ba74b0d22010-10-23 05:19:20706 base::ThreadRestrictions::AssertIOAllowed();
[email protected]49930c3a2009-08-06 21:23:07707 DIR* dir = opendir(source.value().c_str());
708 if (!dir)
709 return false;
710
[email protected]8d578822010-01-25 23:54:54711#if !defined(OS_LINUX) && !defined(OS_MACOSX) && !defined(OS_FREEBSD) && \
[email protected]b5c72b822010-02-18 16:11:37712 !defined(OS_OPENBSD) && !defined(OS_SOLARIS)
[email protected]e43eddf12009-12-29 00:32:52713 #error Port warning: depending on the definition of struct dirent, \
714 additional space for pathname may be needed
[email protected]49930c3a2009-08-06 21:23:07715#endif
[email protected]e43eddf12009-12-29 00:32:52716
[email protected]49930c3a2009-08-06 21:23:07717 struct dirent dent_buf;
718 struct dirent* dent;
719 while (readdir_r(dir, &dent_buf, &dent) == 0 && dent) {
720 DirectoryEntryInfo info;
[email protected]49930c3a2009-08-06 21:23:07721 info.filename = FilePath(dent->d_name);
[email protected]2237f5d72009-09-03 22:39:34722
723 FilePath full_name = source.Append(dent->d_name);
724 int ret;
[email protected]49930c3a2009-08-06 21:23:07725 if (show_links)
[email protected]2237f5d72009-09-03 22:39:34726 ret = lstat(full_name.value().c_str(), &info.stat);
[email protected]49930c3a2009-08-06 21:23:07727 else
[email protected]2237f5d72009-09-03 22:39:34728 ret = stat(full_name.value().c_str(), &info.stat);
729 if (ret < 0) {
730 // Print the stat() error message unless it was ENOENT and we're
731 // following symlinks.
[email protected]e131cf52010-03-25 19:10:28732 if (!(errno == ENOENT && !show_links)) {
[email protected]57b765672009-10-13 18:27:40733 PLOG(ERROR) << "Couldn't stat "
734 << source.Append(dent->d_name).value();
[email protected]2237f5d72009-09-03 22:39:34735 }
[email protected]49930c3a2009-08-06 21:23:07736 memset(&info.stat, 0, sizeof(info.stat));
[email protected]8199b3a2009-06-09 05:57:38737 }
[email protected]49930c3a2009-08-06 21:23:07738 entries->push_back(info);
[email protected]b2e97292008-09-02 18:20:34739 }
[email protected]49930c3a2009-08-06 21:23:07740
741 closedir(dir);
742 return true;
743}
744
[email protected]7856bb82008-12-12 23:43:03745///////////////////////////////////////////////
746// MemoryMappedFile
747
748MemoryMappedFile::MemoryMappedFile()
[email protected]cb6037d2009-11-16 22:55:17749 : file_(base::kInvalidPlatformFileValue),
750 data_(NULL),
[email protected]7856bb82008-12-12 23:43:03751 length_(0) {
752}
753
[email protected]85c55dc2009-11-06 03:05:46754bool MemoryMappedFile::MapFileToMemoryInternal() {
[email protected]ba74b0d22010-10-23 05:19:20755 base::ThreadRestrictions::AssertIOAllowed();
756
[email protected]7856bb82008-12-12 23:43:03757 struct stat file_stat;
[email protected]cb6037d2009-11-16 22:55:17758 if (fstat(file_, &file_stat) == base::kInvalidPlatformFileValue) {
759 LOG(ERROR) << "Couldn't fstat " << file_ << ", errno " << errno;
[email protected]7856bb82008-12-12 23:43:03760 return false;
[email protected]4883a4e2009-06-06 19:59:36761 }
[email protected]7856bb82008-12-12 23:43:03762 length_ = file_stat.st_size;
763
764 data_ = static_cast<uint8*>(
[email protected]cb6037d2009-11-16 22:55:17765 mmap(NULL, length_, PROT_READ, MAP_SHARED, file_, 0));
[email protected]7856bb82008-12-12 23:43:03766 if (data_ == MAP_FAILED)
[email protected]cb6037d2009-11-16 22:55:17767 LOG(ERROR) << "Couldn't mmap " << file_ << ", errno " << errno;
[email protected]4883a4e2009-06-06 19:59:36768
769 return data_ != MAP_FAILED;
[email protected]7856bb82008-12-12 23:43:03770}
771
772void MemoryMappedFile::CloseHandles() {
[email protected]ba74b0d22010-10-23 05:19:20773 base::ThreadRestrictions::AssertIOAllowed();
774
[email protected]7856bb82008-12-12 23:43:03775 if (data_ != NULL)
776 munmap(data_, length_);
[email protected]cb6037d2009-11-16 22:55:17777 if (file_ != base::kInvalidPlatformFileValue)
778 close(file_);
[email protected]7856bb82008-12-12 23:43:03779
780 data_ = NULL;
781 length_ = 0;
[email protected]cb6037d2009-11-16 22:55:17782 file_ = base::kInvalidPlatformFileValue;
[email protected]7856bb82008-12-12 23:43:03783}
[email protected]13ef7c02008-11-20 22:30:13784
[email protected]35b9be02009-11-26 00:37:06785bool HasFileBeenModifiedSince(const FileEnumerator::FindInfo& find_info,
786 const base::Time& cutoff_time) {
[email protected]e9ad4352010-11-17 11:19:15787 return static_cast<time_t>(find_info.stat.st_mtime) >= cutoff_time.ToTimeT();
[email protected]35b9be02009-11-26 00:37:06788}
789
[email protected]6f5f4322010-06-09 22:56:48790bool NormalizeFilePath(const FilePath& path, FilePath* normalized_path) {
791 FilePath real_path_result;
792 if (!RealPath(path, &real_path_result))
[email protected]01e2a1f2010-05-12 15:13:57793 return false;
794
[email protected]6f5f4322010-06-09 22:56:48795 // To be consistant with windows, fail if |real_path_result| is a
796 // directory.
797 stat_wrapper_t file_info;
798 if (CallStat(real_path_result.value().c_str(), &file_info) != 0 ||
799 S_ISDIR(file_info.st_mode))
800 return false;
801
802 *normalized_path = real_path_result;
[email protected]01e2a1f2010-05-12 15:13:57803 return true;
804}
805
[email protected]84cb1912010-04-21 21:11:36806#if !defined(OS_MACOSX)
807bool GetTempDir(FilePath* path) {
808 const char* tmp = getenv("TMPDIR");
809 if (tmp)
810 *path = FilePath(tmp);
811 else
812 *path = FilePath("/tmp");
813 return true;
814}
815
816bool GetShmemTempDir(FilePath* path) {
817 *path = FilePath("/dev/shm");
818 return true;
819}
820
[email protected]1c657852010-04-22 23:28:05821FilePath GetHomeDir() {
822 const char* home_dir = getenv("HOME");
823 if (home_dir && home_dir[0])
824 return FilePath(home_dir);
825
[email protected]ba74b0d22010-10-23 05:19:20826 // g_get_home_dir calls getpwent, which can fall through to LDAP calls.
827 base::ThreadRestrictions::AssertIOAllowed();
828
[email protected]1c657852010-04-22 23:28:05829 home_dir = g_get_home_dir();
830 if (home_dir && home_dir[0])
831 return FilePath(home_dir);
832
833 FilePath rv;
834 if (file_util::GetTempDir(&rv))
835 return rv;
836
837 // Last resort.
838 return FilePath("/tmp");
839}
840
[email protected]84cb1912010-04-21 21:11:36841bool CopyFile(const FilePath& from_path, const FilePath& to_path) {
[email protected]ba74b0d22010-10-23 05:19:20842 base::ThreadRestrictions::AssertIOAllowed();
[email protected]84cb1912010-04-21 21:11:36843 int infile = open(from_path.value().c_str(), O_RDONLY);
844 if (infile < 0)
845 return false;
846
847 int outfile = creat(to_path.value().c_str(), 0666);
848 if (outfile < 0) {
849 close(infile);
850 return false;
851 }
852
853 const size_t kBufferSize = 32768;
854 std::vector<char> buffer(kBufferSize);
855 bool result = true;
856
857 while (result) {
858 ssize_t bytes_read = HANDLE_EINTR(read(infile, &buffer[0], buffer.size()));
859 if (bytes_read < 0) {
860 result = false;
861 break;
862 }
863 if (bytes_read == 0)
864 break;
865 // Allow for partial writes
866 ssize_t bytes_written_per_read = 0;
867 do {
868 ssize_t bytes_written_partial = HANDLE_EINTR(write(
869 outfile,
870 &buffer[bytes_written_per_read],
871 bytes_read - bytes_written_per_read));
872 if (bytes_written_partial < 0) {
873 result = false;
874 break;
875 }
876 bytes_written_per_read += bytes_written_partial;
877 } while (bytes_written_per_read < bytes_read);
878 }
879
880 if (HANDLE_EINTR(close(infile)) < 0)
881 result = false;
882 if (HANDLE_EINTR(close(outfile)) < 0)
883 result = false;
884
885 return result;
886}
887#endif // defined(OS_MACOSX)
888
[email protected]33e585a2010-11-20 03:38:15889} // namespace file_util