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