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