1/*	$NetBSD: filemon_ktrace.c,v 1.15 2021/07/31 09:30:17 rillig Exp $	*/
2
3/*
4 * Copyright (c) 2019 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Taylor R. Campbell.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32#define _KERNTYPES		/* register_t */
33
34#include "filemon.h"
35
36#include <sys/param.h>
37#include <sys/types.h>
38#include <sys/rbtree.h>
39#include <sys/syscall.h>
40#include <sys/time.h>
41#include <sys/uio.h>
42#include <sys/wait.h>
43
44#include <sys/ktrace.h>
45
46#include <assert.h>
47#include <err.h>
48#include <errno.h>
49#include <fcntl.h>
50#include <stdbool.h>
51#include <stddef.h>
52#include <stdio.h>
53#include <stdlib.h>
54#include <string.h>
55#include <unistd.h>
56
57#ifndef AT_CWD
58#define AT_CWD -1
59#endif
60
61struct filemon;
62struct filemon_key;
63struct filemon_state;
64
65typedef struct filemon_state *filemon_syscall_t(struct filemon *,
66    const struct filemon_key *, const struct ktr_syscall *);
67
68static filemon_syscall_t filemon_sys_chdir;
69static filemon_syscall_t filemon_sys_execve;
70static filemon_syscall_t filemon_sys_exit;
71static filemon_syscall_t filemon_sys_fork;
72static filemon_syscall_t filemon_sys_link;
73static filemon_syscall_t filemon_sys_open;
74static filemon_syscall_t filemon_sys_openat;
75static filemon_syscall_t filemon_sys_symlink;
76static filemon_syscall_t filemon_sys_unlink;
77static filemon_syscall_t filemon_sys_rename;
78
79static filemon_syscall_t *const filemon_syscalls[] = {
80	[SYS_chdir] = &filemon_sys_chdir,
81	[SYS_execve] = &filemon_sys_execve,
82	[SYS_exit] = &filemon_sys_exit,
83	[SYS_fork] = &filemon_sys_fork,
84	[SYS_link] = &filemon_sys_link,
85	[SYS_open] = &filemon_sys_open,
86	[SYS_openat] = &filemon_sys_openat,
87	[SYS_symlink] = &filemon_sys_symlink,
88	[SYS_unlink] = &filemon_sys_unlink,
89	[SYS_rename] = &filemon_sys_rename,
90};
91
92struct filemon {
93	int			ktrfd;	/* kernel writes ktrace events here */
94	FILE			*in;	/* we read ktrace events from here */
95	FILE			*out;	/* we write filemon events to here */
96	rb_tree_t		active;
97	pid_t			child;
98
99	/* I/O state machine.  */
100	enum {
101		FILEMON_START = 0,
102		FILEMON_HEADER,
103		FILEMON_PAYLOAD,
104		FILEMON_ERROR,
105	}			state;
106	unsigned char		*p;
107	size_t			resid;
108
109	/* I/O buffer.  */
110	struct ktr_header	hdr;
111	union {
112		struct ktr_syscall	syscall;
113		struct ktr_sysret	sysret;
114		char			namei[PATH_MAX];
115		unsigned char		buf[4096];
116	}			payload;
117};
118
119struct filemon_state {
120	struct filemon_key {
121		pid_t		pid;
122		lwpid_t		lid;
123	}		key;
124	struct rb_node	node;
125	int		syscode;
126	void		(*show)(struct filemon *, const struct filemon_state *,
127			    const struct ktr_sysret *);
128	unsigned	i;
129	unsigned	npath;
130	char		*path[/*npath*/];
131};
132
133/*ARGSUSED*/
134static int
135compare_filemon_states(void *cookie, const void *na, const void *nb)
136{
137	const struct filemon_state *Sa = na;
138	const struct filemon_state *Sb = nb;
139
140	if (Sa->key.pid < Sb->key.pid)
141		return -1;
142	if (Sa->key.pid > Sb->key.pid)
143		return +1;
144	if (Sa->key.lid < Sb->key.lid)
145		return -1;
146	if (Sa->key.lid > Sb->key.lid)
147		return +1;
148	return 0;
149}
150
151/*ARGSUSED*/
152static int
153compare_filemon_key(void *cookie, const void *n, const void *k)
154{
155	const struct filemon_state *S = n;
156	const struct filemon_key *key = k;
157
158	if (S->key.pid < key->pid)
159		return -1;
160	if (S->key.pid > key->pid)
161		return +1;
162	if (S->key.lid < key->lid)
163		return -1;
164	if (S->key.lid > key->lid)
165		return +1;
166	return 0;
167}
168
169static const rb_tree_ops_t filemon_rb_ops = {
170	.rbto_compare_nodes = &compare_filemon_states,
171	.rbto_compare_key = &compare_filemon_key,
172	.rbto_node_offset = offsetof(struct filemon_state, node),
173	.rbto_context = NULL,
174};
175
176/*
177 * filemon_path()
178 *
179 *	Return a pointer to a constant string denoting the `path' of
180 *	the filemon.
181 */
182const char *
183filemon_path(void)
184{
185
186	return "ktrace";
187}
188
189/*
190 * filemon_open()
191 *
192 *	Allocate a filemon descriptor.  Returns NULL and sets errno on
193 *	failure.
194 */
195struct filemon *
196filemon_open(void)
197{
198	struct filemon *F;
199	int ktrpipe[2];
200	int error;
201
202	/* Allocate and zero a struct filemon object.  */
203	F = calloc(1, sizeof *F);
204	if (F == NULL)
205		return NULL;
206
207	/* Create a pipe for ktrace events.  */
208	if (pipe2(ktrpipe, O_CLOEXEC|O_NONBLOCK) == -1) {
209		error = errno;
210		goto fail0;
211	}
212
213	/* Create a file stream for reading the ktrace events.  */
214	if ((F->in = fdopen(ktrpipe[0], "r")) == NULL) {
215		error = errno;
216		goto fail1;
217	}
218	ktrpipe[0] = -1;	/* claimed by fdopen */
219
220	/*
221	 * Set the fd for writing ktrace events and initialize the
222	 * rbtree.  The rest can be safely initialized to zero.
223	 */
224	F->ktrfd = ktrpipe[1];
225	rb_tree_init(&F->active, &filemon_rb_ops);
226
227	/* Success!  */
228	return F;
229
230fail1:	(void)close(ktrpipe[0]);
231	(void)close(ktrpipe[1]);
232fail0:	free(F);
233	errno = error;
234	return NULL;
235}
236
237/*
238 * filemon_closefd(F)
239 *
240 *	Internal subroutine to try to flush and close the output file.
241 *	If F is not open for output, do nothing.  Never leaves F open
242 *	for output even on failure.  Returns 0 on success; sets errno
243 *	and return -1 on failure.
244 */
245static int
246filemon_closefd(struct filemon *F)
247{
248	int error = 0;
249
250	/* If we're not open, nothing to do.  */
251	if (F->out == NULL)
252		return 0;
253
254	/*
255	 * Flush it, close it, and null it unconditionally, but be
256	 * careful to return the earliest error in errno.
257	 */
258	if (fflush(F->out) == EOF && error == 0)
259		error = errno;
260	if (fclose(F->out) == EOF && error == 0)
261		error = errno;
262	F->out = NULL;
263
264	/* Set errno and return -1 if anything went wrong.  */
265	if (error != 0) {
266		errno = error;
267		return -1;
268	}
269
270	/* Success!  */
271	return 0;
272}
273
274/*
275 * filemon_setfd(F, fd)
276 *
277 *	Cause filemon activity on F to be sent to fd.  Claims ownership
278 *	of fd; caller should not use fd afterward, and any duplicates
279 *	of fd may see their file positions changed.
280 */
281int
282filemon_setfd(struct filemon *F, int fd)
283{
284
285	/*
286	 * Close an existing output file if done.  Fail now if there's
287	 * an error closing.
288	 */
289	if ((filemon_closefd(F)) == -1)
290		return -1;
291	assert(F->out == NULL);
292
293	/* Open a file stream and claim ownership of the fd.  */
294	if ((F->out = fdopen(fd, "a")) == NULL)
295		return -1;
296
297	/*
298	 * Print the opening output.  Any failure will be deferred
299	 * until closing.  For hysterical raisins, we show the parent
300	 * pid, not the child pid.
301	 */
302	fprintf(F->out, "# filemon version 4\n");
303	fprintf(F->out, "# Target pid %jd\n", (intmax_t)getpid());
304	fprintf(F->out, "V 4\n");
305
306	/* Success!  */
307	return 0;
308}
309
310/*
311 * filemon_setpid_parent(F, pid)
312 *
313 *	Set the traced pid, from the parent.  Never fails.
314 */
315void
316filemon_setpid_parent(struct filemon *F, pid_t pid)
317{
318
319	F->child = pid;
320}
321
322/*
323 * filemon_setpid_child(F, pid)
324 *
325 *	Set the traced pid, from the child.  Returns 0 on success; sets
326 *	errno and returns -1 on failure.
327 */
328int
329filemon_setpid_child(const struct filemon *F, pid_t pid)
330{
331	int ops, trpoints;
332
333	ops = KTROP_SET|KTRFLAG_DESCEND;
334	trpoints = KTRFACv2;
335	trpoints |= KTRFAC_SYSCALL|KTRFAC_NAMEI|KTRFAC_SYSRET;
336	trpoints |= KTRFAC_INHERIT;
337	if (fktrace(F->ktrfd, ops, trpoints, pid) == -1)
338		return -1;
339
340	return 0;
341}
342
343/*
344 * filemon_close(F)
345 *
346 *	Close F for output if necessary, and free a filemon descriptor.
347 *	Returns 0 on success; sets errno and returns -1 on failure, but
348 *	frees the filemon descriptor either way;
349 */
350int
351filemon_close(struct filemon *F)
352{
353	struct filemon_state *S;
354	int error = 0;
355
356	/* Close for output.  */
357	if (filemon_closefd(F) == -1 && error == 0)
358		error = errno;
359
360	/* Close the ktrace pipe.  */
361	if (fclose(F->in) == EOF && error == 0)
362		error = errno;
363	if (close(F->ktrfd) == -1 && error == 0)
364		error = errno;
365
366	/* Free any active records.  */
367	while ((S = RB_TREE_MIN(&F->active)) != NULL) {
368		rb_tree_remove_node(&F->active, S);
369		free(S);
370	}
371
372	/* Free the filemon descriptor.  */
373	free(F);
374
375	/* Set errno and return -1 if anything went wrong.  */
376	if (error != 0) {
377		errno = error;
378		return -1;
379	}
380
381	/* Success!  */
382	return 0;
383}
384
385/*
386 * filemon_readfd(F)
387 *
388 *	Returns a file descriptor which will select/poll ready for read
389 *	when there are filemon events to be processed by
390 *	filemon_process, or -1 if anything has gone wrong.
391 */
392int
393filemon_readfd(const struct filemon *F)
394{
395
396	if (F->state == FILEMON_ERROR)
397		return -1;
398	return fileno(F->in);
399}
400
401/*
402 * filemon_dispatch(F)
403 *
404 *	Internal subroutine to dispatch a filemon ktrace event.
405 *	Silently ignore events that we don't recognize.
406 */
407static void
408filemon_dispatch(struct filemon *F)
409{
410	const struct filemon_key key = {
411		.pid = F->hdr.ktr_pid,
412		.lid = F->hdr.ktr_lid,
413	};
414	struct filemon_state *S;
415
416	switch (F->hdr.ktr_type) {
417	case KTR_SYSCALL: {
418		struct ktr_syscall *call = &F->payload.syscall;
419		struct filemon_state *S1;
420
421		/* Validate the syscall code.  */
422		if (call->ktr_code < 0 ||
423		    (size_t)call->ktr_code >= __arraycount(filemon_syscalls) ||
424		    filemon_syscalls[call->ktr_code] == NULL)
425			break;
426
427		/*
428		 * Invoke the syscall-specific logic to create a new
429		 * active state.
430		 */
431		S = (*filemon_syscalls[call->ktr_code])(F, &key, call);
432		if (S == NULL)
433			break;
434
435		/*
436		 * Insert the active state, or ignore it if there
437		 * already is one.
438		 *
439		 * Collisions shouldn't happen because the states are
440		 * keyed by <pid,lid>, in which syscalls should happen
441		 * sequentially in CALL/RET pairs, but let's be
442		 * defensive.
443		 */
444		S1 = rb_tree_insert_node(&F->active, S);
445		if (S1 != S) {
446			/* XXX Which one to drop?  */
447			free(S);
448			break;
449		}
450		break;
451	}
452	case KTR_NAMEI:
453		/* Find an active syscall state, or drop it.  */
454		S = rb_tree_find_node(&F->active, &key);
455		if (S == NULL)
456			break;
457		/* Find the position of the next path, or drop it.  */
458		if (S->i >= S->npath)
459			break;
460		/* Record the path.  */
461		S->path[S->i++] = strndup(F->payload.namei,
462		    sizeof F->payload.namei);
463		break;
464	case KTR_SYSRET: {
465		struct ktr_sysret *ret = &F->payload.sysret;
466		unsigned i;
467
468		/* Find and remove an active syscall state, or drop it.  */
469		S = rb_tree_find_node(&F->active, &key);
470		if (S == NULL)
471			break;
472		rb_tree_remove_node(&F->active, S);
473
474		/*
475		 * If the active syscall state matches this return,
476		 * invoke the syscall-specific logic to show a filemon
477		 * event.
478		 */
479		/* XXX What to do if syscall code doesn't match?  */
480		if (S->i == S->npath && S->syscode == ret->ktr_code)
481			S->show(F, S, ret);
482
483		/* Free the state now that it is no longer active.  */
484		for (i = 0; i < S->i; i++)
485			free(S->path[i]);
486		free(S);
487		break;
488	}
489	default:
490		/* Ignore all other ktrace events.  */
491		break;
492	}
493}
494
495/*
496 * filemon_process(F)
497 *
498 *	Process all pending events after filemon_readfd(F) has
499 *	selected/polled ready for read.
500 *
501 *	Returns -1 on failure, 0 on end of events, and anything else if
502 *	there may be more events.
503 *
504 *	XXX What about fairness to other activities in the event loop?
505 *	If we stop while there's events buffered in F->in, then select
506 *	or poll may not return ready even though there's work queued up
507 *	in the buffer of F->in, but if we don't stop then ktrace events
508 *	may overwhelm all other activity in the event loop.
509 */
510int
511filemon_process(struct filemon *F)
512{
513	size_t nread;
514
515top:	/* If the child has exited, nothing to do.  */
516	/* XXX What if one thread calls exit while another is running?  */
517	if (F->child == 0)
518		return 0;
519
520	/* If we're waiting for input, read some.  */
521	if (F->resid > 0) {
522		nread = fread(F->p, 1, F->resid, F->in);
523		if (nread == 0) {
524			if (feof(F->in) != 0)
525				return 0;
526			assert(ferror(F->in) != 0);
527			/*
528			 * If interrupted or would block, there may be
529			 * more events.  Otherwise fail.
530			 */
531			if (errno == EAGAIN || errno == EINTR)
532				return 1;
533			F->state = FILEMON_ERROR;
534			F->p = NULL;
535			F->resid = 0;
536			return -1;
537		}
538		assert(nread <= F->resid);
539		F->p += nread;
540		F->resid -= nread;
541		if (F->resid > 0)	/* may be more events */
542			return 1;
543	}
544
545	/* Process a state transition now that we've read a buffer.  */
546	switch (F->state) {
547	case FILEMON_START:	/* just started filemon; read header next */
548		F->state = FILEMON_HEADER;
549		F->p = (void *)&F->hdr;
550		F->resid = sizeof F->hdr;
551		goto top;
552	case FILEMON_HEADER:	/* read header */
553		/* Sanity-check ktrace header; then read payload.  */
554		if (F->hdr.ktr_len < 0 ||
555		    (size_t)F->hdr.ktr_len > sizeof F->payload) {
556			F->state = FILEMON_ERROR;
557			F->p = NULL;
558			F->resid = 0;
559			errno = EIO;
560			return -1;
561		}
562		F->state = FILEMON_PAYLOAD;
563		F->p = (void *)&F->payload;
564		F->resid = (size_t)F->hdr.ktr_len;
565		goto top;
566	case FILEMON_PAYLOAD:	/* read header and payload */
567		/* Dispatch ktrace event; then read next header.  */
568		filemon_dispatch(F);
569		F->state = FILEMON_HEADER;
570		F->p = (void *)&F->hdr;
571		F->resid = sizeof F->hdr;
572		goto top;
573	default:		/* paranoia */
574		F->state = FILEMON_ERROR;
575		/*FALLTHROUGH*/
576	case FILEMON_ERROR:	/* persistent error indicator */
577		F->p = NULL;
578		F->resid = 0;
579		errno = EIO;
580		return -1;
581	}
582}
583
584static struct filemon_state *
585syscall_enter(
586    const struct filemon_key *key, const struct ktr_syscall *call,
587    unsigned npath,
588    void (*show)(struct filemon *, const struct filemon_state *,
589	const struct ktr_sysret *))
590{
591	struct filemon_state *S;
592	unsigned i;
593
594	S = calloc(1, offsetof(struct filemon_state, path[npath]));
595	if (S == NULL)
596		return NULL;
597	S->key = *key;
598	S->show = show;
599	S->syscode = call->ktr_code;
600	S->i = 0;
601	S->npath = npath;
602	for (i = 0; i < npath; i++)
603		 S->path[i] = NULL; /* paranoia */
604
605	return S;
606}
607
608static void
609show_paths(struct filemon *F, const struct filemon_state *S,
610    const struct ktr_sysret *ret, const char *prefix)
611{
612	unsigned i;
613
614	/* Caller must ensure all paths have been specified.  */
615	assert(S->i == S->npath);
616
617	/*
618	 * Ignore it if it failed or yielded EJUSTRETURN (-2), or if
619	 * we're not producing output.
620	 */
621	if (ret->ktr_error != 0 && ret->ktr_error != -2)
622		return;
623	if (F->out == NULL)
624		return;
625
626	/*
627	 * Print the prefix, pid, and paths -- with the paths quoted if
628	 * there's more than one.
629	 */
630	fprintf(F->out, "%s %jd", prefix, (intmax_t)S->key.pid);
631	for (i = 0; i < S->npath; i++) {
632		const char *q = S->npath > 1 ? "'" : "";
633		fprintf(F->out, " %s%s%s", q, S->path[i], q);
634	}
635	fprintf(F->out, "\n");
636}
637
638static void
639show_retval(struct filemon *F, const struct filemon_state *S,
640    const struct ktr_sysret *ret, const char *prefix)
641{
642
643	/*
644	 * Ignore it if it failed or yielded EJUSTRETURN (-2), or if
645	 * we're not producing output.
646	 */
647	if (ret->ktr_error != 0 && ret->ktr_error != -2)
648		return;
649	if (F->out == NULL)
650		return;
651
652	fprintf(F->out, "%s %jd %jd\n", prefix, (intmax_t)S->key.pid,
653	    (intmax_t)ret->ktr_retval);
654}
655
656static void
657show_chdir(struct filemon *F, const struct filemon_state *S,
658    const struct ktr_sysret *ret)
659{
660	show_paths(F, S, ret, "C");
661}
662
663static void
664show_execve(struct filemon *F, const struct filemon_state *S,
665    const struct ktr_sysret *ret)
666{
667	show_paths(F, S, ret, "E");
668}
669
670static void
671show_fork(struct filemon *F, const struct filemon_state *S,
672    const struct ktr_sysret *ret)
673{
674	show_retval(F, S, ret, "F");
675}
676
677static void
678show_link(struct filemon *F, const struct filemon_state *S,
679    const struct ktr_sysret *ret)
680{
681	show_paths(F, S, ret, "L"); /* XXX same as symlink */
682}
683
684static void
685show_open_read(struct filemon *F, const struct filemon_state *S,
686    const struct ktr_sysret *ret)
687{
688	show_paths(F, S, ret, "R");
689}
690
691static void
692show_open_write(struct filemon *F, const struct filemon_state *S,
693    const struct ktr_sysret *ret)
694{
695	show_paths(F, S, ret, "W");
696}
697
698static void
699show_open_readwrite(struct filemon *F, const struct filemon_state *S,
700    const struct ktr_sysret *ret)
701{
702	show_paths(F, S, ret, "R");
703	show_paths(F, S, ret, "W");
704}
705
706static void
707show_openat_read(struct filemon *F, const struct filemon_state *S,
708    const struct ktr_sysret *ret)
709{
710	if (S->path[0][0] != '/')
711		show_paths(F, S, ret, "A");
712	show_paths(F, S, ret, "R");
713}
714
715static void
716show_openat_write(struct filemon *F, const struct filemon_state *S,
717    const struct ktr_sysret *ret)
718{
719	if (S->path[0][0] != '/')
720		show_paths(F, S, ret, "A");
721	show_paths(F, S, ret, "W");
722}
723
724static void
725show_openat_readwrite(struct filemon *F, const struct filemon_state *S,
726    const struct ktr_sysret *ret)
727{
728	if (S->path[0][0] != '/')
729		show_paths(F, S, ret, "A");
730	show_paths(F, S, ret, "R");
731	show_paths(F, S, ret, "W");
732}
733
734static void
735show_symlink(struct filemon *F, const struct filemon_state *S,
736    const struct ktr_sysret *ret)
737{
738	show_paths(F, S, ret, "L"); /* XXX same as link */
739}
740
741static void
742show_unlink(struct filemon *F, const struct filemon_state *S,
743    const struct ktr_sysret *ret)
744{
745	show_paths(F, S, ret, "D");
746}
747
748static void
749show_rename(struct filemon *F, const struct filemon_state *S,
750    const struct ktr_sysret *ret)
751{
752	show_paths(F, S, ret, "M");
753}
754
755/*ARGSUSED*/
756static struct filemon_state *
757filemon_sys_chdir(struct filemon *F, const struct filemon_key *key,
758    const struct ktr_syscall *call)
759{
760	return syscall_enter(key, call, 1, &show_chdir);
761}
762
763/* TODO: monitor fchdir as well */
764
765/*ARGSUSED*/
766static struct filemon_state *
767filemon_sys_execve(struct filemon *F, const struct filemon_key *key,
768    const struct ktr_syscall *call)
769{
770	return syscall_enter(key, call, 1, &show_execve);
771}
772
773static struct filemon_state *
774filemon_sys_exit(struct filemon *F, const struct filemon_key *key,
775    const struct ktr_syscall *call)
776{
777	const register_t *args = (const void *)&call[1];
778	int status = (int)args[0];
779
780	if (F->out != NULL) {
781		fprintf(F->out, "X %jd %d\n", (intmax_t)key->pid, status);
782		if (key->pid == F->child) {
783			fprintf(F->out, "# Bye bye\n");
784			F->child = 0;
785		}
786	}
787	return NULL;
788}
789
790/*ARGSUSED*/
791static struct filemon_state *
792filemon_sys_fork(struct filemon *F, const struct filemon_key *key,
793    const struct ktr_syscall *call)
794{
795	return syscall_enter(key, call, 0, &show_fork);
796}
797
798/*ARGSUSED*/
799static struct filemon_state *
800filemon_sys_link(struct filemon *F, const struct filemon_key *key,
801    const struct ktr_syscall *call)
802{
803	return syscall_enter(key, call, 2, &show_link);
804}
805
806/*ARGSUSED*/
807static struct filemon_state *
808filemon_sys_open(struct filemon *F, const struct filemon_key *key,
809    const struct ktr_syscall *call)
810{
811	const register_t *args = (const void *)&call[1];
812	int flags;
813
814	if (call->ktr_argsize < 2)
815		return NULL;
816	flags = (int)args[1];
817
818	if ((flags & O_RDWR) == O_RDWR)
819		return syscall_enter(key, call, 1, &show_open_readwrite);
820	else if ((flags & O_WRONLY) == O_WRONLY)
821		return syscall_enter(key, call, 1, &show_open_write);
822	else if ((flags & O_RDONLY) == O_RDONLY)
823		return syscall_enter(key, call, 1, &show_open_read);
824	else
825		return NULL;	/* XXX Do we care if no read or write?  */
826}
827
828/*ARGSUSED*/
829static struct filemon_state *
830filemon_sys_openat(struct filemon *F, const struct filemon_key *key,
831    const struct ktr_syscall *call)
832{
833	const register_t *args = (const void *)&call[1];
834	int flags, fd;
835
836	/*
837	 * XXX: In the .meta log, the base directory is missing, which makes
838	 * all references to relative pathnames useless.
839	 */
840
841	if (call->ktr_argsize < 3)
842		return NULL;
843	fd = (int)args[0];
844	flags = (int)args[2];
845
846	if (fd == AT_CWD) {
847		if ((flags & O_RDWR) == O_RDWR)
848			return syscall_enter(key, call, 1,
849			    &show_open_readwrite);
850		else if ((flags & O_WRONLY) == O_WRONLY)
851			return syscall_enter(key, call, 1, &show_open_write);
852		else if ((flags & O_RDONLY) == O_RDONLY)
853			return syscall_enter(key, call, 1, &show_open_read);
854		else
855			return NULL;
856	} else {
857		if ((flags & O_RDWR) == O_RDWR)
858			return syscall_enter(key, call, 1,
859			    &show_openat_readwrite);
860		else if ((flags & O_WRONLY) == O_WRONLY)
861			return syscall_enter(key, call, 1, &show_openat_write);
862		else if ((flags & O_RDONLY) == O_RDONLY)
863			return syscall_enter(key, call, 1, &show_openat_read);
864		else
865			return NULL;
866	}
867}
868
869/* TODO: monitor the other *at syscalls as well, not only openat. */
870
871/*ARGSUSED*/
872static struct filemon_state *
873filemon_sys_symlink(struct filemon *F, const struct filemon_key *key,
874    const struct ktr_syscall *call)
875{
876	return syscall_enter(key, call, 2, &show_symlink);
877}
878
879/*ARGSUSED*/
880static struct filemon_state *
881filemon_sys_unlink(struct filemon *F, const struct filemon_key *key,
882    const struct ktr_syscall *call)
883{
884	return syscall_enter(key, call, 1, &show_unlink);
885}
886
887/*ARGSUSED*/
888static struct filemon_state *
889filemon_sys_rename(struct filemon *F, const struct filemon_key *key,
890    const struct ktr_syscall *call)
891{
892	return syscall_enter(key, call, 2, &show_rename);
893}
894