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