Coverage Report

Created: 2025-05-08 06:06

/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