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