]> git.cworth.org Git - tar/blob - gnu/open.c
7c2bbb81d975266bc34162f1a494a35f55524b67
[tar] / gnu / open.c
1 /* -*- buffer-read-only: t -*- vi: set ro: */
2 /* DO NOT EDIT! GENERATED AUTOMATICALLY! */
3 /* Open a descriptor to a file.
4    Copyright (C) 2007-2010 Free Software Foundation, Inc.
5
6    This program is free software: you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 3 of the License, or
9    (at your option) any later version.
10
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    You should have received a copy of the GNU General Public License
17    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
18
19 /* Written by Bruno Haible <bruno@clisp.org>, 2007.  */
20
21 #include <config.h>
22
23 /* Get the original definition of open.  It might be defined as a macro.  */
24 #define __need_system_fcntl_h
25 #include <fcntl.h>
26 #undef __need_system_fcntl_h
27 #include <sys/types.h>
28
29 static inline int
30 orig_open (const char *filename, int flags, mode_t mode)
31 {
32   return open (filename, flags, mode);
33 }
34
35 /* Specification.  */
36 #include <fcntl.h>
37
38 #include <errno.h>
39 #include <stdarg.h>
40 #include <string.h>
41 #include <sys/types.h>
42 #include <sys/stat.h>
43 #include <unistd.h>
44
45 #ifndef REPLACE_OPEN_DIRECTORY
46 # define REPLACE_OPEN_DIRECTORY 0
47 #endif
48
49 int
50 open (const char *filename, int flags, ...)
51 {
52   mode_t mode;
53   int fd;
54
55   mode = 0;
56   if (flags & O_CREAT)
57     {
58       va_list arg;
59       va_start (arg, flags);
60
61       /* We have to use PROMOTED_MODE_T instead of mode_t, otherwise GCC 4
62          creates crashing code when 'mode_t' is smaller than 'int'.  */
63       mode = va_arg (arg, PROMOTED_MODE_T);
64
65       va_end (arg);
66     }
67
68 #if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
69   if (strcmp (filename, "/dev/null") == 0)
70     filename = "NUL";
71 #endif
72
73 #if OPEN_TRAILING_SLASH_BUG
74   /* If the filename ends in a slash and one of O_CREAT, O_WRONLY, O_RDWR
75      is specified, then fail.
76      Rationale: POSIX <http://www.opengroup.org/susv3/basedefs/xbd_chap04.html>
77      says that
78        "A pathname that contains at least one non-slash character and that
79         ends with one or more trailing slashes shall be resolved as if a
80         single dot character ( '.' ) were appended to the pathname."
81      and
82        "The special filename dot shall refer to the directory specified by
83         its predecessor."
84      If the named file already exists as a directory, then
85        - if O_CREAT is specified, open() must fail because of the semantics
86          of O_CREAT,
87        - if O_WRONLY or O_RDWR is specified, open() must fail because POSIX
88          <http://www.opengroup.org/susv3/functions/open.html> says that it
89          fails with errno = EISDIR in this case.
90      If the named file does not exist or does not name a directory, then
91        - if O_CREAT is specified, open() must fail since open() cannot create
92          directories,
93        - if O_WRONLY or O_RDWR is specified, open() must fail because the
94          file does not contain a '.' directory.  */
95   if (flags & (O_CREAT | O_WRONLY | O_RDWR))
96     {
97       size_t len = strlen (filename);
98       if (len > 0 && filename[len - 1] == '/')
99         {
100           errno = EISDIR;
101           return -1;
102         }
103     }
104 #endif
105
106   fd = orig_open (filename, flags, mode);
107
108 #if REPLACE_FCHDIR
109   /* Implementing fchdir and fdopendir requires the ability to open a
110      directory file descriptor.  If open doesn't support that (as on
111      mingw), we use a dummy file that behaves the same as directories
112      on Linux (ie. always reports EOF on attempts to read()), and
113      override fstat() in fchdir.c to hide the fact that we have a
114      dummy.  */
115   if (REPLACE_OPEN_DIRECTORY && fd < 0 && errno == EACCES
116       && (flags & O_ACCMODE) == O_RDONLY)
117     {
118       struct stat statbuf;
119       if (stat (filename, &statbuf) == 0 && S_ISDIR (statbuf.st_mode))
120         {
121           /* Maximum recursion depth of 1.  */
122           fd = open ("/dev/null", flags, mode);
123           if (0 <= fd)
124             fd = _gl_register_fd (fd, filename);
125         }
126       else
127         errno = EACCES;
128     }
129 #endif
130
131 #if OPEN_TRAILING_SLASH_BUG
132   /* If the filename ends in a slash and fd does not refer to a directory,
133      then fail.
134      Rationale: POSIX <http://www.opengroup.org/susv3/basedefs/xbd_chap04.html>
135      says that
136        "A pathname that contains at least one non-slash character and that
137         ends with one or more trailing slashes shall be resolved as if a
138         single dot character ( '.' ) were appended to the pathname."
139      and
140        "The special filename dot shall refer to the directory specified by
141         its predecessor."
142      If the named file without the slash is not a directory, open() must fail
143      with ENOTDIR.  */
144   if (fd >= 0)
145     {
146       /* We know len is positive, since open did not fail with ENOENT.  */
147       size_t len = strlen (filename);
148       if (filename[len - 1] == '/')
149         {
150           struct stat statbuf;
151
152           if (fstat (fd, &statbuf) >= 0 && !S_ISDIR (statbuf.st_mode))
153             {
154               close (fd);
155               errno = ENOTDIR;
156               return -1;
157             }
158         }
159     }
160 #endif
161
162 #if REPLACE_FCHDIR
163   if (!REPLACE_OPEN_DIRECTORY && 0 <= fd)
164     fd = _gl_register_fd (fd, filename);
165 #endif
166
167   return fd;
168 }