1// This is an open source non-commercial project. Dear PVS-Studio, please check
2// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
3
4/// @file fileio.c
5///
6/// Buffered reading/writing to a file. Unlike fileio.c this is not dealing with
7/// Nvim stuctures for buffer, with autocommands, etc: just fopen/fread/fwrite
8/// replacement.
9
10#include <assert.h>
11#include <stddef.h>
12#include <stdbool.h>
13#include <fcntl.h>
14
15#include "auto/config.h"
16
17#ifdef HAVE_SYS_UIO_H
18# include <sys/uio.h>
19#endif
20
21#include <uv.h>
22
23#include "nvim/os/fileio.h"
24#include "nvim/memory.h"
25#include "nvim/os/os.h"
26#include "nvim/globals.h"
27#include "nvim/rbuffer.h"
28#include "nvim/macros.h"
29#include "nvim/message.h"
30
31#ifdef INCLUDE_GENERATED_DECLARATIONS
32# include "os/fileio.c.generated.h"
33#endif
34
35/// Open file
36///
37/// @param[out] ret_fp Address where information needed for reading from or
38/// writing to a file is saved
39/// @param[in] fname File name to open.
40/// @param[in] flags Flags, @see FileOpenFlags. Currently reading from and
41/// writing to the file at once is not supported, so either
42/// kFileWriteOnly or kFileReadOnly is required.
43/// @param[in] mode Permissions for the newly created file (ignored if flags
44/// does not have kFileCreate\*).
45///
46/// @return Error code, or 0 on success. @see os_strerror()
47int file_open(FileDescriptor *const ret_fp, const char *const fname,
48 const int flags, const int mode)
49 FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
50{
51 int os_open_flags = 0;
52 TriState wr = kNone;
53 // -V:FLAG:501
54#define FLAG(flags, flag, fcntl_flags, wrval, cond) \
55 do { \
56 if (flags & flag) { \
57 os_open_flags |= fcntl_flags; \
58 assert(cond); \
59 if (wrval != kNone) { \
60 wr = wrval; \
61 } \
62 } \
63 } while (0)
64 FLAG(flags, kFileWriteOnly, O_WRONLY, kTrue, true);
65 FLAG(flags, kFileCreateOnly, O_CREAT|O_EXCL|O_WRONLY, kTrue, true);
66 FLAG(flags, kFileCreate, O_CREAT|O_WRONLY, kTrue, !(flags & kFileCreateOnly));
67 FLAG(flags, kFileTruncate, O_TRUNC|O_WRONLY, kTrue,
68 !(flags & kFileCreateOnly));
69 FLAG(flags, kFileAppend, O_APPEND|O_WRONLY, kTrue,
70 !(flags & kFileCreateOnly));
71 FLAG(flags, kFileReadOnly, O_RDONLY, kFalse, wr != kTrue);
72#ifdef O_NOFOLLOW
73 FLAG(flags, kFileNoSymlink, O_NOFOLLOW, kNone, true);
74#endif
75#undef FLAG
76 // wr is used for kFileReadOnly flag, but on
77 // QB:neovim-qb-slave-ubuntu-12-04-64bit it still errors out with
78 // `error: variable ‘wr’ set but not used [-Werror=unused-but-set-variable]`
79 (void)wr;
80
81 const int fd = os_open(fname, os_open_flags, mode);
82
83 if (fd < 0) {
84 return fd;
85 }
86 return file_open_fd(ret_fp, fd, flags);
87}
88
89/// Wrap file descriptor with FileDescriptor structure
90///
91/// @warning File descriptor wrapped like this must not be accessed by other
92/// means.
93///
94/// @param[out] ret_fp Address where information needed for reading from or
95/// writing to a file is saved
96/// @param[in] fd File descriptor to wrap.
97/// @param[in] flags Flags, @see FileOpenFlags. Currently reading from and
98/// writing to the file at once is not supported, so either
99/// FILE_WRITE_ONLY or FILE_READ_ONLY is required.
100///
101/// @return Error code (@see os_strerror()) or 0. Currently always returns 0.
102int file_open_fd(FileDescriptor *const ret_fp, const int fd,
103 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->rv = rbuffer_new(kRWBufferSize);
117 ret_fp->_error = 0;
118 if (ret_fp->wr) {
119 ret_fp->rv->data = ret_fp;
120 ret_fp->rv->full_cb = (rbuffer_callback)&file_rb_write_full_cb;
121 }
122 return 0;
123}
124
125/// Like file_open(), but allocate and return ret_fp
126///
127/// @param[out] error Error code, or 0 on success. @see os_strerror()
128/// @param[in] fname File name to open.
129/// @param[in] flags Flags, @see FileOpenFlags.
130/// @param[in] mode Permissions for the newly created file (ignored if flags
131/// does not have kFileCreate\*).
132///
133/// @return [allocated] Opened file or NULL in case of error.
134FileDescriptor *file_open_new(int *const error, const char *const fname,
135 const int flags, const int mode)
136 FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
137{
138 FileDescriptor *const fp = xmalloc(sizeof(*fp));
139 if ((*error = file_open(fp, fname, flags, mode)) != 0) {
140 xfree(fp);
141 return NULL;
142 }
143 return fp;
144}
145
146/// Like file_open_fd(), but allocate and return ret_fp
147///
148/// @param[out] error Error code, or 0 on success. @see os_strerror()
149/// @param[in] fd File descriptor to wrap.
150/// @param[in] flags Flags, @see FileOpenFlags.
151/// @param[in] mode Permissions for the newly created file (ignored if flags
152/// does not have FILE_CREATE\*).
153///
154/// @return [allocated] Opened file or NULL in case of error.
155FileDescriptor *file_open_fd_new(int *const error, const int fd,
156 const int flags)
157 FUNC_ATTR_NONNULL_ALL FUNC_ATTR_MALLOC FUNC_ATTR_WARN_UNUSED_RESULT
158{
159 FileDescriptor *const fp = xmalloc(sizeof(*fp));
160 if ((*error = file_open_fd(fp, fd, flags)) != 0) {
161 xfree(fp);
162 return NULL;
163 }
164 return fp;
165}
166
167/// Close file and free its buffer
168///
169/// @param[in,out] fp File to close.
170/// @param[in] do_fsync If true, use fsync() to write changes to disk.
171///
172/// @return 0 or error code.
173int file_close(FileDescriptor *const fp, const bool do_fsync)
174 FUNC_ATTR_NONNULL_ALL
175{
176 const int flush_error = (do_fsync ? file_fsync(fp) : file_flush(fp));
177 const int close_error = os_close(fp->fd);
178 rbuffer_free(fp->rv);
179 if (close_error != 0) {
180 return close_error;
181 }
182 return flush_error;
183}
184
185/// Close and free file obtained using file_open_new()
186///
187/// @param[in,out] fp File to close.
188/// @param[in] do_fsync If true, use fsync() to write changes to disk.
189///
190/// @return 0 or error code.
191int file_free(FileDescriptor *const fp, const bool do_fsync)
192 FUNC_ATTR_NONNULL_ALL
193{
194 const int ret = file_close(fp, do_fsync);
195 xfree(fp);
196 return ret;
197}
198
199/// Flush file modifications to disk
200///
201/// @param[in,out] fp File to work with.
202///
203/// @return 0 or error code.
204int file_flush(FileDescriptor *const fp)
205 FUNC_ATTR_NONNULL_ALL
206{
207 if (!fp->wr) {
208 return 0;
209 }
210 file_rb_write_full_cb(fp->rv, fp);
211 const int error = fp->_error;
212 fp->_error = 0;
213 return error;
214}
215
216/// Flush file modifications to disk and run fsync()
217///
218/// @param[in,out] fp File to work with.
219///
220/// @return 0 or error code.
221int file_fsync(FileDescriptor *const fp)
222 FUNC_ATTR_NONNULL_ALL
223{
224 if (!fp->wr) {
225 return 0;
226 }
227 const int flush_error = file_flush(fp);
228 if (flush_error != 0) {
229 return flush_error;
230 }
231 const int fsync_error = os_fsync(fp->fd);
232 if (fsync_error != UV_EINVAL
233 && fsync_error != UV_EROFS
234 // fsync not supported on this storage.
235 && fsync_error != UV_ENOTSUP) {
236 return fsync_error;
237 }
238 return 0;
239}
240
241/// Buffer used for writing
242///
243/// Like IObuff, but allows file_\* callers not to care about spoiling it.
244static char writebuf[kRWBufferSize];
245
246/// Function run when RBuffer is full when writing to a file
247///
248/// Actually does writing to the file, may also be invoked directly.
249///
250/// @param[in,out] rv RBuffer instance used.
251/// @param[in,out] fp File to work with.
252static void file_rb_write_full_cb(RBuffer *const rv, FileDescriptor *const fp)
253 FUNC_ATTR_NONNULL_ALL
254{
255 assert(fp->wr);
256 assert(rv->data == (void *)fp);
257 if (rbuffer_size(rv) == 0) {
258 return;
259 }
260 const size_t read_bytes = rbuffer_read(rv, writebuf, kRWBufferSize);
261 const ptrdiff_t wres = os_write(fp->fd, writebuf, read_bytes,
262 fp->non_blocking);
263 if (wres != (ptrdiff_t)read_bytes) {
264 if (wres >= 0) {
265 fp->_error = UV_EIO;
266 } else {
267 fp->_error = (int)wres;
268 }
269 }
270}
271
272/// Read from file
273///
274/// @param[in,out] fp File to work with.
275/// @param[out] ret_buf Buffer to read to. Must not be NULL.
276/// @param[in] size Number of bytes to read. Buffer must have at least ret_buf
277/// bytes.
278///
279/// @return error_code (< 0) or number of bytes read.
280ptrdiff_t file_read(FileDescriptor *const fp, char *const ret_buf,
281 const size_t size)
282 FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
283{
284 assert(!fp->wr);
285 char *buf = ret_buf;
286 size_t read_remaining = size;
287 RBuffer *const rv = fp->rv;
288 bool called_read = false;
289 while (read_remaining) {
290 const size_t rv_size = rbuffer_size(rv);
291 if (rv_size > 0) {
292 const size_t rsize = rbuffer_read(rv, buf, MIN(rv_size, read_remaining));
293 buf += rsize;
294 read_remaining -= rsize;
295 }
296 if (fp->eof
297 // Allow only at most one os_read[v] call.
298 || (called_read && fp->non_blocking)) {
299 break;
300 }
301 if (read_remaining) {
302 assert(rbuffer_size(rv) == 0);
303 rbuffer_reset(rv);
304#ifdef HAVE_READV
305 // If there is readv() syscall, then take an opportunity to populate
306 // both target buffer and RBuffer at once, …
307 size_t write_count;
308 struct iovec iov[] = {
309 { .iov_base = buf, .iov_len = read_remaining },
310 { .iov_base = rbuffer_write_ptr(rv, &write_count),
311 .iov_len = kRWBufferSize },
312 };
313 assert(write_count == kRWBufferSize);
314 const ptrdiff_t r_ret = os_readv(fp->fd, &fp->eof, iov,
315 ARRAY_SIZE(iov), fp->non_blocking);
316 if (r_ret > 0) {
317 if (r_ret > (ptrdiff_t)read_remaining) {
318 rbuffer_produced(rv, (size_t)(r_ret - (ptrdiff_t)read_remaining));
319 read_remaining = 0;
320 } else {
321 buf += (size_t)r_ret;
322 read_remaining -= (size_t)r_ret;
323 }
324 } else if (r_ret < 0) {
325 return r_ret;
326 }
327#else
328 if (read_remaining >= kRWBufferSize) {
329 // …otherwise leave RBuffer empty and populate only target buffer,
330 // because filtering information through rbuffer will be more syscalls.
331 const ptrdiff_t r_ret = os_read(fp->fd, &fp->eof, buf, read_remaining,
332 fp->non_blocking);
333 if (r_ret >= 0) {
334 read_remaining -= (size_t)r_ret;
335 return (ptrdiff_t)(size - read_remaining);
336 } else if (r_ret < 0) {
337 return r_ret;
338 }
339 } else {
340 size_t write_count;
341 const ptrdiff_t r_ret = os_read(fp->fd, &fp->eof,
342 rbuffer_write_ptr(rv, &write_count),
343 kRWBufferSize, fp->non_blocking);
344 assert(write_count == kRWBufferSize);
345 if (r_ret > 0) {
346 rbuffer_produced(rv, (size_t)r_ret);
347 } else if (r_ret < 0) {
348 return r_ret;
349 }
350 }
351#endif
352 called_read = true;
353 }
354 }
355 return (ptrdiff_t)(size - read_remaining);
356}
357
358/// Write to a file
359///
360/// @param[in] fd File descriptor to write to.
361/// @param[in] buf Data to write. May be NULL if size is zero.
362/// @param[in] size Amount of bytes to write.
363///
364/// @return Number of bytes written or libuv error code (< 0).
365ptrdiff_t file_write(FileDescriptor *const fp, const char *const buf,
366 const size_t size)
367 FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ARG(1)
368{
369 assert(fp->wr);
370 const size_t written = rbuffer_write(fp->rv, buf, size);
371 if (fp->_error != 0) {
372 const int error = fp->_error;
373 fp->_error = 0;
374 return error;
375 } else if (written != size) {
376 return UV_EIO;
377 }
378 return (ptrdiff_t)written;
379}
380
381/// Buffer used for skipping. Its contents is undefined and should never be
382/// used.
383static char skipbuf[kRWBufferSize];
384
385/// Skip some bytes
386///
387/// This is like `fseek(fp, size, SEEK_CUR)`, but actual implementation simply
388/// reads to a buffer and discards the result.
389ptrdiff_t file_skip(FileDescriptor *const fp, const size_t size)
390 FUNC_ATTR_NONNULL_ALL
391{
392 assert(!fp->wr);
393 size_t read_bytes = 0;
394 do {
395 const ptrdiff_t new_read_bytes = file_read(
396 fp, skipbuf, MIN(size - read_bytes, sizeof(skipbuf)));
397 if (new_read_bytes < 0) {
398 return new_read_bytes;
399 } else if (new_read_bytes == 0) {
400 break;
401 }
402 read_bytes += (size_t)new_read_bytes;
403 } while (read_bytes < size && !file_eof(fp));
404
405 return (ptrdiff_t)read_bytes;
406}
407
408/// Msgpack callback for writing to a file
409///
410/// @param data File to write to.
411/// @param[in] buf Data to write.
412/// @param[in] len Length of the data to write.
413///
414/// @return 0 in case of success, -1 in case of error.
415int msgpack_file_write(void *data, const char *buf, size_t len)
416 FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
417{
418 assert(len < PTRDIFF_MAX);
419 const ptrdiff_t written_bytes = file_write((FileDescriptor *)data, buf, len);
420 if (written_bytes < 0) {
421 return msgpack_file_write_error((int)written_bytes);
422 }
423 return 0;
424}
425
426/// Print error which occurs when failing to write msgpack data
427///
428/// @param[in] error Error code of the error to print.
429///
430/// @return -1 (error return for msgpack_packer callbacks).
431int msgpack_file_write_error(const int error)
432{
433 emsgf(_("E5420: Failed to write to file: %s"), os_strerror(error));
434 return -1;
435}
436