[go: nahoru, domu]

blob: b6cdc98d9a6a680bd6b34d2893fad581e8b47289 [file] [log] [blame]
thestig@chromium.orgb2f28d22012-03-03 01:54:351// Copyright (c) 2012 The Chromium Authors. All rights reserved.
license.botbf09a502008-08-24 00:55:552// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
initial.commitd7cae122008-07-26 21:49:384
5#include "base/file_util.h"
6
jeremy@chromium.orgc2c998c2009-01-27 19:08:397#if defined(OS_WIN)
8#include <io.h>
9#endif
mark@chromium.org836f1342008-10-01 17:40:1310#include <stdio.h>
11
initial.commitd7cae122008-07-26 21:49:3812#include <fstream>
initial.commitd7cae122008-07-26 21:49:3813
brettw@chromium.org57999812013-02-24 05:40:5214#include "base/files/file_path.h"
initial.commitd7cae122008-07-26 21:49:3815#include "base/logging.h"
estade@chromium.orgb9e04f02008-11-27 04:03:5716#include "base/string_piece.h"
brettw@chromium.org047a03f2009-10-07 02:10:2017#include "base/string_util.h"
brettw@chromium.org57999812013-02-24 05:40:5218#include "base/stringprintf.h"
brettw@chromium.org047a03f2009-10-07 02:10:2019#include "base/utf_string_conversions.h"
estade@chromium.orgb9e04f02008-11-27 04:03:5720
brettw@chromium.org04af979a2013-02-16 04:12:2621using base::FilePath;
22
estade@chromium.orgceeb87e2008-12-04 20:46:0623namespace {
24
25const FilePath::CharType kExtensionSeparator = FILE_PATH_LITERAL('.');
jam@chromium.orge285afa2012-01-31 23:16:3926
27// The maximum number of 'uniquified' files we will try to create.
28// This is used when the filename we're trying to download is already in use,
29// so we create a new unique filename by appending " (nnn)" before the
30// extension, where 1 <= nnn <= kMaxUniqueFiles.
31// Also used by code that cleans up said files.
32static const int kMaxUniqueFiles = 100;
estade@chromium.orgceeb87e2008-12-04 20:46:0633
willchan@chromium.orgc145cbdd2009-04-24 17:44:3934} // namespace
estade@chromium.orgceeb87e2008-12-04 20:46:0635
initial.commitd7cae122008-07-26 21:49:3836namespace file_util {
37
aa@chromium.org354002bb2012-02-05 03:22:3838bool g_bug108724_debug = false;
39
estade@chromium.orgb9e04f02008-11-27 04:03:5740bool EndsWithSeparator(const FilePath& path) {
41 FilePath::StringType value = path.value();
42 if (value.empty())
43 return false;
44
45 return FilePath::IsSeparator(value[value.size() - 1]);
estade@chromium.org7e263942008-11-25 22:04:3746}
47
48bool EnsureEndsWithSeparator(FilePath* path) {
49 if (!DirectoryExists(*path))
50 return false;
51
52 if (EndsWithSeparator(*path))
53 return true;
54
55 FilePath::StringType& path_str =
56 const_cast<FilePath::StringType&>(path->value());
57 path_str.append(&FilePath::kSeparators[0], 1);
58
59 return true;
glen@chromium.org52ab8f902008-11-03 16:14:4660}
61
estade@chromium.orgceeb87e2008-12-04 20:46:0662void InsertBeforeExtension(FilePath* path, const FilePath::StringType& suffix) {
63 FilePath::StringType& value =
64 const_cast<FilePath::StringType&>(path->value());
65
66 const FilePath::StringType::size_type last_dot =
67 value.rfind(kExtensionSeparator);
68 const FilePath::StringType::size_type last_separator =
69 value.find_last_of(FilePath::StringType(FilePath::kSeparators));
70
71 if (last_dot == FilePath::StringType::npos ||
72 (last_separator != std::wstring::npos && last_dot < last_separator)) {
73 // The path looks something like "C:\pics.old\jojo" or "C:\pics\jojo".
74 // We should just append the suffix to the entire path.
75 value.append(suffix);
76 return;
77 }
78
79 value.insert(last_dot, suffix);
80}
81
cevans@chromium.org3cd2c1c2013-02-06 20:38:0782bool Move(const FilePath& from_path, const FilePath& to_path) {
83 if (from_path.ReferencesParent() || to_path.ReferencesParent())
84 return false;
85 return MoveUnsafe(from_path, to_path);
86}
87
88bool CopyFile(const FilePath& from_path, const FilePath& to_path) {
89 if (from_path.ReferencesParent() || to_path.ReferencesParent())
90 return false;
91 return CopyFileUnsafe(from_path, to_path);
92}
93
evanm@google.com640517f2008-10-30 23:54:0494bool ContentsEqual(const FilePath& filename1, const FilePath& filename2) {
initial.commitd7cae122008-07-26 21:49:3895 // We open the file in binary format even if they are text files because
96 // we are just comparing that bytes are exactly same in both files and not
97 // doing anything smart with text formatting.
evanm@google.com640517f2008-10-30 23:54:0498 std::ifstream file1(filename1.value().c_str(),
erikkay@google.com5af2edb92008-08-08 20:16:0899 std::ios::in | std::ios::binary);
evanm@google.com640517f2008-10-30 23:54:04100 std::ifstream file2(filename2.value().c_str(),
erikkay@google.com5af2edb92008-08-08 20:16:08101 std::ios::in | std::ios::binary);
estade@chromium.orgb9e04f02008-11-27 04:03:57102
initial.commitd7cae122008-07-26 21:49:38103 // Even if both files aren't openable (and thus, in some sense, "equal"),
104 // any unusable file yields a result of "false".
105 if (!file1.is_open() || !file2.is_open())
106 return false;
107
108 const int BUFFER_SIZE = 2056;
109 char buffer1[BUFFER_SIZE], buffer2[BUFFER_SIZE];
110 do {
111 file1.read(buffer1, BUFFER_SIZE);
112 file2.read(buffer2, BUFFER_SIZE);
113
mark@chromium.orgb81637c32009-06-26 21:17:24114 if ((file1.eof() != file2.eof()) ||
initial.commitd7cae122008-07-26 21:49:38115 (file1.gcount() != file2.gcount()) ||
116 (memcmp(buffer1, buffer2, file1.gcount()))) {
117 file1.close();
118 file2.close();
119 return false;
120 }
mark@chromium.orgb81637c32009-06-26 21:17:24121 } while (!file1.eof() || !file2.eof());
initial.commitd7cae122008-07-26 21:49:38122
123 file1.close();
124 file2.close();
125 return true;
126}
127
mark@chromium.orgb81637c32009-06-26 21:17:24128bool TextContentsEqual(const FilePath& filename1, const FilePath& filename2) {
129 std::ifstream file1(filename1.value().c_str(), std::ios::in);
130 std::ifstream file2(filename2.value().c_str(), std::ios::in);
131
132 // Even if both files aren't openable (and thus, in some sense, "equal"),
133 // any unusable file yields a result of "false".
134 if (!file1.is_open() || !file2.is_open())
135 return false;
136
137 do {
138 std::string line1, line2;
139 getline(file1, line1);
140 getline(file2, line2);
141
142 // Check for mismatched EOF states, or any error state.
143 if ((file1.eof() != file2.eof()) ||
144 file1.bad() || file2.bad()) {
145 return false;
146 }
147
148 // Trim all '\r' and '\n' characters from the end of the line.
149 std::string::size_type end1 = line1.find_last_not_of("\r\n");
150 if (end1 == std::string::npos)
151 line1.clear();
152 else if (end1 + 1 < line1.length())
153 line1.erase(end1 + 1);
154
155 std::string::size_type end2 = line2.find_last_not_of("\r\n");
156 if (end2 == std::string::npos)
157 line2.clear();
158 else if (end2 + 1 < line2.length())
159 line2.erase(end2 + 1);
160
161 if (line1 != line2)
162 return false;
163 } while (!file1.eof() || !file2.eof());
164
165 return true;
166}
167
erikkay@google.com3c5281022009-01-28 00:22:46168bool ReadFileToString(const FilePath& path, std::string* contents) {
cpu@chromium.org9fea5a92013-01-09 00:38:59169 if (path.ReferencesParent())
170 return false;
mark@chromium.org836f1342008-10-01 17:40:13171 FILE* file = OpenFile(path, "rb");
172 if (!file) {
initial.commitd7cae122008-07-26 21:49:38173 return false;
mark@chromium.org836f1342008-10-01 17:40:13174 }
initial.commitd7cae122008-07-26 21:49:38175
176 char buf[1 << 16];
177 size_t len;
178 while ((len = fread(buf, 1, sizeof(buf), file)) > 0) {
evan@chromium.org4e074bae2010-05-19 11:07:55179 if (contents)
180 contents->append(buf, len);
initial.commitd7cae122008-07-26 21:49:38181 }
mark@chromium.org836f1342008-10-01 17:40:13182 CloseFile(file);
initial.commitd7cae122008-07-26 21:49:38183
184 return true;
initial.commitd7cae122008-07-26 21:49:38185}
186
tfarina@chromium.orgb33f1d92010-05-26 01:40:12187bool IsDirectoryEmpty(const FilePath& dir_path) {
188 FileEnumerator files(dir_path, false,
haruki@chromium.org84c3f162012-08-12 01:57:23189 FileEnumerator::FILES | FileEnumerator::DIRECTORIES);
tfarina@chromium.orgb33f1d92010-05-26 01:40:12190 if (files.Next().value().empty())
191 return true;
192 return false;
193}
194
phajdan.jr@chromium.org6faa0e0d2009-04-28 06:50:36195FILE* CreateAndOpenTemporaryFile(FilePath* path) {
196 FilePath directory;
197 if (!GetTempDir(&directory))
evan@chromium.org628476aa2010-06-10 22:56:23198 return NULL;
phajdan.jr@chromium.org6faa0e0d2009-04-28 06:50:36199
200 return CreateAndOpenTemporaryFileInDir(directory, path);
201}
202
dkegel@google.comeac0709a2008-11-04 21:00:46203bool GetFileSize(const FilePath& file_path, int64* file_size) {
dumi@chromium.org2f0193c22010-09-03 02:28:37204 base::PlatformFileInfo info;
darin@google.comf5e3da4d2008-09-26 01:04:08205 if (!GetFileInfo(file_path, &info))
206 return false;
207 *file_size = info.size;
208 return true;
209}
210
estade@chromium.org1525c682010-02-11 23:27:47211bool IsDot(const FilePath& path) {
212 return FILE_PATH_LITERAL(".") == path.BaseName().value();
213}
214
215bool IsDotDot(const FilePath& path) {
216 return FILE_PATH_LITERAL("..") == path.BaseName().value();
217}
218
dumi@chromium.org507fb9a2010-09-23 23:28:22219bool TouchFile(const FilePath& path,
220 const base::Time& last_accessed,
221 const base::Time& last_modified) {
nhiroki@chromium.org307a825a2012-11-01 11:48:52222 int flags = base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_WRITE_ATTRIBUTES;
223
224#if defined(OS_WIN)
225 // On Windows, FILE_FLAG_BACKUP_SEMANTICS is needed to open a directory.
226 if (DirectoryExists(path))
227 flags |= base::PLATFORM_FILE_BACKUP_SEMANTICS;
228#endif // OS_WIN
229
230 const base::PlatformFile file =
231 base::CreatePlatformFile(path, flags, NULL, NULL);
dumi@chromium.org507fb9a2010-09-23 23:28:22232 if (file != base::kInvalidPlatformFileValue) {
233 bool result = base::TouchPlatformFile(file, last_accessed, last_modified);
234 base::ClosePlatformFile(file);
235 return result;
236 }
237
238 return false;
239}
240
241bool SetLastModifiedTime(const FilePath& path,
242 const base::Time& last_modified) {
243 return TouchFile(path, last_modified, last_modified);
244}
245
mark@chromium.org836f1342008-10-01 17:40:13246bool CloseFile(FILE* file) {
sidchat@google.coma1a19502008-10-21 17:14:45247 if (file == NULL)
248 return true;
mark@chromium.org836f1342008-10-01 17:40:13249 return fclose(file) == 0;
jeremy@chromium.org6e01dae2009-01-27 17:13:02250}
251
jeremy@chromium.orgc2c998c2009-01-27 19:08:39252bool TruncateFile(FILE* file) {
253 if (file == NULL)
254 return false;
255 long current_offset = ftell(file);
256 if (current_offset == -1)
257 return false;
258#if defined(OS_WIN)
259 int fd = _fileno(file);
260 if (_chsize(fd, current_offset) != 0)
261 return false;
262#else
263 int fd = fileno(file);
264 if (ftruncate(fd, current_offset) != 0)
265 return false;
266#endif
267 return true;
268}
269
jam@chromium.orge285afa2012-01-31 23:16:39270int GetUniquePathNumber(
271 const FilePath& path,
272 const FilePath::StringType& suffix) {
273 bool have_suffix = !suffix.empty();
274 if (!PathExists(path) &&
275 (!have_suffix || !PathExists(FilePath(path.value() + suffix)))) {
276 return 0;
277 }
278
279 FilePath new_path;
280 for (int count = 1; count <= kMaxUniqueFiles; ++count) {
281 new_path = path.InsertBeforeExtensionASCII(StringPrintf(" (%d)", count));
282 if (!PathExists(new_path) &&
283 (!have_suffix || !PathExists(FilePath(new_path.value() + suffix)))) {
284 return count;
285 }
286 }
287
288 return -1;
289}
290
aa@chromium.orgee5c29da2009-01-09 22:14:27291bool ContainsPath(const FilePath &parent, const FilePath& child) {
292 FilePath abs_parent = FilePath(parent);
293 FilePath abs_child = FilePath(child);
294
295 if (!file_util::AbsolutePath(&abs_parent) ||
296 !file_util::AbsolutePath(&abs_child))
297 return false;
298
299#if defined(OS_WIN)
300 // file_util::AbsolutePath() does not flatten case on Windows, so we must do
301 // a case-insensitive compare.
302 if (!StartsWith(abs_child.value(), abs_parent.value(), false))
303#else
304 if (!StartsWithASCII(abs_child.value(), abs_parent.value(), true))
305#endif
306 return false;
307
308 // file_util::AbsolutePath() normalizes '/' to '\' on Windows, so we only need
309 // to check kSeparators[0].
310 if (abs_child.value().length() <= abs_parent.value().length() ||
311 abs_child.value()[abs_parent.value().length()] !=
312 FilePath::kSeparators[0])
313 return false;
314
315 return true;
316}
317
cpu@chromium.orgc2c132c2010-03-24 21:56:26318int64 ComputeDirectorySize(const FilePath& root_path) {
319 int64 running_size = 0;
320 FileEnumerator file_iter(root_path, true, FileEnumerator::FILES);
321 for (FilePath current = file_iter.Next(); !current.empty();
322 current = file_iter.Next()) {
323 FileEnumerator::FindInfo info;
324 file_iter.GetFindInfo(&info);
325#if defined(OS_WIN)
326 LARGE_INTEGER li = { info.nFileSizeLow, info.nFileSizeHigh };
327 running_size += li.QuadPart;
328#else
329 running_size += info.stat.st_size;
330#endif
331 }
332 return running_size;
333}
334
rvargas@google.coma04876b2010-06-11 22:53:43335int64 ComputeFilesSize(const FilePath& directory,
336 const FilePath::StringType& pattern) {
337 int64 running_size = 0;
338 FileEnumerator file_iter(directory, false, FileEnumerator::FILES, pattern);
339 for (FilePath current = file_iter.Next(); !current.empty();
340 current = file_iter.Next()) {
341 FileEnumerator::FindInfo info;
342 file_iter.GetFindInfo(&info);
343#if defined(OS_WIN)
344 LARGE_INTEGER li = { info.nFileSizeLow, info.nFileSizeHigh };
345 running_size += li.QuadPart;
346#else
347 running_size += info.stat.st_size;
348#endif
349 }
350 return running_size;
351}
352
estade@chromium.org7856bb82008-12-12 23:43:03353///////////////////////////////////////////////
354// MemoryMappedFile
355
356MemoryMappedFile::~MemoryMappedFile() {
357 CloseHandles();
358}
359
erg@google.comeae9c062011-01-11 00:50:59360bool MemoryMappedFile::Initialize(const FilePath& file_name) {
361 if (IsValid())
362 return false;
363
364 if (!MapFileToMemory(file_name)) {
365 CloseHandles();
366 return false;
367 }
368
369 return true;
370}
371
estade@chromium.orgcb6037d2009-11-16 22:55:17372bool MemoryMappedFile::Initialize(base::PlatformFile file) {
373 if (IsValid())
374 return false;
375
376 file_ = file;
377
378 if (!MapFileToMemoryInternal()) {
379 CloseHandles();
380 return false;
381 }
382
383 return true;
384}
385
tommi@chromium.orge318fe52011-02-23 21:42:48386bool MemoryMappedFile::IsValid() const {
erg@google.comeae9c062011-01-11 00:50:59387 return data_ != NULL;
estade@chromium.org4e1c76f2009-11-13 01:36:50388}
389
estade@chromium.orgcb6037d2009-11-16 22:55:17390bool MemoryMappedFile::MapFileToMemory(const FilePath& file_name) {
dumi@chromium.orged65fec2010-08-31 19:30:27391 file_ = base::CreatePlatformFile(
392 file_name, base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_READ,
393 NULL, NULL);
estade@chromium.orgcb6037d2009-11-16 22:55:17394
395 if (file_ == base::kInvalidPlatformFileValue) {
brettw@chromium.orga42d4632011-10-26 21:48:00396 DLOG(ERROR) << "Couldn't open " << file_name.value();
estade@chromium.orgcb6037d2009-11-16 22:55:17397 return false;
398 }
399
400 return MapFileToMemoryInternal();
estade@chromium.org7856bb82008-12-12 23:43:03401}
402
yuzo@chromium.org8199b3a2009-06-09 05:57:38403///////////////////////////////////////////////
404// FileEnumerator
405//
406// Note: the main logic is in file_util_<platform>.cc
407
408bool FileEnumerator::ShouldSkip(const FilePath& path) {
409 FilePath::StringType basename = path.BaseName().value();
410 return IsDot(path) || (IsDotDot(path) && !(INCLUDE_DOT_DOT & file_type_));
yuzo@chromium.org8199b3a2009-06-09 05:57:38411}
412
initial.commitd7cae122008-07-26 21:49:38413} // namespace