summaryrefslogtreecommitdiff
path: root/fs
diff options
context:
space:
mode:
Diffstat (limited to 'fs')
-rw-r--r--fs/notify/fanotify/fanotify.c75
-rw-r--r--fs/notify/fanotify/fanotify.h12
2 files changed, 59 insertions, 28 deletions
diff --git a/fs/notify/fanotify/fanotify.c b/fs/notify/fanotify/fanotify.c
index 31fd41e91575..c107974d6830 100644
--- a/fs/notify/fanotify/fanotify.c
+++ b/fs/notify/fanotify/fanotify.c
@@ -299,55 +299,78 @@ static u32 fanotify_group_event_mask(struct fsnotify_group *group,
}
/*
+ * Check size needed to encode fanotify_fh.
+ *
+ * Return size of encoded fh without fanotify_fh header.
+ * Return 0 on failure to encode.
+ */
+static int fanotify_encode_fh_len(struct inode *inode)
+{
+ int dwords = 0;
+
+ if (!inode)
+ return 0;
+
+ exportfs_encode_inode_fh(inode, NULL, &dwords, NULL);
+
+ return dwords << 2;
+}
+
+/*
* Encode fanotify_fh.
*
* Return total size of encoded fh including fanotify_fh header.
* Return 0 on failure to encode.
*/
static int fanotify_encode_fh(struct fanotify_fh *fh, struct inode *inode,
- gfp_t gfp)
+ unsigned int fh_len, gfp_t gfp)
{
- int dwords, type, bytes = 0;
+ int dwords, type = 0;
char *ext_buf = NULL;
void *buf = fh->buf;
int err;
fh->type = FILEID_ROOT;
fh->len = 0;
+ fh->flags = 0;
if (!inode)
return 0;
- dwords = 0;
+ /*
+ * !gpf means preallocated variable size fh, but fh_len could
+ * be zero in that case if encoding fh len failed.
+ */
err = -ENOENT;
- type = exportfs_encode_inode_fh(inode, NULL, &dwords, NULL);
- if (!dwords)
+ if (fh_len < 4 || WARN_ON_ONCE(fh_len % 4))
goto out_err;
- bytes = dwords << 2;
- if (bytes > FANOTIFY_INLINE_FH_LEN) {
- /* Treat failure to allocate fh as failure to allocate event */
+ /* No external buffer in a variable size allocated fh */
+ if (gfp && fh_len > FANOTIFY_INLINE_FH_LEN) {
+ /* Treat failure to allocate fh as failure to encode fh */
err = -ENOMEM;
- ext_buf = kmalloc(bytes, gfp);
+ ext_buf = kmalloc(fh_len, gfp);
if (!ext_buf)
goto out_err;
*fanotify_fh_ext_buf_ptr(fh) = ext_buf;
buf = ext_buf;
+ fh->flags |= FANOTIFY_FH_FLAG_EXT_BUF;
}
+ dwords = fh_len >> 2;
type = exportfs_encode_inode_fh(inode, buf, &dwords, NULL);
err = -EINVAL;
- if (!type || type == FILEID_INVALID || bytes != dwords << 2)
+ if (!type || type == FILEID_INVALID || fh_len != dwords << 2)
goto out_err;
fh->type = type;
- fh->len = bytes;
+ fh->len = fh_len;
- return FANOTIFY_FH_HDR_LEN + bytes;
+ return FANOTIFY_FH_HDR_LEN + fh_len;
out_err:
pr_warn_ratelimited("fanotify: failed to encode fid (type=%d, len=%d, err=%i)\n",
- type, bytes, err);
+ type, fh_len, err);
kfree(ext_buf);
*fanotify_fh_ext_buf_ptr(fh) = NULL;
/* Report the event without a file identifier on encode error */
@@ -419,7 +442,8 @@ static struct fanotify_event *fanotify_alloc_fid_event(struct inode *id,
ffe->fae.type = FANOTIFY_EVENT_TYPE_FID;
ffe->fsid = *fsid;
- fanotify_encode_fh(&ffe->object_fh, id, gfp);
+ fanotify_encode_fh(&ffe->object_fh, id, fanotify_encode_fh_len(id),
+ gfp);
return &ffe->fae;
}
@@ -432,8 +456,13 @@ static struct fanotify_event *fanotify_alloc_name_event(struct inode *id,
struct fanotify_name_event *fne;
struct fanotify_info *info;
struct fanotify_fh *dfh;
+ unsigned int dir_fh_len = fanotify_encode_fh_len(id);
+ unsigned int size;
- fne = kmalloc(sizeof(*fne) + file_name->len + 1, gfp);
+ size = sizeof(*fne) + FANOTIFY_FH_HDR_LEN + dir_fh_len;
+ if (file_name)
+ size += file_name->len + 1;
+ fne = kmalloc(size, gfp);
if (!fne)
return NULL;
@@ -442,8 +471,13 @@ static struct fanotify_event *fanotify_alloc_name_event(struct inode *id,
info = &fne->info;
fanotify_info_init(info);
dfh = fanotify_info_dir_fh(info);
- info->dir_fh_totlen = fanotify_encode_fh(dfh, id, gfp);
- fanotify_info_copy_name(info, file_name);
+ info->dir_fh_totlen = fanotify_encode_fh(dfh, id, dir_fh_len, 0);
+ if (file_name)
+ fanotify_info_copy_name(info, file_name);
+
+ pr_debug("%s: ino=%lu size=%u dir_fh_len=%u name_len=%u name='%.*s'\n",
+ __func__, id->i_ino, size, dir_fh_len,
+ info->name_len, info->name_len, fanotify_info_name(info));
return &fne->fae;
}
@@ -658,12 +692,7 @@ static void fanotify_free_fid_event(struct fanotify_event *event)
static void fanotify_free_name_event(struct fanotify_event *event)
{
- struct fanotify_name_event *fne = FANOTIFY_NE(event);
- struct fanotify_fh *dfh = fanotify_info_dir_fh(&fne->info);
-
- if (fanotify_fh_has_ext_buf(dfh))
- kfree(fanotify_fh_ext_buf(dfh));
- kfree(fne);
+ kfree(FANOTIFY_NE(event));
}
static void fanotify_free_event(struct fsnotify_event *fsn_event)
diff --git a/fs/notify/fanotify/fanotify.h b/fs/notify/fanotify/fanotify.h
index 5e104fc56abb..12c204b1489f 100644
--- a/fs/notify/fanotify/fanotify.h
+++ b/fs/notify/fanotify/fanotify.h
@@ -29,8 +29,10 @@ enum {
struct fanotify_fh {
u8 type;
u8 len;
- u8 pad[2];
- unsigned char buf[FANOTIFY_INLINE_FH_LEN];
+#define FANOTIFY_FH_FLAG_EXT_BUF 1
+ u8 flags;
+ u8 pad;
+ unsigned char buf[];
} __aligned(4);
/* Variable size struct for dir file handle + child file handle + name */
@@ -50,7 +52,7 @@ struct fanotify_info {
static inline bool fanotify_fh_has_ext_buf(struct fanotify_fh *fh)
{
- return fh->len > FANOTIFY_INLINE_FH_LEN;
+ return (fh->flags & FANOTIFY_FH_FLAG_EXT_BUF);
}
static inline char **fanotify_fh_ext_buf_ptr(struct fanotify_fh *fh)
@@ -154,6 +156,8 @@ struct fanotify_fid_event {
struct fanotify_event fae;
__kernel_fsid_t fsid;
struct fanotify_fh object_fh;
+ /* Reserve space in object_fh.buf[] - access with fanotify_fh_buf() */
+ unsigned char _inline_fh_buf[FANOTIFY_INLINE_FH_LEN];
};
static inline struct fanotify_fid_event *
@@ -166,8 +170,6 @@ struct fanotify_name_event {
struct fanotify_event fae;
__kernel_fsid_t fsid;
struct fanotify_info info;
- /* Reserve space in info.buf[] - access with fanotify_info_dir_fh() */
- struct fanotify_fh _dir_fh;
};
static inline struct fanotify_name_event *