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

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

diff -u --recursive --new-file v2.1.120/linux/fs/umsdos/dir.c linux/fs/umsdos/dir.c
@@ -23,65 +23,41 @@
 extern struct inode *pseudo_root;
 
 
-/* P.T.Waltenberg
- * I've retained this to facilitate the lookup of some of the hard-wired files/directories UMSDOS
- * uses. It's easier to do once than hack all the other instances. Probably safer as well
- */
-   
-/* 
- * d_dir is directory to search for file, name&len define the file.
- * compat_umsdos_real_lookup returns dentry pointing to wanted file,
- * or NULL if not found. Calling code is respondible to call fin_dentry (result) 
- */
-
-struct dentry *compat_umsdos_real_lookup (struct dentry *d_dir, const char *name, int len)
-{
-	int rv;
-	struct dentry *dentry;
-
-	PRINTK ((KERN_DEBUG "compat_umsdos_real_lookup !!CNTx!!: start\n"));
-	
-	check_dentry_path (d_dir, "compat_umsdos_real_lookup B4 dir");
-	dentry = creat_dentry (name, len, NULL, d_dir);
-
-/*	check_dentry_path (dentry, "compat_umsdos_real_lookup B4");*/
-
-	rv = umsdos_real_lookup (d_dir->d_inode, dentry);
-	check_dentry_path (dentry, "compat_umsdos_real_lookup END");
-
-	if (rv) {
-		printk (KERN_WARNING "compat_umsdos_real_lookup failed with %d\n", rv);
-		return NULL;
-	}
-
 
-	PRINTK ((KERN_DEBUG "compat_umsdos_real_lookup !!CNTx!!: end\n"));
-
-	if (dentry->d_inode) return dentry;	/* all OK */
-	
-	/* otherwise, we have a negative dentry. return NULL */
-	Printk ((KERN_DEBUG "compat_umsdos_real_lookup: negative dentry - file not found\n"));
-	fin_dentry (dentry);
-	return NULL;
-}
-
-
-int compat_msdos_create (struct inode *dir, const char *name, int len, int mode, struct inode **inode)
+/*
+ * This needs to have the parent dentry passed to it.
+ * N.B. Try to get rid of this soon!
+ */
+int compat_msdos_create (struct inode *dir, const char *name, int len, 
+			int mode, struct inode **inode)
 {
-	int rv;
+	int ret;
 	struct dentry *dentry, *d_dir;
 
 	check_inode (dir);
+	ret = -ENOMEM;
 	d_dir = geti_dentry (dir);
+	if (!d_dir) {
+printk(KERN_ERR "compat_msdos_create: flaky i_dentry didn't work\n");
+		goto out;
+	}
+	dget(d_dir);
 	dentry = creat_dentry (name, len, NULL, d_dir);
+	dput(d_dir);
+	if (!dentry)
+		goto out;
+
 	check_dentry_path (dentry, "compat_msdos_create START");
-	rv = msdos_create (dir, dentry, mode);
+	ret = msdos_create (dir, dentry, mode);
 	check_dentry_path (dentry, "compat_msdos_create END");
+	if (ret)
+		goto out;
 	if (inode != NULL)
 		*inode = dentry->d_inode;
 
 	check_inode (dir);
-	return rv;
+out:
+	return ret;
 }
 
 
