summaryrefslogtreecommitdiff
path: root/core/fs
diff options
context:
space:
mode:
authorH. Peter Anvin <hpa@zytor.com>2010-06-27 18:46:36 -0700
committerH. Peter Anvin <hpa@zytor.com>2010-06-27 18:46:36 -0700
commit7e63729cfd781f69f7cb47b85cf8b1fee6b3e62a (patch)
tree4592aa54dc967b8a82b25546d20549dc71135a26 /core/fs
parent82fa68da1ae6d7bea7cd6d0d4dda883ef2c2fd3d (diff)
downloadsyslinux-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.c59
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;
}