Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
att
GitHub Repository: att/ast
Path: blob/master/src/lib/libcmd/cksum.c
1808 views
1
/***********************************************************************
2
* *
3
* This software is part of the ast package *
4
* Copyright (c) 1992-2012 AT&T Intellectual Property *
5
* and is licensed under the *
6
* Eclipse Public License, Version 1.0 *
7
* by AT&T Intellectual Property *
8
* *
9
* A copy of the License is available at *
10
* http://www.eclipse.org/org/documents/epl-v10.html *
11
* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
12
* *
13
* Information and Software Systems Research *
14
* AT&T Research *
15
* Florham Park NJ *
16
* *
17
* Glenn Fowler <[email protected]> *
18
* David Korn <[email protected]> *
19
* *
20
***********************************************************************/
21
#pragma prototyped
22
/*
23
* Glenn Fowler
24
* AT&T Research
25
*
26
* sum -- list file checksum and size
27
*/
28
29
static const char usage[] =
30
"[-?\n@(#)$Id: sum (AT&T Research) 2012-04-20 $\n]"
31
USAGE_LICENSE
32
"[+NAME?cksum,md5sum,sum - print file checksum and block count]"
33
"[+DESCRIPTION?\bsum\b lists the checksum, and for most methods the block"
34
" count, for each file argument. The standard input is read if there are"
35
" no \afile\a arguments. \bgetconf UNIVERSE\b determines the default"
36
" \bsum\b method: \batt\b for the \batt\b universe, \bbsd\b otherwise."
37
" The default for the other commands is the command name itself. The"
38
" \batt\b method is a true sum, all others are order dependent.]"
39
"[+?Method names consist of a leading identifier and 0 or more options"
40
" separated by -.]"
41
"[+?\bgetconf PATH_RESOLVE\b determines how symbolic links are handled. This"
42
" can be explicitly overridden by the \b--logical\b, \b--metaphysical\b,"
43
" and \b--physical\b options below. \bPATH_RESOLVE\b can be one of:]{"
44
" [+logical?Follow all symbolic links.]"
45
" [+metaphysical?Follow command argument symbolic links,"
46
" otherwise don't follow.]"
47
" [+physical?Don't follow symbolic links.]"
48
"}"
49
50
"[a:all?List the checksum for all files. Use with \b--total\b to list both"
51
" individual and total checksums and block counts.]"
52
"[b:binary?Read files in binary mode. This is the default.]"
53
"[B:scale?Block count scale (bytes per block) override for methods that"
54
" include size in the output. The default is method specific.]#[scale]"
55
"[c:check?Each \afile\a is interpreted as the output from a previous \bsum\b."
56
" If \b--header\b or \b--permissions\b was specified in the previous"
57
" \bsum\b then the checksum method is automatically determined,"
58
" otherwise \b--method\b must be specified. The listed checksum is"
59
" compared with the current value and a warning is issued for each file"
60
" that does not match. If \afile\a was generated by \b--permissions\b"
61
" then the file mode, user and group are also checked. Empty lines,"
62
" lines starting with \b#<space>\b, or the line \b#\b are ignored. Lines"
63
" containing no blanks are interpreted as [no]]\aname\a[=\avalue\a]]"
64
" options:]{"
65
" [+method=name?Checksum method to apply to subsequent lines.]"
66
" [+permissions?Subsequent lines were generated with"
67
" \b--permissions\b.]"
68
"}"
69
"[h:header?Print the checksum method as the first output line. Used with"
70
" \b--check\b and \b--permissions\b.]"
71
"[l:list?Each \afile\a is interpreted as a list of files, one per line,"
72
" that is checksummed.]"
73
"[p:permissions?If \b--check\b is not specified then list the file"
74
" mode, user and group between the checksum and path. User and group"
75
" matching the caller are output as \b-\b. If \b--check\b is"
76
" specified then the mode, user and group for each path in \afile\a"
77
" are updated if necessary to match those in \afile\a. A warning is"
78
" printed on the standard error for each changed file.]"
79
"[R:recursive?Recursively checksum the contents of directories.]"
80
"[S:silent|status?No output for \b--check\b; 0 exit status means all sums"
81
" matched, non-0 means at least one sum failed to match. Ignored for"
82
" \b--permissions\b.]"
83
"[t:total?List only the total checksum and block count of all files."
84
" \b--all\b \b--total\b lists each checksum and the total. The"
85
" total checksum and block count may be different from the checksum"
86
" and block count of the catenation of all files due to partial"
87
" blocks that may occur when the files are treated separately.]"
88
"[T:text?Read files in text mode (i.e., treat \b\\r\\n\b as \b\\n\b).]"
89
"[w!:warn?Warn about invalid \b--check\b lines.]"
90
"[x:method|algorithm?Specifies the checksum \amethod\a to"
91
" apply. Parenthesized method options are readonly implementation"
92
" details.]:[method]{\fmethods\f}"
93
"[L:logical|follow?Follow symbolic links when traversing directories. The"
94
" default is determined by \bgetconf PATH_RESOLVE\b.]"
95
"[H:metaphysical?Follow command argument symbolic links, otherwise don't"
96
" follow symbolic links when traversing directories. The default is"
97
" determined by \bgetconf PATH_RESOLVE\b.]"
98
"[P:physical?Don't follow symbolic links when traversing directories. The"
99
" default is determined by \bgetconf PATH_RESOLVE\b.]"
100
"[r:bsd?Equivalent to \b--method=bsd --scale=512\b for compatibility with"
101
" other \bsum\b(1) implementations.]"
102
"[s:sysv?Equivalent to \b--method=sys5\b for compatibility with other"
103
" \bsum\b(1) implementations.]"
104
105
"\n"
106
"\n[ file ... ]\n"
107
"\n"
108
109
"[+SEE ALSO?\bgetconf\b(1), \btw\b(1), \buuencode\b(1)]"
110
;
111
112
#include <cmd.h>
113
#include <sum.h>
114
#include <ls.h>
115
#include <modex.h>
116
#include <fts_fix.h>
117
#include <error.h>
118
119
typedef struct State_s /* program state */
120
{
121
int all; /* list all items */
122
Sfio_t* check; /* check previous output */
123
int flags; /* sumprint() SUM_* flags */
124
gid_t gid; /* caller gid */
125
int header; /* list method on output */
126
int list; /* list file name too */
127
Sum_t* oldsum; /* previous sum method */
128
int permissions; /* include mode,uer,group */
129
int haveperm; /* permissions in the input */
130
int recursive; /* recursively descend dirs */
131
size_t scale; /* scale override */
132
unsigned long size; /* combined size of all files */
133
int silent; /* silent check, 0 exit if ok */
134
int (*sort)(FTSENT* const*, FTSENT* const*);
135
Sum_t* sum; /* sum method */
136
int text; /* \r\n == \n */
137
int total; /* list totals only */
138
uid_t uid; /* caller uid */
139
int warn; /* invalid check line warnings */
140
} State_t;
141
142
static void verify(State_t*, char*, char*, Sfio_t*);
143
144
/*
145
* open path for read mode
146
*/
147
148
static Sfio_t*
149
openfile(const char* path, const char* mode)
150
{
151
Sfio_t* sp;
152
153
if (!path || streq(path, "-") || streq(path, "/dev/stdin") || streq(path, "/dev/fd/0"))
154
{
155
sp = sfstdin;
156
sfopen(sp, NiL, mode);
157
}
158
else if (!(sp = sfopen(NiL, path, mode)))
159
error(ERROR_SYSTEM|2, "%s: cannot read", path);
160
return sp;
161
}
162
163
/*
164
* close an openfile() stream
165
*/
166
167
static int
168
closefile(Sfio_t* sp)
169
{
170
return sp == sfstdin ? 0 : sfclose(sp);
171
}
172
173
/*
174
* compute and print sum on an open file
175
*/
176
177
static void
178
pr(State_t* state, Sfio_t* op, Sfio_t* ip, char* file, int perm, struct stat* st, Sfio_t* check)
179
{
180
register char* p;
181
register char* r;
182
register char* e;
183
register int peek;
184
struct stat ss;
185
186
if (check)
187
{
188
state->oldsum = state->sum;
189
while (p = sfgetr(ip, '\n', 1))
190
verify(state, p, file, check);
191
state->sum = state->oldsum;
192
if (state->warn && !sfeof(ip))
193
error(2, "%s: last line incomplete", file);
194
return;
195
}
196
suminit(state->sum);
197
if (state->text)
198
{
199
peek = 0;
200
while (p = sfreserve(ip, SF_UNBOUND, 0))
201
{
202
e = p + sfvalue(ip);
203
if (peek)
204
{
205
peek = 0;
206
if (*p != '\n')
207
sumblock(state->sum, "\r", 1);
208
}
209
while (r = memchr(p, '\r', e - p))
210
{
211
if (++r >= e)
212
{
213
e--;
214
peek = 1;
215
break;
216
}
217
sumblock(state->sum, p, r - p - (*r == '\n'));
218
p = r;
219
}
220
sumblock(state->sum, p, e - p);
221
}
222
if (peek)
223
sumblock(state->sum, "\r", 1);
224
}
225
else
226
while (p = sfreserve(ip, SF_UNBOUND, 0))
227
sumblock(state->sum, p, sfvalue(ip));
228
if (sfvalue(ip))
229
error(ERROR_SYSTEM|2, "%s: read error", file);
230
sumdone(state->sum);
231
if (!state->total || state->all)
232
{
233
sumprint(state->sum, op, state->flags|SUM_SCALE, state->scale);
234
if (perm >= 0)
235
{
236
if (perm)
237
{
238
if (!st && fstat(sffileno(ip), st = &ss))
239
error(ERROR_SYSTEM|2, "%s: cannot stat", file);
240
else
241
sfprintf(sfstdout, " %04o %s %s",
242
modex(st->st_mode & S_IPERM),
243
(st->st_uid != state->uid && ((st->st_mode & S_ISUID) || (st->st_mode & S_IRUSR) && !(st->st_mode & (S_IRGRP|S_IROTH)) || (st->st_mode & S_IXUSR) && !(st->st_mode & (S_IXGRP|S_IXOTH)))) ? fmtuid(st->st_uid) : "-",
244
(st->st_gid != state->gid && ((st->st_mode & S_ISGID) || (st->st_mode & S_IRGRP) && !(st->st_mode & S_IROTH) || (st->st_mode & S_IXGRP) && !(st->st_mode & S_IXOTH))) ? fmtgid(st->st_gid) : "-");
245
}
246
if (ip != sfstdin)
247
sfprintf(op, " %s", file);
248
sfputc(op, '\n');
249
}
250
}
251
}
252
253
/*
254
* verify previous sum output
255
*/
256
257
static void
258
verify(State_t* state, register char* s, char* check, Sfio_t* rp)
259
{
260
register char* t;
261
char* e;
262
char* file;
263
int attr;
264
int mode;
265
int uid;
266
int gid;
267
Sfio_t* sp;
268
struct stat st;
269
270
if (!*s || *s == '#' && (!*(s + 1) || *(s + 1) == ' ' || *(s + 1) == '\t'))
271
return;
272
if (t = strchr(s, ' '))
273
{
274
if ((t - s) > 10 || !(file = strchr(t + 1, ' ')))
275
file = t;
276
*file++ = 0;
277
attr = 0;
278
if ((mode = strtol(file, &e, 8)) && *e == ' ' && (e - file) == 4)
279
{
280
mode = modei(mode);
281
if (t = strchr(++e, ' '))
282
{
283
if (*e == '-' && (t - e) == 1)
284
uid = -1;
285
else
286
{
287
*t = 0;
288
uid = struid(e);
289
*t = ' ';
290
}
291
if (e = strchr(++t, ' '))
292
{
293
if (*t == '-' && (e - t) == 1)
294
gid = -1;
295
else
296
{
297
*e = 0;
298
gid = struid(t);
299
*e = ' ';
300
}
301
file = e + 1;
302
attr = 1;
303
}
304
}
305
}
306
if (sp = openfile(file, "rb"))
307
{
308
pr(state, rp, sp, file, -1, NiL, NiL);
309
if (!(t = sfstruse(rp)))
310
error(ERROR_SYSTEM|3, "out of space");
311
if (!streq(s, t))
312
{
313
if (state->silent)
314
error_info.errors++;
315
else
316
error(2, "%s: checksum changed", file);
317
}
318
else if (attr)
319
{
320
if (fstat(sffileno(sp), &st))
321
{
322
if (state->silent)
323
error_info.errors++;
324
else
325
error(ERROR_SYSTEM|2, "%s: cannot stat", file);
326
}
327
else
328
{
329
if (uid < 0 || uid == st.st_uid)
330
uid = -1;
331
else if (!state->permissions)
332
{
333
if (state->silent)
334
error_info.errors++;
335
else
336
error(2, "%s: uid should be %s", file, fmtuid(uid));
337
}
338
if (gid < 0 || gid == st.st_gid)
339
gid = -1;
340
else if (!state->permissions)
341
{
342
if (state->silent)
343
error_info.errors++;
344
else
345
error(2, "%s: gid should be %s", file, fmtgid(gid));
346
}
347
if (state->permissions && (uid >= 0 || gid >= 0))
348
{
349
if (chown(file, uid, gid) < 0)
350
{
351
if (uid < 0)
352
error(ERROR_SYSTEM|2, "%s: cannot change group to %s", file, fmtgid(gid));
353
else if (gid < 0)
354
error(ERROR_SYSTEM|2, "%s: cannot change user to %s", file, fmtuid(uid));
355
else
356
error(ERROR_SYSTEM|2, "%s: cannot change user to %s and group to %s", file, fmtuid(uid), fmtgid(gid));
357
}
358
else
359
{
360
if (uid < 0)
361
error(1, "%s: changed group to %s", file, fmtgid(gid));
362
else if (gid < 0)
363
error(1, "%s: changed user to %s", file, fmtuid(uid));
364
else
365
error(1, "%s: changed user to %s and group to %s", file, fmtuid(uid), fmtgid(gid));
366
}
367
}
368
if ((st.st_mode & S_IPERM) ^ mode)
369
{
370
if (state->permissions)
371
{
372
if (chmod(file, mode) < 0)
373
error(ERROR_SYSTEM|2, "%s: cannot change mode to %s", file, fmtmode(mode, 0));
374
else
375
error(ERROR_SYSTEM|1, "%s: changed mode to %s", file, fmtmode(mode, 0));
376
}
377
else if (state->silent)
378
error_info.errors++;
379
else
380
error(2, "%s: mode should be %s", file, fmtmode(mode, 0));
381
}
382
}
383
}
384
closefile(sp);
385
}
386
}
387
else if (strneq(s, "method=", 7))
388
{
389
s += 7;
390
if (state->sum != state->oldsum)
391
sumclose(state->sum);
392
if (!(state->sum = sumopen(s)))
393
error(3, "%s: %s: unknown checksum method", check, s);
394
}
395
else if (streq(s, "permissions"))
396
state->haveperm = 1;
397
else
398
error(1, "%s: %s: unknown option", check, s);
399
}
400
401
/*
402
* sum the list of files in lp
403
*/
404
405
static void
406
list(State_t* state, register Sfio_t* lp)
407
{
408
register char* file;
409
register Sfio_t* sp;
410
411
while (file = sfgetr(lp, '\n', 1))
412
if (sp = openfile(file, state->check ? "rt" : "rb"))
413
{
414
pr(state, sfstdout, sp, file, state->permissions, NiL, state->check);
415
closefile(sp);
416
}
417
}
418
419
/*
420
* order child entries
421
*/
422
423
static int
424
order(FTSENT* const* f1, FTSENT* const* f2)
425
{
426
return strcoll((*f1)->fts_name, (*f2)->fts_name);
427
}
428
429
/*
430
* optget() info discipline function
431
*/
432
433
static int
434
optinfo(Opt_t* op, Sfio_t* sp, const char* s, Optdisc_t* dp)
435
{
436
if (streq(s, "methods"))
437
return sumusage(sp);
438
return 0;
439
}
440
441
int
442
b_cksum(int argc, register char** argv, Shbltin_t* context)
443
{
444
register int flags;
445
char* file;
446
char* method;
447
Sfio_t* sp;
448
FTS* fts;
449
FTSENT* ent;
450
int logical;
451
Optdisc_t optdisc;
452
State_t state;
453
454
cmdinit(argc, argv, context, ERROR_CATALOG, ERROR_NOTIFY);
455
memset(&state, 0, sizeof(state));
456
flags = fts_flags() | FTS_META | FTS_TOP | FTS_NOPOSTORDER;
457
state.flags = SUM_SIZE;
458
state.warn = 1;
459
logical = 1;
460
method = 0;
461
optinit(&optdisc, optinfo);
462
for (;;)
463
{
464
switch (optget(argv, usage))
465
{
466
case 'a':
467
state.all = 1;
468
continue;
469
case 'b':
470
state.text = 0;
471
continue;
472
case 'B':
473
state.scale = opt_info.num;
474
continue;
475
case 'c':
476
if (!(state.check = sfstropen()))
477
error(3, "out of space [check]");
478
continue;
479
case 'h':
480
state.header = 1;
481
continue;
482
case 'l':
483
state.list = 1;
484
continue;
485
case 'p':
486
state.permissions = 1;
487
continue;
488
case 'r':
489
method = "bsd";
490
state.scale = 512;
491
state.flags |= SUM_LEGACY;
492
continue;
493
case 'R':
494
flags &= ~FTS_TOP;
495
state.recursive = 1;
496
state.sort = order;
497
logical = 0;
498
continue;
499
case 's':
500
method = "sys5";
501
continue;
502
case 'S':
503
state.silent = opt_info.num;
504
continue;
505
case 't':
506
state.total = 1;
507
continue;
508
case 'w':
509
state.warn = opt_info.num;
510
continue;
511
case 'x':
512
method = opt_info.arg;
513
continue;
514
case 'H':
515
flags |= FTS_META|FTS_PHYSICAL;
516
logical = 0;
517
continue;
518
case 'L':
519
flags &= ~(FTS_META|FTS_PHYSICAL);
520
logical = 0;
521
continue;
522
case 'P':
523
flags &= ~FTS_META;
524
flags |= FTS_PHYSICAL;
525
logical = 0;
526
continue;
527
case 'T':
528
state.text = 1;
529
continue;
530
case '?':
531
error(ERROR_USAGE|4, "%s", opt_info.arg);
532
break;
533
case ':':
534
error(2, "%s", opt_info.arg);
535
break;
536
}
537
break;
538
}
539
argv += opt_info.index;
540
if (error_info.errors)
541
error(ERROR_USAGE|4, "%s", optusage(NiL));
542
543
/*
544
* check the method
545
*/
546
547
if (method && !(state.sum = sumopen(method)))
548
error(3, "%s: unknown checksum method", method);
549
if (!state.sum && !(state.sum = sumopen(error_info.id)) && !(state.sum = sumopen(astconf("UNIVERSE", NiL, NiL))))
550
state.sum = sumopen(NiL);
551
552
/*
553
* do it
554
*/
555
556
if (logical)
557
{
558
flags &= ~(FTS_META|FTS_PHYSICAL);
559
flags |= FTS_SEEDOTDIR;
560
}
561
if (state.permissions)
562
{
563
state.uid = geteuid();
564
state.gid = getegid();
565
state.silent = 0;
566
}
567
if (!state.check && (state.header || state.permissions))
568
{
569
sfprintf(sfstdout, "method=%s\n", state.sum->name);
570
if (state.permissions)
571
sfprintf(sfstdout, "permissions\n");
572
}
573
if (state.list)
574
{
575
if (*argv)
576
{
577
while (file = *argv++)
578
if (sp = openfile(file, "rt"))
579
{
580
list(&state, sp);
581
closefile(sp);
582
}
583
}
584
else if (sp = openfile(NiL, "rt"))
585
{
586
list(&state, sp);
587
closefile(sp);
588
}
589
}
590
else if (!*argv && !state.recursive)
591
pr(&state, sfstdout, sfstdin, "/dev/stdin", state.permissions, NiL, state.check);
592
else if (!(fts = fts_open(argv, flags, state.sort)))
593
error(ERROR_system(1), "%s: not found", *argv);
594
else
595
{
596
while (!sh_checksig(context) && (ent = fts_read(fts)))
597
switch (ent->fts_info)
598
{
599
case FTS_SL:
600
if (!(flags & FTS_PHYSICAL) || (flags & FTS_META) && ent->fts_level == 1)
601
fts_set(NiL, ent, FTS_FOLLOW);
602
break;
603
case FTS_F:
604
if (sp = openfile(ent->fts_accpath, "rb"))
605
{
606
pr(&state, sfstdout, sp, ent->fts_path, state.permissions, ent->fts_statp, state.check);
607
closefile(sp);
608
}
609
break;
610
case FTS_DC:
611
error(ERROR_warn(0), "%s: directory causes cycle", ent->fts_path);
612
break;
613
case FTS_DNR:
614
error(ERROR_system(0), "%s: cannot read directory", ent->fts_path);
615
break;
616
case FTS_DNX:
617
error(ERROR_system(0), "%s: cannot search directory", ent->fts_path);
618
break;
619
case FTS_NS:
620
error(ERROR_system(0), "%s: not found", ent->fts_path);
621
break;
622
}
623
fts_close(fts);
624
}
625
if (state.total)
626
{
627
sumprint(state.sum, sfstdout, state.flags|SUM_TOTAL|SUM_SCALE, state.scale);
628
sfputc(sfstdout, '\n');
629
}
630
sumclose(state.sum);
631
return error_info.errors != 0;
632
}
633
634