LCOV - code coverage report
Current view: top level - vhd/lib - vhd-util-resize.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 0.0 % 488 0
Test Date: 2026-06-26 00:00:00 Functions: 0.0 % 15 0
Branches: 0.0 % 251 0

             Branch data     Line data    Source code
       1                 :             : /*
       2                 :             :  * Copyright (c) 2016, Citrix Systems, Inc.
       3                 :             :  *
       4                 :             :  * All rights reserved.
       5                 :             :  *
       6                 :             :  * Redistribution and use in source and binary forms, with or without
       7                 :             :  * modification, are permitted provided that the following conditions are met:
       8                 :             :  * 
       9                 :             :  *  1. Redistributions of source code must retain the above copyright
      10                 :             :  *     notice, this list of conditions and the following disclaimer.
      11                 :             :  *  2. Redistributions in binary form must reproduce the above copyright
      12                 :             :  *     notice, this list of conditions and the following disclaimer in the
      13                 :             :  *     documentation and/or other materials provided with the distribution.
      14                 :             :  *  3. Neither the name of the copyright holder nor the names of its 
      15                 :             :  *     contributors may be used to endorse or promote products derived from 
      16                 :             :  *     this software without specific prior written permission.
      17                 :             :  *
      18                 :             :  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
      19                 :             :  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
      20                 :             :  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
      21                 :             :  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
      22                 :             :  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
      23                 :             :  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
      24                 :             :  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
      25                 :             :  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
      26                 :             :  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
      27                 :             :  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
      28                 :             :  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
      29                 :             :  */
      30                 :             : 
      31                 :             : #ifdef HAVE_CONFIG_H
      32                 :             : #include "config.h"
      33                 :             : #endif
      34                 :             : 
      35                 :             : #include <errno.h>
      36                 :             : #include <fcntl.h>
      37                 :             : #include <stdio.h>
      38                 :             : #include <stdlib.h>
      39                 :             : #include <unistd.h>
      40                 :             : #include <string.h>
      41                 :             : #include <syslog.h>
      42                 :             : #include <inttypes.h>
      43                 :             : #include <sys/mman.h>
      44                 :             : 
      45                 :             : #include "libvhd-journal.h"
      46                 :             : 
      47                 :             : #if 1
      48                 :             : #define DFPRINTF(_f, _a...) fprintf(stdout, _f, ##_a)
      49                 :             : #else
      50                 :             : #define DFPRINTF(_f, _a...) ((void)0)
      51                 :             : #endif
      52                 :             : 
      53                 :             : #define EPRINTF(_f, _a...)                                      \
      54                 :             :         do {                                                    \
      55                 :             :                 syslog(LOG_INFO, "%s: " _f, __func__, ##_a);  \
      56                 :             :                 DFPRINTF(_f, _a);                               \
      57                 :             :         } while (0)
      58                 :             : 
      59                 :             : typedef struct vhd_block {
      60                 :             :         uint32_t block;
      61                 :             :         uint32_t offset;
      62                 :             : } vhd_block_t;
      63                 :             : 
      64                 :             : TEST_FAIL_EXTERN_VARS;
      65                 :             : 
      66                 :             : static inline uint32_t
      67                 :             : secs_to_blocks_down(vhd_context_t *vhd, uint64_t secs)
      68                 :             : {
      69                 :             :         return secs / vhd->spb;
      70                 :             : }
      71                 :             : 
      72                 :             : static uint32_t
      73                 :           0 : secs_to_blocks_up(vhd_context_t *vhd, uint64_t secs)
      74                 :             : {
      75                 :           0 :         uint32_t blocks;
      76                 :             : 
      77                 :           0 :         blocks = secs / vhd->spb;
      78                 :           0 :         if (secs % vhd->spb)
      79                 :           0 :                 blocks++;
      80                 :             : 
      81                 :           0 :         return blocks;
      82                 :             : }
      83                 :             : 
      84                 :             : static int
      85                 :           0 : vhd_fixed_shrink(vhd_journal_t *journal, uint64_t secs)
      86                 :             : {
      87                 :           0 :         int err;
      88                 :           0 :         uint64_t new_eof;
      89                 :           0 :         vhd_context_t *vhd;
      90                 :             : 
      91                 :           0 :         vhd = &journal->vhd;
      92                 :             : 
      93         [ #  # ]:           0 :         new_eof = vhd->footer.curr_size - vhd_sectors_to_bytes(secs);
      94         [ #  # ]:           0 :         if (new_eof <= sizeof(vhd_footer_t))
      95                 :             :                 return -EINVAL;
      96                 :             : 
      97                 :           0 :         err = ftruncate(vhd->fd, new_eof);
      98         [ #  # ]:           0 :         if (err)
      99                 :           0 :                 return errno;
     100                 :             : 
     101                 :           0 :         vhd->footer.curr_size = new_eof;
     102                 :           0 :         return vhd_write_footer(vhd, &vhd->footer);
     103                 :             : }
     104                 :             : 
     105                 :             : static int
     106                 :           0 : vhd_write_zeros(vhd_journal_t *journal, off64_t off, uint64_t size)
     107                 :             : {
     108                 :           0 :         int err;
     109                 :           0 :         char *buf;
     110                 :           0 :         vhd_context_t *vhd;
     111                 :           0 :         uint64_t bytes, map;
     112                 :             : 
     113                 :           0 :         vhd = &journal->vhd;
     114                 :           0 :         map = MIN(size, VHD_BLOCK_SIZE);
     115                 :             : 
     116                 :           0 :         err = vhd_seek(vhd, off, SEEK_SET);
     117         [ #  # ]:           0 :         if (err)
     118                 :             :                 return err;
     119                 :             : 
     120                 :           0 :         buf = mmap(0, map, PROT_READ, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
     121         [ #  # ]:           0 :         if (buf == MAP_FAILED)
     122                 :           0 :                 return -errno;
     123                 :             : 
     124                 :           0 :         do {
     125                 :           0 :                 bytes = MIN(size, map);
     126                 :             : 
     127                 :           0 :                 err = vhd_write(vhd, buf, bytes);
     128         [ #  # ]:           0 :                 if (err)
     129                 :             :                         break;
     130                 :             : 
     131                 :           0 :                 size -= bytes;
     132         [ #  # ]:           0 :         } while (size);
     133                 :             : 
     134                 :           0 :         munmap(buf, map);
     135                 :             : 
     136                 :           0 :         return err;
     137                 :             : }
     138                 :             : 
     139                 :             : static int
     140                 :           0 : vhd_fixed_grow(vhd_journal_t *journal, uint64_t secs)
     141                 :             : {
     142                 :           0 :         int err;
     143                 :           0 :         vhd_context_t *vhd;
     144                 :           0 :         uint64_t size, eof, new_eof;
     145                 :             : 
     146                 :           0 :         size = vhd_sectors_to_bytes(secs);
     147                 :           0 :         vhd  = &journal->vhd;
     148                 :             : 
     149                 :           0 :         err = vhd_seek(vhd, 0, SEEK_END);
     150         [ #  # ]:           0 :         if (err)
     151                 :           0 :                 goto out;
     152                 :             : 
     153                 :           0 :         eof = vhd_position(vhd);
     154         [ #  # ]:           0 :         if (eof == (off64_t)-1) {
     155                 :           0 :                 err = -errno;
     156                 :           0 :                 goto out;
     157                 :             :         }
     158                 :             : 
     159                 :           0 :         err = vhd_write_zeros(journal, eof - sizeof(vhd_footer_t), size);
     160         [ #  # ]:           0 :         if (err)
     161                 :           0 :                 goto out;
     162                 :             : 
     163                 :           0 :         new_eof = eof + size;
     164                 :           0 :         err = vhd_seek(vhd, new_eof, SEEK_SET);
     165         [ #  # ]:           0 :         if (err)
     166                 :           0 :                 goto out;
     167                 :             : 
     168                 :           0 :         vhd->footer.curr_size += size;
     169                 :           0 :         err = vhd_write_footer(vhd, &vhd->footer);
     170                 :           0 :         if (err)
     171                 :             :                 goto out;
     172                 :             : 
     173                 :             :         err = 0;
     174                 :             : 
     175                 :           0 : out:
     176                 :           0 :         return err;
     177                 :             : }
     178                 :             : 
     179                 :             : static int
     180                 :           0 : vhd_fixed_resize(vhd_journal_t *journal, uint64_t size)
     181                 :             : {
     182                 :           0 :         int err;
     183                 :           0 :         vhd_context_t *vhd;
     184                 :           0 :         uint64_t cur_secs, new_secs;
     185                 :             : 
     186                 :           0 :         vhd      = &journal->vhd;
     187                 :           0 :         cur_secs = vhd->footer.curr_size >> VHD_SECTOR_SHIFT;
     188                 :           0 :         new_secs = size << (20 - VHD_SECTOR_SHIFT);
     189                 :             : 
     190         [ #  # ]:           0 :         if (cur_secs == new_secs)
     191                 :             :                 return 0;
     192         [ #  # ]:           0 :         else if (cur_secs > new_secs)
     193                 :           0 :                 err = vhd_fixed_shrink(journal, cur_secs - new_secs);
     194                 :             :         else
     195                 :           0 :                 err = vhd_fixed_grow(journal, new_secs - cur_secs);
     196                 :             : 
     197                 :             :         return err;
     198                 :             : }
     199                 :             : 
     200                 :             : static inline void
     201                 :             : swap(vhd_block_t *list, int a, int b)
     202                 :             : {
     203                 :             :         vhd_block_t tmp;
     204                 :             : 
     205                 :             :         tmp     = list[a];
     206                 :             :         list[a] = list[b];
     207                 :             :         list[b] = tmp;
     208                 :             : }
     209                 :             : 
     210                 :             : #if 0
     211                 :             : static int
     212                 :             : partition(vhd_block_t *list, int left, int right, int pidx)
     213                 :             : {
     214                 :             :         int i, sidx;
     215                 :             :         long long pval;
     216                 :             : 
     217                 :             :         sidx = left;
     218                 :             :         pval = list[pidx].offset;
     219                 :             :         swap(list, pidx, right);
     220                 :             : 
     221                 :             :         for (i = left; i < right; i++)
     222                 :             :                 if (list[i].offset >= pval) {
     223                 :             :                         swap(list, sidx, i);
     224                 :             :                         ++sidx;
     225                 :             :                 }
     226                 :             : 
     227                 :             :         swap(list, right, sidx);
     228                 :             :         return sidx;
     229                 :             : }
     230                 :             : #endif
     231                 :             : 
     232                 :             : #if 0
     233                 :             : static void
     234                 :             : quicksort(vhd_block_t *list, int left, int right)
     235                 :             : {
     236                 :             :         int pidx, new_pidx;
     237                 :             : 
     238                 :             :         if (right < left)
     239                 :             :                 return;
     240                 :             : 
     241                 :             :         pidx     = left;
     242                 :             :         new_pidx = partition(list, left, right, pidx);
     243                 :             :         quicksort(list, left, new_pidx - 1);
     244                 :             :         quicksort(list, new_pidx + 1, right);
     245                 :             : }
     246                 :             : #endif
     247                 :             : 
     248                 :             : static int
     249                 :           0 : vhd_move_block(vhd_journal_t *journal, uint32_t src, off64_t offset)
     250                 :             : {
     251                 :           0 :         int err;
     252                 :           0 :         char *buf;
     253                 :           0 :         size_t size;
     254                 :           0 :         vhd_context_t *vhd;
     255                 :           0 :         off64_t off, src_off;
     256                 :             : 
     257                 :           0 :         buf     = NULL;
     258                 :           0 :         vhd     = &journal->vhd;
     259                 :           0 :         off     = offset;
     260         [ #  # ]:           0 :         size    = vhd_sectors_to_bytes(vhd->bm_secs);
     261                 :           0 :         src_off = vhd->bat.bat[src];
     262                 :             : 
     263         [ #  # ]:           0 :         if (src_off == DD_BLK_UNUSED)
     264                 :           0 :                 return -EINVAL;
     265                 :           0 :         src_off = vhd_sectors_to_bytes(src_off);
     266                 :             : 
     267                 :           0 :         err  = vhd_journal_add_block(journal, src,
     268                 :             :                                      VHD_JOURNAL_DATA | VHD_JOURNAL_METADATA);
     269         [ #  # ]:           0 :         if (err)
     270                 :           0 :                 goto out;
     271                 :             : 
     272                 :           0 :         err  = vhd_read_bitmap(vhd, src, &buf);
     273         [ #  # ]:           0 :         if (err)
     274                 :           0 :                 goto out;
     275                 :             : 
     276                 :           0 :         err  = vhd_seek(vhd, off, SEEK_SET);
     277         [ #  # ]:           0 :         if (err)
     278                 :           0 :                 goto out;
     279                 :             : 
     280                 :           0 :         err  = vhd_write(vhd, buf, size);
     281         [ #  # ]:           0 :         if (err)
     282                 :           0 :                 goto out;
     283                 :             : 
     284                 :           0 :         free(buf);
     285                 :           0 :         buf   = NULL;
     286                 :           0 :         off  += size;
     287                 :           0 :         size  = vhd_sectors_to_bytes(vhd->spb);
     288                 :             : 
     289                 :           0 :         err  = vhd_read_block(vhd, src, &buf);
     290         [ #  # ]:           0 :         if (err)
     291                 :           0 :                 goto out;
     292                 :             : 
     293                 :           0 :         err  = vhd_seek(vhd, off, SEEK_SET);
     294         [ #  # ]:           0 :         if (err)
     295                 :           0 :                 goto out;
     296                 :             : 
     297                 :           0 :         err  = vhd_write(vhd, buf, size);
     298         [ #  # ]:           0 :         if (err)
     299                 :           0 :                 goto out;
     300                 :             : 
     301                 :           0 :         vhd->bat.bat[src] = offset >> VHD_SECTOR_SHIFT;
     302                 :             : 
     303                 :           0 :         err = vhd_write_zeros(journal, src_off,
     304                 :           0 :                               vhd_sectors_to_bytes(vhd->bm_secs + vhd->spb));
     305                 :             : 
     306                 :           0 : out:
     307                 :           0 :         free(buf);
     308                 :           0 :         return err;
     309                 :             : }
     310                 :             : 
     311                 :             : #if 0
     312                 :             : static int
     313                 :             : vhd_clobber_block(vhd_journal_t *journal, uint32_t src, uint32_t dest)
     314                 :             : {
     315                 :             :         int err;
     316                 :             :         off64_t off;
     317                 :             :         vhd_context_t *vhd;
     318                 :             : 
     319                 :             :         vhd = &journal->vhd;
     320                 :             :         off = vhd_sectors_to_bytes(vhd->bat.bat[dest]);
     321                 :             : 
     322                 :             :         err = vhd_journal_add_block(journal, dest,
     323                 :             :                                     VHD_JOURNAL_DATA | VHD_JOURNAL_METADATA);
     324                 :             :         if (err)
     325                 :             :                 return err;
     326                 :             : 
     327                 :             :         err = vhd_move_block(journal, src, off);
     328                 :             :         if (err)
     329                 :             :                 return err;
     330                 :             : 
     331                 :             :         vhd->bat.bat[dest] = DD_BLK_UNUSED;
     332                 :             : 
     333                 :             :         return 0;
     334                 :             : }
     335                 :             : #endif
     336                 :             : 
     337                 :             : #if 0
     338                 :             : /*
     339                 :             :  * remove a list of blocks from the vhd file
     340                 :             :  * if a block to be removed:
     341                 :             :  *   - resides at the end of the file: simply clear its bat entry
     342                 :             :  *   - resides elsewhere: move the last block in the file into its position
     343                 :             :  *                        and update the bat to reflect this
     344                 :             :  */
     345                 :             : static int
     346                 :             : vhd_defrag_shrink(vhd_journal_t *journal,
     347                 :             :                   vhd_block_t *original_free_list, int free_cnt)
     348                 :             : {
     349                 :             :         vhd_context_t *vhd;
     350                 :             :         int i, j, free_idx, err;
     351                 :             :         vhd_block_t *blocks, *free_list;
     352                 :             : 
     353                 :             :         err       = 0;
     354                 :             :         blocks    = NULL;
     355                 :             :         free_list = NULL;
     356                 :             :         vhd       = &journal->vhd;
     357                 :             : 
     358                 :             :         blocks = malloc(vhd->bat.entries * sizeof(vhd_block_t));
     359                 :             :         if (!blocks) {
     360                 :             :                 err = -ENOMEM;
     361                 :             :                 goto out;
     362                 :             :         }
     363                 :             : 
     364                 :             :         free_list = malloc(free_cnt * sizeof(vhd_block_t));
     365                 :             :         if (!free_list) {
     366                 :             :                 err = -ENOMEM;
     367                 :             :                 goto out;
     368                 :             :         }
     369                 :             : 
     370                 :             :         for (i = 0; i < vhd->bat.entries; i++) {
     371                 :             :                 blocks[i].block  = i;
     372                 :             :                 blocks[i].offset = vhd->bat.bat[i];
     373                 :             :         }
     374                 :             : 
     375                 :             :         memcpy(free_list, original_free_list,
     376                 :             :                free_cnt * sizeof(vhd_block_t));
     377                 :             : 
     378                 :             :         /* sort both the to-free list and the bat list
     379                 :             :          * in order of descending file offset */
     380                 :             :         quicksort(free_list, 0, free_cnt - 1);
     381                 :             :         quicksort(blocks, 0, vhd->bat.entries - 1);
     382                 :             : 
     383                 :             :         for (i = 0, free_idx = 0;
     384                 :             :              i < vhd->bat.entries && free_idx < free_cnt; i++) {
     385                 :             :                 vhd_block_t *b = blocks + i;
     386                 :             : 
     387                 :             :                 if (b->offset == DD_BLK_UNUSED)
     388                 :             :                         continue;
     389                 :             : 
     390                 :             :                 for (j = free_idx; j < free_cnt; j++)
     391                 :             :                         if (b->block == free_list[j].block) {
     392                 :             :                                 /* the last block in the file is in the list of
     393                 :             :                                  * blocks to remove; no need to shuffle the
     394                 :             :                                  * data -- just clear the bat entry */
     395                 :             :                                 vhd->bat.bat[free_list[j].block] = DD_BLK_UNUSED;
     396                 :             :                                 free_idx++;
     397                 :             :                                 continue;
     398                 :             :                         }
     399                 :             : 
     400                 :             :                 err = vhd_clobber_block(journal, b->block,
     401                 :             :                                         free_list[free_idx++].block);
     402                 :             :                 if (err)
     403                 :             :                         goto out;
     404                 :             :         }
     405                 :             : 
     406                 :             :         /* clear any bat entries for blocks we did not shuffle */
     407                 :             :         for (i = free_idx; i < free_cnt; i++)
     408                 :             :                 vhd->bat.bat[free_list[i].block] = DD_BLK_UNUSED;
     409                 :             : 
     410                 :             : out:
     411                 :             :         free(blocks);
     412                 :             :         free(free_list);
     413                 :             : 
     414                 :             :         return err;
     415                 :             : }
     416                 :             : #endif
     417                 :             : 
     418                 :             : #if 0
     419                 :             : static int
     420                 :             : vhd_clear_bat_entries(vhd_journal_t *journal, uint32_t entries)
     421                 :             : {
     422                 :             :         int i, err;
     423                 :             :         vhd_context_t *vhd;
     424                 :             :         off64_t orig_map_off, new_map_off;
     425                 :             :         uint32_t orig_entries, new_entries;
     426                 :             : 
     427                 :             :         vhd          = &journal->vhd;
     428                 :             :         orig_entries = vhd->header.max_bat_size;
     429                 :             :         new_entries  = orig_entries - entries;
     430                 :             : 
     431                 :             :         if (vhd_has_batmap(vhd)) {
     432                 :             :                 orig_map_off = vhd_batmap_header_offset(vhd);
     433                 :             :         }
     434                 :             : 
     435                 :             :         /* update header */
     436                 :             :         vhd->header.max_bat_size = new_entries;
     437                 :             :         err = vhd_write_header(vhd, &vhd->header);
     438                 :             :         if (err)
     439                 :             :                 return err;
     440                 :             : 
     441                 :             :         /* update footer */
     442                 :             :         vhd->footer.curr_size =      (uint64_t)new_entries * vhd->header.block_size;
     443                 :             :         vhd->footer.geometry  = vhd_chs(vhd->footer.curr_size);
     444                 :             :         err = vhd_write_footer(vhd, &vhd->footer);
     445                 :             :         if (err)
     446                 :             :                 return err;
     447                 :             : 
     448                 :             :         /* update bat -- we don't reclaim space, just clear entries */
     449                 :             :         for (i = new_entries; i < orig_entries; i++)
     450                 :             :                 vhd->bat.bat[i] = 0;
     451                 :             : 
     452                 :             :         err = vhd_write_bat(vhd, &vhd->bat);
     453                 :             :         if (err)
     454                 :             :                 return err;
     455                 :             : 
     456                 :             :         /* update this after write_bat so the end of the bat is zeored */
     457                 :             :         vhd->bat.entries = new_entries;
     458                 :             : 
     459                 :             :         if (!vhd_has_batmap(vhd))
     460                 :             :                 return 0;
     461                 :             : 
     462                 :             :         /* zero out old batmap header if new header has moved */
     463                 :             :         new_map_off = vhd_batmap_header_offset(vhd);
     464                 :             : 
     465                 :             :         if (orig_map_off != new_map_off) {
     466                 :             :                 size_t size;
     467                 :             : 
     468                 :             :                 size = vhd_bytes_padded(sizeof(struct dd_batmap_hdr));
     469                 :             : 
     470                 :             :                 err = vhd_write_zeros(journal, orig_map_off, size);
     471                 :             :                 if (err)
     472                 :             :                         return err;
     473                 :             :         }
     474                 :             : 
     475                 :             :         /* update batmap -- clear entries for freed blocks */
     476                 :             :         for (i = new_entries; i < orig_entries; i++)
     477                 :             :                 vhd_batmap_clear(vhd, &vhd->batmap, i);
     478                 :             : 
     479                 :             :         err = vhd_write_batmap(vhd, &vhd->batmap);
     480                 :             :         if (err)
     481                 :             :                 return err;
     482                 :             : 
     483                 :             :         return 0;
     484                 :             : }
     485                 :             : #endif
     486                 :             : 
     487                 :             : static int
     488                 :           0 : vhd_dynamic_shrink(vhd_journal_t *journal, uint64_t secs)
     489                 :             : {
     490                 :           0 :         printf("dynamic shrink not fully implemented\n");
     491                 :           0 :         return -ENOSYS;
     492                 :             : #if 0
     493                 :             :         off64_t eof;
     494                 :             :         uint32_t blocks;
     495                 :             :         vhd_context_t *vhd;
     496                 :             :         int i, j, err, free_cnt;
     497                 :             :         struct vhd_block *free_list;
     498                 :             : 
     499                 :             :         eof       = 0;
     500                 :             :         free_cnt  = 0;
     501                 :             :         free_list = NULL;
     502                 :             :         vhd       = &journal->vhd;
     503                 :             : 
     504                 :             :         blocks    = secs_to_blocks_down(vhd, secs);
     505                 :             :         if (blocks == 0)
     506                 :             :                 return 0;
     507                 :             : 
     508                 :             :         if (vhd_has_batmap(vhd)) {
     509                 :             :                 err = vhd_get_batmap(vhd);
     510                 :             :                 if (err)
     511                 :             :                         return err;
     512                 :             :         }
     513                 :             : 
     514                 :             :         free_list = malloc(blocks * sizeof(struct vhd_block));
     515                 :             :         if (!free_list)
     516                 :             :                 return -ENOMEM;
     517                 :             : 
     518                 :             :         for (i = vhd->bat.entries - 1, j = 0; i >= 0 && j < blocks; i--, j++) {
     519                 :             :                 uint32_t blk = vhd->bat.bat[i];
     520                 :             : 
     521                 :             :                 if (blk != DD_BLK_UNUSED) {
     522                 :             :                         free_list[free_cnt].block  = i;
     523                 :             :                         free_list[free_cnt].offset = blk;
     524                 :             :                         free_cnt++;
     525                 :             :                 }
     526                 :             :         }
     527                 :             : 
     528                 :             :         if (free_cnt) {
     529                 :             :                 err = vhd_defrag_shrink(journal, free_list, free_cnt);
     530                 :             :                 if (err)
     531                 :             :                         goto out;
     532                 :             :         }
     533                 :             : 
     534                 :             :         err = vhd_clear_bat_entries(journal, blocks);
     535                 :             :         if (err)
     536                 :             :                 goto out;
     537                 :             : 
     538                 :             :         /* remove data beyond footer */
     539                 :             :         err = vhd_end_of_data(vhd, &eof);
     540                 :             :         if (err)
     541                 :             :                 goto out;
     542                 :             : 
     543                 :             :         err = ftruncate(vhd->fd, eof + sizeof(vhd_footer_t));
     544                 :             :         if (err) {
     545                 :             :                 err = -errno;
     546                 :             :                 goto out;
     547                 :             :         }
     548                 :             : 
     549                 :             :         err = 0;
     550                 :             : 
     551                 :             : out:
     552                 :             :         free(free_list);
     553                 :             :         return err;
     554                 :             : #endif
     555                 :             : }
     556                 :             : 
     557                 :             : static inline void
     558                 :           0 : vhd_first_data_block(vhd_context_t *vhd, vhd_block_t *block)
     559                 :             : {
     560                 :           0 :         int i;
     561                 :           0 :         uint32_t blk;
     562                 :             : 
     563                 :           0 :         memset(block, 0, sizeof(vhd_block_t));
     564                 :             : 
     565         [ #  # ]:           0 :         for (i = 0; i < vhd->bat.entries; i++) {
     566                 :           0 :                 blk = vhd->bat.bat[i];
     567                 :             : 
     568         [ #  # ]:           0 :                 if (blk != DD_BLK_UNUSED) {
     569   [ #  #  #  # ]:           0 :                         if (!block->offset || blk < block->offset) {
     570                 :           0 :                                 block->block  = i;
     571                 :           0 :                                 block->offset = blk;
     572                 :             :                         }
     573                 :             :                 }
     574                 :             :         }
     575                 :           0 : }
     576                 :             : 
     577                 :             : static inline uint32_t
     578                 :           0 : vhd_next_block_offset(vhd_context_t *vhd)
     579                 :             : {
     580                 :           0 :         int i;
     581                 :           0 :         uint32_t blk, end, next;
     582                 :             : 
     583                 :           0 :         next = 0;
     584                 :             : 
     585         [ #  # ]:           0 :         for (i = 0; i < vhd->bat.entries; i++) {
     586                 :           0 :                 blk = vhd->bat.bat[i];
     587                 :             : 
     588         [ #  # ]:           0 :                 if (blk != DD_BLK_UNUSED) {
     589                 :           0 :                         end  = blk + vhd->spb + vhd->bm_secs;
     590                 :           0 :                         next = MAX(next, end);
     591                 :             :                 }
     592                 :             :         }
     593                 :             : 
     594                 :           0 :         return next;
     595                 :             : }
     596                 :             : 
     597                 :             : static inline int
     598                 :           0 : in_range(off64_t off, off64_t start, off64_t size)
     599                 :             : {
     600   [ #  #  #  #  :           0 :         return (start < off && start + size > off);
          #  #  #  #  #  
                #  #  # ]
     601                 :             : }
     602                 :             : 
     603                 :             : #define SKIP_HEADER 0x01
     604                 :             : #define SKIP_BAT    0x02
     605                 :             : #define SKIP_BATMAP 0x04
     606                 :             : #define SKIP_PLOC   0x08
     607                 :             : #define SKIP_DATA   0x10
     608                 :             : 
     609                 :             : static inline int
     610                 :           0 : skip_check(int mode, int type)
     611                 :             : {
     612                 :           0 :         return mode & type;
     613                 :             : }
     614                 :             : 
     615                 :             : static int
     616                 :           0 : vhd_check_for_clobber(vhd_context_t *vhd, off64_t off, int mode)
     617                 :             : {
     618                 :           0 :         int i, n;
     619                 :           0 :         char *msg;
     620                 :           0 :         size_t size;
     621                 :           0 :         vhd_block_t fb;
     622                 :           0 :         vhd_parent_locator_t *loc;
     623                 :             : 
     624                 :           0 :         msg = NULL;
     625                 :             : 
     626         [ #  # ]:           0 :         if (!vhd_type_dynamic(vhd))
     627                 :           0 :                 return 0;
     628                 :             : 
     629         [ #  # ]:           0 :         if (off < VHD_SECTOR_SIZE) {
     630                 :           0 :                 msg = "backup footer";
     631                 :           0 :                 goto fail;
     632                 :             :         }
     633                 :             : 
     634         [ #  # ]:           0 :         if (!skip_check(mode, SKIP_HEADER))
     635                 :           0 :                 if (in_range(off,
     636         [ #  # ]:           0 :                              vhd->footer.data_offset, sizeof(vhd_header_t))) {
     637                 :           0 :                         msg = "header";
     638                 :           0 :                         goto fail;
     639                 :             :                 }
     640                 :             : 
     641         [ #  # ]:           0 :         if (!skip_check(mode, SKIP_BAT))
     642                 :           0 :                 if (in_range(off, vhd->header.table_offset,
     643         [ #  # ]:           0 :                              vhd_bytes_padded(vhd->header.max_bat_size *
     644                 :             :                                               sizeof(uint32_t)))) {
     645                 :           0 :                         msg = "bat";
     646                 :           0 :                         goto fail;
     647                 :             :                 }
     648                 :             : 
     649         [ #  # ]:           0 :         if (!skip_check(mode, SKIP_BATMAP))
     650         [ #  # ]:           0 :                 if (vhd_has_batmap(vhd) &&
     651         [ #  # ]:           0 :                     in_range(off, vhd->batmap.header.batmap_offset,
     652         [ #  # ]:           0 :                              vhd_bytes_padded(vhd->batmap.header.batmap_size))) {
     653                 :           0 :                         msg = "batmap";
     654                 :           0 :                         goto fail;
     655                 :             :                 }
     656                 :             : 
     657         [ #  # ]:           0 :         if (!skip_check(mode, SKIP_PLOC)) {
     658                 :             :                 n = sizeof(vhd->header.loc) / sizeof(vhd_parent_locator_t);
     659         [ #  # ]:           0 :                 for (i = 0; i < n; i++) {
     660                 :           0 :                         loc = vhd->header.loc + i;
     661         [ #  # ]:           0 :                         if (loc->code == PLAT_CODE_NONE)
     662                 :           0 :                                 continue;
     663                 :             : 
     664         [ #  # ]:           0 :                         size = vhd_parent_locator_size(loc);
     665         [ #  # ]:           0 :                         if (in_range(off, loc->data_offset, size)) {
     666                 :           0 :                                 msg = "parent locator";
     667                 :           0 :                                 goto fail;
     668                 :             :                         }
     669                 :             :                 }
     670                 :             :         }
     671                 :             : 
     672         [ #  # ]:           0 :         if (!skip_check(mode, SKIP_DATA)) {
     673                 :           0 :                 vhd_first_data_block(vhd, &fb);
     674         [ #  # ]:           0 :                 if (fb.offset && in_range(off,
     675         [ #  # ]:           0 :                                           vhd_sectors_to_bytes(fb.offset),
     676                 :             :                                           VHD_BLOCK_SIZE)) {
     677                 :           0 :                         msg = "data block";
     678                 :           0 :                         goto fail;
     679                 :             :                 }
     680                 :             :         }
     681                 :             : 
     682                 :             :         return 0;
     683                 :             : 
     684                 :           0 : fail:
     685                 :           0 :         EPRINTF("write to 0x%08"PRIx64" would clobber %s\n", off, msg);
     686                 :             :         return -EINVAL;
     687                 :             : }
     688                 :             : 
     689                 :             : /*
     690                 :             :  * take any metadata after the bat (@eob) and shift it
     691                 :             :  */
     692                 :             : static int
     693                 :           0 : vhd_shift_metadata(vhd_journal_t *journal, off64_t eob,
     694                 :             :                    size_t bat_needed, size_t map_needed)
     695                 :             : {
     696                 :           0 :         int i, n, err;
     697                 :           0 :         vhd_context_t *vhd;
     698                 :           0 :         size_t size_needed;
     699                 :           0 :         void *buf = NULL;
     700                 :           0 :         char **locators;
     701                 :           0 :         vhd_parent_locator_t *loc;
     702                 :             : 
     703                 :           0 :         vhd         = &journal->vhd;
     704                 :           0 :         size_needed = bat_needed + map_needed;
     705                 :             : 
     706                 :           0 :         n = sizeof(vhd->header.loc) / sizeof(vhd_parent_locator_t);
     707                 :             : 
     708                 :           0 :         locators = calloc(n, sizeof(char *));
     709         [ #  # ]:           0 :         if (!locators)
     710                 :             :                 return -ENOMEM;
     711                 :             : 
     712         [ #  # ]:           0 :         for (i = 0; i < n; i++) {
     713                 :           0 :                 size_t size;
     714                 :             : 
     715                 :           0 :                 loc = vhd->header.loc + i;
     716         [ #  # ]:           0 :                 if (loc->code == PLAT_CODE_NONE)
     717                 :           0 :                         continue;
     718                 :             : 
     719         [ #  # ]:           0 :                 if (loc->data_offset < eob)
     720                 :           0 :                         continue;
     721                 :             : 
     722         [ #  # ]:           0 :                 size = vhd_parent_locator_size(loc);
     723         [ #  # ]:           0 :                 err  = posix_memalign(&buf, VHD_SECTOR_SIZE, size);
     724                 :           0 :                 if (err) {
     725                 :           0 :                         err = -err;
     726                 :           0 :                         goto out;
     727                 :             :                 }
     728                 :             : 
     729                 :           0 :                 err  = vhd_seek(vhd, loc->data_offset, SEEK_SET);
     730         [ #  # ]:           0 :                 if (err)
     731                 :           0 :                         goto out;
     732                 :             : 
     733                 :           0 :                 err  = vhd_read(vhd, buf, size);
     734         [ #  # ]:           0 :                 if (err)
     735                 :           0 :                         goto out;
     736                 :             : 
     737                 :           0 :                 locators[i] = buf;
     738                 :             :         }
     739                 :             : 
     740                 :           0 :         buf = NULL;
     741                 :             : 
     742         [ #  # ]:           0 :         for (i = 0; i < n; i++) {
     743                 :           0 :                 off64_t off;
     744                 :           0 :                 size_t size;
     745                 :             : 
     746         [ #  # ]:           0 :                 if (!locators[i])
     747                 :           0 :                         continue;
     748                 :             : 
     749                 :           0 :                 loc  = vhd->header.loc + i;
     750                 :           0 :                 off  = loc->data_offset + size_needed;
     751         [ #  # ]:           0 :                 size = vhd_parent_locator_size(loc);
     752                 :             : 
     753         [ #  # ]:           0 :                 if (vhd_check_for_clobber(vhd, off + size, SKIP_PLOC)) {
     754                 :           0 :                         EPRINTF("%s: shifting locator %d would clobber data\n",
     755                 :             :                                 vhd->file, i);
     756                 :           0 :                         err = -EINVAL;
     757                 :           0 :                         goto out;
     758                 :             :                 }
     759                 :             : 
     760                 :           0 :                 err  = vhd_seek(vhd, off, SEEK_SET);
     761         [ #  # ]:           0 :                 if (err)
     762                 :           0 :                         goto out;
     763                 :             : 
     764                 :           0 :                 err  = vhd_write(vhd, locators[i], size);
     765         [ #  # ]:           0 :                 if (err)
     766                 :           0 :                         goto out;
     767                 :             : 
     768                 :           0 :                 free(locators[i]);
     769                 :           0 :                 locators[i]      = NULL;
     770                 :           0 :                 loc->data_offset = off;
     771                 :             : 
     772                 :             :                 /* write the new header after writing the new bat */
     773                 :             :         }
     774                 :             : 
     775   [ #  #  #  # ]:           0 :         if (vhd_has_batmap(vhd) && vhd->batmap.header.batmap_offset > eob) {
     776                 :           0 :                 vhd->batmap.header.batmap_offset += bat_needed;
     777                 :             : 
     778                 :             :                 /* write the new batmap after writing the new bat */
     779                 :             :         }
     780                 :             : 
     781                 :             :         err = 0;
     782                 :             : 
     783                 :           0 : out:
     784                 :           0 :         free(buf);
     785         [ #  # ]:           0 :         for (i = 0; i < n; i++)
     786                 :           0 :                 free(locators[i]);
     787                 :           0 :         free(locators);
     788                 :             : 
     789                 :           0 :         return err;
     790                 :             : }
     791                 :             : 
     792                 :             : static int
     793                 :           0 : vhd_add_bat_entries(vhd_journal_t *journal, int entries)
     794                 :             : {
     795                 :           0 :         int i, err;
     796                 :           0 :         off64_t off;
     797                 :           0 :         vhd_bat_t new_bat;
     798                 :           0 :         vhd_context_t *vhd;
     799                 :           0 :         uint32_t new_entries;
     800                 :           0 :         vhd_batmap_t new_batmap;
     801                 :           0 :         uint64_t bat_size, new_bat_size, map_size, new_map_size;
     802                 :           0 :         void *bat, *map;
     803                 :             : 
     804                 :           0 :         vhd          = &journal->vhd;
     805                 :           0 :         new_entries  = vhd->header.max_bat_size + entries;
     806                 :             : 
     807                 :           0 :         bat_size     = vhd_bytes_padded(vhd->header.max_bat_size *
     808                 :             :                                         sizeof(uint32_t));
     809                 :           0 :         new_bat_size = vhd_bytes_padded(new_entries * sizeof(uint32_t));
     810                 :             : 
     811                 :           0 :         map_size     = vhd_bytes_padded((vhd->header.max_bat_size + 7) >> 3);
     812                 :           0 :         new_map_size = vhd_bytes_padded((new_entries + 7) >> 3);
     813                 :             : 
     814                 :           0 :         off = vhd->header.table_offset + new_bat_size;
     815         [ #  # ]:           0 :         if (vhd_check_for_clobber(vhd, off, SKIP_BAT | SKIP_BATMAP)) {
     816                 :           0 :                 EPRINTF("%s: writing new bat of 0x%"PRIx64" bytes "
     817                 :             :                         "at 0x%08"PRIx64" would clobber data\n", 
     818                 :             :                         vhd->file, new_bat_size, vhd->header.table_offset);
     819                 :           0 :                 return -EINVAL;
     820                 :             :         }
     821                 :             : 
     822         [ #  # ]:           0 :         if (vhd_has_batmap(vhd)) {
     823                 :           0 :                 off = vhd->batmap.header.batmap_offset + new_map_size;
     824         [ #  # ]:           0 :                 if (vhd_check_for_clobber(vhd, off, 0)) {
     825                 :           0 :                         EPRINTF("%s: writing new batmap of 0x%"PRIx64" bytes"
     826                 :             :                                 " at 0x%08"PRIx64" would clobber data\n", vhd->file,
     827                 :             :                                 new_map_size, vhd->batmap.header.batmap_offset);
     828                 :           0 :                         return -EINVAL;
     829                 :             :                 }
     830                 :             :         }
     831                 :             : 
     832                 :             :         /* update header */
     833                 :           0 :         vhd->header.max_bat_size = new_entries;
     834                 :           0 :         err = vhd_write_header(vhd, &vhd->header);
     835         [ #  # ]:           0 :         if (err)
     836                 :             :                 return err;
     837                 :             : 
     838                 :             :         /* allocate new bat */
     839         [ #  # ]:           0 :         err = posix_memalign(&bat, VHD_SECTOR_SIZE, new_bat_size);
     840                 :           0 :         if (err)
     841                 :           0 :                 return -err;
     842                 :             : 
     843                 :           0 :         new_bat.bat     = bat;
     844                 :           0 :         new_bat.spb     = vhd->bat.spb;
     845                 :           0 :         new_bat.entries = new_entries;
     846                 :           0 :         memcpy(new_bat.bat, vhd->bat.bat, bat_size);
     847         [ #  # ]:           0 :         for (i = vhd->bat.entries; i < new_entries; i++)
     848                 :           0 :                 new_bat.bat[i] = DD_BLK_UNUSED;
     849                 :             : 
     850                 :             :         /* write new bat */
     851                 :           0 :         err = vhd_write_bat(vhd, &new_bat);
     852         [ #  # ]:           0 :         if (err) {
     853                 :           0 :                 free(new_bat.bat);
     854                 :           0 :                 return err;
     855                 :             :         }
     856                 :             : 
     857                 :             :         /* update in-memory bat */
     858                 :           0 :         free(vhd->bat.bat);
     859                 :           0 :         vhd->bat = new_bat;
     860                 :             : 
     861         [ #  # ]:           0 :         if (!vhd_has_batmap(vhd))
     862                 :             :                 return 0;
     863                 :             : 
     864                 :             :         /* allocate new batmap */
     865         [ #  # ]:           0 :         err = posix_memalign(&map, VHD_SECTOR_SIZE, new_map_size);
     866                 :           0 :         if (err)
     867                 :             :                 return err;
     868                 :             : 
     869                 :           0 :         new_batmap.map    = map;
     870                 :           0 :         new_batmap.header = vhd->batmap.header;
     871                 :           0 :         new_batmap.header.batmap_size = secs_round_up_no_zero(new_map_size);
     872                 :           0 :         memcpy(new_batmap.map, vhd->batmap.map, map_size);
     873                 :           0 :         memset(new_batmap.map + map_size, 0, new_map_size - map_size);
     874                 :             : 
     875                 :             :         /* write new batmap */
     876                 :           0 :         err = vhd_write_batmap(vhd, &new_batmap);
     877         [ #  # ]:           0 :         if (err) {
     878                 :           0 :                 free(new_batmap.map);
     879                 :           0 :                 return err;
     880                 :             :         }
     881                 :             : 
     882                 :             :         /* update in-memory batmap */
     883                 :           0 :         free(vhd->batmap.map);
     884                 :           0 :         vhd->batmap = new_batmap;
     885                 :             : 
     886                 :             :         /* update footer */
     887                 :           0 :         vhd->footer.curr_size = (uint64_t)new_entries * vhd->header.block_size;
     888                 :           0 :         vhd->footer.geometry  = vhd_chs(vhd->footer.curr_size);
     889                 :           0 :         vhd->footer.checksum  = vhd_checksum_footer(&vhd->footer);
     890                 :           0 :         err = vhd_write_footer(vhd, &vhd->footer);
     891         [ #  # ]:           0 :         if (err)
     892                 :             :                 return err;
     893                 :             : 
     894                 :             :         return 0;
     895                 :             : }
     896                 :             : 
     897                 :             : static int
     898                 :           0 : vhd_dynamic_grow(vhd_journal_t *journal, uint64_t secs)
     899                 :             : {
     900                 :           0 :         int err;
     901                 :           0 :         off64_t eob, eom;
     902                 :           0 :         vhd_context_t *vhd;
     903                 :           0 :         vhd_block_t first_block;
     904                 :           0 :         uint64_t blocks, size_needed;
     905                 :           0 :         uint64_t bat_needed, bat_size, bat_avail, bat_bytes, bat_secs;
     906                 :           0 :         uint64_t map_needed, map_size, map_avail, map_bytes, map_secs;
     907                 :             : 
     908                 :           0 :         vhd         = &journal->vhd;
     909                 :             : 
     910                 :           0 :         size_needed = 0;
     911                 :           0 :         bat_needed  = 0;
     912                 :           0 :         map_needed  = 0;
     913                 :             : 
     914                 :             :         /* number of vhd blocks to add */
     915         [ #  # ]:           0 :         blocks      = secs_to_blocks_up(vhd, secs);
     916                 :             : 
     917                 :             :         /* size in bytes needed for new bat entries */
     918                 :           0 :         bat_needed  = blocks * sizeof(uint32_t);
     919                 :           0 :         map_needed  = (blocks >> 3) + 1;
     920                 :             : 
     921                 :             :         /* available bytes in current bat */
     922                 :           0 :         bat_bytes   = vhd->header.max_bat_size * sizeof(uint32_t);
     923                 :           0 :         bat_secs    = secs_round_up_no_zero(bat_bytes);
     924                 :           0 :         bat_size    = vhd_sectors_to_bytes(bat_secs);
     925                 :           0 :         bat_avail   = bat_size - bat_bytes;
     926                 :             : 
     927         [ #  # ]:           0 :         if (vhd_has_batmap(vhd)) {
     928                 :             :                 /* avaliable bytes in current batmap */
     929                 :           0 :                 map_bytes   = (vhd->header.max_bat_size + 7) >> 3;
     930                 :           0 :                 map_secs    = vhd->batmap.header.batmap_size;
     931                 :           0 :                 map_size    = vhd_sectors_to_bytes(map_secs);
     932                 :           0 :                 map_avail   = map_size - map_bytes;
     933                 :             :         } else {
     934                 :             :                 map_needed  = 0;
     935                 :             :                 map_avail   = 0;
     936                 :             :         }
     937                 :             : 
     938                 :             :         /* we have enough space already; just extend the bat */
     939         [ #  # ]:           0 :         if (bat_needed <= bat_avail && map_needed <= map_avail)
     940                 :           0 :                 goto add_entries;
     941                 :             : 
     942                 :             :         /* we need to add new sectors to the bat */
     943         [ #  # ]:           0 :         if (bat_needed > bat_avail) {
     944                 :           0 :                 bat_needed -= bat_avail;
     945                 :           0 :                 bat_needed  = vhd_bytes_padded(bat_needed);
     946                 :             :         } else
     947                 :             :                 bat_needed  = 0;
     948                 :             : 
     949                 :             :         /* we need to add new sectors to the batmap */
     950         [ #  # ]:           0 :         if (map_needed > map_avail) {
     951                 :           0 :                 map_needed -= map_avail;
     952                 :           0 :                 map_needed  = vhd_bytes_padded(map_needed);
     953                 :             :         } else
     954                 :             :                 map_needed  = 0;
     955                 :             : 
     956                 :             :         /* how many additional bytes do we need? */
     957                 :           0 :         size_needed = bat_needed + map_needed;
     958                 :             : 
     959                 :             :         /* calculate space between end of headers and beginning of data */
     960                 :           0 :         err = vhd_end_of_headers(vhd, &eom);
     961         [ #  # ]:           0 :         if (err)
     962                 :           0 :                 return err;
     963                 :             : 
     964                 :           0 :         eob = vhd->header.table_offset + vhd_sectors_to_bytes(bat_secs);
     965                 :           0 :         vhd_first_data_block(vhd, &first_block);
     966                 :             : 
     967                 :             :         /* no blocks allocated; just shift post-bat metadata */
     968         [ #  # ]:           0 :         if (!first_block.offset)
     969                 :           0 :                 goto shift_metadata;
     970                 :             : 
     971                 :             :         /* 
     972                 :             :          * not enough space -- 
     973                 :             :          * move vhd data blocks to the end of the file to make room 
     974                 :             :          */
     975                 :           0 :         do {
     976                 :           0 :                 off64_t new_off, bm_size, gap_size;
     977                 :             : 
     978                 :           0 :                 new_off = vhd_sectors_to_bytes(vhd_next_block_offset(vhd));
     979                 :             : 
     980                 :             :                 /* data region of segment should begin on page boundary */
     981                 :           0 :                 bm_size = vhd_sectors_to_bytes(vhd->bm_secs);
     982         [ #  # ]:           0 :                 if ((new_off + bm_size) % 4096) {
     983                 :           0 :                         gap_size = 4096 - ((new_off + bm_size) % 4096);
     984                 :             : 
     985                 :           0 :                         err = vhd_write_zeros(journal, new_off, gap_size);
     986         [ #  # ]:           0 :                         if (err)
     987                 :             :                                 return err;
     988                 :             : 
     989                 :           0 :                         new_off += gap_size;
     990                 :             :                 }
     991                 :             : 
     992                 :           0 :                 err = vhd_move_block(journal, first_block.block, new_off);
     993         [ #  # ]:           0 :                 if (err)
     994                 :             :                         return err;
     995                 :             : 
     996                 :           0 :                 vhd_first_data_block(vhd, &first_block);
     997                 :             : 
     998         [ #  # ]:           0 :         } while (eom + size_needed >= vhd_sectors_to_bytes(first_block.offset));
     999                 :             : 
    1000         [ #  # ]:           0 :         TEST_FAIL_AT(FAIL_RESIZE_DATA_MOVED);
    1001                 :             : 
    1002                 :           0 : shift_metadata:
    1003                 :             :         /* shift any metadata after the bat to make room for new bat sectors */
    1004                 :           0 :         err = vhd_shift_metadata(journal, eob, bat_needed, map_needed);
    1005         [ #  # ]:           0 :         if (err)
    1006                 :             :                 return err;
    1007                 :             : 
    1008         [ #  # ]:           0 :         TEST_FAIL_AT(FAIL_RESIZE_METADATA_MOVED);
    1009                 :             : 
    1010                 :           0 : add_entries:
    1011                 :           0 :         return vhd_add_bat_entries(journal, blocks);
    1012                 :             : }
    1013                 :             : 
    1014                 :             : static int
    1015                 :           0 : vhd_dynamic_resize(vhd_journal_t *journal, uint64_t size)
    1016                 :             : {
    1017                 :           0 :         int err;
    1018                 :           0 :         vhd_context_t *vhd;
    1019                 :           0 :         uint64_t cur_secs, new_secs;
    1020                 :             : 
    1021                 :           0 :         vhd      = &journal->vhd;
    1022                 :           0 :         cur_secs = vhd->footer.curr_size >> VHD_SECTOR_SHIFT;
    1023                 :           0 :         new_secs = size << (20 - VHD_SECTOR_SHIFT);
    1024                 :             : 
    1025         [ #  # ]:           0 :         if (cur_secs == new_secs)
    1026                 :             :                 return 0;
    1027                 :             : 
    1028                 :           0 :         err = vhd_get_header(vhd);
    1029         [ #  # ]:           0 :         if (err)
    1030                 :             :                 return err;
    1031                 :             : 
    1032                 :           0 :         err = vhd_get_bat(vhd);
    1033         [ #  # ]:           0 :         if (err)
    1034                 :             :                 return err;
    1035                 :             : 
    1036         [ #  # ]:           0 :         if (vhd_has_batmap(vhd)) {
    1037                 :           0 :                 err = vhd_get_batmap(vhd);
    1038         [ #  # ]:           0 :                 if (err)
    1039                 :             :                         return err;
    1040                 :             :         }
    1041                 :             : 
    1042         [ #  # ]:           0 :         if (cur_secs > new_secs)
    1043                 :           0 :                 err = vhd_dynamic_shrink(journal, cur_secs - new_secs);
    1044                 :             :         else
    1045                 :           0 :                 err = vhd_dynamic_grow(journal, new_secs - cur_secs);
    1046                 :             : 
    1047                 :             :         return err;
    1048                 :             : }
    1049                 :             : 
    1050                 :             : static int
    1051                 :           0 : vhd_util_resize_check_creator(const char *name)
    1052                 :             : {
    1053                 :           0 :         int err;
    1054                 :           0 :         vhd_context_t vhd;
    1055                 :             : 
    1056                 :           0 :         err = vhd_open(&vhd, name, VHD_OPEN_RDONLY | VHD_OPEN_STRICT);
    1057         [ #  # ]:           0 :         if (err) {
    1058                 :           0 :                 printf("error opening %s: %d\n", name, err);
    1059                 :           0 :                 return err;
    1060                 :             :         }
    1061                 :             : 
    1062         [ #  # ]:           0 :         if (!vhd_creator_tapdisk(&vhd)) {
    1063                 :           0 :                 printf("%s not created by xen; resize not supported\n", name);
    1064                 :             :                 err = -EINVAL;
    1065                 :             :         }
    1066                 :             : 
    1067                 :           0 :         vhd_close(&vhd);
    1068                 :             :         return err;
    1069                 :             : }
    1070                 :             : 
    1071                 :             : static int
    1072                 :           0 : vhd_dynamic_grow_fast(const char *name, uint64_t bytes)
    1073                 :             : {
    1074                 :           0 :         vhd_context_t vhd;
    1075                 :           0 :         uint64_t blks, size;
    1076                 :           0 :         int err;
    1077                 :             : 
    1078                 :           0 :         err = vhd_open(&vhd, name, VHD_OPEN_RDWR);
    1079         [ #  # ]:           0 :         if (err)
    1080                 :           0 :                 return err;
    1081                 :             : 
    1082                 :           0 :         err = vhd_get_bat(&vhd);
    1083         [ #  # ]:           0 :         if (err)
    1084                 :           0 :                 goto done;
    1085                 :             : 
    1086         [ #  # ]:           0 :         if (vhd_has_batmap(&vhd)) {
    1087                 :           0 :                 err = vhd_get_batmap(&vhd);
    1088         [ #  # ]:           0 :                 if (err)
    1089                 :           0 :                         goto done;
    1090                 :             :         }
    1091                 :             : 
    1092                 :           0 :         blks   = (bytes + VHD_BLOCK_SIZE - 1) >> VHD_BLOCK_SHIFT;
    1093                 :           0 :         size   = blks << VHD_BLOCK_SHIFT;
    1094         [ #  # ]:           0 :         if (size < vhd.footer.curr_size) {
    1095                 :           0 :                 printf("%s: size (%"PRIu64") < curr size (%"PRIu64")\n", 
    1096                 :             :                        name, size, vhd.footer.curr_size);
    1097                 :           0 :                 err = -EINVAL;
    1098                 :           0 :                 goto done;
    1099                 :             :         }
    1100         [ #  # ]:           0 :         if (size == vhd.footer.curr_size)
    1101                 :           0 :                 goto done;
    1102                 :             : 
    1103                 :           0 :         err = vhd_set_virt_size(&vhd, size);
    1104                 :             : 
    1105                 :           0 : done:
    1106                 :           0 :         vhd_close(&vhd);
    1107                 :             :         return err;
    1108                 :             : }
    1109                 :             : 
    1110                 :             : int
    1111                 :           0 : vhd_util_resize(int argc, char **argv)
    1112                 :             : {
    1113                 :           0 :         char *name, *jname;
    1114                 :           0 :         uint64_t size;
    1115                 :           0 :         int fast, c, err, jerr;
    1116                 :           0 :         vhd_journal_t journal;
    1117                 :           0 :         vhd_context_t *vhd;
    1118                 :             : 
    1119                 :           0 :         err   = -EINVAL;
    1120                 :           0 :         size  = 0;
    1121                 :           0 :         name  = NULL;
    1122                 :           0 :         jname = NULL;
    1123                 :           0 :         fast  = 0;
    1124                 :             : 
    1125                 :           0 :         optind = 0;
    1126         [ #  # ]:           0 :         while ((c = getopt(argc, argv, "n:s:j:fh")) != -1) {
    1127   [ #  #  #  #  :           0 :                 switch (c) {
                      # ]
    1128                 :           0 :                 case 'n':
    1129                 :           0 :                         name = optarg;
    1130                 :           0 :                         break;
    1131                 :           0 :                 case 'j':
    1132                 :           0 :                         jname = optarg;
    1133                 :           0 :                         break;
    1134                 :             :                 case 'f':
    1135                 :             :                         fast = 1;
    1136                 :             :                         break;
    1137                 :           0 :                 case 's':
    1138                 :           0 :                         err  = 0;
    1139                 :           0 :                         size = strtoull(optarg, NULL, 10);
    1140                 :           0 :                         break;
    1141                 :           0 :                 case 'h':
    1142                 :             :                 default:
    1143                 :           0 :                         goto usage;
    1144                 :             :                 }
    1145                 :             :         }
    1146                 :             : 
    1147   [ #  #  #  #  :           0 :         if (err || !name || (!jname && !fast) || argc != optind)
                   #  # ]
    1148                 :           0 :                 goto usage;
    1149                 :             : 
    1150         [ #  # ]:           0 :         if (jname && fast)
    1151                 :           0 :                 goto usage;
    1152                 :             : 
    1153                 :           0 :         err = vhd_util_resize_check_creator(name);
    1154         [ #  # ]:           0 :         if (err)
    1155                 :           0 :                 return err;
    1156                 :             : 
    1157                 :           0 :         libvhd_set_log_level(1);
    1158                 :             : 
    1159         [ #  # ]:           0 :         if (fast)
    1160                 :           0 :                 return vhd_dynamic_grow_fast(name, size << 20);
    1161                 :             : 
    1162                 :           0 :         err = vhd_journal_create(&journal, name, jname);
    1163         [ #  # ]:           0 :         if (err) {
    1164                 :           0 :                 printf("creating journal failed: %d\n", err);
    1165                 :           0 :                 return err;
    1166                 :             :         }
    1167                 :             : 
    1168                 :           0 :         vhd = &journal.vhd;
    1169                 :             : 
    1170                 :           0 :         err = vhd_get_footer(vhd);
    1171         [ #  # ]:           0 :         if (err)
    1172                 :           0 :                 goto out;
    1173                 :             : 
    1174         [ #  # ]:           0 :         TEST_FAIL_AT(FAIL_RESIZE_BEGIN);
    1175                 :             : 
    1176         [ #  # ]:           0 :         if (vhd_type_dynamic(vhd))
    1177                 :           0 :                 err = vhd_dynamic_resize(&journal, size);
    1178                 :             :         else
    1179                 :           0 :                 err = vhd_fixed_resize(&journal, size);
    1180                 :             : 
    1181         [ #  # ]:           0 :         TEST_FAIL_AT(FAIL_RESIZE_END);
    1182                 :             : 
    1183                 :           0 : out:
    1184         [ #  # ]:           0 :         if (err) {
    1185                 :           0 :                 printf("resize failed: %d\n", err);
    1186                 :           0 :                 jerr = vhd_journal_revert(&journal);
    1187                 :             :         } else
    1188                 :           0 :                 jerr = vhd_journal_commit(&journal);
    1189                 :             : 
    1190         [ #  # ]:           0 :         if (jerr) {
    1191                 :           0 :                 printf("closing journal failed: %d\n", jerr);
    1192                 :           0 :                 vhd_journal_close(&journal);
    1193                 :             :         } else
    1194                 :           0 :                 vhd_journal_remove(&journal);
    1195                 :             : 
    1196         [ #  # ]:           0 :         return (err ? : jerr);
    1197                 :             : 
    1198                 :           0 : usage:
    1199                 :           0 :         printf("options: <-n name> <-s size (in MB)> (<-j journal>|<-f fast>) "
    1200                 :             :                         "[-h help]\n\n"
    1201                 :             :                         "The resize operation can only be performed offline "
    1202                 :             :                         "and must be journaled because resizing the metadata "
    1203                 :             :                         "might require moving data blocks. However, if a "
    1204                 :             :                         "VHD was created with -S <msize> option (during "
    1205                 :             :                         "vhd-util create/snapshot), which preallocates the "
    1206                 :             :                         "metadata for growing the VHD up to size <msize>, then "
    1207                 :             :                         "resizing such a VHD up to <msize> can be performed "
    1208                 :             :                         "online without journaling (-f option).\n");
    1209                 :             :         return -EINVAL;
    1210                 :             : }
        

Generated by: LCOV version 2.0-1