blob: 79cfc609be58930749d074de3198c1b09cdf6034 [file] [log] [blame]
Eric Andersen611ec0e2005-06-09 11:11:10 +00001/* vi: set sw=4 ts=4: */
2/*
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 as
5 * published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU Library General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
15 *
16 */
17
18#define _GNU_SOURCE
19#include <stdio.h>
20#include <stdlib.h>
21#include <string.h>
22#include <fcntl.h>
23#include <getopt.h>
24#include <time.h>
25#include <pwd.h>
26#include <grp.h>
27#include <unistd.h>
Eric Anderseneb0ee452005-06-24 07:26:33 +000028#include <ctype.h>
Eric Andersen611ec0e2005-06-09 11:11:10 +000029#include <errno.h>
Eric Anderseneb0ee452005-06-24 07:26:33 +000030#include <libgen.h>
Eric Andersen611ec0e2005-06-09 11:11:10 +000031#include <stdarg.h>
32#include <sys/stat.h>
33#include <sys/types.h>
Bernhard Reutner-Fischer300b8b72007-03-20 17:21:35 +000034#ifndef __APPLE__
Eric Andersen611ec0e2005-06-09 11:11:10 +000035#include <sys/sysmacros.h> /* major() and minor() */
Bernhard Reutner-Fischer300b8b72007-03-20 17:21:35 +000036#endif
Eric Andersen611ec0e2005-06-09 11:11:10 +000037
38const char *bb_applet_name;
39
40void bb_verror_msg(const char *s, va_list p)
41{
42 fflush(stdout);
43 fprintf(stderr, "%s: ", bb_applet_name);
44 vfprintf(stderr, s, p);
45}
46
47void bb_error_msg(const char *s, ...)
48{
49 va_list p;
50
51 va_start(p, s);
52 bb_verror_msg(s, p);
53 va_end(p);
54 putc('\n', stderr);
55}
56
57void bb_error_msg_and_die(const char *s, ...)
58{
59 va_list p;
60
61 va_start(p, s);
62 bb_verror_msg(s, p);
63 va_end(p);
64 putc('\n', stderr);
65 exit(1);
66}
Eric Andersen79e32502005-06-24 09:10:06 +000067
Eric Andersen611ec0e2005-06-09 11:11:10 +000068void bb_vperror_msg(const char *s, va_list p)
69{
70 int err=errno;
71 if(s == 0) s = "";
72 bb_verror_msg(s, p);
73 if (*s) s = ": ";
74 fprintf(stderr, "%s%s\n", s, strerror(err));
75}
76
77void bb_perror_msg(const char *s, ...)
78{
79 va_list p;
80
81 va_start(p, s);
82 bb_vperror_msg(s, p);
83 va_end(p);
84}
85
86void bb_perror_msg_and_die(const char *s, ...)
87{
88 va_list p;
89
90 va_start(p, s);
91 bb_vperror_msg(s, p);
92 va_end(p);
93 exit(1);
94}
95
96FILE *bb_xfopen(const char *path, const char *mode)
97{
98 FILE *fp;
99 if ((fp = fopen(path, mode)) == NULL)
100 bb_perror_msg_and_die("%s", path);
101 return fp;
102}
103
Eric Anderseneb0ee452005-06-24 07:26:33 +0000104enum {
105 FILEUTILS_PRESERVE_STATUS = 1,
106 FILEUTILS_DEREFERENCE = 2,
107 FILEUTILS_RECUR = 4,
108 FILEUTILS_FORCE = 8,
109 FILEUTILS_INTERACTIVE = 16
110};
Eric Andersen611ec0e2005-06-09 11:11:10 +0000111int bb_make_directory (char *path, long mode, int flags)
112{
113 mode_t mask;
114 const char *fail_msg;
115 char *s = path;
116 char c;
117 struct stat st;
Eric Andersen611ec0e2005-06-09 11:11:10 +0000118
119 mask = umask(0);
120 if (mode == -1) {
121 umask(mask);
122 mode = (S_IXUSR | S_IXGRP | S_IXOTH |
123 S_IWUSR | S_IWGRP | S_IWOTH |
124 S_IRUSR | S_IRGRP | S_IROTH) & ~mask;
125 } else {
126 umask(mask & ~0300);
127 }
128
129 do {
130 c = 0;
131
132 if (flags & FILEUTILS_RECUR) { /* Get the parent. */
133 /* Bypass leading non-'/'s and then subsequent '/'s. */
134 while (*s) {
135 if (*s == '/') {
136 do {
137 ++s;
138 } while (*s == '/');
139 c = *s; /* Save the current char */
140 *s = 0; /* and replace it with nul. */
141 break;
142 }
143 ++s;
144 }
145 }
146
147 if (mkdir(path, 0777) < 0) {
148 /* If we failed for any other reason than the directory
149 * already exists, output a diagnostic and return -1.*/
Bernhard Reutner-Fischer300b8b72007-03-20 17:21:35 +0000150 if ((errno != EEXIST && errno != EISDIR)
Eric Andersen79e32502005-06-24 09:10:06 +0000151 || !(flags & FILEUTILS_RECUR)
152 || (stat(path, &st) < 0 || !S_ISDIR(st.st_mode))) {
Eric Andersen611ec0e2005-06-09 11:11:10 +0000153 fail_msg = "create";
154 umask(mask);
155 break;
156 }
157 /* Since the directory exists, don't attempt to change
158 * permissions if it was the full target. Note that
159 * this is not an error conditon. */
160 if (!c) {
161 umask(mask);
162 return 0;
163 }
164 }
165
166 if (!c) {
167 /* Done. If necessary, updated perms on the newly
168 * created directory. Failure to update here _is_
169 * an error.*/
170 umask(mask);
171 if ((mode != -1) && (chmod(path, mode) < 0)){
172 fail_msg = "set permissions of";
173 break;
174 }
175 return 0;
176 }
177
178 /* Remove any inserted nul from the path (recursive mode). */
179 *s = c;
180
181 } while (1);
182
183 bb_perror_msg ("Cannot %s directory `%s'", fail_msg, path);
184 return -1;
185}
186
187const char * const bb_msg_memory_exhausted = "memory exhausted";
188
189void *xmalloc(size_t size)
190{
191 void *ptr = malloc(size);
192 if (ptr == NULL && size != 0)
193 bb_error_msg_and_die(bb_msg_memory_exhausted);
194 return ptr;
195}
196
197void *xcalloc(size_t nmemb, size_t size)
198{
199 void *ptr = calloc(nmemb, size);
200 if (ptr == NULL && nmemb != 0 && size != 0)
201 bb_error_msg_and_die(bb_msg_memory_exhausted);
202 return ptr;
203}
204
205void *xrealloc(void *ptr, size_t size)
206{
207 ptr = realloc(ptr, size);
208 if (ptr == NULL && size != 0)
209 bb_error_msg_and_die(bb_msg_memory_exhausted);
210 return ptr;
211}
212
213char *private_get_line_from_file(FILE *file, int c)
214{
215#define GROWBY (80) /* how large we will grow strings by */
216
217 int ch;
218 int idx = 0;
219 char *linebuf = NULL;
220 int linebufsz = 0;
221
222 while ((ch = getc(file)) != EOF) {
223 /* grow the line buffer as necessary */
224 if (idx > linebufsz - 2) {
225 linebuf = xrealloc(linebuf, linebufsz += GROWBY);
226 }
227 linebuf[idx++] = (char)ch;
228 if (!ch) return linebuf;
229 if (c<2 && ch == '\n') {
230 if (c) {
231 --idx;
232 }
233 break;
234 }
235 }
236 if (linebuf) {
237 if (ferror(file)) {
238 free(linebuf);
239 return NULL;
240 }
241 linebuf[idx] = 0;
242 }
243 return linebuf;
244}
245
246char *bb_get_chomped_line_from_file(FILE *file)
247{
248 return private_get_line_from_file(file, 1);
249}
250
251long my_getpwnam(const char *name)
252{
253 struct passwd *myuser;
254
255 myuser = getpwnam(name);
256 if (myuser==NULL)
257 bb_error_msg_and_die("unknown user name: %s", name);
258
259 return myuser->pw_uid;
260}
261
262long my_getgrnam(const char *name)
263{
264 struct group *mygroup;
265
266 mygroup = getgrnam(name);
267 if (mygroup==NULL)
268 bb_error_msg_and_die("unknown group name: %s", name);
269
270 return (mygroup->gr_gid);
271}
272
273unsigned long get_ug_id(const char *s, long (*my_getxxnam)(const char *))
274{
275 unsigned long r;
276 char *p;
277
278 r = strtoul(s, &p, 10);
279 if (*p || (s == p)) {
280 r = my_getxxnam(s);
281 }
282
283 return r;
284}
285
286char * last_char_is(const char *s, int c)
287{
288 char *sret = (char *)s;
289 if (sret) {
290 sret = strrchr(sret, c);
291 if(sret != NULL && *(sret+1) != 0)
292 sret = NULL;
293 }
294 return sret;
295}
296
297void bb_xasprintf(char **string_ptr, const char *format, ...)
298{
299 va_list p;
300 int r;
301
302 va_start(p, format);
303 r = vasprintf(string_ptr, format, p);
304 va_end(p);
305
306 if (r < 0) {
307 bb_perror_msg_and_die("bb_xasprintf");
308 }
309}
310
311char *concat_path_file(const char *path, const char *filename)
312{
313 char *outbuf;
314 char *lc;
315
316 if (!path)
317 path = "";
318 lc = last_char_is(path, '/');
319 while (*filename == '/')
320 filename++;
321 bb_xasprintf(&outbuf, "%s%s%s", path, (lc==NULL ? "/" : ""), filename);
322
323 return outbuf;
324}
325
Eric Andersen79e32502005-06-24 09:10:06 +0000326void bb_show_usage(void)
327{
328 fprintf(stderr, "%s: [-d device_table] rootdir\n\n", bb_applet_name);
329 fprintf(stderr, "Creates a batch of special files as specified in a device table.\n");
330 fprintf(stderr, "Device table entries take the form of:\n");
331 fprintf(stderr, "type mode user group major minor start increment count\n\n");
332 fprintf(stderr, "Where name is the file name, type can be one of:\n");
333 fprintf(stderr, " f A regular file\n");
334 fprintf(stderr, " d Directory\n");
335 fprintf(stderr, " c Character special device file\n");
336 fprintf(stderr, " b Block special device file\n");
337 fprintf(stderr, " p Fifo (named pipe)\n");
338 fprintf(stderr, "uid is the user id for the target file, gid is the group id for the\n");
339 fprintf(stderr, "target file. The rest of the entries (major, minor, etc) apply to\n");
340 fprintf(stderr, "to device special files. A '-' may be used for blank entries.\n\n");
341 fprintf(stderr, "For example:\n");
342 fprintf(stderr, "<name> <type> <mode> <uid> <gid> <major> <minor> <start> <inc> <count>\n");
343 fprintf(stderr, "/dev d 755 0 0 - - - - -\n");
344 fprintf(stderr, "/dev/console c 666 0 0 5 1 - - -\n");
345 fprintf(stderr, "/dev/null c 666 0 0 1 3 0 0 -\n");
346 fprintf(stderr, "/dev/zero c 666 0 0 1 5 0 0 -\n");
347 fprintf(stderr, "/dev/hda b 640 0 0 3 0 0 0 -\n");
Thomas De Schampheleirea85971a2011-02-23 17:44:41 +0100348 fprintf(stderr, "/dev/hda b 640 0 0 3 1 1 1 15\n");
349 fprintf(stderr, "/dev/rtp b 640 0 0 250 0 0 1 5\n");
350 fprintf(stderr, "/dev/gps b 640 0 0 251 0 1 1 5\n");
351 fprintf(stderr, "/dev/uio b 640 0 0 252 0 1 2 5\n");
352 fprintf(stderr, "/dev/uio b 640 0 0 252 1 6 2 5\n\n");
Eric Andersen79e32502005-06-24 09:10:06 +0000353 fprintf(stderr, "Will Produce:\n");
354 fprintf(stderr, "/dev\n");
355 fprintf(stderr, "/dev/console\n");
356 fprintf(stderr, "/dev/null\n");
357 fprintf(stderr, "/dev/zero\n");
358 fprintf(stderr, "/dev/hda\n");
Thomas De Schampheleirea85971a2011-02-23 17:44:41 +0100359 fprintf(stderr, "/dev/hda[1-15] with minor numbers [1-15]\n");
360 fprintf(stderr, "/dev/rtp[0-4] with minor numbers [0-4]\n");
361 fprintf(stderr, "/dev/gps[1-5] with minor numbers [0-4]\n");
362 fprintf(stderr, "/dev/uio[1-5] with minor numbers 0,2,4,6,8\n");
363 fprintf(stderr, "/dev/uio[6-10] with minor numbers 1,3,5,7,9\n");
Eric Andersen79e32502005-06-24 09:10:06 +0000364 exit(1);
365}
366
Eric Andersen611ec0e2005-06-09 11:11:10 +0000367int main(int argc, char **argv)
368{
369 int opt;
370 FILE *table = stdin;
Eric Andersen79e32502005-06-24 09:10:06 +0000371 char *rootdir = NULL;
372 char *line = NULL;
Eric Anderseneb0ee452005-06-24 07:26:33 +0000373 int linenum = 0;
Eric Andersen611ec0e2005-06-09 11:11:10 +0000374 int ret = EXIT_SUCCESS;
375
Eric Anderseneb0ee452005-06-24 07:26:33 +0000376 bb_applet_name = basename(argv[0]);
Eric Andersen611ec0e2005-06-09 11:11:10 +0000377
Eric Anderseneb0ee452005-06-24 07:26:33 +0000378 while ((opt = getopt(argc, argv, "d:")) != -1) {
Eric Andersen611ec0e2005-06-09 11:11:10 +0000379 switch(opt) {
Eric Andersen79e32502005-06-24 09:10:06 +0000380 case 'd':
381 table = bb_xfopen((line=optarg), "r");
382 break;
383 default:
384 bb_show_usage();
Eric Andersen611ec0e2005-06-09 11:11:10 +0000385 }
386 }
387
Eric Andersen79e32502005-06-24 09:10:06 +0000388 if (optind >= argc || (rootdir=argv[optind])==NULL) {
Eric Anderseneb0ee452005-06-24 07:26:33 +0000389 bb_error_msg_and_die("root directory not speficied");
390 }
Eric Anderseneb0ee452005-06-24 07:26:33 +0000391
392 if (chdir(rootdir) != 0) {
Eric Andersen611ec0e2005-06-09 11:11:10 +0000393 bb_perror_msg_and_die("Couldnt chdir to %s", rootdir);
394 }
395
396 umask(0);
397
Eric Andersen79e32502005-06-24 09:10:06 +0000398 printf("rootdir=%s\n", rootdir);
399 if (line) {
400 printf("table='%s'\n", line);
401 } else {
402 printf("table=<stdin>\n");
403 }
404
Eric Andersen611ec0e2005-06-09 11:11:10 +0000405 while ((line = bb_get_chomped_line_from_file(table))) {
406 char type;
407 unsigned int mode = 0755;
408 unsigned int major = 0;
409 unsigned int minor = 0;
410 unsigned int count = 0;
411 unsigned int increment = 0;
412 unsigned int start = 0;
413 char name[41];
414 char user[41];
415 char group[41];
416 char *full_name;
417 uid_t uid;
418 gid_t gid;
419
Eric Anderseneb0ee452005-06-24 07:26:33 +0000420 linenum++;
421
Eric Andersen611ec0e2005-06-09 11:11:10 +0000422 if ((2 > sscanf(line, "%40s %c %o %40s %40s %u %u %u %u %u", name,
Eric Andersen79e32502005-06-24 09:10:06 +0000423 &type, &mode, user, group, &major,
424 &minor, &start, &increment, &count)) ||
Matt Fleming7879a4b2010-09-24 14:43:17 +0100425 ((major | minor | start | count | increment) > 0xfffff))
Eric Anderseneb0ee452005-06-24 07:26:33 +0000426 {
427 if (*line=='\0' || *line=='#' || isspace(*line))
428 continue;
429 bb_error_msg("line %d invalid: '%s'\n", linenum, line);
Eric Andersen611ec0e2005-06-09 11:11:10 +0000430 ret = EXIT_FAILURE;
431 continue;
432 }
433 if (name[0] == '#') {
434 continue;
435 }
Bernhard Reutner-Fischer3a1147b2005-12-21 15:03:31 +0000436 if (*group) {
Eric Andersen611ec0e2005-06-09 11:11:10 +0000437 gid = get_ug_id(group, my_getgrnam);
438 } else {
439 gid = getgid();
440 }
Bernhard Reutner-Fischer3a1147b2005-12-21 15:03:31 +0000441 if (*user) {
Eric Andersen611ec0e2005-06-09 11:11:10 +0000442 uid = get_ug_id(user, my_getpwnam);
443 } else {
444 uid = getuid();
445 }
446 full_name = concat_path_file(rootdir, name);
447
448 if (type == 'd') {
Eric Anderseneb0ee452005-06-24 07:26:33 +0000449 bb_make_directory(full_name, mode | S_IFDIR, FILEUTILS_RECUR);
Eric Andersen611ec0e2005-06-09 11:11:10 +0000450 if (chown(full_name, uid, gid) == -1) {
Eric Anderseneb0ee452005-06-24 07:26:33 +0000451 bb_perror_msg("line %d: chown failed for %s", linenum, full_name);
Eric Andersen611ec0e2005-06-09 11:11:10 +0000452 ret = EXIT_FAILURE;
453 goto loop;
454 }
Eric Andersen7b8aeae2005-07-18 20:06:49 +0000455 if ((mode != -1) && (chmod(full_name, mode) < 0)){
456 bb_perror_msg("line %d: chmod failed for %s", linenum, full_name);
457 ret = EXIT_FAILURE;
458 goto loop;
459 }
460 } else if (type == 'f') {
461 struct stat st;
462 if ((stat(full_name, &st) < 0 || !S_ISREG(st.st_mode))) {
463 bb_perror_msg("line %d: regular file '%s' does not exist", linenum, full_name);
464 ret = EXIT_FAILURE;
465 goto loop;
466 }
467 if (chown(full_name, uid, gid) == -1) {
468 bb_perror_msg("line %d: chown failed for %s", linenum, full_name);
469 ret = EXIT_FAILURE;
470 goto loop;
471 }
472 if ((mode != -1) && (chmod(full_name, mode) < 0)){
473 bb_perror_msg("line %d: chmod failed for %s", linenum, full_name);
474 ret = EXIT_FAILURE;
475 goto loop;
476 }
477 } else
478 {
Eric Andersen611ec0e2005-06-09 11:11:10 +0000479 dev_t rdev;
480
481 if (type == 'p') {
482 mode |= S_IFIFO;
483 }
484 else if (type == 'c') {
485 mode |= S_IFCHR;
486 }
487 else if (type == 'b') {
488 mode |= S_IFBLK;
489 } else {
Eric Anderseneb0ee452005-06-24 07:26:33 +0000490 bb_error_msg("line %d: Unsupported file type %c", linenum, type);
Eric Andersen611ec0e2005-06-09 11:11:10 +0000491 ret = EXIT_FAILURE;
492 goto loop;
493 }
494
495 if (count > 0) {
496 int i;
497 char *full_name_inc;
498
Matt Fleming7879a4b2010-09-24 14:43:17 +0100499 full_name_inc = xmalloc(strlen(full_name) + 8);
Thomas De Schampheleirea85971a2011-02-23 17:44:41 +0100500 for (i = 0; i < count; i++) {
501 sprintf(full_name_inc, "%s%d", full_name, start + i);
502 rdev = makedev(major, minor + i * increment);
Eric Andersen611ec0e2005-06-09 11:11:10 +0000503 if (mknod(full_name_inc, mode, rdev) == -1) {
Eric Anderseneb0ee452005-06-24 07:26:33 +0000504 bb_perror_msg("line %d: Couldnt create node %s", linenum, full_name_inc);
Eric Andersen611ec0e2005-06-09 11:11:10 +0000505 ret = EXIT_FAILURE;
506 }
507 else if (chown(full_name_inc, uid, gid) == -1) {
Eric Anderseneb0ee452005-06-24 07:26:33 +0000508 bb_perror_msg("line %d: chown failed for %s", linenum, full_name_inc);
Eric Andersen611ec0e2005-06-09 11:11:10 +0000509 ret = EXIT_FAILURE;
510 }
Eric Andersen7b8aeae2005-07-18 20:06:49 +0000511 if ((mode != -1) && (chmod(full_name_inc, mode) < 0)){
512 bb_perror_msg("line %d: chmod failed for %s", linenum, full_name_inc);
513 ret = EXIT_FAILURE;
514 }
Eric Andersen611ec0e2005-06-09 11:11:10 +0000515 }
516 free(full_name_inc);
517 } else {
Bernhard Reutner-Fischer2c7da4d2007-01-21 14:36:25 +0000518 rdev = makedev(major, minor);
Eric Andersen611ec0e2005-06-09 11:11:10 +0000519 if (mknod(full_name, mode, rdev) == -1) {
Eric Anderseneb0ee452005-06-24 07:26:33 +0000520 bb_perror_msg("line %d: Couldnt create node %s", linenum, full_name);
Eric Andersen611ec0e2005-06-09 11:11:10 +0000521 ret = EXIT_FAILURE;
522 }
523 else if (chown(full_name, uid, gid) == -1) {
Eric Anderseneb0ee452005-06-24 07:26:33 +0000524 bb_perror_msg("line %d: chown failed for %s", linenum, full_name);
Eric Andersen611ec0e2005-06-09 11:11:10 +0000525 ret = EXIT_FAILURE;
526 }
Eric Andersen7b8aeae2005-07-18 20:06:49 +0000527 if ((mode != -1) && (chmod(full_name, mode) < 0)){
528 bb_perror_msg("line %d: chmod failed for %s", linenum, full_name);
529 ret = EXIT_FAILURE;
530 }
Eric Andersen611ec0e2005-06-09 11:11:10 +0000531 }
532 }
533loop:
534 free(line);
535 free(full_name);
536 }
537 fclose(table);
538
Thomas Petazzonibe5be0e2008-12-02 22:26:00 +0000539 if (system("/bin/sync"))
540 bb_error_msg("sync failed, continuing anyway");
Eric Andersenf3724ee2006-04-11 00:06:17 +0000541
Eric Andersen611ec0e2005-06-09 11:11:10 +0000542 return 0;
543}