]> git.saurik.com Git - apple/xnu.git/blob - bsd/ufs/ufs/ufs_bmap.c
ca7fd9352de5ec6574b48f062717b325d1fdd7d3
[apple/xnu.git] / bsd / ufs / ufs / ufs_bmap.c
1 /*
2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
11 *
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
18 * under the License.
19 *
20 * @APPLE_LICENSE_HEADER_END@
21 */
22 /* Copyright (c) 1995 NeXT Computer, Inc. All Rights Reserved */
23 /*
24 * Copyright (c) 1989, 1991, 1993
25 * The Regents of the University of California. All rights reserved.
26 * (c) UNIX System Laboratories, Inc.
27 * All or some portions of this file are derived from material licensed
28 * to the University of California by American Telephone and Telegraph
29 * Co. or Unix System Laboratories, Inc. and are reproduced herein with
30 * the permission of UNIX System Laboratories, Inc.
31 *
32 * Redistribution and use in source and binary forms, with or without
33 * modification, are permitted provided that the following conditions
34 * are met:
35 * 1. Redistributions of source code must retain the above copyright
36 * notice, this list of conditions and the following disclaimer.
37 * 2. Redistributions in binary form must reproduce the above copyright
38 * notice, this list of conditions and the following disclaimer in the
39 * documentation and/or other materials provided with the distribution.
40 * 3. All advertising materials mentioning features or use of this software
41 * must display the following acknowledgement:
42 * This product includes software developed by the University of
43 * California, Berkeley and its contributors.
44 * 4. Neither the name of the University nor the names of its contributors
45 * may be used to endorse or promote products derived from this software
46 * without specific prior written permission.
47 *
48 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
49 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
50 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
51 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
52 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
53 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
54 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
55 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
56 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
57 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
58 * SUCH DAMAGE.
59 *
60 * @(#)ufs_bmap.c 8.7 (Berkeley) 3/21/95
61 */
62 /*
63 * HISTORY
64 * 11-July-97 Umesh Vaishampayan (umeshv@apple.com)
65 * Cleanup. Fixed compilation error when tracing is turned on.
66 */
67 #include <rev_endian_fs.h>
68 #include <sys/param.h>
69 #include <sys/buf.h>
70 #include <sys/proc_internal.h> /* for p_stats */
71 #include <sys/vnode_internal.h>
72 #include <sys/mount_internal.h>
73 #include <sys/resourcevar.h>
74 #include <sys/trace.h>
75 #include <sys/quota.h>
76
77 #include <miscfs/specfs/specdev.h>
78
79 #include <ufs/ufs/quota.h>
80 #include <ufs/ufs/inode.h>
81 #include <ufs/ufs/ufsmount.h>
82 #include <ufs/ufs/ufs_extern.h>
83 #if REV_ENDIAN_FS
84 #include <ufs/ufs/ufs_byte_order.h>
85 #include <architecture/byte_order.h>
86 #endif /* REV_ENDIAN_FS */
87
88
89 /*
90 * Indirect blocks are now on the vnode for the file. They are given negative
91 * logical block numbers. Indirect blocks are addressed by the negative
92 * address of the first data block to which they point. Double indirect blocks
93 * are addressed by one less than the address of the first indirect block to
94 * which they point. Triple indirect blocks are addressed by one less than
95 * the address of the first double indirect block to which they point.
96 *
97 * ufs_bmaparray does the bmap conversion, and if requested returns the
98 * array of logical blocks which must be traversed to get to a block.
99 * Each entry contains the offset into that block that gets you to the
100 * next block and the disk address of the block (if it is assigned).
101 */
102
103 int
104 ufs_bmaparray(vp, bn, bnp, ap, nump, runp)
105 vnode_t vp;
106 ufs_daddr_t bn;
107 ufs_daddr_t *bnp;
108 struct indir *ap;
109 int *nump;
110 int *runp;
111 {
112 register struct inode *ip;
113 struct buf *bp;
114 struct ufsmount *ump;
115 struct mount *mp;
116 struct vnode *devvp;
117 struct indir a[NIADDR], *xap;
118 ufs_daddr_t daddr;
119 long metalbn;
120 int error, maxrun, num;
121 #if REV_ENDIAN_FS
122 int rev_endian=0;
123 #endif /* REV_ENDIAN_FS */
124
125 ip = VTOI(vp);
126 mp = vp->v_mount;
127 ump = VFSTOUFS(mp);
128
129 #if REV_ENDIAN_FS
130 rev_endian=(mp->mnt_flag & MNT_REVEND);
131 #endif /* REV_ENDIAN_FS */
132
133 #if DIAGNOSTIC
134 if (ap != NULL && nump == NULL || ap == NULL && nump != NULL)
135 panic("ufs_bmaparray: invalid arguments");
136 #endif
137
138 if (runp) {
139 /*
140 * XXX
141 * If MAXPHYSIO is the largest transfer the disks can handle,
142 * we probably want maxrun to be 1 block less so that we
143 * don't create a block larger than the device can handle.
144 */
145 *runp = 0;
146 maxrun = MAXPHYSIO / mp->mnt_vfsstat.f_iosize - 1;
147 }
148
149 xap = ap == NULL ? a : ap;
150 if (!nump)
151 nump = &num;
152 if (error = ufs_getlbns(vp, bn, xap, nump))
153 return (error);
154
155 num = *nump;
156 if (num == 0) {
157 *bnp = blkptrtodb(ump, ip->i_db[bn]);
158 if (*bnp == 0)
159 *bnp = -1;
160 else if (runp)
161 for (++bn; bn < NDADDR && *runp < maxrun &&
162 is_sequential(ump, ip->i_db[bn - 1], ip->i_db[bn]);
163 ++bn, ++*runp);
164 return (0);
165 }
166
167
168 /* Get disk address out of indirect block array */
169 daddr = ip->i_ib[xap->in_off];
170
171 devvp = VFSTOUFS(vp->v_mount)->um_devvp;
172 for (bp = NULL, ++xap; --num; ++xap) {
173 ufs_daddr_t *dataptr;
174 int bop;
175
176 if ((metalbn = xap->in_lbn) == bn)
177 /*
178 * found the indirect block we were
179 * looking for... exit the loop
180 */
181 break;
182
183 if (daddr == 0)
184 bop = BLK_ONLYVALID | BLK_META;
185 else
186 bop = BLK_META;
187
188 if (bp)
189 buf_brelse(bp);
190 bp = buf_getblk(vp, (daddr64_t)((unsigned)metalbn), mp->mnt_vfsstat.f_iosize, 0, 0, bop);
191
192 if (bp == 0) {
193 /*
194 * Exit the loop if there is no disk address assigned yet and
195 * the indirect block isn't in the cache
196 */
197 break;
198 }
199 /*
200 * If we get here, we've either got the block in the cache
201 * or we have a disk address for it, go fetch it.
202 */
203 xap->in_exists = 1;
204
205 if (buf_valid(bp)) {
206 trace(TR_BREADHIT, pack(vp, mp->mnt_vfsstat.f_iosize), metalbn);
207 }
208 else {
209 trace(TR_BREADMISS, pack(vp, mp->mnt_vfsstat.f_iosize), metalbn);
210 buf_setblkno(bp, blkptrtodb(ump, (daddr64_t)((unsigned)daddr)));
211 buf_setflags(bp, B_READ);
212 VNOP_STRATEGY(bp);
213 current_proc()->p_stats->p_ru.ru_inblock++; /* XXX */
214 if (error = (int)buf_biowait(bp)) {
215 buf_brelse(bp);
216 return (error);
217 }
218 }
219 dataptr = (ufs_daddr_t *)buf_dataptr(bp);
220 daddr = dataptr[xap->in_off];
221 #if REV_ENDIAN_FS
222 if (rev_endian)
223 daddr = NXSwapLong(daddr);
224 #endif /* REV_ENDIAN_FS */
225 if (num == 1 && daddr && runp) {
226 #if REV_ENDIAN_FS
227 if (rev_endian) {
228 for (bn = xap->in_off + 1;
229 bn < MNINDIR(ump) && *runp < maxrun &&
230 is_sequential(ump,
231 NXSwapLong(dataptr[bn - 1]),
232 NXSwapLong(dataptr[bn]));
233 ++bn, ++*runp);
234 } else {
235 #endif /* REV_ENDIAN_FS */
236 for (bn = xap->in_off + 1;
237 bn < MNINDIR(ump) && *runp < maxrun &&
238 is_sequential(ump,
239 dataptr[bn - 1],
240 dataptr[bn]);
241 ++bn, ++*runp);
242 #if REV_ENDIAN_FS
243 }
244 #endif /* REV_ENDIAN_FS */
245 }
246 }
247 if (bp)
248 buf_brelse(bp);
249
250 daddr = blkptrtodb(ump, daddr);
251 *bnp = daddr == 0 ? -1 : daddr;
252 return (0);
253 }
254
255 /*
256 * Create an array of logical block number/offset pairs which represent the
257 * path of indirect blocks required to access a data block. The first "pair"
258 * contains the logical block number of the appropriate single, double or
259 * triple indirect block and the offset into the inode indirect block array.
260 * Note, the logical block number of the inode single/double/triple indirect
261 * block appears twice in the array, once with the offset into the i_ib and
262 * once with the offset into the page itself.
263 */
264 int
265 ufs_getlbns(vp, bn, ap, nump)
266 struct vnode *vp;
267 ufs_daddr_t bn;
268 struct indir *ap;
269 int *nump;
270 {
271 long metalbn, realbn;
272 struct ufsmount *ump;
273 int blockcnt, i, numlevels, off;
274
275 ump = VFSTOUFS(vp->v_mount);
276 if (nump)
277 *nump = 0;
278 numlevels = 0;
279 realbn = bn;
280 if ((long)bn < 0)
281 bn = -(long)bn;
282
283 /* The first NDADDR blocks are direct blocks. */
284 if (bn < NDADDR)
285 return (0);
286
287 /*
288 * Determine the number of levels of indirection. After this loop
289 * is done, blockcnt indicates the number of data blocks possible
290 * at the given level of indirection, and NIADDR - i is the number
291 * of levels of indirection needed to locate the requested block.
292 */
293 for (blockcnt = 1, i = NIADDR, bn -= NDADDR;; i--, bn -= blockcnt) {
294 if (i == 0)
295 return (EFBIG);
296 blockcnt *= MNINDIR(ump);
297 if (bn < blockcnt)
298 break;
299 }
300
301 /* Calculate the address of the first meta-block. */
302 if (realbn >= 0)
303 metalbn = -(realbn - bn + NIADDR - i);
304 else
305 metalbn = -(-realbn - bn + NIADDR - i);
306
307 /*
308 * At each iteration, off is the offset into the bap array which is
309 * an array of disk addresses at the current level of indirection.
310 * The logical block number and the offset in that block are stored
311 * into the argument array.
312 */
313 ap->in_lbn = metalbn;
314 ap->in_off = off = NIADDR - i;
315 ap->in_exists = 0;
316 ap++;
317 for (++numlevels; i <= NIADDR; i++) {
318 /* If searching for a meta-data block, quit when found. */
319 if (metalbn == realbn)
320 break;
321
322 blockcnt /= MNINDIR(ump);
323 off = (bn / blockcnt) % MNINDIR(ump);
324
325 ++numlevels;
326 ap->in_lbn = metalbn;
327 ap->in_off = off;
328 ap->in_exists = 0;
329 ++ap;
330
331 metalbn -= -1 + off * blockcnt;
332 }
333 if (nump)
334 *nump = numlevels;
335 return (0);
336 }
337 /*
338 * blockmap converts a file offsetto its physical block
339 * number on the disk... it optionally returns the physically
340 * contiguous size.
341 */
342 int
343 ufs_blockmap(ap)
344 struct vnop_blockmap_args /* {
345 struct vnode *a_vp;
346 off_t a_foffset;
347 size_t a_size;
348 daddr64_t *a_bpn;
349 size_t *a_run;
350 void *a_poff;
351 int a_flags;
352 } */ *ap;
353 {
354 vnode_t vp = ap->a_vp;
355 daddr64_t * bnp = ap->a_bpn;
356 size_t * runp = ap->a_run;
357 int size = ap->a_size;
358 struct fs * fs;
359 struct inode *ip;
360 ufs_daddr_t lbn;
361 ufs_daddr_t daddr = 0;
362 int devBlockSize = 0;
363 int retsize = 0;
364 int error = 0;
365 int nblks;
366
367 ip = VTOI(vp);
368 fs = ip->i_fs;
369
370 lbn = (ufs_daddr_t)lblkno(fs, ap->a_foffset);
371 devBlockSize = vfs_devblocksize(vnode_mount(vp));
372
373 if (blkoff(fs, ap->a_foffset))
374 panic("ufs_blockmap; allocation requested inside a block");
375
376 if (size % devBlockSize)
377 panic("ufs_blockmap: size is not multiple of device block size\n");
378
379 if ((error = ufs_bmaparray(vp, lbn, &daddr, NULL, NULL, &nblks)))
380 return (error);
381
382 if (bnp)
383 *bnp = (daddr64_t)daddr;
384
385 if (ap->a_poff)
386 *(int *)ap->a_poff = 0;
387
388 if (runp) {
389 if (lbn < 0) {
390 /*
391 * we're dealing with the indirect blocks
392 * which are always fs_bsize in size
393 */
394 retsize = (nblks + 1) * fs->fs_bsize;
395 } else if (daddr == -1 || nblks == 0) {
396 /*
397 * we're dealing with a 'hole'... UFS doesn't
398 * have a clean way to determine it's size
399 * or
400 * there's are no physically contiguous blocks
401 * so
402 * just return the size of the lbn we started with
403 */
404 retsize = blksize(fs, ip, lbn);
405 } else {
406 /*
407 * we have 1 or more blocks that are physically contiguous
408 * to our starting block number... the orignal block + (nblks - 1)
409 * blocks must be full sized since only the last block can be
410 * composed of fragments...
411 */
412 retsize = nblks * fs->fs_bsize;
413
414 /*
415 * now compute the size of the last block and add it in
416 */
417 retsize += blksize(fs, ip, (lbn + nblks));
418 }
419 if (retsize < size)
420 *runp = retsize;
421 else
422 *runp = size;
423 }
424 return (0);
425 }