neovim

Neovim text editor
git clone https://git.dasho.dev/neovim.git
Log | Files | Refs | README

fileio.c (12235B)


      1 /// @file fileio.c
      2 ///
      3 /// Buffered reading/writing to a file. Unlike fileio.c this is not dealing with
      4 /// Nvim structures for buffer, with autocommands, etc: just fopen/fread/fwrite
      5 /// replacement.
      6 
      7 #include <assert.h>
      8 #include <fcntl.h>
      9 #include <stdbool.h>
     10 #include <stddef.h>
     11 #include <string.h>
     12 #include <uv.h>
     13 
     14 #include "auto/config.h"
     15 #include "nvim/log.h"
     16 #include "nvim/macros_defs.h"
     17 #include "nvim/memory.h"
     18 #include "nvim/os/fileio.h"
     19 #include "nvim/os/fs.h"
     20 #include "nvim/os/os_defs.h"
     21 #include "nvim/types_defs.h"
     22 
     23 #ifdef HAVE_SYS_UIO_H
     24 # include <sys/uio.h>
     25 #endif
     26 
     27 #include "os/fileio.c.generated.h"  // IWYU pragma: keep
     28 
     29 /// Open file
     30 ///
     31 /// @param[out]  ret_fp  Address where information needed for reading from or
     32 ///                      writing to a file is saved
     33 /// @param[in]  fname  File name to open.
     34 /// @param[in]  flags  Flags, @see FileOpenFlags. Currently reading from and
     35 ///                    writing to the file at once is not supported, so either
     36 ///                    kFileWriteOnly or kFileReadOnly is required.
     37 /// @param[in]  mode  Permissions for the newly created file (ignored if flags
     38 ///                   does not have kFileCreate\*).
     39 ///
     40 /// @return Error code, or 0 on success. @see os_strerror()
     41 int file_open(FileDescriptor *const ret_fp, const char *const fname, const int flags,
     42              const int mode)
     43  FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
     44 {
     45  int os_open_flags = 0;
     46  TriState wr = kNone;
     47 #define FLAG(flags, flag, fcntl_flags, wrval, cond) \
     48  do { \
     49    if (flags & flag) { \
     50      os_open_flags |= fcntl_flags; \
     51      assert(cond); \
     52      if (wrval != kNone) { \
     53        wr = wrval; \
     54      } \
     55    } \
     56  } while (0)
     57  FLAG(flags, kFileWriteOnly, O_WRONLY, kTrue, true);
     58  FLAG(flags, kFileCreateOnly, O_CREAT|O_EXCL|O_WRONLY, kTrue, true);
     59  FLAG(flags, kFileCreate, O_CREAT|O_WRONLY, kTrue, !(flags & kFileCreateOnly));
     60  FLAG(flags, kFileTruncate, O_TRUNC|O_WRONLY, kTrue,
     61       !(flags & kFileCreateOnly));
     62  FLAG(flags, kFileAppend, O_APPEND|O_WRONLY, kTrue,
     63       !(flags & kFileCreateOnly));
     64  FLAG(flags, kFileReadOnly, O_RDONLY, kFalse, wr != kTrue);
     65 #ifdef O_NOFOLLOW
     66  FLAG(flags, kFileNoSymlink, O_NOFOLLOW, kNone, true);
     67  FLAG(flags, kFileMkDir, O_CREAT|O_WRONLY, kTrue, !(flags & kFileCreateOnly));
     68 #endif
     69 #undef FLAG
     70  // wr is used for kFileReadOnly flag, but on
     71  // QB:neovim-qb-slave-ubuntu-12-04-64bit it still errors out with
     72  // `error: variable ‘wr’ set but not used [-Werror=unused-but-set-variable]`
     73  (void)wr;
     74 
     75  if (flags & kFileMkDir) {
     76    int mkdir_ret = os_file_mkdir((char *)fname, 0755);
     77    if (mkdir_ret < 0) {
     78      return mkdir_ret;
     79    }
     80  }
     81 
     82  const int fd = os_open(fname, os_open_flags, mode);
     83 
     84  if (fd < 0) {
     85    return fd;
     86  }
     87  return file_open_fd(ret_fp, fd, flags);
     88 }
     89 
     90 /// Wrap file descriptor with FileDescriptor structure
     91 ///
     92 /// @warning File descriptor wrapped like this must not be accessed by other
     93 ///          means.
     94 ///
     95 /// @param[out]  ret_fp  Address where information needed for reading from or
     96 ///                      writing to a file is saved
     97 /// @param[in]  fd  File descriptor to wrap.
     98 /// @param[in]  flags  Flags, @see FileOpenFlags. Currently reading from and
     99 ///                    writing to the file at once is not supported, so either
    100 ///                    FILE_WRITE_ONLY or FILE_READ_ONLY is required.
    101 ///
    102 /// @return Error code (@see os_strerror()) or 0. Currently always returns 0.
    103 int file_open_fd(FileDescriptor *const ret_fp, const int fd, const int flags)
    104  FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
    105 {
    106  ret_fp->wr = !!(flags & (kFileCreate
    107                           |kFileCreateOnly
    108                           |kFileTruncate
    109                           |kFileAppend
    110                           |kFileWriteOnly));
    111  ret_fp->non_blocking = !!(flags & kFileNonBlocking);
    112  // Non-blocking writes not supported currently.
    113  assert(!ret_fp->wr || !ret_fp->non_blocking);
    114  ret_fp->fd = fd;
    115  ret_fp->eof = false;
    116  ret_fp->buffer = alloc_block();
    117  ret_fp->read_pos = ret_fp->buffer;
    118  ret_fp->write_pos = ret_fp->buffer;
    119  ret_fp->bytes_read = 0;
    120  return 0;
    121 }
    122 
    123 /// Opens standard input as a FileDescriptor.
    124 int file_open_stdin(FileDescriptor *fp)
    125  FUNC_ATTR_WARN_UNUSED_RESULT
    126 {
    127  int error = file_open_fd(fp, os_open_stdin_fd(), kFileReadOnly|kFileNonBlocking);
    128  if (error != 0) {
    129    ELOG("failed to open stdin: %s", os_strerror(error));
    130  }
    131  return error;
    132 }
    133 
    134 /// opens buffer for reading
    135 void file_open_buffer(FileDescriptor *ret_fp, char *data, size_t len)
    136 {
    137  ret_fp->wr = false;
    138  ret_fp->non_blocking = false;
    139  ret_fp->fd = -1;
    140  ret_fp->eof = true;
    141  ret_fp->buffer = NULL;  // we don't take ownership
    142  ret_fp->read_pos = data;
    143  ret_fp->write_pos = data + len;
    144  ret_fp->bytes_read = 0;
    145 }
    146 
    147 /// Close file and free its buffer
    148 ///
    149 /// @param[in,out]  fp  File to close.
    150 /// @param[in]  do_fsync  If true, use fsync() to write changes to disk.
    151 ///
    152 /// @return 0 or error code.
    153 int file_close(FileDescriptor *const fp, const bool do_fsync)
    154  FUNC_ATTR_NONNULL_ALL
    155 {
    156  if (fp->fd < 0) {
    157    return 0;
    158  }
    159 
    160  const int flush_error = (do_fsync ? file_fsync(fp) : file_flush(fp));
    161  const int close_error = os_close(fp->fd);
    162  free_block(fp->buffer);
    163  if (close_error != 0) {
    164    return close_error;
    165  }
    166  return flush_error;
    167 }
    168 
    169 /// Flush file modifications to disk and run fsync()
    170 ///
    171 /// @param[in,out]  fp  File to work with.
    172 ///
    173 /// @return 0 or error code.
    174 int file_fsync(FileDescriptor *const fp)
    175  FUNC_ATTR_NONNULL_ALL
    176 {
    177  if (!fp->wr) {
    178    return 0;
    179  }
    180  const int flush_error = file_flush(fp);
    181  if (flush_error != 0) {
    182    return flush_error;
    183  }
    184  const int fsync_error = os_fsync(fp->fd);
    185  if (fsync_error != UV_EINVAL
    186      && fsync_error != UV_EROFS
    187      // fsync not supported on this storage.
    188      && fsync_error != UV_ENOTSUP) {
    189    return fsync_error;
    190  }
    191  return 0;
    192 }
    193 
    194 /// Flush file modifications to disk
    195 ///
    196 /// @param[in,out]  fp  File to work with.
    197 ///
    198 /// @return 0 or error code.
    199 int file_flush(FileDescriptor *fp)
    200  FUNC_ATTR_NONNULL_ALL
    201 {
    202  if (!fp->wr) {
    203    return 0;
    204  }
    205 
    206  ptrdiff_t to_write = fp->write_pos - fp->read_pos;
    207  if (to_write == 0) {
    208    return 0;
    209  }
    210  const ptrdiff_t wres = os_write(fp->fd, fp->read_pos, (size_t)to_write,
    211                                  fp->non_blocking);
    212  fp->read_pos = fp->write_pos = fp->buffer;
    213  if (wres != to_write) {
    214    return (wres >= 0) ? UV_EIO : (int)wres;
    215  }
    216  return 0;
    217 }
    218 
    219 /// Read from file
    220 ///
    221 /// @param[in,out]  fp  File to work with.
    222 /// @param[out]  ret_buf  Buffer to read to. Must not be NULL.
    223 /// @param[in]  size  Number of bytes to read. Buffer must have at least ret_buf
    224 ///                   bytes.
    225 ///
    226 /// @return error_code (< 0) or number of bytes read.
    227 ptrdiff_t file_read(FileDescriptor *const fp, char *const ret_buf, const size_t size)
    228  FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
    229 {
    230  assert(!fp->wr);
    231  size_t from_buffer = MIN((size_t)(fp->write_pos - fp->read_pos), size);
    232  memcpy(ret_buf, fp->read_pos, from_buffer);
    233 
    234  char *buf = ret_buf + from_buffer;
    235  size_t read_remaining = size - from_buffer;
    236  if (!read_remaining) {
    237    fp->bytes_read += from_buffer;
    238    fp->read_pos += from_buffer;
    239    return (ptrdiff_t)from_buffer;
    240  }
    241 
    242  // at this point, we have consumed all of an existing buffer. restart from the beginning
    243  fp->read_pos = fp->write_pos = fp->buffer;
    244 
    245 #ifdef HAVE_READV
    246  bool called_read = false;
    247  while (read_remaining) {
    248    // Allow only at most one os_read[v] call.
    249    if (fp->eof || (called_read && fp->non_blocking)) {
    250      break;
    251    }
    252    // If there is readv() syscall, then take an opportunity to populate
    253    // both target buffer and RBuffer at once, …
    254    struct iovec iov[] = {
    255      { .iov_base = buf, .iov_len = read_remaining },
    256      { .iov_base = fp->write_pos,
    257        .iov_len = ARENA_BLOCK_SIZE },
    258    };
    259    const ptrdiff_t r_ret = os_readv(fp->fd, &fp->eof, iov,
    260                                     ARRAY_SIZE(iov), fp->non_blocking);
    261    if (r_ret > 0) {
    262      if (r_ret > (ptrdiff_t)read_remaining) {
    263        fp->write_pos += (size_t)(r_ret - (ptrdiff_t)read_remaining);
    264        read_remaining = 0;
    265      } else {
    266        buf += r_ret;
    267        read_remaining -= (size_t)r_ret;
    268      }
    269    } else if (r_ret < 0) {
    270      return r_ret;
    271    }
    272    called_read = true;
    273  }
    274 #else
    275  if (fp->eof) {
    276    // already eof, cannot read more
    277  } else if (read_remaining >= ARENA_BLOCK_SIZE) {
    278    // …otherwise leave fp->buffer empty and populate only target buffer,
    279    // because filtering information through rbuffer will be more syscalls.
    280    const ptrdiff_t r_ret = os_read(fp->fd, &fp->eof, buf, read_remaining,
    281                                    fp->non_blocking);
    282    if (r_ret >= 0) {
    283      read_remaining -= (size_t)r_ret;
    284    } else if (r_ret < 0) {
    285      return r_ret;
    286    }
    287  } else {
    288    const ptrdiff_t r_ret = os_read(fp->fd, &fp->eof,
    289                                    fp->write_pos,
    290                                    ARENA_BLOCK_SIZE, fp->non_blocking);
    291    if (r_ret < 0) {
    292      return r_ret;
    293    } else {
    294      fp->write_pos += r_ret;
    295      size_t to_copy = MIN((size_t)r_ret, read_remaining);
    296      memcpy(buf, fp->read_pos, to_copy);
    297      fp->read_pos += to_copy;
    298      read_remaining -= to_copy;
    299    }
    300  }
    301 #endif
    302 
    303  fp->bytes_read += (size - read_remaining);
    304  return (ptrdiff_t)(size - read_remaining);
    305 }
    306 
    307 /// try to read already buffered data in place
    308 ///
    309 /// @return NULL if enough data is not available
    310 ///         valid pointer to chunk of "size". pointer becomes invalid in the next "file_read" call!
    311 char *file_try_read_buffered(FileDescriptor *const fp, const size_t size)
    312  FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
    313 {
    314  if ((size_t)(fp->write_pos - fp->read_pos) >= size) {
    315    char *ret = fp->read_pos;
    316    fp->read_pos += size;
    317    fp->bytes_read += size;
    318    return ret;
    319  }
    320  return NULL;
    321 }
    322 
    323 /// Write to a file
    324 ///
    325 /// @param[in]  fd  File descriptor to write to.
    326 /// @param[in]  buf  Data to write. May be NULL if size is zero.
    327 /// @param[in]  size  Amount of bytes to write.
    328 ///
    329 /// @return Number of bytes written or libuv error code (< 0).
    330 ptrdiff_t file_write(FileDescriptor *const fp, const char *const buf, const size_t size)
    331  FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ARG(1)
    332 {
    333  assert(fp->wr);
    334  // includes the trivial case of size==0
    335  if (size < file_space(fp)) {
    336    memcpy(fp->write_pos, buf, size);
    337    fp->write_pos += size;
    338    return (ptrdiff_t)size;
    339  }
    340 
    341  // TODO(bfredl): just as for reading, use iovec to combine fp->buffer with buf
    342  int status = file_flush(fp);
    343  if (status < 0) {
    344    return status;
    345  }
    346 
    347  if (size < ARENA_BLOCK_SIZE) {
    348    memcpy(fp->write_pos, buf, size);
    349    fp->write_pos += size;
    350    return (ptrdiff_t)size;
    351  }
    352 
    353  const ptrdiff_t wres = os_write(fp->fd, buf, size,
    354                                  fp->non_blocking);
    355  return (wres != (ptrdiff_t)size && wres >= 0) ? UV_EIO : wres;
    356 }
    357 
    358 /// Skip some bytes
    359 ///
    360 /// This is like `fseek(fp, size, SEEK_CUR)`, but actual implementation simply
    361 /// reads to the buffer and discards the result.
    362 ptrdiff_t file_skip(FileDescriptor *const fp, const size_t size)
    363  FUNC_ATTR_NONNULL_ALL
    364 {
    365  assert(!fp->wr);
    366  size_t from_buffer = MIN((size_t)(fp->write_pos - fp->read_pos), size);
    367  size_t skip_remaining = size - from_buffer;
    368  if (skip_remaining == 0) {
    369    fp->read_pos += from_buffer;
    370    fp->bytes_read += from_buffer;
    371    return (ptrdiff_t)from_buffer;
    372  }
    373 
    374  fp->read_pos = fp->write_pos = fp->buffer;
    375  bool called_read = false;
    376  while (skip_remaining > 0) {
    377    // Allow only at most one os_read[v] call.
    378    if (fp->eof || (called_read && fp->non_blocking)) {
    379      break;
    380    }
    381    const ptrdiff_t r_ret = os_read(fp->fd, &fp->eof, fp->buffer, ARENA_BLOCK_SIZE,
    382                                    fp->non_blocking);
    383    if (r_ret < 0) {
    384      return r_ret;
    385    } else if ((size_t)r_ret > skip_remaining) {
    386      fp->read_pos = fp->buffer + skip_remaining;
    387      fp->write_pos = fp->buffer + r_ret;
    388      fp->bytes_read += size;
    389      return (ptrdiff_t)size;
    390    }
    391    skip_remaining -= (size_t)r_ret;
    392    called_read = true;
    393  }
    394 
    395  fp->bytes_read += size - skip_remaining;
    396  return (ptrdiff_t)(size - skip_remaining);
    397 }