]> Pileus Git - ~andy/linux/commitdiff
Merge branch 'audit.b21' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/audit...
authorLinus Torvalds <torvalds@g5.osdl.org>
Tue, 20 Jun 2006 22:37:56 +0000 (15:37 -0700)
committerLinus Torvalds <torvalds@g5.osdl.org>
Tue, 20 Jun 2006 22:37:56 +0000 (15:37 -0700)
* 'audit.b21' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/audit-current: (25 commits)
  [PATCH] make set_loginuid obey audit_enabled
  [PATCH] log more info for directory entry change events
  [PATCH] fix AUDIT_FILTER_PREPEND handling
  [PATCH] validate rule fields' types
  [PATCH] audit: path-based rules
  [PATCH] Audit of POSIX Message Queue Syscalls v.2
  [PATCH] fix se_sen audit filter
  [PATCH] deprecate AUDIT_POSSBILE
  [PATCH] inline more audit helpers
  [PATCH] proc_loginuid_write() uses simple_strtoul() on non-terminated array
  [PATCH] update of IPC audit record cleanup
  [PATCH] minor audit updates
  [PATCH] fix audit_krule_to_{rule,data} return values
  [PATCH] add filtering by ppid
  [PATCH] log ppid
  [PATCH] collect sid of those who send signals to auditd
  [PATCH] execve argument logging
  [PATCH] fix deadlocks in AUDIT_LIST/AUDIT_LIST_RULES
  [PATCH] audit_panic() is audit-internal
  [PATCH] inotify (5/5): update kernel documentation
  ...

Manual fixup of conflict in unclude/linux/inotify.h

27 files changed:
Documentation/filesystems/inotify.txt
fs/Kconfig
fs/Makefile
fs/exec.c
fs/inotify.c
fs/inotify_user.c [new file with mode: 0644]
fs/namei.c
fs/open.c
fs/proc/base.c
fs/xattr.c
include/linux/audit.h
include/linux/fsnotify.h
include/linux/inotify.h
include/linux/sched.h
init/Kconfig
ipc/mqueue.c
ipc/msg.c
ipc/sem.c
ipc/shm.c
kernel/audit.c
kernel/audit.h
kernel/auditfilter.c
kernel/auditsc.c
kernel/signal.c
kernel/sysctl.c
kernel/user.c
security/selinux/ss/services.c

index 6d501903f68ed823c12fc788c5f2be6e3d556338..59a919f16144df84dc6e3934dcdc22c26611a6ea 100644 (file)
@@ -69,17 +69,135 @@ Prototypes:
        int inotify_rm_watch (int fd, __u32 mask);
 
 
-(iii) Internal Kernel Implementation
+(iii) Kernel Interface
 
-Each inotify instance is associated with an inotify_device structure.
+Inotify's kernel API consists a set of functions for managing watches and an
+event callback.
+
+To use the kernel API, you must first initialize an inotify instance with a set
+of inotify_operations.  You are given an opaque inotify_handle, which you use
+for any further calls to inotify.
+
+    struct inotify_handle *ih = inotify_init(my_event_handler);
+
+You must provide a function for processing events and a function for destroying
+the inotify watch.
+
+    void handle_event(struct inotify_watch *watch, u32 wd, u32 mask,
+                     u32 cookie, const char *name, struct inode *inode)
+
+       watch - the pointer to the inotify_watch that triggered this call
+       wd - the watch descriptor
+       mask - describes the event that occurred
+       cookie - an identifier for synchronizing events
+       name - the dentry name for affected files in a directory-based event
+       inode - the affected inode in a directory-based event
+
+    void destroy_watch(struct inotify_watch *watch)
+
+You may add watches by providing a pre-allocated and initialized inotify_watch
+structure and specifying the inode to watch along with an inotify event mask.
+You must pin the inode during the call.  You will likely wish to embed the
+inotify_watch structure in a structure of your own which contains other
+information about the watch.  Once you add an inotify watch, it is immediately
+subject to removal depending on filesystem events.  You must grab a reference if
+you depend on the watch hanging around after the call.
+
+    inotify_init_watch(&my_watch->iwatch);
+    inotify_get_watch(&my_watch->iwatch);      // optional
+    s32 wd = inotify_add_watch(ih, &my_watch->iwatch, inode, mask);
+    inotify_put_watch(&my_watch->iwatch);      // optional
+
+You may use the watch descriptor (wd) or the address of the inotify_watch for
+other inotify operations.  You must not directly read or manipulate data in the
+inotify_watch.  Additionally, you must not call inotify_add_watch() more than
+once for a given inotify_watch structure, unless you have first called either
+inotify_rm_watch() or inotify_rm_wd().
+
+To determine if you have already registered a watch for a given inode, you may
+call inotify_find_watch(), which gives you both the wd and the watch pointer for
+the inotify_watch, or an error if the watch does not exist.
+
+    wd = inotify_find_watch(ih, inode, &watchp);
+
+You may use container_of() on the watch pointer to access your own data
+associated with a given watch.  When an existing watch is found,
+inotify_find_watch() bumps the refcount before releasing its locks.  You must
+put that reference with:
+
+    put_inotify_watch(watchp);
+
+Call inotify_find_update_watch() to update the event mask for an existing watch.
+inotify_find_update_watch() returns the wd of the updated watch, or an error if
+the watch does not exist.
+
+    wd = inotify_find_update_watch(ih, inode, mask);
+
+An existing watch may be removed by calling either inotify_rm_watch() or
+inotify_rm_wd().
+
+    int ret = inotify_rm_watch(ih, &my_watch->iwatch);
+    int ret = inotify_rm_wd(ih, wd);
+
+A watch may be removed while executing your event handler with the following:
+
+    inotify_remove_watch_locked(ih, iwatch);
+
+Call inotify_destroy() to remove all watches from your inotify instance and
+release it.  If there are no outstanding references, inotify_destroy() will call
+your destroy_watch op for each watch.
+
+    inotify_destroy(ih);
+
+When inotify removes a watch, it sends an IN_IGNORED event to your callback.
+You may use this event as an indication to free the watch memory.  Note that
+inotify may remove a watch due to filesystem events, as well as by your request.
+If you use IN_ONESHOT, inotify will remove the watch after the first event, at
+which point you may call the final inotify_put_watch.
+
+(iv) Kernel Interface Prototypes
+
+       struct inotify_handle *inotify_init(struct inotify_operations *ops);
+
+       inotify_init_watch(struct inotify_watch *watch);
+
+       s32 inotify_add_watch(struct inotify_handle *ih,
+                             struct inotify_watch *watch,
+                             struct inode *inode, u32 mask);
+
+       s32 inotify_find_watch(struct inotify_handle *ih, struct inode *inode,
+                              struct inotify_watch **watchp);
+
+       s32 inotify_find_update_watch(struct inotify_handle *ih,
+                                     struct inode *inode, u32 mask);
+
+       int inotify_rm_wd(struct inotify_handle *ih, u32 wd);
+
+       int inotify_rm_watch(struct inotify_handle *ih,
+                            struct inotify_watch *watch);
+
+       void inotify_remove_watch_locked(struct inotify_handle *ih,
+                                        struct inotify_watch *watch);
+
+       void inotify_destroy(struct inotify_handle *ih);
+
+       void get_inotify_watch(struct inotify_watch *watch);
+       void put_inotify_watch(struct inotify_watch *watch);
+
+
+(v) Internal Kernel Implementation
+
+Each inotify instance is represented by an inotify_handle structure.
+Inotify's userspace consumers also have an inotify_device which is
+associated with the inotify_handle, and on which events are queued.
 
 Each watch is associated with an inotify_watch structure.  Watches are chained
-off of each associated device and each associated inode.
+off of each associated inotify_handle and each associated inode.
 
-See fs/inotify.c for the locking and lifetime rules.
+See fs/inotify.c and fs/inotify_user.c for the locking and lifetime rules.
 
 
-(iv) Rationale
+(vi) Rationale
 
 Q: What is the design decision behind not tying the watch to the open fd of
    the watched object?
@@ -145,7 +263,7 @@ A: The poor user-space interface is the second biggest problem with dnotify.
    file descriptor-based one that allows basic file I/O and poll/select.
    Obtaining the fd and managing the watches could have been done either via a
    device file or a family of new system calls.  We decided to implement a
-   family of system calls because that is the preffered approach for new kernel
+   family of system calls because that is the preferred approach for new kernel
    interfaces.  The only real difference was whether we wanted to use open(2)
    and ioctl(2) or a couple of new system calls.  System calls beat ioctls.
 
index 572cc435a1bb590a12e71975c2eecb4a98d4009b..20f9b557732d2c9faf031d275878d13602131922 100644 (file)
@@ -393,18 +393,30 @@ config INOTIFY
        bool "Inotify file change notification support"
        default y
        ---help---
-         Say Y here to enable inotify support and the associated system
-         calls.  Inotify is a file change notification system and a
-         replacement for dnotify.  Inotify fixes numerous shortcomings in
-         dnotify and introduces several new features.  It allows monitoring
-         of both files and directories via a single open fd.  Other features
-         include multiple file events, one-shot support, and unmount
+         Say Y here to enable inotify support.  Inotify is a file change
+         notification system and a replacement for dnotify.  Inotify fixes
+         numerous shortcomings in dnotify and introduces several new features
+         including multiple file events, one-shot support, and unmount
          notification.
 
          For more information, see Documentation/filesystems/inotify.txt
 
          If unsure, say Y.
 
+config INOTIFY_USER
+       bool "Inotify support for userspace"
+       depends on INOTIFY
+       default y
+       ---help---
+         Say Y here to enable inotify support for userspace, including the
+         associated system calls.  Inotify allows monitoring of both files and
+         directories via a single open fd.  Events are read from the file
+         descriptor, which is also select()- and poll()-able.
+
+         For more information, see Documentation/filesystems/inotify.txt
+
+         If unsure, say Y.
+
 config QUOTA
        bool "Quota support"
        help
index 078d3d1191a511f3ad88ac0167142fda8c10c5a9..d0ea6bfccf29a6071144919b44e3b31f064ed879 100644 (file)
@@ -13,6 +13,7 @@ obj-y :=      open.o read_write.o file_table.o buffer.o  bio.o super.o \
                ioprio.o pnode.o drop_caches.o splice.o sync.o
 
 obj-$(CONFIG_INOTIFY)          += inotify.o
+obj-$(CONFIG_INOTIFY_USER)     += inotify_user.o
 obj-$(CONFIG_EPOLL)            += eventpoll.o
 obj-$(CONFIG_COMPAT)           += compat.o compat_ioctl.o
 
index 3a79d97ac234422c353ad6c37ab973d3363471bd..d07858c0b7c4e3885c2d6b31521f3e5668b94007 100644 (file)
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -49,6 +49,7 @@
 #include <linux/rmap.h>
 #include <linux/acct.h>
 #include <linux/cn_proc.h>
+#include <linux/audit.h>
 
 #include <asm/uaccess.h>
 #include <asm/mmu_context.h>
@@ -1085,6 +1086,11 @@ int search_binary_handler(struct linux_binprm *bprm,struct pt_regs *regs)
        /* kernel module loader fixup */
        /* so we don't try to load run modprobe in kernel space. */
        set_fs(USER_DS);
