]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blame - libxfs/xfs_inode_fork.c
libxfs: restructure to match kernel layout
[thirdparty/xfsprogs-dev.git] / libxfs / xfs_inode_fork.c
CommitLineData
5d90ab5a
DC
1/*
2 * Copyright (c) 2000-2006 Silicon Graphics, Inc.
3 * All Rights Reserved.
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License as
7 * published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it would be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write the Free Software Foundation,
16 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17 */
18#include <xfs.h>
19
20kmem_zone_t *xfs_ifork_zone;
21
22STATIC int xfs_iformat_local(xfs_inode_t *, xfs_dinode_t *, int, int);
23STATIC int xfs_iformat_extents(xfs_inode_t *, xfs_dinode_t *, int);
24STATIC int xfs_iformat_btree(xfs_inode_t *, xfs_dinode_t *, int);
25
26#ifdef DEBUG
27/*
28 * Make sure that the extents in the given memory buffer
29 * are valid.
30 */
31void
32xfs_validate_extents(
33 xfs_ifork_t *ifp,
34 int nrecs,
35 xfs_exntfmt_t fmt)
36{
37 xfs_bmbt_irec_t irec;
38 xfs_bmbt_rec_host_t rec;
39 int i;
40
41 for (i = 0; i < nrecs; i++) {
42 xfs_bmbt_rec_host_t *ep = xfs_iext_get_ext(ifp, i);
43 rec.l0 = get_unaligned(&ep->l0);
44 rec.l1 = get_unaligned(&ep->l1);
45 xfs_bmbt_get_all(&rec, &irec);
46 if (fmt == XFS_EXTFMT_NOSTATE)
47 ASSERT(irec.br_state == XFS_EXT_NORM);
48 }
49}
50#else /* DEBUG */
51#define xfs_validate_extents(ifp, nrecs, fmt)
52#endif /* DEBUG */
53
54
55/*
56 * Move inode type and inode format specific information from the
57 * on-disk inode to the in-core inode. For fifos, devs, and sockets
58 * this means set if_rdev to the proper value. For files, directories,
59 * and symlinks this means to bring in the in-line data or extent
60 * pointers. For a file in B-tree format, only the root is immediately
61 * brought in-core. The rest will be in-lined in if_extents when it
62 * is first referenced (see xfs_iread_extents()).
63 */
64int
65xfs_iformat_fork(
66 xfs_inode_t *ip,
67 xfs_dinode_t *dip)
68{
69 xfs_attr_shortform_t *atp;
70 int size;
71 int error = 0;
72 xfs_fsize_t di_size;
73
74 if (unlikely(be32_to_cpu(dip->di_nextents) +
75 be16_to_cpu(dip->di_anextents) >
76 be64_to_cpu(dip->di_nblocks))) {
77 xfs_warn(ip->i_mount,
78 "corrupt dinode %Lu, extent total = %d, nblocks = %Lu.",
79 (unsigned long long)ip->i_ino,
80 (int)(be32_to_cpu(dip->di_nextents) +
81 be16_to_cpu(dip->di_anextents)),
82 (unsigned long long)
83 be64_to_cpu(dip->di_nblocks));
84 XFS_CORRUPTION_ERROR("xfs_iformat(1)", XFS_ERRLEVEL_LOW,
85 ip->i_mount, dip);
12b53197 86 return -EFSCORRUPTED;
5d90ab5a
DC
87 }
88
89 if (unlikely(dip->di_forkoff > ip->i_mount->m_sb.sb_inodesize)) {
90 xfs_warn(ip->i_mount, "corrupt dinode %Lu, forkoff = 0x%x.",
91 (unsigned long long)ip->i_ino,
92 dip->di_forkoff);
93 XFS_CORRUPTION_ERROR("xfs_iformat(2)", XFS_ERRLEVEL_LOW,
94 ip->i_mount, dip);
12b53197 95 return -EFSCORRUPTED;
5d90ab5a
DC
96 }
97
98 if (unlikely((ip->i_d.di_flags & XFS_DIFLAG_REALTIME) &&
99 !ip->i_mount->m_rtdev_targp)) {
100 xfs_warn(ip->i_mount,
101 "corrupt dinode %Lu, has realtime flag set.",
102 ip->i_ino);
103 XFS_CORRUPTION_ERROR("xfs_iformat(realtime)",
104 XFS_ERRLEVEL_LOW, ip->i_mount, dip);
12b53197 105 return -EFSCORRUPTED;
5d90ab5a
DC
106 }
107
108 switch (ip->i_d.di_mode & S_IFMT) {
109 case S_IFIFO:
110 case S_IFCHR:
111 case S_IFBLK:
112 case S_IFSOCK:
113 if (unlikely(dip->di_format != XFS_DINODE_FMT_DEV)) {
114 XFS_CORRUPTION_ERROR("xfs_iformat(3)", XFS_ERRLEVEL_LOW,
115 ip->i_mount, dip);
12b53197 116 return -EFSCORRUPTED;
5d90ab5a
DC
117 }
118 ip->i_d.di_size = 0;
119 ip->i_df.if_u2.if_rdev = xfs_dinode_get_rdev(dip);
120 break;
121
122 case S_IFREG:
123 case S_IFLNK:
124 case S_IFDIR:
125 switch (dip->di_format) {
126 case XFS_DINODE_FMT_LOCAL:
127 /*
128 * no local regular files yet
129 */
130 if (unlikely(S_ISREG(be16_to_cpu(dip->di_mode)))) {
131 xfs_warn(ip->i_mount,
132 "corrupt inode %Lu (local format for regular file).",
133 (unsigned long long) ip->i_ino);
134 XFS_CORRUPTION_ERROR("xfs_iformat(4)",
135 XFS_ERRLEVEL_LOW,
136 ip->i_mount, dip);
12b53197 137 return -EFSCORRUPTED;
5d90ab5a
DC
138 }
139
140 di_size = be64_to_cpu(dip->di_size);
e6d77a21
DC
141 if (unlikely(di_size < 0 ||
142 di_size > XFS_DFORK_DSIZE(dip, ip->i_mount))) {
5d90ab5a
DC
143 xfs_warn(ip->i_mount,
144 "corrupt inode %Lu (bad size %Ld for local inode).",
145 (unsigned long long) ip->i_ino,
146 (long long) di_size);
147 XFS_CORRUPTION_ERROR("xfs_iformat(5)",
148 XFS_ERRLEVEL_LOW,
149 ip->i_mount, dip);
12b53197 150 return -EFSCORRUPTED;
5d90ab5a
DC
151 }
152
153 size = (int)di_size;
154 error = xfs_iformat_local(ip, dip, XFS_DATA_FORK, size);
155 break;
156 case XFS_DINODE_FMT_EXTENTS:
157 error = xfs_iformat_extents(ip, dip, XFS_DATA_FORK);
158 break;
159 case XFS_DINODE_FMT_BTREE:
160 error = xfs_iformat_btree(ip, dip, XFS_DATA_FORK);
161 break;
162 default:
163 XFS_ERROR_REPORT("xfs_iformat(6)", XFS_ERRLEVEL_LOW,
164 ip->i_mount);
12b53197 165 return -EFSCORRUPTED;
5d90ab5a
DC
166 }
167 break;
168
169 default:
170 XFS_ERROR_REPORT("xfs_iformat(7)", XFS_ERRLEVEL_LOW, ip->i_mount);
12b53197 171 return -EFSCORRUPTED;
5d90ab5a
DC
172 }
173 if (error) {
174 return error;
175 }
176 if (!XFS_DFORK_Q(dip))
177 return 0;
178
179 ASSERT(ip->i_afp == NULL);
180 ip->i_afp = kmem_zone_zalloc(xfs_ifork_zone, KM_SLEEP | KM_NOFS);
181
182 switch (dip->di_aformat) {
183 case XFS_DINODE_FMT_LOCAL:
184 atp = (xfs_attr_shortform_t *)XFS_DFORK_APTR(dip);
185 size = be16_to_cpu(atp->hdr.totsize);
186
187 if (unlikely(size < sizeof(struct xfs_attr_sf_hdr))) {
188 xfs_warn(ip->i_mount,
189 "corrupt inode %Lu (bad attr fork size %Ld).",
190 (unsigned long long) ip->i_ino,
191 (long long) size);
192 XFS_CORRUPTION_ERROR("xfs_iformat(8)",
193 XFS_ERRLEVEL_LOW,
194 ip->i_mount, dip);
12b53197 195 return -EFSCORRUPTED;
5d90ab5a
DC
196 }
197
198 error = xfs_iformat_local(ip, dip, XFS_ATTR_FORK, size);
199 break;
200 case XFS_DINODE_FMT_EXTENTS:
201 error = xfs_iformat_extents(ip, dip, XFS_ATTR_FORK);
202 break;
203 case XFS_DINODE_FMT_BTREE:
204 error = xfs_iformat_btree(ip, dip, XFS_ATTR_FORK);
205 break;
206 default:
12b53197 207 error = -EFSCORRUPTED;
5d90ab5a
DC
208 break;
209 }
210 if (error) {
211 kmem_zone_free(xfs_ifork_zone, ip->i_afp);
212 ip->i_afp = NULL;
213 xfs_idestroy_fork(ip, XFS_DATA_FORK);
214 }
215 return error;
216}
217
218/*
219 * The file is in-lined in the on-disk inode.
220 * If it fits into if_inline_data, then copy
221 * it there, otherwise allocate a buffer for it
222 * and copy the data there. Either way, set
223 * if_data to point at the data.
224 * If we allocate a buffer for the data, make
225 * sure that its size is a multiple of 4 and
226 * record the real size in i_real_bytes.
227 */
228STATIC int
229xfs_iformat_local(
230 xfs_inode_t *ip,
231 xfs_dinode_t *dip,
232 int whichfork,
233 int size)
234{
235 xfs_ifork_t *ifp;
236 int real_size;
237
238 /*
239 * If the size is unreasonable, then something
240 * is wrong and we just bail out rather than crash in
241 * kmem_alloc() or memcpy() below.
242 */
243 if (unlikely(size > XFS_DFORK_SIZE(dip, ip->i_mount, whichfork))) {
244 xfs_warn(ip->i_mount,
245 "corrupt inode %Lu (bad size %d for local fork, size = %d).",
246 (unsigned long long) ip->i_ino, size,
247 XFS_DFORK_SIZE(dip, ip->i_mount, whichfork));
248 XFS_CORRUPTION_ERROR("xfs_iformat_local", XFS_ERRLEVEL_LOW,
249 ip->i_mount, dip);
12b53197 250 return -EFSCORRUPTED;
5d90ab5a
DC
251 }
252 ifp = XFS_IFORK_PTR(ip, whichfork);
253 real_size = 0;
254 if (size == 0)
255 ifp->if_u1.if_data = NULL;
256 else if (size <= sizeof(ifp->if_u2.if_inline_data))
257 ifp->if_u1.if_data = ifp->if_u2.if_inline_data;
258 else {
259 real_size = roundup(size, 4);
260 ifp->if_u1.if_data = kmem_alloc(real_size, KM_SLEEP | KM_NOFS);
261 }
262 ifp->if_bytes = size;
263 ifp->if_real_bytes = real_size;
264 if (size)
265 memcpy(ifp->if_u1.if_data, XFS_DFORK_PTR(dip, whichfork), size);
266 ifp->if_flags &= ~XFS_IFEXTENTS;
267 ifp->if_flags |= XFS_IFINLINE;
268 return 0;
269}
270
271/*
272 * The file consists of a set of extents all
273 * of which fit into the on-disk inode.
274 * If there are few enough extents to fit into
275 * the if_inline_ext, then copy them there.
276 * Otherwise allocate a buffer for them and copy
277 * them into it. Either way, set if_extents
278 * to point at the extents.
279 */
280STATIC int
281xfs_iformat_extents(
282 xfs_inode_t *ip,
283 xfs_dinode_t *dip,
284 int whichfork)
285{
286 xfs_bmbt_rec_t *dp;
287 xfs_ifork_t *ifp;
288 int nex;
289 int size;
290 int i;
291
292 ifp = XFS_IFORK_PTR(ip, whichfork);
293 nex = XFS_DFORK_NEXTENTS(dip, whichfork);
294 size = nex * (uint)sizeof(xfs_bmbt_rec_t);
295
296 /*
297 * If the number of extents is unreasonable, then something
298 * is wrong and we just bail out rather than crash in
299 * kmem_alloc() or memcpy() below.
300 */
301 if (unlikely(size < 0 || size > XFS_DFORK_SIZE(dip, ip->i_mount, whichfork))) {
302 xfs_warn(ip->i_mount, "corrupt inode %Lu ((a)extents = %d).",
303 (unsigned long long) ip->i_ino, nex);
304 XFS_CORRUPTION_ERROR("xfs_iformat_extents(1)", XFS_ERRLEVEL_LOW,
305 ip->i_mount, dip);
12b53197 306 return -EFSCORRUPTED;
5d90ab5a
DC
307 }
308
309 ifp->if_real_bytes = 0;
310 if (nex == 0)
311 ifp->if_u1.if_extents = NULL;
312 else if (nex <= XFS_INLINE_EXTS)
313 ifp->if_u1.if_extents = ifp->if_u2.if_inline_ext;
314 else
315 xfs_iext_add(ifp, 0, nex);
316
317 ifp->if_bytes = size;
318 if (size) {
319 dp = (xfs_bmbt_rec_t *) XFS_DFORK_PTR(dip, whichfork);
320 xfs_validate_extents(ifp, nex, XFS_EXTFMT_INODE(ip));
321 for (i = 0; i < nex; i++, dp++) {
322 xfs_bmbt_rec_host_t *ep = xfs_iext_get_ext(ifp, i);
323 ep->l0 = get_unaligned_be64(&dp->l0);
324 ep->l1 = get_unaligned_be64(&dp->l1);
325 }
326 XFS_BMAP_TRACE_EXLIST(ip, nex, whichfork);
327 if (whichfork != XFS_DATA_FORK ||
328 XFS_EXTFMT_INODE(ip) == XFS_EXTFMT_NOSTATE)
329 if (unlikely(xfs_check_nostate_extents(
330 ifp, 0, nex))) {
331 XFS_ERROR_REPORT("xfs_iformat_extents(2)",
332 XFS_ERRLEVEL_LOW,
333 ip->i_mount);
12b53197 334 return -EFSCORRUPTED;
5d90ab5a
DC
335 }
336 }
337 ifp->if_flags |= XFS_IFEXTENTS;
338 return 0;
339}
340
341/*
342 * The file has too many extents to fit into
343 * the inode, so they are in B-tree format.
344 * Allocate a buffer for the root of the B-tree
345 * and copy the root into it. The i_extents
346 * field will remain NULL until all of the
347 * extents are read in (when they are needed).
348 */
349STATIC int
350xfs_iformat_btree(
351 xfs_inode_t *ip,
352 xfs_dinode_t *dip,
353 int whichfork)
354{
355 struct xfs_mount *mp = ip->i_mount;
356 xfs_bmdr_block_t *dfp;
357 xfs_ifork_t *ifp;
358 /* REFERENCED */
359 int nrecs;
360 int size;
361
362 ifp = XFS_IFORK_PTR(ip, whichfork);
363 dfp = (xfs_bmdr_block_t *)XFS_DFORK_PTR(dip, whichfork);
364 size = XFS_BMAP_BROOT_SPACE(mp, dfp);
365 nrecs = be16_to_cpu(dfp->bb_numrecs);
366
367 /*
368 * blow out if -- fork has less extents than can fit in
369 * fork (fork shouldn't be a btree format), root btree
370 * block has more records than can fit into the fork,
371 * or the number of extents is greater than the number of
372 * blocks.
373 */
374 if (unlikely(XFS_IFORK_NEXTENTS(ip, whichfork) <=
375 XFS_IFORK_MAXEXT(ip, whichfork) ||
376 XFS_BMDR_SPACE_CALC(nrecs) >
377 XFS_DFORK_SIZE(dip, mp, whichfork) ||
378 XFS_IFORK_NEXTENTS(ip, whichfork) > ip->i_d.di_nblocks)) {
379 xfs_warn(mp, "corrupt inode %Lu (btree).",
380 (unsigned long long) ip->i_ino);
381 XFS_CORRUPTION_ERROR("xfs_iformat_btree", XFS_ERRLEVEL_LOW,
382 mp, dip);
12b53197 383 return -EFSCORRUPTED;
5d90ab5a
DC
384 }
385
386 ifp->if_broot_bytes = size;
387 ifp->if_broot = kmem_alloc(size, KM_SLEEP | KM_NOFS);
388 ASSERT(ifp->if_broot != NULL);
389 /*
390 * Copy and convert from the on-disk structure
391 * to the in-memory structure.
392 */
393 xfs_bmdr_to_bmbt(ip, dfp, XFS_DFORK_SIZE(dip, ip->i_mount, whichfork),
394 ifp->if_broot, size);
395 ifp->if_flags &= ~XFS_IFEXTENTS;
396 ifp->if_flags |= XFS_IFBROOT;
397
398 return 0;
399}
400
401/*
402 * Read in extents from a btree-format inode.
403 * Allocate and fill in if_extents. Real work is done in xfs_bmap.c.
404 */
405int
406xfs_iread_extents(
407 xfs_trans_t *tp,
408 xfs_inode_t *ip,
409 int whichfork)
410{
411 int error;
412 xfs_ifork_t *ifp;
413 xfs_extnum_t nextents;
414
ff105f75
DC
415 ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL));
416
5d90ab5a
DC
417 if (unlikely(XFS_IFORK_FORMAT(ip, whichfork) != XFS_DINODE_FMT_BTREE)) {
418 XFS_ERROR_REPORT("xfs_iread_extents", XFS_ERRLEVEL_LOW,
419 ip->i_mount);
12b53197 420 return -EFSCORRUPTED;
5d90ab5a
DC
421 }
422 nextents = XFS_IFORK_NEXTENTS(ip, whichfork);
423 ifp = XFS_IFORK_PTR(ip, whichfork);
424
425 /*
426 * We know that the size is valid (it's checked in iformat_btree)
427 */
428 ifp->if_bytes = ifp->if_real_bytes = 0;
429 ifp->if_flags |= XFS_IFEXTENTS;
430 xfs_iext_add(ifp, 0, nextents);
431 error = xfs_bmap_read_extents(tp, ip, whichfork);
432 if (error) {
433 xfs_iext_destroy(ifp);
434 ifp->if_flags &= ~XFS_IFEXTENTS;
435 return error;
436 }
437 xfs_validate_extents(ifp, nextents, XFS_EXTFMT_INODE(ip));
438 return 0;
439}
440/*
441 * Reallocate the space for if_broot based on the number of records
442 * being added or deleted as indicated in rec_diff. Move the records
443 * and pointers in if_broot to fit the new size. When shrinking this
444 * will eliminate holes between the records and pointers created by
445 * the caller. When growing this will create holes to be filled in
446 * by the caller.
447 *
448 * The caller must not request to add more records than would fit in
449 * the on-disk inode root. If the if_broot is currently NULL, then
e6d77a21 450 * if we are adding records, one will be allocated. The caller must also
5d90ab5a
DC
451 * not request that the number of records go below zero, although
452 * it can go to zero.
453 *
454 * ip -- the inode whose if_broot area is changing
455 * ext_diff -- the change in the number of records, positive or negative,
456 * requested for the if_broot array.
457 */
458void
459xfs_iroot_realloc(
460 xfs_inode_t *ip,
461 int rec_diff,
462 int whichfork)
463{
464 struct xfs_mount *mp = ip->i_mount;
465 int cur_max;
466 xfs_ifork_t *ifp;
467 struct xfs_btree_block *new_broot;
468 int new_max;
469 size_t new_size;
470 char *np;
471 char *op;
472
473 /*
474 * Handle the degenerate case quietly.
475 */
476 if (rec_diff == 0) {
477 return;
478 }
479
480 ifp = XFS_IFORK_PTR(ip, whichfork);
481 if (rec_diff > 0) {
482 /*
483 * If there wasn't any memory allocated before, just
484 * allocate it now and get out.
485 */
486 if (ifp->if_broot_bytes == 0) {
487 new_size = XFS_BMAP_BROOT_SPACE_CALC(mp, rec_diff);
488 ifp->if_broot = kmem_alloc(new_size, KM_SLEEP | KM_NOFS);
489 ifp->if_broot_bytes = (int)new_size;
490 return;
491 }
492
493 /*
494 * If there is already an existing if_broot, then we need
495 * to realloc() it and shift the pointers to their new
496 * location. The records don't change location because
497 * they are kept butted up against the btree block header.
498 */
499 cur_max = xfs_bmbt_maxrecs(mp, ifp->if_broot_bytes, 0);
500 new_max = cur_max + rec_diff;
501 new_size = XFS_BMAP_BROOT_SPACE_CALC(mp, new_max);
502 ifp->if_broot = kmem_realloc(ifp->if_broot, new_size,
503 XFS_BMAP_BROOT_SPACE_CALC(mp, cur_max),
504 KM_SLEEP | KM_NOFS);
505 op = (char *)XFS_BMAP_BROOT_PTR_ADDR(mp, ifp->if_broot, 1,
506 ifp->if_broot_bytes);
507 np = (char *)XFS_BMAP_BROOT_PTR_ADDR(mp, ifp->if_broot, 1,
508 (int)new_size);
509 ifp->if_broot_bytes = (int)new_size;
510 ASSERT(XFS_BMAP_BMDR_SPACE(ifp->if_broot) <=
511 XFS_IFORK_SIZE(ip, whichfork));
512 memmove(np, op, cur_max * (uint)sizeof(xfs_dfsbno_t));
513 return;
514 }
515
516 /*
517 * rec_diff is less than 0. In this case, we are shrinking the
518 * if_broot buffer. It must already exist. If we go to zero
519 * records, just get rid of the root and clear the status bit.
520 */
521 ASSERT((ifp->if_broot != NULL) && (ifp->if_broot_bytes > 0));
522 cur_max = xfs_bmbt_maxrecs(mp, ifp->if_broot_bytes, 0);
523 new_max = cur_max + rec_diff;
524 ASSERT(new_max >= 0);
525 if (new_max > 0)
526 new_size = XFS_BMAP_BROOT_SPACE_CALC(mp, new_max);
527 else
528 new_size = 0;
529 if (new_size > 0) {
530 new_broot = kmem_alloc(new_size, KM_SLEEP | KM_NOFS);
531 /*
532 * First copy over the btree block header.
533 */
534 memcpy(new_broot, ifp->if_broot,
535 XFS_BMBT_BLOCK_LEN(ip->i_mount));
536 } else {
537 new_broot = NULL;
538 ifp->if_flags &= ~XFS_IFBROOT;
539 }
540
541 /*
542 * Only copy the records and pointers if there are any.
543 */
544 if (new_max > 0) {
545 /*
546 * First copy the records.
547 */
548 op = (char *)XFS_BMBT_REC_ADDR(mp, ifp->if_broot, 1);
549 np = (char *)XFS_BMBT_REC_ADDR(mp, new_broot, 1);
550 memcpy(np, op, new_max * (uint)sizeof(xfs_bmbt_rec_t));
551
552 /*
553 * Then copy the pointers.
554 */
555 op = (char *)XFS_BMAP_BROOT_PTR_ADDR(mp, ifp->if_broot, 1,
556 ifp->if_broot_bytes);
557 np = (char *)XFS_BMAP_BROOT_PTR_ADDR(mp, new_broot, 1,
558 (int)new_size);
559 memcpy(np, op, new_max * (uint)sizeof(xfs_dfsbno_t));
560 }
561 kmem_free(ifp->if_broot);
562 ifp->if_broot = new_broot;
563 ifp->if_broot_bytes = (int)new_size;
564 if (ifp->if_broot)
565 ASSERT(XFS_BMAP_BMDR_SPACE(ifp->if_broot) <=
566 XFS_IFORK_SIZE(ip, whichfork));
567 return;
568}
569
570
571/*
572 * This is called when the amount of space needed for if_data
573 * is increased or decreased. The change in size is indicated by
574 * the number of bytes that need to be added or deleted in the
575 * byte_diff parameter.
576 *
577 * If the amount of space needed has decreased below the size of the
578 * inline buffer, then switch to using the inline buffer. Otherwise,
579 * use kmem_realloc() or kmem_alloc() to adjust the size of the buffer
580 * to what is needed.
581 *
582 * ip -- the inode whose if_data area is changing
583 * byte_diff -- the change in the number of bytes, positive or negative,
584 * requested for the if_data array.
585 */
586void
587xfs_idata_realloc(
588 xfs_inode_t *ip,
589 int byte_diff,
590 int whichfork)
591{
592 xfs_ifork_t *ifp;
593 int new_size;
594 int real_size;
595
596 if (byte_diff == 0) {
597 return;
598 }
599
600 ifp = XFS_IFORK_PTR(ip, whichfork);
601 new_size = (int)ifp->if_bytes + byte_diff;
602 ASSERT(new_size >= 0);
603
604 if (new_size == 0) {
605 if (ifp->if_u1.if_data != ifp->if_u2.if_inline_data) {
606 kmem_free(ifp->if_u1.if_data);
607 }
608 ifp->if_u1.if_data = NULL;
609 real_size = 0;
610 } else if (new_size <= sizeof(ifp->if_u2.if_inline_data)) {
611 /*
612 * If the valid extents/data can fit in if_inline_ext/data,
613 * copy them from the malloc'd vector and free it.
614 */
615 if (ifp->if_u1.if_data == NULL) {
616 ifp->if_u1.if_data = ifp->if_u2.if_inline_data;
617 } else if (ifp->if_u1.if_data != ifp->if_u2.if_inline_data) {
618 ASSERT(ifp->if_real_bytes != 0);
619 memcpy(ifp->if_u2.if_inline_data, ifp->if_u1.if_data,
620 new_size);
621 kmem_free(ifp->if_u1.if_data);
622 ifp->if_u1.if_data = ifp->if_u2.if_inline_data;
623 }
624 real_size = 0;
625 } else {
626 /*
627 * Stuck with malloc/realloc.
628 * For inline data, the underlying buffer must be
629 * a multiple of 4 bytes in size so that it can be
630 * logged and stay on word boundaries. We enforce
631 * that here.
632 */
633 real_size = roundup(new_size, 4);
634 if (ifp->if_u1.if_data == NULL) {
635 ASSERT(ifp->if_real_bytes == 0);
636 ifp->if_u1.if_data = kmem_alloc(real_size,
637 KM_SLEEP | KM_NOFS);
638 } else if (ifp->if_u1.if_data != ifp->if_u2.if_inline_data) {
639 /*
640 * Only do the realloc if the underlying size
641 * is really changing.
642 */
643 if (ifp->if_real_bytes != real_size) {
644 ifp->if_u1.if_data =
645 kmem_realloc(ifp->if_u1.if_data,
646 real_size,
647 ifp->if_real_bytes,
648 KM_SLEEP | KM_NOFS);
649 }
650 } else {
651 ASSERT(ifp->if_real_bytes == 0);
652 ifp->if_u1.if_data = kmem_alloc(real_size,
653 KM_SLEEP | KM_NOFS);
654 memcpy(ifp->if_u1.if_data, ifp->if_u2.if_inline_data,
655 ifp->if_bytes);
656 }
657 }
658 ifp->if_real_bytes = real_size;
659 ifp->if_bytes = new_size;
660 ASSERT(ifp->if_bytes <= XFS_IFORK_SIZE(ip, whichfork));
661}
662
663void
664xfs_idestroy_fork(
665 xfs_inode_t *ip,
666 int whichfork)
667{
668 xfs_ifork_t *ifp;
669
670 ifp = XFS_IFORK_PTR(ip, whichfork);
671 if (ifp->if_broot != NULL) {
672 kmem_free(ifp->if_broot);
673 ifp->if_broot = NULL;
674 }
675
676 /*
677 * If the format is local, then we can't have an extents
678 * array so just look for an inline data array. If we're
679 * not local then we may or may not have an extents list,
680 * so check and free it up if we do.
681 */
682 if (XFS_IFORK_FORMAT(ip, whichfork) == XFS_DINODE_FMT_LOCAL) {
683 if ((ifp->if_u1.if_data != ifp->if_u2.if_inline_data) &&
684 (ifp->if_u1.if_data != NULL)) {
685 ASSERT(ifp->if_real_bytes != 0);
686 kmem_free(ifp->if_u1.if_data);
687 ifp->if_u1.if_data = NULL;
688 ifp->if_real_bytes = 0;
689 }
690 } else if ((ifp->if_flags & XFS_IFEXTENTS) &&
691 ((ifp->if_flags & XFS_IFEXTIREC) ||
692 ((ifp->if_u1.if_extents != NULL) &&
693 (ifp->if_u1.if_extents != ifp->if_u2.if_inline_ext)))) {
694 ASSERT(ifp->if_real_bytes != 0);
695 xfs_iext_destroy(ifp);
696 }
697 ASSERT(ifp->if_u1.if_extents == NULL ||
698 ifp->if_u1.if_extents == ifp->if_u2.if_inline_ext);
699 ASSERT(ifp->if_real_bytes == 0);
700 if (whichfork == XFS_ATTR_FORK) {
701 kmem_zone_free(xfs_ifork_zone, ip->i_afp);
702 ip->i_afp = NULL;
703 }
704}
705
706/*
ff105f75 707 * Convert in-core extents to on-disk form
5d90ab5a 708 *
ff105f75
DC
709 * For either the data or attr fork in extent format, we need to endian convert
710 * the in-core extent as we place them into the on-disk inode.
5d90ab5a 711 *
ff105f75
DC
712 * In the case of the data fork, the in-core and on-disk fork sizes can be
713 * different due to delayed allocation extents. We only copy on-disk extents
714 * here, so callers must always use the physical fork size to determine the
715 * size of the buffer passed to this routine. We will return the size actually
716 * used.
5d90ab5a
DC
717 */
718int
719xfs_iextents_copy(
720 xfs_inode_t *ip,
721 xfs_bmbt_rec_t *dp,
722 int whichfork)
723{
724 int copied;
725 int i;
726 xfs_ifork_t *ifp;
727 int nrecs;
728 xfs_fsblock_t start_block;
729
730 ifp = XFS_IFORK_PTR(ip, whichfork);
731 ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL|XFS_ILOCK_SHARED));
732 ASSERT(ifp->if_bytes > 0);
733
734 nrecs = ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t);
735 XFS_BMAP_TRACE_EXLIST(ip, nrecs, whichfork);
736 ASSERT(nrecs > 0);
737
738 /*
739 * There are some delayed allocation extents in the
740 * inode, so copy the extents one at a time and skip
741 * the delayed ones. There must be at least one
742 * non-delayed extent.
743 */
744 copied = 0;
745 for (i = 0; i < nrecs; i++) {
746 xfs_bmbt_rec_host_t *ep = xfs_iext_get_ext(ifp, i);
747 start_block = xfs_bmbt_get_startblock(ep);
748 if (isnullstartblock(start_block)) {
749 /*
750 * It's a delayed allocation extent, so skip it.
751 */
752 continue;
753 }
754
755 /* Translate to on disk format */
756 put_unaligned_be64(ep->l0, &dp->l0);
757 put_unaligned_be64(ep->l1, &dp->l1);
758 dp++;
759 copied++;
760 }
761 ASSERT(copied != 0);
762 xfs_validate_extents(ifp, copied, XFS_EXTFMT_INODE(ip));
763
764 return (copied * (uint)sizeof(xfs_bmbt_rec_t));
765}
766
767/*
768 * Each of the following cases stores data into the same region
769 * of the on-disk inode, so only one of them can be valid at
770 * any given time. While it is possible to have conflicting formats
771 * and log flags, e.g. having XFS_ILOG_?DATA set when the fork is
772 * in EXTENTS format, this can only happen when the fork has
773 * changed formats after being modified but before being flushed.
774 * In these cases, the format always takes precedence, because the
775 * format indicates the current state of the fork.
776 */
777void
778xfs_iflush_fork(
779 xfs_inode_t *ip,
780 xfs_dinode_t *dip,
781 xfs_inode_log_item_t *iip,
ff105f75 782 int whichfork)
5d90ab5a
DC
783{
784 char *cp;
785 xfs_ifork_t *ifp;
786 xfs_mount_t *mp;
787 static const short brootflag[2] =
788 { XFS_ILOG_DBROOT, XFS_ILOG_ABROOT };
789 static const short dataflag[2] =
790 { XFS_ILOG_DDATA, XFS_ILOG_ADATA };
791 static const short extflag[2] =
792 { XFS_ILOG_DEXT, XFS_ILOG_AEXT };
793
794 if (!iip)
795 return;
796 ifp = XFS_IFORK_PTR(ip, whichfork);
797 /*
798 * This can happen if we gave up in iformat in an error path,
799 * for the attribute fork.
800 */
801 if (!ifp) {
802 ASSERT(whichfork == XFS_ATTR_FORK);
803 return;
804 }
805 cp = XFS_DFORK_PTR(dip, whichfork);
806 mp = ip->i_mount;
807 switch (XFS_IFORK_FORMAT(ip, whichfork)) {
808 case XFS_DINODE_FMT_LOCAL:
809 if ((iip->ili_fields & dataflag[whichfork]) &&
810 (ifp->if_bytes > 0)) {
811 ASSERT(ifp->if_u1.if_data != NULL);
812 ASSERT(ifp->if_bytes <= XFS_IFORK_SIZE(ip, whichfork));
813 memcpy(cp, ifp->if_u1.if_data, ifp->if_bytes);
814 }
815 break;
816
817 case XFS_DINODE_FMT_EXTENTS:
818 ASSERT((ifp->if_flags & XFS_IFEXTENTS) ||
819 !(iip->ili_fields & extflag[whichfork]));
820 if ((iip->ili_fields & extflag[whichfork]) &&
821 (ifp->if_bytes > 0)) {
822 ASSERT(xfs_iext_get_ext(ifp, 0));
823 ASSERT(XFS_IFORK_NEXTENTS(ip, whichfork) > 0);
824 (void)xfs_iextents_copy(ip, (xfs_bmbt_rec_t *)cp,
825 whichfork);
826 }
827 break;
828
829 case XFS_DINODE_FMT_BTREE:
830 if ((iip->ili_fields & brootflag[whichfork]) &&
831 (ifp->if_broot_bytes > 0)) {
832 ASSERT(ifp->if_broot != NULL);
833 ASSERT(XFS_BMAP_BMDR_SPACE(ifp->if_broot) <=
834 XFS_IFORK_SIZE(ip, whichfork));
835 xfs_bmbt_to_bmdr(mp, ifp->if_broot, ifp->if_broot_bytes,
836 (xfs_bmdr_block_t *)cp,
837 XFS_DFORK_SIZE(dip, mp, whichfork));
838 }
839 break;
840
841 case XFS_DINODE_FMT_DEV:
842 if (iip->ili_fields & XFS_ILOG_DEV) {
843 ASSERT(whichfork == XFS_DATA_FORK);
844 xfs_dinode_put_rdev(dip, ip->i_df.if_u2.if_rdev);
845 }
846 break;
847
848 case XFS_DINODE_FMT_UUID:
849 if (iip->ili_fields & XFS_ILOG_UUID) {
850 ASSERT(whichfork == XFS_DATA_FORK);
851 memcpy(XFS_DFORK_DPTR(dip),
852 &ip->i_df.if_u2.if_uuid,
853 sizeof(uuid_t));
854 }
855 break;
856
857 default:
858 ASSERT(0);
859 break;
860 }
861}
862
863/*
864 * Return a pointer to the extent record at file index idx.
865 */
866xfs_bmbt_rec_host_t *
867xfs_iext_get_ext(
868 xfs_ifork_t *ifp, /* inode fork pointer */
869 xfs_extnum_t idx) /* index of target extent */
870{
871 ASSERT(idx >= 0);
872 ASSERT(idx < ifp->if_bytes / sizeof(xfs_bmbt_rec_t));
873
874 if ((ifp->if_flags & XFS_IFEXTIREC) && (idx == 0)) {
875 return ifp->if_u1.if_ext_irec->er_extbuf;
876 } else if (ifp->if_flags & XFS_IFEXTIREC) {
877 xfs_ext_irec_t *erp; /* irec pointer */
878 int erp_idx = 0; /* irec index */
879 xfs_extnum_t page_idx = idx; /* ext index in target list */
880
881 erp = xfs_iext_idx_to_irec(ifp, &page_idx, &erp_idx, 0);
882 return &erp->er_extbuf[page_idx];
883 } else if (ifp->if_bytes) {
884 return &ifp->if_u1.if_extents[idx];
885 } else {
886 return NULL;
887 }
888}
889
890/*
891 * Insert new item(s) into the extent records for incore inode
892 * fork 'ifp'. 'count' new items are inserted at index 'idx'.
893 */
894void
895xfs_iext_insert(
896 xfs_inode_t *ip, /* incore inode pointer */
897 xfs_extnum_t idx, /* starting index of new items */
898 xfs_extnum_t count, /* number of inserted items */
899 xfs_bmbt_irec_t *new, /* items to insert */
900 int state) /* type of extent conversion */
901{
902 xfs_ifork_t *ifp = (state & BMAP_ATTRFORK) ? ip->i_afp : &ip->i_df;
903 xfs_extnum_t i; /* extent record index */
904
905 trace_xfs_iext_insert(ip, idx, new, state, _RET_IP_);
906
907 ASSERT(ifp->if_flags & XFS_IFEXTENTS);
908 xfs_iext_add(ifp, idx, count);
909 for (i = idx; i < idx + count; i++, new++)
910 xfs_bmbt_set_all(xfs_iext_get_ext(ifp, i), new);
911}
912
913/*
914 * This is called when the amount of space required for incore file
915 * extents needs to be increased. The ext_diff parameter stores the
916 * number of new extents being added and the idx parameter contains
917 * the extent index where the new extents will be added. If the new
918 * extents are being appended, then we just need to (re)allocate and
919 * initialize the space. Otherwise, if the new extents are being
920 * inserted into the middle of the existing entries, a bit more work
921 * is required to make room for the new extents to be inserted. The
922 * caller is responsible for filling in the new extent entries upon
923 * return.
924 */
925void
926xfs_iext_add(
927 xfs_ifork_t *ifp, /* inode fork pointer */
928 xfs_extnum_t idx, /* index to begin adding exts */
929 int ext_diff) /* number of extents to add */
930{
931 int byte_diff; /* new bytes being added */
932 int new_size; /* size of extents after adding */
933 xfs_extnum_t nextents; /* number of extents in file */
934
935 nextents = ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t);
936 ASSERT((idx >= 0) && (idx <= nextents));
937 byte_diff = ext_diff * sizeof(xfs_bmbt_rec_t);
938 new_size = ifp->if_bytes + byte_diff;
939 /*
940 * If the new number of extents (nextents + ext_diff)
941 * fits inside the inode, then continue to use the inline
942 * extent buffer.
943 */
944 if (nextents + ext_diff <= XFS_INLINE_EXTS) {
945 if (idx < nextents) {
946 memmove(&ifp->if_u2.if_inline_ext[idx + ext_diff],
947 &ifp->if_u2.if_inline_ext[idx],
948 (nextents - idx) * sizeof(xfs_bmbt_rec_t));
949 memset(&ifp->if_u2.if_inline_ext[idx], 0, byte_diff);
950 }
951 ifp->if_u1.if_extents = ifp->if_u2.if_inline_ext;
952 ifp->if_real_bytes = 0;
953 }
954 /*
955 * Otherwise use a linear (direct) extent list.
956 * If the extents are currently inside the inode,
957 * xfs_iext_realloc_direct will switch us from
958 * inline to direct extent allocation mode.
959 */
960 else if (nextents + ext_diff <= XFS_LINEAR_EXTS) {
961 xfs_iext_realloc_direct(ifp, new_size);
962 if (idx < nextents) {
963 memmove(&ifp->if_u1.if_extents[idx + ext_diff],
964 &ifp->if_u1.if_extents[idx],
965 (nextents - idx) * sizeof(xfs_bmbt_rec_t));
966 memset(&ifp->if_u1.if_extents[idx], 0, byte_diff);
967 }
968 }
969 /* Indirection array */
970 else {
971 xfs_ext_irec_t *erp;
972 int erp_idx = 0;
973 int page_idx = idx;
974
975 ASSERT(nextents + ext_diff > XFS_LINEAR_EXTS);
976 if (ifp->if_flags & XFS_IFEXTIREC) {
977 erp = xfs_iext_idx_to_irec(ifp, &page_idx, &erp_idx, 1);
978 } else {
979 xfs_iext_irec_init(ifp);
980 ASSERT(ifp->if_flags & XFS_IFEXTIREC);
981 erp = ifp->if_u1.if_ext_irec;
982 }
983 /* Extents fit in target extent page */
984 if (erp && erp->er_extcount + ext_diff <= XFS_LINEAR_EXTS) {
985 if (page_idx < erp->er_extcount) {
986 memmove(&erp->er_extbuf[page_idx + ext_diff],
987 &erp->er_extbuf[page_idx],
988 (erp->er_extcount - page_idx) *
989 sizeof(xfs_bmbt_rec_t));
990 memset(&erp->er_extbuf[page_idx], 0, byte_diff);
991 }
992 erp->er_extcount += ext_diff;
993 xfs_iext_irec_update_extoffs(ifp, erp_idx + 1, ext_diff);
994 }
995 /* Insert a new extent page */
996 else if (erp) {
997 xfs_iext_add_indirect_multi(ifp,
998 erp_idx, page_idx, ext_diff);
999 }
1000 /*
1001 * If extent(s) are being appended to the last page in
1002 * the indirection array and the new extent(s) don't fit
1003 * in the page, then erp is NULL and erp_idx is set to
1004 * the next index needed in the indirection array.
1005 */
1006 else {
ff105f75 1007 uint count = ext_diff;
5d90ab5a
DC
1008
1009 while (count) {
1010 erp = xfs_iext_irec_new(ifp, erp_idx);
ff105f75
DC
1011 erp->er_extcount = min(count, XFS_LINEAR_EXTS);
1012 count -= erp->er_extcount;
1013 if (count)
5d90ab5a 1014 erp_idx++;
5d90ab5a
DC
1015 }
1016 }
1017 }
1018 ifp->if_bytes = new_size;
1019}
1020
1021/*
1022 * This is called when incore extents are being added to the indirection
1023 * array and the new extents do not fit in the target extent list. The
1024 * erp_idx parameter contains the irec index for the target extent list
1025 * in the indirection array, and the idx parameter contains the extent
1026 * index within the list. The number of extents being added is stored
1027 * in the count parameter.
1028 *
1029 * |-------| |-------|
1030 * | | | | idx - number of extents before idx
1031 * | idx | | count |
1032 * | | | | count - number of extents being inserted at idx
1033 * |-------| |-------|
1034 * | count | | nex2 | nex2 - number of extents after idx + count
1035 * |-------| |-------|
1036 */
1037void
1038xfs_iext_add_indirect_multi(
1039 xfs_ifork_t *ifp, /* inode fork pointer */
1040 int erp_idx, /* target extent irec index */
1041 xfs_extnum_t idx, /* index within target list */
1042 int count) /* new extents being added */
1043{
1044 int byte_diff; /* new bytes being added */
1045 xfs_ext_irec_t *erp; /* pointer to irec entry */
1046 xfs_extnum_t ext_diff; /* number of extents to add */
1047 xfs_extnum_t ext_cnt; /* new extents still needed */
1048 xfs_extnum_t nex2; /* extents after idx + count */
1049 xfs_bmbt_rec_t *nex2_ep = NULL; /* temp list for nex2 extents */
1050 int nlists; /* number of irec's (lists) */
1051
1052 ASSERT(ifp->if_flags & XFS_IFEXTIREC);
1053 erp = &ifp->if_u1.if_ext_irec[erp_idx];
1054 nex2 = erp->er_extcount - idx;
1055 nlists = ifp->if_real_bytes / XFS_IEXT_BUFSZ;
1056
1057 /*
1058 * Save second part of target extent list
1059 * (all extents past */
1060 if (nex2) {
1061 byte_diff = nex2 * sizeof(xfs_bmbt_rec_t);
1062 nex2_ep = (xfs_bmbt_rec_t *) kmem_alloc(byte_diff, KM_NOFS);
1063 memmove(nex2_ep, &erp->er_extbuf[idx], byte_diff);
1064 erp->er_extcount -= nex2;
1065 xfs_iext_irec_update_extoffs(ifp, erp_idx + 1, -nex2);
1066 memset(&erp->er_extbuf[idx], 0, byte_diff);
1067 }
1068
1069 /*
1070 * Add the new extents to the end of the target
1071 * list, then allocate new irec record(s) and
1072 * extent buffer(s) as needed to store the rest
1073 * of the new extents.
1074 */
1075 ext_cnt = count;
1076 ext_diff = MIN(ext_cnt, (int)XFS_LINEAR_EXTS - erp->er_extcount);
1077 if (ext_diff) {
1078 erp->er_extcount += ext_diff;
1079 xfs_iext_irec_update_extoffs(ifp, erp_idx + 1, ext_diff);
1080 ext_cnt -= ext_diff;
1081 }
1082 while (ext_cnt) {
1083 erp_idx++;
1084 erp = xfs_iext_irec_new(ifp, erp_idx);
1085 ext_diff = MIN(ext_cnt, (int)XFS_LINEAR_EXTS);
1086 erp->er_extcount = ext_diff;
1087 xfs_iext_irec_update_extoffs(ifp, erp_idx + 1, ext_diff);
1088 ext_cnt -= ext_diff;
1089 }
1090
1091 /* Add nex2 extents back to indirection array */
1092 if (nex2) {
1093 xfs_extnum_t ext_avail;
1094 int i;
1095
1096 byte_diff = nex2 * sizeof(xfs_bmbt_rec_t);
1097 ext_avail = XFS_LINEAR_EXTS - erp->er_extcount;
1098 i = 0;
1099 /*
1100 * If nex2 extents fit in the current page, append
1101 * nex2_ep after the new extents.
1102 */
1103 if (nex2 <= ext_avail) {
1104 i = erp->er_extcount;
1105 }
1106 /*
1107 * Otherwise, check if space is available in the
1108 * next page.
1109 */
1110 else if ((erp_idx < nlists - 1) &&
1111 (nex2 <= (ext_avail = XFS_LINEAR_EXTS -
1112 ifp->if_u1.if_ext_irec[erp_idx+1].er_extcount))) {
1113 erp_idx++;
1114 erp++;
1115 /* Create a hole for nex2 extents */
1116 memmove(&erp->er_extbuf[nex2], erp->er_extbuf,
1117 erp->er_extcount * sizeof(xfs_bmbt_rec_t));
1118 }
1119 /*
1120 * Final choice, create a new extent page for
1121 * nex2 extents.
1122 */
1123 else {
1124 erp_idx++;
1125 erp = xfs_iext_irec_new(ifp, erp_idx);
1126 }
1127 memmove(&erp->er_extbuf[i], nex2_ep, byte_diff);
1128 kmem_free(nex2_ep);
1129 erp->er_extcount += nex2;
1130 xfs_iext_irec_update_extoffs(ifp, erp_idx + 1, nex2);
1131 }
1132}
1133
1134/*
1135 * This is called when the amount of space required for incore file
1136 * extents needs to be decreased. The ext_diff parameter stores the
1137 * number of extents to be removed and the idx parameter contains
1138 * the extent index where the extents will be removed from.
1139 *
1140 * If the amount of space needed has decreased below the linear
1141 * limit, XFS_IEXT_BUFSZ, then switch to using the contiguous
1142 * extent array. Otherwise, use kmem_realloc() to adjust the
1143 * size to what is needed.
1144 */
1145void
1146xfs_iext_remove(
1147 xfs_inode_t *ip, /* incore inode pointer */
1148 xfs_extnum_t idx, /* index to begin removing exts */
1149 int ext_diff, /* number of extents to remove */
1150 int state) /* type of extent conversion */
1151{
1152 xfs_ifork_t *ifp = (state & BMAP_ATTRFORK) ? ip->i_afp : &ip->i_df;
1153 xfs_extnum_t nextents; /* number of extents in file */
1154 int new_size; /* size of extents after removal */
1155
1156 trace_xfs_iext_remove(ip, idx, state, _RET_IP_);
1157
1158 ASSERT(ext_diff > 0);
1159 nextents = ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t);
1160 new_size = (nextents - ext_diff) * sizeof(xfs_bmbt_rec_t);
1161
1162 if (new_size == 0) {
1163 xfs_iext_destroy(ifp);
1164 } else if (ifp->if_flags & XFS_IFEXTIREC) {
1165 xfs_iext_remove_indirect(ifp, idx, ext_diff);
1166 } else if (ifp->if_real_bytes) {
1167 xfs_iext_remove_direct(ifp, idx, ext_diff);
1168 } else {
1169 xfs_iext_remove_inline(ifp, idx, ext_diff);
1170 }
1171 ifp->if_bytes = new_size;
1172}
1173
1174/*
1175 * This removes ext_diff extents from the inline buffer, beginning
1176 * at extent index idx.
1177 */
1178void
1179xfs_iext_remove_inline(
1180 xfs_ifork_t *ifp, /* inode fork pointer */
1181 xfs_extnum_t idx, /* index to begin removing exts */
1182 int ext_diff) /* number of extents to remove */
1183{
1184 int nextents; /* number of extents in file */
1185
1186 ASSERT(!(ifp->if_flags & XFS_IFEXTIREC));
1187 ASSERT(idx < XFS_INLINE_EXTS);
1188 nextents = ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t);
1189 ASSERT(((nextents - ext_diff) > 0) &&
1190 (nextents - ext_diff) < XFS_INLINE_EXTS);
1191
1192 if (idx + ext_diff < nextents) {
1193 memmove(&ifp->if_u2.if_inline_ext[idx],
1194 &ifp->if_u2.if_inline_ext[idx + ext_diff],
1195 (nextents - (idx + ext_diff)) *
1196 sizeof(xfs_bmbt_rec_t));
1197 memset(&ifp->if_u2.if_inline_ext[nextents - ext_diff],
1198 0, ext_diff * sizeof(xfs_bmbt_rec_t));
1199 } else {
1200 memset(&ifp->if_u2.if_inline_ext[idx], 0,
1201 ext_diff * sizeof(xfs_bmbt_rec_t));
1202 }
1203}
1204
1205/*
1206 * This removes ext_diff extents from a linear (direct) extent list,
1207 * beginning at extent index idx. If the extents are being removed
1208 * from the end of the list (ie. truncate) then we just need to re-
1209 * allocate the list to remove the extra space. Otherwise, if the
1210 * extents are being removed from the middle of the existing extent
1211 * entries, then we first need to move the extent records beginning
1212 * at idx + ext_diff up in the list to overwrite the records being
1213 * removed, then remove the extra space via kmem_realloc.
1214 */
1215void
1216xfs_iext_remove_direct(
1217 xfs_ifork_t *ifp, /* inode fork pointer */
1218 xfs_extnum_t idx, /* index to begin removing exts */
1219 int ext_diff) /* number of extents to remove */
1220{
1221 xfs_extnum_t nextents; /* number of extents in file */
1222 int new_size; /* size of extents after removal */
1223
1224 ASSERT(!(ifp->if_flags & XFS_IFEXTIREC));
1225 new_size = ifp->if_bytes -
1226 (ext_diff * sizeof(xfs_bmbt_rec_t));
1227 nextents = ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t);
1228
1229 if (new_size == 0) {
1230 xfs_iext_destroy(ifp);
1231 return;
1232 }
1233 /* Move extents up in the list (if needed) */
1234 if (idx + ext_diff < nextents) {
1235 memmove(&ifp->if_u1.if_extents[idx],
1236 &ifp->if_u1.if_extents[idx + ext_diff],
1237 (nextents - (idx + ext_diff)) *
1238 sizeof(xfs_bmbt_rec_t));
1239 }
1240 memset(&ifp->if_u1.if_extents[nextents - ext_diff],
1241 0, ext_diff * sizeof(xfs_bmbt_rec_t));
1242 /*
1243 * Reallocate the direct extent list. If the extents
1244 * will fit inside the inode then xfs_iext_realloc_direct
1245 * will switch from direct to inline extent allocation
1246 * mode for us.
1247 */
1248 xfs_iext_realloc_direct(ifp, new_size);
1249 ifp->if_bytes = new_size;
1250}
1251
1252/*
1253 * This is called when incore extents are being removed from the
1254 * indirection array and the extents being removed span multiple extent
1255 * buffers. The idx parameter contains the file extent index where we
1256 * want to begin removing extents, and the count parameter contains
1257 * how many extents need to be removed.
1258 *
1259 * |-------| |-------|
1260 * | nex1 | | | nex1 - number of extents before idx
1261 * |-------| | count |
1262 * | | | | count - number of extents being removed at idx
1263 * | count | |-------|
1264 * | | | nex2 | nex2 - number of extents after idx + count
1265 * |-------| |-------|
1266 */
1267void
1268xfs_iext_remove_indirect(
1269 xfs_ifork_t *ifp, /* inode fork pointer */
1270 xfs_extnum_t idx, /* index to begin removing extents */
1271 int count) /* number of extents to remove */
1272{
1273 xfs_ext_irec_t *erp; /* indirection array pointer */
1274 int erp_idx = 0; /* indirection array index */
1275 xfs_extnum_t ext_cnt; /* extents left to remove */
1276 xfs_extnum_t ext_diff; /* extents to remove in current list */
1277 xfs_extnum_t nex1; /* number of extents before idx */
1278 xfs_extnum_t nex2; /* extents after idx + count */
1279 int page_idx = idx; /* index in target extent list */
1280
1281 ASSERT(ifp->if_flags & XFS_IFEXTIREC);
1282 erp = xfs_iext_idx_to_irec(ifp, &page_idx, &erp_idx, 0);
1283 ASSERT(erp != NULL);
1284 nex1 = page_idx;
1285 ext_cnt = count;
1286 while (ext_cnt) {
1287 nex2 = MAX((erp->er_extcount - (nex1 + ext_cnt)), 0);
1288 ext_diff = MIN(ext_cnt, (erp->er_extcount - nex1));
1289 /*
1290 * Check for deletion of entire list;
1291 * xfs_iext_irec_remove() updates extent offsets.
1292 */
1293 if (ext_diff == erp->er_extcount) {
1294 xfs_iext_irec_remove(ifp, erp_idx);
1295 ext_cnt -= ext_diff;
1296 nex1 = 0;
1297 if (ext_cnt) {
1298 ASSERT(erp_idx < ifp->if_real_bytes /
1299 XFS_IEXT_BUFSZ);
1300 erp = &ifp->if_u1.if_ext_irec[erp_idx];
1301 nex1 = 0;
1302 continue;
1303 } else {
1304 break;
1305 }
1306 }
1307 /* Move extents up (if needed) */
1308 if (nex2) {
1309 memmove(&erp->er_extbuf[nex1],
1310 &erp->er_extbuf[nex1 + ext_diff],
1311 nex2 * sizeof(xfs_bmbt_rec_t));
1312 }
1313 /* Zero out rest of page */
1314 memset(&erp->er_extbuf[nex1 + nex2], 0, (XFS_IEXT_BUFSZ -
1315 ((nex1 + nex2) * sizeof(xfs_bmbt_rec_t))));
1316 /* Update remaining counters */
1317 erp->er_extcount -= ext_diff;
1318 xfs_iext_irec_update_extoffs(ifp, erp_idx + 1, -ext_diff);
1319 ext_cnt -= ext_diff;
1320 nex1 = 0;
1321 erp_idx++;
1322 erp++;
1323 }
1324 ifp->if_bytes -= count * sizeof(xfs_bmbt_rec_t);
1325 xfs_iext_irec_compact(ifp);
1326}
1327
1328/*
1329 * Create, destroy, or resize a linear (direct) block of extents.
1330 */
1331void
1332xfs_iext_realloc_direct(
1333 xfs_ifork_t *ifp, /* inode fork pointer */
3e23516a 1334 int new_size) /* new size of extents after adding */
5d90ab5a
DC
1335{
1336 int rnew_size; /* real new size of extents */
1337
1338 rnew_size = new_size;
1339
1340 ASSERT(!(ifp->if_flags & XFS_IFEXTIREC) ||
1341 ((new_size >= 0) && (new_size <= XFS_IEXT_BUFSZ) &&
1342 (new_size != ifp->if_real_bytes)));
1343
1344 /* Free extent records */
1345 if (new_size == 0) {
1346 xfs_iext_destroy(ifp);
1347 }
1348 /* Resize direct extent list and zero any new bytes */
1349 else if (ifp->if_real_bytes) {
1350 /* Check if extents will fit inside the inode */
1351 if (new_size <= XFS_INLINE_EXTS * sizeof(xfs_bmbt_rec_t)) {
1352 xfs_iext_direct_to_inline(ifp, new_size /
1353 (uint)sizeof(xfs_bmbt_rec_t));
1354 ifp->if_bytes = new_size;
1355 return;
1356 }
1357 if (!is_power_of_2(new_size)){
1358 rnew_size = roundup_pow_of_two(new_size);
1359 }
1360 if (rnew_size != ifp->if_real_bytes) {
1361 ifp->if_u1.if_extents =
1362 kmem_realloc(ifp->if_u1.if_extents,
1363 rnew_size,
1364 ifp->if_real_bytes, KM_NOFS);
1365 }
1366 if (rnew_size > ifp->if_real_bytes) {
1367 memset(&ifp->if_u1.if_extents[ifp->if_bytes /
1368 (uint)sizeof(xfs_bmbt_rec_t)], 0,
1369 rnew_size - ifp->if_real_bytes);
1370 }
1371 }
3e23516a 1372 /* Switch from the inline extent buffer to a direct extent list */
5d90ab5a 1373 else {
5d90ab5a
DC
1374 if (!is_power_of_2(new_size)) {
1375 rnew_size = roundup_pow_of_two(new_size);
1376 }
1377 xfs_iext_inline_to_direct(ifp, rnew_size);
1378 }
1379 ifp->if_real_bytes = rnew_size;
1380 ifp->if_bytes = new_size;
1381}
1382
1383/*
1384 * Switch from linear (direct) extent records to inline buffer.
1385 */
1386void
1387xfs_iext_direct_to_inline(
1388 xfs_ifork_t *ifp, /* inode fork pointer */
1389 xfs_extnum_t nextents) /* number of extents in file */
1390{
1391 ASSERT(ifp->if_flags & XFS_IFEXTENTS);
1392 ASSERT(nextents <= XFS_INLINE_EXTS);
1393 /*
1394 * The inline buffer was zeroed when we switched
1395 * from inline to direct extent allocation mode,
1396 * so we don't need to clear it here.
1397 */
1398 memcpy(ifp->if_u2.if_inline_ext, ifp->if_u1.if_extents,
1399 nextents * sizeof(xfs_bmbt_rec_t));
1400 kmem_free(ifp->if_u1.if_extents);
1401 ifp->if_u1.if_extents = ifp->if_u2.if_inline_ext;
1402 ifp->if_real_bytes = 0;
1403}
1404
1405/*
1406 * Switch from inline buffer to linear (direct) extent records.
1407 * new_size should already be rounded up to the next power of 2
1408 * by the caller (when appropriate), so use new_size as it is.
1409 * However, since new_size may be rounded up, we can't update
1410 * if_bytes here. It is the caller's responsibility to update
1411 * if_bytes upon return.
1412 */
1413void
1414xfs_iext_inline_to_direct(
1415 xfs_ifork_t *ifp, /* inode fork pointer */
1416 int new_size) /* number of extents in file */
1417{
1418 ifp->if_u1.if_extents = kmem_alloc(new_size, KM_NOFS);
1419 memset(ifp->if_u1.if_extents, 0, new_size);
1420 if (ifp->if_bytes) {
1421 memcpy(ifp->if_u1.if_extents, ifp->if_u2.if_inline_ext,
1422 ifp->if_bytes);
1423 memset(ifp->if_u2.if_inline_ext, 0, XFS_INLINE_EXTS *
1424 sizeof(xfs_bmbt_rec_t));
1425 }
1426 ifp->if_real_bytes = new_size;
1427}
1428
1429/*
1430 * Resize an extent indirection array to new_size bytes.
1431 */
1432STATIC void
1433xfs_iext_realloc_indirect(
1434 xfs_ifork_t *ifp, /* inode fork pointer */
1435 int new_size) /* new indirection array size */
1436{
1437 int nlists; /* number of irec's (ex lists) */
1438 int size; /* current indirection array size */
1439
1440 ASSERT(ifp->if_flags & XFS_IFEXTIREC);
1441 nlists = ifp->if_real_bytes / XFS_IEXT_BUFSZ;
1442 size = nlists * sizeof(xfs_ext_irec_t);
1443 ASSERT(ifp->if_real_bytes);
1444 ASSERT((new_size >= 0) && (new_size != size));
1445 if (new_size == 0) {
1446 xfs_iext_destroy(ifp);
1447 } else {
1448 ifp->if_u1.if_ext_irec = (xfs_ext_irec_t *)
1449 kmem_realloc(ifp->if_u1.if_ext_irec,
1450 new_size, size, KM_NOFS);
1451 }
1452}
1453
1454/*
1455 * Switch from indirection array to linear (direct) extent allocations.
1456 */
1457STATIC void
1458xfs_iext_indirect_to_direct(
1459 xfs_ifork_t *ifp) /* inode fork pointer */
1460{
1461 xfs_bmbt_rec_host_t *ep; /* extent record pointer */
1462 xfs_extnum_t nextents; /* number of extents in file */
1463 int size; /* size of file extents */
1464
1465 ASSERT(ifp->if_flags & XFS_IFEXTIREC);
1466 nextents = ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t);
1467 ASSERT(nextents <= XFS_LINEAR_EXTS);
1468 size = nextents * sizeof(xfs_bmbt_rec_t);
1469
1470 xfs_iext_irec_compact_pages(ifp);
1471 ASSERT(ifp->if_real_bytes == XFS_IEXT_BUFSZ);
1472
1473 ep = ifp->if_u1.if_ext_irec->er_extbuf;
1474 kmem_free(ifp->if_u1.if_ext_irec);
1475 ifp->if_flags &= ~XFS_IFEXTIREC;
1476 ifp->if_u1.if_extents = ep;
1477 ifp->if_bytes = size;
1478 if (nextents < XFS_LINEAR_EXTS) {
1479 xfs_iext_realloc_direct(ifp, size);
1480 }
1481}
1482
1483/*
1484 * Free incore file extents.
1485 */
1486void
1487xfs_iext_destroy(
1488 xfs_ifork_t *ifp) /* inode fork pointer */
1489{
1490 if (ifp->if_flags & XFS_IFEXTIREC) {
1491 int erp_idx;
1492 int nlists;
1493
1494 nlists = ifp->if_real_bytes / XFS_IEXT_BUFSZ;
1495 for (erp_idx = nlists - 1; erp_idx >= 0 ; erp_idx--) {
1496 xfs_iext_irec_remove(ifp, erp_idx);
1497 }
1498 ifp->if_flags &= ~XFS_IFEXTIREC;
1499 } else if (ifp->if_real_bytes) {
1500 kmem_free(ifp->if_u1.if_extents);
1501 } else if (ifp->if_bytes) {
1502 memset(ifp->if_u2.if_inline_ext, 0, XFS_INLINE_EXTS *
1503 sizeof(xfs_bmbt_rec_t));
1504 }
1505 ifp->if_u1.if_extents = NULL;
1506 ifp->if_real_bytes = 0;
1507 ifp->if_bytes = 0;
1508}
1509
1510/*
1511 * Return a pointer to the extent record for file system block bno.
1512 */
1513xfs_bmbt_rec_host_t * /* pointer to found extent record */
1514xfs_iext_bno_to_ext(
1515 xfs_ifork_t *ifp, /* inode fork pointer */
1516 xfs_fileoff_t bno, /* block number to search for */
1517 xfs_extnum_t *idxp) /* index of target extent */
1518{
1519 xfs_bmbt_rec_host_t *base; /* pointer to first extent */
1520 xfs_filblks_t blockcount = 0; /* number of blocks in extent */
1521 xfs_bmbt_rec_host_t *ep = NULL; /* pointer to target extent */
1522 xfs_ext_irec_t *erp = NULL; /* indirection array pointer */
1523 int high; /* upper boundary in search */
1524 xfs_extnum_t idx = 0; /* index of target extent */
1525 int low; /* lower boundary in search */
1526 xfs_extnum_t nextents; /* number of file extents */
1527 xfs_fileoff_t startoff = 0; /* start offset of extent */
1528
1529 nextents = ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t);
1530 if (nextents == 0) {
1531 *idxp = 0;
1532 return NULL;
1533 }
1534 low = 0;
1535 if (ifp->if_flags & XFS_IFEXTIREC) {
1536 /* Find target extent list */
1537 int erp_idx = 0;
1538 erp = xfs_iext_bno_to_irec(ifp, bno, &erp_idx);
1539 base = erp->er_extbuf;
1540 high = erp->er_extcount - 1;
1541 } else {
1542 base = ifp->if_u1.if_extents;
1543 high = nextents - 1;
1544 }
1545 /* Binary search extent records */
1546 while (low <= high) {
1547 idx = (low + high) >> 1;
1548 ep = base + idx;
1549 startoff = xfs_bmbt_get_startoff(ep);
1550 blockcount = xfs_bmbt_get_blockcount(ep);
1551 if (bno < startoff) {
1552 high = idx - 1;
1553 } else if (bno >= startoff + blockcount) {
1554 low = idx + 1;
1555 } else {
1556 /* Convert back to file-based extent index */
1557 if (ifp->if_flags & XFS_IFEXTIREC) {
1558 idx += erp->er_extoff;
1559 }
1560 *idxp = idx;
1561 return ep;
1562 }
1563 }
1564 /* Convert back to file-based extent index */
1565 if (ifp->if_flags & XFS_IFEXTIREC) {
1566 idx += erp->er_extoff;
1567 }
1568 if (bno >= startoff + blockcount) {
1569 if (++idx == nextents) {
1570 ep = NULL;
1571 } else {
1572 ep = xfs_iext_get_ext(ifp, idx);
1573 }
1574 }
1575 *idxp = idx;
1576 return ep;
1577}
1578
1579/*
1580 * Return a pointer to the indirection array entry containing the
1581 * extent record for filesystem block bno. Store the index of the
1582 * target irec in *erp_idxp.
1583 */
1584xfs_ext_irec_t * /* pointer to found extent record */
1585xfs_iext_bno_to_irec(
1586 xfs_ifork_t *ifp, /* inode fork pointer */
1587 xfs_fileoff_t bno, /* block number to search for */
1588 int *erp_idxp) /* irec index of target ext list */
1589{
1590 xfs_ext_irec_t *erp = NULL; /* indirection array pointer */
1591 xfs_ext_irec_t *erp_next; /* next indirection array entry */
1592 int erp_idx; /* indirection array index */
1593 int nlists; /* number of extent irec's (lists) */
1594 int high; /* binary search upper limit */
1595 int low; /* binary search lower limit */
1596
1597 ASSERT(ifp->if_flags & XFS_IFEXTIREC);
1598 nlists = ifp->if_real_bytes / XFS_IEXT_BUFSZ;
1599 erp_idx = 0;
1600 low = 0;
1601 high = nlists - 1;
1602 while (low <= high) {
1603 erp_idx = (low + high) >> 1;
1604 erp = &ifp->if_u1.if_ext_irec[erp_idx];
1605 erp_next = erp_idx < nlists - 1 ? erp + 1 : NULL;
1606 if (bno < xfs_bmbt_get_startoff(erp->er_extbuf)) {
1607 high = erp_idx - 1;
1608 } else if (erp_next && bno >=
1609 xfs_bmbt_get_startoff(erp_next->er_extbuf)) {
1610 low = erp_idx + 1;
1611 } else {
1612 break;
1613 }
1614 }
1615 *erp_idxp = erp_idx;
1616 return erp;
1617}
1618
1619/*
1620 * Return a pointer to the indirection array entry containing the
1621 * extent record at file extent index *idxp. Store the index of the
1622 * target irec in *erp_idxp and store the page index of the target
1623 * extent record in *idxp.
1624 */
1625xfs_ext_irec_t *
1626xfs_iext_idx_to_irec(
1627 xfs_ifork_t *ifp, /* inode fork pointer */
1628 xfs_extnum_t *idxp, /* extent index (file -> page) */
1629 int *erp_idxp, /* pointer to target irec */
1630 int realloc) /* new bytes were just added */
1631{
1632 xfs_ext_irec_t *prev; /* pointer to previous irec */
1633 xfs_ext_irec_t *erp = NULL; /* pointer to current irec */
1634 int erp_idx; /* indirection array index */
1635 int nlists; /* number of irec's (ex lists) */
1636 int high; /* binary search upper limit */
1637 int low; /* binary search lower limit */
1638 xfs_extnum_t page_idx = *idxp; /* extent index in target list */
1639
1640 ASSERT(ifp->if_flags & XFS_IFEXTIREC);
1641 ASSERT(page_idx >= 0);
1642 ASSERT(page_idx <= ifp->if_bytes / sizeof(xfs_bmbt_rec_t));
1643 ASSERT(page_idx < ifp->if_bytes / sizeof(xfs_bmbt_rec_t) || realloc);
1644
1645 nlists = ifp->if_real_bytes / XFS_IEXT_BUFSZ;
1646 erp_idx = 0;
1647 low = 0;
1648 high = nlists - 1;
1649
1650 /* Binary search extent irec's */
1651 while (low <= high) {
1652 erp_idx = (low + high) >> 1;
1653 erp = &ifp->if_u1.if_ext_irec[erp_idx];
1654 prev = erp_idx > 0 ? erp - 1 : NULL;
1655 if (page_idx < erp->er_extoff || (page_idx == erp->er_extoff &&
1656 realloc && prev && prev->er_extcount < XFS_LINEAR_EXTS)) {
1657 high = erp_idx - 1;
1658 } else if (page_idx > erp->er_extoff + erp->er_extcount ||
1659 (page_idx == erp->er_extoff + erp->er_extcount &&
1660 !realloc)) {
1661 low = erp_idx + 1;
1662 } else if (page_idx == erp->er_extoff + erp->er_extcount &&
1663 erp->er_extcount == XFS_LINEAR_EXTS) {
1664 ASSERT(realloc);
1665 page_idx = 0;
1666 erp_idx++;
1667 erp = erp_idx < nlists ? erp + 1 : NULL;
1668 break;
1669 } else {
1670 page_idx -= erp->er_extoff;
1671 break;
1672 }
1673 }
1674 *idxp = page_idx;
1675 *erp_idxp = erp_idx;
af43ca9f 1676 return erp;
5d90ab5a
DC
1677}
1678
1679/*
1680 * Allocate and initialize an indirection array once the space needed
1681 * for incore extents increases above XFS_IEXT_BUFSZ.
1682 */
1683void
1684xfs_iext_irec_init(
1685 xfs_ifork_t *ifp) /* inode fork pointer */
1686{
1687 xfs_ext_irec_t *erp; /* indirection array pointer */
1688 xfs_extnum_t nextents; /* number of extents in file */
1689
1690 ASSERT(!(ifp->if_flags & XFS_IFEXTIREC));
1691 nextents = ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t);
1692 ASSERT(nextents <= XFS_LINEAR_EXTS);
1693
1694 erp = kmem_alloc(sizeof(xfs_ext_irec_t), KM_NOFS);
1695
1696 if (nextents == 0) {
1697 ifp->if_u1.if_extents = kmem_alloc(XFS_IEXT_BUFSZ, KM_NOFS);
1698 } else if (!ifp->if_real_bytes) {
1699 xfs_iext_inline_to_direct(ifp, XFS_IEXT_BUFSZ);
1700 } else if (ifp->if_real_bytes < XFS_IEXT_BUFSZ) {
1701 xfs_iext_realloc_direct(ifp, XFS_IEXT_BUFSZ);
1702 }
1703 erp->er_extbuf = ifp->if_u1.if_extents;
1704 erp->er_extcount = nextents;
1705 erp->er_extoff = 0;
1706
1707 ifp->if_flags |= XFS_IFEXTIREC;
1708 ifp->if_real_bytes = XFS_IEXT_BUFSZ;
1709 ifp->if_bytes = nextents * sizeof(xfs_bmbt_rec_t);
1710 ifp->if_u1.if_ext_irec = erp;
1711
1712 return;
1713}
1714
1715/*
1716 * Allocate and initialize a new entry in the indirection array.
1717 */
1718xfs_ext_irec_t *
1719xfs_iext_irec_new(
1720 xfs_ifork_t *ifp, /* inode fork pointer */
1721 int erp_idx) /* index for new irec */
1722{
1723 xfs_ext_irec_t *erp; /* indirection array pointer */
1724 int i; /* loop counter */
1725 int nlists; /* number of irec's (ex lists) */
1726
1727 ASSERT(ifp->if_flags & XFS_IFEXTIREC);
1728 nlists = ifp->if_real_bytes / XFS_IEXT_BUFSZ;
1729
1730 /* Resize indirection array */
1731 xfs_iext_realloc_indirect(ifp, ++nlists *
1732 sizeof(xfs_ext_irec_t));
1733 /*
1734 * Move records down in the array so the
1735 * new page can use erp_idx.
1736 */
1737 erp = ifp->if_u1.if_ext_irec;
1738 for (i = nlists - 1; i > erp_idx; i--) {
1739 memmove(&erp[i], &erp[i-1], sizeof(xfs_ext_irec_t));
1740 }
1741 ASSERT(i == erp_idx);
1742
1743 /* Initialize new extent record */
1744 erp = ifp->if_u1.if_ext_irec;
1745 erp[erp_idx].er_extbuf = kmem_alloc(XFS_IEXT_BUFSZ, KM_NOFS);
1746 ifp->if_real_bytes = nlists * XFS_IEXT_BUFSZ;
1747 memset(erp[erp_idx].er_extbuf, 0, XFS_IEXT_BUFSZ);
1748 erp[erp_idx].er_extcount = 0;
1749 erp[erp_idx].er_extoff = erp_idx > 0 ?
1750 erp[erp_idx-1].er_extoff + erp[erp_idx-1].er_extcount : 0;
1751 return (&erp[erp_idx]);
1752}
1753
1754/*
1755 * Remove a record from the indirection array.
1756 */
1757void
1758xfs_iext_irec_remove(
1759 xfs_ifork_t *ifp, /* inode fork pointer */
1760 int erp_idx) /* irec index to remove */
1761{
1762 xfs_ext_irec_t *erp; /* indirection array pointer */
1763 int i; /* loop counter */
1764 int nlists; /* number of irec's (ex lists) */
1765
1766 ASSERT(ifp->if_flags & XFS_IFEXTIREC);
1767 nlists = ifp->if_real_bytes / XFS_IEXT_BUFSZ;
1768 erp = &ifp->if_u1.if_ext_irec[erp_idx];
1769 if (erp->er_extbuf) {
1770 xfs_iext_irec_update_extoffs(ifp, erp_idx + 1,
1771 -erp->er_extcount);
1772 kmem_free(erp->er_extbuf);
1773 }
1774 /* Compact extent records */
1775 erp = ifp->if_u1.if_ext_irec;
1776 for (i = erp_idx; i < nlists - 1; i++) {
1777 memmove(&erp[i], &erp[i+1], sizeof(xfs_ext_irec_t));
1778 }
1779 /*
1780 * Manually free the last extent record from the indirection
1781 * array. A call to xfs_iext_realloc_indirect() with a size
1782 * of zero would result in a call to xfs_iext_destroy() which
1783 * would in turn call this function again, creating a nasty
1784 * infinite loop.
1785 */
1786 if (--nlists) {
1787 xfs_iext_realloc_indirect(ifp,
1788 nlists * sizeof(xfs_ext_irec_t));
1789 } else {
1790 kmem_free(ifp->if_u1.if_ext_irec);
1791 }
1792 ifp->if_real_bytes = nlists * XFS_IEXT_BUFSZ;
1793}
1794
1795/*
1796 * This is called to clean up large amounts of unused memory allocated
1797 * by the indirection array. Before compacting anything though, verify
1798 * that the indirection array is still needed and switch back to the
1799 * linear extent list (or even the inline buffer) if possible. The
1800 * compaction policy is as follows:
1801 *
1802 * Full Compaction: Extents fit into a single page (or inline buffer)
1803 * Partial Compaction: Extents occupy less than 50% of allocated space
1804 * No Compaction: Extents occupy at least 50% of allocated space
1805 */
1806void
1807xfs_iext_irec_compact(
1808 xfs_ifork_t *ifp) /* inode fork pointer */
1809{
1810 xfs_extnum_t nextents; /* number of extents in file */
1811 int nlists; /* number of irec's (ex lists) */
1812
1813 ASSERT(ifp->if_flags & XFS_IFEXTIREC);
1814 nlists = ifp->if_real_bytes / XFS_IEXT_BUFSZ;
1815 nextents = ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t);
1816
1817 if (nextents == 0) {
1818 xfs_iext_destroy(ifp);
1819 } else if (nextents <= XFS_INLINE_EXTS) {
1820 xfs_iext_indirect_to_direct(ifp);
1821 xfs_iext_direct_to_inline(ifp, nextents);
1822 } else if (nextents <= XFS_LINEAR_EXTS) {
1823 xfs_iext_indirect_to_direct(ifp);
1824 } else if (nextents < (nlists * XFS_LINEAR_EXTS) >> 1) {
1825 xfs_iext_irec_compact_pages(ifp);
1826 }
1827}
1828
1829/*
1830 * Combine extents from neighboring extent pages.
1831 */
1832void
1833xfs_iext_irec_compact_pages(
1834 xfs_ifork_t *ifp) /* inode fork pointer */
1835{
1836 xfs_ext_irec_t *erp, *erp_next;/* pointers to irec entries */
1837 int erp_idx = 0; /* indirection array index */
1838 int nlists; /* number of irec's (ex lists) */
1839
1840 ASSERT(ifp->if_flags & XFS_IFEXTIREC);
1841 nlists = ifp->if_real_bytes / XFS_IEXT_BUFSZ;
1842 while (erp_idx < nlists - 1) {
1843 erp = &ifp->if_u1.if_ext_irec[erp_idx];
1844 erp_next = erp + 1;
1845 if (erp_next->er_extcount <=
1846 (XFS_LINEAR_EXTS - erp->er_extcount)) {
1847 memcpy(&erp->er_extbuf[erp->er_extcount],
1848 erp_next->er_extbuf, erp_next->er_extcount *
1849 sizeof(xfs_bmbt_rec_t));
1850 erp->er_extcount += erp_next->er_extcount;
1851 /*
1852 * Free page before removing extent record
1853 * so er_extoffs don't get modified in
1854 * xfs_iext_irec_remove.
1855 */
1856 kmem_free(erp_next->er_extbuf);
1857 erp_next->er_extbuf = NULL;
1858 xfs_iext_irec_remove(ifp, erp_idx + 1);
1859 nlists = ifp->if_real_bytes / XFS_IEXT_BUFSZ;
1860 } else {
1861 erp_idx++;
1862 }
1863 }
1864}
1865
1866/*
1867 * This is called to update the er_extoff field in the indirection
1868 * array when extents have been added or removed from one of the
1869 * extent lists. erp_idx contains the irec index to begin updating
1870 * at and ext_diff contains the number of extents that were added
1871 * or removed.
1872 */
1873void
1874xfs_iext_irec_update_extoffs(
1875 xfs_ifork_t *ifp, /* inode fork pointer */
1876 int erp_idx, /* irec index to update */
1877 int ext_diff) /* number of new extents */
1878{
1879 int i; /* loop counter */
1880 int nlists; /* number of irec's (ex lists */
1881
1882 ASSERT(ifp->if_flags & XFS_IFEXTIREC);
1883 nlists = ifp->if_real_bytes / XFS_IEXT_BUFSZ;
1884 for (i = erp_idx; i < nlists; i++) {
1885 ifp->if_u1.if_ext_irec[i].er_extoff += ext_diff;
1886 }
1887}