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>
2009-05-21 17:02:01 -04:00
# include "inotify.h"
2009-12-17 21:24:21 -05:00
/*
* Check if 2 events contain the same information . We do not compare private data
* but at this moment that isn ' t a problem for any know fsnotify listeners .
*/
static bool event_compare ( struct fsnotify_event * old , struct fsnotify_event * new )
{
if ( ( old - > mask = = new - > mask ) & &
( old - > to_tell = = new - > to_tell ) & &
( old - > data_type = = new - > data_type ) & &
( old - > name_len = = new - > name_len ) ) {
switch ( old - > data_type ) {
case ( FSNOTIFY_EVENT_INODE ) :
/* remember, after old was put on the wait_q we aren't
* allowed to look at the inode any more , only thing
* left to check was if the file_name is the same */
if ( ! old - > name_len | |
! strcmp ( old - > file_name , new - > file_name ) )
return true ;
break ;
2010-08-12 14:23:04 -07:00
case ( FSNOTIFY_EVENT_PATH ) :
if ( ( old - > path . mnt = = new - > path . mnt ) & &
( old - > path . dentry = = new - > path . dentry ) )
2009-12-17 21:24:21 -05:00
return true ;
break ;
case ( FSNOTIFY_EVENT_NONE ) :
if ( old - > mask & FS_Q_OVERFLOW )
return true ;
else if ( old - > mask & FS_IN_IGNORED )
return false ;
return true ;
} ;
}
return false ;
}
2010-07-28 10:18:37 -04:00
static struct fsnotify_event * inotify_merge ( struct list_head * list ,
struct fsnotify_event * event )
2009-12-17 21:24:21 -05:00
{
struct fsnotify_event_holder * last_holder ;
struct fsnotify_event * last_event ;
/* and the list better be locked by something too */
spin_lock ( & event - > lock ) ;
last_holder = list_entry ( list - > prev , struct fsnotify_event_holder , event_list ) ;
last_event = last_holder - > event ;
if ( event_compare ( last_event , event ) )
2010-07-28 10:18:37 -04:00
fsnotify_get_event ( last_event ) ;
else
last_event = NULL ;
2009-12-17 21:24:21 -05:00
spin_unlock ( & event - > lock ) ;
2010-07-28 10:18:37 -04:00
return last_event ;
2009-12-17 21:24:21 -05:00
}
2010-07-28 10:18:38 -04:00
static int inotify_handle_event ( struct fsnotify_group * group ,
2010-07-28 10:18:39 -04:00
struct fsnotify_mark * inode_mark ,
struct fsnotify_mark * vfsmount_mark ,
2010-07-28 10:18:38 -04:00
struct fsnotify_event * event )
2009-05-21 17:02:01 -04:00
{
2009-12-17 21:24:24 -05:00
struct inotify_inode_mark * i_mark ;
2009-05-21 17:02:01 -04:00
struct inode * to_tell ;
struct inotify_event_private_data * event_priv ;
struct fsnotify_event_private_data * fsn_event_priv ;
2010-07-28 10:18:37 -04:00
struct fsnotify_event * added_event ;
int wd , ret = 0 ;
2009-05-21 17:02:01 -04:00
2010-07-28 10:18:39 -04:00
BUG_ON ( vfsmount_mark ) ;
2010-07-28 10:18:37 -04:00
pr_debug ( " %s: group=%p event=%p to_tell=%p mask=%x \n " , __func__ , group ,
event , event - > to_tell , event - > mask ) ;
2009-05-21 17:02:01 -04:00
to_tell = event - > to_tell ;
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 ) ;
wd = i_mark - > wd ;
2009-05-21 17:02:01 -04:00
event_priv = kmem_cache_alloc ( event_priv_cachep , GFP_KERNEL ) ;
if ( unlikely ( ! event_priv ) )
return - ENOMEM ;
fsn_event_priv = & event_priv - > fsnotify_event_priv_data ;
fsn_event_priv - > group = group ;
event_priv - > wd = wd ;
2010-07-28 10:18:37 -04:00
added_event = fsnotify_add_notify_event ( group , event , fsn_event_priv , inotify_merge ) ;
if ( added_event ) {
2009-05-21 17:02:01 -04:00
inotify_free_event_priv ( fsn_event_priv ) ;
2010-07-28 10:18:37 -04:00
if ( ! IS_ERR ( added_event ) )
fsnotify_put_event ( added_event ) ;
else
ret = PTR_ERR ( added_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 )
fsnotify_destroy_mark ( inode_mark ) ;
2009-05-21 17:02:01 -04:00
return ret ;
}
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-12-17 21:24:21 -05:00
static bool inotify_should_send_event ( struct fsnotify_group * group , struct inode * inode ,
2010-07-28 10:18:39 -04:00
struct fsnotify_mark * inode_mark ,
2010-07-28 10:18:39 -04:00
struct fsnotify_mark * vfsmount_mark ,
2010-07-28 10:18:38 -04:00
__u32 mask , void * data , int data_type )
2009-05-21 17:02:01 -04:00
{
2010-07-28 10:18:39 -04:00
if ( ( inode_mark - > mask & FS_EXCL_UNLINK ) & &
2010-08-12 14:23:04 -07:00
( data_type = = FSNOTIFY_EVENT_PATH ) ) {
struct path * path = data ;
2010-07-28 10:18:37 -04:00
2010-08-12 14:23:04 -07:00
if ( d_unlinked ( path - > dentry ) )
2010-07-28 10:18:39 -04:00
return false ;
2010-07-28 10:18:37 -04:00
}
2010-07-28 10:18:39 -04:00
return true ;
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 )
printk ( KERN_WARNING " fsn_mark->group=%p inode=%p wd=%d \n " ,
fsn_mark - > group , fsn_mark - > i . inode , 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_remove_all ( & group - > inotify_data . idr ) ;
idr_destroy ( & group - > inotify_data . idr ) ;
2011-04-05 17:20:50 -04:00
atomic_dec ( & group - > inotify_data . user - > inotify_devs ) ;
2010-05-12 15:34:07 -07:00
free_uid ( group - > inotify_data . user ) ;
2009-05-21 17:02:01 -04:00
}
void inotify_free_event_priv ( struct fsnotify_event_private_data * fsn_event_priv )
{
struct inotify_event_private_data * event_priv ;
event_priv = container_of ( fsn_event_priv , struct inotify_event_private_data ,
fsnotify_event_priv_data ) ;
kmem_cache_free ( event_priv_cachep , event_priv ) ;
}
const struct fsnotify_ops inotify_fsnotify_ops = {
. handle_event = inotify_handle_event ,
. should_send_event = inotify_should_send_event ,
. free_group_priv = inotify_free_group_priv ,
. free_event_priv = inotify_free_event_priv ,
. freeing_mark = inotify_freeing_mark ,
} ;