summaryrefslogtreecommitdiffstats
path: root/fs/affs/symlink.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/affs/symlink.c')
-rw-r--r--fs/affs/symlink.c82
1 files changed, 73 insertions, 9 deletions
diff --git a/fs/affs/symlink.c b/fs/affs/symlink.c
index 24d1ed118..22042158a 100644
--- a/fs/affs/symlink.c
+++ b/fs/affs/symlink.c
@@ -19,7 +19,8 @@
#define MIN(a,b) (((a) < (b)) ? (a) : (b))
-static int affs_readlink(struct inode *, char *, int);
+static int affs_readlink(struct inode *, char *, int);
+static struct dentry *affs_follow_link(struct inode *inode, struct dentry *base);
struct inode_operations affs_symlink_inode_operations = {
NULL, /* no file-operations */
@@ -33,9 +34,13 @@ struct inode_operations affs_symlink_inode_operations = {
NULL, /* mknod */
NULL, /* rename */
affs_readlink, /* readlink */
+ affs_follow_link, /* follow_link */
+ NULL, /* readpage */
+ NULL, /* writepage */
NULL, /* bmap */
NULL, /* truncate */
- NULL /* permission */
+ NULL, /* permission */
+ NULL /* smap */
};
static int
@@ -46,21 +51,24 @@ affs_readlink(struct inode *inode, char *buffer, int buflen)
int i, j;
char c;
char lc;
+ char *pf;
pr_debug("AFFS: readlink(ino=%lu,buflen=%d)\n",inode->i_ino,buflen);
bh = affs_bread(inode->i_dev,inode->i_ino,AFFS_I2BSIZE(inode));
- i = 0;
- j = 0;
if (!bh) {
- affs_error(inode->i_sb,"readlink","Cannot read block %lu\n",inode->i_ino);
- goto symlink_end;
+ affs_warning(inode->i_sb,"follow_link","Unable to read i-node block %lu\n",
+ inode->i_ino);
+ return -EIO;
}
lf = (struct slink_front *)bh->b_data;
lc = 0;
+ i = 0;
+ j = 0;
+ pf = inode->i_sb->u.affs_sb.s_prefix ? inode->i_sb->u.affs_sb.s_prefix : "/";
if (strchr(lf->symname,':')) { /* Handle assign or volume name */
- while (i < buflen && (c = inode->i_sb->u.affs_sb.s_prefix[i])) {
+ while (i < buflen && (c = pf[i])) {
put_user(c,buffer++);
i++;
}
@@ -84,8 +92,64 @@ affs_readlink(struct inode *inode, char *buffer, int buflen)
lc = c;
i++, j++;
}
-symlink_end:
- iput(inode);
affs_brelse(bh);
return i;
}
+
+static struct dentry *
+affs_follow_link(struct inode *inode, struct dentry *base)
+{
+ struct buffer_head *bh;
+ struct slink_front *lf;
+ char *buffer;
+ int i, j;
+ char c;
+ char lc;
+ char *pf;
+
+ pr_debug("AFFS: follow_link(ino=%lu)\n",inode->i_ino);
+
+ if (!(buffer = kmalloc(1024,GFP_KERNEL))) {
+ dput(base);
+ return ERR_PTR(-ENOSPC);
+ }
+ bh = affs_bread(inode->i_dev,inode->i_ino,AFFS_I2BSIZE(inode));
+ if (!bh) {
+ affs_warning(inode->i_sb,"follow_link","Unable to read i-node block %lu\n",
+ inode->i_ino);
+ kfree(buffer);
+ dput(base);
+ return ERR_PTR(-EIO);
+ }
+ i = 0;
+ j = 0;
+ lf = (struct slink_front *)bh->b_data;
+ lc = 0;
+ pf = inode->i_sb->u.affs_sb.s_prefix ? inode->i_sb->u.affs_sb.s_prefix : "/";
+
+ if (strchr(lf->symname,':')) { /* Handle assign or volume name */
+ while (i < 1023 && (c = pf[i]))
+ buffer[i++] = c;
+ while (i < 1023 && lf->symname[j] != ':')
+ buffer[i++] = lf->symname[j++];
+ if (i < 1023)
+ buffer[i++] = '/';
+ j++;
+ lc = '/';
+ }
+ while (i < 1023 && (c = lf->symname[j])) {
+ if (c == '/' && lc == '/' && i < 1020) { /* parent dir */
+ buffer[i++] = '.';
+ buffer[i++] = '.';
+ }
+ buffer[i++] = c;
+ lc = c;
+ j++;
+ }
+ buffer[i] = '\0';
+ affs_brelse(bh);
+ base = lookup_dentry(buffer,base,1);
+ kfree(buffer);
+ return base;
+}
+