diff options
Diffstat (limited to 'lib/libufs')
-rw-r--r-- | lib/libufs/Makefile | 40 | ||||
-rw-r--r-- | lib/libufs/Makefile.depend | 15 | ||||
-rw-r--r-- | lib/libufs/block.c | 183 | ||||
-rw-r--r-- | lib/libufs/bread.3 | 97 | ||||
-rw-r--r-- | lib/libufs/cgread.3 | 181 | ||||
-rw-r--r-- | lib/libufs/cgroup.c | 317 | ||||
-rw-r--r-- | lib/libufs/getinode.3 | 129 | ||||
-rw-r--r-- | lib/libufs/inode.c | 119 | ||||
-rw-r--r-- | lib/libufs/libufs.3 | 85 | ||||
-rw-r--r-- | lib/libufs/libufs.h | 187 | ||||
-rw-r--r-- | lib/libufs/sblock.c | 291 | ||||
-rw-r--r-- | lib/libufs/sbread.3 | 203 | ||||
-rw-r--r-- | lib/libufs/type.c | 207 | ||||
-rw-r--r-- | lib/libufs/ufs_disk_close.3 | 120 |
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 *)█ + 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 |