strace/fanotify.c
Eugene Syromyatnikov 78fd3aa2b8 fanotify: decode fanotify fd I/O
* xlat/fan_responses.in: New file.
* defs.h (decode_fanotify_read, decode_fanotify_write): New
declarations.
* fanotify.c: Include "print_fields.h".
[!FAN_AUDIT] (FAN_AUDIT): New macro definition.
Include "xlat/fan_responses.h".
(print_fanfd, decode_fanotify_read, decode_fanotify_write): New
functions.
* io.c (decode_readbuf): Add fanotify fd decoder using
decode_fanotify_read.
(decode_writebuf): Add fanotify fd decoder using decode_fanotify_write.
2018-09-25 03:17:33 +02:00

316 lines
7.2 KiB
C

/*
* Copyright (c) 2014-2015 Dmitry V. Levin <ldv@altlinux.org>
* Copyright (c) 2014-2018 The strace developers.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "defs.h"
#include "print_fields.h"
#include "xlat/fan_classes.h"
#include "xlat/fan_init_flags.h"
#ifndef FAN_ALL_CLASS_BITS
# define FAN_ALL_CLASS_BITS (FAN_CLASS_NOTIF | FAN_CLASS_CONTENT | FAN_CLASS_PRE_CONTENT)
#endif
#ifndef FAN_NOFD
# define FAN_NOFD -1
#endif
#ifndef FAN_AUDIT
# define FAN_AUDIT 0x10
#endif
#include "xlat/fan_mark_flags.h"
#include "xlat/fan_event_flags.h"
#include "xlat/fan_responses.h"
static void
print_fanfd(struct tcb *tcp, int fd)
{
if (fd == FAN_NOFD)
print_xlat_d(FAN_NOFD);
else
printfd(tcp, fd);
}
#define PRINT_FIELD_FANFD(prefix_, where_, field_, tcp_) \
do { \
STRACE_PRINTF("%s%s=", (prefix_), #field_); \
print_fanfd((tcp_), (where_).field_); \
} while (0)
bool
decode_fanotify_read(struct tcb *tcp, int fd, const char *fdpath,
enum fileops op, kernel_ulong_t addr,
kernel_ulong_t addrlen)
{
struct fev_hdr {
uint32_t event_len;
union {
struct {
uint8_t vers;
uint8_t reserved;
uint16_t metadata_len;
};
uint32_t vers_v2;
};
} fev_hdr;
uint32_t fev_ver = 0;
union fev_md {
struct fev_md_v2 {
uint64_t ATTRIBUTE_ALIGNED(8) mask;
int32_t fd;
int32_t pid;
} v2;
struct fev_md_v1 {
int32_t fd;
uint64_t ATTRIBUTE_ALIGNED(8) mask;
int64_t pid;
} ATTRIBUTE_PACKED v1;
} fev_md;
enum {
FEV_V1_SIZE = sizeof(struct fev_hdr) + sizeof(struct fev_md_v1),
FEV_V2_SIZE = sizeof(struct fev_hdr) + sizeof(struct fev_md_v2),
FEV_MIN_SIZE = MIN(FEV_V1_SIZE, FEV_V2_SIZE),
};
kernel_ulong_t pos = 0;
if (addrlen < sizeof(fev_hdr))
return false;
tprints("[");
do {
if (pos)
tprints(", ");
if (umove(tcp, addr + pos, &fev_hdr)) {
printaddr_comment(addr + pos);
break;
}
PRINT_FIELD_U("{", fev_hdr, event_len);
if (fev_hdr.event_len < FEV_MIN_SIZE) {
tprints(", ... /* invalid event_len */}");
if (!fev_hdr.event_len)
goto end_decoded;
goto end_fev_decoded;
}
switch (fev_hdr.vers) {
case 0: case 1: case 2:
switch (fev_hdr.vers_v2) {
case 1: case 2:
PRINT_FIELD_U(", ", fev_hdr, vers_v2);
fev_ver = fev_hdr.vers;
break;
default:
tprints("}");
pos += offsetof(struct fev_hdr, vers_v2);
goto end_decoded;
}
break;
case 3: default:
PRINT_FIELD_U(", ", fev_hdr, vers);
fev_ver = fev_hdr.vers;
if (fev_hdr.reserved)
PRINT_FIELD_U(", ", fev_hdr, reserved);
PRINT_FIELD_U(", ", fev_hdr, metadata_len);
if (fev_hdr.metadata_len < FEV_V2_SIZE) {
tprints(", ... /* invalid metadata_len */}");
goto end_fev_decoded;
}
}
if (fev_ver < 1 || fev_ver > 3) {
tprints(", ... /* invalid vers */}");
goto end_fev_decoded;
}
switch (fev_ver) {
case 1:
if (umove(tcp, addr + pos + sizeof(fev_hdr),
&fev_md.v1)) {
printf(", ...}");
pos += sizeof(fev_hdr);
goto end_decoded;
}
PRINT_FIELD_FANFD(", ", fev_md.v1, fd, tcp);
PRINT_FIELD_FLAGS(", ", fev_md.v1, mask,
fan_event_flags, "FAN_???");
PRINT_FIELD_D(", ", fev_md.v1, pid);
if (FEV_V1_SIZE < fev_hdr.event_len) {
tprints(", ");
printstrn(tcp, addr + pos,
fev_hdr.event_len - FEV_V1_SIZE);
}
break;
case 2: case 3:
if (umove(tcp, addr + pos + sizeof(fev_hdr),
&fev_md.v2)) {
printf(", ...}");
pos += sizeof(fev_hdr);
goto end_decoded;
}
PRINT_FIELD_FLAGS(", ", fev_md.v2, mask,
fan_event_flags, "FAN_???");
PRINT_FIELD_FANFD(", ", fev_md.v2, fd, tcp);
PRINT_FIELD_D(", ", fev_md.v2, pid);
if (FEV_V2_SIZE < fev_hdr.event_len) {
tprints(", ");
printstrn(tcp, addr + pos,
fev_hdr.event_len - FEV_V2_SIZE);
}
}
tprints("}");
end_fev_decoded:
pos += fev_hdr.event_len;
} while (pos <= addrlen - sizeof(fev_hdr));
end_decoded:
if (pos < addrlen) {
if (pos)
tprints(", ");
printstrn(tcp, addr + pos, addrlen - pos);
}
tprints("]");
return true;
}
bool
decode_fanotify_write(struct tcb *tcp, int fd, const char *fdpath,
enum fileops op, kernel_ulong_t addr,
kernel_ulong_t addrlen)
{
struct fresp {
int32_t fd;
uint32_t response;
} fresp;
kernel_ulong_t pos = 0;
if (addrlen < sizeof(fresp))
return false;
tprints("[");
do {
if (pos)
tprints(", ");
if (umove(tcp, addr + pos, &fresp)) {
printaddr_comment(addr + pos);
break;
}
PRINT_FIELD_FD("{", fresp, fd, tcp);
tprints(", response=");
if (fresp.response | FAN_AUDIT) {
print_xlat(FAN_AUDIT);
tprints("|");
}
printxval(fan_responses, fresp.response, "FAN_???");
tprints("}");
pos += sizeof(fresp);
} while (pos <= addrlen - sizeof(fresp));
if (pos < addrlen) {
if (pos)
tprints(", ");
printstrn(tcp, addr + pos, addrlen - pos);
}
tprints("]");
return true;
}
SYS_FUNC(fanotify_init)
{
unsigned int flags = tcp->u_arg[0];
printxval(fan_classes, flags & FAN_ALL_CLASS_BITS, "FAN_CLASS_???");
flags &= ~FAN_ALL_CLASS_BITS;
if (flags) {
tprints("|");
printflags(fan_init_flags, flags, "FAN_???");
}
tprints(", ");
tprint_open_modes((unsigned) tcp->u_arg[1]);
return RVAL_DECODED | RVAL_FD;
}
SYS_FUNC(fanotify_mark)
{
printfd(tcp, tcp->u_arg[0]);
tprints(", ");
printflags(fan_mark_flags, tcp->u_arg[1], "FAN_MARK_???");
tprints(", ");
/*
* the mask argument is defined as 64-bit,
* but kernel uses the lower 32 bits only.
*/
unsigned long long mask = 0;
int argn = getllval(tcp, &mask, 2);
#ifdef HPPA
/* Parsic is weird. See arch/parisc/kernel/sys_parisc32.c. */
mask = (mask << 32) | (mask >> 32);
#endif
printflags64(fan_event_flags, mask, "FAN_???");
tprints(", ");
if ((int) tcp->u_arg[argn] == FAN_NOFD) {
print_xlat_d(FAN_NOFD);
tprints(", ");
} else {
print_dirfd(tcp, tcp->u_arg[argn]);
}
printpath(tcp, tcp->u_arg[argn + 1]);
return RVAL_DECODED;
}