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_ZOS) || \
29 defined(OS_ANDROID) && __ANDROID_API__ < 21
CallFstat(int fd,stat_wrapper_t * sb)30 int CallFstat(int fd, stat_wrapper_t* sb) {
31 return fstat(fd, sb);
32 }
33 #else
34 int CallFstat(int fd, stat_wrapper_t* sb) {
35 return fstat64(fd, sb);
36 }
37 #endif
38
39 // 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)
IsOpenAppend(PlatformFile file)42 bool IsOpenAppend(PlatformFile file) {
43 return (fcntl(file, F_GETFL) & O_APPEND) != 0;
44 }
45
CallFtruncate(PlatformFile file,int64_t length)46 int CallFtruncate(PlatformFile file, int64_t length) {
47 return HANDLE_EINTR(ftruncate(file, length));
48 }
49
50 #if !defined(OS_FUCHSIA)
CallFcntlFlock(PlatformFile file,bool do_lock)51 File::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
63 #else // !defined(OS_AIX)
64
IsOpenAppend(PlatformFile file)65 bool 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
CallFtruncate(PlatformFile file,int64_t length)72 int CallFtruncate(PlatformFile file, int64_t length) {
73 NOTIMPLEMENTED(); // NaCl doesn't implement ftruncate.
74 return 0;
75 }
76
CallFcntlFlock(PlatformFile file,bool do_lock)77 File::Error CallFcntlFlock(PlatformFile file, bool do_lock) {
78 NOTIMPLEMENTED(); // NaCl doesn't implement flock struct.
79 return File::FILE_ERROR_INVALID_OPERATION;
80 }
81 #endif // defined(OS_AIX)
82
83 } // namespace
84
FromStat(const stat_wrapper_t & stat_info)85 void 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
90 #if defined(OS_MACOSX)
91 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;
97 #elif defined(OS_AIX) || defined(OS_ZOS)
98 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;
104 #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;
111 #else
112 #error
113 #endif
114
115 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;
119 }
120
IsValid() const121 bool File::IsValid() const {
122 return file_.is_valid();
123 }
124
GetPlatformFile() const125 PlatformFile File::GetPlatformFile() const {
126 return file_.get();
127 }
128
TakePlatformFile()129 PlatformFile File::TakePlatformFile() {
130 return file_.release();
131 }
132
Close()133 void File::Close() {
134 if (!IsValid())
135 return;
136
137 file_.reset();
138 }
139
Seek(Whence whence,int64_t offset)140 int64_t File::Seek(Whence whence, int64_t offset) {
141 DCHECK(IsValid());
142
143 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));
146 }
147
Read(int64_t offset,char * data,int size)148 int File::Read(int64_t offset, char* data, int size) {
149 DCHECK(IsValid());
150 if (size < 0)
151 return -1;
152
153 int bytes_read = 0;
154 int rv;
155 do {
156 rv = HANDLE_EINTR(pread(file_.get(), data + bytes_read, size - bytes_read,
157 offset + bytes_read));
158 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
ReadAtCurrentPos(char * data,int size)167 int File::ReadAtCurrentPos(char* data, int size) {
168 DCHECK(IsValid());
169 if (size < 0)
170 return -1;
171
172 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
ReadNoBestEffort(int64_t offset,char * data,int size)185 int File::ReadNoBestEffort(int64_t offset, char* data, int size) {
186 DCHECK(IsValid());
187 return HANDLE_EINTR(pread(file_.get(), data, size, offset));
188 }
189
ReadAtCurrentPosNoBestEffort(char * data,int size)190 int File::ReadAtCurrentPosNoBestEffort(char* data, int size) {
191 DCHECK(IsValid());
192 if (size < 0)
193 return -1;
194
195 return HANDLE_EINTR(read(file_.get(), data, size));
196 }
197
Write(int64_t offset,const char * data,int size)198 int File::Write(int64_t offset, const char* data, int size) {
199 if (IsOpenAppend(file_.get()))
200 return WriteAtCurrentPos(data, size);
201
202 DCHECK(IsValid());
203 if (size < 0)
204 return -1;
205
206 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
WriteAtCurrentPos(const char * data,int size)220 int File::WriteAtCurrentPos(const char* data, int size) {
221 DCHECK(IsValid());
222 if (size < 0)
223 return -1;
224
225 int bytes_written = 0;
226 int rv;
227 do {
228 rv = HANDLE_EINTR(
229 write(file_.get(), data + bytes_written, size - bytes_written));
230 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
WriteAtCurrentPosNoBestEffort(const char * data,int size)239 int File::WriteAtCurrentPosNoBestEffort(const char* data, int size) {
240 DCHECK(IsValid());
241 if (size < 0)
242 return -1;
243
244 return HANDLE_EINTR(write(file_.get(), data, size));
245 }
246
GetLength()247 int64_t File::GetLength() {
248 DCHECK(IsValid());
249
250 stat_wrapper_t file_info;
251 if (CallFstat(file_.get(), &file_info))
252 return -1;
253
254 return file_info.st_size;
255 }
256
SetLength(int64_t length)257 bool File::SetLength(int64_t length) {
258 DCHECK(IsValid());
259
260 return !CallFtruncate(file_.get(), length);
261 }
262
GetInfo(Info * info)263 bool File::GetInfo(Info* info) {
264 DCHECK(IsValid());
265
266 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)
Lock()275 File::Error File::Lock() {
276 return CallFcntlFlock(file_.get(), true);
277 }
278
Unlock()279 File::Error File::Unlock() {
280 return CallFcntlFlock(file_.get(), false);
281 }
282 #endif
283
Duplicate() const284 File File::Duplicate() const {
285 if (!IsValid())
286 return File();
287
288 PlatformFile other_fd = HANDLE_EINTR(dup(GetPlatformFile()));
289 if (other_fd == -1)
290 return File(File::GetLastFileError());
291
292 File other(other_fd);
293 return other;
294 }
295
296 // Static.
OSErrorToFileError(int saved_errno)297 File::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:
305 case ETXTBSY:
306 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:
323 // This function should only be called for errors.
324 DCHECK_NE(0, saved_errno);
325 return FILE_ERROR_FAILED;
326 }
327 }
328
DoInitialize(const FilePath & path,uint32_t flags)329 void File::DoInitialize(const FilePath& path, uint32_t flags) {
330 DCHECK(!IsValid());
331
332 int open_flags = 0;
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 open_flags |= O_RDONLY;
353 } else {
354 NOTREACHED();
355 }
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 error_details_ = FILE_OK;
366 file_.reset(descriptor);
367 }
368
Flush()369 bool File::Flush() {
370 DCHECK(IsValid());
371
372 #if defined(OS_LINUX)
373 return !HANDLE_EINTR(fdatasync(file_.get()));
374 #else
375 return !HANDLE_EINTR(fsync(file_.get()));
376 #endif
377 }
378
SetPlatformFile(PlatformFile file)379 void File::SetPlatformFile(PlatformFile file) {
380 DCHECK(!file_.is_valid());
381 file_.reset(file);
382 }
383
384 // static
GetLastFileError()385 File::Error File::GetLastFileError() {
386 return base::File::OSErrorToFileError(errno);
387 }
388
389 } // namespace base
390