1999-08-28 01:08:13 +00:00
|
|
|
/* $FreeBSD$ */
|
1998-02-18 09:28:47 +00:00
|
|
|
/* $NetBSD: msdosfs_fat.c,v 1.28 1997/11/17 15:36:49 ws Exp $ */
|
1994-09-19 15:41:57 +00:00
|
|
|
|
|
|
|
/*-
|
1998-02-18 09:28:47 +00:00
|
|
|
* Copyright (C) 1994, 1995, 1997 Wolfgang Solfrank.
|
|
|
|
* Copyright (C) 1994, 1995, 1997 TooLs GmbH.
|
1994-09-19 15:41:57 +00:00
|
|
|
* All rights reserved.
|
|
|
|
* Original code by Paul Popelka (paulp@uts.amdahl.com) (see below).
|
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions
|
|
|
|
* are met:
|
|
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer.
|
|
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
|
|
* documentation and/or other materials provided with the distribution.
|
|
|
|
* 3. All advertising materials mentioning features or use of this software
|
|
|
|
* must display the following acknowledgement:
|
|
|
|
* This product includes software developed by TooLs GmbH.
|
|
|
|
* 4. The name of TooLs GmbH may not be used to endorse or promote products
|
|
|
|
* derived from this software without specific prior written permission.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR
|
|
|
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
|
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
|
|
* IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
|
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
|
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
|
|
|
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
|
|
|
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
|
|
|
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
|
|
|
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
*/
|
2005-01-06 18:10:42 +00:00
|
|
|
/*-
|
1994-09-19 15:41:57 +00:00
|
|
|
* Written by Paul Popelka (paulp@uts.amdahl.com)
|
1995-05-30 08:16:23 +00:00
|
|
|
*
|
1994-09-19 15:41:57 +00:00
|
|
|
* You can do anything you want with this software, just don't say you wrote
|
|
|
|
* it, and don't remove this notice.
|
1995-05-30 08:16:23 +00:00
|
|
|
*
|
1994-09-19 15:41:57 +00:00
|
|
|
* This software is provided "as is".
|
1995-05-30 08:16:23 +00:00
|
|
|
*
|
1994-09-19 15:41:57 +00:00
|
|
|
* The author supplies this software to be publicly redistributed on the
|
|
|
|
* understanding that the author is not responsible for the correct
|
|
|
|
* functioning of this software in any circumstances and is not liable for
|
|
|
|
* any damages caused by this software.
|
1995-05-30 08:16:23 +00:00
|
|
|
*
|
1994-09-19 15:41:57 +00:00
|
|
|
* October 1992
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <sys/param.h>
|
|
|
|
#include <sys/systm.h>
|
|
|
|
#include <sys/buf.h>
|
2007-08-07 02:20:37 +00:00
|
|
|
#include <sys/mount.h>
|
|
|
|
#include <sys/vnode.h>
|
1994-09-19 15:41:57 +00:00
|
|
|
|
2001-05-25 08:14:14 +00:00
|
|
|
#include <fs/msdosfs/bpb.h>
|
|
|
|
#include <fs/msdosfs/direntry.h>
|
|
|
|
#include <fs/msdosfs/denode.h>
|
|
|
|
#include <fs/msdosfs/fat.h>
|
2007-08-07 02:20:37 +00:00
|
|
|
#include <fs/msdosfs/msdosfsmount.h>
|
1994-09-19 15:41:57 +00:00
|
|
|
|
2016-10-28 11:23:36 +00:00
|
|
|
#define FULL_RUN ((u_int)0xffffffff)
|
|
|
|
|
2002-03-19 22:20:14 +00:00
|
|
|
static int chainalloc(struct msdosfsmount *pmp, u_long start,
|
2002-03-23 12:38:05 +00:00
|
|
|
u_long count, u_long fillwith, u_long *retcluster,
|
|
|
|
u_long *got);
|
2002-03-19 22:20:14 +00:00
|
|
|
static int chainlength(struct msdosfsmount *pmp, u_long start,
|
2002-03-23 12:38:05 +00:00
|
|
|
u_long count);
|
|
|
|
static void fatblock(struct msdosfsmount *pmp, u_long ofs, u_long *bnp,
|
|
|
|
u_long *sizep, u_long *bop);
|
|
|
|
static int fatchain(struct msdosfsmount *pmp, u_long start, u_long count,
|
|
|
|
u_long fillwith);
|
|
|
|
static void fc_lookup(struct denode *dep, u_long findcn, u_long *frcnp,
|
|
|
|
u_long *fsrcnp);
|
2002-03-19 22:20:14 +00:00
|
|
|
static void updatefats(struct msdosfsmount *pmp, struct buf *bp,
|
2002-03-23 12:38:05 +00:00
|
|
|
u_long fatbn);
|
1998-04-15 17:47:40 +00:00
|
|
|
static __inline void
|
2002-03-19 22:20:14 +00:00
|
|
|
usemap_alloc(struct msdosfsmount *pmp, u_long cn);
|
1998-04-15 17:47:40 +00:00
|
|
|
static __inline void
|
2002-03-19 22:20:14 +00:00
|
|
|
usemap_free(struct msdosfsmount *pmp, u_long cn);
|
2010-02-28 17:13:59 +00:00
|
|
|
static int clusteralloc1(struct msdosfsmount *pmp, u_long start,
|
|
|
|
u_long count, u_long fillwith, u_long *retcluster,
|
|
|
|
u_long *got);
|
1995-12-03 16:42:02 +00:00
|
|
|
|
1994-09-19 15:41:57 +00:00
|
|
|
static void
|
2015-01-09 14:50:08 +00:00
|
|
|
fatblock(struct msdosfsmount *pmp, u_long ofs, u_long *bnp, u_long *sizep,
|
|
|
|
u_long *bop)
|
1994-09-19 15:41:57 +00:00
|
|
|
{
|
|
|
|
u_long bn, size;
|
|
|
|
|
|
|
|
bn = ofs / pmp->pm_fatblocksize * pmp->pm_fatblocksec;
|
|
|
|
size = min(pmp->pm_fatblocksec, pmp->pm_FATsecs - bn)
|
2000-01-27 14:43:07 +00:00
|
|
|
* DEV_BSIZE;
|
1998-02-18 09:28:47 +00:00
|
|
|
bn += pmp->pm_fatblk + pmp->pm_curfat * pmp->pm_FATsecs;
|
|
|
|
|
1994-09-19 15:41:57 +00:00
|
|
|
if (bnp)
|
|
|
|
*bnp = bn;
|
|
|
|
if (sizep)
|
|
|
|
*sizep = size;
|
|
|
|
if (bop)
|
|
|
|
*bop = ofs % pmp->pm_fatblocksize;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Map the logical cluster number of a file into a physical disk sector
|
|
|
|
* that is filesystem relative.
|
|
|
|
*
|
|
|
|
* dep - address of denode representing the file of interest
|
|
|
|
* findcn - file relative cluster whose filesystem relative cluster number
|
|
|
|
* and/or block number are/is to be found
|
2002-05-16 21:28:32 +00:00
|
|
|
* bnp - address of where to place the filesystem relative block number.
|
1994-09-19 15:41:57 +00:00
|
|
|
* If this pointer is null then don't return this quantity.
|
2002-05-16 21:28:32 +00:00
|
|
|
* cnp - address of where to place the filesystem relative cluster number.
|
1994-09-19 15:41:57 +00:00
|
|
|
* If this pointer is null then don't return this quantity.
|
2015-01-12 21:55:48 +00:00
|
|
|
* sp - pointer to returned block size
|
1994-09-19 15:41:57 +00:00
|
|
|
*
|
|
|
|
* NOTE: Either bnp or cnp must be non-null.
|
|
|
|
* This function has one side effect. If the requested file relative cluster
|
|
|
|
* is beyond the end of file, then the actual number of clusters in the file
|
|
|
|
* is returned in *cnp. This is useful for determining how long a directory is.
|
|
|
|
* If cnp is null, nothing is returned.
|
|
|
|
*/
|
|
|
|
int
|
2015-01-09 14:50:08 +00:00
|
|
|
pcbmap(struct denode *dep, u_long findcn, daddr_t *bnp, u_long *cnp, int *sp)
|
1994-09-19 15:41:57 +00:00
|
|
|
{
|
|
|
|
int error;
|
|
|
|
u_long i;
|
|
|
|
u_long cn;
|
1998-02-18 09:28:47 +00:00
|
|
|
u_long prevcn = 0; /* XXX: prevcn could be used unititialized */
|
1994-09-19 15:41:57 +00:00
|
|
|
u_long byteoffset;
|
|
|
|
u_long bn;
|
|
|
|
u_long bo;
|
|
|
|
struct buf *bp = NULL;
|
|
|
|
u_long bp_bn = -1;
|
|
|
|
struct msdosfsmount *pmp = dep->de_pmp;
|
|
|
|
u_long bsize;
|
|
|
|
|
2010-02-28 17:07:49 +00:00
|
|
|
KASSERT(bnp != NULL || cnp != NULL || sp != NULL,
|
|
|
|
("pcbmap: extra call"));
|
|
|
|
ASSERT_VOP_ELOCKED(DETOV(dep), "pcbmap");
|
1994-09-19 15:41:57 +00:00
|
|
|
|
|
|
|
cn = dep->de_StartCluster;
|
|
|
|
/*
|
|
|
|
* The "file" that makes up the root directory is contiguous,
|
|
|
|
* permanently allocated, of fixed size, and is not made up of
|
|
|
|
* clusters. If the cluster number is beyond the end of the root
|
|
|
|
* directory, then return the number of clusters in the file.
|
|
|
|
*/
|
|
|
|
if (cn == MSDOSFSROOT) {
|
|
|
|
if (dep->de_Attributes & ATTR_DIRECTORY) {
|
1998-02-18 09:28:47 +00:00
|
|
|
if (de_cn2off(pmp, findcn) >= dep->de_FileSize) {
|
1994-09-19 15:41:57 +00:00
|
|
|
if (cnp)
|
1998-02-18 09:28:47 +00:00
|
|
|
*cnp = de_bn2cn(pmp, pmp->pm_rootdirsize);
|
|
|
|
return (E2BIG);
|
1994-09-19 15:41:57 +00:00
|
|
|
}
|
|
|
|
if (bnp)
|
1998-02-18 09:28:47 +00:00
|
|
|
*bnp = pmp->pm_rootdirblk + de_cn2bn(pmp, findcn);
|
1994-09-19 15:41:57 +00:00
|
|
|
if (cnp)
|
|
|
|
*cnp = MSDOSFSROOT;
|
1998-02-18 09:28:47 +00:00
|
|
|
if (sp)
|
|
|
|
*sp = min(pmp->pm_bpcluster,
|
|
|
|
dep->de_FileSize - de_cn2off(pmp, findcn));
|
|
|
|
return (0);
|
1994-09-19 15:41:57 +00:00
|
|
|
} else { /* just an empty file */
|
|
|
|
if (cnp)
|
|
|
|
*cnp = 0;
|
1998-02-18 09:28:47 +00:00
|
|
|
return (E2BIG);
|
1994-09-19 15:41:57 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
1998-02-18 09:28:47 +00:00
|
|
|
/*
|
|
|
|
* All other files do I/O in cluster sized blocks
|
|
|
|
*/
|
|
|
|
if (sp)
|
|
|
|
*sp = pmp->pm_bpcluster;
|
|
|
|
|
1994-09-19 15:41:57 +00:00
|
|
|
/*
|
2017-05-21 19:29:28 +00:00
|
|
|
* Rummage around in the FAT cache, maybe we can avoid tromping
|
|
|
|
* through every FAT entry for the file. And, keep track of how far
|
1994-09-19 15:41:57 +00:00
|
|
|
* off the cache was from where we wanted to be.
|
|
|
|
*/
|
|
|
|
i = 0;
|
|
|
|
fc_lookup(dep, findcn, &i, &cn);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Handle all other files or directories the normal way.
|
|
|
|
*/
|
|
|
|
for (; i < findcn; i++) {
|
1998-02-18 09:28:47 +00:00
|
|
|
/*
|
|
|
|
* Stop with all reserved clusters, not just with EOF.
|
|
|
|
*/
|
|
|
|
if ((cn | ~pmp->pm_fatmask) >= CLUST_RSRVD)
|
1994-09-19 15:41:57 +00:00
|
|
|
goto hiteof;
|
|
|
|
byteoffset = FATOFS(pmp, cn);
|
|
|
|
fatblock(pmp, byteoffset, &bn, &bsize, &bo);
|
|
|
|
if (bn != bp_bn) {
|
|
|
|
if (bp)
|
|
|
|
brelse(bp);
|
|
|
|
error = bread(pmp->pm_devvp, bn, bsize, NOCRED, &bp);
|
1998-02-18 09:28:47 +00:00
|
|
|
if (error) {
|
|
|
|
brelse(bp);
|
|
|
|
return (error);
|
|
|
|
}
|
1994-09-19 15:41:57 +00:00
|
|
|
bp_bn = bn;
|
|
|
|
}
|
|
|
|
prevcn = cn;
|
2004-09-08 10:57:09 +00:00
|
|
|
if (bo >= bsize) {
|
|
|
|
if (bp)
|
|
|
|
brelse(bp);
|
|
|
|
return (EIO);
|
|
|
|
}
|
1998-02-18 09:28:47 +00:00
|
|
|
if (FAT32(pmp))
|
|
|
|
cn = getulong(&bp->b_data[bo]);
|
|
|
|
else
|
|
|
|
cn = getushort(&bp->b_data[bo]);
|
|
|
|
if (FAT12(pmp) && (prevcn & 1))
|
|
|
|
cn >>= 4;
|
|
|
|
cn &= pmp->pm_fatmask;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Force the special cluster numbers
|
|
|
|
* to be the same for all cluster sizes
|
|
|
|
* to let the rest of msdosfs handle
|
|
|
|
* all cases the same.
|
|
|
|
*/
|
|
|
|
if ((cn | ~pmp->pm_fatmask) >= CLUST_RSRVD)
|
|
|
|
cn |= ~pmp->pm_fatmask;
|
1994-09-19 15:41:57 +00:00
|
|
|
}
|
|
|
|
|
1998-02-18 09:28:47 +00:00
|
|
|
if (!MSDOSFSEOF(pmp, cn)) {
|
1994-09-19 15:41:57 +00:00
|
|
|
if (bp)
|
|
|
|
brelse(bp);
|
|
|
|
if (bnp)
|
|
|
|
*bnp = cntobn(pmp, cn);
|
|
|
|
if (cnp)
|
|
|
|
*cnp = cn;
|
|
|
|
fc_setcache(dep, FC_LASTMAP, i, cn);
|
1998-02-18 09:28:47 +00:00
|
|
|
return (0);
|
1994-09-19 15:41:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
hiteof:;
|
|
|
|
if (cnp)
|
|
|
|
*cnp = i;
|
|
|
|
if (bp)
|
|
|
|
brelse(bp);
|
2017-05-21 19:29:28 +00:00
|
|
|
/* update last file cluster entry in the FAT cache */
|
1994-09-19 15:41:57 +00:00
|
|
|
fc_setcache(dep, FC_LASTFC, i - 1, prevcn);
|
1998-02-18 09:28:47 +00:00
|
|
|
return (E2BIG);
|
1994-09-19 15:41:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2017-05-21 19:29:28 +00:00
|
|
|
* Find the closest entry in the FAT cache to the cluster we are looking
|
1994-09-19 15:41:57 +00:00
|
|
|
* for.
|
|
|
|
*/
|
1995-11-07 14:06:45 +00:00
|
|
|
static void
|
2015-01-09 14:50:08 +00:00
|
|
|
fc_lookup(struct denode *dep, u_long findcn, u_long *frcnp, u_long *fsrcnp)
|
1994-09-19 15:41:57 +00:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
u_long cn;
|
2016-04-15 17:28:24 +00:00
|
|
|
struct fatcache *closest = NULL;
|
1994-09-19 15:41:57 +00:00
|
|
|
|
2010-02-28 17:07:49 +00:00
|
|
|
ASSERT_VOP_LOCKED(DETOV(dep), "fc_lookup");
|
|
|
|
|
1994-09-19 15:41:57 +00:00
|
|
|
for (i = 0; i < FC_SIZE; i++) {
|
|
|
|
cn = dep->de_fc[i].fc_frcn;
|
|
|
|
if (cn != FCE_EMPTY && cn <= findcn) {
|
2016-04-15 17:28:24 +00:00
|
|
|
if (closest == NULL || cn > closest->fc_frcn)
|
1994-09-19 15:41:57 +00:00
|
|
|
closest = &dep->de_fc[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (closest) {
|
|
|
|
*frcnp = closest->fc_frcn;
|
|
|
|
*fsrcnp = closest->fc_fsrcn;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2017-05-21 19:29:28 +00:00
|
|
|
* Purge the FAT cache in denode dep of all entries relating to file
|
1994-09-19 15:41:57 +00:00
|
|
|
* relative cluster frcn and beyond.
|
|
|
|
*/
|
1998-02-18 09:28:47 +00:00
|
|
|
void
|
2015-01-09 14:50:08 +00:00
|
|
|
fc_purge(struct denode *dep, u_int frcn)
|
1994-09-19 15:41:57 +00:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
struct fatcache *fcp;
|
|
|
|
|
2010-02-28 17:07:49 +00:00
|
|
|
ASSERT_VOP_ELOCKED(DETOV(dep), "fc_purge");
|
|
|
|
|
1994-09-19 15:41:57 +00:00
|
|
|
fcp = dep->de_fc;
|
|
|
|
for (i = 0; i < FC_SIZE; i++, fcp++) {
|
|
|
|
if (fcp->fc_frcn >= frcn)
|
|
|
|
fcp->fc_frcn = FCE_EMPTY;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2017-05-21 19:29:28 +00:00
|
|
|
* Update the FAT.
|
|
|
|
* If mirroring the FAT, update all copies, with the first copy as last.
|
|
|
|
* Else update only the current FAT (ignoring the others).
|
1994-09-19 15:41:57 +00:00
|
|
|
*
|
|
|
|
* pmp - msdosfsmount structure for filesystem to update
|
2017-05-21 19:29:28 +00:00
|
|
|
* bp - addr of modified FAT block
|
|
|
|
* fatbn - block number relative to begin of filesystem of the modified FAT block.
|
1994-09-19 15:41:57 +00:00
|
|
|
*/
|
1995-10-29 15:33:36 +00:00
|
|
|
static void
|
2015-01-09 14:50:08 +00:00
|
|
|
updatefats(struct msdosfsmount *pmp, struct buf *bp, u_long fatbn)
|
1994-09-19 15:41:57 +00:00
|
|
|
{
|
|
|
|
struct buf *bpn;
|
2013-02-01 18:25:53 +00:00
|
|
|
int cleanfat, i;
|
1994-09-19 15:41:57 +00:00
|
|
|
|
|
|
|
#ifdef MSDOSFS_DEBUG
|
1998-02-18 09:28:47 +00:00
|
|
|
printf("updatefats(pmp %p, bp %p, fatbn %lu)\n", pmp, bp, fatbn);
|
1994-09-19 15:41:57 +00:00
|
|
|
#endif
|
|
|
|
|
1998-02-18 09:28:47 +00:00
|
|
|
if (pmp->pm_flags & MSDOSFS_FATMIRROR) {
|
|
|
|
/*
|
2017-05-21 19:29:28 +00:00
|
|
|
* Now copy the block(s) of the modified FAT to the other copies of
|
|
|
|
* the FAT and write them out. This is faster than reading in the
|
|
|
|
* other FATs and then writing them back out. This could tie up
|
|
|
|
* the FAT for quite a while. Preventing others from accessing it.
|
|
|
|
* To prevent us from going after the FAT quite so much we use
|
|
|
|
* delayed writes, unless they specified "synchronous" when the
|
1998-02-18 09:28:47 +00:00
|
|
|
* filesystem was mounted. If synch is asked for then use
|
|
|
|
* bwrite()'s and really slow things down.
|
|
|
|
*/
|
2013-02-01 18:25:53 +00:00
|
|
|
if (fatbn != pmp->pm_fatblk || FAT12(pmp))
|
|
|
|
cleanfat = 0;
|
|
|
|
else if (FAT16(pmp))
|
|
|
|
cleanfat = 16;
|
|
|
|
else
|
|
|
|
cleanfat = 32;
|
1998-02-18 09:28:47 +00:00
|
|
|
for (i = 1; i < pmp->pm_FATs; i++) {
|
|
|
|
fatbn += pmp->pm_FATsecs;
|
|
|
|
/* getblk() never fails */
|
2003-03-04 00:04:44 +00:00
|
|
|
bpn = getblk(pmp->pm_devvp, fatbn, bp->b_bcount,
|
|
|
|
0, 0, 0);
|
2017-06-02 18:39:53 +00:00
|
|
|
memcpy(bpn->b_data, bp->b_data, bp->b_bcount);
|
2013-02-01 18:25:53 +00:00
|
|
|
/* Force the clean bit on in the other copies. */
|
|
|
|
if (cleanfat == 16)
|
2017-05-19 18:13:41 +00:00
|
|
|
((uint8_t *)bpn->b_data)[3] |= 0x80;
|
2013-02-01 18:25:53 +00:00
|
|
|
else if (cleanfat == 32)
|
2017-05-19 18:13:41 +00:00
|
|
|
((uint8_t *)bpn->b_data)[7] |= 0x08;
|
2013-02-01 18:30:41 +00:00
|
|
|
if (pmp->pm_mountp->mnt_flag & MNT_SYNCHRONOUS)
|
1998-02-18 09:28:47 +00:00
|
|
|
bwrite(bpn);
|
|
|
|
else
|
|
|
|
bdwrite(bpn);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
1994-09-19 15:41:57 +00:00
|
|
|
/*
|
2017-05-21 19:29:28 +00:00
|
|
|
* Write out the first (or current) FAT last.
|
1994-09-19 15:41:57 +00:00
|
|
|
*/
|
2013-02-01 18:30:41 +00:00
|
|
|
if (pmp->pm_mountp->mnt_flag & MNT_SYNCHRONOUS)
|
1994-09-19 15:41:57 +00:00
|
|
|
bwrite(bp);
|
|
|
|
else
|
|
|
|
bdwrite(bp);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2017-05-21 19:29:28 +00:00
|
|
|
* Updating entries in 12 bit FATs is a pain in the butt.
|
1995-05-30 08:16:23 +00:00
|
|
|
*
|
1994-09-19 15:41:57 +00:00
|
|
|
* The following picture shows where nibbles go when moving from a 12 bit
|
|
|
|
* cluster number into the appropriate bytes in the FAT.
|
1995-05-30 08:16:23 +00:00
|
|
|
*
|
1994-09-19 15:41:57 +00:00
|
|
|
* byte m byte m+1 byte m+2
|
|
|
|
* +----+----+ +----+----+ +----+----+
|
|
|
|
* | 0 1 | | 2 3 | | 4 5 | FAT bytes
|
|
|
|
* +----+----+ +----+----+ +----+----+
|
1995-05-30 08:16:23 +00:00
|
|
|
*
|
1994-09-19 15:41:57 +00:00
|
|
|
* +----+----+----+ +----+----+----+
|
|
|
|
* | 3 0 1 | | 4 5 2 |
|
|
|
|
* +----+----+----+ +----+----+----+
|
|
|
|
* cluster n cluster n+1
|
1995-05-30 08:16:23 +00:00
|
|
|
*
|
1994-09-19 15:41:57 +00:00
|
|
|
* Where n is even. m = n + (n >> 2)
|
1995-05-30 08:16:23 +00:00
|
|
|
*
|
1994-09-19 15:41:57 +00:00
|
|
|
*/
|
1998-04-15 17:47:40 +00:00
|
|
|
static __inline void
|
2015-01-09 14:50:08 +00:00
|
|
|
usemap_alloc(struct msdosfsmount *pmp, u_long cn)
|
1994-09-19 15:41:57 +00:00
|
|
|
{
|
1998-02-18 09:28:47 +00:00
|
|
|
|
2010-02-28 17:13:59 +00:00
|
|
|
MSDOSFS_ASSERT_MP_LOCKED(pmp);
|
2010-02-28 17:15:45 +00:00
|
|
|
|
2016-10-28 11:34:32 +00:00
|
|
|
KASSERT(cn <= pmp->pm_maxcluster, ("cn too large %lu %lu", cn,
|
|
|
|
pmp->pm_maxcluster));
|
2015-04-05 21:08:04 +00:00
|
|
|
KASSERT((pmp->pm_flags & MSDOSFSMNT_RONLY) == 0,
|
|
|
|
("usemap_alloc on ro msdosfs mount"));
|
2010-02-28 17:15:45 +00:00
|
|
|
KASSERT((pmp->pm_inusemap[cn / N_INUSEBITS] & (1 << (cn % N_INUSEBITS)))
|
|
|
|
== 0, ("Allocating used sector %ld %ld %x", cn, cn % N_INUSEBITS,
|
|
|
|
(unsigned)pmp->pm_inusemap[cn / N_INUSEBITS]));
|
1998-02-18 09:28:47 +00:00
|
|
|
pmp->pm_inusemap[cn / N_INUSEBITS] |= 1 << (cn % N_INUSEBITS);
|
2010-02-28 17:15:45 +00:00
|
|
|
KASSERT(pmp->pm_freeclustercount > 0, ("usemap_alloc: too little"));
|
1994-09-19 15:41:57 +00:00
|
|
|
pmp->pm_freeclustercount--;
|
2013-02-17 20:35:54 +00:00
|
|
|
pmp->pm_flags |= MSDOSFS_FSIMOD;
|
1994-09-19 15:41:57 +00:00
|
|
|
}
|
|
|
|
|
1998-04-15 17:47:40 +00:00
|
|
|
static __inline void
|
2015-01-09 14:50:08 +00:00
|
|
|
usemap_free(struct msdosfsmount *pmp, u_long cn)
|
1994-09-19 15:41:57 +00:00
|
|
|
{
|
1998-02-18 09:28:47 +00:00
|
|
|
|
2010-02-28 17:13:59 +00:00
|
|
|
MSDOSFS_ASSERT_MP_LOCKED(pmp);
|
2016-10-28 11:34:32 +00:00
|
|
|
|
|
|
|
KASSERT(cn <= pmp->pm_maxcluster, ("cn too large %lu %lu", cn,
|
|
|
|
pmp->pm_maxcluster));
|
2015-04-05 21:08:04 +00:00
|
|
|
KASSERT((pmp->pm_flags & MSDOSFSMNT_RONLY) == 0,
|
|
|
|
("usemap_free on ro msdosfs mount"));
|
1994-09-19 15:41:57 +00:00
|
|
|
pmp->pm_freeclustercount++;
|
2013-02-17 20:35:54 +00:00
|
|
|
pmp->pm_flags |= MSDOSFS_FSIMOD;
|
2010-02-28 17:15:45 +00:00
|
|
|
KASSERT((pmp->pm_inusemap[cn / N_INUSEBITS] & (1 << (cn % N_INUSEBITS)))
|
|
|
|
!= 0, ("Freeing unused sector %ld %ld %x", cn, cn % N_INUSEBITS,
|
|
|
|
(unsigned)pmp->pm_inusemap[cn / N_INUSEBITS]));
|
1994-09-19 15:41:57 +00:00
|
|
|
pmp->pm_inusemap[cn / N_INUSEBITS] &= ~(1 << (cn % N_INUSEBITS));
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2015-01-09 14:50:08 +00:00
|
|
|
clusterfree(struct msdosfsmount *pmp, u_long cluster, u_long *oldcnp)
|
1994-09-19 15:41:57 +00:00
|
|
|
{
|
|
|
|
int error;
|
|
|
|
u_long oldcn;
|
|
|
|
|
|
|
|
error = fatentry(FAT_GET_AND_SET, pmp, cluster, &oldcn, MSDOSFSFREE);
|
2010-02-28 17:13:59 +00:00
|
|
|
if (error)
|
1998-02-18 09:28:47 +00:00
|
|
|
return (error);
|
|
|
|
/*
|
|
|
|
* If the cluster was successfully marked free, then update
|
|
|
|
* the count of free clusters, and turn off the "allocated"
|
|
|
|
* bit in the "in use" cluster bit map.
|
|
|
|
*/
|
2010-02-28 17:13:59 +00:00
|
|
|
MSDOSFS_LOCK_MP(pmp);
|
|
|
|
usemap_free(pmp, cluster);
|
|
|
|
MSDOSFS_UNLOCK_MP(pmp);
|
1998-02-18 09:28:47 +00:00
|
|
|
if (oldcnp)
|
|
|
|
*oldcnp = oldcn;
|
|
|
|
return (0);
|
1994-09-19 15:41:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2017-05-21 19:29:28 +00:00
|
|
|
* Get or Set or 'Get and Set' the cluster'th entry in the FAT.
|
1994-09-19 15:41:57 +00:00
|
|
|
*
|
2017-05-21 19:29:28 +00:00
|
|
|
* function - whether to get or set a FAT entry
|
1994-09-19 15:41:57 +00:00
|
|
|
* pmp - address of the msdosfsmount structure for the filesystem
|
2017-05-21 19:29:28 +00:00
|
|
|
* whose FAT is to be manipulated.
|
1994-09-19 15:41:57 +00:00
|
|
|
* cn - which cluster is of interest
|
|
|
|
* oldcontents - address of a word that is to receive the contents of the
|
|
|
|
* cluster'th entry if this is a get function
|
|
|
|
* newcontents - the new value to be written into the cluster'th element of
|
2017-05-21 19:29:28 +00:00
|
|
|
* the FAT if this is a set function.
|
1995-05-30 08:16:23 +00:00
|
|
|
*
|
2017-05-21 19:29:28 +00:00
|
|
|
* This function can also be used to free a cluster by setting the FAT entry
|
1994-09-19 15:41:57 +00:00
|
|
|
* for a cluster to 0.
|
1995-05-30 08:16:23 +00:00
|
|
|
*
|
2017-05-21 19:29:28 +00:00
|
|
|
* All copies of the FAT are updated if this is a set function. NOTE: If
|
1994-09-19 15:41:57 +00:00
|
|
|
* fatentry() marks a cluster as free it does not update the inusemap in
|
|
|
|
* the msdosfsmount structure. This is left to the caller.
|
|
|
|
*/
|
|
|
|
int
|
2015-01-12 21:55:48 +00:00
|
|
|
fatentry(int function, struct msdosfsmount *pmp, u_long cn, u_long *oldcontents,
|
|
|
|
u_long newcontents)
|
1994-09-19 15:41:57 +00:00
|
|
|
{
|
|
|
|
int error;
|
|
|
|
u_long readcn;
|
|
|
|
u_long bn, bo, bsize, byteoffset;
|
|
|
|
struct buf *bp;
|
|
|
|
|
1998-02-18 09:28:47 +00:00
|
|
|
#ifdef MSDOSFS_DEBUG
|
|
|
|
printf("fatentry(func %d, pmp %p, clust %lu, oldcon %p, newcon %lx)\n",
|
2007-08-07 03:22:10 +00:00
|
|
|
function, pmp, cn, oldcontents, newcontents);
|
1998-02-18 09:28:47 +00:00
|
|
|
#endif
|
1994-09-19 15:41:57 +00:00
|
|
|
|
|
|
|
#ifdef DIAGNOSTIC
|
|
|
|
/*
|
|
|
|
* Be sure they asked us to do something.
|
|
|
|
*/
|
|
|
|
if ((function & (FAT_SET | FAT_GET)) == 0) {
|
2011-11-22 13:30:36 +00:00
|
|
|
#ifdef MSDOSFS_DEBUG
|
1994-09-19 15:41:57 +00:00
|
|
|
printf("fatentry(): function code doesn't specify get or set\n");
|
2011-11-22 13:30:36 +00:00
|
|
|
#endif
|
1998-02-18 09:28:47 +00:00
|
|
|
return (EINVAL);
|
1994-09-19 15:41:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If they asked us to return a cluster number but didn't tell us
|
|
|
|
* where to put it, give them an error.
|
|
|
|
*/
|
|
|
|
if ((function & FAT_GET) && oldcontents == NULL) {
|
2011-11-22 13:30:36 +00:00
|
|
|
#ifdef MSDOSFS_DEBUG
|
1994-09-19 15:41:57 +00:00
|
|
|
printf("fatentry(): get function with no place to put result\n");
|
2011-11-22 13:30:36 +00:00
|
|
|
#endif
|
1998-02-18 09:28:47 +00:00
|
|
|
return (EINVAL);
|
1994-09-19 15:41:57 +00:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Be sure the requested cluster is in the filesystem.
|
|
|
|
*/
|
|
|
|
if (cn < CLUST_FIRST || cn > pmp->pm_maxcluster)
|
1998-02-18 09:28:47 +00:00
|
|
|
return (EINVAL);
|
1994-09-19 15:41:57 +00:00
|
|
|
|
|
|
|
byteoffset = FATOFS(pmp, cn);
|
|
|
|
fatblock(pmp, byteoffset, &bn, &bsize, &bo);
|
1994-09-27 20:42:59 +00:00
|
|
|
error = bread(pmp->pm_devvp, bn, bsize, NOCRED, &bp);
|
1998-02-18 09:28:47 +00:00
|
|
|
if (error) {
|
|
|
|
brelse(bp);
|
|
|
|
return (error);
|
|
|
|
}
|
1995-05-30 08:16:23 +00:00
|
|
|
|
1994-09-19 15:41:57 +00:00
|
|
|
if (function & FAT_GET) {
|
1998-02-18 09:28:47 +00:00
|
|
|
if (FAT32(pmp))
|
|
|
|
readcn = getulong(&bp->b_data[bo]);
|
|
|
|
else
|
|
|
|
readcn = getushort(&bp->b_data[bo]);
|
|
|
|
if (FAT12(pmp) & (cn & 1))
|
|
|
|
readcn >>= 4;
|
|
|
|
readcn &= pmp->pm_fatmask;
|
2017-05-21 19:29:28 +00:00
|
|
|
/* map reserved FAT entries to same values for all FATs */
|
1998-02-18 09:28:47 +00:00
|
|
|
if ((readcn | ~pmp->pm_fatmask) >= CLUST_RSRVD)
|
|
|
|
readcn |= ~pmp->pm_fatmask;
|
1994-09-19 15:41:57 +00:00
|
|
|
*oldcontents = readcn;
|
|
|
|
}
|
|
|
|
if (function & FAT_SET) {
|
1998-02-18 09:28:47 +00:00
|
|
|
switch (pmp->pm_fatmask) {
|
|
|
|
case FAT12_MASK:
|
1994-09-19 15:41:57 +00:00
|
|
|
readcn = getushort(&bp->b_data[bo]);
|
|
|
|
if (cn & 1) {
|
|
|
|
readcn &= 0x000f;
|
|
|
|
readcn |= newcontents << 4;
|
|
|
|
} else {
|
|
|
|
readcn &= 0xf000;
|
|
|
|
readcn |= newcontents & 0xfff;
|
|
|
|
}
|
|
|
|
putushort(&bp->b_data[bo], readcn);
|
1998-02-18 09:28:47 +00:00
|
|
|
break;
|
|
|
|
case FAT16_MASK:
|
1994-09-19 15:41:57 +00:00
|
|
|
putushort(&bp->b_data[bo], newcontents);
|
1998-02-18 09:28:47 +00:00
|
|
|
break;
|
|
|
|
case FAT32_MASK:
|
|
|
|
/*
|
|
|
|
* According to spec we have to retain the
|
2017-05-21 19:29:28 +00:00
|
|
|
* high order bits of the FAT entry.
|
1998-02-18 09:28:47 +00:00
|
|
|
*/
|
|
|
|
readcn = getulong(&bp->b_data[bo]);
|
|
|
|
readcn &= ~FAT32_MASK;
|
|
|
|
readcn |= newcontents & FAT32_MASK;
|
|
|
|
putulong(&bp->b_data[bo], readcn);
|
|
|
|
break;
|
|
|
|
}
|
1994-09-19 15:41:57 +00:00
|
|
|
updatefats(pmp, bp, bn);
|
|
|
|
bp = NULL;
|
|
|
|
pmp->pm_fmod = 1;
|
|
|
|
}
|
|
|
|
if (bp)
|
|
|
|
brelse(bp);
|
1998-02-18 09:28:47 +00:00
|
|
|
return (0);
|
1994-09-19 15:41:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Update a contiguous cluster chain
|
|
|
|
*
|
|
|
|
* pmp - mount point
|
|
|
|
* start - first cluster of chain
|
|
|
|
* count - number of clusters in chain
|
2017-05-21 19:29:28 +00:00
|
|
|
* fillwith - what to write into FAT entry of last cluster
|
1994-09-19 15:41:57 +00:00
|
|
|
*/
|
|
|
|
static int
|
2015-01-09 14:50:08 +00:00
|
|
|
fatchain(struct msdosfsmount *pmp, u_long start, u_long count, u_long fillwith)
|
1994-09-19 15:41:57 +00:00
|
|
|
{
|
|
|
|
int error;
|
|
|
|
u_long bn, bo, bsize, byteoffset, readcn, newc;
|
|
|
|
struct buf *bp;
|
1995-05-30 08:16:23 +00:00
|
|
|
|
1994-09-19 15:41:57 +00:00
|
|
|
#ifdef MSDOSFS_DEBUG
|
1998-02-18 09:28:47 +00:00
|
|
|
printf("fatchain(pmp %p, start %lu, count %lu, fillwith %lx)\n",
|
|
|
|
pmp, start, count, fillwith);
|
1994-09-19 15:41:57 +00:00
|
|
|
#endif
|
|
|
|
/*
|
|
|
|
* Be sure the clusters are in the filesystem.
|
|
|
|
*/
|
|
|
|
if (start < CLUST_FIRST || start + count - 1 > pmp->pm_maxcluster)
|
1998-02-18 09:28:47 +00:00
|
|
|
return (EINVAL);
|
1995-05-30 08:16:23 +00:00
|
|
|
|
1994-09-19 15:41:57 +00:00
|
|
|
while (count > 0) {
|
|
|
|
byteoffset = FATOFS(pmp, start);
|
|
|
|
fatblock(pmp, byteoffset, &bn, &bsize, &bo);
|
1994-09-27 20:42:59 +00:00
|
|
|
error = bread(pmp->pm_devvp, bn, bsize, NOCRED, &bp);
|
1998-02-18 09:28:47 +00:00
|
|
|
if (error) {
|
|
|
|
brelse(bp);
|
|
|
|
return (error);
|
|
|
|
}
|
1994-09-19 15:41:57 +00:00
|
|
|
while (count > 0) {
|
|
|
|
start++;
|
|
|
|
newc = --count > 0 ? start : fillwith;
|
1998-02-18 09:28:47 +00:00
|
|
|
switch (pmp->pm_fatmask) {
|
|
|
|
case FAT12_MASK:
|
1994-09-19 15:41:57 +00:00
|
|
|
readcn = getushort(&bp->b_data[bo]);
|
|
|
|
if (start & 1) {
|
|
|
|
readcn &= 0xf000;
|
|
|
|
readcn |= newc & 0xfff;
|
|
|
|
} else {
|
|
|
|
readcn &= 0x000f;
|
|
|
|
readcn |= newc << 4;
|
|
|
|
}
|
|
|
|
putushort(&bp->b_data[bo], readcn);
|
|
|
|
bo++;
|
|
|
|
if (!(start & 1))
|
|
|
|
bo++;
|
1998-02-18 09:28:47 +00:00
|
|
|
break;
|
|
|
|
case FAT16_MASK:
|
1994-09-19 15:41:57 +00:00
|
|
|
putushort(&bp->b_data[bo], newc);
|
|
|
|
bo += 2;
|
1998-02-18 09:28:47 +00:00
|
|
|
break;
|
|
|
|
case FAT32_MASK:
|
|
|
|
readcn = getulong(&bp->b_data[bo]);
|
|
|
|
readcn &= ~pmp->pm_fatmask;
|
|
|
|
readcn |= newc & pmp->pm_fatmask;
|
|
|
|
putulong(&bp->b_data[bo], readcn);
|
|
|
|
bo += 4;
|
|
|
|
break;
|
1994-09-19 15:41:57 +00:00
|
|
|
}
|
|
|
|
if (bo >= bsize)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
updatefats(pmp, bp, bn);
|
|
|
|
}
|
|
|
|
pmp->pm_fmod = 1;
|
1998-02-18 09:28:47 +00:00
|
|
|
return (0);
|
1994-09-19 15:41:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check the length of a free cluster chain starting at start.
|
|
|
|
*
|
|
|
|
* pmp - mount point
|
|
|
|
* start - start of chain
|
|
|
|
* count - maximum interesting length
|
|
|
|
*/
|
1995-11-07 14:06:45 +00:00
|
|
|
static int
|
2015-01-09 14:50:08 +00:00
|
|
|
chainlength(struct msdosfsmount *pmp, u_long start, u_long count)
|
1994-09-19 15:41:57 +00:00
|
|
|
{
|
|
|
|
u_long idx, max_idx;
|
|
|
|
u_int map;
|
|
|
|
u_long len;
|
1995-05-30 08:16:23 +00:00
|
|
|
|
2010-02-28 17:13:59 +00:00
|
|
|
MSDOSFS_ASSERT_MP_LOCKED(pmp);
|
|
|
|
|
2016-10-28 11:34:32 +00:00
|
|
|
if (start > pmp->pm_maxcluster)
|
|
|
|
return (0);
|
1994-09-19 15:41:57 +00:00
|
|
|
max_idx = pmp->pm_maxcluster / N_INUSEBITS;
|
|
|
|
idx = start / N_INUSEBITS;
|
|
|
|
start %= N_INUSEBITS;
|
|
|
|
map = pmp->pm_inusemap[idx];
|
|
|
|
map &= ~((1 << start) - 1);
|
|
|
|
if (map) {
|
|
|
|
len = ffs(map) - 1 - start;
|
2016-10-28 11:34:32 +00:00
|
|
|
len = MIN(len, count);
|
|
|
|
if (start + len > pmp->pm_maxcluster)
|
|
|
|
len = pmp->pm_maxcluster - start + 1;
|
|
|
|
return (len);
|
1994-09-19 15:41:57 +00:00
|
|
|
}
|
|
|
|
len = N_INUSEBITS - start;
|
2016-10-28 11:34:32 +00:00
|
|
|
if (len >= count) {
|
|
|
|
len = count;
|
|
|
|
if (start + len > pmp->pm_maxcluster)
|
|
|
|
len = pmp->pm_maxcluster - start + 1;
|
|
|
|
return (len);
|
|
|
|
}
|
1994-09-19 15:41:57 +00:00
|
|
|
while (++idx <= max_idx) {
|
|
|
|
if (len >= count)
|
|
|
|
break;
|
1994-09-27 20:42:59 +00:00
|
|
|
map = pmp->pm_inusemap[idx];
|
|
|
|
if (map) {
|
2007-08-07 03:22:10 +00:00
|
|
|
len += ffs(map) - 1;
|
1994-09-19 15:41:57 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
len += N_INUSEBITS;
|
|
|
|
}
|
2016-10-28 11:34:32 +00:00
|
|
|
len = MIN(len, count);
|
|
|
|
if (start + len > pmp->pm_maxcluster)
|
|
|
|
len = pmp->pm_maxcluster - start + 1;
|
|
|
|
return (len);
|
1994-09-19 15:41:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Allocate contigous free clusters.
|
|
|
|
*
|
|
|
|
* pmp - mount point.
|
|
|
|
* start - start of cluster chain.
|
|
|
|
* count - number of clusters to allocate.
|
2017-05-21 19:29:28 +00:00
|
|
|
* fillwith - put this value into the FAT entry for the
|
1994-09-19 15:41:57 +00:00
|
|
|
* last allocated cluster.
|
|
|
|
* retcluster - put the first allocated cluster's number here.
|
|
|
|
* got - how many clusters were actually allocated.
|
|
|
|
*/
|
1995-11-07 14:06:45 +00:00
|
|
|
static int
|
2015-01-09 14:50:08 +00:00
|
|
|
chainalloc(struct msdosfsmount *pmp, u_long start, u_long count,
|
|
|
|
u_long fillwith, u_long *retcluster, u_long *got)
|
1994-09-19 15:41:57 +00:00
|
|
|
{
|
|
|
|
int error;
|
1998-02-18 09:28:47 +00:00
|
|
|
u_long cl, n;
|
|
|
|
|
2010-02-28 17:13:59 +00:00
|
|
|
MSDOSFS_ASSERT_MP_LOCKED(pmp);
|
2015-04-05 21:08:04 +00:00
|
|
|
KASSERT((pmp->pm_flags & MSDOSFSMNT_RONLY) == 0,
|
|
|
|
("chainalloc on ro msdosfs mount"));
|
2010-02-28 17:13:59 +00:00
|
|
|
|
1998-02-18 09:28:47 +00:00
|
|
|
for (cl = start, n = count; n-- > 0;)
|
|
|
|
usemap_alloc(pmp, cl++);
|
2013-02-17 20:35:54 +00:00
|
|
|
pmp->pm_nxtfree = start + count;
|
|
|
|
if (pmp->pm_nxtfree > pmp->pm_maxcluster)
|
|
|
|
pmp->pm_nxtfree = CLUST_FIRST;
|
|
|
|
pmp->pm_flags |= MSDOSFS_FSIMOD;
|
1994-09-19 15:41:57 +00:00
|
|
|
error = fatchain(pmp, start, count, fillwith);
|
2016-10-28 11:26:44 +00:00
|
|
|
if (error != 0) {
|
|
|
|
for (cl = start, n = count; n-- > 0;)
|
|
|
|
usemap_free(pmp, cl++);
|
1998-02-18 09:28:47 +00:00
|
|
|
return (error);
|
2016-10-28 11:26:44 +00:00
|
|
|
}
|
1994-09-19 15:41:57 +00:00
|
|
|
#ifdef MSDOSFS_DEBUG
|
1998-02-18 09:28:47 +00:00
|
|
|
printf("clusteralloc(): allocated cluster chain at %lu (%lu clusters)\n",
|
|
|
|
start, count);
|
1994-09-19 15:41:57 +00:00
|
|
|
#endif
|
1998-02-18 09:28:47 +00:00
|
|
|
if (retcluster)
|
|
|
|
*retcluster = start;
|
|
|
|
if (got)
|
|
|
|
*got = count;
|
|
|
|
return (0);
|
1994-09-19 15:41:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Allocate contiguous free clusters.
|
|
|
|
*
|
|
|
|
* pmp - mount point.
|
|
|
|
* start - preferred start of cluster chain.
|
|
|
|
* count - number of clusters requested.
|
2017-05-21 19:29:28 +00:00
|
|
|
* fillwith - put this value into the FAT entry for the
|
1994-09-19 15:41:57 +00:00
|
|
|
* last allocated cluster.
|
|
|
|
* retcluster - put the first allocated cluster's number here.
|
|
|
|
* got - how many clusters were actually allocated.
|
|
|
|
*/
|
|
|
|
int
|
2010-02-28 17:13:59 +00:00
|
|
|
clusteralloc(struct msdosfsmount *pmp, u_long start, u_long count,
|
|
|
|
u_long fillwith, u_long *retcluster, u_long *got)
|
|
|
|
{
|
|
|
|
int error;
|
|
|
|
|
|
|
|
MSDOSFS_LOCK_MP(pmp);
|
|
|
|
error = clusteralloc1(pmp, start, count, fillwith, retcluster, got);
|
|
|
|
MSDOSFS_UNLOCK_MP(pmp);
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
clusteralloc1(struct msdosfsmount *pmp, u_long start, u_long count,
|
|
|
|
u_long fillwith, u_long *retcluster, u_long *got)
|
1994-09-19 15:41:57 +00:00
|
|
|
{
|
|
|
|
u_long idx;
|
1998-02-18 09:28:47 +00:00
|
|
|
u_long len, newst, foundl, cn, l;
|
|
|
|
u_long foundcn = 0; /* XXX: foundcn could be used unititialized */
|
1994-09-19 15:41:57 +00:00
|
|
|
u_int map;
|
1995-05-30 08:16:23 +00:00
|
|
|
|
2010-02-28 17:13:59 +00:00
|
|
|
MSDOSFS_ASSERT_MP_LOCKED(pmp);
|
|
|
|
|
1994-09-19 15:41:57 +00:00
|
|
|
#ifdef MSDOSFS_DEBUG
|
2007-08-07 03:22:10 +00:00
|
|
|
printf("clusteralloc(): find %lu clusters\n", count);
|
1994-09-19 15:41:57 +00:00
|
|
|
#endif
|
|
|
|
if (start) {
|
|
|
|
if ((len = chainlength(pmp, start, count)) >= count)
|
1998-02-18 09:28:47 +00:00
|
|
|
return (chainalloc(pmp, start, count, fillwith, retcluster, got));
|
2007-08-07 03:22:10 +00:00
|
|
|
} else
|
1994-09-19 15:41:57 +00:00
|
|
|
len = 0;
|
1995-05-30 08:16:23 +00:00
|
|
|
|
Don't use almost perfectly pessimal cluster allocation. Allocation
of the the first cluster in a file (and, if the allocation cannot be
continued contiguously, for subsequent clusters in a file) was randomized
in an attempt to leave space for contiguous allocation of subsequent
clusters in each file when there are multiple writers. This reduced
internal fragmentation by a few percent, but it increased external
fragmentation by up to a few thousand percent.
Use simple sequential allocation instead. Actually maintain the fsinfo
sequence index for this. The read and write of this index from/to
disk still have many non-critical bugs, but we now write an index that
has something to do with our allocations instead of being modified
garbage. If there is no fsinfo on the disk, then we maintain the index
internally and don't go near the bugs for writing it.
Allocating the first free cluster gives a layout that is almost as good
(better in some cases), but takes too much CPU if the FAT is large and
the first free cluster is not near the beginning.
The effect of this change for untar and tar of a slightly reduced copy
of /usr/src on a new file system was:
Before (msdosfs 4K-clusters):
untar: 459.57 real untar from cached file (actually a pipe)
tar: 342.50 real tar from uncached tree to /dev/zero
Before (ffs2 soft updates 4K-blocks 4K-frags)
untar: 39.18 real
tar: 29.94 real
Before (ffs2 soft updates 16K-blocks 2K-frags)
untar: 31.35 real
tar: 18.30 real
After (msdosfs 4K-clusters):
untar 54.83 real
tar 16.18 real
All of these times can be improved further.
With multiple concurrent writers or readers (especially readers), the
improvement is smaller, but I couldn't find any case where it is
negative. 342 seconds for tarring up about 342 MB on a ~47MB/S partition
is just hard to unimprove on. (This operation would take about 7.3
seconds with reasonably localized allocation and perfect read-ahead.)
However, for active file systems, 342 seconds is closer to normal than
the 16+ seconds above or the 11 seconds with other changes (best I've
measured -- won easily by msdosfs!). E.g., my active /usr/src on ffs1
is quite old and fragmented, so reading to prepare for the above
benchmark takes about 6 times longer than reading back the fresh copies
of it.
Approved by: re (kensmith)
2007-07-10 13:20:24 +00:00
|
|
|
newst = pmp->pm_nxtfree;
|
1994-09-19 15:41:57 +00:00
|
|
|
foundl = 0;
|
1995-05-30 08:16:23 +00:00
|
|
|
|
1994-09-19 15:41:57 +00:00
|
|
|
for (cn = newst; cn <= pmp->pm_maxcluster;) {
|
|
|
|
idx = cn / N_INUSEBITS;
|
|
|
|
map = pmp->pm_inusemap[idx];
|
|
|
|
map |= (1 << (cn % N_INUSEBITS)) - 1;
|
2016-10-28 11:23:36 +00:00
|
|
|
if (map != FULL_RUN) {
|
|
|
|
cn = idx * N_INUSEBITS + ffs(map ^ FULL_RUN) - 1;
|
1994-09-19 15:41:57 +00:00
|
|
|
if ((l = chainlength(pmp, cn, count)) >= count)
|
1998-02-18 09:28:47 +00:00
|
|
|
return (chainalloc(pmp, cn, count, fillwith, retcluster, got));
|
1994-09-19 15:41:57 +00:00
|
|
|
if (l > foundl) {
|
|
|
|
foundcn = cn;
|
|
|
|
foundl = l;
|
|
|
|
}
|
|
|
|
cn += l + 1;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
cn += N_INUSEBITS - cn % N_INUSEBITS;
|
|
|
|
}
|
|
|
|
for (cn = 0; cn < newst;) {
|
|
|
|
idx = cn / N_INUSEBITS;
|
|
|
|
map = pmp->pm_inusemap[idx];
|
|
|
|
map |= (1 << (cn % N_INUSEBITS)) - 1;
|
2016-10-28 11:23:36 +00:00
|
|
|
if (map != FULL_RUN) {
|
|
|
|
cn = idx * N_INUSEBITS + ffs(map ^ FULL_RUN) - 1;
|
1994-09-19 15:41:57 +00:00
|
|
|
if ((l = chainlength(pmp, cn, count)) >= count)
|
1998-02-18 09:28:47 +00:00
|
|
|
return (chainalloc(pmp, cn, count, fillwith, retcluster, got));
|
1994-09-19 15:41:57 +00:00
|
|
|
if (l > foundl) {
|
|
|
|
foundcn = cn;
|
|
|
|
foundl = l;
|
|
|
|
}
|
|
|
|
cn += l + 1;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
cn += N_INUSEBITS - cn % N_INUSEBITS;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!foundl)
|
1998-02-18 09:28:47 +00:00
|
|
|
return (ENOSPC);
|
1994-09-19 15:41:57 +00:00
|
|
|
|
|
|
|
if (len)
|
1998-02-18 09:28:47 +00:00
|
|
|
return (chainalloc(pmp, start, len, fillwith, retcluster, got));
|
1994-09-19 15:41:57 +00:00
|
|
|
else
|
1998-02-18 09:28:47 +00:00
|
|
|
return (chainalloc(pmp, foundcn, foundl, fillwith, retcluster, got));
|
1994-09-19 15:41:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Free a chain of clusters.
|
|
|
|
*
|
|
|
|
* pmp - address of the msdosfs mount structure for the filesystem
|
|
|
|
* containing the cluster chain to be freed.
|
|
|
|
* startcluster - number of the 1st cluster in the chain of clusters to be
|
|
|
|
* freed.
|
|
|
|
*/
|
|
|
|
int
|
2015-01-09 14:50:08 +00:00
|
|
|
freeclusterchain(struct msdosfsmount *pmp, u_long cluster)
|
1994-09-19 15:41:57 +00:00
|
|
|
{
|
1998-02-18 09:28:47 +00:00
|
|
|
int error;
|
1994-09-19 15:41:57 +00:00
|
|
|
struct buf *bp = NULL;
|
|
|
|
u_long bn, bo, bsize, byteoffset;
|
|
|
|
u_long readcn, lbn = -1;
|
1995-05-30 08:16:23 +00:00
|
|
|
|
2010-02-28 17:13:59 +00:00
|
|
|
MSDOSFS_LOCK_MP(pmp);
|
1994-09-19 15:41:57 +00:00
|
|
|
while (cluster >= CLUST_FIRST && cluster <= pmp->pm_maxcluster) {
|
|
|
|
byteoffset = FATOFS(pmp, cluster);
|
|
|
|
fatblock(pmp, byteoffset, &bn, &bsize, &bo);
|
|
|
|
if (lbn != bn) {
|
|
|
|
if (bp)
|
1995-02-10 18:39:45 +00:00
|
|
|
updatefats(pmp, bp, lbn);
|
1994-09-27 20:42:59 +00:00
|
|
|
error = bread(pmp->pm_devvp, bn, bsize, NOCRED, &bp);
|
1998-02-18 09:28:47 +00:00
|
|
|
if (error) {
|
|
|
|
brelse(bp);
|
2010-02-28 17:13:59 +00:00
|
|
|
MSDOSFS_UNLOCK_MP(pmp);
|
1998-02-18 09:28:47 +00:00
|
|
|
return (error);
|
|
|
|
}
|
1994-09-19 15:41:57 +00:00
|
|
|
lbn = bn;
|
|
|
|
}
|
|
|
|
usemap_free(pmp, cluster);
|
1998-02-18 09:28:47 +00:00
|
|
|
switch (pmp->pm_fatmask) {
|
|
|
|
case FAT12_MASK:
|
|
|
|
readcn = getushort(&bp->b_data[bo]);
|
1994-09-19 15:41:57 +00:00
|
|
|
if (cluster & 1) {
|
|
|
|
cluster = readcn >> 4;
|
|
|
|
readcn &= 0x000f;
|
|
|
|
readcn |= MSDOSFSFREE << 4;
|
|
|
|
} else {
|
|
|
|
cluster = readcn;
|
|
|
|
readcn &= 0xf000;
|
|
|
|
readcn |= MSDOSFSFREE & 0xfff;
|
|
|
|
}
|
|
|
|
putushort(&bp->b_data[bo], readcn);
|
1998-02-18 09:28:47 +00:00
|
|
|
break;
|
|
|
|
case FAT16_MASK:
|
|
|
|
cluster = getushort(&bp->b_data[bo]);
|
1994-09-19 15:41:57 +00:00
|
|
|
putushort(&bp->b_data[bo], MSDOSFSFREE);
|
1998-02-18 09:28:47 +00:00
|
|
|
break;
|
|
|
|
case FAT32_MASK:
|
|
|
|
cluster = getulong(&bp->b_data[bo]);
|
|
|
|
putulong(&bp->b_data[bo],
|
|
|
|
(MSDOSFSFREE & FAT32_MASK) | (cluster & ~FAT32_MASK));
|
|
|
|
break;
|
1994-09-19 15:41:57 +00:00
|
|
|
}
|
1998-02-18 09:28:47 +00:00
|
|
|
cluster &= pmp->pm_fatmask;
|
|
|
|
if ((cluster | ~pmp->pm_fatmask) >= CLUST_RSRVD)
|
|
|
|
cluster |= pmp->pm_fatmask;
|
1994-09-19 15:41:57 +00:00
|
|
|
}
|
|
|
|
if (bp)
|
|
|
|
updatefats(pmp, bp, bn);
|
2010-02-28 17:13:59 +00:00
|
|
|
MSDOSFS_UNLOCK_MP(pmp);
|
1998-02-18 09:28:47 +00:00
|
|
|
return (0);
|
1994-09-19 15:41:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2017-05-21 19:29:28 +00:00
|
|
|
* Read in FAT blocks looking for free clusters. For every free cluster
|
1994-09-19 15:41:57 +00:00
|
|
|
* found turn off its corresponding bit in the pm_inusemap.
|
|
|
|
*/
|
|
|
|
int
|
2015-01-09 14:50:08 +00:00
|
|
|
fillinusemap(struct msdosfsmount *pmp)
|
1994-09-19 15:41:57 +00:00
|
|
|
{
|
2017-08-28 21:04:56 +00:00
|
|
|
struct buf *bp;
|
|
|
|
u_long bn, bo, bsize, byteoffset, cn, readcn;
|
1994-09-19 15:41:57 +00:00
|
|
|
int error;
|
|
|
|
|
2010-02-28 17:13:59 +00:00
|
|
|
MSDOSFS_ASSERT_MP_LOCKED(pmp);
|
2017-08-28 21:04:56 +00:00
|
|
|
bp = NULL;
|
2010-02-28 17:13:59 +00:00
|
|
|
|
1994-09-19 15:41:57 +00:00
|
|
|
/*
|
2017-05-21 19:29:28 +00:00
|
|
|
* Mark all clusters in use, we mark the free ones in the FAT scan
|
1994-09-19 15:41:57 +00:00
|
|
|
* loop further down.
|
|
|
|
*/
|
|
|
|
for (cn = 0; cn < (pmp->pm_maxcluster + N_INUSEBITS) / N_INUSEBITS; cn++)
|
2016-10-28 11:23:36 +00:00
|
|
|
pmp->pm_inusemap[cn] = FULL_RUN;
|
1994-09-19 15:41:57 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Figure how many free clusters are in the filesystem by ripping
|
2017-05-21 19:29:28 +00:00
|
|
|
* through the FAT counting the number of entries whose content is
|
1994-09-19 15:41:57 +00:00
|
|
|
* zero. These represent free clusters.
|
|
|
|
*/
|
|
|
|
pmp->pm_freeclustercount = 0;
|
2017-08-28 20:52:32 +00:00
|
|
|
for (cn = 0; cn <= pmp->pm_maxcluster; cn++) {
|
1994-09-19 15:41:57 +00:00
|
|
|
byteoffset = FATOFS(pmp, cn);
|
|
|
|
bo = byteoffset % pmp->pm_fatblocksize;
|
2017-08-28 20:52:32 +00:00
|
|
|
if (bo == 0) {
|
1994-09-19 15:41:57 +00:00
|
|
|
/* Read new FAT block */
|
2017-08-28 20:52:32 +00:00
|
|
|
if (bp != NULL)
|
1994-09-19 15:41:57 +00:00
|
|
|
brelse(bp);
|
|
|
|
fatblock(pmp, byteoffset, &bn, &bsize, NULL);
|
|
|
|
error = bread(pmp->pm_devvp, bn, bsize, NOCRED, &bp);
|
2017-08-28 20:52:32 +00:00
|
|
|
if (error != 0)
|
1998-02-18 09:28:47 +00:00
|
|
|
return (error);
|
1994-09-19 15:41:57 +00:00
|
|
|
}
|
1998-02-18 09:28:47 +00:00
|
|
|
if (FAT32(pmp))
|
|
|
|
readcn = getulong(&bp->b_data[bo]);
|
|
|
|
else
|
|
|
|
readcn = getushort(&bp->b_data[bo]);
|
|
|
|
if (FAT12(pmp) && (cn & 1))
|
|
|
|
readcn >>= 4;
|
|
|
|
readcn &= pmp->pm_fatmask;
|
1994-09-19 15:41:57 +00:00
|
|
|
|
2017-08-28 20:52:32 +00:00
|
|
|
/*
|
|
|
|
* Check if the FAT ID matches the BPB's media descriptor and
|
|
|
|
* all other bits are set to 1.
|
|
|
|
*/
|
|
|
|
if (cn == 0 && readcn != ((pmp->pm_fatmask & 0xffffff00) |
|
|
|
|
pmp->pm_bpb.bpbMedia)) {
|
|
|
|
#ifdef MSDOSFS_DEBUG
|
|
|
|
printf("mountmsdosfs(): Media descriptor in BPB"
|
|
|
|
"does not match FAT ID\n");
|
|
|
|
#endif
|
|
|
|
brelse(bp);
|
|
|
|
return (EINVAL);
|
|
|
|
} else if (readcn == CLUST_FREE)
|
1994-09-19 15:41:57 +00:00
|
|
|
usemap_free(pmp, cn);
|
|
|
|
}
|
2010-02-14 12:10:49 +00:00
|
|
|
if (bp != NULL)
|
|
|
|
brelse(bp);
|
2016-10-28 11:34:32 +00:00
|
|
|
|
|
|
|
for (cn = pmp->pm_maxcluster + 1; cn < (pmp->pm_maxcluster +
|
|
|
|
N_INUSEBITS) / N_INUSEBITS; cn++)
|
|
|
|
pmp->pm_inusemap[cn / N_INUSEBITS] |= 1 << (cn % N_INUSEBITS);
|
|
|
|
|
1998-02-18 09:28:47 +00:00
|
|
|
return (0);
|
1994-09-19 15:41:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Allocate a new cluster and chain it onto the end of the file.
|
|
|
|
*
|
|
|
|
* dep - the file to extend
|
|
|
|
* count - number of clusters to allocate
|
|
|
|
* bpp - where to return the address of the buf header for the first new
|
|
|
|
* file block
|
|
|
|
* ncp - where to put cluster number of the first newly allocated cluster
|
|
|
|
* If this pointer is 0, do not return the cluster number.
|
|
|
|
* flags - see fat.h
|
|
|
|
*
|
|
|
|
* NOTE: This function is not responsible for turning on the DE_UPDATE bit of
|
|
|
|
* the de_flag field of the denode and it does not change the de_FileSize
|
|
|
|
* field. This is left for the caller to do.
|
|
|
|
*/
|
|
|
|
int
|
2015-01-09 14:50:08 +00:00
|
|
|
extendfile(struct denode *dep, u_long count, struct buf **bpp, u_long *ncp,
|
|
|
|
int flags)
|
1994-09-19 15:41:57 +00:00
|
|
|
{
|
1998-02-18 09:28:47 +00:00
|
|
|
int error;
|
1994-09-19 15:41:57 +00:00
|
|
|
u_long frcn;
|
|
|
|
u_long cn, got;
|
|
|
|
struct msdosfsmount *pmp = dep->de_pmp;
|
|
|
|
struct buf *bp;
|
2002-03-15 18:49:47 +00:00
|
|
|
daddr_t blkno;
|
1995-05-30 08:16:23 +00:00
|
|
|
|
1994-09-19 15:41:57 +00:00
|
|
|
/*
|
|
|
|
* Don't try to extend the root directory
|
|
|
|
*/
|
1998-02-18 09:28:47 +00:00
|
|
|
if (dep->de_StartCluster == MSDOSFSROOT
|
|
|
|
&& (dep->de_Attributes & ATTR_DIRECTORY)) {
|
2011-11-22 13:30:36 +00:00
|
|
|
#ifdef MSDOSFS_DEBUG
|
1994-09-19 15:41:57 +00:00
|
|
|
printf("extendfile(): attempt to extend root directory\n");
|
2011-11-22 13:30:36 +00:00
|
|
|
#endif
|
1998-02-18 09:28:47 +00:00
|
|
|
return (ENOSPC);
|
1994-09-19 15:41:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If the "file's last cluster" cache entry is empty, and the file
|
|
|
|
* is not empty, then fill the cache entry by calling pcbmap().
|
|
|
|
*/
|
|
|
|
if (dep->de_fc[FC_LASTFC].fc_frcn == FCE_EMPTY &&
|
|
|
|
dep->de_StartCluster != 0) {
|
1998-02-18 09:28:47 +00:00
|
|
|
error = pcbmap(dep, 0xffff, 0, &cn, 0);
|
1994-09-19 15:41:57 +00:00
|
|
|
/* we expect it to return E2BIG */
|
|
|
|
if (error != E2BIG)
|
1998-02-18 09:28:47 +00:00
|
|
|
return (error);
|
1994-09-19 15:41:57 +00:00
|
|
|
}
|
1995-05-30 08:16:23 +00:00
|
|
|
|
2007-10-25 08:23:08 +00:00
|
|
|
dep->de_fc[FC_NEXTTOLASTFC].fc_frcn =
|
|
|
|
dep->de_fc[FC_LASTFC].fc_frcn;
|
|
|
|
dep->de_fc[FC_NEXTTOLASTFC].fc_fsrcn =
|
|
|
|
dep->de_fc[FC_LASTFC].fc_fsrcn;
|
1994-09-19 15:41:57 +00:00
|
|
|
while (count > 0) {
|
|
|
|
/*
|
1998-02-18 09:28:47 +00:00
|
|
|
* Allocate a new cluster chain and cat onto the end of the
|
2017-06-22 01:11:20 +00:00
|
|
|
* file. If the file is empty we make de_StartCluster point
|
|
|
|
* to the new block. Note that de_StartCluster being 0 is
|
|
|
|
* sufficient to be sure the file is empty since we exclude
|
|
|
|
* attempts to extend the root directory above, and the root
|
|
|
|
* dir is the only file with a startcluster of 0 that has
|
|
|
|
* blocks allocated (sort of).
|
1994-09-19 15:41:57 +00:00
|
|
|
*/
|
|
|
|
if (dep->de_StartCluster == 0)
|
|
|
|
cn = 0;
|
|
|
|
else
|
|
|
|
cn = dep->de_fc[FC_LASTFC].fc_fsrcn + 1;
|
1994-09-27 20:42:59 +00:00
|
|
|
error = clusteralloc(pmp, cn, count, CLUST_EOFE, &cn, &got);
|
|
|
|
if (error)
|
1998-02-18 09:28:47 +00:00
|
|
|
return (error);
|
1995-05-30 08:16:23 +00:00
|
|
|
|
1994-09-19 15:41:57 +00:00
|
|
|
count -= got;
|
1995-05-30 08:16:23 +00:00
|
|
|
|
1994-09-19 15:41:57 +00:00
|
|
|
/*
|
|
|
|
* Give them the filesystem relative cluster number if they want
|
|
|
|
* it.
|
|
|
|
*/
|
|
|
|
if (ncp) {
|
|
|
|
*ncp = cn;
|
|
|
|
ncp = NULL;
|
|
|
|
}
|
1995-05-30 08:16:23 +00:00
|
|
|
|
1994-09-19 15:41:57 +00:00
|
|
|
if (dep->de_StartCluster == 0) {
|
|
|
|
dep->de_StartCluster = cn;
|
|
|
|
frcn = 0;
|
|
|
|
} else {
|
1998-02-18 09:28:47 +00:00
|
|
|
error = fatentry(FAT_SET, pmp,
|
|
|
|
dep->de_fc[FC_LASTFC].fc_fsrcn,
|
1994-09-19 15:41:57 +00:00
|
|
|
0, cn);
|
|
|
|
if (error) {
|
|
|
|
clusterfree(pmp, cn, NULL);
|
1998-02-18 09:28:47 +00:00
|
|
|
return (error);
|
1994-09-19 15:41:57 +00:00
|
|
|
}
|
|
|
|
frcn = dep->de_fc[FC_LASTFC].fc_frcn + 1;
|
|
|
|
}
|
1995-05-30 08:16:23 +00:00
|
|
|
|
1994-09-19 15:41:57 +00:00
|
|
|
/*
|
2017-05-21 19:29:28 +00:00
|
|
|
* Update the "last cluster of the file" entry in the
|
|
|
|
* denode's FAT cache.
|
1994-09-19 15:41:57 +00:00
|
|
|
*/
|
|
|
|
fc_setcache(dep, FC_LASTFC, frcn + got - 1, cn + got - 1);
|
1995-05-30 08:16:23 +00:00
|
|
|
|
1994-09-19 15:41:57 +00:00
|
|
|
if (flags & DE_CLEAR) {
|
|
|
|
while (got-- > 0) {
|
|
|
|
/*
|
|
|
|
* Get the buf header for the new block of the file.
|
|
|
|
*/
|
|
|
|
if (dep->de_Attributes & ATTR_DIRECTORY)
|
2003-03-04 00:04:44 +00:00
|
|
|
bp = getblk(pmp->pm_devvp,
|
2007-08-07 03:22:10 +00:00
|
|
|
cntobn(pmp, cn++),
|
|
|
|
pmp->pm_bpcluster, 0, 0, 0);
|
1994-09-19 15:41:57 +00:00
|
|
|
else {
|
2003-03-04 00:04:44 +00:00
|
|
|
bp = getblk(DETOV(dep),
|
2008-09-01 13:18:16 +00:00
|
|
|
frcn++,
|
2003-03-04 00:04:44 +00:00
|
|
|
pmp->pm_bpcluster, 0, 0, 0);
|
1994-09-19 15:41:57 +00:00
|
|
|
/*
|
|
|
|
* Do the bmap now, as in msdosfs_write
|
|
|
|
*/
|
1998-02-18 09:28:47 +00:00
|
|
|
if (pcbmap(dep,
|
2008-09-01 13:18:16 +00:00
|
|
|
bp->b_lblkno,
|
2002-03-15 18:49:47 +00:00
|
|
|
&blkno, 0, 0))
|
1994-09-19 15:41:57 +00:00
|
|
|
bp->b_blkno = -1;
|
|
|
|
if (bp->b_blkno == -1)
|
|
|
|
panic("extendfile: pcbmap");
|
2002-03-15 18:49:47 +00:00
|
|
|
else
|
|
|
|
bp->b_blkno = blkno;
|
1994-09-19 15:41:57 +00:00
|
|
|
}
|
2007-07-20 16:21:47 +00:00
|
|
|
vfs_bio_clrbuf(bp);
|
1994-09-19 15:41:57 +00:00
|
|
|
if (bpp) {
|
|
|
|
*bpp = bp;
|
|
|
|
bpp = NULL;
|
1998-02-18 09:28:47 +00:00
|
|
|
} else
|
|
|
|
bdwrite(bp);
|
1994-09-19 15:41:57 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
1995-05-30 08:16:23 +00:00
|
|
|
|
1998-02-18 09:28:47 +00:00
|
|
|
return (0);
|
1994-09-19 15:41:57 +00:00
|
|
|
}
|
2003-12-26 17:19:19 +00:00
|
|
|
|
2003-12-29 11:59:05 +00:00
|
|
|
/*-
|
|
|
|
* Routine to mark a FAT16 or FAT32 volume as "clean" or "dirty" by
|
|
|
|
* manipulating the upper bit of the FAT entry for cluster 1. Note that
|
|
|
|
* this bit is not defined for FAT12 volumes, which are always assumed to
|
2013-02-01 17:58:37 +00:00
|
|
|
* be clean.
|
2003-12-26 17:19:19 +00:00
|
|
|
*
|
2003-12-29 11:59:05 +00:00
|
|
|
* The fatentry() routine only works on cluster numbers that a file could
|
|
|
|
* occupy, so it won't manipulate the entry for cluster 1. So we have to do
|
|
|
|
* it here. The code was stolen from fatentry() and tailored for cluster 1.
|
2003-12-26 17:19:19 +00:00
|
|
|
*
|
|
|
|
* Inputs:
|
2003-12-29 11:59:05 +00:00
|
|
|
* pmp The MS-DOS volume to mark
|
|
|
|
* dirty Non-zero if the volume should be marked dirty; zero if it
|
|
|
|
* should be marked clean
|
2003-12-26 17:19:19 +00:00
|
|
|
*
|
|
|
|
* Result:
|
2003-12-29 11:59:05 +00:00
|
|
|
* 0 Success
|
|
|
|
* EROFS Volume is read-only
|
|
|
|
* ? (other errors from called routines)
|
2003-12-26 17:19:19 +00:00
|
|
|
*/
|
2003-12-29 11:59:05 +00:00
|
|
|
int
|
|
|
|
markvoldirty(struct msdosfsmount *pmp, int dirty)
|
2003-12-26 17:19:19 +00:00
|
|
|
{
|
2003-12-29 11:59:05 +00:00
|
|
|
struct buf *bp;
|
|
|
|
u_long bn, bo, bsize, byteoffset, fatval;
|
|
|
|
int error;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* FAT12 does not support a "clean" bit, so don't do anything for
|
|
|
|
* FAT12.
|
|
|
|
*/
|
|
|
|
if (FAT12(pmp))
|
|
|
|
return (0);
|
|
|
|
|
|
|
|
/* Can't change the bit on a read-only filesystem. */
|
|
|
|
if (pmp->pm_flags & MSDOSFSMNT_RONLY)
|
|
|
|
return (EROFS);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Fetch the block containing the FAT entry. It is given by the
|
|
|
|
* pseudo-cluster 1.
|
|
|
|
*/
|
|
|
|
byteoffset = FATOFS(pmp, 1);
|
|
|
|
fatblock(pmp, byteoffset, &bn, &bsize, &bo);
|
|
|
|
error = bread(pmp->pm_devvp, bn, bsize, NOCRED, &bp);
|
|
|
|
if (error) {
|
|
|
|
brelse(bp);
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Get the current value of the FAT entry and set/clear the relevant
|
|
|
|
* bit. Dirty means clear the "clean" bit; clean means set the
|
|
|
|
* "clean" bit.
|
|
|
|
*/
|
|
|
|
if (FAT32(pmp)) {
|
|
|
|
/* FAT32 uses bit 27. */
|
|
|
|
fatval = getulong(&bp->b_data[bo]);
|
|
|
|
if (dirty)
|
|
|
|
fatval &= 0xF7FFFFFF;
|
|
|
|
else
|
|
|
|
fatval |= 0x08000000;
|
|
|
|
putulong(&bp->b_data[bo], fatval);
|
|
|
|
} else {
|
|
|
|
/* Must be FAT16; use bit 15. */
|
|
|
|
fatval = getushort(&bp->b_data[bo]);
|
|
|
|
if (dirty)
|
|
|
|
fatval &= 0x7FFF;
|
|
|
|
else
|
|
|
|
fatval |= 0x8000;
|
|
|
|
putushort(&bp->b_data[bo], fatval);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Write out the modified FAT block synchronously. */
|
|
|
|
return (bwrite(bp));
|
2003-12-26 17:19:19 +00:00
|
|
|
}
|