1 /* $NetBSD: pass2.c,v 1.47 2011/06/09 19:57:52 christos Exp $ */ 2 3 /* 4 * Copyright (c) 1980, 1986, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. Neither the name of the University nor the names of its contributors 16 * may be used to endorse or promote products derived from this software 17 * without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32 #include <sys/cdefs.h> 33 #ifndef lint 34 #if 0 35 static char sccsid[] = "@(#)pass2.c 8.9 (Berkeley) 4/28/95"; 36 #else 37 __RCSID("$NetBSD: pass2.c,v 1.47 2011/06/09 19:57:52 christos Exp $"); 38 #endif 39 #endif /* not lint */ 40 41 #include <sys/param.h> 42 #include <sys/time.h> 43 44 #include <ufs/ufs/dinode.h> 45 #include <ufs/ufs/dir.h> 46 #include <ufs/ffs/fs.h> 47 48 #include <err.h> 49 #include <stdio.h> 50 #include <stdlib.h> 51 #include <string.h> 52 53 #include "fsck.h" 54 #include "fsutil.h" 55 #include "extern.h" 56 #include "exitvalues.h" 57 58 #define MINDIRSIZE (sizeof (struct dirtemplate)) 59 60 static int blksort(const void *, const void *); 61 static int pass2check(struct inodesc *); 62 63 void 64 pass2(void) 65 { 66 union dinode *dp; 67 struct inoinfo **inpp, *inp, *pinp; 68 struct inoinfo **inpend; 69 struct inostat *rinfo, *info; 70 struct inodesc curino; 71 union dinode dino; 72 int i, maxblk; 73 char pathbuf[MAXPATHLEN + 1]; 74 75 rinfo = inoinfo(ROOTINO); 76 switch (rinfo->ino_state) { 77 78 case USTATE: 79 pfatal("ROOT INODE UNALLOCATED"); 80 if (reply("ALLOCATE") == 0) { 81 markclean = 0; 82 ckfini(1); 83 exit(FSCK_EXIT_CHECK_FAILED); 84 } 85 if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO) 86 errexit("CANNOT ALLOCATE ROOT INODE"); 87 break; 88 89 case DCLEAR: 90 pfatal("DUPS/BAD IN ROOT INODE"); 91 if (reply("REALLOCATE")) { 92 freeino(ROOTINO); 93 if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO) 94 errexit("CANNOT ALLOCATE ROOT INODE"); 95 break; 96 } 97 markclean = 0; 98 if (reply("CONTINUE") == 0) { 99 ckfini(1); 100 exit(FSCK_EXIT_CHECK_FAILED); 101 } 102 break; 103 104 case FSTATE: 105 case FCLEAR: 106 pfatal("ROOT INODE NOT DIRECTORY"); 107 if (reply("REALLOCATE")) { 108 freeino(ROOTINO); 109 if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO) 110 errexit("CANNOT ALLOCATE ROOT INODE"); 111 break; 112 } 113 if (reply("FIX") == 0) { 114 markclean = 0; 115 ckfini(1); 116 exit(FSCK_EXIT_CHECK_FAILED); 117 } 118 dp = ginode(ROOTINO); 119 DIP_SET(dp, mode, 120 iswap16((iswap16(DIP(dp, mode)) & ~IFMT) | IFDIR)); 121 inodirty(); 122 break; 123 124 case DSTATE: 125 break; 126 127 default: 128 errexit("BAD STATE %d FOR ROOT INODE", rinfo->ino_state); 129 } 130 if (newinofmt) { 131 info = inoinfo(WINO); 132 info->ino_state = FSTATE; 133 info->ino_type = DT_WHT; 134 } 135 /* 136 * Sort the directory list into disk block order. 137 */ 138 qsort((char *)inpsort, (size_t)inplast, sizeof *inpsort, blksort); 139 /* 140 * Check the integrity of each directory. 141 */ 142 memset(&curino, 0, sizeof(struct inodesc)); 143 curino.id_type = DATA; 144 curino.id_func = pass2check; 145 inpend = &inpsort[inplast]; 146 for (inpp = inpsort; inpp < inpend; inpp++) { 147 if (got_siginfo) { 148 fprintf(stderr, 149 "%s: phase 2: dir %ld of %d (%d%%)\n", cdevname(), 150 (long)(inpp - inpsort), (int)inplast, 151 (int)((inpp - inpsort) * 100 / inplast)); 152 got_siginfo = 0; 153 } 154 #ifdef PROGRESS 155 progress_bar(cdevname(), preen ? NULL : "phase 2", 156 (inpp - inpsort), inplast); 157 #endif /* PROGRESS */ 158 inp = *inpp; 159 if (inp->i_isize == 0) 160 continue; 161 if (inp->i_isize < MINDIRSIZE) { 162 direrror(inp->i_number, "DIRECTORY TOO SHORT"); 163 inp->i_isize = roundup(MINDIRSIZE, dirblksiz); 164 if (reply("FIX") == 1) { 165 dp = ginode(inp->i_number); 166 DIP_SET(dp, size, iswap64(inp->i_isize)); 167 inodirty(); 168 } else 169 markclean = 0; 170 } else if ((inp->i_isize & (dirblksiz - 1)) != 0) { 171 getpathname(pathbuf, sizeof(pathbuf), inp->i_number, 172 inp->i_number); 173 if (usedsoftdep) 174 pfatal("%s %s: LENGTH %lld NOT MULTIPLE OF %d", 175 "DIRECTORY", pathbuf, 176 (long long)inp->i_isize, dirblksiz); 177 else 178 pwarn("%s %s: LENGTH %lld NOT MULTIPLE OF %d", 179 "DIRECTORY", pathbuf, 180 (long long)inp->i_isize, dirblksiz); 181 if (preen) 182 printf(" (ADJUSTED)\n"); 183 inp->i_isize = roundup(inp->i_isize, dirblksiz); 184 if (preen || reply("ADJUST") == 1) { 185 dp = ginode(inp->i_number); 186 DIP_SET(dp, size, iswap64(inp->i_isize)); 187 inodirty(); 188 } else 189 markclean = 0; 190 } 191 memset(&dino, 0, sizeof dino); 192 dp = &dino; 193 if (!is_ufs2) { 194 dp->dp1.di_mode = iswap16(IFDIR); 195 dp->dp1.di_size = iswap64(inp->i_isize); 196 maxblk = inp->i_numblks < NDADDR ? inp->i_numblks : 197 NDADDR; 198 for (i = 0; i < maxblk; i++) 199 dp->dp1.di_db[i] = inp->i_blks[i]; 200 if (inp->i_numblks > NDADDR) { 201 for (i = 0; i < NIADDR; i++) 202 dp->dp1.di_ib[i] = 203 inp->i_blks[NDADDR + i]; 204 } 205 } else { 206 dp->dp2.di_mode = iswap16(IFDIR); 207 dp->dp2.di_size = iswap64(inp->i_isize); 208 maxblk = inp->i_numblks < NDADDR ? inp->i_numblks : 209 NDADDR; 210 for (i = 0; i < maxblk; i++) 211 dp->dp2.di_db[i] = inp->i_blks[i]; 212 if (inp->i_numblks > NDADDR) { 213 for (i = 0; i < NIADDR; i++) 214 dp->dp2.di_ib[i] = 215 inp->i_blks[NDADDR + i]; 216 } 217 } 218 curino.id_number = inp->i_number; 219 curino.id_parent = inp->i_parent; 220 curino.id_uid = iswap32(DIP(dp, uid)); 221 curino.id_gid = iswap32(DIP(dp, gid)); 222 (void)ckinode(&dino, &curino); 223 } 224 225 /* 226 * Byte swapping in directory entries, if needed, has been done. 227 * Now rescan dirs for pass2check() 228 */ 229 if (do_dirswap) { 230 do_dirswap = 0; 231 for (inpp = inpsort; inpp < inpend; inpp++) { 232 inp = *inpp; 233 if (inp->i_isize == 0) 234 continue; 235 memset(&dino, 0, sizeof dino); 236 if (!is_ufs2) { 237 dino.dp1.di_mode = iswap16(IFDIR); 238 dino.dp1.di_size = iswap64(inp->i_isize); 239 for (i = 0; i < inp->i_numblks; i++) 240 dino.dp1.di_db[i] = inp->i_blks[i]; 241 } else { 242 dino.dp2.di_mode = iswap16(IFDIR); 243 dino.dp2.di_size = iswap64(inp->i_isize); 244 for (i = 0; i < inp->i_numblks; i++) 245 dino.dp2.di_db[i] = inp->i_blks[i]; 246 } 247 curino.id_number = inp->i_number; 248 curino.id_parent = inp->i_parent; 249 curino.id_uid = iswap32(DIP(&dino, uid)); 250 curino.id_gid = iswap32(DIP(&dino, gid)); 251 (void)ckinode(&dino, &curino); 252 } 253 } 254 255 /* 256 * Now that the parents of all directories have been found, 257 * make another pass to verify the value of `..' 258 */ 259 for (inpp = inpsort; inpp < inpend; inpp++) { 260 inp = *inpp; 261 if (inp->i_parent == 0 || inp->i_isize == 0) 262 continue; 263 if (inp->i_dotdot == inp->i_parent || 264 inp->i_dotdot == (ino_t)-1) 265 continue; 266 info = inoinfo(inp->i_parent); 267 if (inp->i_dotdot == 0) { 268 inp->i_dotdot = inp->i_parent; 269 fileerror(inp->i_parent, inp->i_number, "MISSING '..'"); 270 if (reply("FIX") == 0) { 271 markclean = 0; 272 continue; 273 } 274 (void)makeentry(inp->i_number, inp->i_parent, ".."); 275 info->ino_linkcnt--; 276 continue; 277 } 278 fileerror(inp->i_parent, inp->i_number, 279 "BAD INODE NUMBER FOR '..'"); 280 if (reply("FIX") == 0) { 281 markclean = 0; 282 continue; 283 } 284 inoinfo(inp->i_dotdot)->ino_linkcnt++; 285 info->ino_linkcnt--; 286 inp->i_dotdot = inp->i_parent; 287 (void)changeino(inp->i_number, "..", inp->i_parent); 288 } 289 /* 290 * Create a list of children for each directory. 291 */ 292 inpend = &inpsort[inplast]; 293 for (inpp = inpsort; inpp < inpend; inpp++) { 294 inp = *inpp; 295 info = inoinfo(inp->i_number); 296 inp->i_child = inp->i_sibling = 0; 297 if (info->ino_state == DFOUND) 298 info->ino_state = DSTATE; 299 } 300 for (inpp = inpsort; inpp < inpend; inpp++) { 301 inp = *inpp; 302 if (inp->i_parent == 0 || 303 inp->i_number == ROOTINO) 304 continue; 305 pinp = getinoinfo(inp->i_parent); 306 inp->i_sibling = pinp->i_child; 307 pinp->i_child = inp; 308 } 309 /* 310 * Mark all the directories that can be found from the root. 311 */ 312 propagate(ROOTINO); 313 314 #ifdef PROGRESS 315 if (!preen) 316 progress_done(); 317 #endif /* PROGRESS */ 318 } 319 320 static int 321 pass2check(struct inodesc *idesc) 322 { 323 struct direct *dirp = idesc->id_dirp; 324 struct inoinfo *inp; 325 struct inostat *info; 326 int n, entrysize, ret = 0; 327 union dinode *dp; 328 const char *errmsg; 329 struct direct proto; 330 char namebuf[MAXPATHLEN + 1]; 331 char pathbuf[MAXPATHLEN + 1]; 332 333 /* 334 * If converting, set directory entry type. 335 */ 336 if (!is_ufs2 && doinglevel2 && iswap32(dirp->d_ino) > 0 && 337 iswap32(dirp->d_ino) < maxino) { 338 dirp->d_type = inoinfo(iswap32(dirp->d_ino))->ino_type; 339 ret |= ALTERED; 340 } 341 /* 342 * check for "." 343 */ 344 if (idesc->id_entryno != 0) 345 goto chk1; 346 if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") == 0) { 347 if (iswap32(dirp->d_ino) != idesc->id_number) { 348 direrror(idesc->id_number, "BAD INODE NUMBER FOR '.'"); 349 dirp->d_ino = iswap32(idesc->id_number); 350 if (reply("FIX") == 1) 351 ret |= ALTERED; 352 else 353 markclean = 0; 354 } 355 if (newinofmt && dirp->d_type != DT_DIR) { 356 direrror(idesc->id_number, "BAD TYPE VALUE FOR '.'"); 357 dirp->d_type = DT_DIR; 358 if (reply("FIX") == 1) 359 ret |= ALTERED; 360 else 361 markclean = 0; 362 } 363 goto chk1; 364 } 365 direrror(idesc->id_number, "MISSING '.'"); 366 proto.d_ino = iswap32(idesc->id_number); 367 if (newinofmt) 368 proto.d_type = DT_DIR; 369 else 370 proto.d_type = 0; 371 proto.d_namlen = 1; 372 (void)strlcpy(proto.d_name, ".", sizeof(proto.d_name)); 373 # if BYTE_ORDER == LITTLE_ENDIAN 374 if (!newinofmt && !needswap) { 375 # else 376 if (!newinofmt && needswap) { 377 # endif 378 u_char tmp; 379 380 tmp = proto.d_type; 381 proto.d_type = proto.d_namlen; 382 proto.d_namlen = tmp; 383 } 384 entrysize = DIRSIZ(0, &proto, 0); 385 if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") != 0) { 386 pfatal("CANNOT FIX, FIRST ENTRY IN DIRECTORY CONTAINS %s\n", 387 dirp->d_name); 388 markclean = 0; 389 } else if (iswap16(dirp->d_reclen) < entrysize) { 390 pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '.'\n"); 391 markclean = 0; 392 } else if (iswap16(dirp->d_reclen) < 2 * entrysize) { 393 proto.d_reclen = dirp->d_reclen; 394 memmove(dirp, &proto, (size_t)entrysize); 395 if (reply("FIX") == 1) 396 ret |= ALTERED; 397 else 398 markclean = 0; 399 } else { 400 n = iswap16(dirp->d_reclen) - entrysize; 401 proto.d_reclen = iswap16(entrysize); 402 memmove(dirp, &proto, (size_t)entrysize); 403 idesc->id_entryno++; 404 inoinfo(iswap32(dirp->d_ino))->ino_linkcnt--; 405 dirp = (struct direct *)((char *)(dirp) + entrysize); 406 memset(dirp, 0, (size_t)n); 407 dirp->d_reclen = iswap16(n); 408 if (reply("FIX") == 1) 409 ret |= ALTERED; 410 else 411 markclean = 0; 412 } 413 chk1: 414 if (idesc->id_entryno > 1) 415 goto chk2; 416 inp = getinoinfo(idesc->id_number); 417 proto.d_ino = iswap32(inp->i_parent); 418 if (newinofmt) 419 proto.d_type = DT_DIR; 420 else 421 proto.d_type = 0; 422 proto.d_namlen = 2; 423 (void)strlcpy(proto.d_name, "..", sizeof(proto.d_name)); 424 #if BYTE_ORDER == LITTLE_ENDIAN 425 if (!newinofmt && !needswap) { 426 #else 427 if (!newinofmt && needswap) { 428 #endif 429 u_char tmp; 430 431 tmp = proto.d_type; 432 proto.d_type = proto.d_namlen; 433 proto.d_namlen = tmp; 434 } 435 entrysize = DIRSIZ(0, &proto, 0); 436 if (idesc->id_entryno == 0) { 437 n = DIRSIZ(0, dirp, 0); 438 if (iswap16(dirp->d_reclen) < n + entrysize) 439 goto chk2; 440 proto.d_reclen = iswap16(iswap16(dirp->d_reclen) - n); 441 dirp->d_reclen = iswap16(n); 442 idesc->id_entryno++; 443 inoinfo(iswap32(dirp->d_ino))->ino_linkcnt--; 444 dirp = (struct direct *)((char *)(dirp) + n); 445 memset(dirp, 0, (size_t)iswap16(proto.d_reclen)); 446 dirp->d_reclen = proto.d_reclen; 447 } 448 if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") == 0) { 449 inp->i_dotdot = iswap32(dirp->d_ino); 450 if (newinofmt && dirp->d_type != DT_DIR) { 451 direrror(idesc->id_number, "BAD TYPE VALUE FOR '..'"); 452 dirp->d_type = DT_DIR; 453 if (reply("FIX") == 1) 454 ret |= ALTERED; 455 else 456 markclean = 0; 457 } 458 goto chk2; 459 } 460 if (iswap32(dirp->d_ino) != 0 && strcmp(dirp->d_name, ".") != 0) { 461 fileerror(inp->i_parent, idesc->id_number, "MISSING '..'"); 462 pfatal("CANNOT FIX, SECOND ENTRY IN DIRECTORY CONTAINS %s\n", 463 dirp->d_name); 464 inp->i_dotdot = (ino_t)-1; 465 markclean = 0; 466 } else if (iswap16(dirp->d_reclen) < entrysize) { 467 fileerror(inp->i_parent, idesc->id_number, "MISSING '..'"); 468 pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '..'\n"); 469 inp->i_dotdot = (ino_t)-1; 470 markclean = 0; 471 } else if (inp->i_parent != 0) { 472 /* 473 * We know the parent, so fix now. 474 */ 475 inp->i_dotdot = inp->i_parent; 476 fileerror(inp->i_parent, idesc->id_number, "MISSING '..'"); 477 proto.d_reclen = dirp->d_reclen; 478 memmove(dirp, &proto, (size_t)entrysize); 479 if (reply("FIX") == 1) 480 ret |= ALTERED; 481 else 482 markclean = 0; 483 } 484 idesc->id_entryno++; 485 if (dirp->d_ino != 0) 486 inoinfo(iswap32(dirp->d_ino))->ino_linkcnt--; 487 return (ret|KEEPON); 488 chk2: 489 if (dirp->d_ino == 0) 490 return (ret|KEEPON); 491 if (dirp->d_namlen <= 2 && 492 dirp->d_name[0] == '.' && 493 idesc->id_entryno >= 2) { 494 if (dirp->d_namlen == 1) { 495 direrror(idesc->id_number, "EXTRA '.' ENTRY"); 496 dirp->d_ino = 0; 497 if (reply("FIX") == 1) 498 ret |= ALTERED; 499 else 500 markclean = 0; 501 return (KEEPON | ret); 502 } 503 if (dirp->d_name[1] == '.') { 504 direrror(idesc->id_number, "EXTRA '..' ENTRY"); 505 dirp->d_ino = 0; 506 if (reply("FIX") == 1) 507 ret |= ALTERED; 508 else 509 markclean = 0; 510 return (KEEPON | ret); 511 } 512 } 513 idesc->id_entryno++; 514 n = 0; 515 if (iswap32(dirp->d_ino) > maxino) { 516 fileerror(idesc->id_number, dirp->d_ino, "I OUT OF RANGE"); 517 n = reply("REMOVE"); 518 if (n == 0) 519 markclean = 0; 520 } else if (newinofmt && 521 ((iswap32(dirp->d_ino) == WINO && dirp->d_type != DT_WHT) || 522 (iswap32(dirp->d_ino) != WINO && dirp->d_type == DT_WHT))) { 523 fileerror(idesc->id_number, iswap32(dirp->d_ino), "BAD WHITEOUT ENTRY"); 524 dirp->d_ino = iswap32(WINO); 525 dirp->d_type = DT_WHT; 526 if (reply("FIX") == 1) 527 ret |= ALTERED; 528 else 529 markclean = 0; 530 } else { 531 again: 532 info = inoinfo(iswap32(dirp->d_ino)); 533 switch (info->ino_state) { 534 case USTATE: 535 if (idesc->id_entryno <= 2) 536 break; 537 fileerror(idesc->id_number, iswap32(dirp->d_ino), "UNALLOCATED"); 538 n = reply("REMOVE"); 539 if (n == 0) 540 markclean = 0; 541 break; 542 543 case DCLEAR: 544 case FCLEAR: 545 if (idesc->id_entryno <= 2) 546 break; 547 if (info->ino_state == FCLEAR) 548 errmsg = "DUP/BAD"; 549 else if (!preen && !usedsoftdep) 550 errmsg = "ZERO LENGTH DIRECTORY"; 551 else { 552 n = 1; 553 break; 554 } 555 fileerror(idesc->id_number, iswap32(dirp->d_ino), errmsg); 556 if ((n = reply("REMOVE")) == 1) 557 break; 558 dp = ginode(iswap32(dirp->d_ino)); 559 info->ino_state = 560 (iswap16(DIP(dp, mode)) & IFMT) == IFDIR ? DSTATE : FSTATE; 561 info->ino_linkcnt = iswap16(DIP(dp, nlink)); 562 goto again; 563 564 case DSTATE: 565 case DFOUND: 566 inp = getinoinfo(iswap32(dirp->d_ino)); 567 if (inp->i_parent != 0 && idesc->id_entryno > 2) { 568 getpathname(pathbuf, sizeof(pathbuf), 569 idesc->id_number, idesc->id_number); 570 getpathname(namebuf, sizeof(namebuf), 571 iswap32(dirp->d_ino), iswap32(dirp->d_ino)); 572 pwarn("%s %s %s\n", pathbuf, 573 "IS AN EXTRANEOUS HARD LINK TO DIRECTORY", 574 namebuf); 575 if (preen) 576 printf(" (IGNORED)\n"); 577 else if ((n = reply("REMOVE")) == 1) 578 break; 579 } 580 if (idesc->id_entryno > 2) 581 inp->i_parent = idesc->id_number; 582 /* fall through */ 583 584 case FSTATE: 585 if (newinofmt && dirp->d_type != info->ino_type) { 586 fileerror(idesc->id_number, iswap32(dirp->d_ino), 587 "BAD TYPE VALUE"); 588 dirp->d_type = info->ino_type; 589 if (reply("FIX") == 1) 590 ret |= ALTERED; 591 else 592 markclean = 0; 593 } 594 info->ino_linkcnt--; 595 break; 596 597 default: 598 errexit("BAD STATE %d FOR INODE I=%d", 599 info->ino_state, iswap32(dirp->d_ino)); 600 } 601 } 602 if (n == 0) 603 return (ret|KEEPON); 604 dirp->d_ino = 0; 605 return (ret|KEEPON|ALTERED); 606 } 607 608 /* 609 * Routine to sort disk blocks. 610 */ 611 static int 612 blksort(const void *arg1, const void *arg2) 613 { 614 615 return ((*(const struct inoinfo *const *)arg1)->i_blks[0] - 616 (*(const struct inoinfo *const *)arg2)->i_blks[0]); 617 } 618