patch-2.1.121 linux/fs/umsdos/namei.c

Next file: linux/fs/umsdos/rdir.c
Previous file: linux/fs/umsdos/mangle.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.1.120/linux/fs/umsdos/namei.c linux/fs/umsdos/namei.c
@@ -51,36 +51,36 @@
 /*
  * Lock all other process out of this directory.
  */
+/* #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.
+ */
 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
@@ -171,11 +171,21 @@
 
 #endif
 
-static int umsdos_nevercreat (
-				     struct inode *dir,
-				     struct dentry *dentry,
-				     int errcod)
-{				/* Length of the name */
+/*
+ * Check whether we can delete from the directory.
+ */
+static int is_sticky(struct inode *dir, int uid)
+{
+	return !((dir->i_mode & S_ISVTX) == 0 || 
+		capable (CAP_FOWNER) ||
+		current->fsuid == uid ||
+		current->fsuid == dir->i_uid);
+}
+
+
+static int umsdos_nevercreat (struct inode *dir, struct dentry *dentry,
+				int errcod)
+{
 	const char *name = dentry->d_name.name;
 	int len = dentry->d_name.len;
 	int ret = 0;
@@ -210,100 +220,127 @@
 /*
  * 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.
+ * is then added to the EMD file.
  * 
  * Return the status of the operation. 0 mean success.
+ *
+ * #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
+ * exists 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 meant 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.
  */
-static int umsdos_create_any (
-				     struct inode *dir,
-				     struct dentry *dentry,	/* name/length etc */
-				     int mode,	/* Permission bit + file type ??? */
-				     int rdev,	/* major, minor or 0 for ordinary file and symlinks */
-				     char flags
-)
-{				/* Will hold the inode of the newly created file */
-
-	int ret;
+static int umsdos_create_any (struct inode *dir, struct dentry *dentry,
+				int mode, int rdev, char flags)
+{
 	struct dentry *fake;
+	struct inode *inode;
+	int ret;
+	struct umsdos_info info;
+
+if (dentry->d_inode)
+printk("umsdos_create_any: %s/%s not negative!\n",
+dentry->d_parent->d_name.name, dentry->d_name.name);
+
+Printk (("umsdos_create_any /mn/: create %.*s in dir=%lu - nevercreat=/",
+(int) dentry->d_name.len, dentry->d_name.name, dir->i_ino));
 
-	Printk (("umsdos_create_any /mn/: create %.*s in dir=%lu - nevercreat=/", (int) dentry->d_name.len, dentry->d_name.name, dir->i_ino));
 	check_dentry_path (dentry, "umsdos_create_any");
 	ret = umsdos_nevercreat (dir, dentry, -EEXIST);
-	Printk (("%d/\n", ret));
-	if (ret == 0) {
-		struct umsdos_info info;
+	if (ret) {
+Printk (("%d/\n", ret));
+		goto out;
+	}
+
+	ret = umsdos_parse (dentry->d_name.name, dentry->d_name.len, &info);
+	if (ret)
+		goto out;
+
+	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 (dentry->d_parent, &info);
+	if (ret)
+		goto out_unlock;
+
+	/* create short name dentry */
+	fake = umsdos_lookup_dentry(dentry->d_parent, info.fake.fname, 
+					info.fake.len);
+	ret = PTR_ERR(fake);
+	if (IS_ERR(fake))
+		goto out_unlock;
+
+	/* should not exist yet ... */
+	ret = -EEXIST;
+	if (fake->d_inode)
+		goto out_remove;
+
+	ret = msdos_create (dir, fake, S_IFREG | 0777);
+	if (ret)
+		goto out_remove;
+
+	inode = fake->d_inode;
+	umsdos_lookup_patch_new(fake, &info.entry, info.f_pos);
+
+Printk (("inode %p[%lu], count=%d ", inode, inode->i_ino, inode->i_count));
+Printk (("Creation OK: [dir %lu] %.*s pid=%d pos %ld\n",
+dir->i_ino, info.fake.len, info.fake.fname, current->pid, info.f_pos));
 
-		ret = umsdos_parse (dentry->d_name.name, dentry->d_name.len, &info);
+	check_dentry_path (dentry, "umsdos_create_any: BEG dentry");
+	check_dentry_path (fake, "umsdos_create_any: BEG fake");
 
-		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) {
-				inc_count (dir);
-				fake = creat_dentry (info.fake.fname, info.fake.len, NULL, dentry->d_parent);	/* create short name dentry */
-				ret = msdos_create (dir, fake, S_IFREG | 0777);
-				if (ret == 0) {
-					struct inode *inode = fake->d_inode;
-
-					umsdos_lookup_patch (dir, inode, &info.entry, info.f_pos);
-					Printk (("inode %p[%lu], count=%d ", inode, inode->i_ino, inode->i_count));
-					Printk (("Creation OK: [dir %lu] %.*s pid=%d pos %ld\n", dir->i_ino,
-						 info.fake.len, info.fake.fname, current->pid, info.f_pos));
-
-					check_dentry_path (dentry, "umsdos_create_any: BEG dentry");
-					check_dentry_path (fake, "umsdos_create_any: BEG fake");
-					d_instantiate (dentry, inode);	/* long name also gets inode info */
-					inc_count (fake->d_inode); /* we must do it, since dput(fake) will iput(our_inode) which we still need for long name (dentry) */
-					/* dput (fake);	/ * FIXME: is this OK ? we try to kill short name dentry that we don't need */
-					check_dentry_path (dentry, "umsdos_create_any: END dentry");
-					check_dentry_path (fake, "umsdos_create_any: END fake");
-
-				} 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.len, info.fake.fname, -ret, current->pid, info.f_pos);
-					}
-					umsdos_delentry (dir, &info, 0);
-				}
-				Printk (("umsdos_create %.*s ret = %d pos %ld\n",
-					 info.fake.len, info.fake.fname, ret, info.f_pos));
-			}
-			umsdos_unlockcreate (dir);
-		}
-	}
-	/* d_add(dentry,dir); /mn/ FIXME: msdos_create already did this for short name ! */
+	/*
+	 * Note! The long and short name might be the same,
+	 * so check first before doing the instantiate ...
+	 */
+	if (dentry != fake) {
+		/* long name also gets inode info */
+		inode->i_count++;
+		d_instantiate (dentry, inode);
+	}
+
+	check_dentry_path (dentry, "umsdos_create_any: END dentry");
+	check_dentry_path (fake, "umsdos_create_any: END fake");
+	goto out_dput;
+
+out_remove:
+if (ret == -EEXIST)
+printk("UMSDOS: out of sync, error [%ld], deleting %.*s %d %d pos %ld\n",
+dir->i_ino ,info.fake.len, info.fake.fname, -ret, current->pid, info.f_pos);
+	umsdos_delentry (dentry->d_parent, &info, 0);
+
+out_dput:
+Printk (("umsdos_create %.*s ret = %d pos %ld\n",
+info.fake.len, info.fake.fname, ret, info.f_pos));
+	/* N.B. any value in keeping short name dentries? */
+	if (dentry != fake)
+		d_drop(fake);
+	dput(fake);
+
+out_unlock:
+	umsdos_unlockcreate (dir);
+out:
 	return ret;
 }
 
