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.24.2.3.6.1 2002/03/31 21:01:32 jrjackson Exp $
29 * traverse directory tree to get backup size estimates
34 #define ROUND(n,x) ((x) + (n) - 1 - (((x) + (n) - 1) % (n)))
37 static unsigned long round_function(n, x)
40 unsigned long remainder = x % n;
47 # define ST_BLOCKS(s) ((s).st_size / 512 + (((s).st_size % 512) ? 1 : 0))
49 #define FILETYPES (S_IFREG|S_IFLNK|S_IFDIR)
51 typedef struct name_s {
65 } dumpstats[MAXDUMPS];
67 time_t dumpdate[MAXDUMPS];
68 int dumplevel[MAXDUMPS];
71 void (*add_file) P((int, struct stat *));
72 long (*final_size) P((int, char *));
75 int main P((int, char **));
76 void traverse_dirs P((char *));
79 void add_file_dump P((int, struct stat *));
80 long final_size_dump P((int, char *));
82 void add_file_gnutar P((int, struct stat *));
83 long final_size_gnutar P((int, char *));
85 void add_file_unknown P((int, struct stat *));
86 long final_size_unknown P((int, char *));
88 #ifdef BUILTIN_EXCLUDE_SUPPORT
89 int use_gtar_excl = 0;
90 char exclude_string[] = "--exclude=";
91 char exclude_list_string[] = "--exclude-list=";
99 /* standalone test to ckeck wether the calculated file size is ok */
102 unsigned long dump_total=0, gtar_total=0;
107 for(fd = 3; fd < FD_SETSIZE; fd++) {
109 * Make sure nobody spoofs us with a lot of extra open files
110 * that would cause an open we do to get a very high file
111 * descriptor, which in turn might be used as an index into
112 * an array (e.g. an fd_set).
117 set_pname("calcsize");
120 fprintf(stderr,"Usage: %s file[s]\n",argv[0]);
123 for(i=1; i<argc; i++) {
124 if(lstat(argv[i], &finfo) == -1) {
125 fprintf(stderr, "%s: %s\n", argv[i], strerror(errno));
128 printf("%s: st_size=%lu", argv[i],(unsigned long)finfo.st_size);
129 printf(": blocks=%lu\n", (unsigned long)ST_BLOCKS(finfo));
130 dump_total += (ST_BLOCKS(finfo) + 1)/2 + 1;
131 gtar_total += ROUND(4,(ST_BLOCKS(finfo) + 1));
133 printf(" gtar dump\n");
134 printf("total %-9lu %-9lu\n",gtar_total,dump_total);
138 char *dirname=NULL, *amname=NULL;
140 unsigned long malloc_hist_1, malloc_size_1;
141 unsigned long malloc_hist_2, malloc_size_2;
143 for(fd = 3; fd < FD_SETSIZE; fd++) {
145 * Make sure nobody spoofs us with a lot of extra open files
146 * that would cause an open we do to get a very high file
147 * descriptor, which in turn might be used as an index into
148 * an array (e.g. an fd_set).
153 set_pname("calcsize");
157 malloc_size_1 = malloc_inuse(&malloc_hist_1);
160 erroutput_type = (ERR_INTERACTIVE|ERR_SYSLOG);
163 argc--, argv++; /* skip program name */
165 /* need at least program, amname, and directory name */
168 #ifdef BUILTIN_EXCLUDE_SUPPORT
171 error("Usage: %s [DUMP|GNUTAR%s] name dir [level date] ...",
173 #ifdef BUILTIN_EXCLUDE_SUPPORT
174 " [-X --exclude[-list]=regexp]"
182 /* parse backup program name */
184 if(strcmp(*argv, "DUMP") == 0) {
185 #if !defined(DUMP) && !defined(XFSDUMP)
186 error("dump not available on this system");
189 add_file = add_file_dump;
190 final_size = final_size_dump;
193 else if(strcmp(*argv, "GNUTAR") == 0) {
195 error("gnutar not available on this system");
198 add_file = add_file_gnutar;
199 final_size = final_size_gnutar;
200 #ifdef BUILTIN_EXCLUDE_SUPPORT
206 add_file = add_file_unknown;
207 final_size = final_size_unknown;
210 #ifdef BUILTIN_EXCLUDE_SUPPORT
211 if ((argc > 1) && strcmp(*argv,"-X") == 0) {
216 if (!use_gtar_excl) {
217 error("exclusion specification not supported");
221 result = stralloc(*argv);
222 if (*result && (cp = strrchr(result,';')))
223 /* delete trailing ; */
225 if (strncmp(result, exclude_string, sizeof(exclude_string)-1) == 0)
226 add_exclude(result+sizeof(exclude_string)-1);
227 else if (strncmp(result, exclude_list_string,
228 sizeof(exclude_list_string)-1) == 0) {
229 if (access(result + sizeof(exclude_list_string)-1, R_OK) != 0) {
230 fprintf(stderr,"Cannot open exclude file %s\n",cp+1);
233 add_exclude_file(result + sizeof(exclude_list_string)-1);
246 /* the amanda name can be different from the directory name */
252 error("missing <name>");
254 /* the toplevel directory name to search from */
259 error("missing <dir>");
261 /* the dump levels to calculate sizes for */
265 if(ndumps < MAXDUMPS) {
266 dumplevel[ndumps] = atoi(argv[0]);
267 dumpdate [ndumps] = (time_t) atol(argv[1]);
269 argc -= 2, argv += 2;
274 error("leftover arg \"%s\", expected <level> and <date>", *argv);
276 traverse_dirs(dirname);
277 for(i = 0; i < ndumps; i++) {
281 lseek(1, (off_t)0, SEEK_END);
283 printf("%s %d SIZE %ld\n",
284 amname, dumplevel[i], final_size(i, dirname));
287 amfunlock(1, "size");
290 malloc_size_2 = malloc_inuse(&malloc_hist_2);
292 if(malloc_size_1 != malloc_size_2) {
293 malloc_list(fileno(stderr), malloc_hist_1, malloc_hist_2);
301 * =========================================================================
304 #ifndef HAVE_BASENAME
310 if ( (cp = strrchr(file,'/')) )
316 void push_name P((char *str));
317 char *pop_name P((void));
319 void traverse_dirs(parent_dir)
325 char *dirname, *newname = NULL;
326 char *newbase = NULL;
327 dev_t parent_dev = 0;
331 if(parent_dir && stat(parent_dir, &finfo) != -1)
332 parent_dev = finfo.st_dev;
334 push_name(parent_dir);
336 for(dirname = pop_name(); dirname; free(dirname), dirname = pop_name()) {
338 #ifdef BUILTIN_EXCLUDE_SUPPORT
340 (check_exclude(basename(dirname)) ||
341 check_exclude(dirname)))
342 /* will not be added by gnutar */
346 if((d = opendir(dirname)) == NULL) {
352 if(l > 0 && dirname[l - 1] != '/') {
353 newbase = newstralloc2(newbase, dirname, "/");
355 newbase = newstralloc(newbase, dirname);
358 while((f = readdir(d)) != NULL) {
359 if(is_dot_or_dotdot(f->d_name)) {
363 newname = newstralloc2(newname, newbase, f->d_name);
364 if(lstat(newname, &finfo) == -1) {
365 fprintf(stderr, "%s/%s: %s\n",
366 dirname, f->d_name, strerror(errno));
370 if(finfo.st_dev != parent_dev) {
374 if((finfo.st_mode & S_IFMT) == S_IFDIR) {
378 for(i = 0; i < ndumps; i++) {
379 if(finfo.st_ctime >= dumpdate[i]) {
383 #ifdef BUILTIN_EXCLUDE_SUPPORT
384 exclude = check_exclude(f->d_name);
387 is_symlink = ((finfo.st_mode & S_IFMT) == S_IFLNK);
391 ((finfo.st_mode & S_IFMT) == S_IFREG
393 || (finfo.st_mode & S_IFMT) == S_IFDIR
405 if(closedir(d) == -1)
418 newp = alloc(sizeof(*newp));
419 newp->str = stralloc(str);
421 newp->next = name_stack;
427 Name *newp = name_stack;
430 if(!newp) return NULL;
432 name_stack = newp->next;
440 * =========================================================================
441 * Backup size calculations for DUMP program
443 * Given the system-dependent nature of dump, it's impossible to pin this
444 * down accurately. Luckily, that's not necessary.
446 * Dump rounds each file up to TP_BSIZE bytes, which is 1k in the BSD dump,
447 * others are unknown. In addition, dump stores three bitmaps at the
448 * beginning of the dump: a used inode map, a dumped dir map, and a dumped
449 * inode map. These are sized by the number of inodes in the filesystem.
451 * We don't take into account the complexities of BSD dump's indirect block
452 * requirements for files with holes, nor the dumping of directories that
453 * are not themselves modified.
455 void add_file_dump(level, sp)
459 /* keep the size in kbytes, rounded up, plus a 1k header block */
460 if((sp->st_mode & S_IFMT) == S_IFREG || (sp->st_mode & S_IFMT) == S_IFDIR)
461 dumpstats[level].total_size += (ST_BLOCKS(*sp) + 1)/2 + 1;
464 long final_size_dump(level, topdir)
468 generic_fs_stats_t stats;
472 /* calculate the map sizes */
474 s = stralloc2(topdir, "/.");
475 if(get_fs_stats(s, &stats) == -1) {
476 error("statfs %s: %s", s, strerror(errno));
480 mapsize = (stats.files + 7) / 8; /* in bytes */
481 mapsize = (mapsize + 1023) / 1024; /* in kbytes */
483 /* the dump contains three maps plus the files */
485 return 3*mapsize + dumpstats[level].total_size;
489 * =========================================================================
490 * Backup size calculations for GNUTAR program
492 * Gnutar's basic blocksize is 512 bytes. Each file is rounded up to that
493 * size, plus one header block. Gnutar stores directories' file lists in
494 * incremental dumps - we'll pick up size of the modified dirs here. These
495 * will be larger than a simple filelist of their contents, but that's ok.
497 * As with DUMP, we only need a reasonable estimate, not an exact figure.
499 void add_file_gnutar(level, sp)
503 /* the header takes one additional block */
504 dumpstats[level].total_size += ROUND(4,(ST_BLOCKS(*sp) + 1));
507 long final_size_gnutar(level, topdir)
511 /* divide by two to get kbytes, rounded up */
512 /* + 4 blocks for security */
513 return (dumpstats[level].total_size + 5) / 2;
517 * =========================================================================
518 * Backup size calculations for unknown backup programs.
520 * Here we'll just add up the file sizes and output that.
523 void add_file_unknown(level, sp)
527 /* just add up the block counts */
528 if((sp->st_mode & S_IFMT) == S_IFREG || (sp->st_mode & S_IFMT) == S_IFDIR)
529 dumpstats[level].total_size += ST_BLOCKS(*sp);
532 long final_size_unknown(level, topdir)
536 /* divide by two to get kbytes, rounded up */
537 return (dumpstats[level].total_size + 1) / 2;