patch-2.1.122 linux/fs/umsdos/namei.c
Next file: linux/fs/umsdos/rdir.c
Previous file: linux/fs/umsdos/mangle.c
Back to the patch index
Back to the overall index
- Lines: 1158
- Date:
Mon Sep 14 17:22:25 1998
- Orig file:
v2.1.121/linux/fs/umsdos/namei.c
- Orig date:
Wed Sep 9 14:51:09 1998
diff -u --recursive --new-file v2.1.121/linux/fs/umsdos/namei.c linux/fs/umsdos/namei.c
@@ -224,8 +224,8 @@
*
* 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 +254,9 @@
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)
@@ -284,12 +274,12 @@
if (ret)
goto out_unlock;
- /* 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;
/* should not exist yet ... */
ret = -EEXIST;
@@ -300,39 +290,25 @@
if (ret)
goto out_remove;
- inode = fake->d_inode;
- umsdos_lookup_patch_new(fake, &info.entry, info.f_pos);
-
-Printk (("inode %p[%lu], count=%d ", inode, inode->i_ino, inode->i_count));
-Printk (("Creation OK: [dir %lu] %.*s pid=%d pos %ld\n",
-dir->i_ino, info.fake.len, info.fake.fname, current->pid, info.f_pos));
-
- 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 = fake->d_inode;
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");
+ umsdos_lookup_patch_new(dentry, &info.entry, info.f_pos);
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);
+ 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));
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);
@@ -345,11 +321,24 @@
}
/*
+ * 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;
@@ -359,10 +348,11 @@
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 +363,7 @@
#undef chkstk
#define chkstk() do { } while (0);
+#endif
/*
* Rename a file (move) in the file system.
@@ -382,124 +373,199 @@
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 dentry *old, *new, *old_emd, *new_target = NULL;
+ 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 "));
+ if (is_sticky(old_dir, old_info.entry.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);
+
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;
+ }
+ /*
+ * Note! If the new short- and long-name dentries are
+ * aliases, the target name will be destroyed by the
+ * msdos-level rename. If in addition the old dentries
+ * _aren't_ aliased, we'll need the original new name
+ * for the final d_move, and so must make a copy here.
+ *
+ * Welcome to the mysteries of the dcache ...
+ */
+ if (new == new_dentry) {
+ dput(new);
+ if (old != old_dentry) {
+ /* make a copy of the target dentry */
+ ret = -ENOMEM;
+ new_target = d_alloc(new_dentry->d_parent,
+ &new_dentry->d_name);
+ if (!new_target)
+ goto out_dput;
+ }
+ }
- Printk (("msdos_rename "));
- check_dentry_path (old, "umsdos_rename_f OLD2");
- check_dentry_path (new, "umsdos_rename_f NEW2");
+ /* 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
/*
- * Update f_pos so notify_change will succeed
- * if the file was already in use.
+ * 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 ...
*/
- 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;
+ 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);
}
- d_move (old_dentry, new_dentry);
+ /* dput() the dentry if we haven't already */
out_dput:
- dput(dret);
+ if (old_dentry != old)
+ dput(old);
+ if (ret)
+ goto out_unlock;
+ /*
+ * Check whether to update the dcache ... if both
+ * old and new dentries match, it's already correct.
+ */
+ if (new_dentry != new) {
+ d_move(old_dentry, new_dentry);
+ } else if (old_dentry != old) {
+ /* new dentry was destroyed ... */
+ d_drop(new_dentry);
+ d_add(new_target, NULL);
+ d_move(old_dentry, new_target);
+ }
+
+ /*
+ * Update f_pos so notify_change will succeed
+ * if the file was already in use.
+ */
+ umsdos_set_dirinfo_new(old_dentry, new_info.f_pos);
out_unlock:
- Printk ((KERN_DEBUG "umsdos_rename_f: unlocking dirs...\n"));
+ dput(new_target);
+ 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,11 +575,11 @@
* 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...
@@ -525,37 +591,33 @@
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("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");
+ printk (KERN_WARNING
+ "UMSDOS: Can't write symbolic link data\n");
} else {
ret = 0;
}
}
if (ret != 0) {
+printk("umsdos_symlink: write failed, unlinking\n");
UMSDOS_unlink (dir, dentry);
}
out:
- Printk (("\n"));
return ret;
}
@@ -576,13 +638,20 @@
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,48 +665,141 @@
if (!buffer)
goto out;
- olddir = olddentry->d_parent->d_inode;
umsdos_lockcreate2 (dir, olddir);
- /* get the entry for the old name */
- ret = umsdos_dentry_to_entry(olddentry, &entry);
+ /*
+ * 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));
+ 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;
+ }
- if (!(entry.flags & UMSDOS_HIDDEN)) {
- struct umsdos_info info;
+ /*
+ * If the visible dentry is a pseudo-hardlink, the original
+ * file must be already hidden.
+ */
+ if (!(old_info.entry.flags & UMSDOS_HLINK)) {
+ int err;
- ret = umsdos_newhidden (olddentry->d_parent, &info);
- if (ret)
+ /* 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);
@@ -647,7 +809,15 @@
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);
}
@@ -657,24 +827,6 @@
/*
- * 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");
- return ret;
-}
-
-
-/*
* Add a sub-directory in a directory
*/
/* #Specification: mkdir / Directory already exist in DOS
@@ -699,10 +851,8 @@
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;
@@ -713,14 +863,12 @@
info.entry.flags = 0;
info.entry.nlink = 1;
ret = umsdos_newentry (dentry->d_parent, &info);
- if (ret) {
-Printk (("newentry %d ", ret));
+ if (ret)
goto out_unlock;
- }
/* 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;
@@ -737,36 +885,27 @@
if (ret)
goto out_remove;
- inode = temp->d_inode;
- umsdos_lookup_patch_new(temp, &info.entry, info.f_pos);
-
/*
* Note! The long and short name might be the same,
* so check first before doing the instantiate ...
*/
if (dentry != temp) {
- if (!dentry->d_inode) {
- inode->i_count++;
- d_instantiate(dentry, inode);
- } else {
- printk("umsdos_mkdir: not negative??\n");
- }
- } else {
- printk("umsdos_mkdir: dentries match, skipping inst\n");
+if (dentry->d_inode)
+printk("umsdos_mkdir: dentry not negative!\n");
+ inode = temp->d_inode;
+ inode->i_count++;
+ d_instantiate(dentry, inode);
}
-
- /* create the EMD file */
- err = umsdos_make_emd(dentry);
+ umsdos_lookup_patch_new(dentry, &info.entry, info.f_pos);
/*
- * 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 */
@@ -776,9 +915,14 @@
out_unlock:
umsdos_unlockcreate (dir);
- Printk (("umsdos_mkdir %d\n", ret));
out:
+ Printk (("umsdos_mkdir %d\n", ret));
return ret;
+
+ /* an error occurred ... remove EMD entry. */
+out_remove:
+ umsdos_delentry (dentry->d_parent, &info, 1);
+ goto out_dput;
}
/*
@@ -801,11 +945,7 @@
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,13 +961,6 @@
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)
- goto out;
-#endif
-
umsdos_lockcreate (dir);
ret = -EBUSY;
if (dentry->d_count > 1) {
@@ -839,6 +972,14 @@
}
}
+ /* 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;
+ }
+
/* check whether the EMD is empty */
empty = umsdos_isempty (dentry);
ret = -ENOTEMPTY;
@@ -848,24 +989,21 @@
/* 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;
- }
+
+Printk (("UMSDOS_rmdir: unlinking empty EMD err=%d", err));
ret = -ENOTEMPTY;
/* see if there's an EMD file ... */
demd = umsdos_get_emd_dentry(dentry);
if (IS_ERR(demd))
goto out_unlock;
-printk("umsdos_rmdir: got EMD dentry %s/%s, inode=%p\n",
-demd->d_parent->d_name.name, demd->d_name.name, demd->d_inode);
err = msdos_unlink (dentry->d_inode, demd);
-Printk (("UMSDOS_rmdir: unlinking empty EMD err=%d", err));
+#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)
goto out_unlock;
@@ -875,7 +1013,7 @@
/* 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;
@@ -884,8 +1022,8 @@
*/
if (temp == dentry) {
dput(temp);
-printk("umsdos_rmdir: %s/%s, short matches long\n",
-dentry->d_parent->d_name.name, dentry->d_name.name);
+Printk(("umsdos_rmdir: %s/%s, short matches long\n",
+dentry->d_parent->d_name.name, dentry->d_name.name));
}
/*
@@ -897,16 +1035,18 @@
/* 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 */
+out_dput:
if (temp != dentry) {
d_drop(temp);
dput(temp);
if (!ret)
d_delete (dentry);
-printk("umsdos_rmdir: %s/%s, short=%s dput\n",
-dentry->d_parent->d_name.name, dentry->d_name.name, info.fake.fname);
}
out_unlock:
@@ -922,8 +1062,8 @@
* 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 +1071,12 @@
*/
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 +1090,13 @@
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 +1105,54 @@
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 +1161,60 @@
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.
+ */
+ 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 (link, &newattrs);
+ }
+
+out_cleanup:
+ d_drop(link);
+ dput(link);
+
out:
Printk (("umsdos_unlink %d\n", ret));
return ret;
@@ -1058,20 +1237,28 @@
if (ret != -EEXIST)
goto out;
+ /*
+ * Something seems to be giving negative lookups when
+ * the file really exists ... track this down!
+ */
+ ret = -EIO;
+ if (!new_dentry->d_inode) {
+printk("UMSDOS_rename: %s/%s negative, error EEXIST??\n",
+new_dentry->d_parent->d_name.name, new_dentry->d_name.name);
+ d_drop(new_dentry);
+ 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) {
+ if (S_ISDIR(new_dentry->d_inode->i_mode))
ret = UMSDOS_rmdir (new_dir, new_dentry);
- chkstk ();
- Printk (("rename rmdir ret %d -- ", ret));
- }
+ else
+ ret = UMSDOS_unlink (new_dir, new_dentry);
if (ret)
goto out;
/* this time the rename should work ... */
- ret = umsdos_rename_f (old_dir, old_dentry, new_dir, new_dentry, 0);
+ ret = umsdos_rename_f(old_dir, old_dentry, new_dir, new_dentry, 0);
out:
return ret;
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov