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 <openssl/md5.h> 58 #endif 59 #ifndef NO_RMD160 60 #include <openssl/ripemd.h> 61 #endif 62 #ifndef NO_SHA 63 #include <openssl/sha.h> 64 #endif 65 66 #include "extern.h" 67 68 #define INDENTNAMELEN 15 69 #define MAXLINELEN 80 70 71 static gid_t gid; 72 static uid_t uid; 73 static mode_t mode; 74 static u_long flags; 75 76 #if defined(__FreeBSD__) || defined(__DragonFly__) 77 #define FTS_CONST const 78 #else 79 #define FTS_CONST 80 #endif 81 82 static int dcmp(const FTSENT *FTS_CONST *, const FTSENT *FTS_CONST *); 83 static void output(FILE *, int, int *, const char *, ...) 84 __printflike(4, 5); 85 static int statd(FILE *, FTS *, FTSENT *, uid_t *, gid_t *, mode_t *, 86 u_long *); 87 static void statf(FILE *, int, FTSENT *); 88 89 void 90 cwalk(FILE *fp) 91 { 92 FTS *t; 93 FTSENT *p; 94 time_t clocktime; 95 char host[MAXHOSTNAMELEN + 1]; 96 const char *user; 97 char *argv[2]; 98 char dot[] = "."; 99 int indent = 0; 100 101 argv[0] = dot; 102 argv[1] = NULL; 103 104 time(&clocktime); 105 gethostname(host, sizeof(host)); 106 host[sizeof(host) - 1] = '\0'; 107 if ((user = getlogin()) == NULL) { 108 struct passwd *pw; 109 user = (pw = getpwuid(getuid())) != NULL ? pw->pw_name : 110 "<unknown>"; 111 } 112 113 if (!nflag) 114 fprintf(fp, 115 "#\t user: %s\n#\tmachine: %s\n#\t tree: %s\n" 116 "#\t date: %s", 117 user, host, fullpath, ctime(&clocktime)); 118 119 if ((t = fts_open(argv, ftsoptions, dcmp)) == NULL) 120 mtree_err("fts_open: %s", strerror(errno)); 121 while ((p = fts_read(t)) != NULL) { 122 if (jflag) 123 indent = p->fts_level * 4; 124 if (check_excludes(p->fts_name, p->fts_path)) { 125 fts_set(t, p, FTS_SKIP); 126 continue; 127 } 128 if (!find_only(p->fts_path)) { 129 fts_set(t, p, FTS_SKIP); 130 continue; 131 } 132 switch(p->fts_info) { 133 case FTS_D: 134 if (!bflag) 135 fprintf(fp, "\n"); 136 if (!nflag) 137 fprintf(fp, "# %s\n", p->fts_path); 138 statd(fp, t, p, &uid, &gid, &mode, &flags); 139 statf(fp, indent, p); 140 break; 141 case FTS_DP: 142 if (p->fts_level > 0) 143 if (!nflag) 144 fprintf(fp, "%*s# %s\n", indent, "", 145 p->fts_path); 146 if (p->fts_level > 0 || flavor == F_FREEBSD9) { 147 fprintf(fp, "%*s..\n", indent, ""); 148 if (!bflag) 149 fprintf(fp, "\n"); 150 } 151 break; 152 case FTS_DNR: 153 case FTS_ERR: 154 case FTS_NS: 155 mtree_err("%s: %s", 156 p->fts_path, strerror(p->fts_errno)); 157 break; 158 default: 159 if (!dflag) 160 statf(fp, indent, p); 161 break; 162 163 } 164 } 165 fts_close(t); 166 if (sflag && keys & F_CKSUM) 167 mtree_err("%s checksum: %u", fullpath, crc_total); 168 } 169 170 static void 171 dosum(FILE *fp, int indent, FTSENT *p, int *offset, int flag, 172 char * (*func)(const char *, char *), const char *key) 173 { 174 char *digestbuf = NULL; 175 176 if ((keys & flag) == 0) 177 return; 178 179 if (func) 180 digestbuf = (*func)(p->fts_accpath, NULL); 181 #if !defined(NO_MD5) && !defined(NO_RMD160) && !defined(NO_SHA) 182 else 183 digestbuf = dohash(flag, p->fts_accpath); 184 #endif 185 if (digestbuf != NULL) { 186 output(fp, indent, offset, "%s=%s", key, digestbuf); 187 free(digestbuf); 188 return; 189 } 190 191 if (qflag) { 192 warn("%s: %s failed", p->fts_path, key); 193 return; 194 } 195 196 mtree_err("%s: %s failed: %s", p->fts_path, key, strerror(errno)); 197 } 198 199 static char * 200 crcFile(const char *fname, char *dummy __unused) 201 { 202 char *ptr; 203 uint32_t val, len; 204 int fd, e; 205 206 if ((fd = open(fname, O_RDONLY)) == -1) 207 goto out; 208 209 e = crc(fd, &val, &len); 210 close(fd); 211 if (e) 212 goto out; 213 214 if (asprintf(&ptr, "%u", val) < 0) 215 goto out; 216 217 return ptr; 218 out: 219 mtree_err("%s: %s", fname, strerror(errno)); 220 return NULL; 221 } 222 223 static void 224 statf(FILE *fp, int indent, FTSENT *p) 225 { 226 int offset; 227 const char *name = NULL; 228 229 offset = fprintf(fp, "%*s%s%s", indent, "", 230 S_ISDIR(p->fts_statp->st_mode) ? "" : " ", vispath(p->fts_name)); 231 232 if (offset > (INDENTNAMELEN + indent)) 233 offset = MAXLINELEN; 234 else 235 offset += fprintf(fp, "%*s", 236 (INDENTNAMELEN + indent) - offset, ""); 237 238 if (!S_ISREG(p->fts_statp->st_mode) && (flavor == F_NETBSD6 || !dflag)) 239 output(fp, indent, &offset, "type=%s", 240 inotype(p->fts_statp->st_mode)); 241 if (keys & (F_UID | F_UNAME) && p->fts_statp->st_uid != uid) { 242 if (keys & F_UNAME && 243 (name = user_from_uid(p->fts_statp->st_uid, 1)) != NULL) 244 output(fp, indent, &offset, "uname=%s", name); 245 if (keys & F_UID || (keys & F_UNAME && name == NULL)) 246 output(fp, indent, &offset, "uid=%u", 247 p->fts_statp->st_uid); 248 } 249 if (keys & (F_GID | F_GNAME) && p->fts_statp->st_gid != gid) { 250 if (keys & F_GNAME && 251 (name = group_from_gid(p->fts_statp->st_gid, 1)) != NULL) 252 output(fp, indent, &offset, "gname=%s", name); 253 if (keys & F_GID || (keys & F_GNAME && name == NULL)) 254 output(fp, indent, &offset, "gid=%u", 255 p->fts_statp->st_gid); 256 } 257 if (keys & F_MODE && (p->fts_statp->st_mode & MBITS) != mode) 258 output(fp, indent, &offset, "mode=%#o", 259 p->fts_statp->st_mode & MBITS); 260 if (keys & F_DEV && 261 (S_ISBLK(p->fts_statp->st_mode) || S_ISCHR(p->fts_statp->st_mode))) 262 output(fp, indent, &offset, "device=%#jx", 263 (uintmax_t)p->fts_statp->st_rdev); 264 if (keys & F_NLINK && p->fts_statp->st_nlink != 1) 265 output(fp, indent, &offset, "nlink=%ju", 266 (uintmax_t)p->fts_statp->st_nlink); 267 if (keys & F_SIZE && 268 (flavor == F_FREEBSD9 || S_ISREG(p->fts_statp->st_mode))) 269 output(fp, indent, &offset, "size=%ju", 270 (uintmax_t)p->fts_statp->st_size); 271 if (keys & F_TIME) 272 #if defined(BSD4_4) && !defined(HAVE_NBTOOL_CONFIG_H) 273 output(fp, indent, &offset, "time=%jd.%09ld", 274 (intmax_t)p->fts_statp->st_mtimespec.tv_sec, 275 p->fts_statp->st_mtimespec.tv_nsec); 276 #else 277 output(fp, indent, &offset, "time=%jd.%09ld", 278 (intmax_t)p->fts_statp->st_mtime, (long)0); 279 #endif 280 if (S_ISREG(p->fts_statp->st_mode)) { 281 dosum(fp, indent, p, &offset, F_CKSUM, crcFile, "cksum"); 282 #ifndef NO_MD5 283 dosum(fp, indent, p, &offset, F_MD5, NULL, MD5KEY); 284 #endif 285 #ifndef NO_RMD160 286 dosum(fp, indent, p, &offset, F_RMD160, NULL, RMD160KEY); 287 #endif 288 #ifndef NO_SHA 289 dosum(fp, indent, p, &offset, F_SHA1, NULL, SHA1KEY); 290 dosum(fp, indent, p, &offset, F_SHA256, NULL, SHA256KEY); 291 dosum(fp, indent, p, &offset, F_SHA384, NULL, SHA384KEY); 292 dosum(fp, indent, p, &offset, F_SHA512, NULL, SHA512KEY); 293 #endif 294 } 295 if (keys & F_SLINK && 296 (p->fts_info == FTS_SL || p->fts_info == FTS_SLNONE)) 297 output(fp, indent, &offset, "link=%s", 298 vispath(rlink(p->fts_accpath))); 299 #if HAVE_STRUCT_STAT_ST_FLAGS 300 if (keys & F_FLAGS && p->fts_statp->st_flags != flags) { 301 char *str = flags_to_string(p->fts_statp->st_flags, "none"); 302 output(fp, indent, &offset, "flags=%s", str); 303 free(str); 304 } 305 #endif 306 putchar('\n'); 307 } 308 309 /* XXX 310 * FLAGS2INDEX will fail once the user and system settable bits need more 311 * than one byte, respectively. 312 */ 313 #define FLAGS2INDEX(x) (((x >> 8) & 0x0000ff00) | (x & 0x000000ff)) 314 315 #define MTREE_MAXGID 5000 316 #define MTREE_MAXUID 5000 317 #define MTREE_MAXMODE (MBITS + 1) 318 #if HAVE_STRUCT_STAT_ST_FLAGS 319 #define MTREE_MAXFLAGS (FLAGS2INDEX(CH_MASK) + 1) /* 1808 */ 320 #else 321 #define MTREE_MAXFLAGS 1 322 #endif 323 #define MTREE_MAXS 16 324 325 static int 326 statd(FILE *fp, FTS *t, FTSENT *parent, uid_t *puid, gid_t *pgid, mode_t *pmode, 327 u_long *pflags) 328 { 329 FTSENT *p; 330 gid_t sgid; 331 uid_t suid; 332 mode_t smode; 333 u_long sflags = 0; 334 const char *name = NULL; 335 gid_t savegid; 336 uid_t saveuid; 337 mode_t savemode; 338 u_long saveflags; 339 u_short maxgid, maxuid, maxmode, maxflags; 340 u_short g[MTREE_MAXGID], u[MTREE_MAXUID], 341 m[MTREE_MAXMODE], f[MTREE_MAXFLAGS]; 342 static int first = 1; 343 344 savegid = *pgid; 345 saveuid = *puid; 346 savemode = *pmode; 347 saveflags = *pflags; 348 if ((p = fts_children(t, 0)) == NULL) { 349 if (errno) 350 mtree_err("%s: %s", RP(parent), strerror(errno)); 351 return (1); 352 } 353 354 memset(g, 0, sizeof(g)); 355 memset(u, 0, sizeof(u)); 356 memset(m, 0, sizeof(m)); 357 memset(f, 0, sizeof(f)); 358 359 maxuid = maxgid = maxmode = maxflags = 0; 360 for (; p; p = p->fts_link) { 361 if (flavor == F_NETBSD6 || !dflag || 362 (dflag && S_ISDIR(p->fts_statp->st_mode))) { 363 smode = p->fts_statp->st_mode & MBITS; 364 if (smode < MTREE_MAXMODE && ++m[smode] > maxmode) { 365 savemode = smode; 366 maxmode = m[smode]; 367 } 368 sgid = p->fts_statp->st_gid; 369 if (sgid < MTREE_MAXGID && ++g[sgid] > maxgid) { 370 savegid = sgid; 371 maxgid = g[sgid]; 372 } 373 suid = p->fts_statp->st_uid; 374 if (suid < MTREE_MAXUID && ++u[suid] > maxuid) { 375 saveuid = suid; 376 maxuid = u[suid]; 377 } 378 379 #if HAVE_STRUCT_STAT_ST_FLAGS 380 sflags = FLAGS2INDEX(p->fts_statp->st_flags); 381 if (sflags < MTREE_MAXFLAGS && ++f[sflags] > maxflags) { 382 saveflags = p->fts_statp->st_flags; 383 maxflags = f[sflags]; 384 } 385 #endif 386 } 387 } 388 /* 389 * If the /set record is the same as the last one we do not need to 390 * output a new one. So first we check to see if anything changed. 391 * Note that we always output a /set record for the first directory. 392 */ 393 if (((keys & (F_UNAME | F_UID)) && (*puid != saveuid)) || 394 ((keys & (F_GNAME | F_GID)) && (*pgid != savegid)) || 395 ((keys & F_MODE) && (*pmode != savemode)) || 396 ((keys & F_FLAGS) && (*pflags != saveflags)) || 397 first) { 398 first = 0; 399 if (flavor != F_NETBSD6 && dflag) 400 fprintf(fp, "/set type=dir"); 401 else 402 fprintf(fp, "/set type=file"); 403 if (keys & (F_UID | F_UNAME)) { 404 if (keys & F_UNAME && 405 (name = user_from_uid(saveuid, 1)) != NULL) 406 fprintf(fp, " uname=%s", name); 407 if (keys & F_UID || (keys & F_UNAME && name == NULL)) 408 fprintf(fp, " uid=%lu", (u_long)saveuid); 409 } 410 if (keys & (F_GID | F_GNAME)) { 411 if (keys & F_GNAME && 412 (name = group_from_gid(savegid, 1)) != NULL) 413 fprintf(fp, " gname=%s", name); 414 if (keys & F_GID || (keys & F_GNAME && name == NULL)) 415 fprintf(fp, " gid=%lu", (u_long)savegid); 416 } 417 if (keys & F_MODE) 418 fprintf(fp, " mode=%#lo", (u_long)savemode); 419 if (keys & F_NLINK) 420 fprintf(fp, " nlink=1"); 421 if (keys & F_FLAGS) { 422 char *str = flags_to_string(saveflags, "none"); 423 fprintf(fp, " flags=%s", str); 424 free(str); 425 } 426 fprintf(fp, "\n"); 427 *puid = saveuid; 428 *pgid = savegid; 429 *pmode = savemode; 430 *pflags = saveflags; 431 } 432 return (0); 433 } 434 435 /* 436 * dcmp -- 437 * used as a comparison function passed to fts_open() to control 438 * the order in which fts_read() returns results. We make 439 * directories sort after non-directories, but otherwise sort in 440 * strcmp() order. 441 * 442 * Keep this in sync with nodecmp() in spec.c. 443 */ 444 static int 445 dcmp(const FTSENT *FTS_CONST *a, const FTSENT *FTS_CONST *b) 446 { 447 448 if (S_ISDIR((*a)->fts_statp->st_mode)) { 449 if (!S_ISDIR((*b)->fts_statp->st_mode)) 450 return (1); 451 } else if (S_ISDIR((*b)->fts_statp->st_mode)) 452 return (-1); 453 return (strcmp((*a)->fts_name, (*b)->fts_name)); 454 } 455 456 void 457 output(FILE *fp, int indent, int *offset, const char *fmt, ...) 458 { 459 va_list ap; 460 char buf[1024]; 461 462 va_start(ap, fmt); 463 vsnprintf(buf, sizeof(buf), fmt, ap); 464 va_end(ap); 465 466 if (*offset + strlen(buf) > MAXLINELEN - 3) { 467 fprintf(fp, " \\\n%*s", INDENTNAMELEN + indent, ""); 468 *offset = INDENTNAMELEN + indent; 469 } 470 *offset += fprintf(fp, " %s", buf) + 1; 471 } 472