create.c revision 1.36 1 /* $NetBSD: create.c,v 1.36 2001/11/07 08:01:52 lukem 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. All advertising materials mentioning features or use of this software
16 * must display the following acknowledgement:
17 * This product includes software developed by the University of
18 * California, Berkeley and its contributors.
19 * 4. Neither the name of the University nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 */
35
36 #include <sys/cdefs.h>
37 #ifndef lint
38 #if 0
39 static char sccsid[] = "@(#)create.c 8.1 (Berkeley) 6/6/93";
40 #else
41 __RCSID("$NetBSD: create.c,v 1.36 2001/11/07 08:01:52 lukem Exp $");
42 #endif
43 #endif /* not lint */
44
45 #include <sys/param.h>
46 #include <sys/stat.h>
47
48 #include <dirent.h>
49 #include <errno.h>
50 #include <fcntl.h>
51 #include <fts.h>
52 #include <grp.h>
53 #include <md5.h>
54 #include <pwd.h>
55 #include <rmd160.h>
56 #include <sha1.h>
57 #include <stdarg.h>
58 #include <stdio.h>
59 #include <stdlib.h>
60 #include <string.h>
61 #include <time.h>
62 #include <unistd.h>
63 #include <vis.h>
64
65 #include "mtree.h"
66 #include "extern.h"
67
68 #define INDENTNAMELEN 15
69 #define MAXLINELEN 80
70 #define VISFLAGS VIS_CSTYLE
71
72 static gid_t gid;
73 static uid_t uid;
74 static mode_t mode;
75 static u_long flags;
76 static char codebuf[4*MAXPATHLEN + 1];
77 static const char extra[] = { ' ', '\t', '\n', '\\', '#', '\0' };
78
79 static int dsort(const FTSENT **, const FTSENT **);
80 static void output(int *, const char *, ...)
81 __attribute__((__format__(__printf__, 2, 3)));
82 static int statd(FTS *, FTSENT *, uid_t *, gid_t *, mode_t *, u_long *);
83 static void statf(FTSENT *);
84
85 void
86 cwalk(void)
87 {
88 FTS *t;
89 FTSENT *p;
90 time_t clocktime;
91 char host[MAXHOSTNAMELEN + 1];
92 char dot[] = "."; /* XXX: work around gcc warning */
93 char *argv[] = { dot, NULL };
94
95 time(&clocktime);
96 gethostname(host, sizeof(host));
97 host[sizeof(host) - 1] = '\0';
98 printf(
99 "#\t user: %s\n#\tmachine: %s\n#\t tree: %s\n#\t date: %s",
100 getlogin(), host, fullpath, ctime(&clocktime));
101
102 if ((t = fts_open(argv, ftsoptions, dsort)) == NULL)
103 mtree_err("fts_open: %s", strerror(errno));
104 while ((p = fts_read(t)) != NULL) {
105 if (check_excludes(p->fts_name, p->fts_path)) {
106 fts_set(t, p, FTS_SKIP);
107 continue;
108 }
109 switch(p->fts_info) {
110 case FTS_D:
111 printf("\n# %s\n", p->fts_path);
112 statd(t, p, &uid, &gid, &mode, &flags);
113 statf(p);
114 break;
115 case FTS_DP:
116 if (p->fts_level > 0)
117 printf("# %s\n..\n\n", p->fts_path);
118 break;
119 case FTS_DNR:
120 case FTS_ERR:
121 case FTS_NS:
122 mtree_err("%s: %s",
123 p->fts_path, strerror(p->fts_errno));
124 break;
125 default:
126 if (!dflag)
127 statf(p);
128 break;
129
130 }
131 }
132 fts_close(t);
133 if (sflag && keys & F_CKSUM)
134 mtree_err("%s checksum: %u\n", fullpath, crc_total);
135 }
136
137 static void
138 statf(FTSENT *p)
139 {
140 u_int32_t len, val;
141 int fd, indent;
142 char digestbuf[41]; /* large enough for {MD5,RMD160,SHA1}File() */
143 const char *name;
144
145 strsvis(codebuf, p->fts_name, VISFLAGS, extra);
146 if (S_ISDIR(p->fts_statp->st_mode))
147 indent = printf("%s", codebuf);
148 else
149 indent = printf(" %s", codebuf);
150
151 if (indent > INDENTNAMELEN)
152 indent = MAXLINELEN;
153 else
154 indent += printf("%*s", INDENTNAMELEN - indent, "");
155
156 if (!S_ISREG(p->fts_statp->st_mode))
157 output(&indent, "type=%s", inotype(p->fts_statp->st_mode));
158 if (keys & (F_UID | F_UNAME) && p->fts_statp->st_uid != uid) {
159 if (keys & F_UNAME &&
160 (name = user_from_uid(p->fts_statp->st_uid, 1)) != NULL)
161 output(&indent, "uname=%s", name);
162 else /* if (keys & F_UID) */
163 output(&indent, "uid=%u", p->fts_statp->st_uid);
164 }
165 if (keys & (F_GID | F_GNAME) && p->fts_statp->st_gid != gid) {
166 if (keys & F_GNAME &&
167 (name = group_from_gid(p->fts_statp->st_gid, 1)) != NULL)
168 output(&indent, "gname=%s", name);
169 else /* if (keys & F_GID) */
170 output(&indent, "gid=%u", p->fts_statp->st_gid);
171 }
172 if (keys & F_MODE && (p->fts_statp->st_mode & MBITS) != mode)
173 output(&indent, "mode=%#o", p->fts_statp->st_mode & MBITS);
174 if (keys & F_DEV &&
175 (S_ISBLK(p->fts_statp->st_mode) || S_ISCHR(p->fts_statp->st_mode)))
176 output(&indent, "device=%#x", p->fts_statp->st_rdev);
177 if (keys & F_NLINK && p->fts_statp->st_nlink != 1)
178 output(&indent, "nlink=%u", p->fts_statp->st_nlink);
179 if (keys & F_SIZE && S_ISREG(p->fts_statp->st_mode))
180 output(&indent, "size=%lld", (long long)p->fts_statp->st_size);
181 #ifdef BSD4_4
182 if (keys & F_TIME)
183 output(&indent, "time=%ld.%ld",
184 (long)p->fts_statp->st_mtimespec.tv_sec,
185 p->fts_statp->st_mtimespec.tv_nsec);
186 #else
187 output(&indent, "time=%ld.%ld",
188 p->fts_statp->st_mtime, 0);
189 #endif
190 if (keys & F_CKSUM && S_ISREG(p->fts_statp->st_mode)) {
191 if ((fd = open(p->fts_accpath, O_RDONLY, 0)) < 0 ||
192 crc(fd, &val, &len))
193 mtree_err("%s: %s", p->fts_accpath, strerror(errno));
194 close(fd);
195 output(&indent, "cksum=%lu", (long)val);
196 }
197 if (keys & F_MD5 && S_ISREG(p->fts_statp->st_mode)) {
198 if (MD5File(p->fts_accpath, digestbuf) == NULL)
199 mtree_err("%s: %s", p->fts_accpath, "MD5File");
200 output(&indent, "md5=%s", digestbuf);
201 }
202 if (keys & F_RMD160 && S_ISREG(p->fts_statp->st_mode)) {
203 if (RMD160File(p->fts_accpath, digestbuf) == NULL)
204 mtree_err("%s: %s", p->fts_accpath, "RMD160File");
205 output(&indent, "rmd160=%s", digestbuf);
206 }
207 if (keys & F_SHA1 && S_ISREG(p->fts_statp->st_mode)) {
208 if (SHA1File(p->fts_accpath, digestbuf) == NULL)
209 mtree_err("%s: %s", p->fts_accpath, "SHA1File");
210 output(&indent, "sha1=%s", digestbuf);
211 }
212 if (keys & F_SLINK &&
213 (p->fts_info == FTS_SL || p->fts_info == FTS_SLNONE))
214 output(&indent, "link=%s", rlink(p->fts_accpath));
215 if (keys & F_FLAGS && p->fts_statp->st_flags != flags)
216 output(&indent, "flags=%s",
217 flags_to_string(p->fts_statp->st_flags, "none"));
218 putchar('\n');
219 }
220
221 /* XXX
222 * FLAGS2INDEX will fail once the user and system settable bits need more
223 * than one byte, respectively.
224 */
225 #define FLAGS2INDEX(x) (((x >> 8) & 0x0000ff00) | (x & 0x000000ff))
226
227 #define MTREE_MAXGID 5000
228 #define MTREE_MAXUID 5000
229 #define MTREE_MAXMODE (MBITS + 1)
230 #define MTREE_MAXFLAGS (FLAGS2INDEX(CH_MASK) + 1) /* 1808 */
231 #define MTREE_MAXS 16
232
233 static int
234 statd(FTS *t, FTSENT *parent, uid_t *puid, gid_t *pgid, mode_t *pmode,
235 u_long *pflags)
236 {
237 FTSENT *p;
238 gid_t sgid;
239 uid_t suid;
240 mode_t smode;
241 u_long sflags;
242 const char *name;
243 gid_t savegid;
244 uid_t saveuid;
245 mode_t savemode;
246 u_long saveflags;
247 u_short maxgid, maxuid, maxmode, maxflags;
248 u_short g[MTREE_MAXGID], u[MTREE_MAXUID],
249 m[MTREE_MAXMODE], f[MTREE_MAXFLAGS];
250 static int first = 1;
251
252 savegid = *pgid;
253 saveuid = *puid;
254 savemode = *pmode;
255 saveflags = *pflags;
256 if ((p = fts_children(t, 0)) == NULL) {
257 if (errno)
258 mtree_err("%s: %s", RP(parent), strerror(errno));
259 return (1);
260 }
261
262 memset(g, 0, sizeof(g));
263 memset(u, 0, sizeof(u));
264 memset(m, 0, sizeof(m));
265 memset(f, 0, sizeof(f));
266
267 maxuid = maxgid = maxmode = maxflags = 0;
268 for (; p; p = p->fts_link) {
269 smode = p->fts_statp->st_mode & MBITS;
270 if (smode < MTREE_MAXMODE && ++m[smode] > maxmode) {
271 savemode = smode;
272 maxmode = m[smode];
273 }
274 sgid = p->fts_statp->st_gid;
275 if (sgid < MTREE_MAXGID && ++g[sgid] > maxgid) {
276 savegid = sgid;
277 maxgid = g[sgid];
278 }
279 suid = p->fts_statp->st_uid;
280 if (suid < MTREE_MAXUID && ++u[suid] > maxuid) {
281 saveuid = suid;
282 maxuid = u[suid];
283 }
284
285 sflags = FLAGS2INDEX(p->fts_statp->st_flags);
286 if (sflags < MTREE_MAXFLAGS && ++f[sflags] > maxflags) {
287 saveflags = p->fts_statp->st_flags;
288 maxflags = f[sflags];
289 }
290 }
291 /*
292 * If the /set record is the same as the last one we do not need to
293 * output a new one. So first we check to see if anything changed.
294 * Note that we always output a /set record for the first directory.
295 */
296 if (((keys & (F_UNAME | F_UID)) && (*puid != saveuid)) ||
297 ((keys & (F_GNAME | F_GID)) && (*pgid != savegid)) ||
298 ((keys & F_MODE) && (*pmode != savemode)) ||
299 ((keys & F_FLAGS) && (*pflags != saveflags)) ||
300 first) {
301 first = 0;
302 printf("/set type=file");
303 if (keys & (F_UID | F_UNAME)) {
304 if (keys & F_UNAME &&
305 (name = user_from_uid(saveuid, 1)) != NULL)
306 printf(" uname=%s", name);
307 else /* if (keys & F_UID) */
308 printf(" uid=%lu", (u_long)saveuid);
309 }
310 if (keys & (F_GID | F_GNAME)) {
311 if (keys & F_GNAME &&
312 (name = group_from_gid(savegid, 1)) != NULL)
313 printf(" gname=%s", name);
314 else /* if (keys & F_UID) */
315 printf(" gid=%lu", (u_long)savegid);
316 }
317 if (keys & F_MODE)
318 printf(" mode=%#lo", (u_long)savemode);
319 if (keys & F_NLINK)
320 printf(" nlink=1");
321 if (keys & F_FLAGS)
322 printf(" flags=%s",
323 flags_to_string(saveflags, "none"));
324 printf("\n");
325 *puid = saveuid;
326 *pgid = savegid;
327 *pmode = savemode;
328 *pflags = saveflags;
329 }
330 return (0);
331 }
332
333 static int
334 dsort(const FTSENT **a, const FTSENT **b)
335 {
336
337 if (S_ISDIR((*a)->fts_statp->st_mode)) {
338 if (!S_ISDIR((*b)->fts_statp->st_mode))
339 return (1);
340 } else if (S_ISDIR((*b)->fts_statp->st_mode))
341 return (-1);
342 return (strcmp((*a)->fts_name, (*b)->fts_name));
343 }
344
345 void
346 output(int *offset, const char *fmt, ...)
347 {
348 va_list ap;
349 char buf[1024];
350
351 va_start(ap, fmt);
352 vsnprintf(buf, sizeof(buf), fmt, ap);
353 va_end(ap);
354
355 if (*offset + strlen(buf) > MAXLINELEN - 3) {
356 printf(" \\\n%*s", INDENTNAMELEN, "");
357 *offset = INDENTNAMELEN;
358 }
359 *offset += printf(" %s", buf) + 1;
360 }
361