From 7e63729cfd781f69f7cb47b85cf8b1fee6b3e62a Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Sun, 27 Jun 2010 18:46:36 -0700 Subject: [PATCH] core, chdir: collapse slashes, avoid copy-to-self Collapse multiple slashes into one (this still doesn't resolve . and .. in the path, since that requires awareness of symlinks.) This code also avoids a copy-over-self bug by introducing a temporary buffer. Reported-by: Gene Cumm Signed-off-by: H. Peter Anvin --- core/fs/chdir.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 48 insertions(+), 11 deletions(-) diff --git a/core/fs/chdir.c b/core/fs/chdir.c index bfce9bc..9e8dfd2 100644 --- a/core/fs/chdir.c +++ b/core/fs/chdir.c @@ -16,15 +16,56 @@ void pm_realpath(com32sys_t *regs) realpath(dst, src, FILENAME_MAX); } +#define EMIT(x) \ +do { \ + if (++n < bufsize) \ + *q++ = (x); \ +} while (0) + +static size_t join_paths(char *dst, size_t bufsize, + const char *s1, const char *s2) +{ + const char *list[2]; + int i; + char c; + const char *p; + char *q = dst; + size_t n = 0; + bool slash = false; + + list[0] = s1; + list[1] = s2; + + for (i = 0; i < 2; i++) { + p = list[i]; + + while ((c = *p++)) { + if (c == '/') { + if (!slash) + EMIT(c); + slash = true; + } else { + EMIT(c); + slash = false; + } + } + } + + if (bufsize) + *q = '\0'; + + return n; +} + size_t realpath(char *dst, const char *src, size_t bufsize) { if (this_fs->fs_ops->realpath) { return this_fs->fs_ops->realpath(this_fs, dst, src, bufsize); } else { /* Filesystems with "common" pathname resolution */ - return snprintf(dst, bufsize, "%s%s", - src[0] == '/' ? "" : this_fs->cwd_name, - src); + return join_paths(dst, bufsize, + src[0] == '/' ? "" : this_fs->cwd_name, + src); } } @@ -32,7 +73,7 @@ int chdir(const char *src) { int rv; struct file *file; - char *p; + char cwd_buf[CURRENTDIR_MAX]; if (this_fs->fs_ops->chdir) return this_fs->fs_ops->chdir(this_fs, src); @@ -53,14 +94,10 @@ int chdir(const char *src) _close_file(file); /* Save the current working directory */ - realpath(this_fs->cwd_name, src, CURRENTDIR_MAX); - p = strchr(this_fs->cwd_name, '\0'); + realpath(cwd_buf, src, CURRENTDIR_MAX); /* Make sure the cwd_name ends in a slash, it's supposed to be a prefix */ - if (p < this_fs->cwd_name + CURRENTDIR_MAX - 1 && - (p == this_fs->cwd_name || p[1] != '/')) { - p[0] = '/'; - p[1] = '\0'; - } + join_paths(this_fs->cwd_name, CURRENTDIR_MAX, cwd_buf, "/"); + return 0; } -- 2.7.4