2 * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3 * Copyright (c) 1991-1998 University of Maryland at College Park
6 * Permission to use, copy, modify, distribute, and sell this software and its
7 * documentation for any purpose is hereby granted without fee, provided that
8 * the above copyright notice appear in all copies and that both that
9 * copyright notice and this permission notice appear in supporting
10 * documentation, and that the name of U.M. not be used in advertising or
11 * publicity pertaining to distribution of the software without specific,
12 * written prior permission. U.M. makes no representations about the
13 * suitability of this software for any purpose. It is provided "as is"
14 * without express or implied warranty.
16 * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
18 * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
20 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
21 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
23 * Authors: the Amanda Development Team. Its members are listed in a
24 * file named AUTHORS, in the root directory of this distribution.
27 * $Id: calcsize.c,v 1.44.2.1 2006/10/19 18:47:25 martinea Exp $
29 * traverse directory tree to get backup size estimates
31 * argv[0] is the calcsize program name
32 * argv[1] is the config name or NOCONFIG
40 #define ROUND(n,x) ((x) + (n) - 1 - (((x) + (n) - 1) % (n)))
48 unsigned long remainder = x % n;
55 #define ST_BLOCKS(s) \
56 (((((off_t)(s).st_blocks * (off_t)512) <= (s).st_size)) ? \
57 ((off_t)(s).st_blocks + (off_t)1) : \
58 ((s).st_size / (off_t)512 + \
59 (off_t)((((s).st_size % (off_t)512) != (off_t)0) ? \
60 (off_t)1 : (off_t)0)))
62 #define FILETYPES (S_IFREG|S_IFLNK|S_IFDIR)
64 typedef struct name_s {
78 off_t total_size_name;
79 } dumpstats[MAXDUMPS];
81 time_t dumpdate[MAXDUMPS];
82 int dumplevel[MAXDUMPS];
85 void (*add_file_name)(int, char *);
86 void (*add_file)(int, struct stat *);
87 off_t (*final_size)(int, char *);
90 int main(int, char **);
91 void traverse_dirs(char *, char *);
94 void add_file_name_dump(int, char *);
95 void add_file_dump(int, struct stat *);
96 off_t final_size_dump(int, char *);
98 void add_file_name_star(int, char *);
99 void add_file_star(int, struct stat *);
100 off_t final_size_star(int, char *);
102 void add_file_name_gnutar(int, char *);
103 void add_file_gnutar(int, struct stat *);
104 off_t final_size_gnutar(int, char *);
106 void add_file_name_unknown(int, char *);
107 void add_file_unknown(int, struct stat *);
108 off_t final_size_unknown(int, char *);
110 sl_t *calc_load_file(char *filename);
111 int calc_check_exclude(char *filename);
113 int use_star_excl = 0;
114 int use_gtar_excl = 0;
115 sl_t *include_sl=NULL, *exclude_sl=NULL;
123 /* standalone test to ckeck wether the calculated file size is ok */
126 off_t dump_total = (off_t)0;
127 off_t gtar_total = (off_t)0;
133 set_pname("calcsize");
137 /* Don't die when child closes pipe */
138 signal(SIGPIPE, SIG_IGN);
141 fprintf(stderr,"Usage: %s file[s]\n",argv[0]);
144 for(i=1; i<argc; i++) {
145 if(lstat(argv[i], &finfo) == -1) {
146 fprintf(stderr, "%s: %s\n", argv[i], strerror(errno));
149 printf("%s: st_size=%lu", argv[i],(unsigned long)finfo.st_size);
150 printf(": blocks=%llu\n", ST_BLOCKS(finfo));
151 dump_total += (ST_BLOCKS(finfo) + (off_t)1) / (off_t)2 + (off_t)1;
152 gtar_total += ROUND(4,(ST_BLOCKS(finfo) + (off_t)1));
154 printf(" gtar dump\n");
155 printf("total %-9lu %-9lu\n",gtar_total,dump_total);
160 char *amname=NULL, *qamname = NULL;
161 char *filename=NULL, *qfilename = NULL;
162 unsigned long malloc_hist_1, malloc_size_1;
163 unsigned long malloc_hist_2, malloc_size_2;
168 set_pname("calcsize");
170 dbopen(DBG_SUBDIR_CLIENT);
171 dbprintf(("%s: version %s\n", debug_prefix(NULL), version()));
173 malloc_size_1 = malloc_inuse(&malloc_hist_1);
176 erroutput_type = (ERR_INTERACTIVE|ERR_SYSLOG);
179 argc--, argv++; /* skip program name */
181 /* need at least program, amname, and directory name */
184 error("Usage: %s config [DUMP|GNUTAR] name dir [-X exclude-file] [-I include-file] [level date]*",
189 dbprintf(("config: %s\n", *argv));
190 if (strcmp(*argv, "NOCONFIG") != 0) {
191 dbrename(*argv, DBG_SUBDIR_CLIENT);
196 /* parse backup program name */
198 if(strcmp(*argv, "DUMP") == 0) {
199 #if !defined(DUMP) && !defined(XFSDUMP)
200 error("dump not available on this system");
203 add_file_name = add_file_name_dump;
204 add_file = add_file_dump;
205 final_size = final_size_dump;
208 else if(strcmp(*argv, "GNUTAR") == 0) {
210 error("gnutar not available on this system");
213 add_file_name = add_file_name_gnutar;
214 add_file = add_file_gnutar;
215 final_size = final_size_gnutar;
220 add_file_name = add_file_name_unknown;
221 add_file = add_file_unknown;
222 final_size = final_size_unknown;
226 /* the amanda name can be different from the directory name */
230 qamname = quote_string(amname);
233 error("missing <name>");
237 /* the toplevel directory name to search from */
242 error("missing <dir>");
246 if ((argc > 1) && strcmp(*argv,"-X") == 0) {
249 if (!(use_gtar_excl || use_star_excl)) {
250 error("exclusion specification not supported");
254 filename = stralloc(*argv);
255 qfilename = quote_string(filename);
256 if (access(filename, R_OK) != 0) {
257 fprintf(stderr,"Cannot open exclude file %s\n", qfilename);
258 use_gtar_excl = use_star_excl = 0;
260 exclude_sl = calc_load_file(filename);
262 fprintf(stderr,"Cannot open exclude file %s: %s\n", qfilename,
264 use_gtar_excl = use_star_excl = 0;
272 use_gtar_excl = use_star_excl = 0;
275 if ((argc > 1) && strcmp(*argv,"-I") == 0) {
278 filename = stralloc(*argv);
279 qfilename = quote_string(filename);
280 if (access(filename, R_OK) != 0) {
281 fprintf(stderr,"Cannot open include file %s\n", qfilename);
282 use_gtar_excl = use_star_excl = 0;
284 include_sl = calc_load_file(filename);
286 fprintf(stderr,"Cannot open include file %s: %s\n", qfilename,
288 use_gtar_excl = use_star_excl = 0;
297 /* the dump levels to calculate sizes for */
301 if(ndumps < MAXDUMPS) {
302 dumplevel[ndumps] = atoi(argv[0]);
303 dumpdate [ndumps] = (time_t) atol(argv[1]);
305 argc -= 2, argv += 2;
310 error("leftover arg \"%s\", expected <level> and <date>", *argv);
314 if(is_empty_sl(include_sl)) {
315 traverse_dirs(dirname,".");
318 sle_t *an_include = include_sl->first;
319 while(an_include != NULL) {
321 char *adirname = stralloc2(dirname, an_include->name+1);
322 traverse_dirs(adirname);
325 traverse_dirs(dirname, an_include->name);
326 an_include = an_include->next;
329 for(i = 0; i < ndumps; i++) {
333 if (fseek(stderr, 0L, SEEK_END) < 0) {
334 dbprintf(("calcsize: warning - seek failed: %s\n",
338 dbprintf(("calcsize: %s %d SIZE " OFF_T_FMT "\n",
339 qamname, dumplevel[i],
340 (OFF_T_FMT_TYPE)final_size(i, dirname)));
341 fprintf(stderr, "%s %d SIZE " OFF_T_FMT "\n",
342 qamname, dumplevel[i],
343 (OFF_T_FMT_TYPE)final_size(i, dirname));
346 amfunlock(1, "size");
349 malloc_size_2 = malloc_inuse(&malloc_hist_2);
351 if(malloc_size_1 != malloc_size_2) {
352 malloc_list(fileno(stderr), malloc_hist_1, malloc_hist_2);
360 * =========================================================================
363 #if !defined(HAVE_BASENAME) && defined(BUILTIN_EXCLUDE_SUPPORT)
371 if ( (cp = strrchr(file,'/')) )
377 void push_name(char *str);
378 char *pop_name(void);
388 char *dirname, *newname = NULL;
389 char *newbase = NULL;
390 dev_t parent_dev = (dev_t)0;
397 if(parent_dir == NULL || include == NULL)
400 has_exclude = !is_empty_sl(exclude_sl) && (use_gtar_excl || use_star_excl);
401 aparent = vstralloc(parent_dir, "/", include, NULL);
403 if(stat(parent_dir, &finfo) != -1)
404 parent_dev = finfo.st_dev;
406 parent_len = strlen(parent_dir);
410 for(; (dirname = pop_name()) != NULL; free(dirname)) {
411 if(has_exclude && calc_check_exclude(dirname+parent_len+1)) {
414 if((d = opendir(dirname)) == NULL) {
420 if(l > 0 && dirname[l - 1] != '/') {
421 newbase = newstralloc2(newbase, dirname, "/");
423 newbase = newstralloc(newbase, dirname);
426 while((f = readdir(d)) != NULL) {
430 if(is_dot_or_dotdot(f->d_name)) {
434 newname = newstralloc2(newname, newbase, f->d_name);
435 if(lstat(newname, &finfo) == -1) {
436 fprintf(stderr, "%s/%s: %s\n",
437 dirname, f->d_name, strerror(errno));
441 if(finfo.st_dev != parent_dev)
445 is_symlink = ((finfo.st_mode & S_IFMT) == S_IFLNK);
447 is_dir = ((finfo.st_mode & S_IFMT) == S_IFDIR);
448 is_file = ((finfo.st_mode & S_IFMT) == S_IFREG);
450 if (!(is_file || is_dir || is_symlink)) {
455 int is_excluded = -1;
456 for(i = 0; i < ndumps; i++) {
457 add_file_name(i, newname);
458 if(is_file && (time_t)finfo.st_ctime >= dumpdate[i]) {
461 if(is_excluded == -1)
463 calc_check_exclude(newname+parent_len+1);
464 if(is_excluded == 1) {
473 if(has_exclude && calc_check_exclude(newname+parent_len+1))
483 if(closedir(d) == -1)
498 newp = alloc(SIZEOF(*newp));
499 newp->str = stralloc(str);
501 newp->next = name_stack;
508 Name *newp = name_stack;
511 if(!newp) return NULL;
513 name_stack = newp->next;
521 * =========================================================================
522 * Backup size calculations for DUMP program
524 * Given the system-dependent nature of dump, it's impossible to pin this
525 * down accurately. Luckily, that's not necessary.
527 * Dump rounds each file up to TP_BSIZE bytes, which is 1k in the BSD dump,
528 * others are unknown. In addition, dump stores three bitmaps at the
529 * beginning of the dump: a used inode map, a dumped dir map, and a dumped
530 * inode map. These are sized by the number of inodes in the filesystem.
532 * We don't take into account the complexities of BSD dump's indirect block
533 * requirements for files with holes, nor the dumping of directories that
534 * are not themselves modified.
541 (void)level; /* Quiet unused parameter warning */
542 (void)name; /* Quiet unused parameter warning */
552 /* keep the size in kbytes, rounded up, plus a 1k header block */
553 if((sp->st_mode & S_IFMT) == S_IFREG || (sp->st_mode & S_IFMT) == S_IFDIR)
554 dumpstats[level].total_size +=
555 (ST_BLOCKS(*sp) + (off_t)1) / (off_t)2 + (off_t)1;
563 generic_fs_stats_t stats;
567 /* calculate the map sizes */
569 s = stralloc2(topdir, "/.");
570 if(get_fs_stats(s, &stats) == -1) {
571 error("statfs %s: %s", s, strerror(errno));
576 mapsize = (stats.files + (off_t)7) / (off_t)8; /* in bytes */
577 mapsize = (mapsize + (off_t)1023) / (off_t)1024; /* in kbytes */
579 /* the dump contains three maps plus the files */
581 return (mapsize * (off_t)3) + dumpstats[level].total_size;
585 * =========================================================================
586 * Backup size calculations for GNUTAR program
588 * Gnutar's basic blocksize is 512 bytes. Each file is rounded up to that
589 * size, plus one header block. Gnutar stores directories' file lists in
590 * incremental dumps - we'll pick up size of the modified dirs here. These
591 * will be larger than a simple filelist of their contents, but that's ok.
593 * As with DUMP, we only need a reasonable estimate, not an exact figure.
596 add_file_name_gnutar(
600 (void)name; /* Quiet unused parameter warning */
602 /* dumpstats[level].total_size_name += strlen(name) + 64;*/
603 dumpstats[level].total_size += (off_t)1;
611 /* the header takes one additional block */
612 dumpstats[level].total_size += ST_BLOCKS(*sp);
620 (void)topdir; /* Quiet unused parameter warning */
622 /* divide by two to get kbytes, rounded up */
623 /* + 4 blocks for security */
624 return (dumpstats[level].total_size + (off_t)5 +
625 (dumpstats[level].total_size_name/(off_t)512)) / (off_t)2;
629 * =========================================================================
630 * Backup size calculations for unknown backup programs.
632 * Here we'll just add up the file sizes and output that.
636 add_file_name_unknown(
640 (void)level; /* Quiet unused parameter warning */
641 (void)name; /* Quiet unused parameter warning */
651 /* just add up the block counts */
652 if((sp->st_mode & S_IFMT) == S_IFREG || (sp->st_mode & S_IFMT) == S_IFDIR)
653 dumpstats[level].total_size += ST_BLOCKS(*sp);
661 (void)topdir; /* Quiet unused parameter warning */
663 /* divide by two to get kbytes, rounded up */
664 return (dumpstats[level].total_size + (off_t)1) / (off_t)2;
668 * =========================================================================
678 FILE *file = fopen(filename, "r");
686 while(fgets(pattern, 1025, file)) {
687 if(strlen(pattern)>0 && pattern[strlen(pattern)-1] == '\n')
688 pattern[strlen(pattern)-1] = '\0';
689 sl_list = append_sl(sl_list, pattern);
701 if(is_empty_sl(exclude_sl)) return 0;
703 an_exclude=exclude_sl->first;
704 while(an_exclude != NULL) {
705 if(match_tar(an_exclude->name, filename)) {
708 an_exclude=an_exclude->next;