diff options
Diffstat (limited to 'src/nvim/os/fs.c')
| -rw-r--r-- | src/nvim/os/fs.c | 185 |
1 files changed, 116 insertions, 69 deletions
diff --git a/src/nvim/os/fs.c b/src/nvim/os/fs.c index c33e1140e8..5412c5daae 100644 --- a/src/nvim/os/fs.c +++ b/src/nvim/os/fs.c @@ -1,3 +1,6 @@ +// 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 + // fs.c -- filesystem access #include <stdbool.h> #include <stddef.h> @@ -58,9 +61,9 @@ void fs_init(void) } -/// Change to the given directory. +/// Changes the current directory to `path`. /// -/// @return `0` on success, a libuv error code on failure. +/// @return 0 on success, or negative error code. int os_chdir(const char *path) FUNC_ATTR_NONNULL_ALL { @@ -128,23 +131,14 @@ bool os_isdir(const char_u *name) /// NODE_WRITABLE: writable device, socket, fifo, etc. /// NODE_OTHER: non-writable things int os_nodetype(const char *name) + FUNC_ATTR_NONNULL_ALL { -#ifdef WIN32 - // Edge case from Vim os_win32.c: - // We can't open a file with a name "\\.\con" or "\\.\prn", trying to read - // from it later will cause Vim to hang. Thus return NODE_WRITABLE here. - if (STRNCMP(name, "\\\\.\\", 4) == 0) { - return NODE_WRITABLE; - } -#endif - +#ifndef WIN32 // Unix uv_stat_t statbuf; if (0 != os_stat(name, &statbuf)) { return NODE_NORMAL; // File doesn't exist. } - -#ifndef WIN32 - // libuv does not handle BLK and DIR in uv_handle_type. + // uv_handle_type does not distinguish BLK and DIR. // Related: https://github.com/joyent/libuv/pull/1421 if (S_ISREG(statbuf.st_mode) || S_ISDIR(statbuf.st_mode)) { return NODE_NORMAL; @@ -152,52 +146,61 @@ int os_nodetype(const char *name) if (S_ISBLK(statbuf.st_mode)) { // block device isn't writable return NODE_OTHER; } -#endif + // Everything else is writable? + // buf_write() expects NODE_WRITABLE for char device /dev/stderr. + return NODE_WRITABLE; +#else // Windows + // Edge case from Vim os_win32.c: + // We can't open a file with a name "\\.\con" or "\\.\prn", trying to read + // from it later will cause Vim to hang. Thus return NODE_WRITABLE here. + if (STRNCMP(name, "\\\\.\\", 4) == 0) { + return NODE_WRITABLE; + } - // Vim os_win32.c:mch_nodetype does this (since patch 7.4.015): - // if (enc_codepage >= 0 && (int)GetACP() != enc_codepage) { - // wn = enc_to_utf16(name, NULL); - // hFile = CreatFile(wn, ...) - // to get a HANDLE. But libuv just calls win32's _get_osfhandle() on the fd we - // give it. uv_fs_open calls fs__capture_path which does a similar dance and - // saves us the hassle. + // Vim os_win32.c:mch_nodetype does (since 7.4.015): + // wn = enc_to_utf16(name, NULL); + // hFile = CreatFile(wn, ...) + // to get a HANDLE. Whereas libuv just calls _get_osfhandle() on the fd we + // give it. But uv_fs_open later calls fs__capture_path which does a similar + // utf8-to-utf16 dance and saves us the hassle. - int nodetype = NODE_WRITABLE; + // macOS: os_open(/dev/stderr) would return UV_EACCES. int fd = os_open(name, O_RDONLY -#ifdef O_NONBLOCK +# ifdef O_NONBLOCK | O_NONBLOCK -#endif +# endif , 0); - switch (uv_guess_handle(fd)) { - case UV_TTY: // FILE_TYPE_CHAR - nodetype = NODE_WRITABLE; - break; - case UV_FILE: // FILE_TYPE_DISK - nodetype = NODE_NORMAL; - break; - case UV_NAMED_PIPE: // not handled explicitly in Vim os_win32.c - case UV_UDP: // unix only - case UV_TCP: // unix only + if (fd < 0) { // open() failed. + return NODE_NORMAL; + } + int guess = uv_guess_handle(fd); + if (close(fd) == -1) { + ELOG("close(%d) failed. name='%s'", fd, name); + } + + switch (guess) { + case UV_TTY: // FILE_TYPE_CHAR + return NODE_WRITABLE; + case UV_FILE: // FILE_TYPE_DISK + return NODE_NORMAL; + case UV_NAMED_PIPE: // not handled explicitly in Vim os_win32.c + case UV_UDP: // unix only + case UV_TCP: // unix only case UV_UNKNOWN_HANDLE: default: -#ifdef WIN32 - nodetype = NODE_NORMAL; -#else - nodetype = NODE_WRITABLE; // Everything else is writable? -#endif - break; + return NODE_OTHER; // Vim os_win32.c default } - - close(fd); - return nodetype; +#endif } /// Gets the absolute path of the currently running executable. +/// May fail if procfs is missing. #6734 +/// @see path_exepath /// -/// @param[out] buffer Returns the path string. +/// @param[out] buffer Full path to the executable. /// @param[in] size Size of `buffer`. /// -/// @return `0` on success, or libuv error code on failure. +/// @return 0 on success, or libuv error code. int os_exepath(char *buffer, size_t *size) FUNC_ATTR_NONNULL_ALL { @@ -219,7 +222,7 @@ int os_exepath(char *buffer, size_t *size) bool os_can_exe(const char_u *name, char_u **abspath, bool use_path) FUNC_ATTR_NONNULL_ARG(1) { - bool no_path = !use_path || path_is_absolute_path(name); + bool no_path = !use_path || path_is_absolute(name); #ifndef WIN32 // If the filename is "qualified" (relative or absolute) do not check $PATH. no_path |= (name[0] == '.' @@ -241,7 +244,7 @@ bool os_can_exe(const char_u *name, char_u **abspath, bool use_path) #endif if (ok) { if (abspath != NULL) { - *abspath = save_absolute_path(name); + *abspath = save_abs_path(name); } return true; } @@ -354,7 +357,7 @@ static bool is_executable_in_path(const char_u *name, char_u **abspath) #endif if (ok) { if (abspath != NULL) { // Caller asked for a copy of the path. - *abspath = save_absolute_path((char_u *)buf); + *abspath = save_abs_path((char_u *)buf); } rv = true; @@ -384,9 +387,11 @@ end: /// @param mode Permissions for the newly-created file (IGNORED if 'flags' is /// not `O_CREAT` or `O_TMPFILE`), subject to the current umask /// @return file descriptor, or libuv error code on failure -int os_open(const char* path, int flags, int mode) - FUNC_ATTR_NONNULL_ALL +int os_open(const char *path, int flags, int mode) { + if (path == NULL) { // uv_fs_open asserts on NULL. #7561 + return UV_EINVAL; + } int r; RUN_UV_FS_FUNC(r, uv_fs_open, path, flags, mode, NULL); return r; @@ -431,6 +436,29 @@ int os_close(const int fd) return r; } +/// Duplicate file descriptor +/// +/// @param[in] fd File descriptor to duplicate. +/// +/// @return New file descriptor or libuv error code (< 0). +int os_dup(const int fd) + FUNC_ATTR_WARN_UNUSED_RESULT +{ + int ret; +os_dup_dup: + ret = dup(fd); + if (ret < 0) { + const int error = os_translate_sys_error(errno); + errno = 0; + if (error == UV_EINTR) { + goto os_dup_dup; + } else { + return error; + } + } + return ret; +} + /// Read from a file /// /// Handles EINTR and ENOMEM, but not other errors. @@ -440,10 +468,11 @@ int os_close(const int fd) /// to false. Initial value is ignored. /// @param[out] ret_buf Buffer to write to. May be NULL if size is zero. /// @param[in] size Amount of bytes to read. +/// @param[in] non_blocking Do not restart syscall if EAGAIN was encountered. /// /// @return Number of bytes read or libuv error code (< 0). -ptrdiff_t os_read(const int fd, bool *ret_eof, char *const ret_buf, - const size_t size) +ptrdiff_t os_read(const int fd, bool *const ret_eof, char *const ret_buf, + const size_t size, const bool non_blocking) FUNC_ATTR_WARN_UNUSED_RESULT { *ret_eof = false; @@ -456,14 +485,16 @@ ptrdiff_t os_read(const int fd, bool *ret_eof, char *const ret_buf, while (read_bytes != size) { assert(size >= read_bytes); const ptrdiff_t cur_read_bytes = read(fd, ret_buf + read_bytes, - size - read_bytes); + IO_COUNT(size - read_bytes)); if (cur_read_bytes > 0) { read_bytes += (size_t)cur_read_bytes; } if (cur_read_bytes < 0) { const int error = os_translate_sys_error(errno); errno = 0; - if (error == UV_EINTR || error == UV_EAGAIN) { + if (non_blocking && error == UV_EAGAIN) { + break; + } else if (error == UV_EINTR || error == UV_EAGAIN) { continue; } else if (error == UV_ENOMEM && !did_try_to_free) { try_to_free_memory(); @@ -493,7 +524,11 @@ ptrdiff_t os_read(const int fd, bool *ret_eof, char *const ret_buf, /// may change, it is incorrect to use data it points to after /// os_readv(). /// @param[in] iov_size Number of buffers in iov. -ptrdiff_t os_readv(int fd, bool *ret_eof, struct iovec *iov, size_t iov_size) +/// @param[in] non_blocking Do not restart syscall if EAGAIN was encountered. +/// +/// @return Number of bytes read or libuv error code (< 0). +ptrdiff_t os_readv(const int fd, bool *const ret_eof, struct iovec *iov, + size_t iov_size, const bool non_blocking) FUNC_ATTR_NONNULL_ALL { *ret_eof = false; @@ -507,7 +542,7 @@ ptrdiff_t os_readv(int fd, bool *ret_eof, struct iovec *iov, size_t iov_size) } while (read_bytes < toread && iov_size && !*ret_eof) { ptrdiff_t cur_read_bytes = readv(fd, iov, (int)iov_size); - if (toread && cur_read_bytes == 0) { + if (cur_read_bytes == 0) { *ret_eof = true; } if (cur_read_bytes > 0) { @@ -526,7 +561,9 @@ ptrdiff_t os_readv(int fd, bool *ret_eof, struct iovec *iov, size_t iov_size) } else if (cur_read_bytes < 0) { const int error = os_translate_sys_error(errno); errno = 0; - if (error == UV_EINTR || error == UV_EAGAIN) { + if (non_blocking && error == UV_EAGAIN) { + break; + } else if (error == UV_EINTR || error == UV_EAGAIN) { continue; } else if (error == UV_ENOMEM && !did_try_to_free) { try_to_free_memory(); @@ -546,9 +583,11 @@ ptrdiff_t os_readv(int fd, bool *ret_eof, struct iovec *iov, size_t iov_size) /// @param[in] fd File descriptor to write to. /// @param[in] buf Data to write. May be NULL if size is zero. /// @param[in] size Amount of bytes to write. +/// @param[in] non_blocking Do not restart syscall if EAGAIN was encountered. /// /// @return Number of bytes written or libuv error code (< 0). -ptrdiff_t os_write(const int fd, const char *const buf, const size_t size) +ptrdiff_t os_write(const int fd, const char *const buf, const size_t size, + const bool non_blocking) FUNC_ATTR_WARN_UNUSED_RESULT { if (buf == NULL) { @@ -559,14 +598,16 @@ ptrdiff_t os_write(const int fd, const char *const buf, const size_t size) while (written_bytes != size) { assert(size >= written_bytes); const ptrdiff_t cur_written_bytes = write(fd, buf + written_bytes, - size - written_bytes); + IO_COUNT(size - written_bytes)); if (cur_written_bytes > 0) { written_bytes += (size_t)cur_written_bytes; } if (cur_written_bytes < 0) { const int error = os_translate_sys_error(errno); errno = 0; - if (error == UV_EINTR || error == UV_EAGAIN) { + if (non_blocking && error == UV_EAGAIN) { + break; + } else if (error == UV_EINTR || error == UV_EAGAIN) { continue; } else { return error; @@ -588,15 +629,19 @@ int os_fsync(int fd) { int r; RUN_UV_FS_FUNC(r, uv_fs_fsync, fd, NULL); + g_stats.fsync++; return r; } /// Get stat information for a file. /// -/// @return libuv return code. +/// @return libuv return code, or -errno static int os_stat(const char *name, uv_stat_t *statbuf) - FUNC_ATTR_NONNULL_ALL + FUNC_ATTR_NONNULL_ARG(2) { + if (!name) { + return UV_EINVAL; + } uv_fs_t request; int result = uv_fs_stat(&fs_loop, &request, name, NULL); *statbuf = request.statbuf; @@ -608,7 +653,6 @@ static int os_stat(const char *name, uv_stat_t *statbuf) /// /// @return libuv error code on error. int32_t os_getperm(const char *name) - FUNC_ATTR_NONNULL_ALL { uv_stat_t statbuf; int stat_result = os_stat(name, &statbuf); @@ -647,7 +691,6 @@ int os_fchown(int fd, uv_uid_t owner, uv_gid_t group) /// /// @return `true` if `path` exists bool os_path_exists(const char_u *path) - FUNC_ATTR_NONNULL_ALL { uv_stat_t statbuf; return os_stat((char *)path, &statbuf) == kLibuvSuccess; @@ -837,7 +880,7 @@ int os_remove(const char *path) /// @param[out] file_info Pointer to a FileInfo to put the information in. /// @return `true` on success, `false` for failure. bool os_fileinfo(const char *path, FileInfo *file_info) - FUNC_ATTR_NONNULL_ALL + FUNC_ATTR_NONNULL_ARG(2) { return os_stat(path, &(file_info->stat)) == kLibuvSuccess; } @@ -848,8 +891,11 @@ bool os_fileinfo(const char *path, FileInfo *file_info) /// @param[out] file_info Pointer to a FileInfo to put the information in. /// @return `true` on success, `false` for failure. bool os_fileinfo_link(const char *path, FileInfo *file_info) - FUNC_ATTR_NONNULL_ALL + FUNC_ATTR_NONNULL_ARG(2) { + if (path == NULL) { + return false; + } uv_fs_t request; int result = uv_fs_lstat(&fs_loop, &request, path, NULL); file_info->stat = request.statbuf; @@ -981,7 +1027,7 @@ bool os_fileid_equal_fileinfo(const FileID *file_id, /// to and return that name in allocated memory. /// Otherwise NULL is returned. char *os_resolve_shortcut(const char *fname) - FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_MALLOC + FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_MALLOC { HRESULT hr; IPersistFile *ppf = NULL; @@ -1064,7 +1110,8 @@ shortcut_end: #endif -int os_translate_sys_error(int sys_errno) { +int os_translate_sys_error(int sys_errno) +{ #ifdef HAVE_UV_TRANSLATE_SYS_ERROR return uv_translate_sys_error(sys_errno); #elif defined(WIN32) |
