mknod: correct misplaced -Z description in --help output
[platform/upstream/coreutils.git] / src / mknod.c
1 /* mknod -- make special files
2    Copyright (C) 90, 91, 1995-2008 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 <http://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/selinux.h>
24
25 #include "system.h"
26 #include "error.h"
27 #include "modechange.h"
28 #include "quote.h"
29 #include "xstrtol.h"
30
31 /* The official name of this program (e.g., no `g' prefix).  */
32 #define PROGRAM_NAME "mknod"
33
34 #define AUTHORS proper_name ("David MacKenzie")
35
36 static struct option const longopts[] =
37 {
38   {GETOPT_SELINUX_CONTEXT_OPTION_DECL},
39   {"mode", required_argument, NULL, 'm'},
40   {GETOPT_HELP_OPTION_DECL},
41   {GETOPT_VERSION_OPTION_DECL},
42   {NULL, 0, NULL, 0}
43 };
44
45 void
46 usage (int status)
47 {
48   if (status != EXIT_SUCCESS)
49     fprintf (stderr, _("Try `%s --help' for more information.\n"),
50              program_name);
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 \n\
58 "), stdout);
59       fputs (_("\
60 Mandatory arguments to long options are mandatory for short options too.\n\
61 "), stdout);
62       fputs(_("\
63   -Z, --context=CTX  set the SELinux security context of NAME to CTX\n\
64 "), stdout);
65       fputs (_("\
66   -m, --mode=MODE   set file permission bits to MODE, not a=rw - umask\n\
67 "), stdout);
68       fputs (HELP_OPTION_DESCRIPTION, stdout);
69       fputs (VERSION_OPTION_DESCRIPTION, stdout);
70       fputs (_("\
71 \n\
72 Both MAJOR and MINOR must be specified when TYPE is b, c, or u, and they\n\
73 must be omitted when TYPE is p.  If MAJOR or MINOR begins with 0x or 0X,\n\
74 it is interpreted as hexadecimal; otherwise, if it begins with 0, as octal;\n\
75 otherwise, as decimal.  TYPE may be:\n\
76 "), stdout);
77       fputs (_("\
78 \n\
79   b      create a block (buffered) special file\n\
80   c, u   create a character (unbuffered) special file\n\
81   p      create a FIFO\n\
82 "), stdout);
83       printf (USAGE_BUILTIN_WARNING, PROGRAM_NAME);
84       emit_bug_reporting_address ();
85     }
86   exit (status);
87 }
88
89 int
90 main (int argc, char **argv)
91 {
92   mode_t newmode;
93   char const *specified_mode = NULL;
94   int optc;
95   int expected_operands;
96   mode_t node_type;
97   security_context_t scontext = NULL;
98
99   initialize_main (&argc, &argv);
100   set_program_name (argv[0]);
101   setlocale (LC_ALL, "");
102   bindtextdomain (PACKAGE, LOCALEDIR);
103   textdomain (PACKAGE);
104
105   atexit (close_stdout);
106
107   while ((optc = getopt_long (argc, argv, "m:Z:", longopts, NULL)) != -1)
108     {
109       switch (optc)
110         {
111         case 'm':
112           specified_mode = optarg;
113           break;
114         case 'Z':
115           scontext = optarg;
116           break;
117         case_GETOPT_HELP_CHAR;
118         case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
119         default:
120           usage (EXIT_FAILURE);
121         }
122     }
123
124   newmode = (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
125   if (specified_mode)
126     {
127       struct mode_change *change = mode_compile (specified_mode);
128       if (!change)
129         error (EXIT_FAILURE, 0, _("invalid mode"));
130       newmode = mode_adjust (newmode, false, umask (0), change, NULL);
131       free (change);
132       if (newmode & ~S_IRWXUGO)
133         error (EXIT_FAILURE, 0,
134                _("mode must specify only file permission bits"));
135     }
136
137   /* If the number of arguments is 0 or 1,
138      or (if it's 2 or more and the second one starts with `p'), then there
139      must be exactly two operands.  Otherwise, there must be four.  */
140   expected_operands = (argc <= optind
141                        || (optind + 1 < argc && argv[optind + 1][0] == 'p')
142                        ? 2 : 4);
143
144   if (argc - optind < expected_operands)
145     {
146       if (argc <= optind)
147         error (0, 0, _("missing operand"));
148       else
149         error (0, 0, _("missing operand after %s"), quote (argv[argc - 1]));
150       if (expected_operands == 4 && argc - optind == 2)
151         fprintf (stderr, "%s\n",
152                  _("Special files require major and minor device numbers."));
153       usage (EXIT_FAILURE);
154     }
155
156   if (expected_operands < argc - optind)
157     {
158       error (0, 0, _("extra operand %s"),
159              quote (argv[optind + expected_operands]));
160       if (expected_operands == 2 && argc - optind == 4)
161         fprintf (stderr, "%s\n",
162                  _("Fifos do not have major and minor device numbers."));
163       usage (EXIT_FAILURE);
164     }
165
166   if (scontext && setfscreatecon (scontext) < 0)
167     error (EXIT_FAILURE, errno,
168            _("failed to set default file creation context to %s"),
169            quote (scontext));
170
171   /* Only check the first character, to allow mnemonic usage like
172      `mknod /dev/rst0 character 18 0'. */
173
174   switch (argv[optind + 1][0])
175     {
176     case 'b':                   /* `block' or `buffered' */
177 #ifndef S_IFBLK
178       error (EXIT_FAILURE, 0, _("block special files not supported"));
179 #else
180       node_type = S_IFBLK;
181 #endif
182       goto block_or_character;
183
184     case 'c':                   /* `character' */
185     case 'u':                   /* `unbuffered' */
186 #ifndef S_IFCHR
187       error (EXIT_FAILURE, 0, _("character special files not supported"));
188 #else
189       node_type = S_IFCHR;
190 #endif
191       goto block_or_character;
192
193     block_or_character:
194       {
195         char const *s_major = argv[optind + 2];
196         char const *s_minor = argv[optind + 3];
197         uintmax_t i_major, i_minor;
198         dev_t device;
199
200         if (xstrtoumax (s_major, NULL, 0, &i_major, NULL) != LONGINT_OK
201             || i_major != (major_t) i_major)
202           error (EXIT_FAILURE, 0,
203                  _("invalid major device number %s"), quote (s_major));
204
205         if (xstrtoumax (s_minor, NULL, 0, &i_minor, NULL) != LONGINT_OK
206             || i_minor != (minor_t) i_minor)
207           error (EXIT_FAILURE, 0,
208                  _("invalid minor device number %s"), quote (s_minor));
209
210         device = makedev (i_major, i_minor);
211 #ifdef NODEV
212         if (device == NODEV)
213           error (EXIT_FAILURE, 0, _("invalid device %s %s"), s_major, s_minor);
214 #endif
215
216         if (mknod (argv[optind], newmode | node_type, device) != 0)
217           error (EXIT_FAILURE, errno, "%s", quote (argv[optind]));
218       }
219       break;
220
221     case 'p':                   /* `pipe' */
222       if (mkfifo (argv[optind], newmode) != 0)
223         error (EXIT_FAILURE, errno, "%s", quote (argv[optind]));
224       break;
225
226     default:
227       error (0, 0, _("invalid device type %s"), quote (argv[optind + 1]));
228       usage (EXIT_FAILURE);
229     }
230
231   exit (EXIT_SUCCESS);
232 }