Commit | Line | Data |
---|---|---|
8b257845 | 1 | /* $FreeBSD$ */ |
984263bc MD |
2 | /* $NetBSD: msdosfs_fat.c,v 1.28 1997/11/17 15:36:49 ws Exp $ */ |
3 | ||
4 | /*- | |
8b257845 TK |
5 | * SPDX-License-Identifier: BSD-4-Clause |
6 | * | |
984263bc MD |
7 | * Copyright (C) 1994, 1995, 1997 Wolfgang Solfrank. |
8 | * Copyright (C) 1994, 1995, 1997 TooLs GmbH. | |
9 | * All rights reserved. | |
10 | * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below). | |
11 | * | |
12 | * Redistribution and use in source and binary forms, with or without | |
13 | * modification, are permitted provided that the following conditions | |
14 | * are met: | |
15 | * 1. Redistributions of source code must retain the above copyright | |
16 | * notice, this list of conditions and the following disclaimer. | |
17 | * 2. Redistributions in binary form must reproduce the above copyright | |
18 | * notice, this list of conditions and the following disclaimer in the | |
19 | * documentation and/or other materials provided with the distribution. | |
20 | * 3. All advertising materials mentioning features or use of this software | |
21 | * must display the following acknowledgement: | |
22 | * This product includes software developed by TooLs GmbH. | |
23 | * 4. The name of TooLs GmbH may not be used to endorse or promote products | |
24 | * derived from this software without specific prior written permission. | |
25 | * | |
26 | * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR | |
27 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | |
28 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | |
29 | * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
30 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |
31 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; | |
32 | * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, | |
33 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR | |
34 | * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF | |
35 | * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
36 | */ | |
8b257845 | 37 | /*- |
984263bc MD |
38 | * Written by Paul Popelka (paulp@uts.amdahl.com) |
39 | * | |
40 | * You can do anything you want with this software, just don't say you wrote | |
41 | * it, and don't remove this notice. | |
42 | * | |
43 | * This software is provided "as is". | |
44 | * | |
45 | * The author supplies this software to be publicly redistributed on the | |
46 | * understanding that the author is not responsible for the correct | |
47 | * functioning of this software in any circumstances and is not liable for | |
48 | * any damages caused by this software. | |
49 | * | |
50 | * October 1992 | |
51 | */ | |
52 | ||
984263bc MD |
53 | #include <sys/param.h> |
54 | #include <sys/systm.h> | |
55 | #include <sys/buf.h> | |
f66b17ff TK |
56 | #include <sys/mount.h> |
57 | #include <sys/vnode.h> | |
984263bc | 58 | |
54341a3b MD |
59 | #include <sys/buf2.h> |
60 | ||
32ef2986 TK |
61 | #include <vfs/msdosfs/bpb.h> |
62 | #include <vfs/msdosfs/direntry.h> | |
63 | #include <vfs/msdosfs/denode.h> | |
64 | #include <vfs/msdosfs/fat.h> | |
65 | #include <vfs/msdosfs/msdosfsmount.h> | |
984263bc | 66 | |
e93dba1e TK |
67 | #define FULL_RUN ((u_int)0xffffffff) |
68 | ||
f66b17ff TK |
69 | static void fc_lookup(struct denode *dep, u_long findcn, u_long *frcnp, |
70 | u_long *fsrcnp); | |
71 | static int clusteralloc1(struct msdosfsmount *pmp, u_long start, | |
72 | u_long count, u_long fillwith, u_long *retcluster, | |
73 | u_long *got); | |
984263bc | 74 | |
452d036c TK |
75 | /* |
76 | * Given a byte offset `ofs` within FAT, return block number in backing device, | |
77 | * block size, and byte offset within a block in FAT. | |
78 | */ | |
984263bc | 79 | static void |
4625f023 | 80 | fatblock(struct msdosfsmount *pmp, u_long ofs, u_long *bnp, u_long *sizep, |
8b257845 | 81 | u_long *bop) |
984263bc MD |
82 | { |
83 | u_long bn, size; | |
84 | ||
85 | bn = ofs / pmp->pm_fatblocksize * pmp->pm_fatblocksec; | |
5d6f1a6b | 86 | size = min(pmp->pm_fatblocksec, pmp->pm_FATsecs - bn) * DEV_BSIZE; |
984263bc MD |
87 | bn += pmp->pm_fatblk + pmp->pm_curfat * pmp->pm_FATsecs; |
88 | ||
89 | if (bnp) | |
90 | *bnp = bn; | |
91 | if (sizep) | |
92 | *sizep = size; | |
93 | if (bop) | |
94 | *bop = ofs % pmp->pm_fatblocksize; | |
95 | } | |
96 | ||
97 | /* | |
98 | * Map the logical cluster number of a file into a physical disk sector | |
99 | * that is filesystem relative. | |
100 | * | |
101 | * dep - address of denode representing the file of interest | |
102 | * findcn - file relative cluster whose filesystem relative cluster number | |
103 | * and/or block number are/is to be found | |
8b257845 | 104 | * bnp - address of where to place the filesystem relative block number. |
984263bc | 105 | * If this pointer is null then don't return this quantity. |
8b257845 | 106 | * cnp - address of where to place the filesystem relative cluster number. |
984263bc | 107 | * If this pointer is null then don't return this quantity. |
8b257845 | 108 | * sp - pointer to returned block size |
984263bc MD |
109 | * |
110 | * NOTE: Either bnp or cnp must be non-null. | |
111 | * This function has one side effect. If the requested file relative cluster | |
112 | * is beyond the end of file, then the actual number of clusters in the file | |
113 | * is returned in *cnp. This is useful for determining how long a directory is. | |
114 | * If cnp is null, nothing is returned. | |
115 | */ | |
116 | int | |
665cb334 | 117 | pcbmap(struct denode *dep, u_long findcn, daddr_t *bnp, u_long *cnp, int *sp) |
984263bc MD |
118 | { |
119 | int error; | |
120 | u_long i; | |
121 | u_long cn; | |
122 | u_long prevcn = 0; /* XXX: prevcn could be used unititialized */ | |
123 | u_long byteoffset; | |
124 | u_long bn; | |
125 | u_long bo; | |
126 | struct buf *bp = NULL; | |
127 | u_long bp_bn = -1; | |
128 | struct msdosfsmount *pmp = dep->de_pmp; | |
129 | u_long bsize; | |
130 | ||
9157abdc TK |
131 | KASSERT(bnp != NULL || cnp != NULL || sp != NULL, |
132 | ("pcbmap: extra call")); | |
bc5f1166 | 133 | ASSERT_VOP_ELOCKED(DETOV(dep), "pcbmap"); |
984263bc MD |
134 | |
135 | cn = dep->de_StartCluster; | |
136 | /* | |
137 | * The "file" that makes up the root directory is contiguous, | |
138 | * permanently allocated, of fixed size, and is not made up of | |
139 | * clusters. If the cluster number is beyond the end of the root | |
140 | * directory, then return the number of clusters in the file. | |
141 | */ | |
142 | if (cn == MSDOSFSROOT) { | |
143 | if (dep->de_Attributes & ATTR_DIRECTORY) { | |
144 | if (de_cn2off(pmp, findcn) >= dep->de_FileSize) { | |
145 | if (cnp) | |
59127828 TK |
146 | *cnp = de_bn2cn(pmp, |
147 | pmp->pm_rootdirsize); | |
984263bc MD |
148 | return (E2BIG); |
149 | } | |
150 | if (bnp) | |
59127828 TK |
151 | *bnp = pmp->pm_rootdirblk + |
152 | de_cn2bn(pmp, findcn); | |
984263bc MD |
153 | if (cnp) |
154 | *cnp = MSDOSFSROOT; | |
155 | if (sp) | |
156 | *sp = min(pmp->pm_bpcluster, | |
157 | dep->de_FileSize - de_cn2off(pmp, findcn)); | |
158 | return (0); | |
159 | } else { /* just an empty file */ | |
160 | if (cnp) | |
161 | *cnp = 0; | |
162 | return (E2BIG); | |
163 | } | |
164 | } | |
165 | ||
166 | /* | |
167 | * All other files do I/O in cluster sized blocks | |
168 | */ | |
169 | if (sp) | |
170 | *sp = pmp->pm_bpcluster; | |
171 | ||
172 | /* | |
8b257845 TK |
173 | * Rummage around in the FAT cache, maybe we can avoid tromping |
174 | * through every FAT entry for the file. And, keep track of how far | |
984263bc MD |
175 | * off the cache was from where we wanted to be. |
176 | */ | |
177 | i = 0; | |
178 | fc_lookup(dep, findcn, &i, &cn); | |
984263bc MD |
179 | |
180 | /* | |
181 | * Handle all other files or directories the normal way. | |
182 | */ | |
183 | for (; i < findcn; i++) { | |
184 | /* | |
185 | * Stop with all reserved clusters, not just with EOF. | |
186 | */ | |
187 | if ((cn | ~pmp->pm_fatmask) >= CLUST_RSRVD) | |
188 | goto hiteof; | |
189 | byteoffset = FATOFS(pmp, cn); | |
190 | fatblock(pmp, byteoffset, &bn, &bsize, &bo); | |
191 | if (bn != bp_bn) { | |
192 | if (bp) | |
193 | brelse(bp); | |
da94bdcb | 194 | error = bread(pmp->pm_devvp, de_bn2doff(pmp, bn), |
59127828 | 195 | bsize, &bp); |
984263bc MD |
196 | if (error) { |
197 | brelse(bp); | |
198 | return (error); | |
199 | } | |
200 | bp_bn = bn; | |
201 | } | |
202 | prevcn = cn; | |
32cb1b04 TK |
203 | if (bo >= bsize) { |
204 | if (bp) | |
205 | brelse(bp); | |
206 | return (EIO); | |
207 | } | |
984263bc | 208 | if (FAT32(pmp)) |
8b257845 | 209 | cn = getulong(bp->b_data + bo); |
984263bc | 210 | else |
8b257845 | 211 | cn = getushort(bp->b_data + bo); |
984263bc MD |
212 | if (FAT12(pmp) && (prevcn & 1)) |
213 | cn >>= 4; | |
214 | cn &= pmp->pm_fatmask; | |
215 | ||
216 | /* | |
217 | * Force the special cluster numbers | |
218 | * to be the same for all cluster sizes | |
219 | * to let the rest of msdosfs handle | |
220 | * all cases the same. | |
221 | */ | |
222 | if ((cn | ~pmp->pm_fatmask) >= CLUST_RSRVD) | |
223 | cn |= ~pmp->pm_fatmask; | |
224 | } | |
225 | ||
226 | if (!MSDOSFSEOF(pmp, cn)) { | |
227 | if (bp) | |
228 | brelse(bp); | |
229 | if (bnp) | |
2184ce67 | 230 | *bnp = cntobn(pmp, cn); |
984263bc MD |
231 | if (cnp) |
232 | *cnp = cn; | |
233 | fc_setcache(dep, FC_LASTMAP, i, cn); | |
234 | return (0); | |
235 | } | |
236 | ||
5d6f1a6b | 237 | hiteof: |
984263bc MD |
238 | if (cnp) |
239 | *cnp = i; | |
240 | if (bp) | |
241 | brelse(bp); | |
8b257845 | 242 | /* update last file cluster entry in the FAT cache */ |
984263bc MD |
243 | fc_setcache(dep, FC_LASTFC, i - 1, prevcn); |
244 | return (E2BIG); | |
245 | } | |
246 | ||
247 | /* | |
8b257845 | 248 | * Find the closest entry in the FAT cache to the cluster we are looking |
984263bc MD |
249 | * for. |
250 | */ | |
251 | static void | |
4625f023 | 252 | fc_lookup(struct denode *dep, u_long findcn, u_long *frcnp, u_long *fsrcnp) |
984263bc MD |
253 | { |
254 | int i; | |
255 | u_long cn; | |
4090d6ff | 256 | struct fatcache *closest = NULL; |
984263bc | 257 | |
bc5f1166 TK |
258 | ASSERT_VOP_LOCKED(DETOV(dep), "fc_lookup"); |
259 | ||
984263bc MD |
260 | for (i = 0; i < FC_SIZE; i++) { |
261 | cn = dep->de_fc[i].fc_frcn; | |
262 | if (cn != FCE_EMPTY && cn <= findcn) { | |
4090d6ff | 263 | if (closest == NULL || cn > closest->fc_frcn) |
984263bc MD |
264 | closest = &dep->de_fc[i]; |
265 | } | |
266 | } | |
267 | if (closest) { | |
268 | *frcnp = closest->fc_frcn; | |
269 | *fsrcnp = closest->fc_fsrcn; | |
270 | } | |
271 | } | |
272 | ||
273 | /* | |
8b257845 | 274 | * Purge the FAT cache in denode dep of all entries relating to file |
984263bc MD |
275 | * relative cluster frcn and beyond. |
276 | */ | |
277 | void | |
4625f023 | 278 | fc_purge(struct denode *dep, u_int frcn) |
984263bc MD |
279 | { |
280 | int i; | |
281 | struct fatcache *fcp; | |
282 | ||
bc5f1166 TK |
283 | ASSERT_VOP_ELOCKED(DETOV(dep), "fc_purge"); |
284 | ||
984263bc MD |
285 | fcp = dep->de_fc; |
286 | for (i = 0; i < FC_SIZE; i++, fcp++) { | |
287 | if (fcp->fc_frcn >= frcn) | |
288 | fcp->fc_frcn = FCE_EMPTY; | |
289 | } | |
290 | } | |
291 | ||
292 | /* | |
8b257845 TK |
293 | * Update the FAT. |
294 | * If mirroring the FAT, update all copies, with the first copy as last. | |
295 | * Else update only the current FAT (ignoring the others). | |
984263bc MD |
296 | * |
297 | * pmp - msdosfsmount structure for filesystem to update | |
8b257845 TK |
298 | * bp - addr of modified FAT block |
299 | * fatbn - block number relative to begin of filesystem of the modified FAT block. | |
984263bc MD |
300 | */ |
301 | static void | |
4625f023 | 302 | updatefats(struct msdosfsmount *pmp, struct buf *bp, u_long fatbn) |
984263bc | 303 | { |
984263bc | 304 | struct buf *bpn; |
5b9edd92 | 305 | int cleanfat, i; |
984263bc | 306 | |
0417b940 | 307 | mprintf("updatefats(pmp %p, bp %p, fatbn %lu)\n", pmp, bp, fatbn); |
984263bc | 308 | |
984263bc MD |
309 | if (pmp->pm_flags & MSDOSFS_FATMIRROR) { |
310 | /* | |
8b257845 TK |
311 | * Now copy the block(s) of the modified FAT to the other copies of |
312 | * the FAT and write them out. This is faster than reading in the | |
313 | * other FATs and then writing them back out. This could tie up | |
314 | * the FAT for quite a while. Preventing others from accessing it. | |
315 | * To prevent us from going after the FAT quite so much we use | |
316 | * delayed writes, unless they specified "synchronous" when the | |
984263bc MD |
317 | * filesystem was mounted. If synch is asked for then use |
318 | * bwrite()'s and really slow things down. | |
319 | */ | |
5b9edd92 TK |
320 | if (fatbn != pmp->pm_fatblk || FAT12(pmp)) |
321 | cleanfat = 0; | |
322 | else if (FAT16(pmp)) | |
323 | cleanfat = 16; | |
324 | else | |
325 | cleanfat = 32; | |
984263bc MD |
326 | for (i = 1; i < pmp->pm_FATs; i++) { |
327 | fatbn += pmp->pm_FATsecs; | |
328 | /* getblk() never fails */ | |
da94bdcb | 329 | bpn = getblk(pmp->pm_devvp, de_bn2doff(pmp, fatbn), |
54078292 | 330 | bp->b_bcount, 0, 0); |
65c11747 | 331 | memcpy(bpn->b_data, bp->b_data, bp->b_bcount); |
5b9edd92 TK |
332 | /* Force the clean bit on in the other copies. */ |
333 | if (cleanfat == 16) | |
334 | ((uint8_t *)bpn->b_data)[3] |= 0x80; | |
335 | else if (cleanfat == 32) | |
336 | ((uint8_t *)bpn->b_data)[7] |= 0x08; | |
ab67bc4e | 337 | if (pmp->pm_mountp->mnt_flag & MNT_SYNCHRONOUS) |
984263bc MD |
338 | bwrite(bpn); |
339 | else | |
340 | bdwrite(bpn); | |
341 | } | |
342 | } | |
343 | ||
344 | /* | |
8b257845 | 345 | * Write out the first (or current) FAT last. |
984263bc | 346 | */ |
ab67bc4e | 347 | if (pmp->pm_mountp->mnt_flag & MNT_SYNCHRONOUS) |
984263bc MD |
348 | bwrite(bp); |
349 | else | |
350 | bdwrite(bp); | |
984263bc MD |
351 | } |
352 | ||
353 | /* | |
8b257845 | 354 | * Updating entries in 12 bit FATs is a pain in the butt. |
984263bc MD |
355 | * |
356 | * The following picture shows where nibbles go when moving from a 12 bit | |
357 | * cluster number into the appropriate bytes in the FAT. | |
358 | * | |
359 | * byte m byte m+1 byte m+2 | |
360 | * +----+----+ +----+----+ +----+----+ | |
361 | * | 0 1 | | 2 3 | | 4 5 | FAT bytes | |
362 | * +----+----+ +----+----+ +----+----+ | |
363 | * | |
364 | * +----+----+----+ +----+----+----+ | |
365 | * | 3 0 1 | | 4 5 2 | | |
366 | * +----+----+----+ +----+----+----+ | |
367 | * cluster n cluster n+1 | |
368 | * | |
369 | * Where n is even. m = n + (n >> 2) | |
370 | * | |
371 | */ | |
372 | static __inline void | |
4625f023 | 373 | usemap_alloc(struct msdosfsmount *pmp, u_long cn) |
984263bc | 374 | { |
3b04d1bc TK |
375 | MSDOSFS_ASSERT_MP_LOCKED(pmp); |
376 | ||
3494b87c TK |
377 | KASSERT(cn <= pmp->pm_maxcluster, ("cn too large %lu %lu", cn, |
378 | pmp->pm_maxcluster)); | |
33b97ce2 TK |
379 | KASSERT((pmp->pm_flags & MSDOSFSMNT_RONLY) == 0, |
380 | ("usemap_alloc on ro msdosfs mount")); | |
b4dc4e98 TK |
381 | KASSERT((pmp->pm_inusemap[cn / N_INUSEBITS] & |
382 | (1U << (cn % N_INUSEBITS))) == 0, | |
383 | ("Allocating used sector %ld %ld %x", cn, cn % N_INUSEBITS, | |
384 | (unsigned)pmp->pm_inusemap[cn / N_INUSEBITS])); | |
0e34c3a0 | 385 | pmp->pm_inusemap[cn / N_INUSEBITS] |= 1U << (cn % N_INUSEBITS); |
c91717f4 | 386 | KASSERT(pmp->pm_freeclustercount > 0, ("usemap_alloc: too little")); |
984263bc | 387 | pmp->pm_freeclustercount--; |
fa58c1da | 388 | pmp->pm_flags |= MSDOSFS_FSIMOD; |
984263bc MD |
389 | } |
390 | ||
5dd3291f | 391 | static int |
4625f023 | 392 | usemap_free(struct msdosfsmount *pmp, u_long cn) |
984263bc | 393 | { |
3b04d1bc TK |
394 | MSDOSFS_ASSERT_MP_LOCKED(pmp); |
395 | ||
3494b87c TK |
396 | KASSERT(cn <= pmp->pm_maxcluster, ("cn too large %lu %lu", cn, |
397 | pmp->pm_maxcluster)); | |
33b97ce2 TK |
398 | KASSERT((pmp->pm_flags & MSDOSFSMNT_RONLY) == 0, |
399 | ("usemap_free on ro msdosfs mount")); | |
5dd3291f TK |
400 | if ((pmp->pm_inusemap[cn / N_INUSEBITS] & |
401 | (1U << (cn % N_INUSEBITS))) == 0) { | |
402 | kprintf("%s: Freeing unused sector %ld %ld %x\n", | |
403 | pmp->pm_mountp->mnt_stat.f_mntonname, cn, cn % N_INUSEBITS, | |
404 | (unsigned)pmp->pm_inusemap[cn / N_INUSEBITS]); | |
405 | return (EINVAL); | |
406 | } | |
984263bc | 407 | pmp->pm_freeclustercount++; |
fa58c1da | 408 | pmp->pm_flags |= MSDOSFS_FSIMOD; |
0e34c3a0 | 409 | pmp->pm_inusemap[cn / N_INUSEBITS] &= ~(1U << (cn % N_INUSEBITS)); |
5dd3291f | 410 | return (0); |
984263bc MD |
411 | } |
412 | ||
011c75d3 TK |
413 | void |
414 | clusterfree(struct msdosfsmount *pmp, u_long cluster) | |
984263bc MD |
415 | { |
416 | int error; | |
417 | u_long oldcn; | |
418 | ||
984263bc | 419 | error = fatentry(FAT_GET_AND_SET, pmp, cluster, &oldcn, MSDOSFSFREE); |
011c75d3 TK |
420 | if (error != 0) |
421 | return; | |
984263bc MD |
422 | /* |
423 | * If the cluster was successfully marked free, then update | |
424 | * the count of free clusters, and turn off the "allocated" | |
425 | * bit in the "in use" cluster bit map. | |
426 | */ | |
3b04d1bc | 427 | MSDOSFS_LOCK_MP(pmp); |
5dd3291f | 428 | error = usemap_free(pmp, cluster); |
3b04d1bc | 429 | MSDOSFS_UNLOCK_MP(pmp); |
984263bc MD |
430 | } |
431 | ||
432 | /* | |
8b257845 | 433 | * Get or Set or 'Get and Set' the cluster'th entry in the FAT. |
984263bc | 434 | * |
8b257845 | 435 | * function - whether to get or set a FAT entry |
984263bc | 436 | * pmp - address of the msdosfsmount structure for the filesystem |
8b257845 | 437 | * whose FAT is to be manipulated. |
984263bc MD |
438 | * cn - which cluster is of interest |
439 | * oldcontents - address of a word that is to receive the contents of the | |
440 | * cluster'th entry if this is a get function | |
441 | * newcontents - the new value to be written into the cluster'th element of | |
8b257845 | 442 | * the FAT if this is a set function. |
984263bc | 443 | * |
8b257845 | 444 | * This function can also be used to free a cluster by setting the FAT entry |
984263bc MD |
445 | * for a cluster to 0. |
446 | * | |
8b257845 | 447 | * All copies of the FAT are updated if this is a set function. NOTE: If |
984263bc MD |
448 | * fatentry() marks a cluster as free it does not update the inusemap in |
449 | * the msdosfsmount structure. This is left to the caller. | |
450 | */ | |
451 | int | |
4625f023 | 452 | fatentry(int function, struct msdosfsmount *pmp, u_long cn, u_long *oldcontents, |
8b257845 | 453 | u_long newcontents) |
984263bc MD |
454 | { |
455 | int error; | |
456 | u_long readcn; | |
457 | u_long bn, bo, bsize, byteoffset; | |
458 | struct buf *bp; | |
459 | ||
0417b940 | 460 | mprintf("fatentry(func %d, pmp %p, clust %lu, oldcon %p, newcon %lx)\n", |
fbae82b1 | 461 | function, pmp, cn, oldcontents, newcontents); |
984263bc MD |
462 | |
463 | #ifdef DIAGNOSTIC | |
464 | /* | |
465 | * Be sure they asked us to do something. | |
466 | */ | |
467 | if ((function & (FAT_SET | FAT_GET)) == 0) { | |
086c1d7e | 468 | kprintf("fatentry(): function code doesn't specify get or set\n"); |
984263bc MD |
469 | return (EINVAL); |
470 | } | |
471 | ||
472 | /* | |
473 | * If they asked us to return a cluster number but didn't tell us | |
474 | * where to put it, give them an error. | |
475 | */ | |
476 | if ((function & FAT_GET) && oldcontents == NULL) { | |
086c1d7e | 477 | kprintf("fatentry(): get function with no place to put result\n"); |
984263bc MD |
478 | return (EINVAL); |
479 | } | |
480 | #endif | |
481 | ||
482 | /* | |
483 | * Be sure the requested cluster is in the filesystem. | |
484 | */ | |
485 | if (cn < CLUST_FIRST || cn > pmp->pm_maxcluster) | |
486 | return (EINVAL); | |
487 | ||
488 | byteoffset = FATOFS(pmp, cn); | |
489 | fatblock(pmp, byteoffset, &bn, &bsize, &bo); | |
da94bdcb | 490 | error = bread(pmp->pm_devvp, de_bn2doff(pmp, bn), bsize, &bp); |
984263bc MD |
491 | if (error) { |
492 | brelse(bp); | |
493 | return (error); | |
494 | } | |
495 | ||
496 | if (function & FAT_GET) { | |
497 | if (FAT32(pmp)) | |
8b257845 | 498 | readcn = getulong(bp->b_data + bo); |
984263bc | 499 | else |
8b257845 | 500 | readcn = getushort(bp->b_data + bo); |
984263bc MD |
501 | if (FAT12(pmp) & (cn & 1)) |
502 | readcn >>= 4; | |
503 | readcn &= pmp->pm_fatmask; | |
8b257845 | 504 | /* map reserved FAT entries to same values for all FATs */ |
984263bc MD |
505 | if ((readcn | ~pmp->pm_fatmask) >= CLUST_RSRVD) |
506 | readcn |= ~pmp->pm_fatmask; | |
507 | *oldcontents = readcn; | |
508 | } | |
509 | if (function & FAT_SET) { | |
510 | switch (pmp->pm_fatmask) { | |
511 | case FAT12_MASK: | |
8b257845 | 512 | readcn = getushort(bp->b_data + bo); |
984263bc MD |
513 | if (cn & 1) { |
514 | readcn &= 0x000f; | |
515 | readcn |= newcontents << 4; | |
516 | } else { | |
517 | readcn &= 0xf000; | |
518 | readcn |= newcontents & 0xfff; | |
519 | } | |
8b257845 | 520 | putushort(bp->b_data + bo, readcn); |
984263bc MD |
521 | break; |
522 | case FAT16_MASK: | |
8b257845 | 523 | putushort(bp->b_data + bo, newcontents); |
984263bc MD |
524 | break; |
525 | case FAT32_MASK: | |
526 | /* | |
527 | * According to spec we have to retain the | |
8b257845 | 528 | * high order bits of the FAT entry. |
984263bc | 529 | */ |
8b257845 | 530 | readcn = getulong(bp->b_data + bo); |
984263bc MD |
531 | readcn &= ~FAT32_MASK; |
532 | readcn |= newcontents & FAT32_MASK; | |
8b257845 | 533 | putulong(bp->b_data + bo, readcn); |
984263bc MD |
534 | break; |
535 | } | |
536 | updatefats(pmp, bp, bn); | |
537 | bp = NULL; | |
538 | pmp->pm_fmod = 1; | |
539 | } | |
540 | if (bp) | |
541 | brelse(bp); | |
542 | return (0); | |
543 | } | |
544 | ||
545 | /* | |
546 | * Update a contiguous cluster chain | |
547 | * | |
548 | * pmp - mount point | |
549 | * start - first cluster of chain | |
550 | * count - number of clusters in chain | |
8b257845 | 551 | * fillwith - what to write into FAT entry of last cluster |
984263bc MD |
552 | */ |
553 | static int | |
4625f023 | 554 | fatchain(struct msdosfsmount *pmp, u_long start, u_long count, u_long fillwith) |
984263bc MD |
555 | { |
556 | int error; | |
557 | u_long bn, bo, bsize, byteoffset, readcn, newc; | |
558 | struct buf *bp; | |
559 | ||
0417b940 | 560 | mprintf("fatchain(pmp %p, start %lu, count %lu, fillwith %lx)\n", |
984263bc | 561 | pmp, start, count, fillwith); |
984263bc MD |
562 | /* |
563 | * Be sure the clusters are in the filesystem. | |
564 | */ | |
565 | if (start < CLUST_FIRST || start + count - 1 > pmp->pm_maxcluster) | |
566 | return (EINVAL); | |
567 | ||
568 | while (count > 0) { | |
569 | byteoffset = FATOFS(pmp, start); | |
570 | fatblock(pmp, byteoffset, &bn, &bsize, &bo); | |
da94bdcb | 571 | error = bread(pmp->pm_devvp, de_bn2doff(pmp, bn), bsize, &bp); |
984263bc MD |
572 | if (error) { |
573 | brelse(bp); | |
574 | return (error); | |
575 | } | |
576 | while (count > 0) { | |
452d036c TK |
577 | start++; /* Callers must guarantee contiguous free |
578 | clusters. */ | |
984263bc MD |
579 | newc = --count > 0 ? start : fillwith; |
580 | switch (pmp->pm_fatmask) { | |
581 | case FAT12_MASK: | |
8b257845 | 582 | readcn = getushort(bp->b_data + bo); |
984263bc MD |
583 | if (start & 1) { |
584 | readcn &= 0xf000; | |
585 | readcn |= newc & 0xfff; | |
586 | } else { | |
587 | readcn &= 0x000f; | |
588 | readcn |= newc << 4; | |
589 | } | |
8b257845 | 590 | putushort(bp->b_data + bo, readcn); |
984263bc MD |
591 | bo++; |
592 | if (!(start & 1)) | |
593 | bo++; | |
594 | break; | |
595 | case FAT16_MASK: | |
8b257845 | 596 | putushort(bp->b_data + bo, newc); |
984263bc MD |
597 | bo += 2; |
598 | break; | |
599 | case FAT32_MASK: | |
8b257845 | 600 | readcn = getulong(bp->b_data + bo); |
984263bc MD |
601 | readcn &= ~pmp->pm_fatmask; |
602 | readcn |= newc & pmp->pm_fatmask; | |
8b257845 | 603 | putulong(bp->b_data + bo, readcn); |
984263bc MD |
604 | bo += 4; |
605 | break; | |
606 | } | |
607 | if (bo >= bsize) | |
608 | break; | |
609 | } | |
610 | updatefats(pmp, bp, bn); | |
611 | } | |
612 | pmp->pm_fmod = 1; | |
613 | return (0); | |
614 | } | |
615 | ||
616 | /* | |
617 | * Check the length of a free cluster chain starting at start. | |
618 | * | |
619 | * pmp - mount point | |
620 | * start - start of chain | |
621 | * count - maximum interesting length | |
622 | */ | |
623 | static int | |
4625f023 | 624 | chainlength(struct msdosfsmount *pmp, u_long start, u_long count) |
984263bc MD |
625 | { |
626 | u_long idx, max_idx; | |
627 | u_int map; | |
628 | u_long len; | |
629 | ||
3b04d1bc TK |
630 | MSDOSFS_ASSERT_MP_LOCKED(pmp); |
631 | ||
3494b87c TK |
632 | if (start > pmp->pm_maxcluster) |
633 | return (0); | |
984263bc MD |
634 | max_idx = pmp->pm_maxcluster / N_INUSEBITS; |
635 | idx = start / N_INUSEBITS; | |
636 | start %= N_INUSEBITS; | |
637 | map = pmp->pm_inusemap[idx]; | |
b4dc4e98 | 638 | map &= ~((1U << start) - 1); |
984263bc MD |
639 | if (map) { |
640 | len = ffs(map) - 1 - start; | |
3494b87c TK |
641 | len = MIN(len, count); |
642 | if (start + len > pmp->pm_maxcluster) | |
643 | len = pmp->pm_maxcluster - start + 1; | |
644 | return (len); | |
984263bc MD |
645 | } |
646 | len = N_INUSEBITS - start; | |
3494b87c TK |
647 | if (len >= count) { |
648 | len = count; | |
649 | if (start + len > pmp->pm_maxcluster) | |
650 | len = pmp->pm_maxcluster - start + 1; | |
651 | return (len); | |
652 | } | |
984263bc MD |
653 | while (++idx <= max_idx) { |
654 | if (len >= count) | |
655 | break; | |
656 | map = pmp->pm_inusemap[idx]; | |
657 | if (map) { | |
8b257845 | 658 | len += ffs(map) - 1; |
984263bc MD |
659 | break; |
660 | } | |
661 | len += N_INUSEBITS; | |
662 | } | |
3494b87c TK |
663 | len = MIN(len, count); |
664 | if (start + len > pmp->pm_maxcluster) | |
665 | len = pmp->pm_maxcluster - start + 1; | |
666 | return (len); | |
984263bc MD |
667 | } |
668 | ||
669 | /* | |
670 | * Allocate contigous free clusters. | |
671 | * | |
672 | * pmp - mount point. | |
673 | * start - start of cluster chain. | |
674 | * count - number of clusters to allocate. | |
8b257845 | 675 | * fillwith - put this value into the FAT entry for the |
984263bc MD |
676 | * last allocated cluster. |
677 | * retcluster - put the first allocated cluster's number here. | |
678 | * got - how many clusters were actually allocated. | |
679 | */ | |
680 | static int | |
4625f023 | 681 | chainalloc(struct msdosfsmount *pmp, u_long start, u_long count, |
8b257845 | 682 | u_long fillwith, u_long *retcluster, u_long *got) |
984263bc MD |
683 | { |
684 | int error; | |
685 | u_long cl, n; | |
686 | ||
3b04d1bc | 687 | MSDOSFS_ASSERT_MP_LOCKED(pmp); |
33b97ce2 TK |
688 | KASSERT((pmp->pm_flags & MSDOSFSMNT_RONLY) == 0, |
689 | ("chainalloc on ro msdosfs mount")); | |
690 | ||
984263bc MD |
691 | for (cl = start, n = count; n-- > 0;) |
692 | usemap_alloc(pmp, cl++); | |
693 | ||
fa58c1da TK |
694 | pmp->pm_nxtfree = start + count; |
695 | if (pmp->pm_nxtfree > pmp->pm_maxcluster) | |
696 | pmp->pm_nxtfree = CLUST_FIRST; | |
697 | pmp->pm_flags |= MSDOSFS_FSIMOD; | |
698 | ||
984263bc | 699 | error = fatchain(pmp, start, count, fillwith); |
33945121 TK |
700 | if (error != 0) { |
701 | for (cl = start, n = count; n-- > 0;) | |
5dd3291f | 702 | (void)usemap_free(pmp, cl++); |
984263bc | 703 | return (error); |
33945121 | 704 | } |
0417b940 | 705 | mprintf("clusteralloc(): allocated cluster chain at %lu (%lu clusters)\n", |
984263bc | 706 | start, count); |
984263bc MD |
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. | |
8b257845 | 720 | * fillwith - put this value into the FAT entry for the |
984263bc MD |
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 | |
4625f023 | 726 | clusteralloc(struct msdosfsmount *pmp, u_long start, u_long count, |
8b257845 | 727 | u_long fillwith, u_long *retcluster, u_long *got) |
1e8c72d4 TK |
728 | { |
729 | int error; | |
730 | ||
3b04d1bc | 731 | MSDOSFS_LOCK_MP(pmp); |
1e8c72d4 | 732 | error = clusteralloc1(pmp, start, count, fillwith, retcluster, got); |
3b04d1bc | 733 | MSDOSFS_UNLOCK_MP(pmp); |
1e8c72d4 TK |
734 | return (error); |
735 | } | |
736 | ||
737 | static int | |
738 | clusteralloc1(struct msdosfsmount *pmp, u_long start, u_long count, | |
739 | u_long fillwith, u_long *retcluster, u_long *got) | |
984263bc MD |
740 | { |
741 | u_long idx; | |
742 | u_long len, newst, foundl, cn, l; | |
743 | u_long foundcn = 0; /* XXX: foundcn could be used unititialized */ | |
744 | u_int map; | |
745 | ||
3b04d1bc TK |
746 | MSDOSFS_ASSERT_MP_LOCKED(pmp); |
747 | ||
0417b940 | 748 | mprintf("clusteralloc(): find %lu clusters\n",count); |
984263bc MD |
749 | if (start) { |
750 | if ((len = chainlength(pmp, start, count)) >= count) | |
59127828 TK |
751 | return (chainalloc(pmp, start, count, fillwith, |
752 | retcluster, got)); | |
753 | } else | |
984263bc MD |
754 | len = 0; |
755 | ||
354ceba4 | 756 | newst = pmp->pm_nxtfree; |
984263bc MD |
757 | foundl = 0; |
758 | ||
759 | for (cn = newst; cn <= pmp->pm_maxcluster;) { | |
760 | idx = cn / N_INUSEBITS; | |
761 | map = pmp->pm_inusemap[idx]; | |
0e34c3a0 | 762 | map |= (1U << (cn % N_INUSEBITS)) - 1; |
e93dba1e TK |
763 | if (map != FULL_RUN) { |
764 | cn = idx * N_INUSEBITS + ffs(map ^ FULL_RUN) - 1; | |
984263bc | 765 | if ((l = chainlength(pmp, cn, count)) >= count) |
59127828 TK |
766 | return (chainalloc(pmp, cn, count, fillwith, |
767 | retcluster, got)); | |
984263bc MD |
768 | if (l > foundl) { |
769 | foundcn = cn; | |
770 | foundl = l; | |
771 | } | |
772 | cn += l + 1; | |
773 | continue; | |
774 | } | |
775 | cn += N_INUSEBITS - cn % N_INUSEBITS; | |
776 | } | |
777 | for (cn = 0; cn < newst;) { | |
778 | idx = cn / N_INUSEBITS; | |
779 | map = pmp->pm_inusemap[idx]; | |
0e34c3a0 | 780 | map |= (1U << (cn % N_INUSEBITS)) - 1; |
e93dba1e TK |
781 | if (map != FULL_RUN) { |
782 | cn = idx * N_INUSEBITS + ffs(map ^ FULL_RUN) - 1; | |
984263bc | 783 | if ((l = chainlength(pmp, cn, count)) >= count) |
59127828 TK |
784 | return (chainalloc(pmp, cn, count, fillwith, |
785 | retcluster, got)); | |
984263bc MD |
786 | if (l > foundl) { |
787 | foundcn = cn; | |
788 | foundl = l; | |
789 | } | |
790 | cn += l + 1; | |
791 | continue; | |
792 | } | |
793 | cn += N_INUSEBITS - cn % N_INUSEBITS; | |
794 | } | |
795 | ||
796 | if (!foundl) | |
797 | return (ENOSPC); | |
798 | ||
799 | if (len) | |
800 | return (chainalloc(pmp, start, len, fillwith, retcluster, got)); | |
801 | else | |
59127828 TK |
802 | return (chainalloc(pmp, foundcn, foundl, fillwith, retcluster, |
803 | got)); | |
984263bc MD |
804 | } |
805 | ||
984263bc MD |
806 | /* |
807 | * Free a chain of clusters. | |
808 | * | |
809 | * pmp - address of the msdosfs mount structure for the filesystem | |
810 | * containing the cluster chain to be freed. | |
811 | * startcluster - number of the 1st cluster in the chain of clusters to be | |
812 | * freed. | |
813 | */ | |
814 | int | |
4625f023 | 815 | freeclusterchain(struct msdosfsmount *pmp, u_long cluster) |
984263bc MD |
816 | { |
817 | int error; | |
818 | struct buf *bp = NULL; | |
819 | u_long bn, bo, bsize, byteoffset; | |
820 | u_long readcn, lbn = -1; | |
821 | ||
3b04d1bc | 822 | MSDOSFS_LOCK_MP(pmp); |
984263bc MD |
823 | while (cluster >= CLUST_FIRST && cluster <= pmp->pm_maxcluster) { |
824 | byteoffset = FATOFS(pmp, cluster); | |
825 | fatblock(pmp, byteoffset, &bn, &bsize, &bo); | |
826 | if (lbn != bn) { | |
827 | if (bp) | |
828 | updatefats(pmp, bp, lbn); | |
da94bdcb | 829 | error = bread(pmp->pm_devvp, de_bn2doff(pmp, bn), |
59127828 | 830 | bsize, &bp); |
984263bc MD |
831 | if (error) { |
832 | brelse(bp); | |
3b04d1bc | 833 | MSDOSFS_UNLOCK_MP(pmp); |
984263bc MD |
834 | return (error); |
835 | } | |
836 | lbn = bn; | |
837 | } | |
5dd3291f TK |
838 | error = usemap_free(pmp, cluster); |
839 | if (error != 0) { | |
840 | updatefats(pmp, bp, lbn); | |
841 | MSDOSFS_UNLOCK_MP(pmp); | |
842 | return (error); | |
843 | } | |
984263bc MD |
844 | switch (pmp->pm_fatmask) { |
845 | case FAT12_MASK: | |
8b257845 | 846 | readcn = getushort(bp->b_data + bo); |
984263bc MD |
847 | if (cluster & 1) { |
848 | cluster = readcn >> 4; | |
849 | readcn &= 0x000f; | |
850 | readcn |= MSDOSFSFREE << 4; | |
851 | } else { | |
852 | cluster = readcn; | |
853 | readcn &= 0xf000; | |
854 | readcn |= MSDOSFSFREE & 0xfff; | |
855 | } | |
8b257845 | 856 | putushort(bp->b_data + bo, readcn); |
984263bc MD |
857 | break; |
858 | case FAT16_MASK: | |
8b257845 TK |
859 | cluster = getushort(bp->b_data + bo); |
860 | putushort(bp->b_data + bo, MSDOSFSFREE); | |
984263bc MD |
861 | break; |
862 | case FAT32_MASK: | |
8b257845 TK |
863 | cluster = getulong(bp->b_data + bo); |
864 | putulong(bp->b_data + bo, | |
fbae82b1 TK |
865 | (MSDOSFSFREE & FAT32_MASK) | |
866 | (cluster & ~FAT32_MASK)); | |
984263bc MD |
867 | break; |
868 | } | |
869 | cluster &= pmp->pm_fatmask; | |
870 | if ((cluster | ~pmp->pm_fatmask) >= CLUST_RSRVD) | |
871 | cluster |= pmp->pm_fatmask; | |
872 | } | |
873 | if (bp) | |
874 | updatefats(pmp, bp, bn); | |
3b04d1bc | 875 | MSDOSFS_UNLOCK_MP(pmp); |
984263bc MD |
876 | return (0); |
877 | } | |
878 | ||
879 | /* | |
8b257845 | 880 | * Read in FAT blocks looking for free clusters. For every free cluster |
984263bc MD |
881 | * found turn off its corresponding bit in the pm_inusemap. |
882 | */ | |
883 | int | |
4625f023 | 884 | fillinusemap(struct msdosfsmount *pmp) |
984263bc | 885 | { |
842c82a8 TK |
886 | struct buf *bp; |
887 | u_long bn, bo, bsize, byteoffset, cn, readcn; | |
984263bc | 888 | int error; |
842c82a8 | 889 | |
3b04d1bc | 890 | MSDOSFS_ASSERT_MP_LOCKED(pmp); |
842c82a8 | 891 | bp = NULL; |
984263bc MD |
892 | |
893 | /* | |
8b257845 | 894 | * Mark all clusters in use, we mark the free ones in the FAT scan |
984263bc MD |
895 | * loop further down. |
896 | */ | |
897 | for (cn = 0; cn < (pmp->pm_maxcluster + N_INUSEBITS) / N_INUSEBITS; cn++) | |
e93dba1e | 898 | pmp->pm_inusemap[cn] = FULL_RUN; |
984263bc MD |
899 | |
900 | /* | |
901 | * Figure how many free clusters are in the filesystem by ripping | |
8b257845 | 902 | * through the FAT counting the number of entries whose content is |
984263bc MD |
903 | * zero. These represent free clusters. |
904 | */ | |
905 | pmp->pm_freeclustercount = 0; | |
b3f23be1 | 906 | for (cn = 0; cn <= pmp->pm_maxcluster; cn++) { |
984263bc MD |
907 | byteoffset = FATOFS(pmp, cn); |
908 | bo = byteoffset % pmp->pm_fatblocksize; | |
b3f23be1 | 909 | if (bo == 0) { |
984263bc | 910 | /* Read new FAT block */ |
b3f23be1 | 911 | if (bp != NULL) |
984263bc MD |
912 | brelse(bp); |
913 | fatblock(pmp, byteoffset, &bn, &bsize, NULL); | |
da94bdcb | 914 | error = bread(pmp->pm_devvp, de_bn2doff(pmp, bn), |
59127828 | 915 | bsize, &bp); |
b3f23be1 | 916 | if (error != 0) { |
984263bc MD |
917 | brelse(bp); |
918 | return (error); | |
919 | } | |
920 | } | |
921 | if (FAT32(pmp)) | |
8b257845 | 922 | readcn = getulong(bp->b_data + bo); |
984263bc | 923 | else |
8b257845 | 924 | readcn = getushort(bp->b_data + bo); |
984263bc MD |
925 | if (FAT12(pmp) && (cn & 1)) |
926 | readcn >>= 4; | |
927 | readcn &= pmp->pm_fatmask; | |
928 | ||
b3f23be1 TK |
929 | /* |
930 | * Check if the FAT ID matches the BPB's media descriptor and | |
931 | * all other bits are set to 1. | |
932 | */ | |
933 | if (cn == 0 && readcn != ((pmp->pm_fatmask & 0xffffff00) | | |
934 | pmp->pm_bpb.bpbMedia)) { | |
935 | mprintf("fillinusemap(): Media descriptor in BPB " | |
936 | "does not match FAT ID\n"); | |
937 | brelse(bp); | |
938 | return (EINVAL); | |
5dd3291f TK |
939 | } else if (readcn == CLUST_FREE) { |
940 | error = usemap_free(pmp, cn); | |
941 | if (error != 0) { | |
942 | brelse(bp); | |
943 | return (error); | |
944 | } | |
945 | } | |
984263bc | 946 | } |
77865035 TK |
947 | if (bp != NULL) |
948 | brelse(bp); | |
3494b87c TK |
949 | |
950 | for (cn = pmp->pm_maxcluster + 1; cn < (pmp->pm_maxcluster + | |
951 | N_INUSEBITS) / N_INUSEBITS; cn++) | |
952 | pmp->pm_inusemap[cn / N_INUSEBITS] |= 1U << (cn % N_INUSEBITS); | |
953 | ||
984263bc MD |
954 | return (0); |
955 | } | |
956 | ||
957 | /* | |
958 | * Allocate a new cluster and chain it onto the end of the file. | |
959 | * | |
960 | * dep - the file to extend | |
961 | * count - number of clusters to allocate | |
962 | * bpp - where to return the address of the buf header for the first new | |
963 | * file block | |
964 | * ncp - where to put cluster number of the first newly allocated cluster | |
965 | * If this pointer is 0, do not return the cluster number. | |
966 | * flags - see fat.h | |
967 | * | |
968 | * NOTE: This function is not responsible for turning on the DE_UPDATE bit of | |
969 | * the de_flag field of the denode and it does not change the de_FileSize | |
970 | * field. This is left for the caller to do. | |
971 | */ | |
972 | int | |
4625f023 | 973 | extendfile(struct denode *dep, u_long count, struct buf **bpp, u_long *ncp, |
8b257845 | 974 | int flags) |
984263bc MD |
975 | { |
976 | int error; | |
977 | u_long frcn; | |
978 | u_long cn, got; | |
979 | struct msdosfsmount *pmp = dep->de_pmp; | |
980 | struct buf *bp; | |
981 | ||
982 | /* | |
983 | * Don't try to extend the root directory | |
984 | */ | |
985 | if (dep->de_StartCluster == MSDOSFSROOT | |
986 | && (dep->de_Attributes & ATTR_DIRECTORY)) { | |
086c1d7e | 987 | kprintf("extendfile(): attempt to extend root directory\n"); |
984263bc MD |
988 | return (ENOSPC); |
989 | } | |
990 | ||
991 | /* | |
992 | * If the "file's last cluster" cache entry is empty, and the file | |
993 | * is not empty, then fill the cache entry by calling pcbmap(). | |
994 | */ | |
984263bc MD |
995 | if (dep->de_fc[FC_LASTFC].fc_frcn == FCE_EMPTY && |
996 | dep->de_StartCluster != 0) { | |
4afd80f1 | 997 | error = pcbmap(dep, 0xffff, NULL, &cn, NULL); |
984263bc MD |
998 | /* we expect it to return E2BIG */ |
999 | if (error != E2BIG) | |
1000 | return (error); | |
1001 | } | |
1002 | ||
7f886c1b TK |
1003 | dep->de_fc[FC_NEXTTOLASTFC].fc_frcn = |
1004 | dep->de_fc[FC_LASTFC].fc_frcn; | |
1005 | dep->de_fc[FC_NEXTTOLASTFC].fc_fsrcn = | |
1006 | dep->de_fc[FC_LASTFC].fc_fsrcn; | |
984263bc MD |
1007 | while (count > 0) { |
1008 | /* | |
1009 | * Allocate a new cluster chain and cat onto the end of the | |
8b257845 | 1010 | * file. If the file is empty we make de_StartCluster point |
984263bc MD |
1011 | * to the new block. Note that de_StartCluster being 0 is |
1012 | * sufficient to be sure the file is empty since we exclude | |
1013 | * attempts to extend the root directory above, and the root | |
1014 | * dir is the only file with a startcluster of 0 that has | |
1015 | * blocks allocated (sort of). | |
1016 | */ | |
1017 | if (dep->de_StartCluster == 0) | |
1018 | cn = 0; | |
1019 | else | |
1020 | cn = dep->de_fc[FC_LASTFC].fc_fsrcn + 1; | |
1021 | error = clusteralloc(pmp, cn, count, CLUST_EOFE, &cn, &got); | |
1022 | if (error) | |
1023 | return (error); | |
1024 | ||
1025 | count -= got; | |
1026 | ||
1027 | /* | |
1028 | * Give them the filesystem relative cluster number if they want | |
1029 | * it. | |
1030 | */ | |
1031 | if (ncp) { | |
1032 | *ncp = cn; | |
1033 | ncp = NULL; | |
1034 | } | |
1035 | ||
1036 | if (dep->de_StartCluster == 0) { | |
1037 | dep->de_StartCluster = cn; | |
1038 | frcn = 0; | |
1039 | } else { | |
1040 | error = fatentry(FAT_SET, pmp, | |
1041 | dep->de_fc[FC_LASTFC].fc_fsrcn, | |
1042 | 0, cn); | |
1043 | if (error) { | |
011c75d3 | 1044 | clusterfree(pmp, cn); |
984263bc MD |
1045 | return (error); |
1046 | } | |
1047 | frcn = dep->de_fc[FC_LASTFC].fc_frcn + 1; | |
1048 | } | |
1049 | ||
1050 | /* | |
4afd80f1 | 1051 | * Update the "last cluster of the file" entry in the |
8b257845 | 1052 | * denode's FAT cache. |
984263bc MD |
1053 | */ |
1054 | fc_setcache(dep, FC_LASTFC, frcn + got - 1, cn + got - 1); | |
1055 | ||
1056 | if (flags & DE_CLEAR) { | |
1057 | while (got-- > 0) { | |
1058 | /* | |
1059 | * Get the buf header for the new block of the file. | |
1060 | */ | |
54078292 MD |
1061 | if (dep->de_Attributes & ATTR_DIRECTORY) { |
1062 | bp = getblk(pmp->pm_devvp, | |
da94bdcb | 1063 | de_bn2doff(pmp, cntobn(pmp, cn)), |
54078292 | 1064 | pmp->pm_bpcluster, 0, 0); |
4afd80f1 | 1065 | ++cn; |
54078292 MD |
1066 | } else { |
1067 | daddr_t dblkno; | |
6455a89b | 1068 | u_long findcn; |
54078292 MD |
1069 | |
1070 | bp = getblk(DETOV(dep), | |
4afd80f1 | 1071 | de_cn2doff(pmp, frcn), |
984263bc | 1072 | pmp->pm_bpcluster, 0, 0); |
4afd80f1 | 1073 | ++frcn; |
6455a89b TK |
1074 | /* |
1075 | * Convert bio1 offset to file relative | |
1076 | * cluster number. | |
1077 | */ | |
1078 | findcn = de_bn2cn(pmp, | |
1079 | (daddr_t)(bp->b_bio1.bio_offset >> | |
1080 | pmp->pm_bnshift)); | |
984263bc MD |
1081 | /* |
1082 | * Do the bmap now, as in msdosfs_write | |
1083 | */ | |
6455a89b TK |
1084 | if (pcbmap(dep, findcn, &dblkno, NULL, |
1085 | NULL)) { | |
54078292 MD |
1086 | bp->b_bio2.bio_offset = NOOFFSET; |
1087 | } else { | |
da94bdcb | 1088 | bp->b_bio2.bio_offset = de_bn2doff(pmp, dblkno); |
81b5c339 | 1089 | } |
54078292 | 1090 | if (bp->b_bio2.bio_offset == NOOFFSET) |
984263bc MD |
1091 | panic("extendfile: pcbmap"); |
1092 | } | |
1093 | clrbuf(bp); | |
1094 | if (bpp) { | |
1095 | *bpp = bp; | |
1096 | bpp = NULL; | |
8b257845 | 1097 | } else { |
984263bc | 1098 | bdwrite(bp); |
8b257845 | 1099 | } |
984263bc MD |
1100 | } |
1101 | } | |
1102 | } | |
1103 | ||
1104 | return (0); | |
1105 | } | |
3e544118 TK |
1106 | |
1107 | /*- | |
1108 | * Routine to mark a FAT16 or FAT32 volume as "clean" or "dirty" by | |
1109 | * manipulating the upper bit of the FAT entry for cluster 1. Note that | |
1110 | * this bit is not defined for FAT12 volumes, which are always assumed to | |
1111 | * be clean. | |
1112 | * | |
1113 | * The fatentry() routine only works on cluster numbers that a file could | |
1114 | * occupy, so it won't manipulate the entry for cluster 1. So we have to do | |
1115 | * it here. The code was stolen from fatentry() and tailored for cluster 1. | |
1116 | * | |
1117 | * Inputs: | |
1118 | * pmp The MS-DOS volume to mark | |
1119 | * dirty Non-zero if the volume should be marked dirty; zero if it | |
1120 | * should be marked clean | |
1121 | * | |
1122 | * Result: | |
1123 | * 0 Success | |
1124 | * EROFS Volume is read-only | |
1125 | * ? (other errors from called routines) | |
1126 | */ | |
1127 | int | |
1128 | markvoldirty_upgrade(struct msdosfsmount *pmp, bool dirty, bool rw_upgrade) | |
1129 | { | |
1130 | struct buf *bp; | |
1131 | u_long bn, bo, bsize, byteoffset, fatval; | |
1132 | int error; | |
1133 | ||
1134 | /* | |
1135 | * FAT12 does not support a "clean" bit, so don't do anything for | |
1136 | * FAT12. | |
1137 | */ | |
1138 | if (FAT12(pmp)) | |
1139 | return (0); | |
1140 | ||
1141 | /* | |
1142 | * Can't change the bit on a read-only filesystem, except as part of | |
1143 | * ro->rw upgrade. | |
1144 | */ | |
1145 | if ((pmp->pm_flags & MSDOSFSMNT_RONLY) != 0 && !rw_upgrade) | |
1146 | return (EROFS); | |
1147 | ||
1148 | /* | |
1149 | * Fetch the block containing the FAT entry. It is given by the | |
1150 | * pseudo-cluster 1. | |
1151 | */ | |
1152 | byteoffset = FATOFS(pmp, 1); | |
1153 | fatblock(pmp, byteoffset, &bn, &bsize, &bo); | |
da94bdcb | 1154 | error = bread(pmp->pm_devvp, de_bn2doff(pmp, bn), bsize, &bp); |
3e544118 TK |
1155 | if (error) |
1156 | return (error); | |
1157 | ||
1158 | /* | |
1159 | * Get the current value of the FAT entry and set/clear the relevant | |
1160 | * bit. Dirty means clear the "clean" bit; clean means set the | |
1161 | * "clean" bit. | |
1162 | */ | |
1163 | if (FAT32(pmp)) { | |
1164 | /* FAT32 uses bit 27. */ | |
1165 | fatval = getulong(&bp->b_data[bo]); | |
1166 | if (dirty) | |
1167 | fatval &= 0xF7FFFFFF; | |
1168 | else | |
1169 | fatval |= 0x08000000; | |
1170 | putulong(&bp->b_data[bo], fatval); | |
1171 | } else { | |
1172 | /* Must be FAT16; use bit 15. */ | |
1173 | fatval = getushort(&bp->b_data[bo]); | |
1174 | if (dirty) | |
1175 | fatval &= 0x7FFF; | |
1176 | else | |
1177 | fatval |= 0x8000; | |
1178 | putushort(&bp->b_data[bo], fatval); | |
1179 | } | |
1180 | #if 0 | |
1181 | /* | |
1182 | * The concern here is that a devvp may be readonly, without reporting | |
1183 | * itself as such through the usual channels. In that case, we'd like | |
1184 | * it if attempting to mount msdosfs rw didn't panic the system. | |
1185 | * | |
1186 | * markvoldirty is invoked as the first write on backing devvps when | |
1187 | * either msdosfs is mounted for the first time, or a ro mount is | |
1188 | * upgraded to rw. | |
1189 | * | |
1190 | * In either event, if a write error occurs dirtying the volume: | |
1191 | * - No user data has been permitted to be written to cache yet. | |
1192 | * - We can abort the high-level operation (mount, or ro->rw) safely. | |
1193 | * - We don't derive any benefit from leaving a zombie dirty buf in | |
1194 | * the cache that can not be cleaned or evicted. | |
1195 | * | |
1196 | * So, mark B_INVALONERR to have bwrite() -> brelse() detect that | |
1197 | * condition and force-invalidate our write to the block if it occurs. | |
1198 | * | |
1199 | * PR 210316 provides more context on the discovery and diagnosis of | |
1200 | * the problem, as well as earlier attempts to solve it. | |
1201 | */ | |
1202 | bp->b_flags |= B_INVALONERR; | |
1203 | #endif | |
1204 | /* Write out the modified FAT block synchronously. */ | |
1205 | return (bwrite(bp)); | |
1206 | } |