2 * summer - program for summarising (with md5 checksums) filesystem trees
5 * cat startpoints.list | summer >data.list
6 * summer startpoints... >data.list
7 * prints md5sum of data-list to stderr
10 * Copyright (C) 2003,2006-2007 Ian Jackson <ian@davenant.greenend.org.uk>
12 * This is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as
14 * published by the Free Software Foundation; either version 3,
15 * or (at your option) any later version.
17 * This is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public
23 * License along with this file; if not, consult the Free Software
24 * Foundation's website at www.fsf.org, or the GNU Project website at
32 #include <sys/types.h>
43 #include "nettle/md5-compat.h"
49 static int quiet=0, hidectime=0, hideatime=0, hidemtime=0;
50 static int hidedirsize=0, hidelinkmtime=0, hidextime=0, onefilesystem=0;
51 static int filenamefieldsep=' ';
54 #define nodeflag_fsvalid 1u
56 static void malloc_fail(void) { perror("summer: alloc failed"); exit(12); }
58 static void *mmalloc(size_t sz) {
60 r= malloc(sz); if (!r) malloc_fail();
64 static void *mrealloc(void *p, size_t sz) {
66 r= realloc(p, sz); if (!r && sz) malloc_fail();
70 static void fn_escaped(FILE *f, const char *fn) {
73 if (c>=33 && c<=126 && c!='\\') putc(c,f);
74 else fprintf(f,"\\x%02x",(int)(unsigned char)c);
78 static void add_pr(int *pr, int printf_ret) {
79 if (printf_ret == EOF) return;
83 static void vproblemx(const char *path, int padto, int per,
84 const char *fmt, va_list al) {
87 if (errfile==stderr) fputs("summer: error: ",stderr);
88 else add_pr(&pr, fprintf(errfile,"\\["));
90 add_pr(&pr, vfprintf(errfile,fmt,al));
91 if (per) add_pr(&pr, fprintf(errfile,": %s",strerror(e)));
93 if (errfile==stderr) {
95 fn_escaped(stderr,path);
100 add_pr(&pr, printf("]"));
106 static void problem_e(const char *path, int padto, const char *fmt, ...) {
109 vproblemx(path,padto,1,fmt,al);
113 static void problem(const char *path, int padto, const char *fmt, ...) {
116 vproblemx(path,padto,0,fmt,al);
120 static void csum_file(const char *path) {
124 unsigned char digest[16];
129 if (!f) { problem_e(path,sizeof(digest)*2,"open"); return; }
133 r= fread(db,1,sizeof(db),f);
135 problem_e(path,sizeof(digest)*2,"read");
138 if (!r) { assert(feof(f)); break; }
141 MD5Final(digest,&mc);
142 if (fclose(f)) { problem_e(path,sizeof(digest)*2,"close"); return; }
144 for (i=0; i<sizeof(digest); i++)
145 printf("%02x", digest[i]);
148 static void csum_dev(int cb, const struct stat *stab) {
149 printf("%c 0x%08lx %3lu %3lu %3lu %3lu ", cb,
150 (unsigned long)stab->st_rdev,
151 ((unsigned long)stab->st_rdev & 0x0ff000000U) >> 24,
152 ((unsigned long)stab->st_rdev & 0x000ff0000U) >> 16,
153 ((unsigned long)stab->st_rdev & 0x00000ff00U) >> 8,
154 ((unsigned long)stab->st_rdev & 0x0000000ffU) >> 0);
157 static void csum_str(const char *s) {
158 printf("%-*s", CSUMXL, s);
161 static void linktargpath(const char *linktarg) {
163 fn_escaped(stdout, linktarg);
166 static void pu10(void) { printf(" %10s", "?"); }
168 #define PTIME(stab, memb) ((stab) ? ptime((stab), (stab)->memb) : pu10())
170 static void ptime(const struct stat *stab, unsigned long val) {
173 if (!hidextime) goto justprint;
174 else if (S_ISCHR(stab->st_mode)) instead= "char";
175 else if (S_ISBLK(stab->st_mode)) instead= "block";
176 else if (S_ISLNK(stab->st_mode)) instead= "link";
177 else if (S_ISSOCK(stab->st_mode)) instead= "sock";
178 else if (S_ISFIFO(stab->st_mode)) instead= "pipe";
181 printf(" %10lu", val);
185 printf(" %10s",instead);
193 static void *hardlinks;
195 static int hardlink_compar(const void *av, const void *bv) {
196 const struct hardlink *a=av, *b=bv;
197 if (a->ino != b->ino) return b->ino - a->ino;
198 return b->dev - a->dev;
201 static void recurse(const char *path, unsigned nodeflags, dev_t fs);
203 static void node(const char *path, unsigned nodeflags, dev_t fs) {
204 char linktarg[MAXFN+1];
205 struct hardlink *foundhl;
206 const struct stat *stab;
210 r= lstat(path, &stabuf);
211 stab= r ? 0 : &stabuf;
214 if (stab && stab->st_nlink>1) {
215 struct hardlink *newhl, **foundhl_node;
216 newhl= mmalloc(sizeof(*newhl) + strlen(path));
217 newhl->dev= stab->st_dev;
218 newhl->ino= stab->st_ino;
219 foundhl_node= tsearch(newhl, &hardlinks, hardlink_compar);
220 if (!foundhl_node) malloc_fail();
221 foundhl= *foundhl_node;
222 if (foundhl!=newhl) {
223 free(newhl); /* hardlink to an earlier object */
225 foundhl= 0; /* new object with link count>1 */
226 strcpy(newhl->path, path);
231 if ((nodeflags & nodeflag_fsvalid) && stab->st_dev != fs)
234 nodeflags |= nodeflag_fsvalid;
237 if (!stab) problem_e(path,CSUMXL,"inaccessible");
238 else if (foundhl) csum_str("hardlink");
239 else if (S_ISREG(stab->st_mode)) csum_file(path);
240 else if (S_ISCHR(stab->st_mode)) csum_dev('c',stab);
241 else if (S_ISBLK(stab->st_mode)) csum_dev('b',stab);
242 else if (S_ISFIFO(stab->st_mode)) csum_str("pipe");
243 else if (S_ISLNK(stab->st_mode)) csum_str("symlink");
244 else if (S_ISSOCK(stab->st_mode)) csum_str("sock");
245 else if (S_ISDIR(stab->st_mode)) csum_str(mountpoint ? "mountpoint" : "dir");
246 else problem(path,CSUMXL,"badobj: 0x%lx", (unsigned long)stab->st_mode);
248 if (stab && S_ISLNK(stab->st_mode)) {
249 r= readlink(path, linktarg, sizeof(linktarg)-1);
250 if (r==sizeof(linktarg)) { problem(path,-1,"readlink too big"); r=-1; }
251 else if (r<0) { problem_e(path,-1,"readlink"); }
252 else assert(r<sizeof(linktarg));
254 if (r<0) strcpy(linktarg,"\\?");
259 if (S_ISDIR(stab->st_mode) && hidedirsize)
260 printf(" %10s","dir");
263 (unsigned long)stab->st_size);
265 printf(" %4o %10ld %10ld",
266 (unsigned)stab->st_mode & 07777U,
267 (unsigned long)stab->st_uid,
268 (unsigned long)stab->st_gid);
270 printf(" %10s %4s %10s %10s", "?","?","?","?");
274 PTIME(stab, st_atime);
277 if (stab && S_ISLNK(stab->st_mode) && hidelinkmtime)
278 printf(" %10s","link");
280 PTIME(stab, st_mtime);
284 PTIME(stab, st_ctime);
286 putchar(filenamefieldsep);
287 fn_escaped(stdout, path);
289 if (foundhl) linktargpath(foundhl->path);
290 if (stab && S_ISLNK(stab->st_mode)) linktargpath(linktarg);
294 if (ferror(stdout)) { perror("summer: stdout"); exit(12); }
296 if (stab && S_ISDIR(stab->st_mode) && !(mountpoint && onefilesystem))
297 recurse(path, nodeflags, fs);
300 static void process(const char *startpoint) {
302 fprintf(stderr,"summer: processing: %s\n",startpoint);
303 node(startpoint, 0,0);
304 tdestroy(hardlinks,free);
308 static int recurse_maxlen;
310 static int recurse_filter(const struct dirent *de) {
312 if (de->d_name[0]=='.' &&
314 (de->d_name[1]=='.' &&
317 l= strlen(de->d_name);
318 if (l > recurse_maxlen) recurse_maxlen= l;
322 static int recurse_compar(const void *av, const void *bv) {
323 const struct dirent *const *a=av, *const *b=bv;
324 return strcmp((*a)->d_name, (*b)->d_name);
327 static void recurse(const char *path_or_buf, unsigned nodeflags, dev_t fs) {
329 static int buf_allocd;
331 struct dirent **namelist, *const *de;
332 const char *path_or_0= path_or_buf==buf ? 0 : path_or_buf;
333 int nentries, pathl, esave, buf_want, i;
335 pathl= strlen(path_or_buf);
337 nentries= scandir(path_or_buf, &namelist, recurse_filter, recurse_compar);
340 buf_want= pathl+1+recurse_maxlen+1;
341 if (buf_want > buf_allocd) {
342 buf= mrealloc(buf, buf_want);
343 buf_allocd= buf_want;
345 /* NOTE that path_or_buf is invalid after this point because
346 * it might have been realloc'd ! */
347 if (path_or_0) strcpy(buf,path_or_0);
352 buf[pathl]= 0; errno= esave;
353 problem_e(buf,CSUMXL+72,"scandir failed");
354 fn_escaped(stdout,buf); putchar('\n');
357 for (i=0, de=namelist; i<nentries; i++, de++) {
358 strcpy(buf+pathl, (*de)->d_name);
359 node(buf, nodeflags, fs);
365 static void from_stdin(void) {
371 fprintf(stderr, "summer: processing stdin lines as startpoints\n");
373 s= fgets(buf,sizeof(buf),stdin);
374 if (ferror(stdin)) { perror("summer: stdin"); exit(12); }
375 if (!s) { if (feof(stdin)) return; else abort(); }
378 if (buf[l-1]!='\n') { fprintf(stderr,"summer: line too long\n"); exit(8); }
384 int main(int argc, const char *const *argv) {
390 while ((arg=argv[1]) && *arg++=='-') {
395 "summer: usage: summer startpoint... >data.list\n"
396 " cat startpoints.list | summer >data.list\n");
402 filenamefieldsep= '\t';
429 fprintf(stderr,"summer: bad usage, try -h\n");
440 fprintf(stderr, "summer: processing command line args as startpoints\n");
441 while ((arg=*++argv)) {
445 if (ferror(stdout) || fclose(stdout)) {
446 perror("summer: stdout (at end)"); exit(12);
449 fputs("summer: done.\n", stderr);