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(¶ms, 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