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