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