summaryrefslogtreecommitdiffstats
path: root/fs/efs/symlink.c
diff options
context:
space:
mode:
authorMike Shaver <shaver@ingenia.com>1997-09-16 15:05:24 +0000
committerMike Shaver <shaver@ingenia.com>1997-09-16 15:05:24 +0000
commit4e589e524e0c4c23fd8c6217696bd9983dc8a721 (patch)
treeeecd52c3e576218b98aa69d57cbaaa030e5a4f29 /fs/efs/symlink.c
parent6ce1685bcf39c7a589acdeb9639e71c4dde91ab0 (diff)
EFS integration (read-only, slight problem with symlinks).
Diffstat (limited to 'fs/efs/symlink.c')
-rw-r--r--fs/efs/symlink.c136
1 files changed, 136 insertions, 0 deletions
diff --git a/fs/efs/symlink.c b/fs/efs/symlink.c
new file mode 100644
index 000000000..6197bb6f3
--- /dev/null
+++ b/fs/efs/symlink.c
@@ -0,0 +1,136 @@
+/* symlink.c
+ *
+ * Symbolic link handling for EFS
+ *
+ * (C)1995,96 Christian Vogelgsang
+ *
+ * Based on the symlink.c from minix-fs by Linus
+ */
+
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/efs_fs.h>
+#include <linux/stat.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <asm/uaccess.h>
+
+static int efs_readlink(struct inode *, char *, int);
+static struct dentry * efs_follow_link(struct inode *, struct dentry *);
+
+struct inode_operations efs_symlink_in_ops = {
+ NULL, /* no file-operations */
+ NULL, /* create */
+ NULL, /* lookup */
+ NULL, /* link */
+ NULL, /* unlink */
+ NULL, /* symlink */
+ NULL, /* mkdir */
+ NULL, /* rmdir */
+ NULL, /* mknod */
+ NULL, /* rename */
+ efs_readlink, /* readlink */
+ efs_follow_link, /* follow_link */
+ NULL,
+ NULL,
+ NULL, /* bmap */
+ NULL, /* truncate */
+ NULL /* permission */
+};
+
+
+/* ----- efs_getlinktarget -----
+ read the target of the link from the data zone of the file
+*/
+static char *efs_getlinktarget(struct inode *in)
+{
+ struct buffer_head * bh;
+ char *name;
+ __u32 size = in->i_size;
+ __u32 block;
+
+ /* link data longer than 1024 not supported */
+ if(size>2*EFS_BLOCK_SIZE) {
+ printk("efs_getlinktarget: name too long: %lu\n",in->i_size);
+ return NULL;
+ }
+
+ /* get some memory from the kernel to store the name */
+ name = kmalloc(size+1,GFP_KERNEL);
+ if(!name) return NULL;
+
+ /* read first 512 bytes of target */
+ block = efs_bmap(in,0);
+ bh = bread(in->i_dev,block,EFS_BLOCK_SIZE);
+ if(!bh) {
+ kfree(name);
+ return NULL;
+ }
+ memcpy(name,bh->b_data,(size>EFS_BLOCK_SIZE)?EFS_BLOCK_SIZE:size);
+ brelse(bh);
+
+ /* if the linktarget is long, read the next block */
+ if(size>EFS_BLOCK_SIZE) {
+ bh = bread(in->i_dev,block+1,EFS_BLOCK_SIZE);
+ if(!bh) {
+ kfree(name);
+ return NULL;
+ }
+ memcpy(name+EFS_BLOCK_SIZE,bh->b_data,size-EFS_BLOCK_SIZE);
+ brelse(bh);
+ }
+
+ /* terminate string and return it */
+ name[size]=0;
+ return name;
+}
+
+
+/* ----- efs_follow_link -----
+ get the inode of the link target
+*/
+static struct dentry * efs_follow_link(struct inode * dir, struct dentry *base)
+{
+ struct buffer_head * bh;
+ __u32 block;
+
+ block = efs_bmap(dir,0);
+ bh = bread(dir->i_dev,block,EFS_BLOCK_SIZE);
+ if (!bh) {
+ dput(base);
+ return ERR_PTR(-EIO);
+ }
+ UPDATE_ATIME(dir);
+ base = lookup_dentry(bh->b_data, base, 1);
+ brelse(bh);
+ return base;
+}
+
+/* ----- efs_readlink -----
+ read the target of a link and return the name
+*/
+static int efs_readlink(struct inode * dir, char * buffer, int buflen)
+{
+ int i;
+ char c;
+ struct buffer_head * bh;
+ __u32 block;
+
+ block = efs_bmap(dir,0);
+ if (buflen > 1023)
+ buflen = 1023;
+ bh = bread(dir->i_dev,block,EFS_BLOCK_SIZE);
+ if (!bh)
+ return 0;
+
+ /* copy the link target to the given buffer */
+ i = 0;
+ while (i<buflen && (c = bh->b_data[i])) {
+ i++;
+ put_user(c,buffer++);
+ }
+
+ brelse(bh);
+ return i;
+}