[syslinux:pathbased] fs: centralize symlink handling
syslinux-bot for H. Peter Anvin
hpa at zytor.com
Mon Feb 15 22:48:04 PST 2010
Commit-ID: c0fbd21bef3f781cec554595455e123ba58b939b
Gitweb: http://syslinux.zytor.com/commit/c0fbd21bef3f781cec554595455e123ba58b939b
Author: H. Peter Anvin <hpa at zytor.com>
AuthorDate: Mon, 15 Feb 2010 22:45:59 -0800
Committer: H. Peter Anvin <hpa at zytor.com>
CommitDate: Mon, 15 Feb 2010 22:45:59 -0800
fs: centralize symlink handling
Put all handling of symbolic links into the central pathname walker.
Signed-off-by: H. Peter Anvin <hpa at zytor.com>
---
core/fs.c | 99 ++++++++++++++++++++++++++++++++-------------------
core/fs/ext2/ext2.c | 52 ++++++---------------------
core/include/fs.h | 2 +-
3 files changed, 74 insertions(+), 79 deletions(-)
diff --git a/core/fs.c b/core/fs.c
index 77c51ac..4df7ab7 100644
--- a/core/fs.c
+++ b/core/fs.c
@@ -1,5 +1,3 @@
-#define DEBUG
-
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
@@ -157,9 +155,9 @@ int searchdir(const char *name)
struct inode *inode;
struct inode *parent;
struct file *file;
- char part[256];
+ char part[256], pathbuf[4096], linkbuf[4096];
char *p;
- int symlink_count = 6;
+ int symlink_count = 20;
dprintf("Filename: %s\n", name);
@@ -177,42 +175,69 @@ int searchdir(const char *name)
goto err;
}
+
/* else, try the generic-path-lookup method */
- if (*name == '/') {
- parent = get_inode(this_fs->root);
- while (*name == '/')
- name++;
- } else {
- parent = get_inode(this_fs->cwd);
- }
- inode = NULL;
-
- while (*name) {
- p = part;
- while (*name && *name != '/')
- *p++ = *name++;
- *p = '\0';
- if (strcmp(part, ".")) {
- inode = this_fs->fs_ops->iget(part, parent);
- if (!inode)
- goto err;
- if (inode->mode == I_SYMLINK) {
- if (!this_fs->fs_ops->follow_symlink ||
- --symlink_count == 0 || /* limit check */
- inode->size >= BLOCK_SIZE(this_fs))
+
+ parent = get_inode(this_fs->cwd);
+
+ do {
+ got_link:
+ if (*name == '/') {
+ put_inode(parent);
+ parent = get_inode(this_fs->root);
+ while (*name == '/')
+ name++;
+ }
+
+ inode = NULL;
+
+ 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;
- name = this_fs->fs_ops->follow_symlink(inode, name);
- put_inode(inode);
- continue;
+ if (inode->mode == I_SYMLINK) {
+ int link_len;
+ int name_len = strlen(name) + 1;
+
+ if (!this_fs->fs_ops->readlink ||
+ --symlink_count == 0 || /* limit check */
+ inode->size + name_len >= sizeof 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)
+ goto err;
+
+ p = linkbuf + link_len;
+ if (p[-1] != '/')
+ *p++ = '/';
+
+ strlcpy(p, name, sizeof linkbuf - (p-linkbuf));
+
+ name = strcpy(pathbuf, linkbuf);
+ put_inode(inode);
+ goto got_link;
+ }
+ put_inode(parent);
+ parent = inode;
}
- put_inode(parent);
- parent = inode;
+ if (!*name)
+ break;
+ while (*name == '/')
+ name++;
}
- if (!*name)
- break;
- while (*name == '/')
- name++;
- }
+ } while (0);
+
put_inode(parent);
if (!inode)
@@ -221,7 +246,7 @@ int searchdir(const char *name)
file->inode = inode;
file->offset = 0;
file->file_len = inode->size;
-
+
return file_to_handle(file);
err:
diff --git a/core/fs/ext2/ext2.c b/core/fs/ext2/ext2.c
index 960b3e8..1a81dd5 100644
--- a/core/fs/ext2/ext2.c
+++ b/core/fs/ext2/ext2.c
@@ -9,22 +9,6 @@
#include "ext2_fs.h"
/*
- * just like the function strcpy(), except it returns non-zero if overflow.
- *
- */
-static int strecpy(char *dst, const char *src, char *end)
-{
- while (*src != '\0')
- *dst++ = *src++;
- *dst = '\0';
-
- if (dst > end)
- return 1;
- else
- return 0;
-}
-
-/*
* get the group's descriptor of group_num
*/
struct ext2_group_desc * ext2_get_group_desc(struct fs_info *fs,
@@ -309,40 +293,26 @@ static struct inode *ext2_iget(char *dname, struct inode *parent)
}
-static char *ext2_follow_symlink(struct inode *inode, const char *name_left)
+int ext2_readlink(struct inode *inode, char *buf)
{
struct fs_info *fs = inode->fs;
int sec_per_block = 1 << (fs->block_shift - fs->sector_shift);
- int fast_symlink;
- char *symlink_buf;
- char *p;
+ bool fast_symlink;
struct cache_struct *cs;
+ size_t bytes = inode->size;
- symlink_buf = malloc(BLOCK_SIZE(fs));
- if (!symlink_buf) {
- malloc_error("symlink buffer");
- return NULL;
- }
+ if (inode->size > BLOCK_SIZE(fs))
+ return -1; /* Error! */
+
fast_symlink = (inode->file_acl ? sec_per_block : 0) == inode->blocks;
if (fast_symlink) {
- memcpy(symlink_buf, inode->pvt, inode->size);
+ memcpy(buf, inode->pvt, bytes);
} else {
cs = get_cache_block(fs->fs_dev, *(uint32_t *)inode->pvt);
- memcpy(symlink_buf, cs->data, inode->size);
+ memcpy(buf, cs->data, bytes);
}
- p = symlink_buf + inode->size;
-
- if (*name_left)
- *p++ = '/';
- if (strecpy(p, name_left, symlink_buf + BLOCK_SIZE(fs))) {
- free(symlink_buf);
- return NULL;
- }
- if(!(p = strdup(symlink_buf)))
- return symlink_buf;
-
- free(symlink_buf);
- return p;
+
+ return bytes;
}
/*
@@ -439,6 +409,6 @@ const struct fs_ops ext2_fs_ops = {
.load_config = generic_load_config,
.iget_root = ext2_iget_root,
.iget = ext2_iget,
- .follow_symlink = ext2_follow_symlink,
+ .readlink = ext2_readlink,
.readdir = ext2_readdir
};
diff --git a/core/include/fs.h b/core/include/fs.h
index d7feb7e..cc9d21c 100644
--- a/core/include/fs.h
+++ b/core/include/fs.h
@@ -65,7 +65,7 @@ struct fs_ops {
struct inode * (*iget_root)(struct fs_info *);
struct inode * (*iget)(char *, struct inode *);
- char * (*follow_symlink)(struct inode *, const char *);
+ int (*readlink)(struct inode *, char *);
/* the _dir_ stuff */
struct dirent * (*readdir)(struct file *);
More information about the Syslinux-commits
mailing list