(root)/
coreutils-9.4/
src/
mknod.c
       1  /* mknod -- make special files
       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  /* Written by 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 "modechange.h"
      27  #include "quote.h"
      28  #include "selinux.h"
      29  #include "smack.h"
      30  #include "xstrtol.h"
      31  
      32  /* The official name of this program (e.g., no 'g' prefix).  */
      33  #define PROGRAM_NAME "mknod"
      34  
      35  #define AUTHORS proper_name ("David MacKenzie")
      36  
      37  static struct option const longopts[] =
      38  {
      39    {GETOPT_SELINUX_CONTEXT_OPTION_DECL},
      40    {"mode", required_argument, nullptr, 'm'},
      41    {GETOPT_HELP_OPTION_DECL},
      42    {GETOPT_VERSION_OPTION_DECL},
      43    {nullptr, 0, nullptr, 0}
      44  };
      45  
      46  void
      47  usage (int status)
      48  {
      49    if (status != EXIT_SUCCESS)
      50      emit_try_help ();
      51    else
      52      {
      53        printf (_("Usage: %s [OPTION]... NAME TYPE [MAJOR MINOR]\n"),
      54                program_name);
      55        fputs (_("\
      56  Create the special file NAME of the given TYPE.\n\
      57  "), stdout);
      58  
      59        emit_mandatory_arg_note ();
      60  
      61        fputs (_("\
      62    -m, --mode=MODE    set file permission bits to MODE, not a=rw - umask\n\
      63  "), stdout);
      64        fputs (_("\
      65    -Z                   set the SELinux security context to default type\n\
      66        --context[=CTX]  like -Z, or if CTX is specified then set the SELinux\n\
      67                           or SMACK security context to CTX\n\
      68  "), stdout);
      69        fputs (HELP_OPTION_DESCRIPTION, stdout);
      70        fputs (VERSION_OPTION_DESCRIPTION, stdout);
      71        fputs (_("\
      72  \n\
      73  Both MAJOR and MINOR must be specified when TYPE is b, c, or u, and they\n\
      74  must be omitted when TYPE is p.  If MAJOR or MINOR begins with 0x or 0X,\n\
      75  it is interpreted as hexadecimal; otherwise, if it begins with 0, as octal;\n\
      76  otherwise, as decimal.  TYPE may be:\n\
      77  "), stdout);
      78        fputs (_("\
      79  \n\
      80    b      create a block (buffered) special file\n\
      81    c, u   create a character (unbuffered) special file\n\
      82    p      create a FIFO\n\
      83  "), stdout);
      84        printf (USAGE_BUILTIN_WARNING, PROGRAM_NAME);
      85        emit_ancillary_info (PROGRAM_NAME);
      86      }
      87    exit (status);
      88  }
      89  
      90  int
      91  main (int argc, char **argv)
      92  {
      93    mode_t newmode;
      94    char const *specified_mode = nullptr;
      95    int optc;
      96    size_t expected_operands;
      97    mode_t node_type;
      98    char const *scontext = nullptr;
      99    struct selabel_handle *set_security_context = nullptr;
     100  
     101    initialize_main (&argc, &argv);
     102    set_program_name (argv[0]);
     103    setlocale (LC_ALL, "");
     104    bindtextdomain (PACKAGE, LOCALEDIR);
     105    textdomain (PACKAGE);
     106  
     107    atexit (close_stdout);
     108  
     109    while ((optc = getopt_long (argc, argv, "m:Z", longopts, nullptr)) != -1)
     110      {
     111        switch (optc)
     112          {
     113          case 'm':
     114            specified_mode = optarg;
     115            break;
     116          case 'Z':
     117            if (is_smack_enabled ())
     118              {
     119                /* We don't yet support -Z to restore context with SMACK.  */
     120                scontext = optarg;
     121              }
     122            else if (is_selinux_enabled () > 0)
     123              {
     124                if (optarg)
     125                  scontext = optarg;
     126                else
     127                  {
     128                    set_security_context = selabel_open (SELABEL_CTX_FILE,
     129                                                         nullptr, 0);
     130                    if (! set_security_context)
     131                      error (0, errno, _("warning: ignoring --context"));
     132                  }
     133              }
     134            else if (optarg)
     135              {
     136                error (0, 0,
     137                       _("warning: ignoring --context; "
     138                         "it requires an SELinux/SMACK-enabled kernel"));
     139              }
     140            break;
     141          case_GETOPT_HELP_CHAR;
     142          case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
     143          default:
     144            usage (EXIT_FAILURE);
     145          }
     146      }
     147  
     148    newmode = MODE_RW_UGO;
     149    if (specified_mode)
     150      {
     151        mode_t umask_value;
     152        struct mode_change *change = mode_compile (specified_mode);
     153        if (!change)
     154          error (EXIT_FAILURE, 0, _("invalid mode"));
     155        umask_value = umask (0);
     156        umask (umask_value);
     157        newmode = mode_adjust (newmode, false, umask_value, change, nullptr);
     158        free (change);
     159        if (newmode & ~S_IRWXUGO)
     160          error (EXIT_FAILURE, 0,
     161                 _("mode must specify only file permission bits"));
     162      }
     163  
     164    /* If the number of arguments is 0 or 1,
     165       or (if it's 2 or more and the second one starts with 'p'), then there
     166       must be exactly two operands.  Otherwise, there must be four.  */
     167    expected_operands = (argc <= optind
     168                         || (optind + 1 < argc && argv[optind + 1][0] == 'p')
     169                         ? 2 : 4);
     170  
     171    if (argc - optind < expected_operands)
     172      {
     173        if (argc <= optind)
     174          error (0, 0, _("missing operand"));
     175        else
     176          error (0, 0, _("missing operand after %s"), quote (argv[argc - 1]));
     177        if (expected_operands == 4 && argc - optind == 2)
     178          fprintf (stderr, "%s\n",
     179                   _("Special files require major and minor device numbers."));
     180        usage (EXIT_FAILURE);
     181      }
     182  
     183    if (expected_operands < argc - optind)
     184      {
     185        error (0, 0, _("extra operand %s"),
     186               quote (argv[optind + expected_operands]));
     187        if (expected_operands == 2 && argc - optind == 4)
     188          fprintf (stderr, "%s\n",
     189                   _("Fifos do not have major and minor device numbers."));
     190        usage (EXIT_FAILURE);
     191      }
     192  
     193    if (scontext)
     194      {
     195        int ret = 0;
     196        if (is_smack_enabled ())
     197          ret = smack_set_label_for_self (scontext);
     198        else
     199          ret = setfscreatecon (scontext);
     200  
     201        if (ret < 0)
     202          error (EXIT_FAILURE, errno,
     203                 _("failed to set default file creation context to %s"),
     204                 quote (scontext));
     205      }
     206  
     207    /* Only check the first character, to allow mnemonic usage like
     208       'mknod /dev/rst0 character 18 0'. */
     209  
     210    switch (argv[optind + 1][0])
     211      {
     212      case 'b':			/* 'block' or 'buffered' */
     213  #ifndef S_IFBLK
     214        error (EXIT_FAILURE, 0, _("block special files not supported"));
     215  #else
     216        node_type = S_IFBLK;
     217  #endif
     218        goto block_or_character;
     219  
     220      case 'c':			/* 'character' */
     221      case 'u':			/* 'unbuffered' */
     222  #ifndef S_IFCHR
     223        error (EXIT_FAILURE, 0, _("character special files not supported"));
     224  #else
     225        node_type = S_IFCHR;
     226  #endif
     227        goto block_or_character;
     228  
     229      block_or_character:
     230        {
     231          char const *s_major = argv[optind + 2];
     232          char const *s_minor = argv[optind + 3];
     233          uintmax_t i_major, i_minor;
     234          dev_t device;
     235  
     236          if (xstrtoumax (s_major, nullptr, 0, &i_major, "") != LONGINT_OK
     237              || i_major != (major_t) i_major)
     238            error (EXIT_FAILURE, 0,
     239                   _("invalid major device number %s"), quote (s_major));
     240  
     241          if (xstrtoumax (s_minor, nullptr, 0, &i_minor, "") != LONGINT_OK
     242              || i_minor != (minor_t) i_minor)
     243            error (EXIT_FAILURE, 0,
     244                   _("invalid minor device number %s"), quote (s_minor));
     245  
     246          device = makedev (i_major, i_minor);
     247  #ifdef NODEV
     248          if (device == NODEV)
     249            error (EXIT_FAILURE, 0, _("invalid device %s %s"),
     250                   s_major, s_minor);
     251  #endif
     252  
     253          if (set_security_context)
     254            defaultcon (set_security_context, argv[optind], node_type);
     255  
     256          if (mknod (argv[optind], newmode | node_type, device) != 0)
     257            error (EXIT_FAILURE, errno, "%s", quotef (argv[optind]));
     258        }
     259        break;
     260  
     261      case 'p':			/* 'pipe' */
     262        if (set_security_context)
     263          defaultcon (set_security_context, argv[optind], S_IFIFO);
     264        if (mkfifo (argv[optind], newmode) != 0)
     265          error (EXIT_FAILURE, errno, "%s", quotef (argv[optind]));
     266        break;
     267  
     268      default:
     269        error (0, 0, _("invalid device type %s"), quote (argv[optind + 1]));
     270        usage (EXIT_FAILURE);
     271      }
     272  
     273    if (specified_mode && lchmod (argv[optind], newmode) != 0)
     274      error (EXIT_FAILURE, errno, _("cannot set permissions of %s"),
     275             quoteaf (argv[optind]));
     276  
     277    return EXIT_SUCCESS;
     278  }