+
+       retval = audit_bprm(bprm);
+       if (retval)
+               return retval;
+
        retval = -ENOENT;
        for (try=0; try<2; try++) {
                read_lock(&binfmt_lock);
index 732ec4bd5774507f8c06c6e05cc90d4d2004a84b..723836a1f71896a5406b7ca6e066dc42ca5a6286 100644 (file)
@@ -5,7 +5,10 @@
  *     John McCutchan  <ttb@tentacle.dhs.org>
  *     Robert Love     <rml@novell.com>
  *
+ * Kernel API added by: Amy Griffis <amy.griffis@hp.com>
+ *
  * Copyright (C) 2005 John McCutchan
+ * Copyright 2006 Hewlett-Packard Development Company, L.P.
  *
  * 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
 
 #include <linux/module.h>
 #include <linux/kernel.h>
-#include <linux/sched.h>
 #include <linux/spinlock.h>
 #include <linux/idr.h>
 #include <linux/slab.h>
 #include <linux/fs.h>
-#include <linux/file.h>
-#include <linux/mount.h>
-#include <linux/namei.h>
-#include <linux/poll.h>
 #include <linux/init.h>
 #include <linux/list.h>
 #include <linux/writeback.h>
 #include <linux/inotify.h>
-#include <linux/syscalls.h>
-
-#include <asm/ioctls.h>
 
 static atomic_t inotify_cookie;
 
-static kmem_cache_t *watch_cachep __read_mostly;
-static kmem_cache_t *event_cachep __read_mostly;
-
-static struct vfsmount *inotify_mnt __read_mostly;
-
-/* these are configurable via /proc/sys/fs/inotify/ */
-int inotify_max_user_instances __read_mostly;
-int inotify_max_user_watches __read_mostly;
-int inotify_max_queued_events __read_mostly;
-
 /*
  * Lock ordering:
  *
@@ -56,327 +41,108 @@ int inotify_max_queued_events __read_mostly;
  * iprune_mutex (synchronize shrink_icache_memory())
  *     inode_lock (protects the super_block->s_inodes list)
  *     inode->inotify_mutex (protects inode->inotify_watches and watches->i_list)
- *             inotify_dev->mutex (protects inotify_device and watches->d_list)
+ *             inotify_handle->mutex (protects inotify_handle and watches->h_list)
+ *
+ * The inode->inotify_mutex and inotify_handle->mutex and held during execution
+ * of a caller's event handler.  Thus, the caller must not hold any locks
+ * taken in their event handler while calling any of the published inotify
+ * interfaces.
  */
 
 /*
- * Lifetimes of the three main data structures--inotify_device, inode, and
+ * Lifetimes of the three main data structures--inotify_handle, inode, and
  * inotify_watch--are managed by reference count.
  *
- * inotify_device: Lifetime is from inotify_init() until release.  Additional
- * references can bump the count via get_inotify_dev() and drop the count via
- * put_inotify_dev().
+ * inotify_handle: Lifetime is from inotify_init() to inotify_destroy().
+ * Additional references can bump the count via get_inotify_handle() and drop
+ * the count via put_inotify_handle().
  *
- * inotify_watch: Lifetime is from create_watch() to destory_watch().
- * Additional references can bump the count via get_inotify_watch() and drop
- * the count via put_inotify_watch().
+ * inotify_watch: for inotify's purposes, lifetime is from inotify_add_watch()
+ * to remove_watch_no_event().  Additional references can bump the count via
+ * get_inotify_watch() and drop the count via put_inotify_watch().  The caller
+ * is reponsible for the final put after receiving IN_IGNORED, or when using
+ * IN_ONESHOT after receiving the first event.  Inotify does the final put if
+ * inotify_destroy() is called.
  *
  * inode: Pinned so long as the inode is associated with a watch, from
- * create_watch() to put_inotify_watch().
+ * inotify_add_watch() to the final put_inotify_watch().
  */
 
 /*
- * struct inotify_device - represents an inotify instance
+ * struct inotify_handle - represents an inotify instance
  *
  * This structure is protected by the mutex 'mutex'.
  */
-struct inotify_device {
-       wait_queue_head_t       wq;             /* wait queue for i/o */
+struct inotify_handle {
        struct idr              idr;            /* idr mapping wd -> watch */
        struct mutex            mutex;          /* protects this bad boy */
-       struct list_head        events;         /* list of queued events */
        struct list_head        watches;        /* list of watches */
        atomic_t                count;          /* reference count */
-       struct user_struct      *user;          /* user who opened this dev */
-       unsigned int            queue_size;     /* size of the queue (bytes) */
-       unsigned int            event_count;    /* number of pending events */
-       unsigned int            max_events;     /* maximum number of events */
        u32                     last_wd;        /* the last wd allocated */
+       const struct inotify_operations *in_ops; /* inotify caller operations */
 };
 
-/*
- * struct inotify_kernel_event - An inotify event, originating from a watch and
- * queued for user-space.  A list of these is attached to each instance of the
- * device.  In read(), this list is walked and all events that can fit in the
- * buffer are returned.
- *
- * Protected by dev->mutex of the device in which we are queued.
- */
-struct inotify_kernel_event {
-       struct inotify_event    event;  /* the user-space event */
-       struct list_head        list;   /* entry in inotify_device's list */
-       char                    *name;  /* filename, if any */
-};
-
-/*
- * struct inotify_watch - represents a watch request on a specific inode
- *
- * d_list is protected by dev->mutex of the associated watch->dev.
- * i_list and mask are protected by inode->inotify_mutex of the associated inode.
- * dev, inode, and wd are never written to once the watch is created.
- */
-struct inotify_watch {
-       struct list_head        d_list; /* entry in inotify_device's list */
-       struct list_head        i_list; /* entry in inode's list */
-       atomic_t                count;  /* reference count */
-       struct inotify_device   *dev;   /* associated device */
-       struct inode            *inode; /* associated inode */
-       s32                     wd;     /* watch descriptor */
-       u32                     mask;   /* event mask for this watch */
-};
-
-#ifdef CONFIG_SYSCTL
-
-#include <linux/sysctl.h>
-
-static int zero;
-
-ctl_table inotify_table[] = {
-       {
-               .ctl_name       = INOTIFY_MAX_USER_INSTANCES,
-               .procname       = "max_user_instances",
-               .data           = &inotify_max_user_instances,
-               .maxlen         = sizeof(int),
-               .mode           = 0644,
-               .proc_handler   = &proc_dointvec_minmax,
-               .strategy       = &sysctl_intvec,
-               .extra1         = &zero,
-       },
-       {
-               .ctl_name       = INOTIFY_MAX_USER_WATCHES,
-               .procname       = "max_user_watches",
-               .data           = &inotify_max_user_watches,
-               .maxlen         = sizeof(int),
-               .mode           = 0644,
-               .proc_handler   = &proc_dointvec_minmax,
-               .strategy       = &sysctl_intvec,
-               .extra1         = &zero, 
-       },
-       {
-               .ctl_name       = INOTIFY_MAX_QUEUED_EVENTS,
-               .procname       = "max_queued_events",
-               .data           = &inotify_max_queued_events,
-               .maxlen         = sizeof(int),
-               .mode           = 0644, 
-               .proc_handler   = &proc_dointvec_minmax,
-               .strategy       = &sysctl_intvec, 
-               .extra1         = &zero
-       },
-       { .ctl_name = 0 }
-};
-#endif /* CONFIG_SYSCTL */
-
-static inline void get_inotify_dev(struct inotify_device *dev)
+static inline void get_inotify_handle(struct inotify_handle *ih)
 {
-       atomic_inc(&dev->count);
+       atomic_inc(&ih->count);
 }
 
-static inline void put_inotify_dev(struct inotify_device *dev)
+static inline void put_inotify_handle(struct inotify_handle *ih)
 {
-       if (atomic_dec_and_test(&dev->count)) {
-               atomic_dec(&dev->user->inotify_devs);
-               free_uid(dev->user);
-               idr_destroy(&dev->idr);
-               kfree(dev);
+       if (atomic_dec_and_test(&ih->count)) {
+               idr_destroy(&ih->idr);
+               kfree(ih);
        }
 }
 
-static inline void get_inotify_watch(struct inotify_watch *watch)
+/**
+ * get_inotify_watch - grab a reference to an inotify_watch
+ * @watch: watch to grab
+ */
+void get_inotify_watch(struct inotify_watch *watch)
 {
        atomic_inc(&watch->count);
 }
+EXPORT_SYMBOL_GPL(get_inotify_watch);
 
-/*
+/**
  * put_inotify_watch - decrements the ref count on a given watch.  cleans up
- * the watch and its references if the count reaches zero.
+ * watch references if the count reaches zero.  inotify_watch is freed by
+ * inotify callers via the destroy_watch() op.
+ * @watch: watch to release
  */
-static inline void put_inotify_watch(struct inotify_watch *watch)
+void put_inotify_watch(struct inotify_watch *watch)
 {
        if (atomic_dec_and_test(&watch->count)) {
-               put_inotify_dev(watch->dev);
-               iput(watch->inode);
-               kmem_cache_free(watch_cachep, watch);
-       }
-}
-
-/*
- * kernel_event - create a new kernel event with the given parameters
- *
- * This function can sleep.
- */
-static struct inotify_kernel_event * kernel_event(s32 wd, u32 mask, u32 cookie,
-                                                 const char *name)
-{
-       struct inotify_kernel_event *kevent;
-
-       kevent = kmem_cache_alloc(event_cachep, GFP_KERNEL);
-       if (unlikely(!kevent))
-               return NULL;
-
-       /* we hand this out to user-space, so zero it just in case */
-       memset(&kevent->event, 0, sizeof(struct inotify_event));
-
-       kevent->event.wd = wd;
-       kevent->event.mask = mask;
-       kevent->event.cookie = cookie;
-
-       INIT_LIST_HEAD(&kevent->list);
-
-       if (name) {
-               size_t len, rem, event_size = sizeof(struct inotify_event);
-
-               /*
-                * We need to pad the filename so as to properly align an
-                * array of inotify_event structures.  Because the structure is
-                * small and the common case is a small filename, we just round
-                * up to the next multiple of the structure's sizeof.  This is
-                * simple and safe for all architectures.
-                */
-               len = strlen(name) + 1;
-               rem = event_size - len;
-               if (len > event_size) {
-                       rem = event_size - (len % event_size);
-                       if (len % event_size == 0)
-                               rem = 0;
-               }
-
-               kevent->name = kmalloc(len + rem, GFP_KERNEL);
-               if (unlikely(!kevent->name)) {
-                       kmem_cache_free(event_cachep, kevent);
-                       return NULL;
-               }
-               memcpy(kevent->name, name, len);
-               if (rem)
-                       memset(kevent->name + len, 0, rem);             
-               kevent->event.len = len + rem;
-       } else {
-               kevent->event.len = 0;
-               kevent->name = NULL;
-       }
-
-       return kevent;
-}
-
-/*
- * inotify_dev_get_event - return the next event in the given dev's queue
- *
- * Caller must hold dev->mutex.
- */
-static inline struct inotify_kernel_event *
-inotify_dev_get_event(struct inotify_device *dev)
-{
-       return list_entry(dev->events.next, struct inotify_kernel_event, list);
-}
-
-/*
- * inotify_dev_queue_event - add a new event to the given device
- *
- * Caller must hold dev->mutex.  Can sleep (calls kernel_event()).
- */
-static void inotify_dev_queue_event(struct inotify_device *dev,
-                                   struct inotify_watch *watch, u32 mask,
-                                   u32 cookie, const char *name)
-{
-       struct inotify_kernel_event *kevent, *last;
-
-       /* coalescing: drop this event if it is a dupe of the previous */
-       last = inotify_dev_get_event(dev);
-       if (last && last->event.mask == mask && last->event.wd == watch->wd &&
-                       last->event.cookie == cookie) {
-               const char *lastname = last->name;
-
-               if (!name && !lastname)
-                       return;
-               if (name && lastname && !strcmp(lastname, name))
-                       return;
-       }
-
-       /* the queue overflowed and we already sent the Q_OVERFLOW event */
-       if (unlikely(dev->event_count > dev->max_events))
-               return;
-
-       /* if the queue overflows, we need to notify user space */
-       if (unlikely(dev->event_count == dev->max_events))
-               kevent = kernel_event(-1, IN_Q_OVERFLOW, cookie, NULL);
-       else
-               kevent = kernel_event(watch->wd, mask, cookie, name);
-
-       if (unlikely(!kevent))
-               return;
-
-       /* queue the event and wake up anyone waiting */
-       dev->event_count++;
-       dev->queue_size += sizeof(struct inotify_event) + kevent->event.len;
-       list_add_tail(&kevent->list, &dev->events);
-       wake_up_interruptible(&dev->wq);
-}
-
-/*
- * remove_kevent - cleans up and ultimately frees the given kevent
- *
- * Caller must hold dev->mutex.
- */
-static void remove_kevent(struct inotify_device *dev,
-                         struct inotify_kernel_event *kevent)
-{
-       list_del(&kevent->list);
-
-       dev->event_count--;
-       dev->queue_size -= sizeof(struct inotify_event) + kevent->event.len;
-
-       kfree(kevent->name);
-       kmem_cache_free(event_cachep, kevent);
-}
+               struct inotify_handle *ih = watch->ih;
 
-/*
- * inotify_dev_event_dequeue - destroy an event on the given device
- *
- * Caller must hold dev->mutex.
- */
-static void inotify_dev_event_dequeue(struct inotify_device *dev)
-{
-       if (!list_empty(&dev->events)) {
-               struct inotify_kernel_event *kevent;
-               kevent = inotify_dev_get_event(dev);
-               remove_kevent(dev, kevent);
+               iput(watch->inode);
+               ih->in_ops->destroy_watch(watch);
+               put_inotify_handle(ih);
        }
 }
+EXPORT_SYMBOL_GPL(put_inotify_watch);
 
 /*
- * inotify_dev_get_wd - returns the next WD for use by the given dev
+ * inotify_handle_get_wd - returns the next WD for use by the given handle
  *
- * Callers must hold dev->mutex.  This function can sleep.
+ * Callers must hold ih->mutex.  This function can sleep.
  */
-static int inotify_dev_get_wd(struct inotify_device *dev,
-                             struct inotify_watch *watch)
+static int inotify_handle_get_wd(struct inotify_handle *ih,
+                                struct inotify_watch *watch)
 {
        int ret;
 
        do {
-               if (unlikely(!idr_pre_get(&dev->idr, GFP_KERNEL)))
+               if (unlikely(!idr_pre_get(&ih->idr, GFP_KERNEL)))
                        return -ENOSPC;
-               ret = idr_get_new_above(&dev->idr, watch, dev->last_wd+1, &watch->wd);
+               ret = idr_get_new_above(&ih->idr, watch, ih->last_wd+1, &watch->wd);
        } while (ret == -EAGAIN);
 
-       return ret;
-}
+       if (likely(!ret))
+               ih->last_wd = watch->wd;
 
-/*
- * find_inode - resolve a user-given path to a specific inode and return a nd
- */
-static int find_inode(const char __user *dirname, struct nameidata *nd,
-                     unsigned flags)
-{
-       int error;
-
-       error = __user_walk(dirname, flags, nd);
-       if (error)
-               return error;
-       /* you can only watch an inode if you have read permissions on it */
-       error = vfs_permission(nd, MAY_READ);
-       if (error) 
-               path_release(nd);
-       return error;
+       return ret;
 }
 
 /*
@@ -422,67 +188,18 @@ static void set_dentry_child_flags(struct inode *inode, int watched)
 }
 
 /*
- * create_watch - creates a watch on the given device.
- *
- * Callers must hold dev->mutex.  Calls inotify_dev_get_wd() so may sleep.
- * Both 'dev' and 'inode' (by way of nameidata) need to be pinned.
- */
-static struct inotify_watch *create_watch(struct inotify_device *dev,
-                                         u32 mask, struct inode *inode)
-{
-       struct inotify_watch *watch;
-       int ret;
-
-       if (atomic_read(&dev->user->inotify_watches) >=
-                       inotify_max_user_watches)
-               return ERR_PTR(-ENOSPC);
-
-       watch = kmem_cache_alloc(watch_cachep, GFP_KERNEL);
-       if (unlikely(!watch))
-               return ERR_PTR(-ENOMEM);
-
-       ret = inotify_dev_get_wd(dev, watch);
-       if (unlikely(ret)) {
-               kmem_cache_free(watch_cachep, watch);
-               return ERR_PTR(ret);
-       }
-
-       dev->last_wd = watch->wd;
-       watch->mask = mask;
-       atomic_set(&watch->count, 0);
-       INIT_LIST_HEAD(&watch->d_list);
-       INIT_LIST_HEAD(&watch->i_list);
-
-       /* save a reference to device and bump the count to make it official */
-       get_inotify_dev(dev);
-       watch->dev = dev;
-
-       /*
-        * Save a reference to the inode and bump the ref count to make it
-        * official.  We hold a reference to nameidata, which makes this safe.
-        */
-       watch->inode = igrab(inode);
-
-       /* bump our own count, corresponding to our entry in dev->watches */
-       get_inotify_watch(watch);
-
-       atomic_inc(&dev->user->inotify_watches);
-
-       return watch;
-}
-
-/*
- * inotify_find_dev - find the watch associated with the given inode and dev
+ * inotify_find_handle - find the watch associated with the given inode and
+ * handle
  *
  * Callers must hold inode->inotify_mutex.
  */
-static struct inotify_watch *inode_find_dev(struct inode *inode,
-                                           struct inotify_device *dev)
+static struct inotify_watch *inode_find_handle(struct inode *inode,
+                                              struct inotify_handle *ih)
 {
        struct inotify_watch *watch;
 
        list_for_each_entry(watch, &inode->inotify_watches, i_list) {
-               if (watch->dev == dev)
+               if (watch->ih == ih)
                        return watch;
        }
 
@@ -490,40 +207,40 @@ static struct inotify_watch *inode_find_dev(struct inode *inode,
 }
 
 /*
- * remove_watch_no_event - remove_watch() without the IN_IGNORED event.
+ * remove_watch_no_event - remove watch without the IN_IGNORED event.
+ *
+ * Callers must hold both inode->inotify_mutex and ih->mutex.
  */
 static void remove_watch_no_event(struct inotify_watch *watch,
-                                 struct inotify_device *dev)
+                                 struct inotify_handle *ih)
 {
        list_del(&watch->i_list);
-       list_del(&watch->d_list);
+       list_del(&watch->h_list);
 
        if (!inotify_inode_watched(watch->inode))
                set_dentry_child_flags(watch->inode, 0);
 
-       atomic_dec(&dev->user->inotify_watches);
-       idr_remove(&dev->idr, watch->wd);
-       put_inotify_watch(watch);
+       idr_remove(&ih->idr, watch->wd);
 }
 
-/*
- * remove_watch - Remove a watch from both the device and the inode.  Sends
- * the IN_IGNORED event to the given device signifying that the inode is no
- * longer watched.
- *
- * Callers must hold both inode->inotify_mutex and dev->mutex.  We drop a
- * reference to the inode before returning.
+/**
+ * inotify_remove_watch_locked - Remove a watch from both the handle and the
+ * inode.  Sends the IN_IGNORED event signifying that the inode is no longer
+ * watched.  May be invoked from a caller's event handler.
+ * @ih: inotify handle associated with watch
+ * @watch: watch to remove
  *
- * The inode is not iput() so as to remain atomic.  If the inode needs to be
- * iput(), the call returns one.  Otherwise, it returns zero.
+ * Callers must hold both inode->inotify_mutex and ih->mutex.
  */
-static void remove_watch(struct inotify_watch *watch,struct inotify_device *dev)
+void inotify_remove_watch_locked(struct inotify_handle *ih,
+                                struct inotify_watch *watch)
 {
-       inotify_dev_queue_event(dev, watch, IN_IGNORED, 0, NULL);
-       remove_watch_no_event(watch, dev);
+       remove_watch_no_event(watch, ih);
+       ih->in_ops->handle_event(watch, watch->wd, IN_IGNORED, 0, NULL, NULL);
 }
+EXPORT_SYMBOL_GPL(inotify_remove_watch_locked);
 
-/* Kernel API */
+/* Kernel API for producing events */
 
 /*
  * inotify_d_instantiate - instantiate dcache entry for inode
@@ -563,9 +280,10 @@ void inotify_d_move(struct dentry *entry)
  * @mask: event mask describing this event
  * @cookie: cookie for synchronization, or zero
  * @name: filename, if any
+ * @n_inode: inode associated with name
  */
 void inotify_inode_queue_event(struct inode *inode, u32 mask, u32 cookie,
-                              const char *name)
+                              const char *name, struct inode *n_inode)
 {
        struct inotify_watch *watch, *next;
 
@@ -576,14 +294,13 @@ void inotify_inode_queue_event(struct inode *inode, u32 mask, u32 cookie,
        list_for_each_entry_safe(watch, next, &inode->inotify_watches, i_list) {
                u32 watch_mask = watch->mask;
                if (watch_mask & mask) {
-                       struct inotify_device *dev = watch->dev;
-                       get_inotify_watch(watch);
-                       mutex_lock(&dev->mutex);
-                       inotify_dev_queue_event(dev, watch, mask, cookie, name);
+                       struct inotify_handle *ih= watch->ih;
+                       mutex_lock(&ih->mutex);
                        if (watch_mask & IN_ONESHOT)
-                               remove_watch_no_event(watch, dev);
-                       mutex_unlock(&dev->mutex);
-                       put_inotify_watch(watch);
+                               remove_watch_no_event(watch, ih);
+                       ih->in_ops->handle_event(watch, watch->wd, mask, cookie,
+                                                name, n_inode);
+                       mutex_unlock(&ih->mutex);
                }
        }
        mutex_unlock(&inode->inotify_mutex);
@@ -613,7 +330,8 @@ void inotify_dentry_parent_queue_event(struct dentry *dentry, u32 mask,
        if (inotify_inode_watched(inode)) {
                dget(parent);
                spin_unlock(&dentry->d_lock);
-               inotify_inode_queue_event(inode, mask, cookie, name);
+               inotify_inode_queue_event(inode, mask, cookie, name,
+                                         dentry->d_inode);
                dput(parent);
        } else
                spin_unlock(&dentry->d_lock);
@@ -665,7 +383,7 @@ void inotify_unmount_inodes(struct list_head *list)
 
                need_iput_tmp = need_iput;
                need_iput = NULL;
-               /* In case the remove_watch() drops a reference. */
+               /* In case inotify_remove_watch_locked() drops a reference. */
                if (inode != need_iput_tmp)
                        __iget(inode);
                else
@@ -694,11 +412,12 @@ void inotify_unmount_inodes(struct list_head *list)
                mutex_lock(&inode->inotify_mutex);
                watches = &inode->inotify_watches;
                list_for_each_entry_safe(watch, next_w, watches, i_list) {
-                       struct inotify_device *dev = watch->dev;
-                       mutex_lock(&dev->mutex);
-                       inotify_dev_queue_event(dev, watch, IN_UNMOUNT,0,NULL);
-                       remove_watch(watch, dev);
-                       mutex_unlock(&dev->mutex);
+                       struct inotify_handle *ih= watch->ih;
+                       mutex_lock(&ih->mutex);
+                       ih->in_ops->handle_event(watch, watch->wd, IN_UNMOUNT, 0,
+                                                NULL, NULL);
+                       inotify_remove_watch_locked(ih, watch);
+                       mutex_unlock(&ih->mutex);
                }
                mutex_unlock(&inode->inotify_mutex);
                iput(inode);            
@@ -718,432 +437,292 @@ void inotify_inode_is_dead(struct inode *inode)
 
        mutex_lock(&inode->inotify_mutex);
        list_for_each_entry_safe(watch, next, &inode->inotify_watches, i_list) {
-               struct inotify_device *dev = watch->dev;
-               mutex_lock(&dev->mutex);
-               remove_watch(watch, dev);
-               mutex_unlock(&dev->mutex);
+               struct inotify_handle *ih = watch->ih;
+               mutex_lock(&ih->mutex);
+               inotify_remove_watch_locked(ih, watch);
+               mutex_unlock(&ih->mutex);
        }
        mutex_unlock(&inode->inotify_mutex);
 }
 EXPORT_SYMBOL_GPL(inotify_inode_is_dead);
 
-/* Device Interface */
+/* Kernel Consumer API */
 
-static unsigned int inotify_poll(struct file *file, poll_table *wait)
+/**
+ * inotify_init - allocate and initialize an inotify instance
+ * @ops: caller's inotify operations
+ */
+struct inotify_handle *inotify_init(const struct inotify_operations *ops)
 {
-       struct inotify_device *dev = file->private_data;
-       int ret = 0;
+       struct inotify_handle *ih;
 
-       poll_wait(file, &dev->wq, wait);
-       mutex_lock(&dev->mutex);
-       if (!list_empty(&dev->events))
-               ret = POLLIN | POLLRDNORM;
-       mutex_unlock(&dev->mutex);
+       ih = kmalloc(sizeof(struct inotify_handle), GFP_KERNEL);
+       if (unlikely(!ih))
+               return ERR_PTR(-ENOMEM);
 
-       return ret;
+       idr_init(&ih->idr);
+       INIT_LIST_HEAD(&ih->watches);
+       mutex_init(&ih->mutex);
+       ih->last_wd = 0;
+       ih->in_ops = ops;
+       atomic_set(&ih->count, 0);
+       get_inotify_handle(ih);
+
+       return ih;
 }
+EXPORT_SYMBOL_GPL(inotify_init);
 
-static ssize_t inotify_read(struct file *file, char __user *buf,
-                           size_t count, loff_t *pos)
+/**
+ * inotify_init_watch - initialize an inotify watch
+ * @watch: watch to initialize
+ */
+void inotify_init_watch(struct inotify_watch *watch)
 {
-       size_t event_size = sizeof (struct inotify_event);
-       struct inotify_device *dev;
-       char __user *start;
-       int ret;
-       DEFINE_WAIT(wait);
-
-       start = buf;
-       dev = file->private_data;
-
-       while (1) {
-               int events;
-
-               prepare_to_wait(&dev->wq, &wait, TASK_INTERRUPTIBLE);
-
-               mutex_lock(&dev->mutex);
-               events = !list_empty(&dev->events);
-               mutex_unlock(&dev->mutex);
-               if (events) {
-                       ret = 0;
-                       break;
-               }
-
-               if (file->f_flags & O_NONBLOCK) {
-                       ret = -EAGAIN;
-                       break;
-               }
-
-               if (signal_pending(current)) {
-                       ret = -EINTR;
-                       break;
-               }
-
-               schedule();
-       }
-
-       finish_wait(&dev->wq, &wait);
-       if (ret)
-               return ret;
-
-       mutex_lock(&dev->mutex);
-       while (1) {
-               struct inotify_kernel_event *kevent;
-
-               ret = buf - start;
-               if (list_empty(&dev->events))
-                       break;
-
-               kevent = inotify_dev_get_event(dev);
-               if (event_size + kevent->event.len > count)
-                       break;
-
-               if (copy_to_user(buf, &kevent->event, event_size)) {
-                       ret = -EFAULT;
-                       break;
-               }
-               buf += event_size;
-               count -= event_size;
-
-               if (kevent->name) {
-                       if (copy_to_user(buf, kevent->name, kevent->event.len)){
-                               ret = -EFAULT;
-                               break;
-                       }
-                       buf += kevent->event.len;
-                       count -= kevent->event.len;
-               }
-
-               remove_kevent(dev, kevent);
-       }
-       mutex_unlock(&dev->mutex);
-
-       return ret;
+       INIT_LIST_HEAD(&watch->h_list);
+       INIT_LIST_HEAD(&watch->i_list);
+       atomic_set(&watch->count, 0);
+       get_inotify_watch(watch); /* initial get */
 }
+EXPORT_SYMBOL_GPL(inotify_init_watch);
 
-static int inotify_release(struct inode *ignored, struct file *file)
+/**
+ * inotify_destroy - clean up and destroy an inotify instance
+ * @ih: inotify handle
+ */
+void inotify_destroy(struct inotify_handle *ih)
 {
-       struct inotify_device *dev = file->private_data;
-
        /*
-        * Destroy all of the watches on this device.  Unfortunately, not very
+        * Destroy all of the watches for this handle. Unfortunately, not very
         * pretty.  We cannot do a simple iteration over the list, because we
         * do not know the inode until we iterate to the watch.  But we need to
-        * hold inode->inotify_mutex before dev->mutex.  The following works.
+        * hold inode->inotify_mutex before ih->mutex.  The following works.
         */
        while (1) {
                struct inotify_watch *watch;
                struct list_head *watches;
                struct inode *inode;
 
-               mutex_lock(&dev->mutex);
-               watches = &dev->watches;
+               mutex_lock(&ih->mutex);
+               watches = &ih->watches;
                if (list_empty(watches)) {
-                       mutex_unlock(&dev->mutex);
+                       mutex_unlock(&ih->mutex);
                        break;
                }
-               watch = list_entry(watches->next, struct inotify_watch, d_list);
+               watch = list_entry(watches->next, struct inotify_watch, h_list);
                get_inotify_watch(watch);
-               mutex_unlock(&dev->mutex);
+               mutex_unlock(&ih->mutex);
 
                inode = watch->inode;
                mutex_lock(&inode->inotify_mutex);
-               mutex_lock(&dev->mutex);
+               mutex_lock(&ih->mutex);
 
                /* make sure we didn't race with another list removal */
-               if (likely(idr_find(&dev->idr, watch->wd)))
-                       remove_watch_no_event(watch, dev);
+               if (likely(idr_find(&ih->idr, watch->wd))) {
+                       remove_watch_no_event(watch, ih);
+                       put_inotify_watch(watch);
+               }
 
-               mutex_unlock(&dev->mutex);
+               mutex_unlock(&ih->mutex);
                mutex_unlock(&inode->inotify_mutex);
                put_inotify_watch(watch);
        }
 
-       /* destroy all of the events on this device */
-       mutex_lock(&dev->mutex);
-       while (!list_empty(&dev->events))
-               inotify_dev_event_dequeue(dev);
-       mutex_unlock(&dev->mutex);
-
-       /* free this device: the put matching the get in inotify_init() */
-       put_inotify_dev(dev);
-
-       return 0;
+       /* free this handle: the put matching the get in inotify_init() */
+       put_inotify_handle(ih);
 }
+EXPORT_SYMBOL_GPL(inotify_destroy);
 
-/*
- * inotify_ignore - remove a given wd from this inotify instance.
+/**
+ * inotify_find_watch - find an existing watch for an (ih,inode) pair
+ * @ih: inotify handle
+ * @inode: inode to watch
+ * @watchp: pointer to existing inotify_watch
  *
- * Can sleep.
+ * Caller must pin given inode (via nameidata).
  */
-static int inotify_ignore(struct inotify_device *dev, s32 wd)
+s32 inotify_find_watch(struct inotify_handle *ih, struct inode *inode,
+                      struct inotify_watch **watchp)
 {
-       struct inotify_watch *watch;
-       struct inode *inode;
-
-       mutex_lock(&dev->mutex);
-       watch = idr_find(&dev->idr, wd);
-       if (unlikely(!watch)) {
-               mutex_unlock(&dev->mutex);
-               return -EINVAL;
-       }
-       get_inotify_watch(watch);
-       inode = watch->inode;
-       mutex_unlock(&dev->mutex);
+       struct inotify_watch *old;
+       int ret = -ENOENT;
 
        mutex_lock(&inode->inotify_mutex);
-       mutex_lock(&dev->mutex);
+       mutex_lock(&ih->mutex);
 
-       /* make sure that we did not race */
-       if (likely(idr_find(&dev->idr, wd) == watch))
-               remove_watch(watch, dev);
+       old = inode_find_handle(inode, ih);
+       if (unlikely(old)) {
+               get_inotify_watch(old); /* caller must put watch */
+               *watchp = old;
+               ret = old->wd;
+       }
 
-       mutex_unlock(&dev->mutex);
+       mutex_unlock(&ih->mutex);
        mutex_unlock(&inode->inotify_mutex);
-       put_inotify_watch(watch);
 
-       return 0;
+       return ret;
 }
+EXPORT_SYMBOL_GPL(inotify_find_watch);
 
-static long inotify_ioctl(struct file *file, unsigned int cmd,
-                         unsigned long arg)
+/**
+ * inotify_find_update_watch - find and update the mask of an existing watch
+ * @ih: inotify handle
+ * @inode: inode's watch to update
+ * @mask: mask of events to watch
+ *
+ * Caller must pin given inode (via nameidata).
+ */
+s32 inotify_find_update_watch(struct inotify_handle *ih, struct inode *inode,
+                             u32 mask)
 {
-       struct inotify_device *dev;
-       void __user *p;
-       int ret = -ENOTTY;
-
-       dev = file->private_data;
-       p = (void __user *) arg;
-
-       switch (cmd) {
-       case FIONREAD:
-               ret = put_user(dev->queue_size, (int __user *) p);
-               break;
-       }
-
-       return ret;
-}
+       struct inotify_watch *old;
+       int mask_add = 0;
+       int ret;
 
-static const struct file_operations inotify_fops = {
-       .poll           = inotify_poll,
-       .read           = inotify_read,
-       .release        = inotify_release,
-       .unlocked_ioctl = inotify_ioctl,
-       .compat_ioctl   = inotify_ioctl,
-};
+       if (mask & IN_MASK_ADD)
+               mask_add = 1;
 
-asmlinkage long sys_inotify_init(void)
-{
-       struct inotify_device *dev;
-       struct user_struct *user;
-       struct file *filp;      
-       int fd, ret;
-
-       fd = get_unused_fd();
-       if (fd < 0)
-               return fd;
-
-       filp = get_empty_filp();
-       if (!filp) {
-               ret = -ENFILE;
-               goto out_put_fd;
-       }
+       /* don't allow invalid bits: we don't want flags set */
+       mask &= IN_ALL_EVENTS | IN_ONESHOT;
+       if (unlikely(!mask))
+               return -EINVAL;
 
-       user = get_uid(current->user);
-       if (unlikely(atomic_read(&user->inotify_devs) >=
-                       inotify_max_user_instances)) {
-               ret = -EMFILE;
-               goto out_free_uid;
-       }
+       mutex_lock(&inode->inotify_mutex);
+       mutex_lock(&ih->mutex);
 
-       dev = kmalloc(sizeof(struct inotify_device), GFP_KERNEL);
-       if (unlikely(!dev)) {
-               ret = -ENOMEM;
-               goto out_free_uid;
+       /*
+        * Handle the case of re-adding a watch on an (inode,ih) pair that we
+        * are already watching.  We just update the mask and return its wd.
+        */
+       old = inode_find_handle(inode, ih);
+       if (unlikely(!old)) {
+               ret = -ENOENT;
+               goto out;
        }
 
-       filp->f_op = &inotify_fops;
-       filp->f_vfsmnt = mntget(inotify_mnt);
-       filp->f_dentry = dget(inotify_mnt->mnt_root);
-       filp->f_mapping = filp->f_dentry->d_inode->i_mapping;
-       filp->f_mode = FMODE_READ;
-       filp->f_flags = O_RDONLY;
-       filp->private_data = dev;
-
-       idr_init(&dev->idr);
-       INIT_LIST_HEAD(&dev->events);
-       INIT_LIST_HEAD(&dev->watches);
-       init_waitqueue_head(&dev->wq);
-       mutex_init(&dev->mutex);
-       dev->event_count = 0;
-       dev->queue_size = 0;
-       dev->max_events = inotify_max_queued_events;
-       dev->user = user;
-       dev->last_wd = 0;
-       atomic_set(&dev->count, 0);
-
-       get_inotify_dev(dev);
-       atomic_inc(&user->inotify_devs);
-       fd_install(fd, filp);
-
-       return fd;
-out_free_uid:
-       free_uid(user);
-       put_filp(filp);
-out_put_fd:
-       put_unused_fd(fd);
+       if (mask_add)
+               old->mask |= mask;
+       else
+               old->mask = mask;
+       ret = old->wd;
+out:
+       mutex_unlock(&ih->mutex);
+       mutex_unlock(&inode->inotify_mutex);
        return ret;
 }
+EXPORT_SYMBOL_GPL(inotify_find_update_watch);
 
-asmlinkage long sys_inotify_add_watch(int fd, const char __user *path, u32 mask)
+/**
+ * inotify_add_watch - add a watch to an inotify instance
+ * @ih: inotify handle
+ * @watch: caller allocated watch structure
+ * @inode: inode to watch
+ * @mask: mask of events to watch
+ *
+ * Caller must pin given inode (via nameidata).
+ * Caller must ensure it only calls inotify_add_watch() once per watch.
+ * Calls inotify_handle_get_wd() so may sleep.
+ */
+s32 inotify_add_watch(struct inotify_handle *ih, struct inotify_watch *watch,
+                     struct inode *inode, u32 mask)
 {
-       struct inotify_watch *watch, *old;
-       struct inode *inode;
-       struct inotify_device *dev;
-       struct nameidata nd;
-       struct file *filp;
-       int ret, fput_needed;
-       int mask_add = 0;
-       unsigned flags = 0;
-
-       filp = fget_light(fd, &fput_needed);
-       if (unlikely(!filp))
-               return -EBADF;
-
-       /* verify that this is indeed an inotify instance */
-       if (unlikely(filp->f_op != &inotify_fops)) {
-               ret = -EINVAL;
-               goto fput_and_out;
-       }
-
-       if (!(mask & IN_DONT_FOLLOW))
-               flags |= LOOKUP_FOLLOW;
-       if (mask & IN_ONLYDIR)
-               flags |= LOOKUP_DIRECTORY;
-
-       ret = find_inode(path, &nd, flags);
-       if (unlikely(ret))
-               goto fput_and_out;
+       int ret = 0;
 
-       /* inode held in place by reference to nd; dev by fget on fd */
-       inode = nd.dentry->d_inode;
-       dev = filp->private_data;
+       /* don't allow invalid bits: we don't want flags set */
+       mask &= IN_ALL_EVENTS | IN_ONESHOT;
+       if (unlikely(!mask))
+               return -EINVAL;
+       watch->mask = mask;
 
        mutex_lock(&inode->inotify_mutex);
-       mutex_lock(&dev->mutex);
-
-       if (mask & IN_MASK_ADD)
-               mask_add = 1;
+       mutex_lock(&ih->mutex);
 
-       /* don't let user-space set invalid bits: we don't want flags set */
-       mask &= IN_ALL_EVENTS | IN_ONESHOT;
-       if (unlikely(!mask)) {
-               ret = -EINVAL;
+       /* Initialize a new watch */
+       ret = inotify_handle_get_wd(ih, watch);
+       if (unlikely(ret))
                goto out;
-       }
+       ret = watch->wd;
+
+       /* save a reference to handle and bump the count to make it official */
+       get_inotify_handle(ih);
+       watch->ih = ih;
 
        /*
-        * Handle the case of re-adding a watch on an (inode,dev) pair that we
-        * are already watching.  We just update the mask and return its wd.
+        * Save a reference to the inode and bump the ref count to make it
+        * official.  We hold a reference to nameidata, which makes this safe.
         */
-       old = inode_find_dev(inode, dev);
-       if (unlikely(old)) {
-               if (mask_add)
-                       old->mask |= mask;
-               else
-                       old->mask = mask;
-               ret = old->wd;
-               goto out;
-       }
-
-       watch = create_watch(dev, mask, inode);
-       if (unlikely(IS_ERR(watch))) {
-               ret = PTR_ERR(watch);
-               goto out;
-       }
+       watch->inode = igrab(inode);
 
        if (!inotify_inode_watched(inode))
                set_dentry_child_flags(inode, 1);
 
-       /* Add the watch to the device's and the inode's list */
-       list_add(&watch->d_list, &dev->watches);
+       /* Add the watch to the handle's and the inode's list */
+       list_add(&watch->h_list, &ih->watches);
        list_add(&watch->i_list, &inode->inotify_watches);
-       ret = watch->wd;
 out:
-       mutex_unlock(&dev->mutex);
+       mutex_unlock(&ih->mutex);
        mutex_unlock(&inode->inotify_mutex);
-       path_release(&nd);
-fput_and_out:
-       fput_light(filp, fput_needed);
        return ret;
 }
+EXPORT_SYMBOL_GPL(inotify_add_watch);
 
-asmlinkage long sys_inotify_rm_watch(int fd, u32 wd)
+/**
+ * inotify_rm_wd - remove a watch from an inotify instance
+ * @ih: inotify handle
+ * @wd: watch descriptor to remove
+ *
+ * Can sleep.
+ */
+int inotify_rm_wd(struct inotify_handle *ih, u32 wd)
 {
-       struct file *filp;
-       struct inotify_device *dev;
-       int ret, fput_needed;
-
-       filp = fget_light(fd, &fput_needed);
-       if (unlikely(!filp))
-               return -EBADF;
+       struct inotify_watch *watch;
+       struct inode *inode;
 
-       /* verify that this is indeed an inotify instance */
-       if (unlikely(filp->f_op != &inotify_fops)) {
-               ret = -EINVAL;
-               goto out;
+       mutex_lock(&ih->mutex);
+       watch = idr_find(&ih->idr, wd);
+       if (unlikely(!watch)) {
+               mutex_unlock(&ih->mutex);
+               return -EINVAL;
        }
+       get_inotify_watch(watch);
+       inode = watch->inode;
+       mutex_unlock(&ih->mutex);
 
-       dev = filp->private_data;
-       ret = inotify_ignore(dev, wd);
+       mutex_lock(&inode->inotify_mutex);
+       mutex_lock(&ih->mutex);
 
-out:
-       fput_light(filp, fput_needed);
-       return ret;
+       /* make sure that we did not race */
+       if (likely(idr_find(&ih->idr, wd) == watch))
+               inotify_remove_watch_locked(ih, watch);
+
+       mutex_unlock(&ih->mutex);
+       mutex_unlock(&inode->inotify_mutex);
+       put_inotify_watch(watch);
+
+       return 0;
 }
+EXPORT_SYMBOL_GPL(inotify_rm_wd);
 
-static struct super_block *
-inotify_get_sb(struct file_system_type *fs_type, int flags,
-              const char *dev_name, void *data)
+/**
+ * inotify_rm_watch - remove a watch from an inotify instance
+ * @ih: inotify handle
+ * @watch: watch to remove
+ *
+ * Can sleep.
+ */
+int inotify_rm_watch(struct inotify_handle *ih,
+                    struct inotify_watch *watch)
 {
-    return get_sb_pseudo(fs_type, "inotify", NULL, 0xBAD1DEA);
+       return inotify_rm_wd(ih, watch->wd);
 }
-
-static struct file_system_type inotify_fs_type = {
-    .name           = "inotifyfs",
-    .get_sb         = inotify_get_sb,
-    .kill_sb        = kill_anon_super,
-};
+EXPORT_SYMBOL_GPL(inotify_rm_watch);
 
 /*
- * inotify_setup - Our initialization function.  Note that we cannnot return
- * error because we have compiled-in VFS hooks.  So an (unlikely) failure here
- * must result in panic().
+ * inotify_setup - core initialization function
  */
 static int __init inotify_setup(void)
 {
-       int ret;
-
-       ret = register_filesystem(&inotify_fs_type);
-       if (unlikely(ret))
-               panic("inotify: register_filesystem returned %d!\n", ret);
-
-       inotify_mnt = kern_mount(&inotify_fs_type);
-       if (IS_ERR(inotify_mnt))
-               panic("inotify: kern_mount ret %ld!\n", PTR_ERR(inotify_mnt));
-
-       inotify_max_queued_events = 16384;
-       inotify_max_user_instances = 128;
-       inotify_max_user_watches = 8192;
-
        atomic_set(&inotify_cookie, 0);
 
-       watch_cachep = kmem_cache_create("inotify_watch_cache",
-                                        sizeof(struct inotify_watch),
-                                        0, SLAB_PANIC, NULL, NULL);
-       event_cachep = kmem_cache_create("inotify_event_cache",
-                                        sizeof(struct inotify_kernel_event),
-                                        0, SLAB_PANIC, NULL, NULL);
-
        return 0;
 }
 
diff --git a/fs/inotify_user.c b/fs/inotify_user.c
new file mode 100644 (file)
index 0000000..9e9931e
--- /dev/null
@@ -0,0 +1,719 @@
+/*
+ * 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.
+ *
+ * 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/kernel.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/file.h>
+#include <linux/mount.h>
+#include <linux/namei.h>
+#include <linux/poll.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/inotify.h>
+#include <linux/syscalls.h>
+
+#include <asm/ioctls.h>
+
+static kmem_cache_t *watch_cachep __read_mostly;
+static kmem_cache_t *event_cachep __read_mostly;
+
+static struct vfsmount *inotify_mnt __read_mostly;
+
+/* these are configurable via /proc/sys/fs/inotify/ */
+int inotify_max_user_instances __read_mostly;
+int inotify_max_user_watches __read_mostly;
+int inotify_max_queued_events __read_mostly;
+
+/*
+ * Lock ordering:
+ *
+ * inotify_dev->up_mutex (ensures we don't re-add the same watch)
+ *     inode->inotify_mutex (protects inode's watch list)
+ *             inotify_handle->mutex (protects inotify_handle's watch list)
+ *                     inotify_dev->ev_mutex (protects device's event queue)
+ */
+
+/*
+ * Lifetimes of the main data structures:
+ *
+ * inotify_device: Lifetime is managed by reference count, from
+ * sys_inotify_init() until release.  Additional references can bump the count
+ * via get_inotify_dev() and drop the count via put_inotify_dev().
+ *
+ * inotify_user_watch: Lifetime is from create_watch() to the receipt of an
+ * IN_IGNORED event from inotify, or when using IN_ONESHOT, to receipt of the
+ * first event, or to inotify_destroy().
+ */
+
+/*
+ * struct inotify_device - represents an inotify instance
+ *
+ * This structure is protected by the mutex 'mutex'.
+ */
+struct inotify_device {
+       wait_queue_head_t       wq;             /* wait queue for i/o */
+       struct mutex            ev_mutex;       /* protects event queue */
+       struct mutex            up_mutex;       /* synchronizes watch updates */
+       struct list_head        events;         /* list of queued events */
+       atomic_t                count;          /* reference count */
+       struct user_struct      *user;          /* user who opened this dev */
+       struct inotify_handle   *ih;            /* inotify handle */
+       unsigned int            queue_size;     /* size of the queue (bytes) */
+       unsigned int            event_count;    /* number of pending events */
+       unsigned int            max_events;     /* maximum number of events */
+};
+
+/*
+ * struct inotify_kernel_event - An inotify event, originating from a watch and
+ * queued for user-space.  A list of these is attached to each instance of the
+ * device.  In read(), this list is walked and all events that can fit in the
+ * buffer are returned.
+ *
+ * Protected by dev->ev_mutex of the device in which we are queued.
+ */
+struct inotify_kernel_event {
+       struct inotify_event    event;  /* the user-space event */
+       struct list_head        list;   /* entry in inotify_device's list */
+       char                    *name;  /* filename, if any */
+};
+
+/*
+ * struct inotify_user_watch - our version of an inotify_watch, we add
+ * a reference to the associated inotify_device.
+ */
+struct inotify_user_watch {
+       struct inotify_device   *dev;   /* associated device */
+       struct inotify_watch    wdata;  /* inotify watch data */
+};
+
+#ifdef CONFIG_SYSCTL
+
+#include <linux/sysctl.h>
+
+static int zero;
+
+ctl_table inotify_table[] = {
+       {
+               .ctl_name       = INOTIFY_MAX_USER_INSTANCES,
+               .procname       = "max_user_instances",
+               .data           = &inotify_max_user_instances,
+               .maxlen         = sizeof(int),
+               .mode           = 0644,
+               .proc_handler   = &proc_dointvec_minmax,
+               .strategy       = &sysctl_intvec,
+               .extra1         = &zero,
+       },
+       {
+               .ctl_name       = INOTIFY_MAX_USER_WATCHES,
+               .procname       = "max_user_watches",
+               .data           = &inotify_max_user_watches,
+               .maxlen         = sizeof(int),
+               .mode           = 0644,
+               .proc_handler   = &proc_dointvec_minmax,
+               .strategy       = &sysctl_intvec,
+               .extra1         = &zero,
+       },
+       {
+               .ctl_name       = INOTIFY_MAX_QUEUED_EVENTS,
+               .procname       = "max_queued_events",
+               .data           = &inotify_max_queued_events,
+               .maxlen         = sizeof(int),
+               .mode           = 0644,
+               .proc_handler   = &proc_dointvec_minmax,
+               .strategy       = &sysctl_intvec,
+               .extra1         = &zero
+       },
+       { .ctl_name = 0 }
+};
+#endif /* CONFIG_SYSCTL */
+
+static inline void get_inotify_dev(struct inotify_device *dev)
+{
+       atomic_inc(&dev->count);
+}
+
+static inline void put_inotify_dev(struct inotify_device *dev)
+{
+       if (atomic_dec_and_test(&dev->count)) {
+               atomic_dec(&dev->user->inotify_devs);
+               free_uid(dev->user);
+               kfree(dev);
+       }
+}
+
+/*
+ * free_inotify_user_watch - cleans up the watch and its references
+ */
+static void free_inotify_user_watch(struct inotify_watch *w)
+{
+       struct inotify_user_watch *watch;
+       struct inotify_device *dev;
+
+       watch = container_of(w, struct inotify_user_watch, wdata);
+       dev = watch->dev;
+
+       atomic_dec(&dev->user->inotify_watches);
+       put_inotify_dev(dev);
+       kmem_cache_free(watch_cachep, watch);
+}
+
+/*
+ * kernel_event - create a new kernel event with the given parameters
+ *
+ * This function can sleep.
+ */
+static struct inotify_kernel_event * kernel_event(s32 wd, u32 mask, u32 cookie,
+                                                 const char *name)
+{
+       struct inotify_kernel_event *kevent;
+
+       kevent = kmem_cache_alloc(event_cachep, GFP_KERNEL);
+       if (unlikely(!kevent))
+               return NULL;
+
+       /* we hand this out to user-space, so zero it just in case */
+       memset(&kevent->event, 0, sizeof(struct inotify_event));
+
+       kevent->event.wd = wd;
+       kevent->event.mask = mask;
+       kevent->event.cookie = cookie;
+
+       INIT_LIST_HEAD(&kevent->list);
+
+       if (name) {
+               size_t len, rem, event_size = sizeof(struct inotify_event);
+
+               /*
+                * We need to pad the filename so as to properly align an
+                * array of inotify_event structures.  Because the structure is
+                * small and the common case is a small filename, we just round
+                * up to the next multiple of the structure's sizeof.  This is
+                * simple and safe for all architectures.
+                */
+               len = strlen(name) + 1;
+               rem = event_size - len;
+               if (len > event_size) {
+                       rem = event_size - (len % event_size);
+                       if (len % event_size == 0)
+                               rem = 0;
+               }
+
+               kevent->name = kmalloc(len + rem, GFP_KERNEL);
+               if (unlikely(!kevent->name)) {
+                       kmem_cache_free(event_cachep, kevent);
+                       return NULL;
+               }
+               memcpy(kevent->name, name, len);
+               if (rem)
+                       memset(kevent->name + len, 0, rem);
+               kevent->event.len = len + rem;
+       } else {
+               kevent->event.len = 0;
+               kevent->name = NULL;
+       }
+
+       return kevent;
+}
+
+/*
+ * inotify_dev_get_event - return the next event in the given dev's queue
+ *
+ * Caller must hold dev->ev_mutex.
+ */
+static inline struct inotify_kernel_event *
+inotify_dev_get_event(struct inotify_device *dev)
+{
+       return list_entry(dev->events.next, struct inotify_kernel_event, list);
+}
+
+/*
+ * inotify_dev_queue_event - event handler registered with core inotify, adds
+ * a new event to the given device
+ *
+ * Can sleep (calls kernel_event()).
+ */
+static void inotify_dev_queue_event(struct inotify_watch *w, u32 wd, u32 mask,
+                                   u32 cookie, const char *name,
+                                   struct inode *ignored)
+{
+       struct inotify_user_watch *watch;
+       struct inotify_device *dev;
+       struct inotify_kernel_event *kevent, *last;
+
+       watch = container_of(w, struct inotify_user_watch, wdata);
+       dev = watch->dev;
+
+       mutex_lock(&dev->ev_mutex);
+
+       /* we can safely put the watch as we don't reference it while
+        * generating the event
+        */
+       if (mask & IN_IGNORED || mask & IN_ONESHOT)
+               put_inotify_watch(w); /* final put */
+
+       /* coalescing: drop this event if it is a dupe of the previous */
+       last = inotify_dev_get_event(dev);
+       if (last && last->event.mask == mask && last->event.wd == wd &&
+                       last->event.cookie == cookie) {
+               const char *lastname = last->name;
+
+               if (!name && !lastname)
+                       goto out;
+               if (name && lastname && !strcmp(lastname, name))
+                       goto out;
+       }
+
+       /* the queue overflowed and we already sent the Q_OVERFLOW event */
+       if (unlikely(dev->event_count > dev->max_events))
+               goto out;
+
+       /* if the queue overflows, we need to notify user space */
+       if (unlikely(dev->event_count == dev->max_events))
+               kevent = kernel_event(-1, IN_Q_OVERFLOW, cookie, NULL);
+       else
+               kevent = kernel_event(wd, mask, cookie, name);
+
+       if (unlikely(!kevent))
+               goto out;
+
+       /* queue the event and wake up anyone waiting */
+       dev->event_count++;
+       dev->queue_size += sizeof(struct inotify_event) + kevent->event.len;
+       list_add_tail(&kevent->list, &dev->events);
+       wake_up_interruptible(&dev->wq);
+
+out:
+       mutex_unlock(&dev->ev_mutex);
+}
+
+/*
+ * remove_kevent - cleans up and ultimately frees the given kevent
+ *
+ * Caller must hold dev->ev_mutex.
+ */
+static void remove_kevent(struct inotify_device *dev,
+                         struct inotify_kernel_event *kevent)
+{
+       list_del(&kevent->list);
+
+       dev->event_count--;
+       dev->queue_size -= sizeof(struct inotify_event) + kevent->event.len;
+
+       kfree(kevent->name);
+       kmem_cache_free(event_cachep, kevent);
+}
+
+/*
+ * inotify_dev_event_dequeue - destroy an event on the given device
+ *
+ * Caller must hold dev->ev_mutex.
+ */
+static void inotify_dev_event_dequeue(struct inotify_device *dev)
+{
+       if (!list_empty(&dev->events)) {
+               struct inotify_kernel_event *kevent;
+               kevent = inotify_dev_get_event(dev);
+               remove_kevent(dev, kevent);
+       }
+}
+
+/*
+ * find_inode - resolve a user-given path to a specific inode and return a nd
+ */
+static int find_inode(const char __user *dirname, struct nameidata *nd,
+                     unsigned flags)
+{
+       int error;
+
+       error = __user_walk(dirname, flags, nd);
+       if (error)
+               return error;
+       /* you can only watch an inode if you have read permissions on it */
+       error = vfs_permission(nd, MAY_READ);
+       if (error)
+               path_release(nd);
+       return error;
+}
+
+/*
+ * create_watch - creates a watch on the given device.
+ *
+ * Callers must hold dev->up_mutex.
+ */
+static int create_watch(struct inotify_device *dev, struct inode *inode,
+                       u32 mask)
+{
+       struct inotify_user_watch *watch;
+       int ret;
+
+       if (atomic_read(&dev->user->inotify_watches) >=
+                       inotify_max_user_watches)
+               return -ENOSPC;
+
+       watch = kmem_cache_alloc(watch_cachep, GFP_KERNEL);
+       if (unlikely(!watch))
+               return -ENOMEM;
+
+       /* save a reference to device and bump the count to make it official */
+       get_inotify_dev(dev);
+       watch->dev = dev;
+
+       atomic_inc(&dev->user->inotify_watches);
+
+       inotify_init_watch(&watch->wdata);
+       ret = inotify_add_watch(dev->ih, &watch->wdata, inode, mask);
+       if (ret < 0)
+               free_inotify_user_watch(&watch->wdata);
+
+       return ret;
+}
+
+/* Device Interface */
+
+static unsigned int inotify_poll(struct file *file, poll_table *wait)
+{
+       struct inotify_device *dev = file->private_data;
+       int ret = 0;
+
+       poll_wait(file, &dev->wq, wait);
+       mutex_lock(&dev->ev_mutex);
+       if (!list_empty(&dev->events))
+               ret = POLLIN | POLLRDNORM;
+       mutex_unlock(&dev->ev_mutex);
+
+       return ret;
+}
+
+static ssize_t inotify_read(struct file *file, char __user *buf,
+                           size_t count, loff_t *pos)
+{
+       size_t event_size = sizeof (struct inotify_event);
+       struct inotify_device *dev;
+       char __user *start;
+       int ret;
+       DEFINE_WAIT(wait);
+
+       start = buf;
+       dev = file->private_data;
+
+       while (1) {
+               int events;
+
+               prepare_to_wait(&dev->wq, &wait, TASK_INTERRUPTIBLE);
+
+               mutex_lock(&dev->ev_mutex);
+               events = !list_empty(&dev->events);
+               mutex_unlock(&dev->ev_mutex);
+               if (events) {
+                       ret = 0;
+                       break;
+               }
+
+               if (file->f_flags & O_NONBLOCK) {
+                       ret = -EAGAIN;
+                       break;
+               }
+
+               if (signal_pending(current)) {
+                       ret = -EINTR;
+                       break;
+               }
+
+               schedule();
+       }
+
+       finish_wait(&dev->wq, &wait);
+       if (ret)
+               return ret;
+
+       mutex_lock(&dev->ev_mutex);
+       while (1) {
+               struct inotify_kernel_event *kevent;
+
+               ret = buf - start;
+               if (list_empty(&dev->events))
+                       break;
+
+               kevent = inotify_dev_get_event(dev);
+               if (event_size + kevent->event.len > count)
+                       break;
+
+               if (copy_to_user(buf, &kevent->event, event_size)) {
+                       ret = -EFAULT;
+                       break;
+               }
+               buf += event_size;
+               count -= event_size;
+
+               if (kevent->name) {
+                       if (copy_to_user(buf, kevent->name, kevent->event.len)){
+                               ret = -EFAULT;
+                               break;
+                       }
+                       buf += kevent->event.len;
+                       count -= kevent->event.len;
+               }
+
+               remove_kevent(dev, kevent);
+       }
+       mutex_unlock(&dev->ev_mutex);
+
+       return ret;
+}
+
+static int inotify_release(struct inode *ignored, struct file *file)
+{
+       struct inotify_device *dev = file->private_data;
+
+       inotify_destroy(dev->ih);
+
+       /* destroy all of the events on this device */
+       mutex_lock(&dev->ev_mutex);
+       while (!list_empty(&dev->events))
+               inotify_dev_event_dequeue(dev);
+       mutex_unlock(&dev->ev_mutex);
+
+       /* free this device: the put matching the get in inotify_init() */
+       put_inotify_dev(dev);
+
+       return 0;
+}
+
+static long inotify_ioctl(struct file *file, unsigned int cmd,
+                         unsigned long arg)
+{
+       struct inotify_device *dev;
+       void __user *p;
+       int ret = -ENOTTY;
+
+       dev = file->private_data;
+       p = (void __user *) arg;
+
+       switch (cmd) {
+       case FIONREAD:
+               ret = put_user(dev->queue_size, (int __user *) p);
+               break;
+       }
+
+       return ret;
+}
+
+static const struct file_operations inotify_fops = {
+       .poll           = inotify_poll,
+       .read           = inotify_read,
+       .release        = inotify_release,
+       .unlocked_ioctl = inotify_ioctl,
+       .compat_ioctl   = inotify_ioctl,
+};
+
+static const struct inotify_operations inotify_user_ops = {
+       .handle_event   = inotify_dev_queue_event,
+       .destroy_watch  = free_inotify_user_watch,
+};
+
+asmlinkage long sys_inotify_init(void)
+{
+       struct inotify_device *dev;
+       struct inotify_handle *ih;
+       struct user_struct *user;
+       struct file *filp;
+       int fd, ret;
+
+       fd = get_unused_fd();
+       if (fd < 0)
+               return fd;
+
+       filp = get_empty_filp();
+       if (!filp) {
+               ret = -ENFILE;
+               goto out_put_fd;
+       }
+
+       user = get_uid(current->user);
+       if (unlikely(atomic_read(&user->inotify_devs) >=
+                       inotify_max_user_instances)) {
+               ret = -EMFILE;
+               goto out_free_uid;
+       }
+
+       dev = kmalloc(sizeof(struct inotify_device), GFP_KERNEL);
+       if (unlikely(!dev)) {
+               ret = -ENOMEM;
+               goto out_free_uid;
+       }
+
+       ih = inotify_init(&inotify_user_ops);
+       if (unlikely(IS_ERR(ih))) {
+               ret = PTR_ERR(ih);
+               goto out_free_dev;
+       }
+       dev->ih = ih;
+
+       filp->f_op = &inotify_fops;
+       filp->f_vfsmnt = mntget(inotify_mnt);
+       filp->f_dentry = dget(inotify_mnt->mnt_root);
+       filp->f_mapping = filp->f_dentry->d_inode->i_mapping;
+       filp->f_mode = FMODE_READ;
+       filp->f_flags = O_RDONLY;
+       filp->private_data = dev;
+
+       INIT_LIST_HEAD(&dev->events);
+       init_waitqueue_head(&dev->wq);
+       mutex_init(&dev->ev_mutex);
+       mutex_init(&dev->up_mutex);
+       dev->event_count = 0;
+       dev->queue_size = 0;
+       dev->max_events = inotify_max_queued_events;
+       dev->user = user;
+       atomic_set(&dev->count, 0);
+
+       get_inotify_dev(dev);
+       atomic_inc(&user->inotify_devs);
+       fd_install(fd, filp);
+
+       return fd;
+out_free_dev:
+       kfree(dev);
+out_free_uid:
+       free_uid(user);
+       put_filp(filp);
+out_put_fd:
+       put_unused_fd(fd);
+       return ret;
+}
+
+asmlinkage long sys_inotify_add_watch(int fd, const char __user *path, u32 mask)
+{
+       struct inode *inode;
+       struct inotify_device *dev;
+       struct nameidata nd;
+       struct file *filp;
+       int ret, fput_needed;
+       unsigned flags = 0;
+
+       filp = fget_light(fd, &fput_needed);
+       if (unlikely(!filp))
+               return -EBADF;
+
+       /* verify that this is indeed an inotify instance */
+       if (unlikely(filp->f_op != &inotify_fops)) {
+               ret = -EINVAL;
+               goto fput_and_out;
+       }
+
+       if (!(mask & IN_DONT_FOLLOW))
+               flags |= LOOKUP_FOLLOW;
+       if (mask & IN_ONLYDIR)
+               flags |= LOOKUP_DIRECTORY;
+
+       ret = find_inode(path, &nd, flags);
+       if (unlikely(ret))
+               goto fput_and_out;
+
+       /* inode held in place by reference to nd; dev by fget on fd */
+       inode = nd.dentry->d_inode;
+       dev = filp->private_data;
+
+       mutex_lock(&dev->up_mutex);
+       ret = inotify_find_update_watch(dev->ih, inode, mask);
+       if (ret == -ENOENT)
+               ret = create_watch(dev, inode, mask);
+       mutex_unlock(&dev->up_mutex);
+
+       path_release(&nd);
+fput_and_out:
+       fput_light(filp, fput_needed);
+       return ret;
+}
+
+asmlinkage long sys_inotify_rm_watch(int fd, u32 wd)
+{
+       struct file *filp;
+       struct inotify_device *dev;
+       int ret, fput_needed;
+
+       filp = fget_light(fd, &fput_needed);
+       if (unlikely(!filp))
+               return -EBADF;
+
+       /* verify that this is indeed an inotify instance */
+       if (unlikely(filp->f_op != &inotify_fops)) {
+               ret = -EINVAL;
+               goto out;
+       }
+
+       dev = filp->private_data;
+
+       /* we free our watch data when we get IN_IGNORED */
+       ret = inotify_rm_wd(dev->ih, wd);
+
+out:
+       fput_light(filp, fput_needed);
+       return ret;
+}
+
+static struct super_block *
+inotify_get_sb(struct file_system_type *fs_type, int flags,
+              const char *dev_name, void *data)
+{
+    return get_sb_pseudo(fs_type, "inotify", NULL, 0xBAD1DEA);
+}
+
+static struct file_system_type inotify_fs_type = {
+    .name           = "inotifyfs",
+    .get_sb         = inotify_get_sb,
+    .kill_sb        = kill_anon_super,
+};
+
+/*
+ * inotify_user_setup - Our initialization function.  Note that we cannnot return
+ * error because we have compiled-in VFS hooks.  So an (unlikely) failure here
+ * must result in panic().
+ */
+static int __init inotify_user_setup(void)
+{
+       int ret;
+
+       ret = register_filesystem(&inotify_fs_type);
+       if (unlikely(ret))
+               panic("inotify: register_filesystem returned %d!\n", ret);
+
+       inotify_mnt = kern_mount(&inotify_fs_type);
+       if (IS_ERR(inotify_mnt))
+               panic("inotify: kern_mount ret %ld!\n", PTR_ERR(inotify_mnt));
+
+       inotify_max_queued_events = 16384;
+       inotify_max_user_instances = 128;
+       inotify_max_user_watches = 8192;
+
+       watch_cachep = kmem_cache_create("inotify_watch_cache",
+                                        sizeof(struct inotify_user_watch),
+                                        0, SLAB_PANIC, NULL, NULL);
+       event_cachep = kmem_cache_create("inotify_event_cache",
+                                        sizeof(struct inotify_kernel_event),
+                                        0, SLAB_PANIC, NULL, NULL);
+
+       return 0;
+}
+
+module_init(inotify_user_setup);
index d6e2ee251736b4eb9bc692e4f0057af5633942c7..184fe4acf82449dae1c62eade2d4489b078f24aa 100644 (file)
@@ -1127,7 +1127,7 @@ out:
        if (likely(retval == 0)) {
                if (unlikely(current->audit_context && nd && nd->dentry &&
                                nd->dentry->d_inode))
-               audit_inode(name, nd->dentry->d_inode, flags);
+               audit_inode(name, nd->dentry->d_inode);
        }
 out_fail:
        return retval;
index 317b7c7f38a73aac8cc8f3edc463919dabb2f947..4f178acd4c09ccfb35a2cac2dfebd42a17081604 100644 (file)
--- a/fs/open.c
+++ b/fs/open.c
@@ -633,7 +633,7 @@ asmlinkage long sys_fchmod(unsigned int fd, mode_t mode)
        dentry = file->f_dentry;
        inode = dentry->d_inode;
 
-       audit_inode(NULL, inode, 0);
+       audit_inode(NULL, inode);
 
        err = -EROFS;
        if (IS_RDONLY(inode))
@@ -786,7 +786,7 @@ asmlinkage long sys_fchown(unsigned int fd, uid_t user, gid_t group)
        if (file) {
                struct dentry * dentry;
                dentry = file->f_dentry;
-               audit_inode(NULL, dentry->d_inode, 0);
+               audit_inode(NULL, dentry->d_inode);
                error = chown_common(dentry, user, group);
                fput(file);
        }
index 6cc77dc3f3ff79c49fa7d4ce89360b595995d839..6afff725a8c922b08bb3b6bbd52bf044ecc42e6c 100644 (file)
@@ -1019,8 +1019,8 @@ static ssize_t proc_loginuid_write(struct file * file, const char __user * buf,
        if (current != task)
                return -EPERM;
 
-       if (count > PAGE_SIZE)
-               count = PAGE_SIZE;
+       if (count >= PAGE_SIZE)
+               count = PAGE_SIZE - 1;
 
        if (*ppos != 0) {
                /* No partial writes. */
@@ -1033,6 +1033,7 @@ static ssize_t proc_loginuid_write(struct file * file, const char __user * buf,
        if (copy_from_user(page, buf, count))
                goto out_free_page;
 
+       page[count] = '\0';
        loginuid = simple_strtoul(page, &tmp, 10);
        if (tmp == page) {
                length = -EINVAL;
index e416190f5e9cda02c34577ae57f2fa88e58945e7..c32f15b5f60fd29d69ec8f4aa7a4f3eec0501fd6 100644 (file)
@@ -242,7 +242,7 @@ sys_fsetxattr(int fd, char __user *name, void __user *value,
        if (!f)
                return error;
        dentry = f->f_dentry;
-       audit_inode(NULL, dentry->d_inode, 0);
+       audit_inode(NULL, dentry->d_inode);
        error = setxattr(dentry, name, value, size, flags);
        fput(f);
        return error;
@@ -469,7 +469,7 @@ sys_fremovexattr(int fd, char __user *name)
        if (!f)
                return error;
        dentry = f->f_dentry;
-       audit_inode(NULL, dentry->d_inode, 0);
+       audit_inode(NULL, dentry->d_inode);
        error = removexattr(dentry, name);
        fput(f);
        return error;
index 14259f6db5bcb139fa09dcba4f33ebaabd4a0567..e051ff9c5b5061fabd5b92dc7fbf5463f9eec4d5 100644 (file)
 #define AUDIT_CONFIG_CHANGE    1305    /* Audit system configuration change */
 #define AUDIT_SOCKADDR         1306    /* sockaddr copied as syscall arg */
 #define AUDIT_CWD              1307    /* Current working directory */
+#define AUDIT_EXECVE           1309    /* execve arguments */
 #define AUDIT_IPC_SET_PERM     1311    /* IPC new permissions record type */
+#define AUDIT_MQ_OPEN          1312    /* POSIX MQ open record type */
+#define AUDIT_MQ_SENDRECV      1313    /* POSIX MQ send/receive record type */
+#define AUDIT_MQ_NOTIFY                1314    /* POSIX MQ notify record type */
+#define AUDIT_MQ_GETSETATTR    1315    /* POSIX MQ get/set attribute record type */
 
 #define AUDIT_AVC              1400    /* SE Linux avc denial or grant */
 #define AUDIT_SELINUX_ERR      1401    /* Internal SE Linux Errors */
 #define AUDIT_SE_TYPE  15      /* security label type */
 #define AUDIT_SE_SEN   16      /* security label sensitivity label */
 #define AUDIT_SE_CLR   17      /* security label clearance label */
+#define AUDIT_PPID     18
 
                                /* These are ONLY useful when checking
                                 * at syscall exit time (AUDIT_AT_EXIT). */
 #define AUDIT_INODE    102
 #define AUDIT_EXIT     103
 #define AUDIT_SUCCESS   104    /* exit >= 0; value ignored */
+#define AUDIT_WATCH    105
 
 #define AUDIT_ARG0      200
 #define AUDIT_ARG1      (AUDIT_ARG0+1)
@@ -277,12 +284,16 @@ struct audit_rule {               /* for AUDIT_LIST, AUDIT_ADD, and AUDIT_DEL */
 struct audit_sig_info {
        uid_t           uid;
        pid_t           pid;
+       char            ctx[0];
 };
 
 struct audit_buffer;
 struct audit_context;
 struct inode;
 struct netlink_skb_parms;
+struct linux_binprm;
+struct mq_attr;
+struct mqstat;
 
 #define AUDITSC_INVALID 0
 #define AUDITSC_SUCCESS 1
@@ -297,15 +308,19 @@ extern void audit_syscall_entry(int arch,
                                int major, unsigned long a0, unsigned long a1,
                                unsigned long a2, unsigned long a3);
 extern void audit_syscall_exit(int failed, long return_code);
-extern void audit_getname(const char *name);
+extern void __audit_getname(const char *name);
 extern void audit_putname(const char *name);
-extern void __audit_inode(const char *name, const struct inode *inode, unsigned flags);
+extern void __audit_inode(const char *name, const struct inode *inode);
 extern void __audit_inode_child(const char *dname, const struct inode *inode,
                                unsigned long pino);
-static inline void audit_inode(const char *name, const struct inode *inode,
-                              unsigned flags) {
+static inline void audit_getname(const char *name)
+{
        if (unlikely(current->audit_context))
-               __audit_inode(name, inode, flags);
+               __audit_getname(name);
+}
+static inline void audit_inode(const char *name, const struct inode *inode) {
+       if (unlikely(current->audit_context))
+               __audit_inode(name, inode);
 }
 static inline void audit_inode_child(const char *dname, 
                                     const struct inode *inode, 
@@ -320,13 +335,61 @@ extern void auditsc_get_stamp(struct audit_context *ctx,
                              struct timespec *t, unsigned int *serial);
 extern int  audit_set_loginuid(struct task_struct *task, uid_t loginuid);
 extern uid_t audit_get_loginuid(struct audit_context *ctx);
-extern int audit_ipc_obj(struct kern_ipc_perm *ipcp);
-extern int audit_ipc_set_perm(unsigned long qbytes, uid_t uid, gid_t gid, mode_t mode, struct kern_ipc_perm *ipcp);
+extern int __audit_ipc_obj(struct kern_ipc_perm *ipcp);
+extern int __audit_ipc_set_perm(unsigned long qbytes, uid_t uid, gid_t gid, mode_t mode);
+extern int audit_bprm(struct linux_binprm *bprm);
 extern int audit_socketcall(int nargs, unsigned long *args);
 extern int audit_sockaddr(int len, void *addr);
 extern int audit_avc_path(struct dentry *dentry, struct vfsmount *mnt);
-extern void audit_signal_info(int sig, struct task_struct *t);
 extern int audit_set_macxattr(const char *name);
+extern int __audit_mq_open(int oflag, mode_t mode, struct mq_attr __user *u_attr);
+extern int __audit_mq_timedsend(mqd_t mqdes, size_t msg_len, unsigned int msg_prio, const struct timespec __user *u_abs_timeout);
+extern int __audit_mq_timedreceive(mqd_t mqdes, size_t msg_len, unsigned int __user *u_msg_prio, const struct timespec __user *u_abs_timeout);
+extern int __audit_mq_notify(mqd_t mqdes, const struct sigevent __user *u_notification);
+extern int __audit_mq_getsetattr(mqd_t mqdes, struct mq_attr *mqstat);
+
+static inline int audit_ipc_obj(struct kern_ipc_perm *ipcp)
+{
+       if (unlikely(current->audit_context))
+               return __audit_ipc_obj(ipcp);
+       return 0;
+}
+static inline int audit_ipc_set_perm(unsigned long qbytes, uid_t uid, gid_t gid, mode_t mode)
+{
+       if (unlikely(current->audit_context))
+               return __audit_ipc_set_perm(qbytes, uid, gid, mode);
+       return 0;
+}
+static inline int audit_mq_open(int oflag, mode_t mode, struct mq_attr __user *u_attr)
+{
+       if (unlikely(current->audit_context))
+               return __audit_mq_open(oflag, mode, u_attr);
+       return 0;
+}
+static inline int audit_mq_timedsend(mqd_t mqdes, size_t msg_len, unsigned int msg_prio, const struct timespec __user *u_abs_timeout)
+{
+       if (unlikely(current->audit_context))
+               return __audit_mq_timedsend(mqdes, msg_len, msg_prio, u_abs_timeout);
+       return 0;
+}
+static inline int audit_mq_timedreceive(mqd_t mqdes, size_t msg_len, unsigned int __user *u_msg_prio, const struct timespec __user *u_abs_timeout)
+{
+       if (unlikely(current->audit_context))
+               return __audit_mq_timedreceive(mqdes, msg_len, u_msg_prio, u_abs_timeout);
+       return 0;
+}
+static inline int audit_mq_notify(mqd_t mqdes, const struct sigevent __user *u_notification)
+{
+       if (unlikely(current->audit_context))
+               return __audit_mq_notify(mqdes, u_notification);
+       return 0;
+}
+static inline int audit_mq_getsetattr(mqd_t mqdes, struct mq_attr *mqstat)
+{
+       if (unlikely(current->audit_context))
+               return __audit_mq_getsetattr(mqdes, mqstat);
+       return 0;
+}
 #else
 #define audit_alloc(t) ({ 0; })
 #define audit_free(t) do { ; } while (0)
@@ -334,19 +397,24 @@ extern int audit_set_macxattr(const char *name);
 #define audit_syscall_exit(f,r) do { ; } while (0)
 #define audit_getname(n) do { ; } while (0)
 #define audit_putname(n) do { ; } while (0)
-#define __audit_inode(n,i,f) do { ; } while (0)
+#define __audit_inode(n,i) do { ; } while (0)
 #define __audit_inode_child(d,i,p) do { ; } while (0)
-#define audit_inode(n,i,f) do { ; } while (0)
+#define audit_inode(n,i) do { ; } while (0)
 #define audit_inode_child(d,i,p) do { ; } while (0)
 #define auditsc_get_stamp(c,t,s) do { BUG(); } while (0)
 #define audit_get_loginuid(c) ({ -1; })
 #define audit_ipc_obj(i) ({ 0; })
-#define audit_ipc_set_perm(q,u,g,m,i) ({ 0; })
+#define audit_ipc_set_perm(q,u,g,m) ({ 0; })
+#define audit_bprm(p) ({ 0; })
 #define audit_socketcall(n,a) ({ 0; })
 #define audit_sockaddr(len, addr) ({ 0; })
 #define audit_avc_path(dentry, mnt) ({ 0; })
-#define audit_signal_info(s,t) do { ; } while (0)
 #define audit_set_macxattr(n) do { ; } while (0)
+#define audit_mq_open(o,m,a) ({ 0; })
+#define audit_mq_timedsend(d,l,p,t) ({ 0; })
+#define audit_mq_timedreceive(d,l,p,t) ({ 0; })
+#define audit_mq_notify(d,n) ({ 0; })
+#define audit_mq_getsetattr(d,s) ({ 0; })
 #endif
 
 #ifdef CONFIG_AUDIT
@@ -364,8 +432,11 @@ extern void                    audit_log_end(struct audit_buffer *ab);
 extern void                audit_log_hex(struct audit_buffer *ab,
                                          const unsigned char *buf,
                                          size_t len);
-extern void                audit_log_untrustedstring(struct audit_buffer *ab,
+extern const char *        audit_log_untrustedstring(struct audit_buffer *ab,
                                                      const char *string);
+extern const char *        audit_log_n_untrustedstring(struct audit_buffer *ab,
+                                                       size_t n,
+                                                       const char *string);
 extern void                audit_log_d_path(struct audit_buffer *ab,
                                             const char *prefix,
                                             struct dentry *dentry,
@@ -383,8 +454,8 @@ extern int  audit_receive_filter(int type, int pid, int uid, int seq,
 #define audit_log_end(b) do { ; } while (0)
 #define audit_log_hex(a,b,l) do { ; } while (0)
 #define audit_log_untrustedstring(a,s) do { ; } while (0)
+#define audit_log_n_untrustedstring(a,n,s) do { ; } while (0)
 #define audit_log_d_path(b,p,d,v) do { ; } while (0)
-#define audit_panic(m) do { ; } while (0)
 #endif
 #endif
 #endif
index 11438eff4d4455573f76789edfd5ffb0cc646473..cc5dec70c32c216f72d238c600ae64f577407e86 100644 (file)
@@ -54,19 +54,20 @@ static inline void fsnotify_move(struct inode *old_dir, struct inode *new_dir,
 
        if (isdir)
                isdir = IN_ISDIR;
-       inotify_inode_queue_event(old_dir, IN_MOVED_FROM|isdir,cookie,old_name);
-       inotify_inode_queue_event(new_dir, IN_MOVED_TO|isdir, cookie, new_name);
+       inotify_inode_queue_event(old_dir, IN_MOVED_FROM|isdir,cookie,old_name,
+                                 source);
+       inotify_inode_queue_event(new_dir, IN_MOVED_TO|isdir, cookie, new_name,
+                                 source);
 
        if (target) {
-               inotify_inode_queue_event(target, IN_DELETE_SELF, 0, NULL);
+               inotify_inode_queue_event(target, IN_DELETE_SELF, 0, NULL, NULL);
                inotify_inode_is_dead(target);
        }
 
        if (source) {
-               inotify_inode_queue_event(source, IN_MOVE_SELF, 0, NULL);
+               inotify_inode_queue_event(source, IN_MOVE_SELF, 0, NULL, NULL);
        }
-       audit_inode_child(old_name, source, old_dir->i_ino);
-       audit_inode_child(new_name, target, new_dir->i_ino);
+       audit_inode_child(new_name, source, new_dir->i_ino);
 }
 
 /*
@@ -85,7 +86,7 @@ static inline void fsnotify_nameremove(struct dentry *dentry, int isdir)
  */
 static inline void fsnotify_inoderemove(struct inode *inode)
 {
-       inotify_inode_queue_event(inode, IN_DELETE_SELF, 0, NULL);
+       inotify_inode_queue_event(inode, IN_DELETE_SELF, 0, NULL, NULL);
        inotify_inode_is_dead(inode);
 }
 
@@ -95,7 +96,8 @@ static inline void fsnotify_inoderemove(struct inode *inode)
 static inline void fsnotify_create(struct inode *inode, struct dentry *dentry)
 {
        inode_dir_notify(inode, DN_CREATE);
-       inotify_inode_queue_event(inode, IN_CREATE, 0, dentry->d_name.name);
+       inotify_inode_queue_event(inode, IN_CREATE, 0, dentry->d_name.name,
+                                 dentry->d_inode);
        audit_inode_child(dentry->d_name.name, dentry->d_inode, inode->i_ino);
 }
 
@@ -106,7 +108,7 @@ static inline void fsnotify_mkdir(struct inode *inode, struct dentry *dentry)
 {
        inode_dir_notify(inode, DN_CREATE);
        inotify_inode_queue_event(inode, IN_CREATE | IN_ISDIR, 0, 
-                                 dentry->d_name.name);
+                                 dentry->d_name.name, dentry->d_inode);
        audit_inode_child(dentry->d_name.name, dentry->d_inode, inode->i_ino);
 }
 
@@ -123,7 +125,7 @@ static inline void fsnotify_access(struct dentry *dentry)
 
        dnotify_parent(dentry, DN_ACCESS);
        inotify_dentry_parent_queue_event(dentry, mask, 0, dentry->d_name.name);
-       inotify_inode_queue_event(inode, mask, 0, NULL);
+       inotify_inode_queue_event(inode, mask, 0, NULL, NULL);
 }
 
 /*
@@ -139,7 +141,7 @@ static inline void fsnotify_modify(struct dentry *dentry)
 
        dnotify_parent(dentry, DN_MODIFY);
        inotify_dentry_parent_queue_event(dentry, mask, 0, dentry->d_name.name);
-       inotify_inode_queue_event(inode, mask, 0, NULL);
+       inotify_inode_queue_event(inode, mask, 0, NULL, NULL);
 }
 
 /*
@@ -154,7 +156,7 @@ static inline void fsnotify_open(struct dentry *dentry)
                mask |= IN_ISDIR;
 
        inotify_dentry_parent_queue_event(dentry, mask, 0, dentry->d_name.name);
-       inotify_inode_queue_event(inode, mask, 0, NULL);        
+       inotify_inode_queue_event(inode, mask, 0, NULL, NULL);
 }
 
 /*
@@ -172,7 +174,7 @@ static inline void fsnotify_close(struct file *file)
                mask |= IN_ISDIR;
 
        inotify_dentry_parent_queue_event(dentry, mask, 0, name);
-       inotify_inode_queue_event(inode, mask, 0, NULL);
+       inotify_inode_queue_event(inode, mask, 0, NULL, NULL);
 }
 
 /*
@@ -187,7 +189,7 @@ static inline void fsnotify_xattr(struct dentry *dentry)
                mask |= IN_ISDIR;
 
        inotify_dentry_parent_queue_event(dentry, mask, 0, dentry->d_name.name);
-       inotify_inode_queue_event(inode, mask, 0, NULL);
+       inotify_inode_queue_event(inode, mask, 0, NULL, NULL);
 }
 
 /*
@@ -234,7 +236,7 @@ static inline void fsnotify_change(struct dentry *dentry, unsigned int ia_valid)
        if (in_mask) {
                if (S_ISDIR(inode->i_mode))
                        in_mask |= IN_ISDIR;
-               inotify_inode_queue_event(inode, in_mask, 0, NULL);
+               inotify_inode_queue_event(inode, in_mask, 0, NULL, NULL);
                inotify_dentry_parent_queue_event(dentry, in_mask, 0,
                                                  dentry->d_name.name);
        }
index 71aa1553ef383488d1cfa6ac601d65da97560709..d4f48c6402e6ffd615b81a50a65ea7c41b47dcaf 100644 (file)
@@ -68,18 +68,65 @@ struct inotify_event {
 #include <linux/dcache.h>
 #include <linux/fs.h>
 
+/*
+ * struct inotify_watch - represents a watch request on a specific inode
+ *
+ * h_list is protected by ih->mutex of the associated inotify_handle.
+ * i_list, mask are protected by inode->inotify_mutex of the associated inode.
+ * ih, inode, and wd are never written to once the watch is created.
+ *
+ * Callers must use the established inotify interfaces to access inotify_watch
+ * contents.  The content of this structure is private to the inotify
+ * implementation.
+ */
+struct inotify_watch {
+       struct list_head        h_list; /* entry in inotify_handle's list */
+       struct list_head        i_list; /* entry in inode's list */
+       atomic_t                count;  /* reference count */
+       struct inotify_handle   *ih;    /* associated inotify handle */
+       struct inode            *inode; /* associated inode */
+       __s32                   wd;     /* watch descriptor */
+       __u32                   mask;   /* event mask for this watch */
+};
+
+struct inotify_operations {
+       void (*handle_event)(struct inotify_watch *, u32, u32, u32,
+                            const char *, struct inode *);
+       void (*destroy_watch)(struct inotify_watch *);
+};
+
 #ifdef CONFIG_INOTIFY
 
+/* Kernel API for producing events */
+
 extern void inotify_d_instantiate(struct dentry *, struct inode *);
 extern void inotify_d_move(struct dentry *);
 extern void inotify_inode_queue_event(struct inode *, __u32, __u32,
-                                     const char *);
+                                     const char *, struct inode *);
 extern void inotify_dentry_parent_queue_event(struct dentry *, __u32, __u32,
                                              const char *);
 extern void inotify_unmount_inodes(struct list_head *);
 extern void inotify_inode_is_dead(struct inode *);
 extern u32 inotify_get_cookie(void);
 
+/* Kernel Consumer API */
+
+extern struct inotify_handle *inotify_init(const struct inotify_operations *);
+extern void inotify_init_watch(struct inotify_watch *);
+extern void inotify_destroy(struct inotify_handle *);
+extern __s32 inotify_find_watch(struct inotify_handle *, struct inode *,
+                               struct inotify_watch **);
+extern __s32 inotify_find_update_watch(struct inotify_handle *, struct inode *,
+                                      u32);
+extern __s32 inotify_add_watch(struct inotify_handle *, struct inotify_watch *,
+                              struct inode *, __u32);
+extern int inotify_rm_watch(struct inotify_handle *, struct inotify_watch *);
+extern int inotify_rm_wd(struct inotify_handle *, __u32);
+extern void inotify_remove_watch_locked(struct inotify_handle *,
+                                       struct inotify_watch *);
+extern void get_inotify_watch(struct inotify_watch *);
+extern void put_inotify_watch(struct inotify_watch *);
+
 #else
 
 static inline void inotify_d_instantiate(struct dentry *dentry,
@@ -93,7 +140,8 @@ static inline void inotify_d_move(struct dentry *dentry)
 
 static inline void inotify_inode_queue_event(struct inode *inode,
                                             __u32 mask, __u32 cookie,
-                                            const char *filename)
+                                            const char *filename,
+                                            struct inode *n_inode)
 {
 }
 
@@ -116,6 +164,62 @@ static inline u32 inotify_get_cookie(void)
        return 0;
 }
 
+static inline struct inotify_handle *inotify_init(const struct inotify_operations *ops)
+{
+       return ERR_PTR(-EOPNOTSUPP);
+}
+
+static inline void inotify_init_watch(struct inotify_watch *watch)
+{
+}
+
+static inline void inotify_destroy(struct inotify_handle *ih)
+{
+}
+
+static inline __s32 inotify_find_watch(struct inotify_handle *ih, struct inode *inode,
+                                      struct inotify_watch **watchp)
+{
+       return -EOPNOTSUPP;
+}
+
+static inline __s32 inotify_find_update_watch(struct inotify_handle *ih,
+                                             struct inode *inode, u32 mask)
+{
+       return -EOPNOTSUPP;
+}
+
+static inline __s32 inotify_add_watch(struct inotify_handle *ih,
+                                     struct inotify_watch *watch,
+                                     struct inode *inode, __u32 mask)
+{
+       return -EOPNOTSUPP;
+}
+
+static inline int inotify_rm_watch(struct inotify_handle *ih,
+                                  struct inotify_watch *watch)
+{
+       return -EOPNOTSUPP;
+}
+
+static inline int inotify_rm_wd(struct inotify_handle *ih, __u32 wd)
+{
+       return -EOPNOTSUPP;
+}
+
+static inline void inotify_remove_watch_locked(struct inotify_handle *ih,
+                                              struct inotify_watch *watch)
+{
+}
+
+static inline void get_inotify_watch(struct inotify_watch *watch)
+{
+}
+
+static inline void put_inotify_watch(struct inotify_watch *watch)
+{
+}
+
 #endif /* CONFIG_INOTIFY */
 
 #endif /* __KERNEL __ */
index 701b8cbceb05637d2e2e32c4a4bcf6cbb950c133..267f1525704064422cffa4e85250a967f3bf8d92 100644 (file)
@@ -494,7 +494,7 @@ struct user_struct {
        atomic_t processes;     /* How many processes does this user have? */
        atomic_t files;         /* How many open files does this user have? */
        atomic_t sigpending;    /* How many pending signals does this user have? */
-#ifdef CONFIG_INOTIFY
+#ifdef CONFIG_INOTIFY_USER
        atomic_t inotify_watches; /* How many inotify watches does this user have? */
        atomic_t inotify_devs;  /* How many inotify devs does this user have opened? */
 #endif
index a7697787946a68d230af8d1c611dded224eb9058..df864a3582217b648786ff3ad48d2ce64fe6457e 100644 (file)
@@ -182,7 +182,8 @@ config AUDITSYSCALL
        help
          Enable low-overhead system-call auditing infrastructure that
          can be used independently or with another kernel subsystem,
-         such as SELinux.
+         such as SELinux.  To use audit's filesystem watch feature, please
+         ensure that INOTIFY is configured.
 
 config IKCONFIG
        bool "Kernel .config support"
index 41ecbd440feda782dd3ac9949632458c16fd4de8..1511714a95851dc8af01ee2421907c5b0d23725c 100644 (file)
@@ -8,6 +8,8 @@
  * Lockless receive & send, fd based notify:
  *                         Manfred Spraul          (manfred@colorfullife.com)
  *
+ * Audit:                   George Wilson           (ltcgcw@us.ibm.com)
+ *
  * This file is released under the GPL.
  */
 
@@ -24,6 +26,7 @@
 #include <linux/skbuff.h>
 #include <linux/netlink.h>
 #include <linux/syscalls.h>
+#include <linux/audit.h>
 #include <linux/signal.h>
 #include <linux/mutex.h>
 
@@ -657,6 +660,10 @@ asmlinkage long sys_mq_open(const char __user *u_name, int oflag, mode_t mode,
        char *name;
        int fd, error;
 
+       error = audit_mq_open(oflag, mode, u_attr);
+       if (error != 0)
+               return error;
+
        if (IS_ERR(name = getname(u_name)))
                return PTR_ERR(name);
 
@@ -814,6 +821,10 @@ asmlinkage long sys_mq_timedsend(mqd_t mqdes, const char __user *u_msg_ptr,
        long timeout;
        int ret;
 
+       ret = audit_mq_timedsend(mqdes, msg_len, msg_prio, u_abs_timeout);
+       if (ret != 0)
+               return ret;
+
        if (unlikely(msg_prio >= (unsigned long) MQ_PRIO_MAX))
                return -EINVAL;
 
@@ -896,6 +907,10 @@ asmlinkage ssize_t sys_mq_timedreceive(mqd_t mqdes, char __user *u_msg_ptr,
        struct mqueue_inode_info *info;
        struct ext_wait_queue wait;
 
+       ret = audit_mq_timedreceive(mqdes, msg_len, u_msg_prio, u_abs_timeout);
+       if (ret != 0)
+               return ret;
+
        timeout = prepare_timeout(u_abs_timeout);
 
        ret = -EBADF;
@@ -975,6 +990,10 @@ asmlinkage long sys_mq_notify(mqd_t mqdes,
        struct mqueue_inode_info *info;
        struct sk_buff *nc;
 
+       ret = audit_mq_notify(mqdes, u_notification);
+       if (ret != 0)
+               return ret;
+
        nc = NULL;
        sock = NULL;
        if (u_notification != NULL) {
@@ -1115,6 +1134,9 @@ asmlinkage long sys_mq_getsetattr(mqd_t mqdes,
        omqstat = info->attr;
        omqstat.mq_flags = filp->f_flags & O_NONBLOCK;
        if (u_mqstat) {
+               ret = audit_mq_getsetattr(mqdes, &mqstat);
+               if (ret != 0)
+                       goto out;
                if (mqstat.mq_flags & O_NONBLOCK)
                        filp->f_flags |= O_NONBLOCK;
                else
index 7d1340ccb16b7d333cbbc9d1b2177943d3d63737..00f015a092d292347d781a60bf9f72c71b4381d1 100644 (file)
--- a/ipc/msg.c
+++ b/ipc/msg.c
@@ -454,6 +454,11 @@ asmlinkage long sys_msgctl (int msqid, int cmd, struct msqid_ds __user *buf)
        err = audit_ipc_obj(ipcp);
        if (err)
                goto out_unlock_up;
+       if (cmd==IPC_SET) {
+               err = audit_ipc_set_perm(setbuf.qbytes, setbuf.uid, setbuf.gid, setbuf.mode);
+               if (err)
+                       goto out_unlock_up;
+       }
 
        err = -EPERM;
        if (current->euid != ipcp->cuid && 
@@ -468,10 +473,6 @@ asmlinkage long sys_msgctl (int msqid, int cmd, struct msqid_ds __user *buf)
        switch (cmd) {
        case IPC_SET:
        {
-               err = audit_ipc_set_perm(setbuf.qbytes, setbuf.uid, setbuf.gid, setbuf.mode, ipcp);
-               if (err)
-                       goto out_unlock_up;
-
                err = -EPERM;
                if (setbuf.qbytes > msg_ctlmnb && !capable(CAP_SYS_RESOURCE))
                        goto out_unlock_up;
index 7919f8ece6bac8c89cd255b367fa8dfcb722fca5..fce0bc8b5ad6f9c8652883a62c0b5ff2821f3853 100644 (file)
--- a/ipc/sem.c
+++ b/ipc/sem.c
@@ -828,6 +828,11 @@ static int semctl_down(int semid, int semnum, int cmd, int version, union semun
        if (err)
                goto out_unlock;
 
+       if (cmd == IPC_SET) {
+               err = audit_ipc_set_perm(0, setbuf.uid, setbuf.gid, setbuf.mode);
+               if (err)
+                       goto out_unlock;
+       }
        if (current->euid != ipcp->cuid && 
            current->euid != ipcp->uid && !capable(CAP_SYS_ADMIN)) {
                err=-EPERM;
@@ -844,9 +849,6 @@ static int semctl_down(int semid, int semnum, int cmd, int version, union semun
                err = 0;
                break;
        case IPC_SET:
-               err = audit_ipc_set_perm(0, setbuf.uid, setbuf.gid, setbuf.mode, ipcp);
-               if (err)
-                       goto out_unlock;
                ipcp->uid = setbuf.uid;
                ipcp->gid = setbuf.gid;
                ipcp->mode = (ipcp->mode & ~S_IRWXUGO)
index 8098968519026406191014b4cb64ee870e2a721c..4f133d24030f386aa5749455111ec1fbe4da33fe 100644 (file)
--- a/ipc/shm.c
+++ b/ipc/shm.c
@@ -643,7 +643,7 @@ asmlinkage long sys_shmctl (int shmid, int cmd, struct shmid_ds __user *buf)
                err = audit_ipc_obj(&(shp->shm_perm));
                if (err)
                        goto out_unlock_up;
-               err = audit_ipc_set_perm(0, setbuf.uid, setbuf.gid, setbuf.mode, &(shp->shm_perm));
+               err = audit_ipc_set_perm(0, setbuf.uid, setbuf.gid, setbuf.mode);
                if (err)
                        goto out_unlock_up;
                err=-EPERM;
index df57b493e1cb2c35a8d6fd7f4602751f2ac7fd6d..7dfac7031bd734f6c117e66b660b436b842e41be 100644 (file)
@@ -56,6 +56,7 @@
 #include <linux/skbuff.h>
 #include <linux/netlink.h>
 #include <linux/selinux.h>
+#include <linux/inotify.h>
 
 #include "audit.h"
 
@@ -89,6 +90,7 @@ static int    audit_backlog_wait_overflow = 0;
 /* The identity of the user shutting down the audit system. */
 uid_t          audit_sig_uid = -1;
 pid_t          audit_sig_pid = -1;
+u32            audit_sig_sid = 0;
 
 /* Records can be lost in several ways:
    0) [suppressed in audit_alloc]
@@ -102,6 +104,12 @@ static atomic_t    audit_lost = ATOMIC_INIT(0);
 /* The netlink socket. */
 static struct sock *audit_sock;
 
+/* Inotify handle. */
+struct inotify_handle *audit_ih;
+
+/* Hash for inode-based rules */
+struct list_head audit_inode_hash[AUDIT_INODE_BUCKETS];
+
 /* The audit_freelist is a list of pre-allocated audit buffers (if more
  * than AUDIT_MAXFREE are in use, the audit buffer is freed instead of
  * being placed on the freelist). */
@@ -114,10 +122,8 @@ static struct task_struct *kauditd_task;
 static DECLARE_WAIT_QUEUE_HEAD(kauditd_wait);
 static DECLARE_WAIT_QUEUE_HEAD(audit_backlog_wait);
 
-/* The netlink socket is only to be read by 1 CPU, which lets us assume
- * that list additions and deletions never happen simultaneously in
- * auditsc.c */
-DEFINE_MUTEX(audit_netlink_mutex);
+/* Serialize requests from userspace. */
+static DEFINE_MUTEX(audit_cmd_mutex);
 
 /* AUDIT_BUFSIZ is the size of the temporary buffer used for formatting
  * audit records.  Since printk uses a 1024 byte buffer, this buffer
@@ -250,7 +256,7 @@ static int audit_set_rate_limit(int limit, uid_t loginuid, u32 sid)
                        "audit_rate_limit=%d old=%d by auid=%u",
                        limit, old, loginuid);
        audit_rate_limit = limit;
-       return old;
+       return 0;
 }
 
 static int audit_set_backlog_limit(int limit, uid_t loginuid, u32 sid)
@@ -273,7 +279,7 @@ static int audit_set_backlog_limit(int limit, uid_t loginuid, u32 sid)
                        "audit_backlog_limit=%d old=%d by auid=%u",
                        limit, old, loginuid);
        audit_backlog_limit = limit;
-       return old;
+       return 0;
 }
 
 static int audit_set_enabled(int state, uid_t loginuid, u32 sid)
@@ -299,7 +305,7 @@ static int audit_set_enabled(int state, uid_t loginuid, u32 sid)
                        "audit_enabled=%d old=%d by auid=%u",
                        state, old, loginuid);
        audit_enabled = state;
-       return old;
+       return 0;
 }
 
 static int audit_set_failure(int state, uid_t loginuid, u32 sid)
@@ -327,7 +333,7 @@ static int audit_set_failure(int state, uid_t loginuid, u32 sid)
                        "audit_failure=%d old=%d by auid=%u",
                        state, old, loginuid);
        audit_failure = state;
-       return old;
+       return 0;
 }
 
 static int kauditd_thread(void *dummy)
@@ -363,9 +369,52 @@ static int kauditd_thread(void *dummy)
                        remove_wait_queue(&kauditd_wait, &wait);
                }
        }
+}
+
+int audit_send_list(void *_dest)
+{
+       struct audit_netlink_list *dest = _dest;
+       int pid = dest->pid;
+       struct sk_buff *skb;
+
+       /* wait for parent to finish and send an ACK */
+       mutex_lock(&audit_cmd_mutex);
+       mutex_unlock(&audit_cmd_mutex);
+
+       while ((skb = __skb_dequeue(&dest->q)) != NULL)
+               netlink_unicast(audit_sock, skb, pid, 0);
+
+       kfree(dest);
+
        return 0;
 }
 
+struct sk_buff *audit_make_reply(int pid, int seq, int type, int done,
+                                int multi, void *payload, int size)
+{
+       struct sk_buff  *skb;
+       struct nlmsghdr *nlh;
+       int             len = NLMSG_SPACE(size);
+       void            *data;
+       int             flags = multi ? NLM_F_MULTI : 0;
+       int             t     = done  ? NLMSG_DONE  : type;
+
+       skb = alloc_skb(len, GFP_KERNEL);
+       if (!skb)
+               return NULL;
+
+       nlh              = NLMSG_PUT(skb, pid, seq, t, size);
+       nlh->nlmsg_flags = flags;
+       data             = NLMSG_DATA(nlh);
+       memcpy(data, payload, size);
+       return skb;
+
+nlmsg_failure:                 /* Used by NLMSG_PUT */
+       if (skb)
+               kfree_skb(skb);
+       return NULL;
+}
+
 /**
  * audit_send_reply - send an audit reply message via netlink
  * @pid: process id to send reply to
@@ -383,29 +432,13 @@ void audit_send_reply(int pid, int seq, int type, int done, int multi,
                      void *payload, int size)
 {
        struct sk_buff  *skb;
-       struct nlmsghdr *nlh;
-       int             len = NLMSG_SPACE(size);
-       void            *data;
-       int             flags = multi ? NLM_F_MULTI : 0;
-       int             t     = done  ? NLMSG_DONE  : type;
-
-       skb = alloc_skb(len, GFP_KERNEL);
+       skb = audit_make_reply(pid, seq, type, done, multi, payload, size);
        if (!skb)
                return;
-
-       nlh              = NLMSG_PUT(skb, pid, seq, t, size);
-       nlh->nlmsg_flags = flags;
-       data             = NLMSG_DATA(nlh);
-       memcpy(data, payload, size);
-
        /* Ignore failure. It'll only happen if the sender goes away,
           because our timeout is set to infinite. */
        netlink_unicast(audit_sock, skb, pid, 0);
        return;
-
-nlmsg_failure:                 /* Used by NLMSG_PUT */
-       if (skb)
-               kfree_skb(skb);
 }
 
 /*
@@ -451,7 +484,9 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
        struct audit_buffer     *ab;
        u16                     msg_type = nlh->nlmsg_type;
        uid_t                   loginuid; /* loginuid of sender */
-       struct audit_sig_info   sig_data;
+       struct audit_sig_info   *sig_data;
+       char                    *ctx;
+       u32                     len;
 
        err = audit_netlink_ok(NETLINK_CB(skb).eff_cap, msg_type);
        if (err)
@@ -503,12 +538,9 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
                if (status_get->mask & AUDIT_STATUS_PID) {
                        int old   = audit_pid;
                        if (sid) {
-                               char *ctx = NULL;
-                               u32 len;
-                               int rc;
-                               if ((rc = selinux_ctxid_to_string(
+                               if ((err = selinux_ctxid_to_string(
                                                sid, &ctx, &len)))
-                                       return rc;
+                                       return err;
                                else
                                        audit_log(NULL, GFP_KERNEL,
                                                AUDIT_CONFIG_CHANGE,
@@ -523,10 +555,10 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
                        audit_pid = status_get->pid;
                }
                if (status_get->mask & AUDIT_STATUS_RATE_LIMIT)
-                       audit_set_rate_limit(status_get->rate_limit,
+                       err = audit_set_rate_limit(status_get->rate_limit,
                                                         loginuid, sid);
                if (status_get->mask & AUDIT_STATUS_BACKLOG_LIMIT)
-                       audit_set_backlog_limit(status_get->backlog_limit,
+                       err = audit_set_backlog_limit(status_get->backlog_limit,
                                                        loginuid, sid);
                break;
        case AUDIT_USER:
@@ -544,8 +576,6 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
                                                 "user pid=%d uid=%u auid=%u",
                                                 pid, uid, loginuid);
                                if (sid) {
-                                       char *ctx = NULL;
-                                       u32 len;
                                        if (selinux_ctxid_to_string(
                                                        sid, &ctx, &len)) {
                                                audit_log_format(ab, 
@@ -584,10 +614,21 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
                                           loginuid, sid);
                break;
        case AUDIT_SIGNAL_INFO:
-               sig_data.uid = audit_sig_uid;
-               sig_data.pid = audit_sig_pid;
+               err = selinux_ctxid_to_string(audit_sig_sid, &ctx, &len);
+               if (err)
+                       return err;
+               sig_data = kmalloc(sizeof(*sig_data) + len, GFP_KERNEL);
+               if (!sig_data) {
+                       kfree(ctx);
+                       return -ENOMEM;
+               }
+               sig_data->uid = audit_sig_uid;
+               sig_data->pid = audit_sig_pid;
+               memcpy(sig_data->ctx, ctx, len);
+               kfree(ctx);
                audit_send_reply(NETLINK_CB(skb).pid, seq, AUDIT_SIGNAL_INFO, 
-                               0, 0, &sig_data, sizeof(sig_data));
+                               0, 0, sig_data, sizeof(*sig_data) + len);
+               kfree(sig_data);
                break;
        default:
                err = -EINVAL;
@@ -629,20 +670,30 @@ static void audit_receive(struct sock *sk, int length)
        struct sk_buff  *skb;
        unsigned int qlen;
 
-       mutex_lock(&audit_netlink_mutex);
+       mutex_lock(&audit_cmd_mutex);
 
        for (qlen = skb_queue_len(&sk->sk_receive_queue); qlen; qlen--) {
                skb = skb_dequeue(&sk->sk_receive_queue);
                audit_receive_skb(skb);
                kfree_skb(skb);
        }
-       mutex_unlock(&audit_netlink_mutex);
+       mutex_unlock(&audit_cmd_mutex);
 }
 
+#ifdef CONFIG_AUDITSYSCALL
+static const struct inotify_operations audit_inotify_ops = {
+       .handle_event   = audit_handle_ievent,
+       .destroy_watch  = audit_free_parent,
+};
+#endif
 
 /* Initialize audit support at boot time. */
 static int __init audit_init(void)
 {
+#ifdef CONFIG_AUDITSYSCALL
+       int i;
+#endif
+
        printk(KERN_INFO "audit: initializing netlink socket (%s)\n",
               audit_default ? "enabled" : "disabled");
        audit_sock = netlink_kernel_create(NETLINK_AUDIT, 0, audit_receive,
@@ -661,6 +712,16 @@ static int __init audit_init(void)
        selinux_audit_set_callback(&selinux_audit_rule_update);
 
        audit_log(NULL, GFP_KERNEL, AUDIT_KERNEL, "initialized");
+
+#ifdef CONFIG_AUDITSYSCALL
+       audit_ih = inotify_init(&audit_inotify_ops);
+       if (IS_ERR(audit_ih))
+               audit_panic("cannot initialize inotify handle");
+
+       for (i = 0; i < AUDIT_INODE_BUCKETS; i++)
+               INIT_LIST_HEAD(&audit_inode_hash[i]);
+#endif
+
        return 0;
 }
 __initcall(audit_init);
@@ -690,10 +751,12 @@ static void audit_buffer_free(struct audit_buffer *ab)
                kfree_skb(ab->skb);
 
        spin_lock_irqsave(&audit_freelist_lock, flags);
-       if (++audit_freelist_count > AUDIT_MAXFREE)
+       if (audit_freelist_count > AUDIT_MAXFREE)
                kfree(ab);
-       else
+       else {
+               audit_freelist_count++;
                list_add(&ab->list, &audit_freelist);
+       }
        spin_unlock_irqrestore(&audit_freelist_lock, flags);
 }
 
@@ -988,28 +1051,76 @@ void audit_log_hex(struct audit_buffer *ab, const unsigned char *buf,
        skb_put(skb, len << 1); /* new string is twice the old string */
 }
 
+/*
+ * Format a string of no more than slen characters into the audit buffer,
+ * enclosed in quote marks.
+ */
+static void audit_log_n_string(struct audit_buffer *ab, size_t slen,
+                              const char *string)
+{
+       int avail, new_len;
+       unsigned char *ptr;
+       struct sk_buff *skb;
+
+       BUG_ON(!ab->skb);
+       skb = ab->skb;
+       avail = skb_tailroom(skb);
+       new_len = slen + 3;     /* enclosing quotes + null terminator */
+       if (new_len > avail) {
+               avail = audit_expand(ab, new_len);
+               if (!avail)
+                       return;
+       }
+       ptr = skb->tail;
+       *ptr++ = '"';
+       memcpy(ptr, string, slen);
+       ptr += slen;
+       *ptr++ = '"';
+       *ptr = 0;
+       skb_put(skb, slen + 2); /* don't include null terminator */
+}
+
 /**
- * audit_log_unstrustedstring - log a string that may contain random characters
+ * audit_log_n_unstrustedstring - log a string that may contain random characters
  * @ab: audit_buffer
+ * @len: lenth of string (not including trailing null)
  * @string: string to be logged
  *
  * This code will escape a string that is passed to it if the string
  * contains a control character, unprintable character, double quote mark,
  * or a space. Unescaped strings will start and end with a double quote mark.
  * Strings that are escaped are printed in hex (2 digits per char).
+ *
+ * The caller specifies the number of characters in the string to log, which may
+ * or may not be the entire string.
  */
-void audit_log_untrustedstring(struct audit_buffer *ab, const char *string)
+const char *audit_log_n_untrustedstring(struct audit_buffer *ab, size_t len,
+                                       const char *string)
 {
        const unsigned char *p = string;
 
        while (*p) {
                if (*p == '"' || *p < 0x21 || *p > 0x7f) {
-                       audit_log_hex(ab, string, strlen(string));
-                       return;
+                       audit_log_hex(ab, string, len);
+                       return string + len + 1;
                }
                p++;
        }
-       audit_log_format(ab, "\"%s\"", string);
+       audit_log_n_string(ab, len, string);
+       return p + 1;
+}
+
+/**
+ * audit_log_unstrustedstring - log a string that may contain random characters
+ * @ab: audit_buffer
+ * @string: string to be logged
+ *
+ * Same as audit_log_n_unstrustedstring(), except that strlen is used to
+ * determine string length.
+ */
+const char *audit_log_untrustedstring(struct audit_buffer *ab, const char *string)
+{
+       return audit_log_n_untrustedstring(ab, strlen(string), string);
 }
 
 /* This is a helper-function to print the escaped d_path */
index 6f733920fd32e1cc88fe6840620720ec8cb62978..8323e4132a3308fe2d679eeb167c6c9b9f86a830 100644 (file)
@@ -19,9 +19,9 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
 
-#include <linux/mutex.h>
 #include <linux/fs.h>
 #include <linux/audit.h>
+#include <linux/skbuff.h>
 
 /* 0 = no checking
    1 = put_count checking
@@ -53,6 +53,18 @@ enum audit_state {
 };
 
 /* Rule lists */
+struct audit_parent;
+
+struct audit_watch {
+       atomic_t                count;  /* reference count */
+       char                    *path;  /* insertion path */
+       dev_t                   dev;    /* associated superblock device */
+       unsigned long           ino;    /* associated inode number */
+       struct audit_parent     *parent; /* associated parent */
+       struct list_head        wlist;  /* entry in parent->watches list */
+       struct list_head        rules;  /* associated rules */
+};
+
 struct audit_field {
        u32                             type;
        u32                             val;
@@ -70,6 +82,9 @@ struct audit_krule {
        u32                     buflen; /* for data alloc on list rules */
        u32                     field_count;
        struct audit_field      *fields;
+       struct audit_field      *inode_f; /* quick access to an inode field */
+       struct audit_watch      *watch; /* associated watch */
+       struct list_head        rlist;  /* entry in audit_watch.rules list */
 };
 
 struct audit_entry {
@@ -78,15 +93,53 @@ struct audit_entry {
        struct audit_krule      rule;
 };
 
-
 extern int audit_pid;
-extern int audit_comparator(const u32 left, const u32 op, const u32 right);
 
+#define AUDIT_INODE_BUCKETS    32
+extern struct list_head audit_inode_hash[AUDIT_INODE_BUCKETS];
+
+static inline int audit_hash_ino(u32 ino)
+{
+       return (ino & (AUDIT_INODE_BUCKETS-1));
+}
+
+extern int audit_comparator(const u32 left, const u32 op, const u32 right);
+extern int audit_compare_dname_path(const char *dname, const char *path,
+                                   int *dirlen);
+extern struct sk_buff *            audit_make_reply(int pid, int seq, int type,
+                                            int done, int multi,
+                                            void *payload, int size);
 extern void                audit_send_reply(int pid, int seq, int type,
                                             int done, int multi,
                                             void *payload, int size);
 extern void                audit_log_lost(const char *message);
 extern void                audit_panic(const char *message);
-extern struct mutex audit_netlink_mutex;
 
+struct audit_netlink_list {
+       int pid;
+       struct sk_buff_head q;
+};
+
+int audit_send_list(void *);
+
+struct inotify_watch;
+extern void audit_free_parent(struct inotify_watch *);
+extern void audit_handle_ievent(struct inotify_watch *, u32, u32, u32,
+                               const char *, struct inode *);
 extern int selinux_audit_rule_update(void);
+
+#ifdef CONFIG_AUDITSYSCALL
+extern void __audit_signal_info(int sig, struct task_struct *t);
+static inline void audit_signal_info(int sig, struct task_struct *t)
+{
+       if (unlikely(audit_pid && t->tgid == audit_pid))
+               __audit_signal_info(sig, t);
+}
+extern enum audit_state audit_filter_inodes(struct task_struct *,
+                                           struct audit_context *);
+extern void audit_set_auditable(struct audit_context *);
+#else
+#define audit_signal_info(s,t)
+#define audit_filter_inodes(t,c) AUDIT_DISABLED
+#define audit_set_auditable(c)
+#endif
index 7c134906d689c8af18397351089dc01fbbf00461..4c99d2c586edcb2549f27e3e0d18f68e77c48763 100644 (file)
 #include <linux/kernel.h>
 #include <linux/audit.h>
 #include <linux/kthread.h>
+#include <linux/mutex.h>
+#include <linux/fs.h>
+#include <linux/namei.h>
 #include <linux/netlink.h>
+#include <linux/sched.h>
+#include <linux/inotify.h>
 #include <linux/selinux.h>
 #include "audit.h"
 
-/* There are three lists of rules -- one to search at task creation
- * time, one to search at syscall entry time, and another to search at
- * syscall exit time. */
+/*
+ * Locking model:
+ *
+ * audit_filter_mutex:
+ *             Synchronizes writes and blocking reads of audit's filterlist
+ *             data.  Rcu is used to traverse the filterlist and access
+ *             contents of structs audit_entry, audit_watch and opaque
+ *             selinux rules during filtering.  If modified, these structures
+ *             must be copied and replace their counterparts in the filterlist.
+ *             An audit_parent struct is not accessed during filtering, so may
+ *             be written directly provided audit_filter_mutex is held.
+ */
+
+/*
+ * Reference counting:
+ *
+ * audit_parent: lifetime is from audit_init_parent() to receipt of an IN_IGNORED
+ *     event.  Each audit_watch holds a reference to its associated parent.
+ *
+ * audit_watch: if added to lists, lifetime is from audit_init_watch() to
+ *     audit_remove_watch().  Additionally, an audit_watch may exist
+ *     temporarily to assist in searching existing filter data.  Each
+ *     audit_krule holds a reference to its associated watch.
+ */
+
+struct audit_parent {
+       struct list_head        ilist;  /* entry in inotify registration list */
+       struct list_head        watches; /* associated watches */
+       struct inotify_watch    wdata;  /* inotify watch data */
+       unsigned                flags;  /* status flags */
+};
+
+/*
+ * audit_parent status flags:
+ *
+ * AUDIT_PARENT_INVALID - set anytime rules/watches are auto-removed due to
+ * a filesystem event to ensure we're adding audit watches to a valid parent.
+ * Technically not needed for IN_DELETE_SELF or IN_UNMOUNT events, as we cannot
+ * receive them while we have nameidata, but must be used for IN_MOVE_SELF which
+ * we can receive while holding nameidata.
+ */
+#define AUDIT_PARENT_INVALID   0x001
+
+/* Audit filter lists, defined in <linux/audit.h> */
 struct list_head audit_filter_list[AUDIT_NR_FILTERS] = {
        LIST_HEAD_INIT(audit_filter_list[0]),
        LIST_HEAD_INIT(audit_filter_list[1]),
@@ -41,9 +87,53 @@ struct list_head audit_filter_list[AUDIT_NR_FILTERS] = {
 #endif
 };
 
+static DEFINE_MUTEX(audit_filter_mutex);
+
+/* Inotify handle */
+extern struct inotify_handle *audit_ih;
+
+/* Inotify events we care about. */
+#define AUDIT_IN_WATCH IN_MOVE|IN_CREATE|IN_DELETE|IN_DELETE_SELF|IN_MOVE_SELF
+
+void audit_free_parent(struct inotify_watch *i_watch)
+{
+       struct audit_parent *parent;
+
+       parent = container_of(i_watch, struct audit_parent, wdata);
+       WARN_ON(!list_empty(&parent->watches));
+       kfree(parent);
+}
+
+static inline void audit_get_watch(struct audit_watch *watch)
+{
+       atomic_inc(&watch->count);
+}
+
+static void audit_put_watch(struct audit_watch *watch)
+{
+       if (atomic_dec_and_test(&watch->count)) {
+               WARN_ON(watch->parent);
+               WARN_ON(!list_empty(&watch->rules));
+               kfree(watch->path);
+               kfree(watch);
+       }
+}
+
+static void audit_remove_watch(struct audit_watch *watch)
+{
+       list_del(&watch->wlist);
+       put_inotify_watch(&watch->parent->wdata);
+       watch->parent = NULL;
+       audit_put_watch(watch); /* match initial get */
+}
+
 static inline void audit_free_rule(struct audit_entry *e)
 {
        int i;
+
+       /* some rules don't have associated watches */
+       if (e->rule.watch)
+               audit_put_watch(e->rule.watch);
        if (e->rule.fields)
                for (i = 0; i < e->rule.field_count; i++) {
                        struct audit_field *f = &e->rule.fields[i];
@@ -60,6 +150,50 @@ static inline void audit_free_rule_rcu(struct rcu_head *head)
        audit_free_rule(e);
 }
 
+/* Initialize a parent watch entry. */
+static struct audit_parent *audit_init_parent(struct nameidata *ndp)
+{
+       struct audit_parent *parent;
+       s32 wd;
+
+       parent = kzalloc(sizeof(*parent), GFP_KERNEL);
+       if (unlikely(!parent))
+               return ERR_PTR(-ENOMEM);
+
+       INIT_LIST_HEAD(&parent->watches);
+       parent->flags = 0;
+
+       inotify_init_watch(&parent->wdata);
+       /* grab a ref so inotify watch hangs around until we take audit_filter_mutex */
+       get_inotify_watch(&parent->wdata);
+       wd = inotify_add_watch(audit_ih, &parent->wdata, ndp->dentry->d_inode,
+                              AUDIT_IN_WATCH);
+       if (wd < 0) {
+               audit_free_parent(&parent->wdata);
+               return ERR_PTR(wd);
+       }
+
+       return parent;
+}
+
+/* Initialize a watch entry. */
+static struct audit_watch *audit_init_watch(char *path)
+{
+       struct audit_watch *watch;
+
+       watch = kzalloc(sizeof(*watch), GFP_KERNEL);
+       if (unlikely(!watch))
+               return ERR_PTR(-ENOMEM);
+
+       INIT_LIST_HEAD(&watch->rules);
+       atomic_set(&watch->count, 1);
+       watch->path = path;
+       watch->dev = (dev_t)-1;
+       watch->ino = (unsigned long)-1;
+
+       return watch;
+}
+
 /* Initialize an audit filterlist entry. */
 static inline struct audit_entry *audit_init_entry(u32 field_count)
 {
@@ -107,6 +241,43 @@ static char *audit_unpack_string(void **bufp, size_t *remain, size_t len)
        return str;
 }
 
+/* Translate an inode field to kernel respresentation. */
+static inline int audit_to_inode(struct audit_krule *krule,
+                                struct audit_field *f)
+{
+       if (krule->listnr != AUDIT_FILTER_EXIT ||
+           krule->watch || krule->inode_f)
+               return -EINVAL;
+
+       krule->inode_f = f;
+       return 0;
+}
+
+/* Translate a watch string to kernel respresentation. */
+static int audit_to_watch(struct audit_krule *krule, char *path, int len,
+                         u32 op)
+{
+       struct audit_watch *watch;
+
+       if (!audit_ih)
+               return -EOPNOTSUPP;
+
+       if (path[0] != '/' || path[len-1] == '/' ||
+           krule->listnr != AUDIT_FILTER_EXIT ||
+           op & ~AUDIT_EQUAL ||
+           krule->inode_f || krule->watch) /* 1 inode # per rule, for hash */
+               return -EINVAL;
+
+       watch = audit_init_watch(path);
+       if (unlikely(IS_ERR(watch)))
+               return PTR_ERR(watch);
+
+       audit_get_watch(watch);
+       krule->watch = watch;
+
+       return 0;
+}
+
 /* Common user-space to kernel rule translation. */
 static inline struct audit_entry *audit_to_entry_common(struct audit_rule *rule)
 {
@@ -128,8 +299,11 @@ static inline struct audit_entry *audit_to_entry_common(struct audit_rule *rule)
 #endif
                ;
        }
-       if (rule->action != AUDIT_NEVER && rule->action != AUDIT_POSSIBLE &&
-           rule->action != AUDIT_ALWAYS)
+       if (unlikely(rule->action == AUDIT_POSSIBLE)) {
+               printk(KERN_ERR "AUDIT_POSSIBLE is deprecated\n");
+               goto exit_err;
+       }
+       if (rule->action != AUDIT_NEVER && rule->action != AUDIT_ALWAYS)
                goto exit_err;
        if (rule->field_count > AUDIT_MAX_FIELDS)
                goto exit_err;
@@ -158,6 +332,7 @@ exit_err:
 static struct audit_entry *audit_rule_to_entry(struct audit_rule *rule)
 {
        struct audit_entry *entry;
+       struct audit_field *f;
        int err = 0;
        int i;
 
@@ -172,14 +347,37 @@ static struct audit_entry *audit_rule_to_entry(struct audit_rule *rule)
                f->type = rule->fields[i] & ~(AUDIT_NEGATE|AUDIT_OPERATORS);
                f->val = rule->values[i];
 
-               if (f->type & AUDIT_UNUSED_BITS ||
-                   f->type == AUDIT_SE_USER ||
-                   f->type == AUDIT_SE_ROLE ||
-                   f->type == AUDIT_SE_TYPE ||
-                   f->type == AUDIT_SE_SEN ||
-                   f->type == AUDIT_SE_CLR) {
-                       err = -EINVAL;
+               err = -EINVAL;
+               switch(f->type) {
+               default:
                        goto exit_free;
+               case AUDIT_PID:
+               case AUDIT_UID:
+               case AUDIT_EUID:
+               case AUDIT_SUID:
+               case AUDIT_FSUID:
+               case AUDIT_GID:
+               case AUDIT_EGID:
+               case AUDIT_SGID:
+               case AUDIT_FSGID:
+               case AUDIT_LOGINUID:
+               case AUDIT_PERS:
+               case AUDIT_ARCH:
+               case AUDIT_MSGTYPE:
+               case AUDIT_DEVMAJOR:
+               case AUDIT_DEVMINOR:
+               case AUDIT_EXIT:
+               case AUDIT_SUCCESS:
+               case AUDIT_ARG0:
+               case AUDIT_ARG1:
+               case AUDIT_ARG2:
+               case AUDIT_ARG3:
+                       break;
+               case AUDIT_INODE:
+                       err = audit_to_inode(&entry->rule, f);
+                       if (err)
+                               goto exit_free;
+                       break;
                }
 
                entry->rule.vers_ops = (f->op & AUDIT_OPERATORS) ? 2 : 1;
@@ -196,6 +394,18 @@ static struct audit_entry *audit_rule_to_entry(struct audit_rule *rule)
                }
        }
 
+       f = entry->rule.inode_f;
+       if (f) {
+               switch(f->op) {
+               case AUDIT_NOT_EQUAL:
+                       entry->rule.inode_f = NULL;
+               case AUDIT_EQUAL:
+                       break;
+               default:
+                       goto exit_free;
+               }
+       }
+
 exit_nofree:
        return entry;
 
@@ -210,6 +420,7 @@ static struct audit_entry *audit_data_to_entry(struct audit_rule_data *data,
 {
        int err = 0;
        struct audit_entry *entry;
+       struct audit_field *f;
        void *bufp;
        size_t remain = datasz - sizeof(struct audit_rule_data);
        int i;
@@ -235,6 +446,29 @@ static struct audit_entry *audit_data_to_entry(struct audit_rule_data *data,
                f->se_str = NULL;
                f->se_rule = NULL;
                switch(f->type) {
+               case AUDIT_PID:
+               case AUDIT_UID:
+               case AUDIT_EUID:
+               case AUDIT_SUID:
+               case AUDIT_FSUID:
+               case AUDIT_GID:
+               case AUDIT_EGID:
+               case AUDIT_SGID:
+               case AUDIT_FSGID:
+               case AUDIT_LOGINUID:
+               case AUDIT_PERS:
+               case AUDIT_ARCH:
+               case AUDIT_MSGTYPE:
+               case AUDIT_PPID:
+               case AUDIT_DEVMAJOR:
+               case AUDIT_DEVMINOR:
+               case AUDIT_EXIT:
+               case AUDIT_SUCCESS:
+               case AUDIT_ARG0:
+               case AUDIT_ARG1:
+               case AUDIT_ARG2:
+               case AUDIT_ARG3:
+                       break;
                case AUDIT_SE_USER:
                case AUDIT_SE_ROLE:
                case AUDIT_SE_TYPE:
@@ -260,6 +494,37 @@ static struct audit_entry *audit_data_to_entry(struct audit_rule_data *data,
                        } else
                                f->se_str = str;
                        break;
+               case AUDIT_WATCH:
+                       str = audit_unpack_string(&bufp, &remain, f->val);
+                       if (IS_ERR(str))
+                               goto exit_free;
+                       entry->rule.buflen += f->val;
+
+                       err = audit_to_watch(&entry->rule, str, f->val, f->op);
+                       if (err) {
+                               kfree(str);
+                               goto exit_free;
+                       }
+                       break;
+               case AUDIT_INODE:
+                       err = audit_to_inode(&entry->rule, f);
+                       if (err)
+                               goto exit_free;
+                       break;
+               default:
+                       goto exit_free;
+               }
+       }
+
+       f = entry->rule.inode_f;
+       if (f) {
+               switch(f->op) {
+               case AUDIT_NOT_EQUAL:
+                       entry->rule.inode_f = NULL;
+               case AUDIT_EQUAL:
+                       break;
+               default:
+                       goto exit_free;
                }
        }
 
@@ -291,7 +556,7 @@ static struct audit_rule *audit_krule_to_rule(struct audit_krule *krule)
 
        rule = kmalloc(sizeof(*rule), GFP_KERNEL);
        if (unlikely(!rule))
-               return ERR_PTR(-ENOMEM);
+               return NULL;
        memset(rule, 0, sizeof(*rule));
 
        rule->flags = krule->flags | krule->listnr;
@@ -322,7 +587,7 @@ static struct audit_rule_data *audit_krule_to_data(struct audit_krule *krule)
 
        data = kmalloc(sizeof(*data) + krule->buflen, GFP_KERNEL);
        if (unlikely(!data))
-               return ERR_PTR(-ENOMEM);
+               return NULL;
        memset(data, 0, sizeof(*data));
 
        data->flags = krule->flags | krule->listnr;
@@ -343,6 +608,10 @@ static struct audit_rule_data *audit_krule_to_data(struct audit_krule *krule)
                        data->buflen += data->values[i] =
                                audit_pack_string(&bufp, f->se_str);
                        break;
+               case AUDIT_WATCH:
+                       data->buflen += data->values[i] =
+                               audit_pack_string(&bufp, krule->watch->path);
+                       break;
                default:
                        data->values[i] = f->val;
                }
@@ -378,6 +647,10 @@ static int audit_compare_rule(struct audit_krule *a, struct audit_krule *b)
                        if (strcmp(a->fields[i].se_str, b->fields[i].se_str))
                                return 1;
                        break;
+               case AUDIT_WATCH:
+                       if (strcmp(a->watch->path, b->watch->path))
+                               return 1;
+                       break;
                default:
                        if (a->fields[i].val != b->fields[i].val)
                                return 1;
@@ -391,6 +664,32 @@ static int audit_compare_rule(struct audit_krule *a, struct audit_krule *b)
        return 0;
 }
 
+/* Duplicate the given audit watch.  The new watch's rules list is initialized
+ * to an empty list and wlist is undefined. */
+static struct audit_watch *audit_dupe_watch(struct audit_watch *old)
+{
+       char *path;
+       struct audit_watch *new;
+
+       path = kstrdup(old->path, GFP_KERNEL);
+       if (unlikely(!path))
+               return ERR_PTR(-ENOMEM);
+
+       new = audit_init_watch(path);
+       if (unlikely(IS_ERR(new))) {
+               kfree(path);
+               goto out;
+       }
+
+       new->dev = old->dev;
+       new->ino = old->ino;
+       get_inotify_watch(&old->parent->wdata);
+       new->parent = old->parent;
+
+out:
+       return new;
+}
+
 /* Duplicate selinux field information.  The se_rule is opaque, so must be
  * re-initialized. */
 static inline int audit_dupe_selinux_field(struct audit_field *df,
@@ -422,8 +721,11 @@ static inline int audit_dupe_selinux_field(struct audit_field *df,
 /* Duplicate an audit rule.  This will be a deep copy with the exception
  * of the watch - that pointer is carried over.  The selinux specific fields
  * will be updated in the copy.  The point is to be able to replace the old
- * rule with the new rule in the filterlist, then free the old rule. */
-static struct audit_entry *audit_dupe_rule(struct audit_krule *old)
+ * rule with the new rule in the filterlist, then free the old rule.
+ * The rlist element is undefined; list manipulations are handled apart from
+ * the initial copy. */
+static struct audit_entry *audit_dupe_rule(struct audit_krule *old,
+                                          struct audit_watch *watch)
 {
        u32 fcount = old->field_count;
        struct audit_entry *entry;
@@ -442,6 +744,8 @@ static struct audit_entry *audit_dupe_rule(struct audit_krule *old)
        for (i = 0; i < AUDIT_BITMASK_SIZE; i++)
                new->mask[i] = old->mask[i];
        new->buflen = old->buflen;
+       new->inode_f = old->inode_f;
+       new->watch = NULL;
        new->field_count = old->field_count;
        memcpy(new->fields, old->fields, sizeof(struct audit_field) * fcount);
 
@@ -463,68 +767,409 @@ static struct audit_entry *audit_dupe_rule(struct audit_krule *old)
                }
        }
 
+       if (watch) {
+               audit_get_watch(watch);
+               new->watch = watch;
+       }
+
        return entry;
 }
 
-/* Add rule to given filterlist if not a duplicate.  Protected by
- * audit_netlink_mutex. */
+/* Update inode info in audit rules based on filesystem event. */
+static void audit_update_watch(struct audit_parent *parent,
+                              const char *dname, dev_t dev,
+                              unsigned long ino, unsigned invalidating)
+{
+       struct audit_watch *owatch, *nwatch, *nextw;
+       struct audit_krule *r, *nextr;
+       struct audit_entry *oentry, *nentry;
+       struct audit_buffer *ab;
+
+       mutex_lock(&audit_filter_mutex);
+       list_for_each_entry_safe(owatch, nextw, &parent->watches, wlist) {
+               if (audit_compare_dname_path(dname, owatch->path, NULL))
+                       continue;
+
+               /* If the update involves invalidating rules, do the inode-based
+                * filtering now, so we don't omit records. */
+               if (invalidating &&
+                   audit_filter_inodes(current, current->audit_context) == AUDIT_RECORD_CONTEXT)
+                       audit_set_auditable(current->audit_context);
+
+               nwatch = audit_dupe_watch(owatch);
+               if (unlikely(IS_ERR(nwatch))) {
+                       mutex_unlock(&audit_filter_mutex);
+                       audit_panic("error updating watch, skipping");
+                       return;
+               }
+               nwatch->dev = dev;
+               nwatch->ino = ino;
+
+               list_for_each_entry_safe(r, nextr, &owatch->rules, rlist) {
+
+                       oentry = container_of(r, struct audit_entry, rule);
+                       list_del(&oentry->rule.rlist);
+                       list_del_rcu(&oentry->list);
+
+                       nentry = audit_dupe_rule(&oentry->rule, nwatch);
+                       if (unlikely(IS_ERR(nentry)))
+                               audit_panic("error updating watch, removing");
+                       else {
+                               int h = audit_hash_ino((u32)ino);
+                               list_add(&nentry->rule.rlist, &nwatch->rules);
+                               list_add_rcu(&nentry->list, &audit_inode_hash[h]);
+                       }
+
+                       call_rcu(&oentry->rcu, audit_free_rule_rcu);
+               }
+
+               ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE);
+               audit_log_format(ab, "audit updated rules specifying watch=");
+               audit_log_untrustedstring(ab, owatch->path);
+               audit_log_format(ab, " with dev=%u ino=%lu\n", dev, ino);
+               audit_log_end(ab);
+
+               audit_remove_watch(owatch);
+               goto add_watch_to_parent; /* event applies to a single watch */
+       }
+       mutex_unlock(&audit_filter_mutex);
+       return;
+
+add_watch_to_parent:
+       list_add(&nwatch->wlist, &parent->watches);
+       mutex_unlock(&audit_filter_mutex);
+       return;
+}
+
+/* Remove all watches & rules associated with a parent that is going away. */
+static void audit_remove_parent_watches(struct audit_parent *parent)
+{
+       struct audit_watch *w, *nextw;
+       struct audit_krule *r, *nextr;
+       struct audit_entry *e;
+
+       mutex_lock(&audit_filter_mutex);
+       parent->flags |= AUDIT_PARENT_INVALID;
+       list_for_each_entry_safe(w, nextw, &parent->watches, wlist) {
+               list_for_each_entry_safe(r, nextr, &w->rules, rlist) {
+                       e = container_of(r, struct audit_entry, rule);
+                       list_del(&r->rlist);
+                       list_del_rcu(&e->list);
+                       call_rcu(&e->rcu, audit_free_rule_rcu);
+
+                       audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE,
+                                "audit implicitly removed rule from list=%d\n",
+                                 AUDIT_FILTER_EXIT);
+               }
+               audit_remove_watch(w);
+       }
+       mutex_unlock(&audit_filter_mutex);
+}
+
+/* Unregister inotify watches for parents on in_list.
+ * Generates an IN_IGNORED event. */
+static void audit_inotify_unregister(struct list_head *in_list)
+{
+       struct audit_parent *p, *n;
+
+       list_for_each_entry_safe(p, n, in_list, ilist) {
+               list_del(&p->ilist);
+               inotify_rm_watch(audit_ih, &p->wdata);
+               /* the put matching the get in audit_do_del_rule() */
+               put_inotify_watch(&p->wdata);
+       }
+}
+
+/* Find an existing audit rule.
+ * Caller must hold audit_filter_mutex to prevent stale rule data. */
+static struct audit_entry *audit_find_rule(struct audit_entry *entry,
+                                          struct list_head *list)
+{
+       struct audit_entry *e, *found = NULL;
+       int h;
+
+       if (entry->rule.watch) {
+               /* we don't know the inode number, so must walk entire hash */
+               for (h = 0; h < AUDIT_INODE_BUCKETS; h++) {
+                       list = &audit_inode_hash[h];
+                       list_for_each_entry(e, list, list)
+                               if (!audit_compare_rule(&entry->rule, &e->rule)) {
+                                       found = e;
+                                       goto out;
+                               }
+               }
+               goto out;
+       }
+
+       list_for_each_entry(e, list, list)
+               if (!audit_compare_rule(&entry->rule, &e->rule)) {
+                       found = e;
+                       goto out;
+               }
+
+out:
+       return found;
+}
+
+/* Get path information necessary for adding watches. */
+static int audit_get_nd(char *path, struct nameidata **ndp,
+                       struct nameidata **ndw)
+{
+       struct nameidata *ndparent, *ndwatch;
+       int err;
+
+       ndparent = kmalloc(sizeof(*ndparent), GFP_KERNEL);
+       if (unlikely(!ndparent))
+               return -ENOMEM;
+
+       ndwatch = kmalloc(sizeof(*ndwatch), GFP_KERNEL);
+       if (unlikely(!ndwatch)) {
+               kfree(ndparent);
+               return -ENOMEM;
+       }
+
+       err = path_lookup(path, LOOKUP_PARENT, ndparent);
+       if (err) {
+               kfree(ndparent);
+               kfree(ndwatch);
+               return err;
+       }
+
+       err = path_lookup(path, 0, ndwatch);
+       if (err) {
+               kfree(ndwatch);
+               ndwatch = NULL;
+       }
+
+       *ndp = ndparent;
+       *ndw = ndwatch;
+
+       return 0;
+}
+
+/* Release resources used for watch path information. */
+static void audit_put_nd(struct nameidata *ndp, struct nameidata *ndw)
+{
+       if (ndp) {
+               path_release(ndp);
+               kfree(ndp);
+       }
+       if (ndw) {
+               path_release(ndw);
+               kfree(ndw);
+       }
+}
+
+/* Associate the given rule with an existing parent inotify_watch.
+ * Caller must hold audit_filter_mutex. */
+static void audit_add_to_parent(struct audit_krule *krule,
+                               struct audit_parent *parent)
+{
+       struct audit_watch *w, *watch = krule->watch;
+       int watch_found = 0;
+
+       list_for_each_entry(w, &parent->watches, wlist) {
+               if (strcmp(watch->path, w->path))
+                       continue;
+
+               watch_found = 1;
+
+               /* put krule's and initial refs to temporary watch */
+               audit_put_watch(watch);
+               audit_put_watch(watch);
+
+               audit_get_watch(w);
+               krule->watch = watch = w;
+               break;
+       }
+
+       if (!watch_found) {
+               get_inotify_watch(&parent->wdata);
+               watch->parent = parent;
+
+               list_add(&watch->wlist, &parent->watches);
+       }
+       list_add(&krule->rlist, &watch->rules);
+}
+
+/* Find a matching watch entry, or add this one.
+ * Caller must hold audit_filter_mutex. */
+static int audit_add_watch(struct audit_krule *krule, struct nameidata *ndp,
+                          struct nameidata *ndw)
+{
+       struct audit_watch *watch = krule->watch;
+       struct inotify_watch *i_watch;
+       struct audit_parent *parent;
+       int ret = 0;
+
+       /* update watch filter fields */
+       if (ndw) {
+               watch->dev = ndw->dentry->d_inode->i_sb->s_dev;
+               watch->ino = ndw->dentry->d_inode->i_ino;
+       }
+
+       /* The audit_filter_mutex must not be held during inotify calls because
+        * we hold it during inotify event callback processing.  If an existing
+        * inotify watch is found, inotify_find_watch() grabs a reference before
+        * returning.
+        */
+       mutex_unlock(&audit_filter_mutex);
+
+       if (inotify_find_watch(audit_ih, ndp->dentry->d_inode, &i_watch) < 0) {
+               parent = audit_init_parent(ndp);
+               if (IS_ERR(parent)) {
+                       /* caller expects mutex locked */
+                       mutex_lock(&audit_filter_mutex);
+                       return PTR_ERR(parent);
+               }
+       } else
+               parent = container_of(i_watch, struct audit_parent, wdata);
+
+       mutex_lock(&audit_filter_mutex);
+
+       /* parent was moved before we took audit_filter_mutex */
+       if (parent->flags & AUDIT_PARENT_INVALID)
+               ret = -ENOENT;
+       else
+               audit_add_to_parent(krule, parent);
+
+       /* match get in audit_init_parent or inotify_find_watch */
+       put_inotify_watch(&parent->wdata);
+       return ret;
+}
+
+/* Add rule to given filterlist if not a duplicate. */
 static inline int audit_add_rule(struct audit_entry *entry,
-                                 struct list_head *list)
+                                struct list_head *list)
 {
        struct audit_entry *e;
+       struct audit_field *inode_f = entry->rule.inode_f;
+       struct audit_watch *watch = entry->rule.watch;
+       struct nameidata *ndp, *ndw;
+       int h, err, putnd_needed = 0;
+
+       if (inode_f) {
+               h = audit_hash_ino(inode_f->val);
+               list = &audit_inode_hash[h];
+       }
 
-       /* Do not use the _rcu iterator here, since this is the only
-        * addition routine. */
-       list_for_each_entry(e, list, list) {
-               if (!audit_compare_rule(&entry->rule, &e->rule))
-                       return -EEXIST;
+       mutex_lock(&audit_filter_mutex);
+       e = audit_find_rule(entry, list);
+       mutex_unlock(&audit_filter_mutex);
+       if (e) {
+               err = -EEXIST;
+               goto error;
+       }
+
+       /* Avoid calling path_lookup under audit_filter_mutex. */
+       if (watch) {
+               err = audit_get_nd(watch->path, &ndp, &ndw);
+               if (err)
+                       goto error;
+               putnd_needed = 1;
+       }
+
+       mutex_lock(&audit_filter_mutex);
+       if (watch) {
+               /* audit_filter_mutex is dropped and re-taken during this call */
+               err = audit_add_watch(&entry->rule, ndp, ndw);
+               if (err) {
+                       mutex_unlock(&audit_filter_mutex);
+                       goto error;
+               }
+               h = audit_hash_ino((u32)watch->ino);
+               list = &audit_inode_hash[h];
        }
 
        if (entry->rule.flags & AUDIT_FILTER_PREPEND) {
                list_add_rcu(&entry->list, list);
+               entry->rule.flags &= ~AUDIT_FILTER_PREPEND;
        } else {
                list_add_tail_rcu(&entry->list, list);
        }
+       mutex_unlock(&audit_filter_mutex);
 
-       return 0;
+       if (putnd_needed)
+               audit_put_nd(ndp, ndw);
+
+       return 0;
+
+error:
+       if (putnd_needed)
+               audit_put_nd(ndp, ndw);
+       if (watch)
+               audit_put_watch(watch); /* tmp watch, matches initial get */
+       return err;
 }
 
-/* Remove an existing rule from filterlist.  Protected by
- * audit_netlink_mutex. */
+/* Remove an existing rule from filterlist. */
 static inline int audit_del_rule(struct audit_entry *entry,
                                 struct list_head *list)
 {
        struct audit_entry  *e;
+       struct audit_field *inode_f = entry->rule.inode_f;
+       struct audit_watch *watch, *tmp_watch = entry->rule.watch;
+       LIST_HEAD(inotify_list);
+       int h, ret = 0;
+
+       if (inode_f) {
+               h = audit_hash_ino(inode_f->val);
+               list = &audit_inode_hash[h];
+       }
 
-       /* Do not use the _rcu iterator here, since this is the only
-        * deletion routine. */
-       list_for_each_entry(e, list, list) {
-               if (!audit_compare_rule(&entry->rule, &e->rule)) {
-                       list_del_rcu(&e->list);
-                       call_rcu(&e->rcu, audit_free_rule_rcu);
-                       return 0;
+       mutex_lock(&audit_filter_mutex);
+       e = audit_find_rule(entry, list);
+       if (!e) {
+               mutex_unlock(&audit_filter_mutex);
+               ret = -ENOENT;
+               goto out;
+       }
+
+       watch = e->rule.watch;
+       if (watch) {
+               struct audit_parent *parent = watch->parent;
+
+               list_del(&e->rule.rlist);
+
+               if (list_empty(&watch->rules)) {
+                       audit_remove_watch(watch);
+
+                       if (list_empty(&parent->watches)) {
+                               /* Put parent on the inotify un-registration
+                                * list.  Grab a reference before releasing
+                                * audit_filter_mutex, to be released in
+                                * audit_inotify_unregister(). */
+                               list_add(&parent->ilist, &inotify_list);
+                               get_inotify_watch(&parent->wdata);
+                       }
                }
        }
-       return -ENOENT;         /* No matching rule */
+
+       list_del_rcu(&e->list);
+       call_rcu(&e->rcu, audit_free_rule_rcu);
+
+       mutex_unlock(&audit_filter_mutex);
+
+       if (!list_empty(&inotify_list))
+               audit_inotify_unregister(&inotify_list);
+
+out:
+       if (tmp_watch)
+               audit_put_watch(tmp_watch); /* match initial get */
+
+       return ret;
 }
 
 /* List rules using struct audit_rule.  Exists for backward
  * compatibility with userspace. */
-static int audit_list(void *_dest)
+static void audit_list(int pid, int seq, struct sk_buff_head *q)
 {
-       int pid, seq;
-       int *dest = _dest;
+       struct sk_buff *skb;
        struct audit_entry *entry;
        int i;
 
-       pid = dest[0];
-       seq = dest[1];
-       kfree(dest);
-
-       mutex_lock(&audit_netlink_mutex);
-
-       /* The *_rcu iterators not needed here because we are
-          always called with audit_netlink_mutex held. */
+       /* This is a blocking read, so use audit_filter_mutex instead of rcu
+        * iterator to sync with list writers. */
        for (i=0; i<AUDIT_NR_FILTERS; i++) {
                list_for_each_entry(entry, &audit_filter_list[i], list) {
                        struct audit_rule *rule;
@@ -532,33 +1177,41 @@ static int audit_list(void *_dest)
                        rule = audit_krule_to_rule(&entry->rule);
                        if (unlikely(!rule))
                                break;
-                       audit_send_reply(pid, seq, AUDIT_LIST, 0, 1,
+                       skb = audit_make_reply(pid, seq, AUDIT_LIST, 0, 1,
                                         rule, sizeof(*rule));
+                       if (skb)
+                               skb_queue_tail(q, skb);
                        kfree(rule);
                }
        }
-       audit_send_reply(pid, seq, AUDIT_LIST, 1, 1, NULL, 0);
-       
-       mutex_unlock(&audit_netlink_mutex);
-       return 0;
+       for (i = 0; i < AUDIT_INODE_BUCKETS; i++) {
+               list_for_each_entry(entry, &audit_inode_hash[i], list) {
+                       struct audit_rule *rule;
+
+                       rule = audit_krule_to_rule(&entry->rule);
+                       if (unlikely(!rule))
+                               break;
+                       skb = audit_make_reply(pid, seq, AUDIT_LIST, 0, 1,
+                                        rule, sizeof(*rule));
+                       if (skb)
+                               skb_queue_tail(q, skb);
+                       kfree(rule);
+               }
+       }
+       skb = audit_make_reply(pid, seq, AUDIT_LIST, 1, 1, NULL, 0);
+       if (skb)
+               skb_queue_tail(q, skb);
 }
 
 /* List rules using struct audit_rule_data. */
-static int audit_list_rules(void *_dest)
+static void audit_list_rules(int pid, int seq, struct sk_buff_head *q)
 {
-       int pid, seq;
-       int *dest = _dest;
+       struct sk_buff *skb;
        struct audit_entry *e;
        int i;
 
-       pid = dest[0];
-       seq = dest[1];
-       kfree(dest);
-
-       mutex_lock(&audit_netlink_mutex);
-
-       /* The *_rcu iterators not needed here because we are
-          always called with audit_netlink_mutex held. */
+       /* This is a blocking read, so use audit_filter_mutex instead of rcu
+        * iterator to sync with list writers. */
        for (i=0; i<AUDIT_NR_FILTERS; i++) {
                list_for_each_entry(e, &audit_filter_list[i], list) {
                        struct audit_rule_data *data;
@@ -566,15 +1219,30 @@ static int audit_list_rules(void *_dest)
                        data = audit_krule_to_data(&e->rule);
                        if (unlikely(!data))
                                break;
-                       audit_send_reply(pid, seq, AUDIT_LIST_RULES, 0, 1,
-                                        data, sizeof(*data));
+                       skb = audit_make_reply(pid, seq, AUDIT_LIST_RULES, 0, 1,
+                                        data, sizeof(*data) + data->buflen);
+                       if (skb)
+                               skb_queue_tail(q, skb);
                        kfree(data);
                }
        }
-       audit_send_reply(pid, seq, AUDIT_LIST_RULES, 1, 1, NULL, 0);
+       for (i=0; i< AUDIT_INODE_BUCKETS; i++) {
+               list_for_each_entry(e, &audit_inode_hash[i], list) {
+                       struct audit_rule_data *data;
 
-       mutex_unlock(&audit_netlink_mutex);
-       return 0;
+                       data = audit_krule_to_data(&e->rule);
+                       if (unlikely(!data))
+                               break;
+                       skb = audit_make_reply(pid, seq, AUDIT_LIST_RULES, 0, 1,
+                                        data, sizeof(*data) + data->buflen);
+                       if (skb)
+                               skb_queue_tail(q, skb);
+                       kfree(data);
+               }
+       }
+       skb = audit_make_reply(pid, seq, AUDIT_LIST_RULES, 1, 1, NULL, 0);
+       if (skb)
+               skb_queue_tail(q, skb);
 }
 
 /**
@@ -592,7 +1260,7 @@ int audit_receive_filter(int type, int pid, int uid, int seq, void *data,
                         size_t datasz, uid_t loginuid, u32 sid)
 {
        struct task_struct *tsk;
-       int *dest;
+       struct audit_netlink_list *dest;
        int err = 0;
        struct audit_entry *entry;
 
@@ -605,18 +1273,22 @@ int audit_receive_filter(int type, int pid, int uid, int seq, void *data,
                 * happen if we're actually running in the context of auditctl
                 * trying to _send_ the stuff */
                 
-               dest = kmalloc(2 * sizeof(int), GFP_KERNEL);
+               dest = kmalloc(sizeof(struct audit_netlink_list), GFP_KERNEL);
                if (!dest)
                        return -ENOMEM;
-               dest[0] = pid;
-               dest[1] = seq;
+               dest->pid = pid;
+               skb_queue_head_init(&dest->q);
 
+               mutex_lock(&audit_filter_mutex);
                if (type == AUDIT_LIST)
-                       tsk = kthread_run(audit_list, dest, "audit_list");
+                       audit_list(pid, seq, &dest->q);
                else
-                       tsk = kthread_run(audit_list_rules, dest,
-                                         "audit_list_rules");
+                       audit_list_rules(pid, seq, &dest->q);
+               mutex_unlock(&audit_filter_mutex);
+
+               tsk = kthread_run(audit_send_list, dest, "audit_send_list");
                if (IS_ERR(tsk)) {
+                       skb_queue_purge(&dest->q);
                        kfree(dest);
                        err = PTR_ERR(tsk);
                }
@@ -632,6 +1304,7 @@ int audit_receive_filter(int type, int pid, int uid, int seq, void *data,
 
                err = audit_add_rule(entry,
                                     &audit_filter_list[entry->rule.listnr]);
+
                if (sid) {
                        char *ctx = NULL;
                        u32 len;
@@ -712,7 +1385,43 @@ int audit_comparator(const u32 left, const u32 op, const u32 right)
        return 0;
 }
 
+/* Compare given dentry name with last component in given path,
+ * return of 0 indicates a match. */
+int audit_compare_dname_path(const char *dname, const char *path,
+                            int *dirlen)
+{
+       int dlen, plen;
+       const char *p;
 
+       if (!dname || !path)
+               return 1;
+
+       dlen = strlen(dname);
+       plen = strlen(path);
+       if (plen < dlen)
+               return 1;
+
+       /* disregard trailing slashes */
+       p = path + plen - 1;
+       while ((*p == '/') && (p > path))
+               p--;
+
+       /* find last path component */
+       p = p - dlen + 1;
+       if (p < path)
+               return 1;
+       else if (p > path) {
+               if (*--p != '/')
+                       return 1;
+               else
+                       p++;
+       }
+
+       /* return length of path's directory component */
+       if (dirlen)
+               *dirlen = p - path;
+       return strncmp(p, dname, dlen);
+}
 
 static int audit_filter_user_rules(struct netlink_skb_parms *cb,
                                   struct audit_krule *rule,
@@ -744,7 +1453,6 @@ static int audit_filter_user_rules(struct netlink_skb_parms *cb,
        }
        switch (rule->action) {
        case AUDIT_NEVER:    *state = AUDIT_DISABLED;       break;
-       case AUDIT_POSSIBLE: *state = AUDIT_BUILD_CONTEXT;  break;
        case AUDIT_ALWAYS:   *state = AUDIT_RECORD_CONTEXT; break;
        }
        return 1;
@@ -826,32 +1534,65 @@ static inline int audit_rule_has_selinux(struct audit_krule *rule)
 int selinux_audit_rule_update(void)
 {
        struct audit_entry *entry, *n, *nentry;
+       struct audit_watch *watch;
        int i, err = 0;
 
-       /* audit_netlink_mutex synchronizes the writers */
-       mutex_lock(&audit_netlink_mutex);
+       /* audit_filter_mutex synchronizes the writers */
+       mutex_lock(&audit_filter_mutex);
 
        for (i = 0; i < AUDIT_NR_FILTERS; i++) {
                list_for_each_entry_safe(entry, n, &audit_filter_list[i], list) {
                        if (!audit_rule_has_selinux(&entry->rule))
                                continue;
 
-                       nentry = audit_dupe_rule(&entry->rule);
+                       watch = entry->rule.watch;
+                       nentry = audit_dupe_rule(&entry->rule, watch);
                        if (unlikely(IS_ERR(nentry))) {
                                /* save the first error encountered for the
                                 * return value */
                                if (!err)
                                        err = PTR_ERR(nentry);
                                audit_panic("error updating selinux filters");
+                               if (watch)
+                                       list_del(&entry->rule.rlist);
                                list_del_rcu(&entry->list);
                        } else {
+                               if (watch) {
+                                       list_add(&nentry->rule.rlist,
+                                                &watch->rules);
+                                       list_del(&entry->rule.rlist);
+                               }
                                list_replace_rcu(&entry->list, &nentry->list);
                        }
                        call_rcu(&entry->rcu, audit_free_rule_rcu);
                }
        }
 
-       mutex_unlock(&audit_netlink_mutex);
+       mutex_unlock(&audit_filter_mutex);
 
        return err;
 }
+
+/* Update watch data in audit rules based on inotify events. */
+void audit_handle_ievent(struct inotify_watch *i_watch, u32 wd, u32 mask,
+                        u32 cookie, const char *dname, struct inode *inode)
+{
+       struct audit_parent *parent;
+
+       parent = container_of(i_watch, struct audit_parent, wdata);
+
+       if (mask & (IN_CREATE|IN_MOVED_TO) && inode)
+               audit_update_watch(parent, dname, inode->i_sb->s_dev,
+                                  inode->i_ino, 0);
+       else if (mask & (IN_DELETE|IN_MOVED_FROM))
+               audit_update_watch(parent, dname, (dev_t)-1, (unsigned long)-1, 1);
+       /* inotify automatically removes the watch and sends IN_IGNORED */
+       else if (mask & (IN_DELETE_SELF|IN_UNMOUNT))
+               audit_remove_parent_watches(parent);
+       /* inotify does not remove the watch, so remove it manually */
+       else if(mask & IN_MOVE_SELF) {
+               audit_remove_parent_watches(parent);
+               inotify_remove_watch_locked(audit_ih, i_watch);
+       } else if (mask & IN_IGNORED)
+               put_inotify_watch(i_watch);
+}
index 1c03a4ed1b27fb8b6f4276b3305ed10907f8f4c3..b097ccb4eb7ea8e10145a29b6f2e1fc14120e0d5 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Copyright 2003-2004 Red Hat Inc., Durham, North Carolina.
  * Copyright 2005 Hewlett-Packard Development Company, L.P.
- * Copyright (C) 2005 IBM Corporation
+ * Copyright (C) 2005, 2006 IBM Corporation
  * All Rights Reserved.
  *
  * This program is free software; you can redistribute it and/or modify
@@ -29,6 +29,9 @@
  * this file -- see entry.S) is based on a GPL'd patch written by
  * okir@suse.de and Copyright 2003 SuSE Linux AG.
  *
+ * POSIX message queue support added by George Wilson <ltcgcw@us.ibm.com>,
+ * 2006.
+ *
  * The support of additional filter rules compares (>, <, >=, <=) was
  * added by Dustin Kirkland <dustin.kirkland@us.ibm.com>, 2005.
  *
@@ -49,6 +52,7 @@
 #include <linux/module.h>
 #include <linux/mount.h>
 #include <linux/socket.h>
+#include <linux/mqueue.h>
 #include <linux/audit.h>
 #include <linux/personality.h>
 #include <linux/time.h>
@@ -59,6 +63,8 @@
 #include <linux/list.h>
 #include <linux/tty.h>
 #include <linux/selinux.h>
+#include <linux/binfmts.h>
+#include <linux/syscalls.h>
 
 #include "audit.h"
 
@@ -76,6 +82,9 @@ extern int audit_enabled;
  * path_lookup. */
 #define AUDIT_NAMES_RESERVED 7
 
+/* Indicates that audit should log the full pathname. */
+#define AUDIT_NAME_FULL -1
+
 /* When fs/namei.c:getname() is called, we store the pointer in name and
  * we don't let putname() free it (instead we free all of the saved
  * pointers at syscall exit time).
@@ -83,8 +92,9 @@ extern int audit_enabled;
  * Further, in fs/namei.c:path_lookup() we store the inode and device. */
 struct audit_names {
        const char      *name;
+       int             name_len;       /* number of name's characters to log */
+       unsigned        name_put;       /* call __putname() for this name */
        unsigned long   ino;
-       unsigned long   pino;
        dev_t           dev;
        umode_t         mode;
        uid_t           uid;
@@ -100,6 +110,33 @@ struct audit_aux_data {
 
 #define AUDIT_AUX_IPCPERM      0
 
+struct audit_aux_data_mq_open {
+       struct audit_aux_data   d;
+       int                     oflag;
+       mode_t                  mode;
+       struct mq_attr          attr;
+};
+
+struct audit_aux_data_mq_sendrecv {
+       struct audit_aux_data   d;
+       mqd_t                   mqdes;
+       size_t                  msg_len;
+       unsigned int            msg_prio;
+       struct timespec         abs_timeout;
+};
+
+struct audit_aux_data_mq_notify {
+       struct audit_aux_data   d;
+       mqd_t                   mqdes;
+       struct sigevent         notification;
+};
+
+struct audit_aux_data_mq_getsetattr {
+       struct audit_aux_data   d;
+       mqd_t                   mqdes;
+       struct mq_attr          mqstat;
+};
+
 struct audit_aux_data_ipcctl {
        struct audit_aux_data   d;
        struct ipc_perm         p;
@@ -110,6 +147,13 @@ struct audit_aux_data_ipcctl {
        u32                     osid;
 };
 
+struct audit_aux_data_execve {
+       struct audit_aux_data   d;
+       int argc;
+       int envc;
+       char mem[0];
+};
+
 struct audit_aux_data_socketcall {
        struct audit_aux_data   d;
        int                     nargs;
@@ -148,7 +192,7 @@ struct audit_context {
        struct audit_aux_data *aux;
 
                                /* Save things to print about task_struct */
-       pid_t               pid;
+       pid_t               pid, ppid;
        uid_t               uid, euid, suid, fsuid;
        gid_t               gid, egid, sgid, fsgid;
        unsigned long       personality;
@@ -160,12 +204,13 @@ struct audit_context {
 #endif
 };
 
-
+/* Determine if any context name data matches a rule's watch data */
 /* Compare a task_struct with an audit_rule.  Return 1 on match, 0
  * otherwise. */
 static int audit_filter_rules(struct task_struct *tsk,
                              struct audit_krule *rule,
                              struct audit_context *ctx,
+                             struct audit_names *name,
                              enum audit_state *state)
 {
        int i, j, need_sid = 1;
@@ -179,6 +224,10 @@ static int audit_filter_rules(struct task_struct *tsk,
                case AUDIT_PID:
                        result = audit_comparator(tsk->pid, f->op, f->val);
                        break;
+               case AUDIT_PPID:
+                       if (ctx)
+                               result = audit_comparator(ctx->ppid, f->op, f->val);
+                       break;
                case AUDIT_UID:
                        result = audit_comparator(tsk->uid, f->op, f->val);
                        break;
@@ -224,7 +273,10 @@ static int audit_filter_rules(struct task_struct *tsk,
                        }
                        break;
                case AUDIT_DEVMAJOR:
-                       if (ctx) {
+                       if (name)
+                               result = audit_comparator(MAJOR(name->dev),
+                                                         f->op, f->val);
+                       else if (ctx) {
                                for (j = 0; j < ctx->name_count; j++) {
                                        if (audit_comparator(MAJOR(ctx->names[j].dev),  f->op, f->val)) {
                                                ++result;
@@ -234,7 +286,10 @@ static int audit_filter_rules(struct task_struct *tsk,
                        }
                        break;
                case AUDIT_DEVMINOR:
-                       if (ctx) {
+                       if (name)
+                               result = audit_comparator(MINOR(name->dev),
+                                                         f->op, f->val);
+                       else if (ctx) {
                                for (j = 0; j < ctx->name_count; j++) {
                                        if (audit_comparator(MINOR(ctx->names[j].dev), f->op, f->val)) {
                                                ++result;
@@ -244,16 +299,22 @@ static int audit_filter_rules(struct task_struct *tsk,
                        }
                        break;
                case AUDIT_INODE:
-                       if (ctx) {
+                       if (name)
+                               result = (name->ino == f->val);
+                       else if (ctx) {
                                for (j = 0; j < ctx->name_count; j++) {
-                                       if (audit_comparator(ctx->names[j].ino, f->op, f->val) ||
-                                           audit_comparator(ctx->names[j].pino, f->op, f->val)) {
+                                       if (audit_comparator(ctx->names[j].ino, f->op, f->val)) {
                                                ++result;
                                                break;
                                        }
                                }
                        }
                        break;
+               case AUDIT_WATCH:
+                       if (name && rule->watch->ino != (unsigned long)-1)
+                               result = (name->dev == rule->watch->dev &&
+                                         name->ino == rule->watch->ino);
+                       break;
                case AUDIT_LOGINUID:
                        result = 0;
                        if (ctx)
@@ -294,7 +355,6 @@ static int audit_filter_rules(struct task_struct *tsk,
        }
        switch (rule->action) {
        case AUDIT_NEVER:    *state = AUDIT_DISABLED;       break;
-       case AUDIT_POSSIBLE: *state = AUDIT_BUILD_CONTEXT;  break;
        case AUDIT_ALWAYS:   *state = AUDIT_RECORD_CONTEXT; break;
        }
        return 1;
@@ -311,7 +371,7 @@ static enum audit_state audit_filter_task(struct task_struct *tsk)
 
        rcu_read_lock();
        list_for_each_entry_rcu(e, &audit_filter_list[AUDIT_FILTER_TASK], list) {
-               if (audit_filter_rules(tsk, &e->rule, NULL, &state)) {
+               if (audit_filter_rules(tsk, &e->rule, NULL, NULL, &state)) {
                        rcu_read_unlock();
                        return state;
                }
@@ -341,8 +401,47 @@ static enum audit_state audit_filter_syscall(struct task_struct *tsk,
                int bit  = AUDIT_BIT(ctx->major);
 
                list_for_each_entry_rcu(e, list, list) {
-                       if ((e->rule.mask[word] & bit) == bit
-                                       && audit_filter_rules(tsk, &e->rule, ctx, &state)) {
+                       if ((e->rule.mask[word] & bit) == bit &&
+                           audit_filter_rules(tsk, &e->rule, ctx, NULL,
+                                              &state)) {
+                               rcu_read_unlock();
+                               return state;
+                       }
+               }
+       }
+       rcu_read_unlock();
+       return AUDIT_BUILD_CONTEXT;
+}
+
+/* At syscall exit time, this filter is called if any audit_names[] have been
+ * collected during syscall processing.  We only check rules in sublists at hash
+ * buckets applicable to the inode numbers in audit_names[].
+ * Regarding audit_state, same rules apply as for audit_filter_syscall().
+ */
+enum audit_state audit_filter_inodes(struct task_struct *tsk,
+                                    struct audit_context *ctx)
+{
+       int i;
+       struct audit_entry *e;
+       enum audit_state state;
+
+       if (audit_pid && tsk->tgid == audit_pid)
+               return AUDIT_DISABLED;
+
+       rcu_read_lock();
+       for (i = 0; i < ctx->name_count; i++) {
+               int word = AUDIT_WORD(ctx->major);
+               int bit  = AUDIT_BIT(ctx->major);
+               struct audit_names *n = &ctx->names[i];
+               int h = audit_hash_ino((u32)n->ino);
+               struct list_head *list = &audit_inode_hash[h];
+
+               if (list_empty(list))
+                       continue;
+
+               list_for_each_entry_rcu(e, list, list) {
+                       if ((e->rule.mask[word] & bit) == bit &&
+                           audit_filter_rules(tsk, &e->rule, ctx, n, &state)) {
                                rcu_read_unlock();
                                return state;
                        }
@@ -352,6 +451,11 @@ static enum audit_state audit_filter_syscall(struct task_struct *tsk,
        return AUDIT_BUILD_CONTEXT;
 }
 
+void audit_set_auditable(struct audit_context *ctx)
+{
+       ctx->auditable = 1;
+}
+
 static inline struct audit_context *audit_get_context(struct task_struct *tsk,
                                                      int return_valid,
                                                      int return_code)
@@ -365,12 +469,22 @@ static inline struct audit_context *audit_get_context(struct task_struct *tsk,
 
        if (context->in_syscall && !context->auditable) {
                enum audit_state state;
+
                state = audit_filter_syscall(tsk, context, &audit_filter_list[AUDIT_FILTER_EXIT]);
+               if (state == AUDIT_RECORD_CONTEXT) {
+                       context->auditable = 1;
+                       goto get_context;
+               }
+
+               state = audit_filter_inodes(tsk, context);
                if (state == AUDIT_RECORD_CONTEXT)
                        context->auditable = 1;
+
        }
 
+get_context:
        context->pid = tsk->pid;
+       context->ppid = sys_getppid();  /* sic.  tsk == current in all cases */
        context->uid = tsk->uid;
        context->gid = tsk->gid;
        context->euid = tsk->euid;
@@ -413,7 +527,7 @@ static inline void audit_free_names(struct audit_context *context)
 #endif
 
        for (i = 0; i < context->name_count; i++) {
-               if (context->names[i].name)
+               if (context->names[i].name && context->names[i].name_put)
                        __putname(context->names[i].name);
        }
        context->name_count = 0;
@@ -606,7 +720,7 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts
                tty = "(none)";
        audit_log_format(ab,
                  " a0=%lx a1=%lx a2=%lx a3=%lx items=%d"
-                 " pid=%d auid=%u uid=%u gid=%u"
+                 " ppid=%d pid=%d auid=%u uid=%u gid=%u"
                  " euid=%u suid=%u fsuid=%u"
                  " egid=%u sgid=%u fsgid=%u tty=%s",
                  context->argv[0],
@@ -614,6 +728,7 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts
                  context->argv[2],
                  context->argv[3],
                  context->name_count,
+                 context->ppid,
                  context->pid,
                  context->loginuid,
                  context->uid,
@@ -630,11 +745,48 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts
                        continue; /* audit_panic has been called */
 
                switch (aux->type) {
+               case AUDIT_MQ_OPEN: {
+                       struct audit_aux_data_mq_open *axi = (void *)aux;
+                       audit_log_format(ab,
+                               "oflag=0x%x mode=%#o mq_flags=0x%lx mq_maxmsg=%ld "
+                               "mq_msgsize=%ld mq_curmsgs=%ld",
+                               axi->oflag, axi->mode, axi->attr.mq_flags,
+                               axi->attr.mq_maxmsg, axi->attr.mq_msgsize,
+                               axi->attr.mq_curmsgs);
+                       break; }
+
+               case AUDIT_MQ_SENDRECV: {
+                       struct audit_aux_data_mq_sendrecv *axi = (void *)aux;
+                       audit_log_format(ab,
+                               "mqdes=%d msg_len=%zd msg_prio=%u "
+                               "abs_timeout_sec=%ld abs_timeout_nsec=%ld",
+                               axi->mqdes, axi->msg_len, axi->msg_prio,
+                               axi->abs_timeout.tv_sec, axi->abs_timeout.tv_nsec);
+                       break; }
+
+               case AUDIT_MQ_NOTIFY: {
+                       struct audit_aux_data_mq_notify *axi = (void *)aux;
+                       audit_log_format(ab,
+                               "mqdes=%d sigev_signo=%d",
+                               axi->mqdes,
+                               axi->notification.sigev_signo);
+                       break; }
+
+               case AUDIT_MQ_GETSETATTR: {
+                       struct audit_aux_data_mq_getsetattr *axi = (void *)aux;
+                       audit_log_format(ab,
+                               "mqdes=%d mq_flags=0x%lx mq_maxmsg=%ld mq_msgsize=%ld "
+                               "mq_curmsgs=%ld ",
+                               axi->mqdes,
+                               axi->mqstat.mq_flags, axi->mqstat.mq_maxmsg,
+                               axi->mqstat.mq_msgsize, axi->mqstat.mq_curmsgs);
+                       break; }
+
                case AUDIT_IPC: {
                        struct audit_aux_data_ipcctl *axi = (void *)aux;
                        audit_log_format(ab, 
-                                " qbytes=%lx iuid=%u igid=%u mode=%x",
-                                axi->qbytes, axi->uid, axi->gid, axi->mode);
+                                "ouid=%u ogid=%u mode=%x",
+                                axi->uid, axi->gid, axi->mode);
                        if (axi->osid != 0) {
                                char *ctx = NULL;
                                u32 len;
@@ -652,19 +804,18 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts
                case AUDIT_IPC_SET_PERM: {
                        struct audit_aux_data_ipcctl *axi = (void *)aux;
                        audit_log_format(ab,
-                               " new qbytes=%lx new iuid=%u new igid=%u new mode=%x",
+                               "qbytes=%lx ouid=%u ogid=%u mode=%x",
                                axi->qbytes, axi->uid, axi->gid, axi->mode);
-                       if (axi->osid != 0) {
-                               char *ctx = NULL;
-                               u32 len;
-                               if (selinux_ctxid_to_string(
-                                               axi->osid, &ctx, &len)) {
-                                       audit_log_format(ab, " osid=%u",
-                                                       axi->osid);
-                                       call_panic = 1;
-                               } else
-                                       audit_log_format(ab, " obj=%s", ctx);
-                               kfree(ctx);
+                       break; }
+
+               case AUDIT_EXECVE: {
+                       struct audit_aux_data_execve *axi = (void *)aux;
+                       int i;
+                       const char *p;
+                       for (i = 0, p = axi->mem; i < axi->argc; i++) {
+                               audit_log_format(ab, "a%d=", i);
+                               p = audit_log_untrustedstring(ab, p);
+                               audit_log_format(ab, "\n");
                        }
                        break; }
 
@@ -700,8 +851,7 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts
                }
        }
        for (i = 0; i < context->name_count; i++) {
-               unsigned long ino  = context->names[i].ino;
-               unsigned long pino = context->names[i].pino;
+               struct audit_names *n = &context->names[i];
 
                ab = audit_log_start(context, GFP_KERNEL, AUDIT_PATH);
                if (!ab)
@@ -709,33 +859,47 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts
 
                audit_log_format(ab, "item=%d", i);
 
-               audit_log_format(ab, " name=");
-               if (context->names[i].name)
-                       audit_log_untrustedstring(ab, context->names[i].name);
-               else
-                       audit_log_format(ab, "(null)");
-
-               if (pino != (unsigned long)-1)
-                       audit_log_format(ab, " parent=%lu",  pino);
-               if (ino != (unsigned long)-1)
-                       audit_log_format(ab, " inode=%lu",  ino);
-               if ((pino != (unsigned long)-1) || (ino != (unsigned long)-1))
-                       audit_log_format(ab, " dev=%02x:%02x mode=%#o" 
-                                        " ouid=%u ogid=%u rdev=%02x:%02x", 
-                                        MAJOR(context->names[i].dev), 
-                                        MINOR(context->names[i].dev), 
-                                        context->names[i].mode, 
-                                        context->names[i].uid, 
-                                        context->names[i].gid, 
-                                        MAJOR(context->names[i].rdev), 
-                                        MINOR(context->names[i].rdev));
-               if (context->names[i].osid != 0) {
+               if (n->name) {
+                       switch(n->name_len) {
+                       case AUDIT_NAME_FULL:
+                               /* log the full path */
+                               audit_log_format(ab, " name=");
+                               audit_log_untrustedstring(ab, n->name);
+                               break;
+                       case 0:
+                               /* name was specified as a relative path and the
+                                * directory component is the cwd */
+                               audit_log_d_path(ab, " name=", context->pwd,
+                                                context->pwdmnt);
+                               break;
+                       default:
+                               /* log the name's directory component */
+                               audit_log_format(ab, " name=");
+                               audit_log_n_untrustedstring(ab, n->name_len,
+                                                           n->name);
+                       }
+               } else
+                       audit_log_format(ab, " name=(null)");
+
+               if (n->ino != (unsigned long)-1) {
+                       audit_log_format(ab, " inode=%lu"
+                                        " dev=%02x:%02x mode=%#o"
+                                        " ouid=%u ogid=%u rdev=%02x:%02x",
+                                        n->ino,
+                                        MAJOR(n->dev),
+                                        MINOR(n->dev),
+                                        n->mode,
+                                        n->uid,
+                                        n->gid,
+                                        MAJOR(n->rdev),
+                                        MINOR(n->rdev));
+               }
+               if (n->osid != 0) {
                        char *ctx = NULL;
                        u32 len;
                        if (selinux_ctxid_to_string(
-                               context->names[i].osid, &ctx, &len)) {
-                               audit_log_format(ab, " osid=%u",
-                                               context->names[i].osid);
+                               n->osid, &ctx, &len)) {
+                               audit_log_format(ab, " osid=%u", n->osid);
                                call_panic = 2;
                        } else
                                audit_log_format(ab, " obj=%s", ctx);
@@ -908,11 +1072,11 @@ void audit_syscall_exit(int valid, long return_code)
  * Add a name to the list of audit names for this context.
  * Called from fs/namei.c:getname().
  */
-void audit_getname(const char *name)
+void __audit_getname(const char *name)
 {
        struct audit_context *context = current->audit_context;
 
-       if (!context || IS_ERR(name) || !name)
+       if (IS_ERR(name) || !name)
                return;
 
        if (!context->in_syscall) {
@@ -925,6 +1089,8 @@ void audit_getname(const char *name)
        }
        BUG_ON(context->name_count >= AUDIT_NAMES);
        context->names[context->name_count].name = name;
+       context->names[context->name_count].name_len = AUDIT_NAME_FULL;
+       context->names[context->name_count].name_put = 1;
        context->names[context->name_count].ino  = (unsigned long)-1;
        ++context->name_count;
        if (!context->pwd) {
@@ -991,11 +1157,10 @@ static void audit_inode_context(int idx, const struct inode *inode)
  * audit_inode - store the inode and device from a lookup
  * @name: name being audited
  * @inode: inode being audited
- * @flags: lookup flags (as used in path_lookup())
  *
  * Called from fs/namei.c:path_lookup().
  */
-void __audit_inode(const char *name, const struct inode *inode, unsigned flags)
+void __audit_inode(const char *name, const struct inode *inode)
 {
        int idx;
        struct audit_context *context = current->audit_context;
@@ -1021,20 +1186,13 @@ void __audit_inode(const char *name, const struct inode *inode, unsigned flags)
                ++context->ino_count;
 #endif
        }
+       context->names[idx].ino   = inode->i_ino;
        context->names[idx].dev   = inode->i_sb->s_dev;
        context->names[idx].mode  = inode->i_mode;
        context->names[idx].uid   = inode->i_uid;
        context->names[idx].gid   = inode->i_gid;
        context->names[idx].rdev  = inode->i_rdev;
        audit_inode_context(idx, inode);
-       if ((flags & LOOKUP_PARENT) && (strcmp(name, "/") != 0) && 
-           (strcmp(name, ".") != 0)) {
-               context->names[idx].ino   = (unsigned long)-1;
-               context->names[idx].pino  = inode->i_ino;
-       } else {
-               context->names[idx].ino   = inode->i_ino;
-               context->names[idx].pino  = (unsigned long)-1;
-       }
 }
 
 /**
@@ -1056,51 +1214,40 @@ void __audit_inode_child(const char *dname, const struct inode *inode,
 {
        int idx;
        struct audit_context *context = current->audit_context;
+       const char *found_name = NULL;
+       int dirlen = 0;
 
        if (!context->in_syscall)
                return;
 
        /* determine matching parent */
-       if (dname)
-               for (idx = 0; idx < context->name_count; idx++)
-                       if (context->names[idx].pino == pino) {
-                               const char *n;
-                               const char *name = context->names[idx].name;
-                               int dlen = strlen(dname);
-                               int nlen = name ? strlen(name) : 0;
-
-                               if (nlen < dlen)
-                                       continue;
-                               
-                               /* disregard trailing slashes */
-                               n = name + nlen - 1;
-                               while ((*n == '/') && (n > name))
-                                       n--;
-
-                               /* find last path component */
-                               n = n - dlen + 1;
-                               if (n < name)
-                                       continue;
-                               else if (n > name) {
-                                       if (*--n != '/')
-                                               continue;
-                                       else
-                                               n++;
-                               }
-
-                               if (strncmp(n, dname, dlen) == 0)
-                                       goto update_context;
+       if (!dname)
+               goto update_context;
+       for (idx = 0; idx < context->name_count; idx++)
+               if (context->names[idx].ino == pino) {
+                       const char *name = context->names[idx].name;
+
+                       if (!name)
+                               continue;
+
+                       if (audit_compare_dname_path(dname, name, &dirlen) == 0) {
+                               context->names[idx].name_len = dirlen;
+                               found_name = name;
+                               break;
                        }
+               }
 
-       /* catch-all in case match not found */
+update_context:
        idx = context->name_count++;
-       context->names[idx].name  = NULL;
-       context->names[idx].pino  = pino;
 #if AUDIT_DEBUG
        context->ino_count++;
 #endif
+       /* Re-use the name belonging to the slot for a matching parent directory.
+        * All names for this context are relinquished in audit_free_names() */
+       context->names[idx].name = found_name;
+       context->names[idx].name_len = AUDIT_NAME_FULL;
+       context->names[idx].name_put = 0;       /* don't call __putname() */
 
-update_context:
        if (inode) {
                context->names[idx].ino   = inode->i_ino;
                context->names[idx].dev   = inode->i_sb->s_dev;
@@ -1109,7 +1256,8 @@ update_context:
                context->names[idx].gid   = inode->i_gid;
                context->names[idx].rdev  = inode->i_rdev;
                audit_inode_context(idx, inode);
-       }
+       } else
+               context->names[idx].ino   = (unsigned long)-1;
 }
 
 /**
@@ -1142,18 +1290,23 @@ void auditsc_get_stamp(struct audit_context *ctx,
  */
 int audit_set_loginuid(struct task_struct *task, uid_t loginuid)
 {
-       if (task->audit_context) {
-               struct audit_buffer *ab;
-
-               ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_LOGIN);
-               if (ab) {
-                       audit_log_format(ab, "login pid=%d uid=%u "
-                               "old auid=%u new auid=%u",
-                               task->pid, task->uid, 
-                               task->audit_context->loginuid, loginuid);
-                       audit_log_end(ab);
+       struct audit_context *context = task->audit_context;
+
+       if (context) {
+               /* Only log if audit is enabled */
+               if (context->in_syscall) {
+                       struct audit_buffer *ab;
+
+                       ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_LOGIN);
+                       if (ab) {
+                               audit_log_format(ab, "login pid=%d uid=%u "
+                                       "old auid=%u new auid=%u",
+                                       task->pid, task->uid,
+                                       context->loginuid, loginuid);
+                               audit_log_end(ab);
+                       }
                }
-               task->audit_context->loginuid = loginuid;
+               context->loginuid = loginuid;
        }
        return 0;
 }
@@ -1170,19 +1323,220 @@ uid_t audit_get_loginuid(struct audit_context *ctx)
 }
 
 /**
- * audit_ipc_obj - record audit data for ipc object
- * @ipcp: ipc permissions
+ * __audit_mq_open - record audit data for a POSIX MQ open
+ * @oflag: open flag
+ * @mode: mode bits
+ * @u_attr: queue attributes
  *
  * Returns 0 for success or NULL context or < 0 on error.
  */
-int audit_ipc_obj(struct kern_ipc_perm *ipcp)
+int __audit_mq_open(int oflag, mode_t mode, struct mq_attr __user *u_attr)
 {
-       struct audit_aux_data_ipcctl *ax;
+       struct audit_aux_data_mq_open *ax;
+       struct audit_context *context = current->audit_context;
+
+       if (!audit_enabled)
+               return 0;
+
+       if (likely(!context))
+               return 0;
+
+       ax = kmalloc(sizeof(*ax), GFP_ATOMIC);
+       if (!ax)
+               return -ENOMEM;
+
+       if (u_attr != NULL) {
+               if (copy_from_user(&ax->attr, u_attr, sizeof(ax->attr))) {
+                       kfree(ax);
+                       return -EFAULT;
+               }
+       } else
+               memset(&ax->attr, 0, sizeof(ax->attr));
+
+       ax->oflag = oflag;
+       ax->mode = mode;
+
+       ax->d.type = AUDIT_MQ_OPEN;
+       ax->d.next = context->aux;
+       context->aux = (void *)ax;
+       return 0;
+}
+
+/**
+ * __audit_mq_timedsend - record audit data for a POSIX MQ timed send
+ * @mqdes: MQ descriptor
+ * @msg_len: Message length
+ * @msg_prio: Message priority
+ * @abs_timeout: Message timeout in absolute time
+ *
+ * Returns 0 for success or NULL context or < 0 on error.
+ */
+int __audit_mq_timedsend(mqd_t mqdes, size_t msg_len, unsigned int msg_prio,
+                       const struct timespec __user *u_abs_timeout)
+{
+       struct audit_aux_data_mq_sendrecv *ax;
+       struct audit_context *context = current->audit_context;
+
+       if (!audit_enabled)
+               return 0;
+
+       if (likely(!context))
+               return 0;
+
+       ax = kmalloc(sizeof(*ax), GFP_ATOMIC);
+       if (!ax)
+               return -ENOMEM;
+
+       if (u_abs_timeout != NULL) {
+               if (copy_from_user(&ax->abs_timeout, u_abs_timeout, sizeof(ax->abs_timeout))) {
+                       kfree(ax);
+                       return -EFAULT;
+               }
+       } else
+               memset(&ax->abs_timeout, 0, sizeof(ax->abs_timeout));
+
+       ax->mqdes = mqdes;
+       ax->msg_len = msg_len;
+       ax->msg_prio = msg_prio;
+
+       ax->d.type = AUDIT_MQ_SENDRECV;
+       ax->d.next = context->aux;
+       context->aux = (void *)ax;
+       return 0;
+}
+
+/**
+ * __audit_mq_timedreceive - record audit data for a POSIX MQ timed receive
+ * @mqdes: MQ descriptor
+ * @msg_len: Message length
+ * @msg_prio: Message priority
+ * @abs_timeout: Message timeout in absolute time
+ *
+ * Returns 0 for success or NULL context or < 0 on error.
+ */
+int __audit_mq_timedreceive(mqd_t mqdes, size_t msg_len,
+                               unsigned int __user *u_msg_prio,
+                               const struct timespec __user *u_abs_timeout)
+{
+       struct audit_aux_data_mq_sendrecv *ax;
+       struct audit_context *context = current->audit_context;
+
+       if (!audit_enabled)
+               return 0;
+
+       if (likely(!context))
+               return 0;
+
+       ax = kmalloc(sizeof(*ax), GFP_ATOMIC);
+       if (!ax)
+               return -ENOMEM;
+
+       if (u_msg_prio != NULL) {
+               if (get_user(ax->msg_prio, u_msg_prio)) {
+                       kfree(ax);
+                       return -EFAULT;
+               }
+       } else
+               ax->msg_prio = 0;
+
+       if (u_abs_timeout != NULL) {
+               if (copy_from_user(&ax->abs_timeout, u_abs_timeout, sizeof(ax->abs_timeout))) {
+                       kfree(ax);
+                       return -EFAULT;
+               }
+       } else
+               memset(&ax->abs_timeout, 0, sizeof(ax->abs_timeout));
+
+       ax->mqdes = mqdes;
+       ax->msg_len = msg_len;
+
+       ax->d.type = AUDIT_MQ_SENDRECV;
+       ax->d.next = context->aux;
+       context->aux = (void *)ax;
+       return 0;
+}
+
+/**
+ * __audit_mq_notify - record audit data for a POSIX MQ notify
+ * @mqdes: MQ descriptor
+ * @u_notification: Notification event
+ *
+ * Returns 0 for success or NULL context or < 0 on error.
+ */
+
+int __audit_mq_notify(mqd_t mqdes, const struct sigevent __user *u_notification)
+{
+       struct audit_aux_data_mq_notify *ax;
+       struct audit_context *context = current->audit_context;
+
+       if (!audit_enabled)
+               return 0;
+
+       if (likely(!context))
+               return 0;
+
+       ax = kmalloc(sizeof(*ax), GFP_ATOMIC);
+       if (!ax)
+               return -ENOMEM;
+
+       if (u_notification != NULL) {
+               if (copy_from_user(&ax->notification, u_notification, sizeof(ax->notification))) {
+                       kfree(ax);
+                       return -EFAULT;
+               }
+       } else
+               memset(&ax->notification, 0, sizeof(ax->notification));
+
+       ax->mqdes = mqdes;
+
+       ax->d.type = AUDIT_MQ_NOTIFY;
+       ax->d.next = context->aux;
+       context->aux = (void *)ax;
+       return 0;
+}
+
+/**
+ * __audit_mq_getsetattr - record audit data for a POSIX MQ get/set attribute
+ * @mqdes: MQ descriptor
+ * @mqstat: MQ flags
+ *
+ * Returns 0 for success or NULL context or < 0 on error.
+ */
+int __audit_mq_getsetattr(mqd_t mqdes, struct mq_attr *mqstat)
+{
+       struct audit_aux_data_mq_getsetattr *ax;
        struct audit_context *context = current->audit_context;
 
+       if (!audit_enabled)
+               return 0;
+
        if (likely(!context))
                return 0;
 
+       ax = kmalloc(sizeof(*ax), GFP_ATOMIC);
+       if (!ax)
+               return -ENOMEM;
+
+       ax->mqdes = mqdes;
+       ax->mqstat = *mqstat;
+
+       ax->d.type = AUDIT_MQ_GETSETATTR;
+       ax->d.next = context->aux;
+       context->aux = (void *)ax;
+       return 0;
+}
+
+/**
+ * audit_ipc_obj - record audit data for ipc object
+ * @ipcp: ipc permissions
+ *
+ * Returns 0 for success or NULL context or < 0 on error.
+ */
+int __audit_ipc_obj(struct kern_ipc_perm *ipcp)
+{
+       struct audit_aux_data_ipcctl *ax;
+       struct audit_context *context = current->audit_context;
+
        ax = kmalloc(sizeof(*ax), GFP_ATOMIC);
        if (!ax)
                return -ENOMEM;
@@ -1207,14 +1561,11 @@ int audit_ipc_obj(struct kern_ipc_perm *ipcp)
  *
  * Returns 0 for success or NULL context or < 0 on error.
  */
-int audit_ipc_set_perm(unsigned long qbytes, uid_t uid, gid_t gid, mode_t mode, struct kern_ipc_perm *ipcp)
+int __audit_ipc_set_perm(unsigned long qbytes, uid_t uid, gid_t gid, mode_t mode)
 {
        struct audit_aux_data_ipcctl *ax;
        struct audit_context *context = current->audit_context;
 
-       if (likely(!context))
-               return 0;
-
        ax = kmalloc(sizeof(*ax), GFP_ATOMIC);
        if (!ax)
                return -ENOMEM;
@@ -1223,7 +1574,6 @@ int audit_ipc_set_perm(unsigned long qbytes, uid_t uid, gid_t gid, mode_t mode,
        ax->uid = uid;
        ax->gid = gid;
        ax->mode = mode;
-       selinux_get_ipc_sid(ipcp, &ax->osid);
 
        ax->d.type = AUDIT_IPC_SET_PERM;
        ax->d.next = context->aux;
@@ -1231,6 +1581,39 @@ int audit_ipc_set_perm(unsigned long qbytes, uid_t uid, gid_t gid, mode_t mode,
        return 0;
 }
 
+int audit_bprm(struct linux_binprm *bprm)
+{
+       struct audit_aux_data_execve *ax;
+       struct audit_context *context = current->audit_context;
+       unsigned long p, next;
+       void *to;
+
+       if (likely(!audit_enabled || !context))
+               return 0;
+
+       ax = kmalloc(sizeof(*ax) + PAGE_SIZE * MAX_ARG_PAGES - bprm->p,
+                               GFP_KERNEL);
+       if (!ax)
+               return -ENOMEM;
+
+       ax->argc = bprm->argc;
+       ax->envc = bprm->envc;
+       for (p = bprm->p, to = ax->mem; p < MAX_ARG_PAGES*PAGE_SIZE; p = next) {
+               struct page *page = bprm->page[p / PAGE_SIZE];
+               void *kaddr = kmap(page);
+               next = (p + PAGE_SIZE) & ~(PAGE_SIZE - 1);
+               memcpy(to, kaddr + (p & (PAGE_SIZE - 1)), next - p);
+               to += next - p;
+               kunmap(page);
+       }
+
+       ax->d.type = AUDIT_EXECVE;
+       ax->d.next = context->aux;
+       context->aux = (void *)ax;
+       return 0;
+}
+
+
 /**
  * audit_socketcall - record audit data for sys_socketcall
  * @nargs: number of args
@@ -1325,19 +1708,20 @@ int audit_avc_path(struct dentry *dentry, struct vfsmount *mnt)
  * If the audit subsystem is being terminated, record the task (pid)
  * and uid that is doing that.
  */
-void audit_signal_info(int sig, struct task_struct *t)
+void __audit_signal_info(int sig, struct task_struct *t)
 {
        extern pid_t audit_sig_pid;
        extern uid_t audit_sig_uid;
-
-       if (unlikely(audit_pid && t->tgid == audit_pid)) {
-               if (sig == SIGTERM || sig == SIGHUP) {
-                       struct audit_context *ctx = current->audit_context;
-                       audit_sig_pid = current->pid;
-                       if (ctx)
-                               audit_sig_uid = ctx->loginuid;
-                       else
-                               audit_sig_uid = current->uid;
-               }
+       extern u32 audit_sig_sid;
+
+       if (sig == SIGTERM || sig == SIGHUP || sig == SIGUSR1) {
+               struct task_struct *tsk = current;
+               struct audit_context *ctx = tsk->audit_context;
+               audit_sig_pid = tsk->pid;
+               if (ctx)
+                       audit_sig_uid = ctx->loginuid;
+               else
+                       audit_sig_uid = tsk->uid;
+               selinux_get_task_sid(tsk, &audit_sig_sid);
        }
 }
index e5f8aea78ffebd71a1322ca8279321aac3b077e6..1b3c921737e219a6f56281db270f8ccfbef0e6ab 100644 (file)
 #include <linux/syscalls.h>
 #include <linux/ptrace.h>
 #include <linux/signal.h>
-#include <linux/audit.h>
 #include <linux/capability.h>
 #include <asm/param.h>
 #include <asm/uaccess.h>
 #include <asm/unistd.h>
 #include <asm/siginfo.h>
+#include "audit.h"     /* audit_signal_info() */
 
 /*
  * SLAB caches for signal bits.
index e82726faeeffb4850cc49cc6e112cf28b143d4cb..0d656e61621dd191a38914f24a1492e82529a24e 100644 (file)
@@ -150,7 +150,7 @@ extern ctl_table random_table[];
 #ifdef CONFIG_UNIX98_PTYS
 extern ctl_table pty_table[];
 #endif
-#ifdef CONFIG_INOTIFY
+#ifdef CONFIG_INOTIFY_USER
 extern ctl_table inotify_table[];
 #endif
 
@@ -1028,7 +1028,7 @@ static ctl_table fs_table[] = {
                .mode           = 0644,
                .proc_handler   = &proc_doulongvec_minmax,
        },
-#ifdef CONFIG_INOTIFY
+#ifdef CONFIG_INOTIFY_USER
        {
                .ctl_name       = FS_INOTIFY,
                .procname       = "inotify",
index 2116642f42c62a2f6b02a7238a336d82621ad31f..4b1eb745afa1031fbf0ec9ef84955ef8e201d664 100644 (file)
@@ -140,7 +140,7 @@ struct user_struct * alloc_uid(uid_t uid)
                atomic_set(&new->processes, 0);
                atomic_set(&new->files, 0);
                atomic_set(&new->sigpending, 0);
-#ifdef CONFIG_INOTIFY
+#ifdef CONFIG_INOTIFY_USER
                atomic_set(&new->inotify_watches, 0);
                atomic_set(&new->inotify_devs, 0);
 #endif
index c284dbb8b8c02fbba51aa562d5e4fd4551e641f3..e9548bc049e1d9020161b92fb362743ee7261957 100644 (file)
@@ -1980,7 +1980,7 @@ int selinux_audit_rule_match(u32 ctxid, u32 field, u32 op,
                break;
        case AUDIT_SE_SEN:
        case AUDIT_SE_CLR:
-               level = (op == AUDIT_SE_SEN ?
+               level = (field == AUDIT_SE_SEN ?
                         &ctxt->range.level[0] : &ctxt->range.level[1]);
                switch (op) {
                case AUDIT_EQUAL: