(root)/
coreutils-9.4/
src/
mkdir.c
       1  /* mkdir -- make directories
       2     Copyright (C) 1990-2023 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  /* David MacKenzie <djm@ai.mit.edu>  */
      18  
      19  #include <config.h>
      20  #include <stdio.h>
      21  #include <getopt.h>
      22  #include <sys/types.h>
      23  #include <selinux/label.h>
      24  
      25  #include "system.h"
      26  #include "mkdir-p.h"
      27  #include "modechange.h"
      28  #include "prog-fprintf.h"
      29  #include "quote.h"
      30  #include "savewd.h"
      31  #include "selinux.h"
      32  #include "smack.h"
      33  
      34  /* The official name of this program (e.g., no 'g' prefix).  */
      35  #define PROGRAM_NAME "mkdir"
      36  
      37  #define AUTHORS proper_name ("David MacKenzie")
      38  
      39  static struct option const longopts[] =
      40  {
      41    {GETOPT_SELINUX_CONTEXT_OPTION_DECL},
      42    {"mode", required_argument, nullptr, 'm'},
      43    {"parents", no_argument, nullptr, 'p'},
      44    {"verbose", no_argument, nullptr, 'v'},
      45    {GETOPT_HELP_OPTION_DECL},
      46    {GETOPT_VERSION_OPTION_DECL},
      47    {nullptr, 0, nullptr, 0}
      48  };
      49  
      50  void
      51  usage (int status)
      52  {
      53    if (status != EXIT_SUCCESS)
      54      emit_try_help ();
      55    else
      56      {
      57        printf (_("Usage: %s [OPTION]... DIRECTORY...\n"), program_name);
      58        fputs (_("\
      59  Create the DIRECTORY(ies), if they do not already exist.\n\
      60  "), stdout);
      61  
      62        emit_mandatory_arg_note ();
      63  
      64        fputs (_("\
      65    -m, --mode=MODE   set file mode (as in chmod), not a=rwx - umask\n\
      66    -p, --parents     no error if existing, make parent directories as needed,\n\
      67                      with their file modes unaffected by any -m option.\n\
      68    -v, --verbose     print a message for each created directory\n\
      69  "), stdout);
      70        fputs (_("\
      71    -Z                   set SELinux security context of each created directory\n\
      72                           to the default type\n\
      73        --context[=CTX]  like -Z, or if CTX is specified then set the SELinux\n\
      74                           or SMACK security context to CTX\n\
      75  "), stdout);
      76        fputs (HELP_OPTION_DESCRIPTION, stdout);
      77        fputs (VERSION_OPTION_DESCRIPTION, stdout);
      78        emit_ancillary_info (PROGRAM_NAME);
      79      }
      80    exit (status);
      81  }
      82  
      83  /* Options passed to subsidiary functions.  */
      84  struct mkdir_options
      85  {
      86    /* Function to make an ancestor, or nullptr if ancestors should not be
      87       made.  */
      88    int (*make_ancestor_function) (char const *, char const *, void *);
      89  
      90    /* Umask value for when making an ancestor.  */
      91    mode_t umask_ancestor;
      92  
      93    /* Umask value for when making the directory itself.  */
      94    mode_t umask_self;
      95  
      96    /* Mode for directory itself.  */
      97    mode_t mode;
      98  
      99    /* File mode bits affected by MODE.  */
     100    mode_t mode_bits;
     101  
     102    /* Set the SELinux File Context.  */
     103    struct selabel_handle *set_security_context;
     104  
     105    /* If not null, format to use when reporting newly made directories.  */
     106    char const *created_directory_format;
     107  };
     108  
     109  /* Report that directory DIR was made, if OPTIONS requests this.  */
     110  static void
     111  announce_mkdir (char const *dir, void *options)
     112  {
     113    struct mkdir_options const *o = options;
     114    if (o->created_directory_format)
     115      prog_fprintf (stdout, o->created_directory_format, quoteaf (dir));
     116  }
     117  
     118  /* Make ancestor directory DIR, whose last component is COMPONENT,
     119     with options OPTIONS.  Assume the working directory is COMPONENT's
     120     parent.  Return 0 if successful and the resulting directory is
     121     readable, 1 if successful but the resulting directory is not
     122     readable, -1 (setting errno) otherwise.  */
     123  static int
     124  make_ancestor (char const *dir, char const *component, void *options)
     125  {
     126    struct mkdir_options const *o = options;
     127  
     128    if (o->set_security_context
     129        && defaultcon (o->set_security_context, component, S_IFDIR) < 0
     130        && ! ignorable_ctx_err (errno))
     131      error (0, errno, _("failed to set default creation context for %s"),
     132             quoteaf (dir));
     133  
     134    if (o->umask_ancestor != o->umask_self)
     135      umask (o->umask_ancestor);
     136    int r = mkdir (component, S_IRWXUGO);
     137    if (o->umask_ancestor != o->umask_self)
     138      {
     139        int mkdir_errno = errno;
     140        umask (o->umask_self);
     141        errno = mkdir_errno;
     142      }
     143    if (r == 0)
     144      {
     145        r = (o->umask_ancestor & S_IRUSR) != 0;
     146        announce_mkdir (dir, options);
     147      }
     148    return r;
     149  }
     150  
     151  /* Process a command-line file name.  */
     152  static int
     153  process_dir (char *dir, struct savewd *wd, void *options)
     154  {
     155    struct mkdir_options const *o = options;
     156  
     157    /* If possible set context before DIR created.  */
     158    if (o->set_security_context)
     159      {
     160        if (! o->make_ancestor_function
     161            && defaultcon (o->set_security_context, dir, S_IFDIR) < 0
     162            && ! ignorable_ctx_err (errno))
     163          error (0, errno, _("failed to set default creation context for %s"),
     164                 quoteaf (dir));
     165      }
     166  
     167    int ret = (make_dir_parents (dir, wd, o->make_ancestor_function, options,
     168                                 o->mode, announce_mkdir,
     169                                 o->mode_bits, (uid_t) -1, (gid_t) -1, true)
     170               ? EXIT_SUCCESS
     171               : EXIT_FAILURE);
     172  
     173    /* FIXME: Due to the current structure of make_dir_parents()
     174       we don't have the facility to call defaultcon() before the
     175       final component of DIR is created.  So for now, create the
     176       final component with the context from previous component
     177       and here we set the context for the final component. */
     178    if (ret == EXIT_SUCCESS && o->set_security_context
     179        && o->make_ancestor_function)
     180      {
     181        if (! restorecon (o->set_security_context, last_component (dir), false)
     182            && ! ignorable_ctx_err (errno))
     183          error (0, errno, _("failed to restore context for %s"),
     184                 quoteaf (dir));
     185      }
     186  
     187    return ret;
     188  }
     189  
     190  int
     191  main (int argc, char **argv)
     192  {
     193    char const *specified_mode = nullptr;
     194    int optc;
     195    char const *scontext = nullptr;
     196    struct mkdir_options options;
     197  
     198    options.make_ancestor_function = nullptr;
     199    options.mode = S_IRWXUGO;
     200    options.mode_bits = 0;
     201    options.created_directory_format = nullptr;
     202    options.set_security_context = nullptr;
     203  
     204    initialize_main (&argc, &argv);
     205    set_program_name (argv[0]);
     206    setlocale (LC_ALL, "");
     207    bindtextdomain (PACKAGE, LOCALEDIR);
     208    textdomain (PACKAGE);
     209  
     210    atexit (close_stdout);
     211  
     212    while ((optc = getopt_long (argc, argv, "pm:vZ", longopts, nullptr)) != -1)
     213      {
     214        switch (optc)
     215          {
     216          case 'p':
     217            options.make_ancestor_function = make_ancestor;
     218            break;
     219          case 'm':
     220            specified_mode = optarg;
     221            break;
     222          case 'v': /* --verbose  */
     223            options.created_directory_format = _("created directory %s");
     224            break;
     225          case 'Z':
     226            if (is_smack_enabled ())
     227              {
     228                /* We don't yet support -Z to restore context with SMACK.  */
     229                scontext = optarg;
     230              }
     231            else if (is_selinux_enabled () > 0)
     232              {
     233                if (optarg)
     234                  scontext = optarg;
     235                else
     236                  {
     237                    options.set_security_context = selabel_open (SELABEL_CTX_FILE,
     238                                                                 nullptr, 0);
     239                    if (! options.set_security_context)
     240                      error (0, errno, _("warning: ignoring --context"));
     241                  }
     242              }
     243            else if (optarg)
     244              {
     245                error (0, 0,
     246                       _("warning: ignoring --context; "
     247                         "it requires an SELinux/SMACK-enabled kernel"));
     248              }
     249            break;
     250          case_GETOPT_HELP_CHAR;
     251          case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
     252          default:
     253            usage (EXIT_FAILURE);
     254          }
     255      }
     256  
     257    if (optind == argc)
     258      {
     259        error (0, 0, _("missing operand"));
     260        usage (EXIT_FAILURE);
     261      }
     262  
     263    /* FIXME: This assumes mkdir() is done in the same process.
     264       If that's not always the case we would need to call this
     265       like we do when options.set_security_context.  */
     266    if (scontext)
     267      {
     268        int ret = 0;
     269        if (is_smack_enabled ())
     270          ret = smack_set_label_for_self (scontext);
     271        else
     272          ret = setfscreatecon (scontext);
     273  
     274        if (ret < 0)
     275          error (EXIT_FAILURE, errno,
     276                 _("failed to set default file creation context to %s"),
     277                 quote (scontext));
     278      }
     279  
     280  
     281    if (options.make_ancestor_function || specified_mode)
     282      {
     283        mode_t umask_value = umask (0);
     284        options.umask_ancestor = umask_value & ~(S_IWUSR | S_IXUSR);
     285  
     286        if (specified_mode)
     287          {
     288            struct mode_change *change = mode_compile (specified_mode);
     289            if (!change)
     290              error (EXIT_FAILURE, 0, _("invalid mode %s"),
     291                     quote (specified_mode));
     292            options.mode = mode_adjust (S_IRWXUGO, true, umask_value, change,
     293                                        &options.mode_bits);
     294            options.umask_self = umask_value & ~options.mode;
     295            free (change);
     296          }
     297        else
     298          {
     299            options.mode = S_IRWXUGO;
     300            options.umask_self = umask_value;
     301          }
     302  
     303        umask (options.umask_self);
     304      }
     305  
     306    return savewd_process_files (argc - optind, argv + optind,
     307                                 process_dir, &options);
     308  }