Mail Archives: djgpp-workers/2009/08/09/09:51:52
While I was trying to port m4 I have noticed that the configure script complains
that both functions fopen() and open() do not handle correctly the case when the
pathnames end with a slash.
1) According to POSIX: If the filename contains at least one non-slash character
and ends with one or more trailing slashes and one of the modes O_CREAT,
O_WRONLY or O_RDWR is specified, then open() must fail with EISDIR.
The same applies for fopen() if the modes w or a have been specified.
2) According to POSIX: If the filename ends in a slash and the file descriptor
of the named file without the slash does not refer to a directory, then open()
must fail with ENOTDIR.
See: <http://www.opengroup.org/susv3/basedefs/xbd_chap04.html>,
<http://www.opengroup.org/susv3/functions/fopen.html> and
<http://www.opengroup.org/susv3/functions/open.html>
Because open() is also called by fopen() to do the job, the issue needs only to
be fixed there. Because I need to operate with the filename I had to check that
the string is not NULL or empty. In that case fopen()/open() terminates with EINVAL.
I do not know if this is correct, but I need to handle this case in some way.
As usual, suggestions, objections, comments are welcome.
Regards,
Juan M. Guerrero
2009-07-17 Juan Manuel Guerrero <juan DOT guerrero AT gmx DOT de>
Diffs against djgpp CVS head of 2009-04-14.
* src/libc/posix/fcntl/open.c: Implementation of POSIX conforming
handling of pathnames ending with a slash.
* tests/libc/posix/fcntl/open.c: Checks added to test implementation
of POSIX conforming handling of pathnames ending with a slash.
* src/docs/kb/wc204.txi: Info about open() added.
diff -aprNU5 djgpp.orig/src/docs/kb/wc204.txi djgpp/src/docs/kb/wc204.txi
--- djgpp.orig/src/docs/kb/wc204.txi 2009-04-13 12:34:56 +0000
+++ djgpp/src/docs/kb/wc204.txi 2009-07-17 20:41:30 +0000
@@ -1181,5 +1181,13 @@ Those functions that require the trailin
@findex mkstemp AT r{, and SUS compliance}
The function prototypes of @code{mktemp} and @code{mkstemp} are now also in
@code{<sdtlib.h>}. This is to achieve Single Unix Specification compliance.
To keep backward compatibility, the prototypes are also kept in @code{<sdtio.h>}
but their usage is deprecated.
+
+@findex fopen AT r{, and SUS compliance}
+@findex open AT r{, and SUS compliance}
+To achieve @acronym{Single Unix Specification} compliance, the functions @code{fopen}
+and @code{open} will fail with EISDIR if the pathname ends with a slash and one of
+@code{O_CREAT}, @code{O_WRONLY} or @code{O_RDWR} is specified. They will also fail
+with ENOTDIR if @code{O_RDONLY} is specified and the named file ends with a slash
+but it is not a directory.
diff -aprNU5 djgpp.orig/src/libc/posix/fcntl/open.c djgpp/src/libc/posix/fcntl/open.c
--- djgpp.orig/src/libc/posix/fcntl/open.c 2003-11-21 22:54:56 +0000
+++ djgpp/src/libc/posix/fcntl/open.c 2009-07-17 20:55:46 +0000
@@ -1,5 +1,6 @@
+/* Copyright (C) 2009 DJ Delorie, see COPYING.DJ for details */
/* Copyright (C) 2003 DJ Delorie, see COPYING.DJ for details */
/* Copyright (C) 2001 DJ Delorie, see COPYING.DJ for details */
/* Copyright (C) 2000 DJ Delorie, see COPYING.DJ for details */
/* Copyright (C) 1999 DJ Delorie, see COPYING.DJ for details */
/* Copyright (C) 1998 DJ Delorie, see COPYING.DJ for details */
@@ -10,10 +11,11 @@
#include <libc/symlink.h>
#include <libc/unconst.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
+#include <stdbool.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
@@ -21,10 +23,16 @@
#include <sys/fsext.h>
#include <libc/dosio.h>
#include <libc/fd_props.h>
+#define IS_DRIVE_SPECIFIER(path) ((path)[0] && ((path)[1] == ':'))
+#define IS_ROOT_DIR(path) ((IS_DRIVE_SPECIFIER(path) && IS_SLASH(path[2]) && ((path)[3] == '\0')) || \
+ (IS_SLASH(path[0]) && ((path)[1] == '\0')))
+#define IS_SLASH(path) ((path) == '/' || (path) == '\\')
+
+
/* Extra share flags that can be indicated by the user */
int __djgpp_share_flags;
/* Move a file descriptor FD such that it is at least MIN_FD.
If the file descriptor is changed (meaning it was origially
@@ -108,14 +116,35 @@ opendir_as_fd (const char *filename, con
int
open(const char* filename, int oflag, ...)
{
const int original_oflag = oflag;
- int fd, dmode, bintext, dont_have_share;
+ int fd, dmode, bintext;
char real_name[FILENAME_MAX + 1];
- int should_create = (oflag & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL);
- int dirs_solved = 0; /* Only directories resolved in real_name? */
+ bool should_create = (oflag & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL);
+ bool dirs_solved = false; /* Only directories resolved in real_name? */
+ bool dont_have_share, is_root_dir;
+ size_t length;
+
+
+ if (!(length = strlen(filename)))
+ {
+ errno = EINVAL;
+ return -1;
+ }
+ else
+ is_root_dir = IS_ROOT_DIR(filename);
+
+ /* According to POSIX: If the filename contains at least one
+ non-slash character and ends with one or more trailing slashes
+ and one of O_CREAT, O_WRONLY, O_RDWR is specified, then fail. */
+ if (((oflag & (O_RDONLY | O_CREAT | O_WRONLY | O_RDWR)) != O_RDONLY) && \
+ !is_root_dir && IS_SLASH(filename[length - 1]))
+ {
+ errno = EISDIR;
+ return -1;
+ }
/* Solve symlinks and honor O_NOLINK flag */
if (oflag & O_NOLINK)
{
if (!__solve_dir_symlinks(filename, real_name))
@@ -256,10 +285,19 @@ open(const char* filename, int oflag, ..
/* Don't call _creat on existing files for which _open fails,
since the file could be truncated as a result. */
else if ((oflag & O_CREAT))
fd = _creat(real_name, dmode);
}
+ else
+ /* According to POSIX: If the named file without the slash
+ is not a directory, open() must fail with ENOTDIR. */
+ if (!is_root_dir && IS_SLASH(filename[length - 1]) && access(real_name, D_OK))
+ {
+ close(fd);
+ errno = ENOTDIR;
+ return -1;
+ }
}
/* Is the target a directory? If so, generate a file descriptor
* for the directory. Skip the rest of `open', because it does not
* apply to directories. */
diff -aprNU5 djgpp.orig/tests/libc/posix/fcntl/open.c djgpp/tests/libc/posix/fcntl/open.c
--- djgpp.orig/tests/libc/posix/fcntl/open.c 2003-11-21 22:54:56 +0000
+++ djgpp/tests/libc/posix/fcntl/open.c 2009-07-17 20:28:18 +0000
@@ -115,10 +115,58 @@ int main(void)
testnum, strerror(errno));
exit(EXIT_FAILURE);
}
printf("Test %d passed\n", testnum);
+ ++testnum;
+ fd = open("test3/", O_CREAT | O_WRONLY | O_RDWR);
+ if (fd != -1)
+ {
+ fprintf(stderr, "Test %d failed - unexpected open() success.\n",
+ testnum);
+ exit(EXIT_FAILURE);
+ }
+ if (errno != EISDIR)
+ {
+ fprintf(stderr, "Test %d failed - wrong errno returned: %s\n",
+ testnum, strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ printf("Test %d passed\n", testnum);
+
+ ++testnum;
+ fd = open("test3/", O_RDONLY );
+ if (fd != -1)
+ {
+ fprintf(stderr, "Test %d failed - unexpected open() success.\n",
+ testnum);
+ exit(EXIT_FAILURE);
+ }
+ if (errno != ENOTDIR)
+ {
+ fprintf(stderr, "Test %d failed - wrong errno returned: %s\n",
+ testnum, strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ printf("Test %d passed\n", testnum);
+
+ ++testnum;
+ fd = open("", O_CREAT | O_WRONLY | O_RDWR | O_EXCL | O_NOLINK);
+ if (fd != -1)
+ {
+ fprintf(stderr, "Test %d failed - unexpected open() success.\n",
+ testnum);
+ exit(EXIT_FAILURE);
+ }
+ if (errno != EINVAL)
+ {
+ fprintf(stderr, "Test %d failed - wrong errno returned: %s\n",
+ testnum, strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ printf("Test %d passed\n", testnum);
+
test_success("test2/test1", O_RDONLY | O_NOLINK, "!<symlink>");
test_success("dir/test2", O_RDONLY, "tstlink2");
test_o_temporary();
puts("PASS");
return EXIT_SUCCESS;
- Raw text -