fix copy'n paste error
[buildroot.git] / target / makedevs / makedevs.c
blob56c750ecf354280cfa78c3a5c1769d9705426334
1 /* 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.
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.
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.
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>
28 #include <ctype.h>
29 #include <errno.h>
30 #include <libgen.h>
31 #include <stdarg.h>
32 #include <sys/stat.h>
33 #include <sys/types.h>
34 #ifndef __APPLE__
35 #include <sys/sysmacros.h> /* major() and minor() */
36 #endif
38 const char *bb_applet_name;
40 void bb_verror_msg(const char *s, va_list p)
42 fflush(stdout);
43 fprintf(stderr, "%s: ", bb_applet_name);
44 vfprintf(stderr, s, p);
47 void bb_error_msg(const char *s, ...)
49 va_list p;
51 va_start(p, s);
52 bb_verror_msg(s, p);
53 va_end(p);
54 putc('\n', stderr);
57 void bb_error_msg_and_die(const char *s, ...)
59 va_list p;
61 va_start(p, s);
62 bb_verror_msg(s, p);
63 va_end(p);
64 putc('\n', stderr);
65 exit(1);
68 void bb_vperror_msg(const char *s, va_list p)
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));
77 void bb_perror_msg(const char *s, ...)
79 va_list p;
81 va_start(p, s);
82 bb_vperror_msg(s, p);
83 va_end(p);
86 void bb_perror_msg_and_die(const char *s, ...)
88 va_list p;
90 va_start(p, s);
91 bb_vperror_msg(s, p);
92 va_end(p);
93 exit(1);
96 FILE *bb_xfopen(const char *path, const char *mode)
98 FILE *fp;
99 if ((fp = fopen(path, mode)) == NULL)
100 bb_perror_msg_and_die("%s", path);
101 return fp;
104 enum {
105 FILEUTILS_PRESERVE_STATUS = 1,
106 FILEUTILS_DEREFERENCE = 2,
107 FILEUTILS_RECUR = 4,
108 FILEUTILS_FORCE = 8,
109 FILEUTILS_INTERACTIVE = 16
111 int bb_make_directory (char *path, long mode, int flags)
113 mode_t mask;
114 const char *fail_msg;
115 char *s = path;
116 char c;
117 struct stat st;
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);
129 do {
130 c = 0;
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;
143 ++s;
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.*/
150 if ((errno != EEXIST && errno != EISDIR)
151 || !(flags & FILEUTILS_RECUR)
152 || (stat(path, &st) < 0 || !S_ISDIR(st.st_mode))) {
153 fail_msg = "create";
154 umask(mask);
155 break;
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;
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;
175 return 0;
178 /* Remove any inserted nul from the path (recursive mode). */
179 *s = c;
181 } while (1);
183 bb_perror_msg ("Cannot %s directory `%s'", fail_msg, path);
184 return -1;
187 const char * const bb_msg_memory_exhausted = "memory exhausted";
189 void *xmalloc(size_t size)
191 void *ptr = malloc(size);
192 if (ptr == NULL && size != 0)
193 bb_error_msg_and_die(bb_msg_memory_exhausted);
194 return ptr;
197 void *xcalloc(size_t nmemb, size_t size)
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;
205 void *xrealloc(void *ptr, size_t size)
207 ptr = realloc(ptr, size);
208 if (ptr == NULL && size != 0)
209 bb_error_msg_and_die(bb_msg_memory_exhausted);
210 return ptr;
213 char *private_get_line_from_file(FILE *file, int c)
215 #define GROWBY (80) /* how large we will grow strings by */
217 int ch;
218 int idx = 0;
219 char *linebuf = NULL;
220 int linebufsz = 0;
222 while ((ch = getc(file)) != EOF) {
223 /* grow the line buffer as necessary */
224 if (idx > linebufsz - 2) {
225 linebuf = xrealloc(linebuf, linebufsz += GROWBY);
227 linebuf[idx++] = (char)ch;
228 if (!ch) return linebuf;
229 if (c<2 && ch == '\n') {
230 if (c) {
231 --idx;
233 break;
236 if (linebuf) {
237 if (ferror(file)) {
238 free(linebuf);
239 return NULL;
241 linebuf[idx] = 0;
243 return linebuf;
246 char *bb_get_chomped_line_from_file(FILE *file)
248 return private_get_line_from_file(file, 1);
251 long my_getpwnam(const char *name)
253 struct passwd *myuser;
255 myuser = getpwnam(name);
256 if (myuser==NULL)
257 bb_error_msg_and_die("unknown user name: %s", name);
259 return myuser->pw_uid;
262 long my_getgrnam(const char *name)
264 struct group *mygroup;
266 mygroup = getgrnam(name);
267 if (mygroup==NULL)
268 bb_error_msg_and_die("unknown group name: %s", name);
270 return (mygroup->gr_gid);
273 unsigned long get_ug_id(const char *s, long (*my_getxxnam)(const char *))
275 unsigned long r;
276 char *p;
278 r = strtoul(s, &p, 10);
279 if (*p || (s == p)) {
280 r = my_getxxnam(s);
283 return r;
286 char * last_char_is(const char *s, int c)
288 char *sret = (char *)s;
289 if (sret) {
290 sret = strrchr(sret, c);
291 if(sret != NULL && *(sret+1) != 0)
292 sret = NULL;
294 return sret;
297 void bb_xasprintf(char **string_ptr, const char *format, ...)
299 va_list p;
300 int r;
302 va_start(p, format);
303 r = vasprintf(string_ptr, format, p);
304 va_end(p);
306 if (r < 0) {
307 bb_perror_msg_and_die("bb_xasprintf");
311 char *concat_path_file(const char *path, const char *filename)
313 char *outbuf;
314 char *lc;
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);
323 return outbuf;
326 void bb_show_usage(void)
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");
348 fprintf(stderr, "/dev/hda b 640 0 0 3 1 1 1 15\n\n");
349 fprintf(stderr, "Will Produce:\n");
350 fprintf(stderr, "/dev\n");
351 fprintf(stderr, "/dev/console\n");
352 fprintf(stderr, "/dev/null\n");
353 fprintf(stderr, "/dev/zero\n");
354 fprintf(stderr, "/dev/hda\n");
355 fprintf(stderr, "/dev/hda[0-15]\n");
356 exit(1);
359 int main(int argc, char **argv)
361 int opt;
362 FILE *table = stdin;
363 char *rootdir = NULL;
364 char *line = NULL;
365 int linenum = 0;
366 int ret = EXIT_SUCCESS;
368 bb_applet_name = basename(argv[0]);
370 while ((opt = getopt(argc, argv, "d:")) != -1) {
371 switch(opt) {
372 case 'd':
373 table = bb_xfopen((line=optarg), "r");
374 break;
375 default:
376 bb_show_usage();
380 if (optind >= argc || (rootdir=argv[optind])==NULL) {
381 bb_error_msg_and_die("root directory not speficied");
384 if (chdir(rootdir) != 0) {
385 bb_perror_msg_and_die("Couldnt chdir to %s", rootdir);
388 umask(0);
390 printf("rootdir=%s\n", rootdir);
391 if (line) {
392 printf("table='%s'\n", line);
393 } else {
394 printf("table=<stdin>\n");
397 while ((line = bb_get_chomped_line_from_file(table))) {
398 char type;
399 unsigned int mode = 0755;
400 unsigned int major = 0;
401 unsigned int minor = 0;
402 unsigned int count = 0;
403 unsigned int increment = 0;
404 unsigned int start = 0;
405 char name[41];
406 char user[41];
407 char group[41];
408 char *full_name;
409 uid_t uid;
410 gid_t gid;
412 linenum++;
414 if ((2 > sscanf(line, "%40s %c %o %40s %40s %u %u %u %u %u", name,
415 &type, &mode, user, group, &major,
416 &minor, &start, &increment, &count)) ||
417 ((major | minor | start | count | increment) > 255))
419 if (*line=='\0' || *line=='#' || isspace(*line))
420 continue;
421 bb_error_msg("line %d invalid: '%s'\n", linenum, line);
422 ret = EXIT_FAILURE;
423 continue;
425 if (name[0] == '#') {
426 continue;
428 if (*group) {
429 gid = get_ug_id(group, my_getgrnam);
430 } else {
431 gid = getgid();
433 if (*user) {
434 uid = get_ug_id(user, my_getpwnam);
435 } else {
436 uid = getuid();
438 full_name = concat_path_file(rootdir, name);
440 if (type == 'd') {
441 bb_make_directory(full_name, mode | S_IFDIR, FILEUTILS_RECUR);
442 if (chown(full_name, uid, gid) == -1) {
443 bb_perror_msg("line %d: chown failed for %s", linenum, full_name);
444 ret = EXIT_FAILURE;
445 goto loop;
447 if ((mode != -1) && (chmod(full_name, mode) < 0)){
448 bb_perror_msg("line %d: chmod failed for %s", linenum, full_name);
449 ret = EXIT_FAILURE;
450 goto loop;
452 } else if (type == 'f') {
453 struct stat st;
454 if ((stat(full_name, &st) < 0 || !S_ISREG(st.st_mode))) {
455 bb_perror_msg("line %d: regular file '%s' does not exist", linenum, full_name);
456 ret = EXIT_FAILURE;
457 goto loop;
459 if (chown(full_name, uid, gid) == -1) {
460 bb_perror_msg("line %d: chown failed for %s", linenum, full_name);
461 ret = EXIT_FAILURE;
462 goto loop;
464 if ((mode != -1) && (chmod(full_name, mode) < 0)){
465 bb_perror_msg("line %d: chmod failed for %s", linenum, full_name);
466 ret = EXIT_FAILURE;
467 goto loop;
469 } else
471 dev_t rdev;
473 if (type == 'p') {
474 mode |= S_IFIFO;
476 else if (type == 'c') {
477 mode |= S_IFCHR;
479 else if (type == 'b') {
480 mode |= S_IFBLK;
481 } else {
482 bb_error_msg("line %d: Unsupported file type %c", linenum, type);
483 ret = EXIT_FAILURE;
484 goto loop;
487 if (count > 0) {
488 int i;
489 char *full_name_inc;
491 full_name_inc = xmalloc(strlen(full_name) + 4);
492 for (i = start; i < count; i++) {
493 sprintf(full_name_inc, "%s%d", full_name, i);
494 rdev = makedev(major, minor + (i * increment - start));
495 if (mknod(full_name_inc, mode, rdev) == -1) {
496 bb_perror_msg("line %d: Couldnt create node %s", linenum, full_name_inc);
497 ret = EXIT_FAILURE;
499 else if (chown(full_name_inc, uid, gid) == -1) {
500 bb_perror_msg("line %d: chown failed for %s", linenum, full_name_inc);
501 ret = EXIT_FAILURE;
503 if ((mode != -1) && (chmod(full_name_inc, mode) < 0)){
504 bb_perror_msg("line %d: chmod failed for %s", linenum, full_name_inc);
505 ret = EXIT_FAILURE;
508 free(full_name_inc);
509 } else {
510 rdev = makedev(major, minor);
511 if (mknod(full_name, mode, rdev) == -1) {
512 bb_perror_msg("line %d: Couldnt create node %s", linenum, full_name);
513 ret = EXIT_FAILURE;
515 else if (chown(full_name, uid, gid) == -1) {
516 bb_perror_msg("line %d: chown failed for %s", linenum, full_name);
517 ret = EXIT_FAILURE;
519 if ((mode != -1) && (chmod(full_name, mode) < 0)){
520 bb_perror_msg("line %d: chmod failed for %s", linenum, full_name);
521 ret = EXIT_FAILURE;
525 loop:
526 free(line);
527 free(full_name);
529 fclose(table);
531 sync();
532 return 0;