root / block / snapshot.c @ 8c116b0e
History | View | Annotate | Download (10.7 kB)
1 |
/*
|
---|---|
2 |
* Block layer snapshot related functions
|
3 |
*
|
4 |
* Copyright (c) 2003-2008 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 "block/snapshot.h" |
26 |
#include "block/block_int.h" |
27 |
|
28 |
QemuOptsList internal_snapshot_opts = { |
29 |
.name = "snapshot",
|
30 |
.head = QTAILQ_HEAD_INITIALIZER(internal_snapshot_opts.head), |
31 |
.desc = { |
32 |
{ |
33 |
.name = SNAPSHOT_OPT_ID, |
34 |
.type = QEMU_OPT_STRING, |
35 |
.help = "snapshot id"
|
36 |
},{ |
37 |
.name = SNAPSHOT_OPT_NAME, |
38 |
.type = QEMU_OPT_STRING, |
39 |
.help = "snapshot name"
|
40 |
},{ |
41 |
/* end of list */
|
42 |
} |
43 |
}, |
44 |
}; |
45 |
|
46 |
int bdrv_snapshot_find(BlockDriverState *bs, QEMUSnapshotInfo *sn_info,
|
47 |
const char *name) |
48 |
{ |
49 |
QEMUSnapshotInfo *sn_tab, *sn; |
50 |
int nb_sns, i, ret;
|
51 |
|
52 |
ret = -ENOENT; |
53 |
nb_sns = bdrv_snapshot_list(bs, &sn_tab); |
54 |
if (nb_sns < 0) { |
55 |
return ret;
|
56 |
} |
57 |
for (i = 0; i < nb_sns; i++) { |
58 |
sn = &sn_tab[i]; |
59 |
if (!strcmp(sn->id_str, name) || !strcmp(sn->name, name)) {
|
60 |
*sn_info = *sn; |
61 |
ret = 0;
|
62 |
break;
|
63 |
} |
64 |
} |
65 |
g_free(sn_tab); |
66 |
return ret;
|
67 |
} |
68 |
|
69 |
/**
|
70 |
* Look up an internal snapshot by @id and @name.
|
71 |
* @bs: block device to search
|
72 |
* @id: unique snapshot ID, or NULL
|
73 |
* @name: snapshot name, or NULL
|
74 |
* @sn_info: location to store information on the snapshot found
|
75 |
* @errp: location to store error, will be set only for exception
|
76 |
*
|
77 |
* This function will traverse snapshot list in @bs to search the matching
|
78 |
* one, @id and @name are the matching condition:
|
79 |
* If both @id and @name are specified, find the first one with id @id and
|
80 |
* name @name.
|
81 |
* If only @id is specified, find the first one with id @id.
|
82 |
* If only @name is specified, find the first one with name @name.
|
83 |
* if none is specified, abort().
|
84 |
*
|
85 |
* Returns: true when a snapshot is found and @sn_info will be filled, false
|
86 |
* when error or not found. If all operation succeed but no matching one is
|
87 |
* found, @errp will NOT be set.
|
88 |
*/
|
89 |
bool bdrv_snapshot_find_by_id_and_name(BlockDriverState *bs,
|
90 |
const char *id, |
91 |
const char *name, |
92 |
QEMUSnapshotInfo *sn_info, |
93 |
Error **errp) |
94 |
{ |
95 |
QEMUSnapshotInfo *sn_tab, *sn; |
96 |
int nb_sns, i;
|
97 |
bool ret = false; |
98 |
|
99 |
assert(id || name); |
100 |
|
101 |
nb_sns = bdrv_snapshot_list(bs, &sn_tab); |
102 |
if (nb_sns < 0) { |
103 |
error_setg_errno(errp, -nb_sns, "Failed to get a snapshot list");
|
104 |
return false; |
105 |
} else if (nb_sns == 0) { |
106 |
return false; |
107 |
} |
108 |
|
109 |
if (id && name) {
|
110 |
for (i = 0; i < nb_sns; i++) { |
111 |
sn = &sn_tab[i]; |
112 |
if (!strcmp(sn->id_str, id) && !strcmp(sn->name, name)) {
|
113 |
*sn_info = *sn; |
114 |
ret = true;
|
115 |
break;
|
116 |
} |
117 |
} |
118 |
} else if (id) { |
119 |
for (i = 0; i < nb_sns; i++) { |
120 |
sn = &sn_tab[i]; |
121 |
if (!strcmp(sn->id_str, id)) {
|
122 |
*sn_info = *sn; |
123 |
ret = true;
|
124 |
break;
|
125 |
} |
126 |
} |
127 |
} else if (name) { |
128 |
for (i = 0; i < nb_sns; i++) { |
129 |
sn = &sn_tab[i]; |
130 |
if (!strcmp(sn->name, name)) {
|
131 |
*sn_info = *sn; |
132 |
ret = true;
|
133 |
break;
|
134 |
} |
135 |
} |
136 |
} |
137 |
|
138 |
g_free(sn_tab); |
139 |
return ret;
|
140 |
} |
141 |
|
142 |
int bdrv_can_snapshot(BlockDriverState *bs)
|
143 |
{ |
144 |
BlockDriver *drv = bs->drv; |
145 |
if (!drv || !bdrv_is_inserted(bs) || bdrv_is_read_only(bs)) {
|
146 |
return 0; |
147 |
} |
148 |
|
149 |
if (!drv->bdrv_snapshot_create) {
|
150 |
if (bs->file != NULL) { |
151 |
return bdrv_can_snapshot(bs->file);
|
152 |
} |
153 |
return 0; |
154 |
} |
155 |
|
156 |
return 1; |
157 |
} |
158 |
|
159 |
int bdrv_snapshot_create(BlockDriverState *bs,
|
160 |
QEMUSnapshotInfo *sn_info) |
161 |
{ |
162 |
BlockDriver *drv = bs->drv; |
163 |
if (!drv) {
|
164 |
return -ENOMEDIUM;
|
165 |
} |
166 |
if (drv->bdrv_snapshot_create) {
|
167 |
return drv->bdrv_snapshot_create(bs, sn_info);
|
168 |
} |
169 |
if (bs->file) {
|
170 |
return bdrv_snapshot_create(bs->file, sn_info);
|
171 |
} |
172 |
return -ENOTSUP;
|
173 |
} |
174 |
|
175 |
int bdrv_snapshot_goto(BlockDriverState *bs,
|
176 |
const char *snapshot_id) |
177 |
{ |
178 |
BlockDriver *drv = bs->drv; |
179 |
int ret, open_ret;
|
180 |
|
181 |
if (!drv) {
|
182 |
return -ENOMEDIUM;
|
183 |
} |
184 |
if (drv->bdrv_snapshot_goto) {
|
185 |
return drv->bdrv_snapshot_goto(bs, snapshot_id);
|
186 |
} |
187 |
|
188 |
if (bs->file) {
|
189 |
drv->bdrv_close(bs); |
190 |
ret = bdrv_snapshot_goto(bs->file, snapshot_id); |
191 |
open_ret = drv->bdrv_open(bs, NULL, bs->open_flags, NULL); |
192 |
if (open_ret < 0) { |
193 |
bdrv_unref(bs->file); |
194 |
bs->drv = NULL;
|
195 |
return open_ret;
|
196 |
} |
197 |
return ret;
|
198 |
} |
199 |
|
200 |
return -ENOTSUP;
|
201 |
} |
202 |
|
203 |
/**
|
204 |
* Delete an internal snapshot by @snapshot_id and @name.
|
205 |
* @bs: block device used in the operation
|
206 |
* @snapshot_id: unique snapshot ID, or NULL
|
207 |
* @name: snapshot name, or NULL
|
208 |
* @errp: location to store error
|
209 |
*
|
210 |
* If both @snapshot_id and @name are specified, delete the first one with
|
211 |
* id @snapshot_id and name @name.
|
212 |
* If only @snapshot_id is specified, delete the first one with id
|
213 |
* @snapshot_id.
|
214 |
* If only @name is specified, delete the first one with name @name.
|
215 |
* if none is specified, return -EINVAL.
|
216 |
*
|
217 |
* Returns: 0 on success, -errno on failure. If @bs is not inserted, return
|
218 |
* -ENOMEDIUM. If @snapshot_id and @name are both NULL, return -EINVAL. If @bs
|
219 |
* does not support internal snapshot deletion, return -ENOTSUP. If @bs does
|
220 |
* not support parameter @snapshot_id or @name, or one of them is not correctly
|
221 |
* specified, return -EINVAL. If @bs can't find one matching @id and @name,
|
222 |
* return -ENOENT. If @errp != NULL, it will always be filled with error
|
223 |
* message on failure.
|
224 |
*/
|
225 |
int bdrv_snapshot_delete(BlockDriverState *bs,
|
226 |
const char *snapshot_id, |
227 |
const char *name, |
228 |
Error **errp) |
229 |
{ |
230 |
BlockDriver *drv = bs->drv; |
231 |
if (!drv) {
|
232 |
error_set(errp, QERR_DEVICE_HAS_NO_MEDIUM, bdrv_get_device_name(bs)); |
233 |
return -ENOMEDIUM;
|
234 |
} |
235 |
if (!snapshot_id && !name) {
|
236 |
error_setg(errp, "snapshot_id and name are both NULL");
|
237 |
return -EINVAL;
|
238 |
} |
239 |
if (drv->bdrv_snapshot_delete) {
|
240 |
return drv->bdrv_snapshot_delete(bs, snapshot_id, name, errp);
|
241 |
} |
242 |
if (bs->file) {
|
243 |
return bdrv_snapshot_delete(bs->file, snapshot_id, name, errp);
|
244 |
} |
245 |
error_set(errp, QERR_BLOCK_FORMAT_FEATURE_NOT_SUPPORTED, |
246 |
drv->format_name, bdrv_get_device_name(bs), |
247 |
"internal snapshot deletion");
|
248 |
return -ENOTSUP;
|
249 |
} |
250 |
|
251 |
void bdrv_snapshot_delete_by_id_or_name(BlockDriverState *bs,
|
252 |
const char *id_or_name, |
253 |
Error **errp) |
254 |
{ |
255 |
int ret;
|
256 |
Error *local_err = NULL;
|
257 |
|
258 |
ret = bdrv_snapshot_delete(bs, id_or_name, NULL, &local_err);
|
259 |
if (ret == -ENOENT || ret == -EINVAL) {
|
260 |
error_free(local_err); |
261 |
local_err = NULL;
|
262 |
ret = bdrv_snapshot_delete(bs, NULL, id_or_name, &local_err);
|
263 |
} |
264 |
|
265 |
if (ret < 0) { |
266 |
error_propagate(errp, local_err); |
267 |
} |
268 |
} |
269 |
|
270 |
int bdrv_snapshot_list(BlockDriverState *bs,
|
271 |
QEMUSnapshotInfo **psn_info) |
272 |
{ |
273 |
BlockDriver *drv = bs->drv; |
274 |
if (!drv) {
|
275 |
return -ENOMEDIUM;
|
276 |
} |
277 |
if (drv->bdrv_snapshot_list) {
|
278 |
return drv->bdrv_snapshot_list(bs, psn_info);
|
279 |
} |
280 |
if (bs->file) {
|
281 |
return bdrv_snapshot_list(bs->file, psn_info);
|
282 |
} |
283 |
return -ENOTSUP;
|
284 |
} |
285 |
|
286 |
/**
|
287 |
* Temporarily load an internal snapshot by @snapshot_id and @name.
|
288 |
* @bs: block device used in the operation
|
289 |
* @snapshot_id: unique snapshot ID, or NULL
|
290 |
* @name: snapshot name, or NULL
|
291 |
* @errp: location to store error
|
292 |
*
|
293 |
* If both @snapshot_id and @name are specified, load the first one with
|
294 |
* id @snapshot_id and name @name.
|
295 |
* If only @snapshot_id is specified, load the first one with id
|
296 |
* @snapshot_id.
|
297 |
* If only @name is specified, load the first one with name @name.
|
298 |
* if none is specified, return -EINVAL.
|
299 |
*
|
300 |
* Returns: 0 on success, -errno on fail. If @bs is not inserted, return
|
301 |
* -ENOMEDIUM. If @bs is not readonly, return -EINVAL. If @bs did not support
|
302 |
* internal snapshot, return -ENOTSUP. If qemu can't find a matching @id and
|
303 |
* @name, return -ENOENT. If @errp != NULL, it will always be filled on
|
304 |
* failure.
|
305 |
*/
|
306 |
int bdrv_snapshot_load_tmp(BlockDriverState *bs,
|
307 |
const char *snapshot_id, |
308 |
const char *name, |
309 |
Error **errp) |
310 |
{ |
311 |
BlockDriver *drv = bs->drv; |
312 |
|
313 |
if (!drv) {
|
314 |
error_set(errp, QERR_DEVICE_HAS_NO_MEDIUM, bdrv_get_device_name(bs)); |
315 |
return -ENOMEDIUM;
|
316 |
} |
317 |
if (!snapshot_id && !name) {
|
318 |
error_setg(errp, "snapshot_id and name are both NULL");
|
319 |
return -EINVAL;
|
320 |
} |
321 |
if (!bs->read_only) {
|
322 |
error_setg(errp, "Device is not readonly");
|
323 |
return -EINVAL;
|
324 |
} |
325 |
if (drv->bdrv_snapshot_load_tmp) {
|
326 |
return drv->bdrv_snapshot_load_tmp(bs, snapshot_id, name, errp);
|
327 |
} |
328 |
error_set(errp, QERR_BLOCK_FORMAT_FEATURE_NOT_SUPPORTED, |
329 |
drv->format_name, bdrv_get_device_name(bs), |
330 |
"temporarily load internal snapshot");
|
331 |
return -ENOTSUP;
|
332 |
} |
333 |
|
334 |
int bdrv_snapshot_load_tmp_by_id_or_name(BlockDriverState *bs,
|
335 |
const char *id_or_name, |
336 |
Error **errp) |
337 |
{ |
338 |
int ret;
|
339 |
Error *local_err = NULL;
|
340 |
|
341 |
ret = bdrv_snapshot_load_tmp(bs, id_or_name, NULL, &local_err);
|
342 |
if (ret == -ENOENT || ret == -EINVAL) {
|
343 |
error_free(local_err); |
344 |
local_err = NULL;
|
345 |
ret = bdrv_snapshot_load_tmp(bs, NULL, id_or_name, &local_err);
|
346 |
} |
347 |
|
348 |
if (error_is_set(&local_err)) {
|
349 |
error_propagate(errp, local_err); |
350 |
} |
351 |
|
352 |
return ret;
|
353 |
} |