CHROMIUM: alt-syscall: fallocate emulation.

Some dependent library directly uses the fallocate system call,
which is not supported on some file system, such as ecryptfs.
Because we use ecryptfs, it needs to be emulated using ftruncate,
which is supported on the file system.

BUG=b:29732697
TEST=Ran on x86_64 and arm test devices.

Change-Id: Ifb283005c4998580bca02cdcb2490d0233ce320c
Reviewed-on: https://chromium-review.googlesource.com/360266
Commit-Ready: Hidehiko Abe <hidehiko@chromium.org>
Tested-by: Hidehiko Abe <hidehiko@chromium.org>
Reviewed-by: Kazuhiro Inaba <kinaba@chromium.org>
Reviewed-by: Junichi Uekawa <uekawa@chromium.org>
Reviewed-by: Dylan Reid <dgreid@chromium.org>
(cherry picked from commit 675becfa942005b40cd417da181815e47434774b)
Reviewed-on: https://chromium-review.googlesource.com/360741
(cherry picked from commit 2111d26af549efcff8f01908148309df2cef08af)
Reviewed-on: https://chromium-review.googlesource.com/362532
Trybot-Ready: Hidehiko Abe <hidehiko@chromium.org>
Commit-Queue: Hidehiko Abe <hidehiko@chromium.org>
diff --git a/security/chromiumos/alt-syscall.c b/security/chromiumos/alt-syscall.c
index 38d88ba..0b6ff5f 100644
--- a/security/chromiumos/alt-syscall.c
+++ b/security/chromiumos/alt-syscall.c
@@ -9,11 +9,13 @@
  */
 
 #include <linux/alt-syscall.h>
+#include <linux/fs.h>
 #include <linux/init.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/prctl.h>
 #include <linux/slab.h>
+#include <linux/stat.h>
 #include <linux/syscalls.h>
 
 #include <asm/unistd.h>
@@ -465,6 +467,54 @@
 	return retval;
 }
 
+static int android_fallocate(int fd, int mode, loff_t offset, loff_t len)
+{
+	int retval;
+	struct kstat st;
+
+	retval = sys_fallocate(fd, mode, offset, len);
+	if (retval != -EOPNOTSUPP || mode != 0)
+		return retval;
+
+	/* Emulate fallocate by ftruncate and fstat. */
+	retval = vfs_fstat(fd, &st);
+	if (retval < 0)
+		return -EOPNOTSUPP; /* Do not expose errno from fstat. */
+
+	len += offset;
+	if (len <= st.size) {
+		/*
+		 * When the file size is already larger than requested, do a
+		 * no-op ftruncate by specifying the current file size. In this
+		 * way, this function will return -1 appropriately when |fd| is
+		 * opened for reading.
+		 */
+		len = st.size;
+	}
+
+	return sys_ftruncate(fd, len);
+}
+
+/*
+ * The 64 bit values are passed by using two 32 bit registers. Its order
+ * depends on the endian.
+ */
+#ifdef CONFIG_CPU_BIG_ENDIGAN
+#define PACK64(hi, lo) (((u64)(hi) << 32) | (lo))
+#else
+#define PACK64(lo, hi) (((u64)(hi) << 32) | (lo))
+#endif
+
+static int android_fallocate32(int fd, int mode,
+			       unsigned long offset1, unsigned long offset2,
+			       unsigned long len1, unsigned long len2)
+{
+	return android_fallocate(fd, mode, (loff_t) PACK64(offset1, offset2),
+				 (loff_t) PACK64(len1, len2));
+}
+
+#undef PACK64
+
 static struct syscall_whitelist_entry android_whitelist[] = {
 	SYSCALL_ENTRY(brk),
 	SYSCALL_ENTRY(capget),
@@ -486,7 +536,6 @@
 	SYSCALL_ENTRY(exit),
 	SYSCALL_ENTRY(exit_group),
 	SYSCALL_ENTRY(faccessat),
-	SYSCALL_ENTRY(fallocate),
 	SYSCALL_ENTRY(fchdir),
 	SYSCALL_ENTRY(fchmod),
 	SYSCALL_ENTRY(fchmodat),
@@ -698,6 +747,13 @@
 #endif
 #endif
 
+	/* Inject fallocate. */
+#if defined(CONFIG_X86_64) || defined(CONFIG_ARM64)
+	SYSCALL_ENTRY_ALT(fallocate, android_fallocate),
+#else
+	SYSCALL_ENTRY_ALT(fallocate, android_fallocate32),
+#endif
+
 	/*
 	 * posix_fadvise(2) and sync_file_range(2) have ARM-specific wrappers
 	 * to deal with register alignment.
@@ -824,7 +880,7 @@
 	COMPAT_SYSCALL_ENTRY(exit),
 	COMPAT_SYSCALL_ENTRY(exit_group),
 	COMPAT_SYSCALL_ENTRY(faccessat),
-	COMPAT_SYSCALL_ENTRY(fallocate),
+	COMPAT_SYSCALL_ENTRY_ALT(fallocate, android_fallocate32),
 	COMPAT_SYSCALL_ENTRY(fchdir),
 	COMPAT_SYSCALL_ENTRY(fchmod),
 	COMPAT_SYSCALL_ENTRY(fchmodat),