@@ -950,8 +950,9 @@ static int fanotify_handle_event(struct fsnotify_group *group, u32 mask,
BUILD_BUG_ON(FAN_FS_ERROR != FS_ERROR);
BUILD_BUG_ON(FAN_RENAME != FS_RENAME);
BUILD_BUG_ON(FAN_PRE_ACCESS != FS_PRE_ACCESS);
+ BUILD_BUG_ON(FAN_PRE_MODIFY != FS_PRE_MODIFY);
- BUILD_BUG_ON(HWEIGHT32(ALL_FANOTIFY_EVENT_BITS) != 24);
+ BUILD_BUG_ON(HWEIGHT32(ALL_FANOTIFY_EVENT_BITS) != 25);
mask = fanotify_group_event_mask(group, iter_info, &match_mask,
mask, data, data_type, dir);
@@ -210,19 +210,23 @@ static inline bool fsnotify_object_watched(struct inode *inode, __u32 mnt_mask,
/* Report pre-content event with optional range info */
int fsnotify_pre_content(const struct path *path, const loff_t *ppos,
- size_t count)
+ size_t count, bool write)
{
+ __u32 mask = FS_PRE_ACCESS;
struct file_range range;
+ if (write)
+ mask |= FS_PRE_MODIFY;
+
/* Report page aligned range only when pos is known */
if (!ppos)
- return fsnotify_path(path, FS_PRE_ACCESS);
+ return fsnotify_path(path, mask);
range.path = path;
range.pos = PAGE_ALIGN_DOWN(*ppos);
range.count = PAGE_ALIGN(*ppos + count) - range.pos;
- return fsnotify_parent(path->dentry, FS_PRE_ACCESS, &range,
+ return fsnotify_parent(path->dentry, mask, &range,
FSNOTIFY_EVENT_FILE_RANGE);
}
@@ -745,7 +749,7 @@ static __init int fsnotify_init(void)
{
int ret;
- BUILD_BUG_ON(HWEIGHT32(ALL_FSNOTIFY_BITS) != 26);
+ BUILD_BUG_ON(HWEIGHT32(ALL_FSNOTIFY_BITS) != 27);
ret = init_srcu_struct(&fsnotify_mark_srcu);
if (ret)
@@ -94,7 +94,7 @@
#define FANOTIFY_CONTENT_PERM_EVENTS (FAN_OPEN_PERM | FAN_OPEN_EXEC_PERM | \
FAN_ACCESS_PERM)
/* Pre-content events can be used to fill file content */
-#define FANOTIFY_PRE_CONTENT_EVENTS (FAN_PRE_ACCESS)
+#define FANOTIFY_PRE_CONTENT_EVENTS (FAN_PRE_ACCESS | FAN_PRE_MODIFY)
/* Events that require a permission response from user */
#define FANOTIFY_PERM_EVENTS (FANOTIFY_CONTENT_PERM_EVENTS | \
@@ -144,7 +144,7 @@ static inline int fsnotify_file_area_perm(struct file *file, int perm_mask,
*/
lockdep_assert_once(file_write_not_started(file));
- if (!(perm_mask & (MAY_READ | MAY_WRITE | MAY_ACCESS)))
+ if (!(perm_mask & (MAY_READ | MAY_WRITE)))
return 0;
if (likely(!FMODE_FSNOTIFY_PERM(file->f_mode)))
@@ -154,7 +154,8 @@ static inline int fsnotify_file_area_perm(struct file *file, int perm_mask,
* read()/write() and other types of access generate pre-content events.
*/
if (unlikely(FMODE_FSNOTIFY_HSM(file->f_mode))) {
- int ret = fsnotify_pre_content(&file->f_path, ppos, count);
+ int ret = fsnotify_pre_content(&file->f_path, ppos, count,
+ perm_mask & MAY_WRITE);
if (ret)
return ret;
@@ -182,7 +183,8 @@ static inline int fsnotify_mmap_perm(struct file *file, int prot,
if (!file || likely(!FMODE_FSNOTIFY_HSM(file->f_mode)))
return 0;
- return fsnotify_pre_content(&file->f_path, &off, len);
+ /* XXX: should we report pre-modify for writably shared maps */
+ return fsnotify_pre_content(&file->f_path, &off, len, false);
}
/*
@@ -197,7 +199,7 @@ static inline int fsnotify_truncate_perm(const struct path *path, loff_t length)
FSNOTIFY_PRIO_PRE_CONTENT))
return 0;
- return fsnotify_pre_content(path, &length, 0);
+ return fsnotify_pre_content(path, &length, 0, true);
}
/*
@@ -58,6 +58,7 @@
/* #define FS_DIR_MODIFY 0x00080000 */ /* Deprecated (reserved) */
#define FS_PRE_ACCESS 0x00100000 /* Pre-content access hook */
+#define FS_PRE_MODIFY 0x00200000 /* Pre-content modify hook */
#define FS_MNT_ATTACH 0x01000000 /* Mount was attached */
#define FS_MNT_DETACH 0x02000000 /* Mount was detached */
@@ -91,7 +92,7 @@
#define FSNOTIFY_CONTENT_PERM_EVENTS (FS_OPEN_PERM | FS_OPEN_EXEC_PERM | \
FS_ACCESS_PERM)
/* Pre-content events can be used to fill file content */
-#define FSNOTIFY_PRE_CONTENT_EVENTS (FS_PRE_ACCESS)
+#define FSNOTIFY_PRE_CONTENT_EVENTS (FS_PRE_ACCESS | FS_PRE_MODIFY)
#define ALL_FSNOTIFY_PERM_EVENTS (FSNOTIFY_CONTENT_PERM_EVENTS | \
FSNOTIFY_PRE_CONTENT_EVENTS)
@@ -932,12 +933,13 @@ static inline void fsnotify_init_event(struct fsnotify_event *event)
INIT_LIST_HEAD(&event->list);
}
int fsnotify_pre_content(const struct path *path, const loff_t *ppos,
- size_t count);
+ size_t count, bool write);
#else
static inline int fsnotify_pre_content(const struct path *path,
- const loff_t *ppos, size_t count)
+ const loff_t *ppos, size_t count,
+ bool write)
{
return 0;
}
@@ -28,6 +28,8 @@
/* #define FAN_DIR_MODIFY 0x00080000 */ /* Deprecated (reserved) */
#define FAN_PRE_ACCESS 0x00100000 /* Pre-content access hook */
+#define FAN_PRE_MODIFY 0x00200000 /* Pre-content modify hook */
+
#define FAN_MNT_ATTACH 0x01000000 /* Mount was attached */
#define FAN_MNT_DETACH 0x02000000 /* Mount was detached */
In addition to FAN_PRE_ACCESS event before any access to a file range, also report the FAN_PRE_MODIFY event in case of a write access. This will allow userspace to subscribe only to pre-write access notifications and to respond with error codes associated with write operations using the FAN_DENY_ERRNO macro. Signed-off-by: Amir Goldstein <amir73il@gmail.com> --- Jan, I was looking on th list for the reason we decided to drop FAN_PRE_MODIFY from the pre-content patch set and I couldn't find it. It may have been related to complications ot page fault hooks that are not not relevant? I did find the decision to generate FAN_PRE_ACCESS on both read()/write(), so maybe we thought there was no clear value for the FAN_PRE_MODIFY event? In any case, I realized that we allowed returning custom errors with FAN_DENY_ERRNO(ENOSPC), but that chosing the right error does require knowing whether the call was read() or write(). Becaue mmap() cannot return write() errors like ENOSPC, I decided not to generate FAN_PRE_MODIFY for writably-shared maps, but maybe we should consider this. This patch implement FAN_PRE_{ACCESS,MODIFY} as a combined event. This is not like the way that we generate the permission events FAN_OPEN_PERM and FAN_OPEN_EXEC_PERM as separate events. I do not remember why we chose the seprate events in the case above. I do remember why we chose separate events for FAN_ACCESS_PERM and FAN_PRE_ACCESS - because they do not have the same info. Anyway, this way makes more sense to me for FAN_PRE_{ACCESS,MODIFY}. LTP test pushed to branch fan_hsm. WDYT? Amir. P.S. #1: if you accept this, we may consider sending to stable 6.14 to make FAN_PRE_MODIFY part of the initial version of the API. P.S. #2: I am going to look now at FAN_PRE_ACCESS events on directories that we left out, because we may want to include it in initial API if it turns out to be simple. fs/notify/fanotify/fanotify.c | 3 ++- fs/notify/fsnotify.c | 12 ++++++++---- include/linux/fanotify.h | 2 +- include/linux/fsnotify.h | 10 ++++++---- include/linux/fsnotify_backend.h | 8 +++++--- include/uapi/linux/fanotify.h | 2 ++ 6 files changed, 24 insertions(+), 13 deletions(-)