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