@@ -311,11 +348,9 @@
  * 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 */
+static void umsdos_ren_init (struct umsdos_info *new_info,
+			     struct umsdos_info *old_info, int flags)
+{
 	/* != 0, this is the value of flags */
 	new_info->entry.mode = old_info->entry.mode;
 	new_info->entry.rdev = old_info->entry.rdev;
@@ -343,204 +378,193 @@
  * Rename a file (move) in the file system.
  */
  
-static int umsdos_rename_f (
-				   struct inode *old_dir,
-				   struct dentry *old_dentry,
-				   struct inode *new_dir,
-				   struct dentry *new_dentry,
-				   int flags)
-{				/* 0 == copy flags from old_name */
-				/* != 0, this is the value of flags */
+static int umsdos_rename_f (struct inode *old_dir, struct dentry *old_dentry,
+			    struct inode *new_dir, struct dentry *new_dentry,
+			    int flags)
+{
+	int old_ret, new_ret;
+	struct dentry *old, *new, *dret;
+	struct inode *oldid = NULL;
 	int ret = -EPERM;
 	struct umsdos_info old_info;
-	int old_ret = umsdos_parse (old_dentry->d_name.name, old_dentry->d_name.len, &old_info);
 	struct umsdos_info new_info;
-	int new_ret = umsdos_parse (new_dentry->d_name.name, new_dentry->d_name.len, &new_info);
+
+	old_ret = umsdos_parse (old_dentry->d_name.name,
+				old_dentry->d_name.len, &old_info);
+	if (old_ret)
+		goto out;
+	new_ret = umsdos_parse (new_dentry->d_name.name,
+				new_dentry->d_name.len, &new_info);
+	if (new_ret)
+		goto out;
 
 	check_dentry_path (old_dentry, "umsdos_rename_f OLD");
 	check_dentry_path (new_dentry, "umsdos_rename_f OLD");
 
 	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);
+Printk (("umsdos_rename %d %d ", old_ret, new_ret));
+	umsdos_lockcreate2 (old_dir, new_dir);
+	chkstk ();
+
+	ret = umsdos_findentry(old_dentry->d_parent, &old_info, 0);
+	chkstk ();
+	if (ret) {
+Printk (("ret %d ", ret));
+		goto out_unlock;
+	}
+
+	/* check sticky bit on old_dir */
+	ret = -EPERM;
+	if (is_sticky(old_dir, old_info.entry.uid)) {
+		Printk (("sticky set on old "));
+		goto out_unlock;
+	}
+
+	/* Does new_name already exist? */
+	new_ret = umsdos_findentry(new_dentry->d_parent, &new_info, 0);
+	/* if destination file exists, are we allowed to replace it ? */
+	if (new_ret == 0 && is_sticky(new_dir, new_info.entry.uid)) {
+		Printk (("sticky set on new "));
+		goto out_unlock;
+	}
+
+	Printk (("new newentry "));
+	umsdos_ren_init (&new_info, &old_info, flags);
+	ret = umsdos_newentry (new_dentry->d_parent, &new_info);
+	chkstk ();
+	if (ret) {
+Printk (("ret %d %d ", ret, new_info.fake.len));
+		goto out_unlock;
+	}
+
+	dret = umsdos_lookup_dentry(old_dentry->d_parent, old_info.fake.fname, 
+					old_info.fake.len);
+	ret = PTR_ERR(dret);
+	if (IS_ERR(dret))
+		goto out_unlock;
+#if 0
+	/* This is the same as dret */
+	oldid = dret->d_inode;
+	old = creat_dentry (old_info.fake.fname, old_info.fake.len,
+				oldid, old_dentry->d_parent);
+#endif
+	old = dret;
+	new = umsdos_lookup_dentry(new_dentry->d_parent, new_info.fake.fname, 
+					new_info.fake.len);
+	ret = PTR_ERR(new);
+	if (IS_ERR(new))
+		goto out_dput;
+
+	Printk (("msdos_rename "));
+	check_dentry_path (old, "umsdos_rename_f OLD2");
+	check_dentry_path (new, "umsdos_rename_f NEW2");
+	ret = msdos_rename (old_dir, old, new_dir, new);
+	chkstk ();
+printk("after m_rename ret %d ", ret);
+	/* dput(old); */
+	dput(new);
+
+	if (ret != 0) {
+		umsdos_delentry (new_dentry->d_parent, &new_info,
+				 S_ISDIR (new_info.entry.mode));
 		chkstk ();
-		Printk (("ret %d ", ret));
-		if (ret == 0) {
-			/* check sticky bit on old_dir */
-			if (!(old_dir->i_mode & S_ISVTX) || capable (CAP_FOWNER) ||
-			    current->fsuid == old_info.entry.uid ||
-			    current->fsuid == old_dir->i_uid) {
-				/* Does new_name already exist? */
-				Printk (("new findentry "));
-				ret = umsdos_findentry (new_dir, &new_info, 0);
-				if (ret != 0 ||		/* if destination file exists, are we allowed to replace it ? */
-				    !(new_dir->i_mode & S_ISVTX) || capable (CAP_FOWNER) ||
-				  current->fsuid == new_info.entry.uid ||
-				    current->fsuid == new_dir->i_uid) {
-					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) {
-						struct dentry *old, *new, *d_old_dir, *dret;
-						struct inode *oldid = NULL;
-
-						d_old_dir = creat_dentry ("@d_old_dir@", 11, old_dir, NULL);
-						dret = compat_umsdos_real_lookup (d_old_dir, old_info.fake.fname, old_info.fake.len);
-						if (dret) oldid = dret->d_inode;
-						old = creat_dentry (old_info.fake.fname, old_info.fake.len, oldid, old_dentry->d_parent);
-
-						new = creat_dentry (new_info.fake.fname, new_info.fake.len, NULL, new_dentry->d_parent);
-
-						Printk (("msdos_rename "));
-						inc_count (old_dir);
-						inc_count (new_dir);	/* Both inode are needed later */
-						
-						check_dentry_path (old, "umsdos_rename_f OLD2");
-						check_dentry_path (new, "umsdos_rename_f NEW2");
-						
-						ret = msdos_rename (old_dir, old, new_dir, new);
-						chkstk ();
-						Printk (("after m_rename ret %d ", ret));
-						kill_dentry (old);
-						kill_dentry (new);
-
-						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_x 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;
-
-								inc_count (new_dir);
-								PRINTK ((KERN_DEBUG "rename lookup len %d %d -- ", new_len, new_info.entry.flags));
-								ret = umsdos_lookup_x (new_dir, new_dentry, 0);
-								inode = new_dentry->d_inode;
-								chkstk ();
-								if (ret != 0) {
-									printk ("UMSDOS: partial rename for file %.*s\n"
-										,new_info.entry.name_len, 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);
-									d_move (old_dentry, new_dentry);
-									chkstk ();
-									/* iput (inode); FIXME */
-								}
-							}
-							fin_dentry (dret);
-						}
-					}
-				} else {
-					/* sticky bit set on new_dir */
-					Printk (("sticky set on new "));
-					ret = -EPERM;
-				}
-			} else {
-				/* sticky bit set on old_dir */
-				Printk (("sticky set on old "));
-				ret = -EPERM;
-			}
-		}
-		Printk ((KERN_DEBUG "umsdos_rename_f: unlocking dirs...\n"));
-		umsdos_unlockcreate (old_dir);
-		umsdos_unlockcreate (new_dir);
+		goto out_dput;
+	}
+
+	ret = umsdos_delentry (old_dentry->d_parent, &old_info,
+				 S_ISDIR (old_info.entry.mode));
+	chkstk ();
+	if (ret)
+		goto out_dput;
+#if 0
+	/*
+	 * Update f_pos so notify_change will succeed
+	 * if the file was already in use.
+	 */
+	umsdos_set_dirinfo (new_dentry->d_inode, new_dir, new_info.f_pos);
+#endif
+	if (old_dentry == dret) {
+printk("umsdos_rename_f: old dentries match -- skipping d_move\n");
+		goto out_dput;
 	}
