xref: /illumos-gate/usr/src/uts/common/fs/zfs/dmu_objset.c (revision 7f7322febbcfe774b7270abc3b191c094bfcc517)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <sys/zfs_context.h>
30 #include <sys/dmu_objset.h>
31 #include <sys/dsl_dir.h>
32 #include <sys/dsl_dataset.h>
33 #include <sys/dsl_prop.h>
34 #include <sys/dsl_pool.h>
35 #include <sys/dnode.h>
36 #include <sys/dbuf.h>
37 #include <sys/dmu_tx.h>
38 #include <sys/zio_checksum.h>
39 #include <sys/zap.h>
40 #include <sys/zil.h>
41 #include <sys/dmu_impl.h>
42 
43 
44 spa_t *
45 dmu_objset_spa(objset_t *os)
46 {
47 	return (os->os->os_spa);
48 }
49 
50 zilog_t *
51 dmu_objset_zil(objset_t *os)
52 {
53 	return (os->os->os_zil);
54 }
55 
56 dsl_pool_t *
57 dmu_objset_pool(objset_t *os)
58 {
59 	dsl_dataset_t *ds;
60 
61 	if ((ds = os->os->os_dsl_dataset) != NULL && ds->ds_dir)
62 		return (ds->ds_dir->dd_pool);
63 	else
64 		return (spa_get_dsl(os->os->os_spa));
65 }
66 
67 dsl_dataset_t *
68 dmu_objset_ds(objset_t *os)
69 {
70 	return (os->os->os_dsl_dataset);
71 }
72 
73 dmu_objset_type_t
74 dmu_objset_type(objset_t *os)
75 {
76 	return (os->os->os_phys->os_type);
77 }
78 
79 void
80 dmu_objset_name(objset_t *os, char *buf)
81 {
82 	dsl_dataset_name(os->os->os_dsl_dataset, buf);
83 }
84 
85 uint64_t
86 dmu_objset_id(objset_t *os)
87 {
88 	dsl_dataset_t *ds = os->os->os_dsl_dataset;
89 
90 	return (ds ? ds->ds_object : 0);
91 }
92 
93 static void
94 checksum_changed_cb(void *arg, uint64_t newval)
95 {
96 	objset_impl_t *osi = arg;
97 
98 	/*
99 	 * Inheritance should have been done by now.
100 	 */
101 	ASSERT(newval != ZIO_CHECKSUM_INHERIT);
102 
103 	osi->os_checksum = zio_checksum_select(newval, ZIO_CHECKSUM_ON_VALUE);
104 }
105 
106 static void
107 compression_changed_cb(void *arg, uint64_t newval)
108 {
109 	objset_impl_t *osi = arg;
110 
111 	/*
112 	 * Inheritance and range checking should have been done by now.
113 	 */
114 	ASSERT(newval != ZIO_COMPRESS_INHERIT);
115 
116 	osi->os_compress = zio_compress_select(newval, ZIO_COMPRESS_ON_VALUE);
117 }
118 
119 void
120 dmu_objset_byteswap(void *buf, size_t size)
121 {
122 	objset_phys_t *osp = buf;
123 
124 	ASSERT(size == sizeof (objset_phys_t));
125 	dnode_byteswap(&osp->os_meta_dnode);
126 	byteswap_uint64_array(&osp->os_zil_header, sizeof (zil_header_t));
127 	osp->os_type = BSWAP_64(osp->os_type);
128 }
129 
130 objset_impl_t *
131 dmu_objset_open_impl(spa_t *spa, dsl_dataset_t *ds, blkptr_t *bp)
132 {
133 	objset_impl_t *winner, *osi;
134 	int i, err, checksum;
135 
136 	osi = kmem_zalloc(sizeof (objset_impl_t), KM_SLEEP);
137 	osi->os.os = osi;
138 	osi->os_dsl_dataset = ds;
139 	osi->os_spa = spa;
140 	if (bp)
141 		osi->os_rootbp = *bp;
142 	osi->os_phys = zio_buf_alloc(sizeof (objset_phys_t));
143 	if (!BP_IS_HOLE(&osi->os_rootbp)) {
144 		dprintf_bp(&osi->os_rootbp, "reading %s", "");
145 		(void) arc_read(NULL, spa, &osi->os_rootbp,
146 		    dmu_ot[DMU_OT_OBJSET].ot_byteswap,
147 		    arc_bcopy_func, osi->os_phys,
148 		    ZIO_PRIORITY_SYNC_READ, ZIO_FLAG_MUSTSUCCEED, ARC_WAIT);
149 	} else {
150 		bzero(osi->os_phys, sizeof (objset_phys_t));
151 	}
152 	osi->os_zil = zil_alloc(&osi->os, &osi->os_phys->os_zil_header);
153 
154 	/*
155 	 * Note: the changed_cb will be called once before the register
156 	 * func returns, thus changing the checksum/compression from the
157 	 * default (fletcher2/off).
158 	 */
159 	if (ds) {
160 		err = dsl_prop_register(ds, "checksum",
161 		    checksum_changed_cb, osi);
162 		ASSERT(err == 0);
163 
164 		err = dsl_prop_register(ds, "compression",
165 		    compression_changed_cb, osi);
166 		ASSERT(err == 0);
167 	} else {
168 		/* It's the meta-objset. */
169 		/* XXX - turn off metadata compression temporarily */
170 		osi->os_checksum = ZIO_CHECKSUM_FLETCHER_4;
171 		osi->os_compress = ZIO_COMPRESS_OFF;
172 	}
173 
174 	/*
175 	 * Metadata always gets compressed and checksummed.
176 	 * If the data checksum is multi-bit correctable, and it's not
177 	 * a ZBT-style checksum, then it's suitable for metadata as well.
178 	 * Otherwise, the metadata checksum defaults to fletcher4.
179 	 */
180 	checksum = osi->os_checksum;
181 
182 	if (zio_checksum_table[checksum].ci_correctable &&
183 	    !zio_checksum_table[checksum].ci_zbt)
184 		osi->os_md_checksum = checksum;
185 	else
186 		osi->os_md_checksum = ZIO_CHECKSUM_FLETCHER_4;
187 
188 	/* XXX - turn off metadata compression temporarily */
189 	osi->os_md_compress = ZIO_COMPRESS_OFF;
190 
191 	for (i = 0; i < TXG_SIZE; i++) {
192 		list_create(&osi->os_dirty_dnodes[i], sizeof (dnode_t),
193 		    offsetof(dnode_t, dn_dirty_link[i]));
194 		list_create(&osi->os_free_dnodes[i], sizeof (dnode_t),
195 		    offsetof(dnode_t, dn_dirty_link[i]));
196 	}
197 	list_create(&osi->os_dnodes, sizeof (dnode_t),
198 	    offsetof(dnode_t, dn_link));
199 	list_create(&osi->os_downgraded_dbufs, sizeof (dmu_buf_impl_t),
200 	    offsetof(dmu_buf_impl_t, db_link));
201 
202 	osi->os_meta_dnode = dnode_special_open(osi,
203 	    &osi->os_phys->os_meta_dnode, DMU_META_DNODE_OBJECT);
204 
205 	if (ds != NULL) {
206 		winner = dsl_dataset_set_user_ptr(ds, osi, dmu_objset_evict);
207 		if (winner) {
208 			dmu_objset_evict(ds, osi);
209 			osi = winner;
210 		}
211 	}
212 
213 	return (osi);
214 }
215 
216 /* called from zpl */
217 int
218 dmu_objset_open(const char *name, dmu_objset_type_t type, int mode,
219     objset_t **osp)
220 {
221 	dsl_dataset_t *ds;
222 	int err;
223 	objset_t *os;
224 	objset_impl_t *osi;
225 
226 	os = kmem_alloc(sizeof (objset_t), KM_SLEEP);
227 	err = dsl_dataset_open(name, mode, os, &ds);
228 	if (err) {
229 		kmem_free(os, sizeof (objset_t));
230 		return (err);
231 	}
232 
233 	osi = dsl_dataset_get_user_ptr(ds);
234 	if (osi == NULL) {
235 		blkptr_t bp;
236 
237 		dsl_dataset_get_blkptr(ds, &bp);
238 		osi = dmu_objset_open_impl(dsl_dataset_get_spa(ds), ds, &bp);
239 	}
240 
241 	os->os = osi;
242 	os->os_mode = mode;
243 
244 	if (type != DMU_OST_ANY && type != os->os->os_phys->os_type) {
245 		dmu_objset_close(os);
246 		return (EINVAL);
247 	}
248 	*osp = os;
249 	return (0);
250 }
251 
252 void
253 dmu_objset_close(objset_t *os)
254 {
255 	dsl_dataset_close(os->os->os_dsl_dataset, os->os_mode, os);
256 	kmem_free(os, sizeof (objset_t));
257 }
258 
259 void
260 dmu_objset_evict(dsl_dataset_t *ds, void *arg)
261 {
262 	objset_impl_t *osi = arg;
263 	int err, i;
264 
265 	for (i = 0; i < TXG_SIZE; i++) {
266 		ASSERT(list_head(&osi->os_dirty_dnodes[i]) == NULL);
267 		ASSERT(list_head(&osi->os_free_dnodes[i]) == NULL);
268 	}
269 
270 	if (ds) {
271 		err = dsl_prop_unregister(ds, "checksum",
272 		    checksum_changed_cb, osi);
273 		ASSERT(err == 0);
274 
275 		err = dsl_prop_unregister(ds, "compression",
276 		    compression_changed_cb, osi);
277 		ASSERT(err == 0);
278 	}
279 
280 	ASSERT3P(list_head(&osi->os_dnodes), ==, osi->os_meta_dnode);
281 	ASSERT3P(list_tail(&osi->os_dnodes), ==, osi->os_meta_dnode);
282 	ASSERT3P(list_head(&osi->os_meta_dnode->dn_dbufs), ==, NULL);
283 
284 	dnode_special_close(osi->os_meta_dnode);
285 	zil_free(osi->os_zil);
286 
287 	zio_buf_free(osi->os_phys, sizeof (objset_phys_t));
288 	kmem_free(osi, sizeof (objset_impl_t));
289 }
290 
291 /* called from dsl for meta-objset */
292 objset_impl_t *
293 dmu_objset_create_impl(spa_t *spa, dsl_dataset_t *ds, dmu_objset_type_t type,
294     dmu_tx_t *tx)
295 {
296 	objset_impl_t *osi;
297 	dnode_t *mdn;
298 
299 	ASSERT(dmu_tx_is_syncing(tx));
300 	osi = dmu_objset_open_impl(spa, ds, NULL);
301 	mdn = osi->os_meta_dnode;
302 
303 	dnode_allocate(mdn, DMU_OT_DNODE, 1 << DNODE_BLOCK_SHIFT,
304 	    DN_MAX_INDBLKSHIFT, DMU_OT_NONE, 0, tx);
305 
306 	/*
307 	 * We don't want to have to increase the meta-dnode's nlevels
308 	 * later, because then we could do it in quescing context while
309 	 * we are also accessing it in open context.
310 	 *
311 	 * This precaution is not necessary for the MOS (ds == NULL),
312 	 * because the MOS is only updated in syncing context.
313 	 * This is most fortunate: the MOS is the only objset that
314 	 * needs to be synced multiple times as spa_sync() iterates
315 	 * to convergence, so minimizing its dn_nlevels matters.
316 	 */
317 	if (ds != NULL)
318 		mdn->dn_next_nlevels[tx->tx_txg & TXG_MASK] =
319 		    mdn->dn_nlevels = DN_META_DNODE_LEVELS;
320 
321 	ASSERT(type != DMU_OST_NONE);
322 	ASSERT(type != DMU_OST_ANY);
323 	ASSERT(type < DMU_OST_NUMTYPES);
324 	osi->os_phys->os_type = type;
325 
326 	dsl_dataset_dirty(ds, tx);
327 
328 	return (osi);
329 }
330 
331 struct oscarg {
332 	void (*userfunc)(objset_t *os, void *arg, dmu_tx_t *tx);
333 	void *userarg;
334 	dsl_dataset_t *clone_parent;
335 	const char *fullname;
336 	const char *lastname;
337 	dmu_objset_type_t type;
338 };
339 
340 static int
341 dmu_objset_create_sync(dsl_dir_t *dd, void *arg, dmu_tx_t *tx)
342 {
343 	struct oscarg *oa = arg;
344 	dsl_dataset_t *ds;
345 	int err;
346 	blkptr_t bp;
347 
348 	ASSERT(dmu_tx_is_syncing(tx));
349 
350 	err = dsl_dataset_create_sync(dd, oa->fullname, oa->lastname,
351 	    oa->clone_parent, tx);
352 	dprintf_dd(dd, "fn=%s ln=%s err=%d\n",
353 	    oa->fullname, oa->lastname, err);
354 	if (err)
355 		return (err);
356 
357 	err = dsl_dataset_open_spa(dd->dd_pool->dp_spa, oa->fullname,
358 	    DS_MODE_STANDARD | DS_MODE_READONLY, FTAG, &ds);
359 	ASSERT3U(err, ==, 0);
360 	dsl_dataset_get_blkptr(ds, &bp);
361 	if (BP_IS_HOLE(&bp)) {
362 		objset_impl_t *osi;
363 
364 		/* This is an empty dmu_objset; not a clone. */
365 		osi = dmu_objset_create_impl(dsl_dataset_get_spa(ds),
366 		    ds, oa->type, tx);
367 
368 		if (oa->userfunc)
369 			oa->userfunc(&osi->os, oa->userarg, tx);
370 	}
371 	dsl_dataset_close(ds, DS_MODE_STANDARD | DS_MODE_READONLY, FTAG);
372 
373 	return (0);
374 }
375 
376 int
377 dmu_objset_create(const char *name, dmu_objset_type_t type,
378     objset_t *clone_parent,
379     void (*func)(objset_t *os, void *arg, dmu_tx_t *tx), void *arg)
380 {
381 	dsl_dir_t *pds;
382 	const char *tail;
383 	int err = 0;
384 
385 	pds = dsl_dir_open(name, FTAG, &tail);
386 	if (pds == NULL)
387 		return (ENOENT);
388 	if (tail == NULL) {
389 		dsl_dir_close(pds, FTAG);
390 		return (EEXIST);
391 	}
392 
393 	dprintf("name=%s\n", name);
394 
395 	if (tail[0] == '@') {
396 		/*
397 		 * If we're creating a snapshot, make sure everything
398 		 * they might want is on disk.  XXX Sketchy to know
399 		 * about snapshots here, better to put in DSL.
400 		 */
401 		objset_t *os;
402 		size_t plen = strchr(name, '@') - name + 1;
403 		char *pbuf = kmem_alloc(plen, KM_SLEEP);
404 		bcopy(name, pbuf, plen - 1);
405 		pbuf[plen - 1] = '\0';
406 
407 		err = dmu_objset_open(pbuf, DMU_OST_ANY, DS_MODE_STANDARD, &os);
408 		if (err == 0) {
409 			err = zil_suspend(dmu_objset_zil(os));
410 			if (err == 0) {
411 				err = dsl_dir_sync_task(pds,
412 				    dsl_dataset_snapshot_sync,
413 				    (void*)(tail+1), 16*1024);
414 				zil_resume(dmu_objset_zil(os));
415 			}
416 			dmu_objset_close(os);
417 		}
418 		kmem_free(pbuf, plen);
419 	} else {
420 		struct oscarg oa = { 0 };
421 		oa.userfunc = func;
422 		oa.userarg = arg;
423 		oa.fullname = name;
424 		oa.lastname = tail;
425 		oa.type = type;
426 		if (clone_parent != NULL) {
427 			/*
428 			 * You can't clone to a different type.
429 			 */
430 			if (clone_parent->os->os_phys->os_type != type) {
431 				dsl_dir_close(pds, FTAG);
432 				return (EINVAL);
433 			}
434 			oa.clone_parent = clone_parent->os->os_dsl_dataset;
435 		}
436 		err = dsl_dir_sync_task(pds, dmu_objset_create_sync, &oa,
437 		    256*1024);
438 	}
439 	dsl_dir_close(pds, FTAG);
440 	return (err);
441 }
442 
443 int
444 dmu_objset_destroy(const char *name)
445 {
446 	objset_t *os;
447 	int error;
448 
449 	/*
450 	 * If it looks like we'll be able to destroy it, and there's
451 	 * an unplayed replay log sitting around, destroy the log.
452 	 * It would be nicer to do this in dsl_dataset_destroy_sync(),
453 	 * but the replay log objset is modified in open context.
454 	 */
455 	error = dmu_objset_open(name, DMU_OST_ANY, DS_MODE_EXCLUSIVE, &os);
456 	if (error == 0) {
457 		zil_destroy(dmu_objset_zil(os));
458 		dmu_objset_close(os);
459 	}
460 
461 	/* XXX uncache everything? */
462 	return (dsl_dataset_destroy(name));
463 }
464 
465 int
466 dmu_objset_rollback(const char *name)
467 {
468 	int err;
469 	objset_t *os;
470 
471 	err = dmu_objset_open(name, DMU_OST_ANY, DS_MODE_EXCLUSIVE, &os);
472 	if (err == 0) {
473 		err = zil_suspend(dmu_objset_zil(os));
474 		if (err == 0)
475 			zil_resume(dmu_objset_zil(os));
476 		dmu_objset_close(os);
477 		if (err == 0) {
478 			/* XXX uncache everything? */
479 			err = dsl_dataset_rollback(name);
480 		}
481 	}
482 	return (err);
483 }
484 
485 static void
486 dmu_objset_sync_dnodes(objset_impl_t *os, list_t *list, dmu_tx_t *tx)
487 {
488 	dnode_t *dn = list_head(list);
489 	int level, err;
490 
491 	for (level = 0; dn = list_head(list); level++) {
492 		zio_t *zio;
493 		zio = zio_root(os->os_spa, NULL, NULL, ZIO_FLAG_MUSTSUCCEED);
494 
495 		ASSERT3U(level, <=, DN_MAX_LEVELS);
496 
497 		while (dn) {
498 			dnode_t *next = list_next(list, dn);
499 
500 			list_remove(list, dn);
501 			if (dnode_sync(dn, level, zio, tx) == 0) {
502 				/*
503 				 * This dnode requires syncing at higher
504 				 * levels; put it back onto the list.
505 				 */
506 				if (next)
507 					list_insert_before(list, next, dn);
508 				else
509 					list_insert_tail(list, dn);
510 			}
511 			dn = next;
512 		}
513 		err = zio_wait(zio);
514 		ASSERT(err == 0);
515 	}
516 }
517 
518 /* ARGSUSED */
519 static void
520 killer(zio_t *zio, arc_buf_t *abuf, void *arg)
521 {
522 	objset_impl_t *os = arg;
523 	objset_phys_t *osphys = zio->io_data;
524 	dnode_phys_t *dnp = &osphys->os_meta_dnode;
525 	int i;
526 
527 	ASSERT3U(zio->io_error, ==, 0);
528 
529 	/*
530 	 * Update rootbp fill count.
531 	 */
532 	os->os_rootbp.blk_fill = 1;	/* count the meta-dnode */
533 	for (i = 0; i < dnp->dn_nblkptr; i++)
534 		os->os_rootbp.blk_fill += dnp->dn_blkptr[i].blk_fill;
535 
536 	BP_SET_TYPE(zio->io_bp, DMU_OT_OBJSET);
537 	BP_SET_LEVEL(zio->io_bp, 0);
538 
539 	if (!DVA_EQUAL(BP_IDENTITY(zio->io_bp),
540 	    BP_IDENTITY(&zio->io_bp_orig))) {
541 		dsl_dataset_block_kill(os->os_dsl_dataset, &zio->io_bp_orig,
542 		    os->os_synctx);
543 		dsl_dataset_block_born(os->os_dsl_dataset, zio->io_bp,
544 		    os->os_synctx);
545 	}
546 }
547 
548 
549 /* called from dsl */
550 void
551 dmu_objset_sync(objset_impl_t *os, dmu_tx_t *tx)
552 {
553 	extern taskq_t *dbuf_tq;
554 	int txgoff;
555 	list_t *dirty_list;
556 	int err;
557 	arc_buf_t *abuf =
558 	    arc_buf_alloc(os->os_spa, sizeof (objset_phys_t), FTAG);
559 
560 	ASSERT(dmu_tx_is_syncing(tx));
561 	ASSERT(os->os_synctx == NULL);
562 	/* XXX the write_done callback should really give us the tx... */
563 	os->os_synctx = tx;
564 
565 	dprintf_ds(os->os_dsl_dataset, "txg=%llu\n", tx->tx_txg);
566 
567 	txgoff = tx->tx_txg & TXG_MASK;
568 
569 	dmu_objset_sync_dnodes(os, &os->os_free_dnodes[txgoff], tx);
570 	dmu_objset_sync_dnodes(os, &os->os_dirty_dnodes[txgoff], tx);
571 
572 	/*
573 	 * Free intent log blocks up to this tx.
574 	 */
575 	zil_sync(os->os_zil, tx);
576 
577 	/*
578 	 * Sync meta-dnode
579 	 */
580 	dirty_list = &os->os_dirty_dnodes[txgoff];
581 	ASSERT(list_head(dirty_list) == NULL);
582 	list_insert_tail(dirty_list, os->os_meta_dnode);
583 	dmu_objset_sync_dnodes(os, dirty_list, tx);
584 
585 	/*
586 	 * Sync the root block.
587 	 */
588 	bcopy(os->os_phys, abuf->b_data, sizeof (objset_phys_t));
589 	err = arc_write(NULL, os->os_spa, os->os_md_checksum,
590 	    os->os_md_compress, tx->tx_txg, &os->os_rootbp, abuf, killer, os,
591 	    ZIO_PRIORITY_ASYNC_WRITE, ZIO_FLAG_MUSTSUCCEED, ARC_WAIT);
592 	ASSERT(err == 0);
593 	arc_buf_free(abuf, FTAG);
594 
595 	dsl_dataset_set_blkptr(os->os_dsl_dataset, &os->os_rootbp, tx);
596 
597 	ASSERT3P(os->os_synctx, ==, tx);
598 	taskq_wait(dbuf_tq);
599 	os->os_synctx = NULL;
600 }
601 
602 void
603 dmu_objset_stats(objset_t *os, dmu_objset_stats_t *dds)
604 {
605 	if (os->os->os_dsl_dataset != NULL) {
606 		dsl_dataset_stats(os->os->os_dsl_dataset, dds);
607 	} else {
608 		ASSERT(os->os->os_phys->os_type == DMU_OST_META);
609 		bzero(dds, sizeof (*dds));
610 	}
611 	dds->dds_type = os->os->os_phys->os_type;
612 }
613 
614 int
615 dmu_objset_is_snapshot(objset_t *os)
616 {
617 	if (os->os->os_dsl_dataset != NULL)
618 		return (dsl_dataset_is_snapshot(os->os->os_dsl_dataset));
619 	else
620 		return (B_FALSE);
621 }
622 
623 int
624 dmu_snapshot_list_next(objset_t *os, int namelen, char *name,
625     uint64_t *idp, uint64_t *offp)
626 {
627 	dsl_dataset_t *ds = os->os->os_dsl_dataset;
628 	zap_cursor_t cursor;
629 	zap_attribute_t attr;
630 
631 	if (ds->ds_phys->ds_snapnames_zapobj == 0)
632 		return (ENOENT);
633 
634 	zap_cursor_init_serialized(&cursor,
635 	    ds->ds_dir->dd_pool->dp_meta_objset,
636 	    ds->ds_phys->ds_snapnames_zapobj, *offp);
637 
638 	if (zap_cursor_retrieve(&cursor, &attr) != 0) {
639 		zap_cursor_fini(&cursor);
640 		return (ENOENT);
641 	}
642 
643 	if (strlen(attr.za_name) + 1 > namelen) {
644 		zap_cursor_fini(&cursor);
645 		return (ENAMETOOLONG);
646 	}
647 
648 	(void) strcpy(name, attr.za_name);
649 	if (idp)
650 		*idp = attr.za_first_integer;
651 	zap_cursor_advance(&cursor);
652 	*offp = zap_cursor_serialize(&cursor);
653 	zap_cursor_fini(&cursor);
654 
655 	return (0);
656 }
657 
658 int
659 dmu_dir_list_next(objset_t *os, int namelen, char *name,
660     uint64_t *idp, uint64_t *offp)
661 {
662 	dsl_dir_t *dd = os->os->os_dsl_dataset->ds_dir;
663 	zap_cursor_t cursor;
664 	zap_attribute_t attr;
665 
666 	if (dd->dd_phys->dd_child_dir_zapobj == 0)
667 		return (ENOENT);
668 
669 	/* there is no next dir on a snapshot! */
670 	if (os->os->os_dsl_dataset->ds_object !=
671 	    dd->dd_phys->dd_head_dataset_obj)
672 		return (ENOENT);
673 
674 	zap_cursor_init_serialized(&cursor,
675 	    dd->dd_pool->dp_meta_objset,
676 	    dd->dd_phys->dd_child_dir_zapobj, *offp);
677 
678 	if (zap_cursor_retrieve(&cursor, &attr) != 0) {
679 		zap_cursor_fini(&cursor);
680 		return (ENOENT);
681 	}
682 
683 	if (strlen(attr.za_name) + 1 > namelen) {
684 		zap_cursor_fini(&cursor);
685 		return (ENAMETOOLONG);
686 	}
687 
688 	(void) strcpy(name, attr.za_name);
689 	if (idp)
690 		*idp = attr.za_first_integer;
691 	zap_cursor_advance(&cursor);
692 	*offp = zap_cursor_serialize(&cursor);
693 	zap_cursor_fini(&cursor);
694 
695 	return (0);
696 }
697 
698 /*
699  * Find all objsets under name, and for each, call 'func(child_name, arg)'.
700  */
701 void
702 dmu_objset_find(char *name, void func(char *, void *), void *arg, int flags)
703 {
704 	dsl_dir_t *dd;
705 	objset_t *os;
706 	uint64_t snapobj;
707 	zap_cursor_t zc;
708 	zap_attribute_t attr;
709 	char *child;
710 	int do_self;
711 
712 	dd = dsl_dir_open(name, FTAG, NULL);
713 	if (dd == NULL)
714 		return;
715 
716 	do_self = (dd->dd_phys->dd_head_dataset_obj != 0);
717 
718 	/*
719 	 * Iterate over all children.
720 	 */
721 	if (dd->dd_phys->dd_child_dir_zapobj != 0) {
722 		for (zap_cursor_init(&zc, dd->dd_pool->dp_meta_objset,
723 		    dd->dd_phys->dd_child_dir_zapobj);
724 		    zap_cursor_retrieve(&zc, &attr) == 0;
725 		    (void) zap_cursor_advance(&zc)) {
726 			ASSERT(attr.za_integer_length == sizeof (uint64_t));
727 			ASSERT(attr.za_num_integers == 1);
728 
729 			/*
730 			 * No separating '/' because parent's name ends in /.
731 			 */
732 			child = kmem_alloc(MAXPATHLEN, KM_SLEEP);
733 			/* XXX could probably just use name here */
734 			dsl_dir_name(dd, child);
735 			(void) strcat(child, "/");
736 			(void) strcat(child, attr.za_name);
737 			dmu_objset_find(child, func, arg, flags);
738 			kmem_free(child, MAXPATHLEN);
739 		}
740 		zap_cursor_fini(&zc);
741 	}
742 
743 	/*
744 	 * Iterate over all snapshots.
745 	 */
746 	if ((flags & DS_FIND_SNAPSHOTS) &&
747 	    dmu_objset_open(name, DMU_OST_ANY,
748 	    DS_MODE_STANDARD | DS_MODE_READONLY, &os) == 0) {
749 
750 		snapobj = os->os->os_dsl_dataset->ds_phys->ds_snapnames_zapobj;
751 		dmu_objset_close(os);
752 
753 		for (zap_cursor_init(&zc, dd->dd_pool->dp_meta_objset, snapobj);
754 		    zap_cursor_retrieve(&zc, &attr) == 0;
755 		    (void) zap_cursor_advance(&zc)) {
756 			ASSERT(attr.za_integer_length == sizeof (uint64_t));
757 			ASSERT(attr.za_num_integers == 1);
758 
759 			child = kmem_alloc(MAXPATHLEN, KM_SLEEP);
760 			/* XXX could probably just use name here */
761 			dsl_dir_name(dd, child);
762 			(void) strcat(child, "@");
763 			(void) strcat(child, attr.za_name);
764 			func(child, arg);
765 			kmem_free(child, MAXPATHLEN);
766 		}
767 		zap_cursor_fini(&zc);
768 	}
769 
770 	dsl_dir_close(dd, FTAG);
771 
772 	/*
773 	 * Apply to self if appropriate.
774 	 */
775 	if (do_self)
776 		func(name, arg);
777 }
778