Check device exclusively.
[platform/upstream/cryptsetup.git] / src / crypt_reencrypt.c
index 1ce78ab..642b804 100644 (file)
@@ -17,6 +17,8 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  */
 
+#define PACKAGE_REENC "crypt_reencrypt"
+
 #define _LARGEFILE64_SOURCE
 #define _FILE_OFFSET_BITS 64
 #define SECTOR_SIZE 512
@@ -53,6 +55,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;
@@ -98,7 +101,8 @@ int  MAGIC_L = 6;
 typedef enum {
        MAKE_UNUSABLE,
        MAKE_USABLE,
-       CHECK_UNUSABLE
+       CHECK_UNUSABLE,
+       CHECK_OPEN,
 } header_magic;
 
 __attribute__((format(printf, 5, 6)))
@@ -195,15 +199,27 @@ static int alignment(int fd)
        return alignment;
 }
 
-static int device_magic(struct reenc_ctx *rc, header_magic set_magic)
+static int device_check(struct reenc_ctx *rc, header_magic set_magic)
 {
        char *buf = NULL;
        int r, devfd;
        ssize_t s;
 
-       devfd = open(rc->device, O_RDWR | O_DIRECT);
-       if (devfd == -1)
-               return errno == EBUSY ? -EBUSY : -EINVAL;
+       devfd = open(rc->device, O_RDWR | O_EXCL | O_DIRECT);
+       if (devfd == -1) {
+               if (errno == EBUSY) {
+                       log_err(_("Cannot exclusively open %s, device in use.\n"),
+                               rc->device);
+                       return -EBUSY;
+               }
+               log_err(_("Cannot open device %s\n"), rc->device);
+               return -EINVAL;
+       }
+
+       if (set_magic == CHECK_OPEN) {
+               r = 0;
+               goto out;
+       }
 
        if (posix_memalign((void *)&buf, alignment(devfd), SECTOR_SIZE)) {
                log_err(_("Allocation of aligned memory failed.\n"));
@@ -563,9 +579,9 @@ static int restore_luks_header(struct reenc_ctx *rc)
 
 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)
@@ -580,13 +596,19 @@ static 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 %4.0f 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" :"");
 }
 
@@ -616,16 +638,23 @@ static int copy_data_forward(struct reenc_ctx *rc, int fd_old, int fd_new,
                                (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);
                        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);
        }
@@ -674,16 +703,23 @@ static int copy_data_backward(struct reenc_ctx *rc, int fd_old, int fd_new,
                                (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);
                        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);
        }
@@ -767,7 +803,7 @@ static int initialize_uuid(struct reenc_ctx *rc)
                rc->device_uuid = strdup(crypt_get_uuid(cd));
        else
                /* Reencryption already in progress - magic header? */
-               r = device_magic(rc, CHECK_UNUSABLE);
+               r = device_check(rc, CHECK_UNUSABLE);
 
        crypt_free(cd);
        return r;
@@ -782,13 +818,18 @@ static int init_passphrase1(struct reenc_ctx *rc, struct crypt_device *cd,
 
        retry_count = opt_tries ?: 1;
        while (retry_count--) {
+               set_int_handler();
                r = crypt_get_key(msg, &rc->p[slot].password,
                        &rc->p[slot].passwordLen,
                        0, 0, NULL /*opt_key_file*/,
                        0, 0, cd);
                if (r < 0)
                        return r;
+               if (quit)
+                       return -EAGAIN;
 
+               /* library uses sigint internally, until it is fixed...*/
+               set_int_block(1);
                r = crypt_activate_by_passphrase(cd, NULL, slot_check,
                        rc->p[slot].password, rc->p[slot].passwordLen, 0);
 
@@ -889,6 +930,9 @@ static int initialize_context(struct reenc_ctx *rc, const char *device)
        if (!(rc->device = strndup(device, PATH_MAX)))
                return -ENOMEM;
 
+       if (device_check(rc, CHECK_OPEN) < 0)
+               return -EINVAL;
+
        if (initialize_uuid(rc)) {
                log_err(_("Device %s is not a valid LUKS device.\n"), device);
                return -EINVAL;
@@ -943,7 +987,8 @@ static void destroy_context(struct reenc_ctx *rc)
 
        if ((rc->reencrypt_direction == FORWARD &&
             rc->device_offset == rc->device_size) ||
-            rc->device_offset == 0) {
+           (rc->reencrypt_direction == BACKWARD &&
+            rc->device_offset == 0)) {
                unlink(rc->log_file);
                unlink(rc->header_file_org);
                unlink(rc->header_file_new);
@@ -969,7 +1014,7 @@ static int run_reencrypt(const char *device)
        if (!rc.in_progress) {
                if ((r = initialize_passphrase(&rc, rc.device)) ||
                    (r = backup_luks_headers(&rc)) ||
-                   (r = device_magic(&rc, MAKE_UNUSABLE)))
+                   (r = device_check(&rc, MAKE_UNUSABLE)))
                        goto out;
        } else {
                if ((r = initialize_passphrase(&rc, rc.header_file_new)))
@@ -1012,7 +1057,7 @@ static void _dbg_version_and_cmd(int argc, const char **argv)
 {
        int i;
 
-       log_std("# %s %s processing \"", PACKAGE_NAME, PACKAGE_VERSION);
+       log_std("# %s %s processing \"", PACKAGE_REENC, PACKAGE_VERSION);
        for (i = 0; i < argc; i++) {
                if (i)
                        log_std(" ");
@@ -1044,6 +1089,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") },
@@ -1065,16 +1111,13 @@ int main(int argc, const char **argv)
        poptSetOtherOptionHelp(popt_context,
                               N_("[OPTION...] <device>]"));
 
-       while((r = poptGetNextOpt(popt_context)) > 0) {
-               if (r < 0)
-                       break;
-       }
-
+       while((r = poptGetNextOpt(popt_context)) > 0) ;
        if (r < -1)
                usage(popt_context, EXIT_FAILURE, poptStrerror(r),
                      poptBadOption(popt_context, POPT_BADOPTION_NOALIAS));
+
        if (opt_version_mode) {
-               log_std("%s %s\n", PACKAGE_NAME, PACKAGE_VERSION);
+               log_std("%s %s\n", PACKAGE_REENC, PACKAGE_VERSION);
                poptFreeContext(popt_context);
                exit(EXIT_SUCCESS);
        }
@@ -1115,10 +1158,6 @@ int main(int argc, const char **argv)
        case -ENODEV:   r = 4; break;
        case -ENOMEM:   r = 3; break;
        case -EPERM:    r = 2; break;
-       case -EAGAIN:
-       case -EINVAL:
-       case -ENOENT:
-       case -ENOSYS:
        default:        r = EXIT_FAILURE;
        }
        return r;