aboutsummaryrefslogtreecommitdiff
path: root/lib/libufs
diff options
context:
space:
mode:
Diffstat (limited to 'lib/libufs')
-rw-r--r--lib/libufs/Makefile40
-rw-r--r--lib/libufs/Makefile.depend15
-rw-r--r--lib/libufs/block.c183
-rw-r--r--lib/libufs/bread.397
-rw-r--r--lib/libufs/cgread.3181
-rw-r--r--lib/libufs/cgroup.c317
-rw-r--r--lib/libufs/getinode.3129
-rw-r--r--lib/libufs/inode.c119
-rw-r--r--lib/libufs/libufs.385
-rw-r--r--lib/libufs/libufs.h187
-rw-r--r--lib/libufs/sblock.c291
-rw-r--r--lib/libufs/sbread.3203
-rw-r--r--lib/libufs/type.c207
-rw-r--r--lib/libufs/ufs_disk_close.3120
14 files changed, 2174 insertions, 0 deletions
diff --git a/lib/libufs/Makefile b/lib/libufs/Makefile
new file mode 100644
index 000000000000..3b1911d7a5cd
--- /dev/null
+++ b/lib/libufs/Makefile
@@ -0,0 +1,40 @@
+PACKAGE= ufs
+LIB_PACKAGE=
+
+LIB= ufs
+SHLIBDIR?= /lib
+SHLIB_MAJOR= 8
+
+SRCS= block.c cgroup.c gsb_crc32.c inode.c sblock.c type.c ffs_subr.c
+SRCS+= ffs_tables.c
+INCS= libufs.h
+
+MAN= bread.3 cgread.3 getinode.3 libufs.3 sbread.3 ufs_disk_close.3
+MLINKS+= bread.3 bwrite.3
+MLINKS+= bread.3 berase.3
+MLINKS+= cgread.3 cgread1.3
+MLINKS+= cgread.3 cgget.3
+MLINKS+= cgread.3 cgwrite.3
+MLINKS+= cgread.3 cgwrite1.3
+MLINKS+= cgread.3 cgput.3
+MLINKS+= getinode.3 putinode.3
+MLINKS+= sbread.3 sbwrite.3
+MLINKS+= sbread.3 sbget.3
+MLINKS+= sbread.3 sbsearch.3
+MLINKS+= sbread.3 sbfind.3
+MLINKS+= sbread.3 sbput.3
+MLINKS+= ufs_disk_close.3 ufs_disk_fillout.3
+MLINKS+= ufs_disk_close.3 ufs_disk_fillout_blank.3
+MLINKS+= ufs_disk_close.3 ufs_disk_write.3
+
+.PATH: ${SRCTOP}/sys/libkern ${SRCTOP}/sys/ufs/ffs
+
+WARNS?= 2
+
+CFLAGS+= -D_LIBUFS
+.if defined(LIBUFS_DEBUG)
+CFLAGS+= -D_LIBUFS_DEBUGGING
+.endif
+CFLAGS+= -I${.CURDIR}
+
+.include <bsd.lib.mk>
diff --git a/lib/libufs/Makefile.depend b/lib/libufs/Makefile.depend
new file mode 100644
index 000000000000..6ef78fac5cbf
--- /dev/null
+++ b/lib/libufs/Makefile.depend
@@ -0,0 +1,15 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ include \
+ include/xlocale \
+ lib/${CSU_DIR} \
+ lib/libc \
+ lib/libcompiler_rt \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/lib/libufs/block.c b/lib/libufs/block.c
new file mode 100644
index 000000000000..8bccc18ed3dd
--- /dev/null
+++ b/lib/libufs/block.c
@@ -0,0 +1,183 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2002 Juli Mallett. All rights reserved.
+ *
+ * This software was written by Juli Mallett <jmallett@FreeBSD.org> for the
+ * FreeBSD project. Redistribution and use in source and binary forms, with
+ * or without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * 1. Redistribution of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistribution 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 ``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 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/mount.h>
+#include <sys/disk.h>
+#include <sys/disklabel.h>
+#include <sys/stat.h>
+
+#include <ufs/ufs/extattr.h>
+#include <ufs/ufs/quota.h>
+#include <ufs/ufs/ufsmount.h>
+#include <ufs/ufs/dinode.h>
+#include <ufs/ffs/fs.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <libufs.h>
+
+ssize_t
+bread(struct uufsd *disk, ufs2_daddr_t blockno, void *data, size_t size)
+{
+ void *p2;
+ ssize_t cnt;
+
+ ERROR(disk, NULL);
+
+ BUF_MALLOC(&p2, data, size);
+ if (p2 == NULL) {
+ ERROR(disk, "allocate bounce buffer");
+ goto fail;
+ }
+ cnt = pread(disk->d_fd, p2, size, (off_t)(blockno * disk->d_bsize));
+ if (cnt == -1) {
+ ERROR(disk, "read error from block device");
+ goto fail;
+ }
+ if (cnt == 0) {
+ ERROR(disk, "end of file from block device");
+ goto fail;
+ }
+ if ((size_t)cnt != size) {
+ ERROR(disk, "short read or read error from block device");
+ goto fail;
+ }
+ if (p2 != data) {
+ memcpy(data, p2, size);
+ free(p2);
+ }
+ return (cnt);
+fail: memset(data, 0, size);
+ if (p2 != data) {
+ free(p2);
+ }
+ return (-1);
+}
+
+ssize_t
+bwrite(struct uufsd *disk, ufs2_daddr_t blockno, const void *data, size_t size)
+{
+ ssize_t cnt;
+ int rv;
+ void *p2;
+
+ ERROR(disk, NULL);
+
+ rv = ufs_disk_write(disk);
+ if (rv == -1) {
+ ERROR(disk, "failed to open disk for writing");
+ return (-1);
+ }
+ BUF_MALLOC(&p2, data, size);
+ if (p2 == NULL) {
+ ERROR(disk, "allocate bounce buffer");
+ return (-1);
+ }
+ if (p2 != data)
+ memcpy(p2, data, size);
+ cnt = pwrite(disk->d_fd, p2, size, (off_t)(blockno * disk->d_bsize));
+ if (p2 != data)
+ free(p2);
+ if (cnt == -1) {
+ ERROR(disk, "write error to block device");
+ return (-1);
+ }
+ if ((size_t)cnt != size) {
+ ERROR(disk, "short write to block device");
+ return (-1);
+ }
+ return (cnt);
+}
+
+#ifdef __FreeBSD_kernel__
+
+static int
+berase_helper(struct uufsd *disk, ufs2_daddr_t blockno, ufs2_daddr_t size)
+{
+ off_t ioarg[2];
+
+ ioarg[0] = blockno * disk->d_bsize;
+ ioarg[1] = size;
+ return (ioctl(disk->d_fd, DIOCGDELETE, ioarg));
+}
+
+#else
+
+static int
+berase_helper(struct uufsd *disk, ufs2_daddr_t blockno, ufs2_daddr_t size)
+{
+ char *zero_chunk;
+ off_t offset, zero_chunk_size, pwrite_size;
+ int rv;
+
+ offset = blockno * disk->d_bsize;
+ zero_chunk_size = 65536 * disk->d_bsize;
+ zero_chunk = calloc(1, zero_chunk_size);
+ if (zero_chunk == NULL) {
+ ERROR(disk, "failed to allocate memory");
+ return (-1);
+ }
+ while (size > 0) {
+ pwrite_size = size;
+ if (pwrite_size > zero_chunk_size)
+ pwrite_size = zero_chunk_size;
+ rv = pwrite(disk->d_fd, zero_chunk, pwrite_size, offset);
+ if (rv == -1) {
+ ERROR(disk, "failed writing to disk");
+ break;
+ }
+ size -= rv;
+ offset += rv;
+ rv = 0;
+ }
+ free(zero_chunk);
+ return (rv);
+}
+
+#endif
+
+int
+berase(struct uufsd *disk, ufs2_daddr_t blockno, ufs2_daddr_t size)
+{
+ int rv;
+
+ ERROR(disk, NULL);
+ rv = ufs_disk_write(disk);
+ if (rv == -1) {
+ ERROR(disk, "failed to open disk for writing");
+ return(rv);
+ }
+ return (berase_helper(disk, blockno, size));
+}
diff --git a/lib/libufs/bread.3 b/lib/libufs/bread.3
new file mode 100644
index 000000000000..b3a61b4a5325
--- /dev/null
+++ b/lib/libufs/bread.3
@@ -0,0 +1,97 @@
+.\" Author: Juli Mallett <jmallett@FreeBSD.org>
+.\" Date: June 04, 2003
+.\" Description:
+.\" Manual page for libufs functions:
+.\" bread(3)
+.\" bwrite(3)
+.\"
+.\" This file is in the public domain.
+.\"
+.Dd June 4, 2003
+.Dt BREAD 3
+.Os
+.Sh NAME
+.Nm bread , bwrite
+.Nd read and write blocks of a UFS file system
+.Sh LIBRARY
+.Lb libufs
+.Sh SYNOPSIS
+.In sys/param.h
+.In sys/mount.h
+.In ufs/ufs/ufsmount.h
+.In ufs/ufs/dinode.h
+.In ufs/ffs/fs.h
+.In libufs.h
+.Ft ssize_t
+.Fn bread "struct uufsd *disk" "ufs2_daddr_t blockno" "void *data" "size_t size"
+.Ft ssize_t
+.Fo bwrite
+.Fa "struct uufsd *disk" "ufs2_daddr_t blockno"
+.Fa "const void *data" "size_t size"
+.Fc
+.Ft int
+.Fo berase
+.Fa "struct uufsd *disk" "ufs2_daddr_t blockno" "ufs2_daddr_t size"
+.Fc
+.Sh DESCRIPTION
+The
+.Fn bread ,
+.Fn bwrite
+and
+.Fn berase
+functions provide a block read, write and erase API for
+.Xr libufs 3
+consumers.
+They operate on a userland UFS disk structure, and perform the read
+and write at a given block address, which uses the current
+.Va d_bsize
+value of the structure.
+.Sh RETURN VALUES
+The
+.Fn bread
+and
+.Fn bwrite
+functions return the amount read or written, or \-1 in case of any error,
+including short read.
+.Pp
+The
+.Fn berase
+function returns non-zero on error.
+.Sh ERRORS
+The function
+.Fn bread
+may fail and set
+.Va errno
+for any of the errors specified for the library functions
+.Xr ufs_disk_write 3
+or
+.Xr pread 2 .
+.Pp
+The function
+.Fn bwrite
+may fail and set
+.Va errno
+for any of the errors specified for the library function
+.Xr pwrite 2 .
+.Pp
+The function
+.Fn berase
+may fail and set
+.Va errno
+for any of the errors specified for the library function
+.Xr ioctl 2 .
+.Pp
+Additionally all three functions may follow the
+.Xr libufs 3
+error methodologies in situations where the amount of data written
+is not equal to the amount requested, or in case of a device error.
+.Sh SEE ALSO
+.Xr libufs 3 ,
+.Xr ufs_disk_write 3
+.Sh HISTORY
+These functions first appeared as part of
+.Xr libufs 3
+in
+.Fx 5.0 .
+.Sh AUTHORS
+.An Juli Mallett Aq Mt jmallett@FreeBSD.org
diff --git a/lib/libufs/cgread.3 b/lib/libufs/cgread.3
new file mode 100644
index 000000000000..78a62f75e82b
--- /dev/null
+++ b/lib/libufs/cgread.3
@@ -0,0 +1,181 @@
+.\" Author: Juli Mallett <jmallett@FreeBSD.org>
+.\" Date: June 04, 2003
+.\" Description:
+.\" Manual page for libufs functions:
+.\" cgget(3)
+.\" cgput(3)
+.\" cgread(3)
+.\" cgread1(3)
+.\" cgwrite(3)
+.\" cgwrite1(3)
+.\"
+.\" This file is in the public domain.
+.\"
+.Dd September 2, 2020
+.Dt CGREAD 3
+.Os
+.Sh NAME
+.Nm cgget , cgput , cgread , cgread1 , cgwrite , cgwrite1
+.Nd read/write cylinder groups of UFS disks
+.Sh LIBRARY
+.Lb libufs
+.Sh SYNOPSIS
+.In sys/param.h
+.In sys/mount.h
+.In ufs/ufs/ufsmount.h
+.In ufs/ufs/dinode.h
+.In ufs/ffs/fs.h
+.In libufs.h
+.Ft int
+.Fn cgget "int devfd" "struct fs *fs" "int cg" "struct cg *cgp"
+.Ft int
+.Fn cgput "int devfd" "struct fs *fs" "struct cg *cgp"
+.Ft int
+.Fn cgread "struct uufsd *disk"
+.Ft int
+.Fn cgread1 "struct uufsd *disk" "int cg"
+.Ft int
+.Fn cgwrite "struct uufsd *disk"
+.Ft int
+.Fn cgwrite1 "struct uufsd *disk" "int cg"
+.Sh DESCRIPTION
+The
+.Fn cgget ,
+.Fn cgread ,
+and
+.Fn cgread1
+functions provide cylinder group reads for
+.Xr libufs 3
+consumers.
+The
+.Fn cgput ,
+.Fn cgwrite ,
+and
+.Fn cgwrite1
+functions provide cylinder group writes for
+.Xr libufs 3
+consumers.
+.Pp
+The
+.Fn cgget
+function reads the cylinder group specified by
+.Fa cg
+into the buffer pointed to by
+.Fa cgp
+from the filesystem described by the
+.Fa fs
+superblock using the
+.Fa devfd
+file descriptor that references the filesystem disk.
+The
+.Fn cgget
+function is the only cylinder group read function that is safe to use
+in threaded applications.
+.Pp
+The
+.Fn cgput
+function writes the cylinder group specified by
+.Va cgp
+to the filesystem described by the
+.Fa fs
+superblock using the
+.Fa devfd
+file descriptor that references the filesystem disk.
+The
+.Fn cgput
+function is the only cylinder group write function that is safe to use
+in threaded applications.
+Note that the
+.Fn cgput
+function needs to be called only if the cylinder group has been
+modified and the on-disk copy needs to be updated.
+.Pp
+The
+.Fn cgread1
+function reads from the cylinder group specified by
+.Fa cg
+into the
+.Va d_cg
+cylinder-group structure in a user-land UFS-disk structure.
+It sets the
+.Va d_lcg
+field to the cylinder group number
+.Fa cg .
+.Pp
+The
+.Fn cgread
+function operates on sequential cylinder groups.
+Calling the
+.Fn cgread
+function is equivalent to calling
+.Fn cgread1
+with a cylinder group specifier equivalent to the value of the current
+.Va d_ccg
+field, and then incrementing the
+.Va d_ccg
+field.
+.Pp
+The
+.Fn cgwrite
+function stores on disk the cylinder group held in the
+.Va d_cg
+cylinder-group structure in a user-land UFS-disk structure.
+.Pp
+The
+.Fn cgwrite1
+function provides no additional functionality over the
+.Fn cgwrite
+function as there is only one place that a given cylinder group
+can correctly be written.
+If the caller gets the
+.Fa cg
+parameter wrong, the function fails with the error
+.Er EDOOFUS .
+This function remains only to provide backward compatibility.
+.Sh RETURN VALUES
+The
+.Fn cgread
+function returns 0 if there are no more cylinder groups to read,
+1 if there are more cylinder groups, and \-1 on error.
+The
+.Fn cgread1
+function returns 1 on success and \-1 on error.
+The other functions return 0 on success and \-1 on error.
+.Sh ERRORS
+The
+.Fn cgget ,
+.Fn cgread ,
+and
+.Fn cgread1
+functions may fail and set
+.Va errno
+for any of the errors specified for the library function
+.Xr bread 3 .
+.Pp
+The
+.Fn cgput ,
+.Fn cgwrite ,
+and
+.Fn cgwrite1
+functions may fail and set
+.Va errno
+for any of the errors specified for the library function
+.Xr bwrite 3 .
+Additionally the
+.Fn cgwrite1
+will return the
+.Er EDOOFUS
+error if the cylinder group specified does not match the
+cylinder group that it is requesting to write.
+.Sh SEE ALSO
+.Xr bread 3 ,
+.Xr bwrite 3 ,
+.Xr libufs 3
+.Sh HISTORY
+These functions first appeared as part of
+.Xr libufs 3
+in
+.Fx 5.1 .
+.Sh AUTHORS
+.An Juli Mallett Aq Mt jmallett@FreeBSD.org
+.An Marshall Kirk McKusick Aq Mt mckusick@FreeBSD.org
diff --git a/lib/libufs/cgroup.c b/lib/libufs/cgroup.c
new file mode 100644
index 000000000000..7c7ca0d242b4
--- /dev/null
+++ b/lib/libufs/cgroup.c
@@ -0,0 +1,317 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2003 Juli Mallett. All rights reserved.
+ *
+ * This software was written by Juli Mallett <jmallett@FreeBSD.org> for the
+ * FreeBSD project. Redistribution and use in source and binary forms, with
+ * or without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * 1. Redistribution of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistribution 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 ``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 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/mount.h>
+#include <sys/disklabel.h>
+#include <sys/stat.h>
+
+#include <ufs/ufs/extattr.h>
+#include <ufs/ufs/quota.h>
+#include <ufs/ufs/ufsmount.h>
+#include <ufs/ufs/dinode.h>
+#include <ufs/ffs/fs.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <libufs.h>
+
+ufs2_daddr_t
+cgballoc(struct uufsd *disk)
+{
+ u_int8_t *blksfree;
+ struct cg *cgp;
+ struct fs *fs;
+ long bno;
+
+ fs = &disk->d_fs;
+ cgp = &disk->d_cg;
+ blksfree = cg_blksfree(cgp);
+ for (bno = 0; bno < fs->fs_fpg / fs->fs_frag; bno++)
+ if (ffs_isblock(fs, blksfree, bno))
+ goto gotit;
+ return (0);
+gotit:
+ fs->fs_cs(fs, cgp->cg_cgx).cs_nbfree--;
+ ffs_clrblock(fs, blksfree, (long)bno);
+ ffs_clusteracct(fs, cgp, bno, -1);
+ cgp->cg_cs.cs_nbfree--;
+ fs->fs_cstotal.cs_nbfree--;
+ fs->fs_fmod = 1;
+ return (cgbase(fs, cgp->cg_cgx) + blkstofrags(fs, bno));
+}
+
+int
+cgbfree(struct uufsd *disk, ufs2_daddr_t bno, long size)
+{
+ u_int8_t *blksfree;
+ struct fs *fs;
+ struct cg *cgp;
+ ufs1_daddr_t fragno, cgbno;
+ int i, cg, blk, frags, bbase;
+
+ fs = &disk->d_fs;
+ cg = dtog(fs, bno);
+ if (cgread1(disk, cg) != 1)
+ return (-1);
+ cgp = &disk->d_cg;
+ cgbno = dtogd(fs, bno);
+ blksfree = cg_blksfree(cgp);
+ if (size == fs->fs_bsize) {
+ fragno = fragstoblks(fs, cgbno);
+ ffs_setblock(fs, blksfree, fragno);
+ ffs_clusteracct(fs, cgp, fragno, 1);
+ cgp->cg_cs.cs_nbfree++;
+ fs->fs_cstotal.cs_nbfree++;
+ fs->fs_cs(fs, cg).cs_nbfree++;
+ } else {
+ bbase = cgbno - fragnum(fs, cgbno);
+ /*
+ * decrement the counts associated with the old frags
+ */
+ blk = blkmap(fs, blksfree, bbase);
+ ffs_fragacct(fs, blk, cgp->cg_frsum, -1);
+ /*
+ * deallocate the fragment
+ */
+ frags = numfrags(fs, size);
+ for (i = 0; i < frags; i++)
+ setbit(blksfree, cgbno + i);
+ cgp->cg_cs.cs_nffree += i;
+ fs->fs_cstotal.cs_nffree += i;
+ fs->fs_cs(fs, cg).cs_nffree += i;
+ /*
+ * add back in counts associated with the new frags
+ */
+ blk = blkmap(fs, blksfree, bbase);
+ ffs_fragacct(fs, blk, cgp->cg_frsum, 1);
+ /*
+ * if a complete block has been reassembled, account for it
+ */
+ fragno = fragstoblks(fs, bbase);
+ if (ffs_isblock(fs, blksfree, fragno)) {
+ cgp->cg_cs.cs_nffree -= fs->fs_frag;
+ fs->fs_cstotal.cs_nffree -= fs->fs_frag;
+ fs->fs_cs(fs, cg).cs_nffree -= fs->fs_frag;
+ ffs_clusteracct(fs, cgp, fragno, 1);
+ cgp->cg_cs.cs_nbfree++;
+ fs->fs_cstotal.cs_nbfree++;
+ fs->fs_cs(fs, cg).cs_nbfree++;
+ }
+ }
+ return cgwrite(disk);
+}
+
+ino_t
+cgialloc(struct uufsd *disk)
+{
+ struct ufs2_dinode *dp2;
+ u_int8_t *inosused;
+ struct cg *cgp;
+ struct fs *fs;
+ ino_t ino;
+ int i;
+
+ fs = &disk->d_fs;
+ cgp = &disk->d_cg;
+ inosused = cg_inosused(cgp);
+ for (ino = 0; ino < fs->fs_ipg; ino++)
+ if (isclr(inosused, ino))
+ goto gotit;
+ return (0);
+gotit:
+ if (fs->fs_magic == FS_UFS2_MAGIC &&
+ ino + INOPB(fs) > cgp->cg_initediblk &&
+ cgp->cg_initediblk < cgp->cg_niblk) {
+ char block[MAXBSIZE];
+ bzero(block, (int)fs->fs_bsize);
+ dp2 = (struct ufs2_dinode *)&block;
+ for (i = 0; i < INOPB(fs); i++) {
+ dp2->di_gen = arc4random();
+ dp2++;
+ }
+ if (bwrite(disk, ino_to_fsba(fs,
+ cgp->cg_cgx * fs->fs_ipg + cgp->cg_initediblk),
+ block, fs->fs_bsize))
+ return (0);
+ cgp->cg_initediblk += INOPB(fs);
+ }
+
+ setbit(inosused, ino);
+ cgp->cg_irotor = ino;
+ cgp->cg_cs.cs_nifree--;
+ fs->fs_cstotal.cs_nifree--;
+ fs->fs_cs(fs, cgp->cg_cgx).cs_nifree--;
+ fs->fs_fmod = 1;
+
+ return (ino + (cgp->cg_cgx * fs->fs_ipg));
+}
+
+int
+cgread(struct uufsd *disk)
+{
+
+ if (disk->d_ccg >= disk->d_fs.fs_ncg)
+ return (0);
+ return (cgread1(disk, disk->d_ccg++));
+}
+
+/* Short read/write error messages from cgget()/cgput() */
+static const char *failmsg;
+
+int
+cgread1(struct uufsd *disk, int c)
+{
+
+ if (cgget(disk->d_fd, &disk->d_fs, c, &disk->d_cg) == 0) {
+ disk->d_lcg = c;
+ return (1);
+ }
+ ERROR(disk, NULL);
+ if (failmsg != NULL) {
+ ERROR(disk, failmsg);
+ return (-1);
+ }
+ switch (errno) {
+ case EINTEGRITY:
+ ERROR(disk, "cylinder group checks failed");
+ break;
+ case EIO:
+ ERROR(disk, "read error from block device");
+ break;
+ default:
+ ERROR(disk, strerror(errno));
+ break;
+ }
+ return (-1);
+}
+
+int
+cgget(int devfd, struct fs *fs, int cg, struct cg *cgp)
+{
+ uint32_t cghash, calchash;
+ size_t cnt;
+
+ failmsg = NULL;
+ if ((cnt = pread(devfd, cgp, fs->fs_cgsize,
+ fsbtodb(fs, cgtod(fs, cg)) * (fs->fs_fsize / fsbtodb(fs,1)))) < 0)
+ return (-1);
+ if (cnt == 0) {
+ failmsg = "end of file from block device";
+ errno = EIO;
+ return (-1);
+ }
+ if (cnt != fs->fs_cgsize) {
+ failmsg = "short read from block device";
+ errno = EIO;
+ return (-1);
+ }
+ calchash = cgp->cg_ckhash;
+ if ((fs->fs_metackhash & CK_CYLGRP) != 0) {
+ cghash = cgp->cg_ckhash;
+ cgp->cg_ckhash = 0;
+ calchash = calculate_crc32c(~0L, (void *)cgp, fs->fs_cgsize);
+ cgp->cg_ckhash = cghash;
+ }
+ if (cgp->cg_ckhash != calchash || !cg_chkmagic(cgp) ||
+ cgp->cg_cgx != cg) {
+ errno = EINTEGRITY;
+ return (-1);
+ }
+ return (0);
+}
+
+int
+cgwrite(struct uufsd *disk)
+{
+
+ return (cgwrite1(disk, disk->d_cg.cg_cgx));
+}
+
+int
+cgwrite1(struct uufsd *disk, int cg)
+{
+ static char errmsg[BUFSIZ];
+
+ if (cg == disk->d_cg.cg_cgx) {
+ if (ufs_disk_write(disk) == -1) {
+ ERROR(disk, "failed to open disk for writing");
+ return (-1);
+ }
+ if (cgput(disk->d_fd, &disk->d_fs, &disk->d_cg) == 0)
+ return (0);
+ ERROR(disk, NULL);
+ if (failmsg != NULL) {
+ ERROR(disk, failmsg);
+ return (-1);
+ }
+ switch (errno) {
+ case EIO:
+ ERROR(disk, "unable to write cylinder group");
+ break;
+ default:
+ ERROR(disk, strerror(errno));
+ break;
+ }
+ return (-1);
+ }
+ snprintf(errmsg, BUFSIZ, "Cylinder group %d in buffer does not match "
+ "the cylinder group %d that cgwrite1 requested",
+ disk->d_cg.cg_cgx, cg);
+ ERROR(disk, errmsg);
+ errno = EDOOFUS;
+ return (-1);
+}
+
+int
+cgput(int devfd, struct fs *fs, struct cg *cgp)
+{
+ size_t cnt;
+
+ if ((fs->fs_metackhash & CK_CYLGRP) != 0) {
+ cgp->cg_ckhash = 0;
+ cgp->cg_ckhash =
+ calculate_crc32c(~0L, (void *)cgp, fs->fs_cgsize);
+ }
+ failmsg = NULL;
+ if ((cnt = pwrite(devfd, cgp, fs->fs_cgsize,
+ fsbtodb(fs, cgtod(fs, cgp->cg_cgx)) *
+ (fs->fs_fsize / fsbtodb(fs,1)))) < 0)
+ return (-1);
+ if (cnt != fs->fs_cgsize) {
+ failmsg = "short write to block device";
+ return (-1);
+ }
+ return (0);
+}
diff --git a/lib/libufs/getinode.3 b/lib/libufs/getinode.3
new file mode 100644
index 000000000000..1668e65551e5
--- /dev/null
+++ b/lib/libufs/getinode.3
@@ -0,0 +1,129 @@
+.\" Author: Marshall Kirk McKusick <mckusick@freebsd.org>
+.\" Date: January 19, 2018
+.\" Description:
+.\" Manual page for libufs functions:
+.\" getinode(3)
+.\" putinode(3)
+.\"
+.\" This file is in the public domain.
+.\"
+.Dd September 2, 2020
+.Dt GETINODE 3
+.Os
+.Sh NAME
+.Nm getinode , putinode
+.Nd fetch and store inodes on a UFS file system
+.Sh LIBRARY
+.Lb libufs
+.Sh SYNOPSIS
+.In ufs/ufs/dinode.h
+.In ufs/ffs/fs.h
+.In libufs.h
+.Ft int
+.Fn getinode "struct uufsd *disk" "union dinodep *dp" "ino_t inumber"
+.Ft int
+.Fn putinode "struct uufsd *disk"
+.Sh DESCRIPTION
+The
+.Fn getinode
+and
+.Fn putinode
+functions provide an inode fetch and store API for
+.Xr libufs 3
+consumers.
+They operate on a userland UFS disk structure.
+The
+.Fn getinode
+function fetches the specified inode from the filesystem.
+The
+.Fn putinode
+function stores the most recently fetched inode to the filesystem.
+.Pp
+The
+.Va dinodep
+union is defined as:
+.Bd -literal -offset indent
+union dinodep {
+ struct ufs1_dinode *dp1;
+ struct ufs2_dinode *dp2;
+};
+.Ed
+.Pp
+Sample code to clear write permissions for inode number
+.Fa inumber
+stored on the filesystem described by
+.Fa diskp .
+.Bd -literal -offset indent
+#include <sys/stat.h>
+#include <err.h>
+
+#include <ufs/ufs/dinode.h>
+#include <ufs/ffs/fs.h>
+#include <libufs.h>
+
+void
+clearwrite(struct uufsd *diskp, ino_t inumber)
+{
+ union dinodep dp;
+
+ if (getinode(diskp, &dp, inumber) == -1)
+ err(1, "getinode: %s", diskp->d_error);
+ switch (diskp->d_ufs) {
+ case 1: /* UFS 1 filesystem */
+ dp.dp1->di_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
+ break;
+ case 2: /* UFS 2 filesystem */
+ dp.dp2->di_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
+ break;
+ default:
+ errx(1, "unknown filesystem type");
+ }
+ if (putinode(diskp) == -1)
+ err(1, "putinode: %s", diskp->d_error);
+}
+.Ed
+.Sh RETURN VALUES
+The
+.Fn getinode
+and
+.Fn putinode
+functions return 0 on success, or \-1 in case of any error.
+A string describing the error is stored in
+.Fa diskp->d_error .
+The global
+.Fa errno
+often provides additional information.
+.Sh ERRORS
+The function
+.Fn getinode
+may fail and set
+.Va errno
+for any of the errors specified for the library function
+.Xr pread 2 .
+It can also fail if the inode number is out of the range of inodes
+in the filesystem.
+.Pp
+The function
+.Fn putinode
+may fail and set
+.Va errno
+for any of the errors specified for the library functions
+.Xr ufs_disk_write 3
+or
+.Xr pwrite 2 .
+.Pp
+Additionally both functions may follow the
+.Xr libufs 3
+error methodologies in case of a device error.
+.Sh SEE ALSO
+.Xr pread 2 ,
+.Xr pwrite 2 ,
+.Xr libufs 3 ,
+.Xr ufs_disk_write 3
+.Sh HISTORY
+These functions first appeared as part of
+.Xr libufs 3
+in
+.Fx 13.0 .
+.Sh AUTHORS
+.An Marshall Kirk McKusick Aq Mt mckusick@FreeBSD.org
diff --git a/lib/libufs/inode.c b/lib/libufs/inode.c
new file mode 100644
index 000000000000..863e71867daa
--- /dev/null
+++ b/lib/libufs/inode.c
@@ -0,0 +1,119 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2002 Juli Mallett. All rights reserved.
+ *
+ * This software was written by Juli Mallett <jmallett@FreeBSD.org> for the
+ * FreeBSD project. Redistribution and use in source and binary forms, with
+ * or without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * 1. Redistribution of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistribution 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 ``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 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/mount.h>
+#include <sys/disklabel.h>
+#include <sys/stat.h>
+
+#include <ufs/ufs/extattr.h>
+#include <ufs/ufs/quota.h>
+#include <ufs/ufs/ufsmount.h>
+#include <ufs/ufs/dinode.h>
+#include <ufs/ffs/fs.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <libufs.h>
+
+int
+getinode(struct uufsd *disk, union dinodep *dp, ino_t inum)
+{
+ ino_t min, max;
+ caddr_t inoblock;
+ struct fs *fs;
+ struct timespec now;
+
+ ERROR(disk, NULL);
+
+ fs = &disk->d_fs;
+ if (inum >= (ino_t)fs->fs_ipg * fs->fs_ncg) {
+ ERROR(disk, "inode number out of range");
+ return (-1);
+ }
+ inoblock = (caddr_t)&disk->d_inos[0];
+ min = disk->d_inomin;
+ max = disk->d_inomax;
+
+ if (clock_gettime(CLOCK_REALTIME_FAST, &now) != 0) {
+ ERROR(disk, "cannot get current time of day");
+ return (-1);
+ }
+ if (inum >= min && inum < max)
+ goto gotit;
+ bread(disk, fsbtodb(fs, ino_to_fsba(fs, inum)), inoblock,
+ fs->fs_bsize);
+ disk->d_inomin = min = inum - (inum % INOPB(fs));
+ disk->d_inomax = max = min + INOPB(fs);
+gotit: switch (disk->d_ufs) {
+ case 1:
+ disk->d_dp.dp1 = &((struct ufs1_dinode *)inoblock)[inum - min];
+ if (ffs_oldfscompat_inode_read(fs, disk->d_dp, now.tv_sec))
+ putinode(disk);
+ if (dp != NULL)
+ *dp = disk->d_dp;
+ return (0);
+ case 2:
+ disk->d_dp.dp2 = &((struct ufs2_dinode *)inoblock)[inum - min];
+ if (dp != NULL)
+ *dp = disk->d_dp;
+ if (ffs_verify_dinode_ckhash(fs, disk->d_dp.dp2) == 0) {
+ if (ffs_oldfscompat_inode_read(fs, disk->d_dp,
+ now.tv_sec))
+ putinode(disk);
+ return (0);
+ }
+ ERROR(disk, "check-hash failed for inode read from disk");
+ return (-1);
+ default:
+ break;
+ }
+ ERROR(disk, "unknown UFS filesystem type");
+ return (-1);
+}
+
+int
+putinode(struct uufsd *disk)
+{
+ struct fs *fs;
+
+ fs = &disk->d_fs;
+ if (disk->d_ufs == 2)
+ ffs_update_dinode_ckhash(fs, disk->d_dp.dp2);
+ if (bwrite(disk, fsbtodb(fs, ino_to_fsba(&disk->d_fs, disk->d_inomin)),
+ (caddr_t)&disk->d_inos[0], disk->d_fs.fs_bsize) <= 0)
+ return (-1);
+ return (0);
+}
diff --git a/lib/libufs/libufs.3 b/lib/libufs/libufs.3
new file mode 100644
index 000000000000..aa3386ad9771
--- /dev/null
+++ b/lib/libufs/libufs.3
@@ -0,0 +1,85 @@
+.\" Author: Juli Mallett <jmallett@FreeBSD.org>
+.\" Date: June 04, 2003
+.\" Description:
+.\" Manual page for libufs.
+.\"
+.\" This file is in the public domain.
+.\"
+.Dd September 2, 2020
+.Dt LIBUFS 3
+.Os
+.Sh NAME
+.Nm libufs
+.Nd operate on UFS file systems from userland
+.Sh LIBRARY
+.Lb libufs
+.Sh SYNOPSIS
+.In sys/param.h
+.In sys/mount.h
+.In ufs/ufs/ufsmount.h
+.In ufs/ufs/dinode.h
+.In ufs/ffs/fs.h
+.In libufs.h
+.Sh DESCRIPTION
+The
+.Nm
+library and the functions it provides are used for implementing
+utilities which need to access a UFS file system at a low level from
+userland.
+Facilities provided are used to implement utilities such as
+.Xr newfs 8
+and
+.Xr dumpfs 8 .
+The
+.Nm
+library is designed to be simple, and to provide functions that are
+traditionally useful to have.
+.Pp
+A disk is represented as the type
+.Vt "struct uufsd"
+as defined in
+.In libufs.h .
+The structure is filled out, operations are performed, and the disk
+is closed.
+.Sh ERRORS
+Functions provided by
+.Nm
+return \-1 in every functional error situation.
+They also set the
+.Va d_error
+field of
+.Vt "struct uufsd"
+to a string describing the error.
+.Sh SEE ALSO
+.Xr berase 3 ,
+.Xr bread 3 ,
+.Xr bwrite 3 ,
+.Xr cgget 3 ,
+.Xr cgput 3 ,
+.Xr cgread 3 ,
+.Xr cgread1 3 ,
+.Xr cgwrite 3 ,
+.Xr cgwrite1 3 ,
+.Xr getinode 3 ,
+.Xr putinode 3 ,
+.Xr sbget 3 ,
+.Xr sbput 3 ,
+.Xr sbread 3 ,
+.Xr sbwrite 3 ,
+.Xr ufs_disk_close 3 ,
+.Xr ufs_disk_fillout 3 ,
+.Xr ufs_disk_fillout_blank 3 ,
+.Xr ufs_disk_write 3 ,
+.Xr ffs 4
+.Sh HISTORY
+The
+.Xr libufs 3
+library first appeared in
+.Fx 5.0 .
+.Sh AUTHORS
+.An Juli Mallett Aq Mt jmallett@FreeBSD.org
+.An Marshall Kirk McKusick Aq Mt mckusick@FreeBSD.org
+.Pp
+.An -nosplit
+Additional design, feedback, and ideas were provided by
+.An Poul-Henning Kamp Aq Mt phk@FreeBSD.org .
diff --git a/lib/libufs/libufs.h b/lib/libufs/libufs.h
new file mode 100644
index 000000000000..bb92e082a875
--- /dev/null
+++ b/lib/libufs/libufs.h
@@ -0,0 +1,187 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2002 Juli Mallett. All rights reserved.
+ *
+ * This software was written by Juli Mallett <jmallett@FreeBSD.org> for the
+ * FreeBSD project. Redistribution and use in source and binary forms, with
+ * or without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * 1. Redistribution of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistribution 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 ``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 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 __LIBUFS_H__
+#define __LIBUFS_H__
+#include <stdbool.h>
+
+/*
+ * Various disk controllers require their buffers to be aligned to the size
+ * of a cache line. The LIBUFS_BUFALIGN defines the required alignment size.
+ * The alignment must be a power of 2.
+ */
+#define LIBUFS_BUFALIGN 128
+
+/*
+ * userland ufs disk.
+ */
+struct uufsd {
+ union {
+ struct fs d_fs; /* filesystem information */
+ char d_sb[SBLOCKSIZE]; /* superblock as buffer */
+ } d_sbunion __aligned(LIBUFS_BUFALIGN);
+ union {
+ struct cg d_cg; /* cylinder group */
+ char d_buf[MAXBSIZE]; /* cylinder group storage */
+ } d_cgunion __aligned(LIBUFS_BUFALIGN);
+ union {
+ union dinodep d_ino[1]; /* inode block */
+ char d_inos[MAXBSIZE]; /* inode block as buffer */
+ } d_inosunion __aligned(LIBUFS_BUFALIGN);
+ const char *d_name; /* disk name */
+ const char *d_error; /* human readable disk error */
+ ufs2_daddr_t d_sblock; /* superblock location */
+ struct fs_summary_info *d_si; /* Superblock summary info */
+ union dinodep d_dp; /* pointer to currently active inode */
+ ino_t d_inomin; /* low ino */
+ ino_t d_inomax; /* high ino */
+ off_t d_sblockloc; /* where to look for the superblock */
+ int64_t d_bsize; /* device bsize */
+ int64_t d_lookupflags; /* flags to superblock lookup */
+ int64_t d_mine; /* internal flags */
+ int32_t d_ccg; /* current cylinder group */
+ int32_t d_ufs; /* decimal UFS version */
+ int32_t d_fd; /* raw device file descriptor */
+ int32_t d_lcg; /* last cylinder group (in d_cg) */
+};
+#define d_inos d_inosunion.d_inos
+#define d_fs d_sbunion.d_fs
+#define d_cg d_cgunion.d_cg
+
+/*
+ * libufs macros (internal, non-exported).
+ */
+#ifdef _LIBUFS
+/*
+ * Ensure that the buffer is aligned to the I/O subsystem requirements.
+ */
+#define BUF_MALLOC(newbufpp, data, size) { \
+ if (data != NULL && (((intptr_t)data) & (LIBUFS_BUFALIGN - 1)) == 0) \
+ *newbufpp = (void *)data; \
+ else \
+ *newbufpp = aligned_alloc(LIBUFS_BUFALIGN, size); \
+}
+/*
+ * Trace steps through libufs, to be used at entry and erroneous return.
+ */
+static inline void
+ERROR(struct uufsd *u, const char *str)
+{
+
+#ifdef _LIBUFS_DEBUGGING
+ if (str != NULL) {
+ fprintf(stderr, "libufs: %s", str);
+ if (errno != 0)
+ fprintf(stderr, ": %s", strerror(errno));
+ fprintf(stderr, "\n");
+ }
+#endif
+ if (u != NULL)
+ u->d_error = str;
+}
+#endif /* _LIBUFS */
+
+__BEGIN_DECLS
+
+/*
+ * libufs prototypes.
+ */
+
+/*
+ * ffs_subr.c
+ */
+void ffs_clrblock(struct fs *, u_char *, ufs1_daddr_t);
+void ffs_clusteracct(struct fs *, struct cg *, ufs1_daddr_t, int);
+void ffs_fragacct(struct fs *, int, int32_t [], int);
+int ffs_isblock(struct fs *, u_char *, ufs1_daddr_t);
+int ffs_isfreeblock(struct fs *, u_char *, ufs1_daddr_t);
+bool ffs_oldfscompat_inode_read(struct fs *, union dinodep, time_t);
+int ffs_sbsearch(void *, struct fs **, int, char *,
+ int (*)(void *, off_t, void **, int));
+void ffs_setblock(struct fs *, u_char *, ufs1_daddr_t);
+int ffs_sbget(void *, struct fs **, off_t, int, char *,
+ int (*)(void *, off_t, void **, int));
+int ffs_sbput(void *, struct fs *, off_t,
+ int (*)(void *, off_t, void *, int));
+void ffs_update_dinode_ckhash(struct fs *, struct ufs2_dinode *);
+int ffs_verify_dinode_ckhash(struct fs *, struct ufs2_dinode *);
+
+/*
+ * block.c
+ */
+ssize_t bread(struct uufsd *, ufs2_daddr_t, void *, size_t);
+ssize_t bwrite(struct uufsd *, ufs2_daddr_t, const void *, size_t);
+int berase(struct uufsd *, ufs2_daddr_t, ufs2_daddr_t);
+
+/*
+ * cgroup.c
+ */
+ufs2_daddr_t cgballoc(struct uufsd *);
+int cgbfree(struct uufsd *, ufs2_daddr_t, long);
+ino_t cgialloc(struct uufsd *);
+int cgget(int, struct fs *, int, struct cg *);
+int cgput(int, struct fs *, struct cg *);
+int cgread(struct uufsd *);
+int cgread1(struct uufsd *, int);
+int cgwrite(struct uufsd *);
+int cgwrite1(struct uufsd *, int);
+
+/*
+ * inode.c
+ */
+int getinode(struct uufsd *, union dinodep *, ino_t);
+int putinode(struct uufsd *);
+
+/*
+ * sblock.c
+ */
+int sbread(struct uufsd *);
+int sbfind(struct uufsd *, int);
+int sbwrite(struct uufsd *, int);
+/* low level superblock read/write functions */
+int sbget(int, struct fs **, off_t, int);
+int sbsearch(int, struct fs **, int);
+int sbput(int, struct fs *, int);
+
+/*
+ * type.c
+ */
+int ufs_disk_close(struct uufsd *);
+int ufs_disk_fillout(struct uufsd *, const char *);
+int ufs_disk_fillout_blank(struct uufsd *, const char *);
+int ufs_disk_write(struct uufsd *);
+
+/*
+ * crc32c.c
+ */
+uint32_t calculate_crc32c(uint32_t, const void *, size_t);
+
+__END_DECLS
+
+#endif /* __LIBUFS_H__ */
diff --git a/lib/libufs/sblock.c b/lib/libufs/sblock.c
new file mode 100644
index 000000000000..09d478bfe71e
--- /dev/null
+++ b/lib/libufs/sblock.c
@@ -0,0 +1,291 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2002 Juli Mallett. All rights reserved.
+ *
+ * This software was written by Juli Mallett <jmallett@FreeBSD.org> for the
+ * FreeBSD project. Redistribution and use in source and binary forms, with
+ * or without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * 1. Redistribution of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistribution 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 ``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 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/mount.h>
+#include <sys/disklabel.h>
+#include <sys/stat.h>
+
+#include <ufs/ufs/extattr.h>
+#include <ufs/ufs/quota.h>
+#include <ufs/ufs/ufsmount.h>
+#include <ufs/ufs/dinode.h>
+#include <ufs/ffs/fs.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <libufs.h>
+
+static int handle_disk_read(struct uufsd *, struct fs *, int);
+
+/*
+ * Read the standard superblock.
+ *
+ * The following option flags can be or'ed into disk->d_lookupflags:
+ *
+ * UFS_NOMSG indicates that superblock inconsistency error messages
+ * should not be printed.
+ *
+ * UFS_NOCSUM causes only the superblock itself to be returned, but does
+ * not read in any auxillary data structures like the cylinder group
+ * summary information.
+ */
+int
+sbread(struct uufsd *disk)
+{
+ struct fs *fs;
+ int error;
+
+ error = sbget(disk->d_fd, &fs, disk->d_sblockloc, disk->d_lookupflags);
+ return (handle_disk_read(disk, fs, error));
+}
+
+/*
+ * Make an extensive search to find a superblock. If the superblock
+ * in the standard place cannot be used, try looking for one of the
+ * backup superblocks.
+ *
+ * The flags parameter is made up of the following or'ed together options:
+ *
+ * UFS_NOMSG indicates that superblock inconsistency error messages
+ * should not be printed.
+ *
+ * UFS_NOCSUM causes only the superblock itself to be returned, but does
+ * not read in any auxillary data structures like the cylinder group
+ * summary information.
+ */
+int
+sbfind(struct uufsd *disk, int flags)
+{
+ struct fs *fs;
+ int error;
+
+ error = sbsearch(disk->d_fd, &fs, flags);
+ return (handle_disk_read(disk, fs, error));
+}
+
+static int
+handle_disk_read(struct uufsd *disk, struct fs *fs, int error)
+{
+
+ ERROR(disk, NULL);
+ if (error != 0) {
+ switch (error) {
+ case EIO:
+ ERROR(disk, "non-existent or truncated superblock");
+ break;
+ case ENOENT:
+ ERROR(disk, "no usable known superblock found");
+ break;
+ case EINTEGRITY:
+ ERROR(disk, "superblock check-hash failure");
+ break;
+ case ENOSPC:
+ ERROR(disk, "failed to allocate space for superblock "
+ "information");
+ break;
+ case EINVAL:
+ ERROR(disk, "The previous newfs operation on this "
+ "volume did not complete.\nYou must complete "
+ "newfs before using this volume.");
+ break;
+ default:
+ ERROR(disk, "unknown superblock read error");
+ errno = EIO;
+ break;
+ }
+ disk->d_ufs = 0;
+ return (-1);
+ }
+ memcpy(&disk->d_fs, fs, fs->fs_sbsize);
+ free(fs);
+ fs = &disk->d_fs;
+ if (fs->fs_magic == FS_UFS1_MAGIC)
+ disk->d_ufs = 1;
+ if (fs->fs_magic == FS_UFS2_MAGIC)
+ disk->d_ufs = 2;
+ disk->d_bsize = fs->fs_fsize / fsbtodb(fs, 1);
+ disk->d_sblock = fs->fs_sblockloc / disk->d_bsize;
+ disk->d_si = fs->fs_si;
+ return (0);
+}
+
+int
+sbwrite(struct uufsd *disk, int all)
+{
+ struct fs *fs;
+ int rv;
+
+ ERROR(disk, NULL);
+
+ rv = ufs_disk_write(disk);
+ if (rv == -1) {
+ ERROR(disk, "failed to open disk for writing");
+ return (-1);
+ }
+
+ fs = &disk->d_fs;
+ if ((errno = sbput(disk->d_fd, fs, all ? fs->fs_ncg : 0)) != 0) {
+ switch (errno) {
+ case EIO:
+ ERROR(disk, "failed to write superblock");
+ break;
+ default:
+ ERROR(disk, "unknown superblock write error");
+ errno = EIO;
+ break;
+ }
+ return (-1);
+ }
+ return (0);
+}
+
+/*
+ * These are the low-level functions that actually read and write
+ * the superblock and its associated data. The actual work is done by
+ * the functions ffs_sbget and ffs_sbput in /sys/ufs/ffs/ffs_subr.c.
+ */
+static int use_pread(void *devfd, off_t loc, void **bufp, int size);
+static int use_pwrite(void *devfd, off_t loc, void *buf, int size);
+
+/*
+ * The following two functions read a superblock. Their flags
+ * parameter are made up of the following or'ed together options:
+ *
+ * UFS_NOMSG indicates that superblock inconsistency error messages
+ * should not be printed.
+ *
+ * UFS_NOCSUM causes only the superblock itself to be returned, but does
+ * not read in any auxillary data structures like the cylinder group
+ * summary information.
+ *
+ * Read a superblock from the devfd device allocating memory returned
+ * in fsp.
+ */
+int
+sbget(int devfd, struct fs **fsp, off_t sblockloc, int flags)
+{
+ int error;
+
+ error = ffs_sbget(&devfd, fsp, sblockloc, flags, "user", use_pread);
+ fflush(NULL); /* flush any messages */
+ return (error);
+}
+
+/*
+ * Make an extensive search of the devfd device to find a superblock.
+ * If the superblock in the standard place cannot be used, try looking
+ * for one of the backup superblocks. If found, memory is allocated and
+ * returned in fsp.
+ */
+int
+sbsearch(int devfd, struct fs **fsp, int flags)
+{
+ int error;
+
+ error = ffs_sbsearch(&devfd, fsp, flags, "user", use_pread);
+ fflush(NULL); /* flush any messages */
+ return (error);
+}
+
+/*
+ * A read function for use by user-level programs using libufs.
+ */
+static int
+use_pread(void *devfd, off_t loc, void **bufp, int size)
+{
+ int fd;
+
+ fd = *(int *)devfd;
+ BUF_MALLOC(bufp, NULL, size);
+ if (*bufp == NULL)
+ return (ENOSPC);
+ if (pread(fd, *bufp, size, loc) != size)
+ return (EIO);
+ return (0);
+}
+
+/*
+ * Write a superblock to the devfd device from the memory pointed to by fs.
+ * Also write out the superblock summary information but do not free the
+ * summary information memory.
+ *
+ * Additionally write out numaltwrite of the alternate superblocks. Use
+ * fs->fs_ncg to write out all of the alternate superblocks.
+ */
+int
+sbput(int devfd, struct fs *fs, int numaltwrite)
+{
+ struct csum *savedcsp;
+ off_t savedactualloc;
+ int i, error;
+
+ error = ffs_sbput(&devfd, fs, fs->fs_sblockactualloc, use_pwrite);
+ fflush(NULL); /* flush any messages */
+ if (error != 0 || numaltwrite == 0)
+ return (error);
+ savedactualloc = fs->fs_sblockactualloc;
+ if (fs->fs_si != NULL) {
+ savedcsp = fs->fs_csp;
+ fs->fs_csp = NULL;
+ }
+ for (i = 0; i < numaltwrite; i++) {
+ fs->fs_sblockactualloc = dbtob(fsbtodb(fs, cgsblock(fs, i)));
+ if ((error = ffs_sbput(&devfd, fs, fs->fs_sblockactualloc,
+ use_pwrite)) != 0) {
+ fflush(NULL); /* flush any messages */
+ fs->fs_sblockactualloc = savedactualloc;
+ fs->fs_csp = savedcsp;
+ return (error);
+ }
+ }
+ fs->fs_sblockactualloc = savedactualloc;
+ if (fs->fs_si != NULL)
+ fs->fs_csp = savedcsp;
+ fflush(NULL); /* flush any messages */
+ return (0);
+}
+
+/*
+ * A write function for use by user-level programs using sbput in libufs.
+ */
+static int
+use_pwrite(void *devfd, off_t loc, void *buf, int size)
+{
+ int fd;
+
+ fd = *(int *)devfd;
+ if (pwrite(fd, buf, size, loc) != size)
+ return (EIO);
+ return (0);
+}
diff --git a/lib/libufs/sbread.3 b/lib/libufs/sbread.3
new file mode 100644
index 000000000000..47c32ce029ae
--- /dev/null
+++ b/lib/libufs/sbread.3
@@ -0,0 +1,203 @@
+.\" Author: Juli Mallett <jmallett@FreeBSD.org>
+.\" Date: June 04, 2003
+.\" Description:
+.\" Manual page for libufs functions:
+.\" sbget(3)
+.\" sbsearch(3)
+.\" sbput(3)
+.\" sbread(3)
+.\" sbfind(3)
+.\" sbwrite(3)
+.\"
+.\" This file is in the public domain.
+.\"
+.Dd August 8, 2022
+.Dt SBREAD 3
+.Os
+.Sh NAME
+.Nm sbget , sbsearch , sbput , sbread , sbfind , sbwrite
+.Nd read and write superblocks of a UFS file system
+.Sh LIBRARY
+.Lb libufs
+.Sh SYNOPSIS
+.In sys/param.h
+.In sys/mount.h
+.In ufs/ufs/ufsmount.h
+.In ufs/ufs/dinode.h
+.In ufs/ffs/fs.h
+.In libufs.h
+.Ft int
+.Fn sbget "int devfd" "struct fs **fsp" "off_t sblockloc" "int flags"
+.Ft int
+.Fn sbsearch "int devfd" "struct fs **fsp" "int flags"
+.Ft int
+.Fn sbput "int devfd" "struct fs *fs" "int numaltwrite"
+.Ft int
+.Fn sbread "struct uufsd *disk"
+.Ft int
+.Fn sbfind "struct uufsd *disk" "int flags"
+.Ft int
+.Fn sbwrite "struct uufsd *disk" "int all"
+.Sh DESCRIPTION
+The
+.Fn sbget ,
+.Fn sbsearch ,
+.Fn sbread ,
+and
+.Fn sbfind
+functions provide superblock reads for
+.Xr libufs 3
+consumers.
+The
+.Fn sbput
+and
+.Fn sbwrite
+functions provide superblock writes for
+.Xr libufs 3
+consumers.
+.Pp
+The
+.Fn sbget
+and
+.Fn sbsearch
+functions first allocate a buffer to hold the superblock.
+Using the
+.Va devfd
+file descriptor that references the filesystem disk,
+.Fn sbget
+reads the superblock located at the byte offset specified by
+.Va sblockloc
+into the allocated buffer.
+The value
+.Cm UFS_STDSB
+may be specified for
+.Va sblockloc
+to request that the standard location for the superblock be read.
+The
+.Fn sbsearch
+function uses the
+.Va devfd
+file descriptor that references the filesystem disk,
+to search first for the superblock at the standard location.
+If it is not found or is too damaged to use
+.Fn sbsearch
+will attempt to find one of the filesystem's alternate superblocks.
+Flags are specified by
+.Em or Ns 'ing
+the following values:
+.Pp
+.Bl -tag -width UFS_NOCSUM
+.It Cm UFS_NOCSUM
+Causes only the superblock itself to be returned, but does not read in any
+auxiliary data structures like the cylinder group summary information.
+.It Cm UFS_NOMSG
+Indicates that superblock inconsistency error messages should not be printed.
+.El
+.Pp
+If successful,
+.Fn sbget
+and
+.Fn sbsearch
+functions return a pointer to the buffer containing the superblock in
+.Va fsp .
+The
+.Fn sbget
+and
+.Fn sbsearch
+functions are safe to use in threaded applications.
+.Pp
+The
+.Fn sbput
+function writes the superblock specified by
+.Va fs
+to the location from which it was read on the disk referenced by the
+.Va devfd
+file descriptor.
+Additionally, the
+.Fn sbput
+function will update the first
+.Va numaltwrite
+alternate superblock locations.
+To update all the alternate superblocks,
+specify a
+.Va numaltwrite
+value of
+.Va fs->fs_ncg .
+The
+.Fn sbput
+function is safe to use in threaded applications.
+Note that the
+.Fn sbput
+function needs to be called only if the superblock has been
+modified and the on-disk copy needs to be updated.
+.Pp
+The
+.Fn sbread
+function reads the standard filesystem superblock.
+The
+.Fn sbfind
+function tries to find a usable superblock.
+It searchs first for the superblock at the standard location.
+If it is not found or is too damaged to use
+.Fn sbfind
+will attempt to find one of the filesystem's alternate superblocks.
+If successful
+.Fn sbread
+and
+.Fn sbfind
+return a superblock in the
+.Va d_sb ,
+structure embedded in the given user-land UFS disk structure.
+.Pp
+The
+.Fn sbwrite
+function writes the superblock from the
+.Va d_sb ,
+structure embedded in the given user-land UFS disk structure
+to the location from which it was read.
+Additionally, the
+.Fn sbwrite
+function will write to all the alternate superblock locations if the
+.Fa all
+value is non-zero.
+.Sh RETURN VALUES
+.Rv -std sbread sbwrite
+The
+.Fn sbget ,
+.Fn sbsearch ,
+and
+.Fn sbput
+functions return the value 0 if successful;
+otherwise they return one of the errors described below.
+.Sh ERRORS
+The errors returned by
+.Fn sbget ,
+.Fn sbsearch ,
+.Fn sbread ,
+and
+.Fn sbfind ,
+include any of the errors specified for the library function
+.Xr bread 3 .
+Additionally, they may follow the
+.Xr libufs 3
+error methodologies in situations where no usable superblock could be
+found.
+.Pp
+The errors returned by
+.Fn sbput
+and
+.Fn sbwrite
+include any of the errors specified for the library function
+.Xr bwrite 3 .
+.Sh SEE ALSO
+.Xr bread 3 ,
+.Xr bwrite 3 ,
+.Xr libufs 3
+.Sh HISTORY
+These functions first appeared as part of
+.Xr libufs 3
+in
+.Fx 5.0 .
+.Sh AUTHORS
+.An Juli Mallett Aq Mt jmallett@FreeBSD.org
+.An Marshall Kirk McKusick Aq Mt mckusick@FreeBSD.org
diff --git a/lib/libufs/type.c b/lib/libufs/type.c
new file mode 100644
index 000000000000..557e6cdead63
--- /dev/null
+++ b/lib/libufs/type.c
@@ -0,0 +1,207 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2002 Juli Mallett. All rights reserved.
+ *
+ * This software was written by Juli Mallett <jmallett@FreeBSD.org> for the
+ * FreeBSD project. Redistribution and use in source and binary forms, with
+ * or without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * 1. Redistribution of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistribution 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 ``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 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/mount.h>
+#include <sys/disklabel.h>
+#include <sys/stat.h>
+
+#include <ufs/ufs/extattr.h>
+#include <ufs/ufs/quota.h>
+#include <ufs/ufs/ufsmount.h>
+#include <ufs/ufs/dinode.h>
+#include <ufs/ffs/fs.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <fstab.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <libufs.h>
+
+/* Internally, track the 'name' value, it's ours. */
+#define MINE_NAME 0x01
+/* Track if its fd points to a writable device. */
+#define MINE_WRITE 0x02
+
+int
+ufs_disk_close(struct uufsd *disk)
+{
+ ERROR(disk, NULL);
+ close(disk->d_fd);
+ disk->d_fd = -1;
+ if (disk->d_mine & MINE_NAME) {
+ free((char *)(uintptr_t)disk->d_name);
+ disk->d_name = NULL;
+ }
+ if (disk->d_si != NULL) {
+ free(disk->d_si->si_csp);
+ free(disk->d_si);
+ disk->d_si = NULL;
+ }
+ return (0);
+}
+
+int
+ufs_disk_fillout(struct uufsd *disk, const char *name)
+{
+ if (ufs_disk_fillout_blank(disk, name) == -1) {
+ return (-1);
+ }
+ if (sbread(disk) == -1) {
+ ERROR(disk, "could not read superblock to fill out disk");
+ ufs_disk_close(disk);
+ return (-1);
+ }
+ return (0);
+}
+
+int
+ufs_disk_fillout_blank(struct uufsd *disk, const char *name)
+{
+ struct stat st;
+ struct fstab *fs;
+ struct statfs sfs;
+ const char *oname;
+ char dev[MAXPATHLEN];
+ int fd, ret;
+
+ ERROR(disk, NULL);
+
+ oname = name;
+again: if ((ret = stat(name, &st)) < 0) {
+ if (*name != '/') {
+ snprintf(dev, sizeof(dev), "%s%s", _PATH_DEV, name);
+ name = dev;
+ goto again;
+ }
+ /*
+ * The given object doesn't exist, but don't panic just yet -
+ * it may be still mount point listed in /etc/fstab, but without
+ * existing corresponding directory.
+ */
+ name = oname;
+ }
+ if (ret >= 0 && S_ISREG(st.st_mode)) {
+ /* Possibly a disk image, give it a try. */
+ ;
+ } else if (ret >= 0 && S_ISCHR(st.st_mode)) {
+ /* This is what we need, do nothing. */
+ ;
+ } else if ((fs = getfsfile(name)) != NULL) {
+ /*
+ * The given mount point is listed in /etc/fstab.
+ * It is possible that someone unmounted file system by hand
+ * and different file system is mounted on this mount point,
+ * but we still prefer /etc/fstab entry, because on the other
+ * hand, there could be /etc/fstab entry for this mount
+ * point, but file system is not mounted yet (eg. noauto) and
+ * statfs(2) will point us at different file system.
+ */
+ name = fs->fs_spec;
+ } else if (ret >= 0 && S_ISDIR(st.st_mode)) {
+ /*
+ * The mount point is not listed in /etc/fstab, so it may be
+ * file system mounted by hand.
+ */
+ if (statfs(name, &sfs) < 0) {
+ ERROR(disk, "could not find special device");
+ return (-1);
+ }
+ strlcpy(dev, sfs.f_mntfromname, sizeof(dev));
+ name = dev;
+ } else {
+ ERROR(disk, "could not find special device");
+ return (-1);
+ }
+ fd = open(name, O_RDONLY);
+ if (fd == -1) {
+ ERROR(disk, "could not open special device");
+ return (-1);
+ }
+
+ if (((uintptr_t)disk & ~(LIBUFS_BUFALIGN - 1)) != (uintptr_t)disk) {
+ ERROR(disk, "uufsd structure must be aligned to "
+ "LIBUFS_BUFALIGN byte boundry, see ufs_disk_fillout(3)");
+ close(fd);
+ return (-1);
+ }
+
+ disk->d_bsize = 1;
+ disk->d_ccg = 0;
+ disk->d_fd = fd;
+ disk->d_inomin = 0;
+ disk->d_inomax = 0;
+ disk->d_lcg = 0;
+ disk->d_mine = 0;
+ disk->d_ufs = 0;
+ disk->d_error = NULL;
+ disk->d_si = NULL;
+ disk->d_sblockloc = UFS_STDSB;
+ disk->d_lookupflags = 0;
+
+ if (oname != name) {
+ name = strdup(name);
+ if (name == NULL) {
+ ERROR(disk, "could not allocate memory for disk name");
+ return (-1);
+ }
+ disk->d_mine |= MINE_NAME;
+ }
+ disk->d_name = name;
+
+ return (0);
+}
+
+int
+ufs_disk_write(struct uufsd *disk)
+{
+ int fd;
+
+ ERROR(disk, NULL);
+
+ if (disk->d_mine & MINE_WRITE)
+ return (0);
+
+ fd = open(disk->d_name, O_RDWR);
+ if (fd < 0) {
+ ERROR(disk, "failed to open disk for writing");
+ return (-1);
+ }
+
+ close(disk->d_fd);
+ disk->d_fd = fd;
+ disk->d_mine |= MINE_WRITE;
+
+ return (0);
+}
diff --git a/lib/libufs/ufs_disk_close.3 b/lib/libufs/ufs_disk_close.3
new file mode 100644
index 000000000000..f332a9bb5de9
--- /dev/null
+++ b/lib/libufs/ufs_disk_close.3
@@ -0,0 +1,120 @@
+.\" Author: Juli Mallett <jmallett@FreeBSD.org>
+.\" Date: June 04, 2003
+.\" Description:
+.\" Manual page for libufs functions:
+.\" ufs_disk_close(3)
+.\" ufs_disk_fillout(3)
+.\" ufs_disk_fillout_blank(3)
+.\" ufs_disk_write(3)
+.\"
+.\" This file is in the public domain.
+.\"
+.Dd November 17, 2023
+.Dt UFS_DISK_CLOSE 3
+.Os
+.Sh NAME
+.Nm ufs_disk_close ,
+.Nm ufs_disk_fillout ,
+.Nm ufs_disk_fillout_blank ,
+.Nm ufs_disk_write
+.Nd open and close userland UFS disks
+.Sh LIBRARY
+.Lb libufs
+.Sh SYNOPSIS
+.In sys/param.h
+.In sys/mount.h
+.In ufs/ufs/ufsmount.h
+.In ufs/ufs/dinode.h
+.In ufs/ffs/fs.h
+.In libufs.h
+.Ft int
+.Fn ufs_disk_close "struct uufsd *disk"
+.Ft int
+.Fn ufs_disk_fillout "struct uufsd *disk" "const char *name"
+.Ft int
+.Fn ufs_disk_fillout_blank "struct uufsd *disk" "const char *name"
+.Ft int
+.Fn ufs_disk_write "struct uufsd *disk"
+.Sh DESCRIPTION
+The
+.Fn ufs_disk_close
+function closes a disk and frees internal memory related to it.
+It does not free the
+.Fa disk
+structure.
+.Pp
+The
+.Fn ufs_disk_fillout
+and
+.Fn ufs_disk_fillout_blank
+functions open a disk specified by
+.Fa name
+and populate the structure pointed to by
+.Fa disk .
+The structure referenced by the
+.Fa disk
+pointer must be aligned to at least the alignment specified by
+.Dv LIBUFS_ALIGN
+that is defined in the
+.Lb libufs.h
+header file.
+The disk is opened read-only.
+The specified
+.Fa name
+may be either a mountpoint, a device name or a filesystem image.
+The
+.Fn ufs_disk_fillout
+function assumes there is a valid superblock and will fail if not,
+whereas the
+.Fn ufs_disk_fillout_blank
+function makes no assumptions of that sort.
+.Pp
+The
+.Fn ufs_disk_write
+function attempts to re-open a disk as writable if it is not currently.
+.Sh ERRORS
+The function
+.Fn ufs_disk_close
+has no failure points.
+.Pp
+The function
+.Fn ufs_disk_fillout
+may fail for any of the reasons
+.Fn ufs_disk_fillout_blank
+might, as well as for any reason
+.Xr sbread 3
+might.
+.Pp
+The
+.Fn ufs_disk_fillout_blank
+may fail and set
+.Va errno
+for any of the errors specified for the library functions
+.Xr open 2 ,
+.Xr strdup 3 .
+Additionally, it may follow the
+.Xr libufs 3
+error methodologies in situations where no device could be found to
+open.
+.Pp
+The function
+.Fn ufs_disk_write
+may fail and set
+.Va errno
+for any of the errors specified for the library functions
+.Xr open 2
+and
+.Xr stat 2 .
+Namely, it will fail if the disk in question may not be written to.
+.Sh SEE ALSO
+.Xr open 2 ,
+.Xr getfsfile 3 ,
+.Xr libufs 3 ,
+.Xr sbread 3
+.Sh HISTORY
+These functions first appeared as part of
+.Xr libufs 3
+in
+.Fx 5.0 .
+.Sh AUTHORS
+.An Juli Mallett Aq Mt jmallett@FreeBSD.org