commit b6842439dfa7887aa58fbc5ece001fa7254316ed Author: Sam Leffler Date: Sun Nov 23 19:59:42 2008 +0000 prepare makefs for import to base diff --git a/Makefile b/Makefile new file mode 100644 index 00000000000..e15d6c84d64 --- /dev/null +++ b/Makefile @@ -0,0 +1,22 @@ +# $FreeBSD$ + +PROG= makefs +COMPAT_MTREE= getid.c misc.c spec.c +COMPAT= fparseln.c getmode.c pack_dev.c pwcache.c stat_flags.c \ + strsuftoll.c vis.c +SRCS= ffs.c makefs.c walk.c \ + buf.c ffs_alloc.c ffs_balloc.c mkfs.c ufs_bmap.c \ + ffs_bswap.c ffs_subr.c ffs_tables.c \ + ${COMPAT} ${COMPAT_MTREE} +MAN= makefs.8 + +CFLAGS+=-DHAVE_NBTOOL_CONFIG_H=1 -D_FILE_OFFSET_BITS=64 +CFLAGS+=-I. +CFLAGS+=-Icompat -Icompat/mtree +CFLAGS+=-Iffs -Isys -Isys/ufs + +.PATH: compat compat/mtree ffs sys/ufs/ufs sys/ufs/ffs + +WARNS?= 2 + +.include diff --git a/compat/compat_defs.h b/compat/compat_defs.h new file mode 100644 index 00000000000..8ba4047a7a8 --- /dev/null +++ b/compat/compat_defs.h @@ -0,0 +1,167 @@ +/* $NetBSD: compat_defs.h,v 1.43 2004/06/23 11:08:01 tron Exp $ */ + +#ifndef __NETBSD_COMPAT_DEFS_H__ +#define __NETBSD_COMPAT_DEFS_H__ + +/* Work around some complete brain damage. */ + +#undef _POSIX_SOURCE +#undef _POSIX_C_SOURCE + +/* System headers needed for (re)definitions below. */ + +#include +#include +#include +/* time.h needs to be pulled in first at least on netbsd w/o _NETBSD_SOURCE */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if HAVE_SYS_CDEFS_H +#include +#endif +#if HAVE_SYS_SYSLIMITS_H +#include +#endif +#if HAVE_SYS_SYSMACROS_H +/* major(), minor() on SVR4 */ +#include +#endif +#if HAVE_INTTYPES_H +#include +#endif +#if HAVE_STDDEF_H +#include +#endif + +/* We don't include here, so that "compat_pwd.h" works. */ +struct passwd; + +/* Some things usually in BSD . */ +#ifndef __RENAME +#define __RENAME(x) +#endif + +/* Dirent support. */ +#include +#define NAMLEN(dirent) (strlen((dirent)->d_name)) + +#if !HAVE_FPARSELN || defined(__NetBSD__) +# define FPARSELN_UNESCESC 0x01 +# define FPARSELN_UNESCCONT 0x02 +# define FPARSELN_UNESCCOMM 0x04 +# define FPARSELN_UNESCREST 0x08 +# define FPARSELN_UNESCALL 0x0f +char *fparseln(FILE *, size_t *, size_t *, const char [3], int); +#endif + +#define __nbcompat_bswap16(x) ((((x) << 8) & 0xff00) | (((x) >> 8) & 0x00ff)) + +#define __nbcompat_bswap32(x) ((((x) << 24) & 0xff000000) | \ + (((x) << 8) & 0x00ff0000) | \ + (((x) >> 8) & 0x0000ff00) | \ + (((x) >> 24) & 0x000000ff)) + +#define __nbcompat_bswap64(x) (((u_int64_t)bswap32((x)) << 32) | \ + ((u_int64_t)bswap32((x) >> 32))) + +#if !HAVE_BSWAP16 +#ifdef bswap16 +#undef bswap16 +#endif +#define bswap16(x) __nbcompat_bswap16(x) +#endif +#if !HAVE_BSWAP32 +#ifdef bswap32 +#undef bswap32 +#endif +#define bswap32(x) __nbcompat_bswap32(x) +#endif +#if !HAVE_BSWAP64 +#ifdef bswap64 +#undef bswap64 +#endif +#define bswap64(x) __nbcompat_bswap64(x) +#endif + +#if !HAVE_PWCACHE_USERDB +int uid_from_user(const char *, uid_t *); +int pwcache_userdb(int (*)(int), void (*)(void), + struct passwd * (*)(const char *), struct passwd * (*)(uid_t)); +int gid_from_group(const char *, gid_t *); +int pwcache_groupdb(int (*)(int), void (*)(void), + struct group * (*)(const char *), struct group * (*)(gid_t)); +#endif +/* Make them use our version */ +# define user_from_uid __nbcompat_user_from_uid +/* Make them use our version */ +# define group_from_gid __nbcompat_group_from_gid +#if HAVE_GROUP_FROM_GID +const char *group_from_gid(gid_t, int); +#endif + +#if !HAVE_SETENV +int setenv(const char *, const char *, int); +#endif + +#if !HAVE_STRSUFTOLL +long long strsuftoll(const char *, const char *, long long, long long); +long long strsuftollx(const char *, const char *, + long long, long long, char *, size_t); +#endif + +#if !HAVE_USER_FROM_UID +const char *user_from_uid(uid_t, int); +#endif + +#if !HAVE_GROUP_FROM_GID +const char *group_from_gid(gid_t, int); +#endif + +/* + * getmode() and setmode() are always defined, as these function names + * exist but with very different meanings on other OS's. The compat + * versions here simply accept an octal mode number; the "u+x,g-w" type + * of syntax is not accepted. + */ + +#define getmode __nbcompat_getmode +#define setmode __nbcompat_setmode + +mode_t getmode(const void *, mode_t); +void *setmode(const char *); + +/* Eliminate assertions embedded in binaries. */ + +#undef _DIAGASSERT +#define _DIAGASSERT(x) + +/* Various sources use this */ +#undef __RCSID +#define __RCSID(x) +#undef __SCCSID +#define __SCCSID(x) +#undef __COPYRIGHT +#define __COPYRIGHT(x) +#undef __KERNEL_RCSID +#define __KERNEL_RCSID(x,y) + +/* Heimdal expects this one. */ + +#undef RCSID +#define RCSID(x) + +#ifndef MAXFRAG +#define MAXFRAG 8 +#endif + +#endif /* !__NETBSD_COMPAT_DEFS_H__ */ diff --git a/compat/fparseln.c b/compat/fparseln.c new file mode 100644 index 00000000000..af28131e1c2 --- /dev/null +++ b/compat/fparseln.c @@ -0,0 +1,248 @@ +/* $NetBSD: fparseln.c,v 1.5 2004/06/20 22:20:15 jmc Exp $ */ + +/* + * Copyright (c) 1997 Christos Zoulas. 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 Christos Zoulas. + * 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 +#if defined(LIBC_SCCS) && !defined(lint) +__RCSID("$NetBSD: fparseln.c,v 1.5 2004/06/20 22:20:15 jmc Exp $"); +#endif /* LIBC_SCCS and not lint */ + +#include "namespace.h" + +#include +#include +#include +#include +#include + +#ifdef __weak_alias +__weak_alias(fparseln,_fparseln) +#endif + +#if ! HAVE_FPARSELN + +#ifndef HAVE_NBTOOL_CONFIG_H +#include "reentrant.h" +#include "local.h" +#else +#define FLOCKFILE(fp) +#define FUNLOCKFILE(fp) +#endif + +#if defined(_REENTRANT) && !HAVE_NBTOOL_CONFIG_H +#define __fgetln(f, l) __fgetstr(f, l, '\n') +#else +#define __fgetln(f, l) fgetln(f, l) +#endif + +static int isescaped(const char *, const char *, int); + +/* isescaped(): + * Return true if the character in *p that belongs to a string + * that starts in *sp, is escaped by the escape character esc. + */ +static int +isescaped(const char *sp, const char *p, int esc) +{ + const char *cp; + size_t ne; + + _DIAGASSERT(sp != NULL); + _DIAGASSERT(p != NULL); + + /* No escape character */ + if (esc == '\0') + return 1; + + /* Count the number of escape characters that precede ours */ + for (ne = 0, cp = p; --cp >= sp && *cp == esc; ne++) + continue; + + /* Return true if odd number of escape characters */ + return (ne & 1) != 0; +} + + +/* fparseln(): + * Read a line from a file parsing continuations ending in \ + * and eliminating trailing newlines, or comments starting with + * the comment char. + */ +char * +fparseln(FILE *fp, size_t *size, size_t *lineno, const char str[3], int flags) +{ + static const char dstr[3] = { '\\', '\\', '#' }; + + size_t s, len; + char *buf; + char *ptr, *cp; + int cnt; + char esc, con, nl, com; + + _DIAGASSERT(fp != NULL); + + len = 0; + buf = NULL; + cnt = 1; + + if (str == NULL) + str = dstr; + + esc = str[0]; + con = str[1]; + com = str[2]; + /* + * XXX: it would be cool to be able to specify the newline character, + * but unfortunately, fgetln does not let us + */ + nl = '\n'; + + FLOCKFILE(fp); + + while (cnt) { + cnt = 0; + + if (lineno) + (*lineno)++; + + if ((ptr = __fgetln(fp, &s)) == NULL) + break; + + if (s && com) { /* Check and eliminate comments */ + for (cp = ptr; cp < ptr + s; cp++) + if (*cp == com && !isescaped(ptr, cp, esc)) { + s = cp - ptr; + cnt = s == 0 && buf == NULL; + break; + } + } + + if (s && nl) { /* Check and eliminate newlines */ + cp = &ptr[s - 1]; + + if (*cp == nl) + s--; /* forget newline */ + } + + if (s && con) { /* Check and eliminate continuations */ + cp = &ptr[s - 1]; + + if (*cp == con && !isescaped(ptr, cp, esc)) { + s--; /* forget escape */ + cnt = 1; + } + } + + if (s == 0 && buf != NULL) + continue; + + if ((cp = realloc(buf, len + s + 1)) == NULL) { + FUNLOCKFILE(fp); + free(buf); + return NULL; + } + buf = cp; + + (void) memcpy(buf + len, ptr, s); + len += s; + buf[len] = '\0'; + } + + FUNLOCKFILE(fp); + + if ((flags & FPARSELN_UNESCALL) != 0 && esc && buf != NULL && + strchr(buf, esc) != NULL) { + ptr = cp = buf; + while (cp[0] != '\0') { + int skipesc; + + while (cp[0] != '\0' && cp[0] != esc) + *ptr++ = *cp++; + if (cp[0] == '\0' || cp[1] == '\0') + break; + + skipesc = 0; + if (cp[1] == com) + skipesc += (flags & FPARSELN_UNESCCOMM); + if (cp[1] == con) + skipesc += (flags & FPARSELN_UNESCCONT); + if (cp[1] == esc) + skipesc += (flags & FPARSELN_UNESCESC); + if (cp[1] != com && cp[1] != con && cp[1] != esc) + skipesc = (flags & FPARSELN_UNESCREST); + + if (skipesc) + cp++; + else + *ptr++ = *cp++; + *ptr++ = *cp++; + } + *ptr = '\0'; + len = strlen(buf); + } + + if (size) + *size = len; + return buf; +} + +#ifdef TEST + +int main(int, char **); + +int +main(int argc, char **argv) +{ + char *ptr; + size_t size, line; + + line = 0; + while ((ptr = fparseln(stdin, &size, &line, NULL, + FPARSELN_UNESCALL)) != NULL) + printf("line %d (%d) |%s|\n", line, size, ptr); + return 0; +} + +/* + +# This is a test +line 1 +line 2 \ +line 3 # Comment +line 4 \# Not comment \\\\ + +# And a comment \ +line 5 \\\ +line 6 + +*/ + +#endif /* TEST */ +#endif /* ! HAVE_FPARSELN */ diff --git a/compat/getmode.c b/compat/getmode.c new file mode 100644 index 00000000000..6b0f7bada55 --- /dev/null +++ b/compat/getmode.c @@ -0,0 +1,63 @@ +/* $NetBSD: getmode.c,v 1.6 2004/01/13 00:53:06 simonb Exp $ */ + +/*- + * Copyright (c) 2001 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Todd Vierling. + * + * 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 NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 "nbtool_config.h" +#include + +void * +setmode(const char *str) +{ + mode_t *mp = malloc(sizeof(mode_t)); + + *mp = strtoul(str, NULL, 8); + + return mp; +} + +mode_t +getmode(const void *mp, mode_t mode) +{ + mode_t m; + + m = *((const mode_t *)mp); + + mode &= ~ALLPERMS; /* input mode less RWX permissions */ + m &= ALLPERMS; /* new RWX permissions */ + + return m | mode; +} diff --git a/compat/machine/bswap.h b/compat/machine/bswap.h new file mode 100644 index 00000000000..e69de29bb2d diff --git a/compat/mtree/extern.h b/compat/mtree/extern.h new file mode 100644 index 00000000000..85115d44319 --- /dev/null +++ b/compat/mtree/extern.h @@ -0,0 +1,81 @@ +/* $NetBSD: extern.h,v 1.28 2003/10/27 00:12:44 lukem Exp $ */ + +/*- + * Copyright (c) 1991, 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. + * 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. + * + * @(#)extern.h 8.1 (Berkeley) 6/6/93 + */ + +#include "mtree.h" + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#else +#define HAVE_STRUCT_STAT_ST_FLAGS 1 +#endif + +#include +#include + +#if HAVE_NETDB_H +/* For MAXHOSTNAMELEN on some platforms. */ +#include +#endif + +#ifndef MAXHOSTNAMELEN +#define MAXHOSTNAMELEN 256 +#endif + +void addtag(slist_t *, char *); +int check_excludes(const char *, const char *); +int compare(NODE *, FTSENT *); +int crc(int, u_int32_t *, u_int32_t *); +void cwalk(void); +void dump_nodes(const char *, NODE *, int); +void init_excludes(void); +int matchtags(NODE *); +void mtree_err(const char *, ...) + __attribute__((__format__(__printf__, 1, 2))); +const char *nodetype(u_int); +u_int parsekey(const char *, int *); +void parsetags(slist_t *, char *); +u_int parsetype(const char *); +void read_excludes_file(const char *); +const char *rlink(const char *); +int verify(void); + +extern int dflag, eflag, iflag, lflag, mflag, rflag, sflag, tflag, uflag; +extern int Wflag; +extern size_t mtree_lineno; +extern u_int32_t crc_total; +extern int ftsoptions, keys; +extern char fullpath[]; +extern slist_t includetags, excludetags; + + +#include "stat_flags.h" diff --git a/compat/mtree/getid.c b/compat/mtree/getid.c new file mode 100644 index 00000000000..e6ff045c68c --- /dev/null +++ b/compat/mtree/getid.c @@ -0,0 +1,435 @@ +/* $NetBSD: getid.c,v 1.5 2004/06/20 22:20:18 jmc Exp $ */ +/* from: NetBSD: getpwent.c,v 1.48 2000/10/03 03:22:26 enami Exp */ +/* from: NetBSD: getgrent.c,v 1.41 2002/01/12 23:51:30 lukem Exp */ + +/* + * Copyright (c) 1987, 1988, 1989, 1993, 1994, 1995 + * 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. + * 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. + */ + +/*- + * Copyright (c) 2002 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Luke Mewburn of Wasabi Systems. + * + * 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 NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "extern.h" + +static struct group * gi_getgrnam(const char *); +static struct group * gi_getgrgid(gid_t); +static int gi_setgroupent(int); +static void gi_endgrent(void); +static int grstart(void); +static int grscan(int, gid_t, const char *); +static int grmatchline(int, gid_t, const char *); + +static struct passwd * gi_getpwnam(const char *); +static struct passwd * gi_getpwuid(uid_t); +static int gi_setpassent(int); +static void gi_endpwent(void); +static int pwstart(void); +static int pwscan(int, uid_t, const char *); +static int pwmatchline(int, uid_t, const char *); + +#define MAXGRP 200 +#define MAXLINELENGTH 1024 + +static FILE *_gr_fp; +static struct group _gr_group; +static int _gr_stayopen; +static int _gr_filesdone; +static FILE *_pw_fp; +static struct passwd _pw_passwd; /* password structure */ +static int _pw_stayopen; /* keep fd's open */ +static int _pw_filesdone; + +static char grfile[MAXPATHLEN]; +static char pwfile[MAXPATHLEN]; + +static char *members[MAXGRP]; +static char grline[MAXLINELENGTH]; +static char pwline[MAXLINELENGTH]; + +int +setup_getid(const char *dir) +{ + if (dir == NULL) + return (0); + + /* close existing databases */ + gi_endgrent(); + gi_endpwent(); + + /* build paths to new databases */ + snprintf(grfile, sizeof(grfile), "%s/group", dir); + snprintf(pwfile, sizeof(pwfile), "%s/master.passwd", dir); + + /* try to open new databases */ + if (!grstart() || !pwstart()) + return (0); + + /* switch pwcache(3) lookup functions */ + if (pwcache_groupdb(gi_setgroupent, gi_endgrent, + gi_getgrnam, gi_getgrgid) == -1 + || pwcache_userdb(gi_setpassent, gi_endpwent, + gi_getpwnam, gi_getpwuid) == -1) + return (0); + + return (1); +} + + +/* + * group lookup functions + */ + +static struct group * +gi_getgrnam(const char *name) +{ + int rval; + + if (!grstart()) + return NULL; + rval = grscan(1, 0, name); + if (!_gr_stayopen) + endgrent(); + return (rval) ? &_gr_group : NULL; +} + +static struct group * +gi_getgrgid(gid_t gid) +{ + int rval; + + if (!grstart()) + return NULL; + rval = grscan(1, gid, NULL); + if (!_gr_stayopen) + endgrent(); + return (rval) ? &_gr_group : NULL; +} + +static int +gi_setgroupent(int stayopen) +{ + + if (!grstart()) + return 0; + _gr_stayopen = stayopen; + return 1; +} + +static void +gi_endgrent(void) +{ + + _gr_filesdone = 0; + if (_gr_fp) { + (void)fclose(_gr_fp); + _gr_fp = NULL; + } +} + +static int +grstart(void) +{ + + _gr_filesdone = 0; + if (_gr_fp) { + rewind(_gr_fp); + return 1; + } + if (grfile[0] == '\0') /* sanity check */ + return 0; + return (_gr_fp = fopen(grfile, "r")) ? 1 : 0; +} + + +static int +grscan(int search, gid_t gid, const char *name) +{ + + if (_gr_filesdone) + return 0; + for (;;) { + if (!fgets(grline, sizeof(grline), _gr_fp)) { + if (!search) + _gr_filesdone = 1; + return 0; + } + /* skip lines that are too big */ + if (!strchr(grline, '\n')) { + int ch; + + while ((ch = getc(_gr_fp)) != '\n' && ch != EOF) + ; + continue; + } + if (grmatchline(search, gid, name)) + return 1; + } + /* NOTREACHED */ +} + +static int +grmatchline(int search, gid_t gid, const char *name) +{ + unsigned long id; + char **m; + char *cp, *bp, *ep; + + /* name may be NULL if search is nonzero */ + + bp = grline; + memset(&_gr_group, 0, sizeof(_gr_group)); + _gr_group.gr_name = strsep(&bp, ":\n"); + if (search && name && strcmp(_gr_group.gr_name, name)) + return 0; + _gr_group.gr_passwd = strsep(&bp, ":\n"); + if (!(cp = strsep(&bp, ":\n"))) + return 0; + id = strtoul(cp, &ep, 10); + if (id > GID_MAX || *ep != '\0') + return 0; + _gr_group.gr_gid = (gid_t)id; + if (search && name == NULL && _gr_group.gr_gid != gid) + return 0; + cp = NULL; + if (bp == NULL) + return 0; + for (_gr_group.gr_mem = m = members;; bp++) { + if (m == &members[MAXGRP - 1]) + break; + if (*bp == ',') { + if (cp) { + *bp = '\0'; + *m++ = cp; + cp = NULL; + } + } else if (*bp == '\0' || *bp == '\n' || *bp == ' ') { + if (cp) { + *bp = '\0'; + *m++ = cp; + } + break; + } else if (cp == NULL) + cp = bp; + } + *m = NULL; + return 1; +} + + +/* + * user lookup functions + */ + +static struct passwd * +gi_getpwnam(const char *name) +{ + int rval; + + if (!pwstart()) + return NULL; + rval = pwscan(1, 0, name); + if (!_pw_stayopen) + endpwent(); + return (rval) ? &_pw_passwd : NULL; +} + +static struct passwd * +gi_getpwuid(uid_t uid) +{ + int rval; + + if (!pwstart()) + return NULL; + rval = pwscan(1, uid, NULL); + if (!_pw_stayopen) + endpwent(); + return (rval) ? &_pw_passwd : NULL; +} + +static int +gi_setpassent(int stayopen) +{ + + if (!pwstart()) + return 0; + _pw_stayopen = stayopen; + return 1; +} + +static void +gi_endpwent(void) +{ + + _pw_filesdone = 0; + if (_pw_fp) { + (void)fclose(_pw_fp); + _pw_fp = NULL; + } +} + +static int +pwstart(void) +{ + + _pw_filesdone = 0; + if (_pw_fp) { + rewind(_pw_fp); + return 1; + } + if (pwfile[0] == '\0') /* sanity check */ + return 0; + return (_pw_fp = fopen(pwfile, "r")) ? 1 : 0; +} + + +static int +pwscan(int search, uid_t uid, const char *name) +{ + + if (_pw_filesdone) + return 0; + for (;;) { + if (!fgets(pwline, sizeof(pwline), _pw_fp)) { + if (!search) + _pw_filesdone = 1; + return 0; + } + /* skip lines that are too big */ + if (!strchr(pwline, '\n')) { + int ch; + + while ((ch = getc(_pw_fp)) != '\n' && ch != EOF) + ; + continue; + } + if (pwmatchline(search, uid, name)) + return 1; + } + /* NOTREACHED */ +} + +static int +pwmatchline(int search, uid_t uid, const char *name) +{ + unsigned long id; + char *cp, *bp, *ep; + + /* name may be NULL if search is nonzero */ + + bp = pwline; + memset(&_pw_passwd, 0, sizeof(_pw_passwd)); + _pw_passwd.pw_name = strsep(&bp, ":\n"); /* name */ + if (search && name && strcmp(_pw_passwd.pw_name, name)) + return 0; + + _pw_passwd.pw_passwd = strsep(&bp, ":\n"); /* passwd */ + + if (!(cp = strsep(&bp, ":\n"))) /* uid */ + return 0; + id = strtoul(cp, &ep, 10); + if (id > UID_MAX || *ep != '\0') + return 0; + _pw_passwd.pw_uid = (uid_t)id; + if (search && name == NULL && _pw_passwd.pw_uid != uid) + return 0; + + if (!(cp = strsep(&bp, ":\n"))) /* gid */ + return 0; + id = strtoul(cp, &ep, 10); + if (id > GID_MAX || *ep != '\0') + return 0; + _pw_passwd.pw_gid = (gid_t)id; + + if (!(ep = strsep(&bp, ":"))) /* class */ + return 0; + if (!(ep = strsep(&bp, ":"))) /* change */ + return 0; + if (!(ep = strsep(&bp, ":"))) /* expire */ + return 0; + + if (!(_pw_passwd.pw_gecos = strsep(&bp, ":\n"))) /* gecos */ + return 0; + if (!(_pw_passwd.pw_dir = strsep(&bp, ":\n"))) /* directory */ + return 0; + if (!(_pw_passwd.pw_shell = strsep(&bp, ":\n"))) /* shell */ + return 0; + + if (strchr(bp, ':') != NULL) + return 0; + + return 1; +} + diff --git a/compat/mtree/misc.c b/compat/mtree/misc.c new file mode 100644 index 00000000000..1f3e850e1a6 --- /dev/null +++ b/compat/mtree/misc.c @@ -0,0 +1,295 @@ +/* $NetBSD: misc.c,v 1.25 2004/06/20 22:20:18 jmc Exp $ */ + +/*- + * Copyright (c) 1991, 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. + * 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. + * + * @(#)misc.c 8.1 (Berkeley) 6/6/93 + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include +#if defined(__RCSID) && !defined(lint) +__RCSID("$NetBSD: misc.c,v 1.25 2004/06/20 22:20:18 jmc Exp $"); +#endif /* not lint */ + +#include +#include + +#include +#include +#include +#include + +#include "extern.h" + +typedef struct _key { + const char *name; /* key name */ + u_int val; /* value */ + +#define NEEDVALUE 0x01 + u_int flags; +} KEY; + +/* NB: the following tables must be sorted lexically. */ +static KEY keylist[] = { + {"cksum", F_CKSUM, NEEDVALUE}, + {"device", F_DEV, NEEDVALUE}, + {"flags", F_FLAGS, NEEDVALUE}, + {"gid", F_GID, NEEDVALUE}, + {"gname", F_GNAME, NEEDVALUE}, + {"ignore", F_IGN, 0}, + {"link", F_SLINK, NEEDVALUE}, + {"md5", F_MD5, NEEDVALUE}, + {"md5digest", F_MD5, NEEDVALUE}, + {"mode", F_MODE, NEEDVALUE}, + {"nlink", F_NLINK, NEEDVALUE}, + {"optional", F_OPT, 0}, + {"rmd160", F_RMD160, NEEDVALUE}, + {"rmd160digest",F_RMD160, NEEDVALUE}, + {"sha1", F_SHA1, NEEDVALUE}, + {"sha1digest", F_SHA1, NEEDVALUE}, + {"size", F_SIZE, NEEDVALUE}, + {"tags", F_TAGS, NEEDVALUE}, + {"time", F_TIME, NEEDVALUE}, + {"type", F_TYPE, NEEDVALUE}, + {"uid", F_UID, NEEDVALUE}, + {"uname", F_UNAME, NEEDVALUE} +}; + +static KEY typelist[] = { + {"block", F_BLOCK, }, + {"char", F_CHAR, }, + {"dir", F_DIR, }, + {"fifo", F_FIFO, }, + {"file", F_FILE, }, + {"link", F_LINK, }, + {"socket", F_SOCK, }, +}; + +slist_t excludetags, includetags; +int keys = KEYDEFAULT; + + +int keycompare(const void *, const void *); + +u_int +parsekey(const char *name, int *needvaluep) +{ + static int allbits; + KEY *k, tmp; + + if (allbits == 0) { + int i; + + for (i = 0; i < sizeof(keylist) / sizeof(KEY); i++) + allbits |= keylist[i].val; + } + tmp.name = name; + if (strcmp(name, "all") == 0) + return (allbits); + k = (KEY *)bsearch(&tmp, keylist, sizeof(keylist) / sizeof(KEY), + sizeof(KEY), keycompare); + if (k == NULL) + mtree_err("unknown keyword `%s'", name); + + if (needvaluep) + *needvaluep = k->flags & NEEDVALUE ? 1 : 0; + + return (k->val); +} + +u_int +parsetype(const char *name) +{ + KEY *k, tmp; + + tmp.name = name; + k = (KEY *)bsearch(&tmp, typelist, sizeof(typelist) / sizeof(KEY), + sizeof(KEY), keycompare); + if (k == NULL) + mtree_err("unknown file type `%s'", name); + + return (k->val); +} + +int +keycompare(const void *a, const void *b) +{ + + return (strcmp(((const KEY *)a)->name, ((const KEY *)b)->name)); +} + +void +mtree_err(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vwarnx(fmt, ap); + va_end(ap); + if (mtree_lineno) + warnx("failed at line %lu of the specification", + (u_long) mtree_lineno); + exit(1); + /* NOTREACHED */ +} + +void +addtag(slist_t *list, char *elem) +{ + +#define TAG_CHUNK 20 + + if ((list->count % TAG_CHUNK) == 0) { + char **new; + + new = (char **)realloc(list->list, (list->count + TAG_CHUNK) + * sizeof(char *)); + if (new == NULL) + mtree_err("memory allocation error"); + list->list = new; + } + list->list[list->count] = elem; + list->count++; +} + +void +parsetags(slist_t *list, char *args) +{ + char *p, *e; + int len; + + if (args == NULL) { + addtag(list, NULL); + return; + } + while ((p = strsep(&args, ",")) != NULL) { + if (*p == '\0') + continue; + len = strlen(p) + 3; /* "," + p + ",\0" */ + if ((e = malloc(len)) == NULL) + mtree_err("memory allocation error"); + snprintf(e, len, ",%s,", p); + addtag(list, e); + } +} + +/* + * matchtags + * returns 0 if there's a match from the exclude list in the node's tags, + * or there's an include list and no match. + * return 1 otherwise. + */ +int +matchtags(NODE *node) +{ + int i; + + if (node->tags) { + for (i = 0; i < excludetags.count; i++) + if (strstr(node->tags, excludetags.list[i])) + break; + if (i < excludetags.count) + return (0); + + for (i = 0; i < includetags.count; i++) + if (strstr(node->tags, includetags.list[i])) + break; + if (i > 0 && i == includetags.count) + return (0); + } else if (includetags.count > 0) { + return (0); + } + return (1); +} + +u_int +nodetoino(u_int type) +{ + + switch (type) { + case F_BLOCK: + return S_IFBLK; + case F_CHAR: + return S_IFCHR; + case F_DIR: + return S_IFDIR; + case F_FIFO: + return S_IFIFO; + case F_FILE: + return S_IFREG; + case F_LINK: + return S_IFLNK; +#ifdef S_IFSOCK + case F_SOCK: + return S_IFSOCK; +#endif + default: + printf("unknown type %d", type); + abort(); + } + /* NOTREACHED */ +} + +const char * +nodetype(u_int type) +{ + + return (inotype(nodetoino(type))); +} + + +const char * +inotype(u_int type) +{ + + switch (type & S_IFMT) { + case S_IFBLK: + return ("block"); + case S_IFCHR: + return ("char"); + case S_IFDIR: + return ("dir"); + case S_IFIFO: + return ("fifo"); + case S_IFREG: + return ("file"); + case S_IFLNK: + return ("link"); +#ifdef S_IFSOCK + case S_IFSOCK: + return ("socket"); +#endif + default: + return ("unknown"); + } + /* NOTREACHED */ +} diff --git a/compat/mtree/mtree.h b/compat/mtree/mtree.h new file mode 100644 index 00000000000..59a0cc977fd --- /dev/null +++ b/compat/mtree/mtree.h @@ -0,0 +1,129 @@ +/* $NetBSD: mtree.h,v 1.21 2003/08/07 11:25:36 agc Exp $ */ + +/*- + * 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. + * 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. + * + * @(#)mtree.h 8.1 (Berkeley) 6/6/93 + */ + +#ifndef _MTREE_H_ +#define _MTREE_H_ + +#define KEYDEFAULT (F_GID | F_MODE | F_NLINK | F_SIZE | F_SLINK | \ + F_TIME | F_TYPE | F_UID | F_FLAGS) + +#define MISMATCHEXIT 2 + +typedef struct _node { + struct _node *parent, *child; /* up, down */ + struct _node *prev, *next; /* left, right */ + off_t st_size; /* size */ + struct timespec st_mtimespec; /* last modification time */ + char *slink; /* symbolic link reference */ + uid_t st_uid; /* uid */ + gid_t st_gid; /* gid */ +#define MBITS (S_ISUID|S_ISGID|S_ISTXT|S_IRWXU|S_IRWXG|S_IRWXO) + mode_t st_mode; /* mode */ + dev_t st_rdev; /* device type */ + u_long st_flags; /* flags */ + nlink_t st_nlink; /* link count */ + u_long cksum; /* check sum */ + char *md5digest; /* MD5 digest */ + char *rmd160digest; /* RMD-160 digest */ + char *sha1digest; /* SHA1 digest */ + char *tags; /* tags, comma delimited */ + size_t lineno; /* line # entry came from */ + +#define F_CKSUM 0x00000001 /* cksum(1) check sum */ +#define F_DEV 0x00000002 /* device type */ +#define F_DONE 0x00000004 /* directory done */ +#define F_FLAGS 0x00000008 /* file flags */ +#define F_GID 0x00000010 /* gid */ +#define F_GNAME 0x00000020 /* group name */ +#define F_IGN 0x00000040 /* ignore */ +#define F_MAGIC 0x00000080 /* name has magic chars */ +#define F_MD5 0x00000100 /* MD5 digest */ +#define F_MODE 0x00000200 /* mode */ +#define F_NLINK 0x00000400 /* number of links */ +#define F_OPT 0x00000800 /* existence optional */ +#define F_RMD160 0x00001000 /* RMD-160 digest */ +#define F_SHA1 0x00002000 /* SHA1 digest */ +#define F_SIZE 0x00004000 /* size */ +#define F_SLINK 0x00008000 /* symbolic link */ +#define F_TAGS 0x00010000 /* tags */ +#define F_TIME 0x00020000 /* modification time */ +#define F_TYPE 0x00040000 /* file type */ +#define F_UID 0x00080000 /* uid */ +#define F_UNAME 0x00100000 /* user name */ +#define F_VISIT 0x00200000 /* file visited */ + + int flags; /* items set */ + +#define F_BLOCK 0x001 /* block special */ +#define F_CHAR 0x002 /* char special */ +#define F_DIR 0x004 /* directory */ +#define F_FIFO 0x008 /* fifo */ +#define F_FILE 0x010 /* regular file */ +#define F_LINK 0x020 /* symbolic link */ +#define F_SOCK 0x040 /* socket */ + int type; /* file type */ + + char name[1]; /* file name (must be last) */ +} NODE; + + +typedef struct { + char **list; + int count; +} slist_t; + + +/* + * prototypes for functions published to other programs which want to use + * the specfile parser but don't want to pull in all of "extern.h" + */ +const char *inotype(u_int); +u_int nodetoino(u_int); +int setup_getid(const char *); +NODE *spec(FILE *); +char *vispath(const char *); + + +#define RP(p) \ + ((p)->fts_path[0] == '.' && (p)->fts_path[1] == '/' ? \ + (p)->fts_path + 2 : (p)->fts_path) + +#define UF_MASK ((UF_NODUMP | UF_IMMUTABLE | \ + UF_APPEND | UF_OPAQUE) \ + & UF_SETTABLE) /* user settable flags */ +#define SF_MASK ((SF_ARCHIVED | SF_IMMUTABLE | \ + SF_APPEND) & SF_SETTABLE) /* root settable flags */ +#define CH_MASK (UF_MASK | SF_MASK) /* all settable flags */ +#define SP_FLGS (SF_IMMUTABLE | SF_APPEND) /* special flags */ + +#endif /* _MTREE_H_ */ diff --git a/compat/mtree/spec.c b/compat/mtree/spec.c new file mode 100644 index 00000000000..58d7c5f698b --- /dev/null +++ b/compat/mtree/spec.c @@ -0,0 +1,593 @@ +/* $NetBSD: spec.c,v 1.56 2004/06/20 22:20:18 jmc Exp $ */ + +/*- + * Copyright (c) 1989, 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. + * 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. + */ + +/*- + * Copyright (c) 2001-2002 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Luke Mewburn of Wasabi Systems. + * + * 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 NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include +#if defined(__RCSID) && !defined(lint) +#if 0 +static char sccsid[] = "@(#)spec.c 8.2 (Berkeley) 4/28/95"; +#else +__RCSID("$NetBSD: spec.c,v 1.56 2004/06/20 22:20:18 jmc Exp $"); +#endif +#endif /* not lint */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "extern.h" +#include "pack_dev.h" + +size_t mtree_lineno; /* Current spec line number */ +int Wflag; /* Don't "whack" permissions */ + +static dev_t parsedev(char *); +static void replacenode(NODE *, NODE *); +static void set(char *, NODE *); +static void unset(char *, NODE *); + +NODE * +spec(FILE *fp) +{ + NODE *centry, *last, *pathparent, *cur; + char *p, *e, *next; + NODE ginfo, *root; + char *buf, *tname, *ntname; + size_t tnamelen, plen; + + root = NULL; + centry = last = NULL; + tname = NULL; + tnamelen = 0; + memset(&ginfo, 0, sizeof(ginfo)); + for (mtree_lineno = 0; + (buf = fparseln(fp, NULL, &mtree_lineno, NULL, + FPARSELN_UNESCCOMM | FPARSELN_UNESCCONT | FPARSELN_UNESCESC)); + free(buf)) { + /* Skip leading whitespace. */ + for (p = buf; *p && isspace((unsigned char)*p); ++p) + continue; + + /* If nothing but whitespace, continue. */ + if (!*p) + continue; + +#ifdef DEBUG + fprintf(stderr, "line %lu: {%s}\n", + (u_long)mtree_lineno, p); +#endif + /* Grab file name, "$", "set", or "unset". */ + next = buf; + while ((p = strsep(&next, " \t")) != NULL && *p == '\0') + continue; + if (p == NULL) + mtree_err("missing field"); + + if (p[0] == '/') { + if (strcmp(p + 1, "set") == 0) + set(next, &ginfo); + else if (strcmp(p + 1, "unset") == 0) + unset(next, &ginfo); + else + mtree_err("invalid specification `%s'", p); + continue; + } + + if (strcmp(p, "..") == 0) { + /* Don't go up, if haven't gone down. */ + if (root == NULL) + goto noparent; + if (last->type != F_DIR || last->flags & F_DONE) { + if (last == root) + goto noparent; + last = last->parent; + } + last->flags |= F_DONE; + continue; + +noparent: mtree_err("no parent node"); + } + + plen = strlen(p) + 1; + if (plen > tnamelen) { + if ((ntname = realloc(tname, plen)) == NULL) + mtree_err("realloc: %s", strerror(errno)); + tname = ntname; + tnamelen = plen; + } + if (strunvis(tname, p) == -1) + mtree_err("strunvis failed on `%s'", p); + p = tname; + + pathparent = NULL; + if (strchr(p, '/') != NULL) { + cur = root; + for (; (e = strchr(p, '/')) != NULL; p = e+1) { + if (p == e) + continue; /* handle // */ + *e = '\0'; + if (strcmp(p, ".") != 0) { + while (cur && + strcmp(cur->name, p) != 0) { + cur = cur->next; + } + } + if (cur == NULL || cur->type != F_DIR) { + mtree_err("%s: %s", tname, + strerror(ENOENT)); + } + *e = '/'; + pathparent = cur; + cur = cur->child; + } + if (*p == '\0') + mtree_err("%s: empty leaf element", tname); + } + + if ((centry = calloc(1, sizeof(NODE) + strlen(p))) == NULL) + mtree_err("%s", strerror(errno)); + *centry = ginfo; + centry->lineno = mtree_lineno; + strcpy(centry->name, p); +#define MAGIC "?*[" + if (strpbrk(p, MAGIC)) + centry->flags |= F_MAGIC; + set(next, centry); + + if (root == NULL) { + /* + * empty tree + */ + if (strcmp(centry->name, ".") != 0 || + centry->type != F_DIR) + mtree_err( + "root node must be the directory `.'"); + last = root = centry; + root->parent = root; + } else if (pathparent != NULL) { + /* + * full path entry + */ + centry->parent = pathparent; + cur = pathparent->child; + if (cur == NULL) { + pathparent->child = centry; + last = centry; + } else { + for (; cur != NULL; cur = cur->next) { + if (strcmp(cur->name, centry->name) + == 0) { + /* existing entry; replace */ + replacenode(cur, centry); + break; + } + if (cur->next == NULL) { + /* last entry; add new */ + cur->next = centry; + centry->prev = cur; + break; + } + } + last = cur; + while (last->next != NULL) + last = last->next; + } + } else if (strcmp(centry->name, ".") == 0) { + /* + * duplicate "." entry; always replace + */ + replacenode(root, centry); + } else if (last->type == F_DIR && !(last->flags & F_DONE)) { + /* + * new relative child + * (no duplicate check) + */ + centry->parent = last; + last = last->child = centry; + } else { + /* + * relative entry, up one directory + * (no duplicate check) + */ + centry->parent = last->parent; + centry->prev = last; + last = last->next = centry; + } + } + return (root); +} + +/* + * dump_nodes -- + * dump the NODEs from `cur', based in the directory `dir'. + * if pathlast is none zero, print the path last, otherwise print + * it first. + */ +void +dump_nodes(const char *dir, NODE *root, int pathlast) +{ + NODE *cur; + char path[MAXPATHLEN]; + const char *name; + + for (cur = root; cur != NULL; cur = cur->next) { + if (cur->type != F_DIR && !matchtags(cur)) + continue; + + if (snprintf(path, sizeof(path), "%s%s%s", + dir, *dir ? "/" : "", cur->name) + >= sizeof(path)) + mtree_err("Pathname too long."); + + if (!pathlast) + printf("%s ", vispath(path)); + +#define MATCHFLAG(f) ((keys & (f)) && (cur->flags & (f))) + if (MATCHFLAG(F_TYPE)) + printf("type=%s ", nodetype(cur->type)); + if (MATCHFLAG(F_UID | F_UNAME)) { + if (keys & F_UNAME && + (name = user_from_uid(cur->st_uid, 1)) != NULL) + printf("uname=%s ", name); + else + printf("uid=%u ", cur->st_uid); + } + if (MATCHFLAG(F_GID | F_GNAME)) { + if (keys & F_GNAME && + (name = group_from_gid(cur->st_gid, 1)) != NULL) + printf("gname=%s ", name); + else + printf("gid=%u ", cur->st_gid); + } + if (MATCHFLAG(F_MODE)) + printf("mode=%#o ", cur->st_mode); + if (MATCHFLAG(F_DEV) && + (cur->type == F_BLOCK || cur->type == F_CHAR)) + printf("device=%#x ", cur->st_rdev); + if (MATCHFLAG(F_NLINK)) + printf("nlink=%d ", cur->st_nlink); + if (MATCHFLAG(F_SLINK)) + printf("link=%s ", cur->slink); + if (MATCHFLAG(F_SIZE)) + printf("size=%lld ", (long long)cur->st_size); + if (MATCHFLAG(F_TIME)) + printf("time=%ld.%ld ", (long)cur->st_mtimespec.tv_sec, + cur->st_mtimespec.tv_nsec); + if (MATCHFLAG(F_CKSUM)) + printf("cksum=%lu ", cur->cksum); + if (MATCHFLAG(F_MD5)) + printf("md5=%s ", cur->md5digest); + if (MATCHFLAG(F_RMD160)) + printf("rmd160=%s ", cur->rmd160digest); + if (MATCHFLAG(F_SHA1)) + printf("sha1=%s ", cur->sha1digest); + if (MATCHFLAG(F_FLAGS)) + printf("flags=%s ", + flags_to_string(cur->st_flags, "none")); + if (MATCHFLAG(F_IGN)) + printf("ignore "); + if (MATCHFLAG(F_OPT)) + printf("optional "); + if (MATCHFLAG(F_TAGS)) + printf("tags=%s ", cur->tags); + puts(pathlast ? vispath(path) : ""); + + if (cur->child) + dump_nodes(path, cur->child, pathlast); + } +} + +/* + * vispath -- + * strsvis(3) encodes path, which must not be longer than MAXPATHLEN + * characters long, and returns a pointer to a static buffer containing + * the result. + */ +char * +vispath(const char *path) +{ + const char extra[] = { ' ', '\t', '\n', '\\', '#', '\0' }; + static char pathbuf[4*MAXPATHLEN + 1]; + + strsvis(pathbuf, path, VIS_CSTYLE, extra); + return(pathbuf); +} + + +static dev_t +parsedev(char *arg) +{ +#define MAX_PACK_ARGS 3 + u_long numbers[MAX_PACK_ARGS]; + char *p, *ep, *dev; + int argc; + pack_t *pack; + dev_t result; + const char *error = NULL; + + if ((dev = strchr(arg, ',')) != NULL) { + *dev++='\0'; + if ((pack = pack_find(arg)) == NULL) + mtree_err("unknown format `%s'", arg); + argc = 0; + while ((p = strsep(&dev, ",")) != NULL) { + if (*p == '\0') + mtree_err("missing number"); + numbers[argc++] = strtoul(p, &ep, 0); + if (*ep != '\0') + mtree_err("invalid number `%s'", + p); + if (argc > MAX_PACK_ARGS) + mtree_err("too many arguments"); + } + if (argc < 2) + mtree_err("not enough arguments"); + result = (*pack)(argc, numbers, &error); + if (error != NULL) + mtree_err(error); + } else { + result = (dev_t)strtoul(arg, &ep, 0); + if (*ep != '\0') + mtree_err("invalid device `%s'", arg); + } + return (result); +} + +static void +replacenode(NODE *cur, NODE *new) +{ + + if (cur->type != new->type) + mtree_err("existing entry for `%s', type `%s' does not match type `%s'", + cur->name, nodetype(cur->type), nodetype(new->type)); +#define REPLACE(x) cur->x = new->x +#define REPLACESTR(x) if (cur->x) free(cur->x); cur->x = new->x + + REPLACE(st_size); + REPLACE(st_mtimespec); + REPLACESTR(slink); + REPLACE(st_uid); + REPLACE(st_gid); + REPLACE(st_mode); + REPLACE(st_rdev); + REPLACE(st_flags); + REPLACE(st_nlink); + REPLACE(cksum); + REPLACESTR(md5digest); + REPLACESTR(rmd160digest); + REPLACESTR(sha1digest); + REPLACESTR(tags); + REPLACE(lineno); + REPLACE(flags); + free(new); +} + +static void +set(char *t, NODE *ip) +{ + int type, value, len; + gid_t gid; + uid_t uid; + char *kw, *val, *md, *ep; + void *m; + + val = NULL; + while ((kw = strsep(&t, "= \t")) != NULL) { + if (*kw == '\0') + continue; + if (strcmp(kw, "all") == 0) + mtree_err("invalid keyword `all'"); + ip->flags |= type = parsekey(kw, &value); + if (value) { + while ((val = strsep(&t, " \t")) != NULL && + *val == '\0') + continue; + if (val == NULL) + mtree_err("missing value"); + } + switch(type) { + case F_CKSUM: + ip->cksum = strtoul(val, &ep, 10); + if (*ep) + mtree_err("invalid checksum `%s'", val); + break; + case F_DEV: + ip->st_rdev = parsedev(val); + break; + case F_FLAGS: + if (strcmp("none", val) == 0) + ip->st_flags = 0; + else if (string_to_flags(&val, &ip->st_flags, NULL) + != 0) + mtree_err("invalid flag `%s'", val); + break; + case F_GID: + ip->st_gid = (gid_t)strtoul(val, &ep, 10); + if (*ep) + mtree_err("invalid gid `%s'", val); + break; + case F_GNAME: + if (Wflag) /* don't parse if whacking */ + break; + if (gid_from_group(val, &gid) == -1) + mtree_err("unknown group `%s'", val); + ip->st_gid = gid; + break; + case F_IGN: + /* just set flag bit */ + break; + case F_MD5: + if (val[0]=='0' && val[1]=='x') + md=&val[2]; + else + md=val; + if ((ip->md5digest = strdup(md)) == NULL) + mtree_err("memory allocation error"); + break; + case F_MODE: + if ((m = setmode(val)) == NULL) + mtree_err("invalid file mode `%s'", val); + ip->st_mode = getmode(m, 0); + free(m); + break; + case F_NLINK: + ip->st_nlink = (nlink_t)strtoul(val, &ep, 10); + if (*ep) + mtree_err("invalid link count `%s'", val); + break; + case F_OPT: + /* just set flag bit */ + break; + case F_RMD160: + if (val[0]=='0' && val[1]=='x') + md=&val[2]; + else + md=val; + if ((ip->rmd160digest = strdup(md)) == NULL) + mtree_err("memory allocation error"); + break; + case F_SHA1: + if (val[0]=='0' && val[1]=='x') + md=&val[2]; + else + md=val; + if ((ip->sha1digest = strdup(md)) == NULL) + mtree_err("memory allocation error"); + break; + case F_SIZE: + ip->st_size = (off_t)strtoll(val, &ep, 10); + if (*ep) + mtree_err("invalid size `%s'", val); + break; + case F_SLINK: + if ((ip->slink = strdup(val)) == NULL) + mtree_err("memory allocation error"); + break; + case F_TAGS: + len = strlen(val) + 3; /* "," + str + ",\0" */ + if ((ip->tags = malloc(len)) == NULL) + mtree_err("memory allocation error"); + snprintf(ip->tags, len, ",%s,", val); + break; + case F_TIME: + ip->st_mtimespec.tv_sec = + (time_t)strtoul(val, &ep, 10); + if (*ep != '.') + mtree_err("invalid time `%s'", val); + val = ep + 1; + ip->st_mtimespec.tv_nsec = strtoul(val, &ep, 10); + if (*ep) + mtree_err("invalid time `%s'", val); + break; + case F_TYPE: + ip->type = parsetype(val); + break; + case F_UID: + ip->st_uid = (uid_t)strtoul(val, &ep, 10); + if (*ep) + mtree_err("invalid uid `%s'", val); + break; + case F_UNAME: + if (Wflag) /* don't parse if whacking */ + break; + if (uid_from_user(val, &uid) == -1) + mtree_err("unknown user `%s'", val); + ip->st_uid = uid; + break; + default: + mtree_err( + "set(): unsupported key type 0x%x (INTERNAL ERROR)", + type); + /* NOTREACHED */ + } + } +} + +static void +unset(char *t, NODE *ip) +{ + char *p; + + while ((p = strsep(&t, " \t")) != NULL) { + if (*p == '\0') + continue; + ip->flags &= ~parsekey(p, NULL); + } +} diff --git a/compat/namespace.h b/compat/namespace.h new file mode 100644 index 00000000000..0e6a6f257dc --- /dev/null +++ b/compat/namespace.h @@ -0,0 +1,14 @@ +/* $NetBSD: namespace.h,v 1.3 2003/10/27 00:12:43 lukem Exp $ */ + +/* + * Mainly empty header to make reachover bits of libc happy. + * + * Since all reachover bits will include this, it's a good place to pull + * in nbtool_config.h. + */ +#include "nbtool_config.h" + +/* No aliases in reachover-based libc sources. */ +#undef __indr_reference +#undef __weak_alias +#undef __warn_references diff --git a/compat/nbtool_config.h b/compat/nbtool_config.h new file mode 100644 index 00000000000..676ae03b621 --- /dev/null +++ b/compat/nbtool_config.h @@ -0,0 +1,137 @@ +/* nbtool_config.h. Generated automatically by configure. */ +/* $NetBSD: nbtool_config.h.in,v 1.4 2004/06/20 22:20:15 jmc Exp $ */ + +#ifndef __NETBSD_NBTOOL_CONFIG_H__ +#define __NETBSD_NBTOOL_CONFIG_H__ + +/* Values set by "configure" based on available functions in the host. */ + +#define PATH_BSHELL "/bin/sh" + +/* #undef HAVE_ALLOCA_H */ +#define HAVE_DIRENT_H 1 +#define HAVE_ERR_H 1 +/* #undef HAVE_FEATURES_H */ +#define HAVE_GETOPT_H 1 +#define HAVE_INTTYPES_H 1 +#define HAVE_LIBGEN_H 1 +/* #undef HAVE_NDIR_H */ +#define HAVE_NETDB_H 1 +/* #undef HAVE_MACHINE_BSWAP_H */ +/* #undef HAVE_MALLOC_H */ +#define HAVE_SYS_POLL_H 1 +#define HAVE_STDDEF_H 1 +#define HAVE_STRING_H 1 +/* #undef HAVE_SYS_DIR_H */ +#define HAVE_SYS_ENDIAN_H 1 +/* #undef HAVE_SYS_NDIR_H */ +/* #undef HAVE_SYS_SYSLIMITS_H */ +/* #undef HAVE_SYS_SYSMACROS_H */ +#define HAVE_SYS_TYPES_H 1 +#define HAVE_TERMIOS_H 1 +#define HAVE_UNISTD_H 1 +#define STDC_HEADERS 1 + +#define HAVE_ID_T 1 +#define HAVE_SOCKLEN_T 1 +#define HAVE_LONG_LONG 1 +#define HAVE_U_LONG 1 +#define HAVE_U_CHAR 1 +#define HAVE_U_INT 1 +#define HAVE_U_SHORT 1 +#define HAVE_U_QUAD_T 1 + +/* #undef HAVE_BSWAP16 */ +/* #undef HAVE_BSWAP32 */ +/* #undef HAVE_BSWAP64 */ +/* #undef HAVE_HTOBE16 */ +/* #undef HAVE_HTOBE32 */ +/* #undef HAVE_HTOBE64 */ +/* #undef HAVE_HTOLE16 */ +/* #undef HAVE_HTOLE32 */ +/* #undef HAVE_HTOLE64 */ +/* #undef HAVE_BE16TOH */ +/* #undef HAVE_BE32TOH */ +/* #undef HAVE_BE64TOH */ +/* #undef HAVE_LE16TOH */ +/* #undef HAVE_LE32TOH */ +/* #undef HAVE_LE64TOH */ + +#define HAVE_DIR_DD_FD 1 +#define HAVE_STRUCT_DIRENT_D_NAMLEN 1 +#define HAVE_STRUCT_STAT_ST_FLAGS 1 +#define HAVE_STRUCT_STAT_ST_GEN 1 +#define HAVE_STRUCT_STAT_ST_BIRTHTIME 1 +/* #undef HAVE_STRUCT_STAT_ST_ATIM */ +/* #undef HAVE_STRUCT_STAT_ST_MTIMENSEC */ +#define HAVE_STRUCT_STATFS_F_IOSIZE 1 + +#define HAVE_DECL_OPTIND 1 +#define HAVE_DECL_OPTRESET 1 +#define HAVE_DECL_SYS_SIGNAME 1 + +#define HAVE_ASPRINTF 1 +/* #undef HAVE_ASNPRINTF */ +#define HAVE_BASENAME 1 +/* #undef HAVE_CGETNEXT */ +#define HAVE_DEVNAME 1 +/* #undef HAVE_DIRFD */ +#define HAVE_DIRNAME 1 +#define HAVE_FGETLN 1 +#define HAVE_FLOCK 1 +/* #undef HAVE_FPARSELN */ +#define HAVE_FUTIMES 1 +#define HAVE_GETOPT 1 +#define HAVE_GETOPT_LONG 1 +#define HAVE_GROUP_FROM_GID 1 +#define HAVE_ISBLANK 1 +#define HAVE_ISSETUGID 1 +#define HAVE_LCHFLAGS 1 +#define HAVE_LCHMOD 1 +#define HAVE_LCHOWN 1 +#define HAVE_LUTIMES 1 +#define HAVE_MKSTEMP 1 +#define HAVE_MKDTEMP 1 +#define HAVE_POLL 1 +#define HAVE_PREAD 1 +#define HAVE_PUTC_UNLOCKED 1 +/* #undef HAVE_PWCACHE_USERDB */ +#define HAVE_PWRITE 1 +#define HAVE_RANDOM 1 +#define HAVE_SETENV 1 +#define HAVE_SETGROUPENT 1 +#define HAVE_SETPASSENT 1 +#define HAVE_SETPROGNAME 1 +#define HAVE_SNPRINTF 1 +#define HAVE_STRLCAT 1 +#define HAVE_STRLCPY 1 +#define HAVE_STRSEP 1 +/* #undef HAVE_STRSUFTOULL */ +#define HAVE_STRTOLL 1 +#define HAVE_USER_FROM_UID 1 +#define HAVE_VASPRINTF 1 +/* #undef HAVE_VASNPRINTF */ +#define HAVE_VSNPRINTF 1 + +#define HAVE_DECL_SETGROUPENT 1 +#define HAVE_DECL_SETPASSENT 1 + +/* #undef WORDS_BIGENDIAN */ + +/* Typedefs that might be missing. */ + +/* #undef size_t */ +/* #undef uint8_t */ +/* #undef uint16_t */ +/* #undef uint32_t */ +/* #undef uint64_t */ +/* #undef u_int8_t */ +/* #undef u_int16_t */ +/* #undef u_int32_t */ +/* #undef u_int64_t */ + +/* Now pull in the compatibility definitions. */ + +#include "compat_defs.h" + +#endif /* !__NETBSD_NBTOOL_CONFIG_H__ */ diff --git a/compat/pack_dev.c b/compat/pack_dev.c new file mode 100644 index 00000000000..57faa94a7a0 --- /dev/null +++ b/compat/pack_dev.c @@ -0,0 +1,297 @@ +/* $NetBSD: pack_dev.c,v 1.8 2004/05/11 17:09:58 christos Exp $ */ + +/*- + * Copyright (c) 1998, 2001 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Charles M. Hannum. + * + * 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 NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include +#if !defined(lint) +__RCSID("$NetBSD: pack_dev.c,v 1.8 2004/05/11 17:09:58 christos Exp $"); +#endif /* not lint */ + +#include +#include + +#include +#include +#include +#include +#include + +#include "pack_dev.h" + +static pack_t pack_netbsd; +static pack_t pack_freebsd; +static pack_t pack_8_8; +static pack_t pack_12_20; +static pack_t pack_14_18; +static pack_t pack_8_24; +static pack_t pack_bsdos; +static int compare_format(const void *, const void *); + +static const char iMajorError[] = "invalid major number"; +static const char iMinorError[] = "invalid minor number"; +static const char tooManyFields[] = "too many fields for format"; + + /* exported */ +portdev_t +pack_native(int n, u_long numbers[], const char **error) +{ + portdev_t dev = 0; + + if (n == 2) { + dev = makedev(numbers[0], numbers[1]); + if (major(dev) != numbers[0]) + *error = iMajorError; + else if (minor(dev) != numbers[1]) + *error = iMinorError; + } else + *error = tooManyFields; + return (dev); +} + + +static portdev_t +pack_netbsd(int n, u_long numbers[], const char **error) +{ + portdev_t dev = 0; + + if (n == 2) { + dev = makedev_netbsd(numbers[0], numbers[1]); + if (major_netbsd(dev) != numbers[0]) + *error = iMajorError; + else if (minor_netbsd(dev) != numbers[1]) + *error = iMinorError; + } else + *error = tooManyFields; + return (dev); +} + + +#define major_freebsd(x) ((int32_t)(((x) & 0x0000ff00) >> 8)) +#define minor_freebsd(x) ((int32_t)(((x) & 0xffff00ff) >> 0)) +#define makedev_freebsd(x,y) ((portdev_t)((((x) << 8) & 0x0000ff00) | \ + (((y) << 0) & 0xffff00ff))) + +static portdev_t +pack_freebsd(int n, u_long numbers[], const char **error) +{ + portdev_t dev = 0; + + if (n == 2) { + dev = makedev_freebsd(numbers[0], numbers[1]); + if (major_freebsd(dev) != numbers[0]) + *error = iMajorError; + if (minor_freebsd(dev) != numbers[1]) + *error = iMinorError; + } else + *error = tooManyFields; + return (dev); +} + + +#define major_8_8(x) ((int32_t)(((x) & 0x0000ff00) >> 8)) +#define minor_8_8(x) ((int32_t)(((x) & 0x000000ff) >> 0)) +#define makedev_8_8(x,y) ((portdev_t)((((x) << 8) & 0x0000ff00) | \ + (((y) << 0) & 0x000000ff))) + +static portdev_t +pack_8_8(int n, u_long numbers[], const char **error) +{ + portdev_t dev = 0; + + if (n == 2) { + dev = makedev_8_8(numbers[0], numbers[1]); + if (major_8_8(dev) != numbers[0]) + *error = iMajorError; + if (minor_8_8(dev) != numbers[1]) + *error = iMinorError; + } else + *error = tooManyFields; + return (dev); +} + + +#define major_12_20(x) ((int32_t)(((x) & 0xfff00000) >> 20)) +#define minor_12_20(x) ((int32_t)(((x) & 0x000fffff) >> 0)) +#define makedev_12_20(x,y) ((portdev_t)((((x) << 20) & 0xfff00000) | \ + (((y) << 0) & 0x000fffff))) + +static portdev_t +pack_12_20(int n, u_long numbers[], const char **error) +{ + portdev_t dev = 0; + + if (n == 2) { + dev = makedev_12_20(numbers[0], numbers[1]); + if (major_12_20(dev) != numbers[0]) + *error = iMajorError; + if (minor_12_20(dev) != numbers[1]) + *error = iMinorError; + } else + *error = tooManyFields; + return (dev); +} + + +#define major_14_18(x) ((int32_t)(((x) & 0xfffc0000) >> 18)) +#define minor_14_18(x) ((int32_t)(((x) & 0x0003ffff) >> 0)) +#define makedev_14_18(x,y) ((portdev_t)((((x) << 18) & 0xfffc0000) | \ + (((y) << 0) & 0x0003ffff))) + +static portdev_t +pack_14_18(int n, u_long numbers[], const char **error) +{ + portdev_t dev = 0; + + if (n == 2) { + dev = makedev_14_18(numbers[0], numbers[1]); + if (major_14_18(dev) != numbers[0]) + *error = iMajorError; + if (minor_14_18(dev) != numbers[1]) + *error = iMinorError; + } else + *error = tooManyFields; + return (dev); +} + + +#define major_8_24(x) ((int32_t)(((x) & 0xff000000) >> 24)) +#define minor_8_24(x) ((int32_t)(((x) & 0x00ffffff) >> 0)) +#define makedev_8_24(x,y) ((portdev_t)((((x) << 24) & 0xff000000) | \ + (((y) << 0) & 0x00ffffff))) + +static portdev_t +pack_8_24(int n, u_long numbers[], const char **error) +{ + portdev_t dev = 0; + + if (n == 2) { + dev = makedev_8_24(numbers[0], numbers[1]); + if (major_8_24(dev) != numbers[0]) + *error = iMajorError; + if (minor_8_24(dev) != numbers[1]) + *error = iMinorError; + } else + *error = tooManyFields; + return (dev); +} + + +#define major_12_12_8(x) ((int32_t)(((x) & 0xfff00000) >> 20)) +#define unit_12_12_8(x) ((int32_t)(((x) & 0x000fff00) >> 8)) +#define subunit_12_12_8(x) ((int32_t)(((x) & 0x000000ff) >> 0)) +#define makedev_12_12_8(x,y,z) ((portdev_t)((((x) << 20) & 0xfff00000) | \ + (((y) << 8) & 0x000fff00) | \ + (((z) << 0) & 0x000000ff))) + +static portdev_t +pack_bsdos(int n, u_long numbers[], const char **error) +{ + portdev_t dev = 0; + + if (n == 2) { + dev = makedev_12_20(numbers[0], numbers[1]); + if (major_12_20(dev) != numbers[0]) + *error = iMajorError; + if (minor_12_20(dev) != numbers[1]) + *error = iMinorError; + } else if (n == 3) { + dev = makedev_12_12_8(numbers[0], numbers[1], numbers[2]); + if (major_12_12_8(dev) != numbers[0]) + *error = iMajorError; + if (unit_12_12_8(dev) != numbers[1]) + *error = "invalid unit number"; + if (subunit_12_12_8(dev) != numbers[2]) + *error = "invalid subunit number"; + } else + *error = tooManyFields; + return (dev); +} + + + /* list of formats and pack functions */ + /* this list must be sorted lexically */ +struct format { + const char *name; + pack_t *pack; +} formats[] = { + {"386bsd", pack_8_8}, + {"4bsd", pack_8_8}, + {"bsdos", pack_bsdos}, + {"freebsd", pack_freebsd}, + {"hpux", pack_8_24}, + {"isc", pack_8_8}, + {"linux", pack_8_8}, + {"native", pack_native}, + {"netbsd", pack_netbsd}, + {"osf1", pack_12_20}, + {"sco", pack_8_8}, + {"solaris", pack_14_18}, + {"sunos", pack_8_8}, + {"svr3", pack_8_8}, + {"svr4", pack_14_18}, + {"ultrix", pack_8_8}, +}; + +static int +compare_format(const void *key, const void *element) +{ + const char *name; + const struct format *format; + + name = key; + format = element; + + return (strcmp(name, format->name)); +} + + +pack_t * +pack_find(const char *name) +{ + struct format *format; + + format = bsearch(name, formats, + sizeof(formats)/sizeof(formats[0]), + sizeof(formats[0]), compare_format); + if (format == 0) + return (NULL); + return (format->pack); +} diff --git a/compat/pack_dev.h b/compat/pack_dev.h new file mode 100644 index 00000000000..8ac5bc0507e --- /dev/null +++ b/compat/pack_dev.h @@ -0,0 +1,59 @@ +/* $NetBSD: pack_dev.h,v 1.6 2004/05/11 17:09:58 christos Exp $ */ + +/*- + * Copyright (c) 1998, 2001 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Charles M. Hannum. + * + * 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 NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _PACK_DEV_H +#define _PACK_DEV_H + +#ifdef __CYGWIN__ +typedef __dev32_t portdev_t; +#else +typedef dev_t portdev_t; +#endif +typedef portdev_t pack_t(int, u_long [], const char **); + +pack_t *pack_find(const char *); +pack_t pack_native; + +#define major_netbsd(x) ((int32_t)((((x) & 0x000fff00) >> 8))) +#define minor_netbsd(x) ((int32_t)((((x) & 0xfff00000) >> 12) | \ + (((x) & 0x000000ff) >> 0))) +#define makedev_netbsd(x,y) ((dev_t)((((x) << 8) & 0x000fff00) | \ + (((y) << 12) & 0xfff00000) | \ + (((y) << 0) & 0x000000ff))) + +#endif /* _PACK_DEV_H */ diff --git a/compat/pwcache.c b/compat/pwcache.c new file mode 100644 index 00000000000..157010a84cc --- /dev/null +++ b/compat/pwcache.c @@ -0,0 +1,651 @@ +/* $NetBSD: pwcache.c,v 1.29 2004/06/20 22:20:14 jmc Exp $ */ + +/*- + * Copyright (c) 1992 Keith Muller. + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego. + * + * 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. + */ + +/*- + * Copyright (c) 2002 The NetBSD Foundation, 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +/* + * XXX Undefine the renames of these functions so that we don't + * XXX rename the versions found in the host's by mistake! + */ +#undef group_from_gid +#undef user_from_uid +#endif + +#include +#if defined(LIBC_SCCS) && !defined(lint) +#if 0 +static char sccsid[] = "@(#)cache.c 8.1 (Berkeley) 5/31/93"; +#else +__RCSID("$NetBSD: pwcache.c,v 1.29 2004/06/20 22:20:14 jmc Exp $"); +#endif +#endif /* LIBC_SCCS and not lint */ + +#include "namespace.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#if HAVE_NBTOOL_CONFIG_H +/* XXX Now, re-apply the renaming that we undid above. */ +#define group_from_gid __nbcompat_group_from_gid +#define user_from_uid __nbcompat_user_from_uid +#endif + +#ifdef __weak_alias +__weak_alias(user_from_uid,_user_from_uid) +__weak_alias(group_from_gid,_group_from_gid) +__weak_alias(pwcache_userdb,_pwcache_userdb) +__weak_alias(pwcache_groupdb,_pwcache_groupdb) +#endif + +#if !HAVE_PWCACHE_USERDB || HAVE_NBTOOL_CONFIG_H +#include "pwcache.h" + +/* + * routines that control user, group, uid and gid caches (for the archive + * member print routine). + * IMPORTANT: + * these routines cache BOTH hits and misses, a major performance improvement + */ + +/* + * function pointers to various name lookup routines. + * these may be changed as necessary. + */ +static int (*_pwcache_setgroupent)(int) = setgroupent; +static void (*_pwcache_endgrent)(void) = endgrent; +static struct group * (*_pwcache_getgrnam)(const char *) = getgrnam; +static struct group * (*_pwcache_getgrgid)(gid_t) = getgrgid; +static int (*_pwcache_setpassent)(int) = setpassent; +static void (*_pwcache_endpwent)(void) = endpwent; +static struct passwd * (*_pwcache_getpwnam)(const char *) = getpwnam; +static struct passwd * (*_pwcache_getpwuid)(uid_t) = getpwuid; + +/* + * internal state + */ +static int pwopn; /* is password file open */ +static int gropn; /* is group file open */ +static UIDC **uidtb; /* uid to name cache */ +static GIDC **gidtb; /* gid to name cache */ +static UIDC **usrtb; /* user name to uid cache */ +static GIDC **grptb; /* group name to gid cache */ + +static int uidtb_fail; /* uidtb_start() failed ? */ +static int gidtb_fail; /* gidtb_start() failed ? */ +static int usrtb_fail; /* usrtb_start() failed ? */ +static int grptb_fail; /* grptb_start() failed ? */ + + +static u_int st_hash(const char *, size_t, int); +static int uidtb_start(void); +static int gidtb_start(void); +static int usrtb_start(void); +static int grptb_start(void); + + +static u_int +st_hash(const char *name, size_t len, int tabsz) +{ + u_int key = 0; + + _DIAGASSERT(name != NULL); + + while (len--) { + key += *name++; + key = (key << 8) | (key >> 24); + } + + return (key % tabsz); +} + +/* + * uidtb_start + * creates an an empty uidtb + * Return: + * 0 if ok, -1 otherwise + */ +static int +uidtb_start(void) +{ + + if (uidtb != NULL) + return (0); + if (uidtb_fail) + return (-1); + if ((uidtb = (UIDC **)calloc(UID_SZ, sizeof(UIDC *))) == NULL) { + ++uidtb_fail; + return (-1); + } + return (0); +} + +/* + * gidtb_start + * creates an an empty gidtb + * Return: + * 0 if ok, -1 otherwise + */ +static int +gidtb_start(void) +{ + + if (gidtb != NULL) + return (0); + if (gidtb_fail) + return (-1); + if ((gidtb = (GIDC **)calloc(GID_SZ, sizeof(GIDC *))) == NULL) { + ++gidtb_fail; + return (-1); + } + return (0); +} + +/* + * usrtb_start + * creates an an empty usrtb + * Return: + * 0 if ok, -1 otherwise + */ +static int +usrtb_start(void) +{ + + if (usrtb != NULL) + return (0); + if (usrtb_fail) + return (-1); + if ((usrtb = (UIDC **)calloc(UNM_SZ, sizeof(UIDC *))) == NULL) { + ++usrtb_fail; + return (-1); + } + return (0); +} + +/* + * grptb_start + * creates an an empty grptb + * Return: + * 0 if ok, -1 otherwise + */ +static int +grptb_start(void) +{ + + if (grptb != NULL) + return (0); + if (grptb_fail) + return (-1); + if ((grptb = (GIDC **)calloc(GNM_SZ, sizeof(GIDC *))) == NULL) { + ++grptb_fail; + return (-1); + } + return (0); +} + +/* + * user_from_uid() + * caches the name (if any) for the uid. If noname clear, we always + * return the stored name (if valid or invalid match). + * We use a simple hash table. + * Return + * Pointer to stored name (or a empty string) + */ +const char * +user_from_uid(uid_t uid, int noname) +{ + struct passwd *pw; + UIDC *ptr, **pptr; + + if ((uidtb == NULL) && (uidtb_start() < 0)) + return (NULL); + + /* + * see if we have this uid cached + */ + pptr = uidtb + (uid % UID_SZ); + ptr = *pptr; + + if ((ptr != NULL) && (ptr->valid > 0) && (ptr->uid == uid)) { + /* + * have an entry for this uid + */ + if (!noname || (ptr->valid == VALID)) + return (ptr->name); + return (NULL); + } + + /* + * No entry for this uid, we will add it + */ + if (!pwopn) { + if (_pwcache_setpassent != NULL) + (*_pwcache_setpassent)(1); + ++pwopn; + } + + if (ptr == NULL) + *pptr = ptr = (UIDC *)malloc(sizeof(UIDC)); + + if ((pw = (*_pwcache_getpwuid)(uid)) == NULL) { + /* + * no match for this uid in the local password file + * a string that is the uid in numeric format + */ + if (ptr == NULL) + return (NULL); + ptr->uid = uid; + (void)snprintf(ptr->name, UNMLEN, "%lu", (long) uid); + ptr->valid = INVALID; + if (noname) + return (NULL); + } else { + /* + * there is an entry for this uid in the password file + */ + if (ptr == NULL) + return (pw->pw_name); + ptr->uid = uid; + (void)strlcpy(ptr->name, pw->pw_name, UNMLEN); + ptr->valid = VALID; + } + return (ptr->name); +} + +/* + * group_from_gid() + * caches the name (if any) for the gid. If noname clear, we always + * return the stored name (if valid or invalid match). + * We use a simple hash table. + * Return + * Pointer to stored name (or a empty string) + */ +const char * +group_from_gid(gid_t gid, int noname) +{ + struct group *gr; + GIDC *ptr, **pptr; + + if ((gidtb == NULL) && (gidtb_start() < 0)) + return (NULL); + + /* + * see if we have this gid cached + */ + pptr = gidtb + (gid % GID_SZ); + ptr = *pptr; + + if ((ptr != NULL) && (ptr->valid > 0) && (ptr->gid == gid)) { + /* + * have an entry for this gid + */ + if (!noname || (ptr->valid == VALID)) + return (ptr->name); + return (NULL); + } + + /* + * No entry for this gid, we will add it + */ + if (!gropn) { + if (_pwcache_setgroupent != NULL) + (*_pwcache_setgroupent)(1); + ++gropn; + } + + if (ptr == NULL) + *pptr = ptr = (GIDC *)malloc(sizeof(GIDC)); + + if ((gr = (*_pwcache_getgrgid)(gid)) == NULL) { + /* + * no match for this gid in the local group file, put in + * a string that is the gid in numberic format + */ + if (ptr == NULL) + return (NULL); + ptr->gid = gid; + (void)snprintf(ptr->name, GNMLEN, "%lu", (long) gid); + ptr->valid = INVALID; + if (noname) + return (NULL); + } else { + /* + * there is an entry for this group in the group file + */ + if (ptr == NULL) + return (gr->gr_name); + ptr->gid = gid; + (void)strlcpy(ptr->name, gr->gr_name, GNMLEN); + ptr->valid = VALID; + } + return (ptr->name); +} + +/* + * uid_from_user() + * caches the uid for a given user name. We use a simple hash table. + * Return + * the uid (if any) for a user name, or a -1 if no match can be found + */ +int +uid_from_user(const char *name, uid_t *uid) +{ + struct passwd *pw; + UIDC *ptr, **pptr; + size_t namelen; + + /* + * return -1 for mangled names + */ + if (name == NULL || ((namelen = strlen(name)) == 0)) + return (-1); + if ((usrtb == NULL) && (usrtb_start() < 0)) + return (-1); + + /* + * look up in hash table, if found and valid return the uid, + * if found and invalid, return a -1 + */ + pptr = usrtb + st_hash(name, namelen, UNM_SZ); + ptr = *pptr; + + if ((ptr != NULL) && (ptr->valid > 0) && !strcmp(name, ptr->name)) { + if (ptr->valid == INVALID) + return (-1); + *uid = ptr->uid; + return (0); + } + + if (!pwopn) { + if (_pwcache_setpassent != NULL) + (*_pwcache_setpassent)(1); + ++pwopn; + } + + if (ptr == NULL) + *pptr = ptr = (UIDC *)malloc(sizeof(UIDC)); + + /* + * no match, look it up, if no match store it as an invalid entry, + * or store the matching uid + */ + if (ptr == NULL) { + if ((pw = (*_pwcache_getpwnam)(name)) == NULL) + return (-1); + *uid = pw->pw_uid; + return (0); + } + (void)strlcpy(ptr->name, name, UNMLEN); + if ((pw = (*_pwcache_getpwnam)(name)) == NULL) { + ptr->valid = INVALID; + return (-1); + } + ptr->valid = VALID; + *uid = ptr->uid = pw->pw_uid; + return (0); +} + +/* + * gid_from_group() + * caches the gid for a given group name. We use a simple hash table. + * Return + * the gid (if any) for a group name, or a -1 if no match can be found + */ +int +gid_from_group(const char *name, gid_t *gid) +{ + struct group *gr; + GIDC *ptr, **pptr; + size_t namelen; + + /* + * return -1 for mangled names + */ + if (name == NULL || ((namelen = strlen(name)) == 0)) + return (-1); + if ((grptb == NULL) && (grptb_start() < 0)) + return (-1); + + /* + * look up in hash table, if found and valid return the uid, + * if found and invalid, return a -1 + */ + pptr = grptb + st_hash(name, namelen, GID_SZ); + ptr = *pptr; + + if ((ptr != NULL) && (ptr->valid > 0) && !strcmp(name, ptr->name)) { + if (ptr->valid == INVALID) + return (-1); + *gid = ptr->gid; + return (0); + } + + if (!gropn) { + if (_pwcache_setgroupent != NULL) + (*_pwcache_setgroupent)(1); + ++gropn; + } + + if (ptr == NULL) + *pptr = ptr = (GIDC *)malloc(sizeof(GIDC)); + + /* + * no match, look it up, if no match store it as an invalid entry, + * or store the matching gid + */ + if (ptr == NULL) { + if ((gr = (*_pwcache_getgrnam)(name)) == NULL) + return (-1); + *gid = gr->gr_gid; + return (0); + } + + (void)strlcpy(ptr->name, name, GNMLEN); + if ((gr = (*_pwcache_getgrnam)(name)) == NULL) { + ptr->valid = INVALID; + return (-1); + } + ptr->valid = VALID; + *gid = ptr->gid = gr->gr_gid; + return (0); +} + +#define FLUSHTB(arr, len, fail) \ + do { \ + if (arr != NULL) { \ + for (i = 0; i < len; i++) \ + if (arr[i] != NULL) \ + free(arr[i]); \ + arr = NULL; \ + } \ + fail = 0; \ + } while (/* CONSTCOND */0); + +int +pwcache_userdb( + int (*a_setpassent)(int), + void (*a_endpwent)(void), + struct passwd * (*a_getpwnam)(const char *), + struct passwd * (*a_getpwuid)(uid_t)) +{ + int i; + + /* a_setpassent and a_endpwent may be NULL */ + if (a_getpwnam == NULL || a_getpwuid == NULL) + return (-1); + + if (_pwcache_endpwent != NULL) + (*_pwcache_endpwent)(); + FLUSHTB(uidtb, UID_SZ, uidtb_fail); + FLUSHTB(usrtb, UNM_SZ, usrtb_fail); + pwopn = 0; + _pwcache_setpassent = a_setpassent; + _pwcache_endpwent = a_endpwent; + _pwcache_getpwnam = a_getpwnam; + _pwcache_getpwuid = a_getpwuid; + + return (0); +} + +int +pwcache_groupdb( + int (*a_setgroupent)(int), + void (*a_endgrent)(void), + struct group * (*a_getgrnam)(const char *), + struct group * (*a_getgrgid)(gid_t)) +{ + int i; + + /* a_setgroupent and a_endgrent may be NULL */ + if (a_getgrnam == NULL || a_getgrgid == NULL) + return (-1); + + if (_pwcache_endgrent != NULL) + (*_pwcache_endgrent)(); + FLUSHTB(gidtb, GID_SZ, gidtb_fail); + FLUSHTB(grptb, GNM_SZ, grptb_fail); + gropn = 0; + _pwcache_setgroupent = a_setgroupent; + _pwcache_endgrent = a_endgrent; + _pwcache_getgrnam = a_getgrnam; + _pwcache_getgrgid = a_getgrgid; + + return (0); +} + + +#ifdef TEST_PWCACHE + +struct passwd * +test_getpwnam(const char *name) +{ + static struct passwd foo; + + memset(&foo, 0, sizeof(foo)); + if (strcmp(name, "toor") == 0) { + foo.pw_uid = 666; + return &foo; + } + return (getpwnam(name)); +} + +int +main(int argc, char *argv[]) +{ + uid_t u; + int r, i; + + printf("pass 1 (default userdb)\n"); + for (i = 1; i < argc; i++) { + printf("i: %d, pwopn %d usrtb_fail %d usrtb %p\n", + i, pwopn, usrtb_fail, usrtb); + r = uid_from_user(argv[i], &u); + if (r == -1) + printf(" uid_from_user %s: failed\n", argv[i]); + else + printf(" uid_from_user %s: %d\n", argv[i], u); + } + printf("pass 1 finish: pwopn %d usrtb_fail %d usrtb %p\n", + pwopn, usrtb_fail, usrtb); + + puts(""); + printf("pass 2 (replacement userdb)\n"); + printf("pwcache_userdb returned %d\n", + pwcache_userdb(setpassent, test_getpwnam, getpwuid)); + printf("pwopn %d usrtb_fail %d usrtb %p\n", pwopn, usrtb_fail, usrtb); + + for (i = 1; i < argc; i++) { + printf("i: %d, pwopn %d usrtb_fail %d usrtb %p\n", + i, pwopn, usrtb_fail, usrtb); + u = -1; + r = uid_from_user(argv[i], &u); + if (r == -1) + printf(" uid_from_user %s: failed\n", argv[i]); + else + printf(" uid_from_user %s: %d\n", argv[i], u); + } + printf("pass 2 finish: pwopn %d usrtb_fail %d usrtb %p\n", + pwopn, usrtb_fail, usrtb); + + puts(""); + printf("pass 3 (null pointers)\n"); + printf("pwcache_userdb returned %d\n", + pwcache_userdb(NULL, NULL, NULL)); + + return (0); +} +#endif /* TEST_PWCACHE */ +#endif /* !HAVE_PWCACHE_USERDB */ diff --git a/compat/pwcache.h b/compat/pwcache.h new file mode 100644 index 00000000000..3bd651b604c --- /dev/null +++ b/compat/pwcache.h @@ -0,0 +1,72 @@ +/* $NetBSD: pwcache.h,v 1.5 2003/11/10 08:51:51 wiz Exp $ */ + +/*- + * Copyright (c) 1992 Keith Muller. + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego. + * + * 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. + * + * @(#)cache.h 8.1 (Berkeley) 5/31/93 + */ + +/* + * Constants and data structures used to implement group and password file + * caches. Traditional passwd/group cache routines perform quite poorly with + * archives. The chances of hitting a valid lookup with an archive is quite a + * bit worse than with files already resident on the file system. These misses + * create a MAJOR performance cost. To address this problem, these routines + * cache both hits and misses. + * + * NOTE: name lengths must be as large as those stored in ANY PROTOCOL and + * as stored in the passwd and group files. CACHE SIZES MUST BE PRIME + */ +#define UNMLEN 32 /* >= user name found in any protocol */ +#define GNMLEN 32 /* >= group name found in any protocol */ +#define UID_SZ 317 /* size of uid to user_name cache */ +#define UNM_SZ 317 /* size of user_name to uid cache */ +#define GID_SZ 251 /* size of gid to group_name cache */ +#define GNM_SZ 251 /* size of group_name to gid cache */ +#define VALID 1 /* entry and name are valid */ +#define INVALID 2 /* entry valid, name NOT valid */ + +/* + * Node structures used in the user, group, uid, and gid caches. + */ + +typedef struct uidc { + int valid; /* is this a valid or a miss entry */ + char name[UNMLEN]; /* uid name */ + uid_t uid; /* cached uid */ +} UIDC; + +typedef struct gidc { + int valid; /* is this a valid or a miss entry */ + char name[GNMLEN]; /* gid name */ + gid_t gid; /* cached gid */ +} GIDC; diff --git a/compat/stat_flags.c b/compat/stat_flags.c new file mode 100644 index 00000000000..bfe156a77f3 --- /dev/null +++ b/compat/stat_flags.c @@ -0,0 +1,185 @@ +/* $NetBSD: stat_flags.c,v 1.19 2004/05/25 14:54:55 hannken 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. + * 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. + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#else +#define HAVE_STRUCT_STAT_ST_FLAGS 1 +#endif + +#include +#if !defined(lint) +#if 0 +static char sccsid[] = "@(#)stat_flags.c 8.2 (Berkeley) 7/28/94"; +#else +__RCSID("$NetBSD: stat_flags.c,v 1.19 2004/05/25 14:54:55 hannken Exp $"); +#endif +#endif /* not lint */ + +#include +#include +#include +#include +#include + +#include "stat_flags.h" + +#define SAPPEND(s) do { \ + if (prefix != NULL) \ + (void)strlcat(string, prefix, sizeof(string)); \ + (void)strlcat(string, s, sizeof(string)); \ + prefix = ","; \ +} while (/* CONSTCOND */ 0) + +/* + * flags_to_string -- + * Convert stat flags to a comma-separated string. If no flags + * are set, return the default string. + */ +char * +flags_to_string(u_long flags, const char *def) +{ + static char string[128]; + const char *prefix; + + string[0] = '\0'; + prefix = NULL; +#if HAVE_STRUCT_STAT_ST_FLAGS + if (flags & UF_APPEND) + SAPPEND("uappnd"); + if (flags & UF_IMMUTABLE) + SAPPEND("uchg"); + if (flags & UF_NODUMP) + SAPPEND("nodump"); + if (flags & UF_OPAQUE) + SAPPEND("opaque"); + if (flags & SF_APPEND) + SAPPEND("sappnd"); + if (flags & SF_ARCHIVED) + SAPPEND("arch"); + if (flags & SF_IMMUTABLE) + SAPPEND("schg"); +#ifdef SF_SNAPSHOT + if (flags & SF_SNAPSHOT) + SAPPEND("snap"); +#endif +#endif + if (prefix == NULL) + strlcpy(string, def, sizeof(string)); + return (string); +} + +#define TEST(a, b, f) { \ + if (!strcmp(a, b)) { \ + if (clear) { \ + if (clrp) \ + *clrp |= (f); \ + if (setp) \ + *setp &= ~(f); \ + } else { \ + if (setp) \ + *setp |= (f); \ + if (clrp) \ + *clrp &= ~(f); \ + } \ + break; \ + } \ +} + +/* + * string_to_flags -- + * Take string of arguments and return stat flags. Return 0 on + * success, 1 on failure. On failure, stringp is set to point + * to the offending token. + */ +int +string_to_flags(char **stringp, u_long *setp, u_long *clrp) +{ + int clear; + char *string, *p; + + if (setp) + *setp = 0; + if (clrp) + *clrp = 0; + +#if HAVE_STRUCT_STAT_ST_FLAGS + string = *stringp; + while ((p = strsep(&string, "\t ,")) != NULL) { + clear = 0; + *stringp = p; + if (*p == '\0') + continue; + if (p[0] == 'n' && p[1] == 'o') { + clear = 1; + p += 2; + } + switch (p[0]) { + case 'a': + TEST(p, "arch", SF_ARCHIVED); + TEST(p, "archived", SF_ARCHIVED); + return (1); + case 'd': + clear = !clear; + TEST(p, "dump", UF_NODUMP); + return (1); + case 'n': + /* + * Support `nonodump'. Note that + * the state of clear is not changed. + */ + TEST(p, "nodump", UF_NODUMP); + return (1); + case 'o': + TEST(p, "opaque", UF_OPAQUE); + return (1); + case 's': + TEST(p, "sappnd", SF_APPEND); + TEST(p, "sappend", SF_APPEND); + TEST(p, "schg", SF_IMMUTABLE); + TEST(p, "schange", SF_IMMUTABLE); + TEST(p, "simmutable", SF_IMMUTABLE); + return (1); + case 'u': + TEST(p, "uappnd", UF_APPEND); + TEST(p, "uappend", UF_APPEND); + TEST(p, "uchg", UF_IMMUTABLE); + TEST(p, "uchange", UF_IMMUTABLE); + TEST(p, "uimmutable", UF_IMMUTABLE); + return (1); + default: + return (1); + } + } +#endif + + return (0); +} diff --git a/compat/stat_flags.h b/compat/stat_flags.h new file mode 100644 index 00000000000..40c7131db9e --- /dev/null +++ b/compat/stat_flags.h @@ -0,0 +1,35 @@ +/* $NetBSD: stat_flags.h,v 1.4 2003/08/07 09:05:16 agc Exp $ */ + +/*- + * Copyright (c) 1991, 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. + * 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. + * + * @(#)extern.h 8.1 (Berkeley) 5/31/93 + */ + +char *flags_to_string(u_long, const char *); +int string_to_flags(char **, u_long *, u_long *); diff --git a/compat/strsuftoll.c b/compat/strsuftoll.c new file mode 100644 index 00000000000..a4d157fc897 --- /dev/null +++ b/compat/strsuftoll.c @@ -0,0 +1,247 @@ +/* $NetBSD: strsuftoll.c,v 1.6 2004/03/05 05:58:29 lukem Exp $ */ +/*- + * Copyright (c) 2001-2002,2004 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Luke Mewburn. + * + * 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 NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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) 1991, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego and Lance + * Visser of Convex Computer Corporation. + * + * 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. + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include + +#if defined(LIBC_SCCS) && !defined(lint) +__RCSID("$NetBSD: strsuftoll.c,v 1.6 2004/03/05 05:58:29 lukem Exp $"); +#endif /* LIBC_SCCS and not lint */ + +#ifdef _LIBC +#include "namespace.h" +#endif + +#if !HAVE_STRSUFTOLL + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef _LIBC +# ifdef __weak_alias +__weak_alias(strsuftoll, _strsuftoll) +__weak_alias(strsuftollx, _strsuftollx) +# endif +#endif /* LIBC */ + +/* + * Convert an expression of the following forms to a (u)int64_t. + * 1) A positive decimal number. + * 2) A positive decimal number followed by a b (mult by 512). + * 3) A positive decimal number followed by a k (mult by 1024). + * 4) A positive decimal number followed by a m (mult by 1048576). + * 5) A positive decimal number followed by a g (mult by 1073741824). + * 6) A positive decimal number followed by a t (mult by 1099511627776). + * 7) A positive decimal number followed by a w (mult by sizeof int) + * 8) Two or more positive decimal numbers (with/without k,b or w). + * separated by x (also * for backwards compatibility), specifying + * the product of the indicated values. + * Returns the result upon successful conversion, or exits with an + * appropriate error. + * + */ +/* LONGLONG */ +long long +strsuftoll(const char *desc, const char *val, + long long min, long long max) +{ + long long result; + char errbuf[100]; + + result = strsuftollx(desc, val, min, max, errbuf, sizeof(errbuf)); + if (*errbuf != '\0') + errx(1, "%s", errbuf); + return (result); +} + +/* + * As strsuftoll(), but returns the error message into the provided buffer + * rather than exiting with it. + */ +/* LONGLONG */ +long long +strsuftollx(const char *desc, const char *val, + long long min, long long max, char *ebuf, size_t ebuflen) +{ + long long num, t; + char *expr; + + _DIAGASSERT(desc != NULL); + _DIAGASSERT(val != NULL); + _DIAGASSERT(ebuf != NULL); + + errno = 0; + ebuf[0] = '\0'; + + while (isspace((unsigned char)*val)) /* Skip leading space */ + val++; + + num = strtoll(val, &expr, 10); + if (errno == ERANGE) + goto erange; /* Overflow */ + + if (expr == val) /* No digits */ + goto badnum; + + switch (*expr) { + case 'b': + t = num; + num *= 512; /* 1 block */ + if (t > num) + goto erange; + ++expr; + break; + case 'k': + t = num; + num *= 1024; /* 1 kilobyte */ + if (t > num) + goto erange; + ++expr; + break; + case 'm': + t = num; + num *= 1048576; /* 1 megabyte */ + if (t > num) + goto erange; + ++expr; + break; + case 'g': + t = num; + num *= 1073741824; /* 1 gigabyte */ + if (t > num) + goto erange; + ++expr; + break; + case 't': + t = num; + num *= 1099511627776LL; /* 1 terabyte */ + if (t > num) + goto erange; + ++expr; + break; + case 'w': + t = num; + num *= sizeof(int); /* 1 word */ + if (t > num) + goto erange; + ++expr; + break; + } + + switch (*expr) { + case '\0': + break; + case '*': /* Backward compatible */ + case 'x': + t = num; + num *= strsuftollx(desc, expr + 1, min, max, ebuf, ebuflen); + if (*ebuf != '\0') + return (0); + if (t > num) { + erange: + snprintf(ebuf, ebuflen, + "%s: %s", desc, strerror(ERANGE)); + return (0); + } + break; + default: + badnum: snprintf(ebuf, ebuflen, + "%s `%s': illegal number", desc, val); + return (0); + } + if (num < min) { + /* LONGLONG */ + snprintf(ebuf, ebuflen, "%s %lld is less than %lld.", + desc, (long long)num, (long long)min); + return (0); + } + if (num > max) { + /* LONGLONG */ + snprintf(ebuf, ebuflen, + "%s %lld is greater than %lld.", + desc, (long long)num, (long long)max); + return (0); + } + *ebuf = '\0'; + return (num); +} + +#endif /* !HAVE_STRSUFTOLL */ diff --git a/compat/util.h b/compat/util.h new file mode 100644 index 00000000000..e69de29bb2d diff --git a/compat/vis.c b/compat/vis.c new file mode 100644 index 00000000000..568ced532fb --- /dev/null +++ b/compat/vis.c @@ -0,0 +1,378 @@ +/* $NetBSD: vis.c,v 1.27 2004/02/26 23:01:15 enami Exp $ */ + +/*- + * Copyright (c) 1989, 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. + * 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) 1999 The NetBSD Foundation, 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. + * 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. + */ + +#include +#if defined(LIBC_SCCS) && !defined(lint) +__RCSID("$NetBSD: vis.c,v 1.27 2004/02/26 23:01:15 enami Exp $"); +#endif /* LIBC_SCCS and not lint */ + +#include "namespace.h" +#include + +#include +#include +#include + +#ifdef __weak_alias +__weak_alias(strsvis,_strsvis) +__weak_alias(strsvisx,_strsvisx) +__weak_alias(strvis,_strvis) +__weak_alias(strvisx,_strvisx) +__weak_alias(svis,_svis) +__weak_alias(vis,_vis) +#endif + +#if !HAVE_VIS || !HAVE_SVIS +#include +#include +#include +#include + +#undef BELL +#define BELL '\a' + +#define isoctal(c) (((u_char)(c)) >= '0' && ((u_char)(c)) <= '7') +#define iswhite(c) (c == ' ' || c == '\t' || c == '\n') +#define issafe(c) (c == '\b' || c == BELL || c == '\r') +#define xtoa(c) "0123456789abcdef"[c] + +#define MAXEXTRAS 5 + + +#define MAKEEXTRALIST(flag, extra, orig) \ +do { \ + const char *o = orig; \ + char *e; \ + while (*o++) \ + continue; \ + extra = alloca((size_t)((o - orig) + MAXEXTRAS)); \ + for (o = orig, e = extra; (*e++ = *o++) != '\0';) \ + continue; \ + e--; \ + if (flag & VIS_SP) *e++ = ' '; \ + if (flag & VIS_TAB) *e++ = '\t'; \ + if (flag & VIS_NL) *e++ = '\n'; \ + if ((flag & VIS_NOSLASH) == 0) *e++ = '\\'; \ + *e = '\0'; \ +} while (/*CONSTCOND*/0) + + +/* + * This is HVIS, the macro of vis used to HTTP style (RFC 1808) + */ +#define HVIS(dst, c, flag, nextc, extra) \ +do \ + if (!isascii(c) || !isalnum(c) || strchr("$-_.+!*'(),", c) != NULL) { \ + *dst++ = '%'; \ + *dst++ = xtoa(((unsigned int)c >> 4) & 0xf); \ + *dst++ = xtoa((unsigned int)c & 0xf); \ + } else { \ + SVIS(dst, c, flag, nextc, extra); \ + } \ +while (/*CONSTCOND*/0) + +/* + * This is SVIS, the central macro of vis. + * dst: Pointer to the destination buffer + * c: Character to encode + * flag: Flag word + * nextc: The character following 'c' + * extra: Pointer to the list of extra characters to be + * backslash-protected. + */ +#define SVIS(dst, c, flag, nextc, extra) \ +do { \ + int isextra; \ + isextra = strchr(extra, c) != NULL; \ + if (!isextra && isascii(c) && (isgraph(c) || iswhite(c) || \ + ((flag & VIS_SAFE) && issafe(c)))) { \ + *dst++ = c; \ + break; \ + } \ + if (flag & VIS_CSTYLE) { \ + switch (c) { \ + case '\n': \ + *dst++ = '\\'; *dst++ = 'n'; \ + continue; \ + case '\r': \ + *dst++ = '\\'; *dst++ = 'r'; \ + continue; \ + case '\b': \ + *dst++ = '\\'; *dst++ = 'b'; \ + continue; \ + case BELL: \ + *dst++ = '\\'; *dst++ = 'a'; \ + continue; \ + case '\v': \ + *dst++ = '\\'; *dst++ = 'v'; \ + continue; \ + case '\t': \ + *dst++ = '\\'; *dst++ = 't'; \ + continue; \ + case '\f': \ + *dst++ = '\\'; *dst++ = 'f'; \ + continue; \ + case ' ': \ + *dst++ = '\\'; *dst++ = 's'; \ + continue; \ + case '\0': \ + *dst++ = '\\'; *dst++ = '0'; \ + if (isoctal(nextc)) { \ + *dst++ = '0'; \ + *dst++ = '0'; \ + } \ + continue; \ + default: \ + if (isgraph(c)) { \ + *dst++ = '\\'; *dst++ = c; \ + continue; \ + } \ + } \ + } \ + if (isextra || ((c & 0177) == ' ') || (flag & VIS_OCTAL)) { \ + *dst++ = '\\'; \ + *dst++ = (u_char)(((u_int32_t)(u_char)c >> 6) & 03) + '0'; \ + *dst++ = (u_char)(((u_int32_t)(u_char)c >> 3) & 07) + '0'; \ + *dst++ = (c & 07) + '0'; \ + } else { \ + if ((flag & VIS_NOSLASH) == 0) *dst++ = '\\'; \ + if (c & 0200) { \ + c &= 0177; *dst++ = 'M'; \ + } \ + if (iscntrl(c)) { \ + *dst++ = '^'; \ + if (c == 0177) \ + *dst++ = '?'; \ + else \ + *dst++ = c + '@'; \ + } else { \ + *dst++ = '-'; *dst++ = c; \ + } \ + } \ +} while (/*CONSTCOND*/0) + + +/* + * svis - visually encode characters, also encoding the characters + * pointed to by `extra' + */ +char * +svis(dst, c, flag, nextc, extra) + char *dst; + int c, flag, nextc; + const char *extra; +{ + char *nextra; + _DIAGASSERT(dst != NULL); + _DIAGASSERT(extra != NULL); + MAKEEXTRALIST(flag, nextra, extra); + if (flag & VIS_HTTPSTYLE) + HVIS(dst, c, flag, nextc, nextra); + else + SVIS(dst, c, flag, nextc, nextra); + *dst = '\0'; + return(dst); +} + + +/* + * strsvis, strsvisx - visually encode characters from src into dst + * + * Extra is a pointer to a \0-terminated list of characters to + * be encoded, too. These functions are useful e. g. to + * encode strings in such a way so that they are not interpreted + * by a shell. + * + * Dst must be 4 times the size of src to account for possible + * expansion. The length of dst, not including the trailing NULL, + * is returned. + * + * Strsvisx encodes exactly len bytes from src into dst. + * This is useful for encoding a block of data. + */ +int +strsvis(dst, csrc, flag, extra) + char *dst; + const char *csrc; + int flag; + const char *extra; +{ + int c; + char *start; + char *nextra; + const unsigned char *src = (const unsigned char *)csrc; + + _DIAGASSERT(dst != NULL); + _DIAGASSERT(src != NULL); + _DIAGASSERT(extra != NULL); + MAKEEXTRALIST(flag, nextra, extra); + if (flag & VIS_HTTPSTYLE) { + for (start = dst; (c = *src++) != '\0'; /* empty */) + HVIS(dst, c, flag, *src, nextra); + } else { + for (start = dst; (c = *src++) != '\0'; /* empty */) + SVIS(dst, c, flag, *src, nextra); + } + *dst = '\0'; + return (dst - start); +} + + +int +strsvisx(dst, csrc, len, flag, extra) + char *dst; + const char *csrc; + size_t len; + int flag; + const char *extra; +{ + int c; + char *start; + char *nextra; + const unsigned char *src = (const unsigned char *)csrc; + + _DIAGASSERT(dst != NULL); + _DIAGASSERT(src != NULL); + _DIAGASSERT(extra != NULL); + MAKEEXTRALIST(flag, nextra, extra); + + if (flag & VIS_HTTPSTYLE) { + for (start = dst; len > 0; len--) { + c = *src++; + HVIS(dst, c, flag, len ? *src : '\0', nextra); + } + } else { + for (start = dst; len > 0; len--) { + c = *src++; + SVIS(dst, c, flag, len ? *src : '\0', nextra); + } + } + *dst = '\0'; + return (dst - start); +} +#endif + +#if !HAVE_VIS +/* + * vis - visually encode characters + */ +char * +vis(dst, c, flag, nextc) + char *dst; + int c, flag, nextc; + +{ + char *extra; + + _DIAGASSERT(dst != NULL); + + MAKEEXTRALIST(flag, extra, ""); + if (flag & VIS_HTTPSTYLE) + HVIS(dst, c, flag, nextc, extra); + else + SVIS(dst, c, flag, nextc, extra); + *dst = '\0'; + return (dst); +} + + +/* + * strvis, strvisx - visually encode characters from src into dst + * + * Dst must be 4 times the size of src to account for possible + * expansion. The length of dst, not including the trailing NULL, + * is returned. + * + * Strvisx encodes exactly len bytes from src into dst. + * This is useful for encoding a block of data. + */ +int +strvis(dst, src, flag) + char *dst; + const char *src; + int flag; +{ + char *extra; + + MAKEEXTRALIST(flag, extra, ""); + return (strsvis(dst, src, flag, extra)); +} + + +int +strvisx(dst, src, len, flag) + char *dst; + const char *src; + size_t len; + int flag; +{ + char *extra; + + MAKEEXTRALIST(flag, extra, ""); + return (strsvisx(dst, src, len, flag, extra)); +} +#endif diff --git a/compat/vis.h b/compat/vis.h new file mode 100644 index 00000000000..abba772f25a --- /dev/null +++ b/compat/vis.h @@ -0,0 +1,94 @@ +/* $NetBSD: vis.h,v 1.14 2003/08/07 09:44:12 agc Exp $ */ + +/*- + * 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. + * 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. + * + * @(#)vis.h 8.1 (Berkeley) 6/2/93 + */ + +#ifndef _VIS_H_ +#define _VIS_H_ + +#include + +/* + * to select alternate encoding format + */ +#define VIS_OCTAL 0x01 /* use octal \ddd format */ +#define VIS_CSTYLE 0x02 /* use \[nrft0..] where appropiate */ + +/* + * to alter set of characters encoded (default is to encode all + * non-graphic except space, tab, and newline). + */ +#define VIS_SP 0x04 /* also encode space */ +#define VIS_TAB 0x08 /* also encode tab */ +#define VIS_NL 0x10 /* also encode newline */ +#define VIS_WHITE (VIS_SP | VIS_TAB | VIS_NL) +#define VIS_SAFE 0x20 /* only encode "unsafe" characters */ + +/* + * other + */ +#define VIS_NOSLASH 0x40 /* inhibit printing '\' */ +#define VIS_HTTPSTYLE 0x80 /* http-style escape % HEX HEX */ + +/* + * unvis return codes + */ +#define UNVIS_VALID 1 /* character valid */ +#define UNVIS_VALIDPUSH 2 /* character valid, push back passed char */ +#define UNVIS_NOCHAR 3 /* valid sequence, no character produced */ +#define UNVIS_SYNBAD -1 /* unrecognized escape sequence */ +#define UNVIS_ERROR -2 /* decoder in unknown state (unrecoverable) */ + +/* + * unvis flags + */ +#define UNVIS_END 1 /* no more characters */ + +#include + +__BEGIN_DECLS +char *vis __P((char *, int, int, int)); +char *svis __P((char *, int, int, int, const char *)); +int strvis __P((char *, const char *, int)); +int strsvis __P((char *, const char *, int, const char *)); +int strvisx __P((char *, const char *, size_t, int)); +int strsvisx __P((char *, const char *, size_t, int, const char *)); +int strunvis __P((char *, const char *)); +int strunvisx __P((char *, const char *, int)); +#ifdef __LIBC12_SOURCE__ +int unvis __P((char *, int, int *, int)); +int __unvis13 __P((char *, int, int *, int)); +#else +int unvis __P((char *, int, int *, int)) __RENAME(__unvis13); +#endif +__END_DECLS + +#endif /* !_VIS_H_ */ diff --git a/ffs.c b/ffs.c new file mode 100644 index 00000000000..0c442655a5f --- /dev/null +++ b/ffs.c @@ -0,0 +1,1093 @@ +/* $NetBSD: ffs.c,v 1.30 2004/06/24 22:30:13 lukem Exp $ */ + +/* + * Copyright (c) 2001 Wasabi Systems, Inc. + * All rights reserved. + * + * Written by Luke Mewburn for Wasabi Systems, 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. + * 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 + * Wasabi Systems, Inc. + * 4. The name of Wasabi Systems, Inc. may not be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``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 WASABI SYSTEMS, INC + * 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) 1982, 1986, 1989, 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. + * 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. + * + * @(#)ffs_alloc.c 8.19 (Berkeley) 7/13/95 + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include +#if defined(__RCSID) && !defined(__lint) +__RCSID("$NetBSD: ffs.c,v 1.30 2004/06/24 22:30:13 lukem Exp $"); +#endif /* !__lint */ + +#include + +#if !HAVE_NBTOOL_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "makefs.h" + +#include +#include +#include +#include + +#include "ffs/ufs_inode.h" +#include "ffs/newfs_extern.h" +#include "ffs/ffs_extern.h" + +#undef DIP +#define DIP(dp, field) \ + ((fsopts->version == 1) ? \ + (dp)->ffs1_din.di_##field : (dp)->ffs2_din.di_##field) + +/* + * Various file system defaults (cribbed from newfs(8)). + */ +#define DFL_FRAGSIZE 1024 /* fragment size */ +#define DFL_BLKSIZE 8192 /* block size */ +#define DFL_SECSIZE 512 /* sector size */ +#define DFL_CYLSPERGROUP 65536 /* cylinders per group */ +#define DFL_FRAGSPERINODE 4 /* fragments per inode */ +#define DFL_ROTDELAY 0 /* rotational delay */ +#define DFL_NRPOS 1 /* rotational positions */ +#define DFL_RPM 3600 /* rpm of disk */ +#define DFL_NSECTORS 64 /* # of sectors */ +#define DFL_NTRACKS 16 /* # of tracks */ + + +typedef struct { + u_char *buf; /* buf for directory */ + doff_t size; /* full size of buf */ + doff_t cur; /* offset of current entry */ +} dirbuf_t; + + +static int ffs_create_image(const char *, fsinfo_t *); +static void ffs_dump_fsinfo(fsinfo_t *); +static void ffs_dump_dirbuf(dirbuf_t *, const char *, int); +static void ffs_make_dirbuf(dirbuf_t *, const char *, fsnode *, int); +static int ffs_populate_dir(const char *, fsnode *, fsinfo_t *); +static void ffs_size_dir(fsnode *, fsinfo_t *); +static void ffs_validate(const char *, fsnode *, fsinfo_t *); +static void ffs_write_file(union dinode *, uint32_t, void *, fsinfo_t *); +static void ffs_write_inode(union dinode *, uint32_t, const fsinfo_t *); +static void *ffs_build_dinode1(struct ufs1_dinode *, dirbuf_t *, fsnode *, + fsnode *, fsinfo_t *); +static void *ffs_build_dinode2(struct ufs2_dinode *, dirbuf_t *, fsnode *, + fsnode *, fsinfo_t *); + + + +int sectorsize; /* XXX: for buf.c::getblk() */ + + /* publically visible functions */ + +int +ffs_parse_opts(const char *option, fsinfo_t *fsopts) +{ + option_t ffs_options[] = { + { "bsize", &fsopts->bsize, 1, INT_MAX, + "block size" }, + { "fsize", &fsopts->fsize, 1, INT_MAX, + "fragment size" }, + { "density", &fsopts->density, 1, INT_MAX, + "bytes per inode" }, + { "minfree", &fsopts->minfree, 0, 99, + "minfree" }, + { "maxbpf", &fsopts->maxbpg, 1, INT_MAX, + "max blocks per file in a cg" }, + { "avgfilesize", &fsopts->avgfilesize, 1, INT_MAX, + "expected average file size" }, + { "avgfpdir", &fsopts->avgfpdir, 1, INT_MAX, + "expected # of files per directory" }, + { "extent", &fsopts->maxbsize, 1, INT_MAX, + "maximum # extent size" }, + { "maxbpcg", &fsopts->maxblkspercg, 1, INT_MAX, + "max # of blocks per group" }, + { "version", &fsopts->version, 1, 2, + "UFS version" }, + { NULL } + }; + + char *var, *val; + int rv; + + (void)&ffs_options; + assert(option != NULL); + assert(fsopts != NULL); + + if (debug & DEBUG_FS_PARSE_OPTS) + printf("ffs_parse_opts: got `%s'\n", option); + + if ((var = strdup(option)) == NULL) + err(1, "Allocating memory for copy of option string"); + rv = 0; + + if ((val = strchr(var, '=')) == NULL) { + warnx("Option `%s' doesn't contain a value", var); + goto leave_ffs_parse_opts; + } + *val++ = '\0'; + + if (strcmp(var, "optimization") == 0) { + if (strcmp(val, "time") == 0) { + fsopts->optimization = FS_OPTTIME; + } else if (strcmp(val, "space") == 0) { + fsopts->optimization = FS_OPTSPACE; + } else { + warnx("Invalid optimization `%s'", val); + goto leave_ffs_parse_opts; + } + rv = 1; + } else + rv = set_option(ffs_options, var, val); + + leave_ffs_parse_opts: + if (var) + free(var); + return (rv); +} + + +void +ffs_makefs(const char *image, const char *dir, fsnode *root, fsinfo_t *fsopts) +{ + struct fs *superblock; + struct timeval start; + + assert(image != NULL); + assert(dir != NULL); + assert(root != NULL); + assert(fsopts != NULL); + + if (debug & DEBUG_FS_MAKEFS) + printf("ffs_makefs: image %s directory %s root %p\n", + image, dir, root); + + /* validate tree and options */ + TIMER_START(start); + ffs_validate(dir, root, fsopts); + TIMER_RESULTS(start, "ffs_validate"); + + printf("Calculated size of `%s': %lld bytes, %lld inodes\n", + image, (long long)fsopts->size, (long long)fsopts->inodes); + + /* create image */ + TIMER_START(start); + if (ffs_create_image(image, fsopts) == -1) + errx(1, "Image file `%s' not created.", image); + TIMER_RESULTS(start, "ffs_create_image"); + + fsopts->curinode = ROOTINO; + + if (debug & DEBUG_FS_MAKEFS) + putchar('\n'); + + /* populate image */ + printf("Populating `%s'\n", image); + TIMER_START(start); + if (! ffs_populate_dir(dir, root, fsopts)) + errx(1, "Image file `%s' not populated.", image); + TIMER_RESULTS(start, "ffs_populate_dir"); + + /* ensure no outstanding buffers remain */ + if (debug & DEBUG_FS_MAKEFS) + bcleanup(); + + /* update various superblock parameters */ + superblock = fsopts->superblock; + superblock->fs_fmod = 0; + superblock->fs_old_cstotal.cs_ndir = superblock->fs_cstotal.cs_ndir; + superblock->fs_old_cstotal.cs_nbfree = superblock->fs_cstotal.cs_nbfree; + superblock->fs_old_cstotal.cs_nifree = superblock->fs_cstotal.cs_nifree; + superblock->fs_old_cstotal.cs_nffree = superblock->fs_cstotal.cs_nffree; + + /* write out superblock; image is now complete */ + ffs_write_superblock(fsopts->superblock, fsopts); + if (close(fsopts->fd) == -1) + err(1, "Closing `%s'", image); + fsopts->fd = -1; + printf("Image `%s' complete\n", image); +} + + /* end of public functions */ + + +static void +ffs_validate(const char *dir, fsnode *root, fsinfo_t *fsopts) +{ + int32_t ncg = 1; +#if notyet + int32_t spc, nspf, ncyl, fssize; +#endif + + assert(dir != NULL); + assert(root != NULL); + assert(fsopts != NULL); + + if (debug & DEBUG_FS_VALIDATE) { + printf("ffs_validate: before defaults set:\n"); + ffs_dump_fsinfo(fsopts); + } + + /* set FFS defaults */ + if (fsopts->sectorsize == -1) + fsopts->sectorsize = DFL_SECSIZE; + if (fsopts->fsize == -1) + fsopts->fsize = MAX(DFL_FRAGSIZE, fsopts->sectorsize); + if (fsopts->bsize == -1) + fsopts->bsize = MIN(DFL_BLKSIZE, 8 * fsopts->fsize); + if (fsopts->cpg == -1) + fsopts->cpg = DFL_CYLSPERGROUP; + else + fsopts->cpgflg = 1; + /* fsopts->density is set below */ + if (fsopts->nsectors == -1) + fsopts->nsectors = DFL_NSECTORS; + if (fsopts->minfree == -1) + fsopts->minfree = MINFREE; + if (fsopts->optimization == -1) + fsopts->optimization = DEFAULTOPT; + if (fsopts->maxcontig == -1) + fsopts->maxcontig = + MAX(1, MIN(MAXPHYS, FFS_MAXBSIZE) / fsopts->bsize); + /* XXX ondisk32 */ + if (fsopts->maxbpg == -1) + fsopts->maxbpg = fsopts->bsize / sizeof(int32_t); + if (fsopts->avgfilesize == -1) + fsopts->avgfilesize = AVFILESIZ; + if (fsopts->avgfpdir == -1) + fsopts->avgfpdir = AFPDIR; + + /* calculate size of tree */ + ffs_size_dir(root, fsopts); + fsopts->inodes += ROOTINO; /* include first two inodes */ + + if (debug & DEBUG_FS_VALIDATE) + printf("ffs_validate: size of tree: %lld bytes, %lld inodes\n", + (long long)fsopts->size, (long long)fsopts->inodes); + + /* add requested slop */ + fsopts->size += fsopts->freeblocks; + fsopts->inodes += fsopts->freefiles; + if (fsopts->freefilepc > 0) + fsopts->inodes = + fsopts->inodes * (100 + fsopts->freefilepc) / 100; + if (fsopts->freeblockpc > 0) + fsopts->size = + fsopts->size * (100 + fsopts->freeblockpc) / 100; + + /* add space needed for superblocks */ + /* + * The old SBOFF (SBLOCK_UFS1) is used here because makefs is + * typically used for small filesystems where space matters. + * XXX make this an option. + */ + fsopts->size += (SBLOCK_UFS1 + SBLOCKSIZE) * ncg; + /* add space needed to store inodes, x3 for blockmaps, etc */ + if (fsopts->version == 1) + fsopts->size += ncg * DINODE1_SIZE * + roundup(fsopts->inodes / ncg, fsopts->bsize / DINODE1_SIZE); + else + fsopts->size += ncg * DINODE2_SIZE * + roundup(fsopts->inodes / ncg, fsopts->bsize / DINODE2_SIZE); + + /* add minfree */ + if (fsopts->minfree > 0) + fsopts->size = + fsopts->size * (100 + fsopts->minfree) / 100; + /* + * XXX any other fs slop to add, such as csum's, bitmaps, etc ?? + */ + + if (fsopts->size < fsopts->minsize) /* ensure meets minimum size */ + fsopts->size = fsopts->minsize; + + /* round up to the next block */ + fsopts->size = roundup(fsopts->size, fsopts->bsize); + + /* calculate density if necessary */ + if (fsopts->density == -1) + fsopts->density = fsopts->size / fsopts->inodes + 1; + + if (debug & DEBUG_FS_VALIDATE) { + printf("ffs_validate: after defaults set:\n"); + ffs_dump_fsinfo(fsopts); + printf("ffs_validate: dir %s; %lld bytes, %lld inodes\n", + dir, (long long)fsopts->size, (long long)fsopts->inodes); + } + sectorsize = fsopts->sectorsize; /* XXX - see earlier */ + + /* now check calculated sizes vs requested sizes */ + if (fsopts->maxsize > 0 && fsopts->size > fsopts->maxsize) { + errx(1, "`%s' size of %lld is larger than the maxsize of %lld.", + dir, (long long)fsopts->size, (long long)fsopts->maxsize); + } +} + + +static void +ffs_dump_fsinfo(fsinfo_t *f) +{ + + printf("fsopts at %p\n", f); + + printf("\tsize %lld, inodes %lld, curinode %u\n", + (long long)f->size, (long long)f->inodes, f->curinode); + + printf("\tminsize %lld, maxsize %lld\n", + (long long)f->minsize, (long long)f->maxsize); + printf("\tfree files %lld, freefile %% %d\n", + (long long)f->freefiles, f->freefilepc); + printf("\tfree blocks %lld, freeblock %% %d\n", + (long long)f->freeblocks, f->freeblockpc); + printf("\tneedswap %d, sectorsize %d\n", f->needswap, f->sectorsize); + + printf("\tbsize %d, fsize %d, cpg %d, density %d\n", + f->bsize, f->fsize, f->cpg, f->density); + printf("\tnsectors %d, rpm %d, minfree %d\n", + f->nsectors, f->rpm, f->minfree); + printf("\tmaxcontig %d, maxbpg %d\n", + f->maxcontig, f->maxbpg); + printf("\toptimization %s\n", + f->optimization == FS_OPTSPACE ? "space" : "time"); +} + + +static int +ffs_create_image(const char *image, fsinfo_t *fsopts) +{ +#if HAVE_STRUCT_STATVFS_F_IOSIZE + struct statvfs sfs; +#endif + struct fs *fs; + char *buf; + int i, bufsize; + off_t bufrem; + + assert (image != NULL); + assert (fsopts != NULL); + + /* create image */ + if ((fsopts->fd = open(image, O_RDWR | O_CREAT | O_TRUNC, 0777)) + == -1) { + warn("Can't open `%s' for writing", image); + return (-1); + } + + /* zero image */ +#if HAVE_STRUCT_STATVFS_F_IOSIZE + if (fstatvfs(fsopts->fd, &sfs) == -1) { +#endif + bufsize = 8192; +#if HAVE_STRUCT_STATVFS_F_IOSIZE + warn("can't fstatvfs `%s', using default %d byte chunk", + image, bufsize); + } else + bufsize = sfs.f_iosize; +#endif + bufrem = fsopts->size; + if (debug & DEBUG_FS_CREATE_IMAGE) + printf( + "zero-ing image `%s', %lld sectors, using %d byte chunks\n", + image, (long long)bufrem, bufsize); + if ((buf = calloc(1, bufsize)) == NULL) { + warn("Can't create buffer for sector"); + return (-1); + } + while (bufrem > 0) { + i = write(fsopts->fd, buf, MIN(bufsize, bufrem)); + if (i == -1) { + warn("zeroing image, %lld bytes to go", + (long long)bufrem); + return (-1); + } + bufrem -= i; + } + + /* make the file system */ + if (debug & DEBUG_FS_CREATE_IMAGE) + printf("calling mkfs(\"%s\", ...)\n", image); + fs = ffs_mkfs(image, fsopts); + fsopts->superblock = (void *)fs; + if (debug & DEBUG_FS_CREATE_IMAGE) { + time_t t; + + t = (time_t)((struct fs *)fsopts->superblock)->fs_time; + printf("mkfs returned %p; fs_time %s", + fsopts->superblock, ctime(&t)); + printf("fs totals: nbfree %lld, nffree %lld, nifree %lld, ndir %lld\n", + (long long)fs->fs_cstotal.cs_nbfree, + (long long)fs->fs_cstotal.cs_nffree, + (long long)fs->fs_cstotal.cs_nifree, + (long long)fs->fs_cstotal.cs_ndir); + } + + if (fs->fs_cstotal.cs_nifree + ROOTINO < fsopts->inodes) { + warnx( + "Image file `%s' has %lld free inodes; %lld are required.", + image, + (long long)fs->fs_cstotal.cs_nifree + ROOTINO, + (long long)fsopts->inodes); + return (-1); + } + return (fsopts->fd); +} + + +static void +ffs_size_dir(fsnode *root, fsinfo_t *fsopts) +{ + struct direct tmpdir; + fsnode * node; + int curdirsize, this; + + /* node may be NULL (empty directory) */ + assert(fsopts != NULL); + + if (debug & DEBUG_FS_SIZE_DIR) + printf("ffs_size_dir: entry: bytes %lld inodes %lld\n", + (long long)fsopts->size, (long long)fsopts->inodes); + +#define ADDDIRENT(e) do { \ + tmpdir.d_namlen = strlen((e)); \ + this = DIRSIZ(0, &tmpdir, 0); \ + if (debug & DEBUG_FS_SIZE_DIR_ADD_DIRENT) \ + printf("ADDDIRENT: was: %s (%d) this %d cur %d\n", \ + e, tmpdir.d_namlen, this, curdirsize); \ + if (this + curdirsize > roundup(curdirsize, DIRBLKSIZ)) \ + curdirsize = roundup(curdirsize, DIRBLKSIZ); \ + curdirsize += this; \ + if (debug & DEBUG_FS_SIZE_DIR_ADD_DIRENT) \ + printf("ADDDIRENT: now: %s (%d) this %d cur %d\n", \ + e, tmpdir.d_namlen, this, curdirsize); \ +} while (0); + + /* + * XXX this needs to take into account extra space consumed + * by indirect blocks, etc. + */ +#define ADDSIZE(x) do { \ + fsopts->size += roundup((x), fsopts->fsize); \ +} while (0); + + curdirsize = 0; + for (node = root; node != NULL; node = node->next) { + ADDDIRENT(node->name); + if (FSNODE_EXCLUDE_P(fsopts, node)) + continue; + if (node == root) { /* we're at "." */ + assert(strcmp(node->name, ".") == 0); + ADDDIRENT(".."); + } else if ((node->inode->flags & FI_SIZED) == 0) { + /* don't count duplicate names */ + node->inode->flags |= FI_SIZED; + if (debug & DEBUG_FS_SIZE_DIR_NODE) + printf("ffs_size_dir: `%s' size %lld\n", + node->name, + (long long)node->inode->st.st_size); + fsopts->inodes++; + if (node->type == S_IFREG) + ADDSIZE(node->inode->st.st_size); + if (node->type == S_IFLNK) { + int slen; + + slen = strlen(node->symlink) + 1; + if (slen >= (fsopts->version == 1 ? + MAXSYMLINKLEN_UFS1 : + MAXSYMLINKLEN_UFS2)) + ADDSIZE(slen); + } + } + if (node->type == S_IFDIR) + ffs_size_dir(node->child, fsopts); + } + ADDSIZE(curdirsize); + + if (debug & DEBUG_FS_SIZE_DIR) + printf("ffs_size_dir: exit: size %lld inodes %lld\n", + (long long)fsopts->size, (long long)fsopts->inodes); +} + +static void * +ffs_build_dinode1(struct ufs1_dinode *dinp, dirbuf_t *dbufp, fsnode *cur, + fsnode *root, fsinfo_t *fsopts) +{ + int slen; + void *membuf; + + memset(dinp, 0, sizeof(*dinp)); + dinp->di_mode = cur->inode->st.st_mode; + dinp->di_nlink = cur->inode->nlink; + dinp->di_size = cur->inode->st.st_size; + dinp->di_atime = cur->inode->st.st_atime; + dinp->di_mtime = cur->inode->st.st_mtime; + dinp->di_ctime = cur->inode->st.st_ctime; +#if HAVE_STRUCT_STAT_ST_MTIMENSEC + dinp->di_atimensec = cur->inode->st.st_atimensec; + dinp->di_mtimensec = cur->inode->st.st_mtimensec; + dinp->di_ctimensec = cur->inode->st.st_ctimensec; +#endif +#if HAVE_STRUCT_STAT_ST_FLAGS + dinp->di_flags = cur->inode->st.st_flags; +#endif +#if HAVE_STRUCT_STAT_ST_GEN + dinp->di_gen = cur->inode->st.st_gen; +#endif + dinp->di_uid = cur->inode->st.st_uid; + dinp->di_gid = cur->inode->st.st_gid; + /* not set: di_db, di_ib, di_blocks, di_spare */ + + membuf = NULL; + if (cur == root) { /* "."; write dirbuf */ + membuf = dbufp->buf; + dinp->di_size = dbufp->size; + } else if (S_ISBLK(cur->type) || S_ISCHR(cur->type)) { + dinp->di_size = 0; /* a device */ + dinp->di_rdev = + ufs_rw32(cur->inode->st.st_rdev, fsopts->needswap); + } else if (S_ISLNK(cur->type)) { /* symlink */ + slen = strlen(cur->symlink); + if (slen < MAXSYMLINKLEN_UFS1) { /* short link */ + memcpy(dinp->di_db, cur->symlink, slen); + } else + membuf = cur->symlink; + dinp->di_size = slen; + } + return membuf; +} + +static void * +ffs_build_dinode2(struct ufs2_dinode *dinp, dirbuf_t *dbufp, fsnode *cur, + fsnode *root, fsinfo_t *fsopts) +{ + int slen; + void *membuf; + + memset(dinp, 0, sizeof(*dinp)); + dinp->di_mode = cur->inode->st.st_mode; + dinp->di_nlink = cur->inode->nlink; + dinp->di_size = cur->inode->st.st_size; + dinp->di_atime = cur->inode->st.st_atime; + dinp->di_mtime = cur->inode->st.st_mtime; + dinp->di_ctime = cur->inode->st.st_ctime; +#if HAVE_STRUCT_STAT_ST_MTIMENSEC + dinp->di_atimensec = cur->inode->st.st_atimensec; + dinp->di_mtimensec = cur->inode->st.st_mtimensec; + dinp->di_ctimensec = cur->inode->st.st_ctimensec; +#endif +#if HAVE_STRUCT_STAT_ST_FLAGS + dinp->di_flags = cur->inode->st.st_flags; +#endif +#if HAVE_STRUCT_STAT_ST_GEN + dinp->di_gen = cur->inode->st.st_gen; +#endif +#if HAVE_STRUCT_STAT_BIRTHTIME + dinp->di_birthtime = cur->inode->st.st_birthtime; + dinp->di_birthnsec = cur->inode->st.st_birthtimensec; +#endif + dinp->di_uid = cur->inode->st.st_uid; + dinp->di_gid = cur->inode->st.st_gid; + /* not set: di_db, di_ib, di_blocks, di_spare */ + + membuf = NULL; + if (cur == root) { /* "."; write dirbuf */ + membuf = dbufp->buf; + dinp->di_size = dbufp->size; + } else if (S_ISBLK(cur->type) || S_ISCHR(cur->type)) { + dinp->di_size = 0; /* a device */ + dinp->di_rdev = + ufs_rw64(cur->inode->st.st_rdev, fsopts->needswap); + } else if (S_ISLNK(cur->type)) { /* symlink */ + slen = strlen(cur->symlink); + if (slen < MAXSYMLINKLEN_UFS2) { /* short link */ + memcpy(dinp->di_db, cur->symlink, slen); + } else + membuf = cur->symlink; + dinp->di_size = slen; + } + return membuf; +} + +static int +ffs_populate_dir(const char *dir, fsnode *root, fsinfo_t *fsopts) +{ + fsnode *cur; + dirbuf_t dirbuf; + union dinode din; + void *membuf; + char path[MAXPATHLEN + 1]; + + assert(dir != NULL); + assert(root != NULL); + assert(fsopts != NULL); + + (void)memset(&dirbuf, 0, sizeof(dirbuf)); + + if (debug & DEBUG_FS_POPULATE) + printf("ffs_populate_dir: PASS 1 dir %s node %p\n", dir, root); + + /* + * pass 1: allocate inode numbers, build directory `file' + */ + for (cur = root; cur != NULL; cur = cur->next) { + if (FSNODE_EXCLUDE_P(fsopts, cur)) + continue; + if ((cur->inode->flags & FI_ALLOCATED) == 0) { + cur->inode->flags |= FI_ALLOCATED; + if (cur == root && cur->parent != NULL) + cur->inode->ino = cur->parent->inode->ino; + else { + cur->inode->ino = fsopts->curinode; + fsopts->curinode++; + } + } + ffs_make_dirbuf(&dirbuf, cur->name, cur, fsopts->needswap); + if (cur == root) { /* we're at "."; add ".." */ + ffs_make_dirbuf(&dirbuf, "..", + cur->parent == NULL ? cur : cur->parent->first, + fsopts->needswap); + root->inode->nlink++; /* count my parent's link */ + } else if (cur->child != NULL) + root->inode->nlink++; /* count my child's link */ + + /* + * XXX possibly write file and long symlinks here, + * ensuring that blocks get written before inodes? + * otoh, this isn't a real filesystem, so who + * cares about ordering? :-) + */ + } + if (debug & DEBUG_FS_POPULATE_DIRBUF) + ffs_dump_dirbuf(&dirbuf, dir, fsopts->needswap); + + /* + * pass 2: write out dirbuf, then non-directories at this level + */ + if (debug & DEBUG_FS_POPULATE) + printf("ffs_populate_dir: PASS 2 dir %s\n", dir); + for (cur = root; cur != NULL; cur = cur->next) { + if (FSNODE_EXCLUDE_P(fsopts, cur)) + continue; + if (cur->inode->flags & FI_WRITTEN) + continue; /* skip hard-linked entries */ + cur->inode->flags |= FI_WRITTEN; + + if (snprintf(path, sizeof(path), "%s/%s", dir, cur->name) + >= sizeof(path)) + errx(1, "Pathname too long."); + + if (cur->child != NULL) + continue; /* child creates own inode */ + + /* build on-disk inode */ + if (fsopts->version == 1) + membuf = ffs_build_dinode1(&din.ffs1_din, &dirbuf, cur, + root, fsopts); + else + membuf = ffs_build_dinode2(&din.ffs2_din, &dirbuf, cur, + root, fsopts); + + if (debug & DEBUG_FS_POPULATE_NODE) { + printf("ffs_populate_dir: writing ino %d, %s", + cur->inode->ino, inode_type(cur->type)); + if (cur->inode->nlink > 1) + printf(", nlink %d", cur->inode->nlink); + putchar('\n'); + } + + if (membuf != NULL) { + ffs_write_file(&din, cur->inode->ino, membuf, fsopts); + } else if (S_ISREG(cur->type)) { + ffs_write_file(&din, cur->inode->ino, path, fsopts); + } else { + assert (! S_ISDIR(cur->type)); + ffs_write_inode(&din, cur->inode->ino, fsopts); + } + } + + /* + * pass 3: write out sub-directories + */ + if (debug & DEBUG_FS_POPULATE) + printf("ffs_populate_dir: PASS 3 dir %s\n", dir); + for (cur = root; cur != NULL; cur = cur->next) { + if (FSNODE_EXCLUDE_P(fsopts, cur)) + continue; + if (cur->child == NULL) + continue; + if (snprintf(path, sizeof(path), "%s/%s", dir, cur->name) + >= sizeof(path)) + errx(1, "Pathname too long."); + if (! ffs_populate_dir(path, cur->child, fsopts)) + return (0); + } + + if (debug & DEBUG_FS_POPULATE) + printf("ffs_populate_dir: DONE dir %s\n", dir); + + /* cleanup */ + if (dirbuf.buf != NULL) + free(dirbuf.buf); + return (1); +} + + +static void +ffs_write_file(union dinode *din, uint32_t ino, void *buf, fsinfo_t *fsopts) +{ + int isfile, ffd; + char *fbuf, *p; + off_t bufleft, chunk, offset; + struct inode in; + struct buf * bp; + + assert (din != NULL); + assert (buf != NULL); + assert (fsopts != NULL); + + isfile = S_ISREG(DIP(din, mode)); + fbuf = NULL; + ffd = -1; + + in.i_fs = (struct fs *)fsopts->superblock; + + if (debug & DEBUG_FS_WRITE_FILE) { + printf( + "ffs_write_file: ino %u, din %p, isfile %d, %s, size %lld", + ino, din, isfile, inode_type(DIP(din, mode) & S_IFMT), + (long long)DIP(din, size)); + if (isfile) + printf(", file '%s'\n", (char *)buf); + else + printf(", buffer %p\n", buf); + } + + in.i_number = ino; + in.i_size = DIP(din, size); + if (fsopts->version == 1) + memcpy(&in.i_din.ffs1_din, &din->ffs1_din, + sizeof(in.i_din.ffs1_din)); + else + memcpy(&in.i_din.ffs2_din, &din->ffs2_din, + sizeof(in.i_din.ffs2_din)); + in.i_fd = fsopts->fd; + + if (DIP(din, size) == 0) + goto write_inode_and_leave; /* mmm, cheating */ + + if (isfile) { + if ((fbuf = malloc(fsopts->bsize)) == NULL) + err(1, "Allocating memory for write buffer"); + if ((ffd = open((char *)buf, O_RDONLY, 0444)) == -1) { + warn("Can't open `%s' for reading", (char *)buf); + goto leave_ffs_write_file; + } + } else { + p = buf; + } + + chunk = 0; + for (bufleft = DIP(din, size); bufleft > 0; bufleft -= chunk) { + chunk = MIN(bufleft, fsopts->bsize); + if (isfile) { + if (read(ffd, fbuf, chunk) != chunk) + err(1, "Reading `%s', %lld bytes to go", + (char *)buf, (long long)bufleft); + p = fbuf; + } + offset = DIP(din, size) - bufleft; + if (debug & DEBUG_FS_WRITE_FILE_BLOCK) + printf( + "ffs_write_file: write %p offset %lld size %lld left %lld\n", + p, (long long)offset, + (long long)chunk, (long long)bufleft); + /* + * XXX if holey support is desired, do the check here + * + * XXX might need to write out last bit in fragroundup + * sized chunk. however, ffs_balloc() handles this for us + */ + errno = ffs_balloc(&in, offset, chunk, &bp); + bad_ffs_write_file: + if (errno != 0) + err(1, + "Writing inode %d (%s), bytes %lld + %lld", + ino, + isfile ? (char *)buf : + inode_type(DIP(din, mode) & S_IFMT), + (long long)offset, (long long)chunk); + memcpy(bp->b_data, p, chunk); + errno = bwrite(bp); + if (errno != 0) + goto bad_ffs_write_file; + brelse(bp); + if (!isfile) + p += chunk; + } + + write_inode_and_leave: + ffs_write_inode(&in.i_din, in.i_number, fsopts); + + leave_ffs_write_file: + if (fbuf) + free(fbuf); + if (ffd != -1) + close(ffd); +} + + +static void +ffs_dump_dirbuf(dirbuf_t *dbuf, const char *dir, int needswap) +{ + doff_t i; + struct direct *de; + uint16_t reclen; + + assert (dbuf != NULL); + assert (dir != NULL); + printf("ffs_dump_dirbuf: dir %s size %d cur %d\n", + dir, dbuf->size, dbuf->cur); + + for (i = 0; i < dbuf->size; ) { + de = (struct direct *)(dbuf->buf + i); + reclen = ufs_rw16(de->d_reclen, needswap); + printf( + " inode %4d %7s offset %4d reclen %3d namlen %3d name %s\n", + ufs_rw32(de->d_ino, needswap), + inode_type(DTTOIF(de->d_type)), i, reclen, + de->d_namlen, de->d_name); + i += reclen; + assert(reclen > 0); + } +} + +static void +ffs_make_dirbuf(dirbuf_t *dbuf, const char *name, fsnode *node, int needswap) +{ + struct direct de, *dp; + uint16_t llen, reclen; + char *newbuf; + + assert (dbuf != NULL); + assert (name != NULL); + assert (node != NULL); + /* create direct entry */ + (void)memset(&de, 0, sizeof(de)); + de.d_ino = ufs_rw32(node->inode->ino, needswap); + de.d_type = IFTODT(node->type); + de.d_namlen = (uint8_t)strlen(name); + strcpy(de.d_name, name); + reclen = DIRSIZ(0, &de, needswap); + de.d_reclen = ufs_rw16(reclen, needswap); + + dp = (struct direct *)(dbuf->buf + dbuf->cur); + llen = 0; + if (dp != NULL) + llen = DIRSIZ(0, dp, needswap); + + if (debug & DEBUG_FS_MAKE_DIRBUF) + printf( + "ffs_make_dirbuf: dbuf siz %d cur %d lastlen %d\n" + " ino %d type %d reclen %d namlen %d name %.30s\n", + dbuf->size, dbuf->cur, llen, + ufs_rw32(de.d_ino, needswap), de.d_type, reclen, + de.d_namlen, de.d_name); + + if (reclen + dbuf->cur + llen > roundup(dbuf->size, DIRBLKSIZ)) { + if (debug & DEBUG_FS_MAKE_DIRBUF) + printf("ffs_make_dirbuf: growing buf to %d\n", + dbuf->size + DIRBLKSIZ); + if ((newbuf = realloc(dbuf->buf, dbuf->size + DIRBLKSIZ)) == NULL) + err(1, "Allocating memory for directory buffer"); + dbuf->buf = newbuf; + dbuf->size += DIRBLKSIZ; + memset(dbuf->buf + dbuf->size - DIRBLKSIZ, 0, DIRBLKSIZ); + dbuf->cur = dbuf->size - DIRBLKSIZ; + } else { /* shrink end of previous */ + dp->d_reclen = ufs_rw16(llen,needswap); + dbuf->cur += llen; + } + dp = (struct direct *)(dbuf->buf + dbuf->cur); + memcpy(dp, &de, reclen); + dp->d_reclen = ufs_rw16(dbuf->size - dbuf->cur, needswap); +} + +/* + * cribbed from sys/ufs/ffs/ffs_alloc.c + */ +static void +ffs_write_inode(union dinode *dp, uint32_t ino, const fsinfo_t *fsopts) +{ + char *buf; + struct ufs1_dinode *dp1; + struct ufs2_dinode *dp2, *dip; + struct cg *cgp; + struct fs *fs; + int cg, cgino, i; + daddr_t d; + char sbbuf[FFS_MAXBSIZE]; + int32_t initediblk; + + assert (dp != NULL); + assert (ino > 0); + assert (fsopts != NULL); + + fs = (struct fs *)fsopts->superblock; + cg = ino_to_cg(fs, ino); + cgino = ino % fs->fs_ipg; + if (debug & DEBUG_FS_WRITE_INODE) + printf("ffs_write_inode: din %p ino %u cg %d cgino %d\n", + dp, ino, cg, cgino); + + ffs_rdfs(fsbtodb(fs, cgtod(fs, cg)), (int)fs->fs_cgsize, &sbbuf, + fsopts); + cgp = (struct cg *)sbbuf; + if (!cg_chkmagic(cgp, fsopts->needswap)) + errx(1, "ffs_write_inode: cg %d: bad magic number", cg); + + assert (isclr(cg_inosused(cgp, fsopts->needswap), cgino)); + + buf = malloc(fs->fs_bsize); + if (buf == NULL) + errx(1, "ffs_write_inode: cg %d: can't alloc inode block", cg); + + dp1 = (struct ufs1_dinode *)buf; + dp2 = (struct ufs2_dinode *)buf; + + if (fs->fs_cstotal.cs_nifree == 0) + errx(1, "ffs_write_inode: fs out of inodes for ino %u", + ino); + if (fs->fs_cs(fs, cg).cs_nifree == 0) + errx(1, + "ffs_write_inode: cg %d out of inodes for ino %u", + cg, ino); + setbit(cg_inosused(cgp, fsopts->needswap), cgino); + ufs_add32(cgp->cg_cs.cs_nifree, -1, fsopts->needswap); + fs->fs_cstotal.cs_nifree--; + fs->fs_cs(fs, cg).cs_nifree--; + if (S_ISDIR(DIP(dp, mode))) { + ufs_add32(cgp->cg_cs.cs_ndir, 1, fsopts->needswap); + fs->fs_cstotal.cs_ndir++; + fs->fs_cs(fs, cg).cs_ndir++; + } + + /* + * Initialize inode blocks on the fly for UFS2. + */ + initediblk = ufs_rw32(cgp->cg_initediblk, fsopts->needswap); + if (fsopts->version == 2 && cgino + INOPB(fs) > initediblk && + initediblk < ufs_rw32(cgp->cg_niblk, fsopts->needswap)) { + memset(buf, 0, fs->fs_bsize); + dip = (struct ufs2_dinode *)buf; + srandom(time(NULL)); + for (i = 0; i < INOPB(fs); i++) { + dip->di_gen = random() / 2 + 1; + dip++; + } + ffs_wtfs(fsbtodb(fs, ino_to_fsba(fs, + cg * fs->fs_ipg + initediblk)), + fs->fs_bsize, buf, fsopts); + initediblk += INOPB(fs); + cgp->cg_initediblk = ufs_rw32(initediblk, fsopts->needswap); + } + + + ffs_wtfs(fsbtodb(fs, cgtod(fs, cg)), (int)fs->fs_cgsize, &sbbuf, + fsopts); + + /* now write inode */ + d = fsbtodb(fs, ino_to_fsba(fs, ino)); + ffs_rdfs(d, fs->fs_bsize, buf, fsopts); + if (fsopts->needswap) { + if (fsopts->version == 1) + ffs_dinode1_swap(&dp->ffs1_din, + &dp1[ino_to_fsbo(fs, ino)]); + else + ffs_dinode2_swap(&dp->ffs2_din, + &dp2[ino_to_fsbo(fs, ino)]); + } else { + if (fsopts->version == 1) + dp1[ino_to_fsbo(fs, ino)] = dp->ffs1_din; + else + dp2[ino_to_fsbo(fs, ino)] = dp->ffs2_din; + } + ffs_wtfs(d, fs->fs_bsize, buf, fsopts); + free(buf); +} + +void +panic(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vwarnx(fmt, ap); + va_end(ap); + exit(1); +} diff --git a/ffs/buf.c b/ffs/buf.c new file mode 100644 index 00000000000..3426cec7be5 --- /dev/null +++ b/ffs/buf.c @@ -0,0 +1,228 @@ +/* $NetBSD: buf.c,v 1.12 2004/06/20 22:20:18 jmc Exp $ */ + +/* + * Copyright (c) 2001 Wasabi Systems, Inc. + * All rights reserved. + * + * Written by Luke Mewburn for Wasabi Systems, 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. + * 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 + * Wasabi Systems, Inc. + * 4. The name of Wasabi Systems, Inc. may not be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``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 WASABI SYSTEMS, INC + * 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 HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include +#if defined(__RCSID) && !defined(__lint) +__RCSID("$NetBSD: buf.c,v 1.12 2004/06/20 22:20:18 jmc Exp $"); +#endif /* !__lint */ + +#include +#include + +#include +#include +#include +#include +#include + +#include "makefs.h" + +#include +#include + +#include "ffs/buf.h" +#include "ffs/ufs_inode.h" + +extern int sectorsize; /* XXX: from ffs.c & mkfs.c */ + +TAILQ_HEAD(buftailhead,buf) buftail; + +int +bread(int fd, struct fs *fs, daddr_t blkno, int size, struct buf **bpp) +{ + off_t offset; + ssize_t rv; + + assert (fs != NULL); + assert (bpp != NULL); + + if (debug & DEBUG_BUF_BREAD) + printf("bread: fs %p blkno %lld size %d\n", + fs, (long long)blkno, size); + *bpp = getblk(fd, fs, blkno, size); + offset = (*bpp)->b_blkno * sectorsize; /* XXX */ + if (debug & DEBUG_BUF_BREAD) + printf("bread: bp %p blkno %lld offset %lld bcount %ld\n", + (*bpp), (long long)(*bpp)->b_blkno, (long long) offset, + (*bpp)->b_bcount); + if (lseek((*bpp)->b_fd, offset, SEEK_SET) == -1) + err(1, "bread: lseek %lld (%lld)", + (long long)(*bpp)->b_blkno, (long long)offset); + rv = read((*bpp)->b_fd, (*bpp)->b_data, (*bpp)->b_bcount); + if (debug & DEBUG_BUF_BREAD) + printf("bread: read %ld (%lld) returned %d\n", + (*bpp)->b_bcount, (long long)offset, (int)rv); + if (rv == -1) /* read error */ + err(1, "bread: read %ld (%lld) returned %d", + (*bpp)->b_bcount, (long long)offset, (int)rv); + else if (rv != (*bpp)->b_bcount) /* short read */ + err(1, "bread: read %ld (%lld) returned %d", + (*bpp)->b_bcount, (long long)offset, (int)rv); + else + return (0); +} + +void +brelse(struct buf *bp) +{ + + assert (bp != NULL); + assert (bp->b_data != NULL); + + if (bp->b_lblkno < 0) { + /* + * XXX don't remove any buffers with negative logical block + * numbers (lblkno), so that we retain the mapping + * of negative lblkno -> real blkno that ffs_balloc() + * sets up. + * + * if we instead released these buffers, and implemented + * ufs_strategy() (and ufs_bmaparray()) and called those + * from bread() and bwrite() to convert the lblkno to + * a real blkno, we'd add a lot more code & complexity + * and reading off disk, for little gain, because this + * simple hack works for our purpose. + */ + bp->b_bcount = 0; + return; + } + + TAILQ_REMOVE(&buftail, bp, b_tailq); + free(bp->b_data); + free(bp); +} + +int +bwrite(struct buf *bp) +{ + off_t offset; + ssize_t rv; + + assert (bp != NULL); + offset = bp->b_blkno * sectorsize; /* XXX */ + if (debug & DEBUG_BUF_BWRITE) + printf("bwrite: bp %p blkno %lld offset %lld bcount %ld\n", + bp, (long long)bp->b_blkno, (long long) offset, + bp->b_bcount); + if (lseek(bp->b_fd, offset, SEEK_SET) == -1) + return (errno); + rv = write(bp->b_fd, bp->b_data, bp->b_bcount); + if (debug & DEBUG_BUF_BWRITE) + printf("bwrite: write %ld (offset %lld) returned %lld\n", + bp->b_bcount, (long long)offset, (long long)rv); + if (rv == bp->b_bcount) + return (0); + else if (rv == -1) /* write error */ + return (errno); + else /* short write ? */ + return (EAGAIN); +} + +void +bcleanup(void) +{ + struct buf *bp; + + /* + * XXX this really shouldn't be necessary, but i'm curious to + * know why there's still some buffers lying around that + * aren't brelse()d + */ + + if (TAILQ_EMPTY(&buftail)) + return; + + printf("bcleanup: unflushed buffers:\n"); + TAILQ_FOREACH(bp, &buftail, b_tailq) { + printf("\tlblkno %10lld blkno %10lld count %6ld bufsize %6ld\n", + (long long)bp->b_lblkno, (long long)bp->b_blkno, + bp->b_bcount, bp->b_bufsize); + } + printf("bcleanup: done\n"); +} + +struct buf * +getblk(int fd, struct fs *fs, daddr_t blkno, int size) +{ + static int buftailinitted; + struct buf *bp; + void *n; + + assert (fs != NULL); + if (debug & DEBUG_BUF_GETBLK) + printf("getblk: fs %p blkno %lld size %d\n", fs, + (long long)blkno, size); + + bp = NULL; + if (!buftailinitted) { + if (debug & DEBUG_BUF_GETBLK) + printf("getblk: initialising tailq\n"); + TAILQ_INIT(&buftail); + buftailinitted = 1; + } else { + TAILQ_FOREACH(bp, &buftail, b_tailq) { + if (bp->b_lblkno != blkno) + continue; + break; + } + } + if (bp == NULL) { + if ((bp = calloc(1, sizeof(struct buf))) == NULL) + err(1, "getblk: calloc"); + + bp->b_bufsize = 0; + bp->b_blkno = bp->b_lblkno = blkno; + bp->b_fd = fd; + bp->b_fs = fs; + bp->b_data = NULL; + TAILQ_INSERT_HEAD(&buftail, bp, b_tailq); + } + bp->b_bcount = size; + if (bp->b_data == NULL || bp->b_bcount > bp->b_bufsize) { + n = realloc(bp->b_data, size); + if (n == NULL) + err(1, "getblk: realloc b_data %ld", bp->b_bcount); + bp->b_data = n; + bp->b_bufsize = size; + } + + return (bp); +} diff --git a/ffs/buf.h b/ffs/buf.h new file mode 100644 index 00000000000..4b2e4e9d87a --- /dev/null +++ b/ffs/buf.h @@ -0,0 +1,65 @@ +/* $NetBSD: buf.h,v 1.2 2001/11/02 03:12:49 lukem Exp $ */ + +/* + * Copyright (c) 2001 Wasabi Systems, Inc. + * All rights reserved. + * + * Written by Luke Mewburn for Wasabi Systems, 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. + * 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 + * Wasabi Systems, Inc. + * 4. The name of Wasabi Systems, Inc. may not be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``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 WASABI SYSTEMS, INC + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _FFS_BUF_H +#define _FFS_BUF_H + +#include +#include + +struct buf { + void * b_data; + long b_bufsize; + long b_bcount; + daddr_t b_blkno; + daddr_t b_lblkno; + int b_fd; + struct fs * b_fs; + + TAILQ_ENTRY(buf) b_tailq; +}; + +void bcleanup(void); +int bread(int, struct fs *, daddr_t, int, struct buf **); +void brelse(struct buf *); +int bwrite(struct buf *); +struct buf * getblk(int, struct fs *, daddr_t, int); + +#define bdwrite(bp) bwrite(bp) +#define clrbuf(bp) memset((bp)->b_data, 0, (u_int)(bp)->b_bcount) + +#endif /* _FFS_BUF_H */ diff --git a/ffs/ffs_alloc.c b/ffs/ffs_alloc.c new file mode 100644 index 00000000000..2eef2eb1609 --- /dev/null +++ b/ffs/ffs_alloc.c @@ -0,0 +1,694 @@ +/* $NetBSD: ffs_alloc.c,v 1.14 2004/06/20 22:20:18 jmc Exp $ */ +/* From: NetBSD: ffs_alloc.c,v 1.50 2001/09/06 02:16:01 lukem 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, 1986, 1989, 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. + * 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. + * + * @(#)ffs_alloc.c 8.19 (Berkeley) 7/13/95 + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include +#if defined(__RCSID) && !defined(__lint) +__RCSID("$NetBSD: ffs_alloc.c,v 1.14 2004/06/20 22:20:18 jmc Exp $"); +#endif /* !__lint */ + +#include +#include + +#include + +#include "makefs.h" + +#include +#include +#include + +#include "ffs/buf.h" +#include "ffs/ufs_inode.h" +#include "ffs/ffs_extern.h" + + +static int scanc(u_int, const u_char *, const u_char *, int); + +static daddr_t ffs_alloccg(struct inode *, int, daddr_t, int); +static daddr_t ffs_alloccgblk(struct inode *, struct buf *, daddr_t); +static daddr_t ffs_hashalloc(struct inode *, int, daddr_t, int, + daddr_t (*)(struct inode *, int, daddr_t, int)); +static int32_t ffs_mapsearch(struct fs *, struct cg *, daddr_t, int); + +/* in ffs_tables.c */ +extern const int inside[], around[]; +extern const u_char * const fragtbl[]; + +/* + * Allocate a block in the file system. + * + * The size of the requested block is given, which must be some + * multiple of fs_fsize and <= fs_bsize. + * A preference may be optionally specified. If a preference is given + * the following hierarchy is used to allocate a block: + * 1) allocate the requested block. + * 2) allocate a rotationally optimal block in the same cylinder. + * 3) allocate a block in the same cylinder group. + * 4) quadradically rehash into other cylinder groups, until an + * available block is located. + * If no block preference is given the following hierarchy is used + * to allocate a block: + * 1) allocate a block in the cylinder group that contains the + * inode for the file. + * 2) quadradically rehash into other cylinder groups, until an + * available block is located. + */ +int +ffs_alloc(struct inode *ip, daddr_t lbn, daddr_t bpref, int size, + daddr_t *bnp) +{ + struct fs *fs = ip->i_fs; + daddr_t bno; + int cg; + + *bnp = 0; + if ((u_int)size > fs->fs_bsize || fragoff(fs, size) != 0) { + errx(1, "ffs_alloc: bad size: bsize %d size %d", + fs->fs_bsize, size); + } + if (size == fs->fs_bsize && fs->fs_cstotal.cs_nbfree == 0) + goto nospace; + if (bpref >= fs->fs_size) + bpref = 0; + if (bpref == 0) + cg = ino_to_cg(fs, ip->i_number); + else + cg = dtog(fs, bpref); + bno = ffs_hashalloc(ip, cg, bpref, size, ffs_alloccg); + if (bno > 0) { + if (ip->i_fs->fs_magic == FS_UFS1_MAGIC) + ip->i_ffs1_blocks += size / DEV_BSIZE; + else + ip->i_ffs2_blocks += size / DEV_BSIZE; + *bnp = bno; + return (0); + } +nospace: + return (ENOSPC); +} + +/* + * Select the desired position for the next block in a file. The file is + * logically divided into sections. The first section is composed of the + * direct blocks. Each additional section contains fs_maxbpg blocks. + * + * If no blocks have been allocated in the first section, the policy is to + * request a block in the same cylinder group as the inode that describes + * the file. If no blocks have been allocated in any other section, the + * policy is to place the section in a cylinder group with a greater than + * average number of free blocks. An appropriate cylinder group is found + * by using a rotor that sweeps the cylinder groups. When a new group of + * blocks is needed, the sweep begins in the cylinder group following the + * cylinder group from which the previous allocation was made. The sweep + * continues until a cylinder group with greater than the average number + * of free blocks is found. If the allocation is for the first block in an + * indirect block, the information on the previous allocation is unavailable; + * here a best guess is made based upon the logical block number being + * allocated. + * + * If a section is already partially allocated, the policy is to + * contiguously allocate fs_maxcontig blocks. The end of one of these + * contiguous blocks and the beginning of the next is physically separated + * so that the disk head will be in transit between them for at least + * fs_rotdelay milliseconds. This is to allow time for the processor to + * schedule another I/O transfer. + */ +/* XXX ondisk32 */ +daddr_t +ffs_blkpref_ufs1(struct inode *ip, daddr_t lbn, int indx, int32_t *bap) +{ + struct fs *fs; + int cg; + int avgbfree, startcg; + + fs = ip->i_fs; + if (indx % fs->fs_maxbpg == 0 || bap[indx - 1] == 0) { + if (lbn < NDADDR + NINDIR(fs)) { + cg = ino_to_cg(fs, ip->i_number); + return (fs->fs_fpg * cg + fs->fs_frag); + } + /* + * Find a cylinder with greater than average number of + * unused data blocks. + */ + if (indx == 0 || bap[indx - 1] == 0) + startcg = + ino_to_cg(fs, ip->i_number) + lbn / fs->fs_maxbpg; + else + startcg = dtog(fs, + ufs_rw32(bap[indx - 1], UFS_FSNEEDSWAP(fs)) + 1); + startcg %= fs->fs_ncg; + avgbfree = fs->fs_cstotal.cs_nbfree / fs->fs_ncg; + for (cg = startcg; cg < fs->fs_ncg; cg++) + if (fs->fs_cs(fs, cg).cs_nbfree >= avgbfree) + return (fs->fs_fpg * cg + fs->fs_frag); + for (cg = 0; cg <= startcg; cg++) + if (fs->fs_cs(fs, cg).cs_nbfree >= avgbfree) + return (fs->fs_fpg * cg + fs->fs_frag); + return (0); + } + /* + * We just always try to lay things out contiguously. + */ + return ufs_rw32(bap[indx - 1], UFS_FSNEEDSWAP(fs)) + fs->fs_frag; +} + +daddr_t +ffs_blkpref_ufs2(ip, lbn, indx, bap) + struct inode *ip; + daddr_t lbn; + int indx; + int64_t *bap; +{ + struct fs *fs; + int cg; + int avgbfree, startcg; + + fs = ip->i_fs; + if (indx % fs->fs_maxbpg == 0 || bap[indx - 1] == 0) { + if (lbn < NDADDR + NINDIR(fs)) { + cg = ino_to_cg(fs, ip->i_number); + return (fs->fs_fpg * cg + fs->fs_frag); + } + /* + * Find a cylinder with greater than average number of + * unused data blocks. + */ + if (indx == 0 || bap[indx - 1] == 0) + startcg = + ino_to_cg(fs, ip->i_number) + lbn / fs->fs_maxbpg; + else + startcg = dtog(fs, + ufs_rw64(bap[indx - 1], UFS_FSNEEDSWAP(fs)) + 1); + startcg %= fs->fs_ncg; + avgbfree = fs->fs_cstotal.cs_nbfree / fs->fs_ncg; + for (cg = startcg; cg < fs->fs_ncg; cg++) + if (fs->fs_cs(fs, cg).cs_nbfree >= avgbfree) { + return (fs->fs_fpg * cg + fs->fs_frag); + } + for (cg = 0; cg < startcg; cg++) + if (fs->fs_cs(fs, cg).cs_nbfree >= avgbfree) { + return (fs->fs_fpg * cg + fs->fs_frag); + } + return (0); + } + /* + * We just always try to lay things out contiguously. + */ + return ufs_rw64(bap[indx - 1], UFS_FSNEEDSWAP(fs)) + fs->fs_frag; +} + +/* + * Implement the cylinder overflow algorithm. + * + * The policy implemented by this algorithm is: + * 1) allocate the block in its requested cylinder group. + * 2) quadradically rehash on the cylinder group number. + * 3) brute force search for a free block. + * + * `size': size for data blocks, mode for inodes + */ +/*VARARGS5*/ +static daddr_t +ffs_hashalloc(struct inode *ip, int cg, daddr_t pref, int size, + daddr_t (*allocator)(struct inode *, int, daddr_t, int)) +{ + struct fs *fs; + daddr_t result; + int i, icg = cg; + + fs = ip->i_fs; + /* + * 1: preferred cylinder group + */ + result = (*allocator)(ip, cg, pref, size); + if (result) + return (result); + /* + * 2: quadratic rehash + */ + for (i = 1; i < fs->fs_ncg; i *= 2) { + cg += i; + if (cg >= fs->fs_ncg) + cg -= fs->fs_ncg; + result = (*allocator)(ip, cg, 0, size); + if (result) + return (result); + } + /* + * 3: brute force search + * Note that we start at i == 2, since 0 was checked initially, + * and 1 is always checked in the quadratic rehash. + */ + cg = (icg + 2) % fs->fs_ncg; + for (i = 2; i < fs->fs_ncg; i++) { + result = (*allocator)(ip, cg, 0, size); + if (result) + return (result); + cg++; + if (cg == fs->fs_ncg) + cg = 0; + } + return (0); +} + +/* + * Determine whether a block can be allocated. + * + * Check to see if a block of the appropriate size is available, + * and if it is, allocate it. + */ +static daddr_t +ffs_alloccg(struct inode *ip, int cg, daddr_t bpref, int size) +{ + struct cg *cgp; + struct buf *bp; + daddr_t bno, blkno; + int error, frags, allocsiz, i; + struct fs *fs = ip->i_fs; + const int needswap = UFS_FSNEEDSWAP(fs); + + if (fs->fs_cs(fs, cg).cs_nbfree == 0 && size == fs->fs_bsize) + return (0); + error = bread(ip->i_fd, ip->i_fs, fsbtodb(fs, cgtod(fs, cg)), + (int)fs->fs_cgsize, &bp); + if (error) { + brelse(bp); + return (0); + } + cgp = (struct cg *)bp->b_data; + if (!cg_chkmagic(cgp, needswap) || + (cgp->cg_cs.cs_nbfree == 0 && size == fs->fs_bsize)) { + brelse(bp); + return (0); + } + if (size == fs->fs_bsize) { + bno = ffs_alloccgblk(ip, bp, bpref); + bdwrite(bp); + return (bno); + } + /* + * check to see if any fragments are already available + * allocsiz is the size which will be allocated, hacking + * it down to a smaller size if necessary + */ + frags = numfrags(fs, size); + for (allocsiz = frags; allocsiz < fs->fs_frag; allocsiz++) + if (cgp->cg_frsum[allocsiz] != 0) + break; + if (allocsiz == fs->fs_frag) { + /* + * no fragments were available, so a block will be + * allocated, and hacked up + */ + if (cgp->cg_cs.cs_nbfree == 0) { + brelse(bp); + return (0); + } + bno = ffs_alloccgblk(ip, bp, bpref); + bpref = dtogd(fs, bno); + for (i = frags; i < fs->fs_frag; i++) + setbit(cg_blksfree(cgp, needswap), bpref + i); + i = fs->fs_frag - frags; + ufs_add32(cgp->cg_cs.cs_nffree, i, needswap); + fs->fs_cstotal.cs_nffree += i; + fs->fs_cs(fs, cg).cs_nffree += i; + fs->fs_fmod = 1; + ufs_add32(cgp->cg_frsum[i], 1, needswap); + bdwrite(bp); + return (bno); + } + bno = ffs_mapsearch(fs, cgp, bpref, allocsiz); + for (i = 0; i < frags; i++) + clrbit(cg_blksfree(cgp, needswap), bno + i); + ufs_add32(cgp->cg_cs.cs_nffree, -frags, needswap); + fs->fs_cstotal.cs_nffree -= frags; + fs->fs_cs(fs, cg).cs_nffree -= frags; + fs->fs_fmod = 1; + ufs_add32(cgp->cg_frsum[allocsiz], -1, needswap); + if (frags != allocsiz) + ufs_add32(cgp->cg_frsum[allocsiz - frags], 1, needswap); + blkno = cg * fs->fs_fpg + bno; + bdwrite(bp); + return blkno; +} + +/* + * Allocate a block in a cylinder group. + * + * This algorithm implements the following policy: + * 1) allocate the requested block. + * 2) allocate a rotationally optimal block in the same cylinder. + * 3) allocate the next available block on the block rotor for the + * specified cylinder group. + * Note that this routine only allocates fs_bsize blocks; these + * blocks may be fragmented by the routine that allocates them. + */ +static daddr_t +ffs_alloccgblk(struct inode *ip, struct buf *bp, daddr_t bpref) +{ + struct cg *cgp; + daddr_t blkno; + int32_t bno; + struct fs *fs = ip->i_fs; + const int needswap = UFS_FSNEEDSWAP(fs); + u_int8_t *blksfree; + + cgp = (struct cg *)bp->b_data; + blksfree = cg_blksfree(cgp, needswap); + if (bpref == 0 || dtog(fs, bpref) != ufs_rw32(cgp->cg_cgx, needswap)) { + bpref = ufs_rw32(cgp->cg_rotor, needswap); + } else { + bpref = blknum(fs, bpref); + bno = dtogd(fs, bpref); + /* + * if the requested block is available, use it + */ + if (ffs_isblock(fs, blksfree, fragstoblks(fs, bno))) + goto gotit; + } + /* + * Take the next available one in this cylinder group. + */ + bno = ffs_mapsearch(fs, cgp, bpref, (int)fs->fs_frag); + if (bno < 0) + return (0); + cgp->cg_rotor = ufs_rw32(bno, needswap); +gotit: + blkno = fragstoblks(fs, bno); + ffs_clrblock(fs, blksfree, (long)blkno); + ffs_clusteracct(fs, cgp, blkno, -1); + ufs_add32(cgp->cg_cs.cs_nbfree, -1, needswap); + fs->fs_cstotal.cs_nbfree--; + fs->fs_cs(fs, ufs_rw32(cgp->cg_cgx, needswap)).cs_nbfree--; + fs->fs_fmod = 1; + blkno = ufs_rw32(cgp->cg_cgx, needswap) * fs->fs_fpg + bno; + return (blkno); +} + +/* + * Free a block or fragment. + * + * The specified block or fragment is placed back in the + * free map. If a fragment is deallocated, a possible + * block reassembly is checked. + */ +void +ffs_blkfree(struct inode *ip, daddr_t bno, long size) +{ + struct cg *cgp; + struct buf *bp; + int32_t fragno, cgbno; + int i, error, cg, blk, frags, bbase; + struct fs *fs = ip->i_fs; + const int needswap = UFS_FSNEEDSWAP(fs); + + if ((u_int)size > fs->fs_bsize || fragoff(fs, size) != 0 || + fragnum(fs, bno) + numfrags(fs, size) > fs->fs_frag) { + errx(1, "blkfree: bad size: bno %lld bsize %d size %ld", + (long long)bno, fs->fs_bsize, size); + } + cg = dtog(fs, bno); + if (bno >= fs->fs_size) { + warnx("bad block %lld, ino %d", (long long)bno, ip->i_number); + return; + } + error = bread(ip->i_fd, ip->i_fs, fsbtodb(fs, cgtod(fs, cg)), + (int)fs->fs_cgsize, &bp); + if (error) { + brelse(bp); + return; + } + cgp = (struct cg *)bp->b_data; + if (!cg_chkmagic(cgp, needswap)) { + brelse(bp); + return; + } + cgbno = dtogd(fs, bno); + if (size == fs->fs_bsize) { + fragno = fragstoblks(fs, cgbno); + if (!ffs_isfreeblock(fs, cg_blksfree(cgp, needswap), fragno)) { + errx(1, "blkfree: freeing free block %lld", + (long long)bno); + } + ffs_setblock(fs, cg_blksfree(cgp, needswap), fragno); + ffs_clusteracct(fs, cgp, fragno, 1); + ufs_add32(cgp->cg_cs.cs_nbfree, 1, needswap); + fs->fs_cstotal.cs_nbfree++; + fs->fs_cs(fs, cg).cs_nbfree++; + } else { + bbase = cgbno - fragnum(fs, cgbno); + /* + * decrement the counts associated with the old frags + */ + blk = blkmap(fs, cg_blksfree(cgp, needswap), bbase); + ffs_fragacct(fs, blk, cgp->cg_frsum, -1, needswap); + /* + * deallocate the fragment + */ + frags = numfrags(fs, size); + for (i = 0; i < frags; i++) { + if (isset(cg_blksfree(cgp, needswap), cgbno + i)) { + errx(1, "blkfree: freeing free frag: block %lld", + (long long)(cgbno + i)); + } + setbit(cg_blksfree(cgp, needswap), cgbno + i); + } + ufs_add32(cgp->cg_cs.cs_nffree, i, needswap); + fs->fs_cstotal.cs_nffree += i; + fs->fs_cs(fs, cg).cs_nffree += i; + /* + * add back in counts associated with the new frags + */ + blk = blkmap(fs, cg_blksfree(cgp, needswap), bbase); + ffs_fragacct(fs, blk, cgp->cg_frsum, 1, needswap); + /* + * if a complete block has been reassembled, account for it + */ + fragno = fragstoblks(fs, bbase); + if (ffs_isblock(fs, cg_blksfree(cgp, needswap), fragno)) { + ufs_add32(cgp->cg_cs.cs_nffree, -fs->fs_frag, needswap); + fs->fs_cstotal.cs_nffree -= fs->fs_frag; + fs->fs_cs(fs, cg).cs_nffree -= fs->fs_frag; + ffs_clusteracct(fs, cgp, fragno, 1); + ufs_add32(cgp->cg_cs.cs_nbfree, 1, needswap); + fs->fs_cstotal.cs_nbfree++; + fs->fs_cs(fs, cg).cs_nbfree++; + } + } + fs->fs_fmod = 1; + bdwrite(bp); +} + + +static int +scanc(u_int size, const u_char *cp, const u_char table[], int mask) +{ + const u_char *end = &cp[size]; + + while (cp < end && (table[*cp] & mask) == 0) + cp++; + return (end - cp); +} + +/* + * Find a block of the specified size in the specified cylinder group. + * + * It is a panic if a request is made to find a block if none are + * available. + */ +static int32_t +ffs_mapsearch(struct fs *fs, struct cg *cgp, daddr_t bpref, int allocsiz) +{ + int32_t bno; + int start, len, loc, i; + int blk, field, subfield, pos; + int ostart, olen; + const int needswap = UFS_FSNEEDSWAP(fs); + + /* + * find the fragment by searching through the free block + * map for an appropriate bit pattern + */ + if (bpref) + start = dtogd(fs, bpref) / NBBY; + else + start = ufs_rw32(cgp->cg_frotor, needswap) / NBBY; + len = howmany(fs->fs_fpg, NBBY) - start; + ostart = start; + olen = len; + loc = scanc((u_int)len, + (const u_char *)&cg_blksfree(cgp, needswap)[start], + (const u_char *)fragtbl[fs->fs_frag], + (1 << (allocsiz - 1 + (fs->fs_frag % NBBY)))); + if (loc == 0) { + len = start + 1; + start = 0; + loc = scanc((u_int)len, + (const u_char *)&cg_blksfree(cgp, needswap)[0], + (const u_char *)fragtbl[fs->fs_frag], + (1 << (allocsiz - 1 + (fs->fs_frag % NBBY)))); + if (loc == 0) { + errx(1, + "ffs_alloccg: map corrupted: start %d len %d offset %d %ld", + ostart, olen, + ufs_rw32(cgp->cg_freeoff, needswap), + (long)cg_blksfree(cgp, needswap) - (long)cgp); + /* NOTREACHED */ + } + } + bno = (start + len - loc) * NBBY; + cgp->cg_frotor = ufs_rw32(bno, needswap); + /* + * found the byte in the map + * sift through the bits to find the selected frag + */ + for (i = bno + NBBY; bno < i; bno += fs->fs_frag) { + blk = blkmap(fs, cg_blksfree(cgp, needswap), bno); + blk <<= 1; + field = around[allocsiz]; + subfield = inside[allocsiz]; + for (pos = 0; pos <= fs->fs_frag - allocsiz; pos++) { + if ((blk & field) == subfield) + return (bno + pos); + field <<= 1; + subfield <<= 1; + } + } + errx(1, "ffs_alloccg: block not in map: bno %lld", (long long)bno); + return (-1); +} + +/* + * Update the cluster map because of an allocation or free. + * + * Cnt == 1 means free; cnt == -1 means allocating. + */ +void +ffs_clusteracct(struct fs *fs, struct cg *cgp, int32_t blkno, int cnt) +{ + int32_t *sump; + int32_t *lp; + u_char *freemapp, *mapp; + int i, start, end, forw, back, map, bit; + const int needswap = UFS_FSNEEDSWAP(fs); + + if (fs->fs_contigsumsize <= 0) + return; + freemapp = cg_clustersfree(cgp, needswap); + sump = cg_clustersum(cgp, needswap); + /* + * Allocate or clear the actual block. + */ + if (cnt > 0) + setbit(freemapp, blkno); + else + clrbit(freemapp, blkno); + /* + * Find the size of the cluster going forward. + */ + start = blkno + 1; + end = start + fs->fs_contigsumsize; + if (end >= ufs_rw32(cgp->cg_nclusterblks, needswap)) + end = ufs_rw32(cgp->cg_nclusterblks, needswap); + mapp = &freemapp[start / NBBY]; + map = *mapp++; + bit = 1 << (start % NBBY); + for (i = start; i < end; i++) { + if ((map & bit) == 0) + break; + if ((i & (NBBY - 1)) != (NBBY - 1)) { + bit <<= 1; + } else { + map = *mapp++; + bit = 1; + } + } + forw = i - start; + /* + * Find the size of the cluster going backward. + */ + start = blkno - 1; + end = start - fs->fs_contigsumsize; + if (end < 0) + end = -1; + mapp = &freemapp[start / NBBY]; + map = *mapp--; + bit = 1 << (start % NBBY); + for (i = start; i > end; i--) { + if ((map & bit) == 0) + break; + if ((i & (NBBY - 1)) != 0) { + bit >>= 1; + } else { + map = *mapp--; + bit = 1 << (NBBY - 1); + } + } + back = start - i; + /* + * Account for old cluster and the possibly new forward and + * back clusters. + */ + i = back + forw + 1; + if (i > fs->fs_contigsumsize) + i = fs->fs_contigsumsize; + ufs_add32(sump[i], cnt, needswap); + if (back > 0) + ufs_add32(sump[back], -cnt, needswap); + if (forw > 0) + ufs_add32(sump[forw], -cnt, needswap); + + /* + * Update cluster summary information. + */ + lp = &sump[fs->fs_contigsumsize]; + for (i = fs->fs_contigsumsize; i > 0; i--) + if (ufs_rw32(*lp--, needswap) > 0) + break; + fs->fs_maxcluster[ufs_rw32(cgp->cg_cgx, needswap)] = i; +} diff --git a/ffs/ffs_balloc.c b/ffs/ffs_balloc.c new file mode 100644 index 00000000000..8761e459e23 --- /dev/null +++ b/ffs/ffs_balloc.c @@ -0,0 +1,584 @@ +/* $NetBSD: ffs_balloc.c,v 1.13 2004/06/20 22:20:18 jmc Exp $ */ +/* From NetBSD: ffs_balloc.c,v 1.25 2001/08/08 08:36:36 lukem Exp */ + +/* + * Copyright (c) 1982, 1986, 1989, 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. + * 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. + * + * @(#)ffs_balloc.c 8.8 (Berkeley) 6/16/95 + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include +#if defined(__RCSID) && !defined(__lint) +__RCSID("$NetBSD: ffs_balloc.c,v 1.13 2004/06/20 22:20:18 jmc Exp $"); +#endif /* !__lint */ + +#include +#include + +#include +#include +#include +#include +#include + +#include "makefs.h" + +#include +#include +#include + +#include "ffs/buf.h" +#include "ffs/ufs_inode.h" +#include "ffs/ffs_extern.h" + +static int ffs_balloc_ufs1(struct inode *, off_t, int, struct buf **); +static int ffs_balloc_ufs2(struct inode *, off_t, int, struct buf **); + +/* + * Balloc defines the structure of file system storage + * by allocating the physical blocks on a device given + * the inode and the logical block number in a file. + * + * Assume: flags == B_SYNC | B_CLRBUF + */ + +int +ffs_balloc(struct inode *ip, off_t offset, int bufsize, struct buf **bpp) +{ + if (ip->i_fs->fs_magic == FS_UFS2_MAGIC) + return ffs_balloc_ufs2(ip, offset, bufsize, bpp); + else + return ffs_balloc_ufs1(ip, offset, bufsize, bpp); +} + +static int +ffs_balloc_ufs1(struct inode *ip, off_t offset, int bufsize, struct buf **bpp) +{ + daddr_t lbn, lastlbn; + int size; + int32_t nb; + struct buf *bp, *nbp; + struct fs *fs = ip->i_fs; + struct indir indirs[NIADDR + 2]; + daddr_t newb, pref; + int32_t *bap; + int osize, nsize, num, i, error; + int32_t *allocblk, allociblk[NIADDR + 1]; + int32_t *allocib; + const int needswap = UFS_FSNEEDSWAP(fs); + + lbn = lblkno(fs, offset); + size = blkoff(fs, offset) + bufsize; + if (bpp != NULL) { + *bpp = NULL; + } + + assert(size <= fs->fs_bsize); + if (lbn < 0) + return (EFBIG); + + /* + * If the next write will extend the file into a new block, + * and the file is currently composed of a fragment + * this fragment has to be extended to be a full block. + */ + + lastlbn = lblkno(fs, ip->i_ffs1_size); + if (lastlbn < NDADDR && lastlbn < lbn) { + nb = lastlbn; + osize = blksize(fs, ip, nb); + if (osize < fs->fs_bsize && osize > 0) { + warnx("need to ffs_realloccg; not supported!"); + abort(); + } + } + + /* + * The first NDADDR blocks are direct blocks + */ + + if (lbn < NDADDR) { + nb = ufs_rw32(ip->i_ffs1_db[lbn], needswap); + if (nb != 0 && ip->i_ffs1_size >= lblktosize(fs, lbn + 1)) { + + /* + * The block is an already-allocated direct block + * and the file already extends past this block, + * thus this must be a whole block. + * Just read the block (if requested). + */ + + if (bpp != NULL) { + error = bread(ip->i_fd, ip->i_fs, lbn, + fs->fs_bsize, bpp); + if (error) { + brelse(*bpp); + return (error); + } + } + return (0); + } + if (nb != 0) { + + /* + * Consider need to reallocate a fragment. + */ + + osize = fragroundup(fs, blkoff(fs, ip->i_ffs1_size)); + nsize = fragroundup(fs, size); + if (nsize <= osize) { + + /* + * The existing block is already + * at least as big as we want. + * Just read the block (if requested). + */ + + if (bpp != NULL) { + error = bread(ip->i_fd, ip->i_fs, lbn, + osize, bpp); + if (error) { + brelse(*bpp); + return (error); + } + } + return 0; + } else { + warnx("need to ffs_realloccg; not supported!"); + abort(); + } + } else { + + /* + * the block was not previously allocated, + * allocate a new block or fragment. + */ + + if (ip->i_ffs1_size < lblktosize(fs, lbn + 1)) + nsize = fragroundup(fs, size); + else + nsize = fs->fs_bsize; + error = ffs_alloc(ip, lbn, + ffs_blkpref_ufs1(ip, lbn, (int)lbn, + &ip->i_ffs1_db[0]), + nsize, &newb); + if (error) + return (error); + if (bpp != NULL) { + bp = getblk(ip->i_fd, ip->i_fs, lbn, nsize); + bp->b_blkno = fsbtodb(fs, newb); + clrbuf(bp); + *bpp = bp; + } + } + ip->i_ffs1_db[lbn] = ufs_rw32((int32_t)newb, needswap); + return (0); + } + + /* + * Determine the number of levels of indirection. + */ + + pref = 0; + if ((error = ufs_getlbns(ip, lbn, indirs, &num)) != 0) + return (error); + + if (num < 1) { + warnx("ffs_balloc: ufs_getlbns returned indirect block"); + abort(); + } + + /* + * Fetch the first indirect block allocating if necessary. + */ + + --num; + nb = ufs_rw32(ip->i_ffs1_ib[indirs[0].in_off], needswap); + allocib = NULL; + allocblk = allociblk; + if (nb == 0) { + pref = ffs_blkpref_ufs1(ip, lbn, 0, (int32_t *)0); + error = ffs_alloc(ip, lbn, pref, (int)fs->fs_bsize, &newb); + if (error) + return error; + nb = newb; + *allocblk++ = nb; + bp = getblk(ip->i_fd, ip->i_fs, indirs[1].in_lbn, fs->fs_bsize); + bp->b_blkno = fsbtodb(fs, nb); + clrbuf(bp); + /* + * Write synchronously so that indirect blocks + * never point at garbage. + */ + if ((error = bwrite(bp)) != 0) + return error; + allocib = &ip->i_ffs1_ib[indirs[0].in_off]; + *allocib = ufs_rw32((int32_t)nb, needswap); + } + + /* + * Fetch through the indirect blocks, allocating as necessary. + */ + + for (i = 1;;) { + error = bread(ip->i_fd, ip->i_fs, indirs[i].in_lbn, + fs->fs_bsize, &bp); + if (error) { + brelse(bp); + return error; + } + bap = (int32_t *)bp->b_data; + nb = ufs_rw32(bap[indirs[i].in_off], needswap); + if (i == num) + break; + i++; + if (nb != 0) { + brelse(bp); + continue; + } + if (pref == 0) + pref = ffs_blkpref_ufs1(ip, lbn, 0, (int32_t *)0); + error = ffs_alloc(ip, lbn, pref, (int)fs->fs_bsize, &newb); + if (error) { + brelse(bp); + return error; + } + nb = newb; + *allocblk++ = nb; + nbp = getblk(ip->i_fd, ip->i_fs, indirs[i].in_lbn, + fs->fs_bsize); + nbp->b_blkno = fsbtodb(fs, nb); + clrbuf(nbp); + /* + * Write synchronously so that indirect blocks + * never point at garbage. + */ + + if ((error = bwrite(nbp)) != 0) { + brelse(bp); + return error; + } + bap[indirs[i - 1].in_off] = ufs_rw32(nb, needswap); + + bwrite(bp); + } + + /* + * Get the data block, allocating if necessary. + */ + + if (nb == 0) { + pref = ffs_blkpref_ufs1(ip, lbn, indirs[num].in_off, &bap[0]); + error = ffs_alloc(ip, lbn, pref, (int)fs->fs_bsize, &newb); + if (error) { + brelse(bp); + return error; + } + nb = newb; + *allocblk++ = nb; + if (bpp != NULL) { + nbp = getblk(ip->i_fd, ip->i_fs, lbn, fs->fs_bsize); + nbp->b_blkno = fsbtodb(fs, nb); + clrbuf(nbp); + *bpp = nbp; + } + bap[indirs[num].in_off] = ufs_rw32(nb, needswap); + + /* + * If required, write synchronously, otherwise use + * delayed write. + */ + bwrite(bp); + return (0); + } + brelse(bp); + if (bpp != NULL) { + error = bread(ip->i_fd, ip->i_fs, lbn, (int)fs->fs_bsize, &nbp); + if (error) { + brelse(nbp); + return error; + } + *bpp = nbp; + } + return (0); +} + +static int +ffs_balloc_ufs2(struct inode *ip, off_t offset, int bufsize, struct buf **bpp) +{ + daddr_t lbn, lastlbn; + int size; + struct buf *bp, *nbp; + struct fs *fs = ip->i_fs; + struct indir indirs[NIADDR + 2]; + daddr_t newb, pref, nb; + int64_t *bap; + int osize, nsize, num, i, error; + int64_t *allocblk, allociblk[NIADDR + 1]; + int64_t *allocib; + const int needswap = UFS_FSNEEDSWAP(fs); + + lbn = lblkno(fs, offset); + size = blkoff(fs, offset) + bufsize; + if (bpp != NULL) { + *bpp = NULL; + } + + assert(size <= fs->fs_bsize); + if (lbn < 0) + return (EFBIG); + + /* + * If the next write will extend the file into a new block, + * and the file is currently composed of a fragment + * this fragment has to be extended to be a full block. + */ + + lastlbn = lblkno(fs, ip->i_ffs2_size); + if (lastlbn < NDADDR && lastlbn < lbn) { + nb = lastlbn; + osize = blksize(fs, ip, nb); + if (osize < fs->fs_bsize && osize > 0) { + warnx("need to ffs_realloccg; not supported!"); + abort(); + } + } + + /* + * The first NDADDR blocks are direct blocks + */ + + if (lbn < NDADDR) { + nb = ufs_rw64(ip->i_ffs2_db[lbn], needswap); + if (nb != 0 && ip->i_ffs2_size >= lblktosize(fs, lbn + 1)) { + + /* + * The block is an already-allocated direct block + * and the file already extends past this block, + * thus this must be a whole block. + * Just read the block (if requested). + */ + + if (bpp != NULL) { + error = bread(ip->i_fd, ip->i_fs, lbn, + fs->fs_bsize, bpp); + if (error) { + brelse(*bpp); + return (error); + } + } + return (0); + } + if (nb != 0) { + + /* + * Consider need to reallocate a fragment. + */ + + osize = fragroundup(fs, blkoff(fs, ip->i_ffs2_size)); + nsize = fragroundup(fs, size); + if (nsize <= osize) { + + /* + * The existing block is already + * at least as big as we want. + * Just read the block (if requested). + */ + + if (bpp != NULL) { + error = bread(ip->i_fd, ip->i_fs, lbn, + osize, bpp); + if (error) { + brelse(*bpp); + return (error); + } + } + return 0; + } else { + warnx("need to ffs_realloccg; not supported!"); + abort(); + } + } else { + + /* + * the block was not previously allocated, + * allocate a new block or fragment. + */ + + if (ip->i_ffs2_size < lblktosize(fs, lbn + 1)) + nsize = fragroundup(fs, size); + else + nsize = fs->fs_bsize; + error = ffs_alloc(ip, lbn, + ffs_blkpref_ufs2(ip, lbn, (int)lbn, + &ip->i_ffs2_db[0]), + nsize, &newb); + if (error) + return (error); + if (bpp != NULL) { + bp = getblk(ip->i_fd, ip->i_fs, lbn, nsize); + bp->b_blkno = fsbtodb(fs, newb); + clrbuf(bp); + *bpp = bp; + } + } + ip->i_ffs2_db[lbn] = ufs_rw64(newb, needswap); + return (0); + } + + /* + * Determine the number of levels of indirection. + */ + + pref = 0; + if ((error = ufs_getlbns(ip, lbn, indirs, &num)) != 0) + return (error); + + if (num < 1) { + warnx("ffs_balloc: ufs_getlbns returned indirect block"); + abort(); + } + + /* + * Fetch the first indirect block allocating if necessary. + */ + + --num; + nb = ufs_rw64(ip->i_ffs2_ib[indirs[0].in_off], needswap); + allocib = NULL; + allocblk = allociblk; + if (nb == 0) { + pref = ffs_blkpref_ufs2(ip, lbn, 0, (int64_t *)0); + error = ffs_alloc(ip, lbn, pref, (int)fs->fs_bsize, &newb); + if (error) + return error; + nb = newb; + *allocblk++ = nb; + bp = getblk(ip->i_fd, ip->i_fs, indirs[1].in_lbn, fs->fs_bsize); + bp->b_blkno = fsbtodb(fs, nb); + clrbuf(bp); + /* + * Write synchronously so that indirect blocks + * never point at garbage. + */ + if ((error = bwrite(bp)) != 0) + return error; + allocib = &ip->i_ffs2_ib[indirs[0].in_off]; + *allocib = ufs_rw64(nb, needswap); + } + + /* + * Fetch through the indirect blocks, allocating as necessary. + */ + + for (i = 1;;) { + error = bread(ip->i_fd, ip->i_fs, indirs[i].in_lbn, + fs->fs_bsize, &bp); + if (error) { + brelse(bp); + return error; + } + bap = (int64_t *)bp->b_data; + nb = ufs_rw64(bap[indirs[i].in_off], needswap); + if (i == num) + break; + i++; + if (nb != 0) { + brelse(bp); + continue; + } + if (pref == 0) + pref = ffs_blkpref_ufs2(ip, lbn, 0, (int64_t *)0); + error = ffs_alloc(ip, lbn, pref, (int)fs->fs_bsize, &newb); + if (error) { + brelse(bp); + return error; + } + nb = newb; + *allocblk++ = nb; + nbp = getblk(ip->i_fd, ip->i_fs, indirs[i].in_lbn, + fs->fs_bsize); + nbp->b_blkno = fsbtodb(fs, nb); + clrbuf(nbp); + /* + * Write synchronously so that indirect blocks + * never point at garbage. + */ + + if ((error = bwrite(nbp)) != 0) { + brelse(bp); + return error; + } + bap[indirs[i - 1].in_off] = ufs_rw64(nb, needswap); + + bwrite(bp); + } + + /* + * Get the data block, allocating if necessary. + */ + + if (nb == 0) { + pref = ffs_blkpref_ufs2(ip, lbn, indirs[num].in_off, &bap[0]); + error = ffs_alloc(ip, lbn, pref, (int)fs->fs_bsize, &newb); + if (error) { + brelse(bp); + return error; + } + nb = newb; + *allocblk++ = nb; + if (bpp != NULL) { + nbp = getblk(ip->i_fd, ip->i_fs, lbn, fs->fs_bsize); + nbp->b_blkno = fsbtodb(fs, nb); + clrbuf(nbp); + *bpp = nbp; + } + bap[indirs[num].in_off] = ufs_rw64(nb, needswap); + + /* + * If required, write synchronously, otherwise use + * delayed write. + */ + bwrite(bp); + return (0); + } + brelse(bp); + if (bpp != NULL) { + error = bread(ip->i_fd, ip->i_fs, lbn, (int)fs->fs_bsize, &nbp); + if (error) { + brelse(nbp); + return error; + } + *bpp = nbp; + } + return (0); +} diff --git a/ffs/ffs_extern.h b/ffs/ffs_extern.h new file mode 100644 index 00000000000..53e9fe2ee12 --- /dev/null +++ b/ffs/ffs_extern.h @@ -0,0 +1,76 @@ +/* $NetBSD: ffs_extern.h,v 1.6 2003/08/07 11:25:33 agc Exp $ */ +/* From: NetBSD: ffs_extern.h,v 1.19 2001/08/17 02:18:48 lukem Exp */ + +/*- + * Copyright (c) 1991, 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. + * 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. + * + * @(#)ffs_extern.h 8.6 (Berkeley) 3/30/95 + */ + +#include "ffs/buf.h" + +/* + * Structure used to pass around logical block paths generated by + * ufs_getlbns and used by truncate and bmap code. + */ +struct indir { + daddr_t in_lbn; /* Logical block number. */ + int in_off; /* Offset in buffer. */ + int in_exists; /* Flag if the block exists. */ +}; + + /* ffs.c */ +void panic(const char *, ...) + __attribute__((__noreturn__,__format__(__printf__,1,2))); + + /* ffs_alloc.c */ +int ffs_alloc(struct inode *, daddr_t, daddr_t, int, daddr_t *); +daddr_t ffs_blkpref_ufs1(struct inode *, daddr_t, int, int32_t *); +daddr_t ffs_blkpref_ufs2(struct inode *, daddr_t, int, int64_t *); +void ffs_blkfree(struct inode *, daddr_t, long); +void ffs_clusteracct(struct fs *, struct cg *, int32_t, int); + + /* ffs_balloc.c */ +int ffs_balloc(struct inode *, off_t, int, struct buf **); + + /* ffs_bswap.c */ +void ffs_sb_swap(struct fs*, struct fs *); +void ffs_dinode1_swap(struct ufs1_dinode *, struct ufs1_dinode *); +void ffs_dinode2_swap(struct ufs2_dinode *, struct ufs2_dinode *); +void ffs_csum_swap(struct csum *, struct csum *, int); +void ffs_cg_swap(struct cg *, struct cg *, struct fs *); + + /* ffs_subr.c */ +void ffs_fragacct(struct fs *, int, int32_t[], int, int); +int ffs_isblock(struct fs *, u_char *, int32_t); +int ffs_isfreeblock(struct fs *, u_char *, int32_t); +void ffs_clrblock(struct fs *, u_char *, int32_t); +void ffs_setblock(struct fs *, u_char *, int32_t); + + /* ufs_bmap.c */ +int ufs_getlbns(struct inode *, daddr_t, struct indir *, int *); diff --git a/ffs/mkfs.c b/ffs/mkfs.c new file mode 100644 index 00000000000..1a4c0666538 --- /dev/null +++ b/ffs/mkfs.c @@ -0,0 +1,846 @@ +/* $NetBSD: mkfs.c,v 1.20 2004/06/24 22:30:13 lukem 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) 1980, 1989, 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. + * 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. + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)mkfs.c 8.11 (Berkeley) 5/3/95"; +#else +#ifdef __RCSID +__RCSID("$NetBSD: mkfs.c,v 1.20 2004/06/24 22:30:13 lukem Exp $"); +#endif +#endif +#endif /* not lint */ + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "makefs.h" + +#include +#include +#include + +#include "ffs/ufs_inode.h" +#include "ffs/ffs_extern.h" +#include "ffs/newfs_extern.h" + +static void initcg(int, time_t, const fsinfo_t *); +static int ilog2(int); + +static int count_digits(int); + +/* + * make file system for cylinder-group style file systems + */ +#define UMASK 0755 +#define POWEROF2(num) (((num) & ((num) - 1)) == 0) + +union { + struct fs fs; + char pad[SBLOCKSIZE]; +} fsun; +#define sblock fsun.fs +struct csum *fscs; + +union { + struct cg cg; + char pad[FFS_MAXBSIZE]; +} cgun; +#define acg cgun.cg + +char *iobuf; +int iobufsize; + +char writebuf[FFS_MAXBSIZE]; + +static int Oflag; /* format as an 4.3BSD file system */ +static int64_t fssize; /* file system size */ +static int sectorsize; /* bytes/sector */ +static int fsize; /* fragment size */ +static int bsize; /* block size */ +static int maxbsize; /* maximum clustering */ +static int maxblkspercg; +static int minfree; /* free space threshold */ +static int opt; /* optimization preference (space or time) */ +static int density; /* number of bytes per inode */ +static int maxcontig; /* max contiguous blocks to allocate */ +static int maxbpg; /* maximum blocks per file in a cyl group */ +static int bbsize; /* boot block size */ +static int sbsize; /* superblock size */ +static int avgfilesize; /* expected average file size */ +static int avgfpdir; /* expected number of files per directory */ + +struct fs * +ffs_mkfs(const char *fsys, const fsinfo_t *fsopts) +{ + int fragsperinode, optimalfpg, origdensity, minfpg, lastminfpg; + int32_t cylno, i, csfrags; + long long sizepb; + void *space; + int size, blks; + int nprintcols, printcolwidth; + + Oflag = fsopts->version; + fssize = fsopts->size / fsopts->sectorsize; + sectorsize = fsopts->sectorsize; + fsize = fsopts->fsize; + bsize = fsopts->bsize; + maxbsize = fsopts->maxbsize; + maxblkspercg = fsopts->maxblkspercg; + minfree = fsopts->minfree; + opt = fsopts->optimization; + density = fsopts->density; + maxcontig = fsopts->maxcontig; + maxbpg = fsopts->maxbpg; + avgfilesize = fsopts->avgfilesize; + avgfpdir = fsopts->avgfpdir; + bbsize = BBSIZE; + sbsize = SBLOCKSIZE; + + if (Oflag == 0) { + sblock.fs_old_inodefmt = FS_42INODEFMT; + sblock.fs_maxsymlinklen = 0; + sblock.fs_old_flags = 0; + } else { + sblock.fs_old_inodefmt = FS_44INODEFMT; + sblock.fs_maxsymlinklen = (Oflag == 1 ? MAXSYMLINKLEN_UFS1 : + MAXSYMLINKLEN_UFS2); + sblock.fs_old_flags = FS_FLAGS_UPDATED; + sblock.fs_flags = 0; + } + /* + * Validate the given file system size. + * Verify that its last block can actually be accessed. + * Convert to file system fragment sized units. + */ + if (fssize <= 0) { + printf("preposterous size %lld\n", (long long)fssize); + exit(13); + } + ffs_wtfs(fssize - 1, sectorsize, (char *)&sblock, fsopts); + + /* + * collect and verify the filesystem density info + */ + sblock.fs_avgfilesize = avgfilesize; + sblock.fs_avgfpdir = avgfpdir; + if (sblock.fs_avgfilesize <= 0) + printf("illegal expected average file size %d\n", + sblock.fs_avgfilesize), exit(14); + if (sblock.fs_avgfpdir <= 0) + printf("illegal expected number of files per directory %d\n", + sblock.fs_avgfpdir), exit(15); + /* + * collect and verify the block and fragment sizes + */ + sblock.fs_bsize = bsize; + sblock.fs_fsize = fsize; + if (!POWEROF2(sblock.fs_bsize)) { + printf("block size must be a power of 2, not %d\n", + sblock.fs_bsize); + exit(16); + } + if (!POWEROF2(sblock.fs_fsize)) { + printf("fragment size must be a power of 2, not %d\n", + sblock.fs_fsize); + exit(17); + } + if (sblock.fs_fsize < sectorsize) { + printf("fragment size %d is too small, minimum is %d\n", + sblock.fs_fsize, sectorsize); + exit(18); + } + if (sblock.fs_bsize < MINBSIZE) { + printf("block size %d is too small, minimum is %d\n", + sblock.fs_bsize, MINBSIZE); + exit(19); + } + if (sblock.fs_bsize > FFS_MAXBSIZE) { + printf("block size %d is too large, maximum is %d\n", + sblock.fs_bsize, FFS_MAXBSIZE); + exit(19); + } + if (sblock.fs_bsize < sblock.fs_fsize) { + printf("block size (%d) cannot be smaller than fragment size (%d)\n", + sblock.fs_bsize, sblock.fs_fsize); + exit(20); + } + + if (maxbsize < bsize || !POWEROF2(maxbsize)) { + sblock.fs_maxbsize = sblock.fs_bsize; + printf("Extent size set to %d\n", sblock.fs_maxbsize); + } else if (sblock.fs_maxbsize > FS_MAXCONTIG * sblock.fs_bsize) { + sblock.fs_maxbsize = FS_MAXCONTIG * sblock.fs_bsize; + printf("Extent size reduced to %d\n", sblock.fs_maxbsize); + } else { + sblock.fs_maxbsize = maxbsize; + } + sblock.fs_maxcontig = maxcontig; + if (sblock.fs_maxcontig < sblock.fs_maxbsize / sblock.fs_bsize) { + sblock.fs_maxcontig = sblock.fs_maxbsize / sblock.fs_bsize; + printf("Maxcontig raised to %d\n", sblock.fs_maxbsize); + } + + if (sblock.fs_maxcontig > 1) + sblock.fs_contigsumsize = MIN(sblock.fs_maxcontig,FS_MAXCONTIG); + + sblock.fs_bmask = ~(sblock.fs_bsize - 1); + sblock.fs_fmask = ~(sblock.fs_fsize - 1); + sblock.fs_qbmask = ~sblock.fs_bmask; + sblock.fs_qfmask = ~sblock.fs_fmask; + for (sblock.fs_bshift = 0, i = sblock.fs_bsize; i > 1; i >>= 1) + sblock.fs_bshift++; + for (sblock.fs_fshift = 0, i = sblock.fs_fsize; i > 1; i >>= 1) + sblock.fs_fshift++; + sblock.fs_frag = numfrags(&sblock, sblock.fs_bsize); + for (sblock.fs_fragshift = 0, i = sblock.fs_frag; i > 1; i >>= 1) + sblock.fs_fragshift++; + if (sblock.fs_frag > MAXFRAG) { + printf("fragment size %d is too small, " + "minimum with block size %d is %d\n", + sblock.fs_fsize, sblock.fs_bsize, + sblock.fs_bsize / MAXFRAG); + exit(21); + } + sblock.fs_fsbtodb = ilog2(sblock.fs_fsize / sectorsize); + sblock.fs_size = fssize = dbtofsb(&sblock, fssize); + + if (Oflag <= 1) { + sblock.fs_magic = FS_UFS1_MAGIC; + sblock.fs_sblockloc = SBLOCK_UFS1; + sblock.fs_nindir = sblock.fs_bsize / sizeof(int32_t); + sblock.fs_inopb = sblock.fs_bsize / sizeof(struct ufs1_dinode); + sblock.fs_maxsymlinklen = ((NDADDR + NIADDR) * + sizeof (int32_t)); + sblock.fs_old_inodefmt = FS_44INODEFMT; + sblock.fs_old_cgoffset = 0; + sblock.fs_old_cgmask = 0xffffffff; + sblock.fs_old_size = sblock.fs_size; + sblock.fs_old_rotdelay = 0; + sblock.fs_old_rps = 60; + sblock.fs_old_nspf = sblock.fs_fsize / sectorsize; + sblock.fs_old_cpg = 1; + sblock.fs_old_interleave = 1; + sblock.fs_old_trackskew = 0; + sblock.fs_old_cpc = 0; + sblock.fs_old_postblformat = 1; + sblock.fs_old_nrpos = 1; + } else { + sblock.fs_magic = FS_UFS2_MAGIC; +#if 0 /* XXX makefs is used for small filesystems. */ + sblock.fs_sblockloc = SBLOCK_UFS2; +#else + sblock.fs_sblockloc = SBLOCK_UFS1; +#endif + sblock.fs_nindir = sblock.fs_bsize / sizeof(int64_t); + sblock.fs_inopb = sblock.fs_bsize / sizeof(struct ufs2_dinode); + sblock.fs_maxsymlinklen = ((NDADDR + NIADDR) * + sizeof (int64_t)); + } + + sblock.fs_sblkno = + roundup(howmany(sblock.fs_sblockloc + SBLOCKSIZE, sblock.fs_fsize), + sblock.fs_frag); + sblock.fs_cblkno = (daddr_t)(sblock.fs_sblkno + + roundup(howmany(SBLOCKSIZE, sblock.fs_fsize), sblock.fs_frag)); + sblock.fs_iblkno = sblock.fs_cblkno + sblock.fs_frag; + sblock.fs_maxfilesize = sblock.fs_bsize * NDADDR - 1; + for (sizepb = sblock.fs_bsize, i = 0; i < NIADDR; i++) { + sizepb *= NINDIR(&sblock); + sblock.fs_maxfilesize += sizepb; + } + + /* + * Calculate the number of blocks to put into each cylinder group. + * + * This algorithm selects the number of blocks per cylinder + * group. The first goal is to have at least enough data blocks + * in each cylinder group to meet the density requirement. Once + * this goal is achieved we try to expand to have at least + * 1 cylinder group. Once this goal is achieved, we pack as + * many blocks into each cylinder group map as will fit. + * + * We start by calculating the smallest number of blocks that we + * can put into each cylinder group. If this is too big, we reduce + * the density until it fits. + */ + origdensity = density; + for (;;) { + fragsperinode = MAX(numfrags(&sblock, density), 1); + minfpg = fragsperinode * INOPB(&sblock); + if (minfpg > sblock.fs_size) + minfpg = sblock.fs_size; + sblock.fs_ipg = INOPB(&sblock); + sblock.fs_fpg = roundup(sblock.fs_iblkno + + sblock.fs_ipg / INOPF(&sblock), sblock.fs_frag); + if (sblock.fs_fpg < minfpg) + sblock.fs_fpg = minfpg; + sblock.fs_ipg = roundup(howmany(sblock.fs_fpg, fragsperinode), + INOPB(&sblock)); + sblock.fs_fpg = roundup(sblock.fs_iblkno + + sblock.fs_ipg / INOPF(&sblock), sblock.fs_frag); + if (sblock.fs_fpg < minfpg) + sblock.fs_fpg = minfpg; + sblock.fs_ipg = roundup(howmany(sblock.fs_fpg, fragsperinode), + INOPB(&sblock)); + if (CGSIZE(&sblock) < (unsigned long)sblock.fs_bsize) + break; + density -= sblock.fs_fsize; + } + if (density != origdensity) + printf("density reduced from %d to %d\n", origdensity, density); + + if (maxblkspercg <= 0 || maxblkspercg >= fssize) + maxblkspercg = fssize - 1; + /* + * Start packing more blocks into the cylinder group until + * it cannot grow any larger, the number of cylinder groups + * drops below 1, or we reach the size requested. + */ + for ( ; sblock.fs_fpg < maxblkspercg; sblock.fs_fpg += sblock.fs_frag) { + sblock.fs_ipg = roundup(howmany(sblock.fs_fpg, fragsperinode), + INOPB(&sblock)); + if (sblock.fs_size / sblock.fs_fpg < 1) + break; + if (CGSIZE(&sblock) < (unsigned long)sblock.fs_bsize) + continue; + if (CGSIZE(&sblock) == (unsigned long)sblock.fs_bsize) + break; + sblock.fs_fpg -= sblock.fs_frag; + sblock.fs_ipg = roundup(howmany(sblock.fs_fpg, fragsperinode), + INOPB(&sblock)); + break; + } + /* + * Check to be sure that the last cylinder group has enough blocks + * to be viable. If it is too small, reduce the number of blocks + * per cylinder group which will have the effect of moving more + * blocks into the last cylinder group. + */ + optimalfpg = sblock.fs_fpg; + for (;;) { + sblock.fs_ncg = howmany(sblock.fs_size, sblock.fs_fpg); + lastminfpg = roundup(sblock.fs_iblkno + + sblock.fs_ipg / INOPF(&sblock), sblock.fs_frag); + if (sblock.fs_size < lastminfpg) { + printf("Filesystem size %lld < minimum size of %d\n", + (long long)sblock.fs_size, lastminfpg); + exit(28); + } + if (sblock.fs_size % sblock.fs_fpg >= lastminfpg || + sblock.fs_size % sblock.fs_fpg == 0) + break; + sblock.fs_fpg -= sblock.fs_frag; + sblock.fs_ipg = roundup(howmany(sblock.fs_fpg, fragsperinode), + INOPB(&sblock)); + } + if (optimalfpg != sblock.fs_fpg) + printf("Reduced frags per cylinder group from %d to %d %s\n", + optimalfpg, sblock.fs_fpg, "to enlarge last cyl group"); + sblock.fs_cgsize = fragroundup(&sblock, CGSIZE(&sblock)); + sblock.fs_dblkno = sblock.fs_iblkno + sblock.fs_ipg / INOPF(&sblock); + if (Oflag <= 1) { + sblock.fs_old_spc = sblock.fs_fpg * sblock.fs_old_nspf; + sblock.fs_old_nsect = sblock.fs_old_spc; + sblock.fs_old_npsect = sblock.fs_old_spc; + sblock.fs_old_ncyl = sblock.fs_ncg; + } + + /* + * fill in remaining fields of the super block + */ + sblock.fs_csaddr = cgdmin(&sblock, 0); + sblock.fs_cssize = + fragroundup(&sblock, sblock.fs_ncg * sizeof(struct csum)); + + /* + * Setup memory for temporary in-core cylgroup summaries. + * Cribbed from ffs_mountfs(). + */ + size = sblock.fs_cssize; + blks = howmany(size, sblock.fs_fsize); + if (sblock.fs_contigsumsize > 0) + size += sblock.fs_ncg * sizeof(int32_t); + if ((space = (char *)calloc(1, size)) == NULL) + err(1, "memory allocation error for cg summaries"); + sblock.fs_csp = space; + space = (char *)space + sblock.fs_cssize; + if (sblock.fs_contigsumsize > 0) { + int32_t *lp; + + sblock.fs_maxcluster = lp = space; + for (i = 0; i < sblock.fs_ncg; i++) + *lp++ = sblock.fs_contigsumsize; + } + + sblock.fs_sbsize = fragroundup(&sblock, sizeof(struct fs)); + if (sblock.fs_sbsize > SBLOCKSIZE) + sblock.fs_sbsize = SBLOCKSIZE; + sblock.fs_minfree = minfree; + sblock.fs_maxcontig = maxcontig; + sblock.fs_maxbpg = maxbpg; + sblock.fs_optim = opt; + sblock.fs_cgrotor = 0; + sblock.fs_pendingblocks = 0; + sblock.fs_pendinginodes = 0; + sblock.fs_cstotal.cs_ndir = 0; + sblock.fs_cstotal.cs_nbfree = 0; + sblock.fs_cstotal.cs_nifree = 0; + sblock.fs_cstotal.cs_nffree = 0; + sblock.fs_fmod = 0; + sblock.fs_ronly = 0; + sblock.fs_state = 0; + sblock.fs_clean = FS_ISCLEAN; + sblock.fs_ronly = 0; + sblock.fs_id[0] = start_time.tv_sec; + sblock.fs_id[1] = random(); + sblock.fs_fsmnt[0] = '\0'; + csfrags = howmany(sblock.fs_cssize, sblock.fs_fsize); + sblock.fs_dsize = sblock.fs_size - sblock.fs_sblkno - + sblock.fs_ncg * (sblock.fs_dblkno - sblock.fs_sblkno); + sblock.fs_cstotal.cs_nbfree = + fragstoblks(&sblock, sblock.fs_dsize) - + howmany(csfrags, sblock.fs_frag); + sblock.fs_cstotal.cs_nffree = + fragnum(&sblock, sblock.fs_size) + + (fragnum(&sblock, csfrags) > 0 ? + sblock.fs_frag - fragnum(&sblock, csfrags) : 0); + sblock.fs_cstotal.cs_nifree = sblock.fs_ncg * sblock.fs_ipg - ROOTINO; + sblock.fs_cstotal.cs_ndir = 0; + sblock.fs_dsize -= csfrags; + sblock.fs_time = start_time.tv_sec; + if (Oflag <= 1) { + sblock.fs_old_time = start_time.tv_sec; + sblock.fs_old_dsize = sblock.fs_dsize; + sblock.fs_old_csaddr = sblock.fs_csaddr; + sblock.fs_old_cstotal.cs_ndir = sblock.fs_cstotal.cs_ndir; + sblock.fs_old_cstotal.cs_nbfree = sblock.fs_cstotal.cs_nbfree; + sblock.fs_old_cstotal.cs_nifree = sblock.fs_cstotal.cs_nifree; + sblock.fs_old_cstotal.cs_nffree = sblock.fs_cstotal.cs_nffree; + } + /* + * Dump out summary information about file system. + */ +#define B2MBFACTOR (1 / (1024.0 * 1024.0)) + printf("%s: %.1fMB (%lld sectors) block size %d, " + "fragment size %d\n", + fsys, (float)sblock.fs_size * sblock.fs_fsize * B2MBFACTOR, + (long long)fsbtodb(&sblock, sblock.fs_size), + sblock.fs_bsize, sblock.fs_fsize); + printf("\tusing %d cylinder groups of %.2fMB, %d blks, " + "%d inodes.\n", + sblock.fs_ncg, + (float)sblock.fs_fpg * sblock.fs_fsize * B2MBFACTOR, + sblock.fs_fpg / sblock.fs_frag, sblock.fs_ipg); +#undef B2MBFACTOR + /* + * Now determine how wide each column will be, and calculate how + * many columns will fit in a 76 char line. 76 is the width of the + * subwindows in sysinst. + */ + printcolwidth = count_digits( + fsbtodb(&sblock, cgsblock(&sblock, sblock.fs_ncg -1))); + nprintcols = 76 / (printcolwidth + 2); + + /* + * allocate space for superblock, cylinder group map, and + * two sets of inode blocks. + */ + if (sblock.fs_bsize < SBLOCKSIZE) + iobufsize = SBLOCKSIZE + 3 * sblock.fs_bsize; + else + iobufsize = 4 * sblock.fs_bsize; + if ((iobuf = malloc(iobufsize)) == 0) { + printf("Cannot allocate I/O buffer\n"); + exit(38); + } + memset(iobuf, 0, iobufsize); + /* + * Make a copy of the superblock into the buffer that we will be + * writing out in each cylinder group. + */ + memcpy(writebuf, &sblock, sbsize); + if (fsopts->needswap) + ffs_sb_swap(&sblock, (struct fs*)writebuf); + memcpy(iobuf, writebuf, SBLOCKSIZE); + + printf("super-block backups (for fsck -b #) at:"); + for (cylno = 0; cylno < sblock.fs_ncg; cylno++) { + initcg(cylno, start_time.tv_sec, fsopts); + if (cylno % nprintcols == 0) + printf("\n"); + printf(" %*lld,", printcolwidth, + (long long)fsbtodb(&sblock, cgsblock(&sblock, cylno))); + fflush(stdout); + } + printf("\n"); + + /* + * Now construct the initial file system, + * then write out the super-block. + */ + sblock.fs_time = start_time.tv_sec; + if (Oflag <= 1) { + sblock.fs_old_cstotal.cs_ndir = sblock.fs_cstotal.cs_ndir; + sblock.fs_old_cstotal.cs_nbfree = sblock.fs_cstotal.cs_nbfree; + sblock.fs_old_cstotal.cs_nifree = sblock.fs_cstotal.cs_nifree; + sblock.fs_old_cstotal.cs_nffree = sblock.fs_cstotal.cs_nffree; + } + if (fsopts->needswap) + sblock.fs_flags |= FS_SWAPPED; + ffs_write_superblock(&sblock, fsopts); + return (&sblock); +} + +/* + * Write out the superblock and its duplicates, + * and the cylinder group summaries + */ +void +ffs_write_superblock(struct fs *fs, const fsinfo_t *fsopts) +{ + int cylno, size, blks, i, saveflag; + void *space; + char *wrbuf; + + saveflag = fs->fs_flags & FS_INTERNAL; + fs->fs_flags &= ~FS_INTERNAL; + + memcpy(writebuf, &sblock, sbsize); + if (fsopts->needswap) + ffs_sb_swap(fs, (struct fs*)writebuf); + ffs_wtfs(fs->fs_sblockloc / sectorsize, sbsize, writebuf, fsopts); + + /* Write out the duplicate super blocks */ + for (cylno = 0; cylno < fs->fs_ncg; cylno++) + ffs_wtfs(fsbtodb(fs, cgsblock(fs, cylno)), + sbsize, writebuf, fsopts); + + /* Write out the cylinder group summaries */ + size = fs->fs_cssize; + blks = howmany(size, fs->fs_fsize); + space = (void *)fs->fs_csp; + if ((wrbuf = malloc(size)) == NULL) + err(1, "ffs_write_superblock: malloc %d", size); + for (i = 0; i < blks; i+= fs->fs_frag) { + size = fs->fs_bsize; + if (i + fs->fs_frag > blks) + size = (blks - i) * fs->fs_fsize; + if (fsopts->needswap) + ffs_csum_swap((struct csum *)space, + (struct csum *)wrbuf, size); + else + memcpy(wrbuf, space, (u_int)size); + ffs_wtfs(fsbtodb(fs, fs->fs_csaddr + i), size, wrbuf, fsopts); + space = (char *)space + size; + } + free(wrbuf); + fs->fs_flags |= saveflag; +} + +/* + * Initialize a cylinder group. + */ +static void +initcg(int cylno, time_t utime, const fsinfo_t *fsopts) +{ + daddr_t cbase, dmax; + int32_t i, j, d, dlower, dupper, blkno; + struct ufs1_dinode *dp1; + struct ufs2_dinode *dp2; + int start; + + /* + * Determine block bounds for cylinder group. + * Allow space for super block summary information in first + * cylinder group. + */ + cbase = cgbase(&sblock, cylno); + dmax = cbase + sblock.fs_fpg; + if (dmax > sblock.fs_size) + dmax = sblock.fs_size; + dlower = cgsblock(&sblock, cylno) - cbase; + dupper = cgdmin(&sblock, cylno) - cbase; + if (cylno == 0) + dupper += howmany(sblock.fs_cssize, sblock.fs_fsize); + memset(&acg, 0, sblock.fs_cgsize); + acg.cg_time = utime; + acg.cg_magic = CG_MAGIC; + acg.cg_cgx = cylno; + acg.cg_niblk = sblock.fs_ipg; + acg.cg_initediblk = sblock.fs_ipg < 2 * INOPB(&sblock) ? + sblock.fs_ipg : 2 * INOPB(&sblock); + acg.cg_ndblk = dmax - cbase; + if (sblock.fs_contigsumsize > 0) + acg.cg_nclusterblks = acg.cg_ndblk >> sblock.fs_fragshift; + start = &acg.cg_space[0] - (u_char *)(&acg.cg_firstfield); + if (Oflag == 2) { + acg.cg_iusedoff = start; + } else { + if (cylno == sblock.fs_ncg - 1) + acg.cg_old_ncyl = howmany(acg.cg_ndblk, + sblock.fs_fpg / sblock.fs_old_cpg); + else + acg.cg_old_ncyl = sblock.fs_old_cpg; + acg.cg_old_time = acg.cg_time; + acg.cg_time = 0; + acg.cg_old_niblk = acg.cg_niblk; + acg.cg_niblk = 0; + acg.cg_initediblk = 0; + acg.cg_old_btotoff = start; + acg.cg_old_boff = acg.cg_old_btotoff + + sblock.fs_old_cpg * sizeof(int32_t); + acg.cg_iusedoff = acg.cg_old_boff + + sblock.fs_old_cpg * sizeof(u_int16_t); + } + acg.cg_freeoff = acg.cg_iusedoff + howmany(sblock.fs_ipg, CHAR_BIT); + if (sblock.fs_contigsumsize <= 0) { + acg.cg_nextfreeoff = acg.cg_freeoff + + howmany(sblock.fs_fpg, CHAR_BIT); + } else { + acg.cg_clustersumoff = acg.cg_freeoff + + howmany(sblock.fs_fpg, CHAR_BIT) - sizeof(int32_t); + acg.cg_clustersumoff = + roundup(acg.cg_clustersumoff, sizeof(int32_t)); + acg.cg_clusteroff = acg.cg_clustersumoff + + (sblock.fs_contigsumsize + 1) * sizeof(int32_t); + acg.cg_nextfreeoff = acg.cg_clusteroff + + howmany(fragstoblks(&sblock, sblock.fs_fpg), CHAR_BIT); + } + if (acg.cg_nextfreeoff > sblock.fs_cgsize) { + printf("Panic: cylinder group too big\n"); + exit(37); + } + acg.cg_cs.cs_nifree += sblock.fs_ipg; + if (cylno == 0) + for (i = 0; i < ROOTINO; i++) { + setbit(cg_inosused(&acg, 0), i); + acg.cg_cs.cs_nifree--; + } + if (cylno > 0) { + /* + * In cylno 0, beginning space is reserved + * for boot and super blocks. + */ + for (d = 0, blkno = 0; d < dlower;) { + ffs_setblock(&sblock, cg_blksfree(&acg, 0), blkno); + if (sblock.fs_contigsumsize > 0) + setbit(cg_clustersfree(&acg, 0), blkno); + acg.cg_cs.cs_nbfree++; + d += sblock.fs_frag; + blkno++; + } + } + if ((i = (dupper & (sblock.fs_frag - 1))) != 0) { + acg.cg_frsum[sblock.fs_frag - i]++; + for (d = dupper + sblock.fs_frag - i; dupper < d; dupper++) { + setbit(cg_blksfree(&acg, 0), dupper); + acg.cg_cs.cs_nffree++; + } + } + for (d = dupper, blkno = dupper >> sblock.fs_fragshift; + d + sblock.fs_frag <= acg.cg_ndblk; ) { + ffs_setblock(&sblock, cg_blksfree(&acg, 0), blkno); + if (sblock.fs_contigsumsize > 0) + setbit(cg_clustersfree(&acg, 0), blkno); + acg.cg_cs.cs_nbfree++; + d += sblock.fs_frag; + blkno++; + } + if (d < acg.cg_ndblk) { + acg.cg_frsum[acg.cg_ndblk - d]++; + for (; d < acg.cg_ndblk; d++) { + setbit(cg_blksfree(&acg, 0), d); + acg.cg_cs.cs_nffree++; + } + } + if (sblock.fs_contigsumsize > 0) { + int32_t *sump = cg_clustersum(&acg, 0); + u_char *mapp = cg_clustersfree(&acg, 0); + int map = *mapp++; + int bit = 1; + int run = 0; + + for (i = 0; i < acg.cg_nclusterblks; i++) { + if ((map & bit) != 0) { + run++; + } else if (run != 0) { + if (run > sblock.fs_contigsumsize) + run = sblock.fs_contigsumsize; + sump[run]++; + run = 0; + } + if ((i & (CHAR_BIT - 1)) != (CHAR_BIT - 1)) { + bit <<= 1; + } else { + map = *mapp++; + bit = 1; + } + } + if (run != 0) { + if (run > sblock.fs_contigsumsize) + run = sblock.fs_contigsumsize; + sump[run]++; + } + } + sblock.fs_cs(&sblock, cylno) = acg.cg_cs; + /* + * Write out the duplicate super block, the cylinder group map + * and two blocks worth of inodes in a single write. + */ + start = sblock.fs_bsize > SBLOCKSIZE ? sblock.fs_bsize : SBLOCKSIZE; + memcpy(&iobuf[start], &acg, sblock.fs_cgsize); + if (fsopts->needswap) + ffs_cg_swap(&acg, (struct cg*)&iobuf[start], &sblock); + start += sblock.fs_bsize; + dp1 = (struct ufs1_dinode *)(&iobuf[start]); + dp2 = (struct ufs2_dinode *)(&iobuf[start]); + for (i = 0; i < acg.cg_initediblk; i++) { + if (sblock.fs_magic == FS_UFS1_MAGIC) { + /* No need to swap, it'll stay random */ + dp1->di_gen = random(); + dp1++; + } else { + dp2->di_gen = random(); + dp2++; + } + } + ffs_wtfs(fsbtodb(&sblock, cgsblock(&sblock, cylno)), iobufsize, iobuf, + fsopts); + /* + * For the old file system, we have to initialize all the inodes. + */ + if (Oflag <= 1) { + for (i = 2 * sblock.fs_frag; + i < sblock.fs_ipg / INOPF(&sblock); + i += sblock.fs_frag) { + dp1 = (struct ufs1_dinode *)(&iobuf[start]); + for (j = 0; j < INOPB(&sblock); j++) { + dp1->di_gen = random(); + dp1++; + } + ffs_wtfs(fsbtodb(&sblock, cgimin(&sblock, cylno) + i), + sblock.fs_bsize, &iobuf[start], fsopts); + } + } +} + +/* + * read a block from the file system + */ +void +ffs_rdfs(daddr_t bno, int size, void *bf, const fsinfo_t *fsopts) +{ + int n; + off_t offset; + + offset = bno; + offset *= fsopts->sectorsize; + if (lseek(fsopts->fd, offset, SEEK_SET) < 0) + err(1, "ffs_rdfs: seek error for sector %lld: %s\n", + (long long)bno, strerror(errno)); + n = read(fsopts->fd, bf, size); + if (n == -1) { + abort(); + err(1, "ffs_rdfs: read error bno %lld size %d", (long long)bno, + size); + } + else if (n != size) + errx(1, "ffs_rdfs: read error for sector %lld: %s\n", + (long long)bno, strerror(errno)); +} + +/* + * write a block to the file system + */ +void +ffs_wtfs(daddr_t bno, int size, void *bf, const fsinfo_t *fsopts) +{ + int n; + off_t offset; + + offset = bno; + offset *= fsopts->sectorsize; + if (lseek(fsopts->fd, offset, SEEK_SET) < 0) + err(1, "wtfs: seek error for sector %lld: %s\n", + (long long)bno, strerror(errno)); + n = write(fsopts->fd, bf, size); + if (n == -1) + err(1, "wtfs: write error for sector %lld: %s\n", + (long long)bno, strerror(errno)); + else if (n != size) + errx(1, "wtfs: write error for sector %lld: %s\n", + (long long)bno, strerror(errno)); +} + + +/* Determine how many digits are needed to print a given integer */ +static int +count_digits(int num) +{ + int ndig; + + for(ndig = 1; num > 9; num /=10, ndig++); + + return (ndig); +} + +static int +ilog2(int val) +{ + u_int n; + + for (n = 0; n < sizeof(n) * CHAR_BIT; n++) + if (1 << n == val) + return (n); + errx(1, "ilog2: %d is not a power of 2\n", val); +} diff --git a/ffs/newfs_extern.h b/ffs/newfs_extern.h new file mode 100644 index 00000000000..36c65860437 --- /dev/null +++ b/ffs/newfs_extern.h @@ -0,0 +1,39 @@ +/* $NetBSD: newfs_extern.h,v 1.2 2004/06/24 22:30:13 lukem Exp $ */ +/* From: NetBSD: extern.h,v 1.3 2000/12/01 12:03:27 simonb Exp $ */ + +/* + * Copyright (c) 1997 Christos Zoulas. 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 Christos Zoulas. + * 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. + */ + +/* prototypes */ +struct fs *ffs_mkfs(const char *, const fsinfo_t *); +void ffs_write_superblock(struct fs *, const fsinfo_t *); +void ffs_rdfs(daddr_t, int, void *, const fsinfo_t *); +void ffs_wtfs(daddr_t, int, void *, const fsinfo_t *); + +#define FFS_MAXBSIZE 65536 diff --git a/ffs/ufs_bmap.c b/ffs/ufs_bmap.c new file mode 100644 index 00000000000..8e21ae60323 --- /dev/null +++ b/ffs/ufs_bmap.c @@ -0,0 +1,148 @@ +/* $NetBSD: ufs_bmap.c,v 1.14 2004/06/20 22:20:18 jmc Exp $ */ +/* From: NetBSD: ufs_bmap.c,v 1.14 2001/11/08 05:00:51 chs Exp */ + +/* + * Copyright (c) 1989, 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. + * 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. + * + * @(#)ufs_bmap.c 8.8 (Berkeley) 8/11/95 + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include +#if defined(__RCSID) && !defined(__lint) +__RCSID("$NetBSD: ufs_bmap.c,v 1.14 2004/06/20 22:20:18 jmc Exp $"); +#endif /* !__lint */ + +#include +#include + +#include +#include +#include + +#include "makefs.h" + +#include +#include +#include + +#include "ffs/ufs_inode.h" +#include "ffs/ffs_extern.h" + +/* + * Create an array of logical block number/offset pairs which represent the + * path of indirect blocks required to access a data block. The first "pair" + * contains the logical block number of the appropriate single, double or + * triple indirect block and the offset into the inode indirect block array. + * Note, the logical block number of the inode single/double/triple indirect + * block appears twice in the array, once with the offset into the i_ffs_ib and + * once with the offset into the page itself. + */ +int +ufs_getlbns(struct inode *ip, daddr_t bn, struct indir *ap, int *nump) +{ + daddr_t metalbn, realbn; + int64_t blockcnt; + int lbc; + int i, numlevels, off; + u_long lognindir; + + lognindir = ffs(NINDIR(ip->i_fs)) - 1; + if (nump) + *nump = 0; + numlevels = 0; + realbn = bn; + if ((long)bn < 0) + bn = -(long)bn; + + assert (bn >= NDADDR); + + /* + * Determine the number of levels of indirection. After this loop + * is done, blockcnt indicates the number of data blocks possible + * at the given level of indirection, and NIADDR - i is the number + * of levels of indirection needed to locate the requested block. + */ + + bn -= NDADDR; + for (lbc = 0, i = NIADDR;; i--, bn -= blockcnt) { + if (i == 0) + return (EFBIG); + + lbc += lognindir; + blockcnt = (int64_t)1 << lbc; + + if (bn < blockcnt) + break; + } + + /* Calculate the address of the first meta-block. */ + if (realbn >= 0) + metalbn = -(realbn - bn + NIADDR - i); + else + metalbn = -(-realbn - bn + NIADDR - i); + + /* + * At each iteration, off is the offset into the bap array which is + * an array of disk addresses at the current level of indirection. + * The logical block number and the offset in that block are stored + * into the argument array. + */ + ap->in_lbn = metalbn; + ap->in_off = off = NIADDR - i; + ap->in_exists = 0; + ap++; + for (++numlevels; i <= NIADDR; i++) { + /* If searching for a meta-data block, quit when found. */ + if (metalbn == realbn) + break; + + lbc -= lognindir; + blockcnt = (int64_t)1 << lbc; + off = (bn >> lbc) & (NINDIR(ip->i_fs) - 1); + + ++numlevels; + ap->in_lbn = metalbn; + ap->in_off = off; + ap->in_exists = 0; + ++ap; + + metalbn -= -1 + (off << lbc); + } + if (nump) + *nump = numlevels; + return (0); +} diff --git a/ffs/ufs_inode.h b/ffs/ufs_inode.h new file mode 100644 index 00000000000..3d61de6e995 --- /dev/null +++ b/ffs/ufs_inode.h @@ -0,0 +1,96 @@ +/* $NetBSD: ufs_inode.h,v 1.3 2003/08/07 11:25:34 agc Exp $ */ +/* From: NetBSD: inode.h,v 1.27 2001/12/18 10:57:23 fvdl Exp $ */ + +/* + * Copyright (c) 1982, 1989, 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. + * 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. + * + * @(#)inode.h 8.9 (Berkeley) 5/14/95 + */ + +union dinode { + struct ufs1_dinode ffs1_din; + struct ufs2_dinode ffs2_din; +}; + +struct inode { + ino_t i_number; /* The identity of the inode. */ + struct fs *i_fs; /* File system */ + union dinode i_din; + int i_fd; /* File descriptor */ + uint64_t i_size; +}; + +#define i_ffs1_atime i_din.ffs1_din.di_atime +#define i_ffs1_atimensec i_din.ffs1_din.di_atimensec +#define i_ffs1_blocks i_din.ffs1_din.di_blocks +#define i_ffs1_ctime i_din.ffs1_din.di_ctime +#define i_ffs1_ctimensec i_din.ffs1_din.di_ctimensec +#define i_ffs1_db i_din.ffs1_din.di_db +#define i_ffs1_flags i_din.ffs1_din.di_flags +#define i_ffs1_gen i_din.ffs1_din.di_gen +#define i_ffs11_gid i_din.ffs1_din.di_gid +#define i_ffs1_ib i_din.ffs1_din.di_ib +#define i_ffs1_mode i_din.ffs1_din.di_mode +#define i_ffs1_mtime i_din.ffs1_din.di_mtime +#define i_ffs1_mtimensec i_din.ffs1_din.di_mtimensec +#define i_ffs1_nlink i_din.ffs1_din.di_nlink +#define i_ffs1_rdev i_din.ffs1_din.di_rdev +#define i_ffs1_shortlink i_din.ffs1_din.db +#define i_ffs1_size i_din.ffs1_din.di_size +#define i_ffs1_uid i_din.ffs1_din.di_uid + +#define i_ffs2_atime i_din.ffs2_din.di_atime +#define i_ffs2_atimensec i_din.ffs2_din.di_atimensec +#define i_ffs2_blocks i_din.ffs2_din.di_blocks +#define i_ffs2_ctime i_din.ffs2_din.di_ctime +#define i_ffs2_ctimensec i_din.ffs2_din.di_ctimensec +#define i_ffs2_birthtime i_din.ffs2_din.di_birthtime +#define i_ffs2_birthnsec i_din.ffs2_din.di_birthnsec +#define i_ffs2_db i_din.ffs2_din.di_db +#define i_ffs2_flags i_din.ffs2_din.di_flags +#define i_ffs2_gen i_din.ffs2_din.di_gen +#define i_ffs21_gid i_din.ffs2_din.di_gid +#define i_ffs2_ib i_din.ffs2_din.di_ib +#define i_ffs2_mode i_din.ffs2_din.di_mode +#define i_ffs2_mtime i_din.ffs2_din.di_mtime +#define i_ffs2_mtimensec i_din.ffs2_din.di_mtimensec +#define i_ffs2_nlink i_din.ffs2_din.di_nlink +#define i_ffs2_rdev i_din.ffs2_din.di_rdev +#define i_ffs2_shortlink i_din.ffs2_din.db +#define i_ffs2_size i_din.ffs2_din.di_size +#define i_ffs2_uid i_din.ffs2_din.di_uid + +#undef DIP +#define DIP(ip, field) \ + (((ip)->i_fs->fs_magic == FS_UFS1_MAGIC) ? \ + (ip)->i_ffs1_##field : (ip)->i_ffs2_##field) diff --git a/makefs.8 b/makefs.8 new file mode 100644 index 00000000000..03f61cf5e79 --- /dev/null +++ b/makefs.8 @@ -0,0 +1,286 @@ +.\" $NetBSD: makefs.8,v 1.13 2004/02/13 17:56:18 wiz Exp $ +.\" +.\" Copyright (c) 2001-2003 Wasabi Systems, Inc. +.\" All rights reserved. +.\" +.\" Written by Luke Mewburn for Wasabi Systems, 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. +.\" 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 +.\" Wasabi Systems, Inc. +.\" 4. The name of Wasabi Systems, Inc. may not be used to endorse +.\" or promote products derived from this software without specific prior +.\" written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``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 WASABI SYSTEMS, INC +.\" 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. +.\" +.Dd March 30, 2003 +.Dt MAKEFS 8 +.Os +.Sh NAME +.Nm makefs +.Nd create a file system image from a directory tree +.Sh SYNOPSIS +.Nm +.Bk -words +.Op Fl t Ar fs-type +.Ek +.Bk -words +.Op Fl o Ar fs-options +.Ek +.Bk -words +.Op Fl d Ar debug-mask +.Ek +.Bk -words +.Op Fl B Ar byte-order +.Ek +.Bk -words +.Op Fl S Ar sector-size +.Ek +.Bk -words +.Op Fl M Ar minimum-size +.Ek +.Bk -words +.Op Fl m Ar maximum-size +.Ek +.Bk -words +.Op Fl s Ar image-size +.Ek +.Bk -words +.Op Fl b Ar free-blocks +.Ek +.Bk -words +.Op Fl f Ar free-files +.Ek +.Bk -words +.Op Fl F Ar specfile +.Ek +.Op Fl x +.Bk -words +.Op Fl N Ar userdb-dir +.Ek +.Ar image-file +.Ar directory +.Sh DESCRIPTION +The utility +.Nm +creates a file system image into +.Ar image-file +from the directory tree +.Ar directory . +No special devices or privileges are required to perform this task. +.Pp +The options are as follows: +.Bl -tag -width flag +.It Fl t Ar fs-type +Create an +.Ar fs-type +file system image. +The following file system types are supported: +.Bl -tag -width ffs -offset indent +.It Sy ffs +BSD fast file system (default). +.El +.It Fl o Ar fs-options +Set file system specific options. +.Ar fs-options +is a comma separated list of options. +Valid file system specific options are detailed below. +.It Fl d Ar debug-mask +Enable various levels of debugging, depending upon which bits are set +in +.Ar debug-mask . +XXX: document these +.It Fl B Ar byte-order +Set the byte order of the image to +.Ar byte-order . +Valid byte orders are +.Ql 4321 , +.Ql big +or +.Ql be +for big endian, and +.Ql 1234 , +.Ql little +or +.Ql le +for little endian. +Some file systems may have a fixed byte order; in those cases this +argument will be ignored. +.It Fl S Ar sector-size +Set the file system sector size to +.Ar sector-size . +Defaults to 512. +.It Fl M Ar minimum-size +Set the minimum size of the file system image to +.Ar minimum-size . +.It Fl m Ar maximum-size +Set the maximum size of the file system image to +.Ar maximum-size . +An error will be raised if the target file system needs to be larger +than this to accommodate the provided directory tree. +.It Fl s Ar image-size +Set the size of the file system image to +.Ar image-size . +.It Fl b Ar free-blocks +Ensure that a minimum of +.Ar free-blocks +free blocks exist in the image. +An optional +.Ql % +suffix may be provided to indicate that +.Ar free-blocks +indicates a percentage of the calculated image size +.It Fl f Ar free-files +Ensure that a minimum of +.Ar free-files +free files (inodes) exist in the image. +An optional +.Ql % +suffix may be provided to indicate that +.Ar free-blocks +indicates a percentage of the calculated image size +.It Fl F Ar specfile +Use +.Ar specfile +as an +.Xr mtree 8 +.Sq specfile +specification. +.Pp +If a specfile entry exists in the underlying file system, its permissions and +modification time will be used unless specifically overridden by the specfile. +An error will be raised if the type of entry in the specfile conflicts +with that of an existing entry. +.Pp +In the opposite case +(where a specfile entry does not have an entry in the underlying file system) +the following occurs: +If the specfile entry is marked +.Sy optional , +the specfile entry is ignored. +Otherwise, the entry will be created in the image, +and it is necessary to specify at least the following parameters +in the specfile: +.Sy type , +.Sy mode , +.Sy gname +or +.Sy gid , +and +.Sy uname +or +.Sy uid , +.Sy device +(in the case of block or character devices), and +.Sy link +(in the case of symbolic links). +If +.Sy time +isn't provided, the current time will be used. +If +.Sy flags +isn't provided, the current file flags will be used. +Missing regular file entries will be created as zero-length files. +.It Fl x +Exclude file system nodes not explcitly listed in the specfile. +.It Fl N Ar dbdir +Use the user database text file +.Pa master.passwd +and group database text file +.Pa group +from +.Ar dbdir , +rather than using the results from the system's +.Xr getpwnam 3 +and +.Xr getgrnam 3 +(and related) library calls. +.El +.Pp +Where sizes are specified, a decimal number of bytes is expected. +Two or more numbers may be separated by an +.Dq x +to indicate a product. +Each number may have one of the following optional suffixes: +.Bl -tag -width 3n -offset indent -compact +.It b +Block; multiply by 512 +.It k +Kilo; multiply by 1024 (1 KB) +.It m +Mega; multiply by 1048576 (1 MB) +.It g +Giga; multiply by 1073741824 (1 GB) +.It t +Tera; multiply by 1099511627776 (1 TB) +.It w +Word; multiply by the number of bytes in an integer +.El +.\" +.\" +.Ss FFS-specific options +.Sy ffs +images have ffs-specific optional parameters that may be provided. +Each of the options consists of a keyword, an equals sign +.Pq Ql = , +and a value. +The following keywords are supported: +.Pp +.Bl -tag -width optimization -offset indent -compact +.It Sy avgfilesize +Expected average file size +.It Sy avgfpdir +Expected number of files per directory +.It Sy bsize +Block size +.It Sy density +Bytes per inode +.It Sy fsize +Fragment size +.It Sy maxbpg +Maximum blocks per file in a cylinder group +.It Sy minfree +Minimum % free +.It Sy optimization +Optimization preference; one of +.Ql space +or +.Ql time . +.It Sy extent +Maximum extent size +.It Sy maxbpcg +Maximum total number of blocks in a cylinder group +.It Sy version +UFS version. 1 for FFS (default), 2 for UFS2 +.El +.Sh SEE ALSO +.Xr mtree 8 , +.Xr newfs 8 +.Sh HISTORY +The +.Nm +utility appeared in +.Nx 1.6 . +.Sh AUTHORS +.An Luke Mewburn +.Aq lukem@NetBSD.org . diff --git a/makefs.c b/makefs.c new file mode 100644 index 00000000000..4a64ab397a0 --- /dev/null +++ b/makefs.c @@ -0,0 +1,320 @@ +/* $NetBSD: makefs.c,v 1.20 2004/06/20 22:20:18 jmc Exp $ */ + +/* + * Copyright (c) 2001-2003 Wasabi Systems, Inc. + * All rights reserved. + * + * Written by Luke Mewburn for Wasabi Systems, 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. + * 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 + * Wasabi Systems, Inc. + * 4. The name of Wasabi Systems, Inc. may not be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``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 WASABI SYSTEMS, INC + * 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 HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include +#if defined(__RCSID) && !defined(__lint) +__RCSID("$NetBSD: makefs.c,v 1.20 2004/06/20 22:20:18 jmc Exp $"); +#endif /* !__lint */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "makefs.h" +#include "mtree.h" + +/* + * list of supported file systems and dispatch functions + */ +typedef struct { + const char *type; + int (*parse_options)(const char *, fsinfo_t *); + void (*make_fs)(const char *, const char *, fsnode *, + fsinfo_t *); +} fstype_t; + +static fstype_t fstypes[] = { + { "ffs", ffs_parse_opts, ffs_makefs }, + { NULL }, +}; + +u_int debug; +struct timespec start_time; + +static fstype_t *get_fstype(const char *); +static void usage(void); +int main(int, char *[]); + +int +main(int argc, char *argv[]) +{ + struct timeval start; + fstype_t *fstype; + fsinfo_t fsoptions; + fsnode *root; + int ch, len; + char *specfile; + + setprogname(argv[0]); + + debug = 0; + if ((fstype = get_fstype(DEFAULT_FSTYPE)) == NULL) + errx(1, "Unknown default fs type `%s'.", DEFAULT_FSTYPE); + + /* set default fsoptions */ + (void)memset(&fsoptions, 0, sizeof(fsoptions)); + fsoptions.fd = -1; + fsoptions.sectorsize = -1; + fsoptions.bsize= -1; + fsoptions.fsize= -1; + fsoptions.cpg= -1; + fsoptions.density= -1; + fsoptions.minfree= -1; + fsoptions.optimization= -1; + fsoptions.maxcontig= -1; + fsoptions.maxbpg= -1; + fsoptions.avgfilesize= -1; + fsoptions.avgfpdir= -1; + fsoptions.version = 1; + + specfile = NULL; + if (gettimeofday(&start, NULL) == -1) + err(1, "Unable to get system time"); + + start_time.tv_sec = start.tv_sec; + start_time.tv_nsec = start.tv_usec * 1000; + + while ((ch = getopt(argc, argv, "B:b:d:f:F:M:m:N:o:s:S:t:x")) != -1) { + switch (ch) { + + case 'B': + if (strcmp(optarg, "be") == 0 || + strcmp(optarg, "4321") == 0 || + strcmp(optarg, "big") == 0) { +#if BYTE_ORDER == LITTLE_ENDIAN + fsoptions.needswap = 1; +#endif + } else if (strcmp(optarg, "le") == 0 || + strcmp(optarg, "1234") == 0 || + strcmp(optarg, "little") == 0) { +#if BYTE_ORDER == BIG_ENDIAN + fsoptions.needswap = 1; +#endif + } else { + warnx("Invalid endian `%s'.", optarg); + usage(); + } + break; + + case 'b': + len = strlen(optarg) - 1; + if (optarg[len] == '%') { + optarg[len] = '\0'; + fsoptions.freeblockpc = + strsuftoll("free block percentage", + optarg, 0, 99); + } else { + fsoptions.freeblocks = + strsuftoll("free blocks", + optarg, 0, LLONG_MAX); + } + break; + + case 'd': + debug = + (int)strsuftoll("debug mask", optarg, 0, UINT_MAX); + break; + + case 'f': + len = strlen(optarg) - 1; + if (optarg[len] == '%') { + optarg[len] = '\0'; + fsoptions.freefilepc = + strsuftoll("free file percentage", + optarg, 0, 99); + } else { + fsoptions.freefiles = + strsuftoll("free files", + optarg, 0, LLONG_MAX); + } + break; + + case 'F': + specfile = optarg; + break; + + case 'M': + fsoptions.minsize = + strsuftoll("minimum size", optarg, 1LL, LLONG_MAX); + break; + + case 'N': + if (! setup_getid(optarg)) + errx(1, + "Unable to use user and group databases in `%s'", + optarg); + break; + + case 'm': + fsoptions.maxsize = + strsuftoll("maximum size", optarg, 1LL, LLONG_MAX); + break; + + case 'o': + { + char *p; + + while ((p = strsep(&optarg, ",")) != NULL) { + if (*p == '\0') + errx(1, "Empty option"); + if (! fstype->parse_options(p, &fsoptions)) + usage(); + } + break; + } + + case 's': + fsoptions.minsize = fsoptions.maxsize = + strsuftoll("size", optarg, 1LL, LLONG_MAX); + break; + + case 'S': + fsoptions.sectorsize = + (int)strsuftoll("sector size", optarg, + 1LL, INT_MAX); + break; + + case 't': + if ((fstype = get_fstype(optarg)) == NULL) + errx(1, "Unknown fs type `%s'.", optarg); + break; + + case 'x': + fsoptions.onlyspec = 1; + break; + + case '?': + default: + usage(); + /* NOTREACHED */ + + } + } + if (debug) { + printf("debug mask: 0x%08x\n", debug); + printf("start time: %ld.%ld, %s", + (long)start_time.tv_sec, (long)start_time.tv_nsec, + ctime(&start_time.tv_sec)); + } + argc -= optind; + argv += optind; + + if (argc != 2) + usage(); + + /* -x must be accompanied by -F */ + if (fsoptions.onlyspec != 0 && specfile == NULL) + errx(1, "-x requires -F mtree-specfile."); + + /* walk the tree */ + TIMER_START(start); + root = walk_dir(argv[1], NULL); + TIMER_RESULTS(start, "walk_dir"); + + if (specfile) { /* apply a specfile */ + TIMER_START(start); + apply_specfile(specfile, argv[1], root); + TIMER_RESULTS(start, "apply_specfile"); + } + + if (debug & DEBUG_DUMP_FSNODES) { + printf("\nparent: %s\n", argv[1]); + dump_fsnodes(".", root); + putchar('\n'); + } + + /* build the file system */ + TIMER_START(start); + fstype->make_fs(argv[0], argv[1], root, &fsoptions); + TIMER_RESULTS(start, "make_fs"); + + exit(0); + /* NOTREACHED */ +} + + +int +set_option(option_t *options, const char *var, const char *val) +{ + int i; + + for (i = 0; options[i].name != NULL; i++) { + if (strcmp(options[i].name, var) != 0) + continue; + *options[i].value = (int)strsuftoll(options[i].desc, val, + options[i].minimum, options[i].maximum); + return (1); + } + warnx("Unknown option `%s'", var); + return (0); +} + + +static fstype_t * +get_fstype(const char *type) +{ + int i; + + for (i = 0; fstypes[i].type != NULL; i++) + if (strcmp(fstypes[i].type, type) == 0) + return (&fstypes[i]); + return (NULL); +} + +static void +usage(void) +{ + const char *prog; + + prog = getprogname(); + fprintf(stderr, +"usage: %s [-t fs-type] [-o fs-options] [-d debug-mask] [-B endian]\n" +"\t[-S sector-size] [-M minimum-size] [-m maximum-size] [-s image-size]\n" +"\t[-b free-blocks] [-f free-files] [-F mtree-specfile] [-x]\n" +"\t[-N userdb-dir] image-file directory\n", + prog); + exit(1); +} diff --git a/makefs.h b/makefs.h new file mode 100644 index 00000000000..ada563b1c5e --- /dev/null +++ b/makefs.h @@ -0,0 +1,251 @@ +/* $NetBSD: makefs.h,v 1.14 2004/06/20 22:20:18 jmc Exp $ */ + +/* + * Copyright (c) 2001 Wasabi Systems, Inc. + * All rights reserved. + * + * Written by Luke Mewburn for Wasabi Systems, 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. + * 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 + * Wasabi Systems, Inc. + * 4. The name of Wasabi Systems, Inc. may not be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``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 WASABI SYSTEMS, INC + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _MAKEFS_H +#define _MAKEFS_H + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#else +#define HAVE_STRUCT_STAT_ST_FLAGS 1 +#define HAVE_STRUCT_STAT_ST_GEN 1 +#define HAVE_STRUCT_STAT_ST_MTIMENSEC 1 +#define HAVE_STRUCT_STATVFS_F_IOSIZE 1 +#define HAVE_STRUCT_STAT_BIRTHTIME 1 +#endif + +#include +#include + +/* + * fsnode - + * a component of the tree; contains a filename, a pointer to + * fsinode, optional symlink name, and tree pointers + * + * fsinode - + * equivalent to an inode, containing target file system inode number, + * refcount (nlink), and stat buffer + * + * A tree of fsnodes looks like this: + * + * name "." "bin" "netbsd" + * type S_IFDIR S_IFDIR S_IFREG + * next > > NULL + * parent NULL NULL NULL + * child NULL v + * + * name "." "ls" + * type S_IFDIR S_IFREG + * next > NULL + * parent ^ ^ (to "bin") + * child NULL NULL + * + * Notes: + * - first always points to first entry, at current level, which + * must be "." when the tree has been built; during build it may + * not be if "." hasn't yet been found by readdir(2). + */ + +enum fi_flags { + FI_SIZED = 1<<0, /* inode sized */ + FI_ALLOCATED = 1<<1, /* fsinode->ino allocated */ + FI_WRITTEN = 1<<2, /* inode written */ +}; + +typedef struct { + uint32_t ino; /* inode number used on target fs */ + uint32_t nlink; /* number of links to this entry */ + enum fi_flags flags; /* flags used by fs specific code */ + struct stat st; /* stat entry */ +} fsinode; + +typedef struct _fsnode { + struct _fsnode *parent; /* parent (NULL if root) */ + struct _fsnode *child; /* child (if type == S_IFDIR) */ + struct _fsnode *next; /* next */ + struct _fsnode *first; /* first node of current level (".") */ + uint32_t type; /* type of entry */ + fsinode *inode; /* actual inode data */ + char *symlink; /* symlink target */ + char *name; /* file name */ + int flags; /* misc flags */ +} fsnode; + +#define FSNODE_F_HASSPEC 0x01 /* fsnode has a spec entry */ + +/* + * fsinfo_t - contains various settings and parameters pertaining to + * the image, including current settings, global options, and fs + * specific options + */ +typedef struct { + /* current settings */ + off_t size; /* total size */ + off_t inodes; /* number of inodes */ + uint32_t curinode; /* current inode */ + + /* image settings */ + int fd; /* file descriptor of image */ + void *superblock; /* superblock */ + int onlyspec; /* only add entries in specfile */ + + + /* global options */ + off_t minsize; /* minimum size image should be */ + off_t maxsize; /* maximum size image can be */ + off_t freefiles; /* free file entries to leave */ + int freefilepc; /* free file % */ + off_t freeblocks; /* free blocks to leave */ + int freeblockpc; /* free block % */ + int needswap; /* non-zero if byte swapping needed */ + int sectorsize; /* sector size */ + + /* ffs specific options */ + int bsize; /* block size */ + int fsize; /* fragment size */ + int cpg; /* cylinders per group */ + int cpgflg; /* cpg was specified by user */ + int density; /* bytes per inode */ + int ntracks; /* number of tracks */ + int nsectors; /* number of sectors */ + int rpm; /* rpm */ + int minfree; /* free space threshold */ + int optimization; /* optimization (space or time) */ + int maxcontig; /* max contiguous blocks to allocate */ + int rotdelay; /* rotational delay between blocks */ + int maxbpg; /* maximum blocks per file in a cyl group */ + int nrpos; /* # of distinguished rotational positions */ + int avgfilesize; /* expected average file size */ + int avgfpdir; /* expected # of files per directory */ + int version; /* filesystem version (1 = FFS, 2 = UFS2) */ + int maxbsize; /* maximum extent size */ + int maxblkspercg; /* max # of blocks per cylinder group */ + /* XXX: support `old' file systems ? */ +} fsinfo_t; + + +/* + * option_t - contains option name, description, pointer to location to store + * result, and range checks for the result. Used to simplify fs specific + * option setting + */ +typedef struct { + const char *name; /* option name */ + int *value; /* where to stuff the value */ + int minimum; /* minimum for value */ + int maximum; /* maximum for value */ + const char *desc; /* option description */ +} option_t; + + +void apply_specfile(const char *, const char *, fsnode *); +void dump_fsnodes(const char *, fsnode *); +const char * inode_type(mode_t); +int set_option(option_t *, const char *, const char *); +fsnode * walk_dir(const char *, fsnode *); + +int ffs_parse_opts(const char *, fsinfo_t *); +void ffs_makefs(const char *, const char *, fsnode *, fsinfo_t *); + + + +extern u_int debug; +extern struct timespec start_time; + +/* + * If -x is specified, we want to exclude nodes which do not appear + * in the spec file. + */ +#define FSNODE_EXCLUDE_P(opts, fsnode) \ + ((opts)->onlyspec != 0 && ((fsnode)->flags & FSNODE_F_HASSPEC) == 0) + +#define DEBUG_TIME 0x00000001 + /* debug bits 1..3 unused at this time */ +#define DEBUG_WALK_DIR 0x00000010 +#define DEBUG_WALK_DIR_NODE 0x00000020 +#define DEBUG_WALK_DIR_LINKCHECK 0x00000040 +#define DEBUG_DUMP_FSNODES 0x00000080 +#define DEBUG_DUMP_FSNODES_VERBOSE 0x00000100 +#define DEBUG_FS_PARSE_OPTS 0x00000200 +#define DEBUG_FS_MAKEFS 0x00000400 +#define DEBUG_FS_VALIDATE 0x00000800 +#define DEBUG_FS_CREATE_IMAGE 0x00001000 +#define DEBUG_FS_SIZE_DIR 0x00002000 +#define DEBUG_FS_SIZE_DIR_NODE 0x00004000 +#define DEBUG_FS_SIZE_DIR_ADD_DIRENT 0x00008000 +#define DEBUG_FS_POPULATE 0x00010000 +#define DEBUG_FS_POPULATE_DIRBUF 0x00020000 +#define DEBUG_FS_POPULATE_NODE 0x00040000 +#define DEBUG_FS_WRITE_FILE 0x00080000 +#define DEBUG_FS_WRITE_FILE_BLOCK 0x00100000 +#define DEBUG_FS_MAKE_DIRBUF 0x00200000 +#define DEBUG_FS_WRITE_INODE 0x00400000 +#define DEBUG_BUF_BREAD 0x00800000 +#define DEBUG_BUF_BWRITE 0x01000000 +#define DEBUG_BUF_GETBLK 0x02000000 +#define DEBUG_APPLY_SPECFILE 0x04000000 +#define DEBUG_APPLY_SPECENTRY 0x08000000 + + +#define TIMER_START(x) \ + if (debug & DEBUG_TIME) \ + gettimeofday(&(x), NULL) + +#define TIMER_RESULTS(x,d) \ + if (debug & DEBUG_TIME) { \ + struct timeval end, td; \ + gettimeofday(&end, NULL); \ + timersub(&end, &(x), &td); \ + printf("%s took %ld.%06ld seconds\n", \ + (d), td.tv_sec, td.tv_usec); \ + } + + +#ifndef DEFAULT_FSTYPE +#define DEFAULT_FSTYPE "ffs" +#endif + + +/* + * ffs specific settings + * --------------------- + */ + +#define FFS_EI /* for opposite endian support in ffs headers */ + + +#endif /* _MAKEFS_H */ diff --git a/sys/sys/queue.h b/sys/sys/queue.h new file mode 100644 index 00000000000..419ac60d2b9 --- /dev/null +++ b/sys/sys/queue.h @@ -0,0 +1,612 @@ +/* $NetBSD: queue.h,v 1.39 2004/04/18 14:25:34 lukem Exp $ */ + +/* + * Copyright (c) 1991, 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. + * 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. + * + * @(#)queue.h 8.5 (Berkeley) 8/20/94 + */ + +#ifndef _SYS_QUEUE_H_ +#define _SYS_QUEUE_H_ + +/* + * This file defines five types of data structures: singly-linked lists, + * lists, simple queues, tail queues, and circular queues. + * + * A singly-linked list is headed by a single forward pointer. The + * elements are singly linked for minimum space and pointer manipulation + * overhead at the expense of O(n) removal for arbitrary elements. New + * elements can be added to the list after an existing element or at the + * head of the list. Elements being removed from the head of the list + * should use the explicit macro for this purpose for optimum + * efficiency. A singly-linked list may only be traversed in the forward + * direction. Singly-linked lists are ideal for applications with large + * datasets and few or no removals or for implementing a LIFO queue. + * + * A list is headed by a single forward pointer (or an array of forward + * pointers for a hash table header). The elements are doubly linked + * so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before + * or after an existing element or at the head of the list. A list + * may only be traversed in the forward direction. + * + * A simple queue is headed by a pair of pointers, one the head of the + * list and the other to the tail of the list. The elements are singly + * linked to save space, so only elements can only be removed from the + * head of the list. New elements can be added to the list after + * an existing element, at the head of the list, or at the end of the + * list. A simple queue may only be traversed in the forward direction. + * + * A tail queue is headed by a pair of pointers, one to the head of the + * list and the other to the tail of the list. The elements are doubly + * linked so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before or + * after an existing element, at the head of the list, or at the end of + * the list. A tail queue may be traversed in either direction. + * + * A circle queue is headed by a pair of pointers, one to the head of the + * list and the other to the tail of the list. The elements are doubly + * linked so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before or after + * an existing element, at the head of the list, or at the end of the list. + * A circle queue may be traversed in either direction, but has a more + * complex end of list detection. + * + * For details on the use of these macros, see the queue(3) manual page. + */ + +/* + * List definitions. + */ +#define LIST_HEAD(name, type) \ +struct name { \ + struct type *lh_first; /* first element */ \ +} + +#define LIST_HEAD_INITIALIZER(head) \ + { NULL } + +#define LIST_ENTRY(type) \ +struct { \ + struct type *le_next; /* next element */ \ + struct type **le_prev; /* address of previous next element */ \ +} + +/* + * List functions. + */ +#if defined(_KERNEL) && defined(QUEUEDEBUG) +#define QUEUEDEBUG_LIST_INSERT_HEAD(head, elm, field) \ + if ((head)->lh_first && \ + (head)->lh_first->field.le_prev != &(head)->lh_first) \ + panic("LIST_INSERT_HEAD %p %s:%d", (head), __FILE__, __LINE__); +#define QUEUEDEBUG_LIST_OP(elm, field) \ + if ((elm)->field.le_next && \ + (elm)->field.le_next->field.le_prev != \ + &(elm)->field.le_next) \ + panic("LIST_* forw %p %s:%d", (elm), __FILE__, __LINE__);\ + if (*(elm)->field.le_prev != (elm)) \ + panic("LIST_* back %p %s:%d", (elm), __FILE__, __LINE__); +#define QUEUEDEBUG_LIST_POSTREMOVE(elm, field) \ + (elm)->field.le_next = (void *)1L; \ + (elm)->field.le_prev = (void *)1L; +#else +#define QUEUEDEBUG_LIST_INSERT_HEAD(head, elm, field) +#define QUEUEDEBUG_LIST_OP(elm, field) +#define QUEUEDEBUG_LIST_POSTREMOVE(elm, field) +#endif + +#define LIST_INIT(head) do { \ + (head)->lh_first = NULL; \ +} while (/*CONSTCOND*/0) + +#define LIST_INSERT_AFTER(listelm, elm, field) do { \ + QUEUEDEBUG_LIST_OP((listelm), field) \ + if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \ + (listelm)->field.le_next->field.le_prev = \ + &(elm)->field.le_next; \ + (listelm)->field.le_next = (elm); \ + (elm)->field.le_prev = &(listelm)->field.le_next; \ +} while (/*CONSTCOND*/0) + +#define LIST_INSERT_BEFORE(listelm, elm, field) do { \ + QUEUEDEBUG_LIST_OP((listelm), field) \ + (elm)->field.le_prev = (listelm)->field.le_prev; \ + (elm)->field.le_next = (listelm); \ + *(listelm)->field.le_prev = (elm); \ + (listelm)->field.le_prev = &(elm)->field.le_next; \ +} while (/*CONSTCOND*/0) + +#define LIST_INSERT_HEAD(head, elm, field) do { \ + QUEUEDEBUG_LIST_INSERT_HEAD((head), (elm), field) \ + if (((elm)->field.le_next = (head)->lh_first) != NULL) \ + (head)->lh_first->field.le_prev = &(elm)->field.le_next;\ + (head)->lh_first = (elm); \ + (elm)->field.le_prev = &(head)->lh_first; \ +} while (/*CONSTCOND*/0) + +#define LIST_REMOVE(elm, field) do { \ + QUEUEDEBUG_LIST_OP((elm), field) \ + if ((elm)->field.le_next != NULL) \ + (elm)->field.le_next->field.le_prev = \ + (elm)->field.le_prev; \ + *(elm)->field.le_prev = (elm)->field.le_next; \ + QUEUEDEBUG_LIST_POSTREMOVE((elm), field) \ +} while (/*CONSTCOND*/0) + +#define LIST_FOREACH(var, head, field) \ + for ((var) = ((head)->lh_first); \ + (var); \ + (var) = ((var)->field.le_next)) + +/* + * List access methods. + */ +#define LIST_EMPTY(head) ((head)->lh_first == NULL) +#define LIST_FIRST(head) ((head)->lh_first) +#define LIST_NEXT(elm, field) ((elm)->field.le_next) + + +/* + * Singly-linked List definitions. + */ +#define SLIST_HEAD(name, type) \ +struct name { \ + struct type *slh_first; /* first element */ \ +} + +#define SLIST_HEAD_INITIALIZER(head) \ + { NULL } + +#define SLIST_ENTRY(type) \ +struct { \ + struct type *sle_next; /* next element */ \ +} + +/* + * Singly-linked List functions. + */ +#define SLIST_INIT(head) do { \ + (head)->slh_first = NULL; \ +} while (/*CONSTCOND*/0) + +#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \ + (elm)->field.sle_next = (slistelm)->field.sle_next; \ + (slistelm)->field.sle_next = (elm); \ +} while (/*CONSTCOND*/0) + +#define SLIST_INSERT_HEAD(head, elm, field) do { \ + (elm)->field.sle_next = (head)->slh_first; \ + (head)->slh_first = (elm); \ +} while (/*CONSTCOND*/0) + +#define SLIST_REMOVE_HEAD(head, field) do { \ + (head)->slh_first = (head)->slh_first->field.sle_next; \ +} while (/*CONSTCOND*/0) + +#define SLIST_REMOVE(head, elm, type, field) do { \ + if ((head)->slh_first == (elm)) { \ + SLIST_REMOVE_HEAD((head), field); \ + } \ + else { \ + struct type *curelm = (head)->slh_first; \ + while(curelm->field.sle_next != (elm)) \ + curelm = curelm->field.sle_next; \ + curelm->field.sle_next = \ + curelm->field.sle_next->field.sle_next; \ + } \ +} while (/*CONSTCOND*/0) + +#define SLIST_FOREACH(var, head, field) \ + for((var) = (head)->slh_first; (var); (var) = (var)->field.sle_next) + +/* + * Singly-linked List access methods. + */ +#define SLIST_EMPTY(head) ((head)->slh_first == NULL) +#define SLIST_FIRST(head) ((head)->slh_first) +#define SLIST_NEXT(elm, field) ((elm)->field.sle_next) + + +/* + * Singly-linked Tail queue declarations. + */ +#define STAILQ_HEAD(name, type) \ +struct name { \ + struct type *stqh_first; /* first element */ \ + struct type **stqh_last; /* addr of last next element */ \ +} + +#define STAILQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).stqh_first } + +#define STAILQ_ENTRY(type) \ +struct { \ + struct type *stqe_next; /* next element */ \ +} + +/* + * Singly-linked Tail queue functions. + */ +#define STAILQ_INIT(head) do { \ + (head)->stqh_first = NULL; \ + (head)->stqh_last = &(head)->stqh_first; \ +} while (/*CONSTCOND*/0) + +#define STAILQ_INSERT_HEAD(head, elm, field) do { \ + if (((elm)->field.stqe_next = (head)->stqh_first) == NULL) \ + (head)->stqh_last = &(elm)->field.stqe_next; \ + (head)->stqh_first = (elm); \ +} while (/*CONSTCOND*/0) + +#define STAILQ_INSERT_TAIL(head, elm, field) do { \ + (elm)->field.stqe_next = NULL; \ + *(head)->stqh_last = (elm); \ + (head)->stqh_last = &(elm)->field.stqe_next; \ +} while (/*CONSTCOND*/0) + +#define STAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ + if (((elm)->field.stqe_next = (listelm)->field.stqe_next) == NULL)\ + (head)->stqh_last = &(elm)->field.stqe_next; \ + (listelm)->field.stqe_next = (elm); \ +} while (/*CONSTCOND*/0) + +#define STAILQ_REMOVE_HEAD(head, field) do { \ + if (((head)->stqh_first = (head)->stqh_first->field.stqe_next) == NULL) \ + (head)->stqh_last = &(head)->stqh_first; \ +} while (/*CONSTCOND*/0) + +#define STAILQ_REMOVE(head, elm, type, field) do { \ + if ((head)->stqh_first == (elm)) { \ + STAILQ_REMOVE_HEAD((head), field); \ + } else { \ + struct type *curelm = (head)->stqh_first; \ + while (curelm->field.stqe_next != (elm)) \ + curelm = curelm->field.stqe_next; \ + if ((curelm->field.stqe_next = \ + curelm->field.stqe_next->field.stqe_next) == NULL) \ + (head)->stqh_last = &(curelm)->field.stqe_next; \ + } \ +} while (/*CONSTCOND*/0) + +#define STAILQ_FOREACH(var, head, field) \ + for ((var) = ((head)->stqh_first); \ + (var); \ + (var) = ((var)->field.stqe_next)) + +/* + * Singly-linked Tail queue access methods. + */ +#define STAILQ_EMPTY(head) ((head)->stqh_first == NULL) +#define STAILQ_FIRST(head) ((head)->stqh_first) +#define STAILQ_NEXT(elm, field) ((elm)->field.stqe_next) + + +/* + * Simple queue definitions. + */ +#define SIMPLEQ_HEAD(name, type) \ +struct name { \ + struct type *sqh_first; /* first element */ \ + struct type **sqh_last; /* addr of last next element */ \ +} + +#define SIMPLEQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).sqh_first } + +#define SIMPLEQ_ENTRY(type) \ +struct { \ + struct type *sqe_next; /* next element */ \ +} + +/* + * Simple queue functions. + */ +#define SIMPLEQ_INIT(head) do { \ + (head)->sqh_first = NULL; \ + (head)->sqh_last = &(head)->sqh_first; \ +} while (/*CONSTCOND*/0) + +#define SIMPLEQ_INSERT_HEAD(head, elm, field) do { \ + if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \ + (head)->sqh_last = &(elm)->field.sqe_next; \ + (head)->sqh_first = (elm); \ +} while (/*CONSTCOND*/0) + +#define SIMPLEQ_INSERT_TAIL(head, elm, field) do { \ + (elm)->field.sqe_next = NULL; \ + *(head)->sqh_last = (elm); \ + (head)->sqh_last = &(elm)->field.sqe_next; \ +} while (/*CONSTCOND*/0) + +#define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ + if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\ + (head)->sqh_last = &(elm)->field.sqe_next; \ + (listelm)->field.sqe_next = (elm); \ +} while (/*CONSTCOND*/0) + +#define SIMPLEQ_REMOVE_HEAD(head, field) do { \ + if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \ + (head)->sqh_last = &(head)->sqh_first; \ +} while (/*CONSTCOND*/0) + +#define SIMPLEQ_REMOVE(head, elm, type, field) do { \ + if ((head)->sqh_first == (elm)) { \ + SIMPLEQ_REMOVE_HEAD((head), field); \ + } else { \ + struct type *curelm = (head)->sqh_first; \ + while (curelm->field.sqe_next != (elm)) \ + curelm = curelm->field.sqe_next; \ + if ((curelm->field.sqe_next = \ + curelm->field.sqe_next->field.sqe_next) == NULL) \ + (head)->sqh_last = &(curelm)->field.sqe_next; \ + } \ +} while (/*CONSTCOND*/0) + +#define SIMPLEQ_FOREACH(var, head, field) \ + for ((var) = ((head)->sqh_first); \ + (var); \ + (var) = ((var)->field.sqe_next)) + +/* + * Simple queue access methods. + */ +#define SIMPLEQ_EMPTY(head) ((head)->sqh_first == NULL) +#define SIMPLEQ_FIRST(head) ((head)->sqh_first) +#define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next) + + +/* + * Tail queue definitions. + */ +#define TAILQ_HEAD(name, type) \ +struct name { \ + struct type *tqh_first; /* first element */ \ + struct type **tqh_last; /* addr of last next element */ \ +} + +#define TAILQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).tqh_first } + +#define TAILQ_ENTRY(type) \ +struct { \ + struct type *tqe_next; /* next element */ \ + struct type **tqe_prev; /* address of previous next element */ \ +} + +/* + * Tail queue functions. + */ +#if defined(_KERNEL) && defined(QUEUEDEBUG) +#define QUEUEDEBUG_TAILQ_INSERT_HEAD(head, elm, field) \ + if ((head)->tqh_first && \ + (head)->tqh_first->field.tqe_prev != &(head)->tqh_first) \ + panic("TAILQ_INSERT_HEAD %p %s:%d", (head), __FILE__, __LINE__); +#define QUEUEDEBUG_TAILQ_INSERT_TAIL(head, elm, field) \ + if (*(head)->tqh_last != NULL) \ + panic("TAILQ_INSERT_TAIL %p %s:%d", (head), __FILE__, __LINE__); +#define QUEUEDEBUG_TAILQ_OP(elm, field) \ + if ((elm)->field.tqe_next && \ + (elm)->field.tqe_next->field.tqe_prev != \ + &(elm)->field.tqe_next) \ + panic("TAILQ_* forw %p %s:%d", (elm), __FILE__, __LINE__);\ + if (*(elm)->field.tqe_prev != (elm)) \ + panic("TAILQ_* back %p %s:%d", (elm), __FILE__, __LINE__); +#define QUEUEDEBUG_TAILQ_PREREMOVE(head, elm, field) \ + if ((elm)->field.tqe_next == NULL && \ + (head)->tqh_last != &(elm)->field.tqe_next) \ + panic("TAILQ_PREREMOVE head %p elm %p %s:%d", \ + (head), (elm), __FILE__, __LINE__); +#define QUEUEDEBUG_TAILQ_POSTREMOVE(elm, field) \ + (elm)->field.tqe_next = (void *)1L; \ + (elm)->field.tqe_prev = (void *)1L; +#else +#define QUEUEDEBUG_TAILQ_INSERT_HEAD(head, elm, field) +#define QUEUEDEBUG_TAILQ_INSERT_TAIL(head, elm, field) +#define QUEUEDEBUG_TAILQ_OP(elm, field) +#define QUEUEDEBUG_TAILQ_PREREMOVE(head, elm, field) +#define QUEUEDEBUG_TAILQ_POSTREMOVE(elm, field) +#endif + +#define TAILQ_INIT(head) do { \ + (head)->tqh_first = NULL; \ + (head)->tqh_last = &(head)->tqh_first; \ +} while (/*CONSTCOND*/0) + +#define TAILQ_INSERT_HEAD(head, elm, field) do { \ + QUEUEDEBUG_TAILQ_INSERT_HEAD((head), (elm), field) \ + if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \ + (head)->tqh_first->field.tqe_prev = \ + &(elm)->field.tqe_next; \ + else \ + (head)->tqh_last = &(elm)->field.tqe_next; \ + (head)->tqh_first = (elm); \ + (elm)->field.tqe_prev = &(head)->tqh_first; \ +} while (/*CONSTCOND*/0) + +#define TAILQ_INSERT_TAIL(head, elm, field) do { \ + QUEUEDEBUG_TAILQ_INSERT_TAIL((head), (elm), field) \ + (elm)->field.tqe_next = NULL; \ + (elm)->field.tqe_prev = (head)->tqh_last; \ + *(head)->tqh_last = (elm); \ + (head)->tqh_last = &(elm)->field.tqe_next; \ +} while (/*CONSTCOND*/0) + +#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ + QUEUEDEBUG_TAILQ_OP((listelm), field) \ + if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\ + (elm)->field.tqe_next->field.tqe_prev = \ + &(elm)->field.tqe_next; \ + else \ + (head)->tqh_last = &(elm)->field.tqe_next; \ + (listelm)->field.tqe_next = (elm); \ + (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \ +} while (/*CONSTCOND*/0) + +#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \ + QUEUEDEBUG_TAILQ_OP((listelm), field) \ + (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \ + (elm)->field.tqe_next = (listelm); \ + *(listelm)->field.tqe_prev = (elm); \ + (listelm)->field.tqe_prev = &(elm)->field.tqe_next; \ +} while (/*CONSTCOND*/0) + +#define TAILQ_REMOVE(head, elm, field) do { \ + QUEUEDEBUG_TAILQ_PREREMOVE((head), (elm), field) \ + QUEUEDEBUG_TAILQ_OP((elm), field) \ + if (((elm)->field.tqe_next) != NULL) \ + (elm)->field.tqe_next->field.tqe_prev = \ + (elm)->field.tqe_prev; \ + else \ + (head)->tqh_last = (elm)->field.tqe_prev; \ + *(elm)->field.tqe_prev = (elm)->field.tqe_next; \ + QUEUEDEBUG_TAILQ_POSTREMOVE((elm), field); \ +} while (/*CONSTCOND*/0) + +#define TAILQ_FOREACH(var, head, field) \ + for ((var) = ((head)->tqh_first); \ + (var); \ + (var) = ((var)->field.tqe_next)) + +#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \ + for ((var) = (*(((struct headname *)((head)->tqh_last))->tqh_last)); \ + (var); \ + (var) = (*(((struct headname *)((var)->field.tqe_prev))->tqh_last))) + +/* + * Tail queue access methods. + */ +#define TAILQ_EMPTY(head) ((head)->tqh_first == NULL) +#define TAILQ_FIRST(head) ((head)->tqh_first) +#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next) + +#define TAILQ_LAST(head, headname) \ + (*(((struct headname *)((head)->tqh_last))->tqh_last)) +#define TAILQ_PREV(elm, headname, field) \ + (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last)) + + +/* + * Circular queue definitions. + */ +#define CIRCLEQ_HEAD(name, type) \ +struct name { \ + struct type *cqh_first; /* first element */ \ + struct type *cqh_last; /* last element */ \ +} + +#define CIRCLEQ_HEAD_INITIALIZER(head) \ + { (void *)&head, (void *)&head } + +#define CIRCLEQ_ENTRY(type) \ +struct { \ + struct type *cqe_next; /* next element */ \ + struct type *cqe_prev; /* previous element */ \ +} + +/* + * Circular queue functions. + */ +#define CIRCLEQ_INIT(head) do { \ + (head)->cqh_first = (void *)(head); \ + (head)->cqh_last = (void *)(head); \ +} while (/*CONSTCOND*/0) + +#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ + (elm)->field.cqe_next = (listelm)->field.cqe_next; \ + (elm)->field.cqe_prev = (listelm); \ + if ((listelm)->field.cqe_next == (void *)(head)) \ + (head)->cqh_last = (elm); \ + else \ + (listelm)->field.cqe_next->field.cqe_prev = (elm); \ + (listelm)->field.cqe_next = (elm); \ +} while (/*CONSTCOND*/0) + +#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do { \ + (elm)->field.cqe_next = (listelm); \ + (elm)->field.cqe_prev = (listelm)->field.cqe_prev; \ + if ((listelm)->field.cqe_prev == (void *)(head)) \ + (head)->cqh_first = (elm); \ + else \ + (listelm)->field.cqe_prev->field.cqe_next = (elm); \ + (listelm)->field.cqe_prev = (elm); \ +} while (/*CONSTCOND*/0) + +#define CIRCLEQ_INSERT_HEAD(head, elm, field) do { \ + (elm)->field.cqe_next = (head)->cqh_first; \ + (elm)->field.cqe_prev = (void *)(head); \ + if ((head)->cqh_last == (void *)(head)) \ + (head)->cqh_last = (elm); \ + else \ + (head)->cqh_first->field.cqe_prev = (elm); \ + (head)->cqh_first = (elm); \ +} while (/*CONSTCOND*/0) + +#define CIRCLEQ_INSERT_TAIL(head, elm, field) do { \ + (elm)->field.cqe_next = (void *)(head); \ + (elm)->field.cqe_prev = (head)->cqh_last; \ + if ((head)->cqh_first == (void *)(head)) \ + (head)->cqh_first = (elm); \ + else \ + (head)->cqh_last->field.cqe_next = (elm); \ + (head)->cqh_last = (elm); \ +} while (/*CONSTCOND*/0) + +#define CIRCLEQ_REMOVE(head, elm, field) do { \ + if ((elm)->field.cqe_next == (void *)(head)) \ + (head)->cqh_last = (elm)->field.cqe_prev; \ + else \ + (elm)->field.cqe_next->field.cqe_prev = \ + (elm)->field.cqe_prev; \ + if ((elm)->field.cqe_prev == (void *)(head)) \ + (head)->cqh_first = (elm)->field.cqe_next; \ + else \ + (elm)->field.cqe_prev->field.cqe_next = \ + (elm)->field.cqe_next; \ +} while (/*CONSTCOND*/0) + +#define CIRCLEQ_FOREACH(var, head, field) \ + for ((var) = ((head)->cqh_first); \ + (var) != (void *)(head); \ + (var) = ((var)->field.cqe_next)) + +#define CIRCLEQ_FOREACH_REVERSE(var, head, field) \ + for ((var) = ((head)->cqh_last); \ + (var) != (void *)(head); \ + (var) = ((var)->field.cqe_prev)) + +/* + * Circular queue access methods. + */ +#define CIRCLEQ_EMPTY(head) ((head)->cqh_first == (void *)(head)) +#define CIRCLEQ_FIRST(head) ((head)->cqh_first) +#define CIRCLEQ_LAST(head) ((head)->cqh_last) +#define CIRCLEQ_NEXT(elm, field) ((elm)->field.cqe_next) +#define CIRCLEQ_PREV(elm, field) ((elm)->field.cqe_prev) + +#endif /* !_SYS_QUEUE_H_ */ diff --git a/sys/ufs/ffs/ffs_bswap.c b/sys/ufs/ffs/ffs_bswap.c new file mode 100644 index 00000000000..c9a271c3525 --- /dev/null +++ b/sys/ufs/ffs/ffs_bswap.c @@ -0,0 +1,270 @@ +/* $NetBSD: ffs_bswap.c,v 1.28 2004/05/25 14:54:59 hannken Exp $ */ + +/* + * Copyright (c) 1998 Manuel Bouyer. + * + * 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 Manuel Bouyer. + * 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. + * + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include +__KERNEL_RCSID(0, "$NetBSD: ffs_bswap.c,v 1.28 2004/05/25 14:54:59 hannken Exp $"); + +#include +#if defined(_KERNEL) +#include +#endif + +#include +#include +#include +#include + +#if !defined(_KERNEL) +#include +#include +#include +#include +#define panic(x) printf("%s\n", (x)), abort() +#endif + +void +ffs_sb_swap(struct fs *o, struct fs *n) +{ + int i; + u_int32_t *o32, *n32; + + /* + * In order to avoid a lot of lines, as the first N fields (52) + * of the superblock up to fs_fmod are u_int32_t, we just loop + * here to convert them. + */ + o32 = (u_int32_t *)o; + n32 = (u_int32_t *)n; + for (i = 0; i < offsetof(struct fs, fs_fmod) / sizeof(u_int32_t); i++) + n32[i] = bswap32(o32[i]); + + n->fs_swuid = bswap64(o->fs_swuid); + n->fs_cgrotor = bswap32(o->fs_cgrotor); /* Unused */ + n->fs_old_cpc = bswap32(o->fs_old_cpc); + + /* These fields overlap with a possible location for the + * historic FS_DYNAMICPOSTBLFMT postbl table, and with the + * first half of the historic FS_42POSTBLFMT postbl table. + */ + n->fs_maxbsize = bswap32(o->fs_maxbsize); + n->fs_sblockloc = bswap64(o->fs_sblockloc); + ffs_csumtotal_swap(&o->fs_cstotal, &n->fs_cstotal); + n->fs_time = bswap64(o->fs_time); + n->fs_size = bswap64(o->fs_size); + n->fs_dsize = bswap64(o->fs_dsize); + n->fs_csaddr = bswap64(o->fs_csaddr); + n->fs_pendingblocks = bswap64(o->fs_pendingblocks); + n->fs_pendinginodes = bswap32(o->fs_pendinginodes); + + /* These fields overlap with the second half of the + * historic FS_42POSTBLFMT postbl table + */ + for (i = 0; i < FSMAXSNAP; i++) + n->fs_snapinum[i] = bswap32(o->fs_snapinum[i]); + n->fs_avgfilesize = bswap32(o->fs_avgfilesize); + n->fs_avgfpdir = bswap32(o->fs_avgfpdir); + /* fs_sparecon[28] - ignore for now */ + n->fs_flags = bswap32(o->fs_flags); + n->fs_contigsumsize = bswap32(o->fs_contigsumsize); + n->fs_maxsymlinklen = bswap32(o->fs_maxsymlinklen); + n->fs_old_inodefmt = bswap32(o->fs_old_inodefmt); + n->fs_maxfilesize = bswap64(o->fs_maxfilesize); + n->fs_qbmask = bswap64(o->fs_qbmask); + n->fs_qfmask = bswap64(o->fs_qfmask); + n->fs_state = bswap32(o->fs_state); + n->fs_old_postblformat = bswap32(o->fs_old_postblformat); + n->fs_old_nrpos = bswap32(o->fs_old_nrpos); + n->fs_old_postbloff = bswap32(o->fs_old_postbloff); + n->fs_old_rotbloff = bswap32(o->fs_old_rotbloff); + + n->fs_magic = bswap32(o->fs_magic); +} + +void +ffs_dinode1_swap(struct ufs1_dinode *o, struct ufs1_dinode *n) +{ + + n->di_mode = bswap16(o->di_mode); + n->di_nlink = bswap16(o->di_nlink); + n->di_u.oldids[0] = bswap16(o->di_u.oldids[0]); + n->di_u.oldids[1] = bswap16(o->di_u.oldids[1]); + n->di_size = bswap64(o->di_size); + n->di_atime = bswap32(o->di_atime); + n->di_atimensec = bswap32(o->di_atimensec); + n->di_mtime = bswap32(o->di_mtime); + n->di_mtimensec = bswap32(o->di_mtimensec); + n->di_ctime = bswap32(o->di_ctime); + n->di_ctimensec = bswap32(o->di_ctimensec); + memcpy(n->di_db, o->di_db, (NDADDR + NIADDR) * sizeof(u_int32_t)); + n->di_flags = bswap32(o->di_flags); + n->di_blocks = bswap32(o->di_blocks); + n->di_gen = bswap32(o->di_gen); + n->di_uid = bswap32(o->di_uid); + n->di_gid = bswap32(o->di_gid); +} + +void +ffs_dinode2_swap(struct ufs2_dinode *o, struct ufs2_dinode *n) +{ + n->di_mode = bswap16(o->di_mode); + n->di_nlink = bswap16(o->di_nlink); + n->di_uid = bswap32(o->di_uid); + n->di_gid = bswap32(o->di_gid); + n->di_blksize = bswap32(o->di_blksize); + n->di_size = bswap64(o->di_size); + n->di_blocks = bswap64(o->di_blocks); + n->di_atime = bswap64(o->di_atime); + n->di_atimensec = bswap32(o->di_atimensec); + n->di_mtime = bswap64(o->di_mtime); + n->di_mtimensec = bswap32(o->di_mtimensec); + n->di_ctime = bswap64(o->di_ctime); + n->di_ctimensec = bswap32(o->di_ctimensec); + n->di_birthtime = bswap64(o->di_ctime); + n->di_birthnsec = bswap32(o->di_ctimensec); + n->di_gen = bswap32(o->di_gen); + n->di_kernflags = bswap32(o->di_kernflags); + n->di_flags = bswap32(o->di_flags); + n->di_extsize = bswap32(o->di_extsize); + memcpy(n->di_extb, o->di_extb, (NXADDR + NDADDR + NIADDR) * 8); +} + +void +ffs_csum_swap(struct csum *o, struct csum *n, int size) +{ + int i; + u_int32_t *oint, *nint; + + oint = (u_int32_t*)o; + nint = (u_int32_t*)n; + + for (i = 0; i < size / sizeof(u_int32_t); i++) + nint[i] = bswap32(oint[i]); +} + +void +ffs_csumtotal_swap(struct csum_total *o, struct csum_total *n) +{ + n->cs_ndir = bswap64(o->cs_ndir); + n->cs_nbfree = bswap64(o->cs_nbfree); + n->cs_nifree = bswap64(o->cs_nifree); + n->cs_nffree = bswap64(o->cs_nffree); +} + +/* + * Note that ffs_cg_swap may be called with o == n. + */ +void +ffs_cg_swap(struct cg *o, struct cg *n, struct fs *fs) +{ + int i; + u_int32_t *n32, *o32; + u_int16_t *n16, *o16; + int32_t btotoff, boff, clustersumoff; + + n->cg_firstfield = bswap32(o->cg_firstfield); + n->cg_magic = bswap32(o->cg_magic); + n->cg_old_time = bswap32(o->cg_old_time); + n->cg_cgx = bswap32(o->cg_cgx); + n->cg_old_ncyl = bswap16(o->cg_old_ncyl); + n->cg_old_niblk = bswap16(o->cg_old_niblk); + n->cg_ndblk = bswap32(o->cg_ndblk); + n->cg_cs.cs_ndir = bswap32(o->cg_cs.cs_ndir); + n->cg_cs.cs_nbfree = bswap32(o->cg_cs.cs_nbfree); + n->cg_cs.cs_nifree = bswap32(o->cg_cs.cs_nifree); + n->cg_cs.cs_nffree = bswap32(o->cg_cs.cs_nffree); + n->cg_rotor = bswap32(o->cg_rotor); + n->cg_frotor = bswap32(o->cg_frotor); + n->cg_irotor = bswap32(o->cg_irotor); + for (i = 0; i < MAXFRAG; i++) + n->cg_frsum[i] = bswap32(o->cg_frsum[i]); + + if ((fs->fs_magic != FS_UFS2_MAGIC) && + (fs->fs_old_postblformat == FS_42POSTBLFMT)) { /* old format */ + struct ocg *on, *oo; + int j; + on = (struct ocg *)n; + oo = (struct ocg *)o; + + for (i = 0; i < 32; i++) { + on->cg_btot[i] = bswap32(oo->cg_btot[i]); + for (j = 0; j < 8; j++) + on->cg_b[i][j] = bswap16(oo->cg_b[i][j]); + } + memmove(on->cg_iused, oo->cg_iused, 256); + on->cg_magic = bswap32(oo->cg_magic); + } else { /* new format */ + + n->cg_old_btotoff = bswap32(o->cg_old_btotoff); + n->cg_old_boff = bswap32(o->cg_old_boff); + n->cg_iusedoff = bswap32(o->cg_iusedoff); + n->cg_freeoff = bswap32(o->cg_freeoff); + n->cg_nextfreeoff = bswap32(o->cg_nextfreeoff); + n->cg_clustersumoff = bswap32(o->cg_clustersumoff); + n->cg_clusteroff = bswap32(o->cg_clusteroff); + n->cg_nclusterblks = bswap32(o->cg_nclusterblks); + n->cg_niblk = bswap32(o->cg_niblk); + n->cg_initediblk = bswap32(o->cg_initediblk); + n->cg_time = bswap64(o->cg_time); + + if (fs->fs_magic == FS_UFS2_MAGIC) + return; + + if (n->cg_magic == CG_MAGIC) { + btotoff = n->cg_old_btotoff; + boff = n->cg_old_boff; + clustersumoff = n->cg_clustersumoff; + } else { + btotoff = bswap32(n->cg_old_btotoff); + boff = bswap32(n->cg_old_boff); + clustersumoff = bswap32(n->cg_clustersumoff); + } + n32 = (u_int32_t *)((u_int8_t *)n + btotoff); + o32 = (u_int32_t *)((u_int8_t *)o + btotoff); + n16 = (u_int16_t *)((u_int8_t *)n + boff); + o16 = (u_int16_t *)((u_int8_t *)o + boff); + + for (i = 0; i < fs->fs_old_cpg; i++) + n32[i] = bswap32(o32[i]); + + for (i = 0; i < fs->fs_old_cpg * fs->fs_old_nrpos; i++) + n16[i] = bswap16(o16[i]); + + n32 = (u_int32_t *)((u_int8_t *)n + clustersumoff); + o32 = (u_int32_t *)((u_int8_t *)o + clustersumoff); + for (i = 1; i < fs->fs_contigsumsize + 1; i++) + n32[i] = bswap32(o32[i]); + } +} diff --git a/sys/ufs/ffs/ffs_extern.h b/sys/ufs/ffs/ffs_extern.h new file mode 100644 index 00000000000..ee03ca77f38 --- /dev/null +++ b/sys/ufs/ffs/ffs_extern.h @@ -0,0 +1,202 @@ +/* $NetBSD: ffs_extern.h,v 1.40 2004/06/04 07:43:56 he Exp $ */ + +/*- + * Copyright (c) 1991, 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. + * 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. + * + * @(#)ffs_extern.h 8.6 (Berkeley) 3/30/95 + */ + +#ifndef _UFS_FFS_FFS_EXTERN_H_ +#define _UFS_FFS_FFS_EXTERN_H_ + +/* + * Sysctl values for the fast filesystem. + */ +#define FFS_CLUSTERREAD 1 /* cluster reading enabled */ +#define FFS_CLUSTERWRITE 2 /* cluster writing enabled */ +#define FFS_REALLOCBLKS 3 /* block reallocation enabled */ +#define FFS_ASYNCFREE 4 /* asynchronous block freeing enabled */ +#define FFS_LOG_CHANGEOPT 5 /* log optimalization strategy change */ +#define FFS_MAXID 6 /* number of valid ffs ids */ + +#define FFS_NAMES { \ + { 0, 0 }, \ + { "doclusterread", CTLTYPE_INT }, \ + { "doclusterwrite", CTLTYPE_INT }, \ + { "doreallocblks", CTLTYPE_INT }, \ + { "doasyncfree", CTLTYPE_INT }, \ + { "log_changeopt", CTLTYPE_INT }, \ +} + +struct buf; +struct fid; +struct fs; +struct inode; +struct ufs1_dinode; +struct ufs2_dinode; +struct mount; +struct nameidata; +struct proc; +struct statvfs; +struct timeval; +struct timespec; +struct ucred; +struct ufsmount; +struct uio; +struct vnode; +struct mbuf; +struct cg; + +extern struct pool ffs_inode_pool; /* memory pool for inodes */ +extern struct pool ffs_dinode1_pool; /* memory pool for UFS1 dinodes */ +extern struct pool ffs_dinode2_pool; /* memory pool for UFS2 dinodes */ + +__BEGIN_DECLS + +/* ffs_alloc.c */ +int ffs_alloc __P((struct inode *, daddr_t, daddr_t , int, struct ucred *, + daddr_t *)); +int ffs_realloccg __P((struct inode *, daddr_t, daddr_t, int, int , + struct ucred *, struct buf **, daddr_t *)); +int ffs_reallocblks __P((void *)); +int ffs_valloc __P((void *)); +daddr_t ffs_blkpref_ufs1 __P((struct inode *, daddr_t, int, int32_t *)); +daddr_t ffs_blkpref_ufs2 __P((struct inode *, daddr_t, int, int64_t *)); +void ffs_blkfree __P((struct fs *, struct vnode *, daddr_t, long, ino_t)); +int ffs_vfree __P((void *)); +void ffs_clusteracct __P((struct fs *, struct cg *, int32_t, int)); +int ffs_checkfreefile __P((struct fs *, struct vnode *, ino_t)); + +/* ffs_balloc.c */ +int ffs_balloc __P((void *)); + +/* ffs_bswap.c */ +void ffs_sb_swap __P((struct fs*, struct fs *)); +void ffs_dinode1_swap __P((struct ufs1_dinode *, struct ufs1_dinode *)); +void ffs_dinode2_swap __P((struct ufs2_dinode *, struct ufs2_dinode *)); +void ffs_csum_swap __P((struct csum *, struct csum *, int)); +void ffs_csumtotal_swap __P((struct csum_total *, struct csum_total *)); +void ffs_cg_swap __P((struct cg *, struct cg *, struct fs *)); + +/* ffs_inode.c */ +int ffs_update __P((void *)); +int ffs_truncate __P((void *)); + +/* ffs_subr.c */ +void ffs_load_inode __P((struct buf *, struct inode *, struct fs *, ino_t)); +int ffs_blkatoff __P((void *)); +int ffs_freefile __P((void *)); +void ffs_fragacct __P((struct fs *, int, int32_t[], int, int)); +#ifdef DIAGNOSTIC +void ffs_checkoverlap __P((struct buf *, struct inode *)); +#endif +int ffs_isblock __P((struct fs *, u_char *, int32_t)); +int ffs_isfreeblock __P((struct fs *, u_char *, int32_t)); +void ffs_clrblock __P((struct fs *, u_char *, int32_t)); +void ffs_setblock __P((struct fs *, u_char *, int32_t)); + +/* ffs_vfsops.c */ +void ffs_init __P((void)); +void ffs_reinit __P((void)); +void ffs_done __P((void)); +int ffs_mountroot __P((void)); +int ffs_mount __P((struct mount *, const char *, void *, struct nameidata *, + struct proc *)); +int ffs_reload __P((struct mount *, struct ucred *, struct proc *)); +int ffs_mountfs __P((struct vnode *, struct mount *, struct proc *)); +int ffs_unmount __P((struct mount *, int, struct proc *)); +int ffs_flushfiles __P((struct mount *, int, struct proc *)); +int ffs_statvfs __P((struct mount *, struct statvfs *, struct proc *)); +int ffs_sync __P((struct mount *, int, struct ucred *, struct proc *)); +int ffs_vget __P((struct mount *, ino_t, struct vnode **)); +int ffs_fhtovp __P((struct mount *, struct fid *, struct vnode **)); +int ffs_vptofh __P((struct vnode *, struct fid *)); +int ffs_sbupdate __P((struct ufsmount *, int)); +int ffs_cgupdate __P((struct ufsmount *, int)); + +/* ffs_appleufs.c */ +u_int16_t ffs_appleufs_cksum __P((const struct appleufslabel *)); +int ffs_appleufs_validate __P((const char*,const struct appleufslabel *,struct appleufslabel *)); +void ffs_appleufs_set __P((struct appleufslabel *, const char *, time_t, uint64_t)); + + +/* ffs_vnops.c */ +int ffs_read __P((void *)); +int ffs_write __P((void *)); +int ffs_fsync __P((void *)); +int ffs_reclaim __P((void *)); +int ffs_getpages __P((void *)); +int ffs_putpages __P((void *)); +void ffs_gop_size __P((struct vnode *, off_t, off_t *, int)); + +#ifdef SYSCTL_SETUP_PROTO +SYSCTL_SETUP_PROTO(sysctl_vfs_ffs_setup); +#endif /* SYSCTL_SETUP_PROTO */ + +__END_DECLS + + +/* + * Snapshot function prototypes. + */ +int ffs_snapblkfree(struct fs *, struct vnode *, daddr_t, long, ino_t); +void ffs_snapremove(struct vnode *); +int ffs_snapshot(struct mount *, struct vnode *, struct timespec *); +void ffs_snapshot_mount(struct mount *); +void ffs_snapshot_unmount(struct mount *); +void ffs_snapgone(struct inode *); + +/* + * Soft dependency function prototypes. + */ +void softdep_initialize __P((void)); +void softdep_reinitialize __P((void)); +int softdep_mount __P((struct vnode *, struct mount *, struct fs *, + struct ucred *)); +int softdep_flushworklist __P((struct mount *, int *, struct proc *)); +int softdep_flushfiles __P((struct mount *, int, struct proc *)); +void softdep_update_inodeblock __P((struct inode *, struct buf *, int)); +void softdep_load_inodeblock __P((struct inode *)); +void softdep_freefile __P((void *)); +void softdep_setup_freeblocks __P((struct inode *, off_t, int)); +void softdep_setup_inomapdep __P((struct buf *, struct inode *, ino_t)); +void softdep_setup_blkmapdep __P((struct buf *, struct fs *, daddr_t)); +void softdep_setup_allocdirect __P((struct inode *, daddr_t, daddr_t, + daddr_t, long, long, struct buf *)); +void softdep_setup_allocindir_meta __P((struct buf *, struct inode *, + struct buf *, int, daddr_t)); +void softdep_setup_allocindir_page __P((struct inode *, daddr_t, + struct buf *, int, daddr_t, daddr_t, struct buf *)); +void softdep_fsync_mountdev __P((struct vnode *)); +int softdep_sync_metadata __P((void *)); + +extern int (**ffs_vnodeop_p) __P((void *)); +extern int (**ffs_specop_p) __P((void *)); +extern int (**ffs_fifoop_p) __P((void *)); + +#endif /* !_UFS_FFS_FFS_EXTERN_H_ */ diff --git a/sys/ufs/ffs/ffs_subr.c b/sys/ufs/ffs/ffs_subr.c new file mode 100644 index 00000000000..f08724ea03f --- /dev/null +++ b/sys/ufs/ffs/ffs_subr.c @@ -0,0 +1,351 @@ +/* $NetBSD: ffs_subr.c,v 1.32 2003/12/30 12:33:24 pk Exp $ */ + +/* + * Copyright (c) 1982, 1986, 1989, 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. + * 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. + * + * @(#)ffs_subr.c 8.5 (Berkeley) 3/21/95 + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include +__KERNEL_RCSID(0, "$NetBSD: ffs_subr.c,v 1.32 2003/12/30 12:33:24 pk Exp $"); + +#include + +/* in ffs_tables.c */ +extern const int inside[], around[]; +extern const u_char * const fragtbl[]; + +#ifndef _KERNEL +#include +#include +#include +#include +void panic __P((const char *, ...)) + __attribute__((__noreturn__,__format__(__printf__,1,2))); + +#else /* _KERNEL */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Return buffer with the contents of block "offset" from the beginning of + * directory "ip". If "res" is non-zero, fill it in with a pointer to the + * remaining space in the directory. + */ +int +ffs_blkatoff(v) + void *v; +{ + struct vop_blkatoff_args /* { + struct vnode *a_vp; + off_t a_offset; + char **a_res; + struct buf **a_bpp; + } */ *ap = v; + struct inode *ip; + struct fs *fs; + struct buf *bp; + daddr_t lbn; + int bsize, error; + + ip = VTOI(ap->a_vp); + fs = ip->i_fs; + lbn = lblkno(fs, ap->a_offset); + bsize = blksize(fs, ip, lbn); + + *ap->a_bpp = NULL; + if ((error = bread(ap->a_vp, lbn, bsize, NOCRED, &bp)) != 0) { + brelse(bp); + return (error); + } + if (ap->a_res) + *ap->a_res = (char *)bp->b_data + blkoff(fs, ap->a_offset); + *ap->a_bpp = bp; + return (0); +} + + +/* + * Load up the contents of an inode and copy the appropriate pieces + * to the incore copy. + */ +void +ffs_load_inode(bp, ip, fs, ino) + struct buf *bp; + struct inode *ip; + struct fs *fs; + ino_t ino; +{ + struct ufs1_dinode *dp1; + struct ufs2_dinode *dp2; + + if (ip->i_ump->um_fstype == UFS1) { + dp1 = (struct ufs1_dinode *)bp->b_data + ino_to_fsbo(fs, ino); +#ifdef FFS_EI + if (UFS_FSNEEDSWAP(fs)) + ffs_dinode1_swap(dp1, ip->i_din.ffs1_din); + else +#endif + *ip->i_din.ffs1_din = *dp1; + + ip->i_mode = ip->i_ffs1_mode; + ip->i_nlink = ip->i_ffs1_nlink; + ip->i_size = ip->i_ffs1_size; + ip->i_flags = ip->i_ffs1_flags; + ip->i_gen = ip->i_ffs1_gen; + ip->i_uid = ip->i_ffs1_uid; + ip->i_gid = ip->i_ffs1_gid; + } else { + dp2 = (struct ufs2_dinode *)bp->b_data + ino_to_fsbo(fs, ino); +#ifdef FFS_EI + if (UFS_FSNEEDSWAP(fs)) + ffs_dinode2_swap(dp2, ip->i_din.ffs2_din); + else +#endif + *ip->i_din.ffs2_din = *dp2; + + ip->i_mode = ip->i_ffs2_mode; + ip->i_nlink = ip->i_ffs2_nlink; + ip->i_size = ip->i_ffs2_size; + ip->i_flags = ip->i_ffs2_flags; + ip->i_gen = ip->i_ffs2_gen; + ip->i_uid = ip->i_ffs2_uid; + ip->i_gid = ip->i_ffs2_gid; + } +} + +#endif /* _KERNEL */ + +/* + * Update the frsum fields to reflect addition or deletion + * of some frags. + */ +void +ffs_fragacct(fs, fragmap, fraglist, cnt, needswap) + struct fs *fs; + int fragmap; + int32_t fraglist[]; + int cnt; + int needswap; +{ + int inblk; + int field, subfield; + int siz, pos; + + inblk = (int)(fragtbl[fs->fs_frag][fragmap]) << 1; + fragmap <<= 1; + for (siz = 1; siz < fs->fs_frag; siz++) { + if ((inblk & (1 << (siz + (fs->fs_frag & (NBBY - 1))))) == 0) + continue; + field = around[siz]; + subfield = inside[siz]; + for (pos = siz; pos <= fs->fs_frag; pos++) { + if ((fragmap & field) == subfield) { + fraglist[siz] = ufs_rw32( + ufs_rw32(fraglist[siz], needswap) + cnt, + needswap); + pos += siz; + field <<= siz; + subfield <<= siz; + } + field <<= 1; + subfield <<= 1; + } + } +} + +#if defined(_KERNEL) && defined(DIAGNOSTIC) +void +ffs_checkoverlap(bp, ip) + struct buf *bp; + struct inode *ip; +{ +#if 0 + struct buf *ebp, *ep; + daddr_t start, last; + struct vnode *vp; + + ebp = &buf[nbuf]; + start = bp->b_blkno; + last = start + btodb(bp->b_bcount) - 1; + for (ep = buf; ep < ebp; ep++) { + if (ep == bp || (ep->b_flags & B_INVAL) || + ep->b_vp == NULLVP) + continue; + if (VOP_BMAP(ep->b_vp, (daddr_t)0, &vp, (daddr_t)0, NULL)) + continue; + if (vp != ip->i_devvp) + continue; + /* look for overlap */ + if (ep->b_bcount == 0 || ep->b_blkno > last || + ep->b_blkno + btodb(ep->b_bcount) <= start) + continue; + vprint("Disk overlap", vp); + printf("\tstart %" PRId64 ", end %" PRId64 " overlap start " + "%" PRId64 ", end %" PRId64 "\n", + start, last, ep->b_blkno, + ep->b_blkno + btodb(ep->b_bcount) - 1); + panic("Disk buffer overlap"); + } +#else + printf("ffs_checkoverlap disabled due to buffer cache implementation changes\n"); +#endif +} +#endif /* _KERNEL && DIAGNOSTIC */ + +/* + * block operations + * + * check if a block is available + * returns true if all the correponding bits in the free map are 1 + * returns false if any corresponding bit in the free map is 0 + */ +int +ffs_isblock(fs, cp, h) + struct fs *fs; + u_char *cp; + int32_t h; +{ + u_char mask; + + switch ((int)fs->fs_fragshift) { + case 3: + return (cp[h] == 0xff); + case 2: + mask = 0x0f << ((h & 0x1) << 2); + return ((cp[h >> 1] & mask) == mask); + case 1: + mask = 0x03 << ((h & 0x3) << 1); + return ((cp[h >> 2] & mask) == mask); + case 0: + mask = 0x01 << (h & 0x7); + return ((cp[h >> 3] & mask) == mask); + default: + panic("ffs_isblock: unknown fs_fragshift %d", + (int)fs->fs_fragshift); + } +} + +/* + * check if a block is completely allocated + * returns true if all the corresponding bits in the free map are 0 + * returns false if any corresponding bit in the free map is 1 + */ +int +ffs_isfreeblock(fs, cp, h) + struct fs *fs; + u_char *cp; + int32_t h; +{ + + switch ((int)fs->fs_fragshift) { + case 3: + return (cp[h] == 0); + case 2: + return ((cp[h >> 1] & (0x0f << ((h & 0x1) << 2))) == 0); + case 1: + return ((cp[h >> 2] & (0x03 << ((h & 0x3) << 1))) == 0); + case 0: + return ((cp[h >> 3] & (0x01 << (h & 0x7))) == 0); + default: + panic("ffs_isfreeblock: unknown fs_fragshift %d", + (int)fs->fs_fragshift); + } +} + +/* + * take a block out of the map + */ +void +ffs_clrblock(fs, cp, h) + struct fs *fs; + u_char *cp; + int32_t h; +{ + + switch ((int)fs->fs_fragshift) { + case 3: + cp[h] = 0; + return; + case 2: + cp[h >> 1] &= ~(0x0f << ((h & 0x1) << 2)); + return; + case 1: + cp[h >> 2] &= ~(0x03 << ((h & 0x3) << 1)); + return; + case 0: + cp[h >> 3] &= ~(0x01 << (h & 0x7)); + return; + default: + panic("ffs_clrblock: unknown fs_fragshift %d", + (int)fs->fs_fragshift); + } +} + +/* + * put a block into the map + */ +void +ffs_setblock(fs, cp, h) + struct fs *fs; + u_char *cp; + int32_t h; +{ + + switch ((int)fs->fs_fragshift) { + case 3: + cp[h] = 0xff; + return; + case 2: + cp[h >> 1] |= (0x0f << ((h & 0x1) << 2)); + return; + case 1: + cp[h >> 2] |= (0x03 << ((h & 0x3) << 1)); + return; + case 0: + cp[h >> 3] |= (0x01 << (h & 0x7)); + return; + default: + panic("ffs_setblock: unknown fs_fragshift %d", + (int)fs->fs_fragshift); + } +} diff --git a/sys/ufs/ffs/ffs_tables.c b/sys/ufs/ffs/ffs_tables.c new file mode 100644 index 00000000000..a7c222e899c --- /dev/null +++ b/sys/ufs/ffs/ffs_tables.c @@ -0,0 +1,141 @@ +/* $NetBSD: ffs_tables.c,v 1.7 2003/10/27 00:12:42 lukem Exp $ */ + +/* + * Copyright (c) 1982, 1986, 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. + * 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. + * + * @(#)ffs_tables.c 8.1 (Berkeley) 6/11/93 + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include +__KERNEL_RCSID(0, "$NetBSD: ffs_tables.c,v 1.7 2003/10/27 00:12:42 lukem Exp $"); + +#include + +/* + * Bit patterns for identifying fragments in the block map + * used as ((map & around) == inside) + */ +const int around[9] = { + 0x3, 0x7, 0xf, 0x1f, 0x3f, 0x7f, 0xff, 0x1ff, 0x3ff +}; +const int inside[9] = { + 0x0, 0x2, 0x6, 0xe, 0x1e, 0x3e, 0x7e, 0xfe, 0x1fe +}; + +/* + * Given a block map bit pattern, the frag tables tell whether a + * particular size fragment is available. + * + * used as: + * if ((1 << (size - 1)) & fragtbl[fs->fs_frag][map] { + * at least one fragment of the indicated size is available + * } + * + * These tables are used by the scanc instruction on the VAX to + * quickly find an appropriate fragment. + */ +const u_char fragtbl124[256] = { + 0x00, 0x16, 0x16, 0x2a, 0x16, 0x16, 0x26, 0x4e, + 0x16, 0x16, 0x16, 0x3e, 0x2a, 0x3e, 0x4e, 0x8a, + 0x16, 0x16, 0x16, 0x3e, 0x16, 0x16, 0x36, 0x5e, + 0x16, 0x16, 0x16, 0x3e, 0x3e, 0x3e, 0x5e, 0x9e, + 0x16, 0x16, 0x16, 0x3e, 0x16, 0x16, 0x36, 0x5e, + 0x16, 0x16, 0x16, 0x3e, 0x3e, 0x3e, 0x5e, 0x9e, + 0x2a, 0x3e, 0x3e, 0x2a, 0x3e, 0x3e, 0x2e, 0x6e, + 0x3e, 0x3e, 0x3e, 0x3e, 0x2a, 0x3e, 0x6e, 0xaa, + 0x16, 0x16, 0x16, 0x3e, 0x16, 0x16, 0x36, 0x5e, + 0x16, 0x16, 0x16, 0x3e, 0x3e, 0x3e, 0x5e, 0x9e, + 0x16, 0x16, 0x16, 0x3e, 0x16, 0x16, 0x36, 0x5e, + 0x16, 0x16, 0x16, 0x3e, 0x3e, 0x3e, 0x5e, 0x9e, + 0x26, 0x36, 0x36, 0x2e, 0x36, 0x36, 0x26, 0x6e, + 0x36, 0x36, 0x36, 0x3e, 0x2e, 0x3e, 0x6e, 0xae, + 0x4e, 0x5e, 0x5e, 0x6e, 0x5e, 0x5e, 0x6e, 0x4e, + 0x5e, 0x5e, 0x5e, 0x7e, 0x6e, 0x7e, 0x4e, 0xce, + 0x16, 0x16, 0x16, 0x3e, 0x16, 0x16, 0x36, 0x5e, + 0x16, 0x16, 0x16, 0x3e, 0x3e, 0x3e, 0x5e, 0x9e, + 0x16, 0x16, 0x16, 0x3e, 0x16, 0x16, 0x36, 0x5e, + 0x16, 0x16, 0x16, 0x3e, 0x3e, 0x3e, 0x5e, 0x9e, + 0x16, 0x16, 0x16, 0x3e, 0x16, 0x16, 0x36, 0x5e, + 0x16, 0x16, 0x16, 0x3e, 0x3e, 0x3e, 0x5e, 0x9e, + 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x7e, + 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x7e, 0xbe, + 0x2a, 0x3e, 0x3e, 0x2a, 0x3e, 0x3e, 0x2e, 0x6e, + 0x3e, 0x3e, 0x3e, 0x3e, 0x2a, 0x3e, 0x6e, 0xaa, + 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x7e, + 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x7e, 0xbe, + 0x4e, 0x5e, 0x5e, 0x6e, 0x5e, 0x5e, 0x6e, 0x4e, + 0x5e, 0x5e, 0x5e, 0x7e, 0x6e, 0x7e, 0x4e, 0xce, + 0x8a, 0x9e, 0x9e, 0xaa, 0x9e, 0x9e, 0xae, 0xce, + 0x9e, 0x9e, 0x9e, 0xbe, 0xaa, 0xbe, 0xce, 0x8a, +}; + +const u_char fragtbl8[256] = { + 0x00, 0x01, 0x01, 0x02, 0x01, 0x01, 0x02, 0x04, + 0x01, 0x01, 0x01, 0x03, 0x02, 0x03, 0x04, 0x08, + 0x01, 0x01, 0x01, 0x03, 0x01, 0x01, 0x03, 0x05, + 0x02, 0x03, 0x03, 0x02, 0x04, 0x05, 0x08, 0x10, + 0x01, 0x01, 0x01, 0x03, 0x01, 0x01, 0x03, 0x05, + 0x01, 0x01, 0x01, 0x03, 0x03, 0x03, 0x05, 0x09, + 0x02, 0x03, 0x03, 0x02, 0x03, 0x03, 0x02, 0x06, + 0x04, 0x05, 0x05, 0x06, 0x08, 0x09, 0x10, 0x20, + 0x01, 0x01, 0x01, 0x03, 0x01, 0x01, 0x03, 0x05, + 0x01, 0x01, 0x01, 0x03, 0x03, 0x03, 0x05, 0x09, + 0x01, 0x01, 0x01, 0x03, 0x01, 0x01, 0x03, 0x05, + 0x03, 0x03, 0x03, 0x03, 0x05, 0x05, 0x09, 0x11, + 0x02, 0x03, 0x03, 0x02, 0x03, 0x03, 0x02, 0x06, + 0x03, 0x03, 0x03, 0x03, 0x02, 0x03, 0x06, 0x0a, + 0x04, 0x05, 0x05, 0x06, 0x05, 0x05, 0x06, 0x04, + 0x08, 0x09, 0x09, 0x0a, 0x10, 0x11, 0x20, 0x40, + 0x01, 0x01, 0x01, 0x03, 0x01, 0x01, 0x03, 0x05, + 0x01, 0x01, 0x01, 0x03, 0x03, 0x03, 0x05, 0x09, + 0x01, 0x01, 0x01, 0x03, 0x01, 0x01, 0x03, 0x05, + 0x03, 0x03, 0x03, 0x03, 0x05, 0x05, 0x09, 0x11, + 0x01, 0x01, 0x01, 0x03, 0x01, 0x01, 0x03, 0x05, + 0x01, 0x01, 0x01, 0x03, 0x03, 0x03, 0x05, 0x09, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x07, + 0x05, 0x05, 0x05, 0x07, 0x09, 0x09, 0x11, 0x21, + 0x02, 0x03, 0x03, 0x02, 0x03, 0x03, 0x02, 0x06, + 0x03, 0x03, 0x03, 0x03, 0x02, 0x03, 0x06, 0x0a, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x07, + 0x02, 0x03, 0x03, 0x02, 0x06, 0x07, 0x0a, 0x12, + 0x04, 0x05, 0x05, 0x06, 0x05, 0x05, 0x06, 0x04, + 0x05, 0x05, 0x05, 0x07, 0x06, 0x07, 0x04, 0x0c, + 0x08, 0x09, 0x09, 0x0a, 0x09, 0x09, 0x0a, 0x0c, + 0x10, 0x11, 0x11, 0x12, 0x20, 0x21, 0x40, 0x80, +}; + +/* + * The actual fragtbl array. + */ +const u_char * const fragtbl[MAXFRAG + 1] = { + 0, fragtbl124, fragtbl124, 0, fragtbl124, 0, 0, 0, fragtbl8, +}; diff --git a/sys/ufs/ffs/fs.h b/sys/ufs/ffs/fs.h new file mode 100644 index 00000000000..45aa7ae7b56 --- /dev/null +++ b/sys/ufs/ffs/fs.h @@ -0,0 +1,728 @@ +/* $NetBSD: fs.h,v 1.44 2004/05/25 14:54:59 hannken Exp $ */ + +/* + * Copyright (c) 1982, 1986, 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. + * 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. + * + * @(#)fs.h 8.13 (Berkeley) 3/21/95 + */ + +#ifndef _UFS_FFS_FS_H_ +#define _UFS_FFS_FS_H_ + +/* + * Each disk drive contains some number of file systems. + * A file system consists of a number of cylinder groups. + * Each cylinder group has inodes and data. + * + * A file system is described by its super-block, which in turn + * describes the cylinder groups. The super-block is critical + * data and is replicated in each cylinder group to protect against + * catastrophic loss. This is done at `newfs' time and the critical + * super-block data does not change, so the copies need not be + * referenced further unless disaster strikes. + * + * For file system fs, the offsets of the various blocks of interest + * are given in the super block as: + * [fs->fs_sblkno] Super-block + * [fs->fs_cblkno] Cylinder group block + * [fs->fs_iblkno] Inode blocks + * [fs->fs_dblkno] Data blocks + * The beginning of cylinder group cg in fs, is given by + * the ``cgbase(fs, cg)'' macro. + * + * Depending on the architecture and the media, the superblock may + * reside in any one of four places. For tiny media where every block + * counts, it is placed at the very front of the partition. Historically, + * UFS1 placed it 8K from the front to leave room for the disk label and + * a small bootstrap. For UFS2 it got moved to 64K from the front to leave + * room for the disk label and a bigger bootstrap, and for really piggy + * systems we check at 256K from the front if the first three fail. In + * all cases the size of the superblock will be SBLOCKSIZE. All values are + * given in byte-offset form, so they do not imply a sector size. The + * SBLOCKSEARCH specifies the order in which the locations should be searched. + * + * Unfortunately the UFS2/FFSv2 change was done without adequate consideration + * of backward compatibility. In particular 'newfs' for a FFSv2 partition + * must overwrite any old FFSv1 superblock at 8k, and preferrably as many + * of the alternates as it can find - otherwise attempting to mount on a + * system that only supports FFSv1 is likely to succeed!. + * For a small FFSv1 filesystem, an old FFSv2 superblock can be left on + * the disk, and a system that tries to find an FFSv2 filesystem in preference + * to and FFSv1 one (as NetBSD does) can mount the old FFSv2 filesystem. + * As a added bonus, the 'first alternate' superblock of a FFSv1 filesystem + * with 64k blocks is at 64k - just where the code looks first when playing + * 'hunt the superblock'. + * + * The ffsv2 superblock layout (which might contain an ffsv1 filesystem) + * can be detected by checking for sb->fs_old_flags & FS_FLAGS_UPDATED. + * This is the default suberblock type for NetBSD since ffsv2 support was added. + */ +#define BBSIZE 8192 +#define BBOFF ((off_t)(0)) +#define BBLOCK ((daddr_t)(0)) + +#define SBLOCK_FLOPPY 0 +#define SBLOCK_UFS1 8192 +#define SBLOCK_UFS2 65536 +#define SBLOCK_PIGGY 262144 +#define SBLOCKSIZE 8192 +/* + * NB: Do not, under any circumstances, look for an ffsv1 filesystem at + * SBLOCK_UFS2. Doing so will find the wrong superblock for filesystems + * with a 64k block size. + */ +#define SBLOCKSEARCH \ + { SBLOCK_UFS2, SBLOCK_UFS1, SBLOCK_FLOPPY, SBLOCK_PIGGY, -1 } + +/* + * Max number of fragments per block. This value is NOT tweakable. + */ +#define MAXFRAG 8 + + + +/* + * Addresses stored in inodes are capable of addressing fragments + * of `blocks'. File system blocks of at most size MAXBSIZE can + * be optionally broken into 2, 4, or 8 pieces, each of which is + * addressable; these pieces may be DEV_BSIZE, or some multiple of + * a DEV_BSIZE unit. + * + * Large files consist of exclusively large data blocks. To avoid + * undue wasted disk space, the last data block of a small file may be + * allocated as only as many fragments of a large block as are + * necessary. The file system format retains only a single pointer + * to such a fragment, which is a piece of a single large block that + * has been divided. The size of such a fragment is determinable from + * information in the inode, using the ``blksize(fs, ip, lbn)'' macro. + * + * The file system records space availability at the fragment level; + * to determine block availability, aligned fragments are examined. + */ + +/* + * MINBSIZE is the smallest allowable block size. + * In order to insure that it is possible to create files of size + * 2^32 with only two levels of indirection, MINBSIZE is set to 4096. + * MINBSIZE must be big enough to hold a cylinder group block, + * thus changes to (struct cg) must keep its size within MINBSIZE. + * Note that super blocks are always of size SBSIZE, + * and that both SBSIZE and MAXBSIZE must be >= MINBSIZE. + */ +#define MINBSIZE 4096 + +/* + * The path name on which the file system is mounted is maintained + * in fs_fsmnt. MAXMNTLEN defines the amount of space allocated in + * the super block for this name. + */ +#define MAXMNTLEN 468 + +/* + * The volume name for this filesystem is maintained in fs_volname. + * MAXVOLLEN defines the length of the buffer allocated. + * This space used to be part of of fs_fsmnt. + */ +#define MAXVOLLEN 32 + +/* + * There is a 128-byte region in the superblock reserved for in-core + * pointers to summary information. Originally this included an array + * of pointers to blocks of struct csum; now there are just four + * pointers and the remaining space is padded with fs_ocsp[]. + * NOCSPTRS determines the size of this padding. One pointer (fs_csp) + * is taken away to point to a contiguous array of struct csum for + * all cylinder groups; a second (fs_maxcluster) points to an array + * of cluster sizes that is computed as cylinder groups are inspected; + * the third (fs_contigdirs) points to an array that tracks the + * creation of new directories; and the fourth (fs_active) is used + * by snapshots. + */ +#define NOCSPTRS ((128 / sizeof(void *)) - 4) + +/* + * A summary of contiguous blocks of various sizes is maintained + * in each cylinder group. Normally this is set by the initial + * value of fs_maxcontig. To conserve space, a maximum summary size + * is set by FS_MAXCONTIG. + */ +#define FS_MAXCONTIG 16 + +/* + * The maximum number of snapshot nodes that can be associated + * with each filesystem. This limit affects only the number of + * snapshot files that can be recorded within the superblock so + * that they can be found when the filesystem is mounted. However, + * maintaining too many will slow the filesystem performance, so + * having this limit is a good idea. + */ +#define FSMAXSNAP 20 + +/* + * Used to identify special blocks in snapshots: + * + * BLK_NOCOPY - A block that was unallocated at the time the snapshot + * was taken, hence does not need to be copied when written. + * BLK_SNAP - A block held by another snapshot that is not needed by this + * snapshot. When the other snapshot is freed, the BLK_SNAP entries + * are converted to BLK_NOCOPY. These are needed to allow fsck to + * identify blocks that are in use by other snapshots (which are + * expunged from this snapshot). + */ +#define BLK_NOCOPY ((daddr_t)(1)) +#define BLK_SNAP ((daddr_t)(2)) + +/* + * MINFREE gives the minimum acceptable percentage of file system + * blocks which may be free. If the freelist drops below this level + * only the superuser may continue to allocate blocks. This may + * be set to 0 if no reserve of free blocks is deemed necessary, + * however throughput drops by fifty percent if the file system + * is run at between 95% and 100% full; thus the minimum default + * value of fs_minfree is 5%. However, to get good clustering + * performance, 10% is a better choice. hence we use 10% as our + * default value. With 10% free space, fragmentation is not a + * problem, so we choose to optimize for time. + */ +#define MINFREE 5 +#define DEFAULTOPT FS_OPTTIME + +/* + * Grigoriy Orlov has done some extensive work to fine + * tune the layout preferences for directories within a filesystem. + * His algorithm can be tuned by adjusting the following parameters + * which tell the system the average file size and the average number + * of files per directory. These defaults are well selected for typical + * filesystems, but may need to be tuned for odd cases like filesystems + * being used for squid caches or news spools. + */ +#define AVFILESIZ 16384 /* expected average file size */ +#define AFPDIR 64 /* expected number of files per directory */ + +/* + * Per cylinder group information; summarized in blocks allocated + * from first cylinder group data blocks. These blocks have to be + * read in from fs_csaddr (size fs_cssize) in addition to the + * super block. + */ +struct csum { + int32_t cs_ndir; /* number of directories */ + int32_t cs_nbfree; /* number of free blocks */ + int32_t cs_nifree; /* number of free inodes */ + int32_t cs_nffree; /* number of free frags */ +}; + +struct csum_total { + int64_t cs_ndir; /* number of directories */ + int64_t cs_nbfree; /* number of free blocks */ + int64_t cs_nifree; /* number of free inodes */ + int64_t cs_nffree; /* number of free frags */ + int64_t cs_spare[4]; /* future expansion */ +}; + + +/* + * Super block for an FFS file system in memory. + */ +struct fs { + int32_t fs_firstfield; /* historic file system linked list, */ + int32_t fs_unused_1; /* used for incore super blocks */ + int32_t fs_sblkno; /* addr of super-block in filesys */ + int32_t fs_cblkno; /* offset of cyl-block in filesys */ + int32_t fs_iblkno; /* offset of inode-blocks in filesys */ + int32_t fs_dblkno; /* offset of first data after cg */ + int32_t fs_old_cgoffset; /* cylinder group offset in cylinder */ + int32_t fs_old_cgmask; /* used to calc mod fs_ntrak */ + int32_t fs_old_time; /* last time written */ + int32_t fs_old_size; /* number of blocks in fs */ + int32_t fs_old_dsize; /* number of data blocks in fs */ + int32_t fs_ncg; /* number of cylinder groups */ + int32_t fs_bsize; /* size of basic blocks in fs */ + int32_t fs_fsize; /* size of frag blocks in fs */ + int32_t fs_frag; /* number of frags in a block in fs */ +/* these are configuration parameters */ + int32_t fs_minfree; /* minimum percentage of free blocks */ + int32_t fs_old_rotdelay; /* num of ms for optimal next block */ + int32_t fs_old_rps; /* disk revolutions per second */ +/* these fields can be computed from the others */ + int32_t fs_bmask; /* ``blkoff'' calc of blk offsets */ + int32_t fs_fmask; /* ``fragoff'' calc of frag offsets */ + int32_t fs_bshift; /* ``lblkno'' calc of logical blkno */ + int32_t fs_fshift; /* ``numfrags'' calc number of frags */ +/* these are configuration parameters */ + int32_t fs_maxcontig; /* max number of contiguous blks */ + int32_t fs_maxbpg; /* max number of blks per cyl group */ +/* these fields can be computed from the others */ + int32_t fs_fragshift; /* block to frag shift */ + int32_t fs_fsbtodb; /* fsbtodb and dbtofsb shift constant */ + int32_t fs_sbsize; /* actual size of super block */ + int32_t fs_spare1[2]; /* old fs_csmask */ + /* old fs_csshift */ + int32_t fs_nindir; /* value of NINDIR */ + int32_t fs_inopb; /* value of INOPB */ + int32_t fs_old_nspf; /* value of NSPF */ +/* yet another configuration parameter */ + int32_t fs_optim; /* optimization preference, see below */ +/* these fields are derived from the hardware */ + int32_t fs_old_npsect; /* # sectors/track including spares */ + int32_t fs_old_interleave; /* hardware sector interleave */ + int32_t fs_old_trackskew; /* sector 0 skew, per track */ +/* fs_id takes the space of the unused fs_headswitch and fs_trkseek fields */ + int32_t fs_id[2]; /* unique file system id */ +/* sizes determined by number of cylinder groups and their sizes */ + int32_t fs_old_csaddr; /* blk addr of cyl grp summary area */ + int32_t fs_cssize; /* size of cyl grp summary area */ + int32_t fs_cgsize; /* cylinder group size */ +/* these fields are derived from the hardware */ + int32_t fs_spare2; /* old fs_ntrak */ + int32_t fs_old_nsect; /* sectors per track */ + int32_t fs_old_spc; /* sectors per cylinder */ + int32_t fs_old_ncyl; /* cylinders in file system */ + int32_t fs_old_cpg; /* cylinders per group */ + int32_t fs_ipg; /* inodes per group */ + int32_t fs_fpg; /* blocks per group * fs_frag */ +/* this data must be re-computed after crashes */ + struct csum fs_old_cstotal; /* cylinder summary information */ +/* these fields are cleared at mount time */ + int8_t fs_fmod; /* super block modified flag */ + int8_t fs_clean; /* file system is clean flag */ + int8_t fs_ronly; /* mounted read-only flag */ + uint8_t fs_old_flags; /* see FS_ flags below */ + u_char fs_fsmnt[MAXMNTLEN]; /* name mounted on */ + u_char fs_volname[MAXVOLLEN]; /* volume name */ + uint64_t fs_swuid; /* system-wide uid */ + int32_t fs_pad; +/* these fields retain the current block allocation info */ + int32_t fs_cgrotor; /* last cg searched (UNUSED) */ + void *fs_ocsp[NOCSPTRS]; /* padding; was list of fs_cs buffers */ + u_int8_t *fs_contigdirs; /* # of contiguously allocated dirs */ + struct csum *fs_csp; /* cg summary info buffer for fs_cs */ + int32_t *fs_maxcluster; /* max cluster in each cyl group */ + u_char *fs_active; /* used by snapshots to track fs */ + int32_t fs_old_cpc; /* cyl per cycle in postbl */ +/* this area is otherwise allocated unless fs_old_flags & FS_FLAGS_UPDATED */ + int32_t fs_maxbsize; /* maximum blocking factor permitted */ + int64_t fs_sparecon64[17]; /* old rotation block list head */ + int64_t fs_sblockloc; /* byte offset of standard superblock */ + struct csum_total fs_cstotal; /* cylinder summary information */ + int64_t fs_time; /* last time written */ + int64_t fs_size; /* number of blocks in fs */ + int64_t fs_dsize; /* number of data blocks in fs */ + int64_t fs_csaddr; /* blk addr of cyl grp summary area */ + int64_t fs_pendingblocks; /* blocks in process of being freed */ + int32_t fs_pendinginodes; /* inodes in process of being freed */ + int32_t fs_snapinum[FSMAXSNAP];/* list of snapshot inode numbers */ +/* back to stuff that has been around a while */ + int32_t fs_avgfilesize; /* expected average file size */ + int32_t fs_avgfpdir; /* expected # of files per directory */ + int32_t fs_save_cgsize; /* save real cg size to use fs_bsize */ + int32_t fs_sparecon32[26]; /* reserved for future constants */ + uint32_t fs_flags; /* see FS_ flags below */ +/* back to stuff that has been around a while (again) */ + int32_t fs_contigsumsize; /* size of cluster summary array */ + int32_t fs_maxsymlinklen; /* max length of an internal symlink */ + int32_t fs_old_inodefmt; /* format of on-disk inodes */ + u_int64_t fs_maxfilesize; /* maximum representable file size */ + int64_t fs_qbmask; /* ~fs_bmask for use with 64-bit size */ + int64_t fs_qfmask; /* ~fs_fmask for use with 64-bit size */ + int32_t fs_state; /* validate fs_clean field (UNUSED) */ + int32_t fs_old_postblformat; /* format of positional layout tables */ + int32_t fs_old_nrpos; /* number of rotational positions */ + int32_t fs_spare5[2]; /* old fs_postbloff */ + /* old fs_rotbloff */ + int32_t fs_magic; /* magic number */ +}; + +#define fs_old_postbloff fs_spare5[0] +#define fs_old_rotbloff fs_spare5[1] +#define fs_old_postbl_start fs_maxbsize +#define fs_old_headswitch fs_id[0] +#define fs_old_trkseek fs_id[1] +#define fs_old_csmask fs_spare1[0] +#define fs_old_csshift fs_spare1[1] + +#define FS_42POSTBLFMT -1 /* 4.2BSD rotational table format */ +#define FS_DYNAMICPOSTBLFMT 1 /* dynamic rotational table format */ + +#define old_fs_postbl(fs_, cylno, opostblsave) \ + ((((fs_)->fs_old_postblformat == FS_42POSTBLFMT) || \ + ((fs_)->fs_old_postbloff == offsetof(struct fs, fs_old_postbl_start))) \ + ? ((int16_t *)(opostblsave) + (cylno) * (fs_)->fs_old_nrpos) \ + : ((int16_t *)((uint8_t *)(fs_) + \ + (fs_)->fs_old_postbloff) + (cylno) * (fs_)->fs_old_nrpos)) +#define old_fs_rotbl(fs) \ + (((fs)->fs_old_postblformat == FS_42POSTBLFMT) \ + ? ((uint8_t *)(&(fs)->fs_magic+1)) \ + : ((uint8_t *)((uint8_t *)(fs) + (fs)->fs_old_rotbloff))) + +/* + * File system identification + */ +#define FS_UFS1_MAGIC 0x011954 /* UFS1 fast file system magic number */ +#define FS_UFS2_MAGIC 0x19540119 /* UFS2 fast file system magic number */ +#define FS_UFS1_MAGIC_SWAPPED 0x54190100 +#define FS_UFS2_MAGIC_SWAPPED 0x19015419 +#define FS_OKAY 0x7c269d38 /* superblock checksum */ +#define FS_42INODEFMT -1 /* 4.2BSD inode format */ +#define FS_44INODEFMT 2 /* 4.4BSD inode format */ + +/* + * File system clean flags + */ +#define FS_ISCLEAN 0x01 +#define FS_WASCLEAN 0x02 + +/* + * Preference for optimization. + */ +#define FS_OPTTIME 0 /* minimize allocation time */ +#define FS_OPTSPACE 1 /* minimize disk fragmentation */ + +/* + * File system flags + */ +#define FS_UNCLEAN 0x01 /* file system not clean at mount (unused) */ +#define FS_DOSOFTDEP 0x02 /* file system using soft dependencies */ +#define FS_NEEDSFSCK 0x04 /* needs sync fsck (FreeBSD compat, unused) */ +#define FS_INDEXDIRS 0x08 /* kernel supports indexed directories */ +#define FS_ACLS 0x10 /* file system has ACLs enabled */ +#define FS_MULTILABEL 0x20 /* file system is MAC multi-label */ +#define FS_FLAGS_UPDATED 0x80 /* flags have been moved to new location */ + +/* + * File system internal flags, also in fs_flags. + * (Pick highest number to avoid conflicts with others) + */ +#define FS_SWAPPED 0x80000000 /* file system is endian swapped */ +#define FS_INTERNAL 0x80000000 /* mask for internal flags */ + +/* + * Macros to access bits in the fs_active array. + */ +#define ACTIVECG_SET(fs, cg) \ + do { \ + if ((fs)->fs_active != NULL) \ + setbit((fs)->fs_active, (cg)); \ + } while (/*CONSTCOND*/ 0) +#define ACTIVECG_CLR(fs, cg) \ + do { \ + if ((fs)->fs_active != NULL) \ + clrbit((fs)->fs_active, (cg)); \ + } while (/*CONSTCOND*/ 0) +#define ACTIVECG_ISSET(fs, cg) \ + ((fs)->fs_active != NULL && isset((fs)->fs_active, (cg))) + +/* + * The size of a cylinder group is calculated by CGSIZE. The maximum size + * is limited by the fact that cylinder groups are at most one block. + * Its size is derived from the size of the maps maintained in the + * cylinder group and the (struct cg) size. + */ +#define CGSIZE_IF(fs, ipg, fpg) \ + /* base cg */ (sizeof(struct cg) + sizeof(int32_t) + \ + /* old btotoff */ (fs)->fs_old_cpg * sizeof(int32_t) + \ + /* old boff */ (fs)->fs_old_cpg * sizeof(u_int16_t) + \ + /* inode map */ howmany((ipg), NBBY) + \ + /* block map */ howmany((fpg), NBBY) +\ + /* if present */ ((fs)->fs_contigsumsize <= 0 ? 0 : \ + /* cluster sum */ (fs)->fs_contigsumsize * sizeof(int32_t) + \ + /* cluster map */ howmany(fragstoblks(fs, (fpg)), NBBY))) + +#define CGSIZE(fs) CGSIZE_IF((fs), (fs)->fs_ipg, (fs)->fs_fpg) + +/* + * The minimal number of cylinder groups that should be created. + */ +#define MINCYLGRPS 4 + + +/* + * Convert cylinder group to base address of its global summary info. + */ +#define fs_cs(fs, indx) fs_csp[indx] + +/* + * Cylinder group block for a file system. + */ +#define CG_MAGIC 0x090255 +struct cg { + int32_t cg_firstfield; /* historic cyl groups linked list */ + int32_t cg_magic; /* magic number */ + int32_t cg_old_time; /* time last written */ + int32_t cg_cgx; /* we are the cgx'th cylinder group */ + int16_t cg_old_ncyl; /* number of cyl's this cg */ + int16_t cg_old_niblk; /* number of inode blocks this cg */ + int32_t cg_ndblk; /* number of data blocks this cg */ + struct csum cg_cs; /* cylinder summary information */ + int32_t cg_rotor; /* position of last used block */ + int32_t cg_frotor; /* position of last used frag */ + int32_t cg_irotor; /* position of last used inode */ + int32_t cg_frsum[MAXFRAG]; /* counts of available frags */ + int32_t cg_old_btotoff; /* (int32) block totals per cylinder */ + int32_t cg_old_boff; /* (u_int16) free block positions */ + int32_t cg_iusedoff; /* (u_int8) used inode map */ + int32_t cg_freeoff; /* (u_int8) free block map */ + int32_t cg_nextfreeoff; /* (u_int8) next available space */ + int32_t cg_clustersumoff; /* (u_int32) counts of avail clusters */ + int32_t cg_clusteroff; /* (u_int8) free cluster map */ + int32_t cg_nclusterblks; /* number of clusters this cg */ + int32_t cg_niblk; /* number of inode blocks this cg */ + int32_t cg_initediblk; /* last initialized inode */ + int32_t cg_sparecon32[3]; /* reserved for future use */ + int64_t cg_time; /* time last written */ + int64_t cg_sparecon64[3]; /* reserved for future use */ + u_int8_t cg_space[1]; /* space for cylinder group maps */ +/* actually longer */ +}; + +/* + * The following structure is defined + * for compatibility with old file systems. + */ +struct ocg { + int32_t cg_firstfield; /* historic linked list of cyl groups */ + int32_t cg_unused_1; /* used for incore cyl groups */ + int32_t cg_time; /* time last written */ + int32_t cg_cgx; /* we are the cgx'th cylinder group */ + int16_t cg_ncyl; /* number of cyl's this cg */ + int16_t cg_niblk; /* number of inode blocks this cg */ + int32_t cg_ndblk; /* number of data blocks this cg */ + struct csum cg_cs; /* cylinder summary information */ + int32_t cg_rotor; /* position of last used block */ + int32_t cg_frotor; /* position of last used frag */ + int32_t cg_irotor; /* position of last used inode */ + int32_t cg_frsum[8]; /* counts of available frags */ + int32_t cg_btot[32]; /* block totals per cylinder */ + int16_t cg_b[32][8]; /* positions of free blocks */ + u_int8_t cg_iused[256]; /* used inode map */ + int32_t cg_magic; /* magic number */ + u_int8_t cg_free[1]; /* free block map */ +/* actually longer */ +}; + + +/* + * Macros for access to cylinder group array structures. + */ +#define old_cg_blktot_old(cgp, ns) \ + (((struct ocg *)(cgp))->cg_btot) +#define old_cg_blks_old(fs, cgp, cylno, ns) \ + (((struct ocg *)(cgp))->cg_b[cylno]) + +#define old_cg_blktot_new(cgp, ns) \ + ((int32_t *)((u_int8_t *)(cgp) + \ + ufs_rw32((cgp)->cg_old_btotoff, (ns)))) +#define old_cg_blks_new(fs, cgp, cylno, ns) \ + ((int16_t *)((u_int8_t *)(cgp) + \ + ufs_rw32((cgp)->cg_old_boff, (ns))) + (cylno) * (fs)->fs_old_nrpos) + +#define old_cg_blktot(cgp, ns) \ + ((ufs_rw32((cgp)->cg_magic, (ns)) != CG_MAGIC) ? \ + old_cg_blktot_old(cgp, ns) : old_cg_blktot_new(cgp, ns)) +#define old_cg_blks(fs, cgp, cylno, ns) \ + ((ufs_rw32((cgp)->cg_magic, (ns)) != CG_MAGIC) ? \ + old_cg_blks_old(fs, cgp, cylno, ns) : old_cg_blks_new(fs, cgp, cylno, ns)) + +#define cg_inosused_new(cgp, ns) \ + ((u_int8_t *)((u_int8_t *)(cgp) + \ + ufs_rw32((cgp)->cg_iusedoff, (ns)))) +#define cg_blksfree_new(cgp, ns) \ + ((u_int8_t *)((u_int8_t *)(cgp) + \ + ufs_rw32((cgp)->cg_freeoff, (ns)))) +#define cg_chkmagic_new(cgp, ns) \ + (ufs_rw32((cgp)->cg_magic, (ns)) == CG_MAGIC) + +#define cg_inosused_old(cgp, ns) \ + (((struct ocg *)(cgp))->cg_iused) +#define cg_blksfree_old(cgp, ns) \ + (((struct ocg *)(cgp))->cg_free) +#define cg_chkmagic_old(cgp, ns) \ + (ufs_rw32(((struct ocg *)(cgp))->cg_magic, (ns)) == CG_MAGIC) + +#define cg_inosused(cgp, ns) \ + ((ufs_rw32((cgp)->cg_magic, (ns)) != CG_MAGIC) ? \ + cg_inosused_old(cgp, ns) : cg_inosused_new(cgp, ns)) +#define cg_blksfree(cgp, ns) \ + ((ufs_rw32((cgp)->cg_magic, (ns)) != CG_MAGIC) ? \ + cg_blksfree_old(cgp, ns) : cg_blksfree_new(cgp, ns)) +#define cg_chkmagic(cgp, ns) \ + (cg_chkmagic_new(cgp, ns) || cg_chkmagic_old(cgp, ns)) + +#define cg_clustersfree(cgp, ns) \ + ((u_int8_t *)((u_int8_t *)(cgp) + \ + ufs_rw32((cgp)->cg_clusteroff, (ns)))) +#define cg_clustersum(cgp, ns) \ + ((int32_t *)((u_int8_t *)(cgp) + \ + ufs_rw32((cgp)->cg_clustersumoff, (ns)))) + + +/* + * Turn file system block numbers into disk block addresses. + * This maps file system blocks to device size blocks. + */ +#define fsbtodb(fs, b) ((b) << (fs)->fs_fsbtodb) +#define dbtofsb(fs, b) ((b) >> (fs)->fs_fsbtodb) + +/* + * Cylinder group macros to locate things in cylinder groups. + * They calc file system addresses of cylinder group data structures. + */ +#define cgbase(fs, c) (((daddr_t)(fs)->fs_fpg) * (c)) +#define cgstart_ufs1(fs, c) \ + (cgbase(fs, c) + (fs)->fs_old_cgoffset * ((c) & ~((fs)->fs_old_cgmask))) +#define cgstart_ufs2(fs, c) cgbase((fs), (c)) +#define cgstart(fs, c) ((fs)->fs_magic == FS_UFS2_MAGIC \ + ? cgstart_ufs2((fs), (c)) : cgstart_ufs1((fs), (c))) +#define cgdmin(fs, c) (cgstart(fs, c) + (fs)->fs_dblkno) /* 1st data */ +#define cgimin(fs, c) (cgstart(fs, c) + (fs)->fs_iblkno) /* inode blk */ +#define cgsblock(fs, c) (cgstart(fs, c) + (fs)->fs_sblkno) /* super blk */ +#define cgtod(fs, c) (cgstart(fs, c) + (fs)->fs_cblkno) /* cg block */ + +/* + * Macros for handling inode numbers: + * inode number to file system block offset. + * inode number to cylinder group number. + * inode number to file system block address. + */ +#define ino_to_cg(fs, x) ((x) / (fs)->fs_ipg) +#define ino_to_fsba(fs, x) \ + ((daddr_t)(cgimin(fs, ino_to_cg(fs, x)) + \ + (blkstofrags((fs), (((x) % (fs)->fs_ipg) / INOPB(fs)))))) +#define ino_to_fsbo(fs, x) ((x) % INOPB(fs)) + +/* + * Give cylinder group number for a file system block. + * Give cylinder group block number for a file system block. + */ +#define dtog(fs, d) ((d) / (fs)->fs_fpg) +#define dtogd(fs, d) ((d) % (fs)->fs_fpg) + +/* + * Extract the bits for a block from a map. + * Compute the cylinder and rotational position of a cyl block addr. + */ +#define blkmap(fs, map, loc) \ + (((map)[(loc) / NBBY] >> ((loc) % NBBY)) & (0xff >> (NBBY - (fs)->fs_frag))) +#define old_cbtocylno(fs, bno) \ + (fsbtodb(fs, bno) / (fs)->fs_old_spc) +#define old_cbtorpos(fs, bno) \ + ((fs)->fs_old_nrpos <= 1 ? 0 : \ + (fsbtodb(fs, bno) % (fs)->fs_old_spc / (fs)->fs_old_nsect * (fs)->fs_old_trackskew + \ + fsbtodb(fs, bno) % (fs)->fs_old_spc % (fs)->fs_old_nsect * (fs)->fs_old_interleave) % \ + (fs)->fs_old_nsect * (fs)->fs_old_nrpos / (fs)->fs_old_npsect) + +/* + * The following macros optimize certain frequently calculated + * quantities by using shifts and masks in place of divisions + * modulos and multiplications. + */ +#define blkoff(fs, loc) /* calculates (loc % fs->fs_bsize) */ \ + ((loc) & (fs)->fs_qbmask) +#define fragoff(fs, loc) /* calculates (loc % fs->fs_fsize) */ \ + ((loc) & (fs)->fs_qfmask) +#define lfragtosize(fs, frag) /* calculates ((off_t)frag * fs->fs_fsize) */ \ + (((off_t)(frag)) << (fs)->fs_fshift) +#define lblktosize(fs, blk) /* calculates ((off_t)blk * fs->fs_bsize) */ \ + (((off_t)(blk)) << (fs)->fs_bshift) +#define lblkno(fs, loc) /* calculates (loc / fs->fs_bsize) */ \ + ((loc) >> (fs)->fs_bshift) +#define numfrags(fs, loc) /* calculates (loc / fs->fs_fsize) */ \ + ((loc) >> (fs)->fs_fshift) +#define blkroundup(fs, size) /* calculates roundup(size, fs->fs_bsize) */ \ + (((size) + (fs)->fs_qbmask) & (fs)->fs_bmask) +#define fragroundup(fs, size) /* calculates roundup(size, fs->fs_fsize) */ \ + (((size) + (fs)->fs_qfmask) & (fs)->fs_fmask) +#define fragstoblks(fs, frags) /* calculates (frags / fs->fs_frag) */ \ + ((frags) >> (fs)->fs_fragshift) +#define blkstofrags(fs, blks) /* calculates (blks * fs->fs_frag) */ \ + ((blks) << (fs)->fs_fragshift) +#define fragnum(fs, fsb) /* calculates (fsb % fs->fs_frag) */ \ + ((fsb) & ((fs)->fs_frag - 1)) +#define blknum(fs, fsb) /* calculates rounddown(fsb, fs->fs_frag) */ \ + ((fsb) &~ ((fs)->fs_frag - 1)) + +/* + * Determine the number of available frags given a + * percentage to hold in reserve. + */ +#define freespace(fs, percentreserved) \ + (blkstofrags((fs), (fs)->fs_cstotal.cs_nbfree) + \ + (fs)->fs_cstotal.cs_nffree - \ + (((off_t)((fs)->fs_dsize)) * (percentreserved) / 100)) + +/* + * Determining the size of a file block in the file system. + */ +#define blksize(fs, ip, lbn) \ + (((lbn) >= NDADDR || (ip)->i_size >= lblktosize(fs, (lbn) + 1)) \ + ? (fs)->fs_bsize \ + : (fragroundup(fs, blkoff(fs, (ip)->i_size)))) + +#define sblksize(fs, size, lbn) \ + (((lbn) >= NDADDR || (size) >= ((lbn) + 1) << (fs)->fs_bshift) \ + ? (fs)->fs_bsize \ + : (fragroundup(fs, blkoff(fs, (size))))) + + +/* + * Number of inodes in a secondary storage block/fragment. + */ +#define INOPB(fs) ((fs)->fs_inopb) +#define INOPF(fs) ((fs)->fs_inopb >> (fs)->fs_fragshift) + +/* + * Number of indirects in a file system block. + */ +#define NINDIR(fs) ((fs)->fs_nindir) + +/* + * Apple UFS Label: + * We check for this to decide to use APPLEUFS_DIRBLKSIZ + */ +#define APPLEUFS_LABEL_MAGIC 0x4c41424c /* LABL */ +#define APPLEUFS_LABEL_SIZE 1024 +#define APPLEUFS_LABEL_OFFSET (BBSIZE - APPLEUFS_LABEL_SIZE) /* located at 7k */ +#define APPLEUFS_LABEL_VERSION 1 +#define APPLEUFS_MAX_LABEL_NAME 512 + +struct appleufslabel { + u_int32_t ul_magic; + u_int16_t ul_checksum; + u_int16_t ul_unused0; + u_int32_t ul_version; + u_int32_t ul_time; + u_int16_t ul_namelen; + u_char ul_name[APPLEUFS_MAX_LABEL_NAME]; /* Warning: may not be null terminated */ + u_int16_t ul_unused1; + u_int64_t ul_uuid; /* Note this is only 4 byte aligned */ + u_char ul_reserved[24]; + u_char ul_unused[460]; +} __attribute__((__packed__)); + + +#endif /* !_UFS_FFS_FS_H_ */ diff --git a/sys/ufs/ufs/dinode.h b/sys/ufs/ufs/dinode.h new file mode 100644 index 00000000000..10e62f125dc --- /dev/null +++ b/sys/ufs/ufs/dinode.h @@ -0,0 +1,175 @@ +/* $NetBSD: dinode.h,v 1.18 2003/08/07 16:34:42 agc 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. + * (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. + * 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. + * + * @(#)dinode.h 8.9 (Berkeley) 3/29/95 + */ + +#ifndef _UFS_UFS_DINODE_H_ +#define _UFS_UFS_DINODE_H_ + +/* + * The root inode is the root of the file system. Inode 0 can't be used for + * normal purposes and historically bad blocks were linked to inode 1, thus + * the root inode is 2. (Inode 1 is no longer used for this purpose, however + * numerous dump tapes make this assumption, so we are stuck with it). + */ +#define ROOTINO ((ino_t)2) + +/* + * The Whiteout inode# is a dummy non-zero inode number which will + * never be allocated to a real file. It is used as a place holder + * in the directory entry which has been tagged as a DT_W entry. + * See the comments about ROOTINO above. + */ +#define WINO ((ino_t)1) + +/* + * A dinode contains all the meta-data associated with a UFS file. + * This structure defines the on-disk format of a dinode. Since + * this structure describes an on-disk structure, all its fields + * are defined by types with precise widths. + */ + +#define NXADDR 2 +#define NDADDR 12 /* Direct addresses in inode. */ +#define NIADDR 3 /* Indirect addresses in inode. */ + +struct ufs1_dinode { + u_int16_t di_mode; /* 0: IFMT, permissions; see below. */ + int16_t di_nlink; /* 2: File link count. */ + union { + u_int16_t oldids[2]; /* 4: Ffs: old user and group ids. */ + u_int32_t inumber; /* 4: Lfs: inode number. */ + } di_u; + u_int64_t di_size; /* 8: File byte count. */ + int32_t di_atime; /* 16: Last access time. */ + int32_t di_atimensec; /* 20: Last access time. */ + int32_t di_mtime; /* 24: Last modified time. */ + int32_t di_mtimensec; /* 28: Last modified time. */ + int32_t di_ctime; /* 32: Last inode change time. */ + int32_t di_ctimensec; /* 36: Last inode change time. */ + int32_t di_db[NDADDR]; /* 40: Direct disk blocks. */ + int32_t di_ib[NIADDR]; /* 88: Indirect disk blocks. */ + u_int32_t di_flags; /* 100: Status flags (chflags). */ + u_int32_t di_blocks; /* 104: Blocks actually held. */ + int32_t di_gen; /* 108: Generation number. */ + u_int32_t di_uid; /* 112: File owner. */ + u_int32_t di_gid; /* 116: File group. */ + int32_t di_spare[2]; /* 120: Reserved; currently unused */ +}; + +struct ufs2_dinode { + u_int16_t di_mode; /* 0: IFMT, permissions; see below. */ + int16_t di_nlink; /* 2: File link count. */ + u_int32_t di_uid; /* 4: File owner. */ + u_int32_t di_gid; /* 8: File group. */ + u_int32_t di_blksize; /* 12: Inode blocksize. */ + u_int64_t di_size; /* 16: File byte count. */ + u_int64_t di_blocks; /* 24: Bytes actually held. */ + int64_t di_atime; /* 32: Last access time. */ + int64_t di_mtime; /* 40: Last modified time. */ + int64_t di_ctime; /* 48: Last inode change time. */ + int64_t di_birthtime; /* 56: Inode creation time. */ + int32_t di_mtimensec; /* 64: Last modified time. */ + int32_t di_atimensec; /* 68: Last access time. */ + int32_t di_ctimensec; /* 72: Last inode change time. */ + int32_t di_birthnsec; /* 76: Inode creation time. */ + int32_t di_gen; /* 80: Generation number. */ + u_int32_t di_kernflags; /* 84: Kernel flags. */ + u_int32_t di_flags; /* 88: Status flags (chflags). */ + int32_t di_extsize; /* 92: External attributes block. */ + int64_t di_extb[NXADDR];/* 96: External attributes block. */ + int64_t di_db[NDADDR]; /* 112: Direct disk blocks. */ + int64_t di_ib[NIADDR]; /* 208: Indirect disk blocks. */ + int64_t di_spare[3]; /* 232: Reserved; currently unused */ +}; + +/* + * The di_db fields may be overlaid with other information for + * file types that do not have associated disk storage. Block + * and character devices overlay the first data block with their + * dev_t value. Short symbolic links place their path in the + * di_db area. + */ +#define di_inumber di_u.inumber +#define di_ogid di_u.oldids[1] +#define di_ouid di_u.oldids[0] +#define di_rdev di_db[0] +#define MAXSYMLINKLEN_UFS1 ((NDADDR + NIADDR) * sizeof(int32_t)) +#define MAXSYMLINKLEN_UFS2 ((NDADDR + NIADDR) * sizeof(int64_t)) + +#define MAXSYMLINKLEN(ip) \ + ((ip)->i_ump->um_fstype == UFS1) ? \ + MAXSYMLINKLEN_UFS1 : MAXSYMLINKLEN_UFS2 + +/* NeXT used to keep short symlinks in the inode even when using + * FS_42INODEFMT. In that case fs->fs_maxsymlinklen is probably -1, + * but short symlinks were stored in inodes shorter than this: + */ +#define APPLEUFS_MAXSYMLINKLEN 60 + +/* File permissions. */ +#define IEXEC 0000100 /* Executable. */ +#define IWRITE 0000200 /* Writable. */ +#define IREAD 0000400 /* Readable. */ +#define ISVTX 0001000 /* Sticky bit. */ +#define ISGID 0002000 /* Set-gid. */ +#define ISUID 0004000 /* Set-uid. */ + +/* File types. */ +#define IFMT 0170000 /* Mask of file type. */ +#define IFIFO 0010000 /* Named pipe (fifo). */ +#define IFCHR 0020000 /* Character device. */ +#define IFDIR 0040000 /* Directory file. */ +#define IFBLK 0060000 /* Block device. */ +#define IFREG 0100000 /* Regular file. */ +#define IFLNK 0120000 /* Symbolic link. */ +#define IFSOCK 0140000 /* UNIX domain socket. */ +#define IFWHT 0160000 /* Whiteout. */ + +/* Size of the on-disk inode. */ +#define DINODE1_SIZE (sizeof(struct ufs1_dinode)) /* 128 */ +#define DINODE2_SIZE (sizeof(struct ufs2_dinode)) + +#endif /* !_UFS_UFS_DINODE_H_ */ diff --git a/sys/ufs/ufs/dir.h b/sys/ufs/ufs/dir.h new file mode 100644 index 00000000000..1aed962a1c5 --- /dev/null +++ b/sys/ufs/ufs/dir.h @@ -0,0 +1,160 @@ +/* $NetBSD: dir.h,v 1.17 2003/08/07 16:34:42 agc Exp $ */ + +/* + * Copyright (c) 1982, 1986, 1989, 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. + * 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. + * + * @(#)dir.h 8.5 (Berkeley) 4/27/95 + */ + +#ifndef _UFS_UFS_DIR_H_ +#define _UFS_UFS_DIR_H_ + +/* + * Theoretically, directories can be more than 2Gb in length, however, in + * practice this seems unlikely. So, we define the type doff_t as a 32-bit + * quantity to keep down the cost of doing lookup on a 32-bit machine. + */ +#define doff_t int32_t +#define MAXDIRSIZE (0x7fffffff) + +/* + * A directory consists of some number of blocks of DIRBLKSIZ + * bytes, where DIRBLKSIZ is chosen such that it can be transferred + * to disk in a single atomic operation (e.g. 512 bytes on most machines). + * + * Each DIRBLKSIZ byte block contains some number of directory entry + * structures, which are of variable length. Each directory entry has + * a struct direct at the front of it, containing its inode number, + * the length of the entry, and the length of the name contained in + * the entry. These are followed by the name padded to a 4 byte boundary. + * All names are guaranteed null terminated. + * The maximum length of a name in a directory is MAXNAMLEN. + * + * The macro DIRSIZ(fmt, dp) gives the amount of space required to represent + * a directory entry. Free space in a directory is represented by + * entries which have dp->d_reclen > DIRSIZ(fmt, dp). All DIRBLKSIZ bytes + * in a directory block are claimed by the directory entries. This + * usually results in the last entry in a directory having a large + * dp->d_reclen. When entries are deleted from a directory, the + * space is returned to the previous entry in the same directory + * block by increasing its dp->d_reclen. If the first entry of + * a directory block is free, then its dp->d_ino is set to 0. + * Entries other than the first in a directory do not normally have + * dp->d_ino set to 0. + */ +#undef DIRBLKSIZ +#define DIRBLKSIZ DEV_BSIZE +#undef MAXNAMLEN +#define MAXNAMLEN 255 +#define APPLEUFS_DIRBLKSIZ 1024 + +struct direct { + u_int32_t d_ino; /* inode number of entry */ + u_int16_t d_reclen; /* length of this record */ + u_int8_t d_type; /* file type, see below */ + u_int8_t d_namlen; /* length of string in d_name */ + char d_name[MAXNAMLEN + 1];/* name with length <= MAXNAMLEN */ +}; + +/* + * File types + */ +#define DT_UNKNOWN 0 +#define DT_FIFO 1 +#define DT_CHR 2 +#define DT_DIR 4 +#define DT_BLK 6 +#define DT_REG 8 +#define DT_LNK 10 +#define DT_SOCK 12 +#define DT_WHT 14 + +/* + * Convert between stat structure types and directory types. + */ +#define IFTODT(mode) (((mode) & 0170000) >> 12) +#define DTTOIF(dirtype) ((dirtype) << 12) + +/* + * The DIRSIZ macro gives the minimum record length which will hold + * the directory entry. This requires the amount of space in struct direct + * without the d_name field, plus enough space for the name with a terminating + * null byte (dp->d_namlen+1), rounded up to a 4 byte boundary. + */ +#define DIRECTSIZ(namlen) \ + ((sizeof(struct direct) - (MAXNAMLEN+1)) + (((namlen)+1 + 3) &~ 3)) + +#if (BYTE_ORDER == LITTLE_ENDIAN) +#define DIRSIZ(oldfmt, dp, needswap) \ + (((oldfmt) && !(needswap)) ? \ + DIRECTSIZ((dp)->d_type) : DIRECTSIZ((dp)->d_namlen)) +#else +#define DIRSIZ(oldfmt, dp, needswap) \ + (((oldfmt) && (needswap)) ? \ + DIRECTSIZ((dp)->d_type) : DIRECTSIZ((dp)->d_namlen)) +#endif + +#define OLDDIRFMT 1 +#define NEWDIRFMT 0 + +/* + * Template for manipulating directories. Should use struct direct's, + * but the name field is MAXNAMLEN - 1, and this just won't do. + */ +struct dirtemplate { + u_int32_t dot_ino; + int16_t dot_reclen; + u_int8_t dot_type; + u_int8_t dot_namlen; + char dot_name[4]; /* must be multiple of 4 */ + u_int32_t dotdot_ino; + int16_t dotdot_reclen; + u_int8_t dotdot_type; + u_int8_t dotdot_namlen; + char dotdot_name[4]; /* ditto */ +}; + +/* + * This is the old format of directories, sanz type element. + */ +struct odirtemplate { + u_int32_t dot_ino; + int16_t dot_reclen; + u_int16_t dot_namlen; + char dot_name[4]; /* must be multiple of 4 */ + u_int32_t dotdot_ino; + int16_t dotdot_reclen; + u_int16_t dotdot_namlen; + char dotdot_name[4]; /* ditto */ +}; +#endif /* !_UFS_UFS_DIR_H_ */ diff --git a/sys/ufs/ufs/ufs_bswap.h b/sys/ufs/ufs/ufs_bswap.h new file mode 100644 index 00000000000..faddc7b37de --- /dev/null +++ b/sys/ufs/ufs/ufs_bswap.h @@ -0,0 +1,93 @@ +/* $NetBSD: ufs_bswap.h,v 1.13 2003/10/05 17:48:50 bouyer Exp $ */ + +/* + * Copyright (c) 1998 Manuel Bouyer. + * + * 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 Manuel Bouyer. + * 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. + * + */ + +#ifndef _UFS_UFS_BSWAP_H_ +#define _UFS_UFS_BSWAP_H_ + +#if defined(_KERNEL_OPT) +#include "opt_ffs.h" +#endif + +#include + +/* Macros to access UFS flags */ +#ifdef FFS_EI +#define UFS_MPNEEDSWAP(mp) (VFSTOUFS(mp)->um_flags & UFS_NEEDSWAP) +#define UFS_FSNEEDSWAP(fs) ((fs)->fs_flags & FS_SWAPPED) +#define UFS_IPNEEDSWAP(ip) UFS_MPNEEDSWAP(ITOV(ip)->v_mount) +#else +#define UFS_MPNEEDSWAP(mp) (0) +#define UFS_FSNEEDSWAP(fs) (0) +#define UFS_IPNEEDSWAP(ip) (0) +#endif + +#if !defined(_KERNEL) || defined(FFS_EI) +/* inlines for access to swapped data */ +static __inline u_int16_t ufs_rw16 __P((u_int16_t, int)); +static __inline u_int32_t ufs_rw32 __P((u_int32_t, int)); +static __inline u_int64_t ufs_rw64 __P((u_int64_t, int)); + +static __inline u_int16_t +ufs_rw16(a, ns) + u_int16_t a; + int ns; +{ + return ((ns) ? bswap16(a) : (a)); +} +static __inline u_int32_t +ufs_rw32(a, ns) + u_int32_t a; + int ns; +{ + return ((ns) ? bswap32(a) : (a)); +} +static __inline u_int64_t +ufs_rw64(a, ns) + u_int64_t a; + int ns; +{ + return ((ns) ? bswap64(a) : (a)); +} +#else +#define ufs_rw16(a, ns) ((uint16_t)(a)) +#define ufs_rw32(a, ns) ((uint32_t)(a)) +#define ufs_rw64(a, ns) ((uint64_t)(a)) +#endif + +#define ufs_add16(a, b, ns) \ + (a) = ufs_rw16(ufs_rw16((a), (ns)) + (b), (ns)) +#define ufs_add32(a, b, ns) \ + (a) = ufs_rw32(ufs_rw32((a), (ns)) + (b), (ns)) +#define ufs_add64(a, b, ns) \ + (a) = ufs_rw64(ufs_rw64((a), (ns)) + (b), (ns)) + +#endif /* !_UFS_UFS_BSWAP_H_ */ diff --git a/walk.c b/walk.c new file mode 100644 index 00000000000..da7a402a2e3 --- /dev/null +++ b/walk.c @@ -0,0 +1,540 @@ +/* $NetBSD: walk.c,v 1.17 2004/06/20 22:20:18 jmc Exp $ */ + +/* + * Copyright (c) 2001 Wasabi Systems, Inc. + * All rights reserved. + * + * Written by Luke Mewburn for Wasabi Systems, 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. + * 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 + * Wasabi Systems, Inc. + * 4. The name of Wasabi Systems, Inc. may not be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``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 WASABI SYSTEMS, INC + * 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 function link_check() was inspired from NetBSD's usr.bin/du/du.c, + * which has the following copyright notice: + * + * + * Copyright (c) 1989, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Newcomb. + * + * 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. + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include +#if defined(__RCSID) && !defined(__lint) +__RCSID("$NetBSD: walk.c,v 1.17 2004/06/20 22:20:18 jmc Exp $"); +#endif /* !__lint */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "makefs.h" +#include "mtree.h" + +static void apply_specdir(const char *, NODE *, fsnode *); +static void apply_specentry(const char *, NODE *, fsnode *); +static fsnode *create_fsnode(const char *, struct stat *); +static fsinode *link_check(fsinode *); + + +/* + * walk_dir -- + * build a tree of fsnodes from `dir', with a parent fsnode of `parent' + * (which may be NULL for the root of the tree). + * each "level" is a directory, with the "." entry guaranteed to be + * at the start of the list, and without ".." entries. + */ +fsnode * +walk_dir(const char *dir, fsnode *parent) +{ + fsnode *first, *cur, *prev; + DIR *dirp; + struct dirent *dent; + char path[MAXPATHLEN + 1]; + struct stat stbuf; + + assert(dir != NULL); + + if (debug & DEBUG_WALK_DIR) + printf("walk_dir: %s %p\n", dir, parent); + if ((dirp = opendir(dir)) == NULL) + err(1, "Can't opendir `%s'", dir); + first = prev = NULL; + while ((dent = readdir(dirp)) != NULL) { + if (strcmp(dent->d_name, "..") == 0) + continue; + if (debug & DEBUG_WALK_DIR_NODE) + printf("scanning %s/%s\n", dir, dent->d_name); + if (snprintf(path, sizeof(path), "%s/%s", dir, dent->d_name) + >= sizeof(path)) + errx(1, "Pathname too long."); + if (lstat(path, &stbuf) == -1) + err(1, "Can't lstat `%s'", path); +#ifdef S_ISSOCK + if (S_ISSOCK(stbuf.st_mode & S_IFMT)) { + if (debug & DEBUG_WALK_DIR_NODE) + printf(" skipping socket %s\n", path); + continue; + } +#endif + + cur = create_fsnode(dent->d_name, &stbuf); + cur->parent = parent; + if (strcmp(dent->d_name, ".") == 0) { + /* ensure "." is at the start of the list */ + cur->next = first; + first = cur; + if (! prev) + prev = cur; + } else { /* not "." */ + if (prev) + prev->next = cur; + prev = cur; + if (!first) + first = cur; + if (S_ISDIR(cur->type)) { + cur->child = walk_dir(path, cur); + continue; + } + } + if (stbuf.st_nlink > 1) { + fsinode *curino; + + curino = link_check(cur->inode); + if (curino != NULL) { + free(cur->inode); + cur->inode = curino; + cur->inode->nlink++; + } + } + if (S_ISLNK(cur->type)) { + char slink[PATH_MAX+1]; + int llen; + + llen = readlink(path, slink, sizeof(slink) - 1); + if (llen == -1) + err(1, "Readlink `%s'", path); + slink[llen] = '\0'; + if ((cur->symlink = strdup(slink)) == NULL) + err(1, "Memory allocation error"); + } + } + for (cur = first; cur != NULL; cur = cur->next) + cur->first = first; + if (closedir(dirp) == -1) + err(1, "Can't closedir `%s'", dir); + return (first); +} + +static fsnode * +create_fsnode(const char *name, struct stat *stbuf) +{ + fsnode *cur; + + if ((cur = calloc(1, sizeof(fsnode))) == NULL || + (cur->name = strdup(name)) == NULL || + (cur->inode = calloc(1, sizeof(fsinode))) == NULL) + err(1, "Memory allocation error"); + cur->type = stbuf->st_mode & S_IFMT; + cur->inode->nlink = 1; + cur->inode->st = *stbuf; + return (cur); +} + +/* + * apply_specfile -- + * read in the mtree(8) specfile, and apply it to the tree + * at dir,parent. parameters in parent on equivalent types + * will be changed to those found in specfile, and missing + * entries will be added. + */ +void +apply_specfile(const char *specfile, const char *dir, fsnode *parent) +{ + struct timeval start; + FILE *fp; + NODE *root; + + assert(specfile != NULL); + assert(parent != NULL); + + if (debug & DEBUG_APPLY_SPECFILE) + printf("apply_specfile: %s, %s %p\n", specfile, dir, parent); + + /* read in the specfile */ + if ((fp = fopen(specfile, "r")) == NULL) + err(1, "Can't open `%s'", specfile); + TIMER_START(start); + root = spec(fp); + TIMER_RESULTS(start, "spec"); + if (fclose(fp) == EOF) + err(1, "Can't close `%s'", specfile); + + /* perform some sanity checks */ + if (root == NULL) + errx(1, "Specfile `%s' did not contain a tree", specfile); + assert(strcmp(root->name, ".") == 0); + assert(root->type == F_DIR); + + /* merge in the changes */ + apply_specdir(dir, root, parent); +} + +static void +apply_specdir(const char *dir, NODE *specnode, fsnode *dirnode) +{ + char path[MAXPATHLEN + 1]; + NODE *curnode; + fsnode *curfsnode; + + assert(specnode != NULL); + assert(dirnode != NULL); + + if (debug & DEBUG_APPLY_SPECFILE) + printf("apply_specdir: %s %p %p\n", dir, specnode, dirnode); + + if (specnode->type != F_DIR) + errx(1, "Specfile node `%s/%s' is not a directory", + dir, specnode->name); + if (dirnode->type != S_IFDIR) + errx(1, "Directory node `%s/%s' is not a directory", + dir, dirnode->name); + + apply_specentry(dir, specnode, dirnode); + + /* now walk specnode->child matching up with dirnode */ + for (curnode = specnode->child; curnode != NULL; + curnode = curnode->next) { + if (debug & DEBUG_APPLY_SPECENTRY) + printf("apply_specdir: spec %s\n", + curnode->name); + for (curfsnode = dirnode->next; curfsnode != NULL; + curfsnode = curfsnode->next) { +#if 0 /* too verbose for now */ + if (debug & DEBUG_APPLY_SPECENTRY) + printf("apply_specdir: dirent %s\n", + curfsnode->name); +#endif + if (strcmp(curnode->name, curfsnode->name) == 0) + break; + } + if (snprintf(path, sizeof(path), "%s/%s", + dir, curnode->name) >= sizeof(path)) + errx(1, "Pathname too long."); + if (curfsnode == NULL) { /* need new entry */ + struct stat stbuf; + + /* + * don't add optional spec entries + * that lack an existing fs entry + */ + if ((curnode->flags & F_OPT) && + lstat(path, &stbuf) == -1) + continue; + + /* check that enough info is provided */ +#define NODETEST(t, m) \ + if (!(t)) \ + errx(1, "`%s': %s not provided", path, m) + NODETEST(curnode->flags & F_TYPE, "type"); + NODETEST(curnode->flags & F_MODE, "mode"); + /* XXX: require F_TIME ? */ + NODETEST(curnode->flags & F_GID || + curnode->flags & F_GNAME, "group"); + NODETEST(curnode->flags & F_UID || + curnode->flags & F_UNAME, "user"); + if (curnode->type == F_BLOCK || curnode->type == F_CHAR) + NODETEST(curnode->flags & F_DEV, + "device number"); +#undef NODETEST + + if (debug & DEBUG_APPLY_SPECFILE) + printf("apply_specdir: adding %s\n", + curnode->name); + /* build minimal fsnode */ + memset(&stbuf, 0, sizeof(stbuf)); + stbuf.st_mode = nodetoino(curnode->type); + stbuf.st_nlink = 1; + stbuf.st_mtime = stbuf.st_atime = + stbuf.st_ctime = start_time.tv_sec; +#if HAVE_STRUCT_STAT_ST_MTIMENSEC + stbuf.st_mtimensec = stbuf.st_atimensec = + stbuf.st_ctimensec = start_time.tv_nsec; +#endif + curfsnode = create_fsnode(curnode->name, &stbuf); + curfsnode->parent = dirnode->parent; + curfsnode->first = dirnode; + curfsnode->next = dirnode->next; + dirnode->next = curfsnode; + if (curfsnode->type == S_IFDIR) { + /* for dirs, make "." entry as well */ + curfsnode->child = create_fsnode(".", &stbuf); + curfsnode->child->parent = curfsnode; + curfsnode->child->first = curfsnode->child; + } + if (curfsnode->type == S_IFLNK) { + assert(curnode->slink != NULL); + /* for symlinks, copy the target */ + if ((curfsnode->symlink = + strdup(curnode->slink)) == NULL) + err(1, "Memory allocation error"); + } + } + apply_specentry(dir, curnode, curfsnode); + if (curnode->type == F_DIR) { + if (curfsnode->type != S_IFDIR) + errx(1, "`%s' is not a directory", path); + assert (curfsnode->child != NULL); + apply_specdir(path, curnode, curfsnode->child); + } + } +} + +static void +apply_specentry(const char *dir, NODE *specnode, fsnode *dirnode) +{ + + assert(specnode != NULL); + assert(dirnode != NULL); + + if (nodetoino(specnode->type) != dirnode->type) + errx(1, "`%s/%s' type mismatch: specfile %s, tree %s", + dir, specnode->name, inode_type(nodetoino(specnode->type)), + inode_type(dirnode->type)); + + if (debug & DEBUG_APPLY_SPECENTRY) + printf("apply_specentry: %s/%s\n", dir, dirnode->name); + +#define ASEPRINT(t, b, o, n) \ + if (debug & DEBUG_APPLY_SPECENTRY) \ + printf("\t\t\tchanging %s from " b " to " b "\n", \ + t, o, n) + + if (specnode->flags & (F_GID | F_GNAME)) { + ASEPRINT("gid", "%d", + dirnode->inode->st.st_gid, specnode->st_gid); + dirnode->inode->st.st_gid = specnode->st_gid; + } + if (specnode->flags & F_MODE) { + ASEPRINT("mode", "%#o", + dirnode->inode->st.st_mode & ALLPERMS, specnode->st_mode); + dirnode->inode->st.st_mode &= ~ALLPERMS; + dirnode->inode->st.st_mode |= (specnode->st_mode & ALLPERMS); + } + /* XXX: ignoring F_NLINK for now */ + if (specnode->flags & F_SIZE) { + ASEPRINT("size", "%lld", + (long long)dirnode->inode->st.st_size, + (long long)specnode->st_size); + dirnode->inode->st.st_size = specnode->st_size; + } + if (specnode->flags & F_SLINK) { + assert(dirnode->symlink != NULL); + assert(specnode->slink != NULL); + ASEPRINT("symlink", "%s", dirnode->symlink, specnode->slink); + free(dirnode->symlink); + if ((dirnode->symlink = strdup(specnode->slink)) == NULL) + err(1, "Memory allocation error"); + } + if (specnode->flags & F_TIME) { + ASEPRINT("time", "%ld", + (long)dirnode->inode->st.st_mtime, + (long)specnode->st_mtimespec.tv_sec); + dirnode->inode->st.st_mtime = specnode->st_mtimespec.tv_sec; + dirnode->inode->st.st_atime = specnode->st_mtimespec.tv_sec; + dirnode->inode->st.st_ctime = start_time.tv_sec; +#if HAVE_STRUCT_STAT_ST_MTIMENSEC + dirnode->inode->st.st_mtimensec = specnode->st_mtimespec.tv_nsec; + dirnode->inode->st.st_atimensec = specnode->st_mtimespec.tv_nsec; + dirnode->inode->st.st_ctimensec = start_time.tv_nsec; +#endif + } + if (specnode->flags & (F_UID | F_UNAME)) { + ASEPRINT("uid", "%d", + dirnode->inode->st.st_uid, specnode->st_uid); + dirnode->inode->st.st_uid = specnode->st_uid; + } +#if HAVE_STRUCT_STAT_ST_FLAGS + if (specnode->flags & F_FLAGS) { + ASEPRINT("flags", "%#lX", + (unsigned long)dirnode->inode->st.st_flags, + (unsigned long)specnode->st_flags); + dirnode->inode->st.st_flags = specnode->st_flags; + } +#endif + if (specnode->flags & F_DEV) { + ASEPRINT("rdev", "%#x", + dirnode->inode->st.st_rdev, specnode->st_rdev); + dirnode->inode->st.st_rdev = specnode->st_rdev; + } +#undef ASEPRINT + + dirnode->flags |= FSNODE_F_HASSPEC; +} + + +/* + * dump_fsnodes -- + * dump the fsnodes from `cur', based in the directory `dir' + */ +void +dump_fsnodes(const char *dir, fsnode *root) +{ + fsnode *cur; + char path[MAXPATHLEN + 1]; + + assert (dir != NULL); + printf("dump_fsnodes: %s %p\n", dir, root); + for (cur = root; cur != NULL; cur = cur->next) { + if (snprintf(path, sizeof(path), "%s/%s", dir, cur->name) + >= sizeof(path)) + errx(1, "Pathname too long."); + + if (debug & DEBUG_DUMP_FSNODES_VERBOSE) + printf("cur=%8p parent=%8p first=%8p ", + cur, cur->parent, cur->first); + printf("%7s: %s", inode_type(cur->type), path); + if (S_ISLNK(cur->type)) { + assert(cur->symlink != NULL); + printf(" -> %s", cur->symlink); + } else { + assert (cur->symlink == NULL); + } + if (cur->inode->nlink > 1) + printf(", nlinks=%d", cur->inode->nlink); + putchar('\n'); + + if (cur->child) { + assert (cur->type == S_IFDIR); + dump_fsnodes(path, cur->child); + } + } + printf("dump_fsnodes: finished %s\n", dir); +} + + +/* + * inode_type -- + * for a given inode type `mode', return a descriptive string. + * for most cases, uses inotype() from mtree/misc.c + */ +const char * +inode_type(mode_t mode) +{ + + if (S_ISLNK(mode)) + return ("symlink"); /* inotype() returns "link"... */ + return (inotype(mode)); +} + + +/* + * link_check -- + * return pointer to fsnode matching `entry's st_ino & st_dev if it exists, + * otherwise add `entry' to table and return NULL + */ +static fsinode * +link_check(fsinode *entry) +{ + static struct dupnode { + uint32_t dev; + uint64_t ino; + fsinode *dup; + } *dups, *newdups; + static int ndups, maxdups; + + int i; + + assert (entry != NULL); + + /* XXX; maybe traverse in reverse for speed? */ + for (i = 0; i < ndups; i++) { + if (dups[i].dev == entry->st.st_dev && + dups[i].ino == entry->st.st_ino) { + if (debug & DEBUG_WALK_DIR_LINKCHECK) + printf("link_check: found [%d,%d]\n", + entry->st.st_dev, entry->st.st_ino); + return (dups[i].dup); + } + } + + if (debug & DEBUG_WALK_DIR_LINKCHECK) + printf("link_check: no match for [%d, %d]\n", + entry->st.st_dev, entry->st.st_ino); + if (ndups == maxdups) { + if ((newdups = realloc(dups, sizeof(struct dupnode) * (maxdups + 128))) + == NULL) + err(1, "Memory allocation error"); + dups = newdups; + maxdups += 128; + } + dups[ndups].dev = entry->st.st_dev; + dups[ndups].ino = entry->st.st_ino; + dups[ndups].dup = entry; + ndups++; + + return (NULL); +}