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/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"
14 #include "base/metrics/sparse_histogram.h"
15 #include "base/posix/eintr_wrapper.h"
16 #include "base/strings/utf_string_conversions.h"
17 #include "base/threading/thread_restrictions.h"
18 #include "build/build_config.h"
19
20 #if defined(OS_ANDROID)
21 #include "base/os_compat_android.h"
22 #endif
23
24 namespace base {
25
26 // Make sure our Whence mappings match the system headers.
27 static_assert(File::FROM_BEGIN == SEEK_SET && File::FROM_CURRENT == SEEK_CUR &&
28 File::FROM_END == SEEK_END,
29 "whence mapping must match the system headers");
30
31 namespace {
32
33 #if defined(OS_BSD) || defined(OS_MACOSX) || defined(OS_NACL)
CallFstat(int fd,stat_wrapper_t * sb)34 int CallFstat(int fd, stat_wrapper_t *sb) {
35 ThreadRestrictions::AssertIOAllowed();
36 return fstat(fd, sb);
37 }
38 #else
39 int CallFstat(int fd, stat_wrapper_t *sb) {
40 ThreadRestrictions::AssertIOAllowed();
41 return fstat64(fd, sb);
42 }
43 #endif
44
45 // NaCl doesn't provide the following system calls, so either simulate them or
46 // wrap them in order to minimize the number of #ifdef's in this file.
47 #if !defined(OS_NACL)
IsOpenAppend(PlatformFile file)48 bool IsOpenAppend(PlatformFile file) {
49 return (fcntl(file, F_GETFL) & O_APPEND) != 0;
50 }
51
CallFtruncate(PlatformFile file,int64_t length)52 int CallFtruncate(PlatformFile file, int64_t length) {
53 return HANDLE_EINTR(ftruncate(file, length));
54 }
55
CallFutimes(PlatformFile file,const struct timeval times[2])56 int CallFutimes(PlatformFile file, const struct timeval times[2]) {
57 #ifdef __USE_XOPEN2K8
58 // futimens should be available, but futimes might not be
59 // http://pubs.opengroup.org/onlinepubs/9699919799/
60
61 timespec ts_times[2];
62 ts_times[0].tv_sec = times[0].tv_sec;
63 ts_times[0].tv_nsec = times[0].tv_usec * 1000;
64 ts_times[1].tv_sec = times[1].tv_sec;
65 ts_times[1].tv_nsec = times[1].tv_usec * 1000;
66
67 return futimens(file, ts_times);
68 #else
69 return futimes(file, times);
70 #endif
71 }
72
CallFcntlFlock(PlatformFile file,bool do_lock)73 File::Error CallFcntlFlock(PlatformFile file, bool do_lock) {
74 struct flock lock;
75 lock.l_type = do_lock ? F_WRLCK : F_UNLCK;
76 lock.l_whence = SEEK_SET;
77 lock.l_start = 0;
78 lock.l_len = 0; // Lock entire file.
79 if (HANDLE_EINTR(fcntl(file, F_SETLK, &lock)) == -1)
80 return File::OSErrorToFileError(errno);
81 return File::FILE_OK;
82 }
83 #else // defined(OS_NACL)
84
IsOpenAppend(PlatformFile file)85 bool IsOpenAppend(PlatformFile file) {
86 // NaCl doesn't implement fcntl. Since NaCl's write conforms to the POSIX
87 // standard and always appends if the file is opened with O_APPEND, just
88 // return false here.
89 return false;
90 }
91
CallFtruncate(PlatformFile file,int64_t length)92 int CallFtruncate(PlatformFile file, int64_t length) {
93 NOTIMPLEMENTED(); // NaCl doesn't implement ftruncate.
94 return 0;
95 }
96
CallFutimes(PlatformFile file,const struct timeval times[2])97 int CallFutimes(PlatformFile file, const struct timeval times[2]) {
98 NOTIMPLEMENTED(); // NaCl doesn't implement futimes.
99 return 0;
100 }
101
CallFcntlFlock(PlatformFile file,bool do_lock)102 File::Error CallFcntlFlock(PlatformFile file, bool do_lock) {
103 NOTIMPLEMENTED(); // NaCl doesn't implement flock struct.
104 return File::FILE_ERROR_INVALID_OPERATION;
105 }
106 #endif // defined(OS_NACL)
107
108 } // namespace
109
FromStat(const stat_wrapper_t & stat_info)110 void File::Info::FromStat(const stat_wrapper_t& stat_info) {
111 is_directory = S_ISDIR(stat_info.st_mode);
112 is_symbolic_link = S_ISLNK(stat_info.st_mode);
113 size = stat_info.st_size;
114
115 #if defined(OS_LINUX)
116 time_t last_modified_sec = stat_info.st_mtim.tv_sec;
117 int64_t last_modified_nsec = stat_info.st_mtim.tv_nsec;
118 time_t last_accessed_sec = stat_info.st_atim.tv_sec;
119 int64_t last_accessed_nsec = stat_info.st_atim.tv_nsec;
120 time_t creation_time_sec = stat_info.st_ctim.tv_sec;
121 int64_t creation_time_nsec = stat_info.st_ctim.tv_nsec;
122 #elif defined(OS_ANDROID)
123 time_t last_modified_sec = stat_info.st_mtime;
124 int64_t last_modified_nsec = stat_info.st_mtime_nsec;
125 time_t last_accessed_sec = stat_info.st_atime;
126 int64_t last_accessed_nsec = stat_info.st_atime_nsec;
127 time_t creation_time_sec = stat_info.st_ctime;
128 int64_t creation_time_nsec = stat_info.st_ctime_nsec;
129 #elif defined(OS_MACOSX) || defined(OS_IOS) || defined(OS_BSD)
130 time_t last_modified_sec = stat_info.st_mtimespec.tv_sec;
131 int64_t last_modified_nsec = stat_info.st_mtimespec.tv_nsec;
132 time_t last_accessed_sec = stat_info.st_atimespec.tv_sec;
133 int64_t last_accessed_nsec = stat_info.st_atimespec.tv_nsec;
134 time_t creation_time_sec = stat_info.st_ctimespec.tv_sec;
135 int64_t creation_time_nsec = stat_info.st_ctimespec.tv_nsec;
136 #else
137 time_t last_modified_sec = stat_info.st_mtime;
138 int64_t last_modified_nsec = 0;
139 time_t last_accessed_sec = stat_info.st_atime;
140 int64_t last_accessed_nsec = 0;
141 time_t creation_time_sec = stat_info.st_ctime;
142 int64_t creation_time_nsec = 0;
143 #endif
144
145 last_modified =
146 Time::FromTimeT(last_modified_sec) +
147 TimeDelta::FromMicroseconds(last_modified_nsec /
148 Time::kNanosecondsPerMicrosecond);
149
150 last_accessed =
151 Time::FromTimeT(last_accessed_sec) +
152 TimeDelta::FromMicroseconds(last_accessed_nsec /
153 Time::kNanosecondsPerMicrosecond);
154
155 creation_time =
156 Time::FromTimeT(creation_time_sec) +
157 TimeDelta::FromMicroseconds(creation_time_nsec /
158 Time::kNanosecondsPerMicrosecond);
159 }
160
IsValid() const161 bool File::IsValid() const {
162 return file_.is_valid();
163 }
164
GetPlatformFile() const165 PlatformFile File::GetPlatformFile() const {
166 return file_.get();
167 }
168
TakePlatformFile()169 PlatformFile File::TakePlatformFile() {
170 return file_.release();
171 }
172
Close()173 void File::Close() {
174 if (!IsValid())
175 return;
176
177 SCOPED_FILE_TRACE("Close");
178 ThreadRestrictions::AssertIOAllowed();
179 file_.reset();
180 }
181
Seek(Whence whence,int64_t offset)182 int64_t File::Seek(Whence whence, int64_t offset) {
183 ThreadRestrictions::AssertIOAllowed();
184 DCHECK(IsValid());
185
186 SCOPED_FILE_TRACE_WITH_SIZE("Seek", offset);
187
188 // Additionally check __BIONIC__ since older versions of Android don't define
189 // _FILE_OFFSET_BITS.
190 #if _FILE_OFFSET_BITS != 64 || defined(__BIONIC__)
191 static_assert(sizeof(int64_t) == sizeof(off64_t), "off64_t must be 64 bits");
192 return lseek64(file_.get(), static_cast<off64_t>(offset),
193 static_cast<int>(whence));
194 #else
195 static_assert(sizeof(int64_t) == sizeof(off_t), "off_t must be 64 bits");
196 return lseek(file_.get(), static_cast<off_t>(offset),
197 static_cast<int>(whence));
198 #endif
199 }
200
Read(int64_t offset,char * data,int size)201 int File::Read(int64_t offset, char* data, int size) {
202 ThreadRestrictions::AssertIOAllowed();
203 DCHECK(IsValid());
204 if (size < 0)
205 return -1;
206
207 SCOPED_FILE_TRACE_WITH_SIZE("Read", size);
208
209 int bytes_read = 0;
210 int rv;
211 do {
212 rv = HANDLE_EINTR(pread(file_.get(), data + bytes_read,
213 size - bytes_read, offset + bytes_read));
214 if (rv <= 0)
215 break;
216
217 bytes_read += rv;
218 } while (bytes_read < size);
219
220 return bytes_read ? bytes_read : rv;
221 }
222
ReadAtCurrentPos(char * data,int size)223 int File::ReadAtCurrentPos(char* data, int size) {
224 ThreadRestrictions::AssertIOAllowed();
225 DCHECK(IsValid());
226 if (size < 0)
227 return -1;
228
229 SCOPED_FILE_TRACE_WITH_SIZE("ReadAtCurrentPos", size);
230
231 int bytes_read = 0;
232 int rv;
233 do {
234 rv = HANDLE_EINTR(read(file_.get(), data + bytes_read, size - bytes_read));
235 if (rv <= 0)
236 break;
237
238 bytes_read += rv;
239 } while (bytes_read < size);
240
241 return bytes_read ? bytes_read : rv;
242 }
243
ReadNoBestEffort(int64_t offset,char * data,int size)244 int File::ReadNoBestEffort(int64_t offset, char* data, int size) {
245 ThreadRestrictions::AssertIOAllowed();
246 DCHECK(IsValid());
247 SCOPED_FILE_TRACE_WITH_SIZE("ReadNoBestEffort", size);
248 return HANDLE_EINTR(pread(file_.get(), data, size, offset));
249 }
250
ReadAtCurrentPosNoBestEffort(char * data,int size)251 int File::ReadAtCurrentPosNoBestEffort(char* data, int size) {
252 ThreadRestrictions::AssertIOAllowed();
253 DCHECK(IsValid());
254 if (size < 0)
255 return -1;
256
257 SCOPED_FILE_TRACE_WITH_SIZE("ReadAtCurrentPosNoBestEffort", size);
258 return HANDLE_EINTR(read(file_.get(), data, size));
259 }
260
Write(int64_t offset,const char * data,int size)261 int File::Write(int64_t offset, const char* data, int size) {
262 ThreadRestrictions::AssertIOAllowed();
263
264 if (IsOpenAppend(file_.get()))
265 return WriteAtCurrentPos(data, size);
266
267 DCHECK(IsValid());
268 if (size < 0)
269 return -1;
270
271 SCOPED_FILE_TRACE_WITH_SIZE("Write", size);
272
273 int bytes_written = 0;
274 int rv;
275 do {
276 rv = HANDLE_EINTR(pwrite(file_.get(), data + bytes_written,
277 size - bytes_written, offset + bytes_written));
278 if (rv <= 0)
279 break;
280
281 bytes_written += rv;
282 } while (bytes_written < size);
283
284 return bytes_written ? bytes_written : rv;
285 }
286
WriteAtCurrentPos(const char * data,int size)287 int File::WriteAtCurrentPos(const char* data, int size) {
288 ThreadRestrictions::AssertIOAllowed();
289 DCHECK(IsValid());
290 if (size < 0)
291 return -1;
292
293 SCOPED_FILE_TRACE_WITH_SIZE("WriteAtCurrentPos", size);
294
295 int bytes_written = 0;
296 int rv;
297 do {
298 rv = HANDLE_EINTR(write(file_.get(), data + bytes_written,
299 size - bytes_written));
300 if (rv <= 0)
301 break;
302
303 bytes_written += rv;
304 } while (bytes_written < size);
305
306 return bytes_written ? bytes_written : rv;
307 }
308
WriteAtCurrentPosNoBestEffort(const char * data,int size)309 int File::WriteAtCurrentPosNoBestEffort(const char* data, int size) {
310 ThreadRestrictions::AssertIOAllowed();
311 DCHECK(IsValid());
312 if (size < 0)
313 return -1;
314
315 SCOPED_FILE_TRACE_WITH_SIZE("WriteAtCurrentPosNoBestEffort", size);
316 return HANDLE_EINTR(write(file_.get(), data, size));
317 }
318
GetLength()319 int64_t File::GetLength() {
320 DCHECK(IsValid());
321
322 SCOPED_FILE_TRACE("GetLength");
323
324 stat_wrapper_t file_info;
325 if (CallFstat(file_.get(), &file_info))
326 return false;
327
328 return file_info.st_size;
329 }
330
SetLength(int64_t length)331 bool File::SetLength(int64_t length) {
332 ThreadRestrictions::AssertIOAllowed();
333 DCHECK(IsValid());
334
335 SCOPED_FILE_TRACE_WITH_SIZE("SetLength", length);
336 return !CallFtruncate(file_.get(), length);
337 }
338
SetTimes(Time last_access_time,Time last_modified_time)339 bool File::SetTimes(Time last_access_time, Time last_modified_time) {
340 ThreadRestrictions::AssertIOAllowed();
341 DCHECK(IsValid());
342
343 SCOPED_FILE_TRACE("SetTimes");
344
345 timeval times[2];
346 times[0] = last_access_time.ToTimeVal();
347 times[1] = last_modified_time.ToTimeVal();
348
349 return !CallFutimes(file_.get(), times);
350 }
351
GetInfo(Info * info)352 bool File::GetInfo(Info* info) {
353 DCHECK(IsValid());
354
355 SCOPED_FILE_TRACE("GetInfo");
356
357 stat_wrapper_t file_info;
358 if (CallFstat(file_.get(), &file_info))
359 return false;
360
361 info->FromStat(file_info);
362 return true;
363 }
364
Lock()365 File::Error File::Lock() {
366 SCOPED_FILE_TRACE("Lock");
367 return CallFcntlFlock(file_.get(), true);
368 }
369
Unlock()370 File::Error File::Unlock() {
371 SCOPED_FILE_TRACE("Unlock");
372 return CallFcntlFlock(file_.get(), false);
373 }
374
Duplicate()375 File File::Duplicate() {
376 if (!IsValid())
377 return File();
378
379 SCOPED_FILE_TRACE("Duplicate");
380
381 PlatformFile other_fd = dup(GetPlatformFile());
382 if (other_fd == -1)
383 return File(OSErrorToFileError(errno));
384
385 File other(other_fd);
386 if (async())
387 other.async_ = true;
388 return other;
389 }
390
391 // Static.
OSErrorToFileError(int saved_errno)392 File::Error File::OSErrorToFileError(int saved_errno) {
393 switch (saved_errno) {
394 case EACCES:
395 case EISDIR:
396 case EROFS:
397 case EPERM:
398 return FILE_ERROR_ACCESS_DENIED;
399 case EBUSY:
400 #if !defined(OS_NACL) // ETXTBSY not defined by NaCl.
401 case ETXTBSY:
402 #endif
403 return FILE_ERROR_IN_USE;
404 case EEXIST:
405 return FILE_ERROR_EXISTS;
406 case EIO:
407 return FILE_ERROR_IO;
408 case ENOENT:
409 return FILE_ERROR_NOT_FOUND;
410 case EMFILE:
411 return FILE_ERROR_TOO_MANY_OPENED;
412 case ENOMEM:
413 return FILE_ERROR_NO_MEMORY;
414 case ENOSPC:
415 return FILE_ERROR_NO_SPACE;
416 case ENOTDIR:
417 return FILE_ERROR_NOT_A_DIRECTORY;
418 default:
419 #if !defined(OS_NACL) // NaCl build has no metrics code.
420 UMA_HISTOGRAM_SPARSE_SLOWLY("PlatformFile.UnknownErrors.Posix",
421 saved_errno);
422 #endif
423 return FILE_ERROR_FAILED;
424 }
425 }
426
427 // NaCl doesn't implement system calls to open files directly.
428 #if !defined(OS_NACL)
429 // TODO(erikkay): does it make sense to support FLAG_EXCLUSIVE_* here?
DoInitialize(const FilePath & path,uint32_t flags)430 void File::DoInitialize(const FilePath& path, uint32_t flags) {
431 ThreadRestrictions::AssertIOAllowed();
432 DCHECK(!IsValid());
433
434 int open_flags = 0;
435 if (flags & FLAG_CREATE)
436 open_flags = O_CREAT | O_EXCL;
437
438 created_ = false;
439
440 if (flags & FLAG_CREATE_ALWAYS) {
441 DCHECK(!open_flags);
442 DCHECK(flags & FLAG_WRITE);
443 open_flags = O_CREAT | O_TRUNC;
444 }
445
446 if (flags & FLAG_OPEN_TRUNCATED) {
447 DCHECK(!open_flags);
448 DCHECK(flags & FLAG_WRITE);
449 open_flags = O_TRUNC;
450 }
451
452 if (!open_flags && !(flags & FLAG_OPEN) && !(flags & FLAG_OPEN_ALWAYS)) {
453 NOTREACHED();
454 errno = EOPNOTSUPP;
455 error_details_ = FILE_ERROR_FAILED;
456 return;
457 }
458
459 if (flags & FLAG_WRITE && flags & FLAG_READ) {
460 open_flags |= O_RDWR;
461 } else if (flags & FLAG_WRITE) {
462 open_flags |= O_WRONLY;
463 } else if (!(flags & FLAG_READ) &&
464 !(flags & FLAG_WRITE_ATTRIBUTES) &&
465 !(flags & FLAG_APPEND) &&
466 !(flags & FLAG_OPEN_ALWAYS)) {
467 NOTREACHED();
468 }
469
470 if (flags & FLAG_TERMINAL_DEVICE)
471 open_flags |= O_NOCTTY | O_NDELAY;
472
473 if (flags & FLAG_APPEND && flags & FLAG_READ)
474 open_flags |= O_APPEND | O_RDWR;
475 else if (flags & FLAG_APPEND)
476 open_flags |= O_APPEND | O_WRONLY;
477
478 static_assert(O_RDONLY == 0, "O_RDONLY must equal zero");
479
480 int mode = S_IRUSR | S_IWUSR;
481 #if defined(OS_CHROMEOS)
482 mode |= S_IRGRP | S_IROTH;
483 #endif
484
485 int descriptor = HANDLE_EINTR(open(path.value().c_str(), open_flags, mode));
486
487 if (flags & FLAG_OPEN_ALWAYS) {
488 if (descriptor < 0) {
489 open_flags |= O_CREAT;
490 if (flags & FLAG_EXCLUSIVE_READ || flags & FLAG_EXCLUSIVE_WRITE)
491 open_flags |= O_EXCL; // together with O_CREAT implies O_NOFOLLOW
492
493 descriptor = HANDLE_EINTR(open(path.value().c_str(), open_flags, mode));
494 if (descriptor >= 0)
495 created_ = true;
496 }
497 }
498
499 if (descriptor < 0) {
500 error_details_ = File::OSErrorToFileError(errno);
501 return;
502 }
503
504 if (flags & (FLAG_CREATE_ALWAYS | FLAG_CREATE))
505 created_ = true;
506
507 if (flags & FLAG_DELETE_ON_CLOSE)
508 unlink(path.value().c_str());
509
510 async_ = ((flags & FLAG_ASYNC) == FLAG_ASYNC);
511 error_details_ = FILE_OK;
512 file_.reset(descriptor);
513 }
514 #endif // !defined(OS_NACL)
515
DoFlush()516 bool File::DoFlush() {
517 ThreadRestrictions::AssertIOAllowed();
518 DCHECK(IsValid());
519
520 #if defined(OS_NACL)
521 NOTIMPLEMENTED(); // NaCl doesn't implement fsync.
522 return true;
523 #elif defined(OS_LINUX) || defined(OS_ANDROID)
524 return !HANDLE_EINTR(fdatasync(file_.get()));
525 #else
526 return !HANDLE_EINTR(fsync(file_.get()));
527 #endif
528 }
529
SetPlatformFile(PlatformFile file)530 void File::SetPlatformFile(PlatformFile file) {
531 DCHECK(!file_.is_valid());
532 file_.reset(file);
533 }
534
535 } // namespace base
536