LCOV - code coverage report
Current view: top level - fs/notify/inotify - inotify_fsnotify.c (source / functions) Hit Total Coverage
Test: landlock.info Lines: 61 77 79.2 %
Date: 2021-04-22 12:43:58 Functions: 7 8 87.5 %

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

Generated by: LCOV version 1.14