aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/fileio.c
diff options
context:
space:
mode:
authorJustin M. Keyes <justinkz@gmail.com>2022-06-30 13:16:46 +0200
committerGitHub <noreply@github.com>2022-06-30 04:16:46 -0700
commitf50135a32e11c535e1dc3a8e9460c5b4e640ee86 (patch)
tree4531a75f5f099877cb8d672743abf03004171f4f /src/nvim/fileio.c
parent514e76e4b2903530922529c60bfbf32cadd257a3 (diff)
downloadrneovim-f50135a32e11c535e1dc3a8e9460c5b4e640ee86.tar.gz
rneovim-f50135a32e11c535e1dc3a8e9460c5b4e640ee86.tar.bz2
rneovim-f50135a32e11c535e1dc3a8e9460c5b4e640ee86.zip
feat: stdpath('run'), /tmp/nvim.user/ #18993
Problem: - Since c57f6b28d71d #8519, sockets are created in ~/.local/… but XDG spec says: "XDG_RUNTIME_DIR: Must be on the local filesystem", which implies that XDG_STATE_DIR is potentially non-local. - Not easy to inspect Nvim-created temp files (for debugging etc). Solution: - Store sockets in stdpath('run') ($XDG_RUNTIME_DIR). - Establish "/tmp/nvim.user/" as the tempdir root shared by all Nvims. - Make ok() actually useful. - Introduce assert_nolog(). closes #3517 closes #17093
Diffstat (limited to 'src/nvim/fileio.c')
-rw-r--r--src/nvim/fileio.c96
1 files changed, 66 insertions, 30 deletions
diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c
index ba3e7a0150..33ae1dbad0 100644
--- a/src/nvim/fileio.c
+++ b/src/nvim/fileio.c
@@ -5281,45 +5281,80 @@ void forward_slash(char_u *fname)
}
#endif
-/// Name of Vim's own temp dir. Ends in a slash.
-static char_u *vim_tempdir = NULL;
+/// Path to Nvim's own temp dir. Ends in a slash.
+static char *vim_tempdir = NULL;
-/// Create a directory for private use by this instance of Neovim.
-/// This is done once, and the same directory is used for all temp files.
+/// Creates a directory for private use by this instance of Nvim, trying each of
+/// `TEMP_DIR_NAMES` until one succeeds.
+///
+/// Only done once, the same directory is used for all temp files.
/// This method avoids security problems because of symlink attacks et al.
/// It's also a bit faster, because we only need to check for an existing
/// file when creating the directory and not for each temp file.
-static void vim_maketempdir(void)
+static void vim_mktempdir(void)
{
- static const char *temp_dirs[] = TEMP_DIR_NAMES;
- // Try the entries in `TEMP_DIR_NAMES` to create the temp directory.
- char_u template[TEMP_FILE_PATH_MAXLEN];
- char_u path[TEMP_FILE_PATH_MAXLEN];
+ static const char *temp_dirs[] = TEMP_DIR_NAMES; // Try each of these until one succeeds.
+ char tmp[TEMP_FILE_PATH_MAXLEN];
+ char path[TEMP_FILE_PATH_MAXLEN];
+ char user[40] = { 0 };
+
+ (void)os_get_username(user, sizeof(user));
// Make sure the umask doesn't remove the executable bit.
// "repl" has been reported to use "0177".
mode_t umask_save = umask(0077);
for (size_t i = 0; i < ARRAY_SIZE(temp_dirs); i++) {
- // Expand environment variables, leave room for "/nvimXXXXXX/999999999"
- expand_env((char_u *)temp_dirs[i], template, TEMP_FILE_PATH_MAXLEN - 22);
- if (!os_isdir(template)) { // directory doesn't exist
+ // Expand environment variables, leave room for "/tmp/nvim.<user>/XXXXXX/999999999".
+ expand_env((char_u *)temp_dirs[i], (char_u *)tmp, TEMP_FILE_PATH_MAXLEN - 64);
+ if (!os_isdir((char_u *)tmp)) {
continue;
}
- add_pathsep((char *)template);
- // Concatenate with temporary directory name pattern
- STRCAT(template, "nvimXXXXXX");
+ // "/tmp/" exists, now try to create "/tmp/nvim.<user>/".
+ add_pathsep(tmp);
+ xstrlcat(tmp, "nvim.", sizeof(tmp));
+ xstrlcat(tmp, user, sizeof(tmp));
+ (void)os_mkdir(tmp, 0700); // Always create, to avoid a race.
+ bool owned = os_file_owned(tmp);
+ bool isdir = os_isdir((char_u *)tmp);
+#ifdef UNIX
+ int perm = os_getperm(tmp); // XDG_RUNTIME_DIR must be owned by the user, mode 0700.
+ bool valid = isdir && owned && 0700 == (perm & 0777);
+#else
+ bool valid = isdir && owned; // TODO(justinmk): Windows ACL?
+#endif
+ if (valid) {
+ add_pathsep(tmp);
+ } else {
+ if (!owned) {
+ ELOG("tempdir root not owned by current user (%s): %s", user, tmp);
+ } else if (!isdir) {
+ ELOG("tempdir root not a directory: %s", tmp);
+ }
+#ifdef UNIX
+ if (0700 != (perm & 0777)) {
+ ELOG("tempdir root has invalid permissions (%o): %s", perm, tmp);
+ }
+#endif
+ // If our "root" tempdir is invalid or fails, proceed without "<user>/".
+ // Else user1 could break user2 by creating "/tmp/nvim.user2/".
+ tmp[strlen(tmp) - strlen(user)] = '\0';
+ }
- if (os_mkdtemp((const char *)template, (char *)path) != 0) {
+ // Now try to create "/tmp/nvim.<user>/XXXXXX".
+ xstrlcat(tmp, "XXXXXX", sizeof(tmp)); // mkdtemp "template", will be replaced with random alphanumeric chars.
+ int r = os_mkdtemp(tmp, path);
+ if (r != 0) {
+ WLOG("tempdir create failed: %s: %s", os_strerror(r), tmp);
continue;
}
- if (vim_settempdir((char *)path)) {
+ if (vim_settempdir(path)) {
// Successfully created and set temporary directory so stop trying.
break;
} else {
// Couldn't set `vim_tempdir` to `path` so remove created directory.
- os_rmdir((char *)path);
+ os_rmdir(path);
}
}
(void)umask(umask_save);
@@ -5415,26 +5450,27 @@ void vim_deltempdir(void)
{
if (vim_tempdir != NULL) {
// remove the trailing path separator
- path_tail((char *)vim_tempdir)[-1] = NUL;
- delete_recursive((const char *)vim_tempdir);
+ path_tail(vim_tempdir)[-1] = NUL;
+ delete_recursive(vim_tempdir);
XFREE_CLEAR(vim_tempdir);
}
}
-/// @return the name of temp directory. This directory would be created on the first
-/// call to this function.
-char_u *vim_gettempdir(void)
+/// Gets path to Nvim's own temp dir (ending with slash).
+///
+/// Creates the directory on the first call.
+char *vim_gettempdir(void)
{
if (vim_tempdir == NULL) {
- vim_maketempdir();
+ vim_mktempdir();
}
return vim_tempdir;
}
-/// Set Neovim own temporary directory name to `tempdir`. This directory should
-/// be already created. Expand this name to a full path and put it in
-/// `vim_tempdir`. This avoids that using `:cd` would confuse us.
+/// Sets Nvim's own temporary directory name to `tempdir`. This directory must
+/// already exist. Expands the name to a full path and put it in `vim_tempdir`.
+/// This avoids that using `:cd` would confuse us.
///
/// @param tempdir must be no longer than MAXPATHL.
///
@@ -5447,7 +5483,7 @@ static bool vim_settempdir(char *tempdir)
}
vim_FullName(tempdir, buf, MAXPATHL, false);
add_pathsep(buf);
- vim_tempdir = (char_u *)xstrdup(buf);
+ vim_tempdir = xstrdup(buf);
xfree(buf);
return true;
}
@@ -5456,14 +5492,14 @@ static bool vim_settempdir(char *tempdir)
///
/// @note The temp file is NOT created.
///
-/// @return pointer to the temp file name or NULL if Neovim can't create
+/// @return pointer to the temp file name or NULL if Nvim can't create
/// temporary directory for its own temporary files.
char_u *vim_tempname(void)
{
// Temp filename counter.
static uint64_t temp_count;
- char_u *tempdir = vim_gettempdir();
+ char *tempdir = vim_gettempdir();
if (!tempdir) {
return NULL;
}