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