@@ -118,7 +94,8 @@
 	struct UMSDOS_DIR_ONCE *d = (struct UMSDOS_DIR_ONCE *) buf;
 
 	if (d->count == 0) {
-		PRINTK ((KERN_DEBUG "dir_once :%.*s: offset %Ld\n", len, name, offset));
+		PRINTK ((KERN_DEBUG "dir_once :%.*s: offset %Ld\n", 
+			len, name, offset));
 		ret = d->filldir (d->dirbuf, name, len, offset, ino);
 		d->stop = ret < 0;
 		d->count = 1;
@@ -130,32 +107,29 @@
 /*
  * Read count directory entries from directory filp
  * Return a negative value from linux/errno.h.
- * Return > 0 if success (The amount of byte written by filldir).
+ * Return > 0 if success (the number of bytes written by filldir).
  * 
  * 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 */
-				    void *dirbuf,	/* Will hold count directory entry */
-							/* but filled by the filldir function */
-				    int internal_read,	/* Called for internal purpose */
-				    struct umsdos_dirent *u_entry,	/* Optional umsdos entry */
-				    int follow_hlink,
-				    filldir_t filldir)
+static int umsdos_readdir_x (struct inode *dir, struct file *filp,
+				void *dirbuf, int internal_read,
+				struct umsdos_dirent *u_entry,
+				int follow_hlink, filldir_t filldir)
 {
+	struct dentry *demd;
+	off_t start_fpos;
 	int ret = 0;
-	struct dentry *old_dent;
+	struct file new_filp;
 
 	umsdos_startlookup (dir);
-	if (filp->f_pos == UMSDOS_SPECIAL_DIRFPOS
-	    && pseudo_root
-	    && dir == pseudo_root
-	    && !internal_read) {
 
-		Printk (("umsdos_readdir_x: what UMSDOS_SPECIAL_DIRFPOS /mn/?\n"));
+	if (filp->f_pos == UMSDOS_SPECIAL_DIRFPOS &&
+	    dir == pseudo_root && !internal_read) {
+
+Printk (("umsdos_readdir_x: what UMSDOS_SPECIAL_DIRFPOS /mn/?\n"));
 		/*
 		 * We don't need to simulate this pseudo directory
 		 * when umsdos_readdir_x is called for internal operation
@@ -166,55 +140,16 @@
 		 * linux root), it simulate a directory /DOS which points to
 		 * the real root of the file system.
 		 */
-		if (filldir (dirbuf, "DOS", 3, UMSDOS_SPECIAL_DIRFPOS, UMSDOS_ROOT_INO) == 0) {
+		if (filldir (dirbuf, "DOS", 3, 
+				UMSDOS_SPECIAL_DIRFPOS, UMSDOS_ROOT_INO) == 0) {
 			filp->f_pos++;
 		}
-	} else if (filp->f_pos < 2 || (dir->i_ino != UMSDOS_ROOT_INO && filp->f_pos == 32)) {
-	
-	/* FIXME: that was in 2.0.x: else if (filp->f_pos < 2 || (dir != dir->i_sb->s_mounted && filp->f_pos == 32))
-	 * I'm probably screwing up pseudo-root and stuff with this. It needs proper fix.
-	 */
-	                
+		goto out_end;
+	}
+
+	if (filp->f_pos < 2 || 
+	    (dir->i_ino != UMSDOS_ROOT_INO && filp->f_pos == 32)) {
 	
-		/* #Specification: readdir / . and ..
-		 * The msdos filesystem manages 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.
-		 * 
-		 * Now (linux 1.3), umsdos_readdir can read more than one
-		 * entry even if we limit (umsdos_dir_once) to only one:
-		 * It skips over hidden file. So we switch to
-		 * UMSDOS_SPECIAL_DIRFPOS as soon as we have read successfully
-		 * the .. entry.
-		 */
 		int last_f_pos = filp->f_pos;
 		struct UMSDOS_DIR_ONCE bufk;
 
@@ -229,112 +164,113 @@
 			filp->f_pos = UMSDOS_SPECIAL_DIRFPOS;
 		if (u_entry != NULL)
 			u_entry->flags = 0;
-	} else {
-		Printk (("umsdos_readdir_x: normal file /mn/?\n"));
-		old_dent = filp->f_dentry;	/* save dentry of directory */
+		goto out_end;
+	}
 
-		if (fix_emd_filp (filp) == 0) {
-			off_t start_fpos = filp->f_pos;
+	Printk (("umsdos_readdir_x: normal file /mn/?\n"));
 
-			Printk (("umsdos_readdir_x: emd_dir->i_ino=%ld\n", filp->f_dentry->d_inode->i_ino));
-			if (filp->f_pos <= UMSDOS_SPECIAL_DIRFPOS + 1)
-				filp->f_pos = 0;
-			Printk (("f_pos %Ld i_size %ld\n", filp->f_pos, filp->f_dentry->d_inode->i_size));
-			ret = 0;
-			while (filp->f_pos < filp->f_dentry->d_inode->i_size) {
-				struct umsdos_dirent entry;
-				off_t cur_f_pos = filp->f_pos;
-
-				if (umsdos_emd_dir_readentry (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 dentry *d_dir, *dret;
-
-					umsdos_parse (entry.name, entry.name_len, &info);
-					info.f_pos = cur_f_pos;
-					umsdos_manglename (&info);
-					d_dir = geti_dentry (dir);
-					dret = compat_umsdos_real_lookup (d_dir, info.fake.fname, info.fake.len);
-
-					Printk (("Looking for inode of %s dret %p flags %d\n", info.fake.fname, dret, entry.flags));
-					if (dret && !IS_ERR(dret)
-					   && (entry.flags & UMSDOS_HLINK)
-					   && follow_hlink) {
-						dret = umsdos_solve_hlink (dret);
-					}
+	/* get the EMD dentry */
+	demd = umsdos_get_emd_dentry(filp->f_dentry);
+	ret = PTR_ERR(demd);
+	if (IS_ERR(demd))
+		goto out_end;
+	ret = 0;
+	if (!demd->d_inode)
+		goto out_dput;
+
+	/* set up our private filp ... */
+	fill_new_filp(&new_filp, demd);
+	new_filp.f_pos = filp->f_pos;
+	start_fpos = filp->f_pos;
+
+	if (new_filp.f_pos <= UMSDOS_SPECIAL_DIRFPOS + 1)
+		new_filp.f_pos = 0;
+Printk (("f_pos %Ld i_size %ld\n", new_filp.f_pos, demd->d_inode->i_size));
+	ret = 0;
+	while (new_filp.f_pos < demd->d_inode->i_size) {
+		off_t cur_f_pos = new_filp.f_pos;
+		struct umsdos_info info;
+		struct dentry *dret;
+		struct umsdos_dirent entry;
+
+		ret = -EIO;
+		if (umsdos_emd_dir_readentry (&new_filp, &entry) != 0)
+			break;
+
+		if (entry.name_len == 0)
+			goto remove_name;
+
+		umsdos_parse (entry.name, entry.name_len, &info);
+		info.f_pos = cur_f_pos;
+		umsdos_manglename (&info);
+		dret = umsdos_lookup_dentry(filp->f_dentry, info.fake.fname,
+						 info.fake.len);
+		ret = PTR_ERR(dret);
+		if (IS_ERR(dret))
+			break;
+
+Printk (("Looking for inode of %s/%s, flags=%x\n",
+dret->d_parent->d_name.name, info.fake.fname, entry.flags));
+		if ((entry.flags & UMSDOS_HLINK) && follow_hlink) {
+			dret = umsdos_solve_hlink (dret);
+			ret = PTR_ERR(dret);
+			if (IS_ERR(dret))
+				break;
+		}
 					
-					if (dret && !IS_ERR(dret)) {
-						/* #Specification:  pseudo root / reading real root
-						 * The pseudo root (/linux) is logically
-						 * erased from the real root.  This means that
-						 * ls /DOS, won't show "linux". This avoids
-						 * infinite recursion (/DOS/linux/DOS/linux/...) while
-						 * walking the file system.
-						 */
-						if (dret->d_inode != pseudo_root
-						    && (internal_read || !(entry.flags & UMSDOS_HIDDEN))) {
-							Printk ((KERN_DEBUG "filldir now\n"));
-							if (filldir (dirbuf, entry.name, entry.name_len, cur_f_pos, dret->d_inode->i_ino) < 0) {
-								filp->f_pos = cur_f_pos;
-							}
-							Printk (("Found ino %ld ", dret->d_inode->i_ino));
-							if (u_entry != NULL)
-								*u_entry = entry;
-							fin_dentry (dret);
-							break;
-						}
-						fin_dentry (dret);
-					} else {
-						/* #Specification:  umsdos / readdir / not in MSDOS
-						 * During a readdir operation, if the file is not
-						 * in the MS-DOS directory any more, the entry is
-						 * removed from the EMD file silently.
-						 */
-						Printk (("'Silently' removing EMD for file\n"));
-						ret = umsdos_writeentry (dir, filp->f_dentry->d_inode, &info, 1);
-						if (ret != 0) {
-							break;
-						}
-					}
-				}
+		/* #Specification:  pseudo root / reading real root
+		 * The pseudo root (/linux) is logically
+		 * erased from the real root.  This means that
+		 * ls /DOS, won't show "linux". This avoids
+		 * infinite recursion (/DOS/linux/DOS/linux/...) while
+		 * walking the file system.
+		 */
+		if (dret->d_inode != pseudo_root &&
+		    (internal_read || !(entry.flags & UMSDOS_HIDDEN))) {
+			Printk ((KERN_DEBUG "filldir now\n"));
+			if (filldir (dirbuf, entry.name, entry.name_len,
+				 cur_f_pos, dret->d_inode->i_ino) < 0) {
+				new_filp.f_pos = cur_f_pos;
 			}
-			/*
-			 * If the fillbuf has failed, f_pos is back to 0.
-			 * To avoid getting back into the . and .. state
-			 * (see comments at the beginning), we put back
-			 * the special offset.
-			 */
-			if (filp->f_pos == 0)
-				filp->f_pos = start_fpos;
-			Printk ((KERN_DEBUG "dir.c:  putting emd_dir %lu with i_count=%d\n", filp->f_dentry->d_inode->i_ino, filp->f_dentry->d_inode->i_count));
-
-			fin_dentry (filp->f_dentry);
-			filp->f_dentry = old_dent;	/* restore dentry of directory */
+Printk (("Found %s/%s(%ld)\n",
+dret->d_parent->d_name.name, dret->d_name.name, dret->d_inode->i_ino));
+			if (u_entry != NULL)
+				*u_entry = entry;
+			dput(dret);
+			break;
 		}
+		dput(dret);
+		continue;
+
+	remove_name:
+		/* #Specification:  umsdos / readdir / not in MSDOS
+		 * During a readdir operation, if the file is not
+		 * in the MS-DOS directory any more, the entry is
+		 * removed from the EMD file silently.
+		 */
+		Printk (("'Silently' removing EMD for file\n"));
+		ret = umsdos_delentry(filp->f_dentry, &info, 1);
+		if (ret)
+			break;
+		continue;
 	}
+	/*
+	 * If the fillbuf has failed, f_pos is back to 0.
+	 * To avoid getting back into the . and .. state
+	 * (see comments at the beginning), we put back
+	 * the special offset.
+	 */
+	filp->f_pos = new_filp.f_pos;
+	if (filp->f_pos == 0)
+		filp->f_pos = start_fpos;
+out_dput:
+	dput(demd);
+
+out_end:
 	umsdos_endlookup (dir);
 	
-	Printk ((KERN_DEBUG "read dir %p pos %Ld ret %d\n", dir, filp->f_pos, ret));
+	Printk ((KERN_DEBUG "read dir %p pos %Ld ret %d\n",
+		dir, filp->f_pos, ret));
 	return ret;
 }
 
@@ -345,13 +281,10 @@
  * Return 0 or positive if successful.
  */
  
-static int UMSDOS_readdir (	  struct file *filp,	/* Point to a directory which is read. */
-				  void *dirbuf,		/* Will hold directory entries  */
-				  filldir_t filldir)
+static int UMSDOS_readdir (struct file *filp, void *dirbuf, filldir_t filldir)
 {
 	struct inode *dir = filp->f_dentry->d_inode;
-	int ret = 0;
-	int count = 0;
+	int ret = 0, count = 0;
 	struct UMSDOS_DIR_ONCE bufk;
 
 	bufk.dirbuf = dirbuf;
@@ -363,148 +296,112 @@
 		struct umsdos_dirent entry;
 
 		bufk.count = 0;
-		PRINTK (("UMSDOS_readdir: calling _x (%p,%p,%p,%d,%p,%d,%p)\n", dir, filp, &bufk, 0, &entry, 1, umsdos_dir_once));
-		ret = umsdos_readdir_x (dir, filp, &bufk, 0, &entry, 1, umsdos_dir_once);
+		PRINTK (("UMSDOS_readdir: calling _x (%p,%p,%p,%d,%p,%d,%p)\n",
+			dir, filp, &bufk, 0, &entry, 1, umsdos_dir_once));
+		ret = umsdos_readdir_x (dir, filp, &bufk, 0, &entry, 1, 
+					umsdos_dir_once);
 		if (bufk.count == 0)
 			break;
 		count += bufk.count;
 	}
-	/* FIXME: do we first need to deallocate old dentry ? look/check. see also all other instances of fix_emd_filp */
-	Printk (("UMSDOS_readdir out %d count %d pos %Ld\n", ret, count, filp->f_pos));
+	Printk (("UMSDOS_readdir out %d count %d pos %Ld\n", 
+		ret, count, filp->f_pos));
 	return count ? : 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 modifies the state of a dir inode.  It decides
-	 * whether the dir is a UMSDOS or DOS directory.  This is done
-	 * deeper in umsdos_patch_inode() called at the end of this function.
-	 * 
-	 * Because it is does disk access, umsdos_patch_inode() may block.
-	 * At the same time, another process may get here to initialise
-	 * the same directory inode. There are three 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 locked the inode, so we try
-	 * to lock it and check right afterward check whether
-	 * 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 memory to 4 MB 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.
+ *
+ * This function modifies the state of a dir inode.  It decides
+ * whether the dir is a UMSDOS or DOS directory.  This is done
+ * deeper in umsdos_patch_inode() called at the end of this function.
+ * 
+ * Because it is does disk access, umsdos_patch_inode() may block.
+ * At the same time, another process may get here to initialise
+ * the same directory inode. There are three 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 locked the inode, so we try
+ * to lock it and check right afterward check whether
+ * 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 memory to 4 MB 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.
+ */
+
+void umsdos_lookup_patch (struct inode *dir, struct inode *inode,
+			 struct umsdos_dirent *entry, off_t emd_pos)
+{
+	if (inode->i_sb != dir->i_sb)
+		goto out;
+	if (umsdos_isinit (inode))
+		goto out;
+
+	if (S_ISDIR (inode->i_mode))
+		umsdos_lockcreate (inode);
+	if (umsdos_isinit (inode))
+		goto out_unlock;
+
+	if (S_ISREG (entry->mode))
+		entry->mtime = inode->i_mtime;
+	inode->i_mode = entry->mode;
+	inode->i_rdev = to_kdev_t (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;
+
+	MSDOS_I (inode)->i_binary = 1;
+	/* #Specification: umsdos / i_nlink
+	 * The nlink field of an inode is maintained by the MSDOS file system
+	 * for directory and by UMSDOS for other files.  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 links
+	 * so all file carry nlink==1.  UMSDOS use some info in the
+	 * EMD file to plug the correct value.
 	 */
-
-	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 MS-DOS for mtime for directories
-			 * because the MS-DOS date on a directory is its
-			 * creation time (strange MSDOS behavior) which
-			 * corresponds to none of the three Unix time stamps.
-			 */
-			if (S_ISREG (entry->mode))
-				entry->mtime = inode->i_mtime;
-			inode->i_mode = entry->mode;
-			inode->i_rdev = to_kdev_t (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 / conversion mode
-			 * The msdos filesystem can do some inline conversion
-			 * of the data of a file.  It can translate silently
-			 * from the MS-DOS text file format to the Unix one
-			 * (CRLF -> LF) while reading, and the reverse
-			 * while writing. This is activated using the mount
-			 * option conv=....
-			 * 
-			 * This is not useful for Linux files in a promoted
-			 * directory.  It can even be harmful.  For this
-			 * reason, the binary (no conversion) mode is
-			 * always activated.
-			 */
-			/* #Specification: umsdos / conversion mode / todo
-			 * A flag could be added to file and directories
-			 * forcing an automatic conversion mode (as
-			 * done with the msdos filesystem).
-			 * 
-			 * This flag could be setup on a directory basis
-			 * (instead of file) and all files in it would
-			 * logically inherit it.  If the conversion mode
-			 * is active (conv=) then the i_binary flag would
-			 * be left untouched in those directories.
-			 * 
-			 * It was proposed that the sticky bit be used to set
-			 * this.  A problem with that is that new files would
-			 * be written incorrectly.  The other problem is that
-			 * the sticky bit has a meaning for directories. So
-			 * another bit should be used (there is some space
-			 * in the EMD file for it) and a special utility
-			 * would be used to assign the flag to a directory).
-			 * I don't think it is useful to assign this flag
-			 * on a single file.
-			 */
-
-			MSDOS_I (inode)->i_binary = 1;
-			/* #Specification: umsdos / i_nlink
-			 * The nlink field of an inode is maintained by the MSDOS file system
-			 * for directory and by UMSDOS for other files.  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 links
-			 * 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 (KERN_ERR "UMSDOS:  lookup_patch entry->nlink < 1 ???\n");
-				}
-			}
-			umsdos_patch_inode (inode, dir, emd_pos);
+	if (!S_ISDIR (entry->mode)) {
+		if (entry->nlink > 0) {
+			inode->i_nlink = entry->nlink;
+		} else {
+			printk (KERN_ERR 
+				"UMSDOS:  lookup_patch entry->nlink < 1 ???\n");
 		}
-		if (S_ISDIR (inode->i_mode))
-			umsdos_unlockcreate (inode);
-		if (inode->u.umsdos_i.i_emd_owner == 0)
-			printk (KERN_WARNING "UMSDOS:  emd_owner still 0?\n");
 	}
+	umsdos_patch_inode (inode, dir, emd_pos);
+
+out_unlock:
+	if (S_ISDIR (inode->i_mode))
+		umsdos_unlockcreate (inode);
+	if (inode->u.umsdos_i.i_emd_owner == 0)
+		printk (KERN_WARNING "UMSDOS:  emd_owner still 0?\n");
+out:
+	return;
 }
 
 
+/*
+ * The preferred interface to the above routine ...
+ */
+void umsdos_lookup_patch_new(struct dentry *dentry, struct umsdos_dirent *entry,
+				off_t emd_pos)
+{
+	umsdos_lookup_patch(dentry->d_parent->d_inode, dentry->d_inode, entry,
+				emd_pos);
+}
+
 
 struct UMSDOS_DIRENT_K {
 	off_t f_pos;		/* will hold the offset of the entry in EMD */
@@ -557,18 +454,21 @@
 
 
 /*
- * Locate entry of an inode in a directory.
+ * Locate the directory entry for a dentry in its parent 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 umsdos_dentry_to_entry(struct dentry *dentry, struct umsdos_dirent *entry)
 {
-	int ret = -ENOENT;
+	struct dentry *parent = dentry->d_parent;
+	struct inode *inode = dentry->d_inode;
+	int ret = -ENOENT, err;
+	struct file filp;
+	struct UMSDOS_DIR_SEARCH bufsrch;
+	struct UMSDOS_DIRENT_K bufk;
 
 	if (pseudo_root && inode == pseudo_root) {
 		/*
@@ -578,155 +478,115 @@
 		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);
+		goto out;
+	}
 
-		/* iput (emddir); / * FIXME? */
-		if (emddir == NULL) {
-			/* This is a DOS directory. */
-			struct UMSDOS_DIR_SEARCH bufk;
-			struct file filp;
-			struct dentry *i2e;
-			
-			i2e = creat_dentry ("@i2e.nul@", 9, dir, NULL);
-
-			fill_new_filp (&filp, i2e);
-
-			Printk ((KERN_ERR "umsdos_inode2entry emddir==NULL: WARNING: Known filp problem. segfaulting :) fixed ?/mn/\n"));
-			filp.f_reada = 1;
-			filp.f_pos = 0;
-			bufk.entry = entry;
-			bufk.search_ino = inode->i_ino;
-			fat_readdir (&filp, &bufk, umsdos_dir_search);
-			if (bufk.found) {
-				ret = 0;
-				inode->u.umsdos_i.i_dir_owner = dir->i_ino;
-				inode->u.umsdos_i.i_emd_owner = 0;
-				umsdos_setup_dir_inode (inode);
-			}
-		} else {
-			/* skip . and .. see umsdos_readdir_x() */
-			struct file filp;
-			struct dentry *i2e;
-
-			i2e = creat_dentry ("@i2e.nn@", 8, dir, NULL);
-			fill_new_filp (&filp, i2e);
-
-			filp.f_reada = 1;
-			filp.f_pos = UMSDOS_SPECIAL_DIRFPOS;
-			Printk ((KERN_ERR "umsdos_inode2entry skip...: WARNING: Known filp problem. segfaulting :) fixed ?/mn/\n"));
-			while (1) {
-				struct UMSDOS_DIRENT_K bufk;
-				struct dentry *old_dent;
-
-				old_dent = filp.f_dentry;
-				if (umsdos_readdir_x (dir, &filp, &bufk, 1, entry, 0, umsdos_filldir_k) < 0) {
-					printk ("UMSDOS: can't locate inode %ld in EMD file???\n", inode->i_ino);
-					break;
-				} else if (bufk.ino == inode->i_ino) {
-					ret = 0;
-					filp.f_dentry = old_dent;
-					umsdos_lookup_patch (dir, inode, entry, bufk.f_pos);
-					break;
-				}
-			}
+	/* initialize the file */
+	fill_new_filp (&filp, parent);
+
+	if (!umsdos_have_emd(parent)) {
+		/* This is a DOS directory. */
+		filp.f_pos = 0;
+		bufsrch.entry = entry;
+		bufsrch.search_ino = inode->i_ino;
+		fat_readdir (&filp, &bufsrch, umsdos_dir_search);
+		if (bufsrch.found) {
+			ret = 0;
+			inode->u.umsdos_i.i_dir_owner = parent->d_inode->i_ino;
+			inode->u.umsdos_i.i_emd_owner = 0;
+if (!S_ISDIR(inode->i_mode))
+printk("UMSDOS: %s/%s not a directory!\n",
+dentry->d_parent->d_name.name, dentry->d_name.name);
+			/* N.B. why call this? not always a dir ... */
+			umsdos_setup_dir(dentry);
+		}
+		goto out;
+	}
+
+	/* skip . and .. see umsdos_readdir_x() */
+	filp.f_pos = UMSDOS_SPECIAL_DIRFPOS;
+	while (1) {
+		err = umsdos_readdir_x (parent->d_inode, &filp, &bufk, 1, 
+				entry, 0, umsdos_filldir_k);
+		if (err < 0) { 
+			printk ("UMSDOS: can't locate inode %ld in EMD??\n",
+				inode->i_ino);
+			break;
+		}
+		if (bufk.ino == inode->i_ino) {
+			ret = 0;
+			umsdos_lookup_patch_new(dentry, entry, bufk.f_pos);
+			break;
 		}
 	}
+out:
 	return ret;
 }
 
-
 /*
- * Locate the parent of a directory and the info on that directory
- * Return 0 or a negative error code.
+ * Deprecated. Try to get rid of this soon!
  */
-
-static int umsdos_locate_ancestor (	  struct inode *dir,
-					  struct inode **result,
-					  struct umsdos_dirent *entry)
+int umsdos_inode2entry (struct inode *dir, struct inode *inode,
+			       struct umsdos_dirent *entry)
 {
-	int ret=-99;
-	struct dentry *dret, *d_dir = creat_dentry ("@d_dir2@", 7, dir, NULL);
-
-	umsdos_patch_inode (dir, NULL, 0);
-	/* FIXME */
-	dret = compat_umsdos_real_lookup (d_dir, "..", 2);
-	Printk (("result %p %p ", dret, *result));
-	if (dret) {
-		struct inode *adir;
-		*result = dret->d_inode;
-		adir = *result;
+	int ret = -ENOENT;
+	struct inode *emddir;
+	struct dentry *i2e;
+	struct file filp;
+	struct UMSDOS_DIR_SEARCH bufsrch;
+	struct UMSDOS_DIRENT_K bufk;
 
-		ret = umsdos_inode2entry (adir, dir, entry);
-		fin_dentry (dret);
+	if (pseudo_root && 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;
+		goto out;
 	}
-	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;
-	struct inode *root_inode;
-	char *bpath = (char *) kmalloc (PATH_MAX, GFP_KERNEL);
+	emddir = umsdos_emd_dir_lookup (dir, 0);
+	if (emddir == NULL) {
+		/* This is a DOS directory. */
+		i2e = creat_dentry ("@i2e.nul@", 9, dir, NULL);
+		fill_new_filp (&filp, i2e);
+		filp.f_reada = 1;
+		filp.f_pos = 0;
+		bufsrch.entry = entry;
+		bufsrch.search_ino = inode->i_ino;
+		fat_readdir (&filp, &bufsrch, umsdos_dir_search);
+		if (bufsrch.found) {
+			ret = 0;
+			inode->u.umsdos_i.i_dir_owner = dir->i_ino;
+			inode->u.umsdos_i.i_emd_owner = 0;
+			umsdos_setup_dir_inode (inode);
+		}
+		goto out;
+	}
 
-	root_inode = iget (inode->i_sb, UMSDOS_ROOT_INO);
-	if (bpath == NULL) {
-		ret = -ENOMEM;
-	} else {
-		struct umsdos_dirent entry;
-		char *ptbpath = bpath + PATH_MAX - 1;
+	/* skip . and .. see umsdos_readdir_x() */
 
-		*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: ", entry.name_len, ptbpath));
-				}
-			}
-		} else {
-			inc_count (dir);
+	i2e = creat_dentry ("@i2e.nn@", 8, dir, NULL);
+	fill_new_filp (&filp, i2e);
+	filp.f_reada = 1;
+	filp.f_pos = UMSDOS_SPECIAL_DIRFPOS;
+	while (1) {
+		if (umsdos_readdir_x (dir, &filp, &bufk, 1, 
+				entry, 0, umsdos_filldir_k) < 0) {
+			printk ("UMSDOS: can't locate inode %ld in EMD??\n",
+				inode->i_ino);
+			break;
 		}
-		if (ret == 0) {
-			while (dir != root_inode) {
-				struct inode *adir;
-
-				ret = umsdos_locate_ancestor (dir, &adir, &entry);
-				/* iput (dir); FIXME */
-				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: ", entry.name_len, ptbpath));
-				} else {
-					break;
-				}
-			}
+		if (bufk.ino == inode->i_ino) {
+			ret = 0;
+			umsdos_lookup_patch (dir, inode, entry, bufk.f_pos);
+			break;
 		}
-		strcpy (path, ptbpath);
-		kfree (bpath);
 	}
-	Printk (("\n"));
-	/* iput (dir); / * FIXME?? */
+	iput (emddir);
+out:
 	return ret;
 }
 
@@ -735,8 +595,7 @@
  * Return != 0 if an entry is the pseudo DOS entry in the pseudo root.
  */
 
-int umsdos_is_pseudodos (	struct inode *dir,
-				struct dentry *dentry)
+int umsdos_is_pseudodos (struct inode *dir, struct dentry *dentry)
 {
 	/* #Specification: pseudo root / DOS hard coded
 	 * The pseudo sub-directory DOS in the pseudo root is hard coded.
@@ -745,8 +604,7 @@
 	 * a reserved path and nobody will think of using such a path
 	 * for a package.
 	 */
-	return pseudo_root
-	    && dir == pseudo_root
+	return dir == pseudo_root
 	    && dentry->d_name.len == 3
 	    && dentry->d_name.name[0] == 'D'
 	    && dentry->d_name.name[1] == 'O'
@@ -755,150 +613,126 @@
 
 
 /*
- * Check if a file exists in the current directory.
+ * Check whether a file exists in the current directory.
  * Return 0 if OK, negative error code if not (ex: -ENOENT).
  *
  * fills dentry->d_inode with found inode, and increments its count.
  * if not found, return -ENOENT.
  */
+/* #Specification: umsdos / lookup
+ * A lookup for a file is done in two steps.  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.
+ */
 
-int umsdos_lookup_x (	    struct inode *dir,
-			    struct dentry *dentry,
-			    int nopseudo)	/* Don't care about pseudo root mode */
+int umsdos_lookup_x (struct inode *dir, struct dentry *dentry, int nopseudo)
 {				
-	int ret = -ENOENT;
-	struct inode *root_inode;
-	int len = dentry->d_name.len;
 	const char *name = dentry->d_name.name;
-	
-
-#if UMS_DEBUG
-	Printk ((KERN_DEBUG "umsdos_lookup_x: /mn/ name=%.*s, dir=%lu (i_count=%d), d_parent=%p\n", (int) dentry->d_name.len, dentry->d_name.name, dir->i_ino, dir->i_count, dentry->d_parent));		/* FIXME /mn/ debug only */
-	if (dentry->d_parent)
-		Printk ((KERN_DEBUG "   d_parent is %.*s\n", (int) dentry->d_parent->d_name.len, dentry->d_parent->d_name.name));	/* FIXME : delme /mn/ */
-#endif
-
-	root_inode = iget (dir->i_sb, UMSDOS_ROOT_INO);
-	Printk ((KERN_ERR "umsdos_lookup_x (CNT!): entering root_count+1=%d, dir %lu _count=%d\n", root_inode->i_count, dir->i_ino, dir->i_count));	/* FIXME: DEBUG, DELME */
+	int len = dentry->d_name.len;
+	struct dentry *dret = NULL;
+	struct inode *inode;
+	int ret = -ENOENT;
+	struct umsdos_info info;
 
-	dentry->d_inode = NULL;
 	umsdos_startlookup (dir);
+	/* this shouldn't happen ... */
 	if (len == 1 && name[0] == '.') {
-		d_add (dentry, dir);
-		inc_count (dir);
-		ret = 0;
-	} else if (len == 2 && name[0] == '.' && name[1] == '.') {
-		if (pseudo_root && dir == pseudo_root) {
-			/* #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;
-			d_add (dentry, pseudo_root);
-			inc_count (pseudo_root);
-		} 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 as
-			 * permissions and ownership.
-			 */
-			struct dentry *dret, *d_dir = creat_dentry ("@d_dir3@", 5, dir, NULL);
-
-
-			dret = compat_umsdos_real_lookup (d_dir, "..", 2);
-			Printk (("ancestor ret %p dir %p *result %p ", dret, dir, dentry->d_inode));
-			if (dret
-			    && dentry->d_inode != root_inode
-			    && dentry->d_inode != pseudo_root) {
-				struct inode *aadir;
-				struct umsdos_dirent entry;
-				
-				Printk ((KERN_ERR "WARNING: umsdos_lookup_x: this won't work!\n"));
-				
-				dentry->d_inode = dret->d_inode;	/* FIXME! this should be rewritten ! it won't work this way! */
+		printk("umsdos_lookup_x: UMSDOS broken, please report!\n");
+		goto out;
+	}
 
-				ret = umsdos_locate_ancestor (dentry->d_inode, &aadir, &entry);
-				fin_dentry (dret);
-			}
-		}
-	} else if (umsdos_is_pseudodos (dir, dentry)) {
+	/* this shouldn't happen ... */
+	if (len == 2 && name[0] == '.' && name[1] == '.') {
+		printk("umsdos_lookup_x: UMSDOS broken, please report!\n");
+		goto out;
+	}
+
+	if (umsdos_is_pseudodos (dir, dentry)) {
 		/* #Specification: pseudo root / lookup(DOS)
 		 * A lookup of DOS in the pseudo root will always succeed
 		 * and return the inode of the real root.
 		 */
-		d_add (dentry, root_inode);
-		inc_count (dentry->d_inode); /* FIXME?! */
-		ret = 0;
-	} else {
-		struct umsdos_info info;
+		inode = iget(dir->i_sb, UMSDOS_ROOT_INO);
+		if (inode)
+			goto out_add;
+		ret = -ENOMEM;
+		goto out;
+	}
 
-		ret = umsdos_parse (dentry->d_name.name, dentry->d_name.len, &info);
-		if (ret == 0)
-			ret = umsdos_findentry (dir, &info, 0);
-		Printk (("lookup %.*s pos %lu ret %d len %d ", info.fake.len, 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 steps.  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 dentry *dret, *dir_dentry;
-		
-			dir_dentry = geti_dentry (dir);
-			dret = compat_umsdos_real_lookup (dir_dentry, info.fake.fname, info.fake.len);
-			
-
-			PRINTK ((KERN_DEBUG "umsdos_lookup_x: compat_umsdos_real_lookup for %.*s returned %p\n", info.fake.len, info.fake.fname, dret));
-
-			if (dret == NULL) {
-				printk (KERN_WARNING "UMSDOS:  Erase entry %.*s, out of sync with MS-DOS\n", info.fake.len, info.fake.fname);
-				umsdos_delentry (dir, &info, S_ISDIR (info.entry.mode));
-			} else {
-				Printk ((KERN_DEBUG "umsdos_lookup_x /mn/ debug: ino=%li\n", dret->d_inode->i_ino));
-
-				umsdos_lookup_patch (dir, dret->d_inode, &info.entry, info.f_pos);
-				Printk (("lookup ino %ld flags %d\n", dret->d_inode->i_ino, info.entry.flags));
-				if (info.entry.flags & UMSDOS_HLINK) {
-					dret = umsdos_solve_hlink (dret);
-				}
-				if (pseudo_root && dret->d_inode == 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 links
-					 * which are recorded independently of the pseudo-root
-					 * mode.
-					 */
-					Printk ((KERN_ERR "umsdos_lookup_x: warning: untested /mn/ Pseudo_root thingy\n"));
-					/* iput (pseudo_root); / * FIXME?? */
-					d_instantiate (dentry, NULL);		/* negative lookup */
-					ret = -ENOENT;
-				} else {
-					/* We've found it OK.  Now put inode in dentry. */
-					inc_count (dret->d_inode);	/* lookup should return incremented i_count */
-					d_add (dentry, dret->d_inode);
-				}
-				fin_dentry (dret);
-			}
-		}
+	ret = umsdos_parse (dentry->d_name.name, dentry->d_name.len, &info);
+	if (ret)
+		goto out;
+	ret = umsdos_findentry (dentry->d_parent, &info, 0);
+Printk (("lookup %.*s pos %lu ret %d len %d ", 
+info.fake.len, info.fake.fname, info.f_pos, ret, info.fake.len));
+	if (ret)
+		goto out;
+
+
+	dret = umsdos_lookup_dentry(dentry->d_parent, info.fake.fname,
+					info.fake.len);
+	ret = PTR_ERR(dret);
+	if (IS_ERR(dret))
+		goto out;
+	if (!dret->d_inode)
+		goto out_remove;
+
+	umsdos_lookup_patch_new(dret, &info.entry, info.f_pos);
+
+	/* Check for a hard link */
+	if (info.entry.flags & UMSDOS_HLINK) {
+Printk (("checking hard link %s/%s, ino=%ld, flags=%x\n", 
+dret->d_parent->d_name.name, dret->d_name.name,
+dret->d_inode->i_ino, info.entry.flags));
+		dret = umsdos_solve_hlink (dret);
+		ret = PTR_ERR(dret);
+		if (IS_ERR(dret))
+			goto out;
+	}
+	/* N.B. can dentry be negative after resolving hlinks? */
+
+	if (pseudo_root && dret->d_inode == 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 links
+		 * which are recorded independently of the pseudo-root
+		 * mode.
+		 */
+		Printk (("umsdos_lookup_x: untested Pseudo_root\n"));
+		ret = -ENOENT;
+		goto out_dput;
+	} else {
+		/* We've found it OK.  Now put inode in dentry. */
+		inode = dret->d_inode;
 	}
+
+	/*
+	 * Hash the dentry with the inode.
+	 */
+out_add:
+	inode->i_count++;
+	d_add (dentry, inode);
+	ret = 0;
+
+out_dput:
+	dput(dret);
+out:
 	umsdos_endlookup (dir);
-	iput (root_inode);	/* pair to iget() above.WHY is it not needed ?! */
-	PRINTK ((KERN_DEBUG "umsdos_lookup_x: returning %d : name=%.*s (i_count=%d), dir=%lu (i_count=%d)\n", ret, (int) dentry->d_name.len, dentry->d_name.name, dentry->d_inode->i_count, dir->i_ino, dir->i_count));
-	Printk ((KERN_ERR "umsdos_lookup_x (CNT!): exiting root_count=%d, dir %lu _count=%d\n", root_inode->i_count, dir->i_ino, dir->i_count));	/* FIXME: DEBUG, DELME */
 	return ret;
+
+out_remove:
+	printk(KERN_WARNING "UMSDOS:  entry %s/%s out of sync, erased\n",
+		dentry->d_name.name, info.fake.fname);
+	umsdos_delentry (dentry->d_parent, &info, S_ISDIR (info.entry.mode));
+	ret = -ENOENT;
+	goto out;
 }
 
 
@@ -911,118 +745,164 @@
  *
  */
 
-int UMSDOS_lookup (	  struct inode *dir,
-			  struct dentry *dentry)
+int UMSDOS_lookup (struct inode *dir, struct dentry *dentry)
 {
 	int ret;
 
 	ret = umsdos_lookup_x (dir, dentry, 0);
 
+	/* Create negative dentry if not found. */
 	if (ret == -ENOENT) {
-		Printk ((KERN_DEBUG "UMSDOS_lookup: converting -ENOENT to negative dentry !\n"));
-		d_add (dentry, NULL);	/* Create negative dentry if not found. */
+		Printk ((KERN_DEBUG 
+			"UMSDOS_lookup: converting -ENOENT to negative\n"));
+		d_add (dentry, NULL);
 		ret = 0;
 	}
-
 	return ret;
 }
 
 
 /*
+ * Lookup or create a dentry from within the filesystem.
+ *
+ * We need to use this instead of lookup_dentry, as the 
+ * directory semaphore lock is already held.
+ */
+struct dentry *umsdos_lookup_dentry(struct dentry *parent, char *name, int len)
+{
+	struct dentry *result, *dentry;
+	int error;
+	struct qstr qstr;
+
+	qstr.name = name;
+	qstr.len  = len;
+	qstr.hash = full_name_hash(name, len);
+	result = d_lookup(parent, &qstr);
+	if (!result) {
+		result = ERR_PTR(-ENOMEM);
+		dentry = d_alloc(parent, &qstr);
+		if (dentry) {
+			result = dentry;
+			error = umsdos_real_lookup(parent->d_inode, result);
+			if (error)
+				goto out_fail;
+		}
+	}
+out:
+	return result;
+
+out_fail:
+	dput(result);
+	result = ERR_PTR(error);
+	goto out;
+}
+
+
+/*
  * gets dentry which points to pseudo-hardlink
  *
  * it should try to find file it points to
- * if file is found, it should dput() original dentry and return new one (with d_count = i_count = 1)
+ * if file is found, it should dput() original dentry and return new one
+ * (with d_count = i_count = 1)
  * Otherwise, it should return with error, with dput()ed original dentry.
  *
  */
 
 struct dentry *umsdos_solve_hlink (struct dentry *hlink)
 {
-	struct dentry *base = hlink->d_sb->s_root;	/* root is our root for resolving pseudo-hardlink */
-	struct dentry *final = NULL;
-	struct inode *result;
+	/* root is our root for resolving pseudo-hardlink */
+	struct dentry *base = hlink->d_sb->s_root;
+	struct dentry *final, *dir, *dentry_dst;
+	char *path, *pt;
+	unsigned long len;
 	int ret = -EIO;
-	struct dentry *dentry_dst, *d_dir;
-	char *path;
+	struct file filp;
 
   	check_dentry_path (hlink, "HLINK BEGIN hlink");
 
+	final = ERR_PTR (-ENOMEM);
 	path = (char *) kmalloc (PATH_MAX, GFP_KERNEL);
+	if (path == NULL)
+		goto out;
 
-	result = NULL;
-
-	if (path == NULL) {
-		final = ERR_PTR (-ENOMEM);
-	} else {
-		struct file filp;
-		fill_new_filp (&filp, hlink);
-		filp.f_flags = O_RDONLY;
-		filp.f_pos = 0;
+	fill_new_filp (&filp, hlink);
+	filp.f_flags = O_RDONLY;
+	filp.f_pos = 0;
+
+	Printk (("hlink2inode "));
+	len = umsdos_file_read_kmem (&filp, path, hlink->d_inode->i_size);
+	if (len != hlink->d_inode->i_size)
+		goto out_noread;
+
+	/* start at root dentry */
+	dir = dget(base);
+	path[hlink->d_inode->i_size] = '\0';
+	pt = path;
+	while (1) {
+		char *start = pt;
+		int len;
+
+		while (*pt != '\0' && *pt != '/') pt++;
+		len = (int) (pt - start);
+		if (*pt == '/') *pt++ = '\0';
 
-		Printk (("hlink2inode "));
-		if (umsdos_file_read_kmem (&filp, path, hlink->d_inode->i_size) == hlink->d_inode->i_size) {
-			struct inode *dir;
-			char *pt = path;
-
-			dir = base->d_inode;	/* start at root inode */
-			path[hlink->d_inode->i_size] = '\0';
-			inc_count (dir);	/* since we're going to iput() it in the loop below... */
-
-			while (1) {
-				char *start = pt;
-				int len;
-
-				while (*pt != '\0' && *pt != '/') pt++;
-				len = (int) (pt - start);
-				if (*pt == '/') *pt++ = '\0';
-
-				d_dir = geti_dentry (dir);
-				dentry_dst = creat_dentry (start, len, NULL, d_dir);
-				if (dir->u.umsdos_i.i_emd_dir == 0) {
-					/* This is a DOS directory */
-
-					Printk (("hlink2inode /mn/: doing umsdos_rlookup_x on %.*s\n", (int) dentry_dst->d_name.len, dentry_dst->d_name.name));
-					ret = umsdos_rlookup_x (dir, dentry_dst, 1);
-				} else {
-					Printk (("hlink2inode /mn/: doing umsdos_lookup_x on %.*s\n", (int) dentry_dst->d_name.len, dentry_dst->d_name.name));
-					ret = umsdos_lookup_x (dir, dentry_dst, 1);
-				}
-				
-				iput (dir);	/* dir no longer needed */
-				Printk (("  returned %d\n", ret));
-				result = dentry_dst->d_inode;
-				inc_count (result);	/* we need inode to survive. We'll iput it in next loop */
-
-				fin_dentry (dentry_dst);	/* no longer needed - this is pair to creat_dentry */
-
-				Printk (("h2n lookup :%s: -> %d ", start, ret));
-				
-				if (ret != 0) {
-					iput (result);			/* we have no longer any use for it... */
-					final = ERR_PTR (ret);		/* path componenet not found ! */
-					break;
-				} else {
-					if (*pt != '\0') {
-						dir = result;
-					} else {			/* we're finished! */
-						final = creat_dentry (hlink->d_name.name, hlink->d_name.len, result, hlink->d_parent);
-						break;
-					}
-				}
-			} /* end while */
+		dentry_dst = umsdos_lookup_dentry(dir, start, len);
+		if (IS_ERR(dentry_dst))
+			break;
+		if (dir->d_inode->u.umsdos_i.i_emd_dir == 0) {
+			/* This is a DOS directory */
+			ret = umsdos_rlookup_x (dir->d_inode, dentry_dst, 1);
 		} else {
-			Printk (("umsdos_solve_hlink: failed reading pseudolink!\n"));
+			ret = umsdos_lookup_x (dir->d_inode, dentry_dst, 1);
 		}
-
-		Printk (("hlink2inode ret = %d %p -> %p\n", ret, hlink, result));
-		kfree (path);
+		Printk (("  returned %d\n", ret));
+		dput (dir);	/* dir no longer needed */
+		dir = dentry_dst;
+
+		Printk (("h2n lookup :%s: -> %d ", start, ret));
+		final = ERR_PTR (ret);
+		if (ret != 0) {
+			/* path component not found! */
+			break;
+		}
+		if (*pt == '\0') {	/* we're finished! */
+			final = umsdos_lookup_dentry(hlink->d_parent,
+						(char *) hlink->d_name.name,
+					 	hlink->d_name.len);
+			break;
+		}
+	} /* end while */
+	/*
+	 * See whether we found the path ...
+	 */
+	if (!IS_ERR(final)) {
+		if (!final->d_inode) {
+			d_instantiate(final, dir->d_inode);
+			/* we need inode to survive. */
+			dir->d_inode->i_count++;	
+		} else {
+			printk ("umsdos_solve_hlink: %s/%s already exists\n",
+				final->d_parent->d_name.name,
+				final->d_name.name);
+		}
+printk ("umsdos_solve_hlink: ret = %d, %s/%s -> %s/%s\n",
+ret, hlink->d_parent->d_name.name, hlink->d_name.name,
+final->d_parent->d_name.name, final->d_name.name);
 	}
+	dput(dir);
 
-	fin_dentry (hlink);	/* original hlink no longer needed */
+out_free:
+	kfree (path);
+
+out:
+	dput(hlink);	/* original hlink no longer needed */
   	check_dentry_path (hlink, "HLINK FIN hlink");
   	check_dentry_path (final, "HLINK RET final");
 	return final;
+
+out_noread:
+	printk("umsdos_solve_hlink: failed reading pseudolink!\n");
+	goto out_free;
 }	
 
 
@@ -1038,7 +918,7 @@
 	NULL,			/* no special open code */
 	NULL,			/* flush */
 	NULL,			/* no special release code */
-	NULL			/* fsync *//* in original NULL. changed to file_fsync. FIXME? /mn/ */
+	NULL			/* fsync */
 };
 
 struct inode_operations umsdos_dir_inode_operations =
@@ -1055,9 +935,9 @@
 	UMSDOS_rename,		/* rename */
 	NULL,			/* readlink */
 	NULL,			/* followlink */
-	generic_readpage,	/* readpage *//* in original NULL. changed to generic_readpage. FIXME? /mn/ */
+	generic_readpage,	/* readpage */
 	NULL,			/* writepage */
-	fat_bmap,		/* bmap *//* in original NULL. changed to fat_bmap. FIXME? /mn/ */
+	fat_bmap,		/* bmap */
 	NULL,			/* truncate */
 	NULL,			/* permission */
 	NULL,			/* smap */

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