Annotation of qemu/block/qcow2-snapshot.c, revision 1.1

1.1     ! root        1: /*
        !             2:  * Block driver for the QCOW version 2 format
        !             3:  *
        !             4:  * Copyright (c) 2004-2006 Fabrice Bellard
        !             5:  *
        !             6:  * Permission is hereby granted, free of charge, to any person obtaining a copy
        !             7:  * of this software and associated documentation files (the "Software"), to deal
        !             8:  * in the Software without restriction, including without limitation the rights
        !             9:  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
        !            10:  * copies of the Software, and to permit persons to whom the Software is
        !            11:  * furnished to do so, subject to the following conditions:
        !            12:  *
        !            13:  * The above copyright notice and this permission notice shall be included in
        !            14:  * all copies or substantial portions of the Software.
        !            15:  *
        !            16:  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        !            17:  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        !            18:  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
        !            19:  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        !            20:  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        !            21:  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
        !            22:  * THE SOFTWARE.
        !            23:  */
        !            24: 
        !            25: #include "qemu-common.h"
        !            26: #include "block_int.h"
        !            27: #include "block/qcow2.h"
        !            28: 
        !            29: typedef struct __attribute__((packed)) QCowSnapshotHeader {
        !            30:     /* header is 8 byte aligned */
        !            31:     uint64_t l1_table_offset;
        !            32: 
        !            33:     uint32_t l1_size;
        !            34:     uint16_t id_str_size;
        !            35:     uint16_t name_size;
        !            36: 
        !            37:     uint32_t date_sec;
        !            38:     uint32_t date_nsec;
        !            39: 
        !            40:     uint64_t vm_clock_nsec;
        !            41: 
        !            42:     uint32_t vm_state_size;
        !            43:     uint32_t extra_data_size; /* for extension */
        !            44:     /* extra data follows */
        !            45:     /* id_str follows */
        !            46:     /* name follows  */
        !            47: } QCowSnapshotHeader;
        !            48: 
        !            49: void qcow2_free_snapshots(BlockDriverState *bs)
        !            50: {
        !            51:     BDRVQcowState *s = bs->opaque;
        !            52:     int i;
        !            53: 
        !            54:     for(i = 0; i < s->nb_snapshots; i++) {
        !            55:         qemu_free(s->snapshots[i].name);
        !            56:         qemu_free(s->snapshots[i].id_str);
        !            57:     }
        !            58:     qemu_free(s->snapshots);
        !            59:     s->snapshots = NULL;
        !            60:     s->nb_snapshots = 0;
        !            61: }
        !            62: 
        !            63: int qcow2_read_snapshots(BlockDriverState *bs)
        !            64: {
        !            65:     BDRVQcowState *s = bs->opaque;
        !            66:     QCowSnapshotHeader h;
        !            67:     QCowSnapshot *sn;
        !            68:     int i, id_str_size, name_size;
        !            69:     int64_t offset;
        !            70:     uint32_t extra_data_size;
        !            71: 
        !            72:     if (!s->nb_snapshots) {
        !            73:         s->snapshots = NULL;
        !            74:         s->snapshots_size = 0;
        !            75:         return 0;
        !            76:     }
        !            77: 
        !            78:     offset = s->snapshots_offset;
        !            79:     s->snapshots = qemu_mallocz(s->nb_snapshots * sizeof(QCowSnapshot));
        !            80:     for(i = 0; i < s->nb_snapshots; i++) {
        !            81:         offset = align_offset(offset, 8);
        !            82:         if (bdrv_pread(s->hd, offset, &h, sizeof(h)) != sizeof(h))
        !            83:             goto fail;
        !            84:         offset += sizeof(h);
        !            85:         sn = s->snapshots + i;
        !            86:         sn->l1_table_offset = be64_to_cpu(h.l1_table_offset);
        !            87:         sn->l1_size = be32_to_cpu(h.l1_size);
        !            88:         sn->vm_state_size = be32_to_cpu(h.vm_state_size);
        !            89:         sn->date_sec = be32_to_cpu(h.date_sec);
        !            90:         sn->date_nsec = be32_to_cpu(h.date_nsec);
        !            91:         sn->vm_clock_nsec = be64_to_cpu(h.vm_clock_nsec);
        !            92:         extra_data_size = be32_to_cpu(h.extra_data_size);
        !            93: 
        !            94:         id_str_size = be16_to_cpu(h.id_str_size);
        !            95:         name_size = be16_to_cpu(h.name_size);
        !            96: 
        !            97:         offset += extra_data_size;
        !            98: 
        !            99:         sn->id_str = qemu_malloc(id_str_size + 1);
        !           100:         if (bdrv_pread(s->hd, offset, sn->id_str, id_str_size) != id_str_size)
        !           101:             goto fail;
        !           102:         offset += id_str_size;
        !           103:         sn->id_str[id_str_size] = '\0';
        !           104: 
        !           105:         sn->name = qemu_malloc(name_size + 1);
        !           106:         if (bdrv_pread(s->hd, offset, sn->name, name_size) != name_size)
        !           107:             goto fail;
        !           108:         offset += name_size;
        !           109:         sn->name[name_size] = '\0';
        !           110:     }
        !           111:     s->snapshots_size = offset - s->snapshots_offset;
        !           112:     return 0;
        !           113:  fail:
        !           114:     qcow2_free_snapshots(bs);
        !           115:     return -1;
        !           116: }
        !           117: 
        !           118: /* add at the end of the file a new list of snapshots */
        !           119: static int qcow_write_snapshots(BlockDriverState *bs)
        !           120: {
        !           121:     BDRVQcowState *s = bs->opaque;
        !           122:     QCowSnapshot *sn;
        !           123:     QCowSnapshotHeader h;
        !           124:     int i, name_size, id_str_size, snapshots_size;
        !           125:     uint64_t data64;
        !           126:     uint32_t data32;
        !           127:     int64_t offset, snapshots_offset;
        !           128: 
        !           129:     /* compute the size of the snapshots */
        !           130:     offset = 0;
        !           131:     for(i = 0; i < s->nb_snapshots; i++) {
        !           132:         sn = s->snapshots + i;
        !           133:         offset = align_offset(offset, 8);
        !           134:         offset += sizeof(h);
        !           135:         offset += strlen(sn->id_str);
        !           136:         offset += strlen(sn->name);
        !           137:     }
        !           138:     snapshots_size = offset;
        !           139: 
        !           140:     snapshots_offset = qcow2_alloc_clusters(bs, snapshots_size);
        !           141:     offset = snapshots_offset;
        !           142: 
        !           143:     for(i = 0; i < s->nb_snapshots; i++) {
        !           144:         sn = s->snapshots + i;
        !           145:         memset(&h, 0, sizeof(h));
        !           146:         h.l1_table_offset = cpu_to_be64(sn->l1_table_offset);
        !           147:         h.l1_size = cpu_to_be32(sn->l1_size);
        !           148:         h.vm_state_size = cpu_to_be32(sn->vm_state_size);
        !           149:         h.date_sec = cpu_to_be32(sn->date_sec);
        !           150:         h.date_nsec = cpu_to_be32(sn->date_nsec);
        !           151:         h.vm_clock_nsec = cpu_to_be64(sn->vm_clock_nsec);
        !           152: 
        !           153:         id_str_size = strlen(sn->id_str);
        !           154:         name_size = strlen(sn->name);
        !           155:         h.id_str_size = cpu_to_be16(id_str_size);
        !           156:         h.name_size = cpu_to_be16(name_size);
        !           157:         offset = align_offset(offset, 8);
        !           158:         if (bdrv_pwrite(s->hd, offset, &h, sizeof(h)) != sizeof(h))
        !           159:             goto fail;
        !           160:         offset += sizeof(h);
        !           161:         if (bdrv_pwrite(s->hd, offset, sn->id_str, id_str_size) != id_str_size)
        !           162:             goto fail;
        !           163:         offset += id_str_size;
        !           164:         if (bdrv_pwrite(s->hd, offset, sn->name, name_size) != name_size)
        !           165:             goto fail;
        !           166:         offset += name_size;
        !           167:     }
        !           168: 
        !           169:     /* update the various header fields */
        !           170:     data64 = cpu_to_be64(snapshots_offset);
        !           171:     if (bdrv_pwrite(s->hd, offsetof(QCowHeader, snapshots_offset),
        !           172:                     &data64, sizeof(data64)) != sizeof(data64))
        !           173:         goto fail;
        !           174:     data32 = cpu_to_be32(s->nb_snapshots);
        !           175:     if (bdrv_pwrite(s->hd, offsetof(QCowHeader, nb_snapshots),
        !           176:                     &data32, sizeof(data32)) != sizeof(data32))
        !           177:         goto fail;
        !           178: 
        !           179:     /* free the old snapshot table */
        !           180:     qcow2_free_clusters(bs, s->snapshots_offset, s->snapshots_size);
        !           181:     s->snapshots_offset = snapshots_offset;
        !           182:     s->snapshots_size = snapshots_size;
        !           183:     return 0;
        !           184:  fail:
        !           185:     return -1;
        !           186: }
        !           187: 
        !           188: static void find_new_snapshot_id(BlockDriverState *bs,
        !           189:                                  char *id_str, int id_str_size)
        !           190: {
        !           191:     BDRVQcowState *s = bs->opaque;
        !           192:     QCowSnapshot *sn;
        !           193:     int i, id, id_max = 0;
        !           194: 
        !           195:     for(i = 0; i < s->nb_snapshots; i++) {
        !           196:         sn = s->snapshots + i;
        !           197:         id = strtoul(sn->id_str, NULL, 10);
        !           198:         if (id > id_max)
        !           199:             id_max = id;
        !           200:     }
        !           201:     snprintf(id_str, id_str_size, "%d", id_max + 1);
        !           202: }
        !           203: 
        !           204: static int find_snapshot_by_id(BlockDriverState *bs, const char *id_str)
        !           205: {
        !           206:     BDRVQcowState *s = bs->opaque;
        !           207:     int i;
        !           208: 
        !           209:     for(i = 0; i < s->nb_snapshots; i++) {
        !           210:         if (!strcmp(s->snapshots[i].id_str, id_str))
        !           211:             return i;
        !           212:     }
        !           213:     return -1;
        !           214: }
        !           215: 
        !           216: static int find_snapshot_by_id_or_name(BlockDriverState *bs, const char *name)
        !           217: {
        !           218:     BDRVQcowState *s = bs->opaque;
        !           219:     int i, ret;
        !           220: 
        !           221:     ret = find_snapshot_by_id(bs, name);
        !           222:     if (ret >= 0)
        !           223:         return ret;
        !           224:     for(i = 0; i < s->nb_snapshots; i++) {
        !           225:         if (!strcmp(s->snapshots[i].name, name))
        !           226:             return i;
        !           227:     }
        !           228:     return -1;
        !           229: }
        !           230: 
        !           231: /* if no id is provided, a new one is constructed */
        !           232: int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info)
        !           233: {
        !           234:     BDRVQcowState *s = bs->opaque;
        !           235:     QCowSnapshot *snapshots1, sn1, *sn = &sn1;
        !           236:     int i, ret;
        !           237:     uint64_t *l1_table = NULL;
        !           238: 
        !           239:     memset(sn, 0, sizeof(*sn));
        !           240: 
        !           241:     if (sn_info->id_str[0] == '\0') {
        !           242:         /* compute a new id */
        !           243:         find_new_snapshot_id(bs, sn_info->id_str, sizeof(sn_info->id_str));
        !           244:     }
        !           245: 
        !           246:     /* check that the ID is unique */
        !           247:     if (find_snapshot_by_id(bs, sn_info->id_str) >= 0)
        !           248:         return -ENOENT;
        !           249: 
        !           250:     sn->id_str = qemu_strdup(sn_info->id_str);
        !           251:     if (!sn->id_str)
        !           252:         goto fail;
        !           253:     sn->name = qemu_strdup(sn_info->name);
        !           254:     if (!sn->name)
        !           255:         goto fail;
        !           256:     sn->vm_state_size = sn_info->vm_state_size;
        !           257:     sn->date_sec = sn_info->date_sec;
        !           258:     sn->date_nsec = sn_info->date_nsec;
        !           259:     sn->vm_clock_nsec = sn_info->vm_clock_nsec;
        !           260: 
        !           261:     ret = qcow2_update_snapshot_refcount(bs, s->l1_table_offset, s->l1_size, 1);
        !           262:     if (ret < 0)
        !           263:         goto fail;
        !           264: 
        !           265:     /* create the L1 table of the snapshot */
        !           266:     sn->l1_table_offset = qcow2_alloc_clusters(bs, s->l1_size * sizeof(uint64_t));
        !           267:     sn->l1_size = s->l1_size;
        !           268: 
        !           269:     l1_table = qemu_malloc(s->l1_size * sizeof(uint64_t));
        !           270:     for(i = 0; i < s->l1_size; i++) {
        !           271:         l1_table[i] = cpu_to_be64(s->l1_table[i]);
        !           272:     }
        !           273:     if (bdrv_pwrite(s->hd, sn->l1_table_offset,
        !           274:                     l1_table, s->l1_size * sizeof(uint64_t)) !=
        !           275:         (s->l1_size * sizeof(uint64_t)))
        !           276:         goto fail;
        !           277:     qemu_free(l1_table);
        !           278:     l1_table = NULL;
        !           279: 
        !           280:     snapshots1 = qemu_malloc((s->nb_snapshots + 1) * sizeof(QCowSnapshot));
        !           281:     if (s->snapshots) {
        !           282:         memcpy(snapshots1, s->snapshots, s->nb_snapshots * sizeof(QCowSnapshot));
        !           283:         qemu_free(s->snapshots);
        !           284:     }
        !           285:     s->snapshots = snapshots1;
        !           286:     s->snapshots[s->nb_snapshots++] = *sn;
        !           287: 
        !           288:     if (qcow_write_snapshots(bs) < 0)
        !           289:         goto fail;
        !           290: #ifdef DEBUG_ALLOC
        !           291:     qcow2_check_refcounts(bs);
        !           292: #endif
        !           293:     return 0;
        !           294:  fail:
        !           295:     qemu_free(sn->name);
        !           296:     qemu_free(l1_table);
        !           297:     return -1;
        !           298: }
        !           299: 
        !           300: /* copy the snapshot 'snapshot_name' into the current disk image */
        !           301: int qcow2_snapshot_goto(BlockDriverState *bs, const char *snapshot_id)
        !           302: {
        !           303:     BDRVQcowState *s = bs->opaque;
        !           304:     QCowSnapshot *sn;
        !           305:     int i, snapshot_index, l1_size2;
        !           306: 
        !           307:     snapshot_index = find_snapshot_by_id_or_name(bs, snapshot_id);
        !           308:     if (snapshot_index < 0)
        !           309:         return -ENOENT;
        !           310:     sn = &s->snapshots[snapshot_index];
        !           311: 
        !           312:     if (qcow2_update_snapshot_refcount(bs, s->l1_table_offset, s->l1_size, -1) < 0)
        !           313:         goto fail;
        !           314: 
        !           315:     if (qcow2_grow_l1_table(bs, sn->l1_size) < 0)
        !           316:         goto fail;
        !           317: 
        !           318:     s->l1_size = sn->l1_size;
        !           319:     l1_size2 = s->l1_size * sizeof(uint64_t);
        !           320:     /* copy the snapshot l1 table to the current l1 table */
        !           321:     if (bdrv_pread(s->hd, sn->l1_table_offset,
        !           322:                    s->l1_table, l1_size2) != l1_size2)
        !           323:         goto fail;
        !           324:     if (bdrv_pwrite(s->hd, s->l1_table_offset,
        !           325:                     s->l1_table, l1_size2) != l1_size2)
        !           326:         goto fail;
        !           327:     for(i = 0;i < s->l1_size; i++) {
        !           328:         be64_to_cpus(&s->l1_table[i]);
        !           329:     }
        !           330: 
        !           331:     if (qcow2_update_snapshot_refcount(bs, s->l1_table_offset, s->l1_size, 1) < 0)
        !           332:         goto fail;
        !           333: 
        !           334: #ifdef DEBUG_ALLOC
        !           335:     qcow2_check_refcounts(bs);
        !           336: #endif
        !           337:     return 0;
        !           338:  fail:
        !           339:     return -EIO;
        !           340: }
        !           341: 
        !           342: int qcow2_snapshot_delete(BlockDriverState *bs, const char *snapshot_id)
        !           343: {
        !           344:     BDRVQcowState *s = bs->opaque;
        !           345:     QCowSnapshot *sn;
        !           346:     int snapshot_index, ret;
        !           347: 
        !           348:     snapshot_index = find_snapshot_by_id_or_name(bs, snapshot_id);
        !           349:     if (snapshot_index < 0)
        !           350:         return -ENOENT;
        !           351:     sn = &s->snapshots[snapshot_index];
        !           352: 
        !           353:     ret = qcow2_update_snapshot_refcount(bs, sn->l1_table_offset, sn->l1_size, -1);
        !           354:     if (ret < 0)
        !           355:         return ret;
        !           356:     /* must update the copied flag on the current cluster offsets */
        !           357:     ret = qcow2_update_snapshot_refcount(bs, s->l1_table_offset, s->l1_size, 0);
        !           358:     if (ret < 0)
        !           359:         return ret;
        !           360:     qcow2_free_clusters(bs, sn->l1_table_offset, sn->l1_size * sizeof(uint64_t));
        !           361: 
        !           362:     qemu_free(sn->id_str);
        !           363:     qemu_free(sn->name);
        !           364:     memmove(sn, sn + 1, (s->nb_snapshots - snapshot_index - 1) * sizeof(*sn));
        !           365:     s->nb_snapshots--;
        !           366:     ret = qcow_write_snapshots(bs);
        !           367:     if (ret < 0) {
        !           368:         /* XXX: restore snapshot if error ? */
        !           369:         return ret;
        !           370:     }
        !           371: #ifdef DEBUG_ALLOC
        !           372:     qcow2_check_refcounts(bs);
        !           373: #endif
        !           374:     return 0;
        !           375: }
        !           376: 
        !           377: int qcow2_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_tab)
        !           378: {
        !           379:     BDRVQcowState *s = bs->opaque;
        !           380:     QEMUSnapshotInfo *sn_tab, *sn_info;
        !           381:     QCowSnapshot *sn;
        !           382:     int i;
        !           383: 
        !           384:     if (!s->nb_snapshots) {
        !           385:         *psn_tab = NULL;
        !           386:         return s->nb_snapshots;
        !           387:     }
        !           388: 
        !           389:     sn_tab = qemu_mallocz(s->nb_snapshots * sizeof(QEMUSnapshotInfo));
        !           390:     for(i = 0; i < s->nb_snapshots; i++) {
        !           391:         sn_info = sn_tab + i;
        !           392:         sn = s->snapshots + i;
        !           393:         pstrcpy(sn_info->id_str, sizeof(sn_info->id_str),
        !           394:                 sn->id_str);
        !           395:         pstrcpy(sn_info->name, sizeof(sn_info->name),
        !           396:                 sn->name);
        !           397:         sn_info->vm_state_size = sn->vm_state_size;
        !           398:         sn_info->date_sec = sn->date_sec;
        !           399:         sn_info->date_nsec = sn->date_nsec;
        !           400:         sn_info->vm_clock_nsec = sn->vm_clock_nsec;
        !           401:     }
        !           402:     *psn_tab = sn_tab;
        !           403:     return s->nb_snapshots;
        !           404: }
        !           405: 

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.