1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
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 
22 /*
23  * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
24  * Copyright (c) 2013, 2015 by Delphix. All rights reserved.
25  * Copyright (c) 2012 Pawel Jakub Dawidek. All rights reserved.
26  * Copyright 2014 Nexenta Systems, Inc.  All rights reserved.
27  */
28 
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <strings.h>
32 #include <unistd.h>
33 #include <stddef.h>
34 #include <libintl.h>
35 #include <libzfs.h>
36 
37 #include "libzfs_impl.h"
38 
39 int
zfs_iter_clones(zfs_handle_t * zhp,zfs_iter_f func,void * data)40 zfs_iter_clones(zfs_handle_t *zhp, zfs_iter_f func, void *data)
41 {
42           nvlist_t *nvl = zfs_get_clones_nvl(zhp);
43           nvpair_t *pair;
44 
45           if (nvl == NULL)
46                     return (0);
47 
48           for (pair = nvlist_next_nvpair(nvl, NULL); pair != NULL;
49               pair = nvlist_next_nvpair(nvl, pair)) {
50                     zfs_handle_t *clone = zfs_open(zhp->zfs_hdl, nvpair_name(pair),
51                         ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);
52                     if (clone != NULL) {
53                               int err = func(clone, data);
54                               if (err != 0)
55                                         return (err);
56                     }
57           }
58           return (0);
59 }
60 
61 static int
zfs_do_list_ioctl(zfs_handle_t * zhp,unsigned long arg,zfs_cmd_t * zc)62 zfs_do_list_ioctl(zfs_handle_t *zhp, unsigned long arg, zfs_cmd_t *zc)
63 {
64           int rc;
65           uint64_t  orig_cookie;
66 
67           orig_cookie = zc->zc_cookie;
68 top:
69           (void) strlcpy(zc->zc_name, zhp->zfs_name, sizeof (zc->zc_name));
70           rc = ioctl(zhp->zfs_hdl->libzfs_fd, arg, zc);
71 
72           if (rc == -1) {
73                     switch (errno) {
74                     case ENOMEM:
75                               /* expand nvlist memory and try again */
76                               if (zcmd_expand_dst_nvlist(zhp->zfs_hdl, zc) != 0) {
77                                         zcmd_free_nvlists(zc);
78                                         return (-1);
79                               }
80                               zc->zc_cookie = orig_cookie;
81                               goto top;
82                     /*
83                      * An errno value of ESRCH indicates normal completion.
84                      * If ENOENT is returned, then the underlying dataset
85                      * has been removed since we obtained the handle.
86                      */
87                     case ESRCH:
88                     case ENOENT:
89                               rc = 1;
90                               break;
91                     default:
92                               rc = zfs_standard_error(zhp->zfs_hdl, errno,
93                                   dgettext(TEXT_DOMAIN,
94                                   "cannot iterate filesystems"));
95                               break;
96                     }
97           }
98           return (rc);
99 }
100 
101 /*
102  * Iterate over all child filesystems
103  */
104 int
zfs_iter_filesystems(zfs_handle_t * zhp,zfs_iter_f func,void * data)105 zfs_iter_filesystems(zfs_handle_t *zhp, zfs_iter_f func, void *data)
106 {
107           zfs_cmd_t zc = { 0 };
108           zfs_handle_t *nzhp;
109           int ret;
110 
111           if (zhp->zfs_type != ZFS_TYPE_FILESYSTEM)
112                     return (0);
113 
114           if (zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0)
115                     return (-1);
116 
117           while ((ret = zfs_do_list_ioctl(zhp, ZFS_IOC_DATASET_LIST_NEXT,
118               &zc)) == 0) {
119                     /*
120                      * Silently ignore errors, as the only plausible explanation is
121                      * that the pool has since been removed.
122                      */
123                     if ((nzhp = make_dataset_handle_zc(zhp->zfs_hdl,
124                         &zc)) == NULL) {
125                               continue;
126                     }
127 
128                     if ((ret = func(nzhp, data)) != 0) {
129                               zcmd_free_nvlists(&zc);
130                               return (ret);
131                     }
132           }
133           zcmd_free_nvlists(&zc);
134           return ((ret < 0) ? ret : 0);
135 }
136 
137 /*
138  * Iterate over all snapshots
139  */
140 int
zfs_iter_snapshots(zfs_handle_t * zhp,boolean_t simple,zfs_iter_f func,void * data)141 zfs_iter_snapshots(zfs_handle_t *zhp, boolean_t simple, zfs_iter_f func,
142     void *data)
143 {
144           zfs_cmd_t zc = { 0 };
145           zfs_handle_t *nzhp;
146           int ret;
147 
148           if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT ||
149               zhp->zfs_type == ZFS_TYPE_BOOKMARK)
150                     return (0);
151 
152           zc.zc_simple = simple;
153 
154           if (zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0)
155                     return (-1);
156           while ((ret = zfs_do_list_ioctl(zhp, ZFS_IOC_SNAPSHOT_LIST_NEXT,
157               &zc)) == 0) {
158 
159                     if (simple)
160                               nzhp = make_dataset_simple_handle_zc(zhp, &zc);
161                     else
162                               nzhp = make_dataset_handle_zc(zhp->zfs_hdl, &zc);
163                     if (nzhp == NULL)
164                               continue;
165 
166                     if ((ret = func(nzhp, data)) != 0) {
167                               zcmd_free_nvlists(&zc);
168                               return (ret);
169                     }
170           }
171           zcmd_free_nvlists(&zc);
172           return ((ret < 0) ? ret : 0);
173 }
174 
175 /*
176  * Iterate over all bookmarks
177  */
178 int
zfs_iter_bookmarks(zfs_handle_t * zhp,zfs_iter_f func,void * data)179 zfs_iter_bookmarks(zfs_handle_t *zhp, zfs_iter_f func, void *data)
180 {
181           zfs_handle_t *nzhp;
182           nvlist_t *props = NULL;
183           nvlist_t *bmarks = NULL;
184           int err;
185 
186           if ((zfs_get_type(zhp) & (ZFS_TYPE_SNAPSHOT | ZFS_TYPE_BOOKMARK)) != 0)
187                     return (0);
188 
189           /* Setup the requested properties nvlist. */
190           props = fnvlist_alloc();
191           fnvlist_add_boolean(props, zfs_prop_to_name(ZFS_PROP_GUID));
192           fnvlist_add_boolean(props, zfs_prop_to_name(ZFS_PROP_CREATETXG));
193           fnvlist_add_boolean(props, zfs_prop_to_name(ZFS_PROP_CREATION));
194 
195           if ((err = lzc_get_bookmarks(zhp->zfs_name, props, &bmarks)) != 0)
196                     goto out;
197 
198           for (nvpair_t *pair = nvlist_next_nvpair(bmarks, NULL);
199               pair != NULL; pair = nvlist_next_nvpair(bmarks, pair)) {
200                     char name[ZFS_MAX_DATASET_NAME_LEN];
201                     char *bmark_name;
202                     nvlist_t *bmark_props;
203 
204                     bmark_name = nvpair_name(pair);
205                     bmark_props = fnvpair_value_nvlist(pair);
206 
207                     (void) snprintf(name, sizeof (name), "%s#%s", zhp->zfs_name,
208                         bmark_name);
209 
210                     nzhp = make_bookmark_handle(zhp, name, bmark_props);
211                     if (nzhp == NULL)
212                               continue;
213 
214                     if ((err = func(nzhp, data)) != 0)
215                               goto out;
216           }
217 
218 out:
219           fnvlist_free(props);
220           fnvlist_free(bmarks);
221 
222           return (err);
223 }
224 
225 /*
226  * Routines for dealing with the sorted snapshot functionality
227  */
228 typedef struct zfs_node {
229           zfs_handle_t        *zn_handle;
230           avl_node_t          zn_avlnode;
231 } zfs_node_t;
232 
233 static int
zfs_sort_snaps(zfs_handle_t * zhp,void * data)234 zfs_sort_snaps(zfs_handle_t *zhp, void *data)
235 {
236           avl_tree_t *avl = data;
237           zfs_node_t *node;
238           zfs_node_t search;
239 
240           search.zn_handle = zhp;
241           node = avl_find(avl, &search, NULL);
242           if (node) {
243                     /*
244                      * If this snapshot was renamed while we were creating the
245                      * AVL tree, it's possible that we already inserted it under
246                      * its old name. Remove the old handle before adding the new
247                      * one.
248                      */
249                     zfs_close(node->zn_handle);
250                     avl_remove(avl, node);
251                     free(node);
252           }
253 
254           node = zfs_alloc(zhp->zfs_hdl, sizeof (zfs_node_t));
255           node->zn_handle = zhp;
256           avl_add(avl, node);
257 
258           return (0);
259 }
260 
261 static int
zfs_snapshot_compare(const void * larg,const void * rarg)262 zfs_snapshot_compare(const void *larg, const void *rarg)
263 {
264           zfs_handle_t *l = ((zfs_node_t *)larg)->zn_handle;
265           zfs_handle_t *r = ((zfs_node_t *)rarg)->zn_handle;
266           uint64_t lcreate, rcreate;
267 
268           /*
269            * Sort them according to creation time.  We use the hidden
270            * CREATETXG property to get an absolute ordering of snapshots.
271            */
272           lcreate = zfs_prop_get_int(l, ZFS_PROP_CREATETXG);
273           rcreate = zfs_prop_get_int(r, ZFS_PROP_CREATETXG);
274 
275           if (lcreate < rcreate)
276                     return (-1);
277           else if (lcreate > rcreate)
278                     return (+1);
279           else
280                     return (0);
281 }
282 
283 int
zfs_iter_snapshots_sorted(zfs_handle_t * zhp,zfs_iter_f callback,void * data)284 zfs_iter_snapshots_sorted(zfs_handle_t *zhp, zfs_iter_f callback, void *data)
285 {
286           int ret = 0;
287           zfs_node_t *node;
288           avl_tree_t avl;
289           void *cookie = NULL;
290 
291           avl_create(&avl, zfs_snapshot_compare,
292               sizeof (zfs_node_t), offsetof(zfs_node_t, zn_avlnode));
293 
294           ret = zfs_iter_snapshots(zhp, B_FALSE, zfs_sort_snaps, &avl);
295 
296           for (node = avl_first(&avl); node != NULL; node = AVL_NEXT(&avl, node))
297                     ret |= callback(node->zn_handle, data);
298 
299           while ((node = avl_destroy_nodes(&avl, &cookie)) != NULL)
300                     free(node);
301 
302           avl_destroy(&avl);
303 
304           return (ret);
305 }
306 
307 typedef struct {
308           char *ssa_first;
309           char *ssa_last;
310           boolean_t ssa_seenfirst;
311           boolean_t ssa_seenlast;
312           zfs_iter_f ssa_func;
313           void *ssa_arg;
314 } snapspec_arg_t;
315 
316 static int
snapspec_cb(zfs_handle_t * zhp,void * arg)317 snapspec_cb(zfs_handle_t *zhp, void *arg)
318 {
319           snapspec_arg_t *ssa = arg;
320           char *shortsnapname;
321           int err = 0;
322 
323           if (ssa->ssa_seenlast)
324                     return (0);
325           shortsnapname = zfs_strdup(zhp->zfs_hdl,
326               strchr(zfs_get_name(zhp), '@') + 1);
327 
328           if (!ssa->ssa_seenfirst && strcmp(shortsnapname, ssa->ssa_first) == 0)
329                     ssa->ssa_seenfirst = B_TRUE;
330 
331           if (ssa->ssa_seenfirst) {
332                     err = ssa->ssa_func(zhp, ssa->ssa_arg);
333           } else {
334                     zfs_close(zhp);
335           }
336 
337           if (strcmp(shortsnapname, ssa->ssa_last) == 0)
338                     ssa->ssa_seenlast = B_TRUE;
339           free(shortsnapname);
340 
341           return (err);
342 }
343 
344 /*
345  * spec is a string like "A,B%C,D"
346  *
347  * <snaps>, where <snaps> can be:
348  *      <snap>          (single snapshot)
349  *      <snap>%<snap>   (range of snapshots, inclusive)
350  *      %<snap>         (range of snapshots, starting with earliest)
351  *      <snap>%         (range of snapshots, ending with last)
352  *      %               (all snapshots)
353  *      <snaps>[,...]   (comma separated list of the above)
354  *
355  * If a snapshot can not be opened, continue trying to open the others, but
356  * return ENOENT at the end.
357  */
358 int
zfs_iter_snapspec(zfs_handle_t * fs_zhp,const char * spec_orig,zfs_iter_f func,void * arg)359 zfs_iter_snapspec(zfs_handle_t *fs_zhp, const char *spec_orig,
360     zfs_iter_f func, void *arg)
361 {
362           char *buf, *comma_separated, *cp;
363           int err = 0;
364           int ret = 0;
365 
366           buf = zfs_strdup(fs_zhp->zfs_hdl, spec_orig);
367           cp = buf;
368 
369           while ((comma_separated = strsep(&cp, ",")) != NULL) {
370                     char *pct = strchr(comma_separated, '%');
371                     if (pct != NULL) {
372                               snapspec_arg_t ssa = { 0 };
373                               ssa.ssa_func = func;
374                               ssa.ssa_arg = arg;
375 
376                               if (pct == comma_separated)
377                                         ssa.ssa_seenfirst = B_TRUE;
378                               else
379                                         ssa.ssa_first = comma_separated;
380                               *pct = '\0';
381                               ssa.ssa_last = pct + 1;
382 
383                               /*
384                                * If there is a lastname specified, make sure it
385                                * exists.
386                                */
387                               if (ssa.ssa_last[0] != '\0') {
388                                         char snapname[ZFS_MAX_DATASET_NAME_LEN];
389                                         (void) snprintf(snapname, sizeof (snapname),
390                                             "%s@%s", zfs_get_name(fs_zhp),
391                                             ssa.ssa_last);
392                                         if (!zfs_dataset_exists(fs_zhp->zfs_hdl,
393                                             snapname, ZFS_TYPE_SNAPSHOT)) {
394                                                   ret = ENOENT;
395                                                   continue;
396                                         }
397                               }
398 
399                               err = zfs_iter_snapshots_sorted(fs_zhp,
400                                   snapspec_cb, &ssa);
401                               if (ret == 0)
402                                         ret = err;
403                               if (ret == 0 && (!ssa.ssa_seenfirst ||
404                                   (ssa.ssa_last[0] != '\0' && !ssa.ssa_seenlast))) {
405                                         ret = ENOENT;
406                               }
407                     } else {
408                               char snapname[ZFS_MAX_DATASET_NAME_LEN];
409                               zfs_handle_t *snap_zhp;
410                               (void) snprintf(snapname, sizeof (snapname), "%s@%s",
411                                   zfs_get_name(fs_zhp), comma_separated);
412                               snap_zhp = make_dataset_handle(fs_zhp->zfs_hdl,
413                                   snapname);
414                               if (snap_zhp == NULL) {
415                                         ret = ENOENT;
416                                         continue;
417                               }
418                               err = func(snap_zhp, arg);
419                               if (ret == 0)
420                                         ret = err;
421                     }
422           }
423 
424           free(buf);
425           return (ret);
426 }
427 
428 /*
429  * Iterate over all children, snapshots and filesystems
430  */
431 int
zfs_iter_children(zfs_handle_t * zhp,zfs_iter_f func,void * data)432 zfs_iter_children(zfs_handle_t *zhp, zfs_iter_f func, void *data)
433 {
434           int ret;
435 
436           if ((ret = zfs_iter_filesystems(zhp, func, data)) != 0)
437                     return (ret);
438 
439           return (zfs_iter_snapshots(zhp, B_FALSE, func, data));
440 }
441 
442 
443 typedef struct iter_stack_frame {
444           struct iter_stack_frame *next;
445           zfs_handle_t *zhp;
446 } iter_stack_frame_t;
447 
448 typedef struct iter_dependents_arg {
449           boolean_t first;
450           boolean_t allowrecursion;
451           iter_stack_frame_t *stack;
452           zfs_iter_f func;
453           void *data;
454 } iter_dependents_arg_t;
455 
456 static int
iter_dependents_cb(zfs_handle_t * zhp,void * arg)457 iter_dependents_cb(zfs_handle_t *zhp, void *arg)
458 {
459           iter_dependents_arg_t *ida = arg;
460           int err = 0;
461           boolean_t first = ida->first;
462           ida->first = B_FALSE;
463 
464           if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT) {
465                     err = zfs_iter_clones(zhp, iter_dependents_cb, ida);
466           } else if (zhp->zfs_type != ZFS_TYPE_BOOKMARK) {
467                     iter_stack_frame_t isf;
468                     iter_stack_frame_t *f;
469 
470                     /*
471                      * check if there is a cycle by seeing if this fs is already
472                      * on the stack.
473                      */
474                     for (f = ida->stack; f != NULL; f = f->next) {
475                               if (f->zhp->zfs_dmustats.dds_guid ==
476                                   zhp->zfs_dmustats.dds_guid) {
477                                         if (ida->allowrecursion) {
478                                                   zfs_close(zhp);
479                                                   return (0);
480                                         } else {
481                                                   zfs_error_aux(zhp->zfs_hdl,
482                                                       dgettext(TEXT_DOMAIN,
483                                                       "recursive dependency at '%s'"),
484                                                       zfs_get_name(zhp));
485                                                   err = zfs_error(zhp->zfs_hdl,
486                                                       EZFS_RECURSIVE,
487                                                       dgettext(TEXT_DOMAIN,
488                                                       "cannot determine dependent "
489                                                       "datasets"));
490                                                   zfs_close(zhp);
491                                                   return (err);
492                                         }
493                               }
494                     }
495 
496                     isf.zhp = zhp;
497                     isf.next = ida->stack;
498                     ida->stack = &isf;
499                     err = zfs_iter_filesystems(zhp, iter_dependents_cb, ida);
500                     if (err == 0) {
501                               err = zfs_iter_snapshots(zhp, B_FALSE,
502                                   iter_dependents_cb, ida);
503                     }
504                     ida->stack = isf.next;
505           }
506 
507           if (!first && err == 0)
508                     err = ida->func(zhp, ida->data);
509           else
510                     zfs_close(zhp);
511 
512           return (err);
513 }
514 
515 int
zfs_iter_dependents(zfs_handle_t * zhp,boolean_t allowrecursion,zfs_iter_f func,void * data)516 zfs_iter_dependents(zfs_handle_t *zhp, boolean_t allowrecursion,
517     zfs_iter_f func, void *data)
518 {
519           iter_dependents_arg_t ida;
520           ida.allowrecursion = allowrecursion;
521           ida.stack = NULL;
522           ida.func = func;
523           ida.data = data;
524           ida.first = B_TRUE;
525           return (iter_dependents_cb(zfs_handle_dup(zhp), &ida));
526 }
527