Merge "toolbox: swap utils"
diff --git a/toolbox/Android.mk b/toolbox/Android.mk
index 565ec2a..f60037d 100644
--- a/toolbox/Android.mk
+++ b/toolbox/Android.mk
@@ -65,7 +65,10 @@
 	runcon \
 	getsebool \
 	setsebool \
-	load_policy
+	load_policy \
+	swapon \
+	swapoff \
+	mkswap
 
 ifneq (,$(filter userdebug eng,$(TARGET_BUILD_VARIANT)))
 TOOLS += r
diff --git a/toolbox/mkswap.c b/toolbox/mkswap.c
new file mode 100644
index 0000000..1710ef6
--- /dev/null
+++ b/toolbox/mkswap.c
@@ -0,0 +1,94 @@
+#include <stdio.h>
+#include <unistd.h>
+#include <asm/page.h>
+#include <sys/swap.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+/* XXX This needs to be obtained from kernel headers. See b/9336527 */
+struct linux_swap_header {
+    char            bootbits[1024]; /* Space for disklabel etc. */
+    uint32_t        version;
+    uint32_t        last_page;
+    uint32_t        nr_badpages;
+    unsigned char   sws_uuid[16];
+    unsigned char   sws_volume[16];
+    uint32_t        padding[117];
+    uint32_t        badpages[1];
+};
+
+#define MAGIC_SWAP_HEADER     "SWAPSPACE2"
+#define MAGIC_SWAP_HEADER_LEN 10
+#define MIN_PAGES             10
+
+int mkswap_main(int argc, char **argv)
+{
+    int err = 0;
+    int fd;
+    ssize_t len;
+    off_t swap_size;
+    int pagesize;
+    struct linux_swap_header sw_hdr;
+
+    if (argc != 2) {
+        fprintf(stderr, "Usage: %s <filename>\n", argv[0]);
+        return -EINVAL;
+    }
+
+    fd = open(argv[1], O_WRONLY);
+    if (fd < 0) {
+        err = errno;
+        fprintf(stderr, "Cannot open %s\n", argv[1]);
+        return err;
+    }
+
+    pagesize = getpagesize();
+    /* Determine the length of the swap file */
+    swap_size = lseek(fd, 0, SEEK_END);
+    if (swap_size < MIN_PAGES * pagesize) {
+        fprintf(stderr, "Swap file needs to be at least %dkB\n",
+            (MIN_PAGES * pagesize) >> 10);
+        err = -ENOSPC;
+        goto err;
+    }
+    if (lseek(fd, 0, SEEK_SET)) {
+        err = errno;
+        fprintf(stderr, "Can't seek to the beginning of the file\n");
+        goto err;
+    }
+
+    memset(&sw_hdr, 0, sizeof(sw_hdr));
+    sw_hdr.version = 1;
+    sw_hdr.last_page = (swap_size / pagesize) - 1;
+
+    len = write(fd, &sw_hdr, sizeof(sw_hdr));
+    if (len != sizeof(sw_hdr)) {
+        err = errno;
+        fprintf(stderr, "Failed to write swap header into %s\n", argv[1]);
+        goto err;
+    }
+
+    /* Write the magic header */
+    if (lseek(fd, pagesize - MAGIC_SWAP_HEADER_LEN, SEEK_SET) < 0) {
+        err = errno;
+        fprintf(stderr, "Failed to seek into %s\n", argv[1]);
+        goto err;
+    }
+
+    len = write(fd, MAGIC_SWAP_HEADER, MAGIC_SWAP_HEADER_LEN);
+    if (len != MAGIC_SWAP_HEADER_LEN) {
+        err = errno;
+        fprintf(stderr, "Failed to write magic swap header into %s\n", argv[1]);
+        goto err;
+    }
+
+    if (fsync(fd) < 0) {
+        err = errno;
+        fprintf(stderr, "Failed to sync %s\n", argv[1]);
+        goto err;
+    }
+err:
+    close(fd);
+    return err;
+}
diff --git a/toolbox/swapoff.c b/toolbox/swapoff.c
new file mode 100644
index 0000000..8f14158
--- /dev/null
+++ b/toolbox/swapoff.c
@@ -0,0 +1,21 @@
+#include <stdio.h>
+#include <unistd.h>
+#include <asm/page.h>
+#include <sys/swap.h>
+
+int swapoff_main(int argc, char **argv)
+{
+    int err = 0;
+
+    if (argc != 2) {
+        fprintf(stderr, "Usage: %s <filename>\n", argv[0]);
+        return -EINVAL;
+    }
+
+    err = swapoff(argv[1]);
+    if (err) {
+        fprintf(stderr, "swapoff failed for %s\n", argv[1]);
+    }
+
+    return err;
+}
diff --git a/toolbox/swapon.c b/toolbox/swapon.c
new file mode 100644
index 0000000..afa6868
--- /dev/null
+++ b/toolbox/swapon.c
@@ -0,0 +1,73 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <asm/page.h>
+#include <sys/swap.h>
+
+/* XXX These need to be obtained from kernel headers. See b/9336527 */
+#define SWAP_FLAG_PREFER        0x8000
+#define SWAP_FLAG_PRIO_MASK     0x7fff
+#define SWAP_FLAG_PRIO_SHIFT    0
+#define SWAP_FLAG_DISCARD       0x10000
+
+void usage(char *name)
+{
+    fprintf(stderr, "Usage: %s [-p prio] <filename>\n"
+        "        prio must be between 0 and %d\n", name, SWAP_FLAG_PRIO_MASK);
+}
+
+int parse_prio(char *prio_str)
+{
+    unsigned long p = strtoul(prio_str, NULL, 10);
+
+    return (p > SWAP_FLAG_PRIO_MASK)? -1 : (int)p;
+}
+
+int swapon_main(int argc, char **argv)
+{
+    int err = 0;
+    int flags = 0;
+    int prio;
+
+    opterr = 0;
+    do {
+        int c = getopt(argc, argv, "hp:");
+        if (c == -1)
+            break;
+
+        switch (c) {
+            case 'p':
+                if (optarg != NULL)
+                    prio = parse_prio(optarg);
+                else
+                    prio = -1;
+
+                if (prio < 0) {
+                    usage(argv[0]);
+                    return -EINVAL;
+                }
+                flags |= SWAP_FLAG_PREFER;
+                flags |= (prio << SWAP_FLAG_PRIO_SHIFT) & SWAP_FLAG_PRIO_MASK;
+                break;
+            case 'h':
+                usage(argv[0]);
+                return 0;
+            case '?':
+                fprintf(stderr, "unknown option: %c\n", optopt);
+                return -EINVAL;
+        }
+    } while (1);
+
+    if (optind != argc - 1) {
+        usage(argv[0]);
+        return -EINVAL;
+    }
+
+    err = swapon(argv[argc - 1], flags);
+    if (err) {
+        fprintf(stderr, "swapon failed for %s\n", argv[argc - 1]);
+    }
+
+    return err;
+}