150477Speter/* $FreeBSD$ */ 233548Sjkh/* $NetBSD: msdosfs_fat.c,v 1.28 1997/11/17 15:36:49 ws Exp $ */ 32893Sdfr 42893Sdfr/*- 533548Sjkh * Copyright (C) 1994, 1995, 1997 Wolfgang Solfrank. 633548Sjkh * Copyright (C) 1994, 1995, 1997 TooLs GmbH. 72893Sdfr * All rights reserved. 82893Sdfr * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below). 92893Sdfr * 102893Sdfr * Redistribution and use in source and binary forms, with or without 112893Sdfr * modification, are permitted provided that the following conditions 122893Sdfr * are met: 132893Sdfr * 1. Redistributions of source code must retain the above copyright 142893Sdfr * notice, this list of conditions and the following disclaimer. 152893Sdfr * 2. Redistributions in binary form must reproduce the above copyright 162893Sdfr * notice, this list of conditions and the following disclaimer in the 172893Sdfr * documentation and/or other materials provided with the distribution. 182893Sdfr * 3. All advertising materials mentioning features or use of this software 192893Sdfr * must display the following acknowledgement: 202893Sdfr * This product includes software developed by TooLs GmbH. 212893Sdfr * 4. The name of TooLs GmbH may not be used to endorse or promote products 222893Sdfr * derived from this software without specific prior written permission. 232893Sdfr * 242893Sdfr * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR 252893Sdfr * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 262893Sdfr * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 272893Sdfr * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 282893Sdfr * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 292893Sdfr * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 302893Sdfr * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 312893Sdfr * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 322893Sdfr * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 332893Sdfr * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 342893Sdfr */ 35139776Simp/*- 362893Sdfr * Written by Paul Popelka (paulp@uts.amdahl.com) 378876Srgrimes * 382893Sdfr * You can do anything you want with this software, just don't say you wrote 392893Sdfr * it, and don't remove this notice. 408876Srgrimes * 412893Sdfr * This software is provided "as is". 428876Srgrimes * 432893Sdfr * The author supplies this software to be publicly redistributed on the 442893Sdfr * understanding that the author is not responsible for the correct 452893Sdfr * functioning of this software in any circumstances and is not liable for 462893Sdfr * any damages caused by this software. 478876Srgrimes * 482893Sdfr * October 1992 492893Sdfr */ 502893Sdfr 512893Sdfr#include <sys/param.h> 522893Sdfr#include <sys/systm.h> 532893Sdfr#include <sys/buf.h> 54171752Sbde#include <sys/mount.h> 55171752Sbde#include <sys/vnode.h> 562893Sdfr 5777162Sru#include <fs/msdosfs/bpb.h> 5877162Sru#include <fs/msdosfs/direntry.h> 5977162Sru#include <fs/msdosfs/denode.h> 6077162Sru#include <fs/msdosfs/fat.h> 61171752Sbde#include <fs/msdosfs/msdosfsmount.h> 622893Sdfr 6392727Salfredstatic int chainalloc(struct msdosfsmount *pmp, u_long start, 6493012Sbde u_long count, u_long fillwith, u_long *retcluster, 6593012Sbde u_long *got); 6692727Salfredstatic int chainlength(struct msdosfsmount *pmp, u_long start, 6793012Sbde u_long count); 6893012Sbdestatic void fatblock(struct msdosfsmount *pmp, u_long ofs, u_long *bnp, 6993012Sbde u_long *sizep, u_long *bop); 7093012Sbdestatic int fatchain(struct msdosfsmount *pmp, u_long start, u_long count, 7193012Sbde u_long fillwith); 7293012Sbdestatic void fc_lookup(struct denode *dep, u_long findcn, u_long *frcnp, 7393012Sbde u_long *fsrcnp); 7492727Salfredstatic void updatefats(struct msdosfsmount *pmp, struct buf *bp, 7593012Sbde u_long fatbn); 7635210Sbdestatic __inline void 7792727Salfred usemap_alloc(struct msdosfsmount *pmp, u_long cn); 7835210Sbdestatic __inline void 7992727Salfred usemap_free(struct msdosfsmount *pmp, u_long cn); 80204471Skibstatic int clusteralloc1(struct msdosfsmount *pmp, u_long start, 81204471Skib u_long count, u_long fillwith, u_long *retcluster, 82204471Skib u_long *got); 8312596Sbde 842893Sdfrstatic void 852893Sdfrfatblock(pmp, ofs, bnp, sizep, bop) 862893Sdfr struct msdosfsmount *pmp; 872893Sdfr u_long ofs; 882893Sdfr u_long *bnp; 892893Sdfr u_long *sizep; 902893Sdfr u_long *bop; 912893Sdfr{ 922893Sdfr u_long bn, size; 932893Sdfr 942893Sdfr bn = ofs / pmp->pm_fatblocksize * pmp->pm_fatblocksec; 952893Sdfr size = min(pmp->pm_fatblocksec, pmp->pm_FATsecs - bn) 9656674Snyan * DEV_BSIZE; 9733548Sjkh bn += pmp->pm_fatblk + pmp->pm_curfat * pmp->pm_FATsecs; 9833548Sjkh 992893Sdfr if (bnp) 1002893Sdfr *bnp = bn; 1012893Sdfr if (sizep) 1022893Sdfr *sizep = size; 1032893Sdfr if (bop) 1042893Sdfr *bop = ofs % pmp->pm_fatblocksize; 1052893Sdfr} 1062893Sdfr 1072893Sdfr/* 1082893Sdfr * Map the logical cluster number of a file into a physical disk sector 1092893Sdfr * that is filesystem relative. 1102893Sdfr * 1112893Sdfr * dep - address of denode representing the file of interest 1122893Sdfr * findcn - file relative cluster whose filesystem relative cluster number 1132893Sdfr * and/or block number are/is to be found 11496755Strhodes * bnp - address of where to place the filesystem relative block number. 1152893Sdfr * If this pointer is null then don't return this quantity. 11696755Strhodes * cnp - address of where to place the filesystem relative cluster number. 1172893Sdfr * If this pointer is null then don't return this quantity. 1182893Sdfr * 1192893Sdfr * NOTE: Either bnp or cnp must be non-null. 1202893Sdfr * This function has one side effect. If the requested file relative cluster 1212893Sdfr * is beyond the end of file, then the actual number of clusters in the file 1222893Sdfr * is returned in *cnp. This is useful for determining how long a directory is. 1232893Sdfr * If cnp is null, nothing is returned. 1242893Sdfr */ 1252893Sdfrint 12633548Sjkhpcbmap(dep, findcn, bnp, cnp, sp) 1272893Sdfr struct denode *dep; 1282893Sdfr u_long findcn; /* file relative cluster to get */ 1292893Sdfr daddr_t *bnp; /* returned filesys relative blk number */ 1302893Sdfr u_long *cnp; /* returned cluster number */ 13133548Sjkh int *sp; /* returned block size */ 1322893Sdfr{ 1332893Sdfr int error; 1342893Sdfr u_long i; 1352893Sdfr u_long cn; 13633548Sjkh u_long prevcn = 0; /* XXX: prevcn could be used unititialized */ 1372893Sdfr u_long byteoffset; 1382893Sdfr u_long bn; 1392893Sdfr u_long bo; 1402893Sdfr struct buf *bp = NULL; 1412893Sdfr u_long bp_bn = -1; 1422893Sdfr struct msdosfsmount *pmp = dep->de_pmp; 1432893Sdfr u_long bsize; 1442893Sdfr 145204466Skib KASSERT(bnp != NULL || cnp != NULL || sp != NULL, 146204466Skib ("pcbmap: extra call")); 147204466Skib ASSERT_VOP_ELOCKED(DETOV(dep), "pcbmap"); 1482893Sdfr 1492893Sdfr cn = dep->de_StartCluster; 1502893Sdfr /* 1512893Sdfr * The "file" that makes up the root directory is contiguous, 1522893Sdfr * permanently allocated, of fixed size, and is not made up of 1532893Sdfr * clusters. If the cluster number is beyond the end of the root 1542893Sdfr * directory, then return the number of clusters in the file. 1552893Sdfr */ 1562893Sdfr if (cn == MSDOSFSROOT) { 1572893Sdfr if (dep->de_Attributes & ATTR_DIRECTORY) { 15833548Sjkh if (de_cn2off(pmp, findcn) >= dep->de_FileSize) { 1592893Sdfr if (cnp) 16033548Sjkh *cnp = de_bn2cn(pmp, pmp->pm_rootdirsize); 16133548Sjkh return (E2BIG); 1622893Sdfr } 1632893Sdfr if (bnp) 16433548Sjkh *bnp = pmp->pm_rootdirblk + de_cn2bn(pmp, findcn); 1652893Sdfr if (cnp) 1662893Sdfr *cnp = MSDOSFSROOT; 16733548Sjkh if (sp) 16833548Sjkh *sp = min(pmp->pm_bpcluster, 16933548Sjkh dep->de_FileSize - de_cn2off(pmp, findcn)); 17033548Sjkh return (0); 1712893Sdfr } else { /* just an empty file */ 1722893Sdfr if (cnp) 1732893Sdfr *cnp = 0; 17433548Sjkh return (E2BIG); 1752893Sdfr } 1762893Sdfr } 1772893Sdfr 1782893Sdfr /* 17933548Sjkh * All other files do I/O in cluster sized blocks 18033548Sjkh */ 18133548Sjkh if (sp) 18233548Sjkh *sp = pmp->pm_bpcluster; 18333548Sjkh 18433548Sjkh /* 1852893Sdfr * Rummage around in the fat cache, maybe we can avoid tromping 1862893Sdfr * thru every fat entry for the file. And, keep track of how far 1872893Sdfr * off the cache was from where we wanted to be. 1882893Sdfr */ 1892893Sdfr i = 0; 1902893Sdfr fc_lookup(dep, findcn, &i, &cn); 1912893Sdfr 1922893Sdfr /* 1932893Sdfr * Handle all other files or directories the normal way. 1942893Sdfr */ 1952893Sdfr for (; i < findcn; i++) { 19633548Sjkh /* 19733548Sjkh * Stop with all reserved clusters, not just with EOF. 19833548Sjkh */ 19933548Sjkh if ((cn | ~pmp->pm_fatmask) >= CLUST_RSRVD) 2002893Sdfr goto hiteof; 2012893Sdfr byteoffset = FATOFS(pmp, cn); 2022893Sdfr fatblock(pmp, byteoffset, &bn, &bsize, &bo); 2032893Sdfr if (bn != bp_bn) { 2042893Sdfr if (bp) 2052893Sdfr brelse(bp); 2062893Sdfr error = bread(pmp->pm_devvp, bn, bsize, NOCRED, &bp); 20733548Sjkh if (error) { 20833548Sjkh brelse(bp); 20933548Sjkh return (error); 21033548Sjkh } 2112893Sdfr bp_bn = bn; 2122893Sdfr } 2132893Sdfr prevcn = cn; 214134941Stjr if (bo >= bsize) { 215134941Stjr if (bp) 216134941Stjr brelse(bp); 217134941Stjr return (EIO); 218134941Stjr } 21933548Sjkh if (FAT32(pmp)) 22033548Sjkh cn = getulong(&bp->b_data[bo]); 22133548Sjkh else 22233548Sjkh cn = getushort(&bp->b_data[bo]); 22333548Sjkh if (FAT12(pmp) && (prevcn & 1)) 22433548Sjkh cn >>= 4; 22533548Sjkh cn &= pmp->pm_fatmask; 22633548Sjkh 22733548Sjkh /* 22833548Sjkh * Force the special cluster numbers 22933548Sjkh * to be the same for all cluster sizes 23033548Sjkh * to let the rest of msdosfs handle 23133548Sjkh * all cases the same. 23233548Sjkh */ 23333548Sjkh if ((cn | ~pmp->pm_fatmask) >= CLUST_RSRVD) 23433548Sjkh cn |= ~pmp->pm_fatmask; 2352893Sdfr } 2362893Sdfr 23733548Sjkh if (!MSDOSFSEOF(pmp, cn)) { 2382893Sdfr if (bp) 2392893Sdfr brelse(bp); 2402893Sdfr if (bnp) 2412893Sdfr *bnp = cntobn(pmp, cn); 2422893Sdfr if (cnp) 2432893Sdfr *cnp = cn; 2442893Sdfr fc_setcache(dep, FC_LASTMAP, i, cn); 24533548Sjkh return (0); 2462893Sdfr } 2472893Sdfr 2482893Sdfrhiteof:; 2492893Sdfr if (cnp) 2502893Sdfr *cnp = i; 2512893Sdfr if (bp) 2522893Sdfr brelse(bp); 2532893Sdfr /* update last file cluster entry in the fat cache */ 2542893Sdfr fc_setcache(dep, FC_LASTFC, i - 1, prevcn); 25533548Sjkh return (E2BIG); 2562893Sdfr} 2572893Sdfr 2582893Sdfr/* 2592893Sdfr * Find the closest entry in the fat cache to the cluster we are looking 2602893Sdfr * for. 2612893Sdfr */ 26212144Sphkstatic void 2633152Sphkfc_lookup(dep, findcn, frcnp, fsrcnp) 2642893Sdfr struct denode *dep; 2652893Sdfr u_long findcn; 2662893Sdfr u_long *frcnp; 2672893Sdfr u_long *fsrcnp; 2682893Sdfr{ 2692893Sdfr int i; 2702893Sdfr u_long cn; 2712893Sdfr struct fatcache *closest = 0; 2722893Sdfr 273204466Skib ASSERT_VOP_LOCKED(DETOV(dep), "fc_lookup"); 274204466Skib 2752893Sdfr for (i = 0; i < FC_SIZE; i++) { 2762893Sdfr cn = dep->de_fc[i].fc_frcn; 2772893Sdfr if (cn != FCE_EMPTY && cn <= findcn) { 2782893Sdfr if (closest == 0 || cn > closest->fc_frcn) 2792893Sdfr closest = &dep->de_fc[i]; 2802893Sdfr } 2812893Sdfr } 2822893Sdfr if (closest) { 2832893Sdfr *frcnp = closest->fc_frcn; 2842893Sdfr *fsrcnp = closest->fc_fsrcn; 2852893Sdfr } 2862893Sdfr} 2872893Sdfr 2882893Sdfr/* 2892893Sdfr * Purge the fat cache in denode dep of all entries relating to file 2902893Sdfr * relative cluster frcn and beyond. 2912893Sdfr */ 29233548Sjkhvoid 29333548Sjkhfc_purge(dep, frcn) 2942893Sdfr struct denode *dep; 2952893Sdfr u_int frcn; 2962893Sdfr{ 2972893Sdfr int i; 2982893Sdfr struct fatcache *fcp; 2992893Sdfr 300204466Skib ASSERT_VOP_ELOCKED(DETOV(dep), "fc_purge"); 301204466Skib 3022893Sdfr fcp = dep->de_fc; 3032893Sdfr for (i = 0; i < FC_SIZE; i++, fcp++) { 3042893Sdfr if (fcp->fc_frcn >= frcn) 3052893Sdfr fcp->fc_frcn = FCE_EMPTY; 3062893Sdfr } 3072893Sdfr} 3082893Sdfr 3092893Sdfr/* 31033548Sjkh * Update the fat. 31133548Sjkh * If mirroring the fat, update all copies, with the first copy as last. 31233548Sjkh * Else update only the current fat (ignoring the others). 3132893Sdfr * 3142893Sdfr * pmp - msdosfsmount structure for filesystem to update 3152893Sdfr * bp - addr of modified fat block 3162893Sdfr * fatbn - block number relative to begin of filesystem of the modified fat block. 3172893Sdfr */ 31811921Sphkstatic void 3192893Sdfrupdatefats(pmp, bp, fatbn) 3202893Sdfr struct msdosfsmount *pmp; 3212893Sdfr struct buf *bp; 3222893Sdfr u_long fatbn; 3232893Sdfr{ 3242893Sdfr struct buf *bpn; 325246218Skib int cleanfat, i; 3262893Sdfr 3272893Sdfr#ifdef MSDOSFS_DEBUG 32833548Sjkh printf("updatefats(pmp %p, bp %p, fatbn %lu)\n", pmp, bp, fatbn); 3292893Sdfr#endif 3302893Sdfr 33133548Sjkh if (pmp->pm_flags & MSDOSFS_FATMIRROR) { 33233548Sjkh /* 33333548Sjkh * Now copy the block(s) of the modified fat to the other copies of 33433548Sjkh * the fat and write them out. This is faster than reading in the 33533548Sjkh * other fats and then writing them back out. This could tie up 33633548Sjkh * the fat for quite a while. Preventing others from accessing it. 33733548Sjkh * To prevent us from going after the fat quite so much we use 33833548Sjkh * delayed writes, unless they specfied "synchronous" when the 33933548Sjkh * filesystem was mounted. If synch is asked for then use 34033548Sjkh * bwrite()'s and really slow things down. 34133548Sjkh */ 342246218Skib if (fatbn != pmp->pm_fatblk || FAT12(pmp)) 343246218Skib cleanfat = 0; 344246218Skib else if (FAT16(pmp)) 345246218Skib cleanfat = 16; 346246218Skib else 347246218Skib cleanfat = 32; 34833548Sjkh for (i = 1; i < pmp->pm_FATs; i++) { 34933548Sjkh fatbn += pmp->pm_FATsecs; 35033548Sjkh /* getblk() never fails */ 351111856Sjeff bpn = getblk(pmp->pm_devvp, fatbn, bp->b_bcount, 352111856Sjeff 0, 0, 0); 35333548Sjkh bcopy(bp->b_data, bpn->b_data, bp->b_bcount); 354246218Skib /* Force the clean bit on in the other copies. */ 355246218Skib if (cleanfat == 16) 356246218Skib ((u_int8_t *)bpn->b_data)[3] |= 0x80; 357246218Skib else if (cleanfat == 32) 358246218Skib ((u_int8_t *)bpn->b_data)[7] |= 0x08; 359246219Skib if (pmp->pm_mountp->mnt_flag & MNT_SYNCHRONOUS) 36033548Sjkh bwrite(bpn); 36133548Sjkh else 36233548Sjkh bdwrite(bpn); 36333548Sjkh } 36433548Sjkh } 36533548Sjkh 3662893Sdfr /* 36733548Sjkh * Write out the first (or current) fat last. 3682893Sdfr */ 369246219Skib if (pmp->pm_mountp->mnt_flag & MNT_SYNCHRONOUS) 3702893Sdfr bwrite(bp); 3712893Sdfr else 3722893Sdfr bdwrite(bp); 3732893Sdfr} 3742893Sdfr 3752893Sdfr/* 3762893Sdfr * Updating entries in 12 bit fats is a pain in the butt. 3778876Srgrimes * 3782893Sdfr * The following picture shows where nibbles go when moving from a 12 bit 3792893Sdfr * cluster number into the appropriate bytes in the FAT. 3808876Srgrimes * 3812893Sdfr * byte m byte m+1 byte m+2 3822893Sdfr * +----+----+ +----+----+ +----+----+ 3832893Sdfr * | 0 1 | | 2 3 | | 4 5 | FAT bytes 3842893Sdfr * +----+----+ +----+----+ +----+----+ 3858876Srgrimes * 3862893Sdfr * +----+----+----+ +----+----+----+ 3872893Sdfr * | 3 0 1 | | 4 5 2 | 3882893Sdfr * +----+----+----+ +----+----+----+ 3892893Sdfr * cluster n cluster n+1 3908876Srgrimes * 3912893Sdfr * Where n is even. m = n + (n >> 2) 3928876Srgrimes * 3932893Sdfr */ 39435210Sbdestatic __inline void 3952893Sdfrusemap_alloc(pmp, cn) 3962893Sdfr struct msdosfsmount *pmp; 3972893Sdfr u_long cn; 3982893Sdfr{ 39933548Sjkh 400204471Skib MSDOSFS_ASSERT_MP_LOCKED(pmp); 401204472Skib 402204472Skib KASSERT((pmp->pm_inusemap[cn / N_INUSEBITS] & (1 << (cn % N_INUSEBITS))) 403204472Skib == 0, ("Allocating used sector %ld %ld %x", cn, cn % N_INUSEBITS, 404204472Skib (unsigned)pmp->pm_inusemap[cn / N_INUSEBITS])); 40533548Sjkh pmp->pm_inusemap[cn / N_INUSEBITS] |= 1 << (cn % N_INUSEBITS); 406204472Skib KASSERT(pmp->pm_freeclustercount > 0, ("usemap_alloc: too little")); 4072893Sdfr pmp->pm_freeclustercount--; 408246921Skib pmp->pm_flags |= MSDOSFS_FSIMOD; 4092893Sdfr} 4102893Sdfr 41135210Sbdestatic __inline void 4122893Sdfrusemap_free(pmp, cn) 4132893Sdfr struct msdosfsmount *pmp; 4142893Sdfr u_long cn; 4152893Sdfr{ 41633548Sjkh 417204471Skib MSDOSFS_ASSERT_MP_LOCKED(pmp); 4182893Sdfr pmp->pm_freeclustercount++; 419246921Skib pmp->pm_flags |= MSDOSFS_FSIMOD; 420204472Skib KASSERT((pmp->pm_inusemap[cn / N_INUSEBITS] & (1 << (cn % N_INUSEBITS))) 421204472Skib != 0, ("Freeing unused sector %ld %ld %x", cn, cn % N_INUSEBITS, 422204472Skib (unsigned)pmp->pm_inusemap[cn / N_INUSEBITS])); 4232893Sdfr pmp->pm_inusemap[cn / N_INUSEBITS] &= ~(1 << (cn % N_INUSEBITS)); 4242893Sdfr} 4252893Sdfr 4262893Sdfrint 4272893Sdfrclusterfree(pmp, cluster, oldcnp) 4282893Sdfr struct msdosfsmount *pmp; 4292893Sdfr u_long cluster; 4302893Sdfr u_long *oldcnp; 4312893Sdfr{ 4322893Sdfr int error; 4332893Sdfr u_long oldcn; 4342893Sdfr 4352893Sdfr error = fatentry(FAT_GET_AND_SET, pmp, cluster, &oldcn, MSDOSFSFREE); 436204471Skib if (error) 43733548Sjkh return (error); 43833548Sjkh /* 43933548Sjkh * If the cluster was successfully marked free, then update 44033548Sjkh * the count of free clusters, and turn off the "allocated" 44133548Sjkh * bit in the "in use" cluster bit map. 44233548Sjkh */ 443204471Skib MSDOSFS_LOCK_MP(pmp); 444204471Skib usemap_free(pmp, cluster); 445204471Skib MSDOSFS_UNLOCK_MP(pmp); 44633548Sjkh if (oldcnp) 44733548Sjkh *oldcnp = oldcn; 44833548Sjkh return (0); 4492893Sdfr} 4502893Sdfr 4512893Sdfr/* 4522893Sdfr * Get or Set or 'Get and Set' the cluster'th entry in the fat. 4532893Sdfr * 4542893Sdfr * function - whether to get or set a fat entry 4552893Sdfr * pmp - address of the msdosfsmount structure for the filesystem 4562893Sdfr * whose fat is to be manipulated. 4572893Sdfr * cn - which cluster is of interest 4582893Sdfr * oldcontents - address of a word that is to receive the contents of the 4592893Sdfr * cluster'th entry if this is a get function 4602893Sdfr * newcontents - the new value to be written into the cluster'th element of 4612893Sdfr * the fat if this is a set function. 4628876Srgrimes * 4632893Sdfr * This function can also be used to free a cluster by setting the fat entry 4642893Sdfr * for a cluster to 0. 4658876Srgrimes * 4662893Sdfr * All copies of the fat are updated if this is a set function. NOTE: If 4672893Sdfr * fatentry() marks a cluster as free it does not update the inusemap in 4682893Sdfr * the msdosfsmount structure. This is left to the caller. 4692893Sdfr */ 4702893Sdfrint 4712893Sdfrfatentry(function, pmp, cn, oldcontents, newcontents) 4722893Sdfr int function; 4732893Sdfr struct msdosfsmount *pmp; 4742893Sdfr u_long cn; 4752893Sdfr u_long *oldcontents; 4762893Sdfr u_long newcontents; 4772893Sdfr{ 4782893Sdfr int error; 4792893Sdfr u_long readcn; 4802893Sdfr u_long bn, bo, bsize, byteoffset; 4812893Sdfr struct buf *bp; 4822893Sdfr 48333548Sjkh#ifdef MSDOSFS_DEBUG 48433548Sjkh printf("fatentry(func %d, pmp %p, clust %lu, oldcon %p, newcon %lx)\n", 485171756Sbde function, pmp, cn, oldcontents, newcontents); 48633548Sjkh#endif 4872893Sdfr 4882893Sdfr#ifdef DIAGNOSTIC 4892893Sdfr /* 4902893Sdfr * Be sure they asked us to do something. 4912893Sdfr */ 4922893Sdfr if ((function & (FAT_SET | FAT_GET)) == 0) { 493227817Skib#ifdef MSDOSFS_DEBUG 4942893Sdfr printf("fatentry(): function code doesn't specify get or set\n"); 495227817Skib#endif 49633548Sjkh return (EINVAL); 4972893Sdfr } 4982893Sdfr 4992893Sdfr /* 5002893Sdfr * If they asked us to return a cluster number but didn't tell us 5012893Sdfr * where to put it, give them an error. 5022893Sdfr */ 5032893Sdfr if ((function & FAT_GET) && oldcontents == NULL) { 504227817Skib#ifdef MSDOSFS_DEBUG 5052893Sdfr printf("fatentry(): get function with no place to put result\n"); 506227817Skib#endif 50733548Sjkh return (EINVAL); 5082893Sdfr } 5092893Sdfr#endif 5102893Sdfr 5112893Sdfr /* 5122893Sdfr * Be sure the requested cluster is in the filesystem. 5132893Sdfr */ 5142893Sdfr if (cn < CLUST_FIRST || cn > pmp->pm_maxcluster) 51533548Sjkh return (EINVAL); 5162893Sdfr 5172893Sdfr byteoffset = FATOFS(pmp, cn); 5182893Sdfr fatblock(pmp, byteoffset, &bn, &bsize, &bo); 5193152Sphk error = bread(pmp->pm_devvp, bn, bsize, NOCRED, &bp); 52033548Sjkh if (error) { 52133548Sjkh brelse(bp); 52233548Sjkh return (error); 52333548Sjkh } 5248876Srgrimes 5252893Sdfr if (function & FAT_GET) { 52633548Sjkh if (FAT32(pmp)) 52733548Sjkh readcn = getulong(&bp->b_data[bo]); 52833548Sjkh else 52933548Sjkh readcn = getushort(&bp->b_data[bo]); 53033548Sjkh if (FAT12(pmp) & (cn & 1)) 53133548Sjkh readcn >>= 4; 53233548Sjkh readcn &= pmp->pm_fatmask; 53333548Sjkh /* map reserved fat entries to same values for all fats */ 53433548Sjkh if ((readcn | ~pmp->pm_fatmask) >= CLUST_RSRVD) 53533548Sjkh readcn |= ~pmp->pm_fatmask; 5362893Sdfr *oldcontents = readcn; 5372893Sdfr } 5382893Sdfr if (function & FAT_SET) { 53933548Sjkh switch (pmp->pm_fatmask) { 54033548Sjkh case FAT12_MASK: 5412893Sdfr readcn = getushort(&bp->b_data[bo]); 5422893Sdfr if (cn & 1) { 5432893Sdfr readcn &= 0x000f; 5442893Sdfr readcn |= newcontents << 4; 5452893Sdfr } else { 5462893Sdfr readcn &= 0xf000; 5472893Sdfr readcn |= newcontents & 0xfff; 5482893Sdfr } 5492893Sdfr putushort(&bp->b_data[bo], readcn); 55033548Sjkh break; 55133548Sjkh case FAT16_MASK: 5522893Sdfr putushort(&bp->b_data[bo], newcontents); 55333548Sjkh break; 55433548Sjkh case FAT32_MASK: 55533548Sjkh /* 55633548Sjkh * According to spec we have to retain the 55733548Sjkh * high order bits of the fat entry. 55833548Sjkh */ 55933548Sjkh readcn = getulong(&bp->b_data[bo]); 56033548Sjkh readcn &= ~FAT32_MASK; 56133548Sjkh readcn |= newcontents & FAT32_MASK; 56233548Sjkh putulong(&bp->b_data[bo], readcn); 56333548Sjkh break; 56433548Sjkh } 5652893Sdfr updatefats(pmp, bp, bn); 5662893Sdfr bp = NULL; 5672893Sdfr pmp->pm_fmod = 1; 5682893Sdfr } 5692893Sdfr if (bp) 5702893Sdfr brelse(bp); 57133548Sjkh return (0); 5722893Sdfr} 5732893Sdfr 5742893Sdfr/* 5752893Sdfr * Update a contiguous cluster chain 5762893Sdfr * 5772893Sdfr * pmp - mount point 5782893Sdfr * start - first cluster of chain 5792893Sdfr * count - number of clusters in chain 5802893Sdfr * fillwith - what to write into fat entry of last cluster 5812893Sdfr */ 5822893Sdfrstatic int 5832893Sdfrfatchain(pmp, start, count, fillwith) 5842893Sdfr struct msdosfsmount *pmp; 5852893Sdfr u_long start; 5862893Sdfr u_long count; 5872893Sdfr u_long fillwith; 5882893Sdfr{ 5892893Sdfr int error; 5902893Sdfr u_long bn, bo, bsize, byteoffset, readcn, newc; 5912893Sdfr struct buf *bp; 5928876Srgrimes 5932893Sdfr#ifdef MSDOSFS_DEBUG 59433548Sjkh printf("fatchain(pmp %p, start %lu, count %lu, fillwith %lx)\n", 59533548Sjkh pmp, start, count, fillwith); 5962893Sdfr#endif 5972893Sdfr /* 5982893Sdfr * Be sure the clusters are in the filesystem. 5992893Sdfr */ 6002893Sdfr if (start < CLUST_FIRST || start + count - 1 > pmp->pm_maxcluster) 60133548Sjkh return (EINVAL); 6028876Srgrimes 6032893Sdfr while (count > 0) { 6042893Sdfr byteoffset = FATOFS(pmp, start); 6052893Sdfr fatblock(pmp, byteoffset, &bn, &bsize, &bo); 6063152Sphk error = bread(pmp->pm_devvp, bn, bsize, NOCRED, &bp); 60733548Sjkh if (error) { 60833548Sjkh brelse(bp); 60933548Sjkh return (error); 61033548Sjkh } 6112893Sdfr while (count > 0) { 6122893Sdfr start++; 6132893Sdfr newc = --count > 0 ? start : fillwith; 61433548Sjkh switch (pmp->pm_fatmask) { 61533548Sjkh case FAT12_MASK: 6162893Sdfr readcn = getushort(&bp->b_data[bo]); 6172893Sdfr if (start & 1) { 6182893Sdfr readcn &= 0xf000; 6192893Sdfr readcn |= newc & 0xfff; 6202893Sdfr } else { 6212893Sdfr readcn &= 0x000f; 6222893Sdfr readcn |= newc << 4; 6232893Sdfr } 6242893Sdfr putushort(&bp->b_data[bo], readcn); 6252893Sdfr bo++; 6262893Sdfr if (!(start & 1)) 6272893Sdfr bo++; 62833548Sjkh break; 62933548Sjkh case FAT16_MASK: 6302893Sdfr putushort(&bp->b_data[bo], newc); 6312893Sdfr bo += 2; 63233548Sjkh break; 63333548Sjkh case FAT32_MASK: 63433548Sjkh readcn = getulong(&bp->b_data[bo]); 63533548Sjkh readcn &= ~pmp->pm_fatmask; 63633548Sjkh readcn |= newc & pmp->pm_fatmask; 63733548Sjkh putulong(&bp->b_data[bo], readcn); 63833548Sjkh bo += 4; 63933548Sjkh break; 6402893Sdfr } 6412893Sdfr if (bo >= bsize) 6422893Sdfr break; 6432893Sdfr } 6442893Sdfr updatefats(pmp, bp, bn); 6452893Sdfr } 6462893Sdfr pmp->pm_fmod = 1; 64733548Sjkh return (0); 6482893Sdfr} 6492893Sdfr 6502893Sdfr/* 6512893Sdfr * Check the length of a free cluster chain starting at start. 6522893Sdfr * 6532893Sdfr * pmp - mount point 6542893Sdfr * start - start of chain 6552893Sdfr * count - maximum interesting length 6562893Sdfr */ 65712144Sphkstatic int 6582893Sdfrchainlength(pmp, start, count) 6592893Sdfr struct msdosfsmount *pmp; 6602893Sdfr u_long start; 6612893Sdfr u_long count; 6622893Sdfr{ 6632893Sdfr u_long idx, max_idx; 6642893Sdfr u_int map; 6652893Sdfr u_long len; 6668876Srgrimes 667204471Skib MSDOSFS_ASSERT_MP_LOCKED(pmp); 668204471Skib 6692893Sdfr max_idx = pmp->pm_maxcluster / N_INUSEBITS; 6702893Sdfr idx = start / N_INUSEBITS; 6712893Sdfr start %= N_INUSEBITS; 6722893Sdfr map = pmp->pm_inusemap[idx]; 6732893Sdfr map &= ~((1 << start) - 1); 6742893Sdfr if (map) { 6752893Sdfr len = ffs(map) - 1 - start; 67633548Sjkh return (len > count ? count : len); 6772893Sdfr } 6782893Sdfr len = N_INUSEBITS - start; 6792893Sdfr if (len >= count) 68033548Sjkh return (count); 6812893Sdfr while (++idx <= max_idx) { 6822893Sdfr if (len >= count) 6832893Sdfr break; 6843152Sphk map = pmp->pm_inusemap[idx]; 6853152Sphk if (map) { 686171756Sbde len += ffs(map) - 1; 6872893Sdfr break; 6882893Sdfr } 6892893Sdfr len += N_INUSEBITS; 6902893Sdfr } 69133548Sjkh return (len > count ? count : len); 6922893Sdfr} 6932893Sdfr 6942893Sdfr/* 6952893Sdfr * Allocate contigous free clusters. 6962893Sdfr * 6972893Sdfr * pmp - mount point. 6982893Sdfr * start - start of cluster chain. 6992893Sdfr * count - number of clusters to allocate. 7002893Sdfr * fillwith - put this value into the fat entry for the 7012893Sdfr * last allocated cluster. 7022893Sdfr * retcluster - put the first allocated cluster's number here. 7032893Sdfr * got - how many clusters were actually allocated. 7042893Sdfr */ 70512144Sphkstatic int 7062893Sdfrchainalloc(pmp, start, count, fillwith, retcluster, got) 7072893Sdfr struct msdosfsmount *pmp; 7082893Sdfr u_long start; 7092893Sdfr u_long count; 7102893Sdfr u_long fillwith; 7112893Sdfr u_long *retcluster; 7122893Sdfr u_long *got; 7132893Sdfr{ 7142893Sdfr int error; 71533548Sjkh u_long cl, n; 7168876Srgrimes 717204471Skib MSDOSFS_ASSERT_MP_LOCKED(pmp); 718204471Skib 71933548Sjkh for (cl = start, n = count; n-- > 0;) 72033548Sjkh usemap_alloc(pmp, cl++); 721246921Skib pmp->pm_nxtfree = start + count; 722246921Skib if (pmp->pm_nxtfree > pmp->pm_maxcluster) 723246921Skib pmp->pm_nxtfree = CLUST_FIRST; 724246921Skib pmp->pm_flags |= MSDOSFS_FSIMOD; 7252893Sdfr error = fatchain(pmp, start, count, fillwith); 72633548Sjkh if (error != 0) 72733548Sjkh return (error); 7282893Sdfr#ifdef MSDOSFS_DEBUG 72933548Sjkh printf("clusteralloc(): allocated cluster chain at %lu (%lu clusters)\n", 73033548Sjkh start, count); 7312893Sdfr#endif 73233548Sjkh if (retcluster) 73333548Sjkh *retcluster = start; 73433548Sjkh if (got) 73533548Sjkh *got = count; 73633548Sjkh return (0); 7372893Sdfr} 7382893Sdfr 7392893Sdfr/* 7402893Sdfr * Allocate contiguous free clusters. 7412893Sdfr * 7422893Sdfr * pmp - mount point. 7432893Sdfr * start - preferred start of cluster chain. 7442893Sdfr * count - number of clusters requested. 7452893Sdfr * fillwith - put this value into the fat entry for the 7462893Sdfr * last allocated cluster. 7472893Sdfr * retcluster - put the first allocated cluster's number here. 7482893Sdfr * got - how many clusters were actually allocated. 7492893Sdfr */ 7502893Sdfrint 751204471Skibclusteralloc(struct msdosfsmount *pmp, u_long start, u_long count, 752204471Skib u_long fillwith, u_long *retcluster, u_long *got) 7532893Sdfr{ 754204471Skib int error; 755204471Skib 756204471Skib MSDOSFS_LOCK_MP(pmp); 757204471Skib error = clusteralloc1(pmp, start, count, fillwith, retcluster, got); 758204471Skib MSDOSFS_UNLOCK_MP(pmp); 759204471Skib return (error); 760204471Skib} 761204471Skib 762204471Skibstatic int 763204471Skibclusteralloc1(struct msdosfsmount *pmp, u_long start, u_long count, 764204471Skib u_long fillwith, u_long *retcluster, u_long *got) 765204471Skib{ 7662893Sdfr u_long idx; 76733548Sjkh u_long len, newst, foundl, cn, l; 76833548Sjkh u_long foundcn = 0; /* XXX: foundcn could be used unititialized */ 7692893Sdfr u_int map; 7708876Srgrimes 771204471Skib MSDOSFS_ASSERT_MP_LOCKED(pmp); 772204471Skib 7732893Sdfr#ifdef MSDOSFS_DEBUG 774171756Sbde printf("clusteralloc(): find %lu clusters\n", count); 7752893Sdfr#endif 7762893Sdfr if (start) { 7772893Sdfr if ((len = chainlength(pmp, start, count)) >= count) 77833548Sjkh return (chainalloc(pmp, start, count, fillwith, retcluster, got)); 779171756Sbde } else 7802893Sdfr len = 0; 7818876Srgrimes 782171343Sbde newst = pmp->pm_nxtfree; 7832893Sdfr foundl = 0; 7848876Srgrimes 7852893Sdfr for (cn = newst; cn <= pmp->pm_maxcluster;) { 7862893Sdfr idx = cn / N_INUSEBITS; 7872893Sdfr map = pmp->pm_inusemap[idx]; 7882893Sdfr map |= (1 << (cn % N_INUSEBITS)) - 1; 7892893Sdfr if (map != (u_int)-1) { 7902893Sdfr cn = idx * N_INUSEBITS + ffs(map^(u_int)-1) - 1; 7912893Sdfr if ((l = chainlength(pmp, cn, count)) >= count) 79233548Sjkh return (chainalloc(pmp, cn, count, fillwith, retcluster, got)); 7932893Sdfr if (l > foundl) { 7942893Sdfr foundcn = cn; 7952893Sdfr foundl = l; 7962893Sdfr } 7972893Sdfr cn += l + 1; 7982893Sdfr continue; 7992893Sdfr } 8002893Sdfr cn += N_INUSEBITS - cn % N_INUSEBITS; 8012893Sdfr } 8022893Sdfr for (cn = 0; cn < newst;) { 8032893Sdfr idx = cn / N_INUSEBITS; 8042893Sdfr map = pmp->pm_inusemap[idx]; 8052893Sdfr map |= (1 << (cn % N_INUSEBITS)) - 1; 8062893Sdfr if (map != (u_int)-1) { 8072893Sdfr cn = idx * N_INUSEBITS + ffs(map^(u_int)-1) - 1; 8082893Sdfr if ((l = chainlength(pmp, cn, count)) >= count) 80933548Sjkh return (chainalloc(pmp, cn, count, fillwith, retcluster, got)); 8102893Sdfr if (l > foundl) { 8112893Sdfr foundcn = cn; 8122893Sdfr foundl = l; 8132893Sdfr } 8142893Sdfr cn += l + 1; 8152893Sdfr continue; 8162893Sdfr } 8172893Sdfr cn += N_INUSEBITS - cn % N_INUSEBITS; 8182893Sdfr } 8192893Sdfr 8202893Sdfr if (!foundl) 82133548Sjkh return (ENOSPC); 8222893Sdfr 8232893Sdfr if (len) 82433548Sjkh return (chainalloc(pmp, start, len, fillwith, retcluster, got)); 8252893Sdfr else 82633548Sjkh return (chainalloc(pmp, foundcn, foundl, fillwith, retcluster, got)); 8272893Sdfr} 8282893Sdfr 8292893Sdfr 8302893Sdfr/* 8312893Sdfr * Free a chain of clusters. 8322893Sdfr * 8332893Sdfr * pmp - address of the msdosfs mount structure for the filesystem 8342893Sdfr * containing the cluster chain to be freed. 8352893Sdfr * startcluster - number of the 1st cluster in the chain of clusters to be 8362893Sdfr * freed. 8372893Sdfr */ 8382893Sdfrint 8392893Sdfrfreeclusterchain(pmp, cluster) 8402893Sdfr struct msdosfsmount *pmp; 8412893Sdfr u_long cluster; 8422893Sdfr{ 84333548Sjkh int error; 8442893Sdfr struct buf *bp = NULL; 8452893Sdfr u_long bn, bo, bsize, byteoffset; 8462893Sdfr u_long readcn, lbn = -1; 8478876Srgrimes 848204471Skib MSDOSFS_LOCK_MP(pmp); 8492893Sdfr while (cluster >= CLUST_FIRST && cluster <= pmp->pm_maxcluster) { 8502893Sdfr byteoffset = FATOFS(pmp, cluster); 8512893Sdfr fatblock(pmp, byteoffset, &bn, &bsize, &bo); 8522893Sdfr if (lbn != bn) { 8532893Sdfr if (bp) 8546303Sbde updatefats(pmp, bp, lbn); 8553152Sphk error = bread(pmp->pm_devvp, bn, bsize, NOCRED, &bp); 85633548Sjkh if (error) { 85733548Sjkh brelse(bp); 858204471Skib MSDOSFS_UNLOCK_MP(pmp); 85933548Sjkh return (error); 86033548Sjkh } 8612893Sdfr lbn = bn; 8622893Sdfr } 8632893Sdfr usemap_free(pmp, cluster); 86433548Sjkh switch (pmp->pm_fatmask) { 86533548Sjkh case FAT12_MASK: 86633548Sjkh readcn = getushort(&bp->b_data[bo]); 8672893Sdfr if (cluster & 1) { 8682893Sdfr cluster = readcn >> 4; 8692893Sdfr readcn &= 0x000f; 8702893Sdfr readcn |= MSDOSFSFREE << 4; 8712893Sdfr } else { 8722893Sdfr cluster = readcn; 8732893Sdfr readcn &= 0xf000; 8742893Sdfr readcn |= MSDOSFSFREE & 0xfff; 8752893Sdfr } 8762893Sdfr putushort(&bp->b_data[bo], readcn); 87733548Sjkh break; 87833548Sjkh case FAT16_MASK: 87933548Sjkh cluster = getushort(&bp->b_data[bo]); 8802893Sdfr putushort(&bp->b_data[bo], MSDOSFSFREE); 88133548Sjkh break; 88233548Sjkh case FAT32_MASK: 88333548Sjkh cluster = getulong(&bp->b_data[bo]); 88433548Sjkh putulong(&bp->b_data[bo], 88533548Sjkh (MSDOSFSFREE & FAT32_MASK) | (cluster & ~FAT32_MASK)); 88633548Sjkh break; 8872893Sdfr } 88833548Sjkh cluster &= pmp->pm_fatmask; 88933548Sjkh if ((cluster | ~pmp->pm_fatmask) >= CLUST_RSRVD) 89033548Sjkh cluster |= pmp->pm_fatmask; 8912893Sdfr } 8922893Sdfr if (bp) 8932893Sdfr updatefats(pmp, bp, bn); 894204471Skib MSDOSFS_UNLOCK_MP(pmp); 89533548Sjkh return (0); 8962893Sdfr} 8972893Sdfr 8982893Sdfr/* 8992893Sdfr * Read in fat blocks looking for free clusters. For every free cluster 9002893Sdfr * found turn off its corresponding bit in the pm_inusemap. 9012893Sdfr */ 9022893Sdfrint 9032893Sdfrfillinusemap(pmp) 9042893Sdfr struct msdosfsmount *pmp; 9052893Sdfr{ 9062893Sdfr struct buf *bp = NULL; 9072893Sdfr u_long cn, readcn; 9082893Sdfr int error; 9092893Sdfr u_long bn, bo, bsize, byteoffset; 9102893Sdfr 911204471Skib MSDOSFS_ASSERT_MP_LOCKED(pmp); 912204471Skib 9132893Sdfr /* 9142893Sdfr * Mark all clusters in use, we mark the free ones in the fat scan 9152893Sdfr * loop further down. 9162893Sdfr */ 9172893Sdfr for (cn = 0; cn < (pmp->pm_maxcluster + N_INUSEBITS) / N_INUSEBITS; cn++) 9182893Sdfr pmp->pm_inusemap[cn] = (u_int)-1; 9192893Sdfr 9202893Sdfr /* 9212893Sdfr * Figure how many free clusters are in the filesystem by ripping 9222893Sdfr * through the fat counting the number of entries whose content is 9232893Sdfr * zero. These represent free clusters. 9242893Sdfr */ 9252893Sdfr pmp->pm_freeclustercount = 0; 9262893Sdfr for (cn = CLUST_FIRST; cn <= pmp->pm_maxcluster; cn++) { 9272893Sdfr byteoffset = FATOFS(pmp, cn); 9282893Sdfr bo = byteoffset % pmp->pm_fatblocksize; 9292893Sdfr if (!bo || !bp) { 9302893Sdfr /* Read new FAT block */ 9312893Sdfr if (bp) 9322893Sdfr brelse(bp); 9332893Sdfr fatblock(pmp, byteoffset, &bn, &bsize, NULL); 9342893Sdfr error = bread(pmp->pm_devvp, bn, bsize, NOCRED, &bp); 93533548Sjkh if (error) { 93633548Sjkh brelse(bp); 93733548Sjkh return (error); 93833548Sjkh } 9392893Sdfr } 94033548Sjkh if (FAT32(pmp)) 94133548Sjkh readcn = getulong(&bp->b_data[bo]); 94233548Sjkh else 94333548Sjkh readcn = getushort(&bp->b_data[bo]); 94433548Sjkh if (FAT12(pmp) && (cn & 1)) 94533548Sjkh readcn >>= 4; 94633548Sjkh readcn &= pmp->pm_fatmask; 9472893Sdfr 9482893Sdfr if (readcn == 0) 9492893Sdfr usemap_free(pmp, cn); 9502893Sdfr } 951203866Skib if (bp != NULL) 952203866Skib brelse(bp); 95333548Sjkh return (0); 9542893Sdfr} 9552893Sdfr 9562893Sdfr/* 9572893Sdfr * Allocate a new cluster and chain it onto the end of the file. 9582893Sdfr * 9592893Sdfr * dep - the file to extend 9602893Sdfr * count - number of clusters to allocate 9612893Sdfr * bpp - where to return the address of the buf header for the first new 9622893Sdfr * file block 9632893Sdfr * ncp - where to put cluster number of the first newly allocated cluster 9642893Sdfr * If this pointer is 0, do not return the cluster number. 9652893Sdfr * flags - see fat.h 9662893Sdfr * 9672893Sdfr * NOTE: This function is not responsible for turning on the DE_UPDATE bit of 9682893Sdfr * the de_flag field of the denode and it does not change the de_FileSize 9692893Sdfr * field. This is left for the caller to do. 9702893Sdfr */ 9712893Sdfrint 9722893Sdfrextendfile(dep, count, bpp, ncp, flags) 9732893Sdfr struct denode *dep; 9742893Sdfr u_long count; 9752893Sdfr struct buf **bpp; 9762893Sdfr u_long *ncp; 9772893Sdfr int flags; 9782893Sdfr{ 97933548Sjkh int error; 9802893Sdfr u_long frcn; 9812893Sdfr u_long cn, got; 9822893Sdfr struct msdosfsmount *pmp = dep->de_pmp; 9832893Sdfr struct buf *bp; 98492363Smckusick daddr_t blkno; 9858876Srgrimes 9862893Sdfr /* 9872893Sdfr * Don't try to extend the root directory 9882893Sdfr */ 98933548Sjkh if (dep->de_StartCluster == MSDOSFSROOT 99033548Sjkh && (dep->de_Attributes & ATTR_DIRECTORY)) { 991227817Skib#ifdef MSDOSFS_DEBUG 9922893Sdfr printf("extendfile(): attempt to extend root directory\n"); 993227817Skib#endif 99433548Sjkh return (ENOSPC); 9952893Sdfr } 9962893Sdfr 9972893Sdfr /* 9982893Sdfr * If the "file's last cluster" cache entry is empty, and the file 9992893Sdfr * is not empty, then fill the cache entry by calling pcbmap(). 10002893Sdfr */ 10012893Sdfr if (dep->de_fc[FC_LASTFC].fc_frcn == FCE_EMPTY && 10022893Sdfr dep->de_StartCluster != 0) { 100333548Sjkh error = pcbmap(dep, 0xffff, 0, &cn, 0); 10042893Sdfr /* we expect it to return E2BIG */ 10052893Sdfr if (error != E2BIG) 100633548Sjkh return (error); 10072893Sdfr } 10088876Srgrimes 1009172954Strhodes dep->de_fc[FC_NEXTTOLASTFC].fc_frcn = 1010172954Strhodes dep->de_fc[FC_LASTFC].fc_frcn; 1011172954Strhodes dep->de_fc[FC_NEXTTOLASTFC].fc_fsrcn = 1012172954Strhodes dep->de_fc[FC_LASTFC].fc_fsrcn; 10132893Sdfr while (count > 0) { 10142893Sdfr /* 101533548Sjkh * Allocate a new cluster chain and cat onto the end of the 101633548Sjkh * file. * If the file is empty we make de_StartCluster point 101733548Sjkh * to the new block. Note that de_StartCluster being 0 is 101833548Sjkh * sufficient to be sure the file is empty since we exclude 101933548Sjkh * attempts to extend the root directory above, and the root 102033548Sjkh * dir is the only file with a startcluster of 0 that has 102133548Sjkh * blocks allocated (sort of). 10222893Sdfr */ 10232893Sdfr if (dep->de_StartCluster == 0) 10242893Sdfr cn = 0; 10252893Sdfr else 10262893Sdfr cn = dep->de_fc[FC_LASTFC].fc_fsrcn + 1; 10273152Sphk error = clusteralloc(pmp, cn, count, CLUST_EOFE, &cn, &got); 10283152Sphk if (error) 102933548Sjkh return (error); 10308876Srgrimes 10312893Sdfr count -= got; 10328876Srgrimes 10332893Sdfr /* 10342893Sdfr * Give them the filesystem relative cluster number if they want 10352893Sdfr * it. 10362893Sdfr */ 10372893Sdfr if (ncp) { 10382893Sdfr *ncp = cn; 10392893Sdfr ncp = NULL; 10402893Sdfr } 10418876Srgrimes 10422893Sdfr if (dep->de_StartCluster == 0) { 10432893Sdfr dep->de_StartCluster = cn; 10442893Sdfr frcn = 0; 10452893Sdfr } else { 104633548Sjkh error = fatentry(FAT_SET, pmp, 104733548Sjkh dep->de_fc[FC_LASTFC].fc_fsrcn, 10482893Sdfr 0, cn); 10492893Sdfr if (error) { 10502893Sdfr clusterfree(pmp, cn, NULL); 105133548Sjkh return (error); 10522893Sdfr } 10532893Sdfr frcn = dep->de_fc[FC_LASTFC].fc_frcn + 1; 10542893Sdfr } 10558876Srgrimes 10562893Sdfr /* 10572893Sdfr * Update the "last cluster of the file" entry in the denode's fat 10582893Sdfr * cache. 10592893Sdfr */ 10602893Sdfr fc_setcache(dep, FC_LASTFC, frcn + got - 1, cn + got - 1); 10618876Srgrimes 10622893Sdfr if (flags & DE_CLEAR) { 10632893Sdfr while (got-- > 0) { 10642893Sdfr /* 10652893Sdfr * Get the buf header for the new block of the file. 10662893Sdfr */ 10672893Sdfr if (dep->de_Attributes & ATTR_DIRECTORY) 1068111856Sjeff bp = getblk(pmp->pm_devvp, 1069171756Sbde cntobn(pmp, cn++), 1070171756Sbde pmp->pm_bpcluster, 0, 0, 0); 10712893Sdfr else { 1072111856Sjeff bp = getblk(DETOV(dep), 1073182600Skib frcn++, 1074111856Sjeff pmp->pm_bpcluster, 0, 0, 0); 10752893Sdfr /* 10762893Sdfr * Do the bmap now, as in msdosfs_write 10772893Sdfr */ 107833548Sjkh if (pcbmap(dep, 1079182600Skib bp->b_lblkno, 108092363Smckusick &blkno, 0, 0)) 10812893Sdfr bp->b_blkno = -1; 10822893Sdfr if (bp->b_blkno == -1) 10832893Sdfr panic("extendfile: pcbmap"); 108492363Smckusick else 108592363Smckusick bp->b_blkno = blkno; 10862893Sdfr } 1087171522Sbde vfs_bio_clrbuf(bp); 10882893Sdfr if (bpp) { 10892893Sdfr *bpp = bp; 10902893Sdfr bpp = NULL; 109133548Sjkh } else 109233548Sjkh bdwrite(bp); 10932893Sdfr } 10942893Sdfr } 10952893Sdfr } 10968876Srgrimes 109733548Sjkh return (0); 10982893Sdfr} 1099123873Strhodes 1100123967Sbde/*- 1101123967Sbde * Routine to mark a FAT16 or FAT32 volume as "clean" or "dirty" by 1102123967Sbde * manipulating the upper bit of the FAT entry for cluster 1. Note that 1103123967Sbde * this bit is not defined for FAT12 volumes, which are always assumed to 1104246215Skib * be clean. 1105123873Strhodes * 1106123967Sbde * The fatentry() routine only works on cluster numbers that a file could 1107123967Sbde * occupy, so it won't manipulate the entry for cluster 1. So we have to do 1108123967Sbde * it here. The code was stolen from fatentry() and tailored for cluster 1. 1109123873Strhodes * 1110123873Strhodes * Inputs: 1111123967Sbde * pmp The MS-DOS volume to mark 1112123967Sbde * dirty Non-zero if the volume should be marked dirty; zero if it 1113123967Sbde * should be marked clean 1114123873Strhodes * 1115123873Strhodes * Result: 1116123967Sbde * 0 Success 1117123967Sbde * EROFS Volume is read-only 1118123967Sbde * ? (other errors from called routines) 1119123873Strhodes */ 1120123967Sbdeint 1121123967Sbdemarkvoldirty(struct msdosfsmount *pmp, int dirty) 1122123873Strhodes{ 1123123967Sbde struct buf *bp; 1124123967Sbde u_long bn, bo, bsize, byteoffset, fatval; 1125123967Sbde int error; 1126123873Strhodes 1127123967Sbde /* 1128123967Sbde * FAT12 does not support a "clean" bit, so don't do anything for 1129123967Sbde * FAT12. 1130123967Sbde */ 1131123967Sbde if (FAT12(pmp)) 1132123967Sbde return (0); 1133123873Strhodes 1134123967Sbde /* Can't change the bit on a read-only filesystem. */ 1135123967Sbde if (pmp->pm_flags & MSDOSFSMNT_RONLY) 1136123967Sbde return (EROFS); 1137123873Strhodes 1138123967Sbde /* 1139123967Sbde * Fetch the block containing the FAT entry. It is given by the 1140123967Sbde * pseudo-cluster 1. 1141123967Sbde */ 1142123967Sbde byteoffset = FATOFS(pmp, 1); 1143123967Sbde fatblock(pmp, byteoffset, &bn, &bsize, &bo); 1144123967Sbde error = bread(pmp->pm_devvp, bn, bsize, NOCRED, &bp); 1145123967Sbde if (error) { 1146123967Sbde brelse(bp); 1147123967Sbde return (error); 1148123967Sbde } 1149123873Strhodes 1150123967Sbde /* 1151123967Sbde * Get the current value of the FAT entry and set/clear the relevant 1152123967Sbde * bit. Dirty means clear the "clean" bit; clean means set the 1153123967Sbde * "clean" bit. 1154123967Sbde */ 1155123967Sbde if (FAT32(pmp)) { 1156123967Sbde /* FAT32 uses bit 27. */ 1157123967Sbde fatval = getulong(&bp->b_data[bo]); 1158123967Sbde if (dirty) 1159123967Sbde fatval &= 0xF7FFFFFF; 1160123967Sbde else 1161123967Sbde fatval |= 0x08000000; 1162123967Sbde putulong(&bp->b_data[bo], fatval); 1163123967Sbde } else { 1164123967Sbde /* Must be FAT16; use bit 15. */ 1165123967Sbde fatval = getushort(&bp->b_data[bo]); 1166123967Sbde if (dirty) 1167123967Sbde fatval &= 0x7FFF; 1168123967Sbde else 1169123967Sbde fatval |= 0x8000; 1170123967Sbde putushort(&bp->b_data[bo], fatval); 1171123967Sbde } 1172123873Strhodes 1173123967Sbde /* Write out the modified FAT block synchronously. */ 1174123967Sbde return (bwrite(bp)); 1175123873Strhodes} 1176