LCOV - code coverage report
Current view: top level - fs/notify/inotify - inotify_fsnotify.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 0 64 0.0 %
Date: 2023-04-06 08:38:28 Functions: 0 8 0.0 %

          Line data    Source code
       1             : // SPDX-License-Identifier: GPL-2.0-or-later
       2             : /*
       3             :  * fs/inotify_user.c - inotify support for userspace
       4             :  *
       5             :  * Authors:
       6             :  *      John McCutchan  <ttb@tentacle.dhs.org>
       7             :  *      Robert Love     <rml@novell.com>
       8             :  *
       9             :  * Copyright (C) 2005 John McCutchan
      10             :  * Copyright 2006 Hewlett-Packard Development Company, L.P.
      11             :  *
      12             :  * Copyright (C) 2009 Eric Paris <Red Hat Inc>
      13             :  * inotify was largely rewriten to make use of the fsnotify infrastructure
      14             :  */
      15             : 
      16             : #include <linux/dcache.h> /* d_unlinked */
      17             : #include <linux/fs.h> /* struct inode */
      18             : #include <linux/fsnotify_backend.h>
      19             : #include <linux/inotify.h>
      20             : #include <linux/path.h> /* struct path */
      21             : #include <linux/slab.h> /* kmem_* */
      22             : #include <linux/types.h>
      23             : #include <linux/sched.h>
      24             : #include <linux/sched/user.h>
      25             : #include <linux/sched/mm.h>
      26             : 
      27             : #include "inotify.h"
      28             : 
      29             : /*
      30             :  * Check if 2 events contain the same information.
      31             :  */
      32           0 : static bool event_compare(struct fsnotify_event *old_fsn,
      33             :                           struct fsnotify_event *new_fsn)
      34             : {
      35             :         struct inotify_event_info *old, *new;
      36             : 
      37           0 :         old = INOTIFY_E(old_fsn);
      38           0 :         new = INOTIFY_E(new_fsn);
      39           0 :         if (old->mask & FS_IN_IGNORED)
      40             :                 return false;
      41           0 :         if ((old->mask == new->mask) &&
      42           0 :             (old->wd == new->wd) &&
      43           0 :             (old->name_len == new->name_len) &&
      44           0 :             (!old->name_len || !strcmp(old->name, new->name)))
      45             :                 return true;
      46             :         return false;
      47             : }
      48             : 
      49           0 : static int inotify_merge(struct fsnotify_group *group,
      50             :                          struct fsnotify_event *event)
      51             : {
      52           0 :         struct list_head *list = &group->notification_list;
      53             :         struct fsnotify_event *last_event;
      54             : 
      55           0 :         last_event = list_entry(list->prev, struct fsnotify_event, list);
      56           0 :         return event_compare(last_event, event);
      57             : }
      58             : 
      59           0 : int inotify_handle_inode_event(struct fsnotify_mark *inode_mark, u32 mask,
      60             :                                struct inode *inode, struct inode *dir,
      61             :                                const struct qstr *name, u32 cookie)
      62             : {
      63             :         struct inotify_inode_mark *i_mark;
      64             :         struct inotify_event_info *event;
      65             :         struct fsnotify_event *fsn_event;
      66           0 :         struct fsnotify_group *group = inode_mark->group;
      67             :         int ret;
      68           0 :         int len = 0;
      69           0 :         int alloc_len = sizeof(struct inotify_event_info);
      70             :         struct mem_cgroup *old_memcg;
      71             : 
      72           0 :         if (name) {
      73           0 :                 len = name->len;
      74           0 :                 alloc_len += len + 1;
      75             :         }
      76             : 
      77             :         pr_debug("%s: group=%p mark=%p mask=%x\n", __func__, group, inode_mark,
      78             :                  mask);
      79             : 
      80           0 :         i_mark = container_of(inode_mark, struct inotify_inode_mark,
      81             :                               fsn_mark);
      82             : 
      83             :         /*
      84             :          * Whoever is interested in the event, pays for the allocation. Do not
      85             :          * trigger OOM killer in the target monitoring memcg as it may have
      86             :          * security repercussion.
      87             :          */
      88           0 :         old_memcg = set_active_memcg(group->memcg);
      89           0 :         event = kmalloc(alloc_len, GFP_KERNEL_ACCOUNT | __GFP_RETRY_MAYFAIL);
      90             :         set_active_memcg(old_memcg);
      91             : 
      92           0 :         if (unlikely(!event)) {
      93             :                 /*
      94             :                  * Treat lost event due to ENOMEM the same way as queue
      95             :                  * overflow to let userspace know event was lost.
      96             :                  */
      97           0 :                 fsnotify_queue_overflow(group);
      98           0 :                 return -ENOMEM;
      99             :         }
     100             : 
     101             :         /*
     102             :          * We now report FS_ISDIR flag with MOVE_SELF and DELETE_SELF events
     103             :          * for fanotify. inotify never reported IN_ISDIR with those events.
     104             :          * It looks like an oversight, but to avoid the risk of breaking
     105             :          * existing inotify programs, mask the flag out from those events.
     106             :          */
     107           0 :         if (mask & (IN_MOVE_SELF | IN_DELETE_SELF))
     108           0 :                 mask &= ~IN_ISDIR;
     109             : 
     110           0 :         fsn_event = &event->fse;
     111           0 :         fsnotify_init_event(fsn_event);
     112           0 :         event->mask = mask;
     113           0 :         event->wd = i_mark->wd;
     114           0 :         event->sync_cookie = cookie;
     115           0 :         event->name_len = len;
     116           0 :         if (len)
     117           0 :                 strcpy(event->name, name->name);
     118             : 
     119           0 :         ret = fsnotify_add_event(group, fsn_event, inotify_merge);
     120           0 :         if (ret) {
     121             :                 /* Our event wasn't used in the end. Free it. */
     122           0 :                 fsnotify_destroy_event(group, fsn_event);
     123             :         }
     124             : 
     125           0 :         if (inode_mark->flags & FSNOTIFY_MARK_FLAG_IN_ONESHOT)
     126           0 :                 fsnotify_destroy_mark(inode_mark, group);
     127             : 
     128             :         return 0;
     129             : }
     130             : 
     131           0 : static void inotify_freeing_mark(struct fsnotify_mark *fsn_mark, struct fsnotify_group *group)
     132             : {
     133           0 :         inotify_ignored_and_remove_idr(fsn_mark, group);
     134           0 : }
     135             : 
     136             : /*
     137             :  * This is NEVER supposed to be called.  Inotify marks should either have been
     138             :  * removed from the idr when the watch was removed or in the
     139             :  * fsnotify_destroy_mark_by_group() call when the inotify instance was being
     140             :  * torn down.  This is only called if the idr is about to be freed but there
     141             :  * are still marks in it.
     142             :  */
     143           0 : static int idr_callback(int id, void *p, void *data)
     144             : {
     145             :         struct fsnotify_mark *fsn_mark;
     146             :         struct inotify_inode_mark *i_mark;
     147             :         static bool warned = false;
     148             : 
     149           0 :         if (warned)
     150             :                 return 0;
     151             : 
     152           0 :         warned = true;
     153           0 :         fsn_mark = p;
     154           0 :         i_mark = container_of(fsn_mark, struct inotify_inode_mark, fsn_mark);
     155             : 
     156           0 :         WARN(1, "inotify closing but id=%d for fsn_mark=%p in group=%p still in "
     157             :                 "idr.  Probably leaking memory\n", id, p, data);
     158             : 
     159             :         /*
     160             :          * I'm taking the liberty of assuming that the mark in question is a
     161             :          * valid address and I'm dereferencing it.  This might help to figure
     162             :          * out why we got here and the panic is no worse than the original
     163             :          * BUG() that was here.
     164             :          */
     165           0 :         if (fsn_mark)
     166           0 :                 printk(KERN_WARNING "fsn_mark->group=%p wd=%d\n",
     167             :                         fsn_mark->group, i_mark->wd);
     168             :         return 0;
     169             : }
     170             : 
     171           0 : static void inotify_free_group_priv(struct fsnotify_group *group)
     172             : {
     173             :         /* ideally the idr is empty and we won't hit the BUG in the callback */
     174           0 :         idr_for_each(&group->inotify_data.idr, idr_callback, group);
     175           0 :         idr_destroy(&group->inotify_data.idr);
     176           0 :         if (group->inotify_data.ucounts)
     177           0 :                 dec_inotify_instances(group->inotify_data.ucounts);
     178           0 : }
     179             : 
     180           0 : static void inotify_free_event(struct fsnotify_group *group,
     181             :                                struct fsnotify_event *fsn_event)
     182             : {
     183           0 :         kfree(INOTIFY_E(fsn_event));
     184           0 : }
     185             : 
     186             : /* ding dong the mark is dead */
     187           0 : static void inotify_free_mark(struct fsnotify_mark *fsn_mark)
     188             : {
     189             :         struct inotify_inode_mark *i_mark;
     190             : 
     191           0 :         i_mark = container_of(fsn_mark, struct inotify_inode_mark, fsn_mark);
     192             : 
     193           0 :         kmem_cache_free(inotify_inode_mark_cachep, i_mark);
     194           0 : }
     195             : 
     196             : const struct fsnotify_ops inotify_fsnotify_ops = {
     197             :         .handle_inode_event = inotify_handle_inode_event,
     198             :         .free_group_priv = inotify_free_group_priv,
     199             :         .free_event = inotify_free_event,
     200             :         .freeing_mark = inotify_freeing_mark,
     201             :         .free_mark = inotify_free_mark,
     202             : };

Generated by: LCOV version 1.14