Use ETA in progress report.
[platform/upstream/cryptsetup.git] / src / crypt_reencrypt.c
index 5731634..3154d59 100644 (file)
@@ -53,6 +53,7 @@ static int opt_random = 0;
 static int opt_urandom = 0;
 static int opt_bsize = 4;
 static int opt_directio = 0;
+static int opt_fsync = 0;
 static int opt_write_log = 0;
 static int opt_tries = 3;
 static int opt_key_slot = CRYPT_ANY_SLOT;
@@ -79,6 +80,7 @@ struct reenc_ctx {
        char crypt_path_org[PATH_MAX];
        char crypt_path_new[PATH_MAX];
        int log_fd;
+       char *log_buf;
 
        struct {
                char *password;
@@ -178,7 +180,7 @@ static void set_int_handler(void)
 }
 
 /* The difference in seconds between two times in "timeval" format. */
-double time_diff(struct timeval start, struct timeval end)
+static double time_diff(struct timeval start, struct timeval end)
 {
        return (end.tv_sec - start.tv_sec)
                + (end.tv_usec - start.tv_usec) / 1E6;
@@ -255,11 +257,13 @@ out:
 static int create_empty_header(const char *new_file, const char *old_file)
 {
        struct stat st;
-       size_t size;
+       ssize_t size;
        int fd, r = 0;
        char *buf;
 
-       if (stat(old_file, &st) == -1 || (st.st_mode & S_IFMT) != S_IFREG)
+       if (stat(old_file, &st) == -1 ||
+                (st.st_mode & S_IFMT) != S_IFREG ||
+                (st.st_size > 16 * 1024 * 1024))
                return -EINVAL;
        size = st.st_size;
 
@@ -285,18 +289,17 @@ static int create_empty_header(const char *new_file, const char *old_file)
 
 static int write_log(struct reenc_ctx *rc)
 {
-       static char buf[SECTOR_SIZE];
        ssize_t r;
 
-       memset(buf, 0, SECTOR_SIZE);
-       snprintf(buf, SECTOR_SIZE, "# LUKS reencryption log, DO NOT EDIT OR DELETE.\n"
+       memset(rc->log_buf, 0, SECTOR_SIZE);
+       snprintf(rc->log_buf, SECTOR_SIZE, "# LUKS reencryption log, DO NOT EDIT OR DELETE.\n"
                "version = %d\nUUID = %s\ndirection = %d\n"
                "offset = %" PRIu64 "\nshift = %" PRIu64 "\n# EOF\n",
                1, rc->device_uuid, rc->reencrypt_direction,
                rc->device_offset, rc->device_shift);
 
        lseek(rc->log_fd, 0, SEEK_SET);
-       r = write(rc->log_fd, buf, SECTOR_SIZE);
+       r = write(rc->log_fd, rc->log_buf, SECTOR_SIZE);
        if (r < 0 || r != SECTOR_SIZE) {
                log_err(_("Cannot write reencryption log file.\n"));
                return -EIO;
@@ -342,18 +345,17 @@ static int parse_line_log(struct reenc_ctx *rc, const char *line)
 
 static int parse_log(struct reenc_ctx *rc)
 {
-       static char buf[SECTOR_SIZE];
        char *start, *end;
        ssize_t s;
 
-       s = read(rc->log_fd, buf, SECTOR_SIZE);
+       s = read(rc->log_fd, rc->log_buf, SECTOR_SIZE);
        if (s == -1) {
                log_err(_("Cannot read reencryption log file.\n"));
                return -EIO;
        }
 
-       buf[SECTOR_SIZE - 1] = '\0';
-       start = buf;
+       rc->log_buf[SECTOR_SIZE - 1] = '\0';
+       start = rc->log_buf;
        do {
                end = strchr(start, '\n');
                if (end) {
@@ -370,19 +372,33 @@ static int parse_log(struct reenc_ctx *rc)
        return 0;
 }
 
+static void close_log(struct reenc_ctx *rc)
+{
+       log_dbg("Closing LUKS reencryption log file %s.", rc->log_file);
+       if (rc->log_fd != -1)
+               close(rc->log_fd);
+       free(rc->log_buf);
+       rc->log_buf = NULL;
+}
+
 static int open_log(struct reenc_ctx *rc)
 {
-       int flags;
+       int flags, create_new;
        struct stat st;
 
-       if(stat(rc->log_file, &st) < 0) {
+       if (!stat(rc->log_file, &st))
+               create_new = 0;
+       else if (errno == ENOENT)
+               create_new = 1;
+       else
+               return -EINVAL;
+
+       if (create_new) {
                log_dbg("Creating LUKS reencryption log file %s.", rc->log_file);
                flags = opt_directio ? O_RDWR|O_CREAT|O_DIRECT : O_RDWR|O_CREAT;
                rc->log_fd = open(rc->log_file, flags, S_IRUSR|S_IWUSR);
                if (rc->log_fd == -1)
                        return -EINVAL;
-               if (write_log(rc) < 0)
-                       return -EIO;
        } else {
                log_dbg("Log file %s exists, restarting.", rc->log_file);
                flags = opt_directio ? O_RDWR|O_DIRECT : O_RDWR;
@@ -392,17 +408,21 @@ static int open_log(struct reenc_ctx *rc)
                rc->in_progress = 1;
        }
 
+       if (posix_memalign((void *)&rc->log_buf, alignment(rc->log_fd), SECTOR_SIZE)) {
+               log_err(_("Allocation of aligned memory failed.\n"));
+               close_log(rc);
+               return -ENOMEM;
+       }
+
+       if (create_new && write_log(rc) < 0) {
+               close_log(rc);
+               return -EIO;
+       }
+
        /* Be sure it is correct format */
        return parse_log(rc);
 }
 
-static void close_log(struct reenc_ctx *rc)
-{
-       log_dbg("Closing LUKS reencryption log file %s.", rc->log_file);
-       if (rc->log_fd != -1)
-               close(rc->log_fd);
-}
-
 static int activate_luks_headers(struct reenc_ctx *rc)
 {
        struct crypt_device *cd = NULL, *cd_new = NULL;
@@ -521,17 +541,17 @@ static void remove_headers(struct reenc_ctx *rc)
        crypt_free(cd);
 }
 
-static int restore_luks_header(struct reenc_ctx *rc, const char *backup)
+static int restore_luks_header(struct reenc_ctx *rc)
 {
        struct crypt_device *cd = NULL;
        int r;
 
-       log_dbg("Restoring header for %s from %s.", rc->device, backup);
+       log_dbg("Restoring header for %s from %s.", rc->device, rc->header_file_new);
 
        r = crypt_init(&cd, rc->device);
        if (r == 0) {
                crypt_set_confirm_callback(cd, NULL, NULL);
-               r = crypt_header_restore(cd, CRYPT_LUKS1, backup);
+               r = crypt_header_restore(cd, CRYPT_LUKS1, rc->header_file_new);
        }
 
        crypt_free(cd);
@@ -542,11 +562,11 @@ static int restore_luks_header(struct reenc_ctx *rc, const char *backup)
        return r;
 }
 
-void print_progress(struct reenc_ctx *rc, uint64_t bytes, int final)
+static void print_progress(struct reenc_ctx *rc, uint64_t bytes, int final)
 {
-       uint64_t mbytes = (bytes - rc->restart_bytes) / 1024 / 1024;
+       unsigned long long mbytes, eta;
        struct timeval now_time;
-       double tdiff;
+       double tdiff, mib;
 
        gettimeofday(&now_time, NULL);
        if (!final && time_diff(rc->end_time, now_time) < 0.5)
@@ -561,13 +581,19 @@ void print_progress(struct reenc_ctx *rc, uint64_t bytes, int final)
        if (!tdiff)
                return;
 
+       mbytes = (bytes - rc->restart_bytes) / 1024 / 1024;
+       mib = (double)(mbytes) / tdiff;
+       if (!mib)
+               return;
+
+       eta = (unsigned long long)(rc->device_size / 1024 / 1024 / mib - tdiff);
+
        /* vt100 code clear line */
        log_err("\33[2K\r");
-       log_err(_("Progress: %5.1f%%, time elapsed %3.1f seconds, "
+       log_err(_("Progress: %5.1f%%, ETA %02llu:%02llu, "
                "%4llu MiB written, speed %5.1f MiB/s%s"),
                (double)bytes / rc->device_size * 100,
-               time_diff(rc->start_time, rc->end_time),
-               (unsigned long long)mbytes, (double)(mbytes) / tdiff,
+               eta / 60, eta % 60, mbytes, mib,
                final ? "\n" :"");
 }
 
@@ -591,19 +617,29 @@ static int copy_data_forward(struct reenc_ctx *rc, int fd_old, int fd_new,
 
        while (!quit && rc->device_offset < rc->device_size) {
                s1 = read(fd_old, buf, block_size);
-               if (s1 < 0 || (s1 != block_size && (rc->device_offset + s1) != rc->device_size)) {
-                       log_dbg("Read error, expecting %d, got %d.", (int)block_size, (int)s1);
+               if (s1 < 0 || ((size_t)s1 != block_size &&
+                   (rc->device_offset + s1) != rc->device_size)) {
+                       log_dbg("Read error, expecting %d, got %d.",
+                               (int)block_size, (int)s1);
                        return -EIO;
                }
+
                s2 = write(fd_new, buf, s1);
                if (s2 < 0) {
-                       log_dbg("Write error, expecting %d, got %d.", (int)block_size, (int)s2);
+                       log_dbg("Write error, expecting %d, got %d.",
+                               (int)block_size, (int)s2);
                        return -EIO;
                }
+
                rc->device_offset += s1;
                if (opt_write_log && write_log(rc) < 0)
                        return -EIO;
 
+               if (opt_fsync && fsync(fd_new) < 0) {
+                       log_dbg("Write error, fsync.");
+                       return -EIO;
+               }
+
                *bytes += (uint64_t)s2;
                print_progress(rc, *bytes, 0);
        }
@@ -648,18 +684,27 @@ static int copy_data_backward(struct reenc_ctx *rc, int fd_old, int fd_new,
 
                s1 = read(fd_old, buf, working_block);
                if (s1 < 0 || (s1 != working_block)) {
-                       log_dbg("Read error, expecting %d, got %d.", (int)block_size, (int)s1);
+                       log_dbg("Read error, expecting %d, got %d.",
+                               (int)block_size, (int)s1);
                        return -EIO;
                }
+
                s2 = write(fd_new, buf, working_block);
                if (s2 < 0) {
-                       log_dbg("Write error, expecting %d, got %d.", (int)block_size, (int)s2);
+                       log_dbg("Write error, expecting %d, got %d.",
+                               (int)block_size, (int)s2);
                        return -EIO;
                }
+
                rc->device_offset -= s1;
                if (opt_write_log && write_log(rc) < 0)
                        return -EIO;
 
+               if (opt_fsync && fsync(fd_new) < 0) {
+                       log_dbg("Write error, fsync.");
+                       return -EIO;
+               }
+
                *bytes += (uint64_t)s2;
                print_progress(rc, *bytes, 0);
        }
@@ -846,7 +891,7 @@ static int initialize_passphrase(struct reenc_ctx *rc, const char *device)
                if (ki != CRYPT_SLOT_ACTIVE && ki != CRYPT_SLOT_ACTIVE_LAST)
                        continue;
 
-               snprintf(msg, sizeof(msg), _("Enter LUKS passphrase for key slot %u): "), i);
+               snprintf(msg, sizeof(msg), _("Enter LUKS passphrase for key slot %u: "), i);
                r = init_passphrase1(rc, cd, msg, i);
                if (r < 0)
                        break;
@@ -932,7 +977,7 @@ static void destroy_context(struct reenc_ctx *rc)
        free(rc->device_uuid);
 }
 
-int run_reencrypt(const char *device)
+static int run_reencrypt(const char *device)
 {
        int r = -EINVAL;
        struct reenc_ctx rc = {};
@@ -958,7 +1003,7 @@ int run_reencrypt(const char *device)
        if ((r = copy_data(&rc)))
                goto out;
 
-       r = restore_luks_header(&rc, rc.header_file_new);
+       r = restore_luks_header(&rc);
 out:
        destroy_context(&rc);
        return r;
@@ -1020,6 +1065,7 @@ int main(int argc, const char **argv)
                { "use-random",        '\0', POPT_ARG_NONE, &opt_random,                0, N_("Use /dev/random for generating volume key."), NULL },
                { "use-urandom",       '\0', POPT_ARG_NONE, &opt_urandom,               0, N_("Use /dev/urandom for generating volume key."), NULL },
                { "use-directio",      '\0', POPT_ARG_NONE, &opt_directio,              0, N_("Use direct-io when accesing devices."), NULL },
+               { "use-fsync",         '\0', POPT_ARG_NONE, &opt_fsync,                 0, N_("Use fsync after each block."), NULL },
                { "write-log",         '\0', POPT_ARG_NONE, &opt_write_log,             0, N_("Update log file after every block."), NULL },
                { "key-slot",          'S',  POPT_ARG_INT, &opt_key_slot,               0, N_("Use only this slot (others will be disabled)."), NULL },
                { "keyfile-offset",   '\0',  POPT_ARG_LONG, &opt_keyfile_offset,        0, N_("Number of bytes to skip in keyfile"), N_("bytes") },