aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/os/fileio.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nvim/os/fileio.c')
-rw-r--r--src/nvim/os/fileio.c175
1 files changed, 144 insertions, 31 deletions
diff --git a/src/nvim/os/fileio.c b/src/nvim/os/fileio.c
index 6cee102305..ccf35fd57c 100644
--- a/src/nvim/os/fileio.c
+++ b/src/nvim/os/fileio.c
@@ -1,10 +1,12 @@
+// This is an open source non-commercial project. Dear PVS-Studio, please check
+// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
+
/// @file fileio.c
///
/// Buffered reading/writing to a file. Unlike fileio.c this is not dealing with
-/// Neovim stuctures for buffer, with autocommands, etc: just fopen/fread/fwrite
+/// Nvim stuctures for buffer, with autocommands, etc: just fopen/fread/fwrite
/// replacement.
-#include <unistd.h>
#include <assert.h>
#include <stddef.h>
#include <stdbool.h>
@@ -24,6 +26,7 @@
#include "nvim/globals.h"
#include "nvim/rbuffer.h"
#include "nvim/macros.h"
+#include "nvim/message.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "os/fileio.c.generated.h"
@@ -36,18 +39,18 @@
/// @param[in] fname File name to open.
/// @param[in] flags Flags, @see FileOpenFlags. Currently reading from and
/// writing to the file at once is not supported, so either
-/// FILE_WRITE_ONLY or FILE_READ_ONLY is required.
+/// kFileWriteOnly or kFileReadOnly is required.
/// @param[in] mode Permissions for the newly created file (ignored if flags
-/// does not have FILE_CREATE\*).
+/// does not have kFileCreate\*).
///
-/// @return Error code (@see os_strerror()) or 0.
+/// @return Error code, or 0 on success. @see os_strerror()
int file_open(FileDescriptor *const ret_fp, const char *const fname,
const int flags, const int mode)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
{
int os_open_flags = 0;
- int fd;
TriState wr = kNone;
+ // -V:FLAG:501
#define FLAG(flags, flag, fcntl_flags, wrval, cond) \
do { \
if (flags & flag) { \
@@ -63,19 +66,51 @@ int file_open(FileDescriptor *const ret_fp, const char *const fname,
FLAG(flags, kFileCreate, O_CREAT|O_WRONLY, kTrue, !(flags & kFileCreateOnly));
FLAG(flags, kFileTruncate, O_TRUNC|O_WRONLY, kTrue,
!(flags & kFileCreateOnly));
+ FLAG(flags, kFileAppend, O_APPEND|O_WRONLY, kTrue,
+ !(flags & kFileCreateOnly));
FLAG(flags, kFileReadOnly, O_RDONLY, kFalse, wr != kTrue);
#ifdef O_NOFOLLOW
FLAG(flags, kFileNoSymlink, O_NOFOLLOW, kNone, true);
#endif
#undef FLAG
+ // wr is used for kFileReadOnly flag, but on
+ // QB:neovim-qb-slave-ubuntu-12-04-64bit it still errors out with
+ // `error: variable ‘wr’ set but not used [-Werror=unused-but-set-variable]`
+ (void)wr;
- fd = os_open(fname, os_open_flags, mode);
+ const int fd = os_open(fname, os_open_flags, mode);
if (fd < 0) {
return fd;
}
+ return file_open_fd(ret_fp, fd, flags);
+}
- ret_fp->wr = (wr == kTrue);
+/// Wrap file descriptor with FileDescriptor structure
+///
+/// @warning File descriptor wrapped like this must not be accessed by other
+/// means.
+///
+/// @param[out] ret_fp Address where information needed for reading from or
+/// writing to a file is saved
+/// @param[in] fd File descriptor to wrap.
+/// @param[in] flags Flags, @see FileOpenFlags. Currently reading from and
+/// writing to the file at once is not supported, so either
+/// FILE_WRITE_ONLY or FILE_READ_ONLY is required.
+///
+/// @return Error code (@see os_strerror()) or 0. Currently always returns 0.
+int file_open_fd(FileDescriptor *const ret_fp, const int fd,
+ const int flags)
+ FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
+{
+ ret_fp->wr = !!(flags & (kFileCreate
+ |kFileCreateOnly
+ |kFileTruncate
+ |kFileAppend
+ |kFileWriteOnly));
+ ret_fp->non_blocking = !!(flags & kFileNonBlocking);
+ // Non-blocking writes not supported currently.
+ assert(!ret_fp->wr || !ret_fp->non_blocking);
ret_fp->fd = fd;
ret_fp->eof = false;
ret_fp->rv = rbuffer_new(kRWBufferSize);
@@ -89,17 +124,16 @@ int file_open(FileDescriptor *const ret_fp, const char *const fname,
/// Like file_open(), but allocate and return ret_fp
///
-/// @param[out] error Error code, @see os_strerror(). Is set to zero on
-/// success.
+/// @param[out] error Error code, or 0 on success. @see os_strerror()
/// @param[in] fname File name to open.
/// @param[in] flags Flags, @see FileOpenFlags.
/// @param[in] mode Permissions for the newly created file (ignored if flags
-/// does not have FILE_CREATE\*).
+/// does not have kFileCreate\*).
///
/// @return [allocated] Opened file or NULL in case of error.
FileDescriptor *file_open_new(int *const error, const char *const fname,
const int flags, const int mode)
- FUNC_ATTR_NONNULL_ALL FUNC_ATTR_MALLOC FUNC_ATTR_WARN_UNUSED_RESULT
+ FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
{
FileDescriptor *const fp = xmalloc(sizeof(*fp));
if ((*error = file_open(fp, fname, flags, mode)) != 0) {
@@ -109,30 +143,55 @@ FileDescriptor *file_open_new(int *const error, const char *const fname,
return fp;
}
+/// Like file_open_fd(), but allocate and return ret_fp
+///
+/// @param[out] error Error code, or 0 on success. @see os_strerror()
+/// @param[in] fd File descriptor to wrap.
+/// @param[in] flags Flags, @see FileOpenFlags.
+/// @param[in] mode Permissions for the newly created file (ignored if flags
+/// does not have FILE_CREATE\*).
+///
+/// @return [allocated] Opened file or NULL in case of error.
+FileDescriptor *file_open_fd_new(int *const error, const int fd,
+ const int flags)
+ FUNC_ATTR_NONNULL_ALL FUNC_ATTR_MALLOC FUNC_ATTR_WARN_UNUSED_RESULT
+{
+ FileDescriptor *const fp = xmalloc(sizeof(*fp));
+ if ((*error = file_open_fd(fp, fd, flags)) != 0) {
+ xfree(fp);
+ return NULL;
+ }
+ return fp;
+}
+
/// Close file and free its buffer
///
/// @param[in,out] fp File to close.
+/// @param[in] do_fsync If true, use fsync() to write changes to disk.
///
/// @return 0 or error code.
-int file_close(FileDescriptor *const fp) FUNC_ATTR_NONNULL_ALL
+int file_close(FileDescriptor *const fp, const bool do_fsync)
+ FUNC_ATTR_NONNULL_ALL
{
- const int error = file_fsync(fp);
- const int error2 = os_close(fp->fd);
+ const int flush_error = (do_fsync ? file_fsync(fp) : file_flush(fp));
+ const int close_error = os_close(fp->fd);
rbuffer_free(fp->rv);
- if (error2 != 0) {
- return error2;
+ if (close_error != 0) {
+ return close_error;
}
- return error;
+ return flush_error;
}
/// Close and free file obtained using file_open_new()
///
/// @param[in,out] fp File to close.
+/// @param[in] do_fsync If true, use fsync() to write changes to disk.
///
/// @return 0 or error code.
-int file_free(FileDescriptor *const fp) FUNC_ATTR_NONNULL_ALL
+int file_free(FileDescriptor *const fp, const bool do_fsync)
+ FUNC_ATTR_NONNULL_ALL
{
- const int ret = file_close(fp);
+ const int ret = file_close(fp, do_fsync);
xfree(fp);
return ret;
}
@@ -142,19 +201,38 @@ int file_free(FileDescriptor *const fp) FUNC_ATTR_NONNULL_ALL
/// @param[in,out] fp File to work with.
///
/// @return 0 or error code.
-int file_fsync(FileDescriptor *const fp)
+int file_flush(FileDescriptor *const fp)
FUNC_ATTR_NONNULL_ALL
{
if (!fp->wr) {
return 0;
}
file_rb_write_full_cb(fp->rv, fp);
- if (fp->_error != 0) {
- const int error = fp->_error;
- fp->_error = 0;
- return error;
+ const int error = fp->_error;
+ fp->_error = 0;
+ return error;
+}
+
+/// Flush file modifications to disk and run fsync()
+///
+/// @param[in,out] fp File to work with.
+///
+/// @return 0 or error code.
+int file_fsync(FileDescriptor *const fp)
+ FUNC_ATTR_NONNULL_ALL
+{
+ if (!fp->wr) {
+ return 0;
+ }
+ const int flush_error = file_flush(fp);
+ if (flush_error != 0) {
+ return flush_error;
+ }
+ const int fsync_error = os_fsync(fp->fd);
+ if (fsync_error != UV_EINVAL && fsync_error != UV_EROFS) {
+ return fsync_error;
}
- return os_fsync(fp->fd);
+ return 0;
}
/// Buffer used for writing
@@ -177,7 +255,8 @@ static void file_rb_write_full_cb(RBuffer *const rv, FileDescriptor *const fp)
return;
}
const size_t read_bytes = rbuffer_read(rv, writebuf, kRWBufferSize);
- const ptrdiff_t wres = os_write(fp->fd, writebuf, read_bytes);
+ const ptrdiff_t wres = os_write(fp->fd, writebuf, read_bytes,
+ fp->non_blocking);
if (wres != (ptrdiff_t)read_bytes) {
if (wres >= 0) {
fp->_error = UV_EIO;
@@ -203,6 +282,7 @@ ptrdiff_t file_read(FileDescriptor *const fp, char *const ret_buf,
char *buf = ret_buf;
size_t read_remaining = size;
RBuffer *const rv = fp->rv;
+ bool called_read = false;
while (read_remaining) {
const size_t rv_size = rbuffer_size(rv);
if (rv_size > 0) {
@@ -210,7 +290,9 @@ ptrdiff_t file_read(FileDescriptor *const fp, char *const ret_buf,
buf += rsize;
read_remaining -= rsize;
}
- if (fp->eof) {
+ if (fp->eof
+ // Allow only at most one os_read[v] call.
+ || (called_read && fp->non_blocking)) {
break;
}
if (read_remaining) {
@@ -227,7 +309,7 @@ ptrdiff_t file_read(FileDescriptor *const fp, char *const ret_buf,
};
assert(write_count == kRWBufferSize);
const ptrdiff_t r_ret = os_readv(fp->fd, &fp->eof, iov,
- ARRAY_SIZE(iov));
+ ARRAY_SIZE(iov), fp->non_blocking);
if (r_ret > 0) {
if (r_ret > (ptrdiff_t)read_remaining) {
rbuffer_produced(rv, (size_t)(r_ret - (ptrdiff_t)read_remaining));
@@ -243,7 +325,8 @@ ptrdiff_t file_read(FileDescriptor *const fp, char *const ret_buf,
if (read_remaining >= kRWBufferSize) {
// …otherwise leave RBuffer empty and populate only target buffer,
// because filtering information through rbuffer will be more syscalls.
- const ptrdiff_t r_ret = os_read(fp->fd, &fp->eof, buf, read_remaining);
+ const ptrdiff_t r_ret = os_read(fp->fd, &fp->eof, buf, read_remaining,
+ fp->non_blocking);
if (r_ret >= 0) {
read_remaining -= (size_t)r_ret;
return (ptrdiff_t)(size - read_remaining);
@@ -254,7 +337,7 @@ ptrdiff_t file_read(FileDescriptor *const fp, char *const ret_buf,
size_t write_count;
const ptrdiff_t r_ret = os_read(fp->fd, &fp->eof,
rbuffer_write_ptr(rv, &write_count),
- kRWBufferSize);
+ kRWBufferSize, fp->non_blocking);
assert(write_count == kRWBufferSize);
if (r_ret > 0) {
rbuffer_produced(rv, (size_t)r_ret);
@@ -263,6 +346,7 @@ ptrdiff_t file_read(FileDescriptor *const fp, char *const ret_buf,
}
}
#endif
+ called_read = true;
}
}
return (ptrdiff_t)(size - read_remaining);
@@ -317,3 +401,32 @@ ptrdiff_t file_skip(FileDescriptor *const fp, const size_t size)
return (ptrdiff_t)read_bytes;
}
+
+/// Msgpack callback for writing to a file
+///
+/// @param data File to write to.
+/// @param[in] buf Data to write.
+/// @param[in] len Length of the data to write.
+///
+/// @return 0 in case of success, -1 in case of error.
+int msgpack_file_write(void *data, const char *buf, size_t len)
+ FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
+{
+ assert(len < PTRDIFF_MAX);
+ const ptrdiff_t written_bytes = file_write((FileDescriptor *)data, buf, len);
+ if (written_bytes < 0) {
+ return msgpack_file_write_error((int)written_bytes);
+ }
+ return 0;
+}
+
+/// Print error which occurs when failing to write msgpack data
+///
+/// @param[in] error Error code of the error to print.
+///
+/// @return -1 (error return for msgpack_packer callbacks).
+int msgpack_file_write_error(const int error)
+{
+ emsgf(_("E5420: Failed to write to file: %s"), os_strerror(error));
+ return -1;
+}