[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