author | eschrock |
Tue, 05 Sep 2006 11:37:36 -0700 | |
changeset 2676 | 5cee47eddab6 |
parent 2379 | 0ff8239b1e93 |
child 3635 | 63e2169c1249 |
permissions | -rw-r--r-- |
789 | 1 |
/* |
2 |
* CDDL HEADER START |
|
3 |
* |
|
4 |
* The contents of this file are subject to the terms of the |
|
2082 | 5 |
* Common Development and Distribution License (the "License"). |
6 |
* You may not use this file except in compliance with the License. |
|
789 | 7 |
* |
8 |
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE |
|
9 |
* or http://www.opensolaris.org/os/licensing. |
|
10 |
* See the License for the specific language governing permissions |
|
11 |
* and limitations under the License. |
|
12 |
* |
|
13 |
* When distributing Covered Code, include this CDDL HEADER in each |
|
14 |
* file and include the License file at usr/src/OPENSOLARIS.LICENSE. |
|
15 |
* If applicable, add the following below this CDDL HEADER, with the |
|
16 |
* fields enclosed by brackets "[]" replaced with your own identifying |
|
17 |
* information: Portions Copyright [yyyy] [name of copyright owner] |
|
18 |
* |
|
19 |
* CDDL HEADER END |
|
20 |
*/ |
|
21 |
/* |
|
1356
e021b5e4aa0e
6377671 zfs mount -a shouldn't bother checking snapshots
eschrock
parents:
789
diff
changeset
|
22 |
* Copyright 2006 Sun Microsystems, Inc. All rights reserved. |
789 | 23 |
* Use is subject to license terms. |
24 |
*/ |
|
25 |
||
26 |
#pragma ident "%Z%%M% %I% %E% SMI" |
|
27 |
||
28 |
#include <libintl.h> |
|
29 |
#include <libuutil.h> |
|
30 |
#include <stddef.h> |
|
31 |
#include <stdio.h> |
|
32 |
#include <stdlib.h> |
|
33 |
#include <strings.h> |
|
34 |
||
35 |
#include <libzfs.h> |
|
36 |
||
37 |
#include "zfs_util.h" |
|
2379 | 38 |
#include "zfs_iter.h" |
789 | 39 |
|
40 |
/* |
|
41 |
* This is a private interface used to gather up all the datasets specified on |
|
42 |
* the command line so that we can iterate over them in order. |
|
43 |
* |
|
44 |
* First, we iterate over all filesystems, gathering them together into an |
|
2379 | 45 |
* AVL tree. We report errors for any explicitly specified datasets |
789 | 46 |
* that we couldn't open. |
47 |
* |
|
48 |
* When finished, we have an AVL tree of ZFS handles. We go through and execute |
|
49 |
* the provided callback for each one, passing whatever data the user supplied. |
|
50 |
*/ |
|
51 |
||
52 |
typedef struct zfs_node { |
|
53 |
zfs_handle_t *zn_handle; |
|
54 |
uu_avl_node_t zn_avlnode; |
|
55 |
} zfs_node_t; |
|
56 |
||
57 |
typedef struct callback_data { |
|
58 |
uu_avl_t *cb_avl; |
|
59 |
int cb_recurse; |
|
60 |
zfs_type_t cb_types; |
|
2379 | 61 |
zfs_sort_column_t *cb_sortcol; |
2676 | 62 |
zfs_proplist_t **cb_proplist; |
789 | 63 |
} callback_data_t; |
64 |
||
65 |
uu_avl_pool_t *avl_pool; |
|
66 |
||
67 |
/* |
|
68 |
* Called for each dataset. If the object the object is of an appropriate type, |
|
69 |
* add it to the avl tree and recurse over any children as necessary. |
|
70 |
*/ |
|
71 |
int |
|
72 |
zfs_callback(zfs_handle_t *zhp, void *data) |
|
73 |
{ |
|
74 |
callback_data_t *cb = data; |
|
75 |
int dontclose = 0; |
|
76 |
||
77 |
/* |
|
78 |
* If this object is of the appropriate type, add it to the AVL tree. |
|
79 |
*/ |
|
80 |
if (zfs_get_type(zhp) & cb->cb_types) { |
|
81 |
uu_avl_index_t idx; |
|
82 |
zfs_node_t *node = safe_malloc(sizeof (zfs_node_t)); |
|
83 |
||
84 |
node->zn_handle = zhp; |
|
85 |
uu_avl_node_init(node, &node->zn_avlnode, avl_pool); |
|
2379 | 86 |
if (uu_avl_find(cb->cb_avl, node, cb->cb_sortcol, |
87 |
&idx) == NULL) { |
|
2676 | 88 |
if (cb->cb_proplist && |
89 |
zfs_expand_proplist(zhp, cb->cb_proplist) != 0) { |
|
90 |
free(node); |
|
91 |
return (-1); |
|
92 |
} |
|
789 | 93 |
uu_avl_insert(cb->cb_avl, node, idx); |
94 |
dontclose = 1; |
|
95 |
} else { |
|
96 |
free(node); |
|
97 |
} |
|
98 |
} |
|
99 |
||
100 |
/* |
|
1356
e021b5e4aa0e
6377671 zfs mount -a shouldn't bother checking snapshots
eschrock
parents:
789
diff
changeset
|
101 |
* Recurse if necessary. |
789 | 102 |
*/ |
103 |
if (cb->cb_recurse && (zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM || |
|
1356
e021b5e4aa0e
6377671 zfs mount -a shouldn't bother checking snapshots
eschrock
parents:
789
diff
changeset
|
104 |
(zfs_get_type(zhp) == ZFS_TYPE_VOLUME && (cb->cb_types & |
e021b5e4aa0e
6377671 zfs mount -a shouldn't bother checking snapshots
eschrock
parents:
789
diff
changeset
|
105 |
ZFS_TYPE_SNAPSHOT)))) |
789 | 106 |
(void) zfs_iter_children(zhp, zfs_callback, data); |
107 |
||
108 |
if (!dontclose) |
|
109 |
zfs_close(zhp); |
|
110 |
||
111 |
return (0); |
|
112 |
} |
|
113 |
||
2676 | 114 |
int |
115 |
zfs_add_sort_column(zfs_sort_column_t **sc, const char *name, |
|
2379 | 116 |
boolean_t reverse) |
117 |
{ |
|
118 |
zfs_sort_column_t *col; |
|
2676 | 119 |
zfs_prop_t prop; |
120 |
||
121 |
if ((prop = zfs_name_to_prop(name)) == ZFS_PROP_INVAL && |
|
122 |
!zfs_prop_user(name)) |
|
123 |
return (-1); |
|
2379 | 124 |
|
125 |
col = safe_malloc(sizeof (zfs_sort_column_t)); |
|
126 |
||
127 |
col->sc_prop = prop; |
|
128 |
col->sc_reverse = reverse; |
|
2676 | 129 |
if (prop == ZFS_PROP_INVAL) { |
130 |
col->sc_user_prop = safe_malloc(strlen(name) + 1); |
|
131 |
(void) strcpy(col->sc_user_prop, name); |
|
132 |
} |
|
2379 | 133 |
|
134 |
if (*sc == NULL) { |
|
135 |
col->sc_last = col; |
|
136 |
*sc = col; |
|
137 |
} else { |
|
138 |
(*sc)->sc_last->sc_next = col; |
|
139 |
(*sc)->sc_last = col; |
|
140 |
} |
|
2676 | 141 |
|
142 |
return (0); |
|
2379 | 143 |
} |
144 |
||
145 |
void |
|
146 |
zfs_free_sort_columns(zfs_sort_column_t *sc) |
|
147 |
{ |
|
148 |
zfs_sort_column_t *col; |
|
149 |
||
150 |
while (sc != NULL) { |
|
151 |
col = sc->sc_next; |
|
2676 | 152 |
free(sc->sc_user_prop); |
2379 | 153 |
free(sc); |
154 |
sc = col; |
|
155 |
} |
|
156 |
} |
|
157 |
||
789 | 158 |
/* ARGSUSED */ |
159 |
static int |
|
160 |
zfs_compare(const void *larg, const void *rarg, void *unused) |
|
161 |
{ |
|
162 |
zfs_handle_t *l = ((zfs_node_t *)larg)->zn_handle; |
|
163 |
zfs_handle_t *r = ((zfs_node_t *)rarg)->zn_handle; |
|
164 |
const char *lname = zfs_get_name(l); |
|
165 |
const char *rname = zfs_get_name(r); |
|
166 |
char *lat, *rat; |
|
167 |
uint64_t lcreate, rcreate; |
|
168 |
int ret; |
|
169 |
||
170 |
lat = (char *)strchr(lname, '@'); |
|
171 |
rat = (char *)strchr(rname, '@'); |
|
172 |
||
173 |
if (lat != NULL) |
|
174 |
*lat = '\0'; |
|
175 |
if (rat != NULL) |
|
176 |
*rat = '\0'; |
|
177 |
||
178 |
ret = strcmp(lname, rname); |
|
179 |
if (ret == 0) { |
|
180 |
/* |
|
181 |
* If we're comparing a dataset to one of its snapshots, we |
|
182 |
* always make the full dataset first. |
|
183 |
*/ |
|
184 |
if (lat == NULL) { |
|
185 |
ret = -1; |
|
186 |
} else if (rat == NULL) { |
|
187 |
ret = 1; |
|
188 |
} else { |
|
189 |
/* |
|
190 |
* If we have two snapshots from the same dataset, then |
|
191 |
* we want to sort them according to creation time. We |
|
192 |
* use the hidden CREATETXG property to get an absolute |
|
193 |
* ordering of snapshots. |
|
194 |
*/ |
|
195 |
lcreate = zfs_prop_get_int(l, ZFS_PROP_CREATETXG); |
|
196 |
rcreate = zfs_prop_get_int(r, ZFS_PROP_CREATETXG); |
|
197 |
||
198 |
if (lcreate < rcreate) |
|
199 |
ret = -1; |
|
200 |
else if (lcreate > rcreate) |
|
201 |
ret = 1; |
|
202 |
} |
|
203 |
} |
|
204 |
||
205 |
if (lat != NULL) |
|
206 |
*lat = '@'; |
|
207 |
if (rat != NULL) |
|
208 |
*rat = '@'; |
|
209 |
||
210 |
return (ret); |
|
211 |
} |
|
212 |
||
2379 | 213 |
/* |
214 |
* Sort datasets by specified columns. |
|
215 |
* |
|
216 |
* o Numeric types sort in ascending order. |
|
217 |
* o String types sort in alphabetical order. |
|
218 |
* o Types inappropriate for a row sort that row to the literal |
|
219 |
* bottom, regardless of the specified ordering. |
|
220 |
* |
|
221 |
* If no sort columns are specified, or two datasets compare equally |
|
222 |
* across all specified columns, they are sorted alphabetically by name |
|
223 |
* with snapshots grouped under their parents. |
|
224 |
*/ |
|
225 |
static int |
|
226 |
zfs_sort(const void *larg, const void *rarg, void *data) |
|
227 |
{ |
|
228 |
zfs_handle_t *l = ((zfs_node_t *)larg)->zn_handle; |
|
229 |
zfs_handle_t *r = ((zfs_node_t *)rarg)->zn_handle; |
|
230 |
zfs_sort_column_t *sc = (zfs_sort_column_t *)data; |
|
231 |
zfs_sort_column_t *psc; |
|
232 |
||
233 |
for (psc = sc; psc != NULL; psc = psc->sc_next) { |
|
2676 | 234 |
char lbuf[ZFS_MAXPROPLEN], rbuf[ZFS_MAXPROPLEN]; |
235 |
char *lstr, *rstr; |
|
2379 | 236 |
uint64_t lnum, rnum; |
2676 | 237 |
boolean_t lvalid, rvalid; |
2379 | 238 |
int ret = 0; |
239 |
||
2676 | 240 |
/* |
241 |
* We group the checks below the generic code. If 'lstr' and |
|
242 |
* 'rstr' are non-NULL, then we do a string based comparison. |
|
243 |
* Otherwise, we compare 'lnum' and 'rnum'. |
|
244 |
*/ |
|
245 |
lstr = rstr = NULL; |
|
246 |
if (psc->sc_prop == ZFS_PROP_INVAL) { |
|
247 |
nvlist_t *luser, *ruser; |
|
248 |
nvlist_t *lval, *rval; |
|
249 |
||
250 |
luser = zfs_get_user_props(l); |
|
251 |
ruser = zfs_get_user_props(r); |
|
2379 | 252 |
|
2676 | 253 |
lvalid = (nvlist_lookup_nvlist(luser, |
254 |
psc->sc_user_prop, &lval) == 0); |
|
255 |
rvalid = (nvlist_lookup_nvlist(ruser, |
|
256 |
psc->sc_user_prop, &rval) == 0); |
|
2379 | 257 |
|
2676 | 258 |
if (lvalid) |
259 |
verify(nvlist_lookup_string(lval, |
|
260 |
ZFS_PROP_VALUE, &lstr) == 0); |
|
261 |
if (rvalid) |
|
262 |
verify(nvlist_lookup_string(rval, |
|
263 |
ZFS_PROP_VALUE, &rstr) == 0); |
|
264 |
||
265 |
} else if (zfs_prop_is_string(psc->sc_prop)) { |
|
266 |
lvalid = (zfs_prop_get(l, psc->sc_prop, lbuf, |
|
267 |
sizeof (lbuf), NULL, NULL, 0, B_TRUE) == 0); |
|
268 |
rvalid = (zfs_prop_get(r, psc->sc_prop, rbuf, |
|
269 |
sizeof (rbuf), NULL, NULL, 0, B_TRUE) == 0); |
|
270 |
||
271 |
lstr = lbuf; |
|
272 |
rstr = rbuf; |
|
2379 | 273 |
} else { |
274 |
lvalid = zfs_prop_valid_for_type(psc->sc_prop, |
|
275 |
zfs_get_type(l)); |
|
276 |
rvalid = zfs_prop_valid_for_type(psc->sc_prop, |
|
277 |
zfs_get_type(r)); |
|
278 |
||
2676 | 279 |
if (lvalid) |
280 |
(void) zfs_prop_get_numeric(l, psc->sc_prop, |
|
281 |
&lnum, NULL, NULL, 0); |
|
282 |
if (rvalid) |
|
283 |
(void) zfs_prop_get_numeric(r, psc->sc_prop, |
|
284 |
&rnum, NULL, NULL, 0); |
|
285 |
} |
|
2379 | 286 |
|
2676 | 287 |
if (!lvalid && !rvalid) |
288 |
continue; |
|
289 |
else if (!lvalid) |
|
290 |
return (1); |
|
291 |
else if (!rvalid) |
|
292 |
return (-1); |
|
2379 | 293 |
|
2676 | 294 |
if (lstr) |
295 |
ret = strcmp(lstr, rstr); |
|
296 |
if (lnum < rnum) |
|
297 |
ret = -1; |
|
298 |
else if (lnum > rnum) |
|
299 |
ret = 1; |
|
2379 | 300 |
|
301 |
if (ret != 0) { |
|
302 |
if (psc->sc_reverse == B_TRUE) |
|
303 |
ret = (ret < 0) ? 1 : -1; |
|
304 |
return (ret); |
|
305 |
} |
|
306 |
} |
|
307 |
||
308 |
return (zfs_compare(larg, rarg, NULL)); |
|
309 |
} |
|
310 |
||
789 | 311 |
int |
2082 | 312 |
zfs_for_each(int argc, char **argv, boolean_t recurse, zfs_type_t types, |
2676 | 313 |
zfs_sort_column_t *sortcol, zfs_proplist_t **proplist, zfs_iter_f callback, |
314 |
void *data) |
|
789 | 315 |
{ |
316 |
callback_data_t cb; |
|
317 |
int ret = 0; |
|
318 |
zfs_node_t *node; |
|
319 |
uu_avl_walk_t *walk; |
|
320 |
||
321 |
avl_pool = uu_avl_pool_create("zfs_pool", sizeof (zfs_node_t), |
|
2379 | 322 |
offsetof(zfs_node_t, zn_avlnode), zfs_sort, UU_DEFAULT); |
789 | 323 |
|
324 |
if (avl_pool == NULL) { |
|
325 |
(void) fprintf(stderr, |
|
326 |
gettext("internal error: out of memory\n")); |
|
327 |
exit(1); |
|
328 |
} |
|
329 |
||
2379 | 330 |
cb.cb_sortcol = sortcol; |
789 | 331 |
cb.cb_recurse = recurse; |
2676 | 332 |
cb.cb_proplist = proplist; |
789 | 333 |
cb.cb_types = types; |
334 |
if ((cb.cb_avl = uu_avl_create(avl_pool, NULL, UU_DEFAULT)) == NULL) { |
|
335 |
(void) fprintf(stderr, |
|
336 |
gettext("internal error: out of memory\n")); |
|
337 |
exit(1); |
|
338 |
} |
|
339 |
||
340 |
if (argc == 0) { |
|
341 |
/* |
|
342 |
* If given no arguments, iterate over all datasets. |
|
343 |
*/ |
|
344 |
cb.cb_recurse = 1; |
|
2082 | 345 |
ret = zfs_iter_root(g_zfs, zfs_callback, &cb); |
789 | 346 |
} else { |
347 |
int i; |
|
348 |
zfs_handle_t *zhp; |
|
349 |
zfs_type_t argtype; |
|
350 |
||
351 |
/* |
|
352 |
* If we're recursive, then we always allow filesystems as |
|
353 |
* arguments. If we also are interested in snapshots, then we |
|
354 |
* can take volumes as well. |
|
355 |
*/ |
|
356 |
argtype = types; |
|
357 |
if (recurse) { |
|
358 |
argtype |= ZFS_TYPE_FILESYSTEM; |
|
359 |
if (types & ZFS_TYPE_SNAPSHOT) |
|
360 |
argtype |= ZFS_TYPE_VOLUME; |
|
361 |
} |
|
362 |
||
363 |
for (i = 0; i < argc; i++) { |
|
2082 | 364 |
if ((zhp = zfs_open(g_zfs, argv[i], argtype)) != NULL) |
365 |
ret |= zfs_callback(zhp, &cb); |
|
789 | 366 |
else |
367 |
ret = 1; |
|
368 |
} |
|
369 |
} |
|
370 |
||
371 |
/* |
|
372 |
* At this point we've got our AVL tree full of zfs handles, so iterate |
|
373 |
* over each one and execute the real user callback. |
|
374 |
*/ |
|
375 |
for (node = uu_avl_first(cb.cb_avl); node != NULL; |
|
376 |
node = uu_avl_next(cb.cb_avl, node)) |
|
377 |
ret |= callback(node->zn_handle, data); |
|
378 |
||
379 |
/* |
|
380 |
* Finally, clean up the AVL tree. |
|
381 |
*/ |
|
382 |
if ((walk = uu_avl_walk_start(cb.cb_avl, UU_WALK_ROBUST)) == NULL) { |
|
383 |
(void) fprintf(stderr, |
|
384 |
gettext("internal error: out of memory")); |
|
385 |
exit(1); |
|
386 |
} |
|
387 |
||
388 |
while ((node = uu_avl_walk_next(walk)) != NULL) { |
|
389 |
uu_avl_remove(cb.cb_avl, node); |
|
390 |
zfs_close(node->zn_handle); |
|
391 |
free(node); |
|
392 |
} |
|
393 |
||
394 |
uu_avl_walk_end(walk); |
|
395 |
uu_avl_destroy(cb.cb_avl); |
|
396 |
uu_avl_pool_destroy(avl_pool); |
|
397 |
||
398 |
return (ret); |
|
399 |
} |