diff --git a/sys/ufs/ffs/ffs_subr.c b/sys/ufs/ffs/ffs_subr.c --- a/sys/ufs/ffs/ffs_subr.c +++ b/sys/ufs/ffs/ffs_subr.c @@ -35,6 +35,7 @@ __FBSDID("$FreeBSD$"); #include +#include #ifndef _KERNEL #include @@ -50,6 +51,7 @@ struct malloc_type; #define UFS_MALLOC(size, type, flags) malloc(size) #define UFS_FREE(ptr, type) free(ptr) +#define maxphys MAXPHYS #else /* _KERNEL */ #include @@ -125,6 +127,7 @@ static off_t sblock_try[] = SBLOCKSEARCH; static int readsuper(void *, struct fs **, off_t, int, int, int (*)(void *, off_t, void **, int)); +static int validate_sblock(struct fs *, int); /* * Read a superblock from the devfd device. @@ -141,7 +144,7 @@ * EIO: non-existent or truncated superblock. * EIO: error reading summary information. * ENOENT: no usable known superblock found. - * ENOSPC: failed to allocate space for the superblock. + * ENOMEM: failed to allocate space for the superblock. * EINVAL: The previous newfs operation on this volume did not complete. * The administrator must complete newfs before using this volume. */ @@ -152,7 +155,8 @@ { struct fs *fs; struct fs_summary_info *fs_si; - int i, error, size, blks; + int i, error; + uint64_t size, blks; uint8_t *space; int32_t *lp; char *buf; @@ -190,17 +194,16 @@ if (fs->fs_contigsumsize > 0) size += fs->fs_ncg * sizeof(int32_t); size += fs->fs_ncg * sizeof(u_int8_t); - /* When running in libufs or libsa, UFS_MALLOC may fail */ - if ((fs_si = UFS_MALLOC(sizeof(*fs_si), filltype, M_WAITOK)) == NULL) { + if ((fs_si = UFS_MALLOC(sizeof(*fs_si), filltype, M_NOWAIT)) == NULL) { UFS_FREE(fs, filltype); - return (ENOSPC); + return (ENOMEM); } bzero(fs_si, sizeof(*fs_si)); fs->fs_si = fs_si; - if ((space = UFS_MALLOC(size, filltype, M_WAITOK)) == NULL) { + if ((space = UFS_MALLOC(size, filltype, M_NOWAIT)) == NULL) { UFS_FREE(fs->fs_si, filltype); UFS_FREE(fs, filltype); - return (ENOSPC); + return (ENOMEM); } fs->fs_csp = (struct csum *)space; for (i = 0; i < blks; i += fs->fs_frag) { @@ -253,16 +256,8 @@ fs = *fsp; if (fs->fs_magic == FS_BAD_MAGIC) return (EINVAL); - if (!(((fs->fs_magic == FS_UFS1_MAGIC && (isaltsblk || - sblockloc <= SBLOCK_UFS1)) || - (fs->fs_magic == FS_UFS2_MAGIC && (isaltsblk || - sblockloc == fs->fs_sblockloc))) && - fs->fs_ncg >= 1 && - fs->fs_bsize >= MINBSIZE && - fs->fs_bsize <= MAXBSIZE && - fs->fs_bsize >= roundup(sizeof(struct fs), DEV_BSIZE) && - fs->fs_sbsize <= SBLOCKSIZE)) - return (ENOENT); + if ((error = validate_sblock(fs, isaltsblk)) != 0) + return (error); /* * If the filesystem has been run on a kernel without * metadata check hashes, disable them. @@ -310,6 +305,140 @@ return (0); } +/* + * Verify the filesystem values. + */ +#define ILOG2(num) (fls(num) - 1) + +static int +validate_sblock(struct fs *fs, int isaltsblk) +{ + int i, sectorsize; + u_int64_t maxfilesize, minfpg, sizepb; + + sectorsize = dbtob(1); + if (fs->fs_magic == FS_UFS2_MAGIC) { + if ((!isaltsblk && (fs->fs_sblockloc != SBLOCK_UFS2 || + fs->fs_sblockactualloc != SBLOCK_UFS2)) || + fs->fs_maxsymlinklen != ((UFS_NDADDR + UFS_NIADDR) * + sizeof(ufs2_daddr_t)) || + fs->fs_nindir != fs->fs_bsize / sizeof(ufs2_daddr_t) || + fs->fs_inopb != fs->fs_bsize / sizeof(struct ufs2_dinode)) + return (ENOENT); + } else if (fs->fs_magic == FS_UFS1_MAGIC) { + if ((!isaltsblk && (fs->fs_sblockloc > SBLOCK_UFS1 || + fs->fs_sblockactualloc != SBLOCK_UFS1)) || + fs->fs_nindir != fs->fs_bsize / sizeof(ufs1_daddr_t) || + fs->fs_inopb != fs->fs_bsize / sizeof(struct ufs1_dinode) || + fs->fs_maxsymlinklen != ((UFS_NDADDR + UFS_NIADDR) * + sizeof(ufs1_daddr_t)) || + fs->fs_old_inodefmt != FS_44INODEFMT || + fs->fs_old_cgoffset != 0 || + fs->fs_old_cgmask != 0xffffffff || + fs->fs_old_size != fs->fs_size || + fs->fs_old_rotdelay != 0 || + fs->fs_old_rps != 60 || + fs->fs_old_nspf != fs->fs_fsize / sectorsize || + fs->fs_old_cpg != 1 || + fs->fs_old_interleave != 1 || + fs->fs_old_trackskew != 0 || + fs->fs_old_cpc != 0 || + fs->fs_old_postblformat != 1 || + fs->fs_old_nrpos != 1 || + fs->fs_old_spc != fs->fs_fpg * fs->fs_old_nspf || + fs->fs_old_nsect != fs->fs_old_spc || + fs->fs_old_npsect != fs->fs_old_spc || + fs->fs_old_dsize != fs->fs_dsize || + fs->fs_old_ncyl != fs->fs_ncg) + return (ENOENT); + } else { + return (ENOENT); + } + if (fs->fs_bsize < MINBSIZE || fs->fs_bsize > MAXBSIZE || + fs->fs_bsize < roundup(sizeof(struct fs), DEV_BSIZE) || + fs->fs_sbsize > SBLOCKSIZE || fs->fs_sbsize < fs->fs_fsize || + !powerof2(fs->fs_bsize)) + return (ENOENT); + if (fs->fs_fsize < sectorsize || fs->fs_fsize > fs->fs_bsize || + fs->fs_fsize * MAXFRAG < fs->fs_bsize || !powerof2(fs->fs_fsize)) + return (ENOENT); + if (fs->fs_maxbsize < fs->fs_bsize || !powerof2(fs->fs_maxbsize) || + fs->fs_maxbsize > FS_MAXCONTIG * fs->fs_bsize) + return (ENOENT); + if (fs->fs_bmask != ~(fs->fs_bsize - 1) || + fs->fs_fmask != ~(fs->fs_fsize - 1) || + fs->fs_qbmask != ~fs->fs_bmask || + fs->fs_qfmask != ~fs->fs_fmask || + fs->fs_bshift != ILOG2(fs->fs_bsize) || + fs->fs_fshift != ILOG2(fs->fs_fsize) || + fs->fs_frag != numfrags(fs, fs->fs_bsize) || + fs->fs_fragshift != ILOG2(fs->fs_frag) || + fs->fs_frag > MAXFRAG || + fs->fs_fsbtodb != ILOG2(fs->fs_fsize / sectorsize)) + return (ENOENT); + if (fs->fs_sblkno != + roundup(howmany(fs->fs_sblockloc + SBLOCKSIZE, fs->fs_fsize), + fs->fs_frag) || + fs->fs_cblkno != fs->fs_sblkno + + roundup(howmany(SBLOCKSIZE, fs->fs_fsize), fs->fs_frag) || + fs->fs_iblkno != fs->fs_cblkno + fs->fs_frag || + fs->fs_dblkno != fs->fs_iblkno + fs->fs_ipg / INOPF(fs) || + fs->fs_cgsize != fragroundup(fs, CGSIZE(fs))) + return (ENOENT); + if (fs->fs_csaddr != cgdmin(fs, 0) || + fs->fs_cssize != + fragroundup(fs, fs->fs_ncg * sizeof(struct csum)) || + fs->fs_dsize != fs->fs_size - fs->fs_sblkno - + fs->fs_ncg * (fs->fs_dblkno - fs->fs_sblkno) - + howmany(fs->fs_cssize, fs->fs_fsize) || + fs->fs_metaspace < 0 || fs->fs_metaspace > fs->fs_fpg / 2 || + fs->fs_minfree > 99) + return (ENOENT); + maxfilesize = fs->fs_bsize * UFS_NDADDR - 1; + for (sizepb = fs->fs_bsize, i = 0; i < UFS_NIADDR; i++) { + sizepb *= NINDIR(fs); + maxfilesize += sizepb; + } + if (fs->fs_maxfilesize != maxfilesize) + return (ENOENT); + /* + * These values have a tight interaction with each other that + * makes it hard to tightly bound them. So we can only check + * that they are within a broader possible range. + * + * Calculate minfpg, the minimum number of fragments that can be + * in a cylinder group. The value 12289 is calculated in newfs(8) + * when creating the smallest block size UFS version 1 filesystem + * (4096 block size) with no fragments (4096 fragment size). That + * number may be depressed even further for very small filesystems + * since newfs(8) strives to have at least four cylinder groups. + */ + minfpg = MIN(12289, fs->fs_size / 4); + if (fs->fs_ncg < 1 || fs->fs_ncg > (fs->fs_size / minfpg) + 1 || + fs->fs_fpg < minfpg || fs->fs_fpg > fs->fs_size || + fs->fs_ipg * fs->fs_ncg > (((int64_t)(1)) << 32) - INOPB(fs) || + fs->fs_ipg > fs->fs_fpg || fs->fs_size < 8 * fs->fs_frag) + return (ENOENT); + if (fs->fs_size <= (fs->fs_ncg - 1) * fs->fs_fpg || + fs->fs_size > fs->fs_ncg * fs->fs_fpg) + return (ENOENT); + /* + * Maxcontig sets the default for the maximum number of blocks + * that may be allocated sequentially. With file system clustering + * it is possible to allocate contiguous blocks up to the maximum + * transfer size permitted by the controller or buffering. + */ + if (fs->fs_maxcontig < 1 || + fs->fs_maxcontig > MAX(1, maxphys / fs->fs_bsize)) + return (ENOENT); + if (fs->fs_maxcontig < 0 || + (fs->fs_maxcontig == 0 && fs->fs_contigsumsize != 0) || + (fs->fs_maxcontig > 1 && + fs->fs_contigsumsize != MIN(fs->fs_maxcontig, FS_MAXCONTIG))) + return (ENOENT); + return (0); +} + /* * Write a superblock to the devfd device from the memory pointed to by fs. * Write out the superblock summary information if it is present.