tests: ensure touch honors trailing slash
[coreutils/ericb.git] / src / mknod.c
blobcf59c2bdd8da27ef1e87e7f6ce5706c9f4574cd2
1 /* mknod -- make special files
2 Copyright (C) 90, 91, 1995-2009 Free Software Foundation, Inc.
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.
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.
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/>. */
17 /* Written by David MacKenzie <djm@ai.mit.edu> */
19 #include <config.h>
20 #include <stdio.h>
21 #include <getopt.h>
22 #include <sys/types.h>
23 #include <selinux/selinux.h>
25 #include "system.h"
26 #include "error.h"
27 #include "modechange.h"
28 #include "quote.h"
29 #include "xstrtol.h"
31 /* The official name of this program (e.g., no `g' prefix). */
32 #define PROGRAM_NAME "mknod"
34 #define AUTHORS proper_name ("David MacKenzie")
36 static struct option const longopts[] =
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}
45 void
46 usage (int status)
48 if (status != EXIT_SUCCESS)
49 fprintf (stderr, _("Try `%s --help' for more information.\n"),
50 program_name);
51 else
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 -m, --mode=MODE set file permission bits to MODE, not a=rw - umask\n\
64 "), stdout);
65 fputs (_("\
66 -Z, --context=CTX set the SELinux security context of NAME to CTX\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_ancillary_info ();
86 exit (status);
89 int
90 main (int argc, char **argv)
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;
99 initialize_main (&argc, &argv);
100 set_program_name (argv[0]);
101 setlocale (LC_ALL, "");
102 bindtextdomain (PACKAGE, LOCALEDIR);
103 textdomain (PACKAGE);
105 atexit (close_stdout);
107 while ((optc = getopt_long (argc, argv, "m:Z:", longopts, NULL)) != -1)
109 switch (optc)
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);
124 newmode = (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
125 if (specified_mode)
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"));
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);
144 if (argc - optind < expected_operands)
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);
156 if (expected_operands < argc - optind)
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);
166 if (scontext && setfscreatecon (scontext) < 0)
167 error (EXIT_FAILURE, errno,
168 _("failed to set default file creation context to %s"),
169 quote (scontext));
171 /* Only check the first character, to allow mnemonic usage like
172 `mknod /dev/rst0 character 18 0'. */
174 switch (argv[optind + 1][0])
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;
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;
193 block_or_character:
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;
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));
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));
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
216 if (mknod (argv[optind], newmode | node_type, device) != 0)
217 error (EXIT_FAILURE, errno, "%s", quote (argv[optind]));
219 break;
221 case 'p': /* `pipe' */
222 if (mkfifo (argv[optind], newmode) != 0)
223 error (EXIT_FAILURE, errno, "%s", quote (argv[optind]));
224 break;
226 default:
227 error (0, 0, _("invalid device type %s"), quote (argv[optind + 1]));
228 usage (EXIT_FAILURE);
231 exit (EXIT_SUCCESS);