1 /* $NetBSD: create.c,v 1.11 1996/09/05 09:24:19 mycroft Exp $ */ 2 /* $OpenBSD: create.c,v 1.26 2009/10/27 23:59:53 deraadt 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 #include <time.h> 36 #include <fcntl.h> 37 #include <fts.h> 38 #include <dirent.h> 39 #include <grp.h> 40 #include <pwd.h> 41 #include <errno.h> 42 #include <unistd.h> 43 #include <stdio.h> 44 #include <stdarg.h> 45 #include <vis.h> 46 #include <md5.h> 47 #include <sha1.h> 48 #include <rmd160.h> 49 #include "mtree.h" 50 #include "extern.h" 51 52 #define INDENTNAMELEN 15 53 #define MAXLINELEN 80 54 55 extern u_int32_t crc_total; 56 extern int ftsoptions; 57 extern int dflag, iflag, nflag, sflag; 58 extern u_int keys; 59 extern char fullpath[MAXPATHLEN]; 60 61 static gid_t gid; 62 static uid_t uid; 63 static mode_t mode; 64 65 static void output(int, int *, const char *, ...) 66 __attribute__((__format__ (printf, 3, 4))); 67 static int statd(FTS *, FTSENT *, uid_t *, gid_t *, mode_t *); 68 static void statf(int, FTSENT *); 69 70 void 71 cwalk(void) 72 { 73 FTS *t; 74 FTSENT *p; 75 time_t clock; 76 char *argv[2], host[MAXHOSTNAMELEN]; 77 int indent = 0; 78 79 (void)time(&clock); 80 (void)gethostname(host, sizeof(host)); 81 (void)printf( 82 "#\t user: %s\n#\tmachine: %s\n#\t tree: %s\n#\t date: %s", 83 getlogin(), host, fullpath, ctime(&clock)); 84 85 argv[0] = "."; 86 argv[1] = NULL; 87 if ((t = fts_open(argv, ftsoptions, dsort)) == NULL) 88 error("fts_open: %s", strerror(errno)); 89 while ((p = fts_read(t))) { 90 if (iflag) 91 indent = p->fts_level * 4; 92 switch(p->fts_info) { 93 case FTS_D: 94 if (!dflag) 95 (void)printf("\n"); 96 if (!nflag) 97 (void)printf("# %s\n", p->fts_path); 98 statd(t, p, &uid, &gid, &mode); 99 statf(indent, p); 100 break; 101 case FTS_DP: 102 if (!nflag && (p->fts_level > 0)) 103 (void)printf("%*s# %s\n", indent, "", p->fts_path); 104 (void)printf("%*s..\n", indent, ""); 105 if (!dflag) 106 (void)printf("\n"); 107 break; 108 case FTS_DNR: 109 case FTS_ERR: 110 case FTS_NS: 111 (void)fprintf(stderr, "mtree: %s: %s\n", 112 p->fts_path, strerror(p->fts_errno)); 113 break; 114 default: 115 if (!dflag) 116 statf(indent, p); 117 break; 118 119 } 120 } 121 (void)fts_close(t); 122 if (sflag && keys & F_CKSUM) 123 (void)fprintf(stderr, 124 "mtree: %s checksum: %u\n", fullpath, crc_total); 125 } 126 127 static void 128 statf(int indent, FTSENT *p) 129 { 130 struct group *gr; 131 struct passwd *pw; 132 u_int32_t len, val; 133 int fd, offset; 134 char *name, *escaped_name; 135 size_t esc_len; 136 137 esc_len = p->fts_namelen * 4 + 1; 138 escaped_name = malloc(esc_len); 139 if (escaped_name == NULL) 140 error("statf: %s", strerror(errno)); 141 strnvis(escaped_name, p->fts_name, esc_len, 142 VIS_WHITE | VIS_OCTAL | VIS_GLOB); 143 144 if (iflag || S_ISDIR(p->fts_statp->st_mode)) 145 offset = printf("%*s%s", indent, "", escaped_name); 146 else 147 offset = printf("%*s %s", indent, "", escaped_name); 148 149 free(escaped_name); 150 151 if (offset > (INDENTNAMELEN + indent)) 152 offset = MAXLINELEN; 153 else 154 offset += printf("%*s", (INDENTNAMELEN + indent) - offset, ""); 155 156 if (!S_ISREG(p->fts_statp->st_mode) && !dflag) 157 output(indent, &offset, "type=%s", inotype(p->fts_statp->st_mode)); 158 if (p->fts_statp->st_uid != uid) { 159 if (keys & F_UNAME) { 160 if ((pw = getpwuid(p->fts_statp->st_uid)) != NULL) { 161 output(indent, &offset, "uname=%s", pw->pw_name); 162 } else { 163 error("could not get uname for uid=%u", 164 p->fts_statp->st_uid); 165 } 166 } 167 if (keys & F_UID) 168 output(indent, &offset, "uid=%u", p->fts_statp->st_uid); 169 } 170 if (p->fts_statp->st_gid != gid) { 171 if (keys & F_GNAME) { 172 if ((gr = getgrgid(p->fts_statp->st_gid)) != NULL) { 173 output(indent, &offset, "gname=%s", gr->gr_name); 174 } else { 175 error("could not get gname for gid=%u", 176 p->fts_statp->st_gid); 177 } 178 } 179 if (keys & F_GID) 180 output(indent, &offset, "gid=%u", p->fts_statp->st_gid); 181 } 182 if (keys & F_MODE && (p->fts_statp->st_mode & MBITS) != mode) 183 output(indent, &offset, "mode=%#o", p->fts_statp->st_mode & MBITS); 184 if (keys & F_NLINK && p->fts_statp->st_nlink != 1) 185 output(indent, &offset, "nlink=%u", p->fts_statp->st_nlink); 186 if (keys & F_SIZE && S_ISREG(p->fts_statp->st_mode)) 187 output(indent, &offset, "size=%qd", p->fts_statp->st_size); 188 if (keys & F_TIME) 189 output(indent, &offset, "time=%ld.%ld", 190 p->fts_statp->st_mtimespec.tv_sec, 191 p->fts_statp->st_mtimespec.tv_nsec); 192 if (keys & F_CKSUM && S_ISREG(p->fts_statp->st_mode)) { 193 if ((fd = open(p->fts_accpath, MTREE_O_FLAGS, 0)) < 0 || 194 crc(fd, &val, &len)) 195 error("%s: %s", p->fts_accpath, strerror(errno)); 196 (void)close(fd); 197 output(indent, &offset, "cksum=%lu", val); 198 } 199 if (keys & F_MD5 && S_ISREG(p->fts_statp->st_mode)) { 200 char *md5digest, buf[MD5_DIGEST_STRING_LENGTH]; 201 202 md5digest = MD5File(p->fts_accpath,buf); 203 if (!md5digest) 204 error("%s: %s", p->fts_accpath, strerror(errno)); 205 else 206 output(indent, &offset, "md5digest=%s", md5digest); 207 } 208 if (keys & F_RMD160 && S_ISREG(p->fts_statp->st_mode)) { 209 char *rmd160digest, buf[RMD160_DIGEST_STRING_LENGTH]; 210 211 rmd160digest = RMD160File(p->fts_accpath,buf); 212 if (!rmd160digest) 213 error("%s: %s", p->fts_accpath, strerror(errno)); 214 else 215 output(indent, &offset, "rmd160digest=%s", rmd160digest); 216 } 217 if (keys & F_SHA1 && S_ISREG(p->fts_statp->st_mode)) { 218 char *sha1digest, buf[SHA1_DIGEST_STRING_LENGTH]; 219 220 sha1digest = SHA1File(p->fts_accpath,buf); 221 if (!sha1digest) 222 error("%s: %s", p->fts_accpath, strerror(errno)); 223 else 224 output(indent, &offset, "sha1digest=%s", sha1digest); 225 } 226 if (keys & F_SLINK && 227 (p->fts_info == FTS_SL || p->fts_info == FTS_SLNONE)) { 228 name = rlink(p->fts_accpath); 229 esc_len = strlen(name) * 4 + 1; 230 escaped_name = malloc(esc_len); 231 if (escaped_name == NULL) 232 error("statf: %s", strerror(errno)); 233 strnvis(escaped_name, name, esc_len, VIS_WHITE | VIS_OCTAL); 234 output(indent, &offset, "link=%s", escaped_name); 235 free(escaped_name); 236 } 237 if (keys & F_FLAGS && !S_ISLNK(p->fts_statp->st_mode)) { 238 char *file_flags; 239 240 file_flags = fflagstostr(p->fts_statp->st_flags); 241 if (file_flags == NULL) 242 error("%s", strerror(errno)); 243 if (*file_flags != '\0') 244 output(indent, &offset, "flags=%s", file_flags); 245 else 246 output(indent, &offset, "flags=none"); 247 free(file_flags); 248 } 249 (void)putchar('\n'); 250 } 251 252 #define MAXGID 5000 253 #define MAXUID 5000 254 #define MAXMODE MBITS + 1 255 256 static int 257 statd(FTS *t, FTSENT *parent, uid_t *puid, gid_t *pgid, mode_t *pmode) 258 { 259 FTSENT *p; 260 gid_t sgid; 261 uid_t suid; 262 mode_t smode; 263 struct group *gr; 264 struct passwd *pw; 265 gid_t savegid = *pgid; 266 uid_t saveuid = *puid; 267 mode_t savemode = *pmode; 268 int maxgid; 269 int maxuid; 270 u_short maxmode; 271 gid_t g[MAXGID]; 272 uid_t u[MAXUID]; 273 mode_t m[MAXMODE]; 274 static int first = 1; 275 276 if ((p = fts_children(t, 0)) == NULL) { 277 if (errno) 278 error("%s: %s", RP(parent), strerror(errno)); 279 return (1); 280 } 281 282 bzero(g, sizeof(g)); 283 bzero(u, sizeof(u)); 284 bzero(m, sizeof(m)); 285 286 maxuid = maxgid = maxmode = 0; 287 for (; p; p = p->fts_link) { 288 if (!dflag || (dflag && S_ISDIR(p->fts_statp->st_mode))) { 289 smode = p->fts_statp->st_mode & MBITS; 290 if (smode < MAXMODE && ++m[smode] > maxmode) { 291 savemode = smode; 292 maxmode = m[smode]; 293 } 294 sgid = p->fts_statp->st_gid; 295 if (sgid < MAXGID && ++g[sgid] > maxgid) { 296 savegid = sgid; 297 maxgid = g[sgid]; 298 } 299 suid = p->fts_statp->st_uid; 300 if (suid < MAXUID && ++u[suid] > maxuid) { 301 saveuid = suid; 302 maxuid = u[suid]; 303 } 304 } 305 } 306 /* 307 * If the /set record is the same as the last one we do not need to output 308 * a new one. So first we check to see if anything changed. Note that we 309 * always output a /set record for the first directory. 310 */ 311 if ((((keys & F_UNAME) | (keys & F_UID)) && (*puid != saveuid)) || 312 (((keys & F_GNAME) | (keys & F_GID)) && (*pgid != savegid)) || 313 ((keys & F_MODE) && (*pmode != savemode)) || (first)) { 314 first = 0; 315 if (dflag) 316 (void)printf("/set type=dir"); 317 else 318 (void)printf("/set type=file"); 319 if (keys & F_UNAME) { 320 if ((pw = getpwuid(saveuid)) != NULL) 321 (void)printf(" uname=%s", pw->pw_name); 322 else 323 error("could not get uname for uid=%u", saveuid); 324 } 325 if (keys & F_UID) 326 (void)printf(" uid=%u", saveuid); 327 if (keys & F_GNAME) { 328 if ((gr = getgrgid(savegid)) != NULL) 329 (void)printf(" gname=%s", gr->gr_name); 330 else 331 error("could not get gname for gid=%u", savegid); 332 } 333 if (keys & F_GID) 334 (void)printf(" gid=%u", savegid); 335 if (keys & F_MODE) 336 (void)printf(" mode=%#o", savemode); 337 if (keys & F_NLINK) 338 (void)printf(" nlink=1"); 339 (void)printf("\n"); 340 *puid = saveuid; 341 *pgid = savegid; 342 *pmode = savemode; 343 } 344 return (0); 345 } 346 347 int 348 dsort(const FTSENT **a, const FTSENT **b) 349 { 350 if (S_ISDIR((*a)->fts_statp->st_mode)) { 351 if (!S_ISDIR((*b)->fts_statp->st_mode)) 352 return (1); 353 } else if (S_ISDIR((*b)->fts_statp->st_mode)) 354 return (-1); 355 return (strcmp((*a)->fts_name, (*b)->fts_name)); 356 } 357 358 void 359 output(int indent, int *offset, const char *fmt, ...) 360 { 361 va_list ap; 362 char buf[1024]; 363 364 va_start(ap, fmt); 365 (void)vsnprintf(buf, sizeof(buf), fmt, ap); 366 va_end(ap); 367 368 if (*offset + strlen(buf) > MAXLINELEN - 3) { 369 (void)printf(" \\\n%*s", INDENTNAMELEN + indent, ""); 370 *offset = INDENTNAMELEN + indent; 371 } 372 *offset += printf(" %s", buf) + 1; 373 } 374