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