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/posix/eintr_wrapper.h"
15 #include "base/strings/utf_string_conversions.h"
16 #include "util/build_config.h"
17
18 namespace base {
19
20 // Make sure our Whence mappings match the system headers.
21 static_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
25 namespace {
26
27 #if defined(OS_BSD) || defined(OS_MACOSX) || defined(OS_NACL) || \
28 defined(OS_HAIKU) || defined(OS_MSYS) || defined(OS_ANDROID) && __ANDROID_API__ < 21
CallFstat(int fd,stat_wrapper_t * sb)29 int CallFstat(int fd, stat_wrapper_t* sb) {
30 return fstat(fd, sb);
31 }
32 #else
33 int CallFstat(int fd, stat_wrapper_t* sb) {
34 return fstat64(fd, sb);
35 }
36 #endif
37
38 // Some systems don't provide the following system calls, so either simulate
39 // them or wrap them in order to minimize the number of #ifdef's in this file.
40 #if !defined(OS_AIX)
IsOpenAppend(PlatformFile file)41 bool IsOpenAppend(PlatformFile file) {
42 return (fcntl(file, F_GETFL) & O_APPEND) != 0;
43 }
44
CallFtruncate(PlatformFile file,int64_t length)45 int CallFtruncate(PlatformFile file, int64_t length) {
46 return HANDLE_EINTR(ftruncate(file, length));
47 }
48
49 #if !defined(OS_FUCHSIA)
CallFcntlFlock(PlatformFile file,bool do_lock)50 File::Error CallFcntlFlock(PlatformFile file, bool do_lock) {
51 struct flock lock;
52 lock.l_type = do_lock ? F_WRLCK : F_UNLCK;
53 lock.l_whence = SEEK_SET;
54 lock.l_start = 0;
55 lock.l_len = 0; // Lock entire file.
56 if (HANDLE_EINTR(fcntl(file, F_SETLK, &lock)) == -1)
57 return File::GetLastFileError();
58 return File::FILE_OK;
59 }
60 #endif
61
62 #else // !defined(OS_AIX)
63
IsOpenAppend(PlatformFile file)64 bool IsOpenAppend(PlatformFile file) {
65 // NaCl doesn't implement fcntl. Since NaCl's write conforms to the POSIX
66 // standard and always appends if the file is opened with O_APPEND, just
67 // return false here.
68 return false;
69 }
70
CallFtruncate(PlatformFile file,int64_t length)71 int CallFtruncate(PlatformFile file, int64_t length) {
72 NOTIMPLEMENTED(); // NaCl doesn't implement ftruncate.
73 return 0;
74 }
75
CallFcntlFlock(PlatformFile file,bool do_lock)76 File::Error CallFcntlFlock(PlatformFile file, bool do_lock) {
77 NOTIMPLEMENTED(); // NaCl doesn't implement flock struct.
78 return File::FILE_ERROR_INVALID_OPERATION;
79 }
80 #endif // defined(OS_AIX)
81
82 } // namespace
83
FromStat(const stat_wrapper_t & stat_info)84 void File::Info::FromStat(const stat_wrapper_t& stat_info) {
85 is_directory = S_ISDIR(stat_info.st_mode);
86 is_symbolic_link = S_ISLNK(stat_info.st_mode);
87 size = stat_info.st_size;
88
89 #if defined(OS_MACOSX)
90 time_t last_modified_sec = stat_info.st_mtimespec.tv_sec;
91 int64_t last_modified_nsec = stat_info.st_mtimespec.tv_nsec;
92 time_t last_accessed_sec = stat_info.st_atimespec.tv_sec;
93 int64_t last_accessed_nsec = stat_info.st_atimespec.tv_nsec;
94 time_t creation_time_sec = stat_info.st_ctimespec.tv_sec;
95 int64_t creation_time_nsec = stat_info.st_ctimespec.tv_nsec;
96 #elif defined(OS_AIX)
97 time_t last_modified_sec = stat_info.st_mtime;
98 int64_t last_modified_nsec = 0;
99 time_t last_accessed_sec = stat_info.st_atime;
100 int64_t last_accessed_nsec = 0;
101 time_t creation_time_sec = stat_info.st_ctime;
102 int64_t creation_time_nsec = 0;
103 #elif defined(OS_POSIX)
104 time_t last_modified_sec = stat_info.st_mtim.tv_sec;
105 int64_t last_modified_nsec = stat_info.st_mtim.tv_nsec;
106 time_t last_accessed_sec = stat_info.st_atim.tv_sec;
107 int64_t last_accessed_nsec = stat_info.st_atim.tv_nsec;
108 time_t creation_time_sec = stat_info.st_ctim.tv_sec;
109 int64_t creation_time_nsec = stat_info.st_ctim.tv_nsec;
110 #else
111 #error
112 #endif
113
114 constexpr uint64_t kNano = 1'000'000'000;
115 last_modified = last_modified_sec * kNano + last_modified_nsec;
116 last_accessed = last_accessed_sec * kNano + last_accessed_nsec;
117 creation_time = creation_time_sec * kNano + creation_time_nsec;
118 }
119
IsValid() const120 bool File::IsValid() const {
121 return file_.is_valid();
122 }
123
GetPlatformFile() const124 PlatformFile File::GetPlatformFile() const {
125 return file_.get();
126 }
127
TakePlatformFile()128 PlatformFile File::TakePlatformFile() {
129 return file_.release();
130 }
131
Close()132 void File::Close() {
133 if (!IsValid())
134 return;
135
136 file_.reset();
137 }
138
Seek(Whence whence,int64_t offset)139 int64_t File::Seek(Whence whence, int64_t offset) {
140 DCHECK(IsValid());
141
142 static_assert(sizeof(int64_t) == sizeof(off_t), "off_t must be 64 bits");
143 return lseek(file_.get(), static_cast<off_t>(offset),
144 static_cast<int>(whence));
145 }
146
Read(int64_t offset,char * data,int size)147 int File::Read(int64_t offset, char* data, int size) {
148 DCHECK(IsValid());
149 if (size < 0)
150 return -1;
151
152 int bytes_read = 0;
153 int rv;
154 do {
155 rv = HANDLE_EINTR(pread(file_.get(), data + bytes_read, size - bytes_read,
156 offset + bytes_read));
157 if (rv <= 0)
158 break;
159
160 bytes_read += rv;
161 } while (bytes_read < size);
162
163 return bytes_read ? bytes_read : rv;
164 }
165
ReadAtCurrentPos(char * data,int size)166 int File::ReadAtCurrentPos(char* data, int size) {
167 DCHECK(IsValid());
168 if (size < 0)
169 return -1;
170
171 int bytes_read = 0;
172 int rv;
173 do {
174 rv = HANDLE_EINTR(read(file_.get(), data + bytes_read, size - bytes_read));
175 if (rv <= 0)
176 break;
177
178 bytes_read += rv;
179 } while (bytes_read < size);
180
181 return bytes_read ? bytes_read : rv;
182 }
183
ReadNoBestEffort(int64_t offset,char * data,int size)184 int File::ReadNoBestEffort(int64_t offset, char* data, int size) {
185 DCHECK(IsValid());
186 return HANDLE_EINTR(pread(file_.get(), data, size, offset));
187 }
188
ReadAtCurrentPosNoBestEffort(char * data,int size)189 int File::ReadAtCurrentPosNoBestEffort(char* data, int size) {
190 DCHECK(IsValid());
191 if (size < 0)
192 return -1;
193
194 return HANDLE_EINTR(read(file_.get(), data, size));
195 }
196
Write(int64_t offset,const char * data,int size)197 int File::Write(int64_t offset, const char* data, int size) {
198 if (IsOpenAppend(file_.get()))
199 return WriteAtCurrentPos(data, size);
200
201 DCHECK(IsValid());
202 if (size < 0)
203 return -1;
204
205 int bytes_written = 0;
206 int rv;
207 do {
208 rv = HANDLE_EINTR(pwrite(file_.get(), data + bytes_written,
209 size - bytes_written, offset + bytes_written));
210 if (rv <= 0)
211 break;
212
213 bytes_written += rv;
214 } while (bytes_written < size);
215
216 return bytes_written ? bytes_written : rv;
217 }
218
WriteAtCurrentPos(const char * data,int size)219 int File::WriteAtCurrentPos(const char* data, int size) {
220 DCHECK(IsValid());
221 if (size < 0)
222 return -1;
223
224 int bytes_written = 0;
225 int rv;
226 do {
227 rv = HANDLE_EINTR(
228 write(file_.get(), data + bytes_written, size - bytes_written));
229 if (rv <= 0)
230 break;
231
232 bytes_written += rv;
233 } while (bytes_written < size);
234
235 return bytes_written ? bytes_written : rv;
236 }
237
WriteAtCurrentPosNoBestEffort(const char * data,int size)238 int File::WriteAtCurrentPosNoBestEffort(const char* data, int size) {
239 DCHECK(IsValid());
240 if (size < 0)
241 return -1;
242
243 return HANDLE_EINTR(write(file_.get(), data, size));
244 }
245
GetLength()246 int64_t File::GetLength() {
247 DCHECK(IsValid());
248
249 stat_wrapper_t file_info;
250 if (CallFstat(file_.get(), &file_info))
251 return -1;
252
253 return file_info.st_size;
254 }
255
SetLength(int64_t length)256 bool File::SetLength(int64_t length) {
257 DCHECK(IsValid());
258
259 return !CallFtruncate(file_.get(), length);
260 }
261
GetInfo(Info * info)262 bool File::GetInfo(Info* info) {
263 DCHECK(IsValid());
264
265 stat_wrapper_t file_info;
266 if (CallFstat(file_.get(), &file_info))
267 return false;
268
269 info->FromStat(file_info);
270 return true;
271 }
272
273 #if !defined(OS_FUCHSIA)
Lock()274 File::Error File::Lock() {
275 return CallFcntlFlock(file_.get(), true);
276 }
277
Unlock()278 File::Error File::Unlock() {
279 return CallFcntlFlock(file_.get(), false);
280 }
281 #endif
282
Duplicate() const283 File File::Duplicate() const {
284 if (!IsValid())
285 return File();
286
287 PlatformFile other_fd = HANDLE_EINTR(dup(GetPlatformFile()));
288 if (other_fd == -1)
289 return File(File::GetLastFileError());
290
291 File other(other_fd);
292 return other;
293 }
294
295 // Static.
OSErrorToFileError(int saved_errno)296 File::Error File::OSErrorToFileError(int saved_errno) {
297 switch (saved_errno) {
298 case EACCES:
299 case EISDIR:
300 case EROFS:
301 case EPERM:
302 return FILE_ERROR_ACCESS_DENIED;
303 case EBUSY:
304 case ETXTBSY:
305 return FILE_ERROR_IN_USE;
306 case EEXIST:
307 return FILE_ERROR_EXISTS;
308 case EIO:
309 return FILE_ERROR_IO;
310 case ENOENT:
311 return FILE_ERROR_NOT_FOUND;
312 case ENFILE: // fallthrough
313 case EMFILE:
314 return FILE_ERROR_TOO_MANY_OPENED;
315 case ENOMEM:
316 return FILE_ERROR_NO_MEMORY;
317 case ENOSPC:
318 return FILE_ERROR_NO_SPACE;
319 case ENOTDIR:
320 return FILE_ERROR_NOT_A_DIRECTORY;
321 default:
322 // This function should only be called for errors.
323 DCHECK_NE(0, saved_errno);
324 return FILE_ERROR_FAILED;
325 }
326 }
327
DoInitialize(const FilePath & path,uint32_t flags)328 void File::DoInitialize(const FilePath& path, uint32_t flags) {
329 DCHECK(!IsValid());
330
331 int open_flags = 0;
332 created_ = false;
333
334 if (flags & FLAG_CREATE_ALWAYS) {
335 DCHECK(!open_flags);
336 DCHECK(flags & FLAG_WRITE);
337 open_flags = O_CREAT | O_TRUNC;
338 }
339
340 if (!open_flags && !(flags & FLAG_OPEN)) {
341 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;
351 } else if (!(flags & FLAG_READ)) {
352 NOTREACHED();
353 }
354
355 static_assert(O_RDONLY == 0, "O_RDONLY must equal zero");
356
357 int mode = S_IRUSR | S_IWUSR;
358 int descriptor = HANDLE_EINTR(open(path.value().c_str(), open_flags, mode));
359
360 if (descriptor < 0) {
361 error_details_ = File::GetLastFileError();
362 return;
363 }
364
365 if (flags & FLAG_CREATE_ALWAYS)
366 created_ = true;
367
368 error_details_ = FILE_OK;
369 file_.reset(descriptor);
370 }
371
Flush()372 bool File::Flush() {
373 DCHECK(IsValid());
374
375 #if defined(OS_LINUX) || defined(OS_BSD) && !defined(OS_DRAGONFLY)
376 return !HANDLE_EINTR(fdatasync(file_.get()));
377 #else
378 return !HANDLE_EINTR(fsync(file_.get()));
379 #endif
380 }
381
SetPlatformFile(PlatformFile file)382 void File::SetPlatformFile(PlatformFile file) {
383 DCHECK(!file_.is_valid());
384 file_.reset(file);
385 }
386
387 // static
GetLastFileError()388 File::Error File::GetLastFileError() {
389 return base::File::OSErrorToFileError(errno);
390 }
391
392 } // namespace base
393