#include <stdio.h>
#include <stdlib.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/mntent.h>
#include <sys/acl.h>
#include <sys/fs/ufs_acl.h>
#include <sys/fs/ufs_fs.h>
#include <sys/vnode.h>
#include <string.h>
#include <sys/fs/ufs_inode.h>
#include "fsck.h"
static caddr_t aclbuf;
static int64_t aclbufoff;
static int64_t maxaclsize;
static int aclblksort(const void *, const void *);
static int bufchk(char *, int64_t, fsck_ino_t);
static void clear_shadow_client(struct shadowclientinfo *,
struct shadowclients *, int);
void
pass3b(void)
{
fsck_ino_t inumber;
struct dinode *dp;
struct inoinfo *aclp;
struct inodesc curino;
struct shadowclientinfo *sci;
struct shadowclients *scc;
int64_t acl_size_limit;
int i;
qsort((char *)aclpsort, (int)aclplast, sizeof (*aclpsort), aclblksort);
acl_size_limit = sizeof (ufs_acl_t) * (4 * MAX_ACL_ENTRIES + 8);
acl_size_limit *= 2;
maxaclsize = 0;
for (inumber = 0; inumber < aclplast; inumber++) {
aclp = aclpsort[inumber];
if ((int64_t)aclp->i_isize > acl_size_limit) {
(void) printf(
"ACL I=%d is excessively large (%lld > %lld)",
inumber,
(longlong_t)aclp->i_isize,
(longlong_t)acl_size_limit);
if (preen) {
(void) printf(" (IGNORING)\n");
} else if (reply("CLEAR") == 1) {
freeino(inumber, TI_PARENT);
} else {
iscorrupt = 1;
(void) printf("IGNORING SHADOW I=%d\n",
inumber);
}
continue;
}
if ((int64_t)aclp->i_isize > maxaclsize)
maxaclsize = (int64_t)aclp->i_isize;
}
maxaclsize = ((maxaclsize / sblock.fs_bsize) + 1) * sblock.fs_bsize;
if (maxaclsize == 0)
goto noacls;
if (aclbuf != NULL) {
free((void *)aclbuf);
}
if ((aclbuf = malloc(maxaclsize)) == NULL) {
errexit("cannot alloc %lld bytes for aclbuf\n",
(longlong_t)maxaclsize);
}
for (inumber = 0; inumber < aclplast; inumber++) {
aclp = aclpsort[inumber];
if ((int64_t)aclp->i_isize > acl_size_limit) {
continue;
}
if ((statemap[aclp->i_number] & STMASK) != SSTATE) {
continue;
}
dp = ginode(aclp->i_number);
init_inodesc(&curino);
curino.id_fix = FIX;
curino.id_type = ACL;
curino.id_func = pass3bcheck;
curino.id_number = aclp->i_number;
curino.id_filesize = aclp->i_isize;
aclbufoff = 0;
(void) memset(aclbuf, 0, (size_t)maxaclsize);
if ((ckinode(dp, &curino, CKI_TRAVERSE) & KEEPON) == 0 ||
bufchk(aclbuf, (int64_t)aclp->i_isize, aclp->i_number)) {
dp = ginode(aclp->i_number);
if (dp->di_nlink <= 0) {
statemap[aclp->i_number] = FSTATE;
continue;
}
(void) printf("ACL I=%d BAD/CORRUPT", aclp->i_number);
if (preen || reply("CLEAR") == 1) {
if (preen)
(void) printf("\n");
freeino(aclp->i_number, TI_PARENT);
} else {
iscorrupt = 1;
}
}
}
noacls:
for (sci = shadowclientinfo; sci; sci = sci->next) {
if ((statemap[sci->shadow] & STMASK) != SSTATE) {
for (scc = sci->clients; scc; scc = scc->next) {
for (i = 0; i < scc->nclients; i++) {
clear_shadow_client(sci, scc, i);
}
}
}
}
free((void *)aclbuf);
aclbuf = NULL;
}
static void
clear_shadow_client(struct shadowclientinfo *sci, struct shadowclients *scc,
int client)
{
int suppress_update = 0;
caddr_t flow;
struct inodesc ldesc;
struct dinode *dp;
(void) printf("I=%d HAS BAD/CLEARED ACL I=%d",
scc->client[client], sci->shadow);
if (preen || reply("FIX") == 1) {
if (preen)
(void) printf("\n");
dp = ginode(scc->client[client]);
dp->di_mode &= IFMT;
dp->di_shadow = 0;
inodirty();
LINK_RANGE(flow, lncntp[dp->di_shadow], 1);
if (flow != NULL) {
LINK_CLEAR(flow, scc->client[client], dp->di_mode,
&ldesc);
if (statemap[scc->client[client]] == USTATE)
suppress_update = 1;
}
if (!suppress_update)
TRACK_LNCNTP(sci->shadow, lncntp[sci->shadow]++);
} else {
iscorrupt = 1;
}
}
int
pass3bcheck(struct inodesc *idesc)
{
struct bufarea *bp;
size_t size, bsize;
if (aclbufoff == idesc->id_filesize) {
return (STOP);
}
bsize = size = sblock.fs_fsize * idesc->id_numfrags;
if ((size + aclbufoff) > idesc->id_filesize)
size = idesc->id_filesize - aclbufoff;
if (aclbufoff + size > maxaclsize)
errexit("acl size %lld exceeds maximum calculated "
"size of %lld bytes",
(longlong_t)aclbufoff + size, (longlong_t)maxaclsize);
bp = getdatablk(idesc->id_blkno, bsize);
if (bp->b_errs != 0) {
brelse(bp);
return (STOP);
}
(void) memmove((void *)(aclbuf + aclbufoff), (void *)bp->b_un.b_buf,
(size_t)size);
aclbufoff += size;
brelse(bp);
return (KEEPON);
}
static int
aclblksort(const void *pp1, const void *pp2)
{
const struct inoinfo **aclpp1 = (const struct inoinfo **)pp1;
const struct inoinfo **aclpp2 = (const struct inoinfo **)pp2;
return ((*aclpp1)->i_blks[0] - (*aclpp2)->i_blks[0]);
}
static int
bufchk(char *buf, int64_t len, fsck_ino_t inum)
{
ufs_fsd_t *fsdp;
ufs_acl_t *ufsaclp = NULL;
int numacls;
int curacl;
struct type_counts_s {
int nuser_objs;
int ngroup_objs;
int nother_objs;
int nclass_objs;
int ndef_user_objs;
int ndef_group_objs;
int ndef_other_objs;
int ndef_class_objs;
int nusers;
int ngroups;
int ndef_users;
int ndef_groups;
} type_counts[3];
struct type_counts_s *tcp, *tcp_all, *tcp_def, *tcp_norm;
int numdefs;
caddr_t bad;
caddr_t end = buf + len;
int64_t recsz = 0;
int64_t min_recsz = FSD_RECSZ(fsdp, sizeof (*fsdp));
struct shadowclientinfo *sci;
struct shadowclients *scc;
fsck_ino_t target;
int numtargets = 0;
if (len == 0) {
pwarn("ACL I=%d HAS ZERO LENGTH\n", inum);
return (1);
}
(void) memset(type_counts, 0, sizeof (type_counts));
for (fsdp = (ufs_fsd_t *)buf;
(caddr_t)fsdp < end;
fsdp = (ufs_fsd_t *)((caddr_t)fsdp + recsz)) {
recsz = FSD_RECSZ(fsdp, fsdp->fsd_size);
if ((recsz < min_recsz) ||
(((caddr_t)fsdp + recsz) > (buf + len))) {
pwarn("Bad FSD entry size %lld in shadow inode %d",
recsz, inum);
if (reply("CLEAR SHADOW INODE") == 1) {
freeino(inum, TI_PARENT);
} else {
iscorrupt = 1;
}
return (0);
}
switch (fsdp->fsd_type) {
case FSD_FREE:
break;
case FSD_ACL:
case FSD_DFACL:
numacls = (fsdp->fsd_size - 2 * sizeof (int)) /
sizeof (ufs_acl_t);
tcp = &type_counts[fsdp->fsd_type];
curacl = 0;
for (ufsaclp = (ufs_acl_t *)fsdp->fsd_data;
numacls; ufsaclp++, curacl++) {
switch (ufsaclp->acl_tag) {
case USER_OBJ:
tcp->nuser_objs++;
break;
case GROUP_OBJ:
tcp->ngroup_objs++;
break;
case OTHER_OBJ:
tcp->nother_objs++;
break;
case CLASS_OBJ:
tcp->nclass_objs++;
break;
case DEF_USER_OBJ:
tcp->ndef_user_objs++;
break;
case DEF_GROUP_OBJ:
tcp->ndef_group_objs++;
break;
case DEF_OTHER_OBJ:
tcp->ndef_other_objs++;
break;
case DEF_CLASS_OBJ:
tcp->ndef_class_objs++;
break;
case USER:
tcp->nusers++;
break;
case GROUP:
tcp->ngroups++;
break;
case DEF_USER:
tcp->ndef_users++;
break;
case DEF_GROUP:
tcp->ndef_groups++;
break;
default:
return (1);
}
if ((ufsaclp->acl_perm & ~07) != 0) {
pwarn("Bad permission 0%o in ACL\n",
ufsaclp->acl_perm);
return (1);
}
numacls--;
}
break;
default:
if (fsdp->fsd_type >= FSD_RESERVED3 &&
fsdp->fsd_type <= FSD_RESERVED7)
bad = "Unexpected";
else
bad = "Unknown";
pwarn("%s FSD type %d in shadow inode %d",
bad, fsdp->fsd_type, inum);
if (preen) {
(void) printf(" (IGNORED)\n");
} else if (reply("IGNORE") == 0) {
if (reply("CLEAR SHADOW INODE") == 1) {
freeino(inum, TI_PARENT);
}
return (0);
}
break;
}
}
if ((caddr_t)fsdp != (buf + len)) {
return (1);
}
if (ufsaclp == NULL)
return (0);
tcp = &type_counts[FSD_DFACL];
if (verbose &&
(tcp->nuser_objs != 0 ||
tcp->ngroup_objs != 0 ||
tcp->nother_objs != 0 ||
tcp->nclass_objs != 0 ||
tcp->nusers != 0 ||
tcp->ngroups != 0)) {
(void) printf("NOTE: ACL I=%d has miscategorized ACLs. ",
inum);
(void) printf("This is harmless, but not normal.\n");
}
tcp = &type_counts[FSD_ACL];
if (verbose &&
(tcp->ndef_user_objs != 0 ||
tcp->ndef_group_objs != 0 ||
tcp->ndef_other_objs != 0 ||
tcp->ndef_class_objs != 0 ||
tcp->ndef_users != 0 ||
tcp->ndef_groups != 0)) {
(void) printf("NOTE: ACL I=%d has miscategorized ACLs.",
inum);
(void) printf(" This is harmless, but not normal.\n");
}
tcp_all = &type_counts[0];
tcp_norm = &type_counts[FSD_ACL];
tcp_def = &type_counts[FSD_DFACL];
tcp_all->nuser_objs = tcp_def->nuser_objs + tcp_norm->nuser_objs;
tcp_all->ngroup_objs = tcp_def->ngroup_objs + tcp_norm->ngroup_objs;
tcp_all->nother_objs = tcp_def->nother_objs + tcp_norm->nother_objs;
tcp_all->nclass_objs = tcp_def->nclass_objs + tcp_norm->nclass_objs;
tcp_all->ndef_user_objs =
tcp_def->ndef_user_objs + tcp_norm->ndef_user_objs;
tcp_all->ndef_group_objs =
tcp_def->ndef_group_objs + tcp_norm->ndef_group_objs;
tcp_all->ndef_other_objs =
tcp_def->ndef_other_objs + tcp_norm->ndef_other_objs;
tcp_all->ndef_class_objs =
tcp_def->ndef_class_objs + tcp_norm->ndef_class_objs;
tcp_all->nusers = tcp_def->nusers + tcp_norm->nusers;
tcp_all->ngroups = tcp_def->ngroups + tcp_norm->ngroups;
tcp_all->ndef_users = tcp_def->ndef_users + tcp_norm->ndef_users;
tcp_all->ndef_groups = tcp_def->ndef_groups + tcp_norm->ndef_groups;
if (tcp_all->nuser_objs != 1 ||
tcp_all->ngroup_objs != 1 ||
tcp_all->nother_objs != 1 ||
tcp_all->nclass_objs > 1) {
return (1);
}
if (tcp_all->ngroups && !tcp_all->nclass_objs) {
return (1);
}
if (tcp_all->ndef_user_objs > 1 ||
tcp_all->ndef_group_objs > 1 ||
tcp_all->ndef_other_objs > 1 ||
tcp_all->ndef_class_objs > 1) {
return (1);
}
numdefs = tcp_all->ndef_other_objs + tcp_all->ndef_user_objs +
tcp_all->ndef_group_objs;
if (numdefs != 0 && numdefs != 3) {
return (1);
}
if (numdefs != 0) {
for (sci = shadowclientinfo; sci != NULL; sci = sci->next)
if (sci->shadow == inum)
break;
if ((sci == NULL) || (sci->clients == NULL))
return (1);
for (scc = sci->clients; scc != NULL; scc = scc->next) {
for (numtargets = 0; numtargets < scc->nclients;
numtargets++) {
target = scc->client[numtargets];
if (!INO_IS_DVALID(target))
return (1);
}
}
}
if (tcp_all->ndef_groups && !tcp_all->ndef_class_objs) {
return (1);
}
if ((tcp_all->ndef_users || tcp_all->ndef_groups) &&
((numdefs != 3) && !tcp_all->ndef_class_objs)) {
return (1);
}
return (0);
}