Re: Symlink on VFAT

Shenghuo ZHU (zsh@cs.rochester.edu)
12 Aug 1999 00:07:33 -0400


--=-=-=

>>>>> "Gordon" == Gordon Chaffee <chaffee@bmrc.berkeley.edu> writes:

Gordon> Thanks, I had been meaning to do this myself. I'll try to get
Gordon> it incorporated.

Hi,

There is a big change in FAT codes of 2.2.11, so the previous patch
will not work. Here is a new patch for 2.2.11. In this patch, I moved
the codes to FAT. Now both MSDOS and VFAT file systems can support
symlink.

If anyone want to test this patch,
1. apply the patch to 2.2.11
2. check Filesystem/FAT symlink support
3. compile and install the kernel
4. mount MSDOS or VFAT file system with option symlink, i.e.
mount -t vfat -o symlink /dev/hda1 /c

ChangeLog:

1999-08-11 Shenghuo ZHU <zsh@cs.rochester.edu>

* fat/inode.c (fat_fill_inode): Symlink extension.
* fat/symlink.c: New file.

--=-=-=
Content-Type: text/x-patch
Content-Disposition: attachment; filename=patch-fat-symlink-2.2.11.diff

--- linux/include/linux/msdos_fs.h.orig Wed Aug 11 17:33:32 1999
+++ linux/include/linux/msdos_fs.h Wed Aug 11 18:30:21 1999
@@ -274,6 +274,13 @@
extern int fat_mmap(struct file *, struct vm_area_struct *);
extern int fat_readpage(struct file *, struct page *);

+/* symlink.c */
+extern int fat_readlink (struct dentry *, char *, int);
+extern struct dentry *fat_follow_link(struct dentry *, struct dentry *,
+ unsigned int);
+extern int fat_symlink (struct inode * dir, struct dentry *dentry,
+ const char * symname);
+extern int fat_is_symlink(struct inode *inode);

/* vfat.c */
extern int init_vfat_fs(void);
--- linux/include/linux/msdos_fs_sb.h.orig Wed Aug 11 16:07:00 1999
+++ linux/include/linux/msdos_fs_sb.h Wed Aug 11 18:23:31 1999
@@ -24,7 +24,8 @@
posixfs:1, /* Allow names like makefile and Makefile to coexist */
numtail:1, /* Does first alias have a numeric '~1' type tail? */
atari:1, /* Use Atari GEMDOS variation of MS-DOS fs */
- fat32:1; /* Is this a FAT32 partition? */
+ fat32:1, /* Is this a FAT32 partition? */
+ symlink:1; /* support symlink */
};

