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() |
47 | int 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. |
102 | int 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. |
134 | FileDescriptor *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. |
155 | FileDescriptor *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. |
173 | int 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. |
191 | int 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. |
204 | int 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. |
221 | int 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. |
244 | static 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. |
252 | static 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. |
280 | ptrdiff_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). |
365 | ptrdiff_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. |
383 | static 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. |
389 | ptrdiff_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. |
415 | int 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). |
431 | int 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 | |