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