15d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner/* 25d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * Block driver for the QCOW version 2 format 35d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * 45d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * Copyright (c) 2004-2006 Fabrice Bellard 55d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * 65d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * Permission is hereby granted, free of charge, to any person obtaining a copy 75d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * of this software and associated documentation files (the "Software"), to deal 85d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * in the Software without restriction, including without limitation the rights 95d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 105d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * copies of the Software, and to permit persons to whom the Software is 115d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * furnished to do so, subject to the following conditions: 125d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * 135d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * The above copyright notice and this permission notice shall be included in 145d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * all copies or substantial portions of the Software. 155d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * 165d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 175d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 185d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 195d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 205d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 215d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 225d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * THE SOFTWARE. 235d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner */ 245d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 255d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner#include "qemu-common.h" 26e1e03df288d5a44bfbffbd86588395c7cbbc27dfDavid 'Digit' Turner#include "block/block_int.h" 275d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner#include "block/qcow2.h" 285d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 295d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turnertypedef struct __attribute__((packed)) QCowSnapshotHeader { 305d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner /* header is 8 byte aligned */ 315d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner uint64_t l1_table_offset; 325d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 335d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner uint32_t l1_size; 345d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner uint16_t id_str_size; 355d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner uint16_t name_size; 365d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 375d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner uint32_t date_sec; 385d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner uint32_t date_nsec; 395d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 405d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner uint64_t vm_clock_nsec; 415d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 425d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner uint32_t vm_state_size; 435d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner uint32_t extra_data_size; /* for extension */ 445d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner /* extra data follows */ 455d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner /* id_str follows */ 465d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner /* name follows */ 475d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner} QCowSnapshotHeader; 485d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 495d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turnervoid qcow2_free_snapshots(BlockDriverState *bs) 505d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner{ 515d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner BDRVQcowState *s = bs->opaque; 525d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner int i; 535d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 545d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner for(i = 0; i < s->nb_snapshots; i++) { 55aa8236dc1b1ea300ab18716db5b8fab42aca3ca7David 'Digit' Turner g_free(s->snapshots[i].name); 56aa8236dc1b1ea300ab18716db5b8fab42aca3ca7David 'Digit' Turner g_free(s->snapshots[i].id_str); 575d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner } 58aa8236dc1b1ea300ab18716db5b8fab42aca3ca7David 'Digit' Turner g_free(s->snapshots); 595d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner s->snapshots = NULL; 605d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner s->nb_snapshots = 0; 615d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner} 625d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 635d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turnerint qcow2_read_snapshots(BlockDriverState *bs) 645d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner{ 655d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner BDRVQcowState *s = bs->opaque; 665d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner QCowSnapshotHeader h; 675d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner QCowSnapshot *sn; 685d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner int i, id_str_size, name_size; 695d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner int64_t offset; 705d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner uint32_t extra_data_size; 715d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 725d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner if (!s->nb_snapshots) { 735d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner s->snapshots = NULL; 745d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner s->snapshots_size = 0; 755d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner return 0; 765d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner } 775d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 785d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner offset = s->snapshots_offset; 79aa8236dc1b1ea300ab18716db5b8fab42aca3ca7David 'Digit' Turner s->snapshots = g_malloc0(s->nb_snapshots * sizeof(QCowSnapshot)); 805d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner for(i = 0; i < s->nb_snapshots; i++) { 815d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner offset = align_offset(offset, 8); 82cb42a1b1461e02efb034582ac5d8f71534723b92David 'Digit' Turner if (bdrv_pread(bs->file, offset, &h, sizeof(h)) != sizeof(h)) 835d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner goto fail; 845d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner offset += sizeof(h); 855d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner sn = s->snapshots + i; 865d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner sn->l1_table_offset = be64_to_cpu(h.l1_table_offset); 875d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner sn->l1_size = be32_to_cpu(h.l1_size); 885d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner sn->vm_state_size = be32_to_cpu(h.vm_state_size); 895d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner sn->date_sec = be32_to_cpu(h.date_sec); 905d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner sn->date_nsec = be32_to_cpu(h.date_nsec); 915d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner sn->vm_clock_nsec = be64_to_cpu(h.vm_clock_nsec); 925d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner extra_data_size = be32_to_cpu(h.extra_data_size); 935d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 945d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner id_str_size = be16_to_cpu(h.id_str_size); 955d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner name_size = be16_to_cpu(h.name_size); 965d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 975d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner offset += extra_data_size; 985d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 99aa8236dc1b1ea300ab18716db5b8fab42aca3ca7David 'Digit' Turner sn->id_str = g_malloc(id_str_size + 1); 100cb42a1b1461e02efb034582ac5d8f71534723b92David 'Digit' Turner if (bdrv_pread(bs->file, offset, sn->id_str, id_str_size) != id_str_size) 1015d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner goto fail; 1025d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner offset += id_str_size; 1035d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner sn->id_str[id_str_size] = '\0'; 1045d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 105aa8236dc1b1ea300ab18716db5b8fab42aca3ca7David 'Digit' Turner sn->name = g_malloc(name_size + 1); 106cb42a1b1461e02efb034582ac5d8f71534723b92David 'Digit' Turner if (bdrv_pread(bs->file, offset, sn->name, name_size) != name_size) 1075d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner goto fail; 1085d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner offset += name_size; 1095d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner sn->name[name_size] = '\0'; 1105d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner } 1115d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner s->snapshots_size = offset - s->snapshots_offset; 1125d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner return 0; 1135d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner fail: 1145d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner qcow2_free_snapshots(bs); 1155d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner return -1; 1165d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner} 1175d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 1185d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner/* add at the end of the file a new list of snapshots */ 1195d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turnerstatic int qcow_write_snapshots(BlockDriverState *bs) 1205d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner{ 1215d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner BDRVQcowState *s = bs->opaque; 1225d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner QCowSnapshot *sn; 1235d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner QCowSnapshotHeader h; 1245d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner int i, name_size, id_str_size, snapshots_size; 1255d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner uint64_t data64; 1265d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner uint32_t data32; 1275d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner int64_t offset, snapshots_offset; 1285d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 1295d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner /* compute the size of the snapshots */ 1305d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner offset = 0; 1315d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner for(i = 0; i < s->nb_snapshots; i++) { 1325d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner sn = s->snapshots + i; 1335d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner offset = align_offset(offset, 8); 1345d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner offset += sizeof(h); 1355d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner offset += strlen(sn->id_str); 1365d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner offset += strlen(sn->name); 1375d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner } 1385d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner snapshots_size = offset; 1395d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 1405d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner snapshots_offset = qcow2_alloc_clusters(bs, snapshots_size); 1415d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner offset = snapshots_offset; 142cb42a1b1461e02efb034582ac5d8f71534723b92David 'Digit' Turner if (offset < 0) { 143cb42a1b1461e02efb034582ac5d8f71534723b92David 'Digit' Turner return offset; 144cb42a1b1461e02efb034582ac5d8f71534723b92David 'Digit' Turner } 1455d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 1465d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner for(i = 0; i < s->nb_snapshots; i++) { 1475d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner sn = s->snapshots + i; 1485d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner memset(&h, 0, sizeof(h)); 1495d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner h.l1_table_offset = cpu_to_be64(sn->l1_table_offset); 1505d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner h.l1_size = cpu_to_be32(sn->l1_size); 1515d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner h.vm_state_size = cpu_to_be32(sn->vm_state_size); 1525d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner h.date_sec = cpu_to_be32(sn->date_sec); 1535d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner h.date_nsec = cpu_to_be32(sn->date_nsec); 1545d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner h.vm_clock_nsec = cpu_to_be64(sn->vm_clock_nsec); 1555d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 1565d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner id_str_size = strlen(sn->id_str); 1575d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner name_size = strlen(sn->name); 1585d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner h.id_str_size = cpu_to_be16(id_str_size); 1595d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner h.name_size = cpu_to_be16(name_size); 1605d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner offset = align_offset(offset, 8); 161cb42a1b1461e02efb034582ac5d8f71534723b92David 'Digit' Turner if (bdrv_pwrite_sync(bs->file, offset, &h, sizeof(h)) < 0) 1625d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner goto fail; 1635d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner offset += sizeof(h); 164cb42a1b1461e02efb034582ac5d8f71534723b92David 'Digit' Turner if (bdrv_pwrite_sync(bs->file, offset, sn->id_str, id_str_size) < 0) 1655d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner goto fail; 1665d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner offset += id_str_size; 167cb42a1b1461e02efb034582ac5d8f71534723b92David 'Digit' Turner if (bdrv_pwrite_sync(bs->file, offset, sn->name, name_size) < 0) 1685d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner goto fail; 1695d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner offset += name_size; 1705d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner } 1715d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 1725d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner /* update the various header fields */ 1735d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner data64 = cpu_to_be64(snapshots_offset); 174cb42a1b1461e02efb034582ac5d8f71534723b92David 'Digit' Turner if (bdrv_pwrite_sync(bs->file, offsetof(QCowHeader, snapshots_offset), 175cb42a1b1461e02efb034582ac5d8f71534723b92David 'Digit' Turner &data64, sizeof(data64)) < 0) 1765d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner goto fail; 1775d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner data32 = cpu_to_be32(s->nb_snapshots); 178cb42a1b1461e02efb034582ac5d8f71534723b92David 'Digit' Turner if (bdrv_pwrite_sync(bs->file, offsetof(QCowHeader, nb_snapshots), 179cb42a1b1461e02efb034582ac5d8f71534723b92David 'Digit' Turner &data32, sizeof(data32)) < 0) 1805d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner goto fail; 1815d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 1825d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner /* free the old snapshot table */ 1835d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner qcow2_free_clusters(bs, s->snapshots_offset, s->snapshots_size); 1845d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner s->snapshots_offset = snapshots_offset; 1855d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner s->snapshots_size = snapshots_size; 1865d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner return 0; 1875d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner fail: 1885d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner return -1; 1895d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner} 1905d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 1915d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turnerstatic void find_new_snapshot_id(BlockDriverState *bs, 1925d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner char *id_str, int id_str_size) 1935d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner{ 1945d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner BDRVQcowState *s = bs->opaque; 1955d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner QCowSnapshot *sn; 1965d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner int i, id, id_max = 0; 1975d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 1985d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner for(i = 0; i < s->nb_snapshots; i++) { 1995d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner sn = s->snapshots + i; 2005d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner id = strtoul(sn->id_str, NULL, 10); 2015d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner if (id > id_max) 2025d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner id_max = id; 2035d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner } 2045d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner snprintf(id_str, id_str_size, "%d", id_max + 1); 2055d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner} 2065d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 2075d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turnerstatic int find_snapshot_by_id(BlockDriverState *bs, const char *id_str) 2085d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner{ 2095d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner BDRVQcowState *s = bs->opaque; 2105d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner int i; 2115d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 2125d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner for(i = 0; i < s->nb_snapshots; i++) { 2135d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner if (!strcmp(s->snapshots[i].id_str, id_str)) 2145d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner return i; 2155d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner } 2165d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner return -1; 2175d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner} 2185d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 2195d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turnerstatic int find_snapshot_by_id_or_name(BlockDriverState *bs, const char *name) 2205d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner{ 2215d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner BDRVQcowState *s = bs->opaque; 2225d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner int i, ret; 2235d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 2245d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner ret = find_snapshot_by_id(bs, name); 2255d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner if (ret >= 0) 2265d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner return ret; 2275d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner for(i = 0; i < s->nb_snapshots; i++) { 2285d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner if (!strcmp(s->snapshots[i].name, name)) 2295d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner return i; 2305d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner } 2315d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner return -1; 2325d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner} 2335d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 2345d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner/* if no id is provided, a new one is constructed */ 2355d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turnerint qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info) 2365d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner{ 2375d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner BDRVQcowState *s = bs->opaque; 2385d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner QCowSnapshot *snapshots1, sn1, *sn = &sn1; 2395d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner int i, ret; 2405d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner uint64_t *l1_table = NULL; 241cb42a1b1461e02efb034582ac5d8f71534723b92David 'Digit' Turner int64_t l1_table_offset; 2425d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 2435d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner memset(sn, 0, sizeof(*sn)); 2445d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 2455d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner if (sn_info->id_str[0] == '\0') { 2465d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner /* compute a new id */ 2475d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner find_new_snapshot_id(bs, sn_info->id_str, sizeof(sn_info->id_str)); 2485d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner } 2495d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 2505d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner /* check that the ID is unique */ 2515d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner if (find_snapshot_by_id(bs, sn_info->id_str) >= 0) 2525d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner return -ENOENT; 2535d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 254aa8236dc1b1ea300ab18716db5b8fab42aca3ca7David 'Digit' Turner sn->id_str = g_strdup(sn_info->id_str); 2555d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner if (!sn->id_str) 2565d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner goto fail; 257aa8236dc1b1ea300ab18716db5b8fab42aca3ca7David 'Digit' Turner sn->name = g_strdup(sn_info->name); 2585d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner if (!sn->name) 2595d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner goto fail; 2605d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner sn->vm_state_size = sn_info->vm_state_size; 2615d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner sn->date_sec = sn_info->date_sec; 2625d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner sn->date_nsec = sn_info->date_nsec; 2635d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner sn->vm_clock_nsec = sn_info->vm_clock_nsec; 2645d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 2655d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner ret = qcow2_update_snapshot_refcount(bs, s->l1_table_offset, s->l1_size, 1); 2665d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner if (ret < 0) 2675d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner goto fail; 2685d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 2695d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner /* create the L1 table of the snapshot */ 270cb42a1b1461e02efb034582ac5d8f71534723b92David 'Digit' Turner l1_table_offset = qcow2_alloc_clusters(bs, s->l1_size * sizeof(uint64_t)); 271cb42a1b1461e02efb034582ac5d8f71534723b92David 'Digit' Turner if (l1_table_offset < 0) { 272cb42a1b1461e02efb034582ac5d8f71534723b92David 'Digit' Turner goto fail; 273cb42a1b1461e02efb034582ac5d8f71534723b92David 'Digit' Turner } 274cb42a1b1461e02efb034582ac5d8f71534723b92David 'Digit' Turner 275cb42a1b1461e02efb034582ac5d8f71534723b92David 'Digit' Turner sn->l1_table_offset = l1_table_offset; 2765d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner sn->l1_size = s->l1_size; 2775d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 278cb42a1b1461e02efb034582ac5d8f71534723b92David 'Digit' Turner if (s->l1_size != 0) { 279aa8236dc1b1ea300ab18716db5b8fab42aca3ca7David 'Digit' Turner l1_table = g_malloc(s->l1_size * sizeof(uint64_t)); 280cb42a1b1461e02efb034582ac5d8f71534723b92David 'Digit' Turner } else { 281cb42a1b1461e02efb034582ac5d8f71534723b92David 'Digit' Turner l1_table = NULL; 282cb42a1b1461e02efb034582ac5d8f71534723b92David 'Digit' Turner } 283cb42a1b1461e02efb034582ac5d8f71534723b92David 'Digit' Turner 2845d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner for(i = 0; i < s->l1_size; i++) { 2855d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner l1_table[i] = cpu_to_be64(s->l1_table[i]); 2865d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner } 287cb42a1b1461e02efb034582ac5d8f71534723b92David 'Digit' Turner if (bdrv_pwrite_sync(bs->file, sn->l1_table_offset, 288cb42a1b1461e02efb034582ac5d8f71534723b92David 'Digit' Turner l1_table, s->l1_size * sizeof(uint64_t)) < 0) 2895d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner goto fail; 290aa8236dc1b1ea300ab18716db5b8fab42aca3ca7David 'Digit' Turner g_free(l1_table); 2915d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner l1_table = NULL; 2925d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 293aa8236dc1b1ea300ab18716db5b8fab42aca3ca7David 'Digit' Turner snapshots1 = g_malloc((s->nb_snapshots + 1) * sizeof(QCowSnapshot)); 2945d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner if (s->snapshots) { 2955d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner memcpy(snapshots1, s->snapshots, s->nb_snapshots * sizeof(QCowSnapshot)); 296aa8236dc1b1ea300ab18716db5b8fab42aca3ca7David 'Digit' Turner g_free(s->snapshots); 2975d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner } 2985d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner s->snapshots = snapshots1; 2995d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner s->snapshots[s->nb_snapshots++] = *sn; 3005d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 3015d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner if (qcow_write_snapshots(bs) < 0) 3025d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner goto fail; 3035d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner#ifdef DEBUG_ALLOC 304cb42a1b1461e02efb034582ac5d8f71534723b92David 'Digit' Turner qcow2_check_refcounts(bs); 3055d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner#endif 3065d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner return 0; 3075d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner fail: 308aa8236dc1b1ea300ab18716db5b8fab42aca3ca7David 'Digit' Turner g_free(sn->name); 309aa8236dc1b1ea300ab18716db5b8fab42aca3ca7David 'Digit' Turner g_free(l1_table); 3105d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner return -1; 3115d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner} 3125d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 3135d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner/* copy the snapshot 'snapshot_name' into the current disk image */ 3145d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turnerint qcow2_snapshot_goto(BlockDriverState *bs, const char *snapshot_id) 3155d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner{ 3165d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner BDRVQcowState *s = bs->opaque; 3175d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner QCowSnapshot *sn; 3185d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner int i, snapshot_index, l1_size2; 3195d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 3205d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner snapshot_index = find_snapshot_by_id_or_name(bs, snapshot_id); 3215d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner if (snapshot_index < 0) 3225d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner return -ENOENT; 3235d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner sn = &s->snapshots[snapshot_index]; 3245d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 3255d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner if (qcow2_update_snapshot_refcount(bs, s->l1_table_offset, s->l1_size, -1) < 0) 3265d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner goto fail; 3275d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 3285d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner if (qcow2_grow_l1_table(bs, sn->l1_size) < 0) 3295d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner goto fail; 3305d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 3315d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner s->l1_size = sn->l1_size; 3325d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner l1_size2 = s->l1_size * sizeof(uint64_t); 3335d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner /* copy the snapshot l1 table to the current l1 table */ 334cb42a1b1461e02efb034582ac5d8f71534723b92David 'Digit' Turner if (bdrv_pread(bs->file, sn->l1_table_offset, 3355d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner s->l1_table, l1_size2) != l1_size2) 3365d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner goto fail; 337cb42a1b1461e02efb034582ac5d8f71534723b92David 'Digit' Turner if (bdrv_pwrite_sync(bs->file, s->l1_table_offset, 338cb42a1b1461e02efb034582ac5d8f71534723b92David 'Digit' Turner s->l1_table, l1_size2) < 0) 3395d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner goto fail; 3405d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner for(i = 0;i < s->l1_size; i++) { 3415d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner be64_to_cpus(&s->l1_table[i]); 3425d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner } 3435d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 3445d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner if (qcow2_update_snapshot_refcount(bs, s->l1_table_offset, s->l1_size, 1) < 0) 3455d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner goto fail; 3465d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 3475d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner#ifdef DEBUG_ALLOC 348cb42a1b1461e02efb034582ac5d8f71534723b92David 'Digit' Turner qcow2_check_refcounts(bs); 3495d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner#endif 3505d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner return 0; 3515d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner fail: 3525d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner return -EIO; 3535d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner} 3545d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 3555d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turnerint qcow2_snapshot_delete(BlockDriverState *bs, const char *snapshot_id) 3565d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner{ 3575d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner BDRVQcowState *s = bs->opaque; 3585d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner QCowSnapshot *sn; 3595d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner int snapshot_index, ret; 3605d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 3615d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner snapshot_index = find_snapshot_by_id_or_name(bs, snapshot_id); 3625d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner if (snapshot_index < 0) 3635d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner return -ENOENT; 3645d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner sn = &s->snapshots[snapshot_index]; 3655d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 3665d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner ret = qcow2_update_snapshot_refcount(bs, sn->l1_table_offset, sn->l1_size, -1); 3675d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner if (ret < 0) 3685d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner return ret; 3695d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner /* must update the copied flag on the current cluster offsets */ 3705d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner ret = qcow2_update_snapshot_refcount(bs, s->l1_table_offset, s->l1_size, 0); 3715d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner if (ret < 0) 3725d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner return ret; 3735d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner qcow2_free_clusters(bs, sn->l1_table_offset, sn->l1_size * sizeof(uint64_t)); 3745d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 375aa8236dc1b1ea300ab18716db5b8fab42aca3ca7David 'Digit' Turner g_free(sn->id_str); 376aa8236dc1b1ea300ab18716db5b8fab42aca3ca7David 'Digit' Turner g_free(sn->name); 3775d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner memmove(sn, sn + 1, (s->nb_snapshots - snapshot_index - 1) * sizeof(*sn)); 3785d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner s->nb_snapshots--; 3795d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner ret = qcow_write_snapshots(bs); 3805d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner if (ret < 0) { 3815d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner /* XXX: restore snapshot if error ? */ 3825d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner return ret; 3835d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner } 3845d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner#ifdef DEBUG_ALLOC 385cb42a1b1461e02efb034582ac5d8f71534723b92David 'Digit' Turner qcow2_check_refcounts(bs); 3865d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner#endif 3875d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner return 0; 3885d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner} 3895d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 3905d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turnerint qcow2_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_tab) 3915d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner{ 3925d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner BDRVQcowState *s = bs->opaque; 3935d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner QEMUSnapshotInfo *sn_tab, *sn_info; 3945d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner QCowSnapshot *sn; 3955d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner int i; 3965d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 3975d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner if (!s->nb_snapshots) { 3985d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner *psn_tab = NULL; 3995d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner return s->nb_snapshots; 4005d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner } 4015d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 402aa8236dc1b1ea300ab18716db5b8fab42aca3ca7David 'Digit' Turner sn_tab = g_malloc0(s->nb_snapshots * sizeof(QEMUSnapshotInfo)); 4035d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner for(i = 0; i < s->nb_snapshots; i++) { 4045d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner sn_info = sn_tab + i; 4055d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner sn = s->snapshots + i; 4065d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner pstrcpy(sn_info->id_str, sizeof(sn_info->id_str), 4075d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner sn->id_str); 4085d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner pstrcpy(sn_info->name, sizeof(sn_info->name), 4095d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner sn->name); 4105d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner sn_info->vm_state_size = sn->vm_state_size; 4115d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner sn_info->date_sec = sn->date_sec; 4125d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner sn_info->date_nsec = sn->date_nsec; 4135d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner sn_info->vm_clock_nsec = sn->vm_clock_nsec; 4145d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner } 4155d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner *psn_tab = sn_tab; 4165d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner return s->nb_snapshots; 4175d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner} 4185d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner 419