fs: centralize symlink handling
authorH. Peter Anvin <hpa@zytor.com>
Tue, 16 Feb 2010 06:45:59 +0000 (22:45 -0800)
committerH. Peter Anvin <hpa@zytor.com>
Tue, 16 Feb 2010 06:45:59 +0000 (22:45 -0800)
Put all handling of symbolic links into the central pathname walker.

Signed-off-by: H. Peter Anvin <hpa@zytor.com>
core/fs.c
core/fs/ext2/ext2.c
core/include/fs.h

index 77c51ac..4df7ab7 100644 (file)
--- 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:
index 960b3e8..1a81dd5 100644 (file)
@@ -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
 };
index d7feb7e..cc9d21c 100644 (file)
@@ -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 *);