1 /* vi: set sw=4 ts=4: */
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.
33 #include <sys/types.h>
35 #include <sys/sysmacros.h> /* major() and minor() */
38 #ifdef EXTENDED_ATTRIBUTES
39 #include <sys/capability.h>
40 #endif /* EXTENDED_ATTRIBUTES */
42 const char *bb_applet_name
;
45 unsigned int recursive_mode
;
46 #define PASSWD_PATH "etc/passwd" /* MUST be relative */
47 #define GROUP_PATH "etc/group" /* MUST be relative */
49 void bb_verror_msg(const char *s
, va_list p
)
52 fprintf(stderr
, "%s: ", bb_applet_name
);
53 vfprintf(stderr
, s
, p
);
56 void bb_error_msg(const char *s
, ...)
66 void bb_error_msg_and_die(const char *s
, ...)
77 void bb_vperror_msg(const char *s
, va_list p
)
83 fprintf(stderr
, "%s%s\n", s
, strerror(err
));
86 void bb_perror_msg(const char *s
, ...)
95 void bb_perror_msg_and_die(const char *s
, ...)
100 bb_vperror_msg(s
, p
);
105 FILE *bb_xfopen(const char *path
, const char *mode
)
108 if ((fp
= fopen(path
, mode
)) == NULL
)
109 bb_perror_msg_and_die("%s", path
);
114 FILEUTILS_PRESERVE_STATUS
= 1,
115 FILEUTILS_DEREFERENCE
= 2,
118 FILEUTILS_INTERACTIVE
= 16
120 int bb_make_directory (char *path
, long mode
, int flags
)
123 const char *fail_msg
;
131 mode
= (S_IXUSR
| S_IXGRP
| S_IXOTH
|
132 S_IWUSR
| S_IWGRP
| S_IWOTH
|
133 S_IRUSR
| S_IRGRP
| S_IROTH
) & ~mask
;
141 if (flags
& FILEUTILS_RECUR
) { /* Get the parent. */
142 /* Bypass leading non-'/'s and then subsequent '/'s. */
148 c
= *s
; /* Save the current char */
149 *s
= 0; /* and replace it with nul. */
156 if (mkdir(path
, 0777) < 0) {
157 /* If we failed for any other reason than the directory
158 * already exists, output a diagnostic and return -1.*/
159 if ((errno
!= EEXIST
&& errno
!= EISDIR
)
160 || !(flags
& FILEUTILS_RECUR
)
161 || (stat(path
, &st
) < 0 || !S_ISDIR(st
.st_mode
))) {
166 /* Since the directory exists, don't attempt to change
167 * permissions if it was the full target. Note that
168 * this is not an error conditon. */
176 /* Done. If necessary, updated perms on the newly
177 * created directory. Failure to update here _is_
180 if ((mode
!= -1) && (chmod(path
, mode
) < 0)){
181 fail_msg
= "set permissions of";
187 /* Remove any inserted nul from the path (recursive mode). */
192 bb_perror_msg ("Cannot %s directory `%s'", fail_msg
, path
);
196 const char * const bb_msg_memory_exhausted
= "memory exhausted";
198 void *xmalloc(size_t size
)
200 void *ptr
= malloc(size
);
201 if (ptr
== NULL
&& size
!= 0)
202 bb_error_msg_and_die(bb_msg_memory_exhausted
);
206 void *xcalloc(size_t nmemb
, size_t size
)
208 void *ptr
= calloc(nmemb
, size
);
209 if (ptr
== NULL
&& nmemb
!= 0 && size
!= 0)
210 bb_error_msg_and_die(bb_msg_memory_exhausted
);
214 void *xrealloc(void *ptr
, size_t size
)
216 ptr
= realloc(ptr
, size
);
217 if (ptr
== NULL
&& size
!= 0)
218 bb_error_msg_and_die(bb_msg_memory_exhausted
);
222 char *private_get_line_from_file(FILE *file
, int c
)
224 #define GROWBY (80) /* how large we will grow strings by */
228 char *linebuf
= NULL
;
231 while ((ch
= getc(file
)) != EOF
) {
232 /* grow the line buffer as necessary */
233 if (idx
> linebufsz
- 2) {
234 linebuf
= xrealloc(linebuf
, linebufsz
+= GROWBY
);
236 linebuf
[idx
++] = (char)ch
;
237 if (!ch
) return linebuf
;
238 if (c
<2 && ch
== '\n') {
255 char *bb_get_chomped_line_from_file(FILE *file
)
257 return private_get_line_from_file(file
, 1);
260 long my_getpwnam(const char *name
)
262 struct passwd
*myuser
;
265 stream
= bb_xfopen(PASSWD_PATH
, "r");
268 myuser
= fgetpwent(stream
);
270 bb_error_msg_and_die("unknown user name: %s", name
);
272 bb_perror_msg_and_die("fgetpwent");
273 if (!strcmp(name
, myuser
->pw_name
))
278 return myuser
->pw_uid
;
281 long my_getgrnam(const char *name
)
283 struct group
*mygroup
;
286 stream
= bb_xfopen(GROUP_PATH
, "r");
289 mygroup
= fgetgrent(stream
);
291 bb_error_msg_and_die("unknown group name: %s", name
);
293 bb_perror_msg_and_die("fgetgrent");
294 if (!strcmp(name
, mygroup
->gr_name
))
299 return mygroup
->gr_gid
;
302 unsigned long get_ug_id(const char *s
, long (*my_getxxnam
)(const char *))
307 r
= strtoul(s
, &p
, 10);
308 if (*p
|| (s
== p
)) {
315 char * last_char_is(const char *s
, int c
)
317 char *sret
= (char *)s
;
319 sret
= strrchr(sret
, c
);
320 if(sret
!= NULL
&& *(sret
+1) != 0)
326 void bb_xasprintf(char **string_ptr
, const char *format
, ...)
332 r
= vasprintf(string_ptr
, format
, p
);
336 bb_perror_msg_and_die("bb_xasprintf");
340 char *concat_path_file(const char *path
, const char *filename
)
347 lc
= last_char_is(path
, '/');
348 while (*filename
== '/')
350 bb_xasprintf(&outbuf
, "%s%s%s", path
, (lc
==NULL
? "/" : ""), filename
);
355 #ifdef EXTENDED_ATTRIBUTES
356 int bb_set_xattr(const char *fpath
, const char *xattr
)
358 cap_t cap
, cap_file
, cap_new
;
359 char *cap_file_text
, *cap_new_text
;
362 cap
= cap_from_text(xattr
);
364 bb_perror_msg_and_die("cap_from_text failed for %s", xattr
);
366 cap_file
= cap_get_file(fpath
);
367 if (cap_file
== NULL
) {
368 /* if no capability was set before, we initialize cap_file */
369 if (errno
!= ENODATA
)
370 bb_perror_msg_and_die("cap_get_file failed on %s", fpath
);
372 cap_file
= cap_init();
374 bb_perror_msg_and_die("cap_init failed");
377 if ((cap_file_text
= cap_to_text(cap_file
, &length
)) == NULL
)
378 bb_perror_msg_and_die("cap_to_name failed on %s", fpath
);
380 bb_xasprintf(&cap_new_text
, "%s %s", cap_file_text
, xattr
);
382 if ((cap_new
= cap_from_text(cap_new_text
)) == NULL
)
383 bb_perror_msg_and_die("cap_from_text failed on %s", cap_new_text
);
385 if (cap_set_file(fpath
, cap_new
) == -1)
386 bb_perror_msg_and_die("cap_set_file failed for %s (xattr = %s)", fpath
, xattr
);
390 cap_free(cap_file_text
);
392 cap_free(cap_new_text
);
396 #endif /* EXTENDED_ATTRIBUTES */
398 void bb_show_usage(void)
400 fprintf(stderr
, "%s: [-d device_table] rootdir\n\n", bb_applet_name
);
401 fprintf(stderr
, "Creates a batch of special files as specified in a device table.\n");
402 fprintf(stderr
, "Device table entries take the form of:\n");
403 fprintf(stderr
, "name type mode user group major minor start increment count\n\n");
404 fprintf(stderr
, "Where name is the file name, type can be one of:\n");
405 fprintf(stderr
, " f A regular file\n");
406 fprintf(stderr
, " d Directory\n");
407 fprintf(stderr
, " r Directory recursively\n");
408 fprintf(stderr
, " c Character special device file\n");
409 fprintf(stderr
, " b Block special device file\n");
410 fprintf(stderr
, " p Fifo (named pipe)\n");
411 fprintf(stderr
, "uid is the user id for the target file, gid is the group id for the\n");
412 fprintf(stderr
, "target file. The rest of the entries (major, minor, etc) apply to\n");
413 fprintf(stderr
, "to device special files. A '-' may be used for blank entries.\n\n");
414 fprintf(stderr
, "For example:\n");
415 fprintf(stderr
, "<name> <type> <mode> <uid> <gid> <major> <minor> <start> <inc> <count>\n");
416 fprintf(stderr
, "/dev d 755 0 0 - - - - -\n");
417 fprintf(stderr
, "/dev/console c 666 0 0 5 1 - - -\n");
418 fprintf(stderr
, "/dev/null c 666 0 0 1 3 0 0 -\n");
419 fprintf(stderr
, "/dev/zero c 666 0 0 1 5 0 0 -\n");
420 fprintf(stderr
, "/dev/hda b 640 0 0 3 0 0 0 -\n");
421 fprintf(stderr
, "/dev/hda b 640 0 0 3 1 1 1 15\n");
422 fprintf(stderr
, "/dev/rtp b 640 0 0 250 0 0 1 5\n");
423 fprintf(stderr
, "/dev/gps b 640 0 0 251 0 1 1 5\n");
424 fprintf(stderr
, "/dev/uio b 640 0 0 252 0 1 2 5\n");
425 fprintf(stderr
, "/dev/uio b 640 0 0 252 1 6 2 5\n\n");
426 fprintf(stderr
, "Will Produce:\n");
427 fprintf(stderr
, "/dev\n");
428 fprintf(stderr
, "/dev/console\n");
429 fprintf(stderr
, "/dev/null\n");
430 fprintf(stderr
, "/dev/zero\n");
431 fprintf(stderr
, "/dev/hda\n");
432 fprintf(stderr
, "/dev/hda[1-15] with minor numbers [1-15]\n");
433 fprintf(stderr
, "/dev/rtp[0-4] with minor numbers [0-4]\n");
434 fprintf(stderr
, "/dev/gps[1-5] with minor numbers [0-4]\n");
435 fprintf(stderr
, "/dev/uio[1-5] with minor numbers 0,2,4,6,8\n");
436 fprintf(stderr
, "/dev/uio[6-10] with minor numbers 1,3,5,7,9\n");
440 int bb_recursive(const char *fpath
, const struct stat
*sb
,
441 int tflag
, struct FTW
*ftwbuf
){
443 if (chown(fpath
, recursive_uid
, recursive_gid
) == -1) {
444 bb_perror_msg("chown failed for %s", fpath
);
447 if (recursive_mode
!= -1) {
448 if (chmod(fpath
, recursive_mode
) < 0) {
449 bb_perror_msg("chmod failed for %s", fpath
);
457 int main(int argc
, char **argv
)
461 char *rootdir
= NULL
;
462 char *full_name
= NULL
;
465 int ret
= EXIT_SUCCESS
;
467 bb_applet_name
= basename(argv
[0]);
469 while ((opt
= getopt(argc
, argv
, "d:")) != -1) {
472 table
= bb_xfopen((line
=optarg
), "r");
479 if (optind
>= argc
|| (rootdir
=argv
[optind
])==NULL
) {
480 bb_error_msg_and_die("root directory not speficied");
483 if (chdir(rootdir
) != 0) {
484 bb_perror_msg_and_die("Couldnt chdir to %s", rootdir
);
489 printf("rootdir=%s\n", rootdir
);
491 printf("table='%s'\n", line
);
493 printf("table=<stdin>\n");
496 while ((line
= bb_get_chomped_line_from_file(table
))) {
498 unsigned int mode
= 0755;
499 unsigned int major
= 0;
500 unsigned int minor
= 0;
501 unsigned int count
= 0;
502 unsigned int increment
= 0;
503 unsigned int start
= 0;
513 if (1 == sscanf(line
, "|xattr %254s", xattr
)) {
514 #ifdef EXTENDED_ATTRIBUTES
516 bb_error_msg_and_die("line %d should be after a file\n", linenum
);
518 if (bb_set_xattr(full_name
, xattr
) < 0)
519 bb_error_msg_and_die("can't set cap %s on file %s\n", xattr
, full_name
);
521 bb_error_msg_and_die("line %d not supported: '%s'\nDid you forget to enable "
522 "BR2_ROOTFS_DEVICE_TABLE_SUPPORTS_EXTENDED_ATTRIBUTES?\n",
524 #endif /* EXTENDED_ATTRIBUTES */
528 if ((2 > sscanf(line
, "%4095s %c %o %40s %40s %u %u %u %u %u", name
,
529 &type
, &mode
, user
, group
, &major
,
530 &minor
, &start
, &increment
, &count
)) ||
531 ((major
| minor
| start
| count
| increment
) > 0xfffff))
533 if (*line
=='\0' || *line
=='#' || isspace(*line
))
535 bb_error_msg("line %d invalid: '%s'\n", linenum
, line
);
539 if (name
[0] == '#') {
543 gid
= get_ug_id(group
, my_getgrnam
);
548 uid
= get_ug_id(user
, my_getpwnam
);
554 * free previous full name
555 * we don't de-allocate full_name at the end of the parsing,
556 * because we may need it if the next line is an xattr.
559 full_name
= concat_path_file(rootdir
, name
);
562 bb_make_directory(full_name
, mode
| S_IFDIR
, FILEUTILS_RECUR
);
563 if (chown(full_name
, uid
, gid
) == -1) {
564 bb_perror_msg("line %d: chown failed for %s", linenum
, full_name
);
568 if ((mode
!= -1) && (chmod(full_name
, mode
) < 0)){
569 bb_perror_msg("line %d: chmod failed for %s", linenum
, full_name
);
573 } else if (type
== 'f') {
575 if ((stat(full_name
, &st
) < 0 || !S_ISREG(st
.st_mode
))) {
576 bb_perror_msg("line %d: regular file '%s' does not exist", linenum
, full_name
);
580 if (chown(full_name
, uid
, gid
) == -1) {
581 bb_perror_msg("line %d: chown failed for %s", linenum
, full_name
);
585 if ((mode
!= -1) && (chmod(full_name
, mode
) < 0)){
586 bb_perror_msg("line %d: chmod failed for %s", linenum
, full_name
);
590 } else if (type
== 'r') {
593 recursive_mode
= mode
;
594 if (nftw(full_name
, bb_recursive
, 20, FTW_MOUNT
| FTW_PHYS
) < 0) {
595 bb_perror_msg("line %d: recursive failed for %s", linenum
, full_name
);
608 else if (type
== 'c') {
611 else if (type
== 'b') {
614 bb_error_msg("line %d: Unsupported file type %c", linenum
, type
);
619 full_name_inc
= xmalloc(strlen(full_name
) + sizeof(int)*3 + 2);
622 for (i
= start
; i
<= start
+ count
; i
++) {
623 sprintf(full_name_inc
, count
? "%s%u" : "%s", full_name
, i
);
624 rdev
= makedev(major
, minor
+ (i
- start
) * increment
);
625 if (mknod(full_name_inc
, mode
, rdev
) < 0) {
626 bb_perror_msg("line %d: can't create node %s", linenum
, full_name_inc
);
628 } else if (chown(full_name_inc
, uid
, gid
) < 0) {
629 bb_perror_msg("line %d: can't chown %s", linenum
, full_name_inc
);
631 } else if (chmod(full_name_inc
, mode
) < 0) {
632 bb_perror_msg("line %d: can't chmod %s", linenum
, full_name_inc
);