summaryrefslogtreecommitdiff
path: root/sys/kern
diff options
context:
space:
mode:
authorPhilip Guenthe <guenther@cvs.openbsd.org>2011-12-14 07:32:17 +0000
committerPhilip Guenthe <guenther@cvs.openbsd.org>2011-12-14 07:32:17 +0000
commit24411f6f435ea431230a0ce4d62b6e8c4a67208e (patch)
tree315790d47bf822b203e1d7c55089fd77c8323b07 /sys/kern
parentfa917446d27705e0fa3c44184e6e9b99906c1af2 (diff)
Handle rthreads consistently in ktrace by moving the flags and vnode into
struct process; KTRFAC_ACTIVE becomes P_INKTR. Also, save the credentials used to open the file in sys_ktrace() and use them for all writes to the vnode. much feedback and ok jsing@
Diffstat (limited to 'sys/kern')
-rw-r--r--sys/kern/kern_exec.c8
-rw-r--r--sys/kern/kern_exit.c14
-rw-r--r--sys/kern/kern_fork.c10
-rw-r--r--sys/kern/kern_ktrace.c208
-rw-r--r--sys/kern/kern_sysctl.c10
5 files changed, 141 insertions, 109 deletions
diff --git a/sys/kern/kern_exec.c b/sys/kern/kern_exec.c
index ff53d3feb9a..c72d2fcdead 100644
--- a/sys/kern/kern_exec.c
+++ b/sys/kern/kern_exec.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: kern_exec.c,v 1.121 2011/12/11 19:42:28 guenther Exp $ */
+/* $OpenBSD: kern_exec.c,v 1.122 2011/12/14 07:32:16 guenther Exp $ */
/* $NetBSD: kern_exec.c,v 1.75 1996/02/09 18:59:28 christos Exp $ */
/*-
@@ -516,10 +516,8 @@ sys_execve(struct proc *p, void *v, register_t *retval)
* If process is being ktraced, turn off - unless
* root set it.
*/
- if (p->p_tracep && !(p->p_traceflag & KTRFAC_ROOT)) {
- p->p_traceflag = 0;
- ktrsettracevnode(p, NULL);
- }
+ if (pr->ps_tracevp && !(pr->ps_traceflag & KTRFAC_ROOT))
+ ktrcleartrace(pr);
#endif
p->p_ucred = crcopy(cred);
if (attr.va_mode & VSUID)
diff --git a/sys/kern/kern_exit.c b/sys/kern/kern_exit.c
index 3e029725a0c..ecd63cf0fda 100644
--- a/sys/kern/kern_exit.c
+++ b/sys/kern/kern_exit.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: kern_exit.c,v 1.104 2011/12/11 19:42:28 guenther Exp $ */
+/* $OpenBSD: kern_exit.c,v 1.105 2011/12/14 07:32:16 guenther Exp $ */
/* $NetBSD: kern_exit.c,v 1.39 1996/04/22 01:38:25 christos Exp $ */
/*
@@ -220,16 +220,14 @@ exit1(struct proc *p, int rv, int flags)
#ifdef ACCOUNTING
(void)acct_process(p);
#endif
- }
#ifdef KTRACE
- /*
- * release trace file
- */
- p->p_traceflag = 0; /* don't trace the vrele() */
- if (p->p_tracep)
- ktrsettracevnode(p, NULL);
+ /* release trace file */
+ if (pr->ps_tracevp)
+ ktrcleartrace(pr);
#endif
+ }
+
#if NSYSTRACE > 0
if (ISSET(p->p_flag, P_SYSTRACE))
systrace_exit(p);
diff --git a/sys/kern/kern_fork.c b/sys/kern/kern_fork.c
index f5cd1f8eeb2..0c3745ba139 100644
--- a/sys/kern/kern_fork.c
+++ b/sys/kern/kern_fork.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: kern_fork.c,v 1.132 2011/11/22 23:20:19 joshe Exp $ */
+/* $OpenBSD: kern_fork.c,v 1.133 2011/12/14 07:32:16 guenther Exp $ */
/* $NetBSD: kern_fork.c,v 1.29 1996/02/09 18:59:34 christos Exp $ */
/*
@@ -367,11 +367,9 @@ fork1(struct proc *curp, int exitsig, int flags, void *stack, pid_t *tidptr,
* Copy traceflag and tracefile if enabled.
* If not inherited, these were zeroed above.
*/
- if (curp->p_traceflag & KTRFAC_INHERIT) {
- p->p_traceflag = curp->p_traceflag;
- if ((p->p_tracep = curp->p_tracep) != NULL)
- vref(p->p_tracep);
- }
+ if ((flags & FORK_THREAD) == 0 && curpr->ps_traceflag & KTRFAC_INHERIT)
+ ktrsettrace(pr, curpr->ps_traceflag, curpr->ps_tracevp,
+ curpr->ps_tracecred);
#endif
/*
diff --git a/sys/kern/kern_ktrace.c b/sys/kern/kern_ktrace.c
index 477a0922a8e..6811cfb48a0 100644
--- a/sys/kern/kern_ktrace.c
+++ b/sys/kern/kern_ktrace.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: kern_ktrace.c,v 1.55 2011/11/21 03:01:20 djm Exp $ */
+/* $OpenBSD: kern_ktrace.c,v 1.56 2011/12/14 07:32:16 guenther Exp $ */
/* $NetBSD: kern_ktrace.c,v 1.23 1996/02/09 18:59:36 christos Exp $ */
/*
@@ -52,32 +52,68 @@
#include <uvm/uvm_extern.h>
-void ktrinitheader(struct ktr_header *, struct proc *, int);
-int ktrops(struct proc *, struct proc *, int, int, struct vnode *);
-int ktrsetchildren(struct proc *, struct process *, int, int,
- struct vnode *);
-int ktrwrite(struct proc *, struct ktr_header *);
-int ktrcanset(struct proc *, struct proc *);
+void ktrinitheader(struct ktr_header *, struct proc *, int);
+int ktrops(struct proc *, struct process *, int, int, struct vnode *,
+ struct ucred *);
+int ktrsetchildren(struct proc *, struct process *, int, int,
+ struct vnode *, struct ucred *);
+int ktrwrite(struct proc *, struct ktr_header *);
+int ktrcanset(struct proc *, struct process *);
/*
- * Change the trace vnode in a correct way (to avoid races).
+ * Clear the trace settings in a correct way (to avoid races).
*/
void
-ktrsettracevnode(struct proc *p, struct vnode *newvp)
+ktrcleartrace(struct process *pr)
{
struct vnode *vp;
+ struct ucred *cred;
- if (p->p_tracep == newvp) /* avoid work */
- return;
-
- if (newvp != NULL)
- vref(newvp);
+ if (pr->ps_tracevp != NULL) {
+ vp = pr->ps_tracevp;
+ cred = pr->ps_tracecred;
- vp = p->p_tracep;
- p->p_tracep = newvp;
+ pr->ps_traceflag = 0;
+ pr->ps_tracevp = NULL;
+ pr->ps_tracecred = NULL;
- if (vp != NULL)
vrele(vp);
+ crfree(cred);
+ }
+}
+
+/*
+ * Change the trace setting in a correct way (to avoid races).
+ */
+void
+ktrsettrace(struct process *pr, int facs, struct vnode *newvp,
+ struct ucred *newcred)
+{
+ struct vnode *oldvp;
+ struct ucred *oldcred;
+
+ KASSERT(newvp != NULL);
+ KASSERT(newcred != NULL);
+
+ pr->ps_traceflag |= facs;
+
+ /* nothing to change about where the trace goes? */
+ if (pr->ps_tracevp == newvp && pr->ps_tracecred == newcred)
+ return;
+
+ vref(newvp);
+ crhold(newcred);
+
+ oldvp = pr->ps_tracevp;
+ oldcred = pr->ps_tracecred;
+
+ pr->ps_tracevp = newvp;
+ pr->ps_tracecred = newcred;
+
+ if (oldvp != NULL) {
+ vrele(oldvp);
+ crfree(oldcred);
+ }
}
void
@@ -109,7 +145,7 @@ ktrsyscall(struct proc *p, register_t code, size_t argsize, register_t args[])
nargs = min(args[1], CTL_MAXNAME);
len += nargs * sizeof(int);
}
- p->p_traceflag |= KTRFAC_ACTIVE;
+ atomic_setbits_int(&p->p_flag, P_INKTR);
ktrinitheader(&kth, p, KTR_SYSCALL);
ktp = malloc(len, M_TEMP, M_WAITOK);
ktp->ktr_code = code;
@@ -125,7 +161,7 @@ ktrsyscall(struct proc *p, register_t code, size_t argsize, register_t args[])
kth.ktr_len = len;
ktrwrite(p, &kth);
free(ktp, M_TEMP);
- p->p_traceflag &= ~KTRFAC_ACTIVE;
+ atomic_clearbits_int(&p->p_flag, P_INKTR);
}
void
@@ -134,7 +170,7 @@ ktrsysret(struct proc *p, register_t code, int error, register_t retval)
struct ktr_header kth;
struct ktr_sysret ktp;
- p->p_traceflag |= KTRFAC_ACTIVE;
+ atomic_setbits_int(&p->p_flag, P_INKTR);
ktrinitheader(&kth, p, KTR_SYSRET);
ktp.ktr_code = code;
ktp.ktr_error = error;
@@ -144,7 +180,7 @@ ktrsysret(struct proc *p, register_t code, int error, register_t retval)
kth.ktr_len = sizeof(struct ktr_sysret);
ktrwrite(p, &kth);
- p->p_traceflag &= ~KTRFAC_ACTIVE;
+ atomic_clearbits_int(&p->p_flag, P_INKTR);
}
void
@@ -152,13 +188,13 @@ ktrnamei(struct proc *p, char *path)
{
struct ktr_header kth;
- p->p_traceflag |= KTRFAC_ACTIVE;
+ atomic_setbits_int(&p->p_flag, P_INKTR);
ktrinitheader(&kth, p, KTR_NAMEI);
kth.ktr_len = strlen(path);
kth.ktr_buf = path;
ktrwrite(p, &kth);
- p->p_traceflag &= ~KTRFAC_ACTIVE;
+ atomic_clearbits_int(&p->p_flag, P_INKTR);
}
void
@@ -166,13 +202,13 @@ ktremul(struct proc *p, char *emul)
{
struct ktr_header kth;
- p->p_traceflag |= KTRFAC_ACTIVE;
+ atomic_setbits_int(&p->p_flag, P_INKTR);
ktrinitheader(&kth, p, KTR_EMUL);
kth.ktr_len = strlen(emul);
kth.ktr_buf = emul;
ktrwrite(p, &kth);
- p->p_traceflag &= ~KTRFAC_ACTIVE;
+ atomic_clearbits_int(&p->p_flag, P_INKTR);
}
void
@@ -188,7 +224,7 @@ ktrgenio(struct proc *p, int fd, enum uio_rw rw, struct iovec *iov, int len,
if (error)
return;
- p->p_traceflag |= KTRFAC_ACTIVE;
+ atomic_setbits_int(&p->p_flag, P_INKTR);
buflen = min(PAGE_SIZE, len + sizeof(struct ktr_genio));
@@ -231,7 +267,7 @@ ktrgenio(struct proc *p, int fd, enum uio_rw rw, struct iovec *iov, int len,
}
free(ktp, M_TEMP);
- p->p_traceflag &= ~KTRFAC_ACTIVE;
+ atomic_clearbits_int(&p->p_flag, P_INKTR);
}
@@ -242,7 +278,7 @@ ktrpsig(struct proc *p, int sig, sig_t action, int mask, int code,
struct ktr_header kth;
struct ktr_psig kp;
- p->p_traceflag |= KTRFAC_ACTIVE;
+ atomic_setbits_int(&p->p_flag, P_INKTR);
ktrinitheader(&kth, p, KTR_PSIG);
kp.signo = (char)sig;
kp.action = action;
@@ -253,7 +289,7 @@ ktrpsig(struct proc *p, int sig, sig_t action, int mask, int code,
kth.ktr_len = sizeof(struct ktr_psig);
ktrwrite(p, &kth);
- p->p_traceflag &= ~KTRFAC_ACTIVE;
+ atomic_clearbits_int(&p->p_flag, P_INKTR);
}
void
@@ -262,7 +298,7 @@ ktrcsw(struct proc *p, int out, int user)
struct ktr_header kth;
struct ktr_csw kc;
- p->p_traceflag |= KTRFAC_ACTIVE;
+ atomic_setbits_int(&p->p_flag, P_INKTR);
ktrinitheader(&kth, p, KTR_CSW);
kc.out = out;
kc.user = user;
@@ -270,7 +306,7 @@ ktrcsw(struct proc *p, int out, int user)
kth.ktr_len = sizeof(struct ktr_csw);
ktrwrite(p, &kth);
- p->p_traceflag &= ~KTRFAC_ACTIVE;
+ atomic_clearbits_int(&p->p_flag, P_INKTR);
}
void
@@ -280,7 +316,7 @@ ktrstruct(struct proc *p, const char *name, const void *data, size_t datalen)
void *buf;
size_t buflen;
- p->p_traceflag |= KTRFAC_ACTIVE;
+ atomic_setbits_int(&p->p_flag, P_INKTR);
ktrinitheader(&kth, p, KTR_STRUCT);
if (data == NULL)
@@ -294,7 +330,7 @@ ktrstruct(struct proc *p, const char *name, const void *data, size_t datalen)
ktrwrite(p, &kth);
free(buf, M_TEMP);
- p->p_traceflag &= ~KTRFAC_ACTIVE;
+ atomic_clearbits_int(&p->p_flag, P_INKTR);
}
/* Interface and common routines */
@@ -315,6 +351,7 @@ sys_ktrace(struct proc *curp, void *v, register_t *retval)
struct vnode *vp = NULL;
struct proc *p = NULL;
struct process *pr = NULL;
+ struct ucred *cred = NULL;
struct pgrp *pg;
int facs = SCARG(uap, facs) & ~((unsigned) KTRFAC_ROOT);
int ops = KTROP(SCARG(uap, ops));
@@ -323,24 +360,23 @@ sys_ktrace(struct proc *curp, void *v, register_t *retval)
int error = 0;
struct nameidata nd;
- curp->p_traceflag |= KTRFAC_ACTIVE;
+ atomic_setbits_int(&curp->p_flag, P_INKTR);
if (ops != KTROP_CLEAR) {
/*
* an operation which requires a file argument.
*/
+ cred = curp->p_ucred;
+ crhold(cred);
NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, fname),
curp);
- if ((error = vn_open(&nd, FREAD|FWRITE|O_NOFOLLOW, 0)) != 0) {
- curp->p_traceflag &= ~KTRFAC_ACTIVE;
- return (error);
- }
+ if ((error = vn_open(&nd, FREAD|FWRITE|O_NOFOLLOW, 0)) != 0)
+ goto done;
vp = nd.ni_vp;
VOP_UNLOCK(vp, 0, curp);
if (vp->v_type != VREG) {
- (void) vn_close(vp, FREAD|FWRITE, curp->p_ucred, curp);
- curp->p_traceflag &= ~KTRFAC_ACTIVE;
- return (EACCES);
+ error = EACCES;
+ goto done;
}
}
/*
@@ -348,11 +384,10 @@ sys_ktrace(struct proc *curp, void *v, register_t *retval)
*/
if (ops == KTROP_CLEARFILE) {
LIST_FOREACH(p, &allproc, p_list) {
- if (p->p_tracep == vp) {
- if (ktrcanset(curp, p)) {
- p->p_traceflag = 0;
- ktrsettracevnode(p, NULL);
- } else
+ if (p->p_p->ps_tracevp == vp) {
+ if (ktrcanset(curp, p->p_p))
+ ktrcleartrace(p->p_p);
+ else
error = EPERM;
}
}
@@ -365,6 +400,8 @@ sys_ktrace(struct proc *curp, void *v, register_t *retval)
error = EINVAL;
goto done;
}
+ if (ops == KTROP_SET && suser(curp, 0) == 0)
+ facs |= KTRFAC_ROOT;
/*
* do it
*/
@@ -379,10 +416,10 @@ sys_ktrace(struct proc *curp, void *v, register_t *retval)
}
LIST_FOREACH(pr, &pg->pg_members, ps_pglist) {
if (descend)
- ret |= ktrsetchildren(curp, pr, ops, facs, vp);
+ ret |= ktrsetchildren(curp, pr, ops, facs, vp,
+ cred);
else
- TAILQ_FOREACH(p, &pr->ps_threads, p_thr_link)
- ret |= ktrops(curp, p, ops, facs, vp);
+ ret |= ktrops(curp, pr, ops, facs, vp, cred);
}
} else {
@@ -395,63 +432,64 @@ sys_ktrace(struct proc *curp, void *v, register_t *retval)
goto done;
}
if (descend)
- ret |= ktrsetchildren(curp, pr, ops, facs, vp);
+ ret |= ktrsetchildren(curp, pr, ops, facs, vp, cred);
else
- TAILQ_FOREACH(p, &pr->ps_threads, p_thr_link) {
- ret |= ktrops(curp, p, ops, facs, vp);
- }
+ ret |= ktrops(curp, pr, ops, facs, vp, cred);
}
if (!ret)
error = EPERM;
done:
if (vp != NULL)
- (void) vn_close(vp, FWRITE, curp->p_ucred, curp);
- curp->p_traceflag &= ~KTRFAC_ACTIVE;
+ (void) vn_close(vp, FREAD|FWRITE, cred, curp);
+ if (cred != NULL)
+ crfree(cred);
+ atomic_clearbits_int(&curp->p_flag, P_INKTR);
return (error);
}
int
-ktrops(struct proc *curp, struct proc *p, int ops, int facs, struct vnode *vp)
+ktrops(struct proc *curp, struct process *pr, int ops, int facs,
+ struct vnode *vp, struct ucred *cred)
{
+ struct proc *p;
- if (!ktrcanset(curp, p))
+ if (!ktrcanset(curp, pr))
return (0);
- if (ops == KTROP_SET) {
- ktrsettracevnode(p, vp);
- p->p_traceflag |= facs;
- if (suser(curp, 0) == 0)
- p->p_traceflag |= KTRFAC_ROOT;
- } else {
+ if (ops == KTROP_SET)
+ ktrsettrace(pr, facs, vp, cred);
+ else {
/* KTROP_CLEAR */
- if (((p->p_traceflag &= ~facs) & KTRFAC_MASK) == 0) {
- /* no more tracing */
- p->p_traceflag = 0;
- ktrsettracevnode(p, NULL);
+ pr->ps_traceflag &= ~facs;
+ if ((pr->ps_traceflag & KTRFAC_MASK) == 0) {
+ /* cleared all the facility bits, so stop completely */
+ ktrcleartrace(pr);
}
}
/*
* Emit an emulation record, every time there is a ktrace
* change/attach request.
+ * XXX an EMUL record for each thread? Perhaps should have
+ * XXX a record type to say "this pid is really a thread of this
+ * XXX other pid" and only generate an EMUL record for the main pid
*/
- if (KTRPOINT(p, KTR_EMUL))
- ktremul(p, p->p_emul->e_name);
+ TAILQ_FOREACH(p, &pr->ps_threads, p_thr_link)
+ if (KTRPOINT(p, KTR_EMUL))
+ ktremul(p, p->p_emul->e_name);
return (1);
}
int
ktrsetchildren(struct proc *curp, struct process *top, int ops, int facs,
- struct vnode *vp)
+ struct vnode *vp, struct ucred *cred)
{
struct process *pr;
- struct proc *p;
int ret = 0;
pr = top;
for (;;) {
- TAILQ_FOREACH(p, &pr->ps_threads, p_thr_link)
- ret |= ktrops(curp, p, ops, facs, vp);
+ ret |= ktrops(curp, pr, ops, facs, vp, cred);
/*
* If this process has children, descend to them next,
* otherwise do any siblings, and if done with this level,
@@ -478,7 +516,8 @@ ktrwrite(struct proc *p, struct ktr_header *kth)
struct uio auio;
struct iovec aiov[2];
int error;
- struct vnode *vp = p->p_tracep;
+ struct vnode *vp = p->p_p->ps_tracevp;
+ struct ucred *cred = p->p_p->ps_tracecred;
if (vp == NULL)
return 0;
@@ -497,8 +536,10 @@ ktrwrite(struct proc *p, struct ktr_header *kth)
aiov[1].iov_len = kth->ktr_len;
auio.uio_resid += kth->ktr_len;
}
+ crhold(cred);
vget(vp, LK_EXCLUSIVE | LK_RETRY, p);
- error = VOP_WRITE(vp, &auio, IO_UNIT|IO_APPEND, p->p_ucred);
+ error = VOP_WRITE(vp, &auio, IO_UNIT|IO_APPEND, cred);
+ crfree(cred);
if (!error) {
vput(vp);
return (0);
@@ -508,12 +549,9 @@ ktrwrite(struct proc *p, struct ktr_header *kth)
*/
log(LOG_NOTICE, "ktrace write failed, errno %d, tracing stopped\n",
error);
- LIST_FOREACH(p, &allproc, p_list) {
- if (p->p_tracep == vp) {
- p->p_traceflag = 0;
- ktrsettracevnode(p, NULL);
- }
- }
+ LIST_FOREACH(p, &allproc, p_list)
+ if (p->p_p->ps_tracevp == vp)
+ ktrcleartrace(p->p_p);
vput(vp);
return (error);
@@ -529,17 +567,17 @@ ktrwrite(struct proc *p, struct ktr_header *kth)
* TODO: check groups. use caller effective gid.
*/
int
-ktrcanset(struct proc *callp, struct proc *targetp)
+ktrcanset(struct proc *callp, struct process *targetpr)
{
struct pcred *caller = callp->p_cred;
- struct pcred *target = targetp->p_cred;
+ struct pcred *target = targetpr->ps_cred;
if ((caller->pc_ucred->cr_uid == target->p_ruid &&
target->p_ruid == target->p_svuid &&
caller->p_rgid == target->p_rgid && /* XXX */
target->p_rgid == target->p_svgid &&
- (targetp->p_traceflag & KTRFAC_ROOT) == 0 &&
- !ISSET(targetp->p_p->ps_flags, PS_SUGID)) ||
+ (targetpr->ps_traceflag & KTRFAC_ROOT) == 0 &&
+ !ISSET(targetpr->ps_flags, PS_SUGID)) ||
caller->pc_ucred->cr_uid == 0)
return (1);
diff --git a/sys/kern/kern_sysctl.c b/sys/kern/kern_sysctl.c
index 4e964d0008c..478ee32e2ca 100644
--- a/sys/kern/kern_sysctl.c
+++ b/sys/kern/kern_sysctl.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: kern_sysctl.c,v 1.209 2011/12/09 16:14:54 nicm Exp $ */
+/* $OpenBSD: kern_sysctl.c,v 1.210 2011/12/14 07:32:16 guenther Exp $ */
/* $NetBSD: kern_sysctl.c,v 1.17 1996/05/20 17:49:05 mrg Exp $ */
/*-
@@ -1291,8 +1291,8 @@ sysctl_file2(int *name, u_int namelen, char *where, size_t *sizep,
FILLIT(NULL, NULL, KERN_FILE_CDIR, fdp->fd_cdir, pp);
if (fdp->fd_rdir)
FILLIT(NULL, NULL, KERN_FILE_RDIR, fdp->fd_rdir, pp);
- if (pp->p_tracep)
- FILLIT(NULL, NULL, KERN_FILE_TRACE, pp->p_tracep, pp);
+ if (pp->p_p->ps_tracevp)
+ FILLIT(NULL, NULL, KERN_FILE_TRACE, pp->p_p->ps_tracevp, pp);
for (i = 0; i < fdp->fd_nfiles; i++) {
if ((fp = fdp->fd_ofiles[i]) == NULL)
continue;
@@ -1317,8 +1317,8 @@ sysctl_file2(int *name, u_int namelen, char *where, size_t *sizep,
FILLIT(NULL, NULL, KERN_FILE_CDIR, fdp->fd_cdir, pp);
if (fdp->fd_rdir)
FILLIT(NULL, NULL, KERN_FILE_RDIR, fdp->fd_rdir, pp);
- if (pp->p_tracep)
- FILLIT(NULL, NULL, KERN_FILE_TRACE, pp->p_tracep, pp);
+ if (pp->p_p->ps_tracevp)
+ FILLIT(NULL, NULL, KERN_FILE_TRACE, pp->p_p->ps_tracevp, pp);
for (i = 0; i < fdp->fd_nfiles; i++) {
if ((fp = fdp->fd_ofiles[i]) == NULL)
continue;