summaryrefslogtreecommitdiffstats
path: root/fs/umsdos
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>1999-01-04 16:03:48 +0000
committerRalf Baechle <ralf@linux-mips.org>1999-01-04 16:03:48 +0000
commit78c388aed2b7184182c08428db1de6c872d815f5 (patch)
tree4b2003b1b4ceb241a17faa995da8dd1004bb8e45 /fs/umsdos
parenteb7a5bf93aaa4be1d7c6181100ab7639e74d67f7 (diff)
Merge with Linux 2.1.131 and more MIPS goodies.
(Did I mention that CVS is buggy ...)
Diffstat (limited to 'fs/umsdos')
-rw-r--r--fs/umsdos/README-WIP.txt198
-rw-r--r--fs/umsdos/check.c82
-rw-r--r--fs/umsdos/dir.c610
-rw-r--r--fs/umsdos/emd.c259
-rw-r--r--fs/umsdos/file.c16
-rw-r--r--fs/umsdos/inode.c609
-rw-r--r--fs/umsdos/ioctl.c54
-rw-r--r--fs/umsdos/mangle.c1
-rw-r--r--fs/umsdos/namei.c1001
-rw-r--r--fs/umsdos/rdir.c111
-rw-r--r--fs/umsdos/specs2
-rw-r--r--fs/umsdos/symlink.c89
12 files changed, 1369 insertions, 1663 deletions
diff --git a/fs/umsdos/README-WIP.txt b/fs/umsdos/README-WIP.txt
index f63a1144f..4535ec6ab 100644
--- a/fs/umsdos/README-WIP.txt
+++ b/fs/umsdos/README-WIP.txt
@@ -1,21 +1,29 @@
Changes by Matija Nalis (mnalis@jagor.srce.hr) on umsdos dentry fixing
(started by Peter T. Waltenberg <peterw@karaka.chch.cri.nz>)
+(Final conversion to dentries Bill Hawes <whawes@star.net>)
----------- WARNING --------- WARNING --------- WARNING -----------
-THIS IS TRULY EXPERIMENTAL. IT IS NOT BETA YET. PLEASE EXCUSE MY
-YELLING, BUT ANY USE OF THIS MODULE MAY VERY WELL DESTROY YOUR
-UMSDOS FILESYSTEM, AND MAYBE EVEN OTHER FILESYSTEMS IN USE.
-YOU'VE BEEN WARNED.
----------- WARNING --------- WARNING --------- WARNING -----------
+There is no warning any more.
+Both read-only and read-write stuff is fixed, both in
+msdos-compatibile mode, and in umsdos EMD mode, and it seems stable.
+There are still few hardlink nuisances, but those are not fatal.
-Current status (980901) - UMSDOS dentry-WIP-Beta 0.82-7:
+I'd call it pre-release, and ask for as many people as possible to
+come and test it! See notes below for some more information, or if
+you are trying to use UMSDOS as root partition.
+
+Legend: those lines marked with '+' on the beggining of line indicates it
+passed all of my tests, and performed perfect in all of them.
+
+Current status (981129) - UMSDOS dentry-pre 0.84:
(1) pure MSDOS (no --linux-.--- EMD file):
+READ:
+ readdir - works
+ lookup - works
+ read file - works
+WRITE:
+ creat file - works
+ delete file - works
+ write file - works
@@ -24,145 +32,95 @@ Current status (980901) - UMSDOS dentry-WIP-Beta 0.82-7:
+ rename dir (same dir) - works
+ rename dir (dif. dir) - works
+ mkdir - works
-- rmdir - QUESTIONABLE. probable problem on non-empty dirs.
-
-Notes: possible very minor problems with dentry/inode/... kernel structures (very rare)
++ rmdir - works
(2) umsdos (with --linux-.--- EMD file):
+READ:
+ readdir - works
+ lookup - works
+ permissions/owners stuff - works
+ long file names - works
+ read file - works
-- switching MSDOS/UMSDOS - works?
-- switching UMSDOS/MSDOS - works?
-- pseudo root things - COMPLETELY UNTESTED (commented out currently!)
++ switching MSDOS/UMSDOS - works
++ switching UMSDOS/MSDOS - works
+- pseudo root things - works mostly. See notes below.
+ resolve symlink - works
+ dereference symlink - works
-- hard links - broken again...
-+ special files (block/char devices, FIFOs, sockets...) - seems to work.
-- other ioctls - some UNTESTED
+ dangling symlink - works
++ hard links - works
++ special files (block/char devices, FIFOs, sockets...) - works
++ various umsdos ioctls - works
+
+
+WRITE:
++ create symlink - works
+- create hardlink - works, but see portability WARNING below
++ create file - works
++ create special file - works
++ write to file - works
++ rename file (same dir) - works
++ rename file (dif. dir) - works
+- rename hardlink (same dir) -
+- rename hardlink (dif. dir) -
++ rename symlink (same dir) - works
++ rename symlink (dif. dir) - works
++ rename dir (same dir) - works
++ rename dir (dif. dir) - works
++ delete file - works
++ notify_change (chown,perms) - works
++ delete hardlink - works
++ mkdir - works
++ rmdir - works
++ umssyncing (many ioctls) - works
-- create symlink - seems to work both on short & long names now !
-- create hardlink - WARNING: NOT FIXED YET!
-- create file - seems to work both on short & long names now !
-- create special file - seems to work both on short & long names now !
-- write to file - seems to work both on short & long names now !
-- rename file (same dir) - seems to work, but with i_count PROBLEMS
-- rename file (dif. dir) - seems to work, but with i_count PROBLEMS
-- rename dir (same dir) - seems to work, but with i_count PROBLEMS
-- rename dir (dif. dir) - seems to work, but with i_count PROBLEMS
-- delete file - seems to work fully now!
-- notify_change (chown,perms) - seems to work!
-- delete hardlink - WARNING: NOT FIXED YET!
-- mkdir - seems to work both on short & long names now !
-- rmdir - may work, but readdir blocks linux afterwards. to be FIXED!
-- umssyncing - seems to work, but NEEDS MORE TESTING
-
-+ CVF-FAT stuff (compressed DOS filesystem) - there is some support from Frank
+
+- CVF-FAT stuff (compressed DOS filesystem) - there is some support from Frank
Gockel <gockel@sent13.uni-duisburg.de> to use it even under umsdosfs, but I
have no way of testing it -- please let me know if there are problems specific
to umsdos (for instance, it works under msdosfs, but not under umsdosfs).
+Some current notes:
-Notes: there is moderate trashing of dentry/inode kernel structures. Probably
-some other kernel structures are compromised. You should have SysRq support
-compiled in, and use Sync/Emergency-remount-RO. If you don't try mounting
-read/write, you should have no big problems. When most things begin to work,
-I'll get to finding/fixing those inode/dentry ?_count leakages.
+Note: creating and using pseudo-hardlinks is always non-perfect, especially
+in filesystems that might be externally modified like umsdos. There is
+example is specs file about it. Specifically, moving directory which
+contains hardlinks will break them.
-Note 4: rmdir(2) fails with EBUSY - sdir->i_count > 1 (like 7 ??). It may be
-some error with dir->i_count++, or something related to iput(). See if
-number changes if we access the directory in different ways.
+Note: (about pseudoroot) If you are currently trying to use UMSDOS as root
+partition (with linux installed in c:\linux) it will boot, but there are
+some problems. Volunteers ready to test pseudoroot are needed (preferably
+ones with working backups or unimportant data). There are problems with
+different interpretation of hard links in normal in pseudo-root modes,
+resulting is 'silent delete' of them sometimes. Also, '/DOS' pseudo
+directory is only partially re-implemented and buggy. It works most of the
+time, though. Update: should work ok in 0.84, although it still does not
+work correctly in combination with initrd featere. Working on this!
-Note 5: there is a problem with unmounting umsdosfs. It seems to stay
-registered or something. Remounting the same device on any mount point with a
-different fstype (such as msdos or vfat) ignores the new fstype and umsdosfs
-kicks back in. Should be fixed in 0.82-6 (at least, with nothing in between)!
-Much of inode/dentry corruption is fixed in 0.82-7, especially when mounting
-read-only.
+Warning: (about creating hardlinks in pseudoroot mode) - hardlinks created in
+pseudoroot mode are not compatibile with 'normal' hardlinks, and vice versa.
+That is because harlink which is /foo in pseudoroot mode, becomes
+/linux/foo in normal mode. I'm thinking about this one. However, since most
+people either always use pseudoroot, or always use normal umsdos filesystem,
+this is no showstopper.
-Note 6: also we screwed umount(2)-ing the fs at times (EBUSY), by missing
-some of those iput/dput's. When most of the rest of the code is fixed, we'll
-put them back at the correct places (hopefully). Also much better in 0.82-7.
+Warning: (about hardlinks) - modifying hardlinks (esp. if there are in
+different directories) are currently somewhat broken, I'm working on it.
------------------------------------------------------------------------------
Some general notes:
-There is a great amount of kernel log messages. Use SysRq log-level 5 to turn
-most of them off, or 4 to turn all but really fatal ones off. Probably good
-idea to kill klogd/syslogd so it will only go to console. You can also
-comment out include/linux/umsdos_fs.h definition of UMS_DEBUG to get rid of
-most debugging messages. Please don't turn it off without good reason.
-
-It should work enough to test it, even enough to give you a few chances to
-umount/rmmod module, recompile it, and reinsert it again (but probably
-screaming about still used inodes on device and stuff). This is first on
-my list of things to get fixed, as it would greatly improve speed of
-compile/test/reboot/set_environment/recompile cycle by removing
-'reboot/set_environment' component that now occurs every few cycles.
-
-I need some help from someone who knows more than I about the use of dentries
-and inodes. If you can help, please contact me. I'm mostly worried about
-iget/iput and dget/dput, and deallocating temporary dentries we create.
-Should we destroy temp dentries? using d_invalidate? using d_drop? just
-dput them?
-
-I'm unfortunately somewhat out of time to read linux-kernel, but I do check
-for messages having "UMSDOS" in the subject, and read them. I might miss
-some in all that volume, though. I should reply to any direct e-mail in few
-days. If I don't, probably I never got your message. You can try
-mnalis@voyager.hr; however mnalis@jagor.srce.hr is preferable.
-
-
-------------------------------------------------------------------------------
-some of my notes for myself /mn/:
-
-+ hardlinks/symlinks. test with files in not_the_same_dir
-- also test not_the_same_dir for other file operations like rename etc.
-- iput: device 00:00 inode 318 still has aliases! problem. Check in iput()
- for device 0,0. Probably null pointer passed around when it shouldn't be ?
-- dput/iput problem...
-- What about .dotfiles? Are they working? How about multiple dots?
-- fix stuff like dir->i_count++ to atomic_inc(&dir->i_count) and similar?
-
-- should check what happen when multiple UMSDOSFS are mounted
-
-- chase down all "FIXME", "DELME", "CNT", check_dentry, check_inode, kill_dentry
- and fix it properly.
-
-- umsdos_create_any - calling msdos_create will create dentry for short name. Hmmmm..?
-
-- what is dir->i_count++ ? locking directory ? should this be lock_parent or
-something ?
-
-+ i_binary=2 is for CVF (compressed filesystem).
-
-- SECURITY WARNING: short dentries should be invalidated, or they could be
- accessed instead of proper long names.
-
-- I've put many check_dentry_path(), check_inode() calls to trace down
- problems. those should be removed in final version.
-
-- iput()s with a "FIXME?" comment are uncommented and probably OK. Those with
- "FIXME??" should be tested but probably work. Commented iput()s with
- any "FIXME" comments should probably be uncommented and tested. At some
- places we may need dput() instead of iput(), but that should be checked.
+Good idea when running development kernels is to have SysRq support compiled
+in kernel, and use Sync/Emergency-remount-RO if you bump into problems (like
+not being able to umount(2) umsdosfs, and because of it root partition also,
+or panics which force you to reboot etc.)
-+ as for iput(): (my only pointer so far. anyone else?)
+I'm unfortunately somewhat out of time to read linux-kernel@vger, but I do
+check for messages having "UMSDOS" in the subject, and read them. I might
+miss some in all that volume, though. I should reply to any direct e-mail
+in few days. If I don't, probably I never got your message. You can try
+mnalis-umsdos@voyager.hr; however mnalis@jagor.srce.hr is preferable.
->development I only know about iput. All functions that get an inode as
->argument and don't return it have to call iput on it before exit, i.e. when
->it is no longer needed and the code returns to vfs layer. The rest is quite
->new to me, but it might be similar for dput. Typical side effect of a
->missing iput was a memory runout (but no crash). You also couldn't unmount
->the filesystem later though no process was using it. On the other hand, one
->iput too much lead to serious pointer corruption and crashed the system
->very soon. I used to look at the FAT filesystem and copy those pieces of
->
-> Frank
diff --git a/fs/umsdos/check.c b/fs/umsdos/check.c
index a97f1cb91..41740fd42 100644
--- a/fs/umsdos/check.c
+++ b/fs/umsdos/check.c
@@ -13,6 +13,7 @@
#include <linux/ptrace.h>
#include <linux/mman.h>
#include <linux/mm.h>
+#include <linux/umsdos_fs.h>
#include <asm/system.h>
@@ -65,49 +66,52 @@ void check_page_tables (void)
void check_sb (struct super_block *sb, const char c)
{
if (sb) {
- Printk ((" (has %c_sb=%d, %d)",
- c, MAJOR (sb->s_dev), MINOR (sb->s_dev)));
+ printk (" (has %c_sb=%d, %d)",
+ c, MAJOR (sb->s_dev), MINOR (sb->s_dev));
} else {
- Printk ((" (%c_sb is NULL)", c));
+ printk (" (%c_sb is NULL)", c);
}
}
/*
* check an inode
*/
+extern struct inode_operations umsdos_rdir_inode_operations;
void check_inode (struct inode *inode)
{
if (inode) {
- Printk ((KERN_DEBUG "* inode is %lu (i_count=%d)",
- inode->i_ino, inode->i_count));
+ printk (KERN_DEBUG "* inode is %lu (i_count=%d)",
+ inode->i_ino, inode->i_count);
check_sb (inode->i_sb, 'i');
if (inode->i_dentry.next) { /* FIXME: does this work ? */
- Printk ((" (has i_dentry)"));
+ printk (" (has i_dentry)");
} else {
- Printk ((" (NO i_dentry)"));
+ printk (" (NO i_dentry)");
}
+ printk (" (i_patched=%d)", inode->u.umsdos_i.i_patched);
+
if (inode->i_op == NULL) {
- Printk ((" (i_op is NULL)\n"));
+ printk (" (i_op is NULL)\n");
} else if (inode->i_op == &umsdos_dir_inode_operations) {
- Printk ((" (i_op is umsdos_dir_inode_operations)\n"));
+ printk (" (i_op is umsdos_dir_inode_operations)\n");
} else if (inode->i_op == &umsdos_file_inode_operations) {
- Printk ((" (i_op is umsdos_file_inode_operations)\n"));
+ printk (" (i_op is umsdos_file_inode_operations)\n");
} else if (inode->i_op == &umsdos_file_inode_operations_no_bmap) {
- Printk ((" (i_op is umsdos_file_inode_operations_no_bmap)\n"));
+ printk (" (i_op is umsdos_file_inode_operations_no_bmap)\n");
} else if (inode->i_op == &umsdos_file_inode_operations_readpage) {
- Printk ((" (i_op is umsdos_file_inode_operations_readpage)\n"));
+ printk (" (i_op is umsdos_file_inode_operations_readpage)\n");
} else if (inode->i_op == &umsdos_rdir_inode_operations) {
- Printk ((" (i_op is umsdos_rdir_inode_operations)\n"));
+ printk (" (i_op is umsdos_rdir_inode_operations)\n");
} else if (inode->i_op == &umsdos_symlink_inode_operations) {
- Printk ((" (i_op is umsdos_symlink_inode_operations)\n"));
+ printk (" (i_op is umsdos_symlink_inode_operations)\n");
} else {
- Printk ((" (i_op is UNKNOWN: %p)\n", inode->i_op));
+ printk ((" (i_op is UNKNOWN: %p)\n", inode->i_op));
}
} else {
- Printk ((KERN_DEBUG "* inode is NULL\n"));
+ printk (KERN_DEBUG "* inode is NULL\n");
}
}
@@ -125,40 +129,40 @@ void checkd_inode (struct inode *inode)
return;
}
- Printk ((KERN_DEBUG "checkd_inode: inode %lu\n", inode->i_ino));
+ printk (KERN_DEBUG "checkd_inode: inode %lu\n", inode->i_ino);
cur = inode->i_dentry.next;
while (count++ < 10) {
PRINTK (("1..."));
if (!cur) {
- Printk ((KERN_ERR "checkd_inode: *** NULL reached. exit.\n"));
+ printk (KERN_ERR "checkd_inode: *** NULL reached. exit.\n");
return;
}
PRINTK (("2..."));
ret = list_entry (cur, struct dentry, d_alias);
PRINTK (("3..."));
if (cur == cur->next) {
- Printk ((KERN_DEBUG "checkd_inode: *** cur=cur->next: normal exit.\n"));
+ printk (KERN_DEBUG "checkd_inode: *** cur=cur->next: normal exit.\n");
return;
}
PRINTK (("4..."));
if (!ret) {
- Printk ((KERN_ERR "checkd_inode: *** ret dentry is NULL. exit.\n"));
+ printk (KERN_ERR "checkd_inode: *** ret dentry is NULL. exit.\n");
return;
}
PRINTK (("5... (ret=%p)...", ret));
PRINTK (("5.1.. (ret->d_dname=%p)...", &(ret->d_name)));
PRINTK (("5.1.1. (ret->d_dname.len=%d)...", (int) ret->d_name.len));
PRINTK (("5.1.2. (ret->d_dname.name=%c)...", ret->d_name.name));
- Printk ((KERN_DEBUG "checkd_inode: i_dentry is %.*s\n", (int) ret->d_name.len, ret->d_name.name));
+ printk (KERN_DEBUG "checkd_inode: i_dentry is %.*s\n", (int) ret->d_name.len, ret->d_name.name);
PRINTK (("6..."));
cur = cur->next;
PRINTK (("7..."));
#if 1
- Printk ((KERN_DEBUG "checkd_inode: *** finished after count 1 (operator forced)\n"));
+ printk (KERN_DEBUG "checkd_inode: *** finished after count 1 (operator forced)\n");
return;
#endif
}
- Printk ((KERN_ERR "checkd_inode: *** OVER LIMIT (loop?) !\n"));
+ printk (KERN_ERR "checkd_inode: *** OVER LIMIT (loop?) !\n");
return;
}
@@ -170,19 +174,19 @@ void checkd_inode (struct inode *inode)
void check_dent_int (struct dentry *dentry, int parent)
{
if (parent) {
- Printk ((KERN_DEBUG "* parent(%d) dentry: %.*s\n",
- parent, (int) dentry->d_name.len, dentry->d_name.name));
+ printk (KERN_DEBUG "* parent(%d) dentry: %.*s\n",
+ parent, (int) dentry->d_name.len, dentry->d_name.name);
} else {
- Printk ((KERN_DEBUG "* checking dentry: %.*s\n",
- (int) dentry->d_name.len, dentry->d_name.name));
+ printk (KERN_DEBUG "* checking dentry: %.*s\n",
+ (int) dentry->d_name.len, dentry->d_name.name);
}
check_inode (dentry->d_inode);
- Printk ((KERN_DEBUG "* d_count=%d", dentry->d_count));
+ printk (KERN_DEBUG "* d_count=%d", dentry->d_count);
check_sb (dentry->d_sb, 'd');
if (dentry->d_op == NULL) {
- Printk ((" (d_op is NULL)\n"));
+ printk (" (d_op is NULL)\n");
} else {
- Printk ((" (d_op is UNKNOWN: %p)\n", dentry->d_op));
+ printk (" (d_op is UNKNOWN: %p)\n", dentry->d_op);
}
}
@@ -194,35 +198,35 @@ void check_dent_int (struct dentry *dentry, int parent)
void check_dentry_path (struct dentry *dentry, const char *desc)
{
int count=0;
- Printk ((KERN_DEBUG "*** check_dentry_path: %.60s\n", desc));
+ printk (KERN_DEBUG "*** check_dentry_path: %.60s\n", desc);
if (!dentry) {
- Printk ((KERN_DEBUG "*** checking dentry... it is NULL !\n"));
+ printk (KERN_DEBUG "*** checking dentry... it is NULL !\n");
return;
}
if (IS_ERR(dentry)) {
- Printk ((KERN_DEBUG "*** checking dentry... it is ERR(%ld) !\n",
- PTR_ERR(dentry)));
+ printk (KERN_DEBUG "*** checking dentry... it is ERR(%ld) !\n",
+ PTR_ERR(dentry));
return;
}
while (dentry && count < 10) {
check_dent_int (dentry, count++);
if (dentry == dentry->d_parent) {
- Printk ((KERN_DEBUG "*** end checking dentry (root reached ok)\n"));
+ printk (KERN_DEBUG "*** end checking dentry (root reached ok)\n");
break;
}
dentry = dentry->d_parent;
}
if (count >= 10) { /* if infinite loop detected */
- Printk ((KERN_ERR
- "*** WARNING ! INFINITE LOOP ! check_dentry_path aborted !\n"));
+ printk (KERN_ERR
+ "*** WARNING ! INFINITE LOOP ! check_dentry_path aborted !\n");
}
if (!dentry) {
- Printk ((KERN_ERR
- "*** WARNING ! NULL dentry ! check_dentry_path aborted !\n"));
+ printk (KERN_ERR
+ "*** WARNING ! NULL dentry ! check_dentry_path aborted !\n");
}
}
#else
diff --git a/fs/umsdos/dir.c b/fs/umsdos/dir.c
index 5a7bcd854..6ab276ac4 100644
--- a/fs/umsdos/dir.c
+++ b/fs/umsdos/dir.c
@@ -20,46 +20,40 @@
#include <asm/uaccess.h>
#define UMSDOS_SPECIAL_DIRFPOS 3
+extern struct dentry *saved_root;
extern struct inode *pseudo_root;
-
+/* #define UMSDOS_DEBUG_VERBOSE 1 */
/*
- * This needs to have the parent dentry passed to it.
- * N.B. Try to get rid of this soon!
+ * Dentry operations routines
*/
-int compat_msdos_create (struct inode *dir, const char *name, int len,
- int mode, struct inode **inode)
+
+/* nothing for now ... */
+static int umsdos_dentry_validate(struct dentry *dentry)
{
- int ret;
- struct dentry *dentry, *d_dir;
+ return 1;
+}
- 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;
+/* for now, drop everything to force lookups ... */
+static void umsdos_dentry_dput(struct dentry *dentry)
+{
+ struct inode *inode = dentry->d_inode;
+ if (inode) {
+ d_drop(dentry);
}
- 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");
- 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);
-out:
- return ret;
}
+struct dentry_operations umsdos_dentry_operations =
+{
+ umsdos_dentry_validate, /* d_validate(struct dentry *) */
+ NULL, /* d_hash */
+ NULL, /* d_compare */
+ umsdos_dentry_dput, /* d_delete(struct dentry *) */
+ NULL,
+ NULL,
+};
+
/*
* So grep * doesn't complain in the presence of directories.
@@ -109,9 +103,9 @@ static int umsdos_dir_once ( void *buf,
* Return a negative value from linux/errno.h.
* 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.
+ * This function is used by the normal readdir VFS entry point,
+ * and in order to get the directory entry from a file's dentry.
+ * See umsdos_dentry_to_entry() below.
*/
static int umsdos_readdir_x (struct inode *dir, struct file *filp,
@@ -129,7 +123,6 @@ static int umsdos_readdir_x (struct inode *dir, struct file *filp,
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
@@ -140,6 +133,8 @@ Printk (("umsdos_readdir_x: what UMSDOS_SPECIAL_DIRFPOS /mn/?\n"));
* linux root), it simulate a directory /DOS which points to
* the real root of the file system.
*/
+
+ Printk ((KERN_WARNING "umsdos_readdir_x: pseudo_root thing UMSDOS_SPECIAL_DIRFPOS\n"));
if (filldir (dirbuf, "DOS", 3,
UMSDOS_SPECIAL_DIRFPOS, UMSDOS_ROOT_INO) == 0) {
filp->f_pos++;
@@ -174,9 +169,13 @@ Printk (("umsdos_readdir_x: what UMSDOS_SPECIAL_DIRFPOS /mn/?\n"));
ret = PTR_ERR(demd);
if (IS_ERR(demd))
goto out_end;
- ret = 0;
- if (!demd->d_inode)
+ ret = -EIO;
+ if (!demd->d_inode) {
+ printk(KERN_WARNING
+ "umsdos_readir_x: EMD file %s/%s not found\n",
+ demd->d_parent->d_name.name, demd->d_name.name);
goto out_dput;
+ }
/* set up our private filp ... */
fill_new_filp(&new_filp, demd);
@@ -189,35 +188,63 @@ 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 inode *inode;
struct umsdos_dirent entry;
+ struct umsdos_info info;
ret = -EIO;
if (umsdos_emd_dir_readentry (&new_filp, &entry) != 0)
break;
-
if (entry.name_len == 0)
- goto remove_name;
+ continue;
+#ifdef UMSDOS_DEBUG_VERBOSE
+if (entry.flags & UMSDOS_HLINK)
+printk("umsdos_readdir_x: %s/%s is hardlink\n",
+filp->f_dentry->d_name.name, entry.name);
+#endif
umsdos_parse (entry.name, entry.name_len, &info);
info.f_pos = cur_f_pos;
umsdos_manglename (&info);
+ /*
+ * Do a real lookup on the short name.
+ */
dret = umsdos_lookup_dentry(filp->f_dentry, info.fake.fname,
- info.fake.len);
+ info.fake.len, 1);
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) {
+ /*
+ * If the file wasn't found, remove it from the EMD.
+ */
+ inode = dret->d_inode;
+ if (!inode)
+ goto remove_name;
+#ifdef UMSDOS_DEBUG_VERBOSE
+if (inode->u.umsdos_i.i_is_hlink)
+printk("umsdos_readdir_x: %s/%s already resolved, ino=%ld\n",
+dret->d_parent->d_name.name, dret->d_name.name, inode->i_ino);
+#endif
+
+Printk (("Found %s/%s, ino=%ld, flags=%x\n",
+dret->d_parent->d_name.name, info.fake.fname, dret->d_inode->i_ino,
+entry.flags));
+ /* check whether to resolve a hard-link */
+ if ((entry.flags & UMSDOS_HLINK) && follow_hlink &&
+ !inode->u.umsdos_i.i_is_hlink) {
dret = umsdos_solve_hlink (dret);
ret = PTR_ERR(dret);
if (IS_ERR(dret))
break;
+ inode = dret->d_inode;
+ if (!inode) {
+printk("umsdos_readdir_x: %s/%s negative after link\n",
+dret->d_parent->d_name.name, dret->d_name.name);
+ goto clean_up;
+ }
}
-
+
/* #Specification: pseudo root / reading real root
* The pseudo root (/linux) is logically
* erased from the real root. This means that
@@ -225,20 +252,21 @@ dret->d_parent->d_name.name, info.fake.fname, entry.flags));
* infinite recursion (/DOS/linux/DOS/linux/...) while
* walking the file system.
*/
- if (dret->d_inode != pseudo_root &&
+ if (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) {
+ cur_f_pos, inode->i_ino) < 0) {
new_filp.f_pos = cur_f_pos;
}
-Printk (("Found %s/%s(%ld)\n",
-dret->d_parent->d_name.name, dret->d_name.name, dret->d_inode->i_ino));
+Printk(("umsdos_readdir_x: got %s/%s, ino=%ld\n",
+dret->d_parent->d_name.name, dret->d_name.name, inode->i_ino));
if (u_entry != NULL)
*u_entry = entry;
dput(dret);
+ ret = 0;
break;
}
+ clean_up:
dput(dret);
continue;
@@ -248,11 +276,17 @@ dret->d_parent->d_name.name, dret->d_name.name, dret->d_inode->i_ino));
* 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);
+#ifdef UMSDOS_PARANOIA
+printk("umsdos_readdir_x: %s/%s out of sync, erasing\n",
+filp->f_dentry->d_name.name, info.entry.name);
+#endif
+ ret = umsdos_delentry(filp->f_dentry, &info,
+ S_ISDIR(info.entry.mode));
if (ret)
- break;
- continue;
+ printk(KERN_WARNING
+ "umsdos_readdir_x: delentry %s, err=%d\n",
+ info.entry.name, ret);
+ goto clean_up;
}
/*
* If the fillbuf has failed, f_pos is back to 0.
@@ -296,8 +330,6 @@ static int UMSDOS_readdir (struct file *filp, void *dirbuf, filldir_t filldir)
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);
if (bufk.count == 0)
@@ -340,19 +372,17 @@ static int UMSDOS_readdir (struct file *filp, void *dirbuf, filldir_t filldir)
* does this automatically.
*/
-void umsdos_lookup_patch (struct inode *dir, struct inode *inode,
- struct umsdos_dirent *entry, off_t emd_pos)
+void umsdos_lookup_patch_new(struct dentry *dentry, struct umsdos_info *info)
{
- 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;
+ struct inode *inode = dentry->d_inode;
+ struct umsdos_dirent *entry = &info->entry;
+ /*
+ * This part of the initialization depends only on i_patched.
+ */
+ if (inode->u.umsdos_i.i_patched)
+ goto out;
+ inode->u.umsdos_i.i_patched = 1;
if (S_ISREG (entry->mode))
entry->mtime = inode->i_mtime;
inode->i_mode = entry->mode;
@@ -380,218 +410,18 @@ void umsdos_lookup_patch (struct inode *dir, struct inode *inode,
"UMSDOS: lookup_patch entry->nlink < 1 ???\n");
}
}
- umsdos_patch_inode (inode, dir, emd_pos);
+ /*
+ * The mode may have changed, so patch the inode again.
+ */
+ umsdos_patch_dentry_inode(dentry, info->f_pos);
+ umsdos_set_dirinfo_new(dentry, info->f_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 */
- ino_t ino;
-};
-
-
-/*
- * Just to record the offset of one entry.
- */
-
-static int umsdos_filldir_k ( void *buf,
- const char *name,
- int len,
- off_t offset,
- ino_t ino)
-{
- struct UMSDOS_DIRENT_K *d = (struct UMSDOS_DIRENT_K *) buf;
-
- d->f_pos = offset;
- d->ino = ino;
- return 0;
-}
-
-struct UMSDOS_DIR_SEARCH {
- struct umsdos_dirent *entry;
- int found;
- ino_t search_ino;
-};
-
-static int umsdos_dir_search ( void *buf,
- const char *name,
- int len,
- off_t offset,
- ino_t ino)
-{
- int ret = 0;
- struct UMSDOS_DIR_SEARCH *d = (struct UMSDOS_DIR_SEARCH *) buf;
-
- if (d->search_ino == ino) {
- d->found = 1;
- memcpy (d->entry->name, name, len);
- d->entry->name[len] = '\0';
- d->entry->name_len = len;
- ret = 1; /* So fat_readdir will terminate */
- }
- return ret;
-}
-
-
-
-/*
- * 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_dentry_to_entry(struct dentry *dentry, struct umsdos_dirent *entry)
-{
- 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) {
- /*
- * 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;
- }
-
- /* 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;
-}
-
-/*
- * Deprecated. Try to get rid of this soon!
- */
-int umsdos_inode2entry (struct inode *dir, struct inode *inode,
- struct umsdos_dirent *entry)
-{
- int ret = -ENOENT;
- struct inode *emddir;
- struct dentry *i2e;
- struct file filp;
- struct UMSDOS_DIR_SEARCH bufsrch;
- struct UMSDOS_DIRENT_K bufk;
-
- 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;
- }
-
- 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;
- }
-
- /* skip . and .. see umsdos_readdir_x() */
-
- 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 (bufk.ino == inode->i_ino) {
- ret = 0;
- umsdos_lookup_patch (dir, inode, entry, bufk.f_pos);
- break;
- }
- }
- iput (emddir);
-out:
- return ret;
-}
-
-
-/*
* Return != 0 if an entry is the pseudo DOS entry in the pseudo root.
*/
@@ -631,71 +461,79 @@ int umsdos_is_pseudodos (struct inode *dir, struct dentry *dentry)
int umsdos_lookup_x (struct inode *dir, struct dentry *dentry, int nopseudo)
{
- const char *name = dentry->d_name.name;
- int len = dentry->d_name.len;
struct dentry *dret = NULL;
struct inode *inode;
int ret = -ENOENT;
struct umsdos_info info;
- umsdos_startlookup (dir);
- /* this shouldn't happen ... */
- if (len == 1 && name[0] == '.') {
- printk("umsdos_lookup_x: UMSDOS broken, please report!\n");
- goto out;
- }
-
- /* this shouldn't happen ... */
- if (len == 2 && name[0] == '.' && name[1] == '.') {
- printk("umsdos_lookup_x: UMSDOS broken, please report!\n");
- goto out;
- }
+#ifdef UMSDOS_DEBUG_VERBOSE
+printk("umsdos_lookup_x: looking for %s/%s\n",
+dentry->d_parent->d_name.name, dentry->d_name.name);
+#endif
+ umsdos_startlookup (dir);
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.
*/
- inode = iget(dir->i_sb, UMSDOS_ROOT_INO);
- if (inode)
- goto out_add;
- ret = -ENOMEM;
- goto out;
+ Printk ((KERN_DEBUG "umsdos_lookup_x: following /DOS\n"));
+ inode = saved_root->d_inode;
+ goto out_add;
}
ret = umsdos_parse (dentry->d_name.name, dentry->d_name.len, &info);
- if (ret)
+ if (ret) {
+printk("umsdos_lookup_x: %s/%s parse failed, ret=%d\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, ret);
goto out;
+ }
+
ret = umsdos_findentry (dentry->d_parent, &info, 0);
+ if (ret) {
+if (ret != -ENOENT)
+printk("umsdos_lookup_x: %s/%s findentry failed, ret=%d\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, ret);
+ goto out;
+ }
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;
-
+ /* do a real lookup to get the short name ... */
dret = umsdos_lookup_dentry(dentry->d_parent, info.fake.fname,
- info.fake.len);
+ info.fake.len, 1);
ret = PTR_ERR(dret);
- if (IS_ERR(dret))
+ if (IS_ERR(dret)) {
+printk("umsdos_lookup_x: %s/%s real lookup failed, ret=%d\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, ret);
goto out;
- if (!dret->d_inode)
+ }
+ inode = dret->d_inode;
+ if (!inode)
goto out_remove;
-
- umsdos_lookup_patch_new(dret, &info.entry, info.f_pos);
+ umsdos_lookup_patch_new(dret, &info);
+#ifdef UMSDOS_DEBUG_VERBOSE
+printk("umsdos_lookup_x: found %s/%s, ino=%ld\n",
+dret->d_parent->d_name.name, dret->d_name.name, dret->d_inode->i_ino);
+#endif
/* 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));
+ if ((info.entry.flags & UMSDOS_HLINK) &&
+ !inode->u.umsdos_i.i_is_hlink) {
dret = umsdos_solve_hlink (dret);
ret = PTR_ERR(dret);
if (IS_ERR(dret))
goto out;
+ ret = -ENOENT;
+ inode = dret->d_inode;
+ if (!inode) {
+printk("umsdos_lookup_x: %s/%s negative after link\n",
+dret->d_parent->d_name.name, dret->d_name.name);
+ goto out_dput;
+ }
}
- /* N.B. can dentry be negative after resolving hlinks? */
- if (pseudo_root && dret->d_inode == pseudo_root && !nopseudo) {
+ if (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.
@@ -705,23 +543,23 @@ dret->d_inode->i_ino, info.entry.flags));
* which are recorded independently of the pseudo-root
* mode.
*/
- Printk (("umsdos_lookup_x: untested Pseudo_root\n"));
+printk("umsdos_lookup_x: skipping DOS/linux\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.
+ * We've found it OK. Now hash the dentry with the inode.
*/
out_add:
inode->i_count++;
d_add (dentry, inode);
+ dentry->d_op = &umsdos_dentry_operations;
ret = 0;
out_dput:
+ if (dret && dret != dentry)
+ d_drop(dret);
dput(dret);
out:
umsdos_endlookup (dir);
@@ -729,10 +567,10 @@ out:
out_remove:
printk(KERN_WARNING "UMSDOS: entry %s/%s out of sync, erased\n",
- dentry->d_name.name, info.fake.fname);
+ dentry->d_parent->d_name.name, dentry->d_name.name);
umsdos_delentry (dentry->d_parent, &info, S_ISDIR (info.entry.mode));
ret = -ENOENT;
- goto out;
+ goto out_dput;
}
@@ -740,9 +578,7 @@ out_remove:
* Check whether a file exists in the current directory.
* Return 0 if OK, negative error code if not (ex: -ENOENT).
*
- * called by VFS. should fill dentry->d_inode (via d_add), and
- * set (increment) dentry->d_inode->i_count.
- *
+ * Called by VFS; should fill dentry->d_inode via d_add.
*/
int UMSDOS_lookup (struct inode *dir, struct dentry *dentry)
@@ -756,6 +592,7 @@ int UMSDOS_lookup (struct inode *dir, struct dentry *dentry)
Printk ((KERN_DEBUG
"UMSDOS_lookup: converting -ENOENT to negative\n"));
d_add (dentry, NULL);
+ dentry->d_op = &umsdos_dentry_operations;
ret = 0;
}
return ret;
@@ -768,7 +605,8 @@ int UMSDOS_lookup (struct inode *dir, struct dentry *dentry)
* 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 *umsdos_lookup_dentry(struct dentry *parent, char *name, int len,
+ int real)
{
struct dentry *result, *dentry;
int error;
@@ -783,7 +621,9 @@ struct dentry *umsdos_lookup_dentry(struct dentry *parent, char *name, int len)
dentry = d_alloc(parent, &qstr);
if (dentry) {
result = dentry;
- error = umsdos_real_lookup(parent->d_inode, result);
+ error = real ?
+ UMSDOS_rlookup(parent->d_inode, result) :
+ UMSDOS_lookup(parent->d_inode, result);
if (error)
goto out_fail;
}
@@ -797,111 +637,134 @@ out_fail:
goto out;
}
+/*
+ * Return a path relative to our root.
+ */
+char * umsdos_d_path(struct dentry *dentry, char * buffer, int len)
+{
+ struct dentry * old_root = current->fs->root;
+ char * path;
+
+ /* N.B. not safe -- fix this soon! */
+ current->fs->root = dentry->d_sb->s_root;
+ path = d_path(dentry, buffer, len);
+ current->fs->root = old_root;
+ return path;
+}
+
/*
- * gets dentry which points to pseudo-hardlink
+ * Return the dentry which points to a 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)
- * Otherwise, it should return with error, with dput()ed original dentry.
+ * if file is found, return new dentry/inode
+ * The resolved inode will have i_is_hlink set.
*
+ * Note: the original dentry is always dput(), even if an error occurs.
*/
struct dentry *umsdos_solve_hlink (struct dentry *hlink)
{
/* root is our root for resolving pseudo-hardlink */
struct dentry *base = hlink->d_sb->s_root;
- struct dentry *final, *dir, *dentry_dst;
+ struct dentry *dentry_dst;
char *path, *pt;
- unsigned long len;
- int ret = -EIO;
+ int len;
struct file filp;
- check_dentry_path (hlink, "HLINK BEGIN hlink");
+#ifdef UMSDOS_DEBUG_VERBOSE
+printk("umsdos_solve_hlink: following %s/%s\n",
+hlink->d_parent->d_name.name, hlink->d_name.name);
+#endif
- final = ERR_PTR (-ENOMEM);
+ dentry_dst = ERR_PTR (-ENOMEM);
path = (char *) kmalloc (PATH_MAX, GFP_KERNEL);
if (path == NULL)
goto out;
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;
+#ifdef UMSDOS_DEBUG_VERBOSE
+printk ("umsdos_solve_hlink: %s/%s is path %s\n",
+hlink->d_parent->d_name.name, hlink->d_name.name, path);
+#endif
/* start at root dentry */
- dir = dget(base);
- path[hlink->d_inode->i_size] = '\0';
- pt = path;
+ dentry_dst = dget(base);
+ path[len] = '\0';
+ pt = path + 1; /* skip leading '/' */
while (1) {
+ struct dentry *dir = dentry_dst, *demd;
char *start = pt;
- int len;
+ int real;
while (*pt != '\0' && *pt != '/') pt++;
len = (int) (pt - start);
if (*pt == '/') *pt++ = '\0';
- dentry_dst = umsdos_lookup_dentry(dir, start, len);
+ real = (dir->d_inode->u.umsdos_i.i_emd_dir == 0);
+ /*
+ * Hack alert! inode->u.umsdos_i.i_emd_dir isn't reliable,
+ * so just check whether there's an EMD file ...
+ */
+ real = 1;
+ demd = umsdos_get_emd_dentry(dir);
+ if (!IS_ERR(demd)) {
+ if (demd->d_inode)
+ real = 0;
+ dput(demd);
+ }
+
+#ifdef UMSDOS_DEBUG_VERBOSE
+printk ("umsdos_solve_hlink: dir %s/%s, name=%s, emd_dir=%ld, real=%d\n",
+dir->d_parent->d_name.name, dir->d_name.name, start,
+dir->d_inode->u.umsdos_i.i_emd_dir, real);
+#endif
+ dentry_dst = umsdos_lookup_dentry(dir, start, len, real);
+ if (real)
+ d_drop(dir);
+ dput (dir);
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 {
- ret = umsdos_lookup_x (dir->d_inode, dentry_dst, 1);
- }
- 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! */
+ /* not found? stop search ... */
+ if (!dentry_dst->d_inode) {
break;
}
- if (*pt == '\0') { /* we're finished! */
- final = umsdos_lookup_dentry(hlink->d_parent,
- (char *) hlink->d_name.name,
- hlink->d_name.len);
+ if (*pt == '\0') /* we're finished! */
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++;
+
+ if (!IS_ERR(dentry_dst)) {
+ struct inode *inode = dentry_dst->d_inode;
+ if (inode) {
+ inode->u.umsdos_i.i_is_hlink = 1;
+#ifdef UMSDOS_DEBUG_VERBOSE
+printk ("umsdos_solve_hlink: resolved link %s/%s, ino=%ld\n",
+dentry_dst->d_parent->d_name.name, dentry_dst->d_name.name, inode->i_ino);
+#endif
} else {
- printk ("umsdos_solve_hlink: %s/%s already exists\n",
- final->d_parent->d_name.name,
- final->d_name.name);
+#ifdef UMSDOS_DEBUG_VERBOSE
+printk ("umsdos_solve_hlink: resolved link %s/%s negative!\n",
+dentry_dst->d_parent->d_name.name, dentry_dst->d_name.name);
+#endif
}
-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);
+ } else
+ printk(KERN_WARNING
+ "umsdos_solve_hlink: err=%ld\n", PTR_ERR(dentry_dst));
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;
+ return dentry_dst;
out_noread:
- printk("umsdos_solve_hlink: failed reading pseudolink!\n");
+ printk(KERN_WARNING "umsdos_solve_hlink: failed reading pseudolink!\n");
goto out_free;
}
@@ -943,5 +806,4 @@ struct inode_operations umsdos_dir_inode_operations =
NULL, /* smap */
NULL, /* updatepage */
NULL, /* revalidate */
-
};
diff --git a/fs/umsdos/emd.c b/fs/umsdos/emd.c
index 2f56b0404..6a4e99c04 100644
--- a/fs/umsdos/emd.c
+++ b/fs/umsdos/emd.c
@@ -30,46 +30,12 @@ ssize_t umsdos_file_read_kmem ( struct file *filp,
char *buf,
size_t count)
{
- int ret;
-
+ ssize_t ret;
mm_segment_t old_fs = get_fs ();
set_fs (KERNEL_DS);
-
- PRINTK ((KERN_DEBUG "umsdos_file_read_kmem /mn/: Checkin: filp=%p, buf=%p, size=%d\n", filp, buf, count));
- PRINTK ((KERN_DEBUG " inode=%lu, i_size=%lu\n", filp->f_dentry->d_inode->i_ino, filp->f_dentry->d_inode->i_size));
- PRINTK ((KERN_DEBUG " f_pos=%Lu\n", filp->f_pos));
- PRINTK ((KERN_DEBUG " name=%.*s\n", (int) filp->f_dentry->d_name.len, filp->f_dentry->d_name.name));
- PRINTK ((KERN_DEBUG " i_binary(sb)=%d\n", MSDOS_I (filp->f_dentry->d_inode)->i_binary));
- PRINTK ((KERN_DEBUG " f_count=%d, f_flags=%d\n", filp->f_count, filp->f_flags));
- PRINTK ((KERN_DEBUG " f_owner=%d\n", filp->f_owner.uid));
- PRINTK ((KERN_DEBUG " f_version=%ld\n", filp->f_version));
- PRINTK ((KERN_DEBUG " f_reada=%ld, f_ramax=%ld, f_raend=%ld, f_ralen=%ld, f_rawin=%ld\n", filp->f_reada, filp->f_ramax, filp->f_raend, filp->f_ralen, filp->f_rawin));
-
MSDOS_I (filp->f_dentry->d_inode)->i_binary = 2;
-
ret = fat_file_read (filp, buf, count, &filp->f_pos);
- PRINTK ((KERN_DEBUG "fat_file_read returned with %d!\n", ret));
-
- PRINTK ((KERN_DEBUG " (ret) inode=%lu, i_size=%lu\n", filp->f_dentry->d_inode->i_ino, filp->f_dentry->d_inode->i_size));
- PRINTK ((KERN_DEBUG " (ret) f_pos=%Lu\n", filp->f_pos));
- PRINTK ((KERN_DEBUG " (ret) name=%.*s\n", (int) filp->f_dentry->d_name.len, filp->f_dentry->d_name.name));
- PRINTK ((KERN_DEBUG " (ret) i_binary(sb)=%d\n", MSDOS_I (filp->f_dentry->d_inode)->i_binary));
- PRINTK ((KERN_DEBUG " (ret) f_count=%d, f_flags=%d\n", filp->f_count, filp->f_flags));
- PRINTK ((KERN_DEBUG " (ret) f_owner=%d\n", filp->f_owner.uid));
- PRINTK ((KERN_DEBUG " (ret) f_version=%ld\n", filp->f_version));
- PRINTK ((KERN_DEBUG " (ret) f_reada=%ld, f_ramax=%ld, f_raend=%ld, f_ralen=%ld, f_rawin=%ld\n", filp->f_reada, filp->f_ramax, filp->f_raend, filp->f_ralen, filp->f_rawin));
-
-#if 0
- {
- struct umsdos_dirent *mydirent = buf;
-
- Printk ((KERN_DEBUG " (DDD) uid=%d\n", mydirent->uid));
- Printk ((KERN_DEBUG " (DDD) gid=%d\n", mydirent->gid));
- Printk ((KERN_DEBUG " (DDD) name=>%.20s<\n", mydirent->name));
- }
-#endif
-
set_fs (old_fs);
return ret;
}
@@ -85,34 +51,30 @@ ssize_t umsdos_file_write_kmem_real (struct file * filp,
const char *buf,
size_t count)
{
- ssize_t ret;
mm_segment_t old_fs = get_fs ();
-
- set_fs (KERNEL_DS);
-
- PRINTK ((KERN_DEBUG "umsdos_file_write_kmem /mn/: Checkin: filp=%p, buf=%p, size=%d\n", filp, buf, count));
- PRINTK ((KERN_DEBUG " struct dentry=%p\n", filp->f_dentry));
- PRINTK ((KERN_DEBUG " struct inode=%p\n", filp->f_dentry->d_inode));
- PRINTK ((KERN_DEBUG " inode=%lu, i_size=%lu\n", filp->f_dentry->d_inode->i_ino, filp->f_dentry->d_inode->i_size));
- PRINTK ((KERN_DEBUG " f_pos=%Lu\n", filp->f_pos));
- PRINTK ((KERN_DEBUG " name=%.*s\n", (int) filp->f_dentry->d_name.len, filp->f_dentry->d_name.name));
- PRINTK ((KERN_DEBUG " i_binary(sb)=%d\n", MSDOS_I (filp->f_dentry->d_inode)->i_binary));
- PRINTK ((KERN_DEBUG " f_count=%d, f_flags=%d\n", filp->f_count, filp->f_flags));
- PRINTK ((KERN_DEBUG " f_owner=%d\n", filp->f_owner.uid));
- PRINTK ((KERN_DEBUG " f_version=%ld\n", filp->f_version));
- PRINTK ((KERN_DEBUG " f_reada=%ld, f_ramax=%ld, f_raend=%ld, f_ralen=%ld, f_rawin=%ld\n", filp->f_reada, filp->f_ramax, filp->f_raend, filp->f_ralen, filp->f_rawin));
+ ssize_t ret;
/* note: i_binary=2 is for CVF-FAT. We put it here, instead of
- * umsdos_file_write_kmem, since it is also wise not to compress symlinks
- * (in the unlikely event that they are > 512 bytes and can be compressed
- * FIXME: should we set it when reading symlinks too? */
+ * umsdos_file_write_kmem, since it is also wise not to compress
+ * symlinks (in the unlikely event that they are > 512 bytes and
+ * can be compressed.
+ * FIXME: should we set it when reading symlinks too?
+ */
MSDOS_I (filp->f_dentry->d_inode)->i_binary = 2;
+ set_fs (KERNEL_DS);
ret = fat_file_write (filp, buf, count, &filp->f_pos);
- Printk ((KERN_DEBUG "fat_file_write returned with %ld!\n", (long int) ret));
-
set_fs (old_fs);
+ if (ret < 0) {
+ printk(KERN_WARNING "umsdos_file_write: ret=%d\n", ret);
+ goto out;
+ }
+#ifdef UMSDOS_PARANOIA
+if (ret != count)
+printk(KERN_WARNING "umsdos_file_write: count=%u, ret=%u\n", count, ret);
+#endif
+out:
return ret;
}
@@ -125,9 +87,8 @@ ssize_t umsdos_file_write_kmem (struct file *filp,
const char *buf,
size_t count)
{
- int ret;
+ ssize_t ret;
- Printk ((KERN_DEBUG " STARTED WRITE_KMEM /mn/\n"));
ret = umsdos_file_write_kmem_real (filp, buf, count);
return ret;
}
@@ -166,7 +127,6 @@ ssize_t umsdos_emd_dir_write ( struct file *filp,
Printk (("umsdos_emd_dir_write /mn/: calling write_kmem with %p, %p, %d, %Ld\n",
filp, buf, count, filp->f_pos));
written = umsdos_file_write_kmem (filp, buf, count);
- Printk (("umsdos_emd_dir_write /mn/: write_kmem returned\n"));
#ifdef __BIG_ENDIAN
d->nlink = le16_to_cpu (d->nlink);
@@ -179,13 +139,13 @@ filp, buf, count, filp->f_pos));
d->mode = le16_to_cpu (d->mode);
#endif
-#if UMS_DEBUG
+#ifdef UMSDOS_PARANOIA
if (written != count)
printk(KERN_ERR "umsdos_emd_dir_write: ERROR: written (%d) != count (%d)\n",
written, count);
#endif
- return written != count ? -EIO : 0;
+ return (written != count) ? -EIO : 0;
}
@@ -199,9 +159,7 @@ written, count);
ssize_t umsdos_emd_dir_read (struct file *filp, char *buf, size_t count)
{
- long int ret = 0;
- int sizeread;
-
+ ssize_t sizeread, ret = 0;
#ifdef __BIG_ENDIAN
struct umsdos_dirent *d = (struct umsdos_dirent *) buf;
@@ -211,8 +169,9 @@ ssize_t umsdos_emd_dir_read (struct file *filp, char *buf, size_t count)
filp->f_flags = 0;
sizeread = umsdos_file_read_kmem (filp, buf, count);
if (sizeread != count) {
-printk ("UMSDOS: problem with EMD file: can't read pos = %Ld (%d != %d)\n",
-filp->f_pos, sizeread, count);
+ printk (KERN_WARNING
+ "UMSDOS: EMD problem, pos=%Ld, count=%d, read=%d\n",
+ filp->f_pos, count, sizeread);
ret = -EIO;
}
#ifdef __BIG_ENDIAN
@@ -226,24 +185,27 @@ filp->f_pos, sizeread, count);
d->mode = le16_to_cpu (d->mode);
#endif
return ret;
-
}
/*
- * Create the EMD dentry for a directory.
+ * Lookup the EMD dentry for a directory.
+ *
+ * Note: the caller must hold a lock on the parent directory.
*/
struct dentry *umsdos_get_emd_dentry(struct dentry *parent)
{
struct dentry *demd;
- demd = umsdos_lookup_dentry (parent, UMSDOS_EMD_FILE,
- UMSDOS_EMD_NAMELEN);
+ demd = umsdos_lookup_dentry(parent, UMSDOS_EMD_FILE,
+ UMSDOS_EMD_NAMELEN, 1);
return demd;
}
/*
* Check whether a directory has an EMD file.
+ *
+ * Note: the caller must hold a lock on the parent directory.
*/
int umsdos_have_emd(struct dentry *dir)
{
@@ -260,37 +222,38 @@ int umsdos_have_emd(struct dentry *dir)
/*
* Create the EMD file for a directory if it doesn't
- * already exist. Returns 0 or and error code.
+ * already exist. Returns 0 or an error code.
+ *
+ * Note: the caller must hold a lock on the parent directory.
*/
int umsdos_make_emd(struct dentry *parent)
{
struct dentry *demd = umsdos_get_emd_dentry(parent);
- struct inode *inode;
int err = PTR_ERR(demd);
- if (IS_ERR(demd))
+ if (IS_ERR(demd)) {
+ printk("umsdos_make_emd: can't get dentry in %s, err=%d\n",
+ parent->d_name.name, err);
goto out;
+ }
/* already created? */
- inode = demd->d_inode;
- if (inode) {
- parent->d_inode->u.umsdos_i.i_emd_dir = inode->i_ino;
- err = 0;
- goto out_dput;
- }
+ err = 0;
+ if (demd->d_inode)
+ goto out_set;
-printk("umsdos_make_emd: creating %s/%s\n",
-parent->d_name.name, demd->d_name.name);
+Printk(("umsdos_make_emd: creating EMD %s/%s\n",
+parent->d_name.name, demd->d_name.name));
err = msdos_create(parent->d_inode, demd, S_IFREG | 0777);
if (err) {
- printk (KERN_WARNING "UMSDOS: Can't create EMD file\n");
+ printk (KERN_WARNING
+ "umsdos_make_emd: create %s/%s failed, err=%d\n",
+ parent->d_name.name, demd->d_name.name, err);
goto out_dput;
}
- inode = demd->d_inode;
- parent->d_inode->u.umsdos_i.i_emd_dir = inode->i_ino;
- /* Disable UMSDOS_notify_change() for EMD file */
- inode->u.umsdos_i.i_emd_owner = 0xffffffff;
+out_set:
+ parent->d_inode->u.umsdos_i.i_emd_dir = demd->d_inode->i_ino;
out_dput:
dput(demd);
@@ -300,92 +263,6 @@ out:
/*
- * Locate the EMD file in a directory.
- *
- * Return NULL if error, dir->u.umsdos_i.emd_inode if OK.
- * Caller must iput() returned inode when finished with it!
- * Note: deprecated; get rid of this soon!
- */
-
-struct inode *umsdos_emd_dir_lookup (struct inode *dir, int creat)
-{
- struct inode *ret = NULL;
- struct dentry *d_dir=NULL, *dlook=NULL;
- int rv;
-
- Printk ((KERN_DEBUG "Entering umsdos_emd_dir_lookup\n"));
- if (!dir) {
- printk (KERN_CRIT "umsdos_emd_dir_lookup: FATAL, dir=NULL!\n");
- goto out;
- }
- check_inode (dir);
-
- if (dir->u.umsdos_i.i_emd_dir != 0) {
- ret = iget (dir->i_sb, dir->u.umsdos_i.i_emd_dir);
- Printk (("umsdos_emd_dir_lookup: deja trouve %ld %p\n",
- dir->u.umsdos_i.i_emd_dir, ret));
- goto out;
- }
-
- PRINTK ((KERN_DEBUG "umsdos /mn/: Looking for %.*s -",
- UMSDOS_EMD_NAMELEN, UMSDOS_EMD_FILE));
-
- d_dir = geti_dentry (dir);
- if (!d_dir) {
-printk("UMSDOS: flaky i_dentry hack failed\n");
- goto out;
- }
- dlook = creat_dentry (UMSDOS_EMD_FILE, UMSDOS_EMD_NAMELEN, NULL, d_dir);
- if (!dlook)
- goto out;
- rv = umsdos_real_lookup (dir, dlook);
-
- PRINTK ((KERN_DEBUG "-returned %d\n", rv));
- Printk ((KERN_INFO "emd_dir_lookup "));
-
- ret = dlook->d_inode;
- if (ret) {
- Printk (("Found --linux "));
- dir->u.umsdos_i.i_emd_dir = ret->i_ino;
- ret->i_count++; /* we'll need the inode */
- check_inode (ret);
- } else if (creat) {
- int code;
-
- Printk ((" * ERROR * /mn/: creat not yet implemented? not fixed? "));
- Printk (("avant create "));
-
- check_inode (ret);
- code = compat_msdos_create (dir, UMSDOS_EMD_FILE,
- UMSDOS_EMD_NAMELEN,
- S_IFREG | 0777, &ret);
- check_inode (ret);
- Printk (("Creat EMD code %d ret %p ", code, ret));
- if (ret != NULL) {
- Printk ((" ino=%lu", ret->i_ino));
- dir->u.umsdos_i.i_emd_dir = ret->i_ino;
- } else {
- printk (KERN_WARNING "UMSDOS: Can't create EMD file\n");
- }
- }
- dput(dlook);
-
- if (ret != NULL) {
- /* Disable UMSDOS_notify_change() for EMD file */
- ret->u.umsdos_i.i_emd_owner = 0xffffffff;
- }
-
-out:
-#if UMS_DEBUG
- Printk ((KERN_DEBUG "umsdos_emd_dir_lookup returning %p /mn/\n", ret));
- if (ret != NULL)
- Printk ((KERN_DEBUG " returning ino=%lu\n", ret->i_ino));
-#endif
- return ret;
-}
-
-
-/*
* Read an entry from the EMD file.
* Support variable length record.
* Return -EIO if error, 0 if OK.
@@ -425,6 +302,8 @@ Printk (("umsdos_emd_dir_readentry /mn/: returning len=%d,name=%.*s\n",
/*
* Write an entry in the EMD file.
* Return 0 if OK, -EIO if some error.
+ *
+ * Note: the caller must hold a lock on the parent directory.
*/
static int umsdos_writeentry (struct dentry *parent, struct umsdos_info *info,
int free_entry)
@@ -443,14 +322,15 @@ static int umsdos_writeentry (struct dentry *parent, struct umsdos_info *info,
/* make sure there's an EMD file */
ret = -EIO;
if (!emd_dentry->d_inode) {
-printk("umsdos_writeentry: no EMD file in %s/%s\n",
-parent->d_parent->d_name.name, parent->d_name.name);
+ printk(KERN_WARNING
+ "umsdos_writeentry: no EMD file in %s/%s\n",
+ parent->d_parent->d_name.name, parent->d_name.name);
goto out_dput;
}
if (free_entry) {
/* #Specification: EMD file / empty entries
- * Unused entry in the EMD file are identified
+ * Unused entries in the EMD file are identified
* by the name_len field equal to 0. However to
* help future extension (or bug correction :-( ),
* empty entries are filled with 0.
@@ -506,6 +386,8 @@ struct find_buffer {
* Unread bytes are simply moved to the beginning.
*
* Return -ENOENT if EOF, 0 if OK, a negative error code if any problem.
+ *
+ * Note: the caller must hold a lock on the parent directory.
*/
static int umsdos_fillbuf (struct find_buffer *buf)
@@ -556,6 +438,7 @@ static int umsdos_fillbuf (struct find_buffer *buf)
* All this to say that umsdos_writeentry must be called after this
* function since it relies on the f_pos field of info.
*
+ * Note: the caller must hold a lock on the parent directory.
*/
/* #Specification: EMD file structure
* The EMD file uses a fairly simple layout. It is made of records
@@ -662,6 +545,8 @@ demd->d_parent->d_name.name, demd->d_name.name, emd_dir));
}
}
}
+Printk(("umsdos_find: ready to mangle %s, len=%d, pos=%ld\n",
+entry->name, entry->name_len, (long)info->f_pos));
umsdos_manglename (info);
out_dput:
@@ -795,8 +680,8 @@ int umsdos_isempty (struct dentry *dentry)
out_dput:
dput(demd);
out:
-printk("umsdos_isempty: checked %s/%s, empty=%d\n",
-dentry->d_parent->d_name.name, dentry->d_name.name, ret);
+Printk(("umsdos_isempty: checked %s/%s, empty=%d\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, ret));
return ret;
}
@@ -805,11 +690,11 @@ dentry->d_parent->d_name.name, dentry->d_name.name, ret);
* Locate an entry in a EMD directory.
* Return 0 if OK, error code if not, generally -ENOENT.
*
- * does not change i_count
+ * expect argument:
+ * 0: anything
+ * 1: file
+ * 2: directory
*/
-/* 0: anything */
-/* 1: file */
-/* 2: directory */
int umsdos_findentry (struct dentry *parent, struct umsdos_info *info,
int expect)
@@ -820,14 +705,16 @@ int umsdos_findentry (struct dentry *parent, struct umsdos_info *info,
if (ret)
goto out;
- if (expect != 0) {
- if (S_ISDIR (info->entry.mode)) {
- if (expect != 2)
- ret = -EISDIR;
- } else if (expect == 2) {
+ switch (expect) {
+ case 1:
+ if (S_ISDIR (info->entry.mode))
+ ret = -EISDIR;
+ break;
+ case 2:
+ if (!S_ISDIR (info->entry.mode))
ret = -ENOTDIR;
- }
}
+
out:
Printk (("umsdos_findentry: returning %d\n", ret));
return ret;
diff --git a/fs/umsdos/file.c b/fs/umsdos/file.c
index 5277c9548..339d82801 100644
--- a/fs/umsdos/file.c
+++ b/fs/umsdos/file.c
@@ -32,15 +32,12 @@ static ssize_t UMSDOS_file_read (
struct dentry *dentry = filp->f_dentry;
struct inode *inode = dentry->d_inode;
- /* We have to set the access time because msdos don't care */
- /* FIXME */
int ret = fat_file_read (filp, buf, count, ppos);
+ /* We have to set the access time because msdos don't care */
if (!IS_RDONLY (inode)) {
inode->i_atime = CURRENT_TIME;
- /* FIXME
- * inode->i_dirt = 1;
- */
+ mark_inode_dirty(inode);
}
return ret;
}
@@ -65,10 +62,11 @@ static ssize_t UMSDOS_file_write (
static void UMSDOS_truncate (struct inode *inode)
{
Printk (("UMSDOS_truncate\n"));
- fat_truncate (inode);
- inode->i_ctime = inode->i_mtime = CURRENT_TIME;
-
- /*FIXME inode->i_dirt = 1; */
+ if (!IS_RDONLY (inode)) {
+ fat_truncate (inode);
+ inode->i_ctime = inode->i_mtime = CURRENT_TIME;
+ mark_inode_dirty(inode);
+ }
}
/* Function for normal file system (512 bytes hardware sector size) */
diff --git a/fs/umsdos/inode.c b/fs/umsdos/inode.c
index ca20bb8c3..ef6e56dc3 100644
--- a/fs/umsdos/inode.c
+++ b/fs/umsdos/inode.c
@@ -19,38 +19,15 @@
#include <linux/umsdos_fs.h>
#include <linux/list.h>
+extern struct dentry_operations umsdos_dentry_operations;
extern struct inode_operations umsdos_rdir_inode_operations;
+struct dentry *saved_root = NULL; /* Original root if changed */
struct inode *pseudo_root = NULL; /* Useful to simulate the pseudo DOS */
/* directory. See UMSDOS_readdir_x() */
-
-/*
- * returns inode->i_dentry
- * Note: Deprecated; won't work reliably
- */
-
-struct dentry *geti_dentry (struct inode *inode)
-{
- struct dentry *ret;
-
- if (!inode) {
- printk (KERN_ERR "geti_dentry: ERROR: inode is NULL!\n");
- return NULL;
- }
- if (list_empty(&inode->i_dentry)) {
- printk (KERN_WARNING
- "geti_dentry: WARNING: no dentry for inode %ld\n",
- inode->i_ino);
- return NULL;
- }
- ret = list_entry (inode->i_dentry.next, struct dentry, d_alias);
-
- PRINTK ((KERN_DEBUG "geti_dentry : inode %lu, dentry is %s/%s\n",
- inode->i_ino, ret->d_parent->d_name.name, ret->d_name.name));
- return ret;
-}
+static struct dentry *check_pseudo_root(struct super_block *);
/*
@@ -59,7 +36,7 @@ struct dentry *geti_dentry (struct inode *inode)
void fill_new_filp (struct file *filp, struct dentry *dentry)
{
if (!dentry)
- printk("fill_new_filp: NULL dentry!\n");
+ printk(KERN_ERR "fill_new_filp: NULL dentry!\n");
memset (filp, 0, sizeof (struct file));
filp->f_reada = 1;
@@ -69,61 +46,6 @@ void fill_new_filp (struct file *filp, struct dentry *dentry)
}
-/*
- * makes dentry. for name name with length len.
- * if inode is not NULL, puts it also.
- * Note: Deprecated; use umsdos_lookup_dentry
- */
-
-struct dentry *creat_dentry (const char *name, const int len,
- struct inode *inode, struct dentry *parent)
-{
-/* FIXME /mn/: parent is not passed many times... if it is not, dentry should be destroyed before someone else gets to use it */
-
- struct dentry *ret;
- struct qstr qname;
-
- if (inode)
- Printk ((KERN_DEBUG "creat_dentry: creating dentry with inode=%lu for %.*s\n", inode->i_ino, len, name));
- else
- Printk ((KERN_DEBUG "creat_dentry: creating empty dentry for %.*s\n", len, name));
-
- qname.name = name;
- qname.len = len;
- qname.hash = full_name_hash (name, len);
-
- ret = d_alloc (parent, &qname); /* create new dentry */
-
- if (parent) {
-#if 0
- Printk ((KERN_DEBUG "creat_dentry: cloning parent d_op !\n"));
- ret->d_op = parent->d_op;
-#else
- ret->d_op = NULL;
-#endif
- } else {
- ret->d_parent = ret;
- Printk ((KERN_WARNING "creat_dentry: WARNING: NO parent! faking root! beware !\n"));
- }
-
-
- if (inode) {
- /* try to fill it in if available. If available in
- * parent->d_sb, d_alloc will add it automatically
- */
- if (!ret->d_sb) ret->d_sb = inode->i_sb;
- d_add (ret, inode);
- }
-
- if (!ret->d_sb) {
- printk (KERN_ERR "creat_dentry: ERROR: NO d_sb !\n");
- } else if (!ret->d_sb->s_dev) {
- printk (KERN_WARNING "creat_dentry: WARNING: NO s_dev. Ugh. !\n");
- }
-
- return ret;
-}
-
void UMSDOS_put_inode (struct inode *inode)
{
@@ -133,11 +55,12 @@ void UMSDOS_put_inode (struct inode *inode)
,inode->u.umsdos_i.i_emd_owner, inode->u.umsdos_i.pos
,inode->u.umsdos_i.i_emd_dir, inode->i_count));
- if (inode && pseudo_root && inode == pseudo_root) {
+ if (inode == pseudo_root) {
printk (KERN_ERR "Umsdos: Oops releasing pseudo_root."
" Notify jacques@solucorp.qc.ca\n");
}
+ inode->u.umsdos_i.i_patched = 0;
fat_put_inode (inode);
}
@@ -145,35 +68,23 @@ void UMSDOS_put_inode (struct inode *inode)
void UMSDOS_put_super (struct super_block *sb)
{
Printk ((KERN_DEBUG "UMSDOS_put_super: entering\n"));
+ if (saved_root) {
+ shrink_dcache_parent(saved_root);
+printk("UMSDOS_put_super: freeing saved root, d_count=%d\n",
+saved_root->d_count);
+ dput(saved_root);
+ saved_root = NULL;
+ pseudo_root = NULL;
+ }
msdos_put_super (sb);
MOD_DEC_USE_COUNT;
}
/*
- * Call msdos_lookup, but set back the original msdos function table.
- * Return 0 if OK, or a negative error code if not.
- * Dentry will hold inode of the file, if successful
- */
-int umsdos_real_lookup (struct inode *dir, struct dentry *dentry)
-{
- int ret;
-
- PRINTK ((KERN_DEBUG "umsdos_real_lookup: looking for %s/%s /",
- dentry->d_parent->d_name.name, dentry->d_name.name));
- ret = msdos_lookup (dir, dentry);
- PRINTK (("/ returned %d\n", ret));
-
- return ret;
-}
-
-/*
- * Complete the setup of an directory dentry.
- * First, it completes the function pointers, then
- * it locates the EMD file. If the EMD is there, then plug the
+ * Complete the setup of a directory dentry based on its
+ * EMD/non-EMD status. If it has an EMD, then plug the
* umsdos function table. If not, use the msdos one.
- *
- * {i,d}_counts are untouched by this function.
*/
void umsdos_setup_dir(struct dentry *dir)
{
@@ -192,89 +103,32 @@ dir->d_parent->d_name.name, dir->d_name.name));
}
}
-/*
- * Complete the setup of an directory inode.
- * First, it completes the function pointers, then
- * it locates the EMD file. If the EMD is there, then plug the
- * umsdos function table. If not, use the msdos one.
- *
- * {i,d}_counts are untouched by this function.
- * Note: Deprecated; use above function if possible.
- */
-void umsdos_setup_dir_inode (struct inode *inode)
-{
- struct inode *emd_dir;
-
- inode->u.umsdos_i.i_emd_dir = 0;
-
- Printk ((KERN_DEBUG
- "umsdos_setup_dir_inode: Entering for inode=%lu\n",
- inode->i_ino));
- check_inode (inode);
- emd_dir = umsdos_emd_dir_lookup (inode, 0);
- Printk ((KERN_DEBUG "umsdos_setup_dir_inode: "
- "umsdos_emd_dir_lookup for inode=%lu returned %p\n",
- inode->i_ino, emd_dir));
- check_inode (inode);
- check_inode (emd_dir);
-
- inode->i_op = &umsdos_rdir_inode_operations;
- if (emd_dir) {
- Printk ((KERN_DEBUG "umsdos_setup_dir_inode: using EMD.\n"));
- inode->i_op = &umsdos_dir_inode_operations;
- iput (emd_dir);
- }
-}
-
/*
* Add some info into an inode so it can find its owner quickly
*/
-void umsdos_set_dirinfo (struct inode *inode, struct inode *dir, off_t f_pos)
+void umsdos_set_dirinfo_new (struct dentry *dentry, off_t f_pos)
{
- struct inode *emd_owner = umsdos_emd_dir_lookup (dir, 1);
+ struct inode *inode = dentry->d_inode;
+ struct dentry *demd;
- if (!emd_owner)
- goto out;
- Printk (("umsdos_set_dirinfo: emd_owner is %lu for dir %lu\n",
- emd_owner->i_ino, dir->i_ino));
- inode->u.umsdos_i.i_dir_owner = dir->i_ino;
- inode->u.umsdos_i.i_emd_owner = emd_owner->i_ino;
+ inode->u.umsdos_i.i_emd_owner = 0;
inode->u.umsdos_i.pos = f_pos;
- iput (emd_owner);
-out:
+
+ /* now check the EMD file */
+ demd = umsdos_get_emd_dentry(dentry->d_parent);
+ if (!IS_ERR(demd)) {
+ if (demd->d_inode)
+ inode->u.umsdos_i.i_emd_owner = demd->d_inode->i_ino;
+ dput(demd);
+ }
return;
}
-/*
- * Tells if an Umsdos inode has been "patched" once.
- * Return != 0 if so.
- */
-int umsdos_isinit (struct inode *inode)
-{
- return inode->u.umsdos_i.i_emd_owner != 0;
-}
-
/*
* Connect the proper tables in the inode and add some info.
- * i_counts is not changed.
- *
- * This function is called very early to setup the inode, somewhat
- * too early (called by UMSDOS_read_inode). At this point, we can't
- * do too much, such as lookup up EMD files and so on. This causes
- * confusion in the kernel. This is why some initialisation
- * will be done when dir != NULL only.
- *
- * UMSDOS do run piggy back on top of msdos fs. It looks like something
- * is missing in the VFS to accommodate stacked fs. Still unclear what
- * (quite honestly).
- *
- * Well, maybe one! A new entry "may_unmount" which would allow
- * the stacked fs to allocate some inode permanently and release
- * them at the end. Doing that now introduce a problem. unmount
- * always fail because some inodes are in use.
*/
/* #Specification: inode / umsdos info
* The first time an inode is seen (inode->i_count == 1),
@@ -282,88 +136,45 @@ int umsdos_isinit (struct inode *inode)
* is tagged to this inode. It allows operations such as
* notify_change to be handled.
*/
-void umsdos_patch_inode (struct inode *inode, struct inode *dir, off_t f_pos)
+void umsdos_patch_dentry_inode(struct dentry *dentry, off_t f_pos)
{
- Printk ((KERN_DEBUG "Entering umsdos_patch_inode for inode=%lu\n",
- inode->i_ino));
+ struct inode *inode = dentry->d_inode;
- if (umsdos_isinit (inode))
- goto already_init;
+PRINTK (("umsdos_patch_dentry_inode: inode=%lu\n", inode->i_ino));
+
+ /*
+ * Classify the inode based on EMD/non-EMD status.
+ */
+PRINTK (("umsdos_patch_inode: call umsdos_set_dirinfo_new(%p,%lu)\n",
+dentry, f_pos));
+ umsdos_set_dirinfo_new(dentry, f_pos);
inode->u.umsdos_i.i_emd_dir = 0;
if (S_ISREG (inode->i_mode)) {
if (MSDOS_SB (inode->i_sb)->cvf_format) {
if (MSDOS_SB (inode->i_sb)->cvf_format->flags & CVF_USE_READPAGE) {
-Printk ((KERN_DEBUG "umsdos_patch_inode /mn/: setting i_op = umsdos_file_inode_operations_readpage\n"));
inode->i_op = &umsdos_file_inode_operations_readpage;
} else {
-Printk ((KERN_DEBUG "umsdos_patch_inode: umsdos_file_inode_ops_no_bmap\n"));
inode->i_op = &umsdos_file_inode_operations_no_bmap;
}
} else {
if (inode->i_op->bmap != NULL) {
-Printk ((KERN_DEBUG "umsdos_patch_inode: umsdos_file_inode_ops\n"));
inode->i_op = &umsdos_file_inode_operations;
} else {
-Printk ((KERN_DEBUG "umsdos_patch_inode: umsdos_file_inode_ops_no_bmap\n"));
inode->i_op = &umsdos_file_inode_operations_no_bmap;
}
}
} else if (S_ISDIR (inode->i_mode)) {
- if (dir != NULL) {
- umsdos_setup_dir_inode (inode);
- }
+ umsdos_setup_dir(dentry);
} else if (S_ISLNK (inode->i_mode)) {
- Printk ((KERN_DEBUG
- "umsdos_patch_inode: umsdos_symlink_inode_ops\n"));
inode->i_op = &umsdos_symlink_inode_operations;
} else if (S_ISCHR (inode->i_mode)) {
- Printk ((KERN_DEBUG "umsdos_patch_inode: chrdev_inode_ops\n"));
inode->i_op = &chrdev_inode_operations;
} else if (S_ISBLK (inode->i_mode)) {
- Printk ((KERN_DEBUG "umsdos_patch_inode: blkdev_inode_ops\n"));
inode->i_op = &blkdev_inode_operations;
} else if (S_ISFIFO (inode->i_mode)) {
- Printk ((KERN_DEBUG "umsdos_patch_inode /mn/: uhm, init_fifo\n"));
init_fifo (inode);
}
- if (dir != NULL) {
- /*
- * This is done last because it also control the
- * status of umsdos_isinit()
- */
- Printk ((KERN_DEBUG
- "umsdos_patch_inode: call x_set_dirinfo(%p,%p,%lu)\n",
- inode, dir, f_pos));
- umsdos_set_dirinfo (inode, dir, f_pos);
- }
- return;
-
-already_init:
- if (dir != NULL) {
- /*
- * Test to see if the info is maintained.
- * This should be removed when the file system will be proven.
- */
- struct inode *emd_owner = umsdos_emd_dir_lookup (dir, 1);
- if (!emd_owner)
- goto out;
- if (emd_owner->i_ino != inode->u.umsdos_i.i_emd_owner) {
-printk ("UMSDOS: *** EMD_OWNER ??? *** ino = %ld %ld <> %ld ",
-inode->i_ino, emd_owner->i_ino, inode->u.umsdos_i.i_emd_owner);
- }
- iput (emd_owner);
- out:
- return;
- }
-}
-
-/*
- * Patch the inode in a dentry.
- */
-void umsdos_patch_dentry_inode(struct dentry *dentry, off_t f_pos)
-{
- umsdos_patch_inode(dentry->d_inode, dentry->d_parent->d_inode, f_pos);
}
@@ -373,127 +184,142 @@ void umsdos_patch_dentry_inode(struct dentry *dentry, off_t f_pos)
/* #Specification: Inode / post initialisation
* To completely initialise an inode, we need access to the owner
* directory, so we can locate more info in the EMD file. This is
- * not available the first time the inode is access, we use
+ * not available the first time the inode is accessed, so we use
* a value in the inode to tell if it has been finally initialised.
*
- * At first, we have tried testing i_count but it was causing
- * problem. It is possible that two or more process use the
- * newly accessed inode. While the first one block during
- * the initialisation (probably while reading the EMD file), the
- * others believe all is well because i_count > 1. They go banana
- * with a broken inode. See umsdos_lookup_patch and umsdos_patch_inode.
+ * New inodes are obtained by the lookup and create routines, and
+ * each of these must ensure that the inode gets patched.
*/
void UMSDOS_read_inode (struct inode *inode)
{
- PRINTK ((KERN_DEBUG "UMSDOS_read_inode %p ino = %lu ",
+ Printk ((KERN_DEBUG "UMSDOS_read_inode %p ino = %lu ",
inode, inode->i_ino));
msdos_read_inode (inode);
- PRINTK (("ino after msdos_read_inode= %lu i_count=%d\n",
- inode->i_ino, inode->i_count));
- if (S_ISDIR (inode->i_mode)
- && (inode->u.umsdos_i.u.dir_info.creating != 0
- || inode->u.umsdos_i.u.dir_info.looking != 0
- || waitqueue_active (&inode->u.umsdos_i.u.dir_info.p))) {
- Printk (("read inode %d %d %p\n"
- ,inode->u.umsdos_i.u.dir_info.creating
- ,inode->u.umsdos_i.u.dir_info.looking
- ,inode->u.umsdos_i.u.dir_info.p));
- }
- /* N.B. Defer this until we have a dentry ... */
- umsdos_patch_inode (inode, NULL, 0);
+ /* inode needs patching */
+ inode->u.umsdos_i.i_patched = 0;
}
-/* #Specification: notify_change / i_nlink > 0
- * notify change is only done for inode with nlink > 0. An inode
- * with nlink == 0 is no longer associated with any entry in
- * the EMD file, so there is nothing to update.
+int umsdos_notify_change_locked(struct dentry *, struct iattr *);
+/*
+ * lock the parent dir before starting ...
*/
-static int internal_notify_change (struct inode *inode, struct iattr *attr)
+int UMSDOS_notify_change (struct dentry *dentry, struct iattr *attr)
{
- unsigned long i_emd_owner = inode->u.umsdos_i.i_emd_owner;
+ struct inode *dir = dentry->d_parent->d_inode;
int ret;
- Printk ((KERN_DEBUG "UMSDOS_notify_change: entering\n"));
+ down(&dir->i_sem);
+ ret = umsdos_notify_change_locked(dentry, attr);
+ up(&dir->i_sem);
+ return ret;
+}
- if ((ret = inode_change_ok (inode, attr)) != 0)
+/*
+ * Must be called with the parent lock held.
+ */
+int umsdos_notify_change_locked(struct dentry *dentry, struct iattr *attr)
+{
+ struct inode *inode = dentry->d_inode;
+ struct dentry *demd;
+ int ret;
+ struct file filp;
+ struct umsdos_dirent entry;
+
+Printk(("UMSDOS_notify_change: entering for %s/%s (%d)\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, inode->u.umsdos_i.i_patched));
+
+ ret = inode_change_ok (inode, attr);
+ if (ret) {
+printk("UMSDOS_notify_change: %s/%s change not OK, ret=%d\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, ret);
goto out;
+ }
if (inode->i_nlink == 0)
- goto out_nolink;
-
+ goto out;
if (inode->i_ino == UMSDOS_ROOT_INO)
- goto out_nolink;
-
- if (i_emd_owner != 0xffffffff && i_emd_owner != 0) {
- /* This inode is not a EMD file nor an inode used internally
- * by MSDOS, so we can update its status.
- * See emd.c
- */
- struct inode *emd_owner;
- struct file filp;
- struct umsdos_dirent entry;
- struct dentry *emd_dentry;
-
- Printk (("notify change %p ", inode));
- ret = -EPERM;
- emd_owner = iget (inode->i_sb, i_emd_owner);
- if (!emd_owner) {
- printk ("UMSDOS: emd_owner = NULL ???");
- goto out_nolink;
- }
- emd_dentry = geti_dentry (emd_owner); /* FIXME? */
- fill_new_filp (&filp, emd_dentry);
-
- filp.f_pos = inode->u.umsdos_i.pos;
- filp.f_reada = 0;
- Printk (("pos = %Lu ", filp.f_pos));
- /* Read only the start of the entry since we don't touch */
- /* the name */
- ret = umsdos_emd_dir_read (&filp, (char *) &entry,
- UMSDOS_REC_SIZE);
- if (!ret) {
- if (attr->ia_valid & ATTR_UID)
- entry.uid = attr->ia_uid;
- if (attr->ia_valid & ATTR_GID)
- entry.gid = attr->ia_gid;
- if (attr->ia_valid & ATTR_MODE)
- entry.mode = attr->ia_mode;
- if (attr->ia_valid & ATTR_ATIME)
- entry.atime = attr->ia_atime;
- if (attr->ia_valid & ATTR_MTIME)
- entry.mtime = attr->ia_mtime;
- if (attr->ia_valid & ATTR_CTIME)
- entry.ctime = attr->ia_ctime;
-
- entry.nlink = inode->i_nlink;
- filp.f_pos = inode->u.umsdos_i.pos;
- ret = umsdos_emd_dir_write (&filp, (char *) &entry,
- UMSDOS_REC_SIZE);
-
- Printk (("notify pos %lu ret %d nlink %d ",
- inode->u.umsdos_i.pos, ret, entry.nlink));
- /* #Specification: notify_change / msdos fs
- * notify_change operation are done only on the
- * EMD file. The msdos fs is not even called.
- */
- }
- iput(emd_owner);
+ goto out;
+
+ /* get the EMD file dentry */
+ demd = umsdos_get_emd_dentry(dentry->d_parent);
+ ret = PTR_ERR(demd);
+ if (IS_ERR(demd))
+ goto out;
+ ret = -EPERM;
+ if (!demd->d_inode) {
+ printk(KERN_WARNING
+ "UMSDOS_notify_change: no EMD file %s/%s\n",
+ demd->d_parent->d_name.name, demd->d_name.name);
+ goto out_dput;
+ }
+
+ ret = 0;
+ /* don't do anything if this is the EMD itself */
+ if (inode == demd->d_inode)
+ goto out_dput;
+
+ /* This inode is not a EMD file nor an inode used internally
+ * by MSDOS, so we can update its status.
+ * See emd.c
+ */
+
+ fill_new_filp (&filp, demd);
+ filp.f_pos = inode->u.umsdos_i.pos;
+Printk(("UMSDOS_notify_change: %s/%s reading at %d\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, (int) filp.f_pos));
+
+ /* Read only the start of the entry since we don't touch the name */
+ ret = umsdos_emd_dir_read (&filp, (char *) &entry, UMSDOS_REC_SIZE);
+ if (ret) {
+ printk(KERN_WARNING
+ "umsdos_notify_change: %s/%s EMD read error, ret=%d\n",
+ dentry->d_parent->d_name.name, dentry->d_name.name,ret);
+ goto out_dput;
}
-out_nolink:
+ if (attr->ia_valid & ATTR_UID)
+ entry.uid = attr->ia_uid;
+ if (attr->ia_valid & ATTR_GID)
+ entry.gid = attr->ia_gid;
+ if (attr->ia_valid & ATTR_MODE)
+ entry.mode = attr->ia_mode;
+ if (attr->ia_valid & ATTR_ATIME)
+ entry.atime = attr->ia_atime;
+ if (attr->ia_valid & ATTR_MTIME)
+ entry.mtime = attr->ia_mtime;
+ if (attr->ia_valid & ATTR_CTIME)
+ entry.ctime = attr->ia_ctime;
+
+ entry.nlink = inode->i_nlink;
+ filp.f_pos = inode->u.umsdos_i.pos;
+ ret = umsdos_emd_dir_write (&filp, (char *) &entry, UMSDOS_REC_SIZE);
+ if (ret)
+ printk(KERN_WARNING
+ "umsdos_notify_change: %s/%s EMD write error, ret=%d\n",
+ dentry->d_parent->d_name.name, dentry->d_name.name,ret);
+
+ Printk (("notify pos %lu ret %d nlink %d ",
+ inode->u.umsdos_i.pos, ret, entry.nlink));
+ /* #Specification: notify_change / msdos fs
+ * notify_change operation are done only on the
+ * EMD file. The msdos fs is not even called.
+ */
+#ifdef UMSDOS_DEBUG_VERBOSE
+if (entry.flags & UMSDOS_HIDDEN)
+printk("umsdos_notify_change: %s/%s hidden, nlink=%d, ret=%d\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, entry.nlink, ret);
+#endif
+
+out_dput:
+ dput(demd);
+out:
if (ret == 0)
inode_setattr (inode, attr);
-out:
return ret;
}
-int UMSDOS_notify_change (struct dentry *dentry, struct iattr *attr)
-{
- return internal_notify_change (dentry->d_inode, attr);
-}
-
/*
* Update the disk with the inode content
*/
@@ -514,7 +340,8 @@ void UMSDOS_write_inode (struct inode *inode)
* But it has the side effect to re"dirt" the inode.
*/
/*
- * internal_notify_change (inode, &newattrs);
+ * UMSDOS_notify_change (inode, &newattrs);
+
* inode->i_state &= ~I_DIRTY; / * FIXME: this doesn't work. We need to remove ourselves from list on dirty inodes. /mn/ */
}
@@ -539,7 +366,7 @@ struct super_block *UMSDOS_read_super (struct super_block *sb, void *data,
int silent)
{
struct super_block *res;
- struct inode *pseudo = NULL;
+ struct dentry *new_root;
MOD_INC_USE_COUNT;
MSDOS_SB(sb)->options.isvfat = 0;
@@ -551,28 +378,38 @@ struct super_block *UMSDOS_read_super (struct super_block *sb, void *data,
if (!res)
goto out_fail;
- printk (KERN_INFO "UMSDOS dentry-WIP-Beta 0.82-7 "
+ printk (KERN_INFO "UMSDOS dentry-pre 0.84 "
"(compatibility level %d.%d, fast msdos)\n",
UMSDOS_VERSION, UMSDOS_RELEASE);
sb->s_op = &umsdos_sops;
MSDOS_SB(sb)->options.dotsOK = 0; /* disable hidden==dotfile */
- /* FIXME:?? clear d_op on root so it will not be inherited */
- sb->s_root->d_op = NULL;
+ /* install our dentry operations ... */
+ sb->s_root->d_op = &umsdos_dentry_operations;
+ umsdos_patch_dentry_inode(sb->s_root, 0);
+
+ /* Check whether to change to the /linux root */
+ new_root = check_pseudo_root(sb);
- pseudo = sb->s_root->d_inode;
- umsdos_setup_dir(sb->s_root);
+ if (new_root) {
+ /* sanity check */
+ if (new_root->d_op != &umsdos_dentry_operations)
+ printk("umsdos_read_super: pseudo-root wrong ops!\n");
-#if 0
- if (pseudo) {
- pseudo_root_stuff();
+ pseudo_root = new_root->d_inode;
+
+ saved_root = sb->s_root;
+ sb->s_root = new_root;
+ printk(KERN_INFO "UMSDOS: changed to alternate root\n");
}
-#endif
/* if d_count is not 1, mount will fail with -EBUSY! */
if (sb->s_root->d_count > 1) {
shrink_dcache_sb(sb);
+ if (sb->s_root->d_count > 1) {
+ printk(KERN_ERR "UMSDOS: root count %d > 1 !", sb->s_root->d_count);
+ }
}
return sb;
@@ -584,78 +421,50 @@ out_fail:
}
/*
- * FIXME URGENT:
- * disable pseudo root-for the moment of testing.
- * re-enable this before release !
+ * Check for an alternate root if we're the root device.
*/
-#if 0
-void pseudo_root_stuff(void)
+static struct dentry *check_pseudo_root(struct super_block *sb)
{
- struct dentry *root, *etc, *etc_rc, *sbin, *init = NULL;
-
- root = creat_dentry (UMSDOS_PSDROOT_NAME,
- strlen (UMSDOS_PSDROOT_NAME),
- NULL, res->s_root);
- sbin = creat_dentry ("sbin", 4, NULL, root);
+ struct dentry *root, *init;
- Printk ((KERN_DEBUG "Mounting root\n"));
- if (umsdos_real_lookup (pseudo, root) == 0
- && (root->d_inode != NULL)
- && S_ISDIR (root->d_inode->i_mode)) {
-
- int pseudo_ok = 0;
-
-Printk ((KERN_DEBUG "/%s is there\n", UMSDOS_PSDROOT_NAME));
- etc = creat_dentry ("etc", 3, NULL, root);
-
-
- if (umsdos_real_lookup (pseudo, etc) == 0
- && S_ISDIR (etc->d_inode->i_mode)) {
-
-Printk ((KERN_DEBUG "/%s/etc is there\n", UMSDOS_PSDROOT_NAME));
-
- init = creat_dentry ("init", 4, NULL, etc);
- etc_rc = creat_dentry ("rc", 2, NULL, etc);
-
- if ((umsdos_real_lookup (pseudo, init) == 0
- && S_ISREG (init->d_inode->i_mode))
- || (umsdos_real_lookup (pseudo, etc_rc) == 0
- && S_ISREG (etc_rc->d_inode->i_mode))) {
- pseudo_ok = 1;
- }
-
- /* FIXME !!!!!! */
- /* iput(init); */
- /* iput(rc); */
- }
- if (!pseudo_ok
- /* && umsdos_real_lookup (pseudo, "sbin", 4, sbin)==0 */
- && umsdos_real_lookup (pseudo, sbin) == 0
- && S_ISDIR (sbin->d_inode->i_mode)) {
-
-Printk ((KERN_DEBUG "/%s/sbin is there\n", UMSDOS_PSDROOT_NAME));
- if (umsdos_real_lookup (pseudo, init) == 0
- && S_ISREG (init->d_inode->i_mode)) {
- pseudo_ok = 1;
- }
- /* FIXME !!!
- * iput (init); */
- }
- if (pseudo_ok) {
- umsdos_setup_dir_inode (pseudo);
-Printk ((KERN_INFO "Activating pseudo root /%s\n", UMSDOS_PSDROOT_NAME));
- pseudo_root = pseudo;
- inc_count (pseudo);
- pseudo = NULL;
- }
- /* FIXME
- *
- * iput (sbin);
- * iput (etc);
- */
+ /*
+ * Check whether we're mounted as the root device.
+ * If so, this should be the only superblock.
+ */
+ if (sb->s_list.next->next != &sb->s_list)
+ goto out_noroot;
+printk("check_pseudo_root: mounted as root\n");
+
+ root = lookup_dentry(UMSDOS_PSDROOT_NAME, dget(sb->s_root), 0);
+ if (IS_ERR(root))
+ goto out_noroot;
+ if (!root->d_inode)
+ goto out_dput;
+printk("check_pseudo_root: found %s/%s\n",
+root->d_parent->d_name.name, root->d_name.name);
+
+ /* look for /sbin/init */
+ init = lookup_dentry("sbin/init", dget(root), 0);
+ if (!IS_ERR(init)) {
+ if (init->d_inode)
+ goto root_ok;
+ dput(init);
}
+ /* check for other files? */
+ goto out_dput;
+
+root_ok:
+printk("check_pseudo_root: found %s/%s, enabling pseudo-root\n",
+init->d_parent->d_name.name, init->d_name.name);
+ dput(init);
+ return root;
+
+ /* Alternate root not found ... */
+out_dput:
+ dput(root);
+out_noroot:
+ return NULL;
}
-#endif
static struct file_system_type umsdos_fs_type =
diff --git a/fs/umsdos/ioctl.c b/fs/umsdos/ioctl.c
index 84e1f5034..f26d19ba8 100644
--- a/fs/umsdos/ioctl.c
+++ b/fs/umsdos/ioctl.c
@@ -78,6 +78,9 @@ int UMSDOS_ioctl_dir(struct inode *dir, struct file *filp, unsigned int cmd,
struct file new_filp;
struct umsdos_ioctl data;
+Printk(("UMSDOS_ioctl_dir: %s/%s, cmd=%d, data=%08lx\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, cmd, data_ptr));
+
/* forward non-umsdos ioctls - this hopefully doesn't cause conflicts */
if (cmd != UMSDOS_GETVERSION
&& cmd != UMSDOS_READDIR_DOS
@@ -164,20 +167,19 @@ int UMSDOS_ioctl_dir(struct inode *dir, struct file *filp, unsigned int cmd,
ret = PTR_ERR(demd);
if (IS_ERR(demd))
goto out;
+ ret = 0;
+ if (!demd->d_inode)
+ goto read_dput;
+
fill_new_filp(&new_filp, demd);
new_filp.f_pos = filp->f_pos;
-
- while (1) {
+ while (new_filp.f_pos < demd->d_inode->i_size) {
off_t f_pos = new_filp.f_pos;
struct umsdos_dirent entry;
struct umsdos_info info;
- ret = 0;
- if (new_filp.f_pos >= demd->d_inode->i_size)
- break;
-
ret = umsdos_emd_dir_readentry (&new_filp, &entry);
- if (ret < 0)
+ if (ret)
break;
if (entry.name_len <= 0)
continue;
@@ -198,6 +200,7 @@ int UMSDOS_ioctl_dir(struct inode *dir, struct file *filp, unsigned int cmd,
}
/* update the original f_pos */
filp->f_pos = new_filp.f_pos;
+ read_dput:
d_drop(demd);
dput(demd);
goto out;
@@ -217,6 +220,8 @@ int UMSDOS_ioctl_dir(struct inode *dir, struct file *filp, unsigned int cmd,
extern struct inode_operations umsdos_rdir_inode_operations;
ret = umsdos_make_emd(dentry);
+Printk(("UMSDOS_ioctl_dir: INIT_EMD %s/%s, ret=%d\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, ret));
dir->i_op = (ret == 0)
? &umsdos_dir_inode_operations
: &umsdos_rdir_inode_operations;
@@ -257,18 +262,18 @@ int UMSDOS_ioctl_dir(struct inode *dir, struct file *filp, unsigned int cmd,
* is in the dos_dirent.name field and the destination
* is in umsdos_dirent.name field.
*
- * This ioctl allows umssync to rename a mangle file
+ * This ioctl allows umssync to rename a mangled file
* name before syncing it back in the EMD.
*/
old_dentry = umsdos_lookup_dentry (dentry,
data.dos_dirent.d_name,
- data.dos_dirent.d_reclen);
+ data.dos_dirent.d_reclen ,1);
ret = PTR_ERR(old_dentry);
if (IS_ERR(old_dentry))
goto out;
new_dentry = umsdos_lookup_dentry (dentry,
data.umsdos_dirent.name,
- data.umsdos_dirent.name_len);
+ data.umsdos_dirent.name_len, 1);
ret = PTR_ERR(new_dentry);
if (!IS_ERR(new_dentry)) {
printk("umsdos_ioctl: renaming %s/%s to %s/%s\n",
@@ -277,7 +282,6 @@ new_dentry->d_parent->d_name.name, new_dentry->d_name.name);
ret = msdos_rename (dir, old_dentry, dir, new_dentry);
dput(new_dentry);
}
- d_drop(old_dentry);
dput(old_dentry);
goto out;
}
@@ -301,6 +305,11 @@ new_dentry->d_parent->d_name.name, new_dentry->d_name.name);
data.umsdos_dirent.name_len, &info);
ret = umsdos_delentry (dentry, &info,
S_ISDIR (data.umsdos_dirent.mode));
+ if (ret) {
+ printk(KERN_WARNING
+ "umsdos_ioctl: delentry %s/%s failed, ret=%d\n",
+ dentry->d_name.name, info.entry.name, ret);
+ }
goto out;
}
else if (cmd == UMSDOS_UNLINK_DOS) {
@@ -314,14 +323,16 @@ new_dentry->d_parent->d_name.name, new_dentry->d_name.name);
* Return 0 if success.
*/
temp = umsdos_lookup_dentry(dentry, data.dos_dirent.d_name,
- data.dos_dirent.d_reclen);
+ data.dos_dirent.d_reclen, 1);
ret = PTR_ERR(temp);
if (IS_ERR(temp))
goto out;
ret = -ENOENT;
- if (temp->d_inode)
- ret = msdos_unlink (dir, temp);
- d_drop(temp);
+ if (temp->d_inode) {
+ ret = -EISDIR;
+ if (!S_ISDIR(temp->d_inode->i_mode))
+ ret = msdos_unlink (dir, temp);
+ }
dput (temp);
goto out;
}
@@ -336,14 +347,16 @@ new_dentry->d_parent->d_name.name, new_dentry->d_name.name);
* Return 0 if success.
*/
temp = umsdos_lookup_dentry(dentry, data.dos_dirent.d_name,
- data.dos_dirent.d_reclen);
+ data.dos_dirent.d_reclen, 1);
ret = PTR_ERR(temp);
if (IS_ERR(temp))
goto out;
ret = -ENOENT;
- if (temp->d_inode)
- ret = msdos_rmdir (dir, temp);
- d_drop(temp);
+ if (temp->d_inode) {
+ ret = -ENOTDIR;
+ if (S_ISDIR(temp->d_inode->i_mode))
+ ret = msdos_rmdir (dir, temp);
+ }
dput (temp);
goto out;
@@ -362,7 +375,7 @@ new_dentry->d_parent->d_name.name, new_dentry->d_name.name);
struct inode *inode;
dret = umsdos_lookup_dentry(dentry, data.dos_dirent.d_name,
- data.dos_dirent.d_reclen);
+ data.dos_dirent.d_reclen, 1);
ret = PTR_ERR(dret);
if (IS_ERR(dret))
goto out;
@@ -380,7 +393,6 @@ new_dentry->d_parent->d_name.name, new_dentry->d_name.name);
sizeof (data.stat)))
ret = 0;
}
- d_drop(dret);
dput(dret);
goto out;
}
diff --git a/fs/umsdos/mangle.c b/fs/umsdos/mangle.c
index c7f12722f..98a5c25ab 100644
--- a/fs/umsdos/mangle.c
+++ b/fs/umsdos/mangle.c
@@ -411,6 +411,7 @@ int umsdos_parse (
/* Why not use info->fake.len everywhere? Is it longer?
*/
memcpy (info->entry.name, fname, len);
+ info->entry.name[len] = '\0'; /* for printk */
info->entry.name_len = len;
ret = 0;
}
diff --git a/fs/umsdos/namei.c b/fs/umsdos/namei.c
index b365fbf04..831802dcf 100644
--- a/fs/umsdos/namei.c
+++ b/fs/umsdos/namei.c
@@ -177,17 +177,15 @@ void umsdos_endlookup (struct inode *dir)
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);
+ current->fsuid == dir->i_uid ||
+ capable (CAP_FOWNER));
}
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;
if (umsdos_is_pseudodos (dir, dentry)) {
@@ -198,20 +196,6 @@ static int umsdos_nevercreat (struct inode *dir, struct dentry *dentry,
* The pseudo sub-directory /DOS can't be removed!
* EPERM is returned.
*/
- ret = -EPERM;
- ret = errcod;
- } else if (name[0] == '.'
- && (len == 1 || (len == 2 && name[1] == '.'))) {
- /* #Specification: create / . and ..
- * If one try to creates . or .., it always fail and return
- * EEXIST.
- *
- * If one try to delete . or .., it always fail and return
- * EPERM.
- *
- * This should be test at the VFS layer level to avoid
- * duplicating this in all file systems. Any comments ?
- */
ret = errcod;
}
return ret;
@@ -224,8 +208,8 @@ static int umsdos_nevercreat (struct inode *dir, struct dentry *dentry,
*
* 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
+ * #Specification: create / file exists in DOS
+ * Here is a situation: we are trying to create a file with
* UMSDOS. The file is unknown to UMSDOS but already
* exists in the DOS directory.
*
@@ -254,19 +238,9 @@ static int umsdos_create_any (struct inode *dir, struct dentry *dentry,
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));
-
- check_dentry_path (dentry, "umsdos_create_any");
ret = umsdos_nevercreat (dir, dentry, -EEXIST);
- if (ret) {
-Printk (("%d/\n", ret));
+ if (ret)
goto out;
- }
ret = umsdos_parse (dentry->d_name.name, dentry->d_name.len, &info);
if (ret)
@@ -279,79 +253,82 @@ Printk (("%d/\n", ret));
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;
+ goto out;
- /* create short name dentry */
+ /* do a real lookup to get the short name dentry */
fake = umsdos_lookup_dentry(dentry->d_parent, info.fake.fname,
- info.fake.len);
+ info.fake.len, 1);
ret = PTR_ERR(fake);
if (IS_ERR(fake))
- goto out_unlock;
+ goto out_remove;
+
+ /* keep the short name anonymous ... */
+ if (dentry != fake)
+ d_drop(fake);
/* should not exist yet ... */
ret = -EEXIST;
if (fake->d_inode)
- goto out_remove;
+ goto out_remove_dput;
ret = msdos_create (dir, fake, S_IFREG | 0777);
if (ret)
- goto out_remove;
+ goto out_remove_dput;
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));
-
- check_dentry_path (dentry, "umsdos_create_any: BEG dentry");
- check_dentry_path (fake, "umsdos_create_any: BEG fake");
-
/*
* 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);
+ if (inode->i_count > 1) {
+ printk(KERN_WARNING
+ "umsdos_create_any: %s/%s, ino=%ld, icount=%d??\n",
+ dentry->d_parent->d_name.name, dentry->d_name.name,
+ inode->i_ino, inode->i_count);
+ }
+ umsdos_lookup_patch_new(dentry, &info);
-out_unlock:
- umsdos_unlockcreate (dir);
out:
return ret;
+
+ /* Creation failed ... remove the EMD entry */
+out_remove_dput:
+ dput(fake);
+out_remove:
+ if (ret == -EEXIST)
+ printk(KERN_WARNING "UMSDOS: out of sync, deleting %s/%s\n",
+ dentry->d_parent->d_name.name, info.fake.fname);
+ umsdos_delentry (dentry->d_parent, &info, S_ISDIR (info.entry.mode));
+ goto out;
+}
+
+/*
+ * 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 EMD file.
+ *
+ * Return the status of the operation. 0 mean success.
+ */
+int UMSDOS_create (struct inode *dir, struct dentry *dentry, int mode)
+{
+ return umsdos_create_any (dir, dentry, mode, 0, 0);
}
+
/*
* 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)
+ struct umsdos_info *old_info)
{
- /* != 0, this is the value of flags */
new_info->entry.mode = old_info->entry.mode;
new_info->entry.rdev = old_info->entry.rdev;
new_info->entry.uid = old_info->entry.uid;
@@ -359,10 +336,11 @@ static void umsdos_ren_init (struct umsdos_info *new_info,
new_info->entry.ctime = old_info->entry.ctime;
new_info->entry.atime = old_info->entry.atime;
new_info->entry.mtime = old_info->entry.mtime;
- new_info->entry.flags = flags ? flags : old_info->entry.flags;
+ new_info->entry.flags = old_info->entry.flags;
new_info->entry.nlink = old_info->entry.nlink;
}
+#ifdef OBSOLETE
#define chkstk() \
if (STACK_MAGIC != *(unsigned long *)current->kernel_stack_page){\
printk(KERN_ALERT "UMSDOS: %s magic %x != %lx ligne %d\n" \
@@ -373,6 +351,7 @@ if (STACK_MAGIC != *(unsigned long *)current->kernel_stack_page){\
#undef chkstk
#define chkstk() do { } while (0);
+#endif
/*
* Rename a file (move) in the file system.
@@ -382,124 +361,238 @@ 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 inode *old_inode = old_dentry->d_inode;
+ struct dentry *old, *new, *old_emd;
+ int err, ret, rehash = 0;
struct umsdos_info old_info;
struct umsdos_info new_info;
- old_ret = umsdos_parse (old_dentry->d_name.name,
+ ret = -EPERM;
+ err = umsdos_parse (old_dentry->d_name.name,
old_dentry->d_name.len, &old_info);
- if (old_ret)
+ if (err)
goto out;
- new_ret = umsdos_parse (new_dentry->d_name.name,
+ err = umsdos_parse (new_dentry->d_name.name,
new_dentry->d_name.len, &new_info);
- if (new_ret)
+ if (err)
goto out;
- check_dentry_path (old_dentry, "umsdos_rename_f OLD");
- check_dentry_path (new_dentry, "umsdos_rename_f OLD");
+ /* Get the EMD dentry for the old parent */
+ old_emd = umsdos_get_emd_dentry(old_dentry->d_parent);
+ ret = PTR_ERR(old_emd);
+ if (IS_ERR(old_emd))
+ goto out;
- chkstk ();
-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 ();
+ ret = umsdos_findentry(old_emd->d_parent, &old_info, 0);
if (ret) {
-Printk (("ret %d ", ret));
+ printk(KERN_ERR
+ "umsdos_rename_f: old entry %s/%s not in EMD, ret=%d\n",
+ old_dentry->d_parent->d_name.name, old_info.entry.name,
+ 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 "));
+printk("umsdos_rename_f: %s/%s old sticky bit, fsuid=%d, uid=%d, dir=%d\n",
+old_dentry->d_parent->d_name.name, old_info.entry.name,
+current->fsuid, old_info.entry.uid, old_dir->i_uid);
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;
+ /*
+ * Check whether the new_name already exists, and
+ * if so whether we're allowed to replace it.
+ */
+ err = umsdos_findentry(new_dentry->d_parent, &new_info, 0);
+ if (err == 0) {
+ /* Are we allowed to replace it? */
+ if (is_sticky(new_dir, new_info.entry.uid)) {
+Printk (("sticky set on new "));
+ goto out_unlock;
+ }
+ /* check whether it _really_ exists ... */
+ ret = -EEXIST;
+ if (new_dentry->d_inode)
+ goto out_unlock;
+
+ /* bogus lookup? complain and fix up the EMD ... */
+ printk(KERN_WARNING
+ "umsdos_rename_f: entry %s/%s exists, inode NULL??\n",
+ new_dentry->d_parent->d_name.name, new_info.entry.name);
+ err = umsdos_delentry(new_dentry->d_parent, &new_info,
+ S_ISDIR(new_info.entry.mode));
}
- Printk (("new newentry "));
- umsdos_ren_init (&new_info, &old_info, flags);
+Printk (("new newentry "));
+ /* create the new entry ... */
+ umsdos_ren_init (&new_info, &old_info);
+ if (flags)
+ new_info.entry.flags = flags;
ret = umsdos_newentry (new_dentry->d_parent, &new_info);
- chkstk ();
if (ret) {
-Printk (("ret %d %d ", ret, new_info.fake.len));
+ printk(KERN_WARNING
+ "umsdos_rename_f: newentry %s/%s failed, ret=%d\n",
+ new_dentry->d_parent->d_name.name, new_info.entry.name,
+ ret);
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))
+ /* If we're moving a hardlink, drop it first */
+ if (old_info.entry.flags & UMSDOS_HLINK) {
+ rehash = !list_empty(&old_dentry->d_hash);
+ d_drop(old_dentry);
+printk("umsdos_rename_f: moving hardlink %s/%s, rehash=%d\n",
+old_dentry->d_parent->d_name.name, old_info.entry.name, rehash);
+ }
+
+ /* Do a real lookup to get the old short name dentry */
+ old = umsdos_lookup_dentry(old_dentry->d_parent, old_info.fake.fname,
+ old_info.fake.len, 1);
+ ret = PTR_ERR(old);
+ if (IS_ERR(old)) {
+ printk(KERN_WARNING
+ "umsdos_rename_f: lookup old dentry %s/%s, ret=%d\n",
+ old_dentry->d_parent->d_name.name, old_info.fake.fname,
+ ret);
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;
+ }
+ /* short and long name dentries match? */
+ if (old == old_dentry)
+ dput(old);
+ else {
+ /* make sure it's the same inode! */
+ ret = -ENOENT;
+ if (old->d_inode != old_inode)
+ goto out_dput;
+ /*
+ * A cross-directory move with different short and long
+ * names has nasty complications: msdos-fs will need to
+ * change inodes, so we must check whether the original
+ * dentry is busy, and if the rename succeeds the short
+ * dentry will come back with a different inode.
+ *
+ * To handle this, we drop the dentry and free the inode,
+ * and then pick up the new inode after the rename.
+ */
+ if (old_dir != new_dir) {
+ ret = -EBUSY;
+ if (old_dentry->d_count > 1) {
+printk("umsdos_rename_f: old dentry %s/%s busy, d_count=%d\n",
+old_dentry->d_parent->d_name.name, old_dentry->d_name.name,old_dentry->d_count);
+ goto out_dput;
+ }
+ d_drop(old_dentry);
+ d_delete(old_dentry);
+printk("umsdos_rename_f: cross-dir move, %s/%s dropped\n",
+old_dentry->d_parent->d_name.name, old_dentry->d_name.name);
+ }
+ }
+
new = umsdos_lookup_dentry(new_dentry->d_parent, new_info.fake.fname,
- new_info.fake.len);
+ new_info.fake.len, 1);
ret = PTR_ERR(new);
- if (IS_ERR(new))
+ if (IS_ERR(new)) {
+ printk(KERN_WARNING
+ "umsdos_rename_f: lookup new dentry %s/%s, ret=%d\n",
+ new_dentry->d_parent->d_name.name, new_info.fake.fname,
+ ret);
goto out_dput;
-
- Printk (("msdos_rename "));
- check_dentry_path (old, "umsdos_rename_f OLD2");
- check_dentry_path (new, "umsdos_rename_f NEW2");
+ }
+#ifdef UMSDOS_PARANOIA
+if (new->d_inode != new_dentry->d_inode)
+printk("umsdos_rename_f: new %s/%s, inode %p!=%p??\n",
+new->d_parent->d_name.name, new->d_name.name, new->d_inode,new_dentry->d_inode);
+#endif
+ /* short and long name dentries match? */
+ if (new == new_dentry)
+ dput(new);
+
+#ifdef UMSDOS_DEBUG_VERBOSE
+printk("umsdos_rename_f: msdos_rename %s/%s(%ld) to %s/%s(%ld)\n",
+old->d_parent->d_name.name, old->d_name.name, old->d_inode->i_ino,
+new->d_parent->d_name.name, new->d_name.name,
+new->d_inode ? new->d_inode->i_ino : 0);
+#endif
+ /* Do the msdos-level rename */
ret = msdos_rename (old_dir, old, new_dir, new);
- chkstk ();
-printk("after m_rename ret %d ", ret);
- /* dput(old); */
- dput(new);
+Printk(("umsdos_rename_f: now %s/%s, ret=%d\n",
+old->d_parent->d_name.name, old->d_name.name, ret));
+
+ if (new != new_dentry)
+ dput(new);
+ /* If the rename failed, remove the new EMD entry */
if (ret != 0) {
+Printk(("umsdos_rename_f: rename failed, ret=%d, removing %s/%s\n",
+ret, new_dentry->d_parent->d_name.name, new_info.entry.name));
umsdos_delentry (new_dentry->d_parent, &new_info,
S_ISDIR (new_info.entry.mode));
- chkstk ();
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
+ /*
+ * Rename successful ... remove the old name from the EMD.
+ * Note that we use the EMD parent here, as the old dentry
+ * may have moved to a new parent ...
+ */
+ err = umsdos_delentry (old_emd->d_parent, &old_info,
+ S_ISDIR (old_info.entry.mode));
+ if (err) {
+ /* Failed? Complain a bit, but don't fail the operation */
+ printk(KERN_WARNING
+ "umsdos_rename_f: delentry %s/%s failed, error=%d\n",
+ old_emd->d_parent->d_name.name, old_info.entry.name,
+ err);
+ }
+
+ /*
+ * Check whether to update the dcache ... if both
+ * old and new dentries match, it's already correct.
+ * If the targets were aliases, the old short-name
+ * dentry has the original target name.
+ */
+ if (old_dentry != old) {
+ if (!old_dentry->d_inode) {
+ struct inode *inode = old->d_inode;
+ inode->i_count++;
+ d_instantiate(old_dentry, inode);
+printk("umsdos_rename_f: %s/%s gets new ino=%ld\n",
+old_dentry->d_parent->d_name.name, old_dentry->d_name.name, inode->i_ino);
+ }
+ if (new_dentry == new)
+ new_dentry = old;
+ goto move_it;
+ } else if (new_dentry != new) {
+ move_it:
+ /* this will rehash the dentry ... */
+ d_move(old_dentry, new_dentry);
+ }
+ /* Check whether the old inode changed ... */
+ if (old_dentry->d_inode != old_inode) {
+ umsdos_lookup_patch_new(old_dentry, &new_info);
+ }
+
/*
* 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);
+ umsdos_set_dirinfo_new(old_dentry, new_info.f_pos);
+ /* dput() the dentry if we haven't already */
out_dput:
- dput(dret);
+ if (old_dentry != old)
+ dput(old);
out_unlock:
- Printk ((KERN_DEBUG "umsdos_rename_f: unlocking dirs...\n"));
+ dput(old_emd);
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;
}
@@ -509,54 +602,51 @@ out:
* Return a negative error code or 0 if OK.
*/
/* #Specification: symbolic links / strategy
- * A symbolic link is simply a file which hold a path. It is
+ * A symbolic link is simply a file which holds 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
+ * I see two different ways to do this: One is to place the link data
+ * in unused entries of the EMD file; the other is to have a separate
* file dedicated to hold all symbolic links data.
*
* Let's go for simplicity...
*/
+extern struct inode_operations umsdos_symlink_inode_operations;
+
static int umsdos_symlink_x (struct inode *dir, struct dentry *dentry,
const char *symname, int mode, char flags)
{
int ret, len;
struct file filp;
+Printk(("umsdos_symlink: %s/%s to %s\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, symname));
+
ret = umsdos_create_any (dir, dentry, mode, 0, flags);
if (ret) {
-Printk (("umsdos_symlink ret %d ", ret));
+ printk(KERN_WARNING
+ "umsdos_symlink: create failed, ret=%d\n", ret);
goto out;
}
- len = strlen (symname);
-
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));
+ len = strlen (symname);
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;
- }
- }
- if (ret != 0) {
- UMSDOS_unlink (dir, dentry);
- }
-
+ if (ret < 0)
+ goto out_unlink;
+ if (ret != len)
+ goto out_error;
+ ret = 0;
out:
- Printk (("\n"));
return ret;
+
+out_error:
+ ret = -EIO;
+out_unlink:
+ printk(KERN_WARNING "umsdos_symlink: write failed, unlinking\n");
+ UMSDOS_unlink (dir, dentry);
+ goto out;
}
/*
@@ -576,13 +666,20 @@ int UMSDOS_link (struct dentry *olddentry, struct inode *dir,
struct dentry *dentry)
{
struct inode *oldinode = olddentry->d_inode;
- struct inode *olddir;
- char *path;
+ struct inode *olddir = olddentry->d_parent->d_inode;
struct dentry *temp;
+ char *path;
unsigned long buffer;
int ret;
- struct umsdos_dirent entry;
+ struct umsdos_info old_info;
+ struct umsdos_info hid_info;
+#ifdef UMSDOS_DEBUG_VERBOSE
+printk("umsdos_link: new %s%s -> %s/%s\n",
+dentry->d_parent->d_name.name, dentry->d_name.name,
+olddentry->d_parent->d_name.name, olddentry->d_name.name);
+#endif
+
ret = -EPERM;
if (S_ISDIR (oldinode->i_mode))
goto out;
@@ -596,80 +693,174 @@ int UMSDOS_link (struct dentry *olddentry, struct inode *dir,
if (!buffer)
goto out;
- olddir = olddentry->d_parent->d_inode;
- umsdos_lockcreate2 (dir, olddir);
+ /*
+ * Lock the link parent if it's not the same directory.
+ */
+ ret = -EDEADLOCK;
+ if (olddir != dir) {
+ if (atomic_read(&olddir->i_sem.count) < 1)
+ goto out_free;
+ down(&olddir->i_sem);
+ }
- /* get the entry for the old name */
- ret = umsdos_dentry_to_entry(olddentry, &entry);
+ /*
+ * Parse the name and get the visible directory entry.
+ */
+ ret = umsdos_parse (olddentry->d_name.name, olddentry->d_name.len,
+ &old_info);
if (ret)
goto out_unlock;
-Printk (("umsdos_link :%.*s: ino %lu flags %d ",
-entry.name_len, entry.name ,oldinode->i_ino, entry.flags));
-
- if (!(entry.flags & UMSDOS_HIDDEN)) {
- struct umsdos_info info;
+ ret = umsdos_findentry (olddentry->d_parent, &old_info, 1);
+ if (ret) {
+printk("UMSDOS_link: %s/%s not in EMD, ret=%d\n",
+olddentry->d_parent->d_name.name, olddentry->d_name.name, ret);
+ goto out_unlock;
+ }
- ret = umsdos_newhidden (olddentry->d_parent, &info);
- if (ret)
+ /*
+ * If the visible dentry is a pseudo-hardlink, the original
+ * file must be already hidden.
+ */
+ if (!(old_info.entry.flags & UMSDOS_HLINK)) {
+ int err;
+
+ /* create a hidden link name */
+ ret = umsdos_newhidden (olddentry->d_parent, &hid_info);
+ if (ret) {
+printk("umsdos_link: can't make hidden %s/%s, ret=%d\n",
+olddentry->d_parent->d_name.name, hid_info.entry.name, 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);
+ /*
+ * Make a dentry and rename the original file ...
+ */
+ temp = umsdos_lookup_dentry(olddentry->d_parent,
+ hid_info.entry.name,
+ hid_info.entry.name_len, 0);
+ ret = PTR_ERR(temp);
+ if (IS_ERR(temp)) {
+printk("umsdos_link: lookup %s/%s failed, ret=%d\n",
+dentry->d_parent->d_name.name, hid_info.entry.name, ret);
+ goto cleanup;
}
+ /* rename the link to the hidden location ... */
+ ret = umsdos_rename_f (olddir, olddentry, olddir, temp,
+ UMSDOS_HIDDEN);
dput(temp);
+ if (ret) {
+printk("umsdos_link: rename to %s/%s failed, ret=%d\n",
+temp->d_parent->d_name.name, temp->d_name.name, ret);
+ goto cleanup;
+ }
+ /* mark the inode as a hardlink */
+ oldinode->u.umsdos_i.i_is_hlink = 1;
+
+ /*
+ * Capture the path to the hidden link.
+ */
+ path = umsdos_d_path(olddentry, (char *) buffer, PAGE_SIZE);
+Printk(("umsdos_link: hidden link path=%s\n", path));
+
+ /*
+ * Recreate a dentry for the original name and symlink it,
+ * then symlink the new dentry. Don't give up if one fails,
+ * or we'll lose the file completely!
+ *
+ * Note: this counts as the "original" reference, so we
+ * don't increment i_nlink for this one.
+ */
+ temp = umsdos_lookup_dentry(olddentry->d_parent,
+ old_info.entry.name,
+ old_info.entry.name_len, 0);
+ ret = PTR_ERR(temp);
+ if (!IS_ERR(temp)) {
+ ret = umsdos_symlink_x (olddir, temp, path,
+ S_IFREG | 0777, UMSDOS_HLINK);
+ dput(temp);
+ }
+
+ /* This symlink increments i_nlink (see below.) */
+ err = umsdos_symlink_x (dir, dentry, path,
+ S_IFREG | 0777, UMSDOS_HLINK);
+ /* fold the two errors */
+ if (!ret)
+ ret = err;
+ goto out_unlock;
+
+ /* creation failed ... remove the link entry */
+ cleanup:
+printk("umsdos_link: link failed, ret=%d, removing %s/%s\n",
+ret, olddentry->d_parent->d_name.name, hid_info.entry.name);
+ err = umsdos_delentry(olddentry->d_parent, &hid_info, 0);
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);
}
+Printk(("UMSDOS_link: %s/%s already hidden\n",
+olddentry->d_parent->d_name.name, olddentry->d_name.name));
+ /*
+ * The original file is already hidden, and we need to get
+ * the dentry for its real name, not the visible name.
+ * N.B. make sure it's the hidden inode ...
+ */
+ if (!oldinode->u.umsdos_i.i_is_hlink)
+ printk("UMSDOS_link: %s/%s hidden, ino=%ld not hlink??\n",
+ olddentry->d_parent->d_name.name,
+ olddentry->d_name.name, oldinode->i_ino);
+
+ /*
+ * In order to get the correct (real) inode, we just drop
+ * the original dentry.
+ */
+ d_drop(olddentry);
+Printk(("UMSDOS_link: hard link %s/%s, fake=%s\n",
+olddentry->d_parent->d_name.name, olddentry->d_name.name, old_info.fake.fname));
+
+ /* Do a real lookup to get the short name dentry */
+ temp = umsdos_lookup_dentry(olddentry->d_parent,
+ old_info.fake.fname,
+ old_info.fake.len, 1);
+ ret = PTR_ERR(temp);
+ if (IS_ERR(temp))
+ goto out_unlock;
+
+ /* now resolve the link ... */
+ temp = umsdos_solve_hlink(temp);
+ ret = PTR_ERR(temp);
+ if (IS_ERR(temp))
+ goto out_unlock;
+ path = umsdos_d_path(temp, (char *) buffer, PAGE_SIZE);
+ dput(temp);
+Printk(("umsdos_link: %s/%s already hidden, path=%s\n",
+olddentry->d_parent->d_name.name, olddentry->d_name.name, path));
+
+ /* finally we can symlink it ... */
+ ret = umsdos_symlink_x (dir, dentry, path, S_IFREG | 0777,UMSDOS_HLINK);
+
out_unlock:
- umsdos_unlockcreate (olddir);
- umsdos_unlockcreate (dir);
- free_page(buffer);
-out:
+ /* remain locked for the call to notify_change ... */
if (ret == 0) {
struct iattr newattrs;
+#ifdef UMSDOS_PARANOIA
+if (!oldinode->u.umsdos_i.i_is_hlink)
+printk("UMSDOS_link: %s/%s, ino=%ld, not marked as hlink!\n",
+olddentry->d_parent->d_name.name, olddentry->d_name.name, oldinode->i_ino);
+#endif
oldinode->i_nlink++;
+Printk(("UMSDOS_link: linked %s/%s, ino=%ld, nlink=%d\n",
+olddentry->d_parent->d_name.name, olddentry->d_name.name,
+oldinode->i_ino, oldinode->i_nlink));
newattrs.ia_valid = 0;
- ret = UMSDOS_notify_change (olddentry, &newattrs);
+ ret = umsdos_notify_change_locked(olddentry, &newattrs);
}
- Printk (("umsdos_link %d\n", ret));
- return ret;
-}
-
+ if (olddir != dir)
+ up(&olddir->i_sem);
-/*
- * 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 EMD file.
- *
- * Return the status of the operation. 0 mean success.
- */
-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");
- ret = umsdos_create_any (dir, dentry, mode, 0, 0);
- check_dentry_path (dentry, "UMSDOS_create END");
+out_free:
+ free_page(buffer);
+out:
+ Printk (("umsdos_link %d\n", ret));
return ret;
}
@@ -699,12 +890,9 @@ int UMSDOS_mkdir (struct inode *dir, struct dentry *dentry, int mode)
goto out;
ret = umsdos_parse (dentry->d_name.name, dentry->d_name.len, &info);
- if (ret) {
-Printk (("umsdos_mkdir %d\n", ret));
+ if (ret)
goto out;
- }
- umsdos_lockcreate (dir);
info.entry.mode = mode | S_IFDIR;
info.entry.rdev = 0;
info.entry.uid = current->fsuid;
@@ -713,72 +901,74 @@ Printk (("umsdos_mkdir %d\n", ret));
info.entry.flags = 0;
info.entry.nlink = 1;
ret = umsdos_newentry (dentry->d_parent, &info);
- if (ret) {
-Printk (("newentry %d ", ret));
- goto out_unlock;
- }
+ if (ret)
+ goto out;
/* lookup the short name dentry */
temp = umsdos_lookup_dentry(dentry->d_parent, info.fake.fname,
- info.fake.len);
+ info.fake.len, 1);
ret = PTR_ERR(temp);
if (IS_ERR(temp))
- goto out_unlock;
+ goto out_remove;
+
+ /* Keep the short name dentry anonymous */
+ if (temp != dentry)
+ d_drop(temp);
/* 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;
+ goto out_remove_dput;
}
ret = msdos_mkdir (dir, temp, mode);
if (ret)
- goto out_remove;
+ goto out_remove_dput;
+ /*
+ * Lock the inode to protect the EMD creation ...
+ */
inode = temp->d_inode;
- umsdos_lookup_patch_new(temp, &info.entry, info.f_pos);
+ down(&inode->i_sem);
/*
* 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");
+if (dentry->d_inode)
+printk("umsdos_mkdir: dentry not negative!\n");
+ inode->i_count++;
+ d_instantiate(dentry, inode);
}
-
- /* create the EMD file */
- err = umsdos_make_emd(dentry);
+ /* N.B. this should have an option to create the EMD ... */
+ umsdos_lookup_patch_new(dentry, &info);
/*
- * set up the dir so it is promoted to EMD,
- * with the EMD file invisible inside it.
+ * Create the EMD file, and set up the dir so it is
+ * promoted to EMD with the EMD file invisible.
+ *
+ * N.B. error return if EMD fails?
*/
- umsdos_setup_dir(temp);
- goto out_dput;
-
-out_remove:
- umsdos_delentry (dentry->d_parent, &info, 1);
+ err = umsdos_make_emd(dentry);
+ umsdos_setup_dir(dentry);
-out_dput:
- /* kill off the short name dentry */
- if (temp != dentry)
- d_drop(temp);
+ up(&inode->i_sem);
dput(temp);
-out_unlock:
- umsdos_unlockcreate (dir);
- Printk (("umsdos_mkdir %d\n", ret));
out:
+ Printk(("umsdos_mkdir: %s/%s, ret=%d\n",
+ dentry->d_parent->d_name.name, dentry->d_name.name, ret));
return ret;
+
+ /* an error occurred ... remove EMD entry. */
+out_remove_dput:
+ dput(temp);
+out_remove:
+ umsdos_delentry (dentry->d_parent, &info, 1);
+ goto out;
}
/*
@@ -801,11 +991,7 @@ out:
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");
- return ret;
+ return umsdos_create_any (dir, dentry, mode, rdev, 0);
}
/*
@@ -821,72 +1007,62 @@ int UMSDOS_rmdir (struct inode *dir, struct dentry *dentry)
if (ret)
goto out;
-#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)
+ ret = -EBUSY;
+ if (!list_empty(&dentry->d_hash))
goto out;
-#endif
- 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",
+ /* check the 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;
- }
+ goto out;
}
/* check whether the EMD is empty */
- empty = umsdos_isempty (dentry);
ret = -ENOTEMPTY;
- if (empty == 0)
- goto out_unlock;
+ empty = umsdos_isempty (dentry);
/* 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);
+ if (!IS_ERR(demd)) {
+ err = -ENOENT;
+ if (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;
- }
+#ifdef UMSDOS_PARANOIA
+if (err)
+printk("umsdos_rmdir: EMD %s/%s unlink failed, err=%d\n",
+demd->d_parent->d_name.name, demd->d_name.name, err);
+#endif
+ dput(demd);
+ if (!err)
+ ret = 0;
+ }
+ } else if (empty == 2)
+ ret = 0;
+ if (ret)
+ goto out;
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);
+ info.fake.len, 1);
ret = PTR_ERR(temp);
if (IS_ERR(temp))
- goto out_unlock;
+ goto out;
/*
- * If the short name matches the dentry, dput() it now.
+ * If the short name is an alias, dput() it now;
+ * otherwise d_drop() it to keep it anonymous.
*/
- if (temp == dentry) {
+ if (temp == dentry)
dput(temp);
-printk("umsdos_rmdir: %s/%s, short matches long\n",
-dentry->d_parent->d_name.name, dentry->d_name.name);
- }
+ else
+ d_drop(temp);
/*
* Attempt to remove the msdos name.
@@ -897,20 +1073,15 @@ dentry->d_parent->d_name.name, dentry->d_name.name);
/* OK so far ... remove the name from the EMD */
ret = umsdos_delentry (dentry->d_parent, &info, 1);
+#ifdef UMSDOS_PARANOIA
+if (ret)
+printk("umsdos_rmdir: delentry %s failed, ret=%d\n", info.entry.name, ret);
+#endif
-out_dput:
/* dput() temp if we didn't do it above */
- if (temp != dentry) {
- d_drop(temp);
+out_dput:
+ if (temp != dentry)
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));
@@ -922,8 +1093,8 @@ out:
* 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
+ * When we delete a file and this file is a link,
+ * we must subtract 1 from the nlink field of the
* hidden link.
*
* If the count goes to 0, we delete this hidden
@@ -931,12 +1102,12 @@ out:
*/
int UMSDOS_unlink (struct inode *dir, struct dentry *dentry)
{
- struct dentry *temp;
+ struct dentry *temp, *link = NULL;
struct inode *inode;
- int ret;
+ int ret, rehash = 0;
struct umsdos_info info;
-Printk (("UMSDOS_unlink: entering %s/%s\n",
+Printk(("UMSDOS_unlink: entering %s/%s\n",
dentry->d_parent->d_name.name, dentry->d_name.name));
ret = umsdos_nevercreat (dir, dentry, -EPERM);
@@ -950,11 +1121,13 @@ dentry->d_parent->d_name.name, dentry->d_name.name));
umsdos_lockcreate (dir);
ret = umsdos_findentry (dentry->d_parent, &info, 1);
if (ret) {
-printk("UMSDOS_unlink: findentry returned %d\n", ret);
+printk("UMSDOS_unlink: %s/%s not in EMD, ret=%d\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, 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)) {
@@ -963,65 +1136,54 @@ dentry->d_parent->d_name.name, dentry->d_name.name);
goto out_unlock;
}
- ret = 0;
+ /*
+ * Note! If this is a hardlink and the names are aliased,
+ * the short-name lookup will return the hardlink dentry.
+ * In order to get the correct (real) inode, we just drop
+ * the original dentry.
+ */
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.
- */
-
- 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_dentry_to_entry (dentry, &entry);
- if (ret == 0) {
- ret = UMSDOS_unlink (dentry->d_parent->d_inode,
- dentry);
- }
- } else {
- struct iattr newattrs;
- newattrs.ia_valid = 0;
- ret = UMSDOS_notify_change (dentry, &newattrs);
- }
+ rehash = !list_empty(&dentry->d_hash);
+ d_drop(dentry);
+Printk(("UMSDOS_unlink: hard link %s/%s, fake=%s, rehash=%d\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, info.fake.fname, rehash));
}
- if (ret)
- goto out_unlock;
- /* get the short name dentry */
+ /* Do a real lookup to get the short name dentry */
temp = umsdos_lookup_dentry(dentry->d_parent, info.fake.fname,
- info.fake.len);
+ info.fake.len, 1);
+ ret = PTR_ERR(temp);
if (IS_ERR(temp))
goto out_unlock;
/*
- * If the short name matches the long,
- * dput() it now so it's not busy.
+ * Resolve hardlinks now, but defer processing until later.
*/
- if (temp == dentry) {
-printk("UMSDOS_unlink: %s/%s, short matches long\n",
-dentry->d_parent->d_name.name, dentry->d_name.name);
- dput(temp);
+ if (info.entry.flags & UMSDOS_HLINK) {
+ link = umsdos_solve_hlink(dget(temp));
}
+ /*
+ * If the short and long names are aliased,
+ * dput() it now so the dentry isn't busy.
+ */
+ if (temp == dentry)
+ dput(temp);
+
+ /* Delete the EMD entry */
ret = umsdos_delentry (dentry->d_parent, &info, 0);
- if (ret && ret != -ENOENT)
+ if (ret && ret != -ENOENT) {
+ printk(KERN_WARNING "UMSDOS_unlink: delentry %s, error=%d\n",
+ info.entry.name, ret);
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));
+#ifdef UMSDOS_PARANOIA
+if (ret)
+printk("umsdos_unlink: %s/%s unlink failed, ret=%d\n",
+temp->d_parent->d_name.name, temp->d_name.name, ret);
+#endif
/* dput() temp if we didn't do it above */
out_dput:
@@ -1030,12 +1192,64 @@ out_dput:
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);
+
+ /*
+ * Now check for deferred handling of a hardlink.
+ */
+ if (!link)
+ goto out;
+
+ if (IS_ERR(link)) {
+printk("umsdos_unlink: failed to resolve %s/%s\n",
+dentry->d_parent->d_name.name, dentry->d_name.name);
+ if (!ret)
+ ret = PTR_ERR(link);
+ goto out;
+ }
+
+Printk(("umsdos_unlink: link %s/%s deferred, pending ret=%d\n",
+link->d_parent->d_name.name, link->d_name.name, ret));
+
+ /* already have an error? */
+ if (ret)
+ goto out_cleanup;
+
+ /* make sure the link exists ... */
+ inode = link->d_inode;
+ if (!inode) {
+ printk(KERN_WARNING "umsdos_unlink: hard link not found\n");
+ goto out_cleanup;
+ }
+
+ /*
+ * If this was the last linked reference, delete it now.
+ *
+ * N.B. Deadlock problem? We should be holding the lock
+ * for the hardlink's parent, but another process might
+ * be holding that lock waiting for us to finish ...
+ */
+ if (inode->i_nlink <= 1) {
+ ret = UMSDOS_unlink (link->d_parent->d_inode, link);
+ if (ret) {
+ printk(KERN_WARNING
+ "umsdos_unlink: link removal failed, ret=%d\n",
+ ret);
+ }
+ } else {
+ struct iattr newattrs;
+ inode->i_nlink--;
+ newattrs.ia_valid = 0;
+ ret = umsdos_notify_change_locked(link, &newattrs);
+ }
+
+out_cleanup:
+ d_drop(link);
+ dput(link);
+
out:
Printk (("umsdos_unlink %d\n", ret));
return ret;
@@ -1048,30 +1262,49 @@ out:
int UMSDOS_rename (struct inode *old_dir, struct dentry *old_dentry,
struct inode *new_dir, struct dentry *new_dentry)
{
+ struct dentry *new_target;
int ret;
+#ifdef UMSDOS_DEBUG_VERBOSE
+printk("umsdos_rename: enter, %s/%s(%ld) to %s/%s(%ld)\n",
+old_dentry->d_parent->d_name.name, old_dentry->d_name.name,
+old_dentry->d_inode->i_ino,
+new_dentry->d_parent->d_name.name, new_dentry->d_name.name,
+new_dentry->d_inode ? new_dentry->d_inode->i_ino : 0);
+#endif
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));
+ /*
+ * If the target already exists, delete it first.
+ */
+ if (new_dentry->d_inode) {
+ if (S_ISDIR(new_dentry->d_inode->i_mode))
+ ret = UMSDOS_rmdir (new_dir, new_dentry);
+ else
+ ret = UMSDOS_unlink (new_dir, new_dentry);
+ if (ret)
+ goto out;
}
- if (ret)
- goto out;
- /* this time the rename should work ... */
- ret = umsdos_rename_f (old_dir, old_dentry, new_dir, new_dentry, 0);
+ /*
+ * If we didn't get a negative dentry, make a copy and hash it.
+ */
+ new_target = new_dentry;
+ if (new_dentry->d_inode) {
+printk("umsdos_rename: %s/%s not negative, hash=%d\n",
+new_dentry->d_parent->d_name.name, new_dentry->d_name.name,
+!list_empty(&new_dentry->d_hash));
+ ret = -ENOMEM;
+ new_target = d_alloc(new_dentry->d_parent, &new_dentry->d_name);
+ if (!new_target)
+ goto out;
+ d_add(new_target, NULL);
+ }
+ ret = umsdos_rename_f(old_dir, old_dentry, new_dir, new_target, 0);
+ if (new_target != new_dentry)
+ dput(new_target);
out:
return ret;
diff --git a/fs/umsdos/rdir.c b/fs/umsdos/rdir.c
index 39605218d..10ff145b7 100644
--- a/fs/umsdos/rdir.c
+++ b/fs/umsdos/rdir.c
@@ -19,7 +19,9 @@
#include <asm/uaccess.h>
+extern struct dentry *saved_root;
extern struct inode *pseudo_root;
+extern struct dentry_operations umsdos_dentry_operations;
struct RDIR_FILLDIR {
void *dirbuf;
@@ -63,9 +65,7 @@ static int UMSDOS_rreaddir (struct file *filp, void *dirbuf, filldir_t filldir)
bufk.filldir = filldir;
bufk.dirbuf = dirbuf;
- bufk.real_root = pseudo_root &&
- dir->i_ino == UMSDOS_ROOT_INO &&
- dir->i_sb == pseudo_root->i_sb;
+ bufk.real_root = pseudo_root && (dir == saved_root->d_inode);
return fat_readdir (filp, &bufk, rdir_filldir);
}
@@ -81,45 +81,40 @@ static int UMSDOS_rreaddir (struct file *filp, void *dirbuf, filldir_t filldir)
*/
int umsdos_rlookup_x ( struct inode *dir, struct dentry *dentry, int nopseudo)
{
- /* so locating "linux" will work */
- const char *name = dentry->d_name.name;
- int len = dentry->d_name.len;
- struct inode *inode;
int ret;
- if (pseudo_root && len == 2 && name[0] == '.' && name[1] == '.' &&
- dir->i_ino == UMSDOS_ROOT_INO && dir->i_sb == pseudo_root->i_sb) {
-printk (KERN_WARNING "umsdos_rlookup_x: we are at pseudo-root thingy?\n");
- pseudo_root->i_count++;
- d_add(dentry, pseudo_root);
- ret = 0;
+ if (saved_root && dir == saved_root->d_inode && !nopseudo &&
+ dentry->d_name.len == UMSDOS_PSDROOT_LEN &&
+ memcmp (dentry->d_name.name, UMSDOS_PSDROOT_NAME, UMSDOS_PSDROOT_LEN) == 0) {
+ /* #Specification: pseudo root / DOS/linux
+ * Even in the real root directory (c:\), the directory
+ * /linux won't show
+ */
+
+ ret = -ENOENT;
goto out;
}
- ret = umsdos_real_lookup (dir, dentry);
- inode = dentry->d_inode;
- if ((ret == 0) && inode) {
- if (inode == pseudo_root && !nopseudo) {
- /* #Specification: pseudo root / DOS/linux
- * Even in the real root directory (c:\), the directory
- * /linux won't show
- */
-printk(KERN_WARNING "umsdos_rlookup_x: do the pseudo-thingy...\n");
- /* make the dentry negative */
- d_delete(dentry);
- }
- else if (S_ISDIR (inode->i_mode)) {
- /* We must place the proper function table
- * depending on whether this is an MS-DOS or
- * a UMSDOS directory
- */
-Printk ((KERN_DEBUG "umsdos_rlookup_x: setting up setup_dir_inode %lu...\n",
-inode->i_ino));
- umsdos_setup_dir(dentry);
- }
+ ret = msdos_lookup (dir, dentry);
+ if (ret) {
+ printk(KERN_WARNING
+ "umsdos_rlookup_x: %s/%s failed, ret=%d\n",
+ dentry->d_parent->d_name.name, dentry->d_name.name,ret);
+ goto out;
+ }
+ if (dentry->d_inode) {
+ /* We must install the proper function table
+ * depending on whether this is an MS-DOS or
+ * a UMSDOS directory
+ */
+Printk ((KERN_DEBUG "umsdos_rlookup_x: patch_dentry_inode %s/%s\n",
+dentry->d_parent->d_name.name, dentry->d_name.name));
+ umsdos_patch_dentry_inode(dentry, 0);
+
}
out:
- PRINTK ((KERN_DEBUG "umsdos_rlookup_x: returning %d\n", ret));
+ /* always install our dentry ops ... */
+ dentry->d_op = &umsdos_dentry_operations;
return ret;
}
@@ -160,54 +155,34 @@ static int UMSDOS_rrmdir ( struct inode *dir, struct dentry *dentry)
if (umsdos_is_pseudodos (dir, dentry))
goto out;
- umsdos_lockcreate (dir);
ret = -EBUSY;
- if (dentry->d_count > 1) {
- shrink_dcache_parent(dentry);
- if (dentry->d_count > 1)
- goto out_unlock;
- }
+ if (!list_empty(&dentry->d_hash))
+ goto out;
ret = msdos_rmdir (dir, dentry);
if (ret != -ENOTEMPTY)
- goto out_check;
-
-#if 0 /* why do this? we have the dentry ... */
- ret = UMSDOS_rlookup (dir, dentry);
- PRINTK (("rrmdir lookup %d ", ret));
- if (ret)
- goto out_unlock;
- ret = -ENOTEMPTY;
-#endif
+ goto out;
empty = umsdos_isempty (dentry);
if (empty == 1) {
- struct dentry *temp;
+ struct dentry *demd;
/* We have to remove the EMD file. */
- temp = umsdos_lookup_dentry(dentry, UMSDOS_EMD_FILE,
- UMSDOS_EMD_NAMELEN);
- ret = PTR_ERR(temp);
- if (!IS_ERR(temp)) {
+ demd = umsdos_get_emd_dentry(dentry);
+ ret = PTR_ERR(demd);
+ if (!IS_ERR(demd)) {
ret = 0;
- if (temp->d_inode)
- ret = msdos_unlink (dentry->d_inode, temp);
- dput(temp);
+ if (demd->d_inode)
+ ret = msdos_unlink (dentry->d_inode, demd);
+ dput(demd);
}
- if (ret)
- goto out_unlock;
}
+ if (ret)
+ goto out;
+
/* now retry the original ... */
ret = msdos_rmdir (dir, dentry);
-out_check:
- /* Check whether we succeeded ... */
- if (!ret)
- d_delete(dentry);
-
-out_unlock:
- umsdos_unlockcreate (dir);
out:
- check_inode (dir);
return ret;
}
diff --git a/fs/umsdos/specs b/fs/umsdos/specs
index 7b88a78e4..0f7d68c0a 100644
--- a/fs/umsdos/specs
+++ b/fs/umsdos/specs
@@ -147,6 +147,7 @@
* same target at the same time. Again, I am not sure it
* is a problem at all.
*/
+
/* #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
@@ -180,6 +181,7 @@
* 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
diff --git a/fs/umsdos/symlink.c b/fs/umsdos/symlink.c
index dc639a7a4..4b3678a22 100644
--- a/fs/umsdos/symlink.c
+++ b/fs/umsdos/symlink.c
@@ -32,52 +32,26 @@ int umsdos_readlink_x ( struct dentry *dentry,
char *buffer,
int bufsiz)
{
- int ret;
+ size_t size = dentry->d_inode->i_size;
loff_t loffs = 0;
+ ssize_t ret;
struct file filp;
- ret = dentry->d_inode->i_size;
-
- if (!(dentry->d_inode)) {
- return -EBADF;
- }
+Printk((KERN_DEBUG "UMSDOS_read: %s/%s, size=%u\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, size));
fill_new_filp (&filp, dentry);
-
filp.f_reada = 0;
filp.f_flags = O_RDONLY;
- filp.f_op = &umsdos_symlink_operations; /* /mn/ - we have to fill it with dummy values so we won't segfault */
-
- if (ret > bufsiz)
- ret = bufsiz;
-
- PRINTK ((KERN_DEBUG "umsdos_readlink_x /mn/: Checkin: filp=%p, buffer=%p, size=%d, offs=%Lu\n", &filp, buffer, ret, loffs));
- PRINTK ((KERN_DEBUG " f_op=%p\n", filp.f_op));
- PRINTK ((KERN_DEBUG " inode=%lu, i_size=%lu\n", filp.f_dentry->d_inode->i_ino, filp.f_dentry->d_inode->i_size));
- PRINTK ((KERN_DEBUG " f_pos=%Lu\n", filp.f_pos));
- PRINTK ((KERN_DEBUG " name=%.*s\n", (int) filp.f_dentry->d_name.len, filp.f_dentry->d_name.name));
- PRINTK ((KERN_DEBUG " i_binary(sb)=%d\n", MSDOS_I (filp.f_dentry->d_inode)->i_binary));
- PRINTK ((KERN_DEBUG " f_count=%d, f_flags=%d\n", filp.f_count, filp.f_flags));
- PRINTK ((KERN_DEBUG " f_owner=%d\n", filp.f_owner.uid));
- PRINTK ((KERN_DEBUG " f_version=%ld\n", filp.f_version));
- PRINTK ((KERN_DEBUG " f_reada=%ld, f_ramax=%ld, f_raend=%ld, f_ralen=%ld, f_rawin=%ld\n", filp.f_reada, filp.f_ramax, filp.f_raend, filp.f_ralen, filp.f_rawin));
+ filp.f_op = &umsdos_symlink_operations;
+ if (size > bufsiz)
+ size = bufsiz;
- PRINTK ((KERN_DEBUG "umsdos_readlink_x: FIXME /mn/: running fat_file_read (%p, %p, %d, %Lu)\n", &filp, buffer, ret, loffs));
- if (fat_file_read (&filp, buffer, (size_t) ret, &loffs) != ret) {
+ ret = fat_file_read (&filp, buffer, size, &loffs);
+ if (ret != size) {
ret = -EIO;
}
-#if 0
- {
- struct umsdos_dirent *mydirent = buffer;
-
- PRINTK ((KERN_DEBUG " (DDD) uid=%d\n", mydirent->uid));
- PRINTK ((KERN_DEBUG " (DDD) gid=%d\n", mydirent->gid));
- PRINTK ((KERN_DEBUG " (DDD) name=>%.20s<\n", mydirent->name));
- }
-#endif
-
- PRINTK ((KERN_DEBUG "umsdos_readlink_x: FIXME /mn/: fat_file_read returned offs=%Lu ret=%d\n", loffs, ret));
return ret;
}
@@ -85,49 +59,40 @@ int umsdos_readlink_x ( struct dentry *dentry,
static int UMSDOS_readlink (struct dentry *dentry, char *buffer, int buflen)
{
- int ret;
-
- PRINTK ((KERN_DEBUG "UMSDOS_readlink: calling umsdos_readlink_x for %.*s\n", (int) dentry->d_name.len, dentry->d_name.name));
-
- ret = umsdos_readlink_x (dentry, buffer, buflen);
- PRINTK ((KERN_DEBUG "readlink %d bufsiz %d\n", ret, buflen));
- /* dput(dentry); / * FIXME /mn/? It seems it is unneeded. d_count is not changed by umsdos_readlink_x */
-
- Printk ((KERN_WARNING "UMSDOS_readlink /mn/: FIXME! skipped dput(dentry). returning %d\n", ret));
- return ret;
-
+ return umsdos_readlink_x (dentry, buffer, buflen);
}
/* this one mostly stolen from romfs :) */
-static struct dentry *UMSDOS_followlink (struct dentry *dentry, struct dentry *base)
+static struct dentry *UMSDOS_followlink (struct dentry *dentry,
+ struct dentry *base,
+ unsigned int follow)
{
struct inode *inode = dentry->d_inode;
- char *symname = NULL;
+ char *symname;
int len, cnt;
mm_segment_t old_fs = get_fs ();
- Printk ((KERN_DEBUG "UMSDOS_followlink /mn/: (%.*s/%.*s)\n", (int) dentry->d_parent->d_name.len, dentry->d_parent->d_name.name, (int) dentry->d_name.len, dentry->d_name.name));
+Printk((KERN_DEBUG "UMSDOS_followlink /mn/: (%s/%s)\n",
+dentry->d_parent->d_name.name, dentry->d_name.name));
len = inode->i_size;
if (!(symname = kmalloc (len + 1, GFP_KERNEL))) {
- dentry = ERR_PTR (-EAGAIN); /* correct? */
+ dentry = ERR_PTR (-ENOMEM);
goto outnobuf;
}
+
set_fs (KERNEL_DS); /* we read into kernel space this time */
- PRINTK ((KERN_DEBUG "UMSDOS_followlink /mn/: Here goes umsdos_readlink_x %p, %p, %d\n", dentry, symname, len));
cnt = umsdos_readlink_x (dentry, symname, len);
- PRINTK ((KERN_DEBUG "UMSDOS_followlink /mn/: back from umsdos_readlink_x %p, %p, %d!\n", dentry, symname, len));
set_fs (old_fs);
- Printk ((KERN_DEBUG "UMSDOS_followlink /mn/: link name is %.*s with len %d\n", cnt, symname, cnt));
if (len != cnt) {
dentry = ERR_PTR (-EIO);
goto out;
- } else
- symname[len] = 0;
+ }
- dentry = lookup_dentry (symname, base, 1);
+ symname[len] = 0;
+ dentry = lookup_dentry (symname, base, follow);
kfree (symname);
if (0) {
@@ -139,7 +104,7 @@ static struct dentry *UMSDOS_followlink (struct dentry *dentry, struct dentry *b
return dentry;
}
-
+/* needed to patch the file structure */
static struct file_operations umsdos_symlink_operations =
{
NULL, /* lseek - default */
@@ -158,7 +123,7 @@ static struct file_operations umsdos_symlink_operations =
struct inode_operations umsdos_symlink_inode_operations =
{
- NULL, /* default file operations */
+ NULL, /* default file operations (none) */
NULL, /* create */
NULL, /* lookup */
NULL, /* link */
@@ -169,14 +134,14 @@ struct inode_operations umsdos_symlink_inode_operations =
NULL, /* mknod */
NULL, /* rename */
UMSDOS_readlink, /* readlink */
- UMSDOS_followlink, /* followlink *//* /mn/ is this REALLY needed ? I recall seeing it working w/o it... */
- generic_readpage, /* readpage *//* in original NULL. changed to generic_readpage. FIXME? /mn/ */
+ UMSDOS_followlink, /* followlink */
+ 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 */
NULL, /* updatepage */
NULL /* revalidate */
-
};
+