diff options
Diffstat (limited to 'fs/notify')
-rw-r--r-- | fs/notify/fanotify/fanotify_user.c | 185 |
1 files changed, 133 insertions, 52 deletions
diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c index 05351936a725..cb7a0c5ff854 100644 --- a/fs/notify/fanotify/fanotify_user.c +++ b/fs/notify/fanotify/fanotify_user.c | |||
@@ -295,31 +295,83 @@ out: | |||
295 | return ret; | 295 | return ret; |
296 | } | 296 | } |
297 | 297 | ||
298 | static int fanotify_remove_mark(struct fsnotify_group *group, | 298 | static void fanotify_update_object_mask(struct fsnotify_group *group, |
299 | struct inode *inode, | 299 | struct inode *inode, |
300 | __u32 mask) | 300 | struct vfsmount *mnt, |
301 | struct fsnotify_mark *fsn_mark, | ||
302 | unsigned int flags, | ||
303 | __u32 mask) | ||
301 | { | 304 | { |
302 | struct fsnotify_mark *fsn_mark; | 305 | __u32 old_mask, new_mask; |
303 | __u32 new_mask; | ||
304 | |||
305 | pr_debug("%s: group=%p inode=%p mask=%x\n", __func__, | ||
306 | group, inode, mask); | ||
307 | 306 | ||
308 | fsn_mark = fsnotify_find_inode_mark(group, inode); | 307 | pr_debug("%s: group=%p inode=%p mnt=%p fsn_mark=%p flags=%x mask=%x\n", |
309 | if (!fsn_mark) | 308 | __func__, group, inode, mnt, fsn_mark, flags, mask); |
310 | return -ENOENT; | ||
311 | 309 | ||
312 | spin_lock(&fsn_mark->lock); | 310 | spin_lock(&fsn_mark->lock); |
313 | fsn_mark->mask &= ~mask; | 311 | old_mask = fsn_mark->mask; |
312 | if (flags & FAN_MARK_ADD) | ||
313 | fsn_mark->mask |= mask; | ||
314 | else if (flags & FAN_MARK_REMOVE) | ||
315 | fsn_mark->mask &= ~mask; | ||
316 | else | ||
317 | BUG(); | ||
314 | new_mask = fsn_mark->mask; | 318 | new_mask = fsn_mark->mask; |
315 | spin_unlock(&fsn_mark->lock); | 319 | spin_unlock(&fsn_mark->lock); |
316 | 320 | ||
317 | if (!new_mask) | 321 | if (!new_mask) |
318 | fsnotify_destroy_mark(fsn_mark); | 322 | fsnotify_destroy_mark(fsn_mark); |
323 | |||
324 | /* we made changes to a mask, update the group mask and the object mask | ||
325 | * so things happen quickly. */ | ||
326 | if (old_mask != new_mask) { | ||
327 | __u32 dropped, do_object, do_group; | ||
328 | |||
329 | /* more bits in old than in new? */ | ||
330 | dropped = (old_mask & ~new_mask); | ||
331 | /* more bits in this fsn_mark than the group? */ | ||
332 | do_group = (new_mask & ~group->mask); | ||
333 | |||
334 | if (inode) { | ||
335 | /* more bits in this fsn_mark than the object's mask? */ | ||
336 | do_object = (new_mask & ~inode->i_fsnotify_mask); | ||
337 | /* update the object with this new fsn_mark */ | ||
338 | if (dropped || do_object) | ||
339 | fsnotify_recalc_inode_mask(inode); | ||
340 | } else if (mnt) { | ||
341 | /* more bits in this fsn_mark than the object's mask? */ | ||
342 | do_object = (new_mask & ~mnt->mnt_fsnotify_mask); | ||
343 | /* update the object with this new fsn_mark */ | ||
344 | if (dropped || do_object) | ||
345 | fsnotify_recalc_vfsmount_mask(mnt); | ||
346 | } else { | ||
347 | BUG(); | ||
348 | } | ||
349 | |||
350 | /* update the group mask with the new mask */ | ||
351 | if (dropped || do_group) | ||
352 | fsnotify_recalc_group_mask(group); | ||
353 | } | ||
354 | } | ||
355 | |||
356 | static int fanotify_remove_mark(struct fsnotify_group *group, struct inode *inode, | ||
357 | struct vfsmount *mnt, unsigned int flags, __u32 mask) | ||
358 | { | ||
359 | struct fsnotify_mark *fsn_mark = NULL; | ||
360 | |||
361 | BUG_ON(inode && mnt); | ||
362 | BUG_ON(!inode && !mnt); | ||
363 | |||
364 | if (inode) | ||
365 | fsn_mark = fsnotify_find_inode_mark(group, inode); | ||
366 | else if (mnt) | ||
367 | fsn_mark = fsnotify_find_vfsmount_mark(group, mnt); | ||
319 | else | 368 | else |
320 | fsnotify_recalc_inode_mask(inode); | 369 | BUG(); |
321 | 370 | ||
322 | fsnotify_recalc_group_mask(group); | 371 | if (!fsn_mark) |
372 | return -ENOENT; | ||
373 | |||
374 | fanotify_update_object_mask(group, inode, mnt, fsn_mark, flags, mask); | ||
323 | 375 | ||
324 | /* matches the fsnotify_find_inode_mark() */ | 376 | /* matches the fsnotify_find_inode_mark() */ |
325 | fsnotify_put_mark(fsn_mark); | 377 | fsnotify_put_mark(fsn_mark); |
@@ -327,22 +379,48 @@ static int fanotify_remove_mark(struct fsnotify_group *group, | |||
327 | return 0; | 379 | return 0; |
328 | } | 380 | } |
329 | 381 | ||
330 | static int fanotify_add_mark(struct fsnotify_group *group, | 382 | static struct fsnotify_mark *fanotify_add_vfsmount_mark(struct fsnotify_group *group, |
331 | struct inode *inode, | 383 | struct vfsmount *mnt) |
332 | __u32 mask) | ||
333 | { | 384 | { |
334 | struct fsnotify_mark *fsn_mark; | 385 | struct fsnotify_mark *fsn_mark; |
335 | __u32 old_mask, new_mask; | ||
336 | int ret; | ||
337 | 386 | ||
338 | pr_debug("%s: group=%p inode=%p mask=%x\n", __func__, | 387 | fsn_mark = fsnotify_find_vfsmount_mark(group, mnt); |
339 | group, inode, mask); | 388 | if (!fsn_mark) { |
389 | struct fsnotify_mark *new_fsn_mark; | ||
390 | int ret; | ||
391 | |||
392 | fsn_mark = ERR_PTR(-ENOMEM); | ||
393 | new_fsn_mark = kmem_cache_alloc(fanotify_mark_cache, GFP_KERNEL); | ||
394 | if (!new_fsn_mark) | ||
395 | goto out; | ||
396 | |||
397 | fsnotify_init_mark(new_fsn_mark, fanotify_free_mark); | ||
398 | ret = fsnotify_add_mark(new_fsn_mark, group, NULL, mnt, 0); | ||
399 | if (ret) { | ||
400 | fsn_mark = ERR_PTR(ret); | ||
401 | fanotify_free_mark(new_fsn_mark); | ||
402 | goto out; | ||
403 | } | ||
404 | |||
405 | fsn_mark = new_fsn_mark; | ||
406 | } | ||
407 | out: | ||
408 | return fsn_mark; | ||
409 | } | ||
410 | |||
411 | static struct fsnotify_mark *fanotify_add_inode_mark(struct fsnotify_group *group, | ||
412 | struct inode *inode) | ||
413 | { | ||
414 | struct fsnotify_mark *fsn_mark; | ||
415 | |||
416 | pr_debug("%s: group=%p inode=%p\n", __func__, group, inode); | ||
340 | 417 | ||
341 | fsn_mark = fsnotify_find_inode_mark(group, inode); | 418 | fsn_mark = fsnotify_find_inode_mark(group, inode); |
342 | if (!fsn_mark) { | 419 | if (!fsn_mark) { |
343 | struct fsnotify_mark *new_fsn_mark; | 420 | struct fsnotify_mark *new_fsn_mark; |
421 | int ret; | ||
344 | 422 | ||
345 | ret = -ENOMEM; | 423 | fsn_mark = ERR_PTR(-ENOMEM); |
346 | new_fsn_mark = kmem_cache_alloc(fanotify_mark_cache, GFP_KERNEL); | 424 | new_fsn_mark = kmem_cache_alloc(fanotify_mark_cache, GFP_KERNEL); |
347 | if (!new_fsn_mark) | 425 | if (!new_fsn_mark) |
348 | goto out; | 426 | goto out; |
@@ -350,57 +428,60 @@ static int fanotify_add_mark(struct fsnotify_group *group, | |||
350 | fsnotify_init_mark(new_fsn_mark, fanotify_free_mark); | 428 | fsnotify_init_mark(new_fsn_mark, fanotify_free_mark); |
351 | ret = fsnotify_add_mark(new_fsn_mark, group, inode, NULL, 0); | 429 | ret = fsnotify_add_mark(new_fsn_mark, group, inode, NULL, 0); |
352 | if (ret) { | 430 | if (ret) { |
431 | fsn_mark = ERR_PTR(ret); | ||
353 | fanotify_free_mark(new_fsn_mark); | 432 | fanotify_free_mark(new_fsn_mark); |
354 | goto out; | 433 | goto out; |
355 | } | 434 | } |
356 | 435 | ||
357 | fsn_mark = new_fsn_mark; | 436 | fsn_mark = new_fsn_mark; |
358 | } | 437 | } |
438 | out: | ||
439 | return fsn_mark; | ||
440 | } | ||
359 | 441 | ||
360 | ret = 0; | 442 | static int fanotify_add_mark(struct fsnotify_group *group, struct inode *inode, |
443 | struct vfsmount *mnt, unsigned int flags, __u32 mask) | ||
444 | { | ||
445 | struct fsnotify_mark *fsn_mark; | ||
361 | 446 | ||
362 | spin_lock(&fsn_mark->lock); | 447 | pr_debug("%s: group=%p inode=%p mnt=%p flags=%x mask=%x\n", |
363 | old_mask = fsn_mark->mask; | 448 | __func__, group, inode, mnt, flags, mask); |
364 | fsn_mark->mask |= mask; | ||
365 | new_mask = fsn_mark->mask; | ||
366 | spin_unlock(&fsn_mark->lock); | ||
367 | 449 | ||
368 | /* we made changes to a mask, update the group mask and the inode mask | 450 | BUG_ON(inode && mnt); |
369 | * so things happen quickly. */ | 451 | BUG_ON(!inode && !mnt); |
370 | if (old_mask != new_mask) { | ||
371 | /* more bits in old than in new? */ | ||
372 | int dropped = (old_mask & ~new_mask); | ||
373 | /* more bits in this mark than the inode's mask? */ | ||
374 | int do_inode = (new_mask & ~inode->i_fsnotify_mask); | ||
375 | /* more bits in this mark than the group? */ | ||
376 | int do_group = (new_mask & ~group->mask); | ||
377 | 452 | ||
378 | /* update the inode with this new mark */ | 453 | if (inode) |
379 | if (dropped || do_inode) | 454 | fsn_mark = fanotify_add_inode_mark(group, inode); |
380 | fsnotify_recalc_inode_mask(inode); | 455 | else if (mnt) |
456 | fsn_mark = fanotify_add_vfsmount_mark(group, mnt); | ||
457 | else | ||
458 | BUG(); | ||
381 | 459 | ||
382 | /* update the group mask with the new mask */ | 460 | if (IS_ERR(fsn_mark)) |
383 | if (dropped || do_group) | 461 | goto out; |
384 | fsnotify_recalc_group_mask(group); | 462 | |
385 | } | 463 | fanotify_update_object_mask(group, inode, mnt, fsn_mark, flags, mask); |
386 | 464 | ||
387 | /* match the init or the find.... */ | 465 | /* match the init or the find.... */ |
388 | fsnotify_put_mark(fsn_mark); | 466 | fsnotify_put_mark(fsn_mark); |
389 | out: | 467 | out: |
390 | return ret; | 468 | return PTR_ERR(fsn_mark); |
391 | } | 469 | } |
392 | 470 | ||
393 | static int fanotify_update_mark(struct fsnotify_group *group, | 471 | static int fanotify_update_mark(struct fsnotify_group *group, |
394 | struct inode *inode, int flags, | 472 | struct inode *inode, struct vfsmount *mnt, |
395 | __u32 mask) | 473 | int flags, __u32 mask) |
396 | { | 474 | { |
397 | pr_debug("%s: group=%p inode=%p flags=%x mask=%x\n", __func__, | 475 | pr_debug("%s: group=%p inode=%p mnt=%p flags=%x mask=%x\n", |
398 | group, inode, flags, mask); | 476 | __func__, group, inode, mnt, flags, mask); |
477 | |||
478 | BUG_ON(inode && mnt); | ||
479 | BUG_ON(!inode && !mnt); | ||
399 | 480 | ||
400 | if (flags & FAN_MARK_ADD) | 481 | if (flags & FAN_MARK_ADD) |
401 | fanotify_add_mark(group, inode, mask); | 482 | fanotify_add_mark(group, inode, mnt, flags, mask); |
402 | else if (flags & FAN_MARK_REMOVE) | 483 | else if (flags & FAN_MARK_REMOVE) |
403 | fanotify_remove_mark(group, inode, mask); | 484 | fanotify_remove_mark(group, inode, mnt, flags, mask); |
404 | else | 485 | else |
405 | BUG(); | 486 | BUG(); |
406 | 487 | ||
@@ -502,7 +583,7 @@ SYSCALL_DEFINE(fanotify_mark)(int fanotify_fd, unsigned int flags, | |||
502 | group = filp->private_data; | 583 | group = filp->private_data; |
503 | 584 | ||
504 | /* create/update an inode mark */ | 585 | /* create/update an inode mark */ |
505 | ret = fanotify_update_mark(group, inode, flags, mask); | 586 | ret = fanotify_update_mark(group, inode, NULL, flags, mask); |
506 | 587 | ||
507 | path_put(&path); | 588 | path_put(&path); |
508 | fput_and_out: | 589 | fput_and_out: |