LCOV - code coverage report
Current view: top level - lib - mkdir-p.c (source / functions) Hit Total Coverage
Test: coreutils.info Lines: 54 59 91.5 %
Date: 2018-01-30 Functions: 1 1 100.0 %

          Line data    Source code
       1             : /* mkdir-p.c -- Ensure that a directory and its parents exist.
       2             : 
       3             :    Copyright (C) 1990, 1997, 1998, 1999, 2000, 2002, 2003, 2004, 2005,
       4             :    2006, 2007 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 Paul Eggert, David MacKenzie, and Jim Meyering.  */
      20             : 
      21             : #include <config.h>
      22             : 
      23             : #include "mkdir-p.h"
      24             : 
      25             : #include <errno.h>
      26             : #include <sys/stat.h>
      27             : #include <unistd.h>
      28             : 
      29             : #include "gettext.h"
      30             : #define _(msgid) gettext (msgid)
      31             : 
      32             : #include "dirchownmod.h"
      33             : #include "dirname.h"
      34             : #include "error.h"
      35             : #include "quote.h"
      36             : #include "mkancesdirs.h"
      37             : #include "savewd.h"
      38             : 
      39             : #ifndef HAVE_FCHMOD
      40             : # define HAVE_FCHMOD false
      41             : #endif
      42             : 
      43             : /* Ensure that the directory DIR exists.
      44             : 
      45             :    WD is the working directory, as in savewd.c.
      46             : 
      47             :    If MAKE_ANCESTOR is not null, create any ancestor directories that
      48             :    don't already exist, by invoking MAKE_ANCESTOR (DIR, ANCESTOR, OPTIONS).
      49             :    This function should return zero if successful, -1 (setting errno)
      50             :    otherwise.  In this case, DIR may be modified by storing '\0' bytes
      51             :    into it, to access the ancestor directories, and this modification
      52             :    is retained on return if the ancestor directories could not be
      53             :    created.
      54             : 
      55             :    Create DIR as a new directory with using mkdir with permissions
      56             :    MODE.  It is also OK if MAKE_ANCESTOR is not null and a
      57             :    directory DIR already exists.
      58             : 
      59             :    Call ANNOUNCE (DIR, OPTIONS) just after successfully making DIR,
      60             :    even if some of the following actions fail.
      61             : 
      62             :    Set DIR's owner to OWNER and group to GROUP, but leave the owner
      63             :    alone if OWNER is (uid_t) -1, and similarly for GROUP.
      64             : 
      65             :    Set DIR's mode bits to MODE, except preserve any of the bits that
      66             :    correspond to zero bits in MODE_BITS.  In other words, MODE_BITS is
      67             :    a mask that specifies which of DIR's mode bits should be set or
      68             :    cleared.  MODE should be a subset of MODE_BITS, which in turn
      69             :    should be a subset of CHMOD_MODE_BITS.  Changing the mode in this
      70             :    way is necessary if DIR already existed or if MODE and MODE_BITS
      71             :    specify non-permissions bits like S_ISUID.
      72             : 
      73             :    However, if PRESERVE_EXISTING is true and DIR already exists,
      74             :    do not attempt to set DIR's ownership and file mode bits.
      75             : 
      76             :    This implementation assumes the current umask is zero.
      77             : 
      78             :    Return true if DIR exists as a directory with the proper ownership
      79             :    and file mode bits when done, or if a child process has been
      80             :    dispatched to do the real work (though the child process may not
      81             :    have finished yet -- it is the caller's responsibility to handle
      82             :    this).  Report a diagnostic and return false on failure, storing
      83             :    '\0' into *DIR if an ancestor directory had problems.  */
      84             : 
      85             : bool
      86         136 : make_dir_parents (char *dir,
      87             :                   struct savewd *wd,
      88             :                   int (*make_ancestor) (char const *, char const *, void *),
      89             :                   void *options,
      90             :                   mode_t mode,
      91             :                   void (*announce) (char const *, void *),
      92             :                   mode_t mode_bits,
      93             :                   uid_t owner,
      94             :                   gid_t group,
      95             :                   bool preserve_existing)
      96             : {
      97         136 :   int mkdir_errno = (IS_ABSOLUTE_FILE_NAME (dir) ? 0 : savewd_errno (wd));
      98             : 
      99         136 :   if (mkdir_errno == 0)
     100             :     {
     101         136 :       ptrdiff_t prefix_len = 0;
     102         136 :       int savewd_chdir_options = (HAVE_FCHMOD ? SAVEWD_CHDIR_SKIP_READABLE : 0);
     103             : 
     104         136 :       if (make_ancestor)
     105             :         {
     106          74 :           prefix_len = mkancesdirs (dir, wd, make_ancestor, options);
     107          74 :           if (prefix_len < 0)
     108             :             {
     109          13 :               if (prefix_len < -1)
     110           0 :                 return true;
     111          13 :               mkdir_errno = errno;
     112             :             }
     113             :         }
     114             : 
     115         136 :       if (0 <= prefix_len)
     116             :         {
     117             :           /* If the ownership might change, or if the directory will be
     118             :              writeable to other users and its special mode bits may
     119             :              change after the directory is created, create it with
     120             :              more restrictive permissions at first, so unauthorized
     121             :              users cannot nip in before the directory is ready.  */
     122         123 :           bool keep_owner = owner == (uid_t) -1 && group == (gid_t) -1;
     123         123 :           bool keep_special_mode_bits =
     124         123 :             ((mode_bits & (S_ISUID | S_ISGID)) | (mode & S_ISVTX)) == 0;
     125         123 :           mode_t mkdir_mode = mode;
     126         123 :           if (! keep_owner)
     127           2 :             mkdir_mode &= ~ (S_IRWXG | S_IRWXO);
     128         121 :           else if (! keep_special_mode_bits)
     129          38 :             mkdir_mode &= ~ (S_IWGRP | S_IWOTH);
     130             : 
     131         123 :           if (mkdir (dir + prefix_len, mkdir_mode) == 0)
     132             :             {
     133           5 :               announce (dir, options);
     134           5 :               preserve_existing = keep_owner & keep_special_mode_bits;
     135           5 :               savewd_chdir_options |=
     136             :                 (SAVEWD_CHDIR_NOFOLLOW
     137           5 :                  | (mode & S_IRUSR ? SAVEWD_CHDIR_READABLE : 0));
     138             :             }
     139             :           else
     140             :             {
     141         118 :               mkdir_errno = errno;
     142         118 :               mkdir_mode = -1;
     143             :             }
     144             : 
     145         123 :           if (preserve_existing)
     146             :             {
     147             :               struct stat st;
     148          84 :               if (mkdir_errno == 0
     149          81 :                   || (mkdir_errno != ENOENT && make_ancestor
     150          20 :                       && stat (dir + prefix_len, &st) == 0
     151           6 :                       && S_ISDIR (st.st_mode)))
     152           9 :                 return true;
     153             :             }
     154             :           else
     155             :             {
     156             :               int open_result[2];
     157          39 :               int chdir_result =
     158          39 :                 savewd_chdir (wd, dir + prefix_len,
     159             :                               savewd_chdir_options, open_result);
     160          39 :               if (chdir_result < -1)
     161          19 :                 return true;
     162             :               else
     163             :                 {
     164          39 :                   bool chdir_ok = (chdir_result == 0);
     165          39 :                   int chdir_errno = errno;
     166          39 :                   int fd = open_result[0];
     167          39 :                   bool chdir_failed_unexpectedly =
     168             :                     (mkdir_errno == 0
     169          39 :                      && ((! chdir_ok && (mode & S_IXUSR))
     170           2 :                          || (fd < 0 && (mode & S_IRUSR))));
     171             : 
     172          39 :                   if (chdir_failed_unexpectedly)
     173             :                     {
     174             :                       /* No need to save errno here; it's irrelevant.  */
     175           0 :                       if (0 <= fd)
     176           0 :                         close (fd);
     177             :                     }
     178             :                   else
     179             :                     {
     180          39 :                       char const *subdir = (chdir_ok ? "." : dir + prefix_len);
     181          39 :                       if (dirchownmod (fd, subdir, mkdir_mode, owner, group,
     182             :                                        mode, mode_bits)
     183             :                           == 0)
     184          15 :                         return true;
     185             :                     }
     186             : 
     187          24 :                   if (mkdir_errno == 0
     188          24 :                       || (mkdir_errno != ENOENT && make_ancestor
     189          22 :                           && errno != ENOTDIR))
     190             :                     {
     191          12 :                       error (0,
     192           8 :                              (! chdir_failed_unexpectedly ? errno
     193           0 :                               : ! chdir_ok && (mode & S_IXUSR) ? chdir_errno
     194           0 :                               : open_result[1]),
     195             :                              _(keep_owner
     196             :                                ? "cannot change permissions of %s"
     197             :                                : "cannot change owner and permissions of %s"),
     198             :                              quote (dir));
     199           4 :                       return false;
     200             :                     }
     201             :                 }
     202             :             }
     203             :         }
     204             :     }
     205             : 
     206         108 :   error (0, mkdir_errno, _("cannot create directory %s"), quote (dir));
     207         108 :   return false;
     208             : }

Generated by: LCOV version 1.10