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