LCOV - code coverage report
Current view: top level - import - qcow2-util.c (source / functions) Hit Total Coverage
Test: systemd_full.info Lines: 0 159 0.0 %
Date: 2019-08-23 13:36:53 Functions: 0 7 0.0 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 0 110 0.0 %

           Branch data     Line data    Source code
       1                 :            : /* SPDX-License-Identifier: LGPL-2.1+ */
       2                 :            : 
       3                 :            : #include <zlib.h>
       4                 :            : 
       5                 :            : #include "alloc-util.h"
       6                 :            : #include "btrfs-util.h"
       7                 :            : #include "qcow2-util.h"
       8                 :            : #include "sparse-endian.h"
       9                 :            : #include "util.h"
      10                 :            : 
      11                 :            : #define QCOW2_MAGIC 0x514649fb
      12                 :            : 
      13                 :            : #define QCOW2_COPIED (1ULL << 63)
      14                 :            : #define QCOW2_COMPRESSED (1ULL << 62)
      15                 :            : #define QCOW2_ZERO (1ULL << 0)
      16                 :            : 
      17                 :            : typedef struct _packed_ Header {
      18                 :            :       be32_t magic;
      19                 :            :       be32_t version;
      20                 :            : 
      21                 :            :       be64_t backing_file_offset;
      22                 :            :       be32_t backing_file_size;
      23                 :            : 
      24                 :            :       be32_t cluster_bits;
      25                 :            :       be64_t size;
      26                 :            :       be32_t crypt_method;
      27                 :            : 
      28                 :            :       be32_t l1_size;
      29                 :            :       be64_t l1_table_offset;
      30                 :            : 
      31                 :            :       be64_t refcount_table_offset;
      32                 :            :       be32_t refcount_table_clusters;
      33                 :            : 
      34                 :            :       be32_t nb_snapshots;
      35                 :            :       be64_t snapshots_offset;
      36                 :            : 
      37                 :            :       /* The remainder is only present on QCOW3 */
      38                 :            :       be64_t incompatible_features;
      39                 :            :       be64_t compatible_features;
      40                 :            :       be64_t autoclear_features;
      41                 :            : 
      42                 :            :       be32_t refcount_order;
      43                 :            :       be32_t header_length;
      44                 :            : } Header;
      45                 :            : 
      46                 :            : #define HEADER_MAGIC(header) be32toh((header)->magic)
      47                 :            : #define HEADER_VERSION(header) be32toh((header)->version)
      48                 :            : #define HEADER_CLUSTER_BITS(header) be32toh((header)->cluster_bits)
      49                 :            : #define HEADER_CLUSTER_SIZE(header) (1ULL << HEADER_CLUSTER_BITS(header))
      50                 :            : #define HEADER_L2_BITS(header) (HEADER_CLUSTER_BITS(header) - 3)
      51                 :            : #define HEADER_SIZE(header) be64toh((header)->size)
      52                 :            : #define HEADER_CRYPT_METHOD(header) be32toh((header)->crypt_method)
      53                 :            : #define HEADER_L1_SIZE(header) be32toh((header)->l1_size)
      54                 :            : #define HEADER_L2_SIZE(header) (HEADER_CLUSTER_SIZE(header)/sizeof(uint64_t))
      55                 :            : #define HEADER_L1_TABLE_OFFSET(header) be64toh((header)->l1_table_offset)
      56                 :            : 
      57                 :          0 : static uint32_t HEADER_HEADER_LENGTH(const Header *h) {
      58         [ #  # ]:          0 :         if (HEADER_VERSION(h) < 3)
      59                 :          0 :                 return offsetof(Header, incompatible_features);
      60                 :            : 
      61                 :          0 :         return be32toh(h->header_length);
      62                 :            : }
      63                 :            : 
      64                 :          0 : static int copy_cluster(
      65                 :            :                 int sfd, uint64_t soffset,
      66                 :            :                 int dfd, uint64_t doffset,
      67                 :            :                 uint64_t cluster_size,
      68                 :            :                 void *buffer) {
      69                 :            : 
      70                 :            :         ssize_t l;
      71                 :            :         int r;
      72                 :            : 
      73                 :          0 :         r = btrfs_clone_range(sfd, soffset, dfd, doffset, cluster_size);
      74         [ #  # ]:          0 :         if (r >= 0)
      75                 :          0 :                 return r;
      76                 :            : 
      77                 :          0 :         l = pread(sfd, buffer, cluster_size, soffset);
      78         [ #  # ]:          0 :         if (l < 0)
      79                 :          0 :                 return -errno;
      80         [ #  # ]:          0 :         if ((uint64_t) l != cluster_size)
      81                 :          0 :                 return -EIO;
      82                 :            : 
      83                 :          0 :         l = pwrite(dfd, buffer, cluster_size, doffset);
      84         [ #  # ]:          0 :         if (l < 0)
      85                 :          0 :                 return -errno;
      86         [ #  # ]:          0 :         if ((uint64_t) l != cluster_size)
      87                 :          0 :                 return -EIO;
      88                 :            : 
      89                 :          0 :         return 0;
      90                 :            : }
      91                 :            : 
      92                 :          0 : static int decompress_cluster(
      93                 :            :                 int sfd, uint64_t soffset,
      94                 :            :                 int dfd, uint64_t doffset,
      95                 :            :                 uint64_t compressed_size,
      96                 :            :                 uint64_t cluster_size,
      97                 :            :                 void *buffer1,
      98                 :            :                 void *buffer2) {
      99                 :            : 
     100                 :          0 :         _cleanup_free_ void *large_buffer = NULL;
     101                 :          0 :         z_stream s = {};
     102                 :            :         uint64_t sz;
     103                 :            :         ssize_t l;
     104                 :            :         int r;
     105                 :            : 
     106         [ #  # ]:          0 :         if (compressed_size > cluster_size) {
     107                 :            :                 /* The usual cluster buffer doesn't suffice, let's
     108                 :            :                  * allocate a larger one, temporarily */
     109                 :            : 
     110                 :          0 :                 large_buffer = malloc(compressed_size);
     111         [ #  # ]:          0 :                 if (!large_buffer)
     112                 :          0 :                         return -ENOMEM;
     113                 :            : 
     114                 :          0 :                 buffer1 = large_buffer;
     115                 :            :         }
     116                 :            : 
     117                 :          0 :         l = pread(sfd, buffer1, compressed_size, soffset);
     118         [ #  # ]:          0 :         if (l < 0)
     119                 :          0 :                 return -errno;
     120         [ #  # ]:          0 :         if ((uint64_t) l != compressed_size)
     121                 :          0 :                 return -EIO;
     122                 :            : 
     123                 :          0 :         s.next_in = buffer1;
     124                 :          0 :         s.avail_in = compressed_size;
     125                 :          0 :         s.next_out = buffer2;
     126                 :          0 :         s.avail_out = cluster_size;
     127                 :            : 
     128                 :          0 :         r = inflateInit2(&s, -12);
     129         [ #  # ]:          0 :         if (r != Z_OK)
     130                 :          0 :                 return -EIO;
     131                 :            : 
     132                 :          0 :         r = inflate(&s, Z_FINISH);
     133                 :          0 :         sz = (uint8_t*) s.next_out - (uint8_t*) buffer2;
     134                 :          0 :         inflateEnd(&s);
     135   [ #  #  #  # ]:          0 :         if (r != Z_STREAM_END || sz != cluster_size)
     136                 :          0 :                 return -EIO;
     137                 :            : 
     138                 :          0 :         l = pwrite(dfd, buffer2, cluster_size, doffset);
     139         [ #  # ]:          0 :         if (l < 0)
     140                 :          0 :                 return -errno;
     141         [ #  # ]:          0 :         if ((uint64_t) l != cluster_size)
     142                 :          0 :                 return -EIO;
     143                 :            : 
     144                 :          0 :         return 0;
     145                 :            : }
     146                 :            : 
     147                 :          0 : static int normalize_offset(
     148                 :            :                 const Header *header,
     149                 :            :                 uint64_t p,
     150                 :            :                 uint64_t *ret,
     151                 :            :                 bool *compressed,
     152                 :            :                 uint64_t *compressed_size) {
     153                 :            : 
     154                 :            :         uint64_t q;
     155                 :            : 
     156                 :          0 :         q = be64toh(p);
     157                 :            : 
     158         [ #  # ]:          0 :         if (q & QCOW2_COMPRESSED) {
     159                 :            :                 uint64_t sz, csize_shift, csize_mask;
     160                 :            : 
     161         [ #  # ]:          0 :                 if (!compressed)
     162                 :          0 :                         return -EOPNOTSUPP;
     163                 :            : 
     164                 :          0 :                 csize_shift = 64 - 2 - (HEADER_CLUSTER_BITS(header) - 8);
     165                 :          0 :                 csize_mask = (1ULL << (HEADER_CLUSTER_BITS(header) - 8)) - 1;
     166                 :          0 :                 sz = (((q >> csize_shift) & csize_mask) + 1) * 512 - (q & 511);
     167                 :          0 :                 q &= ((1ULL << csize_shift) - 1);
     168                 :            : 
     169         [ #  # ]:          0 :                 if (compressed_size)
     170                 :          0 :                         *compressed_size = sz;
     171                 :            : 
     172                 :          0 :                 *compressed = true;
     173                 :            : 
     174                 :            :         } else {
     175         [ #  # ]:          0 :                 if (compressed)  {
     176                 :          0 :                         *compressed = false;
     177                 :          0 :                         *compressed_size = 0;
     178                 :            :                 }
     179                 :            : 
     180         [ #  # ]:          0 :                 if (q & QCOW2_ZERO) {
     181                 :            :                         /* We make no distinction between zero blocks and holes */
     182                 :          0 :                         *ret = 0;
     183                 :          0 :                         return 0;
     184                 :            :                 }
     185                 :            : 
     186                 :          0 :                 q &= ~QCOW2_COPIED;
     187                 :            :         }
     188                 :            : 
     189                 :          0 :         *ret = q;
     190                 :          0 :         return q > 0;  /* returns positive if not a hole */
     191                 :            : }
     192                 :            : 
     193                 :          0 : static int verify_header(const Header *header) {
     194         [ #  # ]:          0 :         assert(header);
     195                 :            : 
     196         [ #  # ]:          0 :         if (HEADER_MAGIC(header) != QCOW2_MAGIC)
     197                 :          0 :                 return -EBADMSG;
     198                 :            : 
     199   [ #  #  #  # ]:          0 :         if (!IN_SET(HEADER_VERSION(header), 2, 3))
     200                 :          0 :                 return -EOPNOTSUPP;
     201                 :            : 
     202         [ #  # ]:          0 :         if (HEADER_CRYPT_METHOD(header) != 0)
     203                 :          0 :                 return -EOPNOTSUPP;
     204                 :            : 
     205         [ #  # ]:          0 :         if (HEADER_CLUSTER_BITS(header) < 9) /* 512K */
     206                 :          0 :                 return -EBADMSG;
     207                 :            : 
     208         [ #  # ]:          0 :         if (HEADER_CLUSTER_BITS(header) > 21) /* 2MB */
     209                 :          0 :                 return -EBADMSG;
     210                 :            : 
     211         [ #  # ]:          0 :         if (HEADER_SIZE(header) % HEADER_CLUSTER_SIZE(header) != 0)
     212                 :          0 :                 return -EBADMSG;
     213                 :            : 
     214         [ #  # ]:          0 :         if (HEADER_L1_SIZE(header) > 32*1024*1024) /* 32MB */
     215                 :          0 :                 return -EBADMSG;
     216                 :            : 
     217         [ #  # ]:          0 :         if (HEADER_VERSION(header) == 3) {
     218                 :            : 
     219         [ #  # ]:          0 :                 if (header->incompatible_features != 0)
     220                 :          0 :                         return -EOPNOTSUPP;
     221                 :            : 
     222         [ #  # ]:          0 :                 if (HEADER_HEADER_LENGTH(header) < sizeof(Header))
     223                 :          0 :                         return -EBADMSG;
     224                 :            :         }
     225                 :            : 
     226                 :          0 :         return 0;
     227                 :            : }
     228                 :            : 
     229                 :          0 : int qcow2_convert(int qcow2_fd, int raw_fd) {
     230                 :          0 :         _cleanup_free_ void *buffer1 = NULL, *buffer2 = NULL;
     231                 :          0 :         _cleanup_free_ be64_t *l1_table = NULL, *l2_table = NULL;
     232                 :            :         uint64_t sz, i;
     233                 :            :         Header header;
     234                 :            :         ssize_t l;
     235                 :            :         int r;
     236                 :            : 
     237                 :          0 :         l = pread(qcow2_fd, &header, sizeof(header), 0);
     238         [ #  # ]:          0 :         if (l < 0)
     239                 :          0 :                 return -errno;
     240         [ #  # ]:          0 :         if (l != sizeof(header))
     241                 :          0 :                 return -EIO;
     242                 :            : 
     243                 :          0 :         r = verify_header(&header);
     244         [ #  # ]:          0 :         if (r < 0)
     245                 :          0 :                 return r;
     246                 :            : 
     247                 :          0 :         l1_table = new(be64_t, HEADER_L1_SIZE(&header));
     248         [ #  # ]:          0 :         if (!l1_table)
     249                 :          0 :                 return -ENOMEM;
     250                 :            : 
     251                 :          0 :         l2_table = malloc(HEADER_CLUSTER_SIZE(&header));
     252         [ #  # ]:          0 :         if (!l2_table)
     253                 :          0 :                 return -ENOMEM;
     254                 :            : 
     255                 :          0 :         buffer1 = malloc(HEADER_CLUSTER_SIZE(&header));
     256         [ #  # ]:          0 :         if (!buffer1)
     257                 :          0 :                 return -ENOMEM;
     258                 :            : 
     259                 :          0 :         buffer2 = malloc(HEADER_CLUSTER_SIZE(&header));
     260         [ #  # ]:          0 :         if (!buffer2)
     261                 :          0 :                 return -ENOMEM;
     262                 :            : 
     263                 :            :         /* Empty the file if it exists, we rely on zero bits */
     264         [ #  # ]:          0 :         if (ftruncate(raw_fd, 0) < 0)
     265                 :          0 :                 return -errno;
     266                 :            : 
     267         [ #  # ]:          0 :         if (ftruncate(raw_fd, HEADER_SIZE(&header)) < 0)
     268                 :          0 :                 return -errno;
     269                 :            : 
     270                 :          0 :         sz = sizeof(uint64_t) * HEADER_L1_SIZE(&header);
     271                 :          0 :         l = pread(qcow2_fd, l1_table, sz, HEADER_L1_TABLE_OFFSET(&header));
     272         [ #  # ]:          0 :         if (l < 0)
     273                 :          0 :                 return -errno;
     274         [ #  # ]:          0 :         if ((uint64_t) l != sz)
     275                 :          0 :                 return -EIO;
     276                 :            : 
     277         [ #  # ]:          0 :         for (i = 0; i < HEADER_L1_SIZE(&header); i ++) {
     278                 :            :                 uint64_t l2_begin, j;
     279                 :            : 
     280                 :          0 :                 r = normalize_offset(&header, l1_table[i], &l2_begin, NULL, NULL);
     281         [ #  # ]:          0 :                 if (r < 0)
     282                 :          0 :                         return r;
     283         [ #  # ]:          0 :                 if (r == 0)
     284                 :          0 :                         continue;
     285                 :            : 
     286                 :          0 :                 l = pread(qcow2_fd, l2_table, HEADER_CLUSTER_SIZE(&header), l2_begin);
     287         [ #  # ]:          0 :                 if (l < 0)
     288                 :          0 :                         return -errno;
     289         [ #  # ]:          0 :                 if ((uint64_t) l != HEADER_CLUSTER_SIZE(&header))
     290                 :          0 :                         return -EIO;
     291                 :            : 
     292         [ #  # ]:          0 :                 for (j = 0; j < HEADER_L2_SIZE(&header); j++) {
     293                 :            :                         uint64_t data_begin, p, compressed_size;
     294                 :            :                         bool compressed;
     295                 :            : 
     296                 :          0 :                         p = ((i << HEADER_L2_BITS(&header)) + j) << HEADER_CLUSTER_BITS(&header);
     297                 :            : 
     298                 :          0 :                         r = normalize_offset(&header, l2_table[j], &data_begin, &compressed, &compressed_size);
     299         [ #  # ]:          0 :                         if (r < 0)
     300                 :          0 :                                 return r;
     301         [ #  # ]:          0 :                         if (r == 0)
     302                 :          0 :                                 continue;
     303                 :            : 
     304         [ #  # ]:          0 :                         if (compressed)
     305                 :          0 :                                 r = decompress_cluster(
     306                 :            :                                                 qcow2_fd, data_begin,
     307                 :            :                                                 raw_fd, p,
     308                 :          0 :                                                 compressed_size, HEADER_CLUSTER_SIZE(&header),
     309                 :            :                                                 buffer1, buffer2);
     310                 :            :                         else
     311                 :          0 :                                 r = copy_cluster(
     312                 :            :                                                 qcow2_fd, data_begin,
     313                 :            :                                                 raw_fd, p,
     314                 :          0 :                                                 HEADER_CLUSTER_SIZE(&header), buffer1);
     315         [ #  # ]:          0 :                         if (r < 0)
     316                 :          0 :                                 return r;
     317                 :            :                 }
     318                 :            :         }
     319                 :            : 
     320                 :          0 :         return 0;
     321                 :            : }
     322                 :            : 
     323                 :          0 : int qcow2_detect(int fd) {
     324                 :            :         be32_t id;
     325                 :            :         ssize_t l;
     326                 :            : 
     327                 :          0 :         l = pread(fd, &id, sizeof(id), 0);
     328         [ #  # ]:          0 :         if (l < 0)
     329                 :          0 :                 return -errno;
     330         [ #  # ]:          0 :         if (l != sizeof(id))
     331                 :          0 :                 return -EIO;
     332                 :            : 
     333                 :          0 :         return htobe32(QCOW2_MAGIC) == id;
     334                 :            : }

Generated by: LCOV version 1.14