diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1999-01-04 16:03:48 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 1999-01-04 16:03:48 +0000 |
commit | 78c388aed2b7184182c08428db1de6c872d815f5 (patch) | |
tree | 4b2003b1b4ceb241a17faa995da8dd1004bb8e45 /fs/umsdos | |
parent | eb7a5bf93aaa4be1d7c6181100ab7639e74d67f7 (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.txt | 198 | ||||
-rw-r--r-- | fs/umsdos/check.c | 82 | ||||
-rw-r--r-- | fs/umsdos/dir.c | 610 | ||||
-rw-r--r-- | fs/umsdos/emd.c | 259 | ||||
-rw-r--r-- | fs/umsdos/file.c | 16 | ||||
-rw-r--r-- | fs/umsdos/inode.c | 609 | ||||
-rw-r--r-- | fs/umsdos/ioctl.c | 54 | ||||
-rw-r--r-- | fs/umsdos/mangle.c | 1 | ||||
-rw-r--r-- | fs/umsdos/namei.c | 1001 | ||||
-rw-r--r-- | fs/umsdos/rdir.c | 111 | ||||
-rw-r--r-- | fs/umsdos/specs | 2 | ||||
-rw-r--r-- | fs/umsdos/symlink.c | 89 |
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 */ - }; + |