diff options
Diffstat (limited to 'gl/open.c')
| -rw-r--r-- | gl/open.c | 127 |
1 files changed, 82 insertions, 45 deletions
| @@ -1,5 +1,5 @@ | |||
| 1 | /* Open a descriptor to a file. | 1 | /* Open a descriptor to a file. |
| 2 | Copyright (C) 2007-2023 Free Software Foundation, Inc. | 2 | Copyright (C) 2007-2025 Free Software Foundation, Inc. |
| 3 | 3 | ||
| 4 | This file is free software: you can redistribute it and/or modify | 4 | This file is free software: you can redistribute it and/or modify |
| 5 | it under the terms of the GNU Lesser General Public License as | 5 | it under the terms of the GNU Lesser General Public License as |
| @@ -38,9 +38,13 @@ orig_open (const char *filename, int flags, mode_t mode) | |||
| 38 | } | 38 | } |
| 39 | 39 | ||
| 40 | /* Specification. */ | 40 | /* Specification. */ |
| 41 | #ifdef __osf__ | ||
| 41 | /* Write "fcntl.h" here, not <fcntl.h>, otherwise OSF/1 5.1 DTK cc eliminates | 42 | /* Write "fcntl.h" here, not <fcntl.h>, otherwise OSF/1 5.1 DTK cc eliminates |
| 42 | this include because of the preliminary #include <fcntl.h> above. */ | 43 | this include because of the preliminary #include <fcntl.h> above. */ |
| 43 | #include "fcntl.h" | 44 | # include "fcntl.h" |
| 45 | #else | ||
| 46 | # include <fcntl.h> | ||
| 47 | #endif | ||
| 44 | 48 | ||
| 45 | #include "cloexec.h" | 49 | #include "cloexec.h" |
| 46 | 50 | ||
| @@ -51,24 +55,29 @@ orig_open (const char *filename, int flags, mode_t mode) | |||
| 51 | #include <sys/stat.h> | 55 | #include <sys/stat.h> |
| 52 | #include <unistd.h> | 56 | #include <unistd.h> |
| 53 | 57 | ||
| 58 | #ifndef HAVE_WORKING_O_DIRECTORY | ||
| 59 | # define HAVE_WORKING_O_DIRECTORY false | ||
| 60 | #endif | ||
| 61 | |||
| 62 | #ifndef OPEN_TRAILING_SLASH_BUG | ||
| 63 | # define OPEN_TRAILING_SLASH_BUG false | ||
| 64 | #endif | ||
| 65 | |||
| 54 | #ifndef REPLACE_OPEN_DIRECTORY | 66 | #ifndef REPLACE_OPEN_DIRECTORY |
| 55 | # define REPLACE_OPEN_DIRECTORY 0 | 67 | # define REPLACE_OPEN_DIRECTORY false |
| 56 | #endif | 68 | #endif |
| 57 | 69 | ||
| 70 | static int | ||
| 71 | lstatif (char const *filename, struct stat *st, int flags) | ||
| 72 | { | ||
| 73 | return flags & O_NOFOLLOW ? lstat (filename, st) : stat (filename, st); | ||
| 74 | } | ||
| 75 | |||
| 58 | int | 76 | int |
| 59 | open (const char *filename, int flags, ...) | 77 | open (const char *filename, int flags, ...) |
| 60 | { | 78 | { |
| 61 | /* 0 = unknown, 1 = yes, -1 = no. */ | 79 | mode_t mode = 0; |
| 62 | #if GNULIB_defined_O_CLOEXEC | ||
| 63 | int have_cloexec = -1; | ||
| 64 | #else | ||
| 65 | static int have_cloexec; | ||
| 66 | #endif | ||
| 67 | |||
| 68 | mode_t mode; | ||
| 69 | int fd; | ||
| 70 | 80 | ||
| 71 | mode = 0; | ||
| 72 | if (flags & O_CREAT) | 81 | if (flags & O_CREAT) |
| 73 | { | 82 | { |
| 74 | va_list arg; | 83 | va_list arg; |
| @@ -95,7 +104,6 @@ open (const char *filename, int flags, ...) | |||
| 95 | filename = "NUL"; | 104 | filename = "NUL"; |
| 96 | #endif | 105 | #endif |
| 97 | 106 | ||
| 98 | #if OPEN_TRAILING_SLASH_BUG | ||
| 99 | /* Fail if one of O_CREAT, O_WRONLY, O_RDWR is specified and the filename | 107 | /* Fail if one of O_CREAT, O_WRONLY, O_RDWR is specified and the filename |
| 100 | ends in a slash, as POSIX says such a filename must name a directory | 108 | ends in a slash, as POSIX says such a filename must name a directory |
| 101 | <https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_13>: | 109 | <https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_13>: |
| @@ -114,21 +122,55 @@ open (const char *filename, int flags, ...) | |||
| 114 | directories, | 122 | directories, |
| 115 | - if O_WRONLY or O_RDWR is specified, open() must fail because the | 123 | - if O_WRONLY or O_RDWR is specified, open() must fail because the |
| 116 | file does not contain a '.' directory. */ | 124 | file does not contain a '.' directory. */ |
| 117 | if ((flags & O_CREAT) | 125 | bool check_for_slash_bug; |
| 118 | || (flags & O_ACCMODE) == O_RDWR | 126 | if (OPEN_TRAILING_SLASH_BUG) |
| 119 | || (flags & O_ACCMODE) == O_WRONLY) | ||
| 120 | { | 127 | { |
| 121 | size_t len = strlen (filename); | 128 | size_t len = strlen (filename); |
| 122 | if (len > 0 && filename[len - 1] == '/') | 129 | check_for_slash_bug = len && filename[len - 1] == '/'; |
| 130 | } | ||
| 131 | else | ||
| 132 | check_for_slash_bug = false; | ||
| 133 | |||
| 134 | if (check_for_slash_bug | ||
| 135 | && (flags & O_CREAT | ||
| 136 | || (flags & O_ACCMODE) == O_RDWR | ||
| 137 | || (flags & O_ACCMODE) == O_WRONLY)) | ||
| 138 | { | ||
| 139 | errno = EISDIR; | ||
| 140 | return -1; | ||
| 141 | } | ||
| 142 | |||
| 143 | /* With the trailing slash bug or without working O_DIRECTORY, check with | ||
| 144 | stat first lest we hang trying to open a fifo. Although there is | ||
| 145 | a race between this and opening the file, we can do no better. | ||
| 146 | After opening the file we will check again with fstat. */ | ||
| 147 | bool check_directory = | ||
| 148 | (check_for_slash_bug | ||
| 149 | || (!HAVE_WORKING_O_DIRECTORY && flags & O_DIRECTORY)); | ||
| 150 | if (check_directory) | ||
| 151 | { | ||
| 152 | struct stat statbuf; | ||
| 153 | if (lstatif (filename, &statbuf, flags) < 0) | ||
| 154 | { | ||
| 155 | if (! (flags & O_CREAT && errno == ENOENT)) | ||
| 156 | return -1; | ||
| 157 | } | ||
| 158 | else if (!S_ISDIR (statbuf.st_mode)) | ||
| 123 | { | 159 | { |
| 124 | errno = EISDIR; | 160 | errno = ENOTDIR; |
| 125 | return -1; | 161 | return -1; |
| 126 | } | 162 | } |
| 127 | } | 163 | } |
| 164 | |||
| 165 | /* 0 = unknown, 1 = yes, -1 = no. */ | ||
| 166 | #if GNULIB_defined_O_CLOEXEC | ||
| 167 | int have_cloexec = -1; | ||
| 168 | #else | ||
| 169 | static int have_cloexec; | ||
| 128 | #endif | 170 | #endif |
| 129 | 171 | ||
| 130 | fd = orig_open (filename, | 172 | int fd = orig_open (filename, |
| 131 | flags & ~(have_cloexec < 0 ? O_CLOEXEC : 0), mode); | 173 | flags & ~(have_cloexec < 0 ? O_CLOEXEC : 0), mode); |
| 132 | 174 | ||
| 133 | if (flags & O_CLOEXEC) | 175 | if (flags & O_CLOEXEC) |
| 134 | { | 176 | { |
| @@ -150,19 +192,21 @@ open (const char *filename, int flags, ...) | |||
| 150 | #if REPLACE_FCHDIR | 192 | #if REPLACE_FCHDIR |
| 151 | /* Implementing fchdir and fdopendir requires the ability to open a | 193 | /* Implementing fchdir and fdopendir requires the ability to open a |
| 152 | directory file descriptor. If open doesn't support that (as on | 194 | directory file descriptor. If open doesn't support that (as on |
| 153 | mingw), we use a dummy file that behaves the same as directories | 195 | mingw), use a dummy file that behaves the same as directories |
| 154 | on Linux (ie. always reports EOF on attempts to read()), and | 196 | on Linux (ie. always reports EOF on attempts to read()), and |
| 155 | override fstat() in fchdir.c to hide the fact that we have a | 197 | override fstat in fchdir.c to hide the dummy. */ |
| 156 | dummy. */ | ||
| 157 | if (REPLACE_OPEN_DIRECTORY && fd < 0 && errno == EACCES | 198 | if (REPLACE_OPEN_DIRECTORY && fd < 0 && errno == EACCES |
| 158 | && ((flags & O_ACCMODE) == O_RDONLY | 199 | && ((flags & (O_ACCMODE | O_CREAT)) == O_RDONLY |
| 159 | || (O_SEARCH != O_RDONLY && (flags & O_ACCMODE) == O_SEARCH))) | 200 | || (O_SEARCH != O_RDONLY |
| 201 | && (flags & (O_ACCMODE | O_CREAT)) == O_SEARCH))) | ||
| 160 | { | 202 | { |
| 161 | struct stat statbuf; | 203 | struct stat statbuf; |
| 162 | if (stat (filename, &statbuf) == 0 && S_ISDIR (statbuf.st_mode)) | 204 | if (check_directory |
| 205 | || (lstatif (filename, &statbuf, flags) == 0 | ||
| 206 | && S_ISDIR (statbuf.st_mode))) | ||
| 163 | { | 207 | { |
| 164 | /* Maximum recursion depth of 1. */ | 208 | /* Maximum recursion depth of 1. */ |
| 165 | fd = open ("/dev/null", flags, mode); | 209 | fd = open ("/dev/null", flags & ~O_DIRECTORY, mode); |
| 166 | if (0 <= fd) | 210 | if (0 <= fd) |
| 167 | fd = _gl_register_fd (fd, filename); | 211 | fd = _gl_register_fd (fd, filename); |
| 168 | } | 212 | } |
| @@ -171,10 +215,8 @@ open (const char *filename, int flags, ...) | |||
| 171 | } | 215 | } |
| 172 | #endif | 216 | #endif |
| 173 | 217 | ||
| 174 | #if OPEN_TRAILING_SLASH_BUG | 218 | /* If checking for directories, fail if fd does not refer to a directory. |
| 175 | /* If the filename ends in a slash and fd does not refer to a directory, | 219 | Rationale: A filename ending in slash cannot name a non-directory |
| 176 | then fail. | ||
| 177 | Rationale: POSIX says such a filename must name a directory | ||
| 178 | <https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_13>: | 220 | <https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_13>: |
| 179 | "A pathname that contains at least one non-<slash> character and that | 221 | "A pathname that contains at least one non-<slash> character and that |
| 180 | ends with one or more trailing <slash> characters shall not be resolved | 222 | ends with one or more trailing <slash> characters shall not be resolved |
| @@ -182,23 +224,18 @@ open (const char *filename, int flags, ...) | |||
| 182 | <slash> characters names an existing directory" | 224 | <slash> characters names an existing directory" |
| 183 | If the named file without the slash is not a directory, open() must fail | 225 | If the named file without the slash is not a directory, open() must fail |
| 184 | with ENOTDIR. */ | 226 | with ENOTDIR. */ |
| 185 | if (fd >= 0) | 227 | if (check_directory && 0 <= fd) |
| 186 | { | 228 | { |
| 187 | /* We know len is positive, since open did not fail with ENOENT. */ | 229 | struct stat statbuf; |
| 188 | size_t len = strlen (filename); | 230 | int r = fstat (fd, &statbuf); |
| 189 | if (filename[len - 1] == '/') | 231 | if (r < 0 || !S_ISDIR (statbuf.st_mode)) |
| 190 | { | 232 | { |
| 191 | struct stat statbuf; | 233 | int err = r < 0 ? errno : ENOTDIR; |
| 192 | 234 | close (fd); | |
| 193 | if (fstat (fd, &statbuf) >= 0 && !S_ISDIR (statbuf.st_mode)) | 235 | errno = err; |
| 194 | { | 236 | return -1; |
| 195 | close (fd); | ||
| 196 | errno = ENOTDIR; | ||
| 197 | return -1; | ||
| 198 | } | ||
| 199 | } | 237 | } |
| 200 | } | 238 | } |
| 201 | #endif | ||
| 202 | 239 | ||
| 203 | #if REPLACE_FCHDIR | 240 | #if REPLACE_FCHDIR |
| 204 | if (!REPLACE_OPEN_DIRECTORY && 0 <= fd) | 241 | if (!REPLACE_OPEN_DIRECTORY && 0 <= fd) |
