1 /*        $NetBSD: v7fs_datablock.c,v 1.5 2011/08/14 09:02:07 apb Exp $         */
2 
3 /*-
4  * Copyright (c) 2011 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by UCHIYAMA Yasushi.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
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.
18  *
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.
30  */
31 
32 #if HAVE_NBTOOL_CONFIG_H
33 #include "nbtool_config.h"
34 #endif
35 
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
39 #include "opt_v7fs.h"
40 #endif
41 
42 #include <sys/types.h>
43 #ifdef _KERNEL
44 #include <sys/systm.h>
45 #include <sys/param.h>
46 #else
47 #include <stdio.h>
48 #include <string.h>
49 #include <errno.h>
50 #endif
51 
52 #include "v7fs.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"
58 
59 #ifdef V7FS_DATABLOCK_DEBUG
60 #define   DPRINTF(fmt, args...)         printf("%s: " fmt, __func__, ##args)
61 #else
62 #define   DPRINTF(fmt, args...)         ((void)0)
63 #endif
64 
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);
75 
76 #ifdef V7FS_DATABLOCK_DEBUG
77 void daddr_map_dump(const struct v7fs_daddr_map *);
78 #else
79 #define   daddr_map_dump(x)   ((void)0)
80 #endif
81 
82 bool
datablock_number_sanity(const struct v7fs_self * fs,v7fs_daddr_t blk)83 datablock_number_sanity(const struct v7fs_self *fs, v7fs_daddr_t blk)
84 {
85           const struct v7fs_superblock *sb = &fs->superblock;
86           bool ok = (blk >= sb->datablock_start_sector) &&
87               (blk < sb->volume_size);
88 
89 #ifdef V7FS_DATABLOCK_DEBUG
90           if (!ok) {
91                     DPRINTF("Bad data block #%d\n", blk);
92           }
93 #endif
94 
95           return ok;
96 }
97 
98 int
v7fs_datablock_allocate(struct v7fs_self * fs,v7fs_daddr_t * block_number)99 v7fs_datablock_allocate(struct v7fs_self *fs, v7fs_daddr_t *block_number)
100 {
101           struct v7fs_superblock *sb = &fs->superblock;
102           v7fs_daddr_t blk;
103           int error = 0;
104 
105           *block_number = 0;
106           SUPERB_LOCK(fs);
107           do {
108                     if (!sb->total_freeblock) {
109                               DPRINTF("free block exhausted!!!\n");
110                               SUPERB_UNLOCK(fs);
111                               return ENOSPC;
112                     }
113 
114                     /* Get free block from superblock cache. */
115                     blk = sb->freeblock[--sb->nfreeblock];
116                     sb->total_freeblock--;
117                     sb->modified = 1;
118 
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");
123                                         SUPERB_UNLOCK(fs);
124                                         return error;
125                               }
126                               /* This freeblock link is no longer required. */
127                               /* use as data block. */
128                     }
129           } while (!datablock_number_sanity(fs, blk)); /* skip bogus block. */
130           SUPERB_UNLOCK(fs);
131 
132           DPRINTF("Get freeblock %d\n", blk);
133           /* Zero clear datablock. */
134           void *buf;
135           if (!(buf = scratch_read(fs, blk)))
136                     return EIO;
137           memset(buf, 0, V7FS_BSIZE);
138           if (!fs->io.write(fs->io.cookie, buf, blk))
139                     error = EIO;
140           scratch_free(fs, buf);
141 
142           if (error == 0)
143                     *block_number = blk;
144 
145           return error;
146 }
147 
148 static int
v7fs_datablock_deallocate(struct v7fs_self * fs,v7fs_daddr_t blk)149 v7fs_datablock_deallocate(struct v7fs_self *fs, v7fs_daddr_t blk)
150 {
151           struct v7fs_superblock *sb = &fs->superblock;
152           void *buf;
153           int error = 0;
154 
155           if (!datablock_number_sanity(fs, blk))
156                     return EIO;
157 
158           /* Add to in-core freelist. */
159           SUPERB_LOCK(fs);
160           if (sb->nfreeblock < V7FS_MAX_FREEBLOCK) {
161                     sb->freeblock[sb->nfreeblock++] = blk;
162                     sb->total_freeblock++;
163                     sb->modified = 1;
164                     DPRINTF("n_freeblock=%d\n", sb->total_freeblock);
165                     SUPERB_UNLOCK(fs);
166                     return 0;
167           }
168 
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))) {
172                     SUPERB_UNLOCK(fs);
173                     return EIO;         /* Fatal */
174           }
175 
176           struct v7fs_freeblock *fb = (struct v7fs_freeblock *)buf;
177           fb->nfreeblock = V7FS_MAX_FREEBLOCK;
178           int i;
179           for (i = 0; i < V7FS_MAX_FREEBLOCK; i++)
180                     fb->freeblock[i] = V7FS_VAL32(fs, sb->freeblock[i]);
181 
182           if (!fs->io.write(fs->io.cookie, (uint8_t *)fb, blk)) {
183                     error =  EIO;       /* Fatal */
184           } else {
185                     /* Link. on next allocate, this block is used as datablock, */
186                     /* and swap outed freeblock list is restored. */
187                     sb->freeblock[0] = blk;
188                     sb->nfreeblock = 1;
189                     sb->total_freeblock++;
190                     sb->modified = 1;
191                     DPRINTF("n_freeblock=%d\n", sb->total_freeblock);
192           }
193           SUPERB_UNLOCK(fs);
194           scratch_free(fs, buf);
195 
196           return error;
197 }
198 
199 int
v7fs_datablock_addr(size_t sz,struct v7fs_daddr_map * map)200 v7fs_datablock_addr(size_t sz, struct v7fs_daddr_map *map)
201 {
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)))
207           if (!sz) {
208                     map->level = 0;
209                     map->index[0] = 0;
210                     return 0;
211           }
212 
213           sz = V7FS_ROUND_BSIZE(sz);
214 
215           /* Direct */
216           if (sz <= DIRECT_SZ) {
217                     map->level = 0;
218                     map->index[0] = (sz >> V7FS_BSHIFT) - 1;
219                     return 0;
220           }
221           /* Index 1 */
222           sz -= DIRECT_SZ;
223 
224           if (sz <= IDX1_SZ) {
225                     map->level = 1;
226                     map->index[0] = (sz >> V7FS_BSHIFT) - 1;
227                     return 0;
228           }
229           sz -= IDX1_SZ;
230 
231           /* Index 2 */
232           if (sz <= IDX2_SZ) {
233                     map->level = 2;
234                     map->index[0] = ROUND(sz, IDX1_SZ) / IDX1_SZ - 1;
235                     map->index[1] = ((sz - (map->index[0] * IDX1_SZ)) >>
236                         V7FS_BSHIFT) - 1;
237                     return 0;
238           }
239           sz -= IDX2_SZ;
240 
241           /* Index 3 */
242           map->level = 3;
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;
248 
249           return map->index[2] >= NIDX ? ENOSPC : 0;
250 }
251 
252 int
v7fs_datablock_foreach(struct v7fs_self * fs,struct v7fs_inode * p,int (* func)(struct v7fs_self *,void *,v7fs_daddr_t,size_t),void * ctx)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)
255 {
256           size_t i;
257           v7fs_daddr_t blk, blk2;
258           size_t filesize;
259           bool last;
260           int ret;
261 
262           if (!(filesize = v7fs_inode_filesize(p)))
263                     return V7FS_ITERATOR_END;
264 #ifdef V7FS_DATABLOCK_DEBUG
265           size_t sz = filesize;
266 #endif
267 
268           /* Direct */
269           for (i = 0; i < V7FS_NADDR_DIRECT; i++, filesize -= V7FS_BSIZE) {
270                     blk = p->addr[i];
271                     if (!datablock_number_sanity(fs, blk)) {
272                               DPRINTF("inode#%d direct=%zu filesize=%zu\n",
273                                   p->inode_number, i, sz);
274                               return EIO;
275                     }
276 
277                     last = filesize <= V7FS_BSIZE;
278                     if ((ret = func(fs, ctx, blk, last ? filesize : V7FS_BSIZE)))
279                               return ret;
280                     if (last)
281                               return V7FS_ITERATOR_END;
282           }
283 
284           /* Index 1 */
285           blk = p->addr[V7FS_NADDR_INDEX1];
286           if (!datablock_number_sanity(fs, blk))
287                     return EIO;
288 
289           if ((ret = v7fs_loop1(fs, blk, &filesize, func, ctx)))
290                     return ret;
291 
292           /* Index 2 */
293           blk = p->addr[V7FS_NADDR_INDEX2];
294           if (!datablock_number_sanity(fs, blk))
295                     return EIO;
296 
297           if ((ret = v7fs_loop2(fs, blk, &filesize, func, ctx)))
298                     return ret;
299 
300           /* Index 3 */
301           blk = p->addr[V7FS_NADDR_INDEX3];
302           if (!datablock_number_sanity(fs, blk))
303                     return EIO;
304 
305           for (i = 0; i < V7FS_DADDR_PER_BLOCK; i++) {
306                     blk2 = v7fs_link(fs, blk, i);
307                     if (!datablock_number_sanity(fs, blk))
308                               return EIO;
309 
310                     if ((ret = v7fs_loop2(fs, blk2, &filesize, func, ctx)))
311                               return ret;
312           }
313 
314           return EFBIG;
315 }
316 
317 static int
v7fs_loop2(struct v7fs_self * fs,v7fs_daddr_t listblk,size_t * filesize,int (* func)(struct v7fs_self *,void *,v7fs_daddr_t,size_t),void * 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)
320 {
321           v7fs_daddr_t blk;
322           int ret;
323           size_t j;
324 
325           for (j = 0; j < V7FS_DADDR_PER_BLOCK; j++) {
326                     blk = v7fs_link(fs, listblk, j);
327                     if (!datablock_number_sanity(fs, blk))
328                               return EIO;
329                     if ((ret = v7fs_loop1(fs, blk, filesize, func, ctx)))
330                               return ret;
331           }
332 
333           return 0;
334 }
335 
336 static int
v7fs_loop1(struct v7fs_self * fs,v7fs_daddr_t listblk,size_t * filesize,int (* func)(struct v7fs_self *,void *,v7fs_daddr_t,size_t),void * 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)
339 {
340           v7fs_daddr_t blk;
341           bool last;
342           int ret;
343           size_t k;
344 
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))
348                               return EIO;
349                     last = *filesize <= V7FS_BSIZE;
350                     if ((ret = func(fs, ctx, blk, last ? *filesize : V7FS_BSIZE)))
351                               return ret;
352                     if (last)
353                               return V7FS_ITERATOR_END;
354           }
355 
356           return 0;
357 }
358 
359 v7fs_daddr_t
v7fs_datablock_last(struct v7fs_self * fs,struct v7fs_inode * inode,v7fs_off_t ofs)360 v7fs_datablock_last(struct v7fs_self *fs, struct v7fs_inode *inode,
361     v7fs_off_t ofs)
362 {
363           struct v7fs_daddr_map map;
364           v7fs_daddr_t blk = 0;
365           v7fs_daddr_t *addr = inode->addr;
366 
367           /* Inquire last data block location. */
368           if (v7fs_datablock_addr(ofs, &map) != 0)
369                     return 0;
370 
371           switch (map.level)
372           {
373           case 0: /*Direct */
374                     blk = inode->addr[map.index[0]];
375                     break;
376           case 1: /*Index1 */
377                     blk = v7fs_link(fs, addr[V7FS_NADDR_INDEX1], map.index[0]);
378                     break;
379           case 2: /*Index2 */
380                     blk = v7fs_link(fs, v7fs_link(fs,
381                         addr[V7FS_NADDR_INDEX2], map.index[0]), map.index[1]);
382                     break;
383           case 3: /*Index3 */
384                     blk = v7fs_link(fs, v7fs_link(fs, v7fs_link(fs,
385                         addr[V7FS_NADDR_INDEX3], map.index[0]), map.index[1]),
386                         map.index[2]);
387                     break;
388           }
389 
390           return blk;
391 }
392 
393 int
v7fs_datablock_expand(struct v7fs_self * fs,struct v7fs_inode * inode,size_t sz)394 v7fs_datablock_expand(struct v7fs_self *fs, struct v7fs_inode *inode, size_t sz)
395 {
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;
400           int error;
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;
403 
404           if (old_nblk == new_nblk) {
405                     inode->filesize += sz;
406                     v7fs_inode_writeback(fs, inode);
407                     return 0; /* no need to expand. */
408           }
409           struct v7fs_inode backup = *inode;
410           v7fs_daddr_t required_blk = new_nblk - old_nblk;
411 
412           DPRINTF("%zu->%zu, required block=%d\n", old_filesize, new_filesize,
413               required_blk);
414 
415           v7fs_datablock_addr(old_filesize, &oldmap);
416           v7fs_daddr_t i;
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);
421 
422                     if (oldmap.level != newmap.level) {
423                               /* Allocate index area */
424                               if ((error = v7fs_datablock_allocate(fs, &idxblk)))
425                                         return error;
426 
427                               switch (newmap.level) {
428                               case 1:
429                                         DPRINTF("0->1\n");
430                                         inode->addr[V7FS_NADDR_INDEX1] = idxblk;
431                                         blk = v7fs_add_leaf(fs, idxblk, 0);
432                                         break;
433                               case 2:
434                                         DPRINTF("1->2\n");
435                                         inode->addr[V7FS_NADDR_INDEX2] = idxblk;
436                                         blk = v7fs_add_leaf(fs, v7fs_add_leaf(fs,
437                                             idxblk, 0), 0);
438                                         break;
439                               case 3:
440                                         DPRINTF("2->3\n");
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);
444                                         break;
445                               }
446                     } else {
447                               switch (newmap.level) {
448                               case 0:
449                                         if ((error = v7fs_datablock_allocate(fs, &blk)))
450                                                   return error;
451                                         inode->addr[newmap.index[0]] = blk;
452                                         DPRINTF("direct index %d = blk%d\n",
453                                             newmap.index[0], blk);
454                                         break;
455                               case 1:
456                                         idxblk = inode->addr[V7FS_NADDR_INDEX1];
457                                         blk = v7fs_add_leaf(fs, idxblk,
458                                             newmap.index[0]);
459                                         break;
460                               case 2:
461                                         idxblk = inode->addr[V7FS_NADDR_INDEX2];
462                                         if (oldmap.index[0] != newmap.index[0]) {
463                                                   v7fs_add_leaf(fs, idxblk,
464                                                       newmap.index[0]);
465                                         }
466                                         blk = v7fs_add_leaf(fs, v7fs_link(fs,idxblk,
467                                             newmap.index[0]), newmap.index[1]);
468                                         break;
469                               case 3:
470                                         idxblk = inode->addr[V7FS_NADDR_INDEX3];
471 
472                                         if (oldmap.index[0] != newmap.index[0]) {
473                                                   v7fs_add_leaf(fs, idxblk,
474                                                       newmap.index[0]);
475                                         }
476 
477                                         if (oldmap.index[1] != newmap.index[1]) {
478                                                   v7fs_add_leaf(fs, v7fs_link(fs, idxblk,
479                                                       newmap.index[0]), newmap.index[1]);
480                                         }
481                                         blk = v7fs_add_leaf(fs, v7fs_link(fs,
482                                             v7fs_link(fs, idxblk, newmap.index[0]),
483                                             newmap.index[1]), newmap.index[2]);
484                                         break;
485                               }
486                     }
487                     if (!blk) {
488                               *inode = backup; /* structure copy; */
489                               return ENOSPC;
490                     }
491                     oldmap = newmap;
492           }
493           inode->filesize += sz;
494           v7fs_inode_writeback(fs, inode);
495 
496           return 0;
497 }
498 
499 static v7fs_daddr_t
v7fs_link(struct v7fs_self * fs,v7fs_daddr_t listblk,int n)500 v7fs_link(struct v7fs_self *fs, v7fs_daddr_t listblk, int n)
501 {
502           v7fs_daddr_t *list;
503           v7fs_daddr_t blk;
504           void *buf;
505 
506           if (!datablock_number_sanity(fs, listblk))
507                     return 0;
508           if (!(buf = scratch_read(fs, listblk)))
509                     return 0;
510           list = (v7fs_daddr_t *)buf;
511           blk = V7FS_VAL32(fs, list[n]);
512           scratch_free(fs, buf);
513 
514           if (!datablock_number_sanity(fs, blk))
515                     return 0;
516 
517           return blk;
518 }
519 
520 static v7fs_daddr_t
v7fs_add_leaf(struct v7fs_self * fs,v7fs_daddr_t up,int idx)521 v7fs_add_leaf(struct v7fs_self *fs, v7fs_daddr_t up, int idx)
522 {
523           v7fs_daddr_t newblk;
524           v7fs_daddr_t *daddr_list;
525           int error = 0;
526           void *buf;
527 
528           if (!up)
529                     return 0;
530           if (!datablock_number_sanity(fs, up))
531                     return 0;
532 
533           if ((error = v7fs_datablock_allocate(fs, &newblk)))
534                     return 0;
535           if (!(buf = scratch_read(fs, up)))
536                     return 0;
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))
540                     newblk = 0;
541           scratch_free(fs, buf);
542 
543           return newblk;
544 }
545 
546 int
v7fs_datablock_contract(struct v7fs_self * fs,struct v7fs_inode * inode,size_t sz)547 v7fs_datablock_contract(struct v7fs_self *fs, struct v7fs_inode *inode,
548     size_t sz)
549 {
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;
554           int error = 0;
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;
557 
558           if (old_nblk == new_nblk) {
559                     inode->filesize -= sz;
560                     v7fs_inode_writeback(fs, inode);
561                     return 0; /* no need to contract; */
562           }
563           v7fs_daddr_t erase_blk = old_nblk - new_nblk;
564 
565           DPRINTF("%zu->%zu # of erased block=%d\n", old_filesize, new_filesize,
566               erase_blk);
567 
568           v7fs_datablock_addr(old_filesize, &oldmap);
569           v7fs_daddr_t i;
570           for (i = 0; i < erase_blk; i++) {
571                     v7fs_datablock_addr(old_filesize - (i+1) * V7FS_BSIZE, &newmap);
572 
573                     if (oldmap.level != newmap.level) {
574                               switch (newmap.level) {
575                               case 0: /*1->0 */
576                                         DPRINTF("1->0\n");
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));
581                                         break;
582                               case 1: /*2->1 */
583                                         DPRINTF("2->1\n");
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,
588                                             idxblk)));
589                                         break;
590                               case 2:/*3->2 */
591                                         DPRINTF("3->2\n");
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))));
597                                         break;
598                               }
599                     } else {
600                               switch (newmap.level) {
601                               case 0:
602                                         DPRINTF("[0] %d\n", oldmap.index[0]);
603                                         blk = inode->addr[oldmap.index[0]];
604                                         error = v7fs_datablock_deallocate(fs, blk);
605                                         break;
606                               case 1:
607                                         DPRINTF("[1] %d\n", oldmap.index[0]);
608                                         idxblk = inode->addr[V7FS_NADDR_INDEX1];
609                                         v7fs_remove_leaf(fs, idxblk, oldmap.index[0]);
610 
611                                         break;
612                               case 2:
613                                         DPRINTF("[2] %d %d\n", oldmap.index[0],
614                                             oldmap.index[1]);
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,
620                                                       oldmap.index[0]);
621                                         }
622                                         break;
623                               case 3:
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]);
630 
631                                         if (oldmap.index[1] != newmap.index[1]) {
632                                                   v7fs_remove_leaf(fs, v7fs_link(fs,
633                                                       idxblk, oldmap.index[0]),
634                                                       oldmap.index[1]);
635                                         }
636                                         if (oldmap.index[0] != newmap.index[0]) {
637                                                   v7fs_remove_leaf(fs, idxblk,
638                                                       oldmap.index[0]);
639                                         }
640                                         break;
641                               }
642                     }
643                     oldmap = newmap;
644           }
645           inode->filesize -= sz;
646           v7fs_inode_writeback(fs, inode);
647 
648           return error;
649 }
650 
651 static v7fs_daddr_t
v7fs_unlink(struct v7fs_self * fs,v7fs_daddr_t idxblk,int n)652 v7fs_unlink(struct v7fs_self *fs, v7fs_daddr_t idxblk, int n)
653 {
654           v7fs_daddr_t *daddr_list;
655           v7fs_daddr_t blk;
656           void *buf;
657 
658           if (!(buf = scratch_read(fs, idxblk)))
659                     return 0;
660           daddr_list = (v7fs_daddr_t *)buf;
661           blk = V7FS_VAL32(fs, daddr_list[n]);
662           daddr_list[n] = 0;
663           fs->io.write(fs->io.cookie, buf, idxblk);
664           scratch_free(fs, buf);
665 
666           return blk; /* unlinked block. */
667 }
668 
669 static v7fs_daddr_t
v7fs_remove_self(struct v7fs_self * fs,v7fs_daddr_t up)670 v7fs_remove_self(struct v7fs_self *fs, v7fs_daddr_t up)
671 {
672           v7fs_daddr_t down;
673 
674           if (!datablock_number_sanity(fs, up))
675                     return 0;
676 
677           /* At 1st, remove from datablock list. */
678           down = v7fs_unlink(fs, up, 0);
679 
680           /* link self to freelist. */
681           v7fs_datablock_deallocate(fs, up);
682 
683           return down;
684 }
685 
686 static v7fs_daddr_t
v7fs_remove_leaf(struct v7fs_self * fs,v7fs_daddr_t up,int n)687 v7fs_remove_leaf(struct v7fs_self *fs, v7fs_daddr_t up, int n)
688 {
689           v7fs_daddr_t down;
690 
691           if (!datablock_number_sanity(fs, up))
692                     return 0;
693 
694           /* At 1st, remove from datablock list. */
695           down = v7fs_unlink(fs, up, n);
696 
697           /* link leaf to freelist. */
698           v7fs_datablock_deallocate(fs, down);
699 
700           return down;
701 }
702 
703 int
v7fs_datablock_size_change(struct v7fs_self * fs,size_t newsz,struct v7fs_inode * inode)704 v7fs_datablock_size_change(struct v7fs_self *fs, size_t newsz,
705     struct v7fs_inode *inode)
706 {
707           ssize_t diff = newsz - v7fs_inode_filesize(inode);
708           int error = 0;
709 
710           if (diff > 0)
711                     error = v7fs_datablock_expand(fs, inode, diff);
712           else if (diff < 0)
713                     error = v7fs_datablock_contract(fs, inode, -diff);
714 
715           return error;
716 }
717 
718 #ifdef V7FS_DATABLOCK_DEBUG
719 void
daddr_map_dump(const struct v7fs_daddr_map * map)720 daddr_map_dump(const struct v7fs_daddr_map *map)
721 {
722 
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]);
727           printf("\n");
728 }
729 #endif
730