create.c revision 50479
1/*- 2 * Copyright (c) 1989, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. All advertising materials mentioning features or use of this software 14 * must display the following acknowledgement: 15 * This product includes software developed by the University of 16 * California, Berkeley and its contributors. 17 * 4. Neither the name of the University nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 34#ifndef lint 35#if 0 36static char sccsid[] = "@(#)create.c 8.1 (Berkeley) 6/6/93"; 37#endif 38static const char rcsid[] = 39 "$FreeBSD: head/usr.sbin/mtree/create.c 50479 1999-08-28 01:35:59Z peter $"; 40#endif /* not lint */ 41 42#include <sys/param.h> 43#include <sys/stat.h> 44#include <dirent.h> 45#include <err.h> 46#include <errno.h> 47#include <fcntl.h> 48#include <fts.h> 49#include <grp.h> 50#ifdef MD5 51#include <md5.h> 52#endif 53#ifdef SHA1 54#include <sha.h> 55#endif 56#ifdef RMD160 57#include <ripemd.h> 58#endif 59#include <pwd.h> 60#include <stdio.h> 61#include <time.h> 62#include <unistd.h> 63#include <vis.h> 64#include "mtree.h" 65#include "extern.h" 66 67#define INDENTNAMELEN 15 68#define MAXLINELEN 80 69 70extern long int crc_total; 71extern int ftsoptions; 72extern int dflag, iflag, nflag, sflag; 73extern u_int keys; 74extern char fullpath[MAXPATHLEN]; 75extern int lineno; 76 77static gid_t gid; 78static uid_t uid; 79static mode_t mode; 80 81static int dsort __P((const FTSENT **, const FTSENT **)); 82static void output __P((int, int *, const char *, ...)); 83static int statd __P((FTS *, FTSENT *, uid_t *, gid_t *, mode_t *)); 84static void statf __P((int, FTSENT *)); 85 86void 87cwalk() 88{ 89 register FTS *t; 90 register FTSENT *p; 91 time_t clock; 92 char *argv[2], host[MAXHOSTNAMELEN]; 93 int indent = 0; 94 95 (void)time(&clock); 96 (void)gethostname(host, sizeof(host)); 97 (void)printf( 98 "#\t user: %s\n#\tmachine: %s\n#\t tree: %s\n#\t date: %s", 99 getlogin(), host, fullpath, ctime(&clock)); 100 101 argv[0] = "."; 102 argv[1] = NULL; 103 if ((t = fts_open(argv, ftsoptions, dsort)) == NULL) 104 err(1, "line %d: fts_open", lineno); 105 while ((p = fts_read(t))) { 106 if (iflag) 107 indent = p->fts_level * 4; 108 switch(p->fts_info) { 109 case FTS_D: 110 if (!dflag) 111 (void)printf("\n"); 112 if (!nflag) 113 (void)printf("# %s\n", p->fts_path); 114 statd(t, p, &uid, &gid, &mode); 115 statf(indent, p); 116 break; 117 case FTS_DP: 118 if (!nflag && (p->fts_level > 0)) 119 (void)printf("%*s# %s\n", indent, "", p->fts_path); 120 (void)printf("%*s..\n", indent, ""); 121 if (!dflag) 122 (void)printf("\n"); 123 break; 124 case FTS_DNR: 125 case FTS_ERR: 126 case FTS_NS: 127 warnx("%s: %s", p->fts_path, strerror(p->fts_errno)); 128 break; 129 default: 130 if (!dflag) 131 statf(indent, p); 132 break; 133 134 } 135 } 136 (void)fts_close(t); 137 if (sflag && keys & F_CKSUM) 138 warnx("%s checksum: %lu", fullpath, crc_total); 139} 140 141static void 142statf(indent, p) 143 int indent; 144 FTSENT *p; 145{ 146 struct group *gr; 147 struct passwd *pw; 148 u_long len, val; 149 int fd, offset; 150 char *escaped_name; 151 152 escaped_name = calloc(1, p->fts_namelen * 4 + 1); 153 if (escaped_name == NULL) 154 errx(1, "statf(): calloc() failed"); 155 strvis(escaped_name, p->fts_name, VIS_WHITE | VIS_OCTAL); 156 157 if (iflag || S_ISDIR(p->fts_statp->st_mode)) 158 offset = printf("%*s%s", indent, "", escaped_name); 159 else 160 offset = printf("%*s %s", indent, "", escaped_name); 161 162 free(escaped_name); 163 164 if (offset > (INDENTNAMELEN + indent)) 165 offset = MAXLINELEN; 166 else 167 offset += printf("%*s", (INDENTNAMELEN + indent) - offset, ""); 168 169 if (!S_ISREG(p->fts_statp->st_mode) && !dflag) 170 output(indent, &offset, "type=%s", inotype(p->fts_statp->st_mode)); 171 if (p->fts_statp->st_uid != uid) { 172 if (keys & F_UNAME) { 173 if ((pw = getpwuid(p->fts_statp->st_uid)) != NULL) { 174 output(indent, &offset, "uname=%s", pw->pw_name); 175 } else { 176 errx(1, 177 "line %d: could not get uname for uid=%u", 178 lineno, p->fts_statp->st_uid); 179 } 180 } 181 if (keys & F_UID) 182 output(indent, &offset, "uid=%u", p->fts_statp->st_uid); 183 } 184 if (p->fts_statp->st_gid != gid) { 185 if (keys & F_GNAME) { 186 if ((gr = getgrgid(p->fts_statp->st_gid)) != NULL) { 187 output(indent, &offset, "gname=%s", gr->gr_name); 188 } else { 189 errx(1, 190 "line %d: could not get gname for gid=%u", 191 lineno, p->fts_statp->st_gid); 192 } 193 } 194 if (keys & F_GID) 195 output(indent, &offset, "gid=%u", p->fts_statp->st_gid); 196 } 197 if (keys & F_MODE && (p->fts_statp->st_mode & MBITS) != mode) 198 output(indent, &offset, "mode=%#o", p->fts_statp->st_mode & MBITS); 199 if (keys & F_NLINK && p->fts_statp->st_nlink != 1) 200 output(indent, &offset, "nlink=%u", p->fts_statp->st_nlink); 201 if (keys & F_SIZE) 202 output(indent, &offset, "size=%qd", p->fts_statp->st_size); 203 if (keys & F_TIME) 204 output(indent, &offset, "time=%ld.%ld", 205 p->fts_statp->st_mtimespec.tv_sec, 206 p->fts_statp->st_mtimespec.tv_nsec); 207 if (keys & F_CKSUM && S_ISREG(p->fts_statp->st_mode)) { 208 if ((fd = open(p->fts_accpath, O_RDONLY, 0)) < 0 || 209 crc(fd, &val, &len)) 210 err(1, "line %d: %s", lineno, p->fts_accpath); 211 (void)close(fd); 212 output(indent, &offset, "cksum=%lu", val); 213 } 214#ifdef MD5 215 if (keys & F_MD5 && S_ISREG(p->fts_statp->st_mode)) { 216 char *digest, buf[33]; 217 218 digest = MD5File(p->fts_accpath, buf); 219 if (!digest) { 220 err(1, "line %d: %s", lineno, p->fts_accpath); 221 } else { 222 output(indent, &offset, "md5digest=%s", digest); 223 } 224 } 225#endif /* MD5 */ 226#ifdef SHA1 227 if (keys & F_SHA1 && S_ISREG(p->fts_statp->st_mode)) { 228 char *digest, buf[41]; 229 230 digest = SHA1_File(p->fts_accpath, buf); 231 if (!digest) { 232 err(1, "line %d: %s", lineno, p->fts_accpath); 233 } else { 234 output(indent, &offset, "sha1digest=%s", digest); 235 } 236 } 237#endif /* SHA1 */ 238#ifdef RMD160 239 if (keys & F_RMD160 && S_ISREG(p->fts_statp->st_mode)) { 240 char *digest, buf[41]; 241 242 digest = RIPEMD160_File(p->fts_accpath, buf); 243 if (!digest) { 244 err(1, "line %d: %s", lineno, p->fts_accpath); 245 } else { 246 output(indent, &offset, "ripemd160digest=%s", digest); 247 } 248 } 249#endif /* RMD160 */ 250 if (keys & F_SLINK && 251 (p->fts_info == FTS_SL || p->fts_info == FTS_SLNONE)) 252 output(indent, &offset, "link=%s", rlink(p->fts_accpath)); 253 (void)putchar('\n'); 254} 255 256#define MAXGID 5000 257#define MAXUID 5000 258#define MAXMODE MBITS + 1 259 260static int 261statd(t, parent, puid, pgid, pmode) 262 FTS *t; 263 FTSENT *parent; 264 uid_t *puid; 265 gid_t *pgid; 266 mode_t *pmode; 267{ 268 register FTSENT *p; 269 register gid_t sgid; 270 register uid_t suid; 271 register mode_t smode; 272 struct group *gr; 273 struct passwd *pw; 274 gid_t savegid = *pgid; 275 uid_t saveuid = *puid; 276 mode_t savemode = *pmode; 277 u_short maxgid, maxuid, maxmode, g[MAXGID], u[MAXUID], m[MAXMODE]; 278 static int first = 1; 279 280 if ((p = fts_children(t, 0)) == NULL) { 281 if (errno) 282 err(1, "line %d: %s", lineno, RP(parent)); 283 return (1); 284 } 285 286 bzero(g, sizeof(g)); 287 bzero(u, sizeof(u)); 288 bzero(m, sizeof(m)); 289 290 maxuid = maxgid = maxmode = 0; 291 for (; p; p = p->fts_link) { 292 if (!dflag || (dflag && S_ISDIR(p->fts_statp->st_mode))) { 293 smode = p->fts_statp->st_mode & MBITS; 294 if (smode < MAXMODE && ++m[smode] > maxmode) { 295 savemode = smode; 296 maxmode = m[smode]; 297 } 298 sgid = p->fts_statp->st_gid; 299 if (sgid < MAXGID && ++g[sgid] > maxgid) { 300 savegid = sgid; 301 maxgid = g[sgid]; 302 } 303 suid = p->fts_statp->st_uid; 304 if (suid < MAXUID && ++u[suid] > maxuid) { 305 saveuid = suid; 306 maxuid = u[suid]; 307 } 308 } 309 } 310 /* 311 * If the /set record is the same as the last one we do not need to output 312 * a new one. So first we check to see if anything changed. Note that we 313 * always output a /set record for the first directory. 314 */ 315 if ((((keys & F_UNAME) | (keys & F_UID)) && (*puid != saveuid)) || 316 (((keys & F_GNAME) | (keys & F_GID)) && (*pgid != savegid)) || 317 ((keys & F_MODE) && (*pmode != savemode)) || (first)) { 318 first = 0; 319 if (dflag) 320 (void)printf("/set type=dir"); 321 else 322 (void)printf("/set type=file"); 323 if (keys & F_UNAME) 324 if ((pw = getpwuid(saveuid)) != NULL) 325 (void)printf(" uname=%s", pw->pw_name); 326 else 327 errx(1, 328 "line %d: could not get uname for uid=%u", 329 lineno, saveuid); 330 if (keys & F_UID) 331 (void)printf(" uid=%lu", (u_long)saveuid); 332 if (keys & F_GNAME) 333 if ((gr = getgrgid(savegid)) != NULL) 334 (void)printf(" gname=%s", gr->gr_name); 335 else 336 errx(1, 337 "line %d: could not get gname for gid=%u", 338 lineno, savegid); 339 if (keys & F_GID) 340 (void)printf(" gid=%lu", (u_long)savegid); 341 if (keys & F_MODE) 342 (void)printf(" mode=%#o", savemode); 343 if (keys & F_NLINK) 344 (void)printf(" nlink=1"); 345 (void)printf("\n"); 346 *puid = saveuid; 347 *pgid = savegid; 348 *pmode = savemode; 349 } 350 return (0); 351} 352 353static int 354dsort(a, b) 355 const FTSENT **a, **b; 356{ 357 if (S_ISDIR((*a)->fts_statp->st_mode)) { 358 if (!S_ISDIR((*b)->fts_statp->st_mode)) 359 return (1); 360 } else if (S_ISDIR((*b)->fts_statp->st_mode)) 361 return (-1); 362 return (strcmp((*a)->fts_name, (*b)->fts_name)); 363} 364 365#if __STDC__ 366#include <stdarg.h> 367#else 368#include <varargs.h> 369#endif 370 371void 372#if __STDC__ 373output(int indent, int *offset, const char *fmt, ...) 374#else 375output(indent, offset, fmt, va_alist) 376 int indent; 377 int *offset; 378 char *fmt; 379 va_dcl 380#endif 381{ 382 va_list ap; 383 char buf[1024]; 384#if __STDC__ 385 va_start(ap, fmt); 386#else 387 va_start(ap); 388#endif 389 (void)vsnprintf(buf, sizeof(buf), fmt, ap); 390 va_end(ap); 391 392 if (*offset + strlen(buf) > MAXLINELEN - 3) { 393 (void)printf(" \\\n%*s", INDENTNAMELEN + indent, ""); 394 *offset = INDENTNAMELEN + indent; 395 } 396 *offset += printf(" %s", buf) + 1; 397} 398