Ben Fennema wrote:
> My first question is, CAN I actually use lookup and readdir on a file
> (Is it ls being too smart by looking at the thing, deceiding its not a
> directory, and giving up -- or is it the kernel?)

The kernel will more or less do what you ask, though Alex Viro's
concerns are valid.

In particular, from my quick skim of fs/namei.c and fs/open.c, namei
will accept your pseudo-directory as a path component, open with
O_DIRECTORY will open it, and readdir will read it.

So almost everything is there for you to try.  The one glaring
inconsistency there is that you can't chdir to it.  I've attached a
patch (compiled but untested) which should fix that.

The reason you might like to try it but _don't_ make it mainstream are
things like this: you open a file inside your pseudo-directory.  You
unlink() the pseudo-directory.  The unlink _succeeds_ because it passes
the !S_ISDIR check.  Now what happens to the child dentries?

Probably it could all be made to work, by simply removing _all_ S_ISDIR
checks in the VFS that affect correctness (just keep any that must be
used for POSIX permissions, if there are any).  And replace those
S_ISDIR checks by a single is_directory macro that checks i_ops _or_
S_ISDIR.  Replace the i_ops check in namei by a call to the macro too.

Then VFS and namei would at least be consistent, and probably it would
all work because they'd consistently treat pseudo-directories as
directories.


Your specific question:

ls
--

The problem is probably Glibc.

Glibc's opendir() opens with O_DIRECTORY -- the kernel only requires
that you have a lookup method for that.  If so the open should succeed.

(Aside: perhaps the kernel should check for readdir _or_ lookup to
permit O_DIRECTORY).

Glibc's opendir then continues with an fstat() and fcntl().  Oh dear --
that will detect the S_ISDIR is not set.  That's why the fstat() is
there -- older kernels would simply ignore O_DIRECTORY, so it's
necessary to fstat() after opening.

It would be possible to change this in Glibc however -- if it's running
on a recent enough kernel, it can assume that when an O_DIRECTORY
succeeds, it has a directory.

Next you get readdir().  In the kernel, that just checks for a readdir
method.

Summary: ls will work if you change opendir() in Glibc to not call fstat().


chdir
-----

So, ls should work after fixing Glibc.  Meanwhile, why not just cd into
the "directory"?

Here the kernel is pickier.  It will let you read and lookup in a
non-directory directory, but it won't let you chdir there.  It does a
specific !S_ISDIR check.

My feeling is that whatever conditions are considered ok for namei to
lookup a directory, should be exactly the same conditions for chdir.

If you can open with O_DIRECTORY or trailing slash, readdir it, make
files in it, make directory and use it as a path component, there is
surely no harm in permitting chdir/fchdir as well.


Overall summary
---------------

The obvious anomalies for the user experience are chdir/fchdir.  I think
they should be changed to call __namei() with
LOOKUP_DIRECTORY|LOOKUP_FOLLOW, instead of just LOOKUP_FOLLOW as they do
now.  (namei(s) just calls __namei(s,LOOKUP_FOLLOW)).  And drop the
S_ISDIR check.

With that tiny change you could chdir into your UDF directories 

Alex might still want to place restrictions, but the proper place for
that is _one_ place: __namei itself.

I've attached a patch which fixed the chdir/fchdir case.

There are other things which check the mode: rmdir, truncate, rename and
mount.

The check in vfs_rename looks important and scary.  The check in
do_rename is a naming policy thing, and it would be ok to check i_ops or
leave it as is.  (I'd change it).

truncate just aborts if S_ISDIR _is_ set -- that seems ok.  mount aborts
if it isn't set -- quite sensible.

In all, it looks like there really isn't much needed to tidy this up.

enjoy,
-- Jamie

diff -u linux-2.3/fs/open.c.xdir linux-2.3/fs/open.c
--- linux-2.3/fs/open.c.xdir    Sun Mar  5 18:31:18 2000
+++ linux-2.3/fs/open.c Sun Mar 12 01:18:19 2000
@@ -341,17 +341,13 @@
 
        lock_kernel();
        
-       dentry = namei(filename);
+       dentry = dnamei(filename);
        error = PTR_ERR(dentry);
        if (IS_ERR(dentry))
                goto out;
 
        inode = dentry->d_inode;
 
-       error = -ENOTDIR;
-       if (!S_ISDIR(inode->i_mode))
-               goto dput_and_out;
-
        error = permission(inode,MAY_EXEC);
        if (error)
                goto dput_and_out;
@@ -387,7 +383,7 @@
                goto out_putf;
 
        error = -ENOTDIR;
-       if (!S_ISDIR(inode->i_mode))
+       if (!inode->i_op || !inode->i_op->lookup)
                goto out_putf;
 
        lock_kernel();
@@ -412,16 +408,12 @@
 
        lock_kernel();
        
-       dentry = namei(filename);
+       dentry = dnamei(filename);
        error = PTR_ERR(dentry);
        if (IS_ERR(dentry))
                goto out;
 
        inode = dentry->d_inode;
-
-       error = -ENOTDIR;
-       if (!S_ISDIR(inode->i_mode))
-               goto dput_and_out;
 
        error = permission(inode,MAY_EXEC);
        if (error)
diff -u linux-2.3/include/linux/fs.h.xdir linux-2.3/include/linux/fs.h
--- linux-2.3/include/linux/fs.h.xdir   Sun Mar  5 21:14:39 2000
+++ linux-2.3/include/linux/fs.h        Sun Mar 12 00:55:39 2000
@@ -955,6 +955,7 @@
 
 #define namei(pathname)                __namei(pathname, 1)
 #define lnamei(pathname)       __namei(pathname, 0)
+#define dnamei(pathname)       __namei(pathname, 3)
 
 extern void iput(struct inode *);
 extern struct inode * igrab(struct inode *);

Reply via email to