/*-
* Copyright (c) 1989, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#define INDENTNAMELEN 8
#define MARK \
do { \
if (flavor == F_FREEBSD9) { \
len = printf("%s changed\n", RP(p)); \
tab = "\t"; \
} else { \
len = printf("%s: ", RP(p)); \
if (len > INDENTNAMELEN) { \
tab = "\t"; \
printf("\n"); \
} else { \
tab = ""; \
printf("%*s", INDENTNAMELEN - (int)len, ""); \
} \
} \
} while (0)
#define LABEL if (!label++) MARK
#if HAVE_STRUCT_STAT_ST_FLAGS
#define CHANGEFLAGS \
if (flags != p->fts_statp->st_flags) { \
char *sf; \
if (!label) { \
MARK; \
sf = flags_to_string(p->fts_statp->st_flags, "none"); \
printf("%sflags (\"%s\"", tab, sf); \
free(sf); \
} \
if (lchflags(p->fts_accpath, flags)) { \
label++; \
printf(", not modified: %s)\n", \
strerror(errno)); \
} else { \
sf = flags_to_string(flags, "none"); \
printf(", modified to \"%s\")\n", sf); \
free(sf); \
} \
}
/* SETFLAGS:
* given pflags, additionally set those flags specified in s->st_flags and
* selected by mask (the other flags are left unchanged).
*/
#define SETFLAGS(pflags, mask) \
do { \
flags = (s->st_flags & (mask)) | (pflags); \
CHANGEFLAGS; \
} while (0)
/* CLEARFLAGS:
* given pflags, reset the flags specified in s->st_flags and selected by mask
* (the other flags are left unchanged).
*/
#define CLEARFLAGS(pflags, mask) \
do { \
flags = (~(s->st_flags & (mask)) & CH_MASK) & (pflags); \
CHANGEFLAGS; \
} while (0)
#endif /* HAVE_STRUCT_STAT_ST_FLAGS */
tab = NULL;
label = 0;
was_unlinked = false;
switch(s->type) {
case F_BLOCK:
if (!S_ISBLK(p->fts_statp->st_mode))
goto typeerr;
break;
case F_CHAR:
if (!S_ISCHR(p->fts_statp->st_mode))
goto typeerr;
break;
case F_DIR:
if (!S_ISDIR(p->fts_statp->st_mode))
goto typeerr;
break;
case F_FIFO:
if (!S_ISFIFO(p->fts_statp->st_mode))
goto typeerr;
break;
case F_FILE:
if (!S_ISREG(p->fts_statp->st_mode))
goto typeerr;
break;
case F_LINK:
if (!S_ISLNK(p->fts_statp->st_mode))
goto typeerr;
break;
#ifdef S_ISSOCK
case F_SOCK:
if (!S_ISSOCK(p->fts_statp->st_mode))
goto typeerr;
break;
#endif
typeerr: LABEL;
printf(flavor == F_FREEBSD9 ?
"\ttype expected %s found %s\n" : "\ttype (%s, %s)\n",
nodetype(s->type), inotype(p->fts_statp->st_mode));
return (label);
}
if (mtree_Wflag)
goto afterpermwhack;
#if HAVE_STRUCT_STAT_ST_FLAGS
if (iflag && !uflag) {
if (s->flags & F_FLAGS)
SETFLAGS(p->fts_statp->st_flags, SP_FLGS);
return (label);
}
if (mflag && !uflag) {
if (s->flags & F_FLAGS)
CLEARFLAGS(p->fts_statp->st_flags, SP_FLGS);
return (label);
}
#endif
if (s->flags & F_DEV &&
(s->type == F_BLOCK || s->type == F_CHAR) &&
s->st_rdev != p->fts_statp->st_rdev) {
LABEL;
printf(flavor == F_FREEBSD9 ?
"%sdevice expected %#jx found %#jx" :
"%sdevice (%#jx, %#jx",
tab, (uintmax_t)s->st_rdev,
(uintmax_t)p->fts_statp->st_rdev);
if (uflag) {
if ((unlink(p->fts_accpath) == -1) ||
(mknod(p->fts_accpath,
s->st_mode | nodetoino(s->type),
s->st_rdev) == -1) ||
(lchown(p->fts_accpath, p->fts_statp->st_uid,
p->fts_statp->st_gid) == -1) ) {
printf(", not modified: %s%s\n",
strerror(errno),
flavor == F_FREEBSD9 ? "" : ")");
} else {
printf(", modified%s\n",
flavor == F_FREEBSD9 ? "" : ")");
was_unlinked = true;
}
} else
printf(")\n");
tab = "\t";
}
/* Set the uid/gid first, then set the mode. */
if (s->flags & (F_UID | F_UNAME) &&
(was_unlinked || s->st_uid != p->fts_statp->st_uid)) {
LABEL;
printf(flavor == F_FREEBSD9 ?
"%suser expected %lu found %lu" : "%suser (%lu, %lu",
tab, (u_long)s->st_uid, (u_long)p->fts_statp->st_uid);
if (uflag) {
if (lchown(p->fts_accpath, s->st_uid, (gid_t)-1))
printf(", not modified: %s%s\n",
strerror(errno),
flavor == F_FREEBSD9 ? "" : ")");
else
printf(", modified%s%s\n",
was_unlinked ? " by unlink" : "",
flavor == F_FREEBSD9 ? "" : ")");
} else
printf(")\n");
tab = "\t";
}
if (s->flags & (F_GID | F_GNAME) &&
(was_unlinked || s->st_gid != p->fts_statp->st_gid)) {
LABEL;
printf(flavor == F_FREEBSD9 ?
"%sgid expected %lu found %lu" : "%sgid (%lu, %lu",
tab, (u_long)s->st_gid, (u_long)p->fts_statp->st_gid);
if (uflag) {
if (lchown(p->fts_accpath, (uid_t)-1, s->st_gid))
printf(", not modified: %s%s\n",
strerror(errno),
flavor == F_FREEBSD9 ? "" : ")");
else
printf(", modified%s%s\n",
was_unlinked ? " by unlink" : "",
flavor == F_FREEBSD9 ? "" : ")");
}
else
printf(")\n");
tab = "\t";
}
if (s->flags & F_MODE &&
(was_unlinked || s->st_mode != (p->fts_statp->st_mode & MBITS))) {
if (lflag && !was_unlinked) {
mode_t tmode, mode;
tmode = s->st_mode;
mode = p->fts_statp->st_mode & MBITS;
/*
* if none of the suid/sgid/etc bits are set,
* then if the mode is a subset of the target,
* skip.
*/
if (!((tmode & ~(S_IRWXU|S_IRWXG|S_IRWXO)) ||
(mode & ~(S_IRWXU|S_IRWXG|S_IRWXO))))
if ((mode | tmode) == tmode)
goto skip;
}
LABEL;
printf(flavor == F_FREEBSD9 ?
"%spermissions expcted %#lo found %#lo" :
"%spermissions (%#lo, %#lo",
tab, (u_long)s->st_mode,
(u_long)p->fts_statp->st_mode & MBITS);
if (uflag) {
if (lchmod(p->fts_accpath, s->st_mode))
printf(", not modified: %s%s\n",
strerror(errno),
flavor == F_FREEBSD9 ? "" : ")");
else
printf(", modified%s%s\n",
was_unlinked ? " by unlink" : "",
flavor == F_FREEBSD9 ? "" : ")");
}
else
printf(")\n");
tab = "\t";
skip: ;
}
if (s->flags & F_NLINK && s->type != F_DIR &&
s->st_nlink != p->fts_statp->st_nlink) {
LABEL;
printf(flavor == F_FREEBSD9 ?
"%slink count expected %lu found %lu\n" :
"%slink count (%lu, %lu)\n",
tab, (u_long)s->st_nlink, (u_long)p->fts_statp->st_nlink);
tab = "\t";
}
if (s->flags & F_SIZE && s->st_size != p->fts_statp->st_size) {
LABEL;
printf(flavor == F_FREEBSD9 ?
"%ssize expected %ju found %ju\n" : "%ssize (%ju, %ju)\n",
tab, (uintmax_t)s->st_size,
(uintmax_t)p->fts_statp->st_size);
tab = "\t";
}
/*
* XXX
* Since utimes(2) only takes a timeval, there's no point in
* comparing the low bits of the timespec nanosecond field. This
* will only result in mismatches that we can never fix.
*
* Doesn't display microsecond differences.
*/
if (s->flags & F_TIME) {
struct timeval tv[2];
struct stat *ps = p->fts_statp;
time_t smtime = s->st_mtimespec.tv_sec;