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@xxxxxxxxx> --- 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(-) diff --git a/fs/notify/fanotify/fanotify.c b/fs/notify/fanotify/fanotify.c index 6d386080faf2..6c877b3646ec 100644 --- a/fs/notify/fanotify/fanotify.c +++ b/fs/notify/fanotify/fanotify.c @@ -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); diff --git a/fs/notify/fsnotify.c b/fs/notify/fsnotify.c index e2b4f17a48bb..7b364f965650 100644 --- a/fs/notify/fsnotify.c +++ b/fs/notify/fsnotify.c @@ -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) diff --git a/include/linux/fanotify.h b/include/linux/fanotify.h index 3c817dc6292e..85def9de2ef4 100644 --- a/include/linux/fanotify.h +++ b/include/linux/fanotify.h @@ -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 | \ diff --git a/include/linux/fsnotify.h b/include/linux/fsnotify.h index 454d8e466958..9beea32c8980 100644 --- a/include/linux/fsnotify.h +++ b/include/linux/fsnotify.h @@ -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); } /* diff --git a/include/linux/fsnotify_backend.h b/include/linux/fsnotify_backend.h index 6cd8d1d28b8b..396943093373 100644 --- a/include/linux/fsnotify_backend.h +++ b/include/linux/fsnotify_backend.h @@ -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; } diff --git a/include/uapi/linux/fanotify.h b/include/uapi/linux/fanotify.h index e710967c7c26..a9728a550daa 100644 --- a/include/uapi/linux/fanotify.h +++ b/include/uapi/linux/fanotify.h @@ -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 */ -- 2.34.1