• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2018 CTERA Networks.  All Rights Reserved.
3  *
4  * This program is free software; you can redistribute it and/or modify it
5  * under the terms of version 2 or any later of the GNU General Public License
6  * as published by the Free Software Foundation.
7  *
8  * This program is distributed in the hope that it would be useful, but
9  * WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11  *
12  * Further, this software is distributed without any warranty that it is
13  * free of the rightful claim of any third person regarding infringement
14  * or the like.  Any license provided herein, whether implied or
15  * otherwise, applies only to this software file.  Patent licenses, if
16  * any, provided herein do not apply to combinations of this program with
17  * other software, or any other product whatsoever.
18  *
19  * You should have received a copy of the GNU General Public License along
20  * with this program; if not, write the Free Software Foundation, Inc.,
21  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22  *
23  * Started by Amir Goldstein <amir73il@gmail.com>
24  *
25  * DESCRIPTION
26  *     Check that inotify work for an overlayfs file after copy up and
27  *     drop caches.
28  *
29  *     An inotify watch pins the file inode in cache, but not the dentry.
30  *     The watch will not report events on the file if overlayfs does not
31  *     obtain the pinned inode to the new allocated dentry after drop caches.
32  *
33  *     The problem has been fixed by commit:
34  *       764baba80168 "ovl: hash non-dir by lower inode for fsnotify"
35  *
36  *
37  * ALGORITHM
38  *     Add watch on an overlayfs lower file then chmod file and drop dentry
39  *     and inode caches. Execute operations on file and expect events to be
40  *     reported on file watch.
41  */
42 
43 #include "config.h"
44 
45 #if defined(HAVE_SYS_INOTIFY_H)
46 # include <sys/inotify.h>
47 #endif
48 #include <stdio.h>
49 #include <sys/stat.h>
50 #include <sys/types.h>
51 #include <sys/sysmacros.h>
52 #include <fcntl.h>
53 #include <errno.h>
54 #include <string.h>
55 #include <sys/syscall.h>
56 #include <sys/mount.h>
57 #include <limits.h>
58 #include "tst_test.h"
59 #include "inotify.h"
60 
61 #if defined(HAVE_SYS_INOTIFY_H)
62 
63 #define EVENT_MAX 1024
64 /* size of the event structure, not counting name */
65 #define EVENT_SIZE  (sizeof (struct inotify_event))
66 /* reasonable guess as to size of 1024 events */
67 #define EVENT_BUF_LEN        (EVENT_MAX * (EVENT_SIZE + 16))
68 
69 #define BUF_SIZE 256
70 static int fd_notify, reap_wd;
71 static int wd;
72 
73 struct event_t {
74 	unsigned int mask;
75 };
76 
77 #define OVL_MNT "ovl"
78 #define FILE_NAME "test_file"
79 #define FILE_PATH OVL_MNT"/"FILE_NAME
80 
81 static int ovl_mounted;
82 
83 static struct event_t event_set[EVENT_MAX];
84 
85 static char event_buf[EVENT_BUF_LEN];
86 
verify_inotify(void)87 void verify_inotify(void)
88 {
89 	int test_cnt = 0;
90 
91 	/*
92 	 * generate sequence of events
93 	 */
94 	SAFE_CHMOD(FILE_PATH, 0644);
95 	event_set[test_cnt].mask = IN_ATTRIB;
96 	test_cnt++;
97 
98 	SAFE_FILE_PRINTF(FILE_PATH, "1");
99 
100 	event_set[test_cnt].mask = IN_OPEN;
101 	test_cnt++;
102 
103 	event_set[test_cnt].mask = IN_CLOSE_WRITE;
104 	test_cnt++;
105 
106 	/* Make sure events on upper/lower do not show in overlay watch */
107 	SAFE_TOUCH("lower/"FILE_NAME, 0644, NULL);
108 	SAFE_TOUCH("upper/"FILE_NAME, 0644, NULL);
109 
110 	int len = read(fd_notify, event_buf, EVENT_BUF_LEN);
111 	if (len == -1 && errno != EAGAIN) {
112 		tst_brk(TBROK | TERRNO,
113 			"read(%d, buf, %zu) failed",
114 			fd_notify, EVENT_BUF_LEN);
115 	}
116 
117 	int i = 0, test_num = 0;
118 	while (i < len) {
119 		struct inotify_event *event;
120 		event = (struct inotify_event *)&event_buf[i];
121 		if (test_num >= test_cnt) {
122 			tst_res(TFAIL,
123 				"get unnecessary event: "
124 				"wd=%d mask=%08x cookie=%-5u len=%-2u "
125 				"name=\"%.*s\"", event->wd, event->mask,
126 				event->cookie, event->len, event->len,
127 				event->name);
128 		} else if (event_set[test_num].mask == event->mask &&
129 			   !event->len) {
130 			tst_res(TPASS,
131 				"get event: wd=%d mask=%08x "
132 				"cookie=%-5u len=%-2u",
133 				event->wd, event->mask,
134 				event->cookie, event->len);
135 		} else {
136 			tst_res(TFAIL, "get event: wd=%d mask=%08x "
137 				"(expected %x) cookie=%-5u len=%-2u "
138 				"name=\"%.*s\" (expected \"\")",
139 				event->wd, event->mask,
140 				event_set[test_num].mask,
141 				event->cookie, event->len, event->len,
142 				event->name);
143 		}
144 		test_num++;
145 		i += EVENT_SIZE + event->len;
146 	}
147 
148 	for (; test_num < test_cnt; test_num++) {
149 		tst_res(TFAIL, "didn't get event: mask=%x ",
150 			event_set[test_num].mask);
151 	}
152 }
153 
setup(void)154 static void setup(void)
155 {
156 	struct stat buf;
157 	int ret;
158 
159 	/* Setup an overlay mount with lower file */
160 	SAFE_MKDIR("lower", 0755);
161 	SAFE_TOUCH("lower/"FILE_NAME, 0644, NULL);
162 	SAFE_MKDIR("upper", 0755);
163 	SAFE_MKDIR("work", 0755);
164 	SAFE_MKDIR(OVL_MNT, 0755);
165 	ret = mount("overlay", OVL_MNT, "overlay", 0,
166 		    "lowerdir=lower,upperdir=upper,workdir=work");
167 	if (ret < 0) {
168 		if (errno == ENODEV) {
169 			tst_brk(TCONF,
170 				"overlayfs is not configured in this kernel.");
171 		} else {
172 			tst_brk(TBROK | TERRNO,
173 				"overlayfs mount failed");
174 		}
175 	}
176 	ovl_mounted = 1;
177 
178 	fd_notify = myinotify_init1(O_NONBLOCK);
179 	if (fd_notify < 0) {
180 		if (errno == ENOSYS) {
181 			tst_brk(TCONF,
182 				"inotify is not configured in this kernel.");
183 		} else {
184 			tst_brk(TBROK | TERRNO,
185 				"inotify_init () failed");
186 		}
187 	}
188 
189 	/* Setup a watch on an overlayfs lower file */
190 	if ((wd = myinotify_add_watch(fd_notify, FILE_PATH,
191 				IN_ATTRIB | IN_OPEN | IN_CLOSE_WRITE)) < 0) {
192 		tst_brk(TBROK | TERRNO,
193 			"inotify_add_watch (%d, " FILE_PATH ", "
194 			"IN_ATTRIB | IN_OPEN | IN_CLOSE_WRITE) failed",
195 			fd_notify);
196 		reap_wd = 1;
197 	};
198 
199 	SAFE_STAT(FILE_PATH, &buf);
200 	tst_res(TINFO, FILE_PATH " ino=%lu, dev=%u:%u", buf.st_ino,
201 			major(buf.st_dev), minor(buf.st_dev));
202 
203 	/* Drop dentry caches, so overlayfs will allocate a new dentry */
204 	SAFE_FILE_PRINTF("/proc/sys/vm/drop_caches", "2");
205 
206 	/* Copy up file */
207 	SAFE_CHMOD(FILE_PATH, 0600);
208 
209 	/* Lookup file and see if we got the watched file inode number */
210 	SAFE_STAT(FILE_PATH, &buf);
211 	tst_res(TINFO, FILE_PATH " ino=%lu, dev=%u:%u", buf.st_ino,
212 			major(buf.st_dev), minor(buf.st_dev));
213 }
214 
cleanup(void)215 static void cleanup(void)
216 {
217 	if (reap_wd && myinotify_rm_watch(fd_notify, wd) < 0) {
218 		tst_res(TWARN,
219 			"inotify_rm_watch (%d, %d) failed,", fd_notify, wd);
220 
221 	}
222 
223 	if (fd_notify > 0)
224 		SAFE_CLOSE(fd_notify);
225 
226 	if (ovl_mounted)
227 		SAFE_UMOUNT(OVL_MNT);
228 }
229 
230 static struct tst_test test = {
231 	.needs_root = 1,
232 	.needs_tmpdir = 1,
233 	.setup = setup,
234 	.cleanup = cleanup,
235 	.test_all = verify_inotify,
236 };
237 
238 #else
239 	TST_TEST_TCONF("system doesn't have required inotify support");
240 #endif
241