| File: | msdosfs/msdosfs_fat.c |
| Warning: | line 853, column 14 Access to field 'b_data' results in a dereference of a null pointer (loaded from variable 'bp') |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 1 | /* $OpenBSD: msdosfs_fat.c,v 1.36 2023/06/16 08:42:08 sf Exp $ */ | |||
| 2 | /* $NetBSD: msdosfs_fat.c,v 1.26 1997/10/17 11:24:02 ws Exp $ */ | |||
| 3 | ||||
| 4 | /*- | |||
| 5 | * Copyright (C) 1994, 1995, 1997 Wolfgang Solfrank. | |||
| 6 | * Copyright (C) 1994, 1995, 1997 TooLs GmbH. | |||
| 7 | * All rights reserved. | |||
| 8 | * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below). | |||
| 9 | * | |||
| 10 | * Redistribution and use in source and binary forms, with or without | |||
| 11 | * modification, are permitted provided that the following conditions | |||
| 12 | * are met: | |||
| 13 | * 1. Redistributions of source code must retain the above copyright | |||
| 14 | * notice, this list of conditions and the following disclaimer. | |||
| 15 | * 2. Redistributions in binary form must reproduce the above copyright | |||
| 16 | * notice, this list of conditions and the following disclaimer in the | |||
| 17 | * documentation and/or other materials provided with the distribution. | |||
| 18 | * 3. All advertising materials mentioning features or use of this software | |||
| 19 | * must display the following acknowledgement: | |||
| 20 | * This product includes software developed by TooLs GmbH. | |||
| 21 | * 4. The name of TooLs GmbH may not be used to endorse or promote products | |||
| 22 | * derived from this software without specific prior written permission. | |||
| 23 | * | |||
| 24 | * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR | |||
| 25 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | |||
| 26 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | |||
| 27 | * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |||
| 28 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |||
| 29 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; | |||
| 30 | * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, | |||
| 31 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR | |||
| 32 | * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF | |||
| 33 | * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||
| 34 | */ | |||
| 35 | /* | |||
| 36 | * Written by Paul Popelka (paulp@uts.amdahl.com) | |||
| 37 | * | |||
| 38 | * You can do anything you want with this software, just don't say you wrote | |||
| 39 | * it, and don't remove this notice. | |||
| 40 | * | |||
| 41 | * This software is provided "as is". | |||
| 42 | * | |||
| 43 | * The author supplies this software to be publicly redistributed on the | |||
| 44 | * understanding that the author is not responsible for the correct | |||
| 45 | * functioning of this software in any circumstances and is not liable for | |||
| 46 | * any damages caused by this software. | |||
| 47 | * | |||
| 48 | * October 1992 | |||
| 49 | */ | |||
| 50 | ||||
| 51 | /* | |||
| 52 | * kernel include files. | |||
| 53 | */ | |||
| 54 | #include <sys/param.h> | |||
| 55 | #include <sys/systm.h> | |||
| 56 | #include <sys/buf.h> | |||
| 57 | #include <sys/namei.h> | |||
| 58 | #include <sys/mount.h> /* to define statfs structure */ | |||
| 59 | #include <sys/vnode.h> /* to define vattr structure */ | |||
| 60 | #include <sys/lock.h> | |||
| 61 | #include <sys/errno.h> | |||
| 62 | #include <sys/dirent.h> | |||
| 63 | ||||
| 64 | /* | |||
| 65 | * msdosfs include files. | |||
| 66 | */ | |||
| 67 | #include <msdosfs/bpb.h> | |||
| 68 | #include <msdosfs/msdosfsmount.h> | |||
| 69 | #include <msdosfs/direntry.h> | |||
| 70 | #include <msdosfs/denode.h> | |||
| 71 | #include <msdosfs/fat.h> | |||
| 72 | ||||
| 73 | /* | |||
| 74 | * Fat cache stats. | |||
| 75 | */ | |||
| 76 | int fc_fileextends; /* # of file extends */ | |||
| 77 | int fc_lfcempty; /* # of time last file cluster cache entry | |||
| 78 | * was empty */ | |||
| 79 | int fc_bmapcalls; /* # of times pcbmap was called */ | |||
| 80 | ||||
| 81 | #define LMMAX20 20 | |||
| 82 | int fc_lmdistance[LMMAX20]; /* counters for how far off the last | |||
| 83 | * cluster mapped entry was. */ | |||
| 84 | int fc_largedistance; /* off by more than LMMAX */ | |||
| 85 | ||||
| 86 | static void fatblock(struct msdosfsmount *, uint32_t, uint32_t *, uint32_t *, | |||
| 87 | uint32_t *); | |||
| 88 | void updatefats(struct msdosfsmount *, struct buf *, uint32_t); | |||
| 89 | static __inline void usemap_free(struct msdosfsmount *, uint32_t); | |||
| 90 | static __inline void usemap_alloc(struct msdosfsmount *, uint32_t); | |||
| 91 | static int fatchain(struct msdosfsmount *, uint32_t, uint32_t, uint32_t); | |||
| 92 | int chainlength(struct msdosfsmount *, uint32_t, uint32_t); | |||
| 93 | int chainalloc(struct msdosfsmount *, uint32_t, uint32_t, uint32_t, uint32_t *, | |||
| 94 | uint32_t *); | |||
| 95 | ||||
| 96 | static void | |||
| 97 | fatblock(struct msdosfsmount *pmp, uint32_t ofs, uint32_t *bnp, uint32_t *sizep, | |||
| 98 | uint32_t *bop) | |||
| 99 | { | |||
| 100 | uint32_t bn, size; | |||
| 101 | ||||
| 102 | bn = ofs / pmp->pm_fatblocksize * pmp->pm_fatblocksec; | |||
| 103 | size = min(pmp->pm_fatblocksec, pmp->pm_FATsecs - bn) * DEV_BSIZE(1 << 9); | |||
| 104 | bn += pmp->pm_fatblk + pmp->pm_curfat * pmp->pm_FATsecs; | |||
| 105 | ||||
| 106 | if (bnp) | |||
| 107 | *bnp = bn; | |||
| 108 | if (sizep) | |||
| 109 | *sizep = size; | |||
| 110 | if (bop) | |||
| 111 | *bop = ofs % pmp->pm_fatblocksize; | |||
| 112 | } | |||
| 113 | ||||
| 114 | /* | |||
| 115 | * Map the logical cluster number of a file into a physical disk sector | |||
| 116 | * that is filesystem relative. | |||
| 117 | * | |||
| 118 | * dep - address of denode representing the file of interest | |||
| 119 | * findcn - file relative cluster whose filesystem relative cluster number | |||
| 120 | * and/or block number are/is to be found | |||
| 121 | * bnp - address of where to place the file system relative block number. | |||
| 122 | * If this pointer is null then don't return this quantity. | |||
| 123 | * cnp - address of where to place the file system relative cluster number. | |||
| 124 | * If this pointer is null then don't return this quantity. | |||
| 125 | * sp - address of where to place the block size for the file/dir | |||
| 126 | * If this pointer is null then don't return this quantity. | |||
| 127 | * | |||
| 128 | * NOTE: Either bnp or cnp must be non-null. | |||
| 129 | * This function has one side effect. If the requested file relative cluster | |||
| 130 | * is beyond the end of file, then the actual number of clusters in the file | |||
| 131 | * is returned in *cnp. This is useful for determining how long a directory is. | |||
| 132 | * If cnp is null, nothing is returned. | |||
| 133 | */ | |||
| 134 | int | |||
| 135 | pcbmap(struct denode *dep, uint32_t findcn, daddr_t *bnp, uint32_t *cnp, | |||
| 136 | int *sp) | |||
| 137 | { | |||
| 138 | int error; | |||
| 139 | uint32_t i; | |||
| 140 | uint32_t cn; | |||
| 141 | uint32_t prevcn = 0; /* XXX: prevcn could be used uninitialized */ | |||
| 142 | uint32_t byteoffset; | |||
| 143 | uint32_t bn; | |||
| 144 | uint32_t bo; | |||
| 145 | struct buf *bp = NULL((void *)0); | |||
| 146 | uint32_t bp_bn = -1; | |||
| 147 | struct msdosfsmount *pmp = dep->de_pmp; | |||
| 148 | uint32_t bsize; | |||
| 149 | ||||
| 150 | fc_bmapcalls++; | |||
| 151 | ||||
| 152 | /* | |||
| 153 | * If they don't give us someplace to return a value then don't | |||
| 154 | * bother doing anything. | |||
| 155 | */ | |||
| 156 | if (bnp == NULL((void *)0) && cnp == NULL((void *)0) && sp == NULL((void *)0)) | |||
| 157 | return (0); | |||
| 158 | ||||
| 159 | cn = dep->de_StartCluster; | |||
| 160 | /* | |||
| 161 | * The "file" that makes up the root directory is contiguous, | |||
| 162 | * permanently allocated, of fixed size, and is not made up of | |||
| 163 | * clusters. If the cluster number is beyond the end of the root | |||
| 164 | * directory, then return the number of clusters in the file. | |||
| 165 | */ | |||
| 166 | if (cn == MSDOSFSROOT0) { | |||
| 167 | if (dep->de_Attributes & ATTR_DIRECTORY0x10) { | |||
| 168 | if (de_cn2off(pmp, findcn)((findcn) << (pmp)->pm_cnshift) >= dep->de_FileSize) { | |||
| 169 | if (cnp) | |||
| 170 | *cnp = de_bn2cn(pmp, pmp->pm_rootdirsize)((pmp->pm_rootdirsize) >> ((pmp)->pm_cnshift - (pmp )->pm_bnshift)); | |||
| 171 | return (E2BIG7); | |||
| 172 | } | |||
| 173 | if (bnp) | |||
| 174 | *bnp = pmp->pm_rootdirblk + de_cn2bn(pmp, findcn)((findcn) << ((pmp)->pm_cnshift - (pmp)->pm_bnshift )); | |||
| 175 | if (cnp) | |||
| 176 | *cnp = MSDOSFSROOT0; | |||
| 177 | if (sp) | |||
| 178 | *sp = min(pmp->pm_bpcluster, | |||
| 179 | dep->de_FileSize - de_cn2off(pmp, findcn)((findcn) << (pmp)->pm_cnshift)); | |||
| 180 | return (0); | |||
| 181 | } else { /* just an empty file */ | |||
| 182 | if (cnp) | |||
| 183 | *cnp = 0; | |||
| 184 | return (E2BIG7); | |||
| 185 | } | |||
| 186 | } | |||
| 187 | ||||
| 188 | /* | |||
| 189 | * All other files do I/O in cluster sized blocks | |||
| 190 | */ | |||
| 191 | if (sp) | |||
| 192 | *sp = pmp->pm_bpcluster; | |||
| 193 | ||||
| 194 | /* | |||
| 195 | * Rummage around in the fat cache, maybe we can avoid tromping | |||
| 196 | * thru every fat entry for the file. And, keep track of how far | |||
| 197 | * off the cache was from where we wanted to be. | |||
| 198 | */ | |||
| 199 | i = 0; | |||
| 200 | fc_lookup(dep, findcn, &i, &cn); | |||
| 201 | if ((bn = findcn - i) >= LMMAX20) | |||
| 202 | fc_largedistance++; | |||
| 203 | else | |||
| 204 | fc_lmdistance[bn]++; | |||
| 205 | ||||
| 206 | /* | |||
| 207 | * Handle all other files or directories the normal way. | |||
| 208 | */ | |||
| 209 | for (; i < findcn; i++) { | |||
| 210 | /* | |||
| 211 | * Stop with all reserved clusters, not just with EOF. | |||
| 212 | */ | |||
| 213 | if ((cn | ~pmp->pm_fatmask) >= CLUST_RSRVD0xfffffff6) | |||
| 214 | goto hiteof; | |||
| 215 | byteoffset = FATOFS(pmp, cn)((cn) * (pmp)->pm_fatmult / (pmp)->pm_fatdiv); | |||
| 216 | fatblock(pmp, byteoffset, &bn, &bsize, &bo); | |||
| 217 | if (bn != bp_bn) { | |||
| 218 | if (bp) | |||
| 219 | brelse(bp); | |||
| 220 | error = bread(pmp->pm_devvp, bn, bsize, &bp); | |||
| 221 | if (error) { | |||
| 222 | brelse(bp); | |||
| 223 | return (error); | |||
| 224 | } | |||
| 225 | bp_bn = bn; | |||
| 226 | } | |||
| 227 | prevcn = cn; | |||
| 228 | if (bo >= bsize) { | |||
| 229 | if (bp) | |||
| 230 | brelse(bp); | |||
| 231 | return (EIO5); | |||
| 232 | } | |||
| 233 | if (FAT32(pmp)(pmp->pm_fatmask == 0x0fffffff)) | |||
| 234 | cn = getulong(&bp->b_data[bo])*((u_int32_t *)(&bp->b_data[bo])); | |||
| 235 | else | |||
| 236 | cn = getushort(&bp->b_data[bo])*((u_int16_t *)(&bp->b_data[bo])); | |||
| 237 | if (FAT12(pmp)(pmp->pm_fatmask == 0x00000fff) && (prevcn & 1)) | |||
| 238 | cn >>= 4; | |||
| 239 | cn &= pmp->pm_fatmask; | |||
| 240 | ||||
| 241 | /* | |||
| 242 | * Force the special cluster numbers | |||
| 243 | * to be the same for all cluster sizes | |||
| 244 | * to let the rest of msdosfs handle | |||
| 245 | * all cases the same. | |||
| 246 | */ | |||
| 247 | if ((cn | ~pmp->pm_fatmask) >= CLUST_RSRVD0xfffffff6) | |||
| 248 | cn |= ~pmp->pm_fatmask; | |||
| 249 | } | |||
| 250 | ||||
| 251 | if (!MSDOSFSEOF(pmp, cn)((((cn) | ~(pmp)->pm_fatmask) & 0xfffffff8) == 0xfffffff8 )) { | |||
| 252 | if (bp) | |||
| 253 | brelse(bp); | |||
| 254 | if (bnp) | |||
| 255 | *bnp = cntobn(pmp, cn)((((cn)-2) << (((pmp))->pm_cnshift - ((pmp))->pm_bnshift )) + (pmp)->pm_firstcluster); | |||
| 256 | if (cnp) | |||
| 257 | *cnp = cn; | |||
| 258 | fc_setcache(dep, FC_LASTMAP, i, cn)(dep)->de_fc[0].fc_frcn = i; (dep)->de_fc[0].fc_fsrcn = cn;; | |||
| 259 | return (0); | |||
| 260 | } | |||
| 261 | ||||
| 262 | hiteof: | |||
| 263 | if (cnp) | |||
| 264 | *cnp = i; | |||
| 265 | if (bp) | |||
| 266 | brelse(bp); | |||
| 267 | /* update last file cluster entry in the fat cache */ | |||
| 268 | fc_setcache(dep, FC_LASTFC, i - 1, prevcn)(dep)->de_fc[1].fc_frcn = i - 1; (dep)->de_fc[1].fc_fsrcn = prevcn;; | |||
| 269 | return (E2BIG7); | |||
| 270 | } | |||
| 271 | ||||
| 272 | /* | |||
| 273 | * Find the closest entry in the fat cache to the cluster we are looking | |||
| 274 | * for. | |||
| 275 | */ | |||
| 276 | void | |||
| 277 | fc_lookup(struct denode *dep, uint32_t findcn, uint32_t *frcnp, uint32_t *fsrcnp) | |||
| 278 | { | |||
| 279 | int i; | |||
| 280 | uint32_t cn; | |||
| 281 | struct fatcache *closest = 0; | |||
| 282 | ||||
| 283 | for (i = 0; i < FC_SIZE3; i++) { | |||
| 284 | cn = dep->de_fc[i].fc_frcn; | |||
| 285 | if (cn != FCE_EMPTY0xffffffff && cn <= findcn) { | |||
| 286 | if (closest == 0 || cn > closest->fc_frcn) | |||
| 287 | closest = &dep->de_fc[i]; | |||
| 288 | } | |||
| 289 | } | |||
| 290 | if (closest) { | |||
| 291 | *frcnp = closest->fc_frcn; | |||
| 292 | *fsrcnp = closest->fc_fsrcn; | |||
| 293 | } | |||
| 294 | } | |||
| 295 | ||||
| 296 | /* | |||
| 297 | * Purge the fat cache in denode dep of all entries relating to file | |||
| 298 | * relative cluster frcn and beyond. | |||
| 299 | */ | |||
| 300 | void | |||
| 301 | fc_purge(struct denode *dep, u_int frcn) | |||
| 302 | { | |||
| 303 | int i; | |||
| 304 | struct fatcache *fcp; | |||
| 305 | ||||
| 306 | fcp = dep->de_fc; | |||
| 307 | for (i = 0; i < FC_SIZE3; i++, fcp++) { | |||
| 308 | if (fcp->fc_frcn >= frcn) | |||
| 309 | fcp->fc_frcn = FCE_EMPTY0xffffffff; | |||
| 310 | } | |||
| 311 | } | |||
| 312 | ||||
| 313 | /* | |||
| 314 | * Update the fat. | |||
| 315 | * If mirroring the fat, update all copies, with the first copy as last. | |||
| 316 | * Else update only the current fat (ignoring the others). | |||
| 317 | * | |||
| 318 | * pmp - msdosfsmount structure for filesystem to update | |||
| 319 | * bp - addr of modified fat block | |||
| 320 | * fatbn - block number relative to begin of filesystem of the modified fat block. | |||
| 321 | */ | |||
| 322 | void | |||
| 323 | updatefats(struct msdosfsmount *pmp, struct buf *bp, uint32_t fatbn) | |||
| 324 | { | |||
| 325 | int i; | |||
| 326 | struct buf *bpn; | |||
| 327 | ||||
| 328 | #ifdef MSDOSFS_DEBUG | |||
| 329 | printf("updatefats(pmp %p, buf %p, fatbn %d)\n", pmp, bp, fatbn); | |||
| 330 | #endif | |||
| 331 | ||||
| 332 | /* | |||
| 333 | * If we have an FSInfo block, update it. | |||
| 334 | */ | |||
| 335 | if (pmp->pm_fsinfo) { | |||
| 336 | if (bread(pmp->pm_devvp, pmp->pm_fsinfo, fsi_size(pmp)(1024 << ((pmp)->pm_BlkPerSec >> 2)), | |||
| 337 | &bpn) != 0) { | |||
| 338 | /* | |||
| 339 | * Ignore the error, but turn off FSInfo update for the future. | |||
| 340 | */ | |||
| 341 | pmp->pm_fsinfo = 0; | |||
| 342 | brelse(bpn); | |||
| 343 | } else { | |||
| 344 | struct fsinfo *fp = (struct fsinfo *)bpn->b_data; | |||
| 345 | ||||
| 346 | putulong(fp->fsinfree, pmp->pm_freeclustercount)(*((u_int32_t *)(fp->fsinfree)) = (pmp->pm_freeclustercount )); | |||
| 347 | if (pmp->pm_flags & MSDOSFSMNT_WAITONFAT0x40000000) | |||
| 348 | bwrite(bpn); | |||
| 349 | else | |||
| 350 | bdwrite(bpn); | |||
| 351 | } | |||
| 352 | } | |||
| 353 | ||||
| 354 | if (pmp->pm_flags & MSDOSFS_FATMIRROR0x20000000) { | |||
| 355 | /* | |||
| 356 | * Now copy the block(s) of the modified fat to the other copies of | |||
| 357 | * the fat and write them out. This is faster than reading in the | |||
| 358 | * other fats and then writing them back out. This could tie up | |||
| 359 | * the fat for quite a while. Preventing others from accessing it. | |||
| 360 | * To prevent us from going after the fat quite so much we use | |||
| 361 | * delayed writes, unless they specified "synchronous" when the | |||
| 362 | * filesystem was mounted. If synch is asked for then use | |||
| 363 | * bwrite()'s and really slow things down. | |||
| 364 | */ | |||
| 365 | for (i = 1; i < pmp->pm_FATspm_bpb.bpbFATs; i++) { | |||
| 366 | fatbn += pmp->pm_FATsecs; | |||
| 367 | /* getblk() never fails */ | |||
| 368 | bpn = getblk(pmp->pm_devvp, fatbn, bp->b_bcount, 0, | |||
| 369 | INFSLP0xffffffffffffffffULL); | |||
| 370 | bcopy(bp->b_data, bpn->b_data, bp->b_bcount); | |||
| 371 | if (pmp->pm_flags & MSDOSFSMNT_WAITONFAT0x40000000) | |||
| 372 | bwrite(bpn); | |||
| 373 | else | |||
| 374 | bdwrite(bpn); | |||
| 375 | } | |||
| 376 | } | |||
| 377 | ||||
| 378 | /* | |||
| 379 | * Write out the first (or current) fat last. | |||
| 380 | */ | |||
| 381 | if (pmp->pm_flags & MSDOSFSMNT_WAITONFAT0x40000000) | |||
| 382 | bwrite(bp); | |||
| 383 | else | |||
| 384 | bdwrite(bp); | |||
| 385 | /* | |||
| 386 | * Maybe update fsinfo sector here? | |||
| 387 | */ | |||
| 388 | } | |||
| 389 | ||||
| 390 | /* | |||
| 391 | * Updating entries in 12 bit fats is a pain in the butt. | |||
| 392 | * | |||
| 393 | * The following picture shows where nibbles go when moving from a 12 bit | |||
| 394 | * cluster number into the appropriate bytes in the FAT. | |||
| 395 | * | |||
| 396 | * byte m byte m+1 byte m+2 | |||
| 397 | * +----+----+ +----+----+ +----+----+ | |||
| 398 | * | 0 1 | | 2 3 | | 4 5 | FAT bytes | |||
| 399 | * +----+----+ +----+----+ +----+----+ | |||
| 400 | * | |||
| 401 | * +----+----+----+ +----+----+----+ | |||
| 402 | * | 3 0 1 | | 4 5 2 | | |||
| 403 | * +----+----+----+ +----+----+----+ | |||
| 404 | * cluster n cluster n+1 | |||
| 405 | * | |||
| 406 | * Where n is even. m = n + (n >> 2) | |||
| 407 | * | |||
| 408 | */ | |||
| 409 | static __inline void | |||
| 410 | usemap_alloc(struct msdosfsmount *pmp, uint32_t cn) | |||
| 411 | { | |||
| 412 | KASSERT(cn <= pmp->pm_maxcluster)((cn <= pmp->pm_maxcluster) ? (void)0 : __assert("diagnostic " , "/usr/src/sys/msdosfs/msdosfs_fat.c", 412, "cn <= pmp->pm_maxcluster" )); | |||
| 413 | ||||
| 414 | pmp->pm_inusemap[cn / N_INUSEBITS(8 * sizeof(u_int))] |= 1U << (cn % N_INUSEBITS(8 * sizeof(u_int))); | |||
| 415 | pmp->pm_freeclustercount--; | |||
| 416 | } | |||
| 417 | ||||
| 418 | static __inline void | |||
| 419 | usemap_free(struct msdosfsmount *pmp, uint32_t cn) | |||
| 420 | { | |||
| 421 | KASSERT(cn <= pmp->pm_maxcluster)((cn <= pmp->pm_maxcluster) ? (void)0 : __assert("diagnostic " , "/usr/src/sys/msdosfs/msdosfs_fat.c", 421, "cn <= pmp->pm_maxcluster" )); | |||
| 422 | ||||
| 423 | pmp->pm_freeclustercount++; | |||
| 424 | pmp->pm_inusemap[cn / N_INUSEBITS(8 * sizeof(u_int))] &= ~(1U << (cn % N_INUSEBITS(8 * sizeof(u_int)))); | |||
| 425 | } | |||
| 426 | ||||
| 427 | int | |||
| 428 | clusterfree(struct msdosfsmount *pmp, uint32_t cluster, uint32_t *oldcnp) | |||
| 429 | { | |||
| 430 | int error; | |||
| 431 | uint32_t oldcn; | |||
| 432 | ||||
| 433 | usemap_free(pmp, cluster); | |||
| 434 | error = fatentry(FAT_GET_AND_SET(0x0001 | 0x0002), pmp, cluster, &oldcn, MSDOSFSFREE0); | |||
| 435 | if (error) { | |||
| 436 | usemap_alloc(pmp, cluster); | |||
| 437 | return (error); | |||
| 438 | } | |||
| 439 | /* | |||
| 440 | * If the cluster was successfully marked free, then update | |||
| 441 | * the count of free clusters, and turn off the "allocated" | |||
| 442 | * bit in the "in use" cluster bit map. | |||
| 443 | */ | |||
| 444 | if (oldcnp) | |||
| 445 | *oldcnp = oldcn; | |||
| 446 | return (0); | |||
| 447 | } | |||
| 448 | ||||
| 449 | /* | |||
| 450 | * Get or Set or 'Get and Set' the cluster'th entry in the fat. | |||
| 451 | * | |||
| 452 | * function - whether to get or set a fat entry | |||
| 453 | * pmp - address of the msdosfsmount structure for the filesystem | |||
| 454 | * whose fat is to be manipulated. | |||
| 455 | * cn - which cluster is of interest | |||
| 456 | * oldcontents - address of a word that is to receive the contents of the | |||
| 457 | * cluster'th entry if this is a get function | |||
| 458 | * newcontents - the new value to be written into the cluster'th element of | |||
| 459 | * the fat if this is a set function. | |||
| 460 | * | |||
| 461 | * This function can also be used to free a cluster by setting the fat entry | |||
| 462 | * for a cluster to 0. | |||
| 463 | * | |||
| 464 | * All copies of the fat are updated if this is a set function. NOTE: If | |||
| 465 | * fatentry() marks a cluster as free it does not update the inusemap in | |||
| 466 | * the msdosfsmount structure. This is left to the caller. | |||
| 467 | */ | |||
| 468 | int | |||
| 469 | fatentry(int function, struct msdosfsmount *pmp, uint32_t cn, uint32_t *oldcontents, | |||
| 470 | uint32_t newcontents) | |||
| 471 | { | |||
| 472 | int error; | |||
| 473 | uint32_t readcn; | |||
| 474 | uint32_t bn, bo, bsize, byteoffset; | |||
| 475 | struct buf *bp; | |||
| 476 | ||||
| 477 | #ifdef MSDOSFS_DEBUG | |||
| 478 | printf("fatentry(func %d, pmp %p, clust %d, oldcon %p, " | |||
| 479 | "newcon %d)\n", function, pmp, cn, oldcontents, newcontents); | |||
| 480 | #endif | |||
| 481 | ||||
| 482 | #ifdef DIAGNOSTIC1 | |||
| 483 | /* | |||
| 484 | * Be sure they asked us to do something. | |||
| 485 | */ | |||
| 486 | if ((function & (FAT_SET0x0002 | FAT_GET0x0001)) == 0) { | |||
| 487 | printf("fatentry(): function code doesn't specify get or set\n"); | |||
| 488 | return (EINVAL22); | |||
| 489 | } | |||
| 490 | ||||
| 491 | /* | |||
| 492 | * If they asked us to return a cluster number but didn't tell us | |||
| 493 | * where to put it, give them an error. | |||
| 494 | */ | |||
| 495 | if ((function & FAT_GET0x0001) && oldcontents == NULL((void *)0)) { | |||
| 496 | printf("fatentry(): get function with no place to put result\n"); | |||
| 497 | return (EINVAL22); | |||
| 498 | } | |||
| 499 | #endif | |||
| 500 | ||||
| 501 | /* | |||
| 502 | * Be sure the requested cluster is in the filesystem. | |||
| 503 | */ | |||
| 504 | if (cn < CLUST_FIRST2 || cn > pmp->pm_maxcluster) | |||
| 505 | return (EINVAL22); | |||
| 506 | ||||
| 507 | byteoffset = FATOFS(pmp, cn)((cn) * (pmp)->pm_fatmult / (pmp)->pm_fatdiv); | |||
| 508 | fatblock(pmp, byteoffset, &bn, &bsize, &bo); | |||
| 509 | if ((error = bread(pmp->pm_devvp, bn, bsize, &bp)) != 0) { | |||
| 510 | brelse(bp); | |||
| 511 | return (error); | |||
| 512 | } | |||
| 513 | ||||
| 514 | if (function & FAT_GET0x0001) { | |||
| 515 | if (FAT32(pmp)(pmp->pm_fatmask == 0x0fffffff)) | |||
| 516 | readcn = getulong(&bp->b_data[bo])*((u_int32_t *)(&bp->b_data[bo])); | |||
| 517 | else | |||
| 518 | readcn = getushort(&bp->b_data[bo])*((u_int16_t *)(&bp->b_data[bo])); | |||
| 519 | if (FAT12(pmp)(pmp->pm_fatmask == 0x00000fff) && (cn & 1)) | |||
| 520 | readcn >>= 4; | |||
| 521 | readcn &= pmp->pm_fatmask; | |||
| 522 | /* map reserved fat entries to same values for all fats */ | |||
| 523 | if ((readcn | ~pmp->pm_fatmask) >= CLUST_RSRVD0xfffffff6) | |||
| 524 | readcn |= ~pmp->pm_fatmask; | |||
| 525 | *oldcontents = readcn; | |||
| 526 | } | |||
| 527 | if (function & FAT_SET0x0002) { | |||
| 528 | switch (pmp->pm_fatmask) { | |||
| 529 | case FAT12_MASK0x00000fff: | |||
| 530 | readcn = getushort(&bp->b_data[bo])*((u_int16_t *)(&bp->b_data[bo])); | |||
| 531 | if (cn & 1) { | |||
| 532 | readcn &= 0x000f; | |||
| 533 | readcn |= newcontents << 4; | |||
| 534 | } else { | |||
| 535 | readcn &= 0xf000; | |||
| 536 | readcn |= newcontents & 0xfff; | |||
| 537 | } | |||
| 538 | putushort(&bp->b_data[bo], readcn)(*((u_int16_t *)(&bp->b_data[bo])) = (readcn)); | |||
| 539 | break; | |||
| 540 | case FAT16_MASK0x0000ffff: | |||
| 541 | putushort(&bp->b_data[bo], newcontents)(*((u_int16_t *)(&bp->b_data[bo])) = (newcontents)); | |||
| 542 | break; | |||
| 543 | case FAT32_MASK0x0fffffff: | |||
| 544 | /* | |||
| 545 | * According to spec we have to retain the | |||
| 546 | * high order bits of the fat entry. | |||
| 547 | */ | |||
| 548 | readcn = getulong(&bp->b_data[bo])*((u_int32_t *)(&bp->b_data[bo])); | |||
| 549 | readcn &= ~FAT32_MASK0x0fffffff; | |||
| 550 | readcn |= newcontents & FAT32_MASK0x0fffffff; | |||
| 551 | putulong(&bp->b_data[bo], readcn)(*((u_int32_t *)(&bp->b_data[bo])) = (readcn)); | |||
| 552 | break; | |||
| 553 | } | |||
| 554 | updatefats(pmp, bp, bn); | |||
| 555 | bp = NULL((void *)0); | |||
| 556 | pmp->pm_fmod = 1; | |||
| 557 | } | |||
| 558 | if (bp) | |||
| 559 | brelse(bp); | |||
| 560 | return (0); | |||
| 561 | } | |||
| 562 | ||||
| 563 | /* | |||
| 564 | * Update a contiguous cluster chain | |||
| 565 | * | |||
| 566 | * pmp - mount point | |||
| 567 | * start - first cluster of chain | |||
| 568 | * count - number of clusters in chain | |||
| 569 | * fillwith - what to write into fat entry of last cluster | |||
| 570 | */ | |||
| 571 | static int | |||
| 572 | fatchain(struct msdosfsmount *pmp, uint32_t start, uint32_t count, uint32_t fillwith) | |||
| 573 | { | |||
| 574 | int error; | |||
| 575 | uint32_t bn, bo, bsize, byteoffset, readcn, newc; | |||
| 576 | struct buf *bp; | |||
| 577 | ||||
| 578 | #ifdef MSDOSFS_DEBUG | |||
| 579 | printf("fatchain(pmp %p, start %d, count %d, fillwith %d)\n", | |||
| 580 | pmp, start, count, fillwith); | |||
| 581 | #endif | |||
| 582 | /* | |||
| 583 | * Be sure the clusters are in the filesystem. | |||
| 584 | */ | |||
| 585 | if (start < CLUST_FIRST2 || start + count - 1 > pmp->pm_maxcluster) | |||
| 586 | return (EINVAL22); | |||
| 587 | ||||
| 588 | while (count > 0) { | |||
| 589 | byteoffset = FATOFS(pmp, start)((start) * (pmp)->pm_fatmult / (pmp)->pm_fatdiv); | |||
| 590 | fatblock(pmp, byteoffset, &bn, &bsize, &bo); | |||
| 591 | error = bread(pmp->pm_devvp, bn, bsize, &bp); | |||
| 592 | if (error) { | |||
| 593 | brelse(bp); | |||
| 594 | return (error); | |||
| 595 | } | |||
| 596 | while (count > 0) { | |||
| 597 | start++; | |||
| 598 | newc = --count > 0 ? start : fillwith; | |||
| 599 | switch (pmp->pm_fatmask) { | |||
| 600 | case FAT12_MASK0x00000fff: | |||
| 601 | readcn = getushort(&bp->b_data[bo])*((u_int16_t *)(&bp->b_data[bo])); | |||
| 602 | if (start & 1) { | |||
| 603 | readcn &= 0xf000; | |||
| 604 | readcn |= newc & 0xfff; | |||
| 605 | } else { | |||
| 606 | readcn &= 0x000f; | |||
| 607 | readcn |= newc << 4; | |||
| 608 | } | |||
| 609 | putushort(&bp->b_data[bo], readcn)(*((u_int16_t *)(&bp->b_data[bo])) = (readcn)); | |||
| 610 | bo++; | |||
| 611 | if (!(start & 1)) | |||
| 612 | bo++; | |||
| 613 | break; | |||
| 614 | case FAT16_MASK0x0000ffff: | |||
| 615 | putushort(&bp->b_data[bo], newc)(*((u_int16_t *)(&bp->b_data[bo])) = (newc)); | |||
| 616 | bo += 2; | |||
| 617 | break; | |||
| 618 | case FAT32_MASK0x0fffffff: | |||
| 619 | readcn = getulong(&bp->b_data[bo])*((u_int32_t *)(&bp->b_data[bo])); | |||
| 620 | readcn &= ~pmp->pm_fatmask; | |||
| 621 | readcn |= newc & pmp->pm_fatmask; | |||
| 622 | putulong(&bp->b_data[bo], readcn)(*((u_int32_t *)(&bp->b_data[bo])) = (readcn)); | |||
| 623 | bo += 4; | |||
| 624 | break; | |||
| 625 | } | |||
| 626 | if (bo >= bsize) | |||
| 627 | break; | |||
| 628 | } | |||
| 629 | updatefats(pmp, bp, bn); | |||
| 630 | } | |||
| 631 | pmp->pm_fmod = 1; | |||
| 632 | return (0); | |||
| 633 | } | |||
| 634 | ||||
| 635 | /* | |||
| 636 | * Check the length of a free cluster chain starting at start. | |||
| 637 | * | |||
| 638 | * pmp - mount point | |||
| 639 | * start - start of chain | |||
| 640 | * count - maximum interesting length | |||
| 641 | */ | |||
| 642 | int | |||
| 643 | chainlength(struct msdosfsmount *pmp, uint32_t start, uint32_t count) | |||
| 644 | { | |||
| 645 | uint32_t idx, max_idx; | |||
| 646 | u_int map; | |||
| 647 | uint32_t len; | |||
| 648 | ||||
| 649 | if (start > pmp->pm_maxcluster) | |||
| 650 | return (0); | |||
| 651 | max_idx = pmp->pm_maxcluster / N_INUSEBITS(8 * sizeof(u_int)); | |||
| 652 | idx = start / N_INUSEBITS(8 * sizeof(u_int)); | |||
| 653 | start %= N_INUSEBITS(8 * sizeof(u_int)); | |||
| 654 | map = pmp->pm_inusemap[idx]; | |||
| 655 | map &= ~((1U << start) - 1); | |||
| 656 | if (map) { | |||
| 657 | len = ffs(map) - 1 - start; | |||
| 658 | len = MIN(len, count)(((len)<(count))?(len):(count)); | |||
| 659 | len = MIN(len, pmp->pm_maxcluster - start + 1)(((len)<(pmp->pm_maxcluster - start + 1))?(len):(pmp-> pm_maxcluster - start + 1)); | |||
| 660 | return (len); | |||
| 661 | } | |||
| 662 | len = N_INUSEBITS(8 * sizeof(u_int)) - start; | |||
| 663 | if (len >= count) { | |||
| 664 | len = MIN(count, pmp->pm_maxcluster - start + 1)(((count)<(pmp->pm_maxcluster - start + 1))?(count):(pmp ->pm_maxcluster - start + 1)); | |||
| 665 | return (len); | |||
| 666 | } | |||
| 667 | while (++idx <= max_idx) { | |||
| 668 | if (len >= count) | |||
| 669 | break; | |||
| 670 | if ((map = pmp->pm_inusemap[idx]) != 0) { | |||
| 671 | len += ffs(map) - 1; | |||
| 672 | break; | |||
| 673 | } | |||
| 674 | len += N_INUSEBITS(8 * sizeof(u_int)); | |||
| 675 | } | |||
| 676 | len = MIN(len, count)(((len)<(count))?(len):(count)); | |||
| 677 | len = MIN(len, pmp->pm_maxcluster - start + 1)(((len)<(pmp->pm_maxcluster - start + 1))?(len):(pmp-> pm_maxcluster - start + 1)); | |||
| 678 | return (len); | |||
| 679 | } | |||
| 680 | ||||
| 681 | /* | |||
| 682 | * Allocate contiguous free clusters. | |||
| 683 | * | |||
| 684 | * pmp - mount point. | |||
| 685 | * start - start of cluster chain. | |||
| 686 | * count - number of clusters to allocate. | |||
| 687 | * fillwith - put this value into the fat entry for the | |||
| 688 | * last allocated cluster. | |||
| 689 | * retcluster - put the first allocated cluster's number here. | |||
| 690 | * got - how many clusters were actually allocated. | |||
| 691 | */ | |||
| 692 | int | |||
| 693 | chainalloc(struct msdosfsmount *pmp, uint32_t start, uint32_t count, | |||
| 694 | uint32_t fillwith, uint32_t *retcluster, uint32_t *got) | |||
| 695 | { | |||
| 696 | int error; | |||
| 697 | uint32_t cl, n; | |||
| 698 | ||||
| 699 | for (cl = start, n = count; n-- > 0;) | |||
| 700 | usemap_alloc(pmp, cl++); | |||
| 701 | if ((error = fatchain(pmp, start, count, fillwith)) != 0) | |||
| 702 | return (error); | |||
| 703 | #ifdef MSDOSFS_DEBUG | |||
| 704 | printf("clusteralloc(): allocated cluster chain at %d (%d clusters)\n", | |||
| 705 | start, count); | |||
| 706 | #endif | |||
| 707 | if (retcluster) | |||
| 708 | *retcluster = start; | |||
| 709 | if (got) | |||
| 710 | *got = count; | |||
| 711 | return (0); | |||
| 712 | } | |||
| 713 | ||||
| 714 | /* | |||
| 715 | * Allocate contiguous free clusters. | |||
| 716 | * | |||
| 717 | * pmp - mount point. | |||
| 718 | * start - preferred start of cluster chain. | |||
| 719 | * count - number of clusters requested. | |||
| 720 | * fillwith - put this value into the fat entry for the | |||
| 721 | * last allocated cluster. | |||
| 722 | * retcluster - put the first allocated cluster's number here. | |||
| 723 | * got - how many clusters were actually allocated. | |||
| 724 | */ | |||
| 725 | int | |||
| 726 | clusteralloc(struct msdosfsmount *pmp, uint32_t start, uint32_t count, | |||
| 727 | uint32_t *retcluster, uint32_t *got) | |||
| 728 | { | |||
| 729 | uint32_t idx; | |||
| 730 | uint32_t len, newst, foundl, cn, l; | |||
| 731 | uint32_t foundcn = 0; /* XXX: foundcn could be used uninitialized */ | |||
| 732 | uint32_t fillwith = CLUST_EOFE0xffffffff; | |||
| 733 | u_int map; | |||
| 734 | ||||
| 735 | #ifdef MSDOSFS_DEBUG | |||
| 736 | printf("clusteralloc(): find %d clusters\n",count); | |||
| 737 | #endif | |||
| 738 | if (start) { | |||
| 739 | if ((len = chainlength(pmp, start, count)) >= count) | |||
| 740 | return (chainalloc(pmp, start, count, fillwith, retcluster, got)); | |||
| 741 | } else { | |||
| 742 | /* | |||
| 743 | * This is a new file, initialize start | |||
| 744 | */ | |||
| 745 | struct timeval tv; | |||
| 746 | ||||
| 747 | microtime(&tv); | |||
| 748 | start = (tv.tv_usec >> 10) | tv.tv_usec; | |||
| 749 | len = 0; | |||
| 750 | } | |||
| 751 | ||||
| 752 | /* | |||
| 753 | * Start at a (pseudo) random place to maximize cluster runs | |||
| 754 | * under multiple writers. | |||
| 755 | */ | |||
| 756 | newst = (start * 1103515245 + 12345) % (pmp->pm_maxcluster + 1); | |||
| 757 | foundl = 0; | |||
| 758 | ||||
| 759 | for (cn = newst; cn <= pmp->pm_maxcluster;) { | |||
| 760 | idx = cn / N_INUSEBITS(8 * sizeof(u_int)); | |||
| 761 | map = pmp->pm_inusemap[idx]; | |||
| 762 | map |= (1U << (cn % N_INUSEBITS(8 * sizeof(u_int)))) - 1; | |||
| 763 | if (map != (u_int)-1) { | |||
| 764 | cn = idx * N_INUSEBITS(8 * sizeof(u_int)) + ffs(map^(u_int)-1) - 1; | |||
| 765 | if ((l = chainlength(pmp, cn, count)) >= count) | |||
| 766 | return (chainalloc(pmp, cn, count, fillwith, retcluster, got)); | |||
| 767 | if (l > foundl) { | |||
| 768 | foundcn = cn; | |||
| 769 | foundl = l; | |||
| 770 | } | |||
| 771 | cn += l + 1; | |||
| 772 | continue; | |||
| 773 | } | |||
| 774 | cn += N_INUSEBITS(8 * sizeof(u_int)) - cn % N_INUSEBITS(8 * sizeof(u_int)); | |||
| 775 | } | |||
| 776 | for (cn = 0; cn < newst;) { | |||
| 777 | idx = cn / N_INUSEBITS(8 * sizeof(u_int)); | |||
| 778 | map = pmp->pm_inusemap[idx]; | |||
| 779 | map |= (1U << (cn % N_INUSEBITS(8 * sizeof(u_int)))) - 1; | |||
| 780 | if (map != (u_int)-1) { | |||
| 781 | cn = idx * N_INUSEBITS(8 * sizeof(u_int)) + ffs(map^(u_int)-1) - 1; | |||
| 782 | if ((l = chainlength(pmp, cn, count)) >= count) | |||
| 783 | return (chainalloc(pmp, cn, count, fillwith, retcluster, got)); | |||
| 784 | if (l > foundl) { | |||
| 785 | foundcn = cn; | |||
| 786 | foundl = l; | |||
| 787 | } | |||
| 788 | cn += l + 1; | |||
| 789 | continue; | |||
| 790 | } | |||
| 791 | cn += N_INUSEBITS(8 * sizeof(u_int)) - cn % N_INUSEBITS(8 * sizeof(u_int)); | |||
| 792 | } | |||
| 793 | ||||
| 794 | if (!foundl) | |||
| 795 | return (ENOSPC28); | |||
| 796 | ||||
| 797 | if (len) | |||
| 798 | return (chainalloc(pmp, start, len, fillwith, retcluster, got)); | |||
| 799 | else | |||
| 800 | return (chainalloc(pmp, foundcn, foundl, fillwith, retcluster, got)); | |||
| 801 | } | |||
| 802 | ||||
| 803 | ||||
| 804 | /* | |||
| 805 | * Free a chain of clusters. | |||
| 806 | * | |||
| 807 | * pmp - address of the msdosfs mount structure for the filesystem | |||
| 808 | * containing the cluster chain to be freed. | |||
| 809 | * startcluster - number of the 1st cluster in the chain of clusters to be | |||
| 810 | * freed. | |||
| 811 | */ | |||
| 812 | int | |||
| 813 | freeclusterchain(struct msdosfsmount *pmp, uint32_t cluster) | |||
| 814 | { | |||
| 815 | int error; | |||
| 816 | struct buf *bp = NULL((void *)0); | |||
| ||||
| 817 | uint32_t bn, bo, bsize, byteoffset; | |||
| 818 | uint32_t readcn, lbn = -1; | |||
| 819 | ||||
| 820 | while (cluster >= CLUST_FIRST2 && cluster <= pmp->pm_maxcluster) { | |||
| 821 | byteoffset = FATOFS(pmp, cluster)((cluster) * (pmp)->pm_fatmult / (pmp)->pm_fatdiv); | |||
| 822 | fatblock(pmp, byteoffset, &bn, &bsize, &bo); | |||
| 823 | if (lbn != bn) { | |||
| 824 | if (bp) | |||
| 825 | updatefats(pmp, bp, lbn); | |||
| 826 | error = bread(pmp->pm_devvp, bn, bsize, &bp); | |||
| 827 | if (error) { | |||
| 828 | brelse(bp); | |||
| 829 | return (error); | |||
| 830 | } | |||
| 831 | lbn = bn; | |||
| 832 | } | |||
| 833 | usemap_free(pmp, cluster); | |||
| 834 | switch (pmp->pm_fatmask) { | |||
| 835 | case FAT12_MASK0x00000fff: | |||
| 836 | readcn = getushort(&bp->b_data[bo])*((u_int16_t *)(&bp->b_data[bo])); | |||
| 837 | if (cluster & 1) { | |||
| 838 | cluster = readcn >> 4; | |||
| 839 | readcn &= 0x000f; | |||
| 840 | readcn |= MSDOSFSFREE0 << 4; | |||
| 841 | } else { | |||
| 842 | cluster = readcn; | |||
| 843 | readcn &= 0xf000; | |||
| 844 | readcn |= MSDOSFSFREE0 & 0xfff; | |||
| 845 | } | |||
| 846 | putushort(&bp->b_data[bo], readcn)(*((u_int16_t *)(&bp->b_data[bo])) = (readcn)); | |||
| 847 | break; | |||
| 848 | case FAT16_MASK0x0000ffff: | |||
| 849 | cluster = getushort(&bp->b_data[bo])*((u_int16_t *)(&bp->b_data[bo])); | |||
| 850 | putushort(&bp->b_data[bo], MSDOSFSFREE)(*((u_int16_t *)(&bp->b_data[bo])) = (0)); | |||
| 851 | break; | |||
| 852 | case FAT32_MASK0x0fffffff: | |||
| 853 | cluster = getulong(&bp->b_data[bo])*((u_int32_t *)(&bp->b_data[bo])); | |||
| ||||
| 854 | putulong(&bp->b_data[bo],(*((u_int32_t *)(&bp->b_data[bo])) = ((0 & 0x0fffffff ) | (cluster & ~0x0fffffff))) | |||
| 855 | (MSDOSFSFREE & FAT32_MASK) | (cluster & ~FAT32_MASK))(*((u_int32_t *)(&bp->b_data[bo])) = ((0 & 0x0fffffff ) | (cluster & ~0x0fffffff))); | |||
| 856 | break; | |||
| 857 | } | |||
| 858 | cluster &= pmp->pm_fatmask; | |||
| 859 | if ((cluster | ~pmp->pm_fatmask) >= CLUST_RSRVD0xfffffff6) | |||
| 860 | cluster |= pmp->pm_fatmask; | |||
| 861 | } | |||
| 862 | if (bp) | |||
| 863 | updatefats(pmp, bp, bn); | |||
| 864 | return (0); | |||
| 865 | } | |||
| 866 | ||||
| 867 | /* | |||
| 868 | * Read in fat blocks looking for free clusters. For every free cluster | |||
| 869 | * found turn off its corresponding bit in the pm_inusemap. | |||
| 870 | */ | |||
| 871 | int | |||
| 872 | fillinusemap(struct msdosfsmount *pmp) | |||
| 873 | { | |||
| 874 | struct buf *bp = NULL((void *)0); | |||
| 875 | uint32_t cn, readcn; | |||
| 876 | int error; | |||
| 877 | uint32_t bn, bo, bsize, byteoffset; | |||
| 878 | ||||
| 879 | /* | |||
| 880 | * Mark all clusters in use, we mark the free ones in the fat scan | |||
| 881 | * loop further down. | |||
| 882 | */ | |||
| 883 | for (cn = 0; cn < howmany(pmp->pm_maxcluster + 1, N_INUSEBITS)(((pmp->pm_maxcluster + 1) + (((8 * sizeof(u_int))) - 1)) / ((8 * sizeof(u_int)))); cn++) | |||
| 884 | pmp->pm_inusemap[cn] = (u_int)-1; | |||
| 885 | ||||
| 886 | /* | |||
| 887 | * Figure how many free clusters are in the filesystem by ripping | |||
| 888 | * through the fat counting the number of entries whose content is | |||
| 889 | * zero. These represent free clusters. | |||
| 890 | */ | |||
| 891 | pmp->pm_freeclustercount = 0; | |||
| 892 | for (cn = CLUST_FIRST2; cn <= pmp->pm_maxcluster; cn++) { | |||
| 893 | byteoffset = FATOFS(pmp, cn)((cn) * (pmp)->pm_fatmult / (pmp)->pm_fatdiv); | |||
| 894 | bo = byteoffset % pmp->pm_fatblocksize; | |||
| 895 | if (!bo || !bp) { | |||
| 896 | /* Read new FAT block */ | |||
| 897 | if (bp) | |||
| 898 | brelse(bp); | |||
| 899 | fatblock(pmp, byteoffset, &bn, &bsize, NULL((void *)0)); | |||
| 900 | error = bread(pmp->pm_devvp, bn, bsize, &bp); | |||
| 901 | if (error) { | |||
| 902 | brelse(bp); | |||
| 903 | return (error); | |||
| 904 | } | |||
| 905 | } | |||
| 906 | if (FAT32(pmp)(pmp->pm_fatmask == 0x0fffffff)) | |||
| 907 | readcn = getulong(&bp->b_data[bo])*((u_int32_t *)(&bp->b_data[bo])); | |||
| 908 | else | |||
| 909 | readcn = getushort(&bp->b_data[bo])*((u_int16_t *)(&bp->b_data[bo])); | |||
| 910 | if (FAT12(pmp)(pmp->pm_fatmask == 0x00000fff) && (cn & 1)) | |||
| 911 | readcn >>= 4; | |||
| 912 | readcn &= pmp->pm_fatmask; | |||
| 913 | ||||
| 914 | if (readcn == 0) | |||
| 915 | usemap_free(pmp, cn); | |||
| 916 | } | |||
| 917 | brelse(bp); | |||
| 918 | return (0); | |||
| 919 | } | |||
| 920 | ||||
| 921 | /* | |||
| 922 | * Allocate a new cluster and chain it onto the end of the file. | |||
| 923 | * | |||
| 924 | * dep - the file to extend | |||
| 925 | * count - number of clusters to allocate | |||
| 926 | * bpp - where to return the address of the buf header for the first new | |||
| 927 | * file block | |||
| 928 | * ncp - where to put cluster number of the first newly allocated cluster | |||
| 929 | * If this pointer is 0, do not return the cluster number. | |||
| 930 | * flags - see fat.h | |||
| 931 | * | |||
| 932 | * NOTE: This function is not responsible for turning on the DE_UPDATE bit of | |||
| 933 | * the de_flag field of the denode and it does not change the de_FileSize | |||
| 934 | * field. This is left for the caller to do. | |||
| 935 | */ | |||
| 936 | int | |||
| 937 | extendfile(struct denode *dep, uint32_t count, struct buf **bpp, uint32_t *ncp, | |||
| 938 | int flags) | |||
| 939 | { | |||
| 940 | int error; | |||
| 941 | uint32_t frcn; | |||
| 942 | uint32_t cn, got; | |||
| 943 | struct msdosfsmount *pmp = dep->de_pmp; | |||
| 944 | struct buf *bp; | |||
| 945 | ||||
| 946 | /* | |||
| 947 | * Don't try to extend the root directory | |||
| 948 | */ | |||
| 949 | if (dep->de_StartCluster == MSDOSFSROOT0 | |||
| 950 | && (dep->de_Attributes & ATTR_DIRECTORY0x10)) { | |||
| 951 | printf("extendfile(): attempt to extend root directory\n"); | |||
| 952 | return (ENOSPC28); | |||
| 953 | } | |||
| 954 | ||||
| 955 | /* | |||
| 956 | * If the "file's last cluster" cache entry is empty, and the file | |||
| 957 | * is not empty, then fill the cache entry by calling pcbmap(). | |||
| 958 | */ | |||
| 959 | fc_fileextends++; | |||
| 960 | if (dep->de_fc[FC_LASTFC1].fc_frcn == FCE_EMPTY0xffffffff && | |||
| 961 | dep->de_StartCluster != 0) { | |||
| 962 | fc_lfcempty++; | |||
| 963 | error = pcbmap(dep, CLUST_END0xffffffff, 0, &cn, 0); | |||
| 964 | /* we expect it to return E2BIG */ | |||
| 965 | if (error != E2BIG7) | |||
| 966 | return (error); | |||
| 967 | } | |||
| 968 | ||||
| 969 | /* | |||
| 970 | * Preserve value for the last cluster before extending the file | |||
| 971 | * to speed up further lookups. | |||
| 972 | */ | |||
| 973 | fc_setcache(dep, FC_OLASTFC, dep->de_fc[FC_LASTFC].fc_frcn,(dep)->de_fc[2].fc_frcn = dep->de_fc[1].fc_frcn; (dep)-> de_fc[2].fc_fsrcn = dep->de_fc[1].fc_fsrcn; | |||
| 974 | dep->de_fc[FC_LASTFC].fc_fsrcn)(dep)->de_fc[2].fc_frcn = dep->de_fc[1].fc_frcn; (dep)-> de_fc[2].fc_fsrcn = dep->de_fc[1].fc_fsrcn;; | |||
| 975 | ||||
| 976 | while (count > 0) { | |||
| 977 | /* | |||
| 978 | * Allocate a new cluster chain and cat onto the end of the | |||
| 979 | * file. * If the file is empty we make de_StartCluster point | |||
| 980 | * to the new block. Note that de_StartCluster being 0 is | |||
| 981 | * sufficient to be sure the file is empty since we exclude | |||
| 982 | * attempts to extend the root directory above, and the root | |||
| 983 | * dir is the only file with a startcluster of 0 that has | |||
| 984 | * blocks allocated (sort of). | |||
| 985 | */ | |||
| 986 | if (dep->de_StartCluster == 0) | |||
| 987 | cn = 0; | |||
| 988 | else | |||
| 989 | cn = dep->de_fc[FC_LASTFC1].fc_fsrcn + 1; | |||
| 990 | error = clusteralloc(pmp, cn, count, &cn, &got); | |||
| 991 | if (error) | |||
| 992 | return (error); | |||
| 993 | ||||
| 994 | count -= got; | |||
| 995 | ||||
| 996 | /* | |||
| 997 | * Give them the filesystem relative cluster number if they want | |||
| 998 | * it. | |||
| 999 | */ | |||
| 1000 | if (ncp) { | |||
| 1001 | *ncp = cn; | |||
| 1002 | ncp = NULL((void *)0); | |||
| 1003 | } | |||
| 1004 | ||||
| 1005 | if (dep->de_StartCluster == 0) { | |||
| 1006 | dep->de_StartCluster = cn; | |||
| 1007 | frcn = 0; | |||
| 1008 | } else { | |||
| 1009 | error = fatentry(FAT_SET0x0002, pmp, | |||
| 1010 | dep->de_fc[FC_LASTFC1].fc_fsrcn, | |||
| 1011 | 0, cn); | |||
| 1012 | if (error) { | |||
| 1013 | clusterfree(pmp, cn, NULL((void *)0)); | |||
| 1014 | return (error); | |||
| 1015 | } | |||
| 1016 | frcn = dep->de_fc[FC_LASTFC1].fc_frcn + 1; | |||
| 1017 | } | |||
| 1018 | ||||
| 1019 | /* | |||
| 1020 | * Update the "last cluster of the file" entry in the denode's fat | |||
| 1021 | * cache. | |||
| 1022 | */ | |||
| 1023 | fc_setcache(dep, FC_LASTFC, frcn + got - 1, cn + got - 1)(dep)->de_fc[1].fc_frcn = frcn + got - 1; (dep)->de_fc[ 1].fc_fsrcn = cn + got - 1;; | |||
| 1024 | ||||
| 1025 | if (flags & DE_CLEAR1) { | |||
| 1026 | while (got-- > 0) { | |||
| 1027 | /* | |||
| 1028 | * Get the buf header for the new block of the file. | |||
| 1029 | */ | |||
| 1030 | if (dep->de_Attributes & ATTR_DIRECTORY0x10) | |||
| 1031 | bp = getblk(pmp->pm_devvp, cntobn(pmp, cn++)((((cn++)-2) << (((pmp))->pm_cnshift - ((pmp))->pm_bnshift )) + (pmp)->pm_firstcluster), | |||
| 1032 | pmp->pm_bpcluster, 0, INFSLP0xffffffffffffffffULL); | |||
| 1033 | else { | |||
| 1034 | bp = getblk(DETOV(dep)((dep)->de_vnode), frcn++, | |||
| 1035 | pmp->pm_bpcluster, 0, INFSLP0xffffffffffffffffULL); | |||
| 1036 | /* | |||
| 1037 | * Do the bmap now, as in msdosfs_write | |||
| 1038 | */ | |||
| 1039 | if (pcbmap(dep, bp->b_lblkno, &bp->b_blkno, 0, 0)) | |||
| 1040 | bp->b_blkno = -1; | |||
| 1041 | if (bp->b_blkno == -1) | |||
| 1042 | panic("extendfile: pcbmap"); | |||
| 1043 | } | |||
| 1044 | clrbuf(bp){ __builtin_bzero(((bp)->b_data), ((bp)->b_bcount)); (bp )->b_resid = 0; }; | |||
| 1045 | if (bpp) { | |||
| 1046 | *bpp = bp; | |||
| 1047 | bpp = NULL((void *)0); | |||
| 1048 | } else | |||
| 1049 | bdwrite(bp); | |||
| 1050 | } | |||
| 1051 | } | |||
| 1052 | } | |||
| 1053 | ||||
| 1054 | return (0); | |||
| 1055 | } |