LCOV - code coverage report
Current view: top level - vhd/lib - vhd-util-resize.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 0 364 0.0 %
Date: 2025-02-07 10:27:58 Functions: 0 17 0.0 %
Branches: 0 249 0.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                 :            :         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                 :            :         int err;
      88                 :            :         uint64_t new_eof;
      89                 :            :         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                 :            :         int err;
     109                 :            :         char *buf;
     110                 :            :         vhd_context_t *vhd;
     111                 :            :         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                 :            :         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                 :            :         int err;
     143                 :            :         vhd_context_t *vhd;
     144                 :            :         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                 :            :                 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                 :            :                 goto out;
     162                 :            : 
     163                 :          0 :         new_eof = eof + size;
     164                 :          0 :         err = vhd_seek(vhd, new_eof, SEEK_SET);
     165         [ #  # ]:          0 :         if (err)
     166                 :            :                 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                 :          0 :         err = 0;
     174                 :            : 
     175                 :            : out:
     176                 :          0 :         return err;
     177                 :            : }
     178                 :            : 
     179                 :            : static int
     180                 :          0 : vhd_fixed_resize(vhd_journal_t *journal, uint64_t size)
     181                 :            : {
     182                 :            :         int err;
     183                 :            :         vhd_context_t *vhd;
     184                 :            :         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                 :          0 :         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                 :            :         int err;
     252                 :            :         char *buf;
     253                 :            :         size_t size;
     254                 :            :         vhd_context_t *vhd;
     255                 :            :         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                 :            :                 goto out;
     271                 :            : 
     272                 :          0 :         err  = vhd_read_bitmap(vhd, src, &buf);
     273         [ #  # ]:          0 :         if (err)
     274                 :            :                 goto out;
     275                 :            : 
     276                 :          0 :         err  = vhd_seek(vhd, off, SEEK_SET);
     277         [ #  # ]:          0 :         if (err)
     278                 :            :                 goto out;
     279                 :            : 
     280                 :          0 :         err  = vhd_write(vhd, buf, size);
     281         [ #  # ]:          0 :         if (err)
     282                 :            :                 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                 :            :                 goto out;
     292                 :            : 
     293                 :          0 :         err  = vhd_seek(vhd, off, SEEK_SET);
     294         [ #  # ]:          0 :         if (err)
     295                 :            :                 goto out;
     296                 :            : 
     297                 :          0 :         err  = vhd_write(vhd, buf, size);
     298         [ #  # ]:          0 :         if (err)
     299                 :            :                 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                 :            : 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                 :            :                 err = vhd_batmap_header_offset(vhd, &orig_map_off);
     433                 :            :                 if (err)
     434                 :            :                         return err;
     435                 :            :         }
     436                 :            : 
     437                 :            :         /* update header */
     438                 :            :         vhd->header.max_bat_size = new_entries;
     439                 :            :         err = vhd_write_header(vhd, &vhd->header);
     440                 :            :         if (err)
     441                 :            :                 return err;
     442                 :            : 
     443                 :            :         /* update footer */
     444                 :            :         vhd->footer.curr_size =      (uint64_t)new_entries * vhd->header.block_size;
     445                 :            :         vhd->footer.geometry  = vhd_chs(vhd->footer.curr_size);
     446                 :            :         err = vhd_write_footer(vhd, &vhd->footer);
     447                 :            :         if (err)
     448                 :            :                 return err;
     449                 :            : 
     450                 :            :         /* update bat -- we don't reclaim space, just clear entries */
     451                 :            :         for (i = new_entries; i < orig_entries; i++)
     452                 :            :                 vhd->bat.bat[i] = 0;
     453                 :            : 
     454                 :            :         err = vhd_write_bat(vhd, &vhd->bat);
     455                 :            :         if (err)
     456                 :            :                 return err;
     457                 :            : 
     458                 :            :         /* update this after write_bat so the end of the bat is zeored */
     459                 :            :         vhd->bat.entries = new_entries;
     460                 :            : 
     461                 :            :         if (!vhd_has_batmap(vhd))
     462                 :            :                 return 0;
     463                 :            : 
     464                 :            :         /* zero out old batmap header if new header has moved */
     465                 :            :         err = vhd_batmap_header_offset(vhd, &new_map_off);
     466                 :            :         if (err)
     467                 :            :                 return err;
     468                 :            : 
     469                 :            :         if (orig_map_off != new_map_off) {
     470                 :            :                 size_t size;
     471                 :            : 
     472                 :            :                 size = vhd_bytes_padded(sizeof(struct dd_batmap_hdr));
     473                 :            : 
     474                 :            :                 err = vhd_write_zeros(journal, orig_map_off, size);
     475                 :            :                 if (err)
     476                 :            :                         return err;
     477                 :            :         }
     478                 :            : 
     479                 :            :         /* update batmap -- clear entries for freed blocks */
     480                 :            :         for (i = new_entries; i < orig_entries; i++)
     481                 :            :                 vhd_batmap_clear(vhd, &vhd->batmap, i);
     482                 :            : 
     483                 :            :         err = vhd_write_batmap(vhd, &vhd->batmap);
     484                 :            :         if (err)
     485                 :            :                 return err;
     486                 :            : 
     487                 :            :         return 0;
     488                 :            : }
     489                 :            : #endif
     490                 :            : 
     491                 :            : static int
     492                 :          0 : vhd_dynamic_shrink(vhd_journal_t *journal, uint64_t secs)
     493                 :            : {
     494                 :            :         printf("dynamic shrink not fully implemented\n");
     495                 :          0 :         return -ENOSYS;
     496                 :            : #if 0
     497                 :            :         off64_t eof;
     498                 :            :         uint32_t blocks;
     499                 :            :         vhd_context_t *vhd;
     500                 :            :         int i, j, err, free_cnt;
     501                 :            :         struct vhd_block *free_list;
     502                 :            : 
     503                 :            :         eof       = 0;
     504                 :            :         free_cnt  = 0;
     505                 :            :         free_list = NULL;
     506                 :            :         vhd       = &journal->vhd;
     507                 :            : 
     508                 :            :         blocks    = secs_to_blocks_down(vhd, secs);
     509                 :            :         if (blocks == 0)
     510                 :            :                 return 0;
     511                 :            : 
     512                 :            :         if (vhd_has_batmap(vhd)) {
     513                 :            :                 err = vhd_get_batmap(vhd);
     514                 :            :                 if (err)
     515                 :            :                         return err;
     516                 :            :         }
     517                 :            : 
     518                 :            :         free_list = malloc(blocks * sizeof(struct vhd_block));
     519                 :            :         if (!free_list)
     520                 :            :                 return -ENOMEM;
     521                 :            : 
     522                 :            :         for (i = vhd->bat.entries - 1, j = 0; i >= 0 && j < blocks; i--, j++) {
     523                 :            :                 uint32_t blk = vhd->bat.bat[i];
     524                 :            : 
     525                 :            :                 if (blk != DD_BLK_UNUSED) {
     526                 :            :                         free_list[free_cnt].block  = i;
     527                 :            :                         free_list[free_cnt].offset = blk;
     528                 :            :                         free_cnt++;
     529                 :            :                 }
     530                 :            :         }
     531                 :            : 
     532                 :            :         if (free_cnt) {
     533                 :            :                 err = vhd_defrag_shrink(journal, free_list, free_cnt);
     534                 :            :                 if (err)
     535                 :            :                         goto out;
     536                 :            :         }
     537                 :            : 
     538                 :            :         err = vhd_clear_bat_entries(journal, blocks);
     539                 :            :         if (err)
     540                 :            :                 goto out;
     541                 :            : 
     542                 :            :         /* remove data beyond footer */
     543                 :            :         err = vhd_end_of_data(vhd, &eof);
     544                 :            :         if (err)
     545                 :            :                 goto out;
     546                 :            : 
     547                 :            :         err = ftruncate(vhd->fd, eof + sizeof(vhd_footer_t));
     548                 :            :         if (err) {
     549                 :            :                 err = -errno;
     550                 :            :                 goto out;
     551                 :            :         }
     552                 :            : 
     553                 :            :         err = 0;
     554                 :            : 
     555                 :            : out:
     556                 :            :         free(free_list);
     557                 :            :         return err;
     558                 :            : #endif
     559                 :            : }
     560                 :            : 
     561                 :            : static inline void
     562                 :          0 : vhd_first_data_block(vhd_context_t *vhd, vhd_block_t *block)
     563                 :            : {
     564                 :            :         int i;
     565                 :            :         uint32_t blk;
     566                 :            : 
     567                 :            :         memset(block, 0, sizeof(vhd_block_t));
     568                 :            : 
     569         [ #  # ]:          0 :         for (i = 0; i < vhd->bat.entries; i++) {
     570                 :          0 :                 blk = vhd->bat.bat[i];
     571                 :            : 
     572         [ #  # ]:          0 :                 if (blk != DD_BLK_UNUSED) {
     573 [ #  # ][ #  # ]:          0 :                         if (!block->offset || blk < block->offset) {
     574                 :          0 :                                 block->block  = i;
     575                 :          0 :                                 block->offset = blk;
     576                 :            :                         }
     577                 :            :                 }
     578                 :            :         }
     579                 :          0 : }
     580                 :            : 
     581                 :            : static inline uint32_t
     582                 :            : vhd_next_block_offset(vhd_context_t *vhd)
     583                 :            : {
     584                 :            :         int i;
     585                 :            :         uint32_t blk, end, next;
     586                 :            : 
     587                 :          0 :         next = 0;
     588                 :            : 
     589         [ #  # ]:          0 :         for (i = 0; i < vhd->bat.entries; i++) {
     590                 :          0 :                 blk = vhd->bat.bat[i];
     591                 :            : 
     592         [ #  # ]:          0 :                 if (blk != DD_BLK_UNUSED) {
     593                 :          0 :                         end  = blk + vhd->spb + vhd->bm_secs;
     594                 :          0 :                         next = MAX(next, end);
     595                 :            :                 }
     596                 :            :         }
     597                 :            : 
     598                 :            :         return next;
     599                 :            : }
     600                 :            : 
     601                 :            : static inline int
     602                 :            : in_range(off64_t off, off64_t start, off64_t size)
     603                 :            : {
     604 [ #  # ][ #  # ]:          0 :         return (start < off && start + size > off);
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
     605                 :            : }
     606                 :            : 
     607                 :            : #define SKIP_HEADER 0x01
     608                 :            : #define SKIP_BAT    0x02
     609                 :            : #define SKIP_BATMAP 0x04
     610                 :            : #define SKIP_PLOC   0x08
     611                 :            : #define SKIP_DATA   0x10
     612                 :            : 
     613                 :            : static inline int
     614                 :            : skip_check(int mode, int type)
     615                 :            : {
     616                 :          0 :         return mode & type;
     617                 :            : }
     618                 :            : 
     619                 :            : static int
     620                 :          0 : vhd_check_for_clobber(vhd_context_t *vhd, off64_t off, int mode)
     621                 :            : {
     622                 :            :         int i, n;
     623                 :            :         char *msg;
     624                 :            :         size_t size;
     625                 :            :         vhd_block_t fb;
     626                 :          0 :         vhd_parent_locator_t *loc;
     627                 :            : 
     628                 :          0 :         msg = NULL;
     629                 :            : 
     630         [ #  # ]:          0 :         if (!vhd_type_dynamic(vhd))
     631                 :          0 :                 return 0;
     632                 :            : 
     633         [ #  # ]:          0 :         if (off < VHD_SECTOR_SIZE) {
     634                 :            :                 msg = "backup footer";
     635                 :            :                 goto fail;
     636                 :            :         }
     637                 :            : 
     638         [ #  # ]:          0 :         if (!skip_check(mode, SKIP_HEADER))
     639         [ #  # ]:          0 :                 if (in_range(off,
     640                 :          0 :                              vhd->footer.data_offset, sizeof(vhd_header_t))) {
     641                 :            :                         msg = "header";
     642                 :            :                         goto fail;
     643                 :            :                 }
     644                 :            : 
     645         [ #  # ]:          0 :         if (!skip_check(mode, SKIP_BAT))
     646         [ #  # ]:          0 :                 if (in_range(off, vhd->header.table_offset,
     647                 :          0 :                              vhd_bytes_padded(vhd->header.max_bat_size *
     648                 :            :                                               sizeof(uint32_t)))) {
     649                 :            :                         msg = "bat";
     650                 :            :                         goto fail;
     651                 :            :                 }
     652                 :            : 
     653         [ #  # ]:          0 :         if (!skip_check(mode, SKIP_BATMAP))
     654 [ #  # ][ #  # ]:          0 :                 if (vhd_has_batmap(vhd) &&
     655                 :          0 :                     in_range(off, vhd->batmap.header.batmap_offset,
     656                 :          0 :                              vhd_bytes_padded(vhd->batmap.header.batmap_size))) {
     657                 :            :                         msg = "batmap";
     658                 :            :                         goto fail;
     659                 :            :                 }
     660                 :            : 
     661         [ #  # ]:          0 :         if (!skip_check(mode, SKIP_PLOC)) {
     662                 :            :                 n = sizeof(vhd->header.loc) / sizeof(vhd_parent_locator_t);
     663         [ #  # ]:          0 :                 for (i = 0; i < n; i++) {
     664                 :          0 :                         loc = vhd->header.loc + i;
     665         [ #  # ]:          0 :                         if (loc->code == PLAT_CODE_NONE)
     666                 :          0 :                                 continue;
     667                 :            : 
     668                 :          0 :                         size = vhd_parent_locator_size(loc);
     669         [ #  # ]:          0 :                         if (in_range(off, loc->data_offset, size)) {
     670                 :            :                                 msg = "parent locator";
     671                 :            :                                 goto fail;
     672                 :            :                         }
     673                 :            :                 }
     674                 :            :         }
     675                 :            : 
     676         [ #  # ]:          0 :         if (!skip_check(mode, SKIP_DATA)) {
     677                 :          0 :                 vhd_first_data_block(vhd, &fb);
     678 [ #  # ][ #  # ]:          0 :                 if (fb.offset && in_range(off,
     679                 :          0 :                                           vhd_sectors_to_bytes(fb.offset),
     680                 :            :                                           VHD_BLOCK_SIZE)) {
     681                 :            :                         msg = "data block";
     682                 :            :                         goto fail;
     683                 :            :                 }
     684                 :            :         }
     685                 :            : 
     686                 :            :         return 0;
     687                 :            : 
     688                 :            : fail:
     689                 :          0 :         EPRINTF("write to 0x%08"PRIx64" would clobber %s\n", off, msg);
     690                 :            :         return -EINVAL;
     691                 :            : }
     692                 :            : 
     693                 :            : /*
     694                 :            :  * take any metadata after the bat (@eob) and shift it
     695                 :            :  */
     696                 :            : static int
     697                 :          0 : vhd_shift_metadata(vhd_journal_t *journal, off64_t eob,
     698                 :            :                    size_t bat_needed, size_t map_needed)
     699                 :            : {
     700                 :            :         int i, n, err;
     701                 :            :         vhd_context_t *vhd;
     702                 :            :         size_t size_needed;
     703                 :          0 :         void *buf = NULL;
     704                 :            :         char **locators;
     705                 :          0 :         vhd_parent_locator_t *loc;
     706                 :            : 
     707                 :          0 :         vhd         = &journal->vhd;
     708                 :          0 :         size_needed = bat_needed + map_needed;
     709                 :            : 
     710                 :          0 :         n = sizeof(vhd->header.loc) / sizeof(vhd_parent_locator_t);
     711                 :            : 
     712                 :          0 :         locators = calloc(n, sizeof(char *));
     713         [ #  # ]:          0 :         if (!locators)
     714                 :          0 :                 return -ENOMEM;
     715                 :            : 
     716         [ #  # ]:          0 :         for (i = 0; i < n; i++) {
     717                 :            :                 size_t size;
     718                 :            : 
     719                 :          0 :                 loc = vhd->header.loc + i;
     720         [ #  # ]:          0 :                 if (loc->code == PLAT_CODE_NONE)
     721                 :          0 :                         continue;
     722                 :            : 
     723         [ #  # ]:          0 :                 if (loc->data_offset < eob)
     724                 :          0 :                         continue;
     725                 :            : 
     726                 :          0 :                 size = vhd_parent_locator_size(loc);
     727                 :          0 :                 err  = posix_memalign(&buf, VHD_SECTOR_SIZE, size);
     728         [ #  # ]:          0 :                 if (err) {
     729                 :          0 :                         err = -err;
     730                 :          0 :                         goto out;
     731                 :            :                 }
     732                 :            : 
     733                 :          0 :                 err  = vhd_seek(vhd, loc->data_offset, SEEK_SET);
     734         [ #  # ]:          0 :                 if (err)
     735                 :            :                         goto out;
     736                 :            : 
     737                 :          0 :                 err  = vhd_read(vhd, buf, size);
     738         [ #  # ]:          0 :                 if (err)
     739                 :            :                         goto out;
     740                 :            : 
     741                 :          0 :                 locators[i] = buf;
     742                 :            :         }
     743                 :            : 
     744                 :          0 :         buf = NULL;
     745                 :            : 
     746         [ #  # ]:          0 :         for (i = 0; i < n; i++) {
     747                 :            :                 off64_t off;
     748                 :            :                 size_t size;
     749                 :            : 
     750         [ #  # ]:          0 :                 if (!locators[i])
     751                 :          0 :                         continue;
     752                 :            : 
     753                 :          0 :                 loc  = vhd->header.loc + i;
     754                 :          0 :                 off  = loc->data_offset + size_needed;
     755                 :          0 :                 size = vhd_parent_locator_size(loc);
     756                 :            : 
     757         [ #  # ]:          0 :                 if (vhd_check_for_clobber(vhd, off + size, SKIP_PLOC)) {
     758                 :          0 :                         EPRINTF("%s: shifting locator %d would clobber data\n",
     759                 :            :                                 vhd->file, i);
     760                 :            :                         err = -EINVAL;
     761                 :            :                         goto out;
     762                 :            :                 }
     763                 :            : 
     764                 :          0 :                 err  = vhd_seek(vhd, off, SEEK_SET);
     765         [ #  # ]:          0 :                 if (err)
     766                 :            :                         goto out;
     767                 :            : 
     768                 :          0 :                 err  = vhd_write(vhd, locators[i], size);
     769         [ #  # ]:          0 :                 if (err)
     770                 :            :                         goto out;
     771                 :            : 
     772                 :          0 :                 free(locators[i]);
     773                 :          0 :                 locators[i]      = NULL;
     774                 :          0 :                 loc->data_offset = off;
     775                 :            : 
     776                 :            :                 /* write the new header after writing the new bat */
     777                 :            :         }
     778                 :            : 
     779 [ #  # ][ #  # ]:          0 :         if (vhd_has_batmap(vhd) && vhd->batmap.header.batmap_offset > eob) {
     780                 :          0 :                 vhd->batmap.header.batmap_offset += bat_needed;
     781                 :            : 
     782                 :            :                 /* write the new batmap after writing the new bat */
     783                 :            :         }
     784                 :            : 
     785                 :            :         err = 0;
     786                 :            : 
     787                 :            : out:
     788                 :          0 :         free(buf);
     789         [ #  # ]:          0 :         for (i = 0; i < n; i++)
     790                 :          0 :                 free(locators[i]);
     791                 :          0 :         free(locators);
     792                 :            : 
     793                 :          0 :         return err;
     794                 :            : }
     795                 :            : 
     796                 :            : static int
     797                 :          0 : vhd_add_bat_entries(vhd_journal_t *journal, int entries)
     798                 :            : {
     799                 :            :         int i, err;
     800                 :            :         off64_t off;
     801                 :            :         vhd_bat_t new_bat;
     802                 :            :         vhd_context_t *vhd;
     803                 :            :         uint32_t new_entries;
     804                 :            :         vhd_batmap_t new_batmap;
     805                 :            :         uint64_t bat_size, new_bat_size, map_size, new_map_size;
     806                 :            :         void *bat, *map;
     807                 :            : 
     808                 :          0 :         vhd          = &journal->vhd;
     809                 :          0 :         new_entries  = vhd->header.max_bat_size + entries;
     810                 :            : 
     811                 :          0 :         bat_size     = vhd_bytes_padded(vhd->header.max_bat_size *
     812                 :            :                                         sizeof(uint32_t));
     813                 :          0 :         new_bat_size = vhd_bytes_padded(new_entries * sizeof(uint32_t));
     814                 :            : 
     815                 :          0 :         map_size     = vhd_bytes_padded((vhd->header.max_bat_size + 7) >> 3);
     816                 :          0 :         new_map_size = vhd_bytes_padded((new_entries + 7) >> 3);
     817                 :            : 
     818                 :          0 :         off = vhd->header.table_offset + new_bat_size;
     819         [ #  # ]:          0 :         if (vhd_check_for_clobber(vhd, off, SKIP_BAT | SKIP_BATMAP)) {
     820                 :          0 :                 EPRINTF("%s: writing new bat of 0x%"PRIx64" bytes "
     821                 :            :                         "at 0x%08"PRIx64" would clobber data\n", 
     822                 :            :                         vhd->file, new_bat_size, vhd->header.table_offset);
     823                 :          0 :                 return -EINVAL;
     824                 :            :         }
     825                 :            : 
     826         [ #  # ]:          0 :         if (vhd_has_batmap(vhd)) {
     827                 :          0 :                 off = vhd->batmap.header.batmap_offset + new_map_size;
     828         [ #  # ]:          0 :                 if (vhd_check_for_clobber(vhd, off, 0)) {
     829                 :          0 :                         EPRINTF("%s: writing new batmap of 0x%"PRIx64" bytes"
     830                 :            :                                 " at 0x%08"PRIx64" would clobber data\n", vhd->file,
     831                 :            :                                 new_map_size, vhd->batmap.header.batmap_offset);
     832                 :            :                         return -EINVAL;
     833                 :            :                 }
     834                 :            :         }
     835                 :            : 
     836                 :            :         /* update header */
     837                 :          0 :         vhd->header.max_bat_size = new_entries;
     838                 :          0 :         err = vhd_write_header(vhd, &vhd->header);
     839         [ #  # ]:          0 :         if (err)
     840                 :            :                 return err;
     841                 :            : 
     842                 :            :         /* allocate new bat */
     843                 :          0 :         err = posix_memalign(&bat, VHD_SECTOR_SIZE, new_bat_size);
     844         [ #  # ]:          0 :         if (err)
     845                 :          0 :                 return -err;
     846                 :            : 
     847                 :          0 :         new_bat.bat     = bat;
     848                 :          0 :         new_bat.spb     = vhd->bat.spb;
     849                 :          0 :         new_bat.entries = new_entries;
     850                 :          0 :         memcpy(new_bat.bat, vhd->bat.bat, bat_size);
     851         [ #  # ]:          0 :         for (i = vhd->bat.entries; i < new_entries; i++)
     852                 :          0 :                 new_bat.bat[i] = DD_BLK_UNUSED;
     853                 :            : 
     854                 :            :         /* write new bat */
     855                 :          0 :         err = vhd_write_bat(vhd, &new_bat);
     856         [ #  # ]:          0 :         if (err) {
     857                 :          0 :                 free(new_bat.bat);
     858                 :          0 :                 return err;
     859                 :            :         }
     860                 :            : 
     861                 :            :         /* update in-memory bat */
     862                 :          0 :         free(vhd->bat.bat);
     863                 :          0 :         vhd->bat = new_bat;
     864                 :            : 
     865         [ #  # ]:          0 :         if (!vhd_has_batmap(vhd))
     866                 :            :                 return 0;
     867                 :            : 
     868                 :            :         /* allocate new batmap */
     869                 :          0 :         err = posix_memalign(&map, VHD_SECTOR_SIZE, new_map_size);
     870         [ #  # ]:          0 :         if (err)
     871                 :            :                 return err;
     872                 :            : 
     873                 :          0 :         new_batmap.map    = map;
     874                 :          0 :         new_batmap.header = vhd->batmap.header;
     875                 :          0 :         new_batmap.header.batmap_size = secs_round_up_no_zero(new_map_size);
     876                 :          0 :         memcpy(new_batmap.map, vhd->batmap.map, map_size);
     877                 :          0 :         memset(new_batmap.map + map_size, 0, new_map_size - map_size);
     878                 :            : 
     879                 :            :         /* write new batmap */
     880                 :          0 :         err = vhd_write_batmap(vhd, &new_batmap);
     881         [ #  # ]:          0 :         if (err) {
     882                 :          0 :                 free(new_batmap.map);
     883                 :          0 :                 return err;
     884                 :            :         }
     885                 :            : 
     886                 :            :         /* update in-memory batmap */
     887                 :          0 :         free(vhd->batmap.map);
     888                 :          0 :         vhd->batmap = new_batmap;
     889                 :            : 
     890                 :            :         /* update footer */
     891                 :          0 :         vhd->footer.curr_size = (uint64_t)new_entries * vhd->header.block_size;
     892                 :          0 :         vhd->footer.geometry  = vhd_chs(vhd->footer.curr_size);
     893                 :          0 :         vhd->footer.checksum  = vhd_checksum_footer(&vhd->footer);
     894                 :          0 :         err = vhd_write_footer(vhd, &vhd->footer);
     895         [ #  # ]:          0 :         if (err)
     896                 :          0 :                 return err;
     897                 :            : 
     898                 :            :         return 0;
     899                 :            : }
     900                 :            : 
     901                 :            : static int
     902                 :          0 : vhd_dynamic_grow(vhd_journal_t *journal, uint64_t secs)
     903                 :            : {
     904                 :            :         int err;
     905                 :            :         off64_t eob, eom;
     906                 :          0 :         vhd_context_t *vhd;
     907                 :            :         vhd_block_t first_block;
     908                 :            :         uint64_t blocks, size_needed;
     909                 :            :         uint64_t bat_needed, bat_size, bat_avail, bat_bytes, bat_secs;
     910                 :            :         uint64_t map_needed, map_size, map_avail, map_bytes, map_secs;
     911                 :            : 
     912                 :          0 :         vhd         = &journal->vhd;
     913                 :            : 
     914                 :          0 :         size_needed = 0;
     915                 :          0 :         bat_needed  = 0;
     916                 :          0 :         map_needed  = 0;
     917                 :            : 
     918                 :            :         /* number of vhd blocks to add */
     919                 :          0 :         blocks      = secs_to_blocks_up(vhd, secs);
     920                 :            : 
     921                 :            :         /* size in bytes needed for new bat entries */
     922                 :          0 :         bat_needed  = blocks * sizeof(uint32_t);
     923                 :          0 :         map_needed  = (blocks >> 3) + 1;
     924                 :            : 
     925                 :            :         /* available bytes in current bat */
     926                 :          0 :         bat_bytes   = vhd->header.max_bat_size * sizeof(uint32_t);
     927                 :          0 :         bat_secs    = secs_round_up_no_zero(bat_bytes);
     928                 :          0 :         bat_size    = vhd_sectors_to_bytes(bat_secs);
     929                 :          0 :         bat_avail   = bat_size - bat_bytes;
     930                 :            : 
     931         [ #  # ]:          0 :         if (vhd_has_batmap(vhd)) {
     932                 :            :                 /* avaliable bytes in current batmap */
     933                 :          0 :                 map_bytes   = (vhd->header.max_bat_size + 7) >> 3;
     934                 :          0 :                 map_secs    = vhd->batmap.header.batmap_size;
     935                 :          0 :                 map_size    = vhd_sectors_to_bytes(map_secs);
     936                 :          0 :                 map_avail   = map_size - map_bytes;
     937                 :            :         } else {
     938                 :            :                 map_needed  = 0;
     939                 :            :                 map_avail   = 0;
     940                 :            :         }
     941                 :            : 
     942                 :            :         /* we have enough space already; just extend the bat */
     943         [ #  # ]:          0 :         if (bat_needed <= bat_avail && map_needed <= map_avail)
     944                 :            :                 goto add_entries;
     945                 :            : 
     946                 :            :         /* we need to add new sectors to the bat */
     947         [ #  # ]:          0 :         if (bat_needed > bat_avail) {
     948                 :          0 :                 bat_needed -= bat_avail;
     949                 :          0 :                 bat_needed  = vhd_bytes_padded(bat_needed);
     950                 :            :         } else
     951                 :            :                 bat_needed  = 0;
     952                 :            : 
     953                 :            :         /* we need to add new sectors to the batmap */
     954         [ #  # ]:          0 :         if (map_needed > map_avail) {
     955                 :          0 :                 map_needed -= map_avail;
     956                 :          0 :                 map_needed  = vhd_bytes_padded(map_needed);
     957                 :            :         } else
     958                 :            :                 map_needed  = 0;
     959                 :            : 
     960                 :            :         /* how many additional bytes do we need? */
     961                 :          0 :         size_needed = bat_needed + map_needed;
     962                 :            : 
     963                 :            :         /* calculate space between end of headers and beginning of data */
     964                 :          0 :         err = vhd_end_of_headers(vhd, &eom);
     965         [ #  # ]:          0 :         if (err)
     966                 :          0 :                 return err;
     967                 :            : 
     968                 :          0 :         eob = vhd->header.table_offset + vhd_sectors_to_bytes(bat_secs);
     969                 :          0 :         vhd_first_data_block(vhd, &first_block);
     970                 :            : 
     971                 :            :         /* no blocks allocated; just shift post-bat metadata */
     972         [ #  # ]:          0 :         if (!first_block.offset)
     973                 :            :                 goto shift_metadata;
     974                 :            : 
     975                 :            :         /* 
     976                 :            :          * not enough space -- 
     977                 :            :          * move vhd data blocks to the end of the file to make room 
     978                 :            :          */
     979                 :            :         do {
     980                 :            :                 off64_t new_off, bm_size, gap_size;
     981                 :            : 
     982                 :          0 :                 new_off = vhd_sectors_to_bytes(vhd_next_block_offset(vhd));
     983                 :            : 
     984                 :            :                 /* data region of segment should begin on page boundary */
     985                 :          0 :                 bm_size = vhd_sectors_to_bytes(vhd->bm_secs);
     986         [ #  # ]:          0 :                 if ((new_off + bm_size) % 4096) {
     987                 :          0 :                         gap_size = 4096 - ((new_off + bm_size) % 4096);
     988                 :            : 
     989                 :          0 :                         err = vhd_write_zeros(journal, new_off, gap_size);
     990         [ #  # ]:          0 :                         if (err)
     991                 :            :                                 return err;
     992                 :            : 
     993                 :          0 :                         new_off += gap_size;
     994                 :            :                 }
     995                 :            : 
     996                 :          0 :                 err = vhd_move_block(journal, first_block.block, new_off);
     997         [ #  # ]:          0 :                 if (err)
     998                 :            :                         return err;
     999                 :            : 
    1000                 :          0 :                 vhd_first_data_block(vhd, &first_block);
    1001                 :            : 
    1002         [ #  # ]:          0 :         } while (eom + size_needed >= vhd_sectors_to_bytes(first_block.offset));
    1003                 :            : 
    1004         [ #  # ]:          0 :         TEST_FAIL_AT(FAIL_RESIZE_DATA_MOVED);
    1005                 :            : 
    1006                 :            : shift_metadata:
    1007                 :            :         /* shift any metadata after the bat to make room for new bat sectors */
    1008                 :          0 :         err = vhd_shift_metadata(journal, eob, bat_needed, map_needed);
    1009         [ #  # ]:          0 :         if (err)
    1010                 :            :                 return err;
    1011                 :            : 
    1012         [ #  # ]:          0 :         TEST_FAIL_AT(FAIL_RESIZE_METADATA_MOVED);
    1013                 :            : 
    1014                 :            : add_entries:
    1015                 :          0 :         return vhd_add_bat_entries(journal, blocks);
    1016                 :            : }
    1017                 :            : 
    1018                 :            : static int
    1019                 :          0 : vhd_dynamic_resize(vhd_journal_t *journal, uint64_t size)
    1020                 :            : {
    1021                 :            :         int err;
    1022                 :            :         vhd_context_t *vhd;
    1023                 :            :         uint64_t cur_secs, new_secs;
    1024                 :            : 
    1025                 :          0 :         vhd      = &journal->vhd;
    1026                 :          0 :         cur_secs = vhd->footer.curr_size >> VHD_SECTOR_SHIFT;
    1027                 :          0 :         new_secs = size << (20 - VHD_SECTOR_SHIFT);
    1028                 :            : 
    1029         [ #  # ]:          0 :         if (cur_secs == new_secs)
    1030                 :            :                 return 0;
    1031                 :            : 
    1032                 :          0 :         err = vhd_get_header(vhd);
    1033         [ #  # ]:          0 :         if (err)
    1034                 :            :                 return err;
    1035                 :            : 
    1036                 :          0 :         err = vhd_get_bat(vhd);
    1037         [ #  # ]:          0 :         if (err)
    1038                 :            :                 return err;
    1039                 :            : 
    1040         [ #  # ]:          0 :         if (vhd_has_batmap(vhd)) {
    1041                 :          0 :                 err = vhd_get_batmap(vhd);
    1042         [ #  # ]:          0 :                 if (err)
    1043                 :            :                         return err;
    1044                 :            :         }
    1045                 :            : 
    1046         [ #  # ]:          0 :         if (cur_secs > new_secs)
    1047                 :          0 :                 err = vhd_dynamic_shrink(journal, cur_secs - new_secs);
    1048                 :            :         else
    1049                 :          0 :                 err = vhd_dynamic_grow(journal, new_secs - cur_secs);
    1050                 :            : 
    1051                 :          0 :         return err;
    1052                 :            : }
    1053                 :            : 
    1054                 :            : static int
    1055                 :          0 : vhd_util_resize_check_creator(const char *name)
    1056                 :            : {
    1057                 :            :         int err;
    1058                 :            :         vhd_context_t vhd;
    1059                 :            : 
    1060                 :          0 :         err = vhd_open(&vhd, name, VHD_OPEN_RDONLY | VHD_OPEN_STRICT);
    1061         [ #  # ]:          0 :         if (err) {
    1062                 :            :                 printf("error opening %s: %d\n", name, err);
    1063                 :          0 :                 return err;
    1064                 :            :         }
    1065                 :            : 
    1066         [ #  # ]:          0 :         if (!vhd_creator_tapdisk(&vhd)) {
    1067                 :            :                 printf("%s not created by xen; resize not supported\n", name);
    1068                 :            :                 err = -EINVAL;
    1069                 :            :         }
    1070                 :            : 
    1071                 :          0 :         vhd_close(&vhd);
    1072                 :            :         return err;
    1073                 :            : }
    1074                 :            : 
    1075                 :            : static int
    1076                 :          0 : vhd_dynamic_grow_fast(const char *name, uint64_t bytes)
    1077                 :            : {
    1078                 :            :         vhd_context_t vhd;
    1079                 :            :         uint64_t blks, size;
    1080                 :            :         int err;
    1081                 :            : 
    1082                 :          0 :         err = vhd_open(&vhd, name, VHD_OPEN_RDWR);
    1083         [ #  # ]:          0 :         if (err)
    1084                 :          0 :                 return err;
    1085                 :            : 
    1086                 :          0 :         err = vhd_get_bat(&vhd);
    1087         [ #  # ]:          0 :         if (err)
    1088                 :            :                 goto done;
    1089                 :            : 
    1090         [ #  # ]:          0 :         if (vhd_has_batmap(&vhd)) {
    1091                 :          0 :                 err = vhd_get_batmap(&vhd);
    1092         [ #  # ]:          0 :                 if (err)
    1093                 :            :                         goto done;
    1094                 :            :         }
    1095                 :            : 
    1096                 :          0 :         blks   = (bytes + VHD_BLOCK_SIZE - 1) >> VHD_BLOCK_SHIFT;
    1097                 :          0 :         size   = blks << VHD_BLOCK_SHIFT;
    1098         [ #  # ]:          0 :         if (size < vhd.footer.curr_size) {
    1099                 :          0 :                 printf("%s: size (%"PRIu64") < curr size (%"PRIu64")\n", 
    1100                 :            :                        name, size, vhd.footer.curr_size);
    1101                 :            :                 err = -EINVAL;
    1102                 :            :                 goto done;
    1103                 :            :         }
    1104         [ #  # ]:          0 :         if (size == vhd.footer.curr_size)
    1105                 :            :                 goto done;
    1106                 :            : 
    1107                 :          0 :         err = vhd_set_virt_size(&vhd, size);
    1108                 :            : 
    1109                 :            : done:
    1110                 :          0 :         vhd_close(&vhd);
    1111                 :            :         return err;
    1112                 :            : }
    1113                 :            : 
    1114                 :            : int
    1115                 :          0 : vhd_util_resize(int argc, char **argv)
    1116                 :            : {
    1117                 :            :         char *name, *jname;
    1118                 :            :         uint64_t size;
    1119                 :            :         int fast, c, err, jerr;
    1120                 :            :         vhd_journal_t journal;
    1121                 :          0 :         vhd_context_t *vhd;
    1122                 :            : 
    1123                 :          0 :         err   = -EINVAL;
    1124                 :          0 :         size  = 0;
    1125                 :          0 :         name  = NULL;
    1126                 :          0 :         jname = NULL;
    1127                 :          0 :         fast  = 0;
    1128                 :            : 
    1129                 :          0 :         optind = 0;
    1130         [ #  # ]:          0 :         while ((c = getopt(argc, argv, "n:s:j:fh")) != -1) {
    1131   [ #  #  #  #  :          0 :                 switch (c) {
                      # ]
    1132                 :            :                 case 'n':
    1133                 :          0 :                         name = optarg;
    1134                 :          0 :                         break;
    1135                 :            :                 case 'j':
    1136                 :          0 :                         jname = optarg;
    1137                 :          0 :                         break;
    1138                 :            :                 case 'f':
    1139                 :            :                         fast = 1;
    1140                 :            :                         break;
    1141                 :            :                 case 's':
    1142                 :          0 :                         err  = 0;
    1143                 :          0 :                         size = strtoull(optarg, NULL, 10);
    1144                 :          0 :                         break;
    1145                 :            :                 case 'h':
    1146                 :            :                 default:
    1147                 :            :                         goto usage;
    1148                 :            :                 }
    1149                 :            :         }
    1150                 :            : 
    1151 [ #  # ][ #  # ]:          0 :         if (err || !name || (!jname && !fast) || argc != optind)
                 [ #  # ]
    1152                 :            :                 goto usage;
    1153                 :            : 
    1154         [ #  # ]:          0 :         if (jname && fast)
    1155                 :            :                 goto usage;
    1156                 :            : 
    1157                 :          0 :         err = vhd_util_resize_check_creator(name);
    1158         [ #  # ]:          0 :         if (err)
    1159                 :          0 :                 return err;
    1160                 :            : 
    1161                 :          0 :         libvhd_set_log_level(1);
    1162                 :            : 
    1163         [ #  # ]:          0 :         if (fast)
    1164                 :          0 :                 return vhd_dynamic_grow_fast(name, size << 20);
    1165                 :            : 
    1166                 :          0 :         err = vhd_journal_create(&journal, name, jname);
    1167         [ #  # ]:          0 :         if (err) {
    1168                 :            :                 printf("creating journal failed: %d\n", err);
    1169                 :            :                 return err;
    1170                 :            :         }
    1171                 :            : 
    1172                 :          0 :         vhd = &journal.vhd;
    1173                 :            : 
    1174                 :          0 :         err = vhd_get_footer(vhd);
    1175         [ #  # ]:          0 :         if (err)
    1176                 :            :                 goto out;
    1177                 :            : 
    1178         [ #  # ]:          0 :         TEST_FAIL_AT(FAIL_RESIZE_BEGIN);
    1179                 :            : 
    1180         [ #  # ]:          0 :         if (vhd_type_dynamic(vhd))
    1181                 :          0 :                 err = vhd_dynamic_resize(&journal, size);
    1182                 :            :         else
    1183                 :          0 :                 err = vhd_fixed_resize(&journal, size);
    1184                 :            : 
    1185         [ #  # ]:          0 :         TEST_FAIL_AT(FAIL_RESIZE_END);
    1186                 :            : 
    1187                 :            : out:
    1188         [ #  # ]:          0 :         if (err) {
    1189                 :            :                 printf("resize failed: %d\n", err);
    1190                 :          0 :                 jerr = vhd_journal_revert(&journal);
    1191                 :            :         } else
    1192                 :          0 :                 jerr = vhd_journal_commit(&journal);
    1193                 :            : 
    1194         [ #  # ]:          0 :         if (jerr) {
    1195                 :            :                 printf("closing journal failed: %d\n", jerr);
    1196                 :          0 :                 vhd_journal_close(&journal);
    1197                 :            :         } else
    1198                 :          0 :                 vhd_journal_remove(&journal);
    1199                 :            : 
    1200         [ #  # ]:          0 :         return (err ? : jerr);
    1201                 :            : 
    1202                 :            : usage:
    1203                 :            :         printf("options: <-n name> <-s size (in MB)> (<-j journal>|<-f fast>) "
    1204                 :            :                         "[-h help]\n\n"
    1205                 :            :                         "The resize operation can only be performed offline "
    1206                 :            :                         "and must be journaled because resizing the metadata "
    1207                 :            :                         "might require moving data blocks. However, if a "
    1208                 :            :                         "VHD was created with -S <msize> option (during "
    1209                 :            :                         "vhd-util create/snapshot), which preallocates the "
    1210                 :            :                         "metadata for growing the VHD up to size <msize>, then "
    1211                 :            :                         "resizing such a VHD up to <msize> can be performed "
    1212                 :            :                         "online without journaling (-f option).\n");
    1213                 :            :         return -EINVAL;
    1214                 :            : }

Generated by: LCOV version 1.13