www.delorie.com/archives/browse.cgi   search  
Mail Archives: djgpp-workers/2009/08/09/09:51:52

X-Authentication-Warning: delorie.com: mail set sender to djgpp-workers-bounces using -f
X-Recipient: djgpp-workers AT delorie DOT com
X-Authenticated: #27081556
X-Provags-ID: V01U2FsdGVkX18brdpvYi0bpQNX99XPYSU3aqF4fvSsKGD6Isim+Z
njpqm9RqCRwauY
From: Juan Manuel Guerrero <juan DOT guerrero AT gmx DOT de>
To: djgpp-workers AT delorie DOT com
Subject: fopen/open and pathnames with trailing slash
Date: Sun, 9 Aug 2009 15:51:18 +0200
User-Agent: KMail/1.9.10
MIME-Version: 1.0
Message-Id: <200908091551.19052.juan.guerrero@gmx.de>
X-Y-GMX-Trusted: 0
X-FuHaFi: 0.42
Reply-To: djgpp-workers AT delorie DOT com

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 -


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