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 }