2005-04-17 02:20:36 +04:00
/*
* Directory notifications for Linux .
*
* Copyright ( C ) 2000 , 2001 , 2002 Stephen Rothwell
*
2009-05-22 01:01:33 +04:00
* Copyright ( C ) 2009 Eric Paris < Red Hat Inc >
* dnotify was largly rewritten to use the new fsnotify infrastructure
*
2005-04-17 02:20:36 +04:00
* 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 .
*/
# include <linux/fs.h>
# include <linux/module.h>
# include <linux/sched.h>
# include <linux/dnotify.h>
# include <linux/init.h>
# include <linux/spinlock.h>
# include <linux/slab.h>
2008-04-24 15:44:08 +04:00
# include <linux/fdtable.h>
2009-05-22 01:01:33 +04:00
# include <linux/fsnotify_backend.h>
2005-04-17 02:20:36 +04:00
2006-03-26 13:37:24 +04:00
int dir_notify_enable __read_mostly = 1 ;
2005-04-17 02:20:36 +04:00
2009-05-22 01:01:33 +04:00
static struct kmem_cache * dnotify_struct_cache __read_mostly ;
2009-12-18 05:24:24 +03:00
static struct kmem_cache * dnotify_mark_cache __read_mostly ;
2009-05-22 01:01:33 +04:00
static struct fsnotify_group * dnotify_group __read_mostly ;
static DEFINE_MUTEX ( dnotify_mark_mutex ) ;
/*
2009-12-18 05:24:24 +03:00
* dnotify will attach one of these to each inode ( i_fsnotify_marks ) which
2009-05-22 01:01:33 +04:00
* is being watched by dnotify . If multiple userspace applications are watching
* the same directory with dnotify their information is chained in dn
*/
2009-12-18 05:24:24 +03:00
struct dnotify_mark {
struct fsnotify_mark fsn_mark ;
2009-05-22 01:01:33 +04:00
struct dnotify_struct * dn ;
} ;
2005-04-17 02:20:36 +04:00
2009-05-22 01:01:33 +04:00
/*
* When a process starts or stops watching an inode the set of events which
* dnotify cares about for that inode may change . This function runs the
* list of everything receiving dnotify events about this directory and calculates
* the set of all those events . After it updates what dnotify is interested in
* it calls the fsnotify function so it can update the set of all events relevant
* to this inode .
*/
2009-12-18 05:24:24 +03:00
static void dnotify_recalc_inode_mask ( struct fsnotify_mark * fsn_mark )
2005-04-17 02:20:36 +04:00
{
2009-05-22 01:01:33 +04:00
__u32 new_mask , old_mask ;
2005-04-17 02:20:36 +04:00
struct dnotify_struct * dn ;
2009-12-18 05:24:24 +03:00
struct dnotify_mark * dn_mark = container_of ( fsn_mark ,
struct dnotify_mark ,
fsn_mark ) ;
2009-05-22 01:01:33 +04:00
2009-12-18 05:24:24 +03:00
assert_spin_locked ( & fsn_mark - > lock ) ;
2005-04-17 02:20:36 +04:00
2009-12-18 05:24:24 +03:00
old_mask = fsn_mark - > mask ;
2005-04-17 02:20:36 +04:00
new_mask = 0 ;
2009-12-18 05:24:24 +03:00
for ( dn = dn_mark - > dn ; dn ! = NULL ; dn = dn - > dn_next )
2009-05-22 01:01:33 +04:00
new_mask | = ( dn - > dn_mask & ~ FS_DN_MULTISHOT ) ;
2009-12-18 05:24:33 +03:00
fsnotify_set_mark_mask_locked ( fsn_mark , new_mask ) ;
2009-05-22 01:01:33 +04:00
if ( old_mask = = new_mask )
return ;
2009-12-18 05:24:24 +03:00
if ( fsn_mark - > i . inode )
fsnotify_recalc_inode_mask ( fsn_mark - > i . inode ) ;
2005-04-17 02:20:36 +04:00
}
2009-05-22 01:01:33 +04:00
/*
* Mains fsnotify call where events are delivered to dnotify .
* Find the dnotify mark on the relevant inode , run the list of dnotify structs
* on that mark and determine which of them has expressed interest in receiving
* events of this type . When found send the correct process and signal and
* destroy the dnotify struct if it was not registered to receive multiple
* events .
*/
static int dnotify_handle_event ( struct fsnotify_group * group ,
2010-07-28 18:18:39 +04:00
struct fsnotify_mark * inode_mark ,
struct fsnotify_mark * vfsmount_mark ,
2009-05-22 01:01:33 +04:00
struct fsnotify_event * event )
{
2009-12-18 05:24:24 +03:00
struct dnotify_mark * dn_mark ;
2009-05-22 01:01:33 +04:00
struct inode * to_tell ;
struct dnotify_struct * dn ;
struct dnotify_struct * * prev ;
struct fown_struct * fown ;
dnotify: ignore FS_EVENT_ON_CHILD
Mask off FS_EVENT_ON_CHILD in dnotify_handle_event(). Otherwise, when there
is more than one watch on a directory and dnotify_should_send_event()
succeeds, events with FS_EVENT_ON_CHILD set will trigger all watches and cause
spurious events.
This case was overlooked in commit e42e2773.
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
static void create_event(int s, siginfo_t* si, void* p)
{
printf("create\n");
}
static void delete_event(int s, siginfo_t* si, void* p)
{
printf("delete\n");
}
int main (void) {
struct sigaction action;
char *tmpdir, *file;
int fd1, fd2;
sigemptyset (&action.sa_mask);
action.sa_flags = SA_SIGINFO;
action.sa_sigaction = create_event;
sigaction (SIGRTMIN + 0, &action, NULL);
action.sa_sigaction = delete_event;
sigaction (SIGRTMIN + 1, &action, NULL);
# define TMPDIR "/tmp/test.XXXXXX"
tmpdir = malloc(strlen(TMPDIR) + 1);
strcpy(tmpdir, TMPDIR);
mkdtemp(tmpdir);
# define TMPFILE "/file"
file = malloc(strlen(tmpdir) + strlen(TMPFILE) + 1);
sprintf(file, "%s/%s", tmpdir, TMPFILE);
fd1 = open (tmpdir, O_RDONLY);
fcntl(fd1, F_SETSIG, SIGRTMIN);
fcntl(fd1, F_NOTIFY, DN_MULTISHOT | DN_CREATE);
fd2 = open (tmpdir, O_RDONLY);
fcntl(fd2, F_SETSIG, SIGRTMIN + 1);
fcntl(fd2, F_NOTIFY, DN_MULTISHOT | DN_DELETE);
if (fork()) {
/* This triggers a create event */
creat(file, 0600);
/* This triggers a create and delete event (!) */
unlink(file);
} else {
sleep(1);
rmdir(tmpdir);
}
return 0;
}
Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
Signed-off-by: Eric Paris <eparis@redhat.com>
2009-10-15 02:13:23 +04:00
__u32 test_mask = event - > mask & ~ FS_EVENT_ON_CHILD ;
2009-05-22 01:01:33 +04:00
2010-07-28 18:18:39 +04:00
BUG_ON ( vfsmount_mark ) ;
2009-05-22 01:01:33 +04:00
to_tell = event - > to_tell ;
2010-07-28 18:18:39 +04:00
dn_mark = container_of ( inode_mark , struct dnotify_mark , fsn_mark ) ;
2009-05-22 01:01:33 +04:00
2010-07-28 18:18:39 +04:00
spin_lock ( & inode_mark - > lock ) ;
2009-12-18 05:24:24 +03:00
prev = & dn_mark - > dn ;
2009-05-22 01:01:33 +04:00
while ( ( dn = * prev ) ! = NULL ) {
dnotify: ignore FS_EVENT_ON_CHILD
Mask off FS_EVENT_ON_CHILD in dnotify_handle_event(). Otherwise, when there
is more than one watch on a directory and dnotify_should_send_event()
succeeds, events with FS_EVENT_ON_CHILD set will trigger all watches and cause
spurious events.
This case was overlooked in commit e42e2773.
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
static void create_event(int s, siginfo_t* si, void* p)
{
printf("create\n");
}
static void delete_event(int s, siginfo_t* si, void* p)
{
printf("delete\n");
}
int main (void) {
struct sigaction action;
char *tmpdir, *file;
int fd1, fd2;
sigemptyset (&action.sa_mask);
action.sa_flags = SA_SIGINFO;
action.sa_sigaction = create_event;
sigaction (SIGRTMIN + 0, &action, NULL);
action.sa_sigaction = delete_event;
sigaction (SIGRTMIN + 1, &action, NULL);
# define TMPDIR "/tmp/test.XXXXXX"
tmpdir = malloc(strlen(TMPDIR) + 1);
strcpy(tmpdir, TMPDIR);
mkdtemp(tmpdir);
# define TMPFILE "/file"
file = malloc(strlen(tmpdir) + strlen(TMPFILE) + 1);
sprintf(file, "%s/%s", tmpdir, TMPFILE);
fd1 = open (tmpdir, O_RDONLY);
fcntl(fd1, F_SETSIG, SIGRTMIN);
fcntl(fd1, F_NOTIFY, DN_MULTISHOT | DN_CREATE);
fd2 = open (tmpdir, O_RDONLY);
fcntl(fd2, F_SETSIG, SIGRTMIN + 1);
fcntl(fd2, F_NOTIFY, DN_MULTISHOT | DN_DELETE);
if (fork()) {
/* This triggers a create event */
creat(file, 0600);
/* This triggers a create and delete event (!) */
unlink(file);
} else {
sleep(1);
rmdir(tmpdir);
}
return 0;
}
Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
Signed-off-by: Eric Paris <eparis@redhat.com>
2009-10-15 02:13:23 +04:00
if ( ( dn - > dn_mask & test_mask ) = = 0 ) {
2009-05-22 01:01:33 +04:00
prev = & dn - > dn_next ;
continue ;
}
fown = & dn - > dn_filp - > f_owner ;
send_sigio ( fown , dn - > dn_fd , POLL_MSG ) ;
if ( dn - > dn_mask & FS_DN_MULTISHOT )
prev = & dn - > dn_next ;
else {
* prev = dn - > dn_next ;
kmem_cache_free ( dnotify_struct_cache , dn ) ;
2010-07-28 18:18:39 +04:00
dnotify_recalc_inode_mask ( inode_mark ) ;
2009-05-22 01:01:33 +04:00
}
}
2010-07-28 18:18:39 +04:00
spin_unlock ( & inode_mark - > lock ) ;
2009-05-22 01:01:33 +04:00
return 0 ;
}
/*
* Given an inode and mask determine if dnotify would be interested in sending
* userspace notification for that pair .
*/
static bool dnotify_should_send_event ( struct fsnotify_group * group ,
2010-07-28 18:18:39 +04:00
struct inode * inode ,
2010-07-28 18:18:39 +04:00
struct fsnotify_mark * inode_mark ,
struct fsnotify_mark * vfsmount_mark ,
__u32 mask , void * data , int data_type )
2009-05-22 01:01:33 +04:00
{
/* not a dir, dnotify doesn't care */
if ( ! S_ISDIR ( inode - > i_mode ) )
return false ;
2010-07-28 18:18:39 +04:00
return true ;
2009-05-22 01:01:33 +04:00
}
2009-12-18 05:24:24 +03:00
static void dnotify_free_mark ( struct fsnotify_mark * fsn_mark )
2009-05-22 01:01:33 +04:00
{
2009-12-18 05:24:24 +03:00
struct dnotify_mark * dn_mark = container_of ( fsn_mark ,
struct dnotify_mark ,
fsn_mark ) ;
2009-05-22 01:01:33 +04:00
2009-12-18 05:24:24 +03:00
BUG_ON ( dn_mark - > dn ) ;
2009-05-22 01:01:33 +04:00
2009-12-18 05:24:24 +03:00
kmem_cache_free ( dnotify_mark_cache , dn_mark ) ;
2009-05-22 01:01:33 +04:00
}
static struct fsnotify_ops dnotify_fsnotify_ops = {
. handle_event = dnotify_handle_event ,
. should_send_event = dnotify_should_send_event ,
. free_group_priv = NULL ,
2009-06-11 19:09:48 +04:00
. freeing_mark = NULL ,
2009-05-22 01:01:50 +04:00
. free_event_priv = NULL ,
2009-05-22 01:01:33 +04:00
} ;
/*
* Called every time a file is closed . Looks first for a dnotify mark on the
2009-12-18 05:24:24 +03:00
* inode . If one is found run all of the - > dn structures attached to that
2009-05-22 01:01:33 +04:00
* mark for one relevant to this process closing the file and remove that
* dnotify_struct . If that was the last dnotify_struct also remove the
2009-12-18 05:24:24 +03:00
* fsnotify_mark .
2009-05-22 01:01:33 +04:00
*/
2005-04-17 02:20:36 +04:00
void dnotify_flush ( struct file * filp , fl_owner_t id )
{
2009-12-18 05:24:24 +03:00
struct fsnotify_mark * fsn_mark ;
struct dnotify_mark * dn_mark ;
2005-04-17 02:20:36 +04:00
struct dnotify_struct * dn ;
struct dnotify_struct * * prev ;
struct inode * inode ;
2006-12-08 13:36:35 +03:00
inode = filp - > f_path . dentry - > d_inode ;
2005-04-17 02:20:36 +04:00
if ( ! S_ISDIR ( inode - > i_mode ) )
return ;
2009-05-22 01:01:33 +04:00
2009-12-18 05:24:27 +03:00
fsn_mark = fsnotify_find_inode_mark ( dnotify_group , inode ) ;
2009-12-18 05:24:24 +03:00
if ( ! fsn_mark )
2009-05-22 01:01:33 +04:00
return ;
2009-12-18 05:24:24 +03:00
dn_mark = container_of ( fsn_mark , struct dnotify_mark , fsn_mark ) ;
2009-05-22 01:01:33 +04:00
mutex_lock ( & dnotify_mark_mutex ) ;
2009-12-18 05:24:24 +03:00
spin_lock ( & fsn_mark - > lock ) ;
prev = & dn_mark - > dn ;
2005-04-17 02:20:36 +04:00
while ( ( dn = * prev ) ! = NULL ) {
if ( ( dn - > dn_owner = = id ) & & ( dn - > dn_filp = = filp ) ) {
* prev = dn - > dn_next ;
2009-05-22 01:01:33 +04:00
kmem_cache_free ( dnotify_struct_cache , dn ) ;
2009-12-18 05:24:24 +03:00
dnotify_recalc_inode_mask ( fsn_mark ) ;
2005-04-17 02:20:36 +04:00
break ;
}
prev = & dn - > dn_next ;
}
2009-05-22 01:01:33 +04:00
2009-12-18 05:24:24 +03:00
spin_unlock ( & fsn_mark - > lock ) ;
2009-05-22 01:01:33 +04:00
/* nothing else could have found us thanks to the dnotify_mark_mutex */
2009-12-18 05:24:24 +03:00
if ( dn_mark - > dn = = NULL )
fsnotify_destroy_mark ( fsn_mark ) ;
2009-05-22 01:01:33 +04:00
mutex_unlock ( & dnotify_mark_mutex ) ;
2009-12-18 05:24:24 +03:00
fsnotify_put_mark ( fsn_mark ) ;
2009-05-22 01:01:33 +04:00
}
/* this conversion is done only at watch creation */
static __u32 convert_arg ( unsigned long arg )
{
__u32 new_mask = FS_EVENT_ON_CHILD ;
if ( arg & DN_MULTISHOT )
new_mask | = FS_DN_MULTISHOT ;
if ( arg & DN_DELETE )
new_mask | = ( FS_DELETE | FS_MOVED_FROM ) ;
if ( arg & DN_MODIFY )
new_mask | = FS_MODIFY ;
if ( arg & DN_ACCESS )
new_mask | = FS_ACCESS ;
if ( arg & DN_ATTRIB )
new_mask | = FS_ATTRIB ;
if ( arg & DN_RENAME )
new_mask | = FS_DN_RENAME ;
if ( arg & DN_CREATE )
new_mask | = ( FS_CREATE | FS_MOVED_TO ) ;
return new_mask ;
2005-04-17 02:20:36 +04:00
}
2009-05-22 01:01:33 +04:00
/*
* If multiple processes watch the same inode with dnotify there is only one
2009-12-18 05:24:24 +03:00
* dnotify mark in inode - > i_fsnotify_marks but we chain a dnotify_struct
2009-05-22 01:01:33 +04:00
* onto that mark . This function either attaches the new dnotify_struct onto
* that list , or it | = the mask onto an existing dnofiy_struct .
*/
2009-12-18 05:24:24 +03:00
static int attach_dn ( struct dnotify_struct * dn , struct dnotify_mark * dn_mark ,
2009-05-22 01:01:33 +04:00
fl_owner_t id , int fd , struct file * filp , __u32 mask )
{
struct dnotify_struct * odn ;
2009-12-18 05:24:24 +03:00
odn = dn_mark - > dn ;
2009-05-22 01:01:33 +04:00
while ( odn ! = NULL ) {
/* adding more events to existing dnofiy_struct? */
if ( ( odn - > dn_owner = = id ) & & ( odn - > dn_filp = = filp ) ) {
odn - > dn_fd = fd ;
odn - > dn_mask | = mask ;
return - EEXIST ;
}
odn = odn - > dn_next ;
}
dn - > dn_mask = mask ;
dn - > dn_fd = fd ;
dn - > dn_filp = filp ;
dn - > dn_owner = id ;
2009-12-18 05:24:24 +03:00
dn - > dn_next = dn_mark - > dn ;
dn_mark - > dn = dn ;
2009-05-22 01:01:33 +04:00
return 0 ;
}
/*
* When a process calls fcntl to attach a dnotify watch to a directory it ends
* up here . Allocate both a mark for fsnotify to add and a dnotify_struct to be
* attached to the fsnotify_mark .
*/
2005-04-17 02:20:36 +04:00
int fcntl_dirnotify ( int fd , struct file * filp , unsigned long arg )
{
2009-12-18 05:24:24 +03:00
struct dnotify_mark * new_dn_mark , * dn_mark ;
struct fsnotify_mark * new_fsn_mark , * fsn_mark ;
2005-04-17 02:20:36 +04:00
struct dnotify_struct * dn ;
struct inode * inode ;
fl_owner_t id = current - > files ;
2008-05-01 06:52:22 +04:00
struct file * f ;
2009-05-22 01:01:33 +04:00
int destroy = 0 , error = 0 ;
__u32 mask ;
/* we use these to tell if we need to kfree */
2009-12-18 05:24:24 +03:00
new_fsn_mark = NULL ;
2009-05-22 01:01:33 +04:00
dn = NULL ;
2005-04-17 02:20:36 +04:00
2009-05-22 01:01:33 +04:00
if ( ! dir_notify_enable ) {
error = - EINVAL ;
goto out_err ;
}
/* a 0 mask means we are explicitly removing the watch */
2005-04-17 02:20:36 +04:00
if ( ( arg & ~ DN_MULTISHOT ) = = 0 ) {
dnotify_flush ( filp , id ) ;
2009-05-22 01:01:33 +04:00
error = 0 ;
goto out_err ;
2005-04-17 02:20:36 +04:00
}
2009-05-22 01:01:33 +04:00
/* dnotify only works on directories */
2006-12-08 13:36:35 +03:00
inode = filp - > f_path . dentry - > d_inode ;
2009-05-22 01:01:33 +04:00
if ( ! S_ISDIR ( inode - > i_mode ) ) {
error = - ENOTDIR ;
goto out_err ;
2005-04-17 02:20:36 +04:00
}
2009-05-22 01:01:33 +04:00
/* expect most fcntl to add new rather than augment old */
dn = kmem_cache_alloc ( dnotify_struct_cache , GFP_KERNEL ) ;
if ( ! dn ) {
error = - ENOMEM ;
goto out_err ;
}
2008-05-01 06:52:22 +04:00
2009-05-22 01:01:33 +04:00
/* new fsnotify mark, we expect most fcntl calls to add a new mark */
2009-12-18 05:24:24 +03:00
new_dn_mark = kmem_cache_alloc ( dnotify_mark_cache , GFP_KERNEL ) ;
if ( ! new_dn_mark ) {
2009-05-22 01:01:33 +04:00
error = - ENOMEM ;
goto out_err ;
}
2005-04-17 02:20:36 +04:00
2009-05-22 01:01:33 +04:00
/* convert the userspace DN_* "arg" to the internal FS_* defines in fsnotify */
mask = convert_arg ( arg ) ;
2005-04-17 02:20:36 +04:00
2009-12-18 05:24:24 +03:00
/* set up the new_fsn_mark and new_dn_mark */
new_fsn_mark = & new_dn_mark - > fsn_mark ;
fsnotify_init_mark ( new_fsn_mark , dnotify_free_mark ) ;
new_fsn_mark - > mask = mask ;
new_dn_mark - > dn = NULL ;
2005-04-17 02:20:36 +04:00
2009-05-22 01:01:33 +04:00
/* this is needed to prevent the fcntl/close race described below */
mutex_lock ( & dnotify_mark_mutex ) ;
2005-04-17 02:20:36 +04:00
2009-12-18 05:24:24 +03:00
/* add the new_fsn_mark or find an old one. */
2009-12-18 05:24:27 +03:00
fsn_mark = fsnotify_find_inode_mark ( dnotify_group , inode ) ;
2009-12-18 05:24:24 +03:00
if ( fsn_mark ) {
dn_mark = container_of ( fsn_mark , struct dnotify_mark , fsn_mark ) ;
spin_lock ( & fsn_mark - > lock ) ;
2009-05-22 01:01:33 +04:00
} else {
2009-12-18 05:24:27 +03:00
fsnotify_add_mark ( new_fsn_mark , dnotify_group , inode , NULL , 0 ) ;
2009-12-18 05:24:24 +03:00
spin_lock ( & new_fsn_mark - > lock ) ;
fsn_mark = new_fsn_mark ;
dn_mark = new_dn_mark ;
/* we used new_fsn_mark, so don't free it */
new_fsn_mark = NULL ;
2009-05-22 01:01:33 +04:00
}
2005-04-17 02:20:36 +04:00
2009-05-22 01:01:33 +04:00
rcu_read_lock ( ) ;
f = fcheck ( fd ) ;
rcu_read_unlock ( ) ;
2005-04-17 02:20:36 +04:00
2009-05-22 01:01:33 +04:00
/* if (f != filp) means that we lost a race and another task/thread
* actually closed the fd we are still playing with before we grabbed
2009-12-18 05:24:24 +03:00
* the dnotify_mark_mutex and fsn_mark - > lock . Since closing the fd is the
2009-12-18 05:24:24 +03:00
* only time we clean up the marks we need to get our mark off
2009-05-22 01:01:33 +04:00
* the list . */
if ( f ! = filp ) {
/* if we added ourselves, shoot ourselves, it's possible that
2009-12-18 05:24:24 +03:00
* the flush actually did shoot this fsn_mark . That ' s fine too
2009-05-22 01:01:33 +04:00
* since multiple calls to destroy_mark is perfectly safe , if
2009-12-18 05:24:24 +03:00
* we found a dn_mark already attached to the inode , just sod
2009-05-22 01:01:33 +04:00
* off silently as the flush at close time dealt with it .
*/
2009-12-18 05:24:24 +03:00
if ( dn_mark = = new_dn_mark )
2009-05-22 01:01:33 +04:00
destroy = 1 ;
goto out ;
}
2005-04-17 02:20:36 +04:00
2009-05-22 01:01:33 +04:00
error = __f_setown ( filp , task_pid ( current ) , PIDTYPE_PID , 0 ) ;
if ( error ) {
/* if we added, we must shoot */
2009-12-18 05:24:24 +03:00
if ( dn_mark = = new_dn_mark )
2009-05-22 01:01:33 +04:00
destroy = 1 ;
goto out ;
2005-04-17 02:20:36 +04:00
}
2009-05-22 01:01:33 +04:00
2009-12-18 05:24:24 +03:00
error = attach_dn ( dn , dn_mark , id , fd , filp , mask ) ;
/* !error means that we attached the dn to the dn_mark, so don't free it */
2009-05-22 01:01:33 +04:00
if ( ! error )
dn = NULL ;
/* -EEXIST means that we didn't add this new dn and used an old one.
* that isn ' t an error ( and the unused dn should be freed ) */
else if ( error = = - EEXIST )
error = 0 ;
2009-12-18 05:24:24 +03:00
dnotify_recalc_inode_mask ( fsn_mark ) ;
2009-05-22 01:01:33 +04:00
out :
2009-12-18 05:24:24 +03:00
spin_unlock ( & fsn_mark - > lock ) ;
2009-05-22 01:01:33 +04:00
if ( destroy )
2009-12-18 05:24:24 +03:00
fsnotify_destroy_mark ( fsn_mark ) ;
2009-05-22 01:01:33 +04:00
mutex_unlock ( & dnotify_mark_mutex ) ;
2009-12-18 05:24:24 +03:00
fsnotify_put_mark ( fsn_mark ) ;
2009-05-22 01:01:33 +04:00
out_err :
2009-12-18 05:24:24 +03:00
if ( new_fsn_mark )
fsnotify_put_mark ( new_fsn_mark ) ;
2009-05-22 01:01:33 +04:00
if ( dn )
kmem_cache_free ( dnotify_struct_cache , dn ) ;
return error ;
2005-04-17 02:20:36 +04:00
}
static int __init dnotify_init ( void )
{
2009-05-22 01:01:33 +04:00
dnotify_struct_cache = KMEM_CACHE ( dnotify_struct , SLAB_PANIC ) ;
2009-12-18 05:24:24 +03:00
dnotify_mark_cache = KMEM_CACHE ( dnotify_mark , SLAB_PANIC ) ;
2009-05-22 01:01:33 +04:00
2009-12-18 05:24:22 +03:00
dnotify_group = fsnotify_alloc_group ( & dnotify_fsnotify_ops ) ;
2009-05-22 01:01:33 +04:00
if ( IS_ERR ( dnotify_group ) )
panic ( " unable to allocate fsnotify group for dnotify \n " ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
module_init ( dnotify_init )