patch-2.1.122 linux/fs/umsdos/dir.c
Next file: linux/fs/umsdos/emd.c
Previous file: linux/fs/umsdos/README-WIP.txt
Back to the patch index
Back to the overall index
- Lines: 649
- Date:
Mon Sep 14 17:22:25 1998
- Orig file:
v2.1.121/linux/fs/umsdos/dir.c
- Orig date:
Wed Sep 9 14:51:09 1998
diff -u --recursive --new-file v2.1.121/linux/fs/umsdos/dir.c linux/fs/umsdos/dir.c
@@ -22,7 +22,36 @@
#define UMSDOS_SPECIAL_DIRFPOS 3
extern struct inode *pseudo_root;
+/* #define UMSDOS_DEBUG_VERBOSE 1 */
+/*
+ * Dentry operations routines
+ */
+
+/* nothing for now ... */
+static int umsdos_dentry_validate(struct dentry *dentry)
+{
+ return 1;
+}
+
+/* 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);
+ }
+}
+
+static 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,
+};
/*
* This needs to have the parent dentry passed to it.
@@ -109,9 +138,9 @@
* 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,
@@ -175,8 +204,10 @@
if (IS_ERR(demd))
goto out_end;
ret = 0;
- if (!demd->d_inode)
+ if (!demd->d_inode) {
+printk("no EMD file??\n");
goto out_dput;
+ }
/* set up our private filp ... */
fill_new_filp(&new_filp, demd);
@@ -189,35 +220,63 @@
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 +284,21 @@
* 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 +308,17 @@
* 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 +362,6 @@
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)
@@ -492,7 +556,6 @@
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",
@@ -509,8 +572,8 @@
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);
+ printk ("umsdos_dentry_to_entry: ino=%ld, err=%d\n",
+ inode->i_ino, err);
break;
}
if (bufk.ino == inode->i_ino) {
@@ -523,73 +586,6 @@
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.
@@ -638,6 +634,11 @@
int ret = -ENOENT;
struct umsdos_info info;
+#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);
/* this shouldn't happen ... */
if (len == 1 && name[0] == '.') {
@@ -664,38 +665,57 @@
}
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);
+#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 +725,22 @@
* which are recorded independently of the pseudo-root
* mode.
*/
- Printk (("umsdos_lookup_x: untested Pseudo_root\n"));
- ret = -ENOENT;
+printk(KERN_WARNING "umsdos_lookup_x: untested, inode == Pseudo_root\n");
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 != dentry)
+ d_drop(dret);
dput(dret);
out:
umsdos_endlookup (dir);
@@ -729,10 +748,10 @@
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 +759,7 @@
* 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 +773,7 @@
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 +786,8 @@
* 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 +802,9 @@
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 +818,134 @@
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);
- }
-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);
+#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
+ }
+ } 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 +987,4 @@
NULL, /* smap */
NULL, /* updatepage */
NULL, /* revalidate */
-
};
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov