nixpkgs mirror (for testing)
github.com/NixOS/nixpkgs
nix
1diff --git a/fsnotifier.h b/fsnotifier.h
2index e7b2a42456bc..9dfb61d8d5d0 100644
3--- a/fsnotifier.h
4+++ b/fsnotifier.h
5@@ -61,7 +61,7 @@ bool init_inotify(void);
6 void set_inotify_callback(void (*callback)(const char *, uint32_t));
7 int get_inotify_fd(void);
8 int watch(const char* root, array* mounts);
9-void unwatch(int id);
10+void unwatch(int id, char* path, unsigned int path_len);
11 bool process_inotify_input(void);
12 void close_inotify(void);
13
14diff --git a/inotify.c b/inotify.c
15index a42846379476..0a33eded78bf 100644
16--- a/inotify.c
17+++ b/inotify.c
18@@ -22,6 +22,8 @@ typedef struct watch_node_str {
19 struct watch_node_str* parent;
20 array* kids;
21 unsigned int path_len;
22+ struct watch_node_str* prev;
23+ struct watch_node_str* next;
24 char path[];
25 } watch_node;
26
27@@ -102,7 +104,7 @@ int get_inotify_fd(void) {
28
29 #define EVENT_MASK (IN_MODIFY | IN_ATTRIB | IN_CREATE | IN_DELETE | IN_MOVE | IN_DELETE_SELF | IN_MOVE_SELF)
30
31-static int add_watch(unsigned int path_len, watch_node* parent) {
32+static int add_watch(unsigned int path_len, watch_node* parent, watch_node** out) {
33 int wd = inotify_add_watch(inotify_fd, path_buf, EVENT_MASK);
34 if (wd < 0) {
35 if (errno == EACCES || errno == ENOENT) {
36@@ -123,36 +125,39 @@ static int add_watch(unsigned int path_len, watch_node* parent) {
37 userlog(LOG_INFO, "watching %s: %d", path_buf, wd);
38 }
39
40- watch_node* node = table_get(watches, wd);
41- if (node != NULL) {
42- if (node->wd != wd) {
43- userlog(LOG_ERR, "table error: corruption at %d:%s / %d:%s / %d", wd, path_buf, node->wd, node->path, watch_count);
44- return ERR_ABORT;
45- }
46- else if (strcmp(node->path, path_buf) != 0) {
47- char buf1[PATH_MAX], buf2[PATH_MAX];
48- const char* normalized1 = realpath(node->path, buf1);
49- const char* normalized2 = realpath(path_buf, buf2);
50- if (normalized1 == NULL || normalized2 == NULL || strcmp(normalized1, normalized2) != 0) {
51- userlog(LOG_ERR, "table error: collision at %d (new %s, existing %s)", wd, path_buf, node->path);
52- return ERR_ABORT;
53- }
54- else {
55- userlog(LOG_INFO, "intersection at %d: (new %s, existing %s, real %s)", wd, path_buf, node->path, normalized1);
56- return ERR_IGNORE;
57- }
58- }
59-
60- return wd;
61- }
62-
63- node = malloc(sizeof(watch_node) + path_len + 1);
64+ watch_node* existing = table_get(watches, wd);
65+ if (existing != NULL) {
66+ for (;;) {
67+ if (existing->wd != wd) {
68+ userlog(LOG_ERR, "table error: corruption at %d:%s / %d:%s / %d", wd, path_buf, existing->wd, existing->path, watch_count);
69+ return ERR_ABORT;
70+ }
71+ if (existing->path_len == path_len && strncmp(existing->path, path_buf, path_len) == 0) {
72+ return wd;
73+ }
74+ char buf1[PATH_MAX], buf2[PATH_MAX];
75+ const char* normalized1 = realpath(existing->path, buf1);
76+ const char* normalized2 = realpath(path_buf, buf2);
77+ if (normalized1 != NULL && normalized2 != NULL && strcmp(normalized1, normalized2) == 0) {
78+ userlog(LOG_INFO, "intersection at %d: (new %s, existing %s, real %s)", wd, path_buf, existing->path, normalized1);
79+ return ERR_IGNORE;
80+ }
81+ if (existing->next == NULL) {
82+ break;
83+ }
84+ existing = existing->next;
85+ }
86+ }
87+
88+ watch_node* node = malloc(sizeof(watch_node) + path_len + 1);
89 CHECK_NULL(node, ERR_ABORT)
90 memcpy(node->path, path_buf, path_len + 1);
91 node->path_len = path_len;
92 node->wd = wd;
93 node->parent = parent;
94 node->kids = NULL;
95+ node->prev = existing;
96+ node->next = NULL;
97
98 if (parent != NULL) {
99 if (parent->kids == NULL) {
100@@ -162,11 +167,15 @@ static int add_watch(unsigned int path_len, watch_node* parent) {
101 CHECK_NULL(array_push(parent->kids, node), ERR_ABORT)
102 }
103
104- if (table_put(watches, wd, node) == NULL) {
105+ if (existing != NULL) {
106+ existing->next = node;
107+ }
108+ else if (table_put(watches, wd, node) == NULL) {
109 userlog(LOG_ERR, "table error: unable to put (%d:%s)", wd, path_buf);
110 return ERR_ABORT;
111 }
112
113+ *out = node;
114 return wd;
115 }
116
117@@ -177,22 +186,27 @@ static void watch_limit_reached(void) {
118 }
119 }
120
121-static void rm_watch(int wd, bool update_parent) {
122- watch_node* node = table_get(watches, wd);
123- if (node == NULL) {
124- return;
125+static void rm_watch(watch_node* node, bool update_parent) {
126+ if (node->prev != NULL) {
127+ node->prev->next = node->next;
128+ node->next->prev = node->prev;
129 }
130-
131- userlog(LOG_INFO, "unwatching %s: %d (%p)", node->path, node->wd, node);
132-
133- if (inotify_rm_watch(inotify_fd, node->wd) < 0) {
134- userlog(LOG_INFO, "inotify_rm_watch(%d:%s): %s", node->wd, node->path, strerror(errno));
135+ else if (node->next != NULL) {
136+ table_put(watches, node->wd, node->next);
137+ node->next->prev = NULL;
138+ }
139+ else {
140+ userlog(LOG_INFO, "unwatching %s: %d (%p)", node->path, node->wd, node);
141+ if (inotify_rm_watch(inotify_fd, node->wd) < 0) {
142+ userlog(LOG_INFO, "inotify_rm_watch(%d:%s): %s", node->wd, node->path, strerror(errno));
143+ }
144+ table_put(watches, node->wd, NULL);
145 }
146
147 for (int i = 0; i < array_size(node->kids); i++) {
148 watch_node* kid = array_get(node->kids, i);
149 if (kid != NULL) {
150- rm_watch(kid->wd, false);
151+ rm_watch(kid, false);
152 }
153 }
154
155@@ -207,7 +221,6 @@ static void rm_watch(int wd, bool update_parent) {
156
157 array_delete(node->kids);
158 free(node);
159- table_put(watches, wd, NULL);
160 }
161
162
163@@ -234,7 +247,9 @@ static int walk_tree(unsigned int path_len, watch_node* parent, bool recursive,
164 }
165 }
166
167- int id = add_watch(path_len, parent);
168+
169+ watch_node* node;
170+ int id = add_watch(path_len, parent, &node);
171
172 if (dir == NULL) {
173 return id;
174@@ -271,7 +286,7 @@ static int walk_tree(unsigned int path_len, watch_node* parent, bool recursive,
175
176 int subdir_id = walk_tree(path_len + 1 + name_len, table_get(watches, id), recursive, mounts);
177 if (subdir_id < 0 && subdir_id != ERR_IGNORE) {
178- rm_watch(id, true);
179+ rm_watch(node, true);
180 id = subdir_id;
181 break;
182 }
183@@ -323,47 +338,49 @@ int watch(const char* root, array* mounts) {
184 }
185
186
187-void unwatch(int id) {
188- rm_watch(id, true);
189+void unwatch(int wd, char* path, unsigned int path_len) {
190+ for (watch_node* node = table_get(watches, wd); node != NULL; node = node->next) {
191+ if (node->path_len == path_len && strncmp(node->path, path, path_len) == 0) {
192+ rm_watch(node, true);
193+ return;
194+ }
195+ }
196 }
197
198
199 static bool process_inotify_event(struct inotify_event* event) {
200- watch_node* node = table_get(watches, event->wd);
201- if (node == NULL) {
202- return true;
203- }
204-
205- bool is_dir = (event->mask & IN_ISDIR) == IN_ISDIR;
206- userlog(LOG_INFO, "inotify: wd=%d mask=%d dir=%d name=%s", event->wd, event->mask & (~IN_ISDIR), is_dir, node->path);
207-
208- unsigned int path_len = node->path_len;
209- memcpy(path_buf, node->path, path_len + 1);
210- if (event->len > 0) {
211- path_buf[path_len] = '/';
212- unsigned int name_len = strlen(event->name);
213- memcpy(path_buf + path_len + 1, event->name, name_len + 1);
214- path_len += name_len + 1;
215- }
216+ for (watch_node* node = table_get(watches, event->wd); node != NULL; node = node->next) {
217+ bool is_dir = (event->mask & IN_ISDIR) == IN_ISDIR;
218+ userlog(LOG_INFO, "inotify: wd=%d mask=%d dir=%d name=%s", event->wd, event->mask & (~IN_ISDIR), is_dir, node->path);
219+
220+ unsigned int path_len = node->path_len;
221+ memcpy(path_buf, node->path, path_len + 1);
222+ if (event->len > 0) {
223+ path_buf[path_len] = '/';
224+ unsigned int name_len = strlen(event->name);
225+ memcpy(path_buf + path_len + 1, event->name, name_len + 1);
226+ path_len += name_len + 1;
227+ }
228
229- if (callback != NULL) {
230- (*callback)(path_buf, event->mask);
231- }
232+ if (callback != NULL) {
233+ (*callback)(path_buf, event->mask);
234+ }
235
236- if (is_dir && event->mask & (IN_CREATE | IN_MOVED_TO)) {
237- int result = walk_tree(path_len, node, true, NULL);
238- if (result < 0 && result != ERR_IGNORE && result != ERR_CONTINUE) {
239- return false;
240+ if (is_dir && event->mask & (IN_CREATE | IN_MOVED_TO)) {
241+ int result = walk_tree(path_len, node, true, NULL);
242+ if (result < 0 && result != ERR_IGNORE && result != ERR_CONTINUE) {
243+ return false;
244+ }
245 }
246- }
247
248- if (is_dir && event->mask & (IN_DELETE | IN_MOVED_FROM)) {
249- for (int i = 0; i < array_size(node->kids); i++) {
250- watch_node* kid = array_get(node->kids, i);
251- if (kid != NULL && strncmp(path_buf, kid->path, kid->path_len) == 0) {
252- rm_watch(kid->wd, false);
253- array_put(node->kids, i, NULL);
254- break;
255+ if (is_dir && event->mask & (IN_DELETE | IN_MOVED_FROM)) {
256+ for (int i = 0; i < array_size(node->kids); i++) {
257+ watch_node* kid = array_get(node->kids, i);
258+ if (kid != NULL && strncmp(path_buf, kid->path, kid->path_len) == 0) {
259+ rm_watch(kid, false);
260+ array_put(node->kids, i, NULL);
261+ break;
262+ }
263 }
264 }
265 }
266diff --git a/main.c b/main.c
267index b6b2e6fdb5b0..32cc8efe7856 100644
268--- a/main.c
269+++ b/main.c
270@@ -246,7 +246,7 @@ static void unregister_roots(void) {
271 watch_root* root;
272 while ((root = array_pop(roots)) != NULL) {
273 userlog(LOG_INFO, "unregistering root: %s", root->path);
274- unwatch(root->id);
275+ unwatch(root->id, root->path, strlen(root->path));
276 free(root->path);
277 free(root);
278 }
279@@ -422,7 +422,7 @@ static void check_root_removal(const char* path) {
280 for (int i = 0; i < array_size(roots); i++) {
281 watch_root* root = array_get(roots, i);
282 if (root->id >= 0 && strcmp(path, UNFLATTEN(root->path)) == 0) {
283- unwatch(root->id);
284+ unwatch(root->id, root->path, strlen(root->path));
285 root->id = -1;
286 userlog(LOG_INFO, "root deleted: %s\n", root->path);
287 report_event("DELETE", path);
288