xref: /linux/samples/fanotify/fs-monitor.c (revision 22c55fb9eb92395d999b8404d73e58540d11bdd8)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright 2021, Collabora Ltd.
4  */
5 
6 #define _GNU_SOURCE
7 #include <errno.h>
8 #include <err.h>
9 #include <stdlib.h>
10 #include <stdio.h>
11 #include <fcntl.h>
12 #include <sys/fanotify.h>
13 #include <sys/types.h>
14 #include <unistd.h>
15 #ifndef __GLIBC__
16 #include <asm-generic/int-ll64.h>
17 #endif
18 
19 #ifndef FAN_FS_ERROR
20 #define FAN_FS_ERROR		0x00008000
21 #define FAN_EVENT_INFO_TYPE_ERROR	5
22 
23 struct fanotify_event_info_error {
24 	struct fanotify_event_info_header hdr;
25 	__s32 error;
26 	__u32 error_count;
27 };
28 #endif
29 
30 #ifndef FILEID_INO32_GEN
31 #define FILEID_INO32_GEN	1
32 #endif
33 
34 #ifndef FILEID_INVALID
35 #define	FILEID_INVALID		0xff
36 #endif
37 
38 static void print_fh(struct file_handle *fh)
39 {
40 	int i;
41 	uint32_t *h = (uint32_t *) fh->f_handle;
42 
43 	printf("\tfh: ");
44 	for (i = 0; i < fh->handle_bytes; i++)
45 		printf("%hhx", fh->f_handle[i]);
46 	printf("\n");
47 
48 	printf("\tdecoded fh: ");
49 	if (fh->handle_type == FILEID_INO32_GEN)
50 		printf("inode=%u gen=%u\n", h[0], h[1]);
51 	else if (fh->handle_type == FILEID_INVALID && !fh->handle_bytes)
52 		printf("Type %d (Superblock error)\n", fh->handle_type);
53 	else
54 		printf("Type %d (Unknown)\n", fh->handle_type);
55 
56 }
57 
58 static void handle_notifications(char *buffer, int len)
59 {
60 	struct fanotify_event_metadata *event =
61 		(struct fanotify_event_metadata *) buffer;
62 	struct fanotify_event_info_header *info;
63 	struct fanotify_event_info_error *err;
64 	struct fanotify_event_info_fid *fid;
65 	int off;
66 
67 	for (; FAN_EVENT_OK(event, len); event = FAN_EVENT_NEXT(event, len)) {
68 
69 		if (event->mask != FAN_FS_ERROR) {
70 			printf("unexpected FAN MARK: %llx\n",
71 							(unsigned long long)event->mask);
72 			goto next_event;
73 		}
74 
75 		if (event->fd != FAN_NOFD) {
76 			printf("Unexpected fd (!= FAN_NOFD)\n");
77 			goto next_event;
78 		}
79 
80 		printf("FAN_FS_ERROR (len=%d)\n", event->event_len);
81 
82 		for (off = sizeof(*event) ; off < event->event_len;
83 		     off += info->len) {
84 			info = (struct fanotify_event_info_header *)
85 				((char *) event + off);
86 
87 			switch (info->info_type) {
88 			case FAN_EVENT_INFO_TYPE_ERROR:
89 				err = (struct fanotify_event_info_error *) info;
90 
91 				printf("\tGeneric Error Record: len=%d\n",
92 				       err->hdr.len);
93 				printf("\terror: %d\n", err->error);
94 				printf("\terror_count: %d\n", err->error_count);
95 				break;
96 
97 			case FAN_EVENT_INFO_TYPE_FID:
98 				fid = (struct fanotify_event_info_fid *) info;
99 
100 				printf("\tfsid: %x%x\n",
101 #if defined(__GLIBC__)
102 				       fid->fsid.val[0], fid->fsid.val[1]);
103 #else
104 				       fid->fsid.__val[0], fid->fsid.__val[1]);
105 #endif
106 				print_fh((struct file_handle *) &fid->handle);
107 				break;
108 
109 			default:
110 				printf("\tUnknown info type=%d len=%d:\n",
111 				       info->info_type, info->len);
112 			}
113 		}
114 next_event:
115 		printf("---\n\n");
116 	}
117 }
118 
119 int main(int argc, char **argv)
120 {
121 	int fd;
122 
123 	char buffer[BUFSIZ];
124 
125 	if (argc < 2) {
126 		printf("Missing path argument\n");
127 		return 1;
128 	}
129 
130 	fd = fanotify_init(FAN_CLASS_NOTIF|FAN_REPORT_FID, O_RDONLY);
131 	if (fd < 0)
132 		errx(1, "fanotify_init");
133 
134 	if (fanotify_mark(fd, FAN_MARK_ADD|FAN_MARK_FILESYSTEM,
135 			  FAN_FS_ERROR, AT_FDCWD, argv[1])) {
136 		errx(1, "fanotify_mark");
137 	}
138 
139 	while (1) {
140 		int n = read(fd, buffer, BUFSIZ);
141 
142 		if (n < 0)
143 			errx(1, "read");
144 
145 		handle_notifications(buffer, n);
146 	}
147 
148 	return 0;
149 }
150