1/*	$NetBSD$	*/
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#include <sys/cdefs.h>
33#ifndef lint
34__RCSID("$NetBSD$");
35#endif /* not lint */
36
37#include <sys/types.h>
38#include <sys/syslimits.h>	/*PATH_MAX */
39#include <stdio.h>
40#include <string.h>
41#include <time.h>
42
43#include "v7fs.h"
44#include "v7fs_impl.h"
45#include "v7fs_inode.h"
46#include "v7fs_datablock.h"
47#include "v7fs_superblock.h"
48#include "v7fs_dirent.h"
49#include "v7fs_file.h"
50#include "fsck_v7fs.h"
51
52/*
53 * Check pathname. for each inode, check parent, if it is directory,
54 * check child.
55 */
56
57static int
58connect_lost_and_found(struct v7fs_self *fs, v7fs_ino_t ino)
59{
60	char name[V7FS_NAME_MAX];
61	v7fs_time_t t;
62
63	if (!lost_and_found.mode || !reply("CONNECT?"))
64		return FSCK_EXIT_CHECK_FAILED;
65
66	snprintf(name, sizeof(name), "%d", ino);
67	v7fs_directory_add_entry(fs, &lost_and_found, ino, name);
68	t = (v7fs_time_t)time(NULL);
69	lost_and_found.mtime = lost_and_found.atime = t;
70	v7fs_inode_writeback(fs, &lost_and_found);
71	v7fs_superblock_writeback(fs); /* # of freeblocks may change. */
72
73	return 0;
74}
75
76/* Check child (dir) */
77struct lookup_child_arg {
78	int dir_cnt;
79	bool print;
80	struct v7fs_inode *parent;
81};
82
83static int
84lookup_child_subr(struct v7fs_self *fs, void *ctx, v7fs_daddr_t blk, size_t sz)
85{
86	struct lookup_child_arg *arg = (struct lookup_child_arg *)ctx;
87	void *buf;
88	int error = 0;
89
90	if (!(buf = scratch_read(fs, blk)))
91		return 0;
92	struct v7fs_dirent *dir = (struct v7fs_dirent *)buf;
93	size_t i, n = sz / sizeof(*dir);
94	if (!v7fs_dirent_endian_convert(fs, dir, n)) {
95		pwarn("*** bogus entry found *** dir#%d entry=%zu\n",
96		    arg->parent->inode_number, n);
97		arg->print = true;
98	}
99
100	for (i = 0; i < n; i++, dir++) {
101		struct v7fs_inode inode;
102		if (arg->print)
103			pwarn("%s %d\n", dir->name, dir->inode_number);
104		/* Bogus enties are removed here. */
105		if ((error = v7fs_inode_load(fs, &inode, dir->inode_number)))
106		{
107			pwarn("entry #%d not found.", dir->inode_number);
108			if (reply("REMOVE?"))
109				v7fs_directory_remove_entry(fs, arg->parent,
110				    dir->name);
111		} else {
112			/* Count child dir. */
113			if (v7fs_inode_isdir(&inode))
114				arg->dir_cnt++;
115		}
116	}
117	scratch_free(fs, buf);
118
119	return error;
120}
121
122static int
123lookup_child_from_dir(struct v7fs_self *fs, struct v7fs_inode *p, bool print)
124{
125	struct lookup_child_arg arg = { .dir_cnt = 0, .print = print,
126	    .parent = p };
127
128	v7fs_datablock_foreach(fs, p, lookup_child_subr, &arg);
129
130	return arg.dir_cnt;
131}
132
133/* Find parent directory (file) */
134struct lookup_parent_arg {
135	v7fs_ino_t target_ino;
136	v7fs_ino_t parent_ino;
137};
138
139static int
140lookup_parent_from_file_subr(struct v7fs_self *fs, void *ctx,
141    struct v7fs_inode *p, v7fs_ino_t ino)
142{
143	struct lookup_parent_arg *arg = (struct lookup_parent_arg *)ctx;
144
145	if (!v7fs_inode_isdir(p))
146		return 0;
147
148	if (v7fs_file_lookup_by_number(fs, p, arg->target_ino, NULL)) {
149		arg->parent_ino = ino; /* My inode found. */
150		return V7FS_ITERATOR_BREAK;
151	}
152
153	return 0; /* not found. */
154}
155
156
157static v7fs_ino_t
158lookup_parent_from_file(struct v7fs_self *fs, v7fs_ino_t ino)
159{
160	struct lookup_parent_arg arg = { .target_ino = ino, .parent_ino = 0 };
161
162	v7fs_ilist_foreach(fs, lookup_parent_from_file_subr, &arg);
163
164	return arg.parent_ino;
165}
166
167/* Find parent directory (dir) */
168static int
169lookup_parent_from_dir_subr(struct v7fs_self *fs, void *ctx, v7fs_daddr_t blk,
170    size_t sz)
171{
172	struct lookup_parent_arg *arg = (struct lookup_parent_arg *)ctx;
173	void *buf;
174	int ret = 0;
175
176	if (!(buf = scratch_read(fs, blk)))
177		return 0;
178	struct v7fs_dirent *dir = (struct v7fs_dirent *)buf;
179	size_t i, n = sz / sizeof(*dir);
180	if (!v7fs_dirent_endian_convert(fs, dir, n)) {
181		scratch_free(fs, buf);
182		return V7FS_ITERATOR_ERROR;
183	}
184
185	for (i = 0; i < n; i++, dir++) {
186		if (strncmp(dir->name, "..", 2) != 0)
187			continue;
188
189		arg->parent_ino = dir->inode_number;
190		ret = V7FS_ITERATOR_BREAK;
191		break;
192	}
193
194	scratch_free(fs, buf);
195	return ret;
196}
197
198static v7fs_ino_t
199lookup_parent_from_dir(struct v7fs_self *fs, struct v7fs_inode *p)
200{
201	struct lookup_parent_arg arg = { .target_ino = 0, .parent_ino = 0 };
202
203	/* Search parent("..") from my dirent. */
204	v7fs_datablock_foreach(fs, p, lookup_parent_from_dir_subr, &arg);
205
206	return arg.parent_ino;
207}
208
209static int
210pathname_check_file(struct v7fs_self *fs, struct v7fs_inode *p, v7fs_ino_t ino)
211{
212	v7fs_ino_t parent_ino;
213	struct v7fs_inode parent_inode;
214	int error = 0;
215
216	if (ino == 1)	/* reserved. */
217		return 0;
218
219	/* Check parent. */
220	if (!(parent_ino = lookup_parent_from_file(fs, ino)) ||
221	    (error = v7fs_inode_load(fs, &parent_inode, parent_ino)) ||
222	    !v7fs_inode_isdir(&parent_inode)) {
223		pwarn("*** inode#%d don't have parent.", ino);
224		v7fs_inode_dump(p);
225		error = connect_lost_and_found(fs, ino);
226	}
227
228	return error;
229}
230
231static int
232pathname_check_dir(struct v7fs_self *fs, struct v7fs_inode *p, v7fs_ino_t ino)
233{
234	v7fs_ino_t parent_ino;
235	struct v7fs_inode parent_inode;
236	int error = 0;
237
238	/* Check parent */
239	if (!(parent_ino = lookup_parent_from_dir(fs, p)) ||
240	    (error = v7fs_inode_load(fs, &parent_inode, parent_ino)) ||
241	    !v7fs_inode_isdir(&parent_inode)) {
242		pwarn("*** ino#%d parent dir missing parent=%d", ino,
243		    parent_ino);
244		/* link to lost+found */
245		v7fs_inode_dump(p);
246		if ((error = connect_lost_and_found(fs, ino)))
247			return error;
248	}
249
250	/* Check child */
251	int cnt = lookup_child_from_dir(fs, p, false);
252	if ((error = (cnt != p->nlink))) {
253		pwarn("*** ino#%d corrupt link # of child"
254		    " dir:%d(inode) != %d(cnt)", ino, p->nlink, cnt);
255		v7fs_inode_dump(p);
256		lookup_child_from_dir(fs, p, true);
257
258		if (reply("CORRECT?")) {
259			p->nlink = cnt;
260			v7fs_inode_writeback(fs, p);
261			error = 0;
262		} else {
263			error = FSCK_EXIT_CHECK_FAILED;
264		}
265	}
266
267	return error;
268}
269
270static int
271pathname_subr(struct v7fs_self *fs, void *ctx __unused, struct v7fs_inode *p,
272    v7fs_ino_t ino)
273{
274	int error = 0;
275
276	if (!v7fs_inode_allocated(p))
277		return 0;
278
279	progress(0);
280
281	if (v7fs_inode_isdir(p)) {
282		error = pathname_check_dir(fs, p, ino);
283	} else if (v7fs_inode_isfile(p)) {
284		error = pathname_check_file(fs, p, ino);
285	}
286
287	return error;
288}
289
290int
291pathname_check(struct v7fs_self *fs)
292{
293	struct v7fs_superblock *sb = &fs->superblock;
294	int inodes = V7FS_MAX_INODE(sb) - sb->total_freeinode;
295
296	progress(&(struct progress_arg){ .label = "pathname", .tick = inodes /
297	    PROGRESS_BAR_GRANULE });
298
299	return v7fs_ilist_foreach(fs, pathname_subr, 0);
300}
301
302
303char *
304filename(struct v7fs_self *fs, v7fs_ino_t ino)
305{
306	static char path[V7FS_PATH_MAX];
307	struct v7fs_inode inode;
308	v7fs_ino_t parent;
309	int error;
310	char name[V7FS_NAME_MAX + 1];
311	char *p = path + V7FS_PATH_MAX;
312	size_t n;
313
314	if ((error = v7fs_inode_load(fs, &inode, ino)))
315		return 0;
316
317	/* Lookup the 1st parent. */
318	if (v7fs_inode_isdir(&inode))
319		parent = lookup_parent_from_dir(fs, &inode);
320	else
321		parent = lookup_parent_from_file(fs, ino);
322
323	if ((error = v7fs_inode_load(fs, &inode, parent)))
324		return 0;
325
326	if (!v7fs_file_lookup_by_number(fs, &inode, ino, name))
327		return 0;
328
329	n = strlen(name) + 1;
330	strcpy(p -= n, name);
331
332	/* Lookup until / */
333	ino = parent;
334	while (parent != V7FS_ROOT_INODE) {
335		parent = lookup_parent_from_dir(fs, &inode);
336		if ((error = v7fs_inode_load(fs, &inode, parent)))
337			return 0;
338		if (!v7fs_file_lookup_by_number(fs, &inode, ino, name))
339			return 0;
340		ino = parent;
341		n = strlen(name) + 1;
342		strcpy(p - n, name);
343		p[-1] = '/';
344		p -= n;
345	}
346	*--p = '/';
347
348	return p;
349}
350