Back to index

nagios-plugins  1.4.16
open.c
Go to the documentation of this file.
00001 /* Open a descriptor to a file.
00002    Copyright (C) 2007-2010 Free Software Foundation, Inc.
00003 
00004    This program is free software: you can redistribute it and/or modify
00005    it under the terms of the GNU General Public License as published by
00006    the Free Software Foundation; either version 3 of the License, or
00007    (at your option) any later version.
00008 
00009    This program is distributed in the hope that it will be useful,
00010    but WITHOUT ANY WARRANTY; without even the implied warranty of
00011    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012    GNU General Public License for more details.
00013 
00014    You should have received a copy of the GNU General Public License
00015    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
00016 
00017 /* Written by Bruno Haible <bruno@clisp.org>, 2007.  */
00018 
00019 #include <config.h>
00020 
00021 /* Get the original definition of open.  It might be defined as a macro.  */
00022 #define __need_system_fcntl_h
00023 #include <fcntl.h>
00024 #undef __need_system_fcntl_h
00025 #include <sys/types.h>
00026 
00027 static inline int
00028 orig_open (const char *filename, int flags, mode_t mode)
00029 {
00030   return open (filename, flags, mode);
00031 }
00032 
00033 /* Specification.  */
00034 #include <fcntl.h>
00035 
00036 #include <errno.h>
00037 #include <stdarg.h>
00038 #include <string.h>
00039 #include <sys/types.h>
00040 #include <sys/stat.h>
00041 #include <unistd.h>
00042 
00043 #ifndef REPLACE_OPEN_DIRECTORY
00044 # define REPLACE_OPEN_DIRECTORY 0
00045 #endif
00046 
00047 int
00048 open (const char *filename, int flags, ...)
00049 {
00050   mode_t mode;
00051   int fd;
00052 
00053   mode = 0;
00054   if (flags & O_CREAT)
00055     {
00056       va_list arg;
00057       va_start (arg, flags);
00058 
00059       /* We have to use PROMOTED_MODE_T instead of mode_t, otherwise GCC 4
00060          creates crashing code when 'mode_t' is smaller than 'int'.  */
00061       mode = va_arg (arg, PROMOTED_MODE_T);
00062 
00063       va_end (arg);
00064     }
00065 
00066 #if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
00067   if (strcmp (filename, "/dev/null") == 0)
00068     filename = "NUL";
00069 #endif
00070 
00071 #if OPEN_TRAILING_SLASH_BUG
00072   /* If the filename ends in a slash and one of O_CREAT, O_WRONLY, O_RDWR
00073      is specified, then fail.
00074      Rationale: POSIX <http://www.opengroup.org/susv3/basedefs/xbd_chap04.html>
00075      says that
00076        "A pathname that contains at least one non-slash character and that
00077         ends with one or more trailing slashes shall be resolved as if a
00078         single dot character ( '.' ) were appended to the pathname."
00079      and
00080        "The special filename dot shall refer to the directory specified by
00081         its predecessor."
00082      If the named file already exists as a directory, then
00083        - if O_CREAT is specified, open() must fail because of the semantics
00084          of O_CREAT,
00085        - if O_WRONLY or O_RDWR is specified, open() must fail because POSIX
00086          <http://www.opengroup.org/susv3/functions/open.html> says that it
00087          fails with errno = EISDIR in this case.
00088      If the named file does not exist or does not name a directory, then
00089        - if O_CREAT is specified, open() must fail since open() cannot create
00090          directories,
00091        - if O_WRONLY or O_RDWR is specified, open() must fail because the
00092          file does not contain a '.' directory.  */
00093   if (flags & (O_CREAT | O_WRONLY | O_RDWR))
00094     {
00095       size_t len = strlen (filename);
00096       if (len > 0 && filename[len - 1] == '/')
00097         {
00098           errno = EISDIR;
00099           return -1;
00100         }
00101     }
00102 #endif
00103 
00104   fd = orig_open (filename, flags, mode);
00105 
00106 #if REPLACE_FCHDIR
00107   /* Implementing fchdir and fdopendir requires the ability to open a
00108      directory file descriptor.  If open doesn't support that (as on
00109      mingw), we use a dummy file that behaves the same as directories
00110      on Linux (ie. always reports EOF on attempts to read()), and
00111      override fstat() in fchdir.c to hide the fact that we have a
00112      dummy.  */
00113   if (REPLACE_OPEN_DIRECTORY && fd < 0 && errno == EACCES
00114       && (flags & O_ACCMODE) == O_RDONLY)
00115     {
00116       struct stat statbuf;
00117       if (stat (filename, &statbuf) == 0 && S_ISDIR (statbuf.st_mode))
00118         {
00119           /* Maximum recursion depth of 1.  */
00120           fd = open ("/dev/null", flags, mode);
00121           if (0 <= fd)
00122             fd = _gl_register_fd (fd, filename);
00123         }
00124       else
00125         errno = EACCES;
00126     }
00127 #endif
00128 
00129 #if OPEN_TRAILING_SLASH_BUG
00130   /* If the filename ends in a slash and fd does not refer to a directory,
00131      then fail.
00132      Rationale: POSIX <http://www.opengroup.org/susv3/basedefs/xbd_chap04.html>
00133      says that
00134        "A pathname that contains at least one non-slash character and that
00135         ends with one or more trailing slashes shall be resolved as if a
00136         single dot character ( '.' ) were appended to the pathname."
00137      and
00138        "The special filename dot shall refer to the directory specified by
00139         its predecessor."
00140      If the named file without the slash is not a directory, open() must fail
00141      with ENOTDIR.  */
00142   if (fd >= 0)
00143     {
00144       /* We know len is positive, since open did not fail with ENOENT.  */
00145       size_t len = strlen (filename);
00146       if (filename[len - 1] == '/')
00147         {
00148           struct stat statbuf;
00149 
00150           if (fstat (fd, &statbuf) >= 0 && !S_ISDIR (statbuf.st_mode))
00151             {
00152               close (fd);
00153               errno = ENOTDIR;
00154               return -1;
00155             }
00156         }
00157     }
00158 #endif
00159 
00160 #if REPLACE_FCHDIR
00161   if (!REPLACE_OPEN_DIRECTORY && 0 <= fd)
00162     fd = _gl_register_fd (fd, filename);
00163 #endif
00164 
00165   return fd;
00166 }