(root)/
coreutils-9.4/
lib/
mkdir-p.c
       1  /* mkdir-p.c -- Ensure that a directory and its parents exist.
       2  
       3     Copyright (C) 1990, 1997-2000, 2002-2007, 2009-2023 Free Software
       4     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 <https://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, using mkdir with permissions MODE;
      56     here, MODE is affected by the umask in the usual way.  It is also
      57     OK if MAKE_ANCESTOR is not null and a 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.  Changing the mode in this way is necessary if DIR already
      69     existed, if MODE and MODE_BITS specify non-permissions bits like
      70     S_ISUID, or if MODE and MODE_BITS specify permissions bits that are
      71     masked out by the umask.  MODE_BITS should be a subset of
      72     CHMOD_MODE_BITS.
      73  
      74     However, if PRESERVE_EXISTING is true and DIR already exists,
      75     do not attempt to set DIR's ownership and file mode bits.
      76  
      77     Return true if DIR exists as a directory with the proper ownership
      78     and file mode bits when done, or if a child process has been
      79     dispatched to do the real work (though the child process may not
      80     have finished yet -- it is the caller's responsibility to handle
      81     this).  Report a diagnostic and return false on failure, storing
      82     '\0' into *DIR if an ancestor directory had problems.  */
      83  
      84  bool
      85  make_dir_parents (char *dir,
      86                    struct savewd *wd,
      87                    int (*make_ancestor) (char const *, char const *, void *),
      88                    void *options,
      89                    mode_t mode,
      90                    void (*announce) (char const *, void *),
      91                    mode_t mode_bits,
      92                    uid_t owner,
      93                    gid_t group,
      94                    bool preserve_existing)
      95  {
      96    int mkdir_errno = (IS_ABSOLUTE_FILE_NAME (dir) ? 0 : savewd_errno (wd));
      97  
      98    if (mkdir_errno == 0)
      99      {
     100        ptrdiff_t prefix_len = 0;
     101        int savewd_chdir_options = (HAVE_FCHMOD ? SAVEWD_CHDIR_SKIP_READABLE : 0);
     102  
     103        if (make_ancestor)
     104          {
     105            prefix_len = mkancesdirs (dir, wd, make_ancestor, options);
     106            if (prefix_len < 0)
     107              {
     108                if (prefix_len < -1)
     109                  return true;
     110                mkdir_errno = errno;
     111              }
     112          }
     113  
     114        if (0 <= prefix_len)
     115          {
     116            /* If the ownership might change, or if the directory will be
     117               writable to other users and its special mode bits may
     118               change after the directory is created, create it with
     119               more restrictive permissions at first, so unauthorized
     120               users cannot nip in before the directory is ready.  */
     121            bool keep_owner = owner == (uid_t) -1 && group == (gid_t) -1;
     122            bool keep_special_mode_bits =
     123              ((mode_bits & (S_ISUID | S_ISGID)) | (mode & S_ISVTX)) == 0;
     124            mode_t mkdir_mode = mode;
     125            if (! keep_owner)
     126              mkdir_mode &= ~ (S_IRWXG | S_IRWXO);
     127            else if (! keep_special_mode_bits)
     128              mkdir_mode &= ~ (S_IWGRP | S_IWOTH);
     129  
     130            if (mkdir (dir + prefix_len, mkdir_mode) == 0)
     131              {
     132                /* True if the caller does not care about the umask's
     133                   effect on the permissions.  */
     134                bool umask_must_be_ok = (mode & mode_bits & S_IRWXUGO) == 0;
     135  
     136                announce (dir, options);
     137                preserve_existing = (keep_owner & keep_special_mode_bits
     138                                     & umask_must_be_ok);
     139                savewd_chdir_options |= SAVEWD_CHDIR_NOFOLLOW;
     140              }
     141            else
     142              {
     143                mkdir_errno = errno;
     144                mkdir_mode = -1;
     145              }
     146  
     147            if (preserve_existing)
     148              {
     149                if (mkdir_errno == 0)
     150                  return true;
     151                if (mkdir_errno != ENOENT && make_ancestor)
     152                  {
     153                    struct stat st;
     154                    if (stat (dir + prefix_len, &st) == 0)
     155                      {
     156                        if (S_ISDIR (st.st_mode))
     157                          return true;
     158                      }
     159                    else if (mkdir_errno == EEXIST
     160                             && errno != ENOENT && errno != ENOTDIR)
     161                      {
     162                        error (0, errno, _("cannot stat %s"), quote (dir));
     163                        return false;
     164                      }
     165                  }
     166              }
     167            else
     168              {
     169                int open_result[2];
     170                int chdir_result =
     171                  savewd_chdir (wd, dir + prefix_len,
     172                                savewd_chdir_options, open_result);
     173                if (chdir_result < -1)
     174                  return true;
     175                else
     176                  {
     177                    bool chdir_ok = (chdir_result == 0);
     178                    char const *subdir = (chdir_ok ? "." : dir + prefix_len);
     179                    if (dirchownmod (open_result[0], subdir, mkdir_mode,
     180                                     owner, group, mode, mode_bits)
     181                        == 0)
     182                      return true;
     183  
     184                    if (mkdir_errno == 0
     185                        || (mkdir_errno != ENOENT && make_ancestor
     186                            && errno != ENOTDIR))
     187                      {
     188                        error (0, errno,
     189                               _(keep_owner
     190                                 ? "cannot change permissions of %s"
     191                                 : "cannot change owner and permissions of %s"),
     192                               quote (dir));
     193                        return false;
     194                      }
     195                  }
     196              }
     197          }
     198      }
     199  
     200    error (0, mkdir_errno, _("cannot create directory %s"), quote (dir));
     201    return false;
     202  }