Change License from GPLv2 only to GPLv2+ ("or any later").
[platform/upstream/cryptsetup.git] / lib / utils_wipe.c
index 523e9e8..858964c 100644 (file)
@@ -2,11 +2,13 @@
  * utils_wipe - wipe a device
  *
  * Copyright (C) 2004-2007, Clemens Fruhwirth <clemens@endorphin.org>
- * Copyright (C) 2011, Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2011-2012, Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2009-2012, Milan Broz
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
- * version 2 as published by the Free Software Foundation.
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
  *
  * This program is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -15,7 +17,7 @@
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  */
 
 #include <stdio.h>
 
 #define MAXIMUM_WIPE_BYTES     1024 * 1024 * 32 /* 32 MiB */
 
-static ssize_t _crypt_wipe_zero(int fd, char *buffer, uint64_t offset, uint64_t size)
+static ssize_t _crypt_wipe_zero(int fd, int bsize, char *buffer,
+                               uint64_t offset, uint64_t size)
 {
        memset(buffer, 0, size);
-       return write_lseek_blockwise(fd, buffer, size, offset);
+       return write_lseek_blockwise(fd, bsize, buffer, size, offset);
+}
+
+static ssize_t _crypt_wipe_random(int fd, int bsize, char *buffer,
+                                 uint64_t offset, uint64_t size)
+{
+       if (crypt_random_get(NULL, buffer, size, CRYPT_RND_NORMAL) < 0)
+               return -EINVAL;
+
+       return write_lseek_blockwise(fd, bsize, buffer, size, offset);
 }
 
 /*
@@ -66,38 +78,45 @@ static void wipeSpecial(char *buffer, size_t buffer_size, unsigned int turn)
        }
 }
 
-static ssize_t _crypt_wipe_disk(int fd, char *buffer, uint64_t offset, uint64_t size)
+static ssize_t _crypt_wipe_disk(int fd, int bsize, char *buffer,
+                               uint64_t offset, uint64_t size)
 {
+       int r;
        unsigned int i;
        ssize_t written;
 
        for(i = 0; i < 39; ++i) {
-               if                (i <  5) crypt_random_get(NULL, buffer, size, CRYPT_RND_NORMAL);
-               else if(i >=  5 && i < 32) wipeSpecial(buffer, size, i - 5);
-               else if(i >= 32 && i < 38) crypt_random_get(NULL, buffer, size, CRYPT_RND_NORMAL);
-               else if(i >= 38 && i < 39) memset(buffer, 0xFF, size);
-
-               written = write_lseek_blockwise(fd, buffer, size, offset);
+               if (i <  5) {
+                       r = crypt_random_get(NULL, buffer, size, CRYPT_RND_NORMAL);
+               } else if(i >=  5 && i < 32) {
+                       wipeSpecial(buffer, size, i - 5);
+                       r = 0;
+               } else if(i >= 32 && i < 38) {
+                       r = crypt_random_get(NULL, buffer, size, CRYPT_RND_NORMAL);
+               } else if(i >= 38 && i < 39) {
+                       memset(buffer, 0xFF, size);
+                       r = 0;
+               }
+               if (r < 0)
+                       return r;
+
+               written = write_lseek_blockwise(fd, bsize, buffer, size, offset);
                if (written < 0 || written != (ssize_t)size)
                        return written;
        }
 
-       return written;
+       /* Rewrite it finally with random */
+       return _crypt_wipe_random(fd, bsize, buffer, offset, size);
 }
 
-static ssize_t _crypt_wipe_random(int fd, char *buffer, uint64_t offset, uint64_t size)
-{
-       crypt_random_get(NULL, buffer, size, CRYPT_RND_NORMAL);
-       return write_lseek_blockwise(fd, buffer, size, offset);
-}
-
-static ssize_t _crypt_wipe_ssd(int fd, char *buffer, uint64_t offset, uint64_t size)
+static ssize_t _crypt_wipe_ssd(int fd, int bsize, char *buffer,
+                              uint64_t offset, uint64_t size)
 {
        // FIXME: for now just rewrite it by random
-       return _crypt_wipe_random(fd, buffer, offset, size);
+       return _crypt_wipe_random(fd, bsize, buffer, offset, size);
 }
 
-int crypt_wipe(const char *device,
+int crypt_wipe(struct device *device,
               uint64_t offset,
               uint64_t size,
               crypt_wipe_type type,
@@ -105,21 +124,22 @@ int crypt_wipe(const char *device,
 {
        struct stat st;
        char *buffer;
-       int devfd, flags, rotational;
+       int devfd, flags, rotational, bsize;
        ssize_t written;
 
        if (!size || size % SECTOR_SIZE || (size > MAXIMUM_WIPE_BYTES)) {
                log_dbg("Unsuported wipe size for device %s: %ld.",
-                       device, (unsigned long)size);
+                       device_path(device), (unsigned long)size);
                return -EINVAL;
        }
 
-       if (stat(device, &st) < 0) {
-               log_dbg("Device %s not found.", device);
+       if (stat(device_path(device), &st) < 0) {
+               log_dbg("Device %s not found.", device_path(device));
                return -EINVAL;
        }
 
-       if (type == CRYPT_WIPE_DISK) {
+       if (type == CRYPT_WIPE_DISK && S_ISBLK(st.st_mode)) {
+               rotational = 0;
                if (!crypt_sysfs_get_rotational(major(st.st_rdev),
                                                minor(st.st_rdev),
                                                &rotational))
@@ -129,34 +149,41 @@ int crypt_wipe(const char *device,
                        type = CRYPT_WIPE_SSD;
        }
 
+       bsize = device_block_size(device);
+       if (bsize <= 0)
+               return -EINVAL;
+
        buffer = malloc(size);
        if (!buffer)
                return -ENOMEM;
 
-       flags = O_WRONLY | O_DIRECT | O_SYNC;
+       flags = O_RDWR | O_DIRECT | O_SYNC;
 
        /* use O_EXCL only for block devices */
        if (exclusive && S_ISBLK(st.st_mode))
                flags |= O_EXCL;
 
-       devfd = open(device, flags);
+       /* coverity[toctou] */
+       devfd = open(device_path(device), flags);
        if (devfd == -1) {
                free(buffer);
-               return errno == EBUSY ? -EBUSY : -EINVAL;
+               return errno ? -errno : -EINVAL;
        }
 
        // FIXME: use fixed block size and loop here
        switch (type) {
                case CRYPT_WIPE_ZERO:
-                       written = _crypt_wipe_zero(devfd, buffer, offset, size);
+                       written = _crypt_wipe_zero(devfd, bsize, buffer, offset, size);
                        break;
                case CRYPT_WIPE_DISK:
-                       written = _crypt_wipe_disk(devfd, buffer, offset, size);
+                       written = _crypt_wipe_disk(devfd, bsize, buffer, offset, size);
+                       break;
                case CRYPT_WIPE_SSD:
-                       written = _crypt_wipe_ssd(devfd, buffer, offset, size);
+                       written = _crypt_wipe_ssd(devfd, bsize, buffer, offset, size);
                        break;
                case CRYPT_WIPE_RANDOM:
-                       written = _crypt_wipe_random(devfd, buffer, offset, size);
+                       written = _crypt_wipe_random(devfd, bsize, buffer, offset, size);
+                       break;
                default:
                        log_dbg("Unsuported wipe type requested: (%d)", type);
                        written = -1;