1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 * Copyright 2024 MNX Cloud, Inc.
25 */
26
27 /*
28 * Routines to allocate and deallocate data blocks on the disk
29 */
30
31 #include <sys/param.h>
32 #include <sys/errno.h>
33 #include <sys/buf.h>
34 #include <sys/vfs.h>
35 #include <sys/vnode.h>
36 #include <sys/cmn_err.h>
37 #include <sys/debug.h>
38 #include <sys/sysmacros.h>
39 #include <sys/systm.h>
40 #include <sys/fs/pc_label.h>
41 #include <sys/fs/pc_fs.h>
42 #include <sys/fs/pc_dir.h>
43 #include <sys/fs/pc_node.h>
44
45 static pc_cluster32_t pc_getcluster(struct pcfs *fsp, pc_cluster32_t cn);
46
47 /*
48 * Convert file logical block (cluster) numbers to disk block numbers.
49 * Also return number of physically contiguous blocks if asked for.
50 * Used for reading only. Use pc_balloc for writing.
51 * Arguments:
52 * pcp - pcnode for file
53 * lcn - logical cluster no
54 * dbnp- ptr to phys block no
55 * contigbp- ptr to number of contiguous bytes, may be zero
56 * if not wanted
57 */
58 int
pc_bmap(struct pcnode * pcp,daddr_t lcn,daddr_t * dbnp,uint_t * contigbp)59 pc_bmap(struct pcnode *pcp, daddr_t lcn, daddr_t *dbnp, uint_t *contigbp)
60 {
61 struct pcfs *fsp; /* pcfs that file is in */
62 struct vnode *vp;
63 pc_cluster32_t cn, ncn; /* current, next cluster number */
64 daddr_t olcn = lcn;
65
66 vp = PCTOV(pcp);
67 fsp = VFSTOPCFS(vp->v_vfsp);
68
69 if (lcn < 0)
70 return (ENOENT);
71
72 /*
73 * FAT12 / FAT16 root directories are a continuous section on disk
74 * before the actual data clusters. Specialcase this here.
75 */
76 if (!IS_FAT32(fsp) && (vp->v_flag & VROOT)) {
77 daddr_t lbn; /* logical (disk) block number */
78
79 lbn = pc_cltodb(fsp, lcn);
80 if (lbn >= fsp->pcfs_rdirsec) {
81 PC_DPRINTF0(2, "pc_bmap: ENOENT1\n");
82 return (ENOENT);
83 }
84 *dbnp = pc_dbdaddr(fsp, fsp->pcfs_rdirstart + lbn);
85 if (contigbp) {
86 ASSERT (*contigbp >= fsp->pcfs_secsize);
87 *contigbp = MIN(*contigbp,
88 fsp->pcfs_secsize * (fsp->pcfs_rdirsec - lbn));
89 }
90 return (0);
91 }
92
93 if (lcn >= fsp->pcfs_ncluster) {
94 PC_DPRINTF0(2, "pc_bmap: ENOENT2\n");
95 return (ENOENT);
96 }
97 if (vp->v_type == VREG &&
98 (pcp->pc_size == 0 ||
99 lcn >= (daddr_t)howmany((offset_t)pcp->pc_size,
100 fsp->pcfs_clsize))) {
101 PC_DPRINTF0(2, "pc_bmap: ENOENT3\n");
102 return (ENOENT);
103 }
104 ncn = pcp->pc_scluster;
105 if (IS_FAT32(fsp) && ncn == 0)
106 ncn = fsp->pcfs_rdirstart;
107
108 /* Do we have a cached index/cluster pair? */
109 if (pcp->pc_lindex > 0 && lcn >= pcp->pc_lindex) {
110 lcn -= pcp->pc_lindex;
111 ncn = pcp->pc_lcluster;
112 }
113 do {
114 cn = ncn;
115 if (!pc_validcl(fsp, cn)) {
116 if (IS_FAT32(fsp) && cn >= PCF_LASTCLUSTER32 &&
117 vp->v_type == VDIR) {
118 PC_DPRINTF0(2, "pc_bmap: ENOENT4\n");
119 return (ENOENT);
120 } else if (!IS_FAT32(fsp) &&
121 cn >= PCF_LASTCLUSTER &&
122 vp->v_type == VDIR) {
123 PC_DPRINTF0(2, "pc_bmap: ENOENT5\n");
124 return (ENOENT);
125 } else {
126 PC_DPRINTF1(1,
127 "pc_bmap: badfs cn=%d\n", cn);
128 (void) pc_badfs(fsp);
129 return (EIO);
130 }
131 }
132 ncn = pc_getcluster(fsp, cn);
133 } while (lcn--);
134
135 /*
136 * Cache this cluster, as we'll most likely visit the
137 * one after this next time. Considerably improves
138 * performance on sequential reads and writes.
139 */
140 pcp->pc_lindex = olcn;
141 pcp->pc_lcluster = cn;
142 *dbnp = pc_cldaddr(fsp, cn);
143
144 if (contigbp && *contigbp > fsp->pcfs_clsize) {
145 uint_t count = fsp->pcfs_clsize;
146
147 while ((cn + 1) == ncn && count < *contigbp &&
148 pc_validcl(fsp, ncn)) {
149 count += fsp->pcfs_clsize;
150 cn = ncn;
151 ncn = pc_getcluster(fsp, ncn);
152 }
153 *contigbp = count;
154 }
155 return (0);
156 }
157
158 /*
159 * Allocate file logical blocks (clusters).
160 * Return disk address of last allocated cluster.
161 */
162 int
pc_balloc(struct pcnode * pcp,daddr_t lcn,int zwrite,daddr_t * dbnp)163 pc_balloc(
164 struct pcnode *pcp, /* pcnode for file */
165 daddr_t lcn, /* logical cluster no */
166 int zwrite, /* zerofill blocks? */
167 daddr_t *dbnp) /* ptr to phys block no */
168 {
169 struct pcfs *fsp; /* pcfs that file is in */
170 struct vnode *vp;
171 pc_cluster32_t cn; /* current cluster number */
172 pc_cluster32_t ncn; /* next cluster number */
173
174 vp = PCTOV(pcp);
175 fsp = VFSTOPCFS(vp -> v_vfsp);
176
177 if (lcn < 0) {
178 return (EFBIG);
179 }
180
181 /*
182 * Again, FAT12/FAT16 root directories are not data clusters.
183 */
184 if (!IS_FAT32(fsp) && (vp->v_flag & VROOT)) {
185 daddr_t lbn;
186
187 lbn = pc_cltodb(fsp, lcn);
188 if (lbn >= fsp->pcfs_rdirsec)
189 return (ENOSPC);
190 *dbnp = pc_dbdaddr(fsp, fsp->pcfs_rdirstart + lbn);
191 return (0);
192 }
193
194 if (lcn >= fsp->pcfs_ncluster)
195 return (ENOSPC);
196 if ((vp->v_type == VREG && pcp->pc_size == 0) ||
197 (vp->v_type == VDIR && lcn == 0)) {
198 switch (cn = pc_alloccluster(fsp, 1)) {
199 case PCF_FREECLUSTER:
200 return (ENOSPC);
201 case PCF_ERRORCLUSTER:
202 return (EIO);
203 }
204 pcp->pc_scluster = cn;
205 } else {
206 cn = pcp->pc_scluster;
207 if (IS_FAT32(fsp) && cn == 0)
208 cn = fsp->pcfs_rdirstart;
209 if (!pc_validcl(fsp, cn)) {
210 PC_DPRINTF1(1, "pc_balloc: badfs cn=%d\n", cn);
211 (void) pc_badfs(fsp);
212 return (EIO);
213 }
214 }
215
216 if (pcp->pc_lindex > 0 && lcn > pcp->pc_lindex) {
217 lcn -= pcp->pc_lindex;
218 cn = pcp->pc_lcluster;
219 }
220 while (lcn-- > 0) {
221 ncn = pc_getcluster(fsp, cn);
222 if ((IS_FAT32(fsp) && ncn >= PCF_LASTCLUSTER32) ||
223 (!IS_FAT32(fsp) && ncn >= PCF_LASTCLUSTER)) {
224 /*
225 * Extend file (no holes).
226 */
227 switch (ncn = pc_alloccluster(fsp, zwrite)) {
228 case PCF_FREECLUSTER:
229 return (ENOSPC);
230 case PCF_ERRORCLUSTER:
231 return (EIO);
232 }
233 pc_setcluster(fsp, cn, ncn);
234 } else if (!pc_validcl(fsp, ncn)) {
235 PC_DPRINTF1(1,
236 "pc_balloc: badfs ncn=%d\n", ncn);
237 (void) pc_badfs(fsp);
238 return (EIO);
239 }
240 cn = ncn;
241 }
242 /*
243 * Do not cache the new cluster/index values; when
244 * extending the file we're interested in the last
245 * written cluster and not the last cluster allocated.
246 */
247 *dbnp = pc_cldaddr(fsp, cn);
248
249 return (0);
250 }
251
252 /*
253 * Free file cluster chain after the first skipcl clusters.
254 */
255 int
pc_bfree(struct pcnode * pcp,pc_cluster32_t skipcl)256 pc_bfree(struct pcnode *pcp, pc_cluster32_t skipcl)
257 {
258 struct pcfs *fsp;
259 pc_cluster32_t cn;
260 pc_cluster32_t ncn;
261 int n;
262 struct vnode *vp;
263
264 vp = PCTOV(pcp);
265 fsp = VFSTOPCFS(vp->v_vfsp);
266 if (!IS_FAT32(fsp) && (vp->v_flag & VROOT)) {
267 panic("pc_bfree");
268 }
269
270 if (pcp->pc_size == 0 && vp->v_type == VREG) {
271 return (0);
272 }
273 if (vp->v_type == VREG) {
274 n = (int)howmany((offset_t)pcp->pc_size, fsp->pcfs_clsize);
275 if (n > fsp->pcfs_ncluster) {
276 PC_DPRINTF1(1, "pc_bfree: badfs n=%d\n", n);
277 (void) pc_badfs(fsp);
278 return (EIO);
279 }
280 } else {
281 n = fsp->pcfs_ncluster;
282 }
283 cn = pcp->pc_scluster;
284 if (IS_FAT32(fsp) && cn == 0)
285 cn = fsp->pcfs_rdirstart;
286 if (skipcl == 0) {
287 if (IS_FAT32(fsp))
288 pcp->pc_scluster = PCF_LASTCLUSTERMARK32;
289 else
290 pcp->pc_scluster = PCF_LASTCLUSTERMARK;
291 }
292
293 /* Invalidate last used cluster cache */
294 pcp->pc_lindex = 0;
295 pcp->pc_lcluster = pcp->pc_scluster;
296
297 while (n--) {
298 if (!pc_validcl(fsp, cn)) {
299 PC_DPRINTF1(1, "pc_bfree: badfs cn=%d\n", cn);
300 (void) pc_badfs(fsp);
301 return (EIO);
302 }
303 ncn = pc_getcluster(fsp, cn);
304 if (skipcl == 0) {
305 pc_setcluster(fsp, cn, PCF_FREECLUSTER);
306 } else {
307 skipcl--;
308 if (skipcl == 0) {
309 if (IS_FAT32(fsp)) {
310 pc_setcluster(fsp, cn,
311 PCF_LASTCLUSTERMARK32);
312 } else
313 pc_setcluster(fsp, cn,
314 PCF_LASTCLUSTERMARK);
315 }
316 }
317 if (IS_FAT32(fsp) && ncn >= PCF_LASTCLUSTER32 &&
318 vp->v_type == VDIR)
319 break;
320 if (!IS_FAT32(fsp) && ncn >= PCF_LASTCLUSTER &&
321 vp->v_type == VDIR)
322 break;
323 cn = ncn;
324 }
325 return (0);
326 }
327
328 /*
329 * Return the number of free blocks in the filesystem.
330 */
331 int
pc_freeclusters(struct pcfs * fsp)332 pc_freeclusters(struct pcfs *fsp)
333 {
334 pc_cluster32_t cn;
335 int free = 0;
336
337 if (IS_FAT32(fsp) &&
338 fsp->pcfs_fsinfo.fs_free_clusters != FSINFO_UNKNOWN)
339 return (fsp->pcfs_fsinfo.fs_free_clusters);
340
341 /*
342 * make sure the FAT is in core
343 */
344 for (cn = PCF_FIRSTCLUSTER; pc_validcl(fsp, cn); cn++) {
345 if (pc_getcluster(fsp, cn) == PCF_FREECLUSTER) {
346 free++;
347 }
348 }
349
350 if (IS_FAT32(fsp)) {
351 ASSERT(fsp->pcfs_fsinfo.fs_free_clusters == FSINFO_UNKNOWN);
352 fsp->pcfs_fsinfo.fs_free_clusters = free;
353 }
354 return (free);
355 }
356
357 /*
358 * Cluster manipulation routines.
359 * FAT must be resident.
360 */
361
362 /*
363 * Get the next cluster in the file cluster chain.
364 * cn = current cluster number in chain
365 */
366 static pc_cluster32_t
pc_getcluster(struct pcfs * fsp,pc_cluster32_t cn)367 pc_getcluster(struct pcfs *fsp, pc_cluster32_t cn)
368 {
369 unsigned char *fp;
370
371 if (fsp->pcfs_fatp == NULL || !pc_validcl(fsp, cn))
372 panic("pc_getcluster");
373
374 switch (fsp->pcfs_fattype) {
375 case FAT32:
376 fp = fsp->pcfs_fatp + (cn << 2);
377 /* Ignore high 4 bits on read */
378 cn = ltohi(*(pc_cluster32_t *)fp) & 0x0FFFFFFF;
379 break;
380 case FAT16:
381 fp = fsp->pcfs_fatp + (cn << 1);
382 cn = ltohs(*(pc_cluster16_t *)fp);
383 break;
384 case FAT12:
385 fp = fsp->pcfs_fatp + (cn + (cn >> 1));
386 if (cn & 01) {
387 cn = (((unsigned int)*fp++ & 0xf0) >> 4);
388 cn += (*fp << 4);
389 } else {
390 cn = *fp++;
391 cn += ((*fp & 0x0f) << 8);
392 }
393 if (cn >= PCF_12BCLUSTER)
394 cn |= PCF_RESCLUSTER;
395 break;
396 default:
397 pc_mark_irrecov(fsp);
398 cn = PCF_ERRORCLUSTER;
399 }
400 return (cn);
401 }
402
403 /*
404 * Set a cluster in the FAT to a value.
405 * cn = cluster number to be set in FAT
406 * ncn = new value
407 */
408 void
pc_setcluster(struct pcfs * fsp,pc_cluster32_t cn,pc_cluster32_t ncn)409 pc_setcluster(struct pcfs *fsp, pc_cluster32_t cn, pc_cluster32_t ncn)
410 {
411 unsigned char *fp;
412 pc_cluster16_t ncn16;
413 pc_cluster32_t ncn32;
414
415 if (fsp->pcfs_fatp == NULL || !pc_validcl(fsp, cn))
416 panic("pc_setcluster");
417 fsp->pcfs_flags |= PCFS_FATMOD;
418 pc_mark_fat_updated(fsp, cn);
419 switch (fsp->pcfs_fattype) {
420 case FAT32:
421 fp = fsp->pcfs_fatp + (cn << 2);
422 /* Preserve high 4 bits on write */
423 ncn32 = ltohi(*(pc_cluster32_t *)fp) & 0xF0000000;
424 ncn32 |= (ncn & 0x0FFFFFFF);
425 *(pc_cluster32_t *)fp = htoli(ncn32);
426 break;
427 case FAT16:
428 fp = fsp->pcfs_fatp + (cn << 1);
429 ncn16 = (pc_cluster16_t)ncn;
430 *(pc_cluster16_t *)fp = htols(ncn16);
431 break;
432 case FAT12:
433 fp = fsp->pcfs_fatp + (cn + (cn >> 1));
434 if (cn & 01) {
435 *fp = (*fp & 0x0f) | ((ncn << 4) & 0xf0);
436 fp++;
437 *fp = (ncn >> 4) & 0xff;
438 } else {
439 *fp++ = ncn & 0xff;
440 *fp = (*fp & 0xf0) | ((ncn >> 8) & 0x0f);
441 }
442 break;
443 default:
444 pc_mark_irrecov(fsp);
445 }
446 if (ncn == PCF_FREECLUSTER) {
447 fsp->pcfs_nxfrecls = PCF_FIRSTCLUSTER;
448 if (IS_FAT32(fsp)) {
449 if (fsp->pcfs_fsinfo.fs_free_clusters !=
450 FSINFO_UNKNOWN)
451 fsp->pcfs_fsinfo.fs_free_clusters++;
452 }
453 }
454 }
455
456 /*
457 * Allocate a new cluster.
458 */
459 pc_cluster32_t
pc_alloccluster(struct pcfs * fsp,int zwrite)460 pc_alloccluster(
461 struct pcfs *fsp, /* file sys to allocate in */
462 int zwrite) /* boolean for writing zeroes */
463 {
464 pc_cluster32_t cn;
465 int error;
466
467 if (fsp->pcfs_fatp == NULL)
468 panic("pc_addcluster: no FAT");
469
470 for (cn = fsp->pcfs_nxfrecls; pc_validcl(fsp, cn); cn++) {
471 if (pc_getcluster(fsp, cn) == PCF_FREECLUSTER) {
472 struct buf *bp;
473
474 if (IS_FAT32(fsp)) {
475 pc_setcluster(fsp, cn, PCF_LASTCLUSTERMARK32);
476 if (fsp->pcfs_fsinfo.fs_free_clusters !=
477 FSINFO_UNKNOWN)
478 fsp->pcfs_fsinfo.fs_free_clusters--;
479 } else
480 pc_setcluster(fsp, cn, PCF_LASTCLUSTERMARK);
481 if (zwrite) {
482 /*
483 * zero the new cluster
484 */
485 bp = ngeteblk(fsp->pcfs_clsize);
486 bp->b_edev = fsp->pcfs_xdev;
487 bp->b_dev = cmpdev(bp->b_edev);
488 bp->b_blkno = pc_cldaddr(fsp, cn);
489 clrbuf(bp);
490 bwrite2(bp);
491 error = geterror(bp);
492 brelse(bp);
493 if (error) {
494 pc_mark_irrecov(fsp);
495 return (PCF_ERRORCLUSTER);
496 }
497 }
498 fsp->pcfs_nxfrecls = cn + 1;
499 return (cn);
500 }
501 }
502 return (PCF_FREECLUSTER);
503 }
504
505 /*
506 * Get the number of clusters used by a file or subdirectory
507 */
508 int
pc_fileclsize(struct pcfs * fsp,pc_cluster32_t startcl,pc_cluster32_t * ncl)509 pc_fileclsize(
510 struct pcfs *fsp,
511 pc_cluster32_t startcl, pc_cluster32_t *ncl)
512 {
513 int count = 0;
514
515 *ncl = 0;
516 for (count = 0; pc_validcl(fsp, startcl);
517 startcl = pc_getcluster(fsp, startcl)) {
518 if (count++ >= fsp->pcfs_ncluster)
519 return (EIO);
520 }
521 *ncl = (pc_cluster32_t)count;
522
523 return (0);
524 }
525