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 const char *bb_applet_name
;
40 void bb_verror_msg(const char *s
, va_list p
)
43 fprintf(stderr
, "%s: ", bb_applet_name
);
44 vfprintf(stderr
, s
, p
);
47 void bb_error_msg(const char *s
, ...)
57 void bb_error_msg_and_die(const char *s
, ...)
68 void bb_vperror_msg(const char *s
, va_list p
)
74 fprintf(stderr
, "%s%s\n", s
, strerror(err
));
77 void bb_perror_msg(const char *s
, ...)
86 void bb_perror_msg_and_die(const char *s
, ...)
96 FILE *bb_xfopen(const char *path
, const char *mode
)
99 if ((fp
= fopen(path
, mode
)) == NULL
)
100 bb_perror_msg_and_die("%s", path
);
105 FILEUTILS_PRESERVE_STATUS
= 1,
106 FILEUTILS_DEREFERENCE
= 2,
109 FILEUTILS_INTERACTIVE
= 16
111 int bb_make_directory (char *path
, long mode
, int flags
)
114 const char *fail_msg
;
122 mode
= (S_IXUSR
| S_IXGRP
| S_IXOTH
|
123 S_IWUSR
| S_IWGRP
| S_IWOTH
|
124 S_IRUSR
| S_IRGRP
| S_IROTH
) & ~mask
;
132 if (flags
& FILEUTILS_RECUR
) { /* Get the parent. */
133 /* Bypass leading non-'/'s and then subsequent '/'s. */
139 c
= *s
; /* Save the current char */
140 *s
= 0; /* and replace it with nul. */
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
))) {
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. */
167 /* Done. If necessary, updated perms on the newly
168 * created directory. Failure to update here _is_
171 if ((mode
!= -1) && (chmod(path
, mode
) < 0)){
172 fail_msg
= "set permissions of";
178 /* Remove any inserted nul from the path (recursive mode). */
183 bb_perror_msg ("Cannot %s directory `%s'", fail_msg
, path
);
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
);
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
);
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
);
213 char *private_get_line_from_file(FILE *file
, int c
)
215 #define GROWBY (80) /* how large we will grow strings by */
219 char *linebuf
= NULL
;
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') {
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
);
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
);
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 *))
278 r
= strtoul(s
, &p
, 10);
279 if (*p
|| (s
== p
)) {
286 char * last_char_is(const char *s
, int c
)
288 char *sret
= (char *)s
;
290 sret
= strrchr(sret
, c
);
291 if(sret
!= NULL
&& *(sret
+1) != 0)
297 void bb_xasprintf(char **string_ptr
, const char *format
, ...)
303 r
= vasprintf(string_ptr
, format
, p
);
307 bb_perror_msg_and_die("bb_xasprintf");
311 char *concat_path_file(const char *path
, const char *filename
)
318 lc
= last_char_is(path
, '/');
319 while (*filename
== '/')
321 bb_xasprintf(&outbuf
, "%s%s%s", path
, (lc
==NULL
? "/" : ""), filename
);
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");
359 int main(int argc
, char **argv
)
363 char *rootdir
= NULL
;
366 int ret
= EXIT_SUCCESS
;
368 bb_applet_name
= basename(argv
[0]);
370 while ((opt
= getopt(argc
, argv
, "d:")) != -1) {
373 table
= bb_xfopen((line
=optarg
), "r");
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
);
390 printf("rootdir=%s\n", rootdir
);
392 printf("table='%s'\n", line
);
394 printf("table=<stdin>\n");
397 while ((line
= bb_get_chomped_line_from_file(table
))) {
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;
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
))
421 bb_error_msg("line %d invalid: '%s'\n", linenum
, line
);
425 if (name
[0] == '#') {
429 gid
= get_ug_id(group
, my_getgrnam
);
434 uid
= get_ug_id(user
, my_getpwnam
);
438 full_name
= concat_path_file(rootdir
, name
);
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
);
447 if ((mode
!= -1) && (chmod(full_name
, mode
) < 0)){
448 bb_perror_msg("line %d: chmod failed for %s", linenum
, full_name
);
452 } else if (type
== 'f') {
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
);
459 if (chown(full_name
, uid
, gid
) == -1) {
460 bb_perror_msg("line %d: chown failed for %s", linenum
, full_name
);
464 if ((mode
!= -1) && (chmod(full_name
, mode
) < 0)){
465 bb_perror_msg("line %d: chmod failed for %s", linenum
, full_name
);
476 else if (type
== 'c') {
479 else if (type
== 'b') {
482 bb_error_msg("line %d: Unsupported file type %c", linenum
, type
);
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
);
499 else if (chown(full_name_inc
, uid
, gid
) == -1) {
500 bb_perror_msg("line %d: chown failed for %s", linenum
, full_name_inc
);
503 if ((mode
!= -1) && (chmod(full_name_inc
, mode
) < 0)){
504 bb_perror_msg("line %d: chmod failed for %s", linenum
, full_name_inc
);
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
);
515 else if (chown(full_name
, uid
, gid
) == -1) {
516 bb_perror_msg("line %d: chown failed for %s", linenum
, full_name
);
519 if ((mode
!= -1) && (chmod(full_name
, mode
) < 0)){
520 bb_perror_msg("line %d: chmod failed for %s", linenum
, full_name
);