blob: b1f9f5e86f3cdf95c8929d91040487c006305ddb [file] [log] [blame]
Scott Graham66962112018-06-08 12:42:08 -07001// 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/files/file.h"
6
7#include <errno.h>
8#include <fcntl.h>
9#include <stdint.h>
10#include <sys/stat.h>
11#include <unistd.h>
12
13#include "base/logging.h"
Scott Graham66962112018-06-08 12:42:08 -070014#include "base/posix/eintr_wrapper.h"
15#include "base/strings/utf_string_conversions.h"
Scott Graham76a8dc72018-06-18 13:37:29 -070016#include "util/build_config.h"
Scott Graham66962112018-06-08 12:42:08 -070017
Scott Graham66962112018-06-08 12:42:08 -070018namespace base {
19
20// Make sure our Whence mappings match the system headers.
21static_assert(File::FROM_BEGIN == SEEK_SET && File::FROM_CURRENT == SEEK_CUR &&
22 File::FROM_END == SEEK_END,
23 "whence mapping must match the system headers");
24
25namespace {
26
Calvin Hill6c7fcc32019-05-16 17:22:01 +010027#if defined(OS_BSD) || defined(OS_MACOSX) || defined(OS_NACL) || \
Gaby Baghdadi45aa8422021-05-12 12:37:08 -040028 defined(OS_HAIKU) || defined(OS_MSYS) || defined(OS_ZOS) || \
29 defined(OS_ANDROID) && __ANDROID_API__ < 21
Scott Graham98cd3ca2018-06-14 22:26:55 -070030int CallFstat(int fd, stat_wrapper_t* sb) {
Scott Graham66962112018-06-08 12:42:08 -070031 return fstat(fd, sb);
32}
33#else
Scott Graham98cd3ca2018-06-14 22:26:55 -070034int CallFstat(int fd, stat_wrapper_t* sb) {
Scott Graham66962112018-06-08 12:42:08 -070035 return fstat64(fd, sb);
36}
37#endif
38
Brett Wilson102cdd42019-09-06 09:41:18 -070039// Some systems don't provide the following system calls, so either simulate
40// them or wrap them in order to minimize the number of #ifdef's in this file.
41#if !defined(OS_AIX)
Scott Graham66962112018-06-08 12:42:08 -070042bool IsOpenAppend(PlatformFile file) {
43 return (fcntl(file, F_GETFL) & O_APPEND) != 0;
44}
45
46int CallFtruncate(PlatformFile file, int64_t length) {
47 return HANDLE_EINTR(ftruncate(file, length));
48}
49
Scott Graham66962112018-06-08 12:42:08 -070050#if !defined(OS_FUCHSIA)
51File::Error CallFcntlFlock(PlatformFile file, bool do_lock) {
52 struct flock lock;
53 lock.l_type = do_lock ? F_WRLCK : F_UNLCK;
54 lock.l_whence = SEEK_SET;
55 lock.l_start = 0;
56 lock.l_len = 0; // Lock entire file.
57 if (HANDLE_EINTR(fcntl(file, F_SETLK, &lock)) == -1)
58 return File::GetLastFileError();
59 return File::FILE_OK;
60}
61#endif
62
Brett Wilson102cdd42019-09-06 09:41:18 -070063#else // !defined(OS_AIX)
Scott Graham66962112018-06-08 12:42:08 -070064
65bool IsOpenAppend(PlatformFile file) {
66 // NaCl doesn't implement fcntl. Since NaCl's write conforms to the POSIX
67 // standard and always appends if the file is opened with O_APPEND, just
68 // return false here.
69 return false;
70}
71
72int CallFtruncate(PlatformFile file, int64_t length) {
73 NOTIMPLEMENTED(); // NaCl doesn't implement ftruncate.
74 return 0;
75}
76
Scott Graham66962112018-06-08 12:42:08 -070077File::Error CallFcntlFlock(PlatformFile file, bool do_lock) {
78 NOTIMPLEMENTED(); // NaCl doesn't implement flock struct.
79 return File::FILE_ERROR_INVALID_OPERATION;
80}
Brett Wilson102cdd42019-09-06 09:41:18 -070081#endif // defined(OS_AIX)
Scott Graham66962112018-06-08 12:42:08 -070082
83} // namespace
84
85void File::Info::FromStat(const stat_wrapper_t& stat_info) {
86 is_directory = S_ISDIR(stat_info.st_mode);
87 is_symbolic_link = S_ISLNK(stat_info.st_mode);
88 size = stat_info.st_size;
89
Peter Collingbourne4dcd2772019-02-05 22:24:34 -080090#if defined(OS_MACOSX)
Scott Graham66962112018-06-08 12:42:08 -070091 time_t last_modified_sec = stat_info.st_mtimespec.tv_sec;
92 int64_t last_modified_nsec = stat_info.st_mtimespec.tv_nsec;
93 time_t last_accessed_sec = stat_info.st_atimespec.tv_sec;
94 int64_t last_accessed_nsec = stat_info.st_atimespec.tv_nsec;
95 time_t creation_time_sec = stat_info.st_ctimespec.tv_sec;
96 int64_t creation_time_nsec = stat_info.st_ctimespec.tv_nsec;
Gaby Baghdadi45aa8422021-05-12 12:37:08 -040097#elif defined(OS_AIX) || defined(OS_ZOS)
John Barboza499868c2018-07-18 22:58:30 -040098 time_t last_modified_sec = stat_info.st_mtime;
99 int64_t last_modified_nsec = 0;
100 time_t last_accessed_sec = stat_info.st_atime;
101 int64_t last_accessed_nsec = 0;
102 time_t creation_time_sec = stat_info.st_ctime;
103 int64_t creation_time_nsec = 0;
Peter Collingbourne4dcd2772019-02-05 22:24:34 -0800104#elif defined(OS_POSIX)
105 time_t last_modified_sec = stat_info.st_mtim.tv_sec;
106 int64_t last_modified_nsec = stat_info.st_mtim.tv_nsec;
107 time_t last_accessed_sec = stat_info.st_atim.tv_sec;
108 int64_t last_accessed_nsec = stat_info.st_atim.tv_nsec;
109 time_t creation_time_sec = stat_info.st_ctim.tv_sec;
110 int64_t creation_time_nsec = stat_info.st_ctim.tv_nsec;
Scott Graham66962112018-06-08 12:42:08 -0700111#else
Scott Graham622472c2018-06-14 16:53:22 -0700112#error
Scott Graham66962112018-06-08 12:42:08 -0700113#endif
114
Scott Grahamce047c92018-06-19 15:56:56 -0700115 constexpr uint64_t kNano = 1'000'000'000;
116 last_modified = last_modified_sec * kNano + last_modified_nsec;
117 last_accessed = last_accessed_sec * kNano + last_accessed_nsec;
118 creation_time = creation_time_sec * kNano + creation_time_nsec;
Scott Graham66962112018-06-08 12:42:08 -0700119}
120
121bool File::IsValid() const {
122 return file_.is_valid();
123}
124
125PlatformFile File::GetPlatformFile() const {
126 return file_.get();
127}
128
129PlatformFile File::TakePlatformFile() {
130 return file_.release();
131}
132
133void File::Close() {
134 if (!IsValid())
135 return;
136
Scott Graham66962112018-06-08 12:42:08 -0700137 file_.reset();
138}
139
140int64_t File::Seek(Whence whence, int64_t offset) {
Scott Graham66962112018-06-08 12:42:08 -0700141 DCHECK(IsValid());
142
Scott Graham66962112018-06-08 12:42:08 -0700143 static_assert(sizeof(int64_t) == sizeof(off_t), "off_t must be 64 bits");
144 return lseek(file_.get(), static_cast<off_t>(offset),
145 static_cast<int>(whence));
Scott Graham66962112018-06-08 12:42:08 -0700146}
147
148int File::Read(int64_t offset, char* data, int size) {
Scott Graham66962112018-06-08 12:42:08 -0700149 DCHECK(IsValid());
150 if (size < 0)
151 return -1;
152
Scott Graham66962112018-06-08 12:42:08 -0700153 int bytes_read = 0;
154 int rv;
155 do {
Scott Graham98cd3ca2018-06-14 22:26:55 -0700156 rv = HANDLE_EINTR(pread(file_.get(), data + bytes_read, size - bytes_read,
157 offset + bytes_read));
Scott Graham66962112018-06-08 12:42:08 -0700158 if (rv <= 0)
159 break;
160
161 bytes_read += rv;
162 } while (bytes_read < size);
163
164 return bytes_read ? bytes_read : rv;
165}
166
167int File::ReadAtCurrentPos(char* data, int size) {
Scott Graham66962112018-06-08 12:42:08 -0700168 DCHECK(IsValid());
169 if (size < 0)
170 return -1;
171
Scott Graham66962112018-06-08 12:42:08 -0700172 int bytes_read = 0;
173 int rv;
174 do {
175 rv = HANDLE_EINTR(read(file_.get(), data + bytes_read, size - bytes_read));
176 if (rv <= 0)
177 break;
178
179 bytes_read += rv;
180 } while (bytes_read < size);
181
182 return bytes_read ? bytes_read : rv;
183}
184
185int File::ReadNoBestEffort(int64_t offset, char* data, int size) {
Scott Graham66962112018-06-08 12:42:08 -0700186 DCHECK(IsValid());
Scott Graham66962112018-06-08 12:42:08 -0700187 return HANDLE_EINTR(pread(file_.get(), data, size, offset));
188}
189
190int File::ReadAtCurrentPosNoBestEffort(char* data, int size) {
Scott Graham66962112018-06-08 12:42:08 -0700191 DCHECK(IsValid());
192 if (size < 0)
193 return -1;
194
Scott Graham66962112018-06-08 12:42:08 -0700195 return HANDLE_EINTR(read(file_.get(), data, size));
196}
197
198int File::Write(int64_t offset, const char* data, int size) {
Scott Graham66962112018-06-08 12:42:08 -0700199 if (IsOpenAppend(file_.get()))
200 return WriteAtCurrentPos(data, size);
201
202 DCHECK(IsValid());
203 if (size < 0)
204 return -1;
205
Scott Graham66962112018-06-08 12:42:08 -0700206 int bytes_written = 0;
207 int rv;
208 do {
209 rv = HANDLE_EINTR(pwrite(file_.get(), data + bytes_written,
210 size - bytes_written, offset + bytes_written));
211 if (rv <= 0)
212 break;
213
214 bytes_written += rv;
215 } while (bytes_written < size);
216
217 return bytes_written ? bytes_written : rv;
218}
219
220int File::WriteAtCurrentPos(const char* data, int size) {
Scott Graham66962112018-06-08 12:42:08 -0700221 DCHECK(IsValid());
222 if (size < 0)
223 return -1;
224
Scott Graham66962112018-06-08 12:42:08 -0700225 int bytes_written = 0;
226 int rv;
227 do {
Scott Graham98cd3ca2018-06-14 22:26:55 -0700228 rv = HANDLE_EINTR(
229 write(file_.get(), data + bytes_written, size - bytes_written));
Scott Graham66962112018-06-08 12:42:08 -0700230 if (rv <= 0)
231 break;
232
233 bytes_written += rv;
234 } while (bytes_written < size);
235
236 return bytes_written ? bytes_written : rv;
237}
238
239int File::WriteAtCurrentPosNoBestEffort(const char* data, int size) {
Scott Graham66962112018-06-08 12:42:08 -0700240 DCHECK(IsValid());
241 if (size < 0)
242 return -1;
243
Scott Graham66962112018-06-08 12:42:08 -0700244 return HANDLE_EINTR(write(file_.get(), data, size));
245}
246
247int64_t File::GetLength() {
248 DCHECK(IsValid());
249
Scott Graham66962112018-06-08 12:42:08 -0700250 stat_wrapper_t file_info;
251 if (CallFstat(file_.get(), &file_info))
252 return -1;
253
254 return file_info.st_size;
255}
256
257bool File::SetLength(int64_t length) {
Scott Graham66962112018-06-08 12:42:08 -0700258 DCHECK(IsValid());
259
Scott Graham66962112018-06-08 12:42:08 -0700260 return !CallFtruncate(file_.get(), length);
261}
262
Scott Graham66962112018-06-08 12:42:08 -0700263bool File::GetInfo(Info* info) {
264 DCHECK(IsValid());
265
Scott Graham66962112018-06-08 12:42:08 -0700266 stat_wrapper_t file_info;
267 if (CallFstat(file_.get(), &file_info))
268 return false;
269
270 info->FromStat(file_info);
271 return true;
272}
273
274#if !defined(OS_FUCHSIA)
275File::Error File::Lock() {
Scott Graham66962112018-06-08 12:42:08 -0700276 return CallFcntlFlock(file_.get(), true);
277}
278
279File::Error File::Unlock() {
Scott Graham66962112018-06-08 12:42:08 -0700280 return CallFcntlFlock(file_.get(), false);
281}
282#endif
283
284File File::Duplicate() const {
285 if (!IsValid())
286 return File();
287
Scott Graham66962112018-06-08 12:42:08 -0700288 PlatformFile other_fd = HANDLE_EINTR(dup(GetPlatformFile()));
289 if (other_fd == -1)
290 return File(File::GetLastFileError());
291
292 File other(other_fd);
Scott Graham66962112018-06-08 12:42:08 -0700293 return other;
294}
295
296// Static.
297File::Error File::OSErrorToFileError(int saved_errno) {
298 switch (saved_errno) {
299 case EACCES:
300 case EISDIR:
301 case EROFS:
302 case EPERM:
303 return FILE_ERROR_ACCESS_DENIED;
304 case EBUSY:
Scott Graham66962112018-06-08 12:42:08 -0700305 case ETXTBSY:
Scott Graham66962112018-06-08 12:42:08 -0700306 return FILE_ERROR_IN_USE;
307 case EEXIST:
308 return FILE_ERROR_EXISTS;
309 case EIO:
310 return FILE_ERROR_IO;
311 case ENOENT:
312 return FILE_ERROR_NOT_FOUND;
313 case ENFILE: // fallthrough
314 case EMFILE:
315 return FILE_ERROR_TOO_MANY_OPENED;
316 case ENOMEM:
317 return FILE_ERROR_NO_MEMORY;
318 case ENOSPC:
319 return FILE_ERROR_NO_SPACE;
320 case ENOTDIR:
321 return FILE_ERROR_NOT_A_DIRECTORY;
322 default:
Scott Graham66962112018-06-08 12:42:08 -0700323 // This function should only be called for errors.
324 DCHECK_NE(0, saved_errno);
325 return FILE_ERROR_FAILED;
326 }
327}
328
Scott Graham66962112018-06-08 12:42:08 -0700329void File::DoInitialize(const FilePath& path, uint32_t flags) {
Scott Graham66962112018-06-08 12:42:08 -0700330 DCHECK(!IsValid());
331
332 int open_flags = 0;
Scott Graham66962112018-06-08 12:42:08 -0700333
334 if (flags & FLAG_CREATE_ALWAYS) {
335 DCHECK(!open_flags);
336 DCHECK(flags & FLAG_WRITE);
337 open_flags = O_CREAT | O_TRUNC;
338 }
339
Nico Weber47e15b72019-11-16 15:09:53 -0500340 if (!open_flags && !(flags & FLAG_OPEN)) {
Scott Graham66962112018-06-08 12:42:08 -0700341 NOTREACHED();
342 errno = EOPNOTSUPP;
343 error_details_ = FILE_ERROR_FAILED;
344 return;
345 }
346
347 if (flags & FLAG_WRITE && flags & FLAG_READ) {
348 open_flags |= O_RDWR;
349 } else if (flags & FLAG_WRITE) {
350 open_flags |= O_WRONLY;
Gaby Baghdadi45aa8422021-05-12 12:37:08 -0400351 } else if (flags & FLAG_READ) {
352 open_flags |= O_RDONLY;
353 } else {
Scott Graham66962112018-06-08 12:42:08 -0700354 NOTREACHED();
355 }
356
Scott Graham66962112018-06-08 12:42:08 -0700357 int mode = S_IRUSR | S_IWUSR;
Scott Graham66962112018-06-08 12:42:08 -0700358 int descriptor = HANDLE_EINTR(open(path.value().c_str(), open_flags, mode));
359
Scott Graham66962112018-06-08 12:42:08 -0700360 if (descriptor < 0) {
361 error_details_ = File::GetLastFileError();
362 return;
363 }
364
Scott Graham66962112018-06-08 12:42:08 -0700365 error_details_ = FILE_OK;
366 file_.reset(descriptor);
367}
Scott Graham66962112018-06-08 12:42:08 -0700368
369bool File::Flush() {
Scott Graham66962112018-06-08 12:42:08 -0700370 DCHECK(IsValid());
Scott Graham66962112018-06-08 12:42:08 -0700371
Scott Graham622472c2018-06-14 16:53:22 -0700372#if defined(OS_LINUX)
Scott Graham66962112018-06-08 12:42:08 -0700373 return !HANDLE_EINTR(fdatasync(file_.get()));
374#else
375 return !HANDLE_EINTR(fsync(file_.get()));
376#endif
377}
378
379void File::SetPlatformFile(PlatformFile file) {
380 DCHECK(!file_.is_valid());
381 file_.reset(file);
382}
383
384// static
385File::Error File::GetLastFileError() {
386 return base::File::OSErrorToFileError(errno);
387}
388
389} // namespace base