summaryrefslogtreecommitdiffstats
path: root/fs/umsdos
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>1994-11-28 11:59:19 +0000
committer <ralf@linux-mips.org>1994-11-28 11:59:19 +0000
commit1513ff9b7899ab588401c89db0e99903dbf5f886 (patch)
treef69cc81a940a502ea23d664c3ffb2d215a479667 /fs/umsdos
Import of Linus's Linux 1.1.68
Diffstat (limited to 'fs/umsdos')
-rw-r--r--fs/umsdos/Makefile44
-rw-r--r--fs/umsdos/README84
-rw-r--r--fs/umsdos/check.c55
-rw-r--r--fs/umsdos/dir.c706
-rw-r--r--fs/umsdos/emd.c505
-rw-r--r--fs/umsdos/file.c103
-rw-r--r--fs/umsdos/inode.c513
-rw-r--r--fs/umsdos/ioctl.c259
-rw-r--r--fs/umsdos/mangle.c478
-rw-r--r--fs/umsdos/namei.c1043
-rw-r--r--fs/umsdos/notes17
-rw-r--r--fs/umsdos/rdir.c239
-rw-r--r--fs/umsdos/symlink.c145
13 files changed, 4191 insertions, 0 deletions
diff --git a/fs/umsdos/Makefile b/fs/umsdos/Makefile
new file mode 100644
index 000000000..cfba11e63
--- /dev/null
+++ b/fs/umsdos/Makefile
@@ -0,0 +1,44 @@
+#
+# Makefile for the umsdos unix-like filesystem routines.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definitions are now in the main makefile...
+
+ifndef CONFIG_UMSDOS_FS
+CFLAGS := $(CFLAGS) -DMODULE
+endif
+
+.c.s:
+ $(CC) $(CFLAGS) -S $<
+.c.o:
+ $(CC) $(CFLAGS) -c $<
+.s.o:
+ $(AS) -o $*.o $<
+
+OBJS= dir.o emd.o file.o inode.o ioctl.o mangle.o namei.o\
+ rdir.o symlink.o #check.o
+
+umsdos.o: $(OBJS)
+ $(LD) -r -o umsdos.o $(OBJS)
+
+clean:
+ rm -f core *.o *.a *.s
+
+dep:
+ $(CPP) -M *.c > .depend
+
+p:
+ proto *.c >/usr/include/linux/umsdos_fs.p
+
+doc:
+ nadoc -i -p umsdos.doc - /tmp/umsdos.mpg
+
+#
+# include a dependency file if one exists
+#
+ifeq (.depend,$(wildcard .depend))
+include .depend
+endif
diff --git a/fs/umsdos/README b/fs/umsdos/README
new file mode 100644
index 000000000..4ce8b4148
--- /dev/null
+++ b/fs/umsdos/README
@@ -0,0 +1,84 @@
+Very short explanation for the impatient!!!
+
+Umsdos is a file system driver that run on top the MSDOS fs driver.
+It is written by Jacques Gelinas (jacques@solucorp.qc.ca)
+
+Umsdos is not a file system per se, but a twist to make a boring
+one into a useful one.
+
+It gives you:
+
+ long file name
+ Permissions and owner
+ Links
+ Special files (devices, pipe...)
+ All is need to be a linux root fs.
+
+There is plenty of documentation on it in the source. A formated document
+made from those comments is available from
+sunsite.unc.edu:/pub/Linux/ALPHA/umsdos
+
+Mostly...
+
+You mount a DOS partition like this
+
+mount -t umsdos /dev/hda3 /mnt
+ ^
+---------|
+
+All option are passed to the msdos drivers. Option like uid,gid etc are
+given to msdos.
+
+The default behavior of Umsdos is to do the same thing as the msdos driver
+mostly passing commands to it without much processing. Again, this is
+the default. After doing the mount on a DOS partition, nothing special
+happen. This is why all mount options are passed to the Msdos fs driver.
+
+Umsdos use a special DOS file --linux-.--- to store the information
+which can't be handle by the normal MsDOS file system. This is the trick.
+
+--linux-.--- is optional. There is one per directory.
+
+**** If --linux-.--- is missing, then Umsdos process the directory the
+ same way the msdos driver do. Short file name, no goodies, default
+ owner and permissions. So each directory may have or not this
+ --linux-.---
+
+Now, how to get those --linux-.---.
+
+\begin joke_section
+
+ Well send me a directory content
+ and I will send you one customised for you.
+ $5 per directory. Add any applicable taxes.
+\end joke_section
+
+A utility umssync creates those and maintain them. It is available
+from the same directory above (sunsite) in the file umsdos_progs-0.3.tar.gz.
+A compiled version is available in umsdos-0.3a.bin.tar.gz.
+
+So in our example, after mounting mnt, we do
+
+umssync .
+
+This will promote this directory (a recursive option is available) to full
+umsdos capabilities (long name ...). A ls -l before and after won't show
+much difference however. The file which were there are still there. But now
+you can do all this:
+
+ chmod 644 *
+ chown you.your_groupe *
+ ls >THIS_IS.A.VERY.LONG.NAME
+ ln -s toto tata
+ ls -l
+
+Once a directory is promoted, all subdirectory created will inherit that
+promotion.
+
+What happen if you boot DOS and create files in those promoted directories ?
+Umsdos won't notice new files, but will signal removed file (it won't crash).
+Using umssync in /etc/rc will make sure the DOS directory is in sync with
+the --linux-.---.
+
+Hope this helps!
+
diff --git a/fs/umsdos/check.c b/fs/umsdos/check.c
new file mode 100644
index 000000000..d1102b4ce
--- /dev/null
+++ b/fs/umsdos/check.c
@@ -0,0 +1,55 @@
+#include <asm/system.h>
+
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/head.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/ptrace.h>
+#include <linux/mman.h>
+
+extern unsigned long high_memory;
+
+static int check_one_table(unsigned long * page_dir)
+{
+ unsigned long pg_table = *page_dir;
+
+ if (!pg_table)
+ return 0;
+ if (pg_table >= high_memory || !(pg_table & PAGE_PRESENT)) {
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * This function frees up all page tables of a process when it exits.
+ */
+void check_page_tables(void)
+{
+ unsigned long pg_dir;
+ static int err = 0;
+
+ int stack_level = (long)(&pg_dir)-current->kernel_stack_page;
+ if (stack_level < 1500) printk ("** %d ** ",stack_level);
+ pg_dir = current->tss.cr3;
+ if (mem_map[MAP_NR(pg_dir)] > 1) {
+ return;
+ }
+ if (err == 0){
+ unsigned long *page_dir = (unsigned long *) pg_dir;
+ unsigned long *base = page_dir;
+ int i;
+ for (i = 0 ; i < PTRS_PER_PAGE ; i++,page_dir++){
+ int notok = check_one_table(page_dir);
+ if (notok){
+ err++;
+ printk ("|%d| ",page_dir-base);
+ }
+ }
+ if (err) printk ("Erreur MM %d\n",err);
+ }
+}
+
diff --git a/fs/umsdos/dir.c b/fs/umsdos/dir.c
new file mode 100644
index 000000000..2a668d102
--- /dev/null
+++ b/fs/umsdos/dir.c
@@ -0,0 +1,706 @@
+/*
+ * linux/fs/umsdos/dir.c
+ *
+ * Written 1993 by Jacques Gelinas
+ * Inspired from linux/fs/msdos/... : Werner Almesberger
+ *
+ * Extended MS-DOS directory handling functions
+ */
+
+#include <asm/segment.h>
+
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/fs.h>
+#include <linux/msdos_fs.h>
+#include <linux/errno.h>
+#include <linux/stat.h>
+#include <linux/limits.h>
+#include <linux/umsdos_fs.h>
+#include <linux/malloc.h>
+
+#define PRINTK(x)
+#define Printk(x) printk x
+
+#define UMSDOS_SPECIAL_DIRFPOS 3
+extern struct inode *pseudo_root;
+/*
+ So grep * doesn't complain in the presence of directories.
+*/
+int UMSDOS_dir_read(struct inode *inode,struct file *filp,char *buf,
+ int count)
+{
+ return -EISDIR;
+}
+/*
+ Read count directory entries from directory filp
+ Return a negative value from linux/errno.h.
+ Return > 0 if success (the length of the file name).
+
+ This function is used by the normal readdir VFS entry point and by
+ some function who try to find out info on a file from a pure MSDOS
+ inode. See umsdos_locate_ancestor() below.
+*/
+static int umsdos_readdir_x(
+ struct inode *dir, /* Point to a description of the super block */
+ struct file *filp, /* Point to a directory which is read */
+ struct dirent *dirent, /* Will hold count directory entry */
+ int dirent_in_fs, /* dirent point in user's space ? */
+ int count,
+ struct umsdos_dirent *u_entry, /* Optional umsdos entry */
+ int follow_hlink,
+ off_t *pt_f_pos) /* will hold the offset of the entry in EMD */
+{
+ int ret = 0;
+
+ umsdos_startlookup(dir);
+ if (filp->f_pos == UMSDOS_SPECIAL_DIRFPOS
+ && dir == pseudo_root
+ && dirent_in_fs){
+ /*
+ We don't need to simulate this pseudo directory
+ when umsdos_readdir_x is called for internal operation
+ of umsdos. This is why dirent_in_fs is tested
+ */
+ /* #Specification: pseudo root / directory /DOS
+ When umsdos operates in pseudo root mode (C:\linux is the
+ linux root), it simulate a directory /DOS which points to
+ the real root of the file system.
+ */
+ put_fs_long(dir->i_sb->s_mounted->i_ino,&dirent->d_ino);
+ memcpy_tofs (dirent->d_name,"DOS",3);
+ put_fs_byte(0,dirent->d_name+3);
+ put_fs_word (3,&dirent->d_reclen);
+ if (u_entry != NULL) u_entry->flags = 0;
+ ret = 3;
+ filp->f_pos++;
+ }else if (filp->f_pos < 2
+ || (dir != dir->i_sb->s_mounted && filp->f_pos == 32)){
+ /* #Specification: readdir / . and ..
+ The msdos filesystem manage the . and .. entry properly
+ so the EMD file won't hold any info about it.
+
+ In readdir, we assume that for the root directory
+ the read position will be 0 for ".", 1 for "..". For
+ a non root directory, the read position will be 0 for "."
+ and 32 for "..".
+ */
+ /*
+ This is a trick used by the msdos file system (fs/msdos/dir.c)
+ to manage . and .. for the root directory of a file system.
+ Since there is no such entry in the root, fs/msdos/dir.c
+ use the following:
+
+ if f_pos == 0, return ".".
+ if f_pos == 1, return "..".
+
+ So let msdos handle it
+
+ Since umsdos entries are much larger, we share the same f_pos.
+ if f_pos is 0 or 1 or 32, we are clearly looking at . and
+ ..
+
+ As soon as we get f_pos == 2 or f_pos == 64, then back to
+ 0, but this time we are reading the EMD file.
+
+ Well, not so true. The problem, is that UMSDOS_REC_SIZE is
+ also 64, so as soon as we read the first record in the
+ EMD, we are back at offset 64. So we set the offset
+ to UMSDOS_SPECIAL_DIRFPOS(3) as soon as we have read the
+ .. entry from msdos.
+ */
+ ret = msdos_readdir(dir,filp,dirent,count);
+ if (filp->f_pos == 64) filp->f_pos = UMSDOS_SPECIAL_DIRFPOS;
+ if (u_entry != NULL) u_entry->flags = 0;
+ }else{
+ struct inode *emd_dir = umsdos_emd_dir_lookup(dir,0);
+ if (emd_dir != NULL){
+ if (filp->f_pos <= UMSDOS_SPECIAL_DIRFPOS+1) filp->f_pos = 0;
+ PRINTK (("f_pos %ld i_size %d\n",filp->f_pos,emd_dir->i_size));
+ ret = 0;
+ while (filp->f_pos < emd_dir->i_size){
+ struct umsdos_dirent entry;
+ off_t cur_f_pos = filp->f_pos;
+ if (umsdos_emd_dir_readentry (emd_dir,filp,&entry)!=0){
+ ret = -EIO;
+ break;
+ }else if (entry.name_len != 0){
+ /* #Specification: umsdos / readdir
+ umsdos_readdir() should fill a struct dirent with
+ an inode number. The cheap way to get it is to
+ do a lookup in the MSDOS directory for each
+ entry processed by the readdir() function.
+ This is not very efficient, but very simple. The
+ other way around is to maintain a copy of the inode
+ number in the EMD file. This is a problem because
+ this has to be maintained in sync using tricks.
+ Remember that MSDOS (the OS) does not update the
+ modification time (mtime) of a directory. There is
+ no easy way to tell that a directory was modified
+ during a DOS session and synchronise the EMD file.
+
+ Suggestion welcome.
+
+ So the easy way is used!
+ */
+ struct umsdos_info info;
+ struct inode *inode;
+ int lret;
+ umsdos_parse (entry.name,entry.name_len,&info);
+ info.f_pos = cur_f_pos;
+ *pt_f_pos = cur_f_pos;
+ umsdos_manglename (&info);
+ lret = umsdos_real_lookup (dir,info.fake.fname
+ ,info.fake.len,&inode);
+ PRINTK (("Cherche inode de %s lret %d flags %d\n"
+ ,info.fake.fname,lret,entry.flags));
+ if (lret == 0
+ && (entry.flags & UMSDOS_HLINK)
+ && follow_hlink){
+ struct inode *rinode;
+ lret = umsdos_hlink2inode (inode,&rinode);
+ inode = rinode;
+ }
+ if (lret == 0){
+ /* #Specification: pseudo root / reading real root
+ The pseudo root (/linux) is logically
+ erased from the real root. This mean that
+ ls /DOS, won't show "linux". This avoids
+ infinite recursion /DOS/linux/DOS/linux while
+ walking the file system.
+ */
+ if (inode != pseudo_root){
+ PRINTK (("Trouve ino %d ",inode->i_ino));
+ if (dirent_in_fs){
+ put_fs_long(inode->i_ino,&dirent->d_ino);
+ memcpy_tofs (dirent->d_name,entry.name
+ ,entry.name_len);
+ put_fs_byte(0,dirent->d_name+entry.name_len);
+ put_fs_word (entry.name_len
+ ,&dirent->d_reclen);
+ /* In this case, the caller only needs */
+ /* flags */
+ if (u_entry != NULL){
+ u_entry->flags = entry.flags;
+ }
+ }else{
+ dirent->d_ino = inode->i_ino;
+ memcpy (dirent->d_name,entry.name
+ ,entry.name_len);
+ dirent->d_name[entry.name_len] = '\0';
+ dirent->d_reclen = entry.name_len;
+ if (u_entry != NULL) *u_entry = entry;
+ }
+ ret = entry.name_len;
+ iput (inode);
+ break;
+ }
+ iput (inode);
+ }else{
+ /* #Specification: umsdos / readdir / not in MSDOS
+ During a readdir operation, if the file is not
+ in the MSDOS directory anymore, the entry is
+ removed from the EMD file silently.
+ */
+ ret = umsdos_writeentry (dir,emd_dir,&info,1);
+ if (ret != 0){
+ break;
+ }
+ }
+ }
+ }
+ iput(emd_dir);
+ }
+ }
+ umsdos_endlookup(dir);
+ PRINTK (("read dir %p pos %d ret %d\n",dir,filp->f_pos,ret));
+ return ret;
+}
+/*
+ Read count directory entries from directory filp
+ Return a negative value from linux/errno.h.
+ Return > 0 if success (the length of the file name).
+*/
+static int UMSDOS_readdir(
+ struct inode *dir, /* Point to a description of the super block */
+ struct file *filp, /* Point to a directory which is read */
+ struct dirent *dirent, /* Will hold count directory entry */
+ int count)
+{
+ int ret = -ENOENT;
+ while (1){
+ struct umsdos_dirent entry;
+ off_t f_pos;
+ ret = umsdos_readdir_x (dir,filp,dirent,1,count,&entry,1,&f_pos);
+ if (ret <= 0 || !(entry.flags & UMSDOS_HIDDEN)) break;
+ }
+ return ret;
+}
+/*
+ Complete the inode content with info from the EMD file
+*/
+void umsdos_lookup_patch (
+ struct inode *dir,
+ struct inode *inode,
+ struct umsdos_dirent *entry,
+ off_t emd_pos)
+{
+ /*
+ This function modify the state of a dir inode. It decides
+ if the dir is a umsdos dir or a dos dir. This is done
+ deeper in umsdos_patch_inode() called at the end of this function.
+
+ umsdos_patch_inode() may block because it is doing disk access.
+ At the same time, another process may get here to initialise
+ the same dir inode. There is 3 cases.
+
+ 1-The inode is already initialised. We do nothing.
+ 2-The inode is not initialised. We lock access and do it.
+ 3-Like 2 but another process has lock the inode, so we try
+ to lock it and right after check if initialisation is still
+ needed.
+
+
+ Thanks to the mem option of the kernel command line, it was
+ possible to consistently reproduce this problem by limiting
+ my mem to 4 meg and running X.
+ */
+ /*
+ Do this only if the inode is freshly read, because we will lose
+ the current (updated) content.
+ */
+ /*
+ A lookup of a mount point directory yield the inode into
+ the other fs, so we don't care about initialising it. iget()
+ does this automatically.
+ */
+ if (inode->i_sb == dir->i_sb && !umsdos_isinit(inode)){
+ if (S_ISDIR(inode->i_mode)) umsdos_lockcreate(inode);
+ if (!umsdos_isinit(inode)){
+ /* #Specification: umsdos / lookup / inode info
+ After successfully reading an inode from the MSDOS
+ filesystem, we use the EMD file to complete it.
+ We update the following field.
+
+ uid, gid, atime, ctime, mtime, mode.
+
+ We rely on MSDOS for mtime. If the file
+ was modified during an MSDOS session, at least
+ mtime will be meaningful. We do this only for regular
+ file.
+
+ We don't rely on MSDOS for mtime for directory because
+ the MSDOS directory date is creation time (strange
+ MSDOS behavior) which fit nowhere in the three UNIX
+ time stamp.
+ */
+ if (S_ISREG(entry->mode)) entry->mtime = inode->i_mtime;
+ inode->i_mode = entry->mode;
+ inode->i_rdev = entry->rdev;
+ inode->i_atime = entry->atime;
+ inode->i_ctime = entry->ctime;
+ inode->i_mtime = entry->mtime;
+ inode->i_uid = entry->uid;
+ inode->i_gid = entry->gid;
+ /* #Specification: umsdos / i_nlink
+ The nlink field of an inode is maintain by the MSDOS file system
+ for directory and by UMSDOS for other file. The logic is that
+ MSDOS is already figuring out what to do for directories and
+ does nothing for other files. For MSDOS, there are no hard link
+ so all file carry nlink==1. UMSDOS use some info in the
+ EMD file to plug the correct value.
+ */
+ if (!S_ISDIR(entry->mode)){
+ if (entry->nlink > 0){
+ inode->i_nlink = entry->nlink;
+ }else{
+ printk ("UMSDOS: lookup_patch entry->nlink < 1 ???\n");
+ }
+ }
+ umsdos_patch_inode(inode,dir,emd_pos);
+ }
+ if (S_ISDIR(inode->i_mode)) umsdos_unlockcreate(inode);
+if (inode->u.umsdos_i.i_emd_owner==0) printk ("emd_owner still 0 ???\n");
+ }
+}
+/*
+ Locate entry of an inode in a directory.
+ Return 0 or a negative error code.
+
+ Normally, this function must succeed. It means a strange corruption
+ in the file system if not.
+*/
+int umsdos_inode2entry (
+ struct inode *dir,
+ struct inode *inode,
+ struct umsdos_dirent *entry) /* Will hold the entry */
+{
+ int ret = -ENOENT;
+ if (inode == pseudo_root){
+ /*
+ Quick way to find the name.
+ Also umsdos_readdir_x won't show /linux anyway
+ */
+ memcpy (entry->name,UMSDOS_PSDROOT_NAME,UMSDOS_PSDROOT_LEN+1);
+ entry->name_len = UMSDOS_PSDROOT_LEN;
+ ret = 0;
+ }else{
+ struct inode *emddir = umsdos_emd_dir_lookup(dir,0);
+ iput (emddir);
+ if (emddir == NULL){
+ /* This is a DOS directory */
+ struct file filp;
+ filp.f_reada = 1;
+ filp.f_pos = 0;
+ while (1){
+ struct dirent dirent;
+ if (umsdos_readdir_kmem (dir,&filp,&dirent,1) <= 0){
+ printk ("UMSDOS: can't locate inode %ld in DOS directory???\n"
+ ,inode->i_ino);
+ }else if (dirent.d_ino == inode->i_ino){
+ ret = 0;
+ memcpy (entry->name,dirent.d_name,dirent.d_reclen);
+ entry->name[dirent.d_reclen] = '\0';
+ entry->name_len = dirent.d_reclen;
+ inode->u.umsdos_i.i_dir_owner = dir->i_ino;
+ inode->u.umsdos_i.i_emd_owner = 0;
+ umsdos_setup_dir_inode(inode);
+ break;
+ }
+ }
+ }else{
+ /* skip . and .. see umsdos_readdir_x() */
+ struct file filp;
+ filp.f_reada = 1;
+ filp.f_pos = UMSDOS_SPECIAL_DIRFPOS;
+ while (1){
+ struct dirent dirent;
+ off_t f_pos;
+ if (umsdos_readdir_x(dir,&filp,&dirent
+ ,0,1,entry,0,&f_pos) <= 0){
+ printk ("UMSDOS: can't locate inode %ld in EMD file???\n"
+ ,inode->i_ino);
+ break;
+ }else if (dirent.d_ino == inode->i_ino){
+ ret = 0;
+ umsdos_lookup_patch (dir,inode,entry,f_pos);
+ break;
+ }
+ }
+ }
+ }
+ return ret;
+}
+/*
+ Locate the parent of a directory and the info on that directory
+ Return 0 or a negative error code.
+*/
+static int umsdos_locate_ancestor (
+ struct inode *dir,
+ struct inode **result,
+ struct umsdos_dirent *entry)
+{
+ int ret;
+ umsdos_patch_inode (dir,NULL,0);
+ ret = umsdos_real_lookup (dir,"..",2,result);
+ PRINTK (("result %d %x ",ret,*result));
+ if (ret == 0){
+ struct inode *adir = *result;
+ ret = umsdos_inode2entry (adir,dir,entry);
+ }
+ PRINTK (("\n"));
+ return ret;
+}
+/*
+ Build the path name of an inode (relative to the file system.
+ This function is need to set (pseudo) hard link.
+
+ It uses the same strategy as the standard getcwd().
+*/
+int umsdos_locate_path (
+ struct inode *inode,
+ char *path)
+{
+ int ret = 0;
+ struct inode *dir = inode;
+ char *bpath = (char*)kmalloc(PATH_MAX,GFP_KERNEL);
+ if (bpath == NULL){
+ ret = -ENOMEM;
+ }else{
+ struct umsdos_dirent entry;
+ char *ptbpath = bpath+PATH_MAX-1;
+ *ptbpath = '\0';
+ PRINTK (("locate_path mode %x ",inode->i_mode));
+ if (!S_ISDIR(inode->i_mode)){
+ ret = umsdos_get_dirowner (inode,&dir);
+ PRINTK (("locate_path ret %d ",ret));
+ if (ret == 0){
+ ret = umsdos_inode2entry (dir,inode,&entry);
+ if (ret == 0){
+ ptbpath -= entry.name_len;
+ memcpy (ptbpath,entry.name,entry.name_len);
+ PRINTK (("ptbpath :%s: ",ptbpath));
+ }
+ }
+ }else{
+ dir->i_count++;
+ }
+ if (ret == 0){
+ while (dir != dir->i_sb->s_mounted){
+ struct inode *adir;
+ ret = umsdos_locate_ancestor (dir,&adir,&entry);
+ iput (dir);
+ dir = NULL;
+ PRINTK (("ancestor %d ",ret));
+ if (ret == 0){
+ *--ptbpath = '/';
+ ptbpath -= entry.name_len;
+ memcpy (ptbpath,entry.name,entry.name_len);
+ dir = adir;
+ PRINTK (("ptbpath :%s: ",ptbpath));
+ }else{
+ break;
+ }
+ }
+ }
+ strcpy (path,ptbpath);
+ kfree (bpath);
+ }
+ PRINTK (("\n"));
+ iput (dir);
+ return ret;
+}
+
+/*
+ Return != 0 if an entry is the pseudo DOS entry in the pseudo root.
+*/
+int umsdos_is_pseudodos (
+ struct inode *dir,
+ const char *name,
+ int len)
+{
+ /* #Specification: pseudo root / DOS hard coded
+ The pseudo sub-directory DOS in the pseudo root is hard coded.
+ The name is DOS. This is done this way to help standardised
+ the umsdos layout. The idea is that from now on /DOS is
+ a reserved path and nobody will think of using such a path
+ for a package.
+ */
+ return dir == pseudo_root
+ && len == 3
+ && name[0] == 'D' && name[1] == 'O' && name[2] == 'S';
+}
+/*
+ Check if a file exist in the current directory.
+ Return 0 if ok, negative error code if not (ex: -ENOENT).
+*/
+static int umsdos_lookup_x (
+ struct inode *dir,
+ const char *name,
+ int len,
+ struct inode **result, /* Will hold inode of the file, if successful */
+ int nopseudo) /* Don't care about pseudo root mode */
+{
+ int ret = -ENOENT;
+ *result = NULL;
+ umsdos_startlookup(dir);
+ if (len == 1 && name[0] == '.'){
+ *result = dir;
+ dir->i_count++;
+ ret = 0;
+ }else if (len == 2 && name[0] == '.' && name[1] == '.'){
+ if (pseudo_root != NULL && dir == pseudo_root->i_sb->s_mounted){
+ /* #Specification: pseudo root / .. in real root
+ Whenever a lookup is those in the real root for
+ the directory .., and pseudo root is active, the
+ pseudo root is returned.
+ */
+ ret = 0;
+ *result = pseudo_root;
+ pseudo_root->i_count++;
+ }else{
+ /* #Specification: locating .. / strategy
+ We use the msdos filesystem to locate the parent directory.
+ But it is more complicated than that.
+
+ We have to step back even further to
+ get the parent of the parent, so we can get the EMD
+ of the parent of the parent. Using the EMD file, we can
+ locate all the info on the parent, such a permissions
+ and owner.
+ */
+ ret = umsdos_real_lookup (dir,"..",2,result);
+ PRINTK (("ancestor ret %d dir %p *result %p ",ret,dir,*result));
+ if (ret == 0
+ && *result != dir->i_sb->s_mounted
+ && *result != pseudo_root){
+ struct inode *aadir;
+ struct umsdos_dirent entry;
+ ret = umsdos_locate_ancestor (*result,&aadir,&entry);
+ iput (aadir);
+ }
+ }
+ }else if (umsdos_is_pseudodos(dir,name,len)){
+ /* #Specification: pseudo root / lookup(DOS)
+ A lookup of DOS in the pseudo root will always succeed
+ and return the inode of the real root.
+ */
+ *result = dir->i_sb->s_mounted;
+ (*result)->i_count++;
+ ret = 0;
+ }else{
+ struct umsdos_info info;
+ ret = umsdos_parse (name,len,&info);
+ if (ret == 0) ret = umsdos_findentry (dir,&info,0);
+ PRINTK (("lookup %s pos %d ret %d len %d ",info.fake.fname,info.f_pos,ret
+ ,info.fake.len));
+ if (ret == 0){
+ /* #Specification: umsdos / lookup
+ A lookup for a file is done in two step. First, we locate
+ the file in the EMD file. If not present, we return
+ an error code (-ENOENT). If it is there, we repeat the
+ operation on the msdos file system. If this fails, it means
+ that the file system is not in sync with the emd file.
+ We silently remove this entry from the emd file,
+ and return ENOENT.
+ */
+ struct inode *inode;
+ ret = umsdos_real_lookup (dir,info.fake.fname,info.fake.len,result);
+ inode = *result;
+ if (inode == NULL){
+ printk ("UMSDOS: Erase entry %s, out of sync with MsDOS\n"
+ ,info.fake.fname);
+ umsdos_delentry (dir,&info,S_ISDIR(info.entry.mode));
+ }else{
+ umsdos_lookup_patch (dir,inode,&info.entry,info.f_pos);
+ PRINTK (("lookup ino %d flags %d\n",inode->i_ino
+ ,info.entry.flags));
+ if (info.entry.flags & UMSDOS_HLINK){
+ ret = umsdos_hlink2inode (inode,result);
+ }
+ if (*result == pseudo_root && !nopseudo){
+ /* #Specification: pseudo root / dir lookup
+ For the same reason as readdir, a lookup in /DOS for
+ the pseudo root directory (linux) will fail.
+ */
+ /*
+ This has to be allowed for resolving hard link
+ which are recorded independently of the pseudo-root
+ mode.
+ */
+ iput (pseudo_root);
+ *result = NULL;
+ ret = -ENOENT;
+ }
+ }
+ }
+ }
+ umsdos_endlookup(dir);
+ iput (dir);
+ return ret;
+}
+/*
+ Check if a file exist in the current directory.
+ Return 0 if ok, negative error code if not (ex: -ENOENT).
+*/
+int UMSDOS_lookup (
+ struct inode *dir,
+ const char *name,
+ int len,
+ struct inode **result) /* Will hold inode of the file, if successful */
+{
+ return umsdos_lookup_x(dir,name,len,result,0);
+}
+/*
+ Locate the inode pointed by a (pseudo) hard link
+ Return 0 if ok, a negative error code if not.
+*/
+int umsdos_hlink2inode (struct inode *hlink, struct inode **result)
+{
+ int ret = -EIO;
+ char *path = (char*)kmalloc(PATH_MAX,GFP_KERNEL);
+ *result = NULL;
+ if (path == NULL){
+ ret = -ENOMEM;
+ iput (hlink);
+ }else{
+ struct file filp;
+ filp.f_reada = 1;
+ filp.f_pos = 0;
+ PRINTK (("hlink2inode "));
+ if (umsdos_file_read_kmem (hlink,&filp,path,hlink->i_size)
+ ==hlink->i_size){
+ struct inode *dir;
+ char *pt = path;
+ dir = hlink->i_sb->s_mounted;
+ path[hlink->i_size] = '\0';
+ iput (hlink);
+ dir->i_count++;
+ while (1){
+ char *start = pt;
+ int len;
+ while (*pt != '\0' && *pt != '/') pt++;
+ len = (int)(pt - start);
+ if (*pt == '/') *pt++ = '\0';
+ if (dir->u.umsdos_i.i_emd_dir == 0){
+ /* This is a DOS directory */
+ ret = msdos_lookup(dir,start,len,result);
+ }else{
+ ret = umsdos_lookup_x(dir,start,len,result,1);
+ }
+ PRINTK (("h2n lookup :%s: -> %d ",start,ret));
+ if (ret == 0 && *pt != '\0'){
+ dir = *result;
+ }else{
+ break;
+ }
+ }
+ }else{
+ iput (hlink);
+ }
+ PRINTK (("hlink2inode ret = %d %p -> %p\n",ret,hlink,*result));
+ kfree (path);
+ }
+ return ret;
+}
+
+static struct file_operations umsdos_dir_operations = {
+ NULL, /* lseek - default */
+ UMSDOS_dir_read, /* read */
+ NULL, /* write - bad */
+ UMSDOS_readdir, /* readdir */
+ NULL, /* select - default */
+ UMSDOS_ioctl_dir, /* ioctl - default */
+ NULL, /* mmap */
+ NULL, /* no special open code */
+ NULL, /* no special release code */
+ NULL /* fsync */
+};
+
+struct inode_operations umsdos_dir_inode_operations = {
+ &umsdos_dir_operations, /* default directory file-ops */
+ UMSDOS_create, /* create */
+ UMSDOS_lookup, /* lookup */
+ UMSDOS_link, /* link */
+ UMSDOS_unlink, /* unlink */
+ UMSDOS_symlink, /* symlink */
+ UMSDOS_mkdir, /* mkdir */
+ UMSDOS_rmdir, /* rmdir */
+ UMSDOS_mknod, /* mknod */
+ UMSDOS_rename, /* rename */
+ NULL, /* readlink */
+ NULL, /* follow_link */
+ NULL, /* bmap */
+ NULL, /* truncate */
+ NULL /* permission */
+};
+
+
+
+
+
+
+
+
+
+
diff --git a/fs/umsdos/emd.c b/fs/umsdos/emd.c
new file mode 100644
index 000000000..e4d6a9470
--- /dev/null
+++ b/fs/umsdos/emd.c
@@ -0,0 +1,505 @@
+/*
+ * linux/fs/umsdos/emd.c
+ *
+ * Written 1993 by Jacques Gelinas
+ *
+ * Extended MS-DOS directory handling functions
+ */
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/kernel.h>
+#include <asm/segment.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/msdos_fs.h>
+#include <linux/umsdos_fs.h>
+
+#define PRINTK(x)
+#define Printk(x) printk x
+
+int umsdos_readdir_kmem(
+ struct inode *inode,
+ struct file *filp,
+ struct dirent *dirent,
+ int count)
+{
+ int ret;
+ int old_fs = get_fs();
+ set_fs (KERNEL_DS);
+ ret = msdos_readdir(inode,filp,dirent,count);
+ set_fs (old_fs);
+ return ret;
+}
+/*
+ Read a file into kernel space memory
+*/
+int umsdos_file_read_kmem(
+ struct inode *inode,
+ struct file *filp,
+ char *buf,
+ int count)
+{
+ int ret;
+ int old_fs = get_fs();
+ set_fs (KERNEL_DS);
+ ret = msdos_file_read(inode,filp,buf,count);
+ set_fs (old_fs);
+ return ret;
+}
+/*
+ Write to a file from kernel space
+*/
+int umsdos_file_write_kmem(
+ struct inode *inode,
+ struct file *filp,
+ char *buf,
+ int count)
+{
+ int ret;
+ int old_fs = get_fs();
+ set_fs (KERNEL_DS);
+ ret = msdos_file_write(inode,filp,buf,count);
+ set_fs (old_fs);
+ return ret;
+}
+
+
+/*
+ Write a block of bytes into one EMD file.
+ The block of data is NOT in user space.
+
+ Return 0 if ok, a negative error code if not.
+*/
+int umsdos_emd_dir_write (
+ struct inode *emd_dir,
+ struct file *filp,
+ char *buf, /* buffer in kernel memory, not in user space */
+ int count)
+{
+ int written;
+ filp->f_flags = 0;
+ written = umsdos_file_write_kmem (emd_dir,filp,buf,count);
+ return written != count ? -EIO : 0;
+}
+/*
+ Read a block of bytes from one EMD file.
+ The block of data is NOT in user space.
+ Return 0 if ok, -EIO if any error.
+*/
+int umsdos_emd_dir_read (
+ struct inode *emd_dir,
+ struct file *filp,
+ char *buf, /* buffer in kernel memory, not in user space */
+ int count)
+{
+ int ret = 0;
+ int sizeread;
+ filp->f_flags = 0;
+ sizeread = umsdos_file_read_kmem (emd_dir,filp,buf,count);
+ if (sizeread != count){
+ printk ("UMSDOS: problem with EMD file. Can't read\n");
+ ret = -EIO;
+ }
+ return ret;
+
+}
+/*
+ Locate the EMD file in a directory and optionally, creates it.
+
+ Return NULL if error. If ok, dir->u.umsdos_i.emd_inode
+*/
+struct inode *umsdos_emd_dir_lookup(struct inode *dir, int creat)
+{
+ struct inode *ret = NULL;
+ if (dir->u.umsdos_i.i_emd_dir != 0){
+ ret = iget (dir->i_sb,dir->u.umsdos_i.i_emd_dir);
+ PRINTK (("deja trouve %d %x [%d] "
+ ,dir->u.umsdos_i.i_emd_dir,ret,ret->i_count));
+ }else{
+ umsdos_real_lookup (dir,UMSDOS_EMD_FILE,UMSDOS_EMD_NAMELEN,&ret);
+ PRINTK (("emd_dir_lookup "));
+ if (ret != NULL){
+ PRINTK (("Find --linux "));
+ dir->u.umsdos_i.i_emd_dir = ret->i_ino;
+ }else if (creat){
+ int code;
+ PRINTK (("avant create "));
+ dir->i_count++;
+ code = msdos_create (dir,UMSDOS_EMD_FILE,UMSDOS_EMD_NAMELEN
+ ,S_IFREG|0777,&ret);
+ PRINTK (("Creat EMD code %d ret %x ",code,ret));
+ if (ret != NULL){
+ dir->u.umsdos_i.i_emd_dir = ret->i_ino;
+ }else{
+ printk ("UMSDOS: Can't create EMD file\n");
+ }
+ }
+ }
+ if (ret != NULL){
+ /* Disable UMSDOS_notify_change() for EMD file */
+ ret->u.umsdos_i.i_emd_owner = 0xffffffff;
+ }
+ return ret;
+}
+
+/*
+ Read an entry from the EMD file.
+ Support variable length record.
+ Return -EIO if error, 0 if ok.
+*/
+int umsdos_emd_dir_readentry (
+ struct inode *emd_dir,
+ struct file *filp,
+ struct umsdos_dirent *entry)
+{
+ int ret = umsdos_emd_dir_read(emd_dir,filp,(char*)entry,UMSDOS_REC_SIZE);
+ if (ret == 0){
+ /* Variable size record. Maybe, we have to read some more */
+ int recsize = umsdos_evalrecsize (entry->name_len);
+ if (recsize > UMSDOS_REC_SIZE){
+ ret = umsdos_emd_dir_read(emd_dir,filp
+ ,((char*)entry)+UMSDOS_REC_SIZE,recsize - UMSDOS_REC_SIZE);
+
+ }
+ }
+ return ret;
+}
+/*
+ Write an entry in the EMD file.
+ Return 0 if ok, -EIO if some error.
+*/
+int umsdos_writeentry (
+ struct inode *dir,
+ struct inode *emd_dir,
+ struct umsdos_info *info,
+ int free_entry) /* This entry is deleted, so Write all 0's */
+{
+ int ret = 0;
+ struct file filp;
+ struct umsdos_dirent *entry = &info->entry;
+ struct umsdos_dirent entry0;
+ if (free_entry){
+ /* #Specification: EMD file / empty entries
+ Unused entry in the EMD file are identify
+ by the name_len field equal to 0. However to
+ help future extension (or bug correction :-( ),
+ empty entries are filled with 0.
+ */
+ memset (&entry0,0,sizeof(entry0));
+ entry = &entry0;
+ }else if (entry->name_len > 0){
+ memset (entry->name+entry->name_len,'\0'
+ ,sizeof(entry->name)-entry->name_len);
+ /* #Specification: EMD file / spare bytes
+ 10 bytes are unused in each record of the EMD. They
+ are set to 0 all the time. So it will be possible
+ to do new stuff and rely on the state of those
+ bytes in old EMD file around.
+ */
+ memset (entry->spare,0,sizeof(entry->spare));
+ }
+ filp.f_pos = info->f_pos;
+ filp.f_reada = 0;
+ ret = umsdos_emd_dir_write(emd_dir,&filp,(char*)entry,info->recsize);
+ if (ret != 0){
+ printk ("UMSDOS: problem with EMD file. Can't write\n");
+ }else{
+ dir->i_ctime = dir->i_mtime = CURRENT_TIME;
+ dir->i_dirt = 1;
+ }
+ return ret;
+}
+
+#define CHUNK_SIZE (8*UMSDOS_REC_SIZE)
+struct find_buffer{
+ char buffer[CHUNK_SIZE];
+ int pos; /* read offset in buffer */
+ int size; /* Current size of buffer */
+ struct file filp;
+};
+
+/*
+ Fill the read buffer and take care of the byte remaining inside.
+ Unread bytes are simply move to the beginning.
+
+ Return -ENOENT if EOF, 0 if ok, a negative error code if any problem.
+*/
+static int umsdos_fillbuf (
+ struct inode *inode,
+ struct find_buffer *buf)
+{
+ int ret = -ENOENT;
+ int mustmove = buf->size - buf->pos;
+ int mustread;
+ int remain;
+ if (mustmove > 0){
+ memcpy (buf->buffer,buf->buffer+buf->pos,mustmove);
+ }
+ buf->pos = 0;
+ mustread = CHUNK_SIZE - mustmove;
+ remain = inode->i_size - buf->filp.f_pos;
+ if (remain < mustread) mustread = remain;
+ if (mustread > 0){
+ ret = umsdos_emd_dir_read (inode,&buf->filp,buf->buffer+mustmove
+ ,mustread);
+ if (ret == 0) buf->size = mustmove + mustread;
+ }else if (mustmove){
+ buf->size = mustmove;
+ ret = 0;
+ }
+ return ret;
+}
+
+/*
+ General search, locate a name in the EMD file or an empty slot to
+ store it. if info->entry.name_len == 0, search the first empty
+ slot (of the proper size).
+
+ Caller must do iput on *pt_emd_dir.
+
+ Return 0 if found, -ENOENT if not found, another error code if
+ other problem.
+
+ So this routine is used to either find an existing entry or to
+ create a new one, while making sure it is a new one. After you
+ get -ENOENT, you make sure the entry is stuffed correctly and
+ call umsdos_writeentry().
+
+ To delete an entry, you find it, zero out the entry (memset)
+ and call umsdos_writeentry().
+
+ All this to say that umsdos_writeentry must be call after this
+ function since it rely on the f_pos field of info.
+*/
+static int umsdos_find (
+ struct inode *dir,
+ struct umsdos_info *info, /* Hold name and name_len */
+ /* Will hold the entry found */
+ struct inode **pt_emd_dir) /* Will hold the emd_dir inode */
+ /* or NULL if not found */
+{
+ /* #Specification: EMD file structure
+ The EMD file uses a fairly simple layout. It is made of records
+ (UMSDOS_REC_SIZE == 64). When a name can't be written is a single
+ record, multiple contiguous record are allocated.
+ */
+ int ret = -ENOENT;
+ struct inode *emd_dir = umsdos_emd_dir_lookup(dir,1);
+ if (emd_dir != NULL){
+ struct umsdos_dirent *entry = &info->entry;
+ int recsize = info->recsize;
+ struct {
+ off_t posok; /* Position available to store the entry */
+ int found; /* A valid empty position has been found */
+ off_t one; /* One empty position -> maybe <- large enough */
+ int onesize; /* size of empty region starting at one */
+ }empty;
+ /* Read several entries at a time to speed up the search */
+ struct find_buffer buf;
+ buf.pos = 0;
+ buf.size = 0;
+ buf.filp.f_pos = 0;
+ buf.filp.f_reada = 1;
+ empty.found = 0;
+ empty.posok = emd_dir->i_size;
+ empty.onesize = 0;
+ while (1){
+ struct umsdos_dirent *rentry = (struct umsdos_dirent*)
+ (buf.buffer + buf.pos);
+ int file_pos = buf.filp.f_pos - buf.size + buf.pos;
+ if (buf.pos == buf.size){
+ ret = umsdos_fillbuf (emd_dir,&buf);
+ if (ret < 0){
+ /* Not found, so note where it can be added */
+ info->f_pos = empty.posok;
+ break;
+ }
+ }else if (rentry->name_len == 0){
+ /* We are looking for an empty section at least */
+ /* recsize large */
+ if (entry->name_len == 0){
+ info->f_pos = file_pos;
+ ret = 0;
+ break;
+ }else if (!empty.found){
+ if (empty.onesize == 0){
+ /* This is the first empty record of a section */
+ empty.one = file_pos;
+ }
+ /* grow the empty section */
+ empty.onesize += UMSDOS_REC_SIZE;
+ if (empty.onesize == recsize){
+ /* here is a large enough section */
+ empty.posok = empty.one;
+ empty.found = 1;
+ }
+ }
+ buf.pos += UMSDOS_REC_SIZE;
+ }else{
+ int entry_size = umsdos_evalrecsize(rentry->name_len);
+ if (buf.pos+entry_size > buf.size){
+ ret = umsdos_fillbuf (emd_dir,&buf);
+ if (ret < 0){
+ /* Not found, so note where it can be added */
+ info->f_pos = empty.posok;
+ break;
+ }
+ }else{
+ empty.onesize = 0; /* Reset the free slot search */
+ if (entry->name_len == rentry->name_len
+ && memcmp(entry->name,rentry->name,rentry->name_len)
+ ==0){
+ info->f_pos = file_pos;
+ *entry = *rentry;
+ ret = 0;
+ break;
+ }else{
+ buf.pos += entry_size;
+ }
+ }
+ }
+ }
+ umsdos_manglename(info);
+ }
+ *pt_emd_dir = emd_dir;
+ return ret;
+}
+/*
+ Add a new entry in the emd file
+ Return 0 if ok or a negative error code.
+ Return -EEXIST if the entry already exist.
+
+ Complete the information missing in info.
+*/
+int umsdos_newentry (
+ struct inode *dir,
+ struct umsdos_info *info)
+{
+ struct inode *emd_dir;
+ int ret = umsdos_find (dir,info,&emd_dir);
+ if (ret == 0){
+ ret = -EEXIST;
+ }else if (ret == -ENOENT){
+ ret = umsdos_writeentry(dir,emd_dir,info,0);
+ PRINTK (("umsdos_newentry EDM ret = %d\n",ret));
+ }
+ iput (emd_dir);
+ return ret;
+}
+/*
+ Create a new hidden link.
+ Return 0 if ok, an error code if not.
+*/
+int umsdos_newhidden (
+ struct inode *dir,
+ struct umsdos_info *info)
+{
+ struct inode *emd_dir;
+ int ret;
+ umsdos_parse ("..LINK",6,info);
+ info->entry.name_len = 0;
+ ret = umsdos_find (dir,info,&emd_dir);
+ iput (emd_dir);
+ if (ret == -ENOENT || ret == 0){
+ /* #Specification: hard link / hidden name
+ When a hard link is created, the original file is renamed
+ to a hidden name. The name is "..LINKNNN" where NNN is a
+ number define from the entry offset in the EMD file.
+ */
+ info->entry.name_len = sprintf (info->entry.name,"..LINK%ld"
+ ,info->f_pos);
+ ret = 0;
+ }
+ return ret;
+}
+/*
+ Remove an entry from the emd file
+ Return 0 if ok, a negative error code otherwise.
+
+ Complete the information missing in info.
+*/
+int umsdos_delentry (
+ struct inode *dir,
+ struct umsdos_info *info,
+ int isdir)
+{
+ struct inode *emd_dir;
+ int ret = umsdos_find (dir,info,&emd_dir);
+ if (ret == 0){
+ if (info->entry.name_len != 0){
+ if ((isdir != 0) != (S_ISDIR(info->entry.mode) != 0)){
+ if (S_ISDIR(info->entry.mode)){
+ ret = -EISDIR;
+ }else{
+ ret = -ENOTDIR;
+ }
+ }else{
+ ret = umsdos_writeentry(dir,emd_dir,info,1);
+ }
+ }
+ }
+ iput(emd_dir);
+ return ret;
+}
+
+
+/*
+ Verify is a EMD directory is empty.
+ Return 0 if not empty
+ 1 if empty
+ 2 if empty, no EMD file.
+*/
+int umsdos_isempty (struct inode *dir)
+{
+ int ret = 2;
+ struct inode *emd_dir = umsdos_emd_dir_lookup(dir,0);
+ /* If the EMD file does not exist, it is certainly empty :-) */
+ if (emd_dir != NULL){
+ struct file filp;
+ /* Find an empty slot */
+ filp.f_pos = 0;
+ filp.f_reada = 1;
+ filp.f_flags = O_RDONLY;
+ ret = 1;
+ while (filp.f_pos < emd_dir->i_size){
+ struct umsdos_dirent entry;
+ if (umsdos_emd_dir_readentry(emd_dir,&filp,&entry)!=0){
+ ret = 0;
+ break;
+ }else if (entry.name_len != 0){
+ ret = 0;
+ break;
+ }
+ }
+ iput (emd_dir);
+ }
+ return ret;
+}
+
+/*
+ Locate an entry in a EMD directory.
+ Return 0 if ok, errcod if not, generally -ENOENT.
+*/
+int umsdos_findentry (
+ struct inode *dir,
+ struct umsdos_info *info,
+ int expect) /* 0: anything */
+ /* 1: file */
+ /* 2: directory */
+{
+ struct inode *emd_dir;
+ int ret = umsdos_find (dir,info,&emd_dir);
+ if (ret == 0){
+ if (expect != 0){
+ if (S_ISDIR(info->entry.mode)){
+ if (expect != 2) ret = -EISDIR;
+ }else if (expect == 2){
+ ret = -ENOTDIR;
+ }
+ }
+ }
+ iput (emd_dir);
+ return ret;
+}
+
diff --git a/fs/umsdos/file.c b/fs/umsdos/file.c
new file mode 100644
index 000000000..d292ea3c2
--- /dev/null
+++ b/fs/umsdos/file.c
@@ -0,0 +1,103 @@
+/*
+ * linux/fs/umsdos/file.c
+ *
+ * Written 1993 by Jacques Gelinas
+ * inspired from linux/fs/msdos/file.c Werner Almesberger
+ *
+ * Extended MS-DOS regular file handling primitives
+ */
+
+#include <asm/segment.h>
+#include <asm/system.h>
+
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/msdos_fs.h>
+#include <linux/errno.h>
+#include <linux/fcntl.h>
+#include <linux/stat.h>
+#include <linux/msdos_fs.h>
+#include <linux/umsdos_fs.h>
+
+
+#define PRINTK(x)
+#define Printk(x) printk x
+/*
+ Read a file into user space memory
+*/
+static int UMSDOS_file_read(
+ struct inode *inode,
+ struct file *filp,
+ char *buf,
+ int count)
+{
+ /* We have to set the access time because msdos don't care */
+ int ret = msdos_file_read(inode,filp,buf,count);
+ inode->i_atime = CURRENT_TIME;
+ inode->i_dirt = 1;
+ return ret;
+}
+/*
+ Write a file from user space memory
+*/
+static int UMSDOS_file_write(
+ struct inode *inode,
+ struct file *filp,
+ char *buf,
+ int count)
+{
+ return msdos_file_write(inode,filp,buf,count);
+}
+/*
+ Truncate a file to 0 length.
+*/
+static void UMSDOS_truncate(struct inode *inode)
+{
+ PRINTK (("UMSDOS_truncate\n"));
+ msdos_truncate (inode);
+ inode->i_ctime = inode->i_mtime = CURRENT_TIME;
+ inode->i_dirt = 1;
+}
+/*
+ See inode.c
+
+ Some entry point are filled dynamically with function pointers
+ from the msdos file_operations and file_inode_operations.
+
+ The idea is to have the code as independent as possible from
+ the msdos file system.
+*/
+
+struct file_operations umsdos_file_operations = {
+ NULL, /* lseek - default */
+ UMSDOS_file_read, /* read */
+ UMSDOS_file_write, /* write */
+ NULL, /* readdir - bad */
+ NULL, /* select - default */
+ NULL, /* ioctl - default */
+ generic_mmap, /* mmap */
+ NULL, /* no special open is needed */
+ NULL, /* release */
+ file_fsync /* fsync */
+};
+
+struct inode_operations umsdos_file_inode_operations = {
+ &umsdos_file_operations, /* default file operations */
+ NULL, /* create */
+ NULL, /* lookup */
+ NULL, /* link */
+ NULL, /* unlink */
+ NULL, /* symlink */
+ NULL, /* mkdir */
+ NULL, /* rmdir */
+ NULL, /* mknod */
+ NULL, /* rename */
+ NULL, /* readlink */
+ NULL, /* follow_link */
+ NULL, /* bmap */
+ UMSDOS_truncate,/* truncate */
+ NULL, /* permission */
+ msdos_smap /* smap */
+};
+
+
diff --git a/fs/umsdos/inode.c b/fs/umsdos/inode.c
new file mode 100644
index 000000000..40f7feb68
--- /dev/null
+++ b/fs/umsdos/inode.c
@@ -0,0 +1,513 @@
+/*
+ * linux/fs/umsdos/inode.c
+ *
+ * Written 1993 by Jacques Gelinas
+ * Inspired from linux/fs/msdos/... by Werner Almesberger
+ *
+ */
+
+#include <linux/fs.h>
+#include <linux/msdos_fs.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <asm/segment.h>
+#include <linux/string.h>
+#include <linux/ctype.h>
+#include <linux/stat.h>
+#include <linux/umsdos_fs.h>
+
+#ifdef MODULE
+ #include <linux/module.h>
+ #include "../../tools/version.h"
+#endif
+
+struct inode *pseudo_root=NULL; /* Useful to simulate the pseudo DOS */
+ /* directory. See UMSDOS_readdir_x() */
+
+/* #Specification: convention / PRINTK Printk and printk
+ Here is the convention for the use of printk inside fs/umsdos
+
+ printk carry important message (error or status).
+ Printk is for debugging (it is a macro defined at the beginning of
+ most source.
+ PRINTK is a nulled Printk macro.
+
+ This convention makes the source easier to read, and Printk easier
+ to shut off.
+*/
+#define PRINTK(x)
+#define Printk(x) printk x
+
+
+void UMSDOS_put_inode(struct inode *inode)
+{
+ PRINTK (("put inode %x owner %x pos %d dir %x\n",inode
+ ,inode->u.umsdos_i.i_emd_owner,inode->u.umsdos_i.pos
+ ,inode->u.umsdos_i.i_emd_dir));
+ msdos_put_inode(inode);
+}
+
+
+void UMSDOS_put_super(struct super_block *sb)
+{
+ msdos_put_super(sb);
+ #ifdef MODULE
+ MOD_DEC_USE_COUNT;
+ #endif
+}
+
+
+void UMSDOS_statfs(struct super_block *sb,struct statfs *buf)
+{
+ msdos_statfs(sb,buf);
+}
+
+
+/*
+ Call msdos_lookup, but set back the original msdos function table.
+ Return 0 if ok, or a negative error code if not.
+*/
+int umsdos_real_lookup (
+ struct inode *dir,
+ const char *name,
+ int len,
+ struct inode **result) /* Will hold inode of the file, if successful */
+{
+ int ret;
+ dir->i_count++;
+ ret = msdos_lookup (dir,name,len,result);
+ return ret;
+}
+/*
+ Complete the setup of an directory inode.
+ First, it completes the function pointers, then
+ it locates the EMD file. If the EMD is there, then plug the
+ umsdos function table. If not, use the msdos one.
+*/
+void umsdos_setup_dir_inode (struct inode *inode)
+{
+ inode->u.umsdos_i.i_emd_dir = 0;
+ {
+ struct inode *emd_dir = umsdos_emd_dir_lookup (inode,0);
+ extern struct inode_operations umsdos_rdir_inode_operations;
+ inode->i_op = emd_dir != NULL
+ ? &umsdos_dir_inode_operations
+ : &umsdos_rdir_inode_operations;
+ iput (emd_dir);
+ }
+}
+/*
+ Add some info into an inode so it can find its owner quickly
+*/
+void umsdos_set_dirinfo(
+ struct inode *inode,
+ struct inode *dir,
+ off_t f_pos)
+{
+ struct inode *emd_owner = umsdos_emd_dir_lookup(dir,1);
+ inode->u.umsdos_i.i_dir_owner = dir->i_ino;
+ inode->u.umsdos_i.i_emd_owner = emd_owner->i_ino;
+ iput (emd_owner);
+ inode->u.umsdos_i.pos = f_pos;
+}
+/*
+ Tells if an Umsdos inode has been "patched" once.
+ Return != 0 if so.
+*/
+int umsdos_isinit (struct inode *inode)
+{
+#if 1
+ return inode->u.umsdos_i.i_emd_owner != 0;
+#elif 0
+ return inode->i_atime != 0;
+#else
+ return inode->i_count > 1;
+#endif
+}
+/*
+ Connect the proper tables in the inode and add some info.
+*/
+void umsdos_patch_inode (
+ struct inode *inode,
+ struct inode *dir, /* May be NULL */
+ off_t f_pos)
+{
+ /*
+ This function is called very early to setup the inode, somewhat
+ too early (called by UMSDOS_read_inode). At this point, we can't
+ do to much, such as lookup up EMD files and so on. This causes
+ confusion in the kernel. This is why some initialisation
+ will be done when dir != NULL only.
+
+ UMSDOS do run piggy back on top of msdos fs. It looks like something
+ is missing in the VFS to accommodate stacked fs. Still unclear what
+ (quite honestly).
+
+ Well, maybe one! A new entry "may_unmount" which would allow
+ the stacked fs to allocate some inode permanently and release
+ them at the end. Doing that now introduce a problem. unmount
+ always fail because some inodes are in use.
+ */
+ if (!umsdos_isinit(inode)){
+ inode->u.umsdos_i.i_emd_dir = 0;
+ if (S_ISREG(inode->i_mode)){
+ static char is_init = 0;
+ if (!is_init){
+ /*
+ I don't want to change the msdos file system code
+ so I get the address of some subroutine dynamically
+ once.
+ */
+ umsdos_file_inode_operations.bmap = inode->i_op->bmap;
+ inode->i_op = &umsdos_file_inode_operations;
+ is_init = 1;
+ }
+ inode->i_op = &umsdos_file_inode_operations;
+ }else if (S_ISDIR(inode->i_mode)){
+ if (dir != NULL){
+ umsdos_setup_dir_inode(inode);
+ }
+ }else if (S_ISLNK(inode->i_mode)){
+ inode->i_op = &umsdos_symlink_inode_operations;
+ }else if (S_ISCHR(inode->i_mode)){
+ inode->i_op = &chrdev_inode_operations;
+ }else if (S_ISBLK(inode->i_mode)){
+ inode->i_op = &blkdev_inode_operations;
+ }else if (S_ISFIFO(inode->i_mode)){
+ init_fifo(inode);
+ }
+ if (dir != NULL){
+ /* #Specification: inode / umsdos info
+ The first time an inode is seen (inode->i_count == 1),
+ the inode number of the EMD file which control this inode
+ is tagged to this inode. It allows operation such
+ as notify_change to be handled.
+ */
+ /*
+ This is done last because it also control the
+ status of umsdos_isinit()
+ */
+ umsdos_set_dirinfo (inode,dir,f_pos);
+ }
+ }else if (dir != NULL){
+ /*
+ Test to see if the info is maintained.
+ This should be removed when the file system will be proven.
+ */
+ struct inode *emd_owner = umsdos_emd_dir_lookup(dir,1);
+ iput (emd_owner);
+ if (emd_owner->i_ino != inode->u.umsdos_i.i_emd_owner){
+ printk ("UMSDOS: *** EMD_OWNER ??? *** ino = %ld %ld <> %ld "
+ ,inode->i_ino,emd_owner->i_ino,inode->u.umsdos_i.i_emd_owner);
+ }
+ }
+}
+/*
+ Get the inode of the directory which owns this inode.
+ Return 0 if ok, -EIO if error.
+*/
+int umsdos_get_dirowner(
+ struct inode *inode,
+ struct inode **result) /* Hold NULL if any error */
+ /* else, the inode of the directory */
+{
+ int ret = -EIO;
+ unsigned long ino = inode->u.umsdos_i.i_dir_owner;
+ *result = NULL;
+ if (ino == 0){
+ printk ("UMSDOS: umsdos_get_dirowner ino == 0\n");
+ }else{
+ struct inode *dir = *result = iget(inode->i_sb,ino);
+ if (dir != NULL){
+ umsdos_patch_inode (dir,NULL,0);
+ ret = 0;
+ }
+ }
+ return ret;
+}
+/*
+ Load an inode from disk.
+*/
+void UMSDOS_read_inode(struct inode *inode)
+{
+ PRINTK (("read inode %x ino = %d ",inode,inode->i_ino));
+ msdos_read_inode(inode);
+ PRINTK (("ino = %d %d\n",inode->i_ino,inode->i_count));
+ if (S_ISDIR(inode->i_mode)
+ && (inode->u.umsdos_i.u.dir_info.creating != 0
+ || inode->u.umsdos_i.u.dir_info.looking != 0
+ || inode->u.umsdos_i.u.dir_info.p != NULL)){
+ Printk (("read inode %d %d %p\n"
+ ,inode->u.umsdos_i.u.dir_info.creating
+ ,inode->u.umsdos_i.u.dir_info.looking
+ ,inode->u.umsdos_i.u.dir_info.p));
+ }
+ /* #Specification: Inode / post initialisation
+ To completely initialise an inode, we need access to the owner
+ directory, so we can locate more info in the EMD file. This is
+ not available the first time the inode is access, we use
+ a value in the inode to tell if it has been finally initialised.
+
+ At first, we have tried testing i_count but it was causing
+ problem. It is possible that two or more process use the
+ newly accessed inode. While the first one block during
+ the initialisation (probably while reading the EMD file), the
+ others believe all is well because i_count > 1. They go banana
+ with a broken inode. See umsdos_lookup_patch and umsdos_patch_inode.
+ */
+ umsdos_patch_inode(inode,NULL,0);
+}
+
+/*
+ Update the disk with the inode content
+*/
+void UMSDOS_write_inode(struct inode *inode)
+{
+ struct iattr newattrs;
+
+ PRINTK (("UMSDOS_write_inode emd %d\n",inode->u.umsdos_i.i_emd_owner));
+ msdos_write_inode(inode);
+ newattrs.ia_mtime = inode->i_mtime;
+ newattrs.ia_atime = inode->i_atime;
+ newattrs.ia_ctime = inode->i_ctime;
+ newattrs.ia_valid = ATTR_MTIME | ATTR_ATIME | ATTR_CTIME;
+ /*
+ UMSDOS_notify_change is convenient to call here
+ to update the EMD entry associated with this inode.
+ But it has the side effect to re"dirt" the inode.
+ */
+ UMSDOS_notify_change (inode, &newattrs);
+ inode->i_dirt = 0;
+}
+
+int UMSDOS_notify_change(struct inode *inode, struct iattr *attr)
+{
+ int ret = 0;
+
+ if ((ret = inode_change_ok(inode, attr)) != 0)
+ return ret;
+
+ if (inode->i_nlink > 0){
+ /* #Specification: notify_change / i_nlink > 0
+ notify change is only done for inode with nlink > 0. An inode
+ with nlink == 0 is no longer associated with any entry in
+ the EMD file, so there is nothing to update.
+ */
+ unsigned long i_emd_owner = inode->u.umsdos_i.i_emd_owner;
+ if (inode == inode->i_sb->s_mounted){
+ /* #Specification: root inode / attributes
+ I don't know yet how this should work. Normally
+ the attributes (permissions bits, owner, times) of
+ a directory are stored in the EMD file of its parent.
+
+ One thing we could do is store the attributes of the root
+ inode in its own EMD file. A simple entry named "." could
+ be used for this special case. It would be read once
+ when the file system is mounted and update in
+ UMSDOS_notify_change() (right here).
+
+ I am not sure of the behavior of the root inode for
+ a real UNIX file system. For now, this is a nop.
+ */
+ }else if (i_emd_owner != 0xffffffff && i_emd_owner != 0){
+ /* This inode is not a EMD file nor an inode used internally
+ by MSDOS, so we can update its status.
+ See emd.c
+ */
+ struct inode *emd_owner = iget (inode->i_sb,i_emd_owner);
+ PRINTK (("notify change %p ",inode));
+ if (emd_owner == NULL){
+ printk ("UMSDOS: emd_owner = NULL ???");
+ ret = -EPERM;
+ }else{
+ struct file filp;
+ struct umsdos_dirent entry;
+ filp.f_pos = inode->u.umsdos_i.pos;
+ filp.f_reada = 0;
+ PRINTK (("pos = %d ",filp.f_pos));
+ /* Read only the start of the entry since we don't touch */
+ /* the name */
+ ret = umsdos_emd_dir_read (emd_owner,&filp,(char*)&entry
+ ,UMSDOS_REC_SIZE);
+ if (ret == 0){
+ if (attr->ia_valid & ATTR_UID)
+ entry.uid = attr->ia_uid;
+ if (attr->ia_valid & ATTR_GID)
+ entry.gid = attr->ia_gid;
+ if (attr->ia_valid & ATTR_MODE)
+ entry.mode = attr->ia_mode;
+ if (attr->ia_valid & ATTR_ATIME)
+ entry.atime = attr->ia_atime;
+ if (attr->ia_valid & ATTR_MTIME)
+ entry.mtime = attr->ia_mtime;
+ if (attr->ia_valid & ATTR_CTIME)
+ entry.ctime = attr->ia_ctime;
+
+ entry.nlink = inode->i_nlink;
+ filp.f_pos = inode->u.umsdos_i.pos;
+ ret = umsdos_emd_dir_write (emd_owner,&filp,(char*)&entry
+ ,UMSDOS_REC_SIZE);
+
+ PRINTK (("notify pos %d ret %d nlink %d "
+ ,inode->u.umsdos_i.pos
+ ,ret,entry.nlink));
+ /* #Specification: notify_change / msdos fs
+ notify_change operation are done only on the
+ EMD file. The msdos fs is not even called.
+ */
+ }
+ iput (emd_owner);
+ }
+ PRINTK (("\n"));
+ }
+ }
+ if (ret == 0)
+ inode_setattr(inode, attr);
+ return ret;
+}
+
+/* #Specification: function name / convention
+ A simple convention for function name has been used in
+ the UMSDOS file system. First all function use the prefix
+ umsdos_ to avoid name clash with other part of the kernel.
+
+ And standard VFS entry point use the prefix UMSDOS (upper case)
+ so it's easier to tell them apart.
+*/
+
+static struct super_operations umsdos_sops = {
+ UMSDOS_read_inode,
+ UMSDOS_notify_change,
+ UMSDOS_write_inode,
+ UMSDOS_put_inode,
+ UMSDOS_put_super,
+ NULL, /* added in 0.96c */
+ UMSDOS_statfs,
+ NULL
+};
+
+/*
+ Read the super block of an Extended MS-DOS FS.
+*/
+struct super_block *UMSDOS_read_super(
+ struct super_block *s,
+ void *data,
+ int silent)
+{
+ /* #Specification: mount / options
+ Umsdos run on top of msdos. Currently, it supports no
+ mount option, but happily pass all option received to
+ the msdos driver. I am not sure if all msdos mount option
+ make sense with Umsdos. Here are at least those who
+ are useful.
+ uid=
+ gid=
+
+ These options affect the operation of umsdos in directories
+ which do not have an EMD file. They behave like normal
+ msdos directory, with all limitation of msdos.
+ */
+ struct super_block *sb = msdos_read_super(s,data,silent);
+ printk ("UMSDOS Alpha 0.5a (compatibility level %d.%d, fast msdos)\n"
+ ,UMSDOS_VERSION,UMSDOS_RELEASE);
+ if (sb != NULL){
+ sb->s_op = &umsdos_sops;
+ PRINTK (("umsdos_read_super %p\n",sb->s_mounted));
+ umsdos_setup_dir_inode (sb->s_mounted);
+ PRINTK (("End umsdos_read_super\n"));
+ if (s == super_blocks){
+ /* #Specification: pseudo root / mount
+ When a umsdos fs is mounted, a special handling is done
+ if it is the root partition. We check for the presence
+ of the file /linux/etc/init or /linux/etc/rc.
+ If one is there, we do a chroot("/linux").
+
+ We check both because (see init/main.c) the kernel
+ try to exec init at different place and if it fails
+ it tries /bin/sh /etc/rc. To be consistent with
+ init/main.c, many more test would have to be done
+ to locate init. Any complain ?
+
+ The chroot is done manually in init/main.c but the
+ info (the inode) is located at mount time and store
+ in a global variable (pseudo_root) which is used at
+ different place in the umsdos driver. There is no
+ need to store this variable elsewhere because it
+ will always be one, not one per mount.
+
+ This feature allows the installation
+ of a linux system within a DOS system in a subdirectory.
+
+ A user may install its linux stuff in c:\linux
+ avoiding any clash with existing DOS file and subdirectory.
+ When linux boots, it hides this fact, showing a normal
+ root directory with /etc /bin /tmp ...
+
+ The word "linux" is hardcoded in /usr/include/linux/umsdos_fs.h
+ in the macro UMSDOS_PSDROOT_NAME.
+ */
+
+ struct inode *pseudo;
+ Printk (("Mounting root\n"));
+ if (umsdos_real_lookup (sb->s_mounted,UMSDOS_PSDROOT_NAME
+ ,UMSDOS_PSDROOT_LEN,&pseudo)==0
+ && S_ISDIR(pseudo->i_mode)){
+ struct inode *etc = NULL;
+ struct inode *rc = NULL;
+ Printk (("/%s is there\n",UMSDOS_PSDROOT_NAME));
+ if (umsdos_real_lookup (pseudo,"etc",3,&etc)==0
+ && S_ISDIR(etc->i_mode)){
+ struct inode *init;
+ Printk (("/%s/etc is there\n",UMSDOS_PSDROOT_NAME));
+ if ((umsdos_real_lookup (etc,"init",4,&init)==0
+ && S_ISREG(init->i_mode))
+ || (umsdos_real_lookup (etc,"rc",2,&rc)==0
+ && S_ISREG(rc->i_mode))){
+ umsdos_setup_dir_inode (pseudo);
+ Printk (("Activating pseudo root /%s\n",UMSDOS_PSDROOT_NAME));
+ pseudo_root = pseudo;
+ pseudo->i_count++;
+ pseudo = NULL;
+ }
+ iput (init);
+ iput (rc);
+ }
+ iput (etc);
+ }
+ iput (pseudo);
+ }
+ #ifdef MODULE
+ MOD_INC_USE_COUNT;
+ #endif
+ }
+ return sb;
+}
+
+
+#ifdef MODULE
+
+char kernel_version[] = UTS_RELEASE;
+
+static struct file_system_type umsdos_fs_type = {
+ UMSDOS_read_super, "umsdos", 1, NULL
+};
+
+int init_module(void)
+{
+ register_filesystem(&umsdos_fs_type);
+ return 0;
+}
+
+void cleanup_module(void)
+{
+ if (MOD_IN_USE)
+ printk("Umsdos: file system in use, remove delayed\n");
+ else
+ {
+ unregister_filesystem(&umsdos_fs_type);
+ }
+}
+
+#endif
+
diff --git a/fs/umsdos/ioctl.c b/fs/umsdos/ioctl.c
new file mode 100644
index 000000000..972571796
--- /dev/null
+++ b/fs/umsdos/ioctl.c
@@ -0,0 +1,259 @@
+/*
+ * linux/fs/umsdos/ioctl.c
+ *
+ * Written 1993 by Jacques Gelinas
+ *
+ * Extended MS-DOS ioctl directory handling functions
+ */
+#include <asm/segment.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/msdos_fs.h>
+#include <linux/umsdos_fs.h>
+
+#define PRINTK(x)
+#define Printk(x) printk x
+
+/*
+ Perform special function on a directory
+*/
+int UMSDOS_ioctl_dir (
+ struct inode *dir,
+ struct file *filp,
+ unsigned int cmd,
+ unsigned long data)
+{
+ int ret = -EPERM;
+ /* #Specification: ioctl / acces
+ Only root (effective id) is allowed to do IOCTL on directory
+ in UMSDOS. EPERM is returned for other user.
+ */
+ if (current->euid == 0
+ || cmd == UMSDOS_GETVERSION){
+ struct umsdos_ioctl *idata = (struct umsdos_ioctl *)data;
+ ret = -EINVAL;
+ /* #Specification: ioctl / prototypes
+ The official prototype for the umsdos ioctl on directory
+ is:
+
+ int ioctl (
+ int fd, // File handle of the directory
+ int cmd, // command
+ struct umsdos_ioctl *data)
+
+ The struct and the commands are defined in linux/umsdos_fs.h.
+
+ umsdos_progs/umsdosio.c provide an interface in C++ to all
+ these ioctl. umsdos_progs/udosctl is a small utility showing
+ all this.
+
+ These ioctl generally allow one to work on the EMD or the
+ DOS directory independently. These are essential to implement
+ the synchronise.
+ */
+ PRINTK (("ioctl %d ",cmd));
+ if (cmd == UMSDOS_GETVERSION){
+ /* #Specification: ioctl / UMSDOS_GETVERSION
+ The field version and release of the structure
+ umsdos_ioctl are filled with the version and release
+ number of the fs code in the kernel. This will allow
+ some form of checking. Users won't be able to run
+ incompatible utility such as the synchroniser (umssync).
+ umsdos_progs/umsdosio.c enforce this checking.
+
+ Return always 0.
+ */
+ put_fs_byte (UMSDOS_VERSION,&idata->version);
+ put_fs_byte (UMSDOS_RELEASE,&idata->release);
+ ret = 0;
+ }else if (cmd == UMSDOS_READDIR_DOS){
+ /* #Specification: ioctl / UMSDOS_READDIR_DOS
+ One entry is read from the DOS directory at the current
+ file position. The entry is put as is in the dos_dirent
+ field of struct umsdos_ioctl.
+
+ Return > 0 if success.
+ */
+ ret = msdos_readdir(dir,filp,&idata->dos_dirent,1);
+ }else if (cmd == UMSDOS_READDIR_EMD){
+ /* #Specification: ioctl / UMSDOS_READDIR_EMD
+ One entry is read from the EMD at the current
+ file position. The entry is put as is in the umsdos_dirent
+ field of struct umsdos_ioctl. The corresponding mangled
+ DOS entry name is put in the dos_dirent field.
+
+ All entries are read including hidden links. Blank
+ entries are skipped.
+
+ Return > 0 if success.
+ */
+ struct inode *emd_dir = umsdos_emd_dir_lookup (dir,0);
+ if (emd_dir != NULL){
+ while (1){
+ if (filp->f_pos >= emd_dir->i_size){
+ ret = 0;
+ break;
+ }else{
+ struct umsdos_dirent entry;
+ off_t f_pos = filp->f_pos;
+ ret = umsdos_emd_dir_readentry (emd_dir,filp,&entry);
+ if (ret < 0){
+ break;
+ }else if (entry.name_len > 0){
+ struct umsdos_info info;
+ ret = entry.name_len;
+ umsdos_parse (entry.name,entry.name_len,&info);
+ info.f_pos = f_pos;
+ umsdos_manglename(&info);
+ memcpy_tofs(&idata->umsdos_dirent,&entry
+ ,sizeof(entry));
+ memcpy_tofs(&idata->dos_dirent.d_name
+ ,info.fake.fname,info.fake.len+1);
+ break;
+ }
+ }
+ }
+ iput (emd_dir);
+ }else{
+ /* The absence of the EMD is simply seen as an EOF */
+ ret = 0;
+ }
+ }else if (cmd == UMSDOS_INIT_EMD){
+ /* #Specification: ioctl / UMSDOS_INIT_EMD
+ The UMSDOS_INIT_EMD command make sure the EMD
+ exist for a directory. If it does not, it is
+ created. Also, it makes sure the directory functions
+ table (struct inode_operations) is set to the UMSDOS
+ semantic. This mean that umssync may be applied to
+ an "opened" msdos directory, and it will change behavior
+ on the fly.
+
+ Return 0 if success.
+ */
+ extern struct inode_operations umsdos_rdir_inode_operations;
+ struct inode *emd_dir = umsdos_emd_dir_lookup (dir,1);
+ ret = emd_dir != NULL;
+ iput (emd_dir);
+
+ dir->i_op = ret
+ ? &umsdos_dir_inode_operations
+ : &umsdos_rdir_inode_operations;
+ }else{
+ struct umsdos_ioctl data;
+ memcpy_fromfs (&data,idata,sizeof(data));
+ if (cmd == UMSDOS_CREAT_EMD){
+ /* #Specification: ioctl / UMSDOS_CREAT_EMD
+ The umsdos_dirent field of the struct umsdos_ioctl is used
+ as is to create a new entry in the EMD of the directory.
+ The DOS directory is not modified.
+ No validation is done (yet).
+
+ Return 0 if success.
+ */
+ struct umsdos_info info;
+ /* This makes sure info.entry and info in general is correctly */
+ /* initialised */
+ memcpy (&info.entry,&data.umsdos_dirent
+ ,sizeof(data.umsdos_dirent));
+ umsdos_parse (data.umsdos_dirent.name
+ ,data.umsdos_dirent.name_len,&info);
+ ret = umsdos_newentry (dir,&info);
+ }else if (cmd == UMSDOS_UNLINK_EMD){
+ /* #Specification: ioctl / UMSDOS_UNLINK_EMD
+ The umsdos_dirent field of the struct umsdos_ioctl is used
+ as is to remove an entry from the EMD of the directory.
+ No validation is done (yet). The mode field is used
+ to validate S_ISDIR or S_ISREG.
+
+ Return 0 if success.
+ */
+ struct umsdos_info info;
+ /* This makes sure info.entry and info in general is correctly */
+ /* initialised */
+ memcpy (&info.entry,&data.umsdos_dirent
+ ,sizeof(data.umsdos_dirent));
+ umsdos_parse (data.umsdos_dirent.name
+ ,data.umsdos_dirent.name_len,&info);
+ ret = umsdos_delentry (dir,&info
+ ,S_ISDIR(data.umsdos_dirent.mode));
+ }else if (cmd == UMSDOS_UNLINK_DOS){
+ /* #Specification: ioctl / UMSDOS_UNLINK_DOS
+ The dos_dirent field of the struct umsdos_ioctl is used to
+ execute a msdos_unlink operation. The d_name and d_reclen
+ fields are used.
+
+ Return 0 if success.
+ */
+ dir->i_count++;
+ ret = msdos_unlink (dir,data.dos_dirent.d_name
+ ,data.dos_dirent.d_reclen);
+ }else if (cmd == UMSDOS_RMDIR_DOS){
+ /* #Specification: ioctl / UMSDOS_RMDIR_DOS
+ The dos_dirent field of the struct umsdos_ioctl is used to
+ execute a msdos_unlink operation. The d_name and d_reclen
+ fields are used.
+
+ Return 0 if success.
+ */
+ dir->i_count++;
+ ret = msdos_rmdir (dir,data.dos_dirent.d_name
+ ,data.dos_dirent.d_reclen);
+ }else if (cmd == UMSDOS_STAT_DOS){
+ /* #Specification: ioctl / UMSDOS_STAT_DOS
+ The dos_dirent field of the struct umsdos_ioctl is
+ used to execute a stat operation in the DOS directory.
+ The d_name and d_reclen fields are used.
+
+ The following field of umsdos_ioctl.stat are filled.
+
+ st_ino,st_mode,st_size,st_atime,st_mtime,st_ctime,
+ Return 0 if success.
+ */
+ struct inode *inode;
+ ret = umsdos_real_lookup (dir,data.dos_dirent.d_name
+ ,data.dos_dirent.d_reclen,&inode);
+ if (ret == 0){
+ data.stat.st_ino = inode->i_ino;
+ data.stat.st_mode = inode->i_mode;
+ data.stat.st_size = inode->i_size;
+ data.stat.st_atime = inode->i_atime;
+ data.stat.st_ctime = inode->i_ctime;
+ data.stat.st_mtime = inode->i_mtime;
+ memcpy_tofs (&idata->stat,&data.stat,sizeof(data.stat));
+ iput (inode);
+ }
+ }else if (cmd == UMSDOS_DOS_SETUP){
+ /* #Specification: ioctl / UMSDOS_DOS_SETUP
+ The UMSDOS_DOS_SETUP ioctl allow changing the
+ default permission of the MsDOS file system driver
+ on the fly. The MsDOS driver apply global permission
+ to every file and directory. Normally these permissions
+ are controlled by a mount option. This is not
+ available for root partition, so a special utility
+ (umssetup) is provided to do this, normally in
+ /etc/rc.local.
+
+ Be aware that this apply ONLY to MsDOS directory
+ (those without EMD --linux-.---). Umsdos directory
+ have independent (standard) permission for each
+ and every file.
+
+ The field umsdos_dirent provide the information needed.
+ umsdos_dirent.uid and gid sets the owner and group.
+ umsdos_dirent.mode set the permissions flags.
+ */
+ dir->i_sb->u.msdos_sb.fs_uid = data.umsdos_dirent.uid;
+ dir->i_sb->u.msdos_sb.fs_gid = data.umsdos_dirent.gid;
+ dir->i_sb->u.msdos_sb.fs_umask = data.umsdos_dirent.mode;
+ ret = 0;
+ }
+ }
+ }
+ PRINTK (("ioctl return %d\n",ret));
+ return ret;
+}
+
+
+
diff --git a/fs/umsdos/mangle.c b/fs/umsdos/mangle.c
new file mode 100644
index 000000000..1f59447e9
--- /dev/null
+++ b/fs/umsdos/mangle.c
@@ -0,0 +1,478 @@
+/*
+ * linux/fs/umsdos/mangle.c
+ *
+ * Written 1993 by Jacques Gelinas
+ *
+ * Control the mangling of file name to fit msdos name space.
+ * Many optimisation by GLU == dglaude@is1.vub.ac.be (GLAUDE DAVID)
+*/
+#include <linux/errno.h>
+#include <linux/ctype.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/umsdos_fs.h>
+
+/*
+ Complete the mangling of the MSDOS fake name
+ based on the position of the entry in the EMD file.
+
+ Simply complete the job of umsdos_parse; fill the extension.
+
+ Beware that info->f_pos must be set.
+*/
+void umsdos_manglename (struct umsdos_info *info)
+{
+ if (info->msdos_reject){
+ /* #Specification: file name / non MSDOS conforming / mangling
+ Each non MSDOS conforming file has a special extension
+ build from the entry position in the EMD file.
+
+ This number is then transform in a base 32 number, where
+ each digit is expressed like hexadecimal number, using
+ digit and letter, except it uses 22 letters from 'a' to 'v'.
+ The number 32 comes from 2**5. It is faster to split a binary
+ number using a base which is a power of two. And I was 32
+ when I started this project. Pick your answer :-) .
+
+ If the result is '0', it is replace with '_', simply
+ to make it odd.
+
+ This is true for the first two character of the extension.
+ The last one is taken from a list of odd character, which
+ are:
+
+ { } ( ) ! ` ^ & @
+
+ With this scheme, we can produce 9216 ( 9* 32 * 32)
+ different extensions which should not clash with any useful
+ extension already popular or meaningful. Since most directory
+ have much less than 32 * 32 files in it, the first character
+ of the extension of any mangle name will be {.
+
+ Here are the reason to do this (this kind of mangling).
+
+ -The mangling is deterministic. Just by the extension, we
+ are able to locate the entry in the EMD file.
+
+ -By keeping to beginning of the file name almost unchanged,
+ we are helping the MSDOS user.
+
+ -The mangling produces names not too ugly, so an msdos user
+ may live with it (remember it, type it, etc...).
+
+ -The mangling produces names ugly enough so no one will
+ ever think of using such a name in real life. This is not
+ fool proof. I don't think there is a total solution to this.
+ */
+ union {
+ int entry_num;
+ struct {
+ unsigned num1:5,num2:5,num3:5;
+ }num;
+ } u;
+ char *pt = info->fake.fname + info->fake.len;
+ /* lookup for encoding the last character of the extension */
+ /* It contain valid character after the ugly one to make sure */
+ /* even if someone overflow the 32 * 32 * 9 limit, it still do */
+ /* something */
+ #define SPECIAL_MANGLING '{','}','(',')','!','`','^','&','@'
+ static char lookup3[]={
+ SPECIAL_MANGLING,
+ /* This is the start of lookup12 */
+ '_','1','2','3','4','5','6','7','8','9',
+ 'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o',
+ 'p','q','r','s','t','u','v'
+ };
+ #define lookup12 (lookup3+9)
+ u.entry_num = info->f_pos / UMSDOS_REC_SIZE;
+ if (u.entry_num > (9* 32 * 32)){
+ printk ("UMSDOS: More than 9216 file in a directory.\n"
+ "This may break the mangling strategy.\n"
+ "Not a killer problem. See doc.\n");
+ }
+ *pt++ = '.';
+ *pt++ = lookup3 [u.num.num3];
+ *pt++ = lookup12[u.num.num2];
+ *pt++ = lookup12[u.num.num1];
+ *pt = '\0'; /* help doing printk */
+ info->fake.len += 4;
+ info->msdos_reject = 0; /* Avoid mangling twice */
+ }
+}
+
+/*
+ Evaluate the record size needed to store of name of len character.
+ The value returned is a multiple of UMSDOS_REC_SIZE.
+*/
+int umsdos_evalrecsize (int len)
+{
+ struct umsdos_dirent dirent;
+ int nbrec = 1+((len-1+(dirent.name-(char*)&dirent))
+ / UMSDOS_REC_SIZE);
+ return nbrec * UMSDOS_REC_SIZE;
+ /*
+ GLU This should be inlined or something to speed it up to the max.
+ GLU nbrec is absolutely not needed to return the value.
+ */
+}
+#ifdef TEST
+int umsdos_evalrecsize_old (int len)
+{
+ struct umsdos_dirent dirent;
+ int size = len + (dirent.name-(char*)&dirent);
+ int nbrec = size / UMSDOS_REC_SIZE;
+ int extra = size % UMSDOS_REC_SIZE;
+ if (extra > 0) nbrec++;
+ return nbrec * UMSDOS_REC_SIZE;
+}
+#endif
+/*
+ Fill the struct info with the full and msdos name of a file
+ Return 0 if all is ok, a negative error code otherwise.
+*/
+int umsdos_parse (
+ const char *fname,
+ int len,
+ struct umsdos_info *info)
+{
+ int ret = -ENAMETOOLONG;
+ /* #Specification: file name / too long
+ If a file name exceed UMSDOS maxima, the file name is silently
+ truncated. This makes it conformant with the other file system
+ of Linux (minix and ext2 at least).
+ */
+ if (len > UMSDOS_MAXNAME) len = UMSDOS_MAXNAME;
+ {
+ const char *firstpt=NULL; /* First place we saw a . in fname */
+ /* #Specification: file name / non MSDOS conforming / base length 0
+ file name beginning with a period '.' are invalid for MsDOS.
+ It needs absolutely a base name. So the file name is mangled
+ */
+ int ivldchar = fname[0] == '.';/* At least one invalid character */
+ int msdos_len = len;
+ int base_len;
+ /*
+ cardinal_per_size tells if there exist at least one
+ DOS pseudo devices on length n. See the test below.
+ */
+ static const char cardinal_per_size[9]={
+ 0, 0, 0, 1, 1, 0, 1, 0, 1
+ };
+ /*
+ lkp translate all character to acceptable character (for DOS).
+ When lkp[n] == n, it means also it is an acceptable one.
+ So it serve both as a flag and as a translator.
+ */
+ static char lkp[256];
+ static char is_init=0;
+ if (!is_init){
+ /*
+ Initialisation of the array is easier and less error prone
+ like this.
+ */
+ int i;
+ static char *spc = "\"*+,/:;<=>?[\\]|~";
+ is_init = 1;
+ for (i=0; i<=32; i++) lkp[i] = '#';
+ for (i=33; i<'A'; i++) lkp[i] = (char)i;
+ for (i='A'; i<='Z'; i++) lkp[i] = (char)(i+('a'-'A'));
+ for (i='Z'+1; i<127; i++) lkp[i] = (char)i;
+ for (i=128; i<256; i++) lkp[i] = '#';
+
+ lkp['.'] = '_';
+ while (*spc != '\0') lkp[(unsigned char)(*spc++)] = '#';
+ }
+ /* GLU
+ file name which are longer than 8+'.'+3 are invalid for MsDOS.
+ So the file name is to be mangled no more test needed.
+ This Speed Up for long and very long name.
+ The position of the last point is no more necessary anyway.
+ */
+ if (len<=(8+1+3)){
+ const char *pt = fname;
+ const char *endpt = fname + len;
+ while (pt < endpt){
+ if (*pt == '.'){
+ if (firstpt != NULL){
+ /* 2 . in a file name. Reject */
+ ivldchar = 1;
+ break;
+ }else{
+ int extlen = (int)(endpt - pt);
+ firstpt = pt;
+ if (firstpt - fname > 8){
+ /* base name longer than 8: reject */
+ ivldchar = 1;
+ break;
+ }else if (extlen > 4){
+ /* Extension longer than 4 (including .): reject */
+ ivldchar = 1;
+ break;
+ }else if (extlen == 1){
+ /* #Specification: file name / non MSDOS conforming / last char == .
+ If the last character of a file name is
+ a period, mangling is applied. MsDOS do
+ not support those file name.
+ */
+ ivldchar = 1;
+ break;
+ }else if (extlen == 4){
+ /* #Specification: file name / non MSDOS conforming / mangling clash
+ To avoid clash with the umsdos mangling, any file
+ with a special character as the first character
+ of the extension will be mangled. This solve the
+ following problem:
+
+ touch FILE
+ # FILE is invalid for DOS, so mangling is applied
+ # file.{_1 is created in the DOS directory
+ touch file.{_1
+ # To UMSDOS file point to a single DOS entry.
+ # So file.{_1 has to be mangled.
+ */
+ static char special[]={
+ SPECIAL_MANGLING,'\0'
+ };
+ if (strchr(special,firstpt[1])!= NULL){
+ ivldchar = 1;
+ break;
+ }
+ }
+ }
+ }else if (lkp[(unsigned char)(*pt)] != *pt){
+ ivldchar = 1;
+ break;
+ }
+ pt++;
+ }
+ }else{
+ ivldchar = 1;
+ }
+ if (ivldchar
+ || (firstpt == NULL && len > 8)
+ || (len == UMSDOS_EMD_NAMELEN
+ && memcmp(fname,UMSDOS_EMD_FILE,UMSDOS_EMD_NAMELEN)==0)){
+ /* #Specification: file name / --linux-.---
+ The name of the EMD file --linux-.--- is map to a mangled
+ name. So UMSDOS does not restrict its use.
+ */
+ /* #Specification: file name / non MSDOS conforming / mangling
+ Non MSDOS conforming file name must use some alias to fit
+ in the MSDOS name space.
+
+ The strategy is simple. The name is simply truncated to
+ 8 char. points are replace with underscore and a
+ number is given as an extension. This number correspond
+ to the entry number in the EMD file. The EMD file
+ only need to carry the real name.
+
+ Upper case is also convert to lower case.
+ Control character are converted to #.
+ Space are converted to #.
+ The following character are also converted to #.
+ " * + , / : ; < = > ? [ \ ] | ~
+
+ Sometime, the problem is not in MsDOS itself but in
+ command.com.
+ */
+ int i;
+ char *pt = info->fake.fname;
+ base_len = msdos_len = (msdos_len>8) ? 8 : msdos_len;
+ /*
+ There is no '.' any more so we know for a fact that
+ the base length is the length.
+ */
+ memcpy (info->fake.fname,fname,msdos_len);
+ for (i=0; i<msdos_len; i++, pt++) *pt = lkp[(unsigned char)(*pt)];
+ *pt = '\0'; /* GLU C'est sur on a un 0 a la fin */
+ info->msdos_reject = 1;
+ /*
+ The numeric extension is added only when we know
+ the position in the EMD file, in umsdos_newentry(),
+ umsdos_delentry(), and umsdos_findentry().
+ See umsdos_manglename().
+ */
+ }else{
+ /* Conforming MSDOS file name */
+ strcpy (info->fake.fname,fname); /* GLU C'est sur on a un 0 a la fin */
+ info->msdos_reject = 0;
+ base_len = firstpt != NULL ? (int)(firstpt - fname) : len;
+ }
+ if (cardinal_per_size[base_len]){
+ /* #Specification: file name / MSDOS devices / mangling
+ To avoid unreachable file from MsDOS, any MsDOS conforming
+ file with a basename equal to one of the MsDOS pseudo
+ devices will be mangled.
+
+ If a file such as "prn" was created, it would be unreachable
+ under MsDOS because prn is assumed to be the printer, even
+ if the file does have an extension.
+
+ Since the extension is unimportant to MsDOS, we must patch
+ the basename also. We simply insert a minus '-'. To avoid
+ conflict with valid file with a minus in front (such as
+ "-prn"), we add an mangled extension like any other
+ mangled file name.
+
+ Here is the list of DOS pseudo devices:
+
+ "prn","con","aux","nul",
+ "lpt1","lpt2","lpt3","lpt4",
+ "com1","com2","com3","com4",
+ "clock$"
+
+ and some standard ones for common DOS programs
+
+ "emmxxxx0","xmsxxxx0","setverxx"
+
+ (Thanks to Chris Hall <CAH17@PHOENIX.CAMBRIDGE.AC.UK>
+ for pointing these to me).
+
+ Is there one missing ?
+ */
+ /* This table must be ordered by length */
+ static const char *tbdev[]={
+ "prn","con","aux","nul",
+ "lpt1","lpt2","lpt3","lpt4",
+ "com1","com2","com3","com4",
+ "clock$",
+ "emmxxxx0","xmsxxxx0","setverxx"
+ };
+ /* Tell where to find in tbdev[], the first name of */
+ /* a certain length */
+ static const char start_ind_dev[9]={
+ 0, 0, 0, 4, 12, 12, 13, 13, 16
+ };
+ char basen[9];
+ int i;
+ for (i=start_ind_dev[base_len-1]; i<start_ind_dev[base_len]; i++){
+ if (memcmp(info->fake.fname,tbdev[i],base_len)==0){
+ memcpy (basen,info->fake.fname,base_len);
+ basen[base_len] = '\0'; /* GLU C'est sur on a un 0 a la fin */
+ /*
+ GLU On ne fait cela que si necessaire, on essaye d'etre le
+ GLU simple dans le cas general (le plus frequent).
+ */
+ info->fake.fname[0] = '-';
+ strcpy (info->fake.fname+1,basen); /* GLU C'est sur on a un 0 a la fin */
+ msdos_len = (base_len==8) ? 8 : base_len + 1;
+ info->msdos_reject = 1;
+ break;
+ }
+ }
+ }
+ info->fake.fname[msdos_len] = '\0'; /* Help doing printk */
+ /* GLU Ce zero devrais deja y etre ! (invariant ?) */
+ info->fake.len = msdos_len;
+ /* Pourquoi ne pas utiliser info->fake.len partout ??? plus long ?*/
+ memcpy (info->entry.name,fname,len);
+ info->entry.name_len = len;
+ ret = 0;
+ }
+ /*
+ Evaluate how many record are needed to store this entry.
+ */
+ info->recsize = umsdos_evalrecsize (len);
+ return ret;
+}
+
+#ifdef TEST
+
+struct MANG_TEST{
+ char *fname; /* Name to validate */
+ int msdos_reject; /* Expected msdos_reject flag */
+ char *msname; /* Expected msdos name */
+};
+
+struct MANG_TEST tb[]={
+ "hello", 0, "hello",
+ "hello.1", 0, "hello.1",
+ "hello.1_", 0, "hello.1_",
+ "prm", 0, "prm",
+
+#ifdef PROPOSITION
+ "HELLO", 1, "hello",
+ "Hello.1", 1, "hello.1",
+ "Hello.c", 1, "hello.c",
+#elseif
+/*
+ Je trouve les trois exemples ci-dessous tres "malheureux".
+ Je propose de mettre en minuscule dans un passe preliminaire,
+ et de tester apres si il y a d'autres caracters "mechants".
+ Bon, je ne l'ai pas fait, parceque ce n'est pas si facilement
+ modifiable que ca. Mais c'est pour le principe.
+ Evidemment cela augmente les chances de "Collision",
+ par exemple: entre "HELLO" et "Hello", mais ces problemes
+ peuvent etre traiter ailleur avec les autres collisions.
+*/
+ "HELLO", 1, "hello",
+ "Hello.1", 1, "hello_1",
+ "Hello.c", 1, "hello_c",
+#endif
+
+ "hello.{_1", 1, "hello_{_",
+ "hello\t", 1, "hello#",
+ "hello.1.1", 1, "hello_1_",
+ "hel,lo", 1, "hel#lo",
+ "Salut.Tu.vas.bien?", 1, "salut_tu",
+ ".profile", 1, "_profile",
+ ".xv", 1, "_xv",
+ "toto.", 1, "toto_",
+ "clock$.x", 1, "-clock$",
+ "emmxxxx0", 1, "-emmxxxx",
+ "emmxxxx0.abcd", 1, "-emmxxxx",
+ "aux", 1, "-aux",
+ "prn", 1, "-prn",
+ "prn.abc", 1, "-prn",
+ "PRN", 1, "-prn",
+/*
+GLU ATTENTION : Le resultat de ceux-ci sont differents avec ma version
+GLU du mangle par rapport au mangle originale.
+GLU CAUSE: La maniere de calculer la variable baselen.
+GLU Pour toi c'est toujours 3
+GLU Pour moi c'est respectivement 7, 8 et 8
+*/
+ "PRN.abc", 1, "prn_abc",
+ "Prn.abcd", 1, "prn_abcd",
+ "prn.abcd", 1, "prn_abcd",
+ "Prn.abcdefghij", 1, "prn_abcd"
+};
+
+int main (int argc, char *argv[])
+{
+ int i,rold,rnew;
+ printf ("Testing the umsdos_parse.\n");
+ for (i=0; i<sizeof(tb)/sizeof(tb[0]); i++){
+ struct MANG_TEST *pttb = tb+i;
+ struct umsdos_info info;
+ int ok = umsdos_parse (pttb->fname,strlen(pttb->fname),&info);
+ if (strcmp(info.fake.fname,pttb->msname)!=0){
+ printf ("**** %s -> ",pttb->fname);
+ printf ("%s <> %s\n",info.fake.fname,pttb->msname);
+ }else if (info.msdos_reject != pttb->msdos_reject){
+ printf ("**** %s -> %s ",pttb->fname,pttb->msname);
+ printf ("%d <> %d\n",info.msdos_reject,pttb->msdos_reject);
+ }else{
+ printf (" %s -> %s %d\n",pttb->fname,pttb->msname
+ ,pttb->msdos_reject);
+ }
+ }
+ printf ("Testing the new umsdos_evalrecsize.");
+ for (i=0; i<UMSDOS_MAXNAME ; i++){
+ rnew=umsdos_evalrecsize (i);
+ rold=umsdos_evalrecsize_old (i);
+ if (!(i%UMSDOS_REC_SIZE)){
+ printf ("\n%d:\t",i);
+ }
+ if (rnew!=rold){
+ printf ("**** %d newres: %d != %d \n", i, rnew, rold);
+ }else{
+ printf(".");
+ }
+ }
+ printf ("\nEnd of Testing.\n");
+
+ return 0;
+}
+
+#endif
diff --git a/fs/umsdos/namei.c b/fs/umsdos/namei.c
new file mode 100644
index 000000000..567039e14
--- /dev/null
+++ b/fs/umsdos/namei.c
@@ -0,0 +1,1043 @@
+/*
+ * linux/fs/umsdos/namei.c
+ *
+ * Written 1993 by Jacques Gelinas
+ * Inspired from linux/fs/msdos/... by Werner Almesberger
+ *
+ * Maintain and access the --linux alternate directory file.
+*/
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/stat.h>
+#include <linux/string.h>
+#include <linux/msdos_fs.h>
+#include <linux/umsdos_fs.h>
+#include <linux/malloc.h>
+
+#define PRINTK(x)
+#define Printk(x) printk x
+
+#if 1
+/*
+ Wait for creation exclusivity.
+ Return 0 if the dir was already available.
+ Return 1 if a wait was necessary.
+ When 1 is return, it means a wait was done. It does not
+ mean the directory is available.
+*/
+static int umsdos_waitcreate(struct inode *dir)
+{
+ int ret = 0;
+ if (dir->u.umsdos_i.u.dir_info.creating
+ && dir->u.umsdos_i.u.dir_info.pid != current->pid){
+ sleep_on(&dir->u.umsdos_i.u.dir_info.p);
+ ret = 1;
+ }
+ return ret;
+}
+/*
+ Wait for any lookup process to finish
+*/
+static void umsdos_waitlookup (struct inode *dir)
+{
+ while (dir->u.umsdos_i.u.dir_info.looking){
+ sleep_on(&dir->u.umsdos_i.u.dir_info.p);
+ }
+}
+/*
+ Lock all other process out of this directory.
+*/
+void umsdos_lockcreate (struct inode *dir)
+{
+ /* #Specification: file creation / not atomic
+ File creation is a two step process. First we create (allocate)
+ an entry in the EMD file and then (using the entry offset) we
+ build a unique name for MSDOS. We create this name in the msdos
+ space.
+
+ We have to use semaphore (sleep_on/wake_up) to prevent lookup
+ into a directory when we create a file or directory and to
+ prevent creation while a lookup is going on. Since many lookup
+ may happen at the same time, the semaphore is a counter.
+
+ Only one creation is allowed at the same time. This protection
+ may not be necessary. The problem arise mainly when a lookup
+ or a readdir is done while a file is partially created. The
+ lookup process see that as a "normal" problem and silently
+ erase the file from the EMD file. Normal because a file
+ may be erased during a MSDOS session, but not removed from
+ the EMD file.
+
+ The locking is done on a directory per directory basis. Each
+ directory inode has its wait_queue.
+
+ For some operation like hard link, things even get worse. Many
+ creation must occur at once (atomic). To simplify the design
+ a process is allowed to recursively lock the directory for
+ creation. The pid of the locking process is kept along with
+ a counter so a second level of locking is granted or not.
+ */
+ /*
+ Wait for any creation process to finish except
+ if we (the process) own the lock
+ */
+ while (umsdos_waitcreate(dir)!=0);
+ dir->u.umsdos_i.u.dir_info.creating++;
+ dir->u.umsdos_i.u.dir_info.pid = current->pid;
+ umsdos_waitlookup (dir);
+}
+/*
+ Lock all other process out of those two directories.
+*/
+static void umsdos_lockcreate2 (struct inode *dir1, struct inode *dir2)
+{
+ /*
+ We must check that both directory are available before
+ locking anyone of them. This is to avoid some deadlock.
+ Thanks to dglaude@is1.vub.ac.be (GLAUDE DAVID) for pointing
+ this to me.
+ */
+ while (1){
+ if (umsdos_waitcreate(dir1)==0
+ && umsdos_waitcreate(dir2)==0){
+ /* We own both now */
+ dir1->u.umsdos_i.u.dir_info.creating++;
+ dir1->u.umsdos_i.u.dir_info.pid = current->pid;
+ dir2->u.umsdos_i.u.dir_info.creating++;
+ dir2->u.umsdos_i.u.dir_info.pid = current->pid;
+ break;
+ }
+ }
+ umsdos_waitlookup(dir1);
+ umsdos_waitlookup(dir2);
+}
+/*
+ Wait until creation is finish in this directory.
+*/
+void umsdos_startlookup (struct inode *dir)
+{
+ while (umsdos_waitcreate (dir) != 0);
+ dir->u.umsdos_i.u.dir_info.looking++;
+}
+void check_page_tables(void);
+
+/*
+ Unlock the directory.
+*/
+void umsdos_unlockcreate (struct inode *dir)
+{
+ dir->u.umsdos_i.u.dir_info.creating--;
+ if (dir->u.umsdos_i.u.dir_info.creating < 0){
+ printk ("UMSDOS: dir->u.umsdos_i.u.dir_info.creating < 0: %d"
+ ,dir->u.umsdos_i.u.dir_info.creating);
+ }
+ wake_up (&dir->u.umsdos_i.u.dir_info.p);
+}
+/*
+ Tell directory lookup is over.
+*/
+void umsdos_endlookup (struct inode *dir)
+{
+ dir->u.umsdos_i.u.dir_info.looking--;
+ if (dir->u.umsdos_i.u.dir_info.looking < 0){
+ printk ("UMSDOS: dir->u.umsdos_i.u.dir_info.looking < 0: %d"
+ ,dir->u.umsdos_i.u.dir_info.looking);
+ }
+ wake_up (&dir->u.umsdos_i.u.dir_info.p);
+}
+#else
+static void umsdos_lockcreate (struct inode *dir){}
+static void umsdos_lockcreate2 (struct inode *dir1, struct inode *dir2){}
+void umsdos_startlookup (struct inode *dir){}
+static void umsdos_unlockcreate (struct inode *dir){}
+void umsdos_endlookup (struct inode *dir){}
+#endif
+static int umsdos_nevercreat(
+ struct inode *dir,
+ const char *name, /* Name of the file to add */
+ int len,
+ int errcod) /* Length of the name */
+{
+ int ret = 0;
+ if (umsdos_is_pseudodos(dir,name,len)){
+ /* #Specification: pseudo root / any file creation /DOS
+ The pseudo sub-directory /DOS can't be created!
+ EEXIST is returned.
+
+ The pseudo sub-directory /DOS can't be removed!
+ EPERM is returned.
+ */
+ ret = -EPERM;
+ ret = errcod;
+ }else if (name[0] == '.'
+ && (len == 1 || (len == 2 && name[1] == '.'))){
+ /* #Specification: create / . and ..
+ If one try to creates . or .., it always fail and return
+ EEXIST.
+
+ If one try to delete . or .., it always fail and return
+ EPERM.
+
+ This should be test at the VFS layer level to avoid
+ duplicating this in all file systems. Any comments ?
+ */
+ ret = errcod;
+ }
+ return ret;
+}
+
+/*
+ Add a new file (ordinary or special) into the alternate directory.
+ The file is added to the real MSDOS directory. If successful, it
+ is then added to the EDM file.
+
+ Return the status of the operation. 0 mean success.
+*/
+static int umsdos_create_any (
+ struct inode *dir,
+ const char *name, /* Name of the file to add */
+ int len, /* Length of the name */
+ int mode, /* Permission bit + file type ??? */
+ int rdev, /* major, minor or 0 for ordinary file */
+ /* and symlinks */
+ char flags,
+ struct inode **result) /* Will hold the inode of the newly created */
+ /* file */
+{
+ int ret = umsdos_nevercreat(dir,name,len,-EEXIST);
+ if (ret == 0){
+ struct umsdos_info info;
+ ret = umsdos_parse (name,len,&info);
+ *result = NULL;
+ if (ret == 0){
+ info.entry.mode = mode;
+ info.entry.rdev = rdev;
+ info.entry.flags = flags;
+ info.entry.uid = current->fsuid;
+ info.entry.gid = (dir->i_mode & S_ISGID)
+ ? dir->i_gid : current->fsgid;
+ info.entry.ctime = info.entry.atime = info.entry.mtime
+ = CURRENT_TIME;
+ info.entry.nlink = 1;
+ umsdos_lockcreate(dir);
+ ret = umsdos_newentry (dir,&info);
+ if (ret == 0){
+ dir->i_count++;
+ ret = msdos_create (dir,info.fake.fname,info.fake.len
+ ,S_IFREG|0777,result);
+ if (ret == 0){
+ struct inode *inode = *result;
+ umsdos_lookup_patch (dir,inode,&info.entry,info.f_pos);
+ PRINTK (("inode %p[%d] ",inode,inode->i_count));
+ PRINTK (("Creation OK: [%d] %s %d pos %d\n",dir->i_ino
+ ,info.fake.fname,current->pid,info.f_pos));
+ }else{
+ /* #Specification: create / file exist in DOS
+ Here is a situation. Trying to create a file with
+ UMSDOS. The file is unknown to UMSDOS but already
+ exist in the DOS directory.
+
+ Here is what we are NOT doing:
+
+ We could silently assume that everything is fine
+ and allows the creation to succeed.
+
+ It is possible not all files in the partition
+ are mean to be visible from linux. By trying to create
+ those file in some directory, one user may get access
+ to those file without proper permissions. Looks like
+ a security hole to me. Off course sharing a file system
+ with DOS is some kind of security hole :-)
+
+ So ?
+
+ We return EEXIST in this case.
+ The same is true for directory creation.
+ */
+ if (ret == -EEXIST){
+ printk ("UMSDOS: out of sync, Creation error [%ld], "
+ "deleting %s %d %d pos %ld\n",dir->i_ino
+ ,info.fake.fname,-ret,current->pid,info.f_pos);
+ }
+ umsdos_delentry (dir,&info,0);
+ }
+ PRINTK (("umsdos_create %s ret = %d pos %d\n"
+ ,info.fake.fname,ret,info.f_pos));
+ }
+ umsdos_unlockcreate(dir);
+ }
+ }
+ iput (dir);
+ return ret;
+}
+/*
+ Initialise the new_entry from the old for a rename operation.
+ (Only useful for umsdos_rename_f() below).
+*/
+static void umsdos_ren_init(
+ struct umsdos_info *new_info,
+ struct umsdos_info *old_info,
+ int flags) /* 0 == copy flags from old_name */
+ /* != 0, this is the value of flags */
+{
+ new_info->entry.mode = old_info->entry.mode;
+ new_info->entry.rdev = old_info->entry.rdev;
+ new_info->entry.uid = old_info->entry.uid;
+ new_info->entry.gid = old_info->entry.gid;
+ new_info->entry.ctime = old_info->entry.ctime;
+ new_info->entry.atime = old_info->entry.atime;
+ new_info->entry.mtime = old_info->entry.mtime;
+ new_info->entry.flags = flags ? flags : old_info->entry.flags;
+ new_info->entry.nlink = old_info->entry.nlink;
+}
+
+#define chkstk() \
+ if (STACK_MAGIC != *(unsigned long *)current->kernel_stack_page){\
+ printk(KERN_ALERT "UMSDOS: %s magic %x != %lx ligne %d\n" \
+ , current->comm,STACK_MAGIC \
+ ,*(unsigned long *)current->kernel_stack_page \
+ ,__LINE__); \
+ }
+
+/*
+ Rename a file (move) in the file system.
+*/
+static int umsdos_rename_f(
+ struct inode * old_dir,
+ const char * old_name,
+ int old_len,
+ struct inode * new_dir,
+ const char * new_name,
+ int new_len,
+ int flags) /* 0 == copy flags from old_name */
+ /* != 0, this is the value of flags */
+{
+ int ret = EPERM;
+ struct umsdos_info old_info;
+ int old_ret = umsdos_parse (old_name,old_len,&old_info);
+ struct umsdos_info new_info;
+ int new_ret = umsdos_parse (new_name,new_len,&new_info);
+chkstk();
+ PRINTK (("umsdos_rename %d %d ",old_ret,new_ret));
+ if (old_ret == 0 && new_ret == 0){
+ umsdos_lockcreate2(old_dir,new_dir);
+chkstk();
+ PRINTK (("old findentry "));
+ ret = umsdos_findentry(old_dir,&old_info,0);
+chkstk();
+ PRINTK (("ret %d ",ret));
+ if (ret == 0){
+ PRINTK (("new newentry "));
+ umsdos_ren_init(&new_info,&old_info,flags);
+ ret = umsdos_newentry (new_dir,&new_info);
+chkstk();
+ PRINTK (("ret %d %d ",ret,new_info.fake.len));
+ if (ret == 0){
+ PRINTK (("msdos_rename "));
+ old_dir->i_count++;
+ new_dir->i_count++; /* Both inode are needed later */
+ ret = msdos_rename (old_dir
+ ,old_info.fake.fname,old_info.fake.len
+ ,new_dir
+ ,new_info.fake.fname,new_info.fake.len);
+chkstk();
+ PRINTK (("after m_rename ret %d ",ret));
+ if (ret != 0){
+ umsdos_delentry (new_dir,&new_info
+ ,S_ISDIR(new_info.entry.mode));
+chkstk();
+ }else{
+ ret = umsdos_delentry (old_dir,&old_info
+ ,S_ISDIR(old_info.entry.mode));
+chkstk();
+ if (ret == 0){
+ /*
+ This UMSDOS_lookup does not look very useful.
+ It makes sure that the inode of the file will
+ be correctly setup (umsdos_patch_inode()) in
+ case it is already in use.
+
+ Not very efficient ...
+ */
+ struct inode *inode;
+ new_dir->i_count++;
+ PRINTK (("rename lookup len %d %d -- ",new_len,new_info.entry.flags));
+ ret = UMSDOS_lookup (new_dir,new_name,new_len
+ ,&inode);
+chkstk();
+ if (ret != 0){
+ printk ("UMSDOS: partial rename for file %s\n"
+ ,new_info.entry.name);
+ }else{
+ /*
+ Update f_pos so notify_change will succeed
+ if the file was already in use.
+ */
+ umsdos_set_dirinfo (inode,new_dir,new_info.f_pos);
+chkstk();
+ iput (inode);
+ }
+ }
+ }
+ }
+ }
+ umsdos_unlockcreate(old_dir);
+ umsdos_unlockcreate(new_dir);
+ }
+ iput (old_dir);
+ iput (new_dir);
+ PRINTK (("\n"));
+ return ret;
+}
+/*
+ Setup un Symbolic link or a (pseudo) hard link
+ Return a negative error code or 0 if ok.
+*/
+static int umsdos_symlink_x(
+ struct inode * dir,
+ const char * name,
+ int len,
+ const char * symname, /* name will point to this path */
+ int mode,
+ char flags)
+{
+ /* #Specification: symbolic links / strategy
+ A symbolic link is simply a file which hold a path. It is
+ implemented as a normal MSDOS file (not very space efficient :-()
+
+ I see 2 different way to do it. One is to place the link data
+ in unused entry of the EMD file. The other is to have a separate
+ file dedicated to hold all symbolic links data.
+
+ Lets go for simplicity...
+ */
+ struct inode *inode;
+ int ret;
+ dir->i_count++; /* We keep the inode in case we need it */
+ /* later */
+ ret = umsdos_create_any (dir,name,len,mode,0,flags,&inode);
+ PRINTK (("umsdos_symlink ret %d ",ret));
+ if (ret == 0){
+ int len = strlen(symname);
+ struct file filp;
+ filp.f_pos = 0;
+ /* Make the inode acceptable to MSDOS */
+ ret = umsdos_file_write_kmem (inode,&filp,(char*)symname,len);
+ iput (inode);
+ if (ret >= 0){
+ if (ret != len){
+ ret = -EIO;
+ printk ("UMSDOS: "
+ "Can't write symbolic link data\n");
+ }else{
+ ret = 0;
+ }
+ }
+ if (ret != 0){
+ UMSDOS_unlink (dir,name,len);
+ dir = NULL;
+ }
+ }
+ iput (dir);
+ PRINTK (("\n"));
+ return ret;
+}
+/*
+ Setup un Symbolic link.
+ Return a negative error code or 0 if ok.
+*/
+int UMSDOS_symlink(
+ struct inode * dir,
+ const char * name,
+ int len,
+ const char * symname) /* name will point to this path */
+{
+ return umsdos_symlink_x (dir,name,len,symname,S_IFLNK|0777,0);
+}
+/*
+ Add a link to an inode in a directory
+*/
+int UMSDOS_link (
+ struct inode * oldinode,
+ struct inode * dir,
+ const char * name,
+ int len)
+{
+ /* #Specification: hard link / strategy
+ Well ... hard link are difficult to implement on top of an
+ MsDOS fat file system. Unlike UNIX file systems, there are no
+ inode. A directory entry hold the functionality of the inode
+ and the entry.
+
+ We will used the same strategy as a normal Unix file system
+ (with inode) except we will do it symbolically (using paths).
+
+ Because anything can happen during a DOS session (defragment,
+ directory sorting, etc...), we can't rely on MsDOS pseudo
+ inode number to record the link. For this reason, the link
+ will be done using hidden symbolic links. The following
+ scenario illustrate how it work.
+
+ Given a file /foo/file
+
+ ln /foo/file /tmp/file2
+
+ become internally
+
+ mv /foo/file /foo/-LINK1
+ ln -s /foo/-LINK1 /foo/file
+ ln -s /foo/-LINK1 /tmp/file2
+
+ Using this strategy, we can operate on /foo/file or /foo/file2.
+ We can remove one and keep the other, like a normal Unix hard link.
+ We can rename /foo/file or /tmp/file2 independently.
+
+ The entry -LINK1 will be hidden. It will hold a link count.
+ When all link are erased, the hidden file is erased too.
+ */
+ /* #Specification: weakness / hard link
+ The strategy for hard link introduces a side effect that
+ may or may not be acceptable. Here is the sequence
+
+ mkdir subdir1
+ touch subdir1/file
+ mkdir subdir2
+ ln subdir1/file subdir2/file
+ rm subdir1/file
+ rmdir subdir1
+ rmdir: subdir1: Directory not empty
+
+ This happen because there is an invisible file (--link) in
+ subdir1 which is referenced by subdir2/file.
+
+ Any idea ?
+ */
+ /* #Specification: weakness / hard link / rename directory
+ Another weakness of hard link come from the fact that
+ it is based on hidden symbolic links. Here is an example.
+
+ mkdir /subdir1
+ touch /subdir1/file
+ mkdir /subdir2
+ ln /subdir1/file subdir2/file
+ mv /subdir1 subdir3
+ ls -l /subdir2/file
+
+ Since /subdir2/file is a hidden symbolic link
+ to /subdir1/..hlinkNNN, accessing it will fail since
+ /subdir1 does not exist anymore (has been renamed).
+ */
+ int ret = 0;
+ if (S_ISDIR(oldinode->i_mode)){
+ /* #Specification: hard link / directory
+ A hard link can't be made on a directory. EPERM is returned
+ in this case.
+ */
+ ret = -EPERM;
+ }else if ((ret = umsdos_nevercreat(dir,name,len,-EPERM))==0){
+ struct inode *olddir;
+ ret = umsdos_get_dirowner(oldinode,&olddir);
+ PRINTK (("umsdos_link dir_owner = %d -> %p [%d] "
+ ,oldinode->u.umsdos_i.i_dir_owner,olddir,olddir->i_count));
+ if (ret == 0){
+ struct umsdos_dirent entry;
+ umsdos_lockcreate2(dir,olddir);
+ ret = umsdos_inode2entry (olddir,oldinode,&entry);
+ if (ret == 0){
+ PRINTK (("umsdos_link :%s: ino %d flags %d "
+ ,entry.name
+ ,oldinode->i_ino,entry.flags));
+ if (!(entry.flags & UMSDOS_HIDDEN)){
+ /* #Specification: hard link / first hard link
+ The first time a hard link is done on a file, this
+ file must be renamed and hidden. Then an internal
+ symbolic link must be done on the hidden file.
+
+ The second link is done after on this hidden file.
+
+ It is expected that the Linux MSDOS file system
+ keeps the same pseudo inode when a rename operation
+ is done on a file in the same directory.
+ */
+ struct umsdos_info info;
+ ret = umsdos_newhidden (olddir,&info);
+ if (ret == 0){
+ olddir->i_count+=2;
+ PRINTK (("olddir[%d] ",olddir->i_count));
+ ret = umsdos_rename_f (olddir,entry.name
+ ,entry.name_len
+ ,olddir,info.entry.name,info.entry.name_len
+ ,UMSDOS_HIDDEN);
+ if (ret == 0){
+ char *path = (char*)kmalloc(PATH_MAX,GFP_KERNEL);
+ if (path == NULL){
+ ret = -ENOMEM;
+ }else{
+ PRINTK (("olddir[%d] ",olddir->i_count));
+ ret = umsdos_locate_path (oldinode,path);
+ PRINTK (("olddir[%d] ",olddir->i_count));
+ if (ret == 0){
+ olddir->i_count++;
+ ret = umsdos_symlink_x (olddir
+ ,entry.name
+ ,entry.name_len,path
+ ,S_IFREG|0777,UMSDOS_HLINK);
+ if (ret == 0){
+ dir->i_count++;
+ ret = umsdos_symlink_x (dir,name,len
+ ,path
+ ,S_IFREG|0777,UMSDOS_HLINK);
+ }
+ }
+ kfree (path);
+ }
+ }
+ }
+ }else{
+ char *path = (char*)kmalloc(PATH_MAX,GFP_KERNEL);
+ if (path == NULL){
+ ret = -ENOMEM;
+ }else{
+ ret = umsdos_locate_path (oldinode,path);
+ if (ret == 0){
+ dir->i_count++;
+ ret = umsdos_symlink_x (dir,name,len,path
+ ,S_IFREG|0777,UMSDOS_HLINK);
+ }
+ kfree (path);
+ }
+ }
+ }
+ umsdos_unlockcreate(olddir);
+ umsdos_unlockcreate(dir);
+ }
+ iput (olddir);
+ }
+ if (ret == 0){
+ struct iattr newattrs;
+ oldinode->i_nlink++;
+ newattrs.ia_valid = 0;
+ ret = UMSDOS_notify_change(oldinode, &newattrs);
+ }
+ iput (oldinode);
+ iput (dir);
+ PRINTK (("umsdos_link %d\n",ret));
+ return ret;
+}
+/*
+ Add a new file into the alternate directory.
+ The file is added to the real MSDOS directory. If successful, it
+ is then added to the EDM file.
+
+ Return the status of the operation. 0 mean success.
+*/
+int UMSDOS_create (
+ struct inode *dir,
+ const char *name, /* Name of the file to add */
+ int len, /* Length of the name */
+ int mode, /* Permission bit + file type ??? */
+ struct inode **result) /* Will hold the inode of the newly created */
+ /* file */
+{
+ return umsdos_create_any (dir,name,len,mode,0,0,result);
+}
+/*
+ Add a sub-directory in a directory
+*/
+int UMSDOS_mkdir(
+ struct inode * dir,
+ const char * name,
+ int len,
+ int mode)
+{
+ int ret = umsdos_nevercreat(dir,name,len,-EEXIST);
+ if (ret == 0){
+ struct umsdos_info info;
+ ret = umsdos_parse (name,len,&info);
+ PRINTK (("umsdos_mkdir %d\n",ret));
+ if (ret == 0){
+ info.entry.mode = mode | S_IFDIR;
+ info.entry.rdev = 0;
+ info.entry.uid = current->fsuid;
+ info.entry.gid = (dir->i_mode & S_ISGID)
+ ? dir->i_gid : current->fsgid;
+ info.entry.ctime = info.entry.atime = info.entry.mtime
+ = CURRENT_TIME;
+ info.entry.flags = 0;
+ umsdos_lockcreate(dir);
+ info.entry.nlink = 1;
+ ret = umsdos_newentry (dir,&info);
+ PRINTK (("newentry %d ",ret));
+ if (ret == 0){
+ dir->i_count++;
+ ret = msdos_mkdir (dir,info.fake.fname,info.fake.len,mode);
+ if (ret != 0){
+ umsdos_delentry (dir,&info,1);
+ /* #Specification: mkdir / Directory already exist in DOS
+ We do the same thing as for file creation.
+ For all user it is an error.
+ */
+ }else{
+ /* #Specification: mkdir / umsdos directory / create EMD
+ When we created a new sub-directory in a UMSDOS
+ directory (one with full UMSDOS semantic), we
+ create immediately an EMD file in the new
+ sub-directory so it inherit UMSDOS semantic.
+ */
+ struct inode *subdir;
+ ret = umsdos_real_lookup (dir,info.fake.fname
+ ,info.fake.len,&subdir);
+ if (ret == 0){
+ struct inode *result;
+ ret = msdos_create (subdir,UMSDOS_EMD_FILE
+ ,UMSDOS_EMD_NAMELEN,S_IFREG|0777,&result);
+ subdir = NULL;
+ iput (result);
+ }
+ if (ret < 0){
+ printk ("UMSDOS: Can't create empty --linux-.---\n");
+ }
+ iput (subdir);
+ }
+ }
+ umsdos_unlockcreate(dir);
+ }
+ }
+ PRINTK (("umsdos_mkdir %d\n",ret));
+ iput (dir);
+ return ret;
+}
+/*
+ Add a new device special file into a directory.
+*/
+int UMSDOS_mknod(
+ struct inode * dir,
+ const char * name,
+ int len,
+ int mode,
+ int rdev)
+{
+ /* #Specification: Special files / strategy
+ Device special file, pipes, etc ... are created like normal
+ file in the msdos file system. Of course they remain empty.
+
+ One strategy was to create those files only in the EMD file
+ since they were not important for MSDOS. The problem with
+ that, is that there were not getting inode number allocated.
+ The MSDOS filesystems is playing a nice game to fake inode
+ number, so why not use it.
+
+ The absence of inode number compatible with those allocated
+ for ordinary files was causing major trouble with hard link
+ in particular and other parts of the kernel I guess.
+ */
+ struct inode *inode;
+ int ret = umsdos_create_any (dir,name,len,mode,rdev,0,&inode);
+ iput (inode);
+ return ret;
+}
+
+/*
+ Remove a sub-directory.
+*/
+int UMSDOS_rmdir(
+ struct inode * dir,
+ const char * name,
+ int len)
+{
+ /* #Specification: style / iput strategy
+ In the UMSDOS project, I am trying to apply a single
+ programming style regarding inode management. Many
+ entry point are receiving an inode to act on, and must
+ do an iput() as soon as they are finished with
+ the inode.
+
+ For simple case, there is no problem. When you introduce
+ error checking, you end up with many iput placed around the
+ code.
+
+ The coding style I use all around is one where I am trying
+ to provide independent flow logic (I don't know how to
+ name this). With this style, code is easier to understand
+ but you rapidly get iput() all around. Here is an exemple
+ of what I am trying to avoid.
+
+ if (a){
+ ...
+ if(b){
+ ...
+ }
+ ...
+ if (c){
+ // Complex state. Was b true ?
+ ...
+ }
+ ...
+ }
+ // Weird state
+ if (d){
+ // ...
+ }
+ // Was iput finally done ?
+ return status;
+
+ Here is the style I am using. Still sometime I do the
+ first when things are very simple (or very complicated :-( )
+
+ if (a){
+ if (b){
+ ...
+ }else if (c){
+ // A single state gets here
+ }
+ }else if (d){
+ ...
+ }
+ return status;
+
+ Again, while this help clarifying the code, I often get a lot
+ of iput(), unlike the first style, where I can place few
+ "strategic" iput(). "strategic" also mean, more difficult
+ to place.
+
+ So here is the style I will be using from now on in this project.
+ There is always an iput() at the end of a function (which has
+ to do an iput()). One iput by inode. There is also one iput()
+ at the places where a successful operation is achieved. This
+ iput() is often done by a sub-function (often from the msdos
+ file system). So I get one too many iput() ? At the place
+ where an iput() is done, the inode is simply nulled, disabling
+ the last one.
+
+ if (a){
+ if (b){
+ ...
+ }else if (c){
+ msdos_rmdir(dir,...);
+ dir = NULL;
+ }
+ }else if (d){
+ ...
+ }
+ iput (dir);
+ return status;
+
+ Note that the umsdos_lockcreate() and umsdos_unlockcreate() function
+ pair goes against this practice of "forgetting" the inode as soon
+ as possible.
+ */
+ int ret = umsdos_nevercreat(dir,name,len,-EPERM);
+ if (ret == 0){
+ struct inode *sdir;
+ dir->i_count++;
+ ret = UMSDOS_lookup (dir,name,len,&sdir);
+ PRINTK (("rmdir lookup %d ",ret));
+ if (ret == 0){
+ int empty;
+ umsdos_lockcreate(dir);
+ if (sdir->i_count > 1){
+ ret = -EBUSY;
+ }else if ((empty = umsdos_isempty (sdir)) != 0){
+ PRINTK (("isempty %d i_count %d ",empty,sdir->i_count));
+ if (empty == 1){
+ /* We have to removed the EMD file */
+ ret = msdos_unlink(sdir,UMSDOS_EMD_FILE
+ ,UMSDOS_EMD_NAMELEN);
+ sdir = NULL;
+ }
+ /* sdir must be free before msdos_rmdir() */
+ iput (sdir);
+ sdir = NULL;
+ PRINTK (("isempty ret %d nlink %d ",ret,dir->i_nlink));
+ if (ret == 0){
+ struct umsdos_info info;
+ dir->i_count++;
+ umsdos_parse (name,len,&info);
+ /* The findentry is there only to complete */
+ /* the mangling */
+ umsdos_findentry (dir,&info,2);
+ ret = msdos_rmdir (dir,info.fake.fname
+ ,info.fake.len);
+ if (ret == 0){
+ ret = umsdos_delentry (dir,&info,1);
+ }
+ }
+ }else{
+ /*
+ The subdirectory is not empty, so leave it there
+ */
+ ret = -ENOTEMPTY;
+ }
+ iput(sdir);
+ umsdos_unlockcreate(dir);
+ }
+ }
+ iput (dir);
+ PRINTK (("umsdos_rmdir %d\n",ret));
+ return ret;
+}
+/*
+ Remove a file from the directory.
+*/
+int UMSDOS_unlink (
+ struct inode * dir,
+ const char * name,
+ int len)
+{
+ struct umsdos_info info;
+ int ret = umsdos_nevercreat(dir,name,len,-EPERM);
+ if (ret == 0){
+ ret = umsdos_parse (name,len,&info);
+ if (ret == 0){
+ umsdos_lockcreate(dir);
+ ret = umsdos_findentry(dir,&info,1);
+ if (ret == 0){
+ PRINTK (("UMSDOS_unlink %s ",info.fake.fname));
+ if (info.entry.flags & UMSDOS_HLINK){
+ /* #Specification: hard link / deleting a link
+ When we deletes a file, and this file is a link
+ we must subtract 1 to the nlink field of the
+ hidden link.
+
+ If the count goes to 0, we delete this hidden
+ link too.
+ */
+ /*
+ First, get the inode of the hidden link
+ using the standard lookup function.
+ */
+ struct inode *inode;
+ dir->i_count++;
+ ret = UMSDOS_lookup (dir,name,len,&inode);
+ if (ret == 0){
+ PRINTK (("unlink nlink = %d ",inode->i_nlink));
+ inode->i_nlink--;
+ if (inode->i_nlink == 0){
+ struct inode *hdir = iget(inode->i_sb
+ ,inode->u.umsdos_i.i_dir_owner);
+ struct umsdos_dirent entry;
+ ret = umsdos_inode2entry (hdir,inode,&entry);
+ if (ret == 0){
+ ret = UMSDOS_unlink (hdir,entry.name
+ ,entry.name_len);
+ }else{
+ iput (hdir);
+ }
+ }else{
+ struct iattr newattrs;
+ newattrs.ia_valid = 0;
+ ret = UMSDOS_notify_change (inode, &newattrs);
+ }
+ iput (inode);
+ }
+ }
+ if (ret == 0){
+ ret = umsdos_delentry (dir,&info,0);
+ if (ret == 0){
+ PRINTK (("Avant msdos_unlink %s ",info.fake.fname));
+ dir->i_count++;
+ ret = msdos_unlink_umsdos (dir,info.fake.fname
+ ,info.fake.len);
+ PRINTK (("msdos_unlink %s %o ret %d ",info.fake.fname
+ ,info.entry.mode,ret));
+ }
+ }
+ }
+ umsdos_unlockcreate(dir);
+ }
+ }
+ iput (dir);
+ PRINTK (("umsdos_unlink %d\n",ret));
+ return ret;
+}
+
+/*
+ Rename a file (move) in the file system.
+*/
+int UMSDOS_rename(
+ struct inode * old_dir,
+ const char * old_name,
+ int old_len,
+ struct inode * new_dir,
+ const char * new_name,
+ int new_len)
+{
+ /* #Specification: weakness / rename
+ There is a case where UMSDOS rename has a different behavior
+ than normal UNIX file system. Renaming an open file across
+ directory boundary does not work. Renaming an open file within
+ a directory does work however.
+
+ The problem (not sure) is in the linux VFS msdos driver.
+ I believe this is not a bug but a design feature, because
+ an inode number represent some sort of directory address
+ in the MSDOS directory structure. So moving the file into
+ another directory does not preserve the inode number.
+ */
+ int ret = umsdos_nevercreat(new_dir,new_name,new_len,-EEXIST);
+ if (ret == 0){
+ /* umsdos_rename_f eat the inode and we may need those later */
+ old_dir->i_count++;
+ new_dir->i_count++;
+ ret = umsdos_rename_f (old_dir,old_name,old_len,new_dir,new_name
+ ,new_len,0);
+ if (ret == -EEXIST){
+ /* #Specification: rename / new name exist
+ If the destination name already exist, it will
+ silently be removed. EXT2 does it this way
+ and this is the spec of SUNOS. So does UMSDOS.
+
+ If the destination is an empty directory it will
+ also be removed.
+ */
+ /* #Specification: rename / new name exist / possible flaw
+ The code to handle the deletion of the target (file
+ and directory) use to be in umsdos_rename_f, surrounded
+ by proper directory locking. This was insuring that only
+ one process could achieve a rename (modification) operation
+ in the source and destination directory. This was also
+ insuring the operation was "atomic".
+
+ This has been changed because this was creating a kernel
+ stack overflow (stack is only 4k in the kernel). To avoid
+ the code doing the deletion of the target (if exist) has
+ been moved to a upper layer. umsdos_rename_f is tried
+ once and if it fails with EEXIST, the target is removed
+ and umsdos_rename_f is done again.
+
+ This makes the code cleaner and (not sure) solve a
+ deadlock problem one tester was experiencing.
+
+ The point is to mention that possibly, the semantic of
+ "rename" may be wrong. Anyone dare to check that :-)
+ Be aware that IF it is wrong, to produce the problem you
+ will need two process trying to rename a file to the
+ same target at the same time. Again, I am not sure it
+ is a problem at all.
+ */
+ /* This is not super efficient but should work */
+ new_dir->i_count++;
+ ret = UMSDOS_unlink (new_dir,new_name,new_len);
+chkstk();
+ PRINTK (("rename unlink ret %d %d -- ",ret,new_len));
+ if (ret == -EISDIR){
+ new_dir->i_count++;
+ ret = UMSDOS_rmdir (new_dir,new_name,new_len);
+chkstk();
+ PRINTK (("rename rmdir ret %d -- ",ret));
+ }
+ if (ret == 0){
+ ret = umsdos_rename_f (old_dir,old_name,old_len
+ ,new_dir,new_name,new_len,0);
+ new_dir = old_dir = NULL;
+ }
+ }
+ }
+ iput (new_dir);
+ iput (old_dir);
+ return ret;
+}
+
diff --git a/fs/umsdos/notes b/fs/umsdos/notes
new file mode 100644
index 000000000..3c47d1f4f
--- /dev/null
+++ b/fs/umsdos/notes
@@ -0,0 +1,17 @@
+This file contain idea and things I don't want to forget
+
+Possible bug in fs/read_write.c
+Function sys_readdir()
+
+ There is a call the verify_area that does not take in account
+ the count parameter. I guess it should read
+
+ error = verify_area(VERIFY_WRITE, dirent, count*sizeof (*dirent));
+
+ instead of
+
+ error = verify_area(VERIFY_WRITE, dirent, sizeof (*dirent));
+
+ Of course, now , count is always 1
+
+
diff --git a/fs/umsdos/rdir.c b/fs/umsdos/rdir.c
new file mode 100644
index 000000000..d7272ed96
--- /dev/null
+++ b/fs/umsdos/rdir.c
@@ -0,0 +1,239 @@
+/*
+ * linux/fs/umsdos/rdir.c
+ *
+ * Written 1994 by Jacques Gelinas
+ *
+ * Extended MS-DOS directory pure MS-DOS handling functions
+ * (For directory without EMD file).
+ */
+
+#include <asm/segment.h>
+
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/msdos_fs.h>
+#include <linux/errno.h>
+#include <linux/stat.h>
+#include <linux/limits.h>
+#include <linux/umsdos_fs.h>
+#include <linux/malloc.h>
+
+#define PRINTK(x)
+#define Printk(x) printk x
+
+
+extern struct inode *pseudo_root;
+
+static int UMSDOS_rreaddir (
+ struct inode *dir,
+ struct file *filp,
+ struct dirent *dirent,
+ int count)
+{
+ int ret = 0;
+ while (1){
+ ret = msdos_readdir(dir,filp,dirent,count);
+ if (ret == 5
+ && pseudo_root != NULL
+ && dir->i_sb->s_mounted == pseudo_root->i_sb->s_mounted){
+ /*
+ In pseudo root mode, we must eliminate logically
+ the directory linux from the real root.
+ */
+ char name[5];
+ memcpy_fromfs (name,dirent->d_name,5);
+ if (memcmp(name,UMSDOS_PSDROOT_NAME,UMSDOS_PSDROOT_LEN)!=0) break;
+ }else{
+ if (pseudo_root != NULL
+ && ret == 2
+ && dir == dir->i_sb->s_mounted
+ && dir == pseudo_root->i_sb->s_mounted){
+ char name[2];
+ memcpy_fromfs (name,dirent->d_name,2);
+ if (name[0] == '.' && name[1] == '.'){
+ put_fs_long (pseudo_root->i_ino,&dirent->d_ino);
+ }
+ }
+ break;
+ }
+ }
+ return ret;
+}
+
+int UMSDOS_rlookup(
+ struct inode *dir,
+ const char *name,
+ int len,
+ struct inode **result) /* Will hold inode of the file, if successful */
+{
+ int ret;
+ if (pseudo_root != NULL
+ && len == 2
+ && name[0] == '.'
+ && name[1] == '.'
+ && dir == dir->i_sb->s_mounted
+ && dir == pseudo_root->i_sb->s_mounted){
+ *result = pseudo_root;
+ pseudo_root->i_count++;
+ ret = 0;
+ /* #Specification: pseudo root / DOS/..
+ In the real root directory (c:\), the directory ..
+ is the pseudo root (c:\linux).
+ */
+ }else{
+ ret = umsdos_real_lookup (dir,name,len,result);
+ if (ret == 0){
+ struct inode *inode = *result;
+ if (inode == pseudo_root){
+ /* #Specification: pseudo root / DOS/linux
+ Even in the real root directory (c:\), the directory
+ /linux won't show
+ */
+ ret = -ENOENT;
+ iput (pseudo_root);
+ *result = NULL;
+ }else if (S_ISDIR(inode->i_mode)){
+ /* We must place the proper function table */
+ /* depending if this is a MsDOS directory or an UMSDOS directory */
+ umsdos_setup_dir_inode(inode);
+ }
+ }
+ }
+ iput (dir);
+ return ret;
+}
+
+static int UMSDOS_rrmdir (
+ struct inode *dir,
+ const char *name,
+ int len)
+{
+ /* #Specification: dual mode / rmdir in a DOS directory
+ In a DOS (not EMD in it) directory, we use a reverse strategy
+ compared with an Umsdos directory. We assume that a subdirectory
+ of a DOS directory is also a DOS directory. This is not always
+ true (umssync may be used anywhere), but make sense.
+
+ So we call msdos_rmdir() directly. If it failed with a -ENOTEMPTY
+ then we check if it is a Umsdos directory. We check if it is
+ really empty (only . .. and --linux-.--- in it). If it is true
+ we remove the EMD and do a msdos_rmdir() again.
+
+ In a Umsdos directory, we assume all subdirectory are also
+ Umsdos directory, so we check the EMD file first.
+ */
+ int ret;
+ if (umsdos_is_pseudodos(dir,name,len)){
+ /* #Specification: pseudo root / rmdir /DOS
+ The pseudo sub-directory /DOS can't be removed!
+ This is done even if the pseudo root is not a Umsdos
+ directory anymore (very unlikely), but an accident (under
+ MsDOS) is always possible.
+
+ EPERM is returned.
+ */
+ ret = -EPERM;
+ }else{
+ umsdos_lockcreate (dir);
+ dir->i_count++;
+ ret = msdos_rmdir (dir,name,len);
+ if (ret == -ENOTEMPTY){
+ struct inode *sdir;
+ dir->i_count++;
+ ret = UMSDOS_rlookup (dir,name,len,&sdir);
+ PRINTK (("rrmdir lookup %d ",ret));
+ if (ret == 0){
+ int empty;
+ if ((empty = umsdos_isempty (sdir)) != 0){
+ PRINTK (("isempty %d i_count %d ",empty,sdir->i_count));
+ if (empty == 2){
+ /*
+ Not a Umsdos directory, so the previous msdos_rmdir
+ was not lying :-)
+ */
+ ret = -ENOTEMPTY;
+ }else if (empty == 1){
+ /* We have to removed the EMD file */
+ ret = msdos_unlink(sdir,UMSDOS_EMD_FILE
+ ,UMSDOS_EMD_NAMELEN);
+ sdir = NULL;
+ if (ret == 0){
+ dir->i_count++;
+ ret = msdos_rmdir (dir,name,len);
+ }
+ }
+ }else{
+ ret = -ENOTEMPTY;
+ }
+ iput (sdir);
+ }
+ }
+ umsdos_unlockcreate (dir);
+ }
+ iput (dir);
+ return ret;
+}
+
+/* #Specification: dual mode / introduction
+ One goal of UMSDOS is to allow a practical and simple coexistence
+ between MsDOS and Linux in a single partition. Using the EMD file
+ in each directory, UMSDOS add Unix semantics and capabilities to
+ normal DOS file system. To help and simplify coexistence, here is
+ the logic related to the EMD file.
+
+ If it is missing, then the directory is managed by the MsDOS driver.
+ The names are limited to DOS limits (8.3). No links, no device special
+ and pipe and so on.
+
+ If it is there, it is the directory. If it is there but empty, then
+ the directory looks empty. The utility umssync allows synchronisation
+ of the real DOS directory and the EMD.
+
+ Whenever umssync is applied to a directory without EMD, one is
+ created on the fly. The directory is promoted to full unix semantic.
+ Of course, the ls command will show exactly the same content as before
+ the umssync session.
+
+ It is believed that the user/admin will promote directories to unix
+ semantic as needed.
+
+ The strategy to implement this is to use two function table (struct
+ inode_operations). One for true UMSDOS directory and one for directory
+ with missing EMD.
+
+ Functions related to the DOS semantic (but aware of UMSDOS) generally
+ have a "r" prefix (r for real) such as UMSDOS_rlookup, to differentiate
+ from the one with full UMSDOS semantic.
+*/
+static struct file_operations umsdos_rdir_operations = {
+ NULL, /* lseek - default */
+ UMSDOS_dir_read, /* read */
+ NULL, /* write - bad */
+ UMSDOS_rreaddir, /* readdir */
+ NULL, /* select - default */
+ UMSDOS_ioctl_dir, /* ioctl - default */
+ NULL, /* mmap */
+ NULL, /* no special open code */
+ NULL, /* no special release code */
+ NULL /* fsync */
+};
+
+struct inode_operations umsdos_rdir_inode_operations = {
+ &umsdos_rdir_operations, /* default directory file-ops */
+ msdos_create, /* create */
+ UMSDOS_rlookup, /* lookup */
+ NULL, /* link */
+ msdos_unlink, /* unlink */
+ NULL, /* symlink */
+ msdos_mkdir, /* mkdir */
+ UMSDOS_rrmdir, /* rmdir */
+ NULL, /* mknod */
+ msdos_rename, /* rename */
+ NULL, /* readlink */
+ NULL, /* follow_link */
+ NULL, /* bmap */
+ NULL, /* truncate */
+ NULL /* permission */
+};
+
+
diff --git a/fs/umsdos/symlink.c b/fs/umsdos/symlink.c
new file mode 100644
index 000000000..1b1e561c2
--- /dev/null
+++ b/fs/umsdos/symlink.c
@@ -0,0 +1,145 @@
+/*
+ * linux/fs/umsdos/file.c
+ *
+ * Written 1992 by Jacques Gelinas
+ * inspired from linux/fs/msdos/file.c Werner Almesberger
+ *
+ * Extended MS-DOS regular file handling primitives
+ */
+
+#include <asm/segment.h>
+#include <asm/system.h>
+
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/msdos_fs.h>
+#include <linux/errno.h>
+#include <linux/fcntl.h>
+#include <linux/stat.h>
+#include <linux/umsdos_fs.h>
+#include <linux/malloc.h>
+
+#define PRINTK(x)
+#define Printk(x) printk x
+/*
+ Read the data associate with the symlink.
+ Return length read in buffer or a negative error code.
+*/
+static int umsdos_readlink_x (
+ struct inode *inode,
+ char *buffer,
+ int (*msdos_read)(struct inode *, struct file *, char *, int),
+ int bufsiz)
+{
+ int ret = inode->i_size;
+ struct file filp;
+ filp.f_pos = 0;
+ filp.f_reada = 0;
+ if (ret > bufsiz) ret = bufsiz;
+ if ((*msdos_read) (inode, &filp, buffer,ret) != ret){
+ ret = -EIO;
+ }
+ return ret;
+}
+/*
+ Follow a symbolic link chain by calling open_namei recursively
+ until an inode is found.
+
+ Return 0 if ok, or a negative error code if not.
+*/
+static int UMSDOS_follow_link(
+ struct inode * dir,
+ struct inode * inode,
+ int flag,
+ int mode,
+ struct inode ** res_inode)
+{
+ int ret = -ELOOP;
+ *res_inode = NULL;
+ if (current->link_count < 5) {
+ char *path = (char*)kmalloc(PATH_MAX,GFP_KERNEL);
+ if (path == NULL){
+ ret = -ENOMEM;
+ }else{
+ if (!dir) {
+ dir = current->fs[1].root;
+ dir->i_count++;
+ }
+ if (!inode){
+ PRINTK (("symlink: inode = NULL\n"));
+ ret = -ENOENT;
+ }else if (!S_ISLNK(inode->i_mode)){
+ PRINTK (("symlink: Not ISLNK\n"));
+ *res_inode = inode;
+ inode = NULL;
+ ret = 0;
+ }else{
+ ret = umsdos_readlink_x (inode,path
+ ,umsdos_file_read_kmem,PATH_MAX-1);
+ if (ret > 0){
+ path[ret] = '\0';
+ PRINTK (("follow :%s: %d ",path,ret));
+ iput(inode);
+ inode = NULL;
+ current->link_count++;
+ ret = open_namei(path,flag,mode,res_inode,dir);
+ current->link_count--;
+ dir = NULL;
+ }else{
+ ret = -EIO;
+ }
+ }
+ kfree (path);
+ }
+ }
+ iput(inode);
+ iput(dir);
+ PRINTK (("follow_link ret %d\n",ret));
+ return ret;
+}
+
+static int UMSDOS_readlink(struct inode * inode, char * buffer, int buflen)
+{
+ int ret = -EINVAL;
+ if (S_ISLNK(inode->i_mode)) {
+ ret = umsdos_readlink_x (inode,buffer,msdos_file_read,buflen);
+ }
+ PRINTK (("readlink %d %x bufsiz %d\n",ret,inode->i_mode,buflen));
+ iput(inode);
+ return ret;
+
+}
+
+static struct file_operations umsdos_symlink_operations = {
+ NULL, /* lseek - default */
+ NULL, /* read */
+ NULL, /* write */
+ NULL, /* readdir - bad */
+ NULL, /* select - default */
+ NULL, /* ioctl - default */
+ NULL, /* mmap */
+ NULL, /* no special open is needed */
+ NULL, /* release */
+ NULL /* fsync */
+};
+
+struct inode_operations umsdos_symlink_inode_operations = {
+ &umsdos_symlink_operations, /* default file operations */
+ NULL, /* create */
+ NULL, /* lookup */
+ NULL, /* link */
+ NULL, /* unlink */
+ NULL, /* symlink */
+ NULL, /* mkdir */
+ NULL, /* rmdir */
+ NULL, /* mknod */
+ NULL, /* rename */
+ UMSDOS_readlink, /* readlink */
+ UMSDOS_follow_link, /* follow_link */
+ NULL, /* bmap */
+ NULL, /* truncate */
+ NULL /* permission */
+};
+
+
+