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