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