Put all handling of symbolic links into the central pathname walker.
Signed-off-by: H. Peter Anvin <hpa@zytor.com>
-#define DEBUG
-
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
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);
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)
file->inode = inode;
file->offset = 0;
file->file_len = inode->size;
-
+
return file_to_handle(file);
err:
#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,
}
-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;
}
/*
.load_config = generic_load_config,
.iget_root = ext2_iget_root,
.iget = ext2_iget,
- .follow_symlink = ext2_follow_symlink,
+ .readlink = ext2_readlink,
.readdir = ext2_readdir
};
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 *);