Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 30 Nov 1995 12:38:18 -0700 (MST)
From:      Terry Lambert <terry@lambert.org>
To:        julian@ref.tfs.com (Julian Elischer)
Cc:        hackers@freebsd.org
Subject:   Re: seekdir broken..
Message-ID:  <199511301938.MAA01261@phaeton.artisoft.com>
In-Reply-To: <199511301149.DAA11852@ref.tfs.com> from "Julian Elischer" at Nov 30, 95 03:49:24 am

next in thread | previous in thread | raw e-mail | index | archive | help
> The following code attempts to read a directory, in small gulps..
> 
> it seems silly, but there is a reason for doing this in WINE.
> The code THINKS it;s stepping through the directory..
> but in fact as we can see, the seekdir is having NO effect at all,
> so we just keep getting the first two entries over and over..
> 
> any ideas?
> this breaks WINE big-time..
> I'm AMAZED nothing else breaks..
> the readdir code is SHIT!
> you can't read two directories  at once because it uses static
> elements!

Man page says:
==========================================================================
     The readdir() function returns a pointer to the next directory entry.  It
     returns NULL upon reaching the end of the directory or detecting an in-
     valid seekdir() operation.

     The telldir() function returns the current location associated with the
     named directory stream.

     The seekdir() function sets the position of the next readdir() operation
     on the directory stream. The new position reverts to the one associated
     with the directory stream when the telldir() operation was performed.
     Values returned by telldir() are good only for the lifetime of the DIR
     pointer, dirp, from which they are derived.  If the directory is closed
     and then reopened, the telldir() value may be invalidated due to unde-
     tected directory compaction.  It is safe to use a previous telldir() val-
     ue immediately after a call to opendir() and before any calls to
     readdir().
==========================================================================

It is bogus to open and close the directory and expect the token to
live past the life of the open.

It is bogus to do the telldir() before the readdir() and expect a
readdir() following a seekdir() to do anything other than return
the current location.

The telldir() return is a *token*.

Assuming the directory has not changed, doing a telldir() *after*
the readdir() would work on BSD.


The real problem is that for WINE, it wants to have a search context
that is a 32 bit value and a drive ID, but the "search context" for a
UNIX system is the dir inode number (32 bits) the offset into the dir
(32 bits) and the mounted device the inode is on (dev_t -- 32 bits).


By converting the findfirst/findnext ot use getdirentries (bletch: see
POSIX section on file times updates to understand the "bletch"), you
can get directories a block (512 bytes) at a time.

By realizing the size of a directory block that is translated to get
the entry, you can traverse the entries in a single block at a time.
Since a block will contain a minimum of 12 bytes (long + short + short +
4 byte aligned name of 1-3 bytes), this give you a maximum of 512/12
entries in a block, worst case (you need 6 bits for this number).

So you steal 8 bits, and the other 24 are the offset of the 512 byte
block in the directory.  The 8 bits are the offset into the directory
of the entry: the lexical offset, not the 12 adjusted offset.

A "findnext" consists of getting block # (off & 0x00ffffff) by seeking
and calling getdents with a 512 byte buffer, then linearly traversing
the entries in the particular buffer to get the ((off >> 24) & 0x000000ff)th
entry.

This will work portably on any POSIX system, since the directory block
read system call is a mandated POSIX system call.

The "correct" mechanism for handling this is to rewrite the WINE
interface and the VOP_READDIR interface to get rid of the stupid cookies
necessitated by rewriting the dricetory entry layout into the user buffer
prematurely.  The VOP_READDIR topic has been discussed to death on this
list before; the majority don't want to provide an export interface for
it for system call and NFS interface consumers, so the problem remains.

> I think I'm going over to see what NetBSD have done about this..

They handle their "cookies" differently, so they luck out.  This doesn't
mean their handling is correct: in point of fact, there should be *no*
cookies whatsoever.  The NFS client "file at a time" requests that they
are provided for could be handled cleanly in the NFS export layer without
a great deal of difficulty, if one existed.  The additional expense is
a directory entry "VOP_PARSEDIR", which for UFS would be a NULL OP.  The
object would be to convert the block, which would be a buffer pointer to
the block buffer in the vnode, to the interface expected by the consumer.

Since no VOP_READDIR consumers actually have a smaller structure than
(struct dirent), there is no failure mode in doing this.  It *will* cause
a reduction in the number of entries returned per getdireentries call,
but since an EOF is signalled by 0 entries returned, this will not
effect typical operation for anything other than NFS.  And on NFS, the
limiting factor is wire time, not computation, in any case.


					Regards,
					Terry Lambert
					terry@lambert.org
---
Any opinions in this posting are my own and not those of my present
or previous employers.



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?199511301938.MAA01261>