/* * Copyright (C) 2005-2010 Junjiro R. Okajima * * This program, aufs 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 of the License, 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /* * fsnotify for the lower directories */ #include "aufs.h" /* FS_IN_IGNORED is unnecessary */ static const __u32 AuHfsnMask = (FS_MOVED_TO | FS_MOVED_FROM | FS_DELETE | FS_CREATE | FS_EVENT_ON_CHILD); static struct fsnotify_group *au_hfsn_group; static void au_hfsn_free_mark(struct fsnotify_mark *mark) { #if 0 struct au_hnotify *hn = container_of(mark, struct au_hnotify, hn_mark); au_cache_free_hnotify(hn); #endif AuDbg("here\n"); } static int au_hfsn_alloc(struct au_hnotify *hn, struct inode *h_inode) { struct fsnotify_mark *mark; mark = &hn->hn_mark; fsnotify_init_mark(mark, au_hfsn_free_mark); mark->mask = AuHfsnMask; return fsnotify_add_mark(mark, au_hfsn_group, h_inode, NULL, 1); } static void au_hfsn_free(struct au_hnotify *hn) { struct fsnotify_mark *mark; mark = &hn->hn_mark; fsnotify_destroy_mark(mark); fsnotify_put_mark(mark); } /* ---------------------------------------------------------------------- */ static void au_hfsn_ctl(struct au_hinode *hinode, int do_set) { struct fsnotify_mark *mark; mark = &hinode->hi_notify->hn_mark; spin_lock(&mark->lock); if (do_set) { AuDebugOn(mark->mask & AuHfsnMask); mark->mask |= AuHfsnMask; } else { AuDebugOn(!(mark->mask & AuHfsnMask)); mark->mask &= ~AuHfsnMask; } spin_unlock(&mark->lock); /* fsnotify_recalc_inode_mask(hinode->hi_inode); */ } /* ---------------------------------------------------------------------- */ /* #define AuDbgHnotify */ #ifdef AuDbgHnotify static char *au_hfsn_name(u32 mask) { #ifdef CONFIG_AUFS_DEBUG #define test_ret(flag) if (mask & flag) \ return #flag; test_ret(FS_ACCESS); test_ret(FS_MODIFY); test_ret(FS_ATTRIB); test_ret(FS_CLOSE_WRITE); test_ret(FS_CLOSE_NOWRITE); test_ret(FS_OPEN); test_ret(FS_MOVED_FROM); test_ret(FS_MOVED_TO); test_ret(FS_CREATE); test_ret(FS_DELETE); test_ret(FS_DELETE_SELF); test_ret(FS_MOVE_SELF); test_ret(FS_UNMOUNT); test_ret(FS_Q_OVERFLOW); test_ret(FS_IN_IGNORED); test_ret(FS_IN_ISDIR); test_ret(FS_IN_ONESHOT); test_ret(FS_EVENT_ON_CHILD); return ""; #undef test_ret #else return "??"; #endif } #endif /* ---------------------------------------------------------------------- */ static int au_hfsn_handle_event(struct fsnotify_group *group, struct fsnotify_mark *inode_mark, struct fsnotify_mark *vfsmount_mark, struct fsnotify_event *event) { int err; struct au_hnotify *hnotify; struct inode *h_dir, *h_inode; __u32 mask; struct qstr h_child_qstr = { .name = event->file_name, .len = event->name_len }; AuDebugOn(event->data_type != FSNOTIFY_EVENT_INODE); err = 0; /* if FS_UNMOUNT happens, there must be another bug */ mask = event->mask; AuDebugOn(mask & FS_UNMOUNT); if (mask & (FS_IN_IGNORED | FS_UNMOUNT)) goto out; h_dir = event->to_tell; h_inode = event->inode; #ifdef AuDbgHnotify au_debug(1); if (1 || h_child_qstr.len != sizeof(AUFS_XINO_FNAME) - 1 || strncmp(h_child_qstr.name, AUFS_XINO_FNAME, h_child_qstr.len)) { AuDbg("i%lu, mask 0x%x %s, hcname %.*s, hi%lu\n", h_dir->i_ino, mask, au_hfsn_name(mask), AuLNPair(&h_child_qstr), h_inode ? h_inode->i_ino : 0); /* WARN_ON(1); */ } au_debug(0); #endif AuDebugOn(!inode_mark); hnotify = container_of(inode_mark, struct au_hnotify, hn_mark); err = au_hnotify(h_dir, hnotify, mask, &h_child_qstr, h_inode); fsnotify_put_mark(inode_mark); out: return err; } /* copied from linux/fs/notify/inotify/inotify_fsnotify.c */ /* it should be exported to modules */ static bool au_hfsn_should_send_event(struct fsnotify_group *group, struct inode *inode, struct fsnotify_mark *inode_mark, struct fsnotify_mark *vfsmount_mark, __u32 mask, void *data, int data_type) { bool send; mask = (mask & ~FS_EVENT_ON_CHILD); send = (inode_mark->mask & mask); /* find took a reference */ fsnotify_put_mark(inode_mark); return send; } static struct fsnotify_ops au_hfsn_ops = { .should_send_event = au_hfsn_should_send_event, .handle_event = au_hfsn_handle_event }; /* ---------------------------------------------------------------------- */ static int __init au_hfsn_init(void) { int err; err = 0; au_hfsn_group = fsnotify_alloc_group(&au_hfsn_ops); if (IS_ERR(au_hfsn_group)) { err = PTR_ERR(au_hfsn_group); pr_err("fsnotify_alloc_group() failed, %d\n", err); } AuTraceErr(err); return err; } static void au_hfsn_fin(void) { fsnotify_put_group(au_hfsn_group); } const struct au_hnotify_op au_hnotify_op = { .ctl = au_hfsn_ctl, .alloc = au_hfsn_alloc, .free = au_hfsn_free, .fin = au_hfsn_fin, .init = au_hfsn_init };