diff options
| author | Paul Eggert | 2017-03-30 11:08:23 -0700 |
|---|---|---|
| committer | Paul Eggert | 2017-03-30 11:08:42 -0700 |
| commit | 6ff870218dd4bc015cc4115ceb2febd8d807e57c (patch) | |
| tree | 67eaa089c0754194e75eea898e24c4a6bcfbe91a | |
| parent | 1be3330b31f9c0d0d0f7d25641e8b81c807ca616 (diff) | |
| download | emacs-6ff870218dd4bc015cc4115ceb2febd8d807e57c.tar.gz emacs-6ff870218dd4bc015cc4115ceb2febd8d807e57c.zip | |
Some inotify cleanup
This catches some problems with integer overflow and races
that I noticed in inotify.c after reviewing the changes
installed to fix Bug#26126.
* src/fns.c, src/lisp.h (equal_no_quit): Now extern.
* src/inotify.c (aspect_to_inotifymask):
Check for cycles and for improper lists.
(make_lispy_mask, lispy_mask_match_p): Remove.
All callers changed to use INTEGER_TO_CONS and CONS_TO_INTEGER.
(inotifyevent_to_event, add_watch):
Don’t assume watch descriptors and cookies fit in fixnums.
(add_watch): Use assoc_no_quit, not Fassoc.
Avoid integer overflow in (very!) long-running processes where
the Emacs watch ID could overflow. Avoid some duplicate code.
(find_descriptor): New function.
(remove_descriptor): First arg is now the returned value from
find_descriptor, rather than the descriptor. This way, the
value can be removed without calling Fdelete, which might quit.
Wait until the end (when watch_list is consistent) before signaling
any errors.
(remove_watch, inotify_callback):
Use find_descriptor to avoid the need for Fdelete.
(inotify_callback): Use simpler tests for ioctl failure.
Free temporary buffer if signaled, and put it on the stack if small.
Use ssize_t to index through read results, to avoid a cast.
(valid_watch_descriptor): New function, with a tighter check.
(Finotify_rm_watch, Finotify_valid_p): Use it.
(Finotify_valid_p): Use assoc_no_quit and ass_no_quit instead
of Fassoc. Do not assume the first assoc succeeds.
* test/src/inotify-tests.el (inotify-valid-p-simple):
Add inotify-valid-p tests, some of which dump core without
the fixes noted above.
| -rw-r--r-- | src/fns.c | 3 | ||||
| -rw-r--r-- | src/inotify.c | 250 | ||||
| -rw-r--r-- | src/lisp.h | 1 | ||||
| -rw-r--r-- | test/src/inotify-tests.el | 9 |
4 files changed, 149 insertions, 114 deletions
| @@ -38,7 +38,6 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */ | |||
| 38 | 38 | ||
| 39 | static void sort_vector_copy (Lisp_Object, ptrdiff_t, | 39 | static void sort_vector_copy (Lisp_Object, ptrdiff_t, |
| 40 | Lisp_Object *restrict, Lisp_Object *restrict); | 40 | Lisp_Object *restrict, Lisp_Object *restrict); |
| 41 | static bool equal_no_quit (Lisp_Object, Lisp_Object); | ||
| 42 | enum equal_kind { EQUAL_NO_QUIT, EQUAL_PLAIN, EQUAL_INCLUDING_PROPERTIES }; | 41 | enum equal_kind { EQUAL_NO_QUIT, EQUAL_PLAIN, EQUAL_INCLUDING_PROPERTIES }; |
| 43 | static bool internal_equal (Lisp_Object, Lisp_Object, | 42 | static bool internal_equal (Lisp_Object, Lisp_Object, |
| 44 | enum equal_kind, int, Lisp_Object); | 43 | enum equal_kind, int, Lisp_Object); |
| @@ -2121,7 +2120,7 @@ of strings. (`equal' ignores text properties.) */) | |||
| 2121 | Use this only on arguments that are cycle-free and not too large and | 2120 | Use this only on arguments that are cycle-free and not too large and |
| 2122 | are not window configurations. */ | 2121 | are not window configurations. */ |
| 2123 | 2122 | ||
| 2124 | static bool | 2123 | bool |
| 2125 | equal_no_quit (Lisp_Object o1, Lisp_Object o2) | 2124 | equal_no_quit (Lisp_Object o1, Lisp_Object o2) |
| 2126 | { | 2125 | { |
| 2127 | return internal_equal (o1, o2, EQUAL_NO_QUIT, 0, Qnil); | 2126 | return internal_equal (o1, o2, EQUAL_NO_QUIT, 0, Qnil); |
diff --git a/src/inotify.c b/src/inotify.c index 004689bd4b5..a0a89aa0f41 100644 --- a/src/inotify.c +++ b/src/inotify.c | |||
| @@ -41,16 +41,16 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */ | |||
| 41 | #ifndef IN_ONLYDIR | 41 | #ifndef IN_ONLYDIR |
| 42 | # define IN_ONLYDIR 0 | 42 | # define IN_ONLYDIR 0 |
| 43 | #endif | 43 | #endif |
| 44 | #define INOTIFY_DEFAULT_MASK (IN_ALL_EVENTS|IN_EXCL_UNLINK) | 44 | #define INOTIFY_DEFAULT_MASK (IN_ALL_EVENTS | IN_EXCL_UNLINK) |
| 45 | 45 | ||
| 46 | /* File handle for inotify. */ | 46 | /* File handle for inotify. */ |
| 47 | static int inotifyfd = -1; | 47 | static int inotifyfd = -1; |
| 48 | 48 | ||
| 49 | /* Alist of files being watched. We want the returned descriptor to | 49 | /* Alist of files being watched. We want the returned descriptor to |
| 50 | be unique for every watch, but inotify returns the same descriptor | 50 | be unique for every watch, but inotify returns the same descriptor |
| 51 | for multiple calls to inotify_add_watch with the same file. In | 51 | WD for multiple calls to inotify_add_watch with the same file. |
| 52 | order to solve this problem, we add a ID, uniquely identifying a | 52 | Supply a nonnegative integer ID, so that WD and ID together |
| 53 | watch/file combination. | 53 | uniquely identify a watch/file combination. |
| 54 | 54 | ||
| 55 | For the same reason, we also need to store the watch's mask and we | 55 | For the same reason, we also need to store the watch's mask and we |
| 56 | can't allow the following flags to be used. | 56 | can't allow the following flags to be used. |
| @@ -60,12 +60,21 @@ static int inotifyfd = -1; | |||
| 60 | IN_ONESHOT | 60 | IN_ONESHOT |
| 61 | IN_ONLYDIR | 61 | IN_ONLYDIR |
| 62 | 62 | ||
| 63 | Format: (descriptor . ((id filename callback mask) ...)) | 63 | Each element of this list is of the form (DESCRIPTOR . WATCHES) |
| 64 | */ | 64 | where no two DESCRIPTOR values are the same. DESCRIPTOR represents |
| 65 | the inotify watch descriptor and WATCHES is a list with elements of | ||
| 66 | the form (ID FILENAME CALLBACK MASK), where ID is the integer | ||
| 67 | described above, FILENAME names the file being watched, CALLBACK is | ||
| 68 | invoked when the event occurs, and MASK represents the aspects | ||
| 69 | being watched. The WATCHES list is sorted by ID. Although | ||
| 70 | DESCRIPTOR and MASK are ordinarily integers, they are conses when | ||
| 71 | representing integers outside of fixnum range. */ | ||
| 72 | |||
| 65 | static Lisp_Object watch_list; | 73 | static Lisp_Object watch_list; |
| 66 | 74 | ||
| 67 | static Lisp_Object | 75 | static Lisp_Object |
| 68 | mask_to_aspects (uint32_t mask) { | 76 | mask_to_aspects (uint32_t mask) |
| 77 | { | ||
| 69 | Lisp_Object aspects = Qnil; | 78 | Lisp_Object aspects = Qnil; |
| 70 | if (mask & IN_ACCESS) | 79 | if (mask & IN_ACCESS) |
| 71 | aspects = Fcons (Qaccess, aspects); | 80 | aspects = Fcons (Qaccess, aspects); |
| @@ -149,15 +158,13 @@ symbol_to_inotifymask (Lisp_Object symb) | |||
| 149 | static uint32_t | 158 | static uint32_t |
| 150 | aspect_to_inotifymask (Lisp_Object aspect) | 159 | aspect_to_inotifymask (Lisp_Object aspect) |
| 151 | { | 160 | { |
| 152 | if (CONSP (aspect)) | 161 | if (CONSP (aspect) || NILP (aspect)) |
| 153 | { | 162 | { |
| 154 | Lisp_Object x = aspect; | 163 | Lisp_Object x = aspect; |
| 155 | uint32_t mask = 0; | 164 | uint32_t mask = 0; |
| 156 | while (CONSP (x)) | 165 | FOR_EACH_TAIL (x) |
| 157 | { | 166 | mask |= symbol_to_inotifymask (XCAR (x)); |
| 158 | mask |= symbol_to_inotifymask (XCAR (x)); | 167 | CHECK_LIST_END (x, aspect); |
| 159 | x = XCDR (x); | ||
| 160 | } | ||
| 161 | return mask; | 168 | return mask; |
| 162 | } | 169 | } |
| 163 | else | 170 | else |
| @@ -165,25 +172,13 @@ aspect_to_inotifymask (Lisp_Object aspect) | |||
| 165 | } | 172 | } |
| 166 | 173 | ||
| 167 | static Lisp_Object | 174 | static Lisp_Object |
| 168 | make_lispy_mask (uint32_t mask) | ||
| 169 | { | ||
| 170 | return Fcons (make_number (mask & 0xffff), | ||
| 171 | make_number (mask >> 16)); | ||
| 172 | } | ||
| 173 | |||
| 174 | static bool | ||
| 175 | lispy_mask_match_p (Lisp_Object mask, uint32_t other) | ||
| 176 | { | ||
| 177 | return (XINT (XCAR (mask)) & other) | ||
| 178 | || ((XINT (XCDR (mask)) << 16) & other); | ||
| 179 | } | ||
| 180 | |||
| 181 | static Lisp_Object | ||
| 182 | inotifyevent_to_event (Lisp_Object watch, struct inotify_event const *ev) | 175 | inotifyevent_to_event (Lisp_Object watch, struct inotify_event const *ev) |
| 183 | { | 176 | { |
| 184 | Lisp_Object name = Qnil; | 177 | Lisp_Object name; |
| 178 | uint32_t mask; | ||
| 179 | CONS_TO_INTEGER (Fnth (make_number (3), watch), uint32_t, mask); | ||
| 185 | 180 | ||
| 186 | if (! lispy_mask_match_p (Fnth (make_number (3), watch), ev->mask)) | 181 | if (! (mask & ev->mask)) |
| 187 | return Qnil; | 182 | return Qnil; |
| 188 | 183 | ||
| 189 | if (ev->len > 0) | 184 | if (ev->len > 0) |
| @@ -195,10 +190,10 @@ inotifyevent_to_event (Lisp_Object watch, struct inotify_event const *ev) | |||
| 195 | else | 190 | else |
| 196 | name = XCAR (XCDR (watch)); | 191 | name = XCAR (XCDR (watch)); |
| 197 | 192 | ||
| 198 | return list2 (list4 (Fcons (make_number (ev->wd), XCAR (watch)), | 193 | return list2 (list4 (Fcons (INTEGER_TO_CONS (ev->wd), XCAR (watch)), |
| 199 | mask_to_aspects (ev->mask), | 194 | mask_to_aspects (ev->mask), |
| 200 | name, | 195 | name, |
| 201 | make_number (ev->cookie)), | 196 | INTEGER_TO_CONS (ev->cookie)), |
| 202 | Fnth (make_number (2), watch)); | 197 | Fnth (make_number (2), watch)); |
| 203 | } | 198 | } |
| 204 | 199 | ||
| @@ -209,55 +204,88 @@ static Lisp_Object | |||
| 209 | add_watch (int wd, Lisp_Object filename, | 204 | add_watch (int wd, Lisp_Object filename, |
| 210 | Lisp_Object aspect, Lisp_Object callback) | 205 | Lisp_Object aspect, Lisp_Object callback) |
| 211 | { | 206 | { |
| 212 | Lisp_Object descriptor = make_number (wd); | 207 | Lisp_Object descriptor = INTEGER_TO_CONS (wd); |
| 213 | Lisp_Object elt = Fassoc (descriptor, watch_list); | 208 | Lisp_Object tail = assoc_no_quit (descriptor, watch_list); |
| 214 | Lisp_Object watches = Fcdr (elt); | ||
| 215 | Lisp_Object watch, watch_id; | 209 | Lisp_Object watch, watch_id; |
| 216 | Lisp_Object mask = make_lispy_mask (aspect_to_inotifymask (aspect)); | 210 | uint32_t imask = aspect_to_inotifymask (aspect); |
| 211 | Lisp_Object mask = INTEGER_TO_CONS (imask); | ||
| 217 | 212 | ||
| 218 | int id = 0; | 213 | EMACS_INT id = 0; |
| 219 | 214 | if (NILP (tail)) | |
| 220 | while (! NILP (watches)) | 215 | { |
| 216 | tail = list1 (descriptor); | ||
| 217 | watch_list = Fcons (tail, watch_list); | ||
| 218 | } | ||
| 219 | else | ||
| 221 | { | 220 | { |
| 222 | id = max (id, 1 + XINT (XCAR (XCAR (watches)))); | 221 | /* Assign a watch ID that is not already in use, by looking |
| 223 | watches = XCDR (watches); | 222 | for a gap in the existing sorted list. */ |
| 223 | for (; ! NILP (XCDR (tail)); tail = XCDR (tail), id++) | ||
| 224 | if (!EQ (XCAR (XCAR (XCDR (tail))), make_number (id))) | ||
| 225 | break; | ||
| 226 | if (MOST_POSITIVE_FIXNUM < id) | ||
| 227 | emacs_abort (); | ||
| 224 | } | 228 | } |
| 225 | 229 | ||
| 226 | watch_id = make_number (id); | 230 | watch_id = make_number (id); |
| 227 | watch = list4 (watch_id, filename, callback, mask); | 231 | watch = list4 (watch_id, filename, callback, mask); |
| 228 | 232 | XSETCDR (tail, Fcons (watch, XCDR (tail))); | |
| 229 | if (NILP (elt)) | ||
| 230 | watch_list = Fcons (Fcons (descriptor, Fcons (watch, Qnil)), | ||
| 231 | watch_list); | ||
| 232 | else | ||
| 233 | XSETCDR (elt, Fcons (watch, XCDR (elt))); | ||
| 234 | 233 | ||
| 235 | return Fcons (descriptor, watch_id); | 234 | return Fcons (descriptor, watch_id); |
| 236 | } | 235 | } |
| 237 | 236 | ||
| 238 | /* Remove all watches associated with descriptor. If INVALID_P is | 237 | /* Find the watch list element (if any) matching DESCRIPTOR. Return |
| 239 | true, the descriptor is already invalid, i.e. it received a | 238 | nil if not found. If found, return t if the first element matches |
| 240 | IN_IGNORED event. In this case skip calling inotify_rm_watch. */ | 239 | DESCRIPTOR; otherwise, return the cons whose cdr matches |
| 240 | DESCRIPTOR. This lets the caller easily remove the element | ||
| 241 | matching DESCRIPTOR without having to search for it again, and | ||
| 242 | without calling Fdelete (which might quit). */ | ||
| 243 | |||
| 244 | static Lisp_Object | ||
| 245 | find_descriptor (Lisp_Object descriptor) | ||
| 246 | { | ||
| 247 | Lisp_Object tail, prevtail = Qt; | ||
| 248 | for (tail = watch_list; !NILP (tail); prevtail = tail, tail = XCDR (tail)) | ||
| 249 | if (equal_no_quit (XCAR (XCAR (tail)), descriptor)) | ||
| 250 | return prevtail; | ||
| 251 | return Qnil; | ||
| 252 | } | ||
| 253 | |||
| 254 | /* Remove all watches associated with the watch list element after | ||
| 255 | PREVTAIL, or after the first element if PREVTAIL is t. If INVALID_P | ||
| 256 | is true, the descriptor is already invalid, i.e., it received a | ||
| 257 | IN_IGNORED event. In this case skip calling inotify_rm_watch. */ | ||
| 241 | static void | 258 | static void |
| 242 | remove_descriptor (Lisp_Object descriptor, bool invalid_p) | 259 | remove_descriptor (Lisp_Object prevtail, bool invalid_p) |
| 243 | { | 260 | { |
| 244 | Lisp_Object elt = Fassoc (descriptor, watch_list); | 261 | Lisp_Object tail = CONSP (prevtail) ? XCDR (prevtail) : watch_list; |
| 245 | 262 | ||
| 246 | if (! NILP (elt)) | 263 | int inotify_errno = 0; |
| 264 | if (! invalid_p) | ||
| 247 | { | 265 | { |
| 248 | int wd = XINT (descriptor); | 266 | int wd; |
| 267 | CONS_TO_INTEGER (XCAR (XCAR (tail)), int, wd); | ||
| 268 | if (inotify_rm_watch (inotifyfd, wd) != 0) | ||
| 269 | inotify_errno = errno; | ||
| 270 | } | ||
| 249 | 271 | ||
| 250 | watch_list = Fdelete (elt, watch_list); | 272 | if (CONSP (prevtail)) |
| 251 | if (! invalid_p) | 273 | XSETCDR (prevtail, XCDR (tail)); |
| 252 | if (inotify_rm_watch (inotifyfd, wd) == -1) | 274 | else |
| 253 | report_file_notify_error ("Could not rm watch", descriptor); | 275 | { |
| 276 | watch_list = XCDR (tail); | ||
| 277 | if (NILP (watch_list)) | ||
| 278 | { | ||
| 279 | delete_read_fd (inotifyfd); | ||
| 280 | emacs_close (inotifyfd); | ||
| 281 | inotifyfd = -1; | ||
| 282 | } | ||
| 254 | } | 283 | } |
| 255 | /* Cleanup if no more files are watched. */ | 284 | |
| 256 | if (NILP (watch_list)) | 285 | if (inotify_errno != 0) |
| 257 | { | 286 | { |
| 258 | emacs_close (inotifyfd); | 287 | errno = inotify_errno; |
| 259 | delete_read_fd (inotifyfd); | 288 | report_file_notify_error ("Could not rm watch", XCAR (tail)); |
| 260 | inotifyfd = -1; | ||
| 261 | } | 289 | } |
| 262 | } | 290 | } |
| 263 | 291 | ||
| @@ -265,19 +293,19 @@ remove_descriptor (Lisp_Object descriptor, bool invalid_p) | |||
| 265 | static void | 293 | static void |
| 266 | remove_watch (Lisp_Object descriptor, Lisp_Object id) | 294 | remove_watch (Lisp_Object descriptor, Lisp_Object id) |
| 267 | { | 295 | { |
| 268 | Lisp_Object elt = Fassoc (descriptor, watch_list); | 296 | Lisp_Object prevtail = find_descriptor (descriptor); |
| 269 | 297 | if (NILP (prevtail)) | |
| 270 | if (! NILP (elt)) | 298 | return; |
| 271 | { | 299 | |
| 272 | Lisp_Object watch = Fassoc (id, XCDR (elt)); | 300 | Lisp_Object elt = XCAR (CONSP (prevtail) ? XCDR (prevtail) : watch_list); |
| 273 | 301 | for (Lisp_Object prev = elt; !NILP (XCDR (prev)); prev = XCDR (prev)) | |
| 274 | if (! NILP (watch)) | 302 | if (EQ (id, XCAR (XCAR (XCDR (prev))))) |
| 275 | XSETCDR (elt, Fdelete (watch, XCDR (elt))); | 303 | { |
| 276 | 304 | XSETCDR (prev, XCDR (XCDR (prev))); | |
| 277 | /* Remove the descriptor if noone is watching it. */ | 305 | if (NILP (XCDR (elt))) |
| 278 | if (NILP (XCDR (elt))) | 306 | remove_descriptor (prevtail, false); |
| 279 | remove_descriptor (descriptor, false); | 307 | break; |
| 280 | } | 308 | } |
| 281 | } | 309 | } |
| 282 | 310 | ||
| 283 | /* This callback is called when the FD is available for read. The inotify | 311 | /* This callback is called when the FD is available for read. The inotify |
| @@ -285,52 +313,44 @@ remove_watch (Lisp_Object descriptor, Lisp_Object id) | |||
| 285 | static void | 313 | static void |
| 286 | inotify_callback (int fd, void *_) | 314 | inotify_callback (int fd, void *_) |
| 287 | { | 315 | { |
| 288 | struct input_event event; | ||
| 289 | int to_read; | 316 | int to_read; |
| 290 | char *buffer; | 317 | if (ioctl (fd, FIONREAD, &to_read) < 0) |
| 291 | ssize_t n; | ||
| 292 | size_t i; | ||
| 293 | |||
| 294 | to_read = 0; | ||
| 295 | if (ioctl (fd, FIONREAD, &to_read) == -1) | ||
| 296 | report_file_notify_error ("Error while retrieving file system events", | 318 | report_file_notify_error ("Error while retrieving file system events", |
| 297 | Qnil); | 319 | Qnil); |
| 298 | buffer = xmalloc (to_read); | 320 | USE_SAFE_ALLOCA; |
| 299 | n = read (fd, buffer, to_read); | 321 | char *buffer = SAFE_ALLOCA (to_read); |
| 322 | ssize_t n = read (fd, buffer, to_read); | ||
| 300 | if (n < 0) | 323 | if (n < 0) |
| 301 | { | 324 | report_file_notify_error ("Error while reading file system events", Qnil); |
| 302 | xfree (buffer); | ||
| 303 | report_file_notify_error ("Error while reading file system events", Qnil); | ||
| 304 | } | ||
| 305 | 325 | ||
| 326 | struct input_event event; | ||
| 306 | EVENT_INIT (event); | 327 | EVENT_INIT (event); |
| 307 | event.kind = FILE_NOTIFY_EVENT; | 328 | event.kind = FILE_NOTIFY_EVENT; |
| 308 | 329 | ||
| 309 | i = 0; | 330 | for (ssize_t i = 0; i < n; ) |
| 310 | while (i < (size_t)n) | ||
| 311 | { | 331 | { |
| 312 | struct inotify_event *ev = (struct inotify_event *) &buffer[i]; | 332 | struct inotify_event *ev = (struct inotify_event *) &buffer[i]; |
| 313 | Lisp_Object descriptor = make_number (ev->wd); | 333 | Lisp_Object descriptor = INTEGER_TO_CONS (ev->wd); |
| 314 | Lisp_Object elt = Fassoc (descriptor, watch_list); | 334 | Lisp_Object prevtail = find_descriptor (descriptor); |
| 315 | 335 | ||
| 316 | if (! NILP (elt)) | 336 | if (! NILP (prevtail)) |
| 317 | { | 337 | { |
| 318 | Lisp_Object watches = XCDR (elt); | 338 | Lisp_Object tail = CONSP (prevtail) ? XCDR (prevtail) : watch_list; |
| 319 | while (! NILP (watches)) | 339 | for (Lisp_Object watches = XCDR (XCAR (tail)); ! NILP (watches); |
| 340 | watches = XCDR (watches)) | ||
| 320 | { | 341 | { |
| 321 | event.arg = inotifyevent_to_event (XCAR (watches), ev); | 342 | event.arg = inotifyevent_to_event (XCAR (watches), ev); |
| 322 | if (!NILP (event.arg)) | 343 | if (!NILP (event.arg)) |
| 323 | kbd_buffer_store_event (&event); | 344 | kbd_buffer_store_event (&event); |
| 324 | watches = XCDR (watches); | ||
| 325 | } | 345 | } |
| 326 | /* If event was removed automatically: Drop it from watch list. */ | 346 | /* If event was removed automatically: Drop it from watch list. */ |
| 327 | if (ev->mask & IN_IGNORED) | 347 | if (ev->mask & IN_IGNORED) |
| 328 | remove_descriptor (descriptor, true); | 348 | remove_descriptor (prevtail, true); |
| 329 | } | 349 | } |
| 330 | i += sizeof (*ev) + ev->len; | 350 | i += sizeof (*ev) + ev->len; |
| 331 | } | 351 | } |
| 332 | 352 | ||
| 333 | xfree (buffer); | 353 | SAFE_FREE (); |
| 334 | } | 354 | } |
| 335 | 355 | ||
| 336 | DEFUN ("inotify-add-watch", Finotify_add_watch, Sinotify_add_watch, 3, 3, 0, | 356 | DEFUN ("inotify-add-watch", Finotify_add_watch, Sinotify_add_watch, 3, 3, 0, |
| @@ -407,7 +427,7 @@ IN_ONLYDIR */) | |||
| 407 | 427 | ||
| 408 | if (inotifyfd < 0) | 428 | if (inotifyfd < 0) |
| 409 | { | 429 | { |
| 410 | inotifyfd = inotify_init1 (IN_NONBLOCK|IN_CLOEXEC); | 430 | inotifyfd = inotify_init1 (IN_NONBLOCK | IN_CLOEXEC); |
| 411 | if (inotifyfd < 0) | 431 | if (inotifyfd < 0) |
| 412 | report_file_notify_error ("File watching is not available", Qnil); | 432 | report_file_notify_error ("File watching is not available", Qnil); |
| 413 | watch_list = Qnil; | 433 | watch_list = Qnil; |
| @@ -416,12 +436,24 @@ IN_ONLYDIR */) | |||
| 416 | 436 | ||
| 417 | encoded_file_name = ENCODE_FILE (filename); | 437 | encoded_file_name = ENCODE_FILE (filename); |
| 418 | wd = inotify_add_watch (inotifyfd, SSDATA (encoded_file_name), mask); | 438 | wd = inotify_add_watch (inotifyfd, SSDATA (encoded_file_name), mask); |
| 419 | if (wd == -1) | 439 | if (wd < 0) |
| 420 | report_file_notify_error ("Could not add watch for file", filename); | 440 | report_file_notify_error ("Could not add watch for file", filename); |
| 421 | 441 | ||
| 422 | return add_watch (wd, filename, aspect, callback); | 442 | return add_watch (wd, filename, aspect, callback); |
| 423 | } | 443 | } |
| 424 | 444 | ||
| 445 | static bool | ||
| 446 | valid_watch_descriptor (Lisp_Object wd) | ||
| 447 | { | ||
| 448 | return (CONSP (wd) | ||
| 449 | && (RANGED_INTEGERP (0, XCAR (wd), INT_MAX) | ||
| 450 | || (CONSP (XCAR (wd)) | ||
| 451 | && RANGED_INTEGERP ((MOST_POSITIVE_FIXNUM >> 16) + 1, | ||
| 452 | XCAR (XCAR (wd)), INT_MAX >> 16) | ||
| 453 | && RANGED_INTEGERP (0, XCDR (XCAR (wd)), (1 << 16) - 1))) | ||
| 454 | && NATNUMP (XCDR (wd))); | ||
| 455 | } | ||
| 456 | |||
| 425 | DEFUN ("inotify-rm-watch", Finotify_rm_watch, Sinotify_rm_watch, 1, 1, 0, | 457 | DEFUN ("inotify-rm-watch", Finotify_rm_watch, Sinotify_rm_watch, 1, 1, 0, |
| 426 | doc: /* Remove an existing WATCH-DESCRIPTOR. | 458 | doc: /* Remove an existing WATCH-DESCRIPTOR. |
| 427 | 459 | ||
| @@ -433,9 +465,7 @@ See inotify_rm_watch(2) for more information. */) | |||
| 433 | 465 | ||
| 434 | Lisp_Object descriptor, id; | 466 | Lisp_Object descriptor, id; |
| 435 | 467 | ||
| 436 | if (! (CONSP (watch_descriptor) | 468 | if (! valid_watch_descriptor (watch_descriptor)) |
| 437 | && INTEGERP (XCAR (watch_descriptor)) | ||
| 438 | && INTEGERP (XCDR (watch_descriptor)))) | ||
| 439 | report_file_notify_error ("Invalid descriptor ", watch_descriptor); | 469 | report_file_notify_error ("Invalid descriptor ", watch_descriptor); |
| 440 | 470 | ||
| 441 | descriptor = XCAR (watch_descriptor); | 471 | descriptor = XCAR (watch_descriptor); |
| @@ -456,16 +486,12 @@ reason. Removing the watch by calling `inotify-rm-watch' also makes | |||
| 456 | it invalid. */) | 486 | it invalid. */) |
| 457 | (Lisp_Object watch_descriptor) | 487 | (Lisp_Object watch_descriptor) |
| 458 | { | 488 | { |
| 459 | Lisp_Object elt, watch; | 489 | if (! valid_watch_descriptor (watch_descriptor)) |
| 460 | |||
| 461 | if (! (CONSP (watch_descriptor) | ||
| 462 | && INTEGERP (XCAR (watch_descriptor)) | ||
| 463 | && INTEGERP (XCDR (watch_descriptor)))) | ||
| 464 | return Qnil; | 490 | return Qnil; |
| 465 | 491 | Lisp_Object tail = assoc_no_quit (XCAR (watch_descriptor), watch_list); | |
| 466 | elt = Fassoc (XCAR (watch_descriptor), watch_list); | 492 | if (NILP (tail)) |
| 467 | watch = Fassoc (XCDR (watch_descriptor), XCDR (elt)); | 493 | return Qnil; |
| 468 | 494 | Lisp_Object watch = assq_no_quit (XCDR (watch_descriptor), XCDR (tail)); | |
| 469 | return ! NILP (watch) ? Qt : Qnil; | 495 | return ! NILP (watch) ? Qt : Qnil; |
| 470 | } | 496 | } |
| 471 | 497 | ||
diff --git a/src/lisp.h b/src/lisp.h index 4b9cd3c4702..3125bd2a5dd 100644 --- a/src/lisp.h +++ b/src/lisp.h | |||
| @@ -3376,6 +3376,7 @@ extern Lisp_Object merge (Lisp_Object, Lisp_Object, Lisp_Object); | |||
| 3376 | extern Lisp_Object do_yes_or_no_p (Lisp_Object); | 3376 | extern Lisp_Object do_yes_or_no_p (Lisp_Object); |
| 3377 | extern Lisp_Object concat2 (Lisp_Object, Lisp_Object); | 3377 | extern Lisp_Object concat2 (Lisp_Object, Lisp_Object); |
| 3378 | extern Lisp_Object concat3 (Lisp_Object, Lisp_Object, Lisp_Object); | 3378 | extern Lisp_Object concat3 (Lisp_Object, Lisp_Object, Lisp_Object); |
| 3379 | extern bool equal_no_quit (Lisp_Object, Lisp_Object); | ||
| 3379 | extern Lisp_Object nconc2 (Lisp_Object, Lisp_Object); | 3380 | extern Lisp_Object nconc2 (Lisp_Object, Lisp_Object); |
| 3380 | extern Lisp_Object assq_no_quit (Lisp_Object, Lisp_Object); | 3381 | extern Lisp_Object assq_no_quit (Lisp_Object, Lisp_Object); |
| 3381 | extern Lisp_Object assoc_no_quit (Lisp_Object, Lisp_Object); | 3382 | extern Lisp_Object assoc_no_quit (Lisp_Object, Lisp_Object); |
diff --git a/test/src/inotify-tests.el b/test/src/inotify-tests.el index f30aecc9c4f..987e1fc0777 100644 --- a/test/src/inotify-tests.el +++ b/test/src/inotify-tests.el | |||
| @@ -28,6 +28,13 @@ | |||
| 28 | (declare-function inotify-add-watch "inotify.c" (file-name aspect callback)) | 28 | (declare-function inotify-add-watch "inotify.c" (file-name aspect callback)) |
| 29 | (declare-function inotify-rm-watch "inotify.c" (watch-descriptor)) | 29 | (declare-function inotify-rm-watch "inotify.c" (watch-descriptor)) |
| 30 | 30 | ||
| 31 | (ert-deftest inotify-valid-p-simple () | ||
| 32 | "Simple tests for `inotify-valid-p'." | ||
| 33 | (skip-unless (featurep 'inotify)) | ||
| 34 | (should-not (inotify-valid-p 0)) | ||
| 35 | (should-not (inotify-valid-p nil)) | ||
| 36 | (should-not (inotify-valid-p '(0 . 0)))) | ||
| 37 | |||
| 31 | ;; (ert-deftest filewatch-file-watch-aspects-check () | 38 | ;; (ert-deftest filewatch-file-watch-aspects-check () |
| 32 | ;; "Test whether `file-watch' properly checks the aspects." | 39 | ;; "Test whether `file-watch' properly checks the aspects." |
| 33 | ;; (let ((temp-file (make-temp-file "filewatch-aspects"))) | 40 | ;; (let ((temp-file (make-temp-file "filewatch-aspects"))) |
| @@ -56,7 +63,9 @@ | |||
| 56 | (insert "Foo\n")) | 63 | (insert "Foo\n")) |
| 57 | (read-event nil nil 5) | 64 | (read-event nil nil 5) |
| 58 | (should (> events 0))) | 65 | (should (> events 0))) |
| 66 | (should (inotify-valid-p wd)) | ||
| 59 | (inotify-rm-watch wd) | 67 | (inotify-rm-watch wd) |
| 68 | (should-not (inotify-valid-p wd)) | ||
| 60 | (delete-file temp-file))))) | 69 | (delete-file temp-file))))) |
| 61 | 70 | ||
| 62 | (provide 'inotify-tests) | 71 | (provide 'inotify-tests) |