1/*	$NetBSD: create.c,v 1.72 2013/10/17 17:22:59 christos Exp $	*/
2
3/*-
4 * Copyright (c) 1989, 1993
5 *	The Regents of the University of California.  All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the University nor the names of its contributors
16 *    may be used to endorse or promote products derived from this software
17 *    without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32#if HAVE_NBTOOL_CONFIG_H
33#include "nbtool_config.h"
34#endif
35
36#include <sys/cdefs.h>
37#if defined(__RCSID) && !defined(lint)
38#if 0
39static char sccsid[] = "@(#)create.c	8.1 (Berkeley) 6/6/93";
40#else
41__RCSID("$NetBSD: create.c,v 1.72 2013/10/17 17:22:59 christos Exp $");
42#endif
43#endif /* not lint */
44
45#include <sys/param.h>
46#include <sys/stat.h>
47
48#if ! HAVE_NBTOOL_CONFIG_H
49#include <dirent.h>
50#endif
51
52#include <errno.h>
53#include <fcntl.h>
54#include <grp.h>
55#include <pwd.h>
56#include <stdio.h>
57#include <stdarg.h>
58#include <stdint.h>
59#include <stdlib.h>
60#include <string.h>
61#include <time.h>
62#include <unistd.h>
63
64#ifndef NO_MD5
65#include <md5.h>
66#endif
67#ifndef NO_RMD160
68#include <rmd160.h>
69#endif
70#ifndef NO_SHA1
71#include <sha1.h>
72#endif
73#ifndef NO_SHA2
74#include <sha2.h>
75#endif
76
77#include "extern.h"
78
79#define	INDENTNAMELEN	15
80#define	MAXLINELEN	80
81
82static gid_t gid;
83static uid_t uid;
84static mode_t mode;
85static u_long flags;
86
87#ifdef __FreeBSD__
88#define	FTS_CONST const
89#else
90#define	FTS_CONST
91#endif
92
93static int	dcmp(const FTSENT *FTS_CONST *, const FTSENT *FTS_CONST *);
94static void	output(int, int *, const char *, ...)
95	__attribute__((__format__(__printf__, 3, 4)));
96static int	statd(FTS *, FTSENT *, uid_t *, gid_t *, mode_t *, u_long *);
97static void	statf(int, FTSENT *);
98
99void
100cwalk(void)
101{
102	FTS *t;
103	FTSENT *p;
104	time_t clocktime;
105	char host[MAXHOSTNAMELEN + 1];
106	const char *user;
107	char *argv[2];
108	char  dot[] = ".";
109	int indent = 0;
110
111	argv[0] = dot;
112	argv[1] = NULL;
113
114	time(&clocktime);
115	gethostname(host, sizeof(host));
116	host[sizeof(host) - 1] = '\0';
117	if ((user = getlogin()) == NULL) {
118		struct passwd *pw;
119		user = (pw = getpwuid(getuid())) == NULL ? pw->pw_name :
120		    "<unknown>";
121	}
122
123	if (!nflag)
124		printf(
125	    	    "#\t   user: %s\n#\tmachine: %s\n#\t   tree: %s\n"
126		    "#\t   date: %s",
127		    user, host, fullpath, ctime(&clocktime));
128
129	if ((t = fts_open(argv, ftsoptions, dcmp)) == NULL)
130		mtree_err("fts_open: %s", strerror(errno));
131	while ((p = fts_read(t)) != NULL) {
132		if (jflag)
133			indent = p->fts_level * 4;
134		if (check_excludes(p->fts_name, p->fts_path)) {
135			fts_set(t, p, FTS_SKIP);
136			continue;
137		}
138		if (!find_only(p->fts_path)) {
139			fts_set(t, p, FTS_SKIP);
140			continue;
141		}
142		switch(p->fts_info) {
143		case FTS_D:
144			if (!bflag)
145				printf("\n");
146			if (!nflag)
147				printf("# %s\n", p->fts_path);
148			statd(t, p, &uid, &gid, &mode, &flags);
149			statf(indent, p);
150			break;
151		case FTS_DP:
152			if (p->fts_level > 0)
153				if (!nflag)
154					printf("%*s# %s\n", indent, "",
155					    p->fts_path);
156			if (p->fts_level > 0 || flavor == F_FREEBSD9) {
157				printf("%*s..\n", indent, "");
158				if (!bflag)
159					printf("\n");
160			}
161			break;
162		case FTS_DNR:
163		case FTS_ERR:
164		case FTS_NS:
165			mtree_err("%s: %s",
166			    p->fts_path, strerror(p->fts_errno));
167			break;
168		default:
169			if (!dflag)
170				statf(indent, p);
171			break;
172
173		}
174	}
175	fts_close(t);
176	if (sflag && keys & F_CKSUM)
177		mtree_err("%s checksum: %u", fullpath, crc_total);
178}
179
180static void
181statf(int indent, FTSENT *p)
182{
183	u_int32_t len, val;
184	int fd, offset;
185	const char *name = NULL;
186#if !defined(NO_MD5) || !defined(NO_RMD160) || !defined(NO_SHA1) || !defined(NO_SHA2)
187	char *digestbuf;
188#endif
189
190	offset = printf("%*s%s%s", indent, "",
191	    S_ISDIR(p->fts_statp->st_mode) ? "" : "    ", vispath(p->fts_name));
192
193	if (offset > (INDENTNAMELEN + indent))
194		offset = MAXLINELEN;
195	else
196		offset += printf("%*s", (INDENTNAMELEN + indent) - offset, "");
197
198	if (!S_ISREG(p->fts_statp->st_mode) && (flavor == F_NETBSD6 || !dflag))
199		output(indent, &offset, "type=%s",
200		    inotype(p->fts_statp->st_mode));
201	if (keys & (F_UID | F_UNAME) && p->fts_statp->st_uid != uid) {
202		if (keys & F_UNAME &&
203		    (name = user_from_uid(p->fts_statp->st_uid, 1)) != NULL)
204			output(indent, &offset, "uname=%s", name);
205		if (keys & F_UID || (keys & F_UNAME && name == NULL))
206			output(indent, &offset, "uid=%u", p->fts_statp->st_uid);
207	}
208	if (keys & (F_GID | F_GNAME) && p->fts_statp->st_gid != gid) {
209		if (keys & F_GNAME &&
210		    (name = group_from_gid(p->fts_statp->st_gid, 1)) != NULL)
211			output(indent, &offset, "gname=%s", name);
212		if (keys & F_GID || (keys & F_GNAME && name == NULL))
213			output(indent, &offset, "gid=%u", p->fts_statp->st_gid);
214	}
215	if (keys & F_MODE && (p->fts_statp->st_mode & MBITS) != mode)
216		output(indent, &offset, "mode=%#o",
217		    p->fts_statp->st_mode & MBITS);
218	if (keys & F_DEV &&
219	    (S_ISBLK(p->fts_statp->st_mode) || S_ISCHR(p->fts_statp->st_mode)))
220		output(indent, &offset, "device=%#jx",
221		    (uintmax_t)p->fts_statp->st_rdev);
222	if (keys & F_NLINK && p->fts_statp->st_nlink != 1)
223		output(indent, &offset, "nlink=%u", p->fts_statp->st_nlink);
224	if (keys & F_SIZE &&
225	    (flavor == F_FREEBSD9 || S_ISREG(p->fts_statp->st_mode)))
226		output(indent, &offset, "size=%ju",
227		    (uintmax_t)p->fts_statp->st_size);
228	if (keys & F_TIME)
229#if defined(BSD4_4) && !defined(HAVE_NBTOOL_CONFIG_H)
230		output(indent, &offset, "time=%jd.%09ld",
231		    (intmax_t)p->fts_statp->st_mtimespec.tv_sec,
232		    p->fts_statp->st_mtimespec.tv_nsec);
233#else
234		output(indent, &offset, "time=%jd.%09ld",
235		    (intmax_t)p->fts_statp->st_mtime, (long)0);
236#endif
237	if (keys & F_CKSUM && S_ISREG(p->fts_statp->st_mode)) {
238		if ((fd = open(p->fts_accpath, O_RDONLY, 0)) < 0 ||
239		    crc(fd, &val, &len))
240			mtree_err("%s: %s", p->fts_accpath, strerror(errno));
241		close(fd);
242		output(indent, &offset, "cksum=%lu", (long)val);
243	}
244#ifndef NO_MD5
245	if (keys & F_MD5 && S_ISREG(p->fts_statp->st_mode)) {
246		if ((digestbuf = MD5File(p->fts_accpath, NULL)) == NULL)
247			mtree_err("%s: MD5File failed: %s", p->fts_accpath,
248			    strerror(errno));
249		output(indent, &offset, "%s=%s", MD5KEY, digestbuf);
250		free(digestbuf);
251	}
252#endif	/* ! NO_MD5 */
253#ifndef NO_RMD160
254	if (keys & F_RMD160 && S_ISREG(p->fts_statp->st_mode)) {
255		if ((digestbuf = RMD160File(p->fts_accpath, NULL)) == NULL)
256			mtree_err("%s: RMD160File failed: %s", p->fts_accpath,
257			    strerror(errno));
258		output(indent, &offset, "%s=%s", RMD160KEY, digestbuf);
259		free(digestbuf);
260	}
261#endif	/* ! NO_RMD160 */
262#ifndef NO_SHA1
263	if (keys & F_SHA1 && S_ISREG(p->fts_statp->st_mode)) {
264		if ((digestbuf = SHA1File(p->fts_accpath, NULL)) == NULL)
265			mtree_err("%s: SHA1File failed: %s", p->fts_accpath,
266			    strerror(errno));
267		output(indent, &offset, "%s=%s", SHA1KEY, digestbuf);
268		free(digestbuf);
269	}
270#endif	/* ! NO_SHA1 */
271#ifndef NO_SHA2
272	if (keys & F_SHA256 && S_ISREG(p->fts_statp->st_mode)) {
273		if ((digestbuf = SHA256_File(p->fts_accpath, NULL)) == NULL)
274			mtree_err("%s: SHA256_File failed: %s", p->fts_accpath,
275			    strerror(errno));
276		output(indent, &offset, "%s=%s", SHA256KEY, digestbuf);
277		free(digestbuf);
278	}
279#ifdef SHA384_BLOCK_LENGTH
280	if (keys & F_SHA384 && S_ISREG(p->fts_statp->st_mode)) {
281		if ((digestbuf = SHA384_File(p->fts_accpath, NULL)) == NULL)
282			mtree_err("%s: SHA384_File failed: %s", p->fts_accpath,
283			    strerror(errno));
284		output(indent, &offset, "%s=%s", SHA384KEY, digestbuf);
285		free(digestbuf);
286	}
287#endif
288	if (keys & F_SHA512 && S_ISREG(p->fts_statp->st_mode)) {
289		if ((digestbuf = SHA512_File(p->fts_accpath, NULL)) == NULL)
290			mtree_err("%s: SHA512_File failed: %s", p->fts_accpath,
291			    strerror(errno));
292		output(indent, &offset, "%s=%s", SHA512KEY, digestbuf);
293		free(digestbuf);
294	}
295#endif	/* ! NO_SHA2 */
296	if (keys & F_SLINK &&
297	    (p->fts_info == FTS_SL || p->fts_info == FTS_SLNONE))
298		output(indent, &offset, "link=%s",
299		    vispath(rlink(p->fts_accpath)));
300#if HAVE_STRUCT_STAT_ST_FLAGS
301	if (keys & F_FLAGS && p->fts_statp->st_flags != flags) {
302		char *str = flags_to_string(p->fts_statp->st_flags, "none");
303		output(indent, &offset, "flags=%s", str);
304		free(str);
305	}
306#endif
307	putchar('\n');
308}
309
310/* XXX
311 * FLAGS2INDEX will fail once the user and system settable bits need more
312 * than one byte, respectively.
313 */
314#define FLAGS2INDEX(x)  (((x >> 8) & 0x0000ff00) | (x & 0x000000ff))
315
316#define	MTREE_MAXGID	5000
317#define	MTREE_MAXUID	5000
318#define	MTREE_MAXMODE	(MBITS + 1)
319#if HAVE_STRUCT_STAT_ST_FLAGS
320#define	MTREE_MAXFLAGS  (FLAGS2INDEX(CH_MASK) + 1)   /* 1808 */
321#else
322#define MTREE_MAXFLAGS	1
323#endif
324#define	MTREE_MAXS 16
325
326static int
327statd(FTS *t, FTSENT *parent, uid_t *puid, gid_t *pgid, mode_t *pmode,
328      u_long *pflags)
329{
330	FTSENT *p;
331	gid_t sgid;
332	uid_t suid;
333	mode_t smode;
334	u_long sflags = 0;
335	const char *name = NULL;
336	gid_t savegid;
337	uid_t saveuid;
338	mode_t savemode;
339	u_long saveflags;
340	u_short maxgid, maxuid, maxmode, maxflags;
341	u_short g[MTREE_MAXGID], u[MTREE_MAXUID],
342		m[MTREE_MAXMODE], f[MTREE_MAXFLAGS];
343	static int first = 1;
344
345	savegid = *pgid;
346	saveuid = *puid;
347	savemode = *pmode;
348	saveflags = *pflags;
349	if ((p = fts_children(t, 0)) == NULL) {
350		if (errno)
351			mtree_err("%s: %s", RP(parent), strerror(errno));
352		return (1);
353	}
354
355	memset(g, 0, sizeof(g));
356	memset(u, 0, sizeof(u));
357	memset(m, 0, sizeof(m));
358	memset(f, 0, sizeof(f));
359
360	maxuid = maxgid = maxmode = maxflags = 0;
361	for (; p; p = p->fts_link) {
362		if (flavor == F_NETBSD6 || !dflag ||
363		    (dflag && S_ISDIR(p->fts_statp->st_mode))) {
364			smode = p->fts_statp->st_mode & MBITS;
365			if (smode < MTREE_MAXMODE && ++m[smode] > maxmode) {
366				savemode = smode;
367				maxmode = m[smode];
368			}
369			sgid = p->fts_statp->st_gid;
370			if (sgid < MTREE_MAXGID && ++g[sgid] > maxgid) {
371				savegid = sgid;
372				maxgid = g[sgid];
373			}
374			suid = p->fts_statp->st_uid;
375			if (suid < MTREE_MAXUID && ++u[suid] > maxuid) {
376				saveuid = suid;
377				maxuid = u[suid];
378			}
379
380#if HAVE_STRUCT_STAT_ST_FLAGS
381			sflags = FLAGS2INDEX(p->fts_statp->st_flags);
382			if (sflags < MTREE_MAXFLAGS && ++f[sflags] > maxflags) {
383				saveflags = p->fts_statp->st_flags;
384				maxflags = f[sflags];
385			}
386#endif
387		}
388	}
389	/*
390	 * If the /set record is the same as the last one we do not need to
391	 * output a new one.  So first we check to see if anything changed.
392	 * Note that we always output a /set record for the first directory.
393	 */
394	if (((keys & (F_UNAME | F_UID)) && (*puid != saveuid)) ||
395	    ((keys & (F_GNAME | F_GID)) && (*pgid != savegid)) ||
396	    ((keys & F_MODE) && (*pmode != savemode)) ||
397	    ((keys & F_FLAGS) && (*pflags != saveflags)) ||
398	    first) {
399		first = 0;
400		if (flavor != F_NETBSD6 && dflag)
401			printf("/set type=dir");
402		else
403			printf("/set type=file");
404		if (keys & (F_UID | F_UNAME)) {
405			if (keys & F_UNAME &&
406			    (name = user_from_uid(saveuid, 1)) != NULL)
407				printf(" uname=%s", name);
408			if (keys & F_UID || (keys & F_UNAME && name == NULL))
409				printf(" uid=%lu", (u_long)saveuid);
410		}
411		if (keys & (F_GID | F_GNAME)) {
412			if (keys & F_GNAME &&
413			    (name = group_from_gid(savegid, 1)) != NULL)
414				printf(" gname=%s", name);
415			if (keys & F_GID || (keys & F_GNAME && name == NULL))
416				printf(" gid=%lu", (u_long)savegid);
417		}
418		if (keys & F_MODE)
419			printf(" mode=%#lo", (u_long)savemode);
420		if (keys & F_NLINK)
421			printf(" nlink=1");
422		if (keys & F_FLAGS) {
423			char *str = flags_to_string(saveflags, "none");
424			printf(" flags=%s", str);
425			free(str);
426		}
427		printf("\n");
428		*puid = saveuid;
429		*pgid = savegid;
430		*pmode = savemode;
431		*pflags = saveflags;
432	}
433	return (0);
434}
435
436/*
437 * dcmp --
438 *	used as a comparison function passed to fts_open() to control
439 *	the order in which fts_read() returns results.	We make
440 *	directories sort after non-directories, but otherwise sort in
441 *	strcmp() order.
442 *
443 * Keep this in sync with nodecmp() in spec.c.
444 */
445static int
446dcmp(const FTSENT *FTS_CONST *a, const FTSENT *FTS_CONST *b)
447{
448
449	if (S_ISDIR((*a)->fts_statp->st_mode)) {
450		if (!S_ISDIR((*b)->fts_statp->st_mode))
451			return (1);
452	} else if (S_ISDIR((*b)->fts_statp->st_mode))
453		return (-1);
454	return (strcmp((*a)->fts_name, (*b)->fts_name));
455}
456
457void
458output(int indent, int *offset, const char *fmt, ...)
459{
460	va_list ap;
461	char buf[1024];
462
463	va_start(ap, fmt);
464	vsnprintf(buf, sizeof(buf), fmt, ap);
465	va_end(ap);
466
467	if (*offset + strlen(buf) > MAXLINELEN - 3) {
468		printf(" \\\n%*s", INDENTNAMELEN + indent, "");
469		*offset = INDENTNAMELEN + indent;
470	}
471	*offset += printf(" %s", buf) + 1;
472}
473