/* 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 #include #include #include 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"); }