aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/os/fs.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nvim/os/fs.c')
-rw-r--r--src/nvim/os/fs.c65
1 files changed, 58 insertions, 7 deletions
diff --git a/src/nvim/os/fs.c b/src/nvim/os/fs.c
index 24c7678633..901a1bc5a6 100644
--- a/src/nvim/os/fs.c
+++ b/src/nvim/os/fs.c
@@ -40,8 +40,10 @@
bool did_try_to_free = false; \
uv_call_start: {} \
uv_fs_t req; \
+ fs_loop_lock(); \
ret = func(&fs_loop, &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; \
@@ -52,14 +54,27 @@ uv_call_start: {} \
// 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`.
///
@@ -98,9 +113,12 @@ 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();
return false;
}
+ fs_loop_unlock();
if (S_ISLNK(request.statbuf.st_mode)) {
return false;
} else {
@@ -108,7 +126,7 @@ bool os_isrealdir(const char *name)
}
}
-/// Check if the given path is a directory or not.
+/// Check if the given path exists and is a directory.
///
/// @return `true` if `name` is a directory.
bool os_isdir(const char_u *name)
@@ -738,7 +756,9 @@ 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();
if (result == kLibuvSuccess) {
*statbuf = request.statbuf;
}
@@ -771,6 +791,27 @@ int os_setperm(const char *const name, int perm)
return (r == kLibuvSuccess ? OK : FAIL);
}
+#ifdef UNIX
+/// Checks if the current user owns a file.
+///
+/// Uses both uv_fs_stat() and uv_fs_lstat() via os_fileinfo() and
+/// os_fileinfo_link() respectively for extra security.
+bool os_file_owned(const char *fname)
+ FUNC_ATTR_NONNULL_ALL
+{
+ uid_t uid = getuid();
+ FileInfo finfo;
+ bool file_owned = os_fileinfo(fname, &finfo) && finfo.stat.st_uid == uid;
+ bool link_owned = os_fileinfo_link(fname, &finfo) && finfo.stat.st_uid == uid;
+ return file_owned && link_owned;
+}
+#else
+bool os_file_owned(const char *fname)
+{
+ return true; // TODO(justinmk): Windows. #8244
+}
+#endif
+
/// Changes the owner and group of a file, like chown(2).
///
/// @return 0 on success, or libuv error code on failure.
@@ -894,7 +935,7 @@ int os_mkdir_recurse(const char *const dir, int32_t mode, char **const failed_di
const char *const real_end = e;
const char past_head_save = *past_head;
while (!os_isdir((char_u *)curdir)) {
- e = (char *)path_tail_with_sep((char_u *)curdir);
+ e = path_tail_with_sep(curdir);
if (e <= past_head) {
*past_head = NUL;
break;
@@ -935,9 +976,11 @@ int os_mkdtemp(const char *template, 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();
if (result == kLibuvSuccess) {
- STRNCPY(path, request.path, TEMP_FILE_PATH_MAXLEN);
+ xstrlcpy(path, request.path, TEMP_FILE_PATH_MAXLEN);
}
uv_fs_req_cleanup(&request);
return result;
@@ -962,7 +1005,9 @@ 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();
if (r < 0) {
os_closedir(dir);
}
@@ -1023,7 +1068,9 @@ 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();
if (ok) {
file_info->stat = request.statbuf;
}
@@ -1041,6 +1088,7 @@ bool os_fileinfo_fd(int file_descriptor, FileInfo *file_info)
{
uv_fs_t request;
memset(file_info, 0, sizeof(*file_info));
+ fs_loop_lock();
bool ok = uv_fs_fstat(&fs_loop,
&request,
file_descriptor,
@@ -1049,6 +1097,7 @@ 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;
}
@@ -1165,6 +1214,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);
if (result == kLibuvSuccess) {
if (buf == NULL) {
@@ -1173,6 +1223,7 @@ 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;
}
@@ -1261,7 +1312,7 @@ shortcut_end:
return rfname;
}
-# define is_path_sep(c) ((c) == L'\\' || (c) == L'/')
+# define IS_PATH_SEP(c) ((c) == L'\\' || (c) == L'/')
/// Returns true if the path contains a reparse point (junction or symbolic
/// link). Otherwise false in returned.
bool os_is_reparse_point_include(const char *path)
@@ -1278,9 +1329,9 @@ bool os_is_reparse_point_include(const char *path)
}
p = utf16_path;
- if (isalpha(p[0]) && p[1] == L':' && is_path_sep(p[2])) {
+ if (isalpha(p[0]) && p[1] == L':' && IS_PATH_SEP(p[2])) {
p += 3;
- } else if (is_path_sep(p[0]) && is_path_sep(p[1])) {
+ } else if (IS_PATH_SEP(p[0]) && IS_PATH_SEP(p[1])) {
p += 2;
}