* file named AUTHORS, in the root directory of this distribution.
*/
/*
- * $Id: calcsize.c,v 1.24.2.3.6.1 2002/03/31 21:01:32 jrjackson Exp $
+ * $Id: calcsize.c,v 1.44 2006/07/25 18:27:56 martinea Exp $
*
* traverse directory tree to get backup size estimates
+ *
+ * argv[0] is the calcsize program name
+ * argv[1] is the config name or NOCONFIG
*/
#include "amanda.h"
-#include "statfs.h"
+#include "match.h"
+#include "conffile.h"
+#include "fsusage.h"
+#include "sl.h"
+#include "util.h"
#define ROUND(n,x) ((x) + (n) - 1 - (((x) + (n) - 1) % (n)))
/*
-static unsigned long round_function(n, x)
-unsigned long n, x;
+static off_t
+round_function(n, x)
+ off_t n,
+ off_t x)
{
unsigned long remainder = x % n;
if (remainder)
- x += n-remainder;
+ x += n - remainder;
return x;
}
*/
-# define ST_BLOCKS(s) ((s).st_size / 512 + (((s).st_size % 512) ? 1 : 0))
+#define ST_BLOCKS(s) \
+ (((((off_t)(s).st_blocks * (off_t)512) <= (s).st_size)) ? \
+ ((off_t)(s).st_blocks + (off_t)1) : \
+ ((s).st_size / (off_t)512 + \
+ (off_t)((((s).st_size % (off_t)512) != (off_t)0) ? \
+ (off_t)1 : (off_t)0)))
#define FILETYPES (S_IFREG|S_IFLNK|S_IFDIR)
int max_inode;
int total_dirs;
int total_files;
- long total_size;
+ off_t total_size;
+ off_t total_size_name;
} dumpstats[MAXDUMPS];
time_t dumpdate[MAXDUMPS];
int dumplevel[MAXDUMPS];
int ndumps;
-void (*add_file) P((int, struct stat *));
-long (*final_size) P((int, char *));
+void (*add_file_name)(int, char *);
+void (*add_file)(int, struct stat *);
+off_t (*final_size)(int, char *);
+
+int main(int, char **);
+void traverse_dirs(char *, char *);
-int main P((int, char **));
-void traverse_dirs P((char *));
+void add_file_name_dump(int, char *);
+void add_file_dump(int, struct stat *);
+off_t final_size_dump(int, char *);
-void add_file_dump P((int, struct stat *));
-long final_size_dump P((int, char *));
+void add_file_name_star(int, char *);
+void add_file_star(int, struct stat *);
+off_t final_size_star(int, char *);
-void add_file_gnutar P((int, struct stat *));
-long final_size_gnutar P((int, char *));
+void add_file_name_gnutar(int, char *);
+void add_file_gnutar(int, struct stat *);
+off_t final_size_gnutar(int, char *);
-void add_file_unknown P((int, struct stat *));
-long final_size_unknown P((int, char *));
+void add_file_name_unknown(int, char *);
+void add_file_unknown(int, struct stat *);
+off_t final_size_unknown(int, char *);
-#ifdef BUILTIN_EXCLUDE_SUPPORT
+sl_t *calc_load_file(char *filename);
+int calc_check_exclude(char *filename);
+
+int use_star_excl = 0;
int use_gtar_excl = 0;
-char exclude_string[] = "--exclude=";
-char exclude_list_string[] = "--exclude-list=";
-#endif
+sl_t *include_sl=NULL, *exclude_sl=NULL;
-int main(argc, argv)
-int argc;
-char **argv;
+int
+main(
+ int argc,
+ char ** argv)
{
#ifdef TEST
/* standalone test to ckeck wether the calculated file size is ok */
struct stat finfo;
int i;
- unsigned long dump_total=0, gtar_total=0;
+ off_t dump_total = (off_t)0;
+ off_t gtar_total = (off_t)0;
char *d;
int l, w;
- int fd;
-
- for(fd = 3; fd < FD_SETSIZE; fd++) {
- /*
- * Make sure nobody spoofs us with a lot of extra open files
- * that would cause an open we do to get a very high file
- * descriptor, which in turn might be used as an index into
- * an array (e.g. an fd_set).
- */
- close(fd);
- }
+
+ /*
+ * Configure program for internationalization:
+ * 1) Only set the message locale for now.
+ * 2) Set textdomain for all amanda related programs to "amanda"
+ * We don't want to be forced to support dozens of message catalogs.
+ */
+ setlocale(LC_MESSAGES, "C");
+ textdomain("amanda");
+
+ safe_fd(-1, 0);
set_pname("calcsize");
+ dbopen(NULL);
+ config_init(CONFIG_INIT_CLIENT, NULL);
+
+ /* Don't die when child closes pipe */
+ signal(SIGPIPE, SIG_IGN);
+
if (argc < 2) {
- fprintf(stderr,"Usage: %s file[s]\n",argv[0]);
+ g_fprintf(stderr,_("Usage: %s file[s]\n"),argv[0]);
return 1;
}
for(i=1; i<argc; i++) {
if(lstat(argv[i], &finfo) == -1) {
- fprintf(stderr, "%s: %s\n", argv[i], strerror(errno));
+ g_fprintf(stderr, "%s: %s\n", argv[i], strerror(errno));
continue;
}
- printf("%s: st_size=%lu", argv[i],(unsigned long)finfo.st_size);
- printf(": blocks=%lu\n", (unsigned long)ST_BLOCKS(finfo));
- dump_total += (ST_BLOCKS(finfo) + 1)/2 + 1;
- gtar_total += ROUND(4,(ST_BLOCKS(finfo) + 1));
+ g_printf("%s: st_size=%lu", argv[i],(unsigned long)finfo.st_size);
+ g_printf(": blocks=%llu\n", ST_BLOCKS(finfo));
+ dump_total += (ST_BLOCKS(finfo) + (off_t)1) / (off_t)2 + (off_t)1;
+ gtar_total += ROUND(4,(ST_BLOCKS(finfo) + (off_t)1));
}
- printf(" gtar dump\n");
- printf("total %-9lu %-9lu\n",gtar_total,dump_total);
+ g_printf(" gtar dump\n");
+ g_printf("total %-9lu %-9lu\n",gtar_total,dump_total);
return 0;
#else
int i;
- char *dirname=NULL, *amname=NULL;
- int fd;
- unsigned long malloc_hist_1, malloc_size_1;
- unsigned long malloc_hist_2, malloc_size_2;
-
- for(fd = 3; fd < FD_SETSIZE; fd++) {
- /*
- * Make sure nobody spoofs us with a lot of extra open files
- * that would cause an open we do to get a very high file
- * descriptor, which in turn might be used as an index into
- * an array (e.g. an fd_set).
- */
- close(fd);
- }
-
- set_pname("calcsize");
+ char *dirname=NULL;
+ char *amname=NULL, *qamname=NULL;
+ char *filename=NULL, *qfilename = NULL;
+ safe_fd(-1, 0);
safe_cd();
- malloc_size_1 = malloc_inuse(&malloc_hist_1);
+ set_pname("calcsize");
+
+ dbopen(DBG_SUBDIR_CLIENT);
+ config_init(CONFIG_INIT_CLIENT, NULL);
+ dbprintf(_("version %s\n"), VERSION);
-#if 0
- erroutput_type = (ERR_INTERACTIVE|ERR_SYSLOG);
+ /* drop root privileges; we'll regain them for the required operations */
+#ifdef WANT_SETUID_CLIENT
+ check_running_as(RUNNING_AS_CLIENT_LOGIN | RUNNING_AS_UID_ONLY);
+ if (!set_root_privs(0)) {
+ error(_("calcsize must be run setuid root"));
+ }
+#else
+ check_running_as(RUNNING_AS_CLIENT_LOGIN);
#endif
argc--, argv++; /* skip program name */
/* need at least program, amname, and directory name */
- if(argc < 3) {
-#ifdef BUILTIN_EXCLUDE_SUPPORT
- usage:
-#endif
- error("Usage: %s [DUMP|GNUTAR%s] name dir [level date] ...",
- get_pname(),
-#ifdef BUILTIN_EXCLUDE_SUPPORT
- " [-X --exclude[-list]=regexp]"
-#else
- ""
-#endif
- );
- return 1;
+ if(argc < 4) {
+ error(_("Usage: %s config [DUMP|STAR|GNUTAR] name dir [-X exclude-file] [-I include-file] [level date]*"),
+ get_pname());
+ /*NOTREACHED*/
}
+ dbprintf(_("config: %s\n"), *argv);
+ if (strcmp(*argv, "NOCONFIG") != 0) {
+ dbrename(*argv, DBG_SUBDIR_CLIENT);
+ }
+ argc--;
+ argv++;
+
/* parse backup program name */
if(strcmp(*argv, "DUMP") == 0) {
#if !defined(DUMP) && !defined(XFSDUMP)
error("dump not available on this system");
- return 1;
+ /*NOTREACHED*/
#else
+ add_file_name = add_file_name_dump;
add_file = add_file_dump;
final_size = final_size_dump;
#endif
else if(strcmp(*argv, "GNUTAR") == 0) {
#ifndef GNUTAR
error("gnutar not available on this system");
- return 1;
+ /*NOTREACHED*/
#else
+ add_file_name = add_file_name_gnutar;
add_file = add_file_gnutar;
final_size = final_size_gnutar;
-#ifdef BUILTIN_EXCLUDE_SUPPORT
use_gtar_excl++;
-#endif
#endif
}
else {
+ add_file_name = add_file_name_unknown;
add_file = add_file_unknown;
final_size = final_size_unknown;
}
argc--, argv++;
-#ifdef BUILTIN_EXCLUDE_SUPPORT
- if ((argc > 1) && strcmp(*argv,"-X") == 0) {
- char *result = NULL;
- char *cp = NULL;
- argv++;
-
- if (!use_gtar_excl) {
- error("exclusion specification not supported");
- return 1;
- }
-
- result = stralloc(*argv);
- if (*result && (cp = strrchr(result,';')))
- /* delete trailing ; */
- *cp = 0;
- if (strncmp(result, exclude_string, sizeof(exclude_string)-1) == 0)
- add_exclude(result+sizeof(exclude_string)-1);
- else if (strncmp(result, exclude_list_string,
- sizeof(exclude_list_string)-1) == 0) {
- if (access(result + sizeof(exclude_list_string)-1, R_OK) != 0) {
- fprintf(stderr,"Cannot open exclude file %s\n",cp+1);
- use_gtar_excl = 0;
- } else {
- add_exclude_file(result + sizeof(exclude_list_string)-1);
- }
- } else {
- amfree(result);
- goto usage;
- }
- amfree(result);
- argc -= 2;
- argv++;
- } else
- use_gtar_excl = 0;
-#endif
/* the amanda name can be different from the directory name */
if (argc > 0) {
amname = *argv;
+ qamname = quote_string(amname);
argc--, argv++;
- } else
+ } else {
error("missing <name>");
+ /*NOTREACHED*/
+ }
/* the toplevel directory name to search from */
if (argc > 0) {
dirname = *argv;
argc--, argv++;
- } else
+ } else {
error("missing <dir>");
+ /*NOTREACHED*/
+ }
+
+ if ((argc > 1) && strcmp(*argv,"-X") == 0) {
+ argv++;
+
+ if (!(use_gtar_excl || use_star_excl)) {
+ error("exclusion specification not supported");
+ /*NOTREACHED*/
+ }
+
+ filename = stralloc(*argv);
+ qfilename = quote_string(filename);
+ if (access(filename, R_OK) != 0) {
+ g_fprintf(stderr,"Cannot open exclude file %s\n", qfilename);
+ use_gtar_excl = use_star_excl = 0;
+ } else {
+ exclude_sl = calc_load_file(filename);
+ if (!exclude_sl) {
+ g_fprintf(stderr,"Cannot open exclude file %s: %s\n", qfilename,
+ strerror(errno));
+ use_gtar_excl = use_star_excl = 0;
+ }
+ }
+ amfree(qfilename);
+ amfree(filename);
+ argc -= 2;
+ argv++;
+ } else {
+ use_gtar_excl = use_star_excl = 0;
+ }
+
+ if ((argc > 1) && strcmp(*argv,"-I") == 0) {
+ argv++;
+
+ filename = stralloc(*argv);
+ qfilename = quote_string(filename);
+ if (access(filename, R_OK) != 0) {
+ g_fprintf(stderr,"Cannot open include file %s\n", qfilename);
+ use_gtar_excl = use_star_excl = 0;
+ } else {
+ include_sl = calc_load_file(filename);
+ if (!include_sl) {
+ g_fprintf(stderr,"Cannot open include file %s: %s\n", qfilename,
+ strerror(errno));
+ use_gtar_excl = use_star_excl = 0;
+ }
+ }
+ amfree(qfilename);
+ amfree(filename);
+ argc -= 2;
+ argv++;
+ }
/* the dump levels to calculate sizes for */
}
}
- if(argc)
+ if(argc) {
error("leftover arg \"%s\", expected <level> and <date>", *argv);
+ /*NOTREACHED*/
+ }
- traverse_dirs(dirname);
+ if(is_empty_sl(include_sl)) {
+ traverse_dirs(dirname,".");
+ }
+ else {
+ sle_t *an_include = include_sl->first;
+ while(an_include != NULL) {
+/*
+ char *adirname = stralloc2(dirname, an_include->name+1);
+ traverse_dirs(adirname);
+ amfree(adirname);
+*/
+ traverse_dirs(dirname, an_include->name);
+ an_include = an_include->next;
+ }
+ }
for(i = 0; i < ndumps; i++) {
amflock(1, "size");
- lseek(1, (off_t)0, SEEK_END);
-
- printf("%s %d SIZE %ld\n",
- amname, dumplevel[i], final_size(i, dirname));
- fflush(stdout);
+ dbprintf("calcsize: %s %d SIZE %lld\n",
+ qamname, dumplevel[i],
+ (long long)final_size(i, dirname));
+ g_fprintf(stderr, "%s %d SIZE %lld\n",
+ qamname, dumplevel[i],
+ (long long)final_size(i, dirname));
+ fflush(stderr);
amfunlock(1, "size");
}
-
- malloc_size_2 = malloc_inuse(&malloc_hist_2);
-
- if(malloc_size_1 != malloc_size_2) {
- malloc_list(fileno(stderr), malloc_hist_1, malloc_hist_2);
- }
+ amfree(qamname);
return 0;
#endif
* =========================================================================
*/
-#ifndef HAVE_BASENAME
-char *basename(file)
-char *file;
+#if !defined(HAVE_BASENAME) && defined(BUILTIN_EXCLUDE_SUPPORT)
+
+static char *
+basename(
+ char * file)
{
char *cp;
}
#endif
-void push_name P((char *str));
-char *pop_name P((void));
+void push_name(char *str);
+char *pop_name(void);
-void traverse_dirs(parent_dir)
-char *parent_dir;
+void
+traverse_dirs(
+ char * parent_dir,
+ char * include)
{
DIR *d;
struct dirent *f;
struct stat finfo;
char *dirname, *newname = NULL;
char *newbase = NULL;
- dev_t parent_dev = 0;
+ dev_t parent_dev = (dev_t)0;
int i;
- int l;
+ size_t l;
+ size_t parent_len;
+ int has_exclude;
+ char *aparent;
+
+ if(parent_dir == NULL || include == NULL)
+ return;
- if(parent_dir && stat(parent_dir, &finfo) != -1)
+ has_exclude = !is_empty_sl(exclude_sl) && (use_gtar_excl || use_star_excl);
+ aparent = vstralloc(parent_dir, "/", include, NULL);
+
+ /* We (may) need root privs for the *stat() calls here. */
+ set_root_privs(1);
+ if(stat(parent_dir, &finfo) != -1)
parent_dev = finfo.st_dev;
- push_name(parent_dir);
+ parent_len = strlen(parent_dir);
- for(dirname = pop_name(); dirname; free(dirname), dirname = pop_name()) {
+ push_name(aparent);
-#ifdef BUILTIN_EXCLUDE_SUPPORT
- if(use_gtar_excl &&
- (check_exclude(basename(dirname)) ||
- check_exclude(dirname)))
- /* will not be added by gnutar */
+ for(; (dirname = pop_name()) != NULL; free(dirname)) {
+ if(has_exclude && calc_check_exclude(dirname+parent_len+1)) {
continue;
-#endif
-
+ }
if((d = opendir(dirname)) == NULL) {
perror(dirname);
continue;
}
while((f = readdir(d)) != NULL) {
+ int is_symlink = 0;
+ int is_dir;
+ int is_file;
if(is_dot_or_dotdot(f->d_name)) {
continue;
}
newname = newstralloc2(newname, newbase, f->d_name);
if(lstat(newname, &finfo) == -1) {
- fprintf(stderr, "%s/%s: %s\n",
+ g_fprintf(stderr, "%s/%s: %s\n",
dirname, f->d_name, strerror(errno));
continue;
}
- if(finfo.st_dev != parent_dev) {
+ if(finfo.st_dev != parent_dev)
continue;
- }
- if((finfo.st_mode & S_IFMT) == S_IFDIR) {
- push_name(newname);
- }
-
- for(i = 0; i < ndumps; i++) {
- if(finfo.st_ctime >= dumpdate[i]) {
- int exclude = 0;
- int is_symlink = 0;
-
-#ifdef BUILTIN_EXCLUDE_SUPPORT
- exclude = check_exclude(f->d_name);
-#endif
#ifdef S_IFLNK
- is_symlink = ((finfo.st_mode & S_IFMT) == S_IFLNK);
+ is_symlink = ((finfo.st_mode & S_IFMT) == S_IFLNK);
#endif
- if (! exclude &&
- /* regular files */
- ((finfo.st_mode & S_IFMT) == S_IFREG
- /* directories */
- || (finfo.st_mode & S_IFMT) == S_IFDIR
- /* symbolic links */
- || is_symlink)) {
+ is_dir = ((finfo.st_mode & S_IFMT) == S_IFDIR);
+ is_file = ((finfo.st_mode & S_IFMT) == S_IFREG);
+
+ if (!(is_file || is_dir || is_symlink)) {
+ continue;
+ }
+
+ {
+ int is_excluded = -1;
+ for(i = 0; i < ndumps; i++) {
+ add_file_name(i, newname);
+ if(is_file && (time_t)finfo.st_ctime >= dumpdate[i]) {
+
+ if(has_exclude) {
+ if(is_excluded == -1)
+ is_excluded =
+ calc_check_exclude(newname+parent_len+1);
+ if(is_excluded == 1) {
+ i = ndumps;
+ continue;
+ }
+ }
add_file(i, &finfo);
}
}
+ if(is_dir) {
+ if(has_exclude && calc_check_exclude(newname+parent_len+1))
+ continue;
+ push_name(newname);
+ }
}
}
perror(dirname);
#endif
}
+
+ /* drop root privs -- we're done with the permission-sensitive calls */
+ set_root_privs(0);
+
amfree(newbase);
amfree(newname);
+ amfree(aparent);
}
-void push_name(str)
-char *str;
+void
+push_name(
+ char * str)
{
Name *newp;
- newp = alloc(sizeof(*newp));
+ newp = alloc(SIZEOF(*newp));
newp->str = stralloc(str);
newp->next = name_stack;
name_stack = newp;
}
-char *pop_name()
+char *
+pop_name(void)
{
Name *newp = name_stack;
char *str;
* requirements for files with holes, nor the dumping of directories that
* are not themselves modified.
*/
-void add_file_dump(level, sp)
-int level;
-struct stat *sp;
+void
+add_file_name_dump(
+ int level,
+ char * name)
+{
+ (void)level; /* Quiet unused parameter warning */
+ (void)name; /* Quiet unused parameter warning */
+
+ return;
+}
+
+void
+add_file_dump(
+ int level,
+ struct stat * sp)
{
/* keep the size in kbytes, rounded up, plus a 1k header block */
if((sp->st_mode & S_IFMT) == S_IFREG || (sp->st_mode & S_IFMT) == S_IFDIR)
- dumpstats[level].total_size += (ST_BLOCKS(*sp) + 1)/2 + 1;
+ dumpstats[level].total_size +=
+ (ST_BLOCKS(*sp) + (off_t)1) / (off_t)2 + (off_t)1;
}
-long final_size_dump(level, topdir)
-int level;
-char *topdir;
+off_t
+final_size_dump(
+ int level,
+ char * topdir)
{
- generic_fs_stats_t stats;
- int mapsize;
+ struct fs_usage fsusage;
+ off_t mapsize;
char *s;
/* calculate the map sizes */
s = stralloc2(topdir, "/.");
- if(get_fs_stats(s, &stats) == -1) {
+ if(get_fs_usage(s, NULL, &fsusage) == -1) {
error("statfs %s: %s", s, strerror(errno));
+ /*NOTREACHED*/
}
amfree(s);
- mapsize = (stats.files + 7) / 8; /* in bytes */
- mapsize = (mapsize + 1023) / 1024; /* in kbytes */
+ mapsize = (fsusage.fsu_files + (off_t)7) / (off_t)8; /* in bytes */
+ mapsize = (mapsize + (off_t)1023) / (off_t)1024; /* in kbytes */
/* the dump contains three maps plus the files */
- return 3*mapsize + dumpstats[level].total_size;
+ return (mapsize * (off_t)3) + dumpstats[level].total_size;
}
/*
*
* As with DUMP, we only need a reasonable estimate, not an exact figure.
*/
-void add_file_gnutar(level, sp)
-int level;
-struct stat *sp;
+void
+add_file_name_gnutar(
+ int level,
+ char * name)
+{
+ (void)name; /* Quiet unused parameter warning */
+
+/* dumpstats[level].total_size_name += strlen(name) + 64;*/
+ dumpstats[level].total_size += (off_t)1;
+}
+
+void
+add_file_gnutar(
+ int level,
+ struct stat * sp)
{
/* the header takes one additional block */
- dumpstats[level].total_size += ROUND(4,(ST_BLOCKS(*sp) + 1));
+ dumpstats[level].total_size += ST_BLOCKS(*sp);
}
-long final_size_gnutar(level, topdir)
-int level;
-char *topdir;
+off_t
+final_size_gnutar(
+ int level,
+ char * topdir)
{
+ (void)topdir; /* Quiet unused parameter warning */
+
/* divide by two to get kbytes, rounded up */
/* + 4 blocks for security */
- return (dumpstats[level].total_size + 5) / 2;
+ return (dumpstats[level].total_size + (off_t)5 +
+ (dumpstats[level].total_size_name/(off_t)512)) / (off_t)2;
}
/*
* Here we'll just add up the file sizes and output that.
*/
-void add_file_unknown(level, sp)
-int level;
-struct stat *sp;
+void
+add_file_name_unknown(
+ int level,
+ char * name)
+{
+ (void)level; /* Quiet unused parameter warning */
+ (void)name; /* Quiet unused parameter warning */
+
+ return;
+}
+
+void
+add_file_unknown(
+ int level,
+ struct stat * sp)
{
/* just add up the block counts */
if((sp->st_mode & S_IFMT) == S_IFREG || (sp->st_mode & S_IFMT) == S_IFDIR)
dumpstats[level].total_size += ST_BLOCKS(*sp);
}
-long final_size_unknown(level, topdir)
-int level;
-char *topdir;
+off_t
+final_size_unknown(
+ int level,
+ char * topdir)
{
+ (void)topdir; /* Quiet unused parameter warning */
+
/* divide by two to get kbytes, rounded up */
- return (dumpstats[level].total_size + 1) / 2;
+ return (dumpstats[level].total_size + (off_t)1) / (off_t)2;
+}
+
+/*
+ * =========================================================================
+ */
+sl_t *
+calc_load_file(
+ char * filename)
+{
+ char pattern[1025];
+
+ sl_t *sl_list;
+
+ FILE *file = fopen(filename, "r");
+
+ if (!file) {
+ return NULL;
+ }
+
+ sl_list = new_sl();
+
+ while(fgets(pattern, 1025, file)) {
+ if(strlen(pattern)>0 && pattern[strlen(pattern)-1] == '\n')
+ pattern[strlen(pattern)-1] = '\0';
+ sl_list = append_sl(sl_list, pattern);
+ }
+ fclose(file);
+
+ return sl_list;
+}
+
+int
+calc_check_exclude(
+ char * filename)
+{
+ sle_t *an_exclude;
+ if(is_empty_sl(exclude_sl)) return 0;
+
+ an_exclude=exclude_sl->first;
+ while(an_exclude != NULL) {
+ if(match_tar(an_exclude->name, filename)) {
+ return 1;
+ }
+ an_exclude=an_exclude->next;
+ }
+ return 0;
}