diff options
author | bdrewery <bdrewery@FreeBSD.org> | 2016-06-21 20:13:19 +0000 |
---|---|---|
committer | bdrewery <bdrewery@FreeBSD.org> | 2016-06-21 20:13:19 +0000 |
commit | 25a0ea679a9c5b8f34db216b7b036b9211c872ac (patch) | |
tree | 7b28648b0d6f6a0438793717267b560b0210b490 | |
parent | d3fb5294b09a6129d53ecfd9667cb1c4c47ddddb (diff) | |
download | FreeBSD-src-25a0ea679a9c5b8f34db216b7b036b9211c872ac.zip FreeBSD-src-25a0ea679a9c5b8f34db216b7b036b9211c872ac.tar.gz |
MFC r297156,r297157,r297158,r297159,r297161,r297172,r297200,r297201,r297202,
r297203,r297256:
r297156:
Track filemon usage via a proc.p_filemon pointer rather than its own lists.
r297157:
Stop tracking stat(2).
r297158:
Consolidate open(2) and openat(2) code.
r297159:
Use curthread for vn_fullpath.
r297161:
Attempt to use the namecache for openat(2) path resolution.
r297172:
Consolidate common link(2) logic.
r297200:
Follow-up r297156: Close the log in filemon_dtr rather than in the last
reference.
r297201:
Return any log write failure encountered when closing the filemon fd.
r297202:
Remove unused done argument to copyinstr(9).
r297203:
Handle copyin failures.
r297256:
Remove unneeded return left from refactoring.
Relnotes: yes (filemon stability/performance updates)
Sponsored by: EMC / Isilon Storage Division
-rw-r--r-- | share/man/man4/filemon.4 | 46 | ||||
-rw-r--r-- | sys/dev/filemon/filemon.c | 356 | ||||
-rw-r--r-- | sys/dev/filemon/filemon_lock.c | 57 | ||||
-rw-r--r-- | sys/dev/filemon/filemon_wrapper.c | 423 | ||||
-rw-r--r-- | sys/kern/kern_fork.c | 1 | ||||
-rw-r--r-- | sys/sys/param.h | 2 | ||||
-rw-r--r-- | sys/sys/proc.h | 2 |
7 files changed, 477 insertions, 410 deletions
diff --git a/share/man/man4/filemon.4 b/share/man/man4/filemon.4 index 92876a3..65ef7bc 100644 --- a/share/man/man4/filemon.4 +++ b/share/man/man4/filemon.4 @@ -31,7 +31,7 @@ .\" .\" $FreeBSD$ .\" -.Dd March 9, 2016 +.Dd June 21, 2016 .Dt FILEMON 4 .Os .Sh NAME @@ -83,8 +83,6 @@ System calls are denoted using the following single letters: .It Ql R .Xr open 2 for read -.It Ql S -.Xr stat 2 .It Ql W .Xr open 2 for write @@ -116,6 +114,10 @@ Each takes a single argument. Write the internal tracing buffer to the supplied open file descriptor. .It Dv FILEMON_SET_PID Child process ID to trace. +This should normally be done under the control of a parent in the child after +.Xr fork 2 +but before anything else. +See the example below. .El .Sh RETURN VALUES .\" .Rv -std ioctl @@ -138,6 +140,35 @@ The .Nm handle is already associated with a file descriptor. .El +.Pp +The +.Fn ioctl +system call +with +.Dv FILEMON_SET_PID +will fail if: +.Bl -tag -width Er +.It Bq Er ESRCH +No process having the specified process ID exists. +.It Bq Er EBUSY +The process ID specified is already being traced and was not the current +process. +.El +.Pp +The +.Fn close +system call on the filemon file descriptor may fail with the errors from +.Xr write 2 +if any error is encountered while writing the log. +It may also fail if: +.Bl -tag -width Er +.It Bq Er EFAULT +An invalid address was used for a traced system call argument, resulting in +no log entry for the system call. +.It Bq Er ENAMETOOLONG +An argument for a traced system call was too long, resulting in +no log entry for the system call. +.El .Sh FILES .Bl -tag -width ".Pa /dev/filemon" .It Pa /dev/filemon @@ -198,14 +229,5 @@ A device appeared in .Fx 9.1 . .Sh BUGS -Loading -.Nm -may reduce system performance for the noted syscalls. -.Pp -Only children of the set process are logged. -Processes can escape being traced by double forking. -This is not seen as a problem as the intended use is build monitoring, which -does not make sense to have daemons for. -.Pp Unloading the module may panic the system, thus requires using .Ic kldunload -f . diff --git a/sys/dev/filemon/filemon.c b/sys/dev/filemon/filemon.c index cd40c5a..10f27ad 100644 --- a/sys/dev/filemon/filemon.c +++ b/sys/dev/filemon/filemon.c @@ -1,7 +1,7 @@ /*- * Copyright (c) 2011, David E. O'Brien. * Copyright (c) 2009-2011, Juniper Networks, Inc. - * Copyright (c) 2015, EMC Corp. + * Copyright (c) 2015-2016, EMC Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -46,7 +46,6 @@ __FBSDID("$FreeBSD$"); #include <sys/module.h> #include <sys/poll.h> #include <sys/proc.h> -#include <sys/queue.h> #include <sys/sx.h> #include <sys/syscall.h> #include <sys/sysent.h> @@ -80,23 +79,112 @@ static struct cdevsw filemon_cdevsw = { MALLOC_DECLARE(M_FILEMON); MALLOC_DEFINE(M_FILEMON, "filemon", "File access monitor"); +/* + * The filemon->lock protects several things currently: + * - fname1/fname2/msgbufr are pre-allocated and used per syscall + * for logging and copyins rather than stack variables. + * - Serializing the filemon's log output. + * - Preventing inheritance or removal of the filemon into proc.p_filemon. + */ struct filemon { - TAILQ_ENTRY(filemon) link; /* Link into the in-use list. */ - struct sx lock; /* Lock mutex for this filemon. */ + struct sx lock; /* Lock for this filemon. */ struct file *fp; /* Output file pointer. */ - struct proc *p; /* The process being monitored. */ char fname1[MAXPATHLEN]; /* Temporary filename buffer. */ char fname2[MAXPATHLEN]; /* Temporary filename buffer. */ char msgbufr[1024]; /* Output message buffer. */ + int error; /* Log write error, returned on close(2). */ + u_int refcnt; /* Pointer reference count. */ + u_int proccnt; /* Process count. */ }; -static TAILQ_HEAD(, filemon) filemons_inuse = TAILQ_HEAD_INITIALIZER(filemons_inuse); -static TAILQ_HEAD(, filemon) filemons_free = TAILQ_HEAD_INITIALIZER(filemons_free); -static struct sx access_lock; - static struct cdev *filemon_dev; +static void filemon_output(struct filemon *filemon, char *msg, size_t len); + +static __inline struct filemon * +filemon_acquire(struct filemon *filemon) +{ + + if (filemon != NULL) + refcount_acquire(&filemon->refcnt); + return (filemon); +} + +/* + * Release a reference and free on the last one. + */ +static void +filemon_release(struct filemon *filemon) +{ + + if (refcount_release(&filemon->refcnt) == 0) + return; + /* + * There are valid cases of releasing while locked, such as in + * filemon_untrack_processes, but none which are done where there + * is not at least 1 reference remaining. + */ + sx_assert(&filemon->lock, SA_UNLOCKED); + + sx_destroy(&filemon->lock); + free(filemon, M_FILEMON); +} + +/* + * Acquire the proc's p_filemon reference and lock the filemon. + * The proc's p_filemon may not match this filemon on return. + */ +static struct filemon * +filemon_proc_get(struct proc *p) +{ + struct filemon *filemon; + + PROC_LOCK(p); + filemon = filemon_acquire(p->p_filemon); + PROC_UNLOCK(p); + + if (filemon == NULL) + return (NULL); + /* + * The p->p_filemon may have changed by now. That case is handled + * by the exit and fork hooks and filemon_attach_proc specially. + */ + sx_xlock(&filemon->lock); + return (filemon); +} + +/* Remove and release the filemon on the given process. */ +static void +filemon_proc_drop(struct proc *p) +{ + struct filemon *filemon; + + KASSERT(p->p_filemon != NULL, ("%s: proc %p NULL p_filemon", + __func__, p)); + sx_assert(&p->p_filemon->lock, SA_XLOCKED); + PROC_LOCK(p); + filemon = p->p_filemon; + p->p_filemon = NULL; + --filemon->proccnt; + PROC_UNLOCK(p); + /* + * This should not be the last reference yet. filemon_release() + * cannot be called with filemon locked, which the caller expects + * will stay locked. + */ + KASSERT(filemon->refcnt > 1, ("%s: proc %p dropping filemon %p " + "with last reference", __func__, p, filemon)); + filemon_release(filemon); +} + +/* Unlock and release the filemon. */ +static __inline void +filemon_drop(struct filemon *filemon) +{ + + sx_xunlock(&filemon->lock); + filemon_release(filemon); +} -#include "filemon_lock.c" #include "filemon_wrapper.c" static void @@ -115,35 +203,151 @@ filemon_comment(struct filemon *filemon) filemon_output(filemon, filemon->msgbufr, len); } +/* + * Invalidate the passed filemon in all processes. + */ static void -filemon_dtr(void *data) +filemon_untrack_processes(struct filemon *filemon) { - struct filemon *filemon = data; + struct proc *p; - if (filemon != NULL) { - struct file *fp; + sx_assert(&filemon->lock, SA_XLOCKED); - /* Follow same locking order as filemon_pid_check. */ - filemon_lock_write(); - sx_xlock(&filemon->lock); + /* Avoid allproc loop if there is no need. */ + if (filemon->proccnt == 0) + return; + + /* + * Processes in this list won't go away while here since + * filemon_event_process_exit() will lock on filemon->lock + * which we hold. + */ + sx_slock(&allproc_lock); + FOREACH_PROC_IN_SYSTEM(p) { + /* + * No PROC_LOCK is needed to compare here since it is + * guaranteed to not change since we have its filemon + * locked. Everything that changes this p_filemon will + * be locked on it. + */ + if (p->p_filemon == filemon) + filemon_proc_drop(p); + } + sx_sunlock(&allproc_lock); + + /* + * It's possible some references were acquired but will be + * dropped shortly as they are restricted from being + * inherited. There is at least the reference in cdevpriv remaining. + */ + KASSERT(filemon->refcnt > 0, ("%s: filemon %p should have " + "references still.", __func__, filemon)); + KASSERT(filemon->proccnt == 0, ("%s: filemon %p should not have " + "attached procs still.", __func__, filemon)); +} - /* Remove from the in-use list. */ - TAILQ_REMOVE(&filemons_inuse, filemon, link); +/* + * Close out the log. + */ +static void +filemon_close_log(struct filemon *filemon) +{ + struct file *fp; + struct timeval now; + size_t len; - fp = filemon->fp; - filemon->fp = NULL; - filemon->p = NULL; + sx_assert(&filemon->lock, SA_XLOCKED); + if (filemon->fp == NULL) + return; - /* Add to the free list. */ - TAILQ_INSERT_TAIL(&filemons_free, filemon, link); + getmicrotime(&now); - /* Give up write access. */ - sx_xunlock(&filemon->lock); - filemon_unlock_write(); + len = snprintf(filemon->msgbufr, + sizeof(filemon->msgbufr), + "# Stop %ju.%06ju\n# Bye bye\n", + (uintmax_t)now.tv_sec, (uintmax_t)now.tv_usec); + + filemon_output(filemon, filemon->msgbufr, len); + fp = filemon->fp; + filemon->fp = NULL; + + sx_xunlock(&filemon->lock); + fdrop(fp, curthread); + sx_xlock(&filemon->lock); +} + +/* + * The devfs file is being closed. Untrace all processes. It is possible + * filemon_close/close(2) was not called. + */ +static void +filemon_dtr(void *data) +{ + struct filemon *filemon = data; - if (fp != NULL) - fdrop(fp, curthread); + if (filemon == NULL) + return; + + sx_xlock(&filemon->lock); + /* + * Detach the filemon. It cannot be inherited after this. + */ + filemon_untrack_processes(filemon); + filemon_close_log(filemon); + filemon_drop(filemon); +} + +/* Attach the filemon to the process. */ +static int +filemon_attach_proc(struct filemon *filemon, struct proc *p) +{ + struct filemon *filemon2; + + sx_assert(&filemon->lock, SA_XLOCKED); + PROC_LOCK_ASSERT(p, MA_OWNED); + KASSERT((p->p_flag & P_WEXIT) == 0, + ("%s: filemon %p attaching to exiting process %p", + __func__, filemon, p)); + + if (p->p_filemon == filemon) + return (0); + /* + * Don't allow truncating other process traces. It is + * not really intended to trace procs other than curproc + * anyhow. + */ + if (p->p_filemon != NULL && p != curproc) + return (EBUSY); + /* + * Historic behavior of filemon has been to let a child initiate + * tracing on itself and cease existing tracing. Bmake + * .META + .MAKE relies on this. It is only relevant for attaching to + * curproc. + */ + while (p->p_filemon != NULL) { + PROC_UNLOCK(p); + sx_xunlock(&filemon->lock); + while ((filemon2 = filemon_proc_get(p)) != NULL) { + /* It may have changed. */ + if (p->p_filemon == filemon2) + filemon_proc_drop(p); + filemon_drop(filemon2); + } + sx_xlock(&filemon->lock); + PROC_LOCK(p); + /* + * It may have been attached to, though unlikely. + * Try again if needed. + */ } + + KASSERT(p->p_filemon == NULL, + ("%s: proc %p didn't detach filemon %p", __func__, p, + p->p_filemon)); + p->p_filemon = filemon_acquire(filemon); + ++filemon->proccnt; + + return (0); } static int @@ -178,10 +382,16 @@ filemon_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag __unused, /* Set the monitored process ID. */ case FILEMON_SET_PID: + /* Invalidate any existing processes already set. */ + filemon_untrack_processes(filemon); + error = pget(*((pid_t *)data), PGET_CANDEBUG | PGET_NOTWEXIT, &p); if (error == 0) { - filemon->p = p; + KASSERT(p->p_filemon != filemon, + ("%s: proc %p didn't untrack filemon %p", + __func__, p, filemon)); + error = filemon_attach_proc(filemon, p); PROC_UNLOCK(p); } break; @@ -199,49 +409,48 @@ static int filemon_open(struct cdev *dev, int oflags __unused, int devtype __unused, struct thread *td __unused) { + int error; struct filemon *filemon; - /* Get exclusive write access. */ - filemon_lock_write(); - - if ((filemon = TAILQ_FIRST(&filemons_free)) != NULL) - TAILQ_REMOVE(&filemons_free, filemon, link); - - /* Give up write access. */ - filemon_unlock_write(); - - if (filemon == NULL) { - filemon = malloc(sizeof(struct filemon), M_FILEMON, - M_WAITOK | M_ZERO); - sx_init(&filemon->lock, "filemon"); - } - - devfs_set_cdevpriv(filemon, filemon_dtr); - - /* Get exclusive write access. */ - filemon_lock_write(); + filemon = malloc(sizeof(*filemon), M_FILEMON, + M_WAITOK | M_ZERO); + sx_init(&filemon->lock, "filemon"); + refcount_init(&filemon->refcnt, 1); - /* Add to the in-use list. */ - TAILQ_INSERT_TAIL(&filemons_inuse, filemon, link); + error = devfs_set_cdevpriv(filemon, filemon_dtr); + if (error != 0) + filemon_release(filemon); - /* Give up write access. */ - filemon_unlock_write(); - - return (0); + return (error); } +/* Called on close of last devfs file handle, before filemon_dtr(). */ static int filemon_close(struct cdev *dev __unused, int flag __unused, int fmt __unused, struct thread *td __unused) { + struct filemon *filemon; + int error; - return (0); + if ((error = devfs_get_cdevpriv((void **) &filemon)) != 0) + return (error); + + sx_xlock(&filemon->lock); + filemon_close_log(filemon); + error = filemon->error; + sx_xunlock(&filemon->lock); + /* + * Processes are still being traced but won't log anything + * now. After this call returns filemon_dtr() is called which + * will detach processes. + */ + + return (error); } static void filemon_load(void *dummy __unused) { - sx_init(&access_lock, "filemons_inuse"); /* Install the syscall wrappers. */ filemon_wrapper_install(); @@ -253,38 +462,11 @@ filemon_load(void *dummy __unused) static int filemon_unload(void) { - struct filemon *filemon; - int error = 0; - - /* Get exclusive write access. */ - filemon_lock_write(); - - if (TAILQ_FIRST(&filemons_inuse) != NULL) - error = EBUSY; - else { - destroy_dev(filemon_dev); - - /* Deinstall the syscall wrappers. */ - filemon_wrapper_deinstall(); - } - /* Give up write access. */ - filemon_unlock_write(); + destroy_dev(filemon_dev); + filemon_wrapper_deinstall(); - if (error == 0) { - /* free() filemon structs free list. */ - filemon_lock_write(); - while ((filemon = TAILQ_FIRST(&filemons_free)) != NULL) { - TAILQ_REMOVE(&filemons_free, filemon, link); - sx_destroy(&filemon->lock); - free(filemon, M_FILEMON); - } - filemon_unlock_write(); - - sx_destroy(&access_lock); - } - - return (error); + return (0); } static int diff --git a/sys/dev/filemon/filemon_lock.c b/sys/dev/filemon/filemon_lock.c deleted file mode 100644 index 5cac47c..0000000 --- a/sys/dev/filemon/filemon_lock.c +++ /dev/null @@ -1,57 +0,0 @@ -/*- - * Copyright (c) 2009-2011, Juniper Networks, Inc. - * Copyright (c) 2015, EMC Corp. - * 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. - * - * THIS SOFTWARE IS PROVIDED BY JUNIPER NETWORKS 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 JUNIPER NETWORKS 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. - */ - -#include <sys/cdefs.h> -__FBSDID("$FreeBSD$"); - -static __inline void -filemon_lock_read(void) -{ - - sx_slock(&access_lock); -} - -static __inline void -filemon_unlock_read(void) -{ - - sx_sunlock(&access_lock); -} - -static __inline void -filemon_lock_write(void) -{ - - sx_xlock(&access_lock); -} - -static __inline void -filemon_unlock_write(void) -{ - - sx_xunlock(&access_lock); -} diff --git a/sys/dev/filemon/filemon_wrapper.c b/sys/dev/filemon/filemon_wrapper.c index ccadbce..dc0fec4 100644 --- a/sys/dev/filemon/filemon_wrapper.c +++ b/sys/dev/filemon/filemon_wrapper.c @@ -1,7 +1,7 @@ /*- * Copyright (c) 2011, David E. O'Brien. * Copyright (c) 2009-2011, Juniper Networks, Inc. - * Copyright (c) 2015, EMC Corp. + * Copyright (c) 2015-2016, EMC Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -29,8 +29,9 @@ #include <sys/cdefs.h> __FBSDID("$FreeBSD$"); -#include <sys/imgact.h> #include <sys/eventhandler.h> +#include <sys/filedesc.h> +#include <sys/imgact.h> #include <sys/sx.h> #include <sys/vnode.h> @@ -45,6 +46,7 @@ filemon_output(struct filemon *filemon, char *msg, size_t len) { struct uio auio; struct iovec aiov; + int error; if (filemon->fp == NULL) return; @@ -62,56 +64,33 @@ filemon_output(struct filemon *filemon, char *msg, size_t len) if (filemon->fp->f_type == DTYPE_VNODE) bwillwrite(); - fo_write(filemon->fp, &auio, curthread->td_ucred, 0, curthread); -} - -static struct filemon * -filemon_pid_check(struct proc *p) -{ - struct filemon *filemon; - - filemon_lock_read(); - if (TAILQ_EMPTY(&filemons_inuse)) { - filemon_unlock_read(); - return (NULL); - } - sx_slock(&proctree_lock); - while (p->p_pid != 0) { - TAILQ_FOREACH(filemon, &filemons_inuse, link) { - if (p == filemon->p) { - sx_sunlock(&proctree_lock); - sx_xlock(&filemon->lock); - filemon_unlock_read(); - return (filemon); - } - } - p = proc_realparent(p); - } - sx_sunlock(&proctree_lock); - filemon_unlock_read(); - return (NULL); + error = fo_write(filemon->fp, &auio, curthread->td_ucred, 0, curthread); + if (error != 0) + filemon->error = error; } static int filemon_wrapper_chdir(struct thread *td, struct chdir_args *uap) { - int ret; - size_t done; + int error, ret; size_t len; struct filemon *filemon; if ((ret = sys_chdir(td, uap)) == 0) { - if ((filemon = filemon_pid_check(curproc)) != NULL) { - copyinstr(uap->path, filemon->fname1, - sizeof(filemon->fname1), &done); + if ((filemon = filemon_proc_get(curproc)) != NULL) { + if ((error = copyinstr(uap->path, filemon->fname1, + sizeof(filemon->fname1), NULL)) != 0) { + filemon->error = error; + goto copyfail; + } len = snprintf(filemon->msgbufr, sizeof(filemon->msgbufr), "C %d %s\n", curproc->p_pid, filemon->fname1); filemon_output(filemon, filemon->msgbufr, len); - - sx_xunlock(&filemon->lock); +copyfail: + filemon_drop(filemon); } } @@ -126,12 +105,11 @@ filemon_event_process_exec(void *arg __unused, struct proc *p, char *fullpath, *freepath; size_t len; - if ((filemon = filemon_pid_check(p)) != NULL) { + if ((filemon = filemon_proc_get(p)) != NULL) { fullpath = "<unknown>"; freepath = NULL; - vn_fullpath(FIRST_THREAD_IN_PROC(p), imgp->vp, &fullpath, - &freepath); + vn_fullpath(curthread, imgp->vp, &fullpath, &freepath); len = snprintf(filemon->msgbufr, sizeof(filemon->msgbufr), "E %d %s\n", @@ -139,321 +117,244 @@ filemon_event_process_exec(void *arg __unused, struct proc *p, filemon_output(filemon, filemon->msgbufr, len); - sx_xunlock(&filemon->lock); + filemon_drop(filemon); free(freepath, M_TEMP); } } -static int -filemon_wrapper_open(struct thread *td, struct open_args *uap) +static void +_filemon_wrapper_openat(struct thread *td, char *upath, int flags, int fd) { - int ret; - size_t done; + int error; size_t len; + struct file *fp; struct filemon *filemon; + char *atpath, *freepath; + cap_rights_t rights; - if ((ret = sys_open(td, uap)) == 0) { - if ((filemon = filemon_pid_check(curproc)) != NULL) { - copyinstr(uap->path, filemon->fname1, - sizeof(filemon->fname1), &done); - - if (uap->flags & O_RDWR) { - /* - * We'll get the W record below, but need - * to also output an R to distingish from - * O_WRONLY. - */ - len = snprintf(filemon->msgbufr, - sizeof(filemon->msgbufr), "R %d %s\n", - curproc->p_pid, filemon->fname1); - filemon_output(filemon, filemon->msgbufr, len); - } + if ((filemon = filemon_proc_get(curproc)) != NULL) { + atpath = ""; + freepath = NULL; + fp = NULL; + if ((error = copyinstr(upath, filemon->fname1, + sizeof(filemon->fname1), NULL)) != 0) { + filemon->error = error; + goto copyfail; + } + if (filemon->fname1[0] != '/' && fd != AT_FDCWD) { + /* + * rats - we cannot do too much about this. + * the trace should show a dir we read + * recently.. output an A record as a clue + * until we can do better. + * XXX: This may be able to come out with + * the namecache lookup now. + */ len = snprintf(filemon->msgbufr, - sizeof(filemon->msgbufr), "%c %d %s\n", - (uap->flags & O_ACCMODE) ? 'W':'R', + sizeof(filemon->msgbufr), "A %d %s\n", curproc->p_pid, filemon->fname1); filemon_output(filemon, filemon->msgbufr, len); - - sx_xunlock(&filemon->lock); + /* + * Try to resolve the path from the vnode using the + * namecache. It may be inaccurate, but better + * than nothing. + */ + if (getvnode(td->td_proc->p_fd, fd, + cap_rights_init(&rights, CAP_LOOKUP), &fp) == 0) { + vn_fullpath(td, fp->f_vnode, &atpath, + &freepath); + } + } + if (flags & O_RDWR) { + /* + * We'll get the W record below, but need + * to also output an R to distinguish from + * O_WRONLY. + */ + len = snprintf(filemon->msgbufr, + sizeof(filemon->msgbufr), "R %d %s%s%s\n", + curproc->p_pid, atpath, + atpath[0] != '\0' ? "/" : "", filemon->fname1); + filemon_output(filemon, filemon->msgbufr, len); } - } - return (ret); + len = snprintf(filemon->msgbufr, + sizeof(filemon->msgbufr), "%c %d %s%s%s\n", + (flags & O_ACCMODE) ? 'W':'R', + curproc->p_pid, atpath, + atpath[0] != '\0' ? "/" : "", filemon->fname1); + filemon_output(filemon, filemon->msgbufr, len); +copyfail: + filemon_drop(filemon); + if (fp != NULL) + fdrop(fp, td); + free(freepath, M_TEMP); + } } static int -filemon_wrapper_openat(struct thread *td, struct openat_args *uap) +filemon_wrapper_open(struct thread *td, struct open_args *uap) { int ret; - size_t done; - size_t len; - struct filemon *filemon; - if ((ret = sys_openat(td, uap)) == 0) { - if ((filemon = filemon_pid_check(curproc)) != NULL) { - copyinstr(uap->path, filemon->fname1, - sizeof(filemon->fname1), &done); - - filemon->fname2[0] = '\0'; - if (filemon->fname1[0] != '/' && uap->fd != AT_FDCWD) { - /* - * rats - we cannot do too much about this. - * the trace should show a dir we read - * recently.. output an A record as a clue - * until we can do better. - */ - len = snprintf(filemon->msgbufr, - sizeof(filemon->msgbufr), "A %d %s\n", - curproc->p_pid, filemon->fname1); - filemon_output(filemon, filemon->msgbufr, len); - } - if (uap->flag & O_RDWR) { - /* - * We'll get the W record below, but need - * to also output an R to distingish from - * O_WRONLY. - */ - len = snprintf(filemon->msgbufr, - sizeof(filemon->msgbufr), "R %d %s%s\n", - curproc->p_pid, filemon->fname2, filemon->fname1); - filemon_output(filemon, filemon->msgbufr, len); - } - - - len = snprintf(filemon->msgbufr, - sizeof(filemon->msgbufr), "%c %d %s%s\n", - (uap->flag & O_ACCMODE) ? 'W':'R', - curproc->p_pid, filemon->fname2, filemon->fname1); - filemon_output(filemon, filemon->msgbufr, len); - - sx_xunlock(&filemon->lock); - } - } + if ((ret = sys_open(td, uap)) == 0) + _filemon_wrapper_openat(td, uap->path, uap->flags, AT_FDCWD); return (ret); } static int -filemon_wrapper_rename(struct thread *td, struct rename_args *uap) +filemon_wrapper_openat(struct thread *td, struct openat_args *uap) { int ret; - size_t done; - size_t len; - struct filemon *filemon; - if ((ret = sys_rename(td, uap)) == 0) { - if ((filemon = filemon_pid_check(curproc)) != NULL) { - copyinstr(uap->from, filemon->fname1, - sizeof(filemon->fname1), &done); - copyinstr(uap->to, filemon->fname2, - sizeof(filemon->fname2), &done); - - len = snprintf(filemon->msgbufr, - sizeof(filemon->msgbufr), "M %d '%s' '%s'\n", - curproc->p_pid, filemon->fname1, filemon->fname2); - - filemon_output(filemon, filemon->msgbufr, len); - - sx_xunlock(&filemon->lock); - } - } + if ((ret = sys_openat(td, uap)) == 0) + _filemon_wrapper_openat(td, uap->path, uap->flag, uap->fd); return (ret); } static int -filemon_wrapper_link(struct thread *td, struct link_args *uap) +filemon_wrapper_rename(struct thread *td, struct rename_args *uap) { - int ret; - size_t done; + int error, ret; size_t len; struct filemon *filemon; - if ((ret = sys_link(td, uap)) == 0) { - if ((filemon = filemon_pid_check(curproc)) != NULL) { - copyinstr(uap->path, filemon->fname1, - sizeof(filemon->fname1), &done); - copyinstr(uap->link, filemon->fname2, - sizeof(filemon->fname2), &done); + if ((ret = sys_rename(td, uap)) == 0) { + if ((filemon = filemon_proc_get(curproc)) != NULL) { + if (((error = copyinstr(uap->from, filemon->fname1, + sizeof(filemon->fname1), NULL)) != 0) || + ((error = copyinstr(uap->to, filemon->fname2, + sizeof(filemon->fname2), NULL)) != 0)) { + filemon->error = error; + goto copyfail; + } len = snprintf(filemon->msgbufr, - sizeof(filemon->msgbufr), "L %d '%s' '%s'\n", + sizeof(filemon->msgbufr), "M %d '%s' '%s'\n", curproc->p_pid, filemon->fname1, filemon->fname2); filemon_output(filemon, filemon->msgbufr, len); - - sx_xunlock(&filemon->lock); +copyfail: + filemon_drop(filemon); } } return (ret); } -static int -filemon_wrapper_symlink(struct thread *td, struct symlink_args *uap) +static void +_filemon_wrapper_link(struct thread *td, char *upath1, char *upath2) { - int ret; - size_t done; - size_t len; struct filemon *filemon; + size_t len; + int error; + + if ((filemon = filemon_proc_get(curproc)) != NULL) { + if (((error = copyinstr(upath1, filemon->fname1, + sizeof(filemon->fname1), NULL)) != 0) || + ((error = copyinstr(upath2, filemon->fname2, + sizeof(filemon->fname2), NULL)) != 0)) { + filemon->error = error; + goto copyfail; + } - if ((ret = sys_symlink(td, uap)) == 0) { - if ((filemon = filemon_pid_check(curproc)) != NULL) { - copyinstr(uap->path, filemon->fname1, - sizeof(filemon->fname1), &done); - copyinstr(uap->link, filemon->fname2, - sizeof(filemon->fname2), &done); - - len = snprintf(filemon->msgbufr, - sizeof(filemon->msgbufr), "L %d '%s' '%s'\n", - curproc->p_pid, filemon->fname1, filemon->fname2); - - filemon_output(filemon, filemon->msgbufr, len); + len = snprintf(filemon->msgbufr, + sizeof(filemon->msgbufr), "L %d '%s' '%s'\n", + curproc->p_pid, filemon->fname1, filemon->fname2); - sx_xunlock(&filemon->lock); - } + filemon_output(filemon, filemon->msgbufr, len); +copyfail: + filemon_drop(filemon); } - - return (ret); } static int -filemon_wrapper_linkat(struct thread *td, struct linkat_args *uap) +filemon_wrapper_link(struct thread *td, struct link_args *uap) { int ret; - size_t done; - size_t len; - struct filemon *filemon; - - if ((ret = sys_linkat(td, uap)) == 0) { - if ((filemon = filemon_pid_check(curproc)) != NULL) { - copyinstr(uap->path1, filemon->fname1, - sizeof(filemon->fname1), &done); - copyinstr(uap->path2, filemon->fname2, - sizeof(filemon->fname2), &done); - - len = snprintf(filemon->msgbufr, - sizeof(filemon->msgbufr), "L %d '%s' '%s'\n", - curproc->p_pid, filemon->fname1, filemon->fname2); - filemon_output(filemon, filemon->msgbufr, len); - - sx_xunlock(&filemon->lock); - } - } + if ((ret = sys_link(td, uap)) == 0) + _filemon_wrapper_link(td, uap->path, uap->link); return (ret); } static int -filemon_wrapper_stat(struct thread *td, struct stat_args *uap) +filemon_wrapper_symlink(struct thread *td, struct symlink_args *uap) { int ret; - size_t done; - size_t len; - struct filemon *filemon; - if ((ret = sys_stat(td, uap)) == 0) { - if ((filemon = filemon_pid_check(curproc)) != NULL) { - copyinstr(uap->path, filemon->fname1, - sizeof(filemon->fname1), &done); - - len = snprintf(filemon->msgbufr, - sizeof(filemon->msgbufr), "S %d %s\n", - curproc->p_pid, filemon->fname1); - - filemon_output(filemon, filemon->msgbufr, len); - - sx_xunlock(&filemon->lock); - } - } + if ((ret = sys_symlink(td, uap)) == 0) + _filemon_wrapper_link(td, uap->path, uap->link); return (ret); } -#if defined(COMPAT_IA32) || defined(COMPAT_FREEBSD32) || defined(COMPAT_ARCH32) static int -filemon_wrapper_freebsd32_stat(struct thread *td, - struct freebsd32_stat_args *uap) +filemon_wrapper_linkat(struct thread *td, struct linkat_args *uap) { int ret; - size_t done; - size_t len; - struct filemon *filemon; - - if ((ret = freebsd32_stat(td, uap)) == 0) { - if ((filemon = filemon_pid_check(curproc)) != NULL) { - copyinstr(uap->path, filemon->fname1, - sizeof(filemon->fname1), &done); - - len = snprintf(filemon->msgbufr, - sizeof(filemon->msgbufr), "S %d %s\n", - curproc->p_pid, filemon->fname1); - - filemon_output(filemon, filemon->msgbufr, len); - sx_xunlock(&filemon->lock); - } - } + if ((ret = sys_linkat(td, uap)) == 0) + _filemon_wrapper_link(td, uap->path1, uap->path2); return (ret); } -#endif static void filemon_event_process_exit(void *arg __unused, struct proc *p) { size_t len; struct filemon *filemon; - struct timeval now; - /* Get timestamp before locking. */ - getmicrotime(&now); - - if ((filemon = filemon_pid_check(p)) != NULL) { + if ((filemon = filemon_proc_get(p)) != NULL) { len = snprintf(filemon->msgbufr, sizeof(filemon->msgbufr), "X %d %d\n", p->p_pid, W_EXITCODE(p->p_xstat, 0)); filemon_output(filemon, filemon->msgbufr, len); - /* Check if the monitored process is about to exit. */ - if (filemon->p == p) { - len = snprintf(filemon->msgbufr, - sizeof(filemon->msgbufr), - "# Stop %ju.%06ju\n# Bye bye\n", - (uintmax_t)now.tv_sec, (uintmax_t)now.tv_usec); - - filemon_output(filemon, filemon->msgbufr, len); - filemon->p = NULL; - } - - sx_xunlock(&filemon->lock); + /* + * filemon_untrack_processes() may have dropped this p_filemon + * already while in filemon_proc_get() before acquiring the + * filemon lock. + */ + KASSERT(p->p_filemon == NULL || p->p_filemon == filemon, + ("%s: p %p was attached while exiting, expected " + "filemon %p or NULL", __func__, p, filemon)); + if (p->p_filemon == filemon) + filemon_proc_drop(p); + + filemon_drop(filemon); } } static int filemon_wrapper_unlink(struct thread *td, struct unlink_args *uap) { - int ret; - size_t done; + int error, ret; size_t len; struct filemon *filemon; if ((ret = sys_unlink(td, uap)) == 0) { - if ((filemon = filemon_pid_check(curproc)) != NULL) { - copyinstr(uap->path, filemon->fname1, - sizeof(filemon->fname1), &done); + if ((filemon = filemon_proc_get(curproc)) != NULL) { + if ((error = copyinstr(uap->path, filemon->fname1, + sizeof(filemon->fname1), NULL)) != 0) { + filemon->error = error; + goto copyfail; + } len = snprintf(filemon->msgbufr, sizeof(filemon->msgbufr), "D %d %s\n", curproc->p_pid, filemon->fname1); filemon_output(filemon, filemon->msgbufr, len); - - sx_xunlock(&filemon->lock); +copyfail: + filemon_drop(filemon); } } @@ -467,14 +368,34 @@ filemon_event_process_fork(void *arg __unused, struct proc *p1, size_t len; struct filemon *filemon; - if ((filemon = filemon_pid_check(p1)) != NULL) { + if ((filemon = filemon_proc_get(p1)) != NULL) { len = snprintf(filemon->msgbufr, sizeof(filemon->msgbufr), "F %d %d\n", p1->p_pid, p2->p_pid); filemon_output(filemon, filemon->msgbufr, len); - sx_xunlock(&filemon->lock); + /* + * filemon_untrack_processes() or + * filemon_ioctl(FILEMON_SET_PID) may have changed the parent's + * p_filemon while in filemon_proc_get() before acquiring the + * filemon lock. Only inherit if the parent is still traced by + * this filemon. + */ + if (p1->p_filemon == filemon) { + PROC_LOCK(p2); + /* + * It may have been attached to already by a new + * filemon. + */ + if (p2->p_filemon == NULL) { + p2->p_filemon = filemon_acquire(filemon); + ++filemon->proccnt; + } + PROC_UNLOCK(p2); + } + + filemon_drop(filemon); } } @@ -491,7 +412,6 @@ filemon_wrapper_install(void) sv_table[SYS_open].sy_call = (sy_call_t *) filemon_wrapper_open; sv_table[SYS_openat].sy_call = (sy_call_t *) filemon_wrapper_openat; sv_table[SYS_rename].sy_call = (sy_call_t *) filemon_wrapper_rename; - sv_table[SYS_stat].sy_call = (sy_call_t *) filemon_wrapper_stat; sv_table[SYS_unlink].sy_call = (sy_call_t *) filemon_wrapper_unlink; sv_table[SYS_link].sy_call = (sy_call_t *) filemon_wrapper_link; sv_table[SYS_symlink].sy_call = (sy_call_t *) filemon_wrapper_symlink; @@ -504,7 +424,6 @@ filemon_wrapper_install(void) sv_table[FREEBSD32_SYS_open].sy_call = (sy_call_t *) filemon_wrapper_open; sv_table[FREEBSD32_SYS_openat].sy_call = (sy_call_t *) filemon_wrapper_openat; sv_table[FREEBSD32_SYS_rename].sy_call = (sy_call_t *) filemon_wrapper_rename; - sv_table[FREEBSD32_SYS_freebsd32_stat].sy_call = (sy_call_t *) filemon_wrapper_freebsd32_stat; sv_table[FREEBSD32_SYS_unlink].sy_call = (sy_call_t *) filemon_wrapper_unlink; sv_table[FREEBSD32_SYS_link].sy_call = (sy_call_t *) filemon_wrapper_link; sv_table[FREEBSD32_SYS_symlink].sy_call = (sy_call_t *) filemon_wrapper_symlink; @@ -532,7 +451,6 @@ filemon_wrapper_deinstall(void) sv_table[SYS_open].sy_call = (sy_call_t *)sys_open; sv_table[SYS_openat].sy_call = (sy_call_t *)sys_openat; sv_table[SYS_rename].sy_call = (sy_call_t *)sys_rename; - sv_table[SYS_stat].sy_call = (sy_call_t *)sys_stat; sv_table[SYS_unlink].sy_call = (sy_call_t *)sys_unlink; sv_table[SYS_link].sy_call = (sy_call_t *)sys_link; sv_table[SYS_symlink].sy_call = (sy_call_t *)sys_symlink; @@ -545,7 +463,6 @@ filemon_wrapper_deinstall(void) sv_table[FREEBSD32_SYS_open].sy_call = (sy_call_t *)sys_open; sv_table[FREEBSD32_SYS_openat].sy_call = (sy_call_t *)sys_openat; sv_table[FREEBSD32_SYS_rename].sy_call = (sy_call_t *)sys_rename; - sv_table[FREEBSD32_SYS_freebsd32_stat].sy_call = (sy_call_t *)freebsd32_stat; sv_table[FREEBSD32_SYS_unlink].sy_call = (sy_call_t *)sys_unlink; sv_table[FREEBSD32_SYS_link].sy_call = (sy_call_t *)sys_link; sv_table[FREEBSD32_SYS_symlink].sy_call = (sy_call_t *)sys_symlink; diff --git a/sys/kern/kern_fork.c b/sys/kern/kern_fork.c index f24ba20..7b02c4d 100644 --- a/sys/kern/kern_fork.c +++ b/sys/kern/kern_fork.c @@ -411,6 +411,7 @@ do_fork(struct thread *td, int flags, struct proc *p2, struct thread *td2, bzero(&p2->p_startzero, __rangeof(struct proc, p_startzero, p_endzero)); p2->p_treeflag = 0; + p2->p_filemon = NULL; p2->p_ucred = crhold(td->td_ucred); diff --git a/sys/sys/param.h b/sys/sys/param.h index 023747d..9889f78 100644 --- a/sys/sys/param.h +++ b/sys/sys/param.h @@ -58,7 +58,7 @@ * in the range 5 to 9. */ #undef __FreeBSD_version -#define __FreeBSD_version 1003503 /* Master, propagated to newvers */ +#define __FreeBSD_version 1003504 /* Master, propagated to newvers */ /* * __FreeBSD_kernel__ indicates that this system uses the kernel of FreeBSD, diff --git a/sys/sys/proc.h b/sys/sys/proc.h index 836e123..ef60b89 100644 --- a/sys/sys/proc.h +++ b/sys/sys/proc.h @@ -158,6 +158,7 @@ struct pargs { * for write access. */ struct cpuset; +struct filemon; struct kaioinfo; struct kaudit_record; struct kdtrace_proc; @@ -606,6 +607,7 @@ struct proc { reaper which spawned our subtree. */ struct pgrp *p_pgrp; /* (c + e) Pointer to process group. */ + struct filemon *p_filemon; /* (c) filemon-specific data. */ }; #define p_session p_pgrp->pg_session |