summaryrefslogtreecommitdiffstats
path: root/stand/libsa
diff options
context:
space:
mode:
Diffstat (limited to 'stand/libsa')
-rw-r--r--stand/libsa/Makefile160
-rw-r--r--stand/libsa/Makefile.depend15
-rw-r--r--stand/libsa/__main.c43
-rw-r--r--stand/libsa/amd64/_setjmp.S93
-rw-r--r--stand/libsa/arp.c305
-rw-r--r--stand/libsa/assert.c44
-rw-r--r--stand/libsa/bcd.c38
-rw-r--r--stand/libsa/bootp.c791
-rw-r--r--stand/libsa/bootp.h151
-rw-r--r--stand/libsa/bootparam.c435
-rw-r--r--stand/libsa/bootparam.h5
-rw-r--r--stand/libsa/bzipfs.c388
-rw-r--r--stand/libsa/cd9660.c600
-rw-r--r--stand/libsa/close.c98
-rw-r--r--stand/libsa/closeall.c76
-rw-r--r--stand/libsa/crc32.c108
-rw-r--r--stand/libsa/crc32.h13
-rw-r--r--stand/libsa/dev.c61
-rw-r--r--stand/libsa/dosfs.c868
-rw-r--r--stand/libsa/dosfs.h123
-rw-r--r--stand/libsa/environment.c223
-rw-r--r--stand/libsa/ether.c147
-rw-r--r--stand/libsa/ext2fs.c908
-rw-r--r--stand/libsa/fstat.c61
-rw-r--r--stand/libsa/getopt.c108
-rw-r--r--stand/libsa/gets.c112
-rw-r--r--stand/libsa/globals.c38
-rw-r--r--stand/libsa/gpt.c379
-rw-r--r--stand/libsa/gpt.h41
-rw-r--r--stand/libsa/gzipfs.c337
-rw-r--r--stand/libsa/i386/_setjmp.S77
-rw-r--r--stand/libsa/in_cksum.c94
-rw-r--r--stand/libsa/inet_ntoa.c64
-rw-r--r--stand/libsa/ioctl.c88
-rw-r--r--stand/libsa/iodesc.h52
-rw-r--r--stand/libsa/ip.c423
-rw-r--r--stand/libsa/libstand.3676
-rw-r--r--stand/libsa/lseek.c141
-rw-r--r--stand/libsa/mips/_setjmp.S108
-rw-r--r--stand/libsa/nandfs.c1061
-rw-r--r--stand/libsa/net.c283
-rw-r--r--stand/libsa/net.h133
-rw-r--r--stand/libsa/netif.c316
-rw-r--r--stand/libsa/netif.h65
-rw-r--r--stand/libsa/nfs.c860
-rw-r--r--stand/libsa/nfsv2.h121
-rw-r--r--stand/libsa/nullfs.c105
-rw-r--r--stand/libsa/open.c159
-rw-r--r--stand/libsa/pager.c161
-rw-r--r--stand/libsa/panic.c59
-rw-r--r--stand/libsa/pkgfs.c791
-rw-r--r--stand/libsa/powerpc/_setjmp.S115
-rw-r--r--stand/libsa/powerpc/syncicache.c103
-rw-r--r--stand/libsa/printf.c518
-rw-r--r--stand/libsa/qdivrem.c348
-rw-r--r--stand/libsa/quad.h114
-rw-r--r--stand/libsa/random.c70
-rw-r--r--stand/libsa/rarp.c218
-rw-r--r--stand/libsa/read.c127
-rw-r--r--stand/libsa/readdir.c51
-rw-r--r--stand/libsa/rpc.c433
-rw-r--r--stand/libsa/rpc.h66
-rw-r--r--stand/libsa/rpcv2.h87
-rw-r--r--stand/libsa/saioctl.h50
-rw-r--r--stand/libsa/sbrk.c64
-rw-r--r--stand/libsa/sparc64/_setjmp.S94
-rw-r--r--stand/libsa/splitfs.c313
-rw-r--r--stand/libsa/stand.h426
-rw-r--r--stand/libsa/stat.c52
-rw-r--r--stand/libsa/strcasecmp.c73
-rw-r--r--stand/libsa/strdup.c55
-rw-r--r--stand/libsa/strerror.c87
-rw-r--r--stand/libsa/strtol.c132
-rw-r--r--stand/libsa/strtoul.c121
-rw-r--r--stand/libsa/tftp.c785
-rw-r--r--stand/libsa/tftp.h36
-rw-r--r--stand/libsa/twiddle.c69
-rw-r--r--stand/libsa/udp.c180
-rw-r--r--stand/libsa/ufs.c861
-rw-r--r--stand/libsa/ufsread.c326
-rw-r--r--stand/libsa/util.c182
-rw-r--r--stand/libsa/util.h53
-rw-r--r--stand/libsa/uuid_from_string.c132
-rw-r--r--stand/libsa/uuid_to_string.c111
-rw-r--r--stand/libsa/write.c95
-rw-r--r--stand/libsa/zalloc.c316
-rw-r--r--stand/libsa/zalloc_defs.h78
-rw-r--r--stand/libsa/zalloc_malloc.c200
-rw-r--r--stand/libsa/zalloc_mem.h53
-rw-r--r--stand/libsa/zalloc_protos.h35
90 files changed, 19835 insertions, 0 deletions
diff --git a/stand/libsa/Makefile b/stand/libsa/Makefile
new file mode 100644
index 0000000..ea32f3e
--- /dev/null
+++ b/stand/libsa/Makefile
@@ -0,0 +1,160 @@
+# $FreeBSD$
+# Originally from $NetBSD: Makefile,v 1.21 1997/10/26 22:08:38 lukem Exp $
+#
+# Notes:
+# - We don't use the libc strerror/sys_errlist because the string table is
+# quite large.
+#
+
+PACKAGE=lib${LIB}
+MK_PROFILE= no
+MK_SSP= no
+
+.include <bsd.init.mk>
+
+INTERNALLIB=
+LIBSA_CPUARCH?=${MACHINE_CPUARCH}
+LIBC_SRC= ${SRCTOP}/lib/libc
+
+LIB?= sa
+NO_PIC=
+WARNS?= 0
+
+CFLAGS+= -I${SASRC}
+
+# standalone components and stuff we have modified locally
+SRCS+= gzguts.h zutil.h __main.c assert.c bcd.c environment.c getopt.c gets.c \
+ globals.c pager.c panic.c printf.c strdup.c strerror.c strtol.c strtoul.c \
+ random.c sbrk.c twiddle.c zalloc.c zalloc_malloc.c
+
+# private (pruned) versions of libc string functions
+SRCS+= strcasecmp.c
+
+.PATH: ${LIBC_SRC}/net
+
+SRCS+= ntoh.c
+
+# string functions from libc
+.PATH: ${LIBC_SRC}/string
+SRCS+= bcmp.c bcopy.c bzero.c ffs.c fls.c \
+ memccpy.c memchr.c memcmp.c memcpy.c memmove.c memset.c \
+ qdivrem.c strcat.c strchr.c strcmp.c strcpy.c stpcpy.c stpncpy.c \
+ strcspn.c strlcat.c strlcpy.c strlen.c strncat.c strncmp.c strncpy.c \
+ strnlen.c strpbrk.c strrchr.c strsep.c strspn.c strstr.c strtok.c swab.c
+.if ${MACHINE_CPUARCH} == "arm"
+.PATH: ${LIBC_SRC}/arm/gen
+
+# Do not generate movt/movw, because the relocation fixup for them does not
+# translate to the -Bsymbolic -pie format required by self_reloc() in loader(8).
+# Also, the fpu is not available in a standalone environment.
+.if ${COMPILER_VERSION} < 30800
+CFLAGS.clang+= -mllvm -arm-use-movt=0
+.else
+CFLAGS.clang+= -mno-movt
+.endif
+CFLAGS.clang+= -mfpu=none
+
+# Compiler support functions
+.PATH: ${SRCTOP}/contrib/compiler-rt/lib/builtins/
+# __clzsi2 and ctzsi2 for various builtin functions
+SRCS+= clzsi2.c ctzsi2.c
+# Divide and modulus functions called by the compiler
+SRCS+= divmoddi4.c divmodsi4.c divdi3.c divsi3.c moddi3.c modsi3.c
+SRCS+= udivmoddi4.c udivmodsi4.c udivdi3.c udivsi3.c umoddi3.c umodsi3.c
+
+.PATH: ${SRCTOP}/contrib/compiler-rt/lib/builtins/arm/
+SRCS+= aeabi_idivmod.S aeabi_ldivmod.S aeabi_uidivmod.S aeabi_uldivmod.S
+SRCS+= aeabi_memcmp.S aeabi_memcpy.S aeabi_memmove.S aeabi_memset.S
+.endif
+
+.if ${MACHINE_CPUARCH} == "aarch64" || ${MACHINE_CPUARCH} == "riscv"
+.PATH: ${LIBC_SRC}/${MACHINE_CPUARCH}/gen
+.endif
+
+.if ${MACHINE_CPUARCH} == "powerpc"
+.PATH: ${LIBC_SRC}/quad
+SRCS+= ashldi3.c ashrdi3.c
+SRCS+= syncicache.c
+.endif
+
+# uuid functions from libc
+.PATH: ${LIBC_SRC}/uuid
+SRCS+= uuid_create_nil.c uuid_equal.c uuid_from_string.c uuid_is_nil.c uuid_to_string.c
+
+# _setjmp/_longjmp
+.PATH: ${SASRC}/${LIBSA_CPUARCH}
+SRCS+= _setjmp.S
+
+# decompression functionality from libbz2
+# NOTE: to actually test this functionality after libbz2 upgrade compile
+# loader(8) with LOADER_BZIP2_SUPPORT defined
+.PATH: ${SRCTOP}/contrib/bzip2
+CFLAGS+= -DBZ_NO_STDIO -DBZ_NO_COMPRESS
+SRCS+= libsa_bzlib_private.h
+
+.for file in bzlib.c crctable.c decompress.c huffman.c randtable.c
+SRCS+= _${file}
+CLEANFILES+= _${file}
+
+_${file}: ${file}
+ sed "s|bzlib_private\.h|libsa_bzlib_private.h|" \
+ ${.ALLSRC} > ${.TARGET}
+.endfor
+
+CLEANFILES+= libsa_bzlib_private.h
+libsa_bzlib_private.h: bzlib_private.h
+ sed -e 's|<stdlib.h>|"stand.h"|' \
+ ${.ALLSRC} > ${.TARGET}
+
+# decompression functionality from zlib
+.PATH: ${SRCTOP}/contrib/zlib
+CFLAGS+=-DHAVE_MEMCPY -I${SRCTOP}/contrib/zlib
+SRCS+= adler32.c crc32.c libsa_zutil.h libsa_gzguts.h
+
+.for file in infback.c inffast.c inflate.c inftrees.c zutil.c
+SRCS+= _${file}
+CLEANFILES+= _${file}
+
+_${file}: ${file}
+ sed -e "s|zutil\.h|libsa_zutil.h|" \
+ -e "s|gzguts\.h|libsa_gzguts.h|" \
+ ${.ALLSRC} > ${.TARGET}
+.endfor
+
+# depend on stand.h being able to be included multiple times
+.for file in zutil.h gzguts.h
+CLEANFILES+= libsa_${file}
+libsa_${file}: ${file}
+ sed -e 's|<fcntl.h>|"stand.h"|' \
+ -e 's|<stddef.h>|"stand.h"|' \
+ -e 's|<string.h>|"stand.h"|' \
+ -e 's|<stdio.h>|"stand.h"|' \
+ -e 's|<stdlib.h>|"stand.h"|' \
+ ${.ALLSRC} > ${.TARGET}
+.endfor
+
+# io routines
+SRCS+= closeall.c dev.c ioctl.c nullfs.c stat.c \
+ fstat.c close.c lseek.c open.c read.c write.c readdir.c
+
+# network routines
+SRCS+= arp.c ether.c ip.c inet_ntoa.c in_cksum.c net.c udp.c netif.c rpc.c
+
+# network info services:
+SRCS+= bootp.c rarp.c bootparam.c
+
+# boot filesystems
+SRCS+= ufs.c nfs.c cd9660.c tftp.c gzipfs.c bzipfs.c
+SRCS+= dosfs.c ext2fs.c
+SRCS+= splitfs.c
+SRCS+= pkgfs.c
+.if ${MK_NAND} != "no"
+SRCS+= nandfs.c
+.endif
+
+# explicit_bzero
+.PATH: ${SYSDIR}/libkern
+SRCS+= explicit_bzero.c
+
+.include <bsd.stand.mk>
+.include <bsd.lib.mk>
diff --git a/stand/libsa/Makefile.depend b/stand/libsa/Makefile.depend
new file mode 100644
index 0000000..1d86fce
--- /dev/null
+++ b/stand/libsa/Makefile.depend
@@ -0,0 +1,15 @@
+# $FreeBSD$
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ include \
+ include/arpa \
+ include/xlocale \
+ lib/libbz2 \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/stand/libsa/__main.c b/stand/libsa/__main.c
new file mode 100644
index 0000000..e38f338
--- /dev/null
+++ b/stand/libsa/__main.c
@@ -0,0 +1,43 @@
+/* $NetBSD: __main.c,v 1.4 1996/03/14 18:52:03 christos Exp $ */
+
+/*
+ * Copyright (c) 1993 Christopher G. Demetriou
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Christopher G. Demetriou.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission
+ *
+ * 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/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+
+void __main(void);
+
+void
+__main(void)
+{
+}
diff --git a/stand/libsa/amd64/_setjmp.S b/stand/libsa/amd64/_setjmp.S
new file mode 100644
index 0000000..e841f49
--- /dev/null
+++ b/stand/libsa/amd64/_setjmp.S
@@ -0,0 +1,93 @@
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * William Jolitz.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+ .asciz "@(#)_setjmp.s 5.1 (Berkeley) 4/23/90"
+#endif /* LIBC_SCCS and not lint */
+#include <machine/asm.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * C library -- _setjmp, _longjmp
+ *
+ * _longjmp(a,v)
+ * will generate a "return(v)" from the last call to
+ * _setjmp(a)
+ * by restoring registers from the environment 'a'.
+ * The previous signal state is NOT restored.
+ */
+
+ENTRY(_setjmp)
+ movq %rdi,%rax
+ movq 0(%rsp),%rdx /* retval */
+ movq %rdx, 0(%rax) /* 0; retval */
+ movq %rbx, 8(%rax) /* 1; rbx */
+ movq %rsp,16(%rax) /* 2; rsp */
+ movq %rbp,24(%rax) /* 3; rbp */
+ movq %r12,32(%rax) /* 4; r12 */
+ movq %r13,40(%rax) /* 5; r13 */
+ movq %r14,48(%rax) /* 6; r14 */
+ movq %r15,56(%rax) /* 7; r15 */
+ fnstcw 64(%rax) /* 8; fpu cw */
+ stmxcsr 68(%rax) /* and mxcsr */
+ xorq %rax,%rax
+ ret
+END(_setjmp)
+
+ .weak CNAME(_longjmp)
+ENTRY(_longjmp)
+ movq %rdi,%rdx
+ /* Restore the mxcsr, but leave exception flags intact. */
+ stmxcsr -4(%rsp)
+ movl 68(%rdx),%eax
+ andl $0xffffffc0,%eax
+ movl -4(%rsp),%edi
+ andl $0x3f,%edi
+ xorl %eax,%edi
+ movl %edi,-4(%rsp)
+ ldmxcsr -4(%rsp)
+ movq %rsi,%rax /* retval */
+ movq 0(%rdx),%rcx
+ movq 8(%rdx),%rbx
+ movq 16(%rdx),%rsp
+ movq 24(%rdx),%rbp
+ movq 32(%rdx),%r12
+ movq 40(%rdx),%r13
+ movq 48(%rdx),%r14
+ movq 56(%rdx),%r15
+ fldcw 64(%rdx)
+ testq %rax,%rax
+ jnz 1f
+ incq %rax
+1: movq %rcx,0(%rsp)
+ ret
+END(_longjmp)
diff --git a/stand/libsa/arp.c b/stand/libsa/arp.c
new file mode 100644
index 0000000..61b5f1f
--- /dev/null
+++ b/stand/libsa/arp.c
@@ -0,0 +1,305 @@
+/* $NetBSD: arp.c,v 1.18 1997/07/07 15:52:49 drochner Exp $ */
+
+/*
+ * Copyright (c) 1992 Regents of the University of California.
+ * All rights reserved.
+ *
+ * This software was developed by the Computer Systems Engineering group
+ * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
+ * contributed to Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#) Header: arp.c,v 1.5 93/07/15 05:52:26 leres Exp (LBL)
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+
+#include <netinet/in_systm.h>
+
+#include <string.h>
+
+#include "stand.h"
+#include "net.h"
+
+/* Cache stuff */
+#define ARP_NUM 8 /* need at most 3 arp entries */
+
+struct arp_list {
+ struct in_addr addr;
+ u_char ea[6];
+} arp_list[ARP_NUM] = {
+ /* XXX - net order `INADDR_BROADCAST' must be a constant */
+ { {0xffffffff}, BA }
+};
+int arp_num = 1;
+
+/* Local forwards */
+static ssize_t arpsend(struct iodesc *, void *, size_t);
+static ssize_t arprecv(struct iodesc *, void **, void **, time_t);
+
+/* Broadcast an ARP packet, asking who has addr on interface d */
+u_char *
+arpwhohas(struct iodesc *d, struct in_addr addr)
+{
+ int i;
+ struct ether_arp *ah;
+ struct arp_list *al;
+ void *pkt;
+ struct {
+ struct ether_header eh;
+ struct {
+ struct ether_arp arp;
+ u_char pad[18]; /* 60 - sizeof(...) */
+ } data;
+ } wbuf;
+
+ /* Try for cached answer first */
+ for (i = 0, al = arp_list; i < arp_num; ++i, ++al)
+ if (addr.s_addr == al->addr.s_addr)
+ return (al->ea);
+
+ /* Don't overflow cache */
+ if (arp_num > ARP_NUM - 1) {
+ arp_num = 1; /* recycle */
+ printf("arpwhohas: overflowed arp_list!\n");
+ }
+
+#ifdef ARP_DEBUG
+ if (debug)
+ printf("arpwhohas: send request for %s\n", inet_ntoa(addr));
+#endif
+
+ bzero((char*)&wbuf.data, sizeof(wbuf.data));
+ ah = &wbuf.data.arp;
+ ah->arp_hrd = htons(ARPHRD_ETHER);
+ ah->arp_pro = htons(ETHERTYPE_IP);
+ ah->arp_hln = sizeof(ah->arp_sha); /* hardware address length */
+ ah->arp_pln = sizeof(ah->arp_spa); /* protocol address length */
+ ah->arp_op = htons(ARPOP_REQUEST);
+ MACPY(d->myea, ah->arp_sha);
+ bcopy(&d->myip, ah->arp_spa, sizeof(ah->arp_spa));
+ /* Leave zeros in arp_tha */
+ bcopy(&addr, ah->arp_tpa, sizeof(ah->arp_tpa));
+
+ /* Store ip address in cache (incomplete entry). */
+ al->addr = addr;
+
+ pkt = NULL;
+ ah = NULL;
+ i = sendrecv(d,
+ arpsend, &wbuf.data, sizeof(wbuf.data),
+ arprecv, &pkt, (void **)&ah);
+ if (i == -1) {
+ panic("arp: no response for %s\n",
+ inet_ntoa(addr));
+ }
+
+ /* Store ethernet address in cache */
+#ifdef ARP_DEBUG
+ if (debug) {
+ struct ether_header *eh;
+
+ eh = (struct ether_header *)((uintptr_t)pkt + ETHER_ALIGN);
+ printf("arp: response from %s\n",
+ ether_sprintf(eh->ether_shost));
+ printf("arp: cacheing %s --> %s\n",
+ inet_ntoa(addr), ether_sprintf(ah->arp_sha));
+ }
+#endif
+ MACPY(ah->arp_sha, al->ea);
+ ++arp_num;
+
+ free(pkt);
+ return (al->ea);
+}
+
+static ssize_t
+arpsend(struct iodesc *d, void *pkt, size_t len)
+{
+
+#ifdef ARP_DEBUG
+ if (debug)
+ printf("arpsend: called\n");
+#endif
+
+ return (sendether(d, pkt, len, bcea, ETHERTYPE_ARP));
+}
+
+/*
+ * Returns 0 if this is the packet we're waiting for
+ * else -1 (and errno == 0)
+ */
+static ssize_t
+arprecv(struct iodesc *d, void **pkt, void **payload, time_t tleft)
+{
+ ssize_t n;
+ struct ether_arp *ah;
+ u_int16_t etype; /* host order */
+ void *ptr;
+
+#ifdef ARP_DEBUG
+ if (debug)
+ printf("arprecv: ");
+#endif
+
+ ptr = NULL;
+ n = readether(d, &ptr, (void **)&ah, tleft, &etype);
+ errno = 0; /* XXX */
+ if (n == -1 || n < sizeof(struct ether_arp)) {
+#ifdef ARP_DEBUG
+ if (debug)
+ printf("bad len=%d\n", n);
+#endif
+ free(ptr);
+ return (-1);
+ }
+
+ if (etype != ETHERTYPE_ARP) {
+#ifdef ARP_DEBUG
+ if (debug)
+ printf("not arp type=%d\n", etype);
+#endif
+ free(ptr);
+ return (-1);
+ }
+
+ /* Ethernet address now checked in readether() */
+ if (ah->arp_hrd != htons(ARPHRD_ETHER) ||
+ ah->arp_pro != htons(ETHERTYPE_IP) ||
+ ah->arp_hln != sizeof(ah->arp_sha) ||
+ ah->arp_pln != sizeof(ah->arp_spa) )
+ {
+#ifdef ARP_DEBUG
+ if (debug)
+ printf("bad hrd/pro/hln/pln\n");
+#endif
+ free(ptr);
+ return (-1);
+ }
+
+ if (ah->arp_op == htons(ARPOP_REQUEST)) {
+#ifdef ARP_DEBUG
+ if (debug)
+ printf("is request\n");
+#endif
+ arp_reply(d, ah);
+ free(ptr);
+ return (-1);
+ }
+
+ if (ah->arp_op != htons(ARPOP_REPLY)) {
+#ifdef ARP_DEBUG
+ if (debug)
+ printf("not ARP reply\n");
+#endif
+ free(ptr);
+ return (-1);
+ }
+
+ /* Is the reply from the source we want? */
+ if (bcmp(&arp_list[arp_num].addr,
+ ah->arp_spa, sizeof(ah->arp_spa)))
+ {
+#ifdef ARP_DEBUG
+ if (debug)
+ printf("unwanted address\n");
+#endif
+ free(ptr);
+ return (-1);
+ }
+ /* We don't care who the reply was sent to. */
+
+ /* We have our answer. */
+#ifdef ARP_DEBUG
+ if (debug)
+ printf("got it\n");
+#endif
+ *pkt = ptr;
+ *payload = ah;
+ return (n);
+}
+
+/*
+ * Convert an ARP request into a reply and send it.
+ * Notes: Re-uses buffer. Pad to length = 46.
+ */
+void
+arp_reply(struct iodesc *d, void *pkt)
+{
+ struct ether_arp *arp = pkt;
+
+ if (arp->arp_hrd != htons(ARPHRD_ETHER) ||
+ arp->arp_pro != htons(ETHERTYPE_IP) ||
+ arp->arp_hln != sizeof(arp->arp_sha) ||
+ arp->arp_pln != sizeof(arp->arp_spa) )
+ {
+#ifdef ARP_DEBUG
+ if (debug)
+ printf("arp_reply: bad hrd/pro/hln/pln\n");
+#endif
+ return;
+ }
+
+ if (arp->arp_op != htons(ARPOP_REQUEST)) {
+#ifdef ARP_DEBUG
+ if (debug)
+ printf("arp_reply: not request!\n");
+#endif
+ return;
+ }
+
+ /* If we are not the target, ignore the request. */
+ if (bcmp(arp->arp_tpa, &d->myip, sizeof(arp->arp_tpa)))
+ return;
+
+#ifdef ARP_DEBUG
+ if (debug) {
+ printf("arp_reply: to %s\n", ether_sprintf(arp->arp_sha));
+ }
+#endif
+
+ arp->arp_op = htons(ARPOP_REPLY);
+ /* source becomes target */
+ bcopy(arp->arp_sha, arp->arp_tha, sizeof(arp->arp_tha));
+ bcopy(arp->arp_spa, arp->arp_tpa, sizeof(arp->arp_tpa));
+ /* here becomes source */
+ bcopy(d->myea, arp->arp_sha, sizeof(arp->arp_sha));
+ bcopy(&d->myip, arp->arp_spa, sizeof(arp->arp_spa));
+
+ /*
+ * No need to get fancy here. If the send fails, the
+ * requestor will just ask again.
+ */
+ (void) sendether(d, pkt, sizeof(*arp) + 18,
+ arp->arp_tha, ETHERTYPE_ARP);
+}
diff --git a/stand/libsa/assert.c b/stand/libsa/assert.c
new file mode 100644
index 0000000..8eec63a
--- /dev/null
+++ b/stand/libsa/assert.c
@@ -0,0 +1,44 @@
+/*-
+ * Copyright (c) 1998 Michael Smith.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <assert.h>
+
+#include "stand.h"
+
+void
+__assert(const char *func, const char *file, int line, const char *expression)
+{
+ if (func == NULL)
+ panic("Assertion failed: (%s), file %s, line %d.\n",
+ expression, file, line);
+ else
+ panic(
+ "Assertion failed: (%s), function %s, file %s, line %d.\n",
+ expression, func, file, line);
+}
diff --git a/stand/libsa/bcd.c b/stand/libsa/bcd.c
new file mode 100644
index 0000000..7bd67c9
--- /dev/null
+++ b/stand/libsa/bcd.c
@@ -0,0 +1,38 @@
+/*
+ * Some data-tables that are often used.
+ * Cannot be copyrighted.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+
+u_char const bcd2bin_data[] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0,
+ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 0, 0, 0, 0, 0, 0,
+ 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 0, 0, 0, 0, 0, 0,
+ 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 0, 0, 0, 0, 0, 0,
+ 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 0, 0, 0, 0, 0, 0,
+ 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 0, 0, 0, 0, 0, 0,
+ 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 0, 0, 0, 0, 0, 0,
+ 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 0, 0, 0, 0, 0, 0,
+ 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 0, 0, 0, 0, 0, 0,
+ 90, 91, 92, 93, 94, 95, 96, 97, 98, 99
+};
+
+u_char const bin2bcd_data[] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19,
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29,
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49,
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79,
+ 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,
+ 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99
+};
+
+/* This is actually used with radix [2..36] */
+char const hex2ascii_data[] = "0123456789abcdefghijklmnopqrstuvwxyz";
diff --git a/stand/libsa/bootp.c b/stand/libsa/bootp.c
new file mode 100644
index 0000000..9f08faf
--- /dev/null
+++ b/stand/libsa/bootp.c
@@ -0,0 +1,791 @@
+/* $NetBSD: bootp.c,v 1.14 1998/02/16 11:10:54 drochner Exp $ */
+
+/*
+ * Copyright (c) 1992 Regents of the University of California.
+ * All rights reserved.
+ *
+ * This software was developed by the Computer Systems Engineering group
+ * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
+ * contributed to Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#) Header: bootp.c,v 1.4 93/09/11 03:13:51 leres Exp (LBL)
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stddef.h>
+#include <sys/types.h>
+#include <sys/limits.h>
+#include <sys/endian.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+
+#include <string.h>
+
+#define BOOTP_DEBUGxx
+#define SUPPORT_DHCP
+
+#define DHCP_ENV_NOVENDOR 1 /* do not parse vendor options */
+#define DHCP_ENV_PXE 10 /* assume pxe vendor options */
+#define DHCP_ENV_FREEBSD 11 /* assume freebsd vendor options */
+/* set DHCP_ENV to one of the values above to export dhcp options to kenv */
+#define DHCP_ENV DHCP_ENV_NO_VENDOR
+
+#include "stand.h"
+#include "net.h"
+#include "netif.h"
+#include "bootp.h"
+
+
+struct in_addr servip;
+
+static time_t bot;
+
+static char vm_rfc1048[4] = VM_RFC1048;
+#ifdef BOOTP_VEND_CMU
+static char vm_cmu[4] = VM_CMU;
+#endif
+
+/* Local forwards */
+static ssize_t bootpsend(struct iodesc *, void *, size_t);
+static ssize_t bootprecv(struct iodesc *, void **, void **, time_t);
+static int vend_rfc1048(u_char *, u_int);
+#ifdef BOOTP_VEND_CMU
+static void vend_cmu(u_char *);
+#endif
+
+#ifdef DHCP_ENV /* export the dhcp response to kenv */
+struct dhcp_opt;
+static void setenv_(u_char *cp, u_char *ep, struct dhcp_opt *opts);
+#else
+#define setenv_(a, b, c)
+#endif
+
+#ifdef SUPPORT_DHCP
+static char expected_dhcpmsgtype = -1, dhcp_ok;
+struct in_addr dhcp_serverip;
+#endif
+struct bootp *bootp_response;
+size_t bootp_response_size;
+
+static void
+bootp_fill_request(unsigned char *bp_vend)
+{
+ /*
+ * We are booting from PXE, we want to send the string
+ * 'PXEClient' to the DHCP server so you have the option of
+ * only responding to PXE aware dhcp requests.
+ */
+ bp_vend[0] = TAG_CLASSID;
+ bp_vend[1] = 9;
+ bcopy("PXEClient", &bp_vend[2], 9);
+ bp_vend[11] = TAG_USER_CLASS;
+ /* len of each user class + number of user class */
+ bp_vend[12] = 8;
+ /* len of the first user class */
+ bp_vend[13] = 7;
+ bcopy("FreeBSD", &bp_vend[14], 7);
+ bp_vend[21] = TAG_PARAM_REQ;
+ bp_vend[22] = 7;
+ bp_vend[23] = TAG_ROOTPATH;
+ bp_vend[24] = TAG_HOSTNAME;
+ bp_vend[25] = TAG_SWAPSERVER;
+ bp_vend[26] = TAG_GATEWAY;
+ bp_vend[27] = TAG_SUBNET_MASK;
+ bp_vend[28] = TAG_INTF_MTU;
+ bp_vend[29] = TAG_SERVERID;
+ bp_vend[30] = TAG_END;
+}
+
+/* Fetch required bootp infomation */
+void
+bootp(int sock)
+{
+ void *pkt;
+ struct iodesc *d;
+ struct bootp *bp;
+ struct {
+ u_char header[HEADER_SIZE];
+ struct bootp wbootp;
+ } wbuf;
+ struct bootp *rbootp;
+
+#ifdef BOOTP_DEBUG
+ if (debug)
+ printf("bootp: socket=%d\n", sock);
+#endif
+ if (!bot)
+ bot = getsecs();
+
+ if (!(d = socktodesc(sock))) {
+ printf("bootp: bad socket. %d\n", sock);
+ return;
+ }
+#ifdef BOOTP_DEBUG
+ if (debug)
+ printf("bootp: d=%lx\n", (long)d);
+#endif
+
+ bp = &wbuf.wbootp;
+ bzero(bp, sizeof(*bp));
+
+ bp->bp_op = BOOTREQUEST;
+ bp->bp_htype = 1; /* 10Mb Ethernet (48 bits) */
+ bp->bp_hlen = 6;
+ bp->bp_xid = htonl(d->xid);
+ MACPY(d->myea, bp->bp_chaddr);
+ strncpy(bp->bp_file, bootfile, sizeof(bp->bp_file));
+ bcopy(vm_rfc1048, bp->bp_vend, sizeof(vm_rfc1048));
+#ifdef SUPPORT_DHCP
+ bp->bp_vend[4] = TAG_DHCP_MSGTYPE;
+ bp->bp_vend[5] = 1;
+ bp->bp_vend[6] = DHCPDISCOVER;
+ bootp_fill_request(&bp->bp_vend[7]);
+
+#else
+ bp->bp_vend[4] = TAG_END;
+#endif
+
+ d->myip.s_addr = INADDR_ANY;
+ d->myport = htons(IPPORT_BOOTPC);
+ d->destip.s_addr = INADDR_BROADCAST;
+ d->destport = htons(IPPORT_BOOTPS);
+
+#ifdef SUPPORT_DHCP
+ expected_dhcpmsgtype = DHCPOFFER;
+ dhcp_ok = 0;
+#endif
+
+ if(sendrecv(d,
+ bootpsend, bp, sizeof(*bp),
+ bootprecv, &pkt, (void **)&rbootp) == -1) {
+ printf("bootp: no reply\n");
+ return;
+ }
+
+#ifdef SUPPORT_DHCP
+ if(dhcp_ok) {
+ u_int32_t leasetime;
+ bp->bp_vend[6] = DHCPREQUEST;
+ bp->bp_vend[7] = TAG_REQ_ADDR;
+ bp->bp_vend[8] = 4;
+ bcopy(&rbootp->bp_yiaddr, &bp->bp_vend[9], 4);
+ bp->bp_vend[13] = TAG_SERVERID;
+ bp->bp_vend[14] = 4;
+ bcopy(&dhcp_serverip.s_addr, &bp->bp_vend[15], 4);
+ bp->bp_vend[19] = TAG_LEASETIME;
+ bp->bp_vend[20] = 4;
+ leasetime = htonl(300);
+ bcopy(&leasetime, &bp->bp_vend[21], 4);
+ bootp_fill_request(&bp->bp_vend[25]);
+
+ expected_dhcpmsgtype = DHCPACK;
+
+ free(pkt);
+ if(sendrecv(d,
+ bootpsend, bp, sizeof(*bp),
+ bootprecv, &pkt, (void **)&rbootp) == -1) {
+ printf("DHCPREQUEST failed\n");
+ return;
+ }
+ }
+#endif
+
+ myip = d->myip = rbootp->bp_yiaddr;
+ servip = rbootp->bp_siaddr;
+ if (rootip.s_addr == INADDR_ANY)
+ rootip = servip;
+ bcopy(rbootp->bp_file, bootfile, sizeof(bootfile));
+ bootfile[sizeof(bootfile) - 1] = '\0';
+
+ if (!netmask) {
+ if (IN_CLASSA(ntohl(myip.s_addr)))
+ netmask = htonl(IN_CLASSA_NET);
+ else if (IN_CLASSB(ntohl(myip.s_addr)))
+ netmask = htonl(IN_CLASSB_NET);
+ else
+ netmask = htonl(IN_CLASSC_NET);
+#ifdef BOOTP_DEBUG
+ if (debug)
+ printf("'native netmask' is %s\n", intoa(netmask));
+#endif
+ }
+
+#ifdef BOOTP_DEBUG
+ if (debug)
+ printf("mask: %s\n", intoa(netmask));
+#endif
+
+ /* We need a gateway if root is on a different net */
+ if (!SAMENET(myip, rootip, netmask)) {
+#ifdef BOOTP_DEBUG
+ if (debug)
+ printf("need gateway for root ip\n");
+#endif
+ }
+
+ /* Toss gateway if on a different net */
+ if (!SAMENET(myip, gateip, netmask)) {
+#ifdef BOOTP_DEBUG
+ if (debug)
+ printf("gateway ip (%s) bad\n", inet_ntoa(gateip));
+#endif
+ gateip.s_addr = 0;
+ }
+
+ /* Bump xid so next request will be unique. */
+ ++d->xid;
+ free(pkt);
+}
+
+/* Transmit a bootp request */
+static ssize_t
+bootpsend(struct iodesc *d, void *pkt, size_t len)
+{
+ struct bootp *bp;
+
+#ifdef BOOTP_DEBUG
+ if (debug)
+ printf("bootpsend: d=%lx called.\n", (long)d);
+#endif
+
+ bp = pkt;
+ bp->bp_secs = htons((u_short)(getsecs() - bot));
+
+#ifdef BOOTP_DEBUG
+ if (debug)
+ printf("bootpsend: calling sendudp\n");
+#endif
+
+ return (sendudp(d, pkt, len));
+}
+
+static ssize_t
+bootprecv(struct iodesc *d, void **pkt, void **payload, time_t tleft)
+{
+ ssize_t n;
+ struct bootp *bp;
+ void *ptr;
+
+#ifdef BOOTP_DEBUG
+ if (debug)
+ printf("bootp_recvoffer: called\n");
+#endif
+
+ ptr = NULL;
+ n = readudp(d, &ptr, (void **)&bp, tleft);
+ if (n == -1 || n < sizeof(struct bootp) - BOOTP_VENDSIZE)
+ goto bad;
+
+#ifdef BOOTP_DEBUG
+ if (debug)
+ printf("bootprecv: checked. bp = %p, n = %zd\n", bp, n);
+#endif
+ if (bp->bp_xid != htonl(d->xid)) {
+#ifdef BOOTP_DEBUG
+ if (debug) {
+ printf("bootprecv: expected xid 0x%lx, got 0x%x\n",
+ d->xid, ntohl(bp->bp_xid));
+ }
+#endif
+ goto bad;
+ }
+
+#ifdef BOOTP_DEBUG
+ if (debug)
+ printf("bootprecv: got one!\n");
+#endif
+
+ /* Suck out vendor info */
+ if (bcmp(vm_rfc1048, bp->bp_vend, sizeof(vm_rfc1048)) == 0) {
+ int vsize = n - offsetof(struct bootp, bp_vend);
+ if (vend_rfc1048(bp->bp_vend, vsize) != 0)
+ goto bad;
+
+ /* Save copy of bootp reply or DHCP ACK message */
+ if (bp->bp_op == BOOTREPLY &&
+ ((dhcp_ok == 1 && expected_dhcpmsgtype == DHCPACK) ||
+ dhcp_ok == 0)) {
+ free(bootp_response);
+ bootp_response = malloc(n);
+ if (bootp_response != NULL) {
+ bootp_response_size = n;
+ bcopy(bp, bootp_response, bootp_response_size);
+ }
+ }
+ }
+#ifdef BOOTP_VEND_CMU
+ else if (bcmp(vm_cmu, bp->bp_vend, sizeof(vm_cmu)) == 0)
+ vend_cmu(bp->bp_vend);
+#endif
+ else
+ printf("bootprecv: unknown vendor 0x%lx\n", (long)bp->bp_vend);
+
+ *pkt = ptr;
+ *payload = bp;
+ return (n);
+bad:
+ free(ptr);
+ errno = 0;
+ return (-1);
+}
+
+int
+dhcp_try_rfc1048(u_char *cp, u_int len)
+{
+
+ expected_dhcpmsgtype = DHCPACK;
+ if (bcmp(vm_rfc1048, cp, sizeof(vm_rfc1048)) == 0) {
+ return (vend_rfc1048(cp, len));
+ }
+ return (-1);
+}
+
+static int
+vend_rfc1048(u_char *cp, u_int len)
+{
+ u_char *ep;
+ int size;
+ u_char tag;
+ const char *val;
+
+#ifdef BOOTP_DEBUG
+ if (debug)
+ printf("vend_rfc1048 bootp info. len=%d\n", len);
+#endif
+ ep = cp + len;
+
+ /* Step over magic cookie */
+ cp += sizeof(int);
+
+ setenv_(cp, ep, NULL);
+
+ while (cp < ep) {
+ tag = *cp++;
+ size = *cp++;
+ if (tag == TAG_END)
+ break;
+
+ if (tag == TAG_SUBNET_MASK) {
+ bcopy(cp, &netmask, sizeof(netmask));
+ }
+ if (tag == TAG_GATEWAY) {
+ bcopy(cp, &gateip.s_addr, sizeof(gateip.s_addr));
+ }
+ if (tag == TAG_SWAPSERVER) {
+ /* let it override bp_siaddr */
+ bcopy(cp, &rootip.s_addr, sizeof(rootip.s_addr));
+ }
+ if (tag == TAG_ROOTPATH) {
+ if ((val = getenv("dhcp.root-path")) == NULL)
+ val = (const char *)cp;
+ strlcpy(rootpath, val, sizeof(rootpath));
+ }
+ if (tag == TAG_HOSTNAME) {
+ if ((val = getenv("dhcp.host-name")) == NULL)
+ val = (const char *)cp;
+ strlcpy(hostname, val, sizeof(hostname));
+ }
+ if (tag == TAG_INTF_MTU) {
+ intf_mtu = 0;
+ if ((val = getenv("dhcp.interface-mtu")) != NULL) {
+ unsigned long tmp;
+ char *end;
+
+ errno = 0;
+ /*
+ * Do not allow MTU to exceed max IPv4 packet
+ * size, max value of 16-bit word.
+ */
+ tmp = strtoul(val, &end, 0);
+ if (errno != 0 ||
+ *val == '\0' || *end != '\0' ||
+ tmp > USHRT_MAX) {
+ printf("%s: bad value: \"%s\", "
+ "ignoring\n",
+ "dhcp.interface-mtu", val);
+ } else {
+ intf_mtu = (u_int)tmp;
+ }
+ }
+ if (intf_mtu <= 0)
+ intf_mtu = be16dec(cp);
+ }
+#ifdef SUPPORT_DHCP
+ if (tag == TAG_DHCP_MSGTYPE) {
+ if(*cp != expected_dhcpmsgtype)
+ return(-1);
+ dhcp_ok = 1;
+ }
+ if (tag == TAG_SERVERID) {
+ bcopy(cp, &dhcp_serverip.s_addr,
+ sizeof(dhcp_serverip.s_addr));
+ }
+#endif
+ cp += size;
+ }
+ return(0);
+}
+
+#ifdef BOOTP_VEND_CMU
+static void
+vend_cmu(u_char *cp)
+{
+ struct cmu_vend *vp;
+
+#ifdef BOOTP_DEBUG
+ if (debug)
+ printf("vend_cmu bootp info.\n");
+#endif
+ vp = (struct cmu_vend *)cp;
+
+ if (vp->v_smask.s_addr != 0) {
+ netmask = vp->v_smask.s_addr;
+ }
+ if (vp->v_dgate.s_addr != 0) {
+ gateip = vp->v_dgate;
+ }
+}
+#endif
+
+#ifdef DHCP_ENV
+/*
+ * Parse DHCP options and store them into kenv variables.
+ * Original code from Danny Braniss, modifications by Luigi Rizzo.
+ *
+ * The parser is driven by tables which specify the type and name of
+ * each dhcp option and how it appears in kenv.
+ * The first entry in the list contains the prefix used to set the kenv
+ * name (including the . if needed), the last entry must have a 0 tag.
+ * Entries do not need to be sorted though it helps for readability.
+ *
+ * Certain vendor-specific tables can be enabled according to DHCP_ENV.
+ * Set it to 0 if you don't want any.
+ */
+enum opt_fmt { __NONE = 0,
+ __8 = 1, __16 = 2, __32 = 4, /* Unsigned fields, value=size */
+ __IP, /* IPv4 address */
+ __TXT, /* C string */
+ __BYTES, /* byte sequence, printed %02x */
+ __INDIR, /* name=value */
+ __ILIST, /* name=value;name=value ... */
+ __VE, /* vendor specific, recurse */
+};
+
+struct dhcp_opt {
+ uint8_t tag;
+ uint8_t fmt;
+ const char *desc;
+};
+
+static struct dhcp_opt vndr_opt[] = { /* Vendor Specific Options */
+#if DHCP_ENV == DHCP_ENV_FREEBSD /* FreeBSD table in the original code */
+ {0, 0, "FreeBSD"}, /* prefix */
+ {1, __TXT, "kernel"},
+ {2, __TXT, "kernelname"},
+ {3, __TXT, "kernel_options"},
+ {4, __IP, "usr-ip"},
+ {5, __TXT, "conf-path"},
+ {6, __TXT, "rc.conf0"},
+ {7, __TXT, "rc.conf1"},
+ {8, __TXT, "rc.conf2"},
+ {9, __TXT, "rc.conf3"},
+ {10, __TXT, "rc.conf4"},
+ {11, __TXT, "rc.conf5"},
+ {12, __TXT, "rc.conf6"},
+ {13, __TXT, "rc.conf7"},
+ {14, __TXT, "rc.conf8"},
+ {15, __TXT, "rc.conf9"},
+
+ {20, __TXT, "boot.nfsroot.options"},
+
+ {245, __INDIR, ""},
+ {246, __INDIR, ""},
+ {247, __INDIR, ""},
+ {248, __INDIR, ""},
+ {249, __INDIR, ""},
+ {250, __INDIR, ""},
+ {251, __INDIR, ""},
+ {252, __INDIR, ""},
+ {253, __INDIR, ""},
+ {254, __INDIR, ""},
+
+#elif DHCP_ENV == DHCP_ENV_PXE /* some pxe options, RFC4578 */
+ {0, 0, "pxe"}, /* prefix */
+ {93, __16, "system-architecture"},
+ {94, __BYTES, "network-interface"},
+ {97, __BYTES, "machine-identifier"},
+#else /* default (empty) table */
+ {0, 0, "dhcp.vendor."}, /* prefix */
+#endif
+ {0, __TXT, "%soption-%d"}
+};
+
+static struct dhcp_opt dhcp_opt[] = {
+ /* DHCP Option names, formats and codes, from RFC2132. */
+ {0, 0, "dhcp."}, // prefix
+ {1, __IP, "subnet-mask"},
+ {2, __32, "time-offset"}, /* this is signed */
+ {3, __IP, "routers"},
+ {4, __IP, "time-servers"},
+ {5, __IP, "ien116-name-servers"},
+ {6, __IP, "domain-name-servers"},
+ {7, __IP, "log-servers"},
+ {8, __IP, "cookie-servers"},
+ {9, __IP, "lpr-servers"},
+ {10, __IP, "impress-servers"},
+ {11, __IP, "resource-location-servers"},
+ {12, __TXT, "host-name"},
+ {13, __16, "boot-size"},
+ {14, __TXT, "merit-dump"},
+ {15, __TXT, "domain-name"},
+ {16, __IP, "swap-server"},
+ {17, __TXT, "root-path"},
+ {18, __TXT, "extensions-path"},
+ {19, __8, "ip-forwarding"},
+ {20, __8, "non-local-source-routing"},
+ {21, __IP, "policy-filter"},
+ {22, __16, "max-dgram-reassembly"},
+ {23, __8, "default-ip-ttl"},
+ {24, __32, "path-mtu-aging-timeout"},
+ {25, __16, "path-mtu-plateau-table"},
+ {26, __16, "interface-mtu"},
+ {27, __8, "all-subnets-local"},
+ {28, __IP, "broadcast-address"},
+ {29, __8, "perform-mask-discovery"},
+ {30, __8, "mask-supplier"},
+ {31, __8, "perform-router-discovery"},
+ {32, __IP, "router-solicitation-address"},
+ {33, __IP, "static-routes"},
+ {34, __8, "trailer-encapsulation"},
+ {35, __32, "arp-cache-timeout"},
+ {36, __8, "ieee802-3-encapsulation"},
+ {37, __8, "default-tcp-ttl"},
+ {38, __32, "tcp-keepalive-interval"},
+ {39, __8, "tcp-keepalive-garbage"},
+ {40, __TXT, "nis-domain"},
+ {41, __IP, "nis-servers"},
+ {42, __IP, "ntp-servers"},
+ {43, __VE, "vendor-encapsulated-options"},
+ {44, __IP, "netbios-name-servers"},
+ {45, __IP, "netbios-dd-server"},
+ {46, __8, "netbios-node-type"},
+ {47, __TXT, "netbios-scope"},
+ {48, __IP, "x-font-servers"},
+ {49, __IP, "x-display-managers"},
+ {50, __IP, "dhcp-requested-address"},
+ {51, __32, "dhcp-lease-time"},
+ {52, __8, "dhcp-option-overload"},
+ {53, __8, "dhcp-message-type"},
+ {54, __IP, "dhcp-server-identifier"},
+ {55, __8, "dhcp-parameter-request-list"},
+ {56, __TXT, "dhcp-message"},
+ {57, __16, "dhcp-max-message-size"},
+ {58, __32, "dhcp-renewal-time"},
+ {59, __32, "dhcp-rebinding-time"},
+ {60, __TXT, "vendor-class-identifier"},
+ {61, __TXT, "dhcp-client-identifier"},
+ {64, __TXT, "nisplus-domain"},
+ {65, __IP, "nisplus-servers"},
+ {66, __TXT, "tftp-server-name"},
+ {67, __TXT, "bootfile-name"},
+ {68, __IP, "mobile-ip-home-agent"},
+ {69, __IP, "smtp-server"},
+ {70, __IP, "pop-server"},
+ {71, __IP, "nntp-server"},
+ {72, __IP, "www-server"},
+ {73, __IP, "finger-server"},
+ {74, __IP, "irc-server"},
+ {75, __IP, "streettalk-server"},
+ {76, __IP, "streettalk-directory-assistance-server"},
+ {77, __TXT, "user-class"},
+ {85, __IP, "nds-servers"},
+ {86, __TXT, "nds-tree-name"},
+ {87, __TXT, "nds-context"},
+ {210, __TXT, "authenticate"},
+
+ /* use the following entries for arbitrary variables */
+ {246, __ILIST, ""},
+ {247, __ILIST, ""},
+ {248, __ILIST, ""},
+ {249, __ILIST, ""},
+ {250, __INDIR, ""},
+ {251, __INDIR, ""},
+ {252, __INDIR, ""},
+ {253, __INDIR, ""},
+ {254, __INDIR, ""},
+ {0, __TXT, "%soption-%d"}
+};
+
+/*
+ * parse a dhcp response, set environment variables translating options
+ * names and values according to the tables above. Also set dhcp.tags
+ * to the list of selected tags.
+ */
+static void
+setenv_(u_char *cp, u_char *ep, struct dhcp_opt *opts)
+{
+ u_char *ncp;
+ u_char tag;
+ char tags[512], *tp; /* the list of tags */
+
+#define FLD_SEP ',' /* separator in list of elements */
+ ncp = cp;
+ tp = tags;
+ if (opts == NULL)
+ opts = dhcp_opt;
+
+ while (ncp < ep) {
+ unsigned int size; /* option size */
+ char *vp, *endv, buf[256]; /* the value buffer */
+ struct dhcp_opt *op;
+
+ tag = *ncp++; /* extract tag and size */
+ size = *ncp++;
+ cp = ncp; /* current payload */
+ ncp += size; /* point to the next option */
+
+ if (tag == TAG_END)
+ break;
+ if (tag == 0)
+ continue;
+
+ for (op = opts+1; op->tag && op->tag != tag; op++)
+ ;
+ /* if not found we end up on the default entry */
+
+ /*
+ * Copy data into the buffer. libstand does not have snprintf so we
+ * need to be careful with sprintf(). With strings, the source is
+ * always <256 char so shorter than the buffer so we are safe; with
+ * other arguments, the longest string is inet_ntoa which is 16 bytes
+ * so we make sure to have always enough room in the string before
+ * trying an sprint.
+ */
+ vp = buf;
+ *vp = '\0';
+ endv = buf + sizeof(buf) - 1 - 16; /* last valid write position */
+
+ switch(op->fmt) {
+ case __NONE:
+ break; /* should not happen */
+
+ case __VE: /* recurse, vendor specific */
+ setenv_(cp, cp+size, vndr_opt);
+ break;
+
+ case __IP: /* ip address */
+ for (; size > 0 && vp < endv; size -= 4, cp += 4) {
+ struct in_addr in_ip; /* ip addresses */
+ if (vp != buf)
+ *vp++ = FLD_SEP;
+ bcopy(cp, &in_ip.s_addr, sizeof(in_ip.s_addr));
+ sprintf(vp, "%s", inet_ntoa(in_ip));
+ vp += strlen(vp);
+ }
+ break;
+
+ case __BYTES: /* opaque byte string */
+ for (; size > 0 && vp < endv; size -= 1, cp += 1) {
+ sprintf(vp, "%02x", *cp);
+ vp += strlen(vp);
+ }
+ break;
+
+ case __TXT:
+ bcopy(cp, buf, size); /* cannot overflow */
+ buf[size] = 0;
+ break;
+
+ case __32:
+ case __16:
+ case __8: /* op->fmt is also the length of each field */
+ for (; size > 0 && vp < endv; size -= op->fmt, cp += op->fmt) {
+ uint32_t v;
+ if (op->fmt == __32)
+ v = (cp[0]<<24) + (cp[1]<<16) + (cp[2]<<8) + cp[3];
+ else if (op->fmt == __16)
+ v = (cp[0]<<8) + cp[1];
+ else
+ v = cp[0];
+ if (vp != buf)
+ *vp++ = FLD_SEP;
+ sprintf(vp, "%u", v);
+ vp += strlen(vp);
+ }
+ break;
+
+ case __INDIR: /* name=value */
+ case __ILIST: /* name=value;name=value... */
+ bcopy(cp, buf, size); /* cannot overflow */
+ buf[size] = '\0';
+ for (endv = buf; endv; endv = vp) {
+ u_char *s = NULL; /* semicolon ? */
+
+ /* skip leading whitespace */
+ while (*endv && strchr(" \t\n\r", *endv))
+ endv++;
+ vp = strchr(endv, '='); /* find name=value separator */
+ if (!vp)
+ break;
+ *vp++ = 0;
+ if (op->fmt == __ILIST && (s = strchr(vp, ';')))
+ *s++ = '\0';
+ setenv(endv, vp, 1);
+ vp = s; /* prepare for next round */
+ }
+ buf[0] = '\0'; /* option already done */
+ }
+
+ if (tp - tags < sizeof(tags) - 5) { /* add tag to the list */
+ if (tp != tags)
+ *tp++ = FLD_SEP;
+ sprintf(tp, "%d", tag);
+ tp += strlen(tp);
+ }
+ if (buf[0]) {
+ char env[128]; /* the string name */
+
+ if (op->tag == 0)
+ sprintf(env, op->desc, opts[0].desc, tag);
+ else
+ sprintf(env, "%s%s", opts[0].desc, op->desc);
+ /*
+ * Do not replace existing values in the environment, so that
+ * locally-obtained values can override server-provided values.
+ */
+ setenv(env, buf, 0);
+ }
+ }
+ if (tp != tags) {
+ char env[128]; /* the string name */
+ sprintf(env, "%stags", opts[0].desc);
+ setenv(env, tags, 1);
+ }
+}
+#endif /* additional dhcp */
diff --git a/stand/libsa/bootp.h b/stand/libsa/bootp.h
new file mode 100644
index 0000000..2e7049b
--- /dev/null
+++ b/stand/libsa/bootp.h
@@ -0,0 +1,151 @@
+/* $NetBSD: bootp.h,v 1.4 1997/09/06 13:55:57 drochner Exp $ */
+
+/*
+ * Bootstrap Protocol (BOOTP). RFC951 and RFC1048.
+ *
+ * This file specifies the "implementation-independent" BOOTP protocol
+ * information which is common to both client and server.
+ *
+ * Copyright 1988 by Carnegie Mellon.
+ *
+ * Permission to use, copy, modify, and distribute this program for any
+ * purpose and without fee is hereby granted, provided that this copyright
+ * and permission notice appear on all copies and supporting documentation,
+ * the name of Carnegie Mellon not be used in advertising or publicity
+ * pertaining to distribution of the program without specific prior
+ * permission, and notice be given in supporting documentation that copying
+ * and distribution is by permission of Carnegie Mellon and Stanford
+ * University. Carnegie Mellon makes no representations about the
+ * suitability of this software for any purpose. It is provided "as is"
+ * without express or implied warranty.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _BOOTP_H_
+#define _BOOTP_H_
+
+struct bootp {
+ unsigned char bp_op; /* packet opcode type */
+ unsigned char bp_htype; /* hardware addr type */
+ unsigned char bp_hlen; /* hardware addr length */
+ unsigned char bp_hops; /* gateway hops */
+ unsigned int bp_xid; /* transaction ID */
+ unsigned short bp_secs; /* seconds since boot began */
+ unsigned short bp_flags;
+ struct in_addr bp_ciaddr; /* client IP address */
+ struct in_addr bp_yiaddr; /* 'your' IP address */
+ struct in_addr bp_siaddr; /* server IP address */
+ struct in_addr bp_giaddr; /* gateway IP address */
+ unsigned char bp_chaddr[16]; /* client hardware address */
+ unsigned char bp_sname[64]; /* server host name */
+ unsigned char bp_file[128]; /* boot file name */
+#ifdef SUPPORT_DHCP
+#define BOOTP_VENDSIZE 312
+#else
+#define BOOTP_VENDSIZE 64
+#endif
+ unsigned char bp_vend[BOOTP_VENDSIZE]; /* vendor-specific area */
+};
+
+/*
+ * UDP port numbers, server and client.
+ */
+#define IPPORT_BOOTPS 67
+#define IPPORT_BOOTPC 68
+
+#define BOOTREPLY 2
+#define BOOTREQUEST 1
+
+
+/*
+ * Vendor magic cookie (v_magic) for CMU
+ */
+#define VM_CMU "CMU"
+
+/*
+ * Vendor magic cookie (v_magic) for RFC1048
+ */
+#define VM_RFC1048 { 99, 130, 83, 99 }
+
+
+
+/*
+ * RFC1048 tag values used to specify what information is being supplied in
+ * the vendor field of the packet.
+ */
+
+#define TAG_PAD ((unsigned char) 0)
+#define TAG_SUBNET_MASK ((unsigned char) 1)
+#define TAG_TIME_OFFSET ((unsigned char) 2)
+#define TAG_GATEWAY ((unsigned char) 3)
+#define TAG_TIME_SERVER ((unsigned char) 4)
+#define TAG_NAME_SERVER ((unsigned char) 5)
+#define TAG_DOMAIN_SERVER ((unsigned char) 6)
+#define TAG_LOG_SERVER ((unsigned char) 7)
+#define TAG_COOKIE_SERVER ((unsigned char) 8)
+#define TAG_LPR_SERVER ((unsigned char) 9)
+#define TAG_IMPRESS_SERVER ((unsigned char) 10)
+#define TAG_RLP_SERVER ((unsigned char) 11)
+#define TAG_HOSTNAME ((unsigned char) 12)
+#define TAG_BOOTSIZE ((unsigned char) 13)
+#define TAG_DUMPFILE ((unsigned char) 14)
+#define TAG_DOMAINNAME ((unsigned char) 15)
+#define TAG_SWAPSERVER ((unsigned char) 16)
+#define TAG_ROOTPATH ((unsigned char) 17)
+#define TAG_INTF_MTU ((unsigned char) 26)
+
+#ifdef SUPPORT_DHCP
+#define TAG_REQ_ADDR ((unsigned char) 50)
+#define TAG_LEASETIME ((unsigned char) 51)
+#define TAG_OVERLOAD ((unsigned char) 52)
+#define TAG_DHCP_MSGTYPE ((unsigned char) 53)
+#define TAG_SERVERID ((unsigned char) 54)
+#define TAG_PARAM_REQ ((unsigned char) 55)
+#define TAG_MSG ((unsigned char) 56)
+#define TAG_MAXSIZE ((unsigned char) 57)
+#define TAG_T1 ((unsigned char) 58)
+#define TAG_T2 ((unsigned char) 59)
+#define TAG_CLASSID ((unsigned char) 60)
+#define TAG_CLIENTID ((unsigned char) 61)
+#define TAG_USER_CLASS ((unsigned char) 77)
+#endif
+
+#define TAG_END ((unsigned char) 255)
+
+#ifdef SUPPORT_DHCP
+#define DHCPDISCOVER 1
+#define DHCPOFFER 2
+#define DHCPREQUEST 3
+#define DHCPDECLINE 4
+#define DHCPACK 5
+#define DHCPNAK 6
+#define DHCPRELEASE 7
+#endif
+
+/*
+ * "vendor" data permitted for CMU bootp clients.
+ */
+
+struct cmu_vend {
+ unsigned char v_magic[4]; /* magic number */
+ unsigned int v_flags; /* flags/opcodes, etc. */
+ struct in_addr v_smask; /* Subnet mask */
+ struct in_addr v_dgate; /* Default gateway */
+ struct in_addr v_dns1, v_dns2; /* Domain name servers */
+ struct in_addr v_ins1, v_ins2; /* IEN-116 name servers */
+ struct in_addr v_ts1, v_ts2; /* Time servers */
+ unsigned char v_unused[25]; /* currently unused */
+};
+
+
+/* v_flags values */
+#define VF_SMASK 1 /* Subnet mask field contains valid data */
+
+/* cached bootp response/dhcp ack */
+extern struct bootp *bootp_response;
+extern size_t bootp_response_size;
+
+int dhcp_try_rfc1048(u_char *cp, u_int len);
+
+#endif /* _BOOTP_H_ */
diff --git a/stand/libsa/bootparam.c b/stand/libsa/bootparam.c
new file mode 100644
index 0000000..1de2d53
--- /dev/null
+++ b/stand/libsa/bootparam.c
@@ -0,0 +1,435 @@
+/* $NetBSD: bootparam.c,v 1.11 1997/06/26 19:11:32 drochner Exp $ */
+
+/*
+ * Copyright (c) 1995 Gordon W. Ross
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ * 4. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Gordon W. Ross
+ *
+ * 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/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * RPC/bootparams
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+
+#include <net/if.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+
+#include <string.h>
+
+#include "rpcv2.h"
+
+#include "stand.h"
+#include "net.h"
+#include "netif.h"
+#include "rpc.h"
+#include "bootparam.h"
+
+#ifdef DEBUG_RPC
+#define RPC_PRINTF(a) printf a
+#else
+#define RPC_PRINTF(a)
+#endif
+
+struct in_addr bp_server_addr; /* net order */
+n_short bp_server_port; /* net order */
+
+/*
+ * RPC definitions for bootparamd
+ */
+#define BOOTPARAM_PROG 100026
+#define BOOTPARAM_VERS 1
+#define BOOTPARAM_WHOAMI 1
+#define BOOTPARAM_GETFILE 2
+
+/*
+ * Inet address in RPC messages
+ * (Note, really four ints, NOT chars. Blech.)
+ */
+struct xdr_inaddr {
+ u_int32_t atype;
+ int32_t addr[4];
+};
+
+int xdr_inaddr_encode(char **p, struct in_addr ia);
+int xdr_inaddr_decode(char **p, struct in_addr *ia);
+
+int xdr_string_encode(char **p, char *str, int len);
+int xdr_string_decode(char **p, char *str, int *len_p);
+
+
+/*
+ * RPC: bootparam/whoami
+ * Given client IP address, get:
+ * client name (hostname)
+ * domain name (domainname)
+ * gateway address
+ *
+ * The hostname and domainname are set here for convenience.
+ *
+ * Note - bpsin is initialized to the broadcast address,
+ * and will be replaced with the bootparam server address
+ * after this call is complete. Have to use PMAP_PROC_CALL
+ * to make sure we get responses only from a servers that
+ * know about us (don't want to broadcast a getport call).
+ */
+int
+bp_whoami(int sockfd)
+{
+ /* RPC structures for PMAPPROC_CALLIT */
+ struct args {
+ u_int32_t prog;
+ u_int32_t vers;
+ u_int32_t proc;
+ u_int32_t arglen;
+ struct xdr_inaddr xina;
+ } *args;
+ struct repl {
+ u_int16_t _pad;
+ u_int16_t port;
+ u_int32_t encap_len;
+ /* encapsulated data here */
+ n_long capsule[64];
+ } *repl;
+ struct {
+ n_long h[RPC_HEADER_WORDS];
+ struct args d;
+ } sdata;
+ char *send_tail, *recv_head;
+ struct iodesc *d;
+ void *pkt;
+ int len, x, rc;
+
+ RPC_PRINTF(("bp_whoami: myip=%s\n", inet_ntoa(myip)));
+
+ rc = -1;
+ if (!(d = socktodesc(sockfd))) {
+ RPC_PRINTF(("bp_whoami: bad socket. %d\n", sockfd));
+ return (rc);
+ }
+ args = &sdata.d;
+
+ /*
+ * Build request args for PMAPPROC_CALLIT.
+ */
+ args->prog = htonl(BOOTPARAM_PROG);
+ args->vers = htonl(BOOTPARAM_VERS);
+ args->proc = htonl(BOOTPARAM_WHOAMI);
+ args->arglen = htonl(sizeof(struct xdr_inaddr));
+ send_tail = (char*) &args->xina;
+
+ /*
+ * append encapsulated data (client IP address)
+ */
+ if (xdr_inaddr_encode(&send_tail, myip))
+ return (rc);
+
+ /* RPC: portmap/callit */
+ d->myport = htons(--rpc_port);
+ d->destip.s_addr = INADDR_BROADCAST; /* XXX: subnet bcast? */
+ /* rpc_call will set d->destport */
+
+ pkt = NULL;
+ len = rpc_call(d, PMAPPROG, PMAPVERS, PMAPPROC_CALLIT,
+ args, send_tail - (char*)args, (void **)&repl, &pkt);
+ if (len < 8) {
+ printf("bootparamd: 'whoami' call failed\n");
+ goto done;
+ }
+
+ /* Save bootparam server address (from IP header). */
+ rpc_fromaddr(repl, &bp_server_addr, &bp_server_port);
+
+ /*
+ * Note that bp_server_port is now 111 due to the
+ * indirect call (using PMAPPROC_CALLIT), so get the
+ * actual port number from the reply data.
+ */
+ bp_server_port = repl->port;
+
+ RPC_PRINTF(("bp_whoami: server at %s:%d\n",
+ inet_ntoa(bp_server_addr), ntohs(bp_server_port)));
+
+ /* We have just done a portmap call, so cache the portnum. */
+ rpc_pmap_putcache(bp_server_addr,
+ BOOTPARAM_PROG,
+ BOOTPARAM_VERS,
+ (int)ntohs(bp_server_port));
+
+ /*
+ * Parse the encapsulated results from bootparam/whoami
+ */
+ x = ntohl(repl->encap_len);
+ if (len < x) {
+ printf("bp_whoami: short reply, %d < %d\n", len, x);
+ goto done;
+ }
+ recv_head = (char*) repl->capsule;
+
+ /* client name */
+ hostnamelen = MAXHOSTNAMELEN-1;
+ if (xdr_string_decode(&recv_head, hostname, &hostnamelen)) {
+ RPC_PRINTF(("bp_whoami: bad hostname\n"));
+ goto done;
+ }
+
+ /* domain name */
+ domainnamelen = MAXHOSTNAMELEN-1;
+ if (xdr_string_decode(&recv_head, domainname, &domainnamelen)) {
+ RPC_PRINTF(("bp_whoami: bad domainname\n"));
+ goto done;
+ }
+
+ /* gateway address */
+ if (xdr_inaddr_decode(&recv_head, &gateip)) {
+ RPC_PRINTF(("bp_whoami: bad gateway\n"));
+ goto done;
+ }
+
+ /* success */
+ rc = 0;
+done:
+ free(pkt);
+ return (rc);
+}
+
+
+/*
+ * RPC: bootparam/getfile
+ * Given client name and file "key", get:
+ * server name
+ * server IP address
+ * server pathname
+ */
+int
+bp_getfile(int sockfd, char *key, struct in_addr *serv_addr, char *pathname)
+{
+ struct {
+ n_long h[RPC_HEADER_WORDS];
+ n_long d[64];
+ } sdata;
+ void *pkt;
+ char serv_name[FNAME_SIZE];
+ char *rdata, *send_tail;
+ /* misc... */
+ struct iodesc *d;
+ int rc = -1, sn_len, path_len, rlen;
+
+ if (!(d = socktodesc(sockfd))) {
+ RPC_PRINTF(("bp_getfile: bad socket. %d\n", sockfd));
+ return (-1);
+ }
+
+ send_tail = (char*) sdata.d;
+
+ /*
+ * Build request message.
+ */
+
+ /* client name (hostname) */
+ if (xdr_string_encode(&send_tail, hostname, hostnamelen)) {
+ RPC_PRINTF(("bp_getfile: bad client\n"));
+ return (-1);
+ }
+
+ /* key name (root or swap) */
+ if (xdr_string_encode(&send_tail, key, strlen(key))) {
+ RPC_PRINTF(("bp_getfile: bad key\n"));
+ return (-1);
+ }
+
+ /* RPC: bootparam/getfile */
+ d->myport = htons(--rpc_port);
+ d->destip = bp_server_addr;
+ /* rpc_call will set d->destport */
+ pkt = NULL;
+ rlen = rpc_call(d,
+ BOOTPARAM_PROG, BOOTPARAM_VERS, BOOTPARAM_GETFILE,
+ sdata.d, send_tail - (char*)sdata.d,
+ (void **)&rdata, &pkt);
+ if (rlen < 4) {
+ RPC_PRINTF(("bp_getfile: short reply\n"));
+ errno = EBADRPC;
+ goto done;
+ }
+
+ /*
+ * Parse result message.
+ */
+
+ /* server name */
+ sn_len = FNAME_SIZE-1;
+ if (xdr_string_decode(&rdata, serv_name, &sn_len)) {
+ RPC_PRINTF(("bp_getfile: bad server name\n"));
+ goto done;
+ }
+
+ /* server IP address (mountd/NFS) */
+ if (xdr_inaddr_decode(&rdata, serv_addr)) {
+ RPC_PRINTF(("bp_getfile: bad server addr\n"));
+ goto done;
+ }
+
+ /* server pathname */
+ path_len = MAXPATHLEN-1;
+ if (xdr_string_decode(&rdata, pathname, &path_len)) {
+ RPC_PRINTF(("bp_getfile: bad server path\n"));
+ goto done;
+ }
+
+ /* success */
+ rc = 0;
+done:
+ free(pkt);
+ return (rc);
+}
+
+
+/*
+ * eXternal Data Representation routines.
+ * (but with non-standard args...)
+ */
+
+
+int
+xdr_string_encode(char **pkt, char *str, int len)
+{
+ uint32_t *lenp;
+ char *datap;
+ int padlen = (len + 3) & ~3; /* padded length */
+
+ /* The data will be int aligned. */
+ lenp = (uint32_t *) *pkt;
+ *pkt += sizeof(*lenp);
+ *lenp = htonl(len);
+
+ datap = *pkt;
+ *pkt += padlen;
+ bcopy(str, datap, len);
+
+ return (0);
+}
+
+int
+xdr_string_decode(char **pkt, char *str, int *len_p)
+{
+ uint32_t *lenp;
+ char *datap;
+ int slen; /* string length */
+ int plen; /* padded length */
+
+ /* The data will be int aligned. */
+ lenp = (uint32_t *) *pkt;
+ *pkt += sizeof(*lenp);
+ slen = ntohl(*lenp);
+ plen = (slen + 3) & ~3;
+
+ if (slen > *len_p)
+ slen = *len_p;
+ datap = *pkt;
+ *pkt += plen;
+ bcopy(datap, str, slen);
+
+ str[slen] = '\0';
+ *len_p = slen;
+
+ return (0);
+}
+
+
+int
+xdr_inaddr_encode(char **pkt, struct in_addr ia)
+{
+ struct xdr_inaddr *xi;
+ u_char *cp;
+ int32_t *ip;
+ union {
+ n_long l; /* network order */
+ u_char c[4];
+ } uia;
+
+ /* The data will be int aligned. */
+ xi = (struct xdr_inaddr *) *pkt;
+ *pkt += sizeof(*xi);
+ xi->atype = htonl(1);
+ uia.l = ia.s_addr;
+ cp = uia.c;
+ ip = xi->addr;
+ /*
+ * Note: the htonl() calls below DO NOT
+ * imply that uia.l is in host order.
+ * In fact this needs it in net order.
+ */
+ *ip++ = htonl((unsigned int)*cp++);
+ *ip++ = htonl((unsigned int)*cp++);
+ *ip++ = htonl((unsigned int)*cp++);
+ *ip++ = htonl((unsigned int)*cp++);
+
+ return (0);
+}
+
+int
+xdr_inaddr_decode(char **pkt, struct in_addr *ia)
+{
+ struct xdr_inaddr *xi;
+ u_char *cp;
+ int32_t *ip;
+ union {
+ n_long l; /* network order */
+ u_char c[4];
+ } uia;
+
+ /* The data will be int aligned. */
+ xi = (struct xdr_inaddr *) *pkt;
+ *pkt += sizeof(*xi);
+ if (xi->atype != htonl(1)) {
+ RPC_PRINTF(("xdr_inaddr_decode: bad addrtype=%d\n",
+ ntohl(xi->atype)));
+ return(-1);
+ }
+
+ cp = uia.c;
+ ip = xi->addr;
+ /*
+ * Note: the ntohl() calls below DO NOT
+ * imply that uia.l is in host order.
+ * In fact this needs it in net order.
+ */
+ *cp++ = ntohl(*ip++);
+ *cp++ = ntohl(*ip++);
+ *cp++ = ntohl(*ip++);
+ *cp++ = ntohl(*ip++);
+ ia->s_addr = uia.l;
+
+ return (0);
+}
diff --git a/stand/libsa/bootparam.h b/stand/libsa/bootparam.h
new file mode 100644
index 0000000..6f0c773
--- /dev/null
+++ b/stand/libsa/bootparam.h
@@ -0,0 +1,5 @@
+/* $NetBSD: bootparam.h,v 1.3 1998/01/05 19:19:41 perry Exp $ */
+
+int bp_whoami(int sock);
+int bp_getfile(int sock, char *key, struct in_addr *addrp, char *path);
+
diff --git a/stand/libsa/bzipfs.c b/stand/libsa/bzipfs.c
new file mode 100644
index 0000000..ff1514e
--- /dev/null
+++ b/stand/libsa/bzipfs.c
@@ -0,0 +1,388 @@
+/*
+ * Copyright (c) 1998 Michael Smith.
+ * Copyright (c) 2000 Maxim Sobolev
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#ifndef REGRESSION
+#include "stand.h"
+#else
+#include <stdlib.h>
+#include <sys/errno.h>
+#include <sys/fcntl.h>
+#include <sys/types.h>
+#include <sys/unistd.h>
+
+struct open_file {
+ int f_flags; /* see F_* below */
+ void *f_fsdata; /* file system specific data */
+};
+#define F_READ 0x0001 /* file opened for reading */
+#define EOFFSET (ELAST+8) /* relative seek not supported */
+static inline u_int min(u_int a, u_int b) { return(a < b ? a : b); }
+#define panic(x, y) abort()
+#endif
+
+#include <sys/stat.h>
+#include <string.h>
+#include <bzlib.h>
+
+#define BZ_BUFSIZE 2048 /* XXX larger? */
+
+struct bz_file
+{
+ int bzf_rawfd;
+ bz_stream bzf_bzstream;
+ char bzf_buf[BZ_BUFSIZE];
+ int bzf_endseen;
+};
+
+static int bzf_fill(struct bz_file *z);
+static int bzf_open(const char *path, struct open_file *f);
+static int bzf_close(struct open_file *f);
+static int bzf_read(struct open_file *f, void *buf, size_t size, size_t *resid);
+static off_t bzf_seek(struct open_file *f, off_t offset, int where);
+static int bzf_stat(struct open_file *f, struct stat *sb);
+
+#ifndef REGRESSION
+struct fs_ops bzipfs_fsops = {
+ "bzip",
+ bzf_open,
+ bzf_close,
+ bzf_read,
+ null_write,
+ bzf_seek,
+ bzf_stat,
+ null_readdir
+};
+#endif
+
+static int
+bzf_fill(struct bz_file *bzf)
+{
+ int result;
+ int req;
+
+ req = BZ_BUFSIZE - bzf->bzf_bzstream.avail_in;
+ result = 0;
+
+ /* If we need more */
+ if (req > 0) {
+ /* move old data to bottom of buffer */
+ if (req < BZ_BUFSIZE)
+ bcopy(bzf->bzf_buf + req, bzf->bzf_buf, BZ_BUFSIZE - req);
+
+ /* read to fill buffer and update availibility data */
+ result = read(bzf->bzf_rawfd, bzf->bzf_buf + bzf->bzf_bzstream.avail_in, req);
+ bzf->bzf_bzstream.next_in = bzf->bzf_buf;
+ if (result >= 0)
+ bzf->bzf_bzstream.avail_in += result;
+ }
+ return(result);
+}
+
+/*
+ * Adapted from get_byte/check_header in libz
+ *
+ * Returns 0 if the header is OK, nonzero if not.
+ */
+static int
+get_byte(struct bz_file *bzf)
+{
+ if ((bzf->bzf_bzstream.avail_in == 0) && (bzf_fill(bzf) == -1))
+ return(-1);
+ bzf->bzf_bzstream.avail_in--;
+ return(*(bzf->bzf_bzstream.next_in)++);
+}
+
+static int bz_magic[3] = {'B', 'Z', 'h'}; /* bzip2 magic header */
+
+static int
+check_header(struct bz_file *bzf)
+{
+ unsigned int len;
+ int c;
+
+ /* Check the bzip2 magic header */
+ for (len = 0; len < 3; len++) {
+ c = get_byte(bzf);
+ if (c != bz_magic[len]) {
+ return(1);
+ }
+ }
+ /* Check that the block size is valid */
+ c = get_byte(bzf);
+ if (c < '1' || c > '9')
+ return(1);
+
+ /* Put back bytes that we've took from the input stream */
+ bzf->bzf_bzstream.next_in -= 4;
+ bzf->bzf_bzstream.avail_in += 4;
+
+ return(0);
+}
+
+static int
+bzf_open(const char *fname, struct open_file *f)
+{
+ static char *bzfname;
+ int rawfd;
+ struct bz_file *bzf;
+ char *cp;
+ int error;
+ struct stat sb;
+
+ /* Have to be in "just read it" mode */
+ if (f->f_flags != F_READ)
+ return(EPERM);
+
+ /* If the name already ends in .gz or .bz2, ignore it */
+ if ((cp = strrchr(fname, '.')) && (!strcmp(cp, ".gz")
+ || !strcmp(cp, ".bz2") || !strcmp(cp, ".split")))
+ return(ENOENT);
+
+ /* Construct new name */
+ bzfname = malloc(strlen(fname) + 5);
+ if (bzfname == NULL)
+ return(ENOMEM);
+ sprintf(bzfname, "%s.bz2", fname);
+
+ /* Try to open the compressed datafile */
+ rawfd = open(bzfname, O_RDONLY);
+ free(bzfname);
+ if (rawfd == -1)
+ return(ENOENT);
+
+ if (fstat(rawfd, &sb) < 0) {
+ printf("bzf_open: stat failed\n");
+ close(rawfd);
+ return(ENOENT);
+ }
+ if (!S_ISREG(sb.st_mode)) {
+ printf("bzf_open: not a file\n");
+ close(rawfd);
+ return(EISDIR); /* best guess */
+ }
+
+ /* Allocate a bz_file structure, populate it */
+ bzf = malloc(sizeof(struct bz_file));
+ if (bzf == NULL)
+ return(ENOMEM);
+ bzero(bzf, sizeof(struct bz_file));
+ bzf->bzf_rawfd = rawfd;
+
+ /* Verify that the file is bzipped */
+ if (check_header(bzf)) {
+ close(bzf->bzf_rawfd);
+ free(bzf);
+ return(EFTYPE);
+ }
+
+ /* Initialise the inflation engine */
+ if ((error = BZ2_bzDecompressInit(&(bzf->bzf_bzstream), 0, 1)) != BZ_OK) {
+ printf("bzf_open: BZ2_bzDecompressInit returned %d\n", error);
+ close(bzf->bzf_rawfd);
+ free(bzf);
+ return(EIO);
+ }
+
+ /* Looks OK, we'll take it */
+ f->f_fsdata = bzf;
+ return(0);
+}
+
+static int
+bzf_close(struct open_file *f)
+{
+ struct bz_file *bzf = (struct bz_file *)f->f_fsdata;
+
+ BZ2_bzDecompressEnd(&(bzf->bzf_bzstream));
+ close(bzf->bzf_rawfd);
+ free(bzf);
+ return(0);
+}
+
+static int
+bzf_read(struct open_file *f, void *buf, size_t size, size_t *resid)
+{
+ struct bz_file *bzf = (struct bz_file *)f->f_fsdata;
+ int error;
+
+ bzf->bzf_bzstream.next_out = buf; /* where and how much */
+ bzf->bzf_bzstream.avail_out = size;
+
+ while (bzf->bzf_bzstream.avail_out && bzf->bzf_endseen == 0) {
+ if ((bzf->bzf_bzstream.avail_in == 0) && (bzf_fill(bzf) == -1)) {
+ printf("bzf_read: fill error\n");
+ return(EIO);
+ }
+ if (bzf->bzf_bzstream.avail_in == 0) { /* oops, unexpected EOF */
+ printf("bzf_read: unexpected EOF\n");
+ if (bzf->bzf_bzstream.avail_out == size)
+ return(EIO);
+ break;
+ }
+
+ error = BZ2_bzDecompress(&bzf->bzf_bzstream); /* decompression pass */
+ if (error == BZ_STREAM_END) { /* EOF, all done */
+ bzf->bzf_endseen = 1;
+ break;
+ }
+ if (error != BZ_OK) { /* argh, decompression error */
+ printf("bzf_read: BZ2_bzDecompress returned %d\n", error);
+ return(EIO);
+ }
+ }
+ if (resid != NULL)
+ *resid = bzf->bzf_bzstream.avail_out;
+ return(0);
+}
+
+static int
+bzf_rewind(struct open_file *f)
+{
+ struct bz_file *bzf = (struct bz_file *)f->f_fsdata;
+ struct bz_file *bzf_tmp;
+
+ /*
+ * Since bzip2 does not have an equivalent inflateReset function a crude
+ * one needs to be provided. The functions all called in such a way that
+ * at any time an error occurs a roll back can be done (effectively making
+ * this rewind 'atomic', either the reset occurs successfully or not at all,
+ * with no 'undefined' state happening).
+ */
+
+ /* Allocate a bz_file structure, populate it */
+ bzf_tmp = malloc(sizeof(struct bz_file));
+ if (bzf_tmp == NULL)
+ return(-1);
+ bzero(bzf_tmp, sizeof(struct bz_file));
+ bzf_tmp->bzf_rawfd = bzf->bzf_rawfd;
+
+ /* Initialise the inflation engine */
+ if (BZ2_bzDecompressInit(&(bzf_tmp->bzf_bzstream), 0, 1) != BZ_OK) {
+ free(bzf_tmp);
+ return(-1);
+ }
+
+ /* Seek back to the beginning of the file */
+ if (lseek(bzf->bzf_rawfd, 0, SEEK_SET) == -1) {
+ BZ2_bzDecompressEnd(&(bzf_tmp->bzf_bzstream));
+ free(bzf_tmp);
+ return(-1);
+ }
+
+ /* Free old bz_file data */
+ BZ2_bzDecompressEnd(&(bzf->bzf_bzstream));
+ free(bzf);
+
+ /* Use the new bz_file data */
+ f->f_fsdata = bzf_tmp;
+
+ return(0);
+}
+
+static off_t
+bzf_seek(struct open_file *f, off_t offset, int where)
+{
+ struct bz_file *bzf = (struct bz_file *)f->f_fsdata;
+ off_t target;
+ char discard[16];
+
+ switch (where) {
+ case SEEK_SET:
+ target = offset;
+ break;
+ case SEEK_CUR:
+ target = offset + bzf->bzf_bzstream.total_out_lo32;
+ break;
+ default:
+ errno = EINVAL;
+ return(-1);
+ }
+
+ /* Can we get there from here? */
+ if (target < bzf->bzf_bzstream.total_out_lo32 && bzf_rewind(f) != 0) {
+ errno = EOFFSET;
+ return -1;
+ }
+
+ /* if bzf_rewind was called then bzf has changed */
+ bzf = (struct bz_file *)f->f_fsdata;
+
+ /* skip forwards if required */
+ while (target > bzf->bzf_bzstream.total_out_lo32) {
+ errno = bzf_read(f, discard, min(sizeof(discard),
+ target - bzf->bzf_bzstream.total_out_lo32), NULL);
+ if (errno)
+ return(-1);
+ }
+ /* This is where we are (be honest if we overshot) */
+ return(bzf->bzf_bzstream.total_out_lo32);
+}
+
+static int
+bzf_stat(struct open_file *f, struct stat *sb)
+{
+ struct bz_file *bzf = (struct bz_file *)f->f_fsdata;
+ int result;
+
+ /* stat as normal, but indicate that size is unknown */
+ if ((result = fstat(bzf->bzf_rawfd, sb)) == 0)
+ sb->st_size = -1;
+ return(result);
+}
+
+void
+bz_internal_error(int errorcode)
+{
+ panic("bzipfs: critical error %d in bzip2 library occured\n", errorcode);
+}
+
+#ifdef REGRESSION
+/* Small test case, open and decompress test.bz2 */
+int main()
+{
+ struct open_file f;
+ char buf[1024];
+ size_t resid;
+ int err;
+
+ memset(&f, '\0', sizeof(f));
+ f.f_flags = F_READ;
+ err = bzf_open("test", &f);
+ if (err != 0)
+ exit(1);
+ do {
+ err = bzf_read(&f, buf, sizeof(buf), &resid);
+ } while (err == 0 && resid != sizeof(buf));
+
+ if (err != 0)
+ exit(2);
+ exit(0);
+}
+#endif
diff --git a/stand/libsa/cd9660.c b/stand/libsa/cd9660.c
new file mode 100644
index 0000000..c561883
--- /dev/null
+++ b/stand/libsa/cd9660.c
@@ -0,0 +1,600 @@
+/* $NetBSD: cd9660.c,v 1.5 1997/06/26 19:11:33 drochner Exp $ */
+
+/*
+ * Copyright (C) 1996 Wolfgang Solfrank.
+ * Copyright (C) 1996 TooLs GmbH.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by TooLs GmbH.
+ * 4. The name of TooLs GmbH may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``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 TOOLS GMBH 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/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Stand-alone ISO9660 file reading package.
+ *
+ * Note: This doesn't support Rock Ridge extensions, extended attributes,
+ * blocksizes other than 2048 bytes, multi-extent files, etc.
+ */
+#include <sys/param.h>
+#include <string.h>
+#include <sys/dirent.h>
+#include <isofs/cd9660/iso.h>
+#include <isofs/cd9660/cd9660_rrip.h>
+
+#include "stand.h"
+
+#define SUSP_CONTINUATION "CE"
+#define SUSP_PRESENT "SP"
+#define SUSP_STOP "ST"
+#define SUSP_EXTREF "ER"
+#define RRIP_NAME "NM"
+
+typedef struct {
+ ISO_SUSP_HEADER h;
+ u_char signature [ISODCL ( 5, 6)];
+ u_char len_skp [ISODCL ( 7, 7)]; /* 711 */
+} ISO_SUSP_PRESENT;
+
+static int buf_read_file(struct open_file *f, char **buf_p,
+ size_t *size_p);
+static int cd9660_open(const char *path, struct open_file *f);
+static int cd9660_close(struct open_file *f);
+static int cd9660_read(struct open_file *f, void *buf, size_t size,
+ size_t *resid);
+static int cd9660_write(struct open_file *f, void *buf, size_t size,
+ size_t *resid);
+static off_t cd9660_seek(struct open_file *f, off_t offset, int where);
+static int cd9660_stat(struct open_file *f, struct stat *sb);
+static int cd9660_readdir(struct open_file *f, struct dirent *d);
+static int dirmatch(struct open_file *f, const char *path,
+ struct iso_directory_record *dp, int use_rrip, int lenskip);
+static int rrip_check(struct open_file *f, struct iso_directory_record *dp,
+ int *lenskip);
+static char *rrip_lookup_name(struct open_file *f,
+ struct iso_directory_record *dp, int lenskip, size_t *len);
+static ISO_SUSP_HEADER *susp_lookup_record(struct open_file *f,
+ const char *identifier, struct iso_directory_record *dp,
+ int lenskip);
+
+struct fs_ops cd9660_fsops = {
+ "cd9660",
+ cd9660_open,
+ cd9660_close,
+ cd9660_read,
+ cd9660_write,
+ cd9660_seek,
+ cd9660_stat,
+ cd9660_readdir
+};
+
+#define F_ISDIR 0x0001 /* Directory */
+#define F_ROOTDIR 0x0002 /* Root directory */
+#define F_RR 0x0004 /* Rock Ridge on this volume */
+
+struct file {
+ int f_flags; /* file flags */
+ off_t f_off; /* Current offset within file */
+ daddr_t f_bno; /* Starting block number */
+ off_t f_size; /* Size of file */
+ daddr_t f_buf_blkno; /* block number of data block */
+ char *f_buf; /* buffer for data block */
+ int f_susp_skip; /* len_skip for SUSP records */
+};
+
+struct ptable_ent {
+ char namlen [ISODCL( 1, 1)]; /* 711 */
+ char extlen [ISODCL( 2, 2)]; /* 711 */
+ char block [ISODCL( 3, 6)]; /* 732 */
+ char parent [ISODCL( 7, 8)]; /* 722 */
+ char name [1];
+};
+#define PTFIXSZ 8
+#define PTSIZE(pp) roundup(PTFIXSZ + isonum_711((pp)->namlen), 2)
+
+#define cdb2devb(bno) ((bno) * ISO_DEFAULT_BLOCK_SIZE / DEV_BSIZE)
+
+static ISO_SUSP_HEADER *
+susp_lookup_record(struct open_file *f, const char *identifier,
+ struct iso_directory_record *dp, int lenskip)
+{
+ static char susp_buffer[ISO_DEFAULT_BLOCK_SIZE];
+ ISO_SUSP_HEADER *sh;
+ ISO_RRIP_CONT *shc;
+ char *p, *end;
+ int error;
+ size_t read;
+
+ p = dp->name + isonum_711(dp->name_len) + lenskip;
+ /* Names of even length have a padding byte after the name. */
+ if ((isonum_711(dp->name_len) & 1) == 0)
+ p++;
+ end = (char *)dp + isonum_711(dp->length);
+ while (p + 3 < end) {
+ sh = (ISO_SUSP_HEADER *)p;
+ if (bcmp(sh->type, identifier, 2) == 0)
+ return (sh);
+ if (bcmp(sh->type, SUSP_STOP, 2) == 0)
+ return (NULL);
+ if (bcmp(sh->type, SUSP_CONTINUATION, 2) == 0) {
+ shc = (ISO_RRIP_CONT *)sh;
+ error = f->f_dev->dv_strategy(f->f_devdata, F_READ,
+ cdb2devb(isonum_733(shc->location)),
+ ISO_DEFAULT_BLOCK_SIZE, susp_buffer, &read);
+
+ /* Bail if it fails. */
+ if (error != 0 || read != ISO_DEFAULT_BLOCK_SIZE)
+ return (NULL);
+ p = susp_buffer + isonum_733(shc->offset);
+ end = p + isonum_733(shc->length);
+ } else {
+ /* Ignore this record and skip to the next. */
+ p += isonum_711(sh->length);
+
+ /* Avoid infinite loops with corrupted file systems */
+ if (isonum_711(sh->length) == 0)
+ return (NULL);
+ }
+ }
+ return (NULL);
+}
+
+static char *
+rrip_lookup_name(struct open_file *f, struct iso_directory_record *dp,
+ int lenskip, size_t *len)
+{
+ ISO_RRIP_ALTNAME *p;
+
+ if (len == NULL)
+ return (NULL);
+
+ p = (ISO_RRIP_ALTNAME *)susp_lookup_record(f, RRIP_NAME, dp, lenskip);
+ if (p == NULL)
+ return (NULL);
+ switch (*p->flags) {
+ case ISO_SUSP_CFLAG_CURRENT:
+ *len = 1;
+ return (".");
+ case ISO_SUSP_CFLAG_PARENT:
+ *len = 2;
+ return ("..");
+ case 0:
+ *len = isonum_711(p->h.length) - 5;
+ return ((char *)p + 5);
+ default:
+ /*
+ * We don't handle hostnames or continued names as they are
+ * too hard, so just bail and use the default name.
+ */
+ return (NULL);
+ }
+}
+
+static int
+rrip_check(struct open_file *f, struct iso_directory_record *dp, int *lenskip)
+{
+ ISO_SUSP_PRESENT *sp;
+ ISO_RRIP_EXTREF *er;
+ char *p;
+
+ /* First, see if we can find a SP field. */
+ p = dp->name + isonum_711(dp->name_len);
+ if (p > (char *)dp + isonum_711(dp->length))
+ return (0);
+ sp = (ISO_SUSP_PRESENT *)p;
+ if (bcmp(sp->h.type, SUSP_PRESENT, 2) != 0)
+ return (0);
+ if (isonum_711(sp->h.length) != sizeof(ISO_SUSP_PRESENT))
+ return (0);
+ if (sp->signature[0] != 0xbe || sp->signature[1] != 0xef)
+ return (0);
+ *lenskip = isonum_711(sp->len_skp);
+
+ /*
+ * Now look for an ER field. If RRIP is present, then there must
+ * be at least one of these. It would be more pedantic to walk
+ * through the list of fields looking for a Rock Ridge ER field.
+ */
+ er = (ISO_RRIP_EXTREF *)susp_lookup_record(f, SUSP_EXTREF, dp, 0);
+ if (er == NULL)
+ return (0);
+ return (1);
+}
+
+static int
+dirmatch(struct open_file *f, const char *path, struct iso_directory_record *dp,
+ int use_rrip, int lenskip)
+{
+ size_t len;
+ char *cp;
+ int i, icase;
+
+ if (use_rrip)
+ cp = rrip_lookup_name(f, dp, lenskip, &len);
+ else
+ cp = NULL;
+ if (cp == NULL) {
+ len = isonum_711(dp->name_len);
+ cp = dp->name;
+ icase = 1;
+ } else
+ icase = 0;
+ for (i = len; --i >= 0; path++, cp++) {
+ if (!*path || *path == '/')
+ break;
+ if (*path == *cp)
+ continue;
+ if (!icase && toupper(*path) == *cp)
+ continue;
+ return 0;
+ }
+ if (*path && *path != '/')
+ return 0;
+ /*
+ * Allow stripping of trailing dots and the version number.
+ * Note that this will find the first instead of the last version
+ * of a file.
+ */
+ if (i >= 0 && (*cp == ';' || *cp == '.')) {
+ /* This is to prevent matching of numeric extensions */
+ if (*cp == '.' && cp[1] != ';')
+ return 0;
+ while (--i >= 0)
+ if (*++cp != ';' && (*cp < '0' || *cp > '9'))
+ return 0;
+ }
+ return 1;
+}
+
+static int
+cd9660_open(const char *path, struct open_file *f)
+{
+ struct file *fp = NULL;
+ void *buf;
+ struct iso_primary_descriptor *vd;
+ size_t buf_size, read, dsize, off;
+ daddr_t bno, boff;
+ struct iso_directory_record rec;
+ struct iso_directory_record *dp = NULL;
+ int rc, first, use_rrip, lenskip;
+
+ /* First find the volume descriptor */
+ buf = malloc(buf_size = ISO_DEFAULT_BLOCK_SIZE);
+ vd = buf;
+ for (bno = 16;; bno++) {
+ twiddle(1);
+ rc = f->f_dev->dv_strategy(f->f_devdata, F_READ, cdb2devb(bno),
+ ISO_DEFAULT_BLOCK_SIZE, buf, &read);
+ if (rc)
+ goto out;
+ if (read != ISO_DEFAULT_BLOCK_SIZE) {
+ rc = EIO;
+ goto out;
+ }
+ rc = EINVAL;
+ if (bcmp(vd->id, ISO_STANDARD_ID, sizeof vd->id) != 0)
+ goto out;
+ if (isonum_711(vd->type) == ISO_VD_END)
+ goto out;
+ if (isonum_711(vd->type) == ISO_VD_PRIMARY)
+ break;
+ }
+ if (isonum_723(vd->logical_block_size) != ISO_DEFAULT_BLOCK_SIZE)
+ goto out;
+
+ rec = *(struct iso_directory_record *) vd->root_directory_record;
+ if (*path == '/') path++; /* eat leading '/' */
+
+ first = 1;
+ use_rrip = 0;
+ while (*path) {
+ bno = isonum_733(rec.extent) + isonum_711(rec.ext_attr_length);
+ dsize = isonum_733(rec.size);
+ off = 0;
+ boff = 0;
+
+ while (off < dsize) {
+ if ((off % ISO_DEFAULT_BLOCK_SIZE) == 0) {
+ twiddle(1);
+ rc = f->f_dev->dv_strategy
+ (f->f_devdata, F_READ,
+ cdb2devb(bno + boff),
+ ISO_DEFAULT_BLOCK_SIZE,
+ buf, &read);
+ if (rc)
+ goto out;
+ if (read != ISO_DEFAULT_BLOCK_SIZE) {
+ rc = EIO;
+ goto out;
+ }
+ boff++;
+ dp = (struct iso_directory_record *) buf;
+ }
+ if (isonum_711(dp->length) == 0) {
+ /* skip to next block, if any */
+ off = boff * ISO_DEFAULT_BLOCK_SIZE;
+ continue;
+ }
+
+ /* See if RRIP is in use. */
+ if (first)
+ use_rrip = rrip_check(f, dp, &lenskip);
+
+ if (dirmatch(f, path, dp, use_rrip,
+ first ? 0 : lenskip)) {
+ first = 0;
+ break;
+ } else
+ first = 0;
+
+ dp = (struct iso_directory_record *)
+ ((char *) dp + isonum_711(dp->length));
+ /* If the new block has zero length, it is padding. */
+ if (isonum_711(dp->length) == 0) {
+ /* Skip to next block, if any. */
+ off = boff * ISO_DEFAULT_BLOCK_SIZE;
+ continue;
+ }
+ off += isonum_711(dp->length);
+ }
+ if (off >= dsize) {
+ rc = ENOENT;
+ goto out;
+ }
+
+ rec = *dp;
+ while (*path && *path != '/') /* look for next component */
+ path++;
+ if (*path) path++; /* skip '/' */
+ }
+
+ /* allocate file system specific data structure */
+ fp = malloc(sizeof(struct file));
+ bzero(fp, sizeof(struct file));
+ f->f_fsdata = (void *)fp;
+
+ if ((isonum_711(rec.flags) & 2) != 0) {
+ fp->f_flags = F_ISDIR;
+ }
+ if (first) {
+ fp->f_flags |= F_ROOTDIR;
+
+ /* Check for Rock Ridge since we didn't in the loop above. */
+ bno = isonum_733(rec.extent) + isonum_711(rec.ext_attr_length);
+ twiddle(1);
+ rc = f->f_dev->dv_strategy(f->f_devdata, F_READ, cdb2devb(bno),
+ ISO_DEFAULT_BLOCK_SIZE, buf, &read);
+ if (rc)
+ goto out;
+ if (read != ISO_DEFAULT_BLOCK_SIZE) {
+ rc = EIO;
+ goto out;
+ }
+ dp = (struct iso_directory_record *)buf;
+ use_rrip = rrip_check(f, dp, &lenskip);
+ }
+ if (use_rrip) {
+ fp->f_flags |= F_RR;
+ fp->f_susp_skip = lenskip;
+ }
+ fp->f_off = 0;
+ fp->f_bno = isonum_733(rec.extent) + isonum_711(rec.ext_attr_length);
+ fp->f_size = isonum_733(rec.size);
+ free(buf);
+
+ return 0;
+
+out:
+ if (fp)
+ free(fp);
+ free(buf);
+
+ return rc;
+}
+
+static int
+cd9660_close(struct open_file *f)
+{
+ struct file *fp = (struct file *)f->f_fsdata;
+
+ f->f_fsdata = NULL;
+ free(fp);
+
+ return 0;
+}
+
+static int
+buf_read_file(struct open_file *f, char **buf_p, size_t *size_p)
+{
+ struct file *fp = (struct file *)f->f_fsdata;
+ daddr_t blkno, blkoff;
+ int rc = 0;
+ size_t read;
+
+ blkno = fp->f_off / ISO_DEFAULT_BLOCK_SIZE + fp->f_bno;
+ blkoff = fp->f_off % ISO_DEFAULT_BLOCK_SIZE;
+
+ if (blkno != fp->f_buf_blkno) {
+ if (fp->f_buf == (char *)0)
+ fp->f_buf = malloc(ISO_DEFAULT_BLOCK_SIZE);
+
+ twiddle(16);
+ rc = f->f_dev->dv_strategy(f->f_devdata, F_READ,
+ cdb2devb(blkno), ISO_DEFAULT_BLOCK_SIZE,
+ fp->f_buf, &read);
+ if (rc)
+ return (rc);
+ if (read != ISO_DEFAULT_BLOCK_SIZE)
+ return (EIO);
+
+ fp->f_buf_blkno = blkno;
+ }
+
+ *buf_p = fp->f_buf + blkoff;
+ *size_p = ISO_DEFAULT_BLOCK_SIZE - blkoff;
+
+ if (*size_p > fp->f_size - fp->f_off)
+ *size_p = fp->f_size - fp->f_off;
+ return (rc);
+}
+
+static int
+cd9660_read(struct open_file *f, void *start, size_t size, size_t *resid)
+{
+ struct file *fp = (struct file *)f->f_fsdata;
+ char *buf, *addr;
+ size_t buf_size, csize;
+ int rc = 0;
+
+ addr = start;
+ while (size) {
+ if (fp->f_off < 0 || fp->f_off >= fp->f_size)
+ break;
+
+ rc = buf_read_file(f, &buf, &buf_size);
+ if (rc)
+ break;
+
+ csize = size > buf_size ? buf_size : size;
+ bcopy(buf, addr, csize);
+
+ fp->f_off += csize;
+ addr += csize;
+ size -= csize;
+ }
+ if (resid)
+ *resid = size;
+ return (rc);
+}
+
+static int
+cd9660_readdir(struct open_file *f, struct dirent *d)
+{
+ struct file *fp = (struct file *)f->f_fsdata;
+ struct iso_directory_record *ep;
+ size_t buf_size, reclen, namelen;
+ int error = 0;
+ int lenskip;
+ char *buf, *name;
+
+again:
+ if (fp->f_off >= fp->f_size)
+ return (ENOENT);
+ error = buf_read_file(f, &buf, &buf_size);
+ if (error)
+ return (error);
+ ep = (struct iso_directory_record *)buf;
+
+ if (isonum_711(ep->length) == 0) {
+ daddr_t blkno;
+
+ /* skip to next block, if any */
+ blkno = fp->f_off / ISO_DEFAULT_BLOCK_SIZE;
+ fp->f_off = (blkno + 1) * ISO_DEFAULT_BLOCK_SIZE;
+ goto again;
+ }
+
+ if (fp->f_flags & F_RR) {
+ if (fp->f_flags & F_ROOTDIR && fp->f_off == 0)
+ lenskip = 0;
+ else
+ lenskip = fp->f_susp_skip;
+ name = rrip_lookup_name(f, ep, lenskip, &namelen);
+ } else
+ name = NULL;
+ if (name == NULL) {
+ namelen = isonum_711(ep->name_len);
+ name = ep->name;
+ if (namelen == 1) {
+ if (ep->name[0] == 0)
+ name = ".";
+ else if (ep->name[0] == 1) {
+ namelen = 2;
+ name = "..";
+ }
+ }
+ }
+ reclen = sizeof(struct dirent) - (MAXNAMLEN+1) + namelen + 1;
+ reclen = (reclen + 3) & ~3;
+
+ d->d_fileno = isonum_733(ep->extent);
+ d->d_reclen = reclen;
+ if (isonum_711(ep->flags) & 2)
+ d->d_type = DT_DIR;
+ else
+ d->d_type = DT_REG;
+ d->d_namlen = namelen;
+
+ bcopy(name, d->d_name, d->d_namlen);
+ d->d_name[d->d_namlen] = 0;
+
+ fp->f_off += isonum_711(ep->length);
+ return (0);
+}
+
+static int
+cd9660_write(struct open_file *f __unused, void *start __unused, size_t size __unused, size_t *resid __unused)
+{
+ return EROFS;
+}
+
+static off_t
+cd9660_seek(struct open_file *f, off_t offset, int where)
+{
+ struct file *fp = (struct file *)f->f_fsdata;
+
+ switch (where) {
+ case SEEK_SET:
+ fp->f_off = offset;
+ break;
+ case SEEK_CUR:
+ fp->f_off += offset;
+ break;
+ case SEEK_END:
+ fp->f_off = fp->f_size - offset;
+ break;
+ default:
+ return -1;
+ }
+ return fp->f_off;
+}
+
+static int
+cd9660_stat(struct open_file *f, struct stat *sb)
+{
+ struct file *fp = (struct file *)f->f_fsdata;
+
+ /* only important stuff */
+ sb->st_mode = S_IRUSR | S_IRGRP | S_IROTH;
+ if (fp->f_flags & F_ISDIR)
+ sb->st_mode |= S_IFDIR;
+ else
+ sb->st_mode |= S_IFREG;
+ sb->st_uid = sb->st_gid = 0;
+ sb->st_size = fp->f_size;
+ return 0;
+}
diff --git a/stand/libsa/close.c b/stand/libsa/close.c
new file mode 100644
index 0000000..939f025
--- /dev/null
+++ b/stand/libsa/close.c
@@ -0,0 +1,98 @@
+/* $NetBSD: close.c,v 1.7 1997/01/22 00:38:09 cgd Exp $ */
+
+/*-
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * The Mach Operating System project at Carnegie-Mellon University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)close.c 8.1 (Berkeley) 6/11/93
+ *
+ *
+ * Copyright (c) 1989, 1990, 1991 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Author: Alessandro Forin
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie the
+ * rights to redistribute these changes.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "stand.h"
+
+int
+close(int fd)
+{
+ struct open_file *f = &files[fd];
+ int err1 = 0, err2 = 0;
+
+ if ((unsigned)fd >= SOPEN_MAX || f->f_flags == 0) {
+ errno = EBADF;
+ return (-1);
+ }
+ if (f->f_rabuf != NULL) {
+ free(f->f_rabuf);
+ f->f_rabuf = NULL;
+ }
+ if (!(f->f_flags & F_RAW) && f->f_ops)
+ err1 = (f->f_ops->fo_close)(f);
+ if (!(f->f_flags & F_NODEV) && f->f_dev)
+ err2 = (f->f_dev->dv_close)(f);
+ if (f->f_devdata != NULL)
+ devclose(f);
+ f->f_flags = 0;
+ if (err1) {
+ errno = err1;
+ return (-1);
+ }
+ if (err2) {
+ errno = err2;
+ return (-1);
+ }
+ return (0);
+}
diff --git a/stand/libsa/closeall.c b/stand/libsa/closeall.c
new file mode 100644
index 0000000..a661426
--- /dev/null
+++ b/stand/libsa/closeall.c
@@ -0,0 +1,76 @@
+/* $NetBSD: closeall.c,v 1.1 1996/01/13 22:25:36 leo Exp $ */
+
+/*-
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * The Mach Operating System project at Carnegie-Mellon University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)close.c 8.1 (Berkeley) 6/11/93
+ *
+ *
+ * Copyright (c) 1989, 1990, 1991 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Author: Alessandro Forin
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie the
+ * rights to redistribute these changes.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "stand.h"
+
+void
+closeall()
+{
+ int i;
+
+ for (i = 0; i < SOPEN_MAX; i++)
+ if (files[i].f_flags != 0)
+ (void)close(i);
+}
diff --git a/stand/libsa/crc32.c b/stand/libsa/crc32.c
new file mode 100644
index 0000000..1a3e3a3
--- /dev/null
+++ b/stand/libsa/crc32.c
@@ -0,0 +1,108 @@
+/*-
+ * COPYRIGHT (C) 1986 Gary S. Brown. You may use this program, or
+ * code or tables extracted from it, as desired without restriction.
+ */
+
+/*
+ * First, the polynomial itself and its table of feedback terms. The
+ * polynomial is
+ * X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0
+ *
+ * Note that we take it "backwards" and put the highest-order term in
+ * the lowest-order bit. The X^32 term is "implied"; the LSB is the
+ * X^31 term, etc. The X^0 term (usually shown as "+1") results in
+ * the MSB being 1
+ *
+ * Note that the usual hardware shift register implementation, which
+ * is what we're using (we're merely optimizing it by doing eight-bit
+ * chunks at a time) shifts bits into the lowest-order term. In our
+ * implementation, that means shifting towards the right. Why do we
+ * do it this way? Because the calculated CRC must be transmitted in
+ * order from highest-order term to lowest-order term. UARTs transmit
+ * characters in order from LSB to MSB. By storing the CRC this way
+ * we hand it to the UART in the order low-byte to high-byte; the UART
+ * sends each low-bit to hight-bit; and the result is transmission bit
+ * by bit from highest- to lowest-order term without requiring any bit
+ * shuffling on our part. Reception works similarly
+ *
+ * The feedback terms table consists of 256, 32-bit entries. Notes
+ *
+ * The table can be generated at runtime if desired; code to do so
+ * is shown later. It might not be obvious, but the feedback
+ * terms simply represent the results of eight shift/xor opera
+ * tions for all combinations of data and CRC register values
+ *
+ * The values must be right-shifted by eight bits by the "updcrc
+ * logic; the shift must be unsigned (bring in zeroes). On some
+ * hardware you could probably optimize the shift in assembler by
+ * using byte-swap instructions
+ * polynomial $edb88320
+ *
+ *
+ * CRC32 code derived from work by Gary S. Brown.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+
+#include "crc32.h"
+
+static const uint32_t crc32_tab[] = {
+ 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
+ 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
+ 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
+ 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
+ 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
+ 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
+ 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
+ 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
+ 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
+ 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
+ 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
+ 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
+ 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
+ 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
+ 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
+ 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
+ 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
+ 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
+ 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
+ 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
+ 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
+ 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
+ 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
+ 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
+ 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
+ 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
+ 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
+ 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
+ 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
+ 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
+ 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
+ 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
+ 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
+ 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
+ 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
+ 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
+ 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
+ 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
+ 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
+ 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
+ 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
+ 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
+ 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
+};
+
+uint32_t
+crc32(const void *buf, size_t size)
+{
+ const uint8_t *p = buf;
+ uint32_t crc;
+
+ crc = ~0U;
+ while (size--)
+ crc = crc32_tab[(crc ^ *p++) & 0xFF] ^ (crc >> 8);
+ return (crc ^ ~0U);
+}
diff --git a/stand/libsa/crc32.h b/stand/libsa/crc32.h
new file mode 100644
index 0000000..adfd628
--- /dev/null
+++ b/stand/libsa/crc32.h
@@ -0,0 +1,13 @@
+/*-
+ * COPYRIGHT (C) 1986 Gary S. Brown. You may use this program, or
+ * code or tables extracted from it, as desired without restriction.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _CRC32_H_
+#define _CRC32_H_
+
+uint32_t crc32(const void *buf, size_t size);
+
+#endif /* !_CRC32_H_ */
diff --git a/stand/libsa/dev.c b/stand/libsa/dev.c
new file mode 100644
index 0000000..482c082
--- /dev/null
+++ b/stand/libsa/dev.c
@@ -0,0 +1,61 @@
+/* $NetBSD: dev.c,v 1.4 1994/10/30 21:48:23 cgd Exp $ */
+
+/*-
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)dev.c 8.1 (Berkeley) 6/11/93
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/reboot.h>
+
+#include "stand.h"
+
+int
+nodev()
+{
+ return (ENXIO);
+}
+
+void
+nullsys()
+{
+}
+
+/* ARGSUSED */
+int
+noioctl(f, cmd, data)
+ struct open_file *f;
+ u_long cmd;
+ void *data;
+{
+ return (EINVAL);
+}
diff --git a/stand/libsa/dosfs.c b/stand/libsa/dosfs.c
new file mode 100644
index 0000000..6bbe961
--- /dev/null
+++ b/stand/libsa/dosfs.c
@@ -0,0 +1,868 @@
+/*
+ * Copyright (c) 1996, 1998 Robert Nordier
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Readonly filesystem for Microsoft FAT12/FAT16/FAT32 filesystems,
+ * also supports VFAT.
+ */
+
+#include <sys/types.h>
+#include <string.h>
+#include <stddef.h>
+
+#include "stand.h"
+
+#include "dosfs.h"
+
+
+static int dos_open(const char *path, struct open_file *fd);
+static int dos_close(struct open_file *fd);
+static int dos_read(struct open_file *fd, void *buf, size_t size, size_t *resid);
+static off_t dos_seek(struct open_file *fd, off_t offset, int whence);
+static int dos_stat(struct open_file *fd, struct stat *sb);
+static int dos_readdir(struct open_file *fd, struct dirent *d);
+
+struct fs_ops dosfs_fsops = {
+ "dosfs",
+ dos_open,
+ dos_close,
+ dos_read,
+ null_write,
+ dos_seek,
+ dos_stat,
+ dos_readdir
+};
+
+#define SECSIZ 512 /* sector size */
+#define SSHIFT 9 /* SECSIZ shift */
+#define DEPSEC 16 /* directory entries per sector */
+#define DSHIFT 4 /* DEPSEC shift */
+#define LOCLUS 2 /* lowest cluster number */
+#define FATBLKSZ 0x20000 /* size of block in the FAT cache buffer */
+
+/* DOS "BIOS Parameter Block" */
+typedef struct {
+ u_char secsiz[2]; /* sector size */
+ u_char spc; /* sectors per cluster */
+ u_char ressec[2]; /* reserved sectors */
+ u_char fats; /* FATs */
+ u_char dirents[2]; /* root directory entries */
+ u_char secs[2]; /* total sectors */
+ u_char media; /* media descriptor */
+ u_char spf[2]; /* sectors per FAT */
+ u_char spt[2]; /* sectors per track */
+ u_char heads[2]; /* drive heads */
+ u_char hidsec[4]; /* hidden sectors */
+ u_char lsecs[4]; /* huge sectors */
+ u_char lspf[4]; /* huge sectors per FAT */
+ u_char xflg[2]; /* flags */
+ u_char vers[2]; /* filesystem version */
+ u_char rdcl[4]; /* root directory start cluster */
+ u_char infs[2]; /* filesystem info sector */
+ u_char bkbs[2]; /* backup boot sector */
+} DOS_BPB;
+
+/* Initial portion of DOS boot sector */
+typedef struct {
+ u_char jmp[3]; /* usually 80x86 'jmp' opcode */
+ u_char oem[8]; /* OEM name and version */
+ DOS_BPB bpb; /* BPB */
+} DOS_BS;
+
+/* Supply missing "." and ".." root directory entries */
+static const char *const dotstr[2] = {".", ".."};
+static DOS_DE dot[2] = {
+ {". ", " ", FA_DIR, {0, 0, {0, 0}, {0, 0}, {0, 0}, {0, 0}},
+ {0, 0}, {0x21, 0}, {0, 0}, {0, 0, 0, 0}},
+ {".. ", " ", FA_DIR, {0, 0, {0, 0}, {0, 0}, {0, 0}, {0, 0}},
+ {0, 0}, {0x21, 0}, {0, 0}, {0, 0, 0, 0}}
+};
+
+/* The usual conversion macros to avoid multiplication and division */
+#define bytsec(n) ((n) >> SSHIFT)
+#define secbyt(s) ((s) << SSHIFT)
+#define entsec(e) ((e) >> DSHIFT)
+#define bytblk(fs, n) ((n) >> (fs)->bshift)
+#define blkbyt(fs, b) ((b) << (fs)->bshift)
+#define secblk(fs, s) ((s) >> ((fs)->bshift - SSHIFT))
+#define blksec(fs, b) ((b) << ((fs)->bshift - SSHIFT))
+
+/* Convert cluster number to offset within filesystem */
+#define blkoff(fs, b) (secbyt((fs)->lsndta) + blkbyt(fs, (b) - LOCLUS))
+
+/* Convert cluster number to logical sector number */
+#define blklsn(fs, b) ((fs)->lsndta + blksec(fs, (b) - LOCLUS))
+
+/* Convert cluster number to offset within FAT */
+#define fatoff(sz, c) ((sz) == 12 ? (c) + ((c) >> 1) : \
+ (sz) == 16 ? (c) << 1 : \
+ (c) << 2)
+
+/* Does cluster number reference a valid data cluster? */
+#define okclus(fs, c) ((c) >= LOCLUS && (c) <= (fs)->xclus)
+
+/* Get start cluster from directory entry */
+#define stclus(sz, de) ((sz) != 32 ? cv2((de)->clus) : \
+ ((u_int)cv2((de)->dex.h_clus) << 16) | \
+ cv2((de)->clus))
+
+static int parsebs(DOS_FS *, DOS_BS *);
+static int namede(DOS_FS *, const char *, DOS_DE **);
+static int lookup(DOS_FS *, u_int, const char *, DOS_DE **);
+static void cp_xdnm(u_char *, DOS_XDE *);
+static void cp_sfn(u_char *, DOS_DE *);
+static off_t fsize(DOS_FS *, DOS_DE *);
+static int fatcnt(DOS_FS *, u_int);
+static int fatget(DOS_FS *, u_int *);
+static int fatend(u_int, u_int);
+static int ioread(DOS_FS *, u_int, void *, size_t);
+static int ioget(struct open_file *, daddr_t, void *, size_t);
+
+static int
+dos_read_fatblk(DOS_FS *fs, struct open_file *fd, u_int blknum)
+{
+ int err;
+ size_t io_size;
+ daddr_t offset_in_fat, max_offset_in_fat;
+
+ offset_in_fat = ((daddr_t)blknum) * FATBLKSZ;
+ max_offset_in_fat = secbyt(fs->spf);
+ io_size = FATBLKSZ;
+ if (offset_in_fat > max_offset_in_fat)
+ offset_in_fat = max_offset_in_fat;
+ if (offset_in_fat + io_size > max_offset_in_fat)
+ io_size = ((size_t)(max_offset_in_fat - offset_in_fat));
+
+ if (io_size != 0) {
+ err = ioget(fd, fs->lsnfat + bytsec(offset_in_fat),
+ fs->fatbuf, io_size);
+ if (err != 0) {
+ fs->fatbuf_blknum = ((u_int)(-1));
+ return (err);
+ }
+ }
+ if (io_size < FATBLKSZ)
+ memset(fs->fatbuf + io_size, 0, FATBLKSZ - io_size);
+
+ fs->fatbuf_blknum = blknum;
+ return (0);
+}
+
+/*
+ * Mount DOS filesystem
+ */
+static int
+dos_mount(DOS_FS *fs, struct open_file *fd)
+{
+ int err;
+ u_char *buf;
+
+ bzero(fs, sizeof(DOS_FS));
+ fs->fd = fd;
+
+ if ((buf = malloc(secbyt(1))) == NULL)
+ return (errno);
+ if ((err = ioget(fs->fd, 0, buf, secbyt(1))) ||
+ (err = parsebs(fs, (DOS_BS *)buf))) {
+ free(buf);
+ return (err);
+ }
+ free(buf);
+
+ if ((fs->fatbuf = malloc(FATBLKSZ)) == NULL)
+ return (errno);
+ err = dos_read_fatblk(fs, fd, 0);
+ if (err != 0) {
+ free(fs->fatbuf);
+ return (err);
+ }
+
+ fs->root = dot[0];
+ fs->root.name[0] = ' ';
+ if (fs->fatsz == 32) {
+ fs->root.clus[0] = fs->rdcl & 0xff;
+ fs->root.clus[1] = (fs->rdcl >> 8) & 0xff;
+ fs->root.dex.h_clus[0] = (fs->rdcl >> 16) & 0xff;
+ fs->root.dex.h_clus[1] = (fs->rdcl >> 24) & 0xff;
+ }
+ return (0);
+}
+
+/*
+ * Unmount mounted filesystem
+ */
+static int
+dos_unmount(DOS_FS *fs)
+{
+ if (fs->links)
+ return (EBUSY);
+ free(fs->fatbuf);
+ free(fs);
+ return (0);
+}
+
+/*
+ * Open DOS file
+ */
+static int
+dos_open(const char *path, struct open_file *fd)
+{
+ DOS_DE *de;
+ DOS_FILE *f;
+ DOS_FS *fs;
+ u_int size, clus;
+ int err;
+
+ /* Allocate mount structure, associate with open */
+ if ((fs = malloc(sizeof(DOS_FS))) == NULL)
+ return (errno);
+ if ((err = dos_mount(fs, fd))) {
+ free(fs);
+ return (err);
+ }
+
+ if ((err = namede(fs, path, &de))) {
+ dos_unmount(fs);
+ return (err);
+ }
+
+ clus = stclus(fs->fatsz, de);
+ size = cv4(de->size);
+
+ if ((!(de->attr & FA_DIR) && (!clus != !size)) ||
+ ((de->attr & FA_DIR) && size) ||
+ (clus && !okclus(fs, clus))) {
+ dos_unmount(fs);
+ return (EINVAL);
+ }
+ if ((f = malloc(sizeof(DOS_FILE))) == NULL) {
+ err = errno;
+ dos_unmount(fs);
+ return (err);
+ }
+ bzero(f, sizeof(DOS_FILE));
+ f->fs = fs;
+ fs->links++;
+ f->de = *de;
+ fd->f_fsdata = (void *)f;
+ return (0);
+}
+
+/*
+ * Read from file
+ */
+static int
+dos_read(struct open_file *fd, void *buf, size_t nbyte, size_t *resid)
+{
+ off_t size;
+ u_int nb, off, clus, c, cnt, n;
+ DOS_FILE *f = (DOS_FILE *)fd->f_fsdata;
+ int err = 0;
+
+ /*
+ * as ioget() can be called *a lot*, use twiddle here.
+ * also 4 seems to be good value not to slow loading down too much:
+ * with 270MB file (~540k ioget() calls, twiddle can easily waste 4-5sec.
+ */
+ twiddle(4);
+ nb = (u_int)nbyte;
+ if ((size = fsize(f->fs, &f->de)) == -1)
+ return (EINVAL);
+ if (nb > (n = size - f->offset))
+ nb = n;
+ off = f->offset;
+ if ((clus = stclus(f->fs->fatsz, &f->de)))
+ off &= f->fs->bsize - 1;
+ c = f->c;
+ cnt = nb;
+ while (cnt) {
+ n = 0;
+ if (!c) {
+ if ((c = clus))
+ n = bytblk(f->fs, f->offset);
+ } else if (!off)
+ n++;
+ while (n--) {
+ if ((err = fatget(f->fs, &c)))
+ goto out;
+ if (!okclus(f->fs, c)) {
+ err = EINVAL;
+ goto out;
+ }
+ }
+ if (!clus || (n = f->fs->bsize - off) > cnt)
+ n = cnt;
+ if ((err = ioread(f->fs, (c ? blkoff(f->fs, c) :
+ secbyt(f->fs->lsndir)) + off, buf, n)))
+ goto out;
+ f->offset += n;
+ f->c = c;
+ off = 0;
+ buf = (char *)buf + n;
+ cnt -= n;
+ }
+ out:
+ if (resid)
+ *resid = nbyte - nb + cnt;
+ return (err);
+}
+
+/*
+ * Reposition within file
+ */
+static off_t
+dos_seek(struct open_file *fd, off_t offset, int whence)
+{
+ off_t off;
+ u_int size;
+ DOS_FILE *f = (DOS_FILE *)fd->f_fsdata;
+
+ size = cv4(f->de.size);
+ switch (whence) {
+ case SEEK_SET:
+ off = 0;
+ break;
+ case SEEK_CUR:
+ off = f->offset;
+ break;
+ case SEEK_END:
+ off = size;
+ break;
+ default:
+ errno = EINVAL;
+ return (-1);
+ }
+ off += offset;
+ if (off < 0 || off > size) {
+ errno = EINVAL;
+ return (-1);
+ }
+ f->offset = (u_int)off;
+ f->c = 0;
+ return (off);
+}
+
+/*
+ * Close open file
+ */
+static int
+dos_close(struct open_file *fd)
+{
+ DOS_FILE *f = (DOS_FILE *)fd->f_fsdata;
+ DOS_FS *fs = f->fs;
+
+ f->fs->links--;
+ free(f);
+ dos_unmount(fs);
+ return (0);
+}
+
+/*
+ * Return some stat information on a file.
+ */
+static int
+dos_stat(struct open_file *fd, struct stat *sb)
+{
+ DOS_FILE *f = (DOS_FILE *)fd->f_fsdata;
+
+ /* only important stuff */
+ sb->st_mode = f->de.attr & FA_DIR ? S_IFDIR | 0555 : S_IFREG | 0444;
+ sb->st_nlink = 1;
+ sb->st_uid = 0;
+ sb->st_gid = 0;
+ if ((sb->st_size = fsize(f->fs, &f->de)) == -1)
+ return (EINVAL);
+ return (0);
+}
+
+static int
+dos_checksum(char *name, char *ext)
+{
+ int x, i;
+ char buf[11];
+
+ bcopy(name, buf, 8);
+ bcopy(ext, buf+8, 3);
+ x = 0;
+ for (i = 0; i < 11; i++) {
+ x = ((x & 1) << 7) | (x >> 1);
+ x += buf[i];
+ x &= 0xff;
+ }
+ return (x);
+}
+
+static int
+dos_readdir(struct open_file *fd, struct dirent *d)
+{
+ /* DOS_FILE *f = (DOS_FILE *)fd->f_fsdata; */
+ u_char fn[261];
+ DOS_DIR dd;
+ size_t res;
+ u_int chk, i, x, xdn;
+ int err;
+
+ x = chk = 0;
+ while (1) {
+ xdn = x;
+ x = 0;
+ err = dos_read(fd, &dd, sizeof(dd), &res);
+ if (err)
+ return (err);
+ if (res == sizeof(dd))
+ return (ENOENT);
+ if (dd.de.name[0] == 0)
+ return (ENOENT);
+
+ /* Skip deleted entries */
+ if (dd.de.name[0] == 0xe5)
+ continue;
+
+ /* Check if directory entry is volume label */
+ if (dd.de.attr & FA_LABEL) {
+ /*
+ * If volume label set, check if the current entry is
+ * extended entry (FA_XDE) for long file names.
+ */
+ if ((dd.de.attr & FA_MASK) == FA_XDE) {
+ /*
+ * Read through all following extended entries
+ * to get the long file name. 0x40 marks the
+ * last entry containing part of long file name.
+ */
+ if (dd.xde.seq & 0x40)
+ chk = dd.xde.chk;
+ else if (dd.xde.seq != xdn - 1 || dd.xde.chk != chk)
+ continue;
+ x = dd.xde.seq & ~0x40;
+ if (x < 1 || x > 20) {
+ x = 0;
+ continue;
+ }
+ cp_xdnm(fn, &dd.xde);
+ } else {
+ /* skip only volume label entries */
+ continue;
+ }
+ } else {
+ if (xdn == 1) {
+ x = dos_checksum(dd.de.name, dd.de.ext);
+ if (x == chk)
+ break;
+ } else {
+ cp_sfn(fn, &dd.de);
+ break;
+ }
+ x = 0;
+ }
+ }
+
+ d->d_fileno = (dd.de.clus[1] << 8) + dd.de.clus[0];
+ d->d_reclen = sizeof(*d);
+ d->d_type = (dd.de.attr & FA_DIR) ? DT_DIR : DT_REG;
+ memcpy(d->d_name, fn, sizeof(d->d_name));
+ return (0);
+}
+
+/*
+ * Parse DOS boot sector
+ */
+static int
+parsebs(DOS_FS *fs, DOS_BS *bs)
+{
+ u_int sc;
+
+ if ((bs->jmp[0] != 0x69 &&
+ bs->jmp[0] != 0xe9 &&
+ (bs->jmp[0] != 0xeb || bs->jmp[2] != 0x90)) ||
+ bs->bpb.media < 0xf0)
+ return (EINVAL);
+ if (cv2(bs->bpb.secsiz) != SECSIZ)
+ return (EINVAL);
+ if (!(fs->spc = bs->bpb.spc) || fs->spc & (fs->spc - 1))
+ return (EINVAL);
+ fs->bsize = secbyt(fs->spc);
+ fs->bshift = ffs(fs->bsize) - 1;
+ if ((fs->spf = cv2(bs->bpb.spf))) {
+ if (bs->bpb.fats != 2)
+ return (EINVAL);
+ if (!(fs->dirents = cv2(bs->bpb.dirents)))
+ return (EINVAL);
+ } else {
+ if (!(fs->spf = cv4(bs->bpb.lspf)))
+ return (EINVAL);
+ if (!bs->bpb.fats || bs->bpb.fats > 16)
+ return (EINVAL);
+ if ((fs->rdcl = cv4(bs->bpb.rdcl)) < LOCLUS)
+ return (EINVAL);
+ }
+ if (!(fs->lsnfat = cv2(bs->bpb.ressec)))
+ return (EINVAL);
+ fs->lsndir = fs->lsnfat + fs->spf * bs->bpb.fats;
+ fs->lsndta = fs->lsndir + entsec(fs->dirents);
+ if (!(sc = cv2(bs->bpb.secs)) && !(sc = cv4(bs->bpb.lsecs)))
+ return (EINVAL);
+ if (fs->lsndta > sc)
+ return (EINVAL);
+ if ((fs->xclus = secblk(fs, sc - fs->lsndta) + 1) < LOCLUS)
+ return (EINVAL);
+ fs->fatsz = fs->dirents ? fs->xclus < 0xff6 ? 12 : 16 : 32;
+ sc = (secbyt(fs->spf) << 1) / (fs->fatsz >> 2) - 1;
+ if (fs->xclus > sc)
+ fs->xclus = sc;
+ return (0);
+}
+
+/*
+ * Return directory entry from path
+ */
+static int
+namede(DOS_FS *fs, const char *path, DOS_DE **dep)
+{
+ char name[256];
+ DOS_DE *de;
+ char *s;
+ size_t n;
+ int err;
+
+ err = 0;
+ de = &fs->root;
+ while (*path) {
+ while (*path == '/')
+ path++;
+ if (*path == '\0')
+ break;
+ if (!(s = strchr(path, '/')))
+ s = strchr(path, 0);
+ if ((n = s - path) > 255)
+ return (ENAMETOOLONG);
+ memcpy(name, path, n);
+ name[n] = 0;
+ path = s;
+ if (!(de->attr & FA_DIR))
+ return (ENOTDIR);
+ if ((err = lookup(fs, stclus(fs->fatsz, de), name, &de)))
+ return (err);
+ }
+ *dep = de;
+ return (0);
+}
+
+/*
+ * Lookup path segment
+ */
+static int
+lookup(DOS_FS *fs, u_int clus, const char *name, DOS_DE **dep)
+{
+ static DOS_DIR dir[DEPSEC];
+ u_char lfn[261];
+ u_char sfn[13];
+ u_int nsec, lsec, xdn, chk, sec, ent, x;
+ int err, ok, i;
+
+ if (!clus)
+ for (ent = 0; ent < 2; ent++)
+ if (!strcasecmp(name, dotstr[ent])) {
+ *dep = dot + ent;
+ return (0);
+ }
+ if (!clus && fs->fatsz == 32)
+ clus = fs->rdcl;
+ nsec = !clus ? entsec(fs->dirents) : fs->spc;
+ lsec = 0;
+ xdn = chk = 0;
+ for (;;) {
+ if (!clus && !lsec)
+ lsec = fs->lsndir;
+ else if (okclus(fs, clus))
+ lsec = blklsn(fs, clus);
+ else
+ return (EINVAL);
+ for (sec = 0; sec < nsec; sec++) {
+ if ((err = ioget(fs->fd, lsec + sec, dir, secbyt(1))))
+ return (err);
+ for (ent = 0; ent < DEPSEC; ent++) {
+ if (!*dir[ent].de.name)
+ return (ENOENT);
+ if (*dir[ent].de.name != 0xe5) {
+ if ((dir[ent].de.attr & FA_MASK) == FA_XDE) {
+ x = dir[ent].xde.seq;
+ if (x & 0x40 || (x + 1 == xdn &&
+ dir[ent].xde.chk == chk)) {
+ if (x & 0x40) {
+ chk = dir[ent].xde.chk;
+ x &= ~0x40;
+ }
+ if (x >= 1 && x <= 20) {
+ cp_xdnm(lfn, &dir[ent].xde);
+ xdn = x;
+ continue;
+ }
+ }
+ } else if (!(dir[ent].de.attr & FA_LABEL)) {
+ if ((ok = xdn == 1)) {
+ x = dos_checksum(dir[ent].de.name, dir[ent].de.ext);
+ ok = chk == x &&
+ !strcasecmp(name, (const char *)lfn);
+ }
+ if (!ok) {
+ cp_sfn(sfn, &dir[ent].de);
+ ok = !strcasecmp(name, (const char *)sfn);
+ }
+ if (ok) {
+ *dep = &dir[ent].de;
+ return (0);
+ }
+ }
+ }
+ xdn = 0;
+ }
+ }
+ if (!clus)
+ break;
+ if ((err = fatget(fs, &clus)))
+ return (err);
+ if (fatend(fs->fatsz, clus))
+ break;
+ }
+ return (ENOENT);
+}
+
+/*
+ * Copy name from extended directory entry
+ */
+static void
+cp_xdnm(u_char *lfn, DOS_XDE *xde)
+{
+ static struct {
+ u_int off;
+ u_int dim;
+ } ix[3] = {
+ {offsetof(DOS_XDE, name1), sizeof(xde->name1) / 2},
+ {offsetof(DOS_XDE, name2), sizeof(xde->name2) / 2},
+ {offsetof(DOS_XDE, name3), sizeof(xde->name3) / 2}
+ };
+ u_char *p;
+ u_int n, x, c;
+
+ lfn += 13 * ((xde->seq & ~0x40) - 1);
+ for (n = 0; n < 3; n++)
+ for (p = (u_char *)xde + ix[n].off, x = ix[n].dim; x;
+ p += 2, x--) {
+ if ((c = cv2(p)) && (c < 32 || c > 127))
+ c = '?';
+ if (!(*lfn++ = c))
+ return;
+ }
+ if (xde->seq & 0x40)
+ *lfn = 0;
+}
+
+/*
+ * Copy short filename
+ */
+static void
+cp_sfn(u_char *sfn, DOS_DE *de)
+{
+ u_char *p;
+ int j, i;
+
+ p = sfn;
+ if (*de->name != ' ') {
+ for (j = 7; de->name[j] == ' '; j--);
+ for (i = 0; i <= j; i++)
+ *p++ = de->name[i];
+ if (*de->ext != ' ') {
+ *p++ = '.';
+ for (j = 2; de->ext[j] == ' '; j--);
+ for (i = 0; i <= j; i++)
+ *p++ = de->ext[i];
+ }
+ }
+ *p = 0;
+ if (*sfn == 5)
+ *sfn = 0xe5;
+}
+
+/*
+ * Return size of file in bytes
+ */
+static off_t
+fsize(DOS_FS *fs, DOS_DE *de)
+{
+ u_long size;
+ u_int c;
+ int n;
+
+ if (!(size = cv4(de->size)) && de->attr & FA_DIR) {
+ if (!(c = cv2(de->clus)))
+ size = fs->dirents * sizeof(DOS_DE);
+ else {
+ if ((n = fatcnt(fs, c)) == -1)
+ return (n);
+ size = blkbyt(fs, n);
+ }
+ }
+ return (size);
+}
+
+/*
+ * Count number of clusters in chain
+ */
+static int
+fatcnt(DOS_FS *fs, u_int c)
+{
+ int n;
+
+ for (n = 0; okclus(fs, c); n++)
+ if (fatget(fs, &c))
+ return (-1);
+ return (fatend(fs->fatsz, c) ? n : -1);
+}
+
+/*
+ * Get next cluster in cluster chain. Use in core fat cache unless
+ * the number of current 128K block in FAT has changed.
+ */
+static int
+fatget(DOS_FS *fs, u_int *c)
+{
+ u_int val_in, val_out, offset, blknum, nbyte;
+ const u_char *p_entry;
+ int err;
+
+ /* check input value to prevent overflow in fatoff() */
+ val_in = *c;
+ if (val_in & 0xf0000000)
+ return (EINVAL);
+
+ /* ensure that current 128K FAT block is cached */
+ offset = fatoff(fs->fatsz, val_in);
+ nbyte = fs->fatsz != 32 ? 2 : 4;
+ if (offset + nbyte > secbyt(fs->spf))
+ return (EINVAL);
+ blknum = offset / FATBLKSZ;
+ offset %= FATBLKSZ;
+ if (offset + nbyte > FATBLKSZ)
+ return (EINVAL);
+ if (blknum != fs->fatbuf_blknum) {
+ err = dos_read_fatblk(fs, fs->fd, blknum);
+ if (err != 0)
+ return (err);
+ }
+ p_entry = fs->fatbuf + offset;
+
+ /* extract cluster number from FAT entry */
+ switch (fs->fatsz) {
+ case 32:
+ val_out = cv4(p_entry);
+ val_out &= 0x0fffffff;
+ break;
+ case 16:
+ val_out = cv2(p_entry);
+ break;
+ case 12:
+ val_out = cv2(p_entry);
+ if (val_in & 1)
+ val_out >>= 4;
+ else
+ val_out &= 0xfff;
+ break;
+ default:
+ return (EINVAL);
+ }
+ *c = val_out;
+ return (0);
+}
+
+/*
+ * Is cluster an end-of-chain marker?
+ */
+static int
+fatend(u_int sz, u_int c)
+{
+ return (c > (sz == 12 ? 0xff7U : sz == 16 ? 0xfff7U : 0xffffff7));
+}
+
+/*
+ * Offset-based I/O primitive
+ */
+static int
+ioread(DOS_FS *fs, u_int offset, void *buf, size_t nbyte)
+{
+ char *s;
+ u_int off, n;
+ int err;
+ u_char local_buf[SECSIZ];
+
+ s = buf;
+ if ((off = offset & (SECSIZ - 1))) {
+ offset -= off;
+ if ((n = SECSIZ - off) > nbyte)
+ n = nbyte;
+ if ((err = ioget(fs->fd, bytsec(offset), local_buf, sizeof(local_buf))))
+ return (err);
+ memcpy(s, local_buf + off, n);
+ offset += SECSIZ;
+ s += n;
+ nbyte -= n;
+ }
+ n = nbyte & (SECSIZ - 1);
+ if (nbyte -= n) {
+ if ((err = ioget(fs->fd, bytsec(offset), s, nbyte)))
+ return (err);
+ offset += nbyte;
+ s += nbyte;
+ }
+ if (n) {
+ if ((err = ioget(fs->fd, bytsec(offset), local_buf, sizeof(local_buf))))
+ return (err);
+ memcpy(s, local_buf, n);
+ }
+ return (0);
+}
+
+/*
+ * Sector-based I/O primitive
+ */
+static int
+ioget(struct open_file *fd, daddr_t lsec, void *buf, size_t size)
+{
+ size_t rsize;
+ int rv;
+
+ /* Make sure we get full read or error. */
+ rsize = 0;
+ rv = (fd->f_dev->dv_strategy)(fd->f_devdata, F_READ, lsec,
+ size, buf, &rsize);
+ if ((rv == 0) && (size != rsize))
+ rv = EIO;
+ return (rv);
+}
diff --git a/stand/libsa/dosfs.h b/stand/libsa/dosfs.h
new file mode 100644
index 0000000..5b11e0f
--- /dev/null
+++ b/stand/libsa/dosfs.h
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 1996, 1998 Robert Nordier
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef DOSIO_H
+#define DOSIO_H
+
+/*
+ * DOS file attributes
+ */
+
+#define FA_RDONLY 001 /* read-only */
+#define FA_HIDDEN 002 /* hidden file */
+#define FA_SYSTEM 004 /* system file */
+#define FA_LABEL 010 /* volume label */
+#define FA_DIR 020 /* directory */
+#define FA_ARCH 040 /* archive (file modified) */
+#define FA_XDE 017 /* extended directory entry */
+#define FA_MASK 077 /* all attributes */
+
+/*
+ * Macros to convert DOS-format 16-bit and 32-bit quantities
+ */
+
+#define cv2(p) ((u_int16_t)(p)[0] | \
+ ((u_int16_t)(p)[1] << 010))
+#define cv4(p) ((u_int32_t)(p)[0] | \
+ ((u_int32_t)(p)[1] << 010) | \
+ ((u_int32_t)(p)[2] << 020) | \
+ ((u_int32_t)(p)[3] << 030))
+
+/*
+ * Directory, filesystem, and file structures.
+ */
+
+typedef struct {
+ u_char x_case; /* case */
+ u_char c_hsec; /* created: secs/100 */
+ u_char c_time[2]; /* created: time */
+ u_char c_date[2]; /* created: date */
+ u_char a_date[2]; /* accessed: date */
+ u_char h_clus[2]; /* clus[hi] */
+} DOS_DEX;
+
+typedef struct {
+ u_char name[8]; /* name */
+ u_char ext[3]; /* extension */
+ u_char attr; /* attributes */
+ DOS_DEX dex; /* VFAT/FAT32 only */
+ u_char time[2]; /* modified: time */
+ u_char date[2]; /* modified: date */
+ u_char clus[2]; /* starting cluster */
+ u_char size[4]; /* size */
+} DOS_DE;
+
+typedef struct {
+ u_char seq; /* flags */
+ u_char name1[5][2]; /* 1st name area */
+ u_char attr; /* (see fat_de) */
+ u_char res; /* reserved */
+ u_char chk; /* checksum */
+ u_char name2[6][2]; /* 2nd name area */
+ u_char clus[2]; /* (see fat_de) */
+ u_char name3[2][2]; /* 3rd name area */
+} DOS_XDE;
+
+typedef union {
+ DOS_DE de; /* standard directory entry */
+ DOS_XDE xde; /* extended directory entry */
+} DOS_DIR;
+
+typedef struct {
+ struct open_file *fd; /* file descriptor */
+ u_char *fatbuf; /* FAT cache buffer */
+ u_int fatbuf_blknum; /* number of 128K block in FAT cache buffer */
+ u_int links; /* active links to structure */
+ u_int spc; /* sectors per cluster */
+ u_int bsize; /* cluster size in bytes */
+ u_int bshift; /* cluster conversion shift */
+ u_int dirents; /* root directory entries */
+ u_int spf; /* sectors per fat */
+ u_int rdcl; /* root directory start cluster */
+ u_int lsnfat; /* start of fat */
+ u_int lsndir; /* start of root dir */
+ u_int lsndta; /* start of data area */
+ u_int fatsz; /* FAT entry size */
+ u_int xclus; /* maximum cluster number */
+ DOS_DE root;
+} DOS_FS;
+
+typedef struct {
+ DOS_FS *fs; /* associated filesystem */
+ DOS_DE de; /* directory entry */
+ u_int offset; /* current offset */
+ u_int c; /* last cluster read */
+} DOS_FILE;
+
+#endif /* !DOSIO_H */
diff --git a/stand/libsa/environment.c b/stand/libsa/environment.c
new file mode 100644
index 0000000..83349f1
--- /dev/null
+++ b/stand/libsa/environment.c
@@ -0,0 +1,223 @@
+/*
+ * Copyright (c) 1998 Michael Smith.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Manage an environment-like space in which string variables may be stored.
+ * Provide support for some method-like operations for setting/retrieving
+ * variables in order to allow some type strength.
+ */
+
+#include "stand.h"
+
+#include <string.h>
+
+static void env_discard(struct env_var *ev);
+
+struct env_var *environ = NULL;
+
+/*
+ * Look up (name) and return it's env_var structure.
+ */
+struct env_var *
+env_getenv(const char *name)
+{
+ struct env_var *ev;
+
+ for (ev = environ; ev != NULL; ev = ev->ev_next)
+ if (!strcmp(ev->ev_name, name))
+ break;
+ return(ev);
+}
+
+/*
+ * Some notes:
+ *
+ * If the EV_VOLATILE flag is set, a copy of the variable is made.
+ * If EV_DYNAMIC is set, the variable has been allocated with
+ * malloc and ownership transferred to the environment.
+ * If (value) is NULL, the variable is set but has no value.
+ */
+int
+env_setenv(const char *name, int flags, const void *value,
+ ev_sethook_t sethook, ev_unsethook_t unsethook)
+{
+ struct env_var *ev, *curr, *last;
+
+ if ((ev = env_getenv(name)) != NULL) {
+ /*
+ * If there's a set hook, let it do the work (unless we are working
+ * for one already.
+ */
+ if ((ev->ev_sethook != NULL) && !(flags & EV_NOHOOK))
+ return (ev->ev_sethook(ev, flags, value));
+
+ /* If there is data in the variable, discard it. */
+ if (ev->ev_value != NULL && (ev->ev_flags & EV_DYNAMIC) != 0)
+ free(ev->ev_value);
+ ev->ev_value = NULL;
+ ev->ev_flags &= ~EV_DYNAMIC;
+
+ } else {
+
+ /*
+ * New variable; create and sort into list
+ */
+ ev = malloc(sizeof(struct env_var));
+ ev->ev_name = strdup(name);
+ ev->ev_value = NULL;
+ ev->ev_flags = 0;
+ /* hooks can only be set when the variable is instantiated */
+ ev->ev_sethook = sethook;
+ ev->ev_unsethook = unsethook;
+
+ /* Sort into list */
+ ev->ev_prev = NULL;
+ ev->ev_next = NULL;
+ /* Search for the record to insert before */
+ for (last = NULL, curr = environ;
+ curr != NULL;
+ last = curr, curr = curr->ev_next) {
+
+ if (strcmp(ev->ev_name, curr->ev_name) < 0) {
+ if (curr->ev_prev) {
+ curr->ev_prev->ev_next = ev;
+ } else {
+ environ = ev;
+ }
+ ev->ev_next = curr;
+ ev->ev_prev = curr->ev_prev;
+ curr->ev_prev = ev;
+ break;
+ }
+ }
+ if (curr == NULL) {
+ if (last == NULL) {
+ environ = ev;
+ } else {
+ last->ev_next = ev;
+ ev->ev_prev = last;
+ }
+ }
+ }
+
+ /* If we have a new value, use it */
+ if (flags & EV_VOLATILE) {
+ ev->ev_value = strdup(value);
+ ev->ev_flags |= EV_DYNAMIC;
+ } else {
+ ev->ev_value = (char *)value;
+ ev->ev_flags |= flags & EV_DYNAMIC;
+ }
+
+ return(0);
+}
+
+char *
+getenv(const char *name)
+{
+ struct env_var *ev;
+
+ /* Set but no value gives empty string */
+ if ((ev = env_getenv(name)) != NULL) {
+ if (ev->ev_value != NULL)
+ return(ev->ev_value);
+ return("");
+ }
+ return(NULL);
+}
+
+int
+setenv(const char *name, const char *value, int overwrite)
+{
+ /* No guarantees about state, always assume volatile */
+ if (overwrite || (env_getenv(name) == NULL))
+ return(env_setenv(name, EV_VOLATILE, value, NULL, NULL));
+ return(0);
+}
+
+int
+putenv(char *string)
+{
+ char *value, *copy;
+ int result;
+
+ copy = strdup(string);
+ if ((value = strchr(copy, '=')) != NULL)
+ *(value++) = 0;
+ result = setenv(copy, value, 1);
+ free(copy);
+ return(result);
+}
+
+int
+unsetenv(const char *name)
+{
+ struct env_var *ev;
+ int err;
+
+ err = 0;
+ if ((ev = env_getenv(name)) == NULL) {
+ err = ENOENT;
+ } else {
+ if (ev->ev_unsethook != NULL)
+ err = ev->ev_unsethook(ev);
+ if (err == 0) {
+ env_discard(ev);
+ }
+ }
+ return(err);
+}
+
+static void
+env_discard(struct env_var *ev)
+{
+ if (ev->ev_prev)
+ ev->ev_prev->ev_next = ev->ev_next;
+ if (ev->ev_next)
+ ev->ev_next->ev_prev = ev->ev_prev;
+ if (environ == ev)
+ environ = ev->ev_next;
+ free(ev->ev_name);
+ if (ev->ev_value != NULL && (ev->ev_flags & EV_DYNAMIC) != 0)
+ free(ev->ev_value);
+ free(ev);
+}
+
+int
+env_noset(struct env_var *ev __unused, int flags __unused,
+ const void *value __unused)
+{
+ return(EPERM);
+}
+
+int
+env_nounset(struct env_var *ev __unused)
+{
+ return(EPERM);
+}
diff --git a/stand/libsa/ether.c b/stand/libsa/ether.c
new file mode 100644
index 0000000..7ab9d74
--- /dev/null
+++ b/stand/libsa/ether.c
@@ -0,0 +1,147 @@
+/* $NetBSD: ether.c,v 1.11 1997/07/07 15:52:50 drochner Exp $ */
+
+/*
+ * Copyright (c) 1992 Regents of the University of California.
+ * All rights reserved.
+ *
+ * This software was developed by the Computer Systems Engineering group
+ * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
+ * contributed to Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#) Header: net.c,v 1.9 93/08/06 19:32:15 leres Exp (LBL)
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <string.h>
+
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+
+#include "stand.h"
+#include "net.h"
+#include "netif.h"
+
+/* Caller must leave room for ethernet header in front!! */
+ssize_t
+sendether(struct iodesc *d, void *pkt, size_t len, uint8_t *dea, int etype)
+{
+ ssize_t n;
+ struct ether_header *eh;
+
+#ifdef ETHER_DEBUG
+ if (debug)
+ printf("sendether: called\n");
+#endif
+
+ eh = (struct ether_header *)pkt - 1;
+ len += sizeof(*eh);
+
+ MACPY(d->myea, eh->ether_shost); /* by byte */
+ MACPY(dea, eh->ether_dhost); /* by byte */
+ eh->ether_type = htons(etype);
+
+ n = netif_put(d, eh, len);
+ if (n == -1 || n < sizeof(*eh))
+ return (-1);
+
+ n -= sizeof(*eh);
+ return (n);
+}
+
+/*
+ * Get a packet of any Ethernet type, with our address or
+ * the broadcast address. Save the Ether type in etype.
+ * Unless there is an error, we pass the whole packet and the unencapsulated
+ * data.
+ */
+ssize_t
+readether(struct iodesc *d, void **pkt, void **payload, time_t tleft,
+ uint16_t *etype)
+{
+ ssize_t n;
+ struct ether_header *eh;
+ void *ptr;
+
+#ifdef ETHER_DEBUG
+ if (debug)
+ printf("readether: called\n");
+#endif
+
+ ptr = NULL;
+ n = netif_get(d, &ptr, tleft);
+ if (n == -1 || n < sizeof(*eh)) {
+ free(ptr);
+ return (-1);
+ }
+
+ eh = (struct ether_header *)((uintptr_t)ptr + ETHER_ALIGN);
+ /* Validate Ethernet address. */
+ if (bcmp(d->myea, eh->ether_dhost, 6) != 0 &&
+ bcmp(bcea, eh->ether_dhost, 6) != 0) {
+#ifdef ETHER_DEBUG
+ if (debug)
+ printf("readether: not ours (ea=%s)\n",
+ ether_sprintf(eh->ether_dhost));
+#endif
+ free(ptr);
+ return (-1);
+ }
+
+ *pkt = ptr;
+ *payload = (void *)((uintptr_t)eh + sizeof(*eh));
+ *etype = ntohs(eh->ether_type);
+
+ n -= sizeof(*eh);
+ return (n);
+}
+
+/*
+ * Convert Ethernet address to printable (loggable) representation.
+ */
+static char digits[] = "0123456789abcdef";
+char *
+ether_sprintf(u_char *ap)
+{
+ int i;
+ static char etherbuf[18];
+ char *cp = etherbuf;
+
+ for (i = 0; i < 6; i++) {
+ *cp++ = digits[*ap >> 4];
+ *cp++ = digits[*ap++ & 0xf];
+ *cp++ = ':';
+ }
+ *--cp = 0;
+ return (etherbuf);
+}
diff --git a/stand/libsa/ext2fs.c b/stand/libsa/ext2fs.c
new file mode 100644
index 0000000..d0b91e0
--- /dev/null
+++ b/stand/libsa/ext2fs.c
@@ -0,0 +1,908 @@
+/*-
+ * Copyright (c) 1999,2000 Jonathan Lemon <jlemon@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*-
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * The Mach Operating System project at Carnegie-Mellon University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *
+ * Copyright (c) 1990, 1991 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Author: David Golub
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie the
+ * rights to redistribute these changes.
+ */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include "stand.h"
+#include "string.h"
+
+static int ext2fs_open(const char *path, struct open_file *f);
+static int ext2fs_close(struct open_file *f);
+static int ext2fs_read(struct open_file *f, void *buf,
+ size_t size, size_t *resid);
+static off_t ext2fs_seek(struct open_file *f, off_t offset, int where);
+static int ext2fs_stat(struct open_file *f, struct stat *sb);
+static int ext2fs_readdir(struct open_file *f, struct dirent *d);
+
+static int dtmap[] = { DT_UNKNOWN, DT_REG, DT_DIR, DT_CHR,
+ DT_BLK, DT_FIFO, DT_SOCK, DT_LNK };
+#define EXTFTODT(x) (x) > sizeof(dtmap) / sizeof(dtmap[0]) ? \
+ DT_UNKNOWN : dtmap[x]
+
+struct fs_ops ext2fs_fsops = {
+ "ext2fs",
+ ext2fs_open,
+ ext2fs_close,
+ ext2fs_read,
+ null_write,
+ ext2fs_seek,
+ ext2fs_stat,
+ ext2fs_readdir
+};
+
+#define EXT2_SBSIZE 1024
+#define EXT2_SBLOCK (1024 / DEV_BSIZE) /* block offset of superblock */
+#define EXT2_MAGIC 0xef53
+#define EXT2_ROOTINO 2
+
+#define EXT2_REV0 0 /* original revision of ext2 */
+#define EXT2_R0_ISIZE 128 /* inode size */
+#define EXT2_R0_FIRSTINO 11 /* first inode */
+
+#define EXT2_MINBSHIFT 10 /* mininum block shift */
+#define EXT2_MINFSHIFT 10 /* mininum frag shift */
+
+#define NDADDR 12 /* # of direct blocks */
+#define NIADDR 3 /* # of indirect blocks */
+
+/*
+ * file system block to disk address
+ */
+#define fsb_to_db(fs, blk) ((blk) << (fs)->fs_fsbtodb)
+
+/*
+ * inode to block group offset
+ * inode to block group
+ * inode to disk address
+ * inode to block offset
+ */
+#define ino_to_bgo(fs, ino) (((ino) - 1) % (fs)->fs_ipg)
+#define ino_to_bg(fs, ino) (((ino) - 1) / (fs)->fs_ipg)
+#define ino_to_db(fs, bg, ino) \
+ fsb_to_db(fs, ((bg)[ino_to_bg(fs, ino)].bg_inotbl + \
+ ino_to_bgo(fs, ino) / (fs)->fs_ipb))
+#define ino_to_bo(fs, ino) (ino_to_bgo(fs, ino) % (fs)->fs_ipb)
+
+#define nindir(fs) \
+ ((fs)->fs_bsize / sizeof(u_int32_t))
+#define lblkno(fs, loc) /* loc / bsize */ \
+ ((loc) >> (fs)->fs_bshift)
+#define smalllblktosize(fs, blk) /* blk * bsize */ \
+ ((blk) << (fs)->fs_bshift)
+#define blkoff(fs, loc) /* loc % bsize */ \
+ ((loc) & (fs)->fs_bmask)
+#define fragroundup(fs, size) /* roundup(size, fsize) */ \
+ (((size) + (fs)->fs_fmask) & ~(fs)->fs_fmask)
+#define dblksize(fs, dip, lbn) \
+ (((lbn) >= NDADDR || (dip)->di_size >= smalllblktosize(fs, (lbn) + 1)) \
+ ? (fs)->fs_bsize \
+ : (fragroundup(fs, blkoff(fs, (dip)->di_size))))
+
+/*
+ * superblock describing ext2fs
+ */
+struct ext2fs_disk {
+ u_int32_t fd_inodes; /* # of inodes */
+ u_int32_t fd_blocks; /* # of blocks */
+ u_int32_t fd_resblk; /* # of reserved blocks */
+ u_int32_t fd_freeblk; /* # of free blocks */
+ u_int32_t fd_freeino; /* # of free inodes */
+ u_int32_t fd_firstblk; /* first data block */
+ u_int32_t fd_bsize; /* block size */
+ u_int32_t fd_fsize; /* frag size */
+ u_int32_t fd_bpg; /* blocks per group */
+ u_int32_t fd_fpg; /* frags per group */
+ u_int32_t fd_ipg; /* inodes per group */
+ u_int32_t fd_mtime; /* mount time */
+ u_int32_t fd_wtime; /* write time */
+ u_int16_t fd_mount; /* # of mounts */
+ int16_t fd_maxmount; /* max # of mounts */
+ u_int16_t fd_magic; /* magic number */
+ u_int16_t fd_state; /* state */
+ u_int16_t fd_eflag; /* error flags */
+ u_int16_t fd_mnrrev; /* minor revision */
+ u_int32_t fd_lastchk; /* last check */
+ u_int32_t fd_chkintvl; /* maximum check interval */
+ u_int32_t fd_os; /* os */
+ u_int32_t fd_revision; /* revision */
+ u_int16_t fd_uid; /* uid for reserved blocks */
+ u_int16_t fd_gid; /* gid for reserved blocks */
+
+ u_int32_t fd_firstino; /* first non-reserved inode */
+ u_int16_t fd_isize; /* inode size */
+ u_int16_t fd_nblkgrp; /* block group # of superblock */
+ u_int32_t fd_fcompat; /* compatible features */
+ u_int32_t fd_fincompat; /* incompatible features */
+ u_int32_t fd_frocompat; /* read-only compatibilties */
+ u_int8_t fd_uuid[16]; /* volume uuid */
+ char fd_volname[16]; /* volume name */
+ char fd_fsmnt[64]; /* name last mounted on */
+ u_int32_t fd_bitmap; /* compression bitmap */
+
+ u_int8_t fd_nblkpa; /* # of blocks to preallocate */
+ u_int8_t fd_ndblkpa; /* # of dir blocks to preallocate */
+};
+
+struct ext2fs_core {
+ int fc_bsize; /* block size */
+ int fc_bshift; /* block shift amount */
+ int fc_bmask; /* block mask */
+ int fc_fsize; /* frag size */
+ int fc_fshift; /* frag shift amount */
+ int fc_fmask; /* frag mask */
+ int fc_isize; /* inode size */
+ int fc_imask; /* inode mask */
+ int fc_firstino; /* first non-reserved inode */
+ int fc_ipb; /* inodes per block */
+ int fc_fsbtodb; /* fsb to ds shift */
+};
+
+struct ext2fs {
+ struct ext2fs_disk fs_fd;
+ char fs_pad[EXT2_SBSIZE - sizeof(struct ext2fs_disk)];
+ struct ext2fs_core fs_fc;
+
+#define fs_magic fs_fd.fd_magic
+#define fs_revision fs_fd.fd_revision
+#define fs_blocks fs_fd.fd_blocks
+#define fs_firstblk fs_fd.fd_firstblk
+#define fs_bpg fs_fd.fd_bpg
+#define fs_ipg fs_fd.fd_ipg
+
+#define fs_bsize fs_fc.fc_bsize
+#define fs_bshift fs_fc.fc_bshift
+#define fs_bmask fs_fc.fc_bmask
+#define fs_fsize fs_fc.fc_fsize
+#define fs_fshift fs_fc.fc_fshift
+#define fs_fmask fs_fc.fc_fmask
+#define fs_isize fs_fc.fc_isize
+#define fs_imask fs_fc.fc_imask
+#define fs_firstino fs_fc.fc_firstino
+#define fs_ipb fs_fc.fc_ipb
+#define fs_fsbtodb fs_fc.fc_fsbtodb
+};
+
+struct ext2blkgrp {
+ u_int32_t bg_blkmap; /* block bitmap */
+ u_int32_t bg_inomap; /* inode bitmap */
+ u_int32_t bg_inotbl; /* inode table */
+ u_int16_t bg_nfblk; /* # of free blocks */
+ u_int16_t bg_nfino; /* # of free inodes */
+ u_int16_t bg_ndirs; /* # of dirs */
+ char bg_pad[14];
+};
+
+struct ext2dinode {
+ u_int16_t di_mode; /* mode */
+ u_int16_t di_uid; /* uid */
+ u_int32_t di_size; /* byte size */
+ u_int32_t di_atime; /* access time */
+ u_int32_t di_ctime; /* creation time */
+ u_int32_t di_mtime; /* modification time */
+ u_int32_t di_dtime; /* deletion time */
+ u_int16_t di_gid; /* gid */
+ u_int16_t di_nlink; /* link count */
+ u_int32_t di_nblk; /* block count */
+ u_int32_t di_flags; /* file flags */
+
+ u_int32_t di_osdep1; /* os dependent stuff */
+
+ u_int32_t di_db[NDADDR]; /* direct blocks */
+ u_int32_t di_ib[NIADDR]; /* indirect blocks */
+ u_int32_t di_version; /* version */
+ u_int32_t di_facl; /* file acl */
+ u_int32_t di_dacl; /* dir acl */
+ u_int32_t di_faddr; /* fragment addr */
+
+ u_int8_t di_frag; /* fragment number */
+ u_int8_t di_fsize; /* fragment size */
+
+ char di_pad[10];
+
+#define di_shortlink di_db
+};
+
+#define EXT2_MAXNAMLEN 255
+
+struct ext2dirent {
+ u_int32_t d_ino; /* inode */
+ u_int16_t d_reclen; /* directory entry length */
+ u_int8_t d_namlen; /* name length */
+ u_int8_t d_type; /* file type */
+ char d_name[EXT2_MAXNAMLEN];
+};
+
+struct file {
+ off_t f_seekp; /* seek pointer */
+ struct ext2fs *f_fs; /* pointer to super-block */
+ struct ext2blkgrp *f_bg; /* pointer to blkgrp map */
+ struct ext2dinode f_di; /* copy of on-disk inode */
+ int f_nindir[NIADDR]; /* number of blocks mapped by
+ indirect block at level i */
+ char *f_blk[NIADDR]; /* buffer for indirect block
+ at level i */
+ size_t f_blksize[NIADDR]; /* size of buffer */
+ daddr_t f_blkno[NIADDR]; /* disk address of block in
+ buffer */
+ char *f_buf; /* buffer for data block */
+ size_t f_buf_size; /* size of data block */
+ daddr_t f_buf_blkno; /* block number of data block */
+};
+
+/* forward decls */
+static int read_inode(ino_t inumber, struct open_file *f);
+static int block_map(struct open_file *f, daddr_t file_block,
+ daddr_t *disk_block_p);
+static int buf_read_file(struct open_file *f, char **buf_p,
+ size_t *size_p);
+static int search_directory(char *name, struct open_file *f,
+ ino_t *inumber_p);
+
+/*
+ * Open a file.
+ */
+static int
+ext2fs_open(const char *upath, struct open_file *f)
+{
+ struct file *fp;
+ struct ext2fs *fs;
+ size_t buf_size;
+ ino_t inumber, parent_inumber;
+ int i, len, groups, bg_per_blk, blkgrps, mult;
+ int nlinks = 0;
+ int error = 0;
+ char *cp, *ncp, *path = NULL, *buf = NULL;
+ char namebuf[MAXPATHLEN+1];
+ char c;
+
+ /* allocate file system specific data structure */
+ fp = malloc(sizeof(struct file));
+ if (fp == NULL)
+ return (ENOMEM);
+ bzero(fp, sizeof(struct file));
+ f->f_fsdata = (void *)fp;
+
+ /* allocate space and read super block */
+ fs = (struct ext2fs *)malloc(sizeof(*fs));
+ fp->f_fs = fs;
+ twiddle(1);
+ error = (f->f_dev->dv_strategy)(f->f_devdata, F_READ,
+ EXT2_SBLOCK, EXT2_SBSIZE, (char *)fs, &buf_size);
+ if (error)
+ goto out;
+
+ if (buf_size != EXT2_SBSIZE || fs->fs_magic != EXT2_MAGIC) {
+ error = EINVAL;
+ goto out;
+ }
+
+ /*
+ * compute in-core values for the superblock
+ */
+ fs->fs_bshift = EXT2_MINBSHIFT + fs->fs_fd.fd_bsize;
+ fs->fs_bsize = 1 << fs->fs_bshift;
+ fs->fs_bmask = fs->fs_bsize - 1;
+
+ fs->fs_fshift = EXT2_MINFSHIFT + fs->fs_fd.fd_fsize;
+ fs->fs_fsize = 1 << fs->fs_fshift;
+ fs->fs_fmask = fs->fs_fsize - 1;
+
+ if (fs->fs_revision == EXT2_REV0) {
+ fs->fs_isize = EXT2_R0_ISIZE;
+ fs->fs_firstino = EXT2_R0_FIRSTINO;
+ } else {
+ fs->fs_isize = fs->fs_fd.fd_isize;
+ fs->fs_firstino = fs->fs_fd.fd_firstino;
+ }
+ fs->fs_imask = fs->fs_isize - 1;
+ fs->fs_ipb = fs->fs_bsize / fs->fs_isize;
+ fs->fs_fsbtodb = (fs->fs_bsize / DEV_BSIZE) - 1;
+
+ /*
+ * we have to load in the "group descriptors" here
+ */
+ groups = howmany(fs->fs_blocks - fs->fs_firstblk, fs->fs_bpg);
+ bg_per_blk = fs->fs_bsize / sizeof(struct ext2blkgrp);
+ blkgrps = howmany(groups, bg_per_blk);
+ len = blkgrps * fs->fs_bsize;
+
+ fp->f_bg = malloc(len);
+ twiddle(1);
+ error = (f->f_dev->dv_strategy)(f->f_devdata, F_READ,
+ EXT2_SBLOCK + EXT2_SBSIZE / DEV_BSIZE, len,
+ (char *)fp->f_bg, &buf_size);
+ if (error)
+ goto out;
+
+ /*
+ * XXX
+ * validation of values? (blocksize, descriptors, etc?)
+ */
+
+ /*
+ * Calculate indirect block levels.
+ */
+ mult = 1;
+ for (i = 0; i < NIADDR; i++) {
+ mult *= nindir(fs);
+ fp->f_nindir[i] = mult;
+ }
+
+ inumber = EXT2_ROOTINO;
+ if ((error = read_inode(inumber, f)) != 0)
+ goto out;
+
+ path = strdup(upath);
+ if (path == NULL) {
+ error = ENOMEM;
+ goto out;
+ }
+ cp = path;
+ while (*cp) {
+ /*
+ * Remove extra separators
+ */
+ while (*cp == '/')
+ cp++;
+ if (*cp == '\0')
+ break;
+
+ /*
+ * Check that current node is a directory.
+ */
+ if (! S_ISDIR(fp->f_di.di_mode)) {
+ error = ENOTDIR;
+ goto out;
+ }
+
+ /*
+ * Get next component of path name.
+ */
+ len = 0;
+
+ ncp = cp;
+ while ((c = *cp) != '\0' && c != '/') {
+ if (++len > EXT2_MAXNAMLEN) {
+ error = ENOENT;
+ goto out;
+ }
+ cp++;
+ }
+ *cp = '\0';
+
+ /*
+ * Look up component in current directory.
+ * Save directory inumber in case we find a
+ * symbolic link.
+ */
+ parent_inumber = inumber;
+ error = search_directory(ncp, f, &inumber);
+ *cp = c;
+ if (error)
+ goto out;
+
+ /*
+ * Open next component.
+ */
+ if ((error = read_inode(inumber, f)) != 0)
+ goto out;
+
+ /*
+ * Check for symbolic link.
+ */
+ if (S_ISLNK(fp->f_di.di_mode)) {
+ int link_len = fp->f_di.di_size;
+ int len;
+
+ len = strlen(cp);
+ if (link_len + len > MAXPATHLEN ||
+ ++nlinks > MAXSYMLINKS) {
+ error = ENOENT;
+ goto out;
+ }
+
+ bcopy(cp, &namebuf[link_len], len + 1);
+ if (fp->f_di.di_nblk == 0) {
+ bcopy(fp->f_di.di_shortlink,
+ namebuf, link_len);
+ } else {
+ /*
+ * Read file for symbolic link
+ */
+ struct ext2fs *fs = fp->f_fs;
+ daddr_t disk_block;
+ size_t buf_size;
+
+ if (! buf)
+ buf = malloc(fs->fs_bsize);
+ error = block_map(f, (daddr_t)0, &disk_block);
+ if (error)
+ goto out;
+
+ twiddle(1);
+ error = (f->f_dev->dv_strategy)(f->f_devdata,
+ F_READ, fsb_to_db(fs, disk_block),
+ fs->fs_bsize, buf, &buf_size);
+ if (error)
+ goto out;
+
+ bcopy((char *)buf, namebuf, link_len);
+ }
+
+ /*
+ * If relative pathname, restart at parent directory.
+ * If absolute pathname, restart at root.
+ */
+ cp = namebuf;
+ if (*cp != '/')
+ inumber = parent_inumber;
+ else
+ inumber = (ino_t)EXT2_ROOTINO;
+
+ if ((error = read_inode(inumber, f)) != 0)
+ goto out;
+ }
+ }
+
+ /*
+ * Found terminal component.
+ */
+ error = 0;
+ fp->f_seekp = 0;
+out:
+ if (buf)
+ free(buf);
+ if (path)
+ free(path);
+ if (error) {
+ if (fp->f_buf)
+ free(fp->f_buf);
+ free(fp->f_fs);
+ free(fp);
+ }
+ return (error);
+}
+
+/*
+ * Read a new inode into a file structure.
+ */
+static int
+read_inode(ino_t inumber, struct open_file *f)
+{
+ struct file *fp = (struct file *)f->f_fsdata;
+ struct ext2fs *fs = fp->f_fs;
+ struct ext2dinode *dp;
+ char *buf;
+ size_t rsize;
+ int level, error = 0;
+
+ /*
+ * Read inode and save it.
+ */
+ buf = malloc(fs->fs_bsize);
+ twiddle(1);
+ error = (f->f_dev->dv_strategy)(f->f_devdata, F_READ,
+ ino_to_db(fs, fp->f_bg, inumber), fs->fs_bsize, buf, &rsize);
+ if (error)
+ goto out;
+ if (rsize != fs->fs_bsize) {
+ error = EIO;
+ goto out;
+ }
+
+ dp = (struct ext2dinode *)buf;
+ fp->f_di = dp[ino_to_bo(fs, inumber)];
+
+ /* clear out old buffers */
+ for (level = 0; level < NIADDR; level++)
+ fp->f_blkno[level] = -1;
+ fp->f_buf_blkno = -1;
+ fp->f_seekp = 0;
+
+out:
+ free(buf);
+ return (error);
+}
+
+/*
+ * Given an offset in a file, find the disk block number that
+ * contains that block.
+ */
+static int
+block_map(struct open_file *f, daddr_t file_block, daddr_t *disk_block_p)
+{
+ struct file *fp = (struct file *)f->f_fsdata;
+ struct ext2fs *fs = fp->f_fs;
+ daddr_t ind_block_num;
+ int32_t *ind_p;
+ int idx, level;
+ int error;
+
+ /*
+ * Index structure of an inode:
+ *
+ * di_db[0..NDADDR-1] hold block numbers for blocks
+ * 0..NDADDR-1
+ *
+ * di_ib[0] index block 0 is the single indirect block
+ * holds block numbers for blocks
+ * NDADDR .. NDADDR + NINDIR(fs)-1
+ *
+ * di_ib[1] index block 1 is the double indirect block
+ * holds block numbers for INDEX blocks for blocks
+ * NDADDR + NINDIR(fs) ..
+ * NDADDR + NINDIR(fs) + NINDIR(fs)**2 - 1
+ *
+ * di_ib[2] index block 2 is the triple indirect block
+ * holds block numbers for double-indirect
+ * blocks for blocks
+ * NDADDR + NINDIR(fs) + NINDIR(fs)**2 ..
+ * NDADDR + NINDIR(fs) + NINDIR(fs)**2
+ * + NINDIR(fs)**3 - 1
+ */
+
+ if (file_block < NDADDR) {
+ /* Direct block. */
+ *disk_block_p = fp->f_di.di_db[file_block];
+ return (0);
+ }
+
+ file_block -= NDADDR;
+
+ /*
+ * nindir[0] = NINDIR
+ * nindir[1] = NINDIR**2
+ * nindir[2] = NINDIR**3
+ * etc
+ */
+ for (level = 0; level < NIADDR; level++) {
+ if (file_block < fp->f_nindir[level])
+ break;
+ file_block -= fp->f_nindir[level];
+ }
+ if (level == NIADDR) {
+ /* Block number too high */
+ return (EFBIG);
+ }
+
+ ind_block_num = fp->f_di.di_ib[level];
+
+ for (; level >= 0; level--) {
+ if (ind_block_num == 0) {
+ *disk_block_p = 0; /* missing */
+ return (0);
+ }
+
+ if (fp->f_blkno[level] != ind_block_num) {
+ if (fp->f_blk[level] == (char *)0)
+ fp->f_blk[level] =
+ malloc(fs->fs_bsize);
+ twiddle(1);
+ error = (f->f_dev->dv_strategy)(f->f_devdata, F_READ,
+ fsb_to_db(fp->f_fs, ind_block_num), fs->fs_bsize,
+ fp->f_blk[level], &fp->f_blksize[level]);
+ if (error)
+ return (error);
+ if (fp->f_blksize[level] != fs->fs_bsize)
+ return (EIO);
+ fp->f_blkno[level] = ind_block_num;
+ }
+
+ ind_p = (int32_t *)fp->f_blk[level];
+
+ if (level > 0) {
+ idx = file_block / fp->f_nindir[level - 1];
+ file_block %= fp->f_nindir[level - 1];
+ } else {
+ idx = file_block;
+ }
+ ind_block_num = ind_p[idx];
+ }
+
+ *disk_block_p = ind_block_num;
+
+ return (0);
+}
+
+/*
+ * Read a portion of a file into an internal buffer. Return
+ * the location in the buffer and the amount in the buffer.
+ */
+static int
+buf_read_file(struct open_file *f, char **buf_p, size_t *size_p)
+{
+ struct file *fp = (struct file *)f->f_fsdata;
+ struct ext2fs *fs = fp->f_fs;
+ long off;
+ daddr_t file_block;
+ daddr_t disk_block;
+ size_t block_size;
+ int error = 0;
+
+ off = blkoff(fs, fp->f_seekp);
+ file_block = lblkno(fs, fp->f_seekp);
+ block_size = dblksize(fs, &fp->f_di, file_block);
+
+ if (file_block != fp->f_buf_blkno) {
+ error = block_map(f, file_block, &disk_block);
+ if (error)
+ goto done;
+
+ if (fp->f_buf == (char *)0)
+ fp->f_buf = malloc(fs->fs_bsize);
+
+ if (disk_block == 0) {
+ bzero(fp->f_buf, block_size);
+ fp->f_buf_size = block_size;
+ } else {
+ twiddle(4);
+ error = (f->f_dev->dv_strategy)(f->f_devdata, F_READ,
+ fsb_to_db(fs, disk_block), block_size,
+ fp->f_buf, &fp->f_buf_size);
+ if (error)
+ goto done;
+ }
+ fp->f_buf_blkno = file_block;
+ }
+
+ /*
+ * Return address of byte in buffer corresponding to
+ * offset, and size of remainder of buffer after that
+ * byte.
+ */
+ *buf_p = fp->f_buf + off;
+ *size_p = block_size - off;
+
+ /*
+ * But truncate buffer at end of file.
+ */
+ if (*size_p > fp->f_di.di_size - fp->f_seekp)
+ *size_p = fp->f_di.di_size - fp->f_seekp;
+done:
+ return (error);
+}
+
+/*
+ * Search a directory for a name and return its
+ * i_number.
+ */
+static int
+search_directory(char *name, struct open_file *f, ino_t *inumber_p)
+{
+ struct file *fp = (struct file *)f->f_fsdata;
+ struct ext2dirent *dp, *edp;
+ char *buf;
+ size_t buf_size;
+ int namlen, length;
+ int error;
+
+ length = strlen(name);
+ fp->f_seekp = 0;
+ while (fp->f_seekp < fp->f_di.di_size) {
+ error = buf_read_file(f, &buf, &buf_size);
+ if (error)
+ return (error);
+ dp = (struct ext2dirent *)buf;
+ edp = (struct ext2dirent *)(buf + buf_size);
+ while (dp < edp) {
+ if (dp->d_ino == (ino_t)0)
+ goto next;
+ namlen = dp->d_namlen;
+ if (namlen == length &&
+ strncmp(name, dp->d_name, length) == 0) {
+ /* found entry */
+ *inumber_p = dp->d_ino;
+ return (0);
+ }
+ next:
+ dp = (struct ext2dirent *)((char *)dp + dp->d_reclen);
+ }
+ fp->f_seekp += buf_size;
+ }
+ return (ENOENT);
+}
+
+static int
+ext2fs_close(struct open_file *f)
+{
+ struct file *fp = (struct file *)f->f_fsdata;
+ int level;
+
+ f->f_fsdata = (void *)0;
+ if (fp == (struct file *)0)
+ return (0);
+
+ for (level = 0; level < NIADDR; level++) {
+ if (fp->f_blk[level])
+ free(fp->f_blk[level]);
+ }
+ if (fp->f_buf)
+ free(fp->f_buf);
+ if (fp->f_bg)
+ free(fp->f_bg);
+ free(fp->f_fs);
+ free(fp);
+ return (0);
+}
+
+static int
+ext2fs_read(struct open_file *f, void *addr, size_t size, size_t *resid)
+{
+ struct file *fp = (struct file *)f->f_fsdata;
+ size_t csize, buf_size;
+ char *buf;
+ int error = 0;
+
+ while (size != 0) {
+ if (fp->f_seekp >= fp->f_di.di_size)
+ break;
+
+ error = buf_read_file(f, &buf, &buf_size);
+ if (error)
+ break;
+
+ csize = size;
+ if (csize > buf_size)
+ csize = buf_size;
+
+ bcopy(buf, addr, csize);
+
+ fp->f_seekp += csize;
+ addr = (char *)addr + csize;
+ size -= csize;
+ }
+ if (resid)
+ *resid = size;
+ return (error);
+}
+
+static off_t
+ext2fs_seek(struct open_file *f, off_t offset, int where)
+{
+ struct file *fp = (struct file *)f->f_fsdata;
+
+ switch (where) {
+ case SEEK_SET:
+ fp->f_seekp = offset;
+ break;
+ case SEEK_CUR:
+ fp->f_seekp += offset;
+ break;
+ case SEEK_END:
+ fp->f_seekp = fp->f_di.di_size - offset;
+ break;
+ default:
+ errno = EINVAL;
+ return (-1);
+ }
+ return (fp->f_seekp);
+}
+
+static int
+ext2fs_stat(struct open_file *f, struct stat *sb)
+{
+ struct file *fp = (struct file *)f->f_fsdata;
+
+ /* only important stuff */
+ sb->st_mode = fp->f_di.di_mode;
+ sb->st_uid = fp->f_di.di_uid;
+ sb->st_gid = fp->f_di.di_gid;
+ sb->st_size = fp->f_di.di_size;
+ return (0);
+}
+
+static int
+ext2fs_readdir(struct open_file *f, struct dirent *d)
+{
+ struct file *fp = (struct file *)f->f_fsdata;
+ struct ext2dirent *ed;
+ char *buf;
+ size_t buf_size;
+ int error;
+
+ /*
+ * assume that a directory entry will not be split across blocks
+ */
+again:
+ if (fp->f_seekp >= fp->f_di.di_size)
+ return (ENOENT);
+ error = buf_read_file(f, &buf, &buf_size);
+ if (error)
+ return (error);
+ ed = (struct ext2dirent *)buf;
+ fp->f_seekp += ed->d_reclen;
+ if (ed->d_ino == (ino_t)0)
+ goto again;
+ d->d_type = EXTFTODT(ed->d_type);
+ strncpy(d->d_name, ed->d_name, ed->d_namlen);
+ d->d_name[ed->d_namlen] = '\0';
+ return (0);
+}
diff --git a/stand/libsa/fstat.c b/stand/libsa/fstat.c
new file mode 100644
index 0000000..13bed08
--- /dev/null
+++ b/stand/libsa/fstat.c
@@ -0,0 +1,61 @@
+/* $NetBSD: fstat.c,v 1.1 1996/01/13 22:25:38 leo Exp $ */
+
+/*-
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)stat.c 8.1 (Berkeley) 6/11/93
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "stand.h"
+
+int
+fstat(fd, sb)
+ int fd;
+ struct stat *sb;
+{
+ struct open_file *f = &files[fd];
+
+ if ((unsigned)fd >= SOPEN_MAX || f->f_flags == 0) {
+ errno = EBADF;
+ return (-1);
+ }
+
+ /* operation not defined on raw devices */
+ if (f->f_flags & F_RAW) {
+ errno = EOPNOTSUPP;
+ return (-1);
+ }
+
+ errno = (f->f_ops->fo_stat)(f, sb);
+ if (errno)
+ return (-1);
+ return (0);
+}
diff --git a/stand/libsa/getopt.c b/stand/libsa/getopt.c
new file mode 100644
index 0000000..cfe405a
--- /dev/null
+++ b/stand/libsa/getopt.c
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 1987, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)getopt.c 8.3 (Berkeley) 4/27/95";
+#endif /* LIBC_SCCS and not lint */
+
+#include "stand.h"
+#include <string.h>
+
+int opterr = 1, /* if error message should be printed */
+ optind = 1, /* index into parent argv vector */
+ optopt, /* character checked for validity */
+ optreset; /* reset getopt */
+char *optarg; /* argument associated with option */
+
+#define BADCH (int)'?'
+#define BADARG (int)':'
+#define EMSG ""
+
+/*
+ * getopt --
+ * Parse argc/argv argument vector.
+ */
+int
+getopt(int nargc, char * const *nargv, const char *ostr)
+{
+ static char *place = EMSG; /* option letter processing */
+ char *oli; /* option letter list index */
+
+ if (optreset || !*place) { /* update scanning pointer */
+ optreset = 0;
+ if (optind >= nargc || *(place = nargv[optind]) != '-') {
+ place = EMSG;
+ return (-1);
+ }
+ if (place[1] && *++place == '-') { /* found "--" */
+ ++optind;
+ place = EMSG;
+ return (-1);
+ }
+ } /* option letter okay? */
+ if ((optopt = (int)*place++) == (int)':' ||
+ !(oli = strchr(ostr, optopt))) {
+ /*
+ * if the user didn't specify '-' as an option,
+ * assume it means -1.
+ */
+ if (optopt == (int)'-')
+ return (-1);
+ if (!*place)
+ ++optind;
+ if (opterr && *ostr != ':')
+ (void)printf("illegal option -- %c\n", optopt);
+ return (BADCH);
+ }
+ if (*++oli != ':') { /* don't need argument */
+ optarg = NULL;
+ if (!*place)
+ ++optind;
+ }
+ else { /* need an argument */
+ if (*place) /* no white space */
+ optarg = place;
+ else if (nargc <= ++optind) { /* no arg */
+ place = EMSG;
+ if (*ostr == ':')
+ return (BADARG);
+ if (opterr)
+ (void)printf("option requires an argument -- %c\n", optopt);
+ return (BADCH);
+ }
+ else /* white space */
+ optarg = nargv[optind];
+ place = EMSG;
+ ++optind;
+ }
+ return (optopt); /* dump back option letter */
+}
diff --git a/stand/libsa/gets.c b/stand/libsa/gets.c
new file mode 100644
index 0000000..7d54b00
--- /dev/null
+++ b/stand/libsa/gets.c
@@ -0,0 +1,112 @@
+/* $NetBSD: gets.c,v 1.6 1995/10/11 21:16:57 pk Exp $ */
+
+/*-
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)gets.c 8.1 (Berkeley) 6/11/93
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "stand.h"
+
+/* gets() with constrained input length */
+
+void
+ngets(char *buf, int n)
+{
+ int c;
+ char *lp;
+
+ for (lp = buf;;)
+ switch (c = getchar() & 0177) {
+ case '\n':
+ case '\r':
+ *lp = '\0';
+ putchar('\n');
+ return;
+ case '\b':
+ case '\177':
+ if (lp > buf) {
+ lp--;
+ putchar('\b');
+ putchar(' ');
+ putchar('\b');
+ }
+ break;
+ case 'r'&037: {
+ char *p;
+
+ putchar('\n');
+ for (p = buf; p < lp; ++p)
+ putchar(*p);
+ break;
+ }
+ case 'u'&037:
+ case 'w'&037:
+ lp = buf;
+ putchar('\n');
+ break;
+ default:
+ if ((n < 1) || ((lp - buf) < n - 1)) {
+ *lp++ = c;
+ putchar(c);
+ }
+ }
+ /*NOTREACHED*/
+}
+
+int
+fgetstr(char *buf, int size, int fd)
+{
+ char c;
+ int err, len;
+
+ size--; /* leave space for terminator */
+ len = 0;
+ while (size != 0) {
+ err = read(fd, &c, sizeof(c));
+ if (err < 0) /* read error */
+ return(-1);
+ if (err == 0) { /* EOF */
+ if (len == 0)
+ return(-1); /* nothing to read */
+ break;
+ }
+ if ((c == '\r') || /* line terminators */
+ (c == '\n'))
+ break;
+ *buf++ = c; /* keep char */
+ size--;
+ len++;
+ }
+ *buf = 0;
+ return(len);
+}
+
diff --git a/stand/libsa/globals.c b/stand/libsa/globals.c
new file mode 100644
index 0000000..8fb56cf
--- /dev/null
+++ b/stand/libsa/globals.c
@@ -0,0 +1,38 @@
+/* $NetBSD: globals.c,v 1.3 1995/09/18 21:19:27 pk Exp $ */
+
+/*
+ * globals.c:
+ *
+ * global variables should be separate, so nothing else
+ * must be included extraneously.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+
+#include "stand.h"
+#include "net.h"
+
+u_char bcea[6] = BA; /* broadcast ethernet address */
+
+char rootpath[FNAME_SIZE] = "/"; /* root mount path */
+char bootfile[FNAME_SIZE]; /* bootp says to boot this */
+char hostname[FNAME_SIZE]; /* our hostname */
+int hostnamelen;
+char domainname[FNAME_SIZE]; /* our DNS domain */
+int domainnamelen;
+int netproto = NET_NONE; /* Network prototol */
+char ifname[IFNAME_SIZE]; /* name of interface (e.g. "le0") */
+struct in_addr myip; /* my ip address */
+struct in_addr nameip; /* DNS server ip address */
+struct in_addr rootip; /* root ip address */
+struct in_addr swapip; /* swap ip address */
+struct in_addr gateip; /* gateway ip address */
+n_long netmask = 0xffffff00; /* subnet or net mask */
+u_int intf_mtu; /* interface mtu from bootp/dhcp */
+int errno; /* our old friend */
+
diff --git a/stand/libsa/gpt.c b/stand/libsa/gpt.c
new file mode 100644
index 0000000..7ab3fc6
--- /dev/null
+++ b/stand/libsa/gpt.c
@@ -0,0 +1,379 @@
+/*-
+ * Copyright (c) 2010 Pawel Jakub Dawidek <pjd@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/gpt.h>
+
+#ifndef LITTLE_ENDIAN
+#error gpt.c works only for little endian architectures
+#endif
+
+#include "crc32.h"
+#include "drv.h"
+#include "util.h"
+#include "gpt.h"
+
+static struct gpt_hdr hdr_primary, hdr_backup, *gpthdr;
+static uint64_t hdr_primary_lba, hdr_backup_lba;
+static struct gpt_ent table_primary[MAXTBLENTS], table_backup[MAXTBLENTS];
+static struct gpt_ent *gpttable;
+static int curent, bootonce;
+
+/*
+ * Buffer below 64kB passed on gptread(), which can hold at least
+ * one sector of data (512 bytes).
+ */
+static char *secbuf;
+
+static void
+gptupdate(const char *which, struct dsk *dskp, struct gpt_hdr *hdr,
+ struct gpt_ent *table)
+{
+ int entries_per_sec, firstent;
+ daddr_t slba;
+
+ /*
+ * We need to update the following for both primary and backup GPT:
+ * 1. Sector on disk that contains current partition.
+ * 2. Partition table checksum.
+ * 3. Header checksum.
+ * 4. Header on disk.
+ */
+
+ entries_per_sec = DEV_BSIZE / hdr->hdr_entsz;
+ slba = curent / entries_per_sec;
+ firstent = slba * entries_per_sec;
+ bcopy(&table[firstent], secbuf, DEV_BSIZE);
+ slba += hdr->hdr_lba_table;
+ if (drvwrite(dskp, secbuf, slba, 1)) {
+ printf("%s: unable to update %s GPT partition table\n",
+ BOOTPROG, which);
+ return;
+ }
+ hdr->hdr_crc_table = crc32(table, hdr->hdr_entries * hdr->hdr_entsz);
+ hdr->hdr_crc_self = 0;
+ hdr->hdr_crc_self = crc32(hdr, hdr->hdr_size);
+ bzero(secbuf, DEV_BSIZE);
+ bcopy(hdr, secbuf, hdr->hdr_size);
+ if (drvwrite(dskp, secbuf, hdr->hdr_lba_self, 1)) {
+ printf("%s: unable to update %s GPT header\n", BOOTPROG, which);
+ return;
+ }
+}
+
+int
+gptfind(const uuid_t *uuid, struct dsk *dskp, int part)
+{
+ struct gpt_ent *ent;
+ int firsttry;
+
+ if (part >= 0) {
+ if (part == 0 || part > gpthdr->hdr_entries) {
+ printf("%s: invalid partition index\n", BOOTPROG);
+ return (-1);
+ }
+ ent = &gpttable[part - 1];
+ if (bcmp(&ent->ent_type, uuid, sizeof(uuid_t)) != 0) {
+ printf("%s: specified partition is not UFS\n",
+ BOOTPROG);
+ return (-1);
+ }
+ curent = part - 1;
+ goto found;
+ }
+
+ firsttry = (curent == -1);
+ curent++;
+ if (curent >= gpthdr->hdr_entries) {
+ curent = gpthdr->hdr_entries;
+ return (-1);
+ }
+ if (bootonce) {
+ /*
+ * First look for partition with both GPT_ENT_ATTR_BOOTME and
+ * GPT_ENT_ATTR_BOOTONCE flags.
+ */
+ for (; curent < gpthdr->hdr_entries; curent++) {
+ ent = &gpttable[curent];
+ if (bcmp(&ent->ent_type, uuid, sizeof(uuid_t)) != 0)
+ continue;
+ if (!(ent->ent_attr & GPT_ENT_ATTR_BOOTME))
+ continue;
+ if (!(ent->ent_attr & GPT_ENT_ATTR_BOOTONCE))
+ continue;
+ /* Ok, found one. */
+ goto found;
+ }
+ bootonce = 0;
+ curent = 0;
+ }
+ for (; curent < gpthdr->hdr_entries; curent++) {
+ ent = &gpttable[curent];
+ if (bcmp(&ent->ent_type, uuid, sizeof(uuid_t)) != 0)
+ continue;
+ if (!(ent->ent_attr & GPT_ENT_ATTR_BOOTME))
+ continue;
+ if (ent->ent_attr & GPT_ENT_ATTR_BOOTONCE)
+ continue;
+ /* Ok, found one. */
+ goto found;
+ }
+ if (firsttry) {
+ /*
+ * No partition with BOOTME flag was found, try to boot from
+ * first UFS partition.
+ */
+ for (curent = 0; curent < gpthdr->hdr_entries; curent++) {
+ ent = &gpttable[curent];
+ if (bcmp(&ent->ent_type, uuid, sizeof(uuid_t)) != 0)
+ continue;
+ /* Ok, found one. */
+ goto found;
+ }
+ }
+ return (-1);
+found:
+ dskp->part = curent + 1;
+ ent = &gpttable[curent];
+ dskp->start = ent->ent_lba_start;
+ if (ent->ent_attr & GPT_ENT_ATTR_BOOTONCE) {
+ /*
+ * Clear BOOTME, but leave BOOTONCE set before trying to
+ * boot from this partition.
+ */
+ if (hdr_primary_lba > 0) {
+ table_primary[curent].ent_attr &= ~GPT_ENT_ATTR_BOOTME;
+ gptupdate("primary", dskp, &hdr_primary, table_primary);
+ }
+ if (hdr_backup_lba > 0) {
+ table_backup[curent].ent_attr &= ~GPT_ENT_ATTR_BOOTME;
+ gptupdate("backup", dskp, &hdr_backup, table_backup);
+ }
+ }
+ return (0);
+}
+
+static int
+gptread_hdr(const char *which, struct dsk *dskp, struct gpt_hdr *hdr,
+ uint64_t hdrlba)
+{
+ uint32_t crc;
+
+ if (drvread(dskp, secbuf, hdrlba, 1)) {
+ printf("%s: unable to read %s GPT header\n", BOOTPROG, which);
+ return (-1);
+ }
+ bcopy(secbuf, hdr, sizeof(*hdr));
+ if (bcmp(hdr->hdr_sig, GPT_HDR_SIG, sizeof(hdr->hdr_sig)) != 0 ||
+ hdr->hdr_lba_self != hdrlba || hdr->hdr_revision < 0x00010000 ||
+ hdr->hdr_entsz < sizeof(struct gpt_ent) ||
+ hdr->hdr_entries > MAXTBLENTS || DEV_BSIZE % hdr->hdr_entsz != 0) {
+ printf("%s: invalid %s GPT header\n", BOOTPROG, which);
+ return (-1);
+ }
+ crc = hdr->hdr_crc_self;
+ hdr->hdr_crc_self = 0;
+ if (crc32(hdr, hdr->hdr_size) != crc) {
+ printf("%s: %s GPT header checksum mismatch\n", BOOTPROG,
+ which);
+ return (-1);
+ }
+ hdr->hdr_crc_self = crc;
+ return (0);
+}
+
+void
+gptbootfailed(struct dsk *dskp)
+{
+
+ if (!(gpttable[curent].ent_attr & GPT_ENT_ATTR_BOOTONCE))
+ return;
+
+ if (hdr_primary_lba > 0) {
+ table_primary[curent].ent_attr &= ~GPT_ENT_ATTR_BOOTONCE;
+ table_primary[curent].ent_attr |= GPT_ENT_ATTR_BOOTFAILED;
+ gptupdate("primary", dskp, &hdr_primary, table_primary);
+ }
+ if (hdr_backup_lba > 0) {
+ table_backup[curent].ent_attr &= ~GPT_ENT_ATTR_BOOTONCE;
+ table_backup[curent].ent_attr |= GPT_ENT_ATTR_BOOTFAILED;
+ gptupdate("backup", dskp, &hdr_backup, table_backup);
+ }
+}
+
+static void
+gptbootconv(const char *which, struct dsk *dskp, struct gpt_hdr *hdr,
+ struct gpt_ent *table)
+{
+ struct gpt_ent *ent;
+ daddr_t slba;
+ int table_updated, sector_updated;
+ int entries_per_sec, nent, part;
+
+ table_updated = 0;
+ entries_per_sec = DEV_BSIZE / hdr->hdr_entsz;
+ for (nent = 0, slba = hdr->hdr_lba_table;
+ slba < hdr->hdr_lba_table + hdr->hdr_entries / entries_per_sec;
+ slba++, nent += entries_per_sec) {
+ sector_updated = 0;
+ for (part = 0; part < entries_per_sec; part++) {
+ ent = &table[nent + part];
+ if ((ent->ent_attr & (GPT_ENT_ATTR_BOOTME |
+ GPT_ENT_ATTR_BOOTONCE |
+ GPT_ENT_ATTR_BOOTFAILED)) !=
+ GPT_ENT_ATTR_BOOTONCE) {
+ continue;
+ }
+ ent->ent_attr &= ~GPT_ENT_ATTR_BOOTONCE;
+ ent->ent_attr |= GPT_ENT_ATTR_BOOTFAILED;
+ table_updated = 1;
+ sector_updated = 1;
+ }
+ if (!sector_updated)
+ continue;
+ bcopy(&table[nent], secbuf, DEV_BSIZE);
+ if (drvwrite(dskp, secbuf, slba, 1)) {
+ printf("%s: unable to update %s GPT partition table\n",
+ BOOTPROG, which);
+ }
+ }
+ if (!table_updated)
+ return;
+ hdr->hdr_crc_table = crc32(table, hdr->hdr_entries * hdr->hdr_entsz);
+ hdr->hdr_crc_self = 0;
+ hdr->hdr_crc_self = crc32(hdr, hdr->hdr_size);
+ bzero(secbuf, DEV_BSIZE);
+ bcopy(hdr, secbuf, hdr->hdr_size);
+ if (drvwrite(dskp, secbuf, hdr->hdr_lba_self, 1))
+ printf("%s: unable to update %s GPT header\n", BOOTPROG, which);
+}
+
+static int
+gptread_table(const char *which, const uuid_t *uuid, struct dsk *dskp,
+ struct gpt_hdr *hdr, struct gpt_ent *table)
+{
+ struct gpt_ent *ent;
+ int entries_per_sec;
+ int part, nent;
+ daddr_t slba;
+
+ if (hdr->hdr_entries == 0)
+ return (0);
+
+ entries_per_sec = DEV_BSIZE / hdr->hdr_entsz;
+ slba = hdr->hdr_lba_table;
+ nent = 0;
+ for (;;) {
+ if (drvread(dskp, secbuf, slba, 1)) {
+ printf("%s: unable to read %s GPT partition table\n",
+ BOOTPROG, which);
+ return (-1);
+ }
+ ent = (struct gpt_ent *)secbuf;
+ for (part = 0; part < entries_per_sec; part++, ent++) {
+ bcopy(ent, &table[nent], sizeof(table[nent]));
+ if (++nent >= hdr->hdr_entries)
+ break;
+ }
+ if (nent >= hdr->hdr_entries)
+ break;
+ slba++;
+ }
+ if (crc32(table, nent * hdr->hdr_entsz) != hdr->hdr_crc_table) {
+ printf("%s: %s GPT table checksum mismatch\n", BOOTPROG, which);
+ return (-1);
+ }
+ return (0);
+}
+
+int
+gptread(const uuid_t *uuid, struct dsk *dskp, char *buf)
+{
+ uint64_t altlba;
+
+ /*
+ * Read and verify both GPT headers: primary and backup.
+ */
+
+ secbuf = buf;
+ hdr_primary_lba = hdr_backup_lba = 0;
+ curent = -1;
+ bootonce = 1;
+ dskp->start = 0;
+
+ if (gptread_hdr("primary", dskp, &hdr_primary, 1) == 0 &&
+ gptread_table("primary", uuid, dskp, &hdr_primary,
+ table_primary) == 0) {
+ hdr_primary_lba = hdr_primary.hdr_lba_self;
+ gpthdr = &hdr_primary;
+ gpttable = table_primary;
+ }
+
+ if (hdr_primary_lba > 0) {
+ /*
+ * If primary header is valid, we can get backup
+ * header location from there.
+ */
+ altlba = hdr_primary.hdr_lba_alt;
+ } else {
+ altlba = drvsize(dskp);
+ if (altlba > 0)
+ altlba--;
+ }
+ if (altlba == 0)
+ printf("%s: unable to locate backup GPT header\n", BOOTPROG);
+ else if (gptread_hdr("backup", dskp, &hdr_backup, altlba) == 0 &&
+ gptread_table("backup", uuid, dskp, &hdr_backup,
+ table_backup) == 0) {
+ hdr_backup_lba = hdr_backup.hdr_lba_self;
+ if (hdr_primary_lba == 0) {
+ gpthdr = &hdr_backup;
+ gpttable = table_backup;
+ printf("%s: using backup GPT\n", BOOTPROG);
+ }
+ }
+
+ /*
+ * Convert all BOOTONCE without BOOTME flags into BOOTFAILED.
+ * BOOTONCE without BOOTME means that we tried to boot from it,
+ * but failed after leaving gptboot and machine was rebooted.
+ * We don't want to leave partitions marked as BOOTONCE only,
+ * because when we boot successfully start-up scripts should
+ * find at most one partition with only BOOTONCE flag and this
+ * will mean that we booted from that partition.
+ */
+ if (hdr_primary_lba != 0)
+ gptbootconv("primary", dskp, &hdr_primary, table_primary);
+ if (hdr_backup_lba != 0)
+ gptbootconv("backup", dskp, &hdr_backup, table_backup);
+
+ if (hdr_primary_lba == 0 && hdr_backup_lba == 0)
+ return (-1);
+ return (0);
+}
diff --git a/stand/libsa/gpt.h b/stand/libsa/gpt.h
new file mode 100644
index 0000000..9d48564
--- /dev/null
+++ b/stand/libsa/gpt.h
@@ -0,0 +1,41 @@
+/*-
+ * Copyright (c) 2010 Pawel Jakub Dawidek <pjd@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _GPT_H_
+#define _GPT_H_
+
+#include <uuid.h>
+#include <drv.h>
+
+#define MAXTBLENTS 128
+
+int gptread(const uuid_t *uuid, struct dsk *dskp, char *buf);
+int gptfind(const uuid_t *uuid, struct dsk *dskp, int part);
+void gptbootfailed(struct dsk *dskp);
+
+#endif /* !_GPT_H_ */
diff --git a/stand/libsa/gzipfs.c b/stand/libsa/gzipfs.c
new file mode 100644
index 0000000..6057c28
--- /dev/null
+++ b/stand/libsa/gzipfs.c
@@ -0,0 +1,337 @@
+/*
+ * Copyright (c) 1998 Michael Smith.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "stand.h"
+
+#include <sys/stat.h>
+#include <string.h>
+#include <zlib.h>
+
+#define Z_BUFSIZE 2048 /* XXX larger? */
+
+struct z_file
+{
+ int zf_rawfd;
+ off_t zf_dataoffset;
+ z_stream zf_zstream;
+ char zf_buf[Z_BUFSIZE];
+ int zf_endseen;
+};
+
+static int zf_fill(struct z_file *z);
+static int zf_open(const char *path, struct open_file *f);
+static int zf_close(struct open_file *f);
+static int zf_read(struct open_file *f, void *buf, size_t size, size_t *resid);
+static off_t zf_seek(struct open_file *f, off_t offset, int where);
+static int zf_stat(struct open_file *f, struct stat *sb);
+
+struct fs_ops gzipfs_fsops = {
+ "zip",
+ zf_open,
+ zf_close,
+ zf_read,
+ null_write,
+ zf_seek,
+ zf_stat,
+ null_readdir
+};
+
+static int
+zf_fill(struct z_file *zf)
+{
+ int result;
+ int req;
+
+ req = Z_BUFSIZE - zf->zf_zstream.avail_in;
+ result = 0;
+
+ /* If we need more */
+ if (req > 0) {
+ /* move old data to bottom of buffer */
+ if (req < Z_BUFSIZE)
+ bcopy(zf->zf_buf + req, zf->zf_buf, Z_BUFSIZE - req);
+
+ /* read to fill buffer and update availibility data */
+ result = read(zf->zf_rawfd, zf->zf_buf + zf->zf_zstream.avail_in, req);
+ zf->zf_zstream.next_in = zf->zf_buf;
+ if (result >= 0)
+ zf->zf_zstream.avail_in += result;
+ }
+ return(result);
+}
+
+/*
+ * Adapted from get_byte/check_header in libz
+ *
+ * Returns 0 if the header is OK, nonzero if not.
+ */
+static int
+get_byte(struct z_file *zf, off_t *curoffp)
+{
+ if ((zf->zf_zstream.avail_in == 0) && (zf_fill(zf) == -1))
+ return(-1);
+ zf->zf_zstream.avail_in--;
+ ++*curoffp;
+ return(*(zf->zf_zstream.next_in)++);
+}
+
+static int gz_magic[2] = {0x1f, 0x8b}; /* gzip magic header */
+
+/* gzip flag byte */
+#define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */
+#define HEAD_CRC 0x02 /* bit 1 set: header CRC present */
+#define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */
+#define ORIG_NAME 0x08 /* bit 3 set: original file name present */
+#define COMMENT 0x10 /* bit 4 set: file comment present */
+#define RESERVED 0xE0 /* bits 5..7: reserved */
+
+static int
+check_header(struct z_file *zf)
+{
+ int method; /* method byte */
+ int flags; /* flags byte */
+ uInt len;
+ int c;
+
+ zf->zf_dataoffset = 0;
+ /* Check the gzip magic header */
+ for (len = 0; len < 2; len++) {
+ c = get_byte(zf, &zf->zf_dataoffset);
+ if (c != gz_magic[len]) {
+ return(1);
+ }
+ }
+ method = get_byte(zf, &zf->zf_dataoffset);
+ flags = get_byte(zf, &zf->zf_dataoffset);
+ if (method != Z_DEFLATED || (flags & RESERVED) != 0) {
+ return(1);
+ }
+
+ /* Discard time, xflags and OS code: */
+ for (len = 0; len < 6; len++) (void)get_byte(zf, &zf->zf_dataoffset);
+
+ if ((flags & EXTRA_FIELD) != 0) { /* skip the extra field */
+ len = (uInt)get_byte(zf, &zf->zf_dataoffset);
+ len += ((uInt)get_byte(zf, &zf->zf_dataoffset))<<8;
+ /* len is garbage if EOF but the loop below will quit anyway */
+ while (len-- != 0 && get_byte(zf, &zf->zf_dataoffset) != -1) ;
+ }
+ if ((flags & ORIG_NAME) != 0) { /* skip the original file name */
+ while ((c = get_byte(zf, &zf->zf_dataoffset)) != 0 && c != -1) ;
+ }
+ if ((flags & COMMENT) != 0) { /* skip the .gz file comment */
+ while ((c = get_byte(zf, &zf->zf_dataoffset)) != 0 && c != -1) ;
+ }
+ if ((flags & HEAD_CRC) != 0) { /* skip the header crc */
+ for (len = 0; len < 2; len++) c = get_byte(zf, &zf->zf_dataoffset);
+ }
+ /* if there's data left, we're in business */
+ return((c == -1) ? 1 : 0);
+}
+
+static int
+zf_open(const char *fname, struct open_file *f)
+{
+ static char *zfname;
+ int rawfd;
+ struct z_file *zf;
+ char *cp;
+ int error;
+ struct stat sb;
+
+ /* Have to be in "just read it" mode */
+ if (f->f_flags != F_READ)
+ return(EPERM);
+
+ /* If the name already ends in .gz or .bz2, ignore it */
+ if ((cp = strrchr(fname, '.')) && (!strcmp(cp, ".gz")
+ || !strcmp(cp, ".bz2") || !strcmp(cp, ".split")))
+ return(ENOENT);
+
+ /* Construct new name */
+ zfname = malloc(strlen(fname) + 4);
+ if (zfname == NULL)
+ return(ENOMEM);
+ sprintf(zfname, "%s.gz", fname);
+
+ /* Try to open the compressed datafile */
+ rawfd = open(zfname, O_RDONLY);
+ free(zfname);
+ if (rawfd == -1)
+ return(ENOENT);
+
+ if (fstat(rawfd, &sb) < 0) {
+ printf("zf_open: stat failed\n");
+ close(rawfd);
+ return(ENOENT);
+ }
+ if (!S_ISREG(sb.st_mode)) {
+ printf("zf_open: not a file\n");
+ close(rawfd);
+ return(EISDIR); /* best guess */
+ }
+
+ /* Allocate a z_file structure, populate it */
+ zf = malloc(sizeof(struct z_file));
+ if (zf == NULL)
+ return(ENOMEM);
+ bzero(zf, sizeof(struct z_file));
+ zf->zf_rawfd = rawfd;
+
+ /* Verify that the file is gzipped */
+ if (check_header(zf)) {
+ close(zf->zf_rawfd);
+ free(zf);
+ return(EFTYPE);
+ }
+
+ /* Initialise the inflation engine */
+ if ((error = inflateInit2(&(zf->zf_zstream), -15)) != Z_OK) {
+ printf("zf_open: inflateInit returned %d : %s\n", error, zf->zf_zstream.msg);
+ close(zf->zf_rawfd);
+ free(zf);
+ return(EIO);
+ }
+
+ /* Looks OK, we'll take it */
+ f->f_fsdata = zf;
+ return(0);
+}
+
+static int
+zf_close(struct open_file *f)
+{
+ struct z_file *zf = (struct z_file *)f->f_fsdata;
+
+ inflateEnd(&(zf->zf_zstream));
+ close(zf->zf_rawfd);
+ free(zf);
+ return(0);
+}
+
+static int
+zf_read(struct open_file *f, void *buf, size_t size, size_t *resid)
+{
+ struct z_file *zf = (struct z_file *)f->f_fsdata;
+ int error;
+
+ zf->zf_zstream.next_out = buf; /* where and how much */
+ zf->zf_zstream.avail_out = size;
+
+ while (zf->zf_zstream.avail_out && zf->zf_endseen == 0) {
+ if ((zf->zf_zstream.avail_in == 0) && (zf_fill(zf) == -1)) {
+ printf("zf_read: fill error\n");
+ return(EIO);
+ }
+ if (zf->zf_zstream.avail_in == 0) { /* oops, unexpected EOF */
+ printf("zf_read: unexpected EOF\n");
+ if (zf->zf_zstream.avail_out == size)
+ return(EIO);
+ break;
+ }
+
+ error = inflate(&zf->zf_zstream, Z_SYNC_FLUSH); /* decompression pass */
+ if (error == Z_STREAM_END) { /* EOF, all done */
+ zf->zf_endseen = 1;
+ break;
+ }
+ if (error != Z_OK) { /* argh, decompression error */
+ printf("inflate: %s\n", zf->zf_zstream.msg);
+ return(EIO);
+ }
+ }
+ if (resid != NULL)
+ *resid = zf->zf_zstream.avail_out;
+ return(0);
+}
+
+static int
+zf_rewind(struct open_file *f)
+{
+ struct z_file *zf = (struct z_file *)f->f_fsdata;
+
+ if (lseek(zf->zf_rawfd, zf->zf_dataoffset, SEEK_SET) == -1)
+ return(-1);
+ zf->zf_zstream.avail_in = 0;
+ zf->zf_zstream.next_in = NULL;
+ zf->zf_endseen = 0;
+ (void)inflateReset(&zf->zf_zstream);
+
+ return(0);
+}
+
+static off_t
+zf_seek(struct open_file *f, off_t offset, int where)
+{
+ struct z_file *zf = (struct z_file *)f->f_fsdata;
+ off_t target;
+ char discard[16];
+
+ switch (where) {
+ case SEEK_SET:
+ target = offset;
+ break;
+ case SEEK_CUR:
+ target = offset + zf->zf_zstream.total_out;
+ break;
+ default:
+ errno = EINVAL;
+ return(-1);
+ }
+
+ /* rewind if required */
+ if (target < zf->zf_zstream.total_out && zf_rewind(f) != 0)
+ return(-1);
+
+ /* skip forwards if required */
+ while (target > zf->zf_zstream.total_out) {
+ errno = zf_read(f, discard, min(sizeof(discard),
+ target - zf->zf_zstream.total_out), NULL);
+ if (errno)
+ return(-1);
+ }
+ /* This is where we are (be honest if we overshot) */
+ return(zf->zf_zstream.total_out);
+}
+
+
+static int
+zf_stat(struct open_file *f, struct stat *sb)
+{
+ struct z_file *zf = (struct z_file *)f->f_fsdata;
+ int result;
+
+ /* stat as normal, but indicate that size is unknown */
+ if ((result = fstat(zf->zf_rawfd, sb)) == 0)
+ sb->st_size = -1;
+ return(result);
+}
+
+
+
diff --git a/stand/libsa/i386/_setjmp.S b/stand/libsa/i386/_setjmp.S
new file mode 100644
index 0000000..cc9de5c
--- /dev/null
+++ b/stand/libsa/i386/_setjmp.S
@@ -0,0 +1,77 @@
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * William Jolitz.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_RCS) && !defined(lint)
+ .text
+ .asciz "$FreeBSD$"
+#endif /* LIBC_RCS and not lint */
+
+/*
+ * C library -- _setjmp, _longjmp
+ *
+ * _longjmp(a,v)
+ * will generate a "return(v)" from the last call to
+ * _setjmp(a)
+ * by restoring registers from the environment 'a'.
+ * The previous signal state is NOT restored.
+ */
+
+#include <machine/asm.h>
+
+ENTRY(_setjmp)
+ movl 4(%esp),%eax
+ movl 0(%esp),%edx
+ movl %edx, 0(%eax) /* rta */
+ movl %ebx, 4(%eax)
+ movl %esp, 8(%eax)
+ movl %ebp,12(%eax)
+ movl %esi,16(%eax)
+ movl %edi,20(%eax)
+ xorl %eax,%eax
+ ret
+END(_setjmp)
+
+ENTRY(_longjmp)
+ movl 4(%esp),%edx
+ movl 8(%esp),%eax
+ movl 0(%edx),%ecx
+ movl 4(%edx),%ebx
+ movl 8(%edx),%esp
+ movl 12(%edx),%ebp
+ movl 16(%edx),%esi
+ movl 20(%edx),%edi
+ testl %eax,%eax
+ jnz 1f
+ incl %eax
+1: movl %ecx,0(%esp)
+ ret
+END(_longjmp)
diff --git a/stand/libsa/in_cksum.c b/stand/libsa/in_cksum.c
new file mode 100644
index 0000000..e6051bf
--- /dev/null
+++ b/stand/libsa/in_cksum.c
@@ -0,0 +1,94 @@
+/* $NetBSD: in_cksum.c,v 1.6 2000/03/31 19:55:09 castor Exp $ */
+
+/*
+ * Copyright (c) 1992 Regents of the University of California.
+ * All rights reserved.
+ *
+ * This software was developed by the Computer Systems Engineering group
+ * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
+ * contributed to Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#) Header: in_cksum.c,v 1.1 92/09/11 01:15:55 leres Exp (LBL)
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <machine/endian.h>
+
+#include "stand.h"
+
+/*
+ * Checksum routine for Internet Protocol family headers.
+ * This routine is very heavily used in the network
+ * code and should be modified for each CPU to be as fast as possible.
+ * In particular, it should not be this one.
+ */
+int
+in_cksum(p, len)
+ void *p;
+ int len;
+{
+ int sum = 0, oddbyte = 0, v = 0;
+ u_char *cp = p;
+
+ /* we assume < 2^16 bytes being summed */
+ while (len > 0) {
+ if (oddbyte) {
+ sum += v + *cp++;
+ len--;
+ }
+ if (((long)cp & 1) == 0) {
+ while ((len -= 2) >= 0) {
+ sum += *(u_short *)cp;
+ cp += 2;
+ }
+ } else {
+ while ((len -= 2) >= 0) {
+#if BYTE_ORDER == BIG_ENDIAN
+ sum += *cp++ << 8;
+ sum += *cp++;
+#else
+ sum += *cp++;
+ sum += *cp++ << 8;
+#endif
+ }
+ }
+ if ((oddbyte = len & 1) != 0)
+#if BYTE_ORDER == BIG_ENDIAN
+ v = *cp << 8;
+#else
+ v = *cp;
+#endif
+ }
+ if (oddbyte)
+ sum += v;
+ sum = (sum >> 16) + (sum & 0xffff); /* add in accumulated carries */
+ sum += sum >> 16; /* add potential last carry */
+ return (0xffff & ~sum);
+}
diff --git a/stand/libsa/inet_ntoa.c b/stand/libsa/inet_ntoa.c
new file mode 100644
index 0000000..5bc8533
--- /dev/null
+++ b/stand/libsa/inet_ntoa.c
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)inet_ntoa.c 8.1 (Berkeley) 6/4/93";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include "stand.h"
+
+/*
+ * Convert network-format internet address
+ * to base 256 d.d.d.d representation.
+ */
+char *
+inet_ntoa(in)
+ struct in_addr in;
+{
+ static const char fmt[] = "%u.%u.%u.%u";
+ static char ret[sizeof "255.255.255.255"];
+ unsigned char *src = (unsigned char *) &in;
+
+ sprintf(ret, fmt, src[0], src[1], src[2], src[3]);
+ return (ret);
+}
+
+/*
+ * Weak aliases for applications that use certain private entry points,
+ * and fail to include <arpa/inet.h>.
+ */
+#undef inet_ntoa
+__weak_reference(__inet_ntoa, inet_ntoa);
diff --git a/stand/libsa/ioctl.c b/stand/libsa/ioctl.c
new file mode 100644
index 0000000..a4512af
--- /dev/null
+++ b/stand/libsa/ioctl.c
@@ -0,0 +1,88 @@
+/* $NetBSD: ioctl.c,v 1.4 1994/10/30 21:48:24 cgd Exp $ */
+
+/*-
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * The Mach Operating System project at Carnegie-Mellon University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ioctl.c 8.1 (Berkeley) 6/11/93
+ *
+ *
+ * Copyright (c) 1989, 1990, 1991 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Author: Alessandro Forin
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie the
+ * rights to redistribute these changes.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "stand.h"
+
+int
+ioctl(fd, cmd, arg)
+ int fd;
+ u_long cmd;
+ char *arg;
+{
+ struct open_file *f = &files[fd];
+
+ if ((unsigned)fd >= SOPEN_MAX || f->f_flags == 0) {
+ errno = EBADF;
+ return (-1);
+ }
+ if (f->f_flags & F_RAW) {
+ errno = (f->f_dev->dv_ioctl)(f, cmd, arg);
+ if (errno)
+ return (-1);
+ return (0);
+ }
+ errno = EIO;
+ return (-1);
+}
diff --git a/stand/libsa/iodesc.h b/stand/libsa/iodesc.h
new file mode 100644
index 0000000..0da1946
--- /dev/null
+++ b/stand/libsa/iodesc.h
@@ -0,0 +1,52 @@
+/* $NetBSD: iodesc.h,v 1.4 1995/09/23 03:31:50 gwr Exp $ */
+
+/*
+ * Copyright (c) 1993 Adam Glass
+ * Copyright (c) 1992 Regents of the University of California.
+ * All rights reserved.
+ *
+ * This software was developed by the Computer Systems Engineering group
+ * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
+ * contributed to Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef __SYS_LIBNETBOOT_IODESC_H
+#define __SYS_LIBNETBOOT_IODESC_H
+
+struct iodesc {
+ struct in_addr destip; /* dest. ip addr, net order */
+ struct in_addr myip; /* local ip addr, net order */
+ u_short destport; /* dest. port, net order */
+ u_short myport; /* local port, net order */
+ u_long xid; /* transaction identification */
+ u_char myea[6]; /* my ethernet address */
+ struct netif *io_netif;
+};
+
+#endif /* __SYS_LIBNETBOOT_IODESC_H */
diff --git a/stand/libsa/ip.c b/stand/libsa/ip.c
new file mode 100644
index 0000000..f2293b3
--- /dev/null
+++ b/stand/libsa/ip.c
@@ -0,0 +1,423 @@
+/*
+ * Copyright (c) 1992 Regents of the University of California.
+ * All rights reserved.
+ *
+ * This software was developed by the Computer Systems Engineering group
+ * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
+ * contributed to Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * The send and receive functions were originally implemented in udp.c and
+ * moved here. Also it is likely some more cleanup can be done, especially
+ * once we will implement the support for tcp.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/queue.h>
+
+#include <string.h>
+
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+#include <netinet/in_systm.h>
+
+#include <netinet/in_pcb.h>
+#include <netinet/ip.h>
+#include <netinet/ip_var.h>
+#include <netinet/udp.h>
+#include <netinet/udp_var.h>
+
+#include "stand.h"
+#include "net.h"
+
+typedef STAILQ_HEAD(ipqueue, ip_queue) ip_queue_t;
+struct ip_queue {
+ void *ipq_pkt;
+ struct ip *ipq_hdr;
+ STAILQ_ENTRY(ip_queue) ipq_next;
+};
+
+/*
+ * Fragment re-assembly queue.
+ */
+struct ip_reasm {
+ struct in_addr ip_src;
+ struct in_addr ip_dst;
+ uint16_t ip_id;
+ uint8_t ip_proto;
+ uint8_t ip_ttl;
+ size_t ip_total_size;
+ ip_queue_t ip_queue;
+ void *ip_pkt;
+ struct ip *ip_hdr;
+ STAILQ_ENTRY(ip_reasm) ip_next;
+};
+
+STAILQ_HEAD(ire_list, ip_reasm) ire_list = STAILQ_HEAD_INITIALIZER(ire_list);
+
+/* Caller must leave room for ethernet and ip headers in front!! */
+ssize_t
+sendip(struct iodesc *d, void *pkt, size_t len, uint8_t proto)
+{
+ ssize_t cc;
+ struct ip *ip;
+ u_char *ea;
+
+#ifdef NET_DEBUG
+ if (debug) {
+ printf("sendip: proto: %x d=%p called.\n", proto, (void *)d);
+ if (d) {
+ printf("saddr: %s:%d",
+ inet_ntoa(d->myip), ntohs(d->myport));
+ printf(" daddr: %s:%d\n",
+ inet_ntoa(d->destip), ntohs(d->destport));
+ }
+ }
+#endif
+
+ ip = (struct ip *)pkt - 1;
+ len += sizeof(*ip);
+
+ bzero(ip, sizeof(*ip));
+
+ ip->ip_v = IPVERSION; /* half-char */
+ ip->ip_hl = sizeof(*ip) >> 2; /* half-char */
+ ip->ip_len = htons(len);
+ ip->ip_p = proto; /* char */
+ ip->ip_ttl = IPDEFTTL; /* char */
+ ip->ip_src = d->myip;
+ ip->ip_dst = d->destip;
+ ip->ip_sum = in_cksum(ip, sizeof(*ip)); /* short, but special */
+
+ if (ip->ip_dst.s_addr == INADDR_BROADCAST || ip->ip_src.s_addr == 0 ||
+ netmask == 0 || SAMENET(ip->ip_src, ip->ip_dst, netmask))
+ ea = arpwhohas(d, ip->ip_dst);
+ else
+ ea = arpwhohas(d, gateip);
+
+ cc = sendether(d, ip, len, ea, ETHERTYPE_IP);
+ if (cc == -1)
+ return (-1);
+ if (cc != len)
+ panic("sendip: bad write (%zd != %zd)", cc, len);
+ return (cc - sizeof(*ip));
+}
+
+static void
+ip_reasm_free(struct ip_reasm *ipr)
+{
+ struct ip_queue *ipq;
+
+ while ((ipq = STAILQ_FIRST(&ipr->ip_queue)) != NULL) {
+ STAILQ_REMOVE_HEAD(&ipr->ip_queue, ipq_next);
+ free(ipq->ipq_pkt);
+ free(ipq);
+ }
+ free(ipr->ip_pkt);
+ free(ipr);
+}
+
+static int
+ip_reasm_add(struct ip_reasm *ipr, void *pkt, struct ip *ip)
+{
+ struct ip_queue *ipq, *prev, *p;
+
+ if ((ipq = calloc(1, sizeof (*ipq))) == NULL)
+ return (1);
+
+ ipq->ipq_pkt = pkt;
+ ipq->ipq_hdr = ip;
+
+ prev = NULL;
+ STAILQ_FOREACH(p, &ipr->ip_queue, ipq_next) {
+ if ((ntohs(p->ipq_hdr->ip_off) & IP_OFFMASK) <
+ (ntohs(ip->ip_off) & IP_OFFMASK)) {
+ prev = p;
+ continue;
+ }
+ if (prev == NULL)
+ break;
+
+ STAILQ_INSERT_AFTER(&ipr->ip_queue, prev, ipq, ipq_next);
+ return (0);
+ }
+ STAILQ_INSERT_HEAD(&ipr->ip_queue, ipq, ipq_next);
+ return (0);
+}
+
+/*
+ * Receive a IP packet and validate it is for us.
+ */
+static ssize_t
+readipv4(struct iodesc *d, void **pkt, void **payload, time_t tleft,
+ uint8_t proto)
+{
+ ssize_t n;
+ size_t hlen;
+ struct ether_header *eh;
+ struct ip *ip;
+ struct udphdr *uh;
+ uint16_t etype; /* host order */
+ char *ptr;
+ struct ip_reasm *ipr;
+ struct ip_queue *ipq, *last;
+
+#ifdef NET_DEBUG
+ if (debug)
+ printf("readip: called\n");
+#endif
+
+ ip = NULL;
+ ptr = NULL;
+ n = readether(d, (void **)&ptr, (void **)&ip, tleft, &etype);
+ if (n == -1 || n < sizeof(*ip) + sizeof(*uh)) {
+ free(ptr);
+ return (-1);
+ }
+
+ /* Ethernet address checks now in readether() */
+
+ /* Need to respond to ARP requests. */
+ if (etype == ETHERTYPE_ARP) {
+ struct arphdr *ah = (void *)ip;
+ if (ah->ar_op == htons(ARPOP_REQUEST)) {
+ /* Send ARP reply */
+ arp_reply(d, ah);
+ }
+ free(ptr);
+ errno = EAGAIN; /* Call me again. */
+ return (-1);
+ }
+
+ if (etype != ETHERTYPE_IP) {
+#ifdef NET_DEBUG
+ if (debug)
+ printf("readip: not IP. ether_type=%x\n", etype);
+#endif
+ free(ptr);
+ return (-1);
+ }
+
+ /* Check ip header */
+ if (ip->ip_v != IPVERSION || /* half char */
+ ip->ip_p != proto) {
+#ifdef NET_DEBUG
+ if (debug) {
+ printf("readip: IP version or proto. ip_v=%d ip_p=%d\n",
+ ip->ip_v, ip->ip_p);
+ }
+#endif
+ free(ptr);
+ return (-1);
+ }
+
+ hlen = ip->ip_hl << 2;
+ if (hlen < sizeof(*ip) ||
+ in_cksum(ip, hlen) != 0) {
+#ifdef NET_DEBUG
+ if (debug)
+ printf("readip: short hdr or bad cksum.\n");
+#endif
+ free(ptr);
+ return (-1);
+ }
+ if (n < ntohs(ip->ip_len)) {
+#ifdef NET_DEBUG
+ if (debug)
+ printf("readip: bad length %d < %d.\n",
+ (int)n, ntohs(ip->ip_len));
+#endif
+ free(ptr);
+ return (-1);
+ }
+ if (d->myip.s_addr && ip->ip_dst.s_addr != d->myip.s_addr) {
+#ifdef NET_DEBUG
+ if (debug) {
+ printf("readip: bad saddr %s != ", inet_ntoa(d->myip));
+ printf("%s\n", inet_ntoa(ip->ip_dst));
+ }
+#endif
+ free(ptr);
+ return (-1);
+ }
+
+ /* Unfragmented packet. */
+ if ((ntohs(ip->ip_off) & IP_MF) == 0 &&
+ (ntohs(ip->ip_off) & IP_OFFMASK) == 0) {
+ uh = (struct udphdr *)((uintptr_t)ip + sizeof (*ip));
+ /* If there were ip options, make them go away */
+ if (hlen != sizeof(*ip)) {
+ bcopy(((u_char *)ip) + hlen, uh, uh->uh_ulen - hlen);
+ ip->ip_len = htons(sizeof(*ip));
+ n -= hlen - sizeof(*ip);
+ }
+
+ n = (n > (ntohs(ip->ip_len) - sizeof(*ip))) ?
+ ntohs(ip->ip_len) - sizeof(*ip) : n;
+ *pkt = ptr;
+ *payload = (void *)((uintptr_t)ip + sizeof(*ip));
+ return (n);
+ }
+
+ STAILQ_FOREACH(ipr, &ire_list, ip_next) {
+ if (ipr->ip_src.s_addr == ip->ip_src.s_addr &&
+ ipr->ip_dst.s_addr == ip->ip_dst.s_addr &&
+ ipr->ip_id == ip->ip_id &&
+ ipr->ip_proto == ip->ip_p)
+ break;
+ }
+
+ /* Allocate new reassembly entry */
+ if (ipr == NULL) {
+ if ((ipr = calloc(1, sizeof (*ipr))) == NULL) {
+ free(ptr);
+ return (-1);
+ }
+
+ ipr->ip_src = ip->ip_src;
+ ipr->ip_dst = ip->ip_dst;
+ ipr->ip_id = ip->ip_id;
+ ipr->ip_proto = ip->ip_p;
+ ipr->ip_ttl = MAXTTL;
+ STAILQ_INIT(&ipr->ip_queue);
+ STAILQ_INSERT_TAIL(&ire_list, ipr, ip_next);
+ }
+
+ if (ip_reasm_add(ipr, ptr, ip) != 0) {
+ STAILQ_REMOVE(&ire_list, ipr, ip_reasm, ip_next);
+ free(ipr);
+ free(ptr);
+ return (-1);
+ }
+
+ if ((ntohs(ip->ip_off) & IP_MF) == 0) {
+ ipr->ip_total_size = (8 * (ntohs(ip->ip_off) & IP_OFFMASK));
+ ipr->ip_total_size += n + sizeof (*ip);
+ ipr->ip_total_size += sizeof (struct ether_header);
+
+ ipr->ip_pkt = malloc(ipr->ip_total_size + 2);
+ if (ipr->ip_pkt == NULL) {
+ STAILQ_REMOVE(&ire_list, ipr, ip_reasm, ip_next);
+ ip_reasm_free(ipr);
+ return (-1);
+ }
+ }
+
+ /*
+ * If we do not have re-assembly buffer ipr->ip_pkt, we are still
+ * missing fragments, so just restart the read.
+ */
+ if (ipr->ip_pkt == NULL) {
+ errno = EAGAIN;
+ return (-1);
+ }
+
+ /*
+ * Walk the packet list in reassembly queue, if we got all the
+ * fragments, build the packet.
+ */
+ n = 0;
+ last = NULL;
+ STAILQ_FOREACH(ipq, &ipr->ip_queue, ipq_next) {
+ if ((ntohs(ipq->ipq_hdr->ip_off) & IP_OFFMASK) != n / 8) {
+ STAILQ_REMOVE(&ire_list, ipr, ip_reasm, ip_next);
+ ip_reasm_free(ipr);
+ return (-1);
+ }
+
+ n += ntohs(ipq->ipq_hdr->ip_len) - (ipq->ipq_hdr->ip_hl << 2);
+ last = ipq;
+ }
+ if ((ntohs(last->ipq_hdr->ip_off) & IP_MF) != 0) {
+ errno = EAGAIN;
+ return (-1);
+ }
+
+ ipq = STAILQ_FIRST(&ipr->ip_queue);
+ /* Fabricate ethernet header */
+ eh = (struct ether_header *)((uintptr_t)ipr->ip_pkt + 2);
+ bcopy((void *)((uintptr_t)ipq->ipq_pkt + 2), eh, sizeof (*eh));
+
+ /* Fabricate IP header */
+ ipr->ip_hdr = (struct ip *)((uintptr_t)eh + sizeof (*eh));
+ bcopy(ipq->ipq_hdr, ipr->ip_hdr, sizeof (*ipr->ip_hdr));
+ ipr->ip_hdr->ip_hl = sizeof (*ipr->ip_hdr) >> 2;
+ ipr->ip_hdr->ip_len = htons(n);
+ ipr->ip_hdr->ip_sum = 0;
+ ipr->ip_hdr->ip_sum = in_cksum(ipr->ip_hdr, sizeof (*ipr->ip_hdr));
+
+ n = 0;
+ ptr = (char *)((uintptr_t)ipr->ip_hdr + sizeof (*ipr->ip_hdr));
+ STAILQ_FOREACH(ipq, &ipr->ip_queue, ipq_next) {
+ char *data;
+ size_t len;
+
+ hlen = ipq->ipq_hdr->ip_hl << 2;
+ len = ntohs(ipq->ipq_hdr->ip_len) - hlen;
+ data = (char *)((uintptr_t)ipq->ipq_hdr + hlen);
+
+ bcopy(data, ptr + n, len);
+ n += len;
+ }
+
+ *pkt = ipr->ip_pkt;
+ ipr->ip_pkt = NULL; /* Avoid free from ip_reasm_free() */
+ *payload = ptr;
+
+ /* Clean up the reassembly list */
+ while ((ipr = STAILQ_FIRST(&ire_list)) != NULL) {
+ STAILQ_REMOVE_HEAD(&ire_list, ip_next);
+ ip_reasm_free(ipr);
+ }
+ return (n);
+}
+
+/*
+ * Receive a IP packet.
+ */
+ssize_t
+readip(struct iodesc *d, void **pkt, void **payload, time_t tleft,
+ uint8_t proto)
+{
+ time_t t;
+ ssize_t ret = -1;
+
+ t = getsecs();
+ while ((getsecs() - t) < tleft) {
+ errno = 0;
+ ret = readipv4(d, pkt, payload, tleft, proto);
+ if (errno != EAGAIN)
+ break;
+ }
+ return (ret);
+}
diff --git a/stand/libsa/libstand.3 b/stand/libsa/libstand.3
new file mode 100644
index 0000000..acb979f
--- /dev/null
+++ b/stand/libsa/libstand.3
@@ -0,0 +1,676 @@
+.\" Copyright (c) Michael Smith
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd August 6, 2004
+.Dt LIBSTAND 3
+.Os
+.Sh NAME
+.Nm libstand
+.Nd support library for standalone executables
+.Sh SYNOPSIS
+.In stand.h
+.Sh DESCRIPTION
+The
+.Nm
+library provides a set of supporting functions for standalone
+applications, mimicking where possible the standard
+.Bx
+programming
+environment.
+The following sections group these functions by kind.
+Unless specifically described here, see the corresponding section 3
+manpages for the given functions.
+.Sh STRING FUNCTIONS
+String functions are available as documented in
+.Xr string 3
+and
+.Xr bstring 3 .
+.Sh MEMORY ALLOCATION
+.Bl -hang -width 10n
+.It Xo
+.Ft "void *"
+.Fn malloc "size_t size"
+.Xc
+.Pp
+Allocate
+.Fa size
+bytes of memory from the heap using a best-fit algorithm.
+.It Xo
+.Ft void
+.Fn free "void *ptr"
+.Xc
+.Pp
+Free the allocated object at
+.Fa ptr .
+.It Xo
+.Ft void
+.Fn setheap "void *start" "void *limit"
+.Xc
+.Pp
+Initialise the heap.
+This function must be called before calling
+.Fn alloc
+for the first time.
+The region between
+.Fa start
+and
+.Fa limit
+will be used for the heap; attempting to allocate beyond this will result
+in a panic.
+.It Xo
+.Ft "char *"
+.Fn sbrk "int junk"
+.Xc
+.Pp
+Provides the behaviour of
+.Fn sbrk 0 ,
+i.e., returns the highest point that the heap has reached.
+This value can
+be used during testing to determine the actual heap usage.
+The
+.Fa junk
+argument is ignored.
+.El
+.Sh ENVIRONMENT
+A set of functions are provided for manipulating a flat variable space similar
+to the traditional shell-supported environment.
+Major enhancements are support
+for set/unset hook functions.
+.Bl -hang -width 10n
+.It Xo
+.Ft "char *"
+.Fn getenv "const char *name"
+.Xc
+.It Xo
+.Ft int
+.Fn setenv "const char *name" "const char *value" "int overwrite"
+.Xc
+.It Xo
+.Ft int
+.Fn putenv "char *string"
+.Xc
+.It Xo
+.Ft int
+.Fn unsetenv "const char *name"
+.Xc
+.Pp
+These functions behave similarly to their standard library counterparts.
+.It Xo
+.Ft "struct env_var *"
+.Fn env_getenv "const char *name"
+.Xc
+.Pp
+Looks up a variable in the environment and returns its entire
+data structure.
+.It Xo
+.Ft int
+.Fn env_setenv "const char *name" "int flags" "const void *value" "ev_sethook_t sethook" "ev_unsethook_t unsethook"
+.Xc
+.Pp
+Creates a new or sets an existing environment variable called
+.Fa name .
+If creating a new variable, the
+.Fa sethook
+and
+.Fa unsethook
+arguments may be specified.
+.Pp
+The set hook is invoked whenever an attempt
+is made to set the variable, unless the EV_NOHOOK flag is set.
+Typically
+a set hook will validate the
+.Fa value
+argument, and then call
+.Fn env_setenv
+again with EV_NOHOOK set to actually save the value.
+The predefined function
+.Fn env_noset
+may be specified to refuse all attempts to set a variable.
+.Pp
+The unset hook is invoked when an attempt is made to unset a variable.
+If it
+returns zero, the variable will be unset.
+The predefined function
+.Fa env_nounset
+may be used to prevent a variable being unset.
+.El
+.Sh STANDARD LIBRARY SUPPORT
+.Bl -hang -width 10n
+.It Xo
+.Ft int
+.Fn getopt "int argc" "char * const *argv" "const char *optstring"
+.Xc
+.It Xo
+.Ft long
+.Fn strtol "const char *nptr" "char **endptr" "int base"
+.Xc
+.It Xo
+.Ft void
+.Fn srandom "unsigned long seed"
+.Xc
+.It Xo
+.Ft "long"
+.Fn random void
+.Xc
+.It Xo
+.Ft "char *"
+.Fn strerror "int error"
+.Xc
+.Pp
+Returns error messages for the subset of errno values supported by
+.Nm .
+.It Fn assert expression
+.Pp
+Requires
+.In assert.h .
+.It Xo
+.Ft int
+.Fn setjmp "jmp_buf env"
+.Xc
+.It Xo
+.Ft void
+.Fn longjmp "jmp_buf env" "int val"
+.Xc
+.Pp
+Defined as
+.Fn _setjmp
+and
+.Fn _longjmp
+respectively as there is no signal state to manipulate.
+Requires
+.In setjmp.h .
+.El
+.Sh CHARACTER I/O
+.Bl -hang -width 10n
+.It Xo
+.Ft void
+.Fn gets "char *buf"
+.Xc
+.Pp
+Read characters from the console into
+.Fa buf .
+All of the standard cautions apply to this function.
+.It Xo
+.Ft void
+.Fn ngets "char *buf" "int size"
+.Xc
+.Pp
+Read at most
+.Fa size
+- 1 characters from the console into
+.Fa buf .
+If
+.Fa size
+is less than 1, the function's behaviour is as for
+.Fn gets .
+.It Xo
+.Ft int
+.Fn fgetstr "char *buf" "int size" "int fd"
+.Xc
+.Pp
+Read a line of at most
+.Fa size
+characters into
+.Fa buf .
+Line terminating characters are stripped, and the buffer is always
+.Dv NUL
+terminated.
+Returns the number of characters in
+.Fa buf
+if successful, or -1 if a read error occurs.
+.It Xo
+.Ft int
+.Fn printf "const char *fmt" "..."
+.Xc
+.It Xo
+.Ft void
+.Fn vprintf "const char *fmt" "va_list ap"
+.Xc
+.It Xo
+.Ft int
+.Fn sprintf "char *buf" "const char *fmt" "..."
+.Xc
+.It Xo
+.Ft void
+.Fn vsprintf "char *buf" "const char *fmt" "va_list ap"
+.Xc
+.Pp
+The *printf functions implement a subset of the standard
+.Fn printf
+family functionality and some extensions.
+The following standard conversions
+are supported: c,d,n,o,p,s,u,x.
+The following modifiers are supported:
++,-,#,*,0,field width,precision,l.
+.Pp
+The
+.Li b
+conversion is provided to decode error registers.
+Its usage is:
+.Bd -ragged -offset indent
+printf(
+.Qq reg=%b\en ,
+regval,
+.Qq <base><arg>*
+);
+.Ed
+.Pp
+where <base> is the output expressed as a control character, e.g.\& \e10 gives
+octal, \e20 gives hex.
+Each <arg> is a sequence of characters, the first of
+which gives the bit number to be inspected (origin 1) and the next characters
+(up to a character less than 32) give the text to be displayed if the bit is set.
+Thus
+.Bd -ragged -offset indent
+printf(
+.Qq reg=%b\en ,
+3,
+.Qq \e10\e2BITTWO\e1BITONE
+);
+.Ed
+.Pp
+would give the output
+.Bd -ragged -offset indent
+reg=3<BITTWO,BITONE>
+.Ed
+.Pp
+The
+.Li D
+conversion provides a hexdump facility, e.g.
+.Bd -ragged -offset indent
+printf(
+.Qq %6D ,
+ptr,
+.Qq \&:
+); gives
+.Qq XX:XX:XX:XX:XX:XX
+.Ed
+.Bd -ragged -offset indent
+printf(
+.Qq %*D ,
+len,
+ptr,
+.Qq "\ "
+); gives
+.Qq XX XX XX ...
+.Ed
+.El
+.Sh CHARACTER TESTS AND CONVERSIONS
+.Bl -hang -width 10n
+.It Xo
+.Ft int
+.Fn isupper "int c"
+.Xc
+.It Xo
+.Ft int
+.Fn islower "int c"
+.Xc
+.It Xo
+.Ft int
+.Fn isspace "int c"
+.Xc
+.It Xo
+.Ft int
+.Fn isdigit "int c"
+.Xc
+.It Xo
+.Ft int
+.Fn isxdigit "int c"
+.Xc
+.It Xo
+.Ft int
+.Fn isascii "int c"
+.Xc
+.It Xo
+.Ft int
+.Fn isalpha "int c"
+.Xc
+.It Xo
+.Ft int
+.Fn toupper "int c"
+.Xc
+.It Xo
+.Ft int
+.Fn tolower "int c"
+.Xc
+.El
+.Sh FILE I/O
+.Bl -hang -width 10n
+.It Xo
+.Ft int
+.Fn open "const char *path" "int flags"
+.Xc
+.Pp
+Similar to the behaviour as specified in
+.Xr open 2 ,
+except that file creation is not supported, so the mode parameter is not
+required.
+The
+.Fa flags
+argument may be one of O_RDONLY, O_WRONLY and O_RDWR (although no file systems
+currently support writing).
+.It Xo
+.Ft int
+.Fn close "int fd"
+.Xc
+.It Xo
+.Ft void
+.Fn closeall void
+.Xc
+.Pp
+Close all open files.
+.It Xo
+.Ft ssize_t
+.Fn read "int fd" "void *buf" "size_t len"
+.Xc
+.It Xo
+.Ft ssize_t
+.Fn write "int fd" "void *buf" "size_t len"
+.Xc
+.Pp
+(No file systems currently support writing.)
+.It Xo
+.Ft off_t
+.Fn lseek "int fd" "off_t offset" "int whence"
+.Xc
+.Pp
+Files being automatically uncompressed during reading cannot seek backwards
+from the current point.
+.It Xo
+.Ft int
+.Fn stat "const char *path" "struct stat *sb"
+.Xc
+.It Xo
+.Ft int
+.Fn fstat "int fd" "struct stat *sb"
+.Xc
+.Pp
+The
+.Fn stat
+and
+.Fn fstat
+functions only fill out the following fields in the
+.Fa sb
+structure: st_mode,st_nlink,st_uid,st_gid,st_size.
+The
+.Nm tftp
+file system cannot provide meaningful values for this call, and the
+.Nm cd9660
+file system always reports files having uid/gid of zero.
+.El
+.Sh PAGER
+The
+.Nm
+library supplies a simple internal pager to ease reading the output of large
+commands.
+.Bl -hang -width 10n
+.It Xo
+.Ft void
+.Fn pager_open
+.Xc
+.Pp
+Initialises the pager and tells it that the next line output will be the top of the
+display.
+The environment variable LINES is consulted to determine the number of
+lines to be displayed before pausing.
+.It Xo
+.Ft void
+.Fn pager_close void
+.Xc
+.Pp
+Closes the pager.
+.It Xo
+.Ft int
+.Fn pager_output "const char *lines"
+.Xc
+.Pp
+Sends the lines in the
+.Dv NUL Ns
+-terminated buffer at
+.Fa lines
+to the pager.
+Newline characters are counted in order to determine the number
+of lines being output (wrapped lines are not accounted for).
+The
+.Fn pager_output
+function will return zero when all of the lines have been output, or nonzero
+if the display was paused and the user elected to quit.
+.It Xo
+.Ft int
+.Fn pager_file "const char *fname"
+.Xc
+.Pp
+Attempts to open and display the file
+.Fa fname .
+Returns -1 on error, 0 at EOF, or 1 if the user elects to quit while reading.
+.El
+.Sh MISC
+.Bl -hang -width 10n
+.It Xo
+.Ft void
+.Fn twiddle void
+.Xc
+.Pp
+Successive calls emit the characters in the sequence |,/,-,\\ followed by a
+backspace in order to provide reassurance to the user.
+.El
+.Sh REQUIRED LOW-LEVEL SUPPORT
+The following resources are consumed by
+.Nm
+- stack, heap, console and devices.
+.Pp
+The stack must be established before
+.Nm
+functions can be invoked.
+Stack requirements vary depending on the functions
+and file systems used by the consumer and the support layer functions detailed
+below.
+.Pp
+The heap must be established before calling
+.Fn alloc
+or
+.Fn open
+by calling
+.Fn setheap .
+Heap usage will vary depending on the number of simultaneously open files,
+as well as client behaviour.
+Automatic decompression will allocate more
+than 64K of data per open file.
+.Pp
+Console access is performed via the
+.Fn getchar ,
+.Fn putchar
+and
+.Fn ischar
+functions detailed below.
+.Pp
+Device access is initiated via
+.Fn devopen
+and is performed through the
+.Fn dv_strategy ,
+.Fn dv_ioctl
+and
+.Fn dv_close
+functions in the device switch structure that
+.Fn devopen
+returns.
+.Pp
+The consumer must provide the following support functions:
+.Bl -hang -width 10n
+.It Xo
+.Ft int
+.Fn getchar void
+.Xc
+.Pp
+Return a character from the console, used by
+.Fn gets ,
+.Fn ngets
+and pager functions.
+.It Xo
+.Ft int
+.Fn ischar void
+.Xc
+.Pp
+Returns nonzero if a character is waiting from the console.
+.It Xo
+.Ft void
+.Fn putchar int
+.Xc
+.Pp
+Write a character to the console, used by
+.Fn gets ,
+.Fn ngets ,
+.Fn *printf ,
+.Fn panic
+and
+.Fn twiddle
+and thus by many other functions for debugging and informational output.
+.It Xo
+.Ft int
+.Fn devopen "struct open_file *of" "const char *name" "const char **file"
+.Xc
+.Pp
+Open the appropriate device for the file named in
+.Fa name ,
+returning in
+.Fa file
+a pointer to the remaining body of
+.Fa name
+which does not refer to the device.
+The
+.Va f_dev
+field in
+.Fa of
+will be set to point to the
+.Vt devsw
+structure for the opened device if successful.
+Device identifiers must
+always precede the path component, but may otherwise be arbitrarily formatted.
+Used by
+.Fn open
+and thus for all device-related I/O.
+.It Xo
+.Ft int
+.Fn devclose "struct open_file *of"
+.Xc
+.Pp
+Close the device allocated for
+.Fa of .
+The device driver itself will already have been called for the close; this call
+should clean up any allocation made by devopen only.
+.It Xo
+.Ft void
+.Fn panic "const char *msg" "..."
+.Xc
+.Pp
+Signal a fatal and unrecoverable error condition.
+The
+.Fa msg ...
+arguments are as for
+.Fn printf .
+.El
+.Sh INTERNAL FILE SYSTEMS
+Internal file systems are enabled by the consumer exporting the array
+.Vt struct fs_ops *file_system[] ,
+which should be initialised with pointers
+to
+.Vt struct fs_ops
+structures.
+The following file system handlers are supplied by
+.Nm ,
+the consumer may supply other file systems of their own:
+.Bl -hang -width ".Va cd9660_fsops"
+.It Va ufs_fsops
+The
+.Bx
+UFS.
+.It Va ext2fs_fsops
+Linux ext2fs file system.
+.It Va tftp_fsops
+File access via TFTP.
+.It Va nfs_fsops
+File access via NFS.
+.It Va cd9660_fsops
+ISO 9660 (CD-ROM) file system.
+.It Va gzipfs_fsops
+Stacked file system supporting gzipped files.
+When trying the gzipfs file system,
+.Nm
+appends
+.Li .gz
+to the end of the filename, and then tries to locate the file using the other
+file systems.
+Placement of this file system in the
+.Va file_system[]
+array determines whether gzipped files will be opened in preference to non-gzipped
+files.
+It is only possible to seek a gzipped file forwards, and
+.Fn stat
+and
+.Fn fstat
+on gzipped files will report an invalid length.
+.It Va bzipfs_fsops
+The same as
+.Va gzipfs_fsops ,
+but for
+.Xr bzip2 1 Ns -compressed
+files.
+.El
+.Pp
+The array of
+.Vt struct fs_ops
+pointers should be terminated with a NULL.
+.Sh DEVICES
+Devices are exported by the supporting code via the array
+.Vt struct devsw *devsw[]
+which is a NULL terminated array of pointers to device switch structures.
+.Sh HISTORY
+The
+.Nm
+library contains contributions from many sources, including:
+.Bl -bullet -compact
+.It
+.Nm libsa
+from
+.Nx
+.It
+.Nm libc
+and
+.Nm libkern
+from
+.Fx 3.0 .
+.It
+.Nm zalloc
+from
+.An Matthew Dillon Aq Mt dillon@backplane.com
+.El
+.Pp
+The reorganisation and port to
+.Fx 3.0 ,
+the environment functions and this manpage were written by
+.An Mike Smith Aq Mt msmith@FreeBSD.org .
+.Sh BUGS
+The lack of detailed memory usage data is unhelpful.
diff --git a/stand/libsa/lseek.c b/stand/libsa/lseek.c
new file mode 100644
index 0000000..b9debd1
--- /dev/null
+++ b/stand/libsa/lseek.c
@@ -0,0 +1,141 @@
+/* $NetBSD: lseek.c,v 1.4 1997/01/22 00:38:10 cgd Exp $ */
+
+/*-
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * The Mach Operating System project at Carnegie-Mellon University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)lseek.c 8.1 (Berkeley) 6/11/93
+ *
+ *
+ * Copyright (c) 1989, 1990, 1991 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Author: Alessandro Forin
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie the
+ * rights to redistribute these changes.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "stand.h"
+
+off_t
+lseek(int fd, off_t offset, int where)
+{
+ off_t bufpos, filepos, target;
+ struct open_file *f = &files[fd];
+
+ if ((unsigned)fd >= SOPEN_MAX || f->f_flags == 0) {
+ errno = EBADF;
+ return (-1);
+ }
+
+ if (f->f_flags & F_RAW) {
+ /*
+ * On RAW devices, update internal offset.
+ */
+ switch (where) {
+ case SEEK_SET:
+ f->f_offset = offset;
+ break;
+ case SEEK_CUR:
+ f->f_offset += offset;
+ break;
+ default:
+ errno = EOFFSET;
+ return (-1);
+ }
+ return (f->f_offset);
+ }
+
+ /*
+ * If there is some unconsumed data in the readahead buffer and it
+ * contains the desired offset, simply adjust the buffer offset and
+ * length. We don't bother with SEEK_END here, since the code to
+ * handle it would fail in the same cases where the non-readahead
+ * code fails (namely, for streams which cannot seek backward and whose
+ * size isn't known in advance).
+ */
+ if (f->f_ralen != 0 && where != SEEK_END) {
+ if ((filepos = (f->f_ops->fo_seek)(f, (off_t)0, SEEK_CUR)) == -1)
+ return (-1);
+ bufpos = filepos - f->f_ralen;
+ switch (where) {
+ case SEEK_SET:
+ target = offset;
+ break;
+ case SEEK_CUR:
+ target = bufpos + offset;
+ break;
+ default:
+ errno = EINVAL;
+ return (-1);
+ }
+ if (bufpos <= target && target < filepos) {
+ f->f_raoffset += target - bufpos;
+ f->f_ralen -= target - bufpos;
+ return (target);
+ }
+ }
+
+ /*
+ * If this is a relative seek, we need to correct the offset for
+ * bytes that we have already read but the caller doesn't know
+ * about.
+ */
+ if (where == SEEK_CUR)
+ offset -= f->f_ralen;
+
+ /*
+ * Invalidate the readahead buffer.
+ */
+ f->f_ralen = 0;
+
+ return (f->f_ops->fo_seek)(f, offset, where);
+}
diff --git a/stand/libsa/mips/_setjmp.S b/stand/libsa/mips/_setjmp.S
new file mode 100644
index 0000000..0289b0e
--- /dev/null
+++ b/stand/libsa/mips/_setjmp.S
@@ -0,0 +1,108 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Ralph Campbell.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <machine/regnum.h>
+#include <machine/asm.h>
+
+#if 0
+#if defined(LIBC_SCCS)
+ .text
+ .asciz "$OpenBSD: _setjmp.S,v 1.6 1996/09/23 21:27:53 imp Exp $"
+#endif /* LIBC_SCCS */
+#endif
+
+/*
+ * C library -- _setjmp, _longjmp
+ *
+ * _longjmp(a,v)
+ * will generate a "return(v)" from
+ * the last call to
+ * _setjmp(a)
+ * by restoring registers from the stack,
+ * The previous signal state is NOT restored.
+ */
+
+LEAF(_setjmp)
+ .set noreorder
+ REG_LI v0, 0xACEDBADE # sigcontext magic number
+ REG_S ra, (2 * SZREG)(a0) # sc_pc = return address
+ REG_S v0, (3 * SZREG)(a0) # saved in sc_regs[0]
+ REG_S s0, ((S0 + 3) * SZREG)(a0)
+ REG_S s1, ((S1 + 3) * SZREG)(a0)
+ REG_S s2, ((S2 + 3) * SZREG)(a0)
+ REG_S s3, ((S3 + 3) * SZREG)(a0)
+ REG_S s4, ((S4 + 3) * SZREG)(a0)
+ REG_S s5, ((S5 + 3) * SZREG)(a0)
+ REG_S s6, ((S6 + 3) * SZREG)(a0)
+ REG_S s7, ((S7 + 3) * SZREG)(a0)
+ REG_S sp, ((SP + 3) * SZREG)(a0)
+ REG_S s8, ((S8 + 3) * SZREG)(a0)
+ j ra
+ move v0, zero
+END(_setjmp)
+
+LEAF(_longjmp)
+#ifdef ABICALLS
+ subu sp, sp, 32
+ .cprestore 16
+#endif
+ .set noreorder
+ REG_L v0, (3 * SZREG)(a0) # get magic number
+ REG_L ra, (2 * SZREG)(a0)
+ bne v0, 0xACEDBADE, botch # jump if error
+
+ addu sp, sp, 32 # does not matter, sanity
+ REG_L s0, ((S0 + 3) * SZREG)(a0)
+ REG_L s1, ((S1 + 3) * SZREG)(a0)
+ REG_L s2, ((S2 + 3) * SZREG)(a0)
+ REG_L s3, ((S3 + 3) * SZREG)(a0)
+ REG_L s4, ((S4 + 3) * SZREG)(a0)
+ REG_L s5, ((S5 + 3) * SZREG)(a0)
+ REG_L s6, ((S6 + 3) * SZREG)(a0)
+ REG_L s7, ((S7 + 3) * SZREG)(a0)
+ REG_L sp, ((SP + 3) * SZREG)(a0)
+ REG_L s8, ((S8 + 3) * SZREG)(a0)
+
+ j ra
+ move v0, a1
+botch:
+ jal _C_LABEL(longjmperror)
+ nop
+ jal _C_LABEL(abort)
+ nop
+END(_longjmp)
diff --git a/stand/libsa/nandfs.c b/stand/libsa/nandfs.c
new file mode 100644
index 0000000..fe3a8ab
--- /dev/null
+++ b/stand/libsa/nandfs.c
@@ -0,0 +1,1061 @@
+/*-
+ * Copyright (c) 2010-2012 Semihalf.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/queue.h>
+#include <sys/stdint.h>
+#include <ufs/ufs/dinode.h>
+#include <fs/nandfs/nandfs_fs.h>
+#include "stand.h"
+#include "string.h"
+#include "zlib.h"
+
+#define DEBUG
+#undef DEBUG
+#ifdef DEBUG
+#define NANDFS_DEBUG(fmt, args...) do { \
+ printf("NANDFS_DEBUG:" fmt "\n", ##args); } while (0)
+#else
+#define NANDFS_DEBUG(fmt, args...)
+#endif
+
+struct nandfs_mdt {
+ uint32_t entries_per_block;
+ uint32_t entries_per_group;
+ uint32_t blocks_per_group;
+ uint32_t groups_per_desc_block; /* desc is super group */
+ uint32_t blocks_per_desc_block; /* desc is super group */
+};
+
+struct bmap_buf {
+ LIST_ENTRY(bmap_buf) list;
+ nandfs_daddr_t blknr;
+ uint64_t *map;
+};
+
+struct nandfs_node {
+ struct nandfs_inode *inode;
+ LIST_HEAD(, bmap_buf) bmap_bufs;
+};
+struct nandfs {
+ int nf_blocksize;
+ int nf_sectorsize;
+ int nf_cpno;
+
+ struct open_file *nf_file;
+ struct nandfs_node *nf_opened_node;
+ u_int nf_offset;
+ uint8_t *nf_buf;
+ int64_t nf_buf_blknr;
+
+ struct nandfs_fsdata *nf_fsdata;
+ struct nandfs_super_block *nf_sb;
+ struct nandfs_segment_summary nf_segsum;
+ struct nandfs_checkpoint nf_checkpoint;
+ struct nandfs_super_root nf_sroot;
+ struct nandfs_node nf_ifile;
+ struct nandfs_node nf_datfile;
+ struct nandfs_node nf_cpfile;
+ struct nandfs_mdt nf_datfile_mdt;
+ struct nandfs_mdt nf_ifile_mdt;
+
+ int nf_nindir[NIADDR];
+};
+
+static int nandfs_open(const char *, struct open_file *);
+static int nandfs_close(struct open_file *);
+static int nandfs_read(struct open_file *, void *, size_t, size_t *);
+static off_t nandfs_seek(struct open_file *, off_t, int);
+static int nandfs_stat(struct open_file *, struct stat *);
+static int nandfs_readdir(struct open_file *, struct dirent *);
+
+static int nandfs_buf_read(struct nandfs *, void **, size_t *);
+static struct nandfs_node *nandfs_lookup_path(struct nandfs *, const char *);
+static int nandfs_read_inode(struct nandfs *, struct nandfs_node *,
+ nandfs_lbn_t, u_int, void *, int);
+static int nandfs_read_blk(struct nandfs *, nandfs_daddr_t, void *, int);
+static int nandfs_bmap_lookup(struct nandfs *, struct nandfs_node *,
+ nandfs_lbn_t, nandfs_daddr_t *, int);
+static int nandfs_get_checkpoint(struct nandfs *, uint64_t,
+ struct nandfs_checkpoint *);
+static nandfs_daddr_t nandfs_vtop(struct nandfs *, nandfs_daddr_t);
+static void nandfs_calc_mdt_consts(int, struct nandfs_mdt *, int);
+static void nandfs_mdt_trans(struct nandfs_mdt *, uint64_t,
+ nandfs_daddr_t *, uint32_t *);
+static int ioread(struct open_file *, off_t, void *, u_int);
+static int nandfs_probe_sectorsize(struct open_file *);
+
+struct fs_ops nandfs_fsops = {
+ "nandfs",
+ nandfs_open,
+ nandfs_close,
+ nandfs_read,
+ null_write,
+ nandfs_seek,
+ nandfs_stat,
+ nandfs_readdir
+};
+
+#define NINDIR(fs) ((fs)->nf_blocksize / sizeof(nandfs_daddr_t))
+
+/* from NetBSD's src/sys/net/if_ethersubr.c */
+static uint32_t
+nandfs_crc32(uint32_t crc, const uint8_t *buf, size_t len)
+{
+ static const uint32_t crctab[] = {
+ 0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac,
+ 0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c,
+ 0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c,
+ 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c
+ };
+ size_t i;
+
+ crc = crc ^ ~0U;
+ for (i = 0; i < len; i++) {
+ crc ^= buf[i];
+ crc = (crc >> 4) ^ crctab[crc & 0xf];
+ crc = (crc >> 4) ^ crctab[crc & 0xf];
+ }
+ return (crc ^ ~0U);
+}
+
+static int
+nandfs_check_fsdata_crc(struct nandfs_fsdata *fsdata)
+{
+ uint32_t fsdata_crc, comp_crc;
+
+ if (fsdata->f_magic != NANDFS_FSDATA_MAGIC)
+ return (0);
+
+ /* Preserve crc */
+ fsdata_crc = fsdata->f_sum;
+
+ /* Calculate */
+ fsdata->f_sum = (0);
+ comp_crc = nandfs_crc32(0, (uint8_t *)fsdata, fsdata->f_bytes);
+
+ /* Restore */
+ fsdata->f_sum = fsdata_crc;
+
+ /* Check CRC */
+ return (fsdata_crc == comp_crc);
+}
+
+static int
+nandfs_check_superblock_crc(struct nandfs_fsdata *fsdata,
+ struct nandfs_super_block *super)
+{
+ uint32_t super_crc, comp_crc;
+
+ /* Check super block magic */
+ if (super->s_magic != NANDFS_SUPER_MAGIC)
+ return (0);
+
+ /* Preserve CRC */
+ super_crc = super->s_sum;
+
+ /* Calculate */
+ super->s_sum = (0);
+ comp_crc = nandfs_crc32(0, (uint8_t *)super, fsdata->f_sbbytes);
+
+ /* Restore */
+ super->s_sum = super_crc;
+
+ /* Check CRC */
+ return (super_crc == comp_crc);
+}
+
+static int
+nandfs_find_super_block(struct nandfs *fs, struct open_file *f)
+{
+ struct nandfs_super_block *sb;
+ int i, j, n, s;
+ int sectors_to_read, error;
+
+ sb = malloc(fs->nf_sectorsize);
+ if (sb == NULL)
+ return (ENOMEM);
+
+ memset(fs->nf_sb, 0, sizeof(*fs->nf_sb));
+
+ sectors_to_read = (NANDFS_NFSAREAS * fs->nf_fsdata->f_erasesize) /
+ fs->nf_sectorsize;
+ for (i = 0; i < sectors_to_read; i++) {
+ NANDFS_DEBUG("reading i %d offset %d\n", i,
+ i * fs->nf_sectorsize);
+ error = ioread(f, i * fs->nf_sectorsize, (char *)sb,
+ fs->nf_sectorsize);
+ if (error) {
+ NANDFS_DEBUG("error %d\n", error);
+ continue;
+ }
+ n = fs->nf_sectorsize / sizeof(struct nandfs_super_block);
+ s = 0;
+ if ((i * fs->nf_sectorsize) % fs->nf_fsdata->f_erasesize == 0) {
+ if (fs->nf_sectorsize == sizeof(struct nandfs_fsdata))
+ continue;
+ else {
+ s += (sizeof(struct nandfs_fsdata) /
+ sizeof(struct nandfs_super_block));
+ }
+ }
+
+ for (j = s; j < n; j++) {
+ if (!nandfs_check_superblock_crc(fs->nf_fsdata, &sb[j]))
+ continue;
+ NANDFS_DEBUG("magic %x wtime %jd, lastcp 0x%jx\n",
+ sb[j].s_magic, sb[j].s_wtime, sb[j].s_last_cno);
+ if (sb[j].s_last_cno > fs->nf_sb->s_last_cno)
+ memcpy(fs->nf_sb, &sb[j], sizeof(*fs->nf_sb));
+ }
+ }
+
+ free(sb);
+
+ return (fs->nf_sb->s_magic != 0 ? 0 : EINVAL);
+}
+
+static int
+nandfs_find_fsdata(struct nandfs *fs, struct open_file *f)
+{
+ int offset, error, i;
+
+ NANDFS_DEBUG("starting\n");
+
+ offset = 0;
+ for (i = 0; i < 64 * NANDFS_NFSAREAS; i++) {
+ error = ioread(f, offset, (char *)fs->nf_fsdata,
+ sizeof(struct nandfs_fsdata));
+ if (error)
+ return (error);
+ if (fs->nf_fsdata->f_magic == NANDFS_FSDATA_MAGIC) {
+ NANDFS_DEBUG("found at %x, volume %s\n", offset,
+ fs->nf_fsdata->f_volume_name);
+ if (nandfs_check_fsdata_crc(fs->nf_fsdata))
+ break;
+ }
+ offset += fs->nf_sectorsize;
+ }
+
+ return (error);
+}
+
+static int
+nandfs_read_structures(struct nandfs *fs, struct open_file *f)
+{
+ int error;
+
+ error = nandfs_find_fsdata(fs, f);
+ if (error)
+ return (error);
+
+ error = nandfs_find_super_block(fs, f);
+
+ if (error == 0)
+ NANDFS_DEBUG("selected sb with w_time %jd last_pseg %jx\n",
+ fs->nf_sb->s_wtime, fs->nf_sb->s_last_pseg);
+
+ return (error);
+}
+
+static int
+nandfs_mount(struct nandfs *fs, struct open_file *f)
+{
+ int err = 0, level;
+ uint64_t last_pseg;
+
+ fs->nf_fsdata = malloc(sizeof(struct nandfs_fsdata));
+ fs->nf_sb = malloc(sizeof(struct nandfs_super_block));
+
+ err = nandfs_read_structures(fs, f);
+ if (err) {
+ free(fs->nf_fsdata);
+ free(fs->nf_sb);
+ return (err);
+ }
+
+ fs->nf_blocksize = 1 << (fs->nf_fsdata->f_log_block_size + 10);
+
+ NANDFS_DEBUG("using superblock with wtime %jd\n", fs->nf_sb->s_wtime);
+
+ fs->nf_cpno = fs->nf_sb->s_last_cno;
+ last_pseg = fs->nf_sb->s_last_pseg;
+
+ /*
+ * Calculate indirect block levels.
+ */
+ nandfs_daddr_t mult;
+
+ mult = 1;
+ for (level = 0; level < NIADDR; level++) {
+ mult *= NINDIR(fs);
+ fs->nf_nindir[level] = mult;
+ }
+
+ nandfs_calc_mdt_consts(fs->nf_blocksize, &fs->nf_datfile_mdt,
+ fs->nf_fsdata->f_dat_entry_size);
+
+ nandfs_calc_mdt_consts(fs->nf_blocksize, &fs->nf_ifile_mdt,
+ fs->nf_fsdata->f_inode_size);
+
+ err = ioread(f, last_pseg * fs->nf_blocksize, &fs->nf_segsum,
+ sizeof(struct nandfs_segment_summary));
+ if (err) {
+ free(fs->nf_sb);
+ free(fs->nf_fsdata);
+ return (err);
+ }
+
+ err = ioread(f, (last_pseg + fs->nf_segsum.ss_nblocks - 1) *
+ fs->nf_blocksize, &fs->nf_sroot, sizeof(struct nandfs_super_root));
+ if (err) {
+ free(fs->nf_sb);
+ free(fs->nf_fsdata);
+ return (err);
+ }
+
+ fs->nf_datfile.inode = &fs->nf_sroot.sr_dat;
+ LIST_INIT(&fs->nf_datfile.bmap_bufs);
+ fs->nf_cpfile.inode = &fs->nf_sroot.sr_cpfile;
+ LIST_INIT(&fs->nf_cpfile.bmap_bufs);
+
+ err = nandfs_get_checkpoint(fs, fs->nf_cpno, &fs->nf_checkpoint);
+ if (err) {
+ free(fs->nf_sb);
+ free(fs->nf_fsdata);
+ return (err);
+ }
+
+ NANDFS_DEBUG("checkpoint cp_cno=%lld\n", fs->nf_checkpoint.cp_cno);
+ NANDFS_DEBUG("checkpoint cp_inodes_count=%lld\n",
+ fs->nf_checkpoint.cp_inodes_count);
+ NANDFS_DEBUG("checkpoint cp_ifile_inode.i_blocks=%lld\n",
+ fs->nf_checkpoint.cp_ifile_inode.i_blocks);
+
+ fs->nf_ifile.inode = &fs->nf_checkpoint.cp_ifile_inode;
+ LIST_INIT(&fs->nf_ifile.bmap_bufs);
+ return (0);
+}
+
+#define NINDIR(fs) ((fs)->nf_blocksize / sizeof(nandfs_daddr_t))
+
+static int
+nandfs_open(const char *path, struct open_file *f)
+{
+ struct nandfs *fs;
+ struct nandfs_node *node;
+ int err, bsize, level;
+
+ NANDFS_DEBUG("nandfs_open('%s', %p)\n", path, f);
+
+ fs = malloc(sizeof(struct nandfs));
+ f->f_fsdata = fs;
+ fs->nf_file = f;
+
+ bsize = nandfs_probe_sectorsize(f);
+ if (bsize < 0) {
+ printf("Cannot probe medium sector size\n");
+ return (EINVAL);
+ }
+
+ fs->nf_sectorsize = bsize;
+
+ /*
+ * Calculate indirect block levels.
+ */
+ nandfs_daddr_t mult;
+
+ mult = 1;
+ for (level = 0; level < NIADDR; level++) {
+ mult *= NINDIR(fs);
+ fs->nf_nindir[level] = mult;
+ }
+
+ NANDFS_DEBUG("fs %p nf_sectorsize=%x\n", fs, fs->nf_sectorsize);
+
+ err = nandfs_mount(fs, f);
+ if (err) {
+ NANDFS_DEBUG("Cannot mount nandfs: %s\n", strerror(err));
+ return (err);
+ }
+
+ node = nandfs_lookup_path(fs, path);
+ if (node == NULL)
+ return (EINVAL);
+
+ fs->nf_offset = 0;
+ fs->nf_buf = NULL;
+ fs->nf_buf_blknr = -1;
+ fs->nf_opened_node = node;
+ LIST_INIT(&fs->nf_opened_node->bmap_bufs);
+ return (0);
+}
+
+static void
+nandfs_free_node(struct nandfs_node *node)
+{
+ struct bmap_buf *bmap, *tmp;
+
+ free(node->inode);
+ LIST_FOREACH_SAFE(bmap, &node->bmap_bufs, list, tmp) {
+ LIST_REMOVE(bmap, list);
+ free(bmap->map);
+ free(bmap);
+ }
+ free(node);
+}
+
+static int
+nandfs_close(struct open_file *f)
+{
+ struct nandfs *fs = f->f_fsdata;
+
+ NANDFS_DEBUG("nandfs_close(%p)\n", f);
+
+ if (fs->nf_buf != NULL)
+ free(fs->nf_buf);
+
+ nandfs_free_node(fs->nf_opened_node);
+ free(fs->nf_sb);
+ free(fs);
+ return (0);
+}
+
+static int
+nandfs_read(struct open_file *f, void *addr, size_t size, size_t *resid)
+{
+ struct nandfs *fs = (struct nandfs *)f->f_fsdata;
+ size_t csize, buf_size;
+ void *buf;
+ int error = 0;
+
+ NANDFS_DEBUG("nandfs_read(file=%p, addr=%p, size=%d)\n", f, addr, size);
+
+ while (size != 0) {
+ if (fs->nf_offset >= fs->nf_opened_node->inode->i_size)
+ break;
+
+ error = nandfs_buf_read(fs, &buf, &buf_size);
+ if (error)
+ break;
+
+ csize = size;
+ if (csize > buf_size)
+ csize = buf_size;
+
+ bcopy(buf, addr, csize);
+
+ fs->nf_offset += csize;
+ addr = (char *)addr + csize;
+ size -= csize;
+ }
+
+ if (resid)
+ *resid = size;
+ return (error);
+}
+
+static off_t
+nandfs_seek(struct open_file *f, off_t offset, int where)
+{
+ struct nandfs *fs = f->f_fsdata;
+ off_t off;
+ u_int size;
+
+ NANDFS_DEBUG("nandfs_seek(file=%p, offset=%lld, where=%d)\n", f,
+ offset, where);
+
+ size = fs->nf_opened_node->inode->i_size;
+
+ switch (where) {
+ case SEEK_SET:
+ off = 0;
+ break;
+ case SEEK_CUR:
+ off = fs->nf_offset;
+ break;
+ case SEEK_END:
+ off = size;
+ break;
+ default:
+ errno = EINVAL;
+ return (-1);
+ }
+
+ off += offset;
+ if (off < 0 || off > size) {
+ errno = EINVAL;
+ return(-1);
+ }
+
+ fs->nf_offset = (u_int)off;
+
+ return (off);
+}
+
+static int
+nandfs_stat(struct open_file *f, struct stat *sb)
+{
+ struct nandfs *fs = f->f_fsdata;
+
+ NANDFS_DEBUG("nandfs_stat(file=%p, stat=%p)\n", f, sb);
+
+ sb->st_size = fs->nf_opened_node->inode->i_size;
+ sb->st_mode = fs->nf_opened_node->inode->i_mode;
+ sb->st_uid = fs->nf_opened_node->inode->i_uid;
+ sb->st_gid = fs->nf_opened_node->inode->i_gid;
+ return (0);
+}
+
+static int
+nandfs_readdir(struct open_file *f, struct dirent *d)
+{
+ struct nandfs *fs = f->f_fsdata;
+ struct nandfs_dir_entry *dirent;
+ void *buf;
+ size_t buf_size;
+
+ NANDFS_DEBUG("nandfs_readdir(file=%p, dirent=%p)\n", f, d);
+
+ if (fs->nf_offset >= fs->nf_opened_node->inode->i_size) {
+ NANDFS_DEBUG("nandfs_readdir(file=%p, dirent=%p) ENOENT\n",
+ f, d);
+ return (ENOENT);
+ }
+
+ if (nandfs_buf_read(fs, &buf, &buf_size)) {
+ NANDFS_DEBUG("nandfs_readdir(file=%p, dirent=%p)"
+ "buf_read failed\n", f, d);
+ return (EIO);
+ }
+
+ NANDFS_DEBUG("nandfs_readdir(file=%p, dirent=%p) moving forward\n",
+ f, d);
+
+ dirent = (struct nandfs_dir_entry *)buf;
+ fs->nf_offset += dirent->rec_len;
+ strncpy(d->d_name, dirent->name, dirent->name_len);
+ d->d_name[dirent->name_len] = '\0';
+ d->d_type = dirent->file_type;
+ return (0);
+}
+
+static int
+nandfs_buf_read(struct nandfs *fs, void **buf_p, size_t *size_p)
+{
+ nandfs_daddr_t blknr, blkoff;
+
+ blknr = fs->nf_offset / fs->nf_blocksize;
+ blkoff = fs->nf_offset % fs->nf_blocksize;
+
+ if (blknr != fs->nf_buf_blknr) {
+ if (fs->nf_buf == NULL)
+ fs->nf_buf = malloc(fs->nf_blocksize);
+
+ if (nandfs_read_inode(fs, fs->nf_opened_node, blknr, 1,
+ fs->nf_buf, 0))
+ return (EIO);
+
+ fs->nf_buf_blknr = blknr;
+ }
+
+ *buf_p = fs->nf_buf + blkoff;
+ *size_p = fs->nf_blocksize - blkoff;
+
+ NANDFS_DEBUG("nandfs_buf_read buf_p=%p size_p=%d\n", *buf_p, *size_p);
+
+ if (*size_p > fs->nf_opened_node->inode->i_size - fs->nf_offset)
+ *size_p = fs->nf_opened_node->inode->i_size - fs->nf_offset;
+
+ return (0);
+}
+
+static struct nandfs_node *
+nandfs_lookup_node(struct nandfs *fs, uint64_t ino)
+{
+ uint64_t blocknr;
+ int entrynr;
+ struct nandfs_inode *buffer;
+ struct nandfs_node *node;
+ struct nandfs_inode *inode;
+
+ NANDFS_DEBUG("nandfs_lookup_node ino=%lld\n", ino);
+
+ if (ino == 0) {
+ printf("nandfs_lookup_node: invalid inode requested\n");
+ return (NULL);
+ }
+
+ buffer = malloc(fs->nf_blocksize);
+ inode = malloc(sizeof(struct nandfs_inode));
+ node = malloc(sizeof(struct nandfs_node));
+
+ nandfs_mdt_trans(&fs->nf_ifile_mdt, ino, &blocknr, &entrynr);
+
+ if (nandfs_read_inode(fs, &fs->nf_ifile, blocknr, 1, buffer, 0))
+ return (NULL);
+
+ memcpy(inode, &buffer[entrynr], sizeof(struct nandfs_inode));
+ node->inode = inode;
+ free(buffer);
+ return (node);
+}
+
+static struct nandfs_node *
+nandfs_lookup_path(struct nandfs *fs, const char *path)
+{
+ struct nandfs_node *node;
+ struct nandfs_dir_entry *dirent;
+ char *namebuf;
+ uint64_t i, done, pinode, inode;
+ int nlinks = 0, counter, len, link_len, nameidx;
+ uint8_t *buffer, *orig;
+ char *strp, *lpath;
+
+ buffer = malloc(fs->nf_blocksize);
+ orig = buffer;
+
+ namebuf = malloc(2 * MAXPATHLEN + 2);
+ strncpy(namebuf, path, MAXPATHLEN);
+ namebuf[MAXPATHLEN] = '\0';
+ done = nameidx = 0;
+ lpath = namebuf;
+
+ /* Get the root inode */
+ node = nandfs_lookup_node(fs, NANDFS_ROOT_INO);
+ inode = NANDFS_ROOT_INO;
+
+ while ((strp = strsep(&lpath, "/")) != NULL) {
+ if (*strp == '\0')
+ continue;
+ if ((node->inode->i_mode & IFMT) != IFDIR) {
+ nandfs_free_node(node);
+ node = NULL;
+ goto out;
+ }
+
+ len = strlen(strp);
+ NANDFS_DEBUG("%s: looking for %s\n", __func__, strp);
+ for (i = 0; i < node->inode->i_blocks; i++) {
+ if (nandfs_read_inode(fs, node, i, 1, orig, 0)) {
+ node = NULL;
+ goto out;
+ }
+
+ buffer = orig;
+ done = counter = 0;
+ while (1) {
+ dirent =
+ (struct nandfs_dir_entry *)(void *)buffer;
+ NANDFS_DEBUG("%s: dirent.name = %s\n",
+ __func__, dirent->name);
+ NANDFS_DEBUG("%s: dirent.rec_len = %d\n",
+ __func__, dirent->rec_len);
+ NANDFS_DEBUG("%s: dirent.inode = %lld\n",
+ __func__, dirent->inode);
+ if (len == dirent->name_len &&
+ (strncmp(strp, dirent->name, len) == 0) &&
+ dirent->inode != 0) {
+ nandfs_free_node(node);
+ node = nandfs_lookup_node(fs,
+ dirent->inode);
+ pinode = inode;
+ inode = dirent->inode;
+ done = 1;
+ break;
+ }
+
+ counter += dirent->rec_len;
+ buffer += dirent->rec_len;
+
+ if (counter == fs->nf_blocksize)
+ break;
+ }
+
+ if (done)
+ break;
+ }
+
+ if (!done) {
+ node = NULL;
+ goto out;
+ }
+
+ NANDFS_DEBUG("%s: %.*s has mode %o\n", __func__,
+ dirent->name_len, dirent->name, node->inode->i_mode);
+
+ if ((node->inode->i_mode & IFMT) == IFLNK) {
+ NANDFS_DEBUG("%s: %.*s is symlink\n",
+ __func__, dirent->name_len, dirent->name);
+ link_len = node->inode->i_size;
+
+ if (++nlinks > MAXSYMLINKS) {
+ nandfs_free_node(node);
+ node = NULL;
+ goto out;
+ }
+
+ if (nandfs_read_inode(fs, node, 0, 1, orig, 0)) {
+ nandfs_free_node(node);
+ node = NULL;
+ goto out;
+ }
+
+ NANDFS_DEBUG("%s: symlink is %.*s\n",
+ __func__, link_len, (char *)orig);
+
+ nameidx = (nameidx == 0) ? MAXPATHLEN + 1 : 0;
+ bcopy((char *)orig, namebuf + nameidx,
+ (unsigned)link_len);
+ if (lpath != NULL) {
+ namebuf[nameidx + link_len++] = '/';
+ strncpy(namebuf + nameidx + link_len, lpath,
+ MAXPATHLEN - link_len);
+ namebuf[nameidx + MAXPATHLEN] = '\0';
+ } else
+ namebuf[nameidx + link_len] = '\0';
+
+ NANDFS_DEBUG("%s: strp=%s, lpath=%s, namebuf0=%s, "
+ "namebuf1=%s, idx=%d\n", __func__, strp, lpath,
+ namebuf + 0, namebuf + MAXPATHLEN + 1, nameidx);
+
+ lpath = namebuf + nameidx;
+
+ nandfs_free_node(node);
+
+ /*
+ * If absolute pathname, restart at root. Otherwise
+ * continue with out parent inode.
+ */
+ inode = (orig[0] == '/') ? NANDFS_ROOT_INO : pinode;
+ node = nandfs_lookup_node(fs, inode);
+ }
+ }
+
+out:
+ free(namebuf);
+ free(orig);
+ return (node);
+}
+
+static int
+nandfs_read_inode(struct nandfs *fs, struct nandfs_node *node,
+ nandfs_daddr_t blknr, u_int nblks, void *buf, int raw)
+{
+ uint64_t *pblks;
+ uint64_t *vblks;
+ u_int i;
+ int error;
+
+ pblks = malloc(nblks * sizeof(uint64_t));
+ vblks = malloc(nblks * sizeof(uint64_t));
+
+ NANDFS_DEBUG("nandfs_read_inode fs=%p node=%p blknr=%lld nblks=%d\n",
+ fs, node, blknr, nblks);
+ for (i = 0; i < nblks; i++) {
+ error = nandfs_bmap_lookup(fs, node, blknr + i, &vblks[i], raw);
+ if (error) {
+ free(pblks);
+ free(vblks);
+ return (error);
+ }
+ if (raw == 0)
+ pblks[i] = nandfs_vtop(fs, vblks[i]);
+ else
+ pblks[i] = vblks[i];
+ }
+
+ for (i = 0; i < nblks; i++) {
+ if (ioread(fs->nf_file, pblks[i] * fs->nf_blocksize, buf,
+ fs->nf_blocksize)) {
+ free(pblks);
+ free(vblks);
+ return (EIO);
+ }
+
+ buf = (void *)((uintptr_t)buf + fs->nf_blocksize);
+ }
+
+ free(pblks);
+ free(vblks);
+ return (0);
+}
+
+static int
+nandfs_read_blk(struct nandfs *fs, nandfs_daddr_t blknr, void *buf, int phys)
+{
+ uint64_t pblknr;
+
+ pblknr = (phys ? blknr : nandfs_vtop(fs, blknr));
+
+ return (ioread(fs->nf_file, pblknr * fs->nf_blocksize, buf,
+ fs->nf_blocksize));
+}
+
+static int
+nandfs_get_checkpoint(struct nandfs *fs, uint64_t cpno,
+ struct nandfs_checkpoint *cp)
+{
+ uint64_t blocknr;
+ int blockoff, cp_per_block, dlen;
+ uint8_t *buf;
+
+ NANDFS_DEBUG("nandfs_get_checkpoint(fs=%p cpno=%lld)\n", fs, cpno);
+
+ buf = malloc(fs->nf_blocksize);
+
+ cpno += NANDFS_CPFILE_FIRST_CHECKPOINT_OFFSET - 1;
+ dlen = fs->nf_fsdata->f_checkpoint_size;
+ cp_per_block = fs->nf_blocksize / dlen;
+ blocknr = cpno / cp_per_block;
+ blockoff = (cpno % cp_per_block) * dlen;
+
+ if (nandfs_read_inode(fs, &fs->nf_cpfile, blocknr, 1, buf, 0)) {
+ free(buf);
+ return (EINVAL);
+ }
+
+ memcpy(cp, buf + blockoff, sizeof(struct nandfs_checkpoint));
+ free(buf);
+
+ return (0);
+}
+
+static uint64_t *
+nandfs_get_map(struct nandfs *fs, struct nandfs_node *node, nandfs_daddr_t blknr,
+ int phys)
+{
+ struct bmap_buf *bmap;
+ uint64_t *map;
+
+ LIST_FOREACH(bmap, &node->bmap_bufs, list) {
+ if (bmap->blknr == blknr)
+ return (bmap->map);
+ }
+
+ map = malloc(fs->nf_blocksize);
+ if (nandfs_read_blk(fs, blknr, map, phys)) {
+ free(map);
+ return (NULL);
+ }
+
+ bmap = malloc(sizeof(struct bmap_buf));
+ bmap->blknr = blknr;
+ bmap->map = map;
+
+ LIST_INSERT_HEAD(&node->bmap_bufs, bmap, list);
+
+ NANDFS_DEBUG("%s:(node=%p, map=%p)\n", __func__, node, map);
+ return (map);
+}
+
+static int
+nandfs_bmap_lookup(struct nandfs *fs, struct nandfs_node *node,
+ nandfs_lbn_t lblknr, nandfs_daddr_t *vblknr, int phys)
+{
+ struct nandfs_inode *ino;
+ nandfs_daddr_t ind_block_num;
+ uint64_t *map;
+ int idx;
+ int level;
+
+ ino = node->inode;
+
+ if (lblknr < NDADDR) {
+ *vblknr = ino->i_db[lblknr];
+ return (0);
+ }
+
+ lblknr -= NDADDR;
+
+ /*
+ * nindir[0] = NINDIR
+ * nindir[1] = NINDIR**2
+ * nindir[2] = NINDIR**3
+ * etc
+ */
+ for (level = 0; level < NIADDR; level++) {
+ NANDFS_DEBUG("lblknr=%jx fs->nf_nindir[%d]=%d\n", lblknr, level, fs->nf_nindir[level]);
+ if (lblknr < fs->nf_nindir[level])
+ break;
+ lblknr -= fs->nf_nindir[level];
+ }
+
+ if (level == NIADDR) {
+ /* Block number too high */
+ NANDFS_DEBUG("lblknr %jx too high\n", lblknr);
+ return (EFBIG);
+ }
+
+ ind_block_num = ino->i_ib[level];
+
+ for (; level >= 0; level--) {
+ if (ind_block_num == 0) {
+ *vblknr = 0; /* missing */
+ return (0);
+ }
+
+ twiddle(1);
+ NANDFS_DEBUG("calling get_map with %jx\n", ind_block_num);
+ map = nandfs_get_map(fs, node, ind_block_num, phys);
+ if (map == NULL)
+ return (EIO);
+
+ if (level > 0) {
+ idx = lblknr / fs->nf_nindir[level - 1];
+ lblknr %= fs->nf_nindir[level - 1];
+ } else
+ idx = lblknr;
+
+ ind_block_num = ((nandfs_daddr_t *)map)[idx];
+ }
+
+ *vblknr = ind_block_num;
+
+ return (0);
+}
+
+static nandfs_daddr_t
+nandfs_vtop(struct nandfs *fs, nandfs_daddr_t vblocknr)
+{
+ nandfs_lbn_t blocknr;
+ nandfs_daddr_t pblocknr;
+ int entrynr;
+ struct nandfs_dat_entry *dat;
+
+ dat = malloc(fs->nf_blocksize);
+ nandfs_mdt_trans(&fs->nf_datfile_mdt, vblocknr, &blocknr, &entrynr);
+
+ if (nandfs_read_inode(fs, &fs->nf_datfile, blocknr, 1, dat, 1)) {
+ free(dat);
+ return (0);
+ }
+
+ NANDFS_DEBUG("nandfs_vtop entrynr=%d vblocknr=%lld pblocknr=%lld\n",
+ entrynr, vblocknr, dat[entrynr].de_blocknr);
+
+ pblocknr = dat[entrynr].de_blocknr;
+ free(dat);
+ return (pblocknr);
+}
+
+static void
+nandfs_calc_mdt_consts(int blocksize, struct nandfs_mdt *mdt, int entry_size)
+{
+
+ mdt->entries_per_group = blocksize * 8; /* bits in sector */
+ mdt->entries_per_block = blocksize / entry_size;
+ mdt->blocks_per_group =
+ (mdt->entries_per_group -1) / mdt->entries_per_block + 1 + 1;
+ mdt->groups_per_desc_block =
+ blocksize / sizeof(struct nandfs_block_group_desc);
+ mdt->blocks_per_desc_block =
+ mdt->groups_per_desc_block * mdt->blocks_per_group + 1;
+}
+
+static void
+nandfs_mdt_trans(struct nandfs_mdt *mdt, uint64_t index,
+ nandfs_daddr_t *blocknr, uint32_t *entry_in_block)
+{
+ nandfs_daddr_t blknr;
+ uint64_t group, group_offset, blocknr_in_group;
+ uint64_t desc_block, desc_offset;
+
+ /* Calculate our offset in the file */
+ group = index / mdt->entries_per_group;
+ group_offset = index % mdt->entries_per_group;
+ desc_block = group / mdt->groups_per_desc_block;
+ desc_offset = group % mdt->groups_per_desc_block;
+ blocknr_in_group = group_offset / mdt->entries_per_block;
+
+ /* To descgroup offset */
+ blknr = 1 + desc_block * mdt->blocks_per_desc_block;
+
+ /* To group offset */
+ blknr += desc_offset * mdt->blocks_per_group;
+
+ /* To actual file block */
+ blknr += 1 + blocknr_in_group;
+
+ *blocknr = blknr;
+ *entry_in_block = group_offset % mdt->entries_per_block;
+}
+
+static int
+ioread(struct open_file *f, off_t pos, void *buf, u_int length)
+{
+ void *buffer;
+ int err;
+ int bsize = ((struct nandfs *)f->f_fsdata)->nf_sectorsize;
+ u_int off, nsec;
+
+ off = pos % bsize;
+ pos /= bsize;
+ nsec = howmany(length, bsize);
+
+ NANDFS_DEBUG("pos=%lld length=%d off=%d nsec=%d\n", pos, length,
+ off, nsec);
+
+ buffer = malloc(nsec * bsize);
+
+ err = (f->f_dev->dv_strategy)(f->f_devdata, F_READ, pos,
+ nsec * bsize, buffer, NULL);
+
+ memcpy(buf, (void *)((uintptr_t)buffer + off), length);
+ free(buffer);
+
+ return (err);
+}
+
+static int
+nandfs_probe_sectorsize(struct open_file *f)
+{
+ void *buffer;
+ int i, err;
+
+ buffer = malloc(16 * 1024);
+
+ NANDFS_DEBUG("probing for sector size: ");
+
+ for (i = 512; i < (16 * 1024); i <<= 1) {
+ NANDFS_DEBUG("%d ", i);
+ err = (f->f_dev->dv_strategy)(f->f_devdata, F_READ, 0, i,
+ buffer, NULL);
+
+ if (err == 0) {
+ NANDFS_DEBUG("found");
+ free(buffer);
+ return (i);
+ }
+ }
+
+ free(buffer);
+ NANDFS_DEBUG("not found\n");
+ return (-1);
+}
diff --git a/stand/libsa/net.c b/stand/libsa/net.c
new file mode 100644
index 0000000..175ca5a
--- /dev/null
+++ b/stand/libsa/net.c
@@ -0,0 +1,283 @@
+/* $NetBSD: net.c,v 1.20 1997/12/26 22:41:30 scottr Exp $ */
+
+/*
+ * Copyright (c) 1992 Regents of the University of California.
+ * All rights reserved.
+ *
+ * This software was developed by the Computer Systems Engineering group
+ * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
+ * contributed to Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#) Header: net.c,v 1.9 93/08/06 19:32:15 leres Exp (LBL)
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/socket.h>
+
+#include <string.h>
+
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+#include <netinet/in_systm.h>
+
+#include <netinet/in_pcb.h>
+#include <netinet/ip.h>
+#include <netinet/ip_var.h>
+#include <netinet/udp.h>
+#include <netinet/udp_var.h>
+
+#include "stand.h"
+#include "net.h"
+
+/*
+ * Send a packet and wait for a reply, with exponential backoff.
+ *
+ * The send routine must return the actual number of bytes written,
+ * or -1 on error.
+ *
+ * The receive routine can indicate success by returning the number of
+ * bytes read; it can return 0 to indicate EOF; it can return -1 with a
+ * non-zero errno to indicate failure; finally, it can return -1 with a
+ * zero errno to indicate it isn't done yet.
+ */
+ssize_t
+sendrecv(struct iodesc *d,
+ ssize_t (*sproc)(struct iodesc *, void *, size_t),
+ void *sbuf, size_t ssize,
+ ssize_t (*rproc)(struct iodesc *, void **, void **, time_t),
+ void **pkt, void **payload)
+{
+ ssize_t cc;
+ time_t t, tmo, tlast;
+ long tleft;
+
+#ifdef NET_DEBUG
+ if (debug)
+ printf("sendrecv: called\n");
+#endif
+
+ tmo = MINTMO;
+ tlast = 0;
+ tleft = 0;
+ t = getsecs();
+ for (;;) {
+ if (tleft <= 0) {
+ if (tmo >= MAXTMO) {
+ errno = ETIMEDOUT;
+ return -1;
+ }
+ cc = (*sproc)(d, sbuf, ssize);
+ if (cc != -1 && cc < ssize)
+ panic("sendrecv: short write! (%zd < %zd)",
+ cc, ssize);
+
+ tleft = tmo;
+ tmo += MINTMO;
+ if (tmo > MAXTMO)
+ tmo = MAXTMO;
+
+ if (cc == -1) {
+ /* Error on transmit; wait before retrying */
+ while ((getsecs() - t) < tmo)
+ ;
+ tleft = 0;
+ continue;
+ }
+
+ tlast = t;
+ }
+
+ /* Try to get a packet and process it. */
+ cc = (*rproc)(d, pkt, payload, tleft);
+ /* Return on data, EOF or real error. */
+ if (cc != -1 || errno != 0)
+ return (cc);
+
+ /* Timed out or didn't get the packet we're waiting for */
+ t = getsecs();
+ tleft -= t - tlast;
+ tlast = t;
+ }
+}
+
+/*
+ * Like inet_addr() in the C library, but we only accept base-10.
+ * Return values are in network order.
+ */
+n_long
+inet_addr(char *cp)
+{
+ u_long val;
+ int n;
+ char c;
+ u_int parts[4];
+ u_int *pp = parts;
+
+ for (;;) {
+ /*
+ * Collect number up to ``.''.
+ * Values are specified as for C:
+ * 0x=hex, 0=octal, other=decimal.
+ */
+ val = 0;
+ while ((c = *cp) != '\0') {
+ if (c >= '0' && c <= '9') {
+ val = (val * 10) + (c - '0');
+ cp++;
+ continue;
+ }
+ break;
+ }
+ if (*cp == '.') {
+ /*
+ * Internet format:
+ * a.b.c.d
+ * a.b.c (with c treated as 16-bits)
+ * a.b (with b treated as 24 bits)
+ */
+ if (pp >= parts + 3 || val > 0xff)
+ goto bad;
+ *pp++ = val, cp++;
+ } else
+ break;
+ }
+ /*
+ * Check for trailing characters.
+ */
+ if (*cp != '\0')
+ goto bad;
+
+ /*
+ * Concoct the address according to
+ * the number of parts specified.
+ */
+ n = pp - parts + 1;
+ switch (n) {
+
+ case 1: /* a -- 32 bits */
+ break;
+
+ case 2: /* a.b -- 8.24 bits */
+ if (val > 0xffffff)
+ goto bad;
+ val |= parts[0] << 24;
+ break;
+
+ case 3: /* a.b.c -- 8.8.16 bits */
+ if (val > 0xffff)
+ goto bad;
+ val |= (parts[0] << 24) | (parts[1] << 16);
+ break;
+
+ case 4: /* a.b.c.d -- 8.8.8.8 bits */
+ if (val > 0xff)
+ goto bad;
+ val |= (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8);
+ break;
+ }
+
+ return (htonl(val));
+ bad:
+ return (htonl(INADDR_NONE));
+}
+
+char *
+inet_ntoa(struct in_addr ia)
+{
+ return (intoa(ia.s_addr));
+}
+
+/* Similar to inet_ntoa() */
+char *
+intoa(n_long addr)
+{
+ char *cp;
+ u_int byte;
+ int n;
+ static char buf[17]; /* strlen(".255.255.255.255") + 1 */
+
+ addr = ntohl(addr);
+ cp = &buf[sizeof buf];
+ *--cp = '\0';
+
+ n = 4;
+ do {
+ byte = addr & 0xff;
+ *--cp = byte % 10 + '0';
+ byte /= 10;
+ if (byte > 0) {
+ *--cp = byte % 10 + '0';
+ byte /= 10;
+ if (byte > 0)
+ *--cp = byte + '0';
+ }
+ *--cp = '.';
+ addr >>= 8;
+ } while (--n > 0);
+
+ return (cp+1);
+}
+
+static char *
+number(char *s, int *n)
+{
+ for (*n = 0; isdigit(*s); s++)
+ *n = (*n * 10) + *s - '0';
+ return s;
+}
+
+n_long
+ip_convertaddr(char *p)
+{
+#define IP_ANYADDR 0
+ n_long addr = 0, n;
+
+ if (p == (char *)0 || *p == '\0')
+ return IP_ANYADDR;
+ p = number(p, &n);
+ addr |= (n << 24) & 0xff000000;
+ if (*p == '\0' || *p++ != '.')
+ return IP_ANYADDR;
+ p = number(p, &n);
+ addr |= (n << 16) & 0xff0000;
+ if (*p == '\0' || *p++ != '.')
+ return IP_ANYADDR;
+ p = number(p, &n);
+ addr |= (n << 8) & 0xff00;
+ if (*p == '\0' || *p++ != '.')
+ return IP_ANYADDR;
+ p = number(p, &n);
+ addr |= n & 0xff;
+ if (*p != '\0')
+ return IP_ANYADDR;
+
+ return htonl(addr);
+}
diff --git a/stand/libsa/net.h b/stand/libsa/net.h
new file mode 100644
index 0000000..f91435c
--- /dev/null
+++ b/stand/libsa/net.h
@@ -0,0 +1,133 @@
+/* $NetBSD: net.h,v 1.10 1995/10/20 00:46:30 cgd Exp $ */
+
+/*
+ * Copyright (c) 1993 Adam Glass
+ * Copyright (c) 1992 Regents of the University of California.
+ * All rights reserved.
+ *
+ * This software was developed by the Computer Systems Engineering group
+ * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
+ * contributed to Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _STAND_NET_H
+#define _STAND_NET_H
+#ifndef _KERNEL /* XXX - see <netinet/in.h> */
+#undef __IPADDR
+#define __IPADDR(x) htonl((u_int32_t)(x))
+#endif
+
+#include "iodesc.h"
+
+#define BA { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }
+
+enum net_proto {
+ NET_NONE,
+ NET_NFS,
+ NET_TFTP
+};
+
+/* Returns true if n_long's on the same net */
+#define SAMENET(a1, a2, m) ((a1.s_addr & m) == (a2.s_addr & m))
+
+#define MACPY(s, d) bcopy((char *)s, (char *)d, 6)
+
+#define MAXTMO 120 /* seconds */
+#define MINTMO 2 /* seconds */
+
+#define FNAME_SIZE 128
+#define IFNAME_SIZE 16
+#define RECV_SIZE 1536 /* XXX delete this */
+
+/*
+ * How much room to leave for headers:
+ * 14: struct ether_header
+ * 20: struct ip
+ * 8: struct udphdr
+ * That's 42 but let's pad it out to 48 bytes.
+ */
+#define ETHER_SIZE 14
+#define HEADER_SIZE 48
+
+extern u_char bcea[6];
+extern char rootpath[FNAME_SIZE];
+extern char bootfile[FNAME_SIZE];
+extern char hostname[FNAME_SIZE];
+extern int hostnamelen;
+extern char domainname[FNAME_SIZE];
+extern int domainnamelen;
+extern int netproto;
+extern char ifname[IFNAME_SIZE];
+
+/* All of these are in network order. */
+extern struct in_addr myip;
+extern struct in_addr rootip;
+extern struct in_addr swapip;
+extern struct in_addr gateip;
+extern struct in_addr nameip;
+extern n_long netmask;
+extern u_int intf_mtu;
+
+extern int debug; /* defined in the machdep sources */
+
+extern struct iodesc sockets[SOPEN_MAX];
+
+/* ARP/RevARP functions: */
+u_char *arpwhohas(struct iodesc *, struct in_addr);
+void arp_reply(struct iodesc *, void *);
+int rarp_getipaddress(int);
+
+/* Link functions: */
+ssize_t sendether(struct iodesc *d, void *pkt, size_t len,
+ u_char *dea, int etype);
+ssize_t readether(struct iodesc *, void **, void **, time_t, uint16_t *);
+
+ssize_t sendip(struct iodesc *, void *, size_t, uint8_t);
+ssize_t readip(struct iodesc *, void **, void **, time_t, uint8_t);
+ssize_t sendudp(struct iodesc *, void *, size_t);
+ssize_t readudp(struct iodesc *, void **, void **, time_t);
+ssize_t sendrecv(struct iodesc *,
+ ssize_t (*)(struct iodesc *, void *, size_t),
+ void *, size_t,
+ ssize_t (*)(struct iodesc *, void **, void **, time_t),
+ void **, void **);
+
+/* bootp/DHCP */
+void bootp(int);
+
+/* Utilities: */
+char *ether_sprintf(u_char *);
+int in_cksum(void *, int);
+char *inet_ntoa(struct in_addr);
+char *intoa(n_long); /* similar to inet_ntoa */
+n_long inet_addr(char *);
+
+/* Machine-dependent functions: */
+time_t getsecs(void);
+#endif /* ! _STAND_NET_H */
diff --git a/stand/libsa/netif.c b/stand/libsa/netif.c
new file mode 100644
index 0000000..105f9a3
--- /dev/null
+++ b/stand/libsa/netif.c
@@ -0,0 +1,316 @@
+/* $NetBSD: netif.c,v 1.10 1997/09/06 13:57:14 drochner Exp $ */
+
+/*
+ * Copyright (c) 1993 Adam Glass
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Adam Glass.
+ * 4. The name of the Author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Adam Glass ``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 REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/cdefs.h>
+#include <sys/mount.h>
+#include <string.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+
+#include "stand.h"
+#include "net.h"
+#include "netif.h"
+
+struct iodesc sockets[SOPEN_MAX];
+#ifdef NETIF_DEBUG
+int netif_debug = 0;
+#endif
+
+/*
+ * netif_init:
+ *
+ * initialize the generic network interface layer
+ */
+
+void
+netif_init(void)
+{
+ struct netif_driver *drv;
+ int d, i;
+
+#ifdef NETIF_DEBUG
+ if (netif_debug)
+ printf("netif_init: called\n");
+#endif
+ for (d = 0; netif_drivers[d]; d++) {
+ drv = netif_drivers[d];
+ for (i = 0; i < drv->netif_nifs; i++)
+ drv->netif_ifs[i].dif_used = 0;
+ }
+}
+
+int
+netif_match(struct netif *nif, void *machdep_hint)
+{
+ struct netif_driver *drv = nif->nif_driver;
+
+#if NETIF_DEBUG
+ if (netif_debug)
+ printf("%s%d: netif_match (%d)\n", drv->netif_bname,
+ nif->nif_unit, nif->nif_sel);
+#endif
+ return drv->netif_match(nif, machdep_hint);
+}
+
+struct netif *
+netif_select(void *machdep_hint)
+{
+ int d, u, unit_done, s;
+ struct netif_driver *drv;
+ struct netif cur_if;
+ static struct netif best_if;
+ int best_val;
+ int val;
+
+ best_val = 0;
+ best_if.nif_driver = NULL;
+
+ for (d = 0; netif_drivers[d] != NULL; d++) {
+ cur_if.nif_driver = netif_drivers[d];
+ drv = cur_if.nif_driver;
+
+ for (u = 0; u < drv->netif_nifs; u++) {
+ cur_if.nif_unit = u;
+ unit_done = 0;
+
+#ifdef NETIF_DEBUG
+ if (netif_debug)
+ printf("\t%s%d:", drv->netif_bname,
+ cur_if.nif_unit);
+#endif
+
+ for (s = 0; s < drv->netif_ifs[u].dif_nsel; s++) {
+ cur_if.nif_sel = s;
+
+ if (drv->netif_ifs[u].dif_used & (1 << s)) {
+#ifdef NETIF_DEBUG
+ if (netif_debug)
+ printf(" [%d used]", s);
+#endif
+ continue;
+ }
+
+ val = netif_match(&cur_if, machdep_hint);
+#ifdef NETIF_DEBUG
+ if (netif_debug)
+ printf(" [%d -> %d]", s, val);
+#endif
+ if (val > best_val) {
+ best_val = val;
+ best_if = cur_if;
+ }
+ }
+#ifdef NETIF_DEBUG
+ if (netif_debug)
+ printf("\n");
+#endif
+ }
+ }
+
+ if (best_if.nif_driver == NULL)
+ return NULL;
+
+ best_if.nif_driver->
+ netif_ifs[best_if.nif_unit].dif_used |= (1 << best_if.nif_sel);
+
+#ifdef NETIF_DEBUG
+ if (netif_debug)
+ printf("netif_select: %s%d(%d) wins\n",
+ best_if.nif_driver->netif_bname,
+ best_if.nif_unit, best_if.nif_sel);
+#endif
+ return &best_if;
+}
+
+int
+netif_probe(struct netif *nif, void *machdep_hint)
+{
+ struct netif_driver *drv = nif->nif_driver;
+
+#ifdef NETIF_DEBUG
+ if (netif_debug)
+ printf("%s%d: netif_probe\n", drv->netif_bname, nif->nif_unit);
+#endif
+ return drv->netif_probe(nif, machdep_hint);
+}
+
+void
+netif_attach(struct netif *nif, struct iodesc *desc, void *machdep_hint)
+{
+ struct netif_driver *drv = nif->nif_driver;
+
+#ifdef NETIF_DEBUG
+ if (netif_debug)
+ printf("%s%d: netif_attach\n", drv->netif_bname, nif->nif_unit);
+#endif
+ desc->io_netif = nif;
+#ifdef PARANOID
+ if (drv->netif_init == NULL)
+ panic("%s%d: no netif_init support\n", drv->netif_bname,
+ nif->nif_unit);
+#endif
+ drv->netif_init(desc, machdep_hint);
+ bzero(drv->netif_ifs[nif->nif_unit].dif_stats,
+ sizeof(struct netif_stats));
+}
+
+void
+netif_detach(struct netif *nif)
+{
+ struct netif_driver *drv = nif->nif_driver;
+
+#ifdef NETIF_DEBUG
+ if (netif_debug)
+ printf("%s%d: netif_detach\n", drv->netif_bname, nif->nif_unit);
+#endif
+#ifdef PARANOID
+ if (drv->netif_end == NULL)
+ panic("%s%d: no netif_end support\n", drv->netif_bname,
+ nif->nif_unit);
+#endif
+ drv->netif_end(nif);
+}
+
+ssize_t
+netif_get(struct iodesc *desc, void **pkt, time_t timo)
+{
+#ifdef NETIF_DEBUG
+ struct netif *nif = desc->io_netif;
+#endif
+ struct netif_driver *drv = desc->io_netif->nif_driver;
+ ssize_t rv;
+
+#ifdef NETIF_DEBUG
+ if (netif_debug)
+ printf("%s%d: netif_get\n", drv->netif_bname, nif->nif_unit);
+#endif
+#ifdef PARANOID
+ if (drv->netif_get == NULL)
+ panic("%s%d: no netif_get support\n", drv->netif_bname,
+ nif->nif_unit);
+#endif
+ rv = drv->netif_get(desc, pkt, timo);
+#ifdef NETIF_DEBUG
+ if (netif_debug)
+ printf("%s%d: netif_get returning %d\n", drv->netif_bname,
+ nif->nif_unit, (int)rv);
+#endif
+ return (rv);
+}
+
+ssize_t
+netif_put(struct iodesc *desc, void *pkt, size_t len)
+{
+#ifdef NETIF_DEBUG
+ struct netif *nif = desc->io_netif;
+#endif
+ struct netif_driver *drv = desc->io_netif->nif_driver;
+ ssize_t rv;
+
+#ifdef NETIF_DEBUG
+ if (netif_debug)
+ printf("%s%d: netif_put\n", drv->netif_bname, nif->nif_unit);
+#endif
+#ifdef PARANOID
+ if (drv->netif_put == NULL)
+ panic("%s%d: no netif_put support\n", drv->netif_bname,
+ nif->nif_unit);
+#endif
+ rv = drv->netif_put(desc, pkt, len);
+#ifdef NETIF_DEBUG
+ if (netif_debug)
+ printf("%s%d: netif_put returning %d\n", drv->netif_bname,
+ nif->nif_unit, (int)rv);
+#endif
+ return (rv);
+}
+
+struct iodesc *
+socktodesc(int sock)
+{
+ if (sock >= SOPEN_MAX) {
+ errno = EBADF;
+ return (NULL);
+ }
+ return (&sockets[sock]);
+}
+
+int
+netif_open(void *machdep_hint)
+{
+ int fd;
+ struct iodesc *s;
+ struct netif *nif;
+
+ /* find a free socket */
+ for (fd = 0, s = sockets; fd < SOPEN_MAX; fd++, s++)
+ if (s->io_netif == (struct netif *)0)
+ goto fnd;
+ errno = EMFILE;
+ return (-1);
+
+fnd:
+ bzero(s, sizeof(*s));
+ netif_init();
+ nif = netif_select(machdep_hint);
+ if (!nif)
+ panic("netboot: no interfaces left untried");
+ if (netif_probe(nif, machdep_hint)) {
+ printf("netboot: couldn't probe %s%d\n",
+ nif->nif_driver->netif_bname, nif->nif_unit);
+ errno = EINVAL;
+ return (-1);
+ }
+ netif_attach(nif, s, machdep_hint);
+
+ return (fd);
+}
+
+int
+netif_close(int sock)
+{
+ if (sock >= SOPEN_MAX) {
+ errno = EBADF;
+ return (-1);
+ }
+ netif_detach(sockets[sock].io_netif);
+ sockets[sock].io_netif = (struct netif *)0;
+
+ return (0);
+}
diff --git a/stand/libsa/netif.h b/stand/libsa/netif.h
new file mode 100644
index 0000000..44165ab
--- /dev/null
+++ b/stand/libsa/netif.h
@@ -0,0 +1,65 @@
+/* $NetBSD: netif.h,v 1.4 1995/09/14 23:45:30 pk Exp $ */
+
+/* $FreeBSD$ */
+
+#ifndef __SYS_LIBNETBOOT_NETIF_H
+#define __SYS_LIBNETBOOT_NETIF_H
+#include "iodesc.h"
+
+struct netif_driver {
+ const char *netif_bname;
+ int (*netif_match)(struct netif *, void *);
+ int (*netif_probe)(struct netif *, void *);
+ void (*netif_init)(struct iodesc *, void *);
+ ssize_t (*netif_get)(struct iodesc *, void **, time_t);
+ ssize_t (*netif_put)(struct iodesc *, void *, size_t);
+ void (*netif_end)(struct netif *);
+ struct netif_dif *netif_ifs;
+ int netif_nifs;
+};
+
+struct netif_dif {
+ int dif_unit;
+ int dif_nsel;
+ struct netif_stats *dif_stats;
+ void *dif_private;
+ /* the following fields are used internally by the netif layer */
+ u_long dif_used;
+};
+
+struct netif_stats {
+ int collisions;
+ int collision_error;
+ int missed;
+ int sent;
+ int received;
+ int deferred;
+ int overflow;
+};
+
+struct netif {
+ struct netif_driver *nif_driver;
+ int nif_unit;
+ int nif_sel;
+ void *nif_devdata;
+};
+
+extern struct netif_driver *netif_drivers[]; /* machdep */
+extern int n_netif_drivers;
+
+extern int netif_debug;
+
+void netif_init(void);
+struct netif *netif_select(void *);
+int netif_probe(struct netif *, void *);
+void netif_attach(struct netif *, struct iodesc *, void *);
+void netif_detach(struct netif *);
+ssize_t netif_get(struct iodesc *, void **, time_t);
+ssize_t netif_put(struct iodesc *, void *, size_t);
+
+int netif_open(void *);
+int netif_close(int);
+
+struct iodesc *socktodesc(int);
+
+#endif /* __SYS_LIBNETBOOT_NETIF_H */
diff --git a/stand/libsa/nfs.c b/stand/libsa/nfs.c
new file mode 100644
index 0000000..7e2e1ab
--- /dev/null
+++ b/stand/libsa/nfs.c
@@ -0,0 +1,860 @@
+/* $NetBSD: nfs.c,v 1.2 1998/01/24 12:43:09 drochner Exp $ */
+
+/*-
+ * Copyright (c) 1993 John Brezak
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * 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/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <stddef.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+
+#include "rpcv2.h"
+#include "nfsv2.h"
+
+#include "stand.h"
+#include "net.h"
+#include "netif.h"
+#include "rpc.h"
+
+#define NFS_DEBUGxx
+
+#define NFSREAD_MIN_SIZE 1024
+#define NFSREAD_MAX_SIZE 16384
+
+/* NFSv3 definitions */
+#define NFS_V3MAXFHSIZE 64
+#define NFS_VER3 3
+#define RPCMNT_VER3 3
+#define NFSPROCV3_LOOKUP 3
+#define NFSPROCV3_READLINK 5
+#define NFSPROCV3_READ 6
+#define NFSPROCV3_READDIR 16
+
+typedef struct {
+ uint32_t val[2];
+} n_quad;
+
+struct nfsv3_time {
+ uint32_t nfs_sec;
+ uint32_t nfs_nsec;
+};
+
+struct nfsv3_fattrs {
+ uint32_t fa_type;
+ uint32_t fa_mode;
+ uint32_t fa_nlink;
+ uint32_t fa_uid;
+ uint32_t fa_gid;
+ n_quad fa_size;
+ n_quad fa_used;
+ n_quad fa_rdev;
+ n_quad fa_fsid;
+ n_quad fa_fileid;
+ struct nfsv3_time fa_atime;
+ struct nfsv3_time fa_mtime;
+ struct nfsv3_time fa_ctime;
+};
+
+/*
+ * For NFSv3, the file handle is variable in size, so most fixed sized
+ * structures for arguments won't work. For most cases, a structure
+ * that starts with any fixed size section is followed by an array
+ * that covers the maximum size required.
+ */
+struct nfsv3_readdir_repl {
+ uint32_t errno;
+ uint32_t ok;
+ struct nfsv3_fattrs fa;
+ uint32_t cookiev0;
+ uint32_t cookiev1;
+};
+
+struct nfsv3_readdir_entry {
+ uint32_t follows;
+ uint32_t fid0;
+ uint32_t fid1;
+ uint32_t len;
+ uint32_t nameplus[0];
+};
+
+struct nfs_iodesc {
+ struct iodesc *iodesc;
+ off_t off;
+ uint32_t fhsize;
+ u_char fh[NFS_V3MAXFHSIZE];
+ struct nfsv3_fattrs fa; /* all in network order */
+ uint64_t cookie;
+};
+
+/*
+ * XXX interactions with tftp? See nfswrapper.c for a confusing
+ * issue.
+ */
+int nfs_open(const char *path, struct open_file *f);
+static int nfs_close(struct open_file *f);
+static int nfs_read(struct open_file *f, void *buf, size_t size, size_t *resid);
+static int nfs_write(struct open_file *f, void *buf, size_t size, size_t *resid);
+static off_t nfs_seek(struct open_file *f, off_t offset, int where);
+static int nfs_stat(struct open_file *f, struct stat *sb);
+static int nfs_readdir(struct open_file *f, struct dirent *d);
+
+struct nfs_iodesc nfs_root_node;
+
+struct fs_ops nfs_fsops = {
+ "nfs",
+ nfs_open,
+ nfs_close,
+ nfs_read,
+ nfs_write,
+ nfs_seek,
+ nfs_stat,
+ nfs_readdir
+};
+
+static int nfs_read_size = NFSREAD_MIN_SIZE;
+
+/*
+ * Improve boot performance over NFS
+ */
+static void
+set_nfs_read_size(void)
+{
+ char *env, *end;
+ char buf[10];
+
+ if ((env = getenv("nfs.read_size")) != NULL) {
+ errno = 0;
+ nfs_read_size = (int)strtol(env, &end, 0);
+ if (errno != 0 || *env == '\0' || *end != '\0') {
+ printf("%s: bad value: \"%s\", defaulting to %d\n",
+ "nfs.read_size", env, NFSREAD_MIN_SIZE);
+ nfs_read_size = NFSREAD_MIN_SIZE;
+ }
+ }
+ if (nfs_read_size < NFSREAD_MIN_SIZE) {
+ printf("%s: bad value: \"%d\", defaulting to %d\n",
+ "nfs.read_size", nfs_read_size, NFSREAD_MIN_SIZE);
+ nfs_read_size = NFSREAD_MIN_SIZE;
+ }
+ if (nfs_read_size > NFSREAD_MAX_SIZE) {
+ printf("%s: bad value: \"%d\", defaulting to %d\n",
+ "nfs.read_size", nfs_read_size, NFSREAD_MIN_SIZE);
+ nfs_read_size = NFSREAD_MAX_SIZE;
+ }
+ snprintf(buf, sizeof (buf), "%d", nfs_read_size);
+ setenv("nfs.read_size", buf, 1);
+}
+
+/*
+ * Fetch the root file handle (call mount daemon)
+ * Return zero or error number.
+ */
+int
+nfs_getrootfh(struct iodesc *d, char *path, uint32_t *fhlenp, u_char *fhp)
+{
+ void *pkt = NULL;
+ int len;
+ struct args {
+ uint32_t len;
+ char path[FNAME_SIZE];
+ } *args;
+ struct repl {
+ uint32_t errno;
+ uint32_t fhsize;
+ u_char fh[NFS_V3MAXFHSIZE];
+ uint32_t authcnt;
+ uint32_t auth[7];
+ } *repl;
+ struct {
+ uint32_t h[RPC_HEADER_WORDS];
+ struct args d;
+ } sdata;
+ size_t cc;
+
+#ifdef NFS_DEBUG
+ if (debug)
+ printf("nfs_getrootfh: %s\n", path);
+#endif
+
+ args = &sdata.d;
+
+ bzero(args, sizeof(*args));
+ len = strlen(path);
+ if (len > sizeof(args->path))
+ len = sizeof(args->path);
+ args->len = htonl(len);
+ bcopy(path, args->path, len);
+ len = sizeof(uint32_t) + roundup(len, sizeof(uint32_t));
+
+ cc = rpc_call(d, RPCPROG_MNT, RPCMNT_VER3, RPCMNT_MOUNT,
+ args, len, (void **)&repl, &pkt);
+ if (cc == -1) {
+ free(pkt);
+ /* errno was set by rpc_call */
+ return (errno);
+ }
+ if (cc < 2 * sizeof (uint32_t)) {
+ free(pkt);
+ return (EBADRPC);
+ }
+ if (repl->errno != 0) {
+ free(pkt);
+ return (ntohl(repl->errno));
+ }
+ *fhlenp = ntohl(repl->fhsize);
+ bcopy(repl->fh, fhp, *fhlenp);
+
+ set_nfs_read_size();
+ free(pkt);
+ return (0);
+}
+
+/*
+ * Lookup a file. Store handle and attributes.
+ * Return zero or error number.
+ */
+int
+nfs_lookupfh(struct nfs_iodesc *d, const char *name, struct nfs_iodesc *newfd)
+{
+ void *pkt = NULL;
+ int len, rlen, pos;
+ struct args {
+ uint32_t fhsize;
+ uint32_t fhplusname[1 +
+ (NFS_V3MAXFHSIZE + FNAME_SIZE) / sizeof(uint32_t)];
+ } *args;
+ struct repl {
+ uint32_t errno;
+ uint32_t fhsize;
+ uint32_t fhplusattr[(NFS_V3MAXFHSIZE +
+ 2 * (sizeof(uint32_t) +
+ sizeof(struct nfsv3_fattrs))) / sizeof(uint32_t)];
+ } *repl;
+ struct {
+ uint32_t h[RPC_HEADER_WORDS];
+ struct args d;
+ } sdata;
+ ssize_t cc;
+
+#ifdef NFS_DEBUG
+ if (debug)
+ printf("lookupfh: called\n");
+#endif
+
+ args = &sdata.d;
+
+ bzero(args, sizeof(*args));
+ args->fhsize = htonl(d->fhsize);
+ bcopy(d->fh, args->fhplusname, d->fhsize);
+ len = strlen(name);
+ if (len > FNAME_SIZE)
+ len = FNAME_SIZE;
+ pos = roundup(d->fhsize, sizeof(uint32_t)) / sizeof(uint32_t);
+ args->fhplusname[pos++] = htonl(len);
+ bcopy(name, &args->fhplusname[pos], len);
+ len = sizeof(uint32_t) + pos * sizeof(uint32_t) +
+ roundup(len, sizeof(uint32_t));
+
+ cc = rpc_call(d->iodesc, NFS_PROG, NFS_VER3, NFSPROCV3_LOOKUP,
+ args, len, (void **)&repl, &pkt);
+ if (cc == -1) {
+ free(pkt);
+ return (errno); /* XXX - from rpc_call */
+ }
+ if (cc < 2 * sizeof(uint32_t)) {
+ free(pkt);
+ return (EIO);
+ }
+ if (repl->errno != 0) {
+ free(pkt);
+ /* saerrno.h now matches NFS error numbers. */
+ return (ntohl(repl->errno));
+ }
+ newfd->fhsize = ntohl(repl->fhsize);
+ bcopy(repl->fhplusattr, &newfd->fh, newfd->fhsize);
+ pos = roundup(newfd->fhsize, sizeof(uint32_t)) / sizeof(uint32_t);
+ if (repl->fhplusattr[pos++] == 0) {
+ free(pkt);
+ return (EIO);
+ }
+ bcopy(&repl->fhplusattr[pos], &newfd->fa, sizeof(newfd->fa));
+ free(pkt);
+ return (0);
+}
+
+#ifndef NFS_NOSYMLINK
+/*
+ * Get the destination of a symbolic link.
+ */
+int
+nfs_readlink(struct nfs_iodesc *d, char *buf)
+{
+ void *pkt = NULL;
+ struct args {
+ uint32_t fhsize;
+ u_char fh[NFS_V3MAXFHSIZE];
+ } *args;
+ struct repl {
+ uint32_t errno;
+ uint32_t ok;
+ struct nfsv3_fattrs fa;
+ uint32_t len;
+ u_char path[NFS_MAXPATHLEN];
+ } *repl;
+ struct {
+ uint32_t h[RPC_HEADER_WORDS];
+ struct args d;
+ } sdata;
+ ssize_t cc;
+ int rc = 0;
+
+#ifdef NFS_DEBUG
+ if (debug)
+ printf("readlink: called\n");
+#endif
+
+ args = &sdata.d;
+
+ bzero(args, sizeof(*args));
+ args->fhsize = htonl(d->fhsize);
+ bcopy(d->fh, args->fh, d->fhsize);
+ cc = rpc_call(d->iodesc, NFS_PROG, NFS_VER3, NFSPROCV3_READLINK,
+ args, sizeof(uint32_t) + roundup(d->fhsize, sizeof(uint32_t)),
+ (void **)&repl, &pkt);
+ if (cc == -1)
+ return (errno);
+
+ if (cc < 2 * sizeof(uint32_t)) {
+ rc = EIO;
+ goto done;
+ }
+
+ if (repl->errno != 0) {
+ rc = ntohl(repl->errno);
+ goto done;
+ }
+
+ if (repl->ok == 0) {
+ rc = EIO;
+ goto done;
+ }
+
+ repl->len = ntohl(repl->len);
+ if (repl->len > NFS_MAXPATHLEN) {
+ rc = ENAMETOOLONG;
+ goto done;
+ }
+
+ bcopy(repl->path, buf, repl->len);
+ buf[repl->len] = 0;
+done:
+ free(pkt);
+ return (rc);
+}
+#endif
+
+/*
+ * Read data from a file.
+ * Return transfer count or -1 (and set errno)
+ */
+ssize_t
+nfs_readdata(struct nfs_iodesc *d, off_t off, void *addr, size_t len)
+{
+ void *pkt = NULL;
+ struct args {
+ uint32_t fhsize;
+ uint32_t fhoffcnt[NFS_V3MAXFHSIZE / sizeof(uint32_t) + 3];
+ } *args;
+ struct repl {
+ uint32_t errno;
+ uint32_t ok;
+ struct nfsv3_fattrs fa;
+ uint32_t count;
+ uint32_t eof;
+ uint32_t len;
+ u_char data[NFSREAD_MAX_SIZE];
+ } *repl;
+ struct {
+ uint32_t h[RPC_HEADER_WORDS];
+ struct args d;
+ } sdata;
+ size_t cc;
+ long x;
+ int hlen, rlen, pos;
+
+ args = &sdata.d;
+
+ bzero(args, sizeof(*args));
+ args->fhsize = htonl(d->fhsize);
+ bcopy(d->fh, args->fhoffcnt, d->fhsize);
+ pos = roundup(d->fhsize, sizeof(uint32_t)) / sizeof(uint32_t);
+ args->fhoffcnt[pos++] = 0;
+ args->fhoffcnt[pos++] = htonl((uint32_t)off);
+ if (len > nfs_read_size)
+ len = nfs_read_size;
+ args->fhoffcnt[pos] = htonl((uint32_t)len);
+ hlen = offsetof(struct repl, data[0]);
+
+ cc = rpc_call(d->iodesc, NFS_PROG, NFS_VER3, NFSPROCV3_READ,
+ args, 4 * sizeof(uint32_t) + roundup(d->fhsize, sizeof(uint32_t)),
+ (void **)&repl, &pkt);
+ if (cc == -1) {
+ /* errno was already set by rpc_call */
+ return (-1);
+ }
+ if (cc < hlen) {
+ errno = EBADRPC;
+ free(pkt);
+ return (-1);
+ }
+ if (repl->errno != 0) {
+ errno = ntohl(repl->errno);
+ free(pkt);
+ return (-1);
+ }
+ rlen = cc - hlen;
+ x = ntohl(repl->count);
+ if (rlen < x) {
+ printf("nfsread: short packet, %d < %ld\n", rlen, x);
+ errno = EBADRPC;
+ free(pkt);
+ return (-1);
+ }
+ bcopy(repl->data, addr, x);
+ free(pkt);
+ return (x);
+}
+
+/*
+ * Open a file.
+ * return zero or error number
+ */
+int
+nfs_open(const char *upath, struct open_file *f)
+{
+ struct iodesc *desc;
+ struct nfs_iodesc *currfd;
+ char buf[2 * NFS_V3MAXFHSIZE + 3];
+ u_char *fh;
+ char *cp;
+ int i;
+#ifndef NFS_NOSYMLINK
+ struct nfs_iodesc *newfd;
+ struct nfsv3_fattrs *fa;
+ char *ncp;
+ int c;
+ char namebuf[NFS_MAXPATHLEN + 1];
+ char linkbuf[NFS_MAXPATHLEN + 1];
+ int nlinks = 0;
+#endif
+ int error;
+ char *path;
+
+ if (netproto != NET_NFS)
+ return (EINVAL);
+
+#ifdef NFS_DEBUG
+ if (debug)
+ printf("nfs_open: %s (rootpath=%s)\n", upath, rootpath);
+#endif
+ if (!rootpath[0]) {
+ printf("no rootpath, no nfs\n");
+ return (ENXIO);
+ }
+
+ if (f->f_dev->dv_type != DEVT_NET)
+ return (EINVAL);
+
+ if (!(desc = socktodesc(*(int *)(f->f_devdata))))
+ return (EINVAL);
+
+ /* Bind to a reserved port. */
+ desc->myport = htons(--rpc_port);
+ desc->destip = rootip;
+ if ((error = nfs_getrootfh(desc, rootpath, &nfs_root_node.fhsize,
+ nfs_root_node.fh)))
+ return (error);
+ nfs_root_node.fa.fa_type = htonl(NFDIR);
+ nfs_root_node.fa.fa_mode = htonl(0755);
+ nfs_root_node.fa.fa_nlink = htonl(2);
+ nfs_root_node.iodesc = desc;
+
+ fh = &nfs_root_node.fh[0];
+ buf[0] = 'X';
+ cp = &buf[1];
+ for (i = 0; i < nfs_root_node.fhsize; i++, cp += 2)
+ sprintf(cp, "%02x", fh[i]);
+ sprintf(cp, "X");
+ setenv("boot.nfsroot.server", inet_ntoa(rootip), 1);
+ setenv("boot.nfsroot.path", rootpath, 1);
+ setenv("boot.nfsroot.nfshandle", buf, 1);
+ sprintf(buf, "%d", nfs_root_node.fhsize);
+ setenv("boot.nfsroot.nfshandlelen", buf, 1);
+
+ /* Allocate file system specific data structure */
+ currfd = malloc(sizeof(*newfd));
+ if (currfd == NULL) {
+ error = ENOMEM;
+ goto out;
+ }
+#ifndef NFS_NOSYMLINK
+ bcopy(&nfs_root_node, currfd, sizeof(*currfd));
+ newfd = NULL;
+
+ cp = path = strdup(upath);
+ if (path == NULL) {
+ error = ENOMEM;
+ goto out;
+ }
+ while (*cp) {
+ /*
+ * Remove extra separators
+ */
+ while (*cp == '/')
+ cp++;
+
+ if (*cp == '\0')
+ break;
+ /*
+ * Check that current node is a directory.
+ */
+ if (currfd->fa.fa_type != htonl(NFDIR)) {
+ error = ENOTDIR;
+ goto out;
+ }
+
+ /* allocate file system specific data structure */
+ newfd = malloc(sizeof(*newfd));
+ if (newfd == NULL) {
+ error = ENOMEM;
+ goto out;
+ }
+ newfd->iodesc = currfd->iodesc;
+
+ /*
+ * Get next component of path name.
+ */
+ {
+ int len = 0;
+
+ ncp = cp;
+ while ((c = *cp) != '\0' && c != '/') {
+ if (++len > NFS_MAXNAMLEN) {
+ error = ENOENT;
+ goto out;
+ }
+ cp++;
+ }
+ *cp = '\0';
+ }
+
+ /* lookup a file handle */
+ error = nfs_lookupfh(currfd, ncp, newfd);
+ *cp = c;
+ if (error)
+ goto out;
+
+ /*
+ * Check for symbolic link
+ */
+ if (newfd->fa.fa_type == htonl(NFLNK)) {
+ int link_len, len;
+
+ error = nfs_readlink(newfd, linkbuf);
+ if (error)
+ goto out;
+
+ link_len = strlen(linkbuf);
+ len = strlen(cp);
+
+ if (link_len + len > MAXPATHLEN
+ || ++nlinks > MAXSYMLINKS) {
+ error = ENOENT;
+ goto out;
+ }
+
+ bcopy(cp, &namebuf[link_len], len + 1);
+ bcopy(linkbuf, namebuf, link_len);
+
+ /*
+ * If absolute pathname, restart at root.
+ * If relative pathname, restart at parent directory.
+ */
+ cp = namebuf;
+ if (*cp == '/')
+ bcopy(&nfs_root_node, currfd, sizeof(*currfd));
+
+ free(newfd);
+ newfd = NULL;
+
+ continue;
+ }
+
+ free(currfd);
+ currfd = newfd;
+ newfd = NULL;
+ }
+
+ error = 0;
+
+out:
+ free(newfd);
+ free(path);
+#else
+ currfd->iodesc = desc;
+
+ error = nfs_lookupfh(&nfs_root_node, upath, currfd);
+#endif
+ if (!error) {
+ currfd->off = 0;
+ currfd->cookie = 0;
+ f->f_fsdata = (void *)currfd;
+ return (0);
+ }
+
+#ifdef NFS_DEBUG
+ if (debug)
+ printf("nfs_open: %s lookupfh failed: %s\n",
+ path, strerror(error));
+#endif
+ free(currfd);
+
+ return (error);
+}
+
+int
+nfs_close(struct open_file *f)
+{
+ struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
+
+#ifdef NFS_DEBUG
+ if (debug)
+ printf("nfs_close: fp=0x%lx\n", (u_long)fp);
+#endif
+
+ free(fp);
+ f->f_fsdata = NULL;
+
+ return (0);
+}
+
+/*
+ * read a portion of a file
+ */
+int
+nfs_read(struct open_file *f, void *buf, size_t size, size_t *resid)
+{
+ struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
+ ssize_t cc;
+ char *addr = buf;
+
+#ifdef NFS_DEBUG
+ if (debug)
+ printf("nfs_read: size=%lu off=%d\n", (u_long)size,
+ (int)fp->off);
+#endif
+ while ((int)size > 0) {
+ twiddle(16);
+ cc = nfs_readdata(fp, fp->off, (void *)addr, size);
+ /* XXX maybe should retry on certain errors */
+ if (cc == -1) {
+#ifdef NFS_DEBUG
+ if (debug)
+ printf("nfs_read: read: %s", strerror(errno));
+#endif
+ return (errno); /* XXX - from nfs_readdata */
+ }
+ if (cc == 0) {
+#ifdef NFS_DEBUG
+ if (debug)
+ printf("nfs_read: hit EOF unexpectantly");
+#endif
+ goto ret;
+ }
+ fp->off += cc;
+ addr += cc;
+ size -= cc;
+ }
+ret:
+ if (resid)
+ *resid = size;
+
+ return (0);
+}
+
+/*
+ * Not implemented.
+ */
+int
+nfs_write(struct open_file *f, void *buf, size_t size, size_t *resid)
+{
+ return (EROFS);
+}
+
+off_t
+nfs_seek(struct open_file *f, off_t offset, int where)
+{
+ struct nfs_iodesc *d = (struct nfs_iodesc *)f->f_fsdata;
+ uint32_t size = ntohl(d->fa.fa_size.val[1]);
+
+ switch (where) {
+ case SEEK_SET:
+ d->off = offset;
+ break;
+ case SEEK_CUR:
+ d->off += offset;
+ break;
+ case SEEK_END:
+ d->off = size - offset;
+ break;
+ default:
+ errno = EINVAL;
+ return (-1);
+ }
+
+ return (d->off);
+}
+
+/* NFNON=0, NFREG=1, NFDIR=2, NFBLK=3, NFCHR=4, NFLNK=5, NFSOCK=6, NFFIFO=7 */
+int nfs_stat_types[9] = {
+ 0, S_IFREG, S_IFDIR, S_IFBLK, S_IFCHR, S_IFLNK, S_IFSOCK, S_IFIFO, 0 };
+
+int
+nfs_stat(struct open_file *f, struct stat *sb)
+{
+ struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
+ uint32_t ftype, mode;
+
+ ftype = ntohl(fp->fa.fa_type);
+ mode = ntohl(fp->fa.fa_mode);
+ mode |= nfs_stat_types[ftype & 7];
+
+ sb->st_mode = mode;
+ sb->st_nlink = ntohl(fp->fa.fa_nlink);
+ sb->st_uid = ntohl(fp->fa.fa_uid);
+ sb->st_gid = ntohl(fp->fa.fa_gid);
+ sb->st_size = ntohl(fp->fa.fa_size.val[1]);
+
+ return (0);
+}
+
+static int
+nfs_readdir(struct open_file *f, struct dirent *d)
+{
+ struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
+ struct nfsv3_readdir_repl *repl;
+ struct nfsv3_readdir_entry *rent;
+ static void *pkt = NULL;
+ static char *buf;
+ static struct nfs_iodesc *pfp = NULL;
+ static uint64_t cookie = 0;
+ size_t cc;
+ int pos, rc;
+
+ struct args {
+ uint32_t fhsize;
+ uint32_t fhpluscookie[5 + NFS_V3MAXFHSIZE];
+ } *args;
+ struct {
+ uint32_t h[RPC_HEADER_WORDS];
+ struct args d;
+ } sdata;
+
+ if (fp != pfp || fp->off != cookie) {
+ pfp = NULL;
+ refill:
+ free(pkt);
+ pkt = NULL;
+ args = &sdata.d;
+ bzero(args, sizeof(*args));
+
+ args->fhsize = htonl(fp->fhsize);
+ bcopy(fp->fh, args->fhpluscookie, fp->fhsize);
+ pos = roundup(fp->fhsize, sizeof(uint32_t)) / sizeof(uint32_t);
+ args->fhpluscookie[pos++] = htonl(fp->off >> 32);
+ args->fhpluscookie[pos++] = htonl(fp->off);
+ args->fhpluscookie[pos++] = htonl(fp->cookie >> 32);
+ args->fhpluscookie[pos++] = htonl(fp->cookie);
+ args->fhpluscookie[pos] = htonl(NFS_READDIRSIZE);
+
+ cc = rpc_call(fp->iodesc, NFS_PROG, NFS_VER3, NFSPROCV3_READDIR,
+ args, 6 * sizeof(uint32_t) +
+ roundup(fp->fhsize, sizeof(uint32_t)),
+ (void **)&buf, &pkt);
+ if (cc == -1) {
+ rc = errno;
+ goto err;
+ }
+ repl = (struct nfsv3_readdir_repl *)buf;
+ if (repl->errno != 0) {
+ rc = ntohl(repl->errno);
+ goto err;
+ }
+ pfp = fp;
+ cookie = fp->off;
+ fp->cookie = ((uint64_t)ntohl(repl->cookiev0) << 32) |
+ ntohl(repl->cookiev1);
+ buf += sizeof (struct nfsv3_readdir_repl);
+ }
+ rent = (struct nfsv3_readdir_entry *)buf;
+
+ if (rent->follows == 0) {
+ /* fid0 is actually eof */
+ if (rent->fid0 != 0) {
+ rc = ENOENT;
+ goto err;
+ }
+ goto refill;
+ }
+
+ d->d_namlen = ntohl(rent->len);
+ bcopy(rent->nameplus, d->d_name, d->d_namlen);
+ d->d_name[d->d_namlen] = '\0';
+
+ pos = roundup(d->d_namlen, sizeof(uint32_t)) / sizeof(uint32_t);
+ fp->off = cookie = ((uint64_t)ntohl(rent->nameplus[pos]) << 32) |
+ ntohl(rent->nameplus[pos + 1]);
+ pos += 2;
+ buf = (u_char *)&rent->nameplus[pos];
+ return (0);
+
+err:
+ free(pkt);
+ pkt = NULL;
+ pfp = NULL;
+ cookie = 0;
+ return (rc);
+}
diff --git a/stand/libsa/nfsv2.h b/stand/libsa/nfsv2.h
new file mode 100644
index 0000000..184a47b
--- /dev/null
+++ b/stand/libsa/nfsv2.h
@@ -0,0 +1,121 @@
+/* $FreeBSD$ */
+/* $NetBSD: nfsv2.h,v 1.2 1996/02/26 23:05:23 gwr Exp $ */
+
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Rick Macklem at The University of Guelph.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)nfsv2.h 8.1 (Berkeley) 6/10/93
+ */
+
+/*
+ * nfs definitions as per the version 2 specs
+ */
+
+/*
+ * Constants as defined in the Sun NFS Version 2 spec.
+ * "NFS: Network File System Protocol Specification" RFC1094
+ */
+
+#define NFS_PORT 2049
+#define NFS_PROG 100003
+#define NFS_VER2 2
+#define NFS_MAXDGRAMDATA 8192
+#define NFS_MAXDATA 32768
+#define NFS_MAXPATHLEN 1024
+#define NFS_MAXNAMLEN 255
+#define NFS_FHSIZE 32
+#define NFS_MAXPKTHDR 404
+#define NFS_MAXPACKET (NFS_MAXPKTHDR+NFS_MAXDATA)
+#define NFS_MINPACKET 20
+#define NFS_FABLKSIZE 512 /* Size in bytes of a block wrt fa_blocks */
+#define NFS_READDIRSIZE 1024
+
+/* Stat numbers for rpc returns */
+#define NFS_OK 0
+#define NFSERR_PERM 1
+#define NFSERR_NOENT 2
+#define NFSERR_IO 5
+#define NFSERR_NXIO 6
+#define NFSERR_ACCES 13
+#define NFSERR_EXIST 17
+#define NFSERR_NODEV 19
+#define NFSERR_NOTDIR 20
+#define NFSERR_ISDIR 21
+#define NFSERR_FBIG 27
+#define NFSERR_NOSPC 28
+#define NFSERR_ROFS 30
+#define NFSERR_NAMETOL 63
+#define NFSERR_NOTEMPTY 66
+#define NFSERR_DQUOT 69
+#define NFSERR_STALE 70
+#define NFSERR_WFLUSH 99
+
+/* Sizes in bytes of various nfs rpc components */
+#define NFSX_FH 32
+#define NFSX_UNSIGNED 4
+#define NFSX_FATTR 68
+#define NFSX_SATTR 32
+#define NFSX_STATFS 20
+#define NFSX_COOKIE 4
+
+/* nfs rpc procedure numbers */
+#define NFSPROC_NULL 0
+#define NFSPROC_GETATTR 1
+#define NFSPROC_SETATTR 2
+#define NFSPROC_NOOP 3
+#define NFSPROC_ROOT NFSPROC_NOOP /* Obsolete */
+#define NFSPROC_LOOKUP 4
+#define NFSPROC_READLINK 5
+#define NFSPROC_READ 6
+#define NFSPROC_WRITECACHE NFSPROC_NOOP /* Obsolete */
+#define NFSPROC_WRITE 8
+#define NFSPROC_CREATE 9
+#define NFSPROC_REMOVE 10
+#define NFSPROC_RENAME 11
+#define NFSPROC_LINK 12
+#define NFSPROC_SYMLINK 13
+#define NFSPROC_MKDIR 14
+#define NFSPROC_RMDIR 15
+#define NFSPROC_READDIR 16
+#define NFSPROC_STATFS 17
+
+#define NFS_NPROCS 18
+
+
+/* File types */
+typedef enum {
+ NFNON=0,
+ NFREG=1,
+ NFDIR=2,
+ NFBLK=3,
+ NFCHR=4,
+ NFLNK=5
+} nfstype;
diff --git a/stand/libsa/nullfs.c b/stand/libsa/nullfs.c
new file mode 100644
index 0000000..e4c0b7c
--- /dev/null
+++ b/stand/libsa/nullfs.c
@@ -0,0 +1,105 @@
+/* $NetBSD: nullfs.c,v 1.1 1996/01/13 22:25:39 leo Exp $ */
+
+/*-
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * The Mach Operating System project at Carnegie-Mellon University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)open.c 8.1 (Berkeley) 6/11/93
+ *
+ *
+ * Copyright (c) 1989, 1990, 1991 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Author: Alessandro Forin
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie the
+ * rights to redistribute these changes.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "stand.h"
+
+/*
+ * Null filesystem
+ */
+int null_open (const char *path, struct open_file *f)
+{
+ return EINVAL;
+}
+
+int null_close(struct open_file *f)
+{
+ return 0;
+}
+
+int null_read (struct open_file *f, void *buf, size_t size, size_t *resid)
+{
+ return EIO;
+}
+
+int null_write (struct open_file *f, void *buf, size_t size, size_t *resid)
+{
+ return EIO;
+}
+
+off_t null_seek (struct open_file *f, off_t offset, int where)
+{
+ errno = EIO;
+ return -1;
+}
+
+int null_stat (struct open_file *f, struct stat *sb)
+{
+ return EIO;
+}
+
+int null_readdir(struct open_file *f, struct dirent *d)
+{
+ return EIO;
+}
diff --git a/stand/libsa/open.c b/stand/libsa/open.c
new file mode 100644
index 0000000..214e51b
--- /dev/null
+++ b/stand/libsa/open.c
@@ -0,0 +1,159 @@
+/* $NetBSD: open.c,v 1.16 1997/01/28 09:41:03 pk Exp $ */
+
+/*-
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * The Mach Operating System project at Carnegie-Mellon University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)open.c 8.1 (Berkeley) 6/11/93
+ *
+ *
+ * Copyright (c) 1989, 1990, 1991 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Author: Alessandro Forin
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie the
+ * rights to redistribute these changes.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "stand.h"
+
+struct fs_ops *exclusive_file_system;
+
+struct open_file files[SOPEN_MAX];
+
+static int
+o_gethandle(void)
+{
+ int fd;
+
+ for (fd = 0; fd < SOPEN_MAX; fd++)
+ if (files[fd].f_flags == 0)
+ return(fd);
+ return(-1);
+}
+
+static void
+o_rainit(struct open_file *f)
+{
+ f->f_rabuf = malloc(SOPEN_RASIZE);
+ f->f_ralen = 0;
+ f->f_raoffset = 0;
+}
+
+int
+open(const char *fname, int mode)
+{
+ struct fs_ops *fs;
+ struct open_file *f;
+ int fd, i, error, besterror;
+ const char *file;
+
+ if ((fd = o_gethandle()) == -1) {
+ errno = EMFILE;
+ return(-1);
+ }
+
+ f = &files[fd];
+ f->f_flags = mode + 1;
+ f->f_dev = (struct devsw *)0;
+ f->f_ops = (struct fs_ops *)0;
+ f->f_offset = 0;
+ f->f_devdata = NULL;
+ file = (char *)0;
+
+ if (exclusive_file_system != NULL) {
+ fs = exclusive_file_system;
+ error = (fs->fo_open)(fname, f);
+ if (error == 0)
+ goto ok;
+ goto err;
+ }
+
+ error = devopen(f, fname, &file);
+ if (error ||
+ (((f->f_flags & F_NODEV) == 0) && f->f_dev == (struct devsw *)0))
+ goto err;
+
+ /* see if we opened a raw device; otherwise, 'file' is the file name. */
+ if (file == (char *)0 || *file == '\0') {
+ f->f_flags |= F_RAW;
+ f->f_rabuf = NULL;
+ return (fd);
+ }
+
+ /* pass file name to the different filesystem open routines */
+ besterror = ENOENT;
+ for (i = 0; file_system[i] != NULL; i++) {
+ fs = file_system[i];
+ error = (fs->fo_open)(file, f);
+ if (error == 0)
+ goto ok;
+ if (error != EINVAL)
+ besterror = error;
+ }
+ error = besterror;
+
+ fail:
+ if ((f->f_flags & F_NODEV) == 0 && f->f_dev != NULL)
+ f->f_dev->dv_close(f);
+ if (error)
+ devclose(f);
+
+ err:
+ f->f_flags = 0;
+ errno = error;
+ return (-1);
+
+ ok:
+ f->f_ops = fs;
+ o_rainit(f);
+ return (fd);
+}
diff --git a/stand/libsa/pager.c b/stand/libsa/pager.c
new file mode 100644
index 0000000..a966b0b
--- /dev/null
+++ b/stand/libsa/pager.c
@@ -0,0 +1,161 @@
+/*-
+ * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+/*
+ * Simple paged-output and paged-viewing functions
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "stand.h"
+#include <string.h>
+
+static int p_maxlines = -1;
+static int p_freelines;
+
+static char *pager_prompt1 = " --more-- <space> page down <enter> line down <q> quit ";
+static char *pager_blank = " ";
+
+/*
+ * 'open' the pager
+ */
+void
+pager_open(void)
+{
+ int nlines;
+ char *cp, *lp;
+
+ nlines = 24; /* sensible default */
+ if ((cp = getenv("LINES")) != NULL) {
+ nlines = strtol(cp, &lp, 0);
+ }
+
+ p_maxlines = nlines - 1;
+ if (p_maxlines < 1)
+ p_maxlines = 1;
+ p_freelines = p_maxlines;
+}
+
+/*
+ * 'close' the pager
+ */
+void
+pager_close(void)
+{
+ p_maxlines = -1;
+}
+
+/*
+ * Emit lines to the pager; may not return until the user
+ * has responded to the prompt.
+ *
+ * Will return nonzero if the user enters 'q' or 'Q' at the prompt.
+ *
+ * XXX note that this watches outgoing newlines (and eats them), but
+ * does not handle wrap detection (req. count of columns).
+ */
+
+int
+pager_output(const char *cp)
+{
+ int action;
+
+ if (cp == NULL)
+ return(0);
+
+ for (;;) {
+ if (*cp == 0)
+ return(0);
+
+ putchar(*cp); /* always emit character */
+
+ if (*(cp++) == '\n') { /* got a newline? */
+ p_freelines--;
+ if (p_freelines <= 0) {
+ printf("%s", pager_prompt1);
+ action = 0;
+ while (action == 0) {
+ switch(getchar()) {
+ case '\r':
+ case '\n':
+ p_freelines = 1;
+ action = 1;
+ break;
+ case ' ':
+ p_freelines = p_maxlines;
+ action = 1;
+ break;
+ case 'q':
+ case 'Q':
+ action = 2;
+ break;
+ default:
+ break;
+ }
+ }
+ printf("\r%s\r", pager_blank);
+ if (action == 2)
+ return(1);
+ }
+ }
+ }
+}
+
+/*
+ * Display from (fd).
+ */
+int
+pager_file(const char *fname)
+{
+ char buf[80];
+ size_t hmuch;
+ int fd;
+ int result;
+
+ if ((fd = open(fname, O_RDONLY)) == -1) {
+ printf("can't open '%s': %s\n", fname, strerror(errno));
+ return(-1);
+ }
+
+ for (;;) {
+ hmuch = read(fd, buf, sizeof(buf) - 1);
+ if (hmuch == -1) {
+ result = -1;
+ break;
+ }
+ if (hmuch == 0) {
+ result = 0;
+ break;
+ }
+ buf[hmuch] = 0;
+ if (pager_output(buf)) {
+ result = 1;
+ break;
+ }
+ }
+ close(fd);
+ return(result);
+}
diff --git a/stand/libsa/panic.c b/stand/libsa/panic.c
new file mode 100644
index 0000000..6e4c76d
--- /dev/null
+++ b/stand/libsa/panic.c
@@ -0,0 +1,59 @@
+/*
+ * $NetBSD: panic.c,v 1.2 1997/03/22 01:48:36 thorpej Exp $
+ */
+/*-
+ * Copyright (c) 1996
+ * Matthias Drochner. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed for the NetBSD Project
+ * by Matthias Drochner.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * 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/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stand.h>
+#include <machine/stdarg.h>
+
+extern void exit(int) __dead2;
+
+void
+panic(const char *fmt,...)
+{
+ va_list ap;
+
+ printf("panic: ");
+ va_start(ap, fmt);
+ vprintf(fmt, ap);
+ va_end(ap);
+ printf("\n");
+
+ printf("--> Press a key on the console to reboot <--\n");
+ getchar();
+ printf("Rebooting...\n");
+ exit(1);
+}
diff --git a/stand/libsa/pkgfs.c b/stand/libsa/pkgfs.c
new file mode 100644
index 0000000..fda7f60
--- /dev/null
+++ b/stand/libsa/pkgfs.c
@@ -0,0 +1,791 @@
+/*-
+ * Copyright (c) 2007-2014, Juniper Networks, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "stand.h"
+
+#include <sys/stat.h>
+#include <sys/stdint.h>
+#include <string.h>
+#include <zlib.h>
+
+#ifdef PKGFS_DEBUG
+#define DBG(x) printf x
+#else
+#define DBG(x)
+#endif
+
+static int pkg_open(const char *, struct open_file *);
+static int pkg_close(struct open_file *);
+static int pkg_read(struct open_file *, void *, size_t, size_t *);
+static off_t pkg_seek(struct open_file *, off_t, int);
+static int pkg_stat(struct open_file *, struct stat *);
+static int pkg_readdir(struct open_file *, struct dirent *);
+
+struct fs_ops pkgfs_fsops = {
+ "pkg",
+ pkg_open,
+ pkg_close,
+ pkg_read,
+ null_write,
+ pkg_seek,
+ pkg_stat,
+ pkg_readdir
+};
+
+#define PKG_BUFSIZE 512
+#define PKG_MAXCACHESZ 4096
+
+#define PKG_FILEEXT ".tgz"
+
+/*
+ * Layout of POSIX 'ustar' header.
+ */
+struct ustar_hdr {
+ char ut_name[100];
+ char ut_mode[8];
+ char ut_uid[8];
+ char ut_gid[8];
+ char ut_size[12];
+ char ut_mtime[12];
+ char ut_checksum[8];
+ char ut_typeflag[1];
+ char ut_linkname[100];
+ char ut_magic[6]; /* For POSIX: "ustar\0" */
+ char ut_version[2]; /* For POSIX: "00" */
+ char ut_uname[32];
+ char ut_gname[32];
+ char ut_rdevmajor[8];
+ char ut_rdevminor[8];
+ union {
+ struct {
+ char prefix[155];
+ } posix;
+ struct {
+ char atime[12];
+ char ctime[12];
+ char offset[12];
+ char longnames[4];
+ char unused[1];
+ struct gnu_sparse {
+ char offset[12];
+ char numbytes[12];
+ } sparse[4];
+ char isextended[1];
+ char realsize[12];
+ } gnu;
+ } u;
+ u_char __padding[12];
+};
+
+struct package;
+
+struct tarfile
+{
+ struct package *tf_pkg;
+ struct tarfile *tf_next;
+ struct ustar_hdr tf_hdr;
+ off_t tf_ofs;
+ off_t tf_size;
+ off_t tf_fp;
+ size_t tf_cachesz;
+ void *tf_cache;
+};
+
+struct package
+{
+ struct package *pkg_chain;
+ int pkg_fd;
+ off_t pkg_ofs;
+ z_stream pkg_zs;
+ struct tarfile *pkg_first;
+ struct tarfile *pkg_last;
+ u_char pkg_buf[PKG_BUFSIZE];
+};
+
+static struct package *package = NULL;
+
+static int new_package(int, struct package **);
+
+void
+pkgfs_cleanup(void)
+{
+ struct package *chain;
+ struct tarfile *tf, *tfn;
+
+ while (package != NULL) {
+ inflateEnd(&package->pkg_zs);
+ close(package->pkg_fd);
+
+ tf = package->pkg_first;
+ while (tf != NULL) {
+ tfn = tf->tf_next;
+ if (tf->tf_cachesz > 0)
+ free(tf->tf_cache);
+ free(tf);
+ tf = tfn;
+ }
+
+ chain = package->pkg_chain;
+ free(package);
+ package = chain;
+ }
+}
+
+int
+pkgfs_init(const char *pkgname, struct fs_ops *proto)
+{
+ struct package *pkg;
+ int error, fd;
+
+ if (proto != &pkgfs_fsops)
+ pkgfs_cleanup();
+
+ exclusive_file_system = proto;
+
+ fd = open(pkgname, O_RDONLY);
+
+ exclusive_file_system = NULL;
+
+ if (fd == -1)
+ return (errno);
+
+ error = new_package(fd, &pkg);
+ if (error) {
+ close(fd);
+ return (error);
+ }
+
+ if (pkg == NULL)
+ return (EDOOFUS);
+
+ pkg->pkg_chain = package;
+ package = pkg;
+ exclusive_file_system = &pkgfs_fsops;
+ return (0);
+}
+
+static int get_mode(struct tarfile *);
+static int get_zipped(struct package *, void *, size_t);
+static int new_package(int, struct package **);
+static struct tarfile *scan_tarfile(struct package *, struct tarfile *);
+
+static int
+pkg_open(const char *fn, struct open_file *f)
+{
+ struct tarfile *tf;
+
+ if (fn == NULL || f == NULL)
+ return (EINVAL);
+
+ if (package == NULL)
+ return (ENXIO);
+
+ /*
+ * We can only read from a package, so reject request to open
+ * for write-only or read-write.
+ */
+ if (f->f_flags != F_READ)
+ return (EPERM);
+
+ /*
+ * Scan the file headers for the named file. We stop scanning
+ * at the first filename that has the .pkg extension. This is
+ * a package within a package. We assume we have all the files
+ * we need up-front and without having to dig within nested
+ * packages.
+ *
+ * Note that we preserve streaming properties as much as possible.
+ */
+ while (*fn == '/')
+ fn++;
+
+ /*
+ * Allow opening of the root directory for use by readdir()
+ * to support listing files in the package.
+ */
+ if (*fn == '\0') {
+ f->f_fsdata = NULL;
+ return (0);
+ }
+
+ tf = scan_tarfile(package, NULL);
+ while (tf != NULL) {
+ if (strcmp(fn, tf->tf_hdr.ut_name) == 0) {
+ f->f_fsdata = tf;
+ tf->tf_fp = 0; /* Reset the file pointer. */
+ return (0);
+ }
+ tf = scan_tarfile(package, tf);
+ }
+ return (errno);
+}
+
+static int
+pkg_close(struct open_file *f)
+{
+ struct tarfile *tf;
+
+ tf = (struct tarfile *)f->f_fsdata;
+ if (tf == NULL)
+ return (0);
+
+ /*
+ * Free up the cache if we read all of the file.
+ */
+ if (tf->tf_fp == tf->tf_size && tf->tf_cachesz > 0) {
+ free(tf->tf_cache);
+ tf->tf_cachesz = 0;
+ }
+ return (0);
+}
+
+static int
+pkg_read(struct open_file *f, void *buf, size_t size, size_t *res)
+{
+ struct tarfile *tf;
+ char *p;
+ off_t fp;
+ size_t sz;
+
+ tf = (struct tarfile *)f->f_fsdata;
+ if (tf == NULL) {
+ if (res != NULL)
+ *res = size;
+ return (EBADF);
+ }
+
+ fp = tf->tf_fp;
+ p = buf;
+ sz = 0;
+ while (size > 0) {
+ sz = tf->tf_size - fp;
+ if (fp < tf->tf_cachesz && tf->tf_cachesz < tf->tf_size)
+ sz = tf->tf_cachesz - fp;
+ if (size < sz)
+ sz = size;
+ if (sz == 0)
+ break;
+
+ if (fp < tf->tf_cachesz) {
+ /* Satisfy the request from cache. */
+ memcpy(p, tf->tf_cache + fp, sz);
+ fp += sz;
+ p += sz;
+ size -= sz;
+ continue;
+ }
+
+ if (get_zipped(tf->tf_pkg, p, sz) == -1) {
+ sz = -1;
+ break;
+ }
+
+ fp += sz;
+ p += sz;
+ size -= sz;
+
+ if (tf->tf_cachesz != 0)
+ continue;
+
+ tf->tf_cachesz = (sz <= PKG_MAXCACHESZ) ? sz : PKG_MAXCACHESZ;
+ tf->tf_cache = malloc(tf->tf_cachesz);
+ if (tf->tf_cache != NULL)
+ memcpy(tf->tf_cache, buf, tf->tf_cachesz);
+ else
+ tf->tf_cachesz = 0;
+ }
+
+ tf->tf_fp = fp;
+ if (res != NULL)
+ *res = size;
+ return ((sz == -1) ? errno : 0);
+}
+
+static off_t
+pkg_seek(struct open_file *f, off_t ofs, int whence)
+{
+ char buf[512];
+ struct tarfile *tf;
+ off_t delta;
+ size_t sz, res;
+ int error;
+
+ tf = (struct tarfile *)f->f_fsdata;
+ if (tf == NULL) {
+ errno = EBADF;
+ return (-1);
+ }
+
+ switch (whence) {
+ case SEEK_SET:
+ delta = ofs - tf->tf_fp;
+ break;
+ case SEEK_CUR:
+ delta = ofs;
+ break;
+ case SEEK_END:
+ delta = tf->tf_size - tf->tf_fp + ofs;
+ break;
+ default:
+ errno = EINVAL;
+ return (-1);
+ }
+
+ if (delta < 0) {
+ DBG(("%s: negative file seek (%jd)\n", __func__,
+ (intmax_t)delta));
+ errno = ESPIPE;
+ return (-1);
+ }
+
+ while (delta > 0 && tf->tf_fp < tf->tf_size) {
+ sz = (delta > sizeof(buf)) ? sizeof(buf) : delta;
+ error = pkg_read(f, buf, sz, &res);
+ if (error != 0) {
+ errno = error;
+ return (-1);
+ }
+ delta -= sz - res;
+ }
+
+ return (tf->tf_fp);
+}
+
+static int
+pkg_stat(struct open_file *f, struct stat *sb)
+{
+ struct tarfile *tf;
+
+ tf = (struct tarfile *)f->f_fsdata;
+ if (tf == NULL)
+ return (EBADF);
+ memset(sb, 0, sizeof(*sb));
+ sb->st_mode = get_mode(tf);
+ sb->st_size = tf->tf_size;
+ sb->st_blocks = (tf->tf_size + 511) / 512;
+ return (0);
+}
+
+static int
+pkg_readdir(struct open_file *f, struct dirent *d)
+{
+ struct tarfile *tf;
+
+ tf = (struct tarfile *)f->f_fsdata;
+ if (tf != NULL)
+ return (EBADF);
+
+ tf = scan_tarfile(package, NULL);
+ if (tf == NULL)
+ return (ENOENT);
+
+ d->d_fileno = 0;
+ d->d_reclen = sizeof(*d);
+ d->d_type = DT_REG;
+ memcpy(d->d_name, tf->tf_hdr.ut_name, sizeof(d->d_name));
+ return (0);
+}
+
+/*
+ * Low-level support functions.
+ */
+
+static int
+get_byte(struct package *pkg, off_t *op)
+{
+ int c;
+
+ if (pkg->pkg_zs.avail_in == 0) {
+ c = read(pkg->pkg_fd, pkg->pkg_buf, PKG_BUFSIZE);
+ if (c <= 0)
+ return (-1);
+ pkg->pkg_zs.avail_in = c;
+ pkg->pkg_zs.next_in = pkg->pkg_buf;
+ }
+
+ c = *pkg->pkg_zs.next_in;
+ pkg->pkg_zs.next_in++;
+ pkg->pkg_zs.avail_in--;
+ (*op)++;
+ return (c);
+}
+
+static int
+get_zipped(struct package *pkg, void *buf, size_t bufsz)
+{
+ int c;
+
+ pkg->pkg_zs.next_out = buf;
+ pkg->pkg_zs.avail_out = bufsz;
+
+ while (pkg->pkg_zs.avail_out) {
+ if (pkg->pkg_zs.avail_in == 0) {
+ c = read(pkg->pkg_fd, pkg->pkg_buf, PKG_BUFSIZE);
+ if (c <= 0) {
+ errno = EIO;
+ return (-1);
+ }
+ pkg->pkg_zs.avail_in = c;
+ pkg->pkg_zs.next_in = pkg->pkg_buf;
+ }
+
+ c = inflate(&pkg->pkg_zs, Z_SYNC_FLUSH);
+ if (c != Z_OK && c != Z_STREAM_END) {
+ errno = EIO;
+ return (-1);
+ }
+ }
+
+ pkg->pkg_ofs += bufsz;
+ return (0);
+}
+
+static int
+cache_data(struct tarfile *tf)
+{
+ struct package *pkg;
+ size_t sz;
+
+ if (tf == NULL) {
+ DBG(("%s: no file to cache data for?\n", __func__));
+ errno = EINVAL;
+ return (-1);
+ }
+
+ pkg = tf->tf_pkg;
+ if (pkg == NULL) {
+ DBG(("%s: no package associated with file?\n", __func__));
+ errno = EINVAL;
+ return (-1);
+ }
+
+ if (tf->tf_ofs != pkg->pkg_ofs) {
+ DBG(("%s: caching after partial read of file %s?\n",
+ __func__, tf->tf_hdr.ut_name));
+ errno = EINVAL;
+ return (-1);
+ }
+
+ /* We don't cache everything... */
+ if (tf->tf_size > PKG_MAXCACHESZ) {
+ errno = ENOMEM;
+ return (-1);
+ }
+
+ /* All files are padded to a multiple of 512 bytes. */
+ sz = (tf->tf_size + 0x1ff) & ~0x1ff;
+
+ tf->tf_cache = malloc(sz);
+ if (tf->tf_cache == NULL) {
+ DBG(("%s: could not allocate %d bytes\n", __func__, (int)sz));
+ errno = ENOMEM;
+ return (-1);
+ }
+
+ tf->tf_cachesz = sz;
+ return (get_zipped(pkg, tf->tf_cache, sz));
+}
+
+/*
+ * Note that this implementation does not (and should not!) obey
+ * locale settings; you cannot simply substitute strtol here, since
+ * it does obey locale.
+ */
+static off_t
+pkg_atol8(const char *p, unsigned char_cnt)
+{
+ int64_t l, limit, last_digit_limit;
+ int digit, sign, base;
+
+ base = 8;
+ limit = INT64_MAX / base;
+ last_digit_limit = INT64_MAX % base;
+
+ while (*p == ' ' || *p == '\t')
+ p++;
+ if (*p == '-') {
+ sign = -1;
+ p++;
+ } else
+ sign = 1;
+
+ l = 0;
+ digit = *p - '0';
+ while (digit >= 0 && digit < base && char_cnt-- > 0) {
+ if (l>limit || (l == limit && digit > last_digit_limit)) {
+ l = UINT64_MAX; /* Truncate on overflow. */
+ break;
+ }
+ l = (l * base) + digit;
+ digit = *++p - '0';
+ }
+ return (sign < 0) ? -l : l;
+}
+
+/*
+ * Parse a base-256 integer. This is just a straight signed binary
+ * value in big-endian order, except that the high-order bit is
+ * ignored. Remember that "int64_t" may or may not be exactly 64
+ * bits; the implementation here tries to avoid making any assumptions
+ * about the actual size of an int64_t. It does assume we're using
+ * twos-complement arithmetic, though.
+ */
+static int64_t
+pkg_atol256(const char *_p, unsigned char_cnt)
+{
+ int64_t l, upper_limit, lower_limit;
+ const unsigned char *p = (const unsigned char *)_p;
+
+ upper_limit = INT64_MAX / 256;
+ lower_limit = INT64_MIN / 256;
+
+ /* Pad with 1 or 0 bits, depending on sign. */
+ if ((0x40 & *p) == 0x40)
+ l = (int64_t)-1;
+ else
+ l = 0;
+ l = (l << 6) | (0x3f & *p++);
+ while (--char_cnt > 0) {
+ if (l > upper_limit) {
+ l = INT64_MAX; /* Truncate on overflow */
+ break;
+ } else if (l < lower_limit) {
+ l = INT64_MIN;
+ break;
+ }
+ l = (l << 8) | (0xff & (int64_t)*p++);
+ }
+ return (l);
+}
+
+static off_t
+pkg_atol(const char *p, unsigned char_cnt)
+{
+ /*
+ * Technically, GNU pkg considers a field to be in base-256
+ * only if the first byte is 0xff or 0x80.
+ */
+ if (*p & 0x80)
+ return (pkg_atol256(p, char_cnt));
+ return (pkg_atol8(p, char_cnt));
+}
+
+static int
+get_mode(struct tarfile *tf)
+{
+ return (pkg_atol(tf->tf_hdr.ut_mode, sizeof(tf->tf_hdr.ut_mode)));
+}
+
+/* GZip flag byte */
+#define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */
+#define HEAD_CRC 0x02 /* bit 1 set: header CRC present */
+#define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */
+#define ORIG_NAME 0x08 /* bit 3 set: original file name present */
+#define COMMENT 0x10 /* bit 4 set: file comment present */
+#define RESERVED 0xE0 /* bits 5..7: reserved */
+
+static int
+new_package(int fd, struct package **pp)
+{
+ struct package *pkg;
+ off_t ofs;
+ int flags, i, error;
+
+ pkg = malloc(sizeof(*pkg));
+ if (pkg == NULL)
+ return (ENOMEM);
+
+ bzero(pkg, sizeof(*pkg));
+ pkg->pkg_fd = fd;
+
+ /*
+ * Parse the header.
+ */
+ error = EFTYPE;
+ ofs = 0;
+
+ /* Check megic. */
+ if (get_byte(pkg, &ofs) != 0x1f || get_byte(pkg, &ofs) != 0x8b)
+ goto fail;
+ /* Check method. */
+ if (get_byte(pkg, &ofs) != Z_DEFLATED)
+ goto fail;
+ /* Check flags. */
+ flags = get_byte(pkg, &ofs);
+ if (flags & RESERVED)
+ goto fail;
+
+ /* Skip time, xflags and OS code. */
+ for (i = 0; i < 6; i++) {
+ if (get_byte(pkg, &ofs) == -1)
+ goto fail;
+ }
+
+ /* Skip extra field. */
+ if (flags & EXTRA_FIELD) {
+ i = (get_byte(pkg, &ofs) & 0xff) |
+ ((get_byte(pkg, &ofs) << 8) & 0xff);
+ while (i-- > 0) {
+ if (get_byte(pkg, &ofs) == -1)
+ goto fail;
+ }
+ }
+
+ /* Skip original file name. */
+ if (flags & ORIG_NAME) {
+ do {
+ i = get_byte(pkg, &ofs);
+ } while (i != 0 && i != -1);
+ if (i == -1)
+ goto fail;
+ }
+
+ /* Print the comment if it's there. */
+ if (flags & COMMENT) {
+ while (1) {
+ i = get_byte(pkg, &ofs);
+ if (i == -1)
+ goto fail;
+ if (i == 0)
+ break;
+ putchar(i);
+ }
+ }
+
+ /* Skip the CRC. */
+ if (flags & HEAD_CRC) {
+ if (get_byte(pkg, &ofs) == -1)
+ goto fail;
+ if (get_byte(pkg, &ofs) == -1)
+ goto fail;
+ }
+
+ /*
+ * Done parsing the ZIP header. Spkgt the inflation engine.
+ */
+ error = inflateInit2(&pkg->pkg_zs, -15);
+ if (error != Z_OK)
+ goto fail;
+
+ *pp = pkg;
+ return (0);
+
+ fail:
+ free(pkg);
+ return (error);
+}
+
+static struct tarfile *
+scan_tarfile(struct package *pkg, struct tarfile *last)
+{
+ char buf[512];
+ struct tarfile *cur;
+ off_t ofs;
+ size_t sz;
+
+ cur = (last != NULL) ? last->tf_next : pkg->pkg_first;
+ if (cur == NULL) {
+ ofs = (last != NULL) ? last->tf_ofs + last->tf_size :
+ pkg->pkg_ofs;
+ ofs = (ofs + 0x1ff) & ~0x1ff;
+
+ /* Check if we've reached EOF. */
+ if (ofs < pkg->pkg_ofs) {
+ errno = ENOSPC;
+ return (NULL);
+ }
+
+ if (ofs != pkg->pkg_ofs) {
+ if (last != NULL && pkg->pkg_ofs == last->tf_ofs) {
+ if (cache_data(last) == -1)
+ return (NULL);
+ } else {
+ sz = ofs - pkg->pkg_ofs;
+ while (sz != 0) {
+ if (sz > sizeof(buf))
+ sz = sizeof(buf);
+ if (get_zipped(pkg, buf, sz) == -1)
+ return (NULL);
+ sz = ofs - pkg->pkg_ofs;
+ }
+ }
+ }
+
+ cur = malloc(sizeof(*cur));
+ if (cur == NULL)
+ return (NULL);
+ memset(cur, 0, sizeof(*cur));
+ cur->tf_pkg = pkg;
+
+ while (1) {
+ if (get_zipped(pkg, &cur->tf_hdr,
+ sizeof(cur->tf_hdr)) == -1) {
+ free(cur);
+ return (NULL);
+ }
+
+ /*
+ * There are always 2 empty blocks appended to
+ * a PKG. It marks the end of the archive.
+ */
+ if (strncmp(cur->tf_hdr.ut_magic, "ustar", 5) != 0) {
+ free(cur);
+ errno = ENOSPC;
+ return (NULL);
+ }
+
+ cur->tf_ofs = pkg->pkg_ofs;
+ cur->tf_size = pkg_atol(cur->tf_hdr.ut_size,
+ sizeof(cur->tf_hdr.ut_size));
+
+ if (cur->tf_hdr.ut_name[0] != '+')
+ break;
+
+ /*
+ * Skip package meta-files.
+ */
+ ofs = cur->tf_ofs + cur->tf_size;
+ ofs = (ofs + 0x1ff) & ~0x1ff;
+ while (pkg->pkg_ofs < ofs) {
+ if (get_zipped(pkg, buf, sizeof(buf)) == -1) {
+ free(cur);
+ return (NULL);
+ }
+ }
+ }
+
+ if (last != NULL)
+ last->tf_next = cur;
+ else
+ pkg->pkg_first = cur;
+ pkg->pkg_last = cur;
+ }
+
+ return (cur);
+}
diff --git a/stand/libsa/powerpc/_setjmp.S b/stand/libsa/powerpc/_setjmp.S
new file mode 100644
index 0000000..7c7c24b
--- /dev/null
+++ b/stand/libsa/powerpc/_setjmp.S
@@ -0,0 +1,115 @@
+/* $FreeBSD$ */
+/* from: NetBSD: setjmp.S,v 1.1 1998/01/27 15:13:12 sakamoto Exp $ */
+/* from: OpenBSD: setjmp.S,v 1.2 1996/12/28 06:22:18 rahnds Exp */
+/* kernel version of this file, does not have signal goop */
+/* int setjmp(jmp_buf env) */
+
+#include <machine/asm.h>
+
+#ifdef __powerpc64__
+#define LD_REG ld
+#define ST_REG std
+#define REGWIDTH 8
+#else
+#define LD_REG lwz
+#define ST_REG stw
+#define REGWIDTH 4
+#endif
+
+#define JMP_r1 1*REGWIDTH
+#define JMP_r2 2*REGWIDTH
+#define JMP_r14 3*REGWIDTH
+#define JMP_r15 4*REGWIDTH
+#define JMP_r16 5*REGWIDTH
+#define JMP_r17 6*REGWIDTH
+#define JMP_r18 7*REGWIDTH
+#define JMP_r19 8*REGWIDTH
+#define JMP_r20 9*REGWIDTH
+#define JMP_r21 10*REGWIDTH
+#define JMP_r22 11*REGWIDTH
+#define JMP_r23 12*REGWIDTH
+#define JMP_r24 13*REGWIDTH
+#define JMP_r25 14*REGWIDTH
+#define JMP_r26 15*REGWIDTH
+#define JMP_r27 16*REGWIDTH
+#define JMP_r28 17*REGWIDTH
+#define JMP_r29 18*REGWIDTH
+#define JMP_r30 19*REGWIDTH
+#define JMP_r31 20*REGWIDTH
+#define JMP_lr 21*REGWIDTH
+#define JMP_cr 22*REGWIDTH
+#define JMP_ctr 23*REGWIDTH
+#define JMP_xer 24*REGWIDTH
+#define JMP_sig 25*REGWIDTH
+
+ASENTRY_NOPROF(_setjmp)
+ ST_REG 31, JMP_r31(3)
+ /* r1, r2, r14-r30 */
+ ST_REG 1, JMP_r1 (3)
+ ST_REG 2, JMP_r2 (3)
+ ST_REG 14, JMP_r14(3)
+ ST_REG 15, JMP_r15(3)
+ ST_REG 16, JMP_r16(3)
+ ST_REG 17, JMP_r17(3)
+ ST_REG 18, JMP_r18(3)
+ ST_REG 19, JMP_r19(3)
+ ST_REG 20, JMP_r20(3)
+ ST_REG 21, JMP_r21(3)
+ ST_REG 22, JMP_r22(3)
+ ST_REG 23, JMP_r23(3)
+ ST_REG 24, JMP_r24(3)
+ ST_REG 25, JMP_r25(3)
+ ST_REG 26, JMP_r26(3)
+ ST_REG 27, JMP_r27(3)
+ ST_REG 28, JMP_r28(3)
+ ST_REG 29, JMP_r29(3)
+ ST_REG 30, JMP_r30(3)
+ /* cr, lr, ctr, xer */
+ mfcr 0
+ ST_REG 0, JMP_cr(3)
+ mflr 0
+ ST_REG 0, JMP_lr(3)
+ mfctr 0
+ ST_REG 0, JMP_ctr(3)
+ mfxer 0
+ ST_REG 0, JMP_xer(3)
+ /* f14-f31, fpscr */
+ li 3, 0
+ blr
+
+
+.extern sigsetmask
+ASENTRY_NOPROF(_longjmp)
+ LD_REG 31, JMP_r31(3)
+ /* r1, r2, r14-r30 */
+ LD_REG 1, JMP_r1 (3)
+ LD_REG 2, JMP_r2 (3)
+ LD_REG 14, JMP_r14(3)
+ LD_REG 15, JMP_r15(3)
+ LD_REG 16, JMP_r16(3)
+ LD_REG 17, JMP_r17(3)
+ LD_REG 18, JMP_r18(3)
+ LD_REG 19, JMP_r19(3)
+ LD_REG 20, JMP_r20(3)
+ LD_REG 21, JMP_r21(3)
+ LD_REG 22, JMP_r22(3)
+ LD_REG 23, JMP_r23(3)
+ LD_REG 24, JMP_r24(3)
+ LD_REG 25, JMP_r25(3)
+ LD_REG 26, JMP_r26(3)
+ LD_REG 27, JMP_r27(3)
+ LD_REG 28, JMP_r28(3)
+ LD_REG 29, JMP_r29(3)
+ LD_REG 30, JMP_r30(3)
+ /* cr, lr, ctr, xer */
+ LD_REG 0, JMP_cr(3)
+ mtcr 0
+ LD_REG 0, JMP_lr(3)
+ mtlr 0
+ LD_REG 0, JMP_ctr(3)
+ mtctr 0
+ LD_REG 0, JMP_xer(3)
+ mtxer 0
+ /* f14-f31, fpscr */
+ mr 3, 4
+ blr
diff --git a/stand/libsa/powerpc/syncicache.c b/stand/libsa/powerpc/syncicache.c
new file mode 100644
index 0000000..434dcec
--- /dev/null
+++ b/stand/libsa/powerpc/syncicache.c
@@ -0,0 +1,103 @@
+/*-
+ * Copyright (C) 1995-1997, 1999 Wolfgang Solfrank.
+ * Copyright (C) 1995-1997, 1999 TooLs GmbH.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by TooLs GmbH.
+ * 4. The name of TooLs GmbH may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``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 TOOLS GMBH 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.
+ *
+ * $NetBSD: syncicache.c,v 1.2 1999/05/05 12:36:40 tsubai Exp $
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <sys/param.h>
+#if defined(_KERNEL) || defined(_STANDALONE)
+#include <sys/time.h>
+#include <sys/proc.h>
+#include <vm/vm.h>
+#endif
+#include <sys/sysctl.h>
+
+#include <machine/cpu.h>
+#include <machine/md_var.h>
+
+#ifdef _STANDALONE
+int cacheline_size = 32;
+#endif
+
+#if !defined(_KERNEL) && !defined(_STANDALONE)
+#include <stdlib.h>
+
+int cacheline_size = 0;
+
+static void getcachelinesize(void);
+
+static void
+getcachelinesize()
+{
+ static int cachemib[] = { CTL_MACHDEP, CPU_CACHELINE };
+ int clen;
+
+ clen = sizeof(cacheline_size);
+
+ if (sysctl(cachemib, sizeof(cachemib) / sizeof(cachemib[0]),
+ &cacheline_size, &clen, NULL, 0) < 0 || !cacheline_size) {
+ abort();
+ }
+}
+#endif
+
+void
+__syncicache(void *from, int len)
+{
+ int l, off;
+ char *p;
+
+#if !defined(_KERNEL) && !defined(_STANDALONE)
+ if (!cacheline_size)
+ getcachelinesize();
+#endif
+
+ off = (u_int)from & (cacheline_size - 1);
+ l = len += off;
+ p = (char *)from - off;
+
+ do {
+ __asm __volatile ("dcbst 0,%0" :: "r"(p));
+ p += cacheline_size;
+ } while ((l -= cacheline_size) > 0);
+ __asm __volatile ("sync");
+ p = (char *)from - off;
+ do {
+ __asm __volatile ("icbi 0,%0" :: "r"(p));
+ p += cacheline_size;
+ } while ((len -= cacheline_size) > 0);
+ __asm __volatile ("sync; isync");
+}
+
diff --git a/stand/libsa/printf.c b/stand/libsa/printf.c
new file mode 100644
index 0000000..71e313c
--- /dev/null
+++ b/stand/libsa/printf.c
@@ -0,0 +1,518 @@
+/*-
+ * Copyright (c) 1986, 1988, 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)subr_prf.c 8.3 (Berkeley) 1/21/94
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Standaloneified version of the FreeBSD kernel printf family.
+ */
+
+#include <sys/types.h>
+#include <sys/stddef.h>
+#include <sys/stdint.h>
+#include <limits.h>
+#include <string.h>
+#include "stand.h"
+
+/*
+ * Note that stdarg.h and the ANSI style va_start macro is used for both
+ * ANSI and traditional C compilers.
+ */
+#include <machine/stdarg.h>
+
+#define MAXNBUF (sizeof(intmax_t) * CHAR_BIT + 1)
+
+typedef void (kvprintf_fn_t)(int, void *);
+
+static char *ksprintn (char *buf, uintmax_t num, int base, int *len, int upper);
+static int kvprintf(char const *fmt, kvprintf_fn_t *func, void *arg, int radix, va_list ap);
+
+static void
+putchar_wrapper(int cc, void *arg)
+{
+
+ putchar(cc);
+}
+
+int
+printf(const char *fmt, ...)
+{
+ va_list ap;
+ int retval;
+
+ va_start(ap, fmt);
+ retval = kvprintf(fmt, putchar_wrapper, NULL, 10, ap);
+ va_end(ap);
+ return retval;
+}
+
+void
+vprintf(const char *fmt, va_list ap)
+{
+
+ kvprintf(fmt, putchar_wrapper, NULL, 10, ap);
+}
+
+int
+sprintf(char *buf, const char *cfmt, ...)
+{
+ int retval;
+ va_list ap;
+
+ va_start(ap, cfmt);
+ retval = kvprintf(cfmt, NULL, (void *)buf, 10, ap);
+ buf[retval] = '\0';
+ va_end(ap);
+ return retval;
+}
+
+struct print_buf {
+ char *buf;
+ size_t size;
+};
+
+static void
+snprint_func(int ch, void *arg)
+{
+ struct print_buf *pbuf = arg;
+
+ if (pbuf->size < 2) {
+ /*
+ * Reserve last buffer position for the terminating
+ * character:
+ */
+ return;
+ }
+ *(pbuf->buf)++ = ch;
+ pbuf->size--;
+}
+
+int
+snprintf(char *buf, size_t size, const char *cfmt, ...)
+{
+ int retval;
+ va_list ap;
+ struct print_buf arg;
+
+ arg.buf = buf;
+ arg.size = size;
+
+ va_start(ap, cfmt);
+ retval = kvprintf(cfmt, &snprint_func, &arg, 10, ap);
+ va_end(ap);
+
+ if (arg.size >= 1)
+ *(arg.buf)++ = 0;
+ return retval;
+}
+
+void
+vsprintf(char *buf, const char *cfmt, va_list ap)
+{
+ int retval;
+
+ retval = kvprintf(cfmt, NULL, (void *)buf, 10, ap);
+ buf[retval] = '\0';
+}
+
+/*
+ * Put a NUL-terminated ASCII number (base <= 36) in a buffer in reverse
+ * order; return an optional length and a pointer to the last character
+ * written in the buffer (i.e., the first character of the string).
+ * The buffer pointed to by `nbuf' must have length >= MAXNBUF.
+ */
+static char *
+ksprintn(char *nbuf, uintmax_t num, int base, int *lenp, int upper)
+{
+ char *p, c;
+
+ p = nbuf;
+ *p = '\0';
+ do {
+ c = hex2ascii(num % base);
+ *++p = upper ? toupper(c) : c;
+ } while (num /= base);
+ if (lenp)
+ *lenp = p - nbuf;
+ return (p);
+}
+
+/*
+ * Scaled down version of printf(3).
+ *
+ * Two additional formats:
+ *
+ * The format %b is supported to decode error registers.
+ * Its usage is:
+ *
+ * printf("reg=%b\n", regval, "<base><arg>*");
+ *
+ * where <base> is the output base expressed as a control character, e.g.
+ * \10 gives octal; \20 gives hex. Each arg is a sequence of characters,
+ * the first of which gives the bit number to be inspected (origin 1), and
+ * the next characters (up to a control character, i.e. a character <= 32),
+ * give the name of the register. Thus:
+ *
+ * kvprintf("reg=%b\n", 3, "\10\2BITTWO\1BITONE");
+ *
+ * would produce output:
+ *
+ * reg=3<BITTWO,BITONE>
+ *
+ * XXX: %D -- Hexdump, takes pointer and separator string:
+ * ("%6D", ptr, ":") -> XX:XX:XX:XX:XX:XX
+ * ("%*D", len, ptr, " " -> XX XX XX XX ...
+ */
+static int
+kvprintf(char const *fmt, kvprintf_fn_t *func, void *arg, int radix, va_list ap)
+{
+#define PCHAR(c) {int cc=(c); if (func) (*func)(cc, arg); else *d++ = cc; retval++; }
+ char nbuf[MAXNBUF];
+ char *d;
+ const char *p, *percent, *q;
+ uint16_t *S;
+ u_char *up;
+ int ch, n;
+ uintmax_t num;
+ int base, lflag, qflag, tmp, width, ladjust, sharpflag, neg, sign, dot;
+ int cflag, hflag, jflag, tflag, zflag;
+ int dwidth, upper;
+ char padc;
+ int stop = 0, retval = 0;
+
+ num = 0;
+ if (!func)
+ d = (char *) arg;
+ else
+ d = NULL;
+
+ if (fmt == NULL)
+ fmt = "(fmt null)\n";
+
+ if (radix < 2 || radix > 36)
+ radix = 10;
+
+ for (;;) {
+ padc = ' ';
+ width = 0;
+ while ((ch = (u_char)*fmt++) != '%' || stop) {
+ if (ch == '\0')
+ return (retval);
+ PCHAR(ch);
+ }
+ percent = fmt - 1;
+ qflag = 0; lflag = 0; ladjust = 0; sharpflag = 0; neg = 0;
+ sign = 0; dot = 0; dwidth = 0; upper = 0;
+ cflag = 0; hflag = 0; jflag = 0; tflag = 0; zflag = 0;
+reswitch: switch (ch = (u_char)*fmt++) {
+ case '.':
+ dot = 1;
+ goto reswitch;
+ case '#':
+ sharpflag = 1;
+ goto reswitch;
+ case '+':
+ sign = 1;
+ goto reswitch;
+ case '-':
+ ladjust = 1;
+ goto reswitch;
+ case '%':
+ PCHAR(ch);
+ break;
+ case '*':
+ if (!dot) {
+ width = va_arg(ap, int);
+ if (width < 0) {
+ ladjust = !ladjust;
+ width = -width;
+ }
+ } else {
+ dwidth = va_arg(ap, int);
+ }
+ goto reswitch;
+ case '0':
+ if (!dot) {
+ padc = '0';
+ goto reswitch;
+ }
+ case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ for (n = 0;; ++fmt) {
+ n = n * 10 + ch - '0';
+ ch = *fmt;
+ if (ch < '0' || ch > '9')
+ break;
+ }
+ if (dot)
+ dwidth = n;
+ else
+ width = n;
+ goto reswitch;
+ case 'b':
+ num = (u_int)va_arg(ap, int);
+ p = va_arg(ap, char *);
+ for (q = ksprintn(nbuf, num, *p++, NULL, 0); *q;)
+ PCHAR(*q--);
+
+ if (num == 0)
+ break;
+
+ for (tmp = 0; *p;) {
+ n = *p++;
+ if (num & (1 << (n - 1))) {
+ PCHAR(tmp ? ',' : '<');
+ for (; (n = *p) > ' '; ++p)
+ PCHAR(n);
+ tmp = 1;
+ } else
+ for (; *p > ' '; ++p)
+ continue;
+ }
+ if (tmp)
+ PCHAR('>');
+ break;
+ case 'c':
+ PCHAR(va_arg(ap, int));
+ break;
+ case 'D':
+ up = va_arg(ap, u_char *);
+ p = va_arg(ap, char *);
+ if (!width)
+ width = 16;
+ while(width--) {
+ PCHAR(hex2ascii(*up >> 4));
+ PCHAR(hex2ascii(*up & 0x0f));
+ up++;
+ if (width)
+ for (q=p;*q;q++)
+ PCHAR(*q);
+ }
+ break;
+ case 'd':
+ case 'i':
+ base = 10;
+ sign = 1;
+ goto handle_sign;
+ case 'h':
+ if (hflag) {
+ hflag = 0;
+ cflag = 1;
+ } else
+ hflag = 1;
+ goto reswitch;
+ case 'j':
+ jflag = 1;
+ goto reswitch;
+ case 'l':
+ if (lflag) {
+ lflag = 0;
+ qflag = 1;
+ } else
+ lflag = 1;
+ goto reswitch;
+ case 'n':
+ if (jflag)
+ *(va_arg(ap, intmax_t *)) = retval;
+ else if (qflag)
+ *(va_arg(ap, quad_t *)) = retval;
+ else if (lflag)
+ *(va_arg(ap, long *)) = retval;
+ else if (zflag)
+ *(va_arg(ap, size_t *)) = retval;
+ else if (hflag)
+ *(va_arg(ap, short *)) = retval;
+ else if (cflag)
+ *(va_arg(ap, char *)) = retval;
+ else
+ *(va_arg(ap, int *)) = retval;
+ break;
+ case 'o':
+ base = 8;
+ goto handle_nosign;
+ case 'p':
+ base = 16;
+ sharpflag = (width == 0);
+ sign = 0;
+ num = (uintptr_t)va_arg(ap, void *);
+ goto number;
+ case 'q':
+ qflag = 1;
+ goto reswitch;
+ case 'r':
+ base = radix;
+ if (sign)
+ goto handle_sign;
+ goto handle_nosign;
+ case 's':
+ p = va_arg(ap, char *);
+ if (p == NULL)
+ p = "(null)";
+ if (!dot)
+ n = strlen (p);
+ else
+ for (n = 0; n < dwidth && p[n]; n++)
+ continue;
+
+ width -= n;
+
+ if (!ladjust && width > 0)
+ while (width--)
+ PCHAR(padc);
+ while (n--)
+ PCHAR(*p++);
+ if (ladjust && width > 0)
+ while (width--)
+ PCHAR(padc);
+ break;
+ case 'S': /* Assume console can cope with wide chars */
+ for (S = va_arg(ap, uint16_t *); *S != 0; S++)
+ PCHAR(*S);
+ break;
+ case 't':
+ tflag = 1;
+ goto reswitch;
+ case 'u':
+ base = 10;
+ goto handle_nosign;
+ case 'X':
+ upper = 1;
+ case 'x':
+ base = 16;
+ goto handle_nosign;
+ case 'y':
+ base = 16;
+ sign = 1;
+ goto handle_sign;
+ case 'z':
+ zflag = 1;
+ goto reswitch;
+handle_nosign:
+ sign = 0;
+ if (jflag)
+ num = va_arg(ap, uintmax_t);
+ else if (qflag)
+ num = va_arg(ap, u_quad_t);
+ else if (tflag)
+ num = va_arg(ap, ptrdiff_t);
+ else if (lflag)
+ num = va_arg(ap, u_long);
+ else if (zflag)
+ num = va_arg(ap, size_t);
+ else if (hflag)
+ num = (u_short)va_arg(ap, int);
+ else if (cflag)
+ num = (u_char)va_arg(ap, int);
+ else
+ num = va_arg(ap, u_int);
+ goto number;
+handle_sign:
+ if (jflag)
+ num = va_arg(ap, intmax_t);
+ else if (qflag)
+ num = va_arg(ap, quad_t);
+ else if (tflag)
+ num = va_arg(ap, ptrdiff_t);
+ else if (lflag)
+ num = va_arg(ap, long);
+ else if (zflag)
+ num = va_arg(ap, ssize_t);
+ else if (hflag)
+ num = (short)va_arg(ap, int);
+ else if (cflag)
+ num = (char)va_arg(ap, int);
+ else
+ num = va_arg(ap, int);
+number:
+ if (sign && (intmax_t)num < 0) {
+ neg = 1;
+ num = -(intmax_t)num;
+ }
+ p = ksprintn(nbuf, num, base, &n, upper);
+ tmp = 0;
+ if (sharpflag && num != 0) {
+ if (base == 8)
+ tmp++;
+ else if (base == 16)
+ tmp += 2;
+ }
+ if (neg)
+ tmp++;
+
+ if (!ladjust && padc == '0')
+ dwidth = width - tmp;
+ width -= tmp + imax(dwidth, n);
+ dwidth -= n;
+ if (!ladjust)
+ while (width-- > 0)
+ PCHAR(' ');
+ if (neg)
+ PCHAR('-');
+ if (sharpflag && num != 0) {
+ if (base == 8) {
+ PCHAR('0');
+ } else if (base == 16) {
+ PCHAR('0');
+ PCHAR('x');
+ }
+ }
+ while (dwidth-- > 0)
+ PCHAR('0');
+
+ while (*p)
+ PCHAR(*p--);
+
+ if (ladjust)
+ while (width-- > 0)
+ PCHAR(' ');
+
+ break;
+ default:
+ while (percent < fmt)
+ PCHAR(*percent++);
+ /*
+ * Since we ignore a formatting argument it is no
+ * longer safe to obey the remaining formatting
+ * arguments as the arguments will no longer match
+ * the format specs.
+ */
+ stop = 1;
+ break;
+ }
+ }
+#undef PCHAR
+}
diff --git a/stand/libsa/qdivrem.c b/stand/libsa/qdivrem.c
new file mode 100644
index 0000000..bde3b0d
--- /dev/null
+++ b/stand/libsa/qdivrem.c
@@ -0,0 +1,348 @@
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This software was developed by the Computer Systems Engineering group
+ * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
+ * contributed to Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * From: Id: qdivrem.c,v 1.7 1997/11/07 09:20:40 phk Exp
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Multiprecision divide. This algorithm is from Knuth vol. 2 (2nd ed),
+ * section 4.3.1, pp. 257--259.
+ */
+
+#include "quad.h"
+
+#define B (1 << HALF_BITS) /* digit base */
+
+/* Combine two `digits' to make a single two-digit number. */
+#define COMBINE(a, b) (((u_int)(a) << HALF_BITS) | (b))
+
+_Static_assert(sizeof(int) / 2 == sizeof(short),
+ "Bitwise functions in libstand are broken on this architecture\n");
+
+/* select a type for digits in base B: use unsigned short if they fit */
+typedef unsigned short digit;
+
+/*
+ * Shift p[0]..p[len] left `sh' bits, ignoring any bits that
+ * `fall out' the left (there never will be any such anyway).
+ * We may assume len >= 0. NOTE THAT THIS WRITES len+1 DIGITS.
+ */
+static void
+shl(digit *p, int len, int sh)
+{
+ int i;
+
+ for (i = 0; i < len; i++)
+ p[i] = LHALF(p[i] << sh) | (p[i + 1] >> (HALF_BITS - sh));
+ p[i] = LHALF(p[i] << sh);
+}
+
+/*
+ * __qdivrem(u, v, rem) returns u/v and, optionally, sets *rem to u%v.
+ *
+ * We do this in base 2-sup-HALF_BITS, so that all intermediate products
+ * fit within u_int. As a consequence, the maximum length dividend and
+ * divisor are 4 `digits' in this base (they are shorter if they have
+ * leading zeros).
+ */
+u_quad_t
+__qdivrem(uq, vq, arq)
+ u_quad_t uq, vq, *arq;
+{
+ union uu tmp;
+ digit *u, *v, *q;
+ digit v1, v2;
+ u_int qhat, rhat, t;
+ int m, n, d, j, i;
+ digit uspace[5], vspace[5], qspace[5];
+
+ /*
+ * Take care of special cases: divide by zero, and u < v.
+ */
+ if (vq == 0) {
+ /* divide by zero. */
+ static volatile const unsigned int zero = 0;
+
+ tmp.ul[H] = tmp.ul[L] = 1 / zero;
+ if (arq)
+ *arq = uq;
+ return (tmp.q);
+ }
+ if (uq < vq) {
+ if (arq)
+ *arq = uq;
+ return (0);
+ }
+ u = &uspace[0];
+ v = &vspace[0];
+ q = &qspace[0];
+
+ /*
+ * Break dividend and divisor into digits in base B, then
+ * count leading zeros to determine m and n. When done, we
+ * will have:
+ * u = (u[1]u[2]...u[m+n]) sub B
+ * v = (v[1]v[2]...v[n]) sub B
+ * v[1] != 0
+ * 1 < n <= 4 (if n = 1, we use a different division algorithm)
+ * m >= 0 (otherwise u < v, which we already checked)
+ * m + n = 4
+ * and thus
+ * m = 4 - n <= 2
+ */
+ tmp.uq = uq;
+ u[0] = 0;
+ u[1] = HHALF(tmp.ul[H]);
+ u[2] = LHALF(tmp.ul[H]);
+ u[3] = HHALF(tmp.ul[L]);
+ u[4] = LHALF(tmp.ul[L]);
+ tmp.uq = vq;
+ v[1] = HHALF(tmp.ul[H]);
+ v[2] = LHALF(tmp.ul[H]);
+ v[3] = HHALF(tmp.ul[L]);
+ v[4] = LHALF(tmp.ul[L]);
+ for (n = 4; v[1] == 0; v++) {
+ if (--n == 1) {
+ u_int rbj; /* r*B+u[j] (not root boy jim) */
+ digit q1, q2, q3, q4;
+
+ /*
+ * Change of plan, per exercise 16.
+ * r = 0;
+ * for j = 1..4:
+ * q[j] = floor((r*B + u[j]) / v),
+ * r = (r*B + u[j]) % v;
+ * We unroll this completely here.
+ */
+ t = v[2]; /* nonzero, by definition */
+ q1 = u[1] / t;
+ rbj = COMBINE(u[1] % t, u[2]);
+ q2 = rbj / t;
+ rbj = COMBINE(rbj % t, u[3]);
+ q3 = rbj / t;
+ rbj = COMBINE(rbj % t, u[4]);
+ q4 = rbj / t;
+ if (arq)
+ *arq = rbj % t;
+ tmp.ul[H] = COMBINE(q1, q2);
+ tmp.ul[L] = COMBINE(q3, q4);
+ return (tmp.q);
+ }
+ }
+
+ /*
+ * By adjusting q once we determine m, we can guarantee that
+ * there is a complete four-digit quotient at &qspace[1] when
+ * we finally stop.
+ */
+ for (m = 4 - n; u[1] == 0; u++)
+ m--;
+ for (i = 4 - m; --i >= 0;)
+ q[i] = 0;
+ q += 4 - m;
+
+ /*
+ * Here we run Program D, translated from MIX to C and acquiring
+ * a few minor changes.
+ *
+ * D1: choose multiplier 1 << d to ensure v[1] >= B/2.
+ */
+ d = 0;
+ for (t = v[1]; t < B / 2; t <<= 1)
+ d++;
+ if (d > 0) {
+ shl(&u[0], m + n, d); /* u <<= d */
+ shl(&v[1], n - 1, d); /* v <<= d */
+ }
+ /*
+ * D2: j = 0.
+ */
+ j = 0;
+ v1 = v[1]; /* for D3 -- note that v[1..n] are constant */
+ v2 = v[2]; /* for D3 */
+ do {
+ digit uj0, uj1, uj2;
+
+ /*
+ * D3: Calculate qhat (\^q, in TeX notation).
+ * Let qhat = min((u[j]*B + u[j+1])/v[1], B-1), and
+ * let rhat = (u[j]*B + u[j+1]) mod v[1].
+ * While rhat < B and v[2]*qhat > rhat*B+u[j+2],
+ * decrement qhat and increase rhat correspondingly.
+ * Note that if rhat >= B, v[2]*qhat < rhat*B.
+ */
+ uj0 = u[j + 0]; /* for D3 only -- note that u[j+...] change */
+ uj1 = u[j + 1]; /* for D3 only */
+ uj2 = u[j + 2]; /* for D3 only */
+ if (uj0 == v1) {
+ qhat = B;
+ rhat = uj1;
+ goto qhat_too_big;
+ } else {
+ u_int nn = COMBINE(uj0, uj1);
+ qhat = nn / v1;
+ rhat = nn % v1;
+ }
+ while (v2 * qhat > COMBINE(rhat, uj2)) {
+ qhat_too_big:
+ qhat--;
+ if ((rhat += v1) >= B)
+ break;
+ }
+ /*
+ * D4: Multiply and subtract.
+ * The variable `t' holds any borrows across the loop.
+ * We split this up so that we do not require v[0] = 0,
+ * and to eliminate a final special case.
+ */
+ for (t = 0, i = n; i > 0; i--) {
+ t = u[i + j] - v[i] * qhat - t;
+ u[i + j] = LHALF(t);
+ t = (B - HHALF(t)) & (B - 1);
+ }
+ t = u[j] - t;
+ u[j] = LHALF(t);
+ /*
+ * D5: test remainder.
+ * There is a borrow if and only if HHALF(t) is nonzero;
+ * in that (rare) case, qhat was too large (by exactly 1).
+ * Fix it by adding v[1..n] to u[j..j+n].
+ */
+ if (HHALF(t)) {
+ qhat--;
+ for (t = 0, i = n; i > 0; i--) { /* D6: add back. */
+ t += u[i + j] + v[i];
+ u[i + j] = LHALF(t);
+ t = HHALF(t);
+ }
+ u[j] = LHALF(u[j] + t);
+ }
+ q[j] = qhat;
+ } while (++j <= m); /* D7: loop on j. */
+
+ /*
+ * If caller wants the remainder, we have to calculate it as
+ * u[m..m+n] >> d (this is at most n digits and thus fits in
+ * u[m+1..m+n], but we may need more source digits).
+ */
+ if (arq) {
+ if (d) {
+ for (i = m + n; i > m; --i)
+ u[i] = (u[i] >> d) |
+ LHALF(u[i - 1] << (HALF_BITS - d));
+ u[i] = 0;
+ }
+ tmp.ul[H] = COMBINE(uspace[1], uspace[2]);
+ tmp.ul[L] = COMBINE(uspace[3], uspace[4]);
+ *arq = tmp.q;
+ }
+
+ tmp.ul[H] = COMBINE(qspace[1], qspace[2]);
+ tmp.ul[L] = COMBINE(qspace[3], qspace[4]);
+ return (tmp.q);
+}
+
+/*
+ * Divide two unsigned quads.
+ */
+
+u_quad_t
+__udivdi3(a, b)
+ u_quad_t a, b;
+{
+
+ return (__qdivrem(a, b, (u_quad_t *)0));
+}
+
+/*
+ * Return remainder after dividing two unsigned quads.
+ */
+u_quad_t
+__umoddi3(a, b)
+ u_quad_t a, b;
+{
+ u_quad_t r;
+
+ (void)__qdivrem(a, b, &r);
+ return (r);
+}
+
+/*
+ * Divide two signed quads.
+ * ??? if -1/2 should produce -1 on this machine, this code is wrong
+ */
+quad_t
+__divdi3(a, b)
+ quad_t a, b;
+{
+ u_quad_t ua, ub, uq;
+ int neg;
+
+ if (a < 0)
+ ua = -(u_quad_t)a, neg = 1;
+ else
+ ua = a, neg = 0;
+ if (b < 0)
+ ub = -(u_quad_t)b, neg ^= 1;
+ else
+ ub = b;
+ uq = __qdivrem(ua, ub, (u_quad_t *)0);
+ return (neg ? -uq : uq);
+}
+
+/*
+ * Return remainder after dividing two signed quads.
+ *
+ * XXX
+ * If -1/2 should produce -1 on this machine, this code is wrong.
+ */
+quad_t
+__moddi3(a, b)
+ quad_t a, b;
+{
+ u_quad_t ua, ub, ur;
+ int neg;
+
+ if (a < 0)
+ ua = -(u_quad_t)a, neg = 1;
+ else
+ ua = a, neg = 0;
+ if (b < 0)
+ ub = -(u_quad_t)b;
+ else
+ ub = b;
+ (void)__qdivrem(ua, ub, &ur);
+ return (neg ? -ur : ur);
+}
diff --git a/stand/libsa/quad.h b/stand/libsa/quad.h
new file mode 100644
index 0000000..349540a
--- /dev/null
+++ b/stand/libsa/quad.h
@@ -0,0 +1,114 @@
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This software was developed by the Computer Systems Engineering group
+ * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
+ * contributed to Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)quad.h 8.1 (Berkeley) 6/4/93
+ * $FreeBSD$
+ */
+
+/*
+ * Quad arithmetic.
+ *
+ * This library makes the following assumptions:
+ *
+ * - The type long long (aka quad_t) exists.
+ *
+ * - A quad variable is exactly twice as long as `long'.
+ *
+ * - The machine's arithmetic is two's complement.
+ *
+ * This library can provide 128-bit arithmetic on a machine with 128-bit
+ * quads and 64-bit longs, for instance, or 96-bit arithmetic on machines
+ * with 48-bit longs.
+ */
+
+#include <sys/cdefs.h>
+#include <sys/types.h>
+#include <limits.h>
+
+_Static_assert(sizeof(quad_t) == sizeof(int) * 2,
+ "Bitwise function in libstand are broken on this architecture\n");
+
+/*
+ * Depending on the desired operation, we view a `long long' (aka quad_t) in
+ * one or more of the following formats.
+ */
+union uu {
+ quad_t q; /* as a (signed) quad */
+ quad_t uq; /* as an unsigned quad */
+ int sl[2]; /* as two signed ints */
+ u_int ul[2]; /* as two unsigned ints */
+};
+
+/*
+ * Define high and low longwords.
+ */
+#define H _QUAD_HIGHWORD
+#define L _QUAD_LOWWORD
+
+/*
+ * Total number of bits in a quad_t and in the pieces that make it up.
+ * These are used for shifting, and also below for halfword extraction
+ * and assembly.
+ */
+#define QUAD_BITS (sizeof(quad_t) * CHAR_BIT)
+#define HALF_BITS (sizeof(int) * CHAR_BIT / 2)
+
+/*
+ * Extract high and low shortwords from longword, and move low shortword of
+ * longword to upper half of long, i.e., produce the upper longword of
+ * ((quad_t)(x) << (number_of_bits_in_long/2)). (`x' must actually be u_long.)
+ *
+ * These are used in the multiply code, to split a longword into upper
+ * and lower halves, and to reassemble a product as a quad_t, shifted left
+ * (sizeof(long)*CHAR_BIT/2).
+ */
+#define HHALF(x) ((x) >> HALF_BITS)
+#define LHALF(x) ((x) & ((1 << HALF_BITS) - 1))
+#define LHUP(x) ((x) << HALF_BITS)
+
+quad_t __divdi3(quad_t a, quad_t b);
+quad_t __moddi3(quad_t a, quad_t b);
+u_quad_t __qdivrem(u_quad_t u, u_quad_t v, u_quad_t *rem);
+u_quad_t __udivdi3(u_quad_t a, u_quad_t b);
+u_quad_t __umoddi3(u_quad_t a, u_quad_t b);
+
+/*
+ * XXX
+ * Compensate for gcc 1 vs gcc 2. Gcc 1 defines ?sh?di3's second argument
+ * as u_quad_t, while gcc 2 correctly uses int. Unfortunately, we still use
+ * both compilers.
+ */
+#if __GNUC__ >= 2
+typedef unsigned int qshift_t;
+#else
+typedef u_quad_t qshift_t;
+#endif
diff --git a/stand/libsa/random.c b/stand/libsa/random.c
new file mode 100644
index 0000000..a5b2ad6
--- /dev/null
+++ b/stand/libsa/random.c
@@ -0,0 +1,70 @@
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)random.c 8.1 (Berkeley) 6/10/93
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+
+static u_long randseed = 1;
+
+void
+srandom(seed)
+ u_long seed;
+{
+ randseed = seed;
+}
+
+/*
+ * Pseudo-random number generator for randomizing the profiling clock,
+ * and whatever else we might use it for. The result is uniform on
+ * [0, 2^31 - 1].
+ */
+long
+random(void)
+{
+ long x, hi, lo, t;
+
+ /*
+ * Compute x[n + 1] = (7^5 * x[n]) mod (2^31 - 1).
+ * From "Random number generators: good ones are hard to find",
+ * Park and Miller, Communications of the ACM, vol. 31, no. 10,
+ * October 1988, p. 1195.
+ */
+ x = randseed;
+ hi = x / 127773;
+ lo = x % 127773;
+ t = 16807 * lo - 2836 * hi;
+ if (t <= 0)
+ t += 0x7fffffff;
+ randseed = t;
+ return (t);
+}
diff --git a/stand/libsa/rarp.c b/stand/libsa/rarp.c
new file mode 100644
index 0000000..3b8088e
--- /dev/null
+++ b/stand/libsa/rarp.c
@@ -0,0 +1,218 @@
+/* $NetBSD: rarp.c,v 1.16 1997/07/07 15:52:52 drochner Exp $ */
+
+/*
+ * Copyright (c) 1992 Regents of the University of California.
+ * All rights reserved.
+ *
+ * This software was developed by the Computer Systems Engineering group
+ * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
+ * contributed to Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#) Header: arp.c,v 1.5 93/07/15 05:52:26 leres Exp (LBL)
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+
+#include <netinet/in_systm.h>
+
+#include <string.h>
+
+#include "stand.h"
+#include "net.h"
+#include "netif.h"
+
+
+static ssize_t rarpsend(struct iodesc *, void *, size_t);
+static ssize_t rarprecv(struct iodesc *, void **, void **, time_t);
+
+/*
+ * Ethernet (Reverse) Address Resolution Protocol (see RFC 903, and 826).
+ */
+int
+rarp_getipaddress(int sock)
+{
+ struct iodesc *d;
+ struct ether_arp *ap;
+ void *pkt;
+ struct {
+ u_char header[ETHER_SIZE];
+ struct {
+ struct ether_arp arp;
+ u_char pad[18]; /* 60 - sizeof(arp) */
+ } data;
+ } wbuf;
+
+#ifdef RARP_DEBUG
+ if (debug)
+ printf("rarp: socket=%d\n", sock);
+#endif
+ if (!(d = socktodesc(sock))) {
+ printf("rarp: bad socket. %d\n", sock);
+ return (-1);
+ }
+#ifdef RARP_DEBUG
+ if (debug)
+ printf("rarp: d=%x\n", (u_int)d);
+#endif
+
+ bzero((char*)&wbuf.data, sizeof(wbuf.data));
+ ap = &wbuf.data.arp;
+ ap->arp_hrd = htons(ARPHRD_ETHER);
+ ap->arp_pro = htons(ETHERTYPE_IP);
+ ap->arp_hln = sizeof(ap->arp_sha); /* hardware address length */
+ ap->arp_pln = sizeof(ap->arp_spa); /* protocol address length */
+ ap->arp_op = htons(ARPOP_REVREQUEST);
+ bcopy(d->myea, ap->arp_sha, 6);
+ bcopy(d->myea, ap->arp_tha, 6);
+ pkt = NULL;
+
+ if (sendrecv(d,
+ rarpsend, &wbuf.data, sizeof(wbuf.data),
+ rarprecv, &pkt, (void *)&ap) < 0) {
+ printf("No response for RARP request\n");
+ return (-1);
+ }
+
+ bcopy(ap->arp_tpa, (char *)&myip, sizeof(myip));
+#if 0
+ /* XXX - Can NOT assume this is our root server! */
+ bcopy(ap->arp_spa, (char *)&rootip, sizeof(rootip));
+#endif
+ free(pkt);
+
+ /* Compute our "natural" netmask. */
+ if (IN_CLASSA(myip.s_addr))
+ netmask = IN_CLASSA_NET;
+ else if (IN_CLASSB(myip.s_addr))
+ netmask = IN_CLASSB_NET;
+ else
+ netmask = IN_CLASSC_NET;
+
+ d->myip = myip;
+ return (0);
+}
+
+/*
+ * Broadcast a RARP request (i.e. who knows who I am)
+ */
+static ssize_t
+rarpsend(struct iodesc *d, void *pkt, size_t len)
+{
+
+#ifdef RARP_DEBUG
+ if (debug)
+ printf("rarpsend: called\n");
+#endif
+
+ return (sendether(d, pkt, len, bcea, ETHERTYPE_REVARP));
+}
+
+/*
+ * Returns 0 if this is the packet we're waiting for
+ * else -1 (and errno == 0)
+ */
+static ssize_t
+rarprecv(struct iodesc *d, void **pkt, void **payload, time_t tleft)
+{
+ ssize_t n;
+ struct ether_arp *ap;
+ void *ptr = NULL;
+ uint16_t etype; /* host order */
+
+#ifdef RARP_DEBUG
+ if (debug)
+ printf("rarprecv: ");
+#endif
+
+ n = readether(d, &ptr, (void **)&ap, tleft, &etype);
+ errno = 0; /* XXX */
+ if (n == -1 || n < sizeof(struct ether_arp)) {
+#ifdef RARP_DEBUG
+ if (debug)
+ printf("bad len=%d\n", n);
+#endif
+ free(ptr);
+ return (-1);
+ }
+
+ if (etype != ETHERTYPE_REVARP) {
+#ifdef RARP_DEBUG
+ if (debug)
+ printf("bad type=0x%x\n", etype);
+#endif
+ free(ptr);
+ return (-1);
+ }
+
+ if (ap->arp_hrd != htons(ARPHRD_ETHER) ||
+ ap->arp_pro != htons(ETHERTYPE_IP) ||
+ ap->arp_hln != sizeof(ap->arp_sha) ||
+ ap->arp_pln != sizeof(ap->arp_spa) )
+ {
+#ifdef RARP_DEBUG
+ if (debug)
+ printf("bad hrd/pro/hln/pln\n");
+#endif
+ free(ptr);
+ return (-1);
+ }
+
+ if (ap->arp_op != htons(ARPOP_REVREPLY)) {
+#ifdef RARP_DEBUG
+ if (debug)
+ printf("bad op=0x%x\n", ntohs(ap->arp_op));
+#endif
+ free(ptr);
+ return (-1);
+ }
+
+ /* Is the reply for our Ethernet address? */
+ if (bcmp(ap->arp_tha, d->myea, 6)) {
+#ifdef RARP_DEBUG
+ if (debug)
+ printf("unwanted address\n");
+#endif
+ free(ptr);
+ return (-1);
+ }
+
+ /* We have our answer. */
+#ifdef RARP_DEBUG
+ if (debug)
+ printf("got it\n");
+#endif
+ *pkt = ptr;
+ *payload = ap;
+ return (n);
+}
diff --git a/stand/libsa/read.c b/stand/libsa/read.c
new file mode 100644
index 0000000..c165d68
--- /dev/null
+++ b/stand/libsa/read.c
@@ -0,0 +1,127 @@
+/* $NetBSD: read.c,v 1.8 1997/01/22 00:38:12 cgd Exp $ */
+
+/*-
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * The Mach Operating System project at Carnegie-Mellon University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)read.c 8.1 (Berkeley) 6/11/93
+ *
+ *
+ * Copyright (c) 1989, 1990, 1991 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Author: Alessandro Forin
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie the
+ * rights to redistribute these changes.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include "stand.h"
+
+ssize_t
+read(int fd, void *dest, size_t bcount)
+{
+ struct open_file *f = &files[fd];
+ size_t resid;
+
+ if ((unsigned)fd >= SOPEN_MAX || !(f->f_flags & F_READ)) {
+ errno = EBADF;
+ return (-1);
+ }
+ if (f->f_flags & F_RAW) {
+ twiddle(4);
+ errno = (f->f_dev->dv_strategy)(f->f_devdata, F_READ,
+ btodb(f->f_offset), bcount, dest, &resid);
+ if (errno)
+ return (-1);
+ f->f_offset += resid;
+ return (resid);
+ }
+
+ /*
+ * Optimise reads from regular files using a readahead buffer.
+ * If the request can't be satisfied from the current buffer contents,
+ * check to see if it should be bypassed, or refill the buffer and complete
+ * the request.
+ */
+ resid = bcount;
+ for (;;) {
+ size_t ccount, cresid;
+ /* how much can we supply? */
+ ccount = imin(f->f_ralen, resid);
+ if (ccount > 0) {
+ bcopy(f->f_rabuf + f->f_raoffset, dest, ccount);
+ f->f_raoffset += ccount;
+ f->f_ralen -= ccount;
+ resid -= ccount;
+ if (resid == 0)
+ return(bcount);
+ dest = (char *)dest + ccount;
+ }
+
+ /* will filling the readahead buffer again not help? */
+ if (resid >= SOPEN_RASIZE) {
+ /* bypass the rest of the request and leave the buffer empty */
+ if ((errno = (f->f_ops->fo_read)(f, dest, resid, &cresid)))
+ return (-1);
+ return(bcount - cresid);
+ }
+
+ /* fetch more data */
+ if ((errno = (f->f_ops->fo_read)(f, f->f_rabuf, SOPEN_RASIZE, &cresid)))
+ return (-1);
+ f->f_raoffset = 0;
+ f->f_ralen = SOPEN_RASIZE - cresid;
+ /* no more data, return what we had */
+ if (f->f_ralen == 0)
+ return(bcount - resid);
+ }
+}
diff --git a/stand/libsa/readdir.c b/stand/libsa/readdir.c
new file mode 100644
index 0000000..e49d93d
--- /dev/null
+++ b/stand/libsa/readdir.c
@@ -0,0 +1,51 @@
+/*-
+ * Copyright (c) 1999,2000 Jonathan Lemon <jlemon@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include "stand.h"
+
+struct dirent *
+readdirfd(int fd)
+{
+ static struct dirent dir; /* XXX not thread safe */
+ struct open_file *f = &files[fd];
+
+ if ((unsigned)fd >= SOPEN_MAX || !(f->f_flags & F_READ)) {
+ errno = EBADF;
+ return (NULL);
+ }
+ if (f->f_flags & F_RAW) {
+ errno = EIO;
+ return (NULL);
+ }
+ errno = (f->f_ops->fo_readdir)(f, &dir);
+ if (errno)
+ return (NULL);
+ return (&dir);
+}
diff --git a/stand/libsa/rpc.c b/stand/libsa/rpc.c
new file mode 100644
index 0000000..94e6ce6
--- /dev/null
+++ b/stand/libsa/rpc.c
@@ -0,0 +1,433 @@
+/* $NetBSD: rpc.c,v 1.18 1998/01/23 19:27:45 thorpej Exp $ */
+
+/*
+ * Copyright (c) 1992 Regents of the University of California.
+ * All rights reserved.
+ *
+ * This software was developed by the Computer Systems Engineering group
+ * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
+ * contributed to Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#) Header: rpc.c,v 1.12 93/09/28 08:31:56 leres Exp (LBL)
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * RPC functions used by NFS and bootparams.
+ * Note that bootparams requires the ability to find out the
+ * address of the server from which its response has come.
+ * This is supported by keeping the IP/UDP headers in the
+ * buffer space provided by the caller. (See rpc_fromaddr)
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+
+#include <string.h>
+
+#include "rpcv2.h"
+
+#include "stand.h"
+#include "net.h"
+#include "netif.h"
+#include "rpc.h"
+
+struct auth_info {
+ int32_t authtype; /* auth type */
+ u_int32_t authlen; /* auth length */
+};
+
+struct auth_unix {
+ int32_t ua_time;
+ int32_t ua_hostname; /* null */
+ int32_t ua_uid;
+ int32_t ua_gid;
+ int32_t ua_gidlist; /* null */
+};
+
+struct rpc_call {
+ u_int32_t rp_xid; /* request transaction id */
+ int32_t rp_direction; /* call direction (0) */
+ u_int32_t rp_rpcvers; /* rpc version (2) */
+ u_int32_t rp_prog; /* program */
+ u_int32_t rp_vers; /* version */
+ u_int32_t rp_proc; /* procedure */
+};
+
+struct rpc_reply {
+ u_int32_t rp_xid; /* request transaction id */
+ int32_t rp_direction; /* call direction (1) */
+ int32_t rp_astatus; /* accept status (0: accepted) */
+ union {
+ u_int32_t rpu_errno;
+ struct {
+ struct auth_info rok_auth;
+ u_int32_t rok_status;
+ } rpu_rok;
+ } rp_u;
+};
+
+/* Local forwards */
+static ssize_t recvrpc(struct iodesc *, void **, void **, time_t);
+static int rpc_getport(struct iodesc *, n_long, n_long);
+
+int rpc_xid;
+int rpc_port = 0x400; /* predecrement */
+
+/*
+ * Make a rpc call; return length of answer
+ * Note: Caller must leave room for headers.
+ */
+ssize_t
+rpc_call(struct iodesc *d, n_long prog, n_long vers, n_long proc,
+ void *sdata, size_t slen, void **rdata, void **pkt)
+{
+ ssize_t cc, rsize;
+ struct auth_info *auth;
+ struct rpc_call *call;
+ struct rpc_reply *reply;
+ char *send_head, *send_tail;
+ void *ptr;
+ n_long x;
+ int port; /* host order */
+
+#ifdef RPC_DEBUG
+ if (debug)
+ printf("rpc_call: prog=0x%x vers=%d proc=%d\n",
+ prog, vers, proc);
+#endif
+
+ port = rpc_getport(d, prog, vers);
+ if (port == -1)
+ return (-1);
+
+ d->destport = htons(port);
+
+ /*
+ * Prepend authorization stuff and headers.
+ * Note, must prepend things in reverse order.
+ */
+ send_head = sdata;
+ send_tail = (char *)sdata + slen;
+
+ /* Auth verifier is always auth_null */
+ send_head -= sizeof(*auth);
+ auth = (struct auth_info *)send_head;
+ auth->authtype = htonl(RPCAUTH_NULL);
+ auth->authlen = 0;
+
+ /* Auth credentials: always auth unix (as root) */
+ send_head -= sizeof(struct auth_unix);
+ bzero(send_head, sizeof(struct auth_unix));
+ send_head -= sizeof(*auth);
+ auth = (struct auth_info *)send_head;
+ auth->authtype = htonl(RPCAUTH_UNIX);
+ auth->authlen = htonl(sizeof(struct auth_unix));
+
+ /* RPC call structure. */
+ send_head -= sizeof(*call);
+ call = (struct rpc_call *)send_head;
+ rpc_xid++;
+ call->rp_xid = htonl(rpc_xid);
+ call->rp_direction = htonl(RPC_CALL);
+ call->rp_rpcvers = htonl(RPC_VER2);
+ call->rp_prog = htonl(prog);
+ call->rp_vers = htonl(vers);
+ call->rp_proc = htonl(proc);
+
+ ptr = NULL;
+ cc = sendrecv(d,
+ sendudp, send_head, send_tail - send_head,
+ recvrpc, &ptr, (void **)&reply);
+
+#ifdef RPC_DEBUG
+ if (debug)
+ printf("callrpc: cc=%zd\n", cc);
+#endif
+ if (cc == -1)
+ return (-1);
+
+ if (cc <= sizeof(*reply)) {
+ errno = EBADRPC;
+ free(ptr);
+ return (-1);
+ }
+
+ /*
+ * Check the RPC reply status.
+ * The xid, dir, astatus were already checked.
+ */
+ auth = &reply->rp_u.rpu_rok.rok_auth;
+ x = ntohl(auth->authlen);
+ if (x != 0) {
+#ifdef RPC_DEBUG
+ if (debug)
+ printf("callrpc: reply auth != NULL\n");
+#endif
+ errno = EBADRPC;
+ free(ptr);
+ return (-1);
+ }
+ x = ntohl(reply->rp_u.rpu_rok.rok_status);
+ if (x != 0) {
+ printf("callrpc: error = %ld\n", (long)x);
+ errno = EBADRPC;
+ free(ptr);
+ return (-1);
+ }
+
+ rsize = cc - sizeof(*reply);
+ *rdata = (void *)((uintptr_t)reply + sizeof(*reply));
+ *pkt = ptr;
+ return (rsize);
+}
+
+/*
+ * Returns true if packet is the one we're waiting for.
+ * This just checks the XID, direction, acceptance.
+ * Remaining checks are done by callrpc
+ */
+static ssize_t
+recvrpc(struct iodesc *d, void **pkt, void **payload, time_t tleft)
+{
+ void *ptr;
+ struct rpc_reply *reply;
+ ssize_t n;
+ int x;
+
+ errno = 0;
+#ifdef RPC_DEBUG
+ if (debug)
+ printf("recvrpc: called\n");
+#endif
+
+ ptr = NULL;
+ n = readudp(d, &ptr, (void **)&reply, tleft);
+ if (n <= (4 * 4)) {
+ free(ptr);
+ return (-1);
+ }
+
+ x = ntohl(reply->rp_xid);
+ if (x != rpc_xid) {
+#ifdef RPC_DEBUG
+ if (debug)
+ printf("recvrpc: rp_xid %d != xid %d\n", x, rpc_xid);
+#endif
+ free(ptr);
+ return (-1);
+ }
+
+ x = ntohl(reply->rp_direction);
+ if (x != RPC_REPLY) {
+#ifdef RPC_DEBUG
+ if (debug)
+ printf("recvrpc: rp_direction %d != REPLY\n", x);
+#endif
+ free(ptr);
+ return (-1);
+ }
+
+ x = ntohl(reply->rp_astatus);
+ if (x != RPC_MSGACCEPTED) {
+ errno = ntohl(reply->rp_u.rpu_errno);
+ printf("recvrpc: reject, astat=%d, errno=%d\n", x, errno);
+ free(ptr);
+ return (-1);
+ }
+
+ *pkt = ptr;
+ *payload = reply;
+ /* Return data count (thus indicating success) */
+ return (n);
+}
+
+/*
+ * Given a pointer to a reply just received,
+ * dig out the IP address/port from the headers.
+ */
+void
+rpc_fromaddr(void *pkt, struct in_addr *addr, u_short *port)
+{
+ struct hackhdr {
+ /* Tail of IP header: just IP addresses */
+ n_long ip_src;
+ n_long ip_dst;
+ /* UDP header: */
+ u_int16_t uh_sport; /* source port */
+ u_int16_t uh_dport; /* destination port */
+ int16_t uh_ulen; /* udp length */
+ u_int16_t uh_sum; /* udp checksum */
+ /* RPC reply header: */
+ struct rpc_reply rpc;
+ } *hhdr;
+
+ hhdr = ((struct hackhdr *)pkt) - 1;
+ addr->s_addr = hhdr->ip_src;
+ *port = hhdr->uh_sport;
+}
+
+/*
+ * RPC Portmapper cache
+ */
+#define PMAP_NUM 8 /* need at most 5 pmap entries */
+
+int rpc_pmap_num;
+struct pmap_list {
+ struct in_addr addr; /* server, net order */
+ u_int prog; /* host order */
+ u_int vers; /* host order */
+ int port; /* host order */
+} rpc_pmap_list[PMAP_NUM];
+
+/*
+ * return port number in host order, or -1.
+ * arguments are:
+ * addr .. server, net order.
+ * prog .. host order.
+ * vers .. host order.
+ */
+int
+rpc_pmap_getcache(struct in_addr addr, u_int prog, u_int vers)
+{
+ struct pmap_list *pl;
+
+ for (pl = rpc_pmap_list; pl < &rpc_pmap_list[rpc_pmap_num]; pl++) {
+ if (pl->addr.s_addr == addr.s_addr &&
+ pl->prog == prog && pl->vers == vers )
+ {
+ return (pl->port);
+ }
+ }
+ return (-1);
+}
+
+/*
+ * arguments are:
+ * addr .. server, net order.
+ * prog .. host order.
+ * vers .. host order.
+ * port .. host order.
+ */
+void
+rpc_pmap_putcache(struct in_addr addr, u_int prog, u_int vers, int port)
+{
+ struct pmap_list *pl;
+
+ /* Don't overflow cache... */
+ if (rpc_pmap_num >= PMAP_NUM) {
+ /* ... just re-use the last entry. */
+ rpc_pmap_num = PMAP_NUM - 1;
+#ifdef RPC_DEBUG
+ printf("rpc_pmap_putcache: cache overflow\n");
+#endif
+ }
+
+ pl = &rpc_pmap_list[rpc_pmap_num];
+ rpc_pmap_num++;
+
+ /* Cache answer */
+ pl->addr = addr;
+ pl->prog = prog;
+ pl->vers = vers;
+ pl->port = port;
+}
+
+
+/*
+ * Request a port number from the port mapper.
+ * Returns the port in host order.
+ * prog and vers are host order.
+ */
+int
+rpc_getport(struct iodesc *d, n_long prog, n_long vers)
+{
+ struct args {
+ n_long prog; /* call program */
+ n_long vers; /* call version */
+ n_long proto; /* call protocol */
+ n_long port; /* call port (unused) */
+ } *args;
+ struct res {
+ n_long port;
+ } *res;
+ struct {
+ n_long h[RPC_HEADER_WORDS];
+ struct args d;
+ } sdata;
+ void *pkt;
+ ssize_t cc;
+ int port;
+
+#ifdef RPC_DEBUG
+ if (debug)
+ printf("%s: prog=0x%x vers=%d\n", __func__, prog, vers);
+#endif
+
+ /* This one is fixed forever. */
+ if (prog == PMAPPROG) {
+ port = PMAPPORT;
+ goto out;
+ }
+
+ /* Try for cached answer first */
+ port = rpc_pmap_getcache(d->destip, prog, vers);
+ if (port != -1)
+ goto out;
+
+ args = &sdata.d;
+ args->prog = htonl(prog);
+ args->vers = htonl(vers);
+ args->proto = htonl(IPPROTO_UDP);
+ args->port = 0;
+ pkt = NULL;
+
+ cc = rpc_call(d, PMAPPROG, PMAPVERS, PMAPPROC_GETPORT,
+ args, sizeof(*args), (void **)&res, &pkt);
+ if (cc < sizeof(*res)) {
+ printf("getport: %s", strerror(errno));
+ errno = EBADRPC;
+ free(pkt);
+ return (-1);
+ }
+ port = (int)ntohl(res->port);
+ free(pkt);
+
+ rpc_pmap_putcache(d->destip, prog, vers, port);
+
+out:
+#ifdef RPC_DEBUG
+ if (debug)
+ printf("%s: port=%u\n", __func__, port);
+#endif
+ return (port);
+}
diff --git a/stand/libsa/rpc.h b/stand/libsa/rpc.h
new file mode 100644
index 0000000..6dda060
--- /dev/null
+++ b/stand/libsa/rpc.h
@@ -0,0 +1,66 @@
+/* $NetBSD: rpc.h,v 1.8 1996/09/26 23:22:03 cgd Exp $ */
+
+/*
+ * Copyright (c) 1992 Regents of the University of California.
+ * All rights reserved.
+ *
+ * This software was developed by the Computer Systems Engineering group
+ * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
+ * contributed to Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/* XXX defines we can't easily get from system includes */
+#define PMAPPORT 111
+#define PMAPPROG 100000
+#define PMAPVERS 2
+#define PMAPPROC_NULL 0
+#define PMAPPROC_SET 1
+#define PMAPPROC_UNSET 2
+#define PMAPPROC_GETPORT 3
+#define PMAPPROC_DUMP 4
+#define PMAPPROC_CALLIT 5
+
+/* RPC functions: */
+ssize_t rpc_call(struct iodesc *, n_long, n_long, n_long,
+ void *, size_t, void **, void **);
+void rpc_fromaddr(void *, struct in_addr *, u_short *);
+int rpc_pmap_getcache(struct in_addr, u_int, u_int);
+void rpc_pmap_putcache(struct in_addr, u_int, u_int, int);
+
+extern int rpc_port; /* decrement before bind */
+
+/*
+ * How much space to leave in front of RPC requests.
+ * In 32-bit words (alignment) we have:
+ * 12: Ether + IP + UDP + padding
+ * 6: RPC call header
+ * 7: Auth UNIX
+ * 2: Auth NULL
+ */
+#define RPC_HEADER_WORDS 28
diff --git a/stand/libsa/rpcv2.h b/stand/libsa/rpcv2.h
new file mode 100644
index 0000000..b59a760
--- /dev/null
+++ b/stand/libsa/rpcv2.h
@@ -0,0 +1,87 @@
+/* $NetBSD: rpcv2.h,v 1.1 1996/02/26 23:05:32 gwr Exp $ */
+
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Rick Macklem at The University of Guelph.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)rpcv2.h 8.1 (Berkeley) 6/10/93
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Definitions for Sun RPC Version 2, from
+ * "RPC: Remote Procedure Call Protocol Specification" RFC1057
+ */
+
+/* Version # */
+#define RPC_VER2 2
+
+/* Authentication */
+#define RPCAUTH_NULL 0
+#define RPCAUTH_UNIX 1
+#define RPCAUTH_SHORT 2
+#define RPCAUTH_MAXSIZ 400
+#define RPCAUTH_UNIXGIDS 16
+
+/* Rpc Constants */
+#define RPC_CALL 0
+#define RPC_REPLY 1
+#define RPC_MSGACCEPTED 0
+#define RPC_MSGDENIED 1
+#define RPC_PROGUNAVAIL 1
+#define RPC_PROGMISMATCH 2
+#define RPC_PROCUNAVAIL 3
+#define RPC_GARBAGE 4 /* I like this one */
+#define RPC_MISMATCH 0
+#define RPC_AUTHERR 1
+
+/* Authentication failures */
+#define AUTH_BADCRED 1
+#define AUTH_REJECTCRED 2
+#define AUTH_BADVERF 3
+#define AUTH_REJECTVERF 4
+#define AUTH_TOOWEAK 5 /* Give em wheaties */
+
+/* Sizes of rpc header parts */
+#define RPC_SIZ 24
+#define RPC_REPLYSIZ 28
+
+/* RPC Prog definitions */
+#define RPCPROG_MNT 100005
+#define RPCMNT_VER1 1
+#define RPCMNT_MOUNT 1
+#define RPCMNT_DUMP 2
+#define RPCMNT_UMOUNT 3
+#define RPCMNT_UMNTALL 4
+#define RPCMNT_EXPORT 5
+#define RPCMNT_NAMELEN 255
+#define RPCMNT_PATHLEN 1024
+#define RPCPROG_NFS 100003
diff --git a/stand/libsa/saioctl.h b/stand/libsa/saioctl.h
new file mode 100644
index 0000000..5f888ff
--- /dev/null
+++ b/stand/libsa/saioctl.h
@@ -0,0 +1,50 @@
+/* $NetBSD: saioctl.h,v 1.2 1994/10/26 05:45:04 cgd Exp $ */
+
+/*-
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)saioctl.h 8.1 (Berkeley) 6/11/93
+ *
+ * $FreeBSD$
+ */
+
+/* ioctl's -- for disks just now */
+#define SAIOHDR (('d'<<8)|1) /* next i/o includes header */
+#define SAIOCHECK (('d'<<8)|2) /* next i/o checks data */
+#define SAIOHCHECK (('d'<<8)|3) /* next i/o checks header & data */
+#define SAIONOBAD (('d'<<8)|4) /* inhibit bad sector forwarding */
+#define SAIODOBAD (('d'<<8)|5) /* enable bad sector forwarding */
+#define SAIOECCLIM (('d'<<8)|6) /* set limit to ecc correction, bits */
+#define SAIOECCUNL (('d'<<8)|7) /* use standard ecc procedures */
+#define SAIORETRIES (('d'<<8)|8) /* set retry count for unit */
+#define SAIODEVDATA (('d'<<8)|9) /* get pointer to pack label */
+#define SAIOSSI (('d'<<8)|10) /* set skip sector inhibit */
+#define SAIONOSSI (('d'<<8)|11) /* inhibit skip sector handling */
+#define SAIOSSDEV (('d'<<8)|12) /* is device skip sector type? */
+#define SAIODEBUG (('d'<<8)|13) /* enable/disable debugging */
+#define SAIOGBADINFO (('d'<<8)|14) /* get bad-sector table */
diff --git a/stand/libsa/sbrk.c b/stand/libsa/sbrk.c
new file mode 100644
index 0000000..471e78e
--- /dev/null
+++ b/stand/libsa/sbrk.c
@@ -0,0 +1,64 @@
+/*-
+ * Copyright (c) 1998 Michael Smith
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Minimal sbrk() emulation required for malloc support.
+ */
+
+#include <string.h>
+#include "stand.h"
+#include "zalloc_defs.h"
+
+static size_t maxheap, heapsize = 0;
+static void *heapbase;
+
+void
+setheap(void *base, void *top)
+{
+ /* Align start address for the malloc code. Sigh. */
+ heapbase = (void *)(((uintptr_t)base + MALLOCALIGN_MASK) &
+ ~MALLOCALIGN_MASK);
+ maxheap = (char *)top - (char *)heapbase;
+}
+
+char *
+sbrk(int incr)
+{
+ char *ret;
+
+ if ((heapsize + incr) <= maxheap) {
+ ret = (char *)heapbase + heapsize;
+ bzero(ret, incr);
+ heapsize += incr;
+ return(ret);
+ }
+ errno = ENOMEM;
+ return((char *)-1);
+}
+
diff --git a/stand/libsa/sparc64/_setjmp.S b/stand/libsa/sparc64/_setjmp.S
new file mode 100644
index 0000000..50c924e
--- /dev/null
+++ b/stand/libsa/sparc64/_setjmp.S
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This software was developed by the Computer Systems Engineering group
+ * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
+ * contributed to Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Header: _setjmp.s,v 1.1 91/07/06 16:45:53 torek Exp
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+#if 0
+ .asciz "@(#)_setjmp.s 8.1 (Berkeley) 6/4/93"
+#else
+ RCSID("$NetBSD: _setjmp.S,v 1.4 1998/10/08 02:27:59 eeh Exp $")
+#endif
+#endif /* LIBC_SCCS and not lint */
+
+#include <machine/asm.h>
+__FBSDID("$FreeBSD$");
+
+#define _JB_FP 0x0
+#define _JB_PC 0x8
+#define _JB_SP 0x10
+
+ .register %g2,#ignore
+ .register %g3,#ignore
+
+/*
+ * C library -- setjmp, longjmp
+ *
+ * longjmp(a,v)
+ * will generate a "return(v?v:1)" from
+ * the last call to
+ * setjmp(a)
+ * by restoring the previous context.
+ */
+
+ENTRY(_setjmp)
+ stx %sp, [%o0 + _JB_SP]
+ stx %o7, [%o0 + _JB_PC]
+ stx %fp, [%o0 + _JB_FP]
+ retl
+ clr %o0
+END(_setjmp)
+
+ENTRY(_longjmp)
+ mov 1, %g1
+ movrnz %o1, %o1, %g1
+ mov %o0, %g2
+ ldx [%g2 + _JB_FP], %g3
+1: cmp %fp, %g3
+ bl,a 1b
+ restore
+ be,a 2f
+ ldx [%g2 + _JB_SP], %o0
+
+.Lbotch:
+ illtrap
+
+2: cmp %o0, %sp
+ bge,a 3f
+ mov %o0, %sp
+ b,a .Lbotch
+ nop
+3: ldx [%g2 + _JB_PC], %o7
+ retl
+ mov %g1, %o0
+END(_longjmp)
diff --git a/stand/libsa/splitfs.c b/stand/libsa/splitfs.c
new file mode 100644
index 0000000..af28704
--- /dev/null
+++ b/stand/libsa/splitfs.c
@@ -0,0 +1,313 @@
+/*
+ * Copyright (c) 2002 Maxim Sobolev
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "stand.h"
+
+#define NTRIES (3)
+#define CONF_BUF (512)
+#define SEEK_BUF (512)
+
+struct split_file
+{
+ char **filesv; /* Filenames */
+ char **descsv; /* Descriptions */
+ int filesc; /* Number of parts */
+ int curfile; /* Current file number */
+ int curfd; /* Current file descriptor */
+ off_t tot_pos; /* Offset from the beginning of the sequence */
+ off_t file_pos; /* Offset from the beginning of the slice */
+};
+
+static int split_openfile(struct split_file *sf);
+static int splitfs_open(const char *path, struct open_file *f);
+static int splitfs_close(struct open_file *f);
+static int splitfs_read(struct open_file *f, void *buf, size_t size, size_t *resid);
+static off_t splitfs_seek(struct open_file *f, off_t offset, int where);
+static int splitfs_stat(struct open_file *f, struct stat *sb);
+
+struct fs_ops splitfs_fsops = {
+ "split",
+ splitfs_open,
+ splitfs_close,
+ splitfs_read,
+ null_write,
+ splitfs_seek,
+ splitfs_stat,
+ null_readdir
+};
+
+static void
+split_file_destroy(struct split_file *sf)
+{
+ int i;
+
+ if (sf->filesc > 0) {
+ for (i = 0; i < sf->filesc; i++) {
+ free(sf->filesv[i]);
+ free(sf->descsv[i]);
+ }
+ free(sf->filesv);
+ free(sf->descsv);
+ }
+ free(sf);
+}
+
+static int
+split_openfile(struct split_file *sf)
+{
+ int i;
+
+ for (i = 0;; i++) {
+ sf->curfd = open(sf->filesv[sf->curfile], O_RDONLY);
+ if (sf->curfd >= 0)
+ break;
+ if ((sf->curfd == -1) && (errno != ENOENT))
+ return (errno);
+ if (i == NTRIES)
+ return (EIO);
+ printf("\nInsert disk labelled %s and press any key...",
+ sf->descsv[sf->curfile]);
+ getchar();
+ putchar('\n');
+ }
+ sf->file_pos = 0;
+ return (0);
+}
+
+static int
+splitfs_open(const char *fname, struct open_file *f)
+{
+ char *buf, *confname, *cp;
+ int conffd;
+ struct split_file *sf;
+ struct stat sb;
+
+ /* Have to be in "just read it" mode */
+ if (f->f_flags != F_READ)
+ return(EPERM);
+
+ /* If the name already ends in `.split', ignore it */
+ if ((cp = strrchr(fname, '.')) && (!strcmp(cp, ".split")))
+ return(ENOENT);
+
+ /* Construct new name */
+ confname = malloc(strlen(fname) + 7);
+ sprintf(confname, "%s.split", fname);
+
+ /* Try to open the configuration file */
+ conffd = open(confname, O_RDONLY);
+ free(confname);
+ if (conffd == -1)
+ return(ENOENT);
+
+ if (fstat(conffd, &sb) < 0) {
+ printf("splitfs_open: stat failed\n");
+ close(conffd);
+ return(ENOENT);
+ }
+ if (!S_ISREG(sb.st_mode)) {
+ printf("splitfs_open: not a file\n");
+ close(conffd);
+ return(EISDIR); /* best guess */
+ }
+
+ /* Allocate a split_file structure, populate it from the config file */
+ sf = malloc(sizeof(struct split_file));
+ bzero(sf, sizeof(struct split_file));
+ buf = malloc(CONF_BUF);
+ while (fgetstr(buf, CONF_BUF, conffd) > 0) {
+ cp = buf;
+ while ((*cp != '\0') && (isspace(*cp) == 0))
+ cp++;
+ if (*cp != '\0') {
+ *cp = '\0';
+ cp++;
+ }
+ while ((*cp != '\0') && (isspace(*cp) != 0))
+ cp++;
+ if (*cp == '\0')
+ cp = buf;
+ sf->filesc++;
+ sf->filesv = realloc(sf->filesv, sizeof(*(sf->filesv)) * sf->filesc);
+ sf->descsv = realloc(sf->descsv, sizeof(*(sf->descsv)) * sf->filesc);
+ sf->filesv[sf->filesc - 1] = strdup(buf);
+ sf->descsv[sf->filesc - 1] = strdup(cp);
+ }
+ free(buf);
+ close(conffd);
+
+ if (sf->filesc == 0) {
+ split_file_destroy(sf);
+ return(ENOENT);
+ }
+ errno = split_openfile(sf);
+ if (errno != 0) {
+ split_file_destroy(sf);
+ return(ENOENT);
+ }
+
+ /* Looks OK, we'll take it */
+ f->f_fsdata = sf;
+ return (0);
+}
+
+static int
+splitfs_close(struct open_file *f)
+{
+ int fd;
+ struct split_file *sf;
+
+ sf = (struct split_file *)f->f_fsdata;
+ fd = sf->curfd;
+ split_file_destroy(sf);
+ return(close(fd));
+}
+
+static int
+splitfs_read(struct open_file *f, void *buf, size_t size, size_t *resid)
+{
+ ssize_t nread;
+ size_t totread;
+ struct split_file *sf;
+
+ sf = (struct split_file *)f->f_fsdata;
+ totread = 0;
+ do {
+ nread = read(sf->curfd, buf, size - totread);
+
+ /* Error? */
+ if (nread == -1)
+ return (errno);
+
+ sf->tot_pos += nread;
+ sf->file_pos += nread;
+ totread += nread;
+ buf = (char *)buf + nread;
+
+ if (totread < size) { /* EOF */
+ if (sf->curfile == (sf->filesc - 1)) /* Last slice */
+ break;
+
+ /* Close previous slice */
+ if (close(sf->curfd) != 0)
+ return (errno);
+
+ sf->curfile++;
+ errno = split_openfile(sf);
+ if (errno)
+ return (errno);
+ }
+ } while (totread < size);
+
+ if (resid != NULL)
+ *resid = size - totread;
+
+ return (0);
+}
+
+static off_t
+splitfs_seek(struct open_file *f, off_t offset, int where)
+{
+ int nread;
+ size_t resid;
+ off_t new_pos, seek_by;
+ struct split_file *sf;
+
+ sf = (struct split_file *)f->f_fsdata;
+
+ seek_by = offset;
+ switch (where) {
+ case SEEK_SET:
+ seek_by -= sf->tot_pos;
+ break;
+ case SEEK_CUR:
+ break;
+ case SEEK_END:
+ panic("splitfs_seek: SEEK_END not supported");
+ break;
+ default:
+ errno = EINVAL;
+ return (-1);
+ }
+
+ if (seek_by > 0) {
+ /*
+ * Seek forward - implemented using splitfs_read(), because otherwise we'll be
+ * unable to detect that we have crossed slice boundary and hence
+ * unable to do a long seek crossing that boundary.
+ */
+ void *tmp;
+
+ tmp = malloc(SEEK_BUF);
+ if (tmp == NULL) {
+ errno = ENOMEM;
+ return (-1);
+ }
+
+ nread = 0;
+ for (; seek_by > 0; seek_by -= nread) {
+ resid = 0;
+ errno = splitfs_read(f, tmp, min(seek_by, SEEK_BUF), &resid);
+ nread = min(seek_by, SEEK_BUF) - resid;
+ if ((errno != 0) || (nread == 0))
+ /* Error or EOF */
+ break;
+ }
+ free(tmp);
+ if (errno != 0)
+ return (-1);
+ }
+
+ if (seek_by != 0) {
+ /* Seek backward or seek past the boundary of the last slice */
+ if (sf->file_pos + seek_by < 0)
+ panic("splitfs_seek: can't seek past the beginning of the slice");
+ new_pos = lseek(sf->curfd, seek_by, SEEK_CUR);
+ if (new_pos < 0) {
+ errno = EINVAL;
+ return (-1);
+ }
+ sf->tot_pos += new_pos - sf->file_pos;
+ sf->file_pos = new_pos;
+ }
+
+ return (sf->tot_pos);
+}
+
+static int
+splitfs_stat(struct open_file *f, struct stat *sb)
+{
+ int result;
+ struct split_file *sf = (struct split_file *)f->f_fsdata;
+
+ /* stat as normal, but indicate that size is unknown */
+ if ((result = fstat(sf->curfd, sb)) == 0)
+ sb->st_size = -1;
+ return (result);
+}
diff --git a/stand/libsa/stand.h b/stand/libsa/stand.h
new file mode 100644
index 0000000..deb6ad8
--- /dev/null
+++ b/stand/libsa/stand.h
@@ -0,0 +1,426 @@
+/*
+ * Copyright (c) 1998 Michael Smith.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ * From $NetBSD: stand.h,v 1.22 1997/06/26 19:17:40 drochner Exp $
+ */
+
+/*-
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)stand.h 8.1 (Berkeley) 6/11/93
+ */
+
+#ifndef STAND_H
+#define STAND_H
+
+#include <sys/types.h>
+#include <sys/cdefs.h>
+#include <sys/stat.h>
+#include <sys/dirent.h>
+
+/* this header intentionally exports NULL from <string.h> */
+#include <string.h>
+
+#define CHK(fmt, args...) printf("%s(%d): " fmt "\n", __func__, __LINE__ , ##args)
+#define PCHK(fmt, args...) {printf("%s(%d): " fmt "\n", __func__, __LINE__ , ##args); getchar();}
+
+/* Avoid unwanted userlandish components */
+#define _KERNEL
+#include <sys/errno.h>
+#undef _KERNEL
+
+/* special stand error codes */
+#define EADAPT (ELAST+1) /* bad adaptor */
+#define ECTLR (ELAST+2) /* bad controller */
+#define EUNIT (ELAST+3) /* bad unit */
+#define ESLICE (ELAST+4) /* bad slice */
+#define EPART (ELAST+5) /* bad partition */
+#define ERDLAB (ELAST+6) /* can't read disk label */
+#define EUNLAB (ELAST+7) /* unlabeled disk */
+#define EOFFSET (ELAST+8) /* relative seek not supported */
+#define ESALAST (ELAST+8) /* */
+
+struct open_file;
+
+/*
+ * This structure is used to define file system operations in a file system
+ * independent way.
+ *
+ * XXX note that filesystem providers should export a pointer to their fs_ops
+ * struct, so that consumers can reference this and thus include the
+ * filesystems that they require.
+ */
+struct fs_ops {
+ const char *fs_name;
+ int (*fo_open)(const char *path, struct open_file *f);
+ int (*fo_close)(struct open_file *f);
+ int (*fo_read)(struct open_file *f, void *buf,
+ size_t size, size_t *resid);
+ int (*fo_write)(struct open_file *f, void *buf,
+ size_t size, size_t *resid);
+ off_t (*fo_seek)(struct open_file *f, off_t offset, int where);
+ int (*fo_stat)(struct open_file *f, struct stat *sb);
+ int (*fo_readdir)(struct open_file *f, struct dirent *d);
+};
+
+/*
+ * libstand-supplied filesystems
+ */
+extern struct fs_ops ufs_fsops;
+extern struct fs_ops tftp_fsops;
+extern struct fs_ops nfs_fsops;
+extern struct fs_ops cd9660_fsops;
+extern struct fs_ops nandfs_fsops;
+extern struct fs_ops gzipfs_fsops;
+extern struct fs_ops bzipfs_fsops;
+extern struct fs_ops dosfs_fsops;
+extern struct fs_ops ext2fs_fsops;
+extern struct fs_ops splitfs_fsops;
+extern struct fs_ops pkgfs_fsops;
+
+/* where values for lseek(2) */
+#define SEEK_SET 0 /* set file offset to offset */
+#define SEEK_CUR 1 /* set file offset to current plus offset */
+#define SEEK_END 2 /* set file offset to EOF plus offset */
+
+/*
+ * Device switch
+ */
+struct devsw {
+ const char dv_name[8];
+ int dv_type; /* opaque type constant, arch-dependant */
+ int (*dv_init)(void); /* early probe call */
+ int (*dv_strategy)(void *devdata, int rw, daddr_t blk,
+ size_t size, char *buf, size_t *rsize);
+ int (*dv_open)(struct open_file *f, ...);
+ int (*dv_close)(struct open_file *f);
+ int (*dv_ioctl)(struct open_file *f, u_long cmd, void *data);
+ int (*dv_print)(int verbose); /* print device information */
+ void (*dv_cleanup)(void);
+};
+
+/*
+ * libstand-supplied device switch
+ */
+extern struct devsw netdev;
+
+extern int errno;
+
+/*
+ * Generic device specifier; architecture-dependent
+ * versions may be larger, but should be allowed to
+ * overlap.
+ */
+struct devdesc
+{
+ struct devsw *d_dev;
+ int d_type;
+#define DEVT_NONE 0
+#define DEVT_DISK 1
+#define DEVT_NET 2
+#define DEVT_CD 3
+#define DEVT_ZFS 4
+#define DEVT_FD 5
+ int d_unit;
+ void *d_opendata;
+};
+
+struct open_file {
+ int f_flags; /* see F_* below */
+ struct devsw *f_dev; /* pointer to device operations */
+ void *f_devdata; /* device specific data */
+ struct fs_ops *f_ops; /* pointer to file system operations */
+ void *f_fsdata; /* file system specific data */
+ off_t f_offset; /* current file offset */
+ char *f_rabuf; /* readahead buffer pointer */
+ size_t f_ralen; /* valid data in readahead buffer */
+ off_t f_raoffset; /* consumer offset in readahead buffer */
+#define SOPEN_RASIZE 512
+};
+
+#define SOPEN_MAX 64
+extern struct open_file files[];
+
+/* f_flags values */
+#define F_READ 0x0001 /* file opened for reading */
+#define F_WRITE 0x0002 /* file opened for writing */
+#define F_RAW 0x0004 /* raw device open - no file system */
+#define F_NODEV 0x0008 /* network open - no device */
+#define F_MASK 0xFFFF
+/* Mode modifier for strategy() */
+#define F_NORA (0x01 << 16) /* Disable Read-Ahead */
+
+#define isascii(c) (((c) & ~0x7F) == 0)
+
+static __inline int isupper(int c)
+{
+ return c >= 'A' && c <= 'Z';
+}
+
+static __inline int islower(int c)
+{
+ return c >= 'a' && c <= 'z';
+}
+
+static __inline int isspace(int c)
+{
+ return c == ' ' || (c >= 0x9 && c <= 0xd);
+}
+
+static __inline int isdigit(int c)
+{
+ return c >= '0' && c <= '9';
+}
+
+static __inline int isxdigit(int c)
+{
+ return isdigit(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F');
+}
+
+static __inline int isalpha(int c)
+{
+ return isupper(c) || islower(c);
+}
+
+static __inline int isalnum(int c)
+{
+ return isalpha(c) || isdigit(c);
+}
+
+static __inline int toupper(int c)
+{
+ return islower(c) ? c - 'a' + 'A' : c;
+}
+
+static __inline int tolower(int c)
+{
+ return isupper(c) ? c - 'A' + 'a' : c;
+}
+
+/* sbrk emulation */
+extern void setheap(void *base, void *top);
+extern char *sbrk(int incr);
+
+/* Matt Dillon's zalloc/zmalloc */
+extern void *malloc(size_t bytes);
+extern void free(void *ptr);
+/*#define free(p) {CHK("free %p", p); free(p);} */ /* use for catching guard violations */
+extern void *calloc(size_t n1, size_t n2);
+extern void *realloc(void *ptr, size_t size);
+extern void *reallocf(void *ptr, size_t size);
+extern void mallocstats(void);
+
+extern int printf(const char *fmt, ...) __printflike(1, 2);
+extern void vprintf(const char *fmt, __va_list);
+extern int sprintf(char *buf, const char *cfmt, ...) __printflike(2, 3);
+extern int snprintf(char *buf, size_t size, const char *cfmt, ...) __printflike(3, 4);
+extern void vsprintf(char *buf, const char *cfmt, __va_list);
+
+extern void twiddle(u_int callerdiv);
+extern void twiddle_divisor(u_int globaldiv);
+
+extern void ngets(char *, int);
+#define gets(x) ngets((x), 0)
+extern int fgetstr(char *buf, int size, int fd);
+
+extern int open(const char *, int);
+#define O_RDONLY 0x0
+#define O_WRONLY 0x1
+#define O_RDWR 0x2
+extern int close(int);
+extern void closeall(void);
+extern ssize_t read(int, void *, size_t);
+extern ssize_t write(int, void *, size_t);
+extern struct dirent *readdirfd(int);
+
+extern void srandom(u_long seed);
+extern long random(void);
+
+/* imports from stdlib, locally modified */
+extern long strtol(const char *, char **, int);
+extern unsigned long strtoul(const char *, char **, int);
+extern char *optarg; /* getopt(3) external variables */
+extern int optind, opterr, optopt, optreset;
+extern int getopt(int, char * const [], const char *);
+
+/* pager.c */
+extern void pager_open(void);
+extern void pager_close(void);
+extern int pager_output(const char *lines);
+extern int pager_file(const char *fname);
+
+/* No signal state to preserve */
+#define setjmp _setjmp
+#define longjmp _longjmp
+
+/* environment.c */
+#define EV_DYNAMIC (1<<0) /* value was dynamically allocated, free if changed/unset */
+#define EV_VOLATILE (1<<1) /* value is volatile, make a copy of it */
+#define EV_NOHOOK (1<<2) /* don't call hook when setting */
+
+struct env_var;
+typedef char *(ev_format_t)(struct env_var *ev);
+typedef int (ev_sethook_t)(struct env_var *ev, int flags,
+ const void *value);
+typedef int (ev_unsethook_t)(struct env_var *ev);
+
+struct env_var
+{
+ char *ev_name;
+ int ev_flags;
+ void *ev_value;
+ ev_sethook_t *ev_sethook;
+ ev_unsethook_t *ev_unsethook;
+ struct env_var *ev_next, *ev_prev;
+};
+extern struct env_var *environ;
+
+extern struct env_var *env_getenv(const char *name);
+extern int env_setenv(const char *name, int flags,
+ const void *value, ev_sethook_t sethook,
+ ev_unsethook_t unsethook);
+extern char *getenv(const char *name);
+extern int setenv(const char *name, const char *value,
+ int overwrite);
+extern int putenv(char *string);
+extern int unsetenv(const char *name);
+
+extern ev_sethook_t env_noset; /* refuse set operation */
+extern ev_unsethook_t env_nounset; /* refuse unset operation */
+
+/* BCD conversions (undocumented) */
+extern u_char const bcd2bin_data[];
+extern u_char const bin2bcd_data[];
+extern char const hex2ascii_data[];
+
+#define bcd2bin(bcd) (bcd2bin_data[bcd])
+#define bin2bcd(bin) (bin2bcd_data[bin])
+#define hex2ascii(hex) (hex2ascii_data[hex])
+
+/* min/max (undocumented) */
+static __inline int imax(int a, int b) { return (a > b ? a : b); }
+static __inline int imin(int a, int b) { return (a < b ? a : b); }
+static __inline long lmax(long a, long b) { return (a > b ? a : b); }
+static __inline long lmin(long a, long b) { return (a < b ? a : b); }
+static __inline u_int max(u_int a, u_int b) { return (a > b ? a : b); }
+static __inline u_int min(u_int a, u_int b) { return (a < b ? a : b); }
+static __inline quad_t qmax(quad_t a, quad_t b) { return (a > b ? a : b); }
+static __inline quad_t qmin(quad_t a, quad_t b) { return (a < b ? a : b); }
+static __inline u_long ulmax(u_long a, u_long b) { return (a > b ? a : b); }
+static __inline u_long ulmin(u_long a, u_long b) { return (a < b ? a : b); }
+
+
+/* null functions for device/filesystem switches (undocumented) */
+extern int nodev(void);
+extern int noioctl(struct open_file *, u_long, void *);
+extern void nullsys(void);
+
+extern int null_open(const char *path, struct open_file *f);
+extern int null_close(struct open_file *f);
+extern int null_read(struct open_file *f, void *buf, size_t size, size_t *resid);
+extern int null_write(struct open_file *f, void *buf, size_t size, size_t *resid);
+extern off_t null_seek(struct open_file *f, off_t offset, int where);
+extern int null_stat(struct open_file *f, struct stat *sb);
+extern int null_readdir(struct open_file *f, struct dirent *d);
+
+
+/*
+ * Machine dependent functions and data, must be provided or stubbed by
+ * the consumer
+ */
+extern void exit(int);
+extern int getchar(void);
+extern int ischar(void);
+extern void putchar(int);
+extern int devopen(struct open_file *, const char *, const char **);
+extern int devclose(struct open_file *f);
+extern void panic(const char *, ...) __dead2 __printflike(1, 2);
+extern struct fs_ops *file_system[];
+extern struct fs_ops *exclusive_file_system;
+extern struct devsw *devsw[];
+
+/*
+ * Expose byteorder(3) functions.
+ */
+#ifndef _BYTEORDER_PROTOTYPED
+#define _BYTEORDER_PROTOTYPED
+extern uint32_t htonl(uint32_t);
+extern uint16_t htons(uint16_t);
+extern uint32_t ntohl(uint32_t);
+extern uint16_t ntohs(uint16_t);
+#endif
+
+#ifndef _BYTEORDER_FUNC_DEFINED
+#define _BYTEORDER_FUNC_DEFINED
+#define htonl(x) __htonl(x)
+#define htons(x) __htons(x)
+#define ntohl(x) __ntohl(x)
+#define ntohs(x) __ntohs(x)
+#endif
+
+void *Malloc(size_t, const char *, int);
+void *Calloc(size_t, size_t, const char *, int);
+void *Realloc(void *, size_t, const char *, int);
+void Free(void *, const char *, int);
+
+#if 1
+#define malloc(x) Malloc(x, __FILE__, __LINE__)
+#define calloc(x, y) Calloc(x, y, __FILE__, __LINE__)
+#define free(x) Free(x, __FILE__, __LINE__)
+#define realloc(x, y) Realloc(x, y, __FILE__, __LINE__)
+#else
+#define malloc(x) Malloc(x, NULL, 0)
+#define calloc(x, y) Calloc(x, y, NULL, 0)
+#define free(x) Free(x, NULL, 0)
+#define realloc(x, y) Realloc(x, y, NULL, 0)
+#endif
+
+#endif /* STAND_H */
diff --git a/stand/libsa/stat.c b/stand/libsa/stat.c
new file mode 100644
index 0000000..addee5d
--- /dev/null
+++ b/stand/libsa/stat.c
@@ -0,0 +1,52 @@
+/* $NetBSD: stat.c,v 1.4 1996/01/13 22:25:43 leo Exp $ */
+
+/*-
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)stat.c 8.1 (Berkeley) 6/11/93
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "stand.h"
+
+int
+stat(str, sb)
+ const char *str;
+ struct stat *sb;
+{
+ int fd, rv;
+
+ fd = open(str, O_RDONLY);
+ if (fd < 0)
+ return (-1);
+ rv = fstat(fd, sb);
+ (void)close(fd);
+ return (rv);
+}
diff --git a/stand/libsa/strcasecmp.c b/stand/libsa/strcasecmp.c
new file mode 100644
index 0000000..7ef3f59
--- /dev/null
+++ b/stand/libsa/strcasecmp.c
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 1987, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/cdefs.h>
+#include <string.h>
+#include "stand.h"
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)strcasecmp.c 8.1 (Berkeley) 6/4/93";
+#endif /* LIBC_SCCS and not lint */
+
+int
+strcasecmp(s1, s2)
+ const char *s1, *s2;
+{
+ const u_char
+ *us1 = (const u_char *)s1,
+ *us2 = (const u_char *)s2;
+
+ while (tolower(*us1) == tolower(*us2++))
+ if (*us1++ == '\0')
+ return (0);
+ return (tolower(*us1) - tolower(*--us2));
+}
+
+int
+strncasecmp(s1, s2, n)
+ const char *s1, *s2;
+ size_t n;
+{
+ if (n != 0) {
+ const u_char
+ *us1 = (const u_char *)s1,
+ *us2 = (const u_char *)s2;
+
+ do {
+ if (tolower(*us1) != tolower(*us2++))
+ return (tolower(*us1) - tolower(*--us2));
+ if (*us1++ == '\0')
+ break;
+ } while (--n != 0);
+ }
+ return (0);
+}
diff --git a/stand/libsa/strdup.c b/stand/libsa/strdup.c
new file mode 100644
index 0000000..45a5414
--- /dev/null
+++ b/stand/libsa/strdup.c
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)strdup.c 8.1 (Berkeley) 6/4/93";
+#endif /* LIBC_SCCS and not lint */
+
+#include "stand.h"
+#include <stddef.h>
+#include <string.h>
+
+char *
+strdup(str)
+ const char *str;
+{
+ size_t len;
+ char *copy = NULL;
+
+ if (str != NULL) {
+ len = strlen(str) + 1;
+ if ((copy = malloc(len)) == NULL)
+ return (NULL);
+ memcpy(copy, str, len);
+ }
+ return (copy);
+}
diff --git a/stand/libsa/strerror.c b/stand/libsa/strerror.c
new file mode 100644
index 0000000..56571ef
--- /dev/null
+++ b/stand/libsa/strerror.c
@@ -0,0 +1,87 @@
+/* $NetBSD: strerror.c,v 1.12 1997/01/25 00:37:50 cgd Exp $ */
+
+/*-
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "stand.h"
+
+static struct
+{
+ int err;
+ char *msg;
+} errtab[] = {
+ {0, "no error"},
+ /* standard errors */
+ {EPERM, "operation not permitted"},
+ {ENOENT, "no such file or directory"},
+ {EIO, "input/output error"},
+ {ENXIO, "device not configured"},
+ {ENOEXEC, "exec format error"},
+ {EBADF, "bad file descriptor"},
+ {ENOMEM, "cannot allocate memory"},
+ {ENODEV, "operation not supported by device"},
+ {ENOTDIR, "not a directory"},
+ {EISDIR, "is a directory"},
+ {EINVAL, "invalid argument"},
+ {EMFILE, "too many open files"},
+ {EFBIG, "file too large"},
+ {EROFS, "read-only filesystem"},
+ {EOPNOTSUPP, "operation not supported"},
+ {ETIMEDOUT, "operation timed out"},
+ {ESTALE, "stale NFS file handle"},
+ {EBADRPC, "RPC struct is bad"},
+ {EFTYPE, "inappropriate file type or format"},
+
+ {EADAPT, "bad adaptor number"},
+ {ECTLR, "bad controller number"},
+ {EUNIT, "bad unit number"},
+ {ESLICE, "bad slice number"},
+ {EPART, "bad partition"},
+ {ERDLAB, "can't read disk label"},
+ {EUNLAB, "disk unlabelled"},
+ {EOFFSET, "illegal seek"},
+ {0, NULL}
+};
+
+
+char *
+strerror(int err)
+{
+ static char msg[32];
+ int i;
+
+ for (i = 0; errtab[i].msg != NULL; i++)
+ if (errtab[i].err == err)
+ return(errtab[i].msg);
+ sprintf(msg, "unknown error (%d)", err);
+ return(msg);
+}
diff --git a/stand/libsa/strtol.c b/stand/libsa/strtol.c
new file mode 100644
index 0000000..be82fb1
--- /dev/null
+++ b/stand/libsa/strtol.c
@@ -0,0 +1,132 @@
+/*-
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)strtol.c 8.1 (Berkeley) 6/4/93";
+#endif /* LIBC_SCCS and not lint */
+
+#include "stand.h"
+#include <limits.h>
+
+/*
+ * Convert a string to a long integer.
+ *
+ * Ignores `locale' stuff. Assumes that the upper and lower case
+ * alphabets and digits are each contiguous.
+ */
+long
+strtol(nptr, endptr, base)
+ const char *nptr;
+ char **endptr;
+ int base;
+{
+ const char *s;
+ unsigned long acc;
+ unsigned char c;
+ unsigned long cutoff;
+ int neg = 0, any, cutlim;
+
+ /* Be sensible about NULL strings */
+ if (nptr == NULL)
+ nptr = "";
+ s = nptr;
+
+ /*
+ * Skip white space and pick up leading +/- sign if any.
+ * If base is 0, allow 0x for hex and 0 for octal, else
+ * assume decimal; if base is already 16, allow 0x.
+ */
+ do {
+ c = *s++;
+ } while (isspace(c));
+ if (c == '-') {
+ neg = 1;
+ c = *s++;
+ } else if (c == '+')
+ c = *s++;
+ if ((base == 0 || base == 16) &&
+ c == '0' && (*s == 'x' || *s == 'X')) {
+ c = s[1];
+ s += 2;
+ base = 16;
+ }
+ if (base == 0)
+ base = c == '0' ? 8 : 10;
+
+ /*
+ * Compute the cutoff value between legal numbers and illegal
+ * numbers. That is the largest legal value, divided by the
+ * base. An input number that is greater than this value, if
+ * followed by a legal input character, is too big. One that
+ * is equal to this value may be valid or not; the limit
+ * between valid and invalid numbers is then based on the last
+ * digit. For instance, if the range for longs is
+ * [-2147483648..2147483647] and the input base is 10,
+ * cutoff will be set to 214748364 and cutlim to either
+ * 7 (neg==0) or 8 (neg==1), meaning that if we have accumulated
+ * a value > 214748364, or equal but the next digit is > 7 (or 8),
+ * the number is too big, and we will return a range error.
+ *
+ * Set any if any `digits' consumed; make it negative to indicate
+ * overflow.
+ */
+ cutoff = neg ? -(unsigned long)LONG_MIN : LONG_MAX;
+ cutlim = cutoff % (unsigned long)base;
+ cutoff /= (unsigned long)base;
+ for (acc = 0, any = 0;; c = *s++) {
+ if (!isascii(c))
+ break;
+ if (isdigit(c))
+ c -= '0';
+ else if (isalpha(c))
+ c -= isupper(c) ? 'A' - 10 : 'a' - 10;
+ else
+ break;
+ if (c >= base)
+ break;
+ if (any < 0 || acc > cutoff || (acc == cutoff && c > cutlim))
+ any = -1;
+ else {
+ any = 1;
+ acc *= base;
+ acc += c;
+ }
+ }
+ if (any < 0) {
+ acc = neg ? LONG_MIN : LONG_MAX;
+ errno = ERANGE;
+ } else if (neg)
+ acc = -acc;
+ if (endptr != NULL)
+ *endptr = (char *)(any ? s - 1 : nptr);
+ return (acc);
+}
diff --git a/stand/libsa/strtoul.c b/stand/libsa/strtoul.c
new file mode 100644
index 0000000..5735d20
--- /dev/null
+++ b/stand/libsa/strtoul.c
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Copyright (c) 2011 The FreeBSD Foundation
+ * All rights reserved.
+ * Portions of this software were developed by David Chisnall
+ * under sponsorship from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)strtoul.c 8.1 (Berkeley) 6/4/93";
+#endif /* LIBC_SCCS and not lint */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "stand.h"
+#include <limits.h>
+
+/*
+ * Convert a string to an unsigned long integer.
+ *
+ * Assumes that the upper and lower case
+ * alphabets and digits are each contiguous.
+ */
+unsigned long
+strtoul(const char * __restrict nptr, char ** __restrict endptr, int base)
+{
+ const char *s;
+ unsigned long acc;
+ char c;
+ unsigned long cutoff;
+ int neg, any, cutlim;
+
+ /*
+ * See strtol for comments as to the logic used.
+ */
+ s = nptr;
+ do {
+ c = *s++;
+ } while (isspace((unsigned char)c));
+ if (c == '-') {
+ neg = 1;
+ c = *s++;
+ } else {
+ neg = 0;
+ if (c == '+')
+ c = *s++;
+ }
+ if ((base == 0 || base == 16) &&
+ c == '0' && (*s == 'x' || *s == 'X') &&
+ ((s[1] >= '0' && s[1] <= '9') ||
+ (s[1] >= 'A' && s[1] <= 'F') ||
+ (s[1] >= 'a' && s[1] <= 'f'))) {
+ c = s[1];
+ s += 2;
+ base = 16;
+ }
+ if (base == 0)
+ base = c == '0' ? 8 : 10;
+ acc = any = 0;
+ if (base < 2 || base > 36)
+ goto noconv;
+
+ cutoff = ULONG_MAX / base;
+ cutlim = ULONG_MAX % base;
+ for ( ; ; c = *s++) {
+ if (c >= '0' && c <= '9')
+ c -= '0';
+ else if (c >= 'A' && c <= 'Z')
+ c -= 'A' - 10;
+ else if (c >= 'a' && c <= 'z')
+ c -= 'a' - 10;
+ else
+ break;
+ if (c >= base)
+ break;
+ if (any < 0 || acc > cutoff || (acc == cutoff && c > cutlim))
+ any = -1;
+ else {
+ any = 1;
+ acc *= base;
+ acc += c;
+ }
+ }
+ if (any < 0) {
+ acc = ULONG_MAX;
+ errno = ERANGE;
+ } else if (!any) {
+noconv:
+ errno = EINVAL;
+ } else if (neg)
+ acc = -acc;
+ if (endptr != NULL)
+ *endptr = (char *)(any ? s - 1 : nptr);
+ return (acc);
+}
diff --git a/stand/libsa/tftp.c b/stand/libsa/tftp.c
new file mode 100644
index 0000000..9bef464
--- /dev/null
+++ b/stand/libsa/tftp.c
@@ -0,0 +1,785 @@
+/* $NetBSD: tftp.c,v 1.4 1997/09/17 16:57:07 drochner Exp $ */
+
+/*
+ * Copyright (c) 1996
+ * Matthias Drochner. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed for the NetBSD Project
+ * by Matthias Drochner.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * 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/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Simple TFTP implementation for libsa.
+ * Assumes:
+ * - socket descriptor (int) at open_file->f_devdata
+ * - server host IP in global servip
+ * Restrictions:
+ * - read only
+ * - lseek only with SEEK_SET or SEEK_CUR
+ * - no big time differences between transfers (<tftp timeout)
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <netinet/in.h>
+#include <netinet/udp.h>
+#include <netinet/in_systm.h>
+#include <arpa/tftp.h>
+
+#include <string.h>
+
+#include "stand.h"
+#include "net.h"
+#include "netif.h"
+
+#include "tftp.h"
+
+struct tftp_handle;
+
+static int tftp_open(const char *path, struct open_file *f);
+static int tftp_close(struct open_file *f);
+static int tftp_parse_oack(struct tftp_handle *h, char *buf, size_t len);
+static int tftp_read(struct open_file *f, void *buf, size_t size, size_t *resid);
+static int tftp_write(struct open_file *f, void *buf, size_t size, size_t *resid);
+static off_t tftp_seek(struct open_file *f, off_t offset, int where);
+static int tftp_set_blksize(struct tftp_handle *h, const char *str);
+static int tftp_stat(struct open_file *f, struct stat *sb);
+static ssize_t sendrecv_tftp(struct tftp_handle *h,
+ ssize_t (*sproc)(struct iodesc *, void *, size_t),
+ void *sbuf, size_t ssize,
+ ssize_t (*rproc)(struct tftp_handle *h, void **, void **, time_t, unsigned short *),
+ void **, void **, unsigned short *rtype);
+
+struct fs_ops tftp_fsops = {
+ "tftp",
+ tftp_open,
+ tftp_close,
+ tftp_read,
+ tftp_write,
+ tftp_seek,
+ tftp_stat,
+ null_readdir
+};
+
+extern struct in_addr servip;
+
+static int tftpport = 2000;
+static int is_open = 0;
+
+/*
+ * The legacy TFTP_BLKSIZE value was SEGSIZE(512).
+ * TFTP_REQUESTED_BLKSIZE of 1428 is (Ethernet MTU, less the TFTP, UDP and
+ * IP header lengths).
+ */
+#define TFTP_REQUESTED_BLKSIZE 1428
+
+/*
+ * Choose a blksize big enough so we can test with Ethernet
+ * Jumbo frames in the future.
+ */
+#define TFTP_MAX_BLKSIZE 9008
+
+struct tftp_handle {
+ struct iodesc *iodesc;
+ int currblock; /* contents of lastdata */
+ int islastblock; /* flag */
+ int validsize;
+ int off;
+ char *path; /* saved for re-requests */
+ unsigned int tftp_blksize;
+ unsigned long tftp_tsize;
+ void *pkt;
+ struct tftphdr *tftp_hdr;
+};
+
+#define TFTP_MAX_ERRCODE EOPTNEG
+static const int tftperrors[TFTP_MAX_ERRCODE + 1] = {
+ 0, /* ??? */
+ ENOENT,
+ EPERM,
+ ENOSPC,
+ EINVAL, /* ??? */
+ EINVAL, /* ??? */
+ EEXIST,
+ EINVAL, /* ??? */
+ EINVAL, /* Option negotiation failed. */
+};
+
+static int tftp_getnextblock(struct tftp_handle *h);
+
+/* send error message back. */
+static void
+tftp_senderr(struct tftp_handle *h, u_short errcode, const char *msg)
+{
+ struct {
+ u_char header[HEADER_SIZE];
+ struct tftphdr t;
+ u_char space[63]; /* +1 from t */
+ } __packed __aligned(4) wbuf;
+ char *wtail;
+ int len;
+
+ len = strlen(msg);
+ if (len > sizeof(wbuf.space))
+ len = sizeof(wbuf.space);
+
+ wbuf.t.th_opcode = htons((u_short) ERROR);
+ wbuf.t.th_code = htons(errcode);
+
+ wtail = wbuf.t.th_msg;
+ bcopy(msg, wtail, len);
+ wtail[len] = '\0';
+ wtail += len + 1;
+
+ sendudp(h->iodesc, &wbuf.t, wtail - (char *) &wbuf.t);
+}
+
+static void
+tftp_sendack(struct tftp_handle *h)
+{
+ struct {
+ u_char header[HEADER_SIZE];
+ struct tftphdr t;
+ } __packed __aligned(4) wbuf;
+ char *wtail;
+
+ wbuf.t.th_opcode = htons((u_short) ACK);
+ wtail = (char *) &wbuf.t.th_block;
+ wbuf.t.th_block = htons((u_short) h->currblock);
+ wtail += 2;
+
+ sendudp(h->iodesc, &wbuf.t, wtail - (char *) &wbuf.t);
+}
+
+static ssize_t
+recvtftp(struct tftp_handle *h, void **pkt, void **payload, time_t tleft,
+ unsigned short *rtype)
+{
+ struct iodesc *d = h->iodesc;
+ struct tftphdr *t;
+ void *ptr = NULL;
+ ssize_t len;
+
+ errno = 0;
+
+ len = readudp(d, &ptr, (void **)&t, tleft);
+
+ if (len < 4) {
+ free(ptr);
+ return (-1);
+ }
+
+ *rtype = ntohs(t->th_opcode);
+ switch (ntohs(t->th_opcode)) {
+ case DATA: {
+ int got;
+
+ if (htons(t->th_block) != (u_short) d->xid) {
+ /*
+ * Expected block?
+ */
+ free(ptr);
+ return (-1);
+ }
+ if (d->xid == 1) {
+ /*
+ * First data packet from new port.
+ */
+ struct udphdr *uh;
+ uh = (struct udphdr *) t - 1;
+ d->destport = uh->uh_sport;
+ } /* else check uh_sport has not changed??? */
+ got = len - (t->th_data - (char *)t);
+ *pkt = ptr;
+ *payload = t;
+ return (got);
+ }
+ case ERROR:
+ if ((unsigned) ntohs(t->th_code) > TFTP_MAX_ERRCODE) {
+ printf("illegal tftp error %d\n", ntohs(t->th_code));
+ errno = EIO;
+ } else {
+#ifdef TFTP_DEBUG
+ printf("tftp-error %d\n", ntohs(t->th_code));
+#endif
+ errno = tftperrors[ntohs(t->th_code)];
+ }
+ free(ptr);
+ return (-1);
+ case OACK: {
+ struct udphdr *uh;
+ int tftp_oack_len;
+
+ /*
+ * Unexpected OACK. TFTP transfer already in progress.
+ * Drop the pkt.
+ */
+ if (d->xid != 1) {
+ free(ptr);
+ return (-1);
+ }
+
+ /*
+ * Remember which port this OACK came from, because we need
+ * to send the ACK or errors back to it.
+ */
+ uh = (struct udphdr *) t - 1;
+ d->destport = uh->uh_sport;
+
+ /* Parse options ACK-ed by the server. */
+ tftp_oack_len = len - sizeof(t->th_opcode);
+ if (tftp_parse_oack(h, t->th_u.tu_stuff, tftp_oack_len) != 0) {
+ tftp_senderr(h, EOPTNEG, "Malformed OACK");
+ errno = EIO;
+ free(ptr);
+ return (-1);
+ }
+ *pkt = ptr;
+ *payload = t;
+ return (0);
+ }
+ default:
+#ifdef TFTP_DEBUG
+ printf("tftp type %d not handled\n", ntohs(t->th_opcode));
+#endif
+ free(ptr);
+ return (-1);
+ }
+}
+
+/* send request, expect first block (or error) */
+static int
+tftp_makereq(struct tftp_handle *h)
+{
+ struct {
+ u_char header[HEADER_SIZE];
+ struct tftphdr t;
+ u_char space[FNAME_SIZE + 6];
+ } __packed __aligned(4) wbuf;
+ char *wtail;
+ int l;
+ ssize_t res;
+ void *pkt;
+ struct tftphdr *t;
+ char *tftp_blksize = NULL;
+ int blksize_l;
+ unsigned short rtype = 0;
+
+ /*
+ * Allow overriding default TFTP block size by setting
+ * a tftp.blksize environment variable.
+ */
+ if ((tftp_blksize = getenv("tftp.blksize")) != NULL) {
+ tftp_set_blksize(h, tftp_blksize);
+ }
+
+ wbuf.t.th_opcode = htons((u_short) RRQ);
+ wtail = wbuf.t.th_stuff;
+ l = strlen(h->path);
+#ifdef TFTP_PREPEND_PATH
+ if (l > FNAME_SIZE - (sizeof(TFTP_PREPEND_PATH) - 1))
+ return (ENAMETOOLONG);
+ bcopy(TFTP_PREPEND_PATH, wtail, sizeof(TFTP_PREPEND_PATH) - 1);
+ wtail += sizeof(TFTP_PREPEND_PATH) - 1;
+#else
+ if (l > FNAME_SIZE)
+ return (ENAMETOOLONG);
+#endif
+ bcopy(h->path, wtail, l + 1);
+ wtail += l + 1;
+ bcopy("octet", wtail, 6);
+ wtail += 6;
+ bcopy("blksize", wtail, 8);
+ wtail += 8;
+ blksize_l = sprintf(wtail, "%d", h->tftp_blksize);
+ wtail += blksize_l + 1;
+ bcopy("tsize", wtail, 6);
+ wtail += 6;
+ bcopy("0", wtail, 2);
+ wtail += 2;
+
+ /* h->iodesc->myport = htons(--tftpport); */
+ h->iodesc->myport = htons(tftpport + (getsecs() & 0x3ff));
+ h->iodesc->destport = htons(IPPORT_TFTP);
+ h->iodesc->xid = 1; /* expected block */
+
+ h->currblock = 0;
+ h->islastblock = 0;
+ h->validsize = 0;
+
+ pkt = NULL;
+ res = sendrecv_tftp(h, &sendudp, &wbuf.t, wtail - (char *) &wbuf.t,
+ &recvtftp, &pkt, (void **)&t, &rtype);
+ if (res == -1) {
+ free(pkt);
+ return (errno);
+ }
+
+ free(h->pkt);
+ h->pkt = pkt;
+ h->tftp_hdr = t;
+
+ if (rtype == OACK)
+ return (tftp_getnextblock(h));
+
+ /* Server ignored our blksize request, revert to TFTP default. */
+ h->tftp_blksize = SEGSIZE;
+
+ switch (rtype) {
+ case DATA: {
+ h->currblock = 1;
+ h->validsize = res;
+ h->islastblock = 0;
+ if (res < h->tftp_blksize) {
+ h->islastblock = 1; /* very short file */
+ tftp_sendack(h);
+ }
+ return (0);
+ }
+ case ERROR:
+ default:
+ return (errno);
+ }
+
+}
+
+/* ack block, expect next */
+static int
+tftp_getnextblock(struct tftp_handle *h)
+{
+ struct {
+ u_char header[HEADER_SIZE];
+ struct tftphdr t;
+ } __packed __aligned(4) wbuf;
+ char *wtail;
+ int res;
+ void *pkt;
+ struct tftphdr *t;
+ unsigned short rtype = 0;
+ wbuf.t.th_opcode = htons((u_short) ACK);
+ wtail = (char *) &wbuf.t.th_block;
+ wbuf.t.th_block = htons((u_short) h->currblock);
+ wtail += 2;
+
+ h->iodesc->xid = h->currblock + 1; /* expected block */
+
+ pkt = NULL;
+ res = sendrecv_tftp(h, &sendudp, &wbuf.t, wtail - (char *) &wbuf.t,
+ &recvtftp, &pkt, (void **)&t, &rtype);
+
+ if (res == -1) { /* 0 is OK! */
+ free(pkt);
+ return (errno);
+ }
+
+ free(h->pkt);
+ h->pkt = pkt;
+ h->tftp_hdr = t;
+ h->currblock++;
+ h->validsize = res;
+ if (res < h->tftp_blksize)
+ h->islastblock = 1; /* EOF */
+
+ if (h->islastblock == 1) {
+ /* Send an ACK for the last block */
+ wbuf.t.th_block = htons((u_short) h->currblock);
+ sendudp(h->iodesc, &wbuf.t, wtail - (char *)&wbuf.t);
+ }
+
+ return (0);
+}
+
+static int
+tftp_open(const char *path, struct open_file *f)
+{
+ struct tftp_handle *tftpfile;
+ struct iodesc *io;
+ int res;
+ size_t pathsize;
+ const char *extraslash;
+
+ if (netproto != NET_TFTP)
+ return (EINVAL);
+
+ if (f->f_dev->dv_type != DEVT_NET)
+ return (EINVAL);
+
+ if (is_open)
+ return (EBUSY);
+
+ tftpfile = (struct tftp_handle *) malloc(sizeof(*tftpfile));
+ if (!tftpfile)
+ return (ENOMEM);
+
+ memset(tftpfile, 0, sizeof(*tftpfile));
+ tftpfile->tftp_blksize = TFTP_REQUESTED_BLKSIZE;
+ tftpfile->iodesc = io = socktodesc(*(int *) (f->f_devdata));
+ if (io == NULL)
+ return (EINVAL);
+
+ io->destip = servip;
+ tftpfile->off = 0;
+ pathsize = (strlen(rootpath) + 1 + strlen(path) + 1) * sizeof(char);
+ tftpfile->path = malloc(pathsize);
+ if (tftpfile->path == NULL) {
+ free(tftpfile);
+ return(ENOMEM);
+ }
+ if (rootpath[strlen(rootpath) - 1] == '/' || path[0] == '/')
+ extraslash = "";
+ else
+ extraslash = "/";
+ res = snprintf(tftpfile->path, pathsize, "%s%s%s",
+ rootpath, extraslash, path);
+ if (res < 0 || res > pathsize) {
+ free(tftpfile->path);
+ free(tftpfile);
+ return(ENOMEM);
+ }
+
+ res = tftp_makereq(tftpfile);
+
+ if (res) {
+ free(tftpfile->path);
+ free(tftpfile->pkt);
+ free(tftpfile);
+ return (res);
+ }
+ f->f_fsdata = (void *) tftpfile;
+ is_open = 1;
+ return (0);
+}
+
+static int
+tftp_read(struct open_file *f, void *addr, size_t size,
+ size_t *resid /* out */)
+{
+ struct tftp_handle *tftpfile;
+ tftpfile = (struct tftp_handle *) f->f_fsdata;
+
+ while (size > 0) {
+ int needblock, count;
+
+ twiddle(32);
+
+ needblock = tftpfile->off / tftpfile->tftp_blksize + 1;
+
+ if (tftpfile->currblock > needblock) { /* seek backwards */
+ tftp_senderr(tftpfile, 0, "No error: read aborted");
+ tftp_makereq(tftpfile); /* no error check, it worked
+ * for open */
+ }
+
+ while (tftpfile->currblock < needblock) {
+ int res;
+
+ res = tftp_getnextblock(tftpfile);
+ if (res) { /* no answer */
+#ifdef TFTP_DEBUG
+ printf("tftp: read error\n");
+#endif
+ return (res);
+ }
+ if (tftpfile->islastblock)
+ break;
+ }
+
+ if (tftpfile->currblock == needblock) {
+ int offinblock, inbuffer;
+
+ offinblock = tftpfile->off % tftpfile->tftp_blksize;
+
+ inbuffer = tftpfile->validsize - offinblock;
+ if (inbuffer < 0) {
+#ifdef TFTP_DEBUG
+ printf("tftp: invalid offset %d\n",
+ tftpfile->off);
+#endif
+ return (EINVAL);
+ }
+ count = (size < inbuffer ? size : inbuffer);
+ bcopy(tftpfile->tftp_hdr->th_data + offinblock,
+ addr, count);
+
+ addr = (char *)addr + count;
+ tftpfile->off += count;
+ size -= count;
+
+ if ((tftpfile->islastblock) && (count == inbuffer))
+ break; /* EOF */
+ } else {
+#ifdef TFTP_DEBUG
+ printf("tftp: block %d not found\n", needblock);
+#endif
+ return (EINVAL);
+ }
+
+ }
+
+ if (resid)
+ *resid = size;
+ return (0);
+}
+
+static int
+tftp_close(struct open_file *f)
+{
+ struct tftp_handle *tftpfile;
+ tftpfile = (struct tftp_handle *) f->f_fsdata;
+
+ /* let it time out ... */
+
+ if (tftpfile) {
+ free(tftpfile->path);
+ free(tftpfile->pkt);
+ free(tftpfile);
+ }
+ is_open = 0;
+ return (0);
+}
+
+static int
+tftp_write(struct open_file *f __unused, void *start __unused, size_t size __unused,
+ size_t *resid __unused /* out */)
+{
+ return (EROFS);
+}
+
+static int
+tftp_stat(struct open_file *f, struct stat *sb)
+{
+ struct tftp_handle *tftpfile;
+ tftpfile = (struct tftp_handle *) f->f_fsdata;
+
+ sb->st_mode = 0444 | S_IFREG;
+ sb->st_nlink = 1;
+ sb->st_uid = 0;
+ sb->st_gid = 0;
+ sb->st_size = (off_t) tftpfile->tftp_tsize;
+ return (0);
+}
+
+static off_t
+tftp_seek(struct open_file *f, off_t offset, int where)
+{
+ struct tftp_handle *tftpfile;
+ tftpfile = (struct tftp_handle *) f->f_fsdata;
+
+ switch (where) {
+ case SEEK_SET:
+ tftpfile->off = offset;
+ break;
+ case SEEK_CUR:
+ tftpfile->off += offset;
+ break;
+ default:
+ errno = EOFFSET;
+ return (-1);
+ }
+ return (tftpfile->off);
+}
+
+static ssize_t
+sendrecv_tftp(struct tftp_handle *h,
+ ssize_t (*sproc)(struct iodesc *, void *, size_t),
+ void *sbuf, size_t ssize,
+ ssize_t (*rproc)(struct tftp_handle *, void **, void **, time_t,
+ unsigned short *),
+ void **pkt, void **payload, unsigned short *rtype)
+{
+ struct iodesc *d = h->iodesc;
+ ssize_t cc;
+ time_t t, t1, tleft;
+
+#ifdef TFTP_DEBUG
+ if (debug)
+ printf("sendrecv: called\n");
+#endif
+
+ tleft = MINTMO;
+ t = t1 = getsecs();
+ for (;;) {
+ if ((getsecs() - t) > MAXTMO) {
+ errno = ETIMEDOUT;
+ return -1;
+ }
+
+ cc = (*sproc)(d, sbuf, ssize);
+ if (cc != -1 && cc < ssize)
+ panic("sendrecv: short write! (%zd < %zu)",
+ cc, ssize);
+
+ if (cc == -1) {
+ /* Error on transmit; wait before retrying */
+ while ((getsecs() - t1) < tleft);
+ continue;
+ }
+
+recvnext:
+ /* Try to get a packet and process it. */
+ cc = (*rproc)(h, pkt, payload, tleft, rtype);
+ /* Return on data, EOF or real error. */
+ if (cc != -1 || errno != 0)
+ return (cc);
+ if ((getsecs() - t1) < tleft) {
+ goto recvnext;
+ }
+
+ /* Timed out or didn't get the packet we're waiting for */
+ tleft += MINTMO;
+ if (tleft > (2 * MINTMO)) {
+ tleft = (2 * MINTMO);
+ }
+ t1 = getsecs();
+ }
+}
+
+static int
+tftp_set_blksize(struct tftp_handle *h, const char *str)
+{
+ char *endptr;
+ int new_blksize;
+ int ret = 0;
+
+ if (h == NULL || str == NULL)
+ return (ret);
+
+ new_blksize =
+ (unsigned int)strtol(str, &endptr, 0);
+
+ /*
+ * Only accept blksize value if it is numeric.
+ * RFC2348 specifies that acceptable values are 8-65464.
+ * Let's choose a limit less than MAXRSPACE.
+ */
+ if (*endptr == '\0' && new_blksize >= 8
+ && new_blksize <= TFTP_MAX_BLKSIZE) {
+ h->tftp_blksize = new_blksize;
+ ret = 1;
+ }
+
+ return (ret);
+}
+
+/*
+ * In RFC2347, the TFTP Option Acknowledgement package (OACK)
+ * is used to acknowledge a client's option negotiation request.
+ * The format of an OACK packet is:
+ * +-------+---~~---+---+---~~---+---+---~~---+---+---~~---+---+
+ * | opc | opt1 | 0 | value1 | 0 | optN | 0 | valueN | 0 |
+ * +-------+---~~---+---+---~~---+---+---~~---+---+---~~---+---+
+ *
+ * opc
+ * The opcode field contains a 6, for Option Acknowledgment.
+ *
+ * opt1
+ * The first option acknowledgment, copied from the original
+ * request.
+ *
+ * value1
+ * The acknowledged value associated with the first option. If
+ * and how this value may differ from the original request is
+ * detailed in the specification for the option.
+ *
+ * optN, valueN
+ * The final option/value acknowledgment pair.
+ */
+static int
+tftp_parse_oack(struct tftp_handle *h, char *buf, size_t len)
+{
+ /*
+ * We parse the OACK strings into an array
+ * of name-value pairs.
+ */
+ char *tftp_options[128] = { 0 };
+ char *val = buf;
+ int i = 0;
+ int option_idx = 0;
+ int blksize_is_set = 0;
+ int tsize = 0;
+
+ unsigned int orig_blksize;
+
+ while (option_idx < 128 && i < len) {
+ if (buf[i] == '\0') {
+ if (&buf[i] > val) {
+ tftp_options[option_idx] = val;
+ val = &buf[i] + 1;
+ ++option_idx;
+ }
+ }
+ ++i;
+ }
+
+ /* Save the block size we requested for sanity check later. */
+ orig_blksize = h->tftp_blksize;
+
+ /*
+ * Parse individual TFTP options.
+ * * "blksize" is specified in RFC2348.
+ * * "tsize" is specified in RFC2349.
+ */
+ for (i = 0; i < option_idx; i += 2) {
+ if (strcasecmp(tftp_options[i], "blksize") == 0) {
+ if (i + 1 < option_idx)
+ blksize_is_set =
+ tftp_set_blksize(h, tftp_options[i + 1]);
+ } else if (strcasecmp(tftp_options[i], "tsize") == 0) {
+ if (i + 1 < option_idx)
+ tsize = strtol(tftp_options[i + 1], (char **)NULL, 10);
+ if (tsize != 0)
+ h->tftp_tsize = tsize;
+ } else {
+ /* Do not allow any options we did not expect to be ACKed. */
+ printf("unexpected tftp option '%s'\n", tftp_options[i]);
+ return (-1);
+ }
+ }
+
+ if (!blksize_is_set) {
+ /*
+ * If TFTP blksize was not set, try defaulting
+ * to the legacy TFTP blksize of SEGSIZE(512)
+ */
+ h->tftp_blksize = SEGSIZE;
+ } else if (h->tftp_blksize > orig_blksize) {
+ /*
+ * Server should not be proposing block sizes that
+ * exceed what we said we can handle.
+ */
+ printf("unexpected blksize %u\n", h->tftp_blksize);
+ return (-1);
+ }
+
+#ifdef TFTP_DEBUG
+ printf("tftp_blksize: %u\n", h->tftp_blksize);
+ printf("tftp_tsize: %lu\n", h->tftp_tsize);
+#endif
+ return 0;
+}
diff --git a/stand/libsa/tftp.h b/stand/libsa/tftp.h
new file mode 100644
index 0000000..cbbbbd7
--- /dev/null
+++ b/stand/libsa/tftp.h
@@ -0,0 +1,36 @@
+/* $NetBSD: tftp.h,v 1.1.1.1 1997/03/14 02:40:31 perry Exp $ */
+
+/*
+ * Copyright (c) 1996
+ * Matthias Drochner. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed for the NetBSD Project
+ * by Matthias Drochner.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * 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.
+ *
+ */
+
+
+#define IPPORT_TFTP 69
diff --git a/stand/libsa/twiddle.c b/stand/libsa/twiddle.c
new file mode 100644
index 0000000..96ebbbe
--- /dev/null
+++ b/stand/libsa/twiddle.c
@@ -0,0 +1,69 @@
+/*-
+ * Copyright (c) 1986, 1988, 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)subr_prf.c 8.3 (Berkeley) 1/21/94
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include "stand.h"
+
+/* Extra functions from NetBSD standalone printf.c */
+
+static u_int globaldiv;
+
+void
+twiddle(u_int callerdiv)
+{
+ static u_int callercnt, globalcnt, pos;
+
+ callercnt++;
+ if (callerdiv > 1 && (callercnt % callerdiv) != 0)
+ return;
+
+ globalcnt++;
+ if (globaldiv > 1 && (globalcnt % globaldiv) != 0)
+ return;
+
+ putchar("|/-\\"[pos++ & 3]);
+ putchar('\b');
+}
+
+void
+twiddle_divisor(u_int gdiv)
+{
+
+ globaldiv = gdiv;
+}
diff --git a/stand/libsa/udp.c b/stand/libsa/udp.c
new file mode 100644
index 0000000..8e52f29
--- /dev/null
+++ b/stand/libsa/udp.c
@@ -0,0 +1,180 @@
+/* Taken from $NetBSD: net.c,v 1.20 1997/12/26 22:41:30 scottr Exp $ */
+
+/*
+ * Copyright (c) 1992 Regents of the University of California.
+ * All rights reserved.
+ *
+ * This software was developed by the Computer Systems Engineering group
+ * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
+ * contributed to Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#) Header: net.c,v 1.9 93/08/06 19:32:15 leres Exp (LBL)
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/socket.h>
+
+#include <string.h>
+
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+#include <netinet/in_systm.h>
+
+#include <netinet/in_pcb.h>
+#include <netinet/ip.h>
+#include <netinet/ip_var.h>
+#include <netinet/udp.h>
+#include <netinet/udp_var.h>
+
+#include "stand.h"
+#include "net.h"
+
+/* Caller must leave room for ethernet, ip and udp headers in front!! */
+ssize_t
+sendudp(struct iodesc *d, void *pkt, size_t len)
+{
+ ssize_t cc;
+ struct udpiphdr *ui;
+ struct udphdr *uh;
+
+#ifdef NET_DEBUG
+ if (debug) {
+ printf("sendudp: d=%lx called.\n", (long)d);
+ if (d) {
+ printf("saddr: %s:%d",
+ inet_ntoa(d->myip), ntohs(d->myport));
+ printf(" daddr: %s:%d\n",
+ inet_ntoa(d->destip), ntohs(d->destport));
+ }
+ }
+#endif
+
+ ui = (struct udpiphdr *)pkt - 1;
+ bzero(ui, sizeof(*ui));
+
+ uh = (struct udphdr *)pkt - 1;
+ len += sizeof(*uh);
+
+ uh->uh_sport = d->myport;
+ uh->uh_dport = d->destport;
+ uh->uh_ulen = htons(len);
+
+ ui->ui_pr = IPPROTO_UDP;
+ ui->ui_len = uh->uh_ulen;
+ ui->ui_src = d->myip;
+ ui->ui_dst = d->destip;
+
+#ifndef UDP_NO_CKSUM
+ uh->uh_sum = in_cksum(ui, len + sizeof (struct ip));
+#endif
+
+ cc = sendip(d, uh, len, IPPROTO_UDP);
+ if (cc == -1)
+ return (-1);
+ if (cc != len)
+ panic("sendudp: bad write (%zd != %zd)", cc, len);
+ return (cc - sizeof(*uh));
+}
+
+/*
+ * Receive a UDP packet and validate it is for us.
+ */
+ssize_t
+readudp(struct iodesc *d, void **pkt, void **payload, time_t tleft)
+{
+ ssize_t n;
+ struct udphdr *uh;
+ void *ptr;
+
+#ifdef NET_DEBUG
+ if (debug)
+ printf("readudp: called\n");
+#endif
+
+ uh = NULL;
+ ptr = NULL;
+ n = readip(d, &ptr, (void **)&uh, tleft, IPPROTO_UDP);
+ if (n == -1 || n < sizeof(*uh) || n != ntohs(uh->uh_ulen)) {
+ free(ptr);
+ return (-1);
+ }
+
+ if (uh->uh_dport != d->myport) {
+#ifdef NET_DEBUG
+ if (debug)
+ printf("readudp: bad dport %d != %d\n",
+ d->myport, ntohs(uh->uh_dport));
+#endif
+ free(ptr);
+ return (-1);
+ }
+
+#ifndef UDP_NO_CKSUM
+ if (uh->uh_sum) {
+ struct udpiphdr *ui;
+ struct ip *ip;
+ struct ip tip;
+
+ n = ntohs(uh->uh_ulen) + sizeof(*ip);
+
+ /* Check checksum (must save and restore ip header) */
+ ip = (struct ip *)uh - 1;
+ tip = *ip;
+ ui = (struct udpiphdr *)ip;
+ bzero(&ui->ui_x1, sizeof(ui->ui_x1));
+ ui->ui_len = uh->uh_ulen;
+ if (in_cksum(ui, n) != 0) {
+#ifdef NET_DEBUG
+ if (debug)
+ printf("readudp: bad cksum\n");
+#endif
+ free(ptr);
+ return (-1);
+ }
+ *ip = tip;
+ }
+#endif
+ if (ntohs(uh->uh_ulen) < sizeof(*uh)) {
+#ifdef NET_DEBUG
+ if (debug)
+ printf("readudp: bad udp len %d < %d\n",
+ ntohs(uh->uh_ulen), (int)sizeof(*uh));
+#endif
+ free(ptr);
+ return (-1);
+ }
+
+ n = (n > (ntohs(uh->uh_ulen) - sizeof(*uh))) ?
+ ntohs(uh->uh_ulen) - sizeof(*uh) : n;
+ *pkt = ptr;
+ *payload = (void *)((uintptr_t)uh + sizeof(*uh));
+ return (n);
+}
diff --git a/stand/libsa/ufs.c b/stand/libsa/ufs.c
new file mode 100644
index 0000000..928a1d1
--- /dev/null
+++ b/stand/libsa/ufs.c
@@ -0,0 +1,861 @@
+/* $NetBSD: ufs.c,v 1.20 1998/03/01 07:15:39 ross Exp $ */
+
+/*-
+ * Copyright (c) 2002 Networks Associates Technology, Inc.
+ * All rights reserved.
+ *
+ * This software was developed for the FreeBSD Project by Marshall
+ * Kirk McKusick and Network Associates Laboratories, the Security
+ * Research Division of Network Associates, Inc. under DARPA/SPAWAR
+ * contract N66001-01-C-8035 ("CBOSS"), as part of the DARPA CHATS
+ * research program
+ *
+ * Copyright (c) 1982, 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * The Mach Operating System project at Carnegie-Mellon University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *
+ * Copyright (c) 1990, 1991 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Author: David Golub
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie the
+ * rights to redistribute these changes.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Stand-alone file reading package.
+ */
+
+#include <sys/param.h>
+#include <sys/disklabel.h>
+#include <sys/time.h>
+#include <ufs/ufs/dinode.h>
+#include <ufs/ufs/dir.h>
+#include <ufs/ffs/fs.h>
+#include "stand.h"
+#include "string.h"
+
+static int ufs_open(const char *path, struct open_file *f);
+static int ufs_write(struct open_file *f, void *buf, size_t size, size_t *resid);
+static int ufs_close(struct open_file *f);
+static int ufs_read(struct open_file *f, void *buf, size_t size, size_t *resid);
+static off_t ufs_seek(struct open_file *f, off_t offset, int where);
+static int ufs_stat(struct open_file *f, struct stat *sb);
+static int ufs_readdir(struct open_file *f, struct dirent *d);
+
+struct fs_ops ufs_fsops = {
+ "ufs",
+ ufs_open,
+ ufs_close,
+ ufs_read,
+ ufs_write,
+ ufs_seek,
+ ufs_stat,
+ ufs_readdir
+};
+
+/*
+ * In-core open file.
+ */
+struct file {
+ off_t f_seekp; /* seek pointer */
+ struct fs *f_fs; /* pointer to super-block */
+ union dinode {
+ struct ufs1_dinode di1;
+ struct ufs2_dinode di2;
+ } f_di; /* copy of on-disk inode */
+ int f_nindir[NIADDR];
+ /* number of blocks mapped by
+ indirect block at level i */
+ char *f_blk[NIADDR]; /* buffer for indirect block at
+ level i */
+ size_t f_blksize[NIADDR];
+ /* size of buffer */
+ ufs2_daddr_t f_blkno[NIADDR];/* disk address of block in buffer */
+ ufs2_daddr_t f_buf_blkno; /* block number of data block */
+ char *f_buf; /* buffer for data block */
+ size_t f_buf_size; /* size of data block */
+};
+#define DIP(fp, field) \
+ ((fp)->f_fs->fs_magic == FS_UFS1_MAGIC ? \
+ (fp)->f_di.di1.field : (fp)->f_di.di2.field)
+
+static int read_inode(ino_t, struct open_file *);
+static int block_map(struct open_file *, ufs2_daddr_t, ufs2_daddr_t *);
+static int buf_read_file(struct open_file *, char **, size_t *);
+static int buf_write_file(struct open_file *, char *, size_t *);
+static int search_directory(char *, struct open_file *, ino_t *);
+
+/*
+ * Read a new inode into a file structure.
+ */
+static int
+read_inode(inumber, f)
+ ino_t inumber;
+ struct open_file *f;
+{
+ struct file *fp = (struct file *)f->f_fsdata;
+ struct fs *fs = fp->f_fs;
+ char *buf;
+ size_t rsize;
+ int rc;
+
+ if (fs == NULL)
+ panic("fs == NULL");
+
+ /*
+ * Read inode and save it.
+ */
+ buf = malloc(fs->fs_bsize);
+ twiddle(1);
+ rc = (f->f_dev->dv_strategy)(f->f_devdata, F_READ,
+ fsbtodb(fs, ino_to_fsba(fs, inumber)), fs->fs_bsize,
+ buf, &rsize);
+ if (rc)
+ goto out;
+ if (rsize != fs->fs_bsize) {
+ rc = EIO;
+ goto out;
+ }
+
+ if (fp->f_fs->fs_magic == FS_UFS1_MAGIC)
+ fp->f_di.di1 = ((struct ufs1_dinode *)buf)
+ [ino_to_fsbo(fs, inumber)];
+ else
+ fp->f_di.di2 = ((struct ufs2_dinode *)buf)
+ [ino_to_fsbo(fs, inumber)];
+
+ /*
+ * Clear out the old buffers
+ */
+ {
+ int level;
+
+ for (level = 0; level < NIADDR; level++)
+ fp->f_blkno[level] = -1;
+ fp->f_buf_blkno = -1;
+ }
+ fp->f_seekp = 0;
+out:
+ free(buf);
+ return (rc);
+}
+
+/*
+ * Given an offset in a file, find the disk block number that
+ * contains that block.
+ */
+static int
+block_map(f, file_block, disk_block_p)
+ struct open_file *f;
+ ufs2_daddr_t file_block;
+ ufs2_daddr_t *disk_block_p; /* out */
+{
+ struct file *fp = (struct file *)f->f_fsdata;
+ struct fs *fs = fp->f_fs;
+ int level;
+ int idx;
+ ufs2_daddr_t ind_block_num;
+ int rc;
+
+ /*
+ * Index structure of an inode:
+ *
+ * di_db[0..NDADDR-1] hold block numbers for blocks
+ * 0..NDADDR-1
+ *
+ * di_ib[0] index block 0 is the single indirect block
+ * holds block numbers for blocks
+ * NDADDR .. NDADDR + NINDIR(fs)-1
+ *
+ * di_ib[1] index block 1 is the double indirect block
+ * holds block numbers for INDEX blocks for blocks
+ * NDADDR + NINDIR(fs) ..
+ * NDADDR + NINDIR(fs) + NINDIR(fs)**2 - 1
+ *
+ * di_ib[2] index block 2 is the triple indirect block
+ * holds block numbers for double-indirect
+ * blocks for blocks
+ * NDADDR + NINDIR(fs) + NINDIR(fs)**2 ..
+ * NDADDR + NINDIR(fs) + NINDIR(fs)**2
+ * + NINDIR(fs)**3 - 1
+ */
+
+ if (file_block < NDADDR) {
+ /* Direct block. */
+ *disk_block_p = DIP(fp, di_db[file_block]);
+ return (0);
+ }
+
+ file_block -= NDADDR;
+
+ /*
+ * nindir[0] = NINDIR
+ * nindir[1] = NINDIR**2
+ * nindir[2] = NINDIR**3
+ * etc
+ */
+ for (level = 0; level < NIADDR; level++) {
+ if (file_block < fp->f_nindir[level])
+ break;
+ file_block -= fp->f_nindir[level];
+ }
+ if (level == NIADDR) {
+ /* Block number too high */
+ return (EFBIG);
+ }
+
+ ind_block_num = DIP(fp, di_ib[level]);
+
+ for (; level >= 0; level--) {
+ if (ind_block_num == 0) {
+ *disk_block_p = 0; /* missing */
+ return (0);
+ }
+
+ if (fp->f_blkno[level] != ind_block_num) {
+ if (fp->f_blk[level] == (char *)0)
+ fp->f_blk[level] =
+ malloc(fs->fs_bsize);
+ twiddle(1);
+ rc = (f->f_dev->dv_strategy)(f->f_devdata, F_READ,
+ fsbtodb(fp->f_fs, ind_block_num),
+ fs->fs_bsize,
+ fp->f_blk[level],
+ &fp->f_blksize[level]);
+ if (rc)
+ return (rc);
+ if (fp->f_blksize[level] != fs->fs_bsize)
+ return (EIO);
+ fp->f_blkno[level] = ind_block_num;
+ }
+
+ if (level > 0) {
+ idx = file_block / fp->f_nindir[level - 1];
+ file_block %= fp->f_nindir[level - 1];
+ } else
+ idx = file_block;
+
+ if (fp->f_fs->fs_magic == FS_UFS1_MAGIC)
+ ind_block_num = ((ufs1_daddr_t *)fp->f_blk[level])[idx];
+ else
+ ind_block_num = ((ufs2_daddr_t *)fp->f_blk[level])[idx];
+ }
+
+ *disk_block_p = ind_block_num;
+
+ return (0);
+}
+
+/*
+ * Write a portion of a file from an internal buffer.
+ */
+static int
+buf_write_file(f, buf_p, size_p)
+ struct open_file *f;
+ char *buf_p;
+ size_t *size_p; /* out */
+{
+ struct file *fp = (struct file *)f->f_fsdata;
+ struct fs *fs = fp->f_fs;
+ long off;
+ ufs_lbn_t file_block;
+ ufs2_daddr_t disk_block;
+ size_t block_size;
+ int rc;
+
+ /*
+ * Calculate the starting block address and offset.
+ */
+ off = blkoff(fs, fp->f_seekp);
+ file_block = lblkno(fs, fp->f_seekp);
+ block_size = sblksize(fs, DIP(fp, di_size), file_block);
+
+ rc = block_map(f, file_block, &disk_block);
+ if (rc)
+ return (rc);
+
+ if (disk_block == 0)
+ /* Because we can't allocate space on the drive */
+ return (EFBIG);
+
+ /*
+ * Truncate buffer at end of file, and at the end of
+ * this block.
+ */
+ if (*size_p > DIP(fp, di_size) - fp->f_seekp)
+ *size_p = DIP(fp, di_size) - fp->f_seekp;
+ if (*size_p > block_size - off)
+ *size_p = block_size - off;
+
+ /*
+ * If we don't entirely occlude the block and it's not
+ * in memory already, read it in first.
+ */
+ if (((off > 0) || (*size_p + off < block_size)) &&
+ (file_block != fp->f_buf_blkno)) {
+
+ if (fp->f_buf == (char *)0)
+ fp->f_buf = malloc(fs->fs_bsize);
+
+ twiddle(4);
+ rc = (f->f_dev->dv_strategy)(f->f_devdata, F_READ,
+ fsbtodb(fs, disk_block),
+ block_size, fp->f_buf, &fp->f_buf_size);
+ if (rc)
+ return (rc);
+
+ fp->f_buf_blkno = file_block;
+ }
+
+ /*
+ * Copy the user data into the cached block.
+ */
+ bcopy(buf_p, fp->f_buf + off, *size_p);
+
+ /*
+ * Write the block out to storage.
+ */
+
+ twiddle(4);
+ rc = (f->f_dev->dv_strategy)(f->f_devdata, F_WRITE,
+ fsbtodb(fs, disk_block),
+ block_size, fp->f_buf, &fp->f_buf_size);
+ return (rc);
+}
+
+/*
+ * Read a portion of a file into an internal buffer. Return
+ * the location in the buffer and the amount in the buffer.
+ */
+static int
+buf_read_file(f, buf_p, size_p)
+ struct open_file *f;
+ char **buf_p; /* out */
+ size_t *size_p; /* out */
+{
+ struct file *fp = (struct file *)f->f_fsdata;
+ struct fs *fs = fp->f_fs;
+ long off;
+ ufs_lbn_t file_block;
+ ufs2_daddr_t disk_block;
+ size_t block_size;
+ int rc;
+
+ off = blkoff(fs, fp->f_seekp);
+ file_block = lblkno(fs, fp->f_seekp);
+ block_size = sblksize(fs, DIP(fp, di_size), file_block);
+
+ if (file_block != fp->f_buf_blkno) {
+ if (fp->f_buf == (char *)0)
+ fp->f_buf = malloc(fs->fs_bsize);
+
+ rc = block_map(f, file_block, &disk_block);
+ if (rc)
+ return (rc);
+
+ if (disk_block == 0) {
+ bzero(fp->f_buf, block_size);
+ fp->f_buf_size = block_size;
+ } else {
+ twiddle(4);
+ rc = (f->f_dev->dv_strategy)(f->f_devdata, F_READ,
+ fsbtodb(fs, disk_block),
+ block_size, fp->f_buf, &fp->f_buf_size);
+ if (rc)
+ return (rc);
+ }
+
+ fp->f_buf_blkno = file_block;
+ }
+
+ /*
+ * Return address of byte in buffer corresponding to
+ * offset, and size of remainder of buffer after that
+ * byte.
+ */
+ *buf_p = fp->f_buf + off;
+ *size_p = block_size - off;
+
+ /*
+ * But truncate buffer at end of file.
+ */
+ if (*size_p > DIP(fp, di_size) - fp->f_seekp)
+ *size_p = DIP(fp, di_size) - fp->f_seekp;
+
+ return (0);
+}
+
+/*
+ * Search a directory for a name and return its
+ * i_number.
+ */
+static int
+search_directory(name, f, inumber_p)
+ char *name;
+ struct open_file *f;
+ ino_t *inumber_p; /* out */
+{
+ struct file *fp = (struct file *)f->f_fsdata;
+ struct direct *dp;
+ struct direct *edp;
+ char *buf;
+ size_t buf_size;
+ int namlen, length;
+ int rc;
+
+ length = strlen(name);
+
+ fp->f_seekp = 0;
+ while (fp->f_seekp < DIP(fp, di_size)) {
+ rc = buf_read_file(f, &buf, &buf_size);
+ if (rc)
+ return (rc);
+
+ dp = (struct direct *)buf;
+ edp = (struct direct *)(buf + buf_size);
+ while (dp < edp) {
+ if (dp->d_ino == (ino_t)0)
+ goto next;
+#if BYTE_ORDER == LITTLE_ENDIAN
+ if (fp->f_fs->fs_maxsymlinklen <= 0)
+ namlen = dp->d_type;
+ else
+#endif
+ namlen = dp->d_namlen;
+ if (namlen == length &&
+ !strcmp(name, dp->d_name)) {
+ /* found entry */
+ *inumber_p = dp->d_ino;
+ return (0);
+ }
+ next:
+ dp = (struct direct *)((char *)dp + dp->d_reclen);
+ }
+ fp->f_seekp += buf_size;
+ }
+ return (ENOENT);
+}
+
+static int sblock_try[] = SBLOCKSEARCH;
+
+/*
+ * Open a file.
+ */
+static int
+ufs_open(upath, f)
+ const char *upath;
+ struct open_file *f;
+{
+ char *cp, *ncp;
+ int c;
+ ino_t inumber, parent_inumber;
+ struct file *fp;
+ struct fs *fs;
+ int i, rc;
+ size_t buf_size;
+ int nlinks = 0;
+ char namebuf[MAXPATHLEN+1];
+ char *buf = NULL;
+ char *path = NULL;
+
+ /* allocate file system specific data structure */
+ fp = malloc(sizeof(struct file));
+ bzero(fp, sizeof(struct file));
+ f->f_fsdata = (void *)fp;
+
+ /* allocate space and read super block */
+ fs = malloc(SBLOCKSIZE);
+ fp->f_fs = fs;
+ twiddle(1);
+ /*
+ * Try reading the superblock in each of its possible locations.
+ */
+ for (i = 0; sblock_try[i] != -1; i++) {
+ rc = (f->f_dev->dv_strategy)(f->f_devdata, F_READ,
+ sblock_try[i] / DEV_BSIZE, SBLOCKSIZE,
+ (char *)fs, &buf_size);
+ if (rc)
+ goto out;
+ if ((fs->fs_magic == FS_UFS1_MAGIC ||
+ (fs->fs_magic == FS_UFS2_MAGIC &&
+ fs->fs_sblockloc == sblock_try[i])) &&
+ buf_size == SBLOCKSIZE &&
+ fs->fs_bsize <= MAXBSIZE &&
+ fs->fs_bsize >= sizeof(struct fs))
+ break;
+ }
+ if (sblock_try[i] == -1) {
+ rc = EINVAL;
+ goto out;
+ }
+ /*
+ * Calculate indirect block levels.
+ */
+ {
+ ufs2_daddr_t mult;
+ int level;
+
+ mult = 1;
+ for (level = 0; level < NIADDR; level++) {
+ mult *= NINDIR(fs);
+ fp->f_nindir[level] = mult;
+ }
+ }
+
+ inumber = ROOTINO;
+ if ((rc = read_inode(inumber, f)) != 0)
+ goto out;
+
+ cp = path = strdup(upath);
+ if (path == NULL) {
+ rc = ENOMEM;
+ goto out;
+ }
+ while (*cp) {
+
+ /*
+ * Remove extra separators
+ */
+ while (*cp == '/')
+ cp++;
+ if (*cp == '\0')
+ break;
+
+ /*
+ * Check that current node is a directory.
+ */
+ if ((DIP(fp, di_mode) & IFMT) != IFDIR) {
+ rc = ENOTDIR;
+ goto out;
+ }
+
+ /*
+ * Get next component of path name.
+ */
+ {
+ int len = 0;
+
+ ncp = cp;
+ while ((c = *cp) != '\0' && c != '/') {
+ if (++len > MAXNAMLEN) {
+ rc = ENOENT;
+ goto out;
+ }
+ cp++;
+ }
+ *cp = '\0';
+ }
+
+ /*
+ * Look up component in current directory.
+ * Save directory inumber in case we find a
+ * symbolic link.
+ */
+ parent_inumber = inumber;
+ rc = search_directory(ncp, f, &inumber);
+ *cp = c;
+ if (rc)
+ goto out;
+
+ /*
+ * Open next component.
+ */
+ if ((rc = read_inode(inumber, f)) != 0)
+ goto out;
+
+ /*
+ * Check for symbolic link.
+ */
+ if ((DIP(fp, di_mode) & IFMT) == IFLNK) {
+ int link_len = DIP(fp, di_size);
+ int len;
+
+ len = strlen(cp);
+
+ if (link_len + len > MAXPATHLEN ||
+ ++nlinks > MAXSYMLINKS) {
+ rc = ENOENT;
+ goto out;
+ }
+
+ bcopy(cp, &namebuf[link_len], len + 1);
+
+ if (link_len < fs->fs_maxsymlinklen) {
+ if (fp->f_fs->fs_magic == FS_UFS1_MAGIC)
+ cp = (caddr_t)(fp->f_di.di1.di_db);
+ else
+ cp = (caddr_t)(fp->f_di.di2.di_db);
+ bcopy(cp, namebuf, (unsigned) link_len);
+ } else {
+ /*
+ * Read file for symbolic link
+ */
+ size_t buf_size;
+ ufs2_daddr_t disk_block;
+ struct fs *fs = fp->f_fs;
+
+ if (!buf)
+ buf = malloc(fs->fs_bsize);
+ rc = block_map(f, (ufs2_daddr_t)0, &disk_block);
+ if (rc)
+ goto out;
+
+ twiddle(1);
+ rc = (f->f_dev->dv_strategy)(f->f_devdata,
+ F_READ, fsbtodb(fs, disk_block),
+ fs->fs_bsize, buf, &buf_size);
+ if (rc)
+ goto out;
+
+ bcopy((char *)buf, namebuf, (unsigned)link_len);
+ }
+
+ /*
+ * If relative pathname, restart at parent directory.
+ * If absolute pathname, restart at root.
+ */
+ cp = namebuf;
+ if (*cp != '/')
+ inumber = parent_inumber;
+ else
+ inumber = (ino_t)ROOTINO;
+
+ if ((rc = read_inode(inumber, f)) != 0)
+ goto out;
+ }
+ }
+
+ /*
+ * Found terminal component.
+ */
+ rc = 0;
+ fp->f_seekp = 0;
+out:
+ if (buf)
+ free(buf);
+ if (path)
+ free(path);
+ if (rc) {
+ if (fp->f_buf)
+ free(fp->f_buf);
+ free(fp->f_fs);
+ free(fp);
+ }
+ return (rc);
+}
+
+static int
+ufs_close(f)
+ struct open_file *f;
+{
+ struct file *fp = (struct file *)f->f_fsdata;
+ int level;
+
+ f->f_fsdata = (void *)0;
+ if (fp == (struct file *)0)
+ return (0);
+
+ for (level = 0; level < NIADDR; level++) {
+ if (fp->f_blk[level])
+ free(fp->f_blk[level]);
+ }
+ if (fp->f_buf)
+ free(fp->f_buf);
+ free(fp->f_fs);
+ free(fp);
+ return (0);
+}
+
+/*
+ * Copy a portion of a file into kernel memory.
+ * Cross block boundaries when necessary.
+ */
+static int
+ufs_read(f, start, size, resid)
+ struct open_file *f;
+ void *start;
+ size_t size;
+ size_t *resid; /* out */
+{
+ struct file *fp = (struct file *)f->f_fsdata;
+ size_t csize;
+ char *buf;
+ size_t buf_size;
+ int rc = 0;
+ char *addr = start;
+
+ while (size != 0) {
+ if (fp->f_seekp >= DIP(fp, di_size))
+ break;
+
+ rc = buf_read_file(f, &buf, &buf_size);
+ if (rc)
+ break;
+
+ csize = size;
+ if (csize > buf_size)
+ csize = buf_size;
+
+ bcopy(buf, addr, csize);
+
+ fp->f_seekp += csize;
+ addr += csize;
+ size -= csize;
+ }
+ if (resid)
+ *resid = size;
+ return (rc);
+}
+
+/*
+ * Write to a portion of an already allocated file.
+ * Cross block boundaries when necessary. Can not
+ * extend the file.
+ */
+static int
+ufs_write(f, start, size, resid)
+ struct open_file *f;
+ void *start;
+ size_t size;
+ size_t *resid; /* out */
+{
+ struct file *fp = (struct file *)f->f_fsdata;
+ size_t csize;
+ int rc = 0;
+ char *addr = start;
+
+ csize = size;
+ while ((size != 0) && (csize != 0)) {
+ if (fp->f_seekp >= DIP(fp, di_size))
+ break;
+
+ if (csize >= 512) csize = 512; /* XXX */
+
+ rc = buf_write_file(f, addr, &csize);
+ if (rc)
+ break;
+
+ fp->f_seekp += csize;
+ addr += csize;
+ size -= csize;
+ }
+ if (resid)
+ *resid = size;
+ return (rc);
+}
+
+static off_t
+ufs_seek(f, offset, where)
+ struct open_file *f;
+ off_t offset;
+ int where;
+{
+ struct file *fp = (struct file *)f->f_fsdata;
+
+ switch (where) {
+ case SEEK_SET:
+ fp->f_seekp = offset;
+ break;
+ case SEEK_CUR:
+ fp->f_seekp += offset;
+ break;
+ case SEEK_END:
+ fp->f_seekp = DIP(fp, di_size) - offset;
+ break;
+ default:
+ errno = EINVAL;
+ return (-1);
+ }
+ return (fp->f_seekp);
+}
+
+static int
+ufs_stat(f, sb)
+ struct open_file *f;
+ struct stat *sb;
+{
+ struct file *fp = (struct file *)f->f_fsdata;
+
+ /* only important stuff */
+ sb->st_mode = DIP(fp, di_mode);
+ sb->st_uid = DIP(fp, di_uid);
+ sb->st_gid = DIP(fp, di_gid);
+ sb->st_size = DIP(fp, di_size);
+ return (0);
+}
+
+static int
+ufs_readdir(struct open_file *f, struct dirent *d)
+{
+ struct file *fp = (struct file *)f->f_fsdata;
+ struct direct *dp;
+ char *buf;
+ size_t buf_size;
+ int error;
+
+ /*
+ * assume that a directory entry will not be split across blocks
+ */
+again:
+ if (fp->f_seekp >= DIP(fp, di_size))
+ return (ENOENT);
+ error = buf_read_file(f, &buf, &buf_size);
+ if (error)
+ return (error);
+ dp = (struct direct *)buf;
+ fp->f_seekp += dp->d_reclen;
+ if (dp->d_ino == (ino_t)0)
+ goto again;
+ d->d_type = dp->d_type;
+ strcpy(d->d_name, dp->d_name);
+ return (0);
+}
diff --git a/stand/libsa/ufsread.c b/stand/libsa/ufsread.c
new file mode 100644
index 0000000..5ba65a4
--- /dev/null
+++ b/stand/libsa/ufsread.c
@@ -0,0 +1,326 @@
+/*-
+ * Copyright (c) 2002 McAfee, Inc.
+ * All rights reserved.
+ *
+ * This software was developed for the FreeBSD Project by Marshall
+ * Kirk McKusick and McAfee Research,, the Security Research Division of
+ * McAfee, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as
+ * part of the DARPA CHATS research program
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+/*-
+ * Copyright (c) 1998 Robert Nordier
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are freely
+ * permitted provided that the above copyright notice and this
+ * paragraph and the following disclaimer are duplicated in all
+ * such forms.
+ *
+ * This software is provided "AS IS" and without any express or
+ * implied warranties, including, without limitation, the implied
+ * warranties of merchantability and fitness for a particular
+ * purpose.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <ufs/ufs/dinode.h>
+#include <ufs/ufs/dir.h>
+#include <ufs/ffs/fs.h>
+
+#ifdef UFS_SMALL_CGBASE
+/* XXX: Revert to old (broken for over 1.5Tb filesystems) version of cgbase
+ (see sys/ufs/ffs/fs.h rev 1.39) so that small boot loaders (e.g. boot2) can
+ support both UFS1 and UFS2. */
+#undef cgbase
+#define cgbase(fs, c) ((ufs2_daddr_t)((fs)->fs_fpg * (c)))
+#endif
+
+typedef uint32_t ufs_ino_t;
+
+/*
+ * We use 4k `virtual' blocks for filesystem data, whatever the actual
+ * filesystem block size. FFS blocks are always a multiple of 4k.
+ */
+#define VBLKSHIFT 12
+#define VBLKSIZE (1 << VBLKSHIFT)
+#define VBLKMASK (VBLKSIZE - 1)
+#define DBPERVBLK (VBLKSIZE / DEV_BSIZE)
+#define INDIRPERVBLK(fs) (NINDIR(fs) / ((fs)->fs_bsize >> VBLKSHIFT))
+#define IPERVBLK(fs) (INOPB(fs) / ((fs)->fs_bsize >> VBLKSHIFT))
+#define INO_TO_VBA(fs, ipervblk, x) \
+ (fsbtodb(fs, cgimin(fs, ino_to_cg(fs, x))) + \
+ (((x) % (fs)->fs_ipg) / (ipervblk) * DBPERVBLK))
+#define INO_TO_VBO(ipervblk, x) ((x) % ipervblk)
+#define FS_TO_VBA(fs, fsb, off) (fsbtodb(fs, fsb) + \
+ ((off) / VBLKSIZE) * DBPERVBLK)
+#define FS_TO_VBO(fs, fsb, off) ((off) & VBLKMASK)
+
+/* Buffers that must not span a 64k boundary. */
+struct dmadat {
+ char blkbuf[VBLKSIZE]; /* filesystem blocks */
+ char indbuf[VBLKSIZE]; /* indir blocks */
+ char sbbuf[SBLOCKSIZE]; /* superblock */
+ char secbuf[DEV_BSIZE]; /* for MBR/disklabel */
+};
+static struct dmadat *dmadat;
+
+static ufs_ino_t lookup(const char *);
+static ssize_t fsread(ufs_ino_t, void *, size_t);
+
+static uint8_t ls, dsk_meta;
+static uint32_t fs_off;
+
+static __inline uint8_t
+fsfind(const char *name, ufs_ino_t * ino)
+{
+ static char buf[DEV_BSIZE];
+ static struct direct d;
+ char *s;
+ ssize_t n;
+
+ fs_off = 0;
+ while ((n = fsread(*ino, buf, DEV_BSIZE)) > 0)
+ for (s = buf; s < buf + DEV_BSIZE;) {
+ memcpy(&d, s, sizeof(struct direct));
+ if (ls)
+ printf("%s ", d.d_name);
+ else if (!strcmp(name, d.d_name)) {
+ *ino = d.d_ino;
+ return d.d_type;
+ }
+ s += d.d_reclen;
+ }
+ if (n != -1 && ls)
+ printf("\n");
+ return 0;
+}
+
+static ufs_ino_t
+lookup(const char *path)
+{
+ static char name[MAXNAMLEN + 1];
+ const char *s;
+ ufs_ino_t ino;
+ ssize_t n;
+ uint8_t dt;
+
+ ino = ROOTINO;
+ dt = DT_DIR;
+ for (;;) {
+ if (*path == '/')
+ path++;
+ if (!*path)
+ break;
+ for (s = path; *s && *s != '/'; s++);
+ if ((n = s - path) > MAXNAMLEN)
+ return 0;
+ ls = *path == '?' && n == 1 && !*s;
+ memcpy(name, path, n);
+ name[n] = 0;
+ if (dt != DT_DIR) {
+ printf("%s: not a directory.\n", name);
+ return (0);
+ }
+ if ((dt = fsfind(name, &ino)) <= 0)
+ break;
+ path = s;
+ }
+ return dt == DT_REG ? ino : 0;
+}
+
+/*
+ * Possible superblock locations ordered from most to least likely.
+ */
+static int sblock_try[] = SBLOCKSEARCH;
+
+#if defined(UFS2_ONLY)
+#define DIP(field) dp2.field
+#elif defined(UFS1_ONLY)
+#define DIP(field) dp1.field
+#else
+#define DIP(field) fs.fs_magic == FS_UFS1_MAGIC ? dp1.field : dp2.field
+#endif
+
+static ssize_t
+fsread_size(ufs_ino_t inode, void *buf, size_t nbyte, size_t *fsizep)
+{
+#ifndef UFS2_ONLY
+ static struct ufs1_dinode dp1;
+ ufs1_daddr_t addr1;
+#endif
+#ifndef UFS1_ONLY
+ static struct ufs2_dinode dp2;
+#endif
+ static struct fs fs;
+ static ufs_ino_t inomap;
+ char *blkbuf;
+ void *indbuf;
+ char *s;
+ size_t n, nb, size, off, vboff;
+ ufs_lbn_t lbn;
+ ufs2_daddr_t addr2, vbaddr;
+ static ufs2_daddr_t blkmap, indmap;
+ u_int u;
+
+ /* Basic parameter validation. */
+ if ((buf == NULL && nbyte != 0) || dmadat == NULL)
+ return (-1);
+
+ blkbuf = dmadat->blkbuf;
+ indbuf = dmadat->indbuf;
+
+ /*
+ * Force probe if inode is zero to ensure we have a valid fs, otherwise
+ * when probing multiple paritions, reads from subsequent parititions
+ * will incorrectly succeed.
+ */
+ if (!dsk_meta || inode == 0) {
+ inomap = 0;
+ dsk_meta = 0;
+ for (n = 0; sblock_try[n] != -1; n++) {
+ if (dskread(dmadat->sbbuf, sblock_try[n] / DEV_BSIZE,
+ SBLOCKSIZE / DEV_BSIZE))
+ return -1;
+ memcpy(&fs, dmadat->sbbuf, sizeof(struct fs));
+ if ((
+#if defined(UFS1_ONLY)
+ fs.fs_magic == FS_UFS1_MAGIC
+#elif defined(UFS2_ONLY)
+ (fs.fs_magic == FS_UFS2_MAGIC &&
+ fs.fs_sblockloc == sblock_try[n])
+#else
+ fs.fs_magic == FS_UFS1_MAGIC ||
+ (fs.fs_magic == FS_UFS2_MAGIC &&
+ fs.fs_sblockloc == sblock_try[n])
+#endif
+ ) &&
+ fs.fs_bsize <= MAXBSIZE &&
+ fs.fs_bsize >= (int32_t)sizeof(struct fs))
+ break;
+ }
+ if (sblock_try[n] == -1) {
+ return -1;
+ }
+ dsk_meta++;
+ } else
+ memcpy(&fs, dmadat->sbbuf, sizeof(struct fs));
+ if (!inode)
+ return 0;
+ if (inomap != inode) {
+ n = IPERVBLK(&fs);
+ if (dskread(blkbuf, INO_TO_VBA(&fs, n, inode), DBPERVBLK))
+ return -1;
+ n = INO_TO_VBO(n, inode);
+#if defined(UFS1_ONLY)
+ memcpy(&dp1, (struct ufs1_dinode *)(void *)blkbuf + n,
+ sizeof(dp1));
+#elif defined(UFS2_ONLY)
+ memcpy(&dp2, (struct ufs2_dinode *)(void *)blkbuf + n,
+ sizeof(dp2));
+#else
+ if (fs.fs_magic == FS_UFS1_MAGIC)
+ memcpy(&dp1, (struct ufs1_dinode *)(void *)blkbuf + n,
+ sizeof(dp1));
+ else
+ memcpy(&dp2, (struct ufs2_dinode *)(void *)blkbuf + n,
+ sizeof(dp2));
+#endif
+ inomap = inode;
+ fs_off = 0;
+ blkmap = indmap = 0;
+ }
+ s = buf;
+ size = DIP(di_size);
+ n = size - fs_off;
+ if (nbyte > n)
+ nbyte = n;
+ nb = nbyte;
+ while (nb) {
+ lbn = lblkno(&fs, fs_off);
+ off = blkoff(&fs, fs_off);
+ if (lbn < NDADDR) {
+ addr2 = DIP(di_db[lbn]);
+ } else if (lbn < NDADDR + NINDIR(&fs)) {
+ n = INDIRPERVBLK(&fs);
+ addr2 = DIP(di_ib[0]);
+ u = (u_int)(lbn - NDADDR) / n * DBPERVBLK;
+ vbaddr = fsbtodb(&fs, addr2) + u;
+ if (indmap != vbaddr) {
+ if (dskread(indbuf, vbaddr, DBPERVBLK))
+ return -1;
+ indmap = vbaddr;
+ }
+ n = (lbn - NDADDR) & (n - 1);
+#if defined(UFS1_ONLY)
+ memcpy(&addr1, (ufs1_daddr_t *)indbuf + n,
+ sizeof(ufs1_daddr_t));
+ addr2 = addr1;
+#elif defined(UFS2_ONLY)
+ memcpy(&addr2, (ufs2_daddr_t *)indbuf + n,
+ sizeof(ufs2_daddr_t));
+#else
+ if (fs.fs_magic == FS_UFS1_MAGIC) {
+ memcpy(&addr1, (ufs1_daddr_t *)indbuf + n,
+ sizeof(ufs1_daddr_t));
+ addr2 = addr1;
+ } else
+ memcpy(&addr2, (ufs2_daddr_t *)indbuf + n,
+ sizeof(ufs2_daddr_t));
+#endif
+ } else
+ return -1;
+ vbaddr = fsbtodb(&fs, addr2) + (off >> VBLKSHIFT) * DBPERVBLK;
+ vboff = off & VBLKMASK;
+ n = sblksize(&fs, (off_t)size, lbn) - (off & ~VBLKMASK);
+ if (n > VBLKSIZE)
+ n = VBLKSIZE;
+ if (blkmap != vbaddr) {
+ if (dskread(blkbuf, vbaddr, n >> DEV_BSHIFT))
+ return -1;
+ blkmap = vbaddr;
+ }
+ n -= vboff;
+ if (n > nb)
+ n = nb;
+ memcpy(s, blkbuf + vboff, n);
+ s += n;
+ fs_off += n;
+ nb -= n;
+ }
+
+ if (fsizep != NULL)
+ *fsizep = size;
+
+ return nbyte;
+}
+
+static ssize_t
+fsread(ufs_ino_t inode, void *buf, size_t nbyte)
+{
+
+ return fsread_size(inode, buf, nbyte, NULL);
+}
+
diff --git a/stand/libsa/util.c b/stand/libsa/util.c
new file mode 100644
index 0000000..49f42eb
--- /dev/null
+++ b/stand/libsa/util.c
@@ -0,0 +1,182 @@
+/*-
+ * Copyright (c) 1998 Robert Nordier
+ * Copyright (c) 2010 Pawel Jakub Dawidek <pjd@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are freely
+ * permitted provided that the above copyright notice and this
+ * paragraph and the following disclaimer are duplicated in all
+ * such forms.
+ *
+ * This software is provided "AS IS" and without any express or
+ * implied warranties, including, without limitation, the implied
+ * warranties of merchantability and fitness for a particular
+ * purpose.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+
+#include <stdarg.h>
+
+#include "cons.h"
+#include "util.h"
+
+void
+memcpy(void *dst, const void *src, int len)
+{
+ const char *s = src;
+ char *d = dst;
+
+ while (len--)
+ *d++ = *s++;
+}
+
+void
+memset(void *b, int c, size_t len)
+{
+ char *bp = b;
+
+ while (len--)
+ *bp++ = (unsigned char)c;
+}
+
+int
+memcmp(const void *b1, const void *b2, size_t len)
+{
+ const unsigned char *p1, *p2;
+
+ for (p1 = b1, p2 = b2; len > 0; len--, p1++, p2++) {
+ if (*p1 != *p2)
+ return ((*p1) - (*p2));
+ }
+ return (0);
+}
+
+int
+strcmp(const char *s1, const char *s2)
+{
+
+ for (; *s1 == *s2 && *s1 != '\0'; s1++, s2++)
+ ;
+ return ((unsigned char)*s1 - (unsigned char)*s2);
+}
+
+int
+strncmp(const char *s1, const char *s2, size_t len)
+{
+
+ for (; len > 0 && *s1 == *s2 && *s1 != '\0'; len--, s1++, s2++)
+ ;
+ return (len == 0 ? 0 : (unsigned char)*s1 - (unsigned char)*s2);
+}
+
+void
+strcpy(char *dst, const char *src)
+{
+
+ while (*src != '\0')
+ *dst++ = *src++;
+ *dst = '\0';
+}
+
+void
+strcat(char *dst, const char *src)
+{
+
+ while (*dst != '\0')
+ dst++;
+ while (*src != '\0')
+ *dst++ = *src++;
+ *dst = '\0';
+}
+
+char *
+strchr(const char *s, char ch)
+{
+
+ for (; *s != '\0'; s++) {
+ if (*s == ch)
+ return ((char *)(uintptr_t)(const void *)s);
+ }
+ return (NULL);
+}
+
+size_t
+strlen(const char *s)
+{
+ size_t len = 0;
+
+ while (*s++ != '\0')
+ len++;
+ return (len);
+}
+
+int
+printf(const char *fmt, ...)
+{
+ va_list ap;
+ const char *hex = "0123456789abcdef";
+ char buf[32], *s;
+ uint16_t *S;
+ unsigned long long u;
+ int c, l;
+
+ va_start(ap, fmt);
+ while ((c = *fmt++) != '\0') {
+ if (c != '%') {
+ putchar(c);
+ continue;
+ }
+ l = 0;
+nextfmt:
+ c = *fmt++;
+ switch (c) {
+ case 'l':
+ l++;
+ goto nextfmt;
+ case 'c':
+ putchar(va_arg(ap, int));
+ break;
+ case 's':
+ for (s = va_arg(ap, char *); *s != '\0'; s++)
+ putchar(*s);
+ break;
+ case 'S': /* Assume console can cope with wide chars */
+ for (S = va_arg(ap, uint16_t *); *S != 0; S++)
+ putchar(*S);
+ break;
+ case 'd': /* A lie, always prints unsigned */
+ case 'u':
+ case 'x':
+ switch (l) {
+ case 2:
+ u = va_arg(ap, unsigned long long);
+ break;
+ case 1:
+ u = va_arg(ap, unsigned long);
+ break;
+ default:
+ u = va_arg(ap, unsigned int);
+ break;
+ }
+ s = buf;
+ if (c == 'd' || c == 'u') {
+ do
+ *s++ = '0' + (u % 10U);
+ while (u /= 10);
+ } else {
+ do
+ *s++ = hex[u & 0xfu];
+ while (u >>= 4);
+ }
+ while (--s >= buf)
+ putchar(*s);
+ break;
+ }
+ }
+ va_end(ap);
+ return (0);
+}
diff --git a/stand/libsa/util.h b/stand/libsa/util.h
new file mode 100644
index 0000000..88a99f1
--- /dev/null
+++ b/stand/libsa/util.h
@@ -0,0 +1,53 @@
+/*-
+ * Copyright (c) 2010 Pawel Jakub Dawidek <pjd@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _UTIL_H_
+#define _UTIL_H_
+
+#include <sys/types.h>
+
+#include <stdarg.h>
+
+void memcpy(void *dst, const void *src, int len);
+void memset(void *b, int c, size_t len);
+int memcmp(const void *b1, const void *b2, size_t len);
+
+#define bcopy(src, dst, len) memcpy((dst), (src), (len))
+#define bzero(buf, size) memset((buf), 0, (size))
+#define bcmp(b1, b2, len) (memcmp((b1), (b2), (len)) != 0)
+
+int strcmp(const char *s1, const char *s2);
+int strncmp(const char *s1, const char *s2, size_t len);
+void strcpy(char *dst, const char *src);
+void strcat(char *dst, const char *src);
+char *strchr(const char *s, char ch);
+size_t strlen(const char *s);
+
+int printf(const char *fmt, ...);
+
+#endif /* !_UTIL_H_ */
diff --git a/stand/libsa/uuid_from_string.c b/stand/libsa/uuid_from_string.c
new file mode 100644
index 0000000..7a59b41
--- /dev/null
+++ b/stand/libsa/uuid_from_string.c
@@ -0,0 +1,132 @@
+/*-
+ * Copyright (c) 2015 M. Warner Losh
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Note: some comments taken from lib/libc/uuid/uuid_from_string.c
+ * Copyright (c) 2002 Marcel Moolenaar
+ * Copyright (c) 2002 Hiten Mahesh Pandya
+ */
+
+
+#include <stand.h>
+#include <uuid.h>
+
+static int
+hex2int(int ch)
+{
+ if (ch >= '0' && ch <= '9')
+ return ch - '0';
+ if (ch >= 'a' && ch <= 'f')
+ return 10 + ch - 'a';
+ if (ch >= 'A' && ch <= 'F')
+ return 10 + ch - 'A';
+ return 16;
+}
+
+static uint32_t
+fromhex(const char *s, int len, int *ok)
+{
+ uint32_t v;
+ int i, h;
+
+ if (!*ok)
+ return 0;
+ v = 0;
+ for (i = 0; i < len; i++) {
+ h = hex2int(s[i]);
+ if (h == 16) {
+ *ok = 0;
+ return v;
+ }
+ v = (v << 4) | h;
+ }
+ return v;
+}
+
+/*
+ * uuid_from_string() - convert a string representation of an UUID into
+ * a binary representation.
+ * See also:
+ * http://www.opengroup.org/onlinepubs/009629399/uuid_from_string.htm
+ *
+ * NOTE: The sequence field is in big-endian, while the time fields are in
+ * native byte order.
+ *
+ * 01234567-89ab-cdef-0123-456789abcdef
+ * 000000000011111111112222222222333333
+ * 012345678901234567890123456789012345
+ * - - - -
+ * hhhhhhhh-hhhh-hhhh-bbbb-bbbbbbbbbbbb
+ *
+ */
+void
+uuid_from_string(const char *s, uuid_t *u, uint32_t *status)
+{
+ int ok = 1;
+ int n;
+
+ if (s == NULL || *s == '\0') {
+ uuid_create_nil(u, status);
+ return;
+ }
+
+ if (status != NULL)
+ *status = uuid_s_invalid_string_uuid;
+ if (strlen(s) != 36)
+ return;
+ /* Only support new format, check for all the right dashes */
+ if (s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-')
+ return;
+ /* native byte order */
+ u->time_low = fromhex(s , 8, &ok);
+ u->time_mid = fromhex(s + 9, 4, &ok);
+ u->time_hi_and_version = fromhex(s + 14, 4, &ok);
+ /* Big endian, but presented as a whole number so decode as such */
+ u->clock_seq_hi_and_reserved = fromhex(s + 19, 2, &ok);
+ u->clock_seq_low = fromhex(s + 21, 2, &ok);
+ u->node[0] = fromhex(s + 24, 2, &ok);
+ u->node[1] = fromhex(s + 26, 2, &ok);
+ u->node[2] = fromhex(s + 28, 2, &ok);
+ u->node[3] = fromhex(s + 30, 2, &ok);
+ u->node[4] = fromhex(s + 32, 2, &ok);
+ u->node[5] = fromhex(s + 34, 2, &ok);
+ if (!ok)
+ return;
+
+ /* We have a successful scan. Check semantics... */
+ n = u->clock_seq_hi_and_reserved;
+ if ((n & 0x80) != 0x00 && /* variant 0? */
+ (n & 0xc0) != 0x80 && /* variant 1? */
+ (n & 0xe0) != 0xc0) { /* variant 2? */
+ if (status != NULL)
+ *status = uuid_s_bad_version;
+ } else {
+ if (status != NULL)
+ *status = uuid_s_ok;
+ }
+}
diff --git a/stand/libsa/uuid_to_string.c b/stand/libsa/uuid_to_string.c
new file mode 100644
index 0000000..d878af4
--- /dev/null
+++ b/stand/libsa/uuid_to_string.c
@@ -0,0 +1,111 @@
+/*-
+ * Copyright (c) 2015 M. Warner Losh
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+
+/*
+ * Note: some comments taken from lib/libc/uuid/uuid_to_string.c
+ * Copyright (c) 2002,2005 Marcel Moolenaar
+ * Copyright (c) 2002 Hiten Mahesh Pandya
+ */
+
+#include <stand.h>
+#include <uuid.h>
+
+/*
+ * Dump len characters into *buf from val as hex and update *buf
+ */
+static void
+tohex(char **buf, int len, uint32_t val)
+{
+ static const char *hexstr = "0123456789abcdef";
+ char *walker = *buf;
+ int i;
+
+ for (i = len - 1; i >= 0; i--) {
+ walker[i] = hexstr[val & 0xf];
+ val >>= 4;
+ }
+ *buf = walker + len;
+}
+
+/*
+ * uuid_to_string() - Convert a binary UUID into a string representation.
+ * See also:
+ * http://www.opengroup.org/onlinepubs/009629399/uuid_to_string.htm
+ *
+ * NOTE: The references given above do not have a status code for when
+ * the string could not be allocated. The status code has been
+ * taken from the Hewlett-Packard implementation.
+ *
+ * NOTE: we don't support u == NULL for a nil UUID, sorry.
+ *
+ * NOTE: The sequence field is in big-endian, while the time fields are in
+ * native byte order.
+ *
+ * hhhhhhhh-hhhh-hhhh-bbbb-bbbbbbbbbbbb
+ * 01234567-89ab-cdef-0123-456789abcdef
+ */
+void
+uuid_to_string(const uuid_t *u, char **s, uint32_t *status)
+{
+ uuid_t nil;
+ char *w;
+
+ if (status != NULL)
+ *status = uuid_s_ok;
+ if (s == NULL) /* Regular version does this odd-ball behavior too */
+ return;
+ w = *s = malloc(37);
+ if (*s == NULL) {
+ if (status != NULL)
+ *status = uuid_s_no_memory;
+ return;
+ }
+ if (u == NULL) {
+ u = &nil;
+ uuid_create_nil(&nil, NULL);
+ }
+ /* native */
+ tohex(&w, 8, u->time_low);
+ *w++ = '-';
+ tohex(&w, 4, u->time_mid);
+ *w++ = '-';
+ tohex(&w, 4, u->time_hi_and_version);
+ *w++ = '-';
+ /* Big endian, so do a byte at a time */
+ tohex(&w, 2, u->clock_seq_hi_and_reserved);
+ tohex(&w, 2, u->clock_seq_low);
+ *w++ = '-';
+ tohex(&w, 2, u->node[0]);
+ tohex(&w, 2, u->node[1]);
+ tohex(&w, 2, u->node[2]);
+ tohex(&w, 2, u->node[3]);
+ tohex(&w, 2, u->node[4]);
+ tohex(&w, 2, u->node[5]);
+ *w++ = '\0';
+}
diff --git a/stand/libsa/write.c b/stand/libsa/write.c
new file mode 100644
index 0000000..9e02f08
--- /dev/null
+++ b/stand/libsa/write.c
@@ -0,0 +1,95 @@
+/* $NetBSD: write.c,v 1.7 1996/06/21 20:29:30 pk Exp $ */
+
+/*-
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * The Mach Operating System project at Carnegie-Mellon University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)write.c 8.1 (Berkeley) 6/11/93
+ *
+ *
+ * Copyright (c) 1989, 1990, 1991 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Author: Alessandro Forin
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie the
+ * rights to redistribute these changes.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include "stand.h"
+
+ssize_t
+write(fd, dest, bcount)
+ int fd;
+ void *dest;
+ size_t bcount;
+{
+ struct open_file *f = &files[fd];
+ size_t resid;
+
+ if ((unsigned)fd >= SOPEN_MAX || !(f->f_flags & F_WRITE)) {
+ errno = EBADF;
+ return (-1);
+ }
+ if (f->f_flags & F_RAW) {
+ twiddle(4);
+ errno = (f->f_dev->dv_strategy)(f->f_devdata, F_WRITE,
+ btodb(f->f_offset), bcount, dest, &resid);
+ if (errno)
+ return (-1);
+ f->f_offset += resid;
+ return (resid);
+ }
+ resid = bcount;
+ if ((errno = (f->f_ops->fo_write)(f, dest, bcount, &resid)))
+ return (-1);
+ return (bcount - resid);
+}
diff --git a/stand/libsa/zalloc.c b/stand/libsa/zalloc.c
new file mode 100644
index 0000000..4d1ec62
--- /dev/null
+++ b/stand/libsa/zalloc.c
@@ -0,0 +1,316 @@
+/*
+ * This module derived from code donated to the FreeBSD Project by
+ * Matthew Dillon <dillon@backplane.com>
+ *
+ * Copyright (c) 1998 The FreeBSD Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * LIB/MEMORY/ZALLOC.C - self contained low-overhead memory pool/allocation
+ * subsystem
+ *
+ * This subsystem implements memory pools and memory allocation
+ * routines.
+ *
+ * Pools are managed via a linked list of 'free' areas. Allocating
+ * memory creates holes in the freelist, freeing memory fills them.
+ * Since the freelist consists only of free memory areas, it is possible
+ * to allocate the entire pool without incuring any structural overhead.
+ *
+ * The system works best when allocating similarly-sized chunks of
+ * memory. Care must be taken to avoid fragmentation when
+ * allocating/deallocating dissimilar chunks.
+ *
+ * When a memory pool is first allocated, the entire pool is marked as
+ * allocated. This is done mainly because we do not want to modify any
+ * portion of a pool's data area until we are given permission. The
+ * caller must explicitly deallocate portions of the pool to make them
+ * available.
+ *
+ * z[n]xalloc() works like z[n]alloc() but the allocation is made from
+ * within the specified address range. If the segment could not be
+ * allocated, NULL is returned. WARNING! The address range will be
+ * aligned to an 8 or 16 byte boundry depending on the cpu so if you
+ * give an unaligned address range, unexpected results may occur.
+ *
+ * If a standard allocation fails, the reclaim function will be called
+ * to recover some space. This usually causes other portions of the
+ * same pool to be released. Memory allocations at this low level
+ * should not block but you can do that too in your reclaim function
+ * if you want. Reclaim does not function when z[n]xalloc() is used,
+ * only for z[n]alloc().
+ *
+ * Allocation and frees of 0 bytes are valid operations.
+ */
+
+#include "zalloc_defs.h"
+
+/*
+ * Objects in the pool must be aligned to at least the size of struct MemNode.
+ * They must also be aligned to MALLOCALIGN, which should normally be larger
+ * than the struct, so assert that to be so at compile time.
+ */
+typedef char assert_align[(sizeof(struct MemNode) <= MALLOCALIGN) ? 1 : -1];
+
+#define MEMNODE_SIZE_MASK MALLOCALIGN_MASK
+
+/*
+ * znalloc() - allocate memory (without zeroing) from pool. Call reclaim
+ * and retry if appropriate, return NULL if unable to allocate
+ * memory.
+ */
+
+void *
+znalloc(MemPool *mp, uintptr_t bytes)
+{
+ /*
+ * align according to pool object size (can be 0). This is
+ * inclusive of the MEMNODE_SIZE_MASK minimum alignment.
+ *
+ */
+ bytes = (bytes + MEMNODE_SIZE_MASK) & ~MEMNODE_SIZE_MASK;
+
+ if (bytes == 0)
+ return((void *)-1);
+
+ /*
+ * locate freelist entry big enough to hold the object. If all objects
+ * are the same size, this is a constant-time function.
+ */
+
+ if (bytes <= mp->mp_Size - mp->mp_Used) {
+ MemNode **pmn;
+ MemNode *mn;
+
+ for (pmn = &mp->mp_First; (mn=*pmn) != NULL; pmn = &mn->mr_Next) {
+ if (bytes > mn->mr_Bytes)
+ continue;
+
+ /*
+ * Cut a chunk of memory out of the beginning of this
+ * block and fixup the link appropriately.
+ */
+
+ {
+ char *ptr = (char *)mn;
+
+ if (mn->mr_Bytes == bytes) {
+ *pmn = mn->mr_Next;
+ } else {
+ mn = (MemNode *)((char *)mn + bytes);
+ mn->mr_Next = ((MemNode *)ptr)->mr_Next;
+ mn->mr_Bytes = ((MemNode *)ptr)->mr_Bytes - bytes;
+ *pmn = mn;
+ }
+ mp->mp_Used += bytes;
+ return(ptr);
+ }
+ }
+ }
+
+ /*
+ * Memory pool is full, return NULL.
+ */
+
+ return(NULL);
+}
+
+/*
+ * zfree() - free previously allocated memory
+ */
+
+void
+zfree(MemPool *mp, void *ptr, uintptr_t bytes)
+{
+ /*
+ * align according to pool object size (can be 0). This is
+ * inclusive of the MEMNODE_SIZE_MASK minimum alignment.
+ */
+ bytes = (bytes + MEMNODE_SIZE_MASK) & ~MEMNODE_SIZE_MASK;
+
+ if (bytes == 0)
+ return;
+
+ /*
+ * panic if illegal pointer
+ */
+
+ if ((char *)ptr < (char *)mp->mp_Base ||
+ (char *)ptr + bytes > (char *)mp->mp_End ||
+ ((uintptr_t)ptr & MEMNODE_SIZE_MASK) != 0)
+ panic("zfree(%p,%ju): wild pointer", ptr, (uintmax_t)bytes);
+
+ /*
+ * free the segment
+ */
+
+ {
+ MemNode **pmn;
+ MemNode *mn;
+
+ mp->mp_Used -= bytes;
+
+ for (pmn = &mp->mp_First; (mn = *pmn) != NULL; pmn = &mn->mr_Next) {
+ /*
+ * If area between last node and current node
+ * - check range
+ * - check merge with next area
+ * - check merge with previous area
+ */
+ if ((char *)ptr <= (char *)mn) {
+ /*
+ * range check
+ */
+ if ((char *)ptr + bytes > (char *)mn) {
+ panic("zfree(%p,%ju): corrupt memlist1", ptr,
+ (uintmax_t)bytes);
+ }
+
+ /*
+ * merge against next area or create independant area
+ */
+
+ if ((char *)ptr + bytes == (char *)mn) {
+ ((MemNode *)ptr)->mr_Next = mn->mr_Next;
+ ((MemNode *)ptr)->mr_Bytes= bytes + mn->mr_Bytes;
+ } else {
+ ((MemNode *)ptr)->mr_Next = mn;
+ ((MemNode *)ptr)->mr_Bytes= bytes;
+ }
+ *pmn = mn = (MemNode *)ptr;
+
+ /*
+ * merge against previous area (if there is a previous
+ * area).
+ */
+
+ if (pmn != &mp->mp_First) {
+ if ((char*)pmn + ((MemNode*)pmn)->mr_Bytes == (char*)ptr) {
+ ((MemNode *)pmn)->mr_Next = mn->mr_Next;
+ ((MemNode *)pmn)->mr_Bytes += mn->mr_Bytes;
+ mn = (MemNode *)pmn;
+ }
+ }
+ return;
+ /* NOT REACHED */
+ }
+ if ((char *)ptr < (char *)mn + mn->mr_Bytes) {
+ panic("zfree(%p,%ju): corrupt memlist2", ptr,
+ (uintmax_t)bytes);
+ }
+ }
+ /*
+ * We are beyond the last MemNode, append new MemNode. Merge against
+ * previous area if possible.
+ */
+ if (pmn == &mp->mp_First ||
+ (char *)pmn + ((MemNode *)pmn)->mr_Bytes != (char *)ptr
+ ) {
+ ((MemNode *)ptr)->mr_Next = NULL;
+ ((MemNode *)ptr)->mr_Bytes = bytes;
+ *pmn = (MemNode *)ptr;
+ mn = (MemNode *)ptr;
+ } else {
+ ((MemNode *)pmn)->mr_Bytes += bytes;
+ mn = (MemNode *)pmn;
+ }
+ }
+}
+
+/*
+ * zextendPool() - extend memory pool to cover additional space.
+ *
+ * Note: the added memory starts out as allocated, you
+ * must free it to make it available to the memory subsystem.
+ *
+ * Note: mp_Size may not reflect (mp_End - mp_Base) range
+ * due to other parts of the system doing their own sbrk()
+ * calls.
+ */
+
+void
+zextendPool(MemPool *mp, void *base, uintptr_t bytes)
+{
+ if (mp->mp_Size == 0) {
+ mp->mp_Base = base;
+ mp->mp_Used = bytes;
+ mp->mp_End = (char *)base + bytes;
+ mp->mp_Size = bytes;
+ } else {
+ void *pend = (char *)mp->mp_Base + mp->mp_Size;
+
+ if (base < mp->mp_Base) {
+ mp->mp_Size += (char *)mp->mp_Base - (char *)base;
+ mp->mp_Used += (char *)mp->mp_Base - (char *)base;
+ mp->mp_Base = base;
+ }
+ base = (char *)base + bytes;
+ if (base > pend) {
+ mp->mp_Size += (char *)base - (char *)pend;
+ mp->mp_Used += (char *)base - (char *)pend;
+ mp->mp_End = (char *)base;
+ }
+ }
+}
+
+#ifdef ZALLOCDEBUG
+
+void
+zallocstats(MemPool *mp)
+{
+ int abytes = 0;
+ int hbytes = 0;
+ int fcount = 0;
+ MemNode *mn;
+
+ printf("%d bytes reserved", (int) mp->mp_Size);
+
+ mn = mp->mp_First;
+
+ if ((void *)mn != (void *)mp->mp_Base) {
+ abytes += (char *)mn - (char *)mp->mp_Base;
+ }
+
+ while (mn) {
+ if ((char *)mn + mn->mr_Bytes != mp->mp_End) {
+ hbytes += mn->mr_Bytes;
+ ++fcount;
+ }
+ if (mn->mr_Next)
+ abytes += (char *)mn->mr_Next - ((char *)mn + mn->mr_Bytes);
+ mn = mn->mr_Next;
+ }
+ printf(" %d bytes allocated\n%d fragments (%d bytes fragmented)\n",
+ abytes,
+ fcount,
+ hbytes
+ );
+}
+
+#endif
+
diff --git a/stand/libsa/zalloc_defs.h b/stand/libsa/zalloc_defs.h
new file mode 100644
index 0000000..7f2cc12
--- /dev/null
+++ b/stand/libsa/zalloc_defs.h
@@ -0,0 +1,78 @@
+/*
+ * This module derived from code donated to the FreeBSD Project by
+ * Matthew Dillon <dillon@backplane.com>
+ *
+ * Copyright (c) 1998 The FreeBSD Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * DEFS.H
+ */
+
+#define USEGUARD /* use stard/end guard bytes */
+#define USEENDGUARD
+#define DMALLOCDEBUG /* add debugging code to gather stats */
+#define ZALLOCDEBUG
+
+#include <sys/stdint.h>
+#include "stand.h"
+#include "zalloc_mem.h"
+
+#define Library extern
+
+/*
+ * block extension for sbrk()
+ */
+
+#define BLKEXTEND (4 * 1024)
+#define BLKEXTENDMASK (BLKEXTEND - 1)
+
+/*
+ * Required malloc alignment.
+ *
+ * Embedded platforms using the u-boot API drivers require that all I/O buffers
+ * be on a cache line sized boundary. The worst case size for that is 64 bytes.
+ * For other platforms, 16 bytes works fine. The alignment also must be at
+ * least sizeof(struct MemNode); this is asserted in zalloc.c.
+ */
+
+#if defined(__arm__) || defined(__mips__) || defined(__powerpc__)
+#define MALLOCALIGN 64
+#else
+#define MALLOCALIGN 16
+#endif
+#define MALLOCALIGN_MASK (MALLOCALIGN - 1)
+
+typedef struct Guard {
+ size_t ga_Bytes;
+ size_t ga_Magic; /* must be at least 32 bits */
+} Guard;
+
+#define GAMAGIC 0x55FF44FD
+#define GAFREE 0x5F54F4DF
+
+#include "zalloc_protos.h"
diff --git a/stand/libsa/zalloc_malloc.c b/stand/libsa/zalloc_malloc.c
new file mode 100644
index 0000000..b9a295f
--- /dev/null
+++ b/stand/libsa/zalloc_malloc.c
@@ -0,0 +1,200 @@
+/*
+ * This module derived from code donated to the FreeBSD Project by
+ * Matthew Dillon <dillon@backplane.com>
+ *
+ * Copyright (c) 1998 The FreeBSD Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * MALLOC.C - malloc equivalent, runs on top of zalloc and uses sbrk
+ */
+
+#include "zalloc_defs.h"
+
+static MemPool MallocPool;
+
+#ifdef DMALLOCDEBUG
+static int MallocMax;
+static int MallocCount;
+
+void mallocstats(void);
+#endif
+
+#ifdef malloc
+#undef malloc
+#undef free
+#endif
+
+void *
+Malloc(size_t bytes, const char *file, int line)
+{
+ Guard *res;
+
+#ifdef USEENDGUARD
+ bytes += MALLOCALIGN + 1;
+#else
+ bytes += MALLOCALIGN;
+#endif
+
+ while ((res = znalloc(&MallocPool, bytes)) == NULL) {
+ int incr = (bytes + BLKEXTENDMASK) & ~BLKEXTENDMASK;
+ char *base;
+
+ if ((base = sbrk(incr)) == (char *)-1)
+ return(NULL);
+ zextendPool(&MallocPool, base, incr);
+ zfree(&MallocPool, base, incr);
+ }
+#ifdef DMALLOCDEBUG
+ if (++MallocCount > MallocMax)
+ MallocMax = MallocCount;
+#endif
+#ifdef USEGUARD
+ res->ga_Magic = GAMAGIC;
+#endif
+ res->ga_Bytes = bytes;
+#ifdef USEENDGUARD
+ *((signed char *)res + bytes - 1) = -2;
+#endif
+
+ return((char *)res + MALLOCALIGN);
+}
+
+void
+Free(void *ptr, const char *file, int line)
+{
+ size_t bytes;
+
+ if (ptr != NULL) {
+ Guard *res = (void *)((char *)ptr - MALLOCALIGN);
+
+ if (file == NULL)
+ file = "unknown";
+#ifdef USEGUARD
+ if (res->ga_Magic == GAFREE) {
+ printf("free: duplicate free @ %p from %s:%d\n", ptr, file, line);
+ return;
+ }
+ if (res->ga_Magic != GAMAGIC)
+ panic("free: guard1 fail @ %p from %s:%d", ptr, file, line);
+ res->ga_Magic = GAFREE;
+#endif
+#ifdef USEENDGUARD
+ if (*((signed char *)res + res->ga_Bytes - 1) == -1) {
+ printf("free: duplicate2 free @ %p from %s:%d\n", ptr, file, line);
+ return;
+ }
+ if (*((signed char *)res + res->ga_Bytes - 1) != -2)
+ panic("free: guard2 fail @ %p + %zu from %s:%d", ptr, res->ga_Bytes - MALLOCALIGN, file, line);
+ *((signed char *)res + res->ga_Bytes - 1) = -1;
+#endif
+
+ bytes = res->ga_Bytes;
+ zfree(&MallocPool, res, bytes);
+#ifdef DMALLOCDEBUG
+ --MallocCount;
+#endif
+ }
+}
+
+
+void *
+Calloc(size_t n1, size_t n2, const char *file, int line)
+{
+ uintptr_t bytes = (uintptr_t)n1 * (uintptr_t)n2;
+ void *res;
+
+ if ((res = Malloc(bytes, file, line)) != NULL) {
+ bzero(res, bytes);
+#ifdef DMALLOCDEBUG
+ if (++MallocCount > MallocMax)
+ MallocMax = MallocCount;
+#endif
+ }
+ return(res);
+}
+
+/*
+ * realloc() - I could be fancier here and free the old buffer before
+ * allocating the new one (saving potential fragmentation
+ * and potential buffer copies). But I don't bother.
+ */
+
+void *
+Realloc(void *ptr, size_t size, const char *file, int line)
+{
+ void *res;
+ size_t old;
+
+ if ((res = Malloc(size, file, line)) != NULL) {
+ if (ptr) {
+ old = *(size_t *)((char *)ptr - MALLOCALIGN) - MALLOCALIGN;
+ if (old < size)
+ bcopy(ptr, res, old);
+ else
+ bcopy(ptr, res, size);
+ Free(ptr, file, line);
+ } else {
+#ifdef DMALLOCDEBUG
+ if (++MallocCount > MallocMax)
+ MallocMax = MallocCount;
+#ifdef EXITSTATS
+ if (DidAtExit == 0) {
+ DidAtExit = 1;
+ atexit(mallocstats);
+ }
+#endif
+#endif
+ }
+ }
+ return(res);
+}
+
+void *
+Reallocf(void *ptr, size_t size, const char *file, int line)
+{
+ void *res;
+
+ if ((res = Realloc(ptr, size, file, line)) == NULL)
+ Free(ptr, file, line);
+ return(res);
+}
+
+#ifdef DMALLOCDEBUG
+
+void
+mallocstats(void)
+{
+ printf("Active Allocations: %d/%d\n", MallocCount, MallocMax);
+#ifdef ZALLOCDEBUG
+ zallocstats(&MallocPool);
+#endif
+}
+
+#endif
+
diff --git a/stand/libsa/zalloc_mem.h b/stand/libsa/zalloc_mem.h
new file mode 100644
index 0000000..26d388d
--- /dev/null
+++ b/stand/libsa/zalloc_mem.h
@@ -0,0 +1,53 @@
+/*
+ * This module derived from code donated to the FreeBSD Project by
+ * Matthew Dillon <dillon@backplane.com>
+ *
+ * Copyright (c) 1998 The FreeBSD Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * H/MEM.H
+ *
+ * Basic memory pool / memory node structures.
+ */
+
+typedef struct MemNode {
+ struct MemNode *mr_Next;
+ uintptr_t mr_Bytes;
+} MemNode;
+
+typedef struct MemPool {
+ void *mp_Base;
+ void *mp_End;
+ MemNode *mp_First;
+ uintptr_t mp_Size;
+ uintptr_t mp_Used;
+} MemPool;
+
+#define ZNOTE_FREE 0
+#define ZNOTE_REUSE 1
+
diff --git a/stand/libsa/zalloc_protos.h b/stand/libsa/zalloc_protos.h
new file mode 100644
index 0000000..53a40e4
--- /dev/null
+++ b/stand/libsa/zalloc_protos.h
@@ -0,0 +1,35 @@
+/*
+ * This module derived from code donated to the FreeBSD Project by
+ * Matthew Dillon <dillon@backplane.com>
+ *
+ * Copyright (c) 1998 The FreeBSD Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+Library void *znalloc(struct MemPool *mpool, uintptr_t bytes);
+Library void zfree(struct MemPool *mpool, void *ptr, uintptr_t bytes);
+Library void zextendPool(MemPool *mp, void *base, uintptr_t bytes);
+Library void zallocstats(struct MemPool *mp);
OpenPOWER on IntegriCloud