\n
-----BEGIN PGP SIGNATURE----- iQEzBAABCAAdFiEEq1nRK9aeMoq1VSgcnJ2qBz9kQNkFAmGFN6IACgkQnJ2qBz9k QNkfYwgA1w5x/CsN2IMZdx6FTuZFgbOvQpBMTry8iuOPKK3UyIkZaUirTVLKR0cm k3QbBR9/vTfQTNg5weuFJcbPZZaCXKEvlPGvDh+pumMbfTkMwL3FADweNBoZ3PzO EiRrV45AbRgSMOzsfURzCz1T53Gd8fYM3pXxmNXG+bnE7+Ea+heKgor8/jFc4U3w kAKZTfyCiheo7KxVhFGnkGI3ZhIbnbZne4seY/CE4qtv7/bmBE7bhGpmv8LT5FUn h/JBDLjFU0fzJpplXE6n/VHXeGaUwb8adnYpzojWQ0lLYFrMIZFQ0KkDK6PNwmJF MKWGqRxDkf54oeWuEAJ9t4/OorqM9A== =ltE7 -----END PGP SIGNATURE----- Merge tag 'fsnotify_for_v5.16-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/jack/linux-fs Pull fsnotify updates from Jan Kara: "Support for reporting filesystem errors through fanotify so that system health monitoring daemons can watch for these and act instead of scraping system logs" * tag 'fsnotify_for_v5.16-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/jack/linux-fs: (34 commits) samples: remove duplicate include in fs-monitor.c samples: Fix warning in fsnotify sample docs: Fix formatting of literal sections in fanotify docs samples: Make fs-monitor depend on libc and headers docs: Document the FAN_FS_ERROR event samples: Add fs error monitoring example ext4: Send notifications on error fanotify: Allow users to request FAN_FS_ERROR events fanotify: Emit generic error info for error event fanotify: Report fid info for file related file system errors fanotify: WARN_ON against too large file handles fanotify: Add helpers to decide whether to report FID/DFID fanotify: Wrap object_fh inline space in a creator macro fanotify: Support merging of error events fanotify: Support enqueueing of error events fanotify: Pre-allocate pool of error events fanotify: Reserve UAPI bits for FAN_FS_ERROR fsnotify: Support FS_ERROR event type fanotify: Require fid_mode for any non-fd event fanotify: Encode empty file handle when no inode is provided ...
This commit is contained in:
commit
2acda7549e
78
Documentation/admin-guide/filesystem-monitoring.rst
Normal file
78
Documentation/admin-guide/filesystem-monitoring.rst
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
.. SPDX-License-Identifier: GPL-2.0
|
||||||
|
|
||||||
|
====================================
|
||||||
|
File system Monitoring with fanotify
|
||||||
|
====================================
|
||||||
|
|
||||||
|
File system Error Reporting
|
||||||
|
===========================
|
||||||
|
|
||||||
|
Fanotify supports the FAN_FS_ERROR event type for file system-wide error
|
||||||
|
reporting. It is meant to be used by file system health monitoring
|
||||||
|
daemons, which listen for these events and take actions (notify
|
||||||
|
sysadmin, start recovery) when a file system problem is detected.
|
||||||
|
|
||||||
|
By design, a FAN_FS_ERROR notification exposes sufficient information
|
||||||
|
for a monitoring tool to know a problem in the file system has happened.
|
||||||
|
It doesn't necessarily provide a user space application with semantics
|
||||||
|
to verify an IO operation was successfully executed. That is out of
|
||||||
|
scope for this feature. Instead, it is only meant as a framework for
|
||||||
|
early file system problem detection and reporting recovery tools.
|
||||||
|
|
||||||
|
When a file system operation fails, it is common for dozens of kernel
|
||||||
|
errors to cascade after the initial failure, hiding the original failure
|
||||||
|
log, which is usually the most useful debug data to troubleshoot the
|
||||||
|
problem. For this reason, FAN_FS_ERROR tries to report only the first
|
||||||
|
error that occurred for a file system since the last notification, and
|
||||||
|
it simply counts additional errors. This ensures that the most
|
||||||
|
important pieces of information are never lost.
|
||||||
|
|
||||||
|
FAN_FS_ERROR requires the fanotify group to be setup with the
|
||||||
|
FAN_REPORT_FID flag.
|
||||||
|
|
||||||
|
At the time of this writing, the only file system that emits FAN_FS_ERROR
|
||||||
|
notifications is Ext4.
|
||||||
|
|
||||||
|
A FAN_FS_ERROR Notification has the following format::
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
[ Notification Metadata (Mandatory) ]
|
||||||
|
[ Generic Error Record (Mandatory) ]
|
||||||
|
[ FID record (Mandatory) ]
|
||||||
|
|
||||||
|
The order of records is not guaranteed, and new records might be added
|
||||||
|
in the future. Therefore, applications must not rely on the order and
|
||||||
|
must be prepared to skip over unknown records. Please refer to
|
||||||
|
``samples/fanotify/fs-monitor.c`` for an example parser.
|
||||||
|
|
||||||
|
Generic error record
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
The generic error record provides enough information for a file system
|
||||||
|
agnostic tool to learn about a problem in the file system, without
|
||||||
|
providing any additional details about the problem. This record is
|
||||||
|
identified by ``struct fanotify_event_info_header.info_type`` being set
|
||||||
|
to FAN_EVENT_INFO_TYPE_ERROR.
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
struct fanotify_event_info_error {
|
||||||
|
struct fanotify_event_info_header hdr;
|
||||||
|
__s32 error;
|
||||||
|
__u32 error_count;
|
||||||
|
};
|
||||||
|
|
||||||
|
The `error` field identifies the type of error using errno values.
|
||||||
|
`error_count` tracks the number of errors that occurred and were
|
||||||
|
suppressed to preserve the original error information, since the last
|
||||||
|
notification.
|
||||||
|
|
||||||
|
FID record
|
||||||
|
----------
|
||||||
|
|
||||||
|
The FID record can be used to uniquely identify the inode that triggered
|
||||||
|
the error through the combination of fsid and file handle. A file system
|
||||||
|
specific application can use that information to attempt a recovery
|
||||||
|
procedure. Errors that are not related to an inode are reported with an
|
||||||
|
empty file handle of type FILEID_INVALID.
|
@ -82,6 +82,7 @@ configure specific aspects of kernel behavior to your liking.
|
|||||||
edid
|
edid
|
||||||
efi-stub
|
efi-stub
|
||||||
ext4
|
ext4
|
||||||
|
filesystem-monitoring
|
||||||
nfs/index
|
nfs/index
|
||||||
gpio/index
|
gpio/index
|
||||||
highuid
|
highuid
|
||||||
|
@ -46,6 +46,7 @@
|
|||||||
#include <linux/part_stat.h>
|
#include <linux/part_stat.h>
|
||||||
#include <linux/kthread.h>
|
#include <linux/kthread.h>
|
||||||
#include <linux/freezer.h>
|
#include <linux/freezer.h>
|
||||||
|
#include <linux/fsnotify.h>
|
||||||
|
|
||||||
#include "ext4.h"
|
#include "ext4.h"
|
||||||
#include "ext4_extents.h" /* Needed for trace points definition */
|
#include "ext4_extents.h" /* Needed for trace points definition */
|
||||||
@ -759,6 +760,8 @@ void __ext4_error(struct super_block *sb, const char *function,
|
|||||||
sb->s_id, function, line, current->comm, &vaf);
|
sb->s_id, function, line, current->comm, &vaf);
|
||||||
va_end(args);
|
va_end(args);
|
||||||
}
|
}
|
||||||
|
fsnotify_sb_error(sb, NULL, error ? error : EFSCORRUPTED);
|
||||||
|
|
||||||
ext4_handle_error(sb, force_ro, error, 0, block, function, line);
|
ext4_handle_error(sb, force_ro, error, 0, block, function, line);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -789,6 +792,8 @@ void __ext4_error_inode(struct inode *inode, const char *function,
|
|||||||
current->comm, &vaf);
|
current->comm, &vaf);
|
||||||
va_end(args);
|
va_end(args);
|
||||||
}
|
}
|
||||||
|
fsnotify_sb_error(inode->i_sb, inode, error ? error : EFSCORRUPTED);
|
||||||
|
|
||||||
ext4_handle_error(inode->i_sb, false, error, inode->i_ino, block,
|
ext4_handle_error(inode->i_sb, false, error, inode->i_ino, block,
|
||||||
function, line);
|
function, line);
|
||||||
}
|
}
|
||||||
@ -827,6 +832,8 @@ void __ext4_error_file(struct file *file, const char *function,
|
|||||||
current->comm, path, &vaf);
|
current->comm, path, &vaf);
|
||||||
va_end(args);
|
va_end(args);
|
||||||
}
|
}
|
||||||
|
fsnotify_sb_error(inode->i_sb, inode, EFSCORRUPTED);
|
||||||
|
|
||||||
ext4_handle_error(inode->i_sb, false, EFSCORRUPTED, inode->i_ino, block,
|
ext4_handle_error(inode->i_sb, false, EFSCORRUPTED, inode->i_ino, block,
|
||||||
function, line);
|
function, line);
|
||||||
}
|
}
|
||||||
@ -894,6 +901,7 @@ void __ext4_std_error(struct super_block *sb, const char *function,
|
|||||||
printk(KERN_CRIT "EXT4-fs error (device %s) in %s:%d: %s\n",
|
printk(KERN_CRIT "EXT4-fs error (device %s) in %s:%d: %s\n",
|
||||||
sb->s_id, function, line, errstr);
|
sb->s_id, function, line, errstr);
|
||||||
}
|
}
|
||||||
|
fsnotify_sb_error(sb, NULL, errno ? errno : EFSCORRUPTED);
|
||||||
|
|
||||||
ext4_handle_error(sb, false, -errno, 0, 0, function, line);
|
ext4_handle_error(sb, false, -errno, 0, 0, function, line);
|
||||||
}
|
}
|
||||||
|
@ -602,6 +602,9 @@ nfsd_file_fsnotify_handle_event(struct fsnotify_mark *mark, u32 mask,
|
|||||||
struct inode *inode, struct inode *dir,
|
struct inode *inode, struct inode *dir,
|
||||||
const struct qstr *name, u32 cookie)
|
const struct qstr *name, u32 cookie)
|
||||||
{
|
{
|
||||||
|
if (WARN_ON_ONCE(!inode))
|
||||||
|
return 0;
|
||||||
|
|
||||||
trace_nfsd_file_fsnotify_handle_event(inode, mask);
|
trace_nfsd_file_fsnotify_handle_event(inode, mask);
|
||||||
|
|
||||||
/* Should be no marks on non-regular files */
|
/* Should be no marks on non-regular files */
|
||||||
|
@ -111,6 +111,16 @@ static bool fanotify_name_event_equal(struct fanotify_name_event *fne1,
|
|||||||
return fanotify_info_equal(info1, info2);
|
return fanotify_info_equal(info1, info2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool fanotify_error_event_equal(struct fanotify_error_event *fee1,
|
||||||
|
struct fanotify_error_event *fee2)
|
||||||
|
{
|
||||||
|
/* Error events against the same file system are always merged. */
|
||||||
|
if (!fanotify_fsid_equal(&fee1->fsid, &fee2->fsid))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static bool fanotify_should_merge(struct fanotify_event *old,
|
static bool fanotify_should_merge(struct fanotify_event *old,
|
||||||
struct fanotify_event *new)
|
struct fanotify_event *new)
|
||||||
{
|
{
|
||||||
@ -141,6 +151,9 @@ static bool fanotify_should_merge(struct fanotify_event *old,
|
|||||||
case FANOTIFY_EVENT_TYPE_FID_NAME:
|
case FANOTIFY_EVENT_TYPE_FID_NAME:
|
||||||
return fanotify_name_event_equal(FANOTIFY_NE(old),
|
return fanotify_name_event_equal(FANOTIFY_NE(old),
|
||||||
FANOTIFY_NE(new));
|
FANOTIFY_NE(new));
|
||||||
|
case FANOTIFY_EVENT_TYPE_FS_ERROR:
|
||||||
|
return fanotify_error_event_equal(FANOTIFY_EE(old),
|
||||||
|
FANOTIFY_EE(new));
|
||||||
default:
|
default:
|
||||||
WARN_ON_ONCE(1);
|
WARN_ON_ONCE(1);
|
||||||
}
|
}
|
||||||
@ -176,6 +189,10 @@ static int fanotify_merge(struct fsnotify_group *group,
|
|||||||
break;
|
break;
|
||||||
if (fanotify_should_merge(old, new)) {
|
if (fanotify_should_merge(old, new)) {
|
||||||
old->mask |= new->mask;
|
old->mask |= new->mask;
|
||||||
|
|
||||||
|
if (fanotify_is_error_event(old->mask))
|
||||||
|
FANOTIFY_EE(old)->err_count++;
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -343,13 +360,23 @@ static u32 fanotify_group_event_mask(struct fsnotify_group *group,
|
|||||||
static int fanotify_encode_fh_len(struct inode *inode)
|
static int fanotify_encode_fh_len(struct inode *inode)
|
||||||
{
|
{
|
||||||
int dwords = 0;
|
int dwords = 0;
|
||||||
|
int fh_len;
|
||||||
|
|
||||||
if (!inode)
|
if (!inode)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
exportfs_encode_inode_fh(inode, NULL, &dwords, NULL);
|
exportfs_encode_inode_fh(inode, NULL, &dwords, NULL);
|
||||||
|
fh_len = dwords << 2;
|
||||||
|
|
||||||
return dwords << 2;
|
/*
|
||||||
|
* struct fanotify_error_event might be preallocated and is
|
||||||
|
* limited to MAX_HANDLE_SZ. This should never happen, but
|
||||||
|
* safeguard by forcing an invalid file handle.
|
||||||
|
*/
|
||||||
|
if (WARN_ON_ONCE(fh_len > MAX_HANDLE_SZ))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return fh_len;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -370,8 +397,14 @@ static int fanotify_encode_fh(struct fanotify_fh *fh, struct inode *inode,
|
|||||||
fh->type = FILEID_ROOT;
|
fh->type = FILEID_ROOT;
|
||||||
fh->len = 0;
|
fh->len = 0;
|
||||||
fh->flags = 0;
|
fh->flags = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Invalid FHs are used by FAN_FS_ERROR for errors not
|
||||||
|
* linked to any inode. The f_handle won't be reported
|
||||||
|
* back to userspace.
|
||||||
|
*/
|
||||||
if (!inode)
|
if (!inode)
|
||||||
return 0;
|
goto out;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* !gpf means preallocated variable size fh, but fh_len could
|
* !gpf means preallocated variable size fh, but fh_len could
|
||||||
@ -403,8 +436,13 @@ static int fanotify_encode_fh(struct fanotify_fh *fh, struct inode *inode,
|
|||||||
fh->type = type;
|
fh->type = type;
|
||||||
fh->len = fh_len;
|
fh->len = fh_len;
|
||||||
|
|
||||||
/* Mix fh into event merge key */
|
out:
|
||||||
*hash ^= fanotify_hash_fh(fh);
|
/*
|
||||||
|
* Mix fh into event merge key. Hash might be NULL in case of
|
||||||
|
* unhashed FID events (i.e. FAN_FS_ERROR).
|
||||||
|
*/
|
||||||
|
if (hash)
|
||||||
|
*hash ^= fanotify_hash_fh(fh);
|
||||||
|
|
||||||
return FANOTIFY_FH_HDR_LEN + fh_len;
|
return FANOTIFY_FH_HDR_LEN + fh_len;
|
||||||
|
|
||||||
@ -452,7 +490,7 @@ static struct inode *fanotify_dfid_inode(u32 event_mask, const void *data,
|
|||||||
if (event_mask & ALL_FSNOTIFY_DIRENT_EVENTS)
|
if (event_mask & ALL_FSNOTIFY_DIRENT_EVENTS)
|
||||||
return dir;
|
return dir;
|
||||||
|
|
||||||
if (S_ISDIR(inode->i_mode))
|
if (inode && S_ISDIR(inode->i_mode))
|
||||||
return inode;
|
return inode;
|
||||||
|
|
||||||
return dir;
|
return dir;
|
||||||
@ -563,6 +601,44 @@ static struct fanotify_event *fanotify_alloc_name_event(struct inode *id,
|
|||||||
return &fne->fae;
|
return &fne->fae;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct fanotify_event *fanotify_alloc_error_event(
|
||||||
|
struct fsnotify_group *group,
|
||||||
|
__kernel_fsid_t *fsid,
|
||||||
|
const void *data, int data_type,
|
||||||
|
unsigned int *hash)
|
||||||
|
{
|
||||||
|
struct fs_error_report *report =
|
||||||
|
fsnotify_data_error_report(data, data_type);
|
||||||
|
struct inode *inode;
|
||||||
|
struct fanotify_error_event *fee;
|
||||||
|
int fh_len;
|
||||||
|
|
||||||
|
if (WARN_ON_ONCE(!report))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
fee = mempool_alloc(&group->fanotify_data.error_events_pool, GFP_NOFS);
|
||||||
|
if (!fee)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
fee->fae.type = FANOTIFY_EVENT_TYPE_FS_ERROR;
|
||||||
|
fee->error = report->error;
|
||||||
|
fee->err_count = 1;
|
||||||
|
fee->fsid = *fsid;
|
||||||
|
|
||||||
|
inode = report->inode;
|
||||||
|
fh_len = fanotify_encode_fh_len(inode);
|
||||||
|
|
||||||
|
/* Bad fh_len. Fallback to using an invalid fh. Should never happen. */
|
||||||
|
if (!fh_len && inode)
|
||||||
|
inode = NULL;
|
||||||
|
|
||||||
|
fanotify_encode_fh(&fee->object_fh, inode, fh_len, NULL, 0);
|
||||||
|
|
||||||
|
*hash ^= fanotify_hash_fsid(fsid);
|
||||||
|
|
||||||
|
return &fee->fae;
|
||||||
|
}
|
||||||
|
|
||||||
static struct fanotify_event *fanotify_alloc_event(struct fsnotify_group *group,
|
static struct fanotify_event *fanotify_alloc_event(struct fsnotify_group *group,
|
||||||
u32 mask, const void *data,
|
u32 mask, const void *data,
|
||||||
int data_type, struct inode *dir,
|
int data_type, struct inode *dir,
|
||||||
@ -630,6 +706,9 @@ static struct fanotify_event *fanotify_alloc_event(struct fsnotify_group *group,
|
|||||||
|
|
||||||
if (fanotify_is_perm_event(mask)) {
|
if (fanotify_is_perm_event(mask)) {
|
||||||
event = fanotify_alloc_perm_event(path, gfp);
|
event = fanotify_alloc_perm_event(path, gfp);
|
||||||
|
} else if (fanotify_is_error_event(mask)) {
|
||||||
|
event = fanotify_alloc_error_event(group, fsid, data,
|
||||||
|
data_type, &hash);
|
||||||
} else if (name_event && (file_name || child)) {
|
} else if (name_event && (file_name || child)) {
|
||||||
event = fanotify_alloc_name_event(id, fsid, file_name, child,
|
event = fanotify_alloc_name_event(id, fsid, file_name, child,
|
||||||
&hash, gfp);
|
&hash, gfp);
|
||||||
@ -702,6 +781,9 @@ static void fanotify_insert_event(struct fsnotify_group *group,
|
|||||||
|
|
||||||
assert_spin_locked(&group->notification_lock);
|
assert_spin_locked(&group->notification_lock);
|
||||||
|
|
||||||
|
if (!fanotify_is_hashed_event(event->mask))
|
||||||
|
return;
|
||||||
|
|
||||||
pr_debug("%s: group=%p event=%p bucket=%u\n", __func__,
|
pr_debug("%s: group=%p event=%p bucket=%u\n", __func__,
|
||||||
group, event, bucket);
|
group, event, bucket);
|
||||||
|
|
||||||
@ -738,8 +820,9 @@ static int fanotify_handle_event(struct fsnotify_group *group, u32 mask,
|
|||||||
BUILD_BUG_ON(FAN_ONDIR != FS_ISDIR);
|
BUILD_BUG_ON(FAN_ONDIR != FS_ISDIR);
|
||||||
BUILD_BUG_ON(FAN_OPEN_EXEC != FS_OPEN_EXEC);
|
BUILD_BUG_ON(FAN_OPEN_EXEC != FS_OPEN_EXEC);
|
||||||
BUILD_BUG_ON(FAN_OPEN_EXEC_PERM != FS_OPEN_EXEC_PERM);
|
BUILD_BUG_ON(FAN_OPEN_EXEC_PERM != FS_OPEN_EXEC_PERM);
|
||||||
|
BUILD_BUG_ON(FAN_FS_ERROR != FS_ERROR);
|
||||||
|
|
||||||
BUILD_BUG_ON(HWEIGHT32(ALL_FANOTIFY_EVENT_BITS) != 19);
|
BUILD_BUG_ON(HWEIGHT32(ALL_FANOTIFY_EVENT_BITS) != 20);
|
||||||
|
|
||||||
mask = fanotify_group_event_mask(group, iter_info, mask, data,
|
mask = fanotify_group_event_mask(group, iter_info, mask, data,
|
||||||
data_type, dir);
|
data_type, dir);
|
||||||
@ -778,9 +861,8 @@ static int fanotify_handle_event(struct fsnotify_group *group, u32 mask,
|
|||||||
}
|
}
|
||||||
|
|
||||||
fsn_event = &event->fse;
|
fsn_event = &event->fse;
|
||||||
ret = fsnotify_add_event(group, fsn_event, fanotify_merge,
|
ret = fsnotify_insert_event(group, fsn_event, fanotify_merge,
|
||||||
fanotify_is_hashed_event(mask) ?
|
fanotify_insert_event);
|
||||||
fanotify_insert_event : NULL);
|
|
||||||
if (ret) {
|
if (ret) {
|
||||||
/* Permission events shouldn't be merged */
|
/* Permission events shouldn't be merged */
|
||||||
BUG_ON(ret == 1 && mask & FANOTIFY_PERM_EVENTS);
|
BUG_ON(ret == 1 && mask & FANOTIFY_PERM_EVENTS);
|
||||||
@ -805,6 +887,9 @@ static void fanotify_free_group_priv(struct fsnotify_group *group)
|
|||||||
if (group->fanotify_data.ucounts)
|
if (group->fanotify_data.ucounts)
|
||||||
dec_ucount(group->fanotify_data.ucounts,
|
dec_ucount(group->fanotify_data.ucounts,
|
||||||
UCOUNT_FANOTIFY_GROUPS);
|
UCOUNT_FANOTIFY_GROUPS);
|
||||||
|
|
||||||
|
if (mempool_initialized(&group->fanotify_data.error_events_pool))
|
||||||
|
mempool_exit(&group->fanotify_data.error_events_pool);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void fanotify_free_path_event(struct fanotify_event *event)
|
static void fanotify_free_path_event(struct fanotify_event *event)
|
||||||
@ -833,7 +918,16 @@ static void fanotify_free_name_event(struct fanotify_event *event)
|
|||||||
kfree(FANOTIFY_NE(event));
|
kfree(FANOTIFY_NE(event));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void fanotify_free_event(struct fsnotify_event *fsn_event)
|
static void fanotify_free_error_event(struct fsnotify_group *group,
|
||||||
|
struct fanotify_event *event)
|
||||||
|
{
|
||||||
|
struct fanotify_error_event *fee = FANOTIFY_EE(event);
|
||||||
|
|
||||||
|
mempool_free(fee, &group->fanotify_data.error_events_pool);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void fanotify_free_event(struct fsnotify_group *group,
|
||||||
|
struct fsnotify_event *fsn_event)
|
||||||
{
|
{
|
||||||
struct fanotify_event *event;
|
struct fanotify_event *event;
|
||||||
|
|
||||||
@ -855,6 +949,9 @@ static void fanotify_free_event(struct fsnotify_event *fsn_event)
|
|||||||
case FANOTIFY_EVENT_TYPE_OVERFLOW:
|
case FANOTIFY_EVENT_TYPE_OVERFLOW:
|
||||||
kfree(event);
|
kfree(event);
|
||||||
break;
|
break;
|
||||||
|
case FANOTIFY_EVENT_TYPE_FS_ERROR:
|
||||||
|
fanotify_free_error_event(group, event);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
WARN_ON_ONCE(1);
|
WARN_ON_ONCE(1);
|
||||||
}
|
}
|
||||||
|
@ -141,6 +141,7 @@ enum fanotify_event_type {
|
|||||||
FANOTIFY_EVENT_TYPE_PATH,
|
FANOTIFY_EVENT_TYPE_PATH,
|
||||||
FANOTIFY_EVENT_TYPE_PATH_PERM,
|
FANOTIFY_EVENT_TYPE_PATH_PERM,
|
||||||
FANOTIFY_EVENT_TYPE_OVERFLOW, /* struct fanotify_event */
|
FANOTIFY_EVENT_TYPE_OVERFLOW, /* struct fanotify_event */
|
||||||
|
FANOTIFY_EVENT_TYPE_FS_ERROR, /* struct fanotify_error_event */
|
||||||
__FANOTIFY_EVENT_TYPE_NUM
|
__FANOTIFY_EVENT_TYPE_NUM
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -170,12 +171,18 @@ static inline void fanotify_init_event(struct fanotify_event *event,
|
|||||||
event->pid = NULL;
|
event->pid = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define FANOTIFY_INLINE_FH(name, size) \
|
||||||
|
struct { \
|
||||||
|
struct fanotify_fh (name); \
|
||||||
|
/* Space for object_fh.buf[] - access with fanotify_fh_buf() */ \
|
||||||
|
unsigned char _inline_fh_buf[(size)]; \
|
||||||
|
}
|
||||||
|
|
||||||
struct fanotify_fid_event {
|
struct fanotify_fid_event {
|
||||||
struct fanotify_event fae;
|
struct fanotify_event fae;
|
||||||
__kernel_fsid_t fsid;
|
__kernel_fsid_t fsid;
|
||||||
struct fanotify_fh object_fh;
|
|
||||||
/* Reserve space in object_fh.buf[] - access with fanotify_fh_buf() */
|
FANOTIFY_INLINE_FH(object_fh, FANOTIFY_INLINE_FH_LEN);
|
||||||
unsigned char _inline_fh_buf[FANOTIFY_INLINE_FH_LEN];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline struct fanotify_fid_event *
|
static inline struct fanotify_fid_event *
|
||||||
@ -196,12 +203,30 @@ FANOTIFY_NE(struct fanotify_event *event)
|
|||||||
return container_of(event, struct fanotify_name_event, fae);
|
return container_of(event, struct fanotify_name_event, fae);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct fanotify_error_event {
|
||||||
|
struct fanotify_event fae;
|
||||||
|
s32 error; /* Error reported by the Filesystem. */
|
||||||
|
u32 err_count; /* Suppressed errors count */
|
||||||
|
|
||||||
|
__kernel_fsid_t fsid; /* FSID this error refers to. */
|
||||||
|
|
||||||
|
FANOTIFY_INLINE_FH(object_fh, MAX_HANDLE_SZ);
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline struct fanotify_error_event *
|
||||||
|
FANOTIFY_EE(struct fanotify_event *event)
|
||||||
|
{
|
||||||
|
return container_of(event, struct fanotify_error_event, fae);
|
||||||
|
}
|
||||||
|
|
||||||
static inline __kernel_fsid_t *fanotify_event_fsid(struct fanotify_event *event)
|
static inline __kernel_fsid_t *fanotify_event_fsid(struct fanotify_event *event)
|
||||||
{
|
{
|
||||||
if (event->type == FANOTIFY_EVENT_TYPE_FID)
|
if (event->type == FANOTIFY_EVENT_TYPE_FID)
|
||||||
return &FANOTIFY_FE(event)->fsid;
|
return &FANOTIFY_FE(event)->fsid;
|
||||||
else if (event->type == FANOTIFY_EVENT_TYPE_FID_NAME)
|
else if (event->type == FANOTIFY_EVENT_TYPE_FID_NAME)
|
||||||
return &FANOTIFY_NE(event)->fsid;
|
return &FANOTIFY_NE(event)->fsid;
|
||||||
|
else if (event->type == FANOTIFY_EVENT_TYPE_FS_ERROR)
|
||||||
|
return &FANOTIFY_EE(event)->fsid;
|
||||||
else
|
else
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
@ -213,6 +238,8 @@ static inline struct fanotify_fh *fanotify_event_object_fh(
|
|||||||
return &FANOTIFY_FE(event)->object_fh;
|
return &FANOTIFY_FE(event)->object_fh;
|
||||||
else if (event->type == FANOTIFY_EVENT_TYPE_FID_NAME)
|
else if (event->type == FANOTIFY_EVENT_TYPE_FID_NAME)
|
||||||
return fanotify_info_file_fh(&FANOTIFY_NE(event)->info);
|
return fanotify_info_file_fh(&FANOTIFY_NE(event)->info);
|
||||||
|
else if (event->type == FANOTIFY_EVENT_TYPE_FS_ERROR)
|
||||||
|
return &FANOTIFY_EE(event)->object_fh;
|
||||||
else
|
else
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
@ -244,6 +271,19 @@ static inline int fanotify_event_dir_fh_len(struct fanotify_event *event)
|
|||||||
return info ? fanotify_info_dir_fh_len(info) : 0;
|
return info ? fanotify_info_dir_fh_len(info) : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline bool fanotify_event_has_object_fh(struct fanotify_event *event)
|
||||||
|
{
|
||||||
|
/* For error events, even zeroed fh are reported. */
|
||||||
|
if (event->type == FANOTIFY_EVENT_TYPE_FS_ERROR)
|
||||||
|
return true;
|
||||||
|
return fanotify_event_object_fh_len(event) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool fanotify_event_has_dir_fh(struct fanotify_event *event)
|
||||||
|
{
|
||||||
|
return fanotify_event_dir_fh_len(event) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
struct fanotify_path_event {
|
struct fanotify_path_event {
|
||||||
struct fanotify_event fae;
|
struct fanotify_event fae;
|
||||||
struct path path;
|
struct path path;
|
||||||
@ -287,6 +327,11 @@ static inline struct fanotify_event *FANOTIFY_E(struct fsnotify_event *fse)
|
|||||||
return container_of(fse, struct fanotify_event, fse);
|
return container_of(fse, struct fanotify_event, fse);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline bool fanotify_is_error_event(u32 mask)
|
||||||
|
{
|
||||||
|
return mask & FAN_FS_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
static inline bool fanotify_event_has_path(struct fanotify_event *event)
|
static inline bool fanotify_event_has_path(struct fanotify_event *event)
|
||||||
{
|
{
|
||||||
return event->type == FANOTIFY_EVENT_TYPE_PATH ||
|
return event->type == FANOTIFY_EVENT_TYPE_PATH ||
|
||||||
@ -315,7 +360,8 @@ static inline struct path *fanotify_event_path(struct fanotify_event *event)
|
|||||||
*/
|
*/
|
||||||
static inline bool fanotify_is_hashed_event(u32 mask)
|
static inline bool fanotify_is_hashed_event(u32 mask)
|
||||||
{
|
{
|
||||||
return !fanotify_is_perm_event(mask) && !(mask & FS_Q_OVERFLOW);
|
return !(fanotify_is_perm_event(mask) ||
|
||||||
|
fsnotify_is_overflow_event(mask));
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline unsigned int fanotify_event_hash_bucket(
|
static inline unsigned int fanotify_event_hash_bucket(
|
||||||
|
@ -30,6 +30,7 @@
|
|||||||
#define FANOTIFY_DEFAULT_MAX_EVENTS 16384
|
#define FANOTIFY_DEFAULT_MAX_EVENTS 16384
|
||||||
#define FANOTIFY_OLD_DEFAULT_MAX_MARKS 8192
|
#define FANOTIFY_OLD_DEFAULT_MAX_MARKS 8192
|
||||||
#define FANOTIFY_DEFAULT_MAX_GROUPS 128
|
#define FANOTIFY_DEFAULT_MAX_GROUPS 128
|
||||||
|
#define FANOTIFY_DEFAULT_FEE_POOL_SIZE 32
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Legacy fanotify marks limits (8192) is per group and we introduced a tunable
|
* Legacy fanotify marks limits (8192) is per group and we introduced a tunable
|
||||||
@ -114,6 +115,8 @@ struct kmem_cache *fanotify_perm_event_cachep __read_mostly;
|
|||||||
(sizeof(struct fanotify_event_info_fid) + sizeof(struct file_handle))
|
(sizeof(struct fanotify_event_info_fid) + sizeof(struct file_handle))
|
||||||
#define FANOTIFY_PIDFD_INFO_HDR_LEN \
|
#define FANOTIFY_PIDFD_INFO_HDR_LEN \
|
||||||
sizeof(struct fanotify_event_info_pidfd)
|
sizeof(struct fanotify_event_info_pidfd)
|
||||||
|
#define FANOTIFY_ERROR_INFO_LEN \
|
||||||
|
(sizeof(struct fanotify_event_info_error))
|
||||||
|
|
||||||
static int fanotify_fid_info_len(int fh_len, int name_len)
|
static int fanotify_fid_info_len(int fh_len, int name_len)
|
||||||
{
|
{
|
||||||
@ -126,17 +129,26 @@ static int fanotify_fid_info_len(int fh_len, int name_len)
|
|||||||
FANOTIFY_EVENT_ALIGN);
|
FANOTIFY_EVENT_ALIGN);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int fanotify_event_info_len(unsigned int info_mode,
|
static size_t fanotify_event_len(unsigned int info_mode,
|
||||||
struct fanotify_event *event)
|
struct fanotify_event *event)
|
||||||
{
|
{
|
||||||
struct fanotify_info *info = fanotify_event_info(event);
|
size_t event_len = FAN_EVENT_METADATA_LEN;
|
||||||
int dir_fh_len = fanotify_event_dir_fh_len(event);
|
struct fanotify_info *info;
|
||||||
int fh_len = fanotify_event_object_fh_len(event);
|
int dir_fh_len;
|
||||||
int info_len = 0;
|
int fh_len;
|
||||||
int dot_len = 0;
|
int dot_len = 0;
|
||||||
|
|
||||||
if (dir_fh_len) {
|
if (!info_mode)
|
||||||
info_len += fanotify_fid_info_len(dir_fh_len, info->name_len);
|
return event_len;
|
||||||
|
|
||||||
|
if (fanotify_is_error_event(event->mask))
|
||||||
|
event_len += FANOTIFY_ERROR_INFO_LEN;
|
||||||
|
|
||||||
|
info = fanotify_event_info(event);
|
||||||
|
|
||||||
|
if (fanotify_event_has_dir_fh(event)) {
|
||||||
|
dir_fh_len = fanotify_event_dir_fh_len(event);
|
||||||
|
event_len += fanotify_fid_info_len(dir_fh_len, info->name_len);
|
||||||
} else if ((info_mode & FAN_REPORT_NAME) &&
|
} else if ((info_mode & FAN_REPORT_NAME) &&
|
||||||
(event->mask & FAN_ONDIR)) {
|
(event->mask & FAN_ONDIR)) {
|
||||||
/*
|
/*
|
||||||
@ -147,12 +159,14 @@ static int fanotify_event_info_len(unsigned int info_mode,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (info_mode & FAN_REPORT_PIDFD)
|
if (info_mode & FAN_REPORT_PIDFD)
|
||||||
info_len += FANOTIFY_PIDFD_INFO_HDR_LEN;
|
event_len += FANOTIFY_PIDFD_INFO_HDR_LEN;
|
||||||
|
|
||||||
if (fh_len)
|
if (fanotify_event_has_object_fh(event)) {
|
||||||
info_len += fanotify_fid_info_len(fh_len, dot_len);
|
fh_len = fanotify_event_object_fh_len(event);
|
||||||
|
event_len += fanotify_fid_info_len(fh_len, dot_len);
|
||||||
|
}
|
||||||
|
|
||||||
return info_len;
|
return event_len;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -181,7 +195,7 @@ static void fanotify_unhash_event(struct fsnotify_group *group,
|
|||||||
static struct fanotify_event *get_one_event(struct fsnotify_group *group,
|
static struct fanotify_event *get_one_event(struct fsnotify_group *group,
|
||||||
size_t count)
|
size_t count)
|
||||||
{
|
{
|
||||||
size_t event_size = FAN_EVENT_METADATA_LEN;
|
size_t event_size;
|
||||||
struct fanotify_event *event = NULL;
|
struct fanotify_event *event = NULL;
|
||||||
struct fsnotify_event *fsn_event;
|
struct fsnotify_event *fsn_event;
|
||||||
unsigned int info_mode = FAN_GROUP_FLAG(group, FANOTIFY_INFO_MODES);
|
unsigned int info_mode = FAN_GROUP_FLAG(group, FANOTIFY_INFO_MODES);
|
||||||
@ -194,8 +208,7 @@ static struct fanotify_event *get_one_event(struct fsnotify_group *group,
|
|||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
event = FANOTIFY_E(fsn_event);
|
event = FANOTIFY_E(fsn_event);
|
||||||
if (info_mode)
|
event_size = fanotify_event_len(info_mode, event);
|
||||||
event_size += fanotify_event_info_len(info_mode, event);
|
|
||||||
|
|
||||||
if (event_size > count) {
|
if (event_size > count) {
|
||||||
event = ERR_PTR(-EINVAL);
|
event = ERR_PTR(-EINVAL);
|
||||||
@ -316,6 +329,28 @@ static int process_access_response(struct fsnotify_group *group,
|
|||||||
return -ENOENT;
|
return -ENOENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static size_t copy_error_info_to_user(struct fanotify_event *event,
|
||||||
|
char __user *buf, int count)
|
||||||
|
{
|
||||||
|
struct fanotify_event_info_error info;
|
||||||
|
struct fanotify_error_event *fee = FANOTIFY_EE(event);
|
||||||
|
|
||||||
|
info.hdr.info_type = FAN_EVENT_INFO_TYPE_ERROR;
|
||||||
|
info.hdr.pad = 0;
|
||||||
|
info.hdr.len = FANOTIFY_ERROR_INFO_LEN;
|
||||||
|
|
||||||
|
if (WARN_ON(count < info.hdr.len))
|
||||||
|
return -EFAULT;
|
||||||
|
|
||||||
|
info.error = fee->error;
|
||||||
|
info.error_count = fee->err_count;
|
||||||
|
|
||||||
|
if (copy_to_user(buf, &info, sizeof(info)))
|
||||||
|
return -EFAULT;
|
||||||
|
|
||||||
|
return info.hdr.len;
|
||||||
|
}
|
||||||
|
|
||||||
static int copy_fid_info_to_user(__kernel_fsid_t *fsid, struct fanotify_fh *fh,
|
static int copy_fid_info_to_user(__kernel_fsid_t *fsid, struct fanotify_fh *fh,
|
||||||
int info_type, const char *name,
|
int info_type, const char *name,
|
||||||
size_t name_len,
|
size_t name_len,
|
||||||
@ -331,9 +366,6 @@ static int copy_fid_info_to_user(__kernel_fsid_t *fsid, struct fanotify_fh *fh,
|
|||||||
pr_debug("%s: fh_len=%zu name_len=%zu, info_len=%zu, count=%zu\n",
|
pr_debug("%s: fh_len=%zu name_len=%zu, info_len=%zu, count=%zu\n",
|
||||||
__func__, fh_len, name_len, info_len, count);
|
__func__, fh_len, name_len, info_len, count);
|
||||||
|
|
||||||
if (!fh_len)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if (WARN_ON_ONCE(len < sizeof(info) || len > count))
|
if (WARN_ON_ONCE(len < sizeof(info) || len > count))
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
|
|
||||||
@ -368,6 +400,11 @@ static int copy_fid_info_to_user(__kernel_fsid_t *fsid, struct fanotify_fh *fh,
|
|||||||
|
|
||||||
handle.handle_type = fh->type;
|
handle.handle_type = fh->type;
|
||||||
handle.handle_bytes = fh_len;
|
handle.handle_bytes = fh_len;
|
||||||
|
|
||||||
|
/* Mangle handle_type for bad file_handle */
|
||||||
|
if (!fh_len)
|
||||||
|
handle.handle_type = FILEID_INVALID;
|
||||||
|
|
||||||
if (copy_to_user(buf, &handle, sizeof(handle)))
|
if (copy_to_user(buf, &handle, sizeof(handle)))
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
|
|
||||||
@ -444,7 +481,7 @@ static int copy_info_records_to_user(struct fanotify_event *event,
|
|||||||
/*
|
/*
|
||||||
* Event info records order is as follows: dir fid + name, child fid.
|
* Event info records order is as follows: dir fid + name, child fid.
|
||||||
*/
|
*/
|
||||||
if (fanotify_event_dir_fh_len(event)) {
|
if (fanotify_event_has_dir_fh(event)) {
|
||||||
info_type = info->name_len ? FAN_EVENT_INFO_TYPE_DFID_NAME :
|
info_type = info->name_len ? FAN_EVENT_INFO_TYPE_DFID_NAME :
|
||||||
FAN_EVENT_INFO_TYPE_DFID;
|
FAN_EVENT_INFO_TYPE_DFID;
|
||||||
ret = copy_fid_info_to_user(fanotify_event_fsid(event),
|
ret = copy_fid_info_to_user(fanotify_event_fsid(event),
|
||||||
@ -460,7 +497,7 @@ static int copy_info_records_to_user(struct fanotify_event *event,
|
|||||||
total_bytes += ret;
|
total_bytes += ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fanotify_event_object_fh_len(event)) {
|
if (fanotify_event_has_object_fh(event)) {
|
||||||
const char *dot = NULL;
|
const char *dot = NULL;
|
||||||
int dot_len = 0;
|
int dot_len = 0;
|
||||||
|
|
||||||
@ -520,6 +557,15 @@ static int copy_info_records_to_user(struct fanotify_event *event,
|
|||||||
total_bytes += ret;
|
total_bytes += ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (fanotify_is_error_event(event->mask)) {
|
||||||
|
ret = copy_error_info_to_user(event, buf, count);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
buf += ret;
|
||||||
|
count -= ret;
|
||||||
|
total_bytes += ret;
|
||||||
|
}
|
||||||
|
|
||||||
return total_bytes;
|
return total_bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -537,8 +583,7 @@ static ssize_t copy_event_to_user(struct fsnotify_group *group,
|
|||||||
|
|
||||||
pr_debug("%s: group=%p event=%p\n", __func__, group, event);
|
pr_debug("%s: group=%p event=%p\n", __func__, group, event);
|
||||||
|
|
||||||
metadata.event_len = FAN_EVENT_METADATA_LEN +
|
metadata.event_len = fanotify_event_len(info_mode, event);
|
||||||
fanotify_event_info_len(info_mode, event);
|
|
||||||
metadata.metadata_len = FAN_EVENT_METADATA_LEN;
|
metadata.metadata_len = FAN_EVENT_METADATA_LEN;
|
||||||
metadata.vers = FANOTIFY_METADATA_VERSION;
|
metadata.vers = FANOTIFY_METADATA_VERSION;
|
||||||
metadata.reserved = 0;
|
metadata.reserved = 0;
|
||||||
@ -1049,6 +1094,15 @@ out_dec_ucounts:
|
|||||||
return ERR_PTR(ret);
|
return ERR_PTR(ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int fanotify_group_init_error_pool(struct fsnotify_group *group)
|
||||||
|
{
|
||||||
|
if (mempool_initialized(&group->fanotify_data.error_events_pool))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return mempool_init_kmalloc_pool(&group->fanotify_data.error_events_pool,
|
||||||
|
FANOTIFY_DEFAULT_FEE_POOL_SIZE,
|
||||||
|
sizeof(struct fanotify_error_event));
|
||||||
|
}
|
||||||
|
|
||||||
static int fanotify_add_mark(struct fsnotify_group *group,
|
static int fanotify_add_mark(struct fsnotify_group *group,
|
||||||
fsnotify_connp_t *connp, unsigned int type,
|
fsnotify_connp_t *connp, unsigned int type,
|
||||||
@ -1057,6 +1111,7 @@ static int fanotify_add_mark(struct fsnotify_group *group,
|
|||||||
{
|
{
|
||||||
struct fsnotify_mark *fsn_mark;
|
struct fsnotify_mark *fsn_mark;
|
||||||
__u32 added;
|
__u32 added;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
mutex_lock(&group->mark_mutex);
|
mutex_lock(&group->mark_mutex);
|
||||||
fsn_mark = fsnotify_find_mark(connp, group);
|
fsn_mark = fsnotify_find_mark(connp, group);
|
||||||
@ -1067,13 +1122,26 @@ static int fanotify_add_mark(struct fsnotify_group *group,
|
|||||||
return PTR_ERR(fsn_mark);
|
return PTR_ERR(fsn_mark);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Error events are pre-allocated per group, only if strictly
|
||||||
|
* needed (i.e. FAN_FS_ERROR was requested).
|
||||||
|
*/
|
||||||
|
if (!(flags & FAN_MARK_IGNORED_MASK) && (mask & FAN_FS_ERROR)) {
|
||||||
|
ret = fanotify_group_init_error_pool(group);
|
||||||
|
if (ret)
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
added = fanotify_mark_add_to_mask(fsn_mark, mask, flags);
|
added = fanotify_mark_add_to_mask(fsn_mark, mask, flags);
|
||||||
if (added & ~fsnotify_conn_mask(fsn_mark->connector))
|
if (added & ~fsnotify_conn_mask(fsn_mark->connector))
|
||||||
fsnotify_recalc_mask(fsn_mark->connector);
|
fsnotify_recalc_mask(fsn_mark->connector);
|
||||||
|
|
||||||
|
out:
|
||||||
mutex_unlock(&group->mark_mutex);
|
mutex_unlock(&group->mark_mutex);
|
||||||
|
|
||||||
fsnotify_put_mark(fsn_mark);
|
fsnotify_put_mark(fsn_mark);
|
||||||
return 0;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int fanotify_add_vfsmount_mark(struct fsnotify_group *group,
|
static int fanotify_add_vfsmount_mark(struct fsnotify_group *group,
|
||||||
@ -1295,16 +1363,15 @@ out_destroy_group:
|
|||||||
return fd;
|
return fd;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check if filesystem can encode a unique fid */
|
static int fanotify_test_fsid(struct dentry *dentry, __kernel_fsid_t *fsid)
|
||||||
static int fanotify_test_fid(struct path *path, __kernel_fsid_t *fsid)
|
|
||||||
{
|
{
|
||||||
__kernel_fsid_t root_fsid;
|
__kernel_fsid_t root_fsid;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Make sure path is not in filesystem with zero fsid (e.g. tmpfs).
|
* Make sure dentry is not of a filesystem with zero fsid (e.g. fuse).
|
||||||
*/
|
*/
|
||||||
err = vfs_get_fsid(path->dentry, fsid);
|
err = vfs_get_fsid(dentry, fsid);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
@ -1312,10 +1379,10 @@ static int fanotify_test_fid(struct path *path, __kernel_fsid_t *fsid)
|
|||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Make sure path is not inside a filesystem subvolume (e.g. btrfs)
|
* Make sure dentry is not of a filesystem subvolume (e.g. btrfs)
|
||||||
* which uses a different fsid than sb root.
|
* which uses a different fsid than sb root.
|
||||||
*/
|
*/
|
||||||
err = vfs_get_fsid(path->dentry->d_sb->s_root, &root_fsid);
|
err = vfs_get_fsid(dentry->d_sb->s_root, &root_fsid);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
@ -1323,6 +1390,12 @@ static int fanotify_test_fid(struct path *path, __kernel_fsid_t *fsid)
|
|||||||
root_fsid.val[1] != fsid->val[1])
|
root_fsid.val[1] != fsid->val[1])
|
||||||
return -EXDEV;
|
return -EXDEV;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if filesystem can encode a unique fid */
|
||||||
|
static int fanotify_test_fid(struct dentry *dentry)
|
||||||
|
{
|
||||||
/*
|
/*
|
||||||
* We need to make sure that the file system supports at least
|
* We need to make sure that the file system supports at least
|
||||||
* encoding a file handle so user can use name_to_handle_at() to
|
* encoding a file handle so user can use name_to_handle_at() to
|
||||||
@ -1330,8 +1403,8 @@ static int fanotify_test_fid(struct path *path, __kernel_fsid_t *fsid)
|
|||||||
* objects. However, name_to_handle_at() requires that the
|
* objects. However, name_to_handle_at() requires that the
|
||||||
* filesystem also supports decoding file handles.
|
* filesystem also supports decoding file handles.
|
||||||
*/
|
*/
|
||||||
if (!path->dentry->d_sb->s_export_op ||
|
if (!dentry->d_sb->s_export_op ||
|
||||||
!path->dentry->d_sb->s_export_op->fh_to_dentry)
|
!dentry->d_sb->s_export_op->fh_to_dentry)
|
||||||
return -EOPNOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@ -1447,15 +1520,19 @@ static int do_fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask,
|
|||||||
group->priority == FS_PRIO_0)
|
group->priority == FS_PRIO_0)
|
||||||
goto fput_and_out;
|
goto fput_and_out;
|
||||||
|
|
||||||
|
if (mask & FAN_FS_ERROR &&
|
||||||
|
mark_type != FAN_MARK_FILESYSTEM)
|
||||||
|
goto fput_and_out;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Events with data type inode do not carry enough information to report
|
* Events that do not carry enough information to report
|
||||||
* event->fd, so we do not allow setting a mask for inode events unless
|
* event->fd require a group that supports reporting fid. Those
|
||||||
* group supports reporting fid.
|
* events are not supported on a mount mark, because they do not
|
||||||
* inode events are not supported on a mount mark, because they do not
|
* carry enough information (i.e. path) to be filtered by mount
|
||||||
* carry enough information (i.e. path) to be filtered by mount point.
|
* point.
|
||||||
*/
|
*/
|
||||||
fid_mode = FAN_GROUP_FLAG(group, FANOTIFY_FID_BITS);
|
fid_mode = FAN_GROUP_FLAG(group, FANOTIFY_FID_BITS);
|
||||||
if (mask & FANOTIFY_INODE_EVENTS &&
|
if (mask & ~(FANOTIFY_FD_EVENTS|FANOTIFY_EVENT_FLAGS) &&
|
||||||
(!fid_mode || mark_type == FAN_MARK_MOUNT))
|
(!fid_mode || mark_type == FAN_MARK_MOUNT))
|
||||||
goto fput_and_out;
|
goto fput_and_out;
|
||||||
|
|
||||||
@ -1482,7 +1559,11 @@ static int do_fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (fid_mode) {
|
if (fid_mode) {
|
||||||
ret = fanotify_test_fid(&path, &__fsid);
|
ret = fanotify_test_fsid(path.dentry, &__fsid);
|
||||||
|
if (ret)
|
||||||
|
goto path_put_and_out;
|
||||||
|
|
||||||
|
ret = fanotify_test_fid(path.dentry);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto path_put_and_out;
|
goto path_put_and_out;
|
||||||
|
|
||||||
|
@ -252,6 +252,9 @@ static int fsnotify_handle_inode_event(struct fsnotify_group *group,
|
|||||||
if (WARN_ON_ONCE(!ops->handle_inode_event))
|
if (WARN_ON_ONCE(!ops->handle_inode_event))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
if (WARN_ON_ONCE(!inode && !dir))
|
||||||
|
return 0;
|
||||||
|
|
||||||
if ((inode_mark->mask & FS_EXCL_UNLINK) &&
|
if ((inode_mark->mask & FS_EXCL_UNLINK) &&
|
||||||
path && d_unlinked(path->dentry))
|
path && d_unlinked(path->dentry))
|
||||||
return 0;
|
return 0;
|
||||||
@ -455,16 +458,16 @@ static void fsnotify_iter_next(struct fsnotify_iter_info *iter_info)
|
|||||||
* @file_name is relative to
|
* @file_name is relative to
|
||||||
* @file_name: optional file name associated with event
|
* @file_name: optional file name associated with event
|
||||||
* @inode: optional inode associated with event -
|
* @inode: optional inode associated with event -
|
||||||
* either @dir or @inode must be non-NULL.
|
* If @dir and @inode are both non-NULL, event may be
|
||||||
* if both are non-NULL event may be reported to both.
|
* reported to both.
|
||||||
* @cookie: inotify rename cookie
|
* @cookie: inotify rename cookie
|
||||||
*/
|
*/
|
||||||
int fsnotify(__u32 mask, const void *data, int data_type, struct inode *dir,
|
int fsnotify(__u32 mask, const void *data, int data_type, struct inode *dir,
|
||||||
const struct qstr *file_name, struct inode *inode, u32 cookie)
|
const struct qstr *file_name, struct inode *inode, u32 cookie)
|
||||||
{
|
{
|
||||||
const struct path *path = fsnotify_data_path(data, data_type);
|
const struct path *path = fsnotify_data_path(data, data_type);
|
||||||
|
struct super_block *sb = fsnotify_data_sb(data, data_type);
|
||||||
struct fsnotify_iter_info iter_info = {};
|
struct fsnotify_iter_info iter_info = {};
|
||||||
struct super_block *sb;
|
|
||||||
struct mount *mnt = NULL;
|
struct mount *mnt = NULL;
|
||||||
struct inode *parent = NULL;
|
struct inode *parent = NULL;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
@ -483,7 +486,6 @@ int fsnotify(__u32 mask, const void *data, int data_type, struct inode *dir,
|
|||||||
*/
|
*/
|
||||||
parent = dir;
|
parent = dir;
|
||||||
}
|
}
|
||||||
sb = inode->i_sb;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Optimization: srcu_read_lock() has a memory barrier which can
|
* Optimization: srcu_read_lock() has a memory barrier which can
|
||||||
|
@ -88,7 +88,7 @@ void fsnotify_destroy_group(struct fsnotify_group *group)
|
|||||||
* that deliberately ignores overflow events.
|
* that deliberately ignores overflow events.
|
||||||
*/
|
*/
|
||||||
if (group->overflow_event)
|
if (group->overflow_event)
|
||||||
group->ops->free_event(group->overflow_event);
|
group->ops->free_event(group, group->overflow_event);
|
||||||
|
|
||||||
fsnotify_put_group(group);
|
fsnotify_put_group(group);
|
||||||
}
|
}
|
||||||
|
@ -116,7 +116,7 @@ int inotify_handle_inode_event(struct fsnotify_mark *inode_mark, u32 mask,
|
|||||||
if (len)
|
if (len)
|
||||||
strcpy(event->name, name->name);
|
strcpy(event->name, name->name);
|
||||||
|
|
||||||
ret = fsnotify_add_event(group, fsn_event, inotify_merge, NULL);
|
ret = fsnotify_add_event(group, fsn_event, inotify_merge);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
/* Our event wasn't used in the end. Free it. */
|
/* Our event wasn't used in the end. Free it. */
|
||||||
fsnotify_destroy_event(group, fsn_event);
|
fsnotify_destroy_event(group, fsn_event);
|
||||||
@ -177,7 +177,8 @@ static void inotify_free_group_priv(struct fsnotify_group *group)
|
|||||||
dec_inotify_instances(group->inotify_data.ucounts);
|
dec_inotify_instances(group->inotify_data.ucounts);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void inotify_free_event(struct fsnotify_event *fsn_event)
|
static void inotify_free_event(struct fsnotify_group *group,
|
||||||
|
struct fsnotify_event *fsn_event)
|
||||||
{
|
{
|
||||||
kfree(INOTIFY_E(fsn_event));
|
kfree(INOTIFY_E(fsn_event));
|
||||||
}
|
}
|
||||||
|
@ -94,10 +94,10 @@ static inline __u32 inotify_arg_to_mask(struct inode *inode, u32 arg)
|
|||||||
__u32 mask;
|
__u32 mask;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Everything should accept their own ignored and should receive events
|
* Everything should receive events when the inode is unmounted.
|
||||||
* when the inode is unmounted. All directories care about children.
|
* All directories care about children.
|
||||||
*/
|
*/
|
||||||
mask = (FS_IN_IGNORED | FS_UNMOUNT);
|
mask = (FS_UNMOUNT);
|
||||||
if (S_ISDIR(inode->i_mode))
|
if (S_ISDIR(inode->i_mode))
|
||||||
mask |= FS_EVENT_ON_CHILD;
|
mask |= FS_EVENT_ON_CHILD;
|
||||||
|
|
||||||
|
@ -64,7 +64,7 @@ void fsnotify_destroy_event(struct fsnotify_group *group,
|
|||||||
WARN_ON(!list_empty(&event->list));
|
WARN_ON(!list_empty(&event->list));
|
||||||
spin_unlock(&group->notification_lock);
|
spin_unlock(&group->notification_lock);
|
||||||
}
|
}
|
||||||
group->ops->free_event(event);
|
group->ops->free_event(group, event);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -78,12 +78,12 @@ void fsnotify_destroy_event(struct fsnotify_group *group,
|
|||||||
* 2 if the event was not queued - either the queue of events has overflown
|
* 2 if the event was not queued - either the queue of events has overflown
|
||||||
* or the group is shutting down.
|
* or the group is shutting down.
|
||||||
*/
|
*/
|
||||||
int fsnotify_add_event(struct fsnotify_group *group,
|
int fsnotify_insert_event(struct fsnotify_group *group,
|
||||||
struct fsnotify_event *event,
|
struct fsnotify_event *event,
|
||||||
int (*merge)(struct fsnotify_group *,
|
int (*merge)(struct fsnotify_group *,
|
||||||
struct fsnotify_event *),
|
struct fsnotify_event *),
|
||||||
void (*insert)(struct fsnotify_group *,
|
void (*insert)(struct fsnotify_group *,
|
||||||
struct fsnotify_event *))
|
struct fsnotify_event *))
|
||||||
{
|
{
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
struct list_head *list = &group->notification_list;
|
struct list_head *list = &group->notification_list;
|
||||||
|
@ -84,13 +84,20 @@ extern struct ctl_table fanotify_table[]; /* for sysctl */
|
|||||||
*/
|
*/
|
||||||
#define FANOTIFY_DIRENT_EVENTS (FAN_MOVE | FAN_CREATE | FAN_DELETE)
|
#define FANOTIFY_DIRENT_EVENTS (FAN_MOVE | FAN_CREATE | FAN_DELETE)
|
||||||
|
|
||||||
|
/* Events that can be reported with event->fd */
|
||||||
|
#define FANOTIFY_FD_EVENTS (FANOTIFY_PATH_EVENTS | FANOTIFY_PERM_EVENTS)
|
||||||
|
|
||||||
/* Events that can only be reported with data type FSNOTIFY_EVENT_INODE */
|
/* Events that can only be reported with data type FSNOTIFY_EVENT_INODE */
|
||||||
#define FANOTIFY_INODE_EVENTS (FANOTIFY_DIRENT_EVENTS | \
|
#define FANOTIFY_INODE_EVENTS (FANOTIFY_DIRENT_EVENTS | \
|
||||||
FAN_ATTRIB | FAN_MOVE_SELF | FAN_DELETE_SELF)
|
FAN_ATTRIB | FAN_MOVE_SELF | FAN_DELETE_SELF)
|
||||||
|
|
||||||
|
/* Events that can only be reported with data type FSNOTIFY_EVENT_ERROR */
|
||||||
|
#define FANOTIFY_ERROR_EVENTS (FAN_FS_ERROR)
|
||||||
|
|
||||||
/* Events that user can request to be notified on */
|
/* Events that user can request to be notified on */
|
||||||
#define FANOTIFY_EVENTS (FANOTIFY_PATH_EVENTS | \
|
#define FANOTIFY_EVENTS (FANOTIFY_PATH_EVENTS | \
|
||||||
FANOTIFY_INODE_EVENTS)
|
FANOTIFY_INODE_EVENTS | \
|
||||||
|
FANOTIFY_ERROR_EVENTS)
|
||||||
|
|
||||||
/* Events that require a permission response from user */
|
/* Events that require a permission response from user */
|
||||||
#define FANOTIFY_PERM_EVENTS (FAN_OPEN_PERM | FAN_ACCESS_PERM | \
|
#define FANOTIFY_PERM_EVENTS (FAN_OPEN_PERM | FAN_ACCESS_PERM | \
|
||||||
|
@ -26,20 +26,20 @@
|
|||||||
* FS_EVENT_ON_CHILD mask on the parent inode and will not be reported if only
|
* FS_EVENT_ON_CHILD mask on the parent inode and will not be reported if only
|
||||||
* the child is interested and not the parent.
|
* the child is interested and not the parent.
|
||||||
*/
|
*/
|
||||||
static inline void fsnotify_name(struct inode *dir, __u32 mask,
|
static inline int fsnotify_name(__u32 mask, const void *data, int data_type,
|
||||||
struct inode *child,
|
struct inode *dir, const struct qstr *name,
|
||||||
const struct qstr *name, u32 cookie)
|
u32 cookie)
|
||||||
{
|
{
|
||||||
if (atomic_long_read(&dir->i_sb->s_fsnotify_connectors) == 0)
|
if (atomic_long_read(&dir->i_sb->s_fsnotify_connectors) == 0)
|
||||||
return;
|
return 0;
|
||||||
|
|
||||||
fsnotify(mask, child, FSNOTIFY_EVENT_INODE, dir, name, NULL, cookie);
|
return fsnotify(mask, data, data_type, dir, name, NULL, cookie);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void fsnotify_dirent(struct inode *dir, struct dentry *dentry,
|
static inline void fsnotify_dirent(struct inode *dir, struct dentry *dentry,
|
||||||
__u32 mask)
|
__u32 mask)
|
||||||
{
|
{
|
||||||
fsnotify_name(dir, mask, d_inode(dentry), &dentry->d_name, 0);
|
fsnotify_name(mask, dentry, FSNOTIFY_EVENT_DENTRY, dir, &dentry->d_name, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void fsnotify_inode(struct inode *inode, __u32 mask)
|
static inline void fsnotify_inode(struct inode *inode, __u32 mask)
|
||||||
@ -86,7 +86,7 @@ notify_child:
|
|||||||
*/
|
*/
|
||||||
static inline void fsnotify_dentry(struct dentry *dentry, __u32 mask)
|
static inline void fsnotify_dentry(struct dentry *dentry, __u32 mask)
|
||||||
{
|
{
|
||||||
fsnotify_parent(dentry, mask, d_inode(dentry), FSNOTIFY_EVENT_INODE);
|
fsnotify_parent(dentry, mask, dentry, FSNOTIFY_EVENT_DENTRY);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int fsnotify_file(struct file *file, __u32 mask)
|
static inline int fsnotify_file(struct file *file, __u32 mask)
|
||||||
@ -154,8 +154,10 @@ static inline void fsnotify_move(struct inode *old_dir, struct inode *new_dir,
|
|||||||
new_dir_mask |= FS_ISDIR;
|
new_dir_mask |= FS_ISDIR;
|
||||||
}
|
}
|
||||||
|
|
||||||
fsnotify_name(old_dir, old_dir_mask, source, old_name, fs_cookie);
|
fsnotify_name(old_dir_mask, source, FSNOTIFY_EVENT_INODE,
|
||||||
fsnotify_name(new_dir, new_dir_mask, source, new_name, fs_cookie);
|
old_dir, old_name, fs_cookie);
|
||||||
|
fsnotify_name(new_dir_mask, source, FSNOTIFY_EVENT_INODE,
|
||||||
|
new_dir, new_name, fs_cookie);
|
||||||
|
|
||||||
if (target)
|
if (target)
|
||||||
fsnotify_link_count(target);
|
fsnotify_link_count(target);
|
||||||
@ -190,16 +192,22 @@ static inline void fsnotify_inoderemove(struct inode *inode)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* fsnotify_create - 'name' was linked in
|
* fsnotify_create - 'name' was linked in
|
||||||
|
*
|
||||||
|
* Caller must make sure that dentry->d_name is stable.
|
||||||
|
* Note: some filesystems (e.g. kernfs) leave @dentry negative and instantiate
|
||||||
|
* ->d_inode later
|
||||||
*/
|
*/
|
||||||
static inline void fsnotify_create(struct inode *inode, struct dentry *dentry)
|
static inline void fsnotify_create(struct inode *dir, struct dentry *dentry)
|
||||||
{
|
{
|
||||||
audit_inode_child(inode, dentry, AUDIT_TYPE_CHILD_CREATE);
|
audit_inode_child(dir, dentry, AUDIT_TYPE_CHILD_CREATE);
|
||||||
|
|
||||||
fsnotify_dirent(inode, dentry, FS_CREATE);
|
fsnotify_dirent(dir, dentry, FS_CREATE);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* fsnotify_link - new hardlink in 'inode' directory
|
* fsnotify_link - new hardlink in 'inode' directory
|
||||||
|
*
|
||||||
|
* Caller must make sure that new_dentry->d_name is stable.
|
||||||
* Note: We have to pass also the linked inode ptr as some filesystems leave
|
* Note: We have to pass also the linked inode ptr as some filesystems leave
|
||||||
* new_dentry->d_inode NULL and instantiate inode pointer later
|
* new_dentry->d_inode NULL and instantiate inode pointer later
|
||||||
*/
|
*/
|
||||||
@ -209,7 +217,8 @@ static inline void fsnotify_link(struct inode *dir, struct inode *inode,
|
|||||||
fsnotify_link_count(inode);
|
fsnotify_link_count(inode);
|
||||||
audit_inode_child(dir, new_dentry, AUDIT_TYPE_CHILD_CREATE);
|
audit_inode_child(dir, new_dentry, AUDIT_TYPE_CHILD_CREATE);
|
||||||
|
|
||||||
fsnotify_name(dir, FS_CREATE, inode, &new_dentry->d_name, 0);
|
fsnotify_name(FS_CREATE, inode, FSNOTIFY_EVENT_INODE,
|
||||||
|
dir, &new_dentry->d_name, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -227,12 +236,16 @@ static inline void fsnotify_unlink(struct inode *dir, struct dentry *dentry)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* fsnotify_mkdir - directory 'name' was created
|
* fsnotify_mkdir - directory 'name' was created
|
||||||
|
*
|
||||||
|
* Caller must make sure that dentry->d_name is stable.
|
||||||
|
* Note: some filesystems (e.g. kernfs) leave @dentry negative and instantiate
|
||||||
|
* ->d_inode later
|
||||||
*/
|
*/
|
||||||
static inline void fsnotify_mkdir(struct inode *inode, struct dentry *dentry)
|
static inline void fsnotify_mkdir(struct inode *dir, struct dentry *dentry)
|
||||||
{
|
{
|
||||||
audit_inode_child(inode, dentry, AUDIT_TYPE_CHILD_CREATE);
|
audit_inode_child(dir, dentry, AUDIT_TYPE_CHILD_CREATE);
|
||||||
|
|
||||||
fsnotify_dirent(inode, dentry, FS_CREATE | FS_ISDIR);
|
fsnotify_dirent(dir, dentry, FS_CREATE | FS_ISDIR);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -326,4 +339,17 @@ static inline void fsnotify_change(struct dentry *dentry, unsigned int ia_valid)
|
|||||||
fsnotify_dentry(dentry, mask);
|
fsnotify_dentry(dentry, mask);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline int fsnotify_sb_error(struct super_block *sb, struct inode *inode,
|
||||||
|
int error)
|
||||||
|
{
|
||||||
|
struct fs_error_report report = {
|
||||||
|
.error = error,
|
||||||
|
.inode = inode,
|
||||||
|
.sb = sb,
|
||||||
|
};
|
||||||
|
|
||||||
|
return fsnotify(FS_ERROR, &report, FSNOTIFY_EVENT_ERROR,
|
||||||
|
NULL, NULL, NULL, 0);
|
||||||
|
}
|
||||||
|
|
||||||
#endif /* _LINUX_FS_NOTIFY_H */
|
#endif /* _LINUX_FS_NOTIFY_H */
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
#include <linux/atomic.h>
|
#include <linux/atomic.h>
|
||||||
#include <linux/user_namespace.h>
|
#include <linux/user_namespace.h>
|
||||||
#include <linux/refcount.h>
|
#include <linux/refcount.h>
|
||||||
|
#include <linux/mempool.h>
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* IN_* from inotfy.h lines up EXACTLY with FS_*, this is so we can easily
|
* IN_* from inotfy.h lines up EXACTLY with FS_*, this is so we can easily
|
||||||
@ -42,6 +43,12 @@
|
|||||||
|
|
||||||
#define FS_UNMOUNT 0x00002000 /* inode on umount fs */
|
#define FS_UNMOUNT 0x00002000 /* inode on umount fs */
|
||||||
#define FS_Q_OVERFLOW 0x00004000 /* Event queued overflowed */
|
#define FS_Q_OVERFLOW 0x00004000 /* Event queued overflowed */
|
||||||
|
#define FS_ERROR 0x00008000 /* Filesystem Error (fanotify) */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* FS_IN_IGNORED overloads FS_ERROR. It is only used internally by inotify
|
||||||
|
* which does not support FS_ERROR.
|
||||||
|
*/
|
||||||
#define FS_IN_IGNORED 0x00008000 /* last inotify event here */
|
#define FS_IN_IGNORED 0x00008000 /* last inotify event here */
|
||||||
|
|
||||||
#define FS_OPEN_PERM 0x00010000 /* open event in an permission hook */
|
#define FS_OPEN_PERM 0x00010000 /* open event in an permission hook */
|
||||||
@ -95,7 +102,8 @@
|
|||||||
#define ALL_FSNOTIFY_EVENTS (ALL_FSNOTIFY_DIRENT_EVENTS | \
|
#define ALL_FSNOTIFY_EVENTS (ALL_FSNOTIFY_DIRENT_EVENTS | \
|
||||||
FS_EVENTS_POSS_ON_CHILD | \
|
FS_EVENTS_POSS_ON_CHILD | \
|
||||||
FS_DELETE_SELF | FS_MOVE_SELF | FS_DN_RENAME | \
|
FS_DELETE_SELF | FS_MOVE_SELF | FS_DN_RENAME | \
|
||||||
FS_UNMOUNT | FS_Q_OVERFLOW | FS_IN_IGNORED)
|
FS_UNMOUNT | FS_Q_OVERFLOW | FS_IN_IGNORED | \
|
||||||
|
FS_ERROR)
|
||||||
|
|
||||||
/* Extra flags that may be reported with event or control handling of events */
|
/* Extra flags that may be reported with event or control handling of events */
|
||||||
#define ALL_FSNOTIFY_FLAGS (FS_EXCL_UNLINK | FS_ISDIR | FS_IN_ONESHOT | \
|
#define ALL_FSNOTIFY_FLAGS (FS_EXCL_UNLINK | FS_ISDIR | FS_IN_ONESHOT | \
|
||||||
@ -136,6 +144,7 @@ struct mem_cgroup;
|
|||||||
* @dir: optional directory associated with event -
|
* @dir: optional directory associated with event -
|
||||||
* if @file_name is not NULL, this is the directory that
|
* if @file_name is not NULL, this is the directory that
|
||||||
* @file_name is relative to.
|
* @file_name is relative to.
|
||||||
|
* Either @inode or @dir must be non-NULL.
|
||||||
* @file_name: optional file name associated with event
|
* @file_name: optional file name associated with event
|
||||||
* @cookie: inotify rename cookie
|
* @cookie: inotify rename cookie
|
||||||
*
|
*
|
||||||
@ -155,7 +164,7 @@ struct fsnotify_ops {
|
|||||||
const struct qstr *file_name, u32 cookie);
|
const struct qstr *file_name, u32 cookie);
|
||||||
void (*free_group_priv)(struct fsnotify_group *group);
|
void (*free_group_priv)(struct fsnotify_group *group);
|
||||||
void (*freeing_mark)(struct fsnotify_mark *mark, struct fsnotify_group *group);
|
void (*freeing_mark)(struct fsnotify_mark *mark, struct fsnotify_group *group);
|
||||||
void (*free_event)(struct fsnotify_event *event);
|
void (*free_event)(struct fsnotify_group *group, struct fsnotify_event *event);
|
||||||
/* called on final put+free to free memory */
|
/* called on final put+free to free memory */
|
||||||
void (*free_mark)(struct fsnotify_mark *mark);
|
void (*free_mark)(struct fsnotify_mark *mark);
|
||||||
};
|
};
|
||||||
@ -238,6 +247,7 @@ struct fsnotify_group {
|
|||||||
int flags; /* flags from fanotify_init() */
|
int flags; /* flags from fanotify_init() */
|
||||||
int f_flags; /* event_f_flags from fanotify_init() */
|
int f_flags; /* event_f_flags from fanotify_init() */
|
||||||
struct ucounts *ucounts;
|
struct ucounts *ucounts;
|
||||||
|
mempool_t error_events_pool;
|
||||||
} fanotify_data;
|
} fanotify_data;
|
||||||
#endif /* CONFIG_FANOTIFY */
|
#endif /* CONFIG_FANOTIFY */
|
||||||
};
|
};
|
||||||
@ -248,6 +258,14 @@ enum fsnotify_data_type {
|
|||||||
FSNOTIFY_EVENT_NONE,
|
FSNOTIFY_EVENT_NONE,
|
||||||
FSNOTIFY_EVENT_PATH,
|
FSNOTIFY_EVENT_PATH,
|
||||||
FSNOTIFY_EVENT_INODE,
|
FSNOTIFY_EVENT_INODE,
|
||||||
|
FSNOTIFY_EVENT_DENTRY,
|
||||||
|
FSNOTIFY_EVENT_ERROR,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct fs_error_report {
|
||||||
|
int error;
|
||||||
|
struct inode *inode;
|
||||||
|
struct super_block *sb;
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline struct inode *fsnotify_data_inode(const void *data, int data_type)
|
static inline struct inode *fsnotify_data_inode(const void *data, int data_type)
|
||||||
@ -255,8 +273,25 @@ static inline struct inode *fsnotify_data_inode(const void *data, int data_type)
|
|||||||
switch (data_type) {
|
switch (data_type) {
|
||||||
case FSNOTIFY_EVENT_INODE:
|
case FSNOTIFY_EVENT_INODE:
|
||||||
return (struct inode *)data;
|
return (struct inode *)data;
|
||||||
|
case FSNOTIFY_EVENT_DENTRY:
|
||||||
|
return d_inode(data);
|
||||||
case FSNOTIFY_EVENT_PATH:
|
case FSNOTIFY_EVENT_PATH:
|
||||||
return d_inode(((const struct path *)data)->dentry);
|
return d_inode(((const struct path *)data)->dentry);
|
||||||
|
case FSNOTIFY_EVENT_ERROR:
|
||||||
|
return ((struct fs_error_report *)data)->inode;
|
||||||
|
default:
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline struct dentry *fsnotify_data_dentry(const void *data, int data_type)
|
||||||
|
{
|
||||||
|
switch (data_type) {
|
||||||
|
case FSNOTIFY_EVENT_DENTRY:
|
||||||
|
/* Non const is needed for dget() */
|
||||||
|
return (struct dentry *)data;
|
||||||
|
case FSNOTIFY_EVENT_PATH:
|
||||||
|
return ((const struct path *)data)->dentry;
|
||||||
default:
|
default:
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
@ -273,6 +308,35 @@ static inline const struct path *fsnotify_data_path(const void *data,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline struct super_block *fsnotify_data_sb(const void *data,
|
||||||
|
int data_type)
|
||||||
|
{
|
||||||
|
switch (data_type) {
|
||||||
|
case FSNOTIFY_EVENT_INODE:
|
||||||
|
return ((struct inode *)data)->i_sb;
|
||||||
|
case FSNOTIFY_EVENT_DENTRY:
|
||||||
|
return ((struct dentry *)data)->d_sb;
|
||||||
|
case FSNOTIFY_EVENT_PATH:
|
||||||
|
return ((const struct path *)data)->dentry->d_sb;
|
||||||
|
case FSNOTIFY_EVENT_ERROR:
|
||||||
|
return ((struct fs_error_report *) data)->sb;
|
||||||
|
default:
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline struct fs_error_report *fsnotify_data_error_report(
|
||||||
|
const void *data,
|
||||||
|
int data_type)
|
||||||
|
{
|
||||||
|
switch (data_type) {
|
||||||
|
case FSNOTIFY_EVENT_ERROR:
|
||||||
|
return (struct fs_error_report *) data;
|
||||||
|
default:
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
enum fsnotify_obj_type {
|
enum fsnotify_obj_type {
|
||||||
FSNOTIFY_OBJ_TYPE_INODE,
|
FSNOTIFY_OBJ_TYPE_INODE,
|
||||||
FSNOTIFY_OBJ_TYPE_PARENT,
|
FSNOTIFY_OBJ_TYPE_PARENT,
|
||||||
@ -482,16 +546,30 @@ extern int fsnotify_fasync(int fd, struct file *file, int on);
|
|||||||
extern void fsnotify_destroy_event(struct fsnotify_group *group,
|
extern void fsnotify_destroy_event(struct fsnotify_group *group,
|
||||||
struct fsnotify_event *event);
|
struct fsnotify_event *event);
|
||||||
/* attach the event to the group notification queue */
|
/* attach the event to the group notification queue */
|
||||||
extern int fsnotify_add_event(struct fsnotify_group *group,
|
extern int fsnotify_insert_event(struct fsnotify_group *group,
|
||||||
struct fsnotify_event *event,
|
struct fsnotify_event *event,
|
||||||
int (*merge)(struct fsnotify_group *,
|
int (*merge)(struct fsnotify_group *,
|
||||||
struct fsnotify_event *),
|
struct fsnotify_event *),
|
||||||
void (*insert)(struct fsnotify_group *,
|
void (*insert)(struct fsnotify_group *,
|
||||||
struct fsnotify_event *));
|
struct fsnotify_event *));
|
||||||
|
|
||||||
|
static inline int fsnotify_add_event(struct fsnotify_group *group,
|
||||||
|
struct fsnotify_event *event,
|
||||||
|
int (*merge)(struct fsnotify_group *,
|
||||||
|
struct fsnotify_event *))
|
||||||
|
{
|
||||||
|
return fsnotify_insert_event(group, event, merge, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
/* Queue overflow event to a notification group */
|
/* Queue overflow event to a notification group */
|
||||||
static inline void fsnotify_queue_overflow(struct fsnotify_group *group)
|
static inline void fsnotify_queue_overflow(struct fsnotify_group *group)
|
||||||
{
|
{
|
||||||
fsnotify_add_event(group, group->overflow_event, NULL, NULL);
|
fsnotify_add_event(group, group->overflow_event, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool fsnotify_is_overflow_event(u32 mask)
|
||||||
|
{
|
||||||
|
return mask & FS_Q_OVERFLOW;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline bool fsnotify_notify_queue_is_empty(struct fsnotify_group *group)
|
static inline bool fsnotify_notify_queue_is_empty(struct fsnotify_group *group)
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
#define FAN_OPEN_EXEC 0x00001000 /* File was opened for exec */
|
#define FAN_OPEN_EXEC 0x00001000 /* File was opened for exec */
|
||||||
|
|
||||||
#define FAN_Q_OVERFLOW 0x00004000 /* Event queued overflowed */
|
#define FAN_Q_OVERFLOW 0x00004000 /* Event queued overflowed */
|
||||||
|
#define FAN_FS_ERROR 0x00008000 /* Filesystem error */
|
||||||
|
|
||||||
#define FAN_OPEN_PERM 0x00010000 /* File open in perm check */
|
#define FAN_OPEN_PERM 0x00010000 /* File open in perm check */
|
||||||
#define FAN_ACCESS_PERM 0x00020000 /* File accessed in perm check */
|
#define FAN_ACCESS_PERM 0x00020000 /* File accessed in perm check */
|
||||||
@ -125,6 +126,7 @@ struct fanotify_event_metadata {
|
|||||||
#define FAN_EVENT_INFO_TYPE_DFID_NAME 2
|
#define FAN_EVENT_INFO_TYPE_DFID_NAME 2
|
||||||
#define FAN_EVENT_INFO_TYPE_DFID 3
|
#define FAN_EVENT_INFO_TYPE_DFID 3
|
||||||
#define FAN_EVENT_INFO_TYPE_PIDFD 4
|
#define FAN_EVENT_INFO_TYPE_PIDFD 4
|
||||||
|
#define FAN_EVENT_INFO_TYPE_ERROR 5
|
||||||
|
|
||||||
/* Variable length info record following event metadata */
|
/* Variable length info record following event metadata */
|
||||||
struct fanotify_event_info_header {
|
struct fanotify_event_info_header {
|
||||||
@ -159,6 +161,12 @@ struct fanotify_event_info_pidfd {
|
|||||||
__s32 pidfd;
|
__s32 pidfd;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct fanotify_event_info_error {
|
||||||
|
struct fanotify_event_info_header hdr;
|
||||||
|
__s32 error;
|
||||||
|
__u32 error_count;
|
||||||
|
};
|
||||||
|
|
||||||
struct fanotify_response {
|
struct fanotify_response {
|
||||||
__s32 fd;
|
__s32 fd;
|
||||||
__u32 response;
|
__u32 response;
|
||||||
|
@ -160,8 +160,7 @@ static int audit_mark_handle_event(struct fsnotify_mark *inode_mark, u32 mask,
|
|||||||
|
|
||||||
audit_mark = container_of(inode_mark, struct audit_fsnotify_mark, mark);
|
audit_mark = container_of(inode_mark, struct audit_fsnotify_mark, mark);
|
||||||
|
|
||||||
if (WARN_ON_ONCE(inode_mark->group != audit_fsnotify_group) ||
|
if (WARN_ON_ONCE(inode_mark->group != audit_fsnotify_group))
|
||||||
WARN_ON_ONCE(!inode))
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (mask & (FS_CREATE|FS_MOVED_TO|FS_DELETE|FS_MOVED_FROM)) {
|
if (mask & (FS_CREATE|FS_MOVED_TO|FS_DELETE|FS_MOVED_FROM)) {
|
||||||
|
@ -473,8 +473,7 @@ static int audit_watch_handle_event(struct fsnotify_mark *inode_mark, u32 mask,
|
|||||||
|
|
||||||
parent = container_of(inode_mark, struct audit_parent, mark);
|
parent = container_of(inode_mark, struct audit_parent, mark);
|
||||||
|
|
||||||
if (WARN_ON_ONCE(inode_mark->group != audit_watch_group) ||
|
if (WARN_ON_ONCE(inode_mark->group != audit_watch_group))
|
||||||
WARN_ON_ONCE(!inode))
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (mask & (FS_CREATE|FS_MOVED_TO) && inode)
|
if (mask & (FS_CREATE|FS_MOVED_TO) && inode)
|
||||||
|
@ -120,6 +120,15 @@ config SAMPLE_CONNECTOR
|
|||||||
with it.
|
with it.
|
||||||
See also Documentation/driver-api/connector.rst
|
See also Documentation/driver-api/connector.rst
|
||||||
|
|
||||||
|
config SAMPLE_FANOTIFY_ERROR
|
||||||
|
bool "Build fanotify error monitoring sample"
|
||||||
|
depends on FANOTIFY && CC_CAN_LINK && HEADERS_INSTALL
|
||||||
|
help
|
||||||
|
When enabled, this builds an example code that uses the
|
||||||
|
FAN_FS_ERROR fanotify mechanism to monitor filesystem
|
||||||
|
errors.
|
||||||
|
See also Documentation/admin-guide/filesystem-monitoring.rst.
|
||||||
|
|
||||||
config SAMPLE_HIDRAW
|
config SAMPLE_HIDRAW
|
||||||
bool "hidraw sample"
|
bool "hidraw sample"
|
||||||
depends on CC_CAN_LINK && HEADERS_INSTALL
|
depends on CC_CAN_LINK && HEADERS_INSTALL
|
||||||
|
@ -5,6 +5,7 @@ subdir-$(CONFIG_SAMPLE_AUXDISPLAY) += auxdisplay
|
|||||||
subdir-$(CONFIG_SAMPLE_ANDROID_BINDERFS) += binderfs
|
subdir-$(CONFIG_SAMPLE_ANDROID_BINDERFS) += binderfs
|
||||||
obj-$(CONFIG_SAMPLE_CONFIGFS) += configfs/
|
obj-$(CONFIG_SAMPLE_CONFIGFS) += configfs/
|
||||||
obj-$(CONFIG_SAMPLE_CONNECTOR) += connector/
|
obj-$(CONFIG_SAMPLE_CONNECTOR) += connector/
|
||||||
|
obj-$(CONFIG_SAMPLE_FANOTIFY_ERROR) += fanotify/
|
||||||
subdir-$(CONFIG_SAMPLE_HIDRAW) += hidraw
|
subdir-$(CONFIG_SAMPLE_HIDRAW) += hidraw
|
||||||
obj-$(CONFIG_SAMPLE_HW_BREAKPOINT) += hw_breakpoint/
|
obj-$(CONFIG_SAMPLE_HW_BREAKPOINT) += hw_breakpoint/
|
||||||
obj-$(CONFIG_SAMPLE_KDB) += kdb/
|
obj-$(CONFIG_SAMPLE_KDB) += kdb/
|
||||||
|
5
samples/fanotify/Makefile
Normal file
5
samples/fanotify/Makefile
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
# SPDX-License-Identifier: GPL-2.0-only
|
||||||
|
userprogs-always-y += fs-monitor
|
||||||
|
|
||||||
|
userccflags += -I usr/include -Wall
|
||||||
|
|
142
samples/fanotify/fs-monitor.c
Normal file
142
samples/fanotify/fs-monitor.c
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
|
/*
|
||||||
|
* Copyright 2021, Collabora Ltd.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define _GNU_SOURCE
|
||||||
|
#include <errno.h>
|
||||||
|
#include <err.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <sys/fanotify.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#ifndef FAN_FS_ERROR
|
||||||
|
#define FAN_FS_ERROR 0x00008000
|
||||||
|
#define FAN_EVENT_INFO_TYPE_ERROR 5
|
||||||
|
|
||||||
|
struct fanotify_event_info_error {
|
||||||
|
struct fanotify_event_info_header hdr;
|
||||||
|
__s32 error;
|
||||||
|
__u32 error_count;
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef FILEID_INO32_GEN
|
||||||
|
#define FILEID_INO32_GEN 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef FILEID_INVALID
|
||||||
|
#define FILEID_INVALID 0xff
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static void print_fh(struct file_handle *fh)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
uint32_t *h = (uint32_t *) fh->f_handle;
|
||||||
|
|
||||||
|
printf("\tfh: ");
|
||||||
|
for (i = 0; i < fh->handle_bytes; i++)
|
||||||
|
printf("%hhx", fh->f_handle[i]);
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
printf("\tdecoded fh: ");
|
||||||
|
if (fh->handle_type == FILEID_INO32_GEN)
|
||||||
|
printf("inode=%u gen=%u\n", h[0], h[1]);
|
||||||
|
else if (fh->handle_type == FILEID_INVALID && !fh->handle_bytes)
|
||||||
|
printf("Type %d (Superblock error)\n", fh->handle_type);
|
||||||
|
else
|
||||||
|
printf("Type %d (Unknown)\n", fh->handle_type);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_notifications(char *buffer, int len)
|
||||||
|
{
|
||||||
|
struct fanotify_event_metadata *event =
|
||||||
|
(struct fanotify_event_metadata *) buffer;
|
||||||
|
struct fanotify_event_info_header *info;
|
||||||
|
struct fanotify_event_info_error *err;
|
||||||
|
struct fanotify_event_info_fid *fid;
|
||||||
|
int off;
|
||||||
|
|
||||||
|
for (; FAN_EVENT_OK(event, len); event = FAN_EVENT_NEXT(event, len)) {
|
||||||
|
|
||||||
|
if (event->mask != FAN_FS_ERROR) {
|
||||||
|
printf("unexpected FAN MARK: %llx\n",
|
||||||
|
(unsigned long long)event->mask);
|
||||||
|
goto next_event;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event->fd != FAN_NOFD) {
|
||||||
|
printf("Unexpected fd (!= FAN_NOFD)\n");
|
||||||
|
goto next_event;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("FAN_FS_ERROR (len=%d)\n", event->event_len);
|
||||||
|
|
||||||
|
for (off = sizeof(*event) ; off < event->event_len;
|
||||||
|
off += info->len) {
|
||||||
|
info = (struct fanotify_event_info_header *)
|
||||||
|
((char *) event + off);
|
||||||
|
|
||||||
|
switch (info->info_type) {
|
||||||
|
case FAN_EVENT_INFO_TYPE_ERROR:
|
||||||
|
err = (struct fanotify_event_info_error *) info;
|
||||||
|
|
||||||
|
printf("\tGeneric Error Record: len=%d\n",
|
||||||
|
err->hdr.len);
|
||||||
|
printf("\terror: %d\n", err->error);
|
||||||
|
printf("\terror_count: %d\n", err->error_count);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FAN_EVENT_INFO_TYPE_FID:
|
||||||
|
fid = (struct fanotify_event_info_fid *) info;
|
||||||
|
|
||||||
|
printf("\tfsid: %x%x\n",
|
||||||
|
fid->fsid.val[0], fid->fsid.val[1]);
|
||||||
|
print_fh((struct file_handle *) &fid->handle);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
printf("\tUnknown info type=%d len=%d:\n",
|
||||||
|
info->info_type, info->len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
next_event:
|
||||||
|
printf("---\n\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
char buffer[BUFSIZ];
|
||||||
|
|
||||||
|
if (argc < 2) {
|
||||||
|
printf("Missing path argument\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fd = fanotify_init(FAN_CLASS_NOTIF|FAN_REPORT_FID, O_RDONLY);
|
||||||
|
if (fd < 0)
|
||||||
|
errx(1, "fanotify_init");
|
||||||
|
|
||||||
|
if (fanotify_mark(fd, FAN_MARK_ADD|FAN_MARK_FILESYSTEM,
|
||||||
|
FAN_FS_ERROR, AT_FDCWD, argv[1])) {
|
||||||
|
errx(1, "fanotify_mark");
|
||||||
|
}
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
int n = read(fd, buffer, BUFSIZ);
|
||||||
|
|
||||||
|
if (n < 0)
|
||||||
|
errx(1, "read");
|
||||||
|
|
||||||
|
handle_notifications(buffer, n);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user