(root)/
findutils-4.9.0/
gl/
lib/
fopen.c
       1  /* Open a stream to a file.
       2     Copyright (C) 2007-2022 Free Software Foundation, Inc.
       3  
       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
       6     published by the Free Software Foundation; either version 2.1 of the
       7     License, or (at your option) any later version.
       8  
       9     This file 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 Lesser General Public License for more details.
      13  
      14     You should have received a copy of the GNU Lesser General Public License
      15     along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
      16  
      17  /* Written by Bruno Haible <bruno@clisp.org>, 2007.  */
      18  
      19  /* If the user's config.h happens to include <stdio.h>, let it include only
      20     the system's <stdio.h> here, so that orig_fopen doesn't recurse to
      21     rpl_fopen.  */
      22  #define _GL_ALREADY_INCLUDING_STDIO_H
      23  #include <config.h>
      24  
      25  /* Get the original definition of fopen.  It might be defined as a macro.  */
      26  #include <stdio.h>
      27  #undef _GL_ALREADY_INCLUDING_STDIO_H
      28  
      29  static FILE *
      30  orig_fopen (const char *filename, const char *mode)
      31  {
      32    return fopen (filename, mode);
      33  }
      34  
      35  /* Specification.  */
      36  /* Write "stdio.h" here, not <stdio.h>, otherwise OSF/1 5.1 DTK cc eliminates
      37     this include because of the preliminary #include <stdio.h> above.  */
      38  #include "stdio.h"
      39  
      40  #include <errno.h>
      41  #include <fcntl.h>
      42  #include <stdbool.h>
      43  #include <string.h>
      44  #include <unistd.h>
      45  #include <sys/types.h>
      46  #include <sys/stat.h>
      47  
      48  FILE *
      49  rpl_fopen (const char *filename, const char *mode)
      50  {
      51    int open_direction;
      52    int open_flags;
      53  #if GNULIB_FOPEN_GNU
      54    bool open_flags_gnu;
      55  # define BUF_SIZE 80
      56    char fdopen_mode_buf[BUF_SIZE + 1];
      57  #endif
      58  
      59  #if defined _WIN32 && ! defined __CYGWIN__
      60    if (strcmp (filename, "/dev/null") == 0)
      61      filename = "NUL";
      62  #endif
      63  
      64    /* Parse the mode.  */
      65    open_direction = 0;
      66    open_flags = 0;
      67  #if GNULIB_FOPEN_GNU
      68    open_flags_gnu = false;
      69  #endif
      70    {
      71      const char *p = mode;
      72  #if GNULIB_FOPEN_GNU
      73      char *q = fdopen_mode_buf;
      74  #endif
      75  
      76      for (; *p != '\0'; p++)
      77        {
      78          switch (*p)
      79            {
      80            case 'r':
      81              open_direction = O_RDONLY;
      82  #if GNULIB_FOPEN_GNU
      83              if (q < fdopen_mode_buf + BUF_SIZE)
      84                *q++ = *p;
      85  #endif
      86              continue;
      87            case 'w':
      88              open_direction = O_WRONLY;
      89              open_flags |= O_CREAT | O_TRUNC;
      90  #if GNULIB_FOPEN_GNU
      91              if (q < fdopen_mode_buf + BUF_SIZE)
      92                *q++ = *p;
      93  #endif
      94              continue;
      95            case 'a':
      96              open_direction = O_WRONLY;
      97              open_flags |= O_CREAT | O_APPEND;
      98  #if GNULIB_FOPEN_GNU
      99              if (q < fdopen_mode_buf + BUF_SIZE)
     100                *q++ = *p;
     101  #endif
     102              continue;
     103            case 'b':
     104              /* While it is non-standard, O_BINARY is guaranteed by
     105                 gnulib <fcntl.h>.  We can also assume that orig_fopen
     106                 supports the 'b' flag.  */
     107              open_flags |= O_BINARY;
     108  #if GNULIB_FOPEN_GNU
     109              if (q < fdopen_mode_buf + BUF_SIZE)
     110                *q++ = *p;
     111  #endif
     112              continue;
     113            case '+':
     114              open_direction = O_RDWR;
     115  #if GNULIB_FOPEN_GNU
     116              if (q < fdopen_mode_buf + BUF_SIZE)
     117                *q++ = *p;
     118  #endif
     119              continue;
     120  #if GNULIB_FOPEN_GNU
     121            case 'x':
     122              open_flags |= O_EXCL;
     123              open_flags_gnu = true;
     124              continue;
     125            case 'e':
     126              open_flags |= O_CLOEXEC;
     127              open_flags_gnu = true;
     128              continue;
     129  #endif
     130            default:
     131              break;
     132            }
     133  #if GNULIB_FOPEN_GNU
     134          /* The rest of the mode string can be a platform-dependent extension.
     135             Copy it unmodified.  */
     136          {
     137            size_t len = strlen (p);
     138            if (len > fdopen_mode_buf + BUF_SIZE - q)
     139              len = fdopen_mode_buf + BUF_SIZE - q;
     140            memcpy (q, p, len);
     141            q += len;
     142          }
     143  #endif
     144          break;
     145        }
     146  #if GNULIB_FOPEN_GNU
     147      *q = '\0';
     148  #endif
     149    }
     150  
     151  #if FOPEN_TRAILING_SLASH_BUG
     152    /* Fail if the mode requires write access and the filename ends in a slash,
     153       as POSIX says such a filename must name a directory
     154       <https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_13>:
     155         "A pathname that contains at least one non-<slash> character and that
     156          ends with one or more trailing <slash> characters shall not be resolved
     157          successfully unless the last pathname component before the trailing
     158          <slash> characters names an existing directory"
     159       If the named file already exists as a directory, then if a mode that
     160       requires write access is specified, fopen() must fail because POSIX
     161       <https://pubs.opengroup.org/onlinepubs/9699919799/functions/fopen.html>
     162       says that it fails with errno = EISDIR in this case.
     163       If the named file does not exist or does not name a directory, then
     164       fopen() must fail since the file does not contain a '.' directory.  */
     165    {
     166      size_t len = strlen (filename);
     167      if (len > 0 && filename[len - 1] == '/')
     168        {
     169          int fd;
     170          struct stat statbuf;
     171          FILE *fp;
     172  
     173          if (open_direction != O_RDONLY)
     174            {
     175              errno = EISDIR;
     176              return NULL;
     177            }
     178  
     179          fd = open (filename, open_direction | open_flags,
     180                     S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
     181          if (fd < 0)
     182            return NULL;
     183  
     184          if (fstat (fd, &statbuf) >= 0 && !S_ISDIR (statbuf.st_mode))
     185            {
     186              close (fd);
     187              errno = ENOTDIR;
     188              return NULL;
     189            }
     190  
     191  # if GNULIB_FOPEN_GNU
     192          fp = fdopen (fd, fdopen_mode_buf);
     193  # else
     194          fp = fdopen (fd, mode);
     195  # endif
     196          if (fp == NULL)
     197            {
     198              int saved_errno = errno;
     199              close (fd);
     200              errno = saved_errno;
     201            }
     202          return fp;
     203        }
     204    }
     205  #endif
     206  
     207  #if GNULIB_FOPEN_GNU
     208    if (open_flags_gnu)
     209      {
     210        int fd;
     211        FILE *fp;
     212  
     213        fd = open (filename, open_direction | open_flags,
     214                   S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
     215        if (fd < 0)
     216          return NULL;
     217  
     218        fp = fdopen (fd, fdopen_mode_buf);
     219        if (fp == NULL)
     220          {
     221            int saved_errno = errno;
     222            close (fd);
     223            errno = saved_errno;
     224          }
     225        return fp;
     226      }
     227  #endif
     228  
     229    return orig_fopen (filename, mode);
     230  }