Subject: Re: ffs fs_flags superblock update sign extension bug
To: Manuel Bouyer <bouyer@antioche.eu.org>
From: Darrin B. Jewell <dbj@NetBSD.org>
List: tech-kern
Date: 10/07/2003 10:19:16
--=-=-=


Manuel Bouyer <bouyer@antioche.eu.org> writes:

> On Sun, Sep 28, 2003 at 06:20:30PM -0400, Darrin B. Jewell wrote:
> > 
> > Filesystems that were upgraded by -current kernels in the intermediate
> > time will have bad values of fs_flags which will cause a problem which
> > will not be exhibited until some future date when those flag fields
> > are used.  If desired, I can post a simple program which can go in and
> > unset these flags.  Since the lifespan of the incorrect code was
> > small, I do not recommend implementing compatibility in the kernel.
> 
> Yes, please. And maybe add a warning in the kernel until 2.0 is branched.


The following program will check for and correct this problem in the
superblock.  Once this email makes the archives, I'll add a warning
in the kernel that points to this message and plan to remove that warning
when we get closer to branching for the release.

Darrin


--=-=-=
Content-Disposition: attachment; filename=fixufsflags.c
Content-Description: program to fix fs_flags in superblock


/*
 * This program fixes a bug introduced in ffs filesystems whose
 * superblock was updated by a netbsd-current kernel in sept 2003.
 * For more information see mail to tech-kern@netbsd.org
 * Message ID:  <200309282220.h8SMKUN03670@marcela.zlz.net>
 * Archive URL:
 *   http://mail-index.netbsd.org/tech-kern/2003/09/28/0003.html
 *
 *  THIS PROGRAM MODIFIES YOUR FILESYSTEM SUPERBLOCK.  RUN WITH CARE
 *
 * To compile:
 *   cc -o fixufsflags fixufsflags.c -lutil
 *
 * Example invocation:
 *   fixufsflags wd0a
 *
 * Darrin B. Jewell <dbj@netbsd.org> 2003-10-03
 *
 */

#include <stdlib.h>
#include <stdio.h>
#include <assert.h>

#include <unistd.h>
#include <fcntl.h>
#include <fstab.h>
#include <err.h>

#include <util.h> /* For opendisk.  compile this with -lutil */

#include <sys/param.h>
#include <sys/time.h>

#include <ufs/ffs/fs.h>

int fixfs(struct fs *fs);

int
main(int argc, char *argv[])
{
  char devfname[MAXPATHLEN] = "unknown";
  u_int8_t sbbuf[SBLOCKSIZE] = { 0 };
  int r;
  int fd;
  off_t off;
  ssize_t sz;
  int ret;

  setprogname(argv[0]);
  if (argc != 2)
    errx(EXIT_FAILURE,"usage: %s device",getprogname());

  fd = opendisk(argv[1],O_RDWR|O_SYNC|O_RSYNC,devfname,MAXPATHLEN,0);
  if (fd == -1) 
    err(EXIT_FAILURE,"Unable to open disk %s as %s",argv[1],devfname);

  off = lseek(fd, SBLOCK_UFS1, SEEK_SET);
  if (off == -1)
    err(EXIT_FAILURE,"unable to seek to offset %d",SBLOCK_UFS1);
  assert(off == SBLOCK_UFS1);

  sz = read(fd, sbbuf, SBLOCKSIZE);
  if (sz == -1)
    err(EXIT_FAILURE,"unable to read %d bytes at offset %d",SBLOCKSIZE,SBLOCK_UFS1);
  if (sz != SBLOCKSIZE)
    warnx("unable to read superblock, only read %d bytes at offset %d",sz,SBLOCK_UFS1);

  printf("Successfully read %"PRIdMAX" bytes at offset %"PRIdMAX" from %s\n",
	 (intmax_t)sz,(intmax_t)off,devfname);

  ret = fixfs((struct fs *)sbbuf);
  
  if ((ret == 1) && (sz == SBLOCKSIZE)) {
    off = lseek(fd, SBLOCK_UFS1, SEEK_SET);
    if (off == -1)
      err(EXIT_FAILURE,"unable to seek to offset %d",SBLOCK_UFS1);
    assert(off == SBLOCK_UFS1);

    sz = write(fd, sbbuf, SBLOCKSIZE);
    if (sz == -1)
      err(EXIT_FAILURE,"unable to write %d bytes at offset %d",SBLOCKSIZE,SBLOCK_UFS1);
    if (sz != SBLOCKSIZE)
      warnx("unable to write superblock, only wrote %d bytes at offset %d",sz,SBLOCK_UFS1);

    printf("Successfully wrote %"PRIdMAX" bytes at offset %"PRIdMAX" from %s\n",
	   (intmax_t)sz,(intmax_t)off,devfname);

    printf("Superblock updated\n");
  }

  r = close(fd);
  if (r == -1)
    err(EXIT_FAILURE,"close");
  assert(r == 0);

  if (ret == -1)
    return EXIT_FAILURE;

  return EXIT_SUCCESS;
}

int
fixfs(struct fs *fs)
{
  int swapped;

  swapped = fs->fs_magic == FS_UFS1_MAGIC_SWAPPED;

  if (!swapped && fs->fs_magic != FS_UFS1_MAGIC) {
    warnx("Magic number does not indicate ffs filesystem");
    return -1;
  }

  if (swapped) {
    warnx("Swapped ffs filesystems not supported");
    return -1;
  }

  if (fs->fs_bsize != fs->fs_maxbsize) {
    warnx("Does not appear to be upgraded ffs filesystem");
    return -1;
  }

  if (fs->fs_flags & 0xffffff00) {
    printf("Unknown bits set in fs_flags: 0x%08x\n",fs->fs_flags);
    if ((fs->fs_flags & 0x7fffff00) == 0x7fffff00) {
      printf("Stripping incorrect bits from fs_flags\n");
      fs->fs_flags &= 0x7f;
      return 1;
    }
  } else {
    printf("fs_flags bits appear to be clean\n");
  }

  return 0;
}

--=-=-=--