1240afd8cSMark Johnston /*-
24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause
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>
32c6890399SJessica 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
nvlist_find_string(nvlist_t * nvl,const char * key,char ** retp)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);
78752ba100SMark Johnston if (error == 0) {
79752ba100SMark Johnston *retp = ecalloc(1, len + 1);
80752ba100SMark Johnston memcpy(*retp, str, len);
81752ba100SMark Johnston }
82240afd8cSMark Johnston return (error);
83240afd8cSMark Johnston }
84240afd8cSMark Johnston
85240afd8cSMark Johnston static int
nvlist_find_uint64(nvlist_t * nvl,const char * key,uint64_t * retp)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 *
dsl_dir_get_mountpoint(zfs_opt_t * zfs,zfs_dsl_dir_t * dir)99240afd8cSMark Johnston dsl_dir_get_mountpoint(zfs_opt_t *zfs, zfs_dsl_dir_t *dir)
100240afd8cSMark Johnston {
101240afd8cSMark Johnston zfs_dsl_dir_t *pdir;
102752ba100SMark Johnston char *mountpoint;
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 } else {
108240afd8cSMark Johnston /*
109240afd8cSMark Johnston * If we don't have a mountpoint, it's inherited from one of our
110240afd8cSMark Johnston * ancestors. Walk up the hierarchy until we find it, building
111240afd8cSMark Johnston * up our mountpoint along the way. The mountpoint property is
112240afd8cSMark Johnston * always set for the root dataset.
113240afd8cSMark Johnston */
114752ba100SMark Johnston for (pdir = dir->parent, mountpoint = estrdup(dir->name);;
115752ba100SMark Johnston pdir = pdir->parent) {
116752ba100SMark Johnston char *origmountpoint, *tmp;
117752ba100SMark Johnston
118240afd8cSMark Johnston origmountpoint = mountpoint;
119240afd8cSMark Johnston
120240afd8cSMark Johnston if (nvlist_find_string(pdir->propsnv, "mountpoint",
121752ba100SMark Johnston &tmp) == 0) {
122752ba100SMark Johnston easprintf(&mountpoint, "%s%s%s", tmp,
123752ba100SMark Johnston tmp[strlen(tmp) - 1] == '/' ? "" : "/",
124752ba100SMark Johnston origmountpoint);
125752ba100SMark Johnston free(tmp);
126240afd8cSMark Johnston free(origmountpoint);
127240afd8cSMark Johnston break;
128240afd8cSMark Johnston }
129240afd8cSMark Johnston
130240afd8cSMark Johnston easprintf(&mountpoint, "%s/%s", pdir->name,
131240afd8cSMark Johnston origmountpoint);
132240afd8cSMark Johnston free(origmountpoint);
133240afd8cSMark Johnston }
134240afd8cSMark Johnston }
135240afd8cSMark Johnston assert(mountpoint[0] == '/');
136240afd8cSMark Johnston assert(strstr(mountpoint, zfs->rootpath) == mountpoint);
137240afd8cSMark Johnston
138240afd8cSMark Johnston return (mountpoint);
139240afd8cSMark Johnston }
140240afd8cSMark Johnston
141240afd8cSMark Johnston int
dsl_dir_get_canmount(zfs_dsl_dir_t * dir,uint64_t * canmountp)142240afd8cSMark Johnston dsl_dir_get_canmount(zfs_dsl_dir_t *dir, uint64_t *canmountp)
143240afd8cSMark Johnston {
144240afd8cSMark Johnston return (nvlist_find_uint64(dir->propsnv, "canmount", canmountp));
145240afd8cSMark Johnston }
146240afd8cSMark Johnston
147240afd8cSMark Johnston /*
148240afd8cSMark Johnston * Handle dataset properties that we know about; stash them into an nvlist to be
149240afd8cSMark Johnston * written later to the properties ZAP object.
150240afd8cSMark Johnston *
151240afd8cSMark Johnston * If the set of properties we handle grows too much, we should probably explore
152240afd8cSMark Johnston * using libzfs to manage them.
153240afd8cSMark Johnston */
154240afd8cSMark Johnston static void
dsl_dir_set_prop(zfs_opt_t * zfs,zfs_dsl_dir_t * dir,const char * key,const char * val)155240afd8cSMark Johnston dsl_dir_set_prop(zfs_opt_t *zfs, zfs_dsl_dir_t *dir, const char *key,
156240afd8cSMark Johnston const char *val)
157240afd8cSMark Johnston {
158240afd8cSMark Johnston nvlist_t *nvl;
159240afd8cSMark Johnston
160240afd8cSMark Johnston nvl = dir->propsnv;
161240afd8cSMark Johnston if (val == NULL || val[0] == '\0')
162240afd8cSMark Johnston errx(1, "missing value for property `%s'", key);
163240afd8cSMark Johnston if (nvpair_find(nvl, key) != NULL)
164240afd8cSMark Johnston errx(1, "property `%s' already set", key);
165240afd8cSMark Johnston
166240afd8cSMark Johnston if (strcmp(key, "mountpoint") == 0) {
167240afd8cSMark Johnston if (strcmp(val, "none") != 0) {
168240afd8cSMark Johnston if (val[0] != '/')
169240afd8cSMark Johnston errx(1, "mountpoint `%s' is not absolute", val);
170240afd8cSMark Johnston if (strcmp(val, zfs->rootpath) != 0 &&
171240afd8cSMark Johnston strcmp(zfs->rootpath, "/") != 0 &&
172240afd8cSMark Johnston (strstr(val, zfs->rootpath) != val ||
173240afd8cSMark Johnston val[strlen(zfs->rootpath)] != '/')) {
174240afd8cSMark Johnston errx(1, "mountpoint `%s' is not prefixed by "
175240afd8cSMark Johnston "the root path `%s'", val, zfs->rootpath);
176240afd8cSMark Johnston }
177240afd8cSMark Johnston }
178240afd8cSMark Johnston nvlist_add_string(nvl, key, val);
179240afd8cSMark Johnston } else if (strcmp(key, "atime") == 0 || strcmp(key, "exec") == 0 ||
180240afd8cSMark Johnston strcmp(key, "setuid") == 0) {
181240afd8cSMark Johnston if (strcmp(val, "on") == 0)
182240afd8cSMark Johnston nvlist_add_uint64(nvl, key, 1);
183240afd8cSMark Johnston else if (strcmp(val, "off") == 0)
184240afd8cSMark Johnston nvlist_add_uint64(nvl, key, 0);
185240afd8cSMark Johnston else
186240afd8cSMark Johnston errx(1, "invalid value `%s' for %s", val, key);
187240afd8cSMark Johnston } else if (strcmp(key, "canmount") == 0) {
188240afd8cSMark Johnston if (strcmp(val, "noauto") == 0)
189240afd8cSMark Johnston nvlist_add_uint64(nvl, key, 2);
190240afd8cSMark Johnston else if (strcmp(val, "on") == 0)
191240afd8cSMark Johnston nvlist_add_uint64(nvl, key, 1);
192240afd8cSMark Johnston else if (strcmp(val, "off") == 0)
193240afd8cSMark Johnston nvlist_add_uint64(nvl, key, 0);
194240afd8cSMark Johnston else
195240afd8cSMark Johnston errx(1, "invalid value `%s' for %s", val, key);
196240afd8cSMark Johnston } else {
197240afd8cSMark Johnston errx(1, "unknown property `%s'", key);
198240afd8cSMark Johnston }
199240afd8cSMark Johnston }
200240afd8cSMark Johnston
201240afd8cSMark Johnston static zfs_dsl_dir_t *
dsl_metadir_alloc(zfs_opt_t * zfs,const char * name)202240afd8cSMark Johnston dsl_metadir_alloc(zfs_opt_t *zfs, const char *name)
203240afd8cSMark Johnston {
204240afd8cSMark Johnston zfs_dsl_dir_t *dir;
205240afd8cSMark Johnston char *path;
206240afd8cSMark Johnston
207240afd8cSMark Johnston easprintf(&path, "%s/%s", zfs->poolname, name);
208240afd8cSMark Johnston dir = dsl_dir_alloc(zfs, path);
209240afd8cSMark Johnston free(path);
210240afd8cSMark Johnston return (dir);
211240afd8cSMark Johnston }
212240afd8cSMark Johnston
213240afd8cSMark Johnston static void
dsl_origindir_init(zfs_opt_t * zfs)214240afd8cSMark Johnston dsl_origindir_init(zfs_opt_t *zfs)
215240afd8cSMark Johnston {
216240afd8cSMark Johnston dnode_phys_t *clones;
217240afd8cSMark Johnston uint64_t clonesid;
218240afd8cSMark Johnston
219240afd8cSMark Johnston zfs->origindsldir = dsl_metadir_alloc(zfs, "$ORIGIN");
220240afd8cSMark Johnston zfs->originds = dsl_dataset_alloc(zfs, zfs->origindsldir);
221240afd8cSMark Johnston zfs->snapds = dsl_dataset_alloc(zfs, zfs->origindsldir);
222240afd8cSMark Johnston
223240afd8cSMark Johnston clones = objset_dnode_alloc(zfs->mos, DMU_OT_DSL_CLONES, &clonesid);
224240afd8cSMark Johnston zfs->cloneszap = zap_alloc(zfs->mos, clones);
225240afd8cSMark Johnston zfs->origindsldir->phys->dd_clones = clonesid;
226240afd8cSMark Johnston }
227240afd8cSMark Johnston
228240afd8cSMark Johnston void
dsl_init(zfs_opt_t * zfs)229240afd8cSMark Johnston dsl_init(zfs_opt_t *zfs)
230240afd8cSMark Johnston {
231240afd8cSMark Johnston zfs_dsl_dir_t *dir;
232240afd8cSMark Johnston struct dataset_desc *d;
233240afd8cSMark Johnston const char *dspropdelim;
234240afd8cSMark Johnston
235240afd8cSMark Johnston dspropdelim = ";";
236240afd8cSMark Johnston
237240afd8cSMark Johnston zfs->rootdsldir = dsl_dir_alloc(zfs, NULL);
238240afd8cSMark Johnston
239240afd8cSMark Johnston nvlist_add_uint64(zfs->rootdsldir->propsnv, "compression",
240240afd8cSMark Johnston ZIO_COMPRESS_OFF);
241240afd8cSMark Johnston
242240afd8cSMark Johnston zfs->rootds = dsl_dataset_alloc(zfs, zfs->rootdsldir);
243240afd8cSMark Johnston zfs->rootdsldir->headds = zfs->rootds;
244240afd8cSMark Johnston
245240afd8cSMark Johnston zfs->mosdsldir = dsl_metadir_alloc(zfs, "$MOS");
246240afd8cSMark Johnston zfs->freedsldir = dsl_metadir_alloc(zfs, "$FREE");
247240afd8cSMark Johnston dsl_origindir_init(zfs);
248240afd8cSMark Johnston
249240afd8cSMark Johnston /*
250240afd8cSMark Johnston * Go through the list of user-specified datasets and create DSL objects
251240afd8cSMark Johnston * for them.
252240afd8cSMark Johnston */
253240afd8cSMark Johnston STAILQ_FOREACH(d, &zfs->datasetdescs, next) {
254240afd8cSMark Johnston char *dsname, *next, *params, *param, *nextparam;
255240afd8cSMark Johnston
256240afd8cSMark Johnston params = d->params;
257240afd8cSMark Johnston dsname = strsep(¶ms, dspropdelim);
258240afd8cSMark Johnston
259240afd8cSMark Johnston if (strcmp(dsname, zfs->poolname) == 0) {
260240afd8cSMark Johnston /*
261240afd8cSMark Johnston * This is the root dataset; it's already created, so
262240afd8cSMark Johnston * we're just setting options.
263240afd8cSMark Johnston */
264240afd8cSMark Johnston dir = zfs->rootdsldir;
265240afd8cSMark Johnston } else {
266240afd8cSMark Johnston /*
267240afd8cSMark Johnston * This dataset must be a child of the root dataset.
268240afd8cSMark Johnston */
269240afd8cSMark Johnston if (strstr(dsname, zfs->poolname) != dsname ||
270240afd8cSMark Johnston (next = strchr(dsname, '/')) == NULL ||
271240afd8cSMark Johnston (size_t)(next - dsname) != strlen(zfs->poolname)) {
272240afd8cSMark Johnston errx(1, "dataset `%s' must be a child of `%s'",
273240afd8cSMark Johnston dsname, zfs->poolname);
274240afd8cSMark Johnston }
275240afd8cSMark Johnston dir = dsl_dir_alloc(zfs, dsname);
276240afd8cSMark Johnston dir->headds = dsl_dataset_alloc(zfs, dir);
277240afd8cSMark Johnston }
278240afd8cSMark Johnston
279240afd8cSMark Johnston for (nextparam = param = params; nextparam != NULL;) {
280240afd8cSMark Johnston char *key, *val;
281240afd8cSMark Johnston
282240afd8cSMark Johnston param = strsep(&nextparam, dspropdelim);
283240afd8cSMark Johnston
284240afd8cSMark Johnston key = val = param;
285240afd8cSMark Johnston key = strsep(&val, "=");
286240afd8cSMark Johnston dsl_dir_set_prop(zfs, dir, key, val);
287240afd8cSMark Johnston }
288240afd8cSMark Johnston }
289240afd8cSMark Johnston
290240afd8cSMark Johnston /*
291240afd8cSMark Johnston * Set the root dataset's mount point if the user didn't override the
292240afd8cSMark Johnston * default.
293240afd8cSMark Johnston */
294240afd8cSMark Johnston if (nvpair_find(zfs->rootdsldir->propsnv, "mountpoint") == NULL) {
295240afd8cSMark Johnston nvlist_add_string(zfs->rootdsldir->propsnv, "mountpoint",
296240afd8cSMark Johnston zfs->rootpath);
297240afd8cSMark Johnston }
298240afd8cSMark Johnston }
299240afd8cSMark Johnston
300240afd8cSMark Johnston uint64_t
dsl_dir_id(zfs_dsl_dir_t * dir)301240afd8cSMark Johnston dsl_dir_id(zfs_dsl_dir_t *dir)
302240afd8cSMark Johnston {
303240afd8cSMark Johnston return (dir->dirid);
304240afd8cSMark Johnston }
305240afd8cSMark Johnston
306240afd8cSMark Johnston uint64_t
dsl_dir_dataset_id(zfs_dsl_dir_t * dir)307240afd8cSMark Johnston dsl_dir_dataset_id(zfs_dsl_dir_t *dir)
308240afd8cSMark Johnston {
309240afd8cSMark Johnston return (dir->headds->dsid);
310240afd8cSMark Johnston }
311240afd8cSMark Johnston
312240afd8cSMark Johnston static void
dsl_dir_foreach_post(zfs_opt_t * zfs,zfs_dsl_dir_t * dsldir,void (* cb)(zfs_opt_t *,zfs_dsl_dir_t *,void *),void * arg)313240afd8cSMark Johnston dsl_dir_foreach_post(zfs_opt_t *zfs, zfs_dsl_dir_t *dsldir,
314240afd8cSMark Johnston void (*cb)(zfs_opt_t *, zfs_dsl_dir_t *, void *), void *arg)
315240afd8cSMark Johnston {
316240afd8cSMark Johnston zfs_dsl_dir_t *cdsldir;
317240afd8cSMark Johnston
318240afd8cSMark Johnston STAILQ_FOREACH(cdsldir, &dsldir->children, next) {
319240afd8cSMark Johnston dsl_dir_foreach_post(zfs, cdsldir, cb, arg);
320240afd8cSMark Johnston }
321240afd8cSMark Johnston cb(zfs, dsldir, arg);
322240afd8cSMark Johnston }
323240afd8cSMark Johnston
324240afd8cSMark Johnston /*
325240afd8cSMark Johnston * Used when the caller doesn't care about the order one way or another.
326240afd8cSMark Johnston */
327240afd8cSMark Johnston void
dsl_dir_foreach(zfs_opt_t * zfs,zfs_dsl_dir_t * dsldir,void (* cb)(zfs_opt_t *,zfs_dsl_dir_t *,void *),void * arg)328240afd8cSMark Johnston dsl_dir_foreach(zfs_opt_t *zfs, zfs_dsl_dir_t *dsldir,
329240afd8cSMark Johnston void (*cb)(zfs_opt_t *, zfs_dsl_dir_t *, void *), void *arg)
330240afd8cSMark Johnston {
331240afd8cSMark Johnston dsl_dir_foreach_post(zfs, dsldir, cb, arg);
332240afd8cSMark Johnston }
333240afd8cSMark Johnston
334240afd8cSMark Johnston const char *
dsl_dir_fullname(const zfs_dsl_dir_t * dir)335240afd8cSMark Johnston dsl_dir_fullname(const zfs_dsl_dir_t *dir)
336240afd8cSMark Johnston {
337240afd8cSMark Johnston return (dir->fullname);
338240afd8cSMark Johnston }
339240afd8cSMark Johnston
340240afd8cSMark Johnston /*
341240afd8cSMark Johnston * Create a DSL directory, which is effectively an entry in the ZFS namespace.
342240afd8cSMark Johnston * We always create a root DSL directory, whose name is the pool's name, and
343240afd8cSMark Johnston * several metadata directories.
344240afd8cSMark Johnston *
345240afd8cSMark Johnston * Each directory has two ZAP objects, one pointing to child directories, and
346240afd8cSMark Johnston * one for properties (which are inherited by children unless overridden).
347240afd8cSMark Johnston * Directories typically reference a DSL dataset, the "head dataset", which
348240afd8cSMark Johnston * points to an object set.
349240afd8cSMark Johnston */
350240afd8cSMark Johnston static zfs_dsl_dir_t *
dsl_dir_alloc(zfs_opt_t * zfs,const char * name)351240afd8cSMark Johnston dsl_dir_alloc(zfs_opt_t *zfs, const char *name)
352240afd8cSMark Johnston {
353240afd8cSMark Johnston zfs_dsl_dir_list_t l, *lp;
354240afd8cSMark Johnston zfs_dsl_dir_t *dir, *parent;
355240afd8cSMark Johnston dnode_phys_t *dnode;
356240afd8cSMark Johnston char *dirname, *nextdir, *origname;
357240afd8cSMark Johnston uint64_t childid, propsid;
358240afd8cSMark Johnston
359240afd8cSMark Johnston dir = ecalloc(1, sizeof(*dir));
360240afd8cSMark Johnston
361240afd8cSMark Johnston dnode = objset_dnode_bonus_alloc(zfs->mos, DMU_OT_DSL_DIR,
362240afd8cSMark Johnston DMU_OT_DSL_DIR, sizeof(dsl_dir_phys_t), &dir->dirid);
363240afd8cSMark Johnston dir->phys = (dsl_dir_phys_t *)DN_BONUS(dnode);
364240afd8cSMark Johnston
365240afd8cSMark Johnston dnode = objset_dnode_alloc(zfs->mos, DMU_OT_DSL_PROPS, &propsid);
366240afd8cSMark Johnston dir->propszap = zap_alloc(zfs->mos, dnode);
367240afd8cSMark Johnston
368240afd8cSMark Johnston dnode = objset_dnode_alloc(zfs->mos, DMU_OT_DSL_DIR_CHILD_MAP,
369240afd8cSMark Johnston &childid);
370240afd8cSMark Johnston dir->childzap = zap_alloc(zfs->mos, dnode);
371240afd8cSMark Johnston
372240afd8cSMark Johnston dir->propsnv = nvlist_create(NV_UNIQUE_NAME);
373240afd8cSMark Johnston STAILQ_INIT(&dir->children);
374240afd8cSMark Johnston
375240afd8cSMark Johnston dir->phys->dd_child_dir_zapobj = childid;
376240afd8cSMark Johnston dir->phys->dd_props_zapobj = propsid;
377240afd8cSMark Johnston
378240afd8cSMark Johnston if (name == NULL) {
379240afd8cSMark Johnston /*
380240afd8cSMark Johnston * This is the root DSL directory.
381240afd8cSMark Johnston */
382240afd8cSMark Johnston dir->name = estrdup(zfs->poolname);
383240afd8cSMark Johnston dir->fullname = estrdup(zfs->poolname);
384240afd8cSMark Johnston dir->parent = NULL;
385240afd8cSMark Johnston dir->phys->dd_parent_obj = 0;
386240afd8cSMark Johnston
387240afd8cSMark Johnston assert(zfs->rootdsldir == NULL);
388240afd8cSMark Johnston zfs->rootdsldir = dir;
389240afd8cSMark Johnston return (dir);
390240afd8cSMark Johnston }
391240afd8cSMark Johnston
392240afd8cSMark Johnston /*
393240afd8cSMark Johnston * Insert the new directory into the hierarchy. Currently this must be
394240afd8cSMark Johnston * done in order, e.g., when creating pool/a/b, pool/a must already
395240afd8cSMark Johnston * exist.
396240afd8cSMark Johnston */
397240afd8cSMark Johnston STAILQ_INIT(&l);
398240afd8cSMark Johnston STAILQ_INSERT_HEAD(&l, zfs->rootdsldir, next);
399240afd8cSMark Johnston origname = dirname = nextdir = estrdup(name);
400240afd8cSMark Johnston for (lp = &l;; lp = &parent->children) {
401240afd8cSMark Johnston dirname = strsep(&nextdir, "/");
402240afd8cSMark Johnston if (nextdir == NULL)
403240afd8cSMark Johnston break;
404240afd8cSMark Johnston
405240afd8cSMark Johnston STAILQ_FOREACH(parent, lp, next) {
406240afd8cSMark Johnston if (strcmp(parent->name, dirname) == 0)
407240afd8cSMark Johnston break;
408240afd8cSMark Johnston }
409240afd8cSMark Johnston if (parent == NULL) {
410240afd8cSMark Johnston errx(1, "no parent at `%s' for filesystem `%s'",
411240afd8cSMark Johnston dirname, name);
412240afd8cSMark Johnston }
413240afd8cSMark Johnston }
414240afd8cSMark Johnston
415240afd8cSMark Johnston dir->fullname = estrdup(name);
416240afd8cSMark Johnston dir->name = estrdup(dirname);
417240afd8cSMark Johnston free(origname);
418240afd8cSMark Johnston STAILQ_INSERT_TAIL(lp, dir, next);
419240afd8cSMark Johnston zap_add_uint64(parent->childzap, dir->name, dir->dirid);
420240afd8cSMark Johnston
421240afd8cSMark Johnston dir->parent = parent;
422240afd8cSMark Johnston dir->phys->dd_parent_obj = parent->dirid;
423240afd8cSMark Johnston return (dir);
424240afd8cSMark Johnston }
425240afd8cSMark Johnston
426ef067b57SMark Johnston static void
dsl_dir_size_add(zfs_dsl_dir_t * dir,uint64_t bytes)4274f816f5bSMark Johnston dsl_dir_size_add(zfs_dsl_dir_t *dir, uint64_t bytes)
428240afd8cSMark Johnston {
4294f816f5bSMark Johnston dir->phys->dd_used_bytes += bytes;
4304f816f5bSMark Johnston dir->phys->dd_compressed_bytes += bytes;
4314f816f5bSMark Johnston dir->phys->dd_uncompressed_bytes += bytes;
432240afd8cSMark Johnston }
433240afd8cSMark Johnston
434240afd8cSMark Johnston /*
435ef067b57SMark Johnston * See dsl_dir_root_finalize().
436ef067b57SMark Johnston */
437ef067b57SMark Johnston void
dsl_dir_root_finalize(zfs_opt_t * zfs,uint64_t bytes)438ef067b57SMark Johnston dsl_dir_root_finalize(zfs_opt_t *zfs, uint64_t bytes)
439ef067b57SMark Johnston {
440ef067b57SMark Johnston dsl_dir_size_add(zfs->mosdsldir, bytes);
441ef067b57SMark Johnston zfs->mosdsldir->phys->dd_used_breakdown[DD_USED_HEAD] += bytes;
442ef067b57SMark Johnston
443ef067b57SMark Johnston dsl_dir_size_add(zfs->rootdsldir, bytes);
444ef067b57SMark Johnston zfs->rootdsldir->phys->dd_used_breakdown[DD_USED_CHILD] += bytes;
445ef067b57SMark Johnston }
446ef067b57SMark Johnston
447ef067b57SMark Johnston /*
448240afd8cSMark Johnston * Convert dataset properties into entries in the DSL directory's properties
449240afd8cSMark Johnston * ZAP.
450240afd8cSMark Johnston */
451240afd8cSMark Johnston static void
dsl_dir_finalize_props(zfs_dsl_dir_t * dir)452240afd8cSMark Johnston dsl_dir_finalize_props(zfs_dsl_dir_t *dir)
453240afd8cSMark Johnston {
454240afd8cSMark Johnston for (nvp_header_t *nvh = NULL;
455240afd8cSMark Johnston (nvh = nvlist_next_nvpair(dir->propsnv, nvh)) != NULL;) {
456240afd8cSMark Johnston nv_string_t *nvname;
457240afd8cSMark Johnston nv_pair_data_t *nvdata;
458e2259837SMark Johnston char *name;
459240afd8cSMark Johnston
460240afd8cSMark Johnston nvname = (nv_string_t *)(nvh + 1);
461240afd8cSMark Johnston nvdata = (nv_pair_data_t *)(&nvname->nv_data[0] +
462240afd8cSMark Johnston NV_ALIGN4(nvname->nv_size));
463240afd8cSMark Johnston
464240afd8cSMark Johnston name = nvstring_get(nvname);
465240afd8cSMark Johnston switch (nvdata->nv_type) {
466240afd8cSMark Johnston case DATA_TYPE_UINT64: {
467240afd8cSMark Johnston uint64_t val;
468240afd8cSMark Johnston
469240afd8cSMark Johnston memcpy(&val, &nvdata->nv_data[0], sizeof(uint64_t));
470240afd8cSMark Johnston zap_add_uint64(dir->propszap, name, val);
471240afd8cSMark Johnston break;
472240afd8cSMark Johnston }
473240afd8cSMark Johnston case DATA_TYPE_STRING: {
474240afd8cSMark Johnston nv_string_t *nvstr;
475e2259837SMark Johnston char *val;
476240afd8cSMark Johnston
477240afd8cSMark Johnston nvstr = (nv_string_t *)&nvdata->nv_data[0];
478e2259837SMark Johnston val = nvstring_get(nvstr);
479e2259837SMark Johnston zap_add_string(dir->propszap, name, val);
480e2259837SMark Johnston free(val);
481240afd8cSMark Johnston break;
482240afd8cSMark Johnston }
483240afd8cSMark Johnston default:
484240afd8cSMark Johnston assert(0);
485240afd8cSMark Johnston }
486e2259837SMark Johnston free(name);
487240afd8cSMark Johnston }
488240afd8cSMark Johnston }
489240afd8cSMark Johnston
490240afd8cSMark Johnston static void
dsl_dir_finalize(zfs_opt_t * zfs,zfs_dsl_dir_t * dir,void * arg __unused)491240afd8cSMark Johnston dsl_dir_finalize(zfs_opt_t *zfs, zfs_dsl_dir_t *dir, void *arg __unused)
492240afd8cSMark Johnston {
493240afd8cSMark Johnston zfs_dsl_dir_t *cdir;
494240afd8cSMark Johnston dnode_phys_t *snapnames;
495240afd8cSMark Johnston zfs_dsl_dataset_t *headds;
496240afd8cSMark Johnston zfs_objset_t *os;
49796092bf9SMark Johnston uint64_t bytes, childbytes, snapnamesid;
498240afd8cSMark Johnston
499240afd8cSMark Johnston dsl_dir_finalize_props(dir);
500240afd8cSMark Johnston zap_write(zfs, dir->propszap);
501240afd8cSMark Johnston zap_write(zfs, dir->childzap);
502240afd8cSMark Johnston
503240afd8cSMark Johnston headds = dir->headds;
504240afd8cSMark Johnston if (headds == NULL)
505240afd8cSMark Johnston return;
506240afd8cSMark Johnston os = headds->os;
507240afd8cSMark Johnston if (os == NULL)
508240afd8cSMark Johnston return;
509240afd8cSMark Johnston
510240afd8cSMark Johnston snapnames = objset_dnode_alloc(zfs->mos, DMU_OT_DSL_DS_SNAP_MAP,
511240afd8cSMark Johnston &snapnamesid);
512240afd8cSMark Johnston zap_write(zfs, zap_alloc(zfs->mos, snapnames));
513240afd8cSMark Johnston
514240afd8cSMark Johnston dir->phys->dd_head_dataset_obj = headds->dsid;
515240afd8cSMark Johnston dir->phys->dd_clone_parent_obj = zfs->snapds->dsid;
516240afd8cSMark Johnston headds->phys->ds_prev_snap_obj = zfs->snapds->dsid;
517240afd8cSMark Johnston headds->phys->ds_snapnames_zapobj = snapnamesid;
518240afd8cSMark Johnston objset_root_blkptr_copy(os, &headds->phys->ds_bp);
519240afd8cSMark Johnston
520240afd8cSMark Johnston zfs->snapds->phys->ds_num_children++;
521*be2f92a9SMark Johnston zap_add_uint64_self(zfs->cloneszap, headds->dsid);
522240afd8cSMark Johnston
523240afd8cSMark Johnston bytes = objset_space(os);
524240afd8cSMark Johnston headds->phys->ds_used_bytes = bytes;
525240afd8cSMark Johnston headds->phys->ds_uncompressed_bytes = bytes;
526240afd8cSMark Johnston headds->phys->ds_compressed_bytes = bytes;
527240afd8cSMark Johnston
52896092bf9SMark Johnston childbytes = 0;
5294f816f5bSMark Johnston STAILQ_FOREACH(cdir, &dir->children, next) {
5304f816f5bSMark Johnston /*
5314f816f5bSMark Johnston * The root directory needs a special case: the amount of
5324f816f5bSMark Johnston * space used for the MOS isn't known until everything else is
5334f816f5bSMark Johnston * finalized, so it can't be accounted in the MOS directory's
534ef067b57SMark Johnston * parent until then, at which point dsl_dir_root_finalize() is
535ef067b57SMark Johnston * called.
5364f816f5bSMark Johnston */
5374f816f5bSMark Johnston if (dir == zfs->rootdsldir && cdir == zfs->mosdsldir)
5384f816f5bSMark Johnston continue;
53996092bf9SMark Johnston childbytes += cdir->phys->dd_used_bytes;
5404f816f5bSMark Johnston }
54196092bf9SMark Johnston dsl_dir_size_add(dir, bytes + childbytes);
54296092bf9SMark Johnston
54396092bf9SMark Johnston dir->phys->dd_flags |= DD_FLAG_USED_BREAKDOWN;
54496092bf9SMark Johnston dir->phys->dd_used_breakdown[DD_USED_HEAD] = bytes;
54596092bf9SMark Johnston dir->phys->dd_used_breakdown[DD_USED_CHILD] = childbytes;
546240afd8cSMark Johnston }
547240afd8cSMark Johnston
548240afd8cSMark Johnston void
dsl_write(zfs_opt_t * zfs)549240afd8cSMark Johnston dsl_write(zfs_opt_t *zfs)
550240afd8cSMark Johnston {
551240afd8cSMark Johnston zfs_zap_t *snapnameszap;
552240afd8cSMark Johnston dnode_phys_t *snapnames;
553240afd8cSMark Johnston uint64_t snapmapid;
554240afd8cSMark Johnston
555240afd8cSMark Johnston /*
556240afd8cSMark Johnston * Perform accounting, starting from the leaves of the DSL directory
557240afd8cSMark Johnston * tree. Accounting for $MOS is done later, once we've finished
558240afd8cSMark Johnston * allocating space.
559240afd8cSMark Johnston */
560240afd8cSMark Johnston dsl_dir_foreach_post(zfs, zfs->rootdsldir, dsl_dir_finalize, NULL);
561240afd8cSMark Johnston
562240afd8cSMark Johnston snapnames = objset_dnode_alloc(zfs->mos, DMU_OT_DSL_DS_SNAP_MAP,
563240afd8cSMark Johnston &snapmapid);
564240afd8cSMark Johnston snapnameszap = zap_alloc(zfs->mos, snapnames);
565240afd8cSMark Johnston zap_add_uint64(snapnameszap, "$ORIGIN", zfs->snapds->dsid);
566240afd8cSMark Johnston zap_write(zfs, snapnameszap);
567240afd8cSMark Johnston
568240afd8cSMark Johnston zfs->origindsldir->phys->dd_head_dataset_obj = zfs->originds->dsid;
569240afd8cSMark Johnston zfs->originds->phys->ds_prev_snap_obj = zfs->snapds->dsid;
570240afd8cSMark Johnston zfs->originds->phys->ds_snapnames_zapobj = snapmapid;
571240afd8cSMark Johnston
572240afd8cSMark Johnston zfs->snapds->phys->ds_next_snap_obj = zfs->originds->dsid;
573240afd8cSMark Johnston assert(zfs->snapds->phys->ds_num_children > 0);
574240afd8cSMark Johnston zfs->snapds->phys->ds_num_children++;
575240afd8cSMark Johnston
576240afd8cSMark Johnston zap_write(zfs, zfs->cloneszap);
577240afd8cSMark Johnston
578240afd8cSMark Johnston /* XXX-MJ dirs and datasets are leaked */
579240afd8cSMark Johnston }
580240afd8cSMark Johnston
581240afd8cSMark Johnston void
dsl_dir_dataset_write(zfs_opt_t * zfs,zfs_objset_t * os,zfs_dsl_dir_t * dir)582240afd8cSMark Johnston dsl_dir_dataset_write(zfs_opt_t *zfs, zfs_objset_t *os, zfs_dsl_dir_t *dir)
583240afd8cSMark Johnston {
584240afd8cSMark Johnston dir->headds->os = os;
585240afd8cSMark Johnston objset_write(zfs, os);
586240afd8cSMark Johnston }
587240afd8cSMark Johnston
588240afd8cSMark Johnston bool
dsl_dir_has_dataset(zfs_dsl_dir_t * dir)589240afd8cSMark Johnston dsl_dir_has_dataset(zfs_dsl_dir_t *dir)
590240afd8cSMark Johnston {
591240afd8cSMark Johnston return (dir->headds != NULL);
592240afd8cSMark Johnston }
593240afd8cSMark Johnston
594240afd8cSMark Johnston bool
dsl_dir_dataset_has_objset(zfs_dsl_dir_t * dir)595240afd8cSMark Johnston dsl_dir_dataset_has_objset(zfs_dsl_dir_t *dir)
596240afd8cSMark Johnston {
597240afd8cSMark Johnston return (dsl_dir_has_dataset(dir) && dir->headds->os != NULL);
598240afd8cSMark Johnston }
599240afd8cSMark Johnston
600240afd8cSMark Johnston static zfs_dsl_dataset_t *
dsl_dataset_alloc(zfs_opt_t * zfs,zfs_dsl_dir_t * dir)601240afd8cSMark Johnston dsl_dataset_alloc(zfs_opt_t *zfs, zfs_dsl_dir_t *dir)
602240afd8cSMark Johnston {
603240afd8cSMark Johnston zfs_dsl_dataset_t *ds;
604240afd8cSMark Johnston dnode_phys_t *dnode;
605240afd8cSMark Johnston uint64_t deadlistid;
606240afd8cSMark Johnston
607240afd8cSMark Johnston ds = ecalloc(1, sizeof(*ds));
608240afd8cSMark Johnston
609240afd8cSMark Johnston dnode = objset_dnode_bonus_alloc(zfs->mos, DMU_OT_DSL_DATASET,
610240afd8cSMark Johnston DMU_OT_DSL_DATASET, sizeof(dsl_dataset_phys_t), &ds->dsid);
611240afd8cSMark Johnston ds->phys = (dsl_dataset_phys_t *)DN_BONUS(dnode);
612240afd8cSMark Johnston
613240afd8cSMark Johnston dnode = objset_dnode_bonus_alloc(zfs->mos, DMU_OT_DEADLIST,
614240afd8cSMark Johnston DMU_OT_DEADLIST_HDR, sizeof(dsl_deadlist_phys_t), &deadlistid);
615240afd8cSMark Johnston zap_write(zfs, zap_alloc(zfs->mos, dnode));
616240afd8cSMark Johnston
617240afd8cSMark Johnston ds->phys->ds_dir_obj = dir->dirid;
618240afd8cSMark Johnston ds->phys->ds_deadlist_obj = deadlistid;
619240afd8cSMark Johnston ds->phys->ds_creation_txg = TXG - 1;
620240afd8cSMark Johnston if (ds != zfs->snapds)
621240afd8cSMark Johnston ds->phys->ds_prev_snap_txg = TXG - 1;
62214c5cf3aSMark Johnston ds->phys->ds_guid = randomguid();
623240afd8cSMark Johnston ds->dir = dir;
624240afd8cSMark Johnston
625240afd8cSMark Johnston return (ds);
626240afd8cSMark Johnston }
627