aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/os/fs.c
diff options
context:
space:
mode:
authorJosh Rahm <joshuarahm@gmail.com>2023-11-30 20:35:25 +0000
committerJosh Rahm <joshuarahm@gmail.com>2023-11-30 20:35:25 +0000
commit1b7b916b7631ddf73c38e3a0070d64e4636cb2f3 (patch)
treecd08258054db80bb9a11b1061bb091c70b76926a /src/nvim/os/fs.c
parenteaa89c11d0f8aefbb512de769c6c82f61a8baca3 (diff)
parent4a8bf24ac690004aedf5540fa440e788459e5e34 (diff)
downloadrneovim-aucmd_textputpost.tar.gz
rneovim-aucmd_textputpost.tar.bz2
rneovim-aucmd_textputpost.zip
Merge remote-tracking branch 'upstream/master' into aucmd_textputpostaucmd_textputpost
Diffstat (limited to 'src/nvim/os/fs.c')
-rw-r--r--src/nvim/os/fs.c239
1 files changed, 140 insertions, 99 deletions
diff --git a/src/nvim/os/fs.c b/src/nvim/os/fs.c
index 6157341ec9..8f939c3b40 100644
--- a/src/nvim/os/fs.c
+++ b/src/nvim/os/fs.c
@@ -1,6 +1,3 @@
-// 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 <assert.h>
#include <errno.h>
@@ -12,79 +9,75 @@
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
+#include <sys/types.h>
+#include <uv.h>
+
+#ifdef MSWIN
+# include <shlobj.h>
+#endif
#include "auto/config.h"
-#include "nvim/gettext.h"
-#include "nvim/globals.h"
-#include "nvim/log.h"
-#include "nvim/macros.h"
-#include "nvim/option_defs.h"
-#include "nvim/os/fs_defs.h"
-#include "nvim/types.h"
-#include "nvim/vim.h"
+#include "nvim/func_attr.h"
+#include "nvim/os/fs.h"
-#ifdef HAVE_SYS_UIO_H
-# include <sys/uio.h>
+#if defined(HAVE_ACL)
+# ifdef HAVE_SYS_ACL_H
+# include <sys/acl.h>
+# endif
+# ifdef HAVE_SYS_ACCESS_H
+# include <sys/access.h>
+# endif
#endif
-#include <uv.h>
+#ifdef HAVE_XATTR
+# include <sys/xattr.h>
+#endif
-#include "nvim/ascii.h"
+#include "nvim/ascii_defs.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
+#include "nvim/log.h"
+#include "nvim/macros_defs.h"
#include "nvim/memory.h"
#include "nvim/message.h"
+#include "nvim/option_vars.h"
#include "nvim/os/os.h"
#include "nvim/path.h"
+#include "nvim/types_defs.h"
+#include "nvim/vim_defs.h"
-struct iovec;
+#ifdef HAVE_SYS_UIO_H
+# include <sys/uio.h>
+#endif
#ifdef MSWIN
-# include "nvim/mbyte.h" // for utf8_to_utf16, utf16_to_utf8
+# include "nvim/mbyte.h"
+# include "nvim/option.h"
+# include "nvim/strings.h"
#endif
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "os/fs.c.generated.h"
#endif
+#ifdef HAVE_XATTR
+static const char e_xattr_erange[]
+ = N_("E1506: Buffer too small to copy xattr value or key");
+static const char e_xattr_e2big[]
+ = N_("E1508: Size of the extended attribute value is larger than the maximum size allowed");
+static const char e_xattr_other[]
+ = N_("E1509: Error occurred when reading or writing extended attribute");
+#endif
+
#define RUN_UV_FS_FUNC(ret, func, ...) \
do { \
- bool did_try_to_free = false; \
-uv_call_start: {} \
uv_fs_t req; \
- fs_loop_lock(); \
- ret = func(&fs_loop, &req, __VA_ARGS__); \
+ ret = func(NULL, &req, __VA_ARGS__); \
uv_fs_req_cleanup(&req); \
- fs_loop_unlock(); \
- if (ret == UV_ENOMEM && !did_try_to_free) { \
- try_to_free_memory(); \
- did_try_to_free = true; \
- goto uv_call_start; \
- } \
} while (0)
// Many fs functions from libuv return that value on success.
static const int kLibuvSuccess = 0;
-static uv_loop_t fs_loop;
-static uv_mutex_t fs_loop_mutex;
-
-// Initialize the fs module
-void fs_init(void)
-{
- uv_loop_init(&fs_loop);
- uv_mutex_init_recursive(&fs_loop_mutex);
-}
-
-/// TODO(bfredl): some of these operations should
-/// be possible to do the private libuv loop of the
-/// thread, instead of contending the global fs loop
-void fs_loop_lock(void)
-{
- uv_mutex_lock(&fs_loop_mutex);
-}
-
-void fs_loop_unlock(void)
-{
- uv_mutex_unlock(&fs_loop_mutex);
-}
/// Changes the current directory to `path`.
///
@@ -94,7 +87,7 @@ int os_chdir(const char *path)
{
if (p_verbose >= 5) {
verbose_enter();
- smsg("chdir(%s)", path);
+ smsg(0, "chdir(%s)", path);
verbose_leave();
}
return uv_chdir(path);
@@ -123,12 +116,9 @@ bool os_isrealdir(const char *name)
FUNC_ATTR_NONNULL_ALL
{
uv_fs_t request;
- fs_loop_lock();
- if (uv_fs_lstat(&fs_loop, &request, name, NULL) != kLibuvSuccess) {
- fs_loop_unlock();
+ if (uv_fs_lstat(NULL, &request, name, NULL) != kLibuvSuccess) {
return false;
}
- fs_loop_unlock();
if (S_ISLNK(request.statbuf.st_mode)) {
return false;
}
@@ -373,7 +363,7 @@ static bool is_executable_in_path(const char *name, char **abspath)
// is an executable file.
char *p = path;
bool rv = false;
- for (;;) {
+ while (true) {
char *e = xstrchrnul(p, ENV_SEPCHAR);
// Combine the $PATH segment with `name`.
@@ -567,7 +557,6 @@ ptrdiff_t os_read(const int fd, bool *const ret_eof, char *const ret_buf, const
return 0;
}
size_t read_bytes = 0;
- bool did_try_to_free = false;
while (read_bytes != size) {
assert(size >= read_bytes);
const ptrdiff_t cur_read_bytes = read(fd, ret_buf + read_bytes,
@@ -582,10 +571,6 @@ ptrdiff_t os_read(const int fd, bool *const ret_eof, char *const ret_buf, const
break;
} else if (error == UV_EINTR || error == UV_EAGAIN) {
continue;
- } else if (error == UV_ENOMEM && !did_try_to_free) {
- try_to_free_memory();
- did_try_to_free = true;
- continue;
} else {
return (ptrdiff_t)error;
}
@@ -619,7 +604,6 @@ ptrdiff_t os_readv(const int fd, bool *const ret_eof, struct iovec *iov, size_t
{
*ret_eof = false;
size_t read_bytes = 0;
- bool did_try_to_free = false;
size_t toread = 0;
for (size_t i = 0; i < iov_size; i++) {
// Overflow, trying to read too much data
@@ -651,10 +635,6 @@ ptrdiff_t os_readv(const int fd, bool *const ret_eof, struct iovec *iov, size_t
break;
} else if (error == UV_EINTR || error == UV_EAGAIN) {
continue;
- } else if (error == UV_ENOMEM && !did_try_to_free) {
- try_to_free_memory();
- did_try_to_free = true;
- continue;
} else {
return (ptrdiff_t)error;
}
@@ -743,9 +723,7 @@ static int os_stat(const char *name, uv_stat_t *statbuf)
return UV_EINVAL;
}
uv_fs_t request;
- fs_loop_lock();
- int result = uv_fs_stat(&fs_loop, &request, name, NULL);
- fs_loop_unlock();
+ int result = uv_fs_stat(NULL, &request, name, NULL);
if (result == kLibuvSuccess) {
*statbuf = request.statbuf;
}
@@ -777,13 +755,84 @@ int os_setperm(const char *const name, int perm)
return (r == kLibuvSuccess ? OK : FAIL);
}
-#if defined(HAVE_ACL)
-# ifdef HAVE_SYS_ACL_H
-# include <sys/acl.h>
-# endif
-# ifdef HAVE_SYS_ACCESS_H
-# include <sys/access.h>
-# endif
+#ifdef HAVE_XATTR
+/// Copy extended attributes from_file to to_file
+void os_copy_xattr(const char *from_file, const char *to_file)
+{
+ if (from_file == NULL) {
+ return;
+ }
+
+ // get the length of the extended attributes
+ ssize_t size = listxattr((char *)from_file, NULL, 0);
+ // not supported or no attributes to copy
+ if (errno == ENOTSUP || size <= 0) {
+ return;
+ }
+ char *xattr_buf = xmalloc((size_t)size);
+ size = listxattr(from_file, xattr_buf, (size_t)size);
+ ssize_t tsize = size;
+
+ errno = 0;
+
+ ssize_t max_vallen = 0;
+ char *val = NULL;
+ const char *errmsg = NULL;
+
+ for (int round = 0; round < 2; round++) {
+ char *key = xattr_buf;
+ if (round == 1) {
+ size = tsize;
+ }
+
+ while (size > 0) {
+ ssize_t vallen = getxattr(from_file, key, val, round ? (size_t)max_vallen : 0);
+ // only set the attribute in the second round
+ if (vallen >= 0 && round
+ && setxattr(to_file, key, val, (size_t)vallen, 0) == 0) {
+ //
+ } else if (errno) {
+ switch (errno) {
+ case E2BIG:
+ errmsg = e_xattr_e2big;
+ goto error_exit;
+ case ENOTSUP:
+ case EACCES:
+ case EPERM:
+ break;
+ case ERANGE:
+ errmsg = e_xattr_erange;
+ goto error_exit;
+ default:
+ errmsg = e_xattr_other;
+ goto error_exit;
+ }
+ }
+
+ if (round == 0 && vallen > max_vallen) {
+ max_vallen = vallen;
+ }
+
+ // add one for terminating null
+ ssize_t keylen = (ssize_t)strlen(key) + 1;
+ size -= keylen;
+ key += keylen;
+ }
+ if (round) {
+ break;
+ }
+
+ val = xmalloc((size_t)max_vallen + 1);
+ }
+error_exit:
+ xfree(xattr_buf);
+ xfree(val);
+
+ if (errmsg != NULL) {
+ emsg(_(errmsg));
+ }
+}
+#endif
// Return a pointer to the ACL of file "fname" in allocated memory.
// Return NULL if the ACL is not available for whatever reason.
@@ -807,7 +856,6 @@ void os_free_acl(vim_acl_T aclent)
return;
}
}
-#endif
#ifdef UNIX
/// Checks if the current user owns a file.
@@ -938,10 +986,13 @@ int os_mkdir(const char *path, int32_t mode)
/// the name of the directory which os_mkdir_recurse
/// failed to create. I.e. it will contain dir or any
/// of the higher level directories.
+/// @param[out] created Set to the full name of the first created directory.
+/// It will be NULL until that happens.
///
/// @return `0` for success, libuv error code for failure.
-int os_mkdir_recurse(const char *const dir, int32_t mode, char **const failed_dir)
- FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
+int os_mkdir_recurse(const char *const dir, int32_t mode, char **const failed_dir,
+ char **const created)
+ FUNC_ATTR_NONNULL_ARG(1, 3) FUNC_ATTR_WARN_UNUSED_RESULT
{
// Get end of directory name in "dir".
// We're done when it's "/" or "c:/".
@@ -976,6 +1027,8 @@ int os_mkdir_recurse(const char *const dir, int32_t mode, char **const failed_di
if ((ret = os_mkdir(curdir, mode)) != 0) {
*failed_dir = curdir;
return ret;
+ } else if (created != NULL && *created == NULL) {
+ *created = FullName_save(curdir, false);
}
}
xfree(curdir);
@@ -1003,7 +1056,7 @@ int os_file_mkdir(char *fname, int32_t mode)
*tail = NUL;
int r;
char *failed_dir;
- if (((r = os_mkdir_recurse(fname, mode, &failed_dir)) < 0)) {
+ if (((r = os_mkdir_recurse(fname, mode, &failed_dir, NULL)) < 0)) {
semsg(_(e_mkdir), failed_dir, os_strerror(r));
xfree(failed_dir);
}
@@ -1015,18 +1068,16 @@ int os_file_mkdir(char *fname, int32_t mode)
/// Create a unique temporary directory.
///
-/// @param[in] template Template of the path to the directory with XXXXXX
-/// which would be replaced by random chars.
+/// @param[in] templ Template of the path to the directory with XXXXXX
+/// which would be replaced by random chars.
/// @param[out] path Path to created directory for success, undefined for
/// failure.
/// @return `0` for success, non-zero for failure.
-int os_mkdtemp(const char *template, char *path)
+int os_mkdtemp(const char *templ, char *path)
FUNC_ATTR_NONNULL_ALL
{
uv_fs_t request;
- fs_loop_lock();
- int result = uv_fs_mkdtemp(&fs_loop, &request, template, NULL);
- fs_loop_unlock();
+ int result = uv_fs_mkdtemp(NULL, &request, templ, NULL);
if (result == kLibuvSuccess) {
xstrlcpy(path, request.path, TEMP_FILE_PATH_MAXLEN);
}
@@ -1053,9 +1104,7 @@ int os_rmdir(const char *path)
bool os_scandir(Directory *dir, const char *path)
FUNC_ATTR_NONNULL_ALL
{
- fs_loop_lock();
- int r = uv_fs_scandir(&fs_loop, &dir->request, path, 0, NULL);
- fs_loop_unlock();
+ int r = uv_fs_scandir(NULL, &dir->request, path, 0, NULL);
if (r < 0) {
os_closedir(dir);
}
@@ -1116,9 +1165,7 @@ bool os_fileinfo_link(const char *path, FileInfo *file_info)
return false;
}
uv_fs_t request;
- fs_loop_lock();
- bool ok = uv_fs_lstat(&fs_loop, &request, path, NULL) == kLibuvSuccess;
- fs_loop_unlock();
+ bool ok = uv_fs_lstat(NULL, &request, path, NULL) == kLibuvSuccess;
if (ok) {
file_info->stat = request.statbuf;
}
@@ -1136,8 +1183,7 @@ bool os_fileinfo_fd(int file_descriptor, FileInfo *file_info)
{
uv_fs_t request;
CLEAR_POINTER(file_info);
- fs_loop_lock();
- bool ok = uv_fs_fstat(&fs_loop,
+ bool ok = uv_fs_fstat(NULL,
&request,
file_descriptor,
NULL) == kLibuvSuccess;
@@ -1145,7 +1191,6 @@ bool os_fileinfo_fd(int file_descriptor, FileInfo *file_info)
file_info->stat = request.statbuf;
}
uv_fs_req_cleanup(&request);
- fs_loop_unlock();
return ok;
}
@@ -1262,8 +1307,7 @@ char *os_realpath(const char *name, char *buf)
FUNC_ATTR_NONNULL_ARG(1)
{
uv_fs_t request;
- fs_loop_lock();
- int result = uv_fs_realpath(&fs_loop, &request, name, NULL);
+ int result = uv_fs_realpath(NULL, &request, name, NULL);
if (result == kLibuvSuccess) {
if (buf == NULL) {
buf = xmallocz(MAXPATHL);
@@ -1271,13 +1315,10 @@ char *os_realpath(const char *name, char *buf)
xstrlcpy(buf, request.ptr, MAXPATHL + 1);
}
uv_fs_req_cleanup(&request);
- fs_loop_unlock();
return result == kLibuvSuccess ? buf : NULL;
}
#ifdef MSWIN
-# include <shlobj.h>
-
/// When "fname" is the name of a shortcut (*.lnk) resolve the file it points
/// to and return that name in allocated memory.
/// Otherwise NULL is returned.