diff options
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)®s, + ®_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)®s, + 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)®s, + ®_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)®s, + 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 (); |