struct vfat_unicode {
--- linux/fs/Config.in.orig Wed Aug 11 16:06:50 1999
+++ linux/fs/Config.in Wed Aug 11 17:42:13 1999
@@ -18,6 +18,9 @@
dep_tristate ' MSDOS fs support' CONFIG_MSDOS_FS $CONFIG_FAT_FS
dep_tristate ' UMSDOS: Unix-like filesystem on top of standard MSDOS filesystem' CONFIG_UMSDOS_FS $CONFIG_MSDOS_FS
dep_tristate ' VFAT (Windows-95) fs support' CONFIG_VFAT_FS $CONFIG_FAT_FS
+if [ "$CONFIG_EXPERIMENTAL" = "y" -a "$CONFIG_FAT_FS" != "n" ]; then
+ bool ' FAT symlink support (experimental)' CONFIG_FAT_SYMLINK
+fi

tristate 'ISO 9660 CDROM filesystem support' CONFIG_ISO9660_FS
if [ "$CONFIG_ISO9660_FS" != "n" ]; then
--- linux/fs/fat/Makefile.orig Wed Aug 11 17:22:43 1999
+++ linux/fs/fat/Makefile Wed Aug 11 17:23:48 1999
@@ -12,4 +12,8 @@
OX_OBJS := fatfs_syms.o
M_OBJS := $(O_TARGET)

+ifeq ($(CONFIG_FAT_SYMLINK),y)
+O_OBJS += symlink.o
+endif
+
include $(TOPDIR)/Rules.make
--- linux/fs/fat/fatfs_syms.c.orig Wed Aug 11 17:10:58 1999
+++ linux/fs/fat/fatfs_syms.c Wed Aug 11 19:04:13 1999
@@ -59,6 +59,11 @@
EXPORT_SYMBOL(fat_readpage);
EXPORT_SYMBOL(fat_add_entries);
EXPORT_SYMBOL(fat_dir_empty);
+#ifdef CONFIG_FAT_SYMLINK
+EXPORT_SYMBOL(fat_symlink);
+EXPORT_SYMBOL(fat_follow_link);
+EXPORT_SYMBOL(fat_readlink);
+#endif

int init_fat_fs(void)
{
--- linux/fs/fat/symlink.c.orig Wed Aug 11 16:08:05 1999
+++ linux/fs/fat/symlink.c Wed Aug 11 18:25:43 1999
@@ -0,0 +1,170 @@
+/*
+ * linux/fs/fat/symlink.c
+ *
+ * Written 1999 by Shenghuo Zhu
+ *
+ * FAT symlink support. Cygwin b20 compatible.
+ */
+
+#define __NO_VERSION__
+#include <linux/module.h>
+
+#include <asm/uaccess.h>
+#include <linux/msdos_fs.h>
+#include "msbuffer.h"
+
+struct inode_operations fat_symlink_inode_operations = {
+ NULL, /* no file-operations */
+ NULL, /* create */
+ NULL, /* lookup */
+ NULL, /* link */
+ NULL, /* unlink */
+ NULL, /* symlink */
+ NULL, /* mkdir */
+ NULL, /* rmdir */
+ NULL, /* mknod */
+ NULL, /* rename */
+ fat_readlink, /* readlink */
+ fat_follow_link, /* follow_link */
+ NULL, /* readpage */
+ NULL, /* writepage */
+ NULL, /* bmap */
+ NULL, /* truncate */
+ NULL, /* permission */
+ NULL /* smap */
+};
+
+struct dentry * fat_follow_link(struct dentry * dentry,
+ struct dentry *base,
+ unsigned int follow)
+{
+ struct inode *inode = dentry->d_inode;
+ struct super_block *sb = inode->i_sb;
+ struct buffer_head * bh = NULL;
+ char * link;
+ int sector=(MSDOS_I(inode)->i_start-2)*
+ MSDOS_SB(sb)->cluster_size+MSDOS_SB(sb)->data_start;
+
+ if (!(bh = fat_bread(sb, sector))) {
+ printk("dev = %s, sector = %d\n",
+ kdevname(inode->i_dev), sector);
+ dput(base);
+ return ERR_PTR(-EIO);
+ }
+ link=bh->b_data+10;
+ UPDATE_ATIME(inode);
+ base = lookup_dentry(link, base, follow);
+ if (bh)
+ brelse(bh);
+ return base;
+}
+
+int fat_readlink (struct dentry * dentry, char * buffer, int buflen)
+{
+ struct inode *inode = dentry->d_inode;
+ struct super_block *sb = inode->i_sb;
+ struct buffer_head * bh = NULL;
+ char * link;
+ int i;
+ int sector=(MSDOS_I(inode)->i_start-2)*
+ MSDOS_SB(sb)->cluster_size+MSDOS_SB(sb)->data_start;
+
+ if (buflen > sb->s_blocksize - 1)
+ buflen = sb->s_blocksize - 1;
+
+ if (!(bh = fat_bread(sb, sector))) {
+ printk("dev = %s, sector = %d\n",
+ kdevname(inode->i_dev), sector);
+ fat_fs_panic(sb, "fat_readlink");
+ return 0;
+ }
+ link=bh->b_data+10;
+ i = 0;
+ while (i < buflen && link[i])
+ i++;
+ if (copy_to_user(buffer, link, i))
+ i = -EFAULT;
+ UPDATE_ATIME(inode);
+ if (bh)
+ brelse (bh);
+ return i;
+}
+
+static const char fat_symlink_magic[]="!<symlink>";
+
+int fat_is_symlink(struct inode *inode)
+{
+ struct super_block *sb = inode->i_sb;
+ int sector=(MSDOS_I(inode)->i_start-2)*
+ MSDOS_SB(sb)->cluster_size+MSDOS_SB(sb)->data_start;
+ struct buffer_head *bh;
+ int ret;
+ if (!(bh = fat_bread(sb, sector))) {
+ printk("dev = %s, sector = %d\n",
+ kdevname(inode->i_dev), sector);
+ fat_fs_panic(sb, "is_symlink");
+ return 0;
+ }
+ ret = *(unsigned long *)(bh->b_data) ==
+ *(const unsigned long *)fat_symlink_magic &&
+ *(unsigned long *)(bh->b_data+4) ==
+ *(const unsigned long *)(fat_symlink_magic+4) &&
+ *(unsigned short *)(bh->b_data+8) ==
+ *(const unsigned short *)(fat_symlink_magic+8);
+ fat_brelse(sb, bh);
+ return ret;
+}
+
+int fat_symlink (struct inode * dir, struct dentry *dentry,
+ const char * symname)
+{
+ struct inode * inode =NULL;
+ struct buffer_head * bh = NULL;
+ char * link;
+ int i, err = -EIO;
+ char c;
+ const char *magic=fat_symlink_magic;
+ struct super_block *sb=dir->i_sb;
+ int sector;
+ if (! MSDOS_SB(sb)->options.symlink) return -EPERM;
+ if (! dir->i_op || ! dir->i_op->create) return -EPERM;
+ if ((err=dir->i_op->create(dir, dentry, 0)) < 0)
+ goto out_no_entry;
+ inode=dentry->d_inode;
+ if ((err=fat_add_cluster(inode))<0)
+ goto out_no_entry;
+ inode->i_mode = S_IFLNK | S_IRWXUGO;
+ inode->i_op = &fat_symlink_inode_operations;
+ MSDOS_I(inode)->i_attrs = ATTR_SYS | ATTR_ARCH;
+ sector=(MSDOS_I(inode)->i_start-2)*
+ MSDOS_SB(sb)->cluster_size+MSDOS_SB(sb)->data_start;
+ if (!(bh = fat_bread(sb, sector))) {
+ printk("dev = %s, sector = %d\n",
+ kdevname(inode->i_dev), sector);
+ fat_fs_panic(sb, "fat_symlink");
+ return 0;
+ }
+ link = bh->b_data;
+ i = 0;
+ while (i < inode->i_sb->s_blocksize - 1 && (c = *(magic++)))
+ link[i++] = c;
+ while (i < inode->i_sb->s_blocksize - 1 && (c = *(symname++)))
+ link[i++] = c;
+ link[i++] = 0;
+ inode->i_size = i;
+ mark_inode_dirty(inode);
+
+ dir->i_version = ++event;
+
+ fat_mark_buffer_dirty(sb,bh, 1);
+ brelse (bh);
+ err = 0;
+ out:
+ return err;
+
+ out_no_entry:
+ inode->i_nlink--;
+ mark_inode_dirty(inode);
+ iput (inode);
+ goto out;
+}
--- linux/fs/fat/inode.c.orig Wed Aug 11 16:39:08 1999
+++ linux/fs/fat/inode.c Wed Aug 11 19:11:16 1999
@@ -217,6 +217,9 @@
opts->codepage = 0;
opts->utf8 = 0;
opts->iocharset = NULL;
+#ifdef CONFIG_FAT_SYMLINK
+ opts->symlink=0;
+#endif
*debug = *fat = 0;

if (!options)
@@ -348,6 +351,10 @@
if (!value)
return 0;
strncpy(cvf_options,value,100);
+#ifdef CONFIG_FAT_SYMLINK
+ } else if (!strcmp(this_char,"symlink")) {
+ opts->symlink = 1;
+#endif
}

if (this_char != options) *(this_char-1) = ',';
@@ -747,6 +754,10 @@
return 0;
}

+#ifdef CONFIG_FAT_SYMLINK
+extern struct inode_operations fat_symlink_inode_operations;
+#endif
+
/* doesn't deal with root inode */
static void fat_fill_inode(struct inode *inode, struct msdos_dir_entry *de)
{
@@ -832,6 +843,16 @@
? date_dos2unix(CF_LE_W(de->ctime),CF_LE_W(de->cdate))
: inode->i_mtime;
MSDOS_I(inode)->i_ctime_ms = de->ctime_ms;
+#ifdef CONFIG_FAT_SYMLINK
+ if ( MSDOS_SB(sb)->options.symlink &&
+ !S_ISDIR(inode->i_mode) &&
+ de->attr == (ATTR_SYS | ATTR_ARCH) &&
+ fat_is_symlink(inode)) {
+ inode->i_mode = S_IRWXUGO | S_IFLNK;
+ inode->i_op = &fat_symlink_inode_operations;
+ inode->i_flags &= ~S_IMMUTABLE;
+ }
+#endif
}

void fat_write_inode(struct inode *inode)
--- linux/fs/vfat/namei.c.orig Wed Aug 11 16:46:54 1999
+++ linux/fs/vfat/namei.c Wed Aug 11 18:22:11 1999
@@ -1229,7 +1229,11 @@
vfat_lookup, /* lookup */
NULL, /* link */
vfat_unlink, /* unlink */
+#ifdef CONFIG_FAT_SYMLINK
+ fat_symlink, /* symlink */
+#else
NULL, /* symlink */
+#endif
vfat_mkdir, /* mkdir */
vfat_rmdir, /* rmdir */
NULL, /* mknod */
--- linux/fs/msdos/namei.c.orig Wed Aug 11 16:06:51 1999
+++ linux/fs/msdos/namei.c Wed Aug 11 17:22:01 1999
@@ -620,7 +620,11 @@
msdos_lookup, /* lookup */
NULL, /* link */
msdos_unlink, /* unlink */
+#ifdef CONFIG_FAT_SYMLINK
+ fat_symlink, /* symlink */
+#else
NULL, /* symlink */
+#endif
msdos_mkdir, /* mkdir */
msdos_rmdir, /* rmdir */
NULL, /* mknod */

--=-=-=

-- 
Shenghuo

--=-=-=--

- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.rutgers.edu Please read the FAQ at http://www.tux.org/lkml/