diff options
author | H. Peter Anvin <hpa@zytor.com> | 2010-06-27 18:46:36 -0700 |
---|---|---|
committer | H. Peter Anvin <hpa@zytor.com> | 2010-06-27 18:46:36 -0700 |
commit | 7e63729cfd781f69f7cb47b85cf8b1fee6b3e62a (patch) | |
tree | 4592aa54dc967b8a82b25546d20549dc71135a26 /core/fs | |
parent | 82fa68da1ae6d7bea7cd6d0d4dda883ef2c2fd3d (diff) | |
download | syslinux-7e63729cfd781f69f7cb47b85cf8b1fee6b3e62a.tar.gz |
core, chdir: collapse slashes, avoid copy-to-selfsyslinux-4.00-pre64
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 <gene.cumm@gmail.com>
Signed-off-by: H. Peter Anvin <hpa@zytor.com>
Diffstat (limited to 'core/fs')
-rw-r--r-- | core/fs/chdir.c | 59 |
1 files changed, 48 insertions, 11 deletions
diff --git a/core/fs/chdir.c b/core/fs/chdir.c index bfce9bce..9e8dfd2e 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; } |