1/*	$NetBSD: v7fs_superblock.c,v 1.1 2011/06/27 11:52:25 uch 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_superblock.c,v 1.1 2011/06/27 11:52:25 uch Exp $");
38#if defined _KERNEL_OPT
39#include "opt_v7fs.h"
40#endif
41
42#ifdef _KERNEL
43#include <sys/systm.h>
44#include <sys/param.h>	/* errno */
45#else
46#include <stdio.h>
47#include <string.h>
48#include <errno.h>
49#endif
50
51#include "v7fs.h"
52#include "v7fs_impl.h"
53#include "v7fs_endian.h"
54#include "v7fs_superblock.h"
55#include "v7fs_inode.h"
56#include "v7fs_datablock.h"
57
58#ifdef V7FS_SUPERBLOCK_DEBUG
59#define	DPRINTF(fmt, args...)	printf("%s: " fmt, __func__, ##args)
60#define	DPRINTF_(fmt, args...)	printf(fmt, ##args)
61#else
62#define	DPRINTF(fmt, args...)	((void)0)
63#define	DPRINTF_(fmt, args...)	((void)0)
64#endif
65
66static void v7fs_superblock_endian_convert(struct v7fs_self *,
67    struct v7fs_superblock *, struct v7fs_superblock *);
68static int v7fs_superblock_sanity(struct v7fs_self *);
69
70/* Load superblock from disk. */
71int
72v7fs_superblock_load(struct v7fs_self *fs)
73{
74	struct v7fs_superblock *disksb;
75	void *buf;
76	int error;
77
78	if (!(buf = scratch_read(fs, V7FS_SUPERBLOCK_SECTOR)))
79		return EIO;
80	disksb = (struct v7fs_superblock *)buf;
81	v7fs_superblock_endian_convert(fs, &fs->superblock, disksb);
82	scratch_free(fs, buf);
83
84	if ((error = v7fs_superblock_sanity(fs)))
85		return error;
86
87	return 0;
88}
89
90/* Writeback superblock to disk. */
91int
92v7fs_superblock_writeback(struct v7fs_self *fs)
93{
94	struct v7fs_superblock *memsb = &fs->superblock;
95	struct v7fs_superblock *disksb;
96	void *buf;
97	int error = 0;
98
99	if (!memsb->modified)
100		return 0;
101
102	if (!(buf = scratch_read(fs, V7FS_SUPERBLOCK_SECTOR)))
103		return EIO;
104	disksb = (struct v7fs_superblock *)buf;
105	v7fs_superblock_endian_convert(fs, disksb, memsb);
106	if (!fs->io.write(fs->io.cookie, buf, V7FS_SUPERBLOCK_SECTOR))
107		error = EIO;
108	scratch_free(fs, buf);
109
110	memsb->modified = 0;
111	DPRINTF("done. %d\n", error);
112
113	return error;
114}
115
116/* Check endian mismatch. */
117static int
118v7fs_superblock_sanity(struct v7fs_self *fs)
119{
120	const struct v7fs_superblock *sb = &fs->superblock;
121	void *buf = 0;
122
123	if ((sb->volume_size < 128) || /* smaller than 64KB. */
124	    (sb->datablock_start_sector > sb->volume_size) ||
125	    (sb->nfreeinode > V7FS_MAX_FREEINODE) ||
126	    (sb->nfreeblock > V7FS_MAX_FREEBLOCK) ||
127	    (sb->update_time < 0) ||
128	    (sb->total_freeblock > sb->volume_size) ||
129	    ((sb->nfreeinode == 0) && (sb->nfreeblock == 0) &&
130		(sb->total_freeblock == 0) && (sb->total_freeinode == 0)) ||
131	    (!(buf = scratch_read(fs, sb->volume_size - 1)))) {
132		DPRINTF("invalid super block.\n");
133		return EINVAL;
134	}
135	if (buf)
136		scratch_free(fs, buf);
137
138	return 0;
139}
140
141/* Fill free block to superblock cache. */
142int
143v7fs_freeblock_update(struct v7fs_self *fs, v7fs_daddr_t blk)
144{
145	/* Assume superblock is locked by caller. */
146	struct v7fs_superblock *sb = &fs->superblock;
147	struct v7fs_freeblock *fb;
148	void *buf;
149	int error;
150
151	/* Read next freeblock table from disk. */
152	if (!datablock_number_sanity(fs, blk) || !(buf = scratch_read(fs, blk)))
153		return EIO;
154
155	/* Update in-core superblock freelist. */
156	fb = (struct v7fs_freeblock *)buf;
157	if ((error = v7fs_freeblock_endian_convert(fs, fb))) {
158		scratch_free(fs, buf);
159		return error;
160	}
161	DPRINTF("freeblock table#%d, nfree=%d\n", blk, fb->nfreeblock);
162
163	memcpy(sb->freeblock, fb->freeblock, sizeof(blk) * fb->nfreeblock);
164	sb->nfreeblock = fb->nfreeblock;
165	sb->modified = true;
166	scratch_free(fs, buf);
167
168	return 0;
169}
170
171int
172v7fs_freeblock_endian_convert(struct v7fs_self *fs __unused,
173    struct v7fs_freeblock *fb __unused)
174{
175#ifdef V7FS_EI
176	int i;
177	int16_t nfree;
178
179	nfree = V7FS_VAL16(fs, fb->nfreeblock);
180	if (nfree <= 0 || nfree > V7FS_MAX_FREEBLOCK) {
181		DPRINTF("invalid freeblock list. %d (max=%d)\n", nfree,
182		    V7FS_MAX_FREEBLOCK);
183		return ENOSPC;
184	}
185	fb->nfreeblock = nfree;
186
187	for (i = 0; i < nfree; i++) {
188		fb->freeblock[i] = V7FS_VAL32(fs, fb->freeblock[i]);
189	}
190#endif /* V7FS_EI */
191
192	return 0;
193}
194
195/* Fill free inode to superblock cache. */
196int
197v7fs_freeinode_update(struct v7fs_self *fs)
198{
199	/* Assume superblock is locked by caller. */
200	struct v7fs_superblock *sb = &fs->superblock;
201	v7fs_ino_t *freeinode = sb->freeinode;
202	size_t i, j, k;
203	v7fs_ino_t ino;
204
205	/* Loop over all inode list. */
206	for (i = V7FS_ILIST_SECTOR, ino = 1/* inode start from 1*/, k = 0;
207	    i < sb->datablock_start_sector; i++) {
208		struct v7fs_inode_diskimage *di;
209		void *buf;
210		if (!(buf = scratch_read(fs, i))) {
211			DPRINTF("block %zu I/O error.\n", i);
212			ino += V7FS_INODE_PER_BLOCK;
213			continue;
214		}
215		di = (struct v7fs_inode_diskimage *)buf;
216
217		for (j = 0;
218		    (j < V7FS_INODE_PER_BLOCK) && (k < V7FS_MAX_FREEINODE);
219		    j++, di++, ino++) {
220			if (v7fs_inode_allocated(di))
221				continue;
222			DPRINTF("free inode%d\n", ino);
223			freeinode[k++] = ino;
224		}
225		scratch_free(fs, buf);
226	}
227	sb->nfreeinode = k;
228
229	return 0;
230}
231
232static void
233v7fs_superblock_endian_convert(struct v7fs_self *fs __unused,
234    struct v7fs_superblock *to,  struct v7fs_superblock *from)
235{
236#ifdef V7FS_EI
237#define	conv16(m)	(to->m = V7FS_VAL16(fs, from->m))
238#define	conv32(m)	(to->m = V7FS_VAL32(fs, from->m))
239	int i;
240
241	conv16(datablock_start_sector);
242	conv32(volume_size);
243	conv16(nfreeblock);
244	v7fs_daddr_t *dfrom = from->freeblock;
245	v7fs_daddr_t *dto = to->freeblock;
246	for (i = 0; i < V7FS_MAX_FREEBLOCK; i++, dfrom++, dto++)
247		*dto = V7FS_VAL32(fs, *dfrom);
248
249	conv16(nfreeinode);
250	v7fs_ino_t *ifrom = from->freeinode;
251	v7fs_ino_t *ito = to->freeinode;
252	for (i = 0; i < V7FS_MAX_FREEINODE; i++, ifrom++, ito++)
253		*ito = V7FS_VAL16(fs, *ifrom);
254
255	conv32(update_time);
256	conv32(total_freeblock);
257	conv16(total_freeinode);
258#undef conv16
259#undef conv32
260#else /* V7FS_EI */
261	memcpy(to, from , sizeof(*to));
262#endif /* V7FS_EI */
263}
264