summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorH. Peter Anvin <hpa@zytor.com>2010-02-16 09:33:28 -0800
committerH. Peter Anvin <hpa@zytor.com>2010-02-16 09:33:28 -0800
commit7021cdf0235262a6ee0abefda93b178343e83074 (patch)
treedd4dfd8b3a588110e9b37408214eea6185b70977
parent5bce60d1b4222fb36ab786fc89e1fa98a9edf7e1 (diff)
downloadsyslinux-4.00-pre23.tar.gz
fs: use malloc'd mutable pathname bufferssyslinux-4.00-pre23
Use malloc'd mutable pathname buffers during pathname walks. This avoids the need for very large static buffers, as well as the need to copy character by character. It also handles the symlink case fairly nicely. Signed-off-by: H. Peter Anvin <hpa@zytor.com>
-rw-r--r--core/fs.c128
1 files changed, 80 insertions, 48 deletions
diff --git a/core/fs.c b/core/fs.c
index 4df7ab78..d67a87ca 100644
--- a/core/fs.c
+++ b/core/fs.c
@@ -11,6 +11,10 @@ struct fs_info *this_fs = NULL; /* Root filesystem */
/* Actual file structures (we don't have malloc yet...) */
struct file files[MAX_OPEN];
+/* Symlink hard limits */
+#define MAX_SYMLINK_CNT 20
+#define MAX_SYMLINK_BUF 4096
+
/*
* Get a new inode structure
*/
@@ -92,7 +96,7 @@ void pm_unmangle_name(com32sys_t *regs)
{
const char *src = MK_PTR(regs->ds, regs->esi.w[0]);
char *dst = MK_PTR(regs->es, regs->edi.w[0]);
-
+
dst = unmangle_name(dst, src);
/* Update the di register to point to the last null char */
@@ -112,15 +116,15 @@ void getfssec(com32sys_t *regs)
char *buf;
struct file *file;
uint16_t handle;
-
+
sectors = regs->ecx.w[0];
handle = regs->esi.w[0];
file = handle_to_file(handle);
-
+
buf = MK_PTR(regs->es, regs->ebx.w[0]);
bytes_read = file->fs->fs_ops->getfssec(file, buf, sectors, &have_more);
-
+
/*
* If we reach EOF, the filesystem driver will have already closed
* the underlying file... this really should be cleaner.
@@ -129,7 +133,7 @@ void getfssec(com32sys_t *regs)
_close_file(file);
regs->esi.w[0] = 0;
}
-
+
regs->ecx.l = bytes_read;
}
@@ -152,15 +156,13 @@ void pm_searchdir(com32sys_t *regs)
int searchdir(const char *name)
{
- struct inode *inode;
- struct inode *parent;
+ struct inode *inode = NULL;
+ struct inode *parent = NULL;
struct file *file;
- char part[256], pathbuf[4096], linkbuf[4096];
- char *p;
- int symlink_count = 20;
+ char *pathbuf = NULL;
+ char *part, *p, echar;
+ int symlink_count = MAX_SYMLINK_CNT;
- dprintf("Filename: %s\n", name);
-
if (!(file = alloc_file()))
goto err_no_close;
file->fs = this_fs;
@@ -168,7 +170,7 @@ int searchdir(const char *name)
/* if we have ->searchdir method, call it */
if (file->fs->fs_ops->searchdir) {
file->fs->fs_ops->searchdir(name, file);
-
+
if (file->open_file)
return file_to_handle(file);
else
@@ -179,66 +181,90 @@ int searchdir(const char *name)
/* else, try the generic-path-lookup method */
parent = get_inode(this_fs->cwd);
+ p = pathbuf = strdup(name);
+ if (!pathbuf)
+ goto err;
do {
got_link:
- if (*name == '/') {
+ if (*p == '/') {
put_inode(parent);
parent = get_inode(this_fs->root);
- while (*name == '/')
- name++;
}
- inode = NULL;
+ do {
+ while (*p == '/')
+ p++;
+
+ if (!*p)
+ break;
+
+ part = p;
+ while ((echar = *p) && echar != '/')
+ p++;
+ *p++ = '\0';
- while (*name) {
- p = part;
- while (*name && *name != '/')
- *p++ = *name++;
- *p = '\0';
if (part[0] != '.' || part[1] != '\0') {
inode = this_fs->fs_ops->iget(part, parent);
if (!inode)
goto err;
if (inode->mode == I_SYMLINK) {
+ char *linkbuf, *q;
+ int name_len = echar ? strlen(p) : 0;
+ int total_len = inode->size + name_len + (echar ? 2 : 1);
int link_len;
- int name_len = strlen(name) + 1;
- if (!this_fs->fs_ops->readlink ||
+ if (!this_fs->fs_ops->readlink ||
--symlink_count == 0 || /* limit check */
- inode->size + name_len >= sizeof linkbuf)
+ total_len > MAX_SYMLINK_BUF)
+ goto err;
+
+ linkbuf = malloc(total_len);
+ if (!linkbuf)
goto err;
- /*
- * Note: we can't generally put this in pathbuf directly,
- * because the old "name" may very well be in
- * pathbuf already.
- */
link_len = this_fs->fs_ops->readlink(inode, linkbuf);
- if (link_len <= 0)
+ if (link_len <= 0) {
+ free(linkbuf);
goto err;
-
- p = linkbuf + link_len;
- if (p[-1] != '/')
- *p++ = '/';
-
- strlcpy(p, name, sizeof linkbuf - (p-linkbuf));
-
- name = strcpy(pathbuf, linkbuf);
+ }
+
+ q = linkbuf + link_len;
+
+ if (echar) {
+ if (link_len > 0 && q[-1] != '/')
+ *q++ = '/';
+
+ memcpy(q, p, name_len+1);
+ } else {
+ *q = '\0';
+ }
+
+ free(pathbuf);
+ p = pathbuf = linkbuf;
put_inode(inode);
+ inode = NULL;
goto got_link;
}
+
put_inode(parent);
+ parent = NULL;
+
+ if (!echar)
+ break;
+
+ if (inode->mode != I_DIR)
+ goto err;
+
parent = inode;
+ inode = NULL;
}
- if (!*name)
- break;
- while (*name == '/')
- name++;
- }
+ } while (echar);
} while (0);
+ free(pathbuf);
put_inode(parent);
+ parent = NULL;
if (!inode)
goto err;
@@ -248,8 +274,14 @@ int searchdir(const char *name)
file->file_len = inode->size;
return file_to_handle(file);
-
+
err:
+ if (inode)
+ put_inode(inode);
+ if (parent)
+ put_inode(parent);
+ if (pathbuf)
+ free(pathbuf);
_close_file(file);
err_no_close:
return -1;
@@ -259,7 +291,7 @@ void close_file(com32sys_t *regs)
{
uint16_t handle = regs->esi.w[0];
struct file *file;
-
+
if (handle) {
file = handle_to_file(handle);
_close_file(file);
@@ -288,10 +320,10 @@ void fs_init(com32sys_t *regs)
struct device *dev = NULL;
/* ops is a ptr list for several fs_ops */
const struct fs_ops **ops = (const struct fs_ops **)regs->eax.l;
-
+
/* Initialize malloc() */
mem_init();
-
+
/* Default name for the root directory */
fs.cwd_name[0] = '/';