1 /* $NetBSD: v7fs_datablock.c,v 1.5 2011/08/14 09:02:07 apb Exp $ */
4 * Copyright (c) 2011 The NetBSD Foundation, Inc.
7 * This code is derived from software contributed to The NetBSD Foundation
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
32 #if HAVE_NBTOOL_CONFIG_H
33 #include "nbtool_config.h"
36 #include <sys/cdefs.h>
37 __KERNEL_RCSID(0, "$NetBSD: v7fs_datablock.c,v 1.5 2011/08/14 09:02:07 apb Exp $");
38 #if defined _KERNEL_OPT
42 #include <sys/types.h>
44 #include <sys/systm.h>
45 #include <sys/param.h>
53 #include "v7fs_impl.h"
54 #include "v7fs_endian.h"
55 #include "v7fs_inode.h"
56 #include "v7fs_datablock.h"
57 #include "v7fs_superblock.h"
59 #ifdef V7FS_DATABLOCK_DEBUG
60 #define DPRINTF(fmt, args...) printf("%s: " fmt, __func__, ##args)
62 #define DPRINTF(fmt, args...) ((void)0)
65 static int v7fs_datablock_deallocate(struct v7fs_self
*, v7fs_daddr_t
);
66 static int v7fs_loop1(struct v7fs_self
*, v7fs_daddr_t
, size_t *,
67 int (*)(struct v7fs_self
*, void *, v7fs_daddr_t
, size_t), void *);
68 static int v7fs_loop2(struct v7fs_self
*, v7fs_daddr_t
, size_t *,
69 int (*)(struct v7fs_self
*, void *, v7fs_daddr_t
, size_t), void *);
70 static v7fs_daddr_t
v7fs_link(struct v7fs_self
*, v7fs_daddr_t
, int);
71 static v7fs_daddr_t
v7fs_add_leaf(struct v7fs_self
*, v7fs_daddr_t
, int);
72 static v7fs_daddr_t
v7fs_unlink(struct v7fs_self
*, v7fs_daddr_t
, int);
73 static v7fs_daddr_t
v7fs_remove_leaf(struct v7fs_self
*, v7fs_daddr_t
, int);
74 static v7fs_daddr_t
v7fs_remove_self(struct v7fs_self
*, v7fs_daddr_t
);
76 #ifdef V7FS_DATABLOCK_DEBUG
77 void daddr_map_dump(const struct v7fs_daddr_map
*);
79 #define daddr_map_dump(x) ((void)0)
83 datablock_number_sanity(const struct v7fs_self
*fs
, v7fs_daddr_t blk
)
85 const struct v7fs_superblock
*sb
= &fs
->superblock
;
86 bool ok
= (blk
>= sb
->datablock_start_sector
) &&
87 (blk
< sb
->volume_size
);
89 #ifdef V7FS_DATABLOCK_DEBUG
91 DPRINTF("Bad data block #%d\n", blk
);
99 v7fs_datablock_allocate(struct v7fs_self
*fs
, v7fs_daddr_t
*block_number
)
101 struct v7fs_superblock
*sb
= &fs
->superblock
;
108 if (!sb
->total_freeblock
) {
109 DPRINTF("free block exhausted!!!\n");
114 /* Get free block from superblock cache. */
115 blk
= sb
->freeblock
[--sb
->nfreeblock
];
116 sb
->total_freeblock
--;
119 /* If nfreeblock is zero, it block is next freeblock link. */
120 if (sb
->nfreeblock
== 0) {
121 if ((error
= v7fs_freeblock_update(fs
, blk
))) {
122 DPRINTF("no freeblock!!!\n");
126 /* This freeblock link is no longer required. */
127 /* use as data block. */
129 } while (!datablock_number_sanity(fs
, blk
)); /* skip bogus block. */
132 DPRINTF("Get freeblock %d\n", blk
);
133 /* Zero clear datablock. */
135 if (!(buf
= scratch_read(fs
, blk
)))
137 memset(buf
, 0, V7FS_BSIZE
);
138 if (!fs
->io
.write(fs
->io
.cookie
, buf
, blk
))
140 scratch_free(fs
, buf
);
149 v7fs_datablock_deallocate(struct v7fs_self
*fs
, v7fs_daddr_t blk
)
151 struct v7fs_superblock
*sb
= &fs
->superblock
;
155 if (!datablock_number_sanity(fs
, blk
))
158 /* Add to in-core freelist. */
160 if (sb
->nfreeblock
< V7FS_MAX_FREEBLOCK
) {
161 sb
->freeblock
[sb
->nfreeblock
++] = blk
;
162 sb
->total_freeblock
++;
164 DPRINTF("n_freeblock=%d\n", sb
->total_freeblock
);
169 /* No space to push. */
170 /* Make this block to freeblock list.and current cache moved to this. */
171 if (!(buf
= scratch_read(fs
, blk
))) {
173 return EIO
; /* Fatal */
176 struct v7fs_freeblock
*fb
= (struct v7fs_freeblock
*)buf
;
177 fb
->nfreeblock
= V7FS_MAX_FREEBLOCK
;
179 for (i
= 0; i
< V7FS_MAX_FREEBLOCK
; i
++)
180 fb
->freeblock
[i
] = V7FS_VAL32(fs
, sb
->freeblock
[i
]);
182 if (!fs
->io
.write(fs
->io
.cookie
, (uint8_t *)fb
, blk
)) {
183 error
= EIO
; /* Fatal */
185 /* Link. on next allocate, this block is used as datablock, */
186 /* and swap outed freeblock list is restored. */
187 sb
->freeblock
[0] = blk
;
189 sb
->total_freeblock
++;
191 DPRINTF("n_freeblock=%d\n", sb
->total_freeblock
);
194 scratch_free(fs
, buf
);
200 v7fs_datablock_addr(size_t sz
, struct v7fs_daddr_map
*map
)
202 #define NIDX V7FS_DADDR_PER_BLOCK
203 #define DIRECT_SZ (V7FS_NADDR_DIRECT * V7FS_BSIZE)
204 #define IDX1_SZ (NIDX * V7FS_BSIZE)
205 #define IDX2_SZ (NIDX * NIDX * V7FS_BSIZE)
206 #define ROUND(x, a) ((((x) + ((a) - 1)) & ~((a) - 1)))
213 sz
= V7FS_ROUND_BSIZE(sz
);
216 if (sz
<= DIRECT_SZ
) {
218 map
->index
[0] = (sz
>> V7FS_BSHIFT
) - 1;
226 map
->index
[0] = (sz
>> V7FS_BSHIFT
) - 1;
234 map
->index
[0] = ROUND(sz
, IDX1_SZ
) / IDX1_SZ
- 1;
235 map
->index
[1] = ((sz
- (map
->index
[0] * IDX1_SZ
)) >>
243 map
->index
[0] = ROUND(sz
, IDX2_SZ
) / IDX2_SZ
- 1;
244 sz
-= map
->index
[0] * IDX2_SZ
;
245 map
->index
[1] = ROUND(sz
, IDX1_SZ
) / IDX1_SZ
- 1;
246 sz
-= map
->index
[1] * IDX1_SZ
;
247 map
->index
[2] = (sz
>> V7FS_BSHIFT
) - 1;
249 return map
->index
[2] >= NIDX
? ENOSPC
: 0;
253 v7fs_datablock_foreach(struct v7fs_self
*fs
, struct v7fs_inode
*p
,
254 int (*func
)(struct v7fs_self
*, void *, v7fs_daddr_t
, size_t), void *ctx
)
257 v7fs_daddr_t blk
, blk2
;
262 if (!(filesize
= v7fs_inode_filesize(p
)))
263 return V7FS_ITERATOR_END
;
264 #ifdef V7FS_DATABLOCK_DEBUG
265 size_t sz
= filesize
;
269 for (i
= 0; i
< V7FS_NADDR_DIRECT
; i
++, filesize
-= V7FS_BSIZE
) {
271 if (!datablock_number_sanity(fs
, blk
)) {
272 DPRINTF("inode#%d direct=%zu filesize=%zu\n",
273 p
->inode_number
, i
, sz
);
277 last
= filesize
<= V7FS_BSIZE
;
278 if ((ret
= func(fs
, ctx
, blk
, last
? filesize
: V7FS_BSIZE
)))
281 return V7FS_ITERATOR_END
;
285 blk
= p
->addr
[V7FS_NADDR_INDEX1
];
286 if (!datablock_number_sanity(fs
, blk
))
289 if ((ret
= v7fs_loop1(fs
, blk
, &filesize
, func
, ctx
)))
293 blk
= p
->addr
[V7FS_NADDR_INDEX2
];
294 if (!datablock_number_sanity(fs
, blk
))
297 if ((ret
= v7fs_loop2(fs
, blk
, &filesize
, func
, ctx
)))
301 blk
= p
->addr
[V7FS_NADDR_INDEX3
];
302 if (!datablock_number_sanity(fs
, blk
))
305 for (i
= 0; i
< V7FS_DADDR_PER_BLOCK
; i
++) {
306 blk2
= v7fs_link(fs
, blk
, i
);
307 if (!datablock_number_sanity(fs
, blk
))
310 if ((ret
= v7fs_loop2(fs
, blk2
, &filesize
, func
, ctx
)))
318 v7fs_loop2(struct v7fs_self
*fs
, v7fs_daddr_t listblk
, size_t *filesize
,
319 int (*func
)(struct v7fs_self
*, void *, v7fs_daddr_t
, size_t), void *ctx
)
325 for (j
= 0; j
< V7FS_DADDR_PER_BLOCK
; j
++) {
326 blk
= v7fs_link(fs
, listblk
, j
);
327 if (!datablock_number_sanity(fs
, blk
))
329 if ((ret
= v7fs_loop1(fs
, blk
, filesize
, func
, ctx
)))
337 v7fs_loop1(struct v7fs_self
*fs
, v7fs_daddr_t listblk
, size_t *filesize
,
338 int (*func
)(struct v7fs_self
*, void *, v7fs_daddr_t
, size_t), void *ctx
)
345 for (k
= 0; k
< V7FS_DADDR_PER_BLOCK
; k
++, *filesize
-= V7FS_BSIZE
) {
346 blk
= v7fs_link(fs
, listblk
, k
);
347 if (!datablock_number_sanity(fs
, blk
))
349 last
= *filesize
<= V7FS_BSIZE
;
350 if ((ret
= func(fs
, ctx
, blk
, last
? *filesize
: V7FS_BSIZE
)))
353 return V7FS_ITERATOR_END
;
360 v7fs_datablock_last(struct v7fs_self
*fs
, struct v7fs_inode
*inode
,
363 struct v7fs_daddr_map map
;
364 v7fs_daddr_t blk
= 0;
365 v7fs_daddr_t
*addr
= inode
->addr
;
367 /* Inquire last data block location. */
368 if (v7fs_datablock_addr(ofs
, &map
) != 0)
374 blk
= inode
->addr
[map
.index
[0]];
377 blk
= v7fs_link(fs
, addr
[V7FS_NADDR_INDEX1
], map
.index
[0]);
380 blk
= v7fs_link(fs
, v7fs_link(fs
,
381 addr
[V7FS_NADDR_INDEX2
], map
.index
[0]), map
.index
[1]);
384 blk
= v7fs_link(fs
, v7fs_link(fs
, v7fs_link(fs
,
385 addr
[V7FS_NADDR_INDEX3
], map
.index
[0]), map
.index
[1]),
394 v7fs_datablock_expand(struct v7fs_self
*fs
, struct v7fs_inode
*inode
, size_t sz
)
396 size_t old_filesize
= inode
->filesize
;
397 size_t new_filesize
= old_filesize
+ sz
;
398 struct v7fs_daddr_map oldmap
, newmap
;
399 v7fs_daddr_t blk
, idxblk
;
401 v7fs_daddr_t old_nblk
= V7FS_ROUND_BSIZE(old_filesize
) >> V7FS_BSHIFT
;
402 v7fs_daddr_t new_nblk
= V7FS_ROUND_BSIZE(new_filesize
) >> V7FS_BSHIFT
;
404 if (old_nblk
== new_nblk
) {
405 inode
->filesize
+= sz
;
406 v7fs_inode_writeback(fs
, inode
);
407 return 0; /* no need to expand. */
409 struct v7fs_inode backup
= *inode
;
410 v7fs_daddr_t required_blk
= new_nblk
- old_nblk
;
412 DPRINTF("%zu->%zu, required block=%d\n", old_filesize
, new_filesize
,
415 v7fs_datablock_addr(old_filesize
, &oldmap
);
417 for (i
= 0; i
< required_blk
; i
++) {
418 v7fs_datablock_addr(old_filesize
+ (i
+1) * V7FS_BSIZE
, &newmap
);
419 daddr_map_dump(&oldmap
);
420 daddr_map_dump(&newmap
);
422 if (oldmap
.level
!= newmap
.level
) {
423 /* Allocate index area */
424 if ((error
= v7fs_datablock_allocate(fs
, &idxblk
)))
427 switch (newmap
.level
) {
430 inode
->addr
[V7FS_NADDR_INDEX1
] = idxblk
;
431 blk
= v7fs_add_leaf(fs
, idxblk
, 0);
435 inode
->addr
[V7FS_NADDR_INDEX2
] = idxblk
;
436 blk
= v7fs_add_leaf(fs
, v7fs_add_leaf(fs
,
441 inode
->addr
[V7FS_NADDR_INDEX3
] = idxblk
;
442 blk
= v7fs_add_leaf(fs
, v7fs_add_leaf(fs
,
443 v7fs_add_leaf(fs
, idxblk
, 0), 0), 0);
447 switch (newmap
.level
) {
449 if ((error
= v7fs_datablock_allocate(fs
, &blk
)))
451 inode
->addr
[newmap
.index
[0]] = blk
;
452 DPRINTF("direct index %d = blk%d\n",
453 newmap
.index
[0], blk
);
456 idxblk
= inode
->addr
[V7FS_NADDR_INDEX1
];
457 blk
= v7fs_add_leaf(fs
, idxblk
,
461 idxblk
= inode
->addr
[V7FS_NADDR_INDEX2
];
462 if (oldmap
.index
[0] != newmap
.index
[0]) {
463 v7fs_add_leaf(fs
, idxblk
,
466 blk
= v7fs_add_leaf(fs
, v7fs_link(fs
,idxblk
,
467 newmap
.index
[0]), newmap
.index
[1]);
470 idxblk
= inode
->addr
[V7FS_NADDR_INDEX3
];
472 if (oldmap
.index
[0] != newmap
.index
[0]) {
473 v7fs_add_leaf(fs
, idxblk
,
477 if (oldmap
.index
[1] != newmap
.index
[1]) {
478 v7fs_add_leaf(fs
, v7fs_link(fs
, idxblk
,
479 newmap
.index
[0]), newmap
.index
[1]);
481 blk
= v7fs_add_leaf(fs
, v7fs_link(fs
,
482 v7fs_link(fs
, idxblk
, newmap
.index
[0]),
483 newmap
.index
[1]), newmap
.index
[2]);
488 *inode
= backup
; /* structure copy; */
493 inode
->filesize
+= sz
;
494 v7fs_inode_writeback(fs
, inode
);
500 v7fs_link(struct v7fs_self
*fs
, v7fs_daddr_t listblk
, int n
)
506 if (!datablock_number_sanity(fs
, listblk
))
508 if (!(buf
= scratch_read(fs
, listblk
)))
510 list
= (v7fs_daddr_t
*)buf
;
511 blk
= V7FS_VAL32(fs
, list
[n
]);
512 scratch_free(fs
, buf
);
514 if (!datablock_number_sanity(fs
, blk
))
521 v7fs_add_leaf(struct v7fs_self
*fs
, v7fs_daddr_t up
, int idx
)
524 v7fs_daddr_t
*daddr_list
;
530 if (!datablock_number_sanity(fs
, up
))
533 if ((error
= v7fs_datablock_allocate(fs
, &newblk
)))
535 if (!(buf
= scratch_read(fs
, up
)))
537 daddr_list
= (v7fs_daddr_t
*)buf
;
538 daddr_list
[idx
] = V7FS_VAL32(fs
, newblk
);
539 if (!fs
->io
.write(fs
->io
.cookie
, buf
, up
))
541 scratch_free(fs
, buf
);
547 v7fs_datablock_contract(struct v7fs_self
*fs
, struct v7fs_inode
*inode
,
550 size_t old_filesize
= inode
->filesize
;
551 size_t new_filesize
= old_filesize
- sz
;
552 struct v7fs_daddr_map oldmap
, newmap
;
553 v7fs_daddr_t blk
, idxblk
;
555 v7fs_daddr_t old_nblk
= V7FS_ROUND_BSIZE(old_filesize
) >> V7FS_BSHIFT
;
556 v7fs_daddr_t new_nblk
= V7FS_ROUND_BSIZE(new_filesize
) >> V7FS_BSHIFT
;
558 if (old_nblk
== new_nblk
) {
559 inode
->filesize
-= sz
;
560 v7fs_inode_writeback(fs
, inode
);
561 return 0; /* no need to contract; */
563 v7fs_daddr_t erase_blk
= old_nblk
- new_nblk
;
565 DPRINTF("%zu->%zu # of erased block=%d\n", old_filesize
, new_filesize
,
568 v7fs_datablock_addr(old_filesize
, &oldmap
);
570 for (i
= 0; i
< erase_blk
; i
++) {
571 v7fs_datablock_addr(old_filesize
- (i
+1) * V7FS_BSIZE
, &newmap
);
573 if (oldmap
.level
!= newmap
.level
) {
574 switch (newmap
.level
) {
577 idxblk
= inode
->addr
[V7FS_NADDR_INDEX1
];
578 inode
->addr
[V7FS_NADDR_INDEX1
] = 0;
579 error
= v7fs_datablock_deallocate(fs
,
580 v7fs_remove_self(fs
, idxblk
));
584 idxblk
= inode
->addr
[V7FS_NADDR_INDEX2
];
585 inode
->addr
[V7FS_NADDR_INDEX2
] = 0;
586 error
= v7fs_datablock_deallocate(fs
,
587 v7fs_remove_self(fs
, v7fs_remove_self(fs
,
592 idxblk
= inode
->addr
[V7FS_NADDR_INDEX3
];
593 inode
->addr
[V7FS_NADDR_INDEX3
] = 0;
594 error
= v7fs_datablock_deallocate(fs
,
595 v7fs_remove_self(fs
, v7fs_remove_self(fs
,
596 v7fs_remove_self(fs
, idxblk
))));
600 switch (newmap
.level
) {
602 DPRINTF("[0] %d\n", oldmap
.index
[0]);
603 blk
= inode
->addr
[oldmap
.index
[0]];
604 error
= v7fs_datablock_deallocate(fs
, blk
);
607 DPRINTF("[1] %d\n", oldmap
.index
[0]);
608 idxblk
= inode
->addr
[V7FS_NADDR_INDEX1
];
609 v7fs_remove_leaf(fs
, idxblk
, oldmap
.index
[0]);
613 DPRINTF("[2] %d %d\n", oldmap
.index
[0],
615 idxblk
= inode
->addr
[V7FS_NADDR_INDEX2
];
616 v7fs_remove_leaf(fs
, v7fs_link(fs
, idxblk
,
617 oldmap
.index
[0]), oldmap
.index
[1]);
618 if (oldmap
.index
[0] != newmap
.index
[0]) {
619 v7fs_remove_leaf(fs
, idxblk
,
624 DPRINTF("[2] %d %d %d\n", oldmap
.index
[0],
625 oldmap
.index
[1], oldmap
.index
[2]);
626 idxblk
= inode
->addr
[V7FS_NADDR_INDEX3
];
627 v7fs_remove_leaf(fs
, v7fs_link(fs
,
628 v7fs_link(fs
, idxblk
, oldmap
.index
[0]),
629 oldmap
.index
[1]), oldmap
.index
[2]);
631 if (oldmap
.index
[1] != newmap
.index
[1]) {
632 v7fs_remove_leaf(fs
, v7fs_link(fs
,
633 idxblk
, oldmap
.index
[0]),
636 if (oldmap
.index
[0] != newmap
.index
[0]) {
637 v7fs_remove_leaf(fs
, idxblk
,
645 inode
->filesize
-= sz
;
646 v7fs_inode_writeback(fs
, inode
);
652 v7fs_unlink(struct v7fs_self
*fs
, v7fs_daddr_t idxblk
, int n
)
654 v7fs_daddr_t
*daddr_list
;
658 if (!(buf
= scratch_read(fs
, idxblk
)))
660 daddr_list
= (v7fs_daddr_t
*)buf
;
661 blk
= V7FS_VAL32(fs
, daddr_list
[n
]);
663 fs
->io
.write(fs
->io
.cookie
, buf
, idxblk
);
664 scratch_free(fs
, buf
);
666 return blk
; /* unlinked block. */
670 v7fs_remove_self(struct v7fs_self
*fs
, v7fs_daddr_t up
)
674 if (!datablock_number_sanity(fs
, up
))
677 /* At 1st, remove from datablock list. */
678 down
= v7fs_unlink(fs
, up
, 0);
680 /* link self to freelist. */
681 v7fs_datablock_deallocate(fs
, up
);
687 v7fs_remove_leaf(struct v7fs_self
*fs
, v7fs_daddr_t up
, int n
)
691 if (!datablock_number_sanity(fs
, up
))
694 /* At 1st, remove from datablock list. */
695 down
= v7fs_unlink(fs
, up
, n
);
697 /* link leaf to freelist. */
698 v7fs_datablock_deallocate(fs
, down
);
704 v7fs_datablock_size_change(struct v7fs_self
*fs
, size_t newsz
,
705 struct v7fs_inode
*inode
)
707 ssize_t diff
= newsz
- v7fs_inode_filesize(inode
);
711 error
= v7fs_datablock_expand(fs
, inode
, diff
);
713 error
= v7fs_datablock_contract(fs
, inode
, -diff
);
718 #ifdef V7FS_DATABLOCK_DEBUG
720 daddr_map_dump(const struct v7fs_daddr_map
*map
)
723 DPRINTF("level %d ", map
->level
);
724 int m
, n
= !map
->level
? 1 : map
->level
;
725 for (m
= 0; m
< n
; m
++)
726 printf("[%d]", map
->index
[m
]);