2009-05-21 17:02:01 -04:00
/*
* fs / inotify_user . c - inotify support for userspace
*
* Authors :
* John McCutchan < ttb @ tentacle . dhs . org >
* Robert Love < rml @ novell . com >
*
* Copyright ( C ) 2005 John McCutchan
* Copyright 2006 Hewlett - Packard Development Company , L . P .
*
* Copyright ( C ) 2009 Eric Paris < Red Hat Inc >
* inotify was largely rewriten to make use of the fsnotify infrastructure
*
* This program is free software ; you can redistribute it and / or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation ; either version 2 , or ( at your option ) any
* later version .
*
* This program is distributed in the hope that it will be useful , but
* WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
* General Public License for more details .
*/
2010-07-28 10:18:37 -04:00
# include <linux/dcache.h> /* d_unlinked */
2009-05-21 17:02:01 -04:00
# include <linux/fs.h> /* struct inode */
# include <linux/fsnotify_backend.h>
# include <linux/inotify.h>
# include <linux/path.h> /* struct path */
# include <linux/slab.h> /* kmem_* */
# include <linux/types.h>
2010-05-12 15:34:07 -07:00
# include <linux/sched.h>
2017-02-02 17:54:15 +01:00
# include <linux/sched/user.h>
2009-05-21 17:02:01 -04:00
# include "inotify.h"
2009-12-17 21:24:21 -05:00
/*
2014-01-21 15:48:14 -08:00
* Check if 2 events contain the same information .
2009-12-17 21:24:21 -05:00
*/
2014-01-21 15:48:14 -08:00
static bool event_compare ( struct fsnotify_event * old_fsn ,
struct fsnotify_event * new_fsn )
2009-12-17 21:24:21 -05:00
{
2014-01-21 15:48:14 -08:00
struct inotify_event_info * old , * new ;
if ( old_fsn - > mask & FS_IN_IGNORED )
return false ;
old = INOTIFY_E ( old_fsn ) ;
new = INOTIFY_E ( new_fsn ) ;
if ( ( old_fsn - > mask = = new_fsn - > mask ) & &
( old_fsn - > inode = = new_fsn - > inode ) & &
( old - > name_len = = new - > name_len ) & &
( ! old - > name_len | | ! strcmp ( old - > name , new - > name ) ) )
return true ;
2009-12-17 21:24:21 -05:00
return false ;
}
2014-01-28 18:53:22 +01:00
static int inotify_merge ( struct list_head * list ,
struct fsnotify_event * event )
2009-12-17 21:24:21 -05:00
{
struct fsnotify_event * last_event ;
2014-01-21 15:48:14 -08:00
last_event = list_entry ( list - > prev , struct fsnotify_event , list ) ;
2014-01-28 18:53:22 +01:00
return event_compare ( last_event , event ) ;
2009-12-17 21:24:21 -05:00
}
2014-01-21 15:48:14 -08:00
int inotify_handle_event ( struct fsnotify_group * group ,
struct inode * inode ,
struct fsnotify_mark * inode_mark ,
struct fsnotify_mark * vfsmount_mark ,
2016-11-20 20:19:09 -05:00
u32 mask , const void * data , int data_type ,
2016-11-10 17:51:50 +01:00
const unsigned char * file_name , u32 cookie ,
struct fsnotify_iter_info * iter_info )
2009-05-21 17:02:01 -04:00
{
2009-12-17 21:24:24 -05:00
struct inotify_inode_mark * i_mark ;
2014-01-21 15:48:14 -08:00
struct inotify_event_info * event ;
struct fsnotify_event * fsn_event ;
2014-01-28 18:53:22 +01:00
int ret ;
2014-01-21 15:48:14 -08:00
int len = 0 ;
int alloc_len = sizeof ( struct inotify_event_info ) ;
2009-05-21 17:02:01 -04:00
2010-07-28 10:18:39 -04:00
BUG_ON ( vfsmount_mark ) ;
2014-01-21 15:48:15 -08:00
if ( ( inode_mark - > mask & FS_EXCL_UNLINK ) & &
( data_type = = FSNOTIFY_EVENT_PATH ) ) {
2016-11-20 20:19:09 -05:00
const struct path * path = data ;
2014-01-21 15:48:15 -08:00
if ( d_unlinked ( path - > dentry ) )
return 0 ;
}
2014-01-21 15:48:14 -08:00
if ( file_name ) {
len = strlen ( file_name ) ;
alloc_len + = len + 1 ;
}
2010-07-28 10:18:37 -04:00
2014-01-21 15:48:14 -08:00
pr_debug ( " %s: group=%p inode=%p mask=%x \n " , __func__ , group , inode ,
mask ) ;
2009-05-21 17:02:01 -04:00
2010-07-28 10:18:39 -04:00
i_mark = container_of ( inode_mark , struct inotify_inode_mark ,
2009-12-17 21:24:24 -05:00
fsn_mark ) ;
2009-05-21 17:02:01 -04:00
2014-01-21 15:48:14 -08:00
event = kmalloc ( alloc_len , GFP_KERNEL ) ;
if ( unlikely ( ! event ) )
2009-05-21 17:02:01 -04:00
return - ENOMEM ;
2014-01-21 15:48:14 -08:00
fsn_event = & event - > fse ;
fsnotify_init_event ( fsn_event , inode , mask ) ;
event - > wd = i_mark - > wd ;
2014-02-17 13:09:50 +01:00
event - > sync_cookie = cookie ;
2014-01-21 15:48:14 -08:00
event - > name_len = len ;
if ( len )
strcpy ( event - > name , file_name ) ;
2009-05-21 17:02:01 -04:00
2014-08-06 16:03:26 -07:00
ret = fsnotify_add_event ( group , fsn_event , inotify_merge ) ;
2014-01-28 18:53:22 +01:00
if ( ret ) {
2014-01-21 15:48:14 -08:00
/* Our event wasn't used in the end. Free it. */
fsnotify_destroy_event ( group , fsn_event ) ;
2009-08-16 21:51:44 -04:00
}
2009-05-21 17:02:01 -04:00
2010-07-28 10:18:39 -04:00
if ( inode_mark - > mask & IN_ONESHOT )
2011-06-14 17:29:51 +02:00
fsnotify_destroy_mark ( inode_mark , group ) ;
2009-05-21 17:02:01 -04:00
2014-01-28 18:53:22 +01:00
return 0 ;
2009-05-21 17:02:01 -04:00
}
2009-12-17 21:24:24 -05:00
static void inotify_freeing_mark ( struct fsnotify_mark * fsn_mark , struct fsnotify_group * group )
2009-05-21 17:02:01 -04:00
{
2009-12-17 21:24:24 -05:00
inotify_ignored_and_remove_idr ( fsn_mark , group ) ;
2009-05-21 17:02:01 -04:00
}
2009-08-24 16:03:35 -04:00
/*
* This is NEVER supposed to be called . Inotify marks should either have been
* removed from the idr when the watch was removed or in the
* fsnotify_destroy_mark_by_group ( ) call when the inotify instance was being
* torn down . This is only called if the idr is about to be freed but there
* are still marks in it .
*/
2009-05-21 17:02:01 -04:00
static int idr_callback ( int id , void * p , void * data )
{
2009-12-17 21:24:24 -05:00
struct fsnotify_mark * fsn_mark ;
struct inotify_inode_mark * i_mark ;
2009-08-24 16:03:35 -04:00
static bool warned = false ;
if ( warned )
return 0 ;
2010-01-15 12:12:25 -05:00
warned = true ;
2009-12-17 21:24:24 -05:00
fsn_mark = p ;
i_mark = container_of ( fsn_mark , struct inotify_inode_mark , fsn_mark ) ;
2009-08-24 16:03:35 -04:00
2009-12-17 21:24:24 -05:00
WARN ( 1 , " inotify closing but id=%d for fsn_mark=%p in group=%p still in "
2009-08-24 16:03:35 -04:00
" idr. Probably leaking memory \n " , id , p , data ) ;
/*
* I ' m taking the liberty of assuming that the mark in question is a
* valid address and I ' m dereferencing it . This might help to figure
* out why we got here and the panic is no worse than the original
* BUG ( ) that was here .
*/
2009-12-17 21:24:24 -05:00
if ( fsn_mark )
2016-12-09 09:38:55 +01:00
printk ( KERN_WARNING " fsn_mark->group=%p wd=%d \n " ,
fsn_mark - > group , i_mark - > wd ) ;
2009-05-21 17:02:01 -04:00
return 0 ;
}
static void inotify_free_group_priv ( struct fsnotify_group * group )
{
2011-03-30 22:57:33 -03:00
/* ideally the idr is empty and we won't hit the BUG in the callback */
2009-08-24 16:03:35 -04:00
idr_for_each ( & group - > inotify_data . idr , idr_callback , group ) ;
2009-05-21 17:02:01 -04:00
idr_destroy ( & group - > inotify_data . idr ) ;
2016-12-14 15:56:33 +02:00
if ( group - > inotify_data . ucounts )
dec_inotify_instances ( group - > inotify_data . ucounts ) ;
2009-05-21 17:02:01 -04:00
}
2014-01-21 15:48:14 -08:00
static void inotify_free_event ( struct fsnotify_event * fsn_event )
2009-05-21 17:02:01 -04:00
{
2014-01-21 15:48:14 -08:00
kfree ( INOTIFY_E ( fsn_event ) ) ;
2009-05-21 17:02:01 -04:00
}
2016-12-21 18:06:12 +01:00
/* ding dong the mark is dead */
static void inotify_free_mark ( struct fsnotify_mark * fsn_mark )
{
struct inotify_inode_mark * i_mark ;
i_mark = container_of ( fsn_mark , struct inotify_inode_mark , fsn_mark ) ;
kmem_cache_free ( inotify_inode_mark_cachep , i_mark ) ;
}
2009-05-21 17:02:01 -04:00
const struct fsnotify_ops inotify_fsnotify_ops = {
. handle_event = inotify_handle_event ,
. free_group_priv = inotify_free_group_priv ,
2014-01-21 15:48:14 -08:00
. free_event = inotify_free_event ,
2009-05-21 17:02:01 -04:00
. freeing_mark = inotify_freeing_mark ,
2016-12-21 18:06:12 +01:00
. free_mark = inotify_free_mark ,
2009-05-21 17:02:01 -04:00
} ;