+	d_move (old_dentry, new_dentry);
+
+out_dput:
+	dput(dret);
+
+out_unlock:
+	Printk ((KERN_DEBUG "umsdos_rename_f: unlocking dirs...\n"));
+	umsdos_unlockcreate (old_dir);
+	umsdos_unlockcreate (new_dir);
+
+out:
 	check_dentry_path (old_dentry, "umsdos_rename_f OLD3");
 	check_dentry_path (new_dentry, "umsdos_rename_f NEW3");
-
 	Printk ((" _ret=%d\n", ret));
 	return ret;
 }
 
 /*
- * Setup un Symbolic link or a (pseudo) hard link
+ * Setup a Symbolic link or a (pseudo) hard link
  * Return a negative error code or 0 if OK.
  */
-static int umsdos_symlink_x (
-				    struct inode *dir,
-				    struct dentry *dentry,
-				    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.
-	 * 
-	 * Let's go for simplicity...
-	 */
-
+/* #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.
+ * 
+ * Let's go for simplicity...
+ */
 
-	int ret;
+static int umsdos_symlink_x (struct inode *dir, struct dentry *dentry,
+			const char *symname, int mode, char flags)
+{
+	int ret, len;
+	struct file filp;
 
-	inc_count (dir);	/* We keep the inode in case we need it */
-	/* later */
 	ret = umsdos_create_any (dir, dentry, mode, 0, flags);
-	Printk (("umsdos_symlink ret %d ", ret));
-	if (ret == 0) {
-		int len = strlen (symname);
-		struct file filp;
+	if (ret) {
+Printk (("umsdos_symlink ret %d ", ret));
+		goto out;
+	}
 
-		fill_new_filp (&filp, dentry);
-		filp.f_pos = 0;
+	len = strlen (symname);
 
-		/* Make the inode acceptable to MSDOS FIXME */
-		Printk ((KERN_WARNING "umsdos_symlink_x:  /mn/ is this OK?\n"));
-		Printk ((KERN_WARNING "   symname=%s ; dentry name=%.*s (ino=%lu)\n", symname, (int) dentry->d_name.len, dentry->d_name.name, dentry->d_inode->i_ino));
-		ret = umsdos_file_write_kmem_real (&filp, symname, len);
-		/* dput(dentry); ?? where did this come from FIXME */
-		
-		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, dentry);
-			dir = NULL;
+	fill_new_filp (&filp, dentry);
+	filp.f_pos = 0;
+
+	/* Make the inode acceptable to MSDOS FIXME */
+Printk ((KERN_WARNING "   symname=%s ; dentry name=%.*s (ino=%lu)\n",
+symname, (int) dentry->d_name.len, dentry->d_name.name, dentry->d_inode->i_ino));
+	ret = umsdos_file_write_kmem_real (&filp, symname, len);
+	
+	if (ret >= 0) {
+		if (ret != len) {
+			ret = -EIO;
+			printk ("UMSDOS: "
+			     "Can't write symbolic link data\n");
+		} else {
+			ret = 0;
 		}
 	}
-	/* d_instantiate(dentry,dir);   //already done in umsdos_create_any. */
+	if (ret != 0) {
+		UMSDOS_unlink (dir, dentry);
+	}
+
+out:
 	Printk (("\n"));
 	return ret;
 }
 
 /*
- * Setup un Symbolic link.
+ * Setup a Symbolic link.
  * Return a negative error code or 0 if OK.
  */
-int UMSDOS_symlink (
-			   struct inode *dir,
-			   struct dentry *dentry,
-			   const char *symname
-)
+int UMSDOS_symlink ( struct inode *dir, struct dentry *dentry,
+		 const char *symname)
 {
 	return umsdos_symlink_x (dir, dentry, symname, S_IFLNK | 0777, 0);
 }
@@ -548,167 +572,78 @@
 /*
  * Add a link to an inode in a directory
  */
-int UMSDOS_link (
-			struct dentry *olddentry,
-			struct inode *dir,
-			struct dentry *dentry)
+int UMSDOS_link (struct dentry *olddentry, struct inode *dir,
+		 struct dentry *dentry)
 {
 	struct inode *oldinode = olddentry->d_inode;
+	struct inode *olddir;
+	char *path;
+	struct dentry *temp;
+	unsigned long buffer;
+	int ret;
+	struct umsdos_dirent entry;
 
-	/* #Specification: hard link / strategy
-	 * Hard links are difficult to implement on top of an MS-DOS FAT file
-	 * system. Unlike Unix file systems, there are no inodes. A directory
-	 * entry holds the functionality of the inode and the entry.
-	 * 
-	 * We will used the same strategy as a normal Unix file system
-	 * (with inodes) 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 an MS-DOS pseudo
-	 * inode number to record the link. For this reason, the link
-	 * will be done using hidden symbolic links. The following
-	 * scenario illustrates how it works.
-	 * 
-	 * 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;
+	ret = -EPERM;
+	if (S_ISDIR (oldinode->i_mode))
+		goto out;
 
-	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, dentry, -EPERM)) == 0) {
-		struct inode *olddir;
+	ret = umsdos_nevercreat (dir, dentry, -EPERM);
+	if (ret)
+		goto out;
 
-		ret = umsdos_get_dirowner (oldinode, &olddir);
-		Printk (("umsdos_link dir_owner = %lu -> %p [%d] ",
-			 oldinode->u.umsdos_i.i_dir_owner, olddir, olddir->i_count));
-		if (ret == 0) {
-			struct umsdos_dirent entry;
+	ret = -ENOMEM;
+	buffer = get_free_page(GFP_KERNEL);
+	if (!buffer)
+		goto out;
+
+	olddir = olddentry->d_parent->d_inode;
+	umsdos_lockcreate2 (dir, olddir);
+
+	/* get the entry for the old name */
+	ret = umsdos_dentry_to_entry(olddentry, &entry);
+	if (ret)
+		goto out_unlock;
+Printk (("umsdos_link :%.*s: ino %lu flags %d ",
+entry.name_len, entry.name ,oldinode->i_ino, entry.flags));
 
-			umsdos_lockcreate2 (dir, olddir);
-			ret = umsdos_inode2entry (olddir, oldinode, &entry);
-			if (ret == 0) {
-				Printk (("umsdos_link :%.*s: ino %lu flags %d "
-					 ,entry.name_len, 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) {
-						Printk (("olddir[%d] ", olddir->i_count));
-						ret = umsdos_rename_f (olddentry->d_inode, olddentry, dir, dentry, UMSDOS_HIDDEN);
-						if (ret == 0) {
-							char *path = (char *) kmalloc (PATH_MAX, GFP_KERNEL);
-
-							if (path == NULL) {
-								ret = -ENOMEM;
-							} else {
-								struct dentry *temp;
-
-								temp = creat_dentry (entry.name, entry.name_len, NULL, NULL);
-								Printk (("olddir[%d] ", olddir->i_count));
-								ret = umsdos_locate_path (oldinode, path);
-								Printk (("olddir[%d] ", olddir->i_count));
-								if (ret == 0) {
-									inc_count (olddir);
-									ret = umsdos_symlink_x (olddir, temp, path, S_IFREG | 0777, UMSDOS_HLINK);
-									if (ret == 0) {
-										inc_count (dir);
-										ret = umsdos_symlink_x (dir, dentry, 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) {
-							inc_count (dir);
-							ret = umsdos_symlink_x (dir, dentry, path, S_IFREG | 0777, UMSDOS_HLINK);
-						}
-						kfree (path);
-					}
-				}
-			}
-			umsdos_unlockcreate (olddir);
-			umsdos_unlockcreate (dir);
+	if (!(entry.flags & UMSDOS_HIDDEN)) {
+		struct umsdos_info info;
+
+		ret = umsdos_newhidden (olddentry->d_parent, &info);
+		if (ret)
+			goto out_unlock;
+
+		ret = umsdos_rename_f (olddentry->d_inode, olddentry, 
+					dir, dentry, UMSDOS_HIDDEN);
+		if (ret)
+			goto out_unlock;
+		path = d_path(olddentry, (char *) buffer, PAGE_SIZE);
+		if (!path)
+			goto out_unlock;
+		temp = umsdos_lookup_dentry(olddentry->d_parent, entry.name,
+						entry.name_len); 
+		if (IS_ERR(temp))
+			goto out_unlock;
+		ret = umsdos_symlink_x (olddir, temp, path, 
+					S_IFREG | 0777, UMSDOS_HLINK);
+		if (ret == 0) {
+			ret = umsdos_symlink_x (dir, dentry, path,
+					S_IFREG | 0777, UMSDOS_HLINK);
 		}
-		/* iput (olddir); FIXME */
+		dput(temp);
+		goto out_unlock;
+	}
+	path = d_path(olddentry, (char *) buffer, PAGE_SIZE);
+	if (path) {
+		ret = umsdos_symlink_x (dir, dentry, path, 
+				S_IFREG | 0777, UMSDOS_HLINK);
 	}
+
+out_unlock:
+	umsdos_unlockcreate (olddir);
+	umsdos_unlockcreate (dir);
+	free_page(buffer);
+out:
 	if (ret == 0) {
 		struct iattr newattrs;
 
@@ -716,28 +651,20 @@
 		newattrs.ia_valid = 0;
 		ret = UMSDOS_notify_change (olddentry, &newattrs);
 	}
-/*  dput (olddentry);
- * dput (dentry); FIXME.... */
-
 	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.
+ * is then added to the EMD file.
  * 
  * Return the status of the operation. 0 mean success.
  */
-int UMSDOS_create (
-			  struct inode *dir,
-			  struct dentry *dentry,
-			  int mode	/* Permission bit + file type ??? */
-)
-{				/* Will hold the inode of the newly created file */
+int UMSDOS_create (struct inode *dir, struct dentry *dentry, int mode)
+{
 	int ret;
 	Printk ((KERN_ERR "UMSDOS_create: entering\n"));
 	check_dentry_path (dentry, "UMSDOS_create START");
@@ -747,507 +674,405 @@
 }
 
 
-
 /*
  * Add a sub-directory in a directory
  */
-int UMSDOS_mkdir (
-			 struct inode *dir,
-			 struct dentry *dentry,
-			 int mode)
-{
-	int ret = umsdos_nevercreat (dir, dentry, -EEXIST);
+/* #Specification: mkdir / Directory already exist in DOS
+ * We do the same thing as for file creation.
+ * For all user it is an error.
+ */
+/* #Specification: mkdir / umsdos directory / create EMD
+ * When we created a new sub-directory in a UMSDOS
+ * directory (one with full UMSDOS semantics), we
+ * create immediately an EMD file in the new
+ * sub-directory so it inherits UMSDOS semantics.
+ */
+int UMSDOS_mkdir (struct inode *dir, struct dentry *dentry, int mode)
+{
+	struct dentry *temp;
+	struct inode *inode;
+	int ret, err;
+	struct umsdos_info info;
 
-	if (ret == 0) {
-		struct umsdos_info info;
+	ret = umsdos_nevercreat (dir, dentry, -EEXIST);
+	if (ret)
+		goto out;
 
-		ret = umsdos_parse (dentry->d_name.name, dentry->d_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) {
-				struct dentry *temp, *tdir;
+	ret = umsdos_parse (dentry->d_name.name, dentry->d_name.len, &info);
+	if (ret) {
+Printk (("umsdos_mkdir %d\n", ret));
+		goto out;
+	}
+
+	umsdos_lockcreate (dir);
+	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;
+	info.entry.nlink = 1;
+	ret = umsdos_newentry (dentry->d_parent, &info);
+	if (ret) {
+Printk (("newentry %d ", ret));
+		goto out_unlock;
+	}
+
+	/* lookup the short name dentry */
+	temp = umsdos_lookup_dentry(dentry->d_parent, info.fake.fname, 
+					info.fake.len);
+	ret = PTR_ERR(temp);
+	if (IS_ERR(temp))
+		goto out_unlock;
+
+	/* Make sure the short name doesn't exist */
+	ret = -EEXIST;
+	if (temp->d_inode) {
+printk("umsdos_mkdir: short name %s/%s exists\n",
+dentry->d_parent->d_name.name, info.fake.fname);
+		goto out_remove;
+	}
+
+	ret = msdos_mkdir (dir, temp, mode);
+	if (ret)
+		goto out_remove;
 
-				tdir = creat_dentry ("@mkd-dir@", 9, dir, NULL);
-				temp = creat_dentry (info.fake.fname, info.fake.len, NULL, tdir);
-				inc_count (dir);
-				ret = msdos_mkdir (dir, temp, 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=NULL;
-					struct dentry *d_dir, *dret;
-
-					d_dir = creat_dentry ("@d_dir5@", 7, dir, NULL);
-					dret = compat_umsdos_real_lookup (d_dir, info.fake.fname, info.fake.len);
-					if (dret) {
-						struct dentry *tdentry,	*tdsub;
-
-						subdir = dret->d_inode;
-
-						tdsub = creat_dentry ("@mkd-emd@", 9, subdir, NULL);
-						tdentry = creat_dentry (UMSDOS_EMD_FILE, UMSDOS_EMD_NAMELEN, NULL, tdsub);
-						ret = msdos_create (subdir, tdentry, S_IFREG | 0777);
-						kill_dentry (tdentry);	/* we don't want empty EMD file to be visible ! too bad kill_dentry does nothing at the moment :-)  FIXME */
-						kill_dentry (tdsub);
-						umsdos_setup_dir_inode (subdir);	/* this should setup dir so it is promoted to EMD, and EMD file is not visible inside it */
-						subdir = NULL;
-						d_instantiate (dentry, temp->d_inode);
-						/* iput (result); FIXME */
-						fin_dentry (dret);
-					}
-					if (ret < 0) {
-						printk ("UMSDOS: Can't create empty --linux-.---\n");
-					}
-					/*iput (subdir); FIXME*/
-				}
-			}
-			umsdos_unlockcreate (dir);
+	inode = temp->d_inode;
+	umsdos_lookup_patch_new(temp, &info.entry, info.f_pos);
+
+	/*
+	 * Note! The long and short name might be the same,
+	 * so check first before doing the instantiate ...
+	 */
+	if (dentry != temp) {
+		if (!dentry->d_inode) {
+			inode->i_count++;
+			d_instantiate(dentry, inode);
+		} else {
+			printk("umsdos_mkdir: not negative??\n");
 		}
+	} else {
+		printk("umsdos_mkdir: dentries match, skipping inst\n");
 	}
+
+	/* create the EMD file */
+	err = umsdos_make_emd(dentry);
+
+	/* 
+	 * set up the dir so it is promoted to EMD,
+	 * with the EMD file invisible inside it.
+	 */
+	umsdos_setup_dir(temp);
+	goto out_dput;
+
+out_remove:
+	umsdos_delentry (dentry->d_parent, &info, 1);
+
+out_dput:
+	/* kill off the short name dentry */ 
+	if (temp != dentry)
+		d_drop(temp);
+	dput(temp);
+
+out_unlock:
+	umsdos_unlockcreate (dir);
 	Printk (("umsdos_mkdir %d\n", ret));
-	/* dput (dentry); / * FIXME /mn/ */
+out:
 	return ret;
 }
 
 /*
  * Add a new device special file into a directory.
+ *
+ * #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.
  */
-int UMSDOS_mknod (
-			 struct inode *dir,
-			 struct dentry *dentry,
-			 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.
-	 */
-
+int UMSDOS_mknod (struct inode *dir, struct dentry *dentry,
+		 int mode, int rdev)
+{
 	int ret;
-	
 	check_dentry_path (dentry, "UMSDOS_mknod START");
 	ret = umsdos_create_any (dir, dentry, mode, rdev, 0);
 	check_dentry_path (dentry, "UMSDOS_mknod END");
-
-	/* dput(dentry); / * /mn/ FIXME! */
 	return ret;
 }
 
 /*
  * Remove a sub-directory.
  */
-int UMSDOS_rmdir (
-			 struct inode *dir,
-			 struct dentry *dentry)
-{
-	/* #Specification: style / iput strategy
-	 * In the UMSDOS project, I am trying to apply a single
-	 * programming style regarding inode management.  Many
-	 * entry points are receiving an inode to act on, and must
-	 * do an iput() as soon as they are finished with
-	 * the inode.
-	 * 
-	 * For simple cases, there is no problem.  When you introduce
-	 * error checking, you end up with many iput() calls in 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 must do iput() everywhere.  Here is an example
-	 * 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 sometimes 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;
+int UMSDOS_rmdir (struct inode *dir, struct dentry *dentry)
+{
+	struct dentry *temp;
+	int ret, err, empty;
+	struct umsdos_info info;
 
 	ret = umsdos_nevercreat (dir, dentry, -EPERM);
-	if (ret == 0) {
-		/* volatile - DELME: I see no reason vor volatile /mn/ */ struct inode *sdir;
+	if (ret)
+		goto out;
 
-		inc_count (dir);
-		ret = umsdos_lookup_x (dir, dentry, 0);
-		sdir = dentry->d_inode;
-		Printk (("rmdir lookup %d ", ret));
-		if (ret == 0) {
-			int empty;
-
-			umsdos_lockcreate (dir);
+#if 0	/* no need for lookup ... we have a dentry ... */
+	ret = umsdos_lookup_x (dir, dentry, 0);
+	Printk (("rmdir lookup %d ", ret));
+	if (ret != 0)
+		goto out;
+#endif
 
-			Printk ((" /mn/ rmdir: FIXME EBUSY TEST: hmmm, i_count is %d > 1 -- FAKING!\n", sdir->i_count));
-			sdir->i_count = 1;	/* /mn/ FIXME! DELME! FOR TEST ONLY ! */
+	umsdos_lockcreate (dir);
+	ret = -EBUSY;
+	if (dentry->d_count > 1) {
+		shrink_dcache_parent(dentry);
+		if (dentry->d_count > 1) {
+printk("umsdos_rmdir: %s/%s busy\n",
+dentry->d_parent->d_name.name, dentry->d_name.name);
+			goto out_unlock;
+		}
+	}
 
-			if (sdir->i_count > 1) {
-				Printk ((" /mn/ rmdir: FIXME EBUSY: hmmm, i_count is %d > 1 -- FAKING!\n", sdir->i_count));
-				ret = -EBUSY;
-			} else if ((empty = umsdos_isempty (sdir)) != 0) {
-				struct dentry *tdentry, *tedir;
-
-				tedir = creat_dentry ("@emd-rmd@", 9, dir, NULL);
-				tdentry = creat_dentry (UMSDOS_EMD_FILE, UMSDOS_EMD_NAMELEN, NULL, tedir);
-				umsdos_real_lookup (dir, tdentry);	/* fill inode part */
-				Printk (("isempty %d i_count %d ", empty, sdir->i_count));
-				/* check sticky bit */
-				if (!(dir->i_mode & S_ISVTX) || capable (CAP_FOWNER) ||
-				    current->fsuid == sdir->i_uid ||
-				    current->fsuid == dir->i_uid) {
-					if (empty == 1) {
-						/* We have to remove the EMD file */
-						ret = msdos_unlink (sdir, tdentry);
-						Printk (("UMSDOS_rmdir: unlinking empty EMD ret=%d", ret));
-						sdir = NULL;
-					}
-					/* sdir must be free before msdos_rmdir() */
-					/* iput (sdir); FIXME */
-					sdir = NULL;
-					Printk (("isempty ret %d nlink %d ", ret, dir->i_nlink));
-					if (ret == 0) {
-						struct umsdos_info info;
-						struct dentry *temp, *tdir;
-
-						inc_count (dir);
-						umsdos_parse (dentry->d_name.name, dentry->d_name.len, &info);
-						/* The findentry is there only to complete */
-						/* the mangling */
-						umsdos_findentry (dir, &info, 2);
-
-						tdir = creat_dentry ("@dir-rmd@", 9, dir, NULL);
-						temp = creat_dentry (info.fake.fname, info.fake.len, NULL, tdir);
-						umsdos_real_lookup (dir, temp);		/* fill inode part */
-
-						Printk ((KERN_ERR "  rmdir start dir=%lu, dir->sb=%p\n", dir->i_ino, dir->i_sb));	/* FIXME: /mn/ debug only */
-						Printk ((KERN_ERR "    dentry=%.*s d_count=%d ino=%lu\n", (int) temp->d_name.len, temp->d_name.name, temp->d_count, temp->d_inode->i_ino));
-						Printk ((KERN_ERR "    d_parent=%.*s d_count=%d ino=%lu\n", (int) temp->d_parent->d_name.len, temp->d_parent->d_name.name, temp->d_parent->d_count, temp->d_parent->d_inode->i_ino));
-
-						ret = msdos_rmdir (dir, temp);
-
-						Printk ((KERN_ERR "  rmdir passed %d\n", ret));		/* FIXME: /mn/ debug only */
-						Printk ((KERN_ERR "  rmdir end dir=%lu, dir->sb=%p\n", dir->i_ino, dir->i_sb));
-						Printk ((KERN_ERR "    dentry=%.*s d_count=%d ino=%p\n", (int) temp->d_name.len, temp->d_name.name, temp->d_count, temp->d_inode));
-						Printk ((KERN_ERR "    d_parent=%.*s d_count=%d ino=%lu\n", (int) temp->d_parent->d_name.len, temp->d_parent->d_name.name, temp->d_parent->d_count, temp->d_parent->d_inode->i_ino));
-
-						kill_dentry (tdir);
-						kill_dentry (temp);
-
-						if (ret == 0) {
-							ret = umsdos_delentry (dir, &info, 1);
-							d_delete (dentry);
-						}
-					}
-				} else {
-					/* sticky bit set and we don't have permission */
-					Printk (("sticky set "));
-					ret = -EPERM;
-				}
-			} else {
-				/*
-				 * The subdirectory is not empty, so leave it there
-				 */
-				ret = -ENOTEMPTY;
-			}
-			/* iput(sdir); FIXME */
-			umsdos_unlockcreate (dir);
+	/* check whether the EMD is empty */
+	empty = umsdos_isempty (dentry);
+	ret = -ENOTEMPTY;
+	if (empty == 0)
+		goto out_unlock;
+
+	/* Have to remove the EMD file? */
+	if (empty == 1) {
+		struct dentry *demd;
+		/* check sticky bit */
+		ret = -EPERM;
+		if (is_sticky(dir, dentry->d_inode->i_uid)) {
+printk("umsdos_rmdir: %s/%s is sticky\n",
+dentry->d_parent->d_name.name, dentry->d_name.name);
+			goto out_unlock;
 		}
+
+		ret = -ENOTEMPTY;
+		/* see if there's an EMD file ... */
+		demd = umsdos_get_emd_dentry(dentry);
+		if (IS_ERR(demd))
+			goto out_unlock;
+printk("umsdos_rmdir: got EMD dentry %s/%s, inode=%p\n",
+demd->d_parent->d_name.name, demd->d_name.name, demd->d_inode);
+
+		err = msdos_unlink (dentry->d_inode, demd);
+Printk (("UMSDOS_rmdir: unlinking empty EMD err=%d", err));
+		dput(demd);
+		if (err)
+			goto out_unlock;
+	}
+
+	umsdos_parse (dentry->d_name.name, dentry->d_name.len, &info);
+	/* Call findentry to complete the mangling */
+	umsdos_findentry (dentry->d_parent, &info, 2);
+	temp = umsdos_lookup_dentry(dentry->d_parent, info.fake.fname, 
+					info.fake.len);
+	ret = PTR_ERR(temp);
+	if (IS_ERR(temp))
+		goto out_unlock;
+	/*
+	 * If the short name matches the dentry, dput() it now.
+	 */
+	if (temp == dentry) {
+		dput(temp);
+printk("umsdos_rmdir: %s/%s, short matches long\n",
+dentry->d_parent->d_name.name, dentry->d_name.name);
 	}
-	/*  dput(dentry); FIXME /mn/ */
+
+	/*
+	 * Attempt to remove the msdos name.
+	 */
+	ret = msdos_rmdir (dir, temp);
+	if (ret && ret != -ENOENT)
+		goto out_dput;
+
+	/* OK so far ... remove the name from the EMD */
+	ret = umsdos_delentry (dentry->d_parent, &info, 1);
+
+out_dput:
+	/* dput() temp if we didn't do it above */
+	if (temp != dentry) {
+		d_drop(temp);
+		dput(temp);
+		if (!ret)
+			d_delete (dentry);
+printk("umsdos_rmdir: %s/%s, short=%s dput\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, info.fake.fname);
+	}
+
+out_unlock:
+	umsdos_unlockcreate (dir);
+
+out:
 	Printk (("umsdos_rmdir %d\n", ret));
 	return ret;
 }
 
 
-
 /*
  * Remove a file from the directory.
+ *
+ * #Specification: hard link / deleting a link
+ * When we delete 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.
  */
-int UMSDOS_unlink (
-			  struct inode *dir,
-			  struct dentry *dentry)
+int UMSDOS_unlink (struct inode *dir, struct dentry *dentry)
 {
+	struct dentry *temp;
+	struct inode *inode;
 	int ret;
+	struct umsdos_info info;
 
-	Printk ((" *** UMSDOS_unlink entering /mn/ *** \n"));
+Printk (("UMSDOS_unlink: entering %s/%s\n",
+dentry->d_parent->d_name.name, dentry->d_name.name));
 
 	ret = umsdos_nevercreat (dir, dentry, -EPERM);
+	if (ret)
+		goto out;
 
-	Printk (("UMSDOS_unlink /mn/: nevercreat=%d\n", ret));
+	ret = umsdos_parse (dentry->d_name.name, dentry->d_name.len, &info);
+	if (ret)
+		goto out;
+
+	umsdos_lockcreate (dir);
+	ret = umsdos_findentry (dentry->d_parent, &info, 1);
+	if (ret) {
+printk("UMSDOS_unlink: findentry returned %d\n", ret);
+		goto out_unlock;
+	}
+
+Printk (("UMSDOS_unlink %.*s ", info.fake.len, info.fake.fname));
+	ret = -EPERM;
+	/* check sticky bit */
+	if (is_sticky(dir, info.entry.uid)) {
+printk("umsdos_unlink: %s/%s is sticky\n",
+dentry->d_parent->d_name.name, dentry->d_name.name);
+		goto out_unlock;
+	}
+
+	ret = 0;
+	if (info.entry.flags & UMSDOS_HLINK) {
+printk("UMSDOS_unlink: hard link %s/%s, fake=%s\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, info.fake.fname);
+		/*
+		 * First, get the inode of the hidden link
+		 * using the standard lookup function.
+		 */
 
-	if (ret == 0) {
-		struct umsdos_info info;
+		ret = umsdos_lookup_x (dir, dentry, 0);
+		inode = dentry->d_inode;
+		if (ret)
+			goto out_unlock;
+
+		Printk (("unlink nlink = %d ", inode->i_nlink));
+		inode->i_nlink--;
+		if (inode->i_nlink == 0) {
+			struct umsdos_dirent entry;
 
-		ret = umsdos_parse (dentry->d_name.name, dentry->d_name.len, &info);
-		if (ret == 0) {
-			umsdos_lockcreate (dir);
-			ret = umsdos_findentry (dir, &info, 1);
-			Printk (("UMSDOS_unlink: findentry returned %d\n", ret));
+			ret = umsdos_dentry_to_entry (dentry, &entry);
 			if (ret == 0) {
-				Printk (("UMSDOS_unlink %.*s ", info.fake.len, info.fake.fname));
-				/* check sticky bit */
-				if (!(dir->i_mode & S_ISVTX) || capable (CAP_FOWNER) ||
-				    current->fsuid == info.entry.uid ||
-				    current->fsuid == dir->i_uid) {
-					if (info.entry.flags & UMSDOS_HLINK) {
-						/* #Specification: hard link / deleting a link
-						 * When we delete 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;
-
-						inc_count (dir);
-						ret = umsdos_lookup_x (dir, dentry, 0);
-						inode = dentry->d_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, dentry);
-								} else {
-									/* iput (hdir); FIXME */
-								}
-							} else {
-								struct iattr newattrs;
-
-								newattrs.ia_valid = 0;
-								ret = UMSDOS_notify_change (dentry, &newattrs);
-							}
-							/* iput (inode); FIXME */
-						}
-					}
-					if (ret == 0) {
-						ret = umsdos_delentry (dir, &info, 0);
-						if (ret == 0) {
-							struct dentry *temp,
-							*tdir;
-
-							Printk (("Before msdos_unlink %.*s ", info.fake.len, info.fake.fname));
-							/* inc_count (dir);	/ * FIXME /mn/ is this needed any more now that msdos_unlink locks directories using d_parent ? */
-							tdir = creat_dentry ("@dir-del@", 9, dir, NULL);	/* FIXME /mn/: do we need iget(dir->i_ino) or would dir itself suffice ? */
-							temp = creat_dentry (info.fake.fname, info.fake.len, NULL, tdir);
-							umsdos_real_lookup (dir, temp);		/* fill inode part */
-
-							ret = msdos_unlink_umsdos (dir, temp);
-							Printk (("msdos_unlink %.*s %o ret %d ", info.fake.len, info.fake.fname
-								 ,info.entry.mode, ret));
-
-							d_delete (dentry);
-
-							kill_dentry (tdir);
-							kill_dentry (temp);
-						}
-					}
-				} else {
-					/* sticky bit set and we've not got permission */
-					Printk (("Sticky bit set. "));
-					ret = -EPERM;
-				}
+				ret = UMSDOS_unlink (dentry->d_parent->d_inode,
+							dentry);
 			}
-			umsdos_unlockcreate (dir);
+		} else {
+			struct iattr newattrs;
+			newattrs.ia_valid = 0;
+			ret = UMSDOS_notify_change (dentry, &newattrs);
 		}
 	}
-	/* dput(dentry); FIXME: shouldn't this be done in msdos_unlink ? */
+	if (ret)
+		goto out_unlock;
+
+	/* get the short name dentry */
+	temp = umsdos_lookup_dentry(dentry->d_parent, info.fake.fname, 
+					info.fake.len);
+	if (IS_ERR(temp))
+		goto out_unlock;
+
+	/*
+	 * If the short name matches the long,
+	 * dput() it now so it's not busy.
+	 */
+	if (temp == dentry) {
+printk("UMSDOS_unlink: %s/%s, short matches long\n",
+dentry->d_parent->d_name.name, dentry->d_name.name);
+		dput(temp);
+	}
+
+	ret = umsdos_delentry (dentry->d_parent, &info, 0);
+	if (ret && ret != -ENOENT)
+		goto out_dput;
+
+printk("UMSDOS: Before msdos_unlink %.*s ",
+info.fake.len, info.fake.fname);
+	ret = msdos_unlink_umsdos (dir, temp);
+
+Printk (("msdos_unlink %.*s %o ret %d ",
+info.fake.len, info.fake.fname ,info.entry.mode, ret));
+
+	/* dput() temp if we didn't do it above */
+out_dput:
+	if (temp != dentry) {
+		d_drop(temp);
+		dput(temp);
+		if (!ret)
+			d_delete (dentry);
+printk("umsdos_unlink: %s/%s, short=%s dput\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, info.fake.fname);
+	}
+
+out_unlock:
+	umsdos_unlockcreate (dir);
+out:
 	Printk (("umsdos_unlink %d\n", ret));
 	return ret;
 }
 
 
-
 /*
  * Rename (move) a file.
  */
-int UMSDOS_rename (
-			  struct inode *old_dir,
-			  struct dentry *old_dentry,
-			  struct inode *new_dir,
-			  struct dentry *new_dentry)
-{
-	/* #Specification: weakness / rename
-	 * There is a case where UMSDOS rename has a different behavior
-	 * than a 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 may is in Linux VFS driver for msdos.
-	 * I believe this is not a bug but a design feature, because
-	 * an inode number represents 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_dentry, -EEXIST);
+int UMSDOS_rename (struct inode *old_dir, struct dentry *old_dentry,
+		   struct inode *new_dir, struct dentry *new_dentry)
+{
+	int ret;
 
-	if (ret == 0) {
-		/* umsdos_rename_f eat the inode and we may need those later */
-		inc_count (old_dir);
-		inc_count (new_dir);
-		ret = umsdos_rename_f (old_dir, old_dentry, new_dir, new_dentry, 0);
-		if (ret == -EEXIST) {
-			/* #Specification: rename / new name exist
-			 * If the destination name already exists, 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 ensuring that only
-			 * one process could achieve a rename (modification) operation
-			 * in the source and destination directory.  This was also
-			 * ensuring the operation was "atomic".
-			 * 
-			 * This has been changed because this was creating a
-			 * stack overflow (the stack is only 4 kB) 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 may 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 terribly efficient but should work. */
-			inc_count (new_dir);
-			ret = UMSDOS_unlink (new_dir, new_dentry);
-			chkstk ();
-			Printk (("rename unlink ret %d -- ", ret));
-			if (ret == -EISDIR) {
-				inc_count (new_dir);
-				ret = UMSDOS_rmdir (new_dir, new_dentry);
-				chkstk ();
-				Printk (("rename rmdir ret %d -- ", ret));
-			}
-			if (ret == 0) {
-				ret = umsdos_rename_f (old_dir, old_dentry,
-						 new_dir, new_dentry, 0);
-				new_dir = old_dir = NULL;
-			}
-		}
+	ret = umsdos_nevercreat (new_dir, new_dentry, -EEXIST);
+	if (ret)
+		goto out;
+
+	ret = umsdos_rename_f(old_dir, old_dentry, new_dir, new_dentry, 0);
+	if (ret != -EEXIST)
+		goto out;
+
+	/* This is not terribly efficient but should work. */
+	ret = UMSDOS_unlink (new_dir, new_dentry);
+	chkstk ();
+	Printk (("rename unlink ret %d -- ", ret));
+	if (ret == -EISDIR) {
+		ret = UMSDOS_rmdir (new_dir, new_dentry);
+		chkstk ();
+		Printk (("rename rmdir ret %d -- ", ret));
 	}
-	/*
-	 * dput (new_dentry);
-	 * dput (old_dentry); FIXME /mn/ */
+	if (ret)
+		goto out;
+
+	/* this time the rename should work ... */
+	ret = umsdos_rename_f (old_dir, old_dentry, new_dir, new_dentry, 0);
+
+out:
 	return ret;
 }

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov