aboutsummaryrefslogtreecommitdiff
path: root/usr.sbin
diff options
context:
space:
mode:
Diffstat (limited to 'usr.sbin')
-rw-r--r--usr.sbin/Makefile.mips3
-rw-r--r--usr.sbin/ac/ac.c2
-rw-r--r--usr.sbin/acpi/acpidump/acpi.c2
-rw-r--r--usr.sbin/arp/arp.c71
-rw-r--r--usr.sbin/bhyve/bhyve_config.596
-rw-r--r--usr.sbin/bhyve/bhyverun.c3
-rw-r--r--usr.sbin/bhyve/block_if.c105
-rw-r--r--usr.sbin/bhyve/block_if.h4
-rw-r--r--usr.sbin/bhyve/net_backends.c12
-rw-r--r--usr.sbin/bhyve/pci_ahci.c142
-rw-r--r--usr.sbin/bhyve/pci_passthru.c2
-rw-r--r--usr.sbin/bhyve/pci_virtio_block.c2
-rw-r--r--usr.sbin/bhyve/smbiostbl.c140
-rw-r--r--usr.sbin/bluetooth/bthidd/server.c2
-rw-r--r--usr.sbin/bsdinstall/bsdinstall.815
-rwxr-xr-xusr.sbin/bsdinstall/scripts/auto2
-rwxr-xr-xusr.sbin/bsdinstall/scripts/docsinstall3
-rwxr-xr-xusr.sbin/bsdinstall/scripts/mount6
-rwxr-xr-xusr.sbin/bsdinstall/scripts/rootpass12
-rwxr-xr-xusr.sbin/bsdinstall/scripts/umount7
-rwxr-xr-xusr.sbin/bsdinstall/scripts/zfsboot19
-rw-r--r--usr.sbin/bsnmpd/modules/snmp_hostres/hostres_swrun_tbl.c2
-rw-r--r--usr.sbin/certctl/certctl.821
-rwxr-xr-xusr.sbin/certctl/certctl.sh14
-rw-r--r--usr.sbin/crunch/crunchide/exec_elf32.c2
-rw-r--r--usr.sbin/devinfo/devinfo.83
-rwxr-xr-xusr.sbin/etcupdate/etcupdate.sh17
-rw-r--r--usr.sbin/fstyp/hammer2_disk.h4
-rw-r--r--usr.sbin/fstyp/ufs.c4
-rw-r--r--usr.sbin/jail/jail.821
-rw-r--r--usr.sbin/jail/jail.conf.59
-rw-r--r--usr.sbin/makefs/Makefile11
-rw-r--r--usr.sbin/makefs/makefs.897
-rw-r--r--usr.sbin/makefs/makefs.c3
-rw-r--r--usr.sbin/makefs/makefs.h5
-rw-r--r--usr.sbin/makefs/tests/Makefile1
-rw-r--r--usr.sbin/makefs/tests/makefs_zfs_tests.sh634
-rw-r--r--usr.sbin/makefs/zfs.c758
-rw-r--r--usr.sbin/makefs/zfs/Makefile.inc12
-rw-r--r--usr.sbin/makefs/zfs/dsl.c598
-rw-r--r--usr.sbin/makefs/zfs/fs.c981
-rw-r--r--usr.sbin/makefs/zfs/objset.c259
-rw-r--r--usr.sbin/makefs/zfs/vdev.c435
-rw-r--r--usr.sbin/makefs/zfs/zap.c551
-rw-r--r--usr.sbin/makefs/zfs/zfs.h167
-rw-r--r--usr.sbin/mfiutil/mfiutil.821
-rw-r--r--usr.sbin/mixer/mixer.819
-rw-r--r--usr.sbin/mixer/mixer.c23
-rw-r--r--usr.sbin/ndp/Makefile2
-rw-r--r--usr.sbin/ndp/ndp.c388
-rw-r--r--usr.sbin/pkg/Makefile2
-rw-r--r--usr.sbin/pkg/pkg.734
-rw-r--r--usr.sbin/pkg/pkg.c4
-rw-r--r--usr.sbin/pmcannotate/pmcannotate.c5
-rw-r--r--usr.sbin/pmcstat/pmcstat.c81
-rw-r--r--usr.sbin/ppp/ppp.88
-rw-r--r--usr.sbin/prometheus_sysctl_exporter/prometheus_sysctl_exporter.c15
-rw-r--r--usr.sbin/pstat/pstat.822
-rw-r--r--usr.sbin/pw/Makefile1
-rw-r--r--usr.sbin/pw/psdate.c2
-rw-r--r--usr.sbin/pw/pw.87
-rw-r--r--usr.sbin/quot/quot.c4
-rw-r--r--usr.sbin/rpcbind/check_bound.c11
-rw-r--r--usr.sbin/rpcbind/util.c16
-rw-r--r--usr.sbin/sesutil/sesutil.864
-rw-r--r--usr.sbin/tcpsso/tcpsso.827
-rw-r--r--usr.sbin/uhsoctl/uhsoctl.12
-rw-r--r--usr.sbin/unbound/config.h6
-rw-r--r--usr.sbin/wpa/Makefile.inc3
-rw-r--r--usr.sbin/wpa/src/wps/Makefile2
70 files changed, 5340 insertions, 688 deletions
diff --git a/usr.sbin/Makefile.mips b/usr.sbin/Makefile.mips
deleted file mode 100644
index 8987110dba31..000000000000
--- a/usr.sbin/Makefile.mips
+++ /dev/null
@@ -1,3 +0,0 @@
-# $FreeBSD$
-
-SUBDIR+= ofwdump
diff --git a/usr.sbin/ac/ac.c b/usr.sbin/ac/ac.c
index 8e7ed2497937..bfd9e37465c4 100644
--- a/usr.sbin/ac/ac.c
+++ b/usr.sbin/ac/ac.c
@@ -68,7 +68,7 @@ struct user_entry {
};
/*
- * this is for chosing whether to ignore a login
+ * this is for choosing whether to ignore a login
*/
struct tty_entry {
SLIST_ENTRY(tty_entry) next;
diff --git a/usr.sbin/acpi/acpidump/acpi.c b/usr.sbin/acpi/acpidump/acpi.c
index 31baa3601338..c560a38411ba 100644
--- a/usr.sbin/acpi/acpidump/acpi.c
+++ b/usr.sbin/acpi/acpidump/acpi.c
@@ -555,7 +555,7 @@ acpi_print_madt(ACPI_SUBTABLE_HEADER *mp)
printf("\tGICR ADDR=%016jx\n",
(uintmax_t)gicc->GicrBaseAddress);
printf("\tMPIDR=%jx\n", (uintmax_t)gicc->ArmMpidr);
- printf("\tEfficency Class=%d\n", (u_int)gicc->EfficiencyClass);
+ printf("\tEfficiency Class=%d\n", (u_int)gicc->EfficiencyClass);
printf("\tSPE INTR=%d\n", gicc->SpeInterrupt);
break;
case ACPI_MADT_TYPE_GENERIC_DISTRIBUTOR:
diff --git a/usr.sbin/arp/arp.c b/usr.sbin/arp/arp.c
index f018baa2679e..e7c18b0f323f 100644
--- a/usr.sbin/arp/arp.c
+++ b/usr.sbin/arp/arp.c
@@ -79,6 +79,7 @@ __FBSDID("$FreeBSD$");
#include <string.h>
#include <strings.h>
#include <unistd.h>
+#include <ifaddrs.h>
#include <libxo/xo.h>
typedef void (action_fn)(struct sockaddr_dl *sdl, struct sockaddr_in *s_in,
@@ -794,90 +795,70 @@ doit:
* get_ether_addr - get the hardware address of an interface on the
* the same subnet as ipaddr.
*/
-#define MAX_IFS 32
-
static int
get_ether_addr(in_addr_t ipaddr, struct ether_addr *hwaddr)
{
- struct ifreq *ifr, *ifend, *ifp;
+ struct ifaddrs *ifa, *ifd, *ifas = NULL;
in_addr_t ina, mask;
struct sockaddr_dl *dla;
- struct ifreq ifreq;
- struct ifconf ifc;
- struct ifreq ifs[MAX_IFS];
- int sock;
int retval = 0;
- sock = socket(AF_INET, SOCK_DGRAM, 0);
- if (sock < 0)
- xo_err(1, "socket");
-
- ifc.ifc_len = sizeof(ifs);
- ifc.ifc_req = ifs;
- if (ioctl(sock, SIOCGIFCONF, &ifc) < 0) {
- xo_warnx("ioctl(SIOCGIFCONF)");
- goto done;
- }
-
-#define NEXTIFR(i) \
- ((struct ifreq *)((char *)&(i)->ifr_addr \
- + MAX((i)->ifr_addr.sa_len, sizeof((i)->ifr_addr))) )
-
/*
* Scan through looking for an interface with an Internet
* address on the same subnet as `ipaddr'.
*/
- ifend = (struct ifreq *)(ifc.ifc_buf + ifc.ifc_len);
- for (ifr = ifc.ifc_req; ifr < ifend; ifr = NEXTIFR(ifr) ) {
- if (ifr->ifr_addr.sa_family != AF_INET)
+ if (getifaddrs(&ifas) < 0) {
+ xo_warnx("getifaddrs");
+ goto done;
+ }
+
+ for (ifa = ifas; ifa != NULL; ifa = ifa->ifa_next) {
+ if (ifa->ifa_addr == NULL || ifa->ifa_netmask == NULL)
+ continue;
+ if (ifa->ifa_addr->sa_family != AF_INET)
continue;
- strncpy(ifreq.ifr_name, ifr->ifr_name,
- sizeof(ifreq.ifr_name));
- ifreq.ifr_addr = ifr->ifr_addr;
/*
* Check that the interface is up,
* and not point-to-point or loopback.
*/
- if (ioctl(sock, SIOCGIFFLAGS, &ifreq) < 0)
- continue;
- if ((ifreq.ifr_flags &
+ if ((ifa->ifa_flags &
(IFF_UP|IFF_BROADCAST|IFF_POINTOPOINT|
IFF_LOOPBACK|IFF_NOARP)) != (IFF_UP|IFF_BROADCAST))
continue;
/* Get its netmask and check that it's on the right subnet. */
- if (ioctl(sock, SIOCGIFNETMASK, &ifreq) < 0)
- continue;
mask = ((struct sockaddr_in *)
- &ifreq.ifr_addr)->sin_addr.s_addr;
+ ifa->ifa_netmask)->sin_addr.s_addr;
ina = ((struct sockaddr_in *)
- &ifr->ifr_addr)->sin_addr.s_addr;
+ ifa->ifa_addr)->sin_addr.s_addr;
if ((ipaddr & mask) == (ina & mask))
break; /* ok, we got it! */
}
-
- if (ifr >= ifend)
+ if (ifa == NULL)
goto done;
/*
* Now scan through again looking for a link-level address
* for this interface.
*/
- ifp = ifr;
- for (ifr = ifc.ifc_req; ifr < ifend; ifr = NEXTIFR(ifr))
- if (strcmp(ifp->ifr_name, ifr->ifr_name) == 0 &&
- ifr->ifr_addr.sa_family == AF_LINK)
+ for (ifd = ifas; ifd != NULL; ifd = ifd->ifa_next) {
+ if (ifd->ifa_addr == NULL)
+ continue;
+ if (strcmp(ifa->ifa_name, ifd->ifa_name) == 0 &&
+ ifd->ifa_addr->sa_family == AF_LINK)
break;
- if (ifr >= ifend)
+ }
+ if (ifd == NULL)
goto done;
/*
* Found the link-level address - copy it out
*/
- dla = (struct sockaddr_dl *) &ifr->ifr_addr;
+ dla = (struct sockaddr_dl *)ifd->ifa_addr;
memcpy(hwaddr, LLADDR(dla), dla->sdl_alen);
- printf("using interface %s for proxy with address %s\n", ifp->ifr_name,
+ printf("using interface %s for proxy with address %s\n", ifa->ifa_name,
ether_ntoa(hwaddr));
retval = dla->sdl_alen;
done:
- close(sock);
+ if (ifas != NULL)
+ freeifaddrs(ifas);
return (retval);
}
diff --git a/usr.sbin/bhyve/bhyve_config.5 b/usr.sbin/bhyve/bhyve_config.5
index bdf12d0aae16..20109170b094 100644
--- a/usr.sbin/bhyve/bhyve_config.5
+++ b/usr.sbin/bhyve/bhyve_config.5
@@ -23,7 +23,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.Dd March 2, 2022
+.Dd June 30, 2022
.Dt BHYVE_CONFIG 5
.Os
.Sh NAME
@@ -158,6 +158,52 @@ then
.Xr bhyve 8
will write all of its configuration variables to stdout and exit.
No VM will be started.
+.It Va bios.vendor Ta string Ta BHYVE Ta
+This value is used for the guest's System Management BIOS System Information structure.
+.It Va bios.version Ta string Ta 14.0 Ta
+This value is used for the guest's System Management BIOS System Information structure.
+.It Va bios.release_date Ta string Ta 10/17/2021 Ta
+This value is used for the guest's System Management BIOS System Information structure.
+.It Va system.family_name Ta string Ta Virtual Machine Ta
+Family the computer belongs to.
+This value is used for the guest's System Management BIOS System Information structure.
+.It Va system.manufacturer Ta string Ta FreeBSD Ta
+This value is used for the guest's System Management BIOS System Information structure.
+.It Va system.product_name Ta string Ta BHYVE Ta
+This value is used for the guest's System Management BIOS System Information structure.
+.It Va system.serial_number Ta string Ta None Ta
+This value is used for the guest's System Management BIOS System Information structure.
+.It Va system.sku Ta string Ta None Ta
+Stock keeping unit of the computer.
+It's also called product ID or purchase order number.
+This value is used for the guest's System Management BIOS System Information structure.
+.It Va system.version Ta string Ta 1.0 Ta
+This value is used for the guest's System Management BIOS System Information structure.
+.It Va board.manufacturer Ta string Ta FreeBSD Ta
+This value is used for the guest's System Management BIOS System Information structure.
+.It Va board.product_name Ta string Ta BHYVE Ta
+This value is used for the guest's System Management BIOS System Information structure.
+.It Va board.version Ta string Ta 1.0 Ta
+This value is used for the guest's System Management BIOS System Information structure.
+.It Va board.serial_number Ta string Ta None Ta
+This value is used for the guest's System Management BIOS System Information structure.
+.It Va board.asset_tag Ta string Ta None Ta
+This value is used for the guest's System Management BIOS System Information structure.
+.It Va board.location Ta string Ta None Ta
+Describes the board's location within the chassis.
+This value is used for the guest's System Management BIOS System Information structure.
+.It Va chassis.manufacturer Ta string Ta FreeBSD Ta
+This value is used for the guest's System Management BIOS System Information structure.
+.It Va chassis.version Ta string Ta 1.0 Ta
+This value is used for the guest's System Management BIOS System Information structure.
+.It Va chassis.serial_number Ta string Ta None Ta
+This value is used for the guest's System Management BIOS System Information structure.
+.It Va chassis.asset_tag Ta string Ta None Ta
+This value is used for the guest's System Management BIOS System Information structure.
+.It Va chassis.sku Ta string Ta None Ta
+Stock keeping unit of the chassis.
+It's also called product ID or purchase order number.
+This value is used for the guest's System Management BIOS System Information structure.
.El
.Ss x86-Specific Settings
.Bl -column "x86.vmexit_on_pause" "integer" "Default"
@@ -290,16 +336,21 @@ The backend is responsible for passing packets between the device model
and a desired destination.
Configuring a backend requires setting the
.Va backend
-variable to one of the following values:
-.Bl -tag
-.It tap Ns Va N
-Use the named
+variable.
+The type of a backend can either be set explicitly via the
+.Va type
+variable
+or it can be inferred from the value of
+.Va backend .
+.Pp
+The following types of backends are supported:
+.Bl -tag -width "netgraph"
+.It tap
+Use the
.Xr tap 4
-interface as the backend.
-.It vmnet Ns Va N
-Use the named
-.Xr vmnet 4
-interface as the backend.
+interface named in
+.Va backend
+as the backend.
.It netgraph
Use a
.Xr netgraph 4
@@ -322,14 +373,31 @@ The name of the source hook on the created
.Xr ng_socket 4
node.
.El
-.It netmap: Ns Va interface
+.It netmap
Use
.Xr netmap 4
-on a network interface as the backend.
-.It vale Ns Va bridge : Ns Va port
-Use a port on a
+either on a network interface or a port on a
.Xr vale 4
bridge as the backend.
+The value of
+.Va backend
+is passed to
+.Xr nm_open
+to connect to a netmap port.
+.El
+.Pp
+If
+.Va type
+is not specified explicitly, then it is inferred from
+.Va backend
+based on the following patterns:
+.Bl -column -offset indent "valuebridge:port"
+.It Sy Pattern Ta Sy Type
+.It tap Ns Va N Ta tap
+.It vmnet Ns Va N Ta tap
+.It netgraph Ta netgraph
+.It netmap: Ns Va interface Ta netmap
+.It vale Ns Va bridge : Ns Va port Ta netmap
.El
.Ss UART Device Settings
.Bl -column "Name" "Format" "Default"
diff --git a/usr.sbin/bhyve/bhyverun.c b/usr.sbin/bhyve/bhyverun.c
index a412785f5a01..4f9fc4a2b050 100644
--- a/usr.sbin/bhyve/bhyverun.c
+++ b/usr.sbin/bhyve/bhyverun.c
@@ -1536,7 +1536,8 @@ main(int argc, char *argv[])
}
error = smbios_build(ctx);
- assert(error == 0);
+ if (error != 0)
+ exit(4);
if (get_config_bool("acpi_tables")) {
error = acpi_build(ctx, guest_ncpus);
diff --git a/usr.sbin/bhyve/block_if.c b/usr.sbin/bhyve/block_if.c
index 06c3cb50fb13..05c9d9731754 100644
--- a/usr.sbin/bhyve/block_if.c
+++ b/usr.sbin/bhyve/block_if.c
@@ -109,11 +109,9 @@ struct blockif_ctxt {
int bc_psectoff;
int bc_closing;
int bc_paused;
- int bc_work_count;
pthread_t bc_btid[BLOCKIF_NUMTHR];
pthread_mutex_t bc_mtx;
pthread_cond_t bc_cond;
- pthread_cond_t bc_paused_cond;
pthread_cond_t bc_work_done_cond;
blockif_resize_cb *bc_resize_cb;
void *bc_resize_cb_arg;
@@ -362,6 +360,12 @@ blockif_proc(struct blockif_ctxt *bc, struct blockif_elem *be, uint8_t *buf)
(*br->br_callback)(br, err);
}
+static inline bool
+blockif_empty(const struct blockif_ctxt *bc)
+{
+ return (TAILQ_EMPTY(&bc->bc_pendq) && TAILQ_EMPTY(&bc->bc_busyq));
+}
+
static void *
blockif_thr(void *arg)
{
@@ -379,30 +383,21 @@ blockif_thr(void *arg)
pthread_mutex_lock(&bc->bc_mtx);
for (;;) {
- bc->bc_work_count++;
-
- /* We cannot process work if the interface is paused */
- while (!bc->bc_paused && blockif_dequeue(bc, t, &be)) {
+ while (blockif_dequeue(bc, t, &be)) {
pthread_mutex_unlock(&bc->bc_mtx);
blockif_proc(bc, be, buf);
pthread_mutex_lock(&bc->bc_mtx);
blockif_complete(bc, be);
}
- bc->bc_work_count--;
-
- /* If none of the workers are busy, notify the main thread */
- if (bc->bc_work_count == 0)
+ /* If none to work, notify the main thread */
+ if (blockif_empty(bc))
pthread_cond_broadcast(&bc->bc_work_done_cond);
/* Check ctxt status here to see if exit requested */
if (bc->bc_closing)
break;
- /* Make all worker threads wait here if the device is paused */
- while (bc->bc_paused)
- pthread_cond_wait(&bc->bc_paused_cond, &bc->bc_mtx);
-
pthread_cond_wait(&bc->bc_cond, &bc->bc_mtx);
}
pthread_mutex_unlock(&bc->bc_mtx);
@@ -638,8 +633,6 @@ blockif_open(nvlist_t *nvl, const char *ident)
pthread_mutex_init(&bc->bc_mtx, NULL);
pthread_cond_init(&bc->bc_cond, NULL);
bc->bc_paused = 0;
- bc->bc_work_count = 0;
- pthread_cond_init(&bc->bc_paused_cond, NULL);
pthread_cond_init(&bc->bc_work_done_cond, NULL);
TAILQ_INIT(&bc->bc_freeq);
TAILQ_INIT(&bc->bc_pendq);
@@ -737,6 +730,7 @@ blockif_request(struct blockif_ctxt *bc, struct blockif_req *breq,
err = 0;
pthread_mutex_lock(&bc->bc_mtx);
+ assert(!bc->bc_paused);
if (!TAILQ_EMPTY(&bc->bc_freeq)) {
/*
* Enqueue and inform the block i/o thread
@@ -761,7 +755,6 @@ blockif_request(struct blockif_ctxt *bc, struct blockif_req *breq,
int
blockif_read(struct blockif_ctxt *bc, struct blockif_req *breq)
{
-
assert(bc->bc_magic == BLOCKIF_SIG);
return (blockif_request(bc, breq, BOP_READ));
}
@@ -769,7 +762,6 @@ blockif_read(struct blockif_ctxt *bc, struct blockif_req *breq)
int
blockif_write(struct blockif_ctxt *bc, struct blockif_req *breq)
{
-
assert(bc->bc_magic == BLOCKIF_SIG);
return (blockif_request(bc, breq, BOP_WRITE));
}
@@ -777,7 +769,6 @@ blockif_write(struct blockif_ctxt *bc, struct blockif_req *breq)
int
blockif_flush(struct blockif_ctxt *bc, struct blockif_req *breq)
{
-
assert(bc->bc_magic == BLOCKIF_SIG);
return (blockif_request(bc, breq, BOP_FLUSH));
}
@@ -785,7 +776,6 @@ blockif_flush(struct blockif_ctxt *bc, struct blockif_req *breq)
int
blockif_delete(struct blockif_ctxt *bc, struct blockif_req *breq)
{
-
assert(bc->bc_magic == BLOCKIF_SIG);
return (blockif_request(bc, breq, BOP_DELETE));
}
@@ -955,7 +945,6 @@ blockif_chs(struct blockif_ctxt *bc, uint16_t *c, uint8_t *h, uint8_t *s)
off_t
blockif_size(struct blockif_ctxt *bc)
{
-
assert(bc->bc_magic == BLOCKIF_SIG);
return (bc->bc_size);
}
@@ -963,7 +952,6 @@ blockif_size(struct blockif_ctxt *bc)
int
blockif_sectsz(struct blockif_ctxt *bc)
{
-
assert(bc->bc_magic == BLOCKIF_SIG);
return (bc->bc_sectsz);
}
@@ -971,7 +959,6 @@ blockif_sectsz(struct blockif_ctxt *bc)
void
blockif_psectsz(struct blockif_ctxt *bc, int *size, int *off)
{
-
assert(bc->bc_magic == BLOCKIF_SIG);
*size = bc->bc_psectsz;
*off = bc->bc_psectoff;
@@ -980,7 +967,6 @@ blockif_psectsz(struct blockif_ctxt *bc, int *size, int *off)
int
blockif_queuesz(struct blockif_ctxt *bc)
{
-
assert(bc->bc_magic == BLOCKIF_SIG);
return (BLOCKIF_MAXREQ - 1);
}
@@ -988,7 +974,6 @@ blockif_queuesz(struct blockif_ctxt *bc)
int
blockif_is_ro(struct blockif_ctxt *bc)
{
-
assert(bc->bc_magic == BLOCKIF_SIG);
return (bc->bc_rdonly);
}
@@ -996,7 +981,6 @@ blockif_is_ro(struct blockif_ctxt *bc)
int
blockif_candelete(struct blockif_ctxt *bc)
{
-
assert(bc->bc_magic == BLOCKIF_SIG);
return (bc->bc_candelete);
}
@@ -1012,7 +996,7 @@ blockif_pause(struct blockif_ctxt *bc)
bc->bc_paused = 1;
/* The interface is paused. Wait for workers to finish their work */
- while (bc->bc_work_count)
+ while (!blockif_empty(bc))
pthread_cond_wait(&bc->bc_work_done_cond, &bc->bc_mtx);
pthread_mutex_unlock(&bc->bc_mtx);
@@ -1029,71 +1013,6 @@ blockif_resume(struct blockif_ctxt *bc)
pthread_mutex_lock(&bc->bc_mtx);
bc->bc_paused = 0;
- /* resume the threads waiting for paused */
- pthread_cond_broadcast(&bc->bc_paused_cond);
- /* kick the threads after restore */
- pthread_cond_broadcast(&bc->bc_cond);
- pthread_mutex_unlock(&bc->bc_mtx);
-}
-
-int
-blockif_snapshot_req(struct blockif_req *br, struct vm_snapshot_meta *meta)
-{
- int i;
- struct iovec *iov;
- int ret;
-
- SNAPSHOT_VAR_OR_LEAVE(br->br_iovcnt, meta, ret, done);
- SNAPSHOT_VAR_OR_LEAVE(br->br_offset, meta, ret, done);
- SNAPSHOT_VAR_OR_LEAVE(br->br_resid, meta, ret, done);
-
- /*
- * XXX: The callback and parameter must be filled by the virtualized
- * device that uses the interface, during its init; we're not touching
- * them here.
- */
-
- /* Snapshot the iovecs. */
- for (i = 0; i < br->br_iovcnt; i++) {
- iov = &br->br_iov[i];
-
- SNAPSHOT_VAR_OR_LEAVE(iov->iov_len, meta, ret, done);
-
- /* We assume the iov is a guest-mapped address. */
- SNAPSHOT_GUEST2HOST_ADDR_OR_LEAVE(iov->iov_base, iov->iov_len,
- false, meta, ret, done);
- }
-
-done:
- return (ret);
-}
-
-int
-blockif_snapshot(struct blockif_ctxt *bc, struct vm_snapshot_meta *meta)
-{
- int ret;
-
- if (bc->bc_paused == 0) {
- fprintf(stderr, "%s: Snapshot failed: "
- "interface not paused.\r\n", __func__);
- return (ENXIO);
- }
-
- pthread_mutex_lock(&bc->bc_mtx);
-
- SNAPSHOT_VAR_OR_LEAVE(bc->bc_magic, meta, ret, done);
- SNAPSHOT_VAR_OR_LEAVE(bc->bc_ischr, meta, ret, done);
- SNAPSHOT_VAR_OR_LEAVE(bc->bc_isgeom, meta, ret, done);
- SNAPSHOT_VAR_OR_LEAVE(bc->bc_candelete, meta, ret, done);
- SNAPSHOT_VAR_OR_LEAVE(bc->bc_rdonly, meta, ret, done);
- SNAPSHOT_VAR_OR_LEAVE(bc->bc_size, meta, ret, done);
- SNAPSHOT_VAR_OR_LEAVE(bc->bc_sectsz, meta, ret, done);
- SNAPSHOT_VAR_OR_LEAVE(bc->bc_psectsz, meta, ret, done);
- SNAPSHOT_VAR_OR_LEAVE(bc->bc_psectoff, meta, ret, done);
- SNAPSHOT_VAR_OR_LEAVE(bc->bc_closing, meta, ret, done);
-
-done:
pthread_mutex_unlock(&bc->bc_mtx);
- return (ret);
}
-#endif
+#endif /* BHYVE_SNAPSHOT */
diff --git a/usr.sbin/bhyve/block_if.h b/usr.sbin/bhyve/block_if.h
index 0407ff43cf94..cf3acc05c98f 100644
--- a/usr.sbin/bhyve/block_if.h
+++ b/usr.sbin/bhyve/block_if.h
@@ -87,10 +87,6 @@ int blockif_close(struct blockif_ctxt *bc);
#ifdef BHYVE_SNAPSHOT
void blockif_pause(struct blockif_ctxt *bc);
void blockif_resume(struct blockif_ctxt *bc);
-int blockif_snapshot_req(struct blockif_req *br,
- struct vm_snapshot_meta *meta);
-int blockif_snapshot(struct blockif_ctxt *bc,
- struct vm_snapshot_meta *meta);
#endif
#endif /* _BLOCK_IF_H_ */
diff --git a/usr.sbin/bhyve/net_backends.c b/usr.sbin/bhyve/net_backends.c
index ca4c96b10239..9fcc33216bad 100644
--- a/usr.sbin/bhyve/net_backends.c
+++ b/usr.sbin/bhyve/net_backends.c
@@ -980,7 +980,7 @@ netbe_init(struct net_backend **ret, nvlist_t *nvl, net_be_rxeof_t cb,
void *param)
{
struct net_backend **pbe, *nbe, *tbe = NULL;
- const char *value;
+ const char *value, *type;
char *devname;
int err;
@@ -991,11 +991,19 @@ netbe_init(struct net_backend **ret, nvlist_t *nvl, net_be_rxeof_t cb,
devname = strdup(value);
/*
+ * Use the type given by configuration if exists; otherwise
+ * use the prefix of the backend as the type.
+ */
+ type = get_config_value_node(nvl, "type");
+ if (type == NULL)
+ type = devname;
+
+ /*
* Find the network backend that matches the user-provided
* device name. net_backend_set is built using a linker set.
*/
SET_FOREACH(pbe, net_backend_set) {
- if (strncmp(devname, (*pbe)->prefix,
+ if (strncmp(type, (*pbe)->prefix,
strlen((*pbe)->prefix)) == 0) {
tbe = *pbe;
assert(tbe->init != NULL);
diff --git a/usr.sbin/bhyve/pci_ahci.c b/usr.sbin/bhyve/pci_ahci.c
index d07b1f085e3d..ba8921ea3b6f 100644
--- a/usr.sbin/bhyve/pci_ahci.c
+++ b/usr.sbin/bhyve/pci_ahci.c
@@ -2563,113 +2563,13 @@ open_fail:
#ifdef BHYVE_SNAPSHOT
static int
-pci_ahci_snapshot_save_queues(struct ahci_port *port,
- struct vm_snapshot_meta *meta)
-{
- int ret;
- int idx;
- struct ahci_ioreq *ioreq;
-
- STAILQ_FOREACH(ioreq, &port->iofhd, io_flist) {
- idx = ((void *) ioreq - (void *) port->ioreq) / sizeof(*ioreq);
- SNAPSHOT_VAR_OR_LEAVE(idx, meta, ret, done);
- }
-
- idx = -1;
- SNAPSHOT_VAR_OR_LEAVE(idx, meta, ret, done);
-
- TAILQ_FOREACH(ioreq, &port->iobhd, io_blist) {
- idx = ((void *) ioreq - (void *) port->ioreq) / sizeof(*ioreq);
- SNAPSHOT_VAR_OR_LEAVE(idx, meta, ret, done);
-
- /*
- * Snapshot only the busy requests; other requests are
- * not valid.
- */
- ret = blockif_snapshot_req(&ioreq->io_req, meta);
- if (ret != 0) {
- fprintf(stderr, "%s: failed to snapshot req\r\n",
- __func__);
- goto done;
- }
- }
-
- idx = -1;
- SNAPSHOT_VAR_OR_LEAVE(idx, meta, ret, done);
-
-done:
- return (ret);
-}
-
-static int
-pci_ahci_snapshot_restore_queues(struct ahci_port *port,
- struct vm_snapshot_meta *meta)
-{
- int ret;
- int idx;
- struct ahci_ioreq *ioreq;
-
- /* Empty the free queue before restoring. */
- while (!STAILQ_EMPTY(&port->iofhd))
- STAILQ_REMOVE_HEAD(&port->iofhd, io_flist);
-
- /* Restore the free queue. */
- while (1) {
- SNAPSHOT_VAR_OR_LEAVE(idx, meta, ret, done);
- if (idx == -1)
- break;
-
- STAILQ_INSERT_TAIL(&port->iofhd, &port->ioreq[idx], io_flist);
- }
-
- /* Restore the busy queue. */
- while (1) {
- SNAPSHOT_VAR_OR_LEAVE(idx, meta, ret, done);
- if (idx == -1)
- break;
-
- ioreq = &port->ioreq[idx];
- TAILQ_INSERT_TAIL(&port->iobhd, ioreq, io_blist);
-
- /*
- * Restore only the busy requests; other requests are
- * not valid.
- */
- ret = blockif_snapshot_req(&ioreq->io_req, meta);
- if (ret != 0) {
- fprintf(stderr, "%s: failed to restore request\r\n",
- __func__);
- goto done;
- }
-
- /* Re-enqueue the requests in the block interface. */
- if (ioreq->readop)
- ret = blockif_read(port->bctx, &ioreq->io_req);
- else
- ret = blockif_write(port->bctx, &ioreq->io_req);
-
- if (ret != 0) {
- fprintf(stderr,
- "%s: failed to re-enqueue request\r\n",
- __func__);
- goto done;
- }
- }
-
-done:
- return (ret);
-}
-
-static int
pci_ahci_snapshot(struct vm_snapshot_meta *meta)
{
- int i, j, ret;
+ int i, ret;
void *bctx;
struct pci_devinst *pi;
struct pci_ahci_softc *sc;
struct ahci_port *port;
- struct ahci_cmd_hdr *hdr;
- struct ahci_ioreq *ioreq;
pi = meta->dev_data;
sc = pi->pi_arg;
@@ -2753,43 +2653,7 @@ pci_ahci_snapshot(struct vm_snapshot_meta *meta)
SNAPSHOT_VAR_OR_LEAVE(port->fbs, meta, ret, done);
SNAPSHOT_VAR_OR_LEAVE(port->ioqsz, meta, ret, done);
- for (j = 0; j < port->ioqsz; j++) {
- ioreq = &port->ioreq[j];
-
- /* blockif_req snapshot done only for busy requests. */
- hdr = (struct ahci_cmd_hdr *)(port->cmd_lst +
- ioreq->slot * AHCI_CL_SIZE);
- SNAPSHOT_GUEST2HOST_ADDR_OR_LEAVE(ioreq->cfis,
- 0x80 + hdr->prdtl * sizeof(struct ahci_prdt_entry),
- false, meta, ret, done);
-
- SNAPSHOT_VAR_OR_LEAVE(ioreq->len, meta, ret, done);
- SNAPSHOT_VAR_OR_LEAVE(ioreq->done, meta, ret, done);
- SNAPSHOT_VAR_OR_LEAVE(ioreq->slot, meta, ret, done);
- SNAPSHOT_VAR_OR_LEAVE(ioreq->more, meta, ret, done);
- SNAPSHOT_VAR_OR_LEAVE(ioreq->readop, meta, ret, done);
- }
-
- /* Perform save / restore specific operations. */
- if (meta->op == VM_SNAPSHOT_SAVE) {
- ret = pci_ahci_snapshot_save_queues(port, meta);
- if (ret != 0)
- goto done;
- } else if (meta->op == VM_SNAPSHOT_RESTORE) {
- ret = pci_ahci_snapshot_restore_queues(port, meta);
- if (ret != 0)
- goto done;
- } else {
- ret = EINVAL;
- goto done;
- }
-
- ret = blockif_snapshot(port->bctx, meta);
- if (ret != 0) {
- fprintf(stderr, "%s: failed to restore blockif\r\n",
- __func__);
- goto done;
- }
+ assert(TAILQ_EMPTY(&port->iobhd));
}
done:
@@ -2835,7 +2699,7 @@ pci_ahci_resume(struct vmctx *ctx, struct pci_devinst *pi)
return (0);
}
-#endif
+#endif /* BHYVE_SNAPSHOT */
/*
* Use separate emulation names to distinguish drive and atapi devices
diff --git a/usr.sbin/bhyve/pci_passthru.c b/usr.sbin/bhyve/pci_passthru.c
index 153554f97e43..f4b532ed101e 100644
--- a/usr.sbin/bhyve/pci_passthru.c
+++ b/usr.sbin/bhyve/pci_passthru.c
@@ -1094,7 +1094,7 @@ passthru_addr_rom(struct pci_devinst *const pi, const int idx,
} else {
if (vm_mmap_memseg(pi->pi_vmctx, addr, VM_PCIROM,
pi->pi_romoffset, size, PROT_READ | PROT_EXEC) != 0) {
- errx(4, "%s: mnmap_memseg @ [%016lx - %016lx] failed",
+ errx(4, "%s: mmap_memseg @ [%016lx - %016lx] failed",
__func__, addr, addr + size);
}
}
diff --git a/usr.sbin/bhyve/pci_virtio_block.c b/usr.sbin/bhyve/pci_virtio_block.c
index 385b812be84c..29520309af8b 100644
--- a/usr.sbin/bhyve/pci_virtio_block.c
+++ b/usr.sbin/bhyve/pci_virtio_block.c
@@ -592,6 +592,8 @@ struct pci_devemu pci_de_vblk = {
.pe_barread = vi_pci_read,
#ifdef BHYVE_SNAPSHOT
.pe_snapshot = vi_pci_snapshot,
+ .pe_pause = vi_pci_pause,
+ .pe_resume = vi_pci_resume,
#endif
};
PCI_EMUL_SET(pci_de_vblk);
diff --git a/usr.sbin/bhyve/smbiostbl.c b/usr.sbin/bhyve/smbiostbl.c
index 2dc50e6e6f01..bc85bb6c97af 100644
--- a/usr.sbin/bhyve/smbiostbl.c
+++ b/usr.sbin/bhyve/smbiostbl.c
@@ -76,13 +76,18 @@ struct smbios_structure {
uint16_t handle;
} __packed;
+struct smbios_string {
+ const char *node;
+ const char *value;
+};
+
typedef int (*initializer_func_t)(struct smbios_structure *template_entry,
- const char **template_strings, char *curaddr, char **endaddr,
+ struct smbios_string *template_strings, char *curaddr, char **endaddr,
uint16_t *n, uint16_t *size);
struct smbios_template_entry {
struct smbios_structure *entry;
- const char **strings;
+ struct smbios_string *strings;
initializer_func_t initializer;
};
@@ -350,11 +355,11 @@ struct smbios_table_type0 smbios_type0_template = {
0xff /* embedded controller firmware minor release */
};
-const char *smbios_type0_strings[] = {
- "BHYVE", /* vendor string */
- FIRMWARE_VERSION, /* bios version string */
- FIRMWARE_RELEASE_DATE, /* bios release date string */
- NULL
+struct smbios_string smbios_type0_strings[] = {
+ { "bios.vendor", "BHYVE" }, /* vendor string */
+ { "bios.version", FIRMWARE_VERSION }, /* bios version string */
+ { "bios.release_date", FIRMWARE_RELEASE_DATE }, /* bios release date string */
+ { 0 }
};
struct smbios_table_type1 smbios_type1_template = {
@@ -370,17 +375,17 @@ struct smbios_table_type1 smbios_type1_template = {
};
static int smbios_type1_initializer(struct smbios_structure *template_entry,
- const char **template_strings, char *curaddr, char **endaddr,
+ struct smbios_string *template_strings, char *curaddr, char **endaddr,
uint16_t *n, uint16_t *size);
-const char *smbios_type1_strings[] = {
- "FreeBSD", /* manufacturer string */
- "BHYVE", /* product name string */
- "1.0", /* version string */
- "None", /* serial number string */
- "None", /* sku string */
- "Virtual Machine", /* family name string */
- NULL
+struct smbios_string smbios_type1_strings[] = {
+ { "system.manufacturer", "FreeBSD" }, /* manufacturer string */
+ { "system.product_name", "BHYVE" }, /* product string */
+ { "system.version", "1.0" }, /* version string */
+ { "system.serial_number", "None" }, /* serial number string */
+ { "system.sku", "None" }, /* sku string */
+ { "system.family_name", "Virtual Machine" }, /* family string */
+ { 0 }
};
struct smbios_table_type2 smbios_type2_template = {
@@ -397,14 +402,14 @@ struct smbios_table_type2 smbios_type2_template = {
0
};
-const char *smbios_type2_strings[] = {
- "FreeBSD", /* manufacturer string */
- "BHYVE", /* product name string */
- "1.0", /* version string */
- "None", /* serial number string */
- "None", /* asset tag string */
- "None", /* location string */
- NULL
+struct smbios_string smbios_type2_strings[] = {
+ { "board.manufacturer", "FreeBSD" }, /* manufacturer string */
+ { "board.product_name", "BHYVE" }, /* product name string */
+ { "board.version", "1.0" }, /* version string */
+ { "board.serial_number", "None" }, /* serial number string */
+ { "board.asset_tag", "None" }, /* asset tag string */
+ { "board.location", "None" }, /* location string */
+ { 0 }
};
struct smbios_table_type3 smbios_type3_template = {
@@ -426,13 +431,13 @@ struct smbios_table_type3 smbios_type3_template = {
5 /* sku number string */
};
-const char *smbios_type3_strings[] = {
- "FreeBSD", /* manufacturer string */
- "1.0", /* version string */
- "None", /* serial number string */
- "None", /* asset tag string */
- "None", /* sku number string */
- NULL
+struct smbios_string smbios_type3_strings[] = {
+ { "chassis.manufacturer", "FreeBSD" }, /* manufacturer string */
+ { "chassis.version", "1.0" }, /* version string */
+ { "chassis.serial_number", "None" }, /* serial number string */
+ { "chassis.asset_tag", "None" }, /* asset tag string */
+ { "chassis.sku", "None" }, /* sku number string */
+ { 0 }
};
struct smbios_table_type4 smbios_type4_template = {
@@ -462,18 +467,18 @@ struct smbios_table_type4 smbios_type4_template = {
SMBIOS_PRF_OTHER
};
-const char *smbios_type4_strings[] = {
- " ", /* socket designation string */
- " ", /* manufacturer string */
- " ", /* version string */
- "None", /* serial number string */
- "None", /* asset tag string */
- "None", /* part number string */
- NULL
+struct smbios_string smbios_type4_strings[] = {
+ { NULL, " " }, /* socket designation string */
+ { NULL, " " }, /* manufacturer string */
+ { NULL, " " }, /* version string */
+ { NULL, "None" }, /* serial number string */
+ { NULL, "None" }, /* asset tag string */
+ { NULL, "None" }, /* part number string */
+ { 0 }
};
static int smbios_type4_initializer(struct smbios_structure *template_entry,
- const char **template_strings, char *curaddr, char **endaddr,
+ struct smbios_string *template_strings, char *curaddr, char **endaddr,
uint16_t *n, uint16_t *size);
struct smbios_table_type16 smbios_type16_template = {
@@ -488,7 +493,7 @@ struct smbios_table_type16 smbios_type16_template = {
};
static int smbios_type16_initializer(struct smbios_structure *template_entry,
- const char **template_strings, char *curaddr, char **endaddr,
+ struct smbios_string *template_strings, char *curaddr, char **endaddr,
uint16_t *n, uint16_t *size);
struct smbios_table_type17 smbios_type17_template = {
@@ -517,18 +522,18 @@ struct smbios_table_type17 smbios_type17_template = {
0 /* configured voltage in mv (0=unknown) */
};
-const char *smbios_type17_strings[] = {
- " ", /* device locator string */
- " ", /* physical bank locator string */
- " ", /* manufacturer string */
- "None", /* serial number string */
- "None", /* asset tag string */
- "None", /* part number string */
- NULL
+struct smbios_string smbios_type17_strings[] = {
+ { NULL, " " }, /* device locator string */
+ { NULL, " " }, /* physical bank locator string */
+ { NULL, " " }, /* manufacturer string */
+ { NULL, "None" }, /* serial number string */
+ { NULL, "None" }, /* asset tag string */
+ { NULL, "None" }, /* part number string */
+ { 0 }
};
static int smbios_type17_initializer(struct smbios_structure *template_entry,
- const char **template_strings, char *curaddr, char **endaddr,
+ struct smbios_string *template_strings, char *curaddr, char **endaddr,
uint16_t *n, uint16_t *size);
struct smbios_table_type19 smbios_type19_template = {
@@ -542,7 +547,7 @@ struct smbios_table_type19 smbios_type19_template = {
};
static int smbios_type19_initializer(struct smbios_structure *template_entry,
- const char **template_strings, char *curaddr, char **endaddr,
+ struct smbios_string *template_strings, char *curaddr, char **endaddr,
uint16_t *n, uint16_t *size);
struct smbios_table_type32 smbios_type32_template = {
@@ -556,7 +561,7 @@ struct smbios_table_type127 smbios_type127_template = {
};
static int smbios_generic_initializer(struct smbios_structure *template_entry,
- const char **template_strings, char *curaddr, char **endaddr,
+ struct smbios_string *template_strings, char *curaddr, char **endaddr,
uint16_t *n, uint16_t *size);
static struct smbios_template_entry smbios_template[] = {
@@ -598,7 +603,7 @@ static uint16_t type16_handle;
static int
smbios_generic_initializer(struct smbios_structure *template_entry,
- const char **template_strings, char *curaddr, char **endaddr,
+ struct smbios_string *template_strings, char *curaddr, char **endaddr,
uint16_t *n, uint16_t *size)
{
struct smbios_structure *entry;
@@ -610,11 +615,20 @@ smbios_generic_initializer(struct smbios_structure *template_entry,
if (template_strings != NULL) {
int i;
- for (i = 0; template_strings[i] != NULL; i++) {
+ for (i = 0; template_strings[i].value != NULL; i++) {
const char *string;
int len;
- string = template_strings[i];
+ if (template_strings[i].node == NULL) {
+ string = template_strings[i].value;
+ } else {
+ set_config_value_if_unset(
+ template_strings[i].node,
+ template_strings[i].value);
+ string = get_config_value(
+ template_strings[i].node);
+ }
+
len = strlen(string) + 1;
memcpy(curaddr, string, len);
curaddr += len;
@@ -636,7 +650,7 @@ smbios_generic_initializer(struct smbios_structure *template_entry,
static int
smbios_type1_initializer(struct smbios_structure *template_entry,
- const char **template_strings, char *curaddr, char **endaddr,
+ struct smbios_string *template_strings, char *curaddr, char **endaddr,
uint16_t *n, uint16_t *size)
{
struct smbios_table_type1 *type1;
@@ -652,8 +666,10 @@ smbios_type1_initializer(struct smbios_structure *template_entry,
uint32_t status;
uuid_from_string(guest_uuid_str, &uuid, &status);
- if (status != uuid_s_ok)
+ if (status != uuid_s_ok) {
+ EPRINTLN("Invalid UUID");
return (-1);
+ }
uuid_enc_le(&type1->uuid, &uuid);
} else {
@@ -692,7 +708,7 @@ smbios_type1_initializer(struct smbios_structure *template_entry,
static int
smbios_type4_initializer(struct smbios_structure *template_entry,
- const char **template_strings, char *curaddr, char **endaddr,
+ struct smbios_string *template_strings, char *curaddr, char **endaddr,
uint16_t *n, uint16_t *size)
{
int i;
@@ -734,7 +750,7 @@ smbios_type4_initializer(struct smbios_structure *template_entry,
static int
smbios_type16_initializer(struct smbios_structure *template_entry,
- const char **template_strings, char *curaddr, char **endaddr,
+ struct smbios_string *template_strings, char *curaddr, char **endaddr,
uint16_t *n, uint16_t *size)
{
struct smbios_table_type16 *type16;
@@ -751,7 +767,7 @@ smbios_type16_initializer(struct smbios_structure *template_entry,
static int
smbios_type17_initializer(struct smbios_structure *template_entry,
- const char **template_strings, char *curaddr, char **endaddr,
+ struct smbios_string *template_strings, char *curaddr, char **endaddr,
uint16_t *n, uint16_t *size)
{
struct smbios_table_type17 *type17;
@@ -795,7 +811,7 @@ smbios_type17_initializer(struct smbios_structure *template_entry,
static int
smbios_type19_initializer(struct smbios_structure *template_entry,
- const char **template_strings, char *curaddr, char **endaddr,
+ struct smbios_string *template_strings, char *curaddr, char **endaddr,
uint16_t *n, uint16_t *size)
{
struct smbios_table_type19 *type19;
@@ -892,7 +908,7 @@ smbios_build(struct vmctx *ctx)
maxssize = 0;
for (i = 0; smbios_template[i].entry != NULL; i++) {
struct smbios_structure *entry;
- const char **strings;
+ struct smbios_string *strings;
initializer_func_t initializer;
char *endaddr;
uint16_t size;
diff --git a/usr.sbin/bluetooth/bthidd/server.c b/usr.sbin/bluetooth/bthidd/server.c
index 33915307db0e..59b4bafc6a06 100644
--- a/usr.sbin/bluetooth/bthidd/server.c
+++ b/usr.sbin/bluetooth/bthidd/server.c
@@ -114,7 +114,7 @@ server_init(bthid_server_p srv)
return (-1);
}
- /* Create intrrupt socket */
+ /* Create interrupt socket */
srv->intr = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BLUETOOTH_PROTO_L2CAP);
if (srv->intr < 0) {
syslog(LOG_ERR, "Could not create interrupt L2CAP socket. " \
diff --git a/usr.sbin/bsdinstall/bsdinstall.8 b/usr.sbin/bsdinstall/bsdinstall.8
index e2f4f5675ea9..b76cdc04b9c3 100644
--- a/usr.sbin/bsdinstall/bsdinstall.8
+++ b/usr.sbin/bsdinstall/bsdinstall.8
@@ -25,7 +25,7 @@
.\"
.\" $FreeBSD$
.\"
-.Dd March 23, 2022
+.Dd July 18, 2022
.Dt BSDINSTALL 8
.Os
.Sh NAME
@@ -313,7 +313,9 @@ that prompt for a
mirror will skip that step if this variable is already defined in the
environment.
Example:
-.Pa ftp://ftp.freebsd.org/pub/FreeBSD/releases/powerpc/powerpc64/9.1-RELEASE
+.Pa https://download.freebsd.org/ftp/releases/powerpc/powerpc64/13.1-RELEASE/
+or
+.Pa http://ftp-archive.freebsd.org/pub/FreeBSD-Archive/old-releases/amd64/12.2-RELEASE/ .
.It Ev BSDINSTALL_CHROOT
The directory into which the distribution files should be unpacked and the
directory at which the root file system of the new system should be mounted.
@@ -341,6 +343,15 @@ target is executed.
If this directory does not already exist, it will be created.
Default:
.Dq Pa $TMPDIR/bsdinstall_boot
+.It Ev ROOTPASS_ENC
+Encrypted string to set the root password to in the format expected by
+.Xr pw 8
+.Fl H Ar 0 .
+This option is used if both it and
+.Ev ROOTPASS_PLAIN
+are set.
+.It Ev ROOTPASS_PLAIN
+Plain text string to set the root password to.
.It Ev ZFSBOOT_POOL_NAME
Name for the pool containing the base system.
Default:
diff --git a/usr.sbin/bsdinstall/scripts/auto b/usr.sbin/bsdinstall/scripts/auto
index 7398f382273c..7d5396d15c01 100755
--- a/usr.sbin/bsdinstall/scripts/auto
+++ b/usr.sbin/bsdinstall/scripts/auto
@@ -152,7 +152,7 @@ bsdinstall keymap
trap error SIGINT # Catch cntrl-C here
bsdinstall hostname || error "Set hostname failed"
-export DISTRIBUTIONS="base.txz kernel.txz"
+export DISTRIBUTIONS="${DISTRIBUTIONS:-base.txz kernel.txz}"
if [ -f $BSDINSTALL_DISTDIR/MANIFEST ]; then
DISTMENU=`awk -F'\t' '!/^(kernel\.txz|base\.txz)/{print $1,$5,$6}' $BSDINSTALL_DISTDIR/MANIFEST`
DISTMENU="$(echo ${DISTMENU} | sed -E 's/\.txz//g')"
diff --git a/usr.sbin/bsdinstall/scripts/docsinstall b/usr.sbin/bsdinstall/scripts/docsinstall
index c24941658a88..998f86ab9517 100755
--- a/usr.sbin/bsdinstall/scripts/docsinstall
+++ b/usr.sbin/bsdinstall/scripts/docsinstall
@@ -42,7 +42,7 @@ f_include $BSDCFG_SHARE/packages/packages.subr
# List of languages to display (descriptions pulled from $msg_{lang}doc_desc)
#
: ${DOCSINSTALL_LANGS:=\
- bn da de el en es fr hu it ja ko mn nl pl pt ru tr zh_cn zh_tw \
+ bn da de el en es fr hu id it ja ko mn nl pl pt ru tr zh_cn zh_tw \
}
############################################################ GLOBALS
@@ -63,6 +63,7 @@ msg_frdoc_desc="French Documentation"
msg_freebsd_documentation_installation="$OSNAME Documentation Installation"
msg_freebsd_installer="$OSNAME Installer"
msg_hudoc_desc="Hungarian Documentation"
+msg_iddoc_desc="Indonesian Documentation"
msg_itdoc_desc="Italian Documentation"
msg_jadoc_desc="Japanese Documentation"
msg_kodoc_desc="Korean Documentation"
diff --git a/usr.sbin/bsdinstall/scripts/mount b/usr.sbin/bsdinstall/scripts/mount
index 2d7ce14fe045..94b649eaa31b 100755
--- a/usr.sbin/bsdinstall/scripts/mount
+++ b/usr.sbin/bsdinstall/scripts/mount
@@ -56,3 +56,9 @@ done
# User might want a shell and require devfs, so mount it
mkdir $BSDINSTALL_CHROOT/dev 2>/dev/null
mount -t devfs devfs $BSDINSTALL_CHROOT/dev
+
+# If installing from the DVD, mount packages where they'll be accessible
+if [ -d /packages ]; then
+ mkdir -p $BSDINSTALL_CHROOT/dist/packages
+ mount -t nullfs /packages $BSDINSTALL_CHROOT/dist/packages
+fi
diff --git a/usr.sbin/bsdinstall/scripts/rootpass b/usr.sbin/bsdinstall/scripts/rootpass
index 308c60e47a4c..f823c397c384 100755
--- a/usr.sbin/bsdinstall/scripts/rootpass
+++ b/usr.sbin/bsdinstall/scripts/rootpass
@@ -33,7 +33,13 @@ echo "$OSNAME Installer"
echo "========================"
echo
-echo "Please select a password for the system management account (root):"
-echo "Typed characters will not be visible."
+if [ -n "$ROOTPASS_ENC" ]; then
+ printf '%s\n' "$ROOTPASS_ENC" | pw -R $BSDINSTALL_CHROOT usermod root -H 0
+elif [ -n "$ROOTPASS_PLAIN" ]; then
+ printf '%s\n' "$ROOTPASS_PLAIN" | pw -R $BSDINSTALL_CHROOT usermod root -h 0
+else
+ echo "Please select a password for the system management account (root):"
+ echo "Typed characters will not be visible."
-chroot $BSDINSTALL_CHROOT passwd root 2>&1
+ chroot $BSDINSTALL_CHROOT passwd root 2>&1
+fi
diff --git a/usr.sbin/bsdinstall/scripts/umount b/usr.sbin/bsdinstall/scripts/umount
index 3c33eff7e105..a5d96ffffd32 100755
--- a/usr.sbin/bsdinstall/scripts/umount
+++ b/usr.sbin/bsdinstall/scripts/umount
@@ -26,6 +26,13 @@
#
# $FreeBSD$
+# If we mounted the DVD packages, unmount them
+if [ -d $BSDINSTALL_CHROOT/dist/packages ]; then
+ umount $BSDINSTALL_CHROOT/dist/packages
+ rmdir $BSDINSTALL_CHROOT/dist/packages
+ rmdir $BSDINSTALL_CHROOT/dist
+fi
+
TMP_FSTAB=${TMPDIR:-"/tmp"}/bsdinstall-tmp-fstab
cat $PATH_FSTAB | awk -v BSDINSTALL_CHROOT=$BSDINSTALL_CHROOT '{
diff --git a/usr.sbin/bsdinstall/scripts/zfsboot b/usr.sbin/bsdinstall/scripts/zfsboot
index a98cf4a33f28..1e771710cb7c 100755
--- a/usr.sbin/bsdinstall/scripts/zfsboot
+++ b/usr.sbin/bsdinstall/scripts/zfsboot
@@ -659,7 +659,7 @@ dialog_menu_layout()
while :; do
# Loop over list of available disks, resetting state
for disk in $disks; do
- f_isset _${disk}_status && _${disk}_status=
+ f_isset _${disk}_status && setvar _${disk}_status
done
# Loop over list of selected disks and create temporary
@@ -669,7 +669,7 @@ dialog_menu_layout()
$disk $DEVICE_TYPE_DISK disk
f_isset _${disk}_status ||
local _${disk}_status
- _${disk}_status=on
+ setvar _${disk}_status on
done
# Create the checklist menu of discovered disk devices
@@ -1473,9 +1473,18 @@ zfs_create_boot()
# Set canmount=noauto so that the default Boot Environment (BE) does
# not get mounted if a different BE is selected from the beastie menu
#
- f_dprintf "$funcname: Set canmount=noauto for the root of the pool..."
- f_eval_catch $funcname zfs "$ZFS_SET" "canmount=noauto" \
- "$zroot_name/$ZFSBOOT_BEROOT_NAME/$ZFSBOOT_BOOTFS_NAME"
+ f_dprintf "$funcname: Set canmount=noauto for any datasets under the BE..."
+ echo "$ZFSBOOT_DATASETS" | while read dataset options; do
+ # Skip blank lines and comments
+ case "$dataset" in "#"*|"") continue; esac
+ options="${options%%#*}"
+ #
+ case "$dataset" in "/$ZFSBOOT_BEROOT_NAME/$ZFSBOOT_BOOTFS_NAME"*)
+ f_eval_catch $funcname zfs "$ZFS_SET" "canmount=noauto" \
+ "$zroot_name$dataset" || return $FAILURE ;;
+ *) continue ;;
+ esac
+ done
# Last, but not least... required lines for rc.conf(5)/loader.conf(5)
# NOTE: We later concatenate these into their destination
diff --git a/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_swrun_tbl.c b/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_swrun_tbl.c
index f1a2f09e440f..4cb0fc323139 100644
--- a/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_swrun_tbl.c
+++ b/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_swrun_tbl.c
@@ -479,7 +479,7 @@ invalidate_swrun_entry(struct swrun_entry *entry, int commit)
if (entry->index >= NO_PID + 1) {
/* this is a kernel item */
- HRDBG("atempt to unload KLD %d",
+ HRDBG("attempt to unload KLD %d",
entry->index - NO_PID - 1);
if (entry->index == SWOSIndex) {
diff --git a/usr.sbin/certctl/certctl.8 b/usr.sbin/certctl/certctl.8
index 9af2adaba757..9e701cca66f4 100644
--- a/usr.sbin/certctl/certctl.8
+++ b/usr.sbin/certctl/certctl.8
@@ -26,7 +26,7 @@
.\"
.\" $FreeBSD$
.\"
-.Dd June 18, 2021
+.Dd July 13, 2022
.Dt CERTCTL 8
.Os
.Sh NAME
@@ -60,6 +60,8 @@ Flags:
.Bl -tag -width 4n
.It Fl D Ar destdir
Specify the DESTDIR (overriding values from the environment).
+.It Fl d Ar distbase
+Specify the DISTBASE (overriding values from the environment).
.It Fl M Ar metalog
Specify the path of the METALOG file (default: $DESTDIR/METALOG).
.It Fl n
@@ -96,25 +98,28 @@ Remove the specified file from the untrusted list.
.Bl -tag -width UNTRUSTDESTDIR
.It Ev DESTDIR
Alternate destination directory to operate on.
+.It Ev DISTBASE
+Additional path component to include when operating on certificate directories.
.It Ev TRUSTPATH
List of paths to search for trusted certificates.
Default:
-.Pa <DESTDIR>/usr/share/certs/trusted
-.Pa <DESTDIR>/usr/local/share/certs <DESTDIR>/usr/local/etc/ssl/certs
+.Pa <DESTDIR><DISTBASE>/usr/share/certs/trusted
+.Pa <DESTDIR><DISTBASE>/usr/local/share/certs
+.Pa <DESTDIR><DISTBASE>/usr/local/etc/ssl/certs
.It Ev UNTRUSTPATH
List of paths to search for untrusted certificates.
Default:
-.Pa <DESTDIR>/usr/share/certs/untrusted
-.Pa <DESTDIR>/usr/local/etc/ssl/untrusted
-.Pa <DESTDIR>/usr/local/etc/ssl/blacklisted
+.Pa <DESTDIR><DISTBASE>/usr/share/certs/untrusted
+.Pa <DESTDIR><DISTBASE>/usr/local/etc/ssl/untrusted
+.Pa <DESTDIR><DISTBASE>/usr/local/etc/ssl/blacklisted
.It Ev CERTDESTDIR
Destination directory for symbolic links to trusted certificates.
Default:
-.Pa <DESTDIR>/etc/ssl/certs
+.Pa <DESTDIR><DISTBASE>/etc/ssl/certs
.It Ev UNTRUSTDESTDIR
Destination directory for symbolic links to untrusted certificates.
Default:
-.Pa <DESTDIR>/etc/ssl/untrusted
+.Pa <DESTDIR><DISTBASE>/etc/ssl/untrusted
.It Ev EXTENSIONS
List of file extensions to read as certificate files.
Default: *.pem *.crt *.cer *.crl *.0
diff --git a/usr.sbin/certctl/certctl.sh b/usr.sbin/certctl/certctl.sh
index 327eaa6381a6..99fff8848188 100755
--- a/usr.sbin/certctl/certctl.sh
+++ b/usr.sbin/certctl/certctl.sh
@@ -30,6 +30,7 @@
############################################################ CONFIGURATION
: ${DESTDIR:=}
+: ${DISTBASE:=}
: ${FILEPAT:="\.pem$|\.crt$|\.cer$|\.crl$"}
: ${VERBOSE:=0}
@@ -254,7 +255,7 @@ usage()
echo " List trusted certificates"
echo " $SCRIPTNAME [-v] untrusted"
echo " List untrusted certificates"
- echo " $SCRIPTNAME [-nUv] [-D <destdir>] [-M <metalog>] rehash"
+ echo " $SCRIPTNAME [-nUv] [-D <destdir>] [-d <distbase>] [-M <metalog>] rehash"
echo " Generate hash links for all certificates"
echo " $SCRIPTNAME [-nv] untrust <file>"
echo " Add <file> to the list of untrusted certificates"
@@ -265,9 +266,10 @@ usage()
############################################################ MAIN
-while getopts D:M:nUv flag; do
+while getopts D:d:M:nUv flag; do
case "$flag" in
D) DESTDIR=${OPTARG} ;;
+ d) DISTBASE=${OPTARG} ;;
M) METALOG=${OPTARG} ;;
n) NOOP=1 ;;
U) UNPRIV=1 ;;
@@ -280,10 +282,10 @@ shift $(( $OPTIND - 1 ))
INSTALLFLAGS=
[ $UNPRIV -eq 1 ] && INSTALLFLAGS="-U -M ${METALOG} -D ${DESTDIR}"
: ${LOCALBASE:=$(sysctl -n user.localbase)}
-: ${TRUSTPATH:=${DESTDIR}/usr/share/certs/trusted:${DESTDIR}${LOCALBASE}/share/certs:${DESTDIR}${LOCALBASE}/etc/ssl/certs}
-: ${UNTRUSTPATH:=${DESTDIR}/usr/share/certs/untrusted:${DESTDIR}${LOCALBASE}/etc/ssl/untrusted:${DESTDIR}${LOCALBASE}/etc/ssl/blacklisted}
-: ${CERTDESTDIR:=${DESTDIR}/etc/ssl/certs}
-: ${UNTRUSTDESTDIR:=${DESTDIR}/etc/ssl/untrusted}
+: ${TRUSTPATH:=${DESTDIR}${DISTBASE}/usr/share/certs/trusted:${DESTDIR}${LOCALBASE}/share/certs:${DESTDIR}${LOCALBASE}/etc/ssl/certs}
+: ${UNTRUSTPATH:=${DESTDIR}${DISTBASE}/usr/share/certs/untrusted:${DESTDIR}${LOCALBASE}/etc/ssl/untrusted:${DESTDIR}${LOCALBASE}/etc/ssl/blacklisted}
+: ${CERTDESTDIR:=${DESTDIR}${DISTBASE}/etc/ssl/certs}
+: ${UNTRUSTDESTDIR:=${DESTDIR}${DISTBASE}/etc/ssl/untrusted}
[ $# -gt 0 ] || usage
case "$1" in
diff --git a/usr.sbin/crunch/crunchide/exec_elf32.c b/usr.sbin/crunch/crunchide/exec_elf32.c
index f7b8601911fe..b9af82e1e5e7 100644
--- a/usr.sbin/crunch/crunchide/exec_elf32.c
+++ b/usr.sbin/crunch/crunchide/exec_elf32.c
@@ -162,7 +162,7 @@ ELFNAMEEND(check)(int fd, const char *fn __unused)
unsigned char data;
/*
- * Check the header to maek sure it's an ELF file (of the
+ * Check the header to make sure it's an ELF file (of the
* appropriate size).
*/
if (fstat(fd, &sb) == -1)
diff --git a/usr.sbin/devinfo/devinfo.8 b/usr.sbin/devinfo/devinfo.8
index 091248250830..16ac9683b2e0 100644
--- a/usr.sbin/devinfo/devinfo.8
+++ b/usr.sbin/devinfo/devinfo.8
@@ -27,7 +27,7 @@
.\"
.\" $FreeBSD$
.\"
-.Dd November 1, 2021
+.Dd July 5, 2022
.Dt DEVINFO 8
.Os
.Sh NAME
@@ -73,6 +73,7 @@ back to the root of the device tree.
.Sh SEE ALSO
.Xr systat 1 ,
.Xr devinfo 3 ,
+.Xr devctl 8 ,
.Xr iostat 8 ,
.Xr pciconf 8 ,
.Xr vmstat 8 ,
diff --git a/usr.sbin/etcupdate/etcupdate.sh b/usr.sbin/etcupdate/etcupdate.sh
index 40868fe6586e..1da8fbf140d9 100755
--- a/usr.sbin/etcupdate/etcupdate.sh
+++ b/usr.sbin/etcupdate/etcupdate.sh
@@ -673,8 +673,9 @@ install_resolved()
return 1
fi
- log "cp -Rp ${CONFLICTS}$1 ${DESTDIR}$1"
- cp -Rp ${CONFLICTS}$1 ${DESTDIR}$1 >&3 2>&1
+ # Use cat rather than cp to preserve metadata
+ log "cat ${CONFLICTS}$1 > ${DESTDIR}$1"
+ cat ${CONFLICTS}$1 > ${DESTDIR}$1 2>&3
post_install_file $1
return 0
}
@@ -1612,6 +1613,18 @@ EOF
cat $WARNINGS
fi
+ # If this was a dryrun, remove the temporary tree if we built
+ # a new one.
+ if [ -n "$dryrun" ]; then
+ if [ -n "$dir" ]; then
+ if [ -n "$rerun" ]; then
+ panic "Should not have a temporary directory"
+ fi
+ remove_tree $dir
+ fi
+ return
+ fi
+
# Finally, rotate any needed trees.
if [ "$new" != "$NEWTREE" ]; then
if [ -n "$rerun" ]; then
diff --git a/usr.sbin/fstyp/hammer2_disk.h b/usr.sbin/fstyp/hammer2_disk.h
index c975cbd8309b..79891db08904 100644
--- a/usr.sbin/fstyp/hammer2_disk.h
+++ b/usr.sbin/fstyp/hammer2_disk.h
@@ -55,8 +55,8 @@ struct dmsg_hdr {
uint64_t reserved18; /* 18 */
uint32_t cmd; /* 20 flags | cmd | hdr_size / ALIGN */
- uint32_t aux_crc; /* 24 auxillary data crc */
- uint32_t aux_bytes; /* 28 auxillary data length (bytes) */
+ uint32_t aux_crc; /* 24 auxiliary data crc */
+ uint32_t aux_bytes; /* 28 auxiliary data length (bytes) */
uint32_t error; /* 2C error code or 0 */
uint64_t aux_descr; /* 30 negotiated OOB data descr */
uint32_t reserved38; /* 38 */
diff --git a/usr.sbin/fstyp/ufs.c b/usr.sbin/fstyp/ufs.c
index e4de1283e769..69ce90d600f2 100644
--- a/usr.sbin/fstyp/ufs.c
+++ b/usr.sbin/fstyp/ufs.c
@@ -50,11 +50,9 @@ fstyp_ufs(FILE *fp, char *label, size_t labelsize)
{
struct fs *fs;
- switch (sbget(fileno(fp), &fs, STDSB)) {
+ switch (sbget(fileno(fp), &fs, UFS_STDSB, UFS_NOCSUM)) {
case 0:
strlcpy(label, fs->fs_volname, labelsize);
- free(fs->fs_csp);
- free(fs->fs_si);
free(fs);
return (0);
case ENOENT:
diff --git a/usr.sbin/jail/jail.8 b/usr.sbin/jail/jail.8
index 61fe91cf973e..c1eee3226593 100644
--- a/usr.sbin/jail/jail.8
+++ b/usr.sbin/jail/jail.8
@@ -25,7 +25,7 @@
.\"
.\" $FreeBSD$
.\"
-.Dd April 4, 2021
+.Dd July 18, 2022
.Dt JAIL 8
.Os
.Sh NAME
@@ -58,7 +58,8 @@
.Op Fl U Ar username
.Op Fl n Ar jailname
.Op Fl s Ar securelevel
-.Ar path hostname ip Ns [ Ns Ar ,... Ns ] Ar command ...
+.Ar path hostname ip Ns Op Cm \&, Ns Ar ...
+.Ar command ...
.Nm
.Op Fl f Ar conf_file
.Fl e
@@ -941,7 +942,7 @@ To set up a jail directory tree containing an entire
distribution, the following
.Xr sh 1
command script can be used:
-.Bd -literal
+.Bd -literal -offset indent
D=/here/is/the/jail
cd /usr/src
mkdir -p $D
@@ -1021,11 +1022,6 @@ To configure
it is necessary to modify
.Pa /etc/mail/sendmail.cf .
.Pp
-For
-.Xr named 8 ,
-it is necessary to modify
-.Pa /etc/namedb/named.conf .
-.Pp
In addition, a number of services must be recompiled in order to run
them in the host environment.
This includes most applications providing services using
@@ -1200,9 +1196,11 @@ the intended use of the jail, you may also want to run
.Pa /etc/rc.shutdown
from within the jail.
.Pp
-To shut down the jail from the outside, simply remove it with
-.Nm
-.Ar -r ,
+To shut down the jail from the outside, simply remove it with:
+.Bd -literal -offset indent
+jail -r
+.Ed
+.Pp
which will run any commands specified by
.Va exec.stop ,
and then send
@@ -1361,7 +1359,6 @@ environment of the first jail.
.Xr jexec 8 ,
.Xr jls 8 ,
.Xr mount 8 ,
-.Xr named 8 ,
.Xr reboot 8 ,
.Xr rpcbind 8 ,
.Xr sendmail 8 ,
diff --git a/usr.sbin/jail/jail.conf.5 b/usr.sbin/jail/jail.conf.5
index ce414b96463e..67277e6d78ce 100644
--- a/usr.sbin/jail/jail.conf.5
+++ b/usr.sbin/jail/jail.conf.5
@@ -24,7 +24,7 @@
.\"
.\" $FreeBSD$
.\"
-.Dd April 18, 2021
+.Dd July 8, 2022
.Dt JAIL.CONF 5
.Os
.Sh NAME
@@ -178,6 +178,13 @@ shell formats:
.Pp
Comments are legal wherever whitespace is allowed, i.e. anywhere except
in the middle of a string or a token.
+.Sh FILES
+.Bl -tag -width "indent" -compact
+.It Pa /etc/jail.conf
+.It Pa /etc/jail.*.conf
+.It Pa /etc/jail.conf.d/*.conf
+.It Pa /usr/share/examples/jails/
+.El
.Sh EXAMPLES
.Bd -literal
# Typical static defaults:
diff --git a/usr.sbin/makefs/Makefile b/usr.sbin/makefs/Makefile
index 3fea648f9383..fe472d7e7309 100644
--- a/usr.sbin/makefs/Makefile
+++ b/usr.sbin/makefs/Makefile
@@ -19,6 +19,17 @@ MAN= makefs.8
NO_WCAST_ALIGN=
CSTD= c11
+.if ${MK_ZFS} != "no"
+SRCS+= zfs.c
+CFLAGS+=-I${SRCDIR}/zfs \
+ -I${SRCTOP}/stand/libsa \
+ -I${SRCTOP}/sys/cddl/boot
+
+CFLAGS+= -DHAVE_ZFS
+
+.include "${SRCDIR}/zfs/Makefile.inc"
+.endif
+
.include "${SRCDIR}/cd9660/Makefile.inc"
.include "${SRCDIR}/ffs/Makefile.inc"
.include "${SRCDIR}/msdos/Makefile.inc"
diff --git a/usr.sbin/makefs/makefs.8 b/usr.sbin/makefs/makefs.8
index fdf8d532b69f..464583eab3a1 100644
--- a/usr.sbin/makefs/makefs.8
+++ b/usr.sbin/makefs/makefs.8
@@ -35,7 +35,7 @@
.\"
.\" $FreeBSD$
.\"
-.Dd September 17, 2020
+.Dd August 5, 2022
.Dt MAKEFS 8
.Os
.Sh NAME
@@ -266,6 +266,8 @@ BSD fast file system (default).
ISO 9660 file system.
.It Sy msdos
FAT12, FAT16, or FAT32 file system.
+.It Sy zfs
+ZFS pool containing one or more file systems.
.El
.It Fl x
Exclude file system nodes not explicitly listed in the specfile.
@@ -494,10 +496,97 @@ Volume ID.
.It Cm volume_label
Volume Label.
.El
+.Ss zfs-specific options
+Note: ZFS support is currently considered experimental.
+Do not use it for anything critical.
+.Pp
+The image created by
+.Nm
+contains a ZFS pool with a single vdev of type
+.Ql disk .
+The root dataset is always created implicitly and contains the entire input
+directory tree unless additional datasets are specified using the options
+described below.
+.Pp
+The arguments consist of a keyword, an equal sign
+.Pq Ql = ,
+and a value.
+The following keywords are supported:
+.Pp
+.Bl -tag -width omit-trailing-period -offset indent -compact
+.It ashift
+The base-2 logarithm of the minimum block size.
+Typical values are 9 (512B blocks) and 12 (4KB blocks).
+The default value is 12.
+.It bootfs
+The name of the bootable dataset for the pool.
+Specifying this option causes the
+.Ql bootfs
+property to be set in the created pool.
+.It mssize
+The size of metaslabs in the created pool.
+By default,
+.Nm
+allocates large (up to 512MB) metaslabs with the expectation that
+the image will be auto-expanded upon first use.
+This option allows the default heuristic to be overridden.
+.It poolname
+The name of the ZFS pool.
+This option must be specified.
+.It rootpath
+An implicit path prefix added to dataset mountpoints.
+By default it is
+.Pa /<poolname> .
+For creating bootable pools, the
+.Va rootpath
+should be set to
+.Pa / .
+At least one dataset must have a mountpoint equal to
+.Va rootpath .
+.It fs
+Create an additional dataset.
+This option may be specified multiple times.
+The argument value must be of the form
+.Ar <dataset>[;<prop1=v1>[;<prop2=v2>[;...]]] ,
+where
+.Ar dataset
+is the name of the dataset and must belong to the pool's namespace.
+For example, with a pool name of
+.Ql test
+all dataset names must be prefixed by
+.Ql test/ .
+A dataset must exist at each level of the pool's namespace.
+For example, to create
+.Ql test/foo/bar ,
+.Ql test/foo
+must be created as well.
+.Pp
+The dataset mountpoints determine how the datasets are populated with
+files from the staged directory tree.
+Conceptually, all datasets are mounted before any are populated with files.
+The root of the staged directory tree is mapped to
+.Va rootpath .
+.Pp
+Dataset properties, as described in
+.Xr zfsprops 8 ,
+may be specified following the dataset name.
+The following properties may be set for a dataset:
+.Pp
+.Bl -tag -compact -offset indent
+.It atime
+.It canmount
+.It exec
+.It mountpoint
+.It setuid
+.El
+.El
.Sh SEE ALSO
.Xr mtree 5 ,
.Xr mtree 8 ,
-.Xr newfs 8
+.Xr newfs 8 ,
+.Xr zfsconcepts 8 ,
+.Xr zfsprops 8 ,
+.Xr zpoolprops 8
.Sh HISTORY
The
.Nm
@@ -518,4 +607,6 @@ and first appeared in
.An Ram Vedam
(cd9660 support),
.An Christos Zoulas
-(msdos support).
+(msdos support),
+.An Mark Johnston
+(zfs support).
diff --git a/usr.sbin/makefs/makefs.c b/usr.sbin/makefs/makefs.c
index 888a2b3edea7..2a50768d3152 100644
--- a/usr.sbin/makefs/makefs.c
+++ b/usr.sbin/makefs/makefs.c
@@ -77,6 +77,9 @@ static fstype_t fstypes[] = {
ENTRY(cd9660),
ENTRY(ffs),
ENTRY(msdos),
+#ifdef HAVE_ZFS
+ ENTRY(zfs),
+#endif
{ .type = NULL },
};
diff --git a/usr.sbin/makefs/makefs.h b/usr.sbin/makefs/makefs.h
index 68dc0362dd21..e88313e8366d 100644
--- a/usr.sbin/makefs/makefs.h
+++ b/usr.sbin/makefs/makefs.h
@@ -78,12 +78,14 @@ enum fi_flags {
FI_SIZED = 1<<0, /* inode sized */
FI_ALLOCATED = 1<<1, /* fsinode->ino allocated */
FI_WRITTEN = 1<<2, /* inode written */
+ FI_ROOT = 1<<3, /* root of a ZFS dataset */
};
typedef struct {
uint32_t ino; /* inode number used on target fs */
uint32_t nlink; /* number of links to this entry */
enum fi_flags flags; /* flags used by fs specific code */
+ void *param; /* for use by individual fs impls */
struct stat st; /* stat entry */
} fsinode;
@@ -186,6 +188,9 @@ void fs ## _makefs(const char *, const char *, fsnode *, fsinfo_t *)
DECLARE_FUN(cd9660);
DECLARE_FUN(ffs);
DECLARE_FUN(msdos);
+#ifdef HAVE_ZFS
+DECLARE_FUN(zfs);
+#endif
extern u_int debug;
extern int dupsok;
diff --git a/usr.sbin/makefs/tests/Makefile b/usr.sbin/makefs/tests/Makefile
index 85e4b233aea7..c2c9f6bea5b6 100644
--- a/usr.sbin/makefs/tests/Makefile
+++ b/usr.sbin/makefs/tests/Makefile
@@ -2,6 +2,7 @@
ATF_TESTS_SH+= makefs_cd9660_tests
ATF_TESTS_SH+= makefs_ffs_tests
+ATF_TESTS_SH+= makefs_zfs_tests
BINDIR= ${TESTSDIR}
diff --git a/usr.sbin/makefs/tests/makefs_zfs_tests.sh b/usr.sbin/makefs/tests/makefs_zfs_tests.sh
new file mode 100644
index 000000000000..8cd79966c49a
--- /dev/null
+++ b/usr.sbin/makefs/tests/makefs_zfs_tests.sh
@@ -0,0 +1,634 @@
+#-
+# SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+#
+# Copyright (c) 2022 The FreeBSD Foundation
+#
+# This software was developed by Mark Johnston under sponsorship from
+# the FreeBSD Foundation.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+
+MAKEFS="makefs -t zfs -o nowarn=true"
+ZFS_POOL_NAME="makefstest$$"
+TEST_ZFS_POOL_NAME="$TMPDIR/poolname"
+
+. "$(dirname "$0")/makefs_tests_common.sh"
+
+common_cleanup()
+{
+ local pool md
+
+ # Try to force a TXG, this can help catch bugs by triggering a panic.
+ sync
+
+ pool=$(cat $TEST_ZFS_POOL_NAME)
+ if zpool list "$pool" >/dev/null; then
+ zpool destroy "$pool"
+ fi
+
+ md=$(cat $TEST_MD_DEVICE_FILE)
+ if [ -c /dev/"$md" ]; then
+ mdconfig -d -u "$md"
+ fi
+}
+
+import_image()
+{
+ atf_check -e empty -o save:$TEST_MD_DEVICE_FILE -s exit:0 \
+ mdconfig -a -f $TEST_IMAGE
+ atf_check zpool import -R $TEST_MOUNT_DIR $ZFS_POOL_NAME
+ echo "$ZFS_POOL_NAME" > $TEST_ZFS_POOL_NAME
+}
+
+#
+# Test autoexpansion of the vdev.
+#
+# The pool is initially 10GB, so we get 10GB minus one metaslab's worth of
+# usable space for data. Then the pool is expanded to 50GB, and the amount of
+# usable space is 50GB minus one metaslab.
+#
+atf_test_case autoexpand cleanup
+autoexpand_body()
+{
+ local mssize poolsize poolsize1 newpoolsize
+
+ create_test_inputs
+
+ mssize=$((128 * 1024 * 1024))
+ poolsize=$((10 * 1024 * 1024 * 1024))
+ atf_check $MAKEFS -s $poolsize -o mssize=$mssize -o rootpath=/ \
+ -o poolname=$ZFS_POOL_NAME \
+ $TEST_IMAGE $TEST_INPUTS_DIR
+
+ newpoolsize=$((50 * 1024 * 1024 * 1024))
+ truncate -s $newpoolsize $TEST_IMAGE
+
+ import_image
+
+ check_image_contents
+
+ poolsize1=$(zpool list -Hp -o size $ZFS_POOL_NAME)
+ atf_check [ $((poolsize1 + $mssize)) -eq $poolsize ]
+
+ atf_check zpool online -e $ZFS_POOL_NAME /dev/$(cat $TEST_MD_DEVICE_FILE)
+
+ check_image_contents
+
+ poolsize1=$(zpool list -Hp -o size $ZFS_POOL_NAME)
+ atf_check [ $((poolsize1 + $mssize)) -eq $newpoolsize ]
+}
+autoexpand_cleanup()
+{
+ common_cleanup
+}
+
+#
+# Test with some default layout defined by the common code.
+#
+atf_test_case basic cleanup
+basic_body()
+{
+ create_test_inputs
+
+ atf_check $MAKEFS -s 10g -o rootpath=/ -o poolname=$ZFS_POOL_NAME \
+ $TEST_IMAGE $TEST_INPUTS_DIR
+
+ import_image
+
+ check_image_contents
+}
+basic_cleanup()
+{
+ common_cleanup
+}
+
+atf_test_case dataset_removal cleanup
+dataset_removal_body()
+{
+ create_test_dirs
+
+ cd $TEST_INPUTS_DIR
+ mkdir dir
+ cd -
+
+ atf_check $MAKEFS -s 1g -o rootpath=/ -o poolname=$ZFS_POOL_NAME \
+ -o fs=${ZFS_POOL_NAME}/dir \
+ $TEST_IMAGE $TEST_INPUTS_DIR
+
+ import_image
+
+ check_image_contents
+
+ atf_check zfs destroy ${ZFS_POOL_NAME}/dir
+}
+dataset_removal_cleanup()
+{
+ common_cleanup
+}
+
+#
+# Make sure that we can create and remove an empty directory.
+#
+atf_test_case empty_dir cleanup
+empty_dir_body()
+{
+ create_test_dirs
+
+ cd $TEST_INPUTS_DIR
+ mkdir dir
+ cd -
+
+ atf_check $MAKEFS -s 10g -o rootpath=/ -o poolname=$ZFS_POOL_NAME \
+ $TEST_IMAGE $TEST_INPUTS_DIR
+
+ import_image
+
+ check_image_contents
+
+ atf_check rmdir ${TEST_MOUNT_DIR}/dir
+}
+empty_dir_cleanup()
+{
+ common_cleanup
+}
+
+atf_test_case empty_fs cleanup
+empty_fs_body()
+{
+ create_test_dirs
+
+ atf_check $MAKEFS -s 10g -o rootpath=/ -o poolname=$ZFS_POOL_NAME \
+ $TEST_IMAGE $TEST_INPUTS_DIR
+
+ import_image
+
+ check_image_contents
+}
+empty_fs_cleanup()
+{
+ common_cleanup
+}
+
+atf_test_case file_sizes cleanup
+file_sizes_body()
+{
+ local i
+
+ create_test_dirs
+ cd $TEST_INPUTS_DIR
+
+ i=1
+ while [ $i -lt $((1 << 20)) ]; do
+ truncate -s $i ${i}.1
+ truncate -s $(($i - 1)) ${i}.2
+ truncate -s $(($i + 1)) ${i}.3
+ i=$(($i << 1))
+ done
+
+ cd -
+
+ # XXXMJ this creates sparse files, make sure makefs doesn't
+ # preserve the sparseness.
+ # XXXMJ need to test with larger files (at least 128MB for L2 indirs)
+ atf_check $MAKEFS -s 10g -o rootpath=/ -o poolname=$ZFS_POOL_NAME \
+ $TEST_IMAGE $TEST_INPUTS_DIR
+
+ import_image
+
+ check_image_contents
+}
+file_sizes_cleanup()
+{
+ common_cleanup
+}
+
+atf_test_case hard_links cleanup
+hard_links_body()
+{
+ local f
+
+ create_test_dirs
+ cd $TEST_INPUTS_DIR
+
+ mkdir dir
+ echo "hello" > 1
+ ln 1 2
+ ln 1 dir/1
+
+ echo "goodbye" > dir/a
+ ln dir/a dir/b
+ ln dir/a a
+
+ cd -
+
+ atf_check $MAKEFS -s 10g -o rootpath=/ -o poolname=$ZFS_POOL_NAME \
+ $TEST_IMAGE $TEST_INPUTS_DIR
+
+ import_image
+
+ check_image_contents
+
+ stat -f '%i' ${TEST_MOUNT_DIR}/1 > ./ino
+ stat -f '%l' ${TEST_MOUNT_DIR}/1 > ./nlink
+ for f in 1 2 dir/1; do
+ atf_check -o file:./nlink -e empty -s exit:0 \
+ stat -f '%l' ${TEST_MOUNT_DIR}/${f}
+ atf_check -o file:./ino -e empty -s exit:0 \
+ stat -f '%i' ${TEST_MOUNT_DIR}/${f}
+ atf_check cmp -s ${TEST_INPUTS_DIR}/1 ${TEST_MOUNT_DIR}/${f}
+ done
+
+ stat -f '%i' ${TEST_MOUNT_DIR}/dir/a > ./ino
+ stat -f '%l' ${TEST_MOUNT_DIR}/dir/a > ./nlink
+ for f in dir/a dir/b a; do
+ atf_check -o file:./nlink -e empty -s exit:0 \
+ stat -f '%l' ${TEST_MOUNT_DIR}/${f}
+ atf_check -o file:./ino -e empty -s exit:0 \
+ stat -f '%i' ${TEST_MOUNT_DIR}/${f}
+ atf_check cmp -s ${TEST_INPUTS_DIR}/dir/a ${TEST_MOUNT_DIR}/${f}
+ done
+}
+hard_links_cleanup()
+{
+ common_cleanup
+}
+
+# Allocate enough dnodes from an object set that the meta dnode needs to use
+# indirect blocks.
+atf_test_case indirect_dnode_array cleanup
+indirect_dnode_array_body()
+{
+ local count i
+
+ # How many dnodes do we need to allocate? Well, the data block size
+ # for meta dnodes is always 16KB, so with a dnode size of 512B we get
+ # 32 dnodes per direct block. The maximum indirect block size is 128KB
+ # and that can fit 1024 block pointers, so we need at least 32 * 1024
+ # files to force the use of two levels of indirection.
+ #
+ # Unfortunately that number of files makes the test run quite slowly,
+ # so we settle for a single indirect block for now...
+ count=$(jot -r 1 32 1024)
+
+ create_test_dirs
+ cd $TEST_INPUTS_DIR
+ for i in $(seq 1 $count); do
+ touch $i
+ done
+ cd -
+
+ atf_check $MAKEFS -s 10g -o rootpath=/ -o poolname=$ZFS_POOL_NAME \
+ $TEST_IMAGE $TEST_INPUTS_DIR
+
+ import_image
+
+ check_image_contents
+}
+indirect_dnode_array_cleanup()
+{
+ common_cleanup
+}
+
+#
+# Create some files with long names, so as to test fat ZAP handling.
+#
+atf_test_case long_file_name cleanup
+long_file_name_body()
+{
+ local dir i
+
+ create_test_dirs
+ cd $TEST_INPUTS_DIR
+
+ # micro ZAP keys can be at most 50 bytes.
+ for i in $(seq 1 60); do
+ touch $(jot -s '' $i 1 1)
+ done
+ dir=$(jot -s '' 61 1 1)
+ mkdir $dir
+ for i in $(seq 1 60); do
+ touch ${dir}/$(jot -s '' $i 1 1)
+ done
+
+ cd -
+
+ atf_check $MAKEFS -s 10g -o rootpath=/ -o poolname=$ZFS_POOL_NAME \
+ $TEST_IMAGE $TEST_INPUTS_DIR
+
+ import_image
+
+ check_image_contents
+
+ # Add a directory entry in the hope that OpenZFS might catch a bug
+ # in makefs' fat ZAP encoding.
+ touch ${TEST_MOUNT_DIR}/foo
+}
+long_file_name_cleanup()
+{
+ common_cleanup
+}
+
+#
+# Exercise handling of multiple datasets.
+#
+atf_test_case multi_dataset_1 cleanup
+multi_dataset_1_body()
+{
+ create_test_dirs
+ cd $TEST_INPUTS_DIR
+
+ mkdir dir1
+ echo a > dir1/a
+ mkdir dir2
+ echo b > dir2/b
+
+ cd -
+
+ atf_check $MAKEFS -s 1g -o rootpath=/ -o poolname=$ZFS_POOL_NAME \
+ -o fs=${ZFS_POOL_NAME}/dir1 -o fs=${ZFS_POOL_NAME}/dir2 \
+ $TEST_IMAGE $TEST_INPUTS_DIR
+
+ import_image
+
+ check_image_contents
+
+ # Make sure that we have three datasets with the expected mount points.
+ atf_check -o inline:${ZFS_POOL_NAME}\\n -e empty -s exit:0 \
+ zfs list -H -o name ${ZFS_POOL_NAME}
+ atf_check -o inline:${TEST_MOUNT_DIR}\\n -e empty -s exit:0 \
+ zfs list -H -o mountpoint ${ZFS_POOL_NAME}
+
+ atf_check -o inline:${ZFS_POOL_NAME}/dir1\\n -e empty -s exit:0 \
+ zfs list -H -o name ${ZFS_POOL_NAME}/dir1
+ atf_check -o inline:${TEST_MOUNT_DIR}/dir1\\n -e empty -s exit:0 \
+ zfs list -H -o mountpoint ${ZFS_POOL_NAME}/dir1
+
+ atf_check -o inline:${ZFS_POOL_NAME}/dir2\\n -e empty -s exit:0 \
+ zfs list -H -o name ${ZFS_POOL_NAME}/dir2
+ atf_check -o inline:${TEST_MOUNT_DIR}/dir2\\n -e empty -s exit:0 \
+ zfs list -H -o mountpoint ${ZFS_POOL_NAME}/dir2
+}
+multi_dataset_1_cleanup()
+{
+ common_cleanup
+}
+
+#
+# Create a pool with two datasets, where the root dataset is mounted below
+# the child dataset.
+#
+atf_test_case multi_dataset_2 cleanup
+multi_dataset_2_body()
+{
+ create_test_dirs
+ cd $TEST_INPUTS_DIR
+
+ mkdir dir1
+ echo a > dir1/a
+ mkdir dir2
+ echo b > dir2/b
+
+ cd -
+
+ atf_check $MAKEFS -s 1g -o rootpath=/ -o poolname=$ZFS_POOL_NAME \
+ -o fs=${ZFS_POOL_NAME}/dir1\;mountpoint=/ \
+ -o fs=${ZFS_POOL_NAME}\;mountpoint=/dir1 \
+ $TEST_IMAGE $TEST_INPUTS_DIR
+
+ import_image
+
+ check_image_contents
+}
+multi_dataset_2_cleanup()
+{
+ common_cleanup
+}
+
+#
+# Create a dataset with a non-existent mount point.
+#
+atf_test_case multi_dataset_3 cleanup
+multi_dataset_3_body()
+{
+ create_test_dirs
+ cd $TEST_INPUTS_DIR
+
+ mkdir dir1
+ echo a > dir1/a
+
+ cd -
+
+ atf_check $MAKEFS -s 1g -o rootpath=/ -o poolname=$ZFS_POOL_NAME \
+ -o fs=${ZFS_POOL_NAME}/dir1 \
+ -o fs=${ZFS_POOL_NAME}/dir2 \
+ $TEST_IMAGE $TEST_INPUTS_DIR
+
+ import_image
+
+ atf_check -o inline:${TEST_MOUNT_DIR}/dir2\\n -e empty -s exit:0 \
+ zfs list -H -o mountpoint ${ZFS_POOL_NAME}/dir2
+
+ # Mounting dir2 should have created a directory called dir2. Go
+ # back and create it in the staging tree before comparing.
+ atf_check mkdir ${TEST_INPUTS_DIR}/dir2
+
+ check_image_contents
+}
+multi_dataset_3_cleanup()
+{
+ common_cleanup
+}
+
+#
+# Create an unmounted dataset.
+#
+atf_test_case multi_dataset_4 cleanup
+multi_dataset_4_body()
+{
+ create_test_dirs
+ cd $TEST_INPUTS_DIR
+
+ mkdir dir1
+ echo a > dir1/a
+
+ cd -
+
+ atf_check $MAKEFS -s 1g -o rootpath=/ -o poolname=$ZFS_POOL_NAME \
+ -o fs=${ZFS_POOL_NAME}/dir1\;canmount=noauto\;mountpoint=none \
+ $TEST_IMAGE $TEST_INPUTS_DIR
+
+ import_image
+
+ atf_check -o inline:none\\n -e empty -s exit:0 \
+ zfs list -H -o mountpoint ${ZFS_POOL_NAME}/dir1
+
+ check_image_contents
+
+ atf_check zfs set mountpoint=/dir1 ${ZFS_POOL_NAME}/dir1
+ atf_check zfs mount ${ZFS_POOL_NAME}/dir1
+ atf_check -o inline:${TEST_MOUNT_DIR}/dir1\\n -e empty -s exit:0 \
+ zfs list -H -o mountpoint ${ZFS_POOL_NAME}/dir1
+
+ # dir1/a should be part of the root dataset, not dir1.
+ atf_check -s not-exit:0 -e not-empty stat ${TEST_MOUNT_DIR}dir1/a
+}
+multi_dataset_4_cleanup()
+{
+ common_cleanup
+}
+
+#
+# Rudimentary test to verify that two ZFS images created using the same
+# parameters and input hierarchy are byte-identical. In particular, makefs(1)
+# does not preserve file access times.
+#
+atf_test_case reproducible cleanup
+reproducible_body()
+{
+ create_test_inputs
+
+ atf_check $MAKEFS -s 512m -o rootpath=/ -o poolname=$ZFS_POOL_NAME \
+ ${TEST_IMAGE}.1 $TEST_INPUTS_DIR
+
+ atf_check $MAKEFS -s 512m -o rootpath=/ -o poolname=$ZFS_POOL_NAME \
+ ${TEST_IMAGE}.2 $TEST_INPUTS_DIR
+
+ # XXX-MJ cmp(1) is really slow
+ atf_check cmp ${TEST_IMAGE}.1 ${TEST_IMAGE}.2
+}
+reproducible_cleanup()
+{
+}
+
+#
+# Verify that we can take a snapshot of a generated dataset.
+#
+atf_test_case snapshot cleanup
+snapshot_body()
+{
+ create_test_dirs
+ cd $TEST_INPUTS_DIR
+
+ mkdir dir
+ echo "hello" > dir/hello
+ echo "goodbye" > goodbye
+
+ cd -
+
+ atf_check $MAKEFS -s 10g -o rootpath=/ -o poolname=$ZFS_POOL_NAME \
+ $TEST_IMAGE $TEST_INPUTS_DIR
+
+ import_image
+
+ atf_check zfs snapshot ${ZFS_POOL_NAME}@1
+}
+snapshot_cleanup()
+{
+ common_cleanup
+}
+
+#
+# Check handling of symbolic links.
+#
+atf_test_case soft_links cleanup
+soft_links_body()
+{
+ create_test_dirs
+ cd $TEST_INPUTS_DIR
+
+ mkdir dir
+ ln -s a a
+ ln -s dir/../a a
+ ln -s dir/b b
+ echo 'c' > dir
+ ln -s dir/c c
+ # XXX-MJ overflows bonus buffer ln -s $(jot -s '' 320 1 1) 1
+
+ cd -
+
+ atf_check $MAKEFS -s 10g -o rootpath=/ -o poolname=$ZFS_POOL_NAME \
+ $TEST_IMAGE $TEST_INPUTS_DIR
+
+ import_image
+
+ check_image_contents
+}
+soft_links_cleanup()
+{
+ common_cleanup
+}
+
+#
+# Verify that we can set properties on the root dataset.
+#
+atf_test_case root_props cleanup
+root_props_body()
+{
+ create_test_inputs
+
+ atf_check $MAKEFS -s 10g -o rootpath=/ -o poolname=$ZFS_POOL_NAME \
+ -o fs=${ZFS_POOL_NAME}\;atime=off\;setuid=off \
+ $TEST_IMAGE $TEST_INPUTS_DIR
+
+ import_image
+
+ check_image_contents
+
+ atf_check -o inline:off\\n -e empty -s exit:0 \
+ zfs get -H -o value atime $ZFS_POOL_NAME
+ atf_check -o inline:local\\n -e empty -s exit:0 \
+ zfs get -H -o source atime $ZFS_POOL_NAME
+ atf_check -o inline:off\\n -e empty -s exit:0 \
+ zfs get -H -o value setuid $ZFS_POOL_NAME
+ atf_check -o inline:local\\n -e empty -s exit:0 \
+ zfs get -H -o source setuid $ZFS_POOL_NAME
+}
+root_props_cleanup()
+{
+ common_cleanup
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case autoexpand
+ atf_add_test_case basic
+ atf_add_test_case dataset_removal
+ atf_add_test_case empty_dir
+ atf_add_test_case empty_fs
+ atf_add_test_case file_sizes
+ atf_add_test_case hard_links
+ atf_add_test_case indirect_dnode_array
+ atf_add_test_case long_file_name
+ atf_add_test_case multi_dataset_1
+ atf_add_test_case multi_dataset_2
+ atf_add_test_case multi_dataset_3
+ atf_add_test_case multi_dataset_4
+ atf_add_test_case reproducible
+ atf_add_test_case snapshot
+ atf_add_test_case soft_links
+ atf_add_test_case root_props
+
+ # XXXMJ tests:
+ # - test with different ashifts (at least, 9 and 12), different image sizes
+ # - create datasets in imported pool
+}
diff --git a/usr.sbin/makefs/zfs.c b/usr.sbin/makefs/zfs.c
new file mode 100644
index 000000000000..08689a558870
--- /dev/null
+++ b/usr.sbin/makefs/zfs.c
@@ -0,0 +1,758 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2022 The FreeBSD Foundation
+ *
+ * This software was developed by Mark Johnston under sponsorship from
+ * the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/errno.h>
+#include <sys/queue.h>
+
+#include <assert.h>
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <util.h>
+
+#include "makefs.h"
+#include "zfs.h"
+
+#define VDEV_LABEL_SPACE \
+ ((off_t)(VDEV_LABEL_START_SIZE + VDEV_LABEL_END_SIZE))
+_Static_assert(VDEV_LABEL_SPACE <= MINDEVSIZE, "");
+
+#define MINMSSIZE ((off_t)1 << 24) /* 16MB */
+#define DFLTMSSIZE ((off_t)1 << 29) /* 512MB */
+#define MAXMSSIZE ((off_t)1 << 34) /* 16GB */
+
+#define INDIR_LEVELS 6
+/* Indirect blocks are always 128KB. */
+#define BLKPTR_PER_INDIR (MAXBLOCKSIZE / sizeof(blkptr_t))
+
+struct dnode_cursor {
+ char inddir[INDIR_LEVELS][MAXBLOCKSIZE];
+ off_t indloc;
+ off_t indspace;
+ dnode_phys_t *dnode;
+ off_t dataoff;
+ off_t datablksz;
+};
+
+void
+zfs_prep_opts(fsinfo_t *fsopts)
+{
+ zfs_opt_t *zfs = ecalloc(1, sizeof(*zfs));
+
+ const option_t zfs_options[] = {
+ { '\0', "bootfs", &zfs->bootfs, OPT_STRPTR,
+ 0, 0, "Bootable dataset" },
+ { '\0', "mssize", &zfs->mssize, OPT_INT64,
+ MINMSSIZE, MAXMSSIZE, "Metaslab size" },
+ { '\0', "poolname", &zfs->poolname, OPT_STRPTR,
+ 0, 0, "ZFS pool name" },
+ { '\0', "rootpath", &zfs->rootpath, OPT_STRPTR,
+ 0, 0, "Prefix for all dataset mount points" },
+ { '\0', "ashift", &zfs->ashift, OPT_INT32,
+ MINBLOCKSHIFT, MAXBLOCKSHIFT, "ZFS pool ashift" },
+ { '\0', "nowarn", &zfs->nowarn, OPT_BOOL,
+ 0, 0, "Suppress warning about experimental ZFS support" },
+ { .name = NULL }
+ };
+
+ STAILQ_INIT(&zfs->datasetdescs);
+
+ fsopts->fs_specific = zfs;
+ fsopts->fs_options = copy_opts(zfs_options);
+}
+
+int
+zfs_parse_opts(const char *option, fsinfo_t *fsopts)
+{
+ zfs_opt_t *zfs;
+ struct dataset_desc *dsdesc;
+ char buf[BUFSIZ], *opt, *val;
+ int rv;
+
+ zfs = fsopts->fs_specific;
+
+ opt = val = estrdup(option);
+ opt = strsep(&val, "=");
+ if (strcmp(opt, "fs") == 0) {
+ if (val == NULL)
+ errx(1, "invalid filesystem parameters `%s'", option);
+
+ /*
+ * Dataset descriptions will be parsed later, in dsl_init().
+ * Just stash them away for now.
+ */
+ dsdesc = ecalloc(1, sizeof(*dsdesc));
+ dsdesc->params = estrdup(val);
+ free(opt);
+ STAILQ_INSERT_TAIL(&zfs->datasetdescs, dsdesc, next);
+ return (1);
+ }
+ free(opt);
+
+ rv = set_option(fsopts->fs_options, option, buf, sizeof(buf));
+ return (rv == -1 ? 0 : 1);
+}
+
+static void
+zfs_size_vdev(fsinfo_t *fsopts)
+{
+ zfs_opt_t *zfs;
+ off_t asize, mssize, vdevsize, vdevsize1;
+
+ zfs = fsopts->fs_specific;
+
+ assert(fsopts->maxsize != 0);
+ assert(zfs->ashift != 0);
+
+ /*
+ * Figure out how big the vdev should be.
+ */
+ vdevsize = rounddown2(fsopts->maxsize, 1 << zfs->ashift);
+ if (vdevsize < MINDEVSIZE)
+ errx(1, "maximum image size is too small");
+ if (vdevsize < fsopts->minsize || vdevsize > fsopts->maxsize) {
+ errx(1, "image size bounds must be multiples of %d",
+ 1 << zfs->ashift);
+ }
+ asize = vdevsize - VDEV_LABEL_SPACE;
+
+ /*
+ * Size metaslabs according to the following heuristic:
+ * - provide at least 8 metaslabs,
+ * - without using a metaslab size larger than 512MB.
+ * This approximates what OpenZFS does without being complicated. In
+ * practice we expect pools to be expanded upon first use, and OpenZFS
+ * does not resize metaslabs in that case, so there is no right answer
+ * here. In general we want to provide large metaslabs even if the
+ * image size is small, and 512MB is a reasonable size for pools up to
+ * several hundred gigabytes.
+ *
+ * The user may override this heuristic using the "-o mssize" option.
+ */
+ mssize = zfs->mssize;
+ if (mssize == 0) {
+ mssize = MAX(MIN(asize / 8, DFLTMSSIZE), MINMSSIZE);
+ if (!powerof2(mssize))
+ mssize = 1l << (flsll(mssize) - 1);
+ }
+ if (!powerof2(mssize))
+ errx(1, "metaslab size must be a power of 2");
+
+ /*
+ * If we have some slop left over, try to cover it by resizing the vdev,
+ * subject to the maxsize and minsize parameters.
+ */
+ if (asize % mssize != 0) {
+ vdevsize1 = rounddown2(asize, mssize) + VDEV_LABEL_SPACE;
+ if (vdevsize1 < fsopts->minsize)
+ vdevsize1 = roundup2(asize, mssize) + VDEV_LABEL_SPACE;
+ if (vdevsize1 <= fsopts->maxsize)
+ vdevsize = vdevsize1;
+ }
+ asize = vdevsize - VDEV_LABEL_SPACE;
+
+ zfs->asize = asize;
+ zfs->vdevsize = vdevsize;
+ zfs->mssize = mssize;
+ zfs->msshift = flsll(mssize) - 1;
+ zfs->mscount = asize / mssize;
+}
+
+/*
+ * Validate options and set some default values.
+ */
+static void
+zfs_check_opts(fsinfo_t *fsopts)
+{
+ zfs_opt_t *zfs;
+
+ zfs = fsopts->fs_specific;
+
+ if (fsopts->offset != 0)
+ errx(1, "unhandled offset option");
+ if (fsopts->maxsize == 0)
+ errx(1, "an image size must be specified");
+
+ if (zfs->poolname == NULL)
+ errx(1, "a pool name must be specified");
+
+ if (zfs->rootpath == NULL)
+ easprintf(&zfs->rootpath, "/%s", zfs->poolname);
+ if (zfs->rootpath[0] != '/')
+ errx(1, "mountpoint `%s' must be absolute", zfs->rootpath);
+
+ if (zfs->ashift == 0)
+ zfs->ashift = 12;
+
+ zfs_size_vdev(fsopts);
+}
+
+void
+zfs_cleanup_opts(fsinfo_t *fsopts)
+{
+ struct dataset_desc *d, *tmp;
+ zfs_opt_t *zfs;
+
+ zfs = fsopts->fs_specific;
+ free(zfs->rootpath);
+ free(zfs->bootfs);
+ free(__DECONST(void *, zfs->poolname));
+ STAILQ_FOREACH_SAFE(d, &zfs->datasetdescs, next, tmp) {
+ free(d->params);
+ free(d);
+ }
+ free(zfs);
+ free(fsopts->fs_options);
+}
+
+static size_t
+nvlist_size(const nvlist_t *nvl)
+{
+ return (sizeof(nvl->nv_header) + nvl->nv_size);
+}
+
+static void
+nvlist_copy(const nvlist_t *nvl, char *buf, size_t sz)
+{
+ assert(sz >= nvlist_size(nvl));
+
+ memcpy(buf, &nvl->nv_header, sizeof(nvl->nv_header));
+ memcpy(buf + sizeof(nvl->nv_header), nvl->nv_data, nvl->nv_size);
+}
+
+static nvlist_t *
+pool_config_nvcreate(zfs_opt_t *zfs)
+{
+ nvlist_t *featuresnv, *poolnv;
+
+ poolnv = nvlist_create(NV_UNIQUE_NAME);
+ nvlist_add_uint64(poolnv, ZPOOL_CONFIG_POOL_TXG, TXG);
+ nvlist_add_uint64(poolnv, ZPOOL_CONFIG_VERSION, SPA_VERSION);
+ nvlist_add_uint64(poolnv, ZPOOL_CONFIG_POOL_STATE, POOL_STATE_EXPORTED);
+ nvlist_add_string(poolnv, ZPOOL_CONFIG_POOL_NAME, zfs->poolname);
+ nvlist_add_uint64(poolnv, ZPOOL_CONFIG_POOL_GUID, zfs->poolguid);
+ nvlist_add_uint64(poolnv, ZPOOL_CONFIG_TOP_GUID, zfs->vdevguid);
+ nvlist_add_uint64(poolnv, ZPOOL_CONFIG_GUID, zfs->vdevguid);
+ nvlist_add_uint64(poolnv, ZPOOL_CONFIG_VDEV_CHILDREN, 1);
+
+ featuresnv = nvlist_create(NV_UNIQUE_NAME);
+ nvlist_add_nvlist(poolnv, ZPOOL_CONFIG_FEATURES_FOR_READ, featuresnv);
+ nvlist_destroy(featuresnv);
+
+ return (poolnv);
+}
+
+static nvlist_t *
+pool_disk_vdev_config_nvcreate(zfs_opt_t *zfs)
+{
+ nvlist_t *diskvdevnv;
+
+ assert(zfs->objarrid != 0);
+
+ diskvdevnv = nvlist_create(NV_UNIQUE_NAME);
+ nvlist_add_string(diskvdevnv, ZPOOL_CONFIG_TYPE, VDEV_TYPE_DISK);
+ nvlist_add_uint64(diskvdevnv, ZPOOL_CONFIG_ASHIFT, zfs->ashift);
+ nvlist_add_uint64(diskvdevnv, ZPOOL_CONFIG_ASIZE, zfs->asize);
+ nvlist_add_uint64(diskvdevnv, ZPOOL_CONFIG_GUID, zfs->vdevguid);
+ nvlist_add_uint64(diskvdevnv, ZPOOL_CONFIG_ID, 0);
+ nvlist_add_string(diskvdevnv, ZPOOL_CONFIG_PATH, "/dev/null");
+ nvlist_add_uint64(diskvdevnv, ZPOOL_CONFIG_WHOLE_DISK, 1);
+ nvlist_add_uint64(diskvdevnv, ZPOOL_CONFIG_CREATE_TXG, TXG);
+ nvlist_add_uint64(diskvdevnv, ZPOOL_CONFIG_METASLAB_ARRAY,
+ zfs->objarrid);
+ nvlist_add_uint64(diskvdevnv, ZPOOL_CONFIG_METASLAB_SHIFT,
+ zfs->msshift);
+
+ return (diskvdevnv);
+}
+
+static nvlist_t *
+pool_root_vdev_config_nvcreate(zfs_opt_t *zfs)
+{
+ nvlist_t *diskvdevnv, *rootvdevnv;
+
+ diskvdevnv = pool_disk_vdev_config_nvcreate(zfs);
+ rootvdevnv = nvlist_create(NV_UNIQUE_NAME);
+
+ nvlist_add_uint64(rootvdevnv, ZPOOL_CONFIG_ID, 0);
+ nvlist_add_uint64(rootvdevnv, ZPOOL_CONFIG_GUID, zfs->poolguid);
+ nvlist_add_string(rootvdevnv, ZPOOL_CONFIG_TYPE, VDEV_TYPE_ROOT);
+ nvlist_add_uint64(rootvdevnv, ZPOOL_CONFIG_CREATE_TXG, TXG);
+ nvlist_add_nvlist_array(rootvdevnv, ZPOOL_CONFIG_CHILDREN, &diskvdevnv,
+ 1);
+ nvlist_destroy(diskvdevnv);
+
+ return (rootvdevnv);
+}
+
+/*
+ * Create the pool's "config" object, which contains an nvlist describing pool
+ * parameters and the vdev topology. It is similar but not identical to the
+ * nvlist stored in vdev labels. The main difference is that vdev labels do not
+ * describe the full vdev tree and in particular do not contain the "root"
+ * meta-vdev.
+ */
+static void
+pool_init_objdir_config(zfs_opt_t *zfs, zfs_zap_t *objdir)
+{
+ dnode_phys_t *dnode;
+ nvlist_t *poolconfig, *vdevconfig;
+ void *configbuf;
+ uint64_t dnid;
+ off_t configloc, configblksz;
+ int error;
+
+ dnode = objset_dnode_bonus_alloc(zfs->mos, DMU_OT_PACKED_NVLIST,
+ DMU_OT_PACKED_NVLIST_SIZE, sizeof(uint64_t), &dnid);
+
+ poolconfig = pool_config_nvcreate(zfs);
+
+ vdevconfig = pool_root_vdev_config_nvcreate(zfs);
+ nvlist_add_nvlist(poolconfig, ZPOOL_CONFIG_VDEV_TREE, vdevconfig);
+ nvlist_destroy(vdevconfig);
+
+ error = nvlist_export(poolconfig);
+ if (error != 0)
+ errc(1, error, "nvlist_export");
+
+ configblksz = nvlist_size(poolconfig);
+ configloc = objset_space_alloc(zfs, zfs->mos, &configblksz);
+ configbuf = ecalloc(1, configblksz);
+ nvlist_copy(poolconfig, configbuf, configblksz);
+
+ vdev_pwrite_dnode_data(zfs, dnode, configbuf, configblksz, configloc);
+
+ dnode->dn_datablkszsec = configblksz >> MINBLOCKSHIFT;
+ dnode->dn_flags = DNODE_FLAG_USED_BYTES;
+ *(uint64_t *)DN_BONUS(dnode) = nvlist_size(poolconfig);
+
+ zap_add_uint64(objdir, DMU_POOL_CONFIG, dnid);
+
+ nvlist_destroy(poolconfig);
+ free(configbuf);
+}
+
+/*
+ * Add objects block pointer list objects, used for deferred frees. We don't do
+ * anything with them, but they need to be present or OpenZFS will refuse to
+ * import the pool.
+ */
+static void
+pool_init_objdir_bplists(zfs_opt_t *zfs __unused, zfs_zap_t *objdir)
+{
+ uint64_t dnid;
+
+ (void)objset_dnode_bonus_alloc(zfs->mos, DMU_OT_BPOBJ, DMU_OT_BPOBJ_HDR,
+ BPOBJ_SIZE_V2, &dnid);
+ zap_add_uint64(objdir, DMU_POOL_FREE_BPOBJ, dnid);
+
+ (void)objset_dnode_bonus_alloc(zfs->mos, DMU_OT_BPOBJ, DMU_OT_BPOBJ_HDR,
+ BPOBJ_SIZE_V2, &dnid);
+ zap_add_uint64(objdir, DMU_POOL_SYNC_BPLIST, dnid);
+}
+
+/*
+ * Add required feature metadata objects. We don't know anything about ZFS
+ * features, so the objects are just empty ZAPs.
+ */
+static void
+pool_init_objdir_feature_maps(zfs_opt_t *zfs, zfs_zap_t *objdir)
+{
+ dnode_phys_t *dnode;
+ uint64_t dnid;
+
+ dnode = objset_dnode_alloc(zfs->mos, DMU_OTN_ZAP_METADATA, &dnid);
+ zap_add_uint64(objdir, DMU_POOL_FEATURES_FOR_READ, dnid);
+ zap_write(zfs, zap_alloc(zfs->mos, dnode));
+
+ dnode = objset_dnode_alloc(zfs->mos, DMU_OTN_ZAP_METADATA, &dnid);
+ zap_add_uint64(objdir, DMU_POOL_FEATURES_FOR_WRITE, dnid);
+ zap_write(zfs, zap_alloc(zfs->mos, dnode));
+
+ dnode = objset_dnode_alloc(zfs->mos, DMU_OTN_ZAP_METADATA, &dnid);
+ zap_add_uint64(objdir, DMU_POOL_FEATURE_DESCRIPTIONS, dnid);
+ zap_write(zfs, zap_alloc(zfs->mos, dnode));
+}
+
+static void
+pool_init_objdir_dsl(zfs_opt_t *zfs, zfs_zap_t *objdir)
+{
+ zap_add_uint64(objdir, DMU_POOL_ROOT_DATASET,
+ dsl_dir_id(zfs->rootdsldir));
+}
+
+static void
+pool_init_objdir_poolprops(zfs_opt_t *zfs, zfs_zap_t *objdir)
+{
+ dnode_phys_t *dnode;
+ uint64_t id;
+
+ dnode = objset_dnode_alloc(zfs->mos, DMU_OT_POOL_PROPS, &id);
+ zap_add_uint64(objdir, DMU_POOL_PROPS, id);
+
+ zfs->poolprops = zap_alloc(zfs->mos, dnode);
+}
+
+/*
+ * Initialize the MOS object directory, the root of virtually all of the pool's
+ * data and metadata.
+ */
+static void
+pool_init_objdir(zfs_opt_t *zfs)
+{
+ zfs_zap_t *zap;
+ dnode_phys_t *objdir;
+
+ objdir = objset_dnode_lookup(zfs->mos, DMU_POOL_DIRECTORY_OBJECT);
+
+ zap = zap_alloc(zfs->mos, objdir);
+ pool_init_objdir_config(zfs, zap);
+ pool_init_objdir_bplists(zfs, zap);
+ pool_init_objdir_feature_maps(zfs, zap);
+ pool_init_objdir_dsl(zfs, zap);
+ pool_init_objdir_poolprops(zfs, zap);
+ zap_write(zfs, zap);
+}
+
+/*
+ * Initialize the meta-object set (MOS) and immediately write out several
+ * special objects whose contents are already finalized, including the object
+ * directory.
+ *
+ * Once the MOS is finalized, it'll look roughly like this:
+ *
+ * object directory (ZAP)
+ * |-> vdev config object (nvlist)
+ * |-> features for read
+ * |-> features for write
+ * |-> feature descriptions
+ * |-> sync bplist
+ * |-> free bplist
+ * |-> pool properties
+ * L-> root DSL directory
+ * |-> DSL child directory (ZAP)
+ * | |-> $MOS (DSL dir)
+ * | | |-> child map
+ * | | L-> props (ZAP)
+ * | |-> $FREE (DSL dir)
+ * | | |-> child map
+ * | | L-> props (ZAP)
+ * | |-> $ORIGIN (DSL dir)
+ * | | |-> child map
+ * | | |-> dataset
+ * | | | L-> deadlist
+ * | | |-> snapshot
+ * | | | |-> deadlist
+ * | | | L-> snapshot names
+ * | | |-> props (ZAP)
+ * | | L-> clones (ZAP)
+ * | |-> dataset 1 (DSL dir)
+ * | | |-> DSL dataset
+ * | | | |-> snapshot names
+ * | | | L-> deadlist
+ * | | |-> child map
+ * | | | L-> ...
+ * | | L-> props
+ * | |-> dataset 2
+ * | | L-> ...
+ * | |-> ...
+ * | L-> dataset n
+ * |-> DSL root dataset
+ * | |-> snapshot names
+ * | L-> deadlist
+ * L-> props (ZAP)
+ * space map object array
+ * |-> space map 1
+ * |-> space map 2
+ * |-> ...
+ * L-> space map n (zfs->mscount)
+ *
+ * The space map object array is pointed to by the "msarray" property in the
+ * pool configuration.
+ */
+static void
+pool_init(zfs_opt_t *zfs)
+{
+ uint64_t dnid;
+
+ zfs->poolguid = ((uint64_t)random() << 32) | random();
+ zfs->vdevguid = ((uint64_t)random() << 32) | random();
+
+ zfs->mos = objset_alloc(zfs, DMU_OST_META);
+
+ (void)objset_dnode_alloc(zfs->mos, DMU_OT_OBJECT_DIRECTORY, &dnid);
+ assert(dnid == DMU_POOL_DIRECTORY_OBJECT);
+
+ (void)objset_dnode_alloc(zfs->mos, DMU_OT_OBJECT_ARRAY, &zfs->objarrid);
+
+ dsl_init(zfs);
+
+ pool_init_objdir(zfs);
+}
+
+static void
+pool_labels_write(zfs_opt_t *zfs)
+{
+ uberblock_t *ub;
+ vdev_label_t *label;
+ nvlist_t *poolconfig, *vdevconfig;
+ int error;
+
+ label = ecalloc(1, sizeof(*label));
+
+ /*
+ * Assemble the vdev configuration and store it in the label.
+ */
+ poolconfig = pool_config_nvcreate(zfs);
+ vdevconfig = pool_disk_vdev_config_nvcreate(zfs);
+ nvlist_add_nvlist(poolconfig, ZPOOL_CONFIG_VDEV_TREE, vdevconfig);
+ nvlist_destroy(vdevconfig);
+
+ error = nvlist_export(poolconfig);
+ if (error != 0)
+ errc(1, error, "nvlist_export");
+ nvlist_copy(poolconfig, label->vl_vdev_phys.vp_nvlist,
+ sizeof(label->vl_vdev_phys.vp_nvlist));
+ nvlist_destroy(poolconfig);
+
+ /*
+ * Fill out the uberblock. Just make each one the same. The embedded
+ * checksum is calculated in vdev_label_write().
+ */
+ for (size_t uoff = 0; uoff < sizeof(label->vl_uberblock);
+ uoff += (1 << zfs->ashift)) {
+ ub = (uberblock_t *)(&label->vl_uberblock[0] + uoff);
+ ub->ub_magic = UBERBLOCK_MAGIC;
+ ub->ub_version = SPA_VERSION;
+ ub->ub_txg = TXG;
+ ub->ub_guid_sum = zfs->poolguid + zfs->vdevguid;
+ ub->ub_timestamp = 0;
+
+ ub->ub_software_version = SPA_VERSION;
+ ub->ub_mmp_magic = MMP_MAGIC;
+ ub->ub_mmp_delay = 0;
+ ub->ub_mmp_config = 0;
+ ub->ub_checkpoint_txg = 0;
+ objset_root_blkptr_copy(zfs->mos, &ub->ub_rootbp);
+ }
+
+ /*
+ * Write out four copies of the label: two at the beginning of the vdev
+ * and two at the end.
+ */
+ for (int i = 0; i < VDEV_LABELS; i++)
+ vdev_label_write(zfs, i, label);
+
+ free(label);
+}
+
+static void
+pool_fini(zfs_opt_t *zfs)
+{
+ zap_write(zfs, zfs->poolprops);
+ dsl_write(zfs);
+ objset_write(zfs, zfs->mos);
+ pool_labels_write(zfs);
+}
+
+struct dnode_cursor *
+dnode_cursor_init(zfs_opt_t *zfs, zfs_objset_t *os, dnode_phys_t *dnode,
+ off_t size, off_t blksz)
+{
+ struct dnode_cursor *c;
+ uint64_t nbppindir, indlevel, ndatablks, nindblks;
+
+ assert(dnode->dn_nblkptr == 1);
+ assert(blksz <= MAXBLOCKSIZE);
+
+ if (blksz == 0) {
+ /* Must be between 1<<ashift and 128KB. */
+ blksz = MIN(MAXBLOCKSIZE, MAX(1 << zfs->ashift,
+ powerof2(size) ? size : (1ul << flsll(size))));
+ }
+ assert(powerof2(blksz));
+
+ /*
+ * Do we need indirect blocks? Figure out how many levels are needed
+ * (indlevel == 1 means no indirect blocks) and how much space is needed
+ * (it has to be allocated up-front to break the dependency cycle
+ * described in objset_write()).
+ */
+ ndatablks = size == 0 ? 0 : howmany(size, blksz);
+ nindblks = 0;
+ for (indlevel = 1, nbppindir = 1; ndatablks > nbppindir; indlevel++) {
+ nbppindir *= BLKPTR_PER_INDIR;
+ nindblks += howmany(ndatablks, indlevel * nbppindir);
+ }
+ assert(indlevel < INDIR_LEVELS);
+
+ dnode->dn_nlevels = (uint8_t)indlevel;
+ dnode->dn_maxblkid = ndatablks > 0 ? ndatablks - 1 : 0;
+ dnode->dn_datablkszsec = blksz >> MINBLOCKSHIFT;
+
+ c = ecalloc(1, sizeof(*c));
+ if (nindblks > 0) {
+ c->indspace = nindblks * MAXBLOCKSIZE;
+ c->indloc = objset_space_alloc(zfs, os, &c->indspace);
+ }
+ c->dnode = dnode;
+ c->dataoff = 0;
+ c->datablksz = blksz;
+
+ return (c);
+}
+
+static void
+_dnode_cursor_flush(zfs_opt_t *zfs, struct dnode_cursor *c, int levels)
+{
+ blkptr_t *bp, *pbp;
+ void *buf;
+ uint64_t fill;
+ off_t blkid, blksz, loc;
+
+ assert(levels > 0);
+ assert(levels <= c->dnode->dn_nlevels - 1);
+
+ blksz = MAXBLOCKSIZE;
+ blkid = (c->dataoff / c->datablksz) / BLKPTR_PER_INDIR;
+ for (int level = 1; level <= levels; level++) {
+ buf = c->inddir[level - 1];
+
+ if (level == c->dnode->dn_nlevels - 1) {
+ pbp = &c->dnode->dn_blkptr[0];
+ } else {
+ uint64_t iblkid;
+
+ iblkid = blkid & (BLKPTR_PER_INDIR - 1);
+ pbp = (blkptr_t *)
+ &c->inddir[level][iblkid * sizeof(blkptr_t)];
+ }
+
+ /*
+ * Space for indirect blocks is allocated up-front; see the
+ * comment in objset_write().
+ */
+ loc = c->indloc;
+ c->indloc += blksz;
+ assert(c->indspace >= blksz);
+ c->indspace -= blksz;
+
+ bp = buf;
+ fill = 0;
+ for (size_t i = 0; i < BLKPTR_PER_INDIR; i++)
+ fill += BP_GET_FILL(&bp[i]);
+
+ vdev_pwrite_dnode_indir(zfs, c->dnode, level, fill, buf, blksz,
+ loc, pbp);
+ memset(buf, 0, MAXBLOCKSIZE);
+
+ blkid /= BLKPTR_PER_INDIR;
+ }
+}
+
+blkptr_t *
+dnode_cursor_next(zfs_opt_t *zfs, struct dnode_cursor *c, off_t off)
+{
+ off_t blkid, l1id;
+ int levels;
+
+ if (c->dnode->dn_nlevels == 1) {
+ assert(off < MAXBLOCKSIZE);
+ return (&c->dnode->dn_blkptr[0]);
+ }
+
+ assert(off % c->datablksz == 0);
+
+ /* Do we need to flush any full indirect blocks? */
+ if (off > 0) {
+ blkid = off / c->datablksz;
+ for (levels = 0; levels < c->dnode->dn_nlevels - 1; levels++) {
+ if (blkid % BLKPTR_PER_INDIR != 0)
+ break;
+ blkid /= BLKPTR_PER_INDIR;
+ }
+ if (levels > 0)
+ _dnode_cursor_flush(zfs, c, levels);
+ }
+
+ c->dataoff = off;
+ l1id = (off / c->datablksz) & (BLKPTR_PER_INDIR - 1);
+ return ((blkptr_t *)&c->inddir[0][l1id * sizeof(blkptr_t)]);
+}
+
+void
+dnode_cursor_finish(zfs_opt_t *zfs, struct dnode_cursor *c)
+{
+ int levels;
+
+ levels = c->dnode->dn_nlevels - 1;
+ if (levels > 0)
+ _dnode_cursor_flush(zfs, c, levels);
+ assert(c->indspace == 0);
+ free(c);
+}
+
+void
+zfs_makefs(const char *image, const char *dir, fsnode *root, fsinfo_t *fsopts)
+{
+ zfs_opt_t *zfs;
+ int dirfd;
+
+ zfs = fsopts->fs_specific;
+
+ /*
+ * Use a fixed seed to provide reproducible pseudo-random numbers for
+ * on-disk structures when needed (e.g., GUIDs, ZAP hash salts).
+ */
+ srandom(1729);
+
+ zfs_check_opts(fsopts);
+
+ if (!zfs->nowarn) {
+ fprintf(stderr,
+ "ZFS support is currently considered experimental. "
+ "Do not use it for anything critical.\n");
+ }
+
+ dirfd = open(dir, O_DIRECTORY | O_RDONLY);
+ if (dirfd < 0)
+ err(1, "open(%s)", dir);
+
+ vdev_init(zfs, image);
+ pool_init(zfs);
+ fs_build(zfs, dirfd, root);
+ pool_fini(zfs);
+ vdev_fini(zfs);
+}
diff --git a/usr.sbin/makefs/zfs/Makefile.inc b/usr.sbin/makefs/zfs/Makefile.inc
new file mode 100644
index 000000000000..bebe8c322035
--- /dev/null
+++ b/usr.sbin/makefs/zfs/Makefile.inc
@@ -0,0 +1,12 @@
+.PATH: ${SRCDIR}/zfs
+.PATH: ${SRCTOP}/stand/libsa/zfs
+
+SRCS+= dsl.c \
+ fs.c \
+ objset.c \
+ vdev.c \
+ zap.c
+
+SRCS+= nvlist.c
+
+CFLAGS.nvlist.c+= -I${SRCTOP}/stand/libsa -Wno-cast-qual
diff --git a/usr.sbin/makefs/zfs/dsl.c b/usr.sbin/makefs/zfs/dsl.c
new file mode 100644
index 000000000000..5f473e557c02
--- /dev/null
+++ b/usr.sbin/makefs/zfs/dsl.c
@@ -0,0 +1,598 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2022 The FreeBSD Foundation
+ *
+ * This software was developed by Mark Johnston under sponsorship from
+ * the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <assert.h>
+#include <string.h>
+
+#include <util.h>
+
+#include "makefs.h"
+#include "zfs.h"
+
+typedef struct zfs_dsl_dataset {
+ zfs_objset_t *os; /* referenced objset, may be null */
+ dsl_dataset_phys_t *phys; /* on-disk representation */
+ uint64_t dsid; /* DSL dataset dnode */
+
+ struct zfs_dsl_dir *dir; /* containing parent */
+} zfs_dsl_dataset_t;
+
+typedef STAILQ_HEAD(zfs_dsl_dir_list, zfs_dsl_dir) zfs_dsl_dir_list_t;
+
+typedef struct zfs_dsl_dir {
+ char *fullname; /* full dataset name */
+ char *name; /* basename(fullname) */
+ dsl_dir_phys_t *phys; /* on-disk representation */
+ nvlist_t *propsnv; /* properties saved in propszap */
+
+ zfs_dsl_dataset_t *headds; /* principal dataset, may be null */
+
+ uint64_t dirid; /* DSL directory dnode */
+ zfs_zap_t *propszap; /* dataset properties */
+ zfs_zap_t *childzap; /* child directories */
+
+ /* DSL directory tree linkage. */
+ struct zfs_dsl_dir *parent;
+ zfs_dsl_dir_list_t children;
+ STAILQ_ENTRY(zfs_dsl_dir) next;
+} zfs_dsl_dir_t;
+
+static zfs_dsl_dir_t *dsl_dir_alloc(zfs_opt_t *zfs, const char *name);
+static zfs_dsl_dataset_t *dsl_dataset_alloc(zfs_opt_t *zfs, zfs_dsl_dir_t *dir);
+
+static int
+nvlist_find_string(nvlist_t *nvl, const char *key, char **retp)
+{
+ char *str;
+ int error, len;
+
+ error = nvlist_find(nvl, key, DATA_TYPE_STRING, NULL, &str, &len);
+ if (error == 0) {
+ *retp = ecalloc(1, len + 1);
+ memcpy(*retp, str, len);
+ }
+ return (error);
+}
+
+static int
+nvlist_find_uint64(nvlist_t *nvl, const char *key, uint64_t *retp)
+{
+ return (nvlist_find(nvl, key, DATA_TYPE_UINT64, NULL, retp, NULL));
+}
+
+/*
+ * Return an allocated string containing the head dataset's mountpoint,
+ * including the root path prefix.
+ *
+ * If the dataset has a mountpoint property, it is returned. Otherwise we have
+ * to follow ZFS' inheritance rules.
+ */
+char *
+dsl_dir_get_mountpoint(zfs_opt_t *zfs, zfs_dsl_dir_t *dir)
+{
+ zfs_dsl_dir_t *pdir;
+ char *mountpoint, *origmountpoint;
+
+ if (nvlist_find_string(dir->propsnv, "mountpoint", &mountpoint) == 0) {
+ if (strcmp(mountpoint, "none") == 0)
+ return (NULL);
+
+ /*
+ * nvlist_find_string() does not make a copy.
+ */
+ mountpoint = estrdup(mountpoint);
+ } else {
+ /*
+ * If we don't have a mountpoint, it's inherited from one of our
+ * ancestors. Walk up the hierarchy until we find it, building
+ * up our mountpoint along the way. The mountpoint property is
+ * always set for the root dataset.
+ */
+ for (pdir = dir->parent, mountpoint = estrdup(dir->name);;) {
+ origmountpoint = mountpoint;
+
+ if (nvlist_find_string(pdir->propsnv, "mountpoint",
+ &mountpoint) == 0) {
+ easprintf(&mountpoint, "%s%s%s", mountpoint,
+ mountpoint[strlen(mountpoint) - 1] == '/' ?
+ "" : "/", origmountpoint);
+ free(origmountpoint);
+ break;
+ }
+
+ easprintf(&mountpoint, "%s/%s", pdir->name,
+ origmountpoint);
+ free(origmountpoint);
+ pdir = pdir->parent;
+ }
+ }
+ assert(mountpoint[0] == '/');
+ assert(strstr(mountpoint, zfs->rootpath) == mountpoint);
+
+ return (mountpoint);
+}
+
+int
+dsl_dir_get_canmount(zfs_dsl_dir_t *dir, uint64_t *canmountp)
+{
+ return (nvlist_find_uint64(dir->propsnv, "canmount", canmountp));
+}
+
+/*
+ * Handle dataset properties that we know about; stash them into an nvlist to be
+ * written later to the properties ZAP object.
+ *
+ * If the set of properties we handle grows too much, we should probably explore
+ * using libzfs to manage them.
+ */
+static void
+dsl_dir_set_prop(zfs_opt_t *zfs, zfs_dsl_dir_t *dir, const char *key,
+ const char *val)
+{
+ nvlist_t *nvl;
+
+ nvl = dir->propsnv;
+ if (val == NULL || val[0] == '\0')
+ errx(1, "missing value for property `%s'", key);
+ if (nvpair_find(nvl, key) != NULL)
+ errx(1, "property `%s' already set", key);
+
+ if (strcmp(key, "mountpoint") == 0) {
+ if (strcmp(val, "none") != 0) {
+ if (val[0] != '/')
+ errx(1, "mountpoint `%s' is not absolute", val);
+ if (strcmp(val, zfs->rootpath) != 0 &&
+ strcmp(zfs->rootpath, "/") != 0 &&
+ (strstr(val, zfs->rootpath) != val ||
+ val[strlen(zfs->rootpath)] != '/')) {
+ errx(1, "mountpoint `%s' is not prefixed by "
+ "the root path `%s'", val, zfs->rootpath);
+ }
+ }
+ nvlist_add_string(nvl, key, val);
+ } else if (strcmp(key, "atime") == 0 || strcmp(key, "exec") == 0 ||
+ strcmp(key, "setuid") == 0) {
+ if (strcmp(val, "on") == 0)
+ nvlist_add_uint64(nvl, key, 1);
+ else if (strcmp(val, "off") == 0)
+ nvlist_add_uint64(nvl, key, 0);
+ else
+ errx(1, "invalid value `%s' for %s", val, key);
+ } else if (strcmp(key, "canmount") == 0) {
+ if (strcmp(val, "noauto") == 0)
+ nvlist_add_uint64(nvl, key, 2);
+ else if (strcmp(val, "on") == 0)
+ nvlist_add_uint64(nvl, key, 1);
+ else if (strcmp(val, "off") == 0)
+ nvlist_add_uint64(nvl, key, 0);
+ else
+ errx(1, "invalid value `%s' for %s", val, key);
+ } else {
+ errx(1, "unknown property `%s'", key);
+ }
+}
+
+static zfs_dsl_dir_t *
+dsl_metadir_alloc(zfs_opt_t *zfs, const char *name)
+{
+ zfs_dsl_dir_t *dir;
+ char *path;
+
+ easprintf(&path, "%s/%s", zfs->poolname, name);
+ dir = dsl_dir_alloc(zfs, path);
+ free(path);
+ return (dir);
+}
+
+static void
+dsl_origindir_init(zfs_opt_t *zfs)
+{
+ dnode_phys_t *clones;
+ uint64_t clonesid;
+
+ zfs->origindsldir = dsl_metadir_alloc(zfs, "$ORIGIN");
+ zfs->originds = dsl_dataset_alloc(zfs, zfs->origindsldir);
+ zfs->snapds = dsl_dataset_alloc(zfs, zfs->origindsldir);
+
+ clones = objset_dnode_alloc(zfs->mos, DMU_OT_DSL_CLONES, &clonesid);
+ zfs->cloneszap = zap_alloc(zfs->mos, clones);
+ zfs->origindsldir->phys->dd_clones = clonesid;
+}
+
+void
+dsl_init(zfs_opt_t *zfs)
+{
+ zfs_dsl_dir_t *dir;
+ struct dataset_desc *d;
+ const char *dspropdelim;
+
+ dspropdelim = ";";
+
+ zfs->rootdsldir = dsl_dir_alloc(zfs, NULL);
+
+ nvlist_add_uint64(zfs->rootdsldir->propsnv, "compression",
+ ZIO_COMPRESS_OFF);
+
+ zfs->rootds = dsl_dataset_alloc(zfs, zfs->rootdsldir);
+ zfs->rootdsldir->headds = zfs->rootds;
+
+ zfs->mosdsldir = dsl_metadir_alloc(zfs, "$MOS");
+ zfs->freedsldir = dsl_metadir_alloc(zfs, "$FREE");
+ dsl_origindir_init(zfs);
+
+ /*
+ * Go through the list of user-specified datasets and create DSL objects
+ * for them.
+ */
+ STAILQ_FOREACH(d, &zfs->datasetdescs, next) {
+ char *dsname, *next, *params, *param, *nextparam;
+
+ params = d->params;
+ dsname = strsep(&params, dspropdelim);
+
+ if (strcmp(dsname, zfs->poolname) == 0) {
+ /*
+ * This is the root dataset; it's already created, so
+ * we're just setting options.
+ */
+ dir = zfs->rootdsldir;
+ } else {
+ /*
+ * This dataset must be a child of the root dataset.
+ */
+ if (strstr(dsname, zfs->poolname) != dsname ||
+ (next = strchr(dsname, '/')) == NULL ||
+ (size_t)(next - dsname) != strlen(zfs->poolname)) {
+ errx(1, "dataset `%s' must be a child of `%s'",
+ dsname, zfs->poolname);
+ }
+ dir = dsl_dir_alloc(zfs, dsname);
+ dir->headds = dsl_dataset_alloc(zfs, dir);
+ }
+
+ for (nextparam = param = params; nextparam != NULL;) {
+ char *key, *val;
+
+ param = strsep(&nextparam, dspropdelim);
+
+ key = val = param;
+ key = strsep(&val, "=");
+ dsl_dir_set_prop(zfs, dir, key, val);
+ }
+ }
+
+ /*
+ * Set the root dataset's mount point if the user didn't override the
+ * default.
+ */
+ if (nvpair_find(zfs->rootdsldir->propsnv, "mountpoint") == NULL) {
+ nvlist_add_string(zfs->rootdsldir->propsnv, "mountpoint",
+ zfs->rootpath);
+ }
+}
+
+uint64_t
+dsl_dir_id(zfs_dsl_dir_t *dir)
+{
+ return (dir->dirid);
+}
+
+uint64_t
+dsl_dir_dataset_id(zfs_dsl_dir_t *dir)
+{
+ return (dir->headds->dsid);
+}
+
+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)
+{
+ zfs_dsl_dir_t *cdsldir;
+
+ STAILQ_FOREACH(cdsldir, &dsldir->children, next) {
+ dsl_dir_foreach_post(zfs, cdsldir, cb, arg);
+ }
+ cb(zfs, dsldir, arg);
+}
+
+/*
+ * Used when the caller doesn't care about the order one way or another.
+ */
+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)
+{
+ dsl_dir_foreach_post(zfs, dsldir, cb, arg);
+}
+
+const char *
+dsl_dir_fullname(const zfs_dsl_dir_t *dir)
+{
+ return (dir->fullname);
+}
+
+/*
+ * Create a DSL directory, which is effectively an entry in the ZFS namespace.
+ * We always create a root DSL directory, whose name is the pool's name, and
+ * several metadata directories.
+ *
+ * Each directory has two ZAP objects, one pointing to child directories, and
+ * one for properties (which are inherited by children unless overridden).
+ * Directories typically reference a DSL dataset, the "head dataset", which
+ * points to an object set.
+ */
+static zfs_dsl_dir_t *
+dsl_dir_alloc(zfs_opt_t *zfs, const char *name)
+{
+ zfs_dsl_dir_list_t l, *lp;
+ zfs_dsl_dir_t *dir, *parent;
+ dnode_phys_t *dnode;
+ char *dirname, *nextdir, *origname;
+ uint64_t childid, propsid;
+
+ dir = ecalloc(1, sizeof(*dir));
+
+ dnode = objset_dnode_bonus_alloc(zfs->mos, DMU_OT_DSL_DIR,
+ DMU_OT_DSL_DIR, sizeof(dsl_dir_phys_t), &dir->dirid);
+ dir->phys = (dsl_dir_phys_t *)DN_BONUS(dnode);
+
+ dnode = objset_dnode_alloc(zfs->mos, DMU_OT_DSL_PROPS, &propsid);
+ dir->propszap = zap_alloc(zfs->mos, dnode);
+
+ dnode = objset_dnode_alloc(zfs->mos, DMU_OT_DSL_DIR_CHILD_MAP,
+ &childid);
+ dir->childzap = zap_alloc(zfs->mos, dnode);
+
+ dir->propsnv = nvlist_create(NV_UNIQUE_NAME);
+ STAILQ_INIT(&dir->children);
+
+ dir->phys->dd_child_dir_zapobj = childid;
+ dir->phys->dd_props_zapobj = propsid;
+
+ if (name == NULL) {
+ /*
+ * This is the root DSL directory.
+ */
+ dir->name = estrdup(zfs->poolname);
+ dir->fullname = estrdup(zfs->poolname);
+ dir->parent = NULL;
+ dir->phys->dd_parent_obj = 0;
+
+ assert(zfs->rootdsldir == NULL);
+ zfs->rootdsldir = dir;
+ return (dir);
+ }
+
+ /*
+ * Insert the new directory into the hierarchy. Currently this must be
+ * done in order, e.g., when creating pool/a/b, pool/a must already
+ * exist.
+ */
+ STAILQ_INIT(&l);
+ STAILQ_INSERT_HEAD(&l, zfs->rootdsldir, next);
+ origname = dirname = nextdir = estrdup(name);
+ for (lp = &l;; lp = &parent->children) {
+ dirname = strsep(&nextdir, "/");
+ if (nextdir == NULL)
+ break;
+
+ STAILQ_FOREACH(parent, lp, next) {
+ if (strcmp(parent->name, dirname) == 0)
+ break;
+ }
+ if (parent == NULL) {
+ errx(1, "no parent at `%s' for filesystem `%s'",
+ dirname, name);
+ }
+ }
+
+ dir->fullname = estrdup(name);
+ dir->name = estrdup(dirname);
+ free(origname);
+ STAILQ_INSERT_TAIL(lp, dir, next);
+ zap_add_uint64(parent->childzap, dir->name, dir->dirid);
+
+ dir->parent = parent;
+ dir->phys->dd_parent_obj = parent->dirid;
+ return (dir);
+}
+
+void
+dsl_dir_size_set(zfs_dsl_dir_t *dir, uint64_t bytes)
+{
+ dir->phys->dd_used_bytes = bytes;
+ dir->phys->dd_compressed_bytes = bytes;
+ dir->phys->dd_uncompressed_bytes = bytes;
+}
+
+/*
+ * Convert dataset properties into entries in the DSL directory's properties
+ * ZAP.
+ */
+static void
+dsl_dir_finalize_props(zfs_dsl_dir_t *dir)
+{
+ for (nvp_header_t *nvh = NULL;
+ (nvh = nvlist_next_nvpair(dir->propsnv, nvh)) != NULL;) {
+ nv_string_t *nvname;
+ nv_pair_data_t *nvdata;
+ const char *name;
+
+ nvname = (nv_string_t *)(nvh + 1);
+ nvdata = (nv_pair_data_t *)(&nvname->nv_data[0] +
+ NV_ALIGN4(nvname->nv_size));
+
+ name = nvstring_get(nvname);
+ switch (nvdata->nv_type) {
+ case DATA_TYPE_UINT64: {
+ uint64_t val;
+
+ memcpy(&val, &nvdata->nv_data[0], sizeof(uint64_t));
+ zap_add_uint64(dir->propszap, name, val);
+ break;
+ }
+ case DATA_TYPE_STRING: {
+ nv_string_t *nvstr;
+
+ nvstr = (nv_string_t *)&nvdata->nv_data[0];
+ zap_add_string(dir->propszap, name,
+ nvstring_get(nvstr));
+ break;
+ }
+ default:
+ assert(0);
+ }
+ }
+}
+
+static void
+dsl_dir_finalize(zfs_opt_t *zfs, zfs_dsl_dir_t *dir, void *arg __unused)
+{
+ char key[32];
+ zfs_dsl_dir_t *cdir;
+ dnode_phys_t *snapnames;
+ zfs_dsl_dataset_t *headds;
+ zfs_objset_t *os;
+ uint64_t bytes, snapnamesid;
+
+ dsl_dir_finalize_props(dir);
+ zap_write(zfs, dir->propszap);
+ zap_write(zfs, dir->childzap);
+
+ headds = dir->headds;
+ if (headds == NULL)
+ return;
+ os = headds->os;
+ if (os == NULL)
+ return;
+
+ snapnames = objset_dnode_alloc(zfs->mos, DMU_OT_DSL_DS_SNAP_MAP,
+ &snapnamesid);
+ zap_write(zfs, zap_alloc(zfs->mos, snapnames));
+
+ dir->phys->dd_head_dataset_obj = headds->dsid;
+ dir->phys->dd_clone_parent_obj = zfs->snapds->dsid;
+ headds->phys->ds_prev_snap_obj = zfs->snapds->dsid;
+ headds->phys->ds_snapnames_zapobj = snapnamesid;
+ objset_root_blkptr_copy(os, &headds->phys->ds_bp);
+
+ zfs->snapds->phys->ds_num_children++;
+ snprintf(key, sizeof(key), "%jx", (uintmax_t)headds->dsid);
+ zap_add_uint64(zfs->cloneszap, key, headds->dsid);
+
+ bytes = objset_space(os);
+ headds->phys->ds_used_bytes = bytes;
+ headds->phys->ds_uncompressed_bytes = bytes;
+ headds->phys->ds_compressed_bytes = bytes;
+
+ STAILQ_FOREACH(cdir, &dir->children, next)
+ bytes += cdir->phys->dd_used_bytes;
+ dsl_dir_size_set(dir, bytes);
+}
+
+void
+dsl_write(zfs_opt_t *zfs)
+{
+ zfs_zap_t *snapnameszap;
+ dnode_phys_t *snapnames;
+ uint64_t snapmapid;
+
+ /*
+ * Perform accounting, starting from the leaves of the DSL directory
+ * tree. Accounting for $MOS is done later, once we've finished
+ * allocating space.
+ */
+ dsl_dir_foreach_post(zfs, zfs->rootdsldir, dsl_dir_finalize, NULL);
+
+ snapnames = objset_dnode_alloc(zfs->mos, DMU_OT_DSL_DS_SNAP_MAP,
+ &snapmapid);
+ snapnameszap = zap_alloc(zfs->mos, snapnames);
+ zap_add_uint64(snapnameszap, "$ORIGIN", zfs->snapds->dsid);
+ zap_write(zfs, snapnameszap);
+
+ zfs->origindsldir->phys->dd_head_dataset_obj = zfs->originds->dsid;
+ zfs->originds->phys->ds_prev_snap_obj = zfs->snapds->dsid;
+ zfs->originds->phys->ds_snapnames_zapobj = snapmapid;
+
+ zfs->snapds->phys->ds_next_snap_obj = zfs->originds->dsid;
+ assert(zfs->snapds->phys->ds_num_children > 0);
+ zfs->snapds->phys->ds_num_children++;
+
+ zap_write(zfs, zfs->cloneszap);
+
+ /* XXX-MJ dirs and datasets are leaked */
+}
+
+void
+dsl_dir_dataset_write(zfs_opt_t *zfs, zfs_objset_t *os, zfs_dsl_dir_t *dir)
+{
+ dir->headds->os = os;
+ objset_write(zfs, os);
+}
+
+bool
+dsl_dir_has_dataset(zfs_dsl_dir_t *dir)
+{
+ return (dir->headds != NULL);
+}
+
+bool
+dsl_dir_dataset_has_objset(zfs_dsl_dir_t *dir)
+{
+ return (dsl_dir_has_dataset(dir) && dir->headds->os != NULL);
+}
+
+static zfs_dsl_dataset_t *
+dsl_dataset_alloc(zfs_opt_t *zfs, zfs_dsl_dir_t *dir)
+{
+ zfs_dsl_dataset_t *ds;
+ dnode_phys_t *dnode;
+ uint64_t deadlistid;
+
+ ds = ecalloc(1, sizeof(*ds));
+
+ dnode = objset_dnode_bonus_alloc(zfs->mos, DMU_OT_DSL_DATASET,
+ DMU_OT_DSL_DATASET, sizeof(dsl_dataset_phys_t), &ds->dsid);
+ ds->phys = (dsl_dataset_phys_t *)DN_BONUS(dnode);
+
+ dnode = objset_dnode_bonus_alloc(zfs->mos, DMU_OT_DEADLIST,
+ DMU_OT_DEADLIST_HDR, sizeof(dsl_deadlist_phys_t), &deadlistid);
+ zap_write(zfs, zap_alloc(zfs->mos, dnode));
+
+ ds->phys->ds_dir_obj = dir->dirid;
+ ds->phys->ds_deadlist_obj = deadlistid;
+ ds->phys->ds_creation_txg = TXG - 1;
+ if (ds != zfs->snapds)
+ ds->phys->ds_prev_snap_txg = TXG - 1;
+ ds->phys->ds_guid = ((uint64_t)random() << 32) | random();
+ ds->dir = dir;
+
+ return (ds);
+}
diff --git a/usr.sbin/makefs/zfs/fs.c b/usr.sbin/makefs/zfs/fs.c
new file mode 100644
index 000000000000..15025ec5447d
--- /dev/null
+++ b/usr.sbin/makefs/zfs/fs.c
@@ -0,0 +1,981 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2022 The FreeBSD Foundation
+ *
+ * This software was developed by Mark Johnston under sponsorship from
+ * the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/dirent.h>
+#include <sys/stat.h>
+
+#include <assert.h>
+#include <fcntl.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <util.h>
+
+#include "makefs.h"
+#include "zfs.h"
+
+typedef struct {
+ const char *name;
+ unsigned int id;
+ uint16_t size;
+ sa_bswap_type_t bs;
+} zfs_sattr_t;
+
+typedef struct zfs_fs {
+ zfs_objset_t *os;
+
+ /* Offset table for system attributes, indexed by a zpl_attr_t. */
+ uint16_t *saoffs;
+ size_t sacnt;
+ const zfs_sattr_t *satab;
+} zfs_fs_t;
+
+/*
+ * The order of the attributes doesn't matter, this is simply the one hard-coded
+ * by OpenZFS, based on a zdb dump of the SA_REGISTRY table.
+ */
+typedef enum zpl_attr {
+ ZPL_ATIME,
+ ZPL_MTIME,
+ ZPL_CTIME,
+ ZPL_CRTIME,
+ ZPL_GEN,
+ ZPL_MODE,
+ ZPL_SIZE,
+ ZPL_PARENT,
+ ZPL_LINKS,
+ ZPL_XATTR,
+ ZPL_RDEV,
+ ZPL_FLAGS,
+ ZPL_UID,
+ ZPL_GID,
+ ZPL_PAD,
+ ZPL_ZNODE_ACL,
+ ZPL_DACL_COUNT,
+ ZPL_SYMLINK,
+ ZPL_SCANSTAMP,
+ ZPL_DACL_ACES,
+ ZPL_DXATTR,
+ ZPL_PROJID,
+} zpl_attr_t;
+
+/*
+ * This table must be kept in sync with zpl_attr_layout[] and zpl_attr_t.
+ */
+static const zfs_sattr_t zpl_attrs[] = {
+#define _ZPL_ATTR(n, s, b) { .name = #n, .id = n, .size = s, .bs = b }
+ _ZPL_ATTR(ZPL_ATIME, sizeof(uint64_t) * 2, SA_UINT64_ARRAY),
+ _ZPL_ATTR(ZPL_MTIME, sizeof(uint64_t) * 2, SA_UINT64_ARRAY),
+ _ZPL_ATTR(ZPL_CTIME, sizeof(uint64_t) * 2, SA_UINT64_ARRAY),
+ _ZPL_ATTR(ZPL_CRTIME, sizeof(uint64_t) * 2, SA_UINT64_ARRAY),
+ _ZPL_ATTR(ZPL_GEN, sizeof(uint64_t), SA_UINT64_ARRAY),
+ _ZPL_ATTR(ZPL_MODE, sizeof(uint64_t), SA_UINT64_ARRAY),
+ _ZPL_ATTR(ZPL_SIZE, sizeof(uint64_t), SA_UINT64_ARRAY),
+ _ZPL_ATTR(ZPL_PARENT, sizeof(uint64_t), SA_UINT64_ARRAY),
+ _ZPL_ATTR(ZPL_LINKS, sizeof(uint64_t), SA_UINT64_ARRAY),
+ _ZPL_ATTR(ZPL_XATTR, sizeof(uint64_t), SA_UINT64_ARRAY),
+ _ZPL_ATTR(ZPL_RDEV, sizeof(uint64_t), SA_UINT64_ARRAY),
+ _ZPL_ATTR(ZPL_FLAGS, sizeof(uint64_t), SA_UINT64_ARRAY),
+ _ZPL_ATTR(ZPL_UID, sizeof(uint64_t), SA_UINT64_ARRAY),
+ _ZPL_ATTR(ZPL_GID, sizeof(uint64_t), SA_UINT64_ARRAY),
+ _ZPL_ATTR(ZPL_PAD, sizeof(uint64_t), SA_UINT64_ARRAY),
+ _ZPL_ATTR(ZPL_ZNODE_ACL, 88, SA_UINT64_ARRAY),
+ _ZPL_ATTR(ZPL_DACL_COUNT, sizeof(uint64_t), SA_UINT64_ARRAY),
+ _ZPL_ATTR(ZPL_SYMLINK, 0, SA_UINT8_ARRAY),
+ _ZPL_ATTR(ZPL_SCANSTAMP, sizeof(uint64_t) * 4, SA_UINT8_ARRAY),
+ _ZPL_ATTR(ZPL_DACL_ACES, 0, SA_ACL),
+ _ZPL_ATTR(ZPL_DXATTR, 0, SA_UINT8_ARRAY),
+ _ZPL_ATTR(ZPL_PROJID, sizeof(uint64_t), SA_UINT64_ARRAY),
+#undef ZPL_ATTR
+};
+
+/*
+ * This layout matches that of a filesystem created using OpenZFS on FreeBSD.
+ * It need not match in general, but FreeBSD's loader doesn't bother parsing the
+ * layout and just hard-codes attribute offsets.
+ */
+static const sa_attr_type_t zpl_attr_layout[] = {
+ ZPL_MODE,
+ ZPL_SIZE,
+ ZPL_GEN,
+ ZPL_UID,
+ ZPL_GID,
+ ZPL_PARENT,
+ ZPL_FLAGS,
+ ZPL_ATIME,
+ ZPL_MTIME,
+ ZPL_CTIME,
+ ZPL_CRTIME,
+ ZPL_LINKS,
+ ZPL_DACL_COUNT,
+ ZPL_DACL_ACES,
+ ZPL_SYMLINK,
+};
+
+/*
+ * Keys for the ZPL attribute tables in the SA layout ZAP. The first two
+ * indices are reserved for legacy attribute encoding.
+ */
+#define SA_LAYOUT_INDEX_DEFAULT 2
+#define SA_LAYOUT_INDEX_SYMLINK 3
+
+struct fs_populate_dir {
+ SLIST_ENTRY(fs_populate_dir) next;
+ int dirfd;
+ uint64_t objid;
+ zfs_zap_t *zap;
+};
+
+struct fs_populate_arg {
+ zfs_opt_t *zfs;
+ zfs_fs_t *fs; /* owning filesystem */
+ int dirfd; /* current directory fd */
+ uint64_t rootdirid; /* root directory dnode ID */
+ SLIST_HEAD(, fs_populate_dir) dirs; /* stack of directories */
+};
+
+static void fs_build_one(zfs_opt_t *, zfs_dsl_dir_t *, fsnode *, int);
+
+static bool
+fsnode_isroot(const fsnode *cur)
+{
+ return (strcmp(cur->name, ".") == 0);
+}
+
+/*
+ * Visit each node in a directory hierarchy, in pre-order depth-first order.
+ */
+static void
+fsnode_foreach(fsnode *root, int (*cb)(fsnode *, void *), void *arg)
+{
+ assert(root->type == S_IFDIR);
+
+ for (fsnode *cur = root; cur != NULL; cur = cur->next) {
+ assert(cur->type == S_IFREG || cur->type == S_IFDIR ||
+ cur->type == S_IFLNK);
+
+ if (cb(cur, arg) == 0)
+ continue;
+ if (cur->type == S_IFDIR && cur->child != NULL)
+ fsnode_foreach(cur->child, cb, arg);
+ }
+}
+
+static void
+fs_populate_dirent(struct fs_populate_arg *arg, fsnode *cur, uint64_t dnid)
+{
+ struct fs_populate_dir *dir;
+ uint64_t type;
+
+ switch (cur->type) {
+ case S_IFREG:
+ type = DT_REG;
+ break;
+ case S_IFDIR:
+ type = DT_DIR;
+ break;
+ case S_IFLNK:
+ type = DT_LNK;
+ break;
+ default:
+ assert(0);
+ }
+
+ dir = SLIST_FIRST(&arg->dirs);
+ zap_add_uint64(dir->zap, cur->name, ZFS_DIRENT_MAKE(type, dnid));
+}
+
+static void
+fs_populate_attr(zfs_fs_t *fs, char *attrbuf, const void *val, uint16_t ind,
+ size_t *szp)
+{
+ assert(ind < fs->sacnt);
+ assert(fs->saoffs[ind] != 0xffff);
+
+ memcpy(attrbuf + fs->saoffs[ind], val, fs->satab[ind].size);
+ *szp += fs->satab[ind].size;
+}
+
+static void
+fs_populate_varszattr(zfs_fs_t *fs, char *attrbuf, const void *val,
+ size_t valsz, size_t varoff, uint16_t ind, size_t *szp)
+{
+ assert(ind < fs->sacnt);
+ assert(fs->saoffs[ind] != 0xffff);
+ assert(fs->satab[ind].size == 0);
+
+ memcpy(attrbuf + fs->saoffs[ind] + varoff, val, valsz);
+ *szp += valsz;
+}
+
+static void
+fs_populate_sattrs(struct fs_populate_arg *arg, const fsnode *cur,
+ dnode_phys_t *dnode)
+{
+ char target[PATH_MAX];
+ zfs_fs_t *fs;
+ zfs_ace_hdr_t aces[3];
+ struct stat *sb;
+ sa_hdr_phys_t *sahdr;
+ uint64_t daclcount, flags, gen, gid, links, mode, parent, objsize, uid;
+ char *attrbuf;
+ size_t bonussz, hdrsz;
+ int layout;
+
+ assert(dnode->dn_bonustype == DMU_OT_SA);
+ assert(dnode->dn_nblkptr == 1);
+
+ fs = arg->fs;
+ sb = &cur->inode->st;
+
+ switch (cur->type) {
+ case S_IFREG:
+ layout = SA_LAYOUT_INDEX_DEFAULT;
+ links = cur->inode->nlink;
+ objsize = sb->st_size;
+ parent = SLIST_FIRST(&arg->dirs)->objid;
+ break;
+ case S_IFDIR:
+ layout = SA_LAYOUT_INDEX_DEFAULT;
+ links = 1; /* .. */
+ objsize = 1; /* .. */
+
+ /*
+ * The size of a ZPL directory is the number of entries
+ * (including "." and ".."), and the link count is the number of
+ * entries which are directories (including "." and "..").
+ */
+ for (fsnode *c = fsnode_isroot(cur) ? cur->next : cur->child;
+ c != NULL; c = c->next) {
+ if (c->type == S_IFDIR)
+ links++;
+ objsize++;
+ }
+
+ /* The root directory is its own parent. */
+ parent = SLIST_EMPTY(&arg->dirs) ?
+ arg->rootdirid : SLIST_FIRST(&arg->dirs)->objid;
+ break;
+ case S_IFLNK: {
+ ssize_t n;
+
+ if ((n = readlinkat(SLIST_FIRST(&arg->dirs)->dirfd, cur->name,
+ target, sizeof(target) - 1)) == -1)
+ err(1, "readlinkat(%s)", cur->name);
+ target[n] = '\0';
+
+ layout = SA_LAYOUT_INDEX_SYMLINK;
+ links = 1;
+ objsize = strlen(target);
+ parent = SLIST_FIRST(&arg->dirs)->objid;
+ break;
+ }
+ default:
+ assert(0);
+ }
+
+ daclcount = nitems(aces);
+ flags = ZFS_ACL_TRIVIAL | ZFS_ACL_AUTO_INHERIT | ZFS_NO_EXECS_DENIED |
+ ZFS_ARCHIVE | ZFS_AV_MODIFIED; /* XXX-MJ */
+ gen = 1;
+ gid = sb->st_gid;
+ mode = sb->st_mode;
+ uid = sb->st_uid;
+
+ memset(aces, 0, sizeof(aces));
+ aces[0].z_flags = ACE_OWNER;
+ aces[0].z_type = ACE_ACCESS_ALLOWED_ACE_TYPE;
+ aces[0].z_access_mask = ACE_WRITE_ATTRIBUTES | ACE_WRITE_OWNER |
+ ACE_WRITE_ACL | ACE_WRITE_NAMED_ATTRS | ACE_READ_ACL |
+ ACE_READ_ATTRIBUTES | ACE_READ_NAMED_ATTRS | ACE_SYNCHRONIZE;
+ if ((mode & S_IRUSR) != 0)
+ aces[0].z_access_mask |= ACE_READ_DATA;
+ if ((mode & S_IWUSR) != 0)
+ aces[0].z_access_mask |= ACE_WRITE_DATA | ACE_APPEND_DATA;
+ if ((mode & S_IXUSR) != 0)
+ aces[0].z_access_mask |= ACE_EXECUTE;
+
+ aces[1].z_flags = ACE_GROUP | ACE_IDENTIFIER_GROUP;
+ aces[1].z_type = ACE_ACCESS_ALLOWED_ACE_TYPE;
+ aces[1].z_access_mask = ACE_READ_ACL | ACE_READ_ATTRIBUTES |
+ ACE_READ_NAMED_ATTRS | ACE_SYNCHRONIZE;
+ if ((mode & S_IRGRP) != 0)
+ aces[1].z_access_mask |= ACE_READ_DATA;
+ if ((mode & S_IWGRP) != 0)
+ aces[1].z_access_mask |= ACE_WRITE_DATA | ACE_APPEND_DATA;
+ if ((mode & S_IXGRP) != 0)
+ aces[1].z_access_mask |= ACE_EXECUTE;
+
+ aces[2].z_flags = ACE_EVERYONE;
+ aces[2].z_type = ACE_ACCESS_ALLOWED_ACE_TYPE;
+ aces[2].z_access_mask = ACE_READ_ACL | ACE_READ_ATTRIBUTES |
+ ACE_READ_NAMED_ATTRS | ACE_SYNCHRONIZE;
+ if ((mode & S_IROTH) != 0)
+ aces[2].z_access_mask |= ACE_READ_DATA;
+ if ((mode & S_IWOTH) != 0)
+ aces[2].z_access_mask |= ACE_WRITE_DATA | ACE_APPEND_DATA;
+ if ((mode & S_IXOTH) != 0)
+ aces[2].z_access_mask |= ACE_EXECUTE;
+
+ switch (layout) {
+ case SA_LAYOUT_INDEX_DEFAULT:
+ /* At most one variable-length attribute. */
+ hdrsz = sizeof(uint64_t);
+ break;
+ case SA_LAYOUT_INDEX_SYMLINK:
+ /* At most five variable-length attributes. */
+ hdrsz = sizeof(uint64_t) * 2;
+ break;
+ default:
+ assert(0);
+ }
+
+ sahdr = (sa_hdr_phys_t *)DN_BONUS(dnode);
+ sahdr->sa_magic = SA_MAGIC;
+ SA_HDR_LAYOUT_INFO_ENCODE(sahdr->sa_layout_info, layout, hdrsz);
+
+ bonussz = SA_HDR_SIZE(sahdr);
+ attrbuf = (char *)sahdr + SA_HDR_SIZE(sahdr);
+
+ fs_populate_attr(fs, attrbuf, &daclcount, ZPL_DACL_COUNT, &bonussz);
+ fs_populate_attr(fs, attrbuf, &flags, ZPL_FLAGS, &bonussz);
+ fs_populate_attr(fs, attrbuf, &gen, ZPL_GEN, &bonussz);
+ fs_populate_attr(fs, attrbuf, &gid, ZPL_GID, &bonussz);
+ fs_populate_attr(fs, attrbuf, &links, ZPL_LINKS, &bonussz);
+ fs_populate_attr(fs, attrbuf, &mode, ZPL_MODE, &bonussz);
+ fs_populate_attr(fs, attrbuf, &parent, ZPL_PARENT, &bonussz);
+ fs_populate_attr(fs, attrbuf, &objsize, ZPL_SIZE, &bonussz);
+ fs_populate_attr(fs, attrbuf, &uid, ZPL_UID, &bonussz);
+
+ /*
+ * We deliberately set atime = mtime here to ensure that images are
+ * reproducible.
+ */
+ assert(sizeof(sb->st_mtim) == fs->satab[ZPL_ATIME].size);
+ fs_populate_attr(fs, attrbuf, &sb->st_mtim, ZPL_ATIME, &bonussz);
+ assert(sizeof(sb->st_ctim) == fs->satab[ZPL_CTIME].size);
+ fs_populate_attr(fs, attrbuf, &sb->st_ctim, ZPL_CTIME, &bonussz);
+ assert(sizeof(sb->st_mtim) == fs->satab[ZPL_MTIME].size);
+ fs_populate_attr(fs, attrbuf, &sb->st_mtim, ZPL_MTIME, &bonussz);
+ assert(sizeof(sb->st_birthtim) == fs->satab[ZPL_CRTIME].size);
+ fs_populate_attr(fs, attrbuf, &sb->st_birthtim, ZPL_CRTIME, &bonussz);
+
+ fs_populate_varszattr(fs, attrbuf, aces, sizeof(aces), 0,
+ ZPL_DACL_ACES, &bonussz);
+ sahdr->sa_lengths[0] = sizeof(aces);
+
+ if (cur->type == S_IFLNK) {
+ assert(layout == SA_LAYOUT_INDEX_SYMLINK);
+ /* Need to use a spill block pointer if the target is long. */
+ assert(bonussz + objsize <= DN_OLD_MAX_BONUSLEN);
+ fs_populate_varszattr(fs, attrbuf, target, objsize,
+ sahdr->sa_lengths[0], ZPL_SYMLINK, &bonussz);
+ sahdr->sa_lengths[1] = (uint16_t)objsize;
+ }
+
+ dnode->dn_bonuslen = bonussz;
+}
+
+static void
+fs_populate_file(fsnode *cur, struct fs_populate_arg *arg)
+{
+ struct dnode_cursor *c;
+ dnode_phys_t *dnode;
+ zfs_opt_t *zfs;
+ char *buf;
+ uint64_t dnid;
+ ssize_t n;
+ size_t bufsz;
+ off_t size, target;
+ int fd;
+
+ assert(cur->type == S_IFREG);
+ assert((cur->inode->flags & FI_ROOT) == 0);
+
+ zfs = arg->zfs;
+
+ assert(cur->inode->ino != 0);
+ if ((cur->inode->flags & FI_ALLOCATED) != 0) {
+ /*
+ * This is a hard link of an existing file.
+ *
+ * XXX-MJ need to check whether it crosses datasets, add a test
+ * case for that
+ */
+ fs_populate_dirent(arg, cur, cur->inode->ino);
+ return;
+ }
+
+ dnode = objset_dnode_bonus_alloc(arg->fs->os,
+ DMU_OT_PLAIN_FILE_CONTENTS, DMU_OT_SA, 0, &dnid);
+ cur->inode->ino = dnid;
+ cur->inode->flags |= FI_ALLOCATED;
+
+ fd = openat(SLIST_FIRST(&arg->dirs)->dirfd, cur->name, O_RDONLY);
+ if (fd == -1)
+ err(1, "openat(%s)", cur->name);
+
+ buf = zfs->filebuf;
+ bufsz = sizeof(zfs->filebuf);
+ size = cur->inode->st.st_size;
+ c = dnode_cursor_init(zfs, arg->fs->os, dnode, size, 0);
+ for (off_t foff = 0; foff < size; foff += target) {
+ off_t loc, sofar;
+
+ /*
+ * Fill up our buffer, handling partial reads.
+ *
+ * It might be profitable to use copy_file_range(2) here.
+ */
+ sofar = 0;
+ target = MIN(size - foff, (off_t)bufsz);
+ do {
+ n = read(fd, buf + sofar, target);
+ if (n < 0)
+ err(1, "reading from '%s'", cur->name);
+ if (n == 0)
+ errx(1, "unexpected EOF reading '%s'",
+ cur->name);
+ sofar += n;
+ } while (sofar < target);
+
+ if (target < (off_t)bufsz)
+ memset(buf + target, 0, bufsz - target);
+
+ loc = objset_space_alloc(zfs, arg->fs->os, &target);
+ vdev_pwrite_dnode_indir(zfs, dnode, 0, 1, buf, target, loc,
+ dnode_cursor_next(zfs, c, foff));
+ }
+ if (close(fd) != 0)
+ err(1, "close");
+ dnode_cursor_finish(zfs, c);
+
+ fs_populate_sattrs(arg, cur, dnode);
+ fs_populate_dirent(arg, cur, dnid);
+}
+
+static void
+fs_populate_dir(fsnode *cur, struct fs_populate_arg *arg)
+{
+ dnode_phys_t *dnode;
+ zfs_objset_t *os;
+ uint64_t dnid;
+ int dirfd;
+
+ assert(cur->type == S_IFDIR);
+ assert((cur->inode->flags & FI_ALLOCATED) == 0);
+
+ os = arg->fs->os;
+
+ dnode = objset_dnode_bonus_alloc(os, DMU_OT_DIRECTORY_CONTENTS,
+ DMU_OT_SA, 0, &dnid);
+
+ /*
+ * Add an entry to the parent directory and open this directory.
+ */
+ if (!SLIST_EMPTY(&arg->dirs)) {
+ fs_populate_dirent(arg, cur, dnid);
+ dirfd = openat(SLIST_FIRST(&arg->dirs)->dirfd, cur->name,
+ O_DIRECTORY);
+ if (dirfd < 0)
+ err(1, "open(%s)", cur->name);
+ } else {
+ arg->rootdirid = dnid;
+ dirfd = arg->dirfd;
+ }
+
+ /*
+ * Set ZPL attributes.
+ */
+ fs_populate_sattrs(arg, cur, dnode);
+
+ /*
+ * If this is a root directory, then its children belong to a different
+ * dataset and this directory remains empty in the current objset.
+ */
+ if ((cur->inode->flags & FI_ROOT) == 0) {
+ struct fs_populate_dir *dir;
+
+ dir = ecalloc(1, sizeof(*dir));
+ dir->dirfd = dirfd;
+ dir->objid = dnid;
+ dir->zap = zap_alloc(os, dnode);
+ SLIST_INSERT_HEAD(&arg->dirs, dir, next);
+ } else {
+ zap_write(arg->zfs, zap_alloc(os, dnode));
+ fs_build_one(arg->zfs, cur->inode->param, cur->child, dirfd);
+ }
+}
+
+static void
+fs_populate_symlink(fsnode *cur, struct fs_populate_arg *arg)
+{
+ dnode_phys_t *dnode;
+ uint64_t dnid;
+
+ assert(cur->type == S_IFLNK);
+ assert((cur->inode->flags & (FI_ALLOCATED | FI_ROOT)) == 0);
+
+ dnode = objset_dnode_bonus_alloc(arg->fs->os,
+ DMU_OT_PLAIN_FILE_CONTENTS, DMU_OT_SA, 0, &dnid);
+
+ fs_populate_dirent(arg, cur, dnid);
+
+ fs_populate_sattrs(arg, cur, dnode);
+}
+
+static int
+fs_foreach_populate(fsnode *cur, void *_arg)
+{
+ struct fs_populate_arg *arg;
+ struct fs_populate_dir *dir;
+ int ret;
+
+ arg = _arg;
+ switch (cur->type) {
+ case S_IFREG:
+ fs_populate_file(cur, arg);
+ break;
+ case S_IFDIR:
+ if (fsnode_isroot(cur))
+ break;
+ fs_populate_dir(cur, arg);
+ break;
+ case S_IFLNK:
+ fs_populate_symlink(cur, arg);
+ break;
+ default:
+ assert(0);
+ }
+
+ ret = (cur->inode->flags & FI_ROOT) != 0 ? 0 : 1;
+
+ if (cur->next == NULL &&
+ (cur->child == NULL || (cur->inode->flags & FI_ROOT) != 0)) {
+ /*
+ * We reached a terminal node in a subtree. Walk back up and
+ * write out directories. We're done once we hit the root of a
+ * dataset or find a level where we're not on the edge of the
+ * tree.
+ */
+ do {
+ dir = SLIST_FIRST(&arg->dirs);
+ SLIST_REMOVE_HEAD(&arg->dirs, next);
+ zap_write(arg->zfs, dir->zap);
+ if (dir->dirfd != -1 && close(dir->dirfd) != 0)
+ err(1, "close");
+ free(dir);
+ cur = cur->parent;
+ } while (cur != NULL && cur->next == NULL &&
+ (cur->inode->flags & FI_ROOT) == 0);
+ }
+
+ return (ret);
+}
+
+static void
+fs_add_zpl_attr_layout(zfs_zap_t *zap, unsigned int index,
+ const sa_attr_type_t layout[], size_t sacnt)
+{
+ char ti[16];
+
+ assert(sizeof(layout[0]) == 2);
+
+ snprintf(ti, sizeof(ti), "%u", index);
+ zap_add(zap, ti, sizeof(sa_attr_type_t), sacnt,
+ (const uint8_t *)layout);
+}
+
+/*
+ * Initialize system attribute tables.
+ *
+ * There are two elements to this. First, we write the zpl_attrs[] and
+ * zpl_attr_layout[] tables to disk. Then we create a lookup table which
+ * allows us to set file attributes quickly.
+ */
+static uint64_t
+fs_set_zpl_attrs(zfs_opt_t *zfs, zfs_fs_t *fs)
+{
+ zfs_zap_t *sazap, *salzap, *sarzap;
+ zfs_objset_t *os;
+ dnode_phys_t *saobj, *salobj, *sarobj;
+ uint64_t saobjid, salobjid, sarobjid;
+ uint16_t offset;
+
+ os = fs->os;
+
+ /*
+ * The on-disk tables are stored in two ZAP objects, the registry object
+ * and the layout object. Individual attributes are described by
+ * entries in the registry object; for example, the value for the
+ * "ZPL_SIZE" key gives the size and encoding of the ZPL_SIZE attribute.
+ * The attributes of a file are ordered according to one of the layouts
+ * defined in the layout object. The master node object is simply used
+ * to locate the registry and layout objects.
+ */
+ saobj = objset_dnode_alloc(os, DMU_OT_SA_MASTER_NODE, &saobjid);
+ salobj = objset_dnode_alloc(os, DMU_OT_SA_ATTR_LAYOUTS, &salobjid);
+ sarobj = objset_dnode_alloc(os, DMU_OT_SA_ATTR_REGISTRATION, &sarobjid);
+
+ sarzap = zap_alloc(os, sarobj);
+ for (size_t i = 0; i < nitems(zpl_attrs); i++) {
+ const zfs_sattr_t *sa;
+ uint64_t attr;
+
+ attr = 0;
+ sa = &zpl_attrs[i];
+ SA_ATTR_ENCODE(attr, (uint64_t)i, sa->size, sa->bs);
+ zap_add_uint64(sarzap, sa->name, attr);
+ }
+ zap_write(zfs, sarzap);
+
+ /*
+ * Layouts are arrays of indices into the registry. We define two
+ * layouts for use by the ZPL, one for non-symlinks and one for
+ * symlinks. They are identical except that the symlink layout includes
+ * ZPL_SYMLINK as its final attribute.
+ */
+ salzap = zap_alloc(os, salobj);
+ assert(zpl_attr_layout[nitems(zpl_attr_layout) - 1] == ZPL_SYMLINK);
+ fs_add_zpl_attr_layout(salzap, SA_LAYOUT_INDEX_DEFAULT,
+ zpl_attr_layout, nitems(zpl_attr_layout) - 1);
+ fs_add_zpl_attr_layout(salzap, SA_LAYOUT_INDEX_SYMLINK,
+ zpl_attr_layout, nitems(zpl_attr_layout));
+ zap_write(zfs, salzap);
+
+ sazap = zap_alloc(os, saobj);
+ zap_add_uint64(sazap, SA_LAYOUTS, salobjid);
+ zap_add_uint64(sazap, SA_REGISTRY, sarobjid);
+ zap_write(zfs, sazap);
+
+ /* Sanity check. */
+ for (size_t i = 0; i < nitems(zpl_attrs); i++)
+ assert(i == zpl_attrs[i].id);
+
+ /*
+ * Build the offset table used when setting file attributes. File
+ * attributes are stored in the object's bonus buffer; this table
+ * provides the buffer offset of attributes referenced by the layout
+ * table.
+ */
+ fs->sacnt = nitems(zpl_attrs);
+ fs->saoffs = ecalloc(fs->sacnt, sizeof(*fs->saoffs));
+ for (size_t i = 0; i < fs->sacnt; i++)
+ fs->saoffs[i] = 0xffff;
+ offset = 0;
+ for (size_t i = 0; i < nitems(zpl_attr_layout); i++) {
+ uint16_t size;
+
+ assert(zpl_attr_layout[i] < fs->sacnt);
+
+ fs->saoffs[zpl_attr_layout[i]] = offset;
+ size = zpl_attrs[zpl_attr_layout[i]].size;
+ offset += size;
+ }
+ fs->satab = zpl_attrs;
+
+ return (saobjid);
+}
+
+static void
+fs_layout_one(zfs_opt_t *zfs, zfs_dsl_dir_t *dsldir, void *arg)
+{
+ char *mountpoint, *origmountpoint, *name, *next;
+ fsnode *cur, *root;
+ uint64_t canmount;
+
+ if (!dsl_dir_has_dataset(dsldir))
+ return;
+
+ mountpoint = dsl_dir_get_mountpoint(zfs, dsldir);
+ if (mountpoint == NULL)
+ return;
+ if (dsl_dir_get_canmount(dsldir, &canmount) == 0 && canmount == 0)
+ return;
+
+ /*
+ * If we were asked to specify a bootfs, set it here.
+ */
+ if (zfs->bootfs != NULL && strcmp(zfs->bootfs,
+ dsl_dir_fullname(dsldir)) == 0) {
+ zap_add_uint64(zfs->poolprops, "bootfs",
+ dsl_dir_dataset_id(dsldir));
+ }
+
+ origmountpoint = mountpoint;
+
+ /*
+ * Figure out which fsnode corresponds to our mountpoint.
+ */
+ root = arg;
+ cur = root;
+ if (strcmp(mountpoint, zfs->rootpath) != 0) {
+ mountpoint += strlen(zfs->rootpath);
+
+ /*
+ * Look up the directory in the staged tree. For example, if
+ * the dataset's mount point is /foo/bar/baz, we'll search the
+ * root directory for "foo", search "foo" for "baz", and so on.
+ * Each intermediate name must refer to a directory; the final
+ * component need not exist.
+ */
+ cur = root;
+ for (next = name = mountpoint; next != NULL;) {
+ for (; *next == '/'; next++)
+ ;
+ name = strsep(&next, "/");
+
+ for (; cur != NULL && strcmp(cur->name, name) != 0;
+ cur = cur->next)
+ ;
+ if (cur == NULL) {
+ if (next == NULL)
+ break;
+ errx(1, "missing mountpoint directory for `%s'",
+ dsl_dir_fullname(dsldir));
+ }
+ if (cur->type != S_IFDIR) {
+ errx(1,
+ "mountpoint for `%s' is not a directory",
+ dsl_dir_fullname(dsldir));
+ }
+ if (next != NULL)
+ cur = cur->child;
+ }
+ }
+
+ if (cur != NULL) {
+ assert(cur->type == S_IFDIR);
+
+ /*
+ * Multiple datasets shouldn't share a mountpoint. It's
+ * technically allowed, but it's not clear what makefs should do
+ * in that case.
+ */
+ assert((cur->inode->flags & FI_ROOT) == 0);
+ if (cur != root)
+ cur->inode->flags |= FI_ROOT;
+ assert(cur->inode->param == NULL);
+ cur->inode->param = dsldir;
+ }
+
+ free(origmountpoint);
+}
+
+static int
+fs_foreach_mark(fsnode *cur, void *arg)
+{
+ uint64_t *countp;
+
+ countp = arg;
+ if (cur->type == S_IFDIR && fsnode_isroot(cur))
+ return (1);
+
+ if (cur->inode->ino == 0) {
+ cur->inode->ino = ++(*countp);
+ cur->inode->nlink = 1;
+ } else {
+ cur->inode->nlink++;
+ }
+
+ return ((cur->inode->flags & FI_ROOT) != 0 ? 0 : 1);
+}
+
+/*
+ * Create a filesystem dataset. More specifically:
+ * - create an object set for the dataset,
+ * - add required metadata (SA tables, property definitions, etc.) to that
+ * object set,
+ * - optionally populate the object set with file objects, using "root" as the
+ * root directory.
+ *
+ * "dirfd" is a directory descriptor for the directory referenced by "root". It
+ * is closed before returning.
+ */
+static void
+fs_build_one(zfs_opt_t *zfs, zfs_dsl_dir_t *dsldir, fsnode *root, int dirfd)
+{
+ struct fs_populate_arg arg;
+ zfs_fs_t fs;
+ zfs_zap_t *masterzap;
+ zfs_objset_t *os;
+ dnode_phys_t *deleteq, *masterobj;
+ uint64_t deleteqid, dnodecount, moid, rootdirid, saobjid;
+ bool fakedroot;
+
+ /*
+ * This dataset's mountpoint doesn't exist in the staging tree, or the
+ * dataset doesn't have a mountpoint at all. In either case we still
+ * need a root directory. Fake up a root fsnode to handle this case.
+ */
+ fakedroot = root == NULL;
+ if (fakedroot) {
+ struct stat *stp;
+
+ assert(dirfd == -1);
+
+ root = ecalloc(1, sizeof(*root));
+ root->inode = ecalloc(1, sizeof(*root->inode));
+ root->name = estrdup(".");
+ root->type = S_IFDIR;
+
+ stp = &root->inode->st;
+ stp->st_uid = 0;
+ stp->st_gid = 0;
+ stp->st_mode = S_IFDIR | 0755;
+ }
+ assert(root->type == S_IFDIR);
+ assert(fsnode_isroot(root));
+
+ /*
+ * Initialize the object set for this dataset.
+ */
+ os = objset_alloc(zfs, DMU_OST_ZFS);
+ masterobj = objset_dnode_alloc(os, DMU_OT_MASTER_NODE, &moid);
+ assert(moid == MASTER_NODE_OBJ);
+
+ memset(&fs, 0, sizeof(fs));
+ fs.os = os;
+
+ /*
+ * Create the ZAP SA layout now since filesystem object dnodes will
+ * refer to those attributes.
+ */
+ saobjid = fs_set_zpl_attrs(zfs, &fs);
+
+ /*
+ * Make a pass over the staged directory to detect hard links and assign
+ * virtual dnode numbers.
+ */
+ dnodecount = 1; /* root directory */
+ fsnode_foreach(root, fs_foreach_mark, &dnodecount);
+
+ /*
+ * Make a second pass to populate the dataset with files from the
+ * staged directory. Most of our runtime is spent here.
+ */
+ arg.dirfd = dirfd;
+ arg.zfs = zfs;
+ arg.fs = &fs;
+ SLIST_INIT(&arg.dirs);
+ fs_populate_dir(root, &arg);
+ assert(!SLIST_EMPTY(&arg.dirs));
+ fsnode_foreach(root, fs_foreach_populate, &arg);
+ assert(SLIST_EMPTY(&arg.dirs));
+ rootdirid = arg.rootdirid;
+
+ /*
+ * Create an empty delete queue. We don't do anything with it, but
+ * OpenZFS will refuse to mount filesystems that don't have one.
+ */
+ deleteq = objset_dnode_alloc(os, DMU_OT_UNLINKED_SET, &deleteqid);
+ zap_write(zfs, zap_alloc(os, deleteq));
+
+ /*
+ * Populate and write the master node object. This is a ZAP object
+ * containing various dataset properties and the object IDs of the root
+ * directory and delete queue.
+ */
+ masterzap = zap_alloc(os, masterobj);
+ zap_add_uint64(masterzap, ZFS_ROOT_OBJ, rootdirid);
+ zap_add_uint64(masterzap, ZFS_UNLINKED_SET, deleteqid);
+ zap_add_uint64(masterzap, ZFS_SA_ATTRS, saobjid);
+ zap_add_uint64(masterzap, ZPL_VERSION_OBJ, 5 /* ZPL_VERSION_SA */);
+ zap_add_uint64(masterzap, "normalization", 0 /* off */);
+ zap_add_uint64(masterzap, "utf8only", 0 /* off */);
+ zap_add_uint64(masterzap, "casesensitivity", 0 /* case sensitive */);
+ zap_add_uint64(masterzap, "acltype", 2 /* NFSv4 */);
+ zap_write(zfs, masterzap);
+
+ /*
+ * All finished with this object set, we may as well write it now.
+ * The DSL layer will sum up the bytes consumed by each dataset using
+ * information stored in the object set, so it can't be freed just yet.
+ */
+ dsl_dir_dataset_write(zfs, os, dsldir);
+
+ if (fakedroot) {
+ free(root->inode);
+ free(root->name);
+ free(root);
+ }
+ free(fs.saoffs);
+}
+
+/*
+ * Create an object set for each DSL directory which has a dataset and doesn't
+ * already have an object set.
+ */
+static void
+fs_build_unmounted(zfs_opt_t *zfs, zfs_dsl_dir_t *dsldir, void *arg __unused)
+{
+ if (dsl_dir_has_dataset(dsldir) && !dsl_dir_dataset_has_objset(dsldir))
+ fs_build_one(zfs, dsldir, NULL, -1);
+}
+
+/*
+ * Create our datasets and populate them with files.
+ */
+void
+fs_build(zfs_opt_t *zfs, int dirfd, fsnode *root)
+{
+ /*
+ * Run through our datasets and find the root fsnode for each one. Each
+ * root fsnode is flagged so that we can figure out which dataset it
+ * belongs to.
+ */
+ dsl_dir_foreach(zfs, zfs->rootdsldir, fs_layout_one, root);
+
+ /*
+ * Did we find our boot filesystem?
+ */
+ if (zfs->bootfs != NULL && !zap_entry_exists(zfs->poolprops, "bootfs"))
+ errx(1, "no mounted dataset matches bootfs property `%s'",
+ zfs->bootfs);
+
+ /*
+ * Traverse the file hierarchy starting from the root fsnode. One
+ * dataset, not necessarily the root dataset, must "own" the root
+ * directory by having its mountpoint be equal to the root path.
+ *
+ * As roots of other datasets are encountered during the traversal,
+ * fs_build_one() recursively creates the corresponding object sets and
+ * populates them. Once this function has returned, all datasets will
+ * have been fully populated.
+ */
+ fs_build_one(zfs, root->inode->param, root, dirfd);
+
+ /*
+ * Now create object sets for datasets whose mountpoints weren't found
+ * in the staging directory, either because there is no mountpoint, or
+ * because the mountpoint doesn't correspond to an existing directory.
+ */
+ dsl_dir_foreach(zfs, zfs->rootdsldir, fs_build_unmounted, NULL);
+}
diff --git a/usr.sbin/makefs/zfs/objset.c b/usr.sbin/makefs/zfs/objset.c
new file mode 100644
index 000000000000..fdb17167a607
--- /dev/null
+++ b/usr.sbin/makefs/zfs/objset.c
@@ -0,0 +1,259 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2022 The FreeBSD Foundation
+ *
+ * This software was developed by Mark Johnston under sponsorship from
+ * the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <assert.h>
+#include <string.h>
+
+#include <util.h>
+
+#include "zfs.h"
+
+#define DNODES_PER_CHUNK (MAXBLOCKSIZE / sizeof(dnode_phys_t))
+
+struct objset_dnode_chunk {
+ dnode_phys_t buf[DNODES_PER_CHUNK];
+ unsigned int nextfree;
+ STAILQ_ENTRY(objset_dnode_chunk) next;
+};
+
+typedef struct zfs_objset {
+ /* Physical object set. */
+ objset_phys_t *phys;
+ off_t osloc;
+ off_t osblksz;
+ blkptr_t osbp; /* set in objset_write() */
+
+ /* Accounting. */
+ off_t space; /* bytes allocated to this objset */
+
+ /* dnode allocator. */
+ uint64_t dnodecount;
+ STAILQ_HEAD(, objset_dnode_chunk) dnodechunks;
+} zfs_objset_t;
+
+static void
+dnode_init(dnode_phys_t *dnode, uint8_t type, uint8_t bonustype,
+ uint16_t bonuslen)
+{
+ dnode->dn_indblkshift = MAXBLOCKSHIFT;
+ dnode->dn_type = type;
+ dnode->dn_bonustype = bonustype;
+ dnode->dn_bonuslen = bonuslen;
+ dnode->dn_checksum = ZIO_CHECKSUM_FLETCHER_4;
+ dnode->dn_nlevels = 1;
+ dnode->dn_nblkptr = 1;
+ dnode->dn_flags = DNODE_FLAG_USED_BYTES;
+}
+
+zfs_objset_t *
+objset_alloc(zfs_opt_t *zfs, uint64_t type)
+{
+ struct objset_dnode_chunk *chunk;
+ zfs_objset_t *os;
+
+ os = ecalloc(1, sizeof(*os));
+ os->osblksz = sizeof(objset_phys_t);
+ os->osloc = objset_space_alloc(zfs, os, &os->osblksz);
+
+ /*
+ * Object ID zero is always reserved for the meta dnode, which is
+ * embedded in the objset itself.
+ */
+ STAILQ_INIT(&os->dnodechunks);
+ chunk = ecalloc(1, sizeof(*chunk));
+ chunk->nextfree = 1;
+ STAILQ_INSERT_HEAD(&os->dnodechunks, chunk, next);
+ os->dnodecount = 1;
+
+ os->phys = ecalloc(1, os->osblksz);
+ os->phys->os_type = type;
+
+ dnode_init(&os->phys->os_meta_dnode, DMU_OT_DNODE, DMU_OT_NONE, 0);
+ os->phys->os_meta_dnode.dn_datablkszsec =
+ DNODE_BLOCK_SIZE >> MINBLOCKSHIFT;
+
+ return (os);
+}
+
+/*
+ * Write the dnode array and physical object set to disk.
+ */
+static void
+_objset_write(zfs_opt_t *zfs, zfs_objset_t *os, struct dnode_cursor *c,
+ off_t loc)
+{
+ struct objset_dnode_chunk *chunk, *tmp;
+ unsigned int total;
+
+ /*
+ * Write out the dnode array, i.e., the meta-dnode. For some reason its
+ * data blocks must be 16KB in size no matter how large the array is.
+ */
+ total = 0;
+ STAILQ_FOREACH_SAFE(chunk, &os->dnodechunks, next, tmp) {
+ unsigned int i;
+
+ assert(chunk->nextfree <= os->dnodecount);
+ assert(chunk->nextfree <= DNODES_PER_CHUNK);
+
+ for (i = 0; i < chunk->nextfree; i += DNODES_PER_BLOCK) {
+ blkptr_t *bp;
+ uint64_t fill;
+
+ if (chunk->nextfree - i < DNODES_PER_BLOCK)
+ fill = DNODES_PER_BLOCK - (chunk->nextfree - i);
+ else
+ fill = 0;
+ bp = dnode_cursor_next(zfs, c,
+ (total + i) * sizeof(dnode_phys_t));
+ vdev_pwrite_dnode_indir(zfs, &os->phys->os_meta_dnode,
+ 0, fill, chunk->buf + i, DNODE_BLOCK_SIZE, loc, bp);
+ loc += DNODE_BLOCK_SIZE;
+ }
+ total += i;
+
+ free(chunk);
+ }
+ dnode_cursor_finish(zfs, c);
+ STAILQ_INIT(&os->dnodechunks);
+
+ /*
+ * Write the object set itself. The saved block pointer will be copied
+ * into the referencing DSL dataset or the uberblocks.
+ */
+ vdev_pwrite_data(zfs, DMU_OT_OBJSET, ZIO_CHECKSUM_FLETCHER_4, 0, 1,
+ os->phys, os->osblksz, os->osloc, &os->osbp);
+}
+
+void
+objset_write(zfs_opt_t *zfs, zfs_objset_t *os)
+{
+ struct dnode_cursor *c;
+ off_t dnodeloc, dnodesz;
+ uint64_t dnodecount;
+
+ /*
+ * There is a chicken-and-egg problem here when writing the MOS: we
+ * cannot write space maps before we're finished allocating space from
+ * the vdev, and we can't write the MOS without having allocated space
+ * for indirect dnode blocks. Thus, rather than lazily allocating
+ * indirect blocks for the meta-dnode (which would be simpler), they are
+ * allocated up-front and before writing space maps.
+ */
+ dnodecount = os->dnodecount;
+ if (os == zfs->mos)
+ dnodecount += zfs->mscount;
+ dnodesz = dnodecount * sizeof(dnode_phys_t);
+ c = dnode_cursor_init(zfs, os, &os->phys->os_meta_dnode, dnodesz,
+ DNODE_BLOCK_SIZE);
+ dnodesz = roundup2(dnodesz, DNODE_BLOCK_SIZE);
+ dnodeloc = objset_space_alloc(zfs, os, &dnodesz);
+
+ if (os == zfs->mos) {
+ vdev_spacemap_write(zfs);
+
+ /*
+ * We've finished allocating space, account for it in $MOS.
+ */
+ dsl_dir_size_set(zfs->mosdsldir, os->space);
+ }
+ _objset_write(zfs, os, c, dnodeloc);
+}
+
+dnode_phys_t *
+objset_dnode_bonus_alloc(zfs_objset_t *os, uint8_t type, uint8_t bonustype,
+ uint16_t bonuslen, uint64_t *idp)
+{
+ struct objset_dnode_chunk *chunk;
+ dnode_phys_t *dnode;
+
+ assert(bonuslen <= DN_OLD_MAX_BONUSLEN);
+ assert(!STAILQ_EMPTY(&os->dnodechunks));
+
+ chunk = STAILQ_LAST(&os->dnodechunks, objset_dnode_chunk, next);
+ if (chunk->nextfree == DNODES_PER_CHUNK) {
+ chunk = ecalloc(1, sizeof(*chunk));
+ STAILQ_INSERT_TAIL(&os->dnodechunks, chunk, next);
+ }
+ *idp = os->dnodecount++;
+ dnode = &chunk->buf[chunk->nextfree++];
+ dnode_init(dnode, type, bonustype, bonuslen);
+ dnode->dn_datablkszsec = os->osblksz >> MINBLOCKSHIFT;
+ return (dnode);
+}
+
+dnode_phys_t *
+objset_dnode_alloc(zfs_objset_t *os, uint8_t type, uint64_t *idp)
+{
+ return (objset_dnode_bonus_alloc(os, type, DMU_OT_NONE, 0, idp));
+}
+
+/*
+ * Look up a physical dnode by ID. This is not used often so a linear search is
+ * fine.
+ */
+dnode_phys_t *
+objset_dnode_lookup(zfs_objset_t *os, uint64_t id)
+{
+ struct objset_dnode_chunk *chunk;
+
+ assert(id > 0);
+ assert(id < os->dnodecount);
+
+ STAILQ_FOREACH(chunk, &os->dnodechunks, next) {
+ if (id < DNODES_PER_CHUNK)
+ return (&chunk->buf[id]);
+ id -= DNODES_PER_CHUNK;
+ }
+ assert(0);
+ return (NULL);
+}
+
+off_t
+objset_space_alloc(zfs_opt_t *zfs, zfs_objset_t *os, off_t *lenp)
+{
+ off_t loc;
+
+ loc = vdev_space_alloc(zfs, lenp);
+ os->space += *lenp;
+ return (loc);
+}
+
+uint64_t
+objset_space(const zfs_objset_t *os)
+{
+ return (os->space);
+}
+
+void
+objset_root_blkptr_copy(const zfs_objset_t *os, blkptr_t *bp)
+{
+ memcpy(bp, &os->osbp, sizeof(blkptr_t));
+}
diff --git a/usr.sbin/makefs/zfs/vdev.c b/usr.sbin/makefs/zfs/vdev.c
new file mode 100644
index 000000000000..1709a828b7c5
--- /dev/null
+++ b/usr.sbin/makefs/zfs/vdev.c
@@ -0,0 +1,435 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2022 The FreeBSD Foundation
+ *
+ * This software was developed by Mark Johnston under sponsorship from
+ * the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <assert.h>
+#include <fcntl.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <util.h>
+
+#include "zfs.h"
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunused-function"
+#include "zfs/fletcher.c"
+#include "zfs/sha256.c"
+#pragma clang diagnostic pop
+
+static void
+blkptr_set(blkptr_t *bp, off_t off, off_t size, uint8_t dntype, uint8_t level,
+ uint64_t fill, enum zio_checksum cksumt, zio_cksum_t *cksum)
+{
+ dva_t *dva;
+
+ assert(powerof2(size));
+
+ BP_ZERO(bp);
+ BP_SET_LSIZE(bp, size);
+ BP_SET_PSIZE(bp, size);
+ BP_SET_CHECKSUM(bp, cksumt);
+ BP_SET_COMPRESS(bp, ZIO_COMPRESS_OFF);
+ BP_SET_BYTEORDER(bp, ZFS_HOST_BYTEORDER);
+ BP_SET_BIRTH(bp, TXG, TXG);
+ BP_SET_LEVEL(bp, level);
+ BP_SET_FILL(bp, fill);
+ BP_SET_TYPE(bp, dntype);
+
+ dva = BP_IDENTITY(bp);
+ DVA_SET_VDEV(dva, 0);
+ DVA_SET_OFFSET(dva, off);
+ DVA_SET_ASIZE(dva, size);
+ memcpy(&bp->blk_cksum, cksum, sizeof(*cksum));
+}
+
+/*
+ * Write a block of data to the vdev. The offset is always relative to the end
+ * of the second leading vdev label.
+ *
+ * Consumers should generally use the helpers below, which provide block
+ * pointers and update dnode accounting, rather than calling this function
+ * directly.
+ */
+static void
+vdev_pwrite(const zfs_opt_t *zfs, const void *buf, size_t len, off_t off)
+{
+ ssize_t n;
+
+ assert(off >= 0 && off < zfs->asize);
+ assert(powerof2(len));
+ assert((off_t)len > 0 && off + (off_t)len > off &&
+ off + (off_t)len < zfs->asize);
+ if (zfs->spacemap != NULL) {
+ /*
+ * Verify that the blocks being written were in fact allocated.
+ *
+ * The space map isn't available once the on-disk space map is
+ * finalized, so this check doesn't quite catch everything.
+ */
+ assert(bit_ntest(zfs->spacemap, off >> zfs->ashift,
+ (off + len - 1) >> zfs->ashift, 1));
+ }
+
+ off += VDEV_LABEL_START_SIZE;
+ for (size_t sofar = 0; sofar < len; sofar += n) {
+ n = pwrite(zfs->fd, (const char *)buf + sofar, len - sofar,
+ off + sofar);
+ if (n < 0)
+ err(1, "pwrite");
+ assert(n > 0);
+ }
+}
+
+void
+vdev_pwrite_data(zfs_opt_t *zfs, uint8_t datatype, uint8_t cksumtype,
+ uint8_t level, uint64_t fill, const void *data, off_t sz, off_t loc,
+ blkptr_t *bp)
+{
+ zio_cksum_t cksum;
+
+ assert(cksumtype == ZIO_CHECKSUM_FLETCHER_4);
+
+ fletcher_4_native(data, sz, NULL, &cksum);
+ blkptr_set(bp, loc, sz, datatype, level, fill, cksumtype, &cksum);
+ vdev_pwrite(zfs, data, sz, loc);
+}
+
+void
+vdev_pwrite_dnode_indir(zfs_opt_t *zfs, dnode_phys_t *dnode, uint8_t level,
+ uint64_t fill, const void *data, off_t sz, off_t loc, blkptr_t *bp)
+{
+ vdev_pwrite_data(zfs, dnode->dn_type, dnode->dn_checksum, level, fill,
+ data, sz, loc, bp);
+
+ assert((dnode->dn_flags & DNODE_FLAG_USED_BYTES) != 0);
+ dnode->dn_used += sz;
+}
+
+void
+vdev_pwrite_dnode_data(zfs_opt_t *zfs, dnode_phys_t *dnode, const void *data,
+ off_t sz, off_t loc)
+{
+ vdev_pwrite_dnode_indir(zfs, dnode, 0, 1, data, sz, loc,
+ &dnode->dn_blkptr[0]);
+}
+
+static void
+vdev_label_set_checksum(void *buf, off_t off, off_t size)
+{
+ zio_cksum_t cksum;
+ zio_eck_t *eck;
+
+ assert(size > 0 && (size_t)size >= sizeof(zio_eck_t));
+
+ eck = (zio_eck_t *)((char *)buf + size) - 1;
+ eck->zec_magic = ZEC_MAGIC;
+ ZIO_SET_CHECKSUM(&eck->zec_cksum, off, 0, 0, 0);
+ zio_checksum_SHA256(buf, size, NULL, &cksum);
+ eck->zec_cksum = cksum;
+}
+
+/*
+ * Set embedded checksums and write the label at the specified index.
+ */
+void
+vdev_label_write(zfs_opt_t *zfs, int ind, const vdev_label_t *labelp)
+{
+ vdev_label_t *label;
+ ssize_t n;
+ off_t blksz, loff;
+
+ assert(ind >= 0 && ind < VDEV_LABELS);
+
+ /*
+ * Make a copy since we have to modify the label to set checksums.
+ */
+ label = ecalloc(1, sizeof(*label));
+ memcpy(label, labelp, sizeof(*label));
+
+ if (ind < 2)
+ loff = ind * sizeof(*label);
+ else
+ loff = zfs->vdevsize - (VDEV_LABELS - ind) * sizeof(*label);
+
+ /*
+ * Set the verifier checksum for the boot block. We don't use it, but
+ * the FreeBSD loader reads it and will complain if the checksum isn't
+ * valid.
+ */
+ vdev_label_set_checksum(&label->vl_be,
+ loff + __offsetof(vdev_label_t, vl_be), sizeof(label->vl_be));
+
+ /*
+ * Set the verifier checksum for the label.
+ */
+ vdev_label_set_checksum(&label->vl_vdev_phys,
+ loff + __offsetof(vdev_label_t, vl_vdev_phys),
+ sizeof(label->vl_vdev_phys));
+
+ /*
+ * Set the verifier checksum for the uberblocks. There is one uberblock
+ * per sector; for example, with an ashift of 12 we end up with
+ * 128KB/4KB=32 copies of the uberblock in the ring.
+ */
+ blksz = 1 << zfs->ashift;
+ assert(sizeof(label->vl_uberblock) % blksz == 0);
+ for (size_t roff = 0; roff < sizeof(label->vl_uberblock);
+ roff += blksz) {
+ vdev_label_set_checksum(&label->vl_uberblock[0] + roff,
+ loff + __offsetof(vdev_label_t, vl_uberblock) + roff,
+ blksz);
+ }
+
+ n = pwrite(zfs->fd, label, sizeof(*label), loff);
+ if (n < 0)
+ err(1, "writing vdev label");
+ assert(n == sizeof(*label));
+
+ free(label);
+}
+
+/*
+ * Find a chunk of contiguous free space of length *lenp, according to the
+ * following rules:
+ * 1. If the length is less than or equal to 128KB, the returned run's length
+ * will be the smallest power of 2 equal to or larger than the length.
+ * 2. If the length is larger than 128KB, the returned run's length will be
+ * the smallest multiple of 128KB that is larger than the length.
+ * 3. The returned run's length will be size-aligned up to 128KB.
+ *
+ * XXX-MJ the third rule isn't actually required, so this can just be a dumb
+ * bump allocator. Maybe there's some benefit to keeping large blocks aligned,
+ * so let's keep it for now and hope we don't get too much fragmentation.
+ * Alternately we could try to allocate all blocks of a certain size from the
+ * same metaslab.
+ */
+off_t
+vdev_space_alloc(zfs_opt_t *zfs, off_t *lenp)
+{
+ off_t len;
+ int align, loc, minblksz, nbits;
+
+ minblksz = 1 << zfs->ashift;
+ len = roundup2(*lenp, minblksz);
+
+ assert(len != 0);
+ assert(len / minblksz <= INT_MAX);
+
+ if (len < MAXBLOCKSIZE) {
+ if ((len & (len - 1)) != 0)
+ len = (off_t)1 << flsll(len);
+ align = len / minblksz;
+ } else {
+ len = roundup2(len, MAXBLOCKSIZE);
+ align = MAXBLOCKSIZE / minblksz;
+ }
+
+ for (loc = 0, nbits = len / minblksz;; loc = roundup2(loc, align)) {
+ bit_ffc_area_at(zfs->spacemap, loc, zfs->spacemapbits, nbits,
+ &loc);
+ if (loc == -1) {
+ errx(1, "failed to find %ju bytes of space",
+ (uintmax_t)len);
+ }
+ if ((loc & (align - 1)) == 0)
+ break;
+ }
+ assert(loc + nbits > loc);
+ bit_nset(zfs->spacemap, loc, loc + nbits - 1);
+ *lenp = len;
+
+ return ((off_t)loc << zfs->ashift);
+}
+
+static void
+vdev_spacemap_init(zfs_opt_t *zfs)
+{
+ uint64_t nbits;
+
+ assert(powerof2(zfs->mssize));
+
+ nbits = rounddown2(zfs->asize, zfs->mssize) >> zfs->ashift;
+ if (nbits > INT_MAX) {
+ /*
+ * With the smallest block size of 512B, the limit on the image
+ * size is 2TB. That should be enough for anyone.
+ */
+ errx(1, "image size is too large");
+ }
+ zfs->spacemapbits = (int)nbits;
+ zfs->spacemap = bit_alloc(zfs->spacemapbits);
+ if (zfs->spacemap == NULL)
+ err(1, "bitstring allocation failed");
+}
+
+void
+vdev_spacemap_write(zfs_opt_t *zfs)
+{
+ dnode_phys_t *objarr;
+ bitstr_t *spacemap;
+ uint64_t *objarrblk;
+ off_t smblksz, objarrblksz, objarrloc;
+
+ struct {
+ dnode_phys_t *dnode;
+ uint64_t dnid;
+ off_t loc;
+ } *sma;
+
+ objarrblksz = sizeof(uint64_t) * zfs->mscount;
+ assert(objarrblksz <= MAXBLOCKSIZE);
+ objarrloc = objset_space_alloc(zfs, zfs->mos, &objarrblksz);
+ objarrblk = ecalloc(1, objarrblksz);
+
+ objarr = objset_dnode_lookup(zfs->mos, zfs->objarrid);
+ objarr->dn_datablkszsec = objarrblksz >> MINBLOCKSHIFT;
+
+ /*
+ * Use the smallest block size for space maps. The space allocation
+ * algorithm should aim to minimize the number of holes.
+ */
+ smblksz = 1 << zfs->ashift;
+
+ /*
+ * First allocate dnodes and space for all of our space maps. No more
+ * space can be allocated from the vdev after this point.
+ */
+ sma = ecalloc(zfs->mscount, sizeof(*sma));
+ for (uint64_t i = 0; i < zfs->mscount; i++) {
+ sma[i].dnode = objset_dnode_bonus_alloc(zfs->mos,
+ DMU_OT_SPACE_MAP, DMU_OT_SPACE_MAP_HEADER,
+ sizeof(space_map_phys_t), &sma[i].dnid);
+ sma[i].loc = objset_space_alloc(zfs, zfs->mos, &smblksz);
+ }
+ spacemap = zfs->spacemap;
+ zfs->spacemap = NULL;
+
+ /*
+ * Now that the set of allocated space is finalized, populate each space
+ * map and write it to the vdev.
+ */
+ for (uint64_t i = 0; i < zfs->mscount; i++) {
+ space_map_phys_t *sm;
+ uint64_t alloc, length, *smblk;
+ int shift, startb, endb, srunb, erunb;
+
+ /*
+ * We only allocate a single block for this space map, but
+ * OpenZFS assumes that a space map object with sufficient bonus
+ * space supports histograms.
+ */
+ sma[i].dnode->dn_nblkptr = 3;
+ sma[i].dnode->dn_datablkszsec = smblksz >> MINBLOCKSHIFT;
+
+ smblk = ecalloc(1, smblksz);
+
+ alloc = length = 0;
+ shift = zfs->msshift - zfs->ashift;
+ for (srunb = startb = i * (1 << shift),
+ endb = (i + 1) * (1 << shift);
+ srunb < endb; srunb = erunb) {
+ uint64_t runlen, runoff;
+
+ /* Find a run of allocated space. */
+ bit_ffs_at(spacemap, srunb, zfs->spacemapbits, &srunb);
+ if (srunb == -1 || srunb >= endb)
+ break;
+
+ bit_ffc_at(spacemap, srunb, zfs->spacemapbits, &erunb);
+ if (erunb == -1 || erunb > endb)
+ erunb = endb;
+
+ /*
+ * The space represented by [srunb, erunb) has been
+ * allocated. Add a record to the space map to indicate
+ * this. Run offsets are relative to the beginning of
+ * the metaslab.
+ */
+ runlen = erunb - srunb;
+ runoff = srunb - startb;
+
+ assert(length * sizeof(uint64_t) < (uint64_t)smblksz);
+ smblk[length] = SM_PREFIX_ENCODE(SM2_PREFIX) |
+ SM2_RUN_ENCODE(runlen) | SM2_VDEV_ENCODE(0);
+ smblk[length + 1] = SM2_TYPE_ENCODE(SM_ALLOC) |
+ SM2_OFFSET_ENCODE(runoff);
+
+ alloc += runlen << zfs->ashift;
+ length += 2;
+ }
+
+ sm = DN_BONUS(sma[i].dnode);
+ sm->smp_length = length * sizeof(uint64_t);
+ sm->smp_alloc = alloc;
+
+ vdev_pwrite_dnode_data(zfs, sma[i].dnode, smblk, smblksz,
+ sma[i].loc);
+ free(smblk);
+
+ /* Record this space map in the space map object array. */
+ objarrblk[i] = sma[i].dnid;
+ }
+
+ /*
+ * All of the space maps are written, now write the object array.
+ */
+ vdev_pwrite_dnode_data(zfs, objarr, objarrblk, objarrblksz, objarrloc);
+ free(objarrblk);
+
+ assert(zfs->spacemap == NULL);
+ free(spacemap);
+ free(sma);
+}
+
+void
+vdev_init(zfs_opt_t *zfs, const char *image)
+{
+ assert(zfs->ashift >= MINBLOCKSHIFT);
+
+ zfs->fd = open(image, O_RDWR | O_CREAT | O_TRUNC, 0644);
+ if (zfs->fd == -1)
+ err(1, "Can't open `%s' for writing", image);
+ if (ftruncate(zfs->fd, zfs->vdevsize) != 0)
+ err(1, "Failed to extend image file `%s'", image);
+
+ vdev_spacemap_init(zfs);
+}
+
+void
+vdev_fini(zfs_opt_t *zfs)
+{
+ assert(zfs->spacemap == NULL);
+
+ if (zfs->fd != -1) {
+ if (close(zfs->fd) != 0)
+ err(1, "close");
+ zfs->fd = -1;
+ }
+}
diff --git a/usr.sbin/makefs/zfs/zap.c b/usr.sbin/makefs/zfs/zap.c
new file mode 100644
index 000000000000..398c0fbf029c
--- /dev/null
+++ b/usr.sbin/makefs/zfs/zap.c
@@ -0,0 +1,551 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2022 The FreeBSD Foundation
+ *
+ * This software was developed by Mark Johnston under sponsorship from
+ * the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/endian.h>
+
+#include <assert.h>
+#include <stddef.h>
+#include <string.h>
+
+#include <util.h>
+
+#include "makefs.h"
+#include "zfs.h"
+
+typedef struct zfs_zap_entry {
+ char *name; /* entry key, private copy */
+ uint64_t hash; /* key hash */
+ union {
+ uint8_t *valp;
+ uint16_t *val16p;
+ uint32_t *val32p;
+ uint64_t *val64p;
+ }; /* entry value, an integer array */
+ uint64_t val64; /* embedded value for a common case */
+ size_t intsz; /* array element size; 1, 2, 4 or 8 */
+ size_t intcnt; /* array size */
+ STAILQ_ENTRY(zfs_zap_entry) next;
+} zfs_zap_entry_t;
+
+struct zfs_zap {
+ STAILQ_HEAD(, zfs_zap_entry) kvps;
+ uint64_t hashsalt; /* key hash input */
+ unsigned long kvpcnt; /* number of key-value pairs */
+ unsigned long chunks; /* count of chunks needed for fat ZAP */
+ bool micro; /* can this be a micro ZAP? */
+
+ dnode_phys_t *dnode; /* backpointer */
+ zfs_objset_t *os; /* backpointer */
+};
+
+static uint16_t
+zap_entry_chunks(zfs_zap_entry_t *ent)
+{
+ return (1 + howmany(strlen(ent->name) + 1, ZAP_LEAF_ARRAY_BYTES) +
+ howmany(ent->intsz * ent->intcnt, ZAP_LEAF_ARRAY_BYTES));
+}
+
+static uint64_t
+zap_hash(uint64_t salt, const char *name)
+{
+ static uint64_t crc64_table[256];
+ const uint64_t crc64_poly = 0xC96C5795D7870F42UL;
+ const uint8_t *cp;
+ uint64_t crc;
+ uint8_t c;
+
+ assert(salt != 0);
+ if (crc64_table[128] == 0) {
+ for (int i = 0; i < 256; i++) {
+ uint64_t *t;
+
+ t = crc64_table + i;
+ *t = i;
+ for (int j = 8; j > 0; j--)
+ *t = (*t >> 1) ^ (-(*t & 1) & crc64_poly);
+ }
+ }
+ assert(crc64_table[128] == crc64_poly);
+
+ for (cp = (const uint8_t *)name, crc = salt; (c = *cp) != '\0'; cp++)
+ crc = (crc >> 8) ^ crc64_table[(crc ^ c) & 0xFF];
+
+ /*
+ * Only use 28 bits, since we need 4 bits in the cookie for the
+ * collision differentiator. We MUST use the high bits, since
+ * those are the ones that we first pay attention to when
+ * choosing the bucket.
+ */
+ crc &= ~((1ULL << (64 - ZAP_HASHBITS)) - 1);
+
+ return (crc);
+}
+
+zfs_zap_t *
+zap_alloc(zfs_objset_t *os, dnode_phys_t *dnode)
+{
+ zfs_zap_t *zap;
+
+ zap = ecalloc(1, sizeof(*zap));
+ STAILQ_INIT(&zap->kvps);
+ zap->hashsalt = ((uint64_t)random() << 32) | random();
+ zap->micro = true;
+ zap->kvpcnt = 0;
+ zap->chunks = 0;
+ zap->dnode = dnode;
+ zap->os = os;
+ return (zap);
+}
+
+void
+zap_add(zfs_zap_t *zap, const char *name, size_t intsz, size_t intcnt,
+ const uint8_t *val)
+{
+ zfs_zap_entry_t *ent;
+
+ assert(intsz == 1 || intsz == 2 || intsz == 4 || intsz == 8);
+ assert(strlen(name) + 1 <= ZAP_MAXNAMELEN);
+ assert(intcnt <= ZAP_MAXVALUELEN && intcnt * intsz <= ZAP_MAXVALUELEN);
+
+ ent = ecalloc(1, sizeof(*ent));
+ ent->name = estrdup(name);
+ ent->hash = zap_hash(zap->hashsalt, ent->name);
+ ent->intsz = intsz;
+ ent->intcnt = intcnt;
+ if (intsz == sizeof(uint64_t) && intcnt == 1) {
+ /*
+ * Micro-optimization to elide a memory allocation in that most
+ * common case where this is a directory entry.
+ */
+ ent->val64p = &ent->val64;
+ } else {
+ ent->valp = ecalloc(intcnt, intsz);
+ }
+ memcpy(ent->valp, val, intcnt * intsz);
+ zap->kvpcnt++;
+ zap->chunks += zap_entry_chunks(ent);
+ STAILQ_INSERT_TAIL(&zap->kvps, ent, next);
+
+ if (zap->micro && (intcnt != 1 || intsz != sizeof(uint64_t) ||
+ strlen(name) + 1 > MZAP_NAME_LEN || zap->kvpcnt > MZAP_ENT_MAX))
+ zap->micro = false;
+}
+
+void
+zap_add_uint64(zfs_zap_t *zap, const char *name, uint64_t val)
+{
+ zap_add(zap, name, sizeof(uint64_t), 1, (uint8_t *)&val);
+}
+
+void
+zap_add_string(zfs_zap_t *zap, const char *name, const char *val)
+{
+ zap_add(zap, name, 1, strlen(val) + 1, val);
+}
+
+bool
+zap_entry_exists(zfs_zap_t *zap, const char *name)
+{
+ zfs_zap_entry_t *ent;
+
+ STAILQ_FOREACH(ent, &zap->kvps, next) {
+ if (strcmp(ent->name, name) == 0)
+ return (true);
+ }
+ return (false);
+}
+
+static void
+zap_micro_write(zfs_opt_t *zfs, zfs_zap_t *zap)
+{
+ dnode_phys_t *dnode;
+ zfs_zap_entry_t *ent;
+ mzap_phys_t *mzap;
+ mzap_ent_phys_t *ment;
+ off_t bytes, loc;
+
+ memset(zfs->filebuf, 0, sizeof(zfs->filebuf));
+ mzap = (mzap_phys_t *)&zfs->filebuf[0];
+ mzap->mz_block_type = ZBT_MICRO;
+ mzap->mz_salt = zap->hashsalt;
+ mzap->mz_normflags = 0;
+
+ bytes = sizeof(*mzap) + (zap->kvpcnt - 1) * sizeof(*ment);
+ assert(bytes <= (off_t)MZAP_MAX_BLKSZ);
+
+ ment = &mzap->mz_chunk[0];
+ STAILQ_FOREACH(ent, &zap->kvps, next) {
+ memcpy(&ment->mze_value, ent->valp, ent->intsz * ent->intcnt);
+ ment->mze_cd = 0; /* XXX-MJ */
+ strlcpy(ment->mze_name, ent->name, sizeof(ment->mze_name));
+ ment++;
+ }
+
+ loc = objset_space_alloc(zfs, zap->os, &bytes);
+
+ dnode = zap->dnode;
+ dnode->dn_maxblkid = 0;
+ dnode->dn_datablkszsec = bytes >> MINBLOCKSHIFT;
+ dnode->dn_flags = DNODE_FLAG_USED_BYTES;
+
+ vdev_pwrite_dnode_data(zfs, dnode, zfs->filebuf, bytes, loc);
+}
+
+/*
+ * Write some data to the fat ZAP leaf chunk starting at index "li".
+ *
+ * Note that individual integers in the value may be split among consecutive
+ * leaves.
+ */
+static void
+zap_fat_write_array_chunk(zap_leaf_t *l, uint16_t li, size_t sz,
+ const uint8_t *val)
+{
+ struct zap_leaf_array *la;
+
+ assert(sz <= ZAP_MAXVALUELEN);
+
+ for (uint16_t n, resid = sz; resid > 0; resid -= n, val += n, li++) {
+ n = MIN(resid, ZAP_LEAF_ARRAY_BYTES);
+
+ la = &ZAP_LEAF_CHUNK(l, li).l_array;
+ assert(la->la_type == ZAP_CHUNK_FREE);
+ la->la_type = ZAP_CHUNK_ARRAY;
+ memcpy(la->la_array, val, n);
+ la->la_next = li + 1;
+ }
+ la->la_next = 0xffff;
+}
+
+/*
+ * Find the shortest hash prefix length which lets us distribute keys without
+ * overflowing a leaf block. This is not (space) optimal, but is simple, and
+ * directories large enough to overflow a single 128KB leaf block are uncommon.
+ */
+static unsigned int
+zap_fat_write_prefixlen(zfs_zap_t *zap, zap_leaf_t *l)
+{
+ zfs_zap_entry_t *ent;
+ unsigned int prefixlen;
+
+ if (zap->chunks <= ZAP_LEAF_NUMCHUNKS(l)) {
+ /*
+ * All chunks will fit in a single leaf block.
+ */
+ return (0);
+ }
+
+ for (prefixlen = 1; prefixlen < (unsigned int)l->l_bs; prefixlen++) {
+ uint32_t *leafchunks;
+
+ leafchunks = ecalloc(1u << prefixlen, sizeof(*leafchunks));
+ STAILQ_FOREACH(ent, &zap->kvps, next) {
+ uint64_t li;
+ uint16_t chunks;
+
+ li = ZAP_HASH_IDX(ent->hash, prefixlen);
+
+ chunks = zap_entry_chunks(ent);
+ if (ZAP_LEAF_NUMCHUNKS(l) - leafchunks[li] < chunks) {
+ /*
+ * Not enough space, grow the prefix and retry.
+ */
+ break;
+ }
+ leafchunks[li] += chunks;
+ }
+ free(leafchunks);
+
+ if (ent == NULL) {
+ /*
+ * Everything fits, we're done.
+ */
+ break;
+ }
+ }
+
+ /*
+ * If this fails, then we need to expand the pointer table. For now
+ * this situation is unhandled since it is hard to trigger.
+ */
+ assert(prefixlen < (unsigned int)l->l_bs);
+
+ return (prefixlen);
+}
+
+/*
+ * Initialize a fat ZAP leaf block.
+ */
+static void
+zap_fat_write_leaf_init(zap_leaf_t *l, uint64_t prefix, int prefixlen)
+{
+ zap_leaf_phys_t *leaf;
+
+ leaf = l->l_phys;
+
+ leaf->l_hdr.lh_block_type = ZBT_LEAF;
+ leaf->l_hdr.lh_magic = ZAP_LEAF_MAGIC;
+ leaf->l_hdr.lh_nfree = ZAP_LEAF_NUMCHUNKS(l);
+ leaf->l_hdr.lh_prefix = prefix;
+ leaf->l_hdr.lh_prefix_len = prefixlen;
+
+ /* Initialize the leaf hash table. */
+ assert(leaf->l_hdr.lh_nfree < 0xffff);
+ memset(leaf->l_hash, 0xff,
+ ZAP_LEAF_HASH_NUMENTRIES(l) * sizeof(*leaf->l_hash));
+
+ /* Initialize the leaf chunks. */
+ for (uint16_t i = 0; i < ZAP_LEAF_NUMCHUNKS(l); i++) {
+ struct zap_leaf_free *lf;
+
+ lf = &ZAP_LEAF_CHUNK(l, i).l_free;
+ lf->lf_type = ZAP_CHUNK_FREE;
+ if (i + 1 == ZAP_LEAF_NUMCHUNKS(l))
+ lf->lf_next = 0xffff;
+ else
+ lf->lf_next = i + 1;
+ }
+}
+
+static void
+zap_fat_write(zfs_opt_t *zfs, zfs_zap_t *zap)
+{
+ struct dnode_cursor *c;
+ zap_leaf_t l;
+ zap_phys_t *zaphdr;
+ struct zap_table_phys *zt;
+ zfs_zap_entry_t *ent;
+ dnode_phys_t *dnode;
+ uint8_t *leafblks;
+ uint64_t lblkcnt, *ptrhasht;
+ off_t loc, blksz;
+ size_t blkshift;
+ unsigned int prefixlen;
+ int ptrcnt;
+
+ /*
+ * For simplicity, always use the largest block size. This should be ok
+ * since most directories will be micro ZAPs, but it's space inefficient
+ * for small ZAPs and might need to be revisited.
+ */
+ blkshift = MAXBLOCKSHIFT;
+ blksz = (off_t)1 << blkshift;
+
+ /*
+ * Embedded pointer tables give up to 8192 entries. This ought to be
+ * enough for anything except massive directories.
+ */
+ ptrcnt = (blksz / 2) / sizeof(uint64_t);
+
+ memset(zfs->filebuf, 0, sizeof(zfs->filebuf));
+ zaphdr = (zap_phys_t *)&zfs->filebuf[0];
+ zaphdr->zap_block_type = ZBT_HEADER;
+ zaphdr->zap_magic = ZAP_MAGIC;
+ zaphdr->zap_num_entries = zap->kvpcnt;
+ zaphdr->zap_salt = zap->hashsalt;
+
+ l.l_bs = blkshift;
+ l.l_phys = NULL;
+
+ zt = &zaphdr->zap_ptrtbl;
+ zt->zt_blk = 0;
+ zt->zt_numblks = 0;
+ zt->zt_shift = flsll(ptrcnt) - 1;
+ zt->zt_nextblk = 0;
+ zt->zt_blks_copied = 0;
+
+ /*
+ * How many leaf blocks do we need? Initialize them and update the
+ * header.
+ */
+ prefixlen = zap_fat_write_prefixlen(zap, &l);
+ lblkcnt = 1 << prefixlen;
+ leafblks = ecalloc(lblkcnt, blksz);
+ for (unsigned int li = 0; li < lblkcnt; li++) {
+ l.l_phys = (zap_leaf_phys_t *)(leafblks + li * blksz);
+ zap_fat_write_leaf_init(&l, li, prefixlen);
+ }
+ zaphdr->zap_num_leafs = lblkcnt;
+ zaphdr->zap_freeblk = lblkcnt + 1;
+
+ /*
+ * For each entry, figure out which leaf block it belongs to based on
+ * the upper bits of its hash, allocate chunks from that leaf, and fill
+ * them out.
+ */
+ ptrhasht = (uint64_t *)(&zfs->filebuf[0] + blksz / 2);
+ STAILQ_FOREACH(ent, &zap->kvps, next) {
+ struct zap_leaf_entry *le;
+ uint16_t *lptr;
+ uint64_t hi, li;
+ uint16_t namelen, nchunks, nnamechunks, nvalchunks;
+
+ hi = ZAP_HASH_IDX(ent->hash, zt->zt_shift);
+ li = ZAP_HASH_IDX(ent->hash, prefixlen);
+ assert(ptrhasht[hi] == 0 || ptrhasht[hi] == li + 1);
+ ptrhasht[hi] = li + 1;
+ l.l_phys = (zap_leaf_phys_t *)(leafblks + li * blksz);
+
+ namelen = strlen(ent->name) + 1;
+
+ /*
+ * How many leaf chunks do we need for this entry?
+ */
+ nnamechunks = howmany(namelen, ZAP_LEAF_ARRAY_BYTES);
+ nvalchunks = howmany(ent->intcnt,
+ ZAP_LEAF_ARRAY_BYTES / ent->intsz);
+ nchunks = 1 + nnamechunks + nvalchunks;
+
+ /*
+ * Allocate a run of free leaf chunks for this entry,
+ * potentially extending a hash chain.
+ */
+ assert(l.l_phys->l_hdr.lh_nfree >= nchunks);
+ l.l_phys->l_hdr.lh_nfree -= nchunks;
+ l.l_phys->l_hdr.lh_nentries++;
+ lptr = ZAP_LEAF_HASH_ENTPTR(&l, ent->hash);
+ while (*lptr != 0xffff) {
+ assert(*lptr < ZAP_LEAF_NUMCHUNKS(&l));
+ le = ZAP_LEAF_ENTRY(&l, *lptr);
+ assert(le->le_type == ZAP_CHUNK_ENTRY);
+ le->le_cd++;
+ lptr = &le->le_next;
+ }
+ *lptr = l.l_phys->l_hdr.lh_freelist;
+ l.l_phys->l_hdr.lh_freelist += nchunks;
+ assert(l.l_phys->l_hdr.lh_freelist <=
+ ZAP_LEAF_NUMCHUNKS(&l));
+ if (l.l_phys->l_hdr.lh_freelist ==
+ ZAP_LEAF_NUMCHUNKS(&l))
+ l.l_phys->l_hdr.lh_freelist = 0xffff;
+
+ /*
+ * Integer values must be stored in big-endian format.
+ */
+ switch (ent->intsz) {
+ case 1:
+ break;
+ case 2:
+ for (uint16_t *v = ent->val16p;
+ v - ent->val16p < (ptrdiff_t)ent->intcnt;
+ v++)
+ *v = htobe16(*v);
+ break;
+ case 4:
+ for (uint32_t *v = ent->val32p;
+ v - ent->val32p < (ptrdiff_t)ent->intcnt;
+ v++)
+ *v = htobe32(*v);
+ break;
+ case 8:
+ for (uint64_t *v = ent->val64p;
+ v - ent->val64p < (ptrdiff_t)ent->intcnt;
+ v++)
+ *v = htobe64(*v);
+ break;
+ default:
+ assert(0);
+ }
+
+ /*
+ * Finally, write out the leaf chunks for this entry.
+ */
+ le = ZAP_LEAF_ENTRY(&l, *lptr);
+ assert(le->le_type == ZAP_CHUNK_FREE);
+ le->le_type = ZAP_CHUNK_ENTRY;
+ le->le_next = 0xffff;
+ le->le_name_chunk = *lptr + 1;
+ le->le_name_numints = namelen;
+ le->le_value_chunk = *lptr + 1 + nnamechunks;
+ le->le_value_intlen = ent->intsz;
+ le->le_value_numints = ent->intcnt;
+ le->le_hash = ent->hash;
+ zap_fat_write_array_chunk(&l, *lptr + 1, namelen, ent->name);
+ zap_fat_write_array_chunk(&l, *lptr + 1 + nnamechunks,
+ ent->intcnt * ent->intsz, ent->valp);
+ }
+
+ /*
+ * Initialize unused slots of the pointer table.
+ */
+ for (int i = 0; i < ptrcnt; i++)
+ if (ptrhasht[i] == 0)
+ ptrhasht[i] = (i >> (zt->zt_shift - prefixlen)) + 1;
+
+ /*
+ * Write the whole thing to disk.
+ */
+ dnode = zap->dnode;
+ dnode->dn_nblkptr = 1;
+ dnode->dn_datablkszsec = blksz >> MINBLOCKSHIFT;
+ dnode->dn_maxblkid = lblkcnt + 1;
+ dnode->dn_flags = DNODE_FLAG_USED_BYTES;
+
+ c = dnode_cursor_init(zfs, zap->os, zap->dnode,
+ (lblkcnt + 1) * blksz, blksz);
+
+ loc = objset_space_alloc(zfs, zap->os, &blksz);
+ vdev_pwrite_dnode_indir(zfs, dnode, 0, 1, zfs->filebuf, blksz, loc,
+ dnode_cursor_next(zfs, c, 0));
+
+ for (uint64_t i = 0; i < lblkcnt; i++) {
+ loc = objset_space_alloc(zfs, zap->os, &blksz);
+ vdev_pwrite_dnode_indir(zfs, dnode, 0, 1, leafblks + i * blksz,
+ blksz, loc, dnode_cursor_next(zfs, c, (i + 1) * blksz));
+ }
+
+ dnode_cursor_finish(zfs, c);
+
+ free(leafblks);
+}
+
+void
+zap_write(zfs_opt_t *zfs, zfs_zap_t *zap)
+{
+ zfs_zap_entry_t *ent;
+
+ if (zap->micro) {
+ zap_micro_write(zfs, zap);
+ } else {
+ assert(!STAILQ_EMPTY(&zap->kvps));
+ assert(zap->kvpcnt > 0);
+ zap_fat_write(zfs, zap);
+ }
+
+ while ((ent = STAILQ_FIRST(&zap->kvps)) != NULL) {
+ STAILQ_REMOVE_HEAD(&zap->kvps, next);
+ if (ent->val64p != &ent->val64)
+ free(ent->valp);
+ free(ent->name);
+ free(ent);
+ }
+ free(zap);
+}
diff --git a/usr.sbin/makefs/zfs/zfs.h b/usr.sbin/makefs/zfs/zfs.h
new file mode 100644
index 000000000000..b92e2c035669
--- /dev/null
+++ b/usr.sbin/makefs/zfs/zfs.h
@@ -0,0 +1,167 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2022 The FreeBSD Foundation
+ *
+ * This software was developed by Mark Johnston under sponsorship from
+ * the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _MAKEFS_ZFS_H_
+#define _MAKEFS_ZFS_H_
+
+#include <sys/types.h>
+#include <sys/queue.h>
+
+#include <bitstring.h>
+#include <stdbool.h>
+
+#include "makefs.h"
+
+#include "zfs/nvlist.h"
+#define ASSERT assert
+#include "zfs/zfsimpl.h"
+
+#define MAXBLOCKSHIFT 17 /* 128KB */
+#define MAXBLOCKSIZE ((off_t)(1 << MAXBLOCKSHIFT))
+_Static_assert(MAXBLOCKSIZE == SPA_OLDMAXBLOCKSIZE, "");
+#define MINBLOCKSHIFT 9 /* 512B */
+#define MINBLOCKSIZE ((off_t)(1 << MINBLOCKSHIFT))
+_Static_assert(MINBLOCKSIZE == SPA_MINBLOCKSIZE, "");
+#define MINDEVSIZE ((off_t)SPA_MINDEVSIZE)
+
+/* All data was written in this transaction group. */
+#define TXG 4
+
+typedef struct zfs_dsl_dataset zfs_dsl_dataset_t;
+typedef struct zfs_dsl_dir zfs_dsl_dir_t;
+typedef struct zfs_objset zfs_objset_t;
+typedef struct zfs_zap zfs_zap_t;
+
+struct dataset_desc {
+ char *params;
+ STAILQ_ENTRY(dataset_desc) next;
+};
+
+typedef struct {
+ bool nowarn;
+
+ /* I/O buffer, just for convenience. */
+ char filebuf[MAXBLOCKSIZE];
+
+ /* Pool parameters. */
+ const char *poolname;
+ char *rootpath; /* implicit mount point prefix */
+ char *bootfs; /* bootable dataset, pool property */
+ int ashift; /* vdev block size */
+ uint64_t mssize; /* metaslab size */
+ STAILQ_HEAD(, dataset_desc) datasetdescs; /* non-root dataset descrs */
+
+ /* Pool state. */
+ uint64_t poolguid; /* pool and root vdev GUID */
+ zfs_zap_t *poolprops;
+
+ /* MOS state. */
+ zfs_objset_t *mos; /* meta object set */
+ uint64_t objarrid; /* space map object array */
+
+ /* DSL state. */
+ zfs_dsl_dir_t *rootdsldir; /* root DSL directory */
+ zfs_dsl_dataset_t *rootds;
+ zfs_dsl_dir_t *origindsldir; /* $ORIGIN */
+ zfs_dsl_dataset_t *originds;
+ zfs_dsl_dataset_t *snapds;
+ zfs_zap_t *cloneszap;
+ zfs_dsl_dir_t *freedsldir; /* $FREE */
+ zfs_dsl_dir_t *mosdsldir; /* $MOS */
+
+ /* vdev state. */
+ int fd; /* vdev disk fd */
+ uint64_t vdevguid; /* disk vdev GUID */
+ off_t vdevsize; /* vdev size, including labels */
+ off_t asize; /* vdev size, excluding labels */
+ bitstr_t *spacemap; /* space allocation tracking */
+ int spacemapbits; /* one bit per ashift-sized block */
+ uint64_t msshift; /* log2(metaslab size) */
+ uint64_t mscount; /* number of metaslabs for this vdev */
+} zfs_opt_t;
+
+/* dsl.c */
+void dsl_init(zfs_opt_t *);
+const char *dsl_dir_fullname(const zfs_dsl_dir_t *);
+uint64_t dsl_dir_id(zfs_dsl_dir_t *);
+uint64_t dsl_dir_dataset_id(zfs_dsl_dir_t *);
+void dsl_dir_foreach(zfs_opt_t *, zfs_dsl_dir_t *,
+ void (*)(zfs_opt_t *, zfs_dsl_dir_t *, void *), void *);
+int dsl_dir_get_canmount(zfs_dsl_dir_t *, uint64_t *);
+char *dsl_dir_get_mountpoint(zfs_opt_t *, zfs_dsl_dir_t *);
+bool dsl_dir_has_dataset(zfs_dsl_dir_t *);
+bool dsl_dir_dataset_has_objset(zfs_dsl_dir_t *);
+void dsl_dir_dataset_write(zfs_opt_t *, zfs_objset_t *, zfs_dsl_dir_t *);
+void dsl_dir_size_set(zfs_dsl_dir_t *, uint64_t);
+void dsl_write(zfs_opt_t *);
+
+/* fs.c */
+void fs_build(zfs_opt_t *, int, fsnode *);
+
+/* objset.c */
+zfs_objset_t *objset_alloc(zfs_opt_t *zfs, uint64_t type);
+off_t objset_space_alloc(zfs_opt_t *, zfs_objset_t *, off_t *);
+dnode_phys_t *objset_dnode_alloc(zfs_objset_t *, uint8_t, uint64_t *);
+dnode_phys_t *objset_dnode_bonus_alloc(zfs_objset_t *, uint8_t, uint8_t,
+ uint16_t, uint64_t *);
+dnode_phys_t *objset_dnode_lookup(zfs_objset_t *, uint64_t);
+void objset_root_blkptr_copy(const zfs_objset_t *, blkptr_t *);
+uint64_t objset_space(const zfs_objset_t *);
+void objset_write(zfs_opt_t *zfs, zfs_objset_t *os);
+
+/* vdev.c */
+void vdev_init(zfs_opt_t *, const char *);
+off_t vdev_space_alloc(zfs_opt_t *zfs, off_t *lenp);
+void vdev_pwrite_data(zfs_opt_t *zfs, uint8_t datatype, uint8_t cksumtype,
+ uint8_t level, uint64_t fill, const void *data, off_t sz, off_t loc,
+ blkptr_t *bp);
+void vdev_pwrite_dnode_indir(zfs_opt_t *zfs, dnode_phys_t *dnode, uint8_t level,
+ uint64_t fill, const void *data, off_t sz, off_t loc, blkptr_t *bp);
+void vdev_pwrite_dnode_data(zfs_opt_t *zfs, dnode_phys_t *dnode, const void *data,
+ off_t sz, off_t loc);
+void vdev_label_write(zfs_opt_t *zfs, int ind, const vdev_label_t *labelp);
+void vdev_spacemap_write(zfs_opt_t *);
+void vdev_fini(zfs_opt_t *zfs);
+
+/* zap.c */
+zfs_zap_t *zap_alloc(zfs_objset_t *, dnode_phys_t *);
+void zap_add(zfs_zap_t *, const char *, size_t, size_t, const uint8_t *);
+void zap_add_uint64(zfs_zap_t *, const char *, uint64_t);
+void zap_add_string(zfs_zap_t *, const char *, const char *);
+bool zap_entry_exists(zfs_zap_t *, const char *);
+void zap_write(zfs_opt_t *, zfs_zap_t *);
+
+/* zfs.c */
+struct dnode_cursor *dnode_cursor_init(zfs_opt_t *, zfs_objset_t *,
+ dnode_phys_t *, off_t, off_t);
+blkptr_t *dnode_cursor_next(zfs_opt_t *, struct dnode_cursor *, off_t);
+void dnode_cursor_finish(zfs_opt_t *, struct dnode_cursor *);
+
+#endif /* !_MAKEFS_ZFS_H_ */
diff --git a/usr.sbin/mfiutil/mfiutil.8 b/usr.sbin/mfiutil/mfiutil.8
index e3adc0b65698..29409ad53e46 100644
--- a/usr.sbin/mfiutil/mfiutil.8
+++ b/usr.sbin/mfiutil/mfiutil.8
@@ -326,7 +326,8 @@ the controller for each low-level request.
The default is 15 events.
.Pp
By default, matching event log entries from the previous shutdown up to the
-present are displayed. This range can be adjusted via the
+present are displayed.
+This range can be adjusted via the
.Ar start
and
.Ar stop
@@ -383,7 +384,8 @@ The firmware should kick off an array rebuild on its own if a failed drive
is marked as a rebuild drive.
.It Cm syspd Ar drive
Present the drive to the host operating system as a disk SYSPD block device in
-the format /dev/mfisyspdX. Clear this flag with
+the format /dev/mfisyspdX.
+Clear this flag with
.Cm good
.Ar drive
.It Cm drive progress Ar drive
@@ -524,7 +526,7 @@ Creates a single volume by concatenating all of the drives in the single drive
list.
.El
.Pp
-.Sy Note:
+.Sy Note :
Not all volume types are supported by all controllers.
.Pp
If the
@@ -602,7 +604,8 @@ Start a patrol read operation.
.It Cm stop patrol
Stop a currently running patrol read operation.
.It Cm foreign scan
-Scan for foreign configurations and display the number found. The
+Scan for foreign configurations and display the number found.
+The
.Ar config
argument for the commands below takes the form of a number from 0 to the total
configurations found.
@@ -645,8 +648,8 @@ Update battery backup unit (BBU) properties related to battery relearning.
The following settings are configurable:
.Bl -tag -width indent
.It Cm learn-delay
-Add a delay to the next scheduled battery relearn event. This setting is
-given in hours and must lie in the range of 0 to 255.
+Add a delay to the next scheduled battery relearn event.
+This setting is given in hours and must lie in the range of 0 to 255.
.It Cm autolearn-mode
Enable or disable automatic periodic battery relearning.
The setting may be set to
@@ -659,7 +662,8 @@ Mode 0 enables periodic relearning, mode 1 disables it, and mode 2 disables
it and logs a warning to the event log when it detects that a battery relearn
should be performed.
.It Cm bbu-mode
-Set the BBU's mode of operation. This setting is not supported by all BBUs.
+Set the BBU's mode of operation.
+This setting is not supported by all BBUs.
Where it is supported, the possible values are the integers between 1 and 5
inclusive.
Modes 1, 2 and 3 enable a transparent learn cycle, whereas modes 4 and 5 do not.
@@ -670,7 +674,8 @@ With no arguments display the rate of rebuild (percentage)a for volumes.
With an integer argument (0-100), set that value as the new rebuild rate for volumes.
.It Cm ctrlprop Ar alarm Op Ar 0/1
With no arguments display the current alarm enable/disable status.
-With a 0, disable alarms. With a 1, enable alarms.
+With a 0, disable alarms.
+With a 1, enable alarms.
.El
.Sh EXAMPLES
Configure the cache for volume mfid0 to cache only writes:
diff --git a/usr.sbin/mixer/mixer.8 b/usr.sbin/mixer/mixer.8
index 284750538f7e..9a76cbe41f65 100644
--- a/usr.sbin/mixer/mixer.8
+++ b/usr.sbin/mixer/mixer.8
@@ -21,7 +21,7 @@
.\"
.\" $FreeBSD$
.\"
-.Dd March 20, 2022
+.Dd April 29, 2022
.Dt MIXER 8
.Os
.Sh NAME
@@ -112,8 +112,8 @@ with one of the available devices):
.It Sy Name Ta Sy Value
.It Ar dev Cm .volume Ta Xo
.Ar vol |
-.Oo Cm \&+ | Cm \&- Oc Ar lvol
-.Oo Cm \&: Oo Cm \&+ | Cm \&- Oc Ar rvol Oc
+.Oo Cm \&+ | Cm \&- Oc Ar lvol Oo % Oc
+.Oo Cm \&: Oo Cm \&+ | Cm \&- Oc Ar rvol Oo % Oc Oc
.Xc
.It Ar dev Cm .mute Ta Cm 0 | 1 | ^
.It Ar dev Cm .recsrc Ta Cm ^ | + | - | =
@@ -128,16 +128,21 @@ The optional
and/or
.Ar rvol
values have to be specified.
-The values have to be normalized 32-bit floats, from 0.0 to 1.0 inclusively.
-If no
-.Ql \&.
-character is present, the value is treated like a percentage, for backwards compatibility.
+The values should typically be decimal numbers between 0 and 1 with at most 2
+digits after the decimal point.
+A trailing percent sign indicates that the value should be treated as a
+percentage of 1.0, rather than an absolute value.
+Thus, 70% means the same as 0.7.
If the left or right volume values are prefixed with
.Cm +
or
.Cm - ,
the value following will be used as a relative adjustment, modifying the
current settings by the amount specified.
+Note that relative percentages are still relative to 1.0, not to the current
+value.
+If the volume is currently 0.40 and an adjustment of +20% is specified, then
+thet final volume will be set to 0.60.
.Pp
Volume can also be set using the shorthand
.Ar dev Ns Cm =value .
diff --git a/usr.sbin/mixer/mixer.c b/usr.sbin/mixer/mixer.c
index c0a9ec25c6fa..e216efe3313c 100644
--- a/usr.sbin/mixer/mixer.c
+++ b/usr.sbin/mixer/mixer.c
@@ -341,7 +341,7 @@ mod_volume(struct mix_dev *d, void *p)
mix_ctl_t *cp;
mix_volume_t v;
const char *val;
- char lstr[8], rstr[8];
+ char *endp, lstr[8], rstr[8];
float lprev, rprev, lrel, rrel;
int n;
@@ -356,25 +356,32 @@ mod_volume(struct mix_dev *d, void *p)
lrel = rrel = 0;
if (n > 0) {
if (*lstr == '+' || *lstr == '-')
- lrel = rrel = 1;
- v.left = strtof(lstr, NULL);
+ lrel = 1;
+ v.left = strtof(lstr, &endp);
+ if (*endp != '\0' && (*endp != '%' || *(endp + 1) != '\0')) {
+ warnx("invalid volume value: %s", lstr);
+ return (-1);
+ }
- /* be backwards compatible */
- if (strstr(lstr, ".") == NULL)
+ if (*endp == '%')
v.left /= 100.0f;
}
if (n > 1) {
if (*rstr == '+' || *rstr == '-')
rrel = 1;
- v.right = strtof(rstr, NULL);
+ v.right = strtof(rstr, &endp);
+ if (*endp != '\0' && (*endp != '%' || *(endp + 1) != '\0')) {
+ warnx("invalid volume value: %s", rstr);
+ return (-1);
+ }
- /* be backwards compatible */
- if (strstr(rstr, ".") == NULL)
+ if (*endp == '%')
v.right /= 100.0f;
}
switch (n) {
case 1:
v.right = v.left; /* FALLTHROUGH */
+ rrel = lrel;
case 2:
if (lrel)
v.left += m->dev->vol.left;
diff --git a/usr.sbin/ndp/Makefile b/usr.sbin/ndp/Makefile
index 52d8304a436a..84eb586083b3 100644
--- a/usr.sbin/ndp/Makefile
+++ b/usr.sbin/ndp/Makefile
@@ -21,6 +21,8 @@ PROG= ndp
MAN= ndp.8
SRCS= ndp.c gmt2local.c
+LIBADD= xo
+
CFLAGS+= -I. -I${.CURDIR} -I${SRCTOP}/contrib/tcpdump
CFLAGS+= -D_U_=""
diff --git a/usr.sbin/ndp/ndp.c b/usr.sbin/ndp/ndp.c
index ce21e34417c3..5097c156e624 100644
--- a/usr.sbin/ndp/ndp.c
+++ b/usr.sbin/ndp/ndp.c
@@ -109,6 +109,7 @@
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
+#include <libxo/xo.h>
#include "gmt2local.h"
#define NEXTADDR(w, s) \
@@ -158,6 +159,8 @@ static const char *rtpref_str[] = {
"low" /* 11 */
};
+#define NDP_XO_VERSION "1"
+
int
main(int argc, char **argv)
{
@@ -166,6 +169,13 @@ main(int argc, char **argv)
pid = getpid();
thiszone = gmt2local(0);
+
+ argc = xo_parse_args(argc, argv);
+ if (argc < 0)
+ exit(1);
+ xo_set_version(NDP_XO_VERSION);
+ xo_open_container("ndp");
+
while ((ch = getopt(argc, argv, "acd:f:Ii:nprstA:HPR")) != -1)
switch (ch) {
case 'a':
@@ -234,7 +244,9 @@ main(int argc, char **argv)
usage();
/*NOTREACHED*/
}
+ xo_open_list("neighbor-cache");
delete(arg);
+ xo_close_list("neighbor-cache");
break;
case 'I':
#ifdef SIOCSDEFIFACE_IN6 /* XXX: check SIOCGDEFIFACE_IN6 as well? */
@@ -246,12 +258,12 @@ main(int argc, char **argv)
if_nametoindex(*argv))
setdefif(*argv);
else
- errx(1, "invalid interface %s", *argv);
+ xo_errx(1, "invalid interface %s", *argv);
}
getdefif(); /* always call it to print the result */
break;
#else
- errx(1, "not supported yet");
+ xo_errx(1, "not supported yet");
/*NOTREACHED*/
#endif
case 'p':
@@ -304,6 +316,8 @@ main(int argc, char **argv)
get(argv[0]);
break;
}
+ xo_close_container("ndp");
+ xo_finish();
exit(0);
}
@@ -318,7 +332,7 @@ file(char *name)
char line[100], arg[5][50], *args[5], *p;
if ((fp = fopen(name, "r")) == NULL)
- err(1, "cannot open %s", name);
+ xo_err(1, "cannot open %s", name);
args[0] = &arg[0][0];
args[1] = &arg[1][0];
args[2] = &arg[2][0];
@@ -334,7 +348,7 @@ file(char *name)
i = sscanf(line, "%49s %49s %49s %49s %49s",
arg[0], arg[1], arg[2], arg[3], arg[4]);
if (i < 2) {
- warnx("bad line: %s", line);
+ xo_warnx("bad line: %s", line);
retval = 1;
continue;
}
@@ -351,7 +365,7 @@ getsocket()
if (s < 0) {
s = socket(PF_ROUTE, SOCK_RAW, 0);
if (s < 0) {
- err(1, "socket");
+ xo_err(1, "socket");
/* NOTREACHED */
}
}
@@ -402,8 +416,7 @@ set(int argc, char **argv)
hints.ai_family = AF_INET6;
gai_error = getaddrinfo(host, NULL, &hints, &res);
if (gai_error) {
- fprintf(stderr, "ndp: %s: %s\n", host,
- gai_strerror(gai_error));
+ xo_warnx("%s: %s", host, gai_strerror(gai_error));
return 1;
}
sin->sin6_addr = ((struct sockaddr_in6 *)res->ai_addr)->sin6_addr;
@@ -424,7 +437,7 @@ set(int argc, char **argv)
argv++;
}
if (rtmsg(RTM_GET) < 0) {
- errx(1, "RTM_GET(%s) failed", host);
+ xo_errx(1, "RTM_GET(%s) failed", host);
/* NOTREACHED */
}
sin = (struct sockaddr_in6 *)(rtm + 1);
@@ -439,13 +452,13 @@ set(int argc, char **argv)
goto overwrite;
}
}
- fprintf(stderr, "set: cannot configure a new entry\n");
+ xo_warnx("cannot configure a new entry");
return 1;
}
overwrite:
if (sdl->sdl_family != AF_LINK) {
- printf("cannot intuit interface index and type for %s\n", host);
+ xo_warnx("cannot intuit interface index and type for %s", host);
return (1);
}
sdl_m.sdl_type = sdl->sdl_type;
@@ -468,8 +481,7 @@ get(char *host)
hints.ai_family = AF_INET6;
gai_error = getaddrinfo(host, NULL, &hints, &res);
if (gai_error) {
- fprintf(stderr, "ndp: %s: %s\n", host,
- gai_strerror(gai_error));
+ xo_warnx("%s: %s", host, gai_strerror(gai_error));
return;
}
sin->sin6_addr = ((struct sockaddr_in6 *)res->ai_addr)->sin6_addr;
@@ -480,8 +492,7 @@ get(char *host)
getnameinfo((struct sockaddr *)sin, sin->sin6_len, host_buf,
sizeof(host_buf), NULL ,0,
(nflag ? NI_NUMERICHOST : 0));
- printf("%s (%s) -- no entry\n", host, host_buf);
- exit(1);
+ xo_errx(1, "%s (%s) -- no entry", host, host_buf);
}
}
@@ -505,15 +516,14 @@ delete(char *host)
hints.ai_family = AF_INET6;
gai_error = getaddrinfo(host, NULL, &hints, &res);
if (gai_error) {
- fprintf(stderr, "ndp: %s: %s\n", host,
- gai_strerror(gai_error));
+ xo_warnx("%s: %s", host, gai_strerror(gai_error));
return 1;
}
sin->sin6_addr = ((struct sockaddr_in6 *)res->ai_addr)->sin6_addr;
sin->sin6_scope_id =
((struct sockaddr_in6 *)res->ai_addr)->sin6_scope_id;
if (rtmsg(RTM_GET) < 0) {
- errx(1, "RTM_GET(%s) failed", host);
+ xo_errx(1, "RTM_GET(%s) failed", host);
/* NOTREACHED */
}
sin = (struct sockaddr_in6 *)(rtm + 1);
@@ -523,13 +533,13 @@ delete(char *host)
!(rtm->rtm_flags & RTF_GATEWAY)) {
goto delete;
}
- fprintf(stderr, "delete: cannot delete non-NDP entry\n");
+ xo_warnx("delete: cannot delete non-NDP entry");
return 1;
}
delete:
if (sdl->sdl_family != AF_LINK) {
- printf("cannot locate %s\n", host);
+ xo_warnx("cannot locate %s", host);
return (1);
}
/*
@@ -543,7 +553,19 @@ delete:
sin->sin6_len, host_buf,
sizeof(host_buf), NULL, 0,
(nflag ? NI_NUMERICHOST : 0));
- printf("%s (%s) deleted\n", host, host_buf);
+ xo_open_instance("neighbor-cache");
+
+ char *ifname = if_indextoname(sdl->sdl_index, ifix_buf);
+ if (ifname == NULL) {
+ strlcpy(ifix_buf, "?", sizeof(ifix_buf));
+ ifname = ifix_buf;
+ }
+ char abuf[INET6_ADDRSTRLEN];
+ inet_ntop(AF_INET6, &sin->sin6_addr, abuf, sizeof(abuf));
+
+ xo_emit("{:hostname/%s}{d:/ (%s) deleted\n}", host, host_buf);
+ xo_emit("{e:address/%s}{e:interface/%s}", abuf, ifname);
+ xo_close_instance("neighbor-cache");
}
return 0;
@@ -574,11 +596,14 @@ dump(struct sockaddr_in6 *addr, int cflag)
char *ifname;
/* Print header */
- if (!tflag && !cflag)
- printf("%-*.*s %-*.*s %*.*s %-9.9s %1s %5s\n",
- W_ADDR, W_ADDR, "Neighbor", W_LL, W_LL, "Linklayer Address",
- W_IF, W_IF, "Netif", "Expire", "S", "Flags");
-
+ if (!tflag && !cflag) {
+ char xobuf[200];
+ snprintf(xobuf, sizeof(xobuf),
+ "{T:/%%-%d.%ds} {T:/%%-%d.%ds} {T:/%%%d.%ds} {T:/%%-9.9s} {T:%%1s} {T:%%5s}\n",
+ W_ADDR, W_ADDR, W_LL, W_LL, W_IF, W_IF);
+ xo_emit(xobuf, "Neighbor", "Linklayer Address", "Netif", "Expire", "S", "Flags");
+ }
+ xo_open_list("neighbor-cache");
again:;
mib[0] = CTL_NET;
mib[1] = PF_ROUTE;
@@ -591,12 +616,12 @@ again:;
mib[5] = 0;
#endif
if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0)
- err(1, "sysctl(PF_ROUTE estimate)");
+ xo_err(1, "sysctl(PF_ROUTE estimate)");
if (needed > 0) {
if ((buf = malloc(needed)) == NULL)
- err(1, "malloc");
+ xo_err(1, "malloc");
if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0)
- err(1, "sysctl(PF_ROUTE, NET_RT_FLAGS)");
+ xo_err(1, "sysctl(PF_ROUTE, NET_RT_FLAGS)");
lim = buf + needed;
} else
buf = lim = NULL;
@@ -677,46 +702,54 @@ again:;
if (W_ADDR + W_LL + W_IF - addrwidth - llwidth > ifwidth)
ifwidth = W_ADDR + W_LL + W_IF - addrwidth - llwidth;
- printf("%-*.*s %-*.*s %*.*s", addrwidth, addrwidth, host_buf,
- llwidth, llwidth, ether_str(sdl), ifwidth, ifwidth, ifname);
+ xo_open_instance("neighbor-cache");
+ /* Compose format string for libxo, as it doesn't support *.* */
+ char xobuf[200];
+ snprintf(xobuf, sizeof(xobuf),
+ "{:address/%%-%d.%ds/%%s} {:mac-address/%%-%d.%ds/%%s} {:interface/%%%d.%ds/%%s}",
+ addrwidth, addrwidth, llwidth, llwidth, ifwidth, ifwidth);
+ xo_emit(xobuf, host_buf, ether_str(sdl), ifname);
/* Print neighbor discovery specific information */
expire = rtm->rtm_rmx.rmx_expire;
+ int expire_in = expire - now.tv_sec;
if (expire > now.tv_sec)
- printf(" %-9.9s", sec2str(expire - now.tv_sec));
+ xo_emit("{d:/ %-9.9s}{e:expires_sec/%d}", sec2str(expire_in), expire_in);
else if (expire == 0)
- printf(" %-9.9s", "permanent");
+ xo_emit("{d:/ %-9.9s}{en:permanent/true}", "permanent");
else
- printf(" %-9.9s", "expired");
+ xo_emit("{d:/ %-9.9s}{e:expires_sec/%d}", "expired", expire_in);
+ char *lle_state = "";
switch (rtm->rtm_rmx.rmx_state) {
case ND6_LLINFO_NOSTATE:
- printf(" N");
+ lle_state = "N";
break;
#ifdef ND6_LLINFO_WAITDELETE
case ND6_LLINFO_WAITDELETE:
- printf(" W");
+ lle_state = "W";
break;
#endif
case ND6_LLINFO_INCOMPLETE:
- printf(" I");
+ lle_state = "I";
break;
case ND6_LLINFO_REACHABLE:
- printf(" R");
+ lle_state = "R";
break;
case ND6_LLINFO_STALE:
- printf(" S");
+ lle_state = "S";
break;
case ND6_LLINFO_DELAY:
- printf(" D");
+ lle_state = "D";
break;
case ND6_LLINFO_PROBE:
- printf(" P");
+ lle_state = "P";
break;
default:
- printf(" ?");
+ lle_state = "?";
break;
}
+ xo_emit(" {:neighbor-state/%s}", lle_state);
isrouter = rtm->rtm_flags & RTF_GATEWAY;
prbs = rtm->rtm_rmx.rmx_pksent;
@@ -743,22 +776,25 @@ again:;
(rtm->rtm_flags & RTF_ANNOUNCE) ? "p" : "");
#endif
}
- printf(" %s", flgbuf);
+ xo_emit(" {:nd-flags/%s}", flgbuf);
if (prbs)
- printf(" %d", prbs);
+ xo_emit("{d:/ %d}", prbs);
- printf("\n");
+ xo_emit("\n");
+ xo_close_instance("neighbor-cache");
}
if (buf != NULL)
free(buf);
if (repeat) {
- printf("\n");
- fflush(stdout);
+ xo_emit("\n");
+ xo_flush();
sleep(repeat);
goto again;
}
+
+ xo_close_list("neighbor-cache");
}
static struct in6_nbrinfo *
@@ -768,14 +804,14 @@ getnbrinfo(struct in6_addr *addr, int ifindex, int warning)
int sock;
if ((sock = socket(AF_INET6, SOCK_DGRAM, 0)) < 0)
- err(1, "socket");
+ xo_err(1, "socket");
bzero(&nbi, sizeof(nbi));
if_indextoname(ifindex, nbi.ifname);
nbi.addr = *addr;
if (ioctl(sock, SIOCGNBRINFO_IN6, (caddr_t)&nbi) < 0) {
if (warning)
- warn("ioctl(SIOCGNBRINFO_IN6)");
+ xo_warn("ioctl(SIOCGNBRINFO_IN6)");
close(sock);
return(NULL);
}
@@ -809,7 +845,7 @@ ndp_ether_aton(char *a, u_char *n)
i = sscanf(a, "%x:%x:%x:%x:%x:%x", &o[0], &o[1], &o[2],
&o[3], &o[4], &o[5]);
if (i != 6) {
- fprintf(stderr, "ndp: invalid Ethernet address '%s'\n", a);
+ xo_warnx("invalid Ethernet address '%s'", a);
return (1);
}
for (i = 0; i < 6; i++)
@@ -851,8 +887,7 @@ rtmsg(int cmd)
switch (cmd) {
default:
- fprintf(stderr, "ndp: internal wrong cmd\n");
- exit(1);
+ xo_errx(1, "internal wrong cmd");
case RTM_ADD:
rtm->rtm_addrs |= RTA_GATEWAY;
if (expire_time) {
@@ -875,7 +910,7 @@ doit:
rtm->rtm_type = cmd;
if ((rlen = write(s, (char *)&m_rtmsg, l)) < 0) {
if (errno != ESRCH || cmd != RTM_DELETE) {
- err(1, "writing to routing socket");
+ xo_err(1, "writing to routing socket");
/* NOTREACHED */
}
}
@@ -884,8 +919,7 @@ doit:
} while (l > 0 && (rtm->rtm_type != cmd || rtm->rtm_seq != seq ||
rtm->rtm_pid != pid));
if (l < 0)
- (void) fprintf(stderr, "ndp: read from routing socket: %s\n",
- strerror(errno));
+ xo_warn("read from routing socket");
return (0);
}
@@ -900,13 +934,13 @@ ifinfo(char *ifname, int argc, char **argv)
#endif
if ((sock = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
- err(1, "socket");
+ xo_err(1, "socket");
/* NOTREACHED */
}
bzero(&nd, sizeof(nd));
strlcpy(nd.ifname, ifname, sizeof(nd.ifname));
if (ioctl(sock, SIOCGIFINFO_IN6, (caddr_t)&nd) < 0) {
- err(1, "ioctl(SIOCGIFINFO_IN6)");
+ xo_err(1, "ioctl(SIOCGIFINFO_IN6)");
/* NOTREACHED */
}
#define ND nd.ndi
@@ -939,11 +973,11 @@ ifinfo(char *ifname, int argc, char **argv)
if (strncmp(cp, f, strlen(f)) == 0) { \
valptr = strchr(cp, '='); \
if (valptr == NULL) \
- err(1, "syntax error in %s field", (f)); \
+ xo_err(1, "syntax error in %s field", (f)); \
errno = 0; \
newval = strtoul(++valptr, NULL, 0); \
if (errno) \
- err(1, "syntax error in %s's value", (f)); \
+ xo_err(1, "syntax error in %s's value", (f)); \
v = newval; \
} \
} while (0)
@@ -965,7 +999,7 @@ ifinfo(char *ifname, int argc, char **argv)
ND.flags = newflags;
if (ioctl(sock, SIOCSIFINFO_IN6, (caddr_t)&nd) < 0) {
- err(1, "ioctl(SIOCSIFINFO_IN6)");
+ xo_err(1, "ioctl(SIOCSIFINFO_IN6)");
/* NOTREACHED */
}
#undef SETFLAG
@@ -973,21 +1007,25 @@ ifinfo(char *ifname, int argc, char **argv)
}
if (!ND.initialized) {
- errx(1, "%s: not initialized yet", ifname);
+ xo_errx(1, "%s: not initialized yet", ifname);
/* NOTREACHED */
}
if (ioctl(sock, SIOCGIFINFO_IN6, (caddr_t)&nd) < 0) {
- err(1, "ioctl(SIOCGIFINFO_IN6)");
+ xo_err(1, "ioctl(SIOCGIFINFO_IN6)");
/* NOTREACHED */
}
- printf("linkmtu=%d", ND.linkmtu);
- printf(", maxmtu=%d", ND.maxmtu);
- printf(", curhlim=%d", ND.chlim);
- printf(", basereachable=%ds%dms",
- ND.basereachable / 1000, ND.basereachable % 1000);
- printf(", reachable=%ds", ND.reachable);
- printf(", retrans=%ds%dms", ND.retrans / 1000, ND.retrans % 1000);
+ xo_open_container("ifinfo");
+
+ xo_emit("{e:interface/%s}", ifname);
+ xo_emit("linkmtu={:linkmtu/%d}", ND.linkmtu);
+ xo_emit(", maxmtu={:maxmtu/%d}", ND.maxmtu);
+ xo_emit(", curhlim={:curhlim/%d}", ND.chlim);
+ xo_emit("{d:/, basereachable=%ds%dms}{e:basereachable_ms/%u}",
+ ND.basereachable / 1000, ND.basereachable % 1000, ND.basereachable);
+ xo_emit("{d:/, reachable=%ds}{e:reachable_ms/%u}", ND.reachable, ND.reachable * 1000);
+ xo_emit("{d:/, retrans=%ds%dms}{e:retrans_ms/%u}", ND.retrans / 1000, ND.retrans % 1000,
+ ND.retrans);
#ifdef IPV6CTL_USETEMPADDR
memset(nullbuf, 0, sizeof(nullbuf));
if (memcmp(nullbuf, ND.randomid, sizeof(nullbuf)) != 0) {
@@ -995,50 +1033,60 @@ ifinfo(char *ifname, int argc, char **argv)
u_int8_t *rbuf;
for (i = 0; i < 3; i++) {
+ const char *txt, *field;
switch (i) {
case 0:
- printf("\nRandom seed(0): ");
+ txt = "\nRandom seed(0): ";
+ field = "seed_0";
rbuf = ND.randomseed0;
break;
case 1:
- printf("\nRandom seed(1): ");
+ txt = "\nRandom seed(1): ";
+ field = "seed_1";
rbuf = ND.randomseed1;
break;
case 2:
- printf("\nRandom ID: ");
+ txt = "\nRandom ID: ";
+ field = "random_id";
rbuf = ND.randomid;
break;
default:
- errx(1, "impossible case for tempaddr display");
+ xo_errx(1, "impossible case for tempaddr display");
}
+ char abuf[20], xobuf[200];
for (j = 0; j < 8; j++)
- printf("%02x", rbuf[j]);
+ snprintf(&abuf[j * 2], sizeof(abuf), "%02X", rbuf[j]);
+ snprintf(xobuf, sizeof(xobuf), "%s{:%s/%%s}", txt, field);
+ xo_emit(xobuf, abuf);
}
}
#endif /* IPV6CTL_USETEMPADDR */
if (ND.flags) {
- printf("\nFlags: ");
+ xo_emit("\nFlags: {e:flags/%u}", ND.flags);
+ xo_open_list("flags_pretty");
#ifdef ND6_IFF_IFDISABLED
if ((ND.flags & ND6_IFF_IFDISABLED))
- printf("disabled ");
+ xo_emit("{l:%s} ", "disabled");
#endif
if ((ND.flags & ND6_IFF_PERFORMNUD))
- printf("nud ");
+ xo_emit("{l:%s} ", "nud");
#ifdef ND6_IFF_ACCEPT_RTADV
if ((ND.flags & ND6_IFF_ACCEPT_RTADV))
- printf("accept_rtadv ");
+ xo_emit("{l:%s} ", "accept_rtadv");
#endif
#ifdef ND6_IFF_AUTO_LINKLOCAL
if ((ND.flags & ND6_IFF_AUTO_LINKLOCAL))
- printf("auto_linklocal ");
+ xo_emit("{l:%s} ", "auto_linklocal");
#endif
#ifdef ND6_IFF_NO_PREFER_IFACE
if ((ND.flags & ND6_IFF_NO_PREFER_IFACE))
- printf("no_prefer_iface ");
+ xo_emit("{l:%s} ", "no_prefer_iface");
#endif
+ xo_close_list("flags");
}
- putc('\n', stdout);
+ xo_emit("\n");
#undef ND
+ xo_close_container("ifinfo");
close(sock);
}
@@ -1057,49 +1105,76 @@ rtrlist()
struct timeval now;
if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), NULL, &l, NULL, 0) < 0) {
- err(1, "sysctl(ICMPV6CTL_ND6_DRLIST)");
+ xo_err(1, "sysctl(ICMPV6CTL_ND6_DRLIST)");
/*NOTREACHED*/
}
if (l == 0)
return;
buf = malloc(l);
if (!buf) {
- err(1, "malloc");
+ xo_err(1, "malloc");
/*NOTREACHED*/
}
if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), buf, &l, NULL, 0) < 0) {
- err(1, "sysctl(ICMPV6CTL_ND6_DRLIST)");
+ xo_err(1, "sysctl(ICMPV6CTL_ND6_DRLIST)");
/*NOTREACHED*/
}
+ xo_open_list("router-list");
+
ep = (struct in6_defrouter *)(buf + l);
for (p = (struct in6_defrouter *)buf; p < ep; p++) {
int rtpref;
+ char abuf[INET6_ADDRSTRLEN], *paddr;
if (getnameinfo((struct sockaddr *)&p->rtaddr,
p->rtaddr.sin6_len, host_buf, sizeof(host_buf), NULL, 0,
(nflag ? NI_NUMERICHOST : 0)) != 0)
strlcpy(host_buf, "?", sizeof(host_buf));
+ if (nflag)
+ paddr = host_buf;
+ else {
+ inet_ntop(AF_INET6, &p->rtaddr.sin6_addr, abuf, sizeof(abuf));
+ paddr = abuf;
+ }
- printf("%s if=%s", host_buf,
+ xo_open_instance("router-list");
+ xo_emit("{:hostname/%s}{e:address/%s} if={:interface/%s}",
+ host_buf, paddr,
if_indextoname(p->if_index, ifix_buf));
- printf(", flags=%s%s",
- p->flags & ND_RA_FLAG_MANAGED ? "M" : "",
- p->flags & ND_RA_FLAG_OTHER ? "O" : "");
+ xo_open_list("flags_pretty");
+ char rflags[6] = {}, *pflags = rflags;
+ if (p->flags & ND_RA_FLAG_MANAGED) {
+ *pflags++ = 'M';
+ xo_emit("{el:%s}", "managed");
+ }
+ if (p->flags & ND_RA_FLAG_OTHER) {
+ *pflags++ = 'O';
+ xo_emit("{el:%s}", "other");
+ }
#ifdef DRAFT_IETF_6MAN_IPV6ONLY_FLAG
- printf("%s", p->flags & ND_RA_FLAG_IPV6_ONLY ? "S" : "");
+ if (p->flags & ND_RA_FLAG_IPV6_ONLY) {
+ *pflags++ = 'S';
+ xo_emit("{el:%s}", "ipv6only");
+ }
#endif
+ xo_close_list("flags_pretty");
+ xo_emit(", flags={:flags/%s}", rflags);
+
rtpref = ((p->flags & ND_RA_FLAG_RTPREF_MASK) >> 3) & 0xff;
- printf(", pref=%s", rtpref_str[rtpref]);
+ xo_emit(", pref={:preference/%s}", rtpref_str[rtpref]);
gettimeofday(&now, 0);
if (p->expire == 0)
- printf(", expire=Never\n");
+ xo_emit(", expire=Never\n{en:permanent/true}");
else
- printf(", expire=%s\n",
- sec2str(p->expire - now.tv_sec));
+ xo_emit("{d:/, expire=%s\n}{e:expires_sec/%ld}",
+ sec2str(p->expire - now.tv_sec),
+ (long)p->expire - now.tv_sec);
+ xo_close_instance("router-list");
}
free(buf);
+ xo_close_list("router-list");
}
static void
@@ -1116,64 +1191,88 @@ plist()
char namebuf[NI_MAXHOST];
if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), NULL, &l, NULL, 0) < 0) {
- err(1, "sysctl(ICMPV6CTL_ND6_PRLIST)");
+ xo_err(1, "sysctl(ICMPV6CTL_ND6_PRLIST)");
/*NOTREACHED*/
}
buf = malloc(l);
if (!buf) {
- err(1, "malloc");
+ xo_err(1, "malloc");
/*NOTREACHED*/
}
if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), buf, &l, NULL, 0) < 0) {
- err(1, "sysctl(ICMPV6CTL_ND6_PRLIST)");
+ xo_err(1, "sysctl(ICMPV6CTL_ND6_PRLIST)");
/*NOTREACHED*/
}
+ xo_open_list("prefix-list");
+
ep = (struct in6_prefix *)(buf + l);
for (p = (struct in6_prefix *)buf; p < ep; p = n) {
advrtr = (struct sockaddr_in6 *)(p + 1);
n = (struct in6_prefix *)&advrtr[p->advrtrs];
+ xo_open_instance("prefix-list");
if (getnameinfo((struct sockaddr *)&p->prefix,
p->prefix.sin6_len, namebuf, sizeof(namebuf),
NULL, 0, niflags) != 0)
strlcpy(namebuf, "?", sizeof(namebuf));
- printf("%s/%d if=%s\n", namebuf, p->prefixlen,
- if_indextoname(p->if_index, ifix_buf));
+ xo_emit("{:prefix/%s%s%d} if={:interface/%s}\n", namebuf, "/",
+ p->prefixlen, if_indextoname(p->if_index, ifix_buf));
gettimeofday(&now, 0);
/*
* meaning of fields, especially flags, is very different
* by origin. notify the difference to the users.
*/
- printf("flags=%s%s%s%s%s",
- p->raflags.onlink ? "L" : "",
- p->raflags.autonomous ? "A" : "",
- (p->flags & NDPRF_ONLINK) != 0 ? "O" : "",
- (p->flags & NDPRF_DETACHED) != 0 ? "D" : "",
+ char flags[10] = {}, *pflags = flags;
+ xo_open_list("flags_pretty");
+ if (p->raflags.onlink) {
+ *pflags++ = 'L';
+ xo_emit("{el:%s}", "ra_onlink");
+ }
+ if (p->raflags.autonomous) {
+ *pflags++ = 'A';
+ xo_emit("{el:%s}", "ra_autonomous");
+ }
+ if (p->flags & NDPRF_ONLINK) {
+ *pflags++ = 'O';
+ xo_emit("{el:%s}", "is_onlink");
+ }
+ if (p->flags & NDPRF_DETACHED) {
+ *pflags++ = 'D';
+ xo_emit("{el:%s}", "is_detached");
+ }
#ifdef NDPRF_HOME
- (p->flags & NDPRF_HOME) != 0 ? "H" : ""
-#else
- ""
+ if (p->flags & NDPRF_HOME) {
+ *pflags++ = 'H';
+ xo_emit("{el:%s}", "is_home");
+ }
#endif
- );
+ xo_close_list("flags_pretty");
+ xo_emit("flags={:flags/%s}", flags);
+ int expire_in = p->expire - now.tv_sec;
+
if (p->vltime == ND6_INFINITE_LIFETIME)
- printf(" vltime=infinity");
+ xo_emit(" vltime=infinity{e:valid-lifetime/%lu}",
+ (unsigned long)p->vltime);
else
- printf(" vltime=%lu", (unsigned long)p->vltime);
+ xo_emit(" vltime={:valid-lifetime/%lu}",
+ (unsigned long)p->vltime);
if (p->pltime == ND6_INFINITE_LIFETIME)
- printf(", pltime=infinity");
+ xo_emit(", pltime=infinity{e:preferred-lifetime/%lu}",
+ (unsigned long)p->pltime);
else
- printf(", pltime=%lu", (unsigned long)p->pltime);
+ xo_emit(", pltime={:preferred-lifetime/%lu}",
+ (unsigned long)p->pltime);
if (p->expire == 0)
- printf(", expire=Never");
+ xo_emit(", expire=Never{en:permanent/true}");
else if (p->expire >= now.tv_sec)
- printf(", expire=%s",
- sec2str(p->expire - now.tv_sec));
+ xo_emit(", expire=%s{e:expires_sec/%d}",
+ sec2str(expire_in), expire_in);
else
- printf(", expired");
- printf(", ref=%d", p->refcnt);
- printf("\n");
+ xo_emit(", expired{e:expires_sec/%d}", expire_in);
+ xo_emit(", ref={:refcount/%d}", p->refcnt);
+ xo_emit("\n");
/*
* "advertising router" list is meaningful only if the prefix
* information is from RA.
@@ -1183,37 +1282,51 @@ plist()
struct sockaddr_in6 *sin6;
sin6 = advrtr;
- printf(" advertised by\n");
+ xo_emit(" advertised by\n");
+ xo_open_list("advertising-routers");
for (j = 0; j < p->advrtrs; j++) {
struct in6_nbrinfo *nbi;
+ xo_open_instance("advertising-routers");
if (getnameinfo((struct sockaddr *)sin6,
sin6->sin6_len, namebuf, sizeof(namebuf),
NULL, 0, ninflags) != 0)
strlcpy(namebuf, "?", sizeof(namebuf));
- printf(" %s", namebuf);
+ char abuf[INET6_ADDRSTRLEN];
+ inet_ntop(AF_INET6, &sin6->sin6_addr, abuf,
+ sizeof(abuf));
+
+ xo_emit(" {:hostname/%s}{e:address/%s}",
+ namebuf, abuf);
nbi = getnbrinfo(&sin6->sin6_addr,
p->if_index, 0);
+ const char *state = "";
if (nbi) {
switch (nbi->state) {
case ND6_LLINFO_REACHABLE:
case ND6_LLINFO_STALE:
case ND6_LLINFO_DELAY:
case ND6_LLINFO_PROBE:
- printf(" (reachable)\n");
+ state = "reachable";
break;
default:
- printf(" (unreachable)\n");
+ state = "unreachable";
}
} else
- printf(" (no neighbor state)\n");
+ state = "no neighbor state";
+ xo_emit(" ({:state/%s})\n", state);
sin6++;
+ xo_close_instance("advertising-routers");
}
+ xo_close_list("advertising-routers");
} else
- printf(" No advertising router\n");
+ xo_emit(" No advertising router\n");
+ xo_close_instance("prefix-list");
}
free(buf);
+
+ xo_close_list("prefix-list");
}
static void
@@ -1223,10 +1336,10 @@ pfx_flush()
int sock;
if ((sock = socket(AF_INET6, SOCK_DGRAM, 0)) < 0)
- err(1, "socket");
+ xo_err(1, "socket");
strlcpy(dummyif, "lo0", sizeof(dummyif)); /* dummy */
if (ioctl(sock, SIOCSPFXFLUSH_IN6, (caddr_t)&dummyif) < 0)
- err(1, "ioctl(SIOCSPFXFLUSH_IN6)");
+ xo_err(1, "ioctl(SIOCSPFXFLUSH_IN6)");
close(sock);
}
@@ -1238,10 +1351,10 @@ rtr_flush()
int sock;
if ((sock = socket(AF_INET6, SOCK_DGRAM, 0)) < 0)
- err(1, "socket");
+ xo_err(1, "socket");
strlcpy(dummyif, "lo0", sizeof(dummyif)); /* dummy */
if (ioctl(sock, SIOCSRTRFLUSH_IN6, (caddr_t)&dummyif) < 0)
- err(1, "ioctl(SIOCSRTRFLUSH_IN6)");
+ xo_err(1, "ioctl(SIOCSRTRFLUSH_IN6)");
close(sock);
}
@@ -1253,10 +1366,10 @@ harmonize_rtr()
int sock;
if ((sock = socket(AF_INET6, SOCK_DGRAM, 0)) < 0)
- err(1, "socket");
+ xo_err(1, "socket");
strlcpy(dummyif, "lo0", sizeof(dummyif)); /* dummy */
if (ioctl(sock, SIOCSNDFLUSH_IN6, (caddr_t)&dummyif) < 0)
- err(1, "ioctl(SIOCSNDFLUSH_IN6)");
+ xo_err(1, "ioctl(SIOCSNDFLUSH_IN6)");
close(sock);
}
@@ -1273,17 +1386,17 @@ setdefif(char *ifname)
ifindex = 0;
else {
if ((ifindex = if_nametoindex(ifname)) == 0)
- err(1, "failed to resolve i/f index for %s", ifname);
+ xo_err(1, "failed to resolve i/f index for %s", ifname);
}
if ((sock = socket(AF_INET6, SOCK_DGRAM, 0)) < 0)
- err(1, "socket");
+ xo_err(1, "socket");
strlcpy(ndifreq.ifname, "lo0", sizeof(ndifreq.ifname)); /* dummy */
ndifreq.ifindex = ifindex;
if (ioctl(sock, SIOCSDEFIFACE_IN6, (caddr_t)&ndifreq) < 0)
- err(1, "ioctl(SIOCSDEFIFACE_IN6)");
+ xo_err(1, "ioctl(SIOCSDEFIFACE_IN6)");
close(sock);
}
@@ -1296,21 +1409,21 @@ getdefif()
int sock;
if ((sock = socket(AF_INET6, SOCK_DGRAM, 0)) < 0)
- err(1, "socket");
+ xo_err(1, "socket");
memset(&ndifreq, 0, sizeof(ndifreq));
strlcpy(ndifreq.ifname, "lo0", sizeof(ndifreq.ifname)); /* dummy */
if (ioctl(sock, SIOCGDEFIFACE_IN6, (caddr_t)&ndifreq) < 0)
- err(1, "ioctl(SIOCGDEFIFACE_IN6)");
+ xo_err(1, "ioctl(SIOCGDEFIFACE_IN6)");
if (ndifreq.ifindex == 0)
- printf("No default interface.\n");
+ xo_emit("No default interface.\n");
else {
if ((if_indextoname(ndifreq.ifindex, ifname)) == NULL)
- err(1, "failed to resolve ifname for index %lu",
+ xo_err(1, "failed to resolve ifname for index %lu",
ndifreq.ifindex);
- printf("ND default interface = %s\n", ifname);
+ xo_emit("ND default interface = {:default-interface/%s}\n", ifname);
}
close(sock);
@@ -1369,7 +1482,8 @@ ts_print(const struct timeval *tvp)
/* Default */
sec = (tvp->tv_sec + thiszone) % 86400;
- (void)printf("%02d:%02d:%02d.%06u ",
+ xo_emit("{:tv_sec/%lld}{:tv_usec/%lld}%02d:%02d:%02d.%06u ",
+ tvp->tv_sec, tvp->tv_usec,
sec / 3600, (sec % 3600) / 60, sec % 60, (u_int32_t)tvp->tv_usec);
}
diff --git a/usr.sbin/pkg/Makefile b/usr.sbin/pkg/Makefile
index 29bd6ea5e1af..e40265146657 100644
--- a/usr.sbin/pkg/Makefile
+++ b/usr.sbin/pkg/Makefile
@@ -10,7 +10,7 @@ PKGCONFBRANCH?= latest
. if ${BRANCH:MBETA*} || ${BRANCH:MRC*} || ${BRANCH:MRELEASE*}
PKGCONFBRANCH?= quarterly
. else
-. if ${MACHINE} != "amd64" && ${MACHINE} != "i386"
+. if ${MACHINE} != "amd64" && ${MACHINE} != "i386" && ${MACHINE} != "arm64"
PKGCONFBRANCH?= quarterly
. else
PKGCONFBRANCH?= latest
diff --git a/usr.sbin/pkg/pkg.7 b/usr.sbin/pkg/pkg.7
index b92f5c9820cd..c8ebf8272092 100644
--- a/usr.sbin/pkg/pkg.7
+++ b/usr.sbin/pkg/pkg.7
@@ -24,7 +24,7 @@
.\"
.\" $FreeBSD$
.\"
-.Dd February 7, 2021
+.Dd June 30, 2022
.Dt PKG 7
.Os
.Sh NAME
@@ -32,21 +32,19 @@
.Nd a utility for manipulating packages
.Sh SYNOPSIS
.Nm
-.Ao Ar command Ac
+.Ar command ...
.Nm
-add
-.Op Fl f
+.Cm add
+.Op Fl fy
.Op Fl r Ar reponame
-.Op Fl y
-.Ao Pa pkg.txz Ac
+.Ar pkg.txz
.Nm
.Fl N
.Nm
-.Op Fl 4 | Fl 6
-bootstrap
-.Op Fl f
+.Op Fl 46
+.Cm bootstrap
+.Op Fl fy
.Op Fl r Ar reponame
-.Op Fl y
.Sh DESCRIPTION
.Nm
is the package management tool.
@@ -62,15 +60,15 @@ The first time invoked,
will bootstrap the real
.Xr pkg 8
from a remote repository.
-.Bl -tag -width "pkg bootstrap"
-.It Nm Ao Ar command Ac
+.Bl -tag
+.It Nm Ar command ...
If
.Xr pkg 8
is not installed yet, it will be fetched, have its signature verified,
installed, and then have the original command forwarded to it.
If already installed, the command requested will be forwarded to the real
.Xr pkg 8 .
-.It Nm Li add Oo Fl f Oc Oo Fl r Ar reponame Oc Oo Fl y Oc Ao Pa pkg.txz Ac
+.It Nm Cm add Oo Fl fy Oc Oo Fl r Ar reponame Oc Ar pkg.txz
Install
.Xr pkg 8
from a local package instead of fetching from remote.
@@ -96,8 +94,8 @@ Do not bootstrap, just determine if
is actually installed or not.
Returns 0 and the number of packages installed
if it is, otherwise 1.
-.It Nm Oo Fl 4 | Fl 6 Oc Li bootstrap Oo Fl f Oc \
-Oo Fl r Ar reponame Oc Oo Fl y Oc
+.It Nm Oo Fl 46 Oc Cm bootstrap Oo Fl fy Oc \
+Oo Fl r Ar reponame Oc
Attempt to bootstrap and do not forward anything to
.Xr pkg 8
after it is installed.
@@ -243,7 +241,7 @@ configuration files.
.Sh FILES
Configuration is read from the files in the listed order.
This path can be changed by setting
-.Sy REPOS_DIR .
+.Ev REPOS_DIR .
The last enabled repository is the one used for bootstrapping
.Xr pkg 8 .
.Bl -tag -width "/usr/local/etc/pkg/repos/*.conf"
@@ -282,12 +280,12 @@ Remove unneeded dependencies:
.Dl % pkg autoremove
.Pp
Change a package from automatic to non-automatic, which will prevent
-.Ic autoremove
+.Xr pkg-autoremove 8
from removing it:
.Dl % pkg set -A 0 perl
.Pp
Change a package from non-automatic to automatic, which will make
-.Ic autoremove
+.Xr pkg-autoremove 8
allow it be removed once nothing depends on it:
.Dl % pkg set -A 1 perl
.Pp
diff --git a/usr.sbin/pkg/pkg.c b/usr.sbin/pkg/pkg.c
index 292f4a68b824..fdc519be8492 100644
--- a/usr.sbin/pkg/pkg.c
+++ b/usr.sbin/pkg/pkg.c
@@ -236,9 +236,7 @@ fetch_to_fd(const char *url, char *path, const char *fetchOpts)
--retry;
if (retry <= 0)
goto fetchfail;
- if (mirrors == NULL) {
- sleep(1);
- } else {
+ if (mirrors != NULL) {
current = current->next;
if (current == NULL)
current = mirrors;
diff --git a/usr.sbin/pmcannotate/pmcannotate.c b/usr.sbin/pmcannotate/pmcannotate.c
index 03f384d3f531..f3ffeed92738 100644
--- a/usr.sbin/pmcannotate/pmcannotate.c
+++ b/usr.sbin/pmcannotate/pmcannotate.c
@@ -434,11 +434,6 @@ fqueue_getall(const char *bin, char *temp, int asmf)
start = agg->ag_ostart;
end = agg->ag_oend;
- /*
- * Fix-up the end address in order to show it in the objdump's
- * trace.
- */
- end++;
if (asmf)
snprintf(tmpf, sizeof(tmpf),
"objdump --start-address=%p "
diff --git a/usr.sbin/pmcstat/pmcstat.c b/usr.sbin/pmcstat/pmcstat.c
index 3e2d101ab113..f366e2175a25 100644
--- a/usr.sbin/pmcstat/pmcstat.c
+++ b/usr.sbin/pmcstat/pmcstat.c
@@ -59,6 +59,7 @@ __FBSDID("$FreeBSD$");
#include <regex.h>
#include <signal.h>
#include <stdarg.h>
+#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
@@ -116,6 +117,7 @@ static int pmcstat_kq;
static kvm_t *pmcstat_kvm;
static struct kinfo_proc *pmcstat_plist;
struct pmcstat_args args;
+static bool libpmc_initialized = false;
static void
pmcstat_get_cpumask(const char *cpuspec, cpuset_t *cpumask)
@@ -419,6 +421,22 @@ pmcstat_topexit(void)
endwin();
}
+static inline void
+libpmc_initialize(int *npmc)
+{
+
+ if (libpmc_initialized)
+ return;
+ if (pmc_init() < 0)
+ err(EX_UNAVAILABLE, "ERROR: Initialization of the pmc(3)"
+ " library failed");
+
+ /* assume all CPUs are identical */
+ if ((*npmc = pmc_npmc(0)) < 0)
+ err(EX_OSERR, "ERROR: Cannot determine the number of PMCs on "
+ "CPU %d", 0);
+ libpmc_initialized = true;
+}
/*
* Main
*/
@@ -426,14 +444,14 @@ pmcstat_topexit(void)
int
main(int argc, char **argv)
{
- cpuset_t cpumask, rootmask;
+ cpuset_t cpumask, dommask, rootmask;
double interval;
double duration;
int option, npmc;
int c, check_driver_stats;
int do_callchain, do_descendants, do_logproccsw, do_logprocexit;
- int do_print, do_read, do_listcounters, do_descr;
- int do_userspace;
+ int do_print, do_read, do_listcounters, do_descr, domains;
+ int do_userspace, i;
size_t len;
int graphdepth;
int pipefd[2], rfd;
@@ -450,6 +468,7 @@ main(int argc, char **argv)
struct winsize ws;
struct stat sb;
char buffer[PATH_MAX];
+ uint32_t caps;
check_driver_stats = 0;
current_sampling_count = 0;
@@ -460,6 +479,7 @@ main(int argc, char **argv)
do_logproccsw = 0;
do_logprocexit = 0;
do_listcounters = 0;
+ domains = 0;
use_cumulative_counts = 0;
graphfilename = "-";
args.pa_required = 0;
@@ -489,8 +509,10 @@ main(int argc, char **argv)
bzero(&ds_end, sizeof(ds_end));
ev = NULL;
event = NULL;
+ caps = 0;
CPU_ZERO(&cpumask);
+
/* Default to using the running system kernel. */
len = 0;
if (sysctlbyname("kern.bootfile", NULL, &len, NULL, 0) == -1)
@@ -500,6 +522,9 @@ main(int argc, char **argv)
errx(EX_SOFTWARE, "ERROR: Out of memory.");
if (sysctlbyname("kern.bootfile", args.pa_kernel, &len, NULL, 0) == -1)
err(EX_OSERR, "ERROR: Cannot determine path of running kernel");
+ len = sizeof(domains);
+ if (sysctlbyname("vm.ndomains", &domains, &len, NULL, 0) == -1)
+ err(EX_OSERR, "ERROR: Cannot get number of domains");
/*
* The initial CPU mask specifies the root mask of this process
@@ -640,6 +665,7 @@ main(int argc, char **argv)
case 's': /* system-wide counting PMC */
case 'P': /* process virtual sampling PMC */
case 'S': /* system-wide sampling PMC */
+ caps = 0;
if ((ev = malloc(sizeof(*ev))) == NULL)
errx(EX_SOFTWARE, "ERROR: Out of memory.");
@@ -707,12 +733,48 @@ main(int argc, char **argv)
errx(EX_SOFTWARE, "ERROR: Out of memory.");
(void) strncpy(ev->ev_name, optarg, c);
*(ev->ev_name + c) = '\0';
+ libpmc_initialize(&npmc);
+ if (args.pa_flags & FLAG_HAS_SYSTEM_PMCS) {
+ if (pmc_allocate(ev->ev_spec, ev->ev_mode,
+ ev->ev_flags, ev->ev_cpu, &ev->ev_pmcid,
+ ev->ev_count) < 0)
+ err(EX_OSERR, "ERROR: Cannot allocate "
+ "system-mode pmc with specification"
+ " \"%s\"", ev->ev_spec);
+ if (pmc_capabilities(ev->ev_pmcid, &caps)) {
+ pmc_release(ev->ev_pmcid);
+ err(EX_OSERR, "ERROR: Cannot get pmc "
+ "capabilities");
+ }
+ }
+
STAILQ_INSERT_TAIL(&args.pa_events, ev, ev_next);
+ if ((caps & PMC_CAP_SYSWIDE) == PMC_CAP_SYSWIDE)
+ break;
+ if ((caps & PMC_CAP_DOMWIDE) == PMC_CAP_DOMWIDE) {
+ CPU_ZERO(&cpumask);
+ /*
+ * Get number of domains and allocate one
+ * counter in each.
+ * First already allocated.
+ */
+ for (i = 1; i < domains; i++) {
+ CPU_ZERO(&dommask);
+ cpuset_getaffinity(CPU_LEVEL_WHICH,
+ CPU_WHICH_DOMAIN, i, sizeof(dommask),
+ &dommask);
+ CPU_SET(CPU_FFS(&dommask) - 1, &cpumask);
+ }
+ args.pa_flags |= FLAGS_HAS_CPUMASK;
+ }
if (option == 's' || option == 'S') {
CPU_CLR(ev->ev_cpu, &cpumask);
+ pmc_id_t saved_pmcid = ev->ev_pmcid;
+ ev->ev_pmcid = PMC_ID_INVALID;
pmcstat_clone_event_descriptor(ev, &cpumask, &args);
+ ev->ev_pmcid = saved_pmcid;
CPU_SET(ev->ev_cpu, &cpumask);
}
@@ -1050,17 +1112,8 @@ main(int argc, char **argv)
}
/* if we've been asked to process a log file, skip init */
- if ((args.pa_flags & FLAG_READ_LOGFILE) == 0) {
- if (pmc_init() < 0)
- err(EX_UNAVAILABLE,
- "ERROR: Initialization of the pmc(3) library failed"
- );
-
- if ((npmc = pmc_npmc(0)) < 0) /* assume all CPUs are identical */
- err(EX_OSERR,
-"ERROR: Cannot determine the number of PMCs on CPU %d",
- 0);
- }
+ if ((args.pa_flags & FLAG_READ_LOGFILE) == 0)
+ libpmc_initialize(&npmc);
/* Allocate a kqueue */
if ((pmcstat_kq = kqueue()) < 0)
diff --git a/usr.sbin/ppp/ppp.8 b/usr.sbin/ppp/ppp.8
index 00470f6e5273..9a7c362c96ef 100644
--- a/usr.sbin/ppp/ppp.8
+++ b/usr.sbin/ppp/ppp.8
@@ -25,7 +25,7 @@
.\"
.\" $FreeBSD$
.\"
-.Dd May 20, 2021
+.Dd June 27, 2022
.Dt PPP 8
.Os
.Sh NAME
@@ -500,10 +500,7 @@ after altering
Although not strictly relevant to
.Nm Ns No 's
operation, you should configure your resolver so that it works correctly.
-This can be done by configuring a local DNS
-(using
-.Xr named 8 )
-or by adding the correct
+This can be done by configuring a local DNS resolver or by adding the correct
.Sq nameserver
lines to the file
.Pa /etc/resolv.conf .
@@ -6064,7 +6061,6 @@ This socket is used to pass links between different instances of
.Xr getty 8 ,
.Xr inetd 8 ,
.Xr init 8 ,
-.Xr named 8 ,
.Xr ping 8 ,
.Xr pppctl 8 ,
.Xr pppoed 8 ,
diff --git a/usr.sbin/prometheus_sysctl_exporter/prometheus_sysctl_exporter.c b/usr.sbin/prometheus_sysctl_exporter/prometheus_sysctl_exporter.c
index e452e305d2bf..931d8e965d00 100644
--- a/usr.sbin/prometheus_sysctl_exporter/prometheus_sysctl_exporter.c
+++ b/usr.sbin/prometheus_sysctl_exporter/prometheus_sysctl_exporter.c
@@ -67,13 +67,12 @@ oid_get_root(struct oid *o)
}
/* Obtains the OID for a sysctl by name. */
-static void
+static bool
oid_get_by_name(struct oid *o, const char *name)
{
o->len = nitems(o->id);
- if (sysctlnametomib(name, o->id, &o->len) != 0)
- err(1, "sysctl(%s)", name);
+ return (sysctlnametomib(name, o->id, &o->len) == 0);
}
/* Returns whether an OID is placed below another OID. */
@@ -644,7 +643,15 @@ main(int argc, char *argv[])
for (i = 0; i < argc; ++i) {
struct oid o, root;
- oid_get_by_name(&root, argv[i]);
+ if (!oid_get_by_name(&root, argv[i])) {
+ /*
+ * Ignore trees provided as arguments that
+ * can't be found. They might belong, for
+ * example, to kernel modules not currently
+ * loaded.
+ */
+ continue;
+ }
o = root;
do {
oid_print(&o, &on, print_descriptions, exclude, include, fp);
diff --git a/usr.sbin/pstat/pstat.8 b/usr.sbin/pstat/pstat.8
index c6cc99e5571b..db00b10c3bc5 100644
--- a/usr.sbin/pstat/pstat.8
+++ b/usr.sbin/pstat/pstat.8
@@ -35,7 +35,7 @@
.\" @(#)pstat.8 8.5 (Berkeley) 5/13/94
.\" $FreeBSD$
.\"
-.Dd October 11, 2014
+.Dd July 8, 2022
.Dt PSTAT 8
.Os
.Sh NAME
@@ -229,6 +229,26 @@ is also specified,
extract the name list from the specified system instead of the default,
which is the kernel image the system has booted from.
.El
+.Sh ENVIRONMENT
+.Bl -tag -width BLOCKSIZE
+.It Ev BLOCKSIZE
+If the environment variable
+.Ev BLOCKSIZE
+is set, and the
+.Fl h , k ,
+or
+.Fl m
+options are not specified, the block counts will be displayed in units of
+that block size.
+If
+.Ev BLOCKSIZE
+is not set, and the
+.Fl h , k ,
+or
+.Fl m
+options are not specified, the block counts will be displayed in 512-byte
+blocks.
+.El
.Sh SEE ALSO
.Xr ps 1 ,
.Xr systat 1 ,
diff --git a/usr.sbin/pw/Makefile b/usr.sbin/pw/Makefile
index 353eac132c7d..d994c289a04c 100644
--- a/usr.sbin/pw/Makefile
+++ b/usr.sbin/pw/Makefile
@@ -1,5 +1,6 @@
# $FreeBSD$
+PACKAGE= runtime
PROG= pw
MAN= pw.conf.5 pw.8
SRCS= pw.c pw_conf.c pw_user.c pw_group.c pw_log.c pw_nis.c pw_vpw.c \
diff --git a/usr.sbin/pw/psdate.c b/usr.sbin/pw/psdate.c
index 8c833b5c69c7..4baabb30427e 100644
--- a/usr.sbin/pw/psdate.c
+++ b/usr.sbin/pw/psdate.c
@@ -128,8 +128,8 @@ parse_datesub(char const * str, struct tm *t)
l = newlocale(LC_ALL_MASK, "C", NULL);
- memset(&tm, 0, sizeof(tm));
for (i=0; valid_formats[i] != NULL; i++) {
+ memset(&tm, 0, sizeof(tm));
ret = strptime_l(str, valid_formats[i], &tm, l);
if (ret && *ret == '\0') {
t->tm_mday = tm.tm_mday;
diff --git a/usr.sbin/pw/pw.8 b/usr.sbin/pw/pw.8
index d9072872bc2b..64c22ce49848 100644
--- a/usr.sbin/pw/pw.8
+++ b/usr.sbin/pw/pw.8
@@ -24,7 +24,7 @@
.\"
.\" $FreeBSD$
.\"
-.Dd April 3, 2022
+.Dd July 21, 2022
.Dt PW 8
.Os
.Sh NAME
@@ -541,6 +541,11 @@ This is like
.Fl h ,
but the password should be supplied already encrypted in a form
suitable for writing directly to the password database.
+See
+.Xr openssl-passwd 1
+and
+.Xr crypt 3
+for more details about generating an encrypted password hash.
.El
.Pp
It is possible to use
diff --git a/usr.sbin/quot/quot.c b/usr.sbin/quot/quot.c
index fa1ee1c65fe7..7e84dd79e148 100644
--- a/usr.sbin/quot/quot.c
+++ b/usr.sbin/quot/quot.c
@@ -550,7 +550,7 @@ quot(char *name, char *mp)
close(fd);
return;
}
- switch (sbget(fd, &fs, STDSB)) {
+ switch (errno = sbget(fd, &fs, UFS_STDSB, UFS_NOCSUM)) {
case 0:
break;
case ENOENT:
@@ -567,8 +567,6 @@ quot(char *name, char *mp)
printf(" (%s)",mp);
putchar('\n');
(*func)(fd, fs, name);
- free(fs->fs_csp);
- free(fs->fs_si);
free(fs);
close(fd);
}
diff --git a/usr.sbin/rpcbind/check_bound.c b/usr.sbin/rpcbind/check_bound.c
index cf0a029dad72..1f9382d33730 100644
--- a/usr.sbin/rpcbind/check_bound.c
+++ b/usr.sbin/rpcbind/check_bound.c
@@ -161,7 +161,7 @@ char *
mergeaddr(SVCXPRT *xprt, char *netid, char *uaddr, char *saddr)
{
struct fdlist *fdl;
- struct svc_dg_data *dg_data;
+ struct netbuf *callee;
char *c_uaddr, *s_uaddr, *m_uaddr, *allocated_uaddr = NULL;
for (fdl = fdhead; fdl; fdl = fdl->next)
@@ -182,12 +182,11 @@ mergeaddr(SVCXPRT *xprt, char *netid, char *uaddr, char *saddr)
* address by which it contacted us. Use that for the "client" uaddr,
* otherwise use the info from the SVCXPRT.
*/
- dg_data = (struct svc_dg_data*)xprt->xp_p2;
- if (dg_data != NULL && dg_data->su_srcaddr.buf != NULL) {
- c_uaddr = taddr2uaddr(fdl->nconf, &dg_data->su_srcaddr);
+ callee = svc_getrpccallee(xprt);
+ if (callee != NULL && callee->buf != NULL) {
+ c_uaddr = taddr2uaddr(fdl->nconf, callee);
allocated_uaddr = c_uaddr;
- }
- else if (saddr != NULL) {
+ } else if (saddr != NULL) {
c_uaddr = saddr;
} else {
c_uaddr = taddr2uaddr(fdl->nconf, svc_getrpccaller(xprt));
diff --git a/usr.sbin/rpcbind/util.c b/usr.sbin/rpcbind/util.c
index e497e9227690..7662f2357d48 100644
--- a/usr.sbin/rpcbind/util.c
+++ b/usr.sbin/rpcbind/util.c
@@ -101,7 +101,7 @@ bitmaskcmp(struct sockaddr *dst, struct sockaddr *src, struct sockaddr *mask)
/*
* Find a server address that can be used by `caller' to contact
- * the local service specified by `serv_uaddr'. If `clnt_uaddr' is
+ * the local service specified by `serv_uaddr'. If `contct_uaddr' is
* non-NULL, it is used instead of `caller' as a hint suggesting
* the best address (e.g. the `r_addr' field of an rpc, which
* contains the rpcbind server address that the caller used).
@@ -110,8 +110,8 @@ bitmaskcmp(struct sockaddr *dst, struct sockaddr *src, struct sockaddr *mask)
* string which should be freed by the caller. On error, returns NULL.
*/
char *
-addrmerge(struct netbuf *caller, const char *serv_uaddr, const char *clnt_uaddr,
- const char *netid)
+addrmerge(struct netbuf *caller, const char *serv_uaddr,
+ const char *contct_uaddr, const char *netid)
{
struct ifaddrs *ifap, *ifp = NULL, *bestif;
struct netbuf *serv_nbp = NULL, *hint_nbp = NULL, tbuf;
@@ -128,7 +128,7 @@ addrmerge(struct netbuf *caller, const char *serv_uaddr, const char *clnt_uaddr,
#ifdef ND_DEBUG
if (debugging)
fprintf(stderr, "addrmerge(caller, %s, %s, %s\n", serv_uaddr,
- clnt_uaddr == NULL ? "NULL" : clnt_uaddr, netid);
+ contct_uaddr == NULL ? "NULL" : contct_uaddr, netid);
#endif
caller_sa = caller->buf;
if ((nconf = rpcbind_get_conf(netid)) == NULL)
@@ -137,15 +137,15 @@ addrmerge(struct netbuf *caller, const char *serv_uaddr, const char *clnt_uaddr,
goto freeit;
/*
- * Use `clnt_uaddr' as the hint if non-NULL, but ignore it if its
+ * Use `contct_uaddr' as the hint if non-NULL, but ignore it if its
* address family is different from that of the caller.
*/
hint_sa = NULL;
- if (clnt_uaddr != NULL) {
+ if (contct_uaddr != NULL) {
#ifdef ND_DEBUG
- hint_uaddr = clnt_uaddr;
+ hint_uaddr = contct_uaddr;
#endif
- if ((hint_nbp = uaddr2taddr(nconf, clnt_uaddr)) == NULL)
+ if ((hint_nbp = uaddr2taddr(nconf, contct_uaddr)) == NULL)
goto freeit;
hint_sa = hint_nbp->buf;
}
diff --git a/usr.sbin/sesutil/sesutil.8 b/usr.sbin/sesutil/sesutil.8
index 101c93ae40ee..80f3686e0671 100644
--- a/usr.sbin/sesutil/sesutil.8
+++ b/usr.sbin/sesutil/sesutil.8
@@ -24,7 +24,7 @@
.\"
.\" $FreeBSD$
.\"
-.Dd December 12, 2019
+.Dd July 5, 2022
.Dt SESUTIL 8
.Os
.Sh NAME
@@ -34,13 +34,25 @@
.Nm
.Cm fault
.Op Fl u Ar /dev/sesN
-.Aq Ar disk | Ar sesid | Li all
-.Op on | off
+.Ar disk Ns | Ns Cm all
+.Cm on Ns | Ns Cm off
+.Nm
+.Cm fault
+.Fl u Ar /dev/sesN
+.Sm off
+.Ar sesid
+.Sm on
+.Cm on Ns | Ns Cm off
.Nm
.Cm locate
.Op Fl u Ar /dev/sesN
-.Aq Ar disk | Ar sesid | Li all
-.Op on | off
+.Ar disk Ns | Ns Cm all
+.Cm on Ns | Ns Cm off
+.Nm
+.Cm locate
+.Fl u Ar /dev/sesN
+.Ar sesid
+.Cm on Ns | Ns Cm off
.Nm
.Cm map
.Op Fl -libxo Ar options
@@ -61,22 +73,16 @@ Services (SES) devices.
.Pp
List of supported commands:
.Bl -tag -width indent
-.It Fl -libxo
-Generate output via
-.Xr libxo 3
-in a selection of different human and machine readable formats.
-See
-.Xr xo_parse_args 3
-.It Cm fault Oo Fl u Ar /dev/sesN Oc Ao Ar disk | Li all Ac Op on | off
+.It Cm fault Oo Fl u Ar /dev/sesN Oc Ar disk Ns | Ns Cm all Cm on Ns | Ns Cm off
Change the state of the external fault LED associated with
.Ar disk .
.Ar disk
can be the device name of the disk, like
-.Cm da12 ,
+.Ql da12 ,
or
-.Ql all .
+.Cm all
to indicate all disks attached to SES controllers.
-.It Cm fault Fl u Ar /dev/sesN Ar sesid Op on | off
+.It Cm fault Fl u Ar /dev/sesN Ar sesid Cm on Ns | Ns Cm off
Change the state of the external fault LED associated with an element
connected to the SES controller.
.Ar sesid
@@ -84,16 +90,16 @@ must be the element ID of a valid item attached to the controller.
Use the
.Cm map
command to list the elements attached to a controller.
-.It Cm locate Oo Fl u Ar /dev/sesN Oc Ao Ar disk | Li all Ac Op on | off
+.It Cm locate Oo Fl u Ar /dev/sesN Oc Ar disk Ns | Ns Cm all Cm on Ns | Ns Cm off
Change the state of the external locate LED associated with
.Ar disk .
.Ar disk
can be the device name of the disk, like
-.Cm da12 ,
+.Ql da12 ,
or
-.Ql all .
+.Cm all
to indicate all disks attached to SES controllers.
-.It Cm locate Fl u Ar /dev/sesN Ar sesid Op on | off
+.It Cm locate Fl u Ar /dev/sesN Ar sesid Cm on Ns | Ns Cm off
Change the state of the external locate LED associated with an element
connected to the SES controller.
.Ar sesid
@@ -101,22 +107,32 @@ must be the element ID of a valid item attached to the controller.
Use the
.Cm map
command to list the elements attached to a controller.
-.It Cm show Op Fl u Ar /dev/sesN
-Display user-friendly summary of specified
+.It Cm map Oo Fl -libxo Ar options Oc Op Fl u Ar /dev/sesN
+Display a map of all elements connected to the specified
.Xr ses 4
controller.
If no controller is specified, all controllers are mapped.
-.It Cm map Op Fl u Ar /dev/sesN
-Display a map of all elements connected to the specified
+.It Cm show Oo Fl -libxo Ar options Oc Op Fl u Ar /dev/sesN
+Display user-friendly summary of specified
.Xr ses 4
controller.
If no controller is specified, all controllers are mapped.
-.It Cm status Op Fl u Ar /dev/sesN
+.It Cm status Oo Fl -libxo Ar options Oc Op Fl u Ar /dev/sesN
Display the status of the specified
.Xr ses 4
controller.
If no controller is specified, the status of each controller is returned.
.El
+.Pp
+Common options:
+.Bl -tag -width indent
+.It Fl -libxo Ar options
+Generate output via
+.Xr libxo 3
+in a selection of different human and machine readable formats.
+See
+.Xr xo_parse_args 3
+.El
.Sh EXAMPLES
Turn off all locate LEDs:
.Pp
diff --git a/usr.sbin/tcpsso/tcpsso.8 b/usr.sbin/tcpsso/tcpsso.8
index b3aee93ae5da..14402ac4daee 100644
--- a/usr.sbin/tcpsso/tcpsso.8
+++ b/usr.sbin/tcpsso/tcpsso.8
@@ -24,7 +24,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.Dd February 10, 2022
+.Dd June 30, 2022
.Dt TCPSSO 8
.Os
.Sh NAME
@@ -73,6 +73,15 @@ and value
.Ar optval
on a TCP endpoint from the command line.
.Pp
+TCP endpoints in the
+.Dv TIME_WAIT
+state can not be handled by
+.Nm .
+TCP endpoints in the
+.Dv SYN_RCVD
+state can only be handled if their prior state was
+.Dv SYN_SENT .
+.Pp
.Op Ar level
can be specified as a non negative number or a symbolic name like
.Dv SOL_SOCKET ,
@@ -129,8 +138,8 @@ If
.Fl a
is specified then
.Nm
-will apply the socket option to all TCP endpoints not being in the state
-.Dv TIME_WAIT .
+will apply the socket option to all TCP endpoints subject to the above state
+restrictions.
.Pp
If
.Fl C Ar cc-algo
@@ -139,8 +148,7 @@ is specified then
will apply the socket option to all TCP endpoints using the TCP
congestion control algorithm
.Ar cc-algo
-and not being in the state
-.Dv TIME_WAIT .
+and subject to the above state restrictions.
.Pp
If
.Fl S Ar stack
@@ -149,8 +157,7 @@ is specified then
will apply the socket option to all TCP endpoints using the TCP
stack
.Ar stack
-and not being in the state
-.Dv TIME_WAIT .
+and subject to the above state restrictions.
.Pp
If
.Fl s Ar state
@@ -170,6 +177,12 @@ is one of
.Dv CLOSING ,
.Dv LAST_ACK ,
.Dv FIN_WAIT_2 .
+Using
+.Dv SYN_RCVD
+only applies to TCP endpoints in the state
+.Dv SYN_RCVD
+if their prior state was
+.Dv SYN_SENT .
.Pp
If multiple of
.Fl C Ar cc-algo ,
diff --git a/usr.sbin/uhsoctl/uhsoctl.1 b/usr.sbin/uhsoctl/uhsoctl.1
index 826a1ec94c74..839be1021d70 100644
--- a/usr.sbin/uhsoctl/uhsoctl.1
+++ b/usr.sbin/uhsoctl/uhsoctl.1
@@ -23,7 +23,7 @@
.\"
.\" $FreeBSD$
.\"
-.Dd Aug 12, 2009
+.Dd August 12, 2009
.Dt UHSOCTL 1
.Os
.Sh NAME
diff --git a/usr.sbin/unbound/config.h b/usr.sbin/unbound/config.h
index 4cc2dcfeb511..67853a8fc6e5 100644
--- a/usr.sbin/unbound/config.h
+++ b/usr.sbin/unbound/config.h
@@ -731,7 +731,7 @@
#define PACKAGE_NAME "unbound"
/* Define to the full name and version of this package. */
-#define PACKAGE_STRING "unbound 1.13.1"
+#define PACKAGE_STRING "unbound 1.16.2"
/* Define to the one symbol short name of this package. */
#define PACKAGE_TARNAME "unbound"
@@ -740,7 +740,7 @@
#define PACKAGE_URL ""
/* Define to the version of this package. */
-#define PACKAGE_VERSION "1.13.1"
+#define PACKAGE_VERSION "1.16.2"
/* default pidfile location */
#define PIDFILE "/var/unbound/unbound.pid"
@@ -762,7 +762,7 @@
#define ROOT_CERT_FILE "/var/unbound/icannbundle.pem"
/* version number for resource files */
-#define RSRC_PACKAGE_VERSION 1,13,1,0
+#define RSRC_PACKAGE_VERSION 1,16,2,0
/* Directory to chdir to */
#define RUN_DIR "/var/unbound"
diff --git a/usr.sbin/wpa/Makefile.inc b/usr.sbin/wpa/Makefile.inc
index 5ce7dee4d2c5..d674d3ae5fd2 100644
--- a/usr.sbin/wpa/Makefile.inc
+++ b/usr.sbin/wpa/Makefile.inc
@@ -6,7 +6,7 @@ BINDIR?= /usr/sbin
WARNS?= 0
-WPA_DISTDIR?= ${SRCTOP}/contrib/wpa/
+WPA_DISTDIR?= ${SRCTOP}/contrib/wpa
WPA_SUPPLICANT_DISTDIR?=${WPA_DISTDIR}/wpa_supplicant
HOSTAPD_DISTDIR?= ${WPA_DISTDIR}/hostapd
@@ -66,6 +66,7 @@ CFLAGS+=-DCONFIG_WNM
CFLAGS+=-DCONFIG_WNM_AP
CFLAGS+=-DCONFIG_MBO
CFLAGS+=-DCONFIG_RSN_PREAUTH
+CFLAGS+=-DCONFIG_WEP
.if ${MK_WPA_SUPPLICANT_EAPOL} != "no"
CFLAGS+=-DCONFIG_HS20 \
diff --git a/usr.sbin/wpa/src/wps/Makefile b/usr.sbin/wpa/src/wps/Makefile
index 5f5485d69bce..8890c34fb0a3 100644
--- a/usr.sbin/wpa/src/wps/Makefile
+++ b/usr.sbin/wpa/src/wps/Makefile
@@ -27,8 +27,6 @@ SRCS= http_client.c \
wps_upnp_ssdp.c \
wps_upnp_web.c
-CFLAGS+=-DCONFIG_P2P
-
.if ${MK_INET6} != "no"
CFLAGS+= -DCONFIG_IPV6
.endif