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 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 , Jim Meyering Subject: Design for fchdir, revision 2 Content-Type: text/plain; charset=us-ascii Content-Transfer-Encoding: 7bit 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 $Id: fchdir.txt,v 1.6 2003/03/10 23:13:33 rich Exp $