root/maint/gnulib/lib/openat.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. orig_openat
  2. rpl_openat
  3. openat
  4. openat_permissive
  5. openat_needs_fchdir

   1 /* provide a replacement openat function
   2    Copyright (C) 2004-2021 Free Software Foundation, Inc.
   3 
   4    This program is free software: you can redistribute it and/or modify
   5    it under the terms of the GNU General Public License as published by
   6    the Free Software Foundation; either version 3 of the License, or
   7    (at your option) any later version.
   8 
   9    This program is distributed in the hope that it will be useful,
  10    but WITHOUT ANY WARRANTY; without even the implied warranty of
  11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12    GNU General Public License for more details.
  13 
  14    You should have received a copy of the GNU General Public License
  15    along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
  16 
  17 /* written by Jim Meyering */
  18 
  19 /* If the user's config.h happens to include <fcntl.h>, let it include only
  20    the system's <fcntl.h> here, so that orig_openat doesn't recurse to
  21    rpl_openat.  */
  22 #define __need_system_fcntl_h
  23 #include <config.h>
  24 
  25 /* Get the original definition of open.  It might be defined as a macro.  */
  26 #include <fcntl.h>
  27 #include <sys/types.h>
  28 #undef __need_system_fcntl_h
  29 
  30 #if HAVE_OPENAT
  31 static int
  32 orig_openat (int fd, char const *filename, int flags, mode_t mode)
     /* [previous][next][first][last][top][bottom][index][help] */
  33 {
  34   return openat (fd, filename, flags, mode);
  35 }
  36 #endif
  37 
  38 /* Write "fcntl.h" here, not <fcntl.h>, otherwise OSF/1 5.1 DTK cc eliminates
  39    this include because of the preliminary #include <fcntl.h> above.  */
  40 #include "fcntl.h"
  41 
  42 #include "openat.h"
  43 
  44 #include "cloexec.h"
  45 
  46 #include <stdarg.h>
  47 #include <stdbool.h>
  48 #include <stddef.h>
  49 #include <stdlib.h>
  50 #include <string.h>
  51 #include <sys/stat.h>
  52 #include <errno.h>
  53 
  54 #if HAVE_OPENAT
  55 
  56 /* Like openat, but support O_CLOEXEC and work around Solaris 9 bugs
  57    with trailing slash.  */
  58 int
  59 rpl_openat (int dfd, char const *filename, int flags, ...)
     /* [previous][next][first][last][top][bottom][index][help] */
  60 {
  61   /* 0 = unknown, 1 = yes, -1 = no.  */
  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 
  71   mode = 0;
  72   if (flags & O_CREAT)
  73     {
  74       va_list arg;
  75       va_start (arg, flags);
  76 
  77       /* We have to use PROMOTED_MODE_T instead of mode_t, otherwise GCC 4
  78          creates crashing code when 'mode_t' is smaller than 'int'.  */
  79       mode = va_arg (arg, PROMOTED_MODE_T);
  80 
  81       va_end (arg);
  82     }
  83 
  84 # if OPEN_TRAILING_SLASH_BUG
  85   /* Fail if one of O_CREAT, O_WRONLY, O_RDWR is specified and the filename
  86      ends in a slash, as POSIX says such a filename must name a directory
  87      <https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_13>:
  88        "A pathname that contains at least one non-<slash> character and that
  89         ends with one or more trailing <slash> characters shall not be resolved
  90         successfully unless the last pathname component before the trailing
  91         <slash> characters names an existing directory"
  92      If the named file already exists as a directory, then
  93        - if O_CREAT is specified, open() must fail because of the semantics
  94          of O_CREAT,
  95        - if O_WRONLY or O_RDWR is specified, open() must fail because POSIX
  96          <https://pubs.opengroup.org/onlinepubs/9699919799/functions/openat.html>
  97          says that it fails with errno = EISDIR in this case.
  98      If the named file does not exist or does not name a directory, then
  99        - if O_CREAT is specified, open() must fail since open() cannot create
 100          directories,
 101        - if O_WRONLY or O_RDWR is specified, open() must fail because the
 102          file does not contain a '.' directory.  */
 103   if ((flags & O_CREAT)
 104       || (flags & O_ACCMODE) == O_RDWR
 105       || (flags & O_ACCMODE) == O_WRONLY)
 106     {
 107       size_t len = strlen (filename);
 108       if (len > 0 && filename[len - 1] == '/')
 109         {
 110           errno = EISDIR;
 111           return -1;
 112         }
 113     }
 114 # endif
 115 
 116   fd = orig_openat (dfd, filename,
 117                     flags & ~(have_cloexec < 0 ? O_CLOEXEC : 0), mode);
 118 
 119   if (flags & O_CLOEXEC)
 120     {
 121       if (! have_cloexec)
 122         {
 123           if (0 <= fd)
 124             have_cloexec = 1;
 125           else if (errno == EINVAL)
 126             {
 127               fd = orig_openat (dfd, filename, flags & ~O_CLOEXEC, mode);
 128               have_cloexec = -1;
 129             }
 130         }
 131       if (have_cloexec < 0 && 0 <= fd)
 132         set_cloexec_flag (fd, true);
 133     }
 134 
 135 
 136 # if OPEN_TRAILING_SLASH_BUG
 137   /* If the filename ends in a slash and fd does not refer to a directory,
 138      then fail.
 139      Rationale: POSIX says such a filename must name a directory
 140      <https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_13>:
 141        "A pathname that contains at least one non-<slash> character and that
 142         ends with one or more trailing <slash> characters shall not be resolved
 143         successfully unless the last pathname component before the trailing
 144         <slash> characters names an existing directory"
 145      If the named file without the slash is not a directory, open() must fail
 146      with ENOTDIR.  */
 147   if (fd >= 0)
 148     {
 149       /* We know len is positive, since open did not fail with ENOENT.  */
 150       size_t len = strlen (filename);
 151       if (filename[len - 1] == '/')
 152         {
 153           struct stat statbuf;
 154 
 155           if (fstat (fd, &statbuf) >= 0 && !S_ISDIR (statbuf.st_mode))
 156             {
 157               close (fd);
 158               errno = ENOTDIR;
 159               return -1;
 160             }
 161         }
 162     }
 163 # endif
 164 
 165   return fd;
 166 }
 167 
 168 #else /* !HAVE_OPENAT */
 169 
 170 # include "filename.h" /* solely for definition of IS_ABSOLUTE_FILE_NAME */
 171 # include "openat-priv.h"
 172 # include "save-cwd.h"
 173 
 174 /* Replacement for Solaris' openat function.
 175    <https://www.google.com/search?q=openat+site:docs.oracle.com>
 176    First, try to simulate it via open ("/proc/self/fd/FD/FILE").
 177    Failing that, simulate it by doing save_cwd/fchdir/open/restore_cwd.
 178    If either the save_cwd or the restore_cwd fails (relatively unlikely),
 179    then give a diagnostic and exit nonzero.
 180    Otherwise, upon failure, set errno and return -1, as openat does.
 181    Upon successful completion, return a file descriptor.  */
 182 int
 183 openat (int fd, char const *file, int flags, ...)
     /* [previous][next][first][last][top][bottom][index][help] */
 184 {
 185   mode_t mode = 0;
 186 
 187   if (flags & O_CREAT)
 188     {
 189       va_list arg;
 190       va_start (arg, flags);
 191 
 192       /* We have to use PROMOTED_MODE_T instead of mode_t, otherwise GCC 4
 193          creates crashing code when 'mode_t' is smaller than 'int'.  */
 194       mode = va_arg (arg, PROMOTED_MODE_T);
 195 
 196       va_end (arg);
 197     }
 198 
 199   return openat_permissive (fd, file, flags, mode, NULL);
 200 }
 201 
 202 /* Like openat (FD, FILE, FLAGS, MODE), but if CWD_ERRNO is
 203    nonnull, set *CWD_ERRNO to an errno value if unable to save
 204    or restore the initial working directory.  This is needed only
 205    the first time remove.c's remove_dir opens a command-line
 206    directory argument.
 207 
 208    If a previous attempt to restore the current working directory
 209    failed, then we must not even try to access a '.'-relative name.
 210    It is the caller's responsibility not to call this function
 211    in that case.  */
 212 
 213 int
 214 openat_permissive (int fd, char const *file, int flags, mode_t mode,
     /* [previous][next][first][last][top][bottom][index][help] */
 215                    int *cwd_errno)
 216 {
 217   struct saved_cwd saved_cwd;
 218   int saved_errno;
 219   int err;
 220   bool save_ok;
 221 
 222   if (fd == AT_FDCWD || IS_ABSOLUTE_FILE_NAME (file))
 223     return open (file, flags, mode);
 224 
 225   {
 226     char buf[OPENAT_BUFFER_SIZE];
 227     char *proc_file = openat_proc_name (buf, fd, file);
 228     if (proc_file)
 229       {
 230         int open_result = open (proc_file, flags, mode);
 231         int open_errno = errno;
 232         if (proc_file != buf)
 233           free (proc_file);
 234         /* If the syscall succeeds, or if it fails with an unexpected
 235            errno value, then return right away.  Otherwise, fall through
 236            and resort to using save_cwd/restore_cwd.  */
 237         if (0 <= open_result || ! EXPECTED_ERRNO (open_errno))
 238           {
 239             errno = open_errno;
 240             return open_result;
 241           }
 242       }
 243   }
 244 
 245   save_ok = (save_cwd (&saved_cwd) == 0);
 246   if (! save_ok)
 247     {
 248       if (! cwd_errno)
 249         openat_save_fail (errno);
 250       *cwd_errno = errno;
 251     }
 252   if (0 <= fd && fd == saved_cwd.desc)
 253     {
 254       /* If saving the working directory collides with the user's
 255          requested fd, then the user's fd must have been closed to
 256          begin with.  */
 257       free_cwd (&saved_cwd);
 258       errno = EBADF;
 259       return -1;
 260     }
 261 
 262   err = fchdir (fd);
 263   saved_errno = errno;
 264 
 265   if (! err)
 266     {
 267       err = open (file, flags, mode);
 268       saved_errno = errno;
 269       if (save_ok && restore_cwd (&saved_cwd) != 0)
 270         {
 271           if (! cwd_errno)
 272             {
 273               /* Don't write a message to just-created fd 2.  */
 274               saved_errno = errno;
 275               if (err == STDERR_FILENO)
 276                 close (err);
 277               openat_restore_fail (saved_errno);
 278             }
 279           *cwd_errno = errno;
 280         }
 281     }
 282 
 283   free_cwd (&saved_cwd);
 284   errno = saved_errno;
 285   return err;
 286 }
 287 
 288 /* Return true if our openat implementation must resort to
 289    using save_cwd and restore_cwd.  */
 290 bool
 291 openat_needs_fchdir (void)
     /* [previous][next][first][last][top][bottom][index][help] */
 292 {
 293   bool needs_fchdir = true;
 294   int fd = open ("/", O_SEARCH | O_CLOEXEC);
 295 
 296   if (0 <= fd)
 297     {
 298       char buf[OPENAT_BUFFER_SIZE];
 299       char *proc_file = openat_proc_name (buf, fd, ".");
 300       if (proc_file)
 301         {
 302           needs_fchdir = false;
 303           if (proc_file != buf)
 304             free (proc_file);
 305         }
 306       close (fd);
 307     }
 308 
 309   return needs_fchdir;
 310 }
 311 
 312 #endif /* !HAVE_OPENAT */

/* [previous][next][first][last][top][bottom][index][help] */