source: trunk/essentials/app-arch/cpio/tests/genfile.c

Last change on this file was 3332, checked in by bird, 18 years ago

cpio 2.7

File size: 18.0 KB
Line 
1/* Generate a file containing some preset patterns.
2 Print statistics for existing files.
3
4 Copyright (C) 1995, 1996, 1997, 2001, 2003, 2004, 2005, 2006
5 Free Software Foundation, Inc.
6
7 François Pinard <pinard@iro.umontreal.ca>, 1995.
8 Sergey Poznyakoff <gray@mirddin.farlep.net>, 2004, 2005, 2006.
9
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 2, or (at your option)
13 any later version.
14
15 This program is distributed in the hope that it will be useful, but
16 WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 General Public License for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software Foundation,
22 Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
23*/
24
25#include <system.h>
26#include <signal.h>
27#include <stdarg.h>
28#include <argmatch.h>
29#include <argp.h>
30#include <argcv.h>
31#include <getdate.h>
32#include <setenv.h>
33#include <utimens.h>
34#include <inttostr.h>
35#define obstack_chunk_alloc malloc
36#define obstack_chunk_free free
37#include <obstack.h>
38
39#ifndef EXIT_SUCCESS
40# define EXIT_SUCCESS 0
41#endif
42#ifndef EXIT_FAILURE
43# define EXIT_FAILURE 1
44#endif
45
46#if ! defined SIGCHLD && defined SIGCLD
47# define SIGCHLD SIGCLD
48#endif
49
50enum pattern
51{
52 DEFAULT_PATTERN,
53 ZEROS_PATTERN
54};
55
56/* The name this program was run with. */
57const char *program_name;
58
59/* Name of file to generate */
60static char *file_name;
61
62/* Name of the file-list file: */
63static char *files_from;
64static char filename_terminator = '\n';
65
66/* Length of file to generate. */
67static off_t file_length = 0;
68
69/* Pattern to generate. */
70static enum pattern pattern = DEFAULT_PATTERN;
71
72/* Next checkpoint number */
73size_t checkpoint;
74
75enum genfile_mode
76 {
77 mode_generate,
78 mode_sparse,
79 mode_stat,
80 mode_exec
81 };
82
83enum genfile_mode mode = mode_generate;
84
85#define DEFAULT_STAT_FORMAT \
86 "name,dev,ino,mode,nlink,uid,gid,size,blksize,blocks,atime,mtime,ctime"
87
88/* Format for --stat option */
89static char *stat_format = DEFAULT_STAT_FORMAT;
90
91/* Size of a block for sparse file */
92size_t block_size = 512;
93
94/* Block buffer for sparse file */
95char *buffer;
96
97/* Number of arguments and argument vector for mode == mode_exec */
98int exec_argc;
99char **exec_argv;
100
101/* Time for --touch option */
102struct timespec touch_time;
103
104/* Verbose mode */
105int verbose;
106
107const char *argp_program_version = "genfile (" PACKAGE ") " VERSION;
108const char *argp_program_bug_address = "<" PACKAGE_BUGREPORT ">";
109static char doc[] = N_("genfile manipulates data files for GNU paxutils test suite.\n"
110"OPTIONS are:\n");
111
112#define OPT_CHECKPOINT 256
113#define OPT_TOUCH 257
114#define OPT_APPEND 258
115#define OPT_TRUNCATE 259
116#define OPT_EXEC 260
117#define OPT_DATE 261
118#define OPT_VERBOSE 262
119
120static struct argp_option options[] = {
121#define GRP 0
122 {NULL, 0, NULL, 0,
123 N_("File creation options:"), GRP},
124 {"length", 'l', N_("SIZE"), 0,
125 N_("Create file of the given SIZE"), GRP+1 },
126 {"file", 'f', N_("NAME"), 0,
127 N_("Write to file NAME, instead of standard output"), GRP+1},
128 {"files-from", 'T', N_("FILE"), 0,
129 N_("Read file names from FILE"), GRP+1},
130 {"null", '0', NULL, 0,
131 N_("-T reads null-terminated names"), GRP+1},
132 {"pattern", 'p', N_("PATTERN"), 0,
133 N_("Fill the file with the given PATTERN. PATTERN is 'default' or 'zeros'"),
134 GRP+1 },
135 {"block-size", 'b', N_("SIZE"), 0,
136 N_("Size of a block for sparse file"), GRP+1},
137 {"sparse", 's', NULL, 0,
138 N_("Generate sparse file. Rest of the command line gives the file map."),
139 GRP+1 },
140
141#undef GRP
142#define GRP 10
143 {NULL, 0, NULL, 0,
144 N_("File statistics options:"), GRP},
145
146 {"stat", 'S', N_("FORMAT"), OPTION_ARG_OPTIONAL,
147 N_("Print contents of struct stat for each given file. Default FORMAT is: ")
148 DEFAULT_STAT_FORMAT,
149 GRP+1 },
150
151#undef GRP
152#define GRP 20
153 {NULL, 0, NULL, 0,
154 N_("Synchronous execution options:"), GRP},
155
156 {"run", 'r', N_("COMMAND"), 0,
157 N_("Execute given COMMAND. Useful with --checkpoint and one of --cut, --append, --touch"),
158 GRP+1 },
159 {"checkpoint", OPT_CHECKPOINT, N_("NUMBER"), 0,
160 N_("Perform given action (see below) upon reaching checkpoint NUMBER"),
161 GRP+1 },
162 {"date", OPT_DATE, N_("STRING"), 0,
163 N_("Set date for next --touch option"),
164 GRP+1 },
165 {"verbose", OPT_VERBOSE, NULL, 0,
166 N_("Display executed checkpoints and exit status of COMMAND"),
167 GRP+1 },
168#undef GRP
169#define GRP 30
170 {NULL, 0, NULL, 0,
171 N_("Synchronous execution actions. These are executed when checkpoint number given by --checkpoint option is reached."), GRP},
172
173 {"cut", OPT_TRUNCATE, N_("FILE"), 0,
174 N_("Truncate FILE to the size specified by previous --length option (or 0, if it is not given)"),
175 GRP+1 },
176 {"truncate", 0, NULL, OPTION_ALIAS, NULL, GRP+1 },
177 {"append", OPT_APPEND, N_("FILE"), 0,
178 N_("Append SIZE bytes to FILE. SIZE is given by previous --length option."),
179 GRP+1 },
180 {"touch", OPT_TOUCH, N_("FILE"), 0,
181 N_("Update the access and modification times of FILE"),
182 GRP+1 },
183 {"exec", OPT_EXEC, N_("COMMAND"), 0,
184 N_("Execute COMMAND"),
185 GRP+1 },
186#undef GRP
187 { NULL, }
188};
189
190static char const * const pattern_args[] = { "default", "zeros", 0 };
191static enum pattern const pattern_types[] = {DEFAULT_PATTERN, ZEROS_PATTERN};
192
193static int
194xlat_suffix (off_t *vp, const char *p)
195{
196 off_t val = *vp;
197
198 if (p[1])
199 return 1;
200 switch (p[0])
201 {
202 case 'g':
203 case 'G':
204 *vp *= 1024;
205
206 case 'm':
207 case 'M':
208 *vp *= 1024;
209
210 case 'k':
211 case 'K':
212 *vp *= 1024;
213 break;
214
215 default:
216 return 1;
217 }
218 return *vp <= val;
219}
220
221static off_t
222get_size (const char *str, int allow_zero)
223{
224 const char *p;
225 off_t v = 0;
226
227 for (p = str; *p; p++)
228 {
229 int digit = *p - '0';
230 off_t x = v * 10;
231 if (9 < (unsigned) digit)
232 {
233 if (xlat_suffix (&v, p))
234 error (EXIT_FAILURE, 0, _("Invalid size: %s"), str);
235 else
236 break;
237 }
238 else if (x / 10 != v)
239 error (EXIT_FAILURE, 0, _("Number out of allowed range: %s"), str);
240 v = x + digit;
241 if (v < 0)
242 error (EXIT_FAILURE, 0, _("Negative size: %s"), str);
243 }
244 return v;
245}
246
247void
248verify_file (char *file_name)
249{
250 if (file_name)
251 {
252 struct stat st;
253
254 if (stat (file_name, &st))
255 error (0, errno, _("stat(%s) failed"), file_name);
256
257 if (st.st_size != file_length)
258 {
259 printf ("%lu %lu\n", (unsigned long)st.st_size , (unsigned long)file_length);
260 exit (1);
261 }
262
263 if (mode == mode_sparse && !ST_IS_SPARSE (st))
264 exit (1);
265 }
266}
267
268struct action
269{
270 struct action *next;
271 size_t checkpoint;
272 int action;
273 char *name;
274 off_t size;
275 enum pattern pattern;
276 struct timespec ts;
277};
278
279static struct action *action_list;
280
281void
282reg_action (int action, char *arg)
283{
284 struct action *act = xmalloc (sizeof (*act));
285 act->checkpoint = checkpoint;
286 act->action = action;
287 act->pattern = pattern;
288 act->ts = touch_time;
289 act->size = file_length;
290 act->name = arg;
291 act->next = action_list;
292 action_list = act;
293}
294
295static error_t
296parse_opt (int key, char *arg, struct argp_state *state)
297{
298 switch (key)
299 {
300 case '0':
301 filename_terminator = 0;
302 break;
303
304 case 'f':
305 file_name = arg;
306 break;
307
308 case 'l':
309 file_length = get_size (arg, 1);
310 break;
311
312 case 'p':
313 pattern = XARGMATCH ("--pattern", arg, pattern_args, pattern_types);
314 break;
315
316 case 'b':
317 block_size = get_size (arg, 0);
318 break;
319
320 case 's':
321 mode = mode_sparse;
322 break;
323
324 case 'S':
325 mode = mode_stat;
326 if (arg)
327 stat_format = arg;
328 break;
329
330 case 'r':
331 mode = mode_exec;
332 argcv_get (arg, "", NULL, &exec_argc, &exec_argv);
333 break;
334
335 case 'T':
336 files_from = arg;
337 break;
338
339 case OPT_CHECKPOINT:
340 {
341 char *p;
342
343 checkpoint = strtoul (arg, &p, 0);
344 if (*p)
345 argp_error (state, _("Error parsing number near `%s'"), p);
346 }
347 break;
348
349 case OPT_DATE:
350 if (!get_date (&touch_time, arg, NULL))
351 argp_error (state, _("Unknown date format"));
352 break;
353
354 case OPT_APPEND:
355 case OPT_TRUNCATE:
356 case OPT_TOUCH:
357 case OPT_EXEC:
358 reg_action (key, arg);
359 break;
360
361 case OPT_VERBOSE:
362 verbose++;
363 break;
364
365 default:
366 return ARGP_ERR_UNKNOWN;
367 }
368 return 0;
369}
370
371static struct argp argp = {
372 options,
373 parse_opt,
374 N_("[ARGS...]"),
375 doc,
376 NULL,
377 NULL,
378 NULL
379};
380
381
382
383void
384fill (FILE *fp, off_t length, enum pattern pattern)
385{
386 off_t i;
387
388 switch (pattern)
389 {
390 case DEFAULT_PATTERN:
391 for (i = 0; i < length; i++)
392 fputc (i & 255, fp);
393 break;
394
395 case ZEROS_PATTERN:
396 for (i = 0; i < length; i++)
397 fputc (0, fp);
398 break;
399 }
400}
401
402/* Generate Mode: usual files */
403static void
404generate_simple_file (char *filename)
405{
406 FILE *fp;
407
408 if (filename)
409 {
410 fp = fopen (filename, "w");
411 if (!fp)
412 error (EXIT_FAILURE, 0, _("cannot open `%s'"), filename);
413 }
414 else
415 fp = stdout;
416
417 fill (fp, file_length, pattern);
418
419 fclose (fp);
420}
421
422/* A simplified version of the same function from tar */
423int
424read_name_from_file (FILE *fp, struct obstack *stk)
425{
426 int c;
427 size_t counter = 0;
428
429 for (c = getc (fp); c != EOF && c != filename_terminator; c = getc (fp))
430 {
431 if (c == 0)
432 error (EXIT_FAILURE, 0, _("file name contains null character"));
433 obstack_1grow (stk, c);
434 counter++;
435 }
436
437 obstack_1grow (stk, 0);
438
439 return (counter == 0 && c == EOF);
440}
441
442void
443generate_files_from_list ()
444{
445 FILE *fp = strcmp (files_from, "-") ? fopen (files_from, "r") : stdin;
446 struct obstack stk;
447
448 if (!fp)
449 error (EXIT_FAILURE, errno, _("cannot open `%s'"), files_from);
450
451 obstack_init (&stk);
452 while (!read_name_from_file (fp, &stk))
453 {
454 char *name = obstack_finish (&stk);
455 generate_simple_file (name);
456 verify_file (name);
457 obstack_free (&stk, name);
458 }
459 fclose (fp);
460 obstack_free (&stk, NULL);
461}
462
463
464
465/* Generate Mode: sparse files */
466
467static void
468mkhole (int fd, off_t displ)
469{
470 if (lseek (fd, displ, SEEK_CUR) == -1)
471 error (EXIT_FAILURE, errno, "lseek");
472 ftruncate (fd, lseek (fd, 0, SEEK_CUR));
473}
474
475static void
476mksparse (int fd, off_t displ, char *marks)
477{
478 if (lseek (fd, displ, SEEK_CUR) == -1)
479 error (EXIT_FAILURE, errno, "lseek");
480
481 for (; *marks; marks++)
482 {
483 memset (buffer, *marks, block_size);
484 if (write (fd, buffer, block_size) != block_size)
485 error (EXIT_FAILURE, errno, "write");
486 }
487}
488
489static void
490generate_sparse_file (int argc, char **argv)
491{
492 int i;
493 int fd;
494
495 if (!file_name)
496 error (EXIT_FAILURE, 0,
497 _("cannot generate sparse files on standard output, use --file option"));
498 fd = open (file_name, O_CREAT|O_TRUNC|O_RDWR, 0644);
499 if (fd < 0)
500 error (EXIT_FAILURE, 0, _("cannot open `%s'"), file_name);
501
502 buffer = xmalloc (block_size);
503
504 file_length = 0;
505
506 for (i = 0; i < argc; i += 2)
507 {
508 off_t displ = get_size (argv[i], 1);
509 file_length += displ;
510
511 if (i == argc-1)
512 {
513 mkhole (fd, displ);
514 break;
515 }
516 else
517 {
518 file_length += block_size * strlen (argv[i+1]);
519 mksparse (fd, displ, argv[i+1]);
520 }
521 }
522
523 close (fd);
524}
525
526
527
528/* Status Mode */
529
530void
531print_time (time_t t)
532{
533 char buf[20]; /* ccyy-mm-dd HH:MM:SS\0 */
534 strftime (buf, sizeof buf, "%Y-%m-%d %H:%M:%S", gmtime (&t));
535 printf ("%s ", buf);
536}
537
538void
539print_stat (const char *name)
540{
541 char *fmt, *p;
542 struct stat st;
543 char buf[UINTMAX_STRSIZE_BOUND];
544
545 if (stat (name, &st))
546 {
547 error (0, errno, _("stat(%s) failed"), name);
548 return;
549 }
550
551 fmt = strdup (stat_format);
552 for (p = strtok (fmt, ","); p; )
553 {
554 if (memcmp (p, "st_", 3) == 0)
555 p += 3;
556 if (strcmp (p, "name") == 0)
557 printf ("%s", name);
558 else if (strcmp (p, "dev") == 0)
559 printf ("%lu", (unsigned long) st.st_dev);
560 else if (strcmp (p, "ino") == 0)
561 printf ("%lu", (unsigned long) st.st_ino);
562 else if (strncmp (p, "mode", 4) == 0)
563 {
564 mode_t mask = ~0;
565
566 if (ispunct (p[4]))
567 {
568 char *q;
569
570 mask = strtoul (p + 5, &q, 8);
571 if (*q)
572 {
573 printf ("\n");
574 error (EXIT_FAILURE, 0, _("incorrect mask (near `%s')"), q);
575 }
576 }
577 else if (p[4])
578 {
579 printf ("\n");
580 error (EXIT_FAILURE, 0, _("Unknown field `%s'"), p);
581 }
582 printf ("%0o", st.st_mode & mask);
583 }
584 else if (strcmp (p, "nlink") == 0)
585 printf ("%lu", (unsigned long) st.st_nlink);
586 else if (strcmp (p, "uid") == 0)
587 printf ("%ld", (long unsigned) st.st_uid);
588 else if (strcmp (p, "gid") == 0)
589 printf ("%lu", (unsigned long) st.st_gid);
590 else if (strcmp (p, "size") == 0)
591 printf ("%s", umaxtostr (st.st_size, buf));
592 else if (strcmp (p, "blksize") == 0)
593 printf ("%s", umaxtostr (st.st_blksize, buf));
594 else if (strcmp (p, "blocks") == 0)
595 printf ("%s", umaxtostr (st.st_blocks, buf));
596 else if (strcmp (p, "atime") == 0)
597 printf ("%lu", (unsigned long) st.st_atime);
598 else if (strcmp (p, "atimeH") == 0)
599 print_time (st.st_atime);
600 else if (strcmp (p, "mtime") == 0)
601 printf ("%lu", (unsigned long) st.st_mtime);
602 else if (strcmp (p, "mtimeH") == 0)
603 print_time (st.st_mtime);
604 else if (strcmp (p, "ctime") == 0)
605 printf ("%lu", (unsigned long) st.st_ctime);
606 else if (strcmp (p, "ctimeH") == 0)
607 print_time (st.st_ctime);
608 else if (strcmp (p, "sparse") == 0)
609 printf ("%d", ST_IS_SPARSE (st));
610 else
611 {
612 printf ("\n");
613 error (EXIT_FAILURE, 0, _("Unknown field `%s'"), p);
614 }
615 p = strtok (NULL, ",");
616 if (p)
617 printf (" ");
618 }
619 printf ("\n");
620 free (fmt);
621}
622
623
624
625/* Exec Mode */
626
627void
628exec_checkpoint (struct action *p)
629{
630 if (verbose)
631 printf ("processing checkpoint %lu\n", (unsigned long) p->checkpoint);
632 switch (p->action)
633 {
634 case OPT_TOUCH:
635 {
636 struct timespec ts[2];
637
638 ts[0] = ts[1] = p->ts;
639 if (utimens (p->name, ts) != 0)
640 {
641 error (0, errno, _("cannot set time on `%s'"), p->name);
642 break;
643 }
644 }
645 break;
646
647 case OPT_APPEND:
648 {
649 FILE *fp = fopen (p->name, "a");
650 if (!fp)
651 {
652 error (0, errno, _("cannot open `%s'"), p->name);
653 break;
654 }
655
656 fill (fp, p->size, p->pattern);
657 fclose (fp);
658 }
659 break;
660
661 case OPT_TRUNCATE:
662 {
663 int fd = open (p->name, O_RDWR);
664 if (fd == -1)
665 {
666 error (0, errno, _("cannot open `%s'"), p->name);
667 break;
668 }
669 ftruncate (fd, p->size);
670 close (fd);
671 }
672 break;
673
674 case OPT_EXEC:
675 system (p->name);
676 break;
677
678 default:
679 abort ();
680 }
681}
682
683void
684process_checkpoint (size_t n)
685{
686 struct action *p, *prev = NULL;
687
688 for (p = action_list; p; )
689 {
690 struct action *next = p->next;
691
692 if (p->checkpoint <= n)
693 {
694 exec_checkpoint (p);
695 /* Remove the item from the list */
696 if (prev)
697 prev->next = next;
698 else
699 action_list = next;
700 free (p);
701 }
702 else
703 prev = p;
704
705 p = next;
706 }
707}
708
709#define CHECKPOINT_TEXT "Write checkpoint"
710
711void
712exec_command (void)
713{
714 int status;
715 pid_t pid;
716 int fd[2];
717 char *p;
718 FILE *fp;
719 char buf[128];
720
721 /* Insert --checkpoint option.
722 FIXME: This assumes that exec_argv does not use traditional tar options
723 (without dash) */
724 exec_argc++;
725 exec_argv = xrealloc (exec_argv, (exec_argc + 1) * sizeof (*exec_argv));
726 memmove (exec_argv+2, exec_argv+1, (exec_argc - 1) * sizeof (*exec_argv));
727 exec_argv[1] = "--checkpoint";
728
729#ifdef SIGCHLD
730 /* System V fork+wait does not work if SIGCHLD is ignored. */
731 signal (SIGCHLD, SIG_DFL);
732#endif
733
734 pipe (fd);
735
736 pid = fork ();
737 if (pid == -1)
738 error (EXIT_FAILURE, errno, "fork");
739
740 if (pid == 0)
741 {
742 /* Child */
743
744 /* Pipe stderr */
745 if (fd[1] != 2)
746 dup2 (fd[1], 2);
747 close (fd[0]);
748
749 /* Make sure POSIX locale is used */
750 setenv ("LC_ALL", "POSIX", 1);
751
752 execvp (exec_argv[0], exec_argv);
753 error (EXIT_FAILURE, errno, "execvp");
754 }
755
756 /* Master */
757 close (fd[1]);
758 fp = fdopen (fd[0], "r");
759 if (fp == NULL)
760 error (EXIT_FAILURE, errno, "fdopen");
761
762 while ((p = fgets (buf, sizeof buf, fp)))
763 {
764 while (*p && !isspace (*p) && *p != ':')
765 p++;
766
767 if (*p == ':')
768 {
769 for (p++; *p && isspace (*p); p++)
770 ;
771
772 if (*p
773 && memcmp (p, CHECKPOINT_TEXT, sizeof CHECKPOINT_TEXT - 1) == 0)
774 {
775 char *end;
776 size_t n = strtoul (p + sizeof CHECKPOINT_TEXT - 1, &end, 10);
777 if (!(*end && !isspace (*end)))
778 {
779 process_checkpoint (n);
780 continue;
781 }
782 }
783 }
784 fprintf (stderr, "%s", buf);
785 }
786
787 /* Collect exit status */
788 waitpid (pid, &status, 0);
789
790 if (verbose)
791 {
792 if (WIFEXITED (status))
793 {
794 if (WEXITSTATUS (status) == 0)
795 printf (_("Command exited successfully\n"));
796 else
797 printf (_("Command failed with status %d\n"),
798 WEXITSTATUS (status));
799 }
800 else if (WIFSIGNALED (status))
801 printf (_("Command terminated on signal %d\n"), WTERMSIG (status));
802 else if (WIFSTOPPED (status))
803 printf (_("Command stopped on signal %d\n"), WSTOPSIG (status));
804#ifdef WCOREDUMP
805 else if (WCOREDUMP (status))
806 printf (_("Command dumped core\n"));
807#endif
808 else
809 printf(_("Command terminated\n"));
810 }
811
812 if (WIFEXITED (status))
813 exit (WEXITSTATUS (status));
814 exit (EXIT_FAILURE);
815}
816
817int
818main (int argc, char **argv)
819{
820 int index;
821
822 program_name = argv[0];
823 setlocale (LC_ALL, "");
824 bindtextdomain (PACKAGE, LOCALEDIR);
825 textdomain (PACKAGE);
826
827 get_date (&touch_time, "now", NULL);
828
829 /* Decode command options. */
830
831 if (argp_parse (&argp, argc, argv, 0, &index, NULL))
832 exit (EXIT_FAILURE);
833
834 argc -= index;
835 argv += index;
836
837 switch (mode)
838 {
839 case mode_stat:
840 if (argc == 0)
841 error (EXIT_FAILURE, 0, _("--stat requires file names"));
842
843 while (argc--)
844 print_stat (*argv++);
845 break;
846
847 case mode_sparse:
848 generate_sparse_file (argc, argv);
849 verify_file (file_name);
850 break;
851
852 case mode_generate:
853 if (argc)
854 error (EXIT_FAILURE, 0, _("too many arguments"));
855 if (files_from)
856 generate_files_from_list ();
857 else
858 {
859 generate_simple_file (file_name);
860 verify_file (file_name);
861 }
862 break;
863
864 case mode_exec:
865 exec_command ();
866 break;
867
868 default:
869 /* Just in case */
870 abort ();
871 }
872 exit (EXIT_SUCCESS);
873}
Note: See TracBrowser for help on using the repository browser.