From d9bdb9587b4e1cad0c3e9997ea7d2b2a07aed0ad Mon Sep 17 00:00:00 2001 From: "Richard W.M. Jones" Date: Thu, 15 Mar 2012 09:43:04 +0000 Subject: [PATCH] New API: zero_free_space: zero free space in a filesystem. Add an API for doing what virt-sparsify was doing: freeing up free space in a filesystem. The current implementation is simple-minded: we create a file, fill it with zeroes until we run out of space, then delete the file. However the description leaves it open to do a better implementation, eg. using sparsification support that is currently being worked on in ext4 and qemu. The implementation also sends progress notifications, which is an advantage over the old 'dd' method. --- daemon/zero.c | 104 +++++++++++++++++++++++++++++++++ generator/generator_actions.ml | 14 +++++ src/MAX_PROC_NR | 2 +- 3 files changed, 119 insertions(+), 1 deletion(-) diff --git a/daemon/zero.c b/daemon/zero.c index c45088cdb..3076a6027 100644 --- a/daemon/zero.c +++ b/daemon/zero.c @@ -24,6 +24,7 @@ #include #include #include +#include #include "daemon.h" #include "actions.h" @@ -226,3 +227,106 @@ do_is_zero_device (const char *device) return 1; } + +static int +random_name (char *p) +{ + int fd; + unsigned char c; + + fd = open ("/dev/urandom", O_RDONLY|O_CLOEXEC); + if (fd == -1) { + reply_with_perror ("/dev/urandom"); + return -1; + } + + while (*p) { + if (*p == 'X') { + if (read (fd, &c, 1) != 1) { + reply_with_perror ("read: /dev/urandom"); + close (fd); + return -1; + } + *p = "0123456789abcdefghijklmnopqrstuvwxyz"[c % 36]; + } + + p++; + } + + close (fd); + return 0; +} + +/* Current implementation is to create a file of all zeroes, then + * delete it. The description of this function is left open in order + * to allow better implementations in future, including + * sparsification. + */ +int +do_zero_free_space (const char *dir) +{ + size_t len = strlen (dir); + char filename[sysroot_len+len+14]; /* sysroot + dir + "/" + 8.3 + "\0" */ + int fd; + unsigned skip = 0; + struct statvfs statbuf; + fsblkcnt_t bfree_initial; + + /* Choose a randomly named 8.3 file. Because of the random name, + * this won't conflict with existing files, and it should be + * compatible with any filesystem type inc. FAT. + */ + snprintf (filename, sysroot_len+len+14, "%s%s/XXXXXXXX.XXX", sysroot, dir); + if (random_name (&filename[sysroot_len+len]) == -1) + return -1; + + if (verbose) + printf ("random filename: %s\n", filename); + + /* Open file and fill with zeroes until we run out of space. */ + fd = open (filename, O_WRONLY|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0600); + if (fd == -1) { + reply_with_perror ("open: %s", filename); + return -1; + } + + /* To estimate progress in this operation, we're going to track + * free blocks in this filesystem down to zero. + */ + if (fstatvfs (fd, &statbuf) == -1) { + reply_with_perror ("fstatvfs"); + close (fd); + return -1; + } + bfree_initial = statbuf.f_bfree; + + for (;;) { + if (write (fd, zero_buf, sizeof zero_buf) == -1) { + if (errno == ENOSPC) /* expected error */ + break; + reply_with_perror ("write: %s", filename); + close (fd); + unlink (filename); + return -1; + } + + skip++; + if ((skip & 256) == 0 && fstatvfs (fd, &statbuf) == 0) + notify_progress (bfree_initial - statbuf.f_bfree, bfree_initial); + } + + /* Make sure the file is completely written to disk. */ + close (fd); /* expect this to give an error, don't check it */ + + sync_disks (); + + notify_progress (bfree_initial, bfree_initial); + + /* Remove the file. */ + if (unlink (filename) == -1) { + reply_with_perror ("unlink: %s", filename); + return -1; + } + + return 0; +} diff --git a/generator/generator_actions.ml b/generator/generator_actions.ml index 4c6e704b9..4b4e5ffee 100644 --- a/generator/generator_actions.ml +++ b/generator/generator_actions.ml @@ -6748,6 +6748,20 @@ On NTFS filesystems, labels are limited to 128 unicode characters. To read the label on a filesystem, call C."); + ("zero_free_space", (RErr, [Pathname "directory"], []), 311, [Progress], + [InitScratchFS, Always, TestRun ( + [["zero_free_space"; "/"]])], + "zero free space in a filesystem", + "\ +Zero the free space in the filesystem mounted on C. +The filesystem must be mounted read-write. + +The filesystem contents are not affected, but any free space +in the filesystem is freed. + +In future (but not currently) these zeroed blocks will be +\"sparsified\" - that is, given back to the host."); + ] let all_functions = non_daemon_functions @ daemon_functions diff --git a/src/MAX_PROC_NR b/src/MAX_PROC_NR index 54ea97e96..b661fff6c 100644 --- a/src/MAX_PROC_NR +++ b/src/MAX_PROC_NR @@ -1 +1 @@ -310 +311