1 // Copyright (c) 2012 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
7 #if defined(OS_WIN)
8 #include <io.h>
9 #endif
10 #include <stdio.h>
11
12 #include <fstream>
13 #include <limits>
14
15 #include "base/files/file_enumerator.h"
16 #include "base/files/file_path.h"
17 #include "base/logging.h"
18 #include "base/strings/string_piece.h"
19 #include "base/strings/string_util.h"
20 #include "base/strings/stringprintf.h"
21 #include "base/strings/utf_string_conversions.h"
22
23 namespace base {
24
25 namespace {
26
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.
32 static const int kMaxUniqueFiles = 100;
33
34 } // namespace
35
ComputeDirectorySize(const FilePath & root_path)36 int64 ComputeDirectorySize(const FilePath& root_path) {
37 int64 running_size = 0;
38 FileEnumerator file_iter(root_path, true, FileEnumerator::FILES);
39 while (!file_iter.Next().empty())
40 running_size += file_iter.GetInfo().GetSize();
41 return running_size;
42 }
43
Move(const FilePath & from_path,const FilePath & to_path)44 bool Move(const FilePath& from_path, const FilePath& to_path) {
45 if (from_path.ReferencesParent() || to_path.ReferencesParent())
46 return false;
47 return internal::MoveUnsafe(from_path, to_path);
48 }
49
CopyFile(const FilePath & from_path,const FilePath & to_path)50 bool CopyFile(const FilePath& from_path, const FilePath& to_path) {
51 if (from_path.ReferencesParent() || to_path.ReferencesParent())
52 return false;
53 return internal::CopyFileUnsafe(from_path, to_path);
54 }
55
ContentsEqual(const FilePath & filename1,const FilePath & filename2)56 bool ContentsEqual(const FilePath& filename1, const FilePath& filename2) {
57 // We open the file in binary format even if they are text files because
58 // we are just comparing that bytes are exactly same in both files and not
59 // doing anything smart with text formatting.
60 std::ifstream file1(filename1.value().c_str(),
61 std::ios::in | std::ios::binary);
62 std::ifstream file2(filename2.value().c_str(),
63 std::ios::in | std::ios::binary);
64
65 // Even if both files aren't openable (and thus, in some sense, "equal"),
66 // any unusable file yields a result of "false".
67 if (!file1.is_open() || !file2.is_open())
68 return false;
69
70 const int BUFFER_SIZE = 2056;
71 char buffer1[BUFFER_SIZE], buffer2[BUFFER_SIZE];
72 do {
73 file1.read(buffer1, BUFFER_SIZE);
74 file2.read(buffer2, BUFFER_SIZE);
75
76 if ((file1.eof() != file2.eof()) ||
77 (file1.gcount() != file2.gcount()) ||
78 (memcmp(buffer1, buffer2, file1.gcount()))) {
79 file1.close();
80 file2.close();
81 return false;
82 }
83 } while (!file1.eof() || !file2.eof());
84
85 file1.close();
86 file2.close();
87 return true;
88 }
89
TextContentsEqual(const FilePath & filename1,const FilePath & filename2)90 bool TextContentsEqual(const FilePath& filename1, const FilePath& filename2) {
91 std::ifstream file1(filename1.value().c_str(), std::ios::in);
92 std::ifstream file2(filename2.value().c_str(), std::ios::in);
93
94 // Even if both files aren't openable (and thus, in some sense, "equal"),
95 // any unusable file yields a result of "false".
96 if (!file1.is_open() || !file2.is_open())
97 return false;
98
99 do {
100 std::string line1, line2;
101 getline(file1, line1);
102 getline(file2, line2);
103
104 // Check for mismatched EOF states, or any error state.
105 if ((file1.eof() != file2.eof()) ||
106 file1.bad() || file2.bad()) {
107 return false;
108 }
109
110 // Trim all '\r' and '\n' characters from the end of the line.
111 std::string::size_type end1 = line1.find_last_not_of("\r\n");
112 if (end1 == std::string::npos)
113 line1.clear();
114 else if (end1 + 1 < line1.length())
115 line1.erase(end1 + 1);
116
117 std::string::size_type end2 = line2.find_last_not_of("\r\n");
118 if (end2 == std::string::npos)
119 line2.clear();
120 else if (end2 + 1 < line2.length())
121 line2.erase(end2 + 1);
122
123 if (line1 != line2)
124 return false;
125 } while (!file1.eof() || !file2.eof());
126
127 return true;
128 }
129
ReadFileToString(const FilePath & path,std::string * contents,size_t max_size)130 bool ReadFileToString(const FilePath& path,
131 std::string* contents,
132 size_t max_size) {
133 if (contents)
134 contents->clear();
135 if (path.ReferencesParent())
136 return false;
137 FILE* file = OpenFile(path, "rb");
138 if (!file) {
139 return false;
140 }
141
142 char buf[1 << 16];
143 size_t len;
144 size_t size = 0;
145 bool read_status = true;
146
147 // Many files supplied in |path| have incorrect size (proc files etc).
148 // Hence, the file is read sequentially as opposed to a one-shot read.
149 while ((len = fread(buf, 1, sizeof(buf), file)) > 0) {
150 if (contents)
151 contents->append(buf, std::min(len, max_size - size));
152
153 if ((max_size - size) < len) {
154 read_status = false;
155 break;
156 }
157
158 size += len;
159 }
160 read_status = read_status && !ferror(file);
161 CloseFile(file);
162
163 return read_status;
164 }
165
ReadFileToString(const FilePath & path,std::string * contents)166 bool ReadFileToString(const FilePath& path, std::string* contents) {
167 return ReadFileToString(path, contents, std::numeric_limits<size_t>::max());
168 }
169
IsDirectoryEmpty(const FilePath & dir_path)170 bool IsDirectoryEmpty(const FilePath& dir_path) {
171 FileEnumerator files(dir_path, false,
172 FileEnumerator::FILES | FileEnumerator::DIRECTORIES);
173 if (files.Next().empty())
174 return true;
175 return false;
176 }
177
CreateAndOpenTemporaryFile(FilePath * path)178 FILE* CreateAndOpenTemporaryFile(FilePath* path) {
179 FilePath directory;
180 if (!GetTempDir(&directory))
181 return NULL;
182
183 return CreateAndOpenTemporaryFileInDir(directory, path);
184 }
185
CreateDirectory(const FilePath & full_path)186 bool CreateDirectory(const FilePath& full_path) {
187 return CreateDirectoryAndGetError(full_path, NULL);
188 }
189
GetFileSize(const FilePath & file_path,int64 * file_size)190 bool GetFileSize(const FilePath& file_path, int64* file_size) {
191 File::Info info;
192 if (!GetFileInfo(file_path, &info))
193 return false;
194 *file_size = info.size;
195 return true;
196 }
197
TouchFile(const FilePath & path,const Time & last_accessed,const Time & last_modified)198 bool TouchFile(const FilePath& path,
199 const Time& last_accessed,
200 const Time& last_modified) {
201 int flags = File::FLAG_OPEN | File::FLAG_WRITE_ATTRIBUTES;
202
203 #if defined(OS_WIN)
204 // On Windows, FILE_FLAG_BACKUP_SEMANTICS is needed to open a directory.
205 if (DirectoryExists(path))
206 flags |= File::FLAG_BACKUP_SEMANTICS;
207 #endif // OS_WIN
208
209 File file(path, flags);
210 if (!file.IsValid())
211 return false;
212
213 return file.SetTimes(last_accessed, last_modified);
214 }
215
CloseFile(FILE * file)216 bool CloseFile(FILE* file) {
217 if (file == NULL)
218 return true;
219 return fclose(file) == 0;
220 }
221
TruncateFile(FILE * file)222 bool TruncateFile(FILE* file) {
223 if (file == NULL)
224 return false;
225 long current_offset = ftell(file);
226 if (current_offset == -1)
227 return false;
228 #if defined(OS_WIN)
229 int fd = _fileno(file);
230 if (_chsize(fd, current_offset) != 0)
231 return false;
232 #else
233 int fd = fileno(file);
234 if (ftruncate(fd, current_offset) != 0)
235 return false;
236 #endif
237 return true;
238 }
239
GetUniquePathNumber(const FilePath & path,const FilePath::StringType & suffix)240 int GetUniquePathNumber(const FilePath& path,
241 const FilePath::StringType& suffix) {
242 bool have_suffix = !suffix.empty();
243 if (!PathExists(path) &&
244 (!have_suffix || !PathExists(FilePath(path.value() + suffix)))) {
245 return 0;
246 }
247
248 FilePath new_path;
249 for (int count = 1; count <= kMaxUniqueFiles; ++count) {
250 new_path = path.InsertBeforeExtensionASCII(StringPrintf(" (%d)", count));
251 if (!PathExists(new_path) &&
252 (!have_suffix || !PathExists(FilePath(new_path.value() + suffix)))) {
253 return count;
254 }
255 }
256
257 return -1;
258 }
259
260 } // namespace base
261