xref: /illumos-gate/usr/src/uts/common/fs/pcfs/pc_alloc.c (revision 446d25c9e29c018a681c467da83afd71d02b647b)
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