Skip to content

Commit b4b2ff4

Browse files
amir73iljankara
authored andcommitted
fanotify: allow to set errno in FAN_DENY permission response
With FAN_DENY response, user trying to perform the filesystem operation gets an error with errno set to EPERM. It is useful for hierarchical storage management (HSM) service to be able to deny access for reasons more diverse than EPERM, for example EAGAIN, if HSM could retry the operation later. Allow fanotify groups with priority FAN_CLASSS_PRE_CONTENT to responsd to permission events with the response value FAN_DENY_ERRNO(errno), instead of FAN_DENY to return a custom error. Limit custom error values to errors expected on read(2)/write(2) and open(2) of regular files. This list could be extended in the future. Userspace can test for legitimate values of FAN_DENY_ERRNO(errno) by writing a response to an fanotify group fd with a value of FAN_NOFD in the fd field of the response. The change in fanotify_response is backward compatible, because errno is written in the high 8 bits of the 32bit response field and old kernels reject respose value with high bits set. Signed-off-by: Amir Goldstein <[email protected]> Signed-off-by: Jan Kara <[email protected]> Link: https://patch.msgid.link/1e5fb6af84b69ca96b5c849fa5f10bdf4d1dc414.1731684329.git.josef@toxicpanda.com
1 parent 870499b commit b4b2ff4

File tree

5 files changed

+55
-7
lines changed

5 files changed

+55
-7
lines changed

fs/notify/fanotify/fanotify.c

+13-4
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,7 @@ static int fanotify_get_response(struct fsnotify_group *group,
223223
struct fanotify_perm_event *event,
224224
struct fsnotify_iter_info *iter_info)
225225
{
226-
int ret;
226+
int ret, errno;
227227

228228
pr_debug("%s: group=%p event=%p\n", __func__, group, event);
229229

@@ -262,14 +262,23 @@ static int fanotify_get_response(struct fsnotify_group *group,
262262
ret = 0;
263263
break;
264264
case FAN_DENY:
265+
/* Check custom errno from pre-content events */
266+
errno = fanotify_get_response_errno(event->response);
267+
if (errno) {
268+
ret = -errno;
269+
break;
270+
}
271+
fallthrough;
265272
default:
266273
ret = -EPERM;
267274
}
268275

269276
/* Check if the response should be audited */
270-
if (event->response & FAN_AUDIT)
271-
audit_fanotify(event->response & ~FAN_AUDIT,
272-
&event->audit_rule);
277+
if (event->response & FAN_AUDIT) {
278+
u32 response = event->response &
279+
(FANOTIFY_RESPONSE_ACCESS | FANOTIFY_RESPONSE_FLAGS);
280+
audit_fanotify(response & ~FAN_AUDIT, &event->audit_rule);
281+
}
273282

274283
pr_debug("%s: group=%p event=%p about to return ret=%d\n", __func__,
275284
group, event, ret);

fs/notify/fanotify/fanotify.h

+5
Original file line numberDiff line numberDiff line change
@@ -528,3 +528,8 @@ static inline unsigned int fanotify_mark_user_flags(struct fsnotify_mark *mark)
528528

529529
return mflags;
530530
}
531+
532+
static inline u32 fanotify_get_response_errno(int res)
533+
{
534+
return (res >> FAN_ERRNO_SHIFT) & FAN_ERRNO_MASK;
535+
}

fs/notify/fanotify/fanotify_user.c

+27-2
Original file line numberDiff line numberDiff line change
@@ -327,11 +327,12 @@ static int process_access_response(struct fsnotify_group *group,
327327
struct fanotify_perm_event *event;
328328
int fd = response_struct->fd;
329329
u32 response = response_struct->response;
330+
int errno = fanotify_get_response_errno(response);
330331
int ret = info_len;
331332
struct fanotify_response_info_audit_rule friar;
332333

333-
pr_debug("%s: group=%p fd=%d response=%u buf=%p size=%zu\n", __func__,
334-
group, fd, response, info, info_len);
334+
pr_debug("%s: group=%p fd=%d response=%x errno=%d buf=%p size=%zu\n",
335+
__func__, group, fd, response, errno, info, info_len);
335336
/*
336337
* make sure the response is valid, if invalid we do nothing and either
337338
* userspace can send a valid response or we will clean it up after the
@@ -342,7 +343,31 @@ static int process_access_response(struct fsnotify_group *group,
342343

343344
switch (response & FANOTIFY_RESPONSE_ACCESS) {
344345
case FAN_ALLOW:
346+
if (errno)
347+
return -EINVAL;
348+
break;
345349
case FAN_DENY:
350+
/* Custom errno is supported only for pre-content groups */
351+
if (errno && group->priority != FSNOTIFY_PRIO_PRE_CONTENT)
352+
return -EINVAL;
353+
354+
/*
355+
* Limit errno to values expected on open(2)/read(2)/write(2)
356+
* of regular files.
357+
*/
358+
switch (errno) {
359+
case 0:
360+
case EIO:
361+
case EPERM:
362+
case EBUSY:
363+
case ETXTBSY:
364+
case EAGAIN:
365+
case ENOSPC:
366+
case EDQUOT:
367+
break;
368+
default:
369+
return -EINVAL;
370+
}
346371
break;
347372
default:
348373
return -EINVAL;

include/linux/fanotify.h

+3-1
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,9 @@
132132
/* These masks check for invalid bits in permission responses. */
133133
#define FANOTIFY_RESPONSE_ACCESS (FAN_ALLOW | FAN_DENY)
134134
#define FANOTIFY_RESPONSE_FLAGS (FAN_AUDIT | FAN_INFO)
135-
#define FANOTIFY_RESPONSE_VALID_MASK (FANOTIFY_RESPONSE_ACCESS | FANOTIFY_RESPONSE_FLAGS)
135+
#define FANOTIFY_RESPONSE_VALID_MASK \
136+
(FANOTIFY_RESPONSE_ACCESS | FANOTIFY_RESPONSE_FLAGS | \
137+
(FAN_ERRNO_MASK << FAN_ERRNO_SHIFT))
136138

137139
/* Do not use these old uapi constants internally */
138140
#undef FAN_ALL_CLASS_BITS

include/uapi/linux/fanotify.h

+7
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,13 @@ struct fanotify_response_info_audit_rule {
235235
/* Legit userspace responses to a _PERM event */
236236
#define FAN_ALLOW 0x01
237237
#define FAN_DENY 0x02
238+
/* errno other than EPERM can specified in upper byte of deny response */
239+
#define FAN_ERRNO_BITS 8
240+
#define FAN_ERRNO_SHIFT (32 - FAN_ERRNO_BITS)
241+
#define FAN_ERRNO_MASK ((1 << FAN_ERRNO_BITS) - 1)
242+
#define FAN_DENY_ERRNO(err) \
243+
(FAN_DENY | ((((__u32)(err)) & FAN_ERRNO_MASK) << FAN_ERRNO_SHIFT))
244+
238245
#define FAN_AUDIT 0x10 /* Bitmask to create audit record for result */
239246
#define FAN_INFO 0x20 /* Bitmask to indicate additional information */
240247

0 commit comments

Comments
 (0)