1 /* Copyright (c) 2008, XenSource Inc.
2 * All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are met:
6 * * Redistributions of source code must retain the above copyright
7 * notice, this list of conditions and the following disclaimer.
8 * * Redistributions in binary form must reproduce the above copyright
9 * notice, this list of conditions and the following disclaimer in the
10 * documentation and/or other materials provided with the distribution.
11 * * Neither the name of XenSource Inc. nor the names of its contributors
12 * may be used to endorse or promote products derived from this software
13 * without specific prior written permission.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
18 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
19 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
23 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
24 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27 #include <errno.h>
28 #include <stdio.h>
29 #include <fcntl.h>
30 #include <stdlib.h>
31 #include <unistd.h>
32
33 #include "libvhd.h"
34
35 static int
vhd_util_find_snapshot_target(const char * name,char ** result,int * parent_raw)36 vhd_util_find_snapshot_target(const char *name, char **result, int *parent_raw)
37 {
38 int i, err;
39 char *target;
40 vhd_context_t vhd;
41
42 *parent_raw = 0;
43 *result = NULL;
44
45 target = strdup(name);
46 if (!target)
47 return -ENOMEM;
48
49 for (;;) {
50 err = vhd_open(&vhd, target, VHD_OPEN_RDONLY);
51 if (err)
52 return err;
53
54 if (vhd.footer.type != HD_TYPE_DIFF)
55 goto out;
56
57 err = vhd_get_bat(&vhd);
58 if (err)
59 goto out;
60
61 for (i = 0; i < vhd.bat.entries; i++)
62 if (vhd.bat.bat[i] != DD_BLK_UNUSED)
63 goto out;
64
65 free(target);
66 err = vhd_parent_locator_get(&vhd, &target);
67 if (err)
68 goto out;
69
70 if (vhd_parent_raw(&vhd)) {
71 *parent_raw = 1;
72 goto out;
73 }
74
75 vhd_close(&vhd);
76 }
77
78 out:
79 vhd_close(&vhd);
80 if (err)
81 free(target);
82 else
83 *result = target;
84
85 return err;
86 }
87
88 static int
vhd_util_check_depth(const char * name,int * depth)89 vhd_util_check_depth(const char *name, int *depth)
90 {
91 int err;
92 vhd_context_t vhd;
93
94 err = vhd_open(&vhd, name, VHD_OPEN_RDONLY);
95 if (err)
96 return err;
97
98 err = vhd_chain_depth(&vhd, depth);
99 vhd_close(&vhd);
100
101 return err;
102 }
103
104 int
vhd_util_snapshot(int argc,char ** argv)105 vhd_util_snapshot(int argc, char **argv)
106 {
107 vhd_flag_creat_t flags;
108 int c, err, prt_raw, limit;
109 char *name, *pname, *ppath, *backing;
110 uint64_t size;
111 vhd_context_t vhd;
112
113 name = NULL;
114 pname = NULL;
115 ppath = NULL;
116 backing = NULL;
117 size = 0;
118 flags = 0;
119 limit = 0;
120
121 if (!argc || !argv) {
122 err = -EINVAL;
123 goto usage;
124 }
125
126 optind = 0;
127 while ((c = getopt(argc, argv, "n:p:l:mh")) != -1) {
128 switch (c) {
129 case 'n':
130 name = optarg;
131 break;
132 case 'p':
133 pname = optarg;
134 break;
135 case 'l':
136 limit = strtol(optarg, NULL, 10);
137 break;
138 case 'm':
139 vhd_flag_set(flags, VHD_FLAG_CREAT_PARENT_RAW);
140 break;
141 case 'h':
142 err = 0;
143 goto usage;
144 default:
145 err = -EINVAL;
146 goto usage;
147 }
148 }
149
150 if (!name || !pname || optind != argc) {
151 err = -EINVAL;
152 goto usage;
153 }
154
155 ppath = realpath(pname, NULL);
156 if (!ppath)
157 return -errno;
158
159 if (vhd_flag_test(flags, VHD_FLAG_CREAT_PARENT_RAW)) {
160 backing = strdup(ppath);
161 if (!backing) {
162 err = -ENOMEM;
163 goto out;
164 }
165 } else {
166 err = vhd_util_find_snapshot_target(ppath, &backing, &prt_raw);
167 if (err) {
168 backing = NULL;
169 goto out;
170 }
171
172 /*
173 * if the sizes of the parent chain are non-uniform, we need to
174 * pick the right size: that of the supplied parent
175 */
176 if (strcmp(ppath, backing)) {
177 err = vhd_open(&vhd, ppath, VHD_OPEN_RDONLY);
178 if (err)
179 goto out;
180 size = vhd.footer.curr_size;
181 vhd_close(&vhd);
182 }
183
184 if (prt_raw)
185 vhd_flag_set(flags, VHD_FLAG_CREAT_PARENT_RAW);
186 }
187
188 if (limit && !vhd_flag_test(flags, VHD_FLAG_CREAT_PARENT_RAW)) {
189 int depth;
190
191 err = vhd_util_check_depth(backing, &depth);
192 if (err)
193 printf("error checking snapshot depth: %d\n", err);
194 else if (depth + 1 > limit) {
195 err = -ENOSPC;
196 printf("snapshot depth exceeded: "
197 "current depth: %d, limit: %d\n", depth, limit);
198 }
199
200 if (err)
201 goto out;
202 }
203
204 err = vhd_snapshot(name, size, backing, flags);
205
206 out:
207 free(ppath);
208 free(backing);
209
210 return err;
211
212 usage:
213 printf("options: <-n name> <-p parent name> [-l snapshot depth limit]"
214 " [-m parent_is_raw] [-h help]\n");
215 return err;
216 }
217