nnn

mirror of https://github.com/jarun/nnn
Log | Files | Refs | README | LICENSE

commit 4eaaf3506e10facf600fda6966648de9b3c7eb5a
parent e0e393cb977c21f86c539d54382a5dc04498a9ca
Author: Arun Prakash Jana <engineerarun@gmail.com>
Date:   Sun, 12 Apr 2026 21:15:55 +0530

Skip complete path traversal for subdir creation under nnn config dir

Diffstat:
Msrc/nnn.c | 86++++++++++++++++++++++++++++++++++++++++++++-----------------------------------
1 file changed, 48 insertions(+), 38 deletions(-)

diff --git a/src/nnn.c b/src/nnn.c @@ -5774,7 +5774,7 @@ static size_t get_fs_info(const char *path, uchar_t type) } /* Create non-existent parents and a file or dir */ -static bool xmktree(char *path, bool dir) +static bool xmktree(char *path, bool dir, bool tree) { char *p = path; char *slash = path; @@ -5782,46 +5782,50 @@ static bool xmktree(char *path, bool dir) if (!p || !*p) return FALSE; - /* Skip the first '/' */ - ++p; + if (tree) { + /* Skip the first '/' */ + ++p; - while (*p != '\0') { - if (*p == '/') { - slash = p; - *p = '\0'; - } else { - ++p; - continue; - } + while (*p != '\0') { + if (*p == '/') { + slash = p; + *p = '\0'; + } else { + ++p; + continue; + } - /* Create folder from path to '\0' inserted at p */ - if (mkdir(path, 0777) == -1 && errno != EEXIST) { -#ifdef __HAIKU__ - // XDG_CONFIG_HOME contains a directory - // that is read-only, but the full path - // is writeable. - // Try to continue and see what happens. - // TODO: Find a more robust solution. - if (errno == B_READ_ONLY_DEVICE) - goto next; -#endif - DPRINTF_S("mkdir1!"); - DPRINTF_S(strerror(errno)); - *slash = '/'; - return FALSE; - } + /* Create folder from path to '\0' inserted at p */ + if (mkdir(path, 0777) == -1 && errno != EEXIST) { +#ifdef _HAIKU__ + // XDG_CONFIG_HOME contains a directory + // that is read-only, but the full path + // is writeable. + // Try to continue and see what happens. + // TODO: Find a more robust solution. + if (errno == B_READ_ONLY_DEVICE) + goto next; +#endif + DPRINTF_S("mkdir1!"); + DPRINTF_S(path); + DPRINTF_S(strerror(errno)); + *slash = '/'; + return FALSE; + } -#ifdef __HAIKU__ +#ifdef _HAIKU__ next: #endif - /* Restore path */ - *slash = '/'; - ++p; + /* Restore path */ + *slash = '/'; + ++p; + } } if (dir) { if (mkdir(path, 0777) == -1 && errno != EEXIST) { DPRINTF_S("mkdir2!"); + DPRINTF_S(path); DPRINTF_S(strerror(errno)); return FALSE; } @@ -5856,7 +5860,7 @@ static bool handle_archive(char *fpath /* in-out param */, char op) } /* Do not create smart context for current dir */ if (!(*outdir == '.' && outdir[1] == '\0')) { - if (!xmktree(outdir, TRUE) || (chdir(outdir) == -1)) { + if (!xmktree(outdir, TRUE, TRUE) || (chdir(outdir) == -1)) { printwarn(NULL); return FALSE; } @@ -5976,7 +5980,7 @@ static bool archive_mount(char *newpath) mkpath(mntpath, dir, newpath); free(dir); - if (!xmktree(newpath, TRUE)) { + if (!xmktree(newpath, TRUE, TRUE)) { printwarn(NULL); return FALSE; } @@ -6034,7 +6038,7 @@ static bool remote_mount(char *newpath) /* Create the mount point */ mkpath(cfgpath, toks[TOK_MNT], mntpath); mkpath(mntpath, tmp, newpath); - if (!xmktree(newpath, TRUE)) { + if (!xmktree(newpath, TRUE, TRUE)) { printwarn(NULL); return FALSE; } @@ -9403,7 +9407,7 @@ nochange: /* Check if it's a dir or file */ if (r == 'f' || r == 'd') { - ret = xmktree(tmp, r == 'f' ? FALSE : TRUE); + ret = xmktree(tmp, r == 'f' ? FALSE : TRUE, TRUE); } else if (r == 's' || r == 'h') { if (nselected > 1 && tmp[0] == '@' && tmp[1] == '\0') tmp[0] = '\0'; @@ -9707,7 +9711,7 @@ static char *make_tmp_tree(char **paths, ssize_t entries, const char *prefix) *slash = '\0'; if (access(tmpdir, F_OK)) /* Create directory if it doesn't exist */ - xmktree(tmpdir, TRUE); + xmktree(tmpdir, TRUE, TRUE); if (slash) *slash = '/'; @@ -10004,11 +10008,17 @@ static bool setup_config(void) xstrsncpy(cfgpath + r - 1, "/nnn", len - r); DPRINTF_S(cfgpath); + if (!xmktree(cfgpath, TRUE, TRUE)) { + DPRINTF_S(cfgpath); + xerror(); + return FALSE; + } + /* Create bookmarks, sessions, mounts and plugins directories */ for (r = 0; r < ELEMENTS(toks); ++r) { mkpath(cfgpath, toks[r], plgpath); /* The dirs are created on first run, check if they already exist */ - if (access(plgpath, F_OK) && !xmktree(plgpath, TRUE)) { + if (!xmktree(plgpath, TRUE, FALSE)) { DPRINTF_S(toks[r]); xerror(); return FALSE; @@ -10403,7 +10413,7 @@ int main(int argc, char *argv[]) g_state.initfile = 1; } if (dir || (arg != initpath)) { /* We have a directory */ - if (!xdiraccess(initpath) && !xmktree(initpath, TRUE)) { + if (!xdiraccess(initpath) && !xmktree(initpath, TRUE, TRUE)) { xerror(); /* Fail if directory cannot be created */ return EXIT_FAILURE; }