Beispiel #1
0
static int
cgdinit(struct cgd_softc *cs, const char *cpath, struct vnode *vp,
	struct lwp *l)
{
	struct	disk_geom *dg;
	int	ret;
	char	*tmppath;
	uint64_t psize;
	unsigned secsize;
	struct dk_softc *dksc = &cs->sc_dksc;

	cs->sc_tvn = vp;
	cs->sc_tpath = NULL;

	tmppath = malloc(MAXPATHLEN, M_TEMP, M_WAITOK);
	ret = copyinstr(cpath, tmppath, MAXPATHLEN, &cs->sc_tpathlen);
	if (ret)
		goto bail;
	cs->sc_tpath = malloc(cs->sc_tpathlen, M_DEVBUF, M_WAITOK);
	memcpy(cs->sc_tpath, tmppath, cs->sc_tpathlen);

	cs->sc_tdev = vp->v_rdev;

	if ((ret = getdisksize(vp, &psize, &secsize)) != 0)
		goto bail;

	if (psize == 0) {
		ret = ENODEV;
		goto bail;
	}

	/*
	 * XXX here we should probe the underlying device.  If we
	 *     are accessing a partition of type RAW_PART, then
	 *     we should populate our initial geometry with the
	 *     geometry that we discover from the device.
	 */
	dg = &dksc->sc_dkdev.dk_geom;
	memset(dg, 0, sizeof(*dg));
	dg->dg_secperunit = psize;
	// XXX: Inherit?
	dg->dg_secsize = DEV_BSIZE;
	dg->dg_ntracks = 1;
	dg->dg_nsectors = 1024 * (1024 / dg->dg_secsize);
	dg->dg_ncylinders = dg->dg_secperunit / dg->dg_nsectors;

bail:
	free(tmppath, M_TEMP);
	if (ret && cs->sc_tpath)
		free(cs->sc_tpath, M_DEVBUF);
	return ret;
}
Beispiel #2
0
int
adosfs_mountfs(struct vnode *devvp, struct mount *mp, struct lwp *l)
{
	struct disklabel dl;
	struct partition *parp;
	struct adosfsmount *amp;
	struct buf *bp;
	struct vnode *rvp;
	size_t bitmap_sz = 0;
	int error, i;
	uint64_t numsecs;
	unsigned secsize;
	unsigned long secsperblk, blksperdisk, resvblks;

	amp = NULL;

	if ((error = vinvalbuf(devvp, V_SAVE, l->l_cred, l, 0, 0)) != 0)
		return (error);

	/*
	 * open blkdev and read boot and root block
	 */
	vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY);
	if ((error = VOP_OPEN(devvp, FREAD, NOCRED)) != 0) {
		VOP_UNLOCK(devvp);
		return (error);
	}

	error = getdisksize(devvp, &numsecs, &secsize);
	if (error)
		goto fail;

	amp = kmem_zalloc(sizeof(struct adosfsmount), KM_SLEEP);

	/*
	 * compute filesystem parameters from disklabel
	 * on arch/amiga the disklabel is computed from the native
	 * partition tables
	 * - p_fsize is the filesystem block size
	 * - p_frag is the number of sectors per filesystem block
	 * - p_cpg is the number of reserved blocks (boot blocks)
	 * - p_psize is reduced by the number of preallocated blocks
	 *           at the end of a partition
	 *
	 * XXX
	 * - bsize and secsperblk could be computed from the first sector
	 *   of the root block
	 * - resvblks (the number of boot blocks) can only be guessed
	 *   by scanning for the root block as its position moves
	 *   with resvblks
	 */
	error = VOP_IOCTL(devvp, DIOCGDINFO, &dl, FREAD, NOCRED);
	VOP_UNLOCK(devvp);
	if (error)
		goto fail;
	parp = &dl.d_partitions[DISKPART(devvp->v_rdev)];
	if (dl.d_type == DTYPE_FLOPPY) {
		amp->bsize = secsize;
		secsperblk = 1;
		resvblks   = 2;
	} else if (parp->p_fsize > 0 && parp->p_frag > 0) {
		amp->bsize = parp->p_fsize * parp->p_frag;
		secsperblk = parp->p_frag;
		resvblks   = parp->p_cpg;
	} else {
		error = EINVAL;
		goto fail;
	}
	blksperdisk = numsecs / secsperblk;


	/* The filesytem variant ('dostype') is stored in the boot block */
	bp = NULL;
	if ((error = bread(devvp, (daddr_t)BBOFF,
			   amp->bsize, NOCRED, 0, &bp)) != 0) {
		goto fail;
	}
	amp->dostype = adoswordn(bp, 0);
	brelse(bp, 0);

	/* basic sanity checks */
	if (amp->dostype < 0x444f5300 || amp->dostype > 0x444f5305) {
		error = EINVAL;
		goto fail;
	}

	amp->rootb = (blksperdisk - 1 + resvblks) / 2;
	amp->numblks = blksperdisk - resvblks;

	amp->nwords = amp->bsize >> 2;
	amp->dbsize = amp->bsize - (IS_FFS(amp) ? 0 : OFS_DATA_OFFSET);
	amp->devvp = devvp;

	amp->mp = mp;
	mp->mnt_data = amp;
	mp->mnt_stat.f_fsidx.__fsid_val[0] = (long)devvp->v_rdev;
	mp->mnt_stat.f_fsidx.__fsid_val[1] = makefstype(MOUNT_ADOSFS);
	mp->mnt_stat.f_fsid = mp->mnt_stat.f_fsidx.__fsid_val[0];
	mp->mnt_stat.f_namemax = ADMAXNAMELEN;
	mp->mnt_fs_bshift = ffs(amp->bsize) - 1;
	mp->mnt_dev_bshift = DEV_BSHIFT;
	mp->mnt_flag |= MNT_LOCAL;

	/*
	 * init anode table.
	 */
	for (i = 0; i < ANODEHASHSZ; i++)
		LIST_INIT(&amp->anodetab[i]);

	/*
	 * get the root anode, if not a valid fs this will fail.
	 */
	if ((error = VFS_ROOT(mp, &rvp)) != 0)
		goto fail;
	/* allocate and load bitmap, set free space */
	bitmap_sz = ((amp->numblks + 31) / 32) * sizeof(*amp->bitmap);
	amp->bitmap = kmem_alloc(bitmap_sz, KM_SLEEP);
	if (amp->bitmap)
		adosfs_loadbitmap(amp);
	if (mp->mnt_flag & MNT_RDONLY && amp->bitmap) {
		/*
		 * Don't need the bitmap any more if it's read-only.
		 */
		kmem_free(amp->bitmap, bitmap_sz);
		amp->bitmap = NULL;
	}
	vput(rvp);

	return(0);

fail:
	vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY);
	(void) VOP_CLOSE(devvp, FREAD, NOCRED);
	VOP_UNLOCK(devvp);
	if (amp && amp->bitmap)
		kmem_free(amp->bitmap, bitmap_sz);
	if (amp)
		kmem_free(amp, sizeof(*amp));
	return (error);
}
Beispiel #3
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!
 *
 * In BSD, the disk label includes all sorts of useful information,
 * like cpg.  Solaris doesn't have that, and deriving it (as well as
 * some other parameters) is difficult.  Rather than duplicate the
 * code, just ask mkfs what it would've come up with by default.
 * Ideally, we'd just link in the code, but given the source base
 * involved, it's more practical to just get a binary dump.
 *
 * The one minor drawback to the above approach is that newfs and mkfs
 * will produce vastly different layouts for the same partition if
 * they're allowed to default everything.  So, if the superblock that
 * mkfs gives us doesn't work for guessing where the alternates are,
 * we need to try newfs.
 */
static int
calcsb(calcsb_t style, caddr_t dev, int devfd, struct fs *fs)
{
#define	FROM_CHILD	0
#define	TO_FSCK		1
#define	CMD_IDX		0
#define	DEV_IDX		3
#define	SIZE_IDX	4

	int child_pipe[2];
	caddr_t mkfsline[] = {
		"",		/* CMD_IDX */
		"-o",
		"calcbinsb,N",
		NULL,		/* DEV_IDX */
		NULL,		/* SIZE_IDX */
		NULL
	};
	caddr_t newfsline[] = {
		"",		/* CMD_IDX */
		"-B",
		"-N",
		NULL,		/* DEV_IDX */
		NULL
	};
	int pending, transferred;
	caddr_t *cmdline;
	caddr_t target;
	caddr_t sizestr = NULL;
	caddr_t path_old, path_new, mkfs_dir, mkfs_path, newfs_path;
	caddr_t slash;
	diskaddr_t size;
	int devnull;

	switch (style) {
	case MKFS_STYLE:
		if (debug)
			(void) printf("calcsb() going with style MKFS\n");
		cmdline = mkfsline;
		break;
	case NEWFS_STYLE:
		if (debug)
			(void) printf("calcsb() going with style NEWFS\n");
		cmdline = newfsline;
		break;
	default:
		if (debug)
			(void) printf("calcsb() doesn't undestand style %d\n",
			    style);
		return (0);
	}

	cmdline[DEV_IDX] = dev;

	/*
	 * Normally, only use the stock versions of the utilities.
	 * However, if we're debugging, the odds are that we're
	 * using experimental versions of them as well, so allow
	 * some flexibility.
	 */
	mkfs_path = getenv("MKFS_PATH");
	if (!debug || (mkfs_path == NULL))
		mkfs_path = MKFS_PATH;

	newfs_path = getenv("NEWFS_PATH");
	if (!debug || (newfs_path == NULL))
		newfs_path = NEWFS_PATH;

	if (style == MKFS_STYLE) {
		cmdline[CMD_IDX] = mkfs_path;

		size = getdisksize(dev, devfd);
		if (size == 0)
			return (0);

		(void) fsck_asprintf(&sizestr, "%lld", (longlong_t)size);
		cmdline[SIZE_IDX] = sizestr;
	} else if (style == NEWFS_STYLE) {
		/*
		 * Make sure that newfs will find the right version of mkfs.
		 */
		cmdline[CMD_IDX] = newfs_path;
		path_old = getenv("PATH");
		/* mkfs_path is always initialized, despite lint's concerns */
		mkfs_dir = strdup(mkfs_path);
		if (mkfs_dir == NULL)
			return (0);
		/*
		 * If no location data for mkfs, don't need to do
		 * anything about PATH.
		 */
		slash = strrchr(mkfs_dir, '/');
		if (slash != NULL) {
			/*
			 * Just want the dir, so discard the executable name.
			 */
			*slash = '\0';

			/*
			 * newfs uses system() to find mkfs, so make sure
			 * that the one we want to use is first on the
			 * list.  Don't free path_new upon success, as it
			 * has become part of the environment.
			 */
			(void) fsck_asprintf(&path_new, "PATH=%s:%s",
			    mkfs_dir, path_old);
			if (putenv(path_new) != 0) {
				free(mkfs_dir);
				free(path_new);
				return (0);
			}
		}
		free(mkfs_dir);
	} else {
		/*
		 * Bad search style, quietly return failure.
		 */
		if (debug) {
			(void) printf("calcsb: got bad style number %d\n",
			    (int)style);
		}
		return (0);
	}

	if (pipe(child_pipe) < 0) {
		pfatal("calcsb: could not create pipe: %s\n", strerror(errno));
		if (sizestr != NULL)
			free(sizestr);
		return (0);
	}

	switch (fork()) {
	case -1:
		pfatal("calcsb: fork failed: %s\n", strerror(errno));
		if (sizestr != NULL)
			free(sizestr);
		return (0);
	case 0:
		if (dup2(child_pipe[TO_FSCK], fileno(stdout)) < 0) {
			(void) printf(
			    "calcsb: could not rename file descriptor: %s\n",
			    strerror(errno));
			exit(EXBADPARM);
		}
		devnull = open("/dev/null", O_WRONLY);
		if (devnull == -1) {
			(void) printf("calcsb: could not open /dev/null: %s\n",
			    strerror(errno));
			exit(EXBADPARM);
		}
		if (dup2(devnull, fileno(stderr)) < 0) {
			(void) printf(
			    "calcsb: could not rename file descriptor: %s\n",
			    strerror(errno));
			exit(EXBADPARM);
		}
		(void) close(child_pipe[FROM_CHILD]);
		(void) execv(cmdline[CMD_IDX], cmdline);
		(void) printf("calcsb: could not exec %s: %s\n",
		    cmdline[CMD_IDX], strerror(errno));
		exit(EXBADPARM);
		/* NOTREACHED */
	default:
		break;
	}

	(void) close(child_pipe[TO_FSCK]);
	if (sizestr != NULL)
		free(sizestr);

	pending = sizeof (struct fs);
	target = (caddr_t)fs;
	do {
		transferred = read(child_pipe[FROM_CHILD], target, pending);
		pending -= transferred;
		target += transferred;
	} while ((pending > 0) && (transferred > 0));

	if (pending > 0) {
		if (transferred < 0)
			pfatal(
		    "calcsb: binary read of superblock from %s failed: %s\n",
			    (style == MKFS_STYLE) ? "mkfs" : "newfs",
			    (transferred < 0) ? strerror(errno) : "");
		else
			pfatal(
		    "calcsb: short read of superblock from %s\n",
			    (style == MKFS_STYLE) ? "mkfs" : "newfs");
		return (0);
	}

	(void) close(child_pipe[FROM_CHILD]);
	(void) wait(NULL);

	if ((fs->fs_magic != FS_MAGIC) &&
	    (fs->fs_magic != MTB_UFS_MAGIC))
		return (0);

	return (1);
}
Beispiel #4
0
int
msdosfs_mountfs(struct vnode *devvp, struct mount *mp, struct lwp *l, struct msdosfs_args *argp)
{
	struct msdosfsmount *pmp;
	struct buf *bp;
	dev_t dev = devvp->v_rdev;
	union bootsector *bsp;
	struct byte_bpb33 *b33;
	struct byte_bpb50 *b50;
	struct byte_bpb710 *b710;
	uint8_t SecPerClust;
	int	ronly, error, tmp;
	int	bsize;
	uint64_t psize;
	unsigned secsize;

	/* Flush out any old buffers remaining from a previous use. */
	if ((error = vinvalbuf(devvp, V_SAVE, l->l_cred, l, 0, 0)) != 0)
		return (error);

	ronly = (mp->mnt_flag & MNT_RDONLY) != 0;

	bp  = NULL; /* both used in error_exit */
	pmp = NULL;

	error = fstrans_mount(mp);
	if (error)
		goto error_exit;

	error = getdisksize(devvp, &psize, &secsize);
	if (error) {
		if (argp->flags & MSDOSFSMNT_GEMDOSFS)
			goto error_exit;

		/* ok, so it failed.  we most likely don't need the info */
		secsize = DEV_BSIZE;
		psize = 0;
		error = 0;
	}

	if (argp->flags & MSDOSFSMNT_GEMDOSFS) {
		bsize = secsize;
		if (bsize != 512) {
			DPRINTF(("Invalid block bsize %d for GEMDOS\n", bsize));
			error = EINVAL;
			goto error_exit;
		}
	} else
		bsize = 0;

	/*
	 * Read the boot sector of the filesystem, and then check the
	 * boot signature.  If not a dos boot sector then error out.
	 */
	if ((error = bread(devvp, 0, secsize, NOCRED, 0, &bp)) != 0)
		goto error_exit;
	bsp = (union bootsector *)bp->b_data;
	b33 = (struct byte_bpb33 *)bsp->bs33.bsBPB;
	b50 = (struct byte_bpb50 *)bsp->bs50.bsBPB;
	b710 = (struct byte_bpb710 *)bsp->bs710.bsBPB;

	if (!(argp->flags & MSDOSFSMNT_GEMDOSFS)) {
		if (bsp->bs50.bsBootSectSig0 != BOOTSIG0
		    || bsp->bs50.bsBootSectSig1 != BOOTSIG1) {
			DPRINTF(("bootsig0 %d bootsig1 %d\n", 
			    bsp->bs50.bsBootSectSig0,
			    bsp->bs50.bsBootSectSig1));
			error = EINVAL;
			goto error_exit;
		}
	}

	pmp = malloc(sizeof *pmp, M_MSDOSFSMNT, M_WAITOK);
	memset(pmp, 0, sizeof *pmp);
	pmp->pm_mountp = mp;

	/*
	 * Compute several useful quantities from the bpb in the
	 * bootsector.  Copy in the dos 5 variant of the bpb then fix up
	 * the fields that are different between dos 5 and dos 3.3.
	 */
	SecPerClust = b50->bpbSecPerClust;
	pmp->pm_BytesPerSec = getushort(b50->bpbBytesPerSec);
	pmp->pm_ResSectors = getushort(b50->bpbResSectors);
	pmp->pm_FATs = b50->bpbFATs;
	pmp->pm_RootDirEnts = getushort(b50->bpbRootDirEnts);
	pmp->pm_Sectors = getushort(b50->bpbSectors);
	pmp->pm_FATsecs = getushort(b50->bpbFATsecs);
	pmp->pm_SecPerTrack = getushort(b50->bpbSecPerTrack);
	pmp->pm_Heads = getushort(b50->bpbHeads);
	pmp->pm_Media = b50->bpbMedia;

	if (!(argp->flags & MSDOSFSMNT_GEMDOSFS)) {
		/* XXX - We should probably check more values here */
    		if (!pmp->pm_BytesPerSec || !SecPerClust
	    		|| pmp->pm_SecPerTrack > 63) {
			DPRINTF(("bytespersec %d secperclust %d "
			    "secpertrack %d\n", 
			    pmp->pm_BytesPerSec, SecPerClust,
			    pmp->pm_SecPerTrack));
			error = EINVAL;
			goto error_exit;
		}
	}

	if (pmp->pm_Sectors == 0) {
		pmp->pm_HiddenSects = getulong(b50->bpbHiddenSecs);
		pmp->pm_HugeSectors = getulong(b50->bpbHugeSectors);
	} else {
		pmp->pm_HiddenSects = getushort(b33->bpbHiddenSecs);
		pmp->pm_HugeSectors = pmp->pm_Sectors;
	}

	if (pmp->pm_RootDirEnts == 0) {
		unsigned short vers = getushort(b710->bpbFSVers);
		/*
		 * Some say that bsBootSectSig[23] must be zero, but
		 * Windows does not require this and some digital cameras
		 * do not set these to zero.  Therefore, do not insist.
		 */
		if (pmp->pm_Sectors || pmp->pm_FATsecs || vers) {
			DPRINTF(("sectors %d fatsecs %lu vers %d\n",
			    pmp->pm_Sectors, pmp->pm_FATsecs, vers));
			error = EINVAL;
			goto error_exit;
		}
		pmp->pm_fatmask = FAT32_MASK;
		pmp->pm_fatmult = 4;
		pmp->pm_fatdiv = 1;
		pmp->pm_FATsecs = getulong(b710->bpbBigFATsecs);

		/* mirrorring is enabled if the FATMIRROR bit is not set */
		if ((getushort(b710->bpbExtFlags) & FATMIRROR) == 0)
			pmp->pm_flags |= MSDOSFS_FATMIRROR;
		else
			pmp->pm_curfat = getushort(b710->bpbExtFlags) & FATNUM;
	} else
		pmp->pm_flags |= MSDOSFS_FATMIRROR;

	if (argp->flags & MSDOSFSMNT_GEMDOSFS) {
		if (FAT32(pmp)) {
			DPRINTF(("FAT32 for GEMDOS\n"));
			/*
			 * GEMDOS doesn't know FAT32.
			 */
			error = EINVAL;
			goto error_exit;
		}

		/*
		 * Check a few values (could do some more):
		 * - logical sector size: power of 2, >= block size
		 * - sectors per cluster: power of 2, >= 1
		 * - number of sectors:   >= 1, <= size of partition
		 */
		if ( (SecPerClust == 0)
		  || (SecPerClust & (SecPerClust - 1))
		  || (pmp->pm_BytesPerSec < bsize)
		  || (pmp->pm_BytesPerSec & (pmp->pm_BytesPerSec - 1))
		  || (pmp->pm_HugeSectors == 0)
		  || (pmp->pm_HugeSectors * (pmp->pm_BytesPerSec / bsize)
		      > psize)) {
			DPRINTF(("consistency checks for GEMDOS\n"));
			error = EINVAL;
			goto error_exit;
		}
		/*
		 * XXX - Many parts of the msdosfs driver seem to assume that
		 * the number of bytes per logical sector (BytesPerSec) will
		 * always be the same as the number of bytes per disk block
		 * Let's pretend it is.
		 */
		tmp = pmp->pm_BytesPerSec / bsize;
		pmp->pm_BytesPerSec  = bsize;
		pmp->pm_HugeSectors *= tmp;
		pmp->pm_HiddenSects *= tmp;
		pmp->pm_ResSectors  *= tmp;
		pmp->pm_Sectors     *= tmp;
		pmp->pm_FATsecs     *= tmp;
		SecPerClust         *= tmp;
	}

	/* Check that fs has nonzero FAT size */
	if (pmp->pm_FATsecs == 0) {
		DPRINTF(("FATsecs is 0\n"));
		error = EINVAL;
		goto error_exit;
	}

	pmp->pm_fatblk = pmp->pm_ResSectors;
	if (FAT32(pmp)) {
		pmp->pm_rootdirblk = getulong(b710->bpbRootClust);
		pmp->pm_firstcluster = pmp->pm_fatblk
			+ (pmp->pm_FATs * pmp->pm_FATsecs);
		pmp->pm_fsinfo = getushort(b710->bpbFSInfo);
	} else {
		pmp->pm_rootdirblk = pmp->pm_fatblk +
			(pmp->pm_FATs * pmp->pm_FATsecs);
		pmp->pm_rootdirsize = (pmp->pm_RootDirEnts * sizeof(struct direntry)
				       + pmp->pm_BytesPerSec - 1)
			/ pmp->pm_BytesPerSec;/* in sectors */
		pmp->pm_firstcluster = pmp->pm_rootdirblk + pmp->pm_rootdirsize;
	}

	pmp->pm_nmbrofclusters = (pmp->pm_HugeSectors - pmp->pm_firstcluster) /
	    SecPerClust;
	pmp->pm_maxcluster = pmp->pm_nmbrofclusters + 1;
	pmp->pm_fatsize = pmp->pm_FATsecs * pmp->pm_BytesPerSec;

	if (argp->flags & MSDOSFSMNT_GEMDOSFS) {
		if (pmp->pm_nmbrofclusters <= (0xff0 - 2)) {
			pmp->pm_fatmask = FAT12_MASK;
			pmp->pm_fatmult = 3;
			pmp->pm_fatdiv = 2;
		} else {
			pmp->pm_fatmask = FAT16_MASK;
			pmp->pm_fatmult = 2;
			pmp->pm_fatdiv = 1;
		}
	} else if (pmp->pm_fatmask == 0) {
		if (pmp->pm_maxcluster
		    <= ((CLUST_RSRVD - CLUST_FIRST) & FAT12_MASK)) {
			/*
			 * This will usually be a floppy disk. This size makes
			 * sure that one FAT entry will not be split across
			 * multiple blocks.
			 */
			pmp->pm_fatmask = FAT12_MASK;
			pmp->pm_fatmult = 3;
			pmp->pm_fatdiv = 2;
		} else {
			pmp->pm_fatmask = FAT16_MASK;
			pmp->pm_fatmult = 2;
			pmp->pm_fatdiv = 1;
		}
	}
	if (FAT12(pmp))
		pmp->pm_fatblocksize = 3 * pmp->pm_BytesPerSec;
	else
		pmp->pm_fatblocksize = MAXBSIZE;

	pmp->pm_fatblocksec = pmp->pm_fatblocksize / pmp->pm_BytesPerSec;
	pmp->pm_bnshift = ffs(pmp->pm_BytesPerSec) - 1;

	/*
	 * Compute mask and shift value for isolating cluster relative byte
	 * offsets and cluster numbers from a file offset.
	 */
	pmp->pm_bpcluster = SecPerClust * pmp->pm_BytesPerSec;
	pmp->pm_crbomask = pmp->pm_bpcluster - 1;
	pmp->pm_cnshift = ffs(pmp->pm_bpcluster) - 1;

	/*
	 * Check for valid cluster size
	 * must be a power of 2
	 */
	if (pmp->pm_bpcluster ^ (1 << pmp->pm_cnshift)) {
		DPRINTF(("bpcluster %lu cnshift %lu\n", 
		    pmp->pm_bpcluster, pmp->pm_cnshift));
		error = EINVAL;
		goto error_exit;
	}

	/*
	 * Cluster size must be within limit of MAXBSIZE.
	 * Many FAT filesystems will not have clusters larger than
	 * 32KiB due to limits in Windows versions before Vista.
	 */
	if (pmp->pm_bpcluster > MAXBSIZE) {
		DPRINTF(("bpcluster %lu > MAXBSIZE %d\n",
		    pmp->pm_bpcluster, MAXBSIZE));
		error = EINVAL;
		goto error_exit;
	}

	/*
	 * Release the bootsector buffer.
	 */
	brelse(bp, BC_AGE);
	bp = NULL;

	/*
	 * Check FSInfo.
	 */
	if (pmp->pm_fsinfo) {
		struct fsinfo *fp;

		/*
		 * XXX	If the fsinfo block is stored on media with
		 *	2KB or larger sectors, is the fsinfo structure
		 *	padded at the end or in the middle?
		 */
		if ((error = bread(devvp, de_bn2kb(pmp, pmp->pm_fsinfo),
		    pmp->pm_BytesPerSec, NOCRED, 0, &bp)) != 0)
			goto error_exit;
		fp = (struct fsinfo *)bp->b_data;
		if (!memcmp(fp->fsisig1, "RRaA", 4)
		    && !memcmp(fp->fsisig2, "rrAa", 4)
		    && !memcmp(fp->fsisig3, "\0\0\125\252", 4)
		    && !memcmp(fp->fsisig4, "\0\0\125\252", 4))
			pmp->pm_nxtfree = getulong(fp->fsinxtfree);
		else
			pmp->pm_fsinfo = 0;
		brelse(bp, 0);
		bp = NULL;
	}

	/*
	 * Check and validate (or perhaps invalidate?) the fsinfo structure?
	 * XXX
	 */
	if (pmp->pm_fsinfo) {
		if ((pmp->pm_nxtfree == 0xffffffffUL) ||
		    (pmp->pm_nxtfree > pmp->pm_maxcluster))
			pmp->pm_fsinfo = 0;
	}

	/*
	 * Allocate memory for the bitmap of allocated clusters, and then
	 * fill it in.
	 */
	pmp->pm_inusemap = malloc(((pmp->pm_maxcluster + N_INUSEBITS)
				   / N_INUSEBITS)
				  * sizeof(*pmp->pm_inusemap),
				  M_MSDOSFSFAT, M_WAITOK);

	/*
	 * fillinusemap() needs pm_devvp.
	 */
	pmp->pm_dev = dev;
	pmp->pm_devvp = devvp;

	/*
	 * Have the inuse map filled in.
	 */
	if ((error = fillinusemap(pmp)) != 0) {
		DPRINTF(("fillinusemap %d\n", error));
		goto error_exit;
	}

	/*
	 * If they want FAT updates to be synchronous then let them suffer
	 * the performance degradation in exchange for the on disk copy of
	 * the FAT being correct just about all the time.  I suppose this
	 * would be a good thing to turn on if the kernel is still flakey.
	 */
	if (mp->mnt_flag & MNT_SYNCHRONOUS)
		pmp->pm_flags |= MSDOSFSMNT_WAITONFAT;

	/*
	 * Finish up.
	 */
	if (ronly)
		pmp->pm_flags |= MSDOSFSMNT_RONLY;
	else
		pmp->pm_fmod = 1;
	mp->mnt_data = pmp;
	mp->mnt_stat.f_fsidx.__fsid_val[0] = (long)dev;
	mp->mnt_stat.f_fsidx.__fsid_val[1] = makefstype(MOUNT_MSDOS);
	mp->mnt_stat.f_fsid = mp->mnt_stat.f_fsidx.__fsid_val[0];
	mp->mnt_stat.f_namemax = MSDOSFS_NAMEMAX(pmp);
	mp->mnt_flag |= MNT_LOCAL;
	mp->mnt_dev_bshift = pmp->pm_bnshift;
	mp->mnt_fs_bshift = pmp->pm_cnshift;

	/*
	 * If we ever do quotas for DOS filesystems this would be a place
	 * to fill in the info in the msdosfsmount structure. You dolt,
	 * quotas on dos filesystems make no sense because files have no
	 * owners on dos filesystems. of course there is some empty space
	 * in the directory entry where we could put uid's and gid's.
	 */

	spec_node_setmountedfs(devvp, mp);

	return (0);

error_exit:
	fstrans_unmount(mp);
	if (bp)
		brelse(bp, BC_AGE);
	if (pmp) {
		if (pmp->pm_inusemap)
			free(pmp->pm_inusemap, M_MSDOSFSFAT);
		free(pmp, M_MSDOSFSMNT);
		mp->mnt_data = NULL;
	}
	return (error);
}
Beispiel #5
0
/*
 * Lookup and open needed files.
 *
 * For file system internal snapshot initializes sc_mntname, sc_mount,
 * sc_bs_vp and sc_time.
 *
 * Otherwise returns dev and size of the underlying block device.
 * Initializes sc_mntname, sc_mount, sc_bdev, sc_bs_vp and sc_mount
 */
static int
fss_create_files(struct fss_softc *sc, struct fss_set *fss,
    off_t *bsize, struct lwp *l)
{
	int error, bits, fsbsize;
	uint64_t numsec;
	unsigned int secsize;
	struct timespec ts;
	/* nd -> nd2 to reduce mistakes while updating only some namei calls */
	struct pathbuf *pb2;
	struct nameidata nd2;
	struct vnode *vp;

	/*
	 * Get the mounted file system.
	 */

	error = namei_simple_user(fss->fss_mount,
				NSM_FOLLOW_NOEMULROOT, &vp);
	if (error != 0)
		return error;

	if ((vp->v_vflag & VV_ROOT) != VV_ROOT) {
		vrele(vp);
		return EINVAL;
	}

	sc->sc_mount = vp->v_mount;
	memcpy(sc->sc_mntname, sc->sc_mount->mnt_stat.f_mntonname, MNAMELEN);

	vrele(vp);

	/*
	 * Check for file system internal snapshot.
	 */

	error = namei_simple_user(fss->fss_bstore,
				NSM_FOLLOW_NOEMULROOT, &vp);
	if (error != 0)
		return error;

	if (vp->v_type == VREG && vp->v_mount == sc->sc_mount) {
		sc->sc_flags |= FSS_PERSISTENT;
		sc->sc_bs_vp = vp;

		fsbsize = sc->sc_bs_vp->v_mount->mnt_stat.f_iosize;
		bits = sizeof(sc->sc_bs_bshift)*NBBY;
		for (sc->sc_bs_bshift = 1; sc->sc_bs_bshift < bits;
		    sc->sc_bs_bshift++)
			if (FSS_FSBSIZE(sc) == fsbsize)
				break;
		if (sc->sc_bs_bshift >= bits)
			return EINVAL;

		sc->sc_bs_bmask = FSS_FSBSIZE(sc)-1;
		sc->sc_clshift = 0;

		if ((fss->fss_flags & FSS_UNLINK_ON_CREATE) != 0) {
			error = do_sys_unlink(fss->fss_bstore, UIO_USERSPACE);
			if (error)
				return error;
		}
		error = vn_lock(vp, LK_EXCLUSIVE);
		if (error != 0)
			return error;
		error = VFS_SNAPSHOT(sc->sc_mount, sc->sc_bs_vp, &ts);
		TIMESPEC_TO_TIMEVAL(&sc->sc_time, &ts);

		VOP_UNLOCK(sc->sc_bs_vp);

		return error;
	}
	vrele(vp);

	/*
	 * Get the block device it is mounted on and its size.
	 */

	error = spec_node_lookup_by_mount(sc->sc_mount, &vp);
	if (error)
		return error;
	sc->sc_bdev = vp->v_rdev;

	error = getdisksize(vp, &numsec, &secsize);
	vrele(vp);
	if (error)
		return error;

	*bsize = (off_t)numsec*secsize;

	/*
	 * Get the backing store
	 */

	error = pathbuf_copyin(fss->fss_bstore, &pb2);
	if (error) {
 		return error;
	}
	NDINIT(&nd2, LOOKUP, FOLLOW, pb2);
	if ((error = vn_open(&nd2, FREAD|FWRITE, 0)) != 0) {
		pathbuf_destroy(pb2);
		return error;
	}
	VOP_UNLOCK(nd2.ni_vp);

	sc->sc_bs_vp = nd2.ni_vp;

	if (nd2.ni_vp->v_type != VREG && nd2.ni_vp->v_type != VCHR) {
		pathbuf_destroy(pb2);
		return EINVAL;
	}
	pathbuf_destroy(pb2);

	if ((fss->fss_flags & FSS_UNLINK_ON_CREATE) != 0) {
		error = do_sys_unlink(fss->fss_bstore, UIO_USERSPACE);
		if (error)
			return error;
	}
	if (sc->sc_bs_vp->v_type == VREG) {
		fsbsize = sc->sc_bs_vp->v_mount->mnt_stat.f_iosize;
		if (fsbsize & (fsbsize-1))	/* No power of two */
			return EINVAL;
		for (sc->sc_bs_bshift = 1; sc->sc_bs_bshift < 32;
		    sc->sc_bs_bshift++)
			if (FSS_FSBSIZE(sc) == fsbsize)
				break;
		if (sc->sc_bs_bshift >= 32)
			return EINVAL;
		sc->sc_bs_bmask = FSS_FSBSIZE(sc)-1;
	} else {
		sc->sc_bs_bshift = DEV_BSHIFT;
		sc->sc_bs_bmask = FSS_FSBSIZE(sc)-1;
	}

	return 0;
}