xref: /freebsd/usr.sbin/makefs/zfs/dsl.c (revision c6890399fc00bd628066b94c4add3f7e34815b11)
1240afd8cSMark Johnston /*-
2240afd8cSMark Johnston  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3240afd8cSMark Johnston  *
4240afd8cSMark Johnston  * Copyright (c) 2022 The FreeBSD Foundation
5240afd8cSMark Johnston  *
6240afd8cSMark Johnston  * This software was developed by Mark Johnston under sponsorship from
7240afd8cSMark Johnston  * the FreeBSD Foundation.
8240afd8cSMark Johnston  *
9240afd8cSMark Johnston  * Redistribution and use in source and binary forms, with or without
10240afd8cSMark Johnston  * modification, are permitted provided that the following conditions are
11240afd8cSMark Johnston  * met:
12240afd8cSMark Johnston  * 1. Redistributions of source code must retain the above copyright
13240afd8cSMark Johnston  *    notice, this list of conditions and the following disclaimer.
14240afd8cSMark Johnston  * 2. Redistributions in binary form must reproduce the above copyright
15240afd8cSMark Johnston  *    notice, this list of conditions and the following disclaimer in
16240afd8cSMark Johnston  *    the documentation and/or other materials provided with the distribution.
17240afd8cSMark Johnston  *
18240afd8cSMark Johnston  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19240afd8cSMark Johnston  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20240afd8cSMark Johnston  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21240afd8cSMark Johnston  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22240afd8cSMark Johnston  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23240afd8cSMark Johnston  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24240afd8cSMark Johnston  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25240afd8cSMark Johnston  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26240afd8cSMark Johnston  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27240afd8cSMark Johnston  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28240afd8cSMark Johnston  * SUCH DAMAGE.
29240afd8cSMark Johnston  */
30240afd8cSMark Johnston 
31240afd8cSMark Johnston #include <assert.h>
32*c6890399SJessica Clarke #include <stdlib.h>
33240afd8cSMark Johnston #include <string.h>
34240afd8cSMark Johnston 
35240afd8cSMark Johnston #include <util.h>
36240afd8cSMark Johnston 
37240afd8cSMark Johnston #include "makefs.h"
38240afd8cSMark Johnston #include "zfs.h"
39240afd8cSMark Johnston 
40240afd8cSMark Johnston typedef struct zfs_dsl_dataset {
41240afd8cSMark Johnston 	zfs_objset_t	*os;		/* referenced objset, may be null */
42240afd8cSMark Johnston 	dsl_dataset_phys_t *phys;	/* on-disk representation */
43240afd8cSMark Johnston 	uint64_t	dsid;		/* DSL dataset dnode */
44240afd8cSMark Johnston 
45240afd8cSMark Johnston 	struct zfs_dsl_dir *dir;	/* containing parent */
46240afd8cSMark Johnston } zfs_dsl_dataset_t;
47240afd8cSMark Johnston 
48240afd8cSMark Johnston typedef STAILQ_HEAD(zfs_dsl_dir_list, zfs_dsl_dir) zfs_dsl_dir_list_t;
49240afd8cSMark Johnston 
50240afd8cSMark Johnston typedef struct zfs_dsl_dir {
51240afd8cSMark Johnston 	char		*fullname;	/* full dataset name */
52240afd8cSMark Johnston 	char		*name;		/* basename(fullname) */
53240afd8cSMark Johnston 	dsl_dir_phys_t	*phys;		/* on-disk representation */
54240afd8cSMark Johnston 	nvlist_t	*propsnv;	/* properties saved in propszap */
55240afd8cSMark Johnston 
56240afd8cSMark Johnston 	zfs_dsl_dataset_t *headds;	/* principal dataset, may be null */
57240afd8cSMark Johnston 
58240afd8cSMark Johnston 	uint64_t	dirid;		/* DSL directory dnode */
59240afd8cSMark Johnston 	zfs_zap_t	*propszap;	/* dataset properties */
60240afd8cSMark Johnston 	zfs_zap_t	*childzap;	/* child directories */
61240afd8cSMark Johnston 
62240afd8cSMark Johnston 	/* DSL directory tree linkage. */
63240afd8cSMark Johnston 	struct zfs_dsl_dir *parent;
64240afd8cSMark Johnston 	zfs_dsl_dir_list_t children;
65240afd8cSMark Johnston 	STAILQ_ENTRY(zfs_dsl_dir) next;
66240afd8cSMark Johnston } zfs_dsl_dir_t;
67240afd8cSMark Johnston 
68240afd8cSMark Johnston static zfs_dsl_dir_t *dsl_dir_alloc(zfs_opt_t *zfs, const char *name);
69240afd8cSMark Johnston static zfs_dsl_dataset_t *dsl_dataset_alloc(zfs_opt_t *zfs, zfs_dsl_dir_t *dir);
70240afd8cSMark Johnston 
71240afd8cSMark Johnston static int
72240afd8cSMark Johnston nvlist_find_string(nvlist_t *nvl, const char *key, char **retp)
73240afd8cSMark Johnston {
74240afd8cSMark Johnston 	char *str;
75240afd8cSMark Johnston 	int error, len;
76240afd8cSMark Johnston 
77240afd8cSMark Johnston 	error = nvlist_find(nvl, key, DATA_TYPE_STRING, NULL, &str, &len);
78240afd8cSMark Johnston 	if (error == 0) {
79240afd8cSMark Johnston 		*retp = ecalloc(1, len + 1);
80240afd8cSMark Johnston 		memcpy(*retp, str, len);
81240afd8cSMark Johnston 	}
82240afd8cSMark Johnston 	return (error);
83240afd8cSMark Johnston }
84240afd8cSMark Johnston 
85240afd8cSMark Johnston static int
86240afd8cSMark Johnston nvlist_find_uint64(nvlist_t *nvl, const char *key, uint64_t *retp)
87240afd8cSMark Johnston {
88240afd8cSMark Johnston 	return (nvlist_find(nvl, key, DATA_TYPE_UINT64, NULL, retp, NULL));
89240afd8cSMark Johnston }
90240afd8cSMark Johnston 
91240afd8cSMark Johnston /*
92240afd8cSMark Johnston  * Return an allocated string containing the head dataset's mountpoint,
93240afd8cSMark Johnston  * including the root path prefix.
94240afd8cSMark Johnston  *
95240afd8cSMark Johnston  * If the dataset has a mountpoint property, it is returned.  Otherwise we have
96240afd8cSMark Johnston  * to follow ZFS' inheritance rules.
97240afd8cSMark Johnston  */
98240afd8cSMark Johnston char *
99240afd8cSMark Johnston dsl_dir_get_mountpoint(zfs_opt_t *zfs, zfs_dsl_dir_t *dir)
100240afd8cSMark Johnston {
101240afd8cSMark Johnston 	zfs_dsl_dir_t *pdir;
102240afd8cSMark Johnston 	char *mountpoint, *origmountpoint;
103240afd8cSMark Johnston 
104240afd8cSMark Johnston 	if (nvlist_find_string(dir->propsnv, "mountpoint", &mountpoint) == 0) {
105240afd8cSMark Johnston 		if (strcmp(mountpoint, "none") == 0)
106240afd8cSMark Johnston 			return (NULL);
107240afd8cSMark Johnston 
108240afd8cSMark Johnston 		/*
109240afd8cSMark Johnston 		 * nvlist_find_string() does not make a copy.
110240afd8cSMark Johnston 		 */
111240afd8cSMark Johnston 		mountpoint = estrdup(mountpoint);
112240afd8cSMark Johnston 	} else {
113240afd8cSMark Johnston 		/*
114240afd8cSMark Johnston 		 * If we don't have a mountpoint, it's inherited from one of our
115240afd8cSMark Johnston 		 * ancestors.  Walk up the hierarchy until we find it, building
116240afd8cSMark Johnston 		 * up our mountpoint along the way.  The mountpoint property is
117240afd8cSMark Johnston 		 * always set for the root dataset.
118240afd8cSMark Johnston 		 */
119240afd8cSMark Johnston 		for (pdir = dir->parent, mountpoint = estrdup(dir->name);;) {
120240afd8cSMark Johnston 			origmountpoint = mountpoint;
121240afd8cSMark Johnston 
122240afd8cSMark Johnston 			if (nvlist_find_string(pdir->propsnv, "mountpoint",
123240afd8cSMark Johnston 			    &mountpoint) == 0) {
124240afd8cSMark Johnston 				easprintf(&mountpoint, "%s%s%s", mountpoint,
125240afd8cSMark Johnston 				    mountpoint[strlen(mountpoint) - 1] == '/' ?
126240afd8cSMark Johnston 				    "" : "/", origmountpoint);
127240afd8cSMark Johnston 				free(origmountpoint);
128240afd8cSMark Johnston 				break;
129240afd8cSMark Johnston 			}
130240afd8cSMark Johnston 
131240afd8cSMark Johnston 			easprintf(&mountpoint, "%s/%s", pdir->name,
132240afd8cSMark Johnston 			    origmountpoint);
133240afd8cSMark Johnston 			free(origmountpoint);
134240afd8cSMark Johnston 			pdir = pdir->parent;
135240afd8cSMark Johnston 		}
136240afd8cSMark Johnston 	}
137240afd8cSMark Johnston 	assert(mountpoint[0] == '/');
138240afd8cSMark Johnston 	assert(strstr(mountpoint, zfs->rootpath) == mountpoint);
139240afd8cSMark Johnston 
140240afd8cSMark Johnston 	return (mountpoint);
141240afd8cSMark Johnston }
142240afd8cSMark Johnston 
143240afd8cSMark Johnston int
144240afd8cSMark Johnston dsl_dir_get_canmount(zfs_dsl_dir_t *dir, uint64_t *canmountp)
145240afd8cSMark Johnston {
146240afd8cSMark Johnston 	return (nvlist_find_uint64(dir->propsnv, "canmount", canmountp));
147240afd8cSMark Johnston }
148240afd8cSMark Johnston 
149240afd8cSMark Johnston /*
150240afd8cSMark Johnston  * Handle dataset properties that we know about; stash them into an nvlist to be
151240afd8cSMark Johnston  * written later to the properties ZAP object.
152240afd8cSMark Johnston  *
153240afd8cSMark Johnston  * If the set of properties we handle grows too much, we should probably explore
154240afd8cSMark Johnston  * using libzfs to manage them.
155240afd8cSMark Johnston  */
156240afd8cSMark Johnston static void
157240afd8cSMark Johnston dsl_dir_set_prop(zfs_opt_t *zfs, zfs_dsl_dir_t *dir, const char *key,
158240afd8cSMark Johnston     const char *val)
159240afd8cSMark Johnston {
160240afd8cSMark Johnston 	nvlist_t *nvl;
161240afd8cSMark Johnston 
162240afd8cSMark Johnston 	nvl = dir->propsnv;
163240afd8cSMark Johnston 	if (val == NULL || val[0] == '\0')
164240afd8cSMark Johnston 		errx(1, "missing value for property `%s'", key);
165240afd8cSMark Johnston 	if (nvpair_find(nvl, key) != NULL)
166240afd8cSMark Johnston 		errx(1, "property `%s' already set", key);
167240afd8cSMark Johnston 
168240afd8cSMark Johnston 	if (strcmp(key, "mountpoint") == 0) {
169240afd8cSMark Johnston 		if (strcmp(val, "none") != 0) {
170240afd8cSMark Johnston 			if (val[0] != '/')
171240afd8cSMark Johnston 				errx(1, "mountpoint `%s' is not absolute", val);
172240afd8cSMark Johnston 			if (strcmp(val, zfs->rootpath) != 0 &&
173240afd8cSMark Johnston 			    strcmp(zfs->rootpath, "/") != 0 &&
174240afd8cSMark Johnston 			    (strstr(val, zfs->rootpath) != val ||
175240afd8cSMark Johnston 			     val[strlen(zfs->rootpath)] != '/')) {
176240afd8cSMark Johnston 				errx(1, "mountpoint `%s' is not prefixed by "
177240afd8cSMark Johnston 				    "the root path `%s'", val, zfs->rootpath);
178240afd8cSMark Johnston 			}
179240afd8cSMark Johnston 		}
180240afd8cSMark Johnston 		nvlist_add_string(nvl, key, val);
181240afd8cSMark Johnston 	} else if (strcmp(key, "atime") == 0 || strcmp(key, "exec") == 0 ||
182240afd8cSMark Johnston 	    strcmp(key, "setuid") == 0) {
183240afd8cSMark Johnston 		if (strcmp(val, "on") == 0)
184240afd8cSMark Johnston 			nvlist_add_uint64(nvl, key, 1);
185240afd8cSMark Johnston 		else if (strcmp(val, "off") == 0)
186240afd8cSMark Johnston 			nvlist_add_uint64(nvl, key, 0);
187240afd8cSMark Johnston 		else
188240afd8cSMark Johnston 			errx(1, "invalid value `%s' for %s", val, key);
189240afd8cSMark Johnston 	} else if (strcmp(key, "canmount") == 0) {
190240afd8cSMark Johnston 		if (strcmp(val, "noauto") == 0)
191240afd8cSMark Johnston 			nvlist_add_uint64(nvl, key, 2);
192240afd8cSMark Johnston 		else if (strcmp(val, "on") == 0)
193240afd8cSMark Johnston 			nvlist_add_uint64(nvl, key, 1);
194240afd8cSMark Johnston 		else if (strcmp(val, "off") == 0)
195240afd8cSMark Johnston 			nvlist_add_uint64(nvl, key, 0);
196240afd8cSMark Johnston 		else
197240afd8cSMark Johnston 			errx(1, "invalid value `%s' for %s", val, key);
198240afd8cSMark Johnston 	} else {
199240afd8cSMark Johnston 		errx(1, "unknown property `%s'", key);
200240afd8cSMark Johnston 	}
201240afd8cSMark Johnston }
202240afd8cSMark Johnston 
203240afd8cSMark Johnston static zfs_dsl_dir_t *
204240afd8cSMark Johnston dsl_metadir_alloc(zfs_opt_t *zfs, const char *name)
205240afd8cSMark Johnston {
206240afd8cSMark Johnston 	zfs_dsl_dir_t *dir;
207240afd8cSMark Johnston 	char *path;
208240afd8cSMark Johnston 
209240afd8cSMark Johnston 	easprintf(&path, "%s/%s", zfs->poolname, name);
210240afd8cSMark Johnston 	dir = dsl_dir_alloc(zfs, path);
211240afd8cSMark Johnston 	free(path);
212240afd8cSMark Johnston 	return (dir);
213240afd8cSMark Johnston }
214240afd8cSMark Johnston 
215240afd8cSMark Johnston static void
216240afd8cSMark Johnston dsl_origindir_init(zfs_opt_t *zfs)
217240afd8cSMark Johnston {
218240afd8cSMark Johnston 	dnode_phys_t *clones;
219240afd8cSMark Johnston 	uint64_t clonesid;
220240afd8cSMark Johnston 
221240afd8cSMark Johnston 	zfs->origindsldir = dsl_metadir_alloc(zfs, "$ORIGIN");
222240afd8cSMark Johnston 	zfs->originds = dsl_dataset_alloc(zfs, zfs->origindsldir);
223240afd8cSMark Johnston 	zfs->snapds = dsl_dataset_alloc(zfs, zfs->origindsldir);
224240afd8cSMark Johnston 
225240afd8cSMark Johnston 	clones = objset_dnode_alloc(zfs->mos, DMU_OT_DSL_CLONES, &clonesid);
226240afd8cSMark Johnston 	zfs->cloneszap = zap_alloc(zfs->mos, clones);
227240afd8cSMark Johnston 	zfs->origindsldir->phys->dd_clones = clonesid;
228240afd8cSMark Johnston }
229240afd8cSMark Johnston 
230240afd8cSMark Johnston void
231240afd8cSMark Johnston dsl_init(zfs_opt_t *zfs)
232240afd8cSMark Johnston {
233240afd8cSMark Johnston 	zfs_dsl_dir_t *dir;
234240afd8cSMark Johnston 	struct dataset_desc *d;
235240afd8cSMark Johnston 	const char *dspropdelim;
236240afd8cSMark Johnston 
237240afd8cSMark Johnston 	dspropdelim = ";";
238240afd8cSMark Johnston 
239240afd8cSMark Johnston 	zfs->rootdsldir = dsl_dir_alloc(zfs, NULL);
240240afd8cSMark Johnston 
241240afd8cSMark Johnston 	nvlist_add_uint64(zfs->rootdsldir->propsnv, "compression",
242240afd8cSMark Johnston 	    ZIO_COMPRESS_OFF);
243240afd8cSMark Johnston 
244240afd8cSMark Johnston 	zfs->rootds = dsl_dataset_alloc(zfs, zfs->rootdsldir);
245240afd8cSMark Johnston 	zfs->rootdsldir->headds = zfs->rootds;
246240afd8cSMark Johnston 
247240afd8cSMark Johnston 	zfs->mosdsldir = dsl_metadir_alloc(zfs, "$MOS");
248240afd8cSMark Johnston 	zfs->freedsldir = dsl_metadir_alloc(zfs, "$FREE");
249240afd8cSMark Johnston 	dsl_origindir_init(zfs);
250240afd8cSMark Johnston 
251240afd8cSMark Johnston 	/*
252240afd8cSMark Johnston 	 * Go through the list of user-specified datasets and create DSL objects
253240afd8cSMark Johnston 	 * for them.
254240afd8cSMark Johnston 	 */
255240afd8cSMark Johnston 	STAILQ_FOREACH(d, &zfs->datasetdescs, next) {
256240afd8cSMark Johnston 		char *dsname, *next, *params, *param, *nextparam;
257240afd8cSMark Johnston 
258240afd8cSMark Johnston 		params = d->params;
259240afd8cSMark Johnston 		dsname = strsep(&params, dspropdelim);
260240afd8cSMark Johnston 
261240afd8cSMark Johnston 		if (strcmp(dsname, zfs->poolname) == 0) {
262240afd8cSMark Johnston 			/*
263240afd8cSMark Johnston 			 * This is the root dataset; it's already created, so
264240afd8cSMark Johnston 			 * we're just setting options.
265240afd8cSMark Johnston 			 */
266240afd8cSMark Johnston 			dir = zfs->rootdsldir;
267240afd8cSMark Johnston 		} else {
268240afd8cSMark Johnston 			/*
269240afd8cSMark Johnston 			 * This dataset must be a child of the root dataset.
270240afd8cSMark Johnston 			 */
271240afd8cSMark Johnston 			if (strstr(dsname, zfs->poolname) != dsname ||
272240afd8cSMark Johnston 			    (next = strchr(dsname, '/')) == NULL ||
273240afd8cSMark Johnston 			    (size_t)(next - dsname) != strlen(zfs->poolname)) {
274240afd8cSMark Johnston 				errx(1, "dataset `%s' must be a child of `%s'",
275240afd8cSMark Johnston 				    dsname, zfs->poolname);
276240afd8cSMark Johnston 			}
277240afd8cSMark Johnston 			dir = dsl_dir_alloc(zfs, dsname);
278240afd8cSMark Johnston 			dir->headds = dsl_dataset_alloc(zfs, dir);
279240afd8cSMark Johnston 		}
280240afd8cSMark Johnston 
281240afd8cSMark Johnston 		for (nextparam = param = params; nextparam != NULL;) {
282240afd8cSMark Johnston 			char *key, *val;
283240afd8cSMark Johnston 
284240afd8cSMark Johnston 			param = strsep(&nextparam, dspropdelim);
285240afd8cSMark Johnston 
286240afd8cSMark Johnston 			key = val = param;
287240afd8cSMark Johnston 			key = strsep(&val, "=");
288240afd8cSMark Johnston 			dsl_dir_set_prop(zfs, dir, key, val);
289240afd8cSMark Johnston 		}
290240afd8cSMark Johnston 	}
291240afd8cSMark Johnston 
292240afd8cSMark Johnston 	/*
293240afd8cSMark Johnston 	 * Set the root dataset's mount point if the user didn't override the
294240afd8cSMark Johnston 	 * default.
295240afd8cSMark Johnston 	 */
296240afd8cSMark Johnston 	if (nvpair_find(zfs->rootdsldir->propsnv, "mountpoint") == NULL) {
297240afd8cSMark Johnston 		nvlist_add_string(zfs->rootdsldir->propsnv, "mountpoint",
298240afd8cSMark Johnston 		    zfs->rootpath);
299240afd8cSMark Johnston 	}
300240afd8cSMark Johnston }
301240afd8cSMark Johnston 
302240afd8cSMark Johnston uint64_t
303240afd8cSMark Johnston dsl_dir_id(zfs_dsl_dir_t *dir)
304240afd8cSMark Johnston {
305240afd8cSMark Johnston 	return (dir->dirid);
306240afd8cSMark Johnston }
307240afd8cSMark Johnston 
308240afd8cSMark Johnston uint64_t
309240afd8cSMark Johnston dsl_dir_dataset_id(zfs_dsl_dir_t *dir)
310240afd8cSMark Johnston {
311240afd8cSMark Johnston 	return (dir->headds->dsid);
312240afd8cSMark Johnston }
313240afd8cSMark Johnston 
314240afd8cSMark Johnston static void
315240afd8cSMark Johnston dsl_dir_foreach_post(zfs_opt_t *zfs, zfs_dsl_dir_t *dsldir,
316240afd8cSMark Johnston     void (*cb)(zfs_opt_t *, zfs_dsl_dir_t *, void *), void *arg)
317240afd8cSMark Johnston {
318240afd8cSMark Johnston 	zfs_dsl_dir_t *cdsldir;
319240afd8cSMark Johnston 
320240afd8cSMark Johnston 	STAILQ_FOREACH(cdsldir, &dsldir->children, next) {
321240afd8cSMark Johnston 		dsl_dir_foreach_post(zfs, cdsldir, cb, arg);
322240afd8cSMark Johnston 	}
323240afd8cSMark Johnston 	cb(zfs, dsldir, arg);
324240afd8cSMark Johnston }
325240afd8cSMark Johnston 
326240afd8cSMark Johnston /*
327240afd8cSMark Johnston  * Used when the caller doesn't care about the order one way or another.
328240afd8cSMark Johnston  */
329240afd8cSMark Johnston void
330240afd8cSMark Johnston dsl_dir_foreach(zfs_opt_t *zfs, zfs_dsl_dir_t *dsldir,
331240afd8cSMark Johnston     void (*cb)(zfs_opt_t *, zfs_dsl_dir_t *, void *), void *arg)
332240afd8cSMark Johnston {
333240afd8cSMark Johnston 	dsl_dir_foreach_post(zfs, dsldir, cb, arg);
334240afd8cSMark Johnston }
335240afd8cSMark Johnston 
336240afd8cSMark Johnston const char *
337240afd8cSMark Johnston dsl_dir_fullname(const zfs_dsl_dir_t *dir)
338240afd8cSMark Johnston {
339240afd8cSMark Johnston 	return (dir->fullname);
340240afd8cSMark Johnston }
341240afd8cSMark Johnston 
342240afd8cSMark Johnston /*
343240afd8cSMark Johnston  * Create a DSL directory, which is effectively an entry in the ZFS namespace.
344240afd8cSMark Johnston  * We always create a root DSL directory, whose name is the pool's name, and
345240afd8cSMark Johnston  * several metadata directories.
346240afd8cSMark Johnston  *
347240afd8cSMark Johnston  * Each directory has two ZAP objects, one pointing to child directories, and
348240afd8cSMark Johnston  * one for properties (which are inherited by children unless overridden).
349240afd8cSMark Johnston  * Directories typically reference a DSL dataset, the "head dataset", which
350240afd8cSMark Johnston  * points to an object set.
351240afd8cSMark Johnston  */
352240afd8cSMark Johnston static zfs_dsl_dir_t *
353240afd8cSMark Johnston dsl_dir_alloc(zfs_opt_t *zfs, const char *name)
354240afd8cSMark Johnston {
355240afd8cSMark Johnston 	zfs_dsl_dir_list_t l, *lp;
356240afd8cSMark Johnston 	zfs_dsl_dir_t *dir, *parent;
357240afd8cSMark Johnston 	dnode_phys_t *dnode;
358240afd8cSMark Johnston 	char *dirname, *nextdir, *origname;
359240afd8cSMark Johnston 	uint64_t childid, propsid;
360240afd8cSMark Johnston 
361240afd8cSMark Johnston 	dir = ecalloc(1, sizeof(*dir));
362240afd8cSMark Johnston 
363240afd8cSMark Johnston 	dnode = objset_dnode_bonus_alloc(zfs->mos, DMU_OT_DSL_DIR,
364240afd8cSMark Johnston 	    DMU_OT_DSL_DIR, sizeof(dsl_dir_phys_t), &dir->dirid);
365240afd8cSMark Johnston 	dir->phys = (dsl_dir_phys_t *)DN_BONUS(dnode);
366240afd8cSMark Johnston 
367240afd8cSMark Johnston 	dnode = objset_dnode_alloc(zfs->mos, DMU_OT_DSL_PROPS, &propsid);
368240afd8cSMark Johnston 	dir->propszap = zap_alloc(zfs->mos, dnode);
369240afd8cSMark Johnston 
370240afd8cSMark Johnston 	dnode = objset_dnode_alloc(zfs->mos, DMU_OT_DSL_DIR_CHILD_MAP,
371240afd8cSMark Johnston 	    &childid);
372240afd8cSMark Johnston 	dir->childzap = zap_alloc(zfs->mos, dnode);
373240afd8cSMark Johnston 
374240afd8cSMark Johnston 	dir->propsnv = nvlist_create(NV_UNIQUE_NAME);
375240afd8cSMark Johnston 	STAILQ_INIT(&dir->children);
376240afd8cSMark Johnston 
377240afd8cSMark Johnston 	dir->phys->dd_child_dir_zapobj = childid;
378240afd8cSMark Johnston 	dir->phys->dd_props_zapobj = propsid;
379240afd8cSMark Johnston 
380240afd8cSMark Johnston 	if (name == NULL) {
381240afd8cSMark Johnston 		/*
382240afd8cSMark Johnston 		 * This is the root DSL directory.
383240afd8cSMark Johnston 		 */
384240afd8cSMark Johnston 		dir->name = estrdup(zfs->poolname);
385240afd8cSMark Johnston 		dir->fullname = estrdup(zfs->poolname);
386240afd8cSMark Johnston 		dir->parent = NULL;
387240afd8cSMark Johnston 		dir->phys->dd_parent_obj = 0;
388240afd8cSMark Johnston 
389240afd8cSMark Johnston 		assert(zfs->rootdsldir == NULL);
390240afd8cSMark Johnston 		zfs->rootdsldir = dir;
391240afd8cSMark Johnston 		return (dir);
392240afd8cSMark Johnston 	}
393240afd8cSMark Johnston 
394240afd8cSMark Johnston 	/*
395240afd8cSMark Johnston 	 * Insert the new directory into the hierarchy.  Currently this must be
396240afd8cSMark Johnston 	 * done in order, e.g., when creating pool/a/b, pool/a must already
397240afd8cSMark Johnston 	 * exist.
398240afd8cSMark Johnston 	 */
399240afd8cSMark Johnston 	STAILQ_INIT(&l);
400240afd8cSMark Johnston 	STAILQ_INSERT_HEAD(&l, zfs->rootdsldir, next);
401240afd8cSMark Johnston 	origname = dirname = nextdir = estrdup(name);
402240afd8cSMark Johnston 	for (lp = &l;; lp = &parent->children) {
403240afd8cSMark Johnston 		dirname = strsep(&nextdir, "/");
404240afd8cSMark Johnston 		if (nextdir == NULL)
405240afd8cSMark Johnston 			break;
406240afd8cSMark Johnston 
407240afd8cSMark Johnston 		STAILQ_FOREACH(parent, lp, next) {
408240afd8cSMark Johnston 			if (strcmp(parent->name, dirname) == 0)
409240afd8cSMark Johnston 				break;
410240afd8cSMark Johnston 		}
411240afd8cSMark Johnston 		if (parent == NULL) {
412240afd8cSMark Johnston 			errx(1, "no parent at `%s' for filesystem `%s'",
413240afd8cSMark Johnston 			    dirname, name);
414240afd8cSMark Johnston 		}
415240afd8cSMark Johnston 	}
416240afd8cSMark Johnston 
417240afd8cSMark Johnston 	dir->fullname = estrdup(name);
418240afd8cSMark Johnston 	dir->name = estrdup(dirname);
419240afd8cSMark Johnston 	free(origname);
420240afd8cSMark Johnston 	STAILQ_INSERT_TAIL(lp, dir, next);
421240afd8cSMark Johnston 	zap_add_uint64(parent->childzap, dir->name, dir->dirid);
422240afd8cSMark Johnston 
423240afd8cSMark Johnston 	dir->parent = parent;
424240afd8cSMark Johnston 	dir->phys->dd_parent_obj = parent->dirid;
425240afd8cSMark Johnston 	return (dir);
426240afd8cSMark Johnston }
427240afd8cSMark Johnston 
428240afd8cSMark Johnston void
4294f816f5bSMark Johnston dsl_dir_size_add(zfs_dsl_dir_t *dir, uint64_t bytes)
430240afd8cSMark Johnston {
4314f816f5bSMark Johnston 	dir->phys->dd_used_bytes += bytes;
4324f816f5bSMark Johnston 	dir->phys->dd_compressed_bytes += bytes;
4334f816f5bSMark Johnston 	dir->phys->dd_uncompressed_bytes += bytes;
434240afd8cSMark Johnston }
435240afd8cSMark Johnston 
436240afd8cSMark Johnston /*
437240afd8cSMark Johnston  * Convert dataset properties into entries in the DSL directory's properties
438240afd8cSMark Johnston  * ZAP.
439240afd8cSMark Johnston  */
440240afd8cSMark Johnston static void
441240afd8cSMark Johnston dsl_dir_finalize_props(zfs_dsl_dir_t *dir)
442240afd8cSMark Johnston {
443240afd8cSMark Johnston 	for (nvp_header_t *nvh = NULL;
444240afd8cSMark Johnston 	    (nvh = nvlist_next_nvpair(dir->propsnv, nvh)) != NULL;) {
445240afd8cSMark Johnston 		nv_string_t *nvname;
446240afd8cSMark Johnston 		nv_pair_data_t *nvdata;
447e2259837SMark Johnston 		char *name;
448240afd8cSMark Johnston 
449240afd8cSMark Johnston 		nvname = (nv_string_t *)(nvh + 1);
450240afd8cSMark Johnston 		nvdata = (nv_pair_data_t *)(&nvname->nv_data[0] +
451240afd8cSMark Johnston 		    NV_ALIGN4(nvname->nv_size));
452240afd8cSMark Johnston 
453240afd8cSMark Johnston 		name = nvstring_get(nvname);
454240afd8cSMark Johnston 		switch (nvdata->nv_type) {
455240afd8cSMark Johnston 		case DATA_TYPE_UINT64: {
456240afd8cSMark Johnston 			uint64_t val;
457240afd8cSMark Johnston 
458240afd8cSMark Johnston 			memcpy(&val, &nvdata->nv_data[0], sizeof(uint64_t));
459240afd8cSMark Johnston 			zap_add_uint64(dir->propszap, name, val);
460240afd8cSMark Johnston 			break;
461240afd8cSMark Johnston 		}
462240afd8cSMark Johnston 		case DATA_TYPE_STRING: {
463240afd8cSMark Johnston 			nv_string_t *nvstr;
464e2259837SMark Johnston 			char *val;
465240afd8cSMark Johnston 
466240afd8cSMark Johnston 			nvstr = (nv_string_t *)&nvdata->nv_data[0];
467e2259837SMark Johnston 			val = nvstring_get(nvstr);
468e2259837SMark Johnston 			zap_add_string(dir->propszap, name, val);
469e2259837SMark Johnston 			free(val);
470240afd8cSMark Johnston 			break;
471240afd8cSMark Johnston 		}
472240afd8cSMark Johnston 		default:
473240afd8cSMark Johnston 			assert(0);
474240afd8cSMark Johnston 		}
475e2259837SMark Johnston 		free(name);
476240afd8cSMark Johnston 	}
477240afd8cSMark Johnston }
478240afd8cSMark Johnston 
479240afd8cSMark Johnston static void
480240afd8cSMark Johnston dsl_dir_finalize(zfs_opt_t *zfs, zfs_dsl_dir_t *dir, void *arg __unused)
481240afd8cSMark Johnston {
482240afd8cSMark Johnston 	char key[32];
483240afd8cSMark Johnston 	zfs_dsl_dir_t *cdir;
484240afd8cSMark Johnston 	dnode_phys_t *snapnames;
485240afd8cSMark Johnston 	zfs_dsl_dataset_t *headds;
486240afd8cSMark Johnston 	zfs_objset_t *os;
487240afd8cSMark Johnston 	uint64_t bytes, snapnamesid;
488240afd8cSMark Johnston 
489240afd8cSMark Johnston 	dsl_dir_finalize_props(dir);
490240afd8cSMark Johnston 	zap_write(zfs, dir->propszap);
491240afd8cSMark Johnston 	zap_write(zfs, dir->childzap);
492240afd8cSMark Johnston 
493240afd8cSMark Johnston 	headds = dir->headds;
494240afd8cSMark Johnston 	if (headds == NULL)
495240afd8cSMark Johnston 		return;
496240afd8cSMark Johnston 	os = headds->os;
497240afd8cSMark Johnston 	if (os == NULL)
498240afd8cSMark Johnston 		return;
499240afd8cSMark Johnston 
500240afd8cSMark Johnston 	snapnames = objset_dnode_alloc(zfs->mos, DMU_OT_DSL_DS_SNAP_MAP,
501240afd8cSMark Johnston 	    &snapnamesid);
502240afd8cSMark Johnston 	zap_write(zfs, zap_alloc(zfs->mos, snapnames));
503240afd8cSMark Johnston 
504240afd8cSMark Johnston 	dir->phys->dd_head_dataset_obj = headds->dsid;
505240afd8cSMark Johnston 	dir->phys->dd_clone_parent_obj = zfs->snapds->dsid;
506240afd8cSMark Johnston 	headds->phys->ds_prev_snap_obj = zfs->snapds->dsid;
507240afd8cSMark Johnston 	headds->phys->ds_snapnames_zapobj = snapnamesid;
508240afd8cSMark Johnston 	objset_root_blkptr_copy(os, &headds->phys->ds_bp);
509240afd8cSMark Johnston 
510240afd8cSMark Johnston 	zfs->snapds->phys->ds_num_children++;
511240afd8cSMark Johnston 	snprintf(key, sizeof(key), "%jx", (uintmax_t)headds->dsid);
512240afd8cSMark Johnston 	zap_add_uint64(zfs->cloneszap, key, headds->dsid);
513240afd8cSMark Johnston 
514240afd8cSMark Johnston 	bytes = objset_space(os);
515240afd8cSMark Johnston 	headds->phys->ds_used_bytes = bytes;
516240afd8cSMark Johnston 	headds->phys->ds_uncompressed_bytes = bytes;
517240afd8cSMark Johnston 	headds->phys->ds_compressed_bytes = bytes;
518240afd8cSMark Johnston 
5194f816f5bSMark Johnston 	STAILQ_FOREACH(cdir, &dir->children, next) {
5204f816f5bSMark Johnston 		/*
5214f816f5bSMark Johnston 		 * The root directory needs a special case: the amount of
5224f816f5bSMark Johnston 		 * space used for the MOS isn't known until everything else is
5234f816f5bSMark Johnston 		 * finalized, so it can't be accounted in the MOS directory's
5244f816f5bSMark Johnston 		 * parent until then.
5254f816f5bSMark Johnston 		 */
5264f816f5bSMark Johnston 		if (dir == zfs->rootdsldir && cdir == zfs->mosdsldir)
5274f816f5bSMark Johnston 			continue;
528240afd8cSMark Johnston 		bytes += cdir->phys->dd_used_bytes;
5294f816f5bSMark Johnston 	}
5304f816f5bSMark Johnston 	dsl_dir_size_add(dir, bytes);
531240afd8cSMark Johnston }
532240afd8cSMark Johnston 
533240afd8cSMark Johnston void
534240afd8cSMark Johnston dsl_write(zfs_opt_t *zfs)
535240afd8cSMark Johnston {
536240afd8cSMark Johnston 	zfs_zap_t *snapnameszap;
537240afd8cSMark Johnston 	dnode_phys_t *snapnames;
538240afd8cSMark Johnston 	uint64_t snapmapid;
539240afd8cSMark Johnston 
540240afd8cSMark Johnston 	/*
541240afd8cSMark Johnston 	 * Perform accounting, starting from the leaves of the DSL directory
542240afd8cSMark Johnston 	 * tree.  Accounting for $MOS is done later, once we've finished
543240afd8cSMark Johnston 	 * allocating space.
544240afd8cSMark Johnston 	 */
545240afd8cSMark Johnston 	dsl_dir_foreach_post(zfs, zfs->rootdsldir, dsl_dir_finalize, NULL);
546240afd8cSMark Johnston 
547240afd8cSMark Johnston 	snapnames = objset_dnode_alloc(zfs->mos, DMU_OT_DSL_DS_SNAP_MAP,
548240afd8cSMark Johnston 	    &snapmapid);
549240afd8cSMark Johnston 	snapnameszap = zap_alloc(zfs->mos, snapnames);
550240afd8cSMark Johnston 	zap_add_uint64(snapnameszap, "$ORIGIN", zfs->snapds->dsid);
551240afd8cSMark Johnston 	zap_write(zfs, snapnameszap);
552240afd8cSMark Johnston 
553240afd8cSMark Johnston 	zfs->origindsldir->phys->dd_head_dataset_obj = zfs->originds->dsid;
554240afd8cSMark Johnston 	zfs->originds->phys->ds_prev_snap_obj = zfs->snapds->dsid;
555240afd8cSMark Johnston 	zfs->originds->phys->ds_snapnames_zapobj = snapmapid;
556240afd8cSMark Johnston 
557240afd8cSMark Johnston 	zfs->snapds->phys->ds_next_snap_obj = zfs->originds->dsid;
558240afd8cSMark Johnston 	assert(zfs->snapds->phys->ds_num_children > 0);
559240afd8cSMark Johnston 	zfs->snapds->phys->ds_num_children++;
560240afd8cSMark Johnston 
561240afd8cSMark Johnston 	zap_write(zfs, zfs->cloneszap);
562240afd8cSMark Johnston 
563240afd8cSMark Johnston 	/* XXX-MJ dirs and datasets are leaked */
564240afd8cSMark Johnston }
565240afd8cSMark Johnston 
566240afd8cSMark Johnston void
567240afd8cSMark Johnston dsl_dir_dataset_write(zfs_opt_t *zfs, zfs_objset_t *os, zfs_dsl_dir_t *dir)
568240afd8cSMark Johnston {
569240afd8cSMark Johnston 	dir->headds->os = os;
570240afd8cSMark Johnston 	objset_write(zfs, os);
571240afd8cSMark Johnston }
572240afd8cSMark Johnston 
573240afd8cSMark Johnston bool
574240afd8cSMark Johnston dsl_dir_has_dataset(zfs_dsl_dir_t *dir)
575240afd8cSMark Johnston {
576240afd8cSMark Johnston 	return (dir->headds != NULL);
577240afd8cSMark Johnston }
578240afd8cSMark Johnston 
579240afd8cSMark Johnston bool
580240afd8cSMark Johnston dsl_dir_dataset_has_objset(zfs_dsl_dir_t *dir)
581240afd8cSMark Johnston {
582240afd8cSMark Johnston 	return (dsl_dir_has_dataset(dir) && dir->headds->os != NULL);
583240afd8cSMark Johnston }
584240afd8cSMark Johnston 
585240afd8cSMark Johnston static zfs_dsl_dataset_t *
586240afd8cSMark Johnston dsl_dataset_alloc(zfs_opt_t *zfs, zfs_dsl_dir_t *dir)
587240afd8cSMark Johnston {
588240afd8cSMark Johnston 	zfs_dsl_dataset_t *ds;
589240afd8cSMark Johnston 	dnode_phys_t *dnode;
590240afd8cSMark Johnston 	uint64_t deadlistid;
591240afd8cSMark Johnston 
592240afd8cSMark Johnston 	ds = ecalloc(1, sizeof(*ds));
593240afd8cSMark Johnston 
594240afd8cSMark Johnston 	dnode = objset_dnode_bonus_alloc(zfs->mos, DMU_OT_DSL_DATASET,
595240afd8cSMark Johnston 	    DMU_OT_DSL_DATASET, sizeof(dsl_dataset_phys_t), &ds->dsid);
596240afd8cSMark Johnston 	ds->phys = (dsl_dataset_phys_t *)DN_BONUS(dnode);
597240afd8cSMark Johnston 
598240afd8cSMark Johnston 	dnode = objset_dnode_bonus_alloc(zfs->mos, DMU_OT_DEADLIST,
599240afd8cSMark Johnston 	    DMU_OT_DEADLIST_HDR, sizeof(dsl_deadlist_phys_t), &deadlistid);
600240afd8cSMark Johnston 	zap_write(zfs, zap_alloc(zfs->mos, dnode));
601240afd8cSMark Johnston 
602240afd8cSMark Johnston 	ds->phys->ds_dir_obj = dir->dirid;
603240afd8cSMark Johnston 	ds->phys->ds_deadlist_obj = deadlistid;
604240afd8cSMark Johnston 	ds->phys->ds_creation_txg = TXG - 1;
605240afd8cSMark Johnston 	if (ds != zfs->snapds)
606240afd8cSMark Johnston 		ds->phys->ds_prev_snap_txg = TXG - 1;
607240afd8cSMark Johnston 	ds->phys->ds_guid = ((uint64_t)random() << 32) | random();
608240afd8cSMark Johnston 	ds->dir = dir;
609240afd8cSMark Johnston 
610240afd8cSMark Johnston 	return (ds);
611240afd8cSMark Johnston }
612