delorie.com/archives/browse.cgi   search  
Mail Archives: djgpp-workers/2003/03/10/18:21:49

Sender: rich AT phekda DOT freeserve DOT co DOT uk
Message-ID: <3E6D1DFC.D1231827@phekda.freeserve.co.uk>
Date: Mon, 10 Mar 2003 23:21:32 +0000
From: Richard Dawe <rich AT phekda DOT freeserve DOT co DOT uk>
X-Mailer: Mozilla 4.77 [en] (X11; U; Linux 2.2.23 i586)
X-Accept-Language: de,fr
MIME-Version: 1.0
To: DJGPP workers <djgpp-workers AT delorie DOT com>, Jim Meyering <jim AT meyering DOT net>
Subject: Design for fchdir, revision 2
Reply-To: djgpp-workers AT delorie DOT com

Hello.

Below is revision 2 of the design for fchdir in DJGPP. I've actually
implemented this - a patch will follow.

You can also get it here:

    http://www.phekda.freeserve.co.uk/richdawe/djgpp/2.04/fchdir.txt

Bye, Rich =]

-- 
Richard Dawe [ http://www.phekda.freeserve.co.uk/richdawe/ ]

Design for fchdir in DJGPP
~~~~~~~~~~~~~~~~~~~~~~~~~~

Introduction
------------

The new POSIX standard requires that an implementation provides
the fchdir function. DJGPP CVS (to be 2.04) could support
an fchdir function. fchdir is like chdir, but it changes to the directory
specified by a file descriptor. This file descriptor refers to a directory.
So supporting fchdir would require the normal Unixy I/O functions -
open, read, write, etc. - to work with file descriptors referring
to directories.

The fd_props mechanism, which associates some flags and a filename
with a file descriptor, can be used, to mark the file descriptor
as a directory. This will allow the Unixy I/O functions
to handle directories specially. A new flag FILE_DESC_DIRECTORY
would be added.

Only POSIX and other Unixy functions will be modified to support
file descriptors for directories.

The file descriptor will be created so that:

* it is dup'd off nul;
* it is in binary mode;
* it is non-inheritable.

The first two steps can be accomplished using __FSEXT_alloc_fd.
Note that we use __FSEXT_alloc_fd for convenience only -
this is *not* a proposal to use the FSEXT mechanism to support
directory file descriptors.

There are two ways to make the file descriptor non-inheritable.
Both methods will be used:

* ensure that the file descriptor is > 19 - file handles above 19
  are not inheritable, due to a misfeature in the DOS exec call;

* set the no-inherit (close-on-exec) bit using fcntl(..., F_GETFD)
  and fcntl(..., F_SETFD, FD_CLOEXEC|...).

(See move_fd in src/debug/common/dbgredir.c on how to do the first
part.)

POSIX allows us to restrict how directories are handled by
the Unixy I/O functions. So let's make the following restrictions:

* Directories can only be opened read-only.
* Directories can only be read using readdir().

The directory-specific code would be after File System Extensions
(FSEXTs) have had their chance to handle I/O operations.

Now to how specific functions would be updated or implemented:

open
----

src/libc/posix/fcntl/open.c
src/dos/io/_creat.c
src/dos/io/_creat_n.c
src/dos/io/_open.c

I suggest that directories are handled first as a special case
in open. Once the normal open call has failed, if the filename refers
to a directory (check with access(..., D_OK), open would call
this function, say __opendir_as_fd, and then return its return code.

__opendir_as_fd would:

* check that the directory was being opened as read-only - if not,
  fail with errno == EISDIR;

* allocate a file descriptor for the directory;

* set the FILE_DESC_DIRECTORY flag in fd_props for the file descriptor;

* return this file descriptor.

We should probably also check that O_APPEND was not specified.
If it has, return errno == EINVAL. LATER: Don't check this.
Since the call will fail, if write flags are given, is there
any point checking for O_APPEND?

Since _open is a DOS-specific function described as "a direct connection
to the MS-DOS open function call", it requires no attention. It fails
for directories.

ISSUE: Do we want to support the O_DIRECTORY option that Linux does?
Here's a description, taken from open(2):

"O_DIRECTORY
              If pathname is not a directory, cause the  open  to
              fail.   This  flag is Linux-specific, and was added
              in kernel version 2.1.126, to avoid  denial-of-ser-
              vice  problems if opendir(3) is called on a FIFO or
              tape device, but should not be used outside of  the
              implementation of opendir."

I think not, but I mention it here for completeness. If we ever
want to use open in libc to create a directory, then we should
support O_DIRECTORY. But I can't think of any another reason.

read
----

src/libc/posix/unistd/read.c
src/libc/dos/io/_read.c

We can handle both read and _read, by adding a check to _read,
after the FSEXT handler has been called. It would try to find the flags
for the file descriptor by using __get_fd_flags. If it can
and the file descriptor refers to a directory, it would fail with
errno == EISDIR. (EISDIR indicates that readdir should be used instead.)

LATER: Only do it for the POSIX function read. Also, there's no need
to wait for the FSEXT handler to be called, since we can't hook
directories with FSEXTs.

write
-----

src/libc/posix/unistd/write.c
src/libc/dos/io/_write.c

We can handle both write and _write, by adding a check to _write,
after the FSEXT handler has been called. It would try to find the flags
for the file descriptor by using __get_fd_flags. If it can
and the file descriptor refers to a directory, it would fail with
errno == EACCES (LATER: EBADF).

NOTE: POSIX seems a bit unclear here to me. It says that the errno == EBADF
should be used when "The fildes argument is not a valid file descriptor
open for writing." This covers two error conditions: invalid file
descriptor; file descriptor not open for writing. So I'm not sure that
errno == EACCES is correct above. LATER: EACCESS to mean a lot of things
in DJGPP (because of DOS's error codes), so we should use EBADF.

IMPLEMENTATION NOTE: It'll be important here that the file descriptor
is in binary mode, so that no ASCII-conversion code is executed
in write for the directory's file descriptor. If the file descriptor
is in binary mode, it will just call _write.

LATER: Only do it for the POSIX function write. Also, there's no need
to wait for the FSEXT handler to be called, since we can't hook
directories with FSEXTs.

close
-----

No changes would be required.

lseek, llseek, tell
-------------------

src/libc/posix/unistd/lseek.c
src/libc/compat/unistd/llseek.c
src/libc/dos/io/tell.c

These all go through llseek. It would try to find the flags
for the file descriptor by using __get_fd_flags. If it can
and the file descriptor refers to a directory, it would always
return 0.

ftruncate
---------

src/libc/compat/unistd/ftruncat.c

It would try to find the flags for the file descriptor by using
__get_fd_flags. If it can and the file descriptor refers to a directory,
it would fail with errno == EINVAL.

fchmod
------

src/libc/posix/sys/stat/fchmod.c
src/libc/posix/sys/stat/chmod.c

No changes would be required, since this uses chmod() on the file name
obtained from fd_props or the long filename (LFN) API.

fchown
------

src/libc/compat/unistd/fchown.c

As fchown.c says:

/* MS-DOS couldn't care less about file ownerships, so we
   at least check if given handle is valid. */

Since the directory will be dup'd off nul, it will have a valid handle
(aka file descriptor). No changes would be required.

ioctl
-----

src/libc/compat/ioctl/ioctl.c

DOS ioctls:  Assume the programmer knows what he/she is doing.
Unix ioctls: There aren't any interesting ones.

No changes would be required.

fcntl
-----

src/libc/posix/fcntl/fcntl.c

The locking code would be updated to fail with -1 and errno == EINVAL
for directories. Other than that, no changes would be required.

lockf, llockf
-------------

src/libc/compat/unistd/lockf.c
src/libc/compat/unistd/llockf.c

These use the fcntl interface. No changes would be required.

fstat
-----

src/libc/posix/sys/stat/fstat.c

fstat_assist currently assumes that it will not have to handle
directories, because directories cannot be opened. This will
need re-examining.

LATER: We should handle directories specially in fstat, just after
the fstat FSEXT hook has been called.

select
------

src/libc/compat/time/time.c

select() assumes that the file descriptor refers to a file or device.
Disk files are always considered to be ready for reading and writing.
I suggest that directories should always be considered to be ready
for reading and never for writing (since they're supposed to be read-only).

fd_input_ready and fd_output_ready could be modified to check
for directories. Or perhaps it would be better to add another check
after the __FSEXT_ready check, so that we don't pollute
fd_(in|out)put_ready with directory-specific code.

fsync
-----

src/libc/compat/unistd/fsync.c

This would fail with errno == EINVAL, since we can't sync directories.

fchdir
------

Phew, finally. 8)

Probably: src/libc/posix/unistd/fchdir.c

This would the get the directory's filename from the fd_props
and use chdir to change to it. If fchdir was called on a normal file,
it would fail with errno == ENOTDIR.

Finally
-------

Thanks to Eli Zaretskii for reviewing and commenting on the design.

Richard Dawe <rich AT phekda DOT freeserve DOT co DOT uk>

$Id: fchdir.txt,v 1.6 2003/03/10 23:13:33 rich Exp $

- Raw text -


  webmaster     delorie software   privacy  
  Copyright © 2019   by DJ Delorie     Updated Jul 2019