summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.CVS22
-rw-r--r--bsdfsck/Makefile31
-rw-r--r--bsdfsck/dir.c690
-rw-r--r--bsdfsck/fsck.h303
-rw-r--r--bsdfsck/inode.c562
-rw-r--r--bsdfsck/main.c333
-rw-r--r--bsdfsck/pass1.c331
-rw-r--r--bsdfsck/pass1b.c100
-rw-r--r--bsdfsck/pass2.c441
-rw-r--r--bsdfsck/pass3.c72
-rw-r--r--bsdfsck/pass4.c134
-rw-r--r--bsdfsck/pass5.c358
-rw-r--r--bsdfsck/preen.c355
-rw-r--r--bsdfsck/setup.c480
-rw-r--r--bsdfsck/utilities.c567
-rwxr-xr-xconfigure5055
-rw-r--r--configure.in244
-rw-r--r--procfs/ChangeLog177
-rw-r--r--procfs/Makefile30
-rw-r--r--procfs/bootstrap.c95
-rw-r--r--procfs/netfs.c467
-rw-r--r--procfs/node.c195
-rw-r--r--procfs/procfs.c149
-rw-r--r--procfs/procfs.h220
-rw-r--r--procfs/procfs_dir.c664
-rw-r--r--procfs/procfs_nonpid_files.c514
-rw-r--r--procfs/procfs_pid.h88
-rw-r--r--procfs/procfs_pid_files.c576
-rw-r--r--serverboot/Makefile53
-rw-r--r--serverboot/assert.h50
-rw-r--r--serverboot/bootstrap.c438
-rw-r--r--serverboot/bunzip2.c169
-rw-r--r--serverboot/def_pager_setup.c152
-rw-r--r--serverboot/default_pager.c3905
-rw-r--r--serverboot/defs.h95
-rw-r--r--serverboot/dir.h142
-rw-r--r--serverboot/disk_inode.h101
-rw-r--r--serverboot/disk_inode_ffs.h99
-rw-r--r--serverboot/elf-load.c143
-rw-r--r--serverboot/exec.c147
-rw-r--r--serverboot/ext2_file_io.c983
-rw-r--r--serverboot/ffs_compat.c65
-rw-r--r--serverboot/ffs_compat.h54
-rw-r--r--serverboot/ffs_file_io.c969
-rw-r--r--serverboot/file_io.c317
-rw-r--r--serverboot/file_io.h200
-rw-r--r--serverboot/fs.h455
-rw-r--r--serverboot/gets.c90
-rw-r--r--serverboot/gunzip.c188
-rw-r--r--serverboot/kalloc.c291
-rw-r--r--serverboot/load.c555
-rw-r--r--serverboot/mach-exec.h130
-rw-r--r--serverboot/minix_ffs_compat.c62
-rw-r--r--serverboot/minix_ffs_compat.h43
-rw-r--r--serverboot/minix_file_io.c851
-rw-r--r--serverboot/minix_fs.h107
-rw-r--r--serverboot/minix_super.h49
-rw-r--r--serverboot/panic.c58
-rw-r--r--serverboot/queue.h316
-rw-r--r--serverboot/strfcns.c74
-rw-r--r--serverboot/wiring.c175
-rw-r--r--serverboot/wiring.h35
-rw-r--r--ufs-fsck/Makefile35
-rw-r--r--ufs-fsck/dir.c567
-rw-r--r--ufs-fsck/fsck.h183
-rw-r--r--ufs-fsck/inode.c203
-rw-r--r--ufs-fsck/main.c170
-rw-r--r--ufs-fsck/pass1.c437
-rw-r--r--ufs-fsck/pass1b.c90
-rw-r--r--ufs-fsck/pass2.c400
-rw-r--r--ufs-fsck/pass3.c71
-rw-r--r--ufs-fsck/pass4.c94
-rw-r--r--ufs-fsck/pass5.c450
-rw-r--r--ufs-fsck/setup.c191
-rw-r--r--ufs-fsck/utilities.c455
-rw-r--r--ufs-utils/Makefile38
-rw-r--r--ufs-utils/clri.c168
-rw-r--r--ufs-utils/dlabel.c91
-rw-r--r--ufs-utils/mkfs.c1406
-rw-r--r--ufs-utils/newfs.c705
-rw-r--r--ufs-utils/stati.c260
-rw-r--r--ufs/Makefile32
-rw-r--r--ufs/alloc.c1703
-rw-r--r--ufs/bmap.c120
-rw-r--r--ufs/consts.c33
-rw-r--r--ufs/dinode.h137
-rw-r--r--ufs/dir.c988
-rw-r--r--ufs/dir.h163
-rw-r--r--ufs/fs.h509
-rw-r--r--ufs/hyper.c414
-rw-r--r--ufs/inode.c703
-rw-r--r--ufs/main.c210
-rw-r--r--ufs/pager.c806
-rw-r--r--ufs/pokeloc.c85
-rw-r--r--ufs/sizes.c719
-rw-r--r--ufs/subr.c264
-rw-r--r--ufs/tables.c138
-rw-r--r--ufs/ufs.h289
98 files changed, 38141 insertions, 0 deletions
diff --git a/README.CVS b/README.CVS
new file mode 100644
index 00000000..781acdeb
--- /dev/null
+++ b/README.CVS
@@ -0,0 +1,22 @@
+-*- Text -*-
+GNU Hurd CVS version.
+
+
+This is the Hurd. Welcome.
+
+For installation instructions, you might try your luck with the files
+README, INSTALL, and INSTALL-cross. However, they have not been
+updated for a long time.
+
+For now, this file documents the version requirements for the CVS
+version of the Hurd. Other combinations might work, but the stated
+minimum requirements are best tested by the developers.
+
+GNU MiG at least 1.3
+GNU Mach at least 1.3
+GNU C library CVS from 2004-03-09 or later
+GNU C compiler at least 3.3.2
+
+Obviously, you also need somewhat recent versions of binutils, make,
+bash and some other tools. No hard requirements are currently known
+for these, though.
diff --git a/bsdfsck/Makefile b/bsdfsck/Makefile
new file mode 100644
index 00000000..b5dcfbf2
--- /dev/null
+++ b/bsdfsck/Makefile
@@ -0,0 +1,31 @@
+#
+# Copyright (C) 1994, 1995 Free Software Foundation
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2, or (at
+# your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+dir := bsdfsck
+makemode := utility
+
+SRCS = dir.c inode.c main.c pass1.c pass1b.c pass2.c pass3.c pass4.c \
+ pass5.c setup.c utilities.c # preen.c
+OBJS = dir.o inode.o main.o pass1.o pass1b.o pass2.o pass3.o pass4.o \
+ pass5.o setup.o utilities.o tables.o # preen.o
+LCLHDRS = fsck.h
+target = bsdfsck
+
+vpath tables.c ../ufs
+
+include ../Makeconf
+
diff --git a/bsdfsck/dir.c b/bsdfsck/dir.c
new file mode 100644
index 00000000..e63473ae
--- /dev/null
+++ b/bsdfsck/dir.c
@@ -0,0 +1,690 @@
+/*
+ * Copyright (c) 1980, 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. 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.
+ */
+
+#ifndef lint
+/*static char sccsid[] = "from: @(#)dir.c 8.1 (Berkeley) 6/5/93";*/
+static char *rcsid = "$Id: dir.c,v 1.5 1994/11/04 21:54:56 mib Exp $";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include "../ufs/dinode.h"
+#include "../ufs/dir.h"
+#include "../ufs/fs.h"
+#include <stdlib.h>
+#include <string.h>
+#include "fsck.h"
+
+char *lfname = "lost+found";
+int lfmode = 01777;
+struct dirtemplate emptydir = { 0, DIRBLKSIZ };
+struct dirtemplate dirhead = {
+ 0, 12, DT_DIR, 1, ".",
+ 0, DIRBLKSIZ - 12, DT_DIR, 2, ".."
+};
+struct odirtemplate odirhead = {
+ 0, 12, 1, ".",
+ 0, DIRBLKSIZ - 12, 2, ".."
+};
+
+struct direct *fsck_readdir();
+struct bufarea *getdirblk();
+
+/*
+ * Propagate connected state through the tree.
+ */
+propagate()
+{
+ register struct inoinfo **inpp, *inp;
+ struct inoinfo **inpend;
+ long change;
+
+ inpend = &inpsort[inplast];
+ do {
+ change = 0;
+ for (inpp = inpsort; inpp < inpend; inpp++) {
+ inp = *inpp;
+ if (inp->i_parent == 0)
+ continue;
+ if (statemap[inp->i_parent] == DFOUND &&
+ statemap[inp->i_number] == DSTATE) {
+ statemap[inp->i_number] = DFOUND;
+ change++;
+ }
+ }
+ } while (change > 0);
+}
+
+/*
+ * Scan each entry in a directory block.
+ */
+dirscan(idesc)
+ register struct inodesc *idesc;
+{
+ register struct direct *dp;
+ register struct bufarea *bp;
+ int dsize, n;
+ long blksiz;
+ char dbuf[DIRBLKSIZ];
+
+ if (idesc->id_type != DATA)
+ errexit("wrong type to dirscan %d\n", idesc->id_type);
+ if (idesc->id_entryno == 0 &&
+ (idesc->id_filesize & (DIRBLKSIZ - 1)) != 0)
+ idesc->id_filesize = roundup(idesc->id_filesize, DIRBLKSIZ);
+ blksiz = idesc->id_numfrags * sblock.fs_fsize;
+ if (chkrange(idesc->id_blkno, idesc->id_numfrags)) {
+ idesc->id_filesize -= blksiz;
+ return (SKIP);
+ }
+ idesc->id_loc = 0;
+ for (dp = fsck_readdir(idesc); dp != NULL; dp = fsck_readdir(idesc)) {
+ dsize = dp->d_reclen;
+ bcopy((char *)dp, dbuf, (size_t)dsize);
+# if (BYTE_ORDER == LITTLE_ENDIAN)
+ if (!newinofmt) {
+ struct direct *tdp = (struct direct *)dbuf;
+ u_char tmp;
+
+ tmp = tdp->d_namlen;
+ tdp->d_namlen = tdp->d_type;
+ tdp->d_type = tmp;
+ }
+# endif
+ idesc->id_dirp = (struct direct *)dbuf;
+ if ((n = (*idesc->id_func)(idesc)) & ALTERED) {
+# if (BYTE_ORDER == LITTLE_ENDIAN)
+ if (!newinofmt && !doinglevel2) {
+ struct direct *tdp;
+ u_char tmp;
+
+ tdp = (struct direct *)dbuf;
+ tmp = tdp->d_namlen;
+ tdp->d_namlen = tdp->d_type;
+ tdp->d_type = tmp;
+ }
+# endif
+ bp = getdirblk(idesc->id_blkno, blksiz);
+ bcopy(dbuf, bp->b_un.b_buf + idesc->id_loc - dsize,
+ (size_t)dsize);
+ dirty(bp);
+ sbdirty();
+ }
+ if (n & STOP)
+ return (n);
+ }
+ return (idesc->id_filesize > 0 ? KEEPON : STOP);
+}
+
+/*
+ * get next entry in a directory.
+ */
+struct direct *
+fsck_readdir(idesc)
+ register struct inodesc *idesc;
+{
+ register struct direct *dp, *ndp;
+ register struct bufarea *bp;
+ long size, blksiz, fix, dploc;
+
+ blksiz = idesc->id_numfrags * sblock.fs_fsize;
+ bp = getdirblk(idesc->id_blkno, blksiz);
+ if (idesc->id_loc % DIRBLKSIZ == 0 && idesc->id_filesize > 0 &&
+ idesc->id_loc < blksiz) {
+ dp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc);
+ if (dircheck(idesc, dp))
+ goto dpok;
+ fix = dofix(idesc, "DIRECTORY CORRUPTED");
+ bp = getdirblk(idesc->id_blkno, blksiz);
+ dp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc);
+ dp->d_reclen = DIRBLKSIZ;
+ dp->d_ino = 0;
+ dp->d_type = 0;
+ dp->d_namlen = 0;
+ dp->d_name[0] = '\0';
+ if (fix)
+ dirty(bp);
+ idesc->id_loc += DIRBLKSIZ;
+ idesc->id_filesize -= DIRBLKSIZ;
+ return (dp);
+ }
+dpok:
+ if (idesc->id_filesize <= 0 || idesc->id_loc >= blksiz)
+ return NULL;
+ dploc = idesc->id_loc;
+ dp = (struct direct *)(bp->b_un.b_buf + dploc);
+ idesc->id_loc += dp->d_reclen;
+ idesc->id_filesize -= dp->d_reclen;
+ if ((idesc->id_loc % DIRBLKSIZ) == 0)
+ return (dp);
+ ndp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc);
+ if (idesc->id_loc < blksiz && idesc->id_filesize > 0 &&
+ dircheck(idesc, ndp) == 0) {
+ size = DIRBLKSIZ - (idesc->id_loc % DIRBLKSIZ);
+ idesc->id_loc += size;
+ idesc->id_filesize -= size;
+ fix = dofix(idesc, "DIRECTORY CORRUPTED");
+ bp = getdirblk(idesc->id_blkno, blksiz);
+ dp = (struct direct *)(bp->b_un.b_buf + dploc);
+ dp->d_reclen += size;
+ if (fix)
+ dirty(bp);
+ }
+ return (dp);
+}
+
+/*
+ * Verify that a directory entry is valid.
+ * This is a superset of the checks made in the kernel.
+ */
+dircheck(idesc, dp)
+ struct inodesc *idesc;
+ register struct direct *dp;
+{
+ register int size;
+ register char *cp;
+ u_char namlen, type;
+ int spaceleft;
+ spaceleft = DIRBLKSIZ - (idesc->id_loc % DIRBLKSIZ);
+
+ if (dp->d_ino >= maxino ||
+ dp->d_reclen == 0 ||
+ dp->d_reclen > spaceleft ||
+ (dp->d_reclen & 0x3) != 0)
+ return (0);
+ if (dp->d_ino == 0)
+ return (1);
+ size = DIRSIZ(!newinofmt, dp);
+# if (BYTE_ORDER == LITTLE_ENDIAN)
+ if (!newinofmt) {
+ type = dp->d_namlen;
+ namlen = dp->d_type;
+ } else {
+ namlen = dp->d_namlen;
+ type = dp->d_type;
+ }
+# else
+ namlen = dp->d_namlen;
+ type = dp->d_type;
+# endif
+ if (dp->d_reclen < size ||
+ idesc->id_filesize < size ||
+ namlen > MAXNAMLEN ||
+ type > 15)
+ return (0);
+ for (cp = dp->d_name, size = 0; size < namlen; size++)
+ if (*cp == '\0' || (*cp++ == '/'))
+ return (0);
+ if (*cp != '\0')
+ return (0);
+ return (1);
+}
+
+direrror(ino, errmesg)
+ ino_t ino;
+ char *errmesg;
+{
+
+ fileerror(ino, ino, errmesg);
+}
+
+fileerror(cwd, ino, errmesg)
+ ino_t cwd, ino;
+ char *errmesg;
+{
+ register struct dinode *dp;
+ char pathbuf[MAXPATHLEN + 1];
+
+ pwarn("%s ", errmesg);
+ pinode(ino);
+ printf("\n");
+ getpathname(pathbuf, cwd, ino);
+ if (ino < ROOTINO || ino > maxino) {
+ pfatal("NAME=%s\n", pathbuf);
+ return;
+ }
+ dp = ginode(ino);
+ if (ftypeok(dp))
+ pfatal("%s=%s\n",
+ (DI_MODE(dp) & IFMT) == IFDIR ? "DIR" : "FILE", pathbuf);
+ else
+ pfatal("NAME=%s\n", pathbuf);
+}
+
+adjust(idesc, lcnt)
+ register struct inodesc *idesc;
+ short lcnt;
+{
+ register struct dinode *dp;
+
+ dp = ginode(idesc->id_number);
+ if (dp->di_nlink == lcnt) {
+ if (linkup(idesc->id_number, (ino_t)0) == 0)
+ clri(idesc, "UNREF", 0);
+ } else {
+ pwarn("LINK COUNT %s", (lfdir == idesc->id_number) ? lfname :
+ ((DI_MODE(dp) & IFMT) == IFDIR ? "DIR" : "FILE"));
+ pinode(idesc->id_number);
+ printf(" COUNT %d SHOULD BE %d",
+ dp->di_nlink, dp->di_nlink - lcnt);
+ if (preen) {
+ if (lcnt < 0) {
+ printf("\n");
+ pfatal("LINK COUNT INCREASING");
+ }
+ printf(" (ADJUSTED)\n");
+ }
+ if (preen || reply("ADJUST") == 1) {
+ dp->di_nlink -= lcnt;
+ inodirty();
+ }
+ }
+}
+
+mkentry(idesc)
+ struct inodesc *idesc;
+{
+ register struct direct *dirp = idesc->id_dirp;
+ struct direct newent;
+ int newlen, oldlen;
+
+ newent.d_namlen = strlen(idesc->id_name);
+ newlen = DIRSIZ(0, &newent);
+ if (dirp->d_ino != 0)
+ oldlen = DIRSIZ(0, dirp);
+ else
+ oldlen = 0;
+ if (dirp->d_reclen - oldlen < newlen)
+ return (KEEPON);
+ newent.d_reclen = dirp->d_reclen - oldlen;
+ dirp->d_reclen = oldlen;
+ dirp = (struct direct *)(((char *)dirp) + oldlen);
+ dirp->d_ino = idesc->id_parent; /* ino to be entered is in id_parent */
+ if (newinofmt) {
+ dirp->d_type = typemap[idesc->id_parent];
+ dirp->d_namlen = newent.d_namlen;
+ } else {
+# if (BYTE_ORDER == LITTLE_ENDIAN)
+ dirp->d_type = newent.d_namlen;
+ dirp->d_namlen = 0;
+# else
+ dirp->d_type = 0;
+ dirp->d_namlen = newent.d_namlen;
+# endif
+ }
+ dirp->d_reclen = newent.d_reclen;
+ bcopy(idesc->id_name, dirp->d_name, (size_t)newent.d_namlen + 1);
+ return (ALTERED|STOP);
+}
+
+chgino(idesc)
+ struct inodesc *idesc;
+{
+ register struct direct *dirp = idesc->id_dirp;
+
+ if (bcmp(dirp->d_name, idesc->id_name, (int)dirp->d_namlen + 1))
+ return (KEEPON);
+ dirp->d_ino = idesc->id_parent;
+ if (newinofmt)
+ dirp->d_type = typemap[idesc->id_parent];
+ else
+ dirp->d_type = 0;
+ return (ALTERED|STOP);
+}
+
+linkup(orphan, parentdir)
+ ino_t orphan;
+ ino_t parentdir;
+{
+ register struct dinode *dp;
+ int lostdir;
+ ino_t oldlfdir;
+ struct inodesc idesc;
+ char tempname[BUFSIZ];
+ extern int pass4check();
+
+ bzero((char *)&idesc, sizeof(struct inodesc));
+ dp = ginode(orphan);
+ lostdir = (DI_MODE(dp) & IFMT) == IFDIR;
+ pwarn("UNREF %s ", lostdir ? "DIR" : "FILE");
+ pinode(orphan);
+ if (preen && dp->di_size == 0)
+ return (0);
+ if (preen)
+ printf(" (RECONNECTED)\n");
+ else
+ if (reply("RECONNECT") == 0)
+ return (0);
+ if (lfdir == 0) {
+ dp = ginode(ROOTINO);
+ idesc.id_name = lfname;
+ idesc.id_type = DATA;
+ idesc.id_func = findino;
+ idesc.id_number = ROOTINO;
+ if ((ckinode(dp, &idesc) & FOUND) != 0) {
+ lfdir = idesc.id_parent;
+ } else {
+ pwarn("NO lost+found DIRECTORY");
+ if (preen || reply("CREATE")) {
+ lfdir = allocdir(ROOTINO, (ino_t)0, lfmode);
+ if (lfdir != 0) {
+ if (makeentry(ROOTINO, lfdir, lfname) != 0) {
+ if (preen)
+ printf(" (CREATED)\n");
+ } else {
+ freedir(lfdir, ROOTINO);
+ lfdir = 0;
+ if (preen)
+ printf("\n");
+ }
+ }
+ }
+ }
+ if (lfdir == 0) {
+ pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY");
+ printf("\n\n");
+ return (0);
+ }
+ }
+ dp = ginode(lfdir);
+ if ((DI_MODE(dp) & IFMT) != IFDIR) {
+ pfatal("lost+found IS NOT A DIRECTORY");
+ if (reply("REALLOCATE") == 0)
+ return (0);
+ oldlfdir = lfdir;
+ if ((lfdir = allocdir(ROOTINO, (ino_t)0, lfmode)) == 0) {
+ pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n");
+ return (0);
+ }
+ if ((changeino(ROOTINO, lfname, lfdir) & ALTERED) == 0) {
+ pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n");
+ return (0);
+ }
+ inodirty();
+ idesc.id_type = ADDR;
+ idesc.id_func = pass4check;
+ idesc.id_number = oldlfdir;
+ adjust(&idesc, lncntp[oldlfdir] + 1);
+ lncntp[oldlfdir] = 0;
+ dp = ginode(lfdir);
+ }
+ if (statemap[lfdir] != DFOUND) {
+ pfatal("SORRY. NO lost+found DIRECTORY\n\n");
+ return (0);
+ }
+ (void)lftempname(tempname, orphan);
+ if (makeentry(lfdir, orphan, tempname) == 0) {
+ pfatal("SORRY. NO SPACE IN lost+found DIRECTORY");
+ printf("\n\n");
+ return (0);
+ }
+ lncntp[orphan]--;
+ if (lostdir) {
+ if ((changeino(orphan, "..", lfdir) & ALTERED) == 0 &&
+ parentdir != (ino_t)-1)
+ (void)makeentry(orphan, lfdir, "..");
+ dp = ginode(lfdir);
+ dp->di_nlink++;
+ inodirty();
+ lncntp[lfdir]++;
+ pwarn("DIR I=%lu CONNECTED. ", orphan);
+ if (parentdir != (ino_t)-1)
+ printf("PARENT WAS I=%lu\n", parentdir);
+ if (preen == 0)
+ printf("\n");
+ }
+ return (1);
+}
+
+/*
+ * fix an entry in a directory.
+ */
+changeino(dir, name, newnum)
+ ino_t dir;
+ char *name;
+ ino_t newnum;
+{
+ struct inodesc idesc;
+
+ bzero((char *)&idesc, sizeof(struct inodesc));
+ idesc.id_type = DATA;
+ idesc.id_func = chgino;
+ idesc.id_number = dir;
+ idesc.id_fix = DONTKNOW;
+ idesc.id_name = name;
+ idesc.id_parent = newnum; /* new value for name */
+ return (ckinode(ginode(dir), &idesc));
+}
+
+/*
+ * make an entry in a directory
+ */
+makeentry(parent, ino, name)
+ ino_t parent, ino;
+ char *name;
+{
+ struct dinode *dp;
+ struct inodesc idesc;
+ char pathbuf[MAXPATHLEN + 1];
+
+ if (parent < ROOTINO || parent >= maxino ||
+ ino < ROOTINO || ino >= maxino)
+ return (0);
+ bzero((char *)&idesc, sizeof(struct inodesc));
+ idesc.id_type = DATA;
+ idesc.id_func = mkentry;
+ idesc.id_number = parent;
+ idesc.id_parent = ino; /* this is the inode to enter */
+ idesc.id_fix = DONTKNOW;
+ idesc.id_name = name;
+ dp = ginode(parent);
+ if (dp->di_size % DIRBLKSIZ) {
+ dp->di_size = roundup(dp->di_size, DIRBLKSIZ);
+ inodirty();
+ }
+ if ((ckinode(dp, &idesc) & ALTERED) != 0)
+ return (1);
+ getpathname(pathbuf, parent, parent);
+ dp = ginode(parent);
+ if (expanddir(dp, pathbuf) == 0)
+ return (0);
+ return (ckinode(dp, &idesc) & ALTERED);
+}
+
+/*
+ * Attempt to expand the size of a directory
+ */
+expanddir(dp, name)
+ register struct dinode *dp;
+ char *name;
+{
+ daddr_t lastbn, newblk;
+ register struct bufarea *bp;
+ char *cp, firstblk[DIRBLKSIZ];
+
+ lastbn = lblkno(&sblock, dp->di_size);
+ if (lastbn >= NDADDR - 1 || dp->di_db[lastbn] == 0 || dp->di_size == 0)
+ return (0);
+ if ((newblk = allocblk(sblock.fs_frag)) == 0)
+ return (0);
+ dp->di_db[lastbn + 1] = dp->di_db[lastbn];
+ dp->di_db[lastbn] = newblk;
+ dp->di_size += sblock.fs_bsize;
+ dp->di_blocks += btodb(sblock.fs_bsize);
+ bp = getdirblk(dp->di_db[lastbn + 1],
+ (long)dblksize(&sblock, dp, lastbn + 1));
+ if (bp->b_errs)
+ goto bad;
+ bcopy(bp->b_un.b_buf, firstblk, DIRBLKSIZ);
+ bp = getdirblk(newblk, sblock.fs_bsize);
+ if (bp->b_errs)
+ goto bad;
+ bcopy(firstblk, bp->b_un.b_buf, DIRBLKSIZ);
+ for (cp = &bp->b_un.b_buf[DIRBLKSIZ];
+ cp < &bp->b_un.b_buf[sblock.fs_bsize];
+ cp += DIRBLKSIZ)
+ bcopy((char *)&emptydir, cp, sizeof emptydir);
+ dirty(bp);
+ bp = getdirblk(dp->di_db[lastbn + 1],
+ (long)dblksize(&sblock, dp, lastbn + 1));
+ if (bp->b_errs)
+ goto bad;
+ bcopy((char *)&emptydir, bp->b_un.b_buf, sizeof emptydir);
+ pwarn("NO SPACE LEFT IN %s", name);
+ if (preen)
+ printf(" (EXPANDED)\n");
+ else if (reply("EXPAND") == 0)
+ goto bad;
+ dirty(bp);
+ inodirty();
+ return (1);
+bad:
+ dp->di_db[lastbn] = dp->di_db[lastbn + 1];
+ dp->di_db[lastbn + 1] = 0;
+ dp->di_size -= sblock.fs_bsize;
+ dp->di_blocks -= btodb(sblock.fs_bsize);
+ freeblk(newblk, sblock.fs_frag);
+ return (0);
+}
+
+/*
+ * allocate a new directory
+ */
+allocdir(parent, request, mode)
+ ino_t parent, request;
+ int mode;
+{
+ ino_t ino;
+ char *cp;
+ struct dinode *dp;
+ register struct bufarea *bp;
+ struct dirtemplate *dirp;
+
+ ino = allocino(request, IFDIR|mode);
+ if (newinofmt)
+ dirp = &dirhead;
+ else
+ dirp = (struct dirtemplate *)&odirhead;
+ dirp->dot_ino = ino;
+ dirp->dotdot_ino = parent;
+ dp = ginode(ino);
+ bp = getdirblk(dp->di_db[0], sblock.fs_fsize);
+ if (bp->b_errs) {
+ freeino(ino);
+ return (0);
+ }
+ bcopy((char *)dirp, bp->b_un.b_buf, sizeof(struct dirtemplate));
+ for (cp = &bp->b_un.b_buf[DIRBLKSIZ];
+ cp < &bp->b_un.b_buf[sblock.fs_fsize];
+ cp += DIRBLKSIZ)
+ bcopy((char *)&emptydir, cp, sizeof emptydir);
+ dirty(bp);
+ dp->di_nlink = 2;
+ inodirty();
+ if (ino == ROOTINO) {
+ lncntp[ino] = dp->di_nlink;
+ cacheino(dp, ino);
+ return(ino);
+ }
+ if (statemap[parent] != DSTATE && statemap[parent] != DFOUND) {
+ freeino(ino);
+ return (0);
+ }
+ cacheino(dp, ino);
+ statemap[ino] = statemap[parent];
+ if (statemap[ino] == DSTATE) {
+ lncntp[ino] = dp->di_nlink;
+ lncntp[parent]++;
+ }
+ dp = ginode(parent);
+ dp->di_nlink++;
+ inodirty();
+ return (ino);
+}
+
+/*
+ * free a directory inode
+ */
+freedir(ino, parent)
+ ino_t ino, parent;
+{
+ struct dinode *dp;
+
+ if (ino != parent) {
+ dp = ginode(parent);
+ dp->di_nlink--;
+ inodirty();
+ }
+ freeino(ino);
+}
+
+/*
+ * generate a temporary name for the lost+found directory.
+ */
+lftempname(bufp, ino)
+ char *bufp;
+ ino_t ino;
+{
+ register ino_t in;
+ register char *cp;
+ int namlen;
+
+ cp = bufp + 2;
+ for (in = maxino; in > 0; in /= 10)
+ cp++;
+ *--cp = 0;
+ namlen = cp - bufp;
+ in = ino;
+ while (cp > bufp) {
+ *--cp = (in % 10) + '0';
+ in /= 10;
+ }
+ *cp = '#';
+ return (namlen);
+}
+
+/*
+ * Get a directory block.
+ * Insure that it is held until another is requested.
+ */
+struct bufarea *
+getdirblk(blkno, size)
+ daddr_t blkno;
+ long size;
+{
+
+ if (pdirbp != 0)
+ pdirbp->b_flags &= ~B_INUSE;
+ pdirbp = getdatablk(blkno, size);
+ return (pdirbp);
+}
diff --git a/bsdfsck/fsck.h b/bsdfsck/fsck.h
new file mode 100644
index 00000000..c418f66c
--- /dev/null
+++ b/bsdfsck/fsck.h
@@ -0,0 +1,303 @@
+/*
+ * Copyright (c) 1980, 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. 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.
+ *
+ * from: @(#)fsck.h 8.1 (Berkeley) 6/5/93
+ * $Id: fsck.h,v 1.10 1994/10/05 17:09:06 mib Exp $
+ */
+
+/* Begin GNU Hurd */
+
+/* GNU ufs doesn't define struct direct, but fsck needs it. */
+#define direct directory_entry
+
+/* For GNU Hurd: the ufs DIRSIZ macro is different than the BSD
+ 4.4 version that fsck expects. So we provide here the BSD version. */
+#undef DIRSIZ
+#if (BYTE_ORDER == LITTLE_ENDIAN)
+#define DIRSIZ(oldfmt, dp) \
+ ((oldfmt) ? \
+ ((sizeof (struct direct) - (MAXNAMLEN+1)) + (((dp)->d_type+1 + 3) &~ 3)) : \
+ ((sizeof (struct direct) - (MAXNAMLEN+1)) + (((dp)->d_namlen+1 + 3) &~ 3)))
+#else
+#define DIRSIZ(oldfmt, dp) \
+ ((sizeof (struct direct) - (MAXNAMLEN+1)) + (((dp)->d_namlen+1 + 3) &~ 3))
+#endif
+
+/* GNU ufs has no need of struct dirtemplate; so provide the
+ BSD version here. */
+/*
+ * 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_long dot_ino;
+ short dot_reclen;
+ u_char dot_type;
+ u_char dot_namlen;
+ char dot_name[4]; /* must be multiple of 4 */
+ u_long dotdot_ino;
+ short dotdot_reclen;
+ u_char dotdot_type;
+ u_char dotdot_namlen;
+ char dotdot_name[4]; /* ditto */
+};
+/*
+ * This is the old format of directories, sanz type element.
+ */
+struct odirtemplate {
+ u_long dot_ino;
+ short dot_reclen;
+ u_short dot_namlen;
+ char dot_name[4]; /* must be multiple of 4 */
+ u_long dotdot_ino;
+ short dotdot_reclen;
+ u_short dotdot_namlen;
+ char dotdot_name[4]; /* ditto */
+};
+
+/* These shouldn't be used by anyone, but fsck seems to need it */
+#define DEV_BSIZE 512
+#define MAXPATHLEN 1024
+
+/* Provide mode from struct dinode * */
+#define DI_MODE(dp) (((dp)->di_modeh << 16) | (dp)->di_model)
+
+#define NBBY 8
+
+#define MAXPHYS (64 * 1024)
+
+/* The fsck code in setup.c sets the fs_csp table which ufs doesn't want.
+ So here is the fs_cs macro from ufs for use when that table is real. */
+#undef fs_cs
+#define fs_cs(fs, indx) \
+ fs_csp[(indx) >> (fs)->fs_csshift][(indx) & ~(fs)->fs_csmask]
+
+#define dblksize(fs, dip, lbn) \
+ (((lbn) >= NDADDR || (dip)->di_size >= ((lbn) + 1) << (fs)->fs_bshift) \
+ ? (fs)->fs_bsize \
+ : (fragroundup(fs, blkoff(fs, (dip)->di_size))))
+
+/* Don't include dirent.h lest we get confused, but we still want this. */
+#define IFTODT(mode) (((mode) & 0170000) >> 12)
+#define DT_DIR IFTODT (IFDIR)
+
+/* missing macros */
+
+/* Convert bytes to disk blocks */
+#define btodb(bytes) ((bytes) / DEV_BSIZE)
+
+
+
+/* End GNU Hurd additions */
+
+
+#define MAXDUP 10 /* limit on dup blks (per inode) */
+#define MAXBAD 10 /* limit on bad blks (per inode) */
+#define MAXBUFSPACE 40*1024 /* maximum space to allocate to buffers */
+#define INOBUFSIZE 56*1024 /* size of buffer to read inodes in pass1 */
+
+#ifndef BUFSIZ
+#define BUFSIZ 1024
+#endif
+
+#define USTATE 01 /* inode not allocated */
+#define FSTATE 02 /* inode is file */
+#define DSTATE 03 /* inode is directory */
+#define DFOUND 04 /* directory found during descent */
+#define DCLEAR 05 /* directory is to be cleared */
+#define FCLEAR 06 /* file is to be cleared */
+
+/*
+ * buffer cache structure.
+ */
+struct bufarea {
+ struct bufarea *b_next; /* free list queue */
+ struct bufarea *b_prev; /* free list queue */
+ daddr_t b_bno;
+ int b_size;
+ int b_errs;
+ int b_flags;
+ union {
+ char *b_buf; /* buffer space */
+ daddr_t *b_indir; /* indirect block */
+ struct fs *b_fs; /* super block */
+ struct cg *b_cg; /* cylinder group */
+ struct dinode *b_dinode; /* inode block */
+ } b_un;
+ char b_dirty;
+};
+
+#define B_INUSE 1
+
+#define MINBUFS 5 /* minimum number of buffers required */
+struct bufarea bufhead; /* head of list of other blks in filesys */
+struct bufarea sblk; /* file system superblock */
+struct bufarea cgblk; /* cylinder group blocks */
+struct bufarea *pdirbp; /* current directory contents */
+struct bufarea *pbp; /* current inode block */
+struct bufarea *getdatablk();
+
+#define dirty(bp) (bp)->b_dirty = 1
+#define initbarea(bp) \
+ (bp)->b_dirty = 0; \
+ (bp)->b_bno = (daddr_t)-1; \
+ (bp)->b_flags = 0;
+
+#define sbdirty() sblk.b_dirty = 1
+#define cgdirty() cgblk.b_dirty = 1
+#define sblock (*sblk.b_un.b_fs)
+#define cgrp (*cgblk.b_un.b_cg)
+
+enum fixstate {DONTKNOW, NOFIX, FIX, IGNORE};
+
+struct inodesc {
+ enum fixstate id_fix; /* policy on fixing errors */
+ int (*id_func)(); /* function to be applied to blocks of inode */
+ ino_t id_number; /* inode number described */
+ ino_t id_parent; /* for DATA nodes, their parent */
+ daddr_t id_blkno; /* current block number being examined */
+ int id_numfrags; /* number of frags contained in block */
+ quad_t id_filesize; /* for DATA nodes, the size of the directory */
+ int id_loc; /* for DATA nodes, current location in dir */
+ int id_entryno; /* for DATA nodes, current entry number */
+ struct direct *id_dirp; /* for DATA nodes, ptr to current entry */
+ char *id_name; /* for DATA nodes, name to find or enter */
+ char id_type; /* type of descriptor, DATA or ADDR */
+};
+/* file types */
+#define DATA 1
+#define ADDR 2
+
+/*
+ * Linked list of duplicate blocks.
+ *
+ * The list is composed of two parts. The first part of the
+ * list (from duplist through the node pointed to by muldup)
+ * contains a single copy of each duplicate block that has been
+ * found. The second part of the list (from muldup to the end)
+ * contains duplicate blocks that have been found more than once.
+ * To check if a block has been found as a duplicate it is only
+ * necessary to search from duplist through muldup. To find the
+ * total number of times that a block has been found as a duplicate
+ * the entire list must be searched for occurences of the block
+ * in question. The following diagram shows a sample list where
+ * w (found twice), x (found once), y (found three times), and z
+ * (found once) are duplicate block numbers:
+ *
+ * w -> y -> x -> z -> y -> w -> y
+ * ^ ^
+ * | |
+ * duplist muldup
+ */
+struct dups {
+ struct dups *next;
+ daddr_t dup;
+};
+struct dups *duplist; /* head of dup list */
+struct dups *muldup; /* end of unique duplicate dup block numbers */
+
+/*
+ * Linked list of inodes with zero link counts.
+ */
+struct zlncnt {
+ struct zlncnt *next;
+ ino_t zlncnt;
+};
+struct zlncnt *zlnhead; /* head of zero link count list */
+
+/*
+ * Inode cache data structures.
+ */
+struct inoinfo {
+ struct inoinfo *i_nexthash; /* next entry in hash chain */
+ ino_t i_number; /* inode number of this entry */
+ ino_t i_parent; /* inode number of parent */
+ ino_t i_dotdot; /* inode number of `..' */
+ size_t i_isize; /* size of inode */
+ u_int i_numblks; /* size of block array in bytes */
+ daddr_t i_blks[1]; /* actually longer */
+} **inphead, **inpsort;
+long numdirs, listmax, inplast;
+
+char *cdevname; /* name of device being checked */
+long dev_bsize; /* computed value of DEV_BSIZE */
+long secsize; /* actual disk sector size */
+char nflag; /* assume a no response */
+char yflag; /* assume a yes response */
+int bflag; /* location of alternate super block */
+int debug; /* output debugging info */
+int cvtlevel; /* convert to newer file system format */
+int doinglevel1; /* converting to new cylinder group format */
+int doinglevel2; /* converting to new inode format */
+int newinofmt; /* filesystem has new inode format */
+char preen; /* just fix normal inconsistencies */
+char hotroot; /* checking root device */
+char havesb; /* superblock has been read */
+int fsmodified; /* 1 => write done to file system */
+int fsreadfd; /* file descriptor for reading file system */
+int fswritefd; /* file descriptor for writing file system */
+
+daddr_t maxfsblock; /* number of blocks in the file system */
+char *blockmap; /* ptr to primary blk allocation map */
+ino_t maxino; /* number of inodes in file system */
+ino_t lastino; /* last inode in use */
+char *statemap; /* ptr to inode state table */
+char *typemap; /* ptr to inode type table */
+short *lncntp; /* ptr to link count table */
+
+ino_t lfdir; /* lost & found directory inode number */
+char *lfname; /* lost & found directory name */
+int lfmode; /* lost & found directory creation mode */
+
+daddr_t n_blks; /* number of blocks in use */
+daddr_t n_files; /* number of files in use */
+
+#define clearinode(dp) (*(dp) = zino)
+struct dinode zino;
+
+#define setbmap(blkno) setbit(blockmap, blkno)
+#define testbmap(blkno) isset(blockmap, blkno)
+#define clrbmap(blkno) clrbit(blockmap, blkno)
+
+#define STOP 0x01
+#define SKIP 0x02
+#define KEEPON 0x04
+#define ALTERED 0x08
+#define FOUND 0x10
+
+time_t time();
+struct dinode *ginode();
+struct inoinfo *getinoinfo();
+void getblk();
+ino_t allocino();
+int findino();
diff --git a/bsdfsck/inode.c b/bsdfsck/inode.c
new file mode 100644
index 00000000..7b48aef6
--- /dev/null
+++ b/bsdfsck/inode.c
@@ -0,0 +1,562 @@
+/*
+ * Copyright (c) 1980, 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. 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.
+ */
+
+#ifndef lint
+/*static char sccsid[] = "from: @(#)inode.c 8.4 (Berkeley) 4/18/94";*/
+static char *rcsid = "$Id: inode.c,v 1.6 1994/10/05 17:05:30 mib Exp $";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include "../ufs/dinode.h"
+#include "../ufs/dir.h"
+#include "../ufs/fs.h"
+#ifndef SMALL
+#include <pwd.h>
+#endif
+#include <stdlib.h>
+#include <string.h>
+#include "fsck.h"
+
+static ino_t startinum;
+
+ckinode(dp, idesc)
+ struct dinode *dp;
+ register struct inodesc *idesc;
+{
+ register daddr_t *ap;
+ long ret, n, ndb, offset;
+ struct dinode dino;
+ quad_t remsize, sizepb;
+ mode_t mode;
+
+ if (idesc->id_fix != IGNORE)
+ idesc->id_fix = DONTKNOW;
+ idesc->id_entryno = 0;
+ idesc->id_filesize = dp->di_size;
+ mode = DI_MODE(dp) & IFMT;
+ if (mode == IFBLK || mode == IFCHR
+ || (mode == IFLNK && sblock.fs_maxsymlinklen != -1 &&
+ (dp->di_size < sblock.fs_maxsymlinklen
+ || (sblock.fs_maxsymlinklen == 0 && dp->di_blocks == 0))))
+ return (KEEPON);
+ dino = *dp;
+ ndb = howmany(dino.di_size, sblock.fs_bsize);
+ for (ap = &dino.di_db[0]; ap < &dino.di_db[NDADDR]; ap++) {
+ if (--ndb == 0 && (offset = blkoff(&sblock, dino.di_size)) != 0)
+ idesc->id_numfrags =
+ numfrags(&sblock, fragroundup(&sblock, offset));
+ else
+ idesc->id_numfrags = sblock.fs_frag;
+ if (*ap == 0)
+ continue;
+ idesc->id_blkno = *ap;
+ if (idesc->id_type == ADDR)
+ ret = (*idesc->id_func)(idesc);
+ else
+ ret = dirscan(idesc);
+ if (ret & STOP)
+ return (ret);
+ }
+ idesc->id_numfrags = sblock.fs_frag;
+ remsize = dino.di_size - sblock.fs_bsize * NDADDR;
+ sizepb = sblock.fs_bsize;
+ for (ap = &dino.di_ib[0], n = 1; n <= NIADDR; ap++, n++) {
+ if (*ap) {
+ idesc->id_blkno = *ap;
+ ret = iblock(idesc, n, remsize);
+ if (ret & STOP)
+ return (ret);
+ }
+ sizepb *= NINDIR(&sblock);
+ remsize -= sizepb;
+ }
+ /* GNU Hurd extension. */
+ if (dino.di_trans && idesc->id_type == ADDR)
+ {
+ idesc->id_blkno = dino.di_trans;
+ idesc->id_numfrags = sblock.fs_frag;
+ return (*idesc->id_func)(idesc);
+ }
+ return (KEEPON);
+}
+
+iblock(idesc, ilevel, isize)
+ struct inodesc *idesc;
+ long ilevel;
+ quad_t isize;
+{
+ register daddr_t *ap;
+ register daddr_t *aplim;
+ register struct bufarea *bp;
+ int i, n, (*func)(), nif;
+ quad_t sizepb;
+ char buf[BUFSIZ];
+ extern int dirscan(), pass1check();
+
+ if (idesc->id_type == ADDR) {
+ func = idesc->id_func;
+ if (((n = (*func)(idesc)) & KEEPON) == 0)
+ return (n);
+ } else
+ func = dirscan;
+ if (chkrange(idesc->id_blkno, idesc->id_numfrags))
+ return (SKIP);
+ bp = getdatablk(idesc->id_blkno, sblock.fs_bsize);
+ ilevel--;
+ for (sizepb = sblock.fs_bsize, i = 0; i < ilevel; i++)
+ sizepb *= NINDIR(&sblock);
+ nif = howmany(isize , sizepb);
+ if (nif > NINDIR(&sblock))
+ nif = NINDIR(&sblock);
+ if (idesc->id_func == pass1check && nif < NINDIR(&sblock)) {
+ aplim = &bp->b_un.b_indir[NINDIR(&sblock)];
+ for (ap = &bp->b_un.b_indir[nif]; ap < aplim; ap++) {
+ if (*ap == 0)
+ continue;
+ (void)sprintf(buf, "PARTIALLY TRUNCATED INODE I=%lu",
+ idesc->id_number);
+ if (dofix(idesc, buf)) {
+ *ap = 0;
+ dirty(bp);
+ }
+ }
+ flush(fswritefd, bp);
+ }
+ aplim = &bp->b_un.b_indir[nif];
+ for (ap = bp->b_un.b_indir; ap < aplim; ap++) {
+ if (*ap) {
+ idesc->id_blkno = *ap;
+ if (ilevel == 0)
+ n = (*func)(idesc);
+ else
+ n = iblock(idesc, ilevel, isize);
+ if (n & STOP) {
+ bp->b_flags &= ~B_INUSE;
+ return (n);
+ }
+ }
+ isize -= sizepb;
+ }
+ bp->b_flags &= ~B_INUSE;
+ return (KEEPON);
+}
+
+/*
+ * Check that a block in a legal block number.
+ * Return 0 if in range, 1 if out of range.
+ */
+chkrange(blk, cnt)
+ daddr_t blk;
+ int cnt;
+{
+ register int c;
+
+ if ((unsigned)(blk + cnt) > maxfsblock)
+ return (1);
+ c = dtog(&sblock, blk);
+ if (blk < cgdmin(&sblock, c)) {
+ if ((blk + cnt) > cgsblock(&sblock, c)) {
+ if (debug) {
+ printf("blk %ld < cgdmin %ld;",
+ blk, cgdmin(&sblock, c));
+ printf(" blk + cnt %ld > cgsbase %ld\n",
+ blk + cnt, cgsblock(&sblock, c));
+ }
+ return (1);
+ }
+ } else {
+ if ((blk + cnt) > cgbase(&sblock, c+1)) {
+ if (debug) {
+ printf("blk %ld >= cgdmin %ld;",
+ blk, cgdmin(&sblock, c));
+ printf(" blk + cnt %ld > sblock.fs_fpg %ld\n",
+ blk+cnt, sblock.fs_fpg);
+ }
+ return (1);
+ }
+ }
+ return (0);
+}
+
+/*
+ * General purpose interface for reading inodes.
+ */
+struct dinode *
+ginode(inumber)
+ ino_t inumber;
+{
+ daddr_t iblk;
+
+ if (inumber < ROOTINO || inumber > maxino)
+ errexit("bad inode number %d to ginode\n", inumber);
+ if (startinum == 0 ||
+ inumber < startinum || inumber >= startinum + INOPB(&sblock)) {
+ iblk = ino_to_fsba(&sblock, inumber);
+ if (pbp != 0)
+ pbp->b_flags &= ~B_INUSE;
+ pbp = getdatablk(iblk, sblock.fs_bsize);
+ startinum = (inumber / INOPB(&sblock)) * INOPB(&sblock);
+ }
+ return (&pbp->b_un.b_dinode[inumber % INOPB(&sblock)]);
+}
+
+/*
+ * Special purpose version of ginode used to optimize first pass
+ * over all the inodes in numerical order.
+ */
+ino_t nextino, lastinum;
+long readcnt, readpercg, fullcnt, inobufsize, partialcnt, partialsize;
+struct dinode *inodebuf;
+
+struct dinode *
+getnextinode(inumber)
+ ino_t inumber;
+{
+ long size;
+ daddr_t dblk;
+ static struct dinode *dp;
+
+ if (inumber != nextino++ || inumber > maxino)
+ errexit("bad inode number %d to nextinode\n", inumber);
+ if (inumber >= lastinum) {
+ readcnt++;
+ dblk = fsbtodb(&sblock, ino_to_fsba(&sblock, lastinum));
+ if (readcnt % readpercg == 0) {
+ size = partialsize;
+ lastinum += partialcnt;
+ } else {
+ size = inobufsize;
+ lastinum += fullcnt;
+ }
+ (void)bread(fsreadfd, (char *)inodebuf, dblk, size); /* ??? */
+ dp = inodebuf;
+ }
+ return (dp++);
+}
+
+resetinodebuf()
+{
+
+ startinum = 0;
+ nextino = 0;
+ lastinum = 0;
+ readcnt = 0;
+ inobufsize = blkroundup(&sblock, INOBUFSIZE);
+ fullcnt = inobufsize / sizeof(struct dinode);
+ readpercg = sblock.fs_ipg / fullcnt;
+ partialcnt = sblock.fs_ipg % fullcnt;
+ partialsize = partialcnt * sizeof(struct dinode);
+ if (partialcnt != 0) {
+ readpercg++;
+ } else {
+ partialcnt = fullcnt;
+ partialsize = inobufsize;
+ }
+ if (inodebuf == NULL &&
+ (inodebuf = (struct dinode *)malloc((unsigned)inobufsize)) == NULL)
+ errexit("Cannot allocate space for inode buffer\n");
+ while (nextino < ROOTINO)
+ (void)getnextinode(nextino);
+}
+
+freeinodebuf()
+{
+
+ if (inodebuf != NULL)
+ free((char *)inodebuf);
+ inodebuf = NULL;
+}
+
+/*
+ * Routines to maintain information about directory inodes.
+ * This is built during the first pass and used during the
+ * second and third passes.
+ *
+ * Enter inodes into the cache.
+ */
+cacheino(dp, inumber)
+ register struct dinode *dp;
+ ino_t inumber;
+{
+ register struct inoinfo *inp;
+ struct inoinfo **inpp;
+ unsigned int blks;
+
+ blks = howmany(dp->di_size, sblock.fs_bsize);
+ if (blks > NDADDR)
+ blks = NDADDR + NIADDR;
+ inp = (struct inoinfo *)
+ malloc(sizeof(*inp) + (blks - 1) * sizeof(daddr_t));
+ if (inp == NULL)
+ return;
+ inpp = &inphead[inumber % numdirs];
+ inp->i_nexthash = *inpp;
+ *inpp = inp;
+ inp->i_parent = (ino_t)0;
+ inp->i_dotdot = (ino_t)0;
+ inp->i_number = inumber;
+ inp->i_isize = dp->di_size;
+ inp->i_numblks = blks * sizeof(daddr_t);
+ bcopy((char *)&dp->di_db[0], (char *)&inp->i_blks[0],
+ (size_t)inp->i_numblks);
+ if (inplast == listmax) {
+ listmax += 100;
+ inpsort = (struct inoinfo **)realloc((char *)inpsort,
+ (unsigned)listmax * sizeof(struct inoinfo *));
+ if (inpsort == NULL)
+ errexit("cannot increase directory list");
+ }
+ inpsort[inplast++] = inp;
+}
+
+/*
+ * Look up an inode cache structure.
+ */
+struct inoinfo *
+getinoinfo(inumber)
+ ino_t inumber;
+{
+ register struct inoinfo *inp;
+
+ for (inp = inphead[inumber % numdirs]; inp; inp = inp->i_nexthash) {
+ if (inp->i_number != inumber)
+ continue;
+ return (inp);
+ }
+ errexit("cannot find inode %d\n", inumber);
+ return ((struct inoinfo *)0);
+}
+
+/*
+ * Clean up all the inode cache structure.
+ */
+inocleanup()
+{
+ register struct inoinfo **inpp;
+
+ if (inphead == NULL)
+ return;
+ for (inpp = &inpsort[inplast - 1]; inpp >= inpsort; inpp--)
+ free((char *)(*inpp));
+ free((char *)inphead);
+ free((char *)inpsort);
+ inphead = inpsort = NULL;
+}
+
+inodirty()
+{
+
+ dirty(pbp);
+}
+
+clri(idesc, type, flag)
+ register struct inodesc *idesc;
+ char *type;
+ int flag;
+{
+ register struct dinode *dp;
+
+ dp = ginode(idesc->id_number);
+ if (flag == 1) {
+ pwarn("%s %s", type,
+ (DI_MODE(dp) & IFMT) == IFDIR ? "DIR" : "FILE");
+ pinode(idesc->id_number);
+ }
+ if (preen || reply("CLEAR") == 1) {
+ if (preen)
+ printf(" (CLEARED)\n");
+ n_files--;
+ (void)ckinode(dp, idesc);
+ clearinode(dp);
+ statemap[idesc->id_number] = USTATE;
+ inodirty();
+ }
+}
+
+findname(idesc)
+ struct inodesc *idesc;
+{
+ register struct direct *dirp = idesc->id_dirp;
+
+ if (dirp->d_ino != idesc->id_parent)
+ return (KEEPON);
+ bcopy(dirp->d_name, idesc->id_name, (size_t)dirp->d_namlen + 1);
+ return (STOP|FOUND);
+}
+
+findino(idesc)
+ struct inodesc *idesc;
+{
+ register struct direct *dirp = idesc->id_dirp;
+
+ if (dirp->d_ino == 0)
+ return (KEEPON);
+ if (strcmp(dirp->d_name, idesc->id_name) == 0 &&
+ dirp->d_ino >= ROOTINO && dirp->d_ino <= maxino) {
+ idesc->id_parent = dirp->d_ino;
+ return (STOP|FOUND);
+ }
+ return (KEEPON);
+}
+
+pinode(ino)
+ ino_t ino;
+{
+ register struct dinode *dp;
+ register char *p;
+ struct passwd *pw;
+ char *ctime();
+
+ printf(" I=%lu ", ino);
+ if (ino < ROOTINO || ino > maxino)
+ return;
+ dp = ginode(ino);
+ printf(" OWNER=");
+#ifndef SMALL
+ if ((pw = getpwuid((int)dp->di_uid)) != 0)
+ printf("%s ", pw->pw_name);
+ else
+#endif
+ printf("%u ", (unsigned)dp->di_uid);
+ printf("MODE=%o\n", DI_MODE(dp));
+ if (preen)
+ printf("%s: ", cdevname);
+ printf("SIZE=%qu ", dp->di_size);
+ p = ctime(&dp->di_mtime.ts_sec);
+ printf("MTIME=%12.12s %4.4s ", &p[4], &p[20]);
+}
+
+blkerror(ino, type, blk)
+ ino_t ino;
+ char *type;
+ daddr_t blk;
+{
+
+ pfatal("%ld %s I=%lu", blk, type, ino);
+ printf("\n");
+ switch (statemap[ino]) {
+
+ case FSTATE:
+ statemap[ino] = FCLEAR;
+ return;
+
+ case DSTATE:
+ statemap[ino] = DCLEAR;
+ return;
+
+ case FCLEAR:
+ case DCLEAR:
+ return;
+
+ default:
+ errexit("BAD STATE %d TO BLKERR", statemap[ino]);
+ /* NOTREACHED */
+ }
+}
+
+/*
+ * allocate an unused inode
+ */
+ino_t
+allocino(request, type)
+ ino_t request;
+ int type;
+{
+ register ino_t ino;
+ register struct dinode *dp;
+
+ if (request == 0)
+ request = ROOTINO;
+ else if (statemap[request] != USTATE)
+ return (0);
+ for (ino = request; ino < maxino; ino++)
+ if (statemap[ino] == USTATE)
+ break;
+ if (ino == maxino)
+ return (0);
+ switch (type & IFMT) {
+ case IFDIR:
+ statemap[ino] = DSTATE;
+ break;
+ case IFREG:
+ case IFLNK:
+ statemap[ino] = FSTATE;
+ break;
+ default:
+ return (0);
+ }
+ dp = ginode(ino);
+ dp->di_db[0] = allocblk((long)1);
+ if (dp->di_db[0] == 0) {
+ statemap[ino] = USTATE;
+ return (0);
+ }
+#if 0
+ dp->di_mode = type;
+#else
+ dp->di_modeh = (type & 0xffff0000) >> 16;
+ dp->di_model = (type & 0x0000ffff);
+#endif
+ (void)time(&dp->di_atime.ts_sec);
+ dp->di_mtime = dp->di_ctime = dp->di_atime;
+ dp->di_size = sblock.fs_fsize;
+ dp->di_blocks = btodb(sblock.fs_fsize);
+ n_files++;
+ inodirty();
+ if (newinofmt)
+ typemap[ino] = IFTODT(type);
+ return (ino);
+}
+
+/*
+ * deallocate an inode
+ */
+freeino(ino)
+ ino_t ino;
+{
+ struct inodesc idesc;
+ extern int pass4check();
+ struct dinode *dp;
+
+ bzero((char *)&idesc, sizeof(struct inodesc));
+ idesc.id_type = ADDR;
+ idesc.id_func = pass4check;
+ idesc.id_number = ino;
+ dp = ginode(ino);
+ (void)ckinode(dp, &idesc);
+ clearinode(dp);
+ inodirty();
+ statemap[ino] = USTATE;
+ n_files--;
+}
diff --git a/bsdfsck/main.c b/bsdfsck/main.c
new file mode 100644
index 00000000..adf84f74
--- /dev/null
+++ b/bsdfsck/main.c
@@ -0,0 +1,333 @@
+/*
+ * Copyright (c) 1980, 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. 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.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1980, 1986, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+/*static char sccsid[] = "from: @(#)main.c 8.2 (Berkeley) 1/23/94";*/
+static char *rcsid = "$Id: main.c,v 1.4 1994/08/26 18:06:30 mib Exp $";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/time.h>
+/*#include <sys/mount.h> */
+#include "../ufs/dinode.h"
+#include "../ufs/fs.h"
+/* #include <fstab.h> */
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdio.h>
+#include "fsck.h"
+
+void catch(), catchquit(), voidquit();
+int returntosingle;
+
+/* GNU Hurd patch */
+#define blockcheck(a) (a)
+
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int ch;
+ int ret, maxrun = 0;
+ extern int docheck(), checkfilesys();
+ extern char *optarg;
+/* extern char *blockcheck(); */
+ extern int optind;
+
+ sync();
+ while ((ch = getopt(argc, argv, "dpnNyYb:c:l:m:")) != EOF) {
+ switch (ch) {
+ case 'p':
+ preen++;
+ break;
+
+ case 'b':
+ bflag = argtoi('b', "number", optarg, 10);
+ printf("Alternate super block location: %d\n", bflag);
+ break;
+
+ case 'c':
+ cvtlevel = argtoi('c', "conversion level", optarg, 10);
+ break;
+
+ case 'd':
+ debug++;
+ break;
+
+ case 'l':
+ maxrun = argtoi('l', "number", optarg, 10);
+ break;
+
+ case 'm':
+ lfmode = argtoi('m', "mode", optarg, 8);
+ if (lfmode &~ 07777)
+ errexit("bad mode to -m: %o\n", lfmode);
+ printf("** lost+found creation mode %o\n", lfmode);
+ break;
+
+ case 'n':
+ case 'N':
+ nflag++;
+ yflag = 0;
+ break;
+
+ case 'y':
+ case 'Y':
+ yflag++;
+ nflag = 0;
+ break;
+
+ default:
+ errexit("%c option?\n", ch);
+ }
+ }
+ argc -= optind;
+ argv += optind;
+ if (signal(SIGINT, SIG_IGN) != SIG_IGN)
+ (void)signal(SIGINT, catch);
+ if (preen)
+ (void)signal(SIGQUIT, catchquit);
+ if (argc) {
+ while (argc-- > 0)
+ (void)checkfilesys(blockcheck(*argv++), 0, 0L, 0);
+ exit(0);
+ }
+ fprintf (stderr, "You must explicitly name the filesystem to check\n");
+ exit (1);
+#if 0
+ ret = checkfstab(preen, maxrun, docheck, checkfilesys);
+ if (returntosingle)
+ exit(2);
+ exit(ret);
+#endif
+}
+
+argtoi(flag, req, str, base)
+ int flag;
+ char *req, *str;
+ int base;
+{
+ char *cp;
+ int ret;
+
+ ret = (int)strtol(str, &cp, base);
+ if (cp == str || *cp)
+ errexit("-%c flag requires a %s\n", flag, req);
+ return (ret);
+}
+
+#if 0
+/*
+ * Determine whether a filesystem should be checked.
+ */
+docheck(fsp)
+ register struct fstab *fsp;
+{
+
+ if (strcmp(fsp->fs_vfstype, "ufs") ||
+ (strcmp(fsp->fs_type, FSTAB_RW) &&
+ strcmp(fsp->fs_type, FSTAB_RO)) ||
+ fsp->fs_passno == 0)
+ return (0);
+ return (1);
+}
+#endif
+
+/*
+ * Check the specified filesystem.
+ */
+/* ARGSUSED */
+checkfilesys(filesys, mntpt, auxdata, child)
+ char *filesys, *mntpt;
+ long auxdata;
+{
+ daddr_t n_ffree, n_bfree;
+ struct dups *dp;
+ struct zlncnt *zlnp;
+ int cylno;
+
+ if (preen && child)
+ (void)signal(SIGQUIT, voidquit);
+ cdevname = filesys;
+ if (debug && preen)
+ pwarn("starting\n");
+ if (setup(filesys) == 0) {
+ if (preen)
+ pfatal("CAN'T CHECK FILE SYSTEM.");
+ return (0);
+ }
+ /*
+ * 1: scan inodes tallying blocks used
+ */
+ if (preen == 0) {
+ printf("** Last Mounted on %s\n", sblock.fs_fsmnt);
+#if 0
+ if (hotroot)
+ printf("** Root file system\n");
+#endif
+ printf("** Phase 1 - Check Blocks and Sizes\n");
+ }
+ pass1();
+
+ /*
+ * 1b: locate first references to duplicates, if any
+ */
+ if (duplist) {
+ if (preen)
+ pfatal("INTERNAL ERROR: dups with -p");
+ printf("** Phase 1b - Rescan For More DUPS\n");
+ pass1b();
+ }
+
+ /*
+ * 2: traverse directories from root to mark all connected directories
+ */
+ if (preen == 0)
+ printf("** Phase 2 - Check Pathnames\n");
+ pass2();
+
+ /*
+ * 3: scan inodes looking for disconnected directories
+ */
+ if (preen == 0)
+ printf("** Phase 3 - Check Connectivity\n");
+ pass3();
+
+ /*
+ * 4: scan inodes looking for disconnected files; check reference counts
+ */
+ if (preen == 0)
+ printf("** Phase 4 - Check Reference Counts\n");
+ pass4();
+
+ /*
+ * 5: check and repair resource counts in cylinder groups
+ */
+ if (preen == 0)
+ printf("** Phase 5 - Check Cyl groups\n");
+ pass5();
+
+ /*
+ * print out summary statistics
+ */
+ n_ffree = sblock.fs_cstotal.cs_nffree;
+ n_bfree = sblock.fs_cstotal.cs_nbfree;
+ pwarn("%ld files, %ld used, %ld free ",
+ n_files, n_blks, n_ffree + sblock.fs_frag * n_bfree);
+ printf("(%ld frags, %ld blocks, %d.%d%% fragmentation)\n",
+ n_ffree, n_bfree, (n_ffree * 100) / sblock.fs_dsize,
+ ((n_ffree * 1000 + sblock.fs_dsize / 2) / sblock.fs_dsize) % 10);
+ if (debug &&
+ (n_files -= maxino - ROOTINO - sblock.fs_cstotal.cs_nifree))
+ printf("%ld files missing\n", n_files);
+ if (debug) {
+ n_blks += sblock.fs_ncg *
+ (cgdmin(&sblock, 0) - cgsblock(&sblock, 0));
+ n_blks += cgsblock(&sblock, 0) - cgbase(&sblock, 0);
+ n_blks += howmany(sblock.fs_cssize, sblock.fs_fsize);
+ if (n_blks -= maxfsblock - (n_ffree + sblock.fs_frag * n_bfree))
+ printf("%ld blocks missing\n", n_blks);
+ if (duplist != NULL) {
+ printf("The following duplicate blocks remain:");
+ for (dp = duplist; dp; dp = dp->next)
+ printf(" %ld,", dp->dup);
+ printf("\n");
+ }
+ if (zlnhead != NULL) {
+ printf("The following zero link count inodes remain:");
+ for (zlnp = zlnhead; zlnp; zlnp = zlnp->next)
+ printf(" %lu,", zlnp->zlncnt);
+ printf("\n");
+ }
+ }
+ zlnhead = (struct zlncnt *)0;
+ duplist = (struct dups *)0;
+ muldup = (struct dups *)0;
+ inocleanup();
+ if (fsmodified) {
+ (void)time(&sblock.fs_time);
+ sbdirty();
+ }
+ if (cvtlevel && sblk.b_dirty) {
+ /*
+ * Write out the duplicate super blocks
+ */
+ for (cylno = 0; cylno < sblock.fs_ncg; cylno++)
+ bwrite(fswritefd, (char *)&sblock,
+ fsbtodb(&sblock, cgsblock(&sblock, cylno)), SBSIZE);
+ }
+ ckfini();
+ free(blockmap);
+ free(statemap);
+ free((char *)lncntp);
+ if (!fsmodified)
+ return (0);
+ if (!preen)
+ printf("\n***** FILE SYSTEM WAS MODIFIED *****\n");
+#if 0
+ if (hotroot) {
+ struct statfs stfs_buf;
+ /*
+ * We modified the root. Do a mount update on
+ * it, unless it is read-write, so we can continue.
+ */
+ if (statfs("/", &stfs_buf) == 0) {
+ long flags = stfs_buf.f_flags;
+ struct ufs_args args;
+ int ret;
+
+ if (flags & MNT_RDONLY) {
+ args.fspec = 0;
+ args.export.ex_flags = 0;
+ args.export.ex_root = 0;
+ flags |= MNT_UPDATE | MNT_RELOAD;
+ ret = mount(MOUNT_UFS, "/", flags, &args);
+ if (ret == 0)
+ return(0);
+ }
+ }
+ if (!preen)
+ printf("\n***** REBOOT NOW *****\n");
+ sync();
+ return (4);
+ }
+#endif
+ return (0);
+}
diff --git a/bsdfsck/pass1.c b/bsdfsck/pass1.c
new file mode 100644
index 00000000..46b0e109
--- /dev/null
+++ b/bsdfsck/pass1.c
@@ -0,0 +1,331 @@
+/*
+ * Copyright (c) 1980, 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. 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.
+ */
+
+#ifndef lint
+/*static char sccsid[] = "from: @(#)pass1.c 8.1 (Berkeley) 6/5/93";*/
+static char *rcsid = "$Id: pass1.c,v 1.4 1994/10/05 16:53:12 mib Exp $";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include "../ufs/dinode.h"
+#include "../ufs/dir.h"
+#include "../ufs/fs.h"
+#include <stdlib.h>
+#include <string.h>
+#include "fsck.h"
+
+static daddr_t badblk;
+static daddr_t dupblk;
+int pass1check();
+struct dinode *getnextinode();
+
+pass1()
+{
+ ino_t inumber;
+ int c, i, cgd;
+ struct inodesc idesc;
+
+ /*
+ * Set file system reserved blocks in used block map.
+ */
+ for (c = 0; c < sblock.fs_ncg; c++) {
+ cgd = cgdmin(&sblock, c);
+ if (c == 0) {
+ i = cgbase(&sblock, c);
+ cgd += howmany(sblock.fs_cssize, sblock.fs_fsize);
+ } else
+ i = cgsblock(&sblock, c);
+ for (; i < cgd; i++)
+ setbmap(i);
+ }
+ /*
+ * Find all allocated blocks.
+ */
+ bzero((char *)&idesc, sizeof(struct inodesc));
+ idesc.id_type = ADDR;
+ idesc.id_func = pass1check;
+ inumber = 0;
+ n_files = n_blks = 0;
+ resetinodebuf();
+ for (c = 0; c < sblock.fs_ncg; c++) {
+ for (i = 0; i < sblock.fs_ipg; i++, inumber++) {
+ if (inumber < ROOTINO)
+ continue;
+ checkinode(inumber, &idesc);
+ }
+ }
+ freeinodebuf();
+}
+
+checkinode(inumber, idesc)
+ ino_t inumber;
+ register struct inodesc *idesc;
+{
+ register struct dinode *dp;
+ struct zlncnt *zlnp;
+ int ndb, j;
+ mode_t mode;
+ char *symbuf;
+
+ dp = getnextinode(inumber);
+ mode = DI_MODE(dp) & IFMT;
+ if (mode == 0) {
+ /* Check for DI_TRANS here is a GNU Hurd addition. */
+ if (bcmp((char *)dp->di_db, (char *)zino.di_db,
+ NDADDR * sizeof(daddr_t)) ||
+ bcmp((char *)dp->di_ib, (char *)zino.di_ib,
+ NIADDR * sizeof(daddr_t)) ||
+ DI_MODE(dp) || dp->di_size || dp->di_trans) {
+ pfatal("PARTIALLY ALLOCATED INODE I=%lu", inumber);
+ if (reply("CLEAR") == 1) {
+ dp = ginode(inumber);
+ clearinode(dp);
+ inodirty();
+ }
+ }
+ statemap[inumber] = USTATE;
+ return;
+ }
+ lastino = inumber;
+ if (/* dp->di_size < 0 || */
+ dp->di_size + sblock.fs_bsize - 1 < dp->di_size) {
+ if (debug)
+ printf("bad size %qu:", dp->di_size);
+ goto unknown;
+ }
+ if (!preen && mode == IFMT && reply("HOLD BAD BLOCK") == 1) {
+ dp = ginode(inumber);
+ dp->di_size = sblock.fs_fsize;
+#if 0
+ dp->di_mode = IFREG|0600;
+#else
+ dp->di_modeh = 0;
+ dp->di_model = IFREG|0600;
+#endif
+ inodirty();
+ }
+ ndb = howmany(dp->di_size, sblock.fs_bsize);
+ if (ndb < 0) {
+ if (debug)
+ printf("bad size %qu ndb %d:",
+ dp->di_size, ndb);
+ goto unknown;
+ }
+ if (mode == IFBLK || mode == IFCHR)
+ ndb++;
+ if (mode == IFLNK) {
+ /*
+ * Note that the old fastlink format always had di_blocks set
+ * to 0. Other than that we no longer use the `spare' field
+ * (which is now the extended uid) for sanity checking, the
+ * new format is the same as the old. We simply ignore the
+ * conversion altogether. - mycroft, 19MAY1994
+ */
+ if (doinglevel2 &&
+ dp->di_size > 0 && dp->di_size < MAXSYMLINKLEN &&
+ dp->di_blocks != 0) {
+ symbuf = alloca(secsize);
+ if (bread(fsreadfd, symbuf,
+ fsbtodb(&sblock, dp->di_db[0]),
+ (long)secsize) != 0)
+ errexit("cannot read symlink");
+ if (debug) {
+ symbuf[dp->di_size] = 0;
+ printf("convert symlink %d(%s) of size %d\n",
+ inumber, symbuf, (long)dp->di_size);
+ }
+ dp = ginode(inumber);
+ bcopy(symbuf, (caddr_t)dp->di_shortlink,
+ (long)dp->di_size);
+ dp->di_blocks = 0;
+ inodirty();
+ }
+ /*
+ * Fake ndb value so direct/indirect block checks below
+ * will detect any garbage after symlink string.
+ */
+ if (sblock.fs_maxsymlinklen != -1 &&
+ (dp->di_size < sblock.fs_maxsymlinklen ||
+ (sblock.fs_maxsymlinklen == 0 && dp->di_blocks == 0))) {
+ ndb = howmany(dp->di_size, sizeof(daddr_t));
+ if (ndb > NDADDR) {
+ j = ndb - NDADDR;
+ for (ndb = 1; j > 1; j--)
+ ndb *= NINDIR(&sblock);
+ ndb += NDADDR;
+ }
+ }
+ }
+ for (j = ndb; j < NDADDR; j++)
+ if (dp->di_db[j] != 0) {
+ if (debug)
+ printf("bad direct addr: %ld\n", dp->di_db[j]);
+ goto unknown;
+ }
+ for (j = 0, ndb -= NDADDR; ndb > 0; j++)
+ ndb /= NINDIR(&sblock);
+ for (; j < NIADDR; j++)
+ if (dp->di_ib[j] != 0) {
+ if (debug)
+ printf("bad indirect addr: %ld\n",
+ dp->di_ib[j]);
+ goto unknown;
+ }
+ if (ftypeok(dp) == 0)
+ goto unknown;
+ n_files++;
+ lncntp[inumber] = dp->di_nlink;
+ if (dp->di_nlink <= 0) {
+ zlnp = (struct zlncnt *)malloc(sizeof *zlnp);
+ if (zlnp == NULL) {
+ pfatal("LINK COUNT TABLE OVERFLOW");
+ if (reply("CONTINUE") == 0)
+ errexit("");
+ } else {
+ zlnp->zlncnt = inumber;
+ zlnp->next = zlnhead;
+ zlnhead = zlnp;
+ }
+ }
+ if (mode == IFDIR) {
+ if (dp->di_size == 0)
+ statemap[inumber] = DCLEAR;
+ else
+ statemap[inumber] = DSTATE;
+ cacheino(dp, inumber);
+ } else
+ statemap[inumber] = FSTATE;
+ typemap[inumber] = IFTODT(mode);
+ if (doinglevel2 &&
+ (dp->di_ouid != (u_short)-1 || dp->di_ogid != (u_short)-1)) {
+ dp = ginode(inumber);
+ dp->di_uid = dp->di_ouid;
+ dp->di_ouid = -1;
+ dp->di_gid = dp->di_ogid;
+ dp->di_ogid = -1;
+ inodirty();
+ }
+ badblk = dupblk = 0;
+ idesc->id_number = inumber;
+ (void)ckinode(dp, idesc);
+ idesc->id_entryno *= btodb(sblock.fs_fsize);
+ if (dp->di_blocks != idesc->id_entryno) {
+ pwarn("INCORRECT BLOCK COUNT I=%lu (%ld should be %ld)",
+ inumber, dp->di_blocks, idesc->id_entryno);
+ if (preen)
+ printf(" (CORRECTED)\n");
+ else if (reply("CORRECT") == 0)
+ return;
+ dp = ginode(inumber);
+ dp->di_blocks = idesc->id_entryno;
+ inodirty();
+ }
+ return;
+unknown:
+ pfatal("UNKNOWN FILE TYPE I=%lu", inumber);
+ statemap[inumber] = FCLEAR;
+ if (reply("CLEAR") == 1) {
+ statemap[inumber] = USTATE;
+ dp = ginode(inumber);
+ clearinode(dp);
+ inodirty();
+ }
+}
+
+pass1check(idesc)
+ register struct inodesc *idesc;
+{
+ int res = KEEPON;
+ int anyout, nfrags;
+ daddr_t blkno = idesc->id_blkno;
+ register struct dups *dlp;
+ struct dups *new;
+
+ if ((anyout = chkrange(blkno, idesc->id_numfrags)) != 0) {
+ blkerror(idesc->id_number, "BAD", blkno);
+ if (badblk++ >= MAXBAD) {
+ pwarn("EXCESSIVE BAD BLKS I=%lu",
+ idesc->id_number);
+ if (preen)
+ printf(" (SKIPPING)\n");
+ else if (reply("CONTINUE") == 0)
+ errexit("");
+ return (STOP);
+ }
+ }
+ for (nfrags = idesc->id_numfrags; nfrags > 0; blkno++, nfrags--) {
+ if (anyout && chkrange(blkno, 1)) {
+ res = SKIP;
+ } else if (!testbmap(blkno)) {
+ n_blks++;
+ setbmap(blkno);
+ } else {
+ blkerror(idesc->id_number, "DUP", blkno);
+ if (dupblk++ >= MAXDUP) {
+ pwarn("EXCESSIVE DUP BLKS I=%lu",
+ idesc->id_number);
+ if (preen)
+ printf(" (SKIPPING)\n");
+ else if (reply("CONTINUE") == 0)
+ errexit("");
+ return (STOP);
+ }
+ new = (struct dups *)malloc(sizeof(struct dups));
+ if (new == NULL) {
+ pfatal("DUP TABLE OVERFLOW.");
+ if (reply("CONTINUE") == 0)
+ errexit("");
+ return (STOP);
+ }
+ new->dup = blkno;
+ if (muldup == 0) {
+ duplist = muldup = new;
+ new->next = 0;
+ } else {
+ new->next = muldup->next;
+ muldup->next = new;
+ }
+ for (dlp = duplist; dlp != muldup; dlp = dlp->next)
+ if (dlp->dup == blkno)
+ break;
+ if (dlp == muldup && dlp->dup != blkno)
+ muldup = new;
+ }
+ /*
+ * count the number of blocks found in id_entryno
+ */
+ idesc->id_entryno++;
+ }
+ return (res);
+}
diff --git a/bsdfsck/pass1b.c b/bsdfsck/pass1b.c
new file mode 100644
index 00000000..f5aadc06
--- /dev/null
+++ b/bsdfsck/pass1b.c
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 1980, 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. 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.
+ */
+
+#ifndef lint
+/*static char sccsid[] = "from: @(#)pass1b.c 8.1 (Berkeley) 6/5/93";*/
+static char *rcsid = "$Id: pass1b.c,v 1.2 1994/08/23 20:01:24 mib Exp $";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include "../ufs/dinode.h"
+#include "../ufs/fs.h"
+#include <string.h>
+#include "fsck.h"
+
+int pass1bcheck();
+static struct dups *duphead;
+
+pass1b()
+{
+ register int c, i;
+ register struct dinode *dp;
+ struct inodesc idesc;
+ ino_t inumber;
+
+ bzero((char *)&idesc, sizeof(struct inodesc));
+ idesc.id_type = ADDR;
+ idesc.id_func = pass1bcheck;
+ duphead = duplist;
+ inumber = 0;
+ for (c = 0; c < sblock.fs_ncg; c++) {
+ for (i = 0; i < sblock.fs_ipg; i++, inumber++) {
+ if (inumber < ROOTINO)
+ continue;
+ dp = ginode(inumber);
+ if (dp == NULL)
+ continue;
+ idesc.id_number = inumber;
+ if (statemap[inumber] != USTATE &&
+ (ckinode(dp, &idesc) & STOP))
+ return;
+ }
+ }
+}
+
+pass1bcheck(idesc)
+ register struct inodesc *idesc;
+{
+ register struct dups *dlp;
+ int nfrags, res = KEEPON;
+ daddr_t blkno = idesc->id_blkno;
+
+ for (nfrags = idesc->id_numfrags; nfrags > 0; blkno++, nfrags--) {
+ if (chkrange(blkno, 1))
+ res = SKIP;
+ for (dlp = duphead; dlp; dlp = dlp->next) {
+ if (dlp->dup == blkno) {
+ blkerror(idesc->id_number, "DUP", blkno);
+ dlp->dup = duphead->dup;
+ duphead->dup = blkno;
+ duphead = duphead->next;
+ }
+ if (dlp == muldup)
+ break;
+ }
+ if (muldup == 0 || duphead == muldup->next)
+ return (STOP);
+ }
+ return (res);
+}
diff --git a/bsdfsck/pass2.c b/bsdfsck/pass2.c
new file mode 100644
index 00000000..184106c1
--- /dev/null
+++ b/bsdfsck/pass2.c
@@ -0,0 +1,441 @@
+/*
+ * Copyright (c) 1980, 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. 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.
+ */
+
+#ifndef lint
+/*static char sccsid[] = "from: @(#)pass2.c 8.2 (Berkeley) 2/27/94";*/
+static char *rcsid = "$Id: pass2.c,v 1.3 1994/08/24 15:11:56 mib Exp $";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include "../ufs/dinode.h"
+#include "../ufs/dir.h"
+#include "../ufs/fs.h"
+#include <stdlib.h>
+#include <string.h>
+#include "fsck.h"
+
+#define MINDIRSIZE (sizeof (struct dirtemplate))
+
+int pass2check(), blksort();
+
+pass2()
+{
+ register struct dinode *dp;
+ register struct inoinfo **inpp, *inp;
+ struct inoinfo **inpend;
+ struct inodesc curino;
+ struct dinode dino;
+ char pathbuf[MAXPATHLEN + 1];
+
+ switch (statemap[ROOTINO]) {
+
+ case USTATE:
+ pfatal("ROOT INODE UNALLOCATED");
+ if (reply("ALLOCATE") == 0)
+ errexit("");
+ if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO)
+ errexit("CANNOT ALLOCATE ROOT INODE\n");
+ break;
+
+ case DCLEAR:
+ pfatal("DUPS/BAD IN ROOT INODE");
+ if (reply("REALLOCATE")) {
+ freeino(ROOTINO);
+ if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO)
+ errexit("CANNOT ALLOCATE ROOT INODE\n");
+ break;
+ }
+ if (reply("CONTINUE") == 0)
+ errexit("");
+ break;
+
+ case FSTATE:
+ case FCLEAR:
+ pfatal("ROOT INODE NOT DIRECTORY");
+ if (reply("REALLOCATE")) {
+ freeino(ROOTINO);
+ if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO)
+ errexit("CANNOT ALLOCATE ROOT INODE\n");
+ break;
+ }
+ if (reply("FIX") == 0)
+ errexit("");
+ dp = ginode(ROOTINO);
+#if 0
+ dp->di_mode &= ~IFMT;
+ dp->di_mode |= IFDIR;
+#else
+ dp->di_model &= ~IFMT;
+ dp->di_model |= IFDIR;
+#endif
+ inodirty();
+ break;
+
+ case DSTATE:
+ break;
+
+ default:
+ errexit("BAD STATE %d FOR ROOT INODE", statemap[ROOTINO]);
+ }
+ statemap[ROOTINO] = DFOUND;
+ /*
+ * Sort the directory list into disk block order.
+ */
+ qsort((char *)inpsort, (size_t)inplast, sizeof *inpsort, blksort);
+ /*
+ * Check the integrity of each directory.
+ */
+ bzero((char *)&curino, sizeof(struct inodesc));
+ curino.id_type = DATA;
+ curino.id_func = pass2check;
+ dp = &dino;
+ inpend = &inpsort[inplast];
+ for (inpp = inpsort; inpp < inpend; inpp++) {
+ inp = *inpp;
+ if (inp->i_isize == 0)
+ continue;
+ if (inp->i_isize < MINDIRSIZE) {
+ direrror(inp->i_number, "DIRECTORY TOO SHORT");
+ inp->i_isize = roundup(MINDIRSIZE, DIRBLKSIZ);
+ if (reply("FIX") == 1) {
+ dp = ginode(inp->i_number);
+ dp->di_size = inp->i_isize;
+ inodirty();
+ dp = &dino;
+ }
+ } else if ((inp->i_isize & (DIRBLKSIZ - 1)) != 0) {
+ getpathname(pathbuf, inp->i_number, inp->i_number);
+ pwarn("DIRECTORY %s: LENGTH %d NOT MULTIPLE OF %d",
+ pathbuf, inp->i_isize, DIRBLKSIZ);
+ if (preen)
+ printf(" (ADJUSTED)\n");
+ inp->i_isize = roundup(inp->i_isize, DIRBLKSIZ);
+ if (preen || reply("ADJUST") == 1) {
+ dp = ginode(inp->i_number);
+ dp->di_size = roundup(inp->i_isize, DIRBLKSIZ);
+ inodirty();
+ dp = &dino;
+ }
+ }
+ bzero((char *)&dino, sizeof(struct dinode));
+#if 0
+ dino.di_mode = IFDIR;
+#else
+ dino.di_modeh = 0;
+ dino.di_model = IFDIR;
+#endif
+ dp->di_size = inp->i_isize;
+ bcopy((char *)&inp->i_blks[0], (char *)&dp->di_db[0],
+ (size_t)inp->i_numblks);
+ curino.id_number = inp->i_number;
+ curino.id_parent = inp->i_parent;
+ (void)ckinode(dp, &curino);
+ }
+ /*
+ * Now that the parents of all directories have been found,
+ * make another pass to verify the value of `..'
+ */
+ for (inpp = inpsort; inpp < inpend; inpp++) {
+ inp = *inpp;
+ if (inp->i_parent == 0 || inp->i_isize == 0)
+ continue;
+ if (statemap[inp->i_parent] == DFOUND &&
+ statemap[inp->i_number] == DSTATE)
+ statemap[inp->i_number] = DFOUND;
+ if (inp->i_dotdot == inp->i_parent ||
+ inp->i_dotdot == (ino_t)-1)
+ continue;
+ if (inp->i_dotdot == 0) {
+ inp->i_dotdot = inp->i_parent;
+ fileerror(inp->i_parent, inp->i_number, "MISSING '..'");
+ if (reply("FIX") == 0)
+ continue;
+ (void)makeentry(inp->i_number, inp->i_parent, "..");
+ lncntp[inp->i_parent]--;
+ continue;
+ }
+ fileerror(inp->i_parent, inp->i_number,
+ "BAD INODE NUMBER FOR '..'");
+ if (reply("FIX") == 0)
+ continue;
+ lncntp[inp->i_dotdot]++;
+ lncntp[inp->i_parent]--;
+ inp->i_dotdot = inp->i_parent;
+ (void)changeino(inp->i_number, "..", inp->i_parent);
+ }
+ /*
+ * Mark all the directories that can be found from the root.
+ */
+ propagate();
+}
+
+pass2check(idesc)
+ struct inodesc *idesc;
+{
+ register struct direct *dirp = idesc->id_dirp;
+ register struct inoinfo *inp;
+ int n, entrysize, ret = 0;
+ struct dinode *dp;
+ char *errmsg;
+ struct direct proto;
+ char namebuf[MAXPATHLEN + 1];
+ char pathbuf[MAXPATHLEN + 1];
+
+ /*
+ * If converting, set directory entry type.
+ */
+ if (doinglevel2 && dirp->d_ino > 0 && dirp->d_ino < maxino) {
+ dirp->d_type = typemap[dirp->d_ino];
+ ret |= ALTERED;
+ }
+ /*
+ * check for "."
+ */
+ if (idesc->id_entryno != 0)
+ goto chk1;
+ if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") == 0) {
+ if (dirp->d_ino != idesc->id_number) {
+ direrror(idesc->id_number, "BAD INODE NUMBER FOR '.'");
+ dirp->d_ino = idesc->id_number;
+ if (reply("FIX") == 1)
+ ret |= ALTERED;
+ }
+ if (newinofmt && dirp->d_type != DT_DIR) {
+ direrror(idesc->id_number, "BAD TYPE VALUE FOR '.'");
+ dirp->d_type = DT_DIR;
+ if (reply("FIX") == 1)
+ ret |= ALTERED;
+ }
+ goto chk1;
+ }
+ direrror(idesc->id_number, "MISSING '.'");
+ proto.d_ino = idesc->id_number;
+ if (newinofmt)
+ proto.d_type = DT_DIR;
+ else
+ proto.d_type = 0;
+ proto.d_namlen = 1;
+ (void)strcpy(proto.d_name, ".");
+ entrysize = DIRSIZ(0, &proto);
+ if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") != 0) {
+ pfatal("CANNOT FIX, FIRST ENTRY IN DIRECTORY CONTAINS %s\n",
+ dirp->d_name);
+ } else if (dirp->d_reclen < entrysize) {
+ pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '.'\n");
+ } else if (dirp->d_reclen < 2 * entrysize) {
+ proto.d_reclen = dirp->d_reclen;
+ bcopy((char *)&proto, (char *)dirp, (size_t)entrysize);
+ if (reply("FIX") == 1)
+ ret |= ALTERED;
+ } else {
+ n = dirp->d_reclen - entrysize;
+ proto.d_reclen = entrysize;
+ bcopy((char *)&proto, (char *)dirp, (size_t)entrysize);
+ idesc->id_entryno++;
+ lncntp[dirp->d_ino]--;
+ dirp = (struct direct *)((char *)(dirp) + entrysize);
+ bzero((char *)dirp, (size_t)n);
+ dirp->d_reclen = n;
+ if (reply("FIX") == 1)
+ ret |= ALTERED;
+ }
+chk1:
+ if (idesc->id_entryno > 1)
+ goto chk2;
+ inp = getinoinfo(idesc->id_number);
+ proto.d_ino = inp->i_parent;
+ if (newinofmt)
+ proto.d_type = DT_DIR;
+ else
+ proto.d_type = 0;
+ proto.d_namlen = 2;
+ (void)strcpy(proto.d_name, "..");
+ entrysize = DIRSIZ(0, &proto);
+ if (idesc->id_entryno == 0) {
+ n = DIRSIZ(0, dirp);
+ if (dirp->d_reclen < n + entrysize)
+ goto chk2;
+ proto.d_reclen = dirp->d_reclen - n;
+ dirp->d_reclen = n;
+ idesc->id_entryno++;
+ lncntp[dirp->d_ino]--;
+ dirp = (struct direct *)((char *)(dirp) + n);
+ bzero((char *)dirp, (size_t)proto.d_reclen);
+ dirp->d_reclen = proto.d_reclen;
+ }
+ if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") == 0) {
+ inp->i_dotdot = dirp->d_ino;
+ if (newinofmt && dirp->d_type != DT_DIR) {
+ direrror(idesc->id_number, "BAD TYPE VALUE FOR '..'");
+ dirp->d_type = DT_DIR;
+ if (reply("FIX") == 1)
+ ret |= ALTERED;
+ }
+ goto chk2;
+ }
+ if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") != 0) {
+ fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
+ pfatal("CANNOT FIX, SECOND ENTRY IN DIRECTORY CONTAINS %s\n",
+ dirp->d_name);
+ inp->i_dotdot = (ino_t)-1;
+ } else if (dirp->d_reclen < entrysize) {
+ fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
+ pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '..'\n");
+ inp->i_dotdot = (ino_t)-1;
+ } else if (inp->i_parent != 0) {
+ /*
+ * We know the parent, so fix now.
+ */
+ inp->i_dotdot = inp->i_parent;
+ fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
+ proto.d_reclen = dirp->d_reclen;
+ bcopy((char *)&proto, (char *)dirp, (size_t)entrysize);
+ if (reply("FIX") == 1)
+ ret |= ALTERED;
+ }
+ idesc->id_entryno++;
+ if (dirp->d_ino != 0)
+ lncntp[dirp->d_ino]--;
+ return (ret|KEEPON);
+chk2:
+ if (dirp->d_ino == 0)
+ return (ret|KEEPON);
+ if (dirp->d_namlen <= 2 &&
+ dirp->d_name[0] == '.' &&
+ idesc->id_entryno >= 2) {
+ if (dirp->d_namlen == 1) {
+ direrror(idesc->id_number, "EXTRA '.' ENTRY");
+ dirp->d_ino = 0;
+ if (reply("FIX") == 1)
+ ret |= ALTERED;
+ return (KEEPON | ret);
+ }
+ if (dirp->d_name[1] == '.') {
+ direrror(idesc->id_number, "EXTRA '..' ENTRY");
+ dirp->d_ino = 0;
+ if (reply("FIX") == 1)
+ ret |= ALTERED;
+ return (KEEPON | ret);
+ }
+ }
+ idesc->id_entryno++;
+ n = 0;
+ if (dirp->d_ino > maxino) {
+ fileerror(idesc->id_number, dirp->d_ino, "I OUT OF RANGE");
+ n = reply("REMOVE");
+ } else {
+again:
+ switch (statemap[dirp->d_ino]) {
+ case USTATE:
+ if (idesc->id_entryno <= 2)
+ break;
+ fileerror(idesc->id_number, dirp->d_ino, "UNALLOCATED");
+ n = reply("REMOVE");
+ break;
+
+ case DCLEAR:
+ case FCLEAR:
+ if (idesc->id_entryno <= 2)
+ break;
+ if (statemap[dirp->d_ino] == FCLEAR)
+ errmsg = "DUP/BAD";
+ else if (!preen)
+ errmsg = "ZERO LENGTH DIRECTORY";
+ else {
+ n = 1;
+ break;
+ }
+ fileerror(idesc->id_number, dirp->d_ino, errmsg);
+ if ((n = reply("REMOVE")) == 1)
+ break;
+ dp = ginode(dirp->d_ino);
+ statemap[dirp->d_ino] =
+ (DI_MODE(dp) & IFMT) == IFDIR ? DSTATE : FSTATE;
+ lncntp[dirp->d_ino] = dp->di_nlink;
+ goto again;
+
+ case DSTATE:
+ if (statemap[idesc->id_number] == DFOUND)
+ statemap[dirp->d_ino] = DFOUND;
+ /* fall through */
+
+ case DFOUND:
+ inp = getinoinfo(dirp->d_ino);
+ if (inp->i_parent != 0 && idesc->id_entryno > 2) {
+ getpathname(pathbuf, idesc->id_number,
+ idesc->id_number);
+ getpathname(namebuf, dirp->d_ino, dirp->d_ino);
+ pwarn("%s %s %s\n", pathbuf,
+ "IS AN EXTRANEOUS HARD LINK TO DIRECTORY",
+ namebuf);
+ if (preen)
+ printf(" (IGNORED)\n");
+ else if ((n = reply("REMOVE")) == 1)
+ break;
+ }
+ if (idesc->id_entryno > 2)
+ inp->i_parent = idesc->id_number;
+ /* fall through */
+
+ case FSTATE:
+ if (newinofmt && dirp->d_type != typemap[dirp->d_ino]) {
+ fileerror(idesc->id_number, dirp->d_ino,
+ "BAD TYPE VALUE");
+ dirp->d_type = typemap[dirp->d_ino];
+ if (reply("FIX") == 1)
+ ret |= ALTERED;
+ }
+ lncntp[dirp->d_ino]--;
+ break;
+
+ default:
+ errexit("BAD STATE %d FOR INODE I=%d",
+ statemap[dirp->d_ino], dirp->d_ino);
+ }
+ }
+ if (n == 0)
+ return (ret|KEEPON);
+ dirp->d_ino = 0;
+ return (ret|KEEPON|ALTERED);
+}
+
+/*
+ * Routine to sort disk blocks.
+ */
+blksort(inpp1, inpp2)
+ struct inoinfo **inpp1, **inpp2;
+{
+
+ return ((*inpp1)->i_blks[0] - (*inpp2)->i_blks[0]);
+}
diff --git a/bsdfsck/pass3.c b/bsdfsck/pass3.c
new file mode 100644
index 00000000..78fb6a96
--- /dev/null
+++ b/bsdfsck/pass3.c
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 1980, 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. 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.
+ */
+
+#ifndef lint
+/*static char sccsid[] = "from: @(#)pass3.c 8.1 (Berkeley) 6/5/93";*/
+static char *rcsid = "$Id: pass3.c,v 1.2 1994/08/23 20:02:13 mib Exp $";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include "../ufs/dinode.h"
+#include "../ufs/fs.h"
+#include "fsck.h"
+
+pass3()
+{
+ register struct inoinfo **inpp, *inp;
+ ino_t orphan;
+ int loopcnt;
+
+ for (inpp = &inpsort[inplast - 1]; inpp >= inpsort; inpp--) {
+ inp = *inpp;
+ if (inp->i_number == ROOTINO ||
+ !(inp->i_parent == 0 || statemap[inp->i_number] == DSTATE))
+ continue;
+ if (statemap[inp->i_number] == DCLEAR)
+ continue;
+ for (loopcnt = 0; ; loopcnt++) {
+ orphan = inp->i_number;
+ if (inp->i_parent == 0 ||
+ statemap[inp->i_parent] != DSTATE ||
+ loopcnt > numdirs)
+ break;
+ inp = getinoinfo(inp->i_parent);
+ }
+ (void)linkup(orphan, inp->i_dotdot);
+ inp->i_parent = inp->i_dotdot = lfdir;
+ lncntp[lfdir]--;
+ statemap[orphan] = DFOUND;
+ propagate();
+ }
+}
diff --git a/bsdfsck/pass4.c b/bsdfsck/pass4.c
new file mode 100644
index 00000000..449c96ab
--- /dev/null
+++ b/bsdfsck/pass4.c
@@ -0,0 +1,134 @@
+/*
+ * Copyright (c) 1980, 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. 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.
+ */
+
+#ifndef lint
+/*static char sccsid[] = "from: @(#)pass4.c 8.1 (Berkeley) 6/5/93";*/
+static char *rcsid = "$Id: pass4.c,v 1.2 1994/08/23 20:02:28 mib Exp $";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include "../ufs/dinode.h"
+#include "../ufs/fs.h"
+#include <stdlib.h>
+#include <string.h>
+#include "fsck.h"
+
+int pass4check();
+
+pass4()
+{
+ register ino_t inumber;
+ register struct zlncnt *zlnp;
+ struct dinode *dp;
+ struct inodesc idesc;
+ int n;
+
+ bzero((char *)&idesc, sizeof(struct inodesc));
+ idesc.id_type = ADDR;
+ idesc.id_func = pass4check;
+ for (inumber = ROOTINO; inumber <= lastino; inumber++) {
+ idesc.id_number = inumber;
+ switch (statemap[inumber]) {
+
+ case FSTATE:
+ case DFOUND:
+ n = lncntp[inumber];
+ if (n)
+ adjust(&idesc, (short)n);
+ else {
+ for (zlnp = zlnhead; zlnp; zlnp = zlnp->next)
+ if (zlnp->zlncnt == inumber) {
+ zlnp->zlncnt = zlnhead->zlncnt;
+ zlnp = zlnhead;
+ zlnhead = zlnhead->next;
+ free((char *)zlnp);
+ clri(&idesc, "UNREF", 1);
+ break;
+ }
+ }
+ break;
+
+ case DSTATE:
+ clri(&idesc, "UNREF", 1);
+ break;
+
+ case DCLEAR:
+ dp = ginode(inumber);
+ if (dp->di_size == 0) {
+ clri(&idesc, "ZERO LENGTH", 1);
+ break;
+ }
+ /* fall through */
+ case FCLEAR:
+ clri(&idesc, "BAD/DUP", 1);
+ break;
+
+ case USTATE:
+ break;
+
+ default:
+ errexit("BAD STATE %d FOR INODE I=%d",
+ statemap[inumber], inumber);
+ }
+ }
+}
+
+pass4check(idesc)
+ register struct inodesc *idesc;
+{
+ register struct dups *dlp;
+ int nfrags, res = KEEPON;
+ daddr_t blkno = idesc->id_blkno;
+
+ for (nfrags = idesc->id_numfrags; nfrags > 0; blkno++, nfrags--) {
+ if (chkrange(blkno, 1)) {
+ res = SKIP;
+ } else if (testbmap(blkno)) {
+ for (dlp = duplist; dlp; dlp = dlp->next) {
+ if (dlp->dup != blkno)
+ continue;
+ dlp->dup = duplist->dup;
+ dlp = duplist;
+ duplist = duplist->next;
+ free((char *)dlp);
+ break;
+ }
+ if (dlp == 0) {
+ clrbmap(blkno);
+ n_blks--;
+ }
+ }
+ }
+ return (res);
+}
diff --git a/bsdfsck/pass5.c b/bsdfsck/pass5.c
new file mode 100644
index 00000000..11877f9d
--- /dev/null
+++ b/bsdfsck/pass5.c
@@ -0,0 +1,358 @@
+/*
+ * Copyright (c) 1980, 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. 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.
+ */
+
+#ifndef lint
+/*static char sccsid[] = "from: @(#)pass5.c 8.2 (Berkeley) 2/2/94";*/
+static char *rcsid = "$Id: pass5.c,v 1.3 1994/08/26 18:03:07 mib Exp $";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include "../ufs/dinode.h"
+#include "../ufs/fs.h"
+#include <string.h>
+#include "fsck.h"
+
+/* From ../ufs/subr.c: */
+
+/*
+ * Update the frsum fields to reflect addition or deletion
+ * of some frags.
+ */
+void
+ffs_fragacct(fs, fragmap, fraglist, cnt)
+ struct fs *fs;
+ int fragmap;
+ long fraglist[];
+ int cnt;
+{
+ int inblk;
+ register int field, subfield;
+ register 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)))) == 0)
+ continue;
+ field = around[siz];
+ subfield = inside[siz];
+ for (pos = siz; pos <= fs->fs_frag; pos++) {
+ if ((fragmap & field) == subfield) {
+ fraglist[siz] += cnt;
+ pos += siz;
+ field <<= siz;
+ subfield <<= siz;
+ }
+ field <<= 1;
+ subfield <<= 1;
+ }
+ }
+}
+
+
+pass5()
+{
+ int c, blk, frags, basesize, sumsize, mapsize, savednrpos;
+ register struct fs *fs = &sblock;
+ register struct cg *cg = &cgrp;
+ daddr_t dbase, dmax;
+ register daddr_t d;
+ register long i, j;
+ struct csum *cs;
+ struct csum cstotal;
+ struct inodesc idesc[3];
+ char buf[MAXBSIZE];
+ register struct cg *newcg = (struct cg *)buf;
+ struct ocg *ocg = (struct ocg *)buf;
+
+ bzero((char *)newcg, (size_t)fs->fs_cgsize);
+ newcg->cg_niblk = fs->fs_ipg;
+ if (cvtlevel > 3) {
+ if (fs->fs_maxcontig < 2 && fs->fs_contigsumsize > 0) {
+ if (preen)
+ pwarn("DELETING CLUSTERING MAPS\n");
+ if (preen || reply("DELETE CLUSTERING MAPS")) {
+ fs->fs_contigsumsize = 0;
+ doinglevel1 = 1;
+ sbdirty();
+ }
+ }
+ if (fs->fs_maxcontig > 1) {
+ char *doit = 0;
+
+ if (fs->fs_contigsumsize < 1) {
+ doit = "CREAT";
+ } else if (fs->fs_contigsumsize < fs->fs_maxcontig &&
+ fs->fs_contigsumsize < FS_MAXCONTIG) {
+ doit = "EXPAND";
+ }
+ if (doit) {
+ i = fs->fs_contigsumsize;
+ fs->fs_contigsumsize =
+ MIN(fs->fs_maxcontig, FS_MAXCONTIG);
+ if (CGSIZE(fs) > fs->fs_bsize) {
+ pwarn("CANNOT %s CLUSTER MAPS\n", doit);
+ fs->fs_contigsumsize = i;
+ } else if (preen ||
+ reply("CREATE CLUSTER MAPS")) {
+ if (preen)
+ pwarn("%sING CLUSTER MAPS\n",
+ doit);
+ fs->fs_cgsize =
+ fragroundup(fs, CGSIZE(fs));
+ doinglevel1 = 1;
+ sbdirty();
+ }
+ }
+ }
+ }
+ switch ((int)fs->fs_postblformat) {
+
+ case FS_42POSTBLFMT:
+ basesize = (char *)(&ocg->cg_btot[0]) - (char *)(&ocg->cg_link);
+ sumsize = &ocg->cg_iused[0] - (char *)(&ocg->cg_btot[0]);
+ mapsize = &ocg->cg_free[howmany(fs->fs_fpg, NBBY)] -
+ (u_char *)&ocg->cg_iused[0];
+ ocg->cg_magic = CG_MAGIC;
+ savednrpos = fs->fs_nrpos;
+ fs->fs_nrpos = 8;
+ break;
+
+ case FS_DYNAMICPOSTBLFMT:
+ newcg->cg_btotoff =
+ &newcg->cg_space[0] - (u_char *)(&newcg->cg_link);
+ newcg->cg_boff =
+ newcg->cg_btotoff + fs->fs_cpg * sizeof(long);
+ newcg->cg_iusedoff = newcg->cg_boff +
+ fs->fs_cpg * fs->fs_nrpos * sizeof(short);
+ newcg->cg_freeoff =
+ newcg->cg_iusedoff + howmany(fs->fs_ipg, NBBY);
+ if (fs->fs_contigsumsize <= 0) {
+ newcg->cg_nextfreeoff = newcg->cg_freeoff +
+ howmany(fs->fs_cpg * fs->fs_spc / NSPF(fs), NBBY);
+ } else {
+ newcg->cg_clustersumoff = newcg->cg_freeoff +
+ howmany(fs->fs_cpg * fs->fs_spc / NSPF(fs), NBBY) -
+ sizeof(long);
+ newcg->cg_clustersumoff =
+ roundup(newcg->cg_clustersumoff, sizeof(long));
+ newcg->cg_clusteroff = newcg->cg_clustersumoff +
+ (fs->fs_contigsumsize + 1) * sizeof(long);
+ newcg->cg_nextfreeoff = newcg->cg_clusteroff +
+ howmany(fs->fs_cpg * fs->fs_spc / NSPB(fs), NBBY);
+ }
+ newcg->cg_magic = CG_MAGIC;
+ basesize = &newcg->cg_space[0] - (u_char *)(&newcg->cg_link);
+ sumsize = newcg->cg_iusedoff - newcg->cg_btotoff;
+ mapsize = newcg->cg_nextfreeoff - newcg->cg_iusedoff;
+ break;
+
+ default:
+ errexit("UNKNOWN ROTATIONAL TABLE FORMAT %d\n",
+ fs->fs_postblformat);
+ }
+ bzero((char *)&idesc[0], sizeof idesc);
+ for (i = 0; i < 3; i++) {
+ idesc[i].id_type = ADDR;
+ if (doinglevel2)
+ idesc[i].id_fix = FIX;
+ }
+ bzero((char *)&cstotal, sizeof(struct csum));
+ j = blknum(fs, fs->fs_size + fs->fs_frag - 1);
+ for (i = fs->fs_size; i < j; i++)
+ setbmap(i);
+ for (c = 0; c < fs->fs_ncg; c++) {
+ getblk(&cgblk, cgtod(fs, c), fs->fs_cgsize);
+ if (!cg_chkmagic(cg))
+ pfatal("CG %d: BAD MAGIC NUMBER\n", c);
+ dbase = cgbase(fs, c);
+ dmax = dbase + fs->fs_fpg;
+ if (dmax > fs->fs_size)
+ dmax = fs->fs_size;
+ newcg->cg_time = cg->cg_time;
+ newcg->cg_cgx = c;
+ if (c == fs->fs_ncg - 1)
+ newcg->cg_ncyl = fs->fs_ncyl % fs->fs_cpg;
+ else
+ newcg->cg_ncyl = fs->fs_cpg;
+ newcg->cg_ndblk = dmax - dbase;
+ if (fs->fs_contigsumsize > 0)
+ newcg->cg_nclusterblks = newcg->cg_ndblk / fs->fs_frag;
+ newcg->cg_cs.cs_ndir = 0;
+ newcg->cg_cs.cs_nffree = 0;
+ newcg->cg_cs.cs_nbfree = 0;
+ newcg->cg_cs.cs_nifree = fs->fs_ipg;
+ if (cg->cg_rotor < newcg->cg_ndblk)
+ newcg->cg_rotor = cg->cg_rotor;
+ else
+ newcg->cg_rotor = 0;
+ if (cg->cg_frotor < newcg->cg_ndblk)
+ newcg->cg_frotor = cg->cg_frotor;
+ else
+ newcg->cg_frotor = 0;
+ if (cg->cg_irotor < newcg->cg_niblk)
+ newcg->cg_irotor = cg->cg_irotor;
+ else
+ newcg->cg_irotor = 0;
+ bzero((char *)&newcg->cg_frsum[0], sizeof newcg->cg_frsum);
+ bzero((char *)&cg_blktot(newcg)[0],
+ (size_t)(sumsize + mapsize));
+ if (fs->fs_postblformat == FS_42POSTBLFMT)
+ ocg->cg_magic = CG_MAGIC;
+ j = fs->fs_ipg * c;
+ for (i = 0; i < fs->fs_ipg; j++, i++) {
+ switch (statemap[j]) {
+
+ case USTATE:
+ break;
+
+ case DSTATE:
+ case DCLEAR:
+ case DFOUND:
+ newcg->cg_cs.cs_ndir++;
+ /* fall through */
+
+ case FSTATE:
+ case FCLEAR:
+ newcg->cg_cs.cs_nifree--;
+ setbit(cg_inosused(newcg), i);
+ break;
+
+ default:
+ if (j < ROOTINO)
+ break;
+ errexit("BAD STATE %d FOR INODE I=%d",
+ statemap[j], j);
+ }
+ }
+ if (c == 0)
+ for (i = 0; i < ROOTINO; i++) {
+ setbit(cg_inosused(newcg), i);
+ newcg->cg_cs.cs_nifree--;
+ }
+ for (i = 0, d = dbase;
+ d < dmax;
+ d += fs->fs_frag, i += fs->fs_frag) {
+ frags = 0;
+ for (j = 0; j < fs->fs_frag; j++) {
+ if (testbmap(d + j))
+ continue;
+ setbit(cg_blksfree(newcg), i + j);
+ frags++;
+ }
+ if (frags == fs->fs_frag) {
+ newcg->cg_cs.cs_nbfree++;
+ j = cbtocylno(fs, i);
+ cg_blktot(newcg)[j]++;
+ cg_blks(fs, newcg, j)[cbtorpos(fs, i)]++;
+ if (fs->fs_contigsumsize > 0)
+ setbit(cg_clustersfree(newcg),
+ i / fs->fs_frag);
+ } else if (frags > 0) {
+ newcg->cg_cs.cs_nffree += frags;
+ blk = blkmap(fs, cg_blksfree(newcg), i);
+ ffs_fragacct(fs, blk, newcg->cg_frsum, 1);
+ }
+ }
+ if (fs->fs_contigsumsize > 0) {
+ long *sump = cg_clustersum(newcg);
+ u_char *mapp = cg_clustersfree(newcg);
+ int map = *mapp++;
+ int bit = 1;
+ int run = 0;
+
+ for (i = 0; i < newcg->cg_nclusterblks; i++) {
+ if ((map & bit) != 0) {
+ run++;
+ } else if (run != 0) {
+ if (run > fs->fs_contigsumsize)
+ run = fs->fs_contigsumsize;
+ sump[run]++;
+ run = 0;
+ }
+ if ((i & (NBBY - 1)) != (NBBY - 1)) {
+ bit <<= 1;
+ } else {
+ map = *mapp++;
+ bit = 1;
+ }
+ }
+ if (run != 0) {
+ if (run > fs->fs_contigsumsize)
+ run = fs->fs_contigsumsize;
+ sump[run]++;
+ }
+ }
+ cstotal.cs_nffree += newcg->cg_cs.cs_nffree;
+ cstotal.cs_nbfree += newcg->cg_cs.cs_nbfree;
+ cstotal.cs_nifree += newcg->cg_cs.cs_nifree;
+ cstotal.cs_ndir += newcg->cg_cs.cs_ndir;
+ cs = &fs->fs_cs(fs, c);
+ if (bcmp((char *)&newcg->cg_cs, (char *)cs, sizeof *cs) != 0 &&
+ dofix(&idesc[0], "FREE BLK COUNT(S) WRONG IN SUPERBLK")) {
+ bcopy((char *)&newcg->cg_cs, (char *)cs, sizeof *cs);
+ sbdirty();
+ }
+ if (doinglevel1) {
+ bcopy((char *)newcg, (char *)cg, (size_t)fs->fs_cgsize);
+ cgdirty();
+ continue;
+ }
+ if (bcmp(cg_inosused(newcg),
+ cg_inosused(cg), mapsize) != 0 &&
+ dofix(&idesc[1], "BLK(S) MISSING IN BIT MAPS")) {
+ bcopy(cg_inosused(newcg), cg_inosused(cg),
+ (size_t)mapsize);
+ cgdirty();
+ }
+ if ((bcmp((char *)newcg, (char *)cg, basesize) != 0 ||
+ bcmp((char *)&cg_blktot(newcg)[0],
+ (char *)&cg_blktot(cg)[0], sumsize) != 0) &&
+ dofix(&idesc[2], "SUMMARY INFORMATION BAD")) {
+ bcopy((char *)newcg, (char *)cg, (size_t)basesize);
+ bcopy((char *)&cg_blktot(newcg)[0],
+ (char *)&cg_blktot(cg)[0], (size_t)sumsize);
+ cgdirty();
+ }
+ }
+ if (fs->fs_postblformat == FS_42POSTBLFMT)
+ fs->fs_nrpos = savednrpos;
+ if (bcmp((char *)&cstotal, (char *)&fs->fs_cstotal, sizeof *cs) != 0
+ && dofix(&idesc[0], "FREE BLK COUNT(S) WRONG IN SUPERBLK")) {
+ bcopy((char *)&cstotal, (char *)&fs->fs_cstotal, sizeof *cs);
+ fs->fs_ronly = 0;
+ fs->fs_fmod = 0;
+ sbdirty();
+ }
+}
diff --git a/bsdfsck/preen.c b/bsdfsck/preen.c
new file mode 100644
index 00000000..7893a5e1
--- /dev/null
+++ b/bsdfsck/preen.c
@@ -0,0 +1,355 @@
+/*
+ * 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. 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.
+ */
+
+#ifndef lint
+/*static char sccsid[] = "from: @(#)preen.c 8.1 (Berkeley) 6/5/93";*/
+static char *rcsid = "$Id: preen.c,v 1.1 1994/08/23 19:29:25 mib Exp $";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <fstab.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+char *rawname(), *unrawname(), *blockcheck();
+
+struct part {
+ struct part *next; /* forward link of partitions on disk */
+ char *name; /* device name */
+ char *fsname; /* mounted filesystem name */
+ long auxdata; /* auxillary data for application */
+} *badlist, **badnext = &badlist;
+
+struct disk {
+ char *name; /* disk base name */
+ struct disk *next; /* forward link for list of disks */
+ struct part *part; /* head of list of partitions on disk */
+ int pid; /* If != 0, pid of proc working on */
+} *disks;
+
+int nrun, ndisks;
+char hotroot;
+
+checkfstab(preen, maxrun, docheck, chkit)
+ int preen, maxrun;
+ int (*docheck)(), (*chkit)();
+{
+ register struct fstab *fsp;
+ register struct disk *dk, *nextdisk;
+ register struct part *pt;
+ int ret, pid, retcode, passno, sumstatus, status;
+ long auxdata;
+ char *name;
+
+ sumstatus = 0;
+ for (passno = 1; passno <= 2; passno++) {
+ if (setfsent() == 0) {
+ fprintf(stderr, "Can't open checklist file: %s\n",
+ _PATH_FSTAB);
+ return (8);
+ }
+ while ((fsp = getfsent()) != 0) {
+ if ((auxdata = (*docheck)(fsp)) == 0)
+ continue;
+ if (preen == 0 || passno == 1 && fsp->fs_passno == 1) {
+ if (name = blockcheck(fsp->fs_spec)) {
+ if (sumstatus = (*chkit)(name,
+ fsp->fs_file, auxdata, 0))
+ return (sumstatus);
+ } else if (preen)
+ return (8);
+ } else if (passno == 2 && fsp->fs_passno > 1) {
+ if ((name = blockcheck(fsp->fs_spec)) == NULL) {
+ fprintf(stderr, "BAD DISK NAME %s\n",
+ fsp->fs_spec);
+ sumstatus |= 8;
+ continue;
+ }
+ addpart(name, fsp->fs_file, auxdata);
+ }
+ }
+ if (preen == 0)
+ return (0);
+ }
+ if (preen) {
+ if (maxrun == 0)
+ maxrun = ndisks;
+ if (maxrun > ndisks)
+ maxrun = ndisks;
+ nextdisk = disks;
+ for (passno = 0; passno < maxrun; ++passno) {
+ while (ret = startdisk(nextdisk, chkit) && nrun > 0)
+ sleep(10);
+ if (ret)
+ return (ret);
+ nextdisk = nextdisk->next;
+ }
+ while ((pid = wait(&status)) != -1) {
+ for (dk = disks; dk; dk = dk->next)
+ if (dk->pid == pid)
+ break;
+ if (dk == 0) {
+ printf("Unknown pid %d\n", pid);
+ continue;
+ }
+ if (WIFEXITED(status))
+ retcode = WEXITSTATUS(status);
+ else
+ retcode = 0;
+ if (WIFSIGNALED(status)) {
+ printf("%s (%s): EXITED WITH SIGNAL %d\n",
+ dk->part->name, dk->part->fsname,
+ WTERMSIG(status));
+ retcode = 8;
+ }
+ if (retcode != 0) {
+ sumstatus |= retcode;
+ *badnext = dk->part;
+ badnext = &dk->part->next;
+ dk->part = dk->part->next;
+ *badnext = NULL;
+ } else
+ dk->part = dk->part->next;
+ dk->pid = 0;
+ nrun--;
+ if (dk->part == NULL)
+ ndisks--;
+
+ if (nextdisk == NULL) {
+ if (dk->part) {
+ while (ret = startdisk(dk, chkit) &&
+ nrun > 0)
+ sleep(10);
+ if (ret)
+ return (ret);
+ }
+ } else if (nrun < maxrun && nrun < ndisks) {
+ for ( ;; ) {
+ if ((nextdisk = nextdisk->next) == NULL)
+ nextdisk = disks;
+ if (nextdisk->part != NULL &&
+ nextdisk->pid == 0)
+ break;
+ }
+ while (ret = startdisk(nextdisk, chkit) &&
+ nrun > 0)
+ sleep(10);
+ if (ret)
+ return (ret);
+ }
+ }
+ }
+ if (sumstatus) {
+ if (badlist == 0)
+ return (sumstatus);
+ fprintf(stderr, "THE FOLLOWING FILE SYSTEM%s HAD AN %s\n\t",
+ badlist->next ? "S" : "", "UNEXPECTED INCONSISTENCY:");
+ for (pt = badlist; pt; pt = pt->next)
+ fprintf(stderr, "%s (%s)%s", pt->name, pt->fsname,
+ pt->next ? ", " : "\n");
+ return (sumstatus);
+ }
+ (void)endfsent();
+ return (0);
+}
+
+struct disk *
+finddisk(name)
+ char *name;
+{
+ register struct disk *dk, **dkp;
+ register char *p;
+ size_t len;
+
+ for (p = name + strlen(name) - 1; p >= name; --p)
+ if (isdigit(*p)) {
+ len = p - name + 1;
+ break;
+ }
+ if (p < name)
+ len = strlen(name);
+
+ for (dk = disks, dkp = &disks; dk; dkp = &dk->next, dk = dk->next) {
+ if (strncmp(dk->name, name, len) == 0 &&
+ dk->name[len] == 0)
+ return (dk);
+ }
+ if ((*dkp = (struct disk *)malloc(sizeof(struct disk))) == NULL) {
+ fprintf(stderr, "out of memory");
+ exit (8);
+ }
+ dk = *dkp;
+ if ((dk->name = malloc(len + 1)) == NULL) {
+ fprintf(stderr, "out of memory");
+ exit (8);
+ }
+ (void)strncpy(dk->name, name, len);
+ dk->name[len] = '\0';
+ dk->part = NULL;
+ dk->next = NULL;
+ dk->pid = 0;
+ ndisks++;
+ return (dk);
+}
+
+addpart(name, fsname, auxdata)
+ char *name, *fsname;
+ long auxdata;
+{
+ struct disk *dk = finddisk(name);
+ register struct part *pt, **ppt = &dk->part;
+
+ for (pt = dk->part; pt; ppt = &pt->next, pt = pt->next)
+ if (strcmp(pt->name, name) == 0) {
+ printf("%s in fstab more than once!\n", name);
+ return;
+ }
+ if ((*ppt = (struct part *)malloc(sizeof(struct part))) == NULL) {
+ fprintf(stderr, "out of memory");
+ exit (8);
+ }
+ pt = *ppt;
+ if ((pt->name = malloc(strlen(name) + 1)) == NULL) {
+ fprintf(stderr, "out of memory");
+ exit (8);
+ }
+ (void)strcpy(pt->name, name);
+ if ((pt->fsname = malloc(strlen(fsname) + 1)) == NULL) {
+ fprintf(stderr, "out of memory");
+ exit (8);
+ }
+ (void)strcpy(pt->fsname, fsname);
+ pt->next = NULL;
+ pt->auxdata = auxdata;
+}
+
+startdisk(dk, checkit)
+ register struct disk *dk;
+ int (*checkit)();
+{
+ register struct part *pt = dk->part;
+
+ dk->pid = fork();
+ if (dk->pid < 0) {
+ perror("fork");
+ return (8);
+ }
+ if (dk->pid == 0)
+ exit((*checkit)(pt->name, pt->fsname, pt->auxdata, 1));
+ nrun++;
+ return (0);
+}
+
+char *
+blockcheck(name)
+ char *name;
+{
+ struct stat stslash, stblock, stchar;
+ char *raw;
+ int retried = 0;
+
+ hotroot = 0;
+ if (stat("/", &stslash) < 0) {
+ perror("/");
+ printf("Can't stat root\n");
+ return (0);
+ }
+retry:
+ if (stat(name, &stblock) < 0) {
+ perror(name);
+ printf("Can't stat %s\n", name);
+ return (0);
+ }
+ if ((stblock.st_mode & S_IFMT) == S_IFBLK) {
+ if (stslash.st_dev == stblock.st_rdev)
+ hotroot++;
+ raw = rawname(name);
+ if (stat(raw, &stchar) < 0) {
+ perror(raw);
+ printf("Can't stat %s\n", raw);
+ return (name);
+ }
+ if ((stchar.st_mode & S_IFMT) == S_IFCHR) {
+ return (raw);
+ } else {
+ printf("%s is not a character device\n", raw);
+ return (name);
+ }
+ } else if ((stblock.st_mode & S_IFMT) == S_IFCHR && !retried) {
+ name = unrawname(name);
+ retried++;
+ goto retry;
+ }
+ printf("Can't make sense out of name %s\n", name);
+ return (0);
+}
+
+char *
+unrawname(name)
+ char *name;
+{
+ char *dp;
+ struct stat stb;
+
+ if ((dp = rindex(name, '/')) == 0)
+ return (name);
+ if (stat(name, &stb) < 0)
+ return (name);
+ if ((stb.st_mode & S_IFMT) != S_IFCHR)
+ return (name);
+ if (dp[1] != 'r')
+ return (name);
+ (void)strcpy(&dp[1], &dp[2]);
+ return (name);
+}
+
+char *
+rawname(name)
+ char *name;
+{
+ static char rawbuf[32];
+ char *dp;
+
+ if ((dp = rindex(name, '/')) == 0)
+ return (0);
+ *dp = 0;
+ (void)strcpy(rawbuf, name);
+ *dp = '/';
+ (void)strcat(rawbuf, "/r");
+ (void)strcat(rawbuf, &dp[1]);
+ return (rawbuf);
+}
diff --git a/bsdfsck/setup.c b/bsdfsck/setup.c
new file mode 100644
index 00000000..f65ade47
--- /dev/null
+++ b/bsdfsck/setup.c
@@ -0,0 +1,480 @@
+/*
+ * Copyright (c) 1980, 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. 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.
+ */
+
+#ifndef lint
+/*static char sccsid[] = "from: @(#)setup.c 8.2 (Berkeley) 2/21/94";*/
+static char *rcsid = "$Id: setup.c,v 1.3 1994/08/25 15:22:35 mib Exp $";
+#endif /* not lint */
+
+#define DKTYPENAMES
+#include <sys/param.h>
+#include <sys/time.h>
+#include "../ufs/dinode.h"
+#include "../ufs/fs.h"
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+/* #include <sys/disklabel.h> */
+#include <sys/file.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include "fsck.h"
+
+struct bufarea asblk;
+#define altsblock (*asblk.b_un.b_fs)
+#define POWEROF2(num) (((num) & ((num) - 1)) == 0)
+
+struct disklabel *getdisklabel();
+
+setup(dev)
+ char *dev;
+{
+ long cg, size, asked, i, j;
+ long bmapsize;
+/* struct disklabel *lp; */
+ off_t sizepb;
+ struct stat statb;
+ struct fs proto;
+
+ havesb = 0;
+ fswritefd = -1;
+ if (stat(dev, &statb) < 0) {
+ printf("Can't stat %s: %s\n", dev, strerror(errno));
+ return (0);
+ }
+ if ((statb.st_mode & S_IFMT) != S_IFCHR) {
+ pfatal("%s is not a character device", dev);
+ if (reply("CONTINUE") == 0)
+ return (0);
+ }
+ if ((fsreadfd = open(dev, O_RDONLY)) < 0) {
+ printf("Can't open %s: %s\n", dev, strerror(errno));
+ return (0);
+ }
+ if (preen == 0)
+ printf("** %s", dev);
+ if (nflag || (fswritefd = open(dev, O_WRONLY)) < 0) {
+ fswritefd = -1;
+ if (preen)
+ pfatal("NO WRITE ACCESS");
+ printf(" (NO WRITE)");
+ }
+ if (preen == 0)
+ printf("\n");
+ fsmodified = 0;
+ lfdir = 0;
+ initbarea(&sblk);
+ initbarea(&asblk);
+ sblk.b_un.b_buf = malloc(SBSIZE);
+ asblk.b_un.b_buf = malloc(SBSIZE);
+ if (sblk.b_un.b_buf == NULL || asblk.b_un.b_buf == NULL)
+ errexit("cannot allocate space for superblock\n");
+#if 0
+ if (lp = getdisklabel((char *)NULL, fsreadfd))
+ dev_bsize = secsize = lp->d_secsize;
+ else
+#endif
+ dev_bsize = secsize = DEV_BSIZE;
+ /*
+ * Read in the superblock, looking for alternates if necessary
+ */
+ if (readsb(1) == 0) {
+ if (bflag || preen || calcsb(dev, fsreadfd, &proto) == 0)
+ return(0);
+ if (reply("LOOK FOR ALTERNATE SUPERBLOCKS") == 0)
+ return (0);
+ for (cg = 0; cg < proto.fs_ncg; cg++) {
+ bflag = fsbtodb(&proto, cgsblock(&proto, cg));
+ if (readsb(0) != 0)
+ break;
+ }
+ if (cg >= proto.fs_ncg) {
+ printf("%s %s\n%s %s\n%s %s\n",
+ "SEARCH FOR ALTERNATE SUPER-BLOCK",
+ "FAILED. YOU MUST USE THE",
+ "-b OPTION TO FSCK TO SPECIFY THE",
+ "LOCATION OF AN ALTERNATE",
+ "SUPER-BLOCK TO SUPPLY NEEDED",
+ "INFORMATION; SEE fsck(8).");
+ return(0);
+ }
+ pwarn("USING ALTERNATE SUPERBLOCK AT %d\n", bflag);
+ }
+ maxfsblock = sblock.fs_size;
+ maxino = sblock.fs_ncg * sblock.fs_ipg;
+ /*
+ * Check and potentially fix certain fields in the super block.
+ */
+ if (sblock.fs_optim != FS_OPTTIME && sblock.fs_optim != FS_OPTSPACE) {
+ pfatal("UNDEFINED OPTIMIZATION IN SUPERBLOCK");
+ if (reply("SET TO DEFAULT") == 1) {
+ sblock.fs_optim = FS_OPTTIME;
+ sbdirty();
+ }
+ }
+ if ((sblock.fs_minfree < 0 || sblock.fs_minfree > 99)) {
+ pfatal("IMPOSSIBLE MINFREE=%d IN SUPERBLOCK",
+ sblock.fs_minfree);
+ if (reply("SET TO DEFAULT") == 1) {
+ sblock.fs_minfree = 10;
+ sbdirty();
+ }
+ }
+ if (sblock.fs_interleave < 1 ||
+ sblock.fs_interleave > sblock.fs_nsect) {
+ pwarn("IMPOSSIBLE INTERLEAVE=%d IN SUPERBLOCK",
+ sblock.fs_interleave);
+ sblock.fs_interleave = 1;
+ if (preen)
+ printf(" (FIXED)\n");
+ if (preen || reply("SET TO DEFAULT") == 1) {
+ sbdirty();
+ dirty(&asblk);
+ }
+ }
+ if (sblock.fs_npsect < sblock.fs_nsect ||
+ sblock.fs_npsect > sblock.fs_nsect*2) {
+ pwarn("IMPOSSIBLE NPSECT=%d IN SUPERBLOCK",
+ sblock.fs_npsect);
+ sblock.fs_npsect = sblock.fs_nsect;
+ if (preen)
+ printf(" (FIXED)\n");
+ if (preen || reply("SET TO DEFAULT") == 1) {
+ sbdirty();
+ dirty(&asblk);
+ }
+ }
+ if (sblock.fs_inodefmt >= FS_44INODEFMT) {
+ newinofmt = 1;
+ } else {
+ sblock.fs_qbmask = ~sblock.fs_bmask;
+ sblock.fs_qfmask = ~sblock.fs_fmask;
+ newinofmt = 0;
+ }
+ /*
+ * Convert to new inode format.
+ */
+ if (cvtlevel >= 2 && sblock.fs_inodefmt < FS_44INODEFMT) {
+ if (preen)
+ pwarn("CONVERTING TO NEW INODE FORMAT\n");
+ else if (!reply("CONVERT TO NEW INODE FORMAT"))
+ return(0);
+ doinglevel2++;
+ sblock.fs_inodefmt = FS_44INODEFMT;
+ sizepb = sblock.fs_bsize;
+ sblock.fs_maxfilesize = sblock.fs_bsize * NDADDR - 1;
+ for (i = 0; i < NIADDR; i++) {
+ sizepb *= NINDIR(&sblock);
+ sblock.fs_maxfilesize += sizepb;
+ }
+ sblock.fs_maxsymlinklen = MAXSYMLINKLEN;
+ sblock.fs_qbmask = ~sblock.fs_bmask;
+ sblock.fs_qfmask = ~sblock.fs_fmask;
+ sbdirty();
+ dirty(&asblk);
+ }
+ /*
+ * Convert to new cylinder group format.
+ */
+ if (cvtlevel >= 1 && sblock.fs_postblformat == FS_42POSTBLFMT) {
+ if (preen)
+ pwarn("CONVERTING TO NEW CYLINDER GROUP FORMAT\n");
+ else if (!reply("CONVERT TO NEW CYLINDER GROUP FORMAT"))
+ return(0);
+ doinglevel1++;
+ sblock.fs_postblformat = FS_DYNAMICPOSTBLFMT;
+ sblock.fs_nrpos = 8;
+ sblock.fs_postbloff =
+ (char *)(&sblock.fs_opostbl[0][0]) -
+ (char *)(&sblock.fs_link);
+ sblock.fs_rotbloff = &sblock.fs_space[0] -
+ (u_char *)(&sblock.fs_link);
+ sblock.fs_cgsize =
+ fragroundup(&sblock, CGSIZE(&sblock));
+ sbdirty();
+ dirty(&asblk);
+ }
+ if (asblk.b_dirty && !bflag) {
+ bcopy((char *)&sblock, (char *)&altsblock,
+ (size_t)sblock.fs_sbsize);
+ flush(fswritefd, &asblk);
+ }
+ /*
+ * read in the summary info.
+ */
+ asked = 0;
+ for (i = 0, j = 0; i < sblock.fs_cssize; i += sblock.fs_bsize, j++) {
+ size = sblock.fs_cssize - i < sblock.fs_bsize ?
+ sblock.fs_cssize - i : sblock.fs_bsize;
+ sblock.fs_csp[j] = (struct csum *)calloc(1, (unsigned)size);
+ if (bread(fsreadfd, (char *)sblock.fs_csp[j],
+ fsbtodb(&sblock, sblock.fs_csaddr + j * sblock.fs_frag),
+ size) != 0 && !asked) {
+ pfatal("BAD SUMMARY INFORMATION");
+ if (reply("CONTINUE") == 0)
+ errexit("");
+ asked++;
+ }
+ }
+ /*
+ * allocate and initialize the necessary maps
+ */
+ bmapsize = roundup(howmany(maxfsblock, NBBY), sizeof(short));
+ blockmap = calloc((unsigned)bmapsize, sizeof (char));
+ if (blockmap == NULL) {
+ printf("cannot alloc %u bytes for blockmap\n",
+ (unsigned)bmapsize);
+ goto badsb;
+ }
+ statemap = calloc((unsigned)(maxino + 1), sizeof(char));
+ if (statemap == NULL) {
+ printf("cannot alloc %u bytes for statemap\n",
+ (unsigned)(maxino + 1));
+ goto badsb;
+ }
+ typemap = calloc((unsigned)(maxino + 1), sizeof(char));
+ if (typemap == NULL) {
+ printf("cannot alloc %u bytes for typemap\n",
+ (unsigned)(maxino + 1));
+ goto badsb;
+ }
+ lncntp = (short *)calloc((unsigned)(maxino + 1), sizeof(short));
+ if (lncntp == NULL) {
+ printf("cannot alloc %u bytes for lncntp\n",
+ (unsigned)(maxino + 1) * sizeof(short));
+ goto badsb;
+ }
+ numdirs = sblock.fs_cstotal.cs_ndir;
+ inplast = 0;
+ listmax = numdirs + 10;
+ inpsort = (struct inoinfo **)calloc((unsigned)listmax,
+ sizeof(struct inoinfo *));
+ inphead = (struct inoinfo **)calloc((unsigned)numdirs,
+ sizeof(struct inoinfo *));
+ if (inpsort == NULL || inphead == NULL) {
+ printf("cannot alloc %u bytes for inphead\n",
+ (unsigned)numdirs * sizeof(struct inoinfo *));
+ goto badsb;
+ }
+ bufinit();
+ return (1);
+
+badsb:
+ ckfini();
+ return (0);
+}
+
+/*
+ * Read in the super block and its summary info.
+ */
+readsb(listerr)
+ int listerr;
+{
+ daddr_t super = bflag ? bflag : SBOFF / dev_bsize;
+
+ if (bread(fsreadfd, (char *)&sblock, super, (long)SBSIZE) != 0)
+ return (0);
+ sblk.b_bno = super;
+ sblk.b_size = SBSIZE;
+ /*
+ * run a few consistency checks of the super block
+ */
+ if (sblock.fs_magic != FS_MAGIC)
+ { badsb(listerr, "MAGIC NUMBER WRONG"); return (0); }
+ if (sblock.fs_ncg < 1)
+ { badsb(listerr, "NCG OUT OF RANGE"); return (0); }
+ if (sblock.fs_cpg < 1)
+ { badsb(listerr, "CPG OUT OF RANGE"); return (0); }
+ if (sblock.fs_ncg * sblock.fs_cpg < sblock.fs_ncyl ||
+ (sblock.fs_ncg - 1) * sblock.fs_cpg >= sblock.fs_ncyl)
+ { badsb(listerr, "NCYL LESS THAN NCG*CPG"); return (0); }
+ if (sblock.fs_sbsize > SBSIZE)
+ { badsb(listerr, "SIZE PREPOSTEROUSLY LARGE"); return (0); }
+ /*
+ * Compute block size that the filesystem is based on,
+ * according to fsbtodb, and adjust superblock block number
+ * so we can tell if this is an alternate later.
+ */
+ super *= dev_bsize;
+ dev_bsize = sblock.fs_fsize / fsbtodb(&sblock, 1);
+ sblk.b_bno = super / dev_bsize;
+ if (bflag) {
+ havesb = 1;
+ return (1);
+ }
+ /*
+ * Set all possible fields that could differ, then do check
+ * of whole super block against an alternate super block.
+ * When an alternate super-block is specified this check is skipped.
+ */
+ getblk(&asblk, cgsblock(&sblock, sblock.fs_ncg - 1), sblock.fs_sbsize);
+ if (asblk.b_errs)
+ return (0);
+ altsblock.fs_link = sblock.fs_link;
+ altsblock.fs_rlink = sblock.fs_rlink;
+ altsblock.fs_time = sblock.fs_time;
+ altsblock.fs_cstotal = sblock.fs_cstotal;
+ altsblock.fs_cgrotor = sblock.fs_cgrotor;
+ altsblock.fs_fmod = sblock.fs_fmod;
+ altsblock.fs_clean = sblock.fs_clean;
+ altsblock.fs_ronly = sblock.fs_ronly;
+ altsblock.fs_flags = sblock.fs_flags;
+ altsblock.fs_maxcontig = sblock.fs_maxcontig;
+ altsblock.fs_minfree = sblock.fs_minfree;
+ altsblock.fs_optim = sblock.fs_optim;
+ altsblock.fs_rotdelay = sblock.fs_rotdelay;
+ altsblock.fs_maxbpg = sblock.fs_maxbpg;
+ bcopy((char *)sblock.fs_csp, (char *)altsblock.fs_csp,
+ sizeof sblock.fs_csp);
+ bcopy((char *)sblock.fs_fsmnt, (char *)altsblock.fs_fsmnt,
+ sizeof sblock.fs_fsmnt);
+ bcopy((char *)sblock.fs_sparecon, (char *)altsblock.fs_sparecon,
+ sizeof sblock.fs_sparecon);
+ /*
+ * The following should not have to be copied.
+ */
+ altsblock.fs_fsbtodb = sblock.fs_fsbtodb;
+ altsblock.fs_interleave = sblock.fs_interleave;
+ altsblock.fs_npsect = sblock.fs_npsect;
+ altsblock.fs_nrpos = sblock.fs_nrpos;
+ altsblock.fs_qbmask = sblock.fs_qbmask;
+ altsblock.fs_qfmask = sblock.fs_qfmask;
+ altsblock.fs_state = sblock.fs_state;
+ altsblock.fs_maxfilesize = sblock.fs_maxfilesize;
+ if (bcmp((char *)&sblock, (char *)&altsblock, (int)sblock.fs_sbsize)) {
+ badsb(listerr,
+ "VALUES IN SUPER BLOCK DISAGREE WITH THOSE IN FIRST ALTERNATE");
+ return (0);
+ }
+ havesb = 1;
+ return (1);
+}
+
+badsb(listerr, s)
+ int listerr;
+ char *s;
+{
+
+ if (!listerr)
+ return;
+ if (preen)
+ printf("%s: ", cdevname);
+ pfatal("BAD SUPER BLOCK: %s\n", s);
+}
+
+/* XXX */
+calcsb (dev, devfd, fs)
+ char *dev;
+ int devfd;
+ struct fs *fs;
+{
+ return 0;
+}
+
+#if 0
+/*
+ * Calculate a prototype superblock based on information in the disk label.
+ * When done the cgsblock macro can be calculated and the fs_ncg field
+ * can be used. Do NOT attempt to use other macros without verifying that
+ * their needed information is available!
+ */
+calcsb(dev, devfd, fs)
+ char *dev;
+ int devfd;
+ register struct fs *fs;
+{
+ register struct disklabel *lp;
+ register struct partition *pp;
+ register char *cp;
+ int i;
+
+ cp = index(dev, '\0') - 1;
+ if (cp == (char *)-1 || (*cp < 'a' || *cp > 'h') && !isdigit(*cp)) {
+ pfatal("%s: CANNOT FIGURE OUT FILE SYSTEM PARTITION\n", dev);
+ return (0);
+ }
+ lp = getdisklabel(dev, devfd);
+ if (isdigit(*cp))
+ pp = &lp->d_partitions[0];
+ else
+ pp = &lp->d_partitions[*cp - 'a'];
+ if (pp->p_fstype != FS_BSDFFS) {
+ pfatal("%s: NOT LABELED AS A BSD FILE SYSTEM (%s)\n",
+ dev, pp->p_fstype < FSMAXTYPES ?
+ fstypenames[pp->p_fstype] : "unknown");
+ return (0);
+ }
+ bzero((char *)fs, sizeof(struct fs));
+ fs->fs_fsize = pp->p_fsize;
+ fs->fs_frag = pp->p_frag;
+ fs->fs_cpg = pp->p_cpg;
+ fs->fs_size = pp->p_size;
+ fs->fs_ntrak = lp->d_ntracks;
+ fs->fs_nsect = lp->d_nsectors;
+ fs->fs_spc = lp->d_secpercyl;
+ fs->fs_nspf = fs->fs_fsize / lp->d_secsize;
+ fs->fs_sblkno = roundup(
+ howmany(lp->d_bbsize + lp->d_sbsize, fs->fs_fsize),
+ fs->fs_frag);
+ fs->fs_cgmask = 0xffffffff;
+ for (i = fs->fs_ntrak; i > 1; i >>= 1)
+ fs->fs_cgmask <<= 1;
+ if (!POWEROF2(fs->fs_ntrak))
+ fs->fs_cgmask <<= 1;
+ fs->fs_cgoffset = roundup(
+ howmany(fs->fs_nsect, NSPF(fs)), fs->fs_frag);
+ fs->fs_fpg = (fs->fs_cpg * fs->fs_spc) / NSPF(fs);
+ fs->fs_ncg = howmany(fs->fs_size / fs->fs_spc, fs->fs_cpg);
+ for (fs->fs_fsbtodb = 0, i = NSPF(fs); i > 1; i >>= 1)
+ fs->fs_fsbtodb++;
+ dev_bsize = lp->d_secsize;
+ return (1);
+}
+
+struct disklabel *
+getdisklabel(s, fd)
+ char *s;
+ int fd;
+{
+ static struct disklabel lab;
+
+ if (ioctl(fd, DIOCGDINFO, (char *)&lab) < 0) {
+ if (s == NULL)
+ return ((struct disklabel *)NULL);
+ pwarn("ioctl (GCINFO): %s\n", strerror(errno));
+ errexit("%s: can't read disk label\n", s);
+ }
+ return (&lab);
+}
+#endif
diff --git a/bsdfsck/utilities.c b/bsdfsck/utilities.c
new file mode 100644
index 00000000..2141e7f8
--- /dev/null
+++ b/bsdfsck/utilities.c
@@ -0,0 +1,567 @@
+/*
+ * Copyright (c) 1980, 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. 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.
+ */
+
+#ifndef lint
+/*static char sccsid[] = "from: @(#)utilities.c 8.1 (Berkeley) 6/5/93";*/
+static char *rcsid = "$Id: utilities.c,v 1.2 1994/08/23 20:18:15 mib Exp $";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include "../ufs/dinode.h"
+#include "../ufs/dir.h"
+#include "../ufs/fs.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include "fsck.h"
+
+long diskreads, totalreads; /* Disk cache statistics */
+
+ftypeok(dp)
+ struct dinode *dp;
+{
+ switch (DI_MODE(dp) & IFMT) {
+
+ case IFDIR:
+ case IFREG:
+ case IFBLK:
+ case IFCHR:
+ case IFLNK:
+ case IFSOCK:
+ case IFIFO:
+ return (1);
+
+ default:
+ if (debug)
+ printf("bad file type 0%o\n", DI_MODE(dp));
+ return (0);
+ }
+}
+
+reply(question)
+ char *question;
+{
+ int persevere;
+ char c;
+
+ if (preen)
+ pfatal("INTERNAL ERROR: GOT TO reply()");
+ persevere = !strcmp(question, "CONTINUE");
+ printf("\n");
+ if (!persevere && (nflag || fswritefd < 0)) {
+ printf("%s? no\n\n", question);
+ return (0);
+ }
+ if (yflag || (persevere && nflag)) {
+ printf("%s? yes\n\n", question);
+ return (1);
+ }
+ do {
+ printf("%s? [yn] ", question);
+ (void) fflush(stdout);
+ c = getc(stdin);
+ while (c != '\n' && getc(stdin) != '\n')
+ if (feof(stdin))
+ return (0);
+ } while (c != 'y' && c != 'Y' && c != 'n' && c != 'N');
+ printf("\n");
+ if (c == 'y' || c == 'Y')
+ return (1);
+ return (0);
+}
+
+/*
+ * Malloc buffers and set up cache.
+ */
+bufinit()
+{
+ register struct bufarea *bp;
+ long bufcnt, i;
+ char *bufp;
+
+ pbp = pdirbp = (struct bufarea *)0;
+ bufp = malloc((unsigned int)sblock.fs_bsize);
+ if (bufp == 0)
+ errexit("cannot allocate buffer pool\n");
+ cgblk.b_un.b_buf = bufp;
+ initbarea(&cgblk);
+ bufhead.b_next = bufhead.b_prev = &bufhead;
+ bufcnt = MAXBUFSPACE / sblock.fs_bsize;
+ if (bufcnt < MINBUFS)
+ bufcnt = MINBUFS;
+ for (i = 0; i < bufcnt; i++) {
+ bp = (struct bufarea *)malloc(sizeof(struct bufarea));
+ bufp = malloc((unsigned int)sblock.fs_bsize);
+ if (bp == NULL || bufp == NULL) {
+ if (i >= MINBUFS)
+ break;
+ errexit("cannot allocate buffer pool\n");
+ }
+ bp->b_un.b_buf = bufp;
+ bp->b_prev = &bufhead;
+ bp->b_next = bufhead.b_next;
+ bufhead.b_next->b_prev = bp;
+ bufhead.b_next = bp;
+ initbarea(bp);
+ }
+ bufhead.b_size = i; /* save number of buffers */
+}
+
+/*
+ * Manage a cache of directory blocks.
+ */
+struct bufarea *
+getdatablk(blkno, size)
+ daddr_t blkno;
+ long size;
+{
+ register struct bufarea *bp;
+
+ for (bp = bufhead.b_next; bp != &bufhead; bp = bp->b_next)
+ if (bp->b_bno == fsbtodb(&sblock, blkno))
+ goto foundit;
+ for (bp = bufhead.b_prev; bp != &bufhead; bp = bp->b_prev)
+ if ((bp->b_flags & B_INUSE) == 0)
+ break;
+ if (bp == &bufhead)
+ errexit("deadlocked buffer pool\n");
+ getblk(bp, blkno, size);
+ /* fall through */
+foundit:
+ totalreads++;
+ bp->b_prev->b_next = bp->b_next;
+ bp->b_next->b_prev = bp->b_prev;
+ bp->b_prev = &bufhead;
+ bp->b_next = bufhead.b_next;
+ bufhead.b_next->b_prev = bp;
+ bufhead.b_next = bp;
+ bp->b_flags |= B_INUSE;
+ return (bp);
+}
+
+void
+getblk(bp, blk, size)
+ register struct bufarea *bp;
+ daddr_t blk;
+ long size;
+{
+ daddr_t dblk;
+
+ dblk = fsbtodb(&sblock, blk);
+ if (bp->b_bno != dblk) {
+ flush(fswritefd, bp);
+ diskreads++;
+ bp->b_errs = bread(fsreadfd, bp->b_un.b_buf, dblk, size);
+ bp->b_bno = dblk;
+ bp->b_size = size;
+ }
+}
+
+flush(fd, bp)
+ int fd;
+ register struct bufarea *bp;
+{
+ register int i, j;
+
+ if (!bp->b_dirty)
+ return;
+ if (bp->b_errs != 0)
+ pfatal("WRITING %sZERO'ED BLOCK %d TO DISK\n",
+ (bp->b_errs == bp->b_size / dev_bsize) ? "" : "PARTIALLY ",
+ bp->b_bno);
+ bp->b_dirty = 0;
+ bp->b_errs = 0;
+ bwrite(fd, bp->b_un.b_buf, bp->b_bno, (long)bp->b_size);
+ if (bp != &sblk)
+ return;
+ for (i = 0, j = 0; i < sblock.fs_cssize; i += sblock.fs_bsize, j++) {
+ bwrite(fswritefd, (char *)sblock.fs_csp[j],
+ fsbtodb(&sblock, sblock.fs_csaddr + j * sblock.fs_frag),
+ sblock.fs_cssize - i < sblock.fs_bsize ?
+ sblock.fs_cssize - i : sblock.fs_bsize);
+ }
+}
+
+rwerror(mesg, blk)
+ char *mesg;
+ daddr_t blk;
+{
+
+ if (preen == 0)
+ printf("\n");
+ pfatal("CANNOT %s: BLK %ld", mesg, blk);
+ if (reply("CONTINUE") == 0)
+ errexit("Program terminated\n");
+}
+
+ckfini()
+{
+ register struct bufarea *bp, *nbp;
+ int cnt = 0;
+
+ if (fswritefd < 0) {
+ (void)close(fsreadfd);
+ return;
+ }
+ flush(fswritefd, &sblk);
+ if (havesb && sblk.b_bno != SBOFF / dev_bsize &&
+ !preen && reply("UPDATE STANDARD SUPERBLOCK")) {
+ sblk.b_bno = SBOFF / dev_bsize;
+ sbdirty();
+ flush(fswritefd, &sblk);
+ }
+ flush(fswritefd, &cgblk);
+ free(cgblk.b_un.b_buf);
+ for (bp = bufhead.b_prev; bp && bp != &bufhead; bp = nbp) {
+ cnt++;
+ flush(fswritefd, bp);
+ nbp = bp->b_prev;
+ free(bp->b_un.b_buf);
+ free((char *)bp);
+ }
+ if (bufhead.b_size != cnt)
+ errexit("Panic: lost %d buffers\n", bufhead.b_size - cnt);
+ pbp = pdirbp = (struct bufarea *)0;
+ if (debug)
+ printf("cache missed %ld of %ld (%d%%)\n", diskreads,
+ totalreads, (int)(diskreads * 100 / totalreads));
+ (void)close(fsreadfd);
+ (void)close(fswritefd);
+}
+
+bread(fd, buf, blk, size)
+ int fd;
+ char *buf;
+ daddr_t blk;
+ long size;
+{
+ char *cp;
+ int i, errs;
+ off_t offset;
+
+ offset = blk;
+ offset *= dev_bsize;
+ if (lseek(fd, offset, 0) < 0)
+ rwerror("SEEK", blk);
+ else if (read(fd, buf, (int)size) == size)
+ return (0);
+ rwerror("READ", blk);
+ if (lseek(fd, offset, 0) < 0)
+ rwerror("SEEK", blk);
+ errs = 0;
+ bzero(buf, (size_t)size);
+ printf("THE FOLLOWING DISK SECTORS COULD NOT BE READ:");
+ for (cp = buf, i = 0; i < size; i += secsize, cp += secsize) {
+ if (read(fd, cp, (int)secsize) != secsize) {
+ (void)lseek(fd, offset + i + secsize, 0);
+ if (secsize != dev_bsize && dev_bsize != 1)
+ printf(" %ld (%ld),",
+ (blk * dev_bsize + i) / secsize,
+ blk + i / dev_bsize);
+ else
+ printf(" %ld,", blk + i / dev_bsize);
+ errs++;
+ }
+ }
+ printf("\n");
+ return (errs);
+}
+
+bwrite(fd, buf, blk, size)
+ int fd;
+ char *buf;
+ daddr_t blk;
+ long size;
+{
+ int i;
+ char *cp;
+ off_t offset;
+
+ if (fd < 0)
+ return;
+ offset = blk;
+ offset *= dev_bsize;
+ if (lseek(fd, offset, 0) < 0)
+ rwerror("SEEK", blk);
+ else if (write(fd, buf, (int)size) == size) {
+ fsmodified = 1;
+ return;
+ }
+ rwerror("WRITE", blk);
+ if (lseek(fd, offset, 0) < 0)
+ rwerror("SEEK", blk);
+ printf("THE FOLLOWING SECTORS COULD NOT BE WRITTEN:");
+ for (cp = buf, i = 0; i < size; i += dev_bsize, cp += dev_bsize)
+ if (write(fd, cp, (int)dev_bsize) != dev_bsize) {
+ (void)lseek(fd, offset + i + dev_bsize, 0);
+ printf(" %ld,", blk + i / dev_bsize);
+ }
+ printf("\n");
+ return;
+}
+
+/*
+ * allocate a data block with the specified number of fragments
+ */
+allocblk(frags)
+ long frags;
+{
+ register int i, j, k;
+
+ if (frags <= 0 || frags > sblock.fs_frag)
+ return (0);
+ for (i = 0; i < maxfsblock - sblock.fs_frag; i += sblock.fs_frag) {
+ for (j = 0; j <= sblock.fs_frag - frags; j++) {
+ if (testbmap(i + j))
+ continue;
+ for (k = 1; k < frags; k++)
+ if (testbmap(i + j + k))
+ break;
+ if (k < frags) {
+ j += k;
+ continue;
+ }
+ for (k = 0; k < frags; k++)
+ setbmap(i + j + k);
+ n_blks += frags;
+ return (i + j);
+ }
+ }
+ return (0);
+}
+
+/*
+ * Free a previously allocated block
+ */
+freeblk(blkno, frags)
+ daddr_t blkno;
+ long frags;
+{
+ struct inodesc idesc;
+
+ idesc.id_blkno = blkno;
+ idesc.id_numfrags = frags;
+ (void)pass4check(&idesc);
+}
+
+/*
+ * Find a pathname
+ */
+getpathname(namebuf, curdir, ino)
+ char *namebuf;
+ ino_t curdir, ino;
+{
+ int len;
+ register char *cp;
+ struct inodesc idesc;
+ static int busy = 0;
+ extern int findname();
+
+ if (curdir == ino && ino == ROOTINO) {
+ (void)strcpy(namebuf, "/");
+ return;
+ }
+ if (busy ||
+ (statemap[curdir] != DSTATE && statemap[curdir] != DFOUND)) {
+ (void)strcpy(namebuf, "?");
+ return;
+ }
+ busy = 1;
+ bzero((char *)&idesc, sizeof(struct inodesc));
+ idesc.id_type = DATA;
+ idesc.id_fix = IGNORE;
+ cp = &namebuf[MAXPATHLEN - 1];
+ *cp = '\0';
+ if (curdir != ino) {
+ idesc.id_parent = curdir;
+ goto namelookup;
+ }
+ while (ino != ROOTINO) {
+ idesc.id_number = ino;
+ idesc.id_func = findino;
+ idesc.id_name = "..";
+ if ((ckinode(ginode(ino), &idesc) & FOUND) == 0)
+ break;
+ namelookup:
+ idesc.id_number = idesc.id_parent;
+ idesc.id_parent = ino;
+ idesc.id_func = findname;
+ idesc.id_name = namebuf;
+ if ((ckinode(ginode(idesc.id_number), &idesc)&FOUND) == 0)
+ break;
+ len = strlen(namebuf);
+ cp -= len;
+ bcopy(namebuf, cp, (size_t)len);
+ *--cp = '/';
+ if (cp < &namebuf[MAXNAMLEN])
+ break;
+ ino = idesc.id_number;
+ }
+ busy = 0;
+ if (ino != ROOTINO)
+ *--cp = '?';
+ bcopy(cp, namebuf, (size_t)(&namebuf[MAXPATHLEN] - cp));
+}
+
+void
+catch()
+{
+ if (!doinglevel2)
+ ckfini();
+ exit(12);
+}
+
+/*
+ * When preening, allow a single quit to signal
+ * a special exit after filesystem checks complete
+ * so that reboot sequence may be interrupted.
+ */
+void
+catchquit()
+{
+ extern returntosingle;
+
+ printf("returning to single-user after filesystem check\n");
+ returntosingle = 1;
+ (void)signal(SIGQUIT, SIG_DFL);
+}
+
+/*
+ * Ignore a single quit signal; wait and flush just in case.
+ * Used by child processes in preen.
+ */
+void
+voidquit()
+{
+
+ sleep(1);
+ (void)signal(SIGQUIT, SIG_IGN);
+ (void)signal(SIGQUIT, SIG_DFL);
+}
+
+/*
+ * determine whether an inode should be fixed.
+ */
+dofix(idesc, msg)
+ register struct inodesc *idesc;
+ char *msg;
+{
+
+ switch (idesc->id_fix) {
+
+ case DONTKNOW:
+ if (idesc->id_type == DATA)
+ direrror(idesc->id_number, msg);
+ else
+ pwarn(msg);
+ if (preen) {
+ printf(" (SALVAGED)\n");
+ idesc->id_fix = FIX;
+ return (ALTERED);
+ }
+ if (reply("SALVAGE") == 0) {
+ idesc->id_fix = NOFIX;
+ return (0);
+ }
+ idesc->id_fix = FIX;
+ return (ALTERED);
+
+ case FIX:
+ return (ALTERED);
+
+ case NOFIX:
+ case IGNORE:
+ return (0);
+
+ default:
+ errexit("UNKNOWN INODESC FIX MODE %d\n", idesc->id_fix);
+ }
+ /* NOTREACHED */
+}
+
+/* VARARGS1 */
+errexit(s1, s2, s3, s4)
+ char *s1;
+{
+ printf(s1, s2, s3, s4);
+ exit(8);
+}
+
+/*
+ * An unexpected inconsistency occured.
+ * Die if preening, otherwise just print message and continue.
+ */
+/* VARARGS1 */
+pfatal(s, a1, a2, a3)
+ char *s;
+{
+
+ if (preen) {
+ printf("%s: ", cdevname);
+ printf(s, a1, a2, a3);
+ printf("\n");
+ printf("%s: UNEXPECTED INCONSISTENCY; RUN fsck MANUALLY.\n",
+ cdevname);
+ exit(8);
+ }
+ printf(s, a1, a2, a3);
+}
+
+/*
+ * Pwarn just prints a message when not preening,
+ * or a warning (preceded by filename) when preening.
+ */
+/* VARARGS1 */
+pwarn(s, a1, a2, a3, a4, a5, a6)
+ char *s;
+{
+
+ if (preen)
+ printf("%s: ", cdevname);
+ printf(s, a1, a2, a3, a4, a5, a6);
+}
+
+#ifndef lint
+/*
+ * Stub for routines from kernel.
+ */
+panic(s)
+ char *s;
+{
+
+ pfatal("INTERNAL INCONSISTENCY:");
+ errexit(s);
+}
+#endif
diff --git a/configure b/configure
new file mode 100755
index 00000000..c8d91f64
--- /dev/null
+++ b/configure
@@ -0,0 +1,5055 @@
+#! /bin/sh
+# From configure.in Id: configure.in,v 1.37 2007/11/07 13:07:52 tschwinge Exp .
+# Guess values for system-dependent variables and create Makefiles.
+# Generated by GNU Autoconf 2.61.
+#
+# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,
+# 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc.
+# This configure script is free software; the Free Software Foundation
+# gives unlimited permission to copy, distribute and modify it.
+## --------------------- ##
+## M4sh Initialization. ##
+## --------------------- ##
+
+# Be more Bourne compatible
+DUALCASE=1; export DUALCASE # for MKS sh
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
+ emulate sh
+ NULLCMD=:
+ # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which
+ # is contrary to our usage. Disable this feature.
+ alias -g '${1+"$@"}'='"$@"'
+ setopt NO_GLOB_SUBST
+else
+ case `(set -o) 2>/dev/null` in
+ *posix*) set -o posix ;;
+esac
+
+fi
+
+
+
+
+# PATH needs CR
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+ echo "#! /bin/sh" >conf$$.sh
+ echo "exit 0" >>conf$$.sh
+ chmod +x conf$$.sh
+ if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then
+ PATH_SEPARATOR=';'
+ else
+ PATH_SEPARATOR=:
+ fi
+ rm -f conf$$.sh
+fi
+
+# Support unset when possible.
+if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then
+ as_unset=unset
+else
+ as_unset=false
+fi
+
+
+# IFS
+# We need space, tab and new line, in precisely that order. Quoting is
+# there to prevent editors from complaining about space-tab.
+# (If _AS_PATH_WALK were called with IFS unset, it would disable word
+# splitting by setting IFS to empty value.)
+as_nl='
+'
+IFS=" "" $as_nl"
+
+# Find who we are. Look in the path if we contain no directory separator.
+case $0 in
+ *[\\/]* ) as_myself=$0 ;;
+ *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+done
+IFS=$as_save_IFS
+
+ ;;
+esac
+# We did not find ourselves, most probably we were run as `sh COMMAND'
+# in which case we are not to be found in the path.
+if test "x$as_myself" = x; then
+ as_myself=$0
+fi
+if test ! -f "$as_myself"; then
+ echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2
+ { (exit 1); exit 1; }
+fi
+
+# Work around bugs in pre-3.0 UWIN ksh.
+for as_var in ENV MAIL MAILPATH
+do ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var
+done
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+for as_var in \
+ LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \
+ LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \
+ LC_TELEPHONE LC_TIME
+do
+ if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then
+ eval $as_var=C; export $as_var
+ else
+ ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var
+ fi
+done
+
+# Required to use basename.
+if expr a : '\(a\)' >/dev/null 2>&1 &&
+ test "X`expr 00001 : '.*\(...\)'`" = X001; then
+ as_expr=expr
+else
+ as_expr=false
+fi
+
+if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then
+ as_basename=basename
+else
+ as_basename=false
+fi
+
+
+# Name of the executable.
+as_me=`$as_basename -- "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+ X"$0" : 'X\(//\)$' \| \
+ X"$0" : 'X\(/\)' \| . 2>/dev/null ||
+echo X/"$0" |
+ sed '/^.*\/\([^/][^/]*\)\/*$/{
+ s//\1/
+ q
+ }
+ /^X\/\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\/\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+
+# CDPATH.
+$as_unset CDPATH
+
+
+if test "x$CONFIG_SHELL" = x; then
+ if (eval ":") 2>/dev/null; then
+ as_have_required=yes
+else
+ as_have_required=no
+fi
+
+ if test $as_have_required = yes && (eval ":
+(as_func_return () {
+ (exit \$1)
+}
+as_func_success () {
+ as_func_return 0
+}
+as_func_failure () {
+ as_func_return 1
+}
+as_func_ret_success () {
+ return 0
+}
+as_func_ret_failure () {
+ return 1
+}
+
+exitcode=0
+if as_func_success; then
+ :
+else
+ exitcode=1
+ echo as_func_success failed.
+fi
+
+if as_func_failure; then
+ exitcode=1
+ echo as_func_failure succeeded.
+fi
+
+if as_func_ret_success; then
+ :
+else
+ exitcode=1
+ echo as_func_ret_success failed.
+fi
+
+if as_func_ret_failure; then
+ exitcode=1
+ echo as_func_ret_failure succeeded.
+fi
+
+if ( set x; as_func_ret_success y && test x = \"\$1\" ); then
+ :
+else
+ exitcode=1
+ echo positional parameters were not saved.
+fi
+
+test \$exitcode = 0) || { (exit 1); exit 1; }
+
+(
+ as_lineno_1=\$LINENO
+ as_lineno_2=\$LINENO
+ test \"x\$as_lineno_1\" != \"x\$as_lineno_2\" &&
+ test \"x\`expr \$as_lineno_1 + 1\`\" = \"x\$as_lineno_2\") || { (exit 1); exit 1; }
+") 2> /dev/null; then
+ :
+else
+ as_candidate_shells=
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ case $as_dir in
+ /*)
+ for as_base in sh bash ksh sh5; do
+ as_candidate_shells="$as_candidate_shells $as_dir/$as_base"
+ done;;
+ esac
+done
+IFS=$as_save_IFS
+
+
+ for as_shell in $as_candidate_shells $SHELL; do
+ # Try only shells that exist, to save several forks.
+ if { test -f "$as_shell" || test -f "$as_shell.exe"; } &&
+ { ("$as_shell") 2> /dev/null <<\_ASEOF
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
+ emulate sh
+ NULLCMD=:
+ # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which
+ # is contrary to our usage. Disable this feature.
+ alias -g '${1+"$@"}'='"$@"'
+ setopt NO_GLOB_SUBST
+else
+ case `(set -o) 2>/dev/null` in
+ *posix*) set -o posix ;;
+esac
+
+fi
+
+
+:
+_ASEOF
+}; then
+ CONFIG_SHELL=$as_shell
+ as_have_required=yes
+ if { "$as_shell" 2> /dev/null <<\_ASEOF
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
+ emulate sh
+ NULLCMD=:
+ # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which
+ # is contrary to our usage. Disable this feature.
+ alias -g '${1+"$@"}'='"$@"'
+ setopt NO_GLOB_SUBST
+else
+ case `(set -o) 2>/dev/null` in
+ *posix*) set -o posix ;;
+esac
+
+fi
+
+
+:
+(as_func_return () {
+ (exit $1)
+}
+as_func_success () {
+ as_func_return 0
+}
+as_func_failure () {
+ as_func_return 1
+}
+as_func_ret_success () {
+ return 0
+}
+as_func_ret_failure () {
+ return 1
+}
+
+exitcode=0
+if as_func_success; then
+ :
+else
+ exitcode=1
+ echo as_func_success failed.
+fi
+
+if as_func_failure; then
+ exitcode=1
+ echo as_func_failure succeeded.
+fi
+
+if as_func_ret_success; then
+ :
+else
+ exitcode=1
+ echo as_func_ret_success failed.
+fi
+
+if as_func_ret_failure; then
+ exitcode=1
+ echo as_func_ret_failure succeeded.
+fi
+
+if ( set x; as_func_ret_success y && test x = "$1" ); then
+ :
+else
+ exitcode=1
+ echo positional parameters were not saved.
+fi
+
+test $exitcode = 0) || { (exit 1); exit 1; }
+
+(
+ as_lineno_1=$LINENO
+ as_lineno_2=$LINENO
+ test "x$as_lineno_1" != "x$as_lineno_2" &&
+ test "x`expr $as_lineno_1 + 1`" = "x$as_lineno_2") || { (exit 1); exit 1; }
+
+_ASEOF
+}; then
+ break
+fi
+
+fi
+
+ done
+
+ if test "x$CONFIG_SHELL" != x; then
+ for as_var in BASH_ENV ENV
+ do ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var
+ done
+ export CONFIG_SHELL
+ exec "$CONFIG_SHELL" "$as_myself" ${1+"$@"}
+fi
+
+
+ if test $as_have_required = no; then
+ echo This script requires a shell more modern than all the
+ echo shells that I found on your system. Please install a
+ echo modern shell, or manually run the script under such a
+ echo shell if you do have one.
+ { (exit 1); exit 1; }
+fi
+
+
+fi
+
+fi
+
+
+
+(eval "as_func_return () {
+ (exit \$1)
+}
+as_func_success () {
+ as_func_return 0
+}
+as_func_failure () {
+ as_func_return 1
+}
+as_func_ret_success () {
+ return 0
+}
+as_func_ret_failure () {
+ return 1
+}
+
+exitcode=0
+if as_func_success; then
+ :
+else
+ exitcode=1
+ echo as_func_success failed.
+fi
+
+if as_func_failure; then
+ exitcode=1
+ echo as_func_failure succeeded.
+fi
+
+if as_func_ret_success; then
+ :
+else
+ exitcode=1
+ echo as_func_ret_success failed.
+fi
+
+if as_func_ret_failure; then
+ exitcode=1
+ echo as_func_ret_failure succeeded.
+fi
+
+if ( set x; as_func_ret_success y && test x = \"\$1\" ); then
+ :
+else
+ exitcode=1
+ echo positional parameters were not saved.
+fi
+
+test \$exitcode = 0") || {
+ echo No shell found that supports shell functions.
+ echo Please tell autoconf@gnu.org about your system,
+ echo including any error possibly output before this
+ echo message
+}
+
+
+
+ as_lineno_1=$LINENO
+ as_lineno_2=$LINENO
+ test "x$as_lineno_1" != "x$as_lineno_2" &&
+ test "x`expr $as_lineno_1 + 1`" = "x$as_lineno_2" || {
+
+ # Create $as_me.lineno as a copy of $as_myself, but with $LINENO
+ # uniformly replaced by the line number. The first 'sed' inserts a
+ # line-number line after each line using $LINENO; the second 'sed'
+ # does the real work. The second script uses 'N' to pair each
+ # line-number line with the line containing $LINENO, and appends
+ # trailing '-' during substitution so that $LINENO is not a special
+ # case at line end.
+ # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the
+ # scripts with optimization help from Paolo Bonzini. Blame Lee
+ # E. McMahon (1931-1989) for sed's syntax. :-)
+ sed -n '
+ p
+ /[$]LINENO/=
+ ' <$as_myself |
+ sed '
+ s/[$]LINENO.*/&-/
+ t lineno
+ b
+ :lineno
+ N
+ :loop
+ s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/
+ t loop
+ s/-\n.*//
+ ' >$as_me.lineno &&
+ chmod +x "$as_me.lineno" ||
+ { echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2
+ { (exit 1); exit 1; }; }
+
+ # Don't try to exec as it changes $[0], causing all sort of problems
+ # (the dirname of $[0] is not the place where we might find the
+ # original and so on. Autoconf is especially sensitive to this).
+ . "./$as_me.lineno"
+ # Exit status is that of the last command.
+ exit
+}
+
+
+if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then
+ as_dirname=dirname
+else
+ as_dirname=false
+fi
+
+ECHO_C= ECHO_N= ECHO_T=
+case `echo -n x` in
+-n*)
+ case `echo 'x\c'` in
+ *c*) ECHO_T=' ';; # ECHO_T is single tab character.
+ *) ECHO_C='\c';;
+ esac;;
+*)
+ ECHO_N='-n';;
+esac
+
+if expr a : '\(a\)' >/dev/null 2>&1 &&
+ test "X`expr 00001 : '.*\(...\)'`" = X001; then
+ as_expr=expr
+else
+ as_expr=false
+fi
+
+rm -f conf$$ conf$$.exe conf$$.file
+if test -d conf$$.dir; then
+ rm -f conf$$.dir/conf$$.file
+else
+ rm -f conf$$.dir
+ mkdir conf$$.dir
+fi
+echo >conf$$.file
+if ln -s conf$$.file conf$$ 2>/dev/null; then
+ as_ln_s='ln -s'
+ # ... but there are two gotchas:
+ # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
+ # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
+ # In both cases, we have to default to `cp -p'.
+ ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
+ as_ln_s='cp -p'
+elif ln conf$$.file conf$$ 2>/dev/null; then
+ as_ln_s=ln
+else
+ as_ln_s='cp -p'
+fi
+rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
+rmdir conf$$.dir 2>/dev/null
+
+if mkdir -p . 2>/dev/null; then
+ as_mkdir_p=:
+else
+ test -d ./-p && rmdir ./-p
+ as_mkdir_p=false
+fi
+
+if test -x / >/dev/null 2>&1; then
+ as_test_x='test -x'
+else
+ if ls -dL / >/dev/null 2>&1; then
+ as_ls_L_option=L
+ else
+ as_ls_L_option=
+ fi
+ as_test_x='
+ eval sh -c '\''
+ if test -d "$1"; then
+ test -d "$1/.";
+ else
+ case $1 in
+ -*)set "./$1";;
+ esac;
+ case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in
+ ???[sx]*):;;*)false;;esac;fi
+ '\'' sh
+ '
+fi
+as_executable_p=$as_test_x
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+
+
+exec 7<&0 </dev/null 6>&1
+
+# Name of the host.
+# hostname on some systems (SVR3.2, Linux) returns a bogus exit status,
+# so uname gets run too.
+ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q`
+
+#
+# Initializations.
+#
+ac_default_prefix=/usr/local
+ac_clean_files=
+ac_config_libobj_dir=.
+LIBOBJS=
+cross_compiling=no
+subdirs=
+MFLAGS=
+MAKEFLAGS=
+SHELL=${CONFIG_SHELL-/bin/sh}
+
+# Identity of this package.
+PACKAGE_NAME=
+PACKAGE_TARNAME=
+PACKAGE_VERSION=
+PACKAGE_STRING=
+PACKAGE_BUGREPORT=
+
+ac_unique_file="hurd/hurd_types.h"
+ac_default_prefix=
+ac_subst_vars='SHELL
+PATH_SEPARATOR
+PACKAGE_NAME
+PACKAGE_TARNAME
+PACKAGE_VERSION
+PACKAGE_STRING
+PACKAGE_BUGREPORT
+exec_prefix
+prefix
+program_transform_name
+bindir
+sbindir
+libexecdir
+datarootdir
+datadir
+sysconfdir
+sharedstatedir
+localstatedir
+includedir
+oldincludedir
+docdir
+infodir
+htmldir
+dvidir
+pdfdir
+psdir
+libdir
+localedir
+mandir
+DEFS
+ECHO_C
+ECHO_N
+ECHO_T
+LIBS
+build_alias
+host_alias
+target_alias
+build
+build_cpu
+build_vendor
+build_os
+host
+host_cpu
+host_vendor
+host_os
+asm_syntax
+enable_profile
+enable_static_progs
+INSTALL_PROGRAM
+INSTALL_SCRIPT
+INSTALL_DATA
+AWK
+CC
+CFLAGS
+LDFLAGS
+CPPFLAGS
+ac_ct_CC
+EXEEXT
+OBJEXT
+LD
+OBJCOPY
+AR
+RANLIB
+MIG
+LIBCRYPT
+VERSIONING
+libc_cv_gnu89_inline
+boot_store_types
+NCURSESW_INCLUDE
+LIBNCURSESW
+LIBOBJS
+LTLIBOBJS'
+ac_subst_files=''
+ ac_precious_vars='build_alias
+host_alias
+target_alias
+CC
+CFLAGS
+LDFLAGS
+LIBS
+CPPFLAGS'
+
+
+# Initialize some variables set by options.
+ac_init_help=
+ac_init_version=false
+# The variables have the same names as the options, with
+# dashes changed to underlines.
+cache_file=/dev/null
+exec_prefix=NONE
+no_create=
+no_recursion=
+prefix=NONE
+program_prefix=NONE
+program_suffix=NONE
+program_transform_name=s,x,x,
+silent=
+site=
+srcdir=
+verbose=
+x_includes=NONE
+x_libraries=NONE
+
+# Installation directory options.
+# These are left unexpanded so users can "make install exec_prefix=/foo"
+# and all the variables that are supposed to be based on exec_prefix
+# by default will actually change.
+# Use braces instead of parens because sh, perl, etc. also accept them.
+# (The list follows the same order as the GNU Coding Standards.)
+bindir='${exec_prefix}/bin'
+sbindir='${exec_prefix}/sbin'
+libexecdir='${exec_prefix}/libexec'
+datarootdir='${prefix}/share'
+datadir='${datarootdir}'
+sysconfdir='${prefix}/etc'
+sharedstatedir='${prefix}/com'
+localstatedir='${prefix}/var'
+includedir='${prefix}/include'
+oldincludedir='/usr/include'
+docdir='${datarootdir}/doc/${PACKAGE}'
+infodir='${datarootdir}/info'
+htmldir='${docdir}'
+dvidir='${docdir}'
+pdfdir='${docdir}'
+psdir='${docdir}'
+libdir='${exec_prefix}/lib'
+localedir='${datarootdir}/locale'
+mandir='${datarootdir}/man'
+
+ac_prev=
+ac_dashdash=
+for ac_option
+do
+ # If the previous option needs an argument, assign it.
+ if test -n "$ac_prev"; then
+ eval $ac_prev=\$ac_option
+ ac_prev=
+ continue
+ fi
+
+ case $ac_option in
+ *=*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;;
+ *) ac_optarg=yes ;;
+ esac
+
+ # Accept the important Cygnus configure options, so we can diagnose typos.
+
+ case $ac_dashdash$ac_option in
+ --)
+ ac_dashdash=yes ;;
+
+ -bindir | --bindir | --bindi | --bind | --bin | --bi)
+ ac_prev=bindir ;;
+ -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*)
+ bindir=$ac_optarg ;;
+
+ -build | --build | --buil | --bui | --bu)
+ ac_prev=build_alias ;;
+ -build=* | --build=* | --buil=* | --bui=* | --bu=*)
+ build_alias=$ac_optarg ;;
+
+ -cache-file | --cache-file | --cache-fil | --cache-fi \
+ | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c)
+ ac_prev=cache_file ;;
+ -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \
+ | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*)
+ cache_file=$ac_optarg ;;
+
+ --config-cache | -C)
+ cache_file=config.cache ;;
+
+ -datadir | --datadir | --datadi | --datad)
+ ac_prev=datadir ;;
+ -datadir=* | --datadir=* | --datadi=* | --datad=*)
+ datadir=$ac_optarg ;;
+
+ -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \
+ | --dataroo | --dataro | --datar)
+ ac_prev=datarootdir ;;
+ -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \
+ | --dataroot=* | --dataroo=* | --dataro=* | --datar=*)
+ datarootdir=$ac_optarg ;;
+
+ -disable-* | --disable-*)
+ ac_feature=`expr "x$ac_option" : 'x-*disable-\(.*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_feature" : ".*[^-._$as_cr_alnum]" >/dev/null &&
+ { echo "$as_me: error: invalid feature name: $ac_feature" >&2
+ { (exit 1); exit 1; }; }
+ ac_feature=`echo $ac_feature | sed 's/[-.]/_/g'`
+ eval enable_$ac_feature=no ;;
+
+ -docdir | --docdir | --docdi | --doc | --do)
+ ac_prev=docdir ;;
+ -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*)
+ docdir=$ac_optarg ;;
+
+ -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv)
+ ac_prev=dvidir ;;
+ -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*)
+ dvidir=$ac_optarg ;;
+
+ -enable-* | --enable-*)
+ ac_feature=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_feature" : ".*[^-._$as_cr_alnum]" >/dev/null &&
+ { echo "$as_me: error: invalid feature name: $ac_feature" >&2
+ { (exit 1); exit 1; }; }
+ ac_feature=`echo $ac_feature | sed 's/[-.]/_/g'`
+ eval enable_$ac_feature=\$ac_optarg ;;
+
+ -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \
+ | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \
+ | --exec | --exe | --ex)
+ ac_prev=exec_prefix ;;
+ -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \
+ | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \
+ | --exec=* | --exe=* | --ex=*)
+ exec_prefix=$ac_optarg ;;
+
+ -gas | --gas | --ga | --g)
+ # Obsolete; use --with-gas.
+ with_gas=yes ;;
+
+ -help | --help | --hel | --he | -h)
+ ac_init_help=long ;;
+ -help=r* | --help=r* | --hel=r* | --he=r* | -hr*)
+ ac_init_help=recursive ;;
+ -help=s* | --help=s* | --hel=s* | --he=s* | -hs*)
+ ac_init_help=short ;;
+
+ -host | --host | --hos | --ho)
+ ac_prev=host_alias ;;
+ -host=* | --host=* | --hos=* | --ho=*)
+ host_alias=$ac_optarg ;;
+
+ -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht)
+ ac_prev=htmldir ;;
+ -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \
+ | --ht=*)
+ htmldir=$ac_optarg ;;
+
+ -includedir | --includedir | --includedi | --included | --include \
+ | --includ | --inclu | --incl | --inc)
+ ac_prev=includedir ;;
+ -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \
+ | --includ=* | --inclu=* | --incl=* | --inc=*)
+ includedir=$ac_optarg ;;
+
+ -infodir | --infodir | --infodi | --infod | --info | --inf)
+ ac_prev=infodir ;;
+ -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*)
+ infodir=$ac_optarg ;;
+
+ -libdir | --libdir | --libdi | --libd)
+ ac_prev=libdir ;;
+ -libdir=* | --libdir=* | --libdi=* | --libd=*)
+ libdir=$ac_optarg ;;
+
+ -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \
+ | --libexe | --libex | --libe)
+ ac_prev=libexecdir ;;
+ -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \
+ | --libexe=* | --libex=* | --libe=*)
+ libexecdir=$ac_optarg ;;
+
+ -localedir | --localedir | --localedi | --localed | --locale)
+ ac_prev=localedir ;;
+ -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*)
+ localedir=$ac_optarg ;;
+
+ -localstatedir | --localstatedir | --localstatedi | --localstated \
+ | --localstate | --localstat | --localsta | --localst | --locals)
+ ac_prev=localstatedir ;;
+ -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \
+ | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*)
+ localstatedir=$ac_optarg ;;
+
+ -mandir | --mandir | --mandi | --mand | --man | --ma | --m)
+ ac_prev=mandir ;;
+ -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*)
+ mandir=$ac_optarg ;;
+
+ -nfp | --nfp | --nf)
+ # Obsolete; use --without-fp.
+ with_fp=no ;;
+
+ -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+ | --no-cr | --no-c | -n)
+ no_create=yes ;;
+
+ -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r)
+ no_recursion=yes ;;
+
+ -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \
+ | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \
+ | --oldin | --oldi | --old | --ol | --o)
+ ac_prev=oldincludedir ;;
+ -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \
+ | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \
+ | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*)
+ oldincludedir=$ac_optarg ;;
+
+ -prefix | --prefix | --prefi | --pref | --pre | --pr | --p)
+ ac_prev=prefix ;;
+ -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*)
+ prefix=$ac_optarg ;;
+
+ -program-prefix | --program-prefix | --program-prefi | --program-pref \
+ | --program-pre | --program-pr | --program-p)
+ ac_prev=program_prefix ;;
+ -program-prefix=* | --program-prefix=* | --program-prefi=* \
+ | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*)
+ program_prefix=$ac_optarg ;;
+
+ -program-suffix | --program-suffix | --program-suffi | --program-suff \
+ | --program-suf | --program-su | --program-s)
+ ac_prev=program_suffix ;;
+ -program-suffix=* | --program-suffix=* | --program-suffi=* \
+ | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*)
+ program_suffix=$ac_optarg ;;
+
+ -program-transform-name | --program-transform-name \
+ | --program-transform-nam | --program-transform-na \
+ | --program-transform-n | --program-transform- \
+ | --program-transform | --program-transfor \
+ | --program-transfo | --program-transf \
+ | --program-trans | --program-tran \
+ | --progr-tra | --program-tr | --program-t)
+ ac_prev=program_transform_name ;;
+ -program-transform-name=* | --program-transform-name=* \
+ | --program-transform-nam=* | --program-transform-na=* \
+ | --program-transform-n=* | --program-transform-=* \
+ | --program-transform=* | --program-transfor=* \
+ | --program-transfo=* | --program-transf=* \
+ | --program-trans=* | --program-tran=* \
+ | --progr-tra=* | --program-tr=* | --program-t=*)
+ program_transform_name=$ac_optarg ;;
+
+ -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd)
+ ac_prev=pdfdir ;;
+ -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*)
+ pdfdir=$ac_optarg ;;
+
+ -psdir | --psdir | --psdi | --psd | --ps)
+ ac_prev=psdir ;;
+ -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*)
+ psdir=$ac_optarg ;;
+
+ -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+ | -silent | --silent | --silen | --sile | --sil)
+ silent=yes ;;
+
+ -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
+ ac_prev=sbindir ;;
+ -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
+ | --sbi=* | --sb=*)
+ sbindir=$ac_optarg ;;
+
+ -sharedstatedir | --sharedstatedir | --sharedstatedi \
+ | --sharedstated | --sharedstate | --sharedstat | --sharedsta \
+ | --sharedst | --shareds | --shared | --share | --shar \
+ | --sha | --sh)
+ ac_prev=sharedstatedir ;;
+ -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \
+ | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \
+ | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \
+ | --sha=* | --sh=*)
+ sharedstatedir=$ac_optarg ;;
+
+ -site | --site | --sit)
+ ac_prev=site ;;
+ -site=* | --site=* | --sit=*)
+ site=$ac_optarg ;;
+
+ -srcdir | --srcdir | --srcdi | --srcd | --src | --sr)
+ ac_prev=srcdir ;;
+ -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*)
+ srcdir=$ac_optarg ;;
+
+ -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \
+ | --syscon | --sysco | --sysc | --sys | --sy)
+ ac_prev=sysconfdir ;;
+ -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \
+ | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*)
+ sysconfdir=$ac_optarg ;;
+
+ -target | --target | --targe | --targ | --tar | --ta | --t)
+ ac_prev=target_alias ;;
+ -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*)
+ target_alias=$ac_optarg ;;
+
+ -v | -verbose | --verbose | --verbos | --verbo | --verb)
+ verbose=yes ;;
+
+ -version | --version | --versio | --versi | --vers | -V)
+ ac_init_version=: ;;
+
+ -with-* | --with-*)
+ ac_package=`expr "x$ac_option" : 'x-*with-\([^=]*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_package" : ".*[^-._$as_cr_alnum]" >/dev/null &&
+ { echo "$as_me: error: invalid package name: $ac_package" >&2
+ { (exit 1); exit 1; }; }
+ ac_package=`echo $ac_package | sed 's/[-.]/_/g'`
+ eval with_$ac_package=\$ac_optarg ;;
+
+ -without-* | --without-*)
+ ac_package=`expr "x$ac_option" : 'x-*without-\(.*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_package" : ".*[^-._$as_cr_alnum]" >/dev/null &&
+ { echo "$as_me: error: invalid package name: $ac_package" >&2
+ { (exit 1); exit 1; }; }
+ ac_package=`echo $ac_package | sed 's/[-.]/_/g'`
+ eval with_$ac_package=no ;;
+
+ --x)
+ # Obsolete; use --with-x.
+ with_x=yes ;;
+
+ -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \
+ | --x-incl | --x-inc | --x-in | --x-i)
+ ac_prev=x_includes ;;
+ -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \
+ | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*)
+ x_includes=$ac_optarg ;;
+
+ -x-libraries | --x-libraries | --x-librarie | --x-librari \
+ | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l)
+ ac_prev=x_libraries ;;
+ -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \
+ | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*)
+ x_libraries=$ac_optarg ;;
+
+ -*) { echo "$as_me: error: unrecognized option: $ac_option
+Try \`$0 --help' for more information." >&2
+ { (exit 1); exit 1; }; }
+ ;;
+
+ *=*)
+ ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_envvar" : ".*[^_$as_cr_alnum]" >/dev/null &&
+ { echo "$as_me: error: invalid variable name: $ac_envvar" >&2
+ { (exit 1); exit 1; }; }
+ eval $ac_envvar=\$ac_optarg
+ export $ac_envvar ;;
+
+ *)
+ # FIXME: should be removed in autoconf 3.0.
+ echo "$as_me: WARNING: you should use --build, --host, --target" >&2
+ expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null &&
+ echo "$as_me: WARNING: invalid host type: $ac_option" >&2
+ : ${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}
+ ;;
+
+ esac
+done
+
+if test -n "$ac_prev"; then
+ ac_option=--`echo $ac_prev | sed 's/_/-/g'`
+ { echo "$as_me: error: missing argument to $ac_option" >&2
+ { (exit 1); exit 1; }; }
+fi
+
+# Be sure to have absolute directory names.
+for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \
+ datadir sysconfdir sharedstatedir localstatedir includedir \
+ oldincludedir docdir infodir htmldir dvidir pdfdir psdir \
+ libdir localedir mandir
+do
+ eval ac_val=\$$ac_var
+ case $ac_val in
+ [\\/$]* | ?:[\\/]* ) continue;;
+ NONE | '' ) case $ac_var in *prefix ) continue;; esac;;
+ esac
+ { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2
+ { (exit 1); exit 1; }; }
+done
+
+# There might be people who depend on the old broken behavior: `$host'
+# used to hold the argument of --host etc.
+# FIXME: To remove some day.
+build=$build_alias
+host=$host_alias
+target=$target_alias
+
+# FIXME: To remove some day.
+if test "x$host_alias" != x; then
+ if test "x$build_alias" = x; then
+ cross_compiling=maybe
+ echo "$as_me: WARNING: If you wanted to set the --build type, don't use --host.
+ If a cross compiler is detected then cross compile mode will be used." >&2
+ elif test "x$build_alias" != "x$host_alias"; then
+ cross_compiling=yes
+ fi
+fi
+
+ac_tool_prefix=
+test -n "$host_alias" && ac_tool_prefix=$host_alias-
+
+test "$silent" = yes && exec 6>/dev/null
+
+
+ac_pwd=`pwd` && test -n "$ac_pwd" &&
+ac_ls_di=`ls -di .` &&
+ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` ||
+ { echo "$as_me: error: Working directory cannot be determined" >&2
+ { (exit 1); exit 1; }; }
+test "X$ac_ls_di" = "X$ac_pwd_ls_di" ||
+ { echo "$as_me: error: pwd does not report name of working directory" >&2
+ { (exit 1); exit 1; }; }
+
+
+# Find the source files, if location was not specified.
+if test -z "$srcdir"; then
+ ac_srcdir_defaulted=yes
+ # Try the directory containing this script, then the parent directory.
+ ac_confdir=`$as_dirname -- "$0" ||
+$as_expr X"$0" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$0" : 'X\(//\)[^/]' \| \
+ X"$0" : 'X\(//\)$' \| \
+ X"$0" : 'X\(/\)' \| . 2>/dev/null ||
+echo X"$0" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)[^/].*/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+ srcdir=$ac_confdir
+ if test ! -r "$srcdir/$ac_unique_file"; then
+ srcdir=..
+ fi
+else
+ ac_srcdir_defaulted=no
+fi
+if test ! -r "$srcdir/$ac_unique_file"; then
+ test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .."
+ { echo "$as_me: error: cannot find sources ($ac_unique_file) in $srcdir" >&2
+ { (exit 1); exit 1; }; }
+fi
+ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work"
+ac_abs_confdir=`(
+ cd "$srcdir" && test -r "./$ac_unique_file" || { echo "$as_me: error: $ac_msg" >&2
+ { (exit 1); exit 1; }; }
+ pwd)`
+# When building in place, set srcdir=.
+if test "$ac_abs_confdir" = "$ac_pwd"; then
+ srcdir=.
+fi
+# Remove unnecessary trailing slashes from srcdir.
+# Double slashes in file names in object file debugging info
+# mess up M-x gdb in Emacs.
+case $srcdir in
+*/) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;;
+esac
+for ac_var in $ac_precious_vars; do
+ eval ac_env_${ac_var}_set=\${${ac_var}+set}
+ eval ac_env_${ac_var}_value=\$${ac_var}
+ eval ac_cv_env_${ac_var}_set=\${${ac_var}+set}
+ eval ac_cv_env_${ac_var}_value=\$${ac_var}
+done
+
+#
+# Report the --help message.
+#
+if test "$ac_init_help" = "long"; then
+ # Omit some internal or obsolete options to make the list less imposing.
+ # This message is too long to be a string in the A/UX 3.1 sh.
+ cat <<_ACEOF
+\`configure' configures this package to adapt to many kinds of systems.
+
+Usage: $0 [OPTION]... [VAR=VALUE]...
+
+To assign environment variables (e.g., CC, CFLAGS...), specify them as
+VAR=VALUE. See below for descriptions of some of the useful variables.
+
+Defaults for the options are specified in brackets.
+
+Configuration:
+ -h, --help display this help and exit
+ --help=short display options specific to this package
+ --help=recursive display the short help of all the included packages
+ -V, --version display version information and exit
+ -q, --quiet, --silent do not print \`checking...' messages
+ --cache-file=FILE cache test results in FILE [disabled]
+ -C, --config-cache alias for \`--cache-file=config.cache'
+ -n, --no-create do not create output files
+ --srcdir=DIR find the sources in DIR [configure dir or \`..']
+
+Installation directories:
+ --prefix=PREFIX install architecture-independent files in PREFIX
+ [$ac_default_prefix]
+ --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX
+ [PREFIX]
+
+By default, \`make install' will install all the files in
+\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify
+an installation prefix other than \`$ac_default_prefix' using \`--prefix',
+for instance \`--prefix=\$HOME'.
+
+For better control, use the options below.
+
+Fine tuning of the installation directories:
+ --bindir=DIR user executables [EPREFIX/bin]
+ --sbindir=DIR system admin executables [EPREFIX/sbin]
+ --libexecdir=DIR program executables [EPREFIX/libexec]
+ --sysconfdir=DIR read-only single-machine data [PREFIX/etc]
+ --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com]
+ --localstatedir=DIR modifiable single-machine data [PREFIX/var]
+ --libdir=DIR object code libraries [EPREFIX/lib]
+ --includedir=DIR C header files [PREFIX/include]
+ --oldincludedir=DIR C header files for non-gcc [/usr/include]
+ --datarootdir=DIR read-only arch.-independent data root [PREFIX/share]
+ --datadir=DIR read-only architecture-independent data [DATAROOTDIR]
+ --infodir=DIR info documentation [DATAROOTDIR/info]
+ --localedir=DIR locale-dependent data [DATAROOTDIR/locale]
+ --mandir=DIR man documentation [DATAROOTDIR/man]
+ --docdir=DIR documentation root [DATAROOTDIR/doc/PACKAGE]
+ --htmldir=DIR html documentation [DOCDIR]
+ --dvidir=DIR dvi documentation [DOCDIR]
+ --pdfdir=DIR pdf documentation [DOCDIR]
+ --psdir=DIR ps documentation [DOCDIR]
+_ACEOF
+
+ cat <<\_ACEOF
+
+System types:
+ --build=BUILD configure for building on BUILD [guessed]
+ --host=HOST cross-compile to build programs to run on HOST [BUILD]
+_ACEOF
+fi
+
+if test -n "$ac_init_help"; then
+
+ cat <<\_ACEOF
+
+Optional Features:
+ --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no)
+ --enable-FEATURE[=ARG] include FEATURE [ARG=yes]
+ --disable-profile do not build profiled libraries and programs
+ --enable-static-progs=PROGRAMS...
+ build statically-linked PROGRAM.static versions
+ of (only) the listed programs ext2fs,ufs
+ --enable-boot-store-types=TYPES...
+ list of store types included in statically
+ linked filesystems used for booting
+ --disable-ncursesw Do not use ncursesw
+
+Optional Packages:
+ --with-PACKAGE[=ARG] use PACKAGE [ARG=yes]
+ --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no)
+ --without-parted don't try to use GNU Parted libraries
+ --with-ncursesw-include-dir=DIR
+ Set directory containing the include files for
+ use with -lncursesw, when it isn't installed as
+ the default curses library. If DIR is "none",
+ then no special ncursesw include files are used.
+ --without-ncursesw-include-dir
+ Equivalent to --with-ncursesw-include-dir=none
+
+Some influential environment variables:
+ CC C compiler command
+ CFLAGS C compiler flags
+ LDFLAGS linker flags, e.g. -L<lib dir> if you have libraries in a
+ nonstandard directory <lib dir>
+ LIBS libraries to pass to the linker, e.g. -l<library>
+ CPPFLAGS C/C++/Objective C preprocessor flags, e.g. -I<include dir> if
+ you have headers in a nonstandard directory <include dir>
+
+Use these variables to override the choices made by `configure' or to help
+it to find libraries and programs with nonstandard names/locations.
+
+_ACEOF
+ac_status=$?
+fi
+
+if test "$ac_init_help" = "recursive"; then
+ # If there are subdirs, report their specific --help.
+ for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue
+ test -d "$ac_dir" || continue
+ ac_builddir=.
+
+case "$ac_dir" in
+.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;
+*)
+ ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'`
+ # A ".." for each directory in $ac_dir_suffix.
+ ac_top_builddir_sub=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,/..,g;s,/,,'`
+ case $ac_top_builddir_sub in
+ "") ac_top_builddir_sub=. ac_top_build_prefix= ;;
+ *) ac_top_build_prefix=$ac_top_builddir_sub/ ;;
+ esac ;;
+esac
+ac_abs_top_builddir=$ac_pwd
+ac_abs_builddir=$ac_pwd$ac_dir_suffix
+# for backward compatibility:
+ac_top_builddir=$ac_top_build_prefix
+
+case $srcdir in
+ .) # We are building in place.
+ ac_srcdir=.
+ ac_top_srcdir=$ac_top_builddir_sub
+ ac_abs_top_srcdir=$ac_pwd ;;
+ [\\/]* | ?:[\\/]* ) # Absolute name.
+ ac_srcdir=$srcdir$ac_dir_suffix;
+ ac_top_srcdir=$srcdir
+ ac_abs_top_srcdir=$srcdir ;;
+ *) # Relative name.
+ ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix
+ ac_top_srcdir=$ac_top_build_prefix$srcdir
+ ac_abs_top_srcdir=$ac_pwd/$srcdir ;;
+esac
+ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix
+
+ cd "$ac_dir" || { ac_status=$?; continue; }
+ # Check for guested configure.
+ if test -f "$ac_srcdir/configure.gnu"; then
+ echo &&
+ $SHELL "$ac_srcdir/configure.gnu" --help=recursive
+ elif test -f "$ac_srcdir/configure"; then
+ echo &&
+ $SHELL "$ac_srcdir/configure" --help=recursive
+ else
+ echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2
+ fi || ac_status=$?
+ cd "$ac_pwd" || { ac_status=$?; break; }
+ done
+fi
+
+test -n "$ac_init_help" && exit $ac_status
+if $ac_init_version; then
+ cat <<\_ACEOF
+configure
+generated by GNU Autoconf 2.61
+
+Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,
+2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc.
+This configure script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it.
+_ACEOF
+ exit
+fi
+cat >config.log <<_ACEOF
+This file contains any messages produced by compilers while
+running configure, to aid debugging if configure makes a mistake.
+
+It was created by $as_me, which was
+generated by GNU Autoconf 2.61. Invocation command line was
+
+ $ $0 $@
+
+_ACEOF
+exec 5>>config.log
+{
+cat <<_ASUNAME
+## --------- ##
+## Platform. ##
+## --------- ##
+
+hostname = `(hostname || uname -n) 2>/dev/null | sed 1q`
+uname -m = `(uname -m) 2>/dev/null || echo unknown`
+uname -r = `(uname -r) 2>/dev/null || echo unknown`
+uname -s = `(uname -s) 2>/dev/null || echo unknown`
+uname -v = `(uname -v) 2>/dev/null || echo unknown`
+
+/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown`
+/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown`
+
+/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown`
+/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown`
+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown`
+/usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown`
+/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown`
+/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown`
+/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown`
+
+_ASUNAME
+
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ echo "PATH: $as_dir"
+done
+IFS=$as_save_IFS
+
+} >&5
+
+cat >&5 <<_ACEOF
+
+
+## ----------- ##
+## Core tests. ##
+## ----------- ##
+
+_ACEOF
+
+
+# Keep a trace of the command line.
+# Strip out --no-create and --no-recursion so they do not pile up.
+# Strip out --silent because we don't want to record it for future runs.
+# Also quote any args containing shell meta-characters.
+# Make two passes to allow for proper duplicate-argument suppression.
+ac_configure_args=
+ac_configure_args0=
+ac_configure_args1=
+ac_must_keep_next=false
+for ac_pass in 1 2
+do
+ for ac_arg
+ do
+ case $ac_arg in
+ -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;;
+ -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+ | -silent | --silent | --silen | --sile | --sil)
+ continue ;;
+ *\'*)
+ ac_arg=`echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;;
+ esac
+ case $ac_pass in
+ 1) ac_configure_args0="$ac_configure_args0 '$ac_arg'" ;;
+ 2)
+ ac_configure_args1="$ac_configure_args1 '$ac_arg'"
+ if test $ac_must_keep_next = true; then
+ ac_must_keep_next=false # Got value, back to normal.
+ else
+ case $ac_arg in
+ *=* | --config-cache | -C | -disable-* | --disable-* \
+ | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \
+ | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \
+ | -with-* | --with-* | -without-* | --without-* | --x)
+ case "$ac_configure_args0 " in
+ "$ac_configure_args1"*" '$ac_arg' "* ) continue ;;
+ esac
+ ;;
+ -* ) ac_must_keep_next=true ;;
+ esac
+ fi
+ ac_configure_args="$ac_configure_args '$ac_arg'"
+ ;;
+ esac
+ done
+done
+$as_unset ac_configure_args0 || test "${ac_configure_args0+set}" != set || { ac_configure_args0=; export ac_configure_args0; }
+$as_unset ac_configure_args1 || test "${ac_configure_args1+set}" != set || { ac_configure_args1=; export ac_configure_args1; }
+
+# When interrupted or exit'd, cleanup temporary files, and complete
+# config.log. We remove comments because anyway the quotes in there
+# would cause problems or look ugly.
+# WARNING: Use '\'' to represent an apostrophe within the trap.
+# WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug.
+trap 'exit_status=$?
+ # Save into config.log some information that might help in debugging.
+ {
+ echo
+
+ cat <<\_ASBOX
+## ---------------- ##
+## Cache variables. ##
+## ---------------- ##
+_ASBOX
+ echo
+ # The following way of writing the cache mishandles newlines in values,
+(
+ for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do
+ eval ac_val=\$$ac_var
+ case $ac_val in #(
+ *${as_nl}*)
+ case $ac_var in #(
+ *_cv_*) { echo "$as_me:$LINENO: WARNING: Cache variable $ac_var contains a newline." >&5
+echo "$as_me: WARNING: Cache variable $ac_var contains a newline." >&2;} ;;
+ esac
+ case $ac_var in #(
+ _ | IFS | as_nl) ;; #(
+ *) $as_unset $ac_var ;;
+ esac ;;
+ esac
+ done
+ (set) 2>&1 |
+ case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #(
+ *${as_nl}ac_space=\ *)
+ sed -n \
+ "s/'\''/'\''\\\\'\'''\''/g;
+ s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p"
+ ;; #(
+ *)
+ sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p"
+ ;;
+ esac |
+ sort
+)
+ echo
+
+ cat <<\_ASBOX
+## ----------------- ##
+## Output variables. ##
+## ----------------- ##
+_ASBOX
+ echo
+ for ac_var in $ac_subst_vars
+ do
+ eval ac_val=\$$ac_var
+ case $ac_val in
+ *\'\''*) ac_val=`echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;;
+ esac
+ echo "$ac_var='\''$ac_val'\''"
+ done | sort
+ echo
+
+ if test -n "$ac_subst_files"; then
+ cat <<\_ASBOX
+## ------------------- ##
+## File substitutions. ##
+## ------------------- ##
+_ASBOX
+ echo
+ for ac_var in $ac_subst_files
+ do
+ eval ac_val=\$$ac_var
+ case $ac_val in
+ *\'\''*) ac_val=`echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;;
+ esac
+ echo "$ac_var='\''$ac_val'\''"
+ done | sort
+ echo
+ fi
+
+ if test -s confdefs.h; then
+ cat <<\_ASBOX
+## ----------- ##
+## confdefs.h. ##
+## ----------- ##
+_ASBOX
+ echo
+ cat confdefs.h
+ echo
+ fi
+ test "$ac_signal" != 0 &&
+ echo "$as_me: caught signal $ac_signal"
+ echo "$as_me: exit $exit_status"
+ } >&5
+ rm -f core *.core core.conftest.* &&
+ rm -f -r conftest* confdefs* conf$$* $ac_clean_files &&
+ exit $exit_status
+' 0
+for ac_signal in 1 2 13 15; do
+ trap 'ac_signal='$ac_signal'; { (exit 1); exit 1; }' $ac_signal
+done
+ac_signal=0
+
+# confdefs.h avoids OS command line length limits that DEFS can exceed.
+rm -f -r conftest* confdefs.h
+
+# Predefined preprocessor variables.
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_NAME "$PACKAGE_NAME"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_TARNAME "$PACKAGE_TARNAME"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_VERSION "$PACKAGE_VERSION"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_STRING "$PACKAGE_STRING"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT"
+_ACEOF
+
+
+# Let the site file select an alternate cache file if it wants to.
+# Prefer explicitly selected file to automatically selected ones.
+if test -n "$CONFIG_SITE"; then
+ set x "$CONFIG_SITE"
+elif test "x$prefix" != xNONE; then
+ set x "$prefix/share/config.site" "$prefix/etc/config.site"
+else
+ set x "$ac_default_prefix/share/config.site" \
+ "$ac_default_prefix/etc/config.site"
+fi
+shift
+for ac_site_file
+do
+ if test -r "$ac_site_file"; then
+ { echo "$as_me:$LINENO: loading site script $ac_site_file" >&5
+echo "$as_me: loading site script $ac_site_file" >&6;}
+ sed 's/^/| /' "$ac_site_file" >&5
+ . "$ac_site_file"
+ fi
+done
+
+if test -r "$cache_file"; then
+ # Some versions of bash will fail to source /dev/null (special
+ # files actually), so we avoid doing that.
+ if test -f "$cache_file"; then
+ { echo "$as_me:$LINENO: loading cache $cache_file" >&5
+echo "$as_me: loading cache $cache_file" >&6;}
+ case $cache_file in
+ [\\/]* | ?:[\\/]* ) . "$cache_file";;
+ *) . "./$cache_file";;
+ esac
+ fi
+else
+ { echo "$as_me:$LINENO: creating cache $cache_file" >&5
+echo "$as_me: creating cache $cache_file" >&6;}
+ >$cache_file
+fi
+
+# Check that the precious variables saved in the cache have kept the same
+# value.
+ac_cache_corrupted=false
+for ac_var in $ac_precious_vars; do
+ eval ac_old_set=\$ac_cv_env_${ac_var}_set
+ eval ac_new_set=\$ac_env_${ac_var}_set
+ eval ac_old_val=\$ac_cv_env_${ac_var}_value
+ eval ac_new_val=\$ac_env_${ac_var}_value
+ case $ac_old_set,$ac_new_set in
+ set,)
+ { echo "$as_me:$LINENO: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5
+echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;}
+ ac_cache_corrupted=: ;;
+ ,set)
+ { echo "$as_me:$LINENO: error: \`$ac_var' was not set in the previous run" >&5
+echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;}
+ ac_cache_corrupted=: ;;
+ ,);;
+ *)
+ if test "x$ac_old_val" != "x$ac_new_val"; then
+ { echo "$as_me:$LINENO: error: \`$ac_var' has changed since the previous run:" >&5
+echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;}
+ { echo "$as_me:$LINENO: former value: $ac_old_val" >&5
+echo "$as_me: former value: $ac_old_val" >&2;}
+ { echo "$as_me:$LINENO: current value: $ac_new_val" >&5
+echo "$as_me: current value: $ac_new_val" >&2;}
+ ac_cache_corrupted=:
+ fi;;
+ esac
+ # Pass precious variables to config.status.
+ if test "$ac_new_set" = set; then
+ case $ac_new_val in
+ *\'*) ac_arg=$ac_var=`echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;;
+ *) ac_arg=$ac_var=$ac_new_val ;;
+ esac
+ case " $ac_configure_args " in
+ *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy.
+ *) ac_configure_args="$ac_configure_args '$ac_arg'" ;;
+ esac
+ fi
+done
+if $ac_cache_corrupted; then
+ { echo "$as_me:$LINENO: error: changes in the environment can compromise the build" >&5
+echo "$as_me: error: changes in the environment can compromise the build" >&2;}
+ { { echo "$as_me:$LINENO: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&5
+echo "$as_me: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&2;}
+ { (exit 1); exit 1; }; }
+fi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+
+
+ac_aux_dir=
+for ac_dir in "$srcdir" "$srcdir/.." "$srcdir/../.."; do
+ if test -f "$ac_dir/install-sh"; then
+ ac_aux_dir=$ac_dir
+ ac_install_sh="$ac_aux_dir/install-sh -c"
+ break
+ elif test -f "$ac_dir/install.sh"; then
+ ac_aux_dir=$ac_dir
+ ac_install_sh="$ac_aux_dir/install.sh -c"
+ break
+ elif test -f "$ac_dir/shtool"; then
+ ac_aux_dir=$ac_dir
+ ac_install_sh="$ac_aux_dir/shtool install -c"
+ break
+ fi
+done
+if test -z "$ac_aux_dir"; then
+ { { echo "$as_me:$LINENO: error: cannot find install-sh or install.sh in \"$srcdir\" \"$srcdir/..\" \"$srcdir/../..\"" >&5
+echo "$as_me: error: cannot find install-sh or install.sh in \"$srcdir\" \"$srcdir/..\" \"$srcdir/../..\"" >&2;}
+ { (exit 1); exit 1; }; }
+fi
+
+# These three variables are undocumented and unsupported,
+# and are intended to be withdrawn in a future Autoconf release.
+# They can cause serious problems if a builder's source tree is in a directory
+# whose full name contains unusual characters.
+ac_config_guess="$SHELL $ac_aux_dir/config.guess" # Please don't use this var.
+ac_config_sub="$SHELL $ac_aux_dir/config.sub" # Please don't use this var.
+ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var.
+
+
+# Make sure we can run config.sub.
+$SHELL "$ac_aux_dir/config.sub" sun4 >/dev/null 2>&1 ||
+ { { echo "$as_me:$LINENO: error: cannot run $SHELL $ac_aux_dir/config.sub" >&5
+echo "$as_me: error: cannot run $SHELL $ac_aux_dir/config.sub" >&2;}
+ { (exit 1); exit 1; }; }
+
+{ echo "$as_me:$LINENO: checking build system type" >&5
+echo $ECHO_N "checking build system type... $ECHO_C" >&6; }
+if test "${ac_cv_build+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_build_alias=$build_alias
+test "x$ac_build_alias" = x &&
+ ac_build_alias=`$SHELL "$ac_aux_dir/config.guess"`
+test "x$ac_build_alias" = x &&
+ { { echo "$as_me:$LINENO: error: cannot guess build type; you must specify one" >&5
+echo "$as_me: error: cannot guess build type; you must specify one" >&2;}
+ { (exit 1); exit 1; }; }
+ac_cv_build=`$SHELL "$ac_aux_dir/config.sub" $ac_build_alias` ||
+ { { echo "$as_me:$LINENO: error: $SHELL $ac_aux_dir/config.sub $ac_build_alias failed" >&5
+echo "$as_me: error: $SHELL $ac_aux_dir/config.sub $ac_build_alias failed" >&2;}
+ { (exit 1); exit 1; }; }
+
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_build" >&5
+echo "${ECHO_T}$ac_cv_build" >&6; }
+case $ac_cv_build in
+*-*-*) ;;
+*) { { echo "$as_me:$LINENO: error: invalid value of canonical build" >&5
+echo "$as_me: error: invalid value of canonical build" >&2;}
+ { (exit 1); exit 1; }; };;
+esac
+build=$ac_cv_build
+ac_save_IFS=$IFS; IFS='-'
+set x $ac_cv_build
+shift
+build_cpu=$1
+build_vendor=$2
+shift; shift
+# Remember, the first character of IFS is used to create $*,
+# except with old shells:
+build_os=$*
+IFS=$ac_save_IFS
+case $build_os in *\ *) build_os=`echo "$build_os" | sed 's/ /-/g'`;; esac
+
+
+{ echo "$as_me:$LINENO: checking host system type" >&5
+echo $ECHO_N "checking host system type... $ECHO_C" >&6; }
+if test "${ac_cv_host+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test "x$host_alias" = x; then
+ ac_cv_host=$ac_cv_build
+else
+ ac_cv_host=`$SHELL "$ac_aux_dir/config.sub" $host_alias` ||
+ { { echo "$as_me:$LINENO: error: $SHELL $ac_aux_dir/config.sub $host_alias failed" >&5
+echo "$as_me: error: $SHELL $ac_aux_dir/config.sub $host_alias failed" >&2;}
+ { (exit 1); exit 1; }; }
+fi
+
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_host" >&5
+echo "${ECHO_T}$ac_cv_host" >&6; }
+case $ac_cv_host in
+*-*-*) ;;
+*) { { echo "$as_me:$LINENO: error: invalid value of canonical host" >&5
+echo "$as_me: error: invalid value of canonical host" >&2;}
+ { (exit 1); exit 1; }; };;
+esac
+host=$ac_cv_host
+ac_save_IFS=$IFS; IFS='-'
+set x $ac_cv_host
+shift
+host_cpu=$1
+host_vendor=$2
+shift; shift
+# Remember, the first character of IFS is used to create $*,
+# except with old shells:
+host_os=$*
+IFS=$ac_save_IFS
+case $host_os in *\ *) host_os=`echo "$host_os" | sed 's/ /-/g'`;; esac
+
+
+case "$host_os" in
+gnu*) ;;
+none) { { echo "$as_me:$LINENO: error:
+*** You must specify a host of $host_cpu-gnu or $host_cpu-$host_vendor-gnu
+*** to configure; you will need to use the same host specification
+*** to configure other packages for the GNU/Hurd system." >&5
+echo "$as_me: error:
+*** You must specify a host of $host_cpu-gnu or $host_cpu-$host_vendor-gnu
+*** to configure; you will need to use the same host specification
+*** to configure other packages for the GNU/Hurd system." >&2;}
+ { (exit 1); exit 1; }; } ;;
+*) { { echo "$as_me:$LINENO: error: this is the gnu os, host cannot be $host_os
+*** Host configuration must be \`MACHINE-gnu' or \`MACHINE-VENDOR-gnu'.
+*** To cross-compile, you must specify both --host and --build;
+*** for example \`--build=$host --host=$host_cpu-gnu'.
+*** Run $0 --help for more information." >&5
+echo "$as_me: error: this is the gnu os, host cannot be $host_os
+*** Host configuration must be \`MACHINE-gnu' or \`MACHINE-VENDOR-gnu'.
+*** To cross-compile, you must specify both --host and --build;
+*** for example \`--build=$host --host=$host_cpu-gnu'.
+*** Run $0 --help for more information." >&2;}
+ { (exit 1); exit 1; }; } ;;
+esac
+
+case "$host_cpu" in
+alpha*)
+ asm_syntax=alpha
+ ;;
+arm*)
+ asm_syntax=arm
+ ;;
+m68k | m680?0)
+ asm_syntax=m68k
+ ;;
+mips*)
+ asm_syntax=mips
+ ;;
+i?86)
+ asm_syntax=i386
+ ;;
+powerpc*)
+ asm_syntax=ppc
+ ;;
+sparc64* | ultrasparc*)
+ asm_syntax=sparc64
+ ;;
+sparc*)
+ asm_syntax=sparc
+ ;;
+*)
+ asm_syntax="$host_cpu"
+ ;;
+esac
+
+
+test -r "$srcdir/libthreads/$asm_syntax/cthreads.h" || {
+ { echo "$as_me:$LINENO: WARNING: unsupported CPU type $host_cpu" >&5
+echo "$as_me: WARNING: unsupported CPU type $host_cpu" >&2;}
+}
+
+# Check whether --enable-profile was given.
+if test "${enable_profile+set}" = set; then
+ enableval=$enable_profile;
+fi
+
+
+
+# Check whether --enable-static-progs was given.
+if test "${enable_static_progs+set}" = set; then
+ enableval=$enable_static_progs;
+fi
+
+case "$enable_static_progs" in
+'no') enable_static_progs= ;; # we got --disable-static
+'') enable_static_progs='ext2fs,ufs' ;;
+esac
+# Convert comma/space-separated list into space-separated list.
+enable_static_progs=`echo "$enable_static_progs" | sed 's/[, ][, ]*/ /g'`
+
+
+# Don't needlessly overwrite files that whose contents haven't changed. This
+# helps for avoinding unneccessary recompilation cycles when keeping
+# cross-compilation toolchains up-to-date. Thus, unconditionally use the
+# supplied `install-sh', as the GNU Coreutils one doesn't provide this
+# functionality yet (TODO: change that). TODO: $ac_abs_top_builddir et al. are
+# not yet available here, that's why we use `readlink' (but only if available).
+INSTALL="$SHELL $(readlink -f "$ac_install_sh")"\ -C || unset INSTALL
+# Find a good install program. We prefer a C program (faster),
+# so one script is as good as another. But avoid the broken or
+# incompatible versions:
+# SysV /etc/install, /usr/sbin/install
+# SunOS /usr/etc/install
+# IRIX /sbin/install
+# AIX /bin/install
+# AmigaOS /C/install, which installs bootblocks on floppy discs
+# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag
+# AFS /usr/afsws/bin/install, which mishandles nonexistent args
+# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff"
+# OS/2's system install, which has a completely different semantic
+# ./install, which can be erroneously created by make from ./install.sh.
+{ echo "$as_me:$LINENO: checking for a BSD-compatible install" >&5
+echo $ECHO_N "checking for a BSD-compatible install... $ECHO_C" >&6; }
+if test -z "$INSTALL"; then
+if test "${ac_cv_path_install+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ # Account for people who put trailing slashes in PATH elements.
+case $as_dir/ in
+ ./ | .// | /cC/* | \
+ /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \
+ ?:\\/os2\\/install\\/* | ?:\\/OS2\\/INSTALL\\/* | \
+ /usr/ucb/* ) ;;
+ *)
+ # OSF1 and SCO ODT 3.0 have their own names for install.
+ # Don't use installbsd from OSF since it installs stuff as root
+ # by default.
+ for ac_prog in ginstall scoinst install; do
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_prog$ac_exec_ext" && $as_test_x "$as_dir/$ac_prog$ac_exec_ext"; }; then
+ if test $ac_prog = install &&
+ grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
+ # AIX install. It has an incompatible calling convention.
+ :
+ elif test $ac_prog = install &&
+ grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
+ # program-specific install script used by HP pwplus--don't use.
+ :
+ else
+ ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c"
+ break 3
+ fi
+ fi
+ done
+ done
+ ;;
+esac
+done
+IFS=$as_save_IFS
+
+
+fi
+ if test "${ac_cv_path_install+set}" = set; then
+ INSTALL=$ac_cv_path_install
+ else
+ # As a last resort, use the slow shell script. Don't cache a
+ # value for INSTALL within a source directory, because that will
+ # break other packages using the cache if that directory is
+ # removed, or if the value is a relative name.
+ INSTALL=$ac_install_sh
+ fi
+fi
+{ echo "$as_me:$LINENO: result: $INSTALL" >&5
+echo "${ECHO_T}$INSTALL" >&6; }
+
+# Use test -z because SunOS4 sh mishandles braces in ${var-val}.
+# It thinks the first close brace ends the variable substitution.
+test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}'
+
+test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}'
+
+test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644'
+
+for ac_prog in gawk mawk nawk awk
+do
+ # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }
+if test "${ac_cv_prog_AWK+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$AWK"; then
+ ac_cv_prog_AWK="$AWK" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_AWK="$ac_prog"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+AWK=$ac_cv_prog_AWK
+if test -n "$AWK"; then
+ { echo "$as_me:$LINENO: result: $AWK" >&5
+echo "${ECHO_T}$AWK" >&6; }
+else
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+
+ test -n "$AWK" && break
+done
+
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}gcc; ac_word=$2
+{ echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }
+if test "${ac_cv_prog_CC+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_CC="${ac_tool_prefix}gcc"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ { echo "$as_me:$LINENO: result: $CC" >&5
+echo "${ECHO_T}$CC" >&6; }
+else
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_CC"; then
+ ac_ct_CC=$CC
+ # Extract the first word of "gcc", so it can be a program name with args.
+set dummy gcc; ac_word=$2
+{ echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }
+if test "${ac_cv_prog_ac_ct_CC+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$ac_ct_CC"; then
+ ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_ac_ct_CC="gcc"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+ { echo "$as_me:$LINENO: result: $ac_ct_CC" >&5
+echo "${ECHO_T}$ac_ct_CC" >&6; }
+else
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+ if test "x$ac_ct_CC" = x; then
+ CC=""
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ echo "$as_me:$LINENO: WARNING: In the future, Autoconf will not detect cross-tools
+whose name does not start with the host triplet. If you think this
+configuration is useful to you, please write to autoconf@gnu.org." >&5
+echo "$as_me: WARNING: In the future, Autoconf will not detect cross-tools
+whose name does not start with the host triplet. If you think this
+configuration is useful to you, please write to autoconf@gnu.org." >&2;}
+ac_tool_warned=yes ;;
+esac
+ CC=$ac_ct_CC
+ fi
+else
+ CC="$ac_cv_prog_CC"
+fi
+
+if test -z "$CC"; then
+ if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}cc; ac_word=$2
+{ echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }
+if test "${ac_cv_prog_CC+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_CC="${ac_tool_prefix}cc"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ { echo "$as_me:$LINENO: result: $CC" >&5
+echo "${ECHO_T}$CC" >&6; }
+else
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+
+ fi
+fi
+if test -z "$CC"; then
+ # Extract the first word of "cc", so it can be a program name with args.
+set dummy cc; ac_word=$2
+{ echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }
+if test "${ac_cv_prog_CC+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+ ac_prog_rejected=no
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then
+ ac_prog_rejected=yes
+ continue
+ fi
+ ac_cv_prog_CC="cc"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+if test $ac_prog_rejected = yes; then
+ # We found a bogon in the path, so make sure we never use it.
+ set dummy $ac_cv_prog_CC
+ shift
+ if test $# != 0; then
+ # We chose a different compiler from the bogus one.
+ # However, it has the same basename, so the bogon will be chosen
+ # first if we set CC to just the basename; use the full file name.
+ shift
+ ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@"
+ fi
+fi
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ { echo "$as_me:$LINENO: result: $CC" >&5
+echo "${ECHO_T}$CC" >&6; }
+else
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+
+fi
+if test -z "$CC"; then
+ if test -n "$ac_tool_prefix"; then
+ for ac_prog in cl.exe
+ do
+ # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
+set dummy $ac_tool_prefix$ac_prog; ac_word=$2
+{ echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }
+if test "${ac_cv_prog_CC+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_CC="$ac_tool_prefix$ac_prog"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ { echo "$as_me:$LINENO: result: $CC" >&5
+echo "${ECHO_T}$CC" >&6; }
+else
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+
+ test -n "$CC" && break
+ done
+fi
+if test -z "$CC"; then
+ ac_ct_CC=$CC
+ for ac_prog in cl.exe
+do
+ # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }
+if test "${ac_cv_prog_ac_ct_CC+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$ac_ct_CC"; then
+ ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_ac_ct_CC="$ac_prog"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+ { echo "$as_me:$LINENO: result: $ac_ct_CC" >&5
+echo "${ECHO_T}$ac_ct_CC" >&6; }
+else
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+
+ test -n "$ac_ct_CC" && break
+done
+
+ if test "x$ac_ct_CC" = x; then
+ CC=""
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ echo "$as_me:$LINENO: WARNING: In the future, Autoconf will not detect cross-tools
+whose name does not start with the host triplet. If you think this
+configuration is useful to you, please write to autoconf@gnu.org." >&5
+echo "$as_me: WARNING: In the future, Autoconf will not detect cross-tools
+whose name does not start with the host triplet. If you think this
+configuration is useful to you, please write to autoconf@gnu.org." >&2;}
+ac_tool_warned=yes ;;
+esac
+ CC=$ac_ct_CC
+ fi
+fi
+
+fi
+
+
+test -z "$CC" && { { echo "$as_me:$LINENO: error: no acceptable C compiler found in \$PATH
+See \`config.log' for more details." >&5
+echo "$as_me: error: no acceptable C compiler found in \$PATH
+See \`config.log' for more details." >&2;}
+ { (exit 1); exit 1; }; }
+
+# Provide some information about the compiler.
+echo "$as_me:$LINENO: checking for C compiler version" >&5
+ac_compiler=`set X $ac_compile; echo $2`
+{ (ac_try="$ac_compiler --version >&5"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compiler --version >&5") 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }
+{ (ac_try="$ac_compiler -v >&5"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compiler -v >&5") 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }
+{ (ac_try="$ac_compiler -V >&5"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compiler -V >&5") 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }
+
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files a.out a.exe b.out"
+# Try to create an executable without -o first, disregard a.out.
+# It will help us diagnose broken compilers, and finding out an intuition
+# of exeext.
+{ echo "$as_me:$LINENO: checking for C compiler default output file name" >&5
+echo $ECHO_N "checking for C compiler default output file name... $ECHO_C" >&6; }
+ac_link_default=`echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'`
+#
+# List of possible output files, starting from the most likely.
+# The algorithm is not robust to junk in `.', hence go to wildcards (a.*)
+# only as a last resort. b.out is created by i960 compilers.
+ac_files='a_out.exe a.exe conftest.exe a.out conftest a.* conftest.* b.out'
+#
+# The IRIX 6 linker writes into existing files which may not be
+# executable, retaining their permissions. Remove them first so a
+# subsequent execution test works.
+ac_rmfiles=
+for ac_file in $ac_files
+do
+ case $ac_file in
+ *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.o | *.obj ) ;;
+ * ) ac_rmfiles="$ac_rmfiles $ac_file";;
+ esac
+done
+rm -f $ac_rmfiles
+
+if { (ac_try="$ac_link_default"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link_default") 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; then
+ # Autoconf-2.13 could set the ac_cv_exeext variable to `no'.
+# So ignore a value of `no', otherwise this would lead to `EXEEXT = no'
+# in a Makefile. We should not override ac_cv_exeext if it was cached,
+# so that the user can short-circuit this test for compilers unknown to
+# Autoconf.
+for ac_file in $ac_files ''
+do
+ test -f "$ac_file" || continue
+ case $ac_file in
+ *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.o | *.obj )
+ ;;
+ [ab].out )
+ # We found the default executable, but exeext='' is most
+ # certainly right.
+ break;;
+ *.* )
+ if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no;
+ then :; else
+ ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+ fi
+ # We set ac_cv_exeext here because the later test for it is not
+ # safe: cross compilers may not add the suffix if given an `-o'
+ # argument, so we may need to know it at that point already.
+ # Even if this section looks crufty: it has the advantage of
+ # actually working.
+ break;;
+ * )
+ break;;
+ esac
+done
+test "$ac_cv_exeext" = no && ac_cv_exeext=
+
+else
+ ac_file=''
+fi
+
+{ echo "$as_me:$LINENO: result: $ac_file" >&5
+echo "${ECHO_T}$ac_file" >&6; }
+if test -z "$ac_file"; then
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { echo "$as_me:$LINENO: error: C compiler cannot create executables
+See \`config.log' for more details." >&5
+echo "$as_me: error: C compiler cannot create executables
+See \`config.log' for more details." >&2;}
+ { (exit 77); exit 77; }; }
+fi
+
+ac_exeext=$ac_cv_exeext
+
+# Check that the compiler produces executables we can run. If not, either
+# the compiler is broken, or we cross compile.
+{ echo "$as_me:$LINENO: checking whether the C compiler works" >&5
+echo $ECHO_N "checking whether the C compiler works... $ECHO_C" >&6; }
+# FIXME: These cross compiler hacks should be removed for Autoconf 3.0
+# If not cross compiling, check that we can run a simple program.
+if test "$cross_compiling" != yes; then
+ if { ac_try='./$ac_file'
+ { (case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_try") 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ cross_compiling=no
+ else
+ if test "$cross_compiling" = maybe; then
+ cross_compiling=yes
+ else
+ { { echo "$as_me:$LINENO: error: cannot run C compiled programs.
+If you meant to cross compile, use \`--host'.
+See \`config.log' for more details." >&5
+echo "$as_me: error: cannot run C compiled programs.
+If you meant to cross compile, use \`--host'.
+See \`config.log' for more details." >&2;}
+ { (exit 1); exit 1; }; }
+ fi
+ fi
+fi
+{ echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6; }
+
+rm -f a.out a.exe conftest$ac_cv_exeext b.out
+ac_clean_files=$ac_clean_files_save
+# Check that the compiler produces executables we can run. If not, either
+# the compiler is broken, or we cross compile.
+{ echo "$as_me:$LINENO: checking whether we are cross compiling" >&5
+echo $ECHO_N "checking whether we are cross compiling... $ECHO_C" >&6; }
+{ echo "$as_me:$LINENO: result: $cross_compiling" >&5
+echo "${ECHO_T}$cross_compiling" >&6; }
+
+{ echo "$as_me:$LINENO: checking for suffix of executables" >&5
+echo $ECHO_N "checking for suffix of executables... $ECHO_C" >&6; }
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; then
+ # If both `conftest.exe' and `conftest' are `present' (well, observable)
+# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will
+# work properly (i.e., refer to `conftest.exe'), while it won't with
+# `rm'.
+for ac_file in conftest.exe conftest conftest.*; do
+ test -f "$ac_file" || continue
+ case $ac_file in
+ *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.o | *.obj ) ;;
+ *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+ break;;
+ * ) break;;
+ esac
+done
+else
+ { { echo "$as_me:$LINENO: error: cannot compute suffix of executables: cannot compile and link
+See \`config.log' for more details." >&5
+echo "$as_me: error: cannot compute suffix of executables: cannot compile and link
+See \`config.log' for more details." >&2;}
+ { (exit 1); exit 1; }; }
+fi
+
+rm -f conftest$ac_cv_exeext
+{ echo "$as_me:$LINENO: result: $ac_cv_exeext" >&5
+echo "${ECHO_T}$ac_cv_exeext" >&6; }
+
+rm -f conftest.$ac_ext
+EXEEXT=$ac_cv_exeext
+ac_exeext=$EXEEXT
+{ echo "$as_me:$LINENO: checking for suffix of object files" >&5
+echo $ECHO_N "checking for suffix of object files... $ECHO_C" >&6; }
+if test "${ac_cv_objext+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.o conftest.obj
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; then
+ for ac_file in conftest.o conftest.obj conftest.*; do
+ test -f "$ac_file" || continue;
+ case $ac_file in
+ *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf ) ;;
+ *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'`
+ break;;
+ esac
+done
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { echo "$as_me:$LINENO: error: cannot compute suffix of object files: cannot compile
+See \`config.log' for more details." >&5
+echo "$as_me: error: cannot compute suffix of object files: cannot compile
+See \`config.log' for more details." >&2;}
+ { (exit 1); exit 1; }; }
+fi
+
+rm -f conftest.$ac_cv_objext conftest.$ac_ext
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_objext" >&5
+echo "${ECHO_T}$ac_cv_objext" >&6; }
+OBJEXT=$ac_cv_objext
+ac_objext=$OBJEXT
+{ echo "$as_me:$LINENO: checking whether we are using the GNU C compiler" >&5
+echo $ECHO_N "checking whether we are using the GNU C compiler... $ECHO_C" >&6; }
+if test "${ac_cv_c_compiler_gnu+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+int
+main ()
+{
+#ifndef __GNUC__
+ choke me
+#endif
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_compiler_gnu=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_compiler_gnu=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ac_cv_c_compiler_gnu=$ac_compiler_gnu
+
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_c_compiler_gnu" >&5
+echo "${ECHO_T}$ac_cv_c_compiler_gnu" >&6; }
+GCC=`test $ac_compiler_gnu = yes && echo yes`
+ac_test_CFLAGS=${CFLAGS+set}
+ac_save_CFLAGS=$CFLAGS
+{ echo "$as_me:$LINENO: checking whether $CC accepts -g" >&5
+echo $ECHO_N "checking whether $CC accepts -g... $ECHO_C" >&6; }
+if test "${ac_cv_prog_cc_g+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_save_c_werror_flag=$ac_c_werror_flag
+ ac_c_werror_flag=yes
+ ac_cv_prog_cc_g=no
+ CFLAGS="-g"
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_cv_prog_cc_g=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ CFLAGS=""
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ :
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_c_werror_flag=$ac_save_c_werror_flag
+ CFLAGS="-g"
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_cv_prog_cc_g=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ ac_c_werror_flag=$ac_save_c_werror_flag
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_prog_cc_g" >&5
+echo "${ECHO_T}$ac_cv_prog_cc_g" >&6; }
+if test "$ac_test_CFLAGS" = set; then
+ CFLAGS=$ac_save_CFLAGS
+elif test $ac_cv_prog_cc_g = yes; then
+ if test "$GCC" = yes; then
+ CFLAGS="-g -O2"
+ else
+ CFLAGS="-g"
+ fi
+else
+ if test "$GCC" = yes; then
+ CFLAGS="-O2"
+ else
+ CFLAGS=
+ fi
+fi
+{ echo "$as_me:$LINENO: checking for $CC option to accept ISO C89" >&5
+echo $ECHO_N "checking for $CC option to accept ISO C89... $ECHO_C" >&6; }
+if test "${ac_cv_prog_cc_c89+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_prog_cc_c89=no
+ac_save_CC=$CC
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <stdarg.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */
+struct buf { int x; };
+FILE * (*rcsopen) (struct buf *, struct stat *, int);
+static char *e (p, i)
+ char **p;
+ int i;
+{
+ return p[i];
+}
+static char *f (char * (*g) (char **, int), char **p, ...)
+{
+ char *s;
+ va_list v;
+ va_start (v,p);
+ s = g (p, va_arg (v,int));
+ va_end (v);
+ return s;
+}
+
+/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has
+ function prototypes and stuff, but not '\xHH' hex character constants.
+ These don't provoke an error unfortunately, instead are silently treated
+ as 'x'. The following induces an error, until -std is added to get
+ proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an
+ array size at least. It's necessary to write '\x00'==0 to get something
+ that's true only with -std. */
+int osf4_cc_array ['\x00' == 0 ? 1 : -1];
+
+/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters
+ inside strings and character constants. */
+#define FOO(x) 'x'
+int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1];
+
+int test (int i, double x);
+struct s1 {int (*f) (int a);};
+struct s2 {int (*f) (double a);};
+int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int);
+int argc;
+char **argv;
+int
+main ()
+{
+return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1];
+ ;
+ return 0;
+}
+_ACEOF
+for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \
+ -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__"
+do
+ CC="$ac_save_CC $ac_arg"
+ rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_cv_prog_cc_c89=$ac_arg
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext
+ test "x$ac_cv_prog_cc_c89" != "xno" && break
+done
+rm -f conftest.$ac_ext
+CC=$ac_save_CC
+
+fi
+# AC_CACHE_VAL
+case "x$ac_cv_prog_cc_c89" in
+ x)
+ { echo "$as_me:$LINENO: result: none needed" >&5
+echo "${ECHO_T}none needed" >&6; } ;;
+ xno)
+ { echo "$as_me:$LINENO: result: unsupported" >&5
+echo "${ECHO_T}unsupported" >&6; } ;;
+ *)
+ CC="$CC $ac_cv_prog_cc_c89"
+ { echo "$as_me:$LINENO: result: $ac_cv_prog_cc_c89" >&5
+echo "${ECHO_T}$ac_cv_prog_cc_c89" >&6; } ;;
+esac
+
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+# Require GCC.
+if test x$GCC != xyes; then
+ { { echo "$as_me:$LINENO: error: this code uses GNU C extensions, you must compile with GCC" >&5
+echo "$as_me: error: this code uses GNU C extensions, you must compile with GCC" >&2;}
+ { (exit 1); exit 1; }; }
+fi
+
+if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}ld", so it can be a program name with args.
+set dummy ${ac_tool_prefix}ld; ac_word=$2
+{ echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }
+if test "${ac_cv_prog_LD+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$LD"; then
+ ac_cv_prog_LD="$LD" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_LD="${ac_tool_prefix}ld"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+LD=$ac_cv_prog_LD
+if test -n "$LD"; then
+ { echo "$as_me:$LINENO: result: $LD" >&5
+echo "${ECHO_T}$LD" >&6; }
+else
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_LD"; then
+ ac_ct_LD=$LD
+ # Extract the first word of "ld", so it can be a program name with args.
+set dummy ld; ac_word=$2
+{ echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }
+if test "${ac_cv_prog_ac_ct_LD+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$ac_ct_LD"; then
+ ac_cv_prog_ac_ct_LD="$ac_ct_LD" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_ac_ct_LD="ld"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_LD=$ac_cv_prog_ac_ct_LD
+if test -n "$ac_ct_LD"; then
+ { echo "$as_me:$LINENO: result: $ac_ct_LD" >&5
+echo "${ECHO_T}$ac_ct_LD" >&6; }
+else
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+ if test "x$ac_ct_LD" = x; then
+ LD=""
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ echo "$as_me:$LINENO: WARNING: In the future, Autoconf will not detect cross-tools
+whose name does not start with the host triplet. If you think this
+configuration is useful to you, please write to autoconf@gnu.org." >&5
+echo "$as_me: WARNING: In the future, Autoconf will not detect cross-tools
+whose name does not start with the host triplet. If you think this
+configuration is useful to you, please write to autoconf@gnu.org." >&2;}
+ac_tool_warned=yes ;;
+esac
+ LD=$ac_ct_LD
+ fi
+else
+ LD="$ac_cv_prog_LD"
+fi
+
+if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}objcopy", so it can be a program name with args.
+set dummy ${ac_tool_prefix}objcopy; ac_word=$2
+{ echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }
+if test "${ac_cv_prog_OBJCOPY+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$OBJCOPY"; then
+ ac_cv_prog_OBJCOPY="$OBJCOPY" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_OBJCOPY="${ac_tool_prefix}objcopy"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+OBJCOPY=$ac_cv_prog_OBJCOPY
+if test -n "$OBJCOPY"; then
+ { echo "$as_me:$LINENO: result: $OBJCOPY" >&5
+echo "${ECHO_T}$OBJCOPY" >&6; }
+else
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_OBJCOPY"; then
+ ac_ct_OBJCOPY=$OBJCOPY
+ # Extract the first word of "objcopy", so it can be a program name with args.
+set dummy objcopy; ac_word=$2
+{ echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }
+if test "${ac_cv_prog_ac_ct_OBJCOPY+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$ac_ct_OBJCOPY"; then
+ ac_cv_prog_ac_ct_OBJCOPY="$ac_ct_OBJCOPY" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_ac_ct_OBJCOPY="objcopy"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_OBJCOPY=$ac_cv_prog_ac_ct_OBJCOPY
+if test -n "$ac_ct_OBJCOPY"; then
+ { echo "$as_me:$LINENO: result: $ac_ct_OBJCOPY" >&5
+echo "${ECHO_T}$ac_ct_OBJCOPY" >&6; }
+else
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+ if test "x$ac_ct_OBJCOPY" = x; then
+ OBJCOPY=""
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ echo "$as_me:$LINENO: WARNING: In the future, Autoconf will not detect cross-tools
+whose name does not start with the host triplet. If you think this
+configuration is useful to you, please write to autoconf@gnu.org." >&5
+echo "$as_me: WARNING: In the future, Autoconf will not detect cross-tools
+whose name does not start with the host triplet. If you think this
+configuration is useful to you, please write to autoconf@gnu.org." >&2;}
+ac_tool_warned=yes ;;
+esac
+ OBJCOPY=$ac_ct_OBJCOPY
+ fi
+else
+ OBJCOPY="$ac_cv_prog_OBJCOPY"
+fi
+
+if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}ar", so it can be a program name with args.
+set dummy ${ac_tool_prefix}ar; ac_word=$2
+{ echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }
+if test "${ac_cv_prog_AR+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$AR"; then
+ ac_cv_prog_AR="$AR" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_AR="${ac_tool_prefix}ar"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+AR=$ac_cv_prog_AR
+if test -n "$AR"; then
+ { echo "$as_me:$LINENO: result: $AR" >&5
+echo "${ECHO_T}$AR" >&6; }
+else
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_AR"; then
+ ac_ct_AR=$AR
+ # Extract the first word of "ar", so it can be a program name with args.
+set dummy ar; ac_word=$2
+{ echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }
+if test "${ac_cv_prog_ac_ct_AR+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$ac_ct_AR"; then
+ ac_cv_prog_ac_ct_AR="$ac_ct_AR" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_ac_ct_AR="ar"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_AR=$ac_cv_prog_ac_ct_AR
+if test -n "$ac_ct_AR"; then
+ { echo "$as_me:$LINENO: result: $ac_ct_AR" >&5
+echo "${ECHO_T}$ac_ct_AR" >&6; }
+else
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+ if test "x$ac_ct_AR" = x; then
+ AR=""
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ echo "$as_me:$LINENO: WARNING: In the future, Autoconf will not detect cross-tools
+whose name does not start with the host triplet. If you think this
+configuration is useful to you, please write to autoconf@gnu.org." >&5
+echo "$as_me: WARNING: In the future, Autoconf will not detect cross-tools
+whose name does not start with the host triplet. If you think this
+configuration is useful to you, please write to autoconf@gnu.org." >&2;}
+ac_tool_warned=yes ;;
+esac
+ AR=$ac_ct_AR
+ fi
+else
+ AR="$ac_cv_prog_AR"
+fi
+
+if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args.
+set dummy ${ac_tool_prefix}ranlib; ac_word=$2
+{ echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }
+if test "${ac_cv_prog_RANLIB+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$RANLIB"; then
+ ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+RANLIB=$ac_cv_prog_RANLIB
+if test -n "$RANLIB"; then
+ { echo "$as_me:$LINENO: result: $RANLIB" >&5
+echo "${ECHO_T}$RANLIB" >&6; }
+else
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_RANLIB"; then
+ ac_ct_RANLIB=$RANLIB
+ # Extract the first word of "ranlib", so it can be a program name with args.
+set dummy ranlib; ac_word=$2
+{ echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }
+if test "${ac_cv_prog_ac_ct_RANLIB+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$ac_ct_RANLIB"; then
+ ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_ac_ct_RANLIB="ranlib"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB
+if test -n "$ac_ct_RANLIB"; then
+ { echo "$as_me:$LINENO: result: $ac_ct_RANLIB" >&5
+echo "${ECHO_T}$ac_ct_RANLIB" >&6; }
+else
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+ if test "x$ac_ct_RANLIB" = x; then
+ RANLIB=""
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ echo "$as_me:$LINENO: WARNING: In the future, Autoconf will not detect cross-tools
+whose name does not start with the host triplet. If you think this
+configuration is useful to you, please write to autoconf@gnu.org." >&5
+echo "$as_me: WARNING: In the future, Autoconf will not detect cross-tools
+whose name does not start with the host triplet. If you think this
+configuration is useful to you, please write to autoconf@gnu.org." >&2;}
+ac_tool_warned=yes ;;
+esac
+ RANLIB=$ac_ct_RANLIB
+ fi
+else
+ RANLIB="$ac_cv_prog_RANLIB"
+fi
+
+if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}mig", so it can be a program name with args.
+set dummy ${ac_tool_prefix}mig; ac_word=$2
+{ echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }
+if test "${ac_cv_prog_MIG+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$MIG"; then
+ ac_cv_prog_MIG="$MIG" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_MIG="${ac_tool_prefix}mig"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+MIG=$ac_cv_prog_MIG
+if test -n "$MIG"; then
+ { echo "$as_me:$LINENO: result: $MIG" >&5
+echo "${ECHO_T}$MIG" >&6; }
+else
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_MIG"; then
+ ac_ct_MIG=$MIG
+ # Extract the first word of "mig", so it can be a program name with args.
+set dummy mig; ac_word=$2
+{ echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }
+if test "${ac_cv_prog_ac_ct_MIG+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$ac_ct_MIG"; then
+ ac_cv_prog_ac_ct_MIG="$ac_ct_MIG" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_ac_ct_MIG="mig"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_MIG=$ac_cv_prog_ac_ct_MIG
+if test -n "$ac_ct_MIG"; then
+ { echo "$as_me:$LINENO: result: $ac_ct_MIG" >&5
+echo "${ECHO_T}$ac_ct_MIG" >&6; }
+else
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+ if test "x$ac_ct_MIG" = x; then
+ MIG=""
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ echo "$as_me:$LINENO: WARNING: In the future, Autoconf will not detect cross-tools
+whose name does not start with the host triplet. If you think this
+configuration is useful to you, please write to autoconf@gnu.org." >&5
+echo "$as_me: WARNING: In the future, Autoconf will not detect cross-tools
+whose name does not start with the host triplet. If you think this
+configuration is useful to you, please write to autoconf@gnu.org." >&2;}
+ac_tool_warned=yes ;;
+esac
+ MIG=$ac_ct_MIG
+ fi
+else
+ MIG="$ac_cv_prog_MIG"
+fi
+
+# Require MiG.
+if test x${MIG} = x; then
+ { { echo "$as_me:$LINENO: error:
+*** You need GNU MiG to compile the GNU Hurd, please see
+*** http://www.gnu.org/software/hurd/mig.html for further details, or
+*** download it directly from the main GNU server (ftp.gnu.org) or any
+*** GNU mirror." >&5
+echo "$as_me: error:
+*** You need GNU MiG to compile the GNU Hurd, please see
+*** http://www.gnu.org/software/hurd/mig.html for further details, or
+*** download it directly from the main GNU server (ftp.gnu.org) or any
+*** GNU mirror." >&2;}
+ { (exit 1); exit 1; }; }
+fi
+
+
+
+# See if there's a separate libcrypt (many systems put crypt there).
+
+{ echo "$as_me:$LINENO: checking for crypt in -lcrypt" >&5
+echo $ECHO_N "checking for crypt in -lcrypt... $ECHO_C" >&6; }
+if test "${ac_cv_lib_crypt_crypt+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lcrypt $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char crypt ();
+int
+main ()
+{
+return crypt ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ ac_cv_lib_crypt_crypt=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_cv_lib_crypt_crypt=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_lib_crypt_crypt" >&5
+echo "${ECHO_T}$ac_cv_lib_crypt_crypt" >&6; }
+if test $ac_cv_lib_crypt_crypt = yes; then
+ LIBCRYPT=-lcrypt
+fi
+
+
+
+# See if mig groks `retcode'.
+{ echo "$as_me:$LINENO: checking whether $MIG supports the retcode keyword" >&5
+echo $ECHO_N "checking whether $MIG supports the retcode keyword... $ECHO_C" >&6; }
+if test "${hurd_cv_mig_retcode+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat > conftest.defs <<\EOF
+#include <mach/std_types.defs>
+#include <mach/mach_types.defs>
+subsystem foobar 1000;
+type reply_port_t = polymorphic | MACH_MSG_TYPE_PORT_SEND_ONCE
+ ctype: mach_port_t;
+simpleroutine foobar_reply (
+ reply_port: reply_port_t;
+ err: kern_return_t, RetCode);
+EOF
+if { ac_try='CC="${CC}" ${MIG-false} -n conftest.defs 1>&5'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ hurd_cv_mig_retcode=yes
+else
+ hurd_cv_mig_retcode=no
+fi
+rm -f conftest*
+fi
+{ echo "$as_me:$LINENO: result: $hurd_cv_mig_retcode" >&5
+echo "${ECHO_T}$hurd_cv_mig_retcode" >&6; }
+if test $hurd_cv_mig_retcode = yes; then
+ cat >>confdefs.h <<\_ACEOF
+#define HAVE_MIG_RETCODE 1
+_ACEOF
+
+fi
+
+# See if --version-script is available.
+{ echo "$as_me:$LINENO: checking for ld --version-script" >&5
+echo $ECHO_N "checking for ld --version-script... $ECHO_C" >&6; }
+if test "${hurd_cv_ld_version_script_option+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat > conftest.c <<\EOF
+void foobar() {}
+EOF
+cat > conftest.map <<\EOF
+VERS_1 {
+ global: sym;
+};
+
+VERS_2 {
+ global: sym;
+} VERS_1;
+EOF
+
+if { ac_try='eval $ac_compile 1>&5'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='${CC-cc} $CFLAGS -shared -o conftest.so conftest.o
+ -nostartfiles -nostdlib
+ -Wl,--version-script,conftest.map
+ 1>&5'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ hurd_cv_ld_version_script_option=yes
+else
+ hurd_cv_ld_version_script_option=no
+fi
+rm -f conftest*
+fi
+{ echo "$as_me:$LINENO: result: $hurd_cv_ld_version_script_option" >&5
+echo "${ECHO_T}$hurd_cv_ld_version_script_option" >&6; }
+
+# See if libc was built with --enable-libio.
+{ echo "$as_me:$LINENO: checking for libio" >&5
+echo $ECHO_N "checking for libio... $ECHO_C" >&6; }
+if test "${hurd_cv_libio+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <stdio.h>
+#ifndef _STDIO_USES_IOSTREAM
+# error No libio found.
+#endif
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ hurd_cv_libio=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ hurd_cv_libio=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ echo "$as_me:$LINENO: result: $hurd_cv_libio" >&5
+echo "${ECHO_T}$hurd_cv_libio" >&6; }
+
+# The versions of the symbols in libthreads have to match those in
+# libc.so. Since the symbols in a libc that includes libio will be
+# versioned differently from the ones in a libc that uses stdio, this
+# isn't easy to accomplish. Instead we leave things unversioned if
+# libio isn't found.
+if test $hurd_cv_libio = yes; then
+ VERSIONING=$hurd_cv_ld_version_script_option
+else
+ VERSIONING=no
+fi
+
+
+# Check if libc contains getgrouplist and/or uselocale.
+
+
+for ac_func in getgrouplist uselocale
+do
+as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for $ac_func" >&5
+echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6; }
+if { as_var=$as_ac_var; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+/* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func.
+ For example, HP-UX 11i <limits.h> declares gettimeofday. */
+#define $ac_func innocuous_$ac_func
+
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char $ac_func (); below.
+ Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+ <limits.h> exists even on freestanding compilers. */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef $ac_func
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char $ac_func ();
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined __stub_$ac_func || defined __stub___$ac_func
+choke me
+#endif
+
+int
+main ()
+{
+return $ac_func ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ eval "$as_ac_var=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_var=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+fi
+ac_res=`eval echo '${'$as_ac_var'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_var'}'` = yes; then
+ cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+done
+
+
+
+# From glibc HEAD, 2007-11-07.
+{ echo "$as_me:$LINENO: checking for -fgnu89-inline" >&5
+echo $ECHO_N "checking for -fgnu89-inline... $ECHO_C" >&6; }
+if test "${libc_cv_gnu89_inline+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat > conftest.c <<EOF
+int foo;
+#ifdef __GNUC_GNU_INLINE__
+main () { return 0;}
+#else
+#error
+#endif
+EOF
+if { ac_try='${CC-cc} $CFLAGS $CPPFLAGS -S -std=gnu99 -fgnu89-inline
+ -o conftest.s conftest.c 1>&5'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }
+then
+ libc_cv_gnu89_inline=yes
+else
+ libc_cv_gnu89_inline=no
+fi
+rm -f conftest*
+fi
+{ echo "$as_me:$LINENO: result: $libc_cv_gnu89_inline" >&5
+echo "${ECHO_T}$libc_cv_gnu89_inline" >&6; }
+if test $libc_cv_gnu89_inline = yes; then
+ libc_cv_gnu89_inline=-fgnu89-inline
+else
+ libc_cv_gnu89_inline=
+fi
+
+
+
+
+# Check whether --with-parted was given.
+if test "${with_parted+set}" = set; then
+ withval=$with_parted;
+else
+ with_parted=yes
+fi
+
+
+# Check whether --enable-boot-store-types was given.
+if test "${enable_boot_store_types+set}" = set; then
+ enableval=$enable_boot_store_types;
+fi
+if test -z "$enable_boot_store_types"; then
+ boot_store_types='device remap gunzip bunzip2'
+
+ # Check for Parted's static store module.
+ if test "x$with_parted" != xno; then
+ save_LDFLAGS="$LDFLAGS"
+ LDFLAGS="$LDFLAGS -static"
+ { echo "$as_me:$LINENO: checking for store_part_open in -lstore_part" >&5
+echo $ECHO_N "checking for store_part_open in -lstore_part... $ECHO_C" >&6; }
+if test "${ac_cv_lib_store_part_store_part_open+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lstore_part -luuid -lstore $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char store_part_open ();
+int
+main ()
+{
+return store_part_open ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ ac_cv_lib_store_part_store_part_open=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_cv_lib_store_part_store_part_open=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_lib_store_part_store_part_open" >&5
+echo "${ECHO_T}$ac_cv_lib_store_part_store_part_open" >&6; }
+if test $ac_cv_lib_store_part_store_part_open = yes; then
+ boot_store_types="$boot_store_types part"
+fi
+
+ LDFLAGS="$save_LDFLAGS"
+ fi
+elif test "x$enable_boot_store_types" = xno; then
+ { echo "$as_me:$LINENO: WARNING: you probably wanted --disable-static-progs" >&5
+echo "$as_me: WARNING: you probably wanted --disable-static-progs" >&2;}
+else
+ boot_store_types="$enable_boot_store_types"
+fi
+{ echo "$as_me:$LINENO: checking boot store types" >&5
+echo $ECHO_N "checking boot store types... $ECHO_C" >&6; }
+{ echo "$as_me:$LINENO: result: $boot_store_types" >&5
+echo "${ECHO_T}$boot_store_types" >&6; }
+
+# Check for ncursesw, which is needed for the console-curses client.
+
+ # Check whether --enable-ncursesw was given.
+if test "${enable_ncursesw+set}" = set; then
+ enableval=$enable_ncursesw;
+else
+ enable_ncursesw=yes
+fi
+
+ if test "$enable_ncursesw" = yes; then
+ { echo "$as_me:$LINENO: checking for initscr in -lncursesw" >&5
+echo $ECHO_N "checking for initscr in -lncursesw... $ECHO_C" >&6; }
+if test "${ac_cv_lib_ncursesw_initscr+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lncursesw $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char initscr ();
+int
+main ()
+{
+return initscr ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ ac_cv_lib_ncursesw_initscr=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_cv_lib_ncursesw_initscr=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_lib_ncursesw_initscr" >&5
+echo "${ECHO_T}$ac_cv_lib_ncursesw_initscr" >&6; }
+if test $ac_cv_lib_ncursesw_initscr = yes; then
+ LIBNCURSESW="-lncursesw"
+fi
+
+ if test "$LIBNCURSESW"; then
+
+# Check whether --with-ncursesw-include-dir was given.
+if test "${with_ncursesw_include_dir+set}" = set; then
+ withval=$with_ncursesw_include_dir;
+fi
+ if test "${with_ncursesw_include_dir+set}" = set; then
+ { echo "$as_me:$LINENO: checking for ncursesw include dir" >&5
+echo $ECHO_N "checking for ncursesw include dir... $ECHO_C" >&6; }
+ case "$with_ncursesw_include_dir" in
+ no|none)
+ hurd_cv_includedir_ncursesw=none;;
+ *)
+ hurd_cv_includedir_ncursesw="$with_ncursesw_include_dir";;
+ esac
+ { echo "$as_me:$LINENO: result: $hurd_cv_includedir_ncursesw" >&5
+echo "${ECHO_T}$hurd_cv_includedir_ncursesw" >&6; }
+ else
+ { echo "$as_me:$LINENO: checking for ncursesw include dir" >&5
+echo $ECHO_N "checking for ncursesw include dir... $ECHO_C" >&6; }
+if test "${hurd_cv_includedir_ncursesw+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ for D in $includedir $prefix/include /local/include /usr/local/include /include /usr/include; do
+ if test -d $D/ncursesw; then
+ hurd_cv_includedir_ncursesw="$D/ncursesw"
+ break
+ fi
+ test "$hurd_cv_includedir_ncursesw" \
+ || hurd_cv_includedir_ncursesw=none
+ done
+fi
+{ echo "$as_me:$LINENO: result: $hurd_cv_includedir_ncursesw" >&5
+echo "${ECHO_T}$hurd_cv_includedir_ncursesw" >&6; }
+ fi
+ if test "$hurd_cv_includedir_ncursesw" = none; then
+ NCURSESW_INCLUDE=""
+ else
+ NCURSESW_INCLUDE="-I$hurd_cv_includedir_ncursesw"
+ fi
+ fi
+ fi
+
+
+
+if test -f ./$ac_unique_file; then
+ # Configuring in source directory; don't create any Makefiles.
+ makefiles=
+else
+ # We are configuring in a separate build tree.
+ # Create a Makefile in the top-level build directory and
+ # one for each subdirectory Makefile in the source.
+ makefiles="Makeconf:build.mkcf.in \
+ `cd $srcdir; for file in Makefile */Makefile; do \
+ echo ${file}:build.mk.in; done`"
+fi
+
+ac_config_files="$ac_config_files config.make ${makefiles}"
+
+cat >confcache <<\_ACEOF
+# This file is a shell script that caches the results of configure
+# tests run on this system so they can be shared between configure
+# scripts and configure runs, see configure's option --config-cache.
+# It is not useful on other systems. If it contains results you don't
+# want to keep, you may remove or edit it.
+#
+# config.status only pays attention to the cache file if you give it
+# the --recheck option to rerun configure.
+#
+# `ac_cv_env_foo' variables (set or unset) will be overridden when
+# loading this file, other *unset* `ac_cv_foo' will be assigned the
+# following values.
+
+_ACEOF
+
+# The following way of writing the cache mishandles newlines in values,
+# but we know of no workaround that is simple, portable, and efficient.
+# So, we kill variables containing newlines.
+# Ultrix sh set writes to stderr and can't be redirected directly,
+# and sets the high bit in the cache file unless we assign to the vars.
+(
+ for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do
+ eval ac_val=\$$ac_var
+ case $ac_val in #(
+ *${as_nl}*)
+ case $ac_var in #(
+ *_cv_*) { echo "$as_me:$LINENO: WARNING: Cache variable $ac_var contains a newline." >&5
+echo "$as_me: WARNING: Cache variable $ac_var contains a newline." >&2;} ;;
+ esac
+ case $ac_var in #(
+ _ | IFS | as_nl) ;; #(
+ *) $as_unset $ac_var ;;
+ esac ;;
+ esac
+ done
+
+ (set) 2>&1 |
+ case $as_nl`(ac_space=' '; set) 2>&1` in #(
+ *${as_nl}ac_space=\ *)
+ # `set' does not quote correctly, so add quotes (double-quote
+ # substitution turns \\\\ into \\, and sed turns \\ into \).
+ sed -n \
+ "s/'/'\\\\''/g;
+ s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p"
+ ;; #(
+ *)
+ # `set' quotes correctly as required by POSIX, so do not add quotes.
+ sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p"
+ ;;
+ esac |
+ sort
+) |
+ sed '
+ /^ac_cv_env_/b end
+ t clear
+ :clear
+ s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/
+ t end
+ s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/
+ :end' >>confcache
+if diff "$cache_file" confcache >/dev/null 2>&1; then :; else
+ if test -w "$cache_file"; then
+ test "x$cache_file" != "x/dev/null" &&
+ { echo "$as_me:$LINENO: updating cache $cache_file" >&5
+echo "$as_me: updating cache $cache_file" >&6;}
+ cat confcache >$cache_file
+ else
+ { echo "$as_me:$LINENO: not updating unwritable cache $cache_file" >&5
+echo "$as_me: not updating unwritable cache $cache_file" >&6;}
+ fi
+fi
+rm -f confcache
+
+test "x$prefix" = xNONE && prefix=$ac_default_prefix
+# Let make expand exec_prefix.
+test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
+
+# Transform confdefs.h into DEFS.
+# Protect against shell expansion while executing Makefile rules.
+# Protect against Makefile macro expansion.
+#
+# If the first sed substitution is executed (which looks for macros that
+# take arguments), then branch to the quote section. Otherwise,
+# look for a macro that doesn't take arguments.
+ac_script='
+t clear
+:clear
+s/^[ ]*#[ ]*define[ ][ ]*\([^ (][^ (]*([^)]*)\)[ ]*\(.*\)/-D\1=\2/g
+t quote
+s/^[ ]*#[ ]*define[ ][ ]*\([^ ][^ ]*\)[ ]*\(.*\)/-D\1=\2/g
+t quote
+b any
+:quote
+s/[ `~#$^&*(){}\\|;'\''"<>?]/\\&/g
+s/\[/\\&/g
+s/\]/\\&/g
+s/\$/$$/g
+H
+:any
+${
+ g
+ s/^\n//
+ s/\n/ /g
+ p
+}
+'
+DEFS=`sed -n "$ac_script" confdefs.h`
+
+
+ac_libobjs=
+ac_ltlibobjs=
+for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue
+ # 1. Remove the extension, and $U if already installed.
+ ac_script='s/\$U\././;s/\.o$//;s/\.obj$//'
+ ac_i=`echo "$ac_i" | sed "$ac_script"`
+ # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR
+ # will be set to the directory where LIBOBJS objects are built.
+ ac_libobjs="$ac_libobjs \${LIBOBJDIR}$ac_i\$U.$ac_objext"
+ ac_ltlibobjs="$ac_ltlibobjs \${LIBOBJDIR}$ac_i"'$U.lo'
+done
+LIBOBJS=$ac_libobjs
+
+LTLIBOBJS=$ac_ltlibobjs
+
+
+
+: ${CONFIG_STATUS=./config.status}
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files $CONFIG_STATUS"
+{ echo "$as_me:$LINENO: creating $CONFIG_STATUS" >&5
+echo "$as_me: creating $CONFIG_STATUS" >&6;}
+cat >$CONFIG_STATUS <<_ACEOF
+#! $SHELL
+# Generated by $as_me.
+# Run this file to recreate the current configuration.
+# Compiler output produced by configure, useful for debugging
+# configure, is in config.log if it exists.
+
+debug=false
+ac_cs_recheck=false
+ac_cs_silent=false
+SHELL=\${CONFIG_SHELL-$SHELL}
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+## --------------------- ##
+## M4sh Initialization. ##
+## --------------------- ##
+
+# Be more Bourne compatible
+DUALCASE=1; export DUALCASE # for MKS sh
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
+ emulate sh
+ NULLCMD=:
+ # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which
+ # is contrary to our usage. Disable this feature.
+ alias -g '${1+"$@"}'='"$@"'
+ setopt NO_GLOB_SUBST
+else
+ case `(set -o) 2>/dev/null` in
+ *posix*) set -o posix ;;
+esac
+
+fi
+
+
+
+
+# PATH needs CR
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+ echo "#! /bin/sh" >conf$$.sh
+ echo "exit 0" >>conf$$.sh
+ chmod +x conf$$.sh
+ if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then
+ PATH_SEPARATOR=';'
+ else
+ PATH_SEPARATOR=:
+ fi
+ rm -f conf$$.sh
+fi
+
+# Support unset when possible.
+if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then
+ as_unset=unset
+else
+ as_unset=false
+fi
+
+
+# IFS
+# We need space, tab and new line, in precisely that order. Quoting is
+# there to prevent editors from complaining about space-tab.
+# (If _AS_PATH_WALK were called with IFS unset, it would disable word
+# splitting by setting IFS to empty value.)
+as_nl='
+'
+IFS=" "" $as_nl"
+
+# Find who we are. Look in the path if we contain no directory separator.
+case $0 in
+ *[\\/]* ) as_myself=$0 ;;
+ *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+done
+IFS=$as_save_IFS
+
+ ;;
+esac
+# We did not find ourselves, most probably we were run as `sh COMMAND'
+# in which case we are not to be found in the path.
+if test "x$as_myself" = x; then
+ as_myself=$0
+fi
+if test ! -f "$as_myself"; then
+ echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2
+ { (exit 1); exit 1; }
+fi
+
+# Work around bugs in pre-3.0 UWIN ksh.
+for as_var in ENV MAIL MAILPATH
+do ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var
+done
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+for as_var in \
+ LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \
+ LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \
+ LC_TELEPHONE LC_TIME
+do
+ if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then
+ eval $as_var=C; export $as_var
+ else
+ ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var
+ fi
+done
+
+# Required to use basename.
+if expr a : '\(a\)' >/dev/null 2>&1 &&
+ test "X`expr 00001 : '.*\(...\)'`" = X001; then
+ as_expr=expr
+else
+ as_expr=false
+fi
+
+if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then
+ as_basename=basename
+else
+ as_basename=false
+fi
+
+
+# Name of the executable.
+as_me=`$as_basename -- "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+ X"$0" : 'X\(//\)$' \| \
+ X"$0" : 'X\(/\)' \| . 2>/dev/null ||
+echo X/"$0" |
+ sed '/^.*\/\([^/][^/]*\)\/*$/{
+ s//\1/
+ q
+ }
+ /^X\/\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\/\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+
+# CDPATH.
+$as_unset CDPATH
+
+
+
+ as_lineno_1=$LINENO
+ as_lineno_2=$LINENO
+ test "x$as_lineno_1" != "x$as_lineno_2" &&
+ test "x`expr $as_lineno_1 + 1`" = "x$as_lineno_2" || {
+
+ # Create $as_me.lineno as a copy of $as_myself, but with $LINENO
+ # uniformly replaced by the line number. The first 'sed' inserts a
+ # line-number line after each line using $LINENO; the second 'sed'
+ # does the real work. The second script uses 'N' to pair each
+ # line-number line with the line containing $LINENO, and appends
+ # trailing '-' during substitution so that $LINENO is not a special
+ # case at line end.
+ # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the
+ # scripts with optimization help from Paolo Bonzini. Blame Lee
+ # E. McMahon (1931-1989) for sed's syntax. :-)
+ sed -n '
+ p
+ /[$]LINENO/=
+ ' <$as_myself |
+ sed '
+ s/[$]LINENO.*/&-/
+ t lineno
+ b
+ :lineno
+ N
+ :loop
+ s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/
+ t loop
+ s/-\n.*//
+ ' >$as_me.lineno &&
+ chmod +x "$as_me.lineno" ||
+ { echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2
+ { (exit 1); exit 1; }; }
+
+ # Don't try to exec as it changes $[0], causing all sort of problems
+ # (the dirname of $[0] is not the place where we might find the
+ # original and so on. Autoconf is especially sensitive to this).
+ . "./$as_me.lineno"
+ # Exit status is that of the last command.
+ exit
+}
+
+
+if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then
+ as_dirname=dirname
+else
+ as_dirname=false
+fi
+
+ECHO_C= ECHO_N= ECHO_T=
+case `echo -n x` in
+-n*)
+ case `echo 'x\c'` in
+ *c*) ECHO_T=' ';; # ECHO_T is single tab character.
+ *) ECHO_C='\c';;
+ esac;;
+*)
+ ECHO_N='-n';;
+esac
+
+if expr a : '\(a\)' >/dev/null 2>&1 &&
+ test "X`expr 00001 : '.*\(...\)'`" = X001; then
+ as_expr=expr
+else
+ as_expr=false
+fi
+
+rm -f conf$$ conf$$.exe conf$$.file
+if test -d conf$$.dir; then
+ rm -f conf$$.dir/conf$$.file
+else
+ rm -f conf$$.dir
+ mkdir conf$$.dir
+fi
+echo >conf$$.file
+if ln -s conf$$.file conf$$ 2>/dev/null; then
+ as_ln_s='ln -s'
+ # ... but there are two gotchas:
+ # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
+ # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
+ # In both cases, we have to default to `cp -p'.
+ ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
+ as_ln_s='cp -p'
+elif ln conf$$.file conf$$ 2>/dev/null; then
+ as_ln_s=ln
+else
+ as_ln_s='cp -p'
+fi
+rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
+rmdir conf$$.dir 2>/dev/null
+
+if mkdir -p . 2>/dev/null; then
+ as_mkdir_p=:
+else
+ test -d ./-p && rmdir ./-p
+ as_mkdir_p=false
+fi
+
+if test -x / >/dev/null 2>&1; then
+ as_test_x='test -x'
+else
+ if ls -dL / >/dev/null 2>&1; then
+ as_ls_L_option=L
+ else
+ as_ls_L_option=
+ fi
+ as_test_x='
+ eval sh -c '\''
+ if test -d "$1"; then
+ test -d "$1/.";
+ else
+ case $1 in
+ -*)set "./$1";;
+ esac;
+ case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in
+ ???[sx]*):;;*)false;;esac;fi
+ '\'' sh
+ '
+fi
+as_executable_p=$as_test_x
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+
+exec 6>&1
+
+# Save the log message, to keep $[0] and so on meaningful, and to
+# report actual input values of CONFIG_FILES etc. instead of their
+# values after options handling.
+ac_log="
+This file was extended by $as_me, which was
+generated by GNU Autoconf 2.61. Invocation command line was
+
+ CONFIG_FILES = $CONFIG_FILES
+ CONFIG_HEADERS = $CONFIG_HEADERS
+ CONFIG_LINKS = $CONFIG_LINKS
+ CONFIG_COMMANDS = $CONFIG_COMMANDS
+ $ $0 $@
+
+on `(hostname || uname -n) 2>/dev/null | sed 1q`
+"
+
+_ACEOF
+
+cat >>$CONFIG_STATUS <<_ACEOF
+# Files that config.status was made for.
+config_files="$ac_config_files"
+
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+ac_cs_usage="\
+\`$as_me' instantiates files from templates according to the
+current configuration.
+
+Usage: $0 [OPTIONS] [FILE]...
+
+ -h, --help print this help, then exit
+ -V, --version print version number and configuration settings, then exit
+ -q, --quiet do not print progress messages
+ -d, --debug don't remove temporary files
+ --recheck update $as_me by reconfiguring in the same conditions
+ --file=FILE[:TEMPLATE]
+ instantiate the configuration file FILE
+
+Configuration files:
+$config_files
+
+Report bugs to <bug-autoconf@gnu.org>."
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF
+ac_cs_version="\\
+config.status
+configured by $0, generated by GNU Autoconf 2.61,
+ with options \\"`echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`\\"
+
+Copyright (C) 2006 Free Software Foundation, Inc.
+This config.status script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it."
+
+ac_pwd='$ac_pwd'
+srcdir='$srcdir'
+INSTALL='$INSTALL'
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+# If no file are specified by the user, then we need to provide default
+# value. By we need to know if files were specified by the user.
+ac_need_defaults=:
+while test $# != 0
+do
+ case $1 in
+ --*=*)
+ ac_option=`expr "X$1" : 'X\([^=]*\)='`
+ ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'`
+ ac_shift=:
+ ;;
+ *)
+ ac_option=$1
+ ac_optarg=$2
+ ac_shift=shift
+ ;;
+ esac
+
+ case $ac_option in
+ # Handling of the options.
+ -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
+ ac_cs_recheck=: ;;
+ --version | --versio | --versi | --vers | --ver | --ve | --v | -V )
+ echo "$ac_cs_version"; exit ;;
+ --debug | --debu | --deb | --de | --d | -d )
+ debug=: ;;
+ --file | --fil | --fi | --f )
+ $ac_shift
+ CONFIG_FILES="$CONFIG_FILES $ac_optarg"
+ ac_need_defaults=false;;
+ --he | --h | --help | --hel | -h )
+ echo "$ac_cs_usage"; exit ;;
+ -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+ | -silent | --silent | --silen | --sile | --sil | --si | --s)
+ ac_cs_silent=: ;;
+
+ # This is an error.
+ -*) { echo "$as_me: error: unrecognized option: $1
+Try \`$0 --help' for more information." >&2
+ { (exit 1); exit 1; }; } ;;
+
+ *) ac_config_targets="$ac_config_targets $1"
+ ac_need_defaults=false ;;
+
+ esac
+ shift
+done
+
+ac_configure_extra_args=
+
+if $ac_cs_silent; then
+ exec 6>/dev/null
+ ac_configure_extra_args="$ac_configure_extra_args --silent"
+fi
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF
+if \$ac_cs_recheck; then
+ echo "running CONFIG_SHELL=$SHELL $SHELL $0 "$ac_configure_args \$ac_configure_extra_args " --no-create --no-recursion" >&6
+ CONFIG_SHELL=$SHELL
+ export CONFIG_SHELL
+ exec $SHELL "$0"$ac_configure_args \$ac_configure_extra_args --no-create --no-recursion
+fi
+
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF
+exec 5>>config.log
+{
+ echo
+ sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX
+## Running $as_me. ##
+_ASBOX
+ echo "$ac_log"
+} >&5
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+
+# Handling of arguments.
+for ac_config_target in $ac_config_targets
+do
+ case $ac_config_target in
+ "config.make") CONFIG_FILES="$CONFIG_FILES config.make" ;;
+ "${makefiles}") CONFIG_FILES="$CONFIG_FILES ${makefiles}" ;;
+
+ *) { { echo "$as_me:$LINENO: error: invalid argument: $ac_config_target" >&5
+echo "$as_me: error: invalid argument: $ac_config_target" >&2;}
+ { (exit 1); exit 1; }; };;
+ esac
+done
+
+
+# If the user did not use the arguments to specify the items to instantiate,
+# then the envvar interface is used. Set only those that are not.
+# We use the long form for the default assignment because of an extremely
+# bizarre bug on SunOS 4.1.3.
+if $ac_need_defaults; then
+ test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files
+fi
+
+# Have a temporary directory for convenience. Make it in the build tree
+# simply because there is no reason against having it here, and in addition,
+# creating and moving files from /tmp can sometimes cause problems.
+# Hook for its removal unless debugging.
+# Note that there is a small window in which the directory will not be cleaned:
+# after its creation but before its name has been assigned to `$tmp'.
+$debug ||
+{
+ tmp=
+ trap 'exit_status=$?
+ { test -z "$tmp" || test ! -d "$tmp" || rm -fr "$tmp"; } && exit $exit_status
+' 0
+ trap '{ (exit 1); exit 1; }' 1 2 13 15
+}
+# Create a (secure) tmp directory for tmp files.
+
+{
+ tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` &&
+ test -n "$tmp" && test -d "$tmp"
+} ||
+{
+ tmp=./conf$$-$RANDOM
+ (umask 077 && mkdir "$tmp")
+} ||
+{
+ echo "$me: cannot create a temporary directory in ." >&2
+ { (exit 1); exit 1; }
+}
+
+#
+# Set up the sed scripts for CONFIG_FILES section.
+#
+
+# No need to generate the scripts if there are no CONFIG_FILES.
+# This happens for instance when ./config.status config.h
+if test -n "$CONFIG_FILES"; then
+
+_ACEOF
+
+
+
+ac_delim='%!_!# '
+for ac_last_try in false false false false false :; do
+ cat >conf$$subs.sed <<_ACEOF
+SHELL!$SHELL$ac_delim
+PATH_SEPARATOR!$PATH_SEPARATOR$ac_delim
+PACKAGE_NAME!$PACKAGE_NAME$ac_delim
+PACKAGE_TARNAME!$PACKAGE_TARNAME$ac_delim
+PACKAGE_VERSION!$PACKAGE_VERSION$ac_delim
+PACKAGE_STRING!$PACKAGE_STRING$ac_delim
+PACKAGE_BUGREPORT!$PACKAGE_BUGREPORT$ac_delim
+exec_prefix!$exec_prefix$ac_delim
+prefix!$prefix$ac_delim
+program_transform_name!$program_transform_name$ac_delim
+bindir!$bindir$ac_delim
+sbindir!$sbindir$ac_delim
+libexecdir!$libexecdir$ac_delim
+datarootdir!$datarootdir$ac_delim
+datadir!$datadir$ac_delim
+sysconfdir!$sysconfdir$ac_delim
+sharedstatedir!$sharedstatedir$ac_delim
+localstatedir!$localstatedir$ac_delim
+includedir!$includedir$ac_delim
+oldincludedir!$oldincludedir$ac_delim
+docdir!$docdir$ac_delim
+infodir!$infodir$ac_delim
+htmldir!$htmldir$ac_delim
+dvidir!$dvidir$ac_delim
+pdfdir!$pdfdir$ac_delim
+psdir!$psdir$ac_delim
+libdir!$libdir$ac_delim
+localedir!$localedir$ac_delim
+mandir!$mandir$ac_delim
+DEFS!$DEFS$ac_delim
+ECHO_C!$ECHO_C$ac_delim
+ECHO_N!$ECHO_N$ac_delim
+ECHO_T!$ECHO_T$ac_delim
+LIBS!$LIBS$ac_delim
+build_alias!$build_alias$ac_delim
+host_alias!$host_alias$ac_delim
+target_alias!$target_alias$ac_delim
+build!$build$ac_delim
+build_cpu!$build_cpu$ac_delim
+build_vendor!$build_vendor$ac_delim
+build_os!$build_os$ac_delim
+host!$host$ac_delim
+host_cpu!$host_cpu$ac_delim
+host_vendor!$host_vendor$ac_delim
+host_os!$host_os$ac_delim
+asm_syntax!$asm_syntax$ac_delim
+enable_profile!$enable_profile$ac_delim
+enable_static_progs!$enable_static_progs$ac_delim
+INSTALL_PROGRAM!$INSTALL_PROGRAM$ac_delim
+INSTALL_SCRIPT!$INSTALL_SCRIPT$ac_delim
+INSTALL_DATA!$INSTALL_DATA$ac_delim
+AWK!$AWK$ac_delim
+CC!$CC$ac_delim
+CFLAGS!$CFLAGS$ac_delim
+LDFLAGS!$LDFLAGS$ac_delim
+CPPFLAGS!$CPPFLAGS$ac_delim
+ac_ct_CC!$ac_ct_CC$ac_delim
+EXEEXT!$EXEEXT$ac_delim
+OBJEXT!$OBJEXT$ac_delim
+LD!$LD$ac_delim
+OBJCOPY!$OBJCOPY$ac_delim
+AR!$AR$ac_delim
+RANLIB!$RANLIB$ac_delim
+MIG!$MIG$ac_delim
+LIBCRYPT!$LIBCRYPT$ac_delim
+VERSIONING!$VERSIONING$ac_delim
+libc_cv_gnu89_inline!$libc_cv_gnu89_inline$ac_delim
+boot_store_types!$boot_store_types$ac_delim
+NCURSESW_INCLUDE!$NCURSESW_INCLUDE$ac_delim
+LIBNCURSESW!$LIBNCURSESW$ac_delim
+LIBOBJS!$LIBOBJS$ac_delim
+LTLIBOBJS!$LTLIBOBJS$ac_delim
+_ACEOF
+
+ if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 72; then
+ break
+ elif $ac_last_try; then
+ { { echo "$as_me:$LINENO: error: could not make $CONFIG_STATUS" >&5
+echo "$as_me: error: could not make $CONFIG_STATUS" >&2;}
+ { (exit 1); exit 1; }; }
+ else
+ ac_delim="$ac_delim!$ac_delim _$ac_delim!! "
+ fi
+done
+
+ac_eof=`sed -n '/^CEOF[0-9]*$/s/CEOF/0/p' conf$$subs.sed`
+if test -n "$ac_eof"; then
+ ac_eof=`echo "$ac_eof" | sort -nru | sed 1q`
+ ac_eof=`expr $ac_eof + 1`
+fi
+
+cat >>$CONFIG_STATUS <<_ACEOF
+cat >"\$tmp/subs-1.sed" <<\CEOF$ac_eof
+/@[a-zA-Z_][a-zA-Z_0-9]*@/!b end
+_ACEOF
+sed '
+s/[,\\&]/\\&/g; s/@/@|#_!!_#|/g
+s/^/s,@/; s/!/@,|#_!!_#|/
+:n
+t n
+s/'"$ac_delim"'$/,g/; t
+s/$/\\/; p
+N; s/^.*\n//; s/[,\\&]/\\&/g; s/@/@|#_!!_#|/g; b n
+' >>$CONFIG_STATUS <conf$$subs.sed
+rm -f conf$$subs.sed
+cat >>$CONFIG_STATUS <<_ACEOF
+:end
+s/|#_!!_#|//g
+CEOF$ac_eof
+_ACEOF
+
+
+# VPATH may cause trouble with some makes, so we remove $(srcdir),
+# ${srcdir} and @srcdir@ from VPATH if srcdir is ".", strip leading and
+# trailing colons and then remove the whole line if VPATH becomes empty
+# (actually we leave an empty line to preserve line numbers).
+if test "x$srcdir" = x.; then
+ ac_vpsub='/^[ ]*VPATH[ ]*=/{
+s/:*\$(srcdir):*/:/
+s/:*\${srcdir}:*/:/
+s/:*@srcdir@:*/:/
+s/^\([^=]*=[ ]*\):*/\1/
+s/:*$//
+s/^[^=]*=[ ]*$//
+}'
+fi
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+fi # test -n "$CONFIG_FILES"
+
+
+for ac_tag in :F $CONFIG_FILES
+do
+ case $ac_tag in
+ :[FHLC]) ac_mode=$ac_tag; continue;;
+ esac
+ case $ac_mode$ac_tag in
+ :[FHL]*:*);;
+ :L* | :C*:*) { { echo "$as_me:$LINENO: error: Invalid tag $ac_tag." >&5
+echo "$as_me: error: Invalid tag $ac_tag." >&2;}
+ { (exit 1); exit 1; }; };;
+ :[FH]-) ac_tag=-:-;;
+ :[FH]*) ac_tag=$ac_tag:$ac_tag.in;;
+ esac
+ ac_save_IFS=$IFS
+ IFS=:
+ set x $ac_tag
+ IFS=$ac_save_IFS
+ shift
+ ac_file=$1
+ shift
+
+ case $ac_mode in
+ :L) ac_source=$1;;
+ :[FH])
+ ac_file_inputs=
+ for ac_f
+ do
+ case $ac_f in
+ -) ac_f="$tmp/stdin";;
+ *) # Look for the file first in the build tree, then in the source tree
+ # (if the path is not absolute). The absolute path cannot be DOS-style,
+ # because $ac_f cannot contain `:'.
+ test -f "$ac_f" ||
+ case $ac_f in
+ [\\/$]*) false;;
+ *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";;
+ esac ||
+ { { echo "$as_me:$LINENO: error: cannot find input file: $ac_f" >&5
+echo "$as_me: error: cannot find input file: $ac_f" >&2;}
+ { (exit 1); exit 1; }; };;
+ esac
+ ac_file_inputs="$ac_file_inputs $ac_f"
+ done
+
+ # Let's still pretend it is `configure' which instantiates (i.e., don't
+ # use $as_me), people would be surprised to read:
+ # /* config.h. Generated by config.status. */
+ configure_input="Generated from "`IFS=:
+ echo $* | sed 's|^[^:]*/||;s|:[^:]*/|, |g'`" by configure."
+ if test x"$ac_file" != x-; then
+ configure_input="$ac_file. $configure_input"
+ { echo "$as_me:$LINENO: creating $ac_file" >&5
+echo "$as_me: creating $ac_file" >&6;}
+ fi
+
+ case $ac_tag in
+ *:-:* | *:-) cat >"$tmp/stdin";;
+ esac
+ ;;
+ esac
+
+ ac_dir=`$as_dirname -- "$ac_file" ||
+$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$ac_file" : 'X\(//\)[^/]' \| \
+ X"$ac_file" : 'X\(//\)$' \| \
+ X"$ac_file" : 'X\(/\)' \| . 2>/dev/null ||
+echo X"$ac_file" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)[^/].*/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+ { as_dir="$ac_dir"
+ case $as_dir in #(
+ -*) as_dir=./$as_dir;;
+ esac
+ test -d "$as_dir" || { $as_mkdir_p && mkdir -p "$as_dir"; } || {
+ as_dirs=
+ while :; do
+ case $as_dir in #(
+ *\'*) as_qdir=`echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #(
+ *) as_qdir=$as_dir;;
+ esac
+ as_dirs="'$as_qdir' $as_dirs"
+ as_dir=`$as_dirname -- "$as_dir" ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$as_dir" : 'X\(//\)[^/]' \| \
+ X"$as_dir" : 'X\(//\)$' \| \
+ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null ||
+echo X"$as_dir" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)[^/].*/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+ test -d "$as_dir" && break
+ done
+ test -z "$as_dirs" || eval "mkdir $as_dirs"
+ } || test -d "$as_dir" || { { echo "$as_me:$LINENO: error: cannot create directory $as_dir" >&5
+echo "$as_me: error: cannot create directory $as_dir" >&2;}
+ { (exit 1); exit 1; }; }; }
+ ac_builddir=.
+
+case "$ac_dir" in
+.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;
+*)
+ ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'`
+ # A ".." for each directory in $ac_dir_suffix.
+ ac_top_builddir_sub=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,/..,g;s,/,,'`
+ case $ac_top_builddir_sub in
+ "") ac_top_builddir_sub=. ac_top_build_prefix= ;;
+ *) ac_top_build_prefix=$ac_top_builddir_sub/ ;;
+ esac ;;
+esac
+ac_abs_top_builddir=$ac_pwd
+ac_abs_builddir=$ac_pwd$ac_dir_suffix
+# for backward compatibility:
+ac_top_builddir=$ac_top_build_prefix
+
+case $srcdir in
+ .) # We are building in place.
+ ac_srcdir=.
+ ac_top_srcdir=$ac_top_builddir_sub
+ ac_abs_top_srcdir=$ac_pwd ;;
+ [\\/]* | ?:[\\/]* ) # Absolute name.
+ ac_srcdir=$srcdir$ac_dir_suffix;
+ ac_top_srcdir=$srcdir
+ ac_abs_top_srcdir=$srcdir ;;
+ *) # Relative name.
+ ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix
+ ac_top_srcdir=$ac_top_build_prefix$srcdir
+ ac_abs_top_srcdir=$ac_pwd/$srcdir ;;
+esac
+ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix
+
+
+ case $ac_mode in
+ :F)
+ #
+ # CONFIG_FILE
+ #
+
+ case $INSTALL in
+ [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;;
+ *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;;
+ esac
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+# If the template does not know about datarootdir, expand it.
+# FIXME: This hack should be removed a few years after 2.60.
+ac_datarootdir_hack=; ac_datarootdir_seen=
+
+case `sed -n '/datarootdir/ {
+ p
+ q
+}
+/@datadir@/p
+/@docdir@/p
+/@infodir@/p
+/@localedir@/p
+/@mandir@/p
+' $ac_file_inputs` in
+*datarootdir*) ac_datarootdir_seen=yes;;
+*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*)
+ { echo "$as_me:$LINENO: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5
+echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;}
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF
+ ac_datarootdir_hack='
+ s&@datadir@&$datadir&g
+ s&@docdir@&$docdir&g
+ s&@infodir@&$infodir&g
+ s&@localedir@&$localedir&g
+ s&@mandir@&$mandir&g
+ s&\\\${datarootdir}&$datarootdir&g' ;;
+esac
+_ACEOF
+
+# Neutralize VPATH when `$srcdir' = `.'.
+# Shell code in configure.ac might set extrasub.
+# FIXME: do we really want to maintain this feature?
+cat >>$CONFIG_STATUS <<_ACEOF
+ sed "$ac_vpsub
+$extrasub
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF
+:t
+/@[a-zA-Z_][a-zA-Z_0-9]*@/!b
+s&@configure_input@&$configure_input&;t t
+s&@top_builddir@&$ac_top_builddir_sub&;t t
+s&@srcdir@&$ac_srcdir&;t t
+s&@abs_srcdir@&$ac_abs_srcdir&;t t
+s&@top_srcdir@&$ac_top_srcdir&;t t
+s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t
+s&@builddir@&$ac_builddir&;t t
+s&@abs_builddir@&$ac_abs_builddir&;t t
+s&@abs_top_builddir@&$ac_abs_top_builddir&;t t
+s&@INSTALL@&$ac_INSTALL&;t t
+$ac_datarootdir_hack
+" $ac_file_inputs | sed -f "$tmp/subs-1.sed" >$tmp/out
+
+test -z "$ac_datarootdir_hack$ac_datarootdir_seen" &&
+ { ac_out=`sed -n '/\${datarootdir}/p' "$tmp/out"`; test -n "$ac_out"; } &&
+ { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' "$tmp/out"`; test -z "$ac_out"; } &&
+ { echo "$as_me:$LINENO: WARNING: $ac_file contains a reference to the variable \`datarootdir'
+which seems to be undefined. Please make sure it is defined." >&5
+echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir'
+which seems to be undefined. Please make sure it is defined." >&2;}
+
+ rm -f "$tmp/stdin"
+ case $ac_file in
+ -) cat "$tmp/out"; rm -f "$tmp/out";;
+ *) rm -f "$ac_file"; mv "$tmp/out" $ac_file;;
+ esac
+ ;;
+
+
+
+ esac
+
+done # for ac_tag
+
+
+{ (exit 0); exit 0; }
+_ACEOF
+chmod +x $CONFIG_STATUS
+ac_clean_files=$ac_clean_files_save
+
+
+# configure is writing to config.log, and then calls config.status.
+# config.status does its own redirection, appending to config.log.
+# Unfortunately, on DOS this fails, as config.log is still kept open
+# by configure, so config.status won't be able to write to it; its
+# output is simply discarded. So we exec the FD to /dev/null,
+# effectively closing config.log, so it can be properly (re)opened and
+# appended to by config.status. When coming back to configure, we
+# need to make the FD available again.
+if test "$no_create" != yes; then
+ ac_cs_success=:
+ ac_config_status_args=
+ test "$silent" = yes &&
+ ac_config_status_args="$ac_config_status_args --quiet"
+ exec 5>/dev/null
+ $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false
+ exec 5>>config.log
+ # Use ||, not &&, to avoid exiting from the if with $? = 1, which
+ # would make configure fail if this is the last instruction.
+ $ac_cs_success || { (exit 1); exit 1; }
+fi
+
+
diff --git a/configure.in b/configure.in
new file mode 100644
index 00000000..e8c12616
--- /dev/null
+++ b/configure.in
@@ -0,0 +1,244 @@
+dnl Process this file with autoconf to produce a configure script.
+AC_REVISION([$Id: configure.in,v 1.38 2008/11/17 11:34:18 tschwinge Exp $])
+AC_PREREQ(2.54) dnl Minimum Autoconf version required.
+AC_INIT
+AC_CONFIG_SRCDIR([hurd/hurd_types.h]) dnl File to look for in srcdir.
+
+AC_PREFIX_DEFAULT() dnl Default to empty prefix, not /usr/local.
+
+AC_CANONICAL_HOST
+case "$host_os" in
+gnu*) ;;
+none) AC_MSG_ERROR([
+*** You must specify a host of $host_cpu-gnu or $host_cpu-$host_vendor-gnu
+*** to configure; you will need to use the same host specification
+*** to configure other packages for the GNU/Hurd system.]) ;;
+*) AC_MSG_ERROR([this is the gnu os, host cannot be $host_os
+*** Host configuration must be \`MACHINE-gnu' or \`MACHINE-VENDOR-gnu'.
+*** To cross-compile, you must specify both --host and --build;
+*** for example \`--build=$host --host=$host_cpu-gnu'.
+*** Run $0 --help for more information.]) ;;
+esac
+
+case "$host_cpu" in
+alpha*)
+ asm_syntax=alpha
+ ;;
+arm*)
+ asm_syntax=arm
+ ;;
+m68k | m680?0)
+ asm_syntax=m68k
+ ;;
+mips*)
+ asm_syntax=mips
+ ;;
+i?86)
+ asm_syntax=i386
+ ;;
+powerpc*)
+ asm_syntax=ppc
+ ;;
+sparc64* | ultrasparc*)
+ asm_syntax=sparc64
+ ;;
+sparc*)
+ asm_syntax=sparc
+ ;;
+*)
+ asm_syntax="$host_cpu"
+ ;;
+esac
+AC_SUBST(asm_syntax)
+
+test -r "$srcdir/libthreads/$asm_syntax/cthreads.h" || {
+ AC_MSG_WARN([unsupported CPU type $host_cpu])
+}
+
+AC_ARG_ENABLE(profile,
+[ --disable-profile do not build profiled libraries and programs])
+AC_SUBST(enable_profile)
+
+define([default_static],['ext2fs,ufs'])dnl
+AC_ARG_ENABLE(static-progs,
+[ --enable-static-progs=PROGRAMS...
+ build statically-linked PROGRAM.static versions
+ of (only) the listed programs ]dnl
+changequote(',')[default_static]changequote([,]))
+case "$enable_static_progs" in
+'no') enable_static_progs= ;; # we got --disable-static
+'') enable_static_progs=default_static ;;
+esac
+# Convert comma/space-separated list into space-separated list.
+enable_static_progs=`echo "$enable_static_progs" | sed 's/[[, ]][[, ]]*/ /g'`
+AC_SUBST(enable_static_progs)
+
+[# Don't needlessly overwrite files that whose contents haven't changed. This
+# helps for avoinding unneccessary recompilation cycles when keeping
+# cross-compilation toolchains up-to-date. Thus, unconditionally use the
+# supplied `install-sh', as the GNU Coreutils one doesn't provide this
+# functionality yet (TODO: change that). TODO: $ac_abs_top_builddir et al. are
+# not yet available here, that's why we use `readlink' (but only if available).
+INSTALL="$SHELL $(readlink -f "$ac_install_sh")"\ -C || unset INSTALL]
+AC_PROG_INSTALL
+AC_PROG_AWK
+
+AC_PROG_CC
+# Require GCC.
+if test x$GCC != xyes; then
+ AC_MSG_ERROR([this code uses GNU C extensions, you must compile with GCC])
+fi
+
+AC_CHECK_TOOL(LD, ld)
+AC_CHECK_TOOL(OBJCOPY, objcopy)
+AC_CHECK_TOOL(AR, ar)
+AC_CHECK_TOOL(RANLIB, ranlib)
+AC_CHECK_TOOL(MIG, mig)
+# Require MiG.
+if test x${MIG} = x; then
+ AC_MSG_ERROR([
+*** You need GNU MiG to compile the GNU Hurd, please see
+*** http://www.gnu.org/software/hurd/mig.html for further details, or
+*** download it directly from the main GNU server (ftp.gnu.org) or any
+*** GNU mirror.])
+fi
+
+dnl Let these propagate from the environment.
+AC_SUBST(CFLAGS) AC_SUBST(CPPFLAGS) AC_SUBST(LDFLAGS)
+
+# See if there's a separate libcrypt (many systems put crypt there).
+AC_CHECK_LIB(crypt, crypt, LIBCRYPT=-lcrypt)
+AC_SUBST(LIBCRYPT)
+
+hurd_MIG_RETCODE
+
+# See if --version-script is available.
+AC_CACHE_CHECK(for ld --version-script, hurd_cv_ld_version_script_option, [dnl
+cat > conftest.c <<\EOF
+void foobar() {}
+EOF
+cat > conftest.map <<\EOF
+VERS_1 {
+ global: sym;
+};
+
+VERS_2 {
+ global: sym;
+} VERS_1;
+EOF
+
+if AC_TRY_COMMAND([eval $ac_compile 1>&AS_MESSAGE_LOG_FD()]) &&
+ AC_TRY_COMMAND([${CC-cc} $CFLAGS -shared -o conftest.so conftest.o
+ -nostartfiles -nostdlib
+ -Wl,--version-script,conftest.map
+ 1>&AS_MESSAGE_LOG_FD()]); then
+ hurd_cv_ld_version_script_option=yes
+else
+ hurd_cv_ld_version_script_option=no
+fi
+rm -f conftest*])
+
+# See if libc was built with --enable-libio.
+AC_CACHE_CHECK([for libio],
+ hurd_cv_libio,
+ AC_TRY_COMPILE([#include <stdio.h>
+#ifndef _STDIO_USES_IOSTREAM
+# error No libio found.
+#endif],,
+ hurd_cv_libio=yes,
+ hurd_cv_libio=no))
+
+# The versions of the symbols in libthreads have to match those in
+# libc.so. Since the symbols in a libc that includes libio will be
+# versioned differently from the ones in a libc that uses stdio, this
+# isn't easy to accomplish. Instead we leave things unversioned if
+# libio isn't found.
+if test $hurd_cv_libio = yes; then
+ VERSIONING=$hurd_cv_ld_version_script_option
+else
+ VERSIONING=no
+fi
+AC_SUBST(VERSIONING)
+
+# Check if libc contains getgrouplist and/or uselocale.
+AC_CHECK_FUNCS(getgrouplist uselocale)
+
+
+# From glibc HEAD, 2007-11-07.
+AC_CACHE_CHECK(for -fgnu89-inline, libc_cv_gnu89_inline, [dnl
+cat > conftest.c <<EOF
+int foo;
+#ifdef __GNUC_GNU_INLINE__
+main () { return 0;}
+#else
+#error
+#endif
+EOF
+if AC_TRY_COMMAND([${CC-cc} $CFLAGS $CPPFLAGS -S -std=gnu99 -fgnu89-inline
+ -o conftest.s conftest.c 1>&AS_MESSAGE_LOG_FD])
+then
+ libc_cv_gnu89_inline=yes
+else
+ libc_cv_gnu89_inline=no
+fi
+rm -f conftest*])
+if test $libc_cv_gnu89_inline = yes; then
+ libc_cv_gnu89_inline=-fgnu89-inline
+else
+ libc_cv_gnu89_inline=
+fi
+AC_SUBST(libc_cv_gnu89_inline)
+
+
+AC_ARG_WITH(parted, dnl
+[ --without-parted don't try to use GNU Parted libraries],
+ , with_parted=yes)
+
+AC_ARG_ENABLE(boot-store-types,
+[ --enable-boot-store-types=TYPES...
+ list of store types included in statically
+ linked filesystems used for booting])dnl
+if test -z "$enable_boot_store_types"; then
+ boot_store_types='device remap gunzip bunzip2'
+
+ # Check for Parted's static store module.
+ if test "x$with_parted" != xno; then
+ save_LDFLAGS="$LDFLAGS"
+ LDFLAGS="$LDFLAGS -static"
+ AC_CHECK_LIB(store_part, store_part_open, [dnl
+ boot_store_types="$boot_store_types part"], , -luuid -lstore)
+ LDFLAGS="$save_LDFLAGS"
+ fi
+elif test "x$enable_boot_store_types" = xno; then
+ AC_MSG_WARN([you probably wanted --disable-static-progs])
+else
+ boot_store_types="$enable_boot_store_types"
+fi
+AC_SUBST(boot_store_types)dnl
+AC_MSG_CHECKING(boot store types)
+AC_MSG_RESULT($boot_store_types)
+
+# Check for ncursesw, which is needed for the console-curses client.
+hurd_LIB_NCURSESW
+
+if test -f ./$ac_unique_file; then
+ # Configuring in source directory; don't create any Makefiles.
+ makefiles=
+else
+ # We are configuring in a separate build tree.
+ # Create a Makefile in the top-level build directory and
+ # one for each subdirectory Makefile in the source.
+ makefiles="Makeconf:build.mkcf.in \
+ `cd $srcdir; for file in Makefile */Makefile; do \
+ echo ${file}:build.mk.in; done`"
+fi
+
+AC_CONFIG_FILES([config.make ${makefiles}])
+AC_OUTPUT
+
+dnl Local Variables:
+dnl comment-start: "dnl "
+dnl comment-end: ""
+dnl comment-start-skip: "\\bdnl\\b\\s *"
+dnl compile-command: "autoconf"
+dnl End:
diff --git a/procfs/ChangeLog b/procfs/ChangeLog
new file mode 100644
index 00000000..47cbeaf2
--- /dev/null
+++ b/procfs/ChangeLog
@@ -0,0 +1,177 @@
+2010-05-31 Samuel Thibault <samuel.thibault@ens-lyon.org>
+
+ * procfs_nonpid_files.c (procfs_read_nonpid_meminfo): Print swap sizes
+ using %llu.
+
+2008-12-12 Samuel Thibault <samuel.thibault@ens-lyon.org>
+
+ * procfs_nonpid_files.c (procfs_read_nonpid_meminfo): Divide by
+ 1024 value returned by get_swap_size and get_swap_free to get
+ kilobytes.
+
+2008-09-02 Madhusudan.C.S <madhusudancs@gmail.com>
+
+ * netfs.c: (netfs_get_dirents): Add call to
+ procfs_dir_entries_remove().
+
+2008-08-30 Madhusudan.C.S <madhusudancs@gmail.com>
+
+ * procfs_dir.c: (procfs_dir_create): Assign newly created directory to
+ its pointer in netnode.
+ (procfs_dir_remove): Removed function.
+ (free_entry): New function.
+ (ordered_unlink): Likewise.
+ (delete): Likewise.
+ (sweep): Likewise.
+ (procfs_dir_entries_remove): Likewise.
+ (is_in_pid_list): Removed call to make_dir_invalid ().
+ (procfs_fill_root_dir): struct stat *stat -> struct stat stat.
+ Add Read and Execute permissions to all in stat.st_mode.
+ Set stat.st_nlink to 1.
+ Set stat.st_size to 0.
+ Add struct proc_stat *ps definition.
+ Set struct proc_stat data from _proc_stat_create () function and
+ set stat.st_uid and stat.st_gid from the data in that structure.
+ * procfs_pid_files.c: (update_pid_entries): Add Read permissions
+ to all in stat->st_mode.
+
+2008-08-29 Madhusudan.C.S <madhusudancs@gmail.com>
+
+ * AUTHORS: File removed.
+ * COPYING: Likewise.
+ * README: Likewise.
+
+2008-08-29 Madhusudan.C.S <madhusudancs@gmail.com>
+
+ * Makefile: (Copyright): 1997, 2000 -> 2008.
+ (CC): Removed.
+ (CFLAGS): Removed.
+ (INCLUDES): Removed.
+ (all): Removed.
+ ($(target)): Removed.
+ (%.o): Removed.
+ (HURDLIBS): -lnetfs -> netfs, -lfshelp -> fshelp,
+ -liohelp -> iohelp, -lthreads -> threads, -lports -> ports,
+ -lihash -> ihash, -lps -> ps, -lshouldbeinlibc -> shouldbeinlibc.
+ (include): Add include ../Makeconf
+
+2008-08-18 Madhusudan.C.S <madhusudancs@gmail.com>
+
+ * procfs_nonpid_files.c: (procfs_write_nonpid_stat): Changed to
+ procfs_read_nonpid_stat.
+ (procfs_write_nonpid_meminfo): Changed to procfs_read_nonpid_meminfo.
+ (procfs_write_nonpid_loadavg): Changed to procfs_read_nonpid_loadavg.
+ (procfs_write_nonpid_uptime): Changed to procfs_read_nonpid_uptime.
+ (procfs_write_nonpid_version):Changed to procfs_read_nonpid_version.
+ * procfs_pid_files.c: (procfs_write_stat_file): Changed to
+ procfs_read_stat_file.
+ Changed the comment correspondingly from Write to Read.
+ (procfs_write_cmdline_file ): Changed to procfs_read_cmdline_file.
+ Changed the comment correspondingly from Write to Read.
+ (procfs_write_status_file): Changed to procfs_read_status_file.
+ Changed the comment correspondingly from Write to Read.
+ (procfs_write_statm_file): Changed to procfs_read_statm_file.
+ Changed the comment correspondingly from Write to Read.
+ (procfs_write_files_contents): Changed to procfs_read_files_contents.
+ Changed the comment correspondingly from Write to Read.
+ Changed the call to procfs_write_nonpid_stat to procfs_read_nonpid_stat.
+ Changed the call to procfs_write_stat_file to procfs_read_stat_file.
+ Changed the call to procfs_write_cmdline_file to
+ procfs_read_cmdline_file.
+ Changed the call to procfs_write_status_file to
+ procfs_read_status_file.
+ Changed the call to procfs_write_statm_file to
+ procfs_read_statm_file.
+ Changed the call to procfs_write_nonpid_meminfo to
+ procfs_read_nonpid_meminfo.
+ Changed the call to procfs_write_nonpid_loadavg to
+ procfs_read_nonpid_loadavg.
+ Changed the call to procfs_write_nonpid_uptime to
+ procfs_read_nonpid_uptime.
+ Changed the call to procfs_write_nonpid_version to
+ procfs_read_nonpid_version.
+ netfs.c: (netfs_attempt_read): Changed the call from
+ procfs_write_files_contents to procfs_read_files_contents.
+
+2008-08-18 Madhusudan.C.S <madhusudancs@gmail.com>
+
+ * README: Initial Documentation.
+
+2008-08-18 Madhusudan.C.S <madhusudancs@gmail.com>
+
+ * procfs_nonpid_files.c: (get_uptime): Changed the parameter type from
+ double to struct timeval.
+ Changed the parameter name from uptime_secs to uptime.
+ Removed uptime variable.
+ Changed timersub to use the passed pointer instead of the local
+ variable.
+ Removed the calculation of uptime_secs.
+ (get_total_times): Changed the parameters type from double to struct
+ timeval.
+ Changed the parameters name from total_user_time_secs to
+ total_user_time and total_system_time_secs to total_system_time.
+ New variables total_user_time_tmp, total_system_time_tmp and tmpval
+ of type struct timeval.
+ Call timerclear to clear the tmp variables.
+ Remove calculation of times in seconds and do the same on struct
+ timeval variables throughout using the timeradd macro.
+ Assign values of temporary local variables to the pointers passed
+ as parameters.
+ (procfs_write_nonpid_stat): Replaced variables that hold time in
+ seconds with struct timeval type variables and jiffy_t type variables.
+ Argument to get_uptime changed from uptime_secs to uptime.
+ Arguments to get_total_times changed from total_user_time_secs to
+ total_user_time and total_system_time_secs to total_system_time.
+ Replace arithematic time subtraction with timersub macro.
+ Convert all the times in struct timeval type variables to jiffy_t type.
+ Changed the type casting for the asprintf arguments to be compatible
+ with jiffy_t type.
+ (procfs_write_nonpid_uptime): Replaced variables that hold time in
+ seconds with struct timeval type variables.
+ Argument to get_uptime changed from uptime_secs to uptime.
+ Arguments to get_total_times changed from total_user_time_secs to
+ total_user_time and total_system_time_secs to total_system_time.
+ Replace arithematic time subtraction with timersub macro.
+ Convert all the times in struct timeval type variables to seconds.
+
+2008-08-18 Madhusudan.C.S <madhusudancs@gmail.com>
+
+ * procfs_nonpid_files.c: (procfs_write_nonpid_version): New function.
+ * procfs_pid_files.c: (procfs_write_files_contents): Add a check
+ to find if the read is requested for the version file and
+ corresponding a call to it.
+
+2008-08-14 Madhusudan.C.S <madhusudancs@gmail.com>
+
+ * procfs.h: (jiffy_t): New typedef.
+ * procfs_pid.h: "procfs.h" is included.
+ (struct procfs_pid_files): Changed all the occurrences of time_t to
+ jiffy_t.
+ * procfs_pid_files.c: Removed "procfs.h".
+ (adjust_jiffy_time): Changed return type from time_t to jiffy_t.
+ Changed the type of jiffy_time variable from time_t to jiffy_t.
+ (get_live_threads_time): Changed the type of utime and stime from
+ time_t to jiffy_t.
+ (get_stat_data): Changed the type of utime and stime from time_t to
+ jiffy_t.
+
+2008-08-14 Madhusudan.C.S <madhusudancs@gmail.com>
+
+ * ChangeLog: New file.
+ * AUTHORS: New file.
+ * COPYING: New file.
+ * README: New file.
+ * Makefile: New file.
+ * bootstrap.c: New file.
+ * netfs.c: New file.
+ * node.c: New file.
+ * procfs.c: New file.
+ * procfs.h: New file.
+ * procfs_dir.c: New file.
+ * procfs_nonpid_files.c: New file.
+ * procfs_pid.h: New file.
+ * procfs_pid_files.c: New file.
+
+2008-05-13 Madhusudan.C.S <madhusudancs@gmail.com>
+
+ * /sources/hurd/procfs: New directory added to the repository.
diff --git a/procfs/Makefile b/procfs/Makefile
new file mode 100644
index 00000000..500a2371
--- /dev/null
+++ b/procfs/Makefile
@@ -0,0 +1,30 @@
+# Makefile - for procfs
+#
+# Copyright (C) 2008 Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2, or (at
+# your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+dir := procfs
+makemode := server
+
+target = procfs
+
+SRCS = procfs.c bootstrap.c netfs.c procfs_dir.c node.c procfs_pid_files.c procfs_nonpid_files.c
+LCLHDRS = procfs.h procfs_pid.h
+
+OBJS = $(SRCS:.c=.o)
+HURDLIBS = netfs fshelp iohelp threads ports ihash ps shouldbeinlibc
+
+include ../Makeconf
diff --git a/procfs/bootstrap.c b/procfs/bootstrap.c
new file mode 100644
index 00000000..73d31f00
--- /dev/null
+++ b/procfs/bootstrap.c
@@ -0,0 +1,95 @@
+/* procfs -- a translator for providing GNU/Linux compatible
+ proc pseudo-filesystem
+
+ bootstrap.c -- This file is functions for starting up
+ and initializers for the procfs translator
+ defined in procfs.h
+
+ Copyright (C) 2008, FSF.
+ Written as a Summer of Code Project
+
+ procfs is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ procfs is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
+*/
+
+#include <stddef.h>
+#include <hurd/ihash.h>
+#include <hurd/netfs.h>
+
+#include "procfs.h"
+
+struct ps_context *ps_context;
+
+/* This function is used to initialize the whole translator, can be
+ effect called as bootstrapping the translator. */
+error_t procfs_init ()
+{
+ error_t err;
+
+ err = ps_context_create (getproc (), &ps_context);
+
+ return err;
+}
+
+/* Create a new procfs filesystem. */
+error_t procfs_create (char *procfs_root, int fsid,
+ struct procfs **fs)
+{
+ error_t err;
+ /* This is the enclosing directory for this filesystem's
+ root node */
+ struct procfs_dir *topmost_root_dir;
+
+ /* And also a topmost-root node, just used for locking
+ TOPMOST_ROOT_DIR. */
+ struct node *topmost_root;
+
+ /* The new node for the filesystem's root. */
+ struct procfs *new = malloc (sizeof (struct procfs));
+
+ if (! new)
+ return ENOMEM;
+
+ new->fsid = fsid;
+ new->next_inode = 2;
+
+ hurd_ihash_init (&new->inode_mappings,
+ offsetof (struct procfs_dir_entry, inode_locp));
+ spin_lock_init (&new->inode_mappings_lock);
+
+ topmost_root = netfs_make_node (0);
+ if (! topmost_root)
+ err = ENOMEM;
+ else
+ {
+ err = procfs_dir_create (new, topmost_root, procfs_root,
+ &topmost_root_dir);
+ if (! err)
+ {
+ /* ADDITIONAL BOOTSTRAPPING OF THE ROOT NODE */
+ err = procfs_dir_null_lookup (topmost_root_dir, &new->root);
+ }
+ }
+
+ if (err)
+ {
+ hurd_ihash_destroy (&new->inode_mappings);
+ free (new);
+ }
+ else
+ *fs = new;
+
+ return err;
+}
+
diff --git a/procfs/netfs.c b/procfs/netfs.c
new file mode 100644
index 00000000..4f6fd5ce
--- /dev/null
+++ b/procfs/netfs.c
@@ -0,0 +1,467 @@
+/* procfs -- a translator for providing GNU/Linux compatible
+ proc pseudo-filesystem
+
+ Copyright (C) 2008, FSF.
+ Written as a Summer of Code Project
+
+ procfs is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ procfs is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
+*/
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <string.h>
+
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <hurd/netfs.h>
+
+#include "procfs.h"
+
+/* Trivial definitions. */
+
+/* Make sure that NP->nn_stat is filled with current information. CRED
+ identifies the user responsible for the operation. */
+error_t
+netfs_validate_stat (struct node *node, struct iouser *cred)
+{
+ return procfs_refresh_node (node);
+}
+
+/* This should sync the file NODE completely to disk, for the user CRED. If
+ WAIT is set, return only after sync is completely finished. */
+error_t
+netfs_attempt_sync (struct iouser *cred, struct node *node, int wait)
+{
+ return 0;
+}
+
+/* Attempt to create a new directory named NAME in DIR for USER with mode
+ MODE. */
+error_t netfs_attempt_mkdir (struct iouser *user, struct node *dir,
+ char *name, mode_t mode)
+{
+ return EROFS;
+}
+
+/* Attempt to remove directory named NAME in DIR for USER. */
+error_t netfs_attempt_rmdir (struct iouser *user,
+ struct node *dir, char *name)
+{
+ return EROFS;
+}
+
+/* Attempt to set the passive translator record for FILE to ARGZ (of length
+ ARGZLEN) for user CRED. */
+error_t netfs_set_translator (struct iouser *cred, struct node *node,
+ char *argz, size_t argzlen)
+{
+ return EROFS;
+}
+
+/* Attempt to create a file named NAME in DIR for USER with MODE. Set *NODE
+ to the new node upon return. On any error, clear *NODE. *NODE should be
+ locked on success; no matter what, unlock DIR before returning. */
+error_t
+netfs_attempt_create_file (struct iouser *user, struct node *dir,
+ char *name, mode_t mode, struct node **node)
+{
+ *node = NULL;
+ mutex_unlock (&dir->lock);
+ return EROFS;
+}
+
+/* Node NODE is being opened by USER, with FLAGS. NEWNODE is nonzero if we
+ just created this node. Return an error if we should not permit the open
+ to complete because of a permission restriction. */
+error_t
+netfs_check_open_permissions (struct iouser *user, struct node *node,
+ int flags, int newnode)
+{
+ error_t err = procfs_refresh_node (node);
+ if (!err && (flags & O_READ))
+ err = fshelp_access (&node->nn_stat, S_IREAD, user);
+ if (!err && (flags & O_WRITE))
+ err = fshelp_access (&node->nn_stat, S_IWRITE, user);
+ if (!err && (flags & O_EXEC))
+ err = fshelp_access (&node->nn_stat, S_IEXEC, user);
+ return err;
+}
+
+/* This should attempt a utimes call for the user specified by CRED on node
+ NODE, to change the atime to ATIME and the mtime to MTIME. */
+error_t
+netfs_attempt_utimes (struct iouser *cred, struct node *node,
+ struct timespec *atime, struct timespec *mtime)
+{
+ error_t err = procfs_refresh_node (node);
+ int flags = TOUCH_CTIME;
+
+ if (! err)
+ err = fshelp_isowner (&node->nn_stat, cred);
+
+ if (! err)
+ {
+ if (atime)
+ node->nn_stat.st_atim = *atime;
+ else
+ flags |= TOUCH_ATIME;
+
+ if (mtime)
+ node->nn_stat.st_mtim = *mtime;
+ else
+ flags |= TOUCH_MTIME;
+
+ fshelp_touch (&node->nn_stat, flags, procfs_maptime);
+ }
+
+ return err;
+}
+
+/* Return the valid access types (bitwise OR of O_READ, O_WRITE, and O_EXEC)
+ in *TYPES for file NODE and user CRED. */
+error_t
+netfs_report_access (struct iouser *cred, struct node *node, int *types)
+{
+ error_t err = procfs_refresh_node (node);
+
+ if (! err)
+ {
+ *types = 0;
+ if (fshelp_access (&node->nn_stat, S_IREAD, cred) == 0)
+ *types |= O_READ;
+ if (fshelp_access (&node->nn_stat, S_IWRITE, cred) == 0)
+ *types |= O_WRITE;
+ if (fshelp_access (&node->nn_stat, S_IEXEC, cred) == 0)
+ *types |= O_EXEC;
+ }
+
+ return err;
+}
+
+/* The granularity with which we allocate space to return our result. */
+#define DIRENTS_CHUNK_SIZE (8*1024)
+
+/* Returned directory entries are aligned to blocks this many bytes long.
+ Must be a power of two. */
+#define DIRENT_ALIGN 4
+#define DIRENT_NAME_OFFS offsetof (struct dirent, d_name)
+
+/* Length is structure before the name + the name + '\0', all
+ padded to a four-byte alignment. */
+#define DIRENT_LEN(name_len) \
+ ((DIRENT_NAME_OFFS + (name_len) + 1 + (DIRENT_ALIGN - 1)) \
+ & ~(DIRENT_ALIGN - 1))
+
+
+
+/* Fetch a directory */
+error_t
+netfs_get_dirents (struct iouser *cred, struct node *dir,
+ int first_entry, int max_entries, char **data,
+ mach_msg_type_number_t *data_len,
+ vm_size_t max_data_len, int *data_entries)
+{
+ error_t err = procfs_refresh_node (dir);
+ struct procfs_dir_entry *dir_entry;
+
+ if (! err)
+ {
+ if (dir->nn->dir)
+ {
+ if (! procfs_dir_refresh (dir->nn->dir, dir == dir->nn->fs->root))
+ {
+ for (dir_entry = dir->nn->dir->ordered; first_entry > 0 &&
+ dir_entry; first_entry--,
+ dir_entry = dir_entry->ordered_next);
+ if (! dir_entry )
+ max_entries = 0;
+
+ if (max_entries != 0)
+ {
+ size_t size = 0;
+ char *p;
+ int count = 0;
+
+
+ if (max_data_len == 0)
+ size = DIRENTS_CHUNK_SIZE;
+ else if (max_data_len > DIRENTS_CHUNK_SIZE)
+ size = DIRENTS_CHUNK_SIZE;
+ else
+ size = max_data_len;
+
+ *data = mmap (0, size, PROT_READ|PROT_WRITE,
+ MAP_ANON, 0, 0);
+
+ err = ((void *) *data == (void *) -1) ? errno : 0;
+
+ if (! err)
+ {
+ p = *data;
+
+ /* This gets all the actual entries present. */
+
+ while ((max_entries == -1 || count < max_entries) && dir_entry)
+ {
+ struct dirent hdr;
+ size_t name_len = strlen (dir_entry->name);
+ size_t sz = DIRENT_LEN (name_len);
+ int entry_type = IFTODT (dir_entry->stat.st_mode);
+
+ if ((p - *data) + sz > size)
+ {
+ if (max_data_len > 0)
+ break;
+ else /* The Buffer Size must be increased. */
+ {
+ vm_address_t extension = (vm_address_t)(*data + size);
+ err = vm_allocate (mach_task_self (), &extension,
+ DIRENTS_CHUNK_SIZE, 0);
+
+ if (err)
+ break;
+
+ size += DIRENTS_CHUNK_SIZE;
+ }
+ }
+
+ hdr.d_namlen = name_len;
+ hdr.d_fileno = dir_entry->stat.st_ino;
+ hdr.d_reclen = sz;
+ hdr.d_type = entry_type;
+
+ memcpy (p, &hdr, DIRENT_NAME_OFFS);
+ strcpy (p + DIRENT_NAME_OFFS, dir_entry->name);
+
+ p += sz;
+
+ count++;
+ dir_entry = dir_entry->ordered_next;
+ }
+
+ if (err)
+ munmap (*data, size);
+ else
+ {
+ vm_address_t alloc_end = (vm_address_t)(*data + size);
+ vm_address_t real_end = round_page (p);
+ if (alloc_end > real_end)
+ munmap ((caddr_t) real_end, alloc_end - real_end);
+ *data_len = p - *data;
+ *data_entries = count;
+ }
+ }
+ }
+ else
+ {
+ *data_len = 0;
+ *data_entries = 0;
+ }
+ }
+ }
+ else
+ return ENOTDIR;
+ }
+
+ procfs_dir_entries_remove (dir->nn->dir);
+ return err;
+}
+
+/* Lookup NAME in DIR for USER; set *NODE to the found name upon return. If
+ the name was not found, then return ENOENT. On any error, clear *NODE.
+ (*NODE, if found, should be locked, this call should unlock DIR no matter
+ what.) */
+error_t netfs_attempt_lookup (struct iouser *user, struct node *dir,
+ char *name, struct node **node)
+{
+ error_t err = procfs_refresh_node (dir);
+
+ if (! err)
+ err = procfs_dir_lookup (dir->nn->dir, name, node);
+
+ return err;
+}
+
+/* Delete NAME in DIR for USER. */
+error_t netfs_attempt_unlink (struct iouser *user, struct node *dir,
+ char *name)
+{
+ return EROFS;
+}
+
+/* Note that in this one call, neither of the specific nodes are locked. */
+error_t netfs_attempt_rename (struct iouser *user, struct node *fromdir,
+ char *fromname, struct node *todir,
+ char *toname, int excl)
+{
+ return EROFS;
+}
+
+/* This should attempt a chmod call for the user specified by CRED on node
+ NODE, to change the owner to UID and the group to GID. */
+error_t netfs_attempt_chown (struct iouser *cred, struct node *node,
+ uid_t uid, uid_t gid)
+{
+ return EROFS;
+}
+
+/* This should attempt a chauthor call for the user specified by CRED on node
+ NODE, to change the author to AUTHOR. */
+error_t netfs_attempt_chauthor (struct iouser *cred, struct node *node,
+ uid_t author)
+{
+ return EROFS;
+}
+
+/* This should attempt a chmod call for the user specified by CRED on node
+ NODE, to change the mode to MODE. Unlike the normal Unix and Hurd meaning
+ of chmod, this function is also used to attempt to change files into other
+ types. If such a transition is attempted which is impossible, then return
+ EOPNOTSUPP. */
+error_t netfs_attempt_chmod (struct iouser *cred, struct node *node,
+ mode_t mode)
+{
+ return EROFS;
+}
+
+/* Attempt to turn NODE (user CRED) into a symlink with target NAME. */
+error_t netfs_attempt_mksymlink (struct iouser *cred, struct node *node,
+ char *name)
+{
+ return EROFS;
+}
+
+/* Attempt to turn NODE (user CRED) into a device. TYPE is either S_IFBLK or
+ S_IFCHR. */
+error_t netfs_attempt_mkdev (struct iouser *cred, struct node *node,
+ mode_t type, dev_t indexes)
+{
+ return EROFS;
+}
+
+
+/* This should attempt a chflags call for the user specified by CRED on node
+ NODE, to change the flags to FLAGS. */
+error_t netfs_attempt_chflags (struct iouser *cred, struct node *node,
+ int flags)
+{
+ return EROFS;
+}
+
+/* This should attempt to set the size of the file NODE (for user CRED) to
+ SIZE bytes long. */
+error_t netfs_attempt_set_size (struct iouser *cred, struct node *node,
+ off_t size)
+{
+ return EROFS;
+}
+
+/* This should attempt to fetch filesystem status information for the remote
+ filesystem, for the user CRED. */
+error_t
+netfs_attempt_statfs (struct iouser *cred, struct node *node,
+ struct statfs *st)
+{
+ bzero (st, sizeof *st);
+ st->f_type = PROCFILESYSTEM;
+ st->f_fsid = getpid ();
+ return 0;
+}
+
+/* This should sync the entire remote filesystem. If WAIT is set, return
+ only after sync is completely finished. */
+error_t netfs_attempt_syncfs (struct iouser *cred, int wait)
+{
+ return 0;
+}
+
+/* Create a link in DIR with name NAME to FILE for USER. Note that neither
+ DIR nor FILE are locked. If EXCL is set, do not delete the target, but
+ return EEXIST if NAME is already found in DIR. */
+error_t netfs_attempt_link (struct iouser *user, struct node *dir,
+ struct node *file, char *name, int excl)
+{
+ return EROFS;
+}
+
+/* Attempt to create an anonymous file related to DIR for USER with MODE.
+ Set *NODE to the returned file upon success. No matter what, unlock DIR. */
+error_t netfs_attempt_mkfile (struct iouser *user, struct node *dir,
+ mode_t mode, struct node **node)
+{
+ *node = NULL;
+ mutex_unlock (&dir->lock);
+ return EROFS;
+}
+
+/* Read the contents of NODE (a symlink), for USER, into BUF. */
+error_t netfs_attempt_readlink (struct iouser *user, struct node *node, char *buf)
+{
+ error_t err = procfs_refresh_node (node);
+ if (! err)
+ {
+ struct procfs_dir_entry *dir_entry = node->nn->dir_entry;
+ if (dir_entry)
+ bcopy (dir_entry->symlink_target, buf, node->nn_stat.st_size);
+ else
+ err = EINVAL;
+ }
+ return err;
+}
+
+/* Read from the file NODE for user CRED starting at OFFSET and continuing for
+ up to *LEN bytes. Put the data at DATA. Set *LEN to the amount
+ successfully read upon return. */
+error_t netfs_attempt_read (struct iouser *cred, struct node *node,
+ off_t offset, size_t *len, void *data)
+{
+ error_t err;
+ err = procfs_refresh_node (node);
+
+ if (! err)
+ {
+ if (*len > 0)
+ procfs_read_files_contents (node, offset,
+ len, data);
+ if (*len > 0)
+ if (offset >= *len)
+ *len = 0;
+ }
+
+ return err;
+}
+
+/* Write to the file NODE for user CRED starting at OFFSET and continuing for up
+ to *LEN bytes from DATA. Set *LEN to the amount seccessfully written upon
+ return. */
+error_t netfs_attempt_write (struct iouser *cred, struct node *node,
+ off_t offset, size_t *len, void *data)
+{
+ return EROFS;
+}
+
+/* The user must define this function. Node NP is all done; free
+ all its associated storage. */
+void netfs_node_norefs (struct node *np)
+{
+ mutex_lock (&np->lock);
+ *np->prevp = np->next;
+ np->next->prevp = np->prevp;
+ procfs_remove_node (np);
+}
+
diff --git a/procfs/node.c b/procfs/node.c
new file mode 100644
index 00000000..f11fa7b0
--- /dev/null
+++ b/procfs/node.c
@@ -0,0 +1,195 @@
+/* procfs -- a translator for providing GNU/Linux compatible
+ proc pseudo-filesystem
+
+ node.c -- This file contains function defintions to handle
+ node creation and destruction.
+
+ Copyright (C) 2008, FSF.
+ Written as a Summer of Code Project
+
+ procfs is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ procfs is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
+*/
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <string.h>
+#include <errno.h>
+#include <hurd/ihash.h>
+#include <hurd/fshelp.h>
+#include <hurd/iohelp.h>
+
+#include <hurd/netfs.h>
+
+#include "procfs.h"
+
+/* Return a new node in NODE, with a name NAME, and return the
+ new node with a single reference in NODE. */
+error_t procfs_create_node (struct procfs_dir_entry *dir_entry,
+ const char *fs_path, struct node **node)
+{
+ struct node *new;
+ struct netnode *nn = malloc (sizeof (struct netnode));
+ error_t err;
+
+ if (! nn)
+ return ENOMEM;
+ if (! fs_path)
+ fs_path = strdup ("");
+ nn->fs = dir_entry->dir->fs;
+ nn->dir_entry = dir_entry;
+ nn->dir = NULL;
+ nn->fs_path = strdup (fs_path);
+
+ new = netfs_make_node (nn);
+ if (! new)
+ {
+ free (nn);
+ return ENOMEM;
+ }
+
+ fshelp_touch (&new->nn_stat, TOUCH_ATIME|TOUCH_MTIME|TOUCH_CTIME,
+ procfs_maptime);
+
+ spin_lock (&nn->fs->inode_mappings_lock);
+ err = hurd_ihash_add (&nn->fs->inode_mappings, dir_entry->stat.st_ino, dir_entry);
+ spin_unlock (&nn->fs->inode_mappings_lock);
+
+ if (err)
+ {
+ free (nn);
+ free (new);
+ return err;
+ }
+
+ dir_entry->node = new;
+ *node = new;
+
+ return 0;
+}
+
+/* Update the directory entry for NAME to reflect ST and SYMLINK_TARGET.
+ True is returned if successful, or false if there was a memory allocation
+ error. TIMESTAMP is used to record the time of this update. */
+static void
+update_entry (struct procfs_dir_entry *dir_entry, const struct stat *st,
+ const char *symlink_target, time_t timestamp)
+{
+ ino_t ino;
+ struct procfs *fs = dir_entry->dir->fs;
+
+ if (dir_entry->stat.st_ino)
+ ino = dir_entry->stat.st_ino;
+ else
+ ino = fs->next_inode++;
+
+ dir_entry->name_timestamp = timestamp;
+
+ if (st)
+ /* The ST and SYMLINK_TARGET parameters are only valid if ST isn't 0. */
+ {
+ dir_entry->stat = *st;
+ dir_entry->stat_timestamp = timestamp;
+
+ if (!dir_entry->symlink_target || !symlink_target
+ || strcmp (dir_entry->symlink_target, symlink_target) != 0)
+ {
+ if (dir_entry->symlink_target)
+ free (dir_entry->symlink_target);
+ dir_entry->symlink_target = symlink_target ? strdup (symlink_target) : 0;
+ }
+ }
+
+ /* The st_ino field is always valid. */
+ dir_entry->stat.st_ino = ino;
+ dir_entry->stat.st_fsid = fs->fsid;
+ dir_entry->stat.st_fstype = PROCFILESYSTEM;
+}
+
+/* Refresh stat information for NODE */
+error_t procfs_refresh_node (struct node *node)
+{
+ struct netnode *nn = node->nn;
+ struct procfs_dir_entry *dir_entry = nn->dir_entry;
+
+ if (! dir_entry)
+ /* This is a deleted node, don't attempt to do anything. */
+ return 0;
+ else
+ {
+ error_t err = 0;
+
+ struct timeval tv;
+ maptime_read (procfs_maptime, &tv);
+
+ time_t timestamp = tv.tv_sec;
+
+ struct procfs_dir *dir = dir_entry->dir;
+
+ mutex_lock (&dir->node->lock);
+
+ if (! dir_entry->self_p)
+ /* This is a deleted entry, just awaiting disposal; do so. */
+ {
+#if 0
+ nn->dir_entry = 0;
+ free_entry (dir_entry);
+ return 0;
+#endif
+ }
+
+ else if (dir_entry->noent)
+ err = ENOENT;
+ else
+ {
+ if (*(dir_entry->name))
+ {
+ err = procfs_dir_refresh (dir_entry->dir,
+ dir_entry->dir->node == dir_entry->dir->fs->root);
+ if (!err && dir_entry->noent)
+ err = ENOENT;
+
+ if (err == ENOENT)
+ {
+ dir_entry->noent = 1; /* A negative entry. */
+ dir_entry->name_timestamp = timestamp;
+ }
+ }
+ else
+ {
+ /* Refresh the root node with the old stat
+ information. */
+ update_entry (dir_entry, &netfs_root_node->nn_stat, NULL, timestamp);
+ }
+ }
+
+ node->nn_stat = dir_entry->stat;
+ node->nn_translated = S_ISLNK (dir_entry->stat.st_mode) ? S_IFLNK : 0;
+ if (!nn->dir && S_ISDIR (dir_entry->stat.st_mode))
+ procfs_dir_create (nn->fs, node, nn->fs_path, &nn->dir);
+
+ mutex_unlock (&dir->node->lock);
+
+ return err;
+ }
+}
+
+/* Remove NODE from its entry */
+error_t procfs_remove_node (struct node *node)
+{
+
+ /* STUB */
+
+ return 0;
+}
diff --git a/procfs/procfs.c b/procfs/procfs.c
new file mode 100644
index 00000000..1fd0d619
--- /dev/null
+++ b/procfs/procfs.c
@@ -0,0 +1,149 @@
+/* procfs -- a translator for providing GNU/Linux compatible
+ proc pseudo-filesystem
+
+ procfs.c -- This file is the main file of the translator.
+ This has important definitions and initializes
+ the translator
+
+ Copyright (C) 2008, FSF.
+ Written as a Summer of Code Project
+
+ procfs is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ procfs is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
+*/
+
+#include <stdio.h>
+#include <argp.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <unistd.h>
+#include <error.h>
+#include <sys/stat.h>
+#include <hurd/netfs.h>
+
+#include "procfs.h"
+
+/* Defines this Tanslator Name */
+char *netfs_server_name = PROCFS_SERVER_NAME;
+char *netfs_server_version = PROCFS_SERVER_VERSION;
+int netfs_maxsymlinks = 12;
+
+static const struct argp_child argp_children[] =
+ {
+ {&netfs_std_startup_argp, 0, NULL, 0},
+ {0}
+ };
+
+
+const char *argp_program_version = "/proc pseudo-filesystem (" PROCFS_SERVER_NAME
+ ") " PROCFS_SERVER_VERSION "\n"
+"Copyright (C) 2008 Free Software Foundation\n"
+"This is free software; see the source for copying conditions. There is NO\n"
+"warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
+"\n";
+
+static char *args_doc = "PROCFSROOT";
+static char *doc = "proc pseudo-filesystem for Hurd implemented as a translator. "
+"This is still under very humble and initial stages of development.\n"
+"Any Contribution or help is welcome. The code may not even compile";
+
+
+/* The Filesystem */
+struct procfs *procfs;
+
+/* The FILESYSTEM component of PROCFS_FS. */
+char *procfs_root = "";
+
+volatile struct mapped_time_value *procfs_maptime;
+
+/* Startup options. */
+static const struct argp_option procfs_options[] =
+ {
+ { 0 }
+ };
+
+
+/* argp parser function for parsing single procfs command line options */
+static error_t
+parse_procfs_opt (int key, char *arg, struct argp_state *state)
+{
+ switch (key)
+ {
+ case ARGP_KEY_ARG:
+ if (state->arg_num > 1)
+ argp_usage (state);
+ break;
+
+ case ARGP_KEY_NO_ARGS:
+ argp_usage(state);
+ break;
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+}
+
+/* Program entry point. */
+int
+main (int argc, char **argv)
+{
+ error_t err;
+ mach_port_t bootstrap, underlying_node;
+ struct stat underlying_stat;
+
+ struct argp argp =
+ {
+ procfs_options, parse_procfs_opt,
+ args_doc, doc, argp_children,
+ NULL, NULL
+ };
+
+
+ /* Parse the command line arguments */
+// argp_parse (&argp, argc, argv, 0, 0, 0);
+
+ task_get_bootstrap_port (mach_task_self (), &bootstrap);
+
+ netfs_init ();
+
+ if (maptime_map (0, 0, &procfs_maptime))
+ {
+ perror (PROCFS_SERVER_NAME ": Cannot map time");
+ return 1;
+ }
+
+ procfs_init ();
+
+ err = procfs_create (procfs_root, getpid (), &procfs);
+ if (err)
+ error (4, err, "%s", procfs_root);
+
+ /* Create our root node */
+ netfs_root_node = procfs->root;
+
+ /* Start netfs activities */
+ underlying_node = netfs_startup (bootstrap, 0);
+ if (io_stat (underlying_node, &underlying_stat))
+ error (1, err, "cannot stat underling node");
+
+ /* Initialize stat information of the root node. */
+ netfs_root_node->nn_stat = underlying_stat;
+ netfs_root_node->nn_stat.st_mode =
+ S_IFDIR | (underlying_stat.st_mode & ~S_IFMT & ~S_ITRANS);
+
+ for (;;)
+ netfs_server_loop ();
+ return 1;
+}
diff --git a/procfs/procfs.h b/procfs/procfs.h
new file mode 100644
index 00000000..fa2fb7f7
--- /dev/null
+++ b/procfs/procfs.h
@@ -0,0 +1,220 @@
+/* procfs -- a translator for providing GNU/Linux compatible
+ proc pseudo-filesystem
+
+ procfs.h -- This file is the main header file of this
+ translator. This has important header
+ definitions for constants and functions
+ used in the translator.
+
+ Copyright (C) 2008, FSF.
+ Written as a Summer of Code Project
+
+ procfs is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ procfs is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
+
+ A portion of the code in this file is based on ftpfs code
+ present in the hurd repositories copyrighted to FSF. The
+ Copyright notice from that file is given below.
+
+ Copyright (C) 1997,98,2002 Free Software Foundation, Inc.
+ Written by Miles Bader <miles@gnu.org>
+ This file is part of the GNU Hurd.
+*/
+
+#ifndef __PROCFS_H__
+#define __PROCFS_H__
+
+#define PROCFS_SERVER_NAME "procfs"
+#define PROCFS_SERVER_VERSION "0.0.1"
+
+/* /proc Filesystem type. */
+#define PROCFILESYSTEM "procfs"
+
+#define NUMBER_OF_FILES_PER_PID 1
+#define JIFFY_ADJUST 100
+#define PAGES_TO_BYTES(pages) ((pages) * sysconf(_SC_PAGESIZE))
+#define BYTES_TO_PAGES(bytes) ((bytes) / sysconf(_SC_PAGESIZE))
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <cthreads.h>
+#include <maptime.h>
+#include <hurd/ihash.h>
+#include <ps.h>
+
+typedef unsigned long long jiffy_t;
+
+/* A single entry in a directory. */
+struct procfs_dir_entry
+{
+ char *name; /* Name of this entry */
+ size_t hv; /* Hash value of NAME */
+
+ /* The active node referred to by this name (may be 0).
+ NETFS_NODE_REFCNT_LOCK should be held while frobbing this. */
+ struct node *node;
+
+ struct stat stat;
+ char *symlink_target;
+ time_t stat_timestamp;
+
+ /* The directory to which this entry belongs. */
+ struct procfs_dir *dir;
+
+ /* Link to next entry in hash bucket, and address of previous entry's (or
+ hash table's) pointer to this entry. If the SELF_P field is 0, then
+ this is a deleted entry, awaiting final disposal. */
+ struct procfs_dir_entry *next, **self_p;
+
+ /* Next entry in 'directory order', or 0 if none known. */
+ struct procfs_dir_entry *ordered_next, **ordered_self_p;
+
+ /* When the presence/absence of this file was last checked. */
+ time_t name_timestamp;
+
+ hurd_ihash_locp_t inode_locp; /* Used for removing this entry */
+
+ int noent : 1; /* A negative lookup result. */
+ int valid : 1; /* Marker for GC'ing. */
+};
+
+/* A directory. */
+struct procfs_dir
+{
+ /* Number of entries in HTABLE. */
+ size_t num_entries;
+
+ /* The number of entries that have nodes attached. We keep an additional
+ reference to our node if there are any, to prevent it from going away. */
+ size_t num_live_entries;
+
+ /* Hash table of entries. */
+ struct procfs_dir_entry **htable;
+ size_t htable_len; /* # of elements in HTABLE (not bytes). */
+
+ /* List of dir entries in 'directory order', in a linked list using the
+ ORDERED_NEXT and ORDERED_SELF_P fields in each entry. Not all entries
+ in HTABLE need be in this list. */
+ struct procfs_dir_entry *ordered;
+
+ /* The filesystem node that this is the directory for. */
+ struct node *node;
+
+ /* The filesystem this directory is in. */
+ struct procfs *fs;
+
+ /* The path to this directory in the filesystem. */
+ const char *fs_path;
+
+ time_t stat_timestamp;
+ time_t name_timestamp;
+
+};
+
+
+/* libnetfs node structure */
+struct netnode
+{
+ /* Name of this node */
+ char *name;
+
+ /* The path in the filesystem that corresponds
+ this node. */
+ char *fs_path;
+
+ /* The directory entry for this node. */
+ struct procfs_dir_entry *dir_entry;
+
+ /* The proc filesystem */
+ struct procfs *fs;
+
+ /* inode number, assigned to this netnode structure. */
+ unsigned int inode_num;
+
+ /* If this is a directory, the contents, or 0 if not fetched. */
+ struct procfs_dir *dir;
+
+ /* pointer to node structure, assigned to this node. */
+ struct node *node;
+
+ /* links to the previous and next nodes in the list */
+ struct netnode *nextnode, *prevnode;
+
+ /* link to parent netnode of this file or directory */
+ struct netnode *parent;
+
+ /* link to the first child netnode of this directory */
+ struct netnode *child_first;
+};
+
+/* The actual procfs filesystem structure */
+struct procfs
+{
+ /* Root of the filesystem. */
+ struct node *root;
+
+ /* Inode numbers are assigned sequentially in order of creation. */
+ ino_t next_inode;
+ int fsid;
+
+ /* A hash table mapping inode numbers to directory entries. */
+ struct hurd_ihash inode_mappings;
+ spin_lock_t inode_mappings_lock;
+};
+
+extern struct procfs *procfs;
+
+extern volatile struct mapped_time_value *procfs_maptime;
+
+extern struct ps_context *ps_context;
+
+/* Create a new procfs filesystem. */
+error_t procfs_create (char *procfs_root, int fsid,
+ struct procfs **fs);
+
+/* Initialize the procfs filesystem for use. */
+error_t procfs_init ();
+
+/* Refresh stat information for NODE */
+error_t procfs_refresh_node (struct node *node);
+
+/* Return a new node in NODE, with a name NAME,
+ and return the new node with a single
+ reference in NODE. */
+error_t procfs_create_node (struct procfs_dir_entry *dir_entry,
+ const char *fs_path,
+ struct node **node);
+
+/* Remove NODE from its entry */
+error_t procfs_remove_node (struct node *node);
+
+/* Return in DIR a new procfs directory, in the filesystem FS,
+ with node NODE and path PATH. */
+error_t procfs_dir_create (struct procfs *fs, struct node *node,
+ const char *path, struct procfs_dir **dir);
+
+/* Remove the specified DIR and free all its allocated
+ storage. */
+void procfs_dir_remove (struct procfs_dir *dir);
+
+/* Refresh DIR. */
+error_t procfs_dir_refresh (struct procfs_dir *dir, int isroot);
+
+/* Lookup NAME in DIR, returning its entry, or an error.
+ *NODE will contain the result node, locked, and with
+ an additional reference, or 0 if an error occurs. */
+error_t procfs_dir_lookup (struct procfs_dir *dir, const char *name,
+ struct node **node);
+
+#endif /* __PROCFS_H__ */
diff --git a/procfs/procfs_dir.c b/procfs/procfs_dir.c
new file mode 100644
index 00000000..f76e6a4b
--- /dev/null
+++ b/procfs/procfs_dir.c
@@ -0,0 +1,664 @@
+/* procfs -- a translator for providing GNU/Linux compatible
+ proc pseudo-filesystem
+
+ procfs_dir.c -- This file contains definitions to perform
+ directory operations such as creating,
+ removing and refreshing directories.
+
+ Copyright (C) 2008, FSF.
+ Written as a Summer of Code Project
+
+
+ procfs is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ procfs is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
+
+ A portion of the code in this file is based on ftpfs code
+ present in the hurd repositories copyrighted to FSF. The
+ Copyright notice from that file is given below.
+
+ Copyright (C) 1997,98,2002 Free Software Foundation, Inc.
+ Written by Miles Bader <miles@gnu.org>
+ This file is part of the GNU Hurd.
+*/
+
+
+#include <stdio.h>
+#include <unistd.h>
+#include <hurd/netfs.h>
+#include <hurd/ihash.h>
+#include <sys/stat.h>
+
+#include "procfs.h"
+
+/* Initial HASHTABLE length for the new directories to be created. */
+#define INIT_HTABLE_LEN 5
+
+struct procfs_dir_entry **cur_entry;
+
+/* Return in DIR a new procfs directory, in the filesystem FS,
+ with node NODE and path PATH. */
+error_t procfs_dir_create (struct procfs *fs, struct node *node,
+ const char *path, struct procfs_dir **dir)
+{
+ struct procfs_dir *new = malloc (sizeof (struct procfs_dir));
+ if (!new)
+ return ENOMEM;
+ struct procfs_dir_entry **htable = calloc (INIT_HTABLE_LEN,
+ sizeof (struct procfs_dir_entry *));
+ if (!htable)
+ return ENOMEM;
+
+ /* Hold a reference to the new dir's node. */
+ spin_lock (&netfs_node_refcnt_lock);
+ node->references++;
+ spin_unlock (&netfs_node_refcnt_lock);
+
+ new->num_entries = 0;
+ new->num_live_entries = 0;
+ new->htable_len = INIT_HTABLE_LEN;
+ new->htable = htable;
+ new->ordered = NULL;
+ new->fs_path = path;
+ new->fs = fs;
+ new->node = node;
+ new->stat_timestamp = 0;
+ new->name_timestamp = 0;
+
+ *dir = new;
+
+ if (fs->root != 0)
+ node->nn->dir = new;
+
+ return 0;
+}
+
+/* Put the directory entry DIR_ENTRY into the hash table HTABLE. */
+static void
+insert (struct procfs_dir_entry *dir_entry,
+ struct procfs_dir_entry **htable, size_t htable_len)
+{
+ struct procfs_dir_entry **new_htable = &htable[dir_entry->hv % htable_len];
+ if (*new_htable)
+ (*new_htable)->self_p = &dir_entry->next;
+ dir_entry->next = *new_htable;
+ dir_entry->self_p = new_htable;
+ *new_htable = dir_entry;
+}
+
+/* Calculate NAME's hash value. */
+static size_t
+hash (const char *name)
+{
+ size_t hash_value = 0;
+ while (*name)
+ hash_value = ((hash_value << 5) + *name++) & 0xFFFFFF;
+ return hash_value;
+}
+
+/* Extend the existing hashtable for DIR to accomodate values for new length
+ NEW_LEN. We retain all the previous entries. */
+static error_t
+rehash (struct procfs_dir *dir, size_t new_len)
+{
+ int count;
+ size_t old_len = dir->htable_len;
+ struct procfs_dir_entry **old_htable = dir->htable;
+ struct procfs_dir_entry **new_htable = (struct procfs_dir_entry **)
+ malloc (new_len * sizeof (struct procfs_dir_entry *));
+
+ if (! new_htable)
+ return ENOMEM;
+
+ bzero (new_htable, new_len * sizeof (struct procfs_dir_entry *));
+
+ for (count = 0; count < old_len; count++)
+ while (old_htable[count])
+ {
+ struct procfs_dir_entry *dir_entry = old_htable[count];
+
+ /* Remove DIR_ENTRY from the old table */
+ old_htable[count] = dir_entry->next;
+
+ insert (dir_entry, new_htable, new_len);
+ }
+
+ free (old_htable);
+
+ dir->htable = new_htable;
+ dir->htable_len = new_len;
+
+ return 0;
+}
+
+/* Lookup NAME in DIR and return its entry. If there is no such entry, and
+ DNEW, the decision variable, is true, then a new entry is allocated and
+ returned, otherwise 0 is returned (if DNEW is true then 0 can be returned
+ if a memory allocation error occurs). */
+struct procfs_dir_entry *
+lookup_entry (struct procfs_dir *dir, const char *name, int dnew)
+{
+ size_t hv = hash (name);
+ struct procfs_dir_entry *dir_entry = dir->htable[hv % dir->htable_len];
+
+ while (dir_entry && strcmp (name, dir_entry->name) != 0)
+ dir_entry = dir_entry->next;
+
+ if (!dir_entry && dnew)
+ {
+ if (dir->num_entries > dir->htable_len)
+ /* Grow the hash table. */
+ if (rehash (dir, (dir->htable_len + 1) * 2 - 1) != 0)
+ return 0;
+
+ dir_entry =
+ (struct procfs_dir_entry *) malloc (sizeof (struct procfs_dir_entry));
+
+ if (dir_entry)
+ {
+ dir_entry->hv = hv;
+ dir_entry->name = strdup (name);
+ dir_entry->node = 0;
+ dir_entry->dir = dir;
+ dir_entry->stat_timestamp = 0;
+ bzero (&dir_entry->stat, sizeof dir_entry->stat);
+ dir_entry->symlink_target = 0;
+ dir_entry->noent = 0;
+ dir_entry->valid = 0;
+ dir_entry->name_timestamp = 0;
+ dir_entry->ordered_next = 0;
+ dir_entry->ordered_self_p = 0;
+ dir_entry->next = 0;
+ dir_entry->self_p = 0;
+ insert (dir_entry, dir->htable, dir->htable_len);
+ dir->num_entries++;
+ }
+ }
+
+ return dir_entry;
+}
+
+
+/* Lookup NAME in DIR, returning its entry, or an error.
+ *NODE will contain the result node, locked, and with
+ an additional reference, or 0 if an error occurs. */
+error_t procfs_dir_lookup (struct procfs_dir *dir, const char *name,
+ struct node **node)
+{
+ struct procfs_dir_entry *dir_entry = 0;
+ error_t err = 0;
+ char *fs_path = dir->fs_path;
+
+ struct timeval tv;
+ maptime_read (procfs_maptime, &tv);
+
+ time_t timestamp = tv.tv_sec;
+
+ if (*name == '\0' || strcmp (name, ".") == 0)
+ /* Current directory -- just add an additional reference to DIR's node
+ and return it. */
+ {
+ netfs_nref (dir->node);
+ *node = dir->node;
+ return 0;
+ }
+ else if (strcmp (name, "..") == 0)
+ /* Parent directory. */
+ {
+ if (dir->node->nn->dir_entry)
+ {
+ *node = dir->node->nn->dir_entry->dir->node;
+ mutex_lock (&(*node)->lock);
+ netfs_nref (*node);
+ }
+ else
+ {
+ err = ENOENT; /* No .. */
+ *node = 0;
+ }
+
+ mutex_unlock (&dir->node->lock);
+
+ return err;
+ }
+
+ err = procfs_dir_refresh (dir, dir->node == dir->fs->root);
+ if (!err && !dir_entry)
+ dir_entry = lookup_entry (dir, name, 0);
+
+ if (! err)
+ {
+ if (dir_entry && !dir_entry->noent)
+ /* We've got a dir entry, get a node for it. */
+ {
+ /* If there's already a node, add a ref so that it doesn't go
+ away. */
+ spin_lock (&netfs_node_refcnt_lock);
+ if (dir_entry->node)
+ dir_entry->node->references++;
+ spin_unlock (&netfs_node_refcnt_lock);
+
+ if (! dir_entry->node)
+ /* No node; make one and install it into E. */
+ {
+ if (! fs_path)
+ err = EROFS;
+
+ if (! err)
+ {
+ err = procfs_create_node (dir_entry, fs_path, &dir_entry->node);
+
+ if (!err && dir->num_live_entries++ == 0)
+ /* Keep a reference to dir's node corresponding to
+ children. */
+ {
+ spin_lock (&netfs_node_refcnt_lock);
+ dir->node->references++;
+ spin_unlock (&netfs_node_refcnt_lock);
+ }
+ }
+ }
+
+ if (! err)
+ {
+ *node = dir_entry->node;
+ /* We have to unlock DIR's node before locking the child node
+ because the locking order is always child-parent. We know
+ the child node won't go away because we already hold the
+ additional reference to it. */
+ mutex_unlock (&dir->node->lock);
+ mutex_lock (&dir_entry->node->lock);
+ }
+ }
+ else
+ err = ENOENT;
+ }
+
+ if (err)
+ {
+ *node = 0;
+ mutex_unlock (&dir->node->lock);
+ }
+
+#if 0
+ if (fs_path)
+ free (fs_path);
+#endif
+
+ return err;
+}
+
+/* Lookup the null name in DIR, and return a node for it in NODE. Unlike
+ procfs_dir_lookup, this won't attempt to validate the existance of the
+ entry (to avoid opening a new connection if possible) -- that will happen
+ the first time the entry is refreshed. Also unlink ftpfs_dir_lookup, this
+ function doesn't expect DIR to be locked, and won't return *NODE locked.
+ This function is only used for bootstrapping the root node. */
+error_t
+procfs_dir_null_lookup (struct procfs_dir *dir, struct node **node)
+{
+ struct procfs_dir_entry *dir_entry;
+ error_t err = 0;
+
+ dir_entry = lookup_entry (dir, "", 1);
+ if (! dir_entry)
+ return ENOMEM;
+
+ if (! dir_entry->noent)
+ /* We've got a dir entry, get a node for it. */
+ {
+ /* If there's already a node, add a ref so that it doesn't go away. */
+ spin_lock (&netfs_node_refcnt_lock);
+ if (dir_entry->node)
+ dir_entry->node->references++;
+ spin_unlock (&netfs_node_refcnt_lock);
+
+ if (! dir_entry->node)
+ /* No node; make one and install it into DIR_ENTRY. */
+ {
+ err = procfs_create_node (dir_entry, dir->fs_path, &dir_entry->node);
+
+ if (!err && dir->num_live_entries++ == 0)
+ /* Keep a reference to dir's node corresponding to children. */
+ {
+ spin_lock (&netfs_node_refcnt_lock);
+ dir->node->references++;
+ spin_unlock (&netfs_node_refcnt_lock);
+ }
+ }
+
+ if (! err)
+ *node = dir_entry->node;
+ }
+ else
+ err = ENOENT;
+
+ return err;
+}
+
+/* Free the directory entry DIR_ENTRY and all resources it consumes. */
+void
+free_entry (struct procfs_dir_entry *dir_entry)
+{
+
+ assert (! dir_entry->self_p); /* We should only free deleted nodes. */
+ free (dir_entry->name);
+ if (dir_entry->symlink_target)
+ free (dir_entry->symlink_target);
+ free (dir_entry->node->nn->dir);
+ free (dir_entry->node->nn);
+ free (dir_entry->node);
+ free (dir_entry);
+}
+
+/* Remove DIR_ENTRY from its position in the ordered_next chain. */
+static void
+ordered_unlink (struct procfs_dir_entry *dir_entry)
+{
+ if (dir_entry->ordered_self_p)
+ *dir_entry->ordered_self_p = dir_entry->ordered_next;
+ if (dir_entry->ordered_next)
+ dir_entry->ordered_next->self_p = dir_entry->ordered_self_p;
+}
+
+/* Delete DIR_ENTRY from its directory, freeing any resources it holds. */
+static void
+delete (struct procfs_dir_entry *dir_entry, struct procfs_dir *dir)
+{
+ dir->num_entries--;
+
+ /* Take out of the hash chain. */
+ if (dir_entry->self_p)
+ *dir_entry->self_p = dir_entry->next;
+ if (dir_entry->next)
+ dir_entry->next->self_p = dir_entry->self_p;
+
+ /* Take out of the directory ordered list. */
+ ordered_unlink (dir_entry);
+
+ /* If there's a node attached, we'll delete the entry whenever it goes
+ away, otherwise, just delete it now. */
+ if (! dir_entry->node)
+ free_entry (dir_entry);
+}
+
+/* Make all the directory entries invalid */
+static void
+make_dir_invalid (struct procfs_dir *dir)
+{
+ int count;
+ size_t len = dir->htable_len;
+ struct procfs_dir_entry **htable = dir->htable;
+ struct procfs_dir_entry *dir_entry;
+
+ for (count = 0; count < len; count++)
+ {
+ dir_entry = htable[count];
+ while (dir_entry)
+ {
+ dir_entry->valid = 0;
+ dir_entry = dir_entry->next;
+ }
+ }
+}
+
+/* Delete any entries in DIR which don't have their valid bit set. */
+static void
+sweep (struct procfs_dir *dir)
+{
+ size_t len = dir->htable_len, i;
+ struct procfs_dir_entry **htable = dir->htable, *dir_entry;
+
+ for (i = 0; i < len; i++)
+ {
+ dir_entry = htable[i];
+ while (dir_entry)
+ {
+ if (!dir_entry->valid && !dir_entry->noent && dir->num_entries)
+ delete (dir_entry, dir);
+ dir_entry = dir_entry->next;
+ }
+ if (htable[i])
+ {
+ free (htable[i]);
+ htable[i] = 0;
+ }
+
+ }
+
+}
+
+/* Remove the specified DIR and free all its allocated
+ storage. */
+void procfs_dir_entries_remove (struct procfs_dir *dir)
+{
+ /* Free all entries. */
+ make_dir_invalid (dir);
+ sweep (dir);
+}
+
+/* Checks if the DIR name is in list of
+ Active pids. */
+int is_in_pid_list (struct procfs_dir *dir)
+{
+ int dir_name;
+ int count;
+ pid_t *pids = NULL;
+ int pidslen = 0;
+ error_t err;
+
+ if (dir->node->nn)
+ {
+ dir_name = atoi (dir->node->nn->dir_entry->name);
+ err = proc_getallpids (getproc (), &pids, &pidslen);
+
+ for (count = 0; count < pidslen; ++count)
+ if (pids[count] == dir_name)
+ return 1;
+ }
+
+ return 0;
+
+}
+
+/* Checks if DIR is a directory that
+ represents a pid. */
+int check_parent (struct procfs_dir *dir)
+{
+ if (dir == dir->fs->root)
+ return 0;
+ else
+ if (is_in_pid_list (dir))
+ return 1;
+ else
+ return 0;
+
+}
+
+/* Refresh DIR. */
+error_t procfs_dir_refresh (struct procfs_dir *dir, int isroot)
+{
+ error_t err;
+ int is_parent_pid;
+ struct node *node;
+
+ struct timeval tv;
+ maptime_read (procfs_maptime, &tv);
+
+ time_t timestamp = tv.tv_sec;
+ cur_entry = &dir->ordered;
+ if (isroot)
+ err = procfs_fill_root_dir(dir, timestamp);
+ else
+ {
+ err = update_dir_entries (dir, timestamp);
+ is_parent_pid = check_parent (dir);
+ if (is_parent_pid)
+ err = procfs_create_files (dir, &node, timestamp);
+ }
+
+ return err;
+}
+
+/* Update the directory entry for NAME to reflect STAT and SYMLINK_TARGET.
+ This also creates a valid linked list of entries imposing ordering on
+ them. */
+struct procfs_dir_entry*
+update_entries_list (struct procfs_dir *dir, const char *name,
+ const struct stat *stat, time_t timestamp,
+ const char *symlink_target)
+{
+ ino_t ino;
+ struct procfs_dir_entry *dir_entry = lookup_entry (dir, name, 1);
+ struct procfs *fs = dir->fs;
+
+ if (! dir_entry)
+ return ENOMEM;
+
+ if (dir_entry->stat.st_ino)
+ ino = dir_entry->stat.st_ino;
+ else
+ ino = fs->next_inode++;
+
+ dir_entry->name_timestamp = timestamp;
+
+ if (stat)
+ /* The ST and SYMLINK_TARGET parameters are only valid if ST isn't 0. */
+ {
+ dir_entry->stat = *stat;
+ dir_entry->stat_timestamp = timestamp;
+
+ if (!dir_entry->symlink_target || !symlink_target
+ || strcmp (dir_entry->symlink_target, symlink_target) != 0)
+ {
+ if (dir_entry->symlink_target)
+ free (dir_entry->symlink_target);
+ dir_entry->symlink_target = symlink_target ? strdup (symlink_target) : 0;
+ }
+ }
+
+ /* The st_ino field is always valid. */
+ dir_entry->stat.st_ino = ino;
+ dir_entry->stat.st_fsid = fs->fsid;
+ dir_entry->stat.st_fstype = PROCFILESYSTEM;
+
+ dir_entry->valid = 1;
+
+ if (! dir_entry->ordered_self_p)
+ /* Position DIR_ENTRY in the ordered chain following the previously seen entry. */
+ {
+ /* The PREV_ENTRY_NEXT_P field holds a pointer to the NEXT-field of the
+ previous entry, or a pointer to the ORDERED field in the directory. */
+ dir_entry->ordered_self_p = cur_entry;
+
+ if (*dir_entry->ordered_self_p)
+ /* Update the self_p pointer of the previous successor. */
+ (*dir_entry->ordered_self_p)->ordered_self_p = &dir_entry->ordered_next;
+
+ /* DIR_ENTRY comes before the previous successor. */
+ dir_entry->ordered_next = *dir_entry->ordered_self_p;
+
+ *dir_entry->ordered_self_p = dir_entry; /* Put DIR_ENTRY there. */
+ }
+
+ /* Put the next entry after this one. */
+ cur_entry = &dir_entry->ordered_next;
+
+ return dir_entry;
+}
+
+/* Fills DIR, the root directory with all the pids of
+ processes running in the system as directories. */
+error_t
+procfs_fill_root_dir(struct procfs_dir *dir, time_t timestamp)
+{
+ error_t err;
+ char *data;
+ pid_t *pids;
+ int pidslen;
+ struct stat stat;
+ stat.st_mode = S_IFDIR | S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP |
+ S_IROTH | S_IXOTH;
+ stat.st_nlink = 1;
+ stat.st_size = 0;
+
+ int count;
+ char *dir_name_pid;
+ struct node *node;
+ struct procfs_dir *new_dir;
+ struct procfs_dir_entry *dir_entry;
+ struct proc_stat *ps;
+
+ pids = NULL;
+ pidslen = 0;
+ err = proc_getallpids (getproc (), &pids, &pidslen);
+
+ if (!err)
+ {
+ for (count = 0; count < pidslen; count++)
+ {
+ if (asprintf (&dir_name_pid, "%d", pids[count]) == -1)
+ return errno;
+
+#if 0
+ node = (struct node *) malloc (sizeof (struct node));
+ new_dir = (struct procfs_dir *) malloc (sizeof (struct procfs_dir ));
+
+ if (! node || ! new_dir )
+ return ENOMEM;
+#endif
+ err = _proc_stat_create (pids[count], ps_context, &ps);
+ if (! err)
+ {
+ err = set_field_value (ps, PSTAT_PROC_INFO);
+ if (! err)
+ {
+ stat.st_uid = proc_stat_proc_info (ps)->owner;
+ stat.st_gid = proc_stat_proc_info (ps)->pgrp;
+
+ dir_entry = update_entries_list (dir, dir_name_pid,
+ &stat, timestamp, NULL);
+ err = procfs_create_node (dir_entry, dir_name_pid, &node);
+
+ procfs_dir_create (dir->fs, node,
+ dir_name_pid, &new_dir);
+ free(dir_name_pid);
+ _proc_stat_free (ps);
+ }
+ }
+ }
+ }
+
+ if ((err = procfs_create_uptime (dir, &node, timestamp)) != 0)
+ return err;
+
+ if ((err = procfs_create_stat (dir, &node, timestamp)) != 0)
+ return err;
+
+ if ((err = procfs_create_version (dir, &node, timestamp)) != 0)
+ return err;
+
+ if ((err = procfs_create_meminfo (dir, &node, timestamp)) != 0)
+ return err;
+
+ if ((err = procfs_create_loadavg (dir, &node, timestamp)) != 0)
+ return err;
+
+ return 0;
+}
+
+error_t update_dir_entries (struct procfs_dir *dir)
+{
+ /* STUB */
+ return 0;
+}
diff --git a/procfs/procfs_nonpid_files.c b/procfs/procfs_nonpid_files.c
new file mode 100644
index 00000000..2c1209ee
--- /dev/null
+++ b/procfs/procfs_nonpid_files.c
@@ -0,0 +1,514 @@
+/* procfs -- a translator for providing GNU/Linux compatible
+ proc pseudo-filesystem
+
+ procfs_nonpid_files.c -- This file contains function definitions
+ to create and update the non-Per PID
+ files and their contents.
+
+ Copyright (C) 2008, FSF.
+ Written as a Summer of Code Project
+
+
+ procfs is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ procfs is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
+
+ A portion of the code in this file is based on vmstat.c code
+ present in the hurd repositories copyrighted to FSF. The
+ Copyright notice from that file is given below.
+
+ Copyright (C) 1997,98,2002 Free Software Foundation, Inc.
+ Written by Miles Bader <miles@gnu.org>
+ This file is part of the GNU Hurd.
+*/
+
+#include <stdio.h>
+#include <unistd.h>
+#include <hurd/netfs.h>
+#include <hurd/ihash.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/sysinfo.h>
+#include <mach/vm_statistics.h>
+#include <mach/default_pager.h>
+#include <hurd.h>
+#include <hurd/paths.h>
+#include <mach.h>
+#include <ps.h>
+#include <time.h>
+
+#include "procfs.h"
+
+typedef long long val_t;
+#define BADVAL ((val_t) - 1LL)
+
+/* default pager port (must be privileged to fetch this). */
+mach_port_t def_pager;
+struct default_pager_info def_pager_info;
+
+error_t procfs_create_uptime (struct procfs_dir *dir,
+ struct node **node,
+ time_t timestamp)
+{
+ int err;
+ char *file_name, *file_path;
+ struct procfs_dir_entry *dir_entry;
+
+ if (asprintf (&file_name, "%s", "uptime") == -1)
+ return errno;
+ if (asprintf (&file_path, "%s", "uptime") == -1)
+ return errno;
+
+ dir_entry = update_pid_entries (dir, file_name, timestamp, NULL);
+ err = procfs_create_node (dir_entry, file_path, node);
+
+ free (file_name);
+ free (file_path);
+
+ return err;
+}
+
+error_t procfs_create_version(struct procfs_dir *dir,
+ struct node **node,
+ time_t timestamp)
+{
+ int err;
+ char *file_name, *file_path;
+ struct procfs_dir_entry *dir_entry;
+
+ if (asprintf (&file_name, "%s", "version") == -1)
+ return errno;
+ if (asprintf (&file_path, "%s", "version") == -1)
+ return errno;
+
+ dir_entry = update_pid_entries (dir, file_name, timestamp, NULL);
+ err = procfs_create_node (dir_entry, file_path, node);
+
+ free (file_name);
+ free (file_path);
+
+ return 0;
+}
+
+error_t procfs_create_stat (struct procfs_dir *dir,
+ struct node **node,
+ time_t timestamp)
+{
+ int err;
+ char *file_name, *file_path;
+ struct procfs_dir_entry *dir_entry;
+
+ if (asprintf (&file_name, "%s", "stat") == -1)
+ return errno;
+ if (asprintf (&file_path, "%s", "stat") == -1)
+ return errno;
+
+ dir_entry = update_pid_entries (dir, file_name, timestamp, NULL);
+ err = procfs_create_node (dir_entry, file_path, node);
+
+ free (file_name);
+ free (file_path);
+
+ return err;
+}
+
+error_t procfs_create_meminfo (struct procfs_dir *dir,
+ struct node **node,
+ time_t timestamp)
+{
+ int err;
+ char *file_name, *file_path;
+ struct procfs_dir_entry *dir_entry;
+
+ if (asprintf (&file_name, "%s", "meminfo") == -1)
+ return errno;
+ if (asprintf (&file_path, "%s", "meminfo") == -1)
+ return errno;
+
+ dir_entry = update_pid_entries (dir, file_name, timestamp, NULL);
+ err = procfs_create_node (dir_entry, file_path, node);
+
+ free (file_name);
+ free (file_path);
+
+ return err;
+}
+
+error_t procfs_create_loadavg (struct procfs_dir *dir,
+ struct node **node,
+ time_t timestamp)
+{
+ int err;
+ char *file_name, *file_path;
+ struct procfs_dir_entry *dir_entry;
+
+ if (asprintf (&file_name, "%s", "loadavg") == -1)
+ return errno;
+ if (asprintf (&file_path, "%s", "loadavg") == -1)
+ return errno;
+
+ dir_entry = update_pid_entries (dir, file_name, timestamp, NULL);
+ err = procfs_create_node (dir_entry, file_path, node);
+
+ free (file_name);
+ free (file_path);
+
+ return err;
+}
+
+error_t get_uptime (struct timeval *uptime)
+{
+ struct timeval boot_time, now;
+ error_t err;
+ struct proc_stat *ps;
+
+ err = _proc_stat_create (1, ps_context, &ps);
+
+ if (err)
+ return err;
+
+ err = proc_stat_set_flags (ps, PSTAT_TASK_BASIC);
+ if (!err && !(ps->flags & PSTAT_TASK_BASIC))
+ err = EGRATUITOUS;
+
+ if (! err)
+ {
+ time_value_t *const tv = &proc_stat_task_basic_info (ps)->creation_time;
+ boot_time.tv_sec = tv->seconds;
+ boot_time.tv_usec = tv->microseconds;
+ if (gettimeofday (&now, 0) < 0)
+ error (0, errno, "gettimeofday");
+ timersub (&now, &boot_time, uptime);
+ }
+
+ _proc_stat_free (ps);
+ return err;
+}
+
+error_t get_total_times (struct timeval *total_user_time,
+ struct timeval *total_system_time)
+{
+ error_t err;
+ pid_t *pids;
+ int pidslen = 0, count;
+ struct proc_stat *ps;
+ struct task_thread_times_info live_threads_times;
+
+ struct timeval total_user_time_tmp;
+ struct timeval total_system_time_tmp;
+ struct timeval tmpval;
+
+ timerclear (&total_user_time_tmp);
+ timerclear (&total_system_time_tmp);
+
+ pids = NULL;
+ err = proc_getallpids (getproc (), &pids, &pidslen);
+
+ if (!err)
+ for (count = 0; count < pidslen; count++)
+ {
+ err = _proc_stat_create (pids[count], ps_context, &ps);
+ if (err)
+ return err;
+
+ err = proc_stat_set_flags (ps, PSTAT_TASK_BASIC);
+ if (!err && !(ps->flags & PSTAT_TASK_BASIC))
+ err = EGRATUITOUS;
+
+ if (! err)
+ {
+ tmpval.tv_sec = proc_stat_task_basic_info (ps)->user_time.seconds;
+ tmpval.tv_usec = proc_stat_task_basic_info (ps)->user_time.seconds;
+ timeradd (&total_user_time_tmp, &tmpval, &total_user_time_tmp);
+
+ tmpval.tv_sec = proc_stat_task_basic_info (ps)->system_time.seconds;
+ tmpval.tv_usec = proc_stat_task_basic_info (ps)->system_time.seconds;
+ timeradd (&total_system_time_tmp, &tmpval, &total_system_time_tmp);
+
+ error_t err = set_field_value (ps, PSTAT_TASK);
+ if (! err)
+ {
+ err = get_task_thread_times (ps->task, &live_threads_times);
+ if (! err)
+ {
+ tmpval.tv_sec = live_threads_times.user_time.seconds;
+ tmpval.tv_usec = live_threads_times.user_time.microseconds;
+ timeradd (&total_user_time_tmp, &tmpval, &total_user_time_tmp);
+
+ tmpval.tv_sec = live_threads_times.system_time.seconds;
+ tmpval.tv_usec = live_threads_times.system_time.microseconds;
+ timeradd (&total_system_time_tmp, &tmpval, &total_system_time_tmp);
+ }
+ }
+ }
+ _proc_stat_free (ps);
+ }
+
+ total_user_time->tv_sec = total_user_time_tmp.tv_sec;
+ total_user_time->tv_usec = total_user_time_tmp.tv_usec;
+
+ total_system_time->tv_sec = total_system_time_tmp.tv_sec;
+ total_system_time->tv_usec = total_system_time_tmp.tv_usec;
+
+ return err;
+}
+
+error_t procfs_read_nonpid_stat (struct dir_entry *dir_entry,
+ off_t offset, size_t *len, void *data)
+{
+ char *stat_data;
+ error_t err;
+ jiffy_t total_user_time_jiffy, total_system_time_jiffy;
+ jiffy_t idle_time_jiffy;
+ struct timeval uptime, total_user_time, total_system_time;
+ struct timeval idle_time;
+
+ err = get_uptime (&uptime);
+
+ if (! err)
+ {
+ err = get_total_times (&total_user_time, &total_system_time);
+
+ if (! err)
+ {
+ timersub (&uptime, &total_system_time,
+ &idle_time);
+
+ total_user_time_jiffy = 100 * ((double) total_user_time.tv_sec +
+ (double) total_user_time.tv_usec / (1000 * 1000));
+ total_system_time_jiffy = 100 * ((double) total_system_time.tv_sec +
+ (double) total_system_time.tv_usec / (1000 * 1000));
+ idle_time_jiffy = 100 * ((double) idle_time.tv_sec +
+ (double) idle_time.tv_usec / (1000 * 1000));
+
+ if (asprintf (&stat_data, "cpu %llu %llu %llu %llu %llu %llu %d %d %d\n"
+ "cpu0 %llu %llu %llu %llu %llu %llu %d %d %d\n"
+ "intr %llu %llu %llu %llu %llu %llu %d %d %d\n",
+ total_user_time_jiffy, (long long unsigned) 0,
+ total_system_time_jiffy, idle_time_jiffy,
+ (long long unsigned) 0, (long long unsigned) 0,
+ 0, 0, 0,
+ total_user_time_jiffy, (long long unsigned) 0,
+ total_system_time_jiffy, idle_time_jiffy,
+ (long long unsigned) 0, (long long unsigned) 0,
+ 0, 0, 0,
+ (long long unsigned) 0,
+ (long long unsigned) 0, (long long unsigned) 0, (long long unsigned) 0,
+ (long long unsigned) 0,
+ (long long unsigned) 0, (long long unsigned) 0,
+ (long long unsigned) 0, (long long unsigned) 0) == -1)
+ return errno;
+ }
+ }
+
+ memcpy (data, stat_data, strlen(stat_data));
+ *len = strlen (data);
+
+ free (stat_data);
+ return err;
+}
+
+/* Makes sure the default pager port and associated
+ info exists, and returns 0 if not (after printing
+ an error). */
+static int
+ensure_def_pager_info ()
+{
+ error_t err;
+
+ if (def_pager == MACH_PORT_NULL)
+ {
+ mach_port_t host;
+
+ err = get_privileged_ports (&host, 0);
+ if (err == EPERM)
+ {
+ /* We are not root, so try opening the /servers file. */
+ def_pager = file_name_lookup (_SERVERS_DEFPAGER, O_READ, 0);
+ if (def_pager == MACH_PORT_NULL)
+ {
+ error (0, errno, _SERVERS_DEFPAGER);
+ return 0;
+ }
+ }
+ if (def_pager == MACH_PORT_NULL)
+ {
+ if (err)
+ {
+ error (0, err, "get_privileged_ports");
+ return 0;
+ }
+
+ err = vm_set_default_memory_manager (host, &def_pager);
+ mach_port_deallocate (mach_task_self (), host);
+
+ if (err)
+ {
+ error (0, err, "vm_set_default_memory_manager");
+ return 0;
+ }
+ }
+ }
+
+ if (!MACH_PORT_VALID (def_pager))
+ {
+ if (def_pager == MACH_PORT_NULL)
+ {
+ error (0, 0,
+ "No default pager running, so no swap information available");
+ def_pager = MACH_PORT_DEAD; /* so we don't try again */
+ }
+ return 0;
+ }
+
+ err = default_pager_info (def_pager, &def_pager_info);
+ if (err)
+ error (0, err, "default_pager_info");
+ return (err == 0);
+}
+
+#define SWAP_FIELD(getter, expr) \
+ static val_t getter () \
+ { return ensure_def_pager_info () ? (val_t) (expr) : BADVAL; }
+
+SWAP_FIELD (get_swap_size, def_pager_info.dpi_total_space)
+SWAP_FIELD (get_swap_free, def_pager_info.dpi_free_space)
+SWAP_FIELD (get_swap_page_size, def_pager_info.dpi_page_size)
+SWAP_FIELD (get_swap_active, (def_pager_info.dpi_total_space
+ - def_pager_info.dpi_free_space))
+
+error_t procfs_read_nonpid_meminfo (struct dir_entry *dir_entry,
+ off_t offset, size_t *len, void *data)
+{
+ char *meminfo_data;
+ error_t err;
+ struct vm_statistics vmstats;
+
+ err = vm_statistics (mach_task_self (), &vmstats);
+
+ unsigned long mem_size = ((vmstats.free_count +
+ vmstats.active_count + vmstats.inactive_count +
+ vmstats.wire_count) * vmstats.pagesize) / 1024;
+
+ if (! err)
+ if (asprintf (&meminfo_data, "MemTotal:\t%lu kB\n"
+ "MemFree:\t%lu kB\n"
+ "Buffers:\t%ld kB\n"
+ "Cached:\t\t%ld kB\n"
+ "SwapCached:\t%ld kB\n"
+ "Active:\t\t%lu kB\n"
+ "Inactive:\t%lu kB\n"
+ "HighTotal:\t%lu kB\n"
+ "HighFree:\t%lu kB\n"
+ "LowTotal:\t%lu kB\n"
+ "LowFree:\t%lu kB\n"
+ "SwapTotal:\t%llu kB\n"
+ "SwapFree:\t%llu kB\n",
+ mem_size, (PAGES_TO_BYTES(vmstats.free_count)) / 1024 , 0, 0, 0,
+ (PAGES_TO_BYTES(vmstats.active_count)) / 1024,
+ (PAGES_TO_BYTES(vmstats.inactive_count)) / 1024, 0, 0, 0, 0,
+ get_swap_size () / 1024, get_swap_free () / 1024) == -1)
+ return errno;
+
+ memcpy (data, meminfo_data, strlen(meminfo_data));
+ *len = strlen (data);
+
+ free (meminfo_data);
+ return err;
+}
+
+error_t procfs_read_nonpid_loadavg (struct dir_entry *dir_entry,
+ off_t offset, size_t *len, void *data)
+{
+ char *loadavg_data;
+ error_t err;
+ processor_set_info_t info;
+ natural_t *count;
+ struct host_load_info *load;
+ mach_port_t host;
+
+ err = ps_host_load_info (&load);
+ if (err)
+ error (0, err, "ps_host_load_info");
+
+ if (! err)
+ if (asprintf (&loadavg_data, "%.2f %.2f %.2f %d/%d %d\n",
+ (double)load->avenrun[0] / (double)LOAD_SCALE,
+ (double)load->avenrun[1] / (double)LOAD_SCALE,
+ (double)load->avenrun[2] / (double)LOAD_SCALE, 0, 0, 0) == -1)
+ return errno;
+
+ memcpy (data, loadavg_data, strlen(loadavg_data));
+ *len = strlen (data);
+
+ free (loadavg_data);
+ return err;
+}
+
+error_t procfs_read_nonpid_uptime (struct dir_entry *dir_entry,
+ off_t offset, size_t *len, void *data)
+{
+ char *uptime_data;
+ error_t err;
+ double uptime_secs, idle_time_secs;
+
+ struct timeval uptime_val;
+ struct timeval uptime, total_user_time, total_system_time;
+ struct timeval idle_time;
+
+
+ err = get_uptime (&uptime);
+ if (! err)
+ {
+ err = get_total_times (&total_user_time,
+ &total_system_time);
+ if (! err)
+ {
+ timersub (&uptime, &total_system_time,
+ &idle_time);
+
+ uptime_secs = (double) uptime.tv_sec +
+ (double) uptime.tv_usec / (1000 * 1000);
+
+ idle_time_secs = (double) idle_time.tv_sec +
+ (double) idle_time.tv_usec / (1000 * 1000);
+
+ if (asprintf (&uptime_data, "%.2f %.2f\n",
+ uptime_secs, idle_time_secs) == -1)
+ return errno;
+ }
+ }
+
+
+ memcpy (data, uptime_data, strlen(uptime_data));
+ *len = strlen (data);
+
+ free (uptime_data);
+ return err;
+}
+
+error_t procfs_read_nonpid_version (struct dir_entry *dir_entry,
+ off_t offset, size_t *len, void *data)
+{
+ char *version_data;
+ error_t err = 0;
+
+ if (asprintf (&version_data, "Linux version 2.6.18\n", NULL) == -1)
+ return errno;
+
+ memcpy (data, version_data, strlen(version_data));
+ *len = strlen (data);
+
+ free (version_data);
+ return err;
+}
diff --git a/procfs/procfs_pid.h b/procfs/procfs_pid.h
new file mode 100644
index 00000000..566c83ea
--- /dev/null
+++ b/procfs/procfs_pid.h
@@ -0,0 +1,88 @@
+/* procfs -- a translator for providing GNU/Linux compatible
+ proc pseudo-filesystem
+
+ procfs_pid.h -- This is the header file of which contains defintions
+ for structure of directory with PID as the name and
+ structure of each file in this directory.
+
+ Copyright (C) 2008, FSF.
+ Written as a Summer of Code Project
+
+ procfs is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ procfs is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
+*/
+
+#ifndef __PROCFS_PID_H__
+#define __PROCFS_PID_H__
+
+#include "procfs.h"
+
+struct procfs_pid_files
+{
+ struct procfs_cwd *procfs_cwd;
+ struct procfs_environ *procfs_environ;
+ struct procfs_cpu *procfs_cpu;
+ struct procfs_root *procfs_root;
+ struct procfs_exe *procfs_exe;
+ struct procfs_stat *_procfs_stat;
+ struct procfs_statm *procfs_statm;
+};
+
+struct procfs_stat
+{
+ pid_t pid;
+ char *comm;
+ char *state;
+ pid_t ppid;
+ pid_t pgid;
+ pid_t sid;
+ int tty_nr;
+ pid_t tty_pgrp;
+ unsigned flags;
+ long unsigned minflt;
+ long unsigned cminflt;
+ long unsigned majflt;
+ long unsigned cmajflt;
+ jiffy_t utime;
+ jiffy_t stime;
+ jiffy_t cutime;
+ jiffy_t cstime;
+ long priority;
+ long nice;
+ long num_threads;
+ long itrealvalue;
+ long long unsigned starttime;
+ long unsigned vsize;
+ long rss;
+ long unsigned rlim;
+ long unsigned startcode;
+ long unsigned endcode;
+ long unsigned startstack;
+ long unsigned kstkesp;
+ long unsigned kstkeip;
+ long unsigned signal;
+ long unsigned blocked;
+ long unsigned sigignore;
+ long unsigned sigcatch;
+ long unsigned wchan;
+ long unsigned nswap;
+ long unsigned cnswap;
+ int exit_signal;
+ int processor;
+ unsigned rt_priority;
+ unsigned policy;
+ long long unsigned delayacct_blkio_ticks;
+};
+
+#endif
diff --git a/procfs/procfs_pid_files.c b/procfs/procfs_pid_files.c
new file mode 100644
index 00000000..46861531
--- /dev/null
+++ b/procfs/procfs_pid_files.c
@@ -0,0 +1,576 @@
+/* procfs -- a translator for providing GNU/Linux compatible
+ proc pseudo-filesystem
+
+ procfs_pid_files.c -- This file contains definitions to perform
+ file operations such as creating, writing to,
+ reading from and removing files that holds
+ information for each process with PID
+
+ Copyright (C) 2008, FSF.
+ Written as a Summer of Code Project
+
+
+ procfs is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ procfs is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
+
+ A portion of the code in this file is based on ftpfs code
+ present in the hurd repositories copyrighted to FSF. The
+ Copyright notice from that file is given below.
+
+*/
+
+#include <hurd/netfs.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdio.h>
+#include <mach/task_info.h>
+#include <sys/resource.h>
+
+#include "procfs_pid.h"
+
+/* Update the files named NAME within the directory named
+ PID also with SYMLINK TARGET if necessary. */
+struct procfs_dir_entry*
+update_pid_entries (struct procfs_dir *dir, const char *name,
+ time_t timestamp,
+ const char *symlink_target)
+{
+ struct procfs_dir_entry *dir_entry;
+ struct stat *stat = (struct stat *) malloc (sizeof (struct stat));
+ stat->st_mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH;
+
+ dir_entry = update_entries_list (dir, name, stat,
+ timestamp, symlink_target);
+
+ return dir_entry;
+}
+
+/* Creates files to store process information for DIR
+ whose names are pids and returns these files in *NODE. */
+error_t
+procfs_create_files (struct procfs_dir *dir,
+ struct node **node,
+ time_t timestamp)
+{
+ int err;
+ char *file_name, *file_path;
+ struct procfs_dir_entry *dir_entry;
+
+ if (asprintf (&file_name, "%s", "stat") == -1)
+ return errno;
+ if (asprintf (&file_path, "%s/%s", dir->node->nn->dir_entry->name, "stat") == -1)
+ return errno;
+
+ dir_entry = update_pid_entries (dir, file_name, timestamp, NULL);
+ err = procfs_create_node (dir_entry, file_path, node);
+
+ free (file_name);
+ free (file_path);
+
+ if (asprintf (&file_name, "%s", "status") == -1)
+ return errno;
+ if (asprintf (&file_path, "%s/%s", dir->node->nn->dir_entry->name, "status") == -1)
+ return errno;
+
+ dir_entry = update_pid_entries (dir, file_name, timestamp, NULL);
+ err = procfs_create_node (dir_entry, file_path, node);
+
+ free (file_name);
+ free (file_path);
+
+ if (asprintf (&file_name, "%s", "cmdline") == -1)
+ return errno;
+ if (asprintf (&file_path, "%s/%s", dir->node->nn->dir_entry->name, "cmdline") == -1)
+ return errno;
+
+ dir_entry = update_pid_entries (dir, file_name, timestamp, NULL);
+ err = procfs_create_node (dir_entry, file_path, node);
+
+ free (file_name);
+ free (file_path);
+
+ if (asprintf (&file_name, "%s", "statm") == -1)
+ return errno;
+ if (asprintf (&file_path, "%s/%s", dir->node->nn->dir_entry->name, "statm") == -1)
+ return errno;
+
+ dir_entry = update_pid_entries (dir, file_name, timestamp, NULL);
+ err = procfs_create_node (dir_entry, file_path, node);
+
+ free (file_name);
+ free (file_path);
+
+#if 0
+ nodes_list = &node_stat;
+ nodes_list++;
+ node = nodes_list;
+#endif
+
+ return err;
+}
+
+/* Check if the PSTAT_FLAG is set in the corresponding PS
+ structure, if not set it and check again and return error
+ status accordingly. */
+error_t set_field_value (struct proc_stat *ps, int pstat_flag)
+{
+ error_t err;
+
+ if (! (ps->flags & pstat_flag))
+ {
+ err = proc_stat_set_flags (ps, pstat_flag);
+ if (err)
+ return err;
+
+ /* This second check is done since ps.h specifies to
+ do so since the previous call would not have set
+ the required value. */
+ if (! (ps->flags & pstat_flag))
+ return EGRATUITOUS;
+ }
+
+ return 0;
+}
+
+/* Adjusts TIME_VAL structure having Seconds and
+ Microseconds into the value in jiffies. The
+ value of jiffy is a hack to adjust to what
+ procps uses. */
+jiffy_t adjust_jiffy_time (time_value_t time_val)
+{
+ jiffy_t jiffy_time = time_val.seconds * JIFFY_ADJUST;
+ jiffy_time += (time_val.microseconds * JIFFY_ADJUST)
+ / (1000 * 1000);
+
+ return jiffy_time;
+}
+
+/* Extract the user and system time for the live threads of
+ the process. This information is directly retrieved from
+ MACH since neither libps not proc makes this available. */
+error_t get_task_thread_times (task_t task,
+ struct task_thread_times_info *live_threads_times)
+{
+ error_t err;
+ size_t tkcount = TASK_THREAD_TIMES_INFO_COUNT;
+
+ err = task_info (task, TASK_THREAD_TIMES_INFO,
+ (task_info_t) live_threads_times, &tkcount);
+ if (err == MACH_SEND_INVALID_DEST)
+ err = ESRCH;
+
+ return err;
+}
+
+/* Obtains the User Time in UTIME and System Time in STIME from
+ MACH directly since this is neither made available by libps
+ nor by proc server. */
+error_t get_live_threads_time (struct proc_stat *ps,
+ jiffy_t *utime, jiffy_t *stime)
+{
+ struct task_thread_times_info live_threads_times;
+ error_t err = set_field_value (ps, PSTAT_TASK);
+
+ if (! err)
+ {
+ err = get_task_thread_times (ps->task, &live_threads_times);
+ if (! err)
+ {
+ *utime = adjust_jiffy_time (
+ live_threads_times.user_time);
+ *stime = adjust_jiffy_time (
+ live_threads_times.system_time);
+ }
+ }
+
+ return err;
+}
+
+/* Get the data for stat file into the structure
+ PROCFS_STAT. */
+error_t get_stat_data (pid_t pid,
+ struct procfs_stat **procfs_stat)
+{
+ error_t err;
+ struct procfs_stat *new = (struct procfs_stat *)
+ malloc (sizeof (struct procfs_stat));
+
+ struct proc_stat *ps;
+ jiffy_t utime, stime;
+
+ err = _proc_stat_create (pid, ps_context, &ps);
+
+ new->pid = pid;
+
+ if (! err)
+ {
+ err = set_field_value (ps, PSTAT_ARGS);
+ if (! err)
+ asprintf (&new->comm, "%s", ps->args);
+ }
+
+ err = set_field_value (ps, PSTAT_STATE);
+ if (! err)
+ {
+ if (ps->state & PSTAT_STATE_P_STOP)
+ new->state = strdup ("T");
+ if (ps->state & PSTAT_STATE_P_ZOMBIE)
+ new->state = strdup ("Z");
+ if (ps->state & PSTAT_STATE_P_FG)
+ new->state = strdup ("+");
+ if (ps->state & PSTAT_STATE_P_SESSLDR)
+ new->state = strdup ("s");
+ if (ps->state & PSTAT_STATE_P_LOGINLDR)
+ new->state = strdup ("l");
+ if (ps->state & PSTAT_STATE_P_FORKED)
+ new->state = strdup ("f");
+ if (ps->state & PSTAT_STATE_P_NOMSG)
+ new->state = strdup ("m");
+ if (ps->state & PSTAT_STATE_P_NOPARENT)
+ new->state = strdup ("p");
+ if (ps->state & PSTAT_STATE_P_ORPHAN)
+ new->state = strdup ("o");
+ if (ps->state & PSTAT_STATE_P_TRACE)
+ new->state = strdup ("x");
+ if (ps->state & PSTAT_STATE_P_WAIT)
+ new->state = strdup ("w");
+ if (ps->state & PSTAT_STATE_P_GETMSG)
+ new->state = strdup ("g");
+ }
+
+ err = set_field_value (ps, PSTAT_PROC_INFO);
+ if (! err)
+ {
+ new->ppid = ps->proc_info->ppid;
+ new->pgid = ps->proc_info->pgrp;
+ new->sid = ps->proc_info->session;
+ new->tty_pgrp = ps->proc_info->pgrp;
+ }
+ else
+ {
+ new->ppid = 0;
+ new->pgid = 0;
+ new->sid = 0;
+ new->tty_pgrp = 0;
+ }
+
+ err = set_field_value (ps, PSTAT_STATE);
+ if (! err)
+ new->flags = ps->state;
+ else
+ new->flags = 0;
+
+ err = set_field_value (ps, PSTAT_TASK_EVENTS);
+ if (! err)
+ {
+ new->minflt = ps->task_events_info->faults;
+ new->majflt = ps->task_events_info->pageins;
+ }
+ else
+ {
+ new->minflt = 0;
+ new->majflt = 0;
+ }
+
+ /* This seems to be a bit inconsistent with setting of other
+ fields in this code. There are two reasons for this.
+ 1. The actual information required is not made available
+ by libps which should be directly obtained from MACH.
+ 2. The same code which is required to get the information
+ have to be reused in procfs_nonpid_files.c */
+ err = get_live_threads_time (ps, &utime, &stime);
+ if (! err)
+ {
+ new->utime = utime;
+ new->stime = stime;
+ }
+ else
+ {
+ new->utime = 0;
+ new->stime = 0;
+ }
+
+ err = set_field_value (ps, PSTAT_TASK_BASIC);
+ if (! err)
+ {
+ new->cutime = adjust_jiffy_time (
+ ps->task_basic_info->user_time);
+ new->cstime = adjust_jiffy_time (
+ ps->task_basic_info->system_time);
+
+ new->priority = ps->task_basic_info->base_priority;
+ new->starttime = adjust_jiffy_time (
+ ps->task_basic_info->creation_time);
+
+ new->vsize = ps->task_basic_info->virtual_size;
+ new->rss = ps->task_basic_info->resident_size;
+ }
+ else
+ {
+ new->cutime = 0;
+ new->cstime = 0;
+ new->priority = 0;
+ new->starttime = 0;
+ new->vsize = 0;
+ new->rss = 0;
+ }
+
+ new->nice = getpriority (0, pid);
+
+ err = set_field_value (ps, PSTAT_NUM_THREADS);
+ if (! err)
+ new->num_threads = ps->num_threads;
+ else
+ new->num_threads = 0;
+
+ /* Not Supported in Linux 2.6 or later. */
+ new->tty_nr = 0;
+ new->itrealvalue = 0;
+ new->nswap = 0;
+ new->cnswap = 0;
+
+ /* Temporarily set to 0 until correct
+ values are found .*/
+ new->cminflt = 0;
+ new->cmajflt = 0;
+ new->rlim = 0;
+ new->startcode = 0;
+ new->endcode = 0;
+ new->startstack = 0;
+ new->kstkesp = 0;
+ new->kstkeip = 0;
+ new->signal = 0;
+ new->blocked = 0;
+ new->sigignore = 0;
+ new->sigcatch = 0;
+ new->wchan = 0;
+ new->exit_signal = 0;
+ new->processor = 0;
+ new->rt_priority = 0;
+ new->policy = 0;
+ new->delayacct_blkio_ticks = 0;
+
+ *procfs_stat = new;
+ _proc_stat_free (ps);
+
+ return err;
+}
+
+/* Reads required process information from stat file
+ within the directory represented by pid. Return
+ the data in DATA and actual length to be written
+ in LEN. */
+error_t
+procfs_read_stat_file (struct procfs_dir_entry *dir_entry,
+ off_t offset, size_t *len, void *data)
+{
+ error_t err;
+ char *stat_data;
+ struct procfs_stat *procfs_stat;
+ pid_t pid = atoi (dir_entry->dir->node->nn->dir_entry->name);
+
+ err = get_stat_data (pid, &procfs_stat);
+
+ if (asprintf (&stat_data, "%d (%s) %s %d %d %d %d %d %u %lu %lu %lu %lu %lu %lu %ld %ld %ld %ld %ld %ld %llu %lu %ld %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %d %d %u %u %llu \n",
+ procfs_stat->pid, procfs_stat->comm,
+ procfs_stat->state, procfs_stat->ppid,
+ procfs_stat->pgid, procfs_stat->sid,
+ procfs_stat->tty_nr, procfs_stat->tty_pgrp,
+ procfs_stat->flags, procfs_stat->minflt,
+ procfs_stat->cminflt, procfs_stat->majflt,
+ procfs_stat->cmajflt, procfs_stat->utime,
+ procfs_stat->stime, procfs_stat->cutime,
+ procfs_stat->cstime, procfs_stat->priority,
+ procfs_stat->nice, procfs_stat->num_threads,
+ procfs_stat->itrealvalue, procfs_stat->starttime,
+ procfs_stat->vsize, BYTES_TO_PAGES(procfs_stat->rss),
+ procfs_stat->rlim, procfs_stat->startcode,
+ procfs_stat->endcode, procfs_stat->startstack,
+ procfs_stat->kstkesp, procfs_stat->kstkeip,
+ procfs_stat->signal, procfs_stat->blocked,
+ procfs_stat->sigignore, procfs_stat->sigcatch,
+ procfs_stat->wchan, procfs_stat->nswap,
+ procfs_stat->cnswap, procfs_stat->exit_signal,
+ procfs_stat->processor, procfs_stat->rt_priority,
+ procfs_stat->policy,
+ procfs_stat->delayacct_blkio_ticks) == -1)
+ return errno;
+
+
+ memcpy (data, stat_data, strlen(stat_data));
+ *len = strlen (data);
+
+ free (stat_data);
+ free (procfs_stat);
+
+ return err;
+}
+
+/* Reads required process's command line information
+ from cmline file within the directory represented
+ by pid. Return the data in DATA and actual length
+ to be written in LEN. */
+error_t
+procfs_read_cmdline_file (struct procfs_dir_entry *dir_entry,
+ off_t offset, size_t *len, void *data)
+{
+ char *cmdline_data;
+ error_t err;
+ struct proc_stat *ps;
+ pid_t pid = atoi (dir_entry->dir->node->nn->dir_entry->name);
+ err = _proc_stat_create (pid, ps_context, &ps);
+
+ err = set_field_value (ps, PSTAT_ARGS);
+
+ if (! err)
+ if (asprintf (&cmdline_data, "%s \n", ps->args) == -1)
+ return errno;
+
+ memcpy (data, cmdline_data, strlen(cmdline_data));
+ *len = strlen (data);
+
+ _proc_stat_free (ps);
+ free (cmdline_data);
+ return err;
+}
+
+/* Reads required process's information that is represented by
+ stat and statm in a human readable format from status file
+ within the directory represented by pid. Return the data
+ in DATA and actual length to be written in LEN. */
+error_t
+procfs_read_status_file (struct procfs_dir_entry *dir_entry,
+ off_t offset, size_t *len, void *data)
+{
+ char *status_data;
+ error_t err;
+ struct proc_stat *ps;
+ struct procfs_stat *procfs_stat;
+
+ pid_t pid = atoi (dir_entry->dir->node->nn->dir_entry->name);
+ err = _proc_stat_create (pid, ps_context, &ps);
+
+ err = get_stat_data (pid, &procfs_stat);
+
+ if (! err)
+ if (asprintf (&status_data, "Name:\t%s\nState:\t%s\nTgid:\t%d\nPid:\t%d\n", procfs_stat->comm, procfs_stat->state, procfs_stat->pid, procfs_stat->pid) == -1)
+ return errno;
+
+ memcpy (data, status_data, strlen(status_data));
+ *len = strlen (data);
+
+ _proc_stat_free (ps);
+
+ free (status_data);
+ free (procfs_stat);
+
+ return err;
+}
+
+/* Reads required process information from statm file
+ within the directory represented by pid. Return
+ the data in DATA and actual length to be written
+ in LEN. */
+error_t
+procfs_read_statm_file (struct procfs_dir_entry *dir_entry,
+ off_t offset, size_t *len, void *data)
+{
+ char *statm_data;
+ error_t err;
+ struct proc_stat *ps;
+ struct procfs_stat *procfs_stat;
+
+ pid_t pid = atoi (dir_entry->dir->node->nn->dir_entry->name);
+ err = _proc_stat_create (pid, ps_context, &ps);
+
+ err = get_stat_data (pid, &procfs_stat);
+
+ if (! err)
+ if (asprintf (&statm_data, "%lu %ld %d %d %d %d %d\n",
+ BYTES_TO_PAGES(procfs_stat->vsize),
+ BYTES_TO_PAGES(procfs_stat->rss),
+ 0, 0, 0, 0, 0) == -1)
+ return errno;
+
+ memcpy (data, statm_data, strlen(statm_data));
+ *len = strlen (data);
+
+ _proc_stat_free (ps);
+
+ free (statm_data);
+ free (procfs_stat);
+
+ return err;
+}
+
+/* Reads required process information from each of files
+ within directory represented by pid, for files specified
+ by NODE. Return the data in DATA and actual length of
+ data in LEN. */
+error_t
+procfs_read_files_contents (struct node *node,
+ off_t offset, size_t *len, void *data)
+{
+ error_t err;
+
+ if (! strcmp (node->nn->dir_entry->name, "stat"))
+ if (! strcmp (node->nn->dir_entry->dir->fs_path, ""))
+ err = procfs_read_nonpid_stat (node->nn->dir_entry,
+ offset, len, data);
+ else
+ err = procfs_read_stat_file (node->nn->dir_entry,
+ offset, len, data);
+
+ if (! strcmp (node->nn->dir_entry->name, "cmdline"))
+ err = procfs_read_cmdline_file (node->nn->dir_entry,
+ offset, len, data);
+
+ if (! strcmp (node->nn->dir_entry->name, "status"))
+ err = procfs_read_status_file (node->nn->dir_entry,
+ offset, len, data);
+
+ if (! strcmp (node->nn->dir_entry->name, "statm"))
+ err = procfs_read_statm_file (node->nn->dir_entry,
+ offset, len, data);
+
+ if (! strcmp (node->nn->dir_entry->name, "meminfo"))
+ if (! strcmp (node->nn->dir_entry->dir->fs_path, ""))
+ err = procfs_read_nonpid_meminfo (node->nn->dir_entry,
+ offset, len, data);
+ else
+ err = ENOENT;
+
+ if (! strcmp (node->nn->dir_entry->name, "loadavg"))
+ if (! strcmp (node->nn->dir_entry->dir->fs_path, ""))
+ err = procfs_read_nonpid_loadavg (node->nn->dir_entry,
+ offset, len, data);
+ else
+ err = ENOENT;
+
+ if (! strcmp (node->nn->dir_entry->name, "uptime"))
+ if (! strcmp (node->nn->dir_entry->dir->fs_path, ""))
+ err = procfs_read_nonpid_uptime (node->nn->dir_entry,
+ offset, len, data);
+ else
+ err = ENOENT;
+
+ if (! strcmp (node->nn->dir_entry->name, "version"))
+ if (! strcmp (node->nn->dir_entry->dir->fs_path, ""))
+ err = procfs_read_nonpid_version (node->nn->dir_entry,
+ offset, len, data);
+ else
+ err = ENOENT;
+
+ return err;
+}
diff --git a/serverboot/Makefile b/serverboot/Makefile
new file mode 100644
index 00000000..653d5963
--- /dev/null
+++ b/serverboot/Makefile
@@ -0,0 +1,53 @@
+# Copyright (C) 1997,99,2001,02 Free Software Foundation, Inc.
+# This file is part of the GNU Hurd.
+#
+# The GNU Hurd is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+#
+# The GNU Hurd is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with the GNU Hurd; see the file COPYING. If not, write to
+# the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+dir := serverboot
+makemode := utility
+
+SRCS = bootstrap.c ffs_compat.c load.c wiring.c \
+ ffs_file_io.c minix_ffs_compat.c file_io.c \
+ minix_file_io.c ext2_file_io.c strfcns.c exec.c \
+ panic.c elf-load.c gunzip.c bunzip2.c
+LCLHDRS = assert.h disk_inode_ffs.h fs.h queue.h defs.h \
+ minix_ffs_compat.h wiring.h dir.h ffs_compat.h minix_fs.h \
+ disk_inode.h file_io.h minix_super.h mach-exec.h
+EXTRA_DIST = def_pager_setup.c default_pager.c kalloc.c
+target = serverboot
+HURDLIBS = threads
+installationdir = $(bootdir)
+
+UNZIP_OBJS = unzip.o inflate.o util.o do-bunzip2.o
+OBJS = $(subst .c,.o,$(SRCS)) \
+ boot_script.o userland-boot.o \
+ $(UNZIP_OBJS)
+
+include ../Makeconf
+
+vpath boot_script.c $(srcdir)/../boot
+vpath userland-boot.c $(srcdir)/../boot
+
+# Look for zip stuff
+vpath %.c $(srcdir) $(srcdir)/../exec
+
+# If SMALL_BZIP2 is defined, use relatively small memory.
+# It's crucial for serverboot, because swap is not enabled yet.
+CPPFLAGS += -I$(srcdir)/../exec -DGZIP -DBZIP2 -DSMALL_BZIP2
+
+LDFLAGS += -static
+
+# Don't even bother.
+CFLAGS := $(filter-out -Wall,$(CFLAGS))
diff --git a/serverboot/assert.h b/serverboot/assert.h
new file mode 100644
index 00000000..9f70aec3
--- /dev/null
+++ b/serverboot/assert.h
@@ -0,0 +1,50 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990,1989,1988,1987 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+
+#ifndef _ASSERT_H_
+#define _ASSERT_H_
+
+#ifdef ASSERTIONS
+extern void Assert();
+
+#define assert(ex) \
+ do { \
+ if (!(ex)) \
+ Assert(__FILE__, __LINE__); \
+ } while (0)
+
+#ifdef lint
+#define assert_static(x)
+#else /* lint */
+#define assert_static(x) assert(x)
+#endif /* lint */
+
+#else /* ASSERTIONS */
+#define assert(ex)
+#define assert_static(ex)
+#endif /* ASSERTIONS */
+
+#endif /* _ASSERT_H_ */
diff --git a/serverboot/bootstrap.c b/serverboot/bootstrap.c
new file mode 100644
index 00000000..64f7c5eb
--- /dev/null
+++ b/serverboot/bootstrap.c
@@ -0,0 +1,438 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1992,1991,1990,1989 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie the
+ * rights to redistribute these changes.
+ */
+/*
+ * Bootstrap the various built-in servers.
+ */
+
+#include <mach.h>
+#include <mach/message.h>
+#include <sys/reboot.h>
+
+#include <file_io.h>
+
+#include <stdio.h>
+#include <string.h>
+
+#include "../boot/boot_script.h"
+
+#if 0
+/*
+ * Use 8 Kbyte stacks instead of the default 64K.
+ * Use 4 Kbyte waiting stacks instead of the default 8K.
+ */
+#if defined(alpha)
+vm_size_t cthread_stack_size = 16 * 1024;
+#else
+vm_size_t cthread_stack_size = 8 * 1024;
+#endif
+#endif
+
+extern
+vm_size_t cthread_wait_stack_size;
+
+mach_port_t bootstrap_master_device_port; /* local name */
+mach_port_t bootstrap_master_host_port; /* local name */
+
+int boot_load_program();
+
+char *root_name;
+char boot_script_name[MAXPATHLEN];
+
+extern void default_pager();
+extern void default_pager_initialize();
+extern void default_pager_setup();
+
+/*
+ * Convert ASCII to integer.
+ */
+int atoi(str)
+ register const char *str;
+{
+ register int n;
+ register int c;
+ int is_negative = 0;
+
+ n = 0;
+ while (*str == ' ')
+ str++;
+ if (*str == '-') {
+ is_negative = 1;
+ str++;
+ }
+ while ((c = *str++) >= '0' && c <= '9') {
+ n = n * 10 + (c - '0');
+ }
+ if (is_negative)
+ n = -n;
+ return (n);
+}
+
+__main ()
+{
+}
+
+static void
+boot_panic (kern_return_t err)
+{
+#define PFX "bootstrap: "
+ char *err_string = boot_script_error_string (err);
+ char panic_string[strlen (err_string) + sizeof (PFX)];
+ strcpy (panic_string, PFX);
+ strcat (panic_string, err_string);
+ panic (panic_string);
+#undef PFX
+}
+
+void
+safe_gets (char *str, int maxlen)
+{
+ char *c = fgets (str, maxlen, stdin);
+ if (c == 0) {
+ perror ("fgets");
+ panic ("cannot read from console");
+ }
+ c = strchr (c, '\n');
+ if (c)
+ *c = '\0';
+ printf ("\r\n");
+}
+
+printf_init (device_t master)
+{
+ mach_port_t cons;
+ kern_return_t rc;
+ rc = device_open (master, D_READ|D_WRITE, "console", &cons);
+ if (rc)
+ while (1) {
+ volatile int x = 0;
+ (void) host_reboot(bootstrap_master_host_port, RB_DEBUGGER);
+ x = x / x;
+ }
+ stdin = mach_open_devstream (cons, "r");
+ stdout = stderr = mach_open_devstream (cons, "w");
+ mach_port_deallocate (mach_task_self (), cons);
+ setbuf (stdout, 0);
+}
+
+/*
+ * Bootstrap task.
+ * Runs in user spacep.
+ *
+ * Called as 'boot -switches host_port device_port root_name'
+ *
+ */
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ int die = 0;
+ int script_paging_file (const struct cmd *cmd, integer_t *val)
+ {
+ printf ("*** paging files no longer supported in boot scripts ***\n\a"
+ "*** use swapon %s and/or /etc/fstab instead ***\n",
+ cmd->path);
+ return 0;
+ }
+ int script_serverboot_ctl (const struct cmd *cmd, integer_t *val)
+ {
+ const char *const ctl = cmd->path;
+ if (!strcmp (ctl, "die"))
+ die = 1;
+ else
+ printf ("(serverboot): Unknown control word `%s' ignored\n", ctl);
+ return 0;
+ }
+
+ void prompt_for_root ()
+ {
+ static char new_root[MAXPATHLEN/2];
+
+ if (!root_name)
+ root_name = "UNKNOWN";
+ printf ("Root device name? [%s] ", root_name);
+ safe_gets(new_root, sizeof(new_root));
+
+ if (new_root[0] != '\0') {
+ root_name = new_root;
+ (void) strbuild(boot_script_name,
+ "/dev/",
+ root_name,
+ "/boot/servers.boot",
+ (char *)0);
+ }
+ }
+
+ register kern_return_t result;
+ struct file scriptf;
+
+ task_t my_task = mach_task_self();
+
+ char *flag_string;
+
+ boolean_t ask_boot_script = 0;
+ boolean_t ask_root_name = 0;
+
+ /*
+ * Use 4Kbyte cthread wait stacks.
+ */
+ cthread_wait_stack_size = 4 * 1024;
+
+ /*
+ * Arg 1 is flags
+ */
+ if (argv[1][0] != '-')
+ panic("bootstrap: no flags");
+
+ flag_string = argv[1];
+
+ /*
+ * Parse the arguments.
+ */
+ if (argc >= 5)
+ {
+ /*
+ * Arg 0 is program name
+ */
+
+ /*
+ * Arg 2 is host port number
+ */
+ bootstrap_master_host_port = atoi(argv[2]);
+
+ /*
+ * Arg 3 is device port number
+ */
+ bootstrap_master_device_port = atoi(argv[3]);
+
+ /*
+ * Arg 4 is root name
+ */
+ root_name = argv[4];
+ }
+ else if (argc == 3)
+ {
+ root_name = argv[2];
+
+ get_privileged_ports (&bootstrap_master_host_port,
+ &bootstrap_master_device_port);
+ }
+
+ printf_init(bootstrap_master_device_port);
+#ifdef pleasenoXXX
+ panic_init(bootstrap_master_host_port);
+#endif
+
+
+ /*
+ * If the '-a' (ask) switch was specified, or if no
+ * root device was specificed, ask for the root device.
+ */
+
+ if (!root_name || root_name [0] == '\0' || index(flag_string, 'a'))
+ prompt_for_root ();
+
+ (void) strbuild(boot_script_name,
+ "/dev/",
+ root_name,
+ "/boot/servers.boot",
+ (char *)0);
+ /*
+ * If the '-q' (query) switch was specified, ask for the
+ * server boot script.
+ */
+
+ if (index(flag_string, 'q'))
+ ask_boot_script = TRUE;
+
+ while (TRUE) {
+ if (ask_root_name)
+ prompt_for_root ();
+
+ if (ask_boot_script) {
+ char new_boot_script[MAXPATHLEN];
+
+ printf("Server boot script? [%s] ", boot_script_name);
+ safe_gets(new_boot_script, sizeof(new_boot_script));
+ if (new_boot_script[0] != '\0')
+ strcpy(boot_script_name, new_boot_script);
+ }
+
+ result = open_file(bootstrap_master_device_port,
+ boot_script_name,
+ &scriptf);
+ if (result == D_NO_SUCH_DEVICE)
+ {
+ printf ("Root device `%s' does not exist!\n", root_name);
+ ask_root_name = ask_boot_script = TRUE;
+ continue;
+ }
+ else
+ ask_root_name = FALSE;
+ if (result != 0) {
+ printf("Can't open server boot script %s: %s\n",
+ boot_script_name,
+ strerror (result));
+ ask_boot_script = TRUE;
+ continue;
+ }
+ break;
+ }
+
+ /*
+ * If the server boot script name was changed,
+ * then use the new device name as the root device.
+ */
+ {
+ char *dev, *end;
+ int len;
+
+ dev = boot_script_name;
+ if (strncmp(dev, "/dev/", 5) == 0)
+ dev += 5;
+ end = strchr(dev, '/');
+ len = end ? end-dev : strlen(dev);
+ memcpy(root_name, dev, len);
+ root_name[len] = 0;
+ }
+
+ {
+ char *cmdline;
+
+ /* Initialize boot script variables. */
+ if (boot_script_set_variable ("host-port", VAL_PORT,
+ (integer_t) bootstrap_master_host_port)
+ || boot_script_set_variable ("device-port", VAL_PORT,
+ (integer_t) bootstrap_master_device_port)
+ || boot_script_set_variable ("root-device", VAL_STR,
+ (integer_t) root_name)
+ || boot_script_set_variable ("boot-args", VAL_STR,
+ (integer_t) flag_string)
+ || boot_script_define_function ("add-paging-file", VAL_NONE,
+ &script_paging_file)
+ || boot_script_define_function ("add-raw-paging-file", VAL_NONE,
+ &script_paging_file)
+ || boot_script_define_function ("add-linux-paging-file",
+ VAL_NONE,
+ &script_paging_file)
+ || boot_script_define_function ("serverboot",
+ VAL_NONE,
+ &script_serverboot_ctl)
+ )
+ panic ("bootstrap: error setting boot script variables");
+
+ cmdline = getenv ("MULTIBOOT_CMDLINE");
+ if (cmdline != NULL
+ && boot_script_set_variable ("kernel-command-line",
+ VAL_STR,
+ (integer_t) cmdline))
+ panic ("bootstrap: error setting boot script variables");
+
+ parse_script (&scriptf);
+ close_file (&scriptf);
+ }
+
+ if (index (flag_string, 'd'))
+ {
+ char xx[5];
+ printf ("Hit return to boot...");
+ safe_gets (xx, sizeof xx);
+ }
+
+ result = boot_script_exec ();
+
+ if (result)
+ boot_panic (result);
+
+#if 0
+ {
+ /*
+ * Delete the old stack (containing only the arguments).
+ */
+ vm_offset_t addr = (vm_offset_t) argv;
+
+ vm_offset_t r_addr;
+ vm_size_t r_size;
+ vm_prot_t r_protection, r_max_protection;
+ vm_inherit_t r_inheritance;
+ boolean_t r_is_shared;
+ memory_object_name_t r_object_name;
+ vm_offset_t r_offset;
+ kern_return_t kr;
+
+ r_addr = addr;
+
+ kr = vm_region(my_task,
+ &r_addr,
+ &r_size,
+ &r_protection,
+ &r_max_protection,
+ &r_inheritance,
+ &r_is_shared,
+ &r_object_name,
+ &r_offset);
+ if ((kr == KERN_SUCCESS) && MACH_PORT_VALID(r_object_name))
+ (void) mach_port_deallocate(my_task, r_object_name);
+ if ((kr == KERN_SUCCESS) &&
+ (r_addr <= addr) &&
+ ((r_protection & (VM_PROT_READ|VM_PROT_WRITE)) ==
+ (VM_PROT_READ|VM_PROT_WRITE)))
+ (void) vm_deallocate(my_task, r_addr, r_size);
+ }
+#endif
+
+ printf ("(serverboot): terminating\n");
+ while (1)
+ task_terminate (mach_task_self ());
+ /*NOTREACHED*/
+}
+
+/* Parse the boot script. */
+parse_script (struct file *f)
+{
+ char *p, *line, *buf;
+ int amt, fd, err;
+ int n = 0;
+
+ buf = malloc (f->f_size + 1); /* add one for null terminator we will write */
+ err = read_file (f, 0, buf, f->f_size, 0);
+ if (err)
+ panic ("bootstrap: error reading boot script file: %s", strerror (err));
+
+ line = p = buf;
+ while (1)
+ {
+ while (p < buf + f->f_size && *p != '\n')
+ p++;
+ *p = '\0';
+ err = boot_script_parse_line (0, line);
+ if (err)
+ boot_panic (err);
+ if (p == buf + f->f_size)
+ break;
+ line = ++p;
+ }
+ free (buf);
+}
diff --git a/serverboot/bunzip2.c b/serverboot/bunzip2.c
new file mode 100644
index 00000000..9f79ade5
--- /dev/null
+++ b/serverboot/bunzip2.c
@@ -0,0 +1,169 @@
+/* Modified by okuji@kuicr.kyoto-u.ac.jp for use in serverboot. */
+/* Decompressing store backend
+
+ Copyright (C) 1997 Free Software Foundation, Inc.
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <stdio.h>
+#include <string.h>
+#include <setjmp.h>
+#include <cthreads.h>
+#include <errno.h>
+
+#include <file_io.h>
+
+#define IN_BUFFERING (256*1024)
+#define OUT_BUFFERING (512*1024)
+
+static struct mutex bunzip2_lock = MUTEX_INITIALIZER;
+
+/* Uncompress the contents of FROM, which should contain a valid bzip2 file,
+ into memory, returning the result buffer in BUF & BUF_LEN. */
+int
+serverboot_bunzip2 (struct file *from, void **buf, size_t *buf_len)
+{
+ /* Callbacks from do_bunzip2 for I/O and error interface. */
+ extern int (*unzip_read) (char *buf, size_t maxread);
+ extern void (*unzip_write) (const char *buf, size_t nwrite);
+ extern void (*unzip_read_error) (void);
+ extern void (*unzip_error) (const char *msg);
+
+ /* How we return errors from our hook functions. */
+ jmp_buf zerr_jmp_buf;
+ int zerr;
+
+ size_t offset = 0; /* Offset of read point in FROM. */
+
+ /* Read at most MAXREAD (or 0 if eof) bytes into BUF from our current
+ position in FROM. */
+ int zread (char *buf, size_t maxread)
+ {
+ vm_size_t resid;
+ size_t did_read;
+
+ if (from->f_size - offset < maxread)
+ did_read = from->f_size - offset;
+ else
+ did_read = maxread;
+
+ zerr = read_file (from, offset, buf, did_read, &resid);
+ if (zerr)
+ longjmp (zerr_jmp_buf, 1);
+
+ did_read -= resid;
+ offset += did_read;
+
+ return did_read;
+ }
+
+ size_t out_buf_offs = 0; /* Position in the output buffer. */
+
+ /* Write uncompress data to our output buffer. */
+ void zwrite (const char *wbuf, size_t nwrite)
+ {
+ size_t old_buf_len = *buf_len;
+
+ if (out_buf_offs + nwrite > old_buf_len)
+ /* Have to grow the output buffer. */
+ {
+ void *old_buf = *buf;
+ void *new_buf = old_buf + old_buf_len; /* First try. */
+ size_t new_buf_len = round_page (old_buf_len + old_buf_len + nwrite);
+
+ /* Try to grow the buffer. */
+ zerr =
+ vm_allocate (mach_task_self (),
+ (vm_address_t *)&new_buf, new_buf_len - old_buf_len,
+ 0);
+ if (zerr)
+ /* Can't do that, try to make a bigger buffer elsewhere. */
+ {
+ new_buf = old_buf;
+ zerr =
+ vm_allocate (mach_task_self (),
+ (vm_address_t *)&new_buf, new_buf_len, 1);
+ if (zerr)
+ longjmp (zerr_jmp_buf, 1);
+
+ if (out_buf_offs > 0)
+ /* Copy the old buffer into the start of the new & free it. */
+ bcopy (old_buf, new_buf, out_buf_offs);
+
+ vm_deallocate (mach_task_self (),
+ (vm_address_t)old_buf, old_buf_len);
+
+ *buf = new_buf;
+ }
+
+ *buf_len = new_buf_len;
+ }
+
+ bcopy (wbuf, *buf + out_buf_offs, nwrite);
+ out_buf_offs += nwrite;
+ }
+
+ void zreaderr (void)
+ {
+ zerr = EIO;
+ longjmp (zerr_jmp_buf, 1);
+ }
+ void zerror (const char *msg)
+ {
+ zerr = EINVAL;
+ longjmp (zerr_jmp_buf, 2);
+ }
+
+ /* Try to guess a reasonable output buffer size. */
+ *buf_len = round_page (from->f_size * 2);
+ zerr = vm_allocate (mach_task_self (), (vm_address_t *)buf, *buf_len, 1);
+ if (zerr)
+ return zerr;
+
+ mutex_lock (&bunzip2_lock);
+
+ unzip_read = zread;
+ unzip_write = zwrite;
+ unzip_read_error = zreaderr;
+ unzip_error = zerror;
+
+ if (! setjmp (zerr_jmp_buf))
+ {
+ /* Call the bunzip2 engine. */
+ do_bunzip2 ();
+ zerr = 0;
+ }
+
+ mutex_unlock (&bunzip2_lock);
+
+ if (zerr)
+ {
+ if (*buf_len > 0)
+ vm_deallocate (mach_task_self (), (vm_address_t)*buf, *buf_len);
+ }
+ else if (out_buf_offs < *buf_len)
+ /* Trim the output buffer to be the right length. */
+ {
+ size_t end = round_page (out_buf_offs);
+ if (end < *buf_len)
+ vm_deallocate (mach_task_self (),
+ (vm_address_t)(*buf + end), *buf_len - end);
+ *buf_len = out_buf_offs;
+ }
+
+ return zerr;
+}
diff --git a/serverboot/def_pager_setup.c b/serverboot/def_pager_setup.c
new file mode 100644
index 00000000..5e2073ec
--- /dev/null
+++ b/serverboot/def_pager_setup.c
@@ -0,0 +1,152 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1992-1989 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+#include <mach.h>
+#include <mach/mig_errors.h>
+#include <mach/default_pager_types.h>
+
+#include <file_io.h>
+
+extern void *kalloc();
+
+/*
+ * Create a paging partition given a file name
+ */
+extern void create_paging_partition();
+
+kern_return_t
+add_paging_file(master_device_port, file_name, linux_signature)
+ mach_port_t master_device_port;
+ char *file_name;
+ int linux_signature;
+{
+ register struct file_direct *fdp;
+ register kern_return_t result;
+ struct file pfile;
+ boolean_t isa_file;
+
+ bzero((char *) &pfile, sizeof(struct file));
+
+ result = open_file(master_device_port, file_name, &pfile);
+ if (result != KERN_SUCCESS)
+ return result;
+
+ fdp = (struct file_direct *) kalloc(sizeof *fdp);
+ bzero((char *) fdp, sizeof *fdp);
+
+ isa_file = file_is_structured(&pfile);
+
+ result = open_file_direct(pfile.f_dev, fdp, isa_file);
+ if (result)
+ panic("Can't open paging file %s: %s\n",
+ file_name, strerror (result));
+
+ result = add_file_direct(fdp, &pfile);
+ if (result)
+ panic("Can't read disk addresses: %s\n", strerror (result));
+
+ close_file(&pfile);
+
+ /*
+ * Set up the default paging partition
+ */
+ create_paging_partition(file_name, fdp, isa_file, linux_signature);
+
+ return result;
+}
+
+/*
+ * Destroy a paging_partition given a file name
+ */
+kern_return_t
+remove_paging_file(file_name)
+ char *file_name;
+{
+ struct file_direct *fdp = 0;
+ kern_return_t kr;
+
+ kr = destroy_paging_partition(file_name, &fdp);
+ if (kr == KERN_SUCCESS) {
+ remove_file_direct(fdp);
+ kfree(fdp, sizeof(*fdp));
+ }
+ return kr;
+}
+
+kern_return_t
+default_pager_paging_storage (mach_port_t pager,
+ mach_port_t device,
+ recnum_t *runs, mach_msg_type_number_t nrun,
+ default_pager_filename_t name,
+ boolean_t add)
+{
+ return MIG_BAD_ID;
+}
+
+#if 0 /* no longer used */
+/*
+ * Set up default pager
+ */
+extern char *strbuild();
+
+boolean_t
+default_pager_setup(master_device_port, server_dir_name)
+ mach_port_t master_device_port;
+ char *server_dir_name;
+{
+ register kern_return_t result;
+
+ char paging_file_name[MAXPATHLEN+1];
+
+ (void) strbuild(paging_file_name,
+ server_dir_name,
+ "/paging_file",
+ (char *)0);
+
+ while (TRUE) {
+ result = add_paging_file(master_device_port, paging_file_name);
+ if (result == KERN_SUCCESS)
+ break;
+ printf("Can't open paging file %s: %d\n",
+ paging_file_name,
+ result);
+
+ bzero(paging_file_name, sizeof(paging_file_name));
+ printf("Paging file name ? ");
+ safe_gets(paging_file_name, sizeof(paging_file_name));
+
+ if (paging_file_name[0] == 0) {
+ printf("*** WARNING: running without paging area!\n");
+ return FALSE;
+ }
+ }
+
+ /*
+ * Our caller will become the default pager - later
+ */
+
+ return TRUE;
+}
+#endif
diff --git a/serverboot/default_pager.c b/serverboot/default_pager.c
new file mode 100644
index 00000000..c40f7181
--- /dev/null
+++ b/serverboot/default_pager.c
@@ -0,0 +1,3905 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1993-1989 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * Default pager. Pages to paging partition.
+ *
+ * MUST BE ABLE TO ALLOCATE WIRED-DOWN MEMORY!!!
+ */
+
+#include <mach.h>
+#include <mach/message.h>
+#include <mach/notify.h>
+#include <mach/mig_errors.h>
+#include <mach/thread_switch.h>
+#include <mach/task_info.h>
+#include <mach/default_pager_types.h>
+
+#include <cthreads.h>
+
+#include <device/device_types.h>
+#include <device/device.h>
+
+#include <queue.h>
+#include <wiring.h>
+
+#include <assert.h>
+#include <stdio.h>
+
+#include <file_io.h>
+
+#include "default_pager_S.h"
+
+#define debug 0
+
+extern void *kalloc();
+
+static char my_name[] = "(default pager):";
+
+static struct mutex printf_lock = MUTEX_INITIALIZER;
+
+#define dprintf(f, x...) \
+ ({ mutex_lock (&printf_lock); printf (f , ##x); fflush (stdout); mutex_unlock (&printf_lock); })
+#define ddprintf(f, x...) ((void)0)
+
+/*
+ * parallel vs serial switch
+ */
+#define PARALLEL 1
+
+#if 0
+#define CHECKSUM 1
+#endif
+
+#define USE_PRECIOUS 1
+
+#define ptoa(p) ((p)*vm_page_size)
+#define atop(a) ((a)/vm_page_size)
+
+/*
+
+ */
+/*
+ * Bitmap allocation.
+ */
+typedef unsigned int bm_entry_t;
+#define NB_BM 32
+#define BM_MASK 0xffffffff
+
+#define howmany(a,b) (((a) + (b) - 1)/(b))
+
+/*
+ * Value to indicate no block assigned
+ */
+#define NO_BLOCK ((vm_offset_t)-1)
+
+/*
+ * 'Partition' structure for each paging area.
+ * Controls allocation of blocks within paging area.
+ */
+struct part {
+ struct mutex p_lock; /* for bitmap/free */
+ vm_size_t total_size; /* total number of blocks */
+ vm_size_t free; /* number of blocks free */
+ unsigned int id; /* named lookup */
+ bm_entry_t *bitmap; /* allocation map */
+ boolean_t going_away; /* destroy attempt in progress */
+ struct file_direct *file; /* file paged to */
+};
+typedef struct part *partition_t;
+
+struct {
+ struct mutex lock;
+ int n_partitions;
+ partition_t *partition_list;/* array, for quick mapping */
+} all_partitions; /* list of all such */
+
+typedef unsigned char p_index_t;
+
+#define P_INDEX_INVALID ((p_index_t)-1)
+
+#define no_partition(x) ((x) == P_INDEX_INVALID)
+
+partition_t partition_of(x)
+ int x;
+{
+ if (x >= all_partitions.n_partitions || x < 0)
+ panic("partition_of x%x", x);
+ return all_partitions.partition_list[x];
+}
+
+void set_partition_of(x, p)
+ int x;
+ partition_t p;
+{
+ if (x >= all_partitions.n_partitions || x < 0)
+ panic("set_partition_of x%x", x);
+ all_partitions.partition_list[x] = p;
+}
+
+/*
+ * Simple mapping from (file)NAME to id
+ * Saves space, filenames can be long.
+ */
+unsigned int
+part_id(const unsigned char *name)
+{
+ register unsigned int len, id, xorid;
+
+ len = strlen(name);
+ id = xorid = 0;
+ while (len--) {
+ xorid ^= *name;
+ id += *name++;
+ }
+ return (id << 8) | xorid;
+}
+
+partition_init()
+{
+ mutex_init(&all_partitions.lock);
+ all_partitions.n_partitions = 0;
+}
+
+static partition_t
+new_partition (const char *name, struct file_direct *fdp,
+ int check_linux_signature)
+{
+ register partition_t part;
+ register vm_size_t size, bmsize;
+ vm_offset_t raddr;
+ mach_msg_type_number_t rsize;
+ int rc;
+ unsigned int id = part_id(name);
+
+ mutex_lock(&all_partitions.lock);
+ {
+ unsigned int i;
+ for (i = 0; i < all_partitions.n_partitions; i++)
+ {
+ part = partition_of(i);
+ if (part && part->id == id)
+ {
+ printf ("(default pager): Already paging to partition %s!\n",
+ name);
+ mutex_unlock(&all_partitions.lock);
+ return 0;
+ }
+ }
+ }
+ mutex_unlock(&all_partitions.lock);
+
+ size = atop(fdp->fd_size * fdp->fd_bsize);
+ bmsize = howmany(size, NB_BM) * sizeof(bm_entry_t);
+
+ part = (partition_t) kalloc(sizeof(struct part));
+ mutex_init(&part->p_lock);
+ part->total_size = size;
+ part->free = size;
+ part->id = id;
+ part->bitmap = (bm_entry_t *)kalloc(bmsize);
+ part->going_away= FALSE;
+ part->file = fdp;
+
+ bzero((char *)part->bitmap, bmsize);
+
+ if (check_linux_signature < 0)
+ {
+ if (check_linux_signature != -3)
+ printf("(default pager): "
+ "Paging to raw partition %s (%uk paging space)\n",
+ name, part->total_size * (vm_page_size / 1024));
+ return part;
+ }
+
+#define LINUX_PAGE_SIZE 4096 /* size of pages in Linux swap partitions */
+ rc = page_read_file_direct(part->file,
+ 0, LINUX_PAGE_SIZE,
+ &raddr,
+ &rsize);
+ if (rc)
+ panic("(default pager): cannot read first page of %s! rc=%#x\n",
+ name, rc);
+ while (rsize < LINUX_PAGE_SIZE)
+ {
+ /* Filesystem block size is smaller than page size,
+ so we must do several reads to get the whole page. */
+ vm_address_t baddr, bsize;
+ rc = page_read_file_direct(part->file,
+ rsize, LINUX_PAGE_SIZE-rsize,
+ &baddr,
+ &bsize);
+ if (rc)
+ panic("(default pager): "
+ "cannot read first page of %s! rc=%#x at %#x\n",
+ name, rc, rsize);
+
+ memcpy ((char *) raddr + rsize, (void *) baddr, bsize);
+ rsize += bsize;
+ vm_deallocate (mach_task_self (), baddr, bsize);
+ }
+
+ if (!memcmp("SWAP-SPACE", (char *) raddr + LINUX_PAGE_SIZE-10, 10))
+ {
+ /* The partition's first page has a Linux swap signature.
+ This means the beginning of the page contains a bitmap
+ of good pages, and all others are bad. */
+ unsigned int i, j, bad, max;
+ int waste;
+
+ printf("(default pager): Found Linux 2.0 swap signature in %s\n",
+ name);
+
+ /* The first page, and the pages corresponding to the bits
+ occupied by the signature in the final 10 bytes of the page,
+ are always unavailable ("bad"). */
+ *(u_int32_t *)raddr &= ~(u_int32_t) 1;
+ memset((char *) raddr + LINUX_PAGE_SIZE-10, 0, 10);
+
+ max = LINUX_PAGE_SIZE / sizeof(u_int32_t);
+ if (max > (part->total_size + 31) / 32)
+ max = (part->total_size + 31) / 32;
+
+ bad = 0;
+ for (i = 0; i < max; ++i)
+ {
+ u_int32_t bm = ((u_int32_t *) raddr)[i];
+ if (bm == ~(u_int32_t) 0)
+ continue;
+ /* There are some zero bits in this word. */
+ for (j = 0; j < 32; ++j)
+ if ((bm & (1 << j)) == 0)
+ {
+ unsigned int p = i*32 + j;
+ if (p >= part->total_size)
+ break;
+ ++bad;
+ part->bitmap[p / NB_BM] |= 1 << (p % NB_BM);
+ }
+ }
+ part->free -= bad;
+
+ --bad; /* Don't complain about first page. */
+ waste = part->total_size - (8 * (LINUX_PAGE_SIZE-10));
+ if (waste > 0)
+ {
+ /* The wasted pages were already marked "bad". */
+ bad -= waste;
+ if (bad > 0)
+ printf("\
+(default pager): Paging to %s, %dk swap-space (%dk bad, %dk wasted at end)\n",
+ name,
+ part->free * (LINUX_PAGE_SIZE / 1024),
+ bad * (LINUX_PAGE_SIZE / 1024),
+ waste * (LINUX_PAGE_SIZE / 1024));
+ else
+ printf("\
+(default pager): Paging to %s, %dk swap-space (%dk wasted at end)\n",
+ name,
+ part->free * (LINUX_PAGE_SIZE / 1024),
+ waste * (LINUX_PAGE_SIZE / 1024));
+ }
+ else if (bad > 0)
+ printf("\
+(default pager): Paging to %s, %dk swap-space (excludes %dk marked bad)\n",
+ name,
+ part->free * (LINUX_PAGE_SIZE / 1024),
+ bad * (LINUX_PAGE_SIZE / 1024));
+ else
+ printf("\
+(default pager): Paging to %s, %dk swap-space\n",
+ name,
+ part->free * (LINUX_PAGE_SIZE / 1024));
+ }
+ else if (!memcmp("SWAPSPACE2",
+ (char *) raddr + LINUX_PAGE_SIZE-10, 10))
+ {
+ struct
+ {
+ u_int8_t bootbits[1024];
+ u_int32_t version;
+ u_int32_t last_page;
+ u_int32_t nr_badpages;
+ u_int32_t padding[125];
+ u_int32_t badpages[1];
+ } *hdr = (void *) raddr;
+
+ printf("\
+(default pager): Found Linux 2.2 swap signature (v%u) in %s...",
+ hdr->version, name);
+
+ part->bitmap[0] |= 1; /* first page unusable */
+ part->free--;
+
+ switch (hdr->version)
+ {
+ default:
+ if (check_linux_signature)
+ {
+ printf ("version %u unknown! SKIPPING %s!\n",
+ hdr->version,
+ name);
+ vm_deallocate(mach_task_self(), raddr, rsize);
+ kfree(part->bitmap, bmsize);
+ kfree(part, sizeof *part);
+ return 0;
+ }
+ else
+ printf ("version %u unknown! IGNORING SIGNATURE PAGE!"
+ " %dk swap-space\n",
+ hdr->version,
+ part->free * (LINUX_PAGE_SIZE / 1024));
+ break;
+
+ case 1:
+ {
+ unsigned int waste, i;
+ if (hdr->last_page > part->total_size)
+ {
+ printf ("signature says %uk, partition has only %uk! ",
+ hdr->last_page * (LINUX_PAGE_SIZE / 1024),
+ part->total_size * (LINUX_PAGE_SIZE / 1024));
+ waste = 0;
+ }
+ else
+ {
+ waste = part->total_size - hdr->last_page;
+ part->total_size = hdr->last_page;
+ part->free = part->total_size - 1;
+ }
+ for (i = 0; i < hdr->nr_badpages; ++i)
+ {
+ const u_int32_t bad = hdr->badpages[i];
+ part->bitmap[bad / NB_BM] |= 1 << (bad % NB_BM);
+ part->free--;
+ }
+ printf ("%uk swap-space",
+ part->free * (LINUX_PAGE_SIZE / 1024));
+ if (hdr->nr_badpages != 0)
+ printf (" (excludes %uk marked bad)",
+ hdr->nr_badpages * (LINUX_PAGE_SIZE / 1024));
+ if (waste != 0)
+ printf (" (excludes %uk at end of partition)",
+ waste * (LINUX_PAGE_SIZE / 1024));
+ printf ("\n");
+ }
+ }
+ }
+ else if (check_linux_signature)
+ {
+ printf ("(default pager): "
+ "Cannot find Linux swap signature page! "
+ "SKIPPING %s (%uk partition)!",
+ name, part->total_size * (vm_page_size / 1024));
+ kfree(part->bitmap, bmsize);
+ kfree(part, sizeof *part);
+ part = 0;
+ }
+ else
+ printf("(default pager): "
+ "Paging to raw partition %s (%uk paging space)\n",
+ name, part->total_size * (vm_page_size / 1024));
+
+ vm_deallocate(mach_task_self(), raddr, rsize);
+
+ return part;
+}
+
+/*
+ * Create a partition descriptor,
+ * add it to the list of all such.
+ * size is in BYTES.
+ */
+void
+create_paging_partition(const char *name,
+ struct file_direct *fdp, int isa_file,
+ int linux_signature)
+{
+ register partition_t part;
+
+ part = new_partition (name, fdp, linux_signature);
+ if (!part)
+ return;
+
+ mutex_lock(&all_partitions.lock);
+ {
+ register int i;
+
+ for (i = 0; i < all_partitions.n_partitions; i++)
+ if (partition_of(i) == 0) break;
+
+ if (i == all_partitions.n_partitions) {
+ register partition_t *new_list, *old_list;
+ register int n;
+
+ n = i ? (i<<1) : 2;
+ new_list = (partition_t *)
+ kalloc( n * sizeof(partition_t) );
+ if (new_list == 0) no_paging_space(TRUE);
+ bzero(new_list, n*sizeof(partition_t));
+ if (i) {
+ old_list = all_partitions.partition_list;
+ bcopy(old_list, new_list, i*sizeof(partition_t));
+ }
+ all_partitions.partition_list = new_list;
+ all_partitions.n_partitions = n;
+ if (i) kfree(old_list, i*sizeof(partition_t));
+ }
+ set_partition_of(i, part);
+ }
+ mutex_unlock(&all_partitions.lock);
+
+#if 0
+ dprintf("%s Added paging %s %s\n", my_name,
+ (isa_file) ? "file" : "device", name);
+#endif
+ overcommitted(TRUE, part->free);
+}
+
+/*
+ * Choose the most appropriate default partition
+ * for an object of SIZE bytes.
+ * Return the partition locked, unless
+ * the object has no CUR_PARTition.
+ */
+p_index_t
+choose_partition(size, cur_part)
+ unsigned int size;
+ register p_index_t cur_part;
+{
+ register partition_t part;
+ register boolean_t found = FALSE;
+ register int i;
+
+ mutex_lock(&all_partitions.lock);
+ for (i = 0; i < all_partitions.n_partitions; i++) {
+
+ /* the undesireable one ? */
+ if (i == cur_part)
+ continue;
+
+ddprintf ("choose_partition(%x,%d,%d)\n",size,cur_part,i);
+ /* one that was removed ? */
+ if ((part = partition_of(i)) == 0)
+ continue;
+
+ /* one that is being removed ? */
+ if (part->going_away)
+ continue;
+
+ /* is it big enough ? */
+ mutex_lock(&part->p_lock);
+ if (ptoa(part->free) >= size) {
+ if (cur_part != P_INDEX_INVALID) {
+ mutex_unlock(&all_partitions.lock);
+ return (p_index_t)i;
+ } else
+ found = TRUE;
+ }
+ mutex_unlock(&part->p_lock);
+
+ if (found) break;
+ }
+ mutex_unlock(&all_partitions.lock);
+ return (found) ? (p_index_t)i : P_INDEX_INVALID;
+}
+
+/*
+ * Allocate a page in a paging partition
+ * The partition is returned unlocked.
+ */
+vm_offset_t
+pager_alloc_page(pindex, lock_it)
+ p_index_t pindex;
+{
+ register int bm_e;
+ register int bit;
+ register int limit;
+ register bm_entry_t *bm;
+ partition_t part;
+ static char here[] = "%spager_alloc_page";
+
+ if (no_partition(pindex))
+ return (NO_BLOCK);
+ddprintf ("pager_alloc_page(%d,%d)\n",pindex,lock_it);
+ part = partition_of(pindex);
+
+ /* unlikely, but possible deadlock against destroy_partition */
+ if (!part || part->going_away)
+ return (NO_BLOCK);
+
+ if (lock_it)
+ mutex_lock(&part->p_lock);
+
+ if (part->free == 0) {
+ /* out of paging space */
+ mutex_unlock(&part->p_lock);
+ return (NO_BLOCK);
+ }
+
+ limit = howmany(part->total_size, NB_BM);
+ bm = part->bitmap;
+ for (bm_e = 0; bm_e < limit; bm_e++, bm++)
+ if (*bm != BM_MASK)
+ break;
+
+ if (bm_e == limit)
+ panic(here,my_name);
+
+ /*
+ * Find and set the proper bit
+ */
+ {
+ register bm_entry_t b = *bm;
+
+ for (bit = 0; bit < NB_BM; bit++)
+ if ((b & (1<<bit)) == 0)
+ break;
+ if (bit == NB_BM)
+ panic(here,my_name);
+
+ *bm = b | (1<<bit);
+ part->free--;
+
+ }
+
+ mutex_unlock(&part->p_lock);
+
+ return (bm_e*NB_BM+bit);
+}
+
+/*
+ * Deallocate a page in a paging partition
+ */
+void
+pager_dealloc_page(pindex, page, lock_it)
+ p_index_t pindex;
+ register vm_offset_t page;
+{
+ register partition_t part;
+ register int bit, bm_e;
+
+ /* be paranoid */
+ if (no_partition(pindex))
+ panic("%sdealloc_page",my_name);
+ddprintf ("pager_dealloc_page(%d,%x,%d)\n",pindex,page,lock_it);
+ part = partition_of(pindex);
+
+ if (page >= part->total_size)
+ panic("%sdealloc_page",my_name);
+
+ bm_e = page / NB_BM;
+ bit = page % NB_BM;
+
+ if (lock_it)
+ mutex_lock(&part->p_lock);
+
+ part->bitmap[bm_e] &= ~(1<<bit);
+ part->free++;
+
+ if (lock_it)
+ mutex_unlock(&part->p_lock);
+}
+
+/*
+
+ */
+/*
+ * Allocation info for each paging object.
+ *
+ * Most operations, even pager_write_offset and pager_put_checksum,
+ * just need a read lock. Higher-level considerations prevent
+ * conflicting operations on a single page. The lock really protects
+ * the underlying size and block map memory, so pager_extend needs a
+ * write lock.
+ *
+ * An object can now span multiple paging partitions. The allocation
+ * info we keep is a pair (offset,p_index) where the index is in the
+ * array of all partition ptrs, and the offset is partition-relative.
+ * Size wise we are doing ok fitting the pair into a single integer:
+ * the offset really is in pages so we have vm_page_size bits available
+ * for the partition index.
+ */
+#define DEBUG_READER_CONFLICTS 0
+
+#if DEBUG_READER_CONFLICTS
+int default_pager_read_conflicts = 0;
+#endif
+
+union dp_map {
+
+ struct {
+ unsigned int p_offset : 24,
+ p_index : 8;
+ } block;
+
+ union dp_map *indirect;
+};
+typedef union dp_map *dp_map_t;
+
+/* quick check for part==block==invalid */
+#define no_block(e) ((e).indirect == (dp_map_t)NO_BLOCK)
+#define invalidate_block(e) ((e).indirect = (dp_map_t)NO_BLOCK)
+
+struct dpager {
+ struct mutex lock; /* lock for extending block map */
+ /* XXX should be read-write lock */
+#if DEBUG_READER_CONFLICTS
+ int readers;
+ boolean_t writer;
+#endif
+ dp_map_t map; /* block map */
+ vm_size_t size; /* size of paging object, in pages */
+ vm_size_t limit; /* limit (bytes) allowed to grow to */
+ p_index_t cur_partition;
+#ifdef CHECKSUM
+ vm_offset_t *checksum; /* checksum - parallel to block map */
+#define NO_CHECKSUM ((vm_offset_t)-1)
+#endif /* CHECKSUM */
+};
+typedef struct dpager *dpager_t;
+
+/*
+ * A paging object uses either a one- or a two-level map of offsets
+ * into a paging partition.
+ */
+#define PAGEMAP_ENTRIES 64
+ /* number of pages in a second-level map */
+#define PAGEMAP_SIZE(npgs) ((npgs)*sizeof(vm_offset_t))
+
+#define INDIRECT_PAGEMAP_ENTRIES(npgs) \
+ ((((npgs)-1)/PAGEMAP_ENTRIES) + 1)
+#define INDIRECT_PAGEMAP_SIZE(npgs) \
+ (INDIRECT_PAGEMAP_ENTRIES(npgs) * sizeof(vm_offset_t *))
+#define INDIRECT_PAGEMAP(size) \
+ (size > PAGEMAP_ENTRIES)
+
+#define ROUNDUP_TO_PAGEMAP(npgs) \
+ (((npgs) + PAGEMAP_ENTRIES - 1) & ~(PAGEMAP_ENTRIES - 1))
+
+/*
+ * Object sizes are rounded up to the next power of 2,
+ * unless they are bigger than a given maximum size.
+ */
+vm_size_t max_doubled_size = 4 * 1024 * 1024; /* 4 meg */
+
+/*
+ * Attach a new paging object to a paging partition
+ */
+void
+pager_alloc(pager, part, size)
+ register dpager_t pager;
+ p_index_t part;
+ register vm_size_t size; /* in BYTES */
+{
+ register int i;
+ register dp_map_t mapptr, emapptr;
+
+ mutex_init(&pager->lock);
+#if DEBUG_READER_CONFLICTS
+ pager->readers = 0;
+ pager->writer = FALSE;
+#endif
+ pager->cur_partition = part;
+
+ /*
+ * Convert byte size to number of pages, then increase to the nearest
+ * power of 2.
+ */
+ size = atop(size);
+ if (size <= atop(max_doubled_size)) {
+ i = 1;
+ while (i < size)
+ i <<= 1;
+ size = i;
+ } else
+ size = ROUNDUP_TO_PAGEMAP(size);
+
+ /*
+ * Allocate and initialize the block map
+ */
+ {
+ register vm_size_t alloc_size;
+ dp_map_t init_value;
+
+ if (INDIRECT_PAGEMAP(size)) {
+ alloc_size = INDIRECT_PAGEMAP_SIZE(size);
+ init_value = (dp_map_t)0;
+ } else {
+ alloc_size = PAGEMAP_SIZE(size);
+ init_value = (dp_map_t)NO_BLOCK;
+ }
+
+ mapptr = (dp_map_t) kalloc(alloc_size);
+ for (emapptr = &mapptr[(alloc_size-1) / sizeof(vm_offset_t)];
+ emapptr >= mapptr;
+ emapptr--)
+ emapptr->indirect = init_value;
+
+ }
+ pager->map = mapptr;
+ pager->size = size;
+ pager->limit = (vm_size_t)-1;
+
+#ifdef CHECKSUM
+ if (INDIRECT_PAGEMAP(size)) {
+ mapptr = (vm_offset_t *)
+ kalloc(INDIRECT_PAGEMAP_SIZE(size));
+ for (i = INDIRECT_PAGEMAP_ENTRIES(size); --i >= 0;)
+ mapptr[i] = 0;
+ } else {
+ mapptr = (vm_offset_t *) kalloc(PAGEMAP_SIZE(size));
+ for (i = 0; i < size; i++)
+ mapptr[i] = NO_CHECKSUM;
+ }
+ pager->checksum = mapptr;
+#endif /* CHECKSUM */
+}
+
+/*
+ * Return size (in bytes) of space actually allocated to this pager.
+ * The pager is read-locked.
+ */
+
+vm_size_t
+pager_allocated(pager)
+ register dpager_t pager;
+{
+ vm_size_t size;
+ register dp_map_t map, emap;
+ vm_size_t asize;
+
+ size = pager->size; /* in pages */
+ asize = 0; /* allocated, in pages */
+ map = pager->map;
+
+ if (INDIRECT_PAGEMAP(size)) {
+ for (emap = &map[INDIRECT_PAGEMAP_ENTRIES(size)];
+ map < emap; map++) {
+
+ register dp_map_t map2, emap2;
+
+ if ((map2 = map->indirect) == 0)
+ continue;
+
+ for (emap2 = &map2[PAGEMAP_ENTRIES];
+ map2 < emap2; map2++)
+ if ( ! no_block(*map2) )
+ asize++;
+
+ }
+ } else {
+ for (emap = &map[size]; map < emap; map++)
+ if ( ! no_block(*map) )
+ asize++;
+ }
+
+ return ptoa(asize);
+}
+
+/*
+ * Find offsets (in the object) of pages actually allocated to this pager.
+ * Returns the number of allocated pages, whether or not they all fit.
+ * The pager is read-locked.
+ */
+
+unsigned int
+pager_pages(pager, pages, numpages)
+ dpager_t pager;
+ register default_pager_page_t *pages;
+ unsigned int numpages;
+{
+ vm_size_t size;
+ dp_map_t map, emap;
+ unsigned int actual;
+ vm_offset_t offset;
+
+ size = pager->size; /* in pages */
+ map = pager->map;
+ actual = 0;
+ offset = 0;
+
+ if (INDIRECT_PAGEMAP(size)) {
+ for (emap = &map[INDIRECT_PAGEMAP_ENTRIES(size)];
+ map < emap; map++) {
+
+ register dp_map_t map2, emap2;
+
+ if ((map2 = map->indirect) == 0) {
+ offset += vm_page_size * PAGEMAP_ENTRIES;
+ continue;
+ }
+ for (emap2 = &map2[PAGEMAP_ENTRIES];
+ map2 < emap2; map2++)
+ if ( ! no_block(*map2) ) {
+ if (actual++ < numpages)
+ pages++->dpp_offset = offset;
+ }
+ offset += vm_page_size;
+ }
+ } else {
+ for (emap = &map[size]; map < emap; map++)
+ if ( ! no_block(*map) ) {
+ if (actual++ < numpages)
+ pages++->dpp_offset = offset;
+ }
+ offset += vm_page_size;
+ }
+ return actual;
+}
+
+/*
+ * Extend the map for a paging object.
+ *
+ * XXX This implementation can allocate an arbitrary large amount
+ * of wired memory when extending a big block map. Because vm-privileged
+ * threads call pager_extend, this can crash the system by exhausting
+ * system memory.
+ */
+void
+pager_extend(pager, new_size)
+ register dpager_t pager;
+ register vm_size_t new_size; /* in pages */
+{
+ register dp_map_t new_mapptr;
+ register dp_map_t old_mapptr;
+ register int i;
+ register vm_size_t old_size;
+
+ mutex_lock(&pager->lock); /* XXX lock_write */
+#if DEBUG_READER_CONFLICTS
+ pager->writer = TRUE;
+#endif
+ /*
+ * Double current size until we cover new size.
+ * If object is 'too big' just use new size.
+ */
+ old_size = pager->size;
+
+ if (new_size <= atop(max_doubled_size)) {
+ i = old_size;
+ while (i < new_size)
+ i <<= 1;
+ new_size = i;
+ } else
+ new_size = ROUNDUP_TO_PAGEMAP(new_size);
+
+ if (INDIRECT_PAGEMAP(old_size)) {
+ /*
+ * Pager already uses two levels. Allocate
+ * a larger indirect block.
+ */
+ new_mapptr = (dp_map_t)
+ kalloc(INDIRECT_PAGEMAP_SIZE(new_size));
+ old_mapptr = pager->map;
+ for (i = 0; i < INDIRECT_PAGEMAP_ENTRIES(old_size); i++)
+ new_mapptr[i] = old_mapptr[i];
+ for (; i < INDIRECT_PAGEMAP_ENTRIES(new_size); i++)
+ new_mapptr[i].indirect = (dp_map_t)0;
+ kfree((char *)old_mapptr, INDIRECT_PAGEMAP_SIZE(old_size));
+ pager->map = new_mapptr;
+ pager->size = new_size;
+#ifdef CHECKSUM
+ new_mapptr = (vm_offset_t *)
+ kalloc(INDIRECT_PAGEMAP_SIZE(new_size));
+ old_mapptr = pager->checksum;
+ for (i = 0; i < INDIRECT_PAGEMAP_ENTRIES(old_size); i++)
+ new_mapptr[i] = old_mapptr[i];
+ for (; i < INDIRECT_PAGEMAP_ENTRIES(new_size); i++)
+ new_mapptr[i] = 0;
+ kfree((char *)old_mapptr, INDIRECT_PAGEMAP_SIZE(old_size));
+ pager->checksum = new_mapptr;
+#endif /* CHECKSUM */
+#if DEBUG_READER_CONFLICTS
+ pager->writer = FALSE;
+#endif
+ mutex_unlock(&pager->lock);
+#if 0
+ ddprintf ("pager_extend 1 mapptr %x [3b] = %x\n", new_mapptr,
+ new_mapptr[0x3b]);
+ if (new_mapptr[0x3b].indirect > 0x10000
+ && new_mapptr[0x3b].indirect != NO_BLOCK)
+ panic ("debug panic");
+#endif
+ return;
+ }
+
+ if (INDIRECT_PAGEMAP(new_size)) {
+ /*
+ * Changing from direct map to indirect map.
+ * Allocate both indirect and direct map blocks,
+ * since second-level (direct) block must be
+ * full size (PAGEMAP_SIZE(PAGEMAP_ENTRIES)).
+ */
+
+ /*
+ * Allocate new second-level map first.
+ */
+ new_mapptr = (dp_map_t) kalloc(PAGEMAP_SIZE(PAGEMAP_ENTRIES));
+ old_mapptr = pager->map;
+ for (i = 0; i < old_size; i++)
+ new_mapptr[i] = old_mapptr[i];
+ for (; i < PAGEMAP_ENTRIES; i++)
+ invalidate_block(new_mapptr[i]);
+ kfree((char *)old_mapptr, PAGEMAP_SIZE(old_size));
+ old_mapptr = new_mapptr;
+
+#if 0
+ ddprintf ("pager_extend 2 mapptr %x [3b] = %x\n", new_mapptr,
+ new_mapptr[0x3b]);
+ if (new_mapptr[0x3b].indirect > 0x10000
+ && new_mapptr[0x3b].indirect != NO_BLOCK)
+ panic ("debug panic");
+#endif
+
+ /*
+ * Now allocate indirect map.
+ */
+ new_mapptr = (dp_map_t)
+ kalloc(INDIRECT_PAGEMAP_SIZE(new_size));
+ new_mapptr[0].indirect = old_mapptr;
+ for (i = 1; i < INDIRECT_PAGEMAP_ENTRIES(new_size); i++)
+ new_mapptr[i].indirect = 0;
+ pager->map = new_mapptr;
+ pager->size = new_size;
+#ifdef CHECKSUM
+ /*
+ * Allocate new second-level map first.
+ */
+ new_mapptr = (vm_offset_t *)kalloc(PAGEMAP_SIZE(PAGEMAP_ENTRIES));
+ old_mapptr = pager->checksum;
+ for (i = 0; i < old_size; i++)
+ new_mapptr[i] = old_mapptr[i];
+ for (; i < PAGEMAP_ENTRIES; i++)
+ new_mapptr[i] = NO_CHECKSUM;
+ kfree((char *)old_mapptr, PAGEMAP_SIZE(old_size));
+ old_mapptr = new_mapptr;
+
+ /*
+ * Now allocate indirect map.
+ */
+ new_mapptr = (vm_offset_t *)
+ kalloc(INDIRECT_PAGEMAP_SIZE(new_size));
+ new_mapptr[0] = (vm_offset_t) old_mapptr;
+ for (i = 1; i < INDIRECT_PAGEMAP_ENTRIES(new_size); i++)
+ new_mapptr[i] = 0;
+ pager->checksum = new_mapptr;
+#endif /* CHECKSUM */
+#if DEBUG_READER_CONFLICTS
+ pager->writer = FALSE;
+#endif
+ mutex_unlock(&pager->lock);
+ return;
+ }
+ /*
+ * Enlarging a direct block.
+ */
+ new_mapptr = (dp_map_t) kalloc(PAGEMAP_SIZE(new_size));
+ old_mapptr = pager->map;
+ for (i = 0; i < old_size; i++)
+ new_mapptr[i] = old_mapptr[i];
+ for (; i < new_size; i++)
+ invalidate_block(new_mapptr[i]);
+ kfree((char *)old_mapptr, PAGEMAP_SIZE(old_size));
+ pager->map = new_mapptr;
+ pager->size = new_size;
+#ifdef CHECKSUM
+ new_mapptr = (vm_offset_t *)
+ kalloc(PAGEMAP_SIZE(new_size));
+ old_mapptr = pager->checksum;
+ for (i = 0; i < old_size; i++)
+ new_mapptr[i] = old_mapptr[i];
+ for (; i < new_size; i++)
+ new_mapptr[i] = NO_CHECKSUM;
+ kfree((char *)old_mapptr, PAGEMAP_SIZE(old_size));
+ pager->checksum = new_mapptr;
+#endif /* CHECKSUM */
+#if DEBUG_READER_CONFLICTS
+ pager->writer = FALSE;
+#endif
+ mutex_unlock(&pager->lock);
+}
+
+/* Truncate a memory object. First, any pages between the new size
+ and the (larger) old size are deallocated. Then, the size of
+ the pagemap may be reduced, an indirect map may be turned into
+ a direct map.
+
+ The pager must be locked by the caller. */
+static void
+pager_truncate(dpager_t pager, vm_size_t new_size) /* in pages */
+{
+ dp_map_t new_mapptr;
+ dp_map_t old_mapptr;
+ int i;
+ vm_size_t old_size;
+
+ /* This deallocates the pages necessary to truncate a direct map
+ previously of size NEW_SIZE to the smaller size OLD_SIZE. */
+ inline void dealloc_direct (dp_map_t mapptr,
+ vm_size_t old_size, vm_size_t new_size)
+ {
+ vm_size_t i;
+ for (i = new_size; i < old_size; ++i)
+ {
+ const union dp_map entry = mapptr[i];
+ pager_dealloc_page(entry.block.p_index, entry.block.p_offset,
+ TRUE);
+ invalidate_block(mapptr[i]);
+ }
+ }
+
+ old_size = pager->size;
+
+ if (INDIRECT_PAGEMAP(old_size))
+ {
+ /* First handle the entire second-levels blocks that are being freed. */
+ for (i = INDIRECT_PAGEMAP_ENTRIES(new_size);
+ i < INDIRECT_PAGEMAP_ENTRIES(old_size);
+ ++i)
+ {
+ const dp_map_t mapptr = pager->map[i].indirect;
+ pager->map[i].indirect = (dp_map_t)0;
+ dealloc_direct (mapptr, PAGEMAP_ENTRIES, 0);
+ kfree ((char *)mapptr, PAGEMAP_SIZE(PAGEMAP_ENTRIES));
+ }
+
+ /* Now truncate what's now the final nonempty direct block. */
+ dealloc_direct (pager->map[(new_size - 1) / PAGEMAP_ENTRIES].indirect,
+ old_size & (PAGEMAP_ENTRIES - 1),
+ new_size & (PAGEMAP_ENTRIES - 1));
+
+ if (INDIRECT_PAGEMAP (new_size))
+ {
+ if (INDIRECT_PAGEMAP_SIZE (new_size) >= vm_page_size)
+ /* XXX we know how kalloc.c works; avoid copying. */
+ kfree ((char *) round_page ((vm_address_t) pager->map
+ + INDIRECT_PAGEMAP_SIZE (new_size)),
+ round_page (INDIRECT_PAGEMAP_SIZE (old_size))
+ - round_page (INDIRECT_PAGEMAP_SIZE (new_size)));
+ else
+ {
+ const dp_map_t old_mapptr = pager->map;
+ pager->map = (dp_map_t) kalloc (INDIRECT_PAGEMAP_SIZE(new_size));
+ memcpy (pager->map, old_mapptr, INDIRECT_PAGEMAP_SIZE(old_size));
+ kfree ((char *) old_mapptr, INDIRECT_PAGEMAP_SIZE (old_size));
+ }
+ }
+ else
+ {
+ /* We are truncating to a size small enough that it goes to using
+ a one-level map. We already have that map, as the first and only
+ nonempty element in our indirect map. */
+ const dp_map_t mapptr = pager->map[0].indirect;
+ kfree((char *)pager->map, INDIRECT_PAGEMAP_SIZE(old_size));
+ pager->map = mapptr;
+ old_size = PAGEMAP_ENTRIES;
+ }
+ }
+
+ if (new_size == 0)
+ new_size = 1;
+
+ if (! INDIRECT_PAGEMAP(old_size))
+ {
+ /* First deallocate pages in the truncated region. */
+ dealloc_direct (pager->map, old_size, new_size);
+ /* Now reduce the size of the direct map itself. We don't bother
+ with kalloc/kfree if it's not shrinking enough that kalloc.c
+ would actually use less. */
+ if (PAGEMAP_SIZE (new_size) <= PAGEMAP_SIZE (old_size) / 2)
+ {
+ const dp_map_t old_mapptr = pager->map;
+ pager->map = (dp_map_t) kalloc (PAGEMAP_SIZE (new_size));
+ memcpy (pager->map, old_mapptr, PAGEMAP_SIZE (old_size));
+ kfree ((char *) old_mapptr, PAGEMAP_SIZE (old_size));
+ }
+ }
+
+ pager->size = new_size;
+
+#ifdef CHECKSUM
+#error write me
+#endif /* CHECKSUM */
+}
+
+
+/*
+ * Given an offset within a paging object, find the
+ * corresponding block within the paging partition.
+ * Return NO_BLOCK if none allocated.
+ */
+union dp_map
+pager_read_offset(pager, offset)
+ register dpager_t pager;
+ vm_offset_t offset;
+{
+ register vm_offset_t f_page;
+ union dp_map pager_offset;
+
+ f_page = atop(offset);
+
+#if DEBUG_READER_CONFLICTS
+ if (pager->readers > 0)
+ default_pager_read_conflicts++; /* would have proceeded with
+ read/write lock */
+#endif
+ mutex_lock(&pager->lock); /* XXX lock_read */
+#if DEBUG_READER_CONFLICTS
+ pager->readers++;
+#endif
+ if (f_page >= pager->size)
+ {
+ ddprintf ("%spager_read_offset pager %x: bad page %d >= size %d",
+ my_name, pager, f_page, pager->size);
+ return (union dp_map) (union dp_map *) NO_BLOCK;
+#if 0
+ panic("%spager_read_offset",my_name);
+#endif
+ }
+
+ if (INDIRECT_PAGEMAP(pager->size)) {
+ register dp_map_t mapptr;
+
+ mapptr = pager->map[f_page/PAGEMAP_ENTRIES].indirect;
+ if (mapptr == 0)
+ invalidate_block(pager_offset);
+ else
+ pager_offset = mapptr[f_page%PAGEMAP_ENTRIES];
+ }
+ else {
+ pager_offset = pager->map[f_page];
+ }
+
+#if DEBUG_READER_CONFLICTS
+ pager->readers--;
+#endif
+ mutex_unlock(&pager->lock);
+ return (pager_offset);
+}
+
+#if USE_PRECIOUS
+/*
+ * Release a single disk block.
+ */
+pager_release_offset(pager, offset)
+ register dpager_t pager;
+ vm_offset_t offset;
+{
+ register union dp_map entry;
+
+ offset = atop(offset);
+
+ mutex_lock(&pager->lock); /* XXX lock_read */
+
+ if (INDIRECT_PAGEMAP(pager->size)) {
+ register dp_map_t mapptr;
+
+ mapptr = pager->map[offset / PAGEMAP_ENTRIES].indirect;
+ entry = mapptr[offset % PAGEMAP_ENTRIES];
+ invalidate_block(mapptr[offset % PAGEMAP_ENTRIES]);
+ } else {
+ entry = pager->map[offset];
+ invalidate_block(pager->map[offset]);
+ }
+
+ mutex_unlock(&pager->lock);
+
+ pager_dealloc_page(entry.block.p_index, entry.block.p_offset, TRUE);
+}
+#endif /*USE_PRECIOUS*/
+
+
+/*
+ * Move a page from one partition to another
+ * New partition is locked, old partition is
+ * locked unless LOCK_OLD sez otherwise.
+ */
+union dp_map
+pager_move_page(block)
+ union dp_map block;
+{
+ partition_t old_part, new_part;
+ p_index_t old_pindex, new_pindex;
+ union dp_map ret;
+ vm_size_t size;
+ vm_offset_t raddr, offset, new_offset;
+ kern_return_t rc;
+ static char here[] = "%spager_move_page";
+
+ old_pindex = block.block.p_index;
+ invalidate_block(ret);
+
+ /* See if we have room to put it anywhere else */
+ new_pindex = choose_partition( ptoa(1), old_pindex);
+ if (no_partition(new_pindex))
+ return ret;
+
+ /* this unlocks the new partition */
+ new_offset = pager_alloc_page(new_pindex, FALSE);
+ if (new_offset == NO_BLOCK)
+ panic(here,my_name);
+
+ /*
+ * Got the resources, now move the data
+ */
+ddprintf ("pager_move_page(%x,%d,%d)\n",block.block.p_offset,old_pindex,new_pindex);
+ old_part = partition_of(old_pindex);
+ offset = ptoa(block.block.p_offset);
+ rc = page_read_file_direct (old_part->file,
+ offset,
+ vm_page_size,
+ &raddr,
+ &size);
+ if (rc != 0)
+ panic(here,my_name);
+
+ /* release old */
+ pager_dealloc_page(old_pindex, block.block.p_offset, FALSE);
+
+ new_part = partition_of(new_pindex);
+ offset = ptoa(new_offset);
+ rc = page_write_file_direct (new_part->file,
+ offset,
+ raddr,
+ size,
+ &size);
+ if (rc != 0)
+ panic(here,my_name);
+
+ (void) vm_deallocate( mach_task_self(), raddr, size);
+
+ ret.block.p_offset = new_offset;
+ ret.block.p_index = new_pindex;
+
+ return ret;
+}
+
+#ifdef CHECKSUM
+/*
+ * Return the checksum for a block.
+ */
+int
+pager_get_checksum(pager, offset)
+ register dpager_t pager;
+ vm_offset_t offset;
+{
+ register vm_offset_t f_page;
+ int checksum;
+
+ f_page = atop(offset);
+
+ mutex_lock(&pager->lock); /* XXX lock_read */
+ if (f_page >= pager->size)
+ panic("%spager_get_checksum",my_name);
+
+ if (INDIRECT_PAGEMAP(pager->size)) {
+ register vm_offset_t *mapptr;
+
+ mapptr = (vm_offset_t *)pager->checksum[f_page/PAGEMAP_ENTRIES];
+ if (mapptr == 0)
+ checksum = NO_CHECKSUM;
+ else
+ checksum = mapptr[f_page%PAGEMAP_ENTRIES];
+ }
+ else {
+ checksum = pager->checksum[f_page];
+ }
+
+ mutex_unlock(&pager->lock);
+ return (checksum);
+}
+
+/*
+ * Remember the checksum for a block.
+ */
+int
+pager_put_checksum(pager, offset, checksum)
+ register dpager_t pager;
+ vm_offset_t offset;
+ int checksum;
+{
+ register vm_offset_t f_page;
+ static char here[] = "%spager_put_checksum";
+
+ f_page = atop(offset);
+
+ mutex_lock(&pager->lock); /* XXX lock_read */
+ if (f_page >= pager->size)
+ panic(here,my_name);
+
+ if (INDIRECT_PAGEMAP(pager->size)) {
+ register vm_offset_t *mapptr;
+
+ mapptr = (vm_offset_t *)pager->checksum[f_page/PAGEMAP_ENTRIES];
+ if (mapptr == 0)
+ panic(here,my_name);
+
+ mapptr[f_page%PAGEMAP_ENTRIES] = checksum;
+ }
+ else {
+ pager->checksum[f_page] = checksum;
+ }
+ mutex_unlock(&pager->lock);
+}
+
+/*
+ * Compute a checksum - XOR each 32-bit word.
+ */
+int
+compute_checksum(addr, size)
+ vm_offset_t addr;
+ vm_size_t size;
+{
+ register int checksum = NO_CHECKSUM;
+ register int *ptr;
+ register int count;
+
+ ptr = (int *)addr;
+ count = size / sizeof(int);
+
+ while (--count >= 0)
+ checksum ^= *ptr++;
+
+ return (checksum);
+}
+#endif /* CHECKSUM */
+
+/*
+ * Given an offset within a paging object, find the
+ * corresponding block within the paging partition.
+ * Allocate a new block if necessary.
+ *
+ * WARNING: paging objects apparently may be extended
+ * without notice!
+ */
+union dp_map
+pager_write_offset(pager, offset)
+ register dpager_t pager;
+ vm_offset_t offset;
+{
+ register vm_offset_t f_page;
+ register dp_map_t mapptr;
+ register union dp_map block;
+
+ invalidate_block(block);
+
+ f_page = atop(offset);
+
+#if DEBUG_READER_CONFLICTS
+ if (pager->readers > 0)
+ default_pager_read_conflicts++; /* would have proceeded with
+ read/write lock */
+#endif
+ mutex_lock(&pager->lock); /* XXX lock_read */
+#if DEBUG_READER_CONFLICTS
+ pager->readers++;
+#endif
+
+ /* Catch the case where we had no initial fit partition
+ for this object, but one was added later on */
+ if (no_partition(pager->cur_partition)) {
+ p_index_t new_part;
+ vm_size_t size;
+
+ size = (f_page > pager->size) ? f_page : pager->size;
+ new_part = choose_partition(ptoa(size), P_INDEX_INVALID);
+ if (no_partition(new_part))
+ new_part = choose_partition(ptoa(1), P_INDEX_INVALID);
+ if (no_partition(new_part))
+ /* give up right now to avoid confusion */
+ goto out;
+ else
+ pager->cur_partition = new_part;
+ }
+
+ while (f_page >= pager->size) {
+ ddprintf ("pager_write_offset: extending: %x %x\n", f_page, pager->size);
+
+ /*
+ * Paging object must be extended.
+ * Remember that offset is 0-based, but size is 1-based.
+ */
+#if DEBUG_READER_CONFLICTS
+ pager->readers--;
+#endif
+ mutex_unlock(&pager->lock);
+ pager_extend(pager, f_page + 1);
+#if DEBUG_READER_CONFLICTS
+ if (pager->readers > 0)
+ default_pager_read_conflicts++; /* would have proceeded with
+ read/write lock */
+#endif
+ mutex_lock(&pager->lock); /* XXX lock_read */
+#if DEBUG_READER_CONFLICTS
+ pager->readers++;
+#endif
+ ddprintf ("pager_write_offset: done extending: %x %x\n", f_page, pager->size);
+ }
+
+ if (INDIRECT_PAGEMAP(pager->size)) {
+ ddprintf ("pager_write_offset: indirect\n");
+ mapptr = pager->map[f_page/PAGEMAP_ENTRIES].indirect;
+ if (mapptr == 0) {
+ /*
+ * Allocate the indirect block
+ */
+ register int i;
+ ddprintf ("pager_write_offset: allocating indirect\n");
+
+ mapptr = (dp_map_t) kalloc(PAGEMAP_SIZE(PAGEMAP_ENTRIES));
+ if (mapptr == 0) {
+ /* out of space! */
+ no_paging_space(TRUE);
+ goto out;
+ }
+ pager->map[f_page/PAGEMAP_ENTRIES].indirect = mapptr;
+ for (i = 0; i < PAGEMAP_ENTRIES; i++)
+ invalidate_block(mapptr[i]);
+#ifdef CHECKSUM
+ {
+ register vm_offset_t *cksumptr;
+ register int j;
+
+ cksumptr = (vm_offset_t *)
+ kalloc(PAGEMAP_SIZE(PAGEMAP_ENTRIES));
+ if (cksumptr == 0) {
+ /* out of space! */
+ no_paging_space(TRUE);
+ goto out;
+ }
+ pager->checksum[f_page/PAGEMAP_ENTRIES]
+ = (vm_offset_t)cksumptr;
+ for (j = 0; j < PAGEMAP_ENTRIES; j++)
+ cksumptr[j] = NO_CHECKSUM;
+ }
+#endif /* CHECKSUM */
+ }
+ f_page %= PAGEMAP_ENTRIES;
+ }
+ else {
+ mapptr = pager->map;
+ }
+
+ block = mapptr[f_page];
+ ddprintf ("pager_write_offset: block starts as %x[%x] %x\n", mapptr, f_page, block);
+ if (no_block(block)) {
+ vm_offset_t off;
+
+ /* get room now */
+ off = pager_alloc_page(pager->cur_partition, TRUE);
+ if (off == NO_BLOCK) {
+ /*
+ * Before giving up, try all other partitions.
+ */
+ p_index_t new_part;
+
+ ddprintf ("pager_write_offset: could not allocate block\n");
+ /* returns it locked (if any one is non-full) */
+ new_part = choose_partition( ptoa(1), pager->cur_partition);
+ if ( ! no_partition(new_part) ) {
+
+#if debug
+dprintf("%s partition %x filled,", my_name, pager->cur_partition);
+dprintf("extending object %x (size %x) to %x.\n",
+ pager, pager->size, new_part);
+#endif
+
+ /* this one tastes better */
+ pager->cur_partition = new_part;
+
+ /* this unlocks the partition too */
+ off = pager_alloc_page(pager->cur_partition, FALSE);
+
+ }
+
+ if (off == NO_BLOCK) {
+ /*
+ * Oh well.
+ */
+ overcommitted(FALSE, 1);
+ goto out;
+ }
+ ddprintf ("pager_write_offset: decided to allocate block\n");
+ }
+ block.block.p_offset = off;
+ block.block.p_index = pager->cur_partition;
+ mapptr[f_page] = block;
+ ddprintf ("pager_write_offset: mapptr %x [3b] = %x\n", mapptr,
+ mapptr[0x3b]);
+ ddprintf ("pager_write_offset: block is finally %x\n", block);
+ }
+
+out:
+
+#if DEBUG_READER_CONFLICTS
+ pager->readers--;
+#endif
+ mutex_unlock(&pager->lock);
+ return (block);
+}
+
+/*
+ * Deallocate all of the blocks belonging to a paging object.
+ * No locking needed because no other operations can be in progress.
+ */
+void
+pager_dealloc(pager)
+ register dpager_t pager;
+{
+ register int i, j;
+ register dp_map_t mapptr;
+ register union dp_map block;
+
+ if (INDIRECT_PAGEMAP(pager->size)) {
+ for (i = INDIRECT_PAGEMAP_ENTRIES(pager->size); --i >= 0; ) {
+ mapptr = pager->map[i].indirect;
+ if (mapptr != 0) {
+ for (j = 0; j < PAGEMAP_ENTRIES; j++) {
+ block = mapptr[j];
+ if ( ! no_block(block) )
+ pager_dealloc_page(block.block.p_index,
+ block.block.p_offset, TRUE);
+ }
+ kfree((char *)mapptr, PAGEMAP_SIZE(PAGEMAP_ENTRIES));
+ }
+ }
+ kfree((char *)pager->map, INDIRECT_PAGEMAP_SIZE(pager->size));
+#ifdef CHECKSUM
+ for (i = INDIRECT_PAGEMAP_ENTRIES(pager->size); --i >= 0; ) {
+ mapptr = (vm_offset_t *)pager->checksum[i];
+ if (mapptr) {
+ kfree((char *)mapptr, PAGEMAP_SIZE(PAGEMAP_ENTRIES));
+ }
+ }
+ kfree((char *)pager->checksum,
+ INDIRECT_PAGEMAP_SIZE(pager->size));
+#endif /* CHECKSUM */
+ }
+ else {
+ mapptr = pager->map;
+ for (i = 0; i < pager->size; i++ ) {
+ block = mapptr[i];
+ if ( ! no_block(block) )
+ pager_dealloc_page(block.block.p_index,
+ block.block.p_offset, TRUE);
+ }
+ kfree((char *)pager->map, PAGEMAP_SIZE(pager->size));
+#ifdef CHECKSUM
+ kfree((char *)pager->checksum, PAGEMAP_SIZE(pager->size));
+#endif /* CHECKSUM */
+ }
+}
+
+/*
+ * Move all the pages of a PAGER that live in a
+ * partition PINDEX somewhere else.
+ * Pager should be write-locked, partition too.
+ * Returns FALSE if it could not do it, but
+ * some pages might have been moved nonetheless.
+ */
+boolean_t
+pager_realloc(pager, pindex)
+ register dpager_t pager;
+ p_index_t pindex;
+{
+ register dp_map_t map, emap;
+ vm_size_t size;
+ union dp_map block;
+
+ size = pager->size; /* in pages */
+ map = pager->map;
+
+ if (INDIRECT_PAGEMAP(size)) {
+ for (emap = &map[INDIRECT_PAGEMAP_ENTRIES(size)];
+ map < emap; map++) {
+
+ register dp_map_t map2, emap2;
+
+ if ((map2 = map->indirect) == 0)
+ continue;
+
+ for (emap2 = &map2[PAGEMAP_ENTRIES];
+ map2 < emap2; map2++)
+ if ( map2->block.p_index == pindex) {
+
+ block = pager_move_page(*map2);
+ if (!no_block(block))
+ *map2 = block;
+ else
+ return FALSE;
+ }
+
+ }
+ goto ok;
+ }
+
+ /* A small one */
+ for (emap = &map[size]; map < emap; map++)
+ if (map->block.p_index == pindex) {
+ block = pager_move_page(*map);
+ if (!no_block(block))
+ *map = block;
+ else
+ return FALSE;
+ }
+ok:
+ pager->cur_partition = choose_partition(0, P_INDEX_INVALID);
+ return TRUE;
+}
+
+/*
+
+ */
+
+/*
+ * Read/write routines.
+ */
+#define PAGER_SUCCESS 0
+#define PAGER_ABSENT 1
+#define PAGER_ERROR 2
+
+/*
+ * Read data from a default pager. Addr is the address of a buffer
+ * to fill. Out_addr returns the buffer that contains the data;
+ * if it is different from <addr>, it must be deallocated after use.
+ */
+int
+default_read(ds, addr, size, offset, out_addr, deallocate)
+ register dpager_t ds;
+ vm_offset_t addr; /* pointer to block to fill */
+ register vm_size_t size;
+ register vm_offset_t offset;
+ vm_offset_t *out_addr;
+ /* returns pointer to data */
+ boolean_t deallocate;
+{
+ register union dp_map block;
+ vm_offset_t raddr;
+ vm_size_t rsize;
+ register int rc;
+ boolean_t first_time;
+ register partition_t part;
+#ifdef CHECKSUM
+ vm_size_t original_size = size;
+#endif /* CHECKSUM */
+ vm_offset_t original_offset = offset;
+
+ /*
+ * Find the block in the paging partition
+ */
+ block = pager_read_offset(ds, offset);
+ if ( no_block(block) )
+ return (PAGER_ABSENT);
+
+ /*
+ * Read it, trying for the entire page.
+ */
+ offset = ptoa(block.block.p_offset);
+ddprintf ("default_read(%x,%x,%x,%d)\n",addr,size,offset,block.block.p_index);
+ part = partition_of(block.block.p_index);
+ first_time = TRUE;
+ *out_addr = addr;
+
+ do {
+ rc = page_read_file_direct(part->file,
+ offset,
+ size,
+ &raddr,
+ &rsize);
+ if (rc != 0)
+ return (PAGER_ERROR);
+
+ /*
+ * If we got the entire page on the first read, return it.
+ */
+ if (first_time && rsize == size) {
+ *out_addr = raddr;
+ break;
+ }
+ /*
+ * Otherwise, copy the data into the
+ * buffer we were passed, and try for
+ * the next piece.
+ */
+ first_time = FALSE;
+ bcopy((char *)raddr, (char *)addr, rsize);
+ addr += rsize;
+ offset += rsize;
+ size -= rsize;
+ } while (size != 0);
+
+#if USE_PRECIOUS
+ if (deallocate)
+ pager_release_offset(ds, original_offset);
+#endif /*USE_PRECIOUS*/
+
+#ifdef CHECKSUM
+ {
+ int write_checksum,
+ read_checksum;
+
+ write_checksum = pager_get_checksum(ds, original_offset);
+ read_checksum = compute_checksum(*out_addr, original_size);
+ if (write_checksum != read_checksum) {
+ panic(
+ "PAGER CHECKSUM ERROR: offset 0x%x, written 0x%x, read 0x%x",
+ original_offset, write_checksum, read_checksum);
+ }
+ }
+#endif /* CHECKSUM */
+ return (PAGER_SUCCESS);
+}
+
+int
+default_write(ds, addr, size, offset)
+ register dpager_t ds;
+ register vm_offset_t addr;
+ register vm_size_t size;
+ register vm_offset_t offset;
+{
+ register union dp_map block;
+ partition_t part;
+ vm_size_t wsize;
+ register int rc;
+
+ ddprintf ("default_write: pager offset %x\n", offset);
+
+ /*
+ * Find block in paging partition
+ */
+ block = pager_write_offset(ds, offset);
+ if ( no_block(block) )
+ return (PAGER_ERROR);
+
+#ifdef CHECKSUM
+ /*
+ * Save checksum
+ */
+ {
+ int checksum;
+
+ checksum = compute_checksum(addr, size);
+ pager_put_checksum(ds, offset, checksum);
+ }
+#endif /* CHECKSUM */
+ offset = ptoa(block.block.p_offset);
+ddprintf ("default_write(%x,%x,%x,%d)\n",addr,size,offset,block.block.p_index);
+ part = partition_of(block.block.p_index);
+
+ /*
+ * There are various assumptions made here,we
+ * will not get into the next disk 'block' by
+ * accident. It might well be non-contiguous.
+ */
+ do {
+ rc = page_write_file_direct(part->file,
+ offset,
+ addr,
+ size,
+ &wsize);
+ if (rc != 0) {
+ dprintf("*** PAGER ERROR: default_write: ");
+ dprintf("ds=0x%x addr=0x%x size=0x%x offset=0x%x resid=0x%x\n",
+ ds, addr, size, offset, wsize);
+ return (PAGER_ERROR);
+ }
+ addr += wsize;
+ offset += wsize;
+ size -= wsize;
+ } while (size != 0);
+ return (PAGER_SUCCESS);
+}
+
+boolean_t
+default_has_page(ds, offset)
+ dpager_t ds;
+ vm_offset_t offset;
+{
+ return ( ! no_block(pager_read_offset(ds, offset)) );
+}
+/*
+
+ */
+
+/*
+ * Mapping between pager port and paging object.
+ */
+struct dstruct {
+ queue_chain_t links; /* Link in pager-port list */
+
+ struct mutex lock; /* Lock for the structure */
+ struct condition
+ waiting_seqno, /* someone waiting on seqno */
+ waiting_read, /* someone waiting on readers */
+ waiting_write, /* someone waiting on writers */
+ waiting_refs; /* someone waiting on refs */
+
+ memory_object_t pager; /* Pager port */
+ mach_port_seqno_t seqno; /* Pager port sequence number */
+ mach_port_t pager_request; /* Request port */
+ mach_port_urefs_t request_refs; /* Request port user-refs */
+ mach_port_t pager_name; /* Name port */
+ mach_port_urefs_t name_refs; /* Name port user-refs */
+
+ unsigned int readers; /* Reads in progress */
+ unsigned int writers; /* Writes in progress */
+
+ /* This is the reply port of an outstanding
+ default_pager_object_set_size call. */
+ mach_port_t lock_request;
+
+ unsigned int errors; /* Pageout error count */
+ struct dpager dpager; /* Actual pager */
+};
+typedef struct dstruct * default_pager_t;
+#define DEFAULT_PAGER_NULL ((default_pager_t)0)
+
+#if PARALLEL
+#define dstruct_lock_init(ds) mutex_init(&ds->lock)
+#define dstruct_lock(ds) mutex_lock(&ds->lock)
+#define dstruct_unlock(ds) mutex_unlock(&ds->lock)
+#else /* PARALLEL */
+#define dstruct_lock_init(ds)
+#define dstruct_lock(ds)
+#define dstruct_unlock(ds)
+#endif /* PARALLEL */
+
+/*
+ * List of all pagers. A specific pager is
+ * found directly via its port, this list is
+ * only used for monitoring purposes by the
+ * default_pager_object* calls
+ */
+struct pager_port {
+ queue_head_t queue;
+ struct mutex lock;
+ int count; /* saves code */
+ queue_head_t leak_queue;
+} all_pagers;
+
+#define pager_port_list_init() \
+{ \
+ mutex_init(&all_pagers.lock); \
+ queue_init(&all_pagers.queue); \
+ queue_init(&all_pagers.leak_queue); \
+ all_pagers.count = 0; \
+}
+
+void pager_port_list_insert(port, ds)
+ mach_port_t port;
+ default_pager_t ds;
+{
+ mutex_lock(&all_pagers.lock);
+ queue_enter(&all_pagers.queue, ds, default_pager_t, links);
+ all_pagers.count++;
+ mutex_unlock(&all_pagers.lock);
+}
+
+/* given a data structure return a good port-name to associate it to */
+#define pnameof(_x_) (((vm_offset_t)(_x_))+1)
+/* reverse, assumes no-odd-pointers */
+#define dnameof(_x_) (((vm_offset_t)(_x_))&~1)
+
+/* The magic typecast */
+#define pager_port_lookup(_port_) \
+ ((! MACH_PORT_VALID(_port_) || \
+ ((default_pager_t)dnameof(_port_))->pager != (_port_)) ? \
+ DEFAULT_PAGER_NULL : (default_pager_t)dnameof(_port_))
+
+void pager_port_list_delete(ds)
+ default_pager_t ds;
+{
+ mutex_lock(&all_pagers.lock);
+ queue_remove(&all_pagers.queue, ds, default_pager_t, links);
+ all_pagers.count--;
+ mutex_unlock(&all_pagers.lock);
+}
+
+/*
+ * Destroy a paging partition.
+ * XXX this is not re-entrant XXX
+ */
+kern_return_t
+destroy_paging_partition(name, pp_private)
+ char *name;
+ void **pp_private;
+{
+ register unsigned int id = part_id(name);
+ register partition_t part;
+ boolean_t all_ok = TRUE;
+ default_pager_t entry;
+ int pindex;
+
+ /*
+ * Find and take partition out of list
+ * This prevents choose_partition from
+ * getting in the way.
+ */
+ mutex_lock(&all_partitions.lock);
+ for (pindex = 0; pindex < all_partitions.n_partitions; pindex++) {
+ part = partition_of(pindex);
+ if (part && (part->id == id)) break;
+ }
+ if (pindex == all_partitions.n_partitions) {
+ mutex_unlock(&all_partitions.lock);
+ return KERN_INVALID_ARGUMENT;
+ }
+ part->going_away = TRUE;
+ mutex_unlock(&all_partitions.lock);
+
+ /*
+ * This might take a while..
+ */
+all_over_again:
+#if debug
+dprintf("Partition x%x (id x%x) for %s, all_ok %d\n", part, id, name, all_ok);
+#endif
+ all_ok = TRUE;
+ mutex_lock(&part->p_lock);
+
+ mutex_lock(&all_pagers.lock);
+ queue_iterate(&all_pagers.queue, entry, default_pager_t, links) {
+
+ dstruct_lock(entry);
+
+ if (!mutex_try_lock(&entry->dpager.lock)) {
+
+ dstruct_unlock(entry);
+ mutex_unlock(&all_pagers.lock);
+ mutex_unlock(&part->p_lock);
+
+ /* yield the processor */
+ (void) thread_switch(MACH_PORT_NULL,
+ SWITCH_OPTION_NONE, 0);
+
+ goto all_over_again;
+
+ }
+
+ /*
+ * See if we can relocate all the pages of this object
+ * currently on this partition on some other partition
+ */
+ all_ok = pager_realloc(&entry->dpager, pindex);
+
+ mutex_unlock(&entry->dpager.lock);
+ dstruct_unlock(entry);
+
+ if (!all_ok) break;
+
+ }
+ mutex_unlock(&all_pagers.lock);
+
+ if (all_ok) {
+ /* No need to unlock partition, there are no refs left */
+
+ set_partition_of(pindex, 0);
+ *pp_private = part->file;
+ kfree(part->bitmap, howmany(part->total_size, NB_BM) * sizeof(bm_entry_t));
+ kfree(part, sizeof(struct part));
+ dprintf("%s Removed paging partition %s\n", my_name, name);
+ return KERN_SUCCESS;
+ }
+
+ /*
+ * Put partition back in.
+ */
+ part->going_away = FALSE;
+
+ return KERN_FAILURE;
+}
+
+
+/*
+ * We use the sequence numbers on requests to regulate
+ * our parallelism. In general, we allow multiple reads and writes
+ * to proceed in parallel, with the exception that reads must
+ * wait for previous writes to finish. (Because the kernel might
+ * generate a data-request for a page on the heels of a data-write
+ * for the same page, and we must avoid returning stale data.)
+ * terminate requests wait for proceeding reads and writes to finish.
+ */
+
+unsigned int default_pager_total = 0; /* debugging */
+unsigned int default_pager_wait_seqno = 0; /* debugging */
+unsigned int default_pager_wait_read = 0; /* debugging */
+unsigned int default_pager_wait_write = 0; /* debugging */
+unsigned int default_pager_wait_refs = 0; /* debugging */
+
+#if PARALLEL
+/*
+ * Waits for correct sequence number. Leaves pager locked.
+ */
+void pager_port_lock(ds, seqno)
+ default_pager_t ds;
+ mach_port_seqno_t seqno;
+{
+ default_pager_total++;
+ddprintf ("pager_port_lock <%p>: <%p>: %d: 1\n", &ds, ds, seqno);
+ dstruct_lock(ds);
+ddprintf ("pager_port_lock <%p>: <%p>: %d: 2\n", &ds, ds, seqno);
+ while (ds->seqno != seqno) {
+ddprintf ("pager_port_lock <%p>: <%p>: %d: 3\n", &ds, ds, seqno);
+ default_pager_wait_seqno++;
+ condition_wait(&ds->waiting_seqno, &ds->lock);
+ddprintf ("pager_port_lock <%p>: <%p>: %d: 4\n", &ds, ds, seqno);
+ }
+}
+
+/*
+ * Increments sequence number and unlocks pager.
+ */
+void pager_port_unlock(ds)
+ default_pager_t ds;
+{
+ ds->seqno++;
+ddprintf ("pager_port_unlock <%p>: <%p>: seqno => %d\n", &ds, ds, ds->seqno);
+ dstruct_unlock(ds);
+ddprintf ("pager_port_unlock <%p>: <%p>: 2\n", &ds, ds);
+ condition_broadcast(&ds->waiting_seqno);
+ddprintf ("pager_port_unlock <%p>: <%p>: 3\n", &ds, ds);
+}
+
+/*
+ * Start a read - one more reader. Pager must be locked.
+ */
+void pager_port_start_read(ds)
+ default_pager_t ds;
+{
+ ds->readers++;
+}
+
+/*
+ * Wait for readers. Unlocks and relocks pager if wait needed.
+ */
+void pager_port_wait_for_readers(ds)
+ default_pager_t ds;
+{
+ while (ds->readers != 0) {
+ default_pager_wait_read++;
+ condition_wait(&ds->waiting_read, &ds->lock);
+ }
+}
+
+/*
+ * Finish a read. Pager is unlocked and returns unlocked.
+ */
+void pager_port_finish_read(ds)
+ default_pager_t ds;
+{
+ dstruct_lock(ds);
+ if (--ds->readers == 0) {
+ dstruct_unlock(ds);
+ condition_broadcast(&ds->waiting_read);
+ }
+ else {
+ dstruct_unlock(ds);
+ }
+}
+
+/*
+ * Start a write - one more writer. Pager must be locked.
+ */
+void pager_port_start_write(ds)
+ default_pager_t ds;
+{
+ ds->writers++;
+}
+
+/*
+ * Wait for writers. Unlocks and relocks pager if wait needed.
+ */
+void pager_port_wait_for_writers(ds)
+ default_pager_t ds;
+{
+ while (ds->writers != 0) {
+ default_pager_wait_write++;
+ condition_wait(&ds->waiting_write, &ds->lock);
+ }
+}
+
+/*
+ * Finish a write. Pager is unlocked and returns unlocked.
+ */
+void pager_port_finish_write(ds)
+ default_pager_t ds;
+{
+ dstruct_lock(ds);
+ if (--ds->writers == 0) {
+ dstruct_unlock(ds);
+ condition_broadcast(&ds->waiting_write);
+ }
+ else {
+ dstruct_unlock(ds);
+ }
+}
+
+/*
+ * Wait for concurrent default_pager_objects.
+ * Unlocks and relocks pager if wait needed.
+ */
+void pager_port_wait_for_refs(ds)
+ default_pager_t ds;
+{
+ while (ds->name_refs == 0) {
+ default_pager_wait_refs++;
+ condition_wait(&ds->waiting_refs, &ds->lock);
+ }
+}
+
+/*
+ * Finished creating name refs - wake up waiters.
+ */
+void pager_port_finish_refs(ds)
+ default_pager_t ds;
+{
+ condition_broadcast(&ds->waiting_refs);
+}
+
+#else /* PARALLEL */
+
+#define pager_port_lock(ds,seqno)
+#define pager_port_unlock(ds)
+#define pager_port_start_read(ds)
+#define pager_port_wait_for_readers(ds)
+#define pager_port_finish_read(ds)
+#define pager_port_start_write(ds)
+#define pager_port_wait_for_writers(ds)
+#define pager_port_finish_write(ds)
+#define pager_port_wait_for_refs(ds)
+#define pager_port_finish_refs(ds)
+
+#endif /* PARALLEL */
+
+/*
+ * Default pager.
+ */
+task_t default_pager_self; /* Our task port. */
+
+mach_port_t default_pager_default_port; /* Port for memory_object_create. */
+
+/* We catch exceptions on ourself & startup using this port. */
+mach_port_t default_pager_exception_port;
+
+mach_port_t default_pager_internal_set; /* Port set for internal objects. */
+mach_port_t default_pager_external_set; /* Port set for external objects. */
+mach_port_t default_pager_default_set; /* Port set for "default" thread. */
+
+typedef struct default_pager_thread {
+ cthread_t dpt_thread; /* Server thread. */
+ vm_offset_t dpt_buffer; /* Read buffer. */
+ boolean_t dpt_internal; /* Do we handle internal objects? */
+} default_pager_thread_t;
+
+#if PARALLEL
+ /* determine number of threads at run time */
+#define DEFAULT_PAGER_INTERNAL_COUNT (0)
+
+#else /* PARALLEL */
+#define DEFAULT_PAGER_INTERNAL_COUNT (1)
+#endif /* PARALLEL */
+
+/* Memory created by default_pager_object_create should mostly be resident. */
+#define DEFAULT_PAGER_EXTERNAL_COUNT (1)
+
+unsigned int default_pager_internal_count = DEFAULT_PAGER_INTERNAL_COUNT;
+ /* Number of "internal" threads. */
+unsigned int default_pager_external_count = DEFAULT_PAGER_EXTERNAL_COUNT;
+ /* Number of "external" threads. */
+
+default_pager_t pager_port_alloc(size)
+ vm_size_t size;
+{
+ default_pager_t ds;
+ p_index_t part;
+
+ ds = (default_pager_t) kalloc(sizeof *ds);
+ if (ds == DEFAULT_PAGER_NULL)
+ panic("%spager_port_alloc",my_name);
+ bzero((char *) ds, sizeof *ds);
+
+ dstruct_lock_init(ds);
+
+ /*
+ * Get a suitable partition. If none big enough
+ * just pick one and overcommit. If no partitions
+ * at all.. well just fake one so that we will
+ * kill specific objects on pageouts rather than
+ * panicing the system now.
+ */
+ part = choose_partition(size, P_INDEX_INVALID);
+ if (no_partition(part)) {
+ overcommitted(FALSE, atop(size));
+ part = choose_partition(0,P_INDEX_INVALID);
+#if debug
+ if (no_partition(part))
+ dprintf("%s No paging space at all !!\n", my_name);
+#endif
+ }
+ pager_alloc(&ds->dpager, part, size);
+
+ return ds;
+}
+
+mach_port_urefs_t default_pager_max_urefs = 10000;
+
+/*
+ * Check user reference count on pager_request port.
+ * Pager must be locked.
+ * Unlocks and re-locks pager if needs to call kernel.
+ */
+void pager_port_check_request(ds, pager_request)
+ default_pager_t ds;
+ mach_port_t pager_request;
+{
+ mach_port_delta_t delta;
+ kern_return_t kr;
+
+ assert(ds->pager_request == pager_request);
+
+ if (++ds->request_refs > default_pager_max_urefs) {
+ delta = 1 - ds->request_refs;
+ ds->request_refs = 1;
+
+ dstruct_unlock(ds);
+
+ /*
+ * Deallocate excess user references.
+ */
+
+ kr = mach_port_mod_refs(default_pager_self, pager_request,
+ MACH_PORT_RIGHT_SEND, delta);
+ if (kr != KERN_SUCCESS)
+ panic("%spager_port_check_request",my_name);
+
+ dstruct_lock(ds);
+ }
+}
+
+void default_pager_add(ds, internal)
+ default_pager_t ds;
+ boolean_t internal;
+{
+ mach_port_t pager = ds->pager;
+ mach_port_t pset;
+ mach_port_mscount_t sync;
+ mach_port_t previous;
+ kern_return_t kr;
+ static char here[] = "%sdefault_pager_add";
+
+ /*
+ * The port currently has a make-send count of zero,
+ * because either we just created the port or we just
+ * received the port in a memory_object_create request.
+ */
+
+ if (internal) {
+ /* possibly generate an immediate no-senders notification */
+ sync = 0;
+ pset = default_pager_internal_set;
+ } else {
+ /* delay notification till send right is created */
+ sync = 1;
+ pset = default_pager_external_set;
+ }
+
+ kr = mach_port_request_notification(default_pager_self, pager,
+ MACH_NOTIFY_NO_SENDERS, sync,
+ pager, MACH_MSG_TYPE_MAKE_SEND_ONCE,
+ &previous);
+ if ((kr != KERN_SUCCESS) || (previous != MACH_PORT_NULL))
+ panic(here,my_name);
+
+ kr = mach_port_move_member(default_pager_self, pager, pset);
+ if (kr != KERN_SUCCESS)
+ panic(here,my_name);
+}
+
+/*
+ * Routine: memory_object_create
+ * Purpose:
+ * Handle requests for memory objects from the
+ * kernel.
+ * Notes:
+ * Because we only give out the default memory
+ * manager port to the kernel, we don't have to
+ * be so paranoid about the contents.
+ */
+kern_return_t
+seqnos_memory_object_create(old_pager, seqno, new_pager, new_size,
+ new_pager_request, new_pager_name, new_page_size)
+ mach_port_t old_pager;
+ mach_port_seqno_t seqno;
+ mach_port_t new_pager;
+ vm_size_t new_size;
+ mach_port_t new_pager_request;
+ mach_port_t new_pager_name;
+ vm_size_t new_page_size;
+{
+ register default_pager_t ds;
+ kern_return_t kr;
+
+ assert(old_pager == default_pager_default_port);
+ assert(MACH_PORT_VALID(new_pager_request));
+ assert(MACH_PORT_VALID(new_pager_name));
+ assert(new_page_size == vm_page_size);
+
+ ds = pager_port_alloc(new_size);
+rename_it:
+ kr = mach_port_rename( default_pager_self,
+ new_pager, (mach_port_t)pnameof(ds));
+ if (kr != KERN_SUCCESS) {
+ default_pager_t ds1;
+
+ if (kr != KERN_NAME_EXISTS)
+ panic("%s m_o_create", my_name);
+ ds1 = (default_pager_t) kalloc(sizeof *ds1);
+ *ds1 = *ds;
+ mutex_lock(&all_pagers.lock);
+ queue_enter(&all_pagers.leak_queue, ds, default_pager_t, links);
+ mutex_unlock(&all_pagers.lock);
+ ds = ds1;
+ goto rename_it;
+ }
+
+ new_pager = (mach_port_t) pnameof(ds);
+
+ /*
+ * Set up associations between these ports
+ * and this default_pager structure
+ */
+
+ ds->pager = new_pager;
+ ds->pager_request = new_pager_request;
+ ds->request_refs = 1;
+ ds->pager_name = new_pager_name;
+ ds->name_refs = 1;
+
+ /*
+ * After this, other threads might receive requests
+ * for this memory object or find it in the port list.
+ */
+
+ pager_port_list_insert(new_pager, ds);
+ default_pager_add(ds, TRUE);
+
+ return(KERN_SUCCESS);
+}
+
+memory_object_copy_strategy_t default_pager_copy_strategy =
+ MEMORY_OBJECT_COPY_DELAY;
+
+kern_return_t
+seqnos_memory_object_init(pager, seqno, pager_request, pager_name,
+ pager_page_size)
+ mach_port_t pager;
+ mach_port_seqno_t seqno;
+ mach_port_t pager_request;
+ mach_port_t pager_name;
+ vm_size_t pager_page_size;
+{
+ register default_pager_t ds;
+ kern_return_t kr;
+ static char here[] = "%sinit";
+
+ assert(MACH_PORT_VALID(pager_request));
+ assert(MACH_PORT_VALID(pager_name));
+ assert(pager_page_size == vm_page_size);
+
+ ds = pager_port_lookup(pager);
+ if (ds == DEFAULT_PAGER_NULL)
+ panic(here, my_name);
+ pager_port_lock(ds, seqno);
+
+ if (ds->pager_request != MACH_PORT_NULL)
+ panic(here, my_name);
+
+ ds->pager_request = pager_request;
+ ds->request_refs = 1;
+ ds->pager_name = pager_name;
+ ds->name_refs = 1;
+
+ /*
+ * Even if the kernel immediately terminates the object,
+ * the pager_request port won't be destroyed until
+ * we process the terminate request, which won't happen
+ * until we unlock the object.
+ */
+
+ kr = memory_object_set_attributes(pager_request,
+ TRUE,
+ FALSE, /* do not cache */
+ default_pager_copy_strategy);
+ if (kr != KERN_SUCCESS)
+ panic(here, my_name);
+
+ pager_port_unlock(ds);
+
+ return(KERN_SUCCESS);
+}
+
+kern_return_t
+seqnos_memory_object_terminate(pager, seqno, pager_request, pager_name)
+ mach_port_t pager;
+ mach_port_seqno_t seqno;
+ mach_port_t pager_request;
+ mach_port_t pager_name;
+{
+ register default_pager_t ds;
+ mach_port_urefs_t request_refs, name_refs;
+ kern_return_t kr;
+ static char here[] = "%sterminate";
+
+ /*
+ * pager_request and pager_name are receive rights,
+ * not send rights.
+ */
+
+ ds = pager_port_lookup(pager);
+ if (ds == DEFAULT_PAGER_NULL)
+ panic(here, my_name);
+ddprintf ("seqnos_memory_object_terminate <%p>: pager_port_lock: <%p>[s:%d,r:%d,w:%d,l:%d], %d\n",
+ &kr, ds, ds->seqno, ds->readers, ds->writers, ds->lock.held, seqno);
+ pager_port_lock(ds, seqno);
+
+ /*
+ * Wait for read and write requests to terminate.
+ */
+
+ pager_port_wait_for_readers(ds);
+ pager_port_wait_for_writers(ds);
+
+ /*
+ * After memory_object_terminate both memory_object_init
+ * and a no-senders notification are possible, so we need
+ * to clean up the request and name ports but leave
+ * the pager port.
+ *
+ * A concurrent default_pager_objects might be allocating
+ * more references for the name port. In this case,
+ * we must first wait for it to finish.
+ */
+
+ pager_port_wait_for_refs(ds);
+
+ ds->pager_request = MACH_PORT_NULL;
+ request_refs = ds->request_refs;
+ ds->request_refs = 0;
+ assert(ds->pager_name == pager_name);
+ ds->pager_name = MACH_PORT_NULL;
+ name_refs = ds->name_refs;
+ ds->name_refs = 0;
+ddprintf ("seqnos_memory_object_terminate <%p>: pager_port_unlock: <%p>[s:%d,r:%d,w:%d,l:%d]\n",
+ &kr, ds, ds->seqno, ds->readers, ds->writers, ds->lock.held);
+ pager_port_unlock(ds);
+
+ /*
+ * Now we deallocate our various port rights.
+ */
+
+ kr = mach_port_mod_refs(default_pager_self, pager_request,
+ MACH_PORT_RIGHT_SEND, -request_refs);
+ if (kr != KERN_SUCCESS)
+ panic(here,my_name);
+
+ kr = mach_port_mod_refs(default_pager_self, pager_request,
+ MACH_PORT_RIGHT_RECEIVE, -1);
+ if (kr != KERN_SUCCESS)
+ panic(here,my_name);
+
+ kr = mach_port_mod_refs(default_pager_self, pager_name,
+ MACH_PORT_RIGHT_SEND, -name_refs);
+ if (kr != KERN_SUCCESS)
+ panic(here,my_name);
+
+ kr = mach_port_mod_refs(default_pager_self, pager_name,
+ MACH_PORT_RIGHT_RECEIVE, -1);
+ if (kr != KERN_SUCCESS)
+ panic(here,my_name);
+
+ return (KERN_SUCCESS);
+}
+
+void default_pager_no_senders(pager, seqno, mscount)
+ memory_object_t pager;
+ mach_port_seqno_t seqno;
+ mach_port_mscount_t mscount;
+{
+ register default_pager_t ds;
+ kern_return_t kr;
+ static char here[] = "%sno_senders";
+
+ /*
+ * Because we don't give out multiple send rights
+ * for a memory object, there can't be a race
+ * between getting a no-senders notification
+ * and creating a new send right for the object.
+ * Hence we don't keep track of mscount.
+ */
+
+
+ ds = pager_port_lookup(pager);
+ if (ds == DEFAULT_PAGER_NULL)
+ panic(here,my_name);
+ pager_port_lock(ds, seqno);
+
+ /*
+ * We shouldn't get a no-senders notification
+ * when the kernel has the object cached.
+ */
+
+ if (ds->pager_request != MACH_PORT_NULL)
+ panic(here,my_name);
+
+ /*
+ * Unlock the pager (though there should be no one
+ * waiting for it).
+ */
+ dstruct_unlock(ds);
+
+ /*
+ * Remove the memory object port association, and then
+ * the destroy the port itself. We must remove the object
+ * from the port list before deallocating the pager,
+ * because of default_pager_objects.
+ */
+
+ pager_port_list_delete(ds);
+ pager_dealloc(&ds->dpager);
+
+ kr = mach_port_mod_refs(default_pager_self, pager,
+ MACH_PORT_RIGHT_RECEIVE, -1);
+ if (kr != KERN_SUCCESS)
+ panic(here,my_name);
+
+ /*
+ * Do this *after* deallocating the port name
+ */
+ kfree((char *) ds, sizeof(*ds));
+
+ /*
+ * Recover memory that we might have wasted because
+ * of name conflicts
+ */
+ mutex_lock(&all_pagers.lock);
+
+ while (!queue_empty(&all_pagers.leak_queue)) {
+
+ ds = (default_pager_t) queue_first(&all_pagers.leak_queue);
+ queue_remove_first(&all_pagers.leak_queue, ds, default_pager_t, links);
+ kfree((char *) ds, sizeof(*ds));
+ }
+
+ mutex_unlock(&all_pagers.lock);
+}
+
+int default_pager_pagein_count = 0;
+int default_pager_pageout_count = 0;
+
+kern_return_t
+seqnos_memory_object_data_request(pager, seqno, reply_to, offset,
+ length, protection_required)
+ memory_object_t pager;
+ mach_port_seqno_t seqno;
+ mach_port_t reply_to;
+ vm_offset_t offset;
+ vm_size_t length;
+ vm_prot_t protection_required;
+{
+ default_pager_thread_t *dpt;
+ default_pager_t ds;
+ vm_offset_t addr;
+ unsigned int errors;
+ kern_return_t rc;
+ static char here[] = "%sdata_request";
+
+ dpt = (default_pager_thread_t *) cthread_data(cthread_self());
+
+ if (length != vm_page_size)
+ panic(here,my_name);
+
+ ds = pager_port_lookup(pager);
+ if (ds == DEFAULT_PAGER_NULL)
+ panic(here,my_name);
+ddprintf ("seqnos_memory_object_data_request <%p>: pager_port_lock: <%p>[s:%d,r:%d,w:%d,l:%d], %d\n",
+ &ds, ds, ds->seqno, ds->readers, ds->writers, ds->lock.held, seqno);
+ pager_port_lock(ds, seqno);
+ pager_port_check_request(ds, reply_to);
+ pager_port_wait_for_writers(ds);
+ pager_port_start_read(ds);
+
+ /*
+ * Get error count while pager locked.
+ */
+ errors = ds->errors;
+
+ddprintf ("seqnos_memory_object_data_request <%p>: pager_port_unlock: <%p>[s:%d,r:%d,w:%d,l:%d]\n",
+ &ds, ds, ds->seqno, ds->readers, ds->writers, ds->lock.held);
+ pager_port_unlock(ds);
+
+ if (errors) {
+ dprintf("%s %s\n", my_name,
+ "dropping data_request because of previous paging errors");
+ (void) memory_object_data_error(reply_to,
+ offset, vm_page_size,
+ KERN_FAILURE);
+ goto done;
+ }
+
+ if (offset >= ds->dpager.limit)
+ rc = PAGER_ERROR;
+ else
+ rc = default_read(&ds->dpager, dpt->dpt_buffer,
+ vm_page_size, offset,
+ &addr, protection_required & VM_PROT_WRITE);
+
+ switch (rc) {
+ case PAGER_SUCCESS:
+ if (addr != dpt->dpt_buffer) {
+ /*
+ * Deallocates data buffer
+ */
+ (void) memory_object_data_supply(
+ reply_to, offset,
+ addr, vm_page_size, TRUE,
+ VM_PROT_NONE,
+ FALSE, MACH_PORT_NULL);
+ } else {
+ (void) memory_object_data_provided(
+ reply_to, offset,
+ addr, vm_page_size,
+ VM_PROT_NONE);
+ }
+ break;
+
+ case PAGER_ABSENT:
+ (void) memory_object_data_unavailable(
+ reply_to,
+ offset,
+ vm_page_size);
+ break;
+
+ case PAGER_ERROR:
+ (void) memory_object_data_error(
+ reply_to,
+ offset,
+ vm_page_size,
+ KERN_FAILURE);
+ break;
+ }
+
+ default_pager_pagein_count++;
+
+ done:
+ pager_port_finish_read(ds);
+ return(KERN_SUCCESS);
+}
+
+/*
+ * memory_object_data_initialize: check whether we already have each page, and
+ * write it if we do not. The implementation is far from optimized, and
+ * also assumes that the default_pager is single-threaded.
+ */
+kern_return_t
+seqnos_memory_object_data_initialize(pager, seqno, pager_request,
+ offset, addr, data_cnt)
+ memory_object_t pager;
+ mach_port_seqno_t seqno;
+ mach_port_t pager_request;
+ register
+ vm_offset_t offset;
+ register
+ pointer_t addr;
+ vm_size_t data_cnt;
+{
+ vm_offset_t amount_sent;
+ default_pager_t ds;
+ static char here[] = "%sdata_initialize";
+
+#ifdef lint
+ pager_request++;
+#endif /* lint */
+
+ ds = pager_port_lookup(pager);
+ if (ds == DEFAULT_PAGER_NULL)
+ panic(here,my_name);
+ddprintf ("seqnos_memory_object_data_initialize <%p>: pager_port_lock: <%p>[s:%d,r:%d,w:%d,l:%d], %d\n",
+ &ds, ds, ds->seqno, ds->readers, ds->writers, ds->lock.held, seqno);
+ pager_port_lock(ds, seqno);
+ pager_port_check_request(ds, pager_request);
+ pager_port_start_write(ds);
+ddprintf ("seqnos_memory_object_data_initialize <%p>: pager_port_unlock: <%p>[s:%d,r:%d,w:%d,l:%d]\n",
+ &ds, ds, ds->seqno, ds->readers, ds->writers, ds->lock.held);
+ pager_port_unlock(ds);
+
+ for (amount_sent = 0;
+ amount_sent < data_cnt;
+ amount_sent += vm_page_size) {
+
+ if (!default_has_page(&ds->dpager, offset + amount_sent)) {
+ if (default_write(&ds->dpager,
+ addr + amount_sent,
+ vm_page_size,
+ offset + amount_sent)
+ != PAGER_SUCCESS) {
+ dprintf("%s%s write error\n", my_name, here);
+ dstruct_lock(ds);
+ ds->errors++;
+ dstruct_unlock(ds);
+ }
+ }
+ }
+
+ pager_port_finish_write(ds);
+ if (vm_deallocate(default_pager_self, addr, data_cnt) != KERN_SUCCESS)
+ panic(here,my_name);
+
+ return(KERN_SUCCESS);
+}
+
+/*
+ * memory_object_data_write: split up the stuff coming in from
+ * a memory_object_data_write call
+ * into individual pages and pass them off to default_write.
+ */
+kern_return_t
+seqnos_memory_object_data_write(pager, seqno, pager_request,
+ offset, addr, data_cnt)
+ memory_object_t pager;
+ mach_port_seqno_t seqno;
+ mach_port_t pager_request;
+ register
+ vm_offset_t offset;
+ register
+ pointer_t addr;
+ vm_size_t data_cnt;
+{
+ register
+ vm_size_t amount_sent;
+ default_pager_t ds;
+ static char here[] = "%sdata_write";
+ int err;
+
+#ifdef lint
+ pager_request++;
+#endif /* lint */
+
+ddprintf ("seqnos_memory_object_data_write <%p>: 1\n", &err);
+ if ((data_cnt % vm_page_size) != 0)
+ {
+ ddprintf ("fail 1: %d %d\n", data_cnt, vm_page_size);
+ panic(here,my_name);
+ }
+
+
+ddprintf ("seqnos_memory_object_data_write <%p>: 2\n", &err);
+ ds = pager_port_lookup(pager);
+ddprintf ("seqnos_memory_object_data_write <%p>: 3\n", &err);
+ if (ds == DEFAULT_PAGER_NULL)
+ {
+ ddprintf ("fail 2: %d %d\n", pager, ds);
+ panic(here,my_name);
+ }
+
+ddprintf ("seqnos_memory_object_data_write <%p>: 4\n", &err);
+ddprintf ("seqnos_memory_object_data_write <%p>: pager_port_lock: <%p>[s:%d,r:%d,w:%d,l:%d], %d\n",
+ &err, ds, ds->seqno, ds->readers, ds->writers, ds->lock.held, seqno);
+ pager_port_lock(ds, seqno);
+ddprintf ("seqnos_memory_object_data_write <%p>: 5\n", &err);
+ pager_port_check_request(ds, pager_request);
+ddprintf ("seqnos_memory_object_data_write <%p>: 6\n", &err);
+ pager_port_start_write(ds);
+ddprintf ("seqnos_memory_object_data_write <%p>: 7\n", &err);
+ddprintf ("seqnos_memory_object_data_write <%p>: pager_port_unlock: <%p>[s:%d,r:%d,w:%d,l:%d]\n",
+ &err, ds, ds->seqno, ds->readers, ds->writers, ds->lock.held);
+ pager_port_unlock(ds);
+
+ddprintf ("seqnos_memory_object_data_write <%p>: 8\n", &err);
+ for (amount_sent = 0;
+ amount_sent < data_cnt;
+ amount_sent += vm_page_size) {
+
+ register int result;
+
+ddprintf ("seqnos_memory_object_data_write <%p>: 9\n", &err);
+ result = default_write(&ds->dpager,
+ addr + amount_sent,
+ vm_page_size,
+ offset + amount_sent);
+ddprintf ("seqnos_memory_object_data_write <%p>: 10\n", &err);
+ if (result != KERN_SUCCESS) {
+ddprintf ("seqnos_memory_object_data_write <%p>: 11\n", &err);
+#if debug
+ dprintf("%s WRITE ERROR on default_pageout:", my_name);
+ dprintf(" pager=%x, offset=0x%x, length=0x%x, result=%d\n",
+ pager, offset+amount_sent, vm_page_size, result);
+#endif
+ dstruct_lock(ds);
+ ds->errors++;
+ dstruct_unlock(ds);
+ }
+ default_pager_pageout_count++;
+ }
+
+ddprintf ("seqnos_memory_object_data_write <%p>: 12\n", &err);
+ pager_port_finish_write(ds);
+ddprintf ("seqnos_memory_object_data_write <%p>: 13\n", &err);
+ err = vm_deallocate(default_pager_self, addr, data_cnt);
+ddprintf ("seqnos_memory_object_data_write <%p>: 14\n", &err);
+ if (err != KERN_SUCCESS)
+ {
+ ddprintf ("fail 3: %s %s %s %s\n", default_pager_self, addr, data_cnt, &err);
+
+ panic(here,my_name);
+ }
+
+
+ddprintf ("seqnos_memory_object_data_write <%p>: 15\n", &err);
+ return(KERN_SUCCESS);
+}
+
+/*ARGSUSED*/
+kern_return_t
+seqnos_memory_object_copy(old_memory_object, seqno, old_memory_control,
+ offset, length, new_memory_object)
+ memory_object_t old_memory_object;
+ mach_port_seqno_t seqno;
+ memory_object_control_t
+ old_memory_control;
+ vm_offset_t offset;
+ vm_size_t length;
+ memory_object_t new_memory_object;
+{
+ panic("%scopy", my_name);
+ return KERN_FAILURE;
+}
+
+/* We get this when our memory_object_lock_request has completed
+ after we truncated an object. */
+kern_return_t
+seqnos_memory_object_lock_completed (memory_object_t pager,
+ mach_port_seqno_t seqno,
+ mach_port_t pager_request,
+ vm_offset_t offset,
+ vm_size_t length)
+{
+ default_pager_t ds;
+
+ ds = pager_port_lookup(pager);
+ assert(ds != DEFAULT_PAGER_NULL);
+
+ pager_port_lock(ds, seqno);
+ pager_port_wait_for_readers(ds);
+ pager_port_wait_for_writers(ds);
+
+ /* Now that any in-core pages have been flushed, we can apply
+ the limit to prevent any new page-ins. */
+ assert (page_aligned (offset));
+ ds->dpager.limit = offset;
+
+ default_pager_object_set_size_reply (ds->lock_request, KERN_SUCCESS);
+ ds->lock_request = MACH_PORT_NULL;
+
+ if (ds->dpager.size > ds->dpager.limit / vm_page_size)
+ /* Deallocate the old backing store pages and shrink the page map. */
+ pager_truncate (&ds->dpager, ds->dpager.limit / vm_page_size);
+
+ pager_port_unlock(ds, seqno);
+
+ return KERN_SUCCESS;
+}
+
+kern_return_t
+seqnos_memory_object_data_unlock(pager, seqno, pager_request,
+ offset, addr, data_cnt)
+ memory_object_t pager;
+ mach_port_seqno_t seqno;
+ mach_port_t pager_request;
+ vm_offset_t offset;
+ pointer_t addr;
+ vm_size_t data_cnt;
+{
+ panic("%sdata_unlock",my_name);
+ return(KERN_FAILURE);
+}
+
+kern_return_t
+seqnos_memory_object_supply_completed(pager, seqno, pager_request,
+ offset, length,
+ result, error_offset)
+ memory_object_t pager;
+ mach_port_seqno_t seqno;
+ mach_port_t pager_request;
+ vm_offset_t offset;
+ vm_size_t length;
+ kern_return_t result;
+ vm_offset_t error_offset;
+{
+ panic("%ssupply_completed",my_name);
+ return(KERN_FAILURE);
+}
+
+kern_return_t
+seqnos_memory_object_data_return(pager, seqno, pager_request,
+ offset, addr, data_cnt,
+ dirty, kernel_copy)
+ memory_object_t pager;
+ mach_port_seqno_t seqno;
+ mach_port_t pager_request;
+ vm_offset_t offset;
+ pointer_t addr;
+ vm_size_t data_cnt;
+ boolean_t dirty;
+ boolean_t kernel_copy;
+{
+ panic("%sdata_return",my_name);
+ return(KERN_FAILURE);
+}
+
+kern_return_t
+seqnos_memory_object_change_completed(pager, seqno, may_cache, copy_strategy)
+ memory_object_t pager;
+ mach_port_seqno_t seqno;
+ boolean_t may_cache;
+ memory_object_copy_strategy_t copy_strategy;
+{
+ panic("%schange_completed",my_name);
+ return(KERN_FAILURE);
+}
+
+
+boolean_t default_pager_notify_server(in, out)
+ mach_msg_header_t *in, *out;
+{
+ register mach_no_senders_notification_t *n =
+ (mach_no_senders_notification_t *) in;
+
+ /*
+ * The only send-once rights we create are for
+ * receiving no-more-senders notifications.
+ * Hence, if we receive a message directed to
+ * a send-once right, we can assume it is
+ * a genuine no-senders notification from the kernel.
+ */
+
+ if ((n->not_header.msgh_bits !=
+ MACH_MSGH_BITS(0, MACH_MSG_TYPE_PORT_SEND_ONCE)) ||
+ (n->not_header.msgh_id != MACH_NOTIFY_NO_SENDERS))
+ return FALSE;
+
+ assert(n->not_header.msgh_size == sizeof *n);
+ assert(n->not_header.msgh_remote_port == MACH_PORT_NULL);
+
+ assert(n->not_type.msgt_name == MACH_MSG_TYPE_INTEGER_32);
+ assert(n->not_type.msgt_size == 32);
+ assert(n->not_type.msgt_number == 1);
+ assert(n->not_type.msgt_inline);
+ assert(! n->not_type.msgt_longform);
+
+ default_pager_no_senders(n->not_header.msgh_local_port,
+ n->not_header.msgh_seqno, n->not_count);
+
+ out->msgh_remote_port = MACH_PORT_NULL;
+ return TRUE;
+}
+
+extern boolean_t seqnos_memory_object_server();
+extern boolean_t seqnos_memory_object_default_server();
+extern boolean_t default_pager_server();
+extern boolean_t exc_server();
+extern boolean_t bootstrap_server();
+extern void bootstrap_compat();
+
+mach_msg_size_t default_pager_msg_size_object = 128;
+
+boolean_t
+default_pager_demux_object(in, out)
+ mach_msg_header_t *in;
+ mach_msg_header_t *out;
+{
+ /*
+ * We receive memory_object_data_initialize messages in
+ * the memory_object_default interface.
+ */
+
+int rval;
+ddprintf ("DPAGER DEMUX OBJECT <%p>: %d\n", in, in->msgh_id);
+rval =
+ (seqnos_memory_object_server(in, out) ||
+ seqnos_memory_object_default_server(in, out) ||
+ default_pager_notify_server(in, out) ||
+ default_pager_server(in, out));
+ddprintf ("DPAGER DEMUX OBJECT DONE <%p>: %d\n", in, in->msgh_id);
+return rval;
+}
+
+mach_msg_size_t default_pager_msg_size_default = 8 * 1024;
+
+boolean_t
+default_pager_demux_default(in, out)
+ mach_msg_header_t *in;
+ mach_msg_header_t *out;
+{
+ if (in->msgh_local_port == default_pager_default_port) {
+ /*
+ * We receive memory_object_create messages in
+ * the memory_object_default interface.
+ */
+
+int rval;
+ddprintf ("DPAGER DEMUX DEFAULT <%p>: %d\n", in, in->msgh_id);
+rval =
+ (seqnos_memory_object_default_server(in, out) ||
+ default_pager_server(in, out));
+ddprintf ("DPAGER DEMUX DEFAULT DONE <%p>: %d\n", in, in->msgh_id);
+return rval;
+ } else if (in->msgh_local_port == default_pager_exception_port) {
+ /*
+ * We receive exception messages for
+ * ourself and the startup task.
+ */
+
+ return exc_server(in, out);
+ } else {
+ panic(my_name);
+ return FALSE;
+ }
+}
+
+/*
+ * We use multiple threads, for two reasons.
+ *
+ * First, memory objects created by default_pager_object_create
+ * are "external", instead of "internal". This means the kernel
+ * sends data (memory_object_data_write) to the object pageable.
+ * To prevent deadlocks, the external and internal objects must
+ * be managed by different threads.
+ *
+ * Second, the default pager uses synchronous IO operations.
+ * Spreading requests across multiple threads should
+ * recover some of the performance loss from synchronous IO.
+ *
+ * We have 3+ threads.
+ * One receives memory_object_create and
+ * default_pager_object_create requests.
+ * One or more manage internal objects.
+ * One or more manage external objects.
+ */
+
+void
+default_pager_thread_privileges()
+{
+ /*
+ * Set thread privileges.
+ */
+ cthread_wire(); /* attach kernel thread to cthread */
+ wire_thread(); /* grab a kernel stack and memory allocation
+ privileges */
+}
+
+any_t
+default_pager_default_thread (arg)
+ any_t arg;
+{
+ kern_return_t kr;
+ default_pager_thread_privileges ();
+ for (;;) {
+ kr = mach_msg_server(default_pager_demux_default,
+ default_pager_msg_size_default,
+ default_pager_default_set);
+ panic(my_name, kr);
+ }
+}
+
+
+
+any_t
+default_pager_thread(arg)
+ any_t arg;
+{
+ default_pager_thread_t *dpt = (default_pager_thread_t *) arg;
+ mach_port_t pset;
+ kern_return_t kr;
+
+ cthread_set_data(cthread_self(), (any_t) dpt);
+
+
+ /*
+ * Threads handling external objects cannot have
+ * privileges. Otherwise a burst of data-requests for an
+ * external object could empty the free-page queue,
+ * because the fault code only reserves real pages for
+ * requests sent to internal objects.
+ */
+
+ if (dpt->dpt_internal) {
+ default_pager_thread_privileges();
+ pset = default_pager_internal_set;
+ } else {
+ pset = default_pager_external_set;
+ }
+
+ for (;;) {
+ kr = mach_msg_server(default_pager_demux_object,
+ default_pager_msg_size_object,
+ pset);
+ panic(my_name, kr);
+ }
+}
+
+void
+start_default_pager_thread(internal)
+ boolean_t internal;
+{
+ default_pager_thread_t *dpt;
+ kern_return_t kr;
+
+ dpt = (default_pager_thread_t *) kalloc(sizeof *dpt);
+ if (dpt == 0)
+ panic(my_name);
+
+ dpt->dpt_internal = internal;
+
+ kr = vm_allocate(default_pager_self, &dpt->dpt_buffer,
+ vm_page_size, TRUE);
+ if (kr != KERN_SUCCESS)
+ panic(my_name);
+ wire_memory(dpt->dpt_buffer, vm_page_size,
+ VM_PROT_READ|VM_PROT_WRITE);
+
+ dpt->dpt_thread = cthread_fork(default_pager_thread, (any_t) dpt);
+}
+
+void
+default_pager_initialize(host_port)
+ mach_port_t host_port;
+{
+ memory_object_t DMM;
+ kern_return_t kr;
+
+ /*
+ * This task will become the default pager.
+ */
+ default_pager_self = mach_task_self();
+
+ /*
+ * Initialize the "default pager" port.
+ */
+ kr = mach_port_allocate(default_pager_self, MACH_PORT_RIGHT_RECEIVE,
+ &default_pager_default_port);
+ if (kr != KERN_SUCCESS)
+ panic(my_name);
+
+ DMM = default_pager_default_port;
+ kr = vm_set_default_memory_manager(host_port, &DMM);
+ if ((kr != KERN_SUCCESS) || MACH_PORT_VALID(DMM))
+ panic(my_name);
+
+ /*
+ * Initialize the exception port.
+ */
+ kr = mach_port_allocate(default_pager_self, MACH_PORT_RIGHT_RECEIVE,
+ &default_pager_exception_port);
+ if (kr != KERN_SUCCESS)
+ panic(my_name);
+
+ /*
+ * Arrange for wiring privileges.
+ */
+ wire_setup(host_port);
+
+ /*
+ * Find out how many CPUs we have, to determine the number
+ * of threads to create.
+ */
+ if (default_pager_internal_count == 0) {
+ host_basic_info_data_t h_info;
+ natural_t h_info_count;
+
+ h_info_count = HOST_BASIC_INFO_COUNT;
+ (void) host_info(host_port, HOST_BASIC_INFO,
+ (host_info_t)&h_info, &h_info_count);
+
+ /*
+ * Random computation to get more parallelism on
+ * multiprocessors.
+ */
+ default_pager_internal_count =
+ (h_info.avail_cpus > 32 ? 32 : h_info.avail_cpus) / 4 + 3;
+ }
+}
+
+/*
+ * Initialize and Run the default pager
+ */
+void
+default_pager()
+{
+ kern_return_t kr;
+ int i;
+
+ default_pager_thread_privileges();
+
+ /*
+ * Wire down code, data, stack
+ */
+ wire_all_memory();
+
+
+ /*
+ * Initialize the list of all pagers.
+ */
+ pager_port_list_init();
+
+ kr = mach_port_allocate(default_pager_self, MACH_PORT_RIGHT_PORT_SET,
+ &default_pager_internal_set);
+ if (kr != KERN_SUCCESS)
+ panic(my_name);
+
+ kr = mach_port_allocate(default_pager_self, MACH_PORT_RIGHT_PORT_SET,
+ &default_pager_external_set);
+ if (kr != KERN_SUCCESS)
+ panic(my_name);
+
+ kr = mach_port_allocate(default_pager_self, MACH_PORT_RIGHT_PORT_SET,
+ &default_pager_default_set);
+ if (kr != KERN_SUCCESS)
+ panic(my_name);
+
+ kr = mach_port_move_member(default_pager_self,
+ default_pager_default_port,
+ default_pager_default_set);
+ if (kr != KERN_SUCCESS)
+ panic(my_name);
+
+ kr = mach_port_move_member(default_pager_self,
+ default_pager_exception_port,
+ default_pager_default_set);
+ if (kr != KERN_SUCCESS)
+ panic(my_name);
+
+ /*
+ * Now we create the threads that will actually
+ * manage objects.
+ */
+
+ for (i = 0; i < default_pager_internal_count; i++)
+ start_default_pager_thread(TRUE);
+
+ for (i = 0; i < default_pager_external_count; i++)
+ start_default_pager_thread(FALSE);
+
+ default_pager_default_thread(0); /* Become the default_pager server */
+#if 0
+ cthread_fork (default_pager_default_thread, 0);
+ /* cthread_exit (cthread_self ()); */
+ thread_suspend (mach_thread_self ());
+#endif
+}
+
+/*
+ * Create an external object.
+ */
+kern_return_t
+S_default_pager_object_create (mach_port_t pager,
+ mach_port_t *mem_obj,
+ vm_size_t size)
+{
+ default_pager_t ds;
+ mach_port_t port;
+ kern_return_t result;
+
+ if (pager != default_pager_default_port)
+ return KERN_INVALID_ARGUMENT;
+
+ ds = pager_port_alloc(size);
+rename_it:
+ port = (mach_port_t) pnameof(ds);
+ result = mach_port_allocate_name(default_pager_self,
+ MACH_PORT_RIGHT_RECEIVE, port);
+ if (result != KERN_SUCCESS) {
+ default_pager_t ds1;
+
+ if (result != KERN_NAME_EXISTS) return (result);
+
+ ds1 = (default_pager_t) kalloc(sizeof *ds1);
+ *ds1 = *ds;
+ mutex_lock(&all_pagers.lock);
+ queue_enter(&all_pagers.leak_queue, ds, default_pager_t, links);
+ mutex_unlock(&all_pagers.lock);
+ ds = ds1;
+ goto rename_it;
+ }
+
+ /*
+ * Set up associations between these ports
+ * and this default_pager structure
+ */
+
+ ds->pager = port;
+ pager_port_list_insert(port, ds);
+ default_pager_add(ds, FALSE);
+
+ *mem_obj = port;
+ return (KERN_SUCCESS);
+}
+
+kern_return_t
+S_default_pager_info (mach_port_t pager,
+ default_pager_info_t *infop)
+{
+ vm_size_t total, free;
+
+ if (pager != default_pager_default_port)
+ return KERN_INVALID_ARGUMENT;
+
+ mutex_lock(&all_partitions.lock);
+ paging_space_info(&total, &free);
+ mutex_unlock(&all_partitions.lock);
+
+ infop->dpi_total_space = ptoa(total);
+ infop->dpi_free_space = ptoa(free);
+ infop->dpi_page_size = vm_page_size;
+ return KERN_SUCCESS;
+}
+
+kern_return_t
+S_default_pager_objects (mach_port_t pager,
+ default_pager_object_array_t *objectsp,
+ natural_t *ocountp,
+ mach_port_array_t *portsp,
+ natural_t *pcountp)
+{
+ vm_offset_t oaddr; /* memory for objects */
+ vm_size_t osize; /* current size */
+ default_pager_object_t *objects;
+ natural_t opotential;
+
+ vm_offset_t paddr; /* memory for ports */
+ vm_size_t psize; /* current size */
+ mach_port_t *ports;
+ natural_t ppotential;
+
+ unsigned int actual;
+ unsigned int num_pagers;
+ kern_return_t kr;
+ default_pager_t entry;
+
+ if (pager != default_pager_default_port)
+ return KERN_INVALID_ARGUMENT;
+
+ /* start with the inline memory */
+
+ num_pagers = 0;
+
+ objects = *objectsp;
+ opotential = *ocountp;
+
+ ports = *portsp;
+ ppotential = *pcountp;
+
+ mutex_lock(&all_pagers.lock);
+ /*
+ * We will send no more than this many
+ */
+ actual = all_pagers.count;
+ mutex_unlock(&all_pagers.lock);
+
+ if (opotential < actual) {
+ vm_offset_t newaddr;
+ vm_size_t newsize;
+
+ newsize = 2 * round_page(actual * sizeof *objects);
+
+ kr = vm_allocate(default_pager_self, &newaddr, newsize, TRUE);
+ if (kr != KERN_SUCCESS)
+ goto nomemory;
+
+ oaddr = newaddr;
+ osize = newsize;
+ opotential = osize/sizeof *objects;
+ objects = (default_pager_object_t *) oaddr;
+ }
+
+ if (ppotential < actual) {
+ vm_offset_t newaddr;
+ vm_size_t newsize;
+
+ newsize = 2 * round_page(actual * sizeof *ports);
+
+ kr = vm_allocate(default_pager_self, &newaddr, newsize, TRUE);
+ if (kr != KERN_SUCCESS)
+ goto nomemory;
+
+ paddr = newaddr;
+ psize = newsize;
+ ppotential = psize/sizeof *ports;
+ ports = (mach_port_t *) paddr;
+ }
+
+ /*
+ * Now scan the list.
+ */
+
+ mutex_lock(&all_pagers.lock);
+
+ num_pagers = 0;
+ queue_iterate(&all_pagers.queue, entry, default_pager_t, links) {
+
+ mach_port_t port;
+ vm_size_t size;
+
+ if ((num_pagers >= opotential) ||
+ (num_pagers >= ppotential)) {
+ /*
+ * This should be rare. In any case,
+ * we will only miss recent objects,
+ * because they are added at the end.
+ */
+ break;
+ }
+
+ /*
+ * Avoid interfering with normal operations
+ */
+ if (!mutex_try_lock(&entry->dpager.lock))
+ goto not_this_one;
+ size = pager_allocated(&entry->dpager);
+ mutex_unlock(&entry->dpager.lock);
+
+ dstruct_lock(entry);
+
+ port = entry->pager_name;
+ if (port == MACH_PORT_NULL) {
+ /*
+ * The object is waiting for no-senders
+ * or memory_object_init.
+ */
+ dstruct_unlock(entry);
+ goto not_this_one;
+ }
+
+ /*
+ * We need a reference for the reply message.
+ * While we are unlocked, the bucket queue
+ * can change and the object might be terminated.
+ * memory_object_terminate will wait for us,
+ * preventing deallocation of the entry.
+ */
+
+ if (--entry->name_refs == 0) {
+ dstruct_unlock(entry);
+
+ /* keep the list locked, wont take long */
+
+ kr = mach_port_mod_refs(default_pager_self,
+ port, MACH_PORT_RIGHT_SEND,
+ default_pager_max_urefs);
+ if (kr != KERN_SUCCESS)
+ panic("%sdefault_pager_objects",my_name);
+
+ dstruct_lock(entry);
+
+ entry->name_refs += default_pager_max_urefs;
+ pager_port_finish_refs(entry);
+ }
+ dstruct_unlock(entry);
+
+ /* the arrays are wired, so no deadlock worries */
+
+ objects[num_pagers].dpo_object = (vm_offset_t) entry;
+ objects[num_pagers].dpo_size = size;
+ ports [num_pagers++] = port;
+ continue;
+not_this_one:
+ /*
+ * Do not return garbage
+ */
+ objects[num_pagers].dpo_object = (vm_offset_t) 0;
+ objects[num_pagers].dpo_size = 0;
+ ports [num_pagers++] = MACH_PORT_NULL;
+
+ }
+
+ mutex_unlock(&all_pagers.lock);
+
+ /*
+ * Deallocate and clear unused memory.
+ * (Returned memory will automagically become pageable.)
+ */
+
+ if (objects == *objectsp) {
+ /*
+ * Our returned information fit inline.
+ * Nothing to deallocate.
+ */
+
+ *ocountp = num_pagers;
+ } else if (actual == 0) {
+ (void) vm_deallocate(default_pager_self, oaddr, osize);
+
+ /* return zero items inline */
+ *ocountp = 0;
+ } else {
+ vm_offset_t used;
+
+ used = round_page(actual * sizeof *objects);
+
+ if (used != osize)
+ (void) vm_deallocate(default_pager_self,
+ oaddr + used, osize - used);
+
+ *objectsp = objects;
+ *ocountp = num_pagers;
+ }
+
+ if (ports == *portsp) {
+ /*
+ * Our returned information fit inline.
+ * Nothing to deallocate.
+ */
+
+ *pcountp = num_pagers;
+ } else if (actual == 0) {
+ (void) vm_deallocate(default_pager_self, paddr, psize);
+
+ /* return zero items inline */
+ *pcountp = 0;
+ } else {
+ vm_offset_t used;
+
+ used = round_page(actual * sizeof *ports);
+
+ if (used != psize)
+ (void) vm_deallocate(default_pager_self,
+ paddr + used, psize - used);
+
+ *portsp = ports;
+ *pcountp = num_pagers;
+ }
+
+ return KERN_SUCCESS;
+
+ nomemory:
+
+ {
+ register int i;
+ for (i = 0; i < num_pagers; i++)
+ (void) mach_port_deallocate(default_pager_self, ports[i]);
+ }
+
+ if (objects != *objectsp)
+ (void) vm_deallocate(default_pager_self, oaddr, osize);
+
+ if (ports != *portsp)
+ (void) vm_deallocate(default_pager_self, paddr, psize);
+
+ return KERN_RESOURCE_SHORTAGE;
+}
+
+
+kern_return_t
+S_default_pager_object_pages (mach_port_t pager,
+ mach_port_t object,
+ default_pager_page_array_t *pagesp,
+ natural_t *countp)
+{
+ vm_offset_t addr; /* memory for page offsets */
+ vm_size_t size; /* current memory size */
+ default_pager_page_t *pages;
+ natural_t potential, actual;
+ kern_return_t kr;
+
+ if (pager != default_pager_default_port)
+ return KERN_INVALID_ARGUMENT;
+
+ /* we start with the inline space */
+
+ pages = *pagesp;
+ potential = *countp;
+
+ for (;;) {
+ default_pager_t entry;
+
+ mutex_lock(&all_pagers.lock);
+ queue_iterate(&all_pagers.queue, entry, default_pager_t, links) {
+ dstruct_lock(entry);
+ if (entry->pager_name == object) {
+ mutex_unlock(&all_pagers.lock);
+ goto found_object;
+ }
+ dstruct_unlock(entry);
+ }
+ mutex_unlock(&all_pagers.lock);
+
+ /* did not find the object */
+
+ if (pages != *pagesp)
+ (void) vm_deallocate(default_pager_self, addr, size);
+ return KERN_INVALID_ARGUMENT;
+
+ found_object:
+
+ if (!mutex_try_lock(&entry->dpager.lock)) {
+ /* oh well bad luck */
+
+ dstruct_unlock(entry);
+
+ /* yield the processor */
+ (void) thread_switch(MACH_PORT_NULL,
+ SWITCH_OPTION_NONE, 0);
+ continue;
+ }
+
+ actual = pager_pages(&entry->dpager, pages, potential);
+ mutex_unlock(&entry->dpager.lock);
+ dstruct_unlock(entry);
+
+ if (actual <= potential)
+ break;
+
+ /* allocate more memory */
+
+ if (pages != *pagesp)
+ (void) vm_deallocate(default_pager_self, addr, size);
+ size = round_page(actual * sizeof *pages);
+ kr = vm_allocate(default_pager_self, &addr, size, TRUE);
+ if (kr != KERN_SUCCESS)
+ return kr;
+ pages = (default_pager_page_t *) addr;
+ potential = size/sizeof *pages;
+ }
+
+ /*
+ * Deallocate and clear unused memory.
+ * (Returned memory will automagically become pageable.)
+ */
+
+ if (pages == *pagesp) {
+ /*
+ * Our returned information fit inline.
+ * Nothing to deallocate.
+ */
+
+ *countp = actual;
+ } else if (actual == 0) {
+ (void) vm_deallocate(default_pager_self, addr, size);
+
+ /* return zero items inline */
+ *countp = 0;
+ } else {
+ vm_offset_t used;
+
+ used = round_page(actual * sizeof *pages);
+
+ if (used != size)
+ (void) vm_deallocate(default_pager_self,
+ addr + used, size - used);
+
+ *pagesp = pages;
+ *countp = actual;
+ }
+ return KERN_SUCCESS;
+}
+
+
+kern_return_t
+S_default_pager_object_set_size (mach_port_t pager,
+ mach_port_seqno_t seqno,
+ mach_port_t reply_to,
+ vm_size_t limit)
+{
+ kern_return_t kr;
+ default_pager_t ds;
+
+ ds = pager_port_lookup(pager);
+ if (ds == DEFAULT_PAGER_NULL)
+ return KERN_INVALID_ARGUMENT;
+
+ pager_port_lock(ds, seqno);
+ pager_port_check_request(ds, reply_to);
+ pager_port_wait_for_readers(ds);
+ pager_port_wait_for_writers(ds);
+
+ limit = round_page (limit);
+ if (ds->dpager.size <= limit / vm_page_size)
+ {
+ /* The limit has not been exceeded heretofore. Just change it. */
+ ds->dpager.limit = limit;
+ kr = KERN_SUCCESS;
+ }
+ else if (ds->lock_request == MACH_PORT_NULL)
+ {
+ /* Tell the kernel to flush from core all the pages being removed.
+ We will get the memory_object_lock_completed callback when they
+ have been flushed. We handle that by completing the limit update
+ and posting the reply to the pending truncation. */
+ kr = memory_object_lock_request (ds->pager_request,
+ limit,
+ ds->dpager.size * vm_page_size - limit,
+ MEMORY_OBJECT_RETURN_NONE, TRUE,
+ VM_PROT_ALL, ds->pager);
+ if (kr != KERN_SUCCESS)
+ panic ("memory_object_lock_request: %d", kr);
+ ds->lock_request = reply_to;
+ kr = MIG_NO_REPLY;
+ }
+ else
+ /* There is already another call in progress. Tough titties. */
+ kr = KERN_FAILURE;
+
+ pager_port_unlock(ds, seqno);
+
+ return kr;
+}
+
+/*
+ * Add/remove extra paging space
+ */
+
+extern mach_port_t bootstrap_master_device_port;
+extern mach_port_t bootstrap_master_host_port;
+
+kern_return_t
+S_default_pager_paging_file (pager, mdport, file_name, add)
+ mach_port_t pager;
+ mach_port_t mdport;
+ default_pager_filename_t file_name;
+ boolean_t add;
+{
+ kern_return_t kr;
+
+ if (pager != default_pager_default_port)
+ return KERN_INVALID_ARGUMENT;
+
+#if 0
+dprintf("bmd %x md %x\n", bootstrap_master_device_port, mdport);
+#endif
+ if (add) {
+ kr = add_paging_file(bootstrap_master_device_port,
+ file_name, 0);
+ } else {
+ kr = remove_paging_file(file_name);
+ }
+
+ /* XXXX more code needed */
+ if (mdport != bootstrap_master_device_port)
+ mach_port_deallocate( mach_task_self(), mdport);
+
+ return kr;
+}
+
+default_pager_register_fileserver(pager, fileserver)
+ mach_port_t pager;
+ mach_port_t fileserver;
+{
+ if (pager != default_pager_default_port)
+ return KERN_INVALID_ARGUMENT;
+#if notyet
+ mach_port_deallocate(mach_task_self(), fileserver);
+ if (0) dp_helper_paging_space(0,0,0);/*just linkit*/
+#endif
+ return KERN_SUCCESS;
+}
+
+/*
+ * When things do not quite workout...
+ */
+no_paging_space(out_of_memory)
+ boolean_t out_of_memory;
+{
+ static char here[] = "%s *** NOT ENOUGH PAGING SPACE ***";
+
+ if (out_of_memory)
+ dprintf("*** OUT OF MEMORY *** ");
+ panic(here, my_name);
+}
+
+overcommitted(got_more_space, space)
+ boolean_t got_more_space;
+ vm_size_t space; /* in pages */
+{
+ vm_size_t pages_free, pages_total;
+
+ static boolean_t user_warned = FALSE;
+ static vm_size_t pages_shortage = 0;
+
+ paging_space_info(&pages_total, &pages_free);
+
+ /*
+ * If user added more space, see if it is enough
+ */
+ if (got_more_space) {
+ pages_free -= pages_shortage;
+ if (pages_free > 0) {
+ pages_shortage = 0;
+ if (user_warned)
+ dprintf("%s paging space ok now.\n", my_name);
+ } else
+ pages_shortage = pages_free;
+ user_warned = FALSE;
+ return;
+ }
+ /*
+ * We ran out of gas, let user know.
+ */
+ pages_free -= space;
+ pages_shortage = (pages_free > 0) ? 0 : -pages_free;
+ if (!user_warned && pages_shortage) {
+ user_warned = TRUE;
+ dprintf("%s paging space over-committed.\n", my_name);
+ }
+#if debug
+ user_warned = FALSE;
+ dprintf("%s paging space over-committed [+%d (%d) pages].\n",
+ my_name, space, pages_shortage);
+#endif
+}
+
+paging_space_info(totp, freep)
+ vm_size_t *totp, *freep;
+{
+ register vm_size_t total, free;
+ register partition_t part;
+ register int i;
+
+ total = free = 0;
+ for (i = 0; i < all_partitions.n_partitions; i++) {
+
+ if ((part = partition_of(i)) == 0) continue;
+
+ /* no need to lock: by the time this data
+ gets back to any remote requestor it
+ will be obsolete anyways */
+ total += part->total_size;
+ free += part->free;
+#if debug
+ dprintf("Partition %d: x%x total, x%x free\n",
+ i, part->total_size, part->free);
+#endif
+ }
+ *totp = total;
+ *freep = free;
+}
+
+/*
+ * Catch exceptions.
+ */
+
+kern_return_t
+catch_exception_raise(exception_port, thread, task, exception, code, subcode)
+ mach_port_t exception_port;
+ mach_port_t thread, task;
+ int exception, code, subcode;
+{
+ ddprintf ("(default_pager)catch_exception_raise(%d,%d,%d)\n",
+ exception, code, subcode);
+ panic(my_name);
+
+ /* mach_msg_server will deallocate thread/task for us */
+
+ return KERN_FAILURE;
+}
diff --git a/serverboot/defs.h b/serverboot/defs.h
new file mode 100644
index 00000000..7b872fd6
--- /dev/null
+++ b/serverboot/defs.h
@@ -0,0 +1,95 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * Common definitions for Berkeley Fast File System.
+ */
+
+/*
+ * Compatibility definitions for disk IO.
+ */
+
+/*
+ * Disk devices do all IO in 512-byte blocks.
+ */
+#define DEV_BSIZE 512
+
+/*
+ * Conversion between bytes and disk blocks.
+ */
+#define btodb(byte_offset) ((byte_offset) >> 9)
+#define dbtob(block_number) ((block_number) << 9)
+
+/*
+ * Compatibility definitions for old type names.
+ */
+
+typedef struct _quad_ {
+ unsigned int val[2]; /* 2 int values make... */
+} quad; /* an 8-byte item */
+
+#if 0
+typedef unsigned char u_char; /* unsigned char */
+typedef unsigned short u_short; /* unsigned short */
+typedef unsigned int u_int; /* unsigned int */
+
+typedef unsigned int time_t; /* an unsigned int */
+typedef unsigned int daddr_t; /* an unsigned int */
+typedef unsigned int off_t; /* another unsigned int */
+
+typedef unsigned short uid_t;
+typedef unsigned short gid_t;
+typedef unsigned int ino_t;
+#endif
+
+#define NBBY 8
+
+/*
+ * The file system is made out of blocks of at most MAXBSIZE units,
+ * with smaller units (fragments) only in the last direct block.
+ * MAXBSIZE primarily determines the size of buffers in the buffer
+ * pool. It may be made larger without any effect on existing
+ * file systems; however, making it smaller may make some file
+ * systems unmountable.
+ *
+ * Note that the disk devices are assumed to have DEV_BSIZE "sectors"
+ * and that fragments must be some multiple of this size.
+ */
+#define MAXBSIZE 8192
+#define MAXFRAG 8
+
+/*
+ * MAXPATHLEN defines the longest permissible path length
+ * after expanding symbolic links.
+ *
+ * MAXSYMLINKS defines the maximum number of symbolic links
+ * that may be expanded in a path name. It should be set
+ * high enough to allow all legitimate uses, but halt infinite
+ * loops reasonably quickly.
+ */
+
+#define MAXPATHLEN 1024
+#define MAXSYMLINKS 8
+
diff --git a/serverboot/dir.h b/serverboot/dir.h
new file mode 100644
index 00000000..208df5ce
--- /dev/null
+++ b/serverboot/dir.h
@@ -0,0 +1,142 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * Copyright (c) 1982, 1986, 1989 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * @(#)dir.h 7.6 (Berkeley) 5/9/89
+ */
+
+#ifndef _BOOT_UFS_DIR_H_
+#define _BOOT_UFS_DIR_H_
+
+/*
+ * 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
+ * with null bytes. All names are guaranteed null terminated.
+ * The maximum length of a name in a directory is MAXNAMLEN.
+ *
+ * The macro DIRSIZ(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(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.
+ */
+#define DIRBLKSIZ DEV_BSIZE
+#define MAXNAMLEN 255
+
+struct direct {
+ u_int d_ino; /* inode number of entry */
+ u_short d_reclen; /* length of this record */
+ u_short d_namlen; /* length of string in d_name */
+ char d_name[MAXNAMLEN + 1]; /* name with length <= MAXNAMLEN */
+};
+
+/*
+ * 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.
+ */
+#undef DIRSIZ
+#define DIRSIZ(dp) \
+ ((sizeof (struct direct) - (MAXNAMLEN+1)) + (((dp)->d_namlen+1 + 3) &~ 3))
+
+#ifdef KERNEL
+/*
+ * 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_int dot_ino;
+ short dot_reclen;
+ short dot_namlen;
+ char dot_name[4]; /* must be multiple of 4 */
+ u_int dotdot_ino;
+ short dotdot_reclen;
+ short dotdot_namlen;
+ char dotdot_name[4]; /* ditto */
+};
+#endif
+
+/*
+ * The following information should be obtained from <dirent.h>
+ * and is provided solely (and temporarily) for backward compatibility.
+ */
+#ifndef KERNEL
+#define d_fileno d_ino /* compatibility with POSIX */
+#ifndef DEV_BSIZE
+#define DEV_BSIZE 512
+#endif
+/*
+ * Definitions for library routines operating on directories.
+ */
+typedef struct _dirdesc {
+ int dd_fd;
+ int dd_loc;
+ int dd_size;
+ char dd_buf[DIRBLKSIZ];
+} DIR;
+
+#define dirfd(dirp) ((dirp)->dd_fd)
+
+#ifndef NULL
+#define NULL 0
+#endif
+extern DIR *opendir();
+extern struct direct *readdir();
+extern int telldir();
+extern void seekdir();
+#define rewinddir(dirp) seekdir((dirp), (long)0)
+extern void closedir();
+#endif /* not KERNEL */
+#endif /* _BOOT_UFS_DIR_H_ */
diff --git a/serverboot/disk_inode.h b/serverboot/disk_inode.h
new file mode 100644
index 00000000..6eed9104
--- /dev/null
+++ b/serverboot/disk_inode.h
@@ -0,0 +1,101 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * Copyright (c) 1982, 1989 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * @(#)inode.h 7.5 (Berkeley) 7/3/89
+ */
+
+#ifndef _BOOT_UFS_DISK_INODE_H_
+#define _BOOT_UFS_DISK_INODE_H_
+
+/*
+ * The I node is the focus of all file activity in the BSD Fast File System.
+ * There is a unique inode allocated for each active file,
+ * each current directory, each mounted-on file, text file, and the root.
+ * An inode is 'named' by its dev/inumber pair. (iget/iget.c)
+ * Data in icommon is read in from permanent inode on volume.
+ */
+
+#define FFS_NDADDR 12 /* direct addresses in inode */
+#define FFS_NIADDR 3 /* indirect addresses in inode */
+
+#define FFS_MAX_FASTLINK_SIZE ((FFS_NDADDR + FFS_NIADDR) * sizeof(daddr_t))
+
+struct icommon {
+ u_short ic_mode; /* 0: mode and type of file */
+ short ic_nlink; /* 2: number of links to file */
+ short ic_uid; /* 4: owner's user id */
+ short ic_gid; /* 6: owner's group id */
+ quad ic_size; /* 8: number of bytes in file */
+ time_t ic_atime; /* 16: time last accessed */
+ int ic_atspare;
+ time_t ic_mtime; /* 24: time last modified */
+ int ic_mtspare;
+ time_t ic_ctime; /* 32: last time inode changed */
+ int ic_ctspare;
+ union {
+ struct {
+ daddr_t Mb_db[FFS_NDADDR]; /* 40: disk block addresses */
+ daddr_t Mb_ib[FFS_NIADDR]; /* 88: indirect blocks */
+ } ic_Mb;
+ char ic_Msymlink[FFS_MAX_FASTLINK_SIZE];
+ /* 40: symbolic link name */
+ } ic_Mun;
+#define ic_db ic_Mun.ic_Mb.Mb_db
+#define ic_ib ic_Mun.ic_Mb.Mb_ib
+#define ic_symlink ic_Mun.ic_Msymlink
+ int ic_flags; /* 100: status, currently unused */
+ int ic_blocks; /* 104: blocks actually held */
+ int ic_gen; /* 108: generation number */
+ int ic_spare[4]; /* 112: reserved, currently unused */
+} i_ic;
+
+/*
+ * Same structure, but on disk.
+ */
+struct dinode {
+ union {
+ struct icommon di_com;
+ char di_char[128];
+ } di_un;
+};
+#define di_ic di_un.di_com
+
+#endif /* _BOOT_UFS_DISK_INODE_H_ */
diff --git a/serverboot/disk_inode_ffs.h b/serverboot/disk_inode_ffs.h
new file mode 100644
index 00000000..ab352f84
--- /dev/null
+++ b/serverboot/disk_inode_ffs.h
@@ -0,0 +1,99 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * Copyright (c) 1982, 1989 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * @(#)inode.h 7.5 (Berkeley) 7/3/89
+ */
+
+#ifndef _BOOT_UFS_DISK_INODE_FFS_H_
+#define _BOOT_UFS_DISK_INODE_FFS_H_
+
+#define NDADDR FFS_NDADDR
+#define NIADDR FFS_NIADDR
+
+#define MAX_FASTLINK_SIZE FFS_MAX_FASTLINK_SIZE
+
+#define IC_FASTLINK 0x0001 /* Symbolic link in inode */
+
+#define i_mode i_ic.ic_mode
+#define i_nlink i_ic.ic_nlink
+#define i_uid i_ic.ic_uid
+#define i_gid i_ic.ic_gid
+#if BYTE_MSF
+#define i_size i_ic.ic_size.val[1]
+#else /* BYTE_LSF */
+#define i_size i_ic.ic_size.val[0]
+#endif
+#define i_db i_ic.ic_db
+#define i_ib i_ic.ic_ib
+#define i_atime i_ic.ic_atime
+#define i_mtime i_ic.ic_mtime
+#define i_ctime i_ic.ic_ctime
+#define i_blocks i_ic.ic_blocks
+#define i_rdev i_ic.ic_db[0]
+#define i_symlink i_ic.ic_symlink
+#define i_flags i_ic.ic_flags
+#define i_gen i_ic.ic_gen
+
+/* modes */
+#define IFMT 0xf000 /* type of file */
+#define IFCHR 0x2000 /* character special */
+#define IFDIR 0x4000 /* directory */
+#define IFBLK 0x6000 /* block special */
+#define IFREG 0x8000 /* regular */
+#define IFLNK 0xa000 /* symbolic link */
+#define IFSOCK 0xc000 /* socket */
+
+
+#define ISUID 0x0800 /* set user id on execution */
+#define ISGID 0x0400 /* set group id on execution */
+#define ISVTX 0x0200 /* save swapped text even after use */
+#define IREAD 0x0100 /* read, write, execute permissions */
+#define IWRITE 0x0080
+#define IEXEC 0x0040
+
+#define f_fs u.ffs.ffs_fs
+#define i_ic u.ffs.ffs_ic
+#define f_nindir u.ffs.ffs_nindir
+#define f_blk u.ffs.ffs_blk
+#define f_blksize u.ffs.ffs_blksize
+#define f_blkno u.ffs.ffs_blkno
+
+#endif /* _BOOT_UFS_DISK_INODE_FFS_H_ */
diff --git a/serverboot/elf-load.c b/serverboot/elf-load.c
new file mode 100644
index 00000000..603eadf0
--- /dev/null
+++ b/serverboot/elf-load.c
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 1995, 1994, 1993, 1992, 1991, 1990
+ * Open Software Foundation, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appears in all copies and
+ * that both the copyright notice and this permission notice appear in
+ * supporting documentation, and that the name of ("OSF") or Open Software
+ * Foundation not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior permission.
+ *
+ * OSF DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL OSF BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * ACTION OF CONTRACT, NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE
+ */
+/*
+ * OSF Research Institute MK6.1 (unencumbered) 1/31/1995
+ */
+
+#include <alloca.h>
+#include <mach/machine/vm_types.h>
+#include <elf.h>
+#include "mach-exec.h"
+
+int exec_load(exec_read_func_t *read, exec_read_exec_func_t *read_exec,
+ void *handle, exec_info_t *out_info)
+{
+ vm_size_t actual;
+ union { Elf32_Ehdr h; Elf64_Ehdr h64; } x;
+ int i;
+ int result;
+
+ /* Read the ELF header. */
+ if ((result = (*read)(handle, 0, &x, sizeof(x), &actual)) != 0)
+ return result;
+ if (actual < sizeof(x))
+ return EX_NOT_EXECUTABLE;
+
+ if ((x.h.e_ident[EI_MAG0] != ELFMAG0) ||
+ (x.h.e_ident[EI_MAG1] != ELFMAG1) ||
+ (x.h.e_ident[EI_MAG2] != ELFMAG2) ||
+ (x.h.e_ident[EI_MAG3] != ELFMAG3))
+ return EX_NOT_EXECUTABLE;
+
+ /* Make sure the file is of the right architecture. */
+#ifdef i386
+# define MY_CLASS ELFCLASS32
+# define MY_DATA ELFDATA2LSB
+# define MY_MACHINE EM_386
+#elif defined __alpha__
+# define MY_CLASS ELFCLASS64
+# define MY_DATA ELFDATA2LSB
+# define MY_MACHINE EM_ALPHA
+#else
+#error Not ported to this architecture!
+#endif
+
+ if ((x.h.e_ident[EI_CLASS] != MY_CLASS) ||
+ (x.h.e_ident[EI_DATA] != MY_DATA))
+ return EX_WRONG_ARCH;
+
+ if (MY_CLASS == ELFCLASS64)
+ {
+ Elf64_Phdr *phdr, *ph;
+ vm_size_t phsize;
+
+ if (x.h64.e_machine != MY_MACHINE)
+ return EX_WRONG_ARCH;
+
+ /* XXX others */
+ out_info->entry = (vm_offset_t) x.h64.e_entry;
+ out_info->init_dp = 0; /* ? */
+
+ phsize = x.h64.e_phnum * x.h64.e_phentsize;
+ phdr = (Elf64_Phdr *)alloca(phsize);
+
+ result = (*read)(handle, x.h64.e_phoff, phdr, phsize, &actual);
+ if (result)
+ return result;
+ if (actual < phsize)
+ return EX_CORRUPT;
+
+ for (i = 0; i < x.h64.e_phnum; i++)
+ {
+ ph = (Elf64_Phdr *)((vm_offset_t)phdr + i * x.h64.e_phentsize);
+ if (ph->p_type == PT_LOAD)
+ {
+ exec_sectype_t type = EXEC_SECTYPE_ALLOC |
+ EXEC_SECTYPE_LOAD;
+ if (ph->p_flags & PF_R) type |= EXEC_SECTYPE_READ;
+ if (ph->p_flags & PF_W) type |= EXEC_SECTYPE_WRITE;
+ if (ph->p_flags & PF_X) type |= EXEC_SECTYPE_EXECUTE;
+ result = (*read_exec)(handle,
+ ph->p_offset, ph->p_filesz,
+ ph->p_vaddr, ph->p_memsz, type);
+ }
+ }
+ }
+ else
+ {
+ Elf32_Phdr *phdr, *ph;
+ vm_size_t phsize;
+
+ if (x.h.e_machine != MY_MACHINE)
+ return EX_WRONG_ARCH;
+
+ /* XXX others */
+ out_info->entry = (vm_offset_t) x.h.e_entry;
+ out_info->init_dp = 0; /* ? */
+
+ phsize = x.h.e_phnum * x.h.e_phentsize;
+ phdr = (Elf32_Phdr *)alloca(phsize);
+
+ result = (*read)(handle, x.h.e_phoff, phdr, phsize, &actual);
+ if (result)
+ return result;
+ if (actual < phsize)
+ return EX_CORRUPT;
+
+ for (i = 0; i < x.h.e_phnum; i++)
+ {
+ ph = (Elf32_Phdr *)((vm_offset_t)phdr + i * x.h.e_phentsize);
+ if (ph->p_type == PT_LOAD)
+ {
+ exec_sectype_t type = EXEC_SECTYPE_ALLOC |
+ EXEC_SECTYPE_LOAD;
+ if (ph->p_flags & PF_R) type |= EXEC_SECTYPE_READ;
+ if (ph->p_flags & PF_W) type |= EXEC_SECTYPE_WRITE;
+ if (ph->p_flags & PF_X) type |= EXEC_SECTYPE_EXECUTE;
+ result = (*read_exec)(handle,
+ ph->p_offset, ph->p_filesz,
+ ph->p_vaddr, ph->p_memsz, type);
+ }
+ }
+ }
+
+ return 0;
+}
diff --git a/serverboot/exec.c b/serverboot/exec.c
new file mode 100644
index 00000000..a487fb5d
--- /dev/null
+++ b/serverboot/exec.c
@@ -0,0 +1,147 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1993-1989 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * i386-specific routines for loading a.out files.
+ */
+
+#include <mach.h>
+#include <mach/machine/vm_param.h>
+#include "mach-exec.h"
+
+#include <file_io.h>
+
+#ifdef i386
+
+#include <mach/machine/eflags.h>
+
+/*
+ * Machine-dependent portions of execve() for the i386.
+ */
+
+#define STACK_SIZE (64*1024)
+
+char *set_regs(
+ mach_port_t user_task,
+ mach_port_t user_thread,
+ struct exec_info *info,
+ int arg_size)
+{
+ vm_offset_t stack_start;
+ vm_offset_t stack_end;
+ struct i386_thread_state regs;
+ unsigned int reg_size;
+
+ /*
+ * Add space for 5 ints to arguments, for
+ * PS program. XXX
+ */
+ arg_size += 5 * sizeof(int);
+
+ /*
+ * Allocate stack.
+ */
+ stack_end = VM_MAX_ADDRESS;
+ stack_start = VM_MAX_ADDRESS - STACK_SIZE;
+ (void)vm_allocate(user_task,
+ &stack_start,
+ (vm_size_t)(stack_end - stack_start),
+ FALSE);
+
+ reg_size = i386_THREAD_STATE_COUNT;
+ (void)thread_get_state(user_thread,
+ i386_THREAD_STATE,
+ (thread_state_t)&regs,
+ &reg_size);
+
+ regs.eip = info->entry;
+ regs.uesp = (int)((stack_end - arg_size) & ~(sizeof(int)-1));
+
+ /* regs.efl |= EFL_TF; trace flag*/
+
+ (void)thread_set_state(user_thread,
+ i386_THREAD_STATE,
+ (thread_state_t)&regs,
+ reg_size);
+
+ return (char *)regs.uesp;
+}
+
+#elif defined __alpha__
+
+
+/*
+ * Object:
+ * set_regs EXPORTED function
+ *
+ * Initialize enough state for a thread to run, including
+ * stack memory and stack pointer, and program counter.
+ *
+ */
+#define STACK_SIZE (vm_size_t)(128*1024)
+
+char *set_regs(
+ mach_port_t user_task,
+ mach_port_t user_thread,
+ struct exec_info *info,
+ int arg_size)
+{
+ vm_offset_t stack_start;
+ vm_offset_t stack_end;
+ struct alpha_thread_state regs;
+
+ natural_t reg_size;
+
+ /*
+ * Allocate stack.
+ */
+ stack_end = VM_MAX_ADDRESS;
+ stack_start = stack_end - STACK_SIZE;
+ (void)vm_allocate(user_task,
+ &stack_start,
+ (vm_size_t)(STACK_SIZE),
+ FALSE);
+
+ reg_size = ALPHA_THREAD_STATE_COUNT;
+ (void)thread_get_state(user_thread,
+ ALPHA_THREAD_STATE,
+ (thread_state_t)&regs,
+ &reg_size);
+
+ regs.pc = info->entry;
+ regs.r29 = info->init_dp;
+ regs.r30 = (integer_t)((stack_end - arg_size) & ~(sizeof(integer_t)-1));
+
+ (void)thread_set_state(user_thread,
+ ALPHA_THREAD_STATE,
+ (thread_state_t)&regs,
+ reg_size);
+
+ return (char *)regs.r30;
+}
+
+#else
+# error "Not ported to this architecture!"
+#endif
diff --git a/serverboot/ext2_file_io.c b/serverboot/ext2_file_io.c
new file mode 100644
index 00000000..cb915c4a
--- /dev/null
+++ b/serverboot/ext2_file_io.c
@@ -0,0 +1,983 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * Stand-alone file reading package.
+ */
+
+#include <device/device_types.h>
+#include <device/device.h>
+
+#include <mach/mach_traps.h>
+#include <mach/mach_interface.h>
+
+#include "file_io.h"
+#include "ffs_compat.h"
+
+void ext2_close_file(); /* forward */
+
+/*
+ * Free file buffers, but don't close file.
+ */
+static void
+free_file_buffers(fp)
+ register struct file *fp;
+{
+ register int level;
+
+ /*
+ * Free the indirect blocks
+ */
+ for (level = 0; level < NIADDR; level++) {
+ if (fp->f_blk[level] != 0) {
+ (void) vm_deallocate(mach_task_self(),
+ fp->f_blk[level],
+ fp->f_blksize[level]);
+ fp->f_blk[level] = 0;
+ }
+ fp->f_blkno[level] = -1;
+ }
+
+ /*
+ * Free the data block
+ */
+ if (fp->f_buf != 0) {
+ (void) vm_deallocate(mach_task_self(),
+ fp->f_buf,
+ fp->f_buf_size);
+ fp->f_buf = 0;
+ }
+ fp->f_buf_blkno = -1;
+}
+
+/*
+ * Read a new inode into a file structure.
+ */
+static int
+read_inode(inumber, fp)
+ ino_t inumber;
+ register struct file *fp;
+{
+ vm_offset_t buf;
+ mach_msg_type_number_t buf_size;
+ register
+ struct ext2_super_block *fs;
+ daddr_t disk_block;
+ kern_return_t rc;
+
+ fs = fp->f_fs;
+ disk_block = ino2blk(fs, fp->f_gd, inumber);
+
+ rc = device_read(fp->f_dev,
+ 0,
+ (recnum_t) fsbtodb(fp->f_fs, disk_block),
+ (int) EXT2_BLOCK_SIZE(fs),
+ (char **)&buf,
+ &buf_size);
+ if (rc != KERN_SUCCESS)
+ return (rc);
+
+ {
+ register struct ext2_inode *dp;
+
+ dp = (struct ext2_inode *)buf;
+ dp += itoo(fs, inumber);
+ fp->i_ic = *dp;
+ fp->f_size = dp->i_size;
+ }
+
+ (void) vm_deallocate(mach_task_self(), buf, buf_size);
+
+ /*
+ * Clear out the old buffers
+ */
+ free_file_buffers(fp);
+
+ return (0);
+}
+
+/*
+ * Given an offset in a file, find the disk block number that
+ * contains that block.
+ */
+static int
+block_map(fp, file_block, disk_block_p)
+ struct file *fp;
+ daddr_t file_block;
+ daddr_t *disk_block_p; /* out */
+{
+ int level;
+ int idx;
+ daddr_t ind_block_num;
+ kern_return_t rc;
+
+ vm_offset_t olddata[NIADDR+1];
+ vm_size_t oldsize[NIADDR+1];
+
+ /*
+ * Index structure of an inode:
+ *
+ * i_db[0..NDADDR-1] hold block numbers for blocks
+ * 0..NDADDR-1
+ *
+ * i_ib[0] index block 0 is the single indirect
+ * block
+ * holds block numbers for blocks
+ * NDADDR .. NDADDR + NINDIR(fs)-1
+ *
+ * i_ib[1] index block 1 is the double indirect
+ * block
+ * holds block numbers for INDEX blocks
+ * for blocks
+ * NDADDR + NINDIR(fs) ..
+ * NDADDR + NINDIR(fs) + NINDIR(fs)**2 - 1
+ *
+ * i_ib[2] index block 2 is the triple indirect
+ * block
+ * holds block numbers for double-indirect
+ * blocks for blocks
+ * NDADDR + NINDIR(fs) + NINDIR(fs)**2 ..
+ * NDADDR + NINDIR(fs) + NINDIR(fs)**2
+ * + NINDIR(fs)**3 - 1
+ */
+
+ mutex_lock(&fp->f_lock);
+
+ if (file_block < NDADDR) {
+ /* Direct block. */
+ *disk_block_p = fp->i_ic.i_block[file_block];
+ mutex_unlock(&fp->f_lock);
+ return (0);
+ }
+
+ file_block -= NDADDR;
+
+ /*
+ * nindir[0] = NINDIR
+ * nindir[1] = NINDIR**2
+ * nindir[2] = NINDIR**3
+ * etc
+ */
+ for (level = 0; level < NIADDR; level++) {
+ if (file_block < fp->f_nindir[level])
+ break;
+ file_block -= fp->f_nindir[level];
+ }
+ if (level == NIADDR) {
+ /* Block number too high */
+ mutex_unlock(&fp->f_lock);
+ return (FS_NOT_IN_FILE);
+ }
+
+ ind_block_num = fp->i_ic.i_block[level + NDADDR];
+
+ /*
+ * Initialize array of blocks to free.
+ */
+ for (idx = 0; idx < NIADDR; idx++)
+ oldsize[idx] = 0;
+
+ for (; level >= 0; level--) {
+
+ vm_offset_t data;
+ mach_msg_type_number_t size;
+
+ if (ind_block_num == 0)
+ break;
+
+ if (fp->f_blkno[level] == ind_block_num) {
+ /*
+ * Cache hit. Just pick up the data.
+ */
+
+ data = fp->f_blk[level];
+ }
+ else {
+ /*
+ * Drop our lock while doing the read.
+ * (The f_dev and f_fs fields don`t change.)
+ */
+ mutex_unlock(&fp->f_lock);
+
+ rc = device_read(fp->f_dev,
+ 0,
+ (recnum_t) fsbtodb(fp->f_fs, ind_block_num),
+ EXT2_BLOCK_SIZE(fp->f_fs),
+ (char **)&data,
+ &size);
+ if (rc != KERN_SUCCESS)
+ return (rc);
+
+ /*
+ * See if we can cache the data. Need a write lock to
+ * do this. While we hold the write lock, we can`t do
+ * *anything* which might block for memory. Otherwise
+ * a non-privileged thread might deadlock with the
+ * privileged threads. We can`t block while taking the
+ * write lock. Otherwise a non-privileged thread
+ * blocked in the vm_deallocate (while holding a read
+ * lock) will block a privileged thread. For the same
+ * reason, we can`t take a read lock and then use
+ * lock_read_to_write.
+ */
+
+ mutex_lock(&fp->f_lock);
+
+ olddata[level] = fp->f_blk[level];
+ oldsize[level] = fp->f_blksize[level];
+
+ fp->f_blkno[level] = ind_block_num;
+ fp->f_blk[level] = data;
+ fp->f_blksize[level] = size;
+
+ /*
+ * Return to holding a read lock, and
+ * dispose of old data.
+ */
+
+ }
+
+ if (level > 0) {
+ idx = file_block / fp->f_nindir[level-1];
+ file_block %= fp->f_nindir[level-1];
+ }
+ else
+ idx = file_block;
+
+ ind_block_num = ((daddr_t *)data)[idx];
+ }
+
+ mutex_unlock(&fp->f_lock);
+
+ /*
+ * After unlocking the file, free any blocks that
+ * we need to free.
+ */
+ for (idx = 0; idx < NIADDR; idx++)
+ if (oldsize[idx] != 0)
+ (void) vm_deallocate(mach_task_self(),
+ olddata[idx],
+ oldsize[idx]);
+
+ *disk_block_p = ind_block_num;
+ return (0);
+}
+
+/*
+ * Read a portion of a file into an internal buffer. Return
+ * the location in the buffer and the amount in the buffer.
+ */
+static int
+buf_read_file(fp, offset, buf_p, size_p)
+ register struct file *fp;
+ vm_offset_t offset;
+ vm_offset_t *buf_p; /* out */
+ vm_size_t *size_p; /* out */
+{
+ register
+ struct ext2_super_block *fs;
+ vm_offset_t off;
+ register daddr_t file_block;
+ daddr_t disk_block;
+ int rc;
+ vm_offset_t block_size;
+
+ if (offset >= fp->i_ic.i_size)
+ return (FS_NOT_IN_FILE);
+
+ fs = fp->f_fs;
+
+ off = blkoff(fs, offset);
+ file_block = lblkno(fs, offset);
+ block_size = blksize(fs, fp, file_block);
+
+ if (file_block != fp->f_buf_blkno) {
+ rc = block_map(fp, file_block, &disk_block);
+ if (rc != 0)
+ return (rc);
+
+ if (fp->f_buf)
+ (void)vm_deallocate(mach_task_self(),
+ fp->f_buf,
+ fp->f_buf_size);
+
+ if (disk_block == 0) {
+ (void)vm_allocate(mach_task_self(),
+ &fp->f_buf,
+ block_size,
+ TRUE);
+ fp->f_buf_size = block_size;
+ }
+ else {
+ rc = device_read(fp->f_dev,
+ 0,
+ (recnum_t) fsbtodb(fs, disk_block),
+ (int) block_size,
+ (char **) &fp->f_buf,
+ (mach_msg_type_number_t *)&fp->f_buf_size);
+ }
+ if (rc)
+ return (rc);
+
+ fp->f_buf_blkno = file_block;
+ }
+
+ /*
+ * Return address of byte in buffer corresponding to
+ * offset, and size of remainder of buffer after that
+ * byte.
+ */
+ *buf_p = fp->f_buf + off;
+ *size_p = block_size - off;
+
+ /*
+ * But truncate buffer at end of file.
+ */
+ if (*size_p > fp->i_ic.i_size - offset)
+ *size_p = fp->i_ic.i_size - offset;
+
+ return (0);
+}
+
+/*
+ * Search a directory for a name and return its
+ * i_number.
+ */
+static int
+search_directory(name, fp, inumber_p)
+ char * name;
+ register struct file *fp;
+ ino_t *inumber_p; /* out */
+{
+ vm_offset_t buf;
+ vm_size_t buf_size;
+ vm_offset_t offset;
+ struct ext2_dir_entry_2 *dp;
+ int length;
+ kern_return_t rc;
+ char tmp_name[256];
+
+ length = strlen(name);
+
+ offset = 0;
+ while (offset < fp->i_ic.i_size) {
+ rc = buf_read_file(fp, offset, &buf, &buf_size);
+ if (rc != KERN_SUCCESS)
+ return (rc);
+
+ dp = (struct ext2_dir_entry_2 *)buf;
+ if (dp->inode != 0) {
+ strncpy (tmp_name, dp->name, dp->name_len);
+ tmp_name[dp->name_len] = '\0';
+ if (dp->name_len == length &&
+ !strcmp(name, tmp_name))
+ {
+ /* found entry */
+ *inumber_p = dp->inode;
+ return (0);
+ }
+ }
+ offset += dp->rec_len;
+ }
+ return (FS_NO_ENTRY);
+}
+
+static int
+read_fs(dev, fsp, gdp, gd_size_p)
+ mach_port_t dev;
+ struct ext2_super_block **fsp;
+ struct ext2_group_desc **gdp;
+ vm_size_t *gd_size_p;
+{
+ register
+ struct ext2_super_block *fs;
+ vm_offset_t buf;
+ vm_offset_t buf2;
+ mach_msg_type_number_t buf_size;
+ mach_msg_type_number_t buf2_size;
+ int error;
+ int gd_count;
+ int gd_blocks;
+ int gd_size;
+ int gd_location;
+ int gd_sector;
+
+ /*
+ * Read the super block
+ */
+ error = device_read(dev, 0, (recnum_t) SBLOCK, SBSIZE,
+ (char **) &buf, &buf_size);
+ if (error)
+ return (error);
+
+ /*
+ * Check the superblock
+ */
+ fs = (struct ext2_super_block *)buf;
+ if (fs->s_magic != EXT2_SUPER_MAGIC) {
+ (void) vm_deallocate(mach_task_self(), buf, buf_size);
+ return (FS_INVALID_FS);
+ }
+
+ *fsp = fs;
+
+ /*
+ * Compute the groups informations
+ */
+ gd_count = (fs->s_blocks_count - fs->s_first_data_block +
+ fs->s_blocks_per_group - 1) / fs->s_blocks_per_group;
+ gd_blocks = (gd_count + EXT2_DESC_PER_BLOCK(fs) - 1) /
+ EXT2_DESC_PER_BLOCK(fs);
+ gd_size = gd_blocks * EXT2_BLOCK_SIZE(fs);
+ gd_location = fs->s_first_data_block + 1;
+ gd_sector = (gd_location * EXT2_BLOCK_SIZE(fs)) / DEV_BSIZE;
+
+ /*
+ * Read the groups descriptors
+ */
+ error = device_read(dev, 0, (recnum_t) gd_sector, gd_size,
+ (char **) &buf2, &buf2_size);
+ if (error) {
+ (void) vm_deallocate(mach_task_self(), buf, buf_size);
+ return error;
+ }
+
+ *gdp = (struct ext2_group_desc *) buf2;
+ *gd_size_p = gd_size;
+
+ return 0;
+}
+
+static int
+mount_fs(fp)
+ register struct file *fp;
+{
+ register struct ext2_super_block *fs;
+ int error;
+
+ error = read_fs(fp->f_dev, &fp->f_fs, &fp->f_gd, &fp->f_gd_size);
+ if (error)
+ return (error);
+
+ fs = fp->f_fs;
+
+ /*
+ * Calculate indirect block levels.
+ */
+ {
+ register int mult;
+ register int level;
+
+ mult = 1;
+ for (level = 0; level < NIADDR; level++) {
+ mult *= NINDIR(fs);
+ fp->f_nindir[level] = mult;
+ }
+ }
+
+ return (0);
+}
+
+static void
+unmount_fs(fp)
+ register struct file *fp;
+{
+ if (file_is_structured(fp)) {
+ (void) vm_deallocate(mach_task_self(),
+ (vm_offset_t) fp->f_fs,
+ SBSIZE);
+ (void) vm_deallocate(mach_task_self(),
+ (vm_offset_t) fp->f_gd,
+ fp->f_gd_size);
+ fp->f_fs = 0;
+ }
+}
+
+/*
+ * Open a file.
+ */
+int
+ext2_open_file(master_device_port, path, fp)
+ mach_port_t master_device_port;
+ char * path;
+ struct file *fp;
+{
+#define RETURN(code) { rc = (code); goto exit; }
+
+ register char *cp, *component;
+ register int c; /* char */
+ register int rc;
+ ino_t inumber, parent_inumber;
+ int nlinks = 0;
+
+ char namebuf[MAXPATHLEN+1];
+
+ if (path == 0 || *path == '\0') {
+ return FS_NO_ENTRY;
+ }
+
+ /*
+ * Copy name into buffer to allow modifying it.
+ */
+ strcpy(namebuf, path);
+
+ /*
+ * Look for '/dev/xxx' at start of path, for
+ * root device.
+ */
+ if (!strprefix(namebuf, "/dev/")) {
+ printf("no device name\n");
+ return FS_NO_ENTRY;
+ }
+
+ cp = namebuf + 5; /* device */
+ component = cp;
+ while ((c = *cp) != '\0' && c != '/') {
+ cp++;
+ }
+ *cp = '\0';
+
+ bzero (fp, sizeof (struct file));
+
+ rc = device_open(master_device_port,
+ D_READ|D_WRITE,
+ component,
+ &fp->f_dev);
+ if (rc)
+ return rc;
+
+ if (c == 0) {
+ fp->f_fs = 0;
+ goto out_ok;
+ }
+
+ *cp = c;
+
+ rc = mount_fs(fp);
+ if (rc)
+ return rc;
+
+ inumber = (ino_t) ROOTINO;
+ if ((rc = read_inode(inumber, fp)) != 0) {
+ printf("can't read root inode\n");
+ goto exit;
+ }
+
+ while (*cp) {
+
+ /*
+ * Check that current node is a directory.
+ */
+ if ((fp->i_ic.i_mode & IFMT) != IFDIR)
+ RETURN (FS_NOT_DIRECTORY);
+
+ /*
+ * Remove extra separators
+ */
+ while (*cp == '/')
+ cp++;
+
+ /*
+ * Get next component of path name.
+ */
+ component = cp;
+ {
+ register int len = 0;
+
+ while ((c = *cp) != '\0' && c != '/') {
+ if (len++ > MAXNAMLEN)
+ RETURN (FS_NAME_TOO_LONG);
+ if (c & 0200)
+ RETURN (FS_INVALID_PARAMETER);
+ cp++;
+ }
+ *cp = 0;
+ }
+
+ /*
+ * Look up component in current directory.
+ * Save directory inumber in case we find a
+ * symbolic link.
+ */
+ parent_inumber = inumber;
+ rc = search_directory(component, fp, &inumber);
+ if (rc) {
+ printf("%s: not found\n", path);
+ goto exit;
+ }
+ *cp = c;
+
+ /*
+ * Open next component.
+ */
+ if ((rc = read_inode(inumber, fp)) != 0)
+ goto exit;
+
+ /*
+ * Check for symbolic link.
+ */
+ if ((fp->i_ic.i_mode & IFMT) == IFLNK) {
+
+ int link_len = fp->i_ic.i_size;
+ int len;
+
+ len = strlen(cp) + 1;
+
+ if (link_len + len >= MAXPATHLEN - 1)
+ RETURN (FS_NAME_TOO_LONG);
+
+ if (++nlinks > MAXSYMLINKS)
+ RETURN (FS_SYMLINK_LOOP);
+
+ memmove(&namebuf[link_len], cp, len);
+
+#ifdef IC_FASTLINK
+ if (fp->i_ic.i_blocks == 0) {
+ bcopy(fp->i_ic.i_block, namebuf, (unsigned) link_len);
+ }
+ else
+#endif /* IC_FASTLINK */
+ {
+ /*
+ * Read file for symbolic link
+ */
+ vm_offset_t buf;
+ mach_msg_type_number_t buf_size;
+ daddr_t disk_block;
+ register struct ext2_super_block *fs = fp->f_fs;
+
+ (void) block_map(fp, (daddr_t)0, &disk_block);
+ rc = device_read(fp->f_dev,
+ 0,
+ (recnum_t) fsbtodb(fs, disk_block),
+ (int) blksize(fs, fp, 0),
+ (char **) &buf,
+ &buf_size);
+ if (rc)
+ goto exit;
+
+ bcopy((char *)buf, namebuf, (unsigned)link_len);
+ (void) vm_deallocate(mach_task_self(), buf, buf_size);
+ }
+
+ /*
+ * If relative pathname, restart at parent directory.
+ * If absolute pathname, restart at root.
+ * If pathname begins '/dev/<device>/',
+ * restart at root of that device.
+ */
+ cp = namebuf;
+ if (*cp != '/') {
+ inumber = parent_inumber;
+ }
+ else if (!strprefix(cp, "/dev/")) {
+ inumber = (ino_t)ROOTINO;
+ }
+ else {
+ cp += 5;
+ component = cp;
+ while ((c = *cp) != '\0' && c != '/') {
+ cp++;
+ }
+ *cp = '\0';
+
+ /*
+ * Unmount current file system and free buffers.
+ */
+ ext2_close_file(fp);
+
+ /*
+ * Open new root device.
+ */
+ rc = device_open(master_device_port,
+ D_READ,
+ component,
+ &fp->f_dev);
+ if (rc)
+ return (rc);
+
+ if (c == 0) {
+ fp->f_fs = 0;
+ goto out_ok;
+ }
+
+ *cp = c;
+
+ rc = mount_fs(fp);
+ if (rc)
+ return (rc);
+
+ inumber = (ino_t)ROOTINO;
+ }
+ if ((rc = read_inode(inumber, fp)) != 0)
+ goto exit;
+ }
+ }
+
+ /*
+ * Found terminal component.
+ */
+ out_ok:
+ mutex_init(&fp->f_lock);
+ return 0;
+
+ /*
+ * At error exit, close file to free storage.
+ */
+ exit:
+ ext2_close_file(fp);
+ return rc;
+}
+
+/*
+ * Close file - free all storage used.
+ */
+void
+ext2_close_file(fp)
+ register struct file *fp;
+{
+ register int i;
+
+ /*
+ * Free the disk super-block.
+ */
+ unmount_fs(fp);
+
+ /*
+ * Free the inode and data buffers.
+ */
+ free_file_buffers(fp);
+}
+
+int
+ext2_file_is_directory(struct file *fp)
+{
+ return (fp->i_ic.i_mode & IFMT) == IFDIR;
+}
+
+int
+ext2_file_is_regular(struct file *fp)
+{
+ return (fp->i_ic.i_mode & IFMT) == IFREG;
+}
+
+/*
+ * Copy a portion of a file into kernel memory.
+ * Cross block boundaries when necessary.
+ */
+int
+ext2_read_file(fp, offset, start, size, resid)
+ register struct file *fp;
+ vm_offset_t offset;
+ vm_offset_t start;
+ vm_size_t size;
+ vm_size_t *resid; /* out */
+{
+ int rc;
+ register vm_size_t csize;
+ vm_offset_t buf;
+ vm_size_t buf_size;
+
+ while (size != 0) {
+ rc = buf_read_file(fp, offset, &buf, &buf_size);
+ if (rc)
+ return (rc);
+
+ csize = size;
+ if (csize > buf_size)
+ csize = buf_size;
+ if (csize == 0)
+ break;
+
+ bcopy((char *)buf, (char *)start, csize);
+
+ offset += csize;
+ start += csize;
+ size -= csize;
+ }
+ if (resid)
+ *resid = size;
+
+ return (0);
+}
+
+/* simple utility: only works for 2^n */
+static int
+log2(n)
+ register unsigned int n;
+{
+ register int i = 0;
+
+ while ((n & 1) == 0) {
+ i++;
+ n >>= 1;
+ }
+ return i;
+}
+
+/*
+ * Make an empty file_direct for a device.
+ */
+int
+ext2_open_file_direct(dev, fdp, is_structured)
+ mach_port_t dev;
+ register struct file_direct *fdp;
+ boolean_t is_structured;
+{
+ struct ext2_super_block *fs;
+ struct ext2_group_desc *gd;
+ vm_size_t gd_size;
+ int rc;
+
+ if (!is_structured) {
+ fdp->fd_dev = dev;
+ fdp->fd_blocks = (daddr_t *) 0;
+ fdp->fd_bsize = vm_page_size;
+ fdp->fd_bshift = log2(vm_page_size);
+ fdp->fd_fsbtodb = 0; /* later */
+ fdp->fd_size = 0; /* later */
+ return 0;
+ }
+
+ rc = read_fs(dev, &fs, &gd, &gd_size);
+ if (rc)
+ return rc;
+
+ fdp->fd_dev = dev;
+ fdp->fd_blocks = (daddr_t *) 0;
+ fdp->fd_size = 0;
+ fdp->fd_bsize = EXT2_BLOCK_SIZE(fs);
+ fdp->fd_bshift = log2(fdp->fd_bsize);
+ fdp->fd_fsbtodb = log2(fdp->fd_bsize / DEV_BSIZE);
+
+ (void) vm_deallocate(mach_task_self(),
+ (vm_offset_t) fs,
+ SBSIZE);
+ (void) vm_deallocate(mach_task_self(),
+ (vm_offset_t) gd,
+ gd_size);
+
+ return 0;
+}
+
+/*
+ * Add blocks from a file to a file_direct.
+ */
+int
+ext2_add_file_direct(fdp, fp)
+ register struct file_direct *fdp;
+ register struct file *fp;
+{
+ register struct ext2_super_block *fs;
+ long num_blocks, i;
+ vm_offset_t buffer;
+ vm_size_t size;
+ int rc;
+
+ /* the file must be on the same device */
+
+ if (fdp->fd_dev != fp->f_dev)
+ return FS_INVALID_FS;
+
+ if (!file_is_structured(fp)) {
+ int result[DEV_GET_SIZE_COUNT];
+ natural_t count;
+
+ count = DEV_GET_SIZE_COUNT;
+ rc = device_get_status( fdp->fd_dev, DEV_GET_SIZE,
+ result, &count);
+ if (rc)
+ return rc;
+ fdp->fd_size = result[DEV_GET_SIZE_DEVICE_SIZE] >> fdp->fd_bshift;
+ fdp->fd_fsbtodb = log2(fdp->fd_bsize/result[DEV_GET_SIZE_RECORD_SIZE]);
+ return 0;
+ }
+
+ /* it must hold a file system */
+
+ fs = fp->f_fs;
+/*
+ if (fdp->fd_bsize != fs->fs_bsize ||
+ fdp->fd_fsbtodb != fs->fs_fsbtodb)
+*/
+ if (fdp->fd_bsize != EXT2_BLOCK_SIZE(fs))
+ return FS_INVALID_FS;
+
+ /* calculate number of blocks in the file, ignoring fragments */
+
+ num_blocks = lblkno(fs, fp->i_ic.i_size);
+
+ /* allocate memory for a bigger array */
+
+ size = (num_blocks + fdp->fd_size) * sizeof(daddr_t);
+ rc = vm_allocate(mach_task_self(), &buffer, size, TRUE);
+ if (rc != KERN_SUCCESS)
+ return rc;
+
+ /* lookup new block addresses */
+
+ for (i = 0; i < num_blocks; i++) {
+ daddr_t disk_block;
+
+ rc = block_map(fp, (daddr_t) i, &disk_block);
+ if (rc != 0) {
+ (void) vm_deallocate(mach_task_self(), buffer, size);
+ return rc;
+ }
+
+ ((daddr_t *) buffer)[fdp->fd_size + i] = disk_block;
+ }
+
+ /* copy old addresses and install the new array */
+
+ if (fdp->fd_blocks != 0) {
+ bcopy((char *) fdp->fd_blocks, (char *) buffer,
+ fdp->fd_size * sizeof(daddr_t));
+
+ (void) vm_deallocate(mach_task_self(),
+ (vm_offset_t) fdp->fd_blocks,
+ (vm_size_t) (fdp->fd_size * sizeof(daddr_t)));
+ }
+ fdp->fd_blocks = (daddr_t *) buffer;
+ fdp->fd_size += num_blocks;
+
+ /* deallocate cached blocks */
+
+ free_file_buffers(fp);
+
+ return 0;
+}
+
+int
+ext2_remove_file_direct(fdp)
+ struct file_direct *fdp;
+{
+ if (fdp->fd_blocks)
+ (void) vm_deallocate(mach_task_self(),
+ (vm_offset_t) fdp->fd_blocks,
+ (vm_size_t) (fdp->fd_size * sizeof(daddr_t)));
+ fdp->fd_blocks = 0; /* sanity */
+ /* xxx should lose a ref to fdp->fd_dev here (and elsewhere) xxx */
+}
diff --git a/serverboot/ffs_compat.c b/serverboot/ffs_compat.c
new file mode 100644
index 00000000..6e322b63
--- /dev/null
+++ b/serverboot/ffs_compat.c
@@ -0,0 +1,65 @@
+/*
+ * BSD FFS like functions used to ease porting bootstrap to Linux ext2 fs
+ * Copyright (C) 1994 Remy Card
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <device/device_types.h>
+#include <device/device.h>
+
+#include <mach/mach_traps.h>
+#include <mach/mach_interface.h>
+
+#include <file_io.h>
+
+#define EXT2_INODES_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s) / sizeof (struct ext2_inode))
+
+int ino2blk (struct ext2_super_block *fs, struct ext2_group_desc *gd, int ino)
+{
+ int group;
+ int blk;
+
+ group = (ino - 1) / EXT2_INODES_PER_GROUP(fs);
+ blk = gd[group].bg_inode_table +
+ (((ino - 1) % EXT2_INODES_PER_GROUP(fs)) /
+ EXT2_INODES_PER_BLOCK(fs));
+ return blk;
+}
+
+int fsbtodb (struct ext2_super_block *fs, int b)
+{
+ return (b * EXT2_BLOCK_SIZE(fs)) / DEV_BSIZE;
+}
+
+int itoo (struct ext2_super_block *fs, int ino)
+{
+ return (ino - 1) % EXT2_INODES_PER_BLOCK(fs);
+}
+
+int blkoff (struct ext2_super_block * fs, vm_offset_t offset)
+{
+ return offset % EXT2_BLOCK_SIZE(fs);
+}
+
+int lblkno (struct ext2_super_block * fs, vm_offset_t offset)
+{
+ return offset / EXT2_BLOCK_SIZE(fs);
+}
+
+int blksize (struct ext2_super_block *fs, struct file *fp, daddr_t file_block)
+{
+ return EXT2_BLOCK_SIZE(fs); /* XXX - fix for fragments */
+}
diff --git a/serverboot/ffs_compat.h b/serverboot/ffs_compat.h
new file mode 100644
index 00000000..d78840f5
--- /dev/null
+++ b/serverboot/ffs_compat.h
@@ -0,0 +1,54 @@
+/*
+ * BSD FFS like declarations used to ease porting bootstrap to Linux ext2 fs
+ * Copyright (C) 1994 Remy Card
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#define SBSIZE EXT2_MIN_BLOCK_SIZE /* Size of superblock */
+#define SBLOCK ((daddr_t) 2) /* Location of superblock */
+
+#define NDADDR EXT2_NDIR_BLOCKS
+#define NIADDR (EXT2_N_BLOCKS - EXT2_NDIR_BLOCKS)
+
+#define MAXNAMLEN 255
+
+#define ROOTINO EXT2_ROOT_INO
+
+#define NINDIR(fs) EXT2_ADDR_PER_BLOCK(fs)
+
+#define IC_FASTLINK
+
+#define IFMT 00170000
+#define IFSOCK 0140000
+#define IFLNK 0120000
+#define IFREG 0100000
+#define IFBLK 0060000
+#define IFDIR 0040000
+#define IFCHR 0020000
+#define IFIFO 0010000
+#define ISUID 0004000
+#define ISGID 0002000
+#define ISVTX 0001000
+
+#define f_fs u.ext2.ext2_fs
+#define f_gd u.ext2.ext2_gd
+#define f_gd_size u.ext2.ext2_gd_size
+#define i_ic u.ext2.ext2_ic
+#define f_nindir u.ext2.ext2_nindir
+#define f_blk u.ext2.ext2_blk
+#define f_blksize u.ext2.ext2_blksize
+#define f_blkno u.ext2.ext2_blkno
+
diff --git a/serverboot/ffs_file_io.c b/serverboot/ffs_file_io.c
new file mode 100644
index 00000000..1105eacc
--- /dev/null
+++ b/serverboot/ffs_file_io.c
@@ -0,0 +1,969 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * Stand-alone file reading package.
+ */
+
+#include <device/device_types.h>
+#include <device/device.h>
+
+#include <mach/mach_traps.h>
+#include <mach/mach_interface.h>
+
+#include "file_io.h"
+#include "fs.h"
+#include "dir.h"
+#include "disk_inode_ffs.h"
+
+void close_file(); /* forward */
+
+/*
+ * Free file buffers, but don't close file.
+ */
+static void
+free_file_buffers(fp)
+ register struct file *fp;
+{
+ register int level;
+
+ /*
+ * Free the indirect blocks
+ */
+ for (level = 0; level < NIADDR; level++) {
+ if (fp->f_blk[level] != 0) {
+ (void) vm_deallocate(mach_task_self(),
+ fp->f_blk[level],
+ fp->f_blksize[level]);
+ fp->f_blk[level] = 0;
+ }
+ fp->f_blkno[level] = -1;
+ }
+
+ /*
+ * Free the data block
+ */
+ if (fp->f_buf != 0) {
+ (void) vm_deallocate(mach_task_self(),
+ fp->f_buf,
+ fp->f_buf_size);
+ fp->f_buf = 0;
+ }
+ fp->f_buf_blkno = -1;
+}
+
+/*
+ * Read a new inode into a file structure.
+ */
+static int
+read_inode(inumber, fp)
+ ino_t inumber;
+ register struct file *fp;
+{
+ vm_offset_t buf;
+ mach_msg_type_number_t buf_size;
+ register struct fs *fs;
+ daddr_t disk_block;
+ kern_return_t rc;
+
+ fs = fp->f_fs;
+ disk_block = itod(fs, inumber);
+
+ rc = device_read(fp->f_dev,
+ 0,
+ (recnum_t) fsbtodb(fp->f_fs, disk_block),
+ (int) fs->fs_bsize,
+ (char **)&buf,
+ &buf_size);
+ if (rc != KERN_SUCCESS)
+ return (rc);
+
+ {
+ register struct dinode *dp;
+
+ dp = (struct dinode *)buf;
+ dp += itoo(fs, inumber);
+ fp->i_ic = dp->di_ic;
+ fp->f_size = fp->i_size;
+ }
+
+ (void) vm_deallocate(mach_task_self(), buf, buf_size);
+
+ /*
+ * Clear out the old buffers
+ */
+ free_file_buffers(fp);
+
+ return (0);
+}
+
+/*
+ * Given an offset in a file, find the disk block number that
+ * contains that block.
+ */
+static int
+block_map(fp, file_block, disk_block_p)
+ struct file *fp;
+ daddr_t file_block;
+ daddr_t *disk_block_p; /* out */
+{
+ int level;
+ int idx;
+ daddr_t ind_block_num;
+ kern_return_t rc;
+
+ vm_offset_t olddata[NIADDR+1];
+ vm_size_t oldsize[NIADDR+1];
+
+ /*
+ * Index structure of an inode:
+ *
+ * i_db[0..NDADDR-1] hold block numbers for blocks
+ * 0..NDADDR-1
+ *
+ * i_ib[0] index block 0 is the single indirect
+ * block
+ * holds block numbers for blocks
+ * NDADDR .. NDADDR + NINDIR(fs)-1
+ *
+ * i_ib[1] index block 1 is the double indirect
+ * block
+ * holds block numbers for INDEX blocks
+ * for blocks
+ * NDADDR + NINDIR(fs) ..
+ * NDADDR + NINDIR(fs) + NINDIR(fs)**2 - 1
+ *
+ * i_ib[2] index block 2 is the triple indirect
+ * block
+ * holds block numbers for double-indirect
+ * blocks for blocks
+ * NDADDR + NINDIR(fs) + NINDIR(fs)**2 ..
+ * NDADDR + NINDIR(fs) + NINDIR(fs)**2
+ * + NINDIR(fs)**3 - 1
+ */
+
+ mutex_lock(&fp->f_lock);
+
+ if (file_block < NDADDR) {
+ /* Direct block. */
+ *disk_block_p = fp->i_db[file_block];
+ mutex_unlock(&fp->f_lock);
+ return (0);
+ }
+
+ file_block -= NDADDR;
+
+ /*
+ * nindir[0] = NINDIR
+ * nindir[1] = NINDIR**2
+ * nindir[2] = NINDIR**3
+ * etc
+ */
+ for (level = 0; level < NIADDR; level++) {
+ if (file_block < fp->f_nindir[level])
+ break;
+ file_block -= fp->f_nindir[level];
+ }
+ if (level == NIADDR) {
+ /* Block number too high */
+ mutex_unlock(&fp->f_lock);
+ return (FS_NOT_IN_FILE);
+ }
+
+ ind_block_num = fp->i_ib[level];
+
+ /*
+ * Initialize array of blocks to free.
+ */
+ for (idx = 0; idx < NIADDR; idx++)
+ oldsize[idx] = 0;
+
+ for (; level >= 0; level--) {
+
+ vm_offset_t data;
+ mach_msg_type_number_t size;
+
+ if (ind_block_num == 0)
+ break;
+
+ if (fp->f_blkno[level] == ind_block_num) {
+ /*
+ * Cache hit. Just pick up the data.
+ */
+
+ data = fp->f_blk[level];
+ }
+ else {
+ /*
+ * Drop our lock while doing the read.
+ * (The f_dev and f_fs fields don`t change.)
+ */
+ mutex_unlock(&fp->f_lock);
+
+ rc = device_read(fp->f_dev,
+ 0,
+ (recnum_t) fsbtodb(fp->f_fs, ind_block_num),
+ fp->f_fs->fs_bsize,
+ (char **)&data,
+ &size);
+ if (rc != KERN_SUCCESS)
+ return (rc);
+
+ /*
+ * See if we can cache the data. Need a write lock to
+ * do this. While we hold the write lock, we can`t do
+ * *anything* which might block for memory. Otherwise
+ * a non-privileged thread might deadlock with the
+ * privileged threads. We can`t block while taking the
+ * write lock. Otherwise a non-privileged thread
+ * blocked in the vm_deallocate (while holding a read
+ * lock) will block a privileged thread. For the same
+ * reason, we can`t take a read lock and then use
+ * lock_read_to_write.
+ */
+
+ mutex_lock(&fp->f_lock);
+
+ olddata[level] = fp->f_blk[level];
+ oldsize[level] = fp->f_blksize[level];
+
+ fp->f_blkno[level] = ind_block_num;
+ fp->f_blk[level] = data;
+ fp->f_blksize[level] = size;
+
+ /*
+ * Return to holding a read lock, and
+ * dispose of old data.
+ */
+
+ }
+
+ if (level > 0) {
+ idx = file_block / fp->f_nindir[level-1];
+ file_block %= fp->f_nindir[level-1];
+ }
+ else
+ idx = file_block;
+
+ ind_block_num = ((daddr_t *)data)[idx];
+ }
+
+ mutex_unlock(&fp->f_lock);
+
+ /*
+ * After unlocking the file, free any blocks that
+ * we need to free.
+ */
+ for (idx = 0; idx < NIADDR; idx++)
+ if (oldsize[idx] != 0)
+ (void) vm_deallocate(mach_task_self(),
+ olddata[idx],
+ oldsize[idx]);
+
+ *disk_block_p = ind_block_num;
+ return (0);
+}
+
+/*
+ * Read a portion of a file into an internal buffer. Return
+ * the location in the buffer and the amount in the buffer.
+ */
+static int
+buf_read_file(fp, offset, buf_p, size_p)
+ register struct file *fp;
+ vm_offset_t offset;
+ vm_offset_t *buf_p; /* out */
+ vm_size_t *size_p; /* out */
+{
+ register struct fs *fs;
+ vm_offset_t off;
+ register daddr_t file_block;
+ daddr_t disk_block;
+ int rc;
+ vm_offset_t block_size;
+
+ if (offset >= fp->i_size)
+ return (FS_NOT_IN_FILE);
+
+ fs = fp->f_fs;
+
+ off = blkoff(fs, offset);
+ file_block = lblkno(fs, offset);
+ block_size = blksize(fs, fp, file_block);
+
+ if (file_block != fp->f_buf_blkno) {
+ rc = block_map(fp, file_block, &disk_block);
+ if (rc != 0)
+ return (rc);
+
+ if (fp->f_buf)
+ (void)vm_deallocate(mach_task_self(),
+ fp->f_buf,
+ fp->f_buf_size);
+
+ if (disk_block == 0) {
+ (void)vm_allocate(mach_task_self(),
+ &fp->f_buf,
+ block_size,
+ TRUE);
+ fp->f_buf_size = block_size;
+ }
+ else {
+ rc = device_read(fp->f_dev,
+ 0,
+ (recnum_t) fsbtodb(fs, disk_block),
+ (int) block_size,
+ (char **) &fp->f_buf,
+ (mach_msg_type_number_t *)&fp->f_buf_size);
+ }
+ if (rc)
+ return (rc);
+
+ fp->f_buf_blkno = file_block;
+ }
+
+ /*
+ * Return address of byte in buffer corresponding to
+ * offset, and size of remainder of buffer after that
+ * byte.
+ */
+ *buf_p = fp->f_buf + off;
+ *size_p = block_size - off;
+
+ /*
+ * But truncate buffer at end of file.
+ */
+ if (*size_p > fp->i_size - offset)
+ *size_p = fp->i_size - offset;
+
+ return (0);
+}
+
+/* In 4.4 d_reclen is split into d_type and d_namlen */
+struct dirent_44 {
+ unsigned long d_fileno;
+ unsigned short d_reclen;
+ unsigned char d_type;
+ unsigned char d_namlen;
+ char d_name[255 + 1];
+};
+
+/*
+ * Search a directory for a name and return its
+ * i_number.
+ */
+static int
+search_directory(name, fp, inumber_p)
+ char * name;
+ register struct file *fp;
+ ino_t *inumber_p; /* out */
+{
+ vm_offset_t buf;
+ vm_size_t buf_size;
+ vm_offset_t offset;
+ register struct dirent_44 *dp;
+ int length;
+ kern_return_t rc;
+
+ length = strlen(name);
+
+ offset = 0;
+ while (offset < fp->i_size) {
+ rc = buf_read_file(fp, offset, &buf, &buf_size);
+ if (rc != KERN_SUCCESS)
+ return (rc);
+
+ dp = (struct dirent_44 *)buf;
+ if (dp->d_ino != 0) {
+ unsigned short namlen = dp->d_namlen;
+ /*
+ * If namlen is zero, then either this is a 4.3 file
+ * system or the namlen is really zero. In the latter
+ * case also the 4.3 d_namlen field is zero
+ * interpreted either way.
+ */
+ if (namlen == 0)
+ namlen = ((struct direct *)dp)->d_namlen;
+
+ if (namlen == length &&
+ !strcmp(name, dp->d_name))
+ {
+ /* found entry */
+ *inumber_p = dp->d_ino;
+ return (0);
+ }
+ }
+ offset += dp->d_reclen;
+ }
+ return (FS_NO_ENTRY);
+}
+
+static int
+read_fs(dev, fsp)
+ mach_port_t dev;
+ struct fs **fsp;
+{
+ register struct fs *fs;
+ vm_offset_t buf;
+ mach_msg_type_number_t buf_size;
+ int error;
+
+ error = device_read(dev, 0, (recnum_t) SBLOCK, SBSIZE,
+ (char **) &buf, &buf_size);
+ if (error)
+ return (error);
+
+ fs = (struct fs *)buf;
+ if (fs->fs_magic != FS_MAGIC ||
+ fs->fs_bsize > MAXBSIZE ||
+ fs->fs_bsize < sizeof(struct fs)) {
+ (void) vm_deallocate(mach_task_self(), buf, buf_size);
+ return (FS_INVALID_FS);
+ }
+ /* don't read cylinder groups - we aren't modifying anything */
+
+ *fsp = fs;
+ return 0;
+}
+
+static int
+mount_fs(fp)
+ register struct file *fp;
+{
+ register struct fs *fs;
+ int error;
+
+ error = read_fs(fp->f_dev, &fp->f_fs);
+ if (error)
+ return (error);
+ fs = fp->f_fs;
+
+ /*
+ * Calculate indirect block levels.
+ */
+ {
+ register int mult;
+ register int level;
+
+ mult = 1;
+ for (level = 0; level < NIADDR; level++) {
+ mult *= NINDIR(fs);
+ fp->f_nindir[level] = mult;
+ }
+ }
+
+ return (0);
+}
+
+static void
+unmount_fs(fp)
+ register struct file *fp;
+{
+ if (file_is_structured(fp)) {
+ (void) vm_deallocate(mach_task_self(),
+ (vm_offset_t) fp->f_fs,
+ SBSIZE);
+ fp->f_fs = 0;
+ }
+}
+
+/*
+ * Open a file.
+ */
+int
+ffs_open_file(master_device_port, path, fp)
+ mach_port_t master_device_port;
+ char * path;
+ struct file *fp;
+{
+#define RETURN(code) { rc = (code); goto exit; }
+
+ register char *cp, *component;
+ register int c; /* char */
+ register int rc;
+ ino_t inumber, parent_inumber;
+ int nlinks = 0;
+
+ char namebuf[MAXPATHLEN+1];
+
+ if (path == 0 || *path == '\0') {
+ return FS_NO_ENTRY;
+ }
+
+ /*
+ * Copy name into buffer to allow modifying it.
+ */
+ strcpy(namebuf, path);
+
+ /*
+ * Look for '/dev/xxx' at start of path, for
+ * root device.
+ */
+ if (!strprefix(namebuf, "/dev/")) {
+ printf("no device name\n");
+ return FS_NO_ENTRY;
+ }
+
+ cp = namebuf + 5; /* device */
+ component = cp;
+ while ((c = *cp) != '\0' && c != '/') {
+ cp++;
+ }
+ *cp = '\0';
+
+ bzero (fp, sizeof (struct file));
+
+ rc = device_open(master_device_port,
+ D_READ|D_WRITE,
+ component,
+ &fp->f_dev);
+ if (rc)
+ return rc;
+
+ if (c == 0) {
+ fp->f_fs = 0;
+ goto out_ok;
+ }
+
+ *cp = c;
+
+ rc = mount_fs(fp);
+ if (rc)
+ return rc;
+
+ inumber = (ino_t) ROOTINO;
+ if ((rc = read_inode(inumber, fp)) != 0) {
+ printf("can't read root inode\n");
+ goto exit;
+ }
+
+ while (*cp) {
+
+ /*
+ * Check that current node is a directory.
+ */
+ if ((fp->i_mode & IFMT) != IFDIR)
+ RETURN (FS_NOT_DIRECTORY);
+
+ /*
+ * Remove extra separators
+ */
+ while (*cp == '/')
+ cp++;
+
+ /*
+ * Get next component of path name.
+ */
+ component = cp;
+ {
+ register int len = 0;
+
+ while ((c = *cp) != '\0' && c != '/') {
+ if (len++ > MAXNAMLEN)
+ RETURN (FS_NAME_TOO_LONG);
+ if (c & 0200)
+ RETURN (FS_INVALID_PARAMETER);
+ cp++;
+ }
+ *cp = 0;
+ }
+
+ /*
+ * Look up component in current directory.
+ * Save directory inumber in case we find a
+ * symbolic link.
+ */
+ parent_inumber = inumber;
+ rc = search_directory(component, fp, &inumber);
+ if (rc) {
+ printf("%s: not found\n", path);
+ goto exit;
+ }
+ *cp = c;
+
+ /*
+ * Open next component.
+ */
+ if ((rc = read_inode(inumber, fp)) != 0)
+ goto exit;
+
+ /*
+ * Check for symbolic link.
+ */
+ if ((fp->i_mode & IFMT) == IFLNK) {
+
+ int link_len = fp->i_size;
+ int len;
+
+ len = strlen(cp) + 1;
+
+ if (link_len + len >= MAXPATHLEN - 1)
+ RETURN (FS_NAME_TOO_LONG);
+
+ if (++nlinks > MAXSYMLINKS)
+ RETURN (FS_SYMLINK_LOOP);
+
+ memmove (&namebuf[link_len], cp, len);
+
+#ifdef IC_FASTLINK
+ if ((fp->i_flags & IC_FASTLINK) != 0) {
+ bcopy(fp->i_symlink, namebuf, (unsigned) link_len);
+ }
+ else
+#endif /* IC_FASTLINK */
+#if !defined(DISABLE_BSD44_FASTLINKS)
+ /*
+ * There is no bit for fastlinks in 4.4 but instead
+ * all symlinks that fit into the inode are fastlinks.
+ * If the second block (ic_db[1]) is zero the symlink
+ * can't be a fastlink if its length is at least five.
+ * For symlinks of length one to four there is no easy
+ * way of knowing whether we are looking at a 4.4
+ * fastlink or a 4.3 slowlink. This code always
+ * guesses the 4.4 way when in doubt. THIS BREAKS 4.3
+ * SLOWLINKS OF LENGTH FOUR OR LESS.
+ */
+ if ((link_len <= MAX_FASTLINK_SIZE && fp->i_ic.ic_db[1] != 0)
+ || (link_len <= 4))
+ {
+ bcopy(fp->i_symlink, namebuf, (unsigned) link_len);
+ }
+ else
+#endif /* !DISABLE_BSD44_FASTLINKS */
+
+ {
+ /*
+ * Read file for symbolic link
+ */
+ vm_offset_t buf;
+ mach_msg_type_number_t buf_size;
+ daddr_t disk_block;
+ register struct fs *fs = fp->f_fs;
+
+ (void) block_map(fp, (daddr_t)0, &disk_block);
+ rc = device_read(fp->f_dev,
+ 0,
+ (recnum_t) fsbtodb(fs, disk_block),
+ (int) blksize(fs, fp, 0),
+ (char **) &buf,
+ &buf_size);
+ if (rc)
+ goto exit;
+
+ bcopy((char *)buf, namebuf, (unsigned)link_len);
+ (void) vm_deallocate(mach_task_self(), buf, buf_size);
+ }
+
+ /*
+ * If relative pathname, restart at parent directory.
+ * If absolute pathname, restart at root.
+ * If pathname begins '/dev/<device>/',
+ * restart at root of that device.
+ */
+ cp = namebuf;
+ if (*cp != '/') {
+ inumber = parent_inumber;
+ }
+ else if (!strprefix(cp, "/dev/")) {
+ inumber = (ino_t)ROOTINO;
+ }
+ else {
+ cp += 5;
+ component = cp;
+ while ((c = *cp) != '\0' && c != '/') {
+ cp++;
+ }
+ *cp = '\0';
+
+ /*
+ * Unmount current file system and free buffers.
+ */
+ close_file(fp);
+
+ /*
+ * Open new root device.
+ */
+ rc = device_open(master_device_port,
+ D_READ,
+ component,
+ &fp->f_dev);
+ if (rc)
+ return (rc);
+
+ if (c == 0) {
+ fp->f_fs = 0;
+ goto out_ok;
+ }
+
+ *cp = c;
+
+ rc = mount_fs(fp);
+ if (rc)
+ return (rc);
+
+ inumber = (ino_t)ROOTINO;
+ }
+ if ((rc = read_inode(inumber, fp)) != 0)
+ goto exit;
+ }
+ }
+
+ /*
+ * Found terminal component.
+ */
+ out_ok:
+ mutex_init(&fp->f_lock);
+ return 0;
+
+ /*
+ * At error exit, close file to free storage.
+ */
+ exit:
+ close_file(fp);
+ return rc;
+}
+
+/*
+ * Close file - free all storage used.
+ */
+void
+ffs_close_file(fp)
+ register struct file *fp;
+{
+ register int i;
+
+ /*
+ * Free the disk super-block.
+ */
+ unmount_fs(fp);
+
+ /*
+ * Free the inode and data buffers.
+ */
+ free_file_buffers(fp);
+}
+
+int
+ffs_file_is_directory(struct file *fp)
+{
+ return (fp->i_mode & IFMT) == IFDIR;
+}
+
+int
+ffs_file_is_regular(struct file *fp)
+{
+ return (fp->i_mode & IFMT) == IFREG;
+}
+
+/*
+ * Copy a portion of a file into kernel memory.
+ * Cross block boundaries when necessary.
+ */
+int
+ffs_read_file(fp, offset, start, size, resid)
+ register struct file *fp;
+ vm_offset_t offset;
+ vm_offset_t start;
+ vm_size_t size;
+ vm_size_t *resid; /* out */
+{
+ int rc;
+ register vm_size_t csize;
+ vm_offset_t buf;
+ vm_size_t buf_size;
+
+ while (size != 0) {
+ rc = buf_read_file(fp, offset, &buf, &buf_size);
+ if (rc)
+ return (rc);
+
+ csize = size;
+ if (csize > buf_size)
+ csize = buf_size;
+ if (csize == 0)
+ break;
+
+ bcopy((char *)buf, (char *)start, csize);
+
+ offset += csize;
+ start += csize;
+ size -= csize;
+ }
+ if (resid)
+ *resid = size;
+
+ return (0);
+}
+
+/* simple utility: only works for 2^n */
+static int
+log2(n)
+ register unsigned int n;
+{
+ register int i = 0;
+
+ while ((n & 1) == 0) {
+ i++;
+ n >>= 1;
+ }
+ return i;
+}
+
+/*
+ * Make an empty file_direct for a device.
+ */
+int
+ffs_open_file_direct(dev, fdp, is_structured)
+ mach_port_t dev;
+ register struct file_direct *fdp;
+ boolean_t is_structured;
+{
+ struct fs *fs;
+ int rc;
+
+ if (!is_structured) {
+ fdp->fd_dev = dev;
+ fdp->fd_blocks = (daddr_t *) 0;
+ fdp->fd_bsize = vm_page_size;
+ fdp->fd_bshift = log2(vm_page_size);
+ fdp->fd_fsbtodb = 0; /* later */
+ fdp->fd_size = 0; /* later */
+ return 0;
+ }
+
+ rc = read_fs(dev, &fs);
+ if (rc)
+ return rc;
+
+ fdp->fd_dev = dev;
+ fdp->fd_blocks = (daddr_t *) 0;
+ fdp->fd_size = 0;
+ fdp->fd_bsize = fs->fs_bsize;
+ fdp->fd_bshift = fs->fs_bshift;
+ fdp->fd_fsbtodb = fs->fs_fsbtodb;
+
+ (void) vm_deallocate(mach_task_self(),
+ (vm_offset_t) fs,
+ SBSIZE);
+
+ return 0;
+}
+
+/*
+ * Add blocks from a file to a file_direct.
+ */
+int
+ffs_add_file_direct(fdp, fp)
+ register struct file_direct *fdp;
+ register struct file *fp;
+{
+ register struct fs *fs;
+ long num_blocks, i;
+ vm_offset_t buffer;
+ vm_size_t size;
+ int rc;
+
+ /* the file must be on the same device */
+
+ if (fdp->fd_dev != fp->f_dev)
+ return FS_INVALID_FS;
+
+ if (!file_is_structured(fp)) {
+ int result[DEV_GET_SIZE_COUNT];
+ natural_t count;
+
+ count = DEV_GET_SIZE_COUNT;
+ rc = device_get_status( fdp->fd_dev, DEV_GET_SIZE,
+ result, &count);
+ if (rc)
+ return rc;
+ fdp->fd_size = result[DEV_GET_SIZE_DEVICE_SIZE] >> fdp->fd_bshift;
+ fdp->fd_fsbtodb = log2(fdp->fd_bsize/result[DEV_GET_SIZE_RECORD_SIZE]);
+ return 0;
+ }
+
+ /* it must hold a file system */
+
+ fs = fp->f_fs;
+ if (fdp->fd_bsize != fs->fs_bsize ||
+ fdp->fd_fsbtodb != fs->fs_fsbtodb)
+ return FS_INVALID_FS;
+
+ /* calculate number of blocks in the file, ignoring fragments */
+
+ num_blocks = lblkno(fs, fp->i_size);
+
+ /* allocate memory for a bigger array */
+
+ size = (num_blocks + fdp->fd_size) * sizeof(daddr_t);
+ rc = vm_allocate(mach_task_self(), &buffer, size, TRUE);
+ if (rc != KERN_SUCCESS)
+ return rc;
+
+ /* lookup new block addresses */
+
+ for (i = 0; i < num_blocks; i++) {
+ daddr_t disk_block;
+
+ rc = block_map(fp, (daddr_t) i, &disk_block);
+ if (rc != 0) {
+ (void) vm_deallocate(mach_task_self(), buffer, size);
+ return rc;
+ }
+
+ ((daddr_t *) buffer)[fdp->fd_size + i] = disk_block;
+ }
+
+ /* copy old addresses and install the new array */
+
+ if (fdp->fd_blocks != 0) {
+ bcopy((char *) fdp->fd_blocks, (char *) buffer,
+ fdp->fd_size * sizeof(daddr_t));
+
+ (void) vm_deallocate(mach_task_self(),
+ (vm_offset_t) fdp->fd_blocks,
+ (vm_size_t) (fdp->fd_size * sizeof(daddr_t)));
+ }
+ fdp->fd_blocks = (daddr_t *) buffer;
+ fdp->fd_size += num_blocks;
+
+ /* deallocate cached blocks */
+
+ free_file_buffers(fp);
+
+ return 0;
+}
+
+int
+ffs_remove_file_direct(fdp)
+ struct file_direct *fdp;
+{
+ if (fdp->fd_blocks)
+ (void) vm_deallocate(mach_task_self(),
+ (vm_offset_t) fdp->fd_blocks,
+ (vm_size_t) (fdp->fd_size * sizeof(daddr_t)));
+ fdp->fd_blocks = 0; /* sanity */
+ /* xxx should lose a ref to fdp->fd_dev here (and elsewhere) xxx */
+}
diff --git a/serverboot/file_io.c b/serverboot/file_io.c
new file mode 100644
index 00000000..99966f95
--- /dev/null
+++ b/serverboot/file_io.c
@@ -0,0 +1,317 @@
+/*
+ * Copyright (c) 1994 The University of Utah and
+ * the Computer Systems Laboratory at the University of Utah (CSL).
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify and distribute this software is hereby
+ * granted provided that (1) source code retains these copyright, permission,
+ * and disclaimer notices, and (2) redistributions including binaries
+ * reproduce the notices in supporting documentation, and (3) all advertising
+ * materials mentioning features or use of this software display the following
+ * acknowledgement: ``This product includes software developed by the
+ * Computer Systems Laboratory at the University of Utah.''
+ *
+ * THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS
+ * IS" CONDITION. THE UNIVERSITY OF UTAH AND CSL DISCLAIM ANY LIABILITY OF
+ * ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * CSL requests users of this software to return to csl-dist@cs.utah.edu any
+ * improvements that they make and grant CSL redistribution rights.
+ *
+ * Author: Bryan Ford, University of Utah CSL
+ * MINIX FS patches: Csizmazia Balazs, University ELTE, Hungary
+ */
+/* This is just an icky kludgy "VFS layer" (harhar) for ffs and ext2 and minix. */
+
+#include "file_io.h"
+
+int
+open_file(master_device_port, path, fp)
+ mach_port_t master_device_port;
+ char * path;
+ struct file *fp;
+{
+ int rc;
+
+ if ((rc = ext2_open_file(master_device_port, path, fp))
+ != FS_INVALID_FS)
+ {
+ if (rc == 0)
+ fp->f_fstype = EXT2_FS;
+ return rc;
+ }
+ if ( (rc = minix_open_file(master_device_port, path, fp))
+ != FS_INVALID_FS )
+ {
+ if (rc == 0)
+ fp->f_fstype = MINIX_FS;
+ return rc;
+ }
+ fp->f_fstype = BSD_FFS;
+ return ffs_open_file(master_device_port, path, fp);
+}
+
+void
+close_file(fp)
+ register struct file *fp;
+{
+ switch (fp->f_fstype) {
+ case EXT2_FS:
+ ext2_close_file(fp);
+ return;
+ case MINIX_FS:
+ minix_close_file(fp);
+ return;
+ default:
+ ffs_close_file(fp);
+ return;
+ }
+}
+
+int
+read_file(fp, offset, start, size, resid)
+ register struct file *fp;
+ vm_offset_t offset;
+ vm_offset_t start;
+ vm_size_t size;
+ vm_size_t *resid; /* out */
+{
+ switch (fp->f_fstype) {
+ case EXT2_FS:
+ return ext2_read_file(fp, offset, start, size, resid);
+ case MINIX_FS:
+ return minix_read_file(fp, offset, start, size, resid);
+ default:
+ return ffs_read_file(fp, offset, start, size, resid);
+ }
+
+}
+
+int
+file_is_directory(struct file *f)
+{
+ switch (f->f_fstype) {
+ case EXT2_FS:
+ return ext2_file_is_directory(f);
+ case MINIX_FS:
+ return minix_file_is_directory(f);
+ default:
+ return ffs_file_is_directory(f);
+ }
+}
+
+int
+file_is_regular(struct file *f)
+{
+ switch (f->f_fstype) {
+ case EXT2_FS:
+ return ext2_file_is_regular(f);
+ case MINIX_FS:
+ return minix_file_is_regular(f);
+ default:
+ return ffs_file_is_regular(f);
+ }
+
+}
+
+int
+open_file_direct(dev, fdp, is_structured)
+ mach_port_t dev;
+ register struct file_direct *fdp;
+ boolean_t is_structured;
+{
+ int rc;
+
+
+ if ((rc = ext2_open_file_direct(dev, fdp, is_structured))
+ != FS_INVALID_FS)
+ {
+ if (rc == 0)
+ fdp->f_fstype = EXT2_FS;
+ return rc;
+ }
+ if ( (rc = minix_open_file_direct(dev, fdp, is_structured) )
+ != FS_INVALID_FS )
+ {
+ if (rc == 0)
+ fdp->f_fstype = MINIX_FS;
+ return rc;
+ }
+ fdp->f_fstype = BSD_FFS;
+ return ffs_open_file_direct(dev, fdp, is_structured);
+}
+
+int
+add_file_direct(fdp, fp)
+ register struct file_direct *fdp;
+ register struct file *fp;
+{
+ switch (fp->f_fstype) {
+ case EXT2_FS:
+ return ext2_add_file_direct(fdp, fp);
+ case MINIX_FS:
+ return minix_add_file_direct(fdp, fp);
+ default:
+ return ffs_add_file_direct(fdp, fp);
+ }
+}
+
+
+int
+remove_file_direct(fdp)
+ struct file_direct *fdp;
+{
+ switch (fdp->f_fstype) {
+ case EXT2_FS:
+ return ext2_remove_file_direct(fdp);
+ case MINIX_FS:
+ return minix_remove_file_direct(fdp);
+ default:
+ return ffs_remove_file_direct(fdp);
+ }
+}
+
+/*
+ * some other stuff, that was previously defined as macro
+ */
+
+int
+file_is_structured(fp)
+ register struct file *fp;
+{
+ switch (fp->f_fstype) {
+ case EXT2_FS:
+ return (fp)->u.ext2.ext2_fs != 0;
+ case MINIX_FS:
+ return (fp)->u.minix.minix_fs != 0;
+ default:
+ return (fp)->u.ffs.ffs_fs != 0;
+ }
+}
+
+/*
+ * Special read and write routines for default pager.
+ * Assume that all offsets and sizes are multiples
+ * of DEV_BSIZE.
+ */
+
+#define fdir_blkoff(fdp, offset) /* offset % fd_bsize */ \
+ ((offset) & ((fdp)->fd_bsize - 1))
+#define fdir_lblkno(fdp, offset) /* offset / fd_bsize */ \
+ ((offset) >> (fdp)->fd_bshift)
+
+#define fdir_fsbtodb(fdp, block) /* offset * fd_bsize / DEV_BSIZE */ \
+ ((block) << (fdp)->fd_fsbtodb)
+
+/*
+ * Read all or part of a data block, and
+ * return a pointer to the appropriate part.
+ * Caller must deallocate the block when done.
+ */
+int
+page_read_file_direct(fdp, offset, size, addr, size_read)
+ register struct file_direct *fdp;
+ vm_offset_t offset;
+ vm_size_t size;
+ vm_offset_t *addr; /* out */
+ mach_msg_type_number_t *size_read; /* out */
+{
+ vm_offset_t off;
+ register daddr_t file_block;
+ daddr_t disk_block;
+
+ if (offset % DEV_BSIZE != 0 ||
+ size % DEV_BSIZE != 0)
+ panic("page_read_file_direct");
+
+ if (offset >= (fdp->fd_size << fdp->fd_bshift))
+ return (FS_NOT_IN_FILE);
+
+ off = fdir_blkoff(fdp, offset);
+ file_block = fdir_lblkno(fdp, offset);
+
+ if (file_is_device(fdp)) {
+ disk_block = file_block;
+ } else {
+ disk_block = fdp->fd_blocks[file_block];
+ if (disk_block == 0)
+ return (FS_NOT_IN_FILE);
+
+ if (size > fdp->fd_bsize) {
+ /* Read only as much as is contiguous on disk. */
+ daddr_t b = file_block + 1;
+ while (b < file_block + fdp->fd_size &&
+ fdp->fd_blocks[b] == disk_block + fdir_fsbtodb(fdp, 1))
+ ++b;
+ size = (b - file_block) * fdp->fd_bsize;
+ }
+ }
+
+ return (device_read(fdp->fd_dev,
+ 0,
+ (recnum_t) (fdir_fsbtodb(fdp, disk_block) + btodb(off)),
+ (int) size,
+ (char **) addr,
+ size_read));
+}
+
+/*
+ * Write all or part of a data block, and
+ * return the amount written.
+ */
+int
+page_write_file_direct(fdp, offset, addr, size, size_written)
+ register struct file_direct *fdp;
+ vm_offset_t offset;
+ vm_offset_t addr;
+ vm_size_t size;
+ vm_offset_t *size_written; /* out */
+{
+ vm_offset_t off;
+ register daddr_t file_block;
+ daddr_t disk_block;
+ int rc, num_written;
+ vm_offset_t block_size;
+
+ if (offset % DEV_BSIZE != 0 ||
+ size % DEV_BSIZE != 0)
+ panic("page_write_file");
+
+ if (offset >= (fdp->fd_size << fdp->fd_bshift))
+ return (FS_NOT_IN_FILE);
+
+ off = fdir_blkoff(fdp, offset);
+ file_block = fdir_lblkno(fdp, offset);
+
+ if (file_is_device(fdp)) {
+ disk_block = file_block;
+ } else {
+ disk_block = fdp->fd_blocks[file_block];
+ if (disk_block == 0)
+ return (FS_NOT_IN_FILE);
+
+ if (size > fdp->fd_bsize) {
+ /* Write only as much as is contiguous on disk. */
+ daddr_t b = file_block + 1;
+ while (b < file_block + fdp->fd_size &&
+ fdp->fd_blocks[b] == disk_block + fdir_fsbtodb(fdp, 1))
+ ++b;
+ size = (b - file_block) * fdp->fd_bsize;
+ }
+ }
+
+ /*
+ * Write the data. Wait for completion to keep
+ * reads from getting ahead of writes and reading
+ * stale data.
+ */
+ rc = device_write(
+ fdp->fd_dev,
+ 0,
+ (recnum_t) (fdir_fsbtodb(fdp, disk_block) + btodb(off)),
+ (char *) addr,
+ size,
+ &num_written);
+ *size_written = num_written;
+ return rc;
+}
diff --git a/serverboot/file_io.h b/serverboot/file_io.h
new file mode 100644
index 00000000..323e4e9a
--- /dev/null
+++ b/serverboot/file_io.h
@@ -0,0 +1,200 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990,1989,1988 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+
+#ifndef _FILE_IO_H_
+#define _FILE_IO_H_
+
+/*
+ * Read-only file IO.
+ */
+
+#include <mach.h>
+#include <cthreads.h>
+
+#include <stdint.h>
+#include <device/device_types.h>
+
+/* Types used by the ext2 header files. */
+typedef u_int32_t __u32;
+typedef int32_t __s32;
+typedef u_int16_t __u16;
+typedef int16_t __s16;
+typedef u_int8_t __u8;
+typedef int8_t __s8;
+
+#include <defs.h>
+#include "minix_fs.h"
+#include "../ext2fs/ext2_fs.h" /* snarf stolen linux header from ext2fs */
+#include "disk_inode.h"
+
+#define BSD_FFS 0
+#define EXT2_FS 1
+#define MINIX_FS 2
+
+#define EXT2_NIADDR (EXT2_N_BLOCKS - EXT2_NDIR_BLOCKS)
+
+/*
+ * In-core open file.
+ */
+struct file {
+ struct mutex f_lock; /* lock */
+ mach_port_t f_dev; /* port to device */
+ vm_offset_t f_buf; /* buffer for data block */
+ vm_size_t f_buf_size; /* size of data block */
+ daddr_t f_buf_blkno; /* block number of data block */
+ vm_size_t f_size; /* size in bytes of the file */
+
+ int f_fstype; /* contains fs-id */
+
+ union {
+ struct {
+ struct fs * ffs_fs; /* pointer to super-block */
+ struct icommon ffs_ic; /* copy of on-disk inode */
+
+ /* number of blocks mapped by
+ indirect block at level i */
+ int ffs_nindir[FFS_NIADDR+1];
+
+ /* buffer for indirect block at level i */
+ vm_offset_t ffs_blk[FFS_NIADDR];
+
+ /* size of buffer */
+ vm_size_t ffs_blksize[FFS_NIADDR];
+
+ /* disk address of block in buffer */
+ daddr_t ffs_blkno[FFS_NIADDR];
+ } ffs;
+ struct {
+ /* pointer to super-block */
+ struct ext2_super_block*ext2_fs;
+
+ /* pointer to group descriptors */
+ struct ext2_group_desc* ext2_gd;
+
+ /* size of group descriptors */
+ vm_size_t ext2_gd_size;
+
+ /* copy of on-disk inode */
+ struct ext2_inode ext2_ic;
+
+ /* number of blocks mapped by
+ indirect block at level i */
+ int ext2_nindir[EXT2_NIADDR+1];
+
+ /* buffer for indirect block at level i */
+ vm_offset_t ext2_blk[EXT2_NIADDR];
+
+ /* size of buffer */
+ vm_size_t ext2_blksize[EXT2_NIADDR];
+
+ /* disk address of block in buffer */
+ daddr_t ext2_blkno[EXT2_NIADDR];
+ } ext2;
+ struct {
+ /* pointer to super-block */
+ struct minix_super_block* minix_fs;
+
+ /* copy of on-disk inode */
+ struct minix_inode minix_ic;
+
+ /* number of blocks mapped by
+ indirect block at level i */
+ int minix_nindir[MINIX_NIADDR+1];
+
+ /* buffer for indirect block at level i */
+ vm_offset_t minix_blk[MINIX_NIADDR];
+
+ /* size of buffer */
+ vm_size_t minix_blksize[MINIX_NIADDR];
+
+ /* disk address of block in buffer */
+ minix_daddr_t minix_blkno[MINIX_NIADDR];
+ } minix;
+ } u;
+};
+
+/*
+ * In-core open file, with in-core block map.
+ */
+struct file_direct {
+ int f_fstype; /* XXX was: true if ext2, false if ffs */
+
+ mach_port_t fd_dev; /* port to device */
+ daddr_t * fd_blocks; /* array of disk block addresses */
+ long fd_size; /* number of blocks in the array */
+ long fd_bsize; /* disk block size */
+ long fd_bshift; /* log2(fd_bsize) */
+ long fd_fsbtodb; /* log2(fd_bsize / disk sector size) */
+};
+
+#define file_is_device(_fd_) ((_fd_)->fd_blocks == 0)
+
+/*
+ * Exported routines.
+ */
+
+extern int open_file();
+extern void close_file();
+extern int read_file();
+
+extern int open_file_direct();
+extern int add_file_direct();
+extern int remove_file_direct();
+extern int file_wire_direct();
+extern int page_read_file_direct();
+extern int page_write_file_direct();
+
+/*
+ * Error codes for file system errors.
+ */
+
+#include <errno.h>
+
+/* Just use the damn Hurd error numbers. This is old CMU/Utah code from
+ the days of personality-independent Mach where it made sense for this to
+ be a standalone universe. In the Hurd, we compile serverboot against
+ the regular C library anyway. */
+
+#define FS_NOT_DIRECTORY ENOTDIR
+#define FS_NO_ENTRY ENOENT
+#define FS_NAME_TOO_LONG ENAMETOOLONG
+#define FS_SYMLINK_LOOP ELOOP
+#define FS_INVALID_FS EFTYPE /* ? */
+#define FS_NOT_IN_FILE EINVAL
+#define FS_INVALID_PARAMETER EINVAL
+
+#if 0
+#define FS_NOT_DIRECTORY 5000 /* not a directory */
+#define FS_NO_ENTRY 5001 /* name not found */
+#define FS_NAME_TOO_LONG 5002 /* name too long */
+#define FS_SYMLINK_LOOP 5003 /* symbolic link loop */
+#define FS_INVALID_FS 5004 /* bad file system */
+#define FS_NOT_IN_FILE 5005 /* offset not in file */
+#define FS_INVALID_PARAMETER 5006 /* bad parameter to routine */
+#endif
+
+
+#endif /* _FILE_IO_H_ */
diff --git a/serverboot/fs.h b/serverboot/fs.h
new file mode 100644
index 00000000..5809ed93
--- /dev/null
+++ b/serverboot/fs.h
@@ -0,0 +1,455 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * Copyright (c) 1982, 1986 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * @(#)fs.h 7.7 (Berkeley) 5/9/89
+ */
+
+/*
+ * 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.
+ *
+ * The first boot and super blocks are given in absolute disk addresses.
+ * The byte-offset forms are preferred, as they don't imply a sector size.
+ */
+#define BBSIZE 8192
+#define SBSIZE 8192
+#define BBOFF ((off_t)(0))
+#define SBOFF ((off_t)(BBOFF + BBSIZE))
+#define BBLOCK ((daddr_t)(0))
+#define SBLOCK ((daddr_t)(BBLOCK + BBSIZE / DEV_BSIZE))
+
+/*
+ * 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
+ * addressible; 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.
+ *
+ * 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) /* i number of all roots */
+
+/*
+ * 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.
+ * The limit on the amount of summary information per file system
+ * is defined by MAXCSBUFS. It is currently parameterized for a
+ * maximum of two million cylinders.
+ */
+#define MAXMNTLEN 512
+#define MAXCSBUFS 32
+
+/*
+ * 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.
+ *
+ * N.B. sizeof(struct csum) must be a power of two in order for
+ * the ``fs_cs'' macro to work (see below).
+ */
+struct csum {
+ int cs_ndir; /* number of directories */
+ int cs_nbfree; /* number of free blocks */
+ int cs_nifree; /* number of free inodes */
+ int cs_nffree; /* number of free frags */
+};
+
+/*
+ * Super block for a file system.
+ */
+#define FS_MAGIC 0x011954
+struct fs
+{
+ int xxx1; /* struct fs *fs_link;*/
+ int xxx2; /* struct fs *fs_rlink;*/
+ daddr_t fs_sblkno; /* addr of super-block in filesys */
+ daddr_t fs_cblkno; /* offset of cyl-block in filesys */
+ daddr_t fs_iblkno; /* offset of inode-blocks in filesys */
+ daddr_t fs_dblkno; /* offset of first data after cg */
+ int fs_cgoffset; /* cylinder group offset in cylinder */
+ int fs_cgmask; /* used to calc mod fs_ntrak */
+ time_t fs_time; /* last time written */
+ int fs_size; /* number of blocks in fs */
+ int fs_dsize; /* number of data blocks in fs */
+ int fs_ncg; /* number of cylinder groups */
+ int fs_bsize; /* size of basic blocks in fs */
+ int fs_fsize; /* size of frag blocks in fs */
+ int fs_frag; /* number of frags in a block in fs */
+/* these are configuration parameters */
+ int fs_minfree; /* minimum percentage of free blocks */
+ int fs_rotdelay; /* num of ms for optimal next block */
+ int fs_rps; /* disk revolutions per second */
+/* these fields can be computed from the others */
+ int fs_bmask; /* ``blkoff'' calc of blk offsets */
+ int fs_fmask; /* ``fragoff'' calc of frag offsets */
+ int fs_bshift; /* ``lblkno'' calc of logical blkno */
+ int fs_fshift; /* ``numfrags'' calc number of frags */
+/* these are configuration parameters */
+ int fs_maxcontig; /* max number of contiguous blks */
+ int fs_maxbpg; /* max number of blks per cyl group */
+/* these fields can be computed from the others */
+ int fs_fragshift; /* block to frag shift */
+ int fs_fsbtodb; /* fsbtodb and dbtofsb shift constant */
+ int fs_sbsize; /* actual size of super block */
+ int fs_csmask; /* csum block offset */
+ int fs_csshift; /* csum block number */
+ int fs_nindir; /* value of NINDIR */
+ int fs_inopb; /* value of INOPB */
+ int fs_nspf; /* value of NSPF */
+/* yet another configuration parameter */
+ int fs_optim; /* optimization preference, see below */
+/* these fields are derived from the hardware */
+ int fs_npsect; /* # sectors/track including spares */
+ int fs_interleave; /* hardware sector interleave */
+ int fs_trackskew; /* sector 0 skew, per track */
+ int fs_headswitch; /* head switch time, usec */
+ int fs_trkseek; /* track-to-track seek, usec */
+/* sizes determined by number of cylinder groups and their sizes */
+ daddr_t fs_csaddr; /* blk addr of cyl grp summary area */
+ int fs_cssize; /* size of cyl grp summary area */
+ int fs_cgsize; /* cylinder group size */
+/* these fields are derived from the hardware */
+ int fs_ntrak; /* tracks per cylinder */
+ int fs_nsect; /* sectors per track */
+ int fs_spc; /* sectors per cylinder */
+/* this comes from the disk driver partitioning */
+ int fs_ncyl; /* cylinders in file system */
+/* these fields can be computed from the others */
+ int fs_cpg; /* cylinders per group */
+ int fs_ipg; /* inodes per group */
+ int fs_fpg; /* blocks per group * fs_frag */
+/* this data must be re-computed after crashes */
+ struct csum fs_cstotal; /* cylinder summary information */
+/* these fields are cleared at mount time */
+ char fs_fmod; /* super block modified flag */
+ char fs_clean; /* file system is clean flag */
+ char fs_ronly; /* mounted read-only flag */
+ char fs_flags; /* currently unused flag */
+ char fs_fsmnt[MAXMNTLEN]; /* name mounted on */
+/* these fields retain the current block allocation info */
+ int fs_cgrotor; /* last cg searched */
+#if 1
+ int was_fs_csp[MAXCSBUFS];
+#else
+ struct csum *fs_csp[MAXCSBUFS];/* list of fs_cs info buffers */
+#endif
+ int fs_cpc; /* cyl per cycle in postbl */
+ short fs_opostbl[16][8]; /* old rotation block list head */
+ long fs_sparecon[50]; /* reserved for future constants */
+ long fs_contigsumsize; /* size of cluster summary array */
+ long fs_maxsymlinklen; /* max length of an internal symlink */
+ long fs_inodefmt; /* format of on-disk inodes */
+ quad fs_maxfilesize; /* maximum representable file size */
+ quad fs_qbmask; /* ~fs_bmask - for use with quad size */
+ quad fs_qfmask; /* ~fs_fmask - for use with quad size */
+ long fs_state; /* validate fs_clean field */
+ int fs_postblformat; /* format of positional layout tables */
+ int fs_nrpos; /* number of rotaional positions */
+ int fs_postbloff; /* (short) rotation block list head */
+ int fs_rotbloff; /* (u_char) blocks for each rotation */
+ int fs_magic; /* magic number */
+ u_char fs_space[1]; /* list of blocks for each rotation */
+/* actually longer */
+};
+/*
+ * Preference for optimization.
+ */
+#define FS_OPTTIME 0 /* minimize allocation time */
+#define FS_OPTSPACE 1 /* minimize disk fragmentation */
+
+/*
+ * Rotational layout table format types
+ */
+#define FS_42POSTBLFMT -1 /* 4.2BSD rotational table format */
+#define FS_DYNAMICPOSTBLFMT 1 /* dynamic rotational table format */
+/*
+ * Macros for access to superblock array structures
+ */
+#define fs_postbl(fs, cylno) \
+ (((fs)->fs_postblformat == FS_42POSTBLFMT) \
+ ? ((fs)->fs_opostbl[cylno]) \
+ : ((short *)((char *)(fs) + (fs)->fs_postbloff) + (cylno) * (fs)->fs_nrpos))
+#define fs_rotbl(fs) \
+ (((fs)->fs_postblformat == FS_42POSTBLFMT) \
+ ? ((fs)->fs_space) \
+ : ((u_char *)((char *)(fs) + (fs)->fs_rotbloff)))
+
+/*
+ * Convert cylinder group to base address of its global summary info.
+ *
+ * N.B. This macro assumes that sizeof(struct csum) is a power of two.
+ */
+#define fs_cs(fs, indx) \
+ fs_csp[(indx) >> (fs)->fs_csshift][(indx) & ~(fs)->fs_csmask]
+
+/*
+ * Cylinder group block for a file system.
+ */
+#define CG_MAGIC 0x090255
+struct cg {
+ int xxx1; /* struct cg *cg_link;*/
+ int cg_magic; /* magic number */
+ time_t cg_time; /* time last written */
+ int cg_cgx; /* we are the cgx'th cylinder group */
+ short cg_ncyl; /* number of cyl's this cg */
+ short cg_niblk; /* number of inode blocks this cg */
+ int cg_ndblk; /* number of data blocks this cg */
+ struct csum cg_cs; /* cylinder summary information */
+ int cg_rotor; /* position of last used block */
+ int cg_frotor; /* position of last used frag */
+ int cg_irotor; /* position of last used inode */
+ int cg_frsum[MAXFRAG]; /* counts of available frags */
+ int cg_btotoff; /* (long) block totals per cylinder */
+ int cg_boff; /* (short) free block positions */
+ int cg_iusedoff; /* (char) used inode map */
+ int cg_freeoff; /* (u_char) free block map */
+ int cg_nextfreeoff; /* (u_char) next available space */
+ int cg_sparecon[16]; /* reserved for future use */
+ u_char cg_space[1]; /* space for cylinder group maps */
+/* actually longer */
+};
+/*
+ * Macros for access to cylinder group array structures
+ */
+#define cg_blktot(cgp) \
+ (((cgp)->cg_magic != CG_MAGIC) \
+ ? (((struct ocg *)(cgp))->cg_btot) \
+ : ((int *)((char *)(cgp) + (cgp)->cg_btotoff)))
+#define cg_blks(fs, cgp, cylno) \
+ (((cgp)->cg_magic != CG_MAGIC) \
+ ? (((struct ocg *)(cgp))->cg_b[cylno]) \
+ : ((short *)((char *)(cgp) + (cgp)->cg_boff) + (cylno) * (fs)->fs_nrpos))
+#define cg_inosused(cgp) \
+ (((cgp)->cg_magic != CG_MAGIC) \
+ ? (((struct ocg *)(cgp))->cg_iused) \
+ : ((char *)((char *)(cgp) + (cgp)->cg_iusedoff)))
+#define cg_blksfree(cgp) \
+ (((cgp)->cg_magic != CG_MAGIC) \
+ ? (((struct ocg *)(cgp))->cg_free) \
+ : ((u_char *)((char *)(cgp) + (cgp)->cg_freeoff)))
+#define cg_chkmagic(cgp) \
+ ((cgp)->cg_magic == CG_MAGIC || ((struct ocg *)(cgp))->cg_magic == CG_MAGIC)
+
+/*
+ * The following structure is defined
+ * for compatibility with old file systems.
+ */
+struct ocg {
+ int xxx1; /* struct ocg *cg_link;*/
+ int xxx2; /* struct ocg *cg_rlink;*/
+ time_t cg_time; /* time last written */
+ int cg_cgx; /* we are the cgx'th cylinder group */
+ short cg_ncyl; /* number of cyl's this cg */
+ short cg_niblk; /* number of inode blocks this cg */
+ int cg_ndblk; /* number of data blocks this cg */
+ struct csum cg_cs; /* cylinder summary information */
+ int cg_rotor; /* position of last used block */
+ int cg_frotor; /* position of last used frag */
+ int cg_irotor; /* position of last used inode */
+ int cg_frsum[8]; /* counts of available frags */
+ int cg_btot[32]; /* block totals per cylinder */
+ short cg_b[32][8]; /* positions of free blocks */
+ char cg_iused[256]; /* used inode map */
+ int cg_magic; /* magic number */
+ u_char cg_free[1]; /* free block map */
+/* actually longer */
+};
+
+/*
+ * 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(fs, c) \
+ (cgbase(fs, c) + (fs)->fs_cgoffset * ((c) & ~((fs)->fs_cgmask)))
+#define cgsblock(fs, c) (cgstart(fs, c) + (fs)->fs_sblkno) /* super blk */
+#define cgtod(fs, c) (cgstart(fs, c) + (fs)->fs_cblkno) /* cg block */
+#define cgimin(fs, c) (cgstart(fs, c) + (fs)->fs_iblkno) /* inode blk */
+#define cgdmin(fs, c) (cgstart(fs, c) + (fs)->fs_dblkno) /* 1st data */
+
+/*
+ * 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 itoo(fs, x) ((x) % INOPB(fs))
+#define itog(fs, x) ((x) / (fs)->fs_ipg)
+#define itod(fs, x) \
+ ((daddr_t)(cgimin(fs, itog(fs, x)) + \
+ (blkstofrags((fs), (((x) % (fs)->fs_ipg) / 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 cbtocylno(fs, bno) \
+ ((bno) * NSPF(fs) / (fs)->fs_spc)
+#define cbtorpos(fs, bno) \
+ (((bno) * NSPF(fs) % (fs)->fs_spc / (fs)->fs_nsect * (fs)->fs_trackskew + \
+ (bno) * NSPF(fs) % (fs)->fs_spc % (fs)->fs_nsect * (fs)->fs_interleave) % \
+ (fs)->fs_nsect * (fs)->fs_nrpos / (fs)->fs_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_bmask)
+#define fragoff(fs, loc) /* calculates (loc % fs->fs_fsize) */ \
+ ((loc) & ~(fs)->fs_fmask)
+#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_bsize - 1) & (fs)->fs_bmask)
+#define fragroundup(fs, size) /* calculates roundup(size, fs->fs_fsize) */ \
+ (((size) + (fs)->fs_fsize - 1) & (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 - ((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 >= ((lbn) + 1) << (fs)->fs_bshift) \
+ ? (fs)->fs_bsize \
+ : (fragroundup(fs, blkoff(fs, (ip)->i_size))))
+#define dblksize(fs, dip, lbn) \
+ (((lbn) >= NDADDR || (dip)->di_size >= ((lbn) + 1) << (fs)->fs_bshift) \
+ ? (fs)->fs_bsize \
+ : (fragroundup(fs, blkoff(fs, (dip)->di_size))))
+
+/*
+ * Number of disk sectors per block; assumes DEV_BSIZE byte sector size.
+ */
+#define NSPB(fs) ((fs)->fs_nspf << (fs)->fs_fragshift)
+#define NSPF(fs) ((fs)->fs_nspf)
+
+/*
+ * INOPB is the number of inodes in a secondary storage block.
+ */
+#define INOPB(fs) ((fs)->fs_inopb)
+#define INOPF(fs) ((fs)->fs_inopb >> (fs)->fs_fragshift)
+
+/*
+ * NINDIR is the number of indirects in a file system block.
+ */
+#define NINDIR(fs) ((fs)->fs_nindir)
+
diff --git a/serverboot/gets.c b/serverboot/gets.c
new file mode 100644
index 00000000..61d14460
--- /dev/null
+++ b/serverboot/gets.c
@@ -0,0 +1,90 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1993-1989 Carnegie Mellon University.
+ * Copyright (c) 1994 The University of Utah and
+ * the Computer Systems Laboratory (CSL).
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON, THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF
+ * THIS SOFTWARE IN ITS "AS IS" CONDITION, AND DISCLAIM ANY LIABILITY
+ * OF ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF
+ * THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+
+#include <mach.h>
+#include <device/device.h>
+#include <varargs.h>
+
+extern mach_port_t __libmach_console_port;
+
+safe_gets(str, maxlen)
+ char *str;
+ int maxlen;
+{
+ register char *lp;
+ register int c;
+
+ char inbuf[IO_INBAND_MAX];
+ mach_msg_type_number_t count;
+ register char *ip;
+ char *strmax = str + maxlen - 1; /* allow space for trailing 0 */
+
+ lp = str;
+ for (;;) {
+ count = IO_INBAND_MAX;
+ (void) device_read_inband(__libmach_console_port,
+ (dev_mode_t)0, (recnum_t)0,
+ sizeof(inbuf), inbuf, &count);
+ for (ip = inbuf; ip < &inbuf[count]; ip++) {
+ c = *ip;
+ switch (c) {
+ case '\n':
+ case '\r':
+ printf("\n");
+ *lp++ = 0;
+ return;
+
+ case '\b':
+ case '#':
+ case '\177':
+ if (lp > str) {
+ printf("\b \b");
+ lp--;
+ }
+ continue;
+ case '@':
+ case 'u'&037:
+ lp = str;
+ printf("\n\r");
+ continue;
+ default:
+ if (c >= ' ' && c < '\177') {
+ if (lp < strmax) {
+ *lp++ = c;
+ printf("%c", c);
+ }
+ else {
+ printf("%c", '\007'); /* beep */
+ }
+ }
+ }
+ }
+ }
+}
+
diff --git a/serverboot/gunzip.c b/serverboot/gunzip.c
new file mode 100644
index 00000000..f74da111
--- /dev/null
+++ b/serverboot/gunzip.c
@@ -0,0 +1,188 @@
+/* Modified by okuji@kuicr.kyoto-u.ac.jp for use in serverboot. */
+/* Decompressing store backend
+
+ Copyright (C) 1997 Free Software Foundation, Inc.
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <stdio.h>
+#include <string.h>
+#include <setjmp.h>
+#include <cthreads.h>
+#include <errno.h>
+
+#include <file_io.h>
+
+/* gzip.h makes several annoying defines & decls, which we have to work
+ around. */
+#define file_t gzip_file_t
+#include "gzip.h"
+#undef file_t
+#undef head
+
+#define IN_BUFFERING (256*1024)
+#define OUT_BUFFERING (512*1024)
+
+static struct mutex unzip_lock = MUTEX_INITIALIZER;
+
+/* Uncompress the contents of FROM, which should contain a valid gzip file,
+ into memory, returning the result buffer in BUF & BUF_LEN. */
+int
+serverboot_gunzip (struct file *from, void **buf, size_t *buf_len)
+{
+ /* Entry points to unzip engine. */
+ int get_method (int);
+ extern long int bytes_out;
+ /* Callbacks from unzip for I/O and error interface. */
+ extern int (*unzip_read) (char *buf, size_t maxread);
+ extern void (*unzip_write) (const char *buf, size_t nwrite);
+ extern void (*unzip_read_error) (void);
+ extern void (*unzip_error) (const char *msg);
+
+ /* How we return errors from our hook functions. */
+ jmp_buf zerr_jmp_buf;
+ int zerr;
+
+ size_t offset = 0; /* Offset of read point in FROM. */
+
+ /* Read at most MAXREAD (or 0 if eof) bytes into BUF from our current
+ position in FROM. */
+ int zread (char *buf, size_t maxread)
+ {
+ vm_size_t resid;
+ size_t did_read;
+
+ if (from->f_size - offset < maxread)
+ did_read = from->f_size - offset;
+ else
+ did_read = maxread;
+
+ zerr = read_file (from, offset, buf, did_read, &resid);
+ if (zerr)
+ longjmp (zerr_jmp_buf, 1);
+
+ did_read -= resid;
+ offset += did_read;
+
+ return did_read;
+ }
+
+ size_t out_buf_offs = 0; /* Position in the output buffer. */
+
+ /* Write uncompress data to our output buffer. */
+ void zwrite (const char *wbuf, size_t nwrite)
+ {
+ size_t old_buf_len = *buf_len;
+
+ if (out_buf_offs + nwrite > old_buf_len)
+ /* Have to grow the output buffer. */
+ {
+ void *old_buf = *buf;
+ void *new_buf = old_buf + old_buf_len; /* First try. */
+ size_t new_buf_len = round_page (old_buf_len + old_buf_len + nwrite);
+
+ /* Try to grow the buffer. */
+ zerr =
+ vm_allocate (mach_task_self (),
+ (vm_address_t *)&new_buf, new_buf_len - old_buf_len,
+ 0);
+ if (zerr)
+ /* Can't do that, try to make a bigger buffer elsewhere. */
+ {
+ new_buf = old_buf;
+ zerr =
+ vm_allocate (mach_task_self (),
+ (vm_address_t *)&new_buf, new_buf_len, 1);
+ if (zerr)
+ longjmp (zerr_jmp_buf, 1);
+
+ if (out_buf_offs > 0)
+ /* Copy the old buffer into the start of the new & free it. */
+ bcopy (old_buf, new_buf, out_buf_offs);
+
+ vm_deallocate (mach_task_self (),
+ (vm_address_t)old_buf, old_buf_len);
+
+ *buf = new_buf;
+ }
+
+ *buf_len = new_buf_len;
+ }
+
+ bcopy (wbuf, *buf + out_buf_offs, nwrite);
+ out_buf_offs += nwrite;
+ }
+
+ void zreaderr (void)
+ {
+ zerr = EIO;
+ longjmp (zerr_jmp_buf, 1);
+ }
+ void zerror (const char *msg)
+ {
+ zerr = EINVAL;
+ longjmp (zerr_jmp_buf, 2);
+ }
+
+ /* Try to guess a reasonable output buffer size. */
+ *buf_len = round_page (from->f_size * 2);
+ zerr = vm_allocate (mach_task_self (), (vm_address_t *)buf, *buf_len, 1);
+ if (zerr)
+ return zerr;
+
+ mutex_lock (&unzip_lock);
+
+ unzip_read = zread;
+ unzip_write = zwrite;
+ unzip_read_error = zreaderr;
+ unzip_error = zerror;
+
+ if (! setjmp (zerr_jmp_buf))
+ {
+ if (get_method (0) != 0)
+ /* Not a happy gzip file. */
+ zerr = EINVAL;
+ else
+ /* Matched gzip magic number. Ready to unzip.
+ Set up the output stream and let 'er rip. */
+ {
+ /* Call the gunzip engine. */
+ bytes_out = 0;
+ unzip (17, 23); /* Arguments ignored. */
+ zerr = 0;
+ }
+ }
+
+ mutex_unlock (&unzip_lock);
+
+ if (zerr)
+ {
+ if (*buf_len > 0)
+ vm_deallocate (mach_task_self (), (vm_address_t)*buf, *buf_len);
+ }
+ else if (out_buf_offs < *buf_len)
+ /* Trim the output buffer to be the right length. */
+ {
+ size_t end = round_page (out_buf_offs);
+ if (end < *buf_len)
+ vm_deallocate (mach_task_self (),
+ (vm_address_t)(*buf + end), *buf_len - end);
+ *buf_len = out_buf_offs;
+ }
+
+ return zerr;
+}
diff --git a/serverboot/kalloc.c b/serverboot/kalloc.c
new file mode 100644
index 00000000..28c0b55e
--- /dev/null
+++ b/serverboot/kalloc.c
@@ -0,0 +1,291 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1993-1987 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * File: kern/kalloc.c
+ * Author: Avadis Tevanian, Jr.
+ * Date: 1985
+ *
+ * General kernel memory allocator. This allocator is designed
+ * to be used by the kernel to manage dynamic memory fast.
+ */
+
+#include <mach.h>
+#include <cthreads.h> /* for spin locks */
+#include <malloc.h> /* for malloc_hook/free_hook */
+
+static void init_hook (void);
+static void *malloc_hook (size_t size, const void *caller);
+static void free_hook (void *ptr, const void *caller);
+
+void (*__malloc_initialize_hook) (void) = init_hook;
+
+
+#define DEBUG
+
+/*
+ * All allocations of size less than kalloc_max are rounded to the
+ * next highest power of 2.
+ */
+vm_size_t kalloc_max; /* max before we use vm_allocate */
+#define MINSIZE 4 /* minimum allocation size */
+
+struct free_list {
+ spin_lock_t lock;
+ vm_offset_t head; /* head of free list */
+#ifdef DEBUG
+ int count;
+#endif /*DEBUG*/
+};
+
+#define KLIST_MAX 13
+ /* sizes: 4, 8, 16, 32, 64,
+ 128, 256, 512, 1024,
+ 2048, 4096, 8192, 16384 */
+struct free_list kfree_list[KLIST_MAX];
+
+spin_lock_t kget_space_lock;
+vm_offset_t kalloc_next_space = 0;
+vm_offset_t kalloc_end_of_space = 0;
+
+vm_size_t kalloc_wasted_space = 0;
+
+boolean_t kalloc_initialized = FALSE;
+
+/*
+ * Initialize the memory allocator. This should be called only
+ * once on a system wide basis (i.e. first processor to get here
+ * does the initialization).
+ *
+ * This initializes all of the zones.
+ */
+
+void kalloc_init(void)
+{
+ vm_offset_t min, max;
+ vm_size_t size;
+ register int i;
+
+ /*
+ * Support free lists for items up to vm_page_size or
+ * 16Kbytes, whichever is less.
+ */
+
+ if (vm_page_size > 16*1024)
+ kalloc_max = 16*1024;
+ else
+ kalloc_max = vm_page_size;
+
+ for (i = 0; i < KLIST_MAX; i++) {
+ spin_lock_init(&kfree_list[i].lock);
+ kfree_list[i].head = 0;
+ }
+ spin_lock_init(&kget_space_lock);
+
+ /*
+ * Do not allocate memory at address 0.
+ */
+ kalloc_next_space = vm_page_size;
+ kalloc_end_of_space = vm_page_size;
+}
+
+/*
+ * Contiguous space allocator for items of less than a page size.
+ */
+vm_offset_t kget_space(vm_offset_t size)
+{
+ vm_size_t space_to_add;
+ vm_offset_t new_space = 0;
+ vm_offset_t addr;
+
+ spin_lock(&kget_space_lock);
+ while (kalloc_next_space + size > kalloc_end_of_space) {
+ /*
+ * Add at least one page to allocation area.
+ */
+ space_to_add = round_page(size);
+
+ if (new_space == 0) {
+ /*
+ * Unlock and allocate memory.
+ * Try to make it contiguous with the last
+ * allocation area.
+ */
+ spin_unlock(&kget_space_lock);
+
+ new_space = kalloc_end_of_space;
+ if (vm_map(mach_task_self(),
+ &new_space, space_to_add, (vm_offset_t) 0, TRUE,
+ MEMORY_OBJECT_NULL, (vm_offset_t) 0, FALSE,
+ VM_PROT_DEFAULT, VM_PROT_ALL, VM_INHERIT_DEFAULT)
+ != KERN_SUCCESS)
+ return 0;
+ wire_memory(new_space, space_to_add,
+ VM_PROT_READ|VM_PROT_WRITE);
+ spin_lock(&kget_space_lock);
+ continue;
+ }
+
+ /*
+ * Memory was allocated in a previous iteration.
+ * Check whether the new region is contiguous with the
+ * old one.
+ */
+ if (new_space != kalloc_end_of_space) {
+ /*
+ * Throw away the remainder of the old space,
+ * and start a new one.
+ */
+ kalloc_wasted_space +=
+ kalloc_end_of_space - kalloc_next_space;
+ kalloc_next_space = new_space;
+ }
+ kalloc_end_of_space = new_space + space_to_add;
+
+ new_space = 0;
+ }
+
+ addr = kalloc_next_space;
+ kalloc_next_space += size;
+ spin_unlock(&kget_space_lock);
+
+ if (new_space != 0)
+ (void) vm_deallocate(mach_task_self(), new_space, space_to_add);
+
+ return addr;
+}
+
+void *kalloc(vm_size_t size)
+{
+ register vm_size_t allocsize;
+ vm_offset_t addr;
+ register struct free_list *fl;
+
+ if (!kalloc_initialized) {
+ kalloc_init();
+ kalloc_initialized = TRUE;
+ }
+
+ /* compute the size of the block that we will actually allocate */
+
+ allocsize = size;
+ if (size < kalloc_max) {
+ allocsize = MINSIZE;
+ fl = kfree_list;
+ while (allocsize < size) {
+ allocsize <<= 1;
+ fl++;
+ }
+ }
+
+ /*
+ * If our size is still small enough, check the queue for that size
+ * and allocate.
+ */
+
+ if (allocsize < kalloc_max) {
+ spin_lock(&fl->lock);
+ if ((addr = fl->head) != 0) {
+ fl->head = *(vm_offset_t *)addr;
+#ifdef DEBUG
+ fl->count--;
+#endif
+ spin_unlock(&fl->lock);
+ }
+ else {
+ spin_unlock(&fl->lock);
+ addr = kget_space(allocsize);
+ }
+ }
+ else {
+ if (vm_allocate(mach_task_self(), &addr, allocsize, TRUE)
+ != KERN_SUCCESS)
+ addr = 0;
+ }
+ return (void *) addr;
+}
+
+void
+kfree( void *data,
+ vm_size_t size)
+{
+ register vm_size_t freesize;
+ register struct free_list *fl;
+
+ freesize = size;
+ if (size < kalloc_max) {
+ freesize = MINSIZE;
+ fl = kfree_list;
+ while (freesize < size) {
+ freesize <<= 1;
+ fl++;
+ }
+ }
+
+ if (freesize < kalloc_max) {
+ spin_lock(&fl->lock);
+ *(vm_offset_t *)data = fl->head;
+ fl->head = (vm_offset_t) data;
+#ifdef DEBUG
+ fl->count++;
+#endif
+ spin_unlock(&fl->lock);
+ }
+ else {
+ (void) vm_deallocate(mach_task_self(), (vm_offset_t)data, freesize);
+ }
+}
+
+static void
+init_hook (void)
+{
+ __malloc_hook = malloc_hook;
+ __free_hook = free_hook;
+}
+
+static void *
+malloc_hook (size_t size, const void *caller)
+{
+ return (void *) kalloc ((vm_size_t) size);
+}
+
+static void
+free_hook (void *ptr, const void *caller)
+{
+ /* Just ignore harmless attempts at cleanliness. */
+ /* panic("free not implemented"); */
+}
+
+void malloc_fork_prepare()
+{
+}
+
+void malloc_fork_parent()
+{
+}
+
+void malloc_fork_child()
+{
+}
diff --git a/serverboot/load.c b/serverboot/load.c
new file mode 100644
index 00000000..aa481943
--- /dev/null
+++ b/serverboot/load.c
@@ -0,0 +1,555 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990,1989,1988 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+
+#include <stddef.h>
+#include <assert.h>
+#include <mach/mach_interface.h>
+#include "mach-exec.h"
+#include "../boot/boot_script.h"
+
+#include <file_io.h>
+
+
+boolean_t load_protect_text = TRUE;
+
+
+struct stuff
+{
+ struct file *fp;
+ task_t user_task;
+
+ /* uncompressed image */
+ vm_offset_t image_addr;
+ vm_size_t image_size;
+
+ vm_offset_t aout_symtab_ofs;
+ vm_size_t aout_symtab_size;
+ vm_offset_t aout_strtab_ofs;
+ vm_size_t aout_strtab_size;
+};
+
+char *set_regs(
+ mach_port_t user_task,
+ mach_port_t user_thread,
+ struct exec_info *info,
+ int arg_size);
+
+static void read_symtab_from_file(
+ struct file *fp,
+ mach_port_t host_port,
+ task_t task,
+ char * symtab_name,
+ struct stuff *st);
+
+/* Callback functions for reading the executable file. */
+static int prog_read(void *handle, vm_offset_t file_ofs, void *buf, vm_size_t size,
+ vm_size_t *out_actual)
+{
+ struct stuff *st = handle;
+ vm_size_t resid;
+ int result;
+
+ result = read_file(st->fp, file_ofs, buf, size, &resid);
+ if (result)
+ return result;
+ *out_actual = size - resid;
+ return 0;
+}
+
+static int prog_read_exec(void *handle, vm_offset_t file_ofs, vm_size_t file_size,
+ vm_offset_t mem_addr, vm_size_t mem_size,
+ exec_sectype_t sec_type)
+{
+ struct stuff *st = handle;
+ vm_offset_t page_start = trunc_page(mem_addr);
+ vm_offset_t page_end = round_page(mem_addr + mem_size);
+ vm_prot_t mem_prot = sec_type & EXEC_SECTYPE_PROT_MASK;
+ vm_offset_t area_start;
+ int result;
+
+ if (sec_type & EXEC_SECTYPE_AOUT_SYMTAB)
+ {
+ st->aout_symtab_ofs = file_ofs;
+ st->aout_symtab_size = file_size;
+ }
+ if (sec_type & EXEC_SECTYPE_AOUT_STRTAB)
+ {
+ st->aout_strtab_ofs = file_ofs;
+ st->aout_strtab_size = file_size;
+ }
+
+ if (!(sec_type & EXEC_SECTYPE_ALLOC))
+ return 0;
+
+ assert(mem_size > 0);
+ assert(mem_size > file_size);
+
+ /*
+ printf("section %08x-%08x-%08x prot %08x (%08x-%08x)\n",
+ mem_addr, mem_addr+file_size, mem_addr+mem_size, mem_prot, page_start, page_end);
+ */
+
+ result = vm_allocate(mach_task_self(), &area_start, page_end - page_start, TRUE);
+ if (result) return (result);
+
+ if (file_size > 0)
+ {
+ vm_size_t resid;
+
+ result = read_file(st->fp, file_ofs, area_start + (mem_addr - page_start),
+ file_size, &resid);
+ if (result) return result;
+ if (resid) return EX_CORRUPT;
+ }
+
+ if (mem_size > file_size)
+ {
+ bzero((void*)area_start + (mem_addr + file_size - page_start),
+ mem_size - file_size);
+ }
+
+ result = vm_allocate(st->user_task, &page_start, page_end - page_start, FALSE);
+ if (result) return (result);
+ assert(page_start == trunc_page(mem_addr));
+
+ result = vm_write(st->user_task, page_start, area_start, page_end - page_start);
+ if (result) return (result);
+
+ result = vm_deallocate(mach_task_self(), area_start, page_end - page_start);
+ if (result) return (result);
+
+ /*
+ * Protect the segment.
+ */
+ if (load_protect_text && (mem_prot != VM_PROT_ALL)) {
+ result = vm_protect(st->user_task, page_start, page_end - page_start,
+ FALSE, mem_prot);
+ if (result) return (result);
+ }
+
+ return 0;
+}
+
+/* Callback functions for reading the uncompressed image. */
+static int image_read(void *handle, vm_offset_t file_ofs, void *buf,
+ vm_size_t size, vm_size_t *out_actual)
+{
+ struct stuff *st = handle;
+ bcopy(st->image_addr + file_ofs, buf, size);
+ *out_actual = size;
+ return 0;
+}
+
+static int image_read_exec(void *handle, vm_offset_t file_ofs,
+ vm_size_t file_size, vm_offset_t mem_addr,
+ vm_size_t mem_size, exec_sectype_t sec_type)
+{
+ struct stuff *st = handle;
+ vm_offset_t page_start = trunc_page(mem_addr);
+ vm_offset_t page_end = round_page(mem_addr + mem_size);
+ vm_prot_t mem_prot = sec_type & EXEC_SECTYPE_PROT_MASK;
+ vm_offset_t area_start;
+ int result;
+
+ if (sec_type & EXEC_SECTYPE_AOUT_SYMTAB)
+ {
+ st->aout_symtab_ofs = file_ofs;
+ st->aout_symtab_size = file_size;
+ }
+ if (sec_type & EXEC_SECTYPE_AOUT_STRTAB)
+ {
+ st->aout_strtab_ofs = file_ofs;
+ st->aout_strtab_size = file_size;
+ }
+
+ if (!(sec_type & EXEC_SECTYPE_ALLOC))
+ return 0;
+
+ assert(mem_size > 0);
+ assert(mem_size > file_size);
+
+ /*
+ printf("section %08x-%08x-%08x prot %08x (%08x-%08x)\n",
+ mem_addr, mem_addr+file_size, mem_addr+mem_size, mem_prot, page_start, page_end);
+ */
+
+ result = vm_allocate(mach_task_self(), &area_start, page_end - page_start, TRUE);
+ if (result) return (result);
+
+ if (file_size > 0)
+ {
+ bcopy(st->image_addr + file_ofs, area_start + (mem_addr - page_start),
+ file_size);
+ }
+
+ if (mem_size > file_size)
+ {
+ bzero((void*)area_start + (mem_addr + file_size - page_start),
+ mem_size - file_size);
+ }
+
+ result = vm_allocate(st->user_task, &page_start, page_end - page_start, FALSE);
+ if (result) return (result);
+ assert(page_start == trunc_page(mem_addr));
+
+ result = vm_write(st->user_task, page_start, area_start, page_end - page_start);
+ if (result) return (result);
+
+ result = vm_deallocate(mach_task_self(), area_start, page_end - page_start);
+ if (result) return (result);
+
+ /*
+ * Protect the segment.
+ */
+ if (load_protect_text && (mem_prot != VM_PROT_ALL)) {
+ result = vm_protect(st->user_task, page_start, page_end - page_start,
+ FALSE, mem_prot);
+ if (result) return (result);
+ }
+
+ return 0;
+}
+
+mach_port_t boot_script_read_file (const char *file)
+{ return MACH_PORT_NULL; } /* XXX */
+
+int
+boot_script_exec_cmd (void *hook,
+ task_t user_task,
+ char *file_name,
+ int arg_count, char **argv,
+ char *argstrings, int argslen)
+{
+ extern mach_port_t bootstrap_master_device_port, bootstrap_master_host_port;
+ extern char *root_name;
+ extern char **environ;
+ int envc, env_len;
+
+ int arg_len = argslen;
+ char *arg_pos;
+
+ kern_return_t result;
+ thread_t user_thread;
+ struct file file;
+ char namebuf[MAXPATHLEN+1];
+
+ struct stuff st;
+ struct exec_info info;
+
+ extern char * strbuild();
+
+ if (strcmp (file_name, "/dev/"))
+ (void) strbuild(namebuf, "/dev/", root_name, "/", file_name,
+ (char *)0);
+ else
+ strcpy (namebuf, file_name);
+
+ /*
+ * Open the file
+ */
+ bzero((char *)&file, sizeof(file));
+
+ result = open_file(bootstrap_master_device_port, namebuf, &file);
+ if (result != 0) {
+ panic ("%s: %s", namebuf, strerror (result));
+ }
+
+ env_len = 0;
+ for (envc = 0; environ[envc]; ++envc)
+ env_len += strlen (environ[envc]) + 1;
+
+ /*
+ * Add space for:
+ * arg_count
+ * pointers to arguments
+ * trailing 0 pointer
+ * environment variables
+ * trailing 0 pointer
+ * and align to integer boundary
+ */
+ arg_len += sizeof(integer_t) + (envc + 2 + arg_count) * sizeof(char *);
+ arg_len += env_len;
+ arg_len = (arg_len + (sizeof(integer_t) - 1)) & ~(sizeof(integer_t)-1);
+
+ /*
+ * We refrain from checking IEXEC bits to make
+ * things a little easier when things went bad.
+ * Say you have ftp(1) but chmod(1) is gone.
+ */
+ if (!file_is_regular(&file))
+ panic("boot_load_program: %s is not a regular file", namebuf);
+
+ /*
+ * Load the executable file.
+ */
+ st.fp = &file;
+ st.user_task = user_task;
+ st.aout_symtab_size = 0;
+ st.aout_strtab_size = 0;
+ result = exec_load(prog_read, prog_read_exec, &st, &info);
+#ifdef GZIP
+ if (result)
+ {
+ /*
+ * It might be gzip file.
+ */
+ int err;
+ extern int serverboot_gunzip(struct file *,
+ vm_offset_t *, size_t *);
+
+ err = serverboot_gunzip(st.fp,
+ &(st.image_addr),
+ &(st.image_size));
+ if (!err)
+ {
+ result = exec_load(image_read,
+ image_read_exec,
+ &st,
+ &info);
+ vm_deallocate(mach_task_self(),
+ st.image_addr,
+ st.image_size);
+ }
+ }
+#endif /* GZIP */
+#ifdef BZIP2
+ if (result)
+ {
+ /*
+ * It might be bzip2 file.
+ */
+ int err;
+ extern int serverboot_bunzip2(struct file *,
+ vm_offset_t *, size_t *);
+
+ err = serverboot_bunzip2(st.fp,
+ &(st.image_addr),
+ &(st.image_size));
+ if (!err)
+ {
+ result = exec_load(image_read,
+ image_read_exec,
+ &st,
+ &info);
+ vm_deallocate(mach_task_self(),
+ st.image_addr,
+ st.image_size);
+ }
+ }
+#endif /* BZIP2 */
+ if (result)
+ panic ("cannot load %s: %s", namebuf, strerror (result));
+#if 0
+ printf("(serverboot): loaded %s; entrypoint %08x\n", namebuf, info.entry);
+#endif
+
+ /*
+ * Set up the stack and user registers.
+ */
+ result = thread_create (user_task, &user_thread);
+ if (result)
+ panic ("can't create user thread for %s: %s", namebuf,
+ strerror (result));
+ arg_pos = set_regs(user_task, user_thread, &info, arg_len);
+
+ /*
+ * Read symbols from the executable file.
+ */
+#if 0
+ printf("(serverboot): loading symbols from %s\n", namebuf);
+ read_symtab_from_file(&file, bootstrap_master_host_port, user_task, namebuf, &st);
+#endif
+
+ /*
+ * Copy out the arguments.
+ */
+ {
+ vm_offset_t u_arg_start;
+ /* user start of argument list block */
+ vm_offset_t k_arg_start;
+ /* kernel start of argument list block */
+ vm_offset_t u_arg_page_start;
+ /* user start of args, page-aligned */
+ vm_size_t arg_page_size;
+ /* page_aligned size of args */
+ vm_offset_t k_arg_page_start;
+ /* kernel start of args, page-aligned */
+
+ register
+ char ** k_ap; /* kernel arglist address */
+ char * u_cp; /* user argument string address */
+ register
+ char * k_cp; /* kernel argument string address */
+ register
+ int i;
+
+ /*
+ * Get address of argument list in user space
+ */
+ u_arg_start = (vm_offset_t)arg_pos;
+
+ /*
+ * Round to page boundaries, and allocate kernel copy
+ */
+ u_arg_page_start = trunc_page(u_arg_start);
+ arg_page_size = (vm_size_t)(round_page(u_arg_start + arg_len)
+ - u_arg_page_start);
+
+ result = vm_allocate(mach_task_self(),
+ &k_arg_page_start,
+ (vm_size_t)arg_page_size,
+ TRUE);
+ if (result)
+ panic("boot_load_program: arg size");
+
+ /*
+ * Set up addresses corresponding to user pointers
+ * in the kernel block
+ */
+ k_arg_start = k_arg_page_start + (u_arg_start - u_arg_page_start);
+
+ k_ap = (char **)k_arg_start;
+
+ /*
+ * Start the strings after the arg-count and pointers
+ */
+ u_cp = (char *)u_arg_start + arg_count * sizeof(char *)
+ + envc * sizeof(char *)
+ + 2 * sizeof(char *)
+ + sizeof(integer_t);
+ k_cp = (char *)k_arg_start + arg_count * sizeof(char *)
+ + envc * sizeof(char *)
+ + 2 * sizeof(char *)
+ + sizeof(integer_t);
+
+ /*
+ * first the argument count
+ */
+ *k_ap++ = (char *)(intptr_t)arg_count;
+
+ /*
+ * Then the strings and string pointers for each argument
+ */
+ for (i = 0; i < arg_count; i++)
+ *k_ap++ = argv[i] - argstrings + u_cp;
+ *k_ap++ = (char *)0;
+ bcopy (argstrings, k_cp, argslen);
+ k_cp += argslen;
+ u_cp += argslen;
+
+ for (i = 0; i < envc; i++)
+ *k_ap++ = environ[i] - environ[0] + u_cp;
+ *k_ap = (char *)0;
+ bcopy (environ[0], k_cp, env_len);
+
+ /*
+ * Now write all of this to user space.
+ */
+ (void) vm_write(user_task,
+ u_arg_page_start,
+ k_arg_page_start,
+ arg_page_size);
+
+ (void) vm_deallocate(mach_task_self(),
+ k_arg_page_start,
+ arg_page_size);
+ }
+
+ /*
+ * Close the file.
+ */
+ close_file(&file);
+
+ /* Resume the thread. */
+ thread_resume (user_thread);
+ mach_port_deallocate (mach_task_self (), user_thread);
+
+ return (0);
+}
+
+/*
+ * Load symbols from file into kernel debugger.
+ */
+static void read_symtab_from_file(
+ struct file *fp,
+ mach_port_t host_port,
+ task_t task,
+ char * symtab_name,
+ struct stuff *st)
+{
+ vm_size_t resid;
+ kern_return_t result;
+ vm_size_t table_size;
+ vm_offset_t symtab;
+
+#if 0
+
+ if (!st->aout_symtab_size || !st->aout_strtab_size)
+ return;
+
+ /*
+ * Allocate space for the symbol table.
+ */
+ table_size = sizeof(vm_size_t)
+ + st->aout_symtab_size
+ + st->aout_strtab_size;
+ result= vm_allocate(mach_task_self(),
+ &symtab,
+ table_size,
+ TRUE);
+ if (result) {
+ printf("[ error %d allocating space for %s symbol table ]\n",
+ result, symtab_name);
+ return;
+ }
+
+ /*
+ * Set the symbol table length word,
+ * then read in the symbol table and string table.
+ */
+ *(vm_size_t*)symtab = st->aout_symtab_size;
+ result = read_file(fp, st->aout_symtab_ofs,
+ symtab + sizeof(vm_size_t),
+ st->aout_symtab_size + st->aout_strtab_size,
+ &resid);
+ if (result || resid) {
+ printf("[ no valid symbol table present for %s ]\n",
+ symtab_name);
+ }
+ else {
+ /*
+ * Load the symbols into the kernel.
+ */
+ result = host_load_symbol_table(
+ host_port,
+ task,
+ symtab_name,
+ symtab,
+ table_size);
+ }
+ (void) vm_deallocate(mach_task_self(), symtab, table_size);
+#endif
+}
diff --git a/serverboot/mach-exec.h b/serverboot/mach-exec.h
new file mode 100644
index 00000000..94b234b0
--- /dev/null
+++ b/serverboot/mach-exec.h
@@ -0,0 +1,130 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990,1989,1988 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie the
+ * rights to redistribute these changes.
+ */
+
+#ifndef _MACH_EXEC_H_
+#define _MACH_EXEC_H_
+
+#include <mach/machine/vm_types.h>
+#include <mach/vm_prot.h>
+
+/* XXX */
+typedef enum
+{
+ EXEC_ELF = 1,
+ EXEC_AOUT = 2,
+} exec_format_t;
+
+typedef struct exec_info
+{
+ /* Format of executable loaded - see above. */
+ exec_format_t format;
+
+ /* Program entrypoint. */
+ vm_offset_t entry;
+
+ /* Initial data pointer - only some architectures use this. */
+ vm_offset_t init_dp;
+
+ /* (ELF) Address of interpreter string for loading shared libraries, null if none. */
+ vm_offset_t interp;
+
+} exec_info_t;
+
+typedef int exec_sectype_t;
+#define EXEC_SECTYPE_READ VM_PROT_READ
+#define EXEC_SECTYPE_WRITE VM_PROT_WRITE
+#define EXEC_SECTYPE_EXECUTE VM_PROT_EXECUTE
+#define EXEC_SECTYPE_PROT_MASK VM_PROT_ALL
+#define EXEC_SECTYPE_ALLOC ((exec_sectype_t)0x000100)
+#define EXEC_SECTYPE_LOAD ((exec_sectype_t)0x000200)
+#define EXEC_SECTYPE_DEBUG ((exec_sectype_t)0x010000)
+#define EXEC_SECTYPE_AOUT_SYMTAB ((exec_sectype_t)0x020000)
+#define EXEC_SECTYPE_AOUT_STRTAB ((exec_sectype_t)0x040000)
+
+typedef int exec_read_func_t(void *handle, vm_offset_t file_ofs,
+ void *buf, vm_size_t size,
+ vm_size_t *out_actual);
+
+typedef int exec_read_exec_func_t(void *handle,
+ vm_offset_t file_ofs, vm_size_t file_size,
+ vm_offset_t mem_addr, vm_size_t mem_size,
+ exec_sectype_t section_type);
+
+/*
+ * Routines exported from libmach_exec.a
+ */
+
+/* Generic function to interpret an executable "file"
+ and "load" it into "memory".
+ Doesn't really know about files, loading, or memory;
+ all file I/O and destination memory accesses
+ go through provided functions.
+ Thus, this is a very generic loading mechanism.
+
+ The read() function is used to read metadata from the file
+ into the local address space.
+
+ The read_exec() function is used to load the actual sections.
+ It is used for all kinds of sections - code, data, bss, debugging data.
+ The 'section_type' parameter specifies what type of section is being loaded.
+
+ For code, data, and bss, the EXEC_SECTYPE_ALLOC flag will be set.
+ For code and data (i.e. stuff that's actually loaded from the file),
+ EXEC_SECTYPE_LOAD will also be set.
+ The EXEC_SECTYPE_PROT_MASK contains the intended access permissions
+ for the section.
+ 'file_size' may be less than 'mem_size';
+ the remaining data must be zero-filled.
+ 'mem_size' is always greater than zero, but 'file_size' may be zero
+ (e.g. in the case of a bss section).
+ No two read_exec() calls for one executable
+ will load data into the same virtual memory page,
+ although they may load from arbitrary (possibly overlapping) file positions.
+
+ For sections that aren't normally loaded into the process image
+ (e.g. debug sections), EXEC_SECTYPE_ALLOC isn't set,
+ but some other appropriate flag is set to indicate the type of section.
+
+ The 'handle' is an opaque pointer which is simply passed on
+ to the read() and read_exec() functions.
+
+ On return, the specified info structure is filled in
+ with information about the loaded executable.
+*/
+int exec_load(exec_read_func_t *read, exec_read_exec_func_t *read_exec,
+ void *handle, exec_info_t *out_info);
+
+/*
+ * Error codes
+ */
+
+#define EX_NOT_EXECUTABLE 6000 /* not a recognized executable format */
+#define EX_WRONG_ARCH 6001 /* valid executable, but wrong arch. */
+#define EX_CORRUPT 6002 /* recognized executable, but mangled */
+#define EX_BAD_LAYOUT 6003 /* something wrong with the memory or file image layout */
+
+
+#endif /* _MACH_EXEC_H_ */
diff --git a/serverboot/minix_ffs_compat.c b/serverboot/minix_ffs_compat.c
new file mode 100644
index 00000000..7d493520
--- /dev/null
+++ b/serverboot/minix_ffs_compat.c
@@ -0,0 +1,62 @@
+/*
+ * BSD FFS like functions used to ease porting bootstrap to MINIX fs
+ * Copyright (C) 1994 Csizmazia Balazs, University ELTE, Hungary
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <device/device_types.h>
+#include <device/device.h>
+
+#include <mach/mach_traps.h>
+#include <mach/mach_interface.h>
+
+#include <file_io.h>
+
+#define MINIX_BLOCK_SIZE 1024
+
+int minix_ino2blk (struct minix_super_block *fs, int ino)
+{
+ int blk;
+
+ blk=0 /* it's Mach */+2 /* boot+superblock */ + fs->s_imap_blocks +
+ fs->s_zmap_blocks + (ino-1)/MINIX_INODES_PER_BLOCK;
+ return blk;
+}
+
+int minix_fsbtodb (struct minix_super_block *fs, int b)
+{
+ return (b * MINIX_BLOCK_SIZE) / DEV_BSIZE;
+}
+
+int minix_itoo (struct minix_super_block *fs, int ino)
+{
+ return (ino - 1) % MINIX_INODES_PER_BLOCK;
+}
+
+int minix_blkoff (struct minix_super_block * fs, vm_offset_t offset)
+{
+ return offset % MINIX_BLOCK_SIZE;
+}
+
+int minix_lblkno (struct minix_super_block * fs, vm_offset_t offset)
+{
+ return offset / MINIX_BLOCK_SIZE;
+}
+
+int minix_blksize (struct minix_super_block *fs, struct file *fp, minix_daddr_t file_block)
+{
+ return MINIX_BLOCK_SIZE;
+}
diff --git a/serverboot/minix_ffs_compat.h b/serverboot/minix_ffs_compat.h
new file mode 100644
index 00000000..cc038032
--- /dev/null
+++ b/serverboot/minix_ffs_compat.h
@@ -0,0 +1,43 @@
+/*
+ * BSD FFS like declarations used to ease porting bootstrap to MINIX fs
+ * Copyright (C) 1994 Csizmazia Balazs, University ELTE, Hungary
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#define MINIX_SBSIZE MINIX_BLOCK_SIZE /* Size of superblock */
+#define MINIX_SBLOCK ((minix_daddr_t) 2) /* Location of superblock */
+
+#define MINIX_NDADDR 7
+#define MINIX_NIADDR 2
+
+#define MINIX_MAXNAMLEN 14
+
+#define MINIX_ROOTINO 1 /* MINIX ROOT INODE */
+
+#define MINIX_NINDIR(fs) 512 /* DISK_ADDRESSES_PER_BLOCKS */
+
+#define IFMT 00170000
+#define IFREG 0100000
+#define IFDIR 0040000
+#define ISVTX 0001000
+
+#define f_fs u.minix.minix_fs
+#define i_ic u.minix.minix_ic
+#define f_nindir u.minix.minix_nindir
+#define f_blk u.minix.minix_blk
+#define f_blksize u.minix.minix_blksize
+#define f_blkno u.minix.minix_blkno
+
diff --git a/serverboot/minix_file_io.c b/serverboot/minix_file_io.c
new file mode 100644
index 00000000..17beb18c
--- /dev/null
+++ b/serverboot/minix_file_io.c
@@ -0,0 +1,851 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * Stand-alone file reading package.
+ */
+
+#include <device/device_types.h>
+#include <device/device.h>
+
+#include <mach/mach_traps.h>
+#include <mach/mach_interface.h>
+
+#include "file_io.h"
+#include "minix_ffs_compat.h"
+#include "minix_fs.h"
+
+void minix_close_file(); /* forward */
+
+#define MINIX_NAME_LEN 14
+#define MINIX_BLOCK_SIZE 1024
+
+/*
+ * Free file buffers, but don't close file.
+ */
+static void
+free_file_buffers(fp)
+ register struct file *fp;
+{
+ register int level;
+
+ /*
+ * Free the indirect blocks
+ */
+ for (level = 0; level < MINIX_NIADDR; level++) {
+ if (fp->f_blk[level] != 0) {
+ (void) vm_deallocate(mach_task_self(),
+ fp->f_blk[level],
+ fp->f_blksize[level]);
+ fp->f_blk[level] = 0;
+ }
+ fp->f_blkno[level] = -1;
+ }
+
+ /*
+ * Free the data block
+ */
+ if (fp->f_buf != 0) {
+ (void) vm_deallocate(mach_task_self(),
+ fp->f_buf,
+ fp->f_buf_size);
+ fp->f_buf = 0;
+ }
+ fp->f_buf_blkno = -1;
+}
+
+/*
+ * Read a new inode into a file structure.
+ */
+static int
+read_inode(inumber, fp)
+ ino_t inumber;
+ register struct file *fp;
+{
+ vm_offset_t buf;
+ mach_msg_type_number_t buf_size;
+ register
+ struct minix_super_block *fs;
+ minix_daddr_t disk_block;
+ kern_return_t rc;
+
+ fs = fp->f_fs;
+ disk_block = minix_ino2blk(fs, inumber);
+
+ rc = device_read(fp->f_dev,
+ 0,
+ (recnum_t) minix_fsbtodb(fp->f_fs, disk_block),
+ (int) MINIX_BLOCK_SIZE,
+ (char **)&buf,
+ &buf_size);
+ if (rc != KERN_SUCCESS)
+ return (rc);
+
+ {
+ register struct minix_inode *dp;
+
+ dp = (struct minix_inode *)buf;
+ dp += minix_itoo(fs, inumber);
+ fp->i_ic = *dp;
+ fp->f_size = dp->i_size;
+ }
+
+ (void) vm_deallocate(mach_task_self(), buf, buf_size);
+
+ /*
+ * Clear out the old buffers
+ */
+ free_file_buffers(fp);
+
+ return (0);
+}
+
+/*
+ * Given an offset in a file, find the disk block number that
+ * contains that block.
+ */
+static int
+block_map(fp, file_block, disk_block_p)
+ struct file *fp;
+ minix_daddr_t file_block;
+ minix_daddr_t *disk_block_p; /* out */
+{
+ int level;
+ int idx;
+ minix_daddr_t ind_block_num;
+ kern_return_t rc;
+
+ vm_offset_t olddata[MINIX_NIADDR+1];
+ vm_size_t oldsize[MINIX_NIADDR+1];
+
+ /*
+ * Index structure of an inode:
+ *
+ * i_db[0..NDADDR-1] hold block numbers for blocks
+ * 0..NDADDR-1
+ *
+ * i_ib[0] index block 0 is the single indirect
+ * block
+ * holds block numbers for blocks
+ * NDADDR .. NDADDR + NINDIR(fs)-1
+ *
+ * i_ib[1] index block 1 is the double indirect
+ * block
+ * holds block numbers for INDEX blocks
+ * for blocks
+ * NDADDR + NINDIR(fs) ..
+ * NDADDR + NINDIR(fs) + NINDIR(fs)**2 - 1
+ *
+ * i_ib[2] index block 2 is the triple indirect
+ * block
+ * holds block numbers for double-indirect
+ * blocks for blocks
+ * NDADDR + NINDIR(fs) + NINDIR(fs)**2 ..
+ * NDADDR + NINDIR(fs) + NINDIR(fs)**2
+ * + NINDIR(fs)**3 - 1
+ */
+
+ mutex_lock(&fp->f_lock);
+
+ if (file_block < MINIX_NDADDR) {
+ /* Direct block. */
+ *disk_block_p = fp->i_ic.i_zone[file_block];
+ mutex_unlock(&fp->f_lock);
+ return (0);
+ }
+
+ file_block -= MINIX_NDADDR;
+
+ /*
+ * nindir[0] = NINDIR
+ * nindir[1] = NINDIR**2
+ * nindir[2] = NINDIR**3
+ * etc
+ */
+ for (level = 0; level < MINIX_NIADDR; level++) {
+ if (file_block < fp->f_nindir[level])
+ break;
+ file_block -= fp->f_nindir[level];
+ }
+ if (level == MINIX_NIADDR) {
+ /* Block number too high */
+ mutex_unlock(&fp->f_lock);
+ return (FS_NOT_IN_FILE);
+ }
+
+ ind_block_num = fp->i_ic.i_zone[level + MINIX_NDADDR];
+
+ /*
+ * Initialize array of blocks to free.
+ */
+ for (idx = 0; idx < MINIX_NIADDR; idx++)
+ oldsize[idx] = 0;
+
+ for (; level >= 0; level--) {
+
+ vm_offset_t data;
+ mach_msg_type_number_t size;
+
+ if (ind_block_num == 0)
+ break;
+
+ if (fp->f_blkno[level] == ind_block_num) {
+ /*
+ * Cache hit. Just pick up the data.
+ */
+
+ data = fp->f_blk[level];
+ }
+ else {
+ /*
+ * Drop our lock while doing the read.
+ * (The f_dev and f_fs fields don`t change.)
+ */
+ mutex_unlock(&fp->f_lock);
+
+ rc = device_read(fp->f_dev,
+ 0,
+ (recnum_t) minix_fsbtodb(fp->f_fs, ind_block_num),
+ MINIX_BLOCK_SIZE,
+ (char **)&data,
+ &size);
+ if (rc != KERN_SUCCESS)
+ return (rc);
+
+ /*
+ * See if we can cache the data. Need a write lock to
+ * do this. While we hold the write lock, we can`t do
+ * *anything* which might block for memory. Otherwise
+ * a non-privileged thread might deadlock with the
+ * privileged threads. We can`t block while taking the
+ * write lock. Otherwise a non-privileged thread
+ * blocked in the vm_deallocate (while holding a read
+ * lock) will block a privileged thread. For the same
+ * reason, we can`t take a read lock and then use
+ * lock_read_to_write.
+ */
+
+ mutex_lock(&fp->f_lock);
+
+ olddata[level] = fp->f_blk[level];
+ oldsize[level] = fp->f_blksize[level];
+
+ fp->f_blkno[level] = ind_block_num;
+ fp->f_blk[level] = data;
+ fp->f_blksize[level] = size;
+
+ /*
+ * Return to holding a read lock, and
+ * dispose of old data.
+ */
+
+ }
+
+ if (level > 0) {
+ idx = file_block / fp->f_nindir[level-1];
+ file_block %= fp->f_nindir[level-1];
+ }
+ else
+ idx = file_block;
+
+ ind_block_num = ((minix_daddr_t *)data)[idx];
+ }
+
+ mutex_unlock(&fp->f_lock);
+
+ /*
+ * After unlocking the file, free any blocks that
+ * we need to free.
+ */
+ for (idx = 0; idx < MINIX_NIADDR; idx++)
+ if (oldsize[idx] != 0)
+ (void) vm_deallocate(mach_task_self(),
+ olddata[idx],
+ oldsize[idx]);
+
+ *disk_block_p = ind_block_num;
+ return (0);
+}
+
+/*
+ * Read a portion of a file into an internal buffer. Return
+ * the location in the buffer and the amount in the buffer.
+ */
+static int
+buf_read_file(fp, offset, buf_p, size_p)
+ register struct file *fp;
+ vm_offset_t offset;
+ vm_offset_t *buf_p; /* out */
+ vm_size_t *size_p; /* out */
+{
+ register
+ struct minix_super_block *fs;
+ vm_offset_t off;
+ register minix_daddr_t file_block;
+ minix_daddr_t disk_block;
+ int rc;
+ vm_offset_t block_size;
+
+ if (offset >= fp->i_ic.i_size)
+ return (FS_NOT_IN_FILE);
+
+ fs = fp->f_fs;
+
+ off = minix_blkoff(fs, offset);
+ file_block = minix_lblkno(fs, offset);
+ block_size = minix_blksize(fs, fp, file_block);
+
+ if (((daddr_t) file_block) != fp->f_buf_blkno) {
+ rc = block_map(fp, file_block, &disk_block);
+ if (rc != 0)
+ return (rc);
+
+ if (fp->f_buf)
+ (void)vm_deallocate(mach_task_self(),
+ fp->f_buf,
+ fp->f_buf_size);
+
+ if (disk_block == 0) {
+ (void)vm_allocate(mach_task_self(),
+ &fp->f_buf,
+ block_size,
+ TRUE);
+ fp->f_buf_size = block_size;
+ }
+ else {
+ rc = device_read(fp->f_dev,
+ 0,
+ (recnum_t) minix_fsbtodb(fs, disk_block),
+ (int) block_size,
+ (char **) &fp->f_buf,
+ (mach_msg_type_number_t *)&fp->f_buf_size);
+ }
+ if (rc)
+ return (rc);
+
+ fp->f_buf_blkno = (daddr_t) file_block;
+ }
+
+ /*
+ * Return address of byte in buffer corresponding to
+ * offset, and size of remainder of buffer after that
+ * byte.
+ */
+ *buf_p = fp->f_buf + off;
+ *size_p = block_size - off;
+
+ /*
+ * But truncate buffer at end of file.
+ */
+ if (*size_p > fp->i_ic.i_size - offset)
+ *size_p = fp->i_ic.i_size - offset;
+
+ return (0);
+}
+
+/*
+ * Search a directory for a name and return its
+ * i_number.
+ */
+static int
+search_directory(name, fp, inumber_p)
+ char * name;
+ register struct file *fp;
+ ino_t *inumber_p; /* out */
+{
+ vm_offset_t buf;
+ vm_size_t buf_size;
+ vm_offset_t offset;
+ register struct minix_directory_entry *dp;
+ int length;
+ kern_return_t rc;
+ char tmp_name[15];
+
+ length = strlen(name);
+
+ offset = 0;
+ while (offset < fp->i_ic.i_size) {
+ rc = buf_read_file(fp, offset, &buf, &buf_size);
+ if (rc != KERN_SUCCESS)
+ return (rc);
+
+ dp = (struct minix_directory_entry *)buf;
+ if (dp->inode != 0) {
+ strncpy (tmp_name, dp->name, MINIX_NAME_LEN /* XXX it's 14 */);
+ tmp_name[MINIX_NAME_LEN] = '\0';
+ if (strlen(tmp_name) == length &&
+ !strcmp(name, tmp_name))
+ {
+ /* found entry */
+ *inumber_p = dp->inode;
+ return (0);
+ }
+ }
+ offset += 16 /* MINIX dir. entry length - MINIX FS Ver. 1. */;
+ }
+ return (FS_NO_ENTRY);
+}
+
+static int
+read_fs(dev, fsp)
+ mach_port_t dev;
+ struct minix_super_block **fsp;
+{
+ register
+ struct minix_super_block *fs;
+ vm_offset_t buf;
+ mach_msg_type_number_t buf_size;
+ int error;
+
+ /*
+ * Read the super block
+ */
+ error = device_read(dev, 0, (recnum_t) MINIX_SBLOCK, MINIX_SBSIZE,
+ (char **) &buf, &buf_size);
+ if (error)
+ return (error);
+
+ /*
+ * Check the superblock
+ */
+ fs = (struct minix_super_block *)buf;
+ if (fs->s_magic != MINIX_SUPER_MAGIC) {
+ (void) vm_deallocate(mach_task_self(), buf, buf_size);
+ return (FS_INVALID_FS);
+ }
+
+
+ *fsp = fs;
+
+ return 0;
+}
+
+static int
+mount_fs(fp)
+ register struct file *fp;
+{
+ register struct minix_super_block *fs;
+ int error;
+
+ error = read_fs(fp->f_dev, &fp->f_fs);
+ if (error)
+ return (error);
+
+ fs = fp->f_fs;
+
+ /*
+ * Calculate indirect block levels.
+ */
+ {
+ register int mult;
+ register int level;
+
+ mult = 1;
+ for (level = 0; level < MINIX_NIADDR; level++) {
+ mult *= MINIX_NINDIR(fs);
+ fp->f_nindir[level] = mult;
+ }
+ }
+
+ return (0);
+}
+
+static void
+unmount_fs(fp)
+ register struct file *fp;
+{
+ if (file_is_structured(fp)) {
+ (void) vm_deallocate(mach_task_self(),
+ (vm_offset_t) fp->f_fs,
+ MINIX_SBSIZE);
+ fp->f_fs = 0;
+ }
+}
+
+/*
+ * Open a file.
+ */
+int
+minix_open_file(master_device_port, path, fp)
+ mach_port_t master_device_port;
+ char * path;
+ struct file *fp;
+{
+#define RETURN(code) { rc = (code); goto exit; }
+
+ register char *cp, *component;
+ register int c; /* char */
+ register int rc;
+ ino_t inumber, parent_inumber;
+ int nlinks = 0;
+
+ char namebuf[MAXPATHLEN+1];
+
+ if (path == 0 || *path == '\0') {
+ return FS_NO_ENTRY;
+ }
+
+ /*
+ * Copy name into buffer to allow modifying it.
+ */
+ strcpy(namebuf, path);
+
+ /*
+ * Look for '/dev/xxx' at start of path, for
+ * root device.
+ */
+ if (!strprefix(namebuf, "/dev/")) {
+ printf("no device name\n");
+ return FS_NO_ENTRY;
+ }
+
+ cp = namebuf + 5; /* device */
+ component = cp;
+ while ((c = *cp) != '\0' && c != '/') {
+ cp++;
+ }
+ *cp = '\0';
+
+ bzero (fp, sizeof (struct file));
+
+ rc = device_open(master_device_port,
+ D_READ|D_WRITE,
+ component,
+ &fp->f_dev);
+ if (rc)
+ return rc;
+
+ if (c == 0) {
+ fp->f_fs = 0;
+ goto out_ok;
+ }
+
+ *cp = c;
+
+ rc = mount_fs(fp);
+ if (rc)
+ return rc;
+
+ inumber = (ino_t) MINIX_ROOTINO;
+ if ((rc = read_inode(inumber, fp)) != 0) {
+ printf("can't read root inode\n");
+ goto exit;
+ }
+
+ while (*cp) {
+
+ /*
+ * Check that current node is a directory.
+ */
+ if ((fp->i_ic.i_mode & IFMT) != IFDIR)
+ RETURN (FS_NOT_DIRECTORY);
+
+ /*
+ * Remove extra separators
+ */
+ while (*cp == '/')
+ cp++;
+
+ /*
+ * Get next component of path name.
+ */
+ component = cp;
+ {
+ register int len = 0;
+
+ while ((c = *cp) != '\0' && c != '/') {
+ if (len++ > MINIX_MAXNAMLEN)
+ RETURN (FS_NAME_TOO_LONG);
+ if (c & 0200)
+ RETURN (FS_INVALID_PARAMETER);
+ cp++;
+ }
+ *cp = 0;
+ }
+
+ /*
+ * Look up component in current directory.
+ * Save directory inumber in case we find a
+ * symbolic link.
+ */
+ parent_inumber = inumber;
+ rc = search_directory(component, fp, &inumber);
+ if (rc) {
+ printf("%s: not found\n", path);
+ goto exit;
+ }
+ *cp = c;
+
+ /*
+ * Open next component.
+ */
+ if ((rc = read_inode(inumber, fp)) != 0)
+ goto exit;
+
+ /*
+ * Check for symbolic link.
+ */
+ }
+
+ /*
+ * Found terminal component.
+ */
+ out_ok:
+ mutex_init(&fp->f_lock);
+ return 0;
+
+ /*
+ * At error exit, close file to free storage.
+ */
+ exit:
+ minix_close_file(fp);
+ return rc;
+}
+
+/*
+ * Close file - free all storage used.
+ */
+void
+minix_close_file(fp)
+ register struct file *fp;
+{
+ register int i;
+
+ /*
+ * Free the disk super-block.
+ */
+ unmount_fs(fp);
+
+ /*
+ * Free the inode and data buffers.
+ */
+ free_file_buffers(fp);
+}
+
+int
+minix_file_is_directory(struct file *fp)
+{
+ return (fp->i_ic.i_mode & IFMT) == IFDIR;
+}
+
+int
+minix_file_is_regular(struct file *fp)
+{
+ return (fp->i_ic.i_mode & IFMT) == IFREG;
+}
+
+/*
+ * Copy a portion of a file into kernel memory.
+ * Cross block boundaries when necessary.
+ */
+int
+minix_read_file(fp, offset, start, size, resid)
+ register struct file *fp;
+ vm_offset_t offset;
+ vm_offset_t start;
+ vm_size_t size;
+ vm_size_t *resid; /* out */
+{
+ int rc;
+ register vm_size_t csize;
+ vm_offset_t buf;
+ vm_size_t buf_size;
+
+ while (size != 0) {
+ rc = buf_read_file(fp, offset, &buf, &buf_size);
+ if (rc)
+ return (rc);
+
+ csize = size;
+ if (csize > buf_size)
+ csize = buf_size;
+ if (csize == 0)
+ break;
+
+ bcopy((char *)buf, (char *)start, csize);
+
+ offset += csize;
+ start += csize;
+ size -= csize;
+ }
+ if (resid)
+ *resid = size;
+
+ return (0);
+}
+
+/* simple utility: only works for 2^n */
+static int
+log2(n)
+ register unsigned int n;
+{
+ register int i = 0;
+
+ while ((n & 1) == 0) {
+ i++;
+ n >>= 1;
+ }
+ return i;
+}
+
+/*
+ * Make an empty file_direct for a device.
+ */
+int
+minix_open_file_direct(dev, fdp, is_structured)
+ mach_port_t dev;
+ register struct file_direct *fdp;
+ boolean_t is_structured;
+{
+ struct minix_super_block *fs;
+ int rc;
+
+ if (!is_structured) {
+ fdp->fd_dev = dev;
+ fdp->fd_blocks = (daddr_t *) 0;
+ fdp->fd_bsize = vm_page_size;
+ fdp->fd_bshift = log2(vm_page_size);
+ fdp->fd_fsbtodb = 0; /* later */
+ fdp->fd_size = 0; /* later */
+ return 0;
+ }
+
+ rc = read_fs(dev, &fs);
+ if (rc)
+ return rc;
+
+ fdp->fd_dev = dev;
+ fdp->fd_blocks = (daddr_t *) 0;
+ fdp->fd_size = 0;
+ fdp->fd_bsize = MINIX_BLOCK_SIZE;
+ fdp->fd_bshift = log2(fdp->fd_bsize);
+ fdp->fd_fsbtodb = log2(fdp->fd_bsize / DEV_BSIZE);
+
+ (void) vm_deallocate(mach_task_self(),
+ (vm_offset_t) fs,
+ MINIX_SBSIZE);
+
+ return 0;
+}
+
+/*
+ * Add blocks from a file to a file_direct.
+ */
+int
+minix_add_file_direct(fdp, fp)
+ register struct file_direct *fdp;
+ register struct file *fp;
+{
+ register struct minix_super_block *fs;
+ long num_blocks, i;
+ vm_offset_t buffer;
+ vm_size_t size;
+ int rc;
+
+ /* the file must be on the same device */
+
+ if (fdp->fd_dev != fp->f_dev)
+ return FS_INVALID_FS;
+
+ if (!file_is_structured(fp)) {
+ int result[DEV_GET_SIZE_COUNT];
+ natural_t count;
+
+ count = DEV_GET_SIZE_COUNT;
+ rc = device_get_status( fdp->fd_dev, DEV_GET_SIZE,
+ result, &count);
+ if (rc)
+ return rc;
+ fdp->fd_size = result[DEV_GET_SIZE_DEVICE_SIZE] >> fdp->fd_bshift;
+ fdp->fd_fsbtodb = log2(fdp->fd_bsize/result[DEV_GET_SIZE_RECORD_SIZE]);
+ return 0;
+ }
+
+ /* it must hold a file system */
+
+ fs = fp->f_fs;
+/*
+ if (fdp->fd_bsize != fs->fs_bsize ||
+ fdp->fd_fsbtodb != fs->fs_fsbtodb)
+*/
+ if (fdp->fd_bsize != MINIX_BLOCK_SIZE)
+ return FS_INVALID_FS;
+
+ /* calculate number of blocks in the file, ignoring fragments */
+
+ num_blocks = minix_lblkno(fs, fp->i_ic.i_size);
+
+ /* allocate memory for a bigger array */
+
+ size = (num_blocks + fdp->fd_size) * sizeof(minix_daddr_t);
+ rc = vm_allocate(mach_task_self(), &buffer, size, TRUE);
+ if (rc != KERN_SUCCESS)
+ return rc;
+
+ /* lookup new block addresses */
+
+ for (i = 0; i < num_blocks; i++) {
+ minix_daddr_t disk_block;
+
+ rc = block_map(fp, (minix_daddr_t) i, &disk_block);
+ if (rc != 0) {
+ (void) vm_deallocate(mach_task_self(), buffer, size);
+ return rc;
+ }
+
+ ((minix_daddr_t *) buffer)[fdp->fd_size + i] = disk_block;
+ }
+
+ /* copy old addresses and install the new array */
+
+ if (fdp->fd_blocks != 0) {
+ bcopy((char *) fdp->fd_blocks, (char *) buffer,
+ fdp->fd_size * sizeof(minix_daddr_t));
+
+ (void) vm_deallocate(mach_task_self(),
+ (vm_offset_t) fdp->fd_blocks,
+ (vm_size_t) (fdp->fd_size * sizeof(minix_daddr_t)));
+ }
+ fdp->fd_blocks = (daddr_t *) buffer;
+ fdp->fd_size += num_blocks;
+
+ /* deallocate cached blocks */
+
+ free_file_buffers(fp);
+
+ return 0;
+}
+
+int
+minix_remove_file_direct(fdp)
+ struct file_direct *fdp;
+{
+ if (fdp->fd_blocks)
+ (void) vm_deallocate(mach_task_self(),
+ (vm_offset_t) fdp->fd_blocks,
+ (vm_size_t) (fdp->fd_size * sizeof(minix_daddr_t)));
+ fdp->fd_blocks = 0; /* sanity */
+ /* xxx should lose a ref to fdp->fd_dev here (and elsewhere) xxx */
+}
diff --git a/serverboot/minix_fs.h b/serverboot/minix_fs.h
new file mode 100644
index 00000000..678f3a0d
--- /dev/null
+++ b/serverboot/minix_fs.h
@@ -0,0 +1,107 @@
+/*
+ * minix_fs.h
+ * stolen (and slightly extended by csb) from the Linux distribution
+ * Copyright (C) 1994 Linus Torvalds
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef _LINUX_MINIX_FS_H
+#define _LINUX_MINIX_FS_H
+
+/*
+ * The minix filesystem constants/structures
+ */
+
+/*
+ * Thanks to Kees J Bot for sending me the definitions of the new
+ * minix filesystem (aka V2) with bigger inodes and 32-bit block
+ * pointers. It's not actually implemented yet, but I'll look into
+ * it.
+ */
+
+#define MINIX_ROOT_INO 1
+
+/* Not the same as the bogus LINK_MAX in <linux/limits.h>. Oh well. */
+#define MINIX_LINK_MAX 250
+
+#define MINIX_I_MAP_SLOTS 8
+#define MINIX_Z_MAP_SLOTS 8
+#define MINIX_SUPER_MAGIC 0x137F /* original minix fs */
+#define MINIX_SUPER_MAGIC2 0x138F /* minix fs, 30 char names */
+#define NEW_MINIX_SUPER_MAGIC 0x2468 /* minix V2 - not implemented */
+#define MINIX_VALID_FS 0x0001 /* Clean fs. */
+#define MINIX_ERROR_FS 0x0002 /* fs has errors. */
+
+#define MINIX_INODES_PER_BLOCK ((MINIX_BLOCK_SIZE)/(sizeof (struct minix_inode)))
+
+struct minix_inode {
+ unsigned short i_mode;
+ unsigned short i_uid;
+ unsigned long i_size;
+ unsigned long i_time;
+ unsigned char i_gid;
+ unsigned char i_nlinks;
+ unsigned short i_zone[9];
+};
+
+/*
+ * The new minix inode has all the time entries, as well as
+ * long block numbers and a third indirect block (7+1+1+1
+ * instead of 7+1+1). Also, some previously 8-bit values are
+ * now 16-bit. The inode is now 64 bytes instead of 32.
+ */
+struct new_minix_inode {
+ unsigned short i_mode;
+ unsigned short i_nlinks;
+ unsigned short i_uid;
+ unsigned short i_gid;
+ unsigned long i_size;
+ unsigned long i_atime;
+ unsigned long i_mtime;
+ unsigned long i_ctime;
+ unsigned long i_zone[10];
+};
+
+/*
+ * minix super-block data on disk
+ */
+struct minix_super_block {
+ unsigned short s_ninodes;
+ unsigned short s_nzones;
+ unsigned short s_imap_blocks;
+ unsigned short s_zmap_blocks;
+ unsigned short s_firstdatazone;
+ unsigned short s_log_zone_size;
+ unsigned long s_max_size;
+ unsigned short s_magic;
+ unsigned short s_state;
+};
+
+struct minix_dir_entry {
+ unsigned short inode;
+ char name[0];
+};
+
+struct minix_directory_entry {
+ unsigned short inode;
+ char name[14];
+};
+
+#define MINIX_NIADDR 2
+
+typedef unsigned short minix_daddr_t;
+
+#endif
diff --git a/serverboot/minix_super.h b/serverboot/minix_super.h
new file mode 100644
index 00000000..144cf064
--- /dev/null
+++ b/serverboot/minix_super.h
@@ -0,0 +1,49 @@
+/*
+ * minix_super.h
+ * stolen from the Linux distribution
+ * Copyright (C) 1994 Linus Torvalds
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef _LINUX_MINIX_FS_H
+#define _LINUX_MINIX_FS_H
+
+struct minix_super_block {
+ unsigned short s_ninodes;
+ unsigned short s_nzones;
+ unsigned short s_imap_blocks;
+ unsigned short s_zmap_blocks;
+ unsigned short s_firstdatazone;
+ unsigned short s_log_zone_size;
+ unsigned long s_max_size;
+ unsigned short s_magic;
+ unsigned short s_state;
+};
+
+
+struct minix_inode {
+ unsigned short i_mode;
+ unsigned short i_uid;
+ unsigned long i_size;
+ unsigned long i_time;
+ unsigned char i_gid;
+ unsigned char i_nlinks;
+ unsigned short i_zone[9];
+};
+
+#define MINIX_NIADDR 2
+
+#endif
diff --git a/serverboot/panic.c b/serverboot/panic.c
new file mode 100644
index 00000000..25924099
--- /dev/null
+++ b/serverboot/panic.c
@@ -0,0 +1,58 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990,1989 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+
+#include <mach/port.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <errno.h>
+
+static mach_port_t master_host_port;
+
+panic_init(port)
+ mach_port_t port;
+{
+ master_host_port = port;
+}
+
+/*VARARGS1*/
+panic (const char *s, ...)
+{
+ va_list listp;
+
+ clearerr (stdout);
+ printf("%s: panic: ", program_invocation_name);
+ va_start(listp, s);
+ vprintf(s, listp);
+ va_end(listp);
+ printf("\n");
+
+#ifdef PC532
+ { int l; for (l=0;l < 1000000;l++) ; }
+#endif /* PC532 */
+#define RB_DEBUGGER 0x1000 /* enter debugger NOW */
+ (void) host_reboot(master_host_port, RB_DEBUGGER);
+ for (;;);
+}
diff --git a/serverboot/queue.h b/serverboot/queue.h
new file mode 100644
index 00000000..00619174
--- /dev/null
+++ b/serverboot/queue.h
@@ -0,0 +1,316 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990,1989,1988,1987 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon rights
+ * to redistribute these changes.
+ */
+/*
+ * File: queue.h
+ * Author: Avadis Tevanian, Jr.
+ * Date: 1985
+ *
+ * Type definitions for generic queues.
+ *
+ */
+
+#ifndef _QUEUE_H_
+#define _QUEUE_H_
+
+/*
+ * Queue of abstract objects. Queue is maintained
+ * within that object.
+ *
+ * Supports fast removal from within the queue.
+ *
+ * How to declare a queue of elements of type "foo_t":
+ * In the "*foo_t" type, you must have a field of
+ * type "queue_chain_t" to hold together this queue.
+ * There may be more than one chain through a
+ * "foo_t", for use by different queues.
+ *
+ * Declare the queue as a "queue_t" type.
+ *
+ * Elements of the queue (of type "foo_t", that is)
+ * are referred to by reference, and cast to type
+ * "queue_entry_t" within this module.
+ */
+
+/*
+ * A generic doubly-linked list (queue).
+ */
+
+struct queue_entry {
+ struct queue_entry *next; /* next element */
+ struct queue_entry *prev; /* previous element */
+};
+
+typedef struct queue_entry *queue_t;
+typedef struct queue_entry queue_head_t;
+typedef struct queue_entry queue_chain_t;
+typedef struct queue_entry *queue_entry_t;
+
+/*
+ * Macro: queue_init
+ * Function:
+ * Initialize the given queue.
+ * Header:
+ * void queue_init(q)
+ * queue_t q; / * MODIFIED * /
+ */
+#define queue_init(q) ((q)->next = (q)->prev = q)
+
+/*
+ * Macro: queue_first
+ * Function:
+ * Returns the first entry in the queue,
+ * Header:
+ * queue_entry_t queue_first(q)
+ * queue_t q; / * IN * /
+ */
+#define queue_first(q) ((q)->next)
+
+/*
+ * Macro: queue_next
+ * Function:
+ * Returns the entry after an item in the queue.
+ * Header:
+ * queue_entry_t queue_next(qc)
+ * queue_t qc;
+ */
+#define queue_next(qc) ((qc)->next)
+
+/*
+ * Macro: queue_last
+ * Function:
+ * Returns the last entry in the queue.
+ * Header:
+ * queue_entry_t queue_last(q)
+ * queue_t q; / * IN * /
+ */
+#define queue_last(q) ((q)->prev)
+
+/*
+ * Macro: queue_prev
+ * Function:
+ * Returns the entry before an item in the queue.
+ * Header:
+ * queue_entry_t queue_prev(qc)
+ * queue_t qc;
+ */
+#define queue_prev(qc) ((qc)->prev)
+
+/*
+ * Macro: queue_end
+ * Function:
+ * Tests whether a new entry is really the end of
+ * the queue.
+ * Header:
+ * boolean_t queue_end(q, qe)
+ * queue_t q;
+ * queue_entry_t qe;
+ */
+#define queue_end(q, qe) ((q) == (qe))
+
+/*
+ * Macro: queue_empty
+ * Function:
+ * Tests whether a queue is empty.
+ * Header:
+ * boolean_t queue_empty(q)
+ * queue_t q;
+ */
+#define queue_empty(q) queue_end((q), queue_first(q))
+
+
+/*----------------------------------------------------------------*/
+/*
+ * Macros that operate on generic structures. The queue
+ * chain may be at any location within the structure, and there
+ * may be more than one chain.
+ */
+
+/*
+ * Macro: queue_enter
+ * Function:
+ * Insert a new element at the tail of the queue.
+ * Header:
+ * void queue_enter(q, elt, type, field)
+ * queue_t q;
+ * <type> elt;
+ * <type> is what's in our queue
+ * <field> is the chain field in (*<type>)
+ */
+#define queue_enter(head, elt, type, field) \
+{ \
+ register queue_entry_t prev; \
+ \
+ prev = (head)->prev; \
+ if ((head) == prev) { \
+ (head)->next = (queue_entry_t) (elt); \
+ } \
+ else { \
+ ((type)prev)->field.next = (queue_entry_t)(elt);\
+ } \
+ (elt)->field.prev = prev; \
+ (elt)->field.next = head; \
+ (head)->prev = (queue_entry_t) elt; \
+}
+
+/*
+ * Macro: queue_enter_first
+ * Function:
+ * Insert a new element at the head of the queue.
+ * Header:
+ * void queue_enter_first(q, elt, type, field)
+ * queue_t q;
+ * <type> elt;
+ * <type> is what's in our queue
+ * <field> is the chain field in (*<type>)
+ */
+#define queue_enter_first(head, elt, type, field) \
+{ \
+ register queue_entry_t next; \
+ \
+ next = (head)->next; \
+ if ((head) == next) { \
+ (head)->prev = (queue_entry_t) (elt); \
+ } \
+ else { \
+ ((type)next)->field.prev = (queue_entry_t)(elt);\
+ } \
+ (elt)->field.next = next; \
+ (elt)->field.prev = head; \
+ (head)->next = (queue_entry_t) elt; \
+}
+
+/*
+ * Macro: queue_field [internal use only]
+ * Function:
+ * Find the queue_chain_t (or queue_t) for the
+ * given element (thing) in the given queue (head)
+ */
+#define queue_field(head, thing, type, field) \
+ (((head) == (thing)) ? (head) : &((type)(thing))->field)
+
+/*
+ * Macro: queue_remove
+ * Function:
+ * Remove an arbitrary item from the queue.
+ * Header:
+ * void queue_remove(q, qe, type, field)
+ * arguments as in queue_enter
+ */
+#define queue_remove(head, elt, type, field) \
+{ \
+ register queue_entry_t next, prev; \
+ \
+ next = (elt)->field.next; \
+ prev = (elt)->field.prev; \
+ \
+ if ((head) == next) \
+ (head)->prev = prev; \
+ else \
+ ((type)next)->field.prev = prev; \
+ \
+ if ((head) == prev) \
+ (head)->next = next; \
+ else \
+ ((type)prev)->field.next = next; \
+}
+
+/*
+ * Macro: queue_remove_first
+ * Function:
+ * Remove and return the entry at the head of
+ * the queue.
+ * Header:
+ * queue_remove_first(head, entry, type, field)
+ * entry is returned by reference
+ */
+#define queue_remove_first(head, entry, type, field) \
+{ \
+ register queue_entry_t next; \
+ \
+ (entry) = (type) ((head)->next); \
+ next = (entry)->field.next; \
+ \
+ if ((head) == next) \
+ (head)->prev = (head); \
+ else \
+ ((type)(next))->field.prev = (head); \
+ (head)->next = next; \
+}
+
+/*
+ * Macro: queue_remove_last
+ * Function:
+ * Remove and return the entry at the tail of
+ * the queue.
+ * Header:
+ * queue_remove_last(head, entry, type, field)
+ * entry is returned by reference
+ */
+#define queue_remove_last(head, entry, type, field) \
+{ \
+ register queue_entry_t prev; \
+ \
+ (entry) = (type) ((head)->prev); \
+ prev = (entry)->field.prev; \
+ \
+ if ((head) == prev) \
+ (head)->next = (head); \
+ else \
+ ((type)(prev))->field.next = (head); \
+ (head)->prev = prev; \
+}
+
+/*
+ * Macro: queue_assign
+ */
+#define queue_assign(to, from, type, field) \
+{ \
+ ((type)((from)->prev))->field.next = (to); \
+ ((type)((from)->next))->field.prev = (to); \
+ *to = *from; \
+}
+
+/*
+ * Macro: queue_iterate
+ * Function:
+ * iterate over each item in the queue.
+ * Generates a 'for' loop, setting elt to
+ * each item in turn (by reference).
+ * Header:
+ * queue_iterate(q, elt, type, field)
+ * queue_t q;
+ * <type> elt;
+ * <type> is what's in our queue
+ * <field> is the chain field in (*<type>)
+ */
+#define queue_iterate(head, elt, type, field) \
+ for ((elt) = (type) queue_first(head); \
+ !queue_end((head), (queue_entry_t)(elt)); \
+ (elt) = (type) queue_next(&(elt)->field))
+
+
+
+#endif /* _QUEUE_H_ */
diff --git a/serverboot/strfcns.c b/serverboot/strfcns.c
new file mode 100644
index 00000000..cbead7e4
--- /dev/null
+++ b/serverboot/strfcns.c
@@ -0,0 +1,74 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * Character subroutines
+ */
+
+#include <stdarg.h>
+
+#define EXPORT_BOOLEAN
+#include <mach/boolean.h>
+
+/*
+ * Concatenate a group of strings together into a buffer.
+ * Return a pointer to the trailing '\0' character in
+ * the result string.
+ * The list of strings ends with a '(char *)0'.
+ */
+/*VARARGS1*/
+char *
+strbuild(char *dest, ...)
+{
+ va_list argptr;
+ register char * src;
+ register int c;
+
+ va_start(argptr, dest);
+ while ((src = va_arg(argptr, char *)) != (char *)0) {
+
+ while ((c = *src++) != '\0')
+ *dest++ = c;
+ }
+ *dest = '\0';
+ va_end(argptr);
+ return (dest);
+}
+
+/*
+ * Return TRUE if string 2 is a prefix of string 1.
+ */
+boolean_t
+strprefix(s1, s2)
+ register char *s1, *s2;
+{
+ register int c;
+
+ while ((c = *s2++) != '\0') {
+ if (c != *s1++)
+ return (FALSE);
+ }
+ return (TRUE);
+}
diff --git a/serverboot/wiring.c b/serverboot/wiring.c
new file mode 100644
index 00000000..585a3075
--- /dev/null
+++ b/serverboot/wiring.c
@@ -0,0 +1,175 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * Package to wire current task's memory.
+ */
+#include <mach.h>
+#include <mach_init.h>
+#include <mach/machine/vm_param.h>
+
+mach_port_t this_task; /* our task */
+mach_port_t priv_host_port = MACH_PORT_NULL;
+ /* the privileged host port */
+
+void
+wire_setup(host_priv)
+ mach_port_t host_priv;
+{
+ priv_host_port = host_priv;
+ this_task = mach_task_self();
+}
+
+void
+wire_memory(start, size, prot)
+ vm_address_t start;
+ vm_size_t size;
+ vm_prot_t prot;
+{
+ kern_return_t kr;
+
+ if (priv_host_port == MACH_PORT_NULL)
+ return;
+
+ kr = vm_wire(priv_host_port,
+ this_task,
+ start, size, prot);
+ if (kr != KERN_SUCCESS)
+ panic("mem_wire: %d", kr);
+}
+
+void
+wire_thread()
+{
+ kern_return_t kr;
+
+ if (priv_host_port == MACH_PORT_NULL)
+ return;
+
+ kr = thread_wire(priv_host_port,
+ mach_thread_self(),
+ TRUE);
+ if (kr != KERN_SUCCESS)
+ panic("wire_thread: %d", kr);
+}
+
+void
+wire_all_memory()
+{
+ register kern_return_t kr;
+ vm_offset_t address;
+ vm_size_t size;
+ vm_prot_t protection;
+ vm_prot_t max_protection;
+ vm_inherit_t inheritance;
+ boolean_t is_shared;
+ memory_object_name_t object;
+ vm_offset_t offset;
+
+ if (priv_host_port == MACH_PORT_NULL)
+ return;
+
+ /* iterate thru all regions, wiring */
+ address = 0;
+ while (
+ (kr = vm_region(this_task, &address,
+ &size,
+ &protection,
+ &max_protection,
+ &inheritance,
+ &is_shared,
+ &object,
+ &offset))
+ == KERN_SUCCESS)
+ {
+ if (MACH_PORT_VALID(object))
+ (void) mach_port_deallocate(this_task, object);
+ if (protection != VM_PROT_NONE)
+ {
+ /* The VM system cannot cope with a COW fault on another
+ unrelated virtual copy happening later when we have
+ wired down the original page. So we must touch all our
+ pages before wiring to make sure that only we will ever
+ use them. */
+ void *page;
+ if (!(protection & VM_PROT_WRITE))
+ {
+ kr = vm_protect(this_task, address, size,
+ 0, max_protection);
+ }
+ for (page = (void *) address;
+ page < (void *) (address + size);
+ page += vm_page_size)
+ *(volatile int *) page = *(int *) page;
+
+ wire_memory(address, size, protection);
+
+ if (!(protection & VM_PROT_WRITE))
+ {
+ kr = vm_protect(this_task, address, size,
+ 0, protection);
+ }
+ }
+ address += size;
+ }
+}
+
+/*
+ * Alias for vm_allocate to return wired memory.
+ */
+kern_return_t
+vm_allocate(task, address, size, anywhere)
+ task_t task;
+ vm_address_t *address;
+ vm_size_t size;
+ boolean_t anywhere;
+{
+ kern_return_t kr;
+
+ if (anywhere)
+ *address = VM_MIN_ADDRESS;
+ kr = vm_map(task,
+ address, size, (vm_offset_t) 0, anywhere,
+ MEMORY_OBJECT_NULL, (vm_offset_t)0, FALSE,
+ VM_PROT_DEFAULT, VM_PROT_ALL, VM_INHERIT_DEFAULT);
+ if (kr != KERN_SUCCESS)
+ return kr;
+
+ if (task == this_task)
+ (void) vm_wire(priv_host_port, task, *address, size,
+ VM_PROT_DEFAULT);
+ return KERN_SUCCESS;
+}
+
+/* Other versions of this function in libc... */
+kern_return_t
+__vm_allocate (task, address, size, anywhere)
+ task_t task;
+ vm_address_t *address;
+ vm_size_t size;
+ boolean_t anywhere;
+{
+ return vm_allocate (task, address, size, anywhere);
+}
diff --git a/serverboot/wiring.h b/serverboot/wiring.h
new file mode 100644
index 00000000..b5f8e53f
--- /dev/null
+++ b/serverboot/wiring.h
@@ -0,0 +1,35 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon the
+ * rights to redistribute these changes.
+ */
+/*
+ * Package to wire current task's memory.
+ */
+#include <mach.h>
+#include <mach_init.h>
+
+extern void wire_setup(/* mach_port_t host_priv */);
+extern void wire_memory(/* vm_address_t, vm_size_t, vm_prot_t */);
+extern void wire_thread();
+extern void wire_all_memory();
diff --git a/ufs-fsck/Makefile b/ufs-fsck/Makefile
new file mode 100644
index 00000000..a484428e
--- /dev/null
+++ b/ufs-fsck/Makefile
@@ -0,0 +1,35 @@
+#
+# Copyright (C) 1994, 1995, 1996 Free Software Foundation
+# Written by Michael I. Bushnell.
+#
+# This file is part of the GNU Hurd.
+#
+# The GNU Hurd is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2, or (at
+# your option) any later version.
+#
+# The GNU Hurd is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+dir := ufs-fsck
+makemode := utility
+
+SRCS = dir.c main.c pass1.c pass1b.c pass2.c pass3.c pass4.c \
+ pass5.c setup.c utilities.c inode.c
+OBJS = $(subst .c,.o,$(SRCS)) tables.o
+LCLHDRS = fsck.h
+target = fsck.ufs
+installationdir = $(sbindir)
+HURDLIBS=shouldbeinlibc
+
+include ../Makeconf
+
+vpath tables.c $(top_srcdir)/ufs
+
diff --git a/ufs-fsck/dir.c b/ufs-fsck/dir.c
new file mode 100644
index 00000000..85757b16
--- /dev/null
+++ b/ufs-fsck/dir.c
@@ -0,0 +1,567 @@
+/* Directory management subroutines
+ Copyright (C) 1994,96,99,2002 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "fsck.h"
+
+/* This routine is used in pass 1 to initialize DIRARRAY and DIRSORTED.
+ Copy information from DP (for number NUMBER) into a newly allocated
+ dirinfo structure and add it to the arrays. */
+void
+record_directory (struct dinode *dp, ino_t number)
+{
+ u_int blks;
+ struct dirinfo *dnp;
+
+ blks = howmany (dp->di_size, sblock->fs_bsize);
+ if (blks > NDADDR)
+ blks = NDADDR + NIADDR;
+ blks *= sizeof (daddr_t);
+ dnp = malloc (sizeof (struct dirinfo) + blks);
+
+ dnp->i_number = number;
+ dnp->i_parent = dnp->i_dotdot = 0;
+ dnp->i_isize = dp->di_size;
+ dnp->i_numblks = blks;
+ bcopy (dp->di_db, dnp->i_blks, blks);
+
+ if (dirarrayused == dirarraysize)
+ {
+ if (dirarraysize == 0)
+ {
+ dirarraysize = 100;
+ dirarray = malloc (dirarraysize * sizeof (struct dirinfo *));
+ dirsorted = malloc (dirarraysize * sizeof (struct dirinfo *));
+ }
+ else
+ {
+ dirarraysize *= 2;
+ dirarray = realloc (dirarray,
+ dirarraysize * sizeof (struct dirinfo *));
+ dirsorted = realloc (dirsorted,
+ dirarraysize * sizeof (struct dirinfo *));
+ }
+ }
+ dirarray[dirarrayused] = dnp;
+ dirsorted[dirarrayused] = dnp;
+ dirarrayused++;
+}
+
+/* Return the cached dirinfo structure for directory INO. */
+struct dirinfo *
+lookup_directory (ino_t ino)
+{
+ int i;
+
+ for (i = 0; i < dirarrayused; i++)
+ if (dirarray[i]->i_number == ino)
+ return dirarray[i];
+
+ errexit ("Cannot find cached directory I=%Ld\n", ino);
+}
+
+/* Check to see if DIR is really a readable directory; if it
+ isn't, then bail with an appropriate message and return 0;
+ else return 1. MSG identifies the action contemplated */
+static int
+validdir (ino_t dir, char *action)
+{
+ switch (inodestate[dir])
+ {
+ case DIRECTORY:
+ case DIRECTORY|DIR_REF:
+ return 1;
+
+ case UNALLOC:
+ warning (1, "CANNOT %s I=%Ld; NOT ALLOCATED", action, dir);
+ return 0;
+
+ case BADDIR:
+ warning (1, "CANNOT %s I=%Ld; BAD BLOCKS", action, dir);
+ return 0;
+
+ case REG:
+ warning (1, "CANNOT %s I=%Ld; NOT DIRECTORY", action, dir);
+ return 0;
+
+ default:
+ errexit ("ILLEGAL STATE");
+ }
+}
+
+/* Search directory DIR for name NAME. If NAME is found, then
+ set *INO to the inode of the entry; otherwise clear INO. Returns 1 if all
+ was normal, or 0 if there was some error doing the search. */
+int
+searchdir (ino_t dir, char *name, ino_t *ino)
+{
+ struct dinode dino;
+ int len;
+
+ /* Scan through one directory block and see if it
+ contains NAME. */
+ void
+ check1block (void *buf)
+ {
+ struct directory_entry *dp;
+
+ for (dp = buf; (void *)dp - buf < DIRBLKSIZ;
+ dp = (struct directory_entry *) ((void *)dp + dp->d_reclen))
+ {
+ if (dp->d_reclen == 0
+ || dp->d_reclen + (void *)dp - buf > DIRBLKSIZ)
+ return;
+
+ if (dp->d_ino == 0 || dp->d_ino > maxino)
+ continue;
+
+ if (DIRECT_NAMLEN (dp) == len && strcmp (dp->d_name, name) == 0)
+ {
+ *ino = dp->d_ino;
+ return;
+ }
+ }
+ }
+
+ /* Read part of a directory and look to see if it contains
+ NAME. Return 1 if we should keep looking at more
+ blocks. */
+ int
+ checkdirblock (daddr_t bno, int nfrags, off_t offset)
+ {
+ void *buf = alloca (nfrags * sblock->fs_fsize);
+ void *bufp;
+
+ readblock (fsbtodb (sblock, bno), buf, nfrags * sblock->fs_fsize);
+ for (bufp = buf;
+ bufp - buf < nfrags * sblock->fs_fsize
+ && offset + (bufp - buf) + DIRBLKSIZ <= dino.di_size;
+ bufp += DIRBLKSIZ)
+ {
+ check1block (bufp);
+ if (*ino)
+ return 0;
+ }
+ return 1;
+ }
+
+ *ino = 0;
+
+ if (!validdir (dir, "READ"))
+ return 0;
+
+ getinode (dir, &dino);
+
+ len = strlen (name);
+ datablocks_iterate (&dino, checkdirblock);
+
+ return 1;
+}
+
+/* Change the existing entry in DIR for name NAME to be
+ inode INO. Return 1 if the entry was found and
+ replaced, else return 0. */
+int
+changeino (ino_t dir, char *name, ino_t ino)
+{
+ struct dinode dino;
+ int len;
+ int madechange;
+
+ /* Scan through a directory block looking for NAME;
+ if we find it then change the inode pointer to point
+ at INO and return 1; if we don't find it then return 0. */
+ int
+ check1block (void *buf)
+ {
+ struct directory_entry *dp;
+
+ for (dp = buf; (void *)dp - buf < DIRBLKSIZ;
+ dp = (struct directory_entry *) ((void *)dp + dp->d_reclen))
+ {
+ if (dp->d_reclen == 0
+ || dp->d_reclen + (void *)dp - buf > DIRBLKSIZ)
+ return 0;
+
+ if (dp->d_ino == 0 || dp->d_ino > maxino)
+ continue;
+
+ if (DIRECT_NAMLEN (dp) == len && strcmp (dp->d_name, name) == 0)
+ {
+ dp->d_ino = ino;
+ madechange = 1;
+ return 1;
+ }
+ }
+ return 0;
+ }
+
+ /* Read part of a directory and look to see if it
+ contains NAME. Return 1 if we should keep looking
+ at more blocks. */
+ int
+ checkdirblock (daddr_t bno, int nfrags, off_t offset)
+ {
+ void *buf = alloca (nfrags * sblock->fs_fsize);
+ void *bufp;
+
+ readblock (fsbtodb (sblock, bno), buf, nfrags * sblock->fs_fsize);
+ for (bufp = buf;
+ bufp - buf < nfrags * sblock->fs_fsize
+ && offset + (bufp - buf) + DIRBLKSIZ <= dino.di_size;
+ bufp += DIRBLKSIZ)
+ {
+ if (check1block (bufp))
+ {
+ writeblock (fsbtodb (sblock, bno), buf,
+ nfrags * sblock->fs_fsize);
+ return 0;
+ }
+ }
+ return 1;
+ }
+
+ if (!validdir (dir, "REWRITE"))
+ return 0;
+
+ getinode (dir, &dino);
+ len = strlen (name);
+ madechange = 0;
+ datablocks_iterate (&dino, checkdirblock);
+ return madechange;
+}
+
+/* Attempt to expand the size of a directory. Return
+ 1 if we succeeded. */
+static int
+expanddir (struct dinode *dp)
+{
+ daddr_t lastbn, newblk;
+ char *cp, buf[sblock->fs_bsize];
+
+ lastbn = lblkno (sblock, dp->di_size);
+ if (blkoff (sblock, dp->di_size) && lastbn >= NDADDR - 1)
+ return 0;
+ else if (!blkoff (sblock, dp->di_size) && lastbn >= NDADDR)
+ return 0;
+ else if (blkoff (sblock, dp->di_size) && !dp->di_db[lastbn])
+ return 0;
+ else if (!blkoff (sblock, dp->di_size) && dp->di_db[lastbn])
+ return 0;
+
+ newblk = allocblk (sblock->fs_frag);
+ if (!newblk)
+ return 0;
+
+ if (blkoff (sblock, dp->di_size))
+ dp->di_db[lastbn + 1] = dp->di_db[lastbn];
+ dp->di_db[lastbn] = newblk;
+ dp->di_size += sblock->fs_bsize;
+ dp->di_blocks += sblock->fs_bsize / DEV_BSIZE;
+
+ for (cp = buf; cp < buf + sblock->fs_bsize; cp += DIRBLKSIZ)
+ {
+ struct directory_entry *dir = (struct directory_entry *) cp;
+ dir->d_ino = 0;
+ dir->d_reclen = DIRBLKSIZ;
+ }
+
+ writeblock (fsbtodb (sblock, newblk), buf, sblock->fs_bsize);
+ return 1;
+}
+
+/* Add a new link into directory DIR with name NAME and target
+ INO. Return 1 if we succeeded and 0 if we failed. It is
+ an error to call this routine if NAME is already present
+ in DIR. */
+int
+makeentry (ino_t dir, ino_t ino, char *name)
+{
+ int len;
+ struct dinode dino;
+ int needed;
+ int madeentry;
+
+ /* Read a directory block and see if it contains room for the
+ new entry. If so, add it and return 1; otherwise return 0. */
+ int
+ check1block (void *buf)
+ {
+ struct directory_entry *dp;
+
+ for (dp = buf; (void *)dp - buf < DIRBLKSIZ;
+ dp = (struct directory_entry *) ((void *)dp + dp->d_reclen))
+ {
+ if (dp->d_reclen == 0
+ || dp->d_reclen + (void *)dp - buf > DIRBLKSIZ)
+ return 0;
+ if (dp->d_ino
+ && dp->d_reclen - DIRSIZ (DIRECT_NAMLEN (dp)) >= needed)
+ {
+ struct directory_entry *newdp;
+ newdp = (struct directory_entry *)
+ ((void *)dp + DIRSIZ (DIRECT_NAMLEN (dp)));
+
+ newdp->d_reclen = dp->d_reclen - DIRSIZ (DIRECT_NAMLEN (dp));
+ DIRECT_NAMLEN (newdp) = len;
+ newdp->d_ino = ino;
+ if (direct_symlink_extension)
+ newdp->d_type = typemap[ino];
+ bcopy (name, newdp->d_name, len + 1);
+
+ dp->d_reclen -= newdp->d_reclen;
+ madeentry = 1;
+ return 1;
+ }
+ else if (!dp->d_ino && dp->d_reclen >= needed)
+ {
+ DIRECT_NAMLEN (dp) = len;
+ dp->d_ino = ino;
+ if (direct_symlink_extension)
+ dp->d_type = typemap[ino];
+ bcopy (name, dp->d_name, len + 1);
+ madeentry = 1;
+ return 1;
+ }
+ }
+ return 0;
+ }
+
+ /* Read part of a directory and look to see if it
+ contains NAME. Return 1 if we should keep looking
+ at more blocks. */
+ int
+ checkdirblock (daddr_t bno, int nfrags, off_t offset)
+ {
+ void *buf = alloca (nfrags * sblock->fs_fsize);
+ void *bufp;
+
+ readblock (fsbtodb (sblock, bno), buf, nfrags * sblock->fs_fsize);
+ for (bufp = buf;
+ bufp - buf < nfrags * sblock->fs_fsize
+ && offset + (bufp - buf) + DIRBLKSIZ <= dino.di_size;
+ bufp += DIRBLKSIZ)
+ {
+ if (check1block (bufp))
+ {
+ writeblock (fsbtodb (sblock, bno), buf,
+ nfrags * sblock->fs_fsize);
+ return 0;
+ }
+ }
+ return 1;
+ }
+
+ if (!validdir (dir, "MODIFY"))
+ return 0;
+
+ getinode (dir, &dino);
+ len = strlen (name);
+ needed = DIRSIZ (len);
+ madeentry = 0;
+ datablocks_iterate (&dino, checkdirblock);
+ if (!madeentry)
+ {
+ /* Attempt to expand the directory. */
+ problem (0, "NO SPACE LEFT IN DIR INO=%Ld", dir);
+ if (preen || reply ("EXPAND"))
+ {
+ if (expanddir (&dino))
+ {
+ write_inode (ino, &dino);
+ datablocks_iterate (&dino, checkdirblock);
+ pfix ("EXPANDED");
+ }
+ else
+ {
+ pfail (0);
+ warning (1, "CANNOT EXPAND DIRECTORY");
+ }
+ }
+ }
+ return madeentry;
+}
+
+/* Create a directory node whose parent is to be PARENT, whose inode
+ is REQUEST, and whose mode is to be MODE. If REQUEST is zero, then
+ allocate any inode. Initialze the contents of the
+ directory. Return the inode of the new directory. */
+ino_t
+allocdir (ino_t parent, ino_t request, mode_t mode)
+{
+ ino_t ino;
+
+ mode |= IFDIR;
+
+ ino = allocino (request, mode);
+ if (!ino)
+ return 0;
+ if (!makeentry (ino, ino, "."))
+ goto bad;
+ if (!makeentry (ino, parent, ".."))
+ goto bad;
+
+ linkfound[ino]++;
+ linkfound[parent]++;
+ return ino;
+
+ bad:
+ freeino (ino);
+ return 0;
+}
+
+/* Link node INO into lost+found. If PARENT is positive then INO is
+ a directory, and PARENT is the number of `..' as found in INO.
+ If PARENT is zero then INO is a directory without any .. entry.
+ If the node could be linked, return 1; else return 0. */
+int
+linkup (ino_t ino, ino_t parent)
+{
+ int search_failed;
+ struct dinode lfdino;
+ char *tempname;
+ ino_t foo;
+
+ if (lfdir == 0)
+ {
+ if (!searchdir (ROOTINO, lfname, &lfdir))
+ {
+ warning (1, "FAILURE SEARCHING FOR `%s'", lfname);
+ return 0;
+ }
+ if (lfdir == 0)
+ {
+ problem (0, "NO `%s' DIRECTORY", lfname);
+ if (preen || reply ("CREATE"))
+ {
+ lfdir = allocdir (ROOTINO, 0, lfmode);
+ if (lfdir != 0)
+ {
+ if (! makeentry (ROOTINO, lfdir, lfname))
+ {
+ freeino (lfdir);
+ linkfound[ROOTINO]--;
+ lfdir = 0;
+ }
+ }
+ }
+ if (lfdir)
+ pfix ("CREATED");
+ else
+ {
+ pfail (0);
+ warning (1, "SORRY, CANNOT CREATE `%s' DIRECTORY", lfname);
+ return 0;
+ }
+ }
+ }
+
+ getinode (lfdir, &lfdino);
+ if ((lfdino.di_model & IFMT) != IFDIR)
+ {
+ ino_t oldlfdir;
+
+ problem (1, "`%s' IS NOT A DIRECTORY", lfname);
+ if (! reply ("REALLOCATE"))
+ return 0;
+
+ oldlfdir = lfdir;
+
+ lfdir = allocdir (ROOTINO, 0, lfmode);
+ if (!lfdir)
+ {
+ warning (1, "SORRY, CANNOT CREATE `%s' DIRECTORY", lfname);
+ return 0;
+ }
+ if (!changeino (ROOTINO, lfname, lfdir))
+ {
+ warning (1, "SORRY, CANNOT CREATE `%s' DIRECTORY", lfname);
+ return 0;
+ }
+
+ /* One less link to the old one */
+ linkfound[oldlfdir]--;
+
+ getinode (lfdir, &lfdino);
+ }
+
+ if (inodestate[lfdir] != DIRECTORY && inodestate[lfdir] != (DIRECTORY|DIR_REF))
+ {
+ warning (1, "SORRY. `%s' DIRECTORY NOT ALLOCATED", lfname);
+ return 0;
+ }
+
+ asprintf (&tempname, "#%Ld", ino);
+ search_failed = !searchdir (lfdir, tempname, &foo);
+ while (foo)
+ {
+ char *newname;
+ asprintf (&newname, "%sa", tempname);
+ free (tempname);
+ tempname = newname;
+ search_failed = !searchdir (lfdir, tempname, &foo);
+ }
+ if (search_failed)
+ {
+ warning (1, "FAILURE SEARCHING FOR `%s' IN `%s'", tempname, lfname);
+ free (tempname);
+ return 0;
+ }
+ if (!makeentry (lfdir, ino, tempname))
+ {
+ free (tempname);
+ warning (1, "SORRY, NO SPACE IN `%s' DIRECTORY", lfname);
+ return 0;
+ }
+ free (tempname);
+ linkfound[ino]++;
+
+ if (parent != -1)
+ {
+ /* Reset `..' in ino */
+ if (parent)
+ {
+ if (!changeino (ino, "..", lfdir))
+ {
+ warning (1, "CANNOT ADJUST `..' LINK I=%Ld", ino);
+ return 0;
+ }
+ /* Forget about link to old parent */
+ linkfound[parent]--;
+ }
+ else if (!makeentry (ino, lfdir, ".."))
+ {
+ warning (1, "CANNOT CREAT `..' LINK I=%Ld", ino);
+ return 0;
+ }
+
+ /* Account for link to lost+found; update inode directly
+ here to avoid confusing warning later. */
+ linkfound[lfdir]++;
+ linkcount[lfdir]++;
+ lfdino.di_nlink++;
+ write_inode (lfdir, &lfdino);
+
+ if (parent)
+ warning (0, "DIR I=%Ld CONNECTED; PARENT WAS I=%Ld", ino, parent);
+ else
+ warning (0, "DIR I=%Ld CONNECTED", ino);
+ }
+ return 1;
+}
diff --git a/ufs-fsck/fsck.h b/ufs-fsck/fsck.h
new file mode 100644
index 00000000..4a5dabf5
--- /dev/null
+++ b/ufs-fsck/fsck.h
@@ -0,0 +1,183 @@
+/*
+ Copyright (C) 1994,95,96,2002 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <dirent.h>
+
+#define swab_disk 0
+
+#include "../ufs/fs.h"
+#include "../ufs/dinode.h"
+#include "../ufs/dir.h"
+
+/* Type of an inode */
+#define UNALLOC 0
+#define REG 1
+#define DIRECTORY 2
+#define BADDIR 3
+
+/* Added to directories in pass 2 */
+#define DIR_REF 4 /* dir has been found in connectivity search */
+
+/* State of each inode (set by pass 1) */
+char *inodestate;
+
+/* Number of links claimed by each inode (set by pass 1) */
+nlink_t *linkcount;
+
+/* Number of links found to each inode (set by pass 2) */
+nlink_t *linkfound;
+
+/* DT_foo type of each inode (set by pass 1) */
+char *typemap;
+
+/* Map of blocks allocated */
+char *blockmap;
+
+/* A string identifying what we're trying to check. */
+extern char *device_name;
+
+
+/* Command line flags */
+int nowrite; /* all questions fail */
+int noquery; /* all questions succeed */
+
+
+enum contret
+{
+ RET_STOP,
+ RET_GOOD,
+ RET_BAD,
+};
+
+
+/* One of these structures is set up for each directory by
+ pass 1 and used by passes 2 and 3. */
+struct dirinfo
+{
+ struct inoinfo *i_nexthash; /* next entry in hash chain */
+ ino_t i_number; /* inode entry of this dir */
+ ino_t i_parent; /* inode entry of parent */
+ ino_t i_dotdot; /* inode number of `..' */
+ ino_t i_dot; /* inode number of `.' */
+ u_int i_isize; /* size of inode */
+ u_int i_numblks; /* size of block array in bytes */
+ daddr_t i_blks[0]; /* array of inode block addresses */
+};
+
+/* Array of all the dirinfo structures in inode number order */
+struct dirinfo **dirarray;
+
+/* Array of all thi dirinfo structures sorted by their first
+ block address */
+struct dirinfo **dirsorted;
+
+int dirarrayused; /* number of directories */
+int dirarraysize; /* alloced size of dirarray/dirsorted */
+
+struct dups {
+ struct dups *next;
+ daddr_t dup;
+};
+struct dups *duplist; /* head of dup list */
+struct dups *muldup; /* end of unique duplicate dup block numbers */
+
+
+extern struct fs *sblock;
+
+extern daddr_t maxfsblock;
+extern int maxino;
+extern int direct_symlink_extension;
+
+extern int newinofmt;
+
+/* Terse automatic mode for noninteractive use; punts on severe problems. */
+extern int preen;
+
+extern int readfd, writefd;
+
+extern int fix_denied;
+
+extern int fsmodified;
+
+extern ino_t lfdir;
+
+/* Total number of files found on the partition. */
+extern long num_files;
+
+extern mode_t lfmode;
+extern char *lfname;
+
+#define NBBY 8
+#define howmany(x,y) (((x)+((y)-1))/(y))
+#define roundup(x, y) ((((x)+((y)-1))/(y))*(y))
+#define isclr(a, i) (((a)[(i)/NBBY] & (1<<((i)%NBBY))) == 0)
+#define isset(a, i) ((a)[(i)/NBBY] & (1<<((i)%NBBY)))
+#define setbit(a,i) ((a)[(i)/NBBY] |= 1<<((i)%NBBY))
+#define clrbit(a,i) ((a)[(i)/NBBY] &= ~(1<<(i)%NBBY))
+#define DEV_BSIZE 512
+
+#define setbmap(blkno) setbit (blockmap, blkno)
+#define testbmap(blkno) isset (blockmap, blkno)
+#define clrbmap(blkno) clrbit (blockmap, blkno)
+
+#define DI_MODE(dp) (((dp)->di_modeh << 16) | (dp)->di_model)
+
+
+
+int setup (char *);
+void pass1 (), pass1b (), pass2 (), pass3 (), pass4 (), pass5 ();
+
+void readblock (daddr_t, void *, size_t);
+void writeblock (daddr_t, void *, size_t);
+
+void getinode (ino_t, struct dinode *);
+void write_inode (ino_t, struct dinode *);
+void clear_inode (ino_t, struct dinode *);
+
+daddr_t allocblk (int);
+int check_range (daddr_t, int);
+
+ino_t allocino (ino_t, mode_t);
+void freeino (ino_t);
+ino_t allocdir (ino_t, ino_t, mode_t);
+
+int makeentry (ino_t, ino_t, char *);
+int changeino (ino_t, char *, ino_t);
+
+int linkup (ino_t, ino_t);
+
+void datablocks_iterate (struct dinode *, int (*)(daddr_t, int, off_t));
+void allblock_iterate (struct dinode *, int (*)(daddr_t, int, off_t));
+
+void record_directory (struct dinode *, ino_t);
+struct dirinfo *lookup_directory (ino_t);
+
+void errexit (char *, ...) __attribute__ ((format (printf, 1, 2), noreturn));
+void warning (int, char *, ...) __attribute__ ((format (printf, 2, 3)));
+void problem (int, char *, ...) __attribute__ ((format (printf, 2, 3)));
+void pinode (int, ino_t, char *fmt, ...) __attribute__ ((format (printf, 3, 4)));
+void pextend (char *, ...) __attribute__ ((format (printf, 1, 2)));
+void pfix (char *fix), pfail (char *reason);
+int reply (char *);
diff --git a/ufs-fsck/inode.c b/ufs-fsck/inode.c
new file mode 100644
index 00000000..df4b880e
--- /dev/null
+++ b/ufs-fsck/inode.c
@@ -0,0 +1,203 @@
+/* Inode allocation, deallocation, etc.
+ Copyright (C) 1994, 1996 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "fsck.h"
+
+static void
+inode_iterate (struct dinode *dp,
+ int (*fn) (daddr_t, int, off_t),
+ int doaddrblocks)
+{
+ mode_t mode = dp->di_model & IFMT;
+ int nb, maxb;
+ off_t totaloffset = 0;
+
+ /* Call FN for iblock IBLOCK of level LEVEL and recurse down
+ the indirect block pointers. */
+ int
+ scaniblock (daddr_t iblock, int level)
+ {
+ int cont;
+ daddr_t ptrs[NINDIR(sblock)];
+ int i;
+
+ if (doaddrblocks)
+ {
+ cont = (*fn)(iblock, sblock->fs_frag, totaloffset);
+ if (cont == RET_STOP)
+ return RET_STOP;
+ else if (cont == RET_BAD)
+ return RET_GOOD;
+ }
+
+ readblock (fsbtodb (sblock, iblock), ptrs, sblock->fs_bsize);
+ for (i = 0; i < NINDIR (sblock); i++)
+ {
+ if (!ptrs[i])
+ continue;
+
+ if (level == 0)
+ {
+ cont = (*fn)(ptrs[i], sblock->fs_frag, totaloffset);
+ totaloffset += sblock->fs_bsize;
+ }
+ else
+ cont = scaniblock (ptrs[i], level - 1);
+ if (cont == RET_STOP)
+ return RET_STOP;
+ }
+ return RET_GOOD;
+ }
+
+ if (mode == IFBLK || mode == IFCHR
+ || (mode == IFLNK && sblock->fs_maxsymlinklen != -1
+ && (dp->di_size < sblock->fs_maxsymlinklen
+ || (sblock->fs_maxsymlinklen == 0 && dp->di_blocks == 0))))
+ return;
+
+ maxb = lblkno (sblock, dp->di_size - 1);
+ totaloffset = 0;
+ for (nb = 0; nb < NDADDR; nb++)
+ {
+ int offset;
+ int nfrags;
+
+ if (nb == maxb && (offset = blkoff (sblock, dp->di_size)))
+ nfrags = numfrags (sblock, fragroundup (sblock, offset));
+ else
+ nfrags = sblock->fs_frag;
+
+ if (dp->di_db[nb]
+ && (*fn)(dp->di_db[nb], nfrags, totaloffset) != RET_GOOD)
+ return;
+ totaloffset += nfrags * sizeof (sblock->fs_fsize);
+ }
+
+ for (nb = 0; nb < NIADDR; nb++)
+ if (dp->di_ib[nb] && scaniblock (dp->di_ib[nb], nb) != RET_GOOD)
+ return;
+
+ if (doaddrblocks && dp->di_trans)
+ (*fn)(dp->di_trans, sblock->fs_frag, totaloffset);
+}
+
+void
+datablocks_iterate (struct dinode *dp,
+ int (*fn) (daddr_t, int, off_t))
+{
+ inode_iterate (dp, fn, 0);
+}
+
+void
+allblock_iterate (struct dinode *dp,
+ int (*fn) (daddr_t, int, off_t))
+{
+ inode_iterate (dp, fn, 1);
+}
+
+/* Allocate an inode. If INUM is nonzero, then allocate that
+ node specifically, otherwise allocate any available inode.
+ MODE is the mode of the new file. Return the allocated
+ inode number (or 0 if the allocation failed). */
+ino_t
+allocino (ino_t request, mode_t mode)
+{
+ ino_t ino;
+ struct dinode dino;
+ struct timeval tv;
+
+ if (request)
+ {
+ if (inodestate[request] != UNALLOC)
+ return 0;
+ ino = request;
+ }
+ else
+ {
+ for (ino = ROOTINO; ino < maxino; ino++)
+ if (inodestate[ino] == UNALLOC)
+ break;
+ if (ino == maxino)
+ return 0;
+ }
+
+ if ((mode & IFMT) == IFDIR)
+ inodestate[ino] = DIRECTORY | DIR_REF;
+ else
+ inodestate[ino] = REG;
+
+ getinode (ino, &dino);
+ dino.di_modeh = (mode & 0xffff0000) >> 16;
+ dino.di_model = (mode & 0x0000ffff);
+ gettimeofday (&tv, 0);
+ dino.di_atime.tv_sec = tv.tv_sec;
+ dino.di_atime.tv_nsec = tv.tv_usec * 1000;
+ dino.di_mtime = dino.di_ctime = dino.di_atime;
+ dino.di_size = 0;
+ dino.di_blocks = 0;
+ num_files++;
+ write_inode (ino, &dino);
+ typemap[ino] = IFTODT (mode);
+ return ino;
+}
+
+/* Deallocate inode INUM. */
+void
+freeino (ino_t inum)
+{
+ struct dinode dino;
+
+ int
+ clearblock (daddr_t bno, int nfrags, off_t offset)
+ {
+ int i;
+
+ for (i = 0; i < nfrags; i++)
+ {
+ if (check_range (bno + i, 1))
+ return RET_BAD;
+ if (testbmap (bno + i))
+ {
+ struct dups *dlp;
+ for (dlp = duplist; dlp; dlp = dlp->next)
+ {
+ if (dlp->dup != bno + i)
+ continue;
+ dlp->dup = duplist->dup;
+ dlp = duplist;
+ duplist = duplist->next;
+ free (dlp);
+ break;
+ }
+ if (dlp == 0)
+ clrbmap (bno + i);
+ }
+ }
+ return RET_GOOD;
+ }
+
+ getinode (inum, &dino);
+ allblock_iterate (&dino, clearblock);
+
+ clear_inode (inum, &dino);
+ inodestate[inum] = UNALLOC;
+
+ num_files--;
+}
diff --git a/ufs-fsck/main.c b/ufs-fsck/main.c
new file mode 100644
index 00000000..34c32a67
--- /dev/null
+++ b/ufs-fsck/main.c
@@ -0,0 +1,170 @@
+/* Main program for GNU fsck
+ Copyright (C) 1994, 1996, 1997 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <errno.h>
+#include <argp.h>
+#include <hurd.h>
+#include <version.h>
+
+#include "fsck.h"
+
+const char *argp_program_version = STANDARD_HURD_VERSION (fsck.ufs);
+
+char *lfname = "lost+found";
+mode_t lfmode = 0755;
+
+/* Terse automatic mode for noninteractive use; punts on severe problems. */
+int preen = 0;
+
+/* Total number of files found on the partition. */
+long num_files = 0;
+
+static struct argp_option options[] =
+{
+ {"preen", 'p', 0, 0, "Terse automatic mode", 1},
+ {"yes", 'y', 0, 0, "Automatically answer yes to all questions"},
+ {"no", 'n', 0, 0, "Automatically answer no to all questions"},
+ {"lost+found", 'l', "NAME", 0, "The name of the lost+found directory in /"},
+ {"lf-mode", 'm', "MODE", 0, "The mode of the lost+found directory in /"},
+ {0, 0, 0, 0, "In --preen mode, the following also apply:", 2},
+ {"force", 'f', 0, 0, "Check even if clean"},
+ {"silent", 's', 0, 0, "Only print diagostic messages"},
+ {0, 0}
+};
+char *args_doc = "DEVICE";
+
+/* Returns a malloced buffer containing a nice printable size for FRAGS. */
+static char *
+nice_size (long frags)
+{
+ char *rep;
+ char *units = "KMGT", *u = units;
+ float num = ((float)frags * sblock->fs_fsize) / 1024;
+
+ while (num > 1024)
+ {
+ num /= 1024;
+ u++;
+ }
+
+ asprintf (&rep, num >= 1000 ? "%.0f%c" : "%.3g%c", num, *u);
+
+ return rep;
+}
+
+/* Print summary statistics. */
+static void
+show_stats ()
+{
+ long num_ffree = sblock->fs_cstotal.cs_nffree;
+ long num_bfree = sblock->fs_cstotal.cs_nbfree;
+ long tot_ffree = num_ffree + sblock->fs_frag * num_bfree;
+ char *urep = nice_size (sblock->fs_dsize - tot_ffree);
+ char *frep = nice_size (tot_ffree);
+ warning (0, "%ld files, %s used, %s free (%ld.%ld%% fragmentation)",
+ num_files, urep, frep,
+ (num_ffree * 100) / sblock->fs_dsize,
+ (((num_ffree * 1000 + sblock->fs_dsize / 2) / sblock->fs_dsize)
+ % 10));
+ free (urep);
+ free (frep);
+}
+
+int
+main (int argc, char **argv)
+{
+ int silent = 0, force = 0;
+ char *device = 0;
+ error_t parse_opt (int key, char *arg, struct argp_state *state)
+ {
+ switch (key)
+ {
+ case 'p': preen = 1; break;
+ case 'y': noquery = 1; break;
+ case 'n': nowrite = 1; break;
+ case 'l': lfname = arg; break;
+ case 'm': lfmode = strtol (arg, 0, 8); break;
+ case 'f': force = 1; break;
+ case 's': silent = 1; break;
+ case ARGP_KEY_ARG:
+ if (!device)
+ {
+ device = arg;
+ break;
+ }
+ /* Fall through */
+ case ARGP_KEY_NO_ARGS:
+ argp_usage (state);
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+ }
+ struct argp argp = {options, parse_opt, args_doc};
+
+ preen = nowrite = noquery = 0;
+ argp_parse (&argp, argc, argv, 0, 0, 0);
+
+ if (!setup (device))
+ exit (1);
+
+ if (preen && sblock->fs_clean && !force)
+ {
+ if (! silent)
+ warning (0, "FILESYSTEM CLEAN");
+ }
+ else
+ {
+ if (!preen)
+ printf ("** Phase 1 -- Check Blocks and Sizes\n");
+ pass1 ();
+
+ if (duplist)
+ {
+ if (!preen)
+ printf ("** Phase 1b -- Rescan for More Duplicates\n");
+ pass1b ();
+ }
+
+ if (!preen)
+ printf ("** Phase 2 -- Check Pathnames\n");
+ pass2 ();
+
+ if (!preen)
+ printf ("** Phase 3 -- Check Connectivity\n");
+ pass3 ();
+
+ if (!preen)
+ printf ("** Phase 4 -- Check Reference Counts\n");
+ pass4 ();
+
+ if (!preen)
+ printf ("** Phase 5 -- Check Cyl Groups\n");
+ pass5 ();
+
+ if (! silent)
+ show_stats (sblock);
+ }
+
+ if (fsmodified && !preen)
+ printf ("\n***** FILE SYSTEM WAS MODIFIED *****\n");
+
+ exit (fsmodified ? 2 : 0);
+}
diff --git a/ufs-fsck/pass1.c b/ufs-fsck/pass1.c
new file mode 100644
index 00000000..bd41cc62
--- /dev/null
+++ b/ufs-fsck/pass1.c
@@ -0,0 +1,437 @@
+/* Pass one of GNU fsck -- count blocks and verify inodes
+ Copyright (C) 1994,95,96,2002 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+
+#include "fsck.h"
+
+static struct dinode zino;
+
+/* Find all the blocks in use by files and filesystem reserved blocks.
+ Set them in the global block map. For each file, if a block is found
+ allocated twice, then record the block and inode in DUPLIST.
+ Initialize INODESTATE, LINKCOUNT, and TYPEMAP. */
+void
+pass1 ()
+{
+ ino_t number;
+ ino_t i;
+ int cg;
+ struct dinode dino;
+ struct dinode *dp = &dino;
+ mode_t mode, type;
+ int ndb;
+ int holdallblocks;
+ int lbn;
+ int nblocks;
+ int blkerror;
+ int nblkrngerrors;
+ int nblkduperrors;
+
+ /* This function is called for each block of DP. Check to see
+ if the block number is valid. If so, set the entry in the
+ block map. If the block map entry is already set, then keep
+ track of this block and see if the user wants to clear the
+ node. Increment NBLOCKS by the number of data blocks held.
+ Set BLKERROR if this block is invalid.
+ Return RET_GOOD, RET_BAD, RET_STOP if the block is good,
+ bad, or if we should entirely stop checking blocks in this
+ inode. */
+ int
+ checkblock (daddr_t bno, int nfrags, off_t offset)
+ {
+#define MAXBAD 10
+ int outofrange;
+ struct dups *dlp, *new;
+ int wasbad = 0;
+
+ /* Check to see if this block is in range */
+ outofrange = check_range (bno, nfrags);
+ if (outofrange)
+ {
+ blkerror = 1;
+ wasbad = 1;
+ if (nblkrngerrors == 0)
+ warning (0, "I=%Ld HAS BAD BLOCKS", number);
+ if (nblkrngerrors++ > MAXBAD)
+ {
+ problem (0, "EXCESSIVE BAD BLKS I=%Ld", number);
+ if (preen || reply ("SKIP"))
+ {
+ pfail ("SKIPPING");
+ return RET_STOP;
+ }
+ }
+ }
+
+ for (; nfrags > 0; bno++, nfrags--)
+ {
+ if (outofrange && check_range (bno, 1))
+ warning (0, "BAD BLOCK %lu", bno);
+ else
+ {
+ if (!testbmap (bno))
+ setbmap (bno);
+ else
+ {
+ blkerror = 1;
+ if (nblkduperrors == 0)
+ warning (0, "I=%Ld HAS DUPLICATE BLOCKS", number);
+ warning (0, "DUPLICATE BLOCK %ld", bno);
+ wasbad = 1;
+ if (nblkduperrors++ > MAXBAD)
+ {
+ problem (0, "EXCESSIVE DUP BLKS I=%Ld", number);
+ if (preen || reply ("SKIP"))
+ {
+ pfail ("SKIPPING");
+ return RET_STOP;
+ }
+ }
+ new = malloc (sizeof (struct dups));
+ new->dup = bno;
+ if (muldup == 0)
+ {
+ duplist = muldup = new;
+ new->next = 0;
+ }
+ else
+ {
+ new->next = muldup->next;
+ muldup->next = new;
+ }
+ for (dlp = duplist; dlp != muldup; dlp = dlp->next)
+ if (dlp->dup == bno)
+ break;
+ if (dlp == muldup && dlp->dup != bno)
+ muldup = new;
+ }
+ }
+ nblocks += sblock->fs_fsize / DEV_BSIZE;
+ }
+ return wasbad ? RET_BAD : RET_GOOD;
+ }
+
+
+ /* Account for blocks used by meta data */
+ for (cg = 0; cg < sblock->fs_ncg; cg++)
+ {
+ daddr_t firstdata, firstcgblock, bno;
+
+ /* Each cylinder group past the first reserves data
+ from its cylinder group copy to (but not including)
+ the first datablock.
+
+ The first, however, reserves from the very front of the
+ cylinder group (thus including the boot block), and it also
+ reserves the data blocks holding the csum information. */
+ firstdata = cgdmin (sblock, cg);
+ if (cg == 0)
+ {
+ firstcgblock = cgbase (sblock, cg);
+ firstdata += howmany (sblock->fs_cssize, sblock->fs_fsize);
+ }
+ else
+ firstcgblock = cgsblock (sblock, cg);
+
+ /* Mark the blocks set */
+ for (bno = firstcgblock; bno < firstdata; bno++)
+ setbmap (bno);
+ }
+
+ /* Loop through each inode, doing initial checks */
+ for (number = 0, cg = 0; cg < sblock->fs_ncg; cg++)
+ for (i = 0; i < sblock->fs_ipg; i++, number++)
+ {
+ /* These record whether we've already complained about extra
+ direct/indirect blocks. */
+ int dbwarn = 0, ibwarn = 0;
+
+/* if (!preen && !(number % 10000))
+ printf ("I=%Ld\n", number); */
+
+ if (number < ROOTINO)
+ continue;
+
+ getinode (number, dp);
+ mode = DI_MODE (dp);
+ type = mode & IFMT;
+
+ /* If the node is not allocated, then make sure it's
+ properly clear */
+ if (type == 0)
+ {
+ if (bcmp (dp->di_db, zino.di_db, NDADDR * sizeof (daddr_t))
+ || bcmp (dp->di_ib, zino.di_ib, NIADDR * sizeof (daddr_t))
+ || dp->di_trans
+ || DI_MODE (dp)
+ || dp->di_size)
+ {
+ problem (0, "PARTIALLY ALLOCATED INODE I=%Ld", number);
+ if (preen || reply ("CLEAR"))
+ {
+ clear_inode (number, dp);
+ pfix ("CLEARED");
+ }
+ }
+ inodestate[number] = UNALLOC;
+ }
+ else
+ {
+ /* Node is allocated. */
+
+ /* Check to see if we think the node should be cleared */
+
+ /* Verify size for basic validity */
+ holdallblocks = 0;
+
+ if (dp->di_size + sblock->fs_bsize - 1 < dp->di_size)
+ {
+ problem (1, "OVERFLOW IN FILE SIZE I=%Ld (SIZE == %lld)", number,
+ dp->di_size);
+ if (reply ("CLEAR"))
+ {
+ clear_inode (number, dp);
+ inodestate[number] = UNALLOC;
+ continue;
+ }
+ inodestate[number] = UNALLOC;
+ warning (0, "WILL TREAT ANY BLOCKS HELD BY I=%Ld AS ALLOCATED",
+ number);
+ holdallblocks = 1;
+ }
+
+ /* Decode type and set NDB
+ also set inodestate correctly. */
+ inodestate[number] = REG;
+ switch (type)
+ {
+ case IFBLK:
+ case IFCHR:
+ ndb = 1;
+ break;
+
+ case IFIFO:
+ case IFSOCK:
+ ndb = 0;
+ break;
+
+ case IFLNK:
+ if (sblock->fs_maxsymlinklen != -1)
+ {
+ /* Check to see if this is a fastlink. The
+ old fast link format has fs_maxsymlinklen
+ of zero and di_blocks zero; the new format has
+ fs_maxsymlinklen set and we ignore di_blocks.
+ So check for either. */
+ if ((sblock->fs_maxsymlinklen
+ && dp->di_size < sblock->fs_maxsymlinklen)
+ || (!sblock->fs_maxsymlinklen && !dp->di_blocks))
+ {
+ /* Fake NDB value so that we will check
+ all the block pointers past the symlink */
+ ndb = howmany (dp->di_size, sizeof (daddr_t));
+ if (ndb > NDADDR)
+ {
+ int j = ndb - NDADDR;
+ for (ndb = 1; j > 1; i--)
+ ndb *= NINDIR (sblock);
+ ndb += NDADDR;
+ }
+ }
+ else
+ ndb = howmany (dp->di_size, sblock->fs_bsize);
+ }
+ else
+ ndb = howmany (dp->di_size, sblock->fs_bsize);
+ break;
+
+ case IFDIR:
+ inodestate[number] = DIRECTORY;
+ /* Fall through */
+ case IFREG:
+ ndb = howmany (dp->di_size, sblock->fs_bsize);
+ break;
+
+ default:
+ problem (1, "UNKNOWN FILE TYPE I=%Ld (MODE=%ol)", number, mode);
+ if (reply ("CLEAR"))
+ {
+ clear_inode (number, dp);
+ inodestate[number] = UNALLOC;
+ continue;
+ }
+ inodestate[number] = UNALLOC;
+ holdallblocks = 1;
+ warning (0, "WILL TREAT ANY BLOCKS HELD BY I=%Ld "
+ "AS ALLOCATED", number);
+ ndb = 0;
+ }
+
+ if (ndb < 0)
+ {
+ problem (1, "BAD FILE SIZE I= %Ld (SIZE == %lld)", number,
+ dp->di_size);
+ if (reply ("CLEAR"))
+ {
+ clear_inode (number, dp);
+ inodestate[number] = UNALLOC;
+ continue;
+ }
+ inodestate[number] = UNALLOC;
+ warning (0, "WILL TREAT ANY BLOCKS HELD BY I=%Ld AS ALLOCATED",
+ number);
+ holdallblocks = 1;
+ }
+
+ /* Make sure that direct and indirect block pointers
+ past the file size are zero. If the size is bogus, then
+ don't bother (they should all be zero, but the user has
+ requested that they be treated as allocated). */
+ if (!holdallblocks)
+ {
+ if (dp->di_size
+ && (type == IFBLK || type == IFCHR
+ || type == IFSOCK || type == IFIFO))
+ {
+ problem (1, "SPECIAL NODE I=%Ld (MODE=%ol) HAS SIZE %lld",
+ number, mode, dp->di_size);
+ if (reply ("TRUNCATE"))
+ {
+ dp->di_size = 0;
+ write_inode (number, dp);
+ }
+ }
+
+ /* If we haven't set NDB speciall above, then it is set from
+ the file size correctly by the size check. */
+
+ /* Check all the direct and indirect blocks that are past the
+ amount necessary to be zero. */
+ for (lbn = ndb; lbn < NDADDR; lbn++)
+ {
+ if (dp->di_db[lbn])
+ {
+ if (!dbwarn)
+ {
+ dbwarn = 1;
+ problem (0, "INODE I=%Ld HAS EXTRA DIRECT BLOCKS",
+ number);
+ if (preen || reply ("DEALLOCATE"))
+ {
+ dp->di_db[lbn] = 0;
+ dbwarn = 2;
+ pfix ("DEALLOCATED");
+ }
+ }
+ else if (dbwarn == 2)
+ dp->di_db[lbn] = 0;
+ }
+ if (dbwarn == 2)
+ write_inode (number, dp);
+ }
+
+ for (lbn = 0, ndb -= NDADDR; ndb > 0; lbn++)
+ ndb /= NINDIR (sblock);
+ for (; lbn < NIADDR; lbn++)
+ {
+ if (dp->di_ib[lbn])
+ {
+ if (ibwarn)
+ {
+ ibwarn = 1;
+ problem (0, "INODE I=%Ld HAS EXTRA INDIRECT BLOCKS",
+ number);
+ if (preen || reply ("DEALLOCATE"))
+ {
+ dp->di_ib[lbn] = 0;
+ ibwarn = 2;
+ pfix ("DEALLOCATED");
+ }
+ }
+ else if (ibwarn == 2)
+ dp->di_ib[lbn] = 0;
+ }
+ if (ibwarn == 2)
+ write_inode (number, dp);
+ }
+ }
+
+ /* If this node is really allocated (as opposed to something
+ that we should clear but the user won't) then set LINKCOUNT
+ and TYPEMAP entries. */
+ if (inodestate[number] != UNALLOC)
+ {
+ linkcount[number] = dp->di_nlink;
+ typemap[number] = IFTODT (mode);
+ }
+
+ /* Iterate over the blocks of the file,
+ calling CHECKBLOCK for each file. */
+ nblocks = 0;
+ blkerror = 0;
+ nblkduperrors = 0;
+ nblkrngerrors = 0;
+ allblock_iterate (dp, checkblock);
+
+ if (blkerror)
+ {
+ if (preen)
+ warning (1, "DUPLICATE or BAD BLOCKS");
+ else
+ {
+ problem (0, "I=%Ld has ", number);
+ if (nblkduperrors)
+ {
+ pextend ("%d DUPLICATE BLOCKS", nblkduperrors);
+ if (nblkrngerrors)
+ pextend (" and ");
+ }
+ if (nblkrngerrors)
+ pextend ("%d BAD BLOCKS", nblkrngerrors);
+ if (reply ("CLEAR"))
+ {
+ clear_inode (number, dp);
+ inodestate[number] = UNALLOC;
+ continue;
+ }
+ else if (inodestate[number] == DIRECTORY)
+ inodestate[number] = BADDIR;
+ }
+ }
+ else if (dp->di_blocks != nblocks)
+ {
+ problem (0, "INCORRECT BLOCK COUNT I=%Ld (%ld should be %d)",
+ number, dp->di_blocks, nblocks);
+ if (preen || reply ("CORRECT"))
+ {
+ dp->di_blocks = nblocks;
+ write_inode (number, dp);
+ pfix ("CORRECTED");
+ }
+ }
+
+ num_files++;
+
+ if (type == IFDIR)
+ record_directory (dp, number);
+ }
+ }
+}
diff --git a/ufs-fsck/pass1b.c b/ufs-fsck/pass1b.c
new file mode 100644
index 00000000..4da86974
--- /dev/null
+++ b/ufs-fsck/pass1b.c
@@ -0,0 +1,90 @@
+/* Pass 1b of fsck -- scan inodes for references to duplicate blocks
+ Copyright (C) 1994,96,2002 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "fsck.h"
+
+void
+pass1b ()
+{
+ struct dinode dino;
+ struct dinode *dp = &dino;
+ int cg, i;
+ ino_t number = 0;
+ int dupblk;
+ struct dups *duphead = duplist;
+
+ /* Check each block of file DP; if the block is in the dup block
+ list then add it to the dup block list under this file.
+ Return RET_GOOD or RET_BAD if the block is
+ good or bad, respectively. */
+ int
+ checkblock (daddr_t bno, int nfrags, off_t offset)
+ {
+ struct dups *dlp;
+ int hadbad = 0;
+
+ for (; nfrags > 0; bno++, nfrags--)
+ {
+ if (check_range (bno, 1))
+ return RET_BAD;
+ for (dlp = duphead; dlp; dlp = dlp->next)
+ {
+ if (dlp->dup == bno)
+ {
+ dupblk++;
+ warning (0, "DUPLICATE BLOCK %ld\n", bno);
+ dlp->dup = duphead->dup;
+ duphead->dup = bno;
+ duphead = duphead->next;
+ hadbad = 1;
+ }
+ if (dlp == muldup)
+ break;
+ }
+ }
+ return hadbad ? RET_BAD : RET_GOOD;
+ }
+
+ /* Call CHECKBLOCK for each block of each node, to see if it holds
+ a block already found to be a duplicate. */
+ for (cg = 0; cg < sblock->fs_ncg; cg++)
+ for (i = 0; i < sblock->fs_ipg; i++, number++)
+ {
+ if (number < ROOTINO)
+ continue;
+ if (inodestate[number] != UNALLOC)
+ {
+ getinode (number, dp);
+ dupblk = 0;
+ allblock_iterate (dp, checkblock);
+ if (dupblk)
+ {
+ problem (1, "I=%Ld HAS %d DUPLICATE BLOCKS", number, dupblk);
+ if (reply ("CLEAR"))
+ {
+ clear_inode (number, dp);
+ inodestate[number] = UNALLOC;
+ }
+ else if (inodestate[number] == DIRECTORY)
+ inodestate[number] = BADDIR;
+ }
+ }
+ }
+}
diff --git a/ufs-fsck/pass2.c b/ufs-fsck/pass2.c
new file mode 100644
index 00000000..d95929ef
--- /dev/null
+++ b/ufs-fsck/pass2.c
@@ -0,0 +1,400 @@
+/* Pass 2 of GNU fsck -- examine all directories for validity
+ Copyright (C) 1994,96,2002 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "fsck.h"
+#include <assert.h>
+
+/* Verify root inode's allocation and check all directories for
+ viability. Set DIRSORTED array fully and check to make sure
+ each directory has a correct . and .. in it. */
+void
+pass2 ()
+{
+ int nd;
+ struct dirinfo *dnp;
+ struct dinode dino;
+
+ /* Return negative, zero, or positive according to the
+ ordering of the first data block of **DNP1 and **DNP2. */
+ int
+ sortfunc (const void *ptr1, const void *ptr2)
+ {
+ struct dirinfo * const *dnp1 = ptr1;
+ struct dirinfo * const *dnp2 = ptr2;
+ return ((*dnp1)->i_blks[0] - (*dnp2)->i_blks[0]);
+ }
+
+ /* Called for each DIRBLKSIZ chunk of the directory.
+ BUF is the data of the directory block. Return
+ 1 if this block has been modified and should be written
+ to disk; otherwise return 0. */
+ int
+ check1block (void *buf)
+ {
+ struct directory_entry *dp;
+ int mod = 0;
+ u_char namlen;
+ char type;
+ int i;
+
+ for (dp = buf; (void *)dp - buf < DIRBLKSIZ;
+ dp = (struct directory_entry *) ((void *)dp + dp->d_reclen))
+ {
+ /* Check RECLEN for basic validity */
+ if (dp->d_reclen == 0
+ || dp->d_reclen + (void *)dp - buf > DIRBLKSIZ)
+ {
+ /* Perhaps the entire dir block is zero. UFS does that
+ when extending directories. So allow preening
+ to safely patch up all-null dir blocks. */
+ if (dp == buf)
+ {
+ char *bp;
+ for (bp = (char *)buf; bp < (char *)buf + DIRBLKSIZ; bp++)
+ if (*bp)
+ goto reclen_problem;
+
+ problem (0, "NULL BLOCK IN DIRECTORY");
+ if (preen || reply ("PATCH"))
+ {
+ /* Mark this entry free, and return. */
+ dp->d_ino = 0;
+ dp->d_reclen = DIRBLKSIZ;
+ pfix ("PATCHED");
+ return 1;
+ }
+ else
+ return mod;
+ }
+
+ reclen_problem:
+ problem (1, "BAD RECLEN IN DIRECTORY");
+ if (reply ("SALVAGE"))
+ {
+ /* Skip over everything else in this dirblock;
+ mark this entry free. */
+ dp->d_ino = 0;
+ dp->d_reclen = DIRBLKSIZ - ((void *)dp - buf);
+ return 1;
+ }
+ else
+ /* But give up regardless */
+ return mod;
+ }
+
+ /* Check INO */
+ if (dp->d_ino > maxino)
+ {
+ problem (1, "BAD INODE NUMBER IN DIRECTORY");
+ if (reply ("SALVAGE"))
+ {
+ /* Mark this entry clear */
+ dp->d_ino = 0;
+ mod = 1;
+ }
+ }
+
+ if (!dp->d_ino)
+ continue;
+
+ /* Check INO */
+ if (inodestate[dp->d_ino] == UNALLOC)
+ {
+ pinode (0, dnp->i_number, "REF TO UNALLOCATED NODE IN");
+ if (preen || reply ("REMOVE"))
+ {
+ dp->d_ino = 0;
+ mod = 1;
+ pfix ("REMOVED");
+ continue;
+ }
+ }
+
+ /* Check NAMLEN */
+ namlen = DIRECT_NAMLEN (dp);
+ if (namlen > MAXNAMLEN)
+ {
+ problem (1, "BAD NAMLEN IN DIRECTORY");
+ if (reply ("SALVAGE"))
+ {
+ /* Mark this entry clear */
+ dp->d_ino = 0;
+ mod = 1;
+ }
+ }
+ else
+ {
+ /* Check for illegal characters */
+ for (i = 0; i < DIRECT_NAMLEN (dp); i++)
+ if (dp->d_name[i] == '\0' || dp->d_name[i] == '/')
+ {
+ problem (1, "ILLEGAL CHARACTER IN FILE NAME");
+ if (reply ("SALVAGE"))
+ {
+ /* Mark this entry clear */
+ dp->d_ino = 0;
+ mod = 1;
+ break;
+ }
+ }
+ if (dp->d_name[DIRECT_NAMLEN (dp)])
+ {
+ problem (1, "DIRECTORY NAME NOT TERMINATED");
+ if (reply ("SALVAGE"))
+ {
+ /* Mark this entry clear */
+ dp->d_ino = 0;
+ mod = 1;
+ }
+ }
+ }
+
+ if (!dp->d_ino)
+ continue;
+
+ /* Check TYPE */
+ type = DIRECT_TYPE (dp);
+ if (type != DT_UNKNOWN && type != typemap[dp->d_ino])
+ {
+ problem (0, "INCORRECT NODE TYPE IN DIRECTORY");
+ if (preen || reply ("CLEAR"))
+ {
+ pfix ("CLEARED");
+ dp->d_type = 0;
+ mod = 1;
+ }
+ }
+
+ /* Here we should check for duplicate directory entries;
+ that's too much trouble right now. */
+
+ /* Account for the inode in the linkfound map */
+ if (inodestate[dp->d_ino] != UNALLOC)
+ linkfound[dp->d_ino]++;
+
+ if (inodestate[dp->d_ino] == DIRECTORY
+ || inodestate[dp->d_ino] == BADDIR)
+ {
+ if (DIRECT_NAMLEN (dp) == 1 && dp->d_name[0] == '.')
+ dnp->i_dot = dp->d_ino;
+ else if (DIRECT_NAMLEN (dp) == 2
+ && dp->d_name[0] == '.' && dp->d_name[1] == '.')
+ dnp->i_dotdot = dp->d_ino;
+ else
+ {
+ struct dirinfo *targetdir;
+ targetdir = lookup_directory (dp->d_ino);
+ if (targetdir->i_parent)
+ {
+ problem (0, "EXTRANEOUS LINK `%s' TO DIR I=%ld",
+ dp->d_name, dp->d_ino);
+ pextend (" FOUND IN DIR I=%Ld", dnp->i_number);
+ if (preen || reply ("REMOVE"))
+ {
+ dp->d_ino = 0;
+ mod = 1;
+ pfix ("REMOVED");
+ }
+ }
+ else
+ targetdir->i_parent = dnp->i_number;
+ }
+ }
+ }
+ return mod;
+ }
+
+ /* Called for each filesystem block of the directory. Load BNO
+ into core and then call CHECK1BLOCK for each DIRBLKSIZ chunk.
+ OFFSET is the offset this block occupies ithe file.
+ Always return 1. */
+ int
+ checkdirblock (daddr_t bno, int nfrags, off_t offset)
+ {
+ void *buf = alloca (nfrags * sblock->fs_fsize);
+ void *bufp;
+ int rewrite;
+
+ readblock (fsbtodb (sblock, bno), buf, nfrags * sblock->fs_fsize);
+ rewrite = 0;
+ for (bufp = buf;
+ bufp - buf < nfrags * sblock->fs_fsize
+ && offset + (bufp - buf) + DIRBLKSIZ <= dnp->i_isize;
+ bufp += DIRBLKSIZ)
+ {
+ if (check1block (bufp))
+ rewrite = 1;
+ }
+ if (rewrite)
+ writeblock (fsbtodb (sblock, bno), buf, nfrags * sblock->fs_fsize);
+ return 1;
+ }
+
+ switch (inodestate [ROOTINO])
+ {
+ default:
+ errexit ("BAD STATE %d FOR ROOT INODE", (int) (inodestate[ROOTINO]));
+
+ case DIRECTORY:
+ break;
+
+ case UNALLOC:
+ problem (1, "ROOT INODE UNALLOCATED");
+ if (!reply ("ALLOCATE"))
+ errexit ("ABORTING");
+ if (allocdir (ROOTINO, ROOTINO, 0755) != ROOTINO)
+ errexit ("CANNOT ALLOCATE ROOT INODE");
+ break;
+
+ case REG:
+ problem (1, "ROOT INODE NOT DIRECTORY");
+ if (reply ("REALLOCATE"))
+ freeino (ROOTINO);
+ if (allocdir (ROOTINO, ROOTINO, 0755) != ROOTINO)
+ errexit ("CANNOT ALLOCATE ROOT INODE");
+ break;
+
+ case BADDIR:
+ problem (1, "DUPLICATE or BAD BLOCKS IN ROOT INODE");
+ if (reply ("REALLOCATE"))
+ {
+ freeino (ROOTINO);
+ if (allocdir (ROOTINO, ROOTINO, 0755) != ROOTINO)
+ errexit ("CANNOT ALLOCATE ROOT INODE");
+ }
+ if (reply ("CONTINUE") == 0)
+ errexit ("ABORTING");
+ break;
+ }
+
+ /* Sort inpsort */
+ qsort (dirsorted, dirarrayused, sizeof (struct dirinfo *), sortfunc);
+
+ /* Check basic integrity of each directory */
+ for (nd = 0; nd < dirarrayused; nd++)
+ {
+ dnp = dirsorted[nd];
+
+ if (dnp->i_isize == 0)
+ continue;
+ if (dnp->i_isize % DIRBLKSIZ)
+ {
+ problem (0, "DIRECTORY INO=%Ld: LENGTH %d NOT MULTIPLE OF %d",
+ dnp->i_number, dnp->i_isize, DIRBLKSIZ);
+ if (preen || reply ("ADJUST"))
+ {
+ getinode (dnp->i_number, &dino);
+ dino.di_size = roundup (dnp->i_isize, DIRBLKSIZ);
+ write_inode (dnp->i_number, &dino);
+ pfix ("ADJUSTED");
+ }
+ }
+ bzero (&dino, sizeof (struct dinode));
+ dino.di_size = dnp->i_isize;
+ assert (dnp->i_numblks <= (NDADDR + NIADDR) * sizeof (daddr_t));
+ bcopy (dnp->i_blks, dino.di_db, dnp->i_numblks);
+
+ datablocks_iterate (&dino, checkdirblock);
+ }
+
+
+ /* At this point for each directory:
+ If this directory is an entry in another directory, then i_parent is
+ set to that node's number.
+ If this directory has a `..' entry, then i_dotdot is set to that link.
+ Check to see that `..' is set correctly. */
+ for (nd = 0; nd < dirarrayused; nd++)
+ {
+ dnp = dirsorted[nd];
+
+ /* Root is considered to be its own parent even though it isn't
+ listed. */
+ if (dnp->i_number == ROOTINO && !dnp->i_parent)
+ dnp->i_parent = ROOTINO;
+
+ /* Check `.' to make sure it exists and is correct */
+ if (dnp->i_dot == 0)
+ {
+ dnp->i_dot = dnp->i_number;
+ pinode (0, dnp->i_number, "MISSING `.' IN");
+ if ((preen || reply ("FIX"))
+ && makeentry (dnp->i_number, dnp->i_number, "."))
+ {
+ linkfound[dnp->i_number]++;
+ pfix ("FIXED");
+ }
+ else
+ pfail (0);
+ }
+ else if (dnp->i_dot != dnp->i_number)
+ {
+ pinode (0, dnp->i_number, "BAD INODE NUMBER FOR `.' IN");
+ if (preen || reply ("FIX"))
+ {
+ ino_t old_dot = dnp->i_dot;
+ dnp->i_dot = dnp->i_number;
+ if (changeino (dnp->i_number, ".", dnp->i_number))
+ {
+ linkfound[dnp->i_number]++;
+ if (inodestate[old_dot] != UNALLOC)
+ linkfound[old_dot]--;
+ pfix ("FIXED");
+ }
+ else
+ pfail (0);
+ }
+ }
+
+ /* Check `..' to make sure it exists and is correct */
+ if (dnp->i_parent && dnp->i_dotdot == 0)
+ {
+ dnp->i_dotdot = dnp->i_parent;
+ pinode (0, dnp->i_number, "MISSING `..' IN");
+ if ((preen || reply ("FIX"))
+ && makeentry (dnp->i_number, dnp->i_parent, ".."))
+ {
+ linkfound[dnp->i_parent]++;
+ pfix ("FIXED");
+ }
+ else
+ pfail (0);
+ }
+ else if (dnp->i_parent && dnp->i_dotdot != dnp->i_parent)
+ {
+ pinode (0, dnp->i_number, "BAD INODE NUMBER FOR `..' IN");
+ if (preen || reply ("FIX"))
+ {
+ ino_t parent = dnp->i_parent, old_dotdot = dnp->i_dotdot;
+ dnp->i_dotdot = parent;
+ if (changeino (dnp->i_number, "..", parent))
+ /* Adjust what the parent's link count should be; the actual
+ count will be corrected in an later pass. */
+ {
+ linkfound[parent]++;
+ if (inodestate[old_dotdot] != UNALLOC)
+ linkfound[old_dotdot]--;
+ pfix ("FIXED");
+ }
+ else
+ pfail (0);
+ }
+ }
+ }
+}
diff --git a/ufs-fsck/pass3.c b/ufs-fsck/pass3.c
new file mode 100644
index 00000000..fd5ad1b0
--- /dev/null
+++ b/ufs-fsck/pass3.c
@@ -0,0 +1,71 @@
+/* Pass 3 of GNU fsck -- Look for disconnected directories
+ Copyright (C) 1994, 1996 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "fsck.h"
+
+void
+pass3 ()
+{
+ struct dirinfo *dnp;
+ int nd;
+ int change;
+
+ /* Mark all the directories that can be found from the root. */
+
+ inodestate[ROOTINO] |= DIR_REF;
+
+ do
+ {
+ change = 0;
+ for (nd = 0; nd < dirarrayused; nd++)
+ {
+ dnp = dirsorted[nd];
+ if (dnp->i_parent
+ && inodestate[dnp->i_parent] == (DIRECTORY | DIR_REF)
+ && inodestate[dnp->i_number] == DIRECTORY)
+ {
+ inodestate[dnp->i_number] |= DIR_REF;
+ change = 1;
+ }
+ }
+ }
+ while (change);
+
+ /* Check for orphaned directories */
+ for (nd = 0; nd < dirarrayused; nd++)
+ {
+ dnp = dirsorted[nd];
+
+ if (dnp->i_parent == 0)
+ {
+ if (inodestate[dnp->i_number] & DIR_REF)
+ errexit ("ORPHANED DIR MARKED WITH CONNECT");
+ pinode (0, dnp->i_number, "UNREF");
+ if ((preen || reply ("RECONNECT"))
+ && linkup (dnp->i_number, dnp->i_dotdot))
+ {
+ dnp->i_parent = dnp->i_dotdot = lfdir;
+ pfix ("RECONNECTED");
+ }
+ else
+ pfail (0);
+ }
+ }
+}
diff --git a/ufs-fsck/pass4.c b/ufs-fsck/pass4.c
new file mode 100644
index 00000000..f8fe9814
--- /dev/null
+++ b/ufs-fsck/pass4.c
@@ -0,0 +1,94 @@
+/* Pass 4 of GNU fsck -- Check reference counts
+ Copyright (C) 1994, 1996 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "fsck.h"
+
+void
+pass4()
+{
+ ino_t number;
+ /* True if any reconnect attempt failed, in which case we don't try again. */
+ int reconn_failed = 0;
+
+ for (number = ROOTINO; number < maxino; number++)
+ {
+ if (linkfound[number] && inodestate[number] != UNALLOC)
+ {
+ if (linkcount[number] != linkfound[number])
+ {
+ pinode (0, number,
+ "LINK COUNT %d SHOULD BE %d IN",
+ linkcount[number], linkfound[number]);
+ if (preen || reply ("ADJUST"))
+ {
+ struct dinode dino;
+ getinode (number, &dino);
+ dino.di_nlink = linkfound[number];
+ write_inode (number, &dino);
+ pfix ("ADJUSTED");
+ }
+ }
+ }
+ else if (linkfound[number] && inodestate[number] == UNALLOC)
+ {
+ /* This can't happen because we never count links to unallocated
+ nodes. */
+ errexit ("LINK RECORDED FOR UNALLOCATED NODE");
+ }
+ else if (!linkfound[number] && inodestate[number] != UNALLOC)
+ {
+ /* No links to allocated node. If the size is zero, then
+ we want to clear it; if the size is positive, then we
+ want to reattach in. */
+ struct dinode dino;
+
+ pinode (0, number, "UNREF");
+
+ getinode (number, &dino);
+ if (dino.di_size && !reconn_failed)
+ {
+ /* This can't happen for dirctories because pass 3 should
+ already have reset them up. */
+ if ((DI_MODE (&dino) & IFMT) == IFDIR)
+ errexit ("NO LINKS TO NONZERO DIRECTORY");
+
+ if (preen || reply ("RECONNECT"))
+ reconn_failed = !linkup (number, -1);
+ if (! reconn_failed)
+ pfix ("RECONNECTED");
+ if (preen && reconn_failed)
+ pfail ("RECONNECT FAILED");
+ }
+ if (dino.di_size == 0 || reconn_failed)
+ {
+ if (reconn_failed && !preen)
+ /* If preening, the previous call to problem is still active
+ (more likely the failure was too severe, and exited). */
+ problem (0, "RECONNECT FAILED");
+ if (preen || reply ("CLEAR"))
+ {
+ inodestate[number] = UNALLOC;
+ clear_inode (number, &dino);
+ pfix ("CLEARED");
+ }
+ }
+ }
+ }
+}
diff --git a/ufs-fsck/pass5.c b/ufs-fsck/pass5.c
new file mode 100644
index 00000000..cb426a7d
--- /dev/null
+++ b/ufs-fsck/pass5.c
@@ -0,0 +1,450 @@
+/* Pass 5 of GNU fsck -- check allocation maps and summaries
+ Copyright (C) 1994,96,2001 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "fsck.h"
+
+/* From ../ufs/subr.c: */
+
+/*
+ * Update the frsum fields to reflect addition or deletion
+ * of some frags.
+ */
+static void
+ffs_fragacct(fs, fragmap, fraglist, cnt)
+ struct fs *fs;
+ int fragmap;
+ long fraglist[];
+ int cnt;
+{
+ int inblk;
+ register int field, subfield;
+ register 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)))) == 0)
+ continue;
+ field = around[siz];
+ subfield = inside[siz];
+ for (pos = siz; pos <= fs->fs_frag; pos++) {
+ if ((fragmap & field) == subfield) {
+ fraglist[siz] += cnt;
+ pos += siz;
+ field <<= siz;
+ subfield <<= siz;
+ }
+ field <<= 1;
+ subfield <<= 1;
+ }
+ }
+}
+
+void
+pass5 ()
+{
+ struct cg *newcg, *cg;
+ struct ocg *newocg;
+ int savednrpos = 0;
+ struct csum cstotal;
+ int i, j;
+ int c;
+ daddr_t d;
+ struct csum *sbcsums;
+
+ int basesize; /* size of cg not counting flexibly sized */
+ int sumsize; /* size of block totals and pos tbl */
+ int mapsize; /* size of inode map + block map */
+
+ int writesb;
+ int writecg;
+ int writecsum;
+
+ writesb = 0;
+ writecsum = 0;
+
+ cg = alloca (sblock->fs_cgsize);
+
+ newcg = alloca (sblock->fs_cgsize);
+ newocg = (struct ocg *)newcg;
+
+ sbcsums = alloca (fragroundup (sblock, sblock->fs_cssize));
+
+ readblock (fsbtodb (sblock, sblock->fs_csaddr), sbcsums,
+ fragroundup (sblock, sblock->fs_cssize));
+
+ /* Construct a CG structure; initialize everything that's the same
+ in each cylinder group. */
+ bzero (newcg, sblock->fs_cgsize);
+ newcg->cg_niblk = sblock->fs_ipg;
+ switch (sblock->fs_postblformat)
+ {
+ case FS_42POSTBLFMT:
+ /* Initialize size information */
+ basesize = (char *)(&newocg->cg_btot[0]) - (char *)(&newocg->cg_link);
+ sumsize = &newocg->cg_iused[0] - (char *)(&newocg->cg_btot[0]);
+ mapsize = (&newocg->cg_free[howmany(sblock->fs_fpg, NBBY)]
+ - (u_char *)&newocg->cg_iused[0]);
+ savednrpos = sblock->fs_nrpos;
+ sblock->fs_nrpos = 8;
+ break;
+
+ case FS_DYNAMICPOSTBLFMT:
+ /* Set fields unique to new cg structure */
+ newcg->cg_btotoff = &newcg->cg_space[0] - (u_char *)(&newcg->cg_link);
+ newcg->cg_boff = newcg->cg_btotoff + sblock->fs_cpg * sizeof (long);
+ newcg->cg_iusedoff = newcg->cg_boff + (sblock->fs_cpg
+ * sblock->fs_nrpos
+ * sizeof (short));
+ newcg->cg_freeoff = newcg->cg_iusedoff + howmany (sblock->fs_ipg, NBBY);
+
+ if (sblock->fs_contigsumsize <= 0)
+ {
+ newcg->cg_nextfreeoff =
+ (newcg->cg_freeoff
+ + howmany (sblock->fs_cpg * sblock->fs_spc / NSPF (sblock),
+ NBBY));
+ }
+ else
+ {
+ newcg->cg_clustersumoff =
+ (newcg->cg_freeoff
+ + howmany (sblock->fs_cpg * sblock->fs_spc / NSPF (sblock), NBBY)
+ - sizeof (long));
+ newcg->cg_clustersumoff =
+ roundup (newcg->cg_clustersumoff, sizeof (long));
+ newcg->cg_clusteroff =
+ (newcg->cg_clustersumoff
+ + (sblock->fs_contigsumsize + 1) * sizeof (long));
+ newcg->cg_nextfreeoff =
+ (newcg->cg_clusteroff
+ + howmany (sblock->fs_cpg * sblock->fs_spc / NSPB (sblock),
+ NBBY));
+ }
+
+ newcg->cg_magic = CG_MAGIC;
+
+ /* Set map sizes */
+ basesize = &newcg->cg_space[0] - (u_char *)(&newcg->cg_link);
+ sumsize = newcg->cg_iusedoff - newcg->cg_btotoff;
+ mapsize = newcg->cg_nextfreeoff - newcg->cg_iusedoff;
+ break;
+
+ default:
+ errexit ("UNKNOWN POSTBL FORMAT");
+ }
+
+ bzero (&cstotal, sizeof (struct csum));
+
+ /* Mark fragments past the end of the filesystem as used. */
+ j = blknum (sblock, sblock->fs_size + sblock->fs_frag - 1);
+ for (i = sblock->fs_size; i < j; i++)
+ setbmap (i);
+
+ /* Now walk through the cylinder groups, checking each one. */
+ for (c = 0; c < sblock->fs_ncg; c++)
+ {
+ int dbase, dmax;
+
+ /* Read the cylinder group structure */
+ readblock (fsbtodb (sblock, cgtod (sblock, c)), cg, sblock->fs_cgsize);
+ writecg = 0;
+
+ if (!cg_chkmagic (cg))
+ warning (1, "CG %d: BAD MAGIC NUMBER", c);
+
+ /* Compute first and last data block addresses in this group */
+ dbase = cgbase (sblock, c);
+ dmax = dbase + sblock->fs_fpg;
+ if (dmax > sblock->fs_size)
+ dmax = sblock->fs_size;
+
+ /* Initialize newcg fully; values from cg for those
+ we can't check. */
+ newcg->cg_time = cg->cg_time;
+ newcg->cg_cgx = c;
+ if (c == sblock->fs_ncg - 1)
+ newcg->cg_ncyl = sblock->fs_ncyl % sblock->fs_cpg;
+ else
+ newcg->cg_ncyl = sblock->fs_cpg;
+ newcg->cg_ndblk = dmax - dbase;
+ if (sblock->fs_contigsumsize > 0)
+ newcg->cg_nclusterblks = newcg->cg_ndblk / sblock->fs_frag;
+ newcg->cg_cs.cs_ndir = 0;
+ newcg->cg_cs.cs_nffree = 0;
+ newcg->cg_cs.cs_nbfree = 0;
+ newcg->cg_cs.cs_nifree = sblock->fs_ipg;
+
+ /* Check these for basic viability; if they are wrong
+ then clear them. */
+ newcg->cg_rotor = cg->cg_rotor;
+ newcg->cg_frotor = cg->cg_frotor;
+ newcg->cg_irotor = cg->cg_irotor;
+ if (newcg->cg_rotor > newcg->cg_ndblk)
+ {
+ problem (0, "ILLEGAL ROTOR VALUE IN CG %d", c);
+ if (preen || reply ("FIX"))
+ {
+ newcg->cg_rotor = 0;
+ cg->cg_rotor = 0;
+ writecg = 1;
+ pfix ("FIXED");
+ }
+ }
+ if (newcg->cg_frotor > newcg->cg_ndblk)
+ {
+ problem (0, "ILLEGAL FROTOR VALUE IN CG %d", c);
+ if (preen || reply ("FIX"))
+ {
+ newcg->cg_frotor = 0;
+ cg->cg_frotor = 0;
+ writecg = 1;
+ pfix ("FIXED");
+ }
+ }
+ if (newcg->cg_irotor > newcg->cg_niblk)
+ {
+ problem (0, "ILLEGAL IROTOR VALUE IN CG %d", c);
+ if (preen || reply ("FIX"))
+ {
+ newcg->cg_irotor = 0;
+ cg->cg_irotor = 0;
+ writecg = 1;
+ pfix ("FIXED");
+ }
+ }
+
+ /* Zero the block maps and summary areas */
+ bzero (&newcg->cg_frsum[0], sizeof newcg->cg_frsum);
+ bzero (&cg_blktot (newcg)[0], sumsize + mapsize);
+ if (sblock->fs_postblformat == FS_42POSTBLFMT)
+ newocg->cg_magic = CG_MAGIC;
+
+ /* Walk through each inode, accounting for it in
+ the inode map and in newcg->cg_cs. */
+ /* In this loop, J is the inode number, and I is the
+ inode number relative to this CG. */
+ j = sblock->fs_ipg * c;
+ for (i = 0; i < sblock->fs_ipg; j++, i++)
+ switch (inodestate[j])
+ {
+ case DIRECTORY:
+ case DIRECTORY | DIR_REF:
+ case BADDIR:
+ newcg->cg_cs.cs_ndir++;
+ /* Fall through... */
+ case REG:
+ newcg->cg_cs.cs_nifree--;
+ setbit (cg_inosused (newcg), i);
+ /* Fall through... */
+ case UNALLOC:
+ break;
+
+ default:
+ errexit ("UNKNOWN STATE I=%d", j);
+ }
+ /* Account for inodes 0 and 1 */
+ if (c == 0)
+ for (i = 0; i < ROOTINO; i++)
+ {
+ setbit (cg_inosused (newcg), i);
+ newcg->cg_cs.cs_nifree--;
+ }
+
+ /* Walk through each data block, accounting for it in
+ the block map and in newcg->cg_cs. */
+ /* In this look, D is the block number and I is the
+ block number relative to this CG. */
+ for (i = 0, d = dbase;
+ d < dmax;
+ d += sblock->fs_frag, i += sblock->fs_frag)
+ {
+ int frags = 0;
+
+ /* Set each free frag of this block in the block map;
+ count how many frags were free. */
+ for (j = 0; j < sblock->fs_frag; j++)
+ {
+ if (testbmap (d + j))
+ continue;
+ setbit (cg_blksfree (newcg), i + j);
+ frags++;
+ }
+
+ /* If all the frags were free, then count this as
+ a free block too. */
+ if (frags == sblock->fs_frag)
+ {
+ newcg->cg_cs.cs_nbfree++;
+ j = cbtocylno (sblock, i);
+ cg_blktot(newcg)[j]++;
+ cg_blks(sblock, newcg, j)[cbtorpos(sblock, i)]++;
+ if (sblock->fs_contigsumsize > 0)
+ setbit (cg_clustersfree (newcg), i / sblock->fs_frag);
+ }
+ else if (frags)
+ {
+ /* Partial; account for the frags. */
+ int blk;
+ newcg->cg_cs.cs_nffree += frags;
+ blk = blkmap (sblock, cg_blksfree (newcg), i);
+ ffs_fragacct (sblock, blk, newcg->cg_frsum, 1);
+ }
+ }
+
+ if (sblock->fs_contigsumsize > 0)
+ {
+ long *sump = cg_clustersum (newcg);
+ u_char *mapp = cg_clustersfree (newcg);
+ int map = *mapp++;
+ int bit = 1;
+ int run = 0;
+
+ for (i = 0; i < newcg->cg_nclusterblks; i++)
+ {
+ if ((map & bit) != 0)
+ run++;
+ else if (run)
+ {
+ if (run > sblock->fs_contigsumsize)
+ run = sblock->fs_contigsumsize;
+ sump[run]++;
+ run = 0;
+ }
+
+ if ((i & (NBBY - 1)) != (NBBY - 1))
+ bit <<= 1;
+ else
+ {
+ map = *mapp++;
+ bit = 1;
+ }
+ }
+ if (run != 0)
+ {
+ if (run > sblock->fs_contigsumsize)
+ run = sblock->fs_contigsumsize;
+ sump[run]++;
+ }
+ }
+
+ /* Add this cylinder group's totals into the superblock's
+ totals. */
+ cstotal.cs_nffree += newcg->cg_cs.cs_nffree;
+ cstotal.cs_nbfree += newcg->cg_cs.cs_nbfree;
+ cstotal.cs_nifree += newcg->cg_cs.cs_nifree;
+ cstotal.cs_ndir += newcg->cg_cs.cs_ndir;
+
+ /* Check counts in superblock */
+ if (bcmp (&newcg->cg_cs, &sbcsums[c], sizeof (struct csum)))
+ {
+ problem (0, "FREE BLK COUNTS FOR CG %d WRONG IN SUPERBLOCK", c);
+ if (preen || reply ("FIX"))
+ {
+ bcopy (&newcg->cg_cs, &sbcsums[c], sizeof (struct csum));
+ writecsum = 1;
+ pfix ("FIXED");
+ }
+ }
+
+ /* Check inode and block maps */
+ if (bcmp (cg_inosused (newcg), cg_inosused (cg), mapsize))
+ {
+ problem (0, "BLKS OR INOS MISSING IN CG %d BIT MAPS", c);
+ if (preen || reply ("FIX"))
+ {
+ bcopy (cg_inosused (newcg), cg_inosused (cg), mapsize);
+ writecg = 1;
+ pfix ("FIXED");
+ }
+ }
+
+ if (bcmp (&cg_blktot(newcg)[0], &cg_blktot(cg)[0], sumsize))
+ {
+ problem (0, "SUMMARY INFORMATION FOR CG %d BAD", c);
+ if (preen || reply ("FIX"))
+ {
+ bcopy (&cg_blktot(newcg)[0], &cg_blktot(cg)[0], sumsize);
+ writecg = 1;
+ pfix ("FIXED");
+ }
+ }
+
+ if (bcmp (newcg, cg, basesize))
+ {
+ problem (0, "CYLINDER GROUP %d BAD", c);
+ if (preen || reply ("FIX"))
+ {
+ bcopy (newcg, cg, basesize);
+ writecg = 1;
+ pfix ("FIXED");
+ }
+ }
+
+ if (writecg)
+ writeblock (fsbtodb (sblock, cgtod (sblock, c)),
+ cg, sblock->fs_cgsize);
+ }
+
+ /* Restore nrpos */
+ if (sblock->fs_postblformat == FS_42POSTBLFMT)
+ sblock->fs_nrpos = savednrpos;
+
+ if (bcmp (&cstotal, &sblock->fs_cstotal, sizeof (struct csum)))
+ {
+ problem (0, "TOTAL FREE BLK COUNTS WRONG IN SUPERBLOCK");
+ if (preen || reply ("FIX"))
+ {
+ bcopy (&cstotal, &sblock->fs_cstotal, sizeof (struct csum));
+ sblock->fs_ronly = 0;
+ sblock->fs_fmod = 0;
+ writesb = 1;
+ pfix ("FIXED");
+ }
+ }
+
+ if (sblock->fs_clean == 0 && !fix_denied)
+ {
+ problem (0, fsmodified ? "FILESYSTEM MODIFIED" : "FILESYSTEM UNCLEAN");
+ if (preen || reply ("MARK CLEAN"))
+ {
+ sblock->fs_clean = 1;
+ writesb = 1;
+ pfix ("MARKED CLEAN");
+ }
+ }
+
+ if (writesb)
+ writeblock (SBLOCK, sblock, SBSIZE);
+
+ if (writecsum)
+ {
+ const int cssize = fragroundup (sblock, sblock->fs_cssize);
+ struct csum *test;
+
+ writeblock (fsbtodb (sblock, sblock->fs_csaddr), sbcsums,
+ fragroundup (sblock, sblock->fs_cssize));
+
+ test = alloca (cssize);
+ readblock (fsbtodb (sblock, sblock->fs_csaddr), test, cssize);
+ if (bcmp (test, sbcsums, cssize))
+ warning (0, "CSUM WRITE INCONSISTENT");
+ }
+}
diff --git a/ufs-fsck/setup.c b/ufs-fsck/setup.c
new file mode 100644
index 00000000..9433bd68
--- /dev/null
+++ b/ufs-fsck/setup.c
@@ -0,0 +1,191 @@
+/*
+ Copyright (C) 1994,96,99,2002 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "fsck.h"
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <error.h>
+
+static char sblockbuf[SBSIZE];
+struct fs *sblock = (struct fs *)sblockbuf;
+
+/* A string identifying what we're trying to check. */
+char *device_name = 0;
+
+daddr_t maxfsblock;
+int maxino;
+int direct_symlink_extension;
+
+int newinofmt;
+
+int readfd, writefd;
+
+int fix_denied = 0;
+
+int fsmodified = 0;
+
+ino_t lfdir;
+
+/* Get ready to run on device with pathname DEV. */
+int
+setup (char *dev)
+{
+ struct stat st;
+ int changedsb;
+ size_t bmapsize;
+
+ device_name = dev;
+
+ if (stat (dev, &st) == -1)
+ {
+ error (0, errno, "%s", dev);
+ return 0;
+ }
+ if (!S_ISCHR (st.st_mode) && !S_ISBLK (st.st_mode))
+ {
+ problem (1, "%s is not a character or block device", dev);
+ if (! reply ("CONTINUE"))
+ return 0;
+ }
+ if (preen == 0)
+ printf ("** %s", dev);
+ if (!nowrite)
+ readfd = open (dev, O_RDWR);
+ if (nowrite || readfd == -1)
+ {
+ readfd = open (dev, O_RDONLY);
+ if (readfd == -1)
+ {
+ error (0, errno, "%s", dev);
+ return 0;
+ }
+ writefd = -1;
+ nowrite = 1;
+ if (preen)
+ warning (1, "NO WRITE ACCESS");
+ printf (" (NO WRITE)");
+ }
+ else
+ writefd = readfd;
+
+ if (preen == 0)
+ printf ("\n");
+
+ lfdir = 0;
+
+ /* We don't do the alternate superblock stuff here (yet). */
+ readblock (SBLOCK, sblock, SBSIZE);
+ changedsb = 0;
+
+ if (sblock->fs_magic != FS_MAGIC)
+ {
+ warning (1, "BAD MAGIC NUMBER");
+ return 0;
+ }
+ if (sblock->fs_ncg < 1)
+ {
+ warning (1, "NCG OUT OF RANGE");
+ return 0;
+ }
+ if (sblock->fs_cpg < 1)
+ {
+ warning (1, "CPG OUT OF RANGE");
+ return 0;
+ }
+ if (sblock->fs_ncg * sblock->fs_cpg < sblock->fs_ncyl
+ || (sblock->fs_ncg - 1) * sblock->fs_cpg >= sblock->fs_ncyl)
+ {
+ warning (1, "NCYL INCONSISTENT WITH NCG AND CPG");
+ return 0;
+ }
+ if (sblock->fs_sbsize > SBSIZE)
+ {
+ warning (1, "SBLOCK SIZE PREPONTEROUSLY LARGE");
+ return 0;
+ }
+ if (sblock->fs_optim != FS_OPTTIME && sblock->fs_optim != FS_OPTSPACE)
+ {
+ problem (1, "UNDEFINED OPTIMIZATION IN SUPERBLOCK");
+ if (reply ("SET TO DEFAULT"))
+ {
+ sblock->fs_optim = FS_OPTTIME;
+ changedsb = 1;
+ }
+ }
+ if (sblock->fs_minfree < 0 || sblock->fs_minfree > 99)
+ {
+ problem (0, "IMPOSSIBLE MINFREE=%ld IN SUPERBLOCK", sblock->fs_minfree);
+ if (preen || reply ("SET TO DEFAULT"))
+ {
+ sblock->fs_minfree = 10;
+ changedsb = 1;
+ pfix ("SET TO DEFAULT");
+ }
+ }
+ if (sblock->fs_interleave < 1
+ || sblock->fs_interleave > sblock->fs_nsect)
+ {
+ problem (0, "IMPOSSIBLE INTERLEAVE=%ld IN SUPERBLOCK",
+ sblock->fs_interleave);
+ if (preen || reply ("SET TO DEFAULT"))
+ {
+ sblock->fs_interleave = 1;
+ changedsb = 1;
+ pfix ("SET TO DEFAULT");
+ }
+ }
+ if (sblock->fs_npsect < sblock->fs_nsect
+ || sblock->fs_npsect > sblock->fs_nsect * 2)
+ {
+ problem (0, "IMPOSSIBLE NPSECT=%ld IN SUPERBLOCK", sblock->fs_npsect);
+ if (preen || reply ("SET TO DEFAULT"))
+ {
+ sblock->fs_npsect = sblock->fs_nsect;
+ changedsb = 1;
+ pfix ("SET TO DEFAULT");
+ }
+ }
+ if (sblock->fs_inodefmt >= FS_44INODEFMT)
+ newinofmt = 1;
+ else
+ {
+ sblock->fs_qbmask = ~sblock->fs_bmask;
+ sblock->fs_qfmask = ~sblock->fs_fmask;
+ newinofmt = 0;
+ }
+
+ if (changedsb)
+ writeblock (SBLOCK, sblock, SBSIZE);
+
+ /* Constants */
+ maxfsblock = sblock->fs_size;
+ maxino = sblock->fs_ncg * sblock->fs_ipg;
+ direct_symlink_extension = sblock->fs_maxsymlinklen > 0;
+
+ /* Allocate and initialize maps */
+ bmapsize = roundup (howmany (maxfsblock, NBBY), sizeof (short));
+ blockmap = calloc (bmapsize, sizeof (char));
+ inodestate = calloc (maxino + 1, sizeof (char));
+ typemap = calloc (maxino + 1, sizeof (char));
+ linkcount = calloc (maxino + 1, sizeof (nlink_t));
+ linkfound = calloc (maxino + 1, sizeof (nlink_t));
+ return 1;
+}
diff --git a/ufs-fsck/utilities.c b/ufs-fsck/utilities.c
new file mode 100644
index 00000000..5e081fe8
--- /dev/null
+++ b/ufs-fsck/utilities.c
@@ -0,0 +1,455 @@
+/* Miscellaneous functions for fsck
+ Copyright (C) 1994,95,96,99,2001,02 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "fsck.h"
+#include <fcntl.h>
+#include <sys/file.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <pwd.h>
+#include <error.h>
+#include <time.h>
+
+static void retch (char *reason);
+
+/* Read disk block ADDR into BUF of SIZE bytes. */
+void
+readblock (daddr_t addr, void *buf, size_t size)
+{
+ if (lseek (readfd, addr * DEV_BSIZE, L_SET) == -1)
+ errexit ("CANNOT SEEK TO BLOCK %ld", addr);
+ if (read (readfd, buf, size) != size)
+ errexit ("CANNOT READ BLOCK %ld", addr);
+}
+
+/* Write disk block BLKNO from BUF of SIZE bytes. */
+void
+writeblock (daddr_t addr, void *buf, size_t size)
+{
+ if (lseek (writefd, addr * DEV_BSIZE, L_SET) == -1)
+ errexit ("CANNOT SEEK TO BLOCK %ld", addr);
+ if (write (writefd, buf, size) != size)
+ errexit ("CANNOT WRITE BLOCK %ld", addr);
+ fsmodified = 1;
+}
+
+/* Last filesystem fragment that we read an inode from */
+static char *lastifrag;
+static daddr_t lastifragaddr;
+
+/* Read inode number INO into DINODE. */
+void
+getinode (ino_t ino, struct dinode *di)
+{
+ daddr_t iblk;
+
+ if (!lastifrag)
+ lastifrag = malloc (sblock->fs_bsize);
+
+ iblk = ino_to_fsba (sblock, ino);
+ if (iblk != lastifragaddr)
+ readblock (fsbtodb (sblock, iblk), lastifrag, sblock->fs_bsize);
+ lastifragaddr = iblk;
+ bcopy (lastifrag + ino_to_fsbo (sblock, ino) * sizeof (struct dinode),
+ di, sizeof (struct dinode));
+}
+
+/* Write inode number INO from DINODE. */
+void
+write_inode (ino_t ino, struct dinode *di)
+{
+ daddr_t iblk;
+
+ iblk = ino_to_fsba (sblock, ino);
+ if (iblk != lastifragaddr)
+ readblock (fsbtodb (sblock, iblk), lastifrag, sblock->fs_bsize);
+ lastifragaddr = iblk;
+ bcopy (di, lastifrag + ino_to_fsbo (sblock, ino) * sizeof (struct dinode),
+ sizeof (struct dinode));
+ writeblock (fsbtodb (sblock, iblk), lastifrag, sblock->fs_bsize);
+}
+
+/* Clear inode number INO and zero DI. */
+void
+clear_inode (ino_t ino, struct dinode *di)
+{
+ bzero (di, sizeof (struct dinode));
+ write_inode (ino, di);
+}
+
+/* Allocate and return a block and account for it in all the block
+ maps locally. Don't trust or change the disk block maps.
+ The block should be NFRAGS fragments long. */
+daddr_t
+allocblk (int nfrags)
+{
+ daddr_t i;
+ int j, k;
+
+ if (nfrags <= 0 || nfrags > sblock->fs_frag)
+ return 0;
+
+ /* Examine each block of the filesystem. */
+ for (i = 0; i < maxfsblock - sblock->fs_frag; i += sblock->fs_frag)
+ {
+ /* For each piece of the block big enough to hold this frag... */
+ for (j = 0; j <= sblock->fs_frag - nfrags; j++)
+ {
+ /* For each frag of this piece... */
+ for (k = 0; k < nfrags; k++)
+ if (testbmap (i + j + k))
+ break;
+
+ /* If one of the frags was allocated... */
+ if (k < nfrags)
+ {
+ /* Skip at least that far (short cut) */
+ j += k;
+ continue;
+ }
+
+ /* It's free (at address i + j) */
+
+ /* Mark the frags allocated in our map */
+ for (k = 0; k < nfrags; k++)
+ setbmap (i + j + k);
+
+ return (i + j);
+ }
+ }
+ return 0;
+}
+
+/* Check if a block starting at BLK and extending for CNT
+ fragments is out of range; if it is, then return 1; otherwise return 0. */
+int
+check_range (daddr_t blk, int cnt)
+{
+ int c;
+
+ if ((unsigned)(blk + cnt) > maxfsblock)
+ return 1;
+
+ c = dtog (sblock, blk);
+ if (blk < cgdmin (sblock, c))
+ {
+ if (blk + cnt > cgsblock (sblock, c))
+ return 1;
+ }
+ else
+ {
+ if (blk + cnt > cgbase (sblock, c + 1))
+ return 1;
+ }
+
+ return 0;
+}
+
+struct problem {
+ char *desc;
+ struct problem *prev;
+};
+
+/* A queue of problems found by fsck that are waiting resolution. The front
+ of the list is the most recent problem found (and presumably since
+ previous problems haven't been resolved yet, they depend on this one being
+ solved for their resolution). */
+static struct problem *problems = 0;
+
+static struct problem *free_problems = 0;
+
+static void
+push_problem (char *fmt, va_list args)
+{
+ struct problem *prob = free_problems;
+
+ if (! prob)
+ prob = malloc (sizeof (struct problem));
+ else
+ problems = prob->prev;
+ if (! prob)
+ retch ("malloc failed");
+
+ if (vasprintf (&prob->desc, fmt, args) < 0)
+ retch ("vasprintf failed");
+
+ prob->prev = problems;
+ problems = prob;
+}
+
+/* Print the most recent problem, and perhaps how it was resolved. */
+static void
+resolve_problem (char *fix)
+{
+ struct problem *prob = problems;
+
+ if (! prob)
+ retch ("no more problems");
+
+ problems = prob->prev;
+ prob->prev = free_problems;
+
+ if (preen && device_name)
+ printf ("%s: %s", device_name, prob->desc);
+ else
+ printf ("%s", prob->desc);
+ if (fix)
+ printf (" (%s)\n", fix);
+ else
+ putchar ('\n');
+ free (prob->desc);
+}
+
+/* Retire all problems as if they failed. We print them in chronological
+ order rather than lifo order, as this is a bit clearer, and we can do it
+ when we know they're all going to fail. */
+static void
+flush_problems ()
+{
+ struct problem *fail (struct problem *prob)
+ {
+ struct problem *last = prob->prev ? fail (prob->prev) : prob;
+ if (preen && device_name)
+ printf ("%s: %s\n", device_name, prob->desc);
+ else
+ puts (prob->desc);
+ free (prob->desc);
+ return last;
+ }
+ if (problems)
+ {
+ fail (problems)->prev = free_problems;
+ free_problems = problems;
+ }
+}
+
+/* Like printf, but exit after printing. */
+void
+errexit (char *fmt, ...)
+{
+ va_list args;
+
+ flush_problems ();
+
+ if (preen && device_name)
+ printf ("%s: ", device_name);
+
+ va_start (args, fmt);
+ vprintf (fmt, args);
+ va_end (args);
+ putchar ('\n');
+
+ exit (8);
+}
+
+static void
+retch (char *reason)
+{
+ flush_problems ();
+ error (99, 0, "(internal error) %s!", reason);
+}
+
+/* Prints all unresolved problems and exits, printing MSG as well. */
+static void
+punt (char *msg)
+{
+ problem (0, msg);
+ flush_problems ();
+ exit (8);
+}
+
+/* If SEVERE is true, and we're in preen mode, then things are too hair to
+ fix automatically, so tell the user to do it himself and punt. */
+static void
+no_preen (int severe)
+{
+ if (severe && preen)
+ punt ("PLEASE RUN fsck MANUALLY");
+}
+
+/* Store away the given message about a problem found. A call to problem must
+ be matched later with a call to pfix, pfail, or reply; to print more
+ in the same message, intervening calls to pextend can be used. If SEVERE is
+ true, and we're in preen mode, then the program is terminated. */
+void
+problem (int severe, char *fmt, ...)
+{
+ va_list args;
+
+ va_start (args, fmt);
+ push_problem (fmt, args);
+ va_end (args);
+
+ no_preen (severe);
+}
+
+/* Following a call to problem (with perhaps intervening calls to
+ pmore), appends the given message to that message. */
+void
+pextend (char *fmt, ...)
+{
+ va_list args;
+ char *more, *concat;
+ struct problem *prob = problems;
+
+ if (! prob)
+ retch ("No pending problem to add to");
+
+ va_start (args, fmt);
+ if (vasprintf (&more, fmt, args) < 0)
+ retch ("vasprintf failed");
+ va_end (args);
+
+ concat = realloc (prob->desc, strlen (prob->desc) + 1 + strlen (more) + 1);
+ if (! concat)
+ retch ("realloc failed");
+
+ strcpy (concat + strlen (concat), more);
+ prob->desc = concat;
+ free (more);
+}
+
+/* Like problem, but as if immediately followed by pfail. */
+void
+warning (int severe, char *fmt, ...)
+{
+ va_list args;
+
+ va_start (args, fmt);
+ push_problem (fmt, args);
+ va_end (args);
+
+ no_preen (severe);
+
+ resolve_problem (0);
+}
+
+/* Like problem, but appends a helpful description of the given inode number to
+ the message. */
+void
+pinode (int severe, ino_t ino, char *fmt, ...)
+{
+ if (fmt)
+ {
+ va_list args;
+ va_start (args, fmt);
+ push_problem (fmt, args);
+ va_end (args);
+ }
+
+ if (ino < ROOTINO || ino > maxino)
+ pextend (" (BOGUS INODE) I=%Ld", ino);
+ else
+ {
+ char *p;
+ struct dinode dino;
+ struct passwd *pw;
+
+ getinode (ino, &dino);
+
+ pextend (" %s I=%Ld", (DI_MODE (&dino) & IFMT) == IFDIR ? "DIR" : "FILE",
+ ino);
+
+ pw = getpwuid (dino.di_uid);
+ if (pw)
+ pextend (" O=%s", pw->pw_name);
+ else
+ pextend (" O=%lu", dino.di_uid);
+
+ pextend (" M=0%o", DI_MODE (&dino));
+ pextend (" SZ=%llu", dino.di_size);
+ p = ctime (&dino.di_mtime.tv_sec);
+ pextend (" MT=%12.12s %4.4s", &p[4], &p[20]);
+ }
+
+ no_preen (severe);
+}
+
+/* Print a successful resolution to a pending problem. Must follow a call to
+ problem or pextend. */
+void
+pfix (char *fix)
+{
+ if (preen)
+ resolve_problem (fix ?: "FIXED");
+}
+
+/* Print an unsuccessful resolution to a pending problem. Must follow a call
+ to problem or pextend. */
+void
+pfail (char *failure)
+{
+ if (preen)
+ resolve_problem (failure);
+}
+
+/* Ask the user a question; return 1 if the user says yes, and 0
+ if the user says no. This call must follow a call to problem or pextend,
+ which it completes. */
+int
+reply (char *question)
+{
+ int persevere;
+ char c;
+
+ if (preen)
+ retch ("Got to reply() in preen mode");
+
+ /* Emit the problem to which the question pertains. */
+ resolve_problem (0);
+
+ persevere = !strcmp (question, "CONTINUE");
+
+ if (!persevere && (nowrite || writefd < 0))
+ {
+ fix_denied = 1;
+ printf ("%s? no\n\n", question);
+ return 0;
+ }
+ else if (noquery || (persevere && nowrite))
+ {
+ printf ("%s? yes\n\n", question);
+ return 1;
+ }
+ else
+ {
+ do
+ {
+ printf ("%s? [yn] ", question);
+ fflush (stdout);
+ c = getchar ();
+ while (c != '\n' && getchar () != '\n')
+ if (feof (stdin))
+ {
+ fix_denied = 1;
+ return 0;
+ }
+ }
+ while (c != 'y' && c != 'Y' && c != 'n' && c != 'N');
+ putchar ('\n');
+ if (c == 'y' || c == 'Y')
+ return 1;
+ else
+ {
+ fix_denied = 1;
+ return 0;
+ }
+ }
+}
diff --git a/ufs-utils/Makefile b/ufs-utils/Makefile
new file mode 100644
index 00000000..df22440e
--- /dev/null
+++ b/ufs-utils/Makefile
@@ -0,0 +1,38 @@
+#
+# Copyright (C) 1994, 1996, 2008 Free Software Foundation
+# Written by Michael I. Bushnell.
+#
+# This file is part of the GNU Hurd.
+#
+# The GNU Hurd is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2, or (at
+# your option) any later version.
+#
+# The GNU Hurd is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+dir := ufs-utils
+makemode := utilities
+
+targets = mkfs.ufs clri.ufs stati.ufs
+target-suffix = .ufs
+SRCS = mkfs.c clri.c stati.c dlabel.c
+installationdir = $(sbindir)
+
+OBJS = $(SRCS:.c=.o)
+HURDLIBS = store shouldbeinlibc
+
+include ../Makeconf
+
+mkfs.ufs: mkfs.o dlabel.o ../libstore/libstore.a ../libshouldbeinlibc/libshouldbeinlibc.a
+
+$(targets): %.ufs: %.o
+
+
diff --git a/ufs-utils/clri.c b/ufs-utils/clri.c
new file mode 100644
index 00000000..1ad348a1
--- /dev/null
+++ b/ufs-utils/clri.c
@@ -0,0 +1,168 @@
+/*
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Rich $alz of BBN 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.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1990, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+
+static char copyright[] __attribute__ ((unused));
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)clri.c 8.2 (Berkeley) 9/23/93";
+static char sccsid[] __attribute__ ((unused));
+#endif /* not lint */
+
+/* Modified by Michael I. Bushnell for GNU Hurd. */
+
+#include <sys/param.h>
+#include <sys/time.h>
+
+#include "../ufs/dinode.h"
+#include "../ufs/fs.h"
+
+
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#define MAXPHYS (64 * 1024)
+#define DEV_BSIZE 512
+
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ register struct fs *sbp;
+ register struct dinode *ip;
+ register int fd;
+ struct dinode ibuf[MAXBSIZE / sizeof (struct dinode)];
+ long generation, bsize;
+ off_t offset;
+ int inonum;
+ char *fs, sblock[SBSIZE];
+
+ if (argc < 3) {
+ (void)fprintf(stderr, "usage: clri filesystem inode ...\n");
+ exit(1);
+ }
+
+ fs = *++argv;
+
+ /* get the superblock. */
+ if ((fd = open(fs, O_RDWR, 0)) < 0)
+ {
+ perror (fs);
+ exit (1);
+ }
+ if (lseek(fd, (off_t)(SBLOCK * DEV_BSIZE), SEEK_SET) < 0)
+ {
+ perror (fs);
+ exit (1);
+ }
+ if (read(fd, sblock, sizeof(sblock)) != sizeof(sblock)) {
+ (void)fprintf(stderr,
+ "clri: %s: can't read the superblock.\n", fs);
+ exit(1);
+ }
+
+ sbp = (struct fs *)sblock;
+ if (sbp->fs_magic != FS_MAGIC) {
+ (void)fprintf(stderr,
+ "clri: %s: superblock magic number 0x%lux, not 0x%x.\n",
+ fs, sbp->fs_magic, FS_MAGIC);
+ exit(1);
+ }
+ bsize = sbp->fs_bsize;
+
+ /* remaining arguments are inode numbers. */
+ while (*++argv) {
+ /* get the inode number. */
+ if ((inonum = atoi(*argv)) <= 0) {
+ (void)fprintf(stderr,
+ "clri: %s is not a valid inode number.\n", *argv);
+ exit(1);
+ }
+ (void)printf("clearing %d\n", inonum);
+
+ /* read in the appropriate block. */
+ offset = ino_to_fsba(sbp, inonum); /* inode to fs blk */
+ offset = fsbtodb(sbp, offset); /* fs blk disk blk */
+ offset *= DEV_BSIZE; /* disk blk to bytes */
+
+ /* seek and read the block */
+ if (lseek(fd, offset, SEEK_SET) < 0)
+ {
+ perror (fs);
+ exit (1);
+ }
+ if (read(fd, ibuf, bsize) != bsize)
+ {
+ perror (fs);
+ exit (1);
+ }
+
+ /* get the inode within the block. */
+ ip = &ibuf[ino_to_fsbo(sbp, inonum)];
+
+ /* clear the inode, and bump the generation count. */
+ generation = ip->di_gen + 1;
+ memset(ip, 0, sizeof(*ip));
+ ip->di_gen = generation;
+
+ /* backup and write the block */
+ if (lseek(fd, (off_t)-bsize, SEEK_CUR) < 0)
+ {
+ perror (fs);
+ exit (1);
+ }
+ if (write(fd, ibuf, bsize) != bsize)
+ {
+ perror (fs);
+ exit (1);
+ }
+ (void)fsync(fd);
+ }
+ (void)close(fd);
+ exit(0);
+}
diff --git a/ufs-utils/dlabel.c b/ufs-utils/dlabel.c
new file mode 100644
index 00000000..355cb3f6
--- /dev/null
+++ b/ufs-utils/dlabel.c
@@ -0,0 +1,91 @@
+/* Get the disklabel from a device node
+
+ Copyright (C) 1996,99,2001 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <errno.h>
+#include <hurd.h>
+#include <mach.h>
+#include <string.h>
+#include <device/device.h>
+#include <device/disk_status.h>
+#include <hurd/store.h>
+
+/* XXX Ick. */
+#define IOCPARM_MASK 0x1fff/* parameter length, at most 13 bits */
+#define IOC_OUT 0x40000000/* copy out parameters */
+#define _IOC(inout,group,num,len) \
+ (inout | ((len & IOCPARM_MASK) << 16) | ((group) << 8) | (num))
+#define _IOR(g,n,t) _IOC(IOC_OUT,(g), (n), sizeof(t))
+
+static error_t
+fd_get_device (int fd, device_t *device)
+{
+ error_t err;
+ struct store *store;
+ file_t node = getdport (fd);
+
+ if (node == MACH_PORT_NULL)
+ return errno;
+
+ err = store_create (node, 0, 0, &store); /* consumes NODE on success */
+ if (! err)
+ {
+ if (store->class->id != STORAGE_DEVICE
+ /* In addition to requiring a device, we also want the *whole*
+ device -- one contiguous run starting at 0. */
+ || store->num_runs != 1
+ || store->runs[0].start != 0)
+ err = ENODEV;
+ else if (store->port == MACH_PORT_NULL)
+ /* Usually getting a null port back means we didn't have sufficient
+ privileges. */
+ err = EPERM;
+ else
+ {
+ *device = store->port;
+ store->port = MACH_PORT_NULL; /* Steal the port from STORE! */
+ }
+ store_free (store);
+ }
+ else
+ mach_port_deallocate (mach_task_self (), node);
+
+ return err;
+}
+
+error_t
+fd_get_disklabel (int fd, struct disklabel *label)
+{
+ device_t device;
+ error_t err = fd_get_device (fd, &device);
+
+ if (! err)
+ {
+ mach_msg_type_number_t label_len = sizeof *label / sizeof (integer_t);
+
+ err = device_get_status (device, DIOCGDINFO,
+ (dev_status_t)label, &label_len);
+ if (!err && label_len != sizeof *label / sizeof (integer_t))
+ err = ERANGE;
+
+ mach_port_deallocate (mach_task_self (), device);
+ }
+
+ return err;
+}
diff --git a/ufs-utils/mkfs.c b/ufs-utils/mkfs.c
new file mode 100644
index 00000000..aef3ea92
--- /dev/null
+++ b/ufs-utils/mkfs.c
@@ -0,0 +1,1406 @@
+/*
+ * 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. 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.
+ */
+
+#ifndef lint
+/*static char sccsid[] = "from: @(#)mkfs.c 8.3 (Berkeley) 2/3/94";*/
+static char *rcsid = "$Id: mkfs.c,v 1.22 2006/03/14 23:27:50 tschwinge Exp $";
+#endif /* not lint */
+
+#include <unistd.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <argp.h>
+#include <assert.h>
+#include <error.h>
+#include <string.h>
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/wait.h>
+#include <sys/resource.h>
+#include "../ufs/dinode.h"
+#include "../ufs/dir.h"
+#include "../ufs/fs.h"
+/* #include <sys/disklabel.h> */
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <version.h>
+
+#include <device/device_types.h>
+#include <device/disk_status.h>
+
+#include <hurd.h>
+
+/* Begin misc additions for GNU Hurd */
+
+/* For GNU Hurd: the ufs DIRSIZ macro is different than the BSD
+ 4.4 version that mkfs expects. So we provide here the BSD version. */
+#undef DIRSIZ
+#if (BYTE_ORDER == LITTLE_ENDIAN)
+#define DIRSIZ(oldfmt, dp) \
+ ((oldfmt) ? \
+ ((sizeof (struct directory_entry) - (MAXNAMLEN+1)) + (((dp)->d_type+1 + 3) &~ 3)) : \
+ ((sizeof (struct directory_entry) - (MAXNAMLEN+1)) + (((dp)->d_namlen+1 + 3) &~ 3)))
+#else
+#define DIRSIZ(oldfmt, dp) \
+ ((sizeof (struct directory_entry) - (MAXNAMLEN+1)) + (((dp)->d_namlen+1 + 3) &~ 3))
+#endif
+
+#define MAXPHYS (64 * 1024)
+
+/* Provide mode from struct dinode * */
+#define DI_MODE(dp) (((dp)->di_modeh << 16) | (dp)->di_model)
+
+#define DEV_BSIZE 512
+
+#define btodb(bytes) ((bytes) / DEV_BSIZE)
+
+/* End additions for GNU Hurd */
+
+#ifndef STANDALONE
+#include <a.out.h>
+#include <stdio.h>
+#include <time.h>
+#endif
+
+/*
+ * make file system for cylinder-group style file systems
+ */
+
+extern error_t fd_get_disklabel (int fd, struct disklabel *lab);
+static void mkfs (), initcg (), fsinit (), setblock ();
+static void iput (), rdfs (), clrblock (), wtfs ();
+static int makedir (), isblock ();
+
+/*
+ * We limit the size of the inode map to be no more than a
+ * third of the cylinder group space, since we must leave at
+ * least an equal amount of space for the block map.
+ *
+ * N.B.: MAXIPG must be a multiple of INOPB(fs).
+ */
+#define MAXIPG(fs) roundup((fs)->fs_bsize * NBBY / 3, INOPB(fs))
+
+#define UMASK 0755
+#define MAXINOPB (MAXBSIZE / sizeof(struct dinode))
+#define POWEROF2(num) (((num) & ((num) - 1)) == 0)
+
+/*
+ * variables set up by front end.
+ */
+#define extern
+extern int Nflag; /* run mkfs without writing file system */
+extern int Oflag; /* format as an 4.3BSD file system */
+extern int fssize; /* file system size */
+extern int ntracks; /* # tracks/cylinder */
+extern int nsectors; /* # sectors/track */
+extern int nphyssectors; /* # sectors/track including spares */
+extern int secpercyl; /* sectors per cylinder */
+extern int sectorsize; /* bytes/sector */
+extern int rpm; /* revolutions/minute of drive */
+extern int interleave; /* hardware sector interleave */
+extern int trackskew; /* sector 0 skew, per track */
+extern int headswitch; /* head switch time, usec */
+extern int trackseek; /* track-to-track seek, usec */
+extern int fsize; /* fragment size */
+extern int bsize; /* block size */
+extern int cpg; /* cylinders/cylinder group */
+extern int cpgflg; /* cylinders/cylinder group flag was given */
+extern int minfree; /* free space threshold */
+extern int opt; /* optimization preference (space or time) */
+extern int density; /* number of bytes per inode */
+extern int maxcontig; /* max contiguous blocks to allocate */
+extern int rotdelay; /* rotational delay between blocks */
+extern int maxbpg; /* maximum blocks per file in a cyl group */
+extern int nrpos; /* # of distinguished rotational positions */
+extern int bbsize; /* boot block size */
+extern int sbsize; /* superblock size */
+#undef extern
+
+union {
+ struct fs fs;
+ char pad[SBSIZE];
+} fsun;
+#define sblock fsun.fs
+struct csum *fscs;
+
+union {
+ struct cg cg;
+ char pad[MAXBSIZE];
+} cgun;
+#define acg cgun.cg
+
+struct dinode zino[MAXBSIZE / sizeof(struct dinode)];
+
+int fsi, fso;
+daddr_t alloc();
+
+const char *argp_program_version = STANDARD_HURD_VERSION (mkfs.ufs);
+
+#define _STRINGIFY(arg) #arg
+#define STRINGIFY(arg) _STRINGIFY (arg)
+
+static const struct argp_option options[] = {
+ {0,0,0,0,0, 1},
+ {"just-print", 'N', 0, 0,
+ "Just print the file system parameters that would be used"},
+ {"old-format", 'O', 0, 0, "Create a 4.3BSD format filesystem"},
+ {"max-contig", 'a', "BLOCKS", 0,
+ "The maximum number of contiguous blocks that will be laid out before"
+ " forcing a rotational delay; the default is no limit"},
+ {"block-size", 'b', "BYTES", 0, "The block size of the file system"},
+ {"group-cylinders", 'c', "N", 0,
+ "The number of cylinders per cylinder group; the default 16"},
+ {"rot-delay", 'd', "MSEC", 0,
+ "The expected time to service a transfer completion interrupt and"
+ " initiate a new transfer on the same disk; the default is 0"},
+ {"max-bpg", 'e', "BLOCKS", 0,
+ "Maximum number of blocks any single file can allocate out of a cylinder"
+ " group before it is forced to begin allocating blocks from another"
+ " cylinder group; the default is about one quarter of the total blocks"
+ " in a cylinder group"},
+ {"frag-size", 'f', "BYTES", 0, "The fragment size"},
+ {"inode-density", 'i', "BYTES", 0,
+ "The density of inodes in the file system, in bytes of data space per"
+ " inode; the default is one inode per 4 filesystem frags"},
+ {"minfree", 'm', "PERCENT", 0,
+ "The percentage of space reserved from normal users; the default is "
+ STRINGIFY (MINFREE) "%"},
+ {"rot-positions", 'n', "N", 0,
+ "The number of distinct rotational positions; the default is 8"},
+ {"optimization", 'o', "METH", 0, "``space'' or ``time''"},
+ {"size", 's', "SECTORS", 0, "The size of the file system"},
+
+ {0,0,0,0,
+ "The following options override the standard sizes for the disk"
+ " geometry; their default values are taken from the disk label:", 2},
+
+ {"sector-size", 'S', "BYTES", 0, "The size of a sector (usually 512)"},
+ {"skew", 'k', "SECTORS", 0, "Sector 0 skew, per track"},
+ {"interleave", 'l', "LOG-PHYS-RATIO", 0, "Hardware sector interleave"},
+ {"rpm", 'r', "RPM", 0, "The speed of the disk in revolutions per minute"},
+ {"tracks", 't', "N", 0, "The number of tracks/cylinder"},
+ {"sectors", 'u', "N", 0,
+ "The number of sectors per track (does not include sectors reserved for"
+ " bad block replacement"},
+ {"spare-sectors", 'p', "N", 0,
+ "Spare sectors (for bad sector replacement) at the end of each track"},
+ {"cyl-spare-sectors", 'x', "N", 0,
+ "Spare sectors (for bad sector replacement) at the end of the last track"
+ " in each cylinder"},
+ {0, 0}
+};
+static char *args_doc = "DEVICE";
+static char *doc = "Write a ufs filesystem image onto DEVICE.";
+
+struct amark { void *addr; struct amark *next; };
+
+static void
+amarks_add (struct amark **amarks, void *addr)
+ {
+ struct amark *up = malloc (sizeof (struct amark));
+ assert (up != 0);
+ up->addr = addr;
+ up->next = *amarks;
+ *amarks = up;
+ }
+static int
+amarks_contains (struct amark *amarks, void *addr)
+ {
+ while (amarks)
+ if (amarks->addr == addr)
+ return 1;
+ else
+ amarks = amarks->next;
+ return 0;
+ }
+
+static const struct disklabel default_disklabel = {
+ d_rpm: 3600,
+ d_interleave: 1,
+ d_secsize: DEV_BSIZE,
+ d_sparespertrack: 0,
+ d_sparespercyl: 0,
+ d_trackskew: 0,
+ d_cylskew: 0,
+ d_headswitch: 0,
+ d_trkseek: 0,
+};
+
+char *device = 0;
+
+#define deverr(code, err, fmt, args...) \
+ error (code, err, "%s: " fmt, device , ##args)
+
+int
+main (int argc, char **argv)
+{
+ int fdo, fdi;
+ struct amark *uparams = 0;
+ error_t label_err = 0;
+ struct disklabel label_buf, *label = 0;
+ int nspares = 0, ncspares = 0;
+
+ /* Parse our options... */
+ error_t parse_opt (int key, char *arg, struct argp_state *state)
+ {
+ switch (key)
+ {
+ case 'N': Nflag = 1; break;
+ case 'O': Oflag = 1; break;
+
+/* Mark &VAR as being a uparam, and set VAR. */
+#define UP_INT(var) { amarks_add (&uparams, &var); var = atoi (arg); }
+
+ case 'a': UP_INT (maxcontig); break;
+ case 'b': UP_INT (bsize); break;
+ case 'c': UP_INT (cpg); cpgflg = 1; break;
+ case 'd': UP_INT (rotdelay); break;
+ case 'e': UP_INT (maxbpg); break;
+ case 'f': UP_INT (fsize); break;
+ case 'i': UP_INT (density); break;
+ case 'm': UP_INT (minfree); break;
+ case 'n': UP_INT (nrpos); break;
+ case 's': UP_INT (fssize); break;
+ case 'S': UP_INT (sectorsize); break;
+ case 'k': UP_INT (trackskew); break;
+ case 'l': UP_INT (interleave); break;
+ case 'r': UP_INT (rpm); break;
+ case 't': UP_INT (ntracks); break;
+ case 'u': UP_INT (nsectors); break;
+ case 'p': UP_INT (nspares); break;
+ case 'x': UP_INT (ncspares); break;
+
+ case 'o':
+ amarks_add (&uparams, &opt);
+ if (strcmp (arg, "time") == 0)
+ opt = FS_OPTTIME;
+ else if (strcmp (arg, "space") == 0)
+ opt = FS_OPTSPACE;
+ else
+ argp_error (state, "%s: Invalid value for --optimization", arg);
+ break;
+
+ case ARGP_KEY_ARG:
+ if (state->arg_num > 0)
+ return ARGP_ERR_UNKNOWN;
+ device = arg;
+ break;
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+ }
+ const struct argp argp = { options, parse_opt, args_doc, doc };
+
+ /* Tries to get the disklabel for DEVICE; if it can't, then if PARAM_NAME
+ is 0, returns 0, otherwise an error is printed (using PARAM_NAME) and
+ the program exits. */
+ struct disklabel *dl (char *param_name)
+ {
+ if (! label)
+ {
+ if (! label_err)
+ {
+ label_err = fd_get_disklabel (fdi, &label_buf);
+ if (! label_err)
+ label = &label_buf;
+ }
+ if (label_err && param_name)
+ error (9, label_err,
+ "%s: Can't get disklabel; please specify --%s",
+ device, param_name);
+ }
+ return label;
+ }
+ /* Tries to get the integer field at offset OFFS from the disklabel for
+ DEVICE; if it can't, then if PARAM_NAME is 0, returns the corresponding
+ value from DEFAULT_DISKLABEL, otherwise an error is printed and the
+ program exits. */
+ int dl_int (char *param_name, size_t offs)
+ {
+ struct disklabel *l = dl (param_name);
+ return *(int *)((char *)(l ?: &default_disklabel) + offs);
+ }
+ /* A version of dl_int that takes the field name instead of an offset. */
+#define DL_INT(param_name, field) \
+ dl_int (param_name, offsetof (struct disklabel, field))
+
+ /* Like dl_int, but adjust for any difference in sector size between the
+ disklabel and SECTORSIZE. */
+ int dl_secs (char *param_name, size_t offs)
+ {
+ int val = dl_int (param_name, offs);
+ int dl_ss = DL_INT (0, d_secsize);
+ if (sectorsize < dl_ss)
+ deverr (10, 0,
+ "%d: Sector size is less than device sector size (%d)",
+ sectorsize, dl_ss);
+ else if (sectorsize > dl_ss)
+ if (sectorsize % dl_ss != 0)
+ deverr (11, 0,
+ "%d: Sector size not a multiple of device sector size (%d)",
+ sectorsize, dl_ss);
+ else
+ val /= sectorsize / dl_ss;
+ return val;
+ }
+ /* A version of dl_secs that takes the field name instead of an offset. */
+#define DL_SECS(param_name, field) \
+ dl_secs (param_name, offsetof (struct disklabel, field))
+
+ /* Parse our arguments. */
+ argp_parse (&argp, argc, argv, 0, 0, 0);
+
+ fdi = open (device, O_RDONLY);
+ if (fdi == -1)
+ error (2, errno, "%s", device);
+ fdo = open (device, O_WRONLY);
+ if (fdo == -1)
+ error (3, errno, "%s", device);
+
+ /* If VAR hasn't been set by the user, set it to DEF_VAL. */
+#define DEFAULT(var, def_val) \
+ (amarks_contains (uparams, &var) ? 0 : (((var) = (def_val)), 0))
+
+ DEFAULT (sectorsize, DEV_BSIZE);
+ DEFAULT (fssize,
+ ({ struct stat st;
+ if (fstat (fdi, &st) == -1)
+ deverr (4, errno, "Cannot get size");
+ st.st_size / sectorsize; }));
+ DEFAULT (ntracks, DL_INT ("tracks", d_ntracks));
+ DEFAULT (nsectors, DL_SECS ("sectors", d_nsectors));
+ DEFAULT (nspares, DL_SECS (0, d_sparespertrack));
+ DEFAULT (ncspares, DL_SECS (0, d_sparespercyl));
+
+ if (nspares >= nsectors)
+ deverr (5, 0, "%d: Too many spare sectors per track", nspares);
+ if (ncspares >= nsectors)
+ deverr (5, 0, "%d: Too many spare sectors per cylinder", ncspares);
+ nphyssectors = nsectors + nspares;
+
+ secpercyl = nsectors * ntracks;
+
+ DEFAULT (rpm, DL_INT (0, d_rpm));
+ DEFAULT (interleave, DL_INT (0, d_interleave));
+ DEFAULT (trackskew, DL_SECS (0, d_trackskew));
+ DEFAULT (headswitch, DL_INT (0, d_headswitch));
+ DEFAULT (trackseek, DL_INT (0, d_trkseek));
+
+ DEFAULT (fsize, 1024);
+ DEFAULT (bsize, 8192);
+
+ DEFAULT (cpg, 16);
+ DEFAULT (minfree, MINFREE);
+ DEFAULT (opt, DEFAULTOPT);
+ DEFAULT (density, 4 * fsize);
+/* maxcontig = MAX (1, MIN (MAXPHYS, MAXBSIZE) / bsize - 1); */
+ DEFAULT (maxcontig, 0);
+ DEFAULT (rotdelay, 4);
+#define MAXBLKPG(bsize) ((bsize) / sizeof(daddr_t))
+ DEFAULT (maxbpg, MAXBLKPG (bsize));
+ DEFAULT (nrpos, 8);
+
+ bbsize = BBSIZE;
+ sbsize = SBSIZE;
+
+ mkfs (0, device, fdi, fdo);
+
+ return 0;
+}
+
+void
+mkfs(pp, fsys, fi, fo)
+ struct partition *pp;
+ char *fsys;
+ int fi, fo;
+{
+ register long i, mincpc, mincpg, inospercg;
+ long cylno, rpos, blk, j, warn = 0;
+ long used, mincpgcnt, bpcg;
+ long mapcramped, inodecramped;
+ long postblsize, rotblsize, totalsbsize;
+ time_t utime;
+ quad_t sizepb;
+
+#ifndef STANDALONE
+ time(&utime);
+#endif
+ fsi = fi;
+ fso = fo;
+ if (Oflag) {
+ sblock.fs_inodefmt = FS_42INODEFMT;
+ sblock.fs_maxsymlinklen = 0;
+ } else {
+ sblock.fs_inodefmt = FS_44INODEFMT;
+ sblock.fs_maxsymlinklen = MAXSYMLINKLEN;
+ }
+ /*
+ * Validate the given file system size.
+ * Verify that its last block can actually be accessed.
+ */
+ if (fssize <= 0)
+ deverr (13, 0, "preposterous size %d", fssize);
+ wtfs(fssize - 1, sectorsize, (char *)&sblock);
+ /*
+ * collect and verify the sector and track info
+ */
+ sblock.fs_nsect = nsectors;
+ sblock.fs_ntrak = ntracks;
+ if (sblock.fs_ntrak <= 0)
+ deverr (14, 0, "preposterous ntrak %ld", sblock.fs_ntrak);
+ if (sblock.fs_nsect <= 0)
+ deverr (15, 0, "preposterous nsect %ld", sblock.fs_nsect);
+ /*
+ * collect and verify the block and fragment sizes
+ */
+ sblock.fs_bsize = bsize;
+ sblock.fs_fsize = fsize;
+ if (!POWEROF2(sblock.fs_bsize))
+ deverr (16, 0,
+ "block size must be a power of 2, not %ld",
+ sblock.fs_bsize);
+ if (!POWEROF2(sblock.fs_fsize))
+ deverr (17, 0,
+ "fragment size must be a power of 2, not %ld",
+ sblock.fs_fsize);
+ if (sblock.fs_fsize < sectorsize)
+ deverr (18, 0,
+ "fragment size %ld is too small, minimum is %d",
+ sblock.fs_fsize, sectorsize);
+ if (sblock.fs_bsize < MINBSIZE)
+ deverr (19, 0,
+ "block size %ld is too small, minimum is %d",
+ sblock.fs_bsize, MINBSIZE);
+ if (sblock.fs_bsize < sblock.fs_fsize)
+ deverr (20, 0,
+ "block size (%ld) cannot be smaller than fragment size (%ld)",
+ sblock.fs_bsize, sblock.fs_fsize);
+ 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)
+ deverr (21, 0,
+ "fragment size %ld is too small, minimum with block size %ld is %ld",
+ sblock.fs_fsize, sblock.fs_bsize,
+ sblock.fs_bsize / MAXFRAG);
+ sblock.fs_nrpos = nrpos;
+ sblock.fs_nindir = sblock.fs_bsize / sizeof(daddr_t);
+ sblock.fs_inopb = sblock.fs_bsize / sizeof(struct dinode);
+ sblock.fs_nspf = sblock.fs_fsize / sectorsize;
+ for (sblock.fs_fsbtodb = 0, i = NSPF(&sblock); i > 1; i >>= 1)
+ sblock.fs_fsbtodb++;
+ sblock.fs_sblkno =
+ roundup(howmany(bbsize + sbsize, sblock.fs_fsize), sblock.fs_frag);
+ sblock.fs_cblkno = (daddr_t)(sblock.fs_sblkno +
+ roundup(howmany(sbsize, sblock.fs_fsize), sblock.fs_frag));
+ sblock.fs_iblkno = sblock.fs_cblkno + sblock.fs_frag;
+ sblock.fs_cgoffset = roundup(
+ howmany(sblock.fs_nsect, NSPF(&sblock)), sblock.fs_frag);
+ for (sblock.fs_cgmask = 0xffffffff, i = sblock.fs_ntrak; i > 1; i >>= 1)
+ sblock.fs_cgmask <<= 1;
+ if (!POWEROF2(sblock.fs_ntrak))
+ sblock.fs_cgmask <<= 1;
+ 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;
+ }
+ /*
+ * Validate specified/determined secpercyl
+ * and calculate minimum cylinders per group.
+ */
+ sblock.fs_spc = secpercyl;
+ for (sblock.fs_cpc = NSPB(&sblock), i = sblock.fs_spc;
+ sblock.fs_cpc > 1 && (i & 1) == 0;
+ sblock.fs_cpc >>= 1, i >>= 1)
+ /* void */;
+ mincpc = sblock.fs_cpc;
+ bpcg = sblock.fs_spc * sectorsize;
+ inospercg = roundup(bpcg / sizeof(struct dinode), INOPB(&sblock));
+ if (inospercg > MAXIPG(&sblock))
+ inospercg = MAXIPG(&sblock);
+ used = (sblock.fs_iblkno + inospercg / INOPF(&sblock)) * NSPF(&sblock);
+ mincpgcnt = howmany(sblock.fs_cgoffset * (~sblock.fs_cgmask) + used,
+ sblock.fs_spc);
+ mincpg = roundup(mincpgcnt, mincpc);
+ /*
+ * Ensure that cylinder group with mincpg has enough space
+ * for block maps.
+ */
+ sblock.fs_cpg = mincpg;
+ sblock.fs_ipg = inospercg;
+ if (maxcontig > 1)
+ sblock.fs_contigsumsize = MIN(maxcontig, FS_MAXCONTIG);
+ mapcramped = 0;
+ while (CGSIZE(&sblock) > sblock.fs_bsize) {
+ mapcramped = 1;
+ if (sblock.fs_bsize < MAXBSIZE) {
+ sblock.fs_bsize <<= 1;
+ if ((i & 1) == 0) {
+ i >>= 1;
+ } else {
+ sblock.fs_cpc <<= 1;
+ mincpc <<= 1;
+ mincpg = roundup(mincpgcnt, mincpc);
+ sblock.fs_cpg = mincpg;
+ }
+ sblock.fs_frag <<= 1;
+ sblock.fs_fragshift += 1;
+ if (sblock.fs_frag <= MAXFRAG)
+ continue;
+ }
+ if (sblock.fs_fsize == sblock.fs_bsize)
+ deverr (22, 0,
+ "There is no block size that can support this disk");
+ sblock.fs_frag >>= 1;
+ sblock.fs_fragshift -= 1;
+ sblock.fs_fsize <<= 1;
+ sblock.fs_nspf <<= 1;
+ }
+ /*
+ * Ensure that cylinder group with mincpg has enough space for inodes.
+ */
+ inodecramped = 0;
+ used *= sectorsize;
+ inospercg = roundup((mincpg * bpcg - used) / density, INOPB(&sblock));
+ sblock.fs_ipg = inospercg;
+ while (inospercg > MAXIPG(&sblock)) {
+ inodecramped = 1;
+ if (mincpc == 1 || sblock.fs_frag == 1 ||
+ sblock.fs_bsize == MINBSIZE)
+ break;
+ deverr (0, 0,
+ "With a block size of %ld %s %ld", sblock.fs_bsize,
+ "minimum bytes per inode is",
+ (mincpg * bpcg - used) / MAXIPG(&sblock) + 1);
+ sblock.fs_bsize >>= 1;
+ sblock.fs_frag >>= 1;
+ sblock.fs_fragshift -= 1;
+ mincpc >>= 1;
+ sblock.fs_cpg = roundup(mincpgcnt, mincpc);
+ if (CGSIZE(&sblock) > sblock.fs_bsize) {
+ sblock.fs_bsize <<= 1;
+ break;
+ }
+ mincpg = sblock.fs_cpg;
+ inospercg =
+ roundup((mincpg * bpcg - used) / density, INOPB(&sblock));
+ sblock.fs_ipg = inospercg;
+ }
+ if (inodecramped) {
+ if (inospercg > MAXIPG(&sblock))
+ deverr (0, 0, "Minimum bytes per inode is %ld",
+ (mincpg * bpcg - used) / MAXIPG(&sblock) + 1);
+ else if (!mapcramped)
+ deverr (0, 0,
+ "With %d bytes per inode,"
+ " minimum cylinders per group is %ld",
+ density, mincpg);
+ }
+ if (mapcramped)
+ deverr (0, 0,
+ "With %ld sectors per cylinder,"
+ " minimum cylinders per group is %ld",
+ sblock.fs_spc, mincpg);
+ if (inodecramped || mapcramped)
+ if (sblock.fs_bsize != bsize)
+ {
+ deverr (0, 0,
+ "This requires the block size to be changed from %d to %ld",
+ bsize, sblock.fs_bsize);
+ deverr (23, 0,
+ "and the fragment size to be changed from %d to %ld",
+ fsize, sblock.fs_fsize);
+ }
+ else
+ exit(23);
+ /*
+ * Calculate the number of cylinders per group
+ */
+ sblock.fs_cpg = cpg;
+ if (sblock.fs_cpg % mincpc != 0) {
+ deverr (0, 0,
+ "%s groups must have a multiple of %ld cylinders",
+ cpgflg ? "Cylinder" : "Warning: cylinder", mincpc);
+ sblock.fs_cpg = roundup(sblock.fs_cpg, mincpc);
+ if (!cpgflg)
+ cpg = sblock.fs_cpg;
+ }
+ /*
+ * Must ensure there is enough space for inodes.
+ */
+ sblock.fs_ipg = roundup((sblock.fs_cpg * bpcg - used) / density,
+ INOPB(&sblock));
+ while (sblock.fs_ipg > MAXIPG(&sblock)) {
+ inodecramped = 1;
+ sblock.fs_cpg -= mincpc;
+ sblock.fs_ipg = roundup((sblock.fs_cpg * bpcg - used) / density,
+ INOPB(&sblock));
+ }
+ /*
+ * Must ensure there is enough space to hold block map.
+ */
+ while (CGSIZE(&sblock) > sblock.fs_bsize) {
+ mapcramped = 1;
+ sblock.fs_cpg -= mincpc;
+ sblock.fs_ipg = roundup((sblock.fs_cpg * bpcg - used) / density,
+ INOPB(&sblock));
+ }
+ sblock.fs_fpg = (sblock.fs_cpg * sblock.fs_spc) / NSPF(&sblock);
+ if ((sblock.fs_cpg * sblock.fs_spc) % NSPB(&sblock) != 0)
+ deverr (24, 0, "panic (fs_cpg * fs_spc) %% NSPF != 0");
+ if (sblock.fs_cpg < mincpg)
+ deverr (25, 0,
+ "cylinder groups must have at least %ld cylinders", mincpg);
+ else if (sblock.fs_cpg != cpg)
+ {
+ if (cpgflg && !mapcramped && !inodecramped)
+ exit(26);
+ deverr (0, 0,
+ "%s%s cylinders per group to %ld",
+ (cpgflg ? "" : "Warning: "),
+ ((mapcramped && inodecramped)
+ ? "Block size and bytes per inode restrict"
+ : mapcramped ? "Block size restricts"
+ : "Bytes per inode restrict"),
+ sblock.fs_cpg);
+ if (cpgflg)
+ exit(27);
+ }
+ sblock.fs_cgsize = fragroundup(&sblock, CGSIZE(&sblock));
+ /*
+ * Now have size for file system and nsect and ntrak.
+ * Determine number of cylinders and blocks in the file system.
+ */
+ sblock.fs_size = fssize = dbtofsb(&sblock, fssize);
+ sblock.fs_ncyl = fssize * NSPF(&sblock) / sblock.fs_spc;
+ if (fssize * NSPF(&sblock) > sblock.fs_ncyl * sblock.fs_spc) {
+ sblock.fs_ncyl++;
+ warn = 1;
+ }
+ if (sblock.fs_ncyl < 1)
+ deverr (28, 0, "file systems must have at least one cylinder");
+ /*
+ * Determine feasability/values of rotational layout tables.
+ *
+ * The size of the rotational layout tables is limited by the
+ * size of the superblock, SBSIZE. The amount of space available
+ * for tables is calculated as (SBSIZE - sizeof (struct fs)).
+ * The size of these tables is inversely proportional to the block
+ * size of the file system. The size increases if sectors per track
+ * are not powers of two, because more cylinders must be described
+ * by the tables before the rotational pattern repeats (fs_cpc).
+ */
+ sblock.fs_interleave = interleave;
+ sblock.fs_trackskew = trackskew;
+ sblock.fs_npsect = nphyssectors;
+ sblock.fs_postblformat = FS_DYNAMICPOSTBLFMT;
+ sblock.fs_sbsize = fragroundup(&sblock, sizeof(struct fs));
+ if (sblock.fs_ntrak == 1) {
+ sblock.fs_cpc = 0;
+ goto next;
+ }
+ postblsize = sblock.fs_nrpos * sblock.fs_cpc * sizeof(short);
+ rotblsize = sblock.fs_cpc * sblock.fs_spc / NSPB(&sblock);
+ totalsbsize = sizeof(struct fs) + rotblsize;
+ if (sblock.fs_nrpos == 8 && sblock.fs_cpc <= 16) {
+ /* use old static table space */
+ sblock.fs_postbloff = (char *)(&sblock.fs_opostbl[0][0]) -
+ (char *)(&sblock.fs_link);
+ sblock.fs_rotbloff = &sblock.fs_space[0] -
+ (u_char *)(&sblock.fs_link);
+ } else {
+ /* use dynamic table space */
+ sblock.fs_postbloff = &sblock.fs_space[0] -
+ (u_char *)(&sblock.fs_link);
+ sblock.fs_rotbloff = sblock.fs_postbloff + postblsize;
+ totalsbsize += postblsize;
+ }
+ if (totalsbsize > SBSIZE ||
+ sblock.fs_nsect > (1 << NBBY) * NSPB(&sblock))
+ {
+ deverr (0, 0,
+ "Warning: insufficient space in super block for "
+ "rotational layout tables with nsect %ld and ntrak %ld",
+ sblock.fs_nsect, sblock.fs_ntrak);
+ deverr (0, 0, "File system performance may be impaired");
+ sblock.fs_cpc = 0;
+ goto next;
+ }
+ sblock.fs_sbsize = fragroundup(&sblock, totalsbsize);
+ /*
+ * calculate the available blocks for each rotational position
+ */
+ for (cylno = 0; cylno < sblock.fs_cpc; cylno++)
+ for (rpos = 0; rpos < sblock.fs_nrpos; rpos++)
+ fs_postbl(&sblock, cylno)[rpos] = -1;
+ for (i = (rotblsize - 1) * sblock.fs_frag;
+ i >= 0; i -= sblock.fs_frag) {
+ cylno = cbtocylno(&sblock, i);
+ rpos = cbtorpos(&sblock, i);
+ blk = fragstoblks(&sblock, i);
+ if (fs_postbl(&sblock, cylno)[rpos] == -1)
+ fs_rotbl(&sblock)[blk] = 0;
+ else
+ fs_rotbl(&sblock)[blk] =
+ fs_postbl(&sblock, cylno)[rpos] - blk;
+ fs_postbl(&sblock, cylno)[rpos] = blk;
+ }
+next:
+ /*
+ * Compute/validate number of cylinder groups.
+ */
+ sblock.fs_ncg = sblock.fs_ncyl / sblock.fs_cpg;
+ if (sblock.fs_ncyl % sblock.fs_cpg)
+ sblock.fs_ncg++;
+ sblock.fs_dblkno = sblock.fs_iblkno + sblock.fs_ipg / INOPF(&sblock);
+ i = MIN(~sblock.fs_cgmask, sblock.fs_ncg - 1);
+ if (cgdmin(&sblock, i) - cgbase(&sblock, i) >= sblock.fs_fpg)
+ {
+ deverr (0, 0,
+ "Inode blocks/cyl group (%ld) >= data blocks (%ld)",
+ cgdmin(&sblock, i) - cgbase(&sblock, i) / sblock.fs_frag,
+ sblock.fs_fpg / sblock.fs_frag);
+ deverr (29, 0,
+ "number of cylinders per cylinder group (%ld)"
+ " must be increased", sblock.fs_cpg);
+ }
+ j = sblock.fs_ncg - 1;
+ if ((i = fssize - j * sblock.fs_fpg) < sblock.fs_fpg &&
+ cgdmin(&sblock, j) - cgbase(&sblock, j) > i) {
+ if (j == 0)
+ deverr (30, 0,
+ "Filesystem must have at least %ld sectors",
+ NSPF(&sblock)
+ * (cgdmin(&sblock, 0) + 3 * sblock.fs_frag));
+ deverr (0, 0,
+ "Warning: inode blocks/cyl group (%ld) >="
+ " data blocks (%ld) in last cylinder group.",
+ ((cgdmin(&sblock, j) - cgbase(&sblock, j))
+ / sblock.fs_frag),
+ i / sblock.fs_frag);
+ deverr (0, 0,
+ "This implies %ld sector(s) cannot be allocated",
+ i * NSPF(&sblock));
+ sblock.fs_ncg--;
+ sblock.fs_ncyl -= sblock.fs_ncyl % sblock.fs_cpg;
+ sblock.fs_size = fssize = sblock.fs_ncyl * sblock.fs_spc /
+ NSPF(&sblock);
+ warn = 0;
+ }
+ if (warn)
+ deverr (0, 0,
+ "Warning: %ld sector(s) in last cylinder unallocated",
+ sblock.fs_spc
+ - (fssize * NSPF(&sblock) - (sblock.fs_ncyl - 1)
+ * sblock.fs_spc));
+ /*
+ * 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));
+ i = sblock.fs_bsize / sizeof(struct csum);
+ sblock.fs_csmask = ~(i - 1);
+ for (sblock.fs_csshift = 0; i > 1; i >>= 1)
+ sblock.fs_csshift++;
+ fscs = (struct csum *)calloc(1, sblock.fs_cssize);
+ sblock.fs_magic = FS_MAGIC;
+ sblock.fs_rotdelay = rotdelay;
+ sblock.fs_minfree = minfree;
+ sblock.fs_maxcontig = maxcontig;
+ sblock.fs_headswitch = headswitch;
+ sblock.fs_trkseek = trackseek;
+ sblock.fs_maxbpg = maxbpg;
+ sblock.fs_rps = rpm / 60;
+ sblock.fs_optim = opt;
+ sblock.fs_cgrotor = 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_clean = 1;
+
+ /*
+ * Dump out summary information about file system.
+ */
+ printf("%s:\n\t%ld sectors in %ld %s of %ld tracks, %ld sectors\n",
+ fsys, sblock.fs_size * NSPF(&sblock), sblock.fs_ncyl,
+ "cylinders", sblock.fs_ntrak, sblock.fs_nsect);
+#define B2MBFACTOR (1 / (1024.0 * 1024.0))
+ printf("\t%.1fMB in %ld cyl groups (%ld c/g, %.2fMB/g, %ld i/g)\n",
+ (float)sblock.fs_size * sblock.fs_fsize * B2MBFACTOR,
+ sblock.fs_ncg, sblock.fs_cpg,
+ (float)sblock.fs_fpg * sblock.fs_fsize * B2MBFACTOR,
+ sblock.fs_ipg);
+#undef B2MBFACTOR
+
+ /*
+ * Now build the cylinders group blocks and
+ * then print out indices of cylinder groups.
+ */
+ printf("\tsuperblock backups at:");
+ for (cylno = 0; cylno < sblock.fs_ncg; cylno++) {
+ initcg(cylno, utime);
+ if (cylno % 8 == 0)
+ printf("\n\t");
+ printf(" %ld,", fsbtodb(&sblock, cgsblock(&sblock, cylno)));
+ }
+ printf("\n");
+ if (Nflag)
+ exit(0);
+ /*
+ * Now construct the initial file system,
+ * then write out the super-block.
+ */
+ fsinit(utime);
+ sblock.fs_time = utime;
+ wtfs((int)SBOFF / sectorsize, sbsize, (char *)&sblock);
+ for (i = 0; i < sblock.fs_cssize; i += sblock.fs_bsize)
+ wtfs(fsbtodb(&sblock, sblock.fs_csaddr + numfrags(&sblock, i)),
+ sblock.fs_cssize - i < sblock.fs_bsize ?
+ sblock.fs_cssize - i : sblock.fs_bsize,
+ ((char *)fscs) + i);
+ /*
+ * Write out the duplicate super blocks
+ */
+ for (cylno = 0; cylno < sblock.fs_ncg; cylno++)
+ wtfs(fsbtodb(&sblock, cgsblock(&sblock, cylno)),
+ sbsize, (char *)&sblock);
+#if 0 /* Not in Hurd (yet) */
+ /*
+ * Update information about this partion in pack
+ * label, to that it may be updated on disk.
+ */
+ pp->p_fstype = FS_BSDFFS;
+ pp->p_fsize = sblock.fs_fsize;
+ pp->p_frag = sblock.fs_frag;
+ pp->p_cpg = sblock.fs_cpg;
+#endif
+}
+
+/*
+ * Initialize a cylinder group.
+ */
+void
+initcg(cylno, utime)
+ int cylno;
+ time_t utime;
+{
+ long i;
+ daddr_t cbase, d, dlower, dupper, dmax, blkno;
+ register struct csum *cs;
+
+ /*
+ * 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);
+ cs = fscs + cylno;
+ bzero(&acg, sblock.fs_cgsize);
+ acg.cg_time = utime;
+ acg.cg_magic = CG_MAGIC;
+ acg.cg_cgx = cylno;
+ if (cylno == sblock.fs_ncg - 1)
+ acg.cg_ncyl = sblock.fs_ncyl % sblock.fs_cpg;
+ else
+ acg.cg_ncyl = sblock.fs_cpg;
+ acg.cg_niblk = sblock.fs_ipg;
+ acg.cg_ndblk = dmax - cbase;
+ if (sblock.fs_contigsumsize > 0)
+ acg.cg_nclusterblks = acg.cg_ndblk / sblock.fs_frag;
+ acg.cg_btotoff = &acg.cg_space[0] - (u_char *)(&acg.cg_link);
+ acg.cg_boff = acg.cg_btotoff + sblock.fs_cpg * sizeof(long);
+ acg.cg_iusedoff = acg.cg_boff +
+ sblock.fs_cpg * sblock.fs_nrpos * sizeof(short);
+ acg.cg_freeoff = acg.cg_iusedoff + howmany(sblock.fs_ipg, NBBY);
+ if (sblock.fs_contigsumsize <= 0) {
+ acg.cg_nextfreeoff = acg.cg_freeoff +
+ howmany(sblock.fs_cpg * sblock.fs_spc / NSPF(&sblock), NBBY);
+ } else {
+ acg.cg_clustersumoff = acg.cg_freeoff + howmany
+ (sblock.fs_cpg * sblock.fs_spc / NSPF(&sblock), NBBY) -
+ sizeof(long);
+ acg.cg_clustersumoff =
+ roundup(acg.cg_clustersumoff, sizeof(long));
+ acg.cg_clusteroff = acg.cg_clustersumoff +
+ (sblock.fs_contigsumsize + 1) * sizeof(long);
+ acg.cg_nextfreeoff = acg.cg_clusteroff + howmany
+ (sblock.fs_cpg * sblock.fs_spc / NSPB(&sblock), NBBY);
+ }
+ if (acg.cg_nextfreeoff - (long)(&acg.cg_link) > sblock.fs_cgsize)
+ deverr (37, 0, "Panic: cylinder group too big");
+ acg.cg_cs.cs_nifree += sblock.fs_ipg;
+ if (cylno == 0)
+ for (i = 0; i < ROOTINO; i++) {
+ setbit(cg_inosused(&acg), i);
+ acg.cg_cs.cs_nifree--;
+ }
+ for (i = 0; i < sblock.fs_ipg / INOPF(&sblock); i += sblock.fs_frag)
+ wtfs(fsbtodb(&sblock, cgimin(&sblock, cylno) + i),
+ sblock.fs_bsize, (char *)zino);
+ if (cylno > 0) {
+ /*
+ * In cylno 0, beginning space is reserved
+ * for boot and super blocks.
+ */
+ for (d = 0; d < dlower; d += sblock.fs_frag) {
+ blkno = d / sblock.fs_frag;
+ setblock(&sblock, cg_blksfree(&acg), blkno);
+ if (sblock.fs_contigsumsize > 0)
+ setbit(cg_clustersfree(&acg), blkno);
+ acg.cg_cs.cs_nbfree++;
+ cg_blktot(&acg)[cbtocylno(&sblock, d)]++;
+ cg_blks(&sblock, &acg, cbtocylno(&sblock, d))
+ [cbtorpos(&sblock, d)]++;
+ }
+ sblock.fs_dsize += dlower;
+ }
+ sblock.fs_dsize += acg.cg_ndblk - dupper;
+ i = dupper % sblock.fs_frag;
+ if (i) {
+ acg.cg_frsum[sblock.fs_frag - i]++;
+ for (d = dupper + sblock.fs_frag - i; dupper < d; dupper++) {
+ setbit(cg_blksfree(&acg), dupper);
+ acg.cg_cs.cs_nffree++;
+ }
+ }
+ for (d = dupper; d + sblock.fs_frag <= dmax - cbase; ) {
+ blkno = d / sblock.fs_frag;
+ setblock(&sblock, cg_blksfree(&acg), blkno);
+ if (sblock.fs_contigsumsize > 0)
+ setbit(cg_clustersfree(&acg), blkno);
+ acg.cg_cs.cs_nbfree++;
+ cg_blktot(&acg)[cbtocylno(&sblock, d)]++;
+ cg_blks(&sblock, &acg, cbtocylno(&sblock, d))
+ [cbtorpos(&sblock, d)]++;
+ d += sblock.fs_frag;
+ }
+ if (d < dmax - cbase) {
+ acg.cg_frsum[dmax - cbase - d]++;
+ for (; d < dmax - cbase; d++) {
+ setbit(cg_blksfree(&acg), d);
+ acg.cg_cs.cs_nffree++;
+ }
+ }
+ if (sblock.fs_contigsumsize > 0) {
+ long *sump = cg_clustersum(&acg);
+ u_char *mapp = cg_clustersfree(&acg);
+ 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 & (NBBY - 1)) != (NBBY - 1)) {
+ bit <<= 1;
+ } else {
+ map = *mapp++;
+ bit = 1;
+ }
+ }
+ if (run != 0) {
+ if (run > sblock.fs_contigsumsize)
+ run = sblock.fs_contigsumsize;
+ sump[run]++;
+ }
+ }
+ sblock.fs_cstotal.cs_ndir += acg.cg_cs.cs_ndir;
+ sblock.fs_cstotal.cs_nffree += acg.cg_cs.cs_nffree;
+ sblock.fs_cstotal.cs_nbfree += acg.cg_cs.cs_nbfree;
+ sblock.fs_cstotal.cs_nifree += acg.cg_cs.cs_nifree;
+ *cs = acg.cg_cs;
+ wtfs(fsbtodb(&sblock, cgtod(&sblock, cylno)),
+ sblock.fs_bsize, (char *)&acg);
+}
+
+/*
+ * initialize the file system
+ */
+struct dinode node;
+
+#ifdef LOSTDIR
+#define PREDEFDIR 3
+#else
+#define PREDEFDIR 2
+#endif
+
+struct directory_entry root_dir[] = {
+ { ROOTINO, sizeof(struct directory_entry), DT_DIR, 1, "." },
+ { ROOTINO, sizeof(struct directory_entry), DT_DIR, 2, ".." },
+#ifdef LOSTDIR
+ { LOSTFOUNDINO, sizeof(struct directory_entry), DT_DIR, 10, "lost+found" },
+#endif
+};
+struct odirectory_entry {
+ u_long d_ino;
+ u_short d_reclen;
+ u_short d_namlen;
+ u_char d_name[MAXNAMLEN + 1];
+} oroot_dir[] = {
+ { ROOTINO, sizeof(struct directory_entry), 1, "." },
+ { ROOTINO, sizeof(struct directory_entry), 2, ".." },
+#ifdef LOSTDIR
+ { LOSTFOUNDINO, sizeof(struct directory_entry), 10, "lost+found" },
+#endif
+};
+#ifdef LOSTDIR
+struct directory_entry lost_found_dir[] = {
+ { LOSTFOUNDINO, sizeof(struct directory_entry), DT_DIR, 1, "." },
+ { ROOTINO, sizeof(struct directory_entry), DT_DIR, 2, ".." },
+ { 0, DIRBLKSIZ, 0, 0, 0 },
+};
+struct odirectory_entry olost_found_dir[] = {
+ { LOSTFOUNDINO, sizeof(struct directory_entry), 1, "." },
+ { ROOTINO, sizeof(struct directory_entry), 2, ".." },
+ { 0, DIRBLKSIZ, 0, 0 },
+};
+#endif
+char buf[MAXBSIZE];
+
+void
+fsinit(utime)
+ time_t utime;
+{
+ /*
+ * initialize the node
+ */
+ node.di_atime.tv_sec = utime;
+ node.di_mtime.tv_sec = utime;
+ node.di_ctime.tv_sec = utime;
+#ifdef LOSTDIR
+ /*
+ * create the lost+found directory
+ */
+ if (Oflag) {
+ (void)makedir((struct directory_entry *)olost_found_dir, 2);
+ for (i = DIRBLKSIZ; i < sblock.fs_bsize; i += DIRBLKSIZ)
+ bcopy(&olost_found_dir[2], &buf[i],
+ DIRSIZ(0, &olost_found_dir[2]));
+ } else {
+ (void)makedir(lost_found_dir, 2);
+ for (i = DIRBLKSIZ; i < sblock.fs_bsize; i += DIRBLKSIZ)
+ bcopy(&lost_found_dir[2], &buf[i],
+ DIRSIZ(0, &lost_found_dir[2]));
+ }
+ node.di_model = ifdir | UMASK;
+ node.di_modeh = 0;
+ node.di_nlink = 2;
+ node.di_size = sblock.fs_bsize;
+ node.di_db[0] = alloc(node.di_size, DI_MODE (&node));
+ node.di_blocks = btodb(fragroundup(&sblock, node.di_size));
+ wtfs(fsbtodb(&sblock, node.di_db[0]), node.di_size, buf);
+ iput(&node, LOSTFOUNDINO);
+#endif
+ /*
+ * create the root directory
+ */
+ node.di_model = IFDIR | UMASK;
+ node.di_modeh = 0;
+ node.di_nlink = PREDEFDIR;
+
+ /* Set the uid/gid to non-root if run by a non-root user. This
+ is what mke2fs does in e2fsprogs-1.18 (actually it uses the
+ real IDs iff geteuid()!=0, but that is just wrong). */
+ node.di_uid = geteuid();
+ if (node.di_uid != 0)
+ node.di_gid = getegid();
+
+ if (Oflag)
+ node.di_size = makedir((struct directory_entry *)oroot_dir, PREDEFDIR);
+ else
+ node.di_size = makedir(root_dir, PREDEFDIR);
+ node.di_db[0] = alloc(sblock.fs_fsize, DI_MODE (&node));
+ node.di_blocks = btodb(fragroundup(&sblock, node.di_size));
+ wtfs(fsbtodb(&sblock, node.di_db[0]), sblock.fs_fsize, buf);
+ iput(&node, ROOTINO);
+}
+
+/*
+ * construct a set of directory entries in "buf".
+ * return size of directory.
+ */
+int
+makedir(protodir, entries)
+ register struct directory_entry *protodir;
+ int entries;
+{
+ char *cp;
+ int i, spcleft;
+
+ spcleft = DIRBLKSIZ;
+ for (cp = buf, i = 0; i < entries - 1; i++) {
+ protodir[i].d_reclen = DIRSIZ(0, &protodir[i]);
+ bcopy(&protodir[i], cp, protodir[i].d_reclen);
+ cp += protodir[i].d_reclen;
+ spcleft -= protodir[i].d_reclen;
+ }
+ protodir[i].d_reclen = spcleft;
+ bcopy(&protodir[i], cp, DIRSIZ(0, &protodir[i]));
+ return (DIRBLKSIZ);
+}
+
+/*
+ * allocate a block or frag
+ */
+daddr_t
+alloc(size, mode)
+ int size;
+ int mode;
+{
+ int i, frag;
+ daddr_t d, blkno;
+
+ rdfs(fsbtodb(&sblock, cgtod(&sblock, 0)), sblock.fs_cgsize,
+ (char *)&acg);
+ if (acg.cg_magic != CG_MAGIC) {
+ deverr (0, 0, "cg 0: bad magic number");
+ return (0);
+ }
+ if (acg.cg_cs.cs_nbfree == 0) {
+ deverr (0, 0, "first cylinder group ran out of space");
+ return (0);
+ }
+ for (d = 0; d < acg.cg_ndblk; d += sblock.fs_frag)
+ if (isblock(&sblock, cg_blksfree(&acg), d / sblock.fs_frag))
+ goto goth;
+ deverr (0, 0, "internal error: can't find block in cyl 0");
+ return (0);
+goth:
+ blkno = fragstoblks(&sblock, d);
+ clrblock(&sblock, cg_blksfree(&acg), blkno);
+ if (sblock.fs_contigsumsize > 0)
+ clrbit(cg_clustersfree(&acg), blkno);
+ acg.cg_cs.cs_nbfree--;
+ sblock.fs_cstotal.cs_nbfree--;
+ fscs[0].cs_nbfree--;
+ if (mode & IFDIR) {
+ acg.cg_cs.cs_ndir++;
+ sblock.fs_cstotal.cs_ndir++;
+ fscs[0].cs_ndir++;
+ }
+ cg_blktot(&acg)[cbtocylno(&sblock, d)]--;
+ cg_blks(&sblock, &acg, cbtocylno(&sblock, d))[cbtorpos(&sblock, d)]--;
+ if (size != sblock.fs_bsize) {
+ frag = howmany(size, sblock.fs_fsize);
+ fscs[0].cs_nffree += sblock.fs_frag - frag;
+ sblock.fs_cstotal.cs_nffree += sblock.fs_frag - frag;
+ acg.cg_cs.cs_nffree += sblock.fs_frag - frag;
+ acg.cg_frsum[sblock.fs_frag - frag]++;
+ for (i = frag; i < sblock.fs_frag; i++)
+ setbit(cg_blksfree(&acg), d + i);
+ }
+ wtfs(fsbtodb(&sblock, cgtod(&sblock, 0)), sblock.fs_cgsize,
+ (char *)&acg);
+ return (d);
+}
+
+/*
+ * Allocate an inode on the disk
+ */
+void
+iput(ip, ino)
+ register struct dinode *ip;
+ register ino_t ino;
+{
+ struct dinode buf[MAXINOPB];
+ daddr_t d;
+ int c;
+
+ c = ino_to_cg(&sblock, ino);
+ rdfs(fsbtodb(&sblock, cgtod(&sblock, 0)), sblock.fs_cgsize,
+ (char *)&acg);
+ if (acg.cg_magic != CG_MAGIC)
+ deverr (31, 0, "cg 0: bad magic number");
+ acg.cg_cs.cs_nifree--;
+ setbit(cg_inosused(&acg), ino);
+ wtfs(fsbtodb(&sblock, cgtod(&sblock, 0)), sblock.fs_cgsize,
+ (char *)&acg);
+ sblock.fs_cstotal.cs_nifree--;
+ fscs[0].cs_nifree--;
+ if (ino >= sblock.fs_ipg * sblock.fs_ncg)
+ deverr (32, 0, "fsinit: inode value out of range (%Ld)", ino);
+ d = fsbtodb(&sblock, ino_to_fsba(&sblock, ino));
+ rdfs(d, sblock.fs_bsize, buf);
+ buf[ino_to_fsbo(&sblock, ino)] = *ip;
+ wtfs(d, sblock.fs_bsize, buf);
+}
+
+/*
+ * read a block from the file system
+ */
+void
+rdfs(bno, size, bf)
+ daddr_t bno;
+ int size;
+ char *bf;
+{
+ int n;
+ if (lseek(fsi, (off_t)bno * sectorsize, 0) < 0)
+ deverr (33, errno, "rdfs: %ld: seek error", bno);
+ n = read(fsi, bf, size);
+ if (n != size)
+ deverr (34, errno, "rdfs: %ld: read error", bno);
+}
+
+/*
+ * write a block to the file system
+ */
+void
+wtfs(bno, size, bf)
+ daddr_t bno;
+ int size;
+ char *bf;
+{
+ int n;
+ if (Nflag)
+ return;
+ if (lseek(fso, (off_t)bno * sectorsize, SEEK_SET) < 0)
+ deverr (35, errno, "wtfs: %ld: seek error", bno);
+ n = write(fso, bf, size);
+ if (n != size)
+ deverr (36, errno, "wtfs: %ld: write error", bno);
+}
+
+/*
+ * check if a block is available
+ */
+int
+isblock(fs, cp, h)
+ struct fs *fs;
+ unsigned char *cp;
+ int h;
+{
+ unsigned char mask;
+
+ switch (fs->fs_frag) {
+ case 8:
+ return (cp[h] == 0xff);
+ case 4:
+ mask = 0x0f << ((h & 0x1) << 2);
+ return ((cp[h >> 1] & mask) == mask);
+ case 2:
+ mask = 0x03 << ((h & 0x3) << 1);
+ return ((cp[h >> 2] & mask) == mask);
+ case 1:
+ mask = 0x01 << (h & 0x7);
+ return ((cp[h >> 3] & mask) == mask);
+ default:
+ deverr (0, 0, "isblock bad fs_frag %ld", fs->fs_frag);
+ return (0);
+ }
+}
+
+/*
+ * take a block out of the map
+ */
+void
+clrblock(fs, cp, h)
+ struct fs *fs;
+ unsigned char *cp;
+ int h;
+{
+ switch ((fs)->fs_frag) {
+ case 8:
+ cp[h] = 0;
+ return;
+ case 4:
+ cp[h >> 1] &= ~(0x0f << ((h & 0x1) << 2));
+ return;
+ case 2:
+ cp[h >> 2] &= ~(0x03 << ((h & 0x3) << 1));
+ return;
+ case 1:
+ cp[h >> 3] &= ~(0x01 << (h & 0x7));
+ return;
+ default:
+ deverr (0, 0, "clrblock bad fs_frag %ld", fs->fs_frag);
+ return;
+ }
+}
+
+/*
+ * put a block into the map
+ */
+void
+setblock(fs, cp, h)
+ struct fs *fs;
+ unsigned char *cp;
+ int h;
+{
+ switch (fs->fs_frag) {
+ case 8:
+ cp[h] = 0xff;
+ return;
+ case 4:
+ cp[h >> 1] |= (0x0f << ((h & 0x1) << 2));
+ return;
+ case 2:
+ cp[h >> 2] |= (0x03 << ((h & 0x3) << 1));
+ return;
+ case 1:
+ cp[h >> 3] |= (0x01 << (h & 0x7));
+ return;
+ default:
+ deverr (0, 0, "setblock bad fs_frag %ld", fs->fs_frag);
+ return;
+ }
+}
diff --git a/ufs-utils/newfs.c b/ufs-utils/newfs.c
new file mode 100644
index 00000000..efc7dbea
--- /dev/null
+++ b/ufs-utils/newfs.c
@@ -0,0 +1,705 @@
+/*
+ * Copyright (c) 1983, 1989, 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. 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.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1983, 1989, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+/*static char sccsid[] = "from: @(#)newfs.c 8.8 (Berkeley) 4/18/94";*/
+static char *rcsid = "$Id: newfs.c,v 1.2 1994/09/09 14:11:41 mib Exp $";
+#endif /* not lint */
+
+/*
+ * newfs: friendly front end to mkfs
+ */
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+/* #include <sys/disklabel.h> */
+#include <sys/file.h>
+/* #include <sys/mount.h> */
+
+#include "../ufs/dir.h"
+#include "../ufs/fs.h"
+
+#include <ctype.h>
+#include <errno.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#if __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+/* #include "mntopts.h" */
+
+/* GNU Hurd additions */
+#define MAXPATHLEN 2048
+
+/* End GNU Hurd additions */
+
+
+#if 0
+struct mntopt mopts[] = {
+ MOPT_STDOPTS,
+ MOPT_ASYNC,
+ { NULL },
+};
+#endif
+
+#if __STDC__
+void fatal(const char *fmt, ...);
+#else
+void fatal();
+#endif
+
+#define COMPAT /* allow non-labeled disks */
+
+/*
+ * The following two constants set the default block and fragment sizes.
+ * Both constants must be a power of 2 and meet the following constraints:
+ * MINBSIZE <= DESBLKSIZE <= MAXBSIZE
+ * sectorsize <= DESFRAGSIZE <= DESBLKSIZE
+ * DESBLKSIZE / DESFRAGSIZE <= 8
+ */
+#define DFL_FRAGSIZE 1024
+#define DFL_BLKSIZE 8192
+
+/*
+ * Cylinder groups may have up to many cylinders. The actual
+ * number used depends upon how much information can be stored
+ * on a single cylinder. The default is to use 16 cylinders
+ * per group.
+ */
+#define DESCPG 16 /* desired fs_cpg */
+
+/*
+ * ROTDELAY gives the minimum number of milliseconds to initiate
+ * another disk transfer on the same cylinder. It is used in
+ * determining the rotationally optimal layout for disk blocks
+ * within a file; the default of fs_rotdelay is 4ms.
+ */
+#define ROTDELAY 4
+
+/*
+ * MAXBLKPG determines the maximum number of data blocks which are
+ * placed in a single cylinder group. The default is one indirect
+ * block worth of data blocks.
+ */
+#define MAXBLKPG(bsize) ((bsize) / sizeof(daddr_t))
+
+/*
+ * Each file system has a number of inodes statically allocated.
+ * We allocate one inode slot per NFPI fragments, expecting this
+ * to be far more than we will ever need.
+ */
+#define NFPI 4
+
+/*
+ * For each cylinder we keep track of the availability of blocks at different
+ * rotational positions, so that we can lay out the data to be picked
+ * up with minimum rotational latency. NRPOS is the default number of
+ * rotational positions that we distinguish. With NRPOS of 8 the resolution
+ * of our summary information is 2ms for a typical 3600 rpm drive.
+ */
+#define NRPOS 8 /* number distinct rotational positions */
+
+
+int mfs; /* run as the memory based filesystem */
+int Nflag; /* run without writing file system */
+int Oflag; /* format as an 4.3BSD file system */
+int fssize; /* file system size */
+int ntracks; /* # tracks/cylinder */
+int nsectors; /* # sectors/track */
+int nphyssectors; /* # sectors/track including spares */
+int secpercyl; /* sectors per cylinder */
+int trackspares = -1; /* spare sectors per track */
+int cylspares = -1; /* spare sectors per cylinder */
+int sectorsize; /* bytes/sector */
+#ifdef tahoe
+int realsectorsize; /* bytes/sector in hardware */
+#endif
+int rpm; /* revolutions/minute of drive */
+int interleave; /* hardware sector interleave */
+int trackskew = -1; /* sector 0 skew, per track */
+int headswitch; /* head switch time, usec */
+int trackseek; /* track-to-track seek, usec */
+int fsize = 0; /* fragment size */
+int bsize = 0; /* block size */
+int cpg = DESCPG; /* cylinders/cylinder group */
+int cpgflg; /* cylinders/cylinder group flag was given */
+int minfree = MINFREE; /* free space threshold */
+int opt = DEFAULTOPT; /* optimization preference (space or time) */
+int density; /* number of bytes per inode */
+int maxcontig = 0; /* max contiguous blocks to allocate */
+int rotdelay = ROTDELAY; /* rotational delay between blocks */
+int maxbpg; /* maximum blocks per file in a cyl group */
+int nrpos = NRPOS; /* # of distinguished rotational positions */
+int bbsize = BBSIZE; /* boot block size */
+int sbsize = SBSIZE; /* superblock size */
+/* int mntflags = MNT_ASYNC;*/ /* flags to be passed to mount */
+u_long memleft; /* virtual memory available */
+caddr_t membase; /* start address of memory based filesystem */
+#ifdef COMPAT
+char *disktype;
+int unlabeled;
+#endif
+
+char device[MAXPATHLEN];
+char *progname;
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ extern char *optarg;
+ extern int optind;
+ register int ch;
+ register struct partition *pp;
+ register struct disklabel *lp;
+ struct disklabel *getdisklabel();
+/* struct partition oldpartition; */
+ struct stat st;
+ struct statfs *mp;
+ int fsi, fso, len, n;
+ char *cp, *s1, *s2, *special, *opstring, buf[BUFSIZ];
+
+ if (progname = rindex(*argv, '/'))
+ ++progname;
+ else
+ progname = *argv;
+
+#if 0
+ if (strstr(progname, "mfs")) {
+ mfs = 1;
+ Nflag++;
+ }
+#endif
+
+ opstring = mfs ?
+ "NT:a:b:c:d:e:f:i:m:o:s:" :
+ "NOS:T:a:b:c:d:e:f:i:k:l:m:n:o:p:r:s:t:u:x:";
+ while ((ch = getopt(argc, argv, opstring)) != EOF)
+ switch (ch) {
+ case 'N':
+ Nflag = 1;
+ break;
+ case 'O':
+ Oflag = 1;
+ break;
+ case 'S':
+ if ((sectorsize = atoi(optarg)) <= 0)
+ fatal("%s: bad sector size", optarg);
+ break;
+#ifdef COMPAT
+ case 'T':
+ disktype = optarg;
+ break;
+#endif
+ case 'a':
+ if ((maxcontig = atoi(optarg)) <= 0)
+ fatal("%s: bad maximum contiguous blocks\n",
+ optarg);
+ break;
+ case 'b':
+ if ((bsize = atoi(optarg)) < MINBSIZE)
+ fatal("%s: bad block size", optarg);
+ break;
+ case 'c':
+ if ((cpg = atoi(optarg)) <= 0)
+ fatal("%s: bad cylinders/group", optarg);
+ cpgflg++;
+ break;
+ case 'd':
+ if ((rotdelay = atoi(optarg)) < 0)
+ fatal("%s: bad rotational delay\n", optarg);
+ break;
+ case 'e':
+ if ((maxbpg = atoi(optarg)) <= 0)
+ fatal("%s: bad blocks per file in a cylinder group\n",
+ optarg);
+ break;
+ case 'f':
+ if ((fsize = atoi(optarg)) <= 0)
+ fatal("%s: bad fragment size", optarg);
+ break;
+ case 'i':
+ if ((density = atoi(optarg)) <= 0)
+ fatal("%s: bad bytes per inode\n", optarg);
+ break;
+ case 'k':
+ if ((trackskew = atoi(optarg)) < 0)
+ fatal("%s: bad track skew", optarg);
+ break;
+ case 'l':
+ if ((interleave = atoi(optarg)) <= 0)
+ fatal("%s: bad interleave", optarg);
+ break;
+ case 'm':
+ if ((minfree = atoi(optarg)) < 0 || minfree > 99)
+ fatal("%s: bad free space %%\n", optarg);
+ break;
+ case 'n':
+ if ((nrpos = atoi(optarg)) <= 0)
+ fatal("%s: bad rotational layout count\n",
+ optarg);
+ break;
+ case 'o':
+#if 0
+ if (mfs)
+ getmntopts(optarg, mopts, &mntflags);
+ else
+#endif
+ {
+ if (strcmp(optarg, "space") == 0)
+ opt = FS_OPTSPACE;
+ else if (strcmp(optarg, "time") == 0)
+ opt = FS_OPTTIME;
+ else
+ fatal("%s: unknown optimization preference: use `space' or `time'.");
+ }
+ break;
+ case 'p':
+ if ((trackspares = atoi(optarg)) < 0)
+ fatal("%s: bad spare sectors per track",
+ optarg);
+ break;
+ case 'r':
+ if ((rpm = atoi(optarg)) <= 0)
+ fatal("%s: bad revolutions/minute\n", optarg);
+ break;
+ case 's':
+ if ((fssize = atoi(optarg)) <= 0)
+ fatal("%s: bad file system size", optarg);
+ break;
+ case 't':
+ if ((ntracks = atoi(optarg)) <= 0)
+ fatal("%s: bad total tracks", optarg);
+ break;
+ case 'u':
+ if ((nsectors = atoi(optarg)) <= 0)
+ fatal("%s: bad sectors/track", optarg);
+ break;
+ case 'x':
+ if ((cylspares = atoi(optarg)) < 0)
+ fatal("%s: bad spare sectors per cylinder",
+ optarg);
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 2 && (mfs || argc != 1))
+ usage();
+
+ special = argv[0];
+ cp = rindex(special, '/');
+ if (cp == 0) {
+ /*
+ * No path prefix; try /dev/r%s then /dev/%s.
+ */
+ (void)sprintf(device, "%sr%s", _PATH_DEV, special);
+ if (stat(device, &st) == -1)
+ (void)sprintf(device, "%s%s", _PATH_DEV, special);
+ special = device;
+ }
+ if (Nflag) {
+ fso = -1;
+ } else {
+ fso = open(special, O_WRONLY);
+ if (fso < 0)
+ fatal("%s: %s", special, strerror(errno));
+
+#if 0
+ /* Bail if target special is mounted */
+ n = getmntinfo(&mp, MNT_NOWAIT);
+ if (n == 0)
+ fatal("%s: getmntinfo: %s", special, strerror(errno));
+
+ len = sizeof(_PATH_DEV) - 1;
+ s1 = special;
+ if (strncmp(_PATH_DEV, s1, len) == 0)
+ s1 += len;
+
+ while (--n >= 0) {
+ s2 = mp->f_mntfromname;
+ if (strncmp(_PATH_DEV, s2, len) == 0) {
+ s2 += len - 1;
+ *s2 = 'r';
+ }
+ if (strcmp(s1, s2) == 0 || strcmp(s1, &s2[1]) == 0)
+ fatal("%s is mounted on %s",
+ special, mp->f_mntonname);
+ ++mp;
+ }
+#endif
+ }
+#if 0
+ if (mfs && disktype != NULL) {
+ lp = (struct disklabel *)getdiskbyname(disktype);
+ if (lp == NULL)
+ fatal("%s: unknown disk type", disktype);
+ pp = &lp->d_partitions[1];
+ } else {
+ fsi = open(special, O_RDONLY);
+ if (fsi < 0)
+ fatal("%s: %s", special, strerror(errno));
+ if (fstat(fsi, &st) < 0)
+ fatal("%s: %s", special, strerror(errno));
+ if ((st.st_mode & S_IFMT) != S_IFCHR && !mfs)
+ printf("%s: %s: not a character-special device\n",
+ progname, special);
+ cp = index(argv[0], '\0') - 1;
+ if (cp == 0 || (*cp < 'a' || *cp > 'h') && !isdigit(*cp))
+ fatal("%s: can't figure out file system partition",
+ argv[0]);
+#ifdef COMPAT
+ if (!mfs && disktype == NULL)
+ disktype = argv[1];
+#endif
+ lp = getdisklabel(special, fsi);
+ if (isdigit(*cp))
+ pp = &lp->d_partitions[0];
+ else
+ pp = &lp->d_partitions[*cp - 'a'];
+ if (pp->p_size == 0)
+ fatal("%s: `%c' partition is unavailable",
+ argv[0], *cp);
+ if (pp->p_fstype == FS_BOOT)
+ fatal("%s: `%c' partition overlaps boot program",
+ argv[0], *cp);
+ }
+#endif
+ /* GNU Hurd specific here */
+ fsi = open (special, O_RDONLY);
+ if (fsi < 0)
+ fatal("%s: %s", special, strerror(errno));
+ if (fstat(fsi, &st) < 0)
+ fatal("%s: %s", special, strerror(errno));
+ fssize = st.st_size;
+ /* End GNU Hurd specific */
+
+ if (fssize == 0)
+ fssize = pp->p_size;
+ if (fssize > pp->p_size && !mfs)
+ fatal("%s: maximum file system size on the `%c' partition is %d",
+ argv[0], *cp, pp->p_size);
+ if (rpm == 0) {
+ rpm = lp->d_rpm;
+ if (rpm <= 0)
+ rpm = 3600;
+ }
+ if (ntracks == 0) {
+ ntracks = lp->d_ntracks;
+ if (ntracks <= 0)
+ fatal("%s: no default #tracks", argv[0]);
+ }
+ if (nsectors == 0) {
+ nsectors = lp->d_nsectors;
+ if (nsectors <= 0)
+ fatal("%s: no default #sectors/track", argv[0]);
+ }
+ if (sectorsize == 0) {
+ sectorsize = lp->d_secsize;
+ if (sectorsize <= 0)
+ fatal("%s: no default sector size", argv[0]);
+ }
+ if (trackskew == -1) {
+ trackskew = lp->d_trackskew;
+ if (trackskew < 0)
+ trackskew = 0;
+ }
+ if (interleave == 0) {
+ interleave = lp->d_interleave;
+ if (interleave <= 0)
+ interleave = 1;
+ }
+ if (fsize == 0) {
+ fsize = pp->p_fsize;
+ if (fsize <= 0)
+ fsize = MAX(DFL_FRAGSIZE, lp->d_secsize);
+ }
+ if (bsize == 0) {
+ bsize = pp->p_frag * pp->p_fsize;
+ if (bsize <= 0)
+ bsize = MIN(DFL_BLKSIZE, 8 * fsize);
+ }
+ /*
+ * Maxcontig sets the default for the maximum number of blocks
+ * that may be allocated sequentially. With filesystem clustering
+ * it is possible to allocate contiguous blocks up to the maximum
+ * transfer size permitted by the controller or buffering.
+ */
+ if (maxcontig == 0)
+ maxcontig = MAX(1, MIN(MAXPHYS, MAXBSIZE) / bsize - 1);
+ if (density == 0)
+ density = NFPI * fsize;
+ if (minfree < MINFREE && opt != FS_OPTSPACE) {
+ fprintf(stderr, "Warning: changing optimization to space ");
+ fprintf(stderr, "because minfree is less than %d%%\n", MINFREE);
+ opt = FS_OPTSPACE;
+ }
+ if (trackspares == -1) {
+ trackspares = lp->d_sparespertrack;
+ if (trackspares < 0)
+ trackspares = 0;
+ }
+ nphyssectors = nsectors + trackspares;
+ if (cylspares == -1) {
+ cylspares = lp->d_sparespercyl;
+ if (cylspares < 0)
+ cylspares = 0;
+ }
+ secpercyl = nsectors * ntracks - cylspares;
+ if (secpercyl != lp->d_secpercyl)
+ fprintf(stderr, "%s (%d) %s (%lu)\n",
+ "Warning: calculated sectors per cylinder", secpercyl,
+ "disagrees with disk label", lp->d_secpercyl);
+ if (maxbpg == 0)
+ maxbpg = MAXBLKPG(bsize);
+ headswitch = lp->d_headswitch;
+ trackseek = lp->d_trkseek;
+#ifdef notdef /* label may be 0 if faked up by kernel */
+ bbsize = lp->d_bbsize;
+ sbsize = lp->d_sbsize;
+#endif
+ oldpartition = *pp;
+#ifdef tahoe
+ realsectorsize = sectorsize;
+ if (sectorsize != DEV_BSIZE) { /* XXX */
+ int secperblk = DEV_BSIZE / sectorsize;
+
+ sectorsize = DEV_BSIZE;
+ nsectors /= secperblk;
+ nphyssectors /= secperblk;
+ secpercyl /= secperblk;
+ fssize /= secperblk;
+ pp->p_size /= secperblk;
+ }
+#endif
+ mkfs(pp, special, fsi, fso);
+#ifdef tahoe
+ if (realsectorsize != DEV_BSIZE)
+ pp->p_size *= DEV_BSIZE / realsectorsize;
+#endif
+ if (!Nflag && bcmp(pp, &oldpartition, sizeof(oldpartition)))
+ rewritelabel(special, fso, lp);
+ if (!Nflag)
+ close(fso);
+ close(fsi);
+#ifdef MFS
+ if (mfs) {
+ struct mfs_args args;
+
+ sprintf(buf, "mfs:%d", getpid());
+ args.fspec = buf;
+ args.export.ex_root = -2;
+ if (mntflags & MNT_RDONLY)
+ args.export.ex_flags = MNT_EXRDONLY;
+ else
+ args.export.ex_flags = 0;
+ args.base = membase;
+ args.size = fssize * sectorsize;
+ if (mount(MOUNT_MFS, argv[1], mntflags, &args) < 0)
+ fatal("%s: %s", argv[1], strerror(errno));
+ }
+#endif
+ exit(0);
+}
+
+#ifdef COMPAT
+char lmsg[] = "%s: can't read disk label; disk type must be specified";
+#else
+char lmsg[] = "%s: can't read disk label";
+#endif
+
+struct disklabel *
+getdisklabel(s, fd)
+ char *s;
+ int fd;
+{
+ static struct disklabel lab;
+
+ if (ioctl(fd, DIOCGDINFO, (char *)&lab) < 0) {
+#ifdef COMPAT
+ if (disktype) {
+ struct disklabel *lp, *getdiskbyname();
+
+ unlabeled++;
+ lp = getdiskbyname(disktype);
+ if (lp == NULL)
+ fatal("%s: unknown disk type", disktype);
+ return (lp);
+ }
+#endif
+ warn("ioctl (GDINFO)");
+ fatal(lmsg, s);
+ }
+ return (&lab);
+}
+
+rewritelabel(s, fd, lp)
+ char *s;
+ int fd;
+ register struct disklabel *lp;
+{
+#ifdef COMPAT
+ if (unlabeled)
+ return;
+#endif
+ lp->d_checksum = 0;
+ lp->d_checksum = dkcksum(lp);
+ if (ioctl(fd, DIOCWDINFO, (char *)lp) < 0) {
+ warn("ioctl (WDINFO)");
+ fatal("%s: can't rewrite disk label", s);
+ }
+#if vax
+ if (lp->d_type == DTYPE_SMD && lp->d_flags & D_BADSECT) {
+ register i;
+ int cfd;
+ daddr_t alt;
+ char specname[64];
+ char blk[1024];
+ char *cp;
+
+ /*
+ * Make name for 'c' partition.
+ */
+ strcpy(specname, s);
+ cp = specname + strlen(specname) - 1;
+ if (!isdigit(*cp))
+ *cp = 'c';
+ cfd = open(specname, O_WRONLY);
+ if (cfd < 0)
+ fatal("%s: %s", specname, strerror(errno));
+ bzero(blk, sizeof(blk));
+ *(struct disklabel *)(blk + LABELOFFSET) = *lp;
+ alt = lp->d_ncylinders * lp->d_secpercyl - lp->d_nsectors;
+ for (i = 1; i < 11 && i < lp->d_nsectors; i += 2) {
+ if (lseek(cfd, (off_t)(alt + i) * lp->d_secsize,
+ L_SET) == -1)
+ fatal("lseek to badsector area: %s",
+ strerror(errno));
+ if (write(cfd, blk, lp->d_secsize) < lp->d_secsize)
+ warn("alternate label %d write", i/2);
+ }
+ close(cfd);
+ }
+#endif
+}
+
+/*VARARGS*/
+void
+#if __STDC__
+fatal(const char *fmt, ...)
+#else
+fatal(fmt, va_alist)
+ char *fmt;
+ va_dcl
+#endif
+{
+ va_list ap;
+
+#if __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+ if (fcntl(STDERR_FILENO, F_GETFL) < 0) {
+ openlog(progname, LOG_CONS, LOG_DAEMON);
+ vsyslog(LOG_ERR, fmt, ap);
+ closelog();
+ } else {
+ vwarnx(fmt, ap);
+ }
+ va_end(ap);
+ exit(1);
+ /*NOTREACHED*/
+}
+
+usage()
+{
+ if (mfs) {
+ fprintf(stderr,
+ "usage: %s [ -fsoptions ] special-device mount-point\n",
+ progname);
+ } else
+ fprintf(stderr,
+ "usage: %s [ -fsoptions ] special-device%s\n",
+ progname,
+#ifdef COMPAT
+ " [device-type]");
+#else
+ "");
+#endif
+ fprintf(stderr, "where fsoptions are:\n");
+ fprintf(stderr,
+ "\t-N do not create file system, just print out parameters\n");
+ fprintf(stderr, "\t-O create a 4.3BSD format filesystem\n");
+ fprintf(stderr, "\t-S sector size\n");
+#ifdef COMPAT
+ fprintf(stderr, "\t-T disktype\n");
+#endif
+ fprintf(stderr, "\t-a maximum contiguous blocks\n");
+ fprintf(stderr, "\t-b block size\n");
+ fprintf(stderr, "\t-c cylinders/group\n");
+ fprintf(stderr, "\t-d rotational delay between contiguous blocks\n");
+ fprintf(stderr, "\t-e maximum blocks per file in a cylinder group\n");
+ fprintf(stderr, "\t-f frag size\n");
+ fprintf(stderr, "\t-i number of bytes per inode\n");
+ fprintf(stderr, "\t-k sector 0 skew, per track\n");
+ fprintf(stderr, "\t-l hardware sector interleave\n");
+ fprintf(stderr, "\t-m minimum free space %%\n");
+ fprintf(stderr, "\t-n number of distinguished rotational positions\n");
+ fprintf(stderr, "\t-o optimization preference (`space' or `time')\n");
+ fprintf(stderr, "\t-p spare sectors per track\n");
+ fprintf(stderr, "\t-s file system size (sectors)\n");
+ fprintf(stderr, "\t-r revolutions/minute\n");
+ fprintf(stderr, "\t-t tracks/cylinder\n");
+ fprintf(stderr, "\t-u sectors/track\n");
+ fprintf(stderr, "\t-x spare sectors per cylinder\n");
+ exit(1);
+}
diff --git a/ufs-utils/stati.c b/ufs-utils/stati.c
new file mode 100644
index 00000000..ba545bf2
--- /dev/null
+++ b/ufs-utils/stati.c
@@ -0,0 +1,260 @@
+/*
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Rich $alz of BBN 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.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1990, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+
+static char copyright[] __attribute__ ((unused));
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)clri.c 8.2 (Berkeley) 9/23/93";
+static char sccsid[] __attribute__ ((unused));
+#endif /* not lint */
+
+/* Modified by Michael I. Bushnell for GNU Hurd. */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+
+#include "../ufs/dinode.h"
+#include "../ufs/fs.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <time.h>
+#include <unistd.h>
+#include <pwd.h>
+#include <grp.h>
+
+#define MAXPHYS (64 * 1024)
+#define DEV_BSIZE 512
+
+/* Returns a nice representation of a file mode in a static buffer. */
+static char *
+mode_rep (unsigned short mode)
+{
+ static char buf[30];
+ char *p = buf;
+
+ void add_perms (int shift, unsigned sid_mask)
+ {
+ unsigned short smode = mode << shift;
+ *p++ = (smode & S_IREAD) ? 'r' : '-';
+ *p++ = (smode & S_IWRITE) ? 'w' : '-';
+ *p++ = (smode & S_IEXEC) ? ((mode & sid_mask) ? 's' : 'x') : '-';
+ }
+
+ switch (mode & S_IFMT)
+ {
+ case S_IFREG: *p++ = '-'; break;
+ case S_IFDIR: *p++ = 'd'; break;
+ case S_IFCHR: *p++ = 'c'; break;
+ case S_IFBLK: *p++ = 'b'; break;
+ case S_IFLNK: *p++ = 'l'; break;
+ case S_IFSOCK:*p++ = 'p'; break;
+ case S_IFIFO: *p++ = 'f'; break;
+ default: *p++ = '?';
+ }
+
+ add_perms (0, S_ISUID);
+ add_perms (3, S_ISGID);
+ add_perms (6, 0);
+
+ snprintf (p, buf + sizeof buf - p, " [0%0o]", mode);
+
+ return buf;
+}
+
+/* Returns a nice representation of a struct timespec in a static buffer. */
+static char *
+timespec_rep (struct timespec *ts)
+{
+ static char buf[200];
+ char *p = buf;
+ if (ts->tv_sec || ts->tv_nsec)
+ {
+ time_t time = ts->tv_sec;
+ strcpy (buf, ctime (&time));
+ p += strlen (buf);
+ if (p[-1] == '\n')
+ p--;
+ *p++ = ' ';
+ }
+ snprintf (p, buf + sizeof buf - p, "[%ld, %ld]", ts->tv_sec, ts->tv_nsec);
+ return buf;
+}
+
+/* Returns a nice representation of a uid in a static buffer. */
+static char *
+uid_rep (uid_t uid)
+{
+ static char buf[200];
+ struct passwd *pw = getpwuid (uid);
+ if (pw)
+ snprintf (buf, sizeof buf, "%d(%s)", uid, pw->pw_name);
+ else
+ snprintf (buf, sizeof buf, "%d", uid);
+ return buf;
+}
+
+/* Returns a nice representation of a gid in a static buffer. */
+static char *
+gid_rep (gid_t gid)
+{
+ static char buf[200];
+ struct group *gr = getgrgid (gid);
+ if (gr)
+ snprintf (buf, sizeof buf, "%d(%s)", gid, gr->gr_name);
+ else
+ snprintf (buf, sizeof buf, "%d", gid);
+ return buf;
+}
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ register struct fs *sbp;
+ register struct dinode *ip;
+ register int fd;
+ struct dinode ibuf[MAXBSIZE / sizeof (struct dinode)];
+ long bsize;
+ off_t offset;
+ int inonum;
+ char *fs, sblock[SBSIZE];
+
+ if (argc < 3) {
+ (void)fprintf(stderr, "usage: stati filesystem inode ...\n");
+ exit(1);
+ }
+
+ fs = *++argv;
+
+ /* get the superblock. */
+ if ((fd = open(fs, O_RDWR, 0)) < 0)
+ {
+ perror (fs);
+ exit (1);
+ }
+ if (lseek(fd, (off_t)(SBLOCK * DEV_BSIZE), SEEK_SET) < 0)
+ {
+ perror (fs);
+ exit (1);
+ }
+ if (read(fd, sblock, sizeof(sblock)) != sizeof(sblock)) {
+ (void)fprintf(stderr,
+ "stati: %s: can't read the superblock.\n", fs);
+ exit(1);
+ }
+
+ sbp = (struct fs *)sblock;
+ if (sbp->fs_magic != FS_MAGIC) {
+ (void)fprintf(stderr,
+ "stati: %s: superblock magic number 0x%lux, not 0x%x.\n",
+ fs, sbp->fs_magic, FS_MAGIC);
+ exit(1);
+ }
+ bsize = sbp->fs_bsize;
+
+ /* remaining arguments are inode numbers. */
+ while (*++argv) {
+ int i;
+
+ /* get the inode number. */
+ if ((inonum = atoi(*argv)) <= 0) {
+ (void)fprintf(stderr,
+ "stati: %s is not a valid inode number.\n", *argv);
+ exit(1);
+ }
+
+ /* read in the appropriate block. */
+ offset = ino_to_fsba(sbp, inonum); /* inode to fs blk */
+ offset = fsbtodb(sbp, offset); /* fs blk disk blk */
+ offset *= DEV_BSIZE; /* disk blk to bytes */
+
+ /* seek and read the block */
+ if (lseek(fd, offset, SEEK_SET) < 0)
+ {
+ perror (fs);
+ exit (1);
+ }
+ if (read(fd, ibuf, bsize) != bsize)
+ {
+ perror (fs);
+ exit (1);
+ }
+
+ /* get the inode within the block. */
+ ip = &ibuf[ino_to_fsbo(sbp, inonum)];
+
+ if (argc > 3)
+ printf ("inode: %d\n", inonum);
+
+ printf ("mode: %s\n", mode_rep (ip->di_model));
+ printf ("nlink: %d\n", ip->di_nlink);
+ printf ("size: %qd\n", ip->di_size);
+ printf ("atime: %s\n", timespec_rep (&ip->di_atime));
+ printf ("mtime: %s\n", timespec_rep (&ip->di_mtime));
+ printf ("ctime: %s\n", timespec_rep (&ip->di_ctime));
+ printf ("flags: %#lx\n", ip->di_flags);
+ printf ("blocks: %ld\n", ip->di_blocks);
+ printf ("gener: %ld\n", ip->di_gen);
+ printf ("uid: %s\n", uid_rep (ip->di_uid));
+ printf ("gid: %s\n", gid_rep (ip->di_gid));
+ printf ("dblks: ");
+ for (i = 0; i < NDADDR; i++)
+ printf ("%s%ld", (i == 0 ? "" : ", "), ip->di_db[i]);
+ putchar ('\n');
+ printf ("iblks: ");
+ for (i = 0; i < NIADDR; i++)
+ printf ("%s%ld", (i == 0 ? "" : ", "), ip->di_ib[i]);
+ putchar ('\n');
+ printf ("trans: %ld\n", ip->di_trans);
+
+ if (argv[1])
+ putchar ('\n');
+ }
+ (void)close(fd);
+ exit(0);
+}
diff --git a/ufs/Makefile b/ufs/Makefile
new file mode 100644
index 00000000..02cf38ba
--- /dev/null
+++ b/ufs/Makefile
@@ -0,0 +1,32 @@
+# Makefile for ufs
+#
+# Copyright (C) 1994,95,96,99,2000,02 Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2, or (at
+# your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+dir := ufs
+makemode := server
+
+target = ufs
+SRCS = alloc.c consts.c dir.c hyper.c inode.c main.c pager.c \
+ sizes.c subr.c tables.c bmap.c pokeloc.c
+LCLHDRS = ufs.h fs.h dinode.h dir.h
+
+OBJS = $(SRCS:.c=.o)
+HURDLIBS = diskfs iohelp fshelp store pager threads ports ihash shouldbeinlibc
+
+include ../Makeconf
+
+ufs.static: $(boot-store-types:%=../libstore/libstore_%.a)
diff --git a/ufs/alloc.c b/ufs/alloc.c
new file mode 100644
index 00000000..48ee60cc
--- /dev/null
+++ b/ufs/alloc.c
@@ -0,0 +1,1703 @@
+/* Disk allocation routines
+ Copyright (C) 1993,94,95,96,98,2002 Free Software Foundation, Inc.
+
+This file is part of the GNU Hurd.
+
+The GNU Hurd is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+The GNU Hurd is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Modified from UCB by Michael I. Bushnell. */
+/*
+ * 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. 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.
+ *
+ * @(#)ffs_alloc.c 8.8 (Berkeley) 2/21/94
+ */
+
+#include "ufs.h"
+#include <stdio.h>
+#include <string.h>
+
+
+/* These don't work *at all* here; don't even try setting them. */
+#undef DIAGNOSTIC
+#undef QUOTA
+
+extern u_long nextgennumber;
+
+spin_lock_t alloclock = SPIN_LOCK_INITIALIZER;
+
+/* Forward declarations */
+static u_long ffs_hashalloc (struct node *, int, long, int,
+ u_long (*)(struct node *, int, daddr_t, int));
+static u_long ffs_alloccg (struct node *, int, daddr_t, int);
+static daddr_t ffs_fragextend (struct node *, int, long, int, int);
+static ino_t ffs_dirpref (struct fs *);
+static u_long ffs_nodealloccg (struct node *, int, daddr_t, int);
+static daddr_t ffs_alloccgblk (struct fs *, struct cg *, daddr_t);
+static daddr_t ffs_mapsearch (struct fs *, struct cg *, daddr_t, int);
+static void ffs_clusteracct (struct fs *, struct cg *, daddr_t, int);
+
+/* Sync all allocation information and nod eNP if diskfs_synchronous. */
+inline void
+alloc_sync (struct node *np)
+{
+ if (diskfs_synchronous)
+ {
+ if (np)
+ diskfs_node_update (np, 1);
+ copy_sblock ();
+ diskfs_set_hypermetadata (1, 0);
+ sync_disk (1);
+ }
+}
+
+/* Byteswap everything in CGP. */
+void
+swab_cg (struct cg *cg)
+{
+ int i, j;
+
+ if (swab_long (cg->cg_magic) == CG_MAGIC
+ || cg->cg_magic == CG_MAGIC)
+ {
+ cg->cg_magic = swab_long (cg->cg_magic);
+ cg->cg_time = swab_long (cg->cg_time);
+ cg->cg_cgx = swab_long (cg->cg_cgx);
+ cg->cg_ncyl = swab_short (cg->cg_ncyl);
+ cg->cg_niblk = swab_short (cg->cg_niblk);
+ cg->cg_cs.cs_ndir = swab_long (cg->cg_cs.cs_ndir);
+ cg->cg_cs.cs_nbfree = swab_long (cg->cg_cs.cs_nbfree);
+ cg->cg_cs.cs_nifree = swab_long (cg->cg_cs.cs_nifree);
+ cg->cg_cs.cs_nffree = swab_long (cg->cg_cs.cs_nffree);
+ cg->cg_rotor = swab_long (cg->cg_rotor);
+ cg->cg_irotor = swab_long (cg->cg_irotor);
+ for (i = 0; i < MAXFRAG; i++)
+ cg->cg_frsum[i] = swab_long (cg->cg_frsum[i]);
+ cg->cg_btotoff = swab_long (cg->cg_btotoff);
+ cg->cg_boff = swab_long (cg->cg_boff);
+ cg->cg_iusedoff = swab_long (cg->cg_iusedoff);
+ cg->cg_freeoff = swab_long (cg->cg_freeoff);
+ cg->cg_nextfreeoff = swab_long (cg->cg_nextfreeoff);
+ cg->cg_clustersumoff = swab_long (cg->cg_clustersumoff);
+ cg->cg_clusteroff = swab_long (cg->cg_clusteroff);
+ cg->cg_nclusterblks = swab_long (cg->cg_nclusterblks);
+
+ /* blktot map */
+ for (i = 0; i < cg->cg_ncyl; i++)
+ cg_blktot(cg)[i] = swab_long (cg_blktot(cg)[i]);
+
+ /* blks map */
+ for (i = 0; i < cg->cg_ncyl; i++)
+ for (j = 0; j < sblock->fs_nrpos; j++)
+ cg_blks(sblock, cg, i)[j] = swab_short (cg_blks (sblock, cg, i)[j]);
+
+ for (i = 0; i < sblock->fs_contigsumsize; i++)
+ cg_clustersum(cg)[i] = swab_long (cg_clustersum(cg)[i]);
+
+ /* inosused, blksfree, and cg_clustersfree are char arrays */
+ }
+ else
+ {
+ /* Old format cylinder group... */
+ struct ocg *ocg = (struct ocg *) cg;
+
+ if (swab_long (ocg->cg_magic) != CG_MAGIC
+ && ocg->cg_magic != CG_MAGIC)
+ return;
+
+ ocg->cg_time = swab_long (ocg->cg_time);
+ ocg->cg_cgx = swab_long (ocg->cg_cgx);
+ ocg->cg_ncyl = swab_short (ocg->cg_ncyl);
+ ocg->cg_niblk = swab_short (ocg->cg_niblk);
+ ocg->cg_ndblk = swab_long (ocg->cg_ndblk);
+ ocg->cg_cs.cs_ndir = swab_long (ocg->cg_cs.cs_ndir);
+ ocg->cg_cs.cs_nbfree = swab_long (ocg->cg_cs.cs_nbfree);
+ ocg->cg_cs.cs_nifree = swab_long (ocg->cg_cs.cs_nifree);
+ ocg->cg_cs.cs_nffree = swab_long (ocg->cg_cs.cs_nffree);
+ ocg->cg_rotor = swab_long (ocg->cg_rotor);
+ ocg->cg_frotor = swab_long (ocg->cg_frotor);
+ ocg->cg_irotor = swab_long (ocg->cg_irotor);
+ for (i = 0; i < 8; i++)
+ ocg->cg_frsum[i] = swab_long (ocg->cg_frsum[i]);
+ for (i = 0; i < 32; i++)
+ ocg->cg_btot[i] = swab_long (ocg->cg_btot[i]);
+ for (i = 0; i < 32; i++)
+ for (j = 0; j < 8; j++)
+ ocg->cg_b[i][j] = swab_short (ocg->cg_b[i][j]);
+ ocg->cg_magic = swab_long (ocg->cg_magic);
+ }
+}
+
+
+/* Read cylinder group indexed CG. Set *CGPP to point at it.
+ Return 1 if caller should call release_cgp when we're done with it;
+ otherwise zero. */
+int
+read_cg (int cg, struct cg **cgpp)
+{
+ struct cg *diskcg = cg_locate (cg);
+
+ if (swab_disk)
+ {
+ *cgpp = malloc (sblock->fs_cgsize);
+ bcopy (diskcg, *cgpp, sblock->fs_cgsize);
+ swab_cg (*cgpp);
+ return 1;
+ }
+ else
+ {
+ *cgpp = diskcg;
+ return 0;
+ }
+}
+
+/* Caller of read_cg is done with cg; write it back to disk (swapping it
+ along the way) and free the memory allocated in read_cg. */
+void
+release_cg (struct cg *cgp)
+{
+ int cgx = cgp->cg_cgx;
+ swab_cg (cgp);
+ bcopy (cgp, cg_locate (cgx), sblock->fs_cgsize);
+ free (cgp);
+}
+
+
+/*
+ * 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 heirarchy 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.
+ */
+error_t
+ffs_alloc(register struct node *np,
+ daddr_t lbn,
+ daddr_t bpref,
+ int size,
+ daddr_t *bnp,
+ struct protid *cred)
+{
+ register struct fs *fs;
+ daddr_t bno;
+ int cg;
+
+ *bnp = 0;
+ fs = sblock;
+#ifdef DIAGNOSTIC
+ if ((u_int)size > fs->fs_bsize || fragoff(fs, size) != 0) {
+ printf("dev = 0x%x, bsize = %d, size = %d, fs = %s\n",
+ ip->i_dev, fs->fs_bsize, size, fs->fs_fsmnt);
+ panic("ffs_alloc: bad size");
+ }
+ assert (cred);
+#endif /* DIAGNOSTIC */
+ spin_lock (&alloclock);
+ if (size == fs->fs_bsize && fs->fs_cstotal.cs_nbfree == 0)
+ goto nospace;
+ if (cred && !idvec_contains (cred->user->uids, 0)
+ && freespace(fs, fs->fs_minfree) <= 0)
+ goto nospace;
+#ifdef QUOTA
+ if (error = chkdq(ip, (long)btodb(size), cred, 0))
+ return (error);
+#endif
+ if (bpref >= fs->fs_size)
+ bpref = 0;
+ if (bpref == 0)
+ cg = ino_to_cg(fs, np->dn->number);
+ else
+ cg = dtog(fs, bpref);
+ bno = (daddr_t)ffs_hashalloc(np, cg, (long)bpref, size,
+ (u_long (*)())ffs_alloccg);
+ if (bno > 0) {
+ spin_unlock (&alloclock);
+ np->dn_stat.st_blocks += btodb(size);
+ np->dn_set_ctime = 1;
+ np->dn_set_mtime = 1;
+ *bnp = bno;
+ alloc_sync (np);
+ return (0);
+ }
+#ifdef QUOTA
+ /*
+ * Restore user's disk quota because allocation failed.
+ */
+ (void) chkdq(ip, (long)-btodb(size), cred, FORCE);
+#endif
+nospace:
+ spin_unlock (&alloclock);
+ printf ("file system full");
+/* ffs_fserr(fs, cred->cr_uid, "file system full"); */
+/* uprintf("\n%s: write failed, file system is full\n", fs->fs_fsmnt); */
+ return (ENOSPC);
+}
+
+/*
+ * Reallocate a fragment to a bigger size
+ *
+ * The number and size of the old block is given, and a preference
+ * and new size is also specified. The allocator attempts to extend
+ * the original block. Failing that, the regular block allocator is
+ * invoked to get an appropriate block.
+ */
+error_t
+ffs_realloccg(register struct node *np,
+ daddr_t lbprev,
+ volatile daddr_t bpref,
+ int osize,
+ int nsize,
+ daddr_t *pbn,
+ struct protid *cred)
+{
+ register struct fs *fs;
+ int cg, error;
+ volatile int request;
+ daddr_t bprev, bno;
+
+ *pbn = 0;
+ fs = sblock;
+#ifdef DIAGNOSTIC
+ if ((u_int)osize > fs->fs_bsize || fragoff(fs, osize) != 0 ||
+ (u_int)nsize > fs->fs_bsize || fragoff(fs, nsize) != 0) {
+ printf(
+ "dev = 0x%x, bsize = %d, osize = %d, nsize = %d, fs = %s\n",
+ ip->i_dev, fs->fs_bsize, osize, nsize, fs->fs_fsmnt);
+ panic("ffs_realloccg: bad size");
+ }
+ if (cred == NOCRED)
+ panic("ffs_realloccg: missing credential\n");
+#endif /* DIAGNOSTIC */
+
+ spin_lock (&alloclock);
+
+ if (!idvec_contains (cred->user->uids, 0)
+ && freespace(fs, fs->fs_minfree) <= 0)
+ goto nospace;
+ error = diskfs_catch_exception ();
+ if (error)
+ return error;
+ bprev = read_disk_entry ((dino (np->dn->number))->di_db[lbprev]);
+ diskfs_end_catch_exception ();
+ assert ("old block not allocated" && bprev);
+
+#if 0 /* Not needed in GNU Hurd ufs */
+ /*
+ * Allocate the extra space in the buffer.
+ */
+ if (error = bread(ITOV(ip), lbprev, osize, NOCRED, &bp)) {
+ brelse(bp);
+ return (error);
+ }
+#ifdef QUOTA
+ if (error = chkdq(ip, (long)btodb(nsize - osize), cred, 0)) {
+ brelse(bp);
+ return (error);
+ }
+#endif
+#endif /* 0 */
+
+ /*
+ * Check for extension in the existing location.
+ */
+ cg = dtog(fs, bprev);
+ bno = ffs_fragextend(np, cg, (long)bprev, osize, nsize);
+ if (bno) {
+ assert (bno == bprev);
+ spin_unlock (&alloclock);
+ np->dn_stat.st_blocks += btodb(nsize - osize);
+ np->dn_set_ctime = 1;
+ np->dn_set_mtime = 1;
+ *pbn = bno;
+#if 0 /* Not done this way in GNU Hurd ufs. */
+ allocbuf(bp, nsize);
+ bp->b_flags |= B_DONE;
+ bzero((char *)bp->b_data + osize, (u_int)nsize - osize);
+ *bpp = bp;
+#endif
+ alloc_sync (np);
+ return (0);
+ }
+ /*
+ * Allocate a new disk location.
+ */
+ if (bpref >= fs->fs_size)
+ bpref = 0;
+ switch ((int)fs->fs_optim) {
+ case FS_OPTSPACE:
+ /*
+ * Allocate an exact sized fragment. Although this makes
+ * best use of space, we will waste time relocating it if
+ * the file continues to grow. If the fragmentation is
+ * less than half of the minimum free reserve, we choose
+ * to begin optimizing for time.
+ */
+ request = nsize;
+ if (fs->fs_minfree < 5 ||
+ fs->fs_cstotal.cs_nffree >
+ fs->fs_dsize * fs->fs_minfree / (2 * 100))
+ break;
+ printf ("%s: optimization changed from SPACE to TIME\n",
+ fs->fs_fsmnt);
+ fs->fs_optim = FS_OPTTIME;
+ break;
+ case FS_OPTTIME:
+ /*
+ * At this point we have discovered a file that is trying to
+ * grow a small fragment to a larger fragment. To save time,
+ * we allocate a full sized block, then free the unused portion.
+ * If the file continues to grow, the `ffs_fragextend' call
+ * above will be able to grow it in place without further
+ * copying. If aberrant programs cause disk fragmentation to
+ * grow within 2% of the free reserve, we choose to begin
+ * optimizing for space.
+ */
+ request = fs->fs_bsize;
+ if (fs->fs_cstotal.cs_nffree <
+ fs->fs_dsize * (fs->fs_minfree - 2) / 100)
+ break;
+ printf ("%s: optimization changed from TIME to SPACE\n",
+ fs->fs_fsmnt);
+ fs->fs_optim = FS_OPTSPACE;
+ break;
+ default:
+ assert (0);
+ /* NOTREACHED */
+ }
+ bno = (daddr_t)ffs_hashalloc(np, cg, (long)bpref, request,
+ (u_long (*)())ffs_alloccg);
+ if (bno > 0) {
+#if 0 /* Not necessary in GNU Hurd ufs */
+ bp->b_blkno = fsbtodb(fs, bno);
+ (void) vnode_pager_uncache(ITOV(ip));
+#endif
+/* Commented out here for Hurd; we don't want to free this until we've
+ saved the old contents. Callers are responsible for freeing the
+ block when they are done with it. */
+/* ffs_blkfree(np, bprev, (long)osize); */
+ if (nsize < request)
+ ffs_blkfree(np, bno + numfrags(fs, nsize),
+ (long)(request - nsize));
+ spin_unlock (&alloclock);
+ np->dn_stat.st_blocks += btodb(nsize - osize);
+ np->dn_set_mtime = 1;
+ np->dn_set_ctime = 1;
+ *pbn = bno;
+#if 0 /* Not done this way in GNU Hurd ufs */
+ allocbuf(bp, nsize);
+ bp->b_flags |= B_DONE;
+ bzero((char *)bp->b_data + osize, (u_int)nsize - osize);
+ *bpp = bp;
+#endif /* 0 */
+ alloc_sync (np);
+ return (0);
+ }
+#ifdef QUOTA
+ /*
+ * Restore user's disk quota because allocation failed.
+ */
+ (void) chkdq(ip, (long)-btodb(nsize - osize), cred, FORCE);
+#endif
+#if 0 /* Not necesarry in GNU Hurd ufs */
+ brelse(bp);
+#endif
+nospace:
+ /*
+ * no space available
+ */
+ spin_unlock (&alloclock);
+ printf ("file system full");
+/* ffs_fserr(fs, cred->cr_uid, "file system full"); */
+/* uprintf("\n%s: write failed, file system is full\n", fs->fs_fsmnt); */
+ return (ENOSPC);
+}
+
+#if 0 /* Not used (yet?) in GNU Hurd ufs */
+/*
+ * Reallocate a sequence of blocks into a contiguous sequence of blocks.
+ *
+ * The vnode and an array of buffer pointers for a range of sequential
+ * logical blocks to be made contiguous is given. The allocator attempts
+ * to find a range of sequential blocks starting as close as possible to
+ * an fs_rotdelay offset from the end of the allocation for the logical
+ * block immediately preceeding the current range. If successful, the
+ * physical block numbers in the buffer pointers and in the inode are
+ * changed to reflect the new allocation. If unsuccessful, the allocation
+ * is left unchanged. The success in doing the reallocation is returned.
+ * Note that the error return is not reflected back to the user. Rather
+ * the previous block allocation will be used.
+ */
+#include <sys/sysctl.h>
+int doasyncfree = 1;
+struct ctldebug debug14 = { "doasyncfree", &doasyncfree };
+int
+ffs_reallocblks(ap)
+ struct vop_reallocblks_args /* {
+ struct vnode *a_vp;
+ struct cluster_save *a_buflist;
+ } */ *ap;
+{
+ struct fs *fs;
+ struct inode *ip;
+ struct vnode *vp;
+ struct buf *sbp, *ebp;
+ daddr_t *bap, *sbap, *ebap;
+ struct cluster_save *buflist;
+ daddr_t start_lbn, end_lbn, soff, eoff, newblk, blkno;
+ struct indir start_ap[NIADDR + 1], end_ap[NIADDR + 1], *idp;
+ int i, len, start_lvl, end_lvl, pref, ssize;
+
+ vp = ap->a_vp;
+ ip = VTOI(vp);
+ fs = ip->i_fs;
+ if (fs->fs_contigsumsize <= 0)
+ return (ENOSPC);
+ buflist = ap->a_buflist;
+ len = buflist->bs_nchildren;
+ start_lbn = buflist->bs_children[0]->b_lblkno;
+ end_lbn = start_lbn + len - 1;
+#ifdef DIAGNOSTIC
+ for (i = 1; i < len; i++)
+ if (buflist->bs_children[i]->b_lblkno != start_lbn + i)
+ panic("ffs_reallocblks: non-cluster");
+#endif
+ /*
+ * If the latest allocation is in a new cylinder group, assume that
+ * the filesystem has decided to move and do not force it back to
+ * the previous cylinder group.
+ */
+ if (dtog(fs, dbtofsb(fs, buflist->bs_children[0]->b_blkno)) !=
+ dtog(fs, dbtofsb(fs, buflist->bs_children[len - 1]->b_blkno)))
+ return (ENOSPC);
+ if (ufs_getlbns(vp, start_lbn, start_ap, &start_lvl) ||
+ ufs_getlbns(vp, end_lbn, end_ap, &end_lvl))
+ return (ENOSPC);
+ /*
+ * Get the starting offset and block map for the first block.
+ */
+ if (start_lvl == 0) {
+ sbap = &ip->i_db[0];
+ soff = start_lbn;
+ } else {
+ idp = &start_ap[start_lvl - 1];
+ if (bread(vp, idp->in_lbn, (int)fs->fs_bsize, NOCRED, &sbp)) {
+ brelse(sbp);
+ return (ENOSPC);
+ }
+ sbap = (daddr_t *)sbp->b_data;
+ soff = idp->in_off;
+ }
+ /*
+ * Find the preferred location for the cluster.
+ */
+ pref = ffs_blkpref(ip, start_lbn, soff, sbap);
+ /*
+ * If the block range spans two block maps, get the second map.
+ */
+ if (end_lvl == 0 || (idp = &end_ap[end_lvl - 1])->in_off + 1 >= len) {
+ ssize = len;
+ } else {
+#ifdef DIAGNOSTIC
+ if (start_ap[start_lvl-1].in_lbn == idp->in_lbn)
+ panic("ffs_reallocblk: start == end");
+#endif
+ ssize = len - (idp->in_off + 1);
+ if (bread(vp, idp->in_lbn, (int)fs->fs_bsize, NOCRED, &ebp))
+ goto fail;
+ ebap = (daddr_t *)ebp->b_data;
+ }
+ /*
+ * Search the block map looking for an allocation of the desired size.
+ */
+ if ((newblk = (daddr_t)ffs_hashalloc(ip, dtog(fs, pref), (long)pref,
+ len, (u_long (*)())ffs_clusteralloc)) == 0)
+ goto fail;
+ /*
+ * We have found a new contiguous block.
+ *
+ * First we have to replace the old block pointers with the new
+ * block pointers in the inode and indirect blocks associated
+ * with the file.
+ */
+ blkno = newblk;
+ for (bap = &sbap[soff], i = 0; i < len; i++, blkno += fs->fs_frag) {
+ if (i == ssize)
+ bap = ebap;
+#ifdef DIAGNOSTIC
+ if (buflist->bs_children[i]->b_blkno != fsbtodb(fs, *bap))
+ panic("ffs_reallocblks: alloc mismatch");
+#endif
+ *bap++ = blkno;
+ }
+ /*
+ * Next we must write out the modified inode and indirect blocks.
+ * For strict correctness, the writes should be synchronous since
+ * the old block values may have been written to disk. In practise
+ * they are almost never written, but if we are concerned about
+ * strict correctness, the `doasyncfree' flag should be set to zero.
+ *
+ * The test on `doasyncfree' should be changed to test a flag
+ * that shows whether the associated buffers and inodes have
+ * been written. The flag should be set when the cluster is
+ * started and cleared whenever the buffer or inode is flushed.
+ * We can then check below to see if it is set, and do the
+ * synchronous write only when it has been cleared.
+ */
+ if (sbap != &ip->i_db[0]) {
+ if (doasyncfree)
+ bdwrite(sbp);
+ else
+ bwrite(sbp);
+ } else {
+ ip->i_flag |= IN_CHANGE | IN_UPDATE;
+ if (!doasyncfree)
+ VOP_UPDATE(vp, &time, &time, MNT_WAIT);
+ }
+ if (ssize < len)
+ if (doasyncfree)
+ bdwrite(ebp);
+ else
+ bwrite(ebp);
+ /*
+ * Last, free the old blocks and assign the new blocks to the buffers.
+ */
+ for (blkno = newblk, i = 0; i < len; i++, blkno += fs->fs_frag) {
+ ffs_blkfree(ip, dbtofsb(fs, buflist->bs_children[i]->b_blkno),
+ fs->fs_bsize);
+ buflist->bs_children[i]->b_blkno = fsbtodb(fs, blkno);
+ }
+ return (0);
+
+fail:
+ if (ssize < len)
+ brelse(ebp);
+ if (sbap != &ip->i_db[0])
+ brelse(sbp);
+ return (ENOSPC);
+}
+#endif /* 0 */
+
+/*
+ * Allocate an inode in the file system.
+ *
+ * If allocating a directory, use ffs_dirpref to select the inode.
+ * If allocating in a directory, the following hierarchy is followed:
+ * 1) allocate the preferred inode.
+ * 2) allocate an inode in the same cylinder group.
+ * 3) quadradically rehash into other cylinder groups, until an
+ * available inode is located.
+ * If no inode preference is given the following heirarchy is used
+ * to allocate an inode:
+ * 1) allocate an inode in cylinder group 0.
+ * 2) quadradically rehash into other cylinder groups, until an
+ * available inode is located.
+ */
+/* This is now the diskfs_alloc_node callback from the diskfs library
+ (described in <hurd/diskfs.h>). It used to be ffs_valloc in BSD. */
+error_t
+diskfs_alloc_node (struct node *dir,
+ mode_t mode,
+ struct node **npp)
+{
+ register struct fs *fs;
+ struct node *np;
+ ino_t ino, ipref;
+ int cg, error;
+ int sex;
+
+ fs = sblock;
+
+
+ spin_lock (&alloclock);
+
+ if (fs->fs_cstotal.cs_nifree == 0)
+ {
+ spin_unlock (&alloclock);
+ goto noinodes;
+ }
+
+ if (S_ISDIR (mode))
+ ipref = ffs_dirpref(fs);
+ else
+ ipref = dir->dn->number;
+
+ if (ipref >= fs->fs_ncg * fs->fs_ipg)
+ ipref = 0;
+ cg = ino_to_cg(fs, ipref);
+ ino = (ino_t)ffs_hashalloc(dir, cg, (long)ipref,
+ mode, ffs_nodealloccg);
+ spin_unlock (&alloclock);
+ if (ino == 0)
+ goto noinodes;
+ error = diskfs_cached_lookup (ino, &np);
+ assert ("duplicate allocation" && !np->dn_stat.st_mode);
+ assert (! (np->dn_stat.st_mode & S_IPTRANS));
+ if (np->dn_stat.st_blocks) {
+ printf("free inode %Ld had %Ld blocks\n",
+ ino, np->dn_stat.st_blocks);
+ np->dn_stat.st_blocks = 0;
+ np->dn_set_ctime = 1;
+ }
+ np->dn_stat.st_flags = 0;
+ /*
+ * Set up a new generation number for this inode.
+ */
+ spin_lock (&gennumberlock);
+ sex = diskfs_mtime->seconds;
+ if (++nextgennumber < (u_long)sex)
+ nextgennumber = sex;
+ np->dn_stat.st_gen = nextgennumber;
+ spin_unlock (&gennumberlock);
+
+ *npp = np;
+ alloc_sync (np);
+ return (0);
+noinodes:
+ printf ("out of inodes");
+/* ffs_fserr(fs, ap->a_cred->cr_uid, "out of inodes"); */
+/* uprintf("\n%s: create/symlink failed, no inodes free\n", fs->fs_fsmnt);*/
+ return (ENOSPC);
+}
+
+/*
+ * Find a cylinder to place a directory.
+ *
+ * The policy implemented by this algorithm is to select from
+ * among those cylinder groups with above the average number of
+ * free inodes, the one with the smallest number of directories.
+ */
+static ino_t
+ffs_dirpref(register struct fs *fs)
+{
+ int cg, minndir, mincg, avgifree;
+
+ avgifree = fs->fs_cstotal.cs_nifree / fs->fs_ncg;
+ minndir = fs->fs_ipg;
+ mincg = 0;
+ for (cg = 0; cg < fs->fs_ncg; cg++)
+ if (csum[cg].cs_ndir < minndir &&
+ csum[cg].cs_nifree >= avgifree) {
+ mincg = cg;
+ minndir = csum[cg].cs_ndir;
+ }
+ return ((ino_t)(fs->fs_ipg * mincg));
+}
+
+/*
+ * 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.
+ */
+daddr_t
+ffs_blkpref(struct node *np,
+ daddr_t lbn,
+ int indx,
+ daddr_t *bap)
+{
+ register struct fs *fs;
+ register int cg;
+ int avgbfree, startcg;
+ daddr_t nextblk;
+
+ fs = sblock;
+ spin_lock (&alloclock);
+ if (indx % fs->fs_maxbpg == 0 || bap[indx - 1] == 0) {
+ if (lbn < NDADDR) {
+ cg = ino_to_cg(fs, np->dn->number);
+ spin_unlock (&alloclock);
+ 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, np->dn->number)
+ + lbn / fs->fs_maxbpg);
+ else
+ startcg = dtog(fs,
+ read_disk_entry (bap[indx - 1])) + 1;
+ startcg %= fs->fs_ncg;
+ avgbfree = fs->fs_cstotal.cs_nbfree / fs->fs_ncg;
+ for (cg = startcg; cg < fs->fs_ncg; cg++)
+ if (csum[cg].cs_nbfree >= avgbfree) {
+ fs->fs_cgrotor = cg;
+ spin_unlock (&alloclock);
+ return (fs->fs_fpg * cg + fs->fs_frag);
+ }
+ for (cg = 0; cg <= startcg; cg++)
+ if (csum[cg].cs_nbfree >= avgbfree) {
+ fs->fs_cgrotor = cg;
+ spin_unlock (&alloclock);
+ return (fs->fs_fpg * cg + fs->fs_frag);
+ }
+ spin_unlock (&alloclock);
+ return 0;
+ }
+ spin_unlock (&alloclock);
+ /*
+ * One or more previous blocks have been laid out. If less
+ * than fs_maxcontig previous blocks are contiguous, the
+ * next block is requested contiguously, otherwise it is
+ * requested rotationally delayed by fs_rotdelay milliseconds.
+ */
+ nextblk = read_disk_entry (bap[indx - 1]) + fs->fs_frag;
+ if (indx < fs->fs_maxcontig
+ || (read_disk_entry (bap[indx - fs->fs_maxcontig]) +
+ blkstofrags(fs, fs->fs_maxcontig) != nextblk))
+ {
+ return (nextblk);
+ }
+ if (fs->fs_rotdelay != 0)
+ /*
+ * Here we convert ms of delay to frags as:
+ * (frags) = (ms) * (rev/sec) * (sect/rev) /
+ * ((sect/frag) * (ms/sec))
+ * then round up to the next block.
+ */
+ nextblk += roundup(fs->fs_rotdelay * fs->fs_rps * fs->fs_nsect /
+ (NSPF(fs) * 1000), fs->fs_frag);
+ return (nextblk);
+}
+
+/*
+ * 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.
+ */
+/*VARARGS5*/
+static u_long
+ffs_hashalloc(struct node *np,
+ int cg,
+ long pref,
+ int size, /* size for data blocks, mode for inodes */
+ u_long (*allocator)())
+{
+ register struct fs *fs;
+ long result;
+ int i, icg = cg;
+
+ fs = sblock;
+ /*
+ * 1: preferred cylinder group
+ */
+ result = (*allocator)(np, 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)(np, 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)(np, cg, 0, size);
+ if (result)
+ return (result);
+ cg++;
+ if (cg == fs->fs_ncg)
+ cg = 0;
+ }
+ return 0;
+}
+
+/*
+ * Determine whether a fragment can be extended.
+ *
+ * Check to see if the necessary fragments are available, and
+ * if they are, allocate them.
+ */
+static daddr_t
+ffs_fragextend(struct node *np,
+ int cg,
+ long bprev,
+ int osize,
+ int nsize)
+{
+ register struct fs *fs;
+ struct cg *cgp;
+ long bno;
+ int frags, bbase;
+ int i;
+ int releasecg;
+
+ fs = sblock;
+ if (csum[cg].cs_nffree < numfrags(fs, nsize - osize))
+ return 0;
+ frags = numfrags(fs, nsize);
+ bbase = fragnum(fs, bprev);
+ if (bbase > fragnum(fs, (bprev + frags - 1))) {
+ /* cannot extend across a block boundary */
+ return 0;
+ }
+#if 0 /* Wrong for GNU Hurd ufs */
+ error = bread(ip->i_devvp, fsbtodb(fs, cgtod(fs, cg)),
+ (int)fs->fs_cgsize, NOCRED, &bp);
+ if (error) {
+ brelse(bp);
+ return (NULL);
+ }
+ cgp = (struct cg *)bp->b_data;
+#else
+ releasecg = read_cg (cg, &cgp);
+#endif
+ if (!cg_chkmagic(cgp)) {
+/* brelse(bp); */
+ if (releasecg)
+ release_cg (cgp);
+ return 0;
+ }
+ cgp->cg_time = diskfs_mtime->seconds;
+ bno = dtogd(fs, bprev);
+ for (i = numfrags(fs, osize); i < frags; i++)
+ if (isclr(cg_blksfree(cgp), bno + i)) {
+/* brelse(bp); */
+ if (releasecg)
+ release_cg (cgp);
+ return 0;
+ }
+ /*
+ * the current fragment can be extended
+ * deduct the count on fragment being extended into
+ * increase the count on the remaining fragment (if any)
+ * allocate the extended piece
+ */
+ for (i = frags; i < fs->fs_frag - bbase; i++)
+ if (isclr(cg_blksfree(cgp), bno + i))
+ break;
+ cgp->cg_frsum[i - numfrags(fs, osize)]--;
+ if (i != frags)
+ cgp->cg_frsum[i - frags]++;
+ for (i = numfrags(fs, osize); i < frags; i++) {
+ clrbit(cg_blksfree(cgp), bno + i);
+ cgp->cg_cs.cs_nffree--;
+ fs->fs_cstotal.cs_nffree--;
+ csum[cg].cs_nffree--;
+ }
+ if (releasecg)
+ release_cg (cgp);
+ record_poke (cgp, sblock->fs_cgsize);
+ csum_dirty = 1;
+ sblock_dirty = 1;
+ fs->fs_fmod = 1;
+/* bdwrite(bp); */
+ return (bprev);
+}
+
+/*
+ * 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 u_long
+ffs_alloccg(struct node *np,
+ int cg,
+ daddr_t bpref,
+ int size)
+{
+ register struct fs *fs;
+ struct cg *cgp;
+ register int i;
+ int bno, frags, allocsiz;
+ int releasecg;
+
+ fs = sblock;
+ if (csum[cg].cs_nbfree == 0 && size == fs->fs_bsize)
+ return 0;
+#if 0 /* Not this way in GNU Hurd ufs */
+ error = bread(ip->i_devvp, fsbtodb(fs, cgtod(fs, cg)),
+ (int)fs->fs_cgsize, NOCRED, &bp);
+ if (error) {
+ brelse(bp);
+ return (NULL);
+ }
+ cgp = (struct cg *)bp->b_data;
+#else
+ releasecg = read_cg (cg, &cgp);
+#endif
+ if (!cg_chkmagic(cgp) ||
+ (cgp->cg_cs.cs_nbfree == 0 && size == fs->fs_bsize)) {
+/* brelse(bp); */
+ if (releasecg)
+ release_cg (cgp);
+ return 0;
+ }
+ cgp->cg_time = diskfs_mtime->seconds;
+ if (size == fs->fs_bsize) {
+ bno = ffs_alloccgblk(fs, cgp, bpref);
+/* bdwrite(bp); */
+ if (releasecg)
+ release_cg (cgp);
+ 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); */
+ if (releasecg)
+ release_cg (cgp);
+ return 0;
+ }
+ bno = ffs_alloccgblk(fs, cgp, bpref);
+ bpref = dtogd(fs, bno);
+ for (i = frags; i < fs->fs_frag; i++)
+ setbit(cg_blksfree(cgp), bpref + i);
+ i = fs->fs_frag - frags;
+ cgp->cg_cs.cs_nffree += i;
+ fs->fs_cstotal.cs_nffree += i;
+ csum[cg].cs_nffree += i;
+ fs->fs_fmod = 1;
+ cgp->cg_frsum[i]++;
+
+ if (releasecg)
+ release_cg (cgp);
+ record_poke (cgp, sblock->fs_cgsize);
+ csum_dirty = 1;
+ sblock_dirty = 1;
+/* bdwrite(bp); */
+ return (bno);
+ }
+ bno = ffs_mapsearch(fs, cgp, bpref, allocsiz);
+ if (bno < 0) {
+/* brelse(bp); */
+ if (releasecg)
+ release_cg (cgp);
+ return 0;
+ }
+ for (i = 0; i < frags; i++)
+ clrbit(cg_blksfree(cgp), bno + i);
+ cgp->cg_cs.cs_nffree -= frags;
+ fs->fs_cstotal.cs_nffree -= frags;
+ csum[cg].cs_nffree -= frags;
+ fs->fs_fmod = 1;
+ cgp->cg_frsum[allocsiz]--;
+ if (frags != allocsiz)
+ cgp->cg_frsum[allocsiz - frags]++;
+ if (releasecg)
+ release_cg (cgp);
+ record_poke (cgp, sblock->fs_cgsize);
+ csum_dirty = 1;
+ sblock_dirty = 1;
+/* bdwrite(bp); */
+ return (cg * fs->fs_fpg + bno);
+}
+
+/*
+ * 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(register struct fs *fs,
+ register struct cg *cgp,
+ daddr_t bpref)
+{
+ daddr_t bno, blkno;
+ int cylno, pos, delta;
+ short *cylbp;
+ register int i;
+
+ if (bpref == 0 || dtog(fs, bpref) != cgp->cg_cgx) {
+ bpref = cgp->cg_rotor;
+ goto norot;
+ }
+ bpref = blknum(fs, bpref);
+ bpref = dtogd(fs, bpref);
+ /*
+ * if the requested block is available, use it
+ */
+ if (ffs_isblock(fs, cg_blksfree(cgp), fragstoblks(fs, bpref))) {
+ bno = bpref;
+ goto gotit;
+ }
+ /*
+ * check for a block available on the same cylinder
+ */
+ cylno = cbtocylno(fs, bpref);
+ if (cg_blktot(cgp)[cylno] == 0)
+ goto norot;
+ if (fs->fs_cpc == 0) {
+ /*
+ * Block layout information is not available.
+ * Leaving bpref unchanged means we take the
+ * next available free block following the one
+ * we just allocated. Hopefully this will at
+ * least hit a track cache on drives of unknown
+ * geometry (e.g. SCSI).
+ */
+ goto norot;
+ }
+ /*
+ * check the summary information to see if a block is
+ * available in the requested cylinder starting at the
+ * requested rotational position and proceeding around.
+ */
+ cylbp = cg_blks(fs, cgp, cylno);
+ pos = cbtorpos(fs, bpref);
+ for (i = pos; i < fs->fs_nrpos; i++)
+ if (cylbp[i] > 0)
+ break;
+ if (i == fs->fs_nrpos)
+ for (i = 0; i < pos; i++)
+ if (cylbp[i] > 0)
+ break;
+ if (cylbp[i] > 0) {
+ /*
+ * found a rotational position, now find the actual
+ * block. A panic if none is actually there.
+ */
+ pos = cylno % fs->fs_cpc;
+ bno = (cylno - pos) * fs->fs_spc / NSPB(fs);
+ assert (fs_postbl(fs, pos)[i] != -1);
+ for (i = fs_postbl(fs, pos)[i];; ) {
+ if (ffs_isblock(fs, cg_blksfree(cgp), bno + i)) {
+ bno = blkstofrags(fs, (bno + i));
+ goto gotit;
+ }
+ delta = fs_rotbl(fs)[i];
+ if (delta <= 0 ||
+ delta + i > fragstoblks(fs, fs->fs_fpg))
+ break;
+ i += delta;
+ }
+ printf("pos = %d, i = %d, fs = %s\n", pos, i, fs->fs_fsmnt);
+ assert (0);
+ }
+norot:
+ /*
+ * no blocks in the requested cylinder, so take 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 = bno;
+gotit:
+ blkno = fragstoblks(fs, bno);
+ ffs_clrblock(fs, cg_blksfree(cgp), (long)blkno);
+ ffs_clusteracct(fs, cgp, blkno, -1);
+ cgp->cg_cs.cs_nbfree--;
+ fs->fs_cstotal.cs_nbfree--;
+ csum[cgp->cg_cgx].cs_nbfree--;
+ cylno = cbtocylno(fs, bno);
+ cg_blks(fs, cgp, cylno)[cbtorpos(fs, bno)]--;
+ cg_blktot(cgp)[cylno]--;
+ fs->fs_fmod = 1;
+ record_poke (cgp, sblock->fs_cgsize);
+ csum_dirty = 1;
+ sblock_dirty = 1;
+ return (cgp->cg_cgx * fs->fs_fpg + bno);
+}
+
+#if 0 /* Not needed in GNU Hurd ufs (yet?) */
+/*
+ * Determine whether a cluster can be allocated.
+ *
+ * We do not currently check for optimal rotational layout if there
+ * are multiple choices in the same cylinder group. Instead we just
+ * take the first one that we find following bpref.
+ */
+static daddr_t
+ffs_clusteralloc(ip, cg, bpref, len)
+ struct inode *ip;
+ int cg;
+ daddr_t bpref;
+ int len;
+{
+ register struct fs *fs;
+ register struct cg *cgp;
+ struct buf *bp;
+ int i, run, bno, bit, map;
+ u_char *mapp;
+
+ fs = ip->i_fs;
+ if (fs->fs_cs(fs, cg).cs_nbfree < len)
+ return (NULL);
+ if (bread(ip->i_devvp, fsbtodb(fs, cgtod(fs, cg)), (int)fs->fs_cgsize,
+ NOCRED, &bp))
+ goto fail;
+ cgp = (struct cg *)bp->b_data;
+ if (!cg_chkmagic(cgp))
+ goto fail;
+ /*
+ * Check to see if a cluster of the needed size (or bigger) is
+ * available in this cylinder group.
+ */
+ for (i = len; i <= fs->fs_contigsumsize; i++)
+ if (cg_clustersum(cgp)[i] > 0)
+ break;
+ if (i > fs->fs_contigsumsize)
+ goto fail;
+ /*
+ * Search the cluster map to find a big enough cluster.
+ * We take the first one that we find, even if it is larger
+ * than we need as we prefer to get one close to the previous
+ * block allocation. We do not search before the current
+ * preference point as we do not want to allocate a block
+ * that is allocated before the previous one (as we will
+ * then have to wait for another pass of the elevator
+ * algorithm before it will be read). We prefer to fail and
+ * be recalled to try an allocation in the next cylinder group.
+ */
+ if (dtog(fs, bpref) != cg)
+ bpref = 0;
+ else
+ bpref = fragstoblks(fs, dtogd(fs, blknum(fs, bpref)));
+ mapp = &cg_clustersfree(cgp)[bpref / NBBY];
+ map = *mapp++;
+ bit = 1 << (bpref % NBBY);
+ for (run = 0, i = bpref; i < cgp->cg_nclusterblks; i++) {
+ if ((map & bit) == 0) {
+ run = 0;
+ } else {
+ run++;
+ if (run == len)
+ break;
+ }
+ if ((i & (NBBY - 1)) != (NBBY - 1)) {
+ bit <<= 1;
+ } else {
+ map = *mapp++;
+ bit = 1;
+ }
+ }
+ if (i == cgp->cg_nclusterblks)
+ goto fail;
+ /*
+ * Allocate the cluster that we have found.
+ */
+ bno = cg * fs->fs_fpg + blkstofrags(fs, i - run + 1);
+ len = blkstofrags(fs, len);
+ for (i = 0; i < len; i += fs->fs_frag)
+ if (ffs_alloccgblk(fs, cgp, bno + i) != bno + i)
+ panic("ffs_clusteralloc: lost block");
+ brelse(bp);
+ return (bno);
+
+fail:
+ brelse(bp);
+ return (0);
+}
+#endif
+
+/*
+ * Determine whether an inode can be allocated.
+ *
+ * Check to see if an inode is available, and if it is,
+ * allocate it using the following policy:
+ * 1) allocate the requested inode.
+ * 2) allocate the next available inode after the requested
+ * inode in the specified cylinder group.
+ */
+static u_long
+ffs_nodealloccg(struct node *np,
+ int cg,
+ daddr_t ipref,
+ int mode)
+{
+ register struct fs *fs;
+ struct cg *cgp;
+ int start, len, loc, map, i;
+ int releasecg;
+
+ fs = sblock;
+ if (csum[cg].cs_nifree == 0)
+ return 0;
+#if 0 /* Not this way in GNU Hurd ufs */
+ error = bread(ip->i_devvp, fsbtodb(fs, cgtod(fs, cg)),
+ (int)fs->fs_cgsize, NOCRED, &bp);
+ if (error) {
+ brelse(bp);
+ return (NULL);
+ }
+ cgp = (struct cg *)bp->b_data;
+#else
+ releasecg = read_cg (cg, &cgp);
+#endif
+ if (!cg_chkmagic(cgp) || cgp->cg_cs.cs_nifree == 0) {
+/* brelse(bp); */
+ if (releasecg)
+ release_cg (cgp);
+ return 0;
+ }
+ cgp->cg_time = diskfs_mtime->seconds;
+ if (ipref) {
+ ipref %= fs->fs_ipg;
+ if (isclr(cg_inosused(cgp), ipref))
+ goto gotit;
+ }
+ start = cgp->cg_irotor / NBBY;
+ len = howmany(fs->fs_ipg - cgp->cg_irotor, NBBY);
+ loc = skpc(0xff, len, &cg_inosused(cgp)[start]);
+ if (loc == 0) {
+ len = start + 1;
+ start = 0;
+ loc = skpc(0xff, len, &cg_inosused(cgp)[0]);
+ assert (loc != 0);
+ }
+ i = start + len - loc;
+ map = cg_inosused(cgp)[i];
+ ipref = i * NBBY;
+ for (i = 1; i < (1 << NBBY); i <<= 1, ipref++) {
+ if ((map & i) == 0) {
+ cgp->cg_irotor = ipref;
+ goto gotit;
+ }
+ }
+ assert (0);
+ /* NOTREACHED */
+gotit:
+ setbit(cg_inosused(cgp), ipref);
+ cgp->cg_cs.cs_nifree--;
+ fs->fs_cstotal.cs_nifree--;
+ csum[cg].cs_nifree--;
+ fs->fs_fmod = 1;
+ if ((mode & IFMT) == IFDIR) {
+ cgp->cg_cs.cs_ndir++;
+ fs->fs_cstotal.cs_ndir++;
+ csum[cg].cs_ndir++;
+ }
+ if (releasecg)
+ release_cg (cgp);
+ record_poke (cgp, sblock->fs_cgsize);
+ csum_dirty = 1;
+ sblock_dirty = 1;
+/* bdwrite(bp); */
+ return (cg * fs->fs_ipg + ipref);
+}
+
+/*
+ * 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(register struct node *np,
+ daddr_t bno,
+ long size)
+{
+ register struct fs *fs;
+ struct cg *cgp;
+ daddr_t blkno;
+ int i, cg, blk, frags, bbase;
+ int releasecg;
+
+ fs = sblock;
+ assert ((u_int)size <= fs->fs_bsize && !fragoff (fs, size));
+ cg = dtog(fs, bno);
+ if ((u_int)bno >= fs->fs_size) {
+ printf("bad block %ld, ino %Ld\n", bno, np->dn->number);
+/* ffs_fserr(fs, ip->i_uid, "bad block"); */
+ return;
+ }
+#if 0 /* Not this way in GNU Hurd ufs */
+ error = bread(ip->i_devvp, fsbtodb(fs, cgtod(fs, cg)),
+ (int)fs->fs_cgsize, NOCRED, &bp);
+ if (error) {
+ brelse(bp);
+ return;
+ }
+ cgp = (struct cg *)bp->b_data;
+#else
+ releasecg = read_cg (cg, &cgp);
+#endif
+ if (!cg_chkmagic(cgp)) {
+/* brelse(bp); */
+ if (releasecg)
+ release_cg (cgp);
+ return;
+ }
+ cgp->cg_time = diskfs_mtime->seconds;
+ bno = dtogd(fs, bno);
+ if (size == fs->fs_bsize) {
+ blkno = fragstoblks(fs, bno);
+ assert (!ffs_isblock(fs, cg_blksfree (cgp), blkno));
+ ffs_setblock(fs, cg_blksfree(cgp), blkno);
+ ffs_clusteracct(fs, cgp, blkno, 1);
+ cgp->cg_cs.cs_nbfree++;
+ fs->fs_cstotal.cs_nbfree++;
+ csum[cg].cs_nbfree++;
+ i = cbtocylno(fs, bno);
+ cg_blks(fs, cgp, i)[cbtorpos(fs, bno)]++;
+ cg_blktot(cgp)[i]++;
+ } else {
+ bbase = bno - fragnum(fs, bno);
+ /*
+ * decrement the counts associated with the old frags
+ */
+ blk = blkmap(fs, cg_blksfree(cgp), bbase);
+ ffs_fragacct(fs, blk, cgp->cg_frsum, -1);
+ /*
+ * deallocate the fragment
+ */
+ frags = numfrags(fs, size);
+ for (i = 0; i < frags; i++) {
+ assert (!isset (cg_blksfree(cgp), bno + i));
+ setbit(cg_blksfree(cgp), bno + i);
+ }
+ cgp->cg_cs.cs_nffree += i;
+ fs->fs_cstotal.cs_nffree += i;
+ csum[cg].cs_nffree += i;
+ /*
+ * add back in counts associated with the new frags
+ */
+ blk = blkmap(fs, cg_blksfree(cgp), bbase);
+ ffs_fragacct(fs, blk, cgp->cg_frsum, 1);
+ /*
+ * if a complete block has been reassembled, account for it
+ */
+ blkno = fragstoblks(fs, bbase);
+ if (ffs_isblock(fs, cg_blksfree(cgp), blkno)) {
+ cgp->cg_cs.cs_nffree -= fs->fs_frag;
+ fs->fs_cstotal.cs_nffree -= fs->fs_frag;
+ csum[cg].cs_nffree -= fs->fs_frag;
+ ffs_clusteracct(fs, cgp, blkno, 1);
+ cgp->cg_cs.cs_nbfree++;
+ fs->fs_cstotal.cs_nbfree++;
+ csum[cg].cs_nbfree++;
+ i = cbtocylno(fs, bbase);
+ cg_blks(fs, cgp, i)[cbtorpos(fs, bbase)]++;
+ cg_blktot(cgp)[i]++;
+ }
+ }
+ if (releasecg)
+ release_cg (cgp);
+ record_poke (cgp, sblock->fs_cgsize);
+ csum_dirty = 1;
+ sblock_dirty = 1;
+ fs->fs_fmod = 1;
+ alloc_sync (np);
+/* bdwrite(bp); */
+}
+
+/*
+ * Free an inode.
+ *
+ * The specified inode is placed back in the free map.
+ */
+/* Implement diskfs call back diskfs_free_node (described in
+ <hurd/diskfs.h>. This was called ffs_vfree in BSD. */
+void
+diskfs_free_node (struct node *np, mode_t mode)
+{
+ register struct fs *fs;
+ struct cg *cgp;
+ ino_t ino = np->dn->number;
+ int cg;
+ int releasecg;
+
+ fs = sblock;
+ assert (ino < fs->fs_ipg * fs->fs_ncg);
+ cg = ino_to_cg(fs, ino);
+#if 0 /* Not this way in GNU Hurd ufs */
+ error = bread(pip->i_devvp, fsbtodb(fs, cgtod(fs, cg)),
+ (int)fs->fs_cgsize, NOCRED, &bp);
+ if (error) {
+ brelse(bp);
+ return (0);
+ }
+ cgp = (struct cg *)bp->b_data;
+#else
+ releasecg = read_cg (cg, &cgp);
+#endif
+ if (!cg_chkmagic(cgp)) {
+/* brelse(bp); */
+ if (releasecg)
+ release_cg (cgp);
+ return;
+ }
+ cgp->cg_time = diskfs_mtime->seconds;
+ ino %= fs->fs_ipg;
+ if (isclr(cg_inosused(cgp), ino)) {
+/* printf("dev = 0x%x, ino = %Ld, fs = %s\n",
+ pip->i_dev, ino, fs->fs_fsmnt); */
+ assert (diskfs_readonly);
+ }
+ clrbit(cg_inosused(cgp), ino);
+ if (ino < cgp->cg_irotor)
+ cgp->cg_irotor = ino;
+ cgp->cg_cs.cs_nifree++;
+ fs->fs_cstotal.cs_nifree++;
+ csum[cg].cs_nifree++;
+ if ((mode & IFMT) == IFDIR) {
+ cgp->cg_cs.cs_ndir--;
+ fs->fs_cstotal.cs_ndir--;
+ csum[cg].cs_ndir--;
+ }
+ if (releasecg)
+ release_cg (cgp);
+ record_poke (cgp, sblock->fs_cgsize);
+ csum_dirty = 1;
+ sblock_dirty = 1;
+ fs->fs_fmod = 1;
+ alloc_sync (np);
+/* bdwrite(bp); */
+}
+
+/*
+ * 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 daddr_t
+ffs_mapsearch(register struct fs *fs,
+ register struct cg *cgp,
+ daddr_t bpref,
+ int allocsiz)
+{
+ daddr_t bno;
+ int start, len, loc, i;
+ int blk, field, subfield, pos;
+
+ /*
+ * find the fragment by searching through the free block
+ * map for an appropriate bit pattern
+ */
+ if (bpref)
+ start = dtogd(fs, bpref) / NBBY;
+ else
+ start = cgp->cg_frotor / NBBY;
+ len = howmany(fs->fs_fpg, NBBY) - start;
+ loc = scanc((u_int)len, (u_char *)&cg_blksfree(cgp)[start],
+ (u_char *)fragtbl[fs->fs_frag],
+ (u_char)(1 << (allocsiz - 1 + (fs->fs_frag % NBBY))));
+ if (loc == 0) {
+ len = start + 1;
+ start = 0;
+ loc = scanc((u_int)len, (u_char *)&cg_blksfree(cgp)[0],
+ (u_char *)fragtbl[fs->fs_frag],
+ (u_char)(1 << (allocsiz - 1 + (fs->fs_frag % NBBY))));
+ assert (loc);
+
+ }
+ bno = (start + len - loc) * NBBY;
+ cgp->cg_frotor = bno;
+ /*
+ * 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), 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;
+ }
+ }
+ assert (0);
+ return (-1);
+}
+
+/*
+ * Update the cluster map because of an allocation or free.
+ *
+ * Cnt == 1 means free; cnt == -1 means allocating.
+ */
+static void
+ffs_clusteracct(struct fs *fs,
+ struct cg *cgp,
+ daddr_t blkno,
+ int cnt)
+{
+ long *sump;
+ u_char *freemapp, *mapp;
+ int i, start, end, forw, back, map, bit;
+
+ if (fs->fs_contigsumsize <= 0)
+ return;
+ freemapp = cg_clustersfree(cgp);
+ sump = cg_clustersum(cgp);
+ /*
+ * 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 >= cgp->cg_nclusterblks)
+ end = cgp->cg_nclusterblks;
+ 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;
+ sump[i] += cnt;
+ if (back > 0)
+ sump[back] -= cnt;
+ if (forw > 0)
+ sump[forw] -= cnt;
+}
+
+#if 0
+/*
+ * Fserr prints the name of a file system with an error diagnostic.
+ *
+ * The form of the error message is:
+ * fs: error message
+ */
+static void
+ffs_fserr(fs, uid, cp)
+ struct fs *fs;
+ u_int uid;
+ char *cp;
+{
+
+ log(LOG_ERR, "uid %d on %s: %s\n", uid, fs->fs_fsmnt, cp);
+}
+#endif
diff --git a/ufs/bmap.c b/ufs/bmap.c
new file mode 100644
index 00000000..1a138f39
--- /dev/null
+++ b/ufs/bmap.c
@@ -0,0 +1,120 @@
+/* Interpretation of indirect block structure
+ Copyright (C) 1994, 1995, 1996 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "ufs.h"
+
+/* For logical block number LBN of file NP, look it the block address,
+ giving the "path" of indirect blocks to the file, starting
+ with the least indirect. Fill *INDIRS with information for
+ the block. */
+error_t
+fetch_indir_spec (struct node *np, volatile daddr_t lbn,
+ struct iblock_spec *indirs)
+{
+ struct dinode *di = dino (np->dn->number);
+ error_t err;
+ daddr_t *siblock;
+
+ err = diskfs_catch_exception ();
+ if (err)
+ return err;
+
+ indirs[0].offset = -2;
+ indirs[1].offset = -2;
+ indirs[2].offset = -2;
+ indirs[3].offset = -2;
+
+ if (lbn < NDADDR)
+ {
+ if (lbn >= 0)
+ {
+ indirs[0].bno = read_disk_entry (di->di_db[lbn]);
+ indirs[0].offset = -1;
+ }
+
+ diskfs_end_catch_exception ();
+ return 0;
+ }
+
+ lbn -= NDADDR;
+
+ indirs[0].offset = lbn % NINDIR (sblock);
+
+ if (lbn / NINDIR (sblock))
+ {
+ /* We will use the double indirect block */
+ int ibn;
+ daddr_t *diblock;
+
+ ibn = lbn / NINDIR (sblock) - 1;
+
+ indirs[1].offset = ibn % NINDIR (sblock);
+
+ /* We don't support triple indirect blocks, but this
+ is where we'd do it. */
+ assert (!(ibn / NINDIR (sblock)));
+
+ indirs[2].offset = -1;
+ indirs[2].bno = read_disk_entry (di->di_ib[INDIR_DOUBLE]);
+
+ if (indirs[2].bno)
+ {
+ diblock = indir_block (indirs[2].bno);
+ indirs[1].bno = read_disk_entry (diblock[indirs[1].offset]);
+ }
+ else
+ indirs[1].bno = 0;
+ }
+ else
+ {
+ indirs[1].offset = -1;
+ indirs[1].bno = read_disk_entry (di->di_ib[INDIR_SINGLE]);
+ }
+
+ if (indirs[1].bno)
+ {
+ siblock = indir_block (indirs[1].bno);
+ indirs[0].bno = read_disk_entry (siblock[indirs[0].offset]);
+ }
+ else
+ indirs[0].bno = 0;
+
+ diskfs_end_catch_exception ();
+ return 0;
+}
+
+
+/* Mark indirect block BNO as dirty on node NP's list. NP must
+ be locked. */
+void
+mark_indir_dirty (struct node *np, daddr_t bno)
+{
+ struct dirty_indir *d;
+
+ for (d = np->dn->dirty; d; d = d->next)
+ if (d->bno == bno)
+ return;
+
+ d = malloc (sizeof (struct dirty_indir));
+ d->bno = bno;
+ d->next = np->dn->dirty;
+ np->dn->dirty = d;
+}
+
diff --git a/ufs/consts.c b/ufs/consts.c
new file mode 100644
index 00000000..69221233
--- /dev/null
+++ b/ufs/consts.c
@@ -0,0 +1,33 @@
+/* Various constants wanted by the diskfs library
+ Copyright (C) 1994, 1995, 1996, 1999 Free Software Foundation
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "ufs.h"
+#include "dir.h"
+#include <version.h>
+
+int diskfs_link_max = LINK_MAX;
+int diskfs_name_max = MAXNAMLEN;
+int diskfs_maxsymlinks = 8;
+int diskfs_shortcut_symlink = 1;
+int diskfs_shortcut_chrdev = 1;
+int diskfs_shortcut_blkdev = 1;
+int diskfs_shortcut_fifo = 1;
+int diskfs_shortcut_ifsock = 1;
+char *diskfs_server_name = "ufs";
+char *diskfs_server_version = HURD_VERSION;
+char *diskfs_extra_version = "GNU Hurd";
+int diskfs_synchronous = 0;
diff --git a/ufs/dinode.h b/ufs/dinode.h
new file mode 100644
index 00000000..00be0d94
--- /dev/null
+++ b/ufs/dinode.h
@@ -0,0 +1,137 @@
+/*
+ Copyright (C) 1994 Free Software Foundation
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/*
+ * 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. 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.
+ *
+ * @(#)dinode.h 8.3 (Berkeley) 1/21/94
+ */
+
+/*
+ * 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)
+
+/*
+ * A dinode contains all the meta-data associated with a UFS file.
+ * This structure defines the on-disk format of a dinode.
+ */
+
+#define NDADDR 12 /* Direct addresses in inode. */
+#define NIADDR 3 /* Indirect addresses in inode. */
+
+/* Maximum value of di_nlink field. */
+#define LINK_MAX 32767
+
+/* Indexes into di_ib */
+#define INDIR_SINGLE 0
+#define INDIR_DOUBLE 1
+#define INDIR_TRIPLE 2 /* NOT SUPPORTED */
+
+struct dinode {
+ u_short di_model; /* 0: IFMT and permissions. */
+ short di_nlink; /* 2: File link count. */
+ union
+ {
+ u_long diu_author; /* 4: File author */
+ u_short diu_oldids[2]; /* Old format uid and gid */
+ } di_u;
+ u_quad_t di_size; /* 8: File byte count. */
+ struct timespec di_atime; /* 16: Last access time. */
+ struct timespec di_mtime; /* 24: Last modified time. */
+ struct timespec di_ctime; /* 32: Last inode change time. */
+ daddr_t di_db[NDADDR]; /* 40: Direct disk blocks. */
+ daddr_t di_ib[NIADDR]; /* 88: Indirect disk blocks. */
+ u_long di_flags; /* 100: Status flags (chflags). */
+ long di_blocks; /* 104: Blocks actually held. */
+ long di_gen; /* 108: Generation number. */
+ u_long di_uid; /* 112: File owner. */
+ u_long di_gid; /* 116: File group. */
+ u_short di_modeh; /* 120: Mode high bits */
+ u_short di_spare; /* 122: unused */
+ long di_trans; /* 124: filesystem translator */
+};
+
+#define di_author di_u.diu_author /* GNU extension */
+#define di_ouid di_u.diu_oldids[0]
+#define di_ogid di_u.diu_oldids[1]
+
+/*
+ * 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_rdev di_db[0]
+#define di_shortlink di_db
+#define MAXSYMLINKLEN ((NDADDR + NIADDR) * sizeof(daddr_t))
+
+/* File modes. */
+#define IEXEC 0000100 /* Executable. */
+#define IWRITE 0000200 /* Writeable. */
+#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. */
diff --git a/ufs/dir.c b/ufs/dir.c
new file mode 100644
index 00000000..7a8cfa55
--- /dev/null
+++ b/ufs/dir.c
@@ -0,0 +1,988 @@
+/* Directory management routines
+
+ Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2002, 2007
+ Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "ufs.h"
+#include "dir.h"
+
+#include <string.h>
+#include <stdio.h>
+#include <dirent.h>
+
+#undef d_ino
+
+enum slot_status
+{
+ /* This means we haven't yet found room for a new entry. */
+ LOOKING,
+
+ /* This means that the specified entry is free and should be used. */
+ TAKE,
+
+ /* This means that the specified entry has enough room at the end
+ to hold the new entry. */
+ SHRINK,
+
+ /* This means that there is enough space in the block, but not in
+ any one single entry, so they all have to be shifted to make
+ room. */
+ COMPRESS,
+
+ /* This means that the directory will have to be grown to hold the
+ entry. */
+ EXTEND,
+
+ /* For removal and rename, this means that this is the location
+ of the entry found. */
+ HERE_TIS,
+};
+
+struct dirstat
+{
+ /* Type of followp operation expected */
+ enum lookup_type type;
+
+ /* One of the statuses above */
+ enum slot_status stat;
+
+ /* Mapped address and length of directory */
+ vm_address_t mapbuf;
+ vm_size_t mapextent;
+
+ /* Index of this directory block. */
+ int idx;
+
+ /* For stat COMPRESS, this is the address (inside mapbuf)
+ of the first direct in the directory block to be compressed. */
+ /* For stat HERE_TIS, SHRINK, and TAKE, this is the entry referenced. */
+ struct directory_entry *entry;
+
+ /* For stat HERE_TIS, type REMOVE, this is the address of the immediately
+ previous direct in this directory block, or zero if this is the first. */
+ struct directory_entry *preventry;
+
+ /* For stat COMPRESS, this is the number of bytes needed to be copied
+ in order to undertake the compression. */
+ size_t nbytes;
+};
+
+const size_t diskfs_dirstat_size = sizeof (struct dirstat);
+
+/* Initialize DS such that diskfs_drop_dirstat will ignore it. */
+void
+diskfs_null_dirstat (struct dirstat *ds)
+{
+ ds->type = LOOKUP;
+}
+
+static error_t
+dirscanblock (vm_address_t blockoff, struct node *dp, int idx,
+ const char *name, int namelen, enum lookup_type type,
+ struct dirstat *ds, ino_t *inum);
+
+/* Implement the diskfs_lookup from the diskfs library. See
+ <hurd/diskfs.h> for the interface specification. */
+error_t
+diskfs_lookup_hard (struct node *dp, const char *name, enum lookup_type type,
+ struct node **npp, struct dirstat *ds, struct protid *cred)
+{
+ error_t err;
+ ino_t inum;
+ int namelen;
+ int spec_dotdot;
+ struct node *np = 0;
+ int retry_dotdot = 0;
+ memory_object_t memobj;
+ vm_prot_t prot =
+ (type == LOOKUP) ? VM_PROT_READ : (VM_PROT_READ | VM_PROT_WRITE);
+ vm_address_t buf = 0;
+ vm_size_t buflen = 0;
+ int blockaddr;
+ int idx, lastidx;
+ int looped;
+
+ if ((type == REMOVE) || (type == RENAME))
+ assert (npp);
+
+ if (npp)
+ *npp = 0;
+
+ spec_dotdot = type & SPEC_DOTDOT;
+ type &= ~SPEC_DOTDOT;
+
+ namelen = strlen (name);
+
+ if (namelen > MAXNAMLEN)
+ {
+ if (ds)
+ diskfs_null_dirstat (ds);
+ return ENAMETOOLONG;
+ }
+
+ try_again:
+ if (ds)
+ {
+ ds->type = LOOKUP;
+ ds->mapbuf = 0;
+ ds->mapextent = 0;
+ }
+ if (buf)
+ {
+ munmap ((caddr_t) buf, buflen);
+ buf = 0;
+ }
+ if (ds && (type == CREATE || type == RENAME))
+ ds->stat = LOOKING;
+
+ /* Map in the directory contents. */
+ memobj = diskfs_get_filemap (dp, prot);
+
+ if (memobj == MACH_PORT_NULL)
+ return errno;
+
+ buf = 0;
+ /* We allow extra space in case we have to do an EXTEND. */
+ buflen = round_page (dp->dn_stat.st_size + DIRBLKSIZ);
+ err = vm_map (mach_task_self (),
+ &buf, buflen, 0, 1, memobj, 0, 0, prot, prot, 0);
+ mach_port_deallocate (mach_task_self (), memobj);
+
+ inum = 0;
+
+ diskfs_set_node_atime (dp);
+
+ /* Start the lookup at DP->dn->dir_idx. */
+ idx = dp->dn->dir_idx;
+ if (idx * DIRBLKSIZ > dp->dn_stat.st_size)
+ idx = 0; /* just in case */
+ blockaddr = buf + idx * DIRBLKSIZ;
+ looped = (idx == 0);
+ lastidx = idx;
+ if (lastidx == 0)
+ lastidx = dp->dn_stat.st_size / DIRBLKSIZ;
+
+ while (!looped || idx < lastidx)
+ {
+ err = dirscanblock (blockaddr, dp, idx, name, namelen, type, ds, &inum);
+ if (!err)
+ {
+ dp->dn->dir_idx = idx;
+ break;
+ }
+ if (err != ENOENT)
+ {
+ munmap ((caddr_t) buf, buflen);
+ return err;
+ }
+
+ blockaddr += DIRBLKSIZ;
+ idx++;
+ if (blockaddr - buf >= dp->dn_stat.st_size && !looped)
+ {
+ /* We've gotten to the end; start back at the beginning */
+ looped = 1;
+ blockaddr = buf;
+ idx = 0;
+ }
+ }
+
+ diskfs_set_node_atime (dp);
+ if (diskfs_synchronous)
+ diskfs_node_update (dp, 1);
+
+ /* If err is set here, it's ENOENT, and we don't want to
+ think about that as an error yet. */
+ err = 0;
+
+ if (inum && npp)
+ {
+ if (namelen != 2 || name[0] != '.' || name[1] != '.')
+ {
+ if (inum == dp->dn->number)
+ {
+ np = dp;
+ diskfs_nref (np);
+ }
+ else
+ {
+ err = diskfs_cached_lookup (inum, &np);
+ if (err)
+ goto out;
+ }
+ }
+
+ /* We are looking up .. */
+ /* Check to see if this is the root of the filesystem. */
+ else if (dp->dn->number == 2)
+ {
+ err = EAGAIN;
+ goto out;
+ }
+
+ /* We can't just do diskfs_cached_lookup, because we would then deadlock.
+ So we do this. Ick. */
+ else if (retry_dotdot)
+ {
+ /* Check to see that we got the same answer as last time. */
+ if (inum != retry_dotdot)
+ {
+ /* Drop what we *thought* was .. (but isn't any more) and
+ try *again*. */
+ diskfs_nput (np);
+ mutex_unlock (&dp->lock);
+ err = diskfs_cached_lookup (inum, &np);
+ mutex_lock (&dp->lock);
+ if (err)
+ goto out;
+ retry_dotdot = inum;
+ goto try_again;
+ }
+ /* Otherwise, we got it fine and np is already set properly. */
+ }
+ else if (!spec_dotdot)
+ {
+ /* Lock them in the proper order, and then
+ repeat the directory scan to see if this is still
+ right. */
+ mutex_unlock (&dp->lock);
+ err = diskfs_cached_lookup (inum, &np);
+ mutex_lock (&dp->lock);
+ if (err)
+ goto out;
+ retry_dotdot = inum;
+ goto try_again;
+ }
+
+ /* Here below are the spec dotdot cases. */
+ else if (type == RENAME || type == REMOVE)
+ np = ifind (inum);
+
+ else if (type == LOOKUP)
+ {
+ diskfs_nput (dp);
+ err = diskfs_cached_lookup (inum, &np);
+ if (err)
+ goto out;
+ }
+ else
+ assert (0);
+ }
+
+ if ((type == CREATE || type == RENAME) && !inum && ds && ds->stat == LOOKING)
+ {
+ /* We didn't find any room, so mark ds to extend the dir */
+ ds->type = CREATE;
+ ds->stat = EXTEND;
+ ds->idx = dp->dn_stat.st_size / DIRBLKSIZ;
+ }
+
+ /* Return to the user; if we can't, release the reference
+ (and lock) we acquired above. */
+ out:
+ /* Deallocate or save the mapping. */
+ if ((err && err != ENOENT)
+ || !ds
+ || ds->type == LOOKUP)
+ {
+ munmap ((caddr_t) buf, buflen);
+ if (ds)
+ ds->type = LOOKUP; /* set to be ignored by drop_dirstat */
+ }
+ else
+ {
+ ds->mapbuf = buf;
+ ds->mapextent = buflen;
+ }
+
+ if (np)
+ {
+ assert (npp);
+ if (err)
+ {
+ if (!spec_dotdot)
+ {
+ /* Normal case */
+ if (np == dp)
+ diskfs_nrele (np);
+ else
+ diskfs_nput (np);
+ }
+ else if (type == RENAME || type == REMOVE)
+ /* We just did ifind to get np; that allocates
+ no new references, so we don't have anything to do */
+ ;
+ else if (type == LOOKUP)
+ /* We did diskfs_cached_lookup */
+ diskfs_nput (np);
+ }
+ else
+ *npp = np;
+ }
+
+ return err ? : inum ? 0 : ENOENT;
+}
+
+/* Scan block at address BLKADDR (of node DP; block index IDX), for
+ name NAME of length NAMELEN. Args TYPE, DS are as for
+ diskfs_lookup. If found, set *INUM to the inode number, else
+ return ENOENT. */
+static error_t
+dirscanblock (vm_address_t blockaddr, struct node *dp, int idx,
+ const char *name, int namelen, enum lookup_type type,
+ struct dirstat *ds, ino_t *inum)
+{
+ int nfree = 0;
+ int needed = 0;
+ vm_address_t currentoff, prevoff;
+ struct directory_entry *entry = 0;
+ int nentries = 0;
+ size_t nbytes = 0;
+ int looking = 0;
+ int countcopies = 0;
+ int consider_compress = 0;
+
+ if (ds && (ds->stat == LOOKING
+ || ds->stat == COMPRESS))
+ {
+ looking = 1;
+ countcopies = 1;
+ needed = DIRSIZ (namelen);
+ }
+
+ for (currentoff = blockaddr, prevoff = 0;
+ currentoff < blockaddr + DIRBLKSIZ;
+ prevoff = currentoff, currentoff += read_disk_entry (entry->d_reclen))
+ {
+ entry = (struct directory_entry *)currentoff;
+
+ if (!entry->d_reclen
+ || read_disk_entry (entry->d_reclen) % 4
+ || DIRECT_NAMLEN (entry) > MAXNAMLEN
+ || (currentoff + read_disk_entry (entry->d_reclen)
+ > blockaddr + DIRBLKSIZ)
+ || entry->d_name[DIRECT_NAMLEN (entry)]
+ || DIRSIZ (DIRECT_NAMLEN (entry)) > read_disk_entry (entry->d_reclen)
+ || memchr (entry->d_name, '\0', DIRECT_NAMLEN (entry)))
+ {
+ fprintf (stderr, "Bad directory entry: inode: %Ld offset: %zd\n",
+ dp->dn->number, currentoff - blockaddr + idx * DIRBLKSIZ);
+ return ENOENT;
+ }
+
+ if (looking || countcopies)
+ {
+ int thisfree;
+
+ /* Count how much free space this entry has in it. */
+ if (entry->d_ino == 0)
+ thisfree = read_disk_entry (entry->d_reclen);
+ else
+ thisfree = (read_disk_entry (entry->d_reclen)
+ - DIRSIZ (DIRECT_NAMLEN (entry)));
+
+ /* If this isn't at the front of the block, then it will
+ have to be copied if we do a compression; count the
+ number of bytes there too. */
+ if (countcopies && currentoff != blockaddr)
+ nbytes += DIRSIZ (DIRECT_NAMLEN (entry));
+
+ if (ds->stat == COMPRESS && nbytes > ds->nbytes)
+ /* The previously found compress is better than
+ this one, so don't bother counting any more. */
+ countcopies = 0;
+
+ if (thisfree >= needed)
+ {
+ ds->type = CREATE;
+ ds->stat = read_disk_entry (entry->d_ino) == 0 ? TAKE : SHRINK;
+ ds->entry = entry;
+ ds->idx = idx;
+ looking = countcopies = 0;
+ }
+ else
+ {
+ nfree += thisfree;
+ if (nfree >= needed)
+ consider_compress = 1;
+ }
+ }
+
+ if (entry->d_ino)
+ nentries++;
+
+ if (DIRECT_NAMLEN (entry) == namelen
+ && entry->d_name[0] == name[0]
+ && entry->d_ino
+ && !bcmp (entry->d_name, name, namelen))
+ break;
+ }
+
+ if (consider_compress
+ && (ds->type == LOOKING
+ || (ds->type == COMPRESS && ds->nbytes > nbytes)))
+ {
+ ds->type = CREATE;
+ ds->stat = COMPRESS;
+ ds->entry = (struct directory_entry *) blockaddr;
+ ds->idx = idx;
+ ds->nbytes = nbytes;
+ }
+
+ if (currentoff >= blockaddr + DIRBLKSIZ)
+ {
+ int i;
+ /* The name is not in this block. */
+
+ /* Because we scanned the entire block, we should write
+ down how many entries there were. */
+ if (!dp->dn->dirents)
+ {
+ dp->dn->dirents = malloc ((dp->dn_stat.st_size / DIRBLKSIZ)
+ * sizeof (int));
+ for (i = 0; i < dp->dn_stat.st_size/DIRBLKSIZ; i++)
+ dp->dn->dirents[i] = -1;
+ }
+ /* Make sure the count is correct if there is one now. */
+ assert (dp->dn->dirents[idx] == -1
+ || dp->dn->dirents[idx] == nentries);
+ dp->dn->dirents[idx] = nentries;
+
+ return ENOENT;
+ }
+
+ /* We have found the required name. */
+
+ if (ds && type == CREATE)
+ ds->type = LOOKUP; /* it's invalid now */
+ else if (ds && (type == REMOVE || type == RENAME))
+ {
+ ds->type = type;
+ ds->stat = HERE_TIS;
+ ds->entry = entry;
+ ds->idx = idx;
+ ds->preventry = (struct directory_entry *) prevoff;
+ }
+
+ *inum = read_disk_entry (entry->d_ino);
+ return 0;
+}
+
+/* Following a lookup call for CREATE, this adds a node to a directory.
+ DP is the directory to be modified; NAME is the name to be entered;
+ NP is the node being linked in; DS is the cached information returned
+ by lookup; CRED describes the user making the call. This call may
+ only be made if the directory has been held locked continuously since
+ the preceding lookup call, and only if that call returned ENOENT. */
+error_t
+diskfs_direnter_hard(struct node *dp,
+ const char *name,
+ struct node *np,
+ struct dirstat *ds,
+ struct protid *cred)
+{
+ struct directory_entry *new;
+ int namelen = strlen (name);
+ int needed = DIRSIZ (namelen);
+ int oldneeded;
+ vm_address_t fromoff, tooff;
+ int totfreed;
+ error_t err;
+ size_t oldsize = 0;
+
+ assert (ds->type == CREATE);
+
+ dp->dn_set_mtime = 1;
+
+ switch (ds->stat)
+ {
+ case TAKE:
+ /* We are supposed to consume this slot. */
+ assert (ds->entry->d_ino == 0
+ && read_disk_entry (ds->entry->d_reclen) >= needed);
+
+ write_disk_entry (ds->entry->d_ino, np->dn->number);
+ DIRECT_NAMLEN (ds->entry) = namelen;
+ if (direct_symlink_extension)
+ ds->entry->d_type = IFTODT (np->dn_stat.st_mode);
+ bcopy (name, ds->entry->d_name, namelen + 1);
+
+ break;
+
+ case SHRINK:
+ /* We are supposed to take the extra space at the end
+ of this slot. */
+ oldneeded = DIRSIZ (DIRECT_NAMLEN (ds->entry));
+ assert (read_disk_entry (ds->entry->d_reclen) - oldneeded >= needed);
+
+ new = (struct directory_entry *) ((vm_address_t) ds->entry + oldneeded);
+
+ write_disk_entry (new->d_ino, np->dn->number);
+ write_disk_entry (new->d_reclen,
+ read_disk_entry (ds->entry->d_reclen) - oldneeded);
+ DIRECT_NAMLEN (new) = namelen;
+ if (direct_symlink_extension)
+ new->d_type = IFTODT (np->dn_stat.st_mode);
+ bcopy (name, new->d_name, namelen + 1);
+
+ write_disk_entry (ds->entry->d_reclen, oldneeded);
+
+ break;
+
+ case COMPRESS:
+ /* We are supposed to move all the entries to the
+ front of the block, giving each the minimum
+ necessary room. This should free up enough space
+ for the new entry. */
+ fromoff = tooff = (vm_address_t) ds->entry;
+
+ while (fromoff < (vm_address_t) ds->entry + DIRBLKSIZ)
+ {
+ struct directory_entry *from = (struct directory_entry *)fromoff;
+ struct directory_entry *to = (struct directory_entry *) tooff;
+ int fromreclen = read_disk_entry (from->d_reclen);
+
+ if (from->d_ino != 0)
+ {
+ assert (fromoff >= tooff);
+
+ bcopy (from, to, fromreclen);
+ write_disk_entry (to->d_reclen, DIRSIZ (DIRECT_NAMLEN (to)));
+
+ tooff += read_disk_entry (to->d_reclen);
+ }
+ fromoff += fromreclen;
+ }
+
+ totfreed = (vm_address_t) ds->entry + DIRBLKSIZ - tooff;
+ assert (totfreed >= needed);
+
+ new = (struct directory_entry *) tooff;
+ write_disk_entry (new->d_ino, np->dn->number);
+ write_disk_entry (new->d_reclen, totfreed);
+ DIRECT_NAMLEN (new) = namelen;
+ if (direct_symlink_extension)
+ new->d_type = IFTODT (np->dn_stat.st_mode);
+ bcopy (name, new->d_name, namelen + 1);
+ break;
+
+ case EXTEND:
+ /* Extend the file. */
+ assert (needed <= DIRBLKSIZ);
+
+ oldsize = dp->dn_stat.st_size;
+ if ((off_t)(oldsize + DIRBLKSIZ) != dp->dn_stat.st_size + DIRBLKSIZ)
+ {
+ /* We can't possibly map the whole directory in. */
+ munmap ((caddr_t) ds->mapbuf, ds->mapextent);
+ return EOVERFLOW;
+ }
+ while (oldsize + DIRBLKSIZ > dp->allocsize)
+ {
+ err = diskfs_grow (dp, oldsize + DIRBLKSIZ, cred);
+ if (err)
+ {
+ munmap ((caddr_t) ds->mapbuf, ds->mapextent);
+ return err;
+ }
+ }
+
+ new = (struct directory_entry *) (ds->mapbuf + oldsize);
+
+ dp->dn_stat.st_size = oldsize + DIRBLKSIZ;
+ dp->dn_set_ctime = 1;
+
+ write_disk_entry (new->d_ino, np->dn->number);
+ write_disk_entry (new->d_reclen, DIRBLKSIZ);
+ DIRECT_NAMLEN (new) = namelen;
+ if (direct_symlink_extension)
+ new->d_type = IFTODT (np->dn_stat.st_mode);
+ bcopy (name, new->d_name, namelen + 1);
+ break;
+
+ default:
+ assert (0);
+ }
+
+ dp->dn_set_mtime = 1;
+
+ munmap ((caddr_t) ds->mapbuf, ds->mapextent);
+
+ if (ds->stat != EXTEND)
+ {
+ /* If we are keeping count of this block, then keep the count up
+ to date. */
+ if (dp->dn->dirents && dp->dn->dirents[ds->idx] != -1)
+ dp->dn->dirents[ds->idx]++;
+ }
+ else
+ {
+ int i;
+ /* It's cheap, so start a count here even if we aren't counting
+ anything at all. */
+ if (dp->dn->dirents)
+ {
+ dp->dn->dirents = realloc (dp->dn->dirents,
+ (dp->dn_stat.st_size / DIRBLKSIZ
+ * sizeof (int)));
+ for (i = oldsize / DIRBLKSIZ;
+ i < dp->dn_stat.st_size / DIRBLKSIZ;
+ i++)
+ dp->dn->dirents[i] = -1;
+
+ dp->dn->dirents[ds->idx] = 1;
+ }
+ else
+ {
+ dp->dn->dirents = malloc (dp->dn_stat.st_size / DIRBLKSIZ
+ * sizeof (int));
+ for (i = 0; i < dp->dn_stat.st_size / DIRBLKSIZ; i++)
+ dp->dn->dirents[i] = -1;
+ dp->dn->dirents[ds->idx] = 1;
+ }
+ }
+
+ diskfs_file_update (dp, 1);
+
+ return 0;
+}
+
+/* Following a lookup call for REMOVE, this removes the link from the
+ directory. DP is the directory being changed and DS is the cached
+ information returned from lookup. This call is only valid if the
+ directory has been locked continously since the call to lookup, and
+ only if that call succeeded. */
+error_t
+diskfs_dirremove_hard(struct node *dp,
+ struct dirstat *ds)
+{
+ assert (ds->type == REMOVE);
+ assert (ds->stat == HERE_TIS);
+
+ dp->dn_set_mtime = 1;
+
+ if (ds->preventry == 0)
+ ds->entry->d_ino = 0;
+ else
+ {
+ assert ((vm_address_t) ds->entry - (vm_address_t) ds->preventry
+ == read_disk_entry (ds->preventry->d_reclen));
+ write_disk_entry (ds->preventry->d_reclen,
+ (read_disk_entry (ds->preventry->d_reclen)
+ + read_disk_entry (ds->entry->d_reclen)));
+ }
+
+ dp->dn_set_mtime = 1;
+
+ munmap ((caddr_t) ds->mapbuf, ds->mapextent);
+
+ /* If we are keeping count of this block, then keep the count up
+ to date. */
+ if (dp->dn->dirents && dp->dn->dirents[ds->idx] != -1)
+ dp->dn->dirents[ds->idx]--;
+
+ diskfs_file_update (dp, 1);
+
+ return 0;
+}
+
+
+/* Following a lookup call for RENAME, this changes the inode number
+ on a directory entry. DP is the directory being changed; NP is
+ the new node being linked in; DP is the cached information returned
+ by lookup. This call is only valid if the directory has been locked
+ continuously since the call to lookup, and only if that call
+ succeeded. */
+error_t
+diskfs_dirrewrite_hard(struct node *dp,
+ struct node *np,
+ struct dirstat *ds)
+{
+ assert (ds->type == RENAME);
+ assert (ds->stat == HERE_TIS);
+
+ dp->dn_set_mtime = 1;
+ write_disk_entry (ds->entry->d_ino, np->dn->number);
+ if (direct_symlink_extension)
+ ds->entry->d_type = IFTODT (np->dn_stat.st_mode);
+ dp->dn_set_mtime = 1;
+
+ munmap ((caddr_t) ds->mapbuf, ds->mapextent);
+
+ diskfs_file_update (dp, 1);
+
+ return 0;
+}
+
+/* Tell if DP is an empty directory (has only "." and ".." entries). */
+/* This routine must be called from inside a catch_exception (). */
+int
+diskfs_dirempty(struct node *dp,
+ struct protid *cred)
+{
+ struct directory_entry *entry;
+ vm_address_t buf, curoff;
+ memory_object_t memobj;
+ error_t err;
+
+ memobj = diskfs_get_filemap (dp, VM_PROT_READ);
+
+ if (memobj == MACH_PORT_NULL)
+ /* XXX should reflect error properly */
+ return 0;
+
+ buf = 0;
+
+ err = vm_map (mach_task_self (), &buf, dp->dn_stat.st_size, 0,
+ 1, memobj, 0, 0, VM_PROT_READ, VM_PROT_READ, 0);
+ mach_port_deallocate (mach_task_self (), memobj);
+ assert (!err);
+
+ diskfs_set_node_atime (dp);
+
+ for (curoff = buf;
+ curoff < buf + dp->dn_stat.st_size;
+ curoff += read_disk_entry (entry->d_reclen))
+ {
+ entry = (struct directory_entry *) curoff;
+
+ if (entry->d_ino != 0
+ && (DIRECT_NAMLEN (entry) > 2
+ || entry->d_name[0] != '.'
+ || (entry->d_name[1] != '.'
+ && entry->d_name[1] != '\0')))
+ {
+ munmap ((caddr_t) buf, dp->dn_stat.st_size);
+ diskfs_set_node_atime (dp);
+ if (diskfs_synchronous)
+ diskfs_node_update (dp, 1);
+ return 0;
+ }
+ }
+ diskfs_set_node_atime (dp);
+ if (diskfs_synchronous)
+ diskfs_node_update (dp, 1);
+ munmap ((caddr_t) buf, dp->dn_stat.st_size);
+ return 1;
+}
+
+/* Make DS an invalid dirstat. */
+error_t
+diskfs_drop_dirstat (struct node *dp, struct dirstat *ds)
+{
+ if (ds->type != LOOKUP)
+ {
+ assert (ds->mapbuf);
+ munmap ((caddr_t) ds->mapbuf, ds->mapextent);
+ ds->type = LOOKUP;
+ }
+ return 0;
+}
+
+
+/* Count the entries in directory block NB for directory DP and
+ write the answer down in its dirents array. As a side affect
+ fill BUF with the block. */
+static error_t
+count_dirents (struct node *dp, int nb, char *buf)
+{
+ size_t amt;
+ char *offinblk;
+ struct directory_entry *entry;
+ int count = 0;
+ error_t err;
+
+ assert (dp->dn->dirents);
+ assert ((nb + 1) * DIRBLKSIZ <= dp->dn_stat.st_size);
+
+ err = diskfs_node_rdwr (dp, buf, nb * DIRBLKSIZ, DIRBLKSIZ, 0, 0, &amt);
+ if (err)
+ return err;
+ assert (amt == DIRBLKSIZ);
+
+ for (offinblk = buf;
+ offinblk < buf + DIRBLKSIZ;
+ offinblk += read_disk_entry (entry->d_reclen))
+ {
+ entry = (struct directory_entry *) offinblk;
+ if (entry->d_ino)
+ count++;
+ }
+
+ assert (dp->dn->dirents[nb] == -1 || dp->dn->dirents[nb] == count);
+ dp->dn->dirents[nb] = count;
+ return 0;
+}
+
+/* Implement the disikfs_get_directs callback as described in
+ <hurd/diskfs.h>. */
+error_t
+diskfs_get_directs (struct node *dp,
+ int entry,
+ int nentries,
+ char **data,
+ size_t *datacnt,
+ vm_size_t bufsiz,
+ int *amt)
+{
+ int blkno;
+ int nblks;
+ int curentry;
+ char buf[DIRBLKSIZ];
+ char *bufp;
+ int bufvalid;
+ error_t err;
+ int i;
+ char *datap;
+ struct directory_entry *entryp;
+ int allocsize;
+ size_t checklen;
+ struct dirent *userp;
+
+ nblks = dp->dn_stat.st_size/DIRBLKSIZ;
+
+ if (!dp->dn->dirents)
+ {
+ dp->dn->dirents = malloc (nblks * sizeof (int));
+ for (i = 0; i < nblks; i++)
+ dp->dn->dirents[i] = -1;
+ }
+
+ /* Scan through the entries to find ENTRY. If we encounter
+ a -1 in the process then stop to fill it. When we run
+ off the end, ENTRY is too big. */
+ curentry = 0;
+ bufvalid = 0;
+ for (blkno = 0; blkno < nblks; blkno++)
+ {
+ if (dp->dn->dirents[blkno] == -1)
+ {
+ err = count_dirents (dp, blkno, buf);
+ if (err)
+ return err;
+ bufvalid = 1;
+ }
+
+ if (curentry + dp->dn->dirents[blkno] > entry)
+ /* ENTRY starts in this block. */
+ break;
+
+ curentry += dp->dn->dirents[blkno];
+
+ bufvalid = 0;
+ }
+
+ if (blkno == nblks)
+ {
+ /* We reached the end of the directory without seeing ENTRY.
+ This is treated as an EOF condition, meaning we return
+ success with empty results. */
+ *datacnt = 0;
+ *amt = 0;
+ return 0;
+ }
+
+ /* Allocate enough space to hold the maximum we might return */
+ if (!bufsiz || bufsiz > dp->dn_stat.st_size)
+ allocsize = round_page (dp->dn_stat.st_size);
+ else
+ allocsize = round_page (bufsiz);
+
+ if (allocsize > *datacnt)
+ *data = mmap (0, allocsize, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+
+ /* Set bufp appropriately */
+ bufp = buf;
+ if (curentry != entry)
+ {
+ /* Look through the block to find out where to start,
+ setting bufp appropriately. */
+ if (!bufvalid)
+ {
+ err = diskfs_node_rdwr (dp, buf, blkno * DIRBLKSIZ, DIRBLKSIZ,
+ 0, 0, &checklen);
+ if (err)
+ return err;
+ assert (checklen == DIRBLKSIZ);
+ bufvalid = 1;
+ }
+ for (i = 0, bufp = buf;
+ i < entry - curentry && bufp - buf < DIRBLKSIZ;
+ (bufp
+ += read_disk_entry (((struct directory_entry *)bufp)->d_reclen)),
+ i++)
+ ;
+ /* Make sure we didn't run off the end. */
+ assert (bufp - buf < DIRBLKSIZ);
+ }
+
+ i = 0;
+ datap = *data;
+
+ /* Copy the entries, one at a time. */
+ while (((nentries == -1) || (i < nentries))
+ && (!bufsiz || (datap - *data < bufsiz) )
+ && blkno < nblks)
+ {
+ if (!bufvalid)
+ {
+ err = diskfs_node_rdwr (dp, buf, blkno * DIRBLKSIZ, DIRBLKSIZ,
+ 0, 0, &checklen);
+ if (err)
+ return err;
+ assert (checklen == DIRBLKSIZ);
+ bufvalid = 1;
+ bufp = buf;
+ }
+
+ entryp = (struct directory_entry *)bufp;
+
+ if (entryp->d_ino)
+ {
+ userp = (struct dirent *) datap;
+
+ userp->d_fileno = read_disk_entry (entryp->d_ino);
+ userp->d_reclen = DIRSIZ (DIRECT_NAMLEN (entryp));
+ userp->d_namlen = DIRECT_NAMLEN (entryp);
+ bcopy (entryp->d_name, userp->d_name, DIRECT_NAMLEN (entryp) + 1);
+ userp->d_type = DT_UNKNOWN; /* until fixed */
+ i++;
+ datap += DIRSIZ (DIRECT_NAMLEN (entryp));
+ }
+
+ bufp += read_disk_entry (entryp->d_reclen);
+ if (bufp - buf == DIRBLKSIZ)
+ {
+ blkno++;
+ bufvalid = 0;
+ }
+ }
+
+ /* We've copied all we can. If we allocated our own array
+ but didn't fill all of it, then free whatever memory we didn't use. */
+ if (allocsize > *datacnt)
+ {
+ if (round_page (datap - *data) < allocsize)
+ munmap (*data + round_page (datap - *data),
+ allocsize - round_page (datap - *data));
+ }
+
+ /* Set variables for return */
+ *datacnt = datap - *data;
+ *amt = i;
+ return 0;
+}
diff --git a/ufs/dir.h b/ufs/dir.h
new file mode 100644
index 00000000..5730ef44
--- /dev/null
+++ b/ufs/dir.h
@@ -0,0 +1,163 @@
+/* Modified from BSD by Michael I. Bushnell for GNU Hurd ufs server. */
+/*
+ * 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. 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.
+ *
+ * @(#)dir.h 8.2 (Berkeley) 1/21/94
+ */
+
+#ifndef _DIR_H_
+#define _DIR_H_
+
+#include <endian.h>
+
+/*
+ * 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
+ * with null bytes. 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.
+ */
+#define DIRBLKSIZ DEV_BSIZE
+#undef MAXNAMLEN
+#define MAXNAMLEN 255
+
+/* Don't call this struct DIRECT because the library defines that
+ (sometimes) in a possible different way. */
+
+struct directory_entry {
+ u_long d_ino; /* inode number of entry */
+ u_short d_reclen; /* length of this record */
+ u_char d_type; /* file type, see below */
+ u_char d_namlen; /* length of string in d_name */
+ char d_name[MAXNAMLEN + 1]; /* name with length <= MAXNAMLEN */
+};
+
+/* Return the type from a struct directory_entry, paying attention to whether
+ this filesystem supports the type extension */
+#define DIRECT_TYPE(dp) (direct_symlink_extension ? (dp)->d_type : DT_UNKNOWN)
+
+/* Return the namlen from a struct direct, paying attention to whether
+ this filesystem supports the type extension */
+#if (BYTE_ORDER == LITTLE_ENDIAN)
+#define DIRECT_NAMLEN(dp) (*(direct_symlink_extension || swab_disk \
+ ? &(dp)->d_namlen \
+ : &(dp)->d_type))
+#else
+#define DIRECT_NAMLEN(dp) (*(!direct_symlink_extension && swab_disk \
+ ? &(dp)->d_type \
+ : &(dp)->d_namlen))
+#endif
+
+/*
+ * 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.
+ */
+/* In BSD this macro takes a struct direct. Modified by MIB here to
+ take the namelen (as computed by strlen). */
+#define DIRSIZ(namelen) \
+ ((sizeof (struct directory_entry) - (MAXNAMLEN+1)) + (((namelen)+1 + 3) &~ 3))
+
+#if 0 /* This is the BSD definition */
+#if (BYTE_ORDER == LITTLE_ENDIAN)
+#define DIRSIZ(oldfmt, dp) \
+ ((oldfmt) ? \
+ ((sizeof (struct direct) - (MAXNAMLEN+1)) + (((dp)->d_type+1 + 3) &~ 3)) : \
+ ((sizeof (struct direct) - (MAXNAMLEN+1)) + (((dp)->d_namlen+1 + 3) &~ 3)))
+#else
+#define DIRSIZ(oldfmt, dp) \
+ ((sizeof (struct direct) - (MAXNAMLEN+1)) + (((dp)->d_namlen+1 + 3) &~ 3))
+#endif
+#endif /* 0 */
+
+#define OLDDIRFMT 1
+#define NEWDIRFMT 0
+
+#if 0 /* Not used in GNU */
+/*
+ * 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_long dot_ino;
+ short dot_reclen;
+ u_char dot_type;
+ u_char dot_namlen;
+ char dot_name[4]; /* must be multiple of 4 */
+ u_long dotdot_ino;
+ short dotdot_reclen;
+ u_char dotdot_type;
+ u_char dotdot_namlen;
+ char dotdot_name[4]; /* ditto */
+};
+
+/*
+ * This is the old format of directories, sanz type element.
+ */
+struct odirtemplate {
+ u_long dot_ino;
+ short dot_reclen;
+ u_short dot_namlen;
+ char dot_name[4]; /* must be multiple of 4 */
+ u_long dotdot_ino;
+ short dotdot_reclen;
+ u_short dotdot_namlen;
+ char dotdot_name[4]; /* ditto */
+};
+#endif /* 0 */
+
+#endif /* !_DIR_H_ */
diff --git a/ufs/fs.h b/ufs/fs.h
new file mode 100644
index 00000000..a2a3cc9b
--- /dev/null
+++ b/ufs/fs.h
@@ -0,0 +1,509 @@
+/*
+ * 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. 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.
+ *
+ * @(#)fs.h 8.7 (Berkeley) 4/19/94
+ */
+
+/*
+ * 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.
+ *
+ * The first boot and super blocks are given in absolute disk addresses.
+ * The byte-offset forms are preferred, as they don't imply a sector size.
+ */
+#define BBSIZE 8192
+#define SBSIZE 8192
+#define BBOFF ((off_t)(0))
+#define SBOFF ((off_t)(BBOFF + BBSIZE))
+#define BBLOCK ((daddr_t)(0))
+#define SBLOCK ((daddr_t)(BBLOCK + BBSIZE / DEV_BSIZE))
+
+/*
+ * 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
+ * addressible; 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.
+ */
+
+/*
+ * The file system is made out of blocks of at most MAXBSIZE units, with
+ * smaller units (fragments) only in the last direct block. MAXBSIZE
+ * primarily determines the size of buffers in the buffer pool. It may be
+ * made larger without any effect on existing file systems; however making
+ * it smaller make make some file systems unmountable.
+ */
+#define MAXBSIZE MAXPHYS
+#define MAXFRAG 8
+
+/*
+ * 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.
+ * The limit on the amount of summary information per file system
+ * is defined by MAXCSBUFS. It is currently parameterized for a
+ * maximum of two million cylinders.
+ */
+#define MAXMNTLEN 512
+#define MAXCSBUFS 32
+
+/*
+ * 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
+
+/*
+ * 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
+
+/*
+ * 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.
+ *
+ * N.B. sizeof(struct csum) must be a power of two in order for
+ * the ``fs_cs'' macro to work (see below).
+ */
+struct csum {
+ long cs_ndir; /* number of directories */
+ long cs_nbfree; /* number of free blocks */
+ long cs_nifree; /* number of free inodes */
+ long cs_nffree; /* number of free frags */
+};
+
+/*
+ * Super block for a file system.
+ */
+struct fs {
+ struct fs *fs_link; /* linked list of file systems */
+ struct fs *fs_rlink; /* used for incore super blocks */
+ daddr_t fs_sblkno; /* addr of super-block in filesys */
+ daddr_t fs_cblkno; /* offset of cyl-block in filesys */
+ daddr_t fs_iblkno; /* offset of inode-blocks in filesys */
+ daddr_t fs_dblkno; /* offset of first data after cg */
+ long fs_cgoffset; /* cylinder group offset in cylinder */
+ long fs_cgmask; /* used to calc mod fs_ntrak */
+ time_t fs_time; /* last time written */
+ long fs_size; /* number of blocks in fs */
+ long fs_dsize; /* number of data blocks in fs */
+ long fs_ncg; /* number of cylinder groups */
+ long fs_bsize; /* size of basic blocks in fs */
+ long fs_fsize; /* size of frag blocks in fs */
+ long fs_frag; /* number of frags in a block in fs */
+/* these are configuration parameters */
+ long fs_minfree; /* minimum percentage of free blocks */
+ long fs_rotdelay; /* num of ms for optimal next block */
+ long fs_rps; /* disk revolutions per second */
+/* these fields can be computed from the others */
+ long fs_bmask; /* ``blkoff'' calc of blk offsets */
+ long fs_fmask; /* ``fragoff'' calc of frag offsets */
+ long fs_bshift; /* ``lblkno'' calc of logical blkno */
+ long fs_fshift; /* ``numfrags'' calc number of frags */
+/* these are configuration parameters */
+ long fs_maxcontig; /* max number of contiguous blks */
+ long fs_maxbpg; /* max number of blks per cyl group */
+/* these fields can be computed from the others */
+ long fs_fragshift; /* block to frag shift */
+ long fs_fsbtodb; /* fsbtodb and dbtofsb shift constant */
+ long fs_sbsize; /* actual size of super block */
+ long fs_csmask; /* csum block offset */
+ long fs_csshift; /* csum block number */
+ long fs_nindir; /* value of NINDIR */
+ long fs_inopb; /* value of INOPB */
+ long fs_nspf; /* value of NSPF */
+/* yet another configuration parameter */
+ long fs_optim; /* optimization preference, see below */
+/* these fields are derived from the hardware */
+ long fs_npsect; /* # sectors/track including spares */
+ long fs_interleave; /* hardware sector interleave */
+ long fs_trackskew; /* sector 0 skew, per track */
+ long fs_headswitch; /* head switch time, usec */
+ long fs_trkseek; /* track-to-track seek, usec */
+/* sizes determined by number of cylinder groups and their sizes */
+ daddr_t fs_csaddr; /* blk addr of cyl grp summary area */
+ long fs_cssize; /* size of cyl grp summary area */
+ long fs_cgsize; /* cylinder group size */
+/* these fields are derived from the hardware */
+ long fs_ntrak; /* tracks per cylinder */
+ long fs_nsect; /* sectors per track */
+ long fs_spc; /* sectors per cylinder */
+/* this comes from the disk driver partitioning */
+ long fs_ncyl; /* cylinders in file system */
+/* these fields can be computed from the others */
+ long fs_cpg; /* cylinders per group */
+ long fs_ipg; /* inodes per group */
+ long fs_fpg; /* blocks per group * fs_frag */
+/* this data must be re-computed after crashes */
+ struct csum fs_cstotal; /* cylinder summary information */
+/* these fields are cleared at mount time */
+ char fs_fmod; /* super block modified flag */
+ char fs_clean; /* file system is clean flag */
+ char fs_ronly; /* mounted read-only flag */
+ char fs_flags; /* currently unused flag */
+ char fs_fsmnt[MAXMNTLEN]; /* name mounted on */
+/* these fields retain the current block allocation info */
+ long fs_cgrotor; /* last cg searched */
+ struct csum *fs_csp[MAXCSBUFS];/* list of fs_cs info buffers */
+ long fs_cpc; /* cyl per cycle in postbl */
+ short fs_opostbl[16][8]; /* old rotation block list head */
+ long fs_sparecon[50]; /* reserved for future constants */
+ long fs_contigsumsize; /* size of cluster summary array */
+ long fs_maxsymlinklen; /* max length of an internal symlink */
+ long fs_inodefmt; /* format of on-disk inodes */
+ u_quad_t fs_maxfilesize; /* maximum representable file size */
+ quad_t fs_qbmask; /* ~fs_bmask - for use with quad size */
+ quad_t fs_qfmask; /* ~fs_fmask - for use with quad size */
+ long fs_state; /* validate fs_clean field */
+ long fs_postblformat; /* format of positional layout tables */
+ long fs_nrpos; /* number of rotational positions */
+ long fs_postbloff; /* (short) rotation block list head */
+ long fs_rotbloff; /* (u_char) blocks for each rotation */
+ long fs_magic; /* magic number */
+ u_char fs_space[1]; /* list of blocks for each rotation */
+/* actually longer */
+};
+/*
+ * Filesystem idetification
+ */
+#define FS_MAGIC 0x011954 /* the fast filesystem magic number */
+#define FS_OKAY 0x7c269d38 /* superblock checksum */
+#define FS_42INODEFMT -1 /* 4.2BSD inode format */
+#define FS_44INODEFMT 2 /* 4.4BSD inode format */
+/*
+ * Preference for optimization.
+ */
+#define FS_OPTTIME 0 /* minimize allocation time */
+#define FS_OPTSPACE 1 /* minimize disk fragmentation */
+
+/*
+ * Rotational layout table format types
+ */
+#define FS_42POSTBLFMT -1 /* 4.2BSD rotational table format */
+#define FS_DYNAMICPOSTBLFMT 1 /* dynamic rotational table format */
+/*
+ * Macros for access to superblock array structures
+ */
+#define fs_postbl(fs, cylno) \
+ (((fs)->fs_postblformat == FS_42POSTBLFMT) \
+ ? ((fs)->fs_opostbl[cylno]) \
+ : ((short *)((char *)(fs) + (fs)->fs_postbloff) + (cylno) * (fs)->fs_nrpos))
+#define fs_rotbl(fs) \
+ (((fs)->fs_postblformat == FS_42POSTBLFMT) \
+ ? ((fs)->fs_space) \
+ : ((u_char *)((char *)(fs) + (fs)->fs_rotbloff)))
+
+/*
+ * 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(fs) \
+ /* base cg */ (sizeof(struct cg) + sizeof(long) + \
+ /* blktot size */ (fs)->fs_cpg * sizeof(long) + \
+ /* blks size */ (fs)->fs_cpg * (fs)->fs_nrpos * sizeof(short) + \
+ /* inode map */ howmany((fs)->fs_ipg, NBBY) + \
+ /* block map */ howmany((fs)->fs_cpg * (fs)->fs_spc / NSPF(fs), NBBY) +\
+ /* if present */ ((fs)->fs_contigsumsize <= 0 ? 0 : \
+ /* cluster sum */ (fs)->fs_contigsumsize * sizeof(long) + \
+ /* cluster map */ howmany((fs)->fs_cpg * (fs)->fs_spc / NSPB(fs), NBBY)))
+
+#if 0 /* Wrong for GNU Hurd ufs; we don't use fs_csp at all. */
+/*
+ * Convert cylinder group to base address of its global summary info.
+ *
+ * N.B. This macro assumes that sizeof(struct csum) is a power of two.
+ */
+#define fs_cs(fs, indx) \
+ fs_csp[(indx) >> (fs)->fs_csshift][(indx) & ~(fs)->fs_csmask]
+#else
+/* Global variable csum is declared in ufs.h; use it instead
+ of fs_cs stuff. */
+#define fs_cs(fs, indx) this will generate a syntax error.
+#endif
+
+/*
+ * Cylinder group block for a file system.
+ */
+#define CG_MAGIC 0x090255
+struct cg {
+ struct cg *cg_link; /* linked list of cyl groups */
+ long cg_magic; /* magic number */
+ time_t cg_time; /* time last written */
+ long cg_cgx; /* we are the cgx'th cylinder group */
+ short cg_ncyl; /* number of cyl's this cg */
+ short cg_niblk; /* number of inode blocks this cg */
+ long cg_ndblk; /* number of data blocks this cg */
+ struct csum cg_cs; /* cylinder summary information */
+ long cg_rotor; /* position of last used block */
+ long cg_frotor; /* position of last used frag */
+ long cg_irotor; /* position of last used inode */
+ long cg_frsum[MAXFRAG]; /* counts of available frags */
+ long cg_btotoff; /* (long) block totals per cylinder */
+ long cg_boff; /* (short) free block positions */
+ long cg_iusedoff; /* (char) used inode map */
+ long cg_freeoff; /* (u_char) free block map */
+ long cg_nextfreeoff; /* (u_char) next available space */
+ long cg_clustersumoff; /* (long) counts of avail clusters */
+ long cg_clusteroff; /* (char) free cluster map */
+ long cg_nclusterblks; /* number of clusters this cg */
+ long cg_sparecon[13]; /* reserved for future use */
+ u_char cg_space[1]; /* space for cylinder group maps */
+/* actually longer */
+};
+/*
+ * Macros for access to cylinder group array structures
+ */
+#define cg_blktot(cgp) \
+ (((cgp)->cg_magic != CG_MAGIC) \
+ ? (((struct ocg *)(cgp))->cg_btot) \
+ : ((long *)((char *)(cgp) + (cgp)->cg_btotoff)))
+#define cg_blks(fs, cgp, cylno) \
+ (((cgp)->cg_magic != CG_MAGIC) \
+ ? (((struct ocg *)(cgp))->cg_b[cylno]) \
+ : ((short *)((char *)(cgp) + (cgp)->cg_boff) + (cylno) * (fs)->fs_nrpos))
+#define cg_inosused(cgp) \
+ (((cgp)->cg_magic != CG_MAGIC) \
+ ? (((struct ocg *)(cgp))->cg_iused) \
+ : ((char *)((char *)(cgp) + (cgp)->cg_iusedoff)))
+#define cg_blksfree(cgp) \
+ (((cgp)->cg_magic != CG_MAGIC) \
+ ? (((struct ocg *)(cgp))->cg_free) \
+ : ((u_char *)((char *)(cgp) + (cgp)->cg_freeoff)))
+#define cg_chkmagic(cgp) \
+ ((cgp)->cg_magic == CG_MAGIC || ((struct ocg *)(cgp))->cg_magic == CG_MAGIC)
+#define cg_clustersfree(cgp) \
+ ((u_char *)((char *)(cgp) + (cgp)->cg_clusteroff))
+#define cg_clustersum(cgp) \
+ ((long *)((char *)(cgp) + (cgp)->cg_clustersumoff))
+
+/*
+ * The following structure is defined
+ * for compatibility with old file systems.
+ */
+struct ocg {
+ struct ocg *cg_link; /* linked list of cyl groups */
+ struct ocg *cg_rlink; /* used for incore cyl groups */
+ time_t cg_time; /* time last written */
+ long cg_cgx; /* we are the cgx'th cylinder group */
+ short cg_ncyl; /* number of cyl's this cg */
+ short cg_niblk; /* number of inode blocks this cg */
+ long cg_ndblk; /* number of data blocks this cg */
+ struct csum cg_cs; /* cylinder summary information */
+ long cg_rotor; /* position of last used block */
+ long cg_frotor; /* position of last used frag */
+ long cg_irotor; /* position of last used inode */
+ long cg_frsum[8]; /* counts of available frags */
+ long cg_btot[32]; /* block totals per cylinder */
+ short cg_b[32][8]; /* positions of free blocks */
+ char cg_iused[256]; /* used inode map */
+ long cg_magic; /* magic number */
+ u_char cg_free[1]; /* free block map */
+/* actually longer */
+};
+
+/*
+ * 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 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 */
+#define cgstart(fs, c) \
+ (cgbase(fs, c) + (fs)->fs_cgoffset * ((c) & ~((fs)->fs_cgmask)))
+
+/*
+ * 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 cbtocylno(fs, bno) \
+ ((bno) * NSPF(fs) / (fs)->fs_spc)
+#define cbtorpos(fs, bno) \
+ (((bno) * NSPF(fs) % (fs)->fs_spc / (fs)->fs_nsect * (fs)->fs_trackskew + \
+ (bno) * NSPF(fs) % (fs)->fs_spc % (fs)->fs_nsect * (fs)->fs_interleave) % \
+ (fs)->fs_nsect * (fs)->fs_nrpos / (fs)->fs_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 lblktosize(fs, blk) /* calculates (blk * fs->fs_bsize) */ \
+ ((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 - ((fs)->fs_dsize * (percentreserved) / 100))
+
+/*
+ * Determining the size of a file block in the file system.
+ */
+/* Changed from BSD to use allocsize instead of i_size. */
+#define blksize(fs, np, lbn) \
+ (((lbn) >= NDADDR || (np)->allocsize >= ((lbn) + 1) << (fs)->fs_bshift) \
+ ? (fs)->fs_bsize \
+ : (fragroundup(fs, blkoff(fs, (np)->allocsize))))
+
+#if 0 /* Don't use this */
+#define dblksize(fs, dip, lbn) \
+ (((lbn) >= NDADDR || (dip)->di_size >= ((lbn) + 1) << (fs)->fs_bshift) \
+ ? (fs)->fs_bsize \
+ : (fragroundup(fs, blkoff(fs, (dip)->di_size))))
+#endif
+
+/*
+ * Number of disk sectors per block; assumes DEV_BSIZE byte sector size.
+ */
+#define NSPB(fs) ((fs)->fs_nspf << (fs)->fs_fragshift)
+#define NSPF(fs) ((fs)->fs_nspf)
+
+/*
+ * INOPB is the number of inodes in a secondary storage block.
+ */
+#define INOPB(fs) ((fs)->fs_inopb)
+#define INOPF(fs) ((fs)->fs_inopb >> (fs)->fs_fragshift)
+
+/*
+ * NINDIR is the number of indirects in a file system block.
+ */
+#define NINDIR(fs) ((fs)->fs_nindir)
+
+extern int inside[], around[];
+extern u_char *fragtbl[];
diff --git a/ufs/hyper.c b/ufs/hyper.c
new file mode 100644
index 00000000..ece327a2
--- /dev/null
+++ b/ufs/hyper.c
@@ -0,0 +1,414 @@
+/* Fetching and storing the hypermetadata (superblock and cg summary info).
+ Copyright (C) 1994, 95, 96, 97, 98, 1999 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "ufs.h"
+#include <string.h>
+#include <stdio.h>
+#include <error.h>
+#include <hurd/store.h>
+
+static int ufs_clean; /* fs clean before we started writing? */
+
+static int oldformat;
+
+void *zeroblock;
+
+struct fs *sblock;
+struct csum *csum;
+
+void
+swab_sblock (struct fs *sblock)
+{
+ int i, j;
+
+ sblock->fs_sblkno = swab_long (sblock->fs_sblkno);
+ sblock->fs_cblkno = swab_long (sblock->fs_cblkno);
+ sblock->fs_iblkno = swab_long (sblock->fs_iblkno);
+ sblock->fs_dblkno = swab_long (sblock->fs_dblkno);
+ sblock->fs_cgoffset = swab_long (sblock->fs_cgoffset);
+ sblock->fs_cgmask = swab_long (sblock->fs_cgmask);
+ sblock->fs_time = swab_long (sblock->fs_time);
+ sblock->fs_size = swab_long (sblock->fs_size);
+ sblock->fs_dsize = swab_long (sblock->fs_dsize);
+ sblock->fs_ncg = swab_long (sblock->fs_ncg);
+ sblock->fs_bsize = swab_long (sblock->fs_bsize);
+ sblock->fs_fsize = swab_long (sblock->fs_fsize);
+ sblock->fs_frag = swab_long (sblock->fs_frag);
+ sblock->fs_minfree = swab_long (sblock->fs_minfree);
+ sblock->fs_rotdelay = swab_long (sblock->fs_rotdelay);
+ sblock->fs_rps = swab_long (sblock->fs_rps);
+ sblock->fs_bmask = swab_long (sblock->fs_bmask);
+ sblock->fs_fmask = swab_long (sblock->fs_fmask);
+ sblock->fs_bshift = swab_long (sblock->fs_bshift);
+ sblock->fs_fshift = swab_long (sblock->fs_fshift);
+ sblock->fs_maxcontig = swab_long (sblock->fs_maxcontig);
+ sblock->fs_maxbpg = swab_long (sblock->fs_maxbpg);
+ sblock->fs_fragshift = swab_long (sblock->fs_fragshift);
+ sblock->fs_fsbtodb = swab_long (sblock->fs_fsbtodb);
+ sblock->fs_sbsize = swab_long (sblock->fs_sbsize);
+ sblock->fs_csmask = swab_long (sblock->fs_csmask);
+ sblock->fs_csshift = swab_long (sblock->fs_csshift);
+ sblock->fs_nindir = swab_long (sblock->fs_nindir);
+ sblock->fs_inopb = swab_long (sblock->fs_inopb);
+ sblock->fs_nspf = swab_long (sblock->fs_nspf);
+ sblock->fs_optim = swab_long (sblock->fs_optim);
+ sblock->fs_npsect = swab_long (sblock->fs_npsect);
+ sblock->fs_interleave = swab_long (sblock->fs_interleave);
+ sblock->fs_trackskew = swab_long (sblock->fs_trackskew);
+ sblock->fs_headswitch = swab_long (sblock->fs_headswitch);
+ sblock->fs_trkseek = swab_long (sblock->fs_trkseek);
+ sblock->fs_csaddr = swab_long (sblock->fs_csaddr);
+ sblock->fs_cssize = swab_long (sblock->fs_cssize);
+ sblock->fs_cgsize = swab_long (sblock->fs_cgsize);
+ sblock->fs_ntrak = swab_long (sblock->fs_ntrak);
+ sblock->fs_nsect = swab_long (sblock->fs_nsect);
+ sblock->fs_spc = swab_long (sblock->fs_spc);
+ sblock->fs_ncyl = swab_long (sblock->fs_ncyl);
+ sblock->fs_cpg = swab_long (sblock->fs_cpg);
+ sblock->fs_ipg = swab_long (sblock->fs_ipg);
+ sblock->fs_fpg = swab_long (sblock->fs_fpg);
+ sblock->fs_cstotal.cs_ndir = swab_long (sblock->fs_cstotal.cs_ndir);
+ sblock->fs_cstotal.cs_nbfree = swab_long (sblock->fs_cstotal.cs_nbfree);
+ sblock->fs_cstotal.cs_nifree = swab_long (sblock->fs_cstotal.cs_nifree);
+ sblock->fs_cstotal.cs_nffree = swab_long (sblock->fs_cstotal.cs_nffree);
+ /* fs_fmod, fs_clean, fs_ronly, fs_flags, fs_fsmnt are all char */
+ sblock->fs_cgrotor = swab_long (sblock->fs_cgrotor);
+ sblock->fs_cpc = swab_long (sblock->fs_cpc);
+ sblock->fs_contigsumsize = swab_long (sblock->fs_contigsumsize);
+ sblock->fs_maxsymlinklen = swab_long (sblock->fs_maxsymlinklen);
+ sblock->fs_inodefmt = swab_long (sblock->fs_inodefmt);
+ sblock->fs_maxfilesize = swab_long_long (sblock->fs_maxfilesize);
+ sblock->fs_qbmask = swab_long_long (sblock->fs_qbmask);
+ sblock->fs_state = swab_long (sblock->fs_state);
+ sblock->fs_postblformat = swab_long (sblock->fs_postblformat);
+ sblock->fs_nrpos = swab_long (sblock->fs_nrpos);
+ sblock->fs_postbloff = swab_long (sblock->fs_postbloff);
+ sblock->fs_rotbloff = swab_long (sblock->fs_rotbloff);
+ sblock->fs_magic = swab_long (sblock->fs_magic);
+
+ /* Tables */
+ if (sblock->fs_postblformat == FS_42POSTBLFMT)
+ for (i = 0; i < 16; i++)
+ for (j = 0; j < 8; j++)
+ sblock->fs_opostbl[i][j] = swab_short (sblock->fs_opostbl[i][j]);
+ else
+ for (i = 0; i < sblock->fs_cpc; i++)
+ for (j = 0; j < sblock->fs_nrpos; j++)
+ fs_postbl(sblock, j)[i]
+ = swab_short (fs_postbl (sblock, j)[i]);
+
+ /* The rot table is all chars */
+}
+
+void
+swab_csums (struct csum *csum)
+{
+ int i;
+
+ for (i = 0; i < sblock->fs_ncg; i++)
+ {
+ csum[i].cs_ndir = swab_long (csum[i].cs_ndir);
+ csum[i].cs_nbfree = swab_long (csum[i].cs_nbfree);
+ csum[i].cs_nifree = swab_long (csum[i].cs_nifree);
+ csum[i].cs_nffree = swab_long (csum[i].cs_nffree);
+ }
+}
+
+void
+get_hypermetadata (void)
+{
+ error_t err;
+
+ if (!sblock)
+ sblock = malloc (SBSIZE);
+
+ /* Free previous values. */
+ if (zeroblock)
+ munmap ((caddr_t) zeroblock, sblock->fs_bsize);
+ if (csum)
+ free (csum);
+
+ err = diskfs_catch_exception ();
+ assert_perror (err);
+ bcopy (disk_image + SBOFF, sblock, SBSIZE);
+ diskfs_end_catch_exception ();
+
+ if ((swab_long (sblock->fs_magic)) == FS_MAGIC)
+ {
+ swab_disk = 1;
+ swab_sblock (sblock);
+ }
+ else
+ swab_disk = 0;
+
+ if (sblock->fs_magic != FS_MAGIC)
+ {
+ fprintf (stderr, "Bad magic number %#lx (should be %#x)\n",
+ sblock->fs_magic, FS_MAGIC);
+ exit (1);
+ }
+ if (sblock->fs_bsize > 8192)
+ {
+ fprintf (stderr, "Block size %ld is too big (max is 8192 bytes)\n",
+ sblock->fs_bsize);
+ exit (1);
+ }
+ if (sblock->fs_bsize < sizeof (struct fs))
+ {
+ fprintf (stderr, "Block size %ld is too small (min is %Zd bytes)\n",
+ sblock->fs_bsize, sizeof (struct fs));
+ exit (1);
+ }
+
+ if (sblock->fs_maxsymlinklen > (long)MAXSYMLINKLEN)
+ {
+ fprintf (stderr, "Max shortcut symlinklen %ld is too big (max is %ld)\n",
+ sblock->fs_maxsymlinklen, (long)MAXSYMLINKLEN);
+ exit (1);
+ }
+
+ assert ((__vm_page_size % DEV_BSIZE) == 0);
+ assert ((sblock->fs_bsize % DEV_BSIZE) == 0);
+ assert (__vm_page_size <= sblock->fs_bsize);
+
+ /* Examine the clean bit and force read-only if unclean. */
+ ufs_clean = sblock->fs_clean;
+ if (! ufs_clean)
+ {
+ error (0, 0,
+ "%s: warning: FILESYSTEM NOT UNMOUNTED CLEANLY; PLEASE fsck",
+ diskfs_disk_name);
+ if (! diskfs_readonly)
+ {
+ diskfs_readonly = 1;
+ error (0, 0,
+ "%s: MOUNTED READ-ONLY; MUST USE `fsysopts --writable'",
+ diskfs_disk_name);
+ }
+ }
+
+ /* If this is an old filesystem, then we have some more
+ work to do; some crucial constants might not be set; we
+ are therefore forced to set them here. */
+
+ if (sblock->fs_npsect < sblock->fs_nsect)
+ sblock->fs_npsect = sblock->fs_nsect;
+
+ if (sblock->fs_interleave < 1)
+ sblock->fs_interleave = 1;
+
+ if (sblock->fs_postblformat == FS_42POSTBLFMT)
+ sblock->fs_nrpos = 8;
+
+ if (sblock->fs_inodefmt < FS_44INODEFMT)
+ {
+ quad_t sizepb = sblock->fs_bsize;
+ int i;
+
+ oldformat = 1;
+ sblock->fs_maxfilesize = sblock->fs_bsize * NDADDR - 1;
+ for (i = 0; i < NIADDR; i++)
+ {
+ sizepb *= NINDIR (sblock);
+ sblock->fs_maxfilesize += sizepb;
+ }
+ sblock->fs_qbmask = ~sblock->fs_bmask;
+ sblock->fs_qfmask = ~sblock->fs_fmask;
+ }
+
+ /* Find out if we support the 4.4 symlink/dirtype extension */
+ if (sblock->fs_maxsymlinklen > 0)
+ direct_symlink_extension = 1;
+ else
+ direct_symlink_extension = 0;
+
+ csum = malloc (fsaddr (sblock, howmany (sblock->fs_cssize,
+ sblock->fs_fsize)));
+
+ assert (!diskfs_catch_exception ());
+ bcopy (disk_image + fsaddr (sblock, sblock->fs_csaddr),
+ csum,
+ fsaddr (sblock, howmany (sblock->fs_cssize, sblock->fs_fsize)));
+ diskfs_end_catch_exception ();
+
+ if (swab_disk)
+ swab_csums (csum);
+
+ if (store->size < sblock->fs_size * sblock->fs_fsize)
+ {
+ fprintf (stderr,
+ "Disk size (%Ld) less than necessary "
+ "(superblock says we need %ld)\n",
+ store->size, sblock->fs_size * sblock->fs_fsize);
+ exit (1);
+ }
+
+ zeroblock = mmap (0, sblock->fs_bsize, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+
+ /* If the filesystem has new features in it, don't pay attention to
+ the user's request not to use them. */
+ if ((sblock->fs_inodefmt == FS_44INODEFMT
+ || direct_symlink_extension)
+ && compat_mode == COMPAT_BSD42)
+ {
+ compat_mode = COMPAT_BSD44;
+ error (0, 0,
+ "4.2 compat mode requested on 4.4 fs--switched to 4.4 mode");
+ }
+}
+
+/* Write the csum data. This isn't backed by a pager because it is
+ taken from ordinary data blocks and might not be an even number
+ of pages; in that case writing it through the pager would nuke whatever
+ pages came after it on the disk and were backed by file pagers. */
+error_t
+diskfs_set_hypermetadata (int wait, int clean)
+{
+ error_t err;
+
+ spin_lock (&alloclock);
+
+ if (csum_dirty)
+ {
+ /* Copy into a page-aligned buffer to avoid bugs in kernel device
+ code. */
+ void *buf = 0;
+ size_t read = 0;
+ size_t bufsize = round_page (fragroundup (sblock, sblock->fs_cssize));
+
+ err = store_read (store,
+ fsbtodb (sblock, sblock->fs_csaddr)
+ << log2_dev_blocks_per_dev_bsize,
+ bufsize, &buf, &read);
+ if (err)
+ return err;
+ else if (read != bufsize)
+ err = EIO;
+ else
+ {
+ size_t wrote;
+ bcopy (csum, buf, sblock->fs_cssize);
+ if (swab_disk)
+ swab_csums ((struct csum *)buf);
+ err = store_write (store,
+ fsbtodb (sblock, sblock->fs_csaddr)
+ << log2_dev_blocks_per_dev_bsize,
+ buf, bufsize, &wrote);
+ if (!err && wrote != bufsize)
+ err = EIO;
+ }
+
+ munmap (buf, read);
+
+ if (err)
+ {
+ spin_unlock (&alloclock);
+ return err;
+ }
+
+ csum_dirty = 0;
+ }
+
+ if (clean && ufs_clean && !sblock->fs_clean)
+ {
+ /* The filesystem is clean, so set the clean flag. */
+ sblock->fs_clean = 1;
+ sblock_dirty = 1;
+ }
+ else if (!clean && sblock->fs_clean)
+ {
+ /* Clear the clean flag */
+ sblock->fs_clean = 0;
+ sblock_dirty = 1;
+ wait = 1; /* must be synchronous */
+ }
+
+ spin_unlock (&alloclock);
+
+ /* Update the superblock if necessary (clean bit was just set). */
+ copy_sblock ();
+
+ sync_disk (wait);
+ return 0;
+}
+
+/* Copy the sblock into the disk */
+void
+copy_sblock ()
+{
+ error_t err;
+
+ err = diskfs_catch_exception ();
+ assert_perror (err);
+
+ spin_lock (&alloclock);
+
+ if (sblock_dirty)
+ {
+ assert (! diskfs_readonly);
+
+ if (sblock->fs_postblformat == FS_42POSTBLFMT
+ || oldformat
+ || swab_disk)
+ {
+ char sblockcopy[SBSIZE];
+ struct fs *sbcopy = (struct fs *)sblockcopy;
+ bcopy (sblock, sblockcopy, SBSIZE);
+ if (sblock->fs_postblformat == FS_42POSTBLFMT)
+ sbcopy->fs_nrpos = -1;
+ if (oldformat)
+ {
+ sbcopy->fs_maxfilesize = -1;
+ sbcopy->fs_qbmask = -1;
+ sbcopy->fs_qfmask = -1;
+ }
+ if (swab_disk)
+ swab_sblock (sbcopy);
+ bcopy (sbcopy, disk_image + SBOFF, SBSIZE);
+ }
+ else
+ bcopy (sblock, disk_image + SBOFF, SBSIZE);
+ record_poke (disk_image + SBOFF, SBSIZE);
+ sblock_dirty = 0;
+ }
+
+ spin_unlock (&alloclock);
+
+ diskfs_end_catch_exception ();
+}
+
+void
+diskfs_readonly_changed (int readonly)
+{
+ (*(readonly ? store_set_flags : store_clear_flags)) (store, STORE_READONLY);
+
+ mprotect (disk_image, store->size, PROT_READ | (readonly ? 0 : PROT_WRITE));
+
+ if (readonly)
+ {
+ /* We know we are sync'd now. The superblock is marked as dirty
+ because we cleared the clean flag immediately after sync'ing.
+ But now we want to leave it marked clean and not touch it further. */
+ sblock_dirty = 0;
+ return;
+ }
+
+ strcpy (sblock->fs_fsmnt, "Hurd /"); /* XXX */
+
+ if (!sblock->fs_clean)
+ error (0, 0, "WARNING: UNCLEANED FILESYSTEM NOW WRITABLE");
+}
diff --git a/ufs/inode.c b/ufs/inode.c
new file mode 100644
index 00000000..1a8a7098
--- /dev/null
+++ b/ufs/inode.c
@@ -0,0 +1,703 @@
+/* Inode management routines
+
+ Copyright (C) 1994, 1995, 1996, 1997, 1998, 2000, 2001, 2002, 2007
+ Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "ufs.h"
+#include <string.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <netinet/in.h>
+#include <fcntl.h>
+#include <hurd/store.h>
+
+#define INOHSZ 512
+#if ((INOHSZ&(INOHSZ-1)) == 0)
+#define INOHASH(ino) ((ino)&(INOHSZ-1))
+#else
+#define INOHASH(ino) (((unsigned)(ino))%INOHSZ)
+#endif
+
+static struct node *nodehash[INOHSZ];
+static error_t read_disknode (struct node *np);
+
+spin_lock_t gennumberlock = SPIN_LOCK_INITIALIZER;
+
+/* Initialize the inode hash table. */
+void
+inode_init ()
+{
+ int n;
+ for (n = 0; n < INOHSZ; n++)
+ nodehash[n] = 0;
+}
+
+/* Fetch inode INUM, set *NPP to the node structure;
+ gain one user reference and lock the node. */
+error_t
+diskfs_cached_lookup (ino_t inum, struct node **npp)
+{
+ struct disknode *dn;
+ struct node *np;
+ error_t err;
+
+ spin_lock (&diskfs_node_refcnt_lock);
+ for (np = nodehash[INOHASH(inum)]; np; np = np->dn->hnext)
+ {
+ if (np->dn->number != inum)
+ continue;
+
+ np->references++;
+ spin_unlock (&diskfs_node_refcnt_lock);
+ mutex_lock (&np->lock);
+ *npp = np;
+ return 0;
+ }
+
+ dn = malloc (sizeof (struct disknode));
+
+ dn->number = inum;
+ dn->dirents = 0;
+ dn->dir_idx = 0;
+
+ rwlock_init (&dn->allocptrlock);
+ dn->dirty = 0;
+ dn->fileinfo = 0;
+
+ np = diskfs_make_node (dn);
+ np->cache_id = inum;
+
+ mutex_lock (&np->lock);
+ dn->hnext = nodehash[INOHASH(inum)];
+ if (dn->hnext)
+ dn->hnext->dn->hprevp = &dn->hnext;
+ dn->hprevp = &nodehash[INOHASH(inum)];
+ nodehash[INOHASH(inum)] = np;
+ spin_unlock (&diskfs_node_refcnt_lock);
+
+ err = read_disknode (np);
+
+ if (!diskfs_check_readonly () && !np->dn_stat.st_gen)
+ {
+ spin_lock (&gennumberlock);
+ if (++nextgennumber < diskfs_mtime->seconds)
+ nextgennumber = diskfs_mtime->seconds;
+ np->dn_stat.st_gen = nextgennumber;
+ spin_unlock (&gennumberlock);
+ np->dn_set_ctime = 1;
+ }
+
+ if (err)
+ return err;
+ else
+ {
+ *npp = np;
+ return 0;
+ }
+}
+
+/* Lookup node INUM (which must have a reference already) and return it
+ without allocating any new references. */
+struct node *
+ifind (ino_t inum)
+{
+ struct node *np;
+
+ spin_lock (&diskfs_node_refcnt_lock);
+ for (np = nodehash[INOHASH(inum)]; np; np = np->dn->hnext)
+ {
+ if (np->dn->number != inum)
+ continue;
+
+ assert (np->references);
+ spin_unlock (&diskfs_node_refcnt_lock);
+ return np;
+ }
+ assert (0);
+}
+
+/* The last reference to a node has gone away; drop
+ it from the hash table and clean all state in the dn structure. */
+void
+diskfs_node_norefs (struct node *np)
+{
+ *np->dn->hprevp = np->dn->hnext;
+ if (np->dn->hnext)
+ np->dn->hnext->dn->hprevp = np->dn->hprevp;
+ if (np->dn->dirents)
+ free (np->dn->dirents);
+ assert (!np->dn->fileinfo);
+ free (np->dn);
+ free (np);
+}
+
+/* The last hard reference to a node has gone away; arrange to have
+ all the weak references dropped that can be. */
+void
+diskfs_try_dropping_softrefs (struct node *np)
+{
+ drop_pager_softrefs (np);
+}
+
+/* The last hard reference to a node has gone away. */
+void
+diskfs_lost_hardrefs (struct node *np)
+{
+#ifdef notanymore
+ struct port_info *pi;
+ struct pager *p;
+
+ /* Check and see if there is a pager which has only
+ one reference (ours). If so, then drop that reference,
+ breaking the cycle. The complexity in this routine
+ is all due to this cycle. */
+
+ if (np->dn->fileinfo)
+ {
+ spin_lock (&_libports_portrefcntlock);
+ pi = (struct port_info *) np->dn->fileinfo->p;
+ if (pi->refcnt == 1)
+ {
+
+ /* The only way to get a new reference to the pager
+ in this state is to call diskfs_get_filemap; this
+ can't happen as long as we hold NP locked. So
+ we can safely unlock _libports_portrefcntlock for
+ the following call. */
+ spin_unlock (&_libports_portrefcntlock);
+
+ /* Right now the node is locked with no hard refs;
+ this is an anomolous situation. Before messing with
+ the reference count on the file pager, we have to
+ give ourselves a reference back so that we are really
+ allowed to hold the lock. Then we can do the
+ unreference. */
+ p = np->dn->fileinfo->p;
+ np->dn->fileinfo = 0;
+ diskfs_nref (np);
+ pager_unreference (p);
+
+ assert (np->references == 1 && np->light_references == 0);
+
+ /* This will do the real deallocate. Whew. */
+ diskfs_nput (np);
+ }
+ else
+ spin_unlock (&_libports_portrefcntlock);
+ }
+#endif
+}
+
+/* A new hard reference to a node has been created; it's now OK to
+ have unused weak references. */
+void
+diskfs_new_hardrefs (struct node *np)
+{
+ allow_pager_softrefs (np);
+}
+
+/* Read stat information out of the dinode. */
+static error_t
+read_disknode (struct node *np)
+{
+ struct stat *st = &np->dn_stat;
+ struct dinode *di = dino (np->dn->number);
+ error_t err;
+
+ err = diskfs_catch_exception ();
+ if (err)
+ return err;
+
+ st->st_fstype = FSTYPE_UFS;
+ st->st_fsid = getpid (); /* This call is very cheap. */
+ st->st_ino = np->dn->number;
+ st->st_gen = read_disk_entry (di->di_gen);
+ st->st_rdev = read_disk_entry(di->di_rdev);
+ st->st_mode = (((read_disk_entry (di->di_model)
+ | (read_disk_entry (di->di_modeh) << 16))
+ & ~S_ITRANS)
+ | (di->di_trans ? S_IPTRANS : 0));
+ st->st_nlink = read_disk_entry (di->di_nlink);
+ st->st_size = read_disk_entry (di->di_size);
+ st->st_atim.tv_sec = read_disk_entry (di->di_atime.tv_sec);
+ st->st_atim.tv_nsec = read_disk_entry (di->di_atime.tv_nsec);
+ st->st_mtim.tv_sec = read_disk_entry (di->di_mtime.tv_sec);
+ st->st_mtim.tv_nsec = read_disk_entry (di->di_mtime.tv_nsec);
+ st->st_ctim.tv_sec = read_disk_entry (di->di_ctime.tv_sec);
+ st->st_ctim.tv_nsec = read_disk_entry (di->di_ctime.tv_nsec);
+ st->st_blksize = sblock->fs_bsize;
+ st->st_blocks = read_disk_entry (di->di_blocks);
+ st->st_flags = read_disk_entry (di->di_flags);
+
+ if (sblock->fs_inodefmt < FS_44INODEFMT)
+ {
+ st->st_uid = read_disk_entry (di->di_ouid);
+ st->st_gid = read_disk_entry (di->di_ogid);
+ st->st_author = st->st_uid;
+ np->author_tracks_uid = 1;
+ }
+ else
+ {
+ st->st_uid = read_disk_entry (di->di_uid);
+ st->st_gid = read_disk_entry (di->di_gid);
+ st->st_author = read_disk_entry (di->di_author);
+ if (st->st_author == -1)
+ st->st_author = st->st_uid;
+ }
+
+ diskfs_end_catch_exception ();
+ if (!S_ISBLK (st->st_mode) && !S_ISCHR (st->st_mode))
+ st->st_rdev = 0;
+
+ if (S_ISLNK (st->st_mode)
+ && direct_symlink_extension
+ && st->st_size < sblock->fs_maxsymlinklen)
+ np->allocsize = 0;
+ else
+ {
+ if (lblkno (sblock, np->dn_stat.st_size) < NDADDR)
+ np->allocsize = fragroundup (sblock, st->st_size);
+ else
+ np->allocsize = blkroundup (sblock, st->st_size);
+ }
+
+ return 0;
+}
+
+error_t diskfs_node_reload (struct node *node)
+{
+ if (node->dn->dirents)
+ {
+ free (node->dn->dirents);
+ node->dn->dirents = 0;
+ }
+ flush_node_pager (node);
+ read_disknode (node);
+ return 0;
+}
+
+/* Return 0 if NP's author can be changed to AUTHOR; otherwise return an
+ error code. */
+error_t
+diskfs_validate_author_change (struct node *np, uid_t author)
+{
+ if (compat_mode == COMPAT_GNU)
+ return 0;
+ else
+ /* For non-hurd filesystems, the author & owner are the same. */
+ return (author == np->dn_stat.st_uid) ? 0 : EINVAL;
+}
+
+static void
+write_node (struct node *np)
+{
+ struct stat *st = &np->dn_stat;
+ struct dinode *di = dino (np->dn->number);
+ error_t err;
+
+ if (np->dn_stat_dirty)
+ {
+ assert (!diskfs_readonly);
+
+ err = diskfs_catch_exception ();
+ if (err)
+ return;
+
+ write_disk_entry (di->di_gen, st->st_gen);
+
+ if (S_ISBLK (st->st_mode) || S_ISCHR (st->st_mode))
+ write_disk_entry (di->di_rdev, st->st_rdev);
+
+ /* We happen to know that the stat mode bits are the same
+ as the ufs mode bits. */
+
+ if (compat_mode == COMPAT_GNU)
+ {
+ mode_t mode = st->st_mode & ~S_ITRANS;
+ write_disk_entry (di->di_model, mode & 0xffff);
+ write_disk_entry (di->di_modeh, (mode >> 16) & 0xffff);
+ }
+ else
+ {
+ write_disk_entry (di->di_model, st->st_mode & 0xffff & ~S_ITRANS);
+ di->di_modeh = 0;
+ }
+
+ if (compat_mode != COMPAT_BSD42)
+ {
+ write_disk_entry (di->di_uid, st->st_uid);
+ write_disk_entry (di->di_gid, st->st_gid);
+ }
+
+ if (sblock->fs_inodefmt < FS_44INODEFMT)
+ {
+ write_disk_entry (di->di_ouid, st->st_uid & 0xffff);
+ write_disk_entry (di->di_ogid, st->st_gid & 0xffff);
+ }
+ else if (compat_mode == COMPAT_GNU)
+ write_disk_entry (di->di_author, st->st_author);
+
+ write_disk_entry (di->di_nlink, st->st_nlink);
+ write_disk_entry (di->di_size, st->st_size);
+ write_disk_entry (di->di_atime.tv_sec, st->st_atim.tv_sec);
+ write_disk_entry (di->di_atime.tv_nsec, st->st_atim.tv_nsec);
+ write_disk_entry (di->di_mtime.tv_sec, st->st_mtim.tv_sec);
+ write_disk_entry (di->di_mtime.tv_nsec, st->st_mtim.tv_nsec);
+ write_disk_entry (di->di_ctime.tv_sec, st->st_ctim.tv_sec);
+ write_disk_entry (di->di_ctime.tv_nsec, st->st_ctim.tv_nsec);
+ write_disk_entry (di->di_blocks, st->st_blocks);
+ write_disk_entry (di->di_flags, st->st_flags);
+
+ diskfs_end_catch_exception ();
+ np->dn_stat_dirty = 0;
+ record_poke (di, sizeof (struct dinode));
+ }
+}
+
+/* See if we should create a symlink by writing it directly into
+ the block pointer array. Returning EINVAL tells diskfs to do it
+ the usual way. */
+static error_t
+create_symlink_hook (struct node *np, const char *target)
+{
+ int len = strlen (target);
+ error_t err;
+ struct dinode *di;
+
+ if (!direct_symlink_extension)
+ return EINVAL;
+
+ assert (compat_mode != COMPAT_BSD42);
+
+ if (len >= sblock->fs_maxsymlinklen)
+ return EINVAL;
+
+ err = diskfs_catch_exception ();
+ if (err)
+ return err;
+
+ di = dino (np->dn->number);
+ bcopy (target, di->di_shortlink, len);
+ np->dn_stat.st_size = len;
+ np->dn_set_ctime = 1;
+ np->dn_set_mtime = 1;
+ record_poke (di, sizeof (struct dinode));
+
+ diskfs_end_catch_exception ();
+ return 0;
+}
+error_t (*diskfs_create_symlink_hook)(struct node *, const char *)
+ = create_symlink_hook;
+
+/* Check if this symlink is stored directly in the block pointer array.
+ Returning EINVAL tells diskfs to do it the usual way. */
+static error_t
+read_symlink_hook (struct node *np,
+ char *buf)
+{
+ error_t err;
+
+ if (!direct_symlink_extension
+ || np->dn_stat.st_size >= sblock->fs_maxsymlinklen)
+ return EINVAL;
+
+ err = diskfs_catch_exception ();
+ if (err)
+ return err;
+
+ bcopy ((dino (np->dn->number))->di_shortlink, buf, np->dn_stat.st_size);
+
+ diskfs_set_node_atime (np);
+
+ diskfs_end_catch_exception ();
+ return 0;
+}
+error_t (*diskfs_read_symlink_hook)(struct node *, char *)
+ = read_symlink_hook;
+
+error_t
+diskfs_node_iterate (error_t (*fun)(struct node *))
+{
+ struct node *np;
+ struct item {struct item *next; struct node *np;} *list = 0;
+ struct item *i;
+ error_t err;
+ int n;
+
+ /* Acquire a reference on all the nodes in the hash table
+ and enter them into a list on the stack. */
+ spin_lock (&diskfs_node_refcnt_lock);
+ for (n = 0; n < INOHSZ; n++)
+ for (np = nodehash[n]; np; np = np->dn->hnext)
+ {
+ np->references++;
+ i = alloca (sizeof (struct item));
+ i->next = list;
+ i->np = np;
+ list = i;
+ }
+ spin_unlock (&diskfs_node_refcnt_lock);
+
+ err = 0;
+ for (i = list; i; i = i->next)
+ {
+ if (!err)
+ {
+ mutex_lock (&i->np->lock);
+ err = (*fun)(i->np);
+ mutex_unlock (&i->np->lock);
+ }
+ diskfs_nrele (i->np);
+ }
+ return err;
+}
+
+/* Write all active disknodes into the dinode pager. */
+void
+write_all_disknodes ()
+{
+ error_t
+ helper (struct node *np)
+ {
+ diskfs_set_node_times (np);
+ write_node (np);
+ return 0;
+ }
+
+ diskfs_node_iterate (helper);
+}
+
+void
+diskfs_write_disknode (struct node *np, int wait)
+{
+ write_node (np);
+ if (wait)
+ sync_dinode (np->dn->number, 1);
+}
+
+/* Implement the diskfs_set_statfs callback from the diskfs library;
+ see <hurd/diskfs.h> for the interface description. */
+error_t
+diskfs_set_statfs (struct statfs *st)
+{
+ st->f_type = FSTYPE_UFS;
+ st->f_bsize = sblock->fs_fsize;
+ st->f_blocks = sblock->fs_dsize;
+ st->f_bfree = (sblock->fs_cstotal.cs_nbfree * sblock->fs_frag
+ + sblock->fs_cstotal.cs_nffree);
+ st->f_bavail = ((sblock->fs_dsize * (100 - sblock->fs_minfree) / 100)
+ - (sblock->fs_dsize - st->f_bfree));
+ if (st->f_bfree < ((sblock->fs_dsize * (100 - sblock->fs_minfree) / 100)))
+ st->f_bavail = 0;
+ st->f_files = sblock->fs_ncg * sblock->fs_ipg - 2; /* not 0 or 1 */
+ st->f_ffree = sblock->fs_cstotal.cs_nifree;
+ st->f_fsid = getpid ();
+ st->f_namelen = 0;
+ st->f_favail = st->f_ffree;
+ st->f_frsize = sblock->fs_fsize;
+ return 0;
+}
+
+/* Implement the diskfs_set_translator callback from the diskfs
+ library; see <hurd/diskfs.h> for the interface description. */
+error_t
+diskfs_set_translator (struct node *np, const char *name, u_int namelen,
+ struct protid *cred)
+{
+ daddr_t blkno;
+ error_t err;
+ char buf[sblock->fs_bsize];
+ struct dinode *di;
+
+ if (compat_mode != COMPAT_GNU)
+ return EOPNOTSUPP;
+
+ if (namelen + sizeof (u_int) > sblock->fs_bsize)
+ return ENAMETOOLONG;
+
+ err = diskfs_catch_exception ();
+ if (err)
+ return err;
+
+ di = dino (np->dn->number);
+ blkno = read_disk_entry (di->di_trans);
+
+ if (namelen && !blkno)
+ {
+ /* Allocate block for translator */
+ err = ffs_alloc (np, 0, 0, sblock->fs_bsize, &blkno, cred);
+ if (err)
+ {
+ diskfs_end_catch_exception ();
+ return err;
+ }
+ write_disk_entry (di->di_trans, blkno);
+ record_poke (di, sizeof (struct dinode));
+ np->dn_set_ctime = 1;
+ }
+ else if (!namelen && blkno)
+ {
+ /* Clear block for translator going away. */
+ ffs_blkfree (np, blkno, sblock->fs_bsize);
+ di->di_trans = 0;
+ record_poke (di, sizeof (struct dinode));
+ np->dn_stat.st_blocks -= btodb (sblock->fs_bsize);
+ np->dn_stat.st_mode &= ~S_IPTRANS;
+ np->dn_set_ctime = 1;
+ }
+
+ if (namelen)
+ {
+ bcopy (&namelen, buf, sizeof (u_int));
+ bcopy (name, buf + sizeof (u_int), namelen);
+
+ bcopy (buf, disk_image + fsaddr (sblock, blkno), sblock->fs_bsize);
+ sync_disk_blocks (blkno, sblock->fs_bsize, 1);
+
+ np->dn_stat.st_mode |= S_IPTRANS;
+ np->dn_set_ctime = 1;
+ }
+
+ diskfs_end_catch_exception ();
+ return err;
+}
+
+/* Implement the diskfs_get_translator callback from the diskfs library.
+ See <hurd/diskfs.h> for the interface description. */
+error_t
+diskfs_get_translator (struct node *np, char **namep, u_int *namelen)
+{
+ error_t err;
+ daddr_t blkno;
+ u_int datalen;
+ const void *transloc;
+
+ err = diskfs_catch_exception ();
+ if (err)
+ return err;
+
+ blkno = read_disk_entry ((dino (np->dn->number))->di_trans);
+ assert (blkno);
+ transloc = disk_image + fsaddr (sblock, blkno);
+
+ datalen = *(u_int *)transloc;
+ if (datalen > sblock->fs_bsize - sizeof (u_int))
+ err = EFTYPE;
+ else
+ {
+ *namep = malloc (datalen);
+ if (*namep == NULL)
+ err = ENOMEM;
+ memcpy (*namep, transloc + sizeof (u_int), datalen);
+ }
+
+ diskfs_end_catch_exception ();
+
+ *namelen = datalen;
+ return 0;
+}
+
+/* Called when all hard ports have gone away. */
+void
+diskfs_shutdown_soft_ports ()
+{
+ /* Should initiate termination of internally held pager ports
+ (the only things that should be soft) XXX */
+}
+
+/* Return a description of the storage of the file. */
+/* In STORAGE_DATA are the following, in network byte order:
+
+ Inode number (4 bytes)
+ disk address of transator spec (4 bytes)
+ disk address of inode structure (4 bytes)
+ offset into inode block holding inode (4 bytes) */
+error_t
+diskfs_S_file_get_storage_info (struct protid *cred,
+ mach_port_t **ports,
+ mach_msg_type_name_t *ports_type,
+ mach_msg_type_number_t *num_ports,
+ int **ints, mach_msg_type_number_t *num_ints,
+ off_t **offsets,
+ mach_msg_type_number_t *num_offsets,
+ char **data, mach_msg_type_number_t *data_len)
+{
+ error_t err;
+ struct node *np;
+ struct store *file_store;
+ struct store_run runs[NDADDR];
+ size_t num_runs = 0;
+
+ if (! cred)
+ return EOPNOTSUPP;
+
+ np = cred->po->np;
+ mutex_lock (&np->lock);
+
+ /* See if this file fits in the direct block pointers. If not, punt
+ for now. (Reading indir blocks is a pain, and I'm postponing
+ pain.) XXX */
+ if (np->allocsize > NDADDR * sblock->fs_bsize)
+ {
+ mutex_unlock (&np->lock);
+ return EINVAL;
+ }
+
+ err = diskfs_catch_exception ();
+ if (! err)
+ if (!direct_symlink_extension
+ || np->dn_stat.st_size >= sblock->fs_maxsymlinklen
+ || !S_ISLNK (np->dn_stat.st_mode))
+ /* Copy the block pointers */
+ {
+ int i;
+ struct store_run *run = runs;
+ struct dinode *di = dino (np->dn->number);
+
+ for (i = 0; i < NDADDR; i++)
+ {
+ store_offset_t start = fsbtodb (sblock, read_disk_entry (di->di_db[i]));
+ store_offset_t length =
+ (((i + 1) * sblock->fs_bsize > np->allocsize)
+ ? np->allocsize - i * sblock->fs_bsize
+ : sblock->fs_bsize);
+ start <<= log2_dev_blocks_per_dev_bsize;
+ length <<= log2_dev_blocks_per_dev_bsize;
+ if (num_runs == 0 || run->start + run->length != start)
+ *run++ = (struct store_run){ start, length };
+ else
+ run->length += length;
+ }
+ }
+ diskfs_end_catch_exception ();
+
+ mutex_unlock (&np->lock);
+
+ if (! err)
+ err = store_clone (store, &file_store);
+ if (! err)
+ {
+ err = store_remap (file_store, runs, num_runs, &file_store);
+ if (! err)
+ err = store_return (file_store, ports, num_ports, ints, num_ints,
+ offsets, num_offsets, data, data_len);
+ store_free (file_store);
+ }
+ *ports_type = MACH_MSG_TYPE_COPY_SEND;
+
+ return err;
+}
diff --git a/ufs/main.c b/ufs/main.c
new file mode 100644
index 00000000..242107f4
--- /dev/null
+++ b/ufs/main.c
@@ -0,0 +1,210 @@
+/*
+ Copyright (C) 1994,95,96,97,98,99,2002 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+#include "ufs.h"
+#include <stdarg.h>
+#include <stdio.h>
+#include <error.h>
+#include <device/device.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <argz.h>
+#include <argp.h>
+#include <hurd/store.h>
+
+struct node *diskfs_root_node;
+
+struct store *store = 0;
+struct store_parsed *store_parsed = 0;
+
+char *diskfs_disk_name = 0;
+
+/* Number of device blocks per DEV_BSIZE block. */
+unsigned log2_dev_blocks_per_dev_bsize = 0;
+
+/* Set diskfs_root_node to the root inode. */
+static void
+warp_root (void)
+{
+ error_t err;
+ err = diskfs_cached_lookup (2, &diskfs_root_node);
+ assert (!err);
+ mutex_unlock (&diskfs_root_node->lock);
+}
+
+/* XXX */
+struct mutex printf_lock = MUTEX_INITIALIZER;
+int printf (const char *fmt, ...)
+{
+ va_list arg;
+ int done;
+ va_start (arg, fmt);
+ mutex_lock (&printf_lock);
+ done = vprintf (fmt, arg);
+ mutex_unlock (&printf_lock);
+ va_end (arg);
+ return done;
+}
+
+int diskfs_readonly;
+
+/* Ufs-specific options. XXX this should be moved so it can be done at
+ runtime as well as startup. */
+static const struct argp_option
+options[] =
+{
+ {"compat", 'C', "FMT", 0,
+ "FMT may be GNU, 4.4, or 4.2, and determines which filesystem extensions"
+ " are written onto the disk (default is GNU)"},
+ {0}
+};
+
+/* Parse a ufs-specific command line option. */
+static error_t
+parse_opt (int key, char *arg, struct argp_state *state)
+{
+ switch (key)
+ {
+ enum compat_mode mode;
+
+ case 'C':
+ if (strcasecmp (arg, "gnu") == 0)
+ mode = COMPAT_GNU;
+ else if (strcmp (arg, "4.4") == 0)
+ mode = COMPAT_BSD44;
+ else if (strcmp (arg, "4.2") == 0)
+ {
+ if (sblock
+ && (sblock->fs_inodefmt == FS_44INODEFMT
+ || direct_symlink_extension))
+ {
+ argp_failure (state, 0, 0,
+ "4.2 compat mode requested on 4.4 fs");
+ return EINVAL;
+ }
+ mode = COMPAT_BSD42;
+ }
+ else
+ {
+ argp_error (state, "%s: Unknown compatibility mode", arg);
+ return EINVAL;
+ }
+
+ state->hook = (void *)mode; /* Save it for the end. */
+ break;
+
+ case ARGP_KEY_INIT:
+ state->child_inputs[0] = state->input;
+ state->hook = (void *)compat_mode; break;
+ case ARGP_KEY_SUCCESS:
+ compat_mode = (enum compat_mode)state->hook; break;
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+}
+
+/* Add our startup arguments to the standard diskfs set. */
+static const struct argp_child startup_children[] =
+ {{&diskfs_store_startup_argp}, {0}};
+static struct argp startup_argp = {options, parse_opt, 0, 0, startup_children};
+
+/* Similarly at runtime. */
+static const struct argp_child runtime_children[] =
+ {{&diskfs_std_runtime_argp}, {0}};
+static struct argp runtime_argp = {options, parse_opt, 0, 0, runtime_children};
+
+struct argp *diskfs_runtime_argp = (struct argp *)&runtime_argp;
+
+/* Override the standard diskfs routine so we can add our own output. */
+error_t
+diskfs_append_args (char **argz, size_t *argz_len)
+{
+ error_t err;
+
+ /* Get the standard things. */
+ err = diskfs_append_std_options (argz, argz_len);
+
+ if (!err && compat_mode != COMPAT_GNU)
+ err = argz_add (argz, argz_len,
+ ((compat_mode == COMPAT_BSD42)
+ ? "--compat=4.2"
+ : "--compat=4.4"));
+
+ if (! err)
+ err = store_parsed_append_args (store_parsed, argz, argz_len);
+
+ return err;
+}
+
+int
+main (int argc, char **argv)
+{
+ mach_port_t bootstrap;
+
+ /* Initialize the diskfs library, parse arguments, and open the store.
+ This starts the first diskfs thread for us. */
+ store = diskfs_init_main (&startup_argp, argc, argv,
+ &store_parsed, &bootstrap);
+
+ if (store->block_size > DEV_BSIZE)
+ error (4, 0, "%s: Bad device block size %zd (should be <= %d)",
+ diskfs_disk_name, store->block_size, DEV_BSIZE);
+ if (store->size < SBSIZE + SBOFF)
+ error (5, 0, "%s: Disk too small (%Ld bytes)", diskfs_disk_name,
+ store->size);
+
+ log2_dev_blocks_per_dev_bsize = 0;
+ while ((1 << log2_dev_blocks_per_dev_bsize) < DEV_BSIZE)
+ log2_dev_blocks_per_dev_bsize++;
+ log2_dev_blocks_per_dev_bsize -= store->log2_block_size;
+
+ /* Map the entire disk. */
+ create_disk_pager ();
+
+ get_hypermetadata ();
+
+ inode_init ();
+
+ /* Find our root node. */
+ warp_root ();
+
+ /* Now that we are all set up to handle requests, and diskfs_root_node is
+ set properly, it is safe to export our fsys control port to the
+ outside world. */
+ diskfs_startup_diskfs (bootstrap, 0);
+
+ /* SET HOST NAME */
+
+ /* And this thread is done with its work. */
+ cthread_exit (0);
+
+ return 0;
+}
+
+error_t
+diskfs_reload_global_state ()
+{
+ flush_pokes ();
+ pager_flush (diskfs_disk_pager, 1);
+ get_hypermetadata ();
+ return 0;
+}
diff --git a/ufs/pager.c b/ufs/pager.c
new file mode 100644
index 00000000..3038932d
--- /dev/null
+++ b/ufs/pager.c
@@ -0,0 +1,806 @@
+/* Pager for ufs
+ Copyright (C) 1994, 1995, 1996, 1997, 1999 Free Software Foundation
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "ufs.h"
+#include <strings.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <hurd/store.h>
+
+spin_lock_t node2pagelock = SPIN_LOCK_INITIALIZER;
+
+spin_lock_t unlocked_pagein_lock = SPIN_LOCK_INITIALIZER;
+
+#ifdef DONT_CACHE_MEMORY_OBJECTS
+#define MAY_CACHE 0
+#else
+#define MAY_CACHE 1
+#endif
+
+struct port_bucket *pager_bucket;
+
+/* Mapped image of the disk. */
+void *disk_image;
+
+/* Find the location on disk of page OFFSET in pager UPI. Return the
+ disk address (in disk block) in *ADDR. If *NPLOCK is set on
+ return, then release that mutex after I/O on the data has
+ completed. Set DISKSIZE to be the amount of valid data on disk.
+ (If this is an unallocated block, then set *ADDR to zero.)
+ ISREAD is non-zero iff this is for a pagein. */
+static error_t
+find_address (struct user_pager_info *upi,
+ vm_address_t offset,
+ daddr_t *addr,
+ int *disksize,
+ struct rwlock **nplock,
+ int isread)
+{
+ error_t err;
+ struct rwlock *lock;
+
+ assert (upi->type == DISK || upi->type == FILE_DATA);
+
+ if (upi->type == DISK)
+ {
+ *disksize = __vm_page_size;
+ *addr = offset / DEV_BSIZE;
+ *nplock = 0;
+ return 0;
+ }
+ else
+ {
+ struct iblock_spec indirs[NIADDR + 1];
+ struct node *np;
+
+ np = upi->np;
+
+ if (isread)
+ {
+ try_again:
+
+ /* If we should allow an unlocked pagein, do so. (This
+ still has a slight race; there could be a pageout in progress
+ which is blocked on NP->np->allocptrlock itself. In that
+ case the pagein that should proceed unimpeded is blocked
+ in the pager library waiting for the pageout to complete.
+ I think this is sufficiently rare to put it off for the time
+ being.) */
+
+ spin_lock (&unlocked_pagein_lock);
+ if (offset >= upi->allow_unlocked_pagein
+ && (offset + vm_page_size
+ <= upi->allow_unlocked_pagein + upi->unlocked_pagein_length))
+ {
+ spin_unlock (&unlocked_pagein_lock);
+ *nplock = 0;
+ goto have_lock;
+ }
+ spin_unlock (&unlocked_pagein_lock);
+
+ /* Block on the rwlock if necessary; but when we wake up,
+ don't acquire it; check again from the top.
+ This is mutated inline from rwlock.h. */
+ lock = &np->dn->allocptrlock;
+ mutex_lock (&lock->master);
+ if (lock->readers == -1 || lock->writers_waiting)
+ {
+ lock->readers_waiting++;
+ condition_wait (&lock->wakeup, &lock->master);
+ lock->readers_waiting--;
+ mutex_unlock (&lock->master);
+ goto try_again;
+ }
+ lock->readers++;
+ mutex_unlock (&lock->master);
+ *nplock = lock;
+ }
+ else
+ {
+ rwlock_reader_lock (&np->dn->allocptrlock);
+ *nplock = &np->dn->allocptrlock;
+ }
+
+ have_lock:
+
+ if (offset >= np->allocsize)
+ {
+ if (*nplock)
+ rwlock_reader_unlock (*nplock);
+ if (isread)
+ return EIO;
+ else
+ {
+ *addr = 0;
+ *disksize = 0;
+ return 0;
+ }
+ }
+
+ if (offset + __vm_page_size > np->allocsize)
+ *disksize = np->allocsize - offset;
+ else
+ *disksize = __vm_page_size;
+
+ err = fetch_indir_spec (np, lblkno (sblock, offset), indirs);
+ if (err && *nplock)
+ rwlock_reader_unlock (*nplock);
+ else
+ {
+ if (indirs[0].bno)
+ *addr = (fsbtodb (sblock, indirs[0].bno)
+ + blkoff (sblock, offset) / DEV_BSIZE);
+ else
+ *addr = 0;
+ }
+
+ return err;
+ }
+}
+
+
+/* Implement the pager_read_page callback from the pager library. See
+ <hurd/pager.h> for the interface description. */
+error_t
+pager_read_page (struct user_pager_info *pager,
+ vm_offset_t page,
+ vm_address_t *buf,
+ int *writelock)
+{
+ error_t err;
+ struct rwlock *nplock;
+ daddr_t addr;
+ int disksize;
+
+ err = find_address (pager, page, &addr, &disksize, &nplock, 1);
+ if (err)
+ return err;
+
+ if (addr)
+ {
+ size_t read = 0;
+ err = store_read (store, addr << log2_dev_blocks_per_dev_bsize,
+ disksize, (void **)buf, &read);
+ if (read != disksize)
+ err = EIO;
+ if (!err && disksize != __vm_page_size)
+ bzero ((void *)(*buf + disksize), __vm_page_size - disksize);
+ *writelock = 0;
+ }
+ else
+ {
+#if 0
+ printf ("Write-locked pagein Object %#x\tOffset %#x\n", pager, page);
+ fflush (stdout);
+#endif
+ *buf = (vm_address_t) mmap (0, vm_page_size, PROT_READ|PROT_WRITE,
+ MAP_ANON, 0, 0);
+ *writelock = 1;
+ }
+
+ if (nplock)
+ rwlock_reader_unlock (nplock);
+
+ return err;
+}
+
+/* Implement the pager_write_page callback from the pager library. See
+ <hurd/pager.h> for the interface description. */
+error_t
+pager_write_page (struct user_pager_info *pager,
+ vm_offset_t page,
+ vm_address_t buf)
+{
+ daddr_t addr;
+ int disksize;
+ struct rwlock *nplock;
+ error_t err;
+
+ err = find_address (pager, page, &addr, &disksize, &nplock, 0);
+ if (err)
+ return err;
+
+ if (addr)
+ {
+ size_t wrote;
+ err = store_write (store, addr << log2_dev_blocks_per_dev_bsize,
+ (void *)buf, disksize, &wrote);
+ if (wrote != disksize)
+ err = EIO;
+ }
+ else
+ err = 0;
+
+ if (nplock)
+ rwlock_reader_unlock (nplock);
+
+ return err;
+}
+
+/* Implement the pager_unlock_page callback from the pager library. See
+ <hurd/pager.h> for the interface description. */
+error_t
+pager_unlock_page (struct user_pager_info *pager,
+ vm_offset_t address)
+{
+ struct node *np;
+ error_t err;
+ struct iblock_spec indirs[NIADDR + 1];
+ daddr_t bno;
+ struct disknode *dn;
+ struct dinode *di;
+
+ /* Zero an sblock->fs_bsize piece of disk starting at BNO,
+ synchronously. We do this on newly allocated indirect
+ blocks before setting the pointer to them to ensure that an
+ indirect block absolutely never points to garbage. */
+ void zero_disk_block (int bno)
+ {
+ bzero (indir_block (bno), sblock->fs_bsize);
+ sync_disk_blocks (bno, sblock->fs_bsize, 1);
+ };
+
+ /* Problem--where to get cred values for allocation here? */
+
+#if 0
+ printf ("Unlock page request, Object %#x\tOffset %#x...", pager, address);
+ fflush (stdout);
+#endif
+
+ if (pager->type == DISK)
+ return 0;
+
+ np = pager->np;
+ dn = np->dn;
+ di = dino (dn->number);
+
+ rwlock_writer_lock (&dn->allocptrlock);
+
+ /* If this is the last block, we don't let it get unlocked. */
+ if (address + __vm_page_size
+ > blkroundup (sblock, np->allocsize) - sblock->fs_bsize)
+ {
+ printf ("attempt to unlock at last block denied\n");
+ fflush (stdout);
+ rwlock_writer_unlock (&dn->allocptrlock);
+ return EIO;
+ }
+
+ err = fetch_indir_spec (np, lblkno (sblock, address), indirs);
+ if (err)
+ {
+ rwlock_writer_unlock (&dn->allocptrlock);
+ return EIO;
+ }
+
+ err = diskfs_catch_exception ();
+ if (err)
+ {
+ rwlock_writer_unlock (&dn->allocptrlock);
+ return EIO;
+ }
+
+ /* See if we need a triple indirect block; fail if we do. */
+ assert (indirs[0].offset == -1
+ || indirs[1].offset == -1
+ || indirs[2].offset == -1);
+
+ /* Check to see if this block is allocated. */
+ if (indirs[0].bno == 0)
+ {
+ size_t wrote;
+
+ if (indirs[0].offset == -1)
+ {
+ err = ffs_alloc (np, lblkno (sblock, address),
+ ffs_blkpref (np, lblkno (sblock, address),
+ lblkno (sblock, address), di->di_db),
+ sblock->fs_bsize, &bno, 0);
+ if (err)
+ goto out;
+
+ assert (lblkno (sblock, address) < NDADDR);
+ err = store_write (store,
+ fsbtodb (sblock, bno)
+ << log2_dev_blocks_per_dev_bsize,
+ zeroblock, sblock->fs_bsize, &wrote);
+ if (!err && wrote != sblock->fs_bsize)
+ err = EIO;
+ if (err)
+ goto out;
+
+ indirs[0].bno = bno;
+ write_disk_entry (di->di_db[lblkno (sblock, address)], bno);
+ record_poke (di, sizeof (struct dinode));
+ }
+ else
+ {
+ daddr_t *siblock;
+
+ /* We need to set siblock to the single indirect block
+ array; see if the single indirect block is allocated. */
+ if (indirs[1].bno == 0)
+ {
+ if (indirs[1].offset == -1)
+ {
+ err = ffs_alloc (np, lblkno (sblock, address),
+ ffs_blkpref (np, lblkno (sblock, address),
+ INDIR_SINGLE, di->di_ib),
+ sblock->fs_bsize, &bno, 0);
+ if (err)
+ goto out;
+ zero_disk_block (bno);
+ indirs[1].bno = bno;
+ write_disk_entry (di->di_ib[INDIR_SINGLE], bno);
+ record_poke (di, sizeof (struct dinode));
+ }
+ else
+ {
+ daddr_t *diblock;
+
+ /* We need to set diblock to the double indirect
+ block array; see if the double indirect block is
+ allocated. */
+ if (indirs[2].bno == 0)
+ {
+ /* This assert because triple indirection is
+ not supported. */
+ assert (indirs[2].offset == -1);
+
+ err = ffs_alloc (np, lblkno (sblock, address),
+ ffs_blkpref (np, lblkno (sblock,
+ address),
+ INDIR_DOUBLE, di->di_ib),
+ sblock->fs_bsize, &bno, 0);
+ if (err)
+ goto out;
+ zero_disk_block (bno);
+ indirs[2].bno = bno;
+ write_disk_entry (di->di_ib[INDIR_DOUBLE], bno);
+ record_poke (di, sizeof (struct dinode));
+ }
+
+ diblock = indir_block (indirs[2].bno);
+ mark_indir_dirty (np, indirs[2].bno);
+
+ /* Now we can allocate the single indirect block */
+
+ err = ffs_alloc (np, lblkno (sblock, address),
+ ffs_blkpref (np, lblkno (sblock, address),
+ indirs[1].offset, diblock),
+ sblock->fs_bsize, &bno, 0);
+ if (err)
+ goto out;
+ zero_disk_block (bno);
+ indirs[1].bno = bno;
+ write_disk_entry (diblock[indirs[1].offset], bno);
+ record_poke (diblock, sblock->fs_bsize);
+ }
+ }
+
+ siblock = indir_block (indirs[1].bno);
+ mark_indir_dirty (np, indirs[1].bno);
+
+ /* Now we can allocate the data block. */
+
+ err = ffs_alloc (np, lblkno (sblock, address),
+ ffs_blkpref (np, lblkno (sblock, address),
+ indirs[0].offset, siblock),
+ sblock->fs_bsize, &bno, 0);
+ if (err)
+ goto out;
+
+ err = store_write (store,
+ fsbtodb (sblock, bno)
+ << log2_dev_blocks_per_dev_bsize,
+ zeroblock, sblock->fs_bsize, &wrote);
+ if (!err && wrote != sblock->fs_bsize)
+ err = EIO;
+ if (err)
+ goto out;
+
+ indirs[0].bno = bno;
+ write_disk_entry (siblock[indirs[0].offset], bno);
+ record_poke (siblock, sblock->fs_bsize);
+ }
+ }
+
+ out:
+ diskfs_end_catch_exception ();
+ rwlock_writer_unlock (&dn->allocptrlock);
+ return err;
+}
+
+/* Implement the pager_report_extent callback from the pager library. See
+ <hurd/pager.h> for the interface description. */
+inline error_t
+pager_report_extent (struct user_pager_info *pager,
+ vm_address_t *offset,
+ vm_size_t *size)
+{
+ assert (pager->type == DISK || pager->type == FILE_DATA);
+
+ *offset = 0;
+
+ if (pager->type == DISK)
+ *size = store->size;
+ else
+ *size = pager->np->allocsize;
+
+ return 0;
+}
+
+/* Implement the pager_clear_user_data callback from the pager library.
+ See <hurd/pager.h> for the interface description. */
+void
+pager_clear_user_data (struct user_pager_info *upi)
+{
+ /* XXX Do the right thing for the disk pager here too. */
+ if (upi->type == FILE_DATA)
+ {
+ spin_lock (&node2pagelock);
+ if (upi->np->dn->fileinfo == upi)
+ upi->np->dn->fileinfo = 0;
+ spin_unlock (&node2pagelock);
+ diskfs_nrele_light (upi->np);
+ }
+ free (upi);
+}
+
+void
+pager_dropweak (struct user_pager_info *upi __attribute__ ((unused)))
+{
+}
+
+
+
+/* Create the DISK pager. */
+void
+create_disk_pager (void)
+{
+ struct user_pager_info *upi = malloc (sizeof (struct user_pager_info));
+
+ upi->type = DISK;
+ upi->np = 0;
+ pager_bucket = ports_create_bucket ();
+ diskfs_start_disk_pager (upi, pager_bucket, MAY_CACHE, store->size,
+ &disk_image);
+ upi->p = diskfs_disk_pager;
+}
+
+/* This syncs a single file (NP) to disk. Wait for all I/O to complete
+ if WAIT is set. NP->lock must be held. */
+void
+diskfs_file_update (struct node *np,
+ int wait)
+{
+ struct dirty_indir *d, *tmp;
+ struct user_pager_info *upi;
+
+ spin_lock (&node2pagelock);
+ upi = np->dn->fileinfo;
+ if (upi)
+ ports_port_ref (upi->p);
+ spin_unlock (&node2pagelock);
+
+ if (upi)
+ {
+ pager_sync (upi->p, wait);
+ ports_port_deref (upi->p);
+ }
+
+ for (d = np->dn->dirty; d; d = tmp)
+ {
+ sync_disk_blocks (d->bno, sblock->fs_bsize, wait);
+ tmp = d->next;
+ free (d);
+ }
+ np->dn->dirty = 0;
+
+ diskfs_node_update (np, wait);
+}
+
+/* Invalidate any pager data associated with NODE. */
+void
+flush_node_pager (struct node *node)
+{
+ struct user_pager_info *upi;
+ struct disknode *dn = node->dn;
+ struct dirty_indir *dirty = dn->dirty;
+
+ spin_lock (&node2pagelock);
+ upi = dn->fileinfo;
+ if (upi)
+ ports_port_ref (upi->p);
+ spin_unlock (&node2pagelock);
+
+ if (upi)
+ {
+ pager_flush (upi->p, 1);
+ ports_port_deref (upi->p);
+ }
+
+ dn->dirty = 0;
+
+ while (dirty)
+ {
+ struct dirty_indir *next = dirty->next;
+ free (dirty);
+ dirty = next;
+ }
+}
+
+/* Call this to create a FILE_DATA pager and return a send right.
+ NP must be locked. PROT is the max protection desired. */
+mach_port_t
+diskfs_get_filemap (struct node *np, vm_prot_t prot)
+{
+ struct user_pager_info *upi;
+ mach_port_t right;
+
+ assert (S_ISDIR (np->dn_stat.st_mode)
+ || S_ISREG (np->dn_stat.st_mode)
+ || (S_ISLNK (np->dn_stat.st_mode)
+ && (!direct_symlink_extension
+ || np->dn_stat.st_size >= sblock->fs_maxsymlinklen)));
+
+ spin_lock (&node2pagelock);
+ do
+ if (!np->dn->fileinfo)
+ {
+ upi = malloc (sizeof (struct user_pager_info));
+ upi->type = FILE_DATA;
+ upi->np = np;
+ upi->max_prot = prot;
+ upi->allow_unlocked_pagein = 0;
+ upi->unlocked_pagein_length = 0;
+ diskfs_nref_light (np);
+ upi->p = pager_create (upi, pager_bucket,
+ MAY_CACHE, MEMORY_OBJECT_COPY_DELAY);
+ if (upi->p == 0)
+ {
+ diskfs_nrele_light (np);
+ free (upi);
+ spin_unlock (&node2pagelock);
+ return MACH_PORT_NULL;
+ }
+ np->dn->fileinfo = upi;
+ right = pager_get_port (np->dn->fileinfo->p);
+ ports_port_deref (np->dn->fileinfo->p);
+ }
+ else
+ {
+ np->dn->fileinfo->max_prot |= prot;
+
+ /* Because NP->dn->fileinfo->p is not a real reference,
+ this might be nearly deallocated. If that's so, then
+ the port right will be null. In that case, clear here
+ and loop. The deallocation will complete separately. */
+ right = pager_get_port (np->dn->fileinfo->p);
+ if (right == MACH_PORT_NULL)
+ np->dn->fileinfo = 0;
+ }
+ while (right == MACH_PORT_NULL);
+
+ spin_unlock (&node2pagelock);
+
+ mach_port_insert_right (mach_task_self (), right, right,
+ MACH_MSG_TYPE_MAKE_SEND);
+
+ return right;
+}
+
+/* Call this when we should turn off caching so that unused memory object
+ ports get freed. */
+void
+drop_pager_softrefs (struct node *np)
+{
+ struct user_pager_info *upi;
+
+ spin_lock (&node2pagelock);
+ upi = np->dn->fileinfo;
+ if (upi)
+ ports_port_ref (upi->p);
+ spin_unlock (&node2pagelock);
+
+ if (MAY_CACHE && upi)
+ pager_change_attributes (upi->p, 0, MEMORY_OBJECT_COPY_DELAY, 0);
+ if (upi)
+ ports_port_deref (upi->p);
+}
+
+/* Call this when we should turn on caching because it's no longer
+ important for unused memory object ports to get freed. */
+void
+allow_pager_softrefs (struct node *np)
+{
+ struct user_pager_info *upi;
+
+ spin_lock (&node2pagelock);
+ upi = np->dn->fileinfo;
+ if (upi)
+ ports_port_ref (upi->p);
+ spin_unlock (&node2pagelock);
+
+ if (MAY_CACHE && upi)
+ pager_change_attributes (upi->p, 1, MEMORY_OBJECT_COPY_DELAY, 0);
+ if (upi)
+ ports_port_deref (upi->p);
+}
+
+static void
+block_caching ()
+{
+ error_t block_cache (void *arg)
+ {
+ struct pager *p = arg;
+
+ pager_change_attributes (p, 0, MEMORY_OBJECT_COPY_DELAY, 1);
+ return 0;
+ }
+
+ /* Loop through the pagers and turn off caching one by one,
+ synchronously. That should cause termination of each pager. */
+ ports_bucket_iterate (pager_bucket, block_cache);
+}
+
+static void
+enable_caching ()
+{
+ error_t enable_cache (void *arg)
+ {
+ struct pager *p = arg;
+ struct user_pager_info *upi = pager_get_upi (p);
+
+ pager_change_attributes (p, 1, MEMORY_OBJECT_COPY_DELAY, 0);
+
+ /* It's possible that we didn't have caching on before, because
+ the user here is the only reference to the underlying node
+ (actually, that's quite likely inside this particular
+ routine), and if that node has no links. So dinkle the node
+ ref counting scheme here, which will cause caching to be
+ turned off, if that's really necessary. */
+ if (upi->type == FILE_DATA)
+ {
+ diskfs_nref (upi->np);
+ diskfs_nrele (upi->np);
+ }
+
+ return 0;
+ }
+
+ ports_bucket_iterate (pager_bucket, enable_cache);
+}
+
+/* Tell diskfs if there are pagers exported, and if none, then
+ prevent any new ones from showing up. */
+int
+diskfs_pager_users ()
+{
+ int npagers = ports_count_bucket (pager_bucket);
+
+ if (npagers <= 1)
+ return 0;
+
+ if (MAY_CACHE)
+ {
+ block_caching ();
+
+ /* Give it a second; the kernel doesn't actually shutdown
+ immediately. XXX */
+ sleep (1);
+
+ npagers = ports_count_bucket (pager_bucket);
+ if (npagers <= 1)
+ return 0;
+
+ /* Darn, there are actual honest users. Turn caching back on,
+ and return failure. */
+ enable_caching ();
+ }
+
+ ports_enable_bucket (pager_bucket);
+
+ return 1;
+}
+
+/* Return the bitwise or of the maximum prot parameter (the second arg to
+ diskfs_get_filemap) for all active user pagers. */
+vm_prot_t
+diskfs_max_user_pager_prot ()
+{
+ vm_prot_t max_prot = 0;
+ int npagers = ports_count_bucket (pager_bucket);
+
+ if (npagers > 1)
+ /* More than just the disk pager. */
+ {
+ error_t add_pager_max_prot (void *v_p)
+ {
+ struct pager *p = v_p;
+ struct user_pager_info *upi = pager_get_upi (p);
+ if (upi->type == FILE_DATA)
+ max_prot |= upi->max_prot;
+ /* Stop iterating if MAX_PROT is as filled as it's going to get. */
+ return max_prot == (VM_PROT_READ|VM_PROT_WRITE|VM_PROT_EXECUTE);
+ }
+
+ block_caching (); /* Make any silly pagers go away. */
+
+ /* Give it a second; the kernel doesn't actually shutdown
+ immediately. XXX */
+ sleep (1);
+
+ ports_bucket_iterate (pager_bucket, add_pager_max_prot);
+
+ enable_caching ();
+ }
+
+ ports_enable_bucket (pager_bucket);
+
+ return max_prot;
+}
+
+/* Call this to find out the struct pager * corresponding to the
+ FILE_DATA pager of inode IP. This should be used *only* as a subsequent
+ argument to register_memory_fault_area, and will be deleted when
+ the kernel interface is fixed. NP must be locked. */
+struct pager *
+diskfs_get_filemap_pager_struct (struct node *np)
+{
+ /* This is safe because fileinfo can't be cleared; there must be
+ an active mapping for this to be called. */
+ return np->dn->fileinfo->p;
+}
+
+/* Shutdown all the pagers. */
+void
+diskfs_shutdown_pager ()
+{
+ error_t shutdown_one (void *arg)
+ {
+ struct pager *p = arg;
+ /* Don't ever shut down the disk pager. */
+ if (p != diskfs_disk_pager)
+ pager_shutdown (p);
+ return 0;
+ }
+
+ copy_sblock ();
+ write_all_disknodes ();
+ ports_bucket_iterate (pager_bucket, shutdown_one);
+ sync_disk (1);
+}
+
+/* Sync all the pagers. */
+void
+diskfs_sync_everything (int wait)
+{
+ error_t sync_one (void *arg)
+ {
+ struct pager *p = arg;
+ /* Make sure the disk pager is done last. */
+ if (p != diskfs_disk_pager)
+ pager_sync (p, wait);
+ return 0;
+ }
+
+ copy_sblock ();
+ write_all_disknodes ();
+ ports_bucket_iterate (pager_bucket, sync_one);
+ sync_disk (wait);
+}
diff --git a/ufs/pokeloc.c b/ufs/pokeloc.c
new file mode 100644
index 00000000..267aa106
--- /dev/null
+++ b/ufs/pokeloc.c
@@ -0,0 +1,85 @@
+/* Remember where we've written the disk to speed up sync
+ Copyright (C) 1994, 1996 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "ufs.h"
+
+struct pokeloc
+{
+ vm_offset_t offset;
+ vm_size_t length;
+ struct pokeloc *next;
+};
+
+struct pokeloc *pokelist;
+spin_lock_t pokelistlock = SPIN_LOCK_INITIALIZER;
+
+/* Remember that data here on the disk has been modified. */
+void
+record_poke (void *loc, vm_size_t length)
+{
+ struct pokeloc *pl = malloc (sizeof (struct pokeloc));
+ vm_offset_t offset;
+
+ offset = loc - disk_image;
+ pl->offset = trunc_page (offset);
+ pl->length = round_page (offset + length) - pl->offset;
+
+ spin_lock (&pokelistlock);
+ pl->next = pokelist;
+ pokelist = pl;
+ spin_unlock (&pokelistlock);
+}
+
+/* Get rid of any outstanding pokes. */
+void
+flush_pokes ()
+{
+ struct pokeloc *pl;
+
+ spin_lock (&pokelistlock);
+ pl = pokelist;
+ pokelist = 0;
+ spin_unlock (&pokelistlock);
+
+ while (pl)
+ {
+ struct pokeloc *next = pl->next;
+ free (pl);
+ pl = next;
+ }
+}
+
+/* Sync all the modified pieces of disk */
+void
+sync_disk (int wait)
+{
+ struct pokeloc *pl, *tmp;
+
+ spin_lock (&pokelistlock);
+ for (pl = pokelist; pl; pl = tmp)
+ {
+ pager_sync_some (diskfs_disk_pager, pl->offset, pl->length, wait);
+ tmp = pl->next;
+ free (pl);
+ }
+ pokelist = 0;
+ spin_unlock (&pokelistlock);
+}
+
diff --git a/ufs/sizes.c b/ufs/sizes.c
new file mode 100644
index 00000000..58cbfc98
--- /dev/null
+++ b/ufs/sizes.c
@@ -0,0 +1,719 @@
+/* File growth and truncation
+ Copyright (C) 1993, 1994, 1995, 1996, 1997, 1999 Free Software Foundation
+
+This file is part of the GNU Hurd.
+
+The GNU Hurd is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+The GNU Hurd is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Written by Michael I. Bushnell. */
+
+#include "ufs.h"
+#include <string.h>
+
+#ifdef DONT_CACHE_MEMORY_OBJECTS
+#define MAY_CACHE 0
+#else
+#define MAY_CACHE 1
+#endif
+
+static int indir_release (struct node *np, daddr_t bno, int level);
+static void poke_pages (memory_object_t, vm_offset_t, vm_offset_t);
+
+/* Implement the diskfs_truncate callback; sse <hurd/diskfs.h> for the
+ interface description. */
+error_t
+diskfs_truncate (struct node *np,
+ off_t length)
+{
+ int offset;
+ struct dinode *di = dino (np->dn->number);
+ volatile int blocksfreed = 0;
+ error_t err;
+ int i;
+ struct iblock_spec indirs[NIADDR + 1];
+ volatile daddr_t lbn;
+ struct user_pager_info *upi;
+
+ if (length >= np->dn_stat.st_size)
+ return 0;
+
+ diskfs_check_readonly ();
+ assert (!diskfs_readonly);
+
+ /* First check to see if this is a kludged symlink; if so
+ this is special. */
+ if (direct_symlink_extension && S_ISLNK (np->dn_stat.st_mode)
+ && np->dn_stat.st_size < sblock->fs_maxsymlinklen)
+ {
+ error_t err;
+
+ err = diskfs_catch_exception ();
+ if (err)
+ return err;
+ bzero ((char *)di->di_shortlink + length, np->dn_stat.st_size - length);
+ record_poke (di, sizeof (struct dinode));
+ diskfs_end_catch_exception ();
+ np->dn_stat.st_size = length;
+ np->dn_set_ctime = np->dn_set_mtime = 1;
+ diskfs_node_update (np, 1);
+ return 0;
+ }
+
+ /* If the file is not being trucated to a block boundary,
+ the zero the partial bit in the new last block. */
+ offset = blkoff (sblock, length);
+ if (offset)
+ {
+ int bsize; /* size of new last block */
+ int savesize = np->allocsize;
+
+ np->allocsize = length; /* temporary */
+ bsize = blksize (sblock, np, lblkno (sblock, length));
+ np->allocsize = savesize;
+ diskfs_node_rdwr (np, zeroblock, length, bsize - offset, 1, 0, 0);
+ diskfs_file_update (np, 1);
+ }
+
+ /* Now flush all the data past the new size from the kernel.
+ Also force any delayed copies of this data to take place
+ immediately. (We are implicitly changing the data to zeros
+ and doing it without the kernel's immediate knowledge;
+ accordingl we must help out the kernel thusly.) */
+ spin_lock (&node2pagelock);
+ upi = np->dn->fileinfo;
+ if (upi)
+ ports_port_ref (upi->p);
+ spin_unlock (&node2pagelock);
+
+ if (upi)
+ {
+ mach_port_t obj;
+
+ pager_change_attributes (upi->p, MAY_CACHE,
+ MEMORY_OBJECT_COPY_NONE, 1);
+ obj = diskfs_get_filemap (np, VM_PROT_READ | VM_PROT_WRITE);
+ if (obj != MACH_PORT_NULL)
+ {
+ /* XXX should cope with errors from diskfs_get_filemap */
+ poke_pages (obj, round_page (length), round_page (np->allocsize));
+ mach_port_deallocate (mach_task_self (), obj);
+ pager_flush_some (upi->p, round_page (length),
+ np->allocsize - length, 1);
+ }
+ ports_port_deref (upi->p);
+ }
+
+ rwlock_writer_lock (&np->dn->allocptrlock);
+
+ /* Update the size on disk; fsck will finish freeing blocks if necessary
+ should we crash. */
+ np->dn_stat.st_size = length;
+ np->dn_set_mtime = 1;
+ np->dn_set_ctime = 1;
+ diskfs_node_update (np, 1);
+
+ /* Find out the location information for the last block to
+ be retained */
+ lbn = lblkno (sblock, length - 1);
+ err = fetch_indir_spec (np, lbn, indirs);
+ /* err XXX */
+
+ /* We don't support triple indirs */
+ assert (indirs[3].offset == -2);
+
+ err = diskfs_catch_exception ();
+ /* err XXX */
+
+ /* BSD carefully finds out how far to clear; it's vastly simpler
+ to just clear everything after the new last block. */
+
+ /* Free direct blocks */
+ if (indirs[0].offset < 0)
+ {
+ /* ...mapped from the inode. */
+ for (i = lbn + 1; i < NDADDR; i++)
+ if (di->di_db[i])
+ {
+ long bsize = blksize (sblock, np, i);
+ ffs_blkfree (np, read_disk_entry (di->di_db[i]), bsize);
+ di->di_db[i] = 0;
+ blocksfreed += btodb (bsize);
+ }
+ }
+ else
+ {
+ /* ... or mapped from sindir */
+ if (indirs[1].bno)
+ {
+ daddr_t *sindir = indir_block (indirs[1].bno);
+ for (i = indirs[0].offset + 1; i < NINDIR (sblock); i++)
+ if (sindir[i])
+ {
+ ffs_blkfree (np, read_disk_entry (sindir[i]),
+ sblock->fs_bsize);
+ sindir[i] = 0;
+ blocksfreed += btodb (sblock->fs_bsize);
+ }
+ record_poke (sindir, sblock->fs_bsize);
+ }
+ }
+
+ /* Free single indirect blocks */
+ if (indirs[1].offset < 0)
+ {
+ /* ...mapped from the inode */
+ if (di->di_ib[INDIR_SINGLE] && indirs[1].offset == -2)
+ {
+ blocksfreed += indir_release (np,
+ read_disk_entry (di->di_ib
+ [INDIR_SINGLE]),
+ INDIR_SINGLE);
+ di->di_ib[INDIR_SINGLE] = 0;
+ }
+ }
+ else
+ {
+ /* ...or mapped from dindir */
+ if (indirs[2].bno)
+ {
+ daddr_t *dindir = indir_block (indirs[2].bno);
+ for (i = indirs[1].offset + 1; i < NINDIR (sblock); i++)
+ if (dindir[i])
+ {
+ blocksfreed += indir_release (np,
+ read_disk_entry (dindir[i]),
+ INDIR_SINGLE);
+ dindir[i] = 0;
+ }
+ record_poke (dindir, sblock->fs_bsize);
+ }
+ }
+
+ /* Free double indirect block */
+ assert (indirs[2].offset < 0); /* which must be mapped from the inode */
+ if (indirs[2].offset == -2)
+ {
+ if (di->di_ib[INDIR_DOUBLE])
+ {
+ blocksfreed += indir_release (np,
+ read_disk_entry (di->di_ib
+ [INDIR_DOUBLE]),
+ INDIR_DOUBLE);
+ di->di_ib[INDIR_DOUBLE] = 0;
+ }
+ }
+
+ /* Finally, check to see if the new last direct block is
+ changing size; if so release any frags necessary. */
+ if (lbn >= 0 && lbn < NDADDR && di->di_db[lbn])
+ {
+ long oldspace, newspace;
+ daddr_t bn;
+
+ bn = read_disk_entry (di->di_db[lbn]);
+ oldspace = blksize (sblock, np, lbn);
+ np->allocsize = fragroundup (sblock, length);
+ newspace = blksize (sblock, np, lbn);
+
+ assert (newspace);
+
+ if (oldspace - newspace)
+ {
+ bn += numfrags (sblock, newspace);
+ ffs_blkfree (np, bn, oldspace - newspace);
+ blocksfreed += btodb (oldspace - newspace);
+ }
+ }
+ else
+ {
+ if (lbn > NDADDR)
+ np->allocsize = blkroundup (sblock, length);
+ else
+ np->allocsize = fragroundup (sblock, length);
+ }
+
+ record_poke (di, sizeof (struct dinode));
+
+ np->dn_stat.st_blocks -= blocksfreed;
+ np->dn_set_ctime = 1;
+ diskfs_node_update (np, 1);
+
+ rwlock_writer_unlock (&np->dn->allocptrlock);
+
+ /* At this point the last block (as defined by np->allocsize)
+ might not be allocated. We need to allocate it to maintain
+ the rule that the last block of a file is always allocated. */
+
+ if (np->allocsize && indirs[0].bno == 0)
+ {
+ /* The strategy is to reduce LBN until we get one that's allocated;
+ then reduce allocsize accordingly, then call diskfs_grow. */
+
+ do
+ err = fetch_indir_spec (np, --lbn, indirs);
+ /* err XXX */
+ while (indirs[0].bno == 0 && lbn >= 0);
+
+ assert ((lbn + 1) * sblock->fs_bsize < np->allocsize);
+ np->allocsize = (lbn + 1) * sblock->fs_bsize;
+
+ diskfs_grow (np, length, 0);
+ }
+
+ diskfs_end_catch_exception ();
+
+ /* Now we can permit delayed copies again. */
+ spin_lock (&node2pagelock);
+ upi = np->dn->fileinfo;
+ if (upi)
+ ports_port_ref (upi->p);
+ spin_unlock (&node2pagelock);
+ if (upi)
+ {
+ pager_change_attributes (upi->p, MAY_CACHE,
+ MEMORY_OBJECT_COPY_DELAY, 0);
+ ports_port_deref (upi->p);
+ }
+
+ return err;
+}
+
+/* Free indirect block BNO of level LEVEL; recursing if necessary
+ to free other indirect blocks. Return the number of disk
+ blocks freed. */
+static int
+indir_release (struct node *np, daddr_t bno, int level)
+{
+ int count = 0;
+ daddr_t *addrs;
+ int i;
+ struct dirty_indir *d, *prev, *next;
+
+ assert (bno);
+
+ addrs = indir_block (bno);
+ for (i = 0; i < NINDIR (sblock); i++)
+ if (addrs[i])
+ {
+ if (level == INDIR_SINGLE)
+ {
+ ffs_blkfree (np, read_disk_entry (addrs[i]), sblock->fs_bsize);
+ count += btodb (sblock->fs_bsize);
+ }
+ else
+ count += indir_release (np, read_disk_entry (addrs[i]), level - 1);
+ }
+
+ /* Subtlety: this block is no longer necessary; the information
+ the kernel has cached corresponding to ADDRS is now unimportant.
+ Consider that if this block is allocated to a file, it will then
+ be double cached and the kernel might decide to write out
+ the disk_image version of the block. So we have to flush
+ the block from the kernel's memory, making sure we do it
+ synchronously--and BEFORE we attach it to the free list
+ with ffs_blkfree. */
+ pager_flush_some (diskfs_disk_pager, fsaddr (sblock, bno), sblock->fs_bsize, 1);
+
+ /* We should also take this block off the inode's list of
+ dirty indirect blocks if it's there. */
+ prev = 0;
+ d = np->dn->dirty;
+ while (d)
+ {
+ next = d->next;
+ if (d->bno == bno)
+ {
+ if (prev)
+ prev->next = next;
+ else
+ np->dn->dirty = next;
+ free (d);
+ }
+ else
+ {
+ prev = d;
+ next = d->next;
+ }
+ d = next;
+ }
+
+ /* Free designated block */
+ ffs_blkfree (np, bno, sblock->fs_bsize);
+ count += btodb (sblock->fs_bsize);
+
+ return count;
+}
+
+
+/* Offer data at BUF from START of LEN bytes of file NP. */
+void
+offer_data (struct node *np,
+ off_t start,
+ size_t len,
+ vm_address_t buf)
+{
+ vm_address_t addr;
+
+ len = round_page (len);
+
+ assert (start % vm_page_size == 0);
+
+ assert (np->dn->fileinfo);
+ for (addr = start; addr < start + len; addr += vm_page_size)
+ pager_offer_page (np->dn->fileinfo->p, 1, 0, addr, buf + (addr - start));
+}
+
+/* Logical block LBN of node NP has been extended with ffs_realloccg.
+ It used to be allocated at OLD_PBN and is now at NEW_PBN. The old
+ size was OLD_SIZE; it is now NEW_SIZE bytes long. Arrange for the data
+ on disk to be kept consistent, and free the old block if it has moved.
+ Return one iff we've actually moved data around on disk. */
+int
+block_extended (struct node *np,
+ daddr_t lbn,
+ daddr_t old_pbn,
+ daddr_t new_pbn,
+ size_t old_size,
+ size_t new_size)
+{
+ /* Make sure that any pages of this block which just became allocated
+ don't get paged in from disk. */
+ if (round_page (old_size) < round_page (new_size))
+ offer_data (np, lbn * sblock->fs_bsize + round_page (old_size),
+ round_page (new_size) - round_page (old_size),
+ (vm_address_t)zeroblock);
+
+ if (old_pbn != new_pbn)
+ {
+ memory_object_t mapobj;
+ error_t err;
+ vm_address_t mapaddr;
+ volatile int *pokeaddr;
+
+ /* Map in this part of the file */
+ mapobj = diskfs_get_filemap (np, VM_PROT_WRITE | VM_PROT_READ);
+
+ /* XXX Should cope with errors from diskfs_get_filemap and back
+ out the operation here. */
+ assert (mapobj);
+
+ err = vm_map (mach_task_self (), &mapaddr, round_page (old_size), 0, 1,
+ mapobj, lbn * sblock->fs_bsize, 0,
+ VM_PROT_READ|VM_PROT_WRITE, VM_PROT_READ|VM_PROT_WRITE, 0);
+ assert_perror (err);
+
+ /* Allow these pageins to occur even though we're holding the lock */
+ spin_lock (&unlocked_pagein_lock);
+ np->dn->fileinfo->allow_unlocked_pagein = lbn * sblock->fs_bsize;
+ np->dn->fileinfo->unlocked_pagein_length = round_page (old_size);
+ spin_unlock (&unlocked_pagein_lock);
+
+ /* Make sure all waiting pageins see this change. */
+ mutex_lock (&np->dn->allocptrlock.master);
+ condition_broadcast (&np->dn->allocptrlock.wakeup);
+ mutex_unlock (&np->dn->allocptrlock.master);
+
+ /* Force the pages in core and make sure they are dirty */
+ for (pokeaddr = (int *)mapaddr;
+ pokeaddr < (int *) (mapaddr + round_page (old_size));
+ pokeaddr += vm_page_size / sizeof (*pokeaddr))
+ *pokeaddr = *pokeaddr;
+
+ /* Turn off the special pagein permission */
+ spin_lock (&unlocked_pagein_lock);
+ np->dn->fileinfo->allow_unlocked_pagein = 0;
+ np->dn->fileinfo->unlocked_pagein_length = 0;
+ spin_unlock (&unlocked_pagein_lock);
+
+ /* Undo mapping */
+ mach_port_deallocate (mach_task_self (), mapobj);
+ munmap ((caddr_t) mapaddr, round_page (old_size));
+
+ /* Now it's OK to free the old block */
+ ffs_blkfree (np, old_pbn, old_size);
+
+ /* Tell caller that we've moved data */
+ return 1;
+ }
+ else
+ return 0;
+}
+
+
+/* Implement the diskfs_grow callback; see <hurd/diskfs.h> for the
+ interface description. */
+error_t
+diskfs_grow (struct node *np,
+ off_t end,
+ struct protid *cred)
+{
+ daddr_t lbn, olbn;
+ int size, osize;
+ error_t err;
+ struct dinode *di = dino (np->dn->number);
+ mach_port_t pagerpt;
+ int need_sync = 0;
+
+ /* Zero an sblock->fs_bsize piece of disk starting at BNO,
+ synchronously. We do this on newly allocated indirect
+ blocks before setting the pointer to them to ensure that an
+ indirect block absolutely never points to garbage. */
+ void zero_disk_block (int bno)
+ {
+ bzero (indir_block (bno), sblock->fs_bsize);
+ sync_disk_blocks (bno, sblock->fs_bsize, 1);
+ };
+
+ /* Check to see if we don't actually have to do anything */
+ if (end <= np->allocsize)
+ return 0;
+
+ diskfs_check_readonly ();
+ assert (!diskfs_readonly);
+
+ /* This reference will ensure that NP->dn->fileinfo stays allocated. */
+ pagerpt = diskfs_get_filemap (np, VM_PROT_WRITE|VM_PROT_READ);
+
+ if (pagerpt == MACH_PORT_NULL)
+ return errno;
+
+ /* The new last block of the file. */
+ lbn = lblkno (sblock, end - 1);
+
+ /* This is the size of that block if it is in the NDADDR array. */
+ size = fragroundup (sblock, blkoff (sblock, end));
+ if (size == 0)
+ size = sblock->fs_bsize;
+
+ rwlock_writer_lock (&np->dn->allocptrlock);
+
+ /* The old last block of the file. */
+ olbn = lblkno (sblock, np->allocsize - 1);
+
+ /* This is the size of that block if it is in the NDADDR array. */
+ osize = fragroundup (sblock, blkoff (sblock, np->allocsize));
+ if (osize == 0)
+ osize = sblock->fs_bsize;
+
+ /* If this end point is a new block and the file currently
+ has a fragment, then expand the fragment to a full block. */
+ if (np->allocsize && olbn < NDADDR && olbn < lbn)
+ {
+ if (osize < sblock->fs_bsize)
+ {
+ daddr_t old_pbn, bno;
+ err = ffs_realloccg (np, olbn,
+ ffs_blkpref (np, lbn, lbn, di->di_db),
+ osize, sblock->fs_bsize, &bno, cred);
+ if (err)
+ goto out;
+
+ old_pbn = read_disk_entry (di->di_db[olbn]);
+
+ need_sync = block_extended (np, olbn, old_pbn, bno,
+ osize, sblock->fs_bsize);
+
+ write_disk_entry (di->di_db[olbn], bno);
+ record_poke (di, sizeof (struct dinode));
+ np->dn_set_ctime = 1;
+ }
+ }
+
+ if (lbn < NDADDR)
+ {
+ daddr_t bno, old_pbn = read_disk_entry (di->di_db[lbn]);
+
+ if (old_pbn != 0)
+ {
+ /* The last block is already allocated. Therefore we
+ must be expanding the fragment. Make sure that's really
+ what we're up to. */
+ assert (size > osize);
+ assert (lbn == olbn);
+
+ err = ffs_realloccg (np, lbn,
+ ffs_blkpref (np, lbn, lbn, di->di_db),
+ osize, size, &bno, cred);
+ if (err)
+ goto out;
+
+ need_sync = block_extended (np, lbn, old_pbn, bno, osize, size);
+
+ write_disk_entry (di->di_db[lbn], bno);
+ record_poke (di, sizeof (struct dinode));
+ np->dn_set_ctime = 1;
+ }
+ else
+ {
+ /* Allocate a new last block. */
+ err = ffs_alloc (np, lbn,
+ ffs_blkpref (np, lbn, lbn, di->di_db),
+ size, &bno, cred);
+ if (err)
+ goto out;
+
+
+ offer_data (np, lbn * sblock->fs_bsize, size,
+ (vm_address_t)zeroblock);
+ write_disk_entry (di->di_db[lbn], bno);
+ record_poke (di, sizeof (struct dinode));
+ np->dn_set_ctime = 1;
+ }
+ }
+ else
+ {
+ struct iblock_spec indirs[NIADDR + 1];
+ daddr_t *siblock;
+ daddr_t bno;
+
+ /* Count the number of levels of indirection. */
+ err = fetch_indir_spec (np, lbn, indirs);
+ if (err)
+ goto out;
+
+ /* Make sure we didn't miss the NDADDR case
+ above somehow. */
+ assert (indirs[0].offset != -1);
+
+ /* See if we need a triple indirect block; fail if so. */
+ assert (indirs[1].offset == -1 || indirs[2].offset == -1);
+
+ /* Check to see if this block is allocated. If it is
+ that's an error. */
+ assert (indirs[0].bno == 0);
+
+ /* We need to set SIBLOCK to the single indirect block
+ array; see if the single indirect block is allocated. */
+ if (indirs[1].bno == 0)
+ {
+ /* Allocate it. */
+ if (indirs[1].offset == -1)
+ {
+ err = ffs_alloc (np, lbn,
+ ffs_blkpref (np, lbn, INDIR_SINGLE, di->di_ib),
+ sblock->fs_bsize, &bno, 0);
+ if (err)
+ goto out;
+ zero_disk_block (bno);
+ indirs[1].bno = bno;
+ write_disk_entry (di->di_ib[INDIR_SINGLE], bno);
+ record_poke (di, sizeof (struct dinode));
+ }
+ else
+ {
+ daddr_t *diblock;
+
+ /* We need to set diblock to the double indirect block
+ array; see if the double indirect block is allocated. */
+ if (indirs[2].bno == 0)
+ {
+ /* This assert because triple indirection is not
+ supported. */
+ assert (indirs[2].offset == -1);
+ err = ffs_alloc (np, lbn,
+ ffs_blkpref (np, lbn,
+ INDIR_DOUBLE, di->di_ib),
+ sblock->fs_bsize, &bno, 0);
+ if (err)
+ goto out;
+ zero_disk_block (bno);
+ indirs[2].bno = bno;
+ write_disk_entry (di->di_ib[INDIR_DOUBLE], bno);
+ record_poke (di, sizeof (struct dinode));
+ }
+
+ diblock = indir_block (indirs[2].bno);
+ mark_indir_dirty (np, indirs[2].bno);
+
+ /* Now we can allocate the single indirect block */
+ err = ffs_alloc (np, lbn,
+ ffs_blkpref (np, lbn,
+ indirs[1].offset, diblock),
+ sblock->fs_bsize, &bno, 0);
+ if (err)
+ goto out;
+ zero_disk_block (bno);
+ indirs[1].bno = bno;
+ write_disk_entry (diblock[indirs[1].offset], bno);
+ record_poke (diblock, sblock->fs_bsize);
+ }
+ }
+
+ siblock = indir_block (indirs[1].bno);
+ mark_indir_dirty (np, indirs[1].bno);
+
+ /* Now we can allocate the data block. */
+ err = ffs_alloc (np, lbn,
+ ffs_blkpref (np, lbn, indirs[0].offset, siblock),
+ sblock->fs_bsize, &bno, 0);
+ if (err)
+ goto out;
+ offer_data (np, lbn * sblock->fs_bsize, sblock->fs_bsize,
+ (vm_address_t)zeroblock);
+ indirs[0].bno = bno;
+ write_disk_entry (siblock[indirs[0].offset], bno);
+ record_poke (siblock, sblock->fs_bsize);
+ }
+
+ out:
+ mach_port_deallocate (mach_task_self (), pagerpt);
+ if (!err)
+ {
+ int newallocsize;
+ if (lbn < NDADDR)
+ newallocsize = lbn * sblock->fs_bsize + size;
+ else
+ newallocsize = (lbn + 1) * sblock->fs_bsize;
+ assert (newallocsize > np->allocsize);
+ np->allocsize = newallocsize;
+ }
+
+ rwlock_writer_unlock (&np->dn->allocptrlock);
+
+ if (need_sync)
+ diskfs_file_update (np, 1);
+
+ return err;
+}
+
+/* Write something to each page from START to END inclusive of memory
+ object OBJ, but make sure the data doesns't actually change. */
+static void
+poke_pages (memory_object_t obj,
+ vm_offset_t start,
+ vm_offset_t end)
+{
+ vm_address_t addr, poke;
+ vm_size_t len;
+ error_t err;
+
+ while (start < end)
+ {
+ len = 8 * vm_page_size;
+ if (len > end - start)
+ len = end - start;
+ addr = 0;
+ err = vm_map (mach_task_self (), &addr, len, 0, 1, obj, start, 0,
+ VM_PROT_WRITE|VM_PROT_READ, VM_PROT_READ|VM_PROT_WRITE, 0);
+ if (!err)
+ {
+ for (poke = addr; poke < addr + len; poke += vm_page_size)
+ *(volatile int *)poke = *(volatile int *)poke;
+ munmap ((caddr_t) addr, len);
+ }
+ start += len;
+ }
+}
+
diff --git a/ufs/subr.c b/ufs/subr.c
new file mode 100644
index 00000000..2b356ddc
--- /dev/null
+++ b/ufs/subr.c
@@ -0,0 +1,264 @@
+/*
+ * 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. 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.
+ *
+ * @(#)ffs_subr.c 8.2 (Berkeley) 9/21/93
+ */
+
+#include "ufs.h"
+
+#if 0 /* Not needed in GNU Hurd ufs. */
+/*
+ * 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(ap)
+ struct vop_blkatoff_args /* {
+ struct vnode *a_vp;
+ off_t a_offset;
+ char **a_res;
+ struct buf **a_bpp;
+ } */ *ap;
+{
+ struct inode *ip;
+ register 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)) {
+ 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);
+}
+#endif /* 0 */
+
+/*
+ * Update the frsum fields to reflect addition or deletion
+ * of some frags.
+ */
+void
+ffs_fragacct(fs, fragmap, fraglist, cnt)
+ struct fs *fs;
+ int fragmap;
+ long fraglist[];
+ int cnt;
+{
+ int inblk;
+ register int field, subfield;
+ register 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)))) == 0)
+ continue;
+ field = around[siz];
+ subfield = inside[siz];
+ for (pos = siz; pos <= fs->fs_frag; pos++) {
+ if ((fragmap & field) == subfield) {
+ fraglist[siz] += cnt;
+ pos += siz;
+ field <<= siz;
+ subfield <<= siz;
+ }
+ field <<= 1;
+ subfield <<= 1;
+ }
+ }
+}
+
+#if 0 /* Not needed in GNU Hurd ufs. */
+void
+ffs_checkoverlap(bp, ip)
+ struct buf *bp;
+ struct inode *ip;
+{
+ register struct buf *ebp, *ep;
+ register 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);
+ (void)printf("\tstart %d, end %d overlap start %d, end %d\n",
+ start, last, ep->b_blkno,
+ ep->b_blkno + btodb(ep->b_bcount) - 1);
+ panic("Disk buffer overlap");
+ }
+}
+#endif /* 0 */
+
+/*
+ * block operations
+ *
+ * check if a block is available
+ */
+int
+ffs_isblock(fs, cp, h)
+ struct fs *fs;
+ unsigned char *cp;
+ daddr_t h;
+{
+ unsigned char mask;
+
+ switch ((int)fs->fs_frag) {
+ case 8:
+ return (cp[h] == 0xff);
+ case 4:
+ mask = 0x0f << ((h & 0x1) << 2);
+ return ((cp[h >> 1] & mask) == mask);
+ case 2:
+ mask = 0x03 << ((h & 0x3) << 1);
+ return ((cp[h >> 2] & mask) == mask);
+ case 1:
+ mask = 0x01 << (h & 0x7);
+ return ((cp[h >> 3] & mask) == mask);
+ default:
+ assert (0);
+ }
+}
+
+/*
+ * take a block out of the map
+ */
+void
+ffs_clrblock(fs, cp, h)
+ struct fs *fs;
+ u_char *cp;
+ daddr_t h;
+{
+
+ switch ((int)fs->fs_frag) {
+ case 8:
+ cp[h] = 0;
+ return;
+ case 4:
+ cp[h >> 1] &= ~(0x0f << ((h & 0x1) << 2));
+ return;
+ case 2:
+ cp[h >> 2] &= ~(0x03 << ((h & 0x3) << 1));
+ return;
+ case 1:
+ cp[h >> 3] &= ~(0x01 << (h & 0x7));
+ return;
+ default:
+ assert (0);
+ }
+}
+
+/*
+ * put a block into the map
+ */
+void
+ffs_setblock(fs, cp, h)
+ struct fs *fs;
+ unsigned char *cp;
+ daddr_t h;
+{
+
+ switch ((int)fs->fs_frag) {
+
+ case 8:
+ cp[h] = 0xff;
+ return;
+ case 4:
+ cp[h >> 1] |= (0x0f << ((h & 0x1) << 2));
+ return;
+ case 2:
+ cp[h >> 2] |= (0x03 << ((h & 0x3) << 1));
+ return;
+ case 1:
+ cp[h >> 3] |= (0x01 << (h & 0x7));
+ return;
+ default:
+ assert (0);
+ }
+}
+
+/* Taken from 4.4 BSD sys/libkern/skpc.c:
+ @(#)skpc.c 8.1 (Berkeley) 6/10/93
+*/
+int
+skpc(mask0, size, cp0)
+ int mask0;
+ int size;
+ char *cp0;
+{
+ register u_char *cp, *end, mask;
+
+ mask = mask0;
+ cp = (u_char *)cp0;
+ for (end = &cp[size]; cp < end && *cp == mask; ++cp);
+ return (end - cp);
+}
+
+/* Taken from 4.4 BSD sys/libkern/scanc.c:
+ @(#)scanc.c 8.1 (Berkeley) 6/10/93
+*/
+int
+scanc(size, cp, table, mask0)
+ u_int size;
+ register u_char *cp, table[];
+ int mask0;
+{
+ register u_char *end;
+ register u_char mask;
+
+ mask = mask0;
+ for (end = &cp[size]; cp < end && (table[*cp] & mask) == 0; ++cp);
+ return (end - cp);
+}
diff --git a/ufs/tables.c b/ufs/tables.c
new file mode 100644
index 00000000..d345b9e4
--- /dev/null
+++ b/ufs/tables.c
@@ -0,0 +1,138 @@
+/* Modified from BSD for GNU Hurd ufs server by Michael I. Bushnell. */
+/*
+ * 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. 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.
+ *
+ * @(#)ffs_tables.c 8.1 (Berkeley) 6/11/93
+ */
+
+#include <sys/types.h>
+#include "fs.h"
+
+/*
+ * Bit patterns for identifying fragments in the block map
+ * used as ((map & around) == inside)
+ */
+int around[9] = {
+ 0x3, 0x7, 0xf, 0x1f, 0x3f, 0x7f, 0xff, 0x1ff, 0x3ff
+};
+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.
+ */
+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,
+};
+
+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.
+ */
+u_char *fragtbl[MAXFRAG + 1] = {
+ 0, fragtbl124, fragtbl124, 0, fragtbl124, 0, 0, 0, fragtbl8,
+};
diff --git a/ufs/ufs.h b/ufs/ufs.h
new file mode 100644
index 00000000..5d823ebc
--- /dev/null
+++ b/ufs/ufs.h
@@ -0,0 +1,289 @@
+/*
+ Copyright (C) 1994, 1995, 1996, 1997, 1999 Free Software Foundation
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <mach.h>
+#include <hurd.h>
+#include <sys/mman.h>
+#include <hurd/ports.h>
+#include <hurd/pager.h>
+#include <hurd/fshelp.h>
+#include <hurd/iohelp.h>
+#include <hurd/diskfs.h>
+#include <sys/mman.h>
+#include <assert.h>
+#include "fs.h"
+#include "dinode.h"
+
+/* Define this if memory objects should not be cached by the kernel.
+ Normally, don't define it, but defining it causes a much greater rate
+ of paging requests, which may be helpful in catching bugs. */
+
+/* #undef DONT_CACHE_MEMORY_OBJECTS */
+
+struct disknode
+{
+ ino_t number;
+
+ int dir_idx;
+
+ /* For a directory, this array holds the number of directory entries in
+ each DIRBLKSIZE piece of the directory. */
+ int *dirents;
+
+ /* Links on hash list. */
+ struct node *hnext, **hprevp;
+
+ struct rwlock allocptrlock;
+
+ struct dirty_indir *dirty;
+
+ struct user_pager_info *fileinfo;
+};
+
+/* Identifies a particular block and where it's found
+ when interpreting indirect block structure. */
+struct iblock_spec
+{
+ /* Disk address of block */
+ daddr_t bno;
+
+ /* Offset in next block up; -1 if it's in the inode itself. */
+ int offset;
+};
+
+/* Identifies an indirect block owned by this file which
+ might be dirty. */
+struct dirty_indir
+{
+ daddr_t bno; /* Disk address of block. */
+ struct dirty_indir *next;
+};
+
+struct user_pager_info
+{
+ struct node *np;
+ enum pager_type
+ {
+ DISK,
+ FILE_DATA,
+ } type;
+ struct pager *p;
+ vm_prot_t max_prot;
+
+ vm_offset_t allow_unlocked_pagein;
+ vm_size_t unlocked_pagein_length;
+};
+
+#include <hurd/diskfs-pager.h>
+
+/* The physical media. */
+extern struct store *store;
+/* What the user specified. */
+extern struct store_parsed *store_parsed;
+
+/* Mapped image of the disk. */
+extern void *disk_image;
+
+extern void *zeroblock;
+
+extern struct fs *sblock;
+extern struct csum *csum;
+int sblock_dirty;
+int csum_dirty;
+
+spin_lock_t node2pagelock;
+
+spin_lock_t alloclock;
+
+spin_lock_t gennumberlock;
+u_long nextgennumber;
+
+spin_lock_t unlocked_pagein_lock;
+
+/* The compat_mode specifies whether or not we write
+ extensions onto the disk. */
+enum compat_mode
+{
+ COMPAT_GNU = 0,
+ COMPAT_BSD42 = 1,
+ COMPAT_BSD44 = 2,
+} compat_mode;
+
+/* If this is set, then this filesystem has two extensions:
+ 1) directory entries include the type field.
+ 2) symlink targets might be written directly in the di_db field
+ of the dinode. */
+int direct_symlink_extension;
+
+/* If this is set, then the disk is byteswapped from native order. */
+int swab_disk;
+
+/* Number of device blocks per DEV_BSIZE block. */
+unsigned log2_dev_blocks_per_dev_bsize;
+
+/* Handy macros */
+#define DEV_BSIZE 512
+#define NBBY 8
+#define btodb(n) ((n) / DEV_BSIZE)
+#define howmany(x,y) (((x)+((y)-1))/(y))
+#define roundup(x, y) ((((x)+((y)-1))/(y))*(y))
+#define isclr(a, i) (((a)[(i)/NBBY] & (1<<((i)%NBBY))) == 0)
+#define isset(a, i) ((a)[(i)/NBBY] & (1<<((i)%NBBY)))
+#define setbit(a,i) ((a)[(i)/NBBY] |= 1<<((i)%NBBY))
+#define clrbit(a,i) ((a)[(i)/NBBY] &= ~(1<<(i)%NBBY))
+#define fsaddr(fs,n) (fsbtodb(fs,n)*DEV_BSIZE)
+
+
+/* Functions for looking inside disk_image */
+
+/* Convert an inode number to the dinode on disk. */
+extern inline struct dinode *
+dino (ino_t inum)
+{
+ return (struct dinode *)
+ (disk_image
+ + fsaddr (sblock, ino_to_fsba (sblock, inum))
+ + ino_to_fsbo (sblock, inum) * sizeof (struct dinode));
+}
+
+/* Convert a indirect block number to a daddr_t table. */
+extern inline daddr_t *
+indir_block (daddr_t bno)
+{
+ return (daddr_t *) (disk_image + fsaddr (sblock, bno));
+}
+
+/* Convert a cg number to the cylinder group. */
+extern inline struct cg *
+cg_locate (int ncg)
+{
+ return (struct cg *) (disk_image + fsaddr (sblock, cgtod (sblock, ncg)));
+}
+
+/* Sync part of the disk */
+extern inline void
+sync_disk_blocks (daddr_t blkno, size_t nbytes, int wait)
+{
+ pager_sync_some (diskfs_disk_pager, fsaddr (sblock, blkno), nbytes, wait);
+}
+
+/* Sync an disk inode */
+extern inline void
+sync_dinode (int inum, int wait)
+{
+ sync_disk_blocks (ino_to_fsba (sblock, inum), sblock->fs_fsize, wait);
+}
+
+
+/* Functions for byte swapping */
+extern inline short
+swab_short (short arg)
+{
+ return (((arg & 0xff) << 8)
+ | ((arg & 0xff00) >> 8));
+}
+
+extern inline long
+swab_long (long arg)
+{
+ return (((long) swab_short (arg & 0xffff) << 16)
+ | swab_short ((arg & 0xffff0000) >> 16));
+}
+
+extern inline long long
+swab_long_long (long long arg)
+{
+ return (((long long) swab_long (arg & 0xffffffff) << 32)
+ | swab_long ((arg & 0xffffffff00000000LL) >> 32));
+}
+
+/* Return ENTRY, after byteswapping it if necessary */
+#define read_disk_entry(entry) \
+({ \
+ typeof (entry) ret; \
+ if (!swab_disk || sizeof (entry) == 1) \
+ ret = (entry); \
+ else if (sizeof (entry) == 2) \
+ ret = swab_short (entry); \
+ else if (sizeof (entry) == 4) \
+ ret = swab_long (entry); \
+ else \
+ abort (); \
+ ret; \
+})
+
+/* Execute A = B, but byteswap it along the way if necessary */
+#define write_disk_entry(a,b) \
+({ \
+ if (!swab_disk || sizeof (a) == 1) \
+ ((a) = (b)); \
+ else if (sizeof (a) == 2) \
+ ((a) = (swab_short (b))); \
+ else if (sizeof (a) == 4) \
+ ((a) = (swab_long (b))); \
+ else \
+ abort (); \
+})
+
+
+
+
+
+/* From alloc.c: */
+error_t ffs_alloc (struct node *, daddr_t, daddr_t, int, daddr_t *,
+ struct protid *);
+void ffs_blkfree(struct node *, daddr_t bno, long size);
+daddr_t ffs_blkpref (struct node *, daddr_t, int, daddr_t *);
+error_t ffs_realloccg(struct node *, daddr_t, daddr_t,
+ int, int, daddr_t *, struct protid *);
+
+/* From bmap.c */
+error_t fetch_indir_spec (struct node *, daddr_t, struct iblock_spec *);
+void mark_indir_dirty (struct node *, daddr_t);
+
+/* From hyper.c: */
+void get_hypermetadata (void);
+void copy_sblock (void);
+
+/* From inode.c: */
+struct node *ifind (ino_t ino);
+void inode_init (void);
+void write_all_disknodes (void);
+
+/* From pager.c: */
+void create_disk_pager (void);
+void din_map (struct node *);
+void sin_map (struct node *);
+void sin_remap (struct node *, int);
+void sin_unmap (struct node *);
+void din_unmap (struct node *);
+void drop_pager_softrefs (struct node *);
+void allow_pager_softrefs (struct node *);
+void flush_node_pager (struct node *);
+
+/* From subr.c: */
+void ffs_fragacct (struct fs *, int, long [], int);
+int ffs_isblock(struct fs *, u_char *, daddr_t);
+void ffs_clrblock(struct fs *, u_char *, daddr_t);
+void ffs_setblock (struct fs *, u_char *, daddr_t);
+int skpc (int, int, char *);
+int scanc (u_int, u_char *, u_char [], int);
+
+/* From pokeloc.c: */
+void record_poke (void *, vm_size_t);
+void sync_disk (int);
+void flush_pokes ();