53ff6fef0ec4dbc30b1b9913c8a56275259f9d90
[packages/trusty/cirros-testvm.git] / cirros-testvm / src-cirros / buildroot-2015.05 / package / makedevs / makedevs.c
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.
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>
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
37 #include <ftw.h>
38
39 const char *bb_applet_name;
40 uid_t recursive_uid;
41 gid_t recursive_gid;
42 unsigned int recursive_mode;
43
44 void bb_verror_msg(const char *s, va_list p)
45 {
46         fflush(stdout);
47         fprintf(stderr, "%s: ", bb_applet_name);
48         vfprintf(stderr, s, p);
49 }
50
51 void bb_error_msg(const char *s, ...)
52 {
53         va_list p;
54
55         va_start(p, s);
56         bb_verror_msg(s, p);
57         va_end(p);
58         putc('\n', stderr);
59 }
60
61 void bb_error_msg_and_die(const char *s, ...)
62 {
63         va_list p;
64
65         va_start(p, s);
66         bb_verror_msg(s, p);
67         va_end(p);
68         putc('\n', stderr);
69         exit(1);
70 }
71
72 void bb_vperror_msg(const char *s, va_list p)
73 {
74         int err=errno;
75         if(s == 0) s = "";
76         bb_verror_msg(s, p);
77         if (*s) s = ": ";
78         fprintf(stderr, "%s%s\n", s, strerror(err));
79 }
80
81 void bb_perror_msg(const char *s, ...)
82 {
83         va_list p;
84
85         va_start(p, s);
86         bb_vperror_msg(s, p);
87         va_end(p);
88 }
89
90 void bb_perror_msg_and_die(const char *s, ...)
91 {
92         va_list p;
93
94         va_start(p, s);
95         bb_vperror_msg(s, p);
96         va_end(p);
97         exit(1);
98 }
99
100 FILE *bb_xfopen(const char *path, const char *mode)
101 {
102         FILE *fp;
103         if ((fp = fopen(path, mode)) == NULL)
104                 bb_perror_msg_and_die("%s", path);
105         return fp;
106 }
107
108 enum {
109         FILEUTILS_PRESERVE_STATUS = 1,
110         FILEUTILS_DEREFERENCE = 2,
111         FILEUTILS_RECUR = 4,
112         FILEUTILS_FORCE = 8,
113         FILEUTILS_INTERACTIVE = 16
114 };
115 int bb_make_directory (char *path, long mode, int flags)
116 {
117         mode_t mask;
118         const char *fail_msg;
119         char *s = path;
120         char c;
121         struct stat st;
122
123         mask = umask(0);
124         if (mode == -1) {
125                 umask(mask);
126                 mode = (S_IXUSR | S_IXGRP | S_IXOTH |
127                                 S_IWUSR | S_IWGRP | S_IWOTH |
128                                 S_IRUSR | S_IRGRP | S_IROTH) & ~mask;
129         } else {
130                 umask(mask & ~0300);
131         }
132
133         do {
134                 c = 0;
135
136                 if (flags & FILEUTILS_RECUR) {  /* Get the parent. */
137                         /* Bypass leading non-'/'s and then subsequent '/'s. */
138                         while (*s) {
139                                 if (*s == '/') {
140                                         do {
141                                                 ++s;
142                                         } while (*s == '/');
143                                         c = *s;         /* Save the current char */
144                                         *s = 0;         /* and replace it with nul. */
145                                         break;
146                                 }
147                                 ++s;
148                         }
149                 }
150
151                 if (mkdir(path, 0777) < 0) {
152                         /* If we failed for any other reason than the directory
153                          * already exists, output a diagnostic and return -1.*/
154                         if ((errno != EEXIST && errno != EISDIR)
155                                         || !(flags & FILEUTILS_RECUR)
156                                         || (stat(path, &st) < 0 || !S_ISDIR(st.st_mode))) {
157                                 fail_msg = "create";
158                                 umask(mask);
159                                 break;
160                         }
161                         /* Since the directory exists, don't attempt to change
162                          * permissions if it was the full target.  Note that
163                          * this is not an error conditon. */
164                         if (!c) {
165                                 umask(mask);
166                                 return 0;
167                         }
168                 }
169
170                 if (!c) {
171                         /* Done.  If necessary, updated perms on the newly
172                          * created directory.  Failure to update here _is_
173                          * an error.*/
174                         umask(mask);
175                         if ((mode != -1) && (chmod(path, mode) < 0)){
176                                 fail_msg = "set permissions of";
177                                 break;
178                         }
179                         return 0;
180                 }
181
182                 /* Remove any inserted nul from the path (recursive mode). */
183                 *s = c;
184
185         } while (1);
186
187         bb_perror_msg ("Cannot %s directory `%s'", fail_msg, path);
188         return -1;
189 }
190
191 const char * const bb_msg_memory_exhausted = "memory exhausted";
192
193 void *xmalloc(size_t size)
194 {
195         void *ptr = malloc(size);
196         if (ptr == NULL && size != 0)
197                 bb_error_msg_and_die(bb_msg_memory_exhausted);
198         return ptr;
199 }
200
201 void *xcalloc(size_t nmemb, size_t size)
202 {
203         void *ptr = calloc(nmemb, size);
204         if (ptr == NULL && nmemb != 0 && size != 0)
205                 bb_error_msg_and_die(bb_msg_memory_exhausted);
206         return ptr;
207 }
208
209 void *xrealloc(void *ptr, size_t size)
210 {
211         ptr = realloc(ptr, size);
212         if (ptr == NULL && size != 0)
213                 bb_error_msg_and_die(bb_msg_memory_exhausted);
214         return ptr;
215 }
216
217 char *private_get_line_from_file(FILE *file, int c)
218 {
219 #define GROWBY (80)             /* how large we will grow strings by */
220
221         int ch;
222         int idx = 0;
223         char *linebuf = NULL;
224         int linebufsz = 0;
225
226         while ((ch = getc(file)) != EOF) {
227                 /* grow the line buffer as necessary */
228                 if (idx > linebufsz - 2) {
229                         linebuf = xrealloc(linebuf, linebufsz += GROWBY);
230                 }
231                 linebuf[idx++] = (char)ch;
232                 if (!ch) return linebuf;
233                 if (c<2 && ch == '\n') {
234                         if (c) {
235                                 --idx;
236                         }
237                         break;
238                 }
239         }
240         if (linebuf) {
241                 if (ferror(file)) {
242                         free(linebuf);
243                         return NULL;
244                 }
245                 linebuf[idx] = 0;
246         }
247         return linebuf;
248 }
249
250 char *bb_get_chomped_line_from_file(FILE *file)
251 {
252         return private_get_line_from_file(file, 1);
253 }
254
255 long my_getpwnam(const char *name)
256 {
257         struct passwd *myuser;
258
259         myuser  = getpwnam(name);
260         if (myuser==NULL)
261                 bb_error_msg_and_die("unknown user name: %s", name);
262
263         return myuser->pw_uid;
264 }
265
266 long my_getgrnam(const char *name)
267 {
268         struct group *mygroup;
269
270         mygroup  = getgrnam(name);
271         if (mygroup==NULL)
272                 bb_error_msg_and_die("unknown group name: %s", name);
273
274         return (mygroup->gr_gid);
275 }
276
277 unsigned long get_ug_id(const char *s, long (*my_getxxnam)(const char *))
278 {
279         unsigned long r;
280         char *p;
281
282         r = strtoul(s, &p, 10);
283         if (*p || (s == p)) {
284                 r = my_getxxnam(s);
285         }
286
287         return r;
288 }
289
290 char * last_char_is(const char *s, int c)
291 {
292         char *sret = (char *)s;
293         if (sret) {
294                 sret = strrchr(sret, c);
295                 if(sret != NULL && *(sret+1) != 0)
296                         sret = NULL;
297         }
298         return sret;
299 }
300
301 void bb_xasprintf(char **string_ptr, const char *format, ...)
302 {
303         va_list p;
304         int r;
305
306         va_start(p, format);
307         r = vasprintf(string_ptr, format, p);
308         va_end(p);
309
310         if (r < 0) {
311                 bb_perror_msg_and_die("bb_xasprintf");
312         }
313 }
314
315 char *concat_path_file(const char *path, const char *filename)
316 {
317         char *outbuf;
318         char *lc;
319
320         if (!path)
321                 path = "";
322         lc = last_char_is(path, '/');
323         while (*filename == '/')
324                 filename++;
325         bb_xasprintf(&outbuf, "%s%s%s", path, (lc==NULL ? "/" : ""), filename);
326
327         return outbuf;
328 }
329
330 void bb_show_usage(void)
331 {
332         fprintf(stderr, "%s: [-d device_table] rootdir\n\n", bb_applet_name);
333         fprintf(stderr, "Creates a batch of special files as specified in a device table.\n");
334         fprintf(stderr, "Device table entries take the form of:\n");
335         fprintf(stderr, "name type mode user group major minor start increment count\n\n");
336         fprintf(stderr, "Where name is the file name,  type can be one of:\n");
337         fprintf(stderr, "      f       A regular file\n");
338         fprintf(stderr, "      d       Directory\n");
339         fprintf(stderr, "      r       Directory recursively\n");
340         fprintf(stderr, "      c       Character special device file\n");
341         fprintf(stderr, "      b       Block special device file\n");
342         fprintf(stderr, "      p       Fifo (named pipe)\n");
343         fprintf(stderr, "uid is the user id for the target file, gid is the group id for the\n");
344         fprintf(stderr, "target file.  The rest of the entries (major, minor, etc) apply to\n");
345         fprintf(stderr, "to device special files.  A '-' may be used for blank entries.\n\n");
346         fprintf(stderr, "For example:\n");
347         fprintf(stderr, "<name>    <type> <mode> <uid> <gid> <major> <minor> <start> <inc> <count>\n");
348         fprintf(stderr, "/dev         d    755    0    0     -       -       -       -     -\n");
349         fprintf(stderr, "/dev/console c    666    0    0     5       1       -       -     -\n");
350         fprintf(stderr, "/dev/null    c    666    0    0     1       3       0       0     -\n");
351         fprintf(stderr, "/dev/zero    c    666    0    0     1       5       0       0     -\n");
352         fprintf(stderr, "/dev/hda     b    640    0    0     3       0       0       0     -\n");
353         fprintf(stderr, "/dev/hda     b    640    0    0     3       1       1       1     15\n");
354         fprintf(stderr, "/dev/rtp     b    640    0    0     250     0       0       1     5\n");
355         fprintf(stderr, "/dev/gps     b    640    0    0     251     0       1       1     5\n");
356         fprintf(stderr, "/dev/uio     b    640    0    0     252     0       1       2     5\n");
357         fprintf(stderr, "/dev/uio     b    640    0    0     252     1       6       2     5\n\n");
358         fprintf(stderr, "Will Produce:\n");
359         fprintf(stderr, "/dev\n");
360         fprintf(stderr, "/dev/console\n");
361         fprintf(stderr, "/dev/null\n");
362         fprintf(stderr, "/dev/zero\n");
363         fprintf(stderr, "/dev/hda\n");
364         fprintf(stderr, "/dev/hda[1-15] with minor numbers [1-15]\n");
365         fprintf(stderr, "/dev/rtp[0-4]  with minor numbers [0-4]\n");
366         fprintf(stderr, "/dev/gps[1-5]  with minor numbers [0-4]\n");
367         fprintf(stderr, "/dev/uio[1-5]  with minor numbers 0,2,4,6,8\n");
368         fprintf(stderr, "/dev/uio[6-10] with minor numbers 1,3,5,7,9\n");
369         exit(1);
370 }
371
372 int bb_recursive(const char *fpath, const struct stat *sb,
373                 int tflag, struct FTW *ftwbuf){
374
375         if (chown(fpath, recursive_uid, recursive_gid) == -1) {
376                 bb_perror_msg("chown failed for %s", fpath);
377                 return -1;
378         }
379         if (recursive_mode != -1) {
380                 if (chmod(fpath, recursive_mode) < 0) {
381                         bb_perror_msg("chmod failed for %s", fpath);
382                         return -1;
383                 }
384         }
385
386         return 0;
387 }
388
389 int main(int argc, char **argv)
390 {
391         int opt;
392         FILE *table = stdin;
393         char *rootdir = NULL;
394         char *line = NULL;
395         int linenum = 0;
396         int ret = EXIT_SUCCESS;
397
398         bb_applet_name = basename(argv[0]);
399
400         while ((opt = getopt(argc, argv, "d:")) != -1) {
401                 switch(opt) {
402                         case 'd':
403                                 table = bb_xfopen((line=optarg), "r");
404                                 break;
405                         default:
406                                 bb_show_usage();
407                 }
408         }
409
410         if (optind >= argc || (rootdir=argv[optind])==NULL) {
411                 bb_error_msg_and_die("root directory not speficied");
412         }
413
414         if (chdir(rootdir) != 0) {
415                 bb_perror_msg_and_die("Couldnt chdir to %s", rootdir);
416         }
417
418         umask(0);
419
420         printf("rootdir=%s\n", rootdir);
421         if (line) {
422                 printf("table='%s'\n", line);
423         } else {
424                 printf("table=<stdin>\n");
425         }
426
427         while ((line = bb_get_chomped_line_from_file(table))) {
428                 char type;
429                 unsigned int mode = 0755;
430                 unsigned int major = 0;
431                 unsigned int minor = 0;
432                 unsigned int count = 0;
433                 unsigned int increment = 0;
434                 unsigned int start = 0;
435                 char name[4096];
436                 char user[41];
437                 char group[41];
438                 char *full_name;
439                 uid_t uid;
440                 gid_t gid;
441
442                 linenum++;
443
444                 if ((2 > sscanf(line, "%4095s %c %o %40s %40s %u %u %u %u %u", name,
445                                                 &type, &mode, user, group, &major,
446                                                 &minor, &start, &increment, &count)) ||
447                                 ((major | minor | start | count | increment) > 0xfffff))
448                 {
449                         if (*line=='\0' || *line=='#' || isspace(*line))
450                                 continue;
451                         bb_error_msg("line %d invalid: '%s'\n", linenum, line);
452                         ret = EXIT_FAILURE;
453                         continue;
454                 }
455                 if (name[0] == '#') {
456                         continue;
457                 }
458                 if (*group) {
459                         gid = get_ug_id(group, my_getgrnam);
460                 } else {
461                         gid = getgid();
462                 }
463                 if (*user) {
464                         uid = get_ug_id(user, my_getpwnam);
465                 } else {
466                         uid = getuid();
467                 }
468                 full_name = concat_path_file(rootdir, name);
469
470                 if (type == 'd') {
471                         bb_make_directory(full_name, mode | S_IFDIR, FILEUTILS_RECUR);
472                         if (chown(full_name, uid, gid) == -1) {
473                                 bb_perror_msg("line %d: chown failed for %s", linenum, full_name);
474                                 ret = EXIT_FAILURE;
475                                 goto loop;
476                         }
477                         if ((mode != -1) && (chmod(full_name, mode) < 0)){
478                                 bb_perror_msg("line %d: chmod failed for %s", linenum, full_name);
479                                 ret = EXIT_FAILURE;
480                                 goto loop;
481                         }
482                 } else if (type == 'f') {
483                         struct stat st;
484                         if ((stat(full_name, &st) < 0 || !S_ISREG(st.st_mode))) {
485                                 bb_perror_msg("line %d: regular file '%s' does not exist", linenum, full_name);
486                                 ret = EXIT_FAILURE;
487                                 goto loop;
488                         }
489                         if (chown(full_name, uid, gid) == -1) {
490                                 bb_perror_msg("line %d: chown failed for %s", linenum, full_name);
491                                 ret = EXIT_FAILURE;
492                                 goto loop;
493                         }
494                         if ((mode != -1) && (chmod(full_name, mode) < 0)){
495                                 bb_perror_msg("line %d: chmod failed for %s", linenum, full_name);
496                                 ret = EXIT_FAILURE;
497                                 goto loop;
498                         }
499                 } else if (type == 'r') {
500                         recursive_uid = uid;
501                         recursive_gid = gid;
502                         recursive_mode = mode;
503                         if (nftw(full_name, bb_recursive, 20, FTW_MOUNT | FTW_PHYS) < 0) {
504                                 bb_perror_msg("line %d: recursive failed for %s", linenum, full_name);
505                                 ret = EXIT_FAILURE;
506                                 goto loop;
507                         }
508                 } else
509                 {
510                         dev_t rdev;
511
512                         if (type == 'p') {
513                                 mode |= S_IFIFO;
514                         }
515                         else if (type == 'c') {
516                                 mode |= S_IFCHR;
517                         }
518                         else if (type == 'b') {
519                                 mode |= S_IFBLK;
520                         } else {
521                                 bb_error_msg("line %d: Unsupported file type %c", linenum, type);
522                                 ret = EXIT_FAILURE;
523                                 goto loop;
524                         }
525
526                         if (count > 0) {
527                                 int i;
528                                 char *full_name_inc;
529
530                                 full_name_inc = xmalloc(strlen(full_name) + 8);
531                                 for (i = 0; i < count; i++) {
532                                         sprintf(full_name_inc, "%s%d", full_name, start + i);
533                                         rdev = makedev(major, minor + i * increment);
534                                         if (mknod(full_name_inc, mode, rdev) == -1) {
535                                                 bb_perror_msg("line %d: Couldnt create node %s", linenum, full_name_inc);
536                                                 ret = EXIT_FAILURE;
537                                         }
538                                         else if (chown(full_name_inc, uid, gid) == -1) {
539                                                 bb_perror_msg("line %d: chown failed for %s", linenum, full_name_inc);
540                                                 ret = EXIT_FAILURE;
541                                         }
542                                         if ((mode != -1) && (chmod(full_name_inc, mode) < 0)){
543                                                 bb_perror_msg("line %d: chmod failed for %s", linenum, full_name_inc);
544                                                 ret = EXIT_FAILURE;
545                                         }
546                                 }
547                                 free(full_name_inc);
548                         } else {
549                                 rdev = makedev(major, minor);
550                                 if (mknod(full_name, mode, rdev) == -1) {
551                                         bb_perror_msg("line %d: Couldnt create node %s", linenum, full_name);
552                                         ret = EXIT_FAILURE;
553                                 }
554                                 else if (chown(full_name, uid, gid) == -1) {
555                                         bb_perror_msg("line %d: chown failed for %s", linenum, full_name);
556                                         ret = EXIT_FAILURE;
557                                 }
558                                 if ((mode != -1) && (chmod(full_name, mode) < 0)){
559                                         bb_perror_msg("line %d: chmod failed for %s", linenum, full_name);
560                                         ret = EXIT_FAILURE;
561                                 }
562                         }
563                 }
564 loop:
565                 free(line);
566                 free(full_name);
567         }
568         fclose(table);
569
570         return ret;
571 }