int zfs_sa_set_xattr(znode_t *zp) { zfs_sb_t *zsb = ZTOZSB(zp); dmu_tx_t *tx; char *obj; size_t size; int error; ASSERT(RW_WRITE_HELD(&zp->z_xattr_lock)); ASSERT(zp->z_xattr_cached); ASSERT(zp->z_is_sa); error = nvlist_size(zp->z_xattr_cached, &size, NV_ENCODE_XDR); if (error) goto out; obj = zio_buf_alloc(size); error = nvlist_pack(zp->z_xattr_cached, &obj, &size, NV_ENCODE_XDR, KM_SLEEP); if (error) goto out_free; tx = dmu_tx_create(zsb->z_os); dmu_tx_hold_sa_create(tx, size); dmu_tx_hold_sa(tx, zp->z_sa_hdl, B_TRUE); error = dmu_tx_assign(tx, TXG_WAIT); if (error) { dmu_tx_abort(tx); } else { error = sa_update(zp->z_sa_hdl, SA_ZPL_DXATTR(zsb), obj, size, tx); if (error) dmu_tx_abort(tx); else dmu_tx_commit(tx); } out_free: zio_buf_free(obj, size); out: return (error); }
static int pci_iov_get_schema_ioctl(struct cdev *cdev, struct pci_iov_schema *output) { struct pci_devinfo *dinfo; void *packed; size_t output_len, size; int error; packed = NULL; mtx_lock(&Giant); dinfo = cdev->si_drv1; packed = nvlist_pack(dinfo->cfg.iov->iov_schema, &size); mtx_unlock(&Giant); if (packed == NULL) { error = ENOMEM; goto fail; } output_len = output->len; output->len = size; if (size <= output_len) { error = copyout(packed, output->schema, size); if (error != 0) goto fail; output->error = 0; } else /* * If we return an error then the ioctl code won't copyout * output back to userland, so we flag the error in the struct * instead. */ output->error = EMSGSIZE; error = 0; fail: free(packed, M_NVLIST); return (error); }
int zfs_sa_set_xattr(znode_t *zp) { zfsvfs_t *zfsvfs = ZTOZSB(zp); dmu_tx_t *tx; char *obj; size_t size; int error; ASSERT(RW_WRITE_HELD(&zp->z_xattr_lock)); ASSERT(zp->z_xattr_cached); ASSERT(zp->z_is_sa); error = nvlist_size(zp->z_xattr_cached, &size, NV_ENCODE_XDR); if ((error == 0) && (size > SA_ATTR_MAX_LEN)) error = EFBIG; if (error) goto out; obj = vmem_alloc(size, KM_SLEEP); error = nvlist_pack(zp->z_xattr_cached, &obj, &size, NV_ENCODE_XDR, KM_SLEEP); if (error) goto out_free; tx = dmu_tx_create(zfsvfs->z_os); dmu_tx_hold_sa_create(tx, size); dmu_tx_hold_sa(tx, zp->z_sa_hdl, B_TRUE); error = dmu_tx_assign(tx, TXG_WAIT); if (error) { dmu_tx_abort(tx); } else { VERIFY0(sa_update(zp->z_sa_hdl, SA_ZPL_DXATTR(zfsvfs), obj, size, tx)); dmu_tx_commit(tx); } out_free: vmem_free(obj, size); out: return (error); }
int zcmd_write_src_nvlist(libzfs_handle_t *hdl, zfs_cmd_t *zc, nvlist_t *nvl, size_t *size) { char *packed; size_t len; verify(nvlist_size(nvl, &len, NV_ENCODE_NATIVE) == 0); if ((packed = zfs_alloc(hdl, len)) == NULL) return (-1); verify(nvlist_pack(nvl, &packed, &len, NV_ENCODE_NATIVE, 0) == 0); zc->zc_nvlist_src = (uint64_t)(uintptr_t)packed; zc->zc_nvlist_src_size = len; if (size) *size = len; return (0); }
static PyObject * py_set_fsacl(PyObject *self, PyObject *args) { int un; size_t nvsz; zfs_cmd_t zc = { 0 }; char *name, *nvbuf; PyObject *dict, *file; nvlist_t *nvl; int err; if (!PyArg_ParseTuple(args, "siO!", &name, &un, &PyDict_Type, &dict)) return (NULL); nvl = dict2nvl(dict); if (nvl == NULL) return (NULL); err = nvlist_size(nvl, &nvsz, NV_ENCODE_NATIVE); assert(err == 0); nvbuf = malloc(nvsz); err = nvlist_pack(nvl, &nvbuf, &nvsz, NV_ENCODE_NATIVE, 0); assert(err == 0); (void) strlcpy(zc.zc_name, name, sizeof (zc.zc_name)); zc.zc_nvlist_src_size = nvsz; zc.zc_nvlist_src = (uintptr_t)nvbuf; zc.zc_perm_action = un; err = ioctl_with_cmdstr(ZFS_IOC_SET_FSACL, &zc); free(nvbuf); if (err) { seterr(_("cannot set permissions on %s"), name); return (NULL); } Py_RETURN_NONE; }
/* * This function posts the incoming piclevent * It packs the nvlist and posts it to PICL */ static void parse_piclevent(nvlist_t *nvlp) { char *enval; char *ename; size_t nvl_size; char *packed_nvl; int err; if (nvlist_lookup_string(nvlp, PICLEVENTARG_EVENT_NAME, &enval)) return; packed_nvl = NULL; if (nvlist_pack(nvlp, &packed_nvl, &nvl_size, NV_ENCODE_NATIVE, NULL)) return; ename = strdup(enval); if (ename == NULL) { free(packed_nvl); return; } if (piclevent_debug) { syslog(LOG_INFO, "piclevent: posting ename:%s packed_nvl:%p " "nvl_size:0x%x\n", ename, packed_nvl, nvl_size); } err = ptree_post_event(ename, packed_nvl, nvl_size, piclevent_completion_handler); if (err != PICL_SUCCESS) { if (piclevent_debug) syslog(LOG_INFO, "piclevent: posting ename:%s failed err:%d\n", ename, err); free(ename); free(packed_nvl); } }
static int ippctl_add_nvlist( nvlist_t *nvlp, int i) { char *buf; size_t buflen; int rc; /* * NULL the buffer pointer so that a buffer is automatically * allocated for us. */ buf = NULL; /* * Pack the nvlist and get back the buffer pointer and length. */ if ((rc = nvlist_pack(nvlp, &buf, &buflen, NV_ENCODE_NATIVE, KM_SLEEP)) != 0) { ippctl_array[i].buf = NULL; ippctl_array[i].buflen = 0; return (rc); } DBG2(DBG_CBOPS, "added nvlist[%d]: length %lu\n", i, buflen); /* * Store the pointer an length in the array at the given index. */ ippctl_array[i].buf = buf; ippctl_array[i].buflen = buflen; return (0); }
int smb_kmod_unshare(nvlist_t *shrlist) { smb_ioc_share_t *ioc; uint32_t ioclen; char *shrbuf = NULL; size_t bufsz; int rc = ENOMEM; if ((rc = nvlist_pack(shrlist, &shrbuf, &bufsz, NV_ENCODE_XDR, 0)) != 0) return (rc); ioclen = sizeof (smb_ioc_share_t) + bufsz; if ((ioc = malloc(ioclen)) != NULL) { ioc->shrlen = bufsz; bcopy(shrbuf, ioc->shr, bufsz); rc = smb_kmod_ioctl(SMB_IOC_UNSHARE, &ioc->hdr, ioclen); free(ioc); } free(shrbuf); return (rc); }
/* * Create the storage dirs, and pass the path list to the kernel. * This requires the nfssrv module to be loaded; the _nfssys() syscall * will fail ENOTSUP if it is not. * Use libnvpair(3LIB) to pass the data to the kernel. */ static int dss_init(uint_t npaths, char **pathnames) { int i, j, nskipped, error; char *bufp; uint32_t bufsize; size_t buflen; nvlist_t *nvl; if (npaths > 1) { /* * We need to remove duplicate paths; this might be user error * in the general case, but HA-NFSv4 can also cause this. * Sort the pathnames array, and NULL out duplicates, * then write the non-NULL entries to a new array. * Sorting will also allow the kernel to optimise its searches. */ qsort(pathnames, npaths, sizeof (char *), qstrcmp); /* now NULL out any duplicates */ i = 0; j = 1; nskipped = 0; while (j < npaths) { if (strcmp(pathnames[i], pathnames[j]) == NULL) { pathnames[j] = NULL; j++; nskipped++; continue; } /* skip i over any of its NULLed duplicates */ i = j++; } /* finally, write the non-NULL entries to a new array */ if (nskipped > 0) { int nreal; size_t sz; char **tmp_pathnames; nreal = npaths - nskipped; sz = nreal * sizeof (char *); tmp_pathnames = (char **)malloc(sz); if (tmp_pathnames == NULL) { fprintf(stderr, "tmp_pathnames malloc " "failed\n"); exit(1); } for (i = 0, j = 0; i < npaths; i++) if (pathnames[i] != NULL) tmp_pathnames[j++] = pathnames[i]; free(pathnames); pathnames = tmp_pathnames; npaths = nreal; } } /* Create directories to store the distributed state files */ dss_mkleafdirs(npaths, pathnames); /* Create the name-value pair list */ error = nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0); if (error) { fprintf(stderr, "nvlist_alloc failed: %s\n", strerror(errno)); return (1); } /* Add the pathnames array as a single name-value pair */ error = nvlist_add_string_array(nvl, NFS4_DSS_NVPAIR_NAME, pathnames, npaths); if (error) { fprintf(stderr, "nvlist_add_string_array failed: %s\n", strerror(errno)); nvlist_free(nvl); return (1); } /* * Pack list into contiguous memory, for passing to kernel. * nvlist_pack() will allocate the memory for the buffer, * which we should free() when no longer needed. * NV_ENCODE_XDR for safety across ILP32/LP64 kernel boundary. */ bufp = NULL; error = nvlist_pack(nvl, &bufp, &buflen, NV_ENCODE_XDR, 0); if (error) { fprintf(stderr, "nvlist_pack failed: %s\n", strerror(errno)); nvlist_free(nvl); return (1); } /* Now we have the packed buffer, we no longer need the list */ nvlist_free(nvl); /* * Let the kernel know in advance how big the buffer is. * NOTE: we cannot just pass buflen, since size_t is a long, and * thus a different size between ILP32 userland and LP64 kernel. * Use an int for the transfer, since that should be big enough; * this is a no-op at the moment, here, since nfsd is 32-bit, but * that could change. */ bufsize = (uint32_t)buflen; error = _nfssys(NFS4_DSS_SETPATHS_SIZE, &bufsize); if (error) { fprintf(stderr, "_nfssys(NFS4_DSS_SETPATHS_SIZE) failed: %s\n", strerror(errno)); free(bufp); return (1); } /* Pass the packed buffer to the kernel */ error = _nfssys(NFS4_DSS_SETPATHS, bufp); if (error) { fprintf(stderr, "_nfssys(NFS4_DSS_SETPATHS) failed: %s\n", strerror(errno)); free(bufp); return (1); } /* * The kernel has now unpacked the buffer and extracted the * pathnames array, we no longer need the buffer. */ free(bufp); return (0); }
/* * Create the named pool, using the provided vdev list. It is assumed * that the consumer has already validated the contents of the nvlist, so we * don't have to worry about error semantics. */ int zpool_create(libzfs_handle_t *hdl, const char *pool, nvlist_t *nvroot, const char *altroot) { zfs_cmd_t zc = { 0 }; char *packed; size_t len; char msg[1024]; (void) snprintf(msg, sizeof (msg), dgettext(TEXT_DOMAIN, "cannot create '%s'"), pool); if (!zpool_name_valid(hdl, B_FALSE, pool)) return (zfs_error(hdl, EZFS_INVALIDNAME, msg)); if (altroot != NULL && altroot[0] != '/') return (zfs_error(hdl, EZFS_BADPATH, dgettext(TEXT_DOMAIN, "bad alternate root '%s'"), altroot)); if (nvlist_size(nvroot, &len, NV_ENCODE_NATIVE) != 0) return (no_memory(hdl)); if ((packed = zfs_alloc(hdl, len)) == NULL) return (-1); if (nvlist_pack(nvroot, &packed, &len, NV_ENCODE_NATIVE, 0) != 0) { free(packed); return (no_memory(hdl)); } (void) strlcpy(zc.zc_name, pool, sizeof (zc.zc_name)); zc.zc_config_src = (uint64_t)(uintptr_t)packed; zc.zc_config_src_size = len; if (altroot != NULL) (void) strlcpy(zc.zc_root, altroot, sizeof (zc.zc_root)); if (ioctl(hdl->libzfs_fd, ZFS_IOC_POOL_CREATE, &zc) != 0) { free(packed); switch (errno) { case EBUSY: /* * This can happen if the user has specified the same * device multiple times. We can't reliably detect this * until we try to add it and see we already have a * label. */ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "one or more vdevs refer to the same device")); return (zfs_error(hdl, EZFS_BADDEV, msg)); case EOVERFLOW: /* * This occurs when one of the devices is below * SPA_MINDEVSIZE. Unfortunately, we can't detect which * device was the problem device since there's no * reliable way to determine device size from userland. */ { char buf[64]; zfs_nicenum(SPA_MINDEVSIZE, buf, sizeof (buf)); zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "one or more devices is less than the " "minimum size (%s)"), buf); } return (zfs_error(hdl, EZFS_BADDEV, msg)); case ENOSPC: zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "one or more devices is out of space")); return (zfs_error(hdl, EZFS_BADDEV, msg)); default: return (zpool_standard_error(hdl, errno, msg)); } } free(packed); /* * If this is an alternate root pool, then we automatically set the * moutnpoint of the root dataset to be '/'. */ if (altroot != NULL) { zfs_handle_t *zhp; verify((zhp = zfs_open(hdl, pool, ZFS_TYPE_ANY)) != NULL); verify(zfs_prop_set(zhp, ZFS_PROP_MOUNTPOINT, "/") == 0); zfs_close(zhp); } return (0); }
/* * sync out AVL trees to persistent storage. */ void zfs_fuid_sync(zfsvfs_t *zfsvfs, dmu_tx_t *tx) { #ifdef HAVE_ZPL nvlist_t *nvp; nvlist_t **fuids; size_t nvsize = 0; char *packed; dmu_buf_t *db; fuid_domain_t *domnode; int numnodes; int i; if (!zfsvfs->z_fuid_dirty) { return; } rw_enter(&zfsvfs->z_fuid_lock, RW_WRITER); /* * First see if table needs to be created? */ if (zfsvfs->z_fuid_obj == 0) { zfsvfs->z_fuid_obj = dmu_object_alloc(zfsvfs->z_os, DMU_OT_FUID, 1 << 14, DMU_OT_FUID_SIZE, sizeof (uint64_t), tx); VERIFY(zap_add(zfsvfs->z_os, MASTER_NODE_OBJ, ZFS_FUID_TABLES, sizeof (uint64_t), 1, &zfsvfs->z_fuid_obj, tx) == 0); } VERIFY(nvlist_alloc(&nvp, NV_UNIQUE_NAME, KM_SLEEP) == 0); numnodes = avl_numnodes(&zfsvfs->z_fuid_idx); fuids = kmem_alloc(numnodes * sizeof (void *), KM_SLEEP); for (i = 0, domnode = avl_first(&zfsvfs->z_fuid_domain); domnode; i++, domnode = AVL_NEXT(&zfsvfs->z_fuid_domain, domnode)) { VERIFY(nvlist_alloc(&fuids[i], NV_UNIQUE_NAME, KM_SLEEP) == 0); VERIFY(nvlist_add_uint64(fuids[i], FUID_IDX, domnode->f_idx) == 0); VERIFY(nvlist_add_uint64(fuids[i], FUID_OFFSET, 0) == 0); VERIFY(nvlist_add_string(fuids[i], FUID_DOMAIN, domnode->f_ksid->kd_name) == 0); } VERIFY(nvlist_add_nvlist_array(nvp, FUID_NVP_ARRAY, fuids, numnodes) == 0); for (i = 0; i != numnodes; i++) nvlist_free(fuids[i]); kmem_free(fuids, numnodes * sizeof (void *)); VERIFY(nvlist_size(nvp, &nvsize, NV_ENCODE_XDR) == 0); packed = kmem_alloc(nvsize, KM_SLEEP); VERIFY(nvlist_pack(nvp, &packed, &nvsize, NV_ENCODE_XDR, KM_SLEEP) == 0); nvlist_free(nvp); zfsvfs->z_fuid_size = nvsize; dmu_write(zfsvfs->z_os, zfsvfs->z_fuid_obj, 0, zfsvfs->z_fuid_size, packed, tx); kmem_free(packed, zfsvfs->z_fuid_size); VERIFY(0 == dmu_bonus_hold(zfsvfs->z_os, zfsvfs->z_fuid_obj, FTAG, &db)); dmu_buf_will_dirty(db, tx); *(uint64_t *)db->db_data = zfsvfs->z_fuid_size; dmu_buf_rele(db, FTAG); zfsvfs->z_fuid_dirty = B_FALSE; rw_exit(&zfsvfs->z_fuid_lock); #endif /* HAVE_ZPL */ }
static void spa_config_write(spa_config_dirent_t *dp, nvlist_t *nvl) { size_t buflen; char *buf; vnode_t *vp; int oflags = FWRITE | FTRUNC | FCREAT | FOFFMAX; int error; char *temp; /* * If the nvlist is empty (NULL), then remove the old cachefile. */ if (nvl == NULL) { (void) vn_remove(dp->scd_path, UIO_SYSSPACE, RMFILE); return; } /* * Pack the configuration into a buffer. */ VERIFY(nvlist_size(nvl, &buflen, NV_ENCODE_XDR) == 0); buf = vmem_alloc(buflen, KM_SLEEP); temp = kmem_zalloc(MAXPATHLEN, KM_SLEEP); VERIFY(nvlist_pack(nvl, &buf, &buflen, NV_ENCODE_XDR, KM_SLEEP) == 0); #if defined(__linux__) && defined(_KERNEL) /* * Write the configuration to disk. Due to the complexity involved * in performing a rename from within the kernel the file is truncated * and overwritten in place. In the event of an error the file is * unlinked to make sure we always have a consistent view of the data. */ error = vn_open(dp->scd_path, UIO_SYSSPACE, oflags, 0644, &vp, 0, 0); if (error == 0) { error = vn_rdwr(UIO_WRITE, vp, buf, buflen, 0, UIO_SYSSPACE, 0, RLIM64_INFINITY, kcred, NULL); if (error == 0) error = VOP_FSYNC(vp, FSYNC, kcred, NULL); (void) VOP_CLOSE(vp, oflags, 1, 0, kcred, NULL); if (error) (void) vn_remove(dp->scd_path, UIO_SYSSPACE, RMFILE); } #else /* * Write the configuration to disk. We need to do the traditional * 'write to temporary file, sync, move over original' to make sure we * always have a consistent view of the data. */ (void) snprintf(temp, MAXPATHLEN, "%s.tmp", dp->scd_path); error = vn_open(temp, UIO_SYSSPACE, oflags, 0644, &vp, CRCREAT, 0); if (error == 0) { if (vn_rdwr(UIO_WRITE, vp, buf, buflen, 0, UIO_SYSSPACE, 0, RLIM64_INFINITY, kcred, NULL) == 0 && VOP_FSYNC(vp, FSYNC, kcred, NULL) == 0) { (void) vn_rename(temp, dp->scd_path, UIO_SYSSPACE); } (void) VOP_CLOSE(vp, oflags, 1, 0, kcred, NULL); } (void) vn_remove(temp, UIO_SYSSPACE, RMFILE); #endif vmem_free(buf, buflen); kmem_free(temp, MAXPATHLEN); }
int main(int argc, char **argv) { struct stat cachestat; int mapfd = 0; int rv = 0; char *ondiskbuf; size_t newsz = 0; parse_args(argc, argv); errno = 0; devinfo_root = di_init("/", DINFOCPYALL|DINFOFORCE); logmsg(MSG_INFO, "errno = %d after " "di_init(/,DINFOCPYALL|DINFOFORCE)\n", errno); if (devinfo_root == NULL) { logmsg(MSG_ERROR, gettext("Unable to take device tree snapshot " "(%s: %d)\n"), strerror(errno), errno); return (-1); } logmsg(MSG_INFO, "opened root di_node\n"); if (globarg == MPX_CAPABLE_CTRL) { /* we just want to find MPxIO-capable controllers and exit */ if (drvlimit != NULL) { print_mpx_capable(di_drv_first_node(drvlimit, devinfo_root)); } else { print_mpx_capable(di_drv_first_node("fp", devinfo_root)); print_mpx_capable(di_drv_first_node("mpt", devinfo_root)); print_mpx_capable(di_drv_first_node("mpt_sas", devinfo_root)); print_mpx_capable(di_drv_first_node("pmcs", devinfo_root)); } di_fini(devinfo_root); return (0); } mapfd = open(ondiskname, O_RDWR|O_CREAT|O_SYNC, S_IRUSR | S_IWUSR); if (mapfd < 0) { /* we could be in single-user, so try for RO */ if ((mapfd = open(ondiskname, O_RDONLY)) < 0) { logmsg(MSG_ERROR, gettext("Unable to open or create %s:%s\n"), ondiskname, strerror(errno)); return (errno); } readonlyroot = 1; } if (stat(ondiskname, &cachestat) != 0) { logmsg(MSG_ERROR, gettext("Unable to stat() %s: %s\n"), ondiskname, strerror(errno)); return (errno); } ondiskbuf = calloc(1, cachestat.st_size); if (ondiskbuf == NULL) { logmsg(MSG_ERROR, gettext("Unable to allocate memory for the devid " "cache file: %s\n"), strerror(errno)); return (errno); } rv = read(mapfd, ondiskbuf, cachestat.st_size); if (rv != cachestat.st_size) { logmsg(MSG_ERROR, gettext("Unable to read all of devid cache file (got %d " "from expected %d bytes): %s\n"), rv, cachestat.st_size, strerror(errno)); return (errno); } errno = 0; rv = nvlist_unpack(ondiskbuf, cachestat.st_size, &mapnvl, 0); if (rv) { logmsg(MSG_INFO, "Unable to unpack devid cache file %s: %s (%d)\n", ondiskname, strerror(rv), rv); if (nvlist_alloc(&mapnvl, NV_UNIQUE_NAME, 0) != 0) { logmsg(MSG_ERROR, gettext("Unable to allocate root property" "list\n")); return (errno); } } free(ondiskbuf); if (validate_devnvl() < 0) { logmsg(MSG_ERROR, gettext("unable to validate kernel with on-disk devid " "cache file\n")); return (errno); } /* * If we're in single-user mode or maintenance mode, we won't * necessarily have a writable root device (ZFSroot; ufs root is * different in that we _do_ have a writable root device. * This causes problems for the devlink calls (see * $SRC/lib/libdevinfo/devinfo_devlink.c) and we do not try to * write out the devnvl if root is readonly. */ if (!readonlyroot) { rv = nvlist_size(mapnvl, &newsz, NV_ENCODE_NATIVE); if (rv) { logmsg(MSG_ERROR, gettext("Unable to determine size of packed " "on-disk devid cache file %s: %s (%d).\n"), ondiskname, strerror(rv), rv); logmsg(MSG_ERROR, gettext("Terminating\n")); nvlist_free(mapnvl); (void) close(mapfd); return (rv); } if ((ondiskbuf = calloc(1, newsz)) == NULL) { logmsg(MSG_ERROR, "Unable to allocate space for writing out new " "on-disk devid cache file: %s\n", strerror(errno)); (void) close(mapfd); nvlist_free(mapnvl); return (errno); } rv = nvlist_pack(mapnvl, &ondiskbuf, &newsz, NV_ENCODE_NATIVE, 0); if (rv) { logmsg(MSG_ERROR, gettext("Unable to pack on-disk devid cache " "file: %s (%d)\n"), strerror(rv), rv); (void) close(mapfd); free(ondiskbuf); nvlist_free(mapnvl); return (rv); } rv = lseek(mapfd, 0, 0); if (rv == -1) { logmsg(MSG_ERROR, gettext("Unable to seek to start of devid cache " "file: %s (%d)\n"), strerror(errno), errno); (void) close(mapfd); free(ondiskbuf); nvlist_free(mapnvl); return (-1); } if (write(mapfd, ondiskbuf, newsz) != newsz) { logmsg(MSG_ERROR, gettext("Unable to completely write out " "on-disk devid cache file: %s\n"), strerror(errno)); (void) close(mapfd); nvlist_free(mapnvl); free(ondiskbuf); return (errno); } } /* !readonlyroot */ /* Now we can process the command line args */ if (globarg == MPX_PHYSICAL) { report_map(devicep, BOOT); } else if (globarg == MPX_BOOTPATH) { rv = print_bootpath(); di_fini(devinfo_root); return (rv); } else if (globarg == MPX_UPDATEVFSTAB) { rv = update_vfstab(); di_fini(devinfo_root); return (rv); } else if (globarg == MPX_GETPATH) { report_dev_node_name(devicep); } else if (globarg == MPX_DEV_PATH) { report_map(devicep, BOOT_PATH); } else if (globarg != MPX_INIT) { if (globarg & MPX_LIST) list_devs(guid, limctrl); if (globarg == MPX_MAP) report_map(devicep, NONBOOT); } else { logmsg(MSG_INFO, "\nprivate devid cache file initialised\n"); } nvlist_free(mapnvl); di_fini(devinfo_root); return (0); }
int fwrite_nvlist(char *filename, nvlist_t *nvl) { char *buf; char *nvbuf; kfile_t *fp; char *newname; int len, err, err1; size_t buflen; ssize_t n; ASSERT(modrootloaded); nvbuf = NULL; err = nvlist_pack(nvl, &nvbuf, &buflen, NV_ENCODE_NATIVE, 0); if (err != 0) { KFIOERR((CE_CONT, "%s: error %d packing nvlist\n", filename, err)); return (err); } buf = kmem_alloc(sizeof (nvpf_hdr_t) + buflen, KM_SLEEP); bzero(buf, sizeof (nvpf_hdr_t)); ((nvpf_hdr_t *)buf)->nvpf_magic = NVPF_HDR_MAGIC; ((nvpf_hdr_t *)buf)->nvpf_version = NVPF_HDR_VERSION; ((nvpf_hdr_t *)buf)->nvpf_size = buflen; ((nvpf_hdr_t *)buf)->nvpf_chksum = nvp_cksum((uchar_t *)nvbuf, buflen); ((nvpf_hdr_t *)buf)->nvpf_hdr_chksum = nvp_cksum((uchar_t *)buf, sizeof (nvpf_hdr_t)); bcopy(nvbuf, buf + sizeof (nvpf_hdr_t), buflen); kmem_free(nvbuf, buflen); buflen += sizeof (nvpf_hdr_t); len = strlen(filename) + MAX_SUFFIX_LEN + 2; newname = kmem_alloc(len, KM_SLEEP); (void) sprintf(newname, "%s.%s", filename, NEW_FILENAME_SUFFIX); /* * To make it unlikely we suffer data loss, write * data to the new temporary file. Once successful * complete the transaction by renaming the new file * to replace the previous. */ if ((err = kfcreate(newname, &fp)) == 0) { err = kfwrite(fp, buf, buflen, &n); if (err) { KFIOERR((CE_CONT, "%s: write error - %d\n", newname, err)); } else { if (n != buflen) { KFIOERR((CE_CONT, "%s: partial write %ld of %ld bytes\n", newname, n, buflen)); KFIOERR((CE_CONT, "%s: filesystem may be full?\n", newname)); err = EIO; } } if ((err1 = kfclose(fp)) != 0) { KFIOERR((CE_CONT, "%s: close error\n", newname)); if (err == 0) err = err1; } if (err != 0) { if (kfremove(newname) != 0) { KFIOERR((CE_CONT, "%s: remove failed\n", newname)); } } } else { KFIOERR((CE_CONT, "%s: create failed - %d\n", filename, err)); } if (err == 0) { if ((err = kfrename(newname, filename)) != 0) { KFIOERR((CE_CONT, "%s: rename from %s failed\n", newname, filename)); } } kmem_free(newname, len); kmem_free(buf, buflen); return (err); }
/* * Handles the door command IPMGMT_CMD_INITIF. It retrieves all the persisted * interface configuration (interface properties and addresses), for all those * interfaces that need to be initialized. */ static void ipmgmt_initif_handler(void *argp) { ipmgmt_initif_arg_t *initif = argp; size_t buflen, nvlsize; char *buf = NULL, *onvlbuf, *invlbuf; ipmgmt_get_rval_t rval, *rvalp = &rval; ipmgmt_initif_cbarg_t cbarg; int err; assert(initif->ia_cmd == IPMGMT_CMD_INITIF); bzero(&cbarg, sizeof (cbarg)); invlbuf = (char *)argp + sizeof (ipmgmt_initif_arg_t); nvlsize = initif->ia_nvlsize; err = nvlist_unpack(invlbuf, nvlsize, &cbarg.cb_invl, NV_ENCODE_NATIVE); if (err != 0) goto fail; cbarg.cb_family = initif->ia_family; if (nvlist_alloc(&cbarg.cb_onvl, NV_UNIQUE_NAME, 0) != 0) goto fail; err = ipmgmt_db_walk(ipmgmt_db_initif, &cbarg, IPADM_DB_READ); if (err == ENOENT && cbarg.cb_ocnt > 0) { /* * If there is atleast one entry in the nvlist, * do not return error. */ err = 0; } if (err != 0) goto fail; if ((err = nvlist_size(cbarg.cb_onvl, &nvlsize, NV_ENCODE_NATIVE)) != 0) goto fail; buflen = nvlsize + sizeof (ipmgmt_get_rval_t); /* * We cannot use malloc() here because door_return never returns, and * memory allocated by malloc() would get leaked. Use alloca() instead. */ buf = alloca(buflen); onvlbuf = buf + sizeof (ipmgmt_get_rval_t); if ((err = nvlist_pack(cbarg.cb_onvl, &onvlbuf, &nvlsize, NV_ENCODE_NATIVE, 0)) != 0) { goto fail; } nvlist_free(cbarg.cb_invl); nvlist_free(cbarg.cb_onvl); rvalp = (ipmgmt_get_rval_t *)(void *)buf; rvalp->ir_err = 0; rvalp->ir_nvlsize = nvlsize; (void) door_return(buf, buflen, NULL, 0); return; fail: nvlist_free(cbarg.cb_invl); nvlist_free(cbarg.cb_onvl); rvalp->ir_err = err; (void) door_return((char *)rvalp, sizeof (*rvalp), NULL, 0); }
/* * Attach new_disk (fully described by nvroot) to old_disk. * If 'replacing' is specified, tne new disk will replace the old one. */ int zpool_vdev_attach(zpool_handle_t *zhp, const char *old_disk, const char *new_disk, nvlist_t *nvroot, int replacing) { zfs_cmd_t zc = { 0 }; char msg[1024]; char *packed; int ret; size_t len; nvlist_t *tgt; boolean_t avail_spare; uint64_t val; char *path; nvlist_t **child; uint_t children; nvlist_t *config_root; libzfs_handle_t *hdl = zhp->zpool_hdl; if (replacing) (void) snprintf(msg, sizeof (msg), dgettext(TEXT_DOMAIN, "cannot replace %s with %s"), old_disk, new_disk); else (void) snprintf(msg, sizeof (msg), dgettext(TEXT_DOMAIN, "cannot attach %s to %s"), new_disk, old_disk); (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name)); if ((tgt = zpool_find_vdev(zhp, old_disk, &avail_spare)) == 0) return (zfs_error(hdl, EZFS_NODEVICE, msg)); if (avail_spare) return (zfs_error(hdl, EZFS_ISSPARE, msg)); verify(nvlist_lookup_uint64(tgt, ZPOOL_CONFIG_GUID, &zc.zc_guid) == 0); zc.zc_cookie = replacing; if (nvlist_lookup_nvlist_array(nvroot, ZPOOL_CONFIG_CHILDREN, &child, &children) != 0 || children != 1) { zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "new device must be a single disk")); return (zfs_error(hdl, EZFS_INVALCONFIG, msg)); } verify(nvlist_lookup_nvlist(zpool_get_config(zhp, NULL), ZPOOL_CONFIG_VDEV_TREE, &config_root) == 0); /* * If the target is a hot spare that has been swapped in, we can only * replace it with another hot spare. */ if (replacing && nvlist_lookup_uint64(tgt, ZPOOL_CONFIG_IS_SPARE, &val) == 0 && nvlist_lookup_string(child[0], ZPOOL_CONFIG_PATH, &path) == 0 && (zpool_find_vdev(zhp, path, &avail_spare) == NULL || !avail_spare) && is_replacing_spare(config_root, tgt, 1)) { zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "can only be replaced by another hot spare")); return (zfs_error(hdl, EZFS_BADTARGET, msg)); } /* * If we are attempting to replace a spare, it canot be applied to an * already spared device. */ if (replacing && nvlist_lookup_string(child[0], ZPOOL_CONFIG_PATH, &path) == 0 && zpool_find_vdev(zhp, path, &avail_spare) != NULL && avail_spare && is_replacing_spare(config_root, tgt, 0)) { zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "device has already been replaced with a spare")); return (zfs_error(hdl, EZFS_BADTARGET, msg)); } verify(nvlist_size(nvroot, &len, NV_ENCODE_NATIVE) == 0); if ((packed = zfs_alloc(zhp->zpool_hdl, len)) == NULL) return (-1); verify(nvlist_pack(nvroot, &packed, &len, NV_ENCODE_NATIVE, 0) == 0); zc.zc_config_src = (uint64_t)(uintptr_t)packed; zc.zc_config_src_size = len; ret = ioctl(zhp->zpool_hdl->libzfs_fd, ZFS_IOC_VDEV_ATTACH, &zc); free(packed); if (ret == 0) return (0); switch (errno) { case ENOTSUP: /* * Can't attach to or replace this type of vdev. */ if (replacing) zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "cannot replace a replacing device")); else zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "can only attach to mirrors and top-level " "disks")); (void) zfs_error(hdl, EZFS_BADTARGET, msg); break; case EINVAL: /* * The new device must be a single disk. */ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "new device must be a single disk")); (void) zfs_error(hdl, EZFS_INVALCONFIG, msg); break; case EBUSY: zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "%s is busy"), new_disk); (void) zfs_error(hdl, EZFS_BADDEV, msg); break; case EOVERFLOW: /* * The new device is too small. */ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "device is too small")); (void) zfs_error(hdl, EZFS_BADDEV, msg); break; case EDOM: /* * The new device has a different alignment requirement. */ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "devices have different sector alignment")); (void) zfs_error(hdl, EZFS_BADDEV, msg); break; case ENAMETOOLONG: /* * The resulting top-level vdev spec won't fit in the label. */ (void) zfs_error(hdl, EZFS_DEVOVERFLOW, msg); break; default: (void) zpool_standard_error(hdl, errno, msg); } return (-1); }
/* * Synchronize all pools to disk. This must be called with the namespace lock * held. */ void spa_config_sync(void) { spa_t *spa = NULL; nvlist_t *config; size_t buflen; char *buf; vnode_t *vp; int oflags = FWRITE | FTRUNC | FCREAT | FOFFMAX; char pathname[128]; char pathname2[128]; ASSERT(MUTEX_HELD(&spa_namespace_lock)); VERIFY(nvlist_alloc(&config, NV_UNIQUE_NAME, KM_SLEEP) == 0); /* * Add all known pools to the configuration list, ignoring those with * alternate root paths. */ spa = NULL; while ((spa = spa_next(spa)) != NULL) { mutex_enter(&spa->spa_config_cache_lock); if (spa->spa_config && spa->spa_name && spa->spa_root == NULL) VERIFY(nvlist_add_nvlist(config, spa->spa_name, spa->spa_config) == 0); mutex_exit(&spa->spa_config_cache_lock); } /* * Pack the configuration into a buffer. */ VERIFY(nvlist_size(config, &buflen, NV_ENCODE_XDR) == 0); buf = kmem_alloc(buflen, KM_SLEEP); VERIFY(nvlist_pack(config, &buf, &buflen, NV_ENCODE_XDR, KM_SLEEP) == 0); /* * Write the configuration to disk. We need to do the traditional * 'write to temporary file, sync, move over original' to make sure we * always have a consistent view of the data. */ (void) snprintf(pathname, sizeof (pathname), "%s/%s", spa_config_dir, ZPOOL_CACHE_TMP); if (vn_open(pathname, UIO_SYSSPACE, oflags, 0644, &vp, CRCREAT, 0) != 0) goto out; if (vn_rdwr(UIO_WRITE, vp, buf, buflen, 0, UIO_SYSSPACE, 0, RLIM64_INFINITY, kcred, NULL) == 0 && VOP_FSYNC(vp, FSYNC, kcred) == 0) { (void) snprintf(pathname2, sizeof (pathname2), "%s/%s", spa_config_dir, ZPOOL_CACHE_FILE); (void) vn_rename(pathname, pathname2, UIO_SYSSPACE); } (void) VOP_CLOSE(vp, oflags, 1, 0, kcred); VN_RELE(vp); out: (void) vn_remove(pathname, UIO_SYSSPACE, RMFILE); spa_config_generation++; kmem_free(buf, buflen); nvlist_free(config); }
static void spa_config_write(spa_config_dirent_t *dp, nvlist_t *nvl) { size_t buflen; char *buf; vnode_t *vp; int oflags = FWRITE | FTRUNC | FCREAT | FOFFMAX; char tempname[128]; /* * If the nvlist is empty (NULL), then remove the old cachefile. */ if (nvl == NULL) { (void) vn_remove(dp->scd_path, UIO_SYSSPACE, RMFILE); return; } /* * Pack the configuration into a buffer. */ VERIFY(nvlist_size(nvl, &buflen, NV_ENCODE_XDR) == 0); buf = kmem_alloc(buflen, KM_SLEEP); VERIFY(nvlist_pack(nvl, &buf, &buflen, NV_ENCODE_XDR, KM_SLEEP) == 0); #ifdef __APPLE_KERNEL__ /* * OS X - since vn_rename() and vn_remove() are both missing from * the KPI, we have to just write over the existing file! Since * the OS X cache file only contains pools that are built from * file VDEVs, this cache file should be small. */ (void) snprintf(tempname, sizeof (tempname), "%s", dp->scd_path); #else /* * Write the configuration to disk. We need to do the traditional * 'write to temporary file, sync, move over original' to make sure we * always have a consistent view of the data. */ (void) snprintf(tempname, sizeof (tempname), "%s.tmp", dp->scd_path); #endif if (vn_open(tempname, UIO_SYSSPACE, oflags, 0644, &vp, CRCREAT, 0) != 0) goto out; if (vn_rdwr(UIO_WRITE, vp, buf, buflen, 0, UIO_SYSSPACE, 0, RLIM64_INFINITY, kcred, NULL) == 0 && VOP_FSYNC(vp, FSYNC, kcred, NULL) == 0) { (void) vn_rename(tempname, dp->scd_path, UIO_SYSSPACE); } (void) VOP_CLOSE(vp, oflags, 1, 0, kcred, NULL); #ifndef __APPLE__ VN_RELE(vp); #endif out: (void) vn_remove(tempname, UIO_SYSSPACE, RMFILE); kmem_free(buf, buflen); }
/*ARGSUSED*/ static int fm_ioctl(dev_t dev, int cmd, intptr_t data, int flag, cred_t *cred, int *rvalp) { char *buf; int err; uint_t model; const fm_subr_t *subr; uint32_t vers; fm_ioc_data_t fid; nvlist_t *invl = NULL, *onvl = NULL; #ifdef _MULTI_DATAMODEL fm_ioc_data32_t fid32; #endif if (getminor(dev) != 0) return (ENXIO); for (subr = fm_subrs; subr->cmd != cmd; subr++) if (subr->cmd == -1) return (ENOTTY); if (subr->priv && (flag & FWRITE) == 0 && secpolicy_sys_config(CRED(), 0) != 0) return (EPERM); model = ddi_model_convert_from(flag & FMODELS); switch (model) { #ifdef _MULTI_DATAMODEL case DDI_MODEL_ILP32: if (ddi_copyin((void *)data, &fid32, sizeof (fm_ioc_data32_t), flag) != 0) return (EFAULT); fid.fid_version = fid32.fid_version; fid.fid_insz = fid32.fid_insz; fid.fid_inbuf = (caddr_t)(uintptr_t)fid32.fid_inbuf; fid.fid_outsz = fid32.fid_outsz; fid.fid_outbuf = (caddr_t)(uintptr_t)fid32.fid_outbuf; break; #endif /* _MULTI_DATAMODEL */ case DDI_MODEL_NONE: default: if (ddi_copyin((void *)data, &fid, sizeof (fm_ioc_data_t), flag) != 0) return (EFAULT); } if (nvlist_lookup_uint32(fm_vers_nvl, subr->version, &vers) != 0 || fid.fid_version != vers) return (ENOTSUP); if (fid.fid_insz > FM_IOC_MAXBUFSZ) return (ENAMETOOLONG); if (fid.fid_outsz > FM_IOC_OUT_MAXBUFSZ) return (EINVAL); /* * Copy in and unpack the input nvlist. */ if (fid.fid_insz != 0 && fid.fid_inbuf != (caddr_t)0) { buf = kmem_alloc(fid.fid_insz, KM_SLEEP); if (ddi_copyin(fid.fid_inbuf, buf, fid.fid_insz, flag) != 0) { kmem_free(buf, fid.fid_insz); return (EFAULT); } err = nvlist_unpack(buf, fid.fid_insz, &invl, KM_SLEEP); kmem_free(buf, fid.fid_insz); if (err != 0) return (err); } err = subr->func(cmd, invl, &onvl); if (invl != NULL) nvlist_free(invl); if (err != 0) { if (onvl != NULL) nvlist_free(onvl); return (err); } /* * If the output nvlist contains any data, pack it and copyout. */ if (onvl != NULL) { size_t sz; if ((err = nvlist_size(onvl, &sz, NV_ENCODE_NATIVE)) != 0) { nvlist_free(onvl); return (err); } if (sz > fid.fid_outsz) { nvlist_free(onvl); return (ENAMETOOLONG); } buf = kmem_alloc(sz, KM_SLEEP); if ((err = nvlist_pack(onvl, &buf, &sz, NV_ENCODE_NATIVE, KM_SLEEP)) != 0) { kmem_free(buf, sz); nvlist_free(onvl); return (err); } nvlist_free(onvl); if (ddi_copyout(buf, fid.fid_outbuf, sz, flag) != 0) { kmem_free(buf, sz); return (EFAULT); } kmem_free(buf, sz); fid.fid_outsz = sz; switch (model) { #ifdef _MULTI_DATAMODEL case DDI_MODEL_ILP32: fid32.fid_outsz = (size32_t)fid.fid_outsz; if (ddi_copyout(&fid32, (void *)data, sizeof (fm_ioc_data32_t), flag) != 0) return (EFAULT); break; #endif /* _MULTI_DATAMODEL */ case DDI_MODEL_NONE: default: if (ddi_copyout(&fid, (void *)data, sizeof (fm_ioc_data_t), flag) != 0) return (EFAULT); } } return (err); }
/* * Take a snapshot of the current state of processor sets and CPUs, * pack it in the exacct format, and attach it to specified exacct record. */ int pool_pset_pack(ea_object_t *eo_system) { ea_object_t *eo_pset, *eo_cpu; cpupart_t *cpupart; psetid_t mypsetid; pool_pset_t *pset; nvlist_t *nvl; size_t bufsz; cpu_t *cpu; char *buf; int ncpu; ASSERT(pool_lock_held()); mutex_enter(&cpu_lock); mypsetid = zone_pset_get(curproc->p_zone); for (pset = list_head(&pool_pset_list); pset; pset = list_next(&pool_pset_list, pset)) { psetid_t psetid = pset->pset_id; if (!INGLOBALZONE(curproc) && mypsetid != psetid) continue; cpupart = cpupart_find(psetid); ASSERT(cpupart != NULL); eo_pset = ea_alloc_group(EXT_GROUP | EXC_LOCAL | EXD_GROUP_PSET); (void) ea_attach_item(eo_pset, &psetid, sizeof (id_t), EXC_LOCAL | EXD_PSET_PSETID | EXT_UINT32); /* * Pack info for all CPUs in this processor set. */ ncpu = 0; cpu = cpu_list; do { if (cpu->cpu_part != cpupart) /* not our pset */ continue; ncpu++; eo_cpu = ea_alloc_group(EXT_GROUP | EXC_LOCAL | EXD_GROUP_CPU); (void) ea_attach_item(eo_cpu, &cpu->cpu_id, sizeof (processorid_t), EXC_LOCAL | EXD_CPU_CPUID | EXT_UINT32); if (cpu->cpu_props == NULL) { (void) nvlist_alloc(&cpu->cpu_props, NV_UNIQUE_NAME, KM_SLEEP); (void) nvlist_add_string(cpu->cpu_props, "cpu.comment", ""); } (void) nvlist_dup(cpu->cpu_props, &nvl, KM_SLEEP); (void) nvlist_add_int64(nvl, "cpu.sys_id", cpu->cpu_id); (void) nvlist_add_string(nvl, "cpu.status", (char *)cpu_get_state_str(cpu)); buf = NULL; bufsz = 0; (void) nvlist_pack(nvl, &buf, &bufsz, NV_ENCODE_NATIVE, 0); (void) ea_attach_item(eo_cpu, buf, bufsz, EXC_LOCAL | EXD_CPU_PROP | EXT_RAW); (void) nvlist_free(nvl); kmem_free(buf, bufsz); (void) ea_attach_to_group(eo_pset, eo_cpu); } while ((cpu = cpu->cpu_next) != cpu_list); (void) nvlist_dup(pset->pset_props, &nvl, KM_SLEEP); (void) nvlist_add_uint64(nvl, "pset.size", ncpu); (void) nvlist_add_uint64(nvl, "pset.load", (uint64_t)PSET_LOAD(cpupart->cp_hp_avenrun[0])); buf = NULL; bufsz = 0; (void) nvlist_pack(nvl, &buf, &bufsz, NV_ENCODE_NATIVE, 0); (void) ea_attach_item(eo_pset, buf, bufsz, EXC_LOCAL | EXD_PSET_PROP | EXT_RAW); (void) nvlist_free(nvl); kmem_free(buf, bufsz); (void) ea_attach_to_group(eo_system, eo_pset); } mutex_exit(&cpu_lock); return (0); }
/*ARGSUSED*/ static void door_service(void *cookie, char *args, size_t alen, door_desc_t *ddp, uint_t ndid) { nvlist_t *nvl; size_t nvl_size = 0; char rbuf[BUF_THRESHOLD]; door_cookie_t *cook = (door_cookie_t *)cookie; uint64_t seq_num = 0; /* * Special case for asking to free buffer */ if (alen == sizeof (uint64_t)) { free_door_result(cookie, *(uint64_t *)(void *)args); (void) door_return(NULL, 0, NULL, 0); } /* * door_func update args to point to return results. * memory for results are dynamically allocated. */ (*cook->door_func)((void **)&args, &alen); /* * If no results, just return */ if (args == NULL) { dprint("null results returned from door_func().\n"); (void) door_return(NULL, 0, NULL, 0); } /* Determine the size of the packed nvlist */ nvl = (nvlist_t *)(void *)args; args = NULL; alen = 0; if (errno = nvlist_size(nvl, &nvl_size, NV_ENCODE_NATIVE)) { nvlist_free(nvl); dprint("failure to sizeup door results: %s\n", strerror(errno)); (void) door_return(NULL, 0, NULL, 0); } /* * If the size of the packed nvlist would exceed the buffer threshold * then get a sequence number and add it to the nvlist. */ if (nvl_size > BUF_THRESHOLD) { (void) mutex_lock(&cook->door_lock); cook->seq_num++; seq_num = cook->seq_num; (void) mutex_unlock(&cook->door_lock); (void) nvlist_add_uint64(nvl, RCM_SEQ_NUM, seq_num); } /* Refill the args with a packed version of the nvlist */ if (errno = nvlist_pack(nvl, &args, &alen, NV_ENCODE_NATIVE, 0)) { nvlist_free(nvl); dprint("failure to pack door results: %s\n", strerror(errno)); (void) door_return(NULL, 0, NULL, 0); } nvlist_free(nvl); /* * Based on the size of the packed nvlist, either use the local buffer * or add it to the results list. */ if (alen <= BUF_THRESHOLD) { bcopy(args, rbuf, alen); (void) free(args); args = rbuf; } else { /* * for long data, append results to end of queue in cook * and set ndid, ask client to do another door_call * to free the buffer. */ add_door_result(cook, args, seq_num); } (void) door_return(args, alen, NULL, 0); }
/* * Add the given vdevs to the pool. The caller must have already performed the * necessary verification to ensure that the vdev specification is well-formed. */ int zpool_add(zpool_handle_t *zhp, nvlist_t *nvroot) { char *packed; size_t len; zfs_cmd_t zc; int ret; libzfs_handle_t *hdl = zhp->zpool_hdl; char msg[1024]; nvlist_t **spares; uint_t nspares; (void) snprintf(msg, sizeof (msg), dgettext(TEXT_DOMAIN, "cannot add to '%s'"), zhp->zpool_name); if (zpool_get_version(zhp) < ZFS_VERSION_SPARES && nvlist_lookup_nvlist_array(nvroot, ZPOOL_CONFIG_SPARES, &spares, &nspares) == 0) { zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "pool must be " "upgraded to add hot spares")); return (zfs_error(hdl, EZFS_BADVERSION, msg)); } verify(nvlist_size(nvroot, &len, NV_ENCODE_NATIVE) == 0); if ((packed = zfs_alloc(zhp->zpool_hdl, len)) == NULL) return (-1); verify(nvlist_pack(nvroot, &packed, &len, NV_ENCODE_NATIVE, 0) == 0); (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name)); zc.zc_config_src = (uint64_t)(uintptr_t)packed; zc.zc_config_src_size = len; if (ioctl(zhp->zpool_hdl->libzfs_fd, ZFS_IOC_VDEV_ADD, &zc) != 0) { switch (errno) { case EBUSY: /* * This can happen if the user has specified the same * device multiple times. We can't reliably detect this * until we try to add it and see we already have a * label. */ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "one or more vdevs refer to the same device")); (void) zfs_error(hdl, EZFS_BADDEV, msg); break; case EOVERFLOW: /* * This occurrs when one of the devices is below * SPA_MINDEVSIZE. Unfortunately, we can't detect which * device was the problem device since there's no * reliable way to determine device size from userland. */ { char buf[64]; zfs_nicenum(SPA_MINDEVSIZE, buf, sizeof (buf)); zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "device is less than the minimum " "size (%s)"), buf); } (void) zfs_error(hdl, EZFS_BADDEV, msg); break; case ENOTSUP: zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "pool must be upgraded to add raidz2 vdevs")); (void) zfs_error(hdl, EZFS_BADVERSION, msg); break; default: (void) zpool_standard_error(hdl, errno, msg); } ret = -1; } else { ret = 0; } free(packed); return (ret); }
/* * Function: it_config_commit() * * Informs the iscsit service that the configuration has changed and * commits the new configuration to persistent store by calling * stmfSetProviderData. This function can be called multiple times * during a configuration sequence if necessary. * * Parameters: * cfg A C representation of the current iSCSI configuration * * Return Values: * 0 Success * ENOMEM Could not allocate resources * EINVAL Invalid it_config_t structure * TBD ioctl() failed * TBD could not save config to STMF */ int it_config_commit(it_config_t *cfg) { int ret; nvlist_t *cfgnv = NULL; char *packednv = NULL; int iscsit_fd = -1; size_t pnv_size; iscsit_ioc_set_config_t iop; it_tgt_t *tgtp; if (!cfg) { return (EINVAL); } ret = it_config_to_nv(cfg, &cfgnv); if (ret == 0) { ret = nvlist_size(cfgnv, &pnv_size, NV_ENCODE_NATIVE); } /* * If the iscsit service is enabled, send the changes to the * kernel first. Kernel will be the final sanity check before * the config is saved persistently. * * This somewhat leaves open the simultaneous-change hole * that STMF was trying to solve, but is a better sanity * check and allows for graceful handling of target renames. */ if ((ret == 0) && is_iscsit_enabled()) { packednv = malloc(pnv_size); if (!packednv) { ret = ENOMEM; } else { ret = nvlist_pack(cfgnv, &packednv, &pnv_size, NV_ENCODE_NATIVE, 0); } if (ret == 0) { iscsit_fd = open(ISCSIT_NODE, O_RDWR|O_EXCL); if (iscsit_fd != -1) { iop.set_cfg_vers = ISCSIT_API_VERS0; iop.set_cfg_pnvlist = packednv; iop.set_cfg_pnvlist_len = pnv_size; if ((ioctl(iscsit_fd, ISCSIT_IOC_SET_CONFIG, &iop)) != 0) { ret = errno; } (void) close(iscsit_fd); } else { ret = errno; } } if (packednv != NULL) { free(packednv); } } /* * Before saving the config persistently, remove any * PROP_OLD_TARGET_NAME entries. This is only interesting to * the active service. */ if (ret == 0) { boolean_t changed = B_FALSE; tgtp = cfg->config_tgt_list; for (; tgtp != NULL; tgtp = tgtp->tgt_next) { if (!tgtp->tgt_properties) { continue; } if (nvlist_exists(tgtp->tgt_properties, PROP_OLD_TARGET_NAME)) { (void) nvlist_remove_all(tgtp->tgt_properties, PROP_OLD_TARGET_NAME); changed = B_TRUE; } } if (changed) { /* rebuild the config nvlist */ nvlist_free(cfgnv); cfgnv = NULL; ret = it_config_to_nv(cfg, &cfgnv); } } /* * stmfGetProviderDataProt() checks to ensure * that the config data hasn't changed since we fetched it. * * The kernel now has a version we need to save persistently. * CLI will 'do the right thing' and warn the user if it * gets STMF_ERROR_PROV_DATA_STALE. We'll try once to revert * the kernel to the persistently saved data, but ultimately, * it's up to the administrator to validate things are as they * want them to be. */ if (ret == 0) { ret = stmfSetProviderDataProt(ISCSIT_MODNAME, cfgnv, STMF_PORT_PROVIDER_TYPE, &(cfg->stmf_token)); if (ret == STMF_STATUS_SUCCESS) { ret = 0; } else if (ret == STMF_ERROR_NOMEM) { ret = ENOMEM; } else if (ret == STMF_ERROR_PROV_DATA_STALE) { int st; it_config_t *rcfg = NULL; st = it_config_load(&rcfg); if (st == 0) { (void) it_config_commit(rcfg); it_config_free(rcfg); } } } if (cfgnv) { nvlist_free(cfgnv); } return (ret); }
/* * Import the given pool using the known configuration. The configuration * should have come from zpool_find_import(). The 'newname' and 'altroot' * parameters control whether the pool is imported with a different name or with * an alternate root, respectively. */ int zpool_import(libzfs_handle_t *hdl, nvlist_t *config, const char *newname, const char *altroot) { zfs_cmd_t zc; char *packed; size_t len; char *thename; char *origname; int ret; verify(nvlist_lookup_string(config, ZPOOL_CONFIG_POOL_NAME, &origname) == 0); if (newname != NULL) { if (!zpool_name_valid(hdl, B_FALSE, newname)) return (zfs_error(hdl, EZFS_INVALIDNAME, dgettext(TEXT_DOMAIN, "cannot import '%s'"), newname)); thename = (char *)newname; } else { thename = origname; } if (altroot != NULL && altroot[0] != '/') return (zfs_error(hdl, EZFS_BADPATH, dgettext(TEXT_DOMAIN, "bad alternate root '%s'"), altroot)); (void) strlcpy(zc.zc_name, thename, sizeof (zc.zc_name)); if (altroot != NULL) (void) strlcpy(zc.zc_root, altroot, sizeof (zc.zc_root)); else zc.zc_root[0] = '\0'; verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_GUID, &zc.zc_guid) == 0); verify(nvlist_size(config, &len, NV_ENCODE_NATIVE) == 0); if ((packed = zfs_alloc(hdl, len)) == NULL) return (-1); verify(nvlist_pack(config, &packed, &len, NV_ENCODE_NATIVE, 0) == 0); zc.zc_config_src = (uint64_t)(uintptr_t)packed; zc.zc_config_src_size = len; ret = 0; if (ioctl(hdl->libzfs_fd, ZFS_IOC_POOL_IMPORT, &zc) != 0) { char desc[1024]; if (newname == NULL) (void) snprintf(desc, sizeof (desc), dgettext(TEXT_DOMAIN, "cannot import '%s'"), thename); else (void) snprintf(desc, sizeof (desc), dgettext(TEXT_DOMAIN, "cannot import '%s' as '%s'"), origname, thename); switch (errno) { case ENOTSUP: /* * Unsupported version. */ (void) zfs_error(hdl, EZFS_BADVERSION, desc); break; case EINVAL: (void) zfs_error(hdl, EZFS_INVALCONFIG, desc); break; default: (void) zpool_standard_error(hdl, errno, desc); } ret = -1; } else { zpool_handle_t *zhp; /* * This should never fail, but play it safe anyway. */ if (zpool_open_silent(hdl, thename, &zhp) != 0) { ret = -1; } else if (zhp != NULL) { ret = zpool_create_zvol_links(zhp); zpool_close(zhp); } } free(packed); return (ret); }
/*ARGSUSED*/ static void door_server(void *cookie, char *argp, size_t sz, door_desc_t *dp, uint_t ndesc) { nvlist_t *args = NULL; nvlist_t *results = NULL; hp_cmd_t cmd; int rv; dprintf("Door call: cookie=%p, argp=%p, sz=%d\n", cookie, (void *)argp, sz); /* Special case to free a results buffer */ if (sz == sizeof (uint64_t)) { free_buffer(*(uint64_t *)(uintptr_t)argp); (void) door_return(NULL, 0, NULL, 0); return; } /* Unpack the arguments nvlist */ if (nvlist_unpack(argp, sz, &args, 0) != 0) { log_err("Cannot unpack door arguments.\n"); rv = EINVAL; goto fail; } /* Extract the requested command */ if (nvlist_lookup_int32(args, HPD_CMD, (int32_t *)&cmd) != 0) { log_err("Cannot decode door command.\n"); rv = EINVAL; goto fail; } /* Implement the command */ switch (cmd) { case HP_CMD_GETINFO: rv = cmd_getinfo(args, &results); break; case HP_CMD_CHANGESTATE: rv = cmd_changestate(args, &results); break; case HP_CMD_SETPRIVATE: case HP_CMD_GETPRIVATE: rv = cmd_private(cmd, args, &results); break; default: rv = EINVAL; break; } /* The arguments nvlist is no longer needed */ nvlist_free(args); args = NULL; /* * If an nvlist was constructed for the results, * then pack the results nvlist and return it. */ if (results != NULL) { uint64_t seqnum; char *buf = NULL; size_t len = 0; /* Add a sequence number to the results */ seqnum = get_seqnum(); if (nvlist_add_uint64(results, HPD_SEQNUM, seqnum) != 0) { log_err("Cannot add sequence number.\n"); rv = EFAULT; goto fail; } /* Pack the results nvlist */ if (nvlist_pack(results, &buf, &len, NV_ENCODE_NATIVE, 0) != 0) { log_err("Cannot pack door results.\n"); rv = EFAULT; goto fail; } /* Link results buffer into list */ add_buffer(seqnum, buf); /* The results nvlist is no longer needed */ nvlist_free(results); /* Return the results */ (void) door_return(buf, len, NULL, 0); return; } /* Return result code (when no nvlist) */ (void) door_return((char *)&rv, sizeof (int), NULL, 0); return; fail: log_err("Door call failed (%s)\n", strerror(rv)); nvlist_free(args); nvlist_free(results); (void) door_return((char *)&rv, sizeof (int), NULL, 0); }
/*ARGSUSED*/ static void spa_history_log_sync(void *arg1, void *arg2, dmu_tx_t *tx) { spa_t *spa = arg1; history_arg_t *hap = arg2; const char *history_str = hap->ha_history_str; objset_t *mos = spa->spa_meta_objset; dmu_buf_t *dbp; spa_history_phys_t *shpp; size_t reclen; uint64_t le_len; nvlist_t *nvrecord; char *record_packed = NULL; int ret; /* * If we have an older pool that doesn't have a command * history object, create it now. */ mutex_enter(&spa->spa_history_lock); if (!spa->spa_history) spa_history_create_obj(spa, tx); mutex_exit(&spa->spa_history_lock); /* * Get the offset of where we need to write via the bonus buffer. * Update the offset when the write completes. */ VERIFY(0 == dmu_bonus_hold(mos, spa->spa_history, FTAG, &dbp)); shpp = dbp->db_data; dmu_buf_will_dirty(dbp, tx); #ifdef ZFS_DEBUG { dmu_object_info_t doi; dmu_object_info_from_db(dbp, &doi); ASSERT3U(doi.doi_bonus_type, ==, DMU_OT_SPA_HISTORY_OFFSETS); } #endif VERIFY(nvlist_alloc(&nvrecord, NV_UNIQUE_NAME, KM_SLEEP) == 0); VERIFY(nvlist_add_uint64(nvrecord, ZPOOL_HIST_TIME, gethrestime_sec()) == 0); VERIFY(nvlist_add_uint64(nvrecord, ZPOOL_HIST_WHO, hap->ha_uid) == 0); if (hap->ha_zone != NULL) VERIFY(nvlist_add_string(nvrecord, ZPOOL_HIST_ZONE, hap->ha_zone) == 0); #ifdef _KERNEL VERIFY(nvlist_add_string(nvrecord, ZPOOL_HIST_HOST, utsname.nodename) == 0); #endif if (hap->ha_log_type == LOG_CMD_POOL_CREATE || hap->ha_log_type == LOG_CMD_NORMAL) { VERIFY(nvlist_add_string(nvrecord, ZPOOL_HIST_CMD, history_str) == 0); zfs_dbgmsg("command: %s", history_str); } else { VERIFY(nvlist_add_uint64(nvrecord, ZPOOL_HIST_INT_EVENT, hap->ha_event) == 0); VERIFY(nvlist_add_uint64(nvrecord, ZPOOL_HIST_TXG, tx->tx_txg) == 0); VERIFY(nvlist_add_string(nvrecord, ZPOOL_HIST_INT_STR, history_str) == 0); zfs_dbgmsg("internal %s pool:%s txg:%llu %s", zfs_history_event_names[hap->ha_event], spa_name(spa), (longlong_t)tx->tx_txg, history_str); } VERIFY(nvlist_size(nvrecord, &reclen, NV_ENCODE_XDR) == 0); record_packed = kmem_alloc(reclen, KM_SLEEP); VERIFY(nvlist_pack(nvrecord, &record_packed, &reclen, NV_ENCODE_XDR, KM_SLEEP) == 0); mutex_enter(&spa->spa_history_lock); if (hap->ha_log_type == LOG_CMD_POOL_CREATE) VERIFY(shpp->sh_eof == shpp->sh_pool_create_len); /* write out the packed length as little endian */ le_len = LE_64((uint64_t)reclen); ret = spa_history_write(spa, &le_len, sizeof (le_len), shpp, tx); if (!ret) ret = spa_history_write(spa, record_packed, reclen, shpp, tx); if (!ret && hap->ha_log_type == LOG_CMD_POOL_CREATE) { shpp->sh_pool_create_len += sizeof (le_len) + reclen; shpp->sh_bof = shpp->sh_pool_create_len; } mutex_exit(&spa->spa_history_lock); nvlist_free(nvrecord); kmem_free(record_packed, reclen); dmu_buf_rele(dbp, FTAG); strfree(hap->ha_history_str); if (hap->ha_zone != NULL) strfree(hap->ha_zone); kmem_free(hap, sizeof (history_arg_t)); }
int main(int argc, char **argv) { int fd_pool; int fd_log; vdev_label_t vl_pool; vdev_label_t vl_log; nvlist_t *config_pool; nvlist_t *config_log; uint64_t guid; // ZPOOL_CONFIG_GUID uint64_t is_log; // ZPOOL_CONFIG_IS_LOG nvlist_t *vdev_tree; // ZPOOL_CONFIG_VDEV_TREE char *buf; size_t buflen; VERIFY(argc == 4); VERIFY((fd_pool = open(argv[1], O_RDWR)) != -1); VERIFY((fd_log = open(argv[2], O_RDWR)) != -1); VERIFY(sscanf(argv[3], "%" SCNu64 , &guid) == 1); //guid = 9851295902337437618ULL; VERIFY(pread64(fd_pool, &vl_pool, sizeof (vdev_label_t), 0) == sizeof (vdev_label_t)); VERIFY(nvlist_unpack(vl_pool.vl_vdev_phys.vp_nvlist, sizeof (vl_pool.vl_vdev_phys.vp_nvlist), &config_pool, 0) == 0); VERIFY(pread64(fd_log, &vl_log, sizeof (vdev_label_t), 0) == sizeof (vdev_label_t)); VERIFY(nvlist_unpack(vl_log.vl_vdev_phys.vp_nvlist, sizeof (vl_log.vl_vdev_phys.vp_nvlist), &config_log, 0) == 0); // save what we want from config_log -- is_log, vdev_tree VERIFY(nvlist_lookup_uint64(config_log, ZPOOL_CONFIG_IS_LOG, &is_log) == 0); VERIFY(nvlist_lookup_nvlist(config_log, ZPOOL_CONFIG_VDEV_TREE, &vdev_tree) == 0); // fix guid for vdev_log VERIFY(nvlist_remove_all(vdev_tree, ZPOOL_CONFIG_GUID) == 0); VERIFY(nvlist_add_uint64(vdev_tree, ZPOOL_CONFIG_GUID, guid) == 0); // remove what we are going to replace on config_pool VERIFY(nvlist_remove_all(config_pool, ZPOOL_CONFIG_TOP_GUID) == 0); VERIFY(nvlist_remove_all(config_pool, ZPOOL_CONFIG_GUID) == 0); VERIFY(nvlist_remove_all(config_pool, ZPOOL_CONFIG_VDEV_TREE) == 0); // add back what we want VERIFY(nvlist_add_uint64(config_pool, ZPOOL_CONFIG_TOP_GUID, guid) == 0); VERIFY(nvlist_add_uint64(config_pool, ZPOOL_CONFIG_GUID, guid) == 0); VERIFY(nvlist_add_uint64(config_pool, ZPOOL_CONFIG_IS_LOG, is_log) == 0); VERIFY(nvlist_add_nvlist(config_pool, ZPOOL_CONFIG_VDEV_TREE, vdev_tree) == 0); buf = vl_pool.vl_vdev_phys.vp_nvlist; buflen = sizeof (vl_pool.vl_vdev_phys.vp_nvlist); VERIFY(nvlist_pack(config_pool, &buf, &buflen, NV_ENCODE_XDR, 0) == 0); label_write(fd_log, offsetof(vdev_label_t, vl_vdev_phys), VDEV_PHYS_SIZE, &vl_pool.vl_vdev_phys); fsync(fd_log); return (0); }
static #endif int zfs_fuid_find_by_domain(zfsvfs_t *zfsvfs, const char *domain, char **retdomain, dmu_tx_t *tx) { fuid_domain_t searchnode, *findnode; avl_index_t loc; /* * If the dummy "nobody" domain then return an index of 0 * to cause the created FUID to be a standard POSIX id * for the user nobody. */ if (domain[0] == '\0') { *retdomain = ""; return (0); } searchnode.f_ksid = ksid_lookupdomain(domain); if (retdomain) { *retdomain = searchnode.f_ksid->kd_name; } if (!zfsvfs->z_fuid_loaded) zfs_fuid_init(zfsvfs, tx); rw_enter(&zfsvfs->z_fuid_lock, RW_READER); findnode = avl_find(&zfsvfs->z_fuid_domain, &searchnode, &loc); rw_exit(&zfsvfs->z_fuid_lock); if (findnode) { ksiddomain_rele(searchnode.f_ksid); return (findnode->f_idx); } else { fuid_domain_t *domnode; nvlist_t *nvp; nvlist_t **fuids; uint64_t retidx; size_t nvsize = 0; char *packed; dmu_buf_t *db; int i = 0; domnode = kmem_alloc(sizeof (fuid_domain_t), KM_SLEEP); domnode->f_ksid = searchnode.f_ksid; rw_enter(&zfsvfs->z_fuid_lock, RW_WRITER); retidx = domnode->f_idx = avl_numnodes(&zfsvfs->z_fuid_idx) + 1; avl_add(&zfsvfs->z_fuid_domain, domnode); avl_add(&zfsvfs->z_fuid_idx, domnode); /* * Now resync the on-disk nvlist. */ VERIFY(nvlist_alloc(&nvp, NV_UNIQUE_NAME, KM_SLEEP) == 0); domnode = avl_first(&zfsvfs->z_fuid_domain); fuids = kmem_alloc(retidx * sizeof (void *), KM_SLEEP); while (domnode) { VERIFY(nvlist_alloc(&fuids[i], NV_UNIQUE_NAME, KM_SLEEP) == 0); VERIFY(nvlist_add_uint64(fuids[i], FUID_IDX, domnode->f_idx) == 0); VERIFY(nvlist_add_uint64(fuids[i], FUID_OFFSET, 0) == 0); VERIFY(nvlist_add_string(fuids[i++], FUID_DOMAIN, domnode->f_ksid->kd_name) == 0); domnode = AVL_NEXT(&zfsvfs->z_fuid_domain, domnode); } VERIFY(nvlist_add_nvlist_array(nvp, FUID_NVP_ARRAY, fuids, retidx) == 0); for (i = 0; i != retidx; i++) nvlist_free(fuids[i]); kmem_free(fuids, retidx * sizeof (void *)); VERIFY(nvlist_size(nvp, &nvsize, NV_ENCODE_XDR) == 0); packed = kmem_alloc(nvsize, KM_SLEEP); VERIFY(nvlist_pack(nvp, &packed, &nvsize, NV_ENCODE_XDR, KM_SLEEP) == 0); nvlist_free(nvp); zfsvfs->z_fuid_size = nvsize; dmu_write(zfsvfs->z_os, zfsvfs->z_fuid_obj, 0, zfsvfs->z_fuid_size, packed, tx); kmem_free(packed, zfsvfs->z_fuid_size); VERIFY(0 == dmu_bonus_hold(zfsvfs->z_os, zfsvfs->z_fuid_obj, FTAG, &db)); dmu_buf_will_dirty(db, tx); *(uint64_t *)db->db_data = zfsvfs->z_fuid_size; dmu_buf_rele(db, FTAG); rw_exit(&zfsvfs->z_fuid_lock); return (retidx); } }
/* * Perform /dev/fm ioctl. The input and output data are represented by * name-value lists (nvlists). */ int fmd_agent_nvl_ioctl(fmd_agent_hdl_t *hdl, int cmd, uint32_t ver, nvlist_t *innvl, nvlist_t **outnvlp) { fm_ioc_data_t fid; int err = 0; char *inbuf = NULL, *outbuf = NULL; size_t insz = 0, outsz = 0; if (innvl != NULL) { if ((err = nvlist_size(innvl, &insz, NV_ENCODE_NATIVE)) != 0) return (err); if (insz > FM_IOC_MAXBUFSZ) return (ENAMETOOLONG); if ((inbuf = umem_alloc(insz, UMEM_DEFAULT)) == NULL) return (errno); if ((err = nvlist_pack(innvl, &inbuf, &insz, NV_ENCODE_NATIVE, 0)) != 0) { umem_free(inbuf, insz); return (err); } } if (outnvlp != NULL) { outsz = FM_IOC_OUT_BUFSZ; } for (;;) { if (outnvlp != NULL) { outbuf = umem_alloc(outsz, UMEM_DEFAULT); if (outbuf == NULL) { err = errno; break; } } fid.fid_version = ver; fid.fid_insz = insz; fid.fid_inbuf = inbuf; fid.fid_outsz = outsz; fid.fid_outbuf = outbuf; if (ioctl(hdl->agent_devfd, cmd, &fid) < 0) { if (errno == ENAMETOOLONG && outsz != 0 && outsz < (FM_IOC_OUT_MAXBUFSZ / 2)) { umem_free(outbuf, outsz); outsz *= 2; outbuf = umem_alloc(outsz, UMEM_DEFAULT); if (outbuf == NULL) { err = errno; break; } } else { err = errno; break; } } else if (outnvlp != NULL) { err = nvlist_unpack(fid.fid_outbuf, fid.fid_outsz, outnvlp, 0); break; } else { break; } } if (inbuf != NULL) umem_free(inbuf, insz); if (outbuf != NULL) umem_free(outbuf, outsz); return (err); }