summaryrefslogtreecommitdiffstats
path: root/fs/umsdos/namei.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/umsdos/namei.c')
-rw-r--r--fs/umsdos/namei.c1001
1 files changed, 617 insertions, 384 deletions
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;