[go: nahoru, domu]

blob: dbc12d48e479b5e022ad9d7bd41fca1cdbc10984 [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
brettw@chromium.orge3177dd52014-08-13 20:22:145#include "base/files/file_util.h"
initial.commitd7cae122008-07-26 21:49:386
Xiaohan Wangb705a64a2022-01-15 18:31:137#include "build/build_config.h"
8
9#if BUILDFLAG(IS_WIN)
jeremy@chromium.orgc2c998c2009-01-27 19:08:3910#include <io.h>
11#endif
mark@chromium.org836f1342008-10-01 17:40:1312#include <stdio.h>
13
initial.commitd7cae122008-07-26 21:49:3814#include <fstream>
kaliamoorthi@chromium.org0710fdd2014-02-18 16:31:5115#include <limits>
Victor Costanb5a0a97002019-09-08 04:55:1516#include <memory>
Victor Hugo Vianna Silva05f14542021-08-05 16:08:4817#include <vector>
initial.commitd7cae122008-07-26 21:49:3818
Hans Wennborgc3cffa62020-04-27 10:09:1219#include "base/check_op.h"
brettw@chromium.org25a4c1c2013-06-08 04:53:3620#include "base/files/file_enumerator.h"
brettw@chromium.org57999812013-02-24 05:40:5221#include "base/files/file_path.h"
David Sanders83f8ae42022-04-04 23:15:3922#include "base/notreached.h"
Greg Thompson3f7e20f42020-05-02 19:01:1123#include "base/posix/eintr_wrapper.h"
tfarina@chromium.orgeb62f7262013-03-30 14:29:0024#include "base/strings/string_piece.h"
avi@chromium.org251cd6e52013-06-11 13:36:3725#include "base/strings/string_util.h"
26#include "base/strings/stringprintf.h"
avi@chromium.orga4ea1f12013-06-07 18:37:0727#include "base/strings/utf_string_conversions.h"
Etienne Pierre-Doray1da58592018-09-21 14:47:2028#include "base/threading/scoped_blocking_call.h"
avi543540e2015-12-24 05:15:3229#include "build/build_config.h"
estade@chromium.orgb9e04f02008-11-27 04:03:5730
Xiaohan Wangb705a64a2022-01-15 18:31:1331#if BUILDFLAG(IS_WIN)
Bruce Dawson1ad438d2021-07-09 20:10:0332#include <windows.h>
33#endif
34
brettw@chromium.org0408c752013-06-22 22:15:4635namespace base {
brettw@chromium.org04af979a2013-02-16 04:12:2636
Xiaohan Wangb705a64a2022-01-15 18:31:1337#if !BUILDFLAG(IS_WIN)
Lei Zhange3aa0b9a2020-06-11 08:59:2338OnceCallback<void(const FilePath&)> GetDeleteFileCallback() {
Lei Zhang9382efe2020-07-25 20:52:1439 return BindOnce(IgnoreResult(&DeleteFile));
Lei Zhange3aa0b9a2020-06-11 08:59:2340}
Xiaohan Wangb705a64a2022-01-15 18:31:1341#endif // !BUILDFLAG(IS_WIN)
Lei Zhange3aa0b9a2020-06-11 08:59:2342
Lei Zhang3190449f2020-06-12 05:14:0443OnceCallback<void(const FilePath&)> GetDeletePathRecursivelyCallback() {
Lei Zhang746ce472020-07-01 04:39:4544 return BindOnce(IgnoreResult(&DeletePathRecursively));
Lei Zhang3190449f2020-06-12 05:14:0445}
46
avi543540e2015-12-24 05:15:3247int64_t ComputeDirectorySize(const FilePath& root_path) {
48 int64_t running_size = 0;
brettw@chromium.org0408c752013-06-22 22:15:4649 FileEnumerator file_iter(root_path, true, FileEnumerator::FILES);
50 while (!file_iter.Next().empty())
51 running_size += file_iter.GetInfo().GetSize();
52 return running_size;
53}
54
brettw@chromium.org5553d5b2013-07-01 23:07:3655bool Move(const FilePath& from_path, const FilePath& to_path) {
56 if (from_path.ReferencesParent() || to_path.ReferencesParent())
57 return false;
brettw@chromium.orgf0ff2ad2013-07-09 17:42:2658 return internal::MoveUnsafe(from_path, to_path);
59}
60
Alexander Bolodurin53bfc89c22020-11-25 22:43:2961bool CopyFileContents(File& infile, File& outfile) {
Xiaohan Wangb705a64a2022-01-15 18:31:1362#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)
Brian Geffon591e8f22021-04-08 18:33:2363 bool retry_slow = false;
64 bool res =
65 internal::CopyFileContentsWithSendfile(infile, outfile, retry_slow);
66 if (res || !retry_slow) {
67 return res;
68 }
69 // Any failures which allow retrying using read/write will not have modified
70 // either file offset or size.
71#endif
72
Alexander Bolodurin53bfc89c22020-11-25 22:43:2973 static constexpr size_t kBufferSize = 32768;
74 std::vector<char> buffer(kBufferSize);
75
76 for (;;) {
77 int bytes_read = infile.ReadAtCurrentPos(buffer.data(), buffer.size());
78 if (bytes_read < 0) {
79 return false;
80 }
81 if (bytes_read == 0) {
82 return true;
83 }
84 // Allow for partial writes
85 int bytes_written_per_read = 0;
86 do {
87 int bytes_written_partial = outfile.WriteAtCurrentPos(
88 &buffer[bytes_written_per_read], bytes_read - bytes_written_per_read);
89 if (bytes_written_partial < 0) {
90 return false;
91 }
92
93 bytes_written_per_read += bytes_written_partial;
94 } while (bytes_written_per_read < bytes_read);
95 }
96
97 NOTREACHED();
98 return false;
99}
100
evanm@google.com640517f2008-10-30 23:54:04101bool ContentsEqual(const FilePath& filename1, const FilePath& filename2) {
initial.commitd7cae122008-07-26 21:49:38102 // We open the file in binary format even if they are text files because
103 // we are just comparing that bytes are exactly same in both files and not
104 // doing anything smart with text formatting.
Xiaohan Wangb705a64a2022-01-15 18:31:13105#if BUILDFLAG(IS_WIN)
Jan Wilken Dörrie9bb441d2019-11-01 17:48:40106 std::ifstream file1(filename1.value().c_str(),
erikkay@google.com5af2edb92008-08-08 20:16:08107 std::ios::in | std::ios::binary);
Jan Wilken Dörrie9bb441d2019-11-01 17:48:40108 std::ifstream file2(filename2.value().c_str(),
erikkay@google.com5af2edb92008-08-08 20:16:08109 std::ios::in | std::ios::binary);
Xiaohan Wangb705a64a2022-01-15 18:31:13110#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
jdoerrie983968a2019-01-29 12:54:41111 std::ifstream file1(filename1.value(), std::ios::in | std::ios::binary);
112 std::ifstream file2(filename2.value(), std::ios::in | std::ios::binary);
Xiaohan Wangb705a64a2022-01-15 18:31:13113#endif // BUILDFLAG(IS_WIN)
estade@chromium.orgb9e04f02008-11-27 04:03:57114
initial.commitd7cae122008-07-26 21:49:38115 // Even if both files aren't openable (and thus, in some sense, "equal"),
116 // any unusable file yields a result of "false".
117 if (!file1.is_open() || !file2.is_open())
118 return false;
119
120 const int BUFFER_SIZE = 2056;
121 char buffer1[BUFFER_SIZE], buffer2[BUFFER_SIZE];
122 do {
123 file1.read(buffer1, BUFFER_SIZE);
124 file2.read(buffer2, BUFFER_SIZE);
125
mark@chromium.orgb81637c32009-06-26 21:17:24126 if ((file1.eof() != file2.eof()) ||
initial.commitd7cae122008-07-26 21:49:38127 (file1.gcount() != file2.gcount()) ||
pkasting9cf9b942014-10-01 22:18:43128 (memcmp(buffer1, buffer2, static_cast<size_t>(file1.gcount())))) {
initial.commitd7cae122008-07-26 21:49:38129 file1.close();
130 file2.close();
131 return false;
132 }
mark@chromium.orgb81637c32009-06-26 21:17:24133 } while (!file1.eof() || !file2.eof());
initial.commitd7cae122008-07-26 21:49:38134
135 file1.close();
136 file2.close();
137 return true;
138}
139
mark@chromium.orgb81637c32009-06-26 21:17:24140bool TextContentsEqual(const FilePath& filename1, const FilePath& filename2) {
Xiaohan Wangb705a64a2022-01-15 18:31:13141#if BUILDFLAG(IS_WIN)
Jan Wilken Dörrie9bb441d2019-11-01 17:48:40142 std::ifstream file1(filename1.value().c_str(), std::ios::in);
143 std::ifstream file2(filename2.value().c_str(), std::ios::in);
Xiaohan Wangb705a64a2022-01-15 18:31:13144#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
jdoerrie983968a2019-01-29 12:54:41145 std::ifstream file1(filename1.value(), std::ios::in);
146 std::ifstream file2(filename2.value(), std::ios::in);
Xiaohan Wangb705a64a2022-01-15 18:31:13147#endif // BUILDFLAG(IS_WIN)
mark@chromium.orgb81637c32009-06-26 21:17:24148
149 // Even if both files aren't openable (and thus, in some sense, "equal"),
150 // any unusable file yields a result of "false".
151 if (!file1.is_open() || !file2.is_open())
152 return false;
153
154 do {
155 std::string line1, line2;
156 getline(file1, line1);
157 getline(file2, line2);
158
159 // Check for mismatched EOF states, or any error state.
160 if ((file1.eof() != file2.eof()) ||
161 file1.bad() || file2.bad()) {
162 return false;
163 }
164
165 // Trim all '\r' and '\n' characters from the end of the line.
166 std::string::size_type end1 = line1.find_last_not_of("\r\n");
167 if (end1 == std::string::npos)
168 line1.clear();
169 else if (end1 + 1 < line1.length())
170 line1.erase(end1 + 1);
171
172 std::string::size_type end2 = line2.find_last_not_of("\r\n");
173 if (end2 == std::string::npos)
174 line2.clear();
175 else if (end2 + 1 < line2.length())
176 line2.erase(end2 + 1);
177
178 if (line1 != line2)
179 return false;
180 } while (!file1.eof() || !file2.eof());
181
182 return true;
183}
184
Greg Thompson3f7e20f42020-05-02 19:01:11185bool ReadStreamToString(FILE* stream, std::string* contents) {
186 return ReadStreamToStringWithMaxSize(
187 stream, std::numeric_limits<size_t>::max(), contents);
188}
189
190bool ReadStreamToStringWithMaxSize(FILE* stream,
191 size_t max_size,
192 std::string* contents) {
kaliamoorthi@chromium.org0710fdd2014-02-18 16:31:51193 if (contents)
194 contents->clear();
Abhijith Nair1b5b6ee22021-11-18 21:01:59195 if (!stream)
196 return false;
Greg Thompson3f7e20f42020-05-02 19:01:11197 // Seeking to the beginning is best-effort -- it is expected to fail for
198 // certain non-file stream (e.g., pipes).
199 HANDLE_EINTR(fseek(stream, 0, SEEK_SET));
200
201 // Many files have incorrect size (proc files etc). Hence, the file is read
202 // sequentially as opposed to a one-shot read, using file size as a hint for
203 // chunk size if available.
Mikhail Istomin1ede1552018-05-16 17:40:24204 constexpr int64_t kDefaultChunkSize = 1 << 16;
Greg Thompson3f7e20f42020-05-02 19:01:11205 int64_t chunk_size = kDefaultChunkSize - 1;
Greg Thompson3f7e20f42020-05-02 19:01:11206 ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
Xiaohan Wangb705a64a2022-01-15 18:31:13207#if BUILDFLAG(IS_WIN)
Greg Thompson3f7e20f42020-05-02 19:01:11208 BY_HANDLE_FILE_INFORMATION file_info = {};
209 if (::GetFileInformationByHandle(
210 reinterpret_cast<HANDLE>(_get_osfhandle(_fileno(stream))),
211 &file_info)) {
212 LARGE_INTEGER size;
213 size.HighPart = file_info.nFileSizeHigh;
214 size.LowPart = file_info.nFileSizeLow;
215 if (size.QuadPart > 0)
216 chunk_size = size.QuadPart;
217 }
Xiaohan Wangb705a64a2022-01-15 18:31:13218#else // BUILDFLAG(IS_WIN)
Anand K Mistry53fa72d2021-07-15 01:15:26219 // In cases where the reported file size is 0, use a smaller chunk size to
220 // minimize memory allocated and cost of string::resize() in case the read
221 // size is small (i.e. proc files). If the file is larger than this, the read
222 // loop will reset |chunk_size| to kDefaultChunkSize.
223 constexpr int64_t kSmallChunkSize = 4096;
224 chunk_size = kSmallChunkSize - 1;
Greg Thompson3f7e20f42020-05-02 19:01:11225 stat_wrapper_t file_info = {};
226 if (!File::Fstat(fileno(stream), &file_info) && file_info.st_size > 0)
227 chunk_size = file_info.st_size;
Xiaohan Wangb705a64a2022-01-15 18:31:13228#endif // BUILDFLAG(IS_WIN)
Mikhail Istomin1ede1552018-05-16 17:40:24229 // We need to attempt to read at EOF for feof flag to be set so here we
230 // use |chunk_size| + 1.
231 chunk_size = std::min<uint64_t>(chunk_size, max_size) + 1;
Mikhail Istomin1ede1552018-05-16 17:40:24232 size_t bytes_read_this_pass;
233 size_t bytes_read_so_far = 0;
234 bool read_status = true;
235 std::string local_contents;
236 local_contents.resize(chunk_size);
kaliamoorthi@chromium.org0710fdd2014-02-18 16:31:51237
Mikhail Istomin1ede1552018-05-16 17:40:24238 while ((bytes_read_this_pass = fread(&local_contents[bytes_read_so_far], 1,
Greg Thompson3f7e20f42020-05-02 19:01:11239 chunk_size, stream)) > 0) {
Mikhail Istomin1ede1552018-05-16 17:40:24240 if ((max_size - bytes_read_so_far) < bytes_read_this_pass) {
241 // Read more than max_size bytes, bail out.
242 bytes_read_so_far = max_size;
kaliamoorthi@chromium.org0710fdd2014-02-18 16:31:51243 read_status = false;
244 break;
245 }
Mikhail Istomin1ede1552018-05-16 17:40:24246 // In case EOF was not reached, iterate again but revert to the default
247 // chunk size.
248 if (bytes_read_so_far == 0)
249 chunk_size = kDefaultChunkSize;
kaliamoorthi@chromium.org0710fdd2014-02-18 16:31:51250
Mikhail Istomin1ede1552018-05-16 17:40:24251 bytes_read_so_far += bytes_read_this_pass;
252 // Last fread syscall (after EOF) can be avoided via feof, which is just a
253 // flag check.
Greg Thompson3f7e20f42020-05-02 19:01:11254 if (feof(stream))
Mikhail Istomin1ede1552018-05-16 17:40:24255 break;
256 local_contents.resize(bytes_read_so_far + chunk_size);
initial.commitd7cae122008-07-26 21:49:38257 }
Greg Thompson3f7e20f42020-05-02 19:01:11258 read_status = read_status && !ferror(stream);
Mikhail Istomin1ede1552018-05-16 17:40:24259 if (contents) {
260 contents->swap(local_contents);
261 contents->resize(bytes_read_so_far);
262 }
initial.commitd7cae122008-07-26 21:49:38263
kaliamoorthi@chromium.org0710fdd2014-02-18 16:31:51264 return read_status;
265}
266
267bool ReadFileToString(const FilePath& path, std::string* contents) {
hashimoto6da2fef2016-02-24 03:39:58268 return ReadFileToStringWithMaxSize(path, contents,
269 std::numeric_limits<size_t>::max());
initial.commitd7cae122008-07-26 21:49:38270}
271
Greg Thompson3f7e20f42020-05-02 19:01:11272bool ReadFileToStringWithMaxSize(const FilePath& path,
273 std::string* contents,
274 size_t max_size) {
275 if (contents)
276 contents->clear();
277 if (path.ReferencesParent())
278 return false;
279 ScopedFILE file_stream(OpenFile(path, "rb"));
280 if (!file_stream)
281 return false;
282 return ReadStreamToStringWithMaxSize(file_stream.get(), max_size, contents);
283}
284
brettw@chromium.orgfb4bcfa32013-12-02 18:55:49285bool IsDirectoryEmpty(const FilePath& dir_path) {
286 FileEnumerator files(dir_path, false,
287 FileEnumerator::FILES | FileEnumerator::DIRECTORIES);
288 if (files.Next().empty())
289 return true;
290 return false;
291}
292
Greg Thompson3f7e20f42020-05-02 19:01:11293bool CreateTemporaryFile(FilePath* path) {
294 FilePath temp_dir;
295 return GetTempDir(&temp_dir) && CreateTemporaryFileInDir(temp_dir, path);
296}
297
Greg Thompsonf75f5fb2020-04-30 08:13:25298ScopedFILE CreateAndOpenTemporaryStream(FilePath* path) {
brettw@chromium.org03d9afc02013-12-03 17:55:52299 FilePath directory;
300 if (!GetTempDir(&directory))
Ivan Kotenkova16212a52017-11-08 12:37:33301 return nullptr;
brettw@chromium.org03d9afc02013-12-03 17:55:52302
Greg Thompsonf75f5fb2020-04-30 08:13:25303 return CreateAndOpenTemporaryStreamInDir(directory, path);
brettw@chromium.org03d9afc02013-12-03 17:55:52304}
305
brettw@chromium.org426d1c92013-12-03 20:08:54306bool CreateDirectory(const FilePath& full_path) {
Ivan Kotenkova16212a52017-11-08 12:37:33307 return CreateDirectoryAndGetError(full_path, nullptr);
brettw@chromium.org426d1c92013-12-03 20:08:54308}
309
avi543540e2015-12-24 05:15:32310bool GetFileSize(const FilePath& file_path, int64_t* file_size) {
rvargas@chromium.org54124ed02014-01-07 10:06:58311 File::Info info;
brettw@chromium.org9eae4e62013-12-04 20:56:49312 if (!GetFileInfo(file_path, &info))
brettw@chromium.org56285702013-12-04 18:22:49313 return false;
314 *file_size = info.size;
315 return true;
316}
317
brettw@chromium.orgc0d508162013-12-04 22:49:00318bool TouchFile(const FilePath& path,
319 const Time& last_accessed,
320 const Time& last_modified) {
Alexei Svitkine670d67ee2021-11-01 21:51:49321 int flags = File::FLAG_OPEN | File::FLAG_WRITE_ATTRIBUTES;
brettw@chromium.orgc0d508162013-12-04 22:49:00322
Xiaohan Wangb705a64a2022-01-15 18:31:13323#if BUILDFLAG(IS_WIN)
brettw@chromium.orgc0d508162013-12-04 22:49:00324 // On Windows, FILE_FLAG_BACKUP_SEMANTICS is needed to open a directory.
325 if (DirectoryExists(path))
Alexei Svitkinec417b2cd2021-10-21 14:22:40326 flags |= File::FLAG_WIN_BACKUP_SEMANTICS;
Xiaohan Wangb705a64a2022-01-15 18:31:13327#elif BUILDFLAG(IS_FUCHSIA)
Wez432ea8d892019-03-31 01:17:08328 // On Fuchsia, we need O_RDONLY for directories, or O_WRONLY for files.
329 // TODO(https://crbug.com/947802): Find a cleaner workaround for this.
330 flags |= (DirectoryExists(path) ? File::FLAG_READ : File::FLAG_WRITE);
331#endif
brettw@chromium.orgc0d508162013-12-04 22:49:00332
rvargas@chromium.org54124ed02014-01-07 10:06:58333 File file(path, flags);
334 if (!file.IsValid())
335 return false;
brettw@chromium.orgc0d508162013-12-04 22:49:00336
rvargas@chromium.org54124ed02014-01-07 10:06:58337 return file.SetTimes(last_accessed, last_modified);
brettw@chromium.orgc0d508162013-12-04 22:49:00338}
339
mark@chromium.org836f1342008-10-01 17:40:13340bool CloseFile(FILE* file) {
Ivan Kotenkova16212a52017-11-08 12:37:33341 if (file == nullptr)
sidchat@google.coma1a19502008-10-21 17:14:45342 return true;
mark@chromium.org836f1342008-10-01 17:40:13343 return fclose(file) == 0;
344}
345
jeremy@chromium.orgc2c998c2009-01-27 19:08:39346bool TruncateFile(FILE* file) {
Ivan Kotenkova16212a52017-11-08 12:37:33347 if (file == nullptr)
jeremy@chromium.orgc2c998c2009-01-27 19:08:39348 return false;
349 long current_offset = ftell(file);
350 if (current_offset == -1)
351 return false;
Xiaohan Wangb705a64a2022-01-15 18:31:13352#if BUILDFLAG(IS_WIN)
jeremy@chromium.orgc2c998c2009-01-27 19:08:39353 int fd = _fileno(file);
354 if (_chsize(fd, current_offset) != 0)
355 return false;
356#else
357 int fd = fileno(file);
358 if (ftruncate(fd, current_offset) != 0)
359 return false;
360#endif
361 return true;
362}
363
Lei Zhangeebbef62020-05-05 20:16:05364bool WriteFile(const FilePath& filename, span<const uint8_t> data) {
365 int size = checked_cast<int>(data.size());
366 return WriteFile(filename, reinterpret_cast<const char*>(data.data()),
367 size) == size;
368}
369
370bool WriteFile(const FilePath& filename, StringPiece data) {
371 int size = checked_cast<int>(data.size());
372 return WriteFile(filename, data.data(), size) == size;
373}
374
Greg Thompsonb4abcb42019-08-23 01:42:56375int GetUniquePathNumber(const FilePath& path) {
376 DCHECK(!path.empty());
377 if (!PathExists(path))
Greg Thompson42373544a2019-08-14 10:11:10378 return 0;
379
380 std::string number;
381 for (int count = 1; count <= kMaxUniqueFiles; ++count) {
382 StringAppendF(&number, " (%d)", count);
Greg Thompsonb4abcb42019-08-23 01:42:56383 if (!PathExists(path.InsertBeforeExtensionASCII(number)))
Greg Thompson42373544a2019-08-14 10:11:10384 return count;
385 number.clear();
jam@chromium.orge285afa2012-01-31 23:16:39386 }
387
388 return -1;
389}
Bruce Longd096e482019-02-28 17:50:00390
391FilePath GetUniquePath(const FilePath& path) {
Greg Thompsonb4abcb42019-08-23 01:42:56392 DCHECK(!path.empty());
393 const int uniquifier = GetUniquePathNumber(path);
394 if (uniquifier > 0)
395 return path.InsertBeforeExtensionASCII(StringPrintf(" (%d)", uniquifier));
396 return uniquifier == 0 ? path : base::FilePath();
Bruce Longd096e482019-02-28 17:50:00397}
Victor Costanb5a0a97002019-09-08 04:55:15398
399namespace internal {
400
401bool PreReadFileSlow(const FilePath& file_path, int64_t max_bytes) {
402 DCHECK_GE(max_bytes, 0);
403
404 File file(file_path, File::FLAG_OPEN | File::FLAG_READ |
Alexei Svitkinec417b2cd2021-10-21 14:22:40405 File::FLAG_WIN_SEQUENTIAL_SCAN |
406 File::FLAG_WIN_SHARE_DELETE);
Victor Costanb5a0a97002019-09-08 04:55:15407 if (!file.IsValid())
408 return false;
409
410 constexpr int kBufferSize = 1024 * 1024;
411 // Ensures the buffer is deallocated at function exit.
412 std::unique_ptr<char[]> buffer_deleter(new char[kBufferSize]);
413 char* const buffer = buffer_deleter.get();
414
415 while (max_bytes > 0) {
416 // The static_cast<int> is safe because kBufferSize is int, and both values
417 // are non-negative. So, the minimum is guaranteed to fit in int.
418 const int read_size =
419 static_cast<int>(std::min<int64_t>(max_bytes, kBufferSize));
420 DCHECK_GE(read_size, 0);
421 DCHECK_LE(read_size, kBufferSize);
422
423 const int read_bytes = file.ReadAtCurrentPos(buffer, read_size);
424 if (read_bytes < 0)
425 return false;
426 if (read_bytes == 0)
427 break;
428
429 max_bytes -= read_bytes;
430 }
431
432 return true;
433}
434
435} // namespace internal
436
brettw@chromium.orga26f4ae2014-03-13 17:26:21437} // namespace base