diff options
author | root <root@(null).(none)> | 2009-05-03 17:20:00 +0200 |
---|---|---|
committer | root <root@(null).(none)> | 2009-05-03 17:20:00 +0200 |
commit | e0faf22f31c48fb27b43c1825897d26e58feafc4 (patch) | |
tree | 65a09372b31e08a3a865bd0a88cd2718bafcd643 /ufs/hyper.c |
This is my initial working version.
There is a bug in boot in this version: subhurd sometimes cannot boot.
Diffstat (limited to 'ufs/hyper.c')
-rw-r--r-- | ufs/hyper.c | 414 |
1 files changed, 414 insertions, 0 deletions
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"); +} |