Fix fanotify_mark decoding on 32-bit architectures

The fanotify_mark syscall takes a 64-bit mask, and on 32-bit
architectures it is split up into two syscall arguments.

* configure.ac (AC_CHECK_FUNCS): Add fanotify_mark.
(AC_CHECK_HEADERS): Add sys/fanotify.h.
* defs.h (getllval): New prototype.
* util.c (getllval): New function based on printllval.
(printllval): Use getllval.
* fanotify.c (sys_fanotify_mark): Use getllval to properly decode
64-bit mask and two syscall arguments followed by it.
* tests/fanotify_mark.c: New file.
* tests/fanotify_mark.test: New test.
* tests/Makefile.am (check_PROGRAMS): Add fanotify_mark.
(TESTS): Add fanotify_mark.test.
* tests/.gitignore: Add fanotify_mark.
diff --git a/configure.ac b/configure.ac
index 8cb72d7..1a09b98 100644
--- a/configure.ac
+++ b/configure.ac
@@ -205,6 +205,7 @@
 AC_LITTLE_ENDIAN_LONG_LONG
 
 AC_CHECK_FUNCS(m4_normalize([
+	fanotify_mark
 	fopen64
 	fork
 	fputs_unlocked
@@ -239,6 +240,7 @@
 	stropts.h
 	sys/conf.h
 	sys/epoll.h
+	sys/fanotify.h
 	sys/filio.h
 	sys/ioctl.h
 	sys/poll.h
diff --git a/defs.h b/defs.h
index c865110..d0acb19 100644
--- a/defs.h
+++ b/defs.h
@@ -677,6 +677,7 @@
 # define LONG_LONG(a,b) \
 	((long long)((unsigned long long)(unsigned)(b) | ((unsigned long long)(a)<<32)))
 #endif
+extern int getllval(struct tcb *, unsigned long long *, int);
 extern int printllval(struct tcb *, const char *, int);
 
 extern void printxval(const struct xlat *, const unsigned int, const char *);
diff --git a/fanotify.c b/fanotify.c
index 32a3667..02094ea 100644
--- a/fanotify.c
+++ b/fanotify.c
@@ -31,6 +31,9 @@
 int
 sys_fanotify_mark(struct tcb *tcp)
 {
+	unsigned long long mask = 0;
+	int argn;
+
 	if (exiting(tcp))
 		return 0;
 
@@ -38,13 +41,18 @@
 	tprints(", ");
 	printflags(fan_mark_flags, (unsigned) tcp->u_arg[1], "FAN_MARK_???");
 	tprints(", ");
-	printflags(fan_event_flags, tcp->u_arg[2], "FAN_???");
+	/*
+	 * the mask argument is defined as 64-bit,
+	 * but kernel uses the lower 32 bits only.
+	 */
+	argn = getllval(tcp, &mask, 2);
+	printflags(fan_event_flags, mask, "FAN_???");
 	tprints(", ");
-	if ((int) tcp->u_arg[3] == FAN_NOFD)
+	if ((int) tcp->u_arg[argn] == FAN_NOFD)
 		tprints("FAN_NOFD, ");
 	else
-		print_dirfd(tcp, tcp->u_arg[3]);
-	printpath(tcp, tcp->u_arg[4]);
+		print_dirfd(tcp, tcp->u_arg[argn]);
+	printpath(tcp, tcp->u_arg[argn + 1]);
 
 	return 0;
 }
diff --git a/tests/.gitignore b/tests/.gitignore
index af463d0..ce3e210 100644
--- a/tests/.gitignore
+++ b/tests/.gitignore
@@ -1,4 +1,5 @@
 caps
+fanotify_mark
 inet-accept-connect-send-recv
 mmsg
 net-accept-connect
diff --git a/tests/Makefile.am b/tests/Makefile.am
index b88ed55..7c8f7b1 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -5,6 +5,7 @@
 check_PROGRAMS = \
 	inet-accept-connect-send-recv \
 	caps \
+	fanotify_mark \
 	mmsg \
 	net-accept-connect \
 	netlink_inet_diag \
@@ -32,6 +33,7 @@
 	strace-f.test \
 	qual_syscall.test \
 	caps.test \
+	fanotify_mark.test \
 	getdents.test \
 	scm_rights-fd.test \
 	sigaction.test \
diff --git a/tests/fanotify_mark.c b/tests/fanotify_mark.c
new file mode 100644
index 0000000..7160acd
--- /dev/null
+++ b/tests/fanotify_mark.c
@@ -0,0 +1,19 @@
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#if defined HAVE_SYS_FANOTIFY_H && defined HAVE_FANOTIFY_MARK
+# include <sys/fanotify.h>
+int
+main(void)
+{
+	fanotify_mark(-1, FAN_MARK_ADD, FAN_MODIFY | FAN_ONDIR, -100, ".");
+	return 0;
+}
+#else
+int
+main(void)
+{
+	return 77;
+}
+#endif
diff --git a/tests/fanotify_mark.test b/tests/fanotify_mark.test
new file mode 100755
index 0000000..c5eb4eb
--- /dev/null
+++ b/tests/fanotify_mark.test
@@ -0,0 +1,35 @@
+#!/bin/sh
+
+# Check fanotify_mark syscall decoding.
+
+. "${srcdir=.}/init.sh"
+
+check_prog grep
+
+./fanotify_mark || {
+	if [ $? -eq 77 ]; then
+		framework_skip_ 'fanotify_mark is not available'
+	else
+		fail_ 'fanotify_mark failed'
+	fi
+}
+
+args="-efanotify_mark ./fanotify_mark"
+$STRACE -o "$LOG" $args || {
+	cat "$LOG"
+	fail_ "$STRACE $args failed"
+}
+
+grep_log()
+{
+	local syscall="$1"; shift
+
+	LC_ALL=C grep -E -x "$syscall$*" $LOG > /dev/null || {
+		cat $LOG
+		fail_ "$STRACE $args failed to trace \"$syscall\" properly"
+	}
+}
+
+grep_log fanotify_mark '\(-1, FAN_MARK_ADD, FAN_MODIFY\|FAN_ONDIR, AT_FDCWD, "\."\) += -1.*'
+
+exit 0
diff --git a/util.c b/util.c
index 570888c..c7ad34f 100644
--- a/util.c
+++ b/util.c
@@ -242,39 +242,39 @@
 }
 
 /*
- * Print 64bit argument at position arg_no and return the index of the next
- * argument.
+ * Fetch 64bit argument at position arg_no and
+ * return the index of the next argument.
  */
 int
-printllval(struct tcb *tcp, const char *format, int arg_no)
+getllval(struct tcb *tcp, unsigned long long *val, int arg_no)
 {
 #if SIZEOF_LONG > 4 && SIZEOF_LONG == SIZEOF_LONG_LONG
 # if SUPPORTED_PERSONALITIES > 1
 	if (current_wordsize > 4) {
 # endif
-		tprintf(format, tcp->u_arg[arg_no]);
+		*val = tcp->u_arg[arg_no];
 		arg_no++;
 # if SUPPORTED_PERSONALITIES > 1
 	} else {
 #  if defined(AARCH64) || defined(POWERPC64)
 		/* Align arg_no to the next even number. */
 		arg_no = (arg_no + 1) & 0xe;
-#  endif
-		tprintf(format, LONG_LONG(tcp->u_arg[arg_no], tcp->u_arg[arg_no + 1]));
+#  endif /* AARCH64 || POWERPC64 */
+		*val = LONG_LONG(tcp->u_arg[arg_no], tcp->u_arg[arg_no + 1]);
 		arg_no += 2;
 	}
-# endif /* SUPPORTED_PERSONALITIES */
+# endif /* SUPPORTED_PERSONALITIES > 1 */
 #elif SIZEOF_LONG > 4
 #  error Unsupported configuration: SIZEOF_LONG > 4 && SIZEOF_LONG_LONG > SIZEOF_LONG
 #elif defined LINUX_MIPSN32
-	tprintf(format, tcp->ext_arg[arg_no]);
+	*val = tcp->ext_arg[arg_no];
 	arg_no++;
 #elif defined X32
 	if (current_personality == 0) {
-		tprintf(format, tcp->ext_arg[arg_no]);
+		*val = tcp->ext_arg[arg_no];
 		arg_no++;
 	} else {
-		tprintf(format, LONG_LONG(tcp->u_arg[arg_no], tcp->u_arg[arg_no + 1]));
+		*val = LONG_LONG(tcp->u_arg[arg_no], tcp->u_arg[arg_no + 1]);
 		arg_no += 2;
 	}
 #else
@@ -285,7 +285,7 @@
 	/* Align arg_no to the next even number. */
 	arg_no = (arg_no + 1) & 0xe;
 # endif
-	tprintf(format, LONG_LONG(tcp->u_arg[arg_no], tcp->u_arg[arg_no + 1]));
+	*val = LONG_LONG(tcp->u_arg[arg_no], tcp->u_arg[arg_no + 1]);
 	arg_no += 2;
 #endif
 
@@ -293,6 +293,20 @@
 }
 
 /*
+ * Print 64bit argument at position arg_no and
+ * return the index of the next argument.
+ */
+int
+printllval(struct tcb *tcp, const char *format, int arg_no)
+{
+	unsigned long long val = 0;
+
+	arg_no = getllval(tcp, &val, arg_no);
+	tprintf(format, val);
+	return arg_no;
+}
+
+/*
  * Interpret `xlat' as an array of flags
  * print the entries whose bits are on in `flags'
  * return # of flags printed.