1 /* $NetBSD: fsdb.c,v 1.51 2020/04/05 15:25:40 joerg Exp $ */ 2 3 /*- 4 * Copyright (c) 1996, 2017 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by John T. Kohl. 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: fsdb.c,v 1.51 2020/04/05 15:25:40 joerg Exp $"); 35 #endif /* not lint */ 36 37 #include <sys/types.h> 38 #include <sys/stat.h> 39 #include <sys/param.h> 40 #include <sys/time.h> 41 #include <sys/mount.h> 42 #include <ctype.h> 43 #include <fcntl.h> 44 #include <grp.h> 45 #include <histedit.h> 46 #include <limits.h> 47 #include <pwd.h> 48 #include <stdio.h> 49 #include <stdlib.h> 50 #include <string.h> 51 #include <time.h> 52 #include <unistd.h> 53 #include <err.h> 54 55 #include <ufs/ufs/dinode.h> 56 #include <ufs/ufs/dir.h> 57 #include <ufs/ffs/fs.h> 58 #include <ufs/ffs/ffs_extern.h> 59 60 #include "fsdb.h" 61 #include "fsck.h" 62 #include "extern.h" 63 64 struct bufarea bufhead; 65 struct bufarea sblk; 66 struct bufarea asblk; 67 struct bufarea cgblk; 68 struct bufarea appleufsblk; 69 struct bufarea *pdirbp; 70 struct bufarea *pbp; 71 struct fs *sblock; 72 struct fs *altsblock; 73 struct cg *cgrp; 74 struct fs *sblocksave; 75 struct dups *duplist; 76 struct dups *muldup; 77 struct zlncnt *zlnhead; 78 struct inoinfo **inphead, **inpsort; 79 long numdirs, dirhash, listmax, inplast; 80 struct uquot_hash *uquot_user_hash; 81 struct uquot_hash *uquot_group_hash; 82 uint8_t q2h_hash_shift; 83 uint16_t q2h_hash_mask; 84 struct inostatlist *inostathead; 85 long dev_bsize; 86 long secsize; 87 char nflag; 88 char yflag; 89 int Uflag; 90 int bflag; 91 int debug; 92 int zflag; 93 int cvtlevel; 94 int doinglevel1; 95 int doinglevel2; 96 int newinofmt; 97 char usedsoftdep; 98 int preen; 99 int forceimage; 100 int is_ufs2; 101 int markclean; 102 char havesb; 103 char skipclean; 104 int fsmodified; 105 int fsreadfd; 106 int fswritefd; 107 int rerun; 108 char resolved; 109 int endian; 110 int doswap; 111 int needswap; 112 int do_blkswap; 113 int do_dirswap; 114 int isappleufs; 115 daddr_t maxfsblock; 116 char *blockmap; 117 ino_t maxino; 118 int dirblksiz; 119 daddr_t n_blks; 120 ino_t n_files; 121 long countdirs; 122 int got_siginfo; 123 struct ufs1_dinode ufs1_zino; 124 struct ufs2_dinode ufs2_zino; 125 126 /* Used to keep state for "saveblks" command. */ 127 struct wrinfo { 128 off_t size; 129 off_t written_size; 130 int fd; 131 }; 132 133 __dead static void usage(void); 134 static int cmdloop(void); 135 static char *prompt(EditLine *); 136 static int scannames(struct inodesc *); 137 static int dolookup(char *); 138 static int chinumfunc(struct inodesc *); 139 static int chnamefunc(struct inodesc *); 140 static int dotime(char *, int32_t *, int32_t *); 141 static void print_blks32(int32_t *buf, int size, uint64_t *blknum, struct wrinfo *wrp); 142 static void print_blks64(int64_t *buf, int size, uint64_t *blknum, struct wrinfo *wrp); 143 static void print_indirblks32(uint32_t blk, int ind_level, 144 uint64_t *blknum, struct wrinfo *wrp); 145 static void print_indirblks64(uint64_t blk, int ind_level, 146 uint64_t *blknum, struct wrinfo *wrp); 147 static int compare_blk32(uint32_t *, uint32_t); 148 static int compare_blk64(uint64_t *, uint64_t); 149 static int founddatablk(uint64_t); 150 static int find_blks32(uint32_t *buf, int size, uint32_t *blknum); 151 static int find_blks64(uint64_t *buf, int size, uint64_t *blknum); 152 static int find_indirblks32(uint32_t blk, int ind_level, 153 uint32_t *blknum); 154 static int find_indirblks64(uint64_t blk, int ind_level, 155 uint64_t *blknum); 156 157 union dinode *curinode; 158 ino_t curinum; 159 160 static void 161 usage(void) 162 { 163 errx(1, "usage: %s [-dFn] -f <fsname>", getprogname()); 164 } 165 /* 166 * We suck in lots of fsck code, and just pick & choose the stuff we want. 167 * 168 * fsreadfd is set up to read from the file system, fswritefd to write to 169 * the file system. 170 */ 171 int 172 main(int argc, char *argv[]) 173 { 174 int ch, rval; 175 char *fsys = NULL; 176 177 forceimage = 0; 178 debug = 0; 179 isappleufs = 0; 180 while ((ch = getopt(argc, argv, "dFf:n")) != -1) { 181 switch (ch) { 182 case 'd': 183 debug++; 184 break; 185 case 'F': 186 forceimage = 1; 187 break; 188 case 'f': 189 fsys = optarg; 190 break; 191 case 'n': 192 nflag++; 193 break; 194 default: 195 usage(); 196 } 197 } 198 if (fsys == NULL) 199 usage(); 200 endian = 0; 201 if (setup(fsys, fsys) <= 0) 202 errx(1, "cannot set up file system `%s'", fsys); 203 printf("Editing file system `%s'\nLast Mounted on %s\n", fsys, 204 sblock->fs_fsmnt); 205 rval = cmdloop(); 206 if (nflag) 207 exit(rval); 208 sblock->fs_clean = 0; /* mark it dirty */ 209 sbdirty(); 210 markclean = 0; 211 ckfini(1); 212 printf("*** FILE SYSTEM MARKED DIRTY\n"); 213 printf("*** BE SURE TO RUN FSCK TO CLEAN UP ANY DAMAGE\n"); 214 printf("*** IF IT WAS MOUNTED, RE-MOUNT WITH -u -o reload\n"); 215 exit(rval); 216 } 217 218 #define CMDFUNC(func) static int func (int argc, char *argv[]) 219 220 CMDFUNC(helpfn); 221 CMDFUNC(focus); /* focus on inode */ 222 CMDFUNC(active); /* print active inode */ 223 CMDFUNC(focusname); /* focus by name */ 224 CMDFUNC(zapi); /* clear inode */ 225 CMDFUNC(uplink); /* incr link */ 226 CMDFUNC(downlink); /* decr link */ 227 CMDFUNC(linkcount); /* set link count */ 228 CMDFUNC(quit); /* quit */ 229 CMDFUNC(ls); /* list directory */ 230 CMDFUNC(blks); /* list blocks */ 231 CMDFUNC(findblk); /* find block */ 232 CMDFUNC(rm); /* remove name */ 233 CMDFUNC(ln); /* add name */ 234 CMDFUNC(newtype); /* change type */ 235 CMDFUNC(chmode); /* change mode */ 236 CMDFUNC(chlen); /* change length */ 237 CMDFUNC(chaflags); /* change flags */ 238 CMDFUNC(chgen); /* change generation */ 239 CMDFUNC(chowner); /* change owner */ 240 CMDFUNC(chgroup); /* Change group */ 241 CMDFUNC(back); /* pop back to last ino */ 242 CMDFUNC(chmtime); /* Change mtime */ 243 CMDFUNC(chctime); /* Change ctime */ 244 CMDFUNC(chatime); /* Change atime */ 245 CMDFUNC(chinum); /* Change inode # of dirent */ 246 CMDFUNC(chname); /* Change dirname of dirent */ 247 248 static struct cmdtable cmds[] = { 249 {"help", "Print out help", 1, 1, helpfn}, 250 {"?", "Print out help", 1, 1, helpfn}, 251 {"inode", "Set active inode to INUM", 2, 2, focus}, 252 {"clri", "Clear inode INUM", 2, 2, zapi}, 253 {"lookup", "Set active inode by looking up NAME", 2, 2, focusname}, 254 {"cd", "Set active inode by looking up NAME", 2, 2, focusname}, 255 {"back", "Go to previous active inode", 1, 1, back}, 256 {"active", "Print active inode", 1, 1, active}, 257 {"print", "Print active inode", 1, 1, active}, 258 {"uplink", "Increment link count", 1, 1, uplink}, 259 {"downlink", "Decrement link count", 1, 1, downlink}, 260 {"linkcount", "Set link count to COUNT", 2, 2, linkcount}, 261 {"ls", "List current inode as directory", 1, 1, ls}, 262 {"blks", "List current inode's data blocks", 1, 1, blks}, 263 {"saveblks", "Save current inode's data blocks", 2, 2, blks}, 264 {"findblk", "Find inode owning disk block(s)", 2, 33, findblk}, 265 {"rm", "Remove NAME from current inode directory", 2, 2, rm}, 266 {"del", "Remove NAME from current inode directory", 2, 2, rm}, 267 {"ln", "Hardlink INO into current inode directory as NAME", 3, 3, ln}, 268 {"chinum", "Change dir entry number INDEX to INUM", 3, 3, chinum}, 269 {"chname", "Change dir entry number INDEX to NAME", 3, 3, chname}, 270 {"chtype", "Change type of current inode to TYPE", 2, 2, newtype}, 271 {"chmod", "Change mode of current inode to MODE", 2, 2, chmode}, 272 {"chown", "Change owner of current inode to OWNER", 2, 2, chowner}, 273 {"chlen", "Change length of current inode to LENGTH", 2, 2, chlen}, 274 {"chgrp", "Change group of current inode to GROUP", 2, 2, chgroup}, 275 {"chflags", "Change flags of current inode to FLAGS", 2, 2, chaflags}, 276 {"chgen", "Change generation number of current inode to GEN", 2, 2, 277 chgen}, 278 {"mtime", "Change mtime of current inode to MTIME", 2, 2, chmtime}, 279 {"ctime", "Change ctime of current inode to CTIME", 2, 2, chctime}, 280 {"atime", "Change atime of current inode to ATIME", 2, 2, chatime}, 281 {"quit", "Exit", 1, 1, quit}, 282 {"q", "Exit", 1, 1, quit}, 283 {"exit", "Exit", 1, 1, quit}, 284 { .cmd = NULL}, 285 }; 286 287 static int 288 helpfn(int argc, char *argv[]) 289 { 290 struct cmdtable *cmdtp; 291 292 printf("Commands are:\n%-10s %5s %5s %s\n", 293 "command", "min argc", "max argc", "what"); 294 295 for (cmdtp = cmds; cmdtp->cmd; cmdtp++) 296 printf("%-10s %5u %5u %s\n", 297 cmdtp->cmd, cmdtp->minargc, cmdtp->maxargc, cmdtp->helptxt); 298 return 0; 299 } 300 301 static char * 302 prompt(EditLine *el) 303 { 304 static char pstring[64]; 305 snprintf(pstring, sizeof(pstring), "fsdb (inum: %llu)> ", 306 (unsigned long long)curinum); 307 return pstring; 308 } 309 310 311 static int 312 cmdloop(void) 313 { 314 char *line; 315 const char *elline; 316 int cmd_argc, rval = 0, known; 317 #define scratch known 318 char **cmd_argv; 319 struct cmdtable *cmdp; 320 History *hist; 321 HistEvent he; 322 EditLine *elptr; 323 324 curinode = ginode(UFS_ROOTINO); 325 curinum = UFS_ROOTINO; 326 printactive(); 327 328 hist = history_init(); 329 history(hist, &he, H_SETSIZE, 100); /* 100 elt history buffer */ 330 331 elptr = el_init(getprogname(), stdin, stdout, stderr); 332 el_set(elptr, EL_EDITOR, "emacs"); 333 el_set(elptr, EL_PROMPT, prompt); 334 el_set(elptr, EL_HIST, history, hist); 335 el_source(elptr, NULL); 336 337 while ((elline = el_gets(elptr, &scratch)) != NULL && scratch != 0) { 338 if (debug) 339 printf("command `%s'\n", elline); 340 341 history(hist, &he, H_ENTER, elline); 342 343 line = strdup(elline); 344 cmd_argv = crack(line, &cmd_argc); 345 if (cmd_argc) { 346 /* 347 * el_parse returns -1 to signal that it's not been 348 * handled internally. 349 */ 350 if (el_parse(elptr, cmd_argc, (void *)cmd_argv) != -1) 351 continue; 352 known = 0; 353 for (cmdp = cmds; cmdp->cmd; cmdp++) { 354 if (!strcmp(cmdp->cmd, cmd_argv[0])) { 355 if (cmd_argc >= cmdp->minargc && 356 cmd_argc <= cmdp->maxargc) 357 rval = 358 (*cmdp->handler)(cmd_argc, 359 cmd_argv); 360 else 361 rval = argcount(cmdp, cmd_argc, 362 cmd_argv); 363 known = 1; 364 break; 365 } 366 } 367 if (!known) 368 warnx("unknown command `%s'", cmd_argv[0]), 369 rval = 1; 370 } else 371 rval = 0; 372 free(line); 373 if (rval < 0) 374 return rval; 375 if (rval) 376 warnx("rval was %d", rval); 377 } 378 el_end(elptr); 379 history_end(hist); 380 return rval; 381 } 382 383 static ino_t ocurrent; 384 385 #define GETINUM(ac,inum) inum = strtoull(argv[ac], &cp, 0); \ 386 if (inum < UFS_ROOTINO || inum >= maxino || cp == argv[ac] || *cp != '\0' ) { \ 387 printf("inode %llu out of range; range is [%llu,%llu]\n", \ 388 (unsigned long long)inum, (unsigned long long)UFS_ROOTINO, \ 389 (unsigned long long)maxino); \ 390 return 1; \ 391 } 392 393 /* 394 * Focus on given inode number 395 */ 396 CMDFUNC(focus) 397 { 398 ino_t inum; 399 char *cp; 400 401 GETINUM(1, inum); 402 curinode = ginode(inum); 403 ocurrent = curinum; 404 curinum = inum; 405 printactive(); 406 return 0; 407 } 408 409 CMDFUNC(back) 410 { 411 curinum = ocurrent; 412 curinode = ginode(curinum); 413 printactive(); 414 return 0; 415 } 416 417 CMDFUNC(zapi) 418 { 419 ino_t inum; 420 union dinode *dp; 421 char *cp; 422 423 GETINUM(1, inum); 424 dp = ginode(inum); 425 clearinode(dp); 426 inodirty(); 427 if (curinode) /* re-set after potential change */ 428 curinode = ginode(curinum); 429 return 0; 430 } 431 432 CMDFUNC(active) 433 { 434 printactive(); 435 return 0; 436 } 437 438 CMDFUNC(quit) 439 { 440 return -1; 441 } 442 443 CMDFUNC(uplink) 444 { 445 int16_t nlink; 446 447 if (!checkactive()) 448 return 1; 449 nlink = iswap16(DIP(curinode, nlink)); 450 nlink++; 451 DIP_SET(curinode, nlink, iswap16(nlink)); 452 printf("inode %llu link count now %d\n", (unsigned long long)curinum, 453 nlink); 454 inodirty(); 455 return 0; 456 } 457 458 CMDFUNC(downlink) 459 { 460 int16_t nlink; 461 462 if (!checkactive()) 463 return 1; 464 nlink = iswap16(DIP(curinode, nlink)); 465 nlink--; 466 DIP_SET(curinode, nlink, iswap16(nlink)); 467 printf("inode %llu link count now %d\n", (unsigned long long)curinum, 468 nlink); 469 inodirty(); 470 return 0; 471 } 472 473 static const char *typename[] = { 474 "unknown", 475 "fifo", 476 "char special", 477 "unregistered #3", 478 "directory", 479 "unregistered #5", 480 "blk special", 481 "unregistered #7", 482 "regular", 483 "unregistered #9", 484 "symlink", 485 "unregistered #11", 486 "socket", 487 "unregistered #13", 488 "whiteout", 489 }; 490 491 static int slot; 492 493 static int 494 scannames(struct inodesc *idesc) 495 { 496 struct direct *dirp = idesc->id_dirp; 497 498 printf("slot %d ino %d reclen %d: %s, `%.*s'\n", 499 slot++, iswap32(dirp->d_ino), iswap16(dirp->d_reclen), 500 typename[dirp->d_type], 501 dirp->d_namlen, dirp->d_name); 502 return (KEEPON); 503 } 504 505 CMDFUNC(ls) 506 { 507 struct inodesc idesc; 508 checkactivedir(); /* let it go on anyway */ 509 510 slot = 0; 511 idesc.id_number = curinum; 512 idesc.id_func = scannames; 513 idesc.id_type = DATA; 514 idesc.id_fix = IGNORE; 515 ckinode(curinode, &idesc); 516 curinode = ginode(curinum); 517 518 return 0; 519 } 520 521 CMDFUNC(blks) 522 { 523 uint64_t blkno = 0; 524 int i, type; 525 struct wrinfo wrinfo, *wrp = NULL; 526 527 if (strcmp(argv[0], "saveblks") == 0) { 528 wrinfo.fd = open(argv[1], O_WRONLY | O_TRUNC | O_CREAT, 0644); 529 if (wrinfo.fd == -1) { 530 warn("unable to create file %s", argv[1]); 531 return 0; 532 } 533 wrinfo.size = DIP(curinode, size); 534 wrinfo.written_size = 0; 535 wrp = &wrinfo; 536 } 537 if (!curinode) { 538 warnx("no current inode"); 539 return 0; 540 } 541 type = iswap16(DIP(curinode, mode)) & IFMT; 542 if (type != IFDIR && type != IFREG) { 543 warnx("inode %llu not a file or directory", 544 (unsigned long long)curinum); 545 return 0; 546 } 547 if (is_ufs2) { 548 printf("I=%llu %lld blocks\n", (unsigned long long)curinum, 549 (long long)(iswap64(curinode->dp2.di_blocks))); 550 } else { 551 printf("I=%llu %d blocks\n", (unsigned long long)curinum, 552 iswap32(curinode->dp1.di_blocks)); 553 } 554 printf("Direct blocks:\n"); 555 if (is_ufs2) 556 print_blks64(curinode->dp2.di_db, UFS_NDADDR, &blkno, wrp); 557 else 558 print_blks32(curinode->dp1.di_db, UFS_NDADDR, &blkno, wrp); 559 560 if (is_ufs2) { 561 for (i = 0; i < UFS_NIADDR; i++) 562 print_indirblks64(iswap64(curinode->dp2.di_ib[i]), i, 563 &blkno, wrp); 564 } else { 565 for (i = 0; i < UFS_NIADDR; i++) 566 print_indirblks32(iswap32(curinode->dp1.di_ib[i]), i, 567 &blkno, wrp); 568 } 569 return 0; 570 } 571 572 static int findblk_numtofind; 573 static int wantedblksize; 574 CMDFUNC(findblk) 575 { 576 ino_t inum, inosused; 577 uint32_t *wantedblk32 = NULL; 578 uint64_t *wantedblk64 = NULL; 579 struct cg *cgp = cgrp; 580 int i, c; 581 582 ocurrent = curinum; 583 wantedblksize = (argc - 1); 584 if (is_ufs2) { 585 wantedblk64 = malloc(sizeof(uint64_t) * wantedblksize); 586 if (wantedblk64 == NULL) { 587 perror("malloc"); 588 return 1; 589 } 590 memset(wantedblk64, 0, sizeof(uint64_t) * wantedblksize); 591 for (i = 1; i < argc; i++) 592 wantedblk64[i - 1] = 593 FFS_DBTOFSB(sblock, strtoull(argv[i], NULL, 0)); 594 } else { 595 wantedblk32 = malloc(sizeof(uint32_t) * wantedblksize); 596 if (wantedblk32 == NULL) { 597 perror("malloc"); 598 return 1; 599 } 600 memset(wantedblk32, 0, sizeof(uint32_t) * wantedblksize); 601 for (i = 1; i < argc; i++) 602 wantedblk32[i - 1] = 603 FFS_DBTOFSB(sblock, strtoull(argv[i], NULL, 0)); 604 } 605 findblk_numtofind = wantedblksize; 606 for (c = 0; c < sblock->fs_ncg; c++) { 607 inum = c * sblock->fs_ipg; 608 getblk(&cgblk, cgtod(sblock, c), sblock->fs_cgsize); 609 memcpy(cgp, cgblk.b_un.b_cg, sblock->fs_cgsize); 610 if (needswap) 611 ffs_cg_swap(cgblk.b_un.b_cg, cgp, sblock); 612 if (is_ufs2) 613 inosused = cgp->cg_initediblk; 614 else 615 inosused = sblock->fs_ipg; 616 for (; inosused > 0; inum++, inosused--) { 617 if (inum < UFS_ROOTINO) 618 continue; 619 if (is_ufs2 ? compare_blk64(wantedblk64, 620 ino_to_fsba(sblock, inum)) : 621 compare_blk32(wantedblk32, 622 ino_to_fsba(sblock, inum))) { 623 printf("block %llu: inode block (%llu-%llu)\n", 624 (unsigned long long)FFS_FSBTODB(sblock, 625 ino_to_fsba(sblock, inum)), 626 (unsigned long long) 627 (inum / FFS_INOPB(sblock)) * FFS_INOPB(sblock), 628 (unsigned long long) 629 (inum / FFS_INOPB(sblock) + 1) * FFS_INOPB(sblock)); 630 findblk_numtofind--; 631 if (findblk_numtofind == 0) 632 goto end; 633 } 634 curinum = inum; 635 curinode = ginode(inum); 636 switch (iswap16(DIP(curinode, mode)) & IFMT) { 637 case IFDIR: 638 case IFREG: 639 if (DIP(curinode, blocks) == 0) 640 continue; 641 break; 642 case IFLNK: 643 { 644 uint64_t size = iswap64(DIP(curinode, size)); 645 if (size > 0 && 646 size < (uint64_t)sblock->fs_maxsymlinklen && 647 DIP(curinode, blocks) == 0) 648 continue; 649 else 650 break; 651 } 652 default: 653 continue; 654 } 655 if (is_ufs2 ? 656 find_blks64(curinode->dp2.di_db, UFS_NDADDR, 657 wantedblk64) : 658 find_blks32(curinode->dp1.di_db, UFS_NDADDR, 659 wantedblk32)) 660 goto end; 661 for (i = 0; i < UFS_NIADDR; i++) { 662 if (is_ufs2 ? 663 compare_blk64(wantedblk64, 664 iswap64(curinode->dp2.di_ib[i])) : 665 compare_blk32(wantedblk32, 666 iswap32(curinode->dp1.di_ib[i]))) 667 if (founddatablk(is_ufs2 ? 668 iswap64(curinode->dp2.di_ib[i]) : 669 iswap32(curinode->dp1.di_ib[i]))) 670 goto end; 671 if (is_ufs2 ? (curinode->dp2.di_ib[i] != 0) : 672 (curinode->dp1.di_ib[i] != 0)) 673 if (is_ufs2 ? 674 find_indirblks64( 675 iswap64(curinode->dp2.di_ib[i]), 676 i, wantedblk64) : 677 find_indirblks32( 678 iswap32(curinode->dp1.di_ib[i]), 679 i, wantedblk32)) 680 goto end; 681 } 682 } 683 } 684 end: 685 if (wantedblk32) 686 free(wantedblk32); 687 if (wantedblk64) 688 free(wantedblk64); 689 curinum = ocurrent; 690 curinode = ginode(curinum); 691 return 0; 692 } 693 694 static int 695 compare_blk32(uint32_t *wantedblk, uint32_t curblk) 696 { 697 int i; 698 for (i = 0; i < wantedblksize; i++) { 699 if (wantedblk[i] != 0 && wantedblk[i] == curblk) { 700 wantedblk[i] = 0; 701 return 1; 702 } 703 } 704 return 0; 705 } 706 707 static int 708 compare_blk64(uint64_t *wantedblk, uint64_t curblk) 709 { 710 int i; 711 for (i = 0; i < wantedblksize; i++) { 712 if (wantedblk[i] != 0 && wantedblk[i] == curblk) { 713 wantedblk[i] = 0; 714 return 1; 715 } 716 } 717 return 0; 718 } 719 720 static int 721 founddatablk(uint64_t blk) 722 { 723 printf("%llu: data block of inode %llu\n", 724 (unsigned long long)FFS_FSBTODB(sblock, blk), 725 (unsigned long long)curinum); 726 findblk_numtofind--; 727 if (findblk_numtofind == 0) 728 return 1; 729 return 0; 730 } 731 732 static int 733 find_blks32(uint32_t *buf, int size, uint32_t *wantedblk) 734 { 735 int blk; 736 for(blk = 0; blk < size; blk++) { 737 if (buf[blk] == 0) 738 continue; 739 if (compare_blk32(wantedblk, iswap32(buf[blk]))) { 740 if (founddatablk(iswap32(buf[blk]))) 741 return 1; 742 } 743 } 744 return 0; 745 } 746 747 static int 748 find_indirblks32(uint32_t blk, int ind_level, uint32_t *wantedblk) 749 { 750 #define MAXNINDIR (MAXBSIZE / sizeof(uint32_t)) 751 uint32_t idblk[MAXNINDIR]; 752 size_t i; 753 754 bread(fsreadfd, (char *)idblk, FFS_FSBTODB(sblock, blk), 755 (int)sblock->fs_bsize); 756 if (ind_level <= 0) { 757 if (find_blks32(idblk, 758 sblock->fs_bsize / sizeof(uint32_t), wantedblk)) 759 return 1; 760 } else { 761 ind_level--; 762 for (i = 0; i < sblock->fs_bsize / sizeof(uint32_t); i++) { 763 if (compare_blk32(wantedblk, iswap32(idblk[i]))) { 764 if (founddatablk(iswap32(idblk[i]))) 765 return 1; 766 } 767 if(idblk[i] != 0) 768 if (find_indirblks32(iswap32(idblk[i]), 769 ind_level, wantedblk)) 770 return 1; 771 } 772 } 773 #undef MAXNINDIR 774 return 0; 775 } 776 777 778 static int 779 find_blks64(uint64_t *buf, int size, uint64_t *wantedblk) 780 { 781 int blk; 782 for(blk = 0; blk < size; blk++) { 783 if (buf[blk] == 0) 784 continue; 785 if (compare_blk64(wantedblk, iswap64(buf[blk]))) { 786 if (founddatablk(iswap64(buf[blk]))) 787 return 1; 788 } 789 } 790 return 0; 791 } 792 793 static int 794 find_indirblks64(uint64_t blk, int ind_level, uint64_t *wantedblk) 795 { 796 #define MAXNINDIR (MAXBSIZE / sizeof(uint64_t)) 797 uint64_t idblk[MAXNINDIR]; 798 size_t i; 799 800 bread(fsreadfd, (char *)idblk, FFS_FSBTODB(sblock, blk), 801 (int)sblock->fs_bsize); 802 if (ind_level <= 0) { 803 if (find_blks64(idblk, 804 sblock->fs_bsize / sizeof(uint64_t), wantedblk)) 805 return 1; 806 } else { 807 ind_level--; 808 for (i = 0; i < sblock->fs_bsize / sizeof(uint64_t); i++) { 809 if (compare_blk64(wantedblk, iswap64(idblk[i]))) { 810 if (founddatablk(iswap64(idblk[i]))) 811 return 1; 812 } 813 if (idblk[i] != 0) 814 if (find_indirblks64(iswap64(idblk[i]), 815 ind_level, wantedblk)) 816 return 1; 817 } 818 } 819 #undef MAXNINDIR 820 return 0; 821 } 822 823 static int 824 writefileblk(struct wrinfo *wrp, uint64_t blk) 825 { 826 char buf[MAXBSIZE]; 827 long long size; 828 829 size = wrp->size - wrp->written_size; 830 if (size > sblock->fs_bsize) 831 size = sblock->fs_bsize; 832 if (size > (long long)sizeof buf) { 833 warnx("sblock->fs_bsize > MAX_BSIZE"); 834 return -1; 835 } 836 837 if (bread(fsreadfd, buf, FFS_FSBTODB(sblock, blk), size) != 0) 838 return -1; 839 if (write(wrp->fd, buf, size) != size) 840 return -1; 841 wrp->written_size += size; 842 return 0; 843 } 844 845 846 #define CHARS_PER_LINES 70 847 848 static void 849 print_blks32(int32_t *buf, int size, uint64_t *blknum, struct wrinfo *wrp) 850 { 851 int chars; 852 char prbuf[CHARS_PER_LINES+1]; 853 int blk; 854 855 chars = 0; 856 for (blk = 0; blk < size; blk++, (*blknum)++) { 857 if (buf[blk] == 0) 858 continue; 859 if (wrp && writefileblk(wrp, iswap32(buf[blk])) != 0) { 860 warn("unable to write block %d", iswap32(buf[blk])); 861 return; 862 } 863 snprintf(prbuf, CHARS_PER_LINES, "%d ", iswap32(buf[blk])); 864 if ((chars + strlen(prbuf)) > CHARS_PER_LINES) { 865 printf("\n"); 866 chars = 0; 867 } 868 if (chars == 0) 869 printf("%" PRIu64 ": ", *blknum); 870 printf("%s", prbuf); 871 chars += strlen(prbuf); 872 } 873 printf("\n"); 874 } 875 876 static void 877 print_blks64(int64_t *buf, int size, uint64_t *blknum, struct wrinfo *wrp) 878 { 879 int chars; 880 char prbuf[CHARS_PER_LINES+1]; 881 int blk; 882 883 chars = 0; 884 for (blk = 0; blk < size; blk++, (*blknum)++) { 885 if (buf[blk] == 0) 886 continue; 887 if (wrp && writefileblk(wrp, iswap64(buf[blk])) != 0) { 888 warn("unable to write block %lld", 889 (long long)iswap64(buf[blk])); 890 return; 891 } 892 snprintf(prbuf, CHARS_PER_LINES, "%lld ", 893 (long long)iswap64(buf[blk])); 894 if ((chars + strlen(prbuf)) > CHARS_PER_LINES) { 895 printf("\n"); 896 chars = 0; 897 } 898 if (chars == 0) 899 printf("%" PRIu64 ": ", *blknum); 900 printf("%s", prbuf); 901 chars += strlen(prbuf); 902 } 903 printf("\n"); 904 } 905 906 #undef CHARS_PER_LINES 907 908 static void 909 print_indirblks32(uint32_t blk, int ind_level, uint64_t *blknum, struct wrinfo *wrp) 910 { 911 #define MAXNINDIR (MAXBSIZE / sizeof(int32_t)) 912 const int ptrperblk_shift = sblock->fs_bshift - 2; 913 const int ptrperblk = 1 << ptrperblk_shift; 914 int32_t idblk[MAXNINDIR]; 915 int i; 916 917 if (blk == 0) { 918 *blknum += (uint64_t)ptrperblk << (ptrperblk_shift * ind_level); 919 return; 920 } 921 922 printf("Indirect block %lld (level %d):\n", (long long)blk, 923 ind_level+1); 924 bread(fsreadfd, (char *)idblk, FFS_FSBTODB(sblock, blk), 925 (int)sblock->fs_bsize); 926 if (ind_level <= 0) { 927 print_blks32(idblk, ptrperblk, blknum, wrp); 928 } else { 929 ind_level--; 930 for (i = 0; i < ptrperblk; i++) 931 print_indirblks32(iswap32(idblk[i]), ind_level, blknum, 932 wrp); 933 } 934 #undef MAXNINDIR 935 } 936 937 static void 938 print_indirblks64(uint64_t blk, int ind_level, uint64_t *blknum, struct wrinfo *wrp) 939 { 940 #define MAXNINDIR (MAXBSIZE / sizeof(int64_t)) 941 const int ptrperblk_shift = sblock->fs_bshift - 3; 942 const int ptrperblk = 1 << ptrperblk_shift; 943 int64_t idblk[MAXNINDIR]; 944 int i; 945 946 if (blk == 0) { 947 *blknum += (uint64_t)ptrperblk << (ptrperblk_shift * ind_level); 948 return; 949 } 950 951 printf("Indirect block %lld (level %d):\n", (long long)blk, 952 ind_level+1); 953 bread(fsreadfd, (char *)idblk, FFS_FSBTODB(sblock, blk), 954 (int)sblock->fs_bsize); 955 if (ind_level <= 0) { 956 print_blks64(idblk, ptrperblk, blknum, wrp); 957 } else { 958 ind_level--; 959 for (i = 0; i < ptrperblk; i++) 960 print_indirblks64(iswap64(idblk[i]), ind_level, blknum, 961 wrp); 962 } 963 #undef MAXNINDIR 964 } 965 966 static int 967 dolookup(char *name) 968 { 969 struct inodesc idesc; 970 971 if (!checkactivedir()) 972 return 0; 973 idesc.id_number = curinum; 974 idesc.id_func = findino; 975 idesc.id_name = name; 976 idesc.id_type = DATA; 977 idesc.id_fix = IGNORE; 978 if (ckinode(curinode, &idesc) & FOUND) { 979 curinum = idesc.id_parent; 980 curinode = ginode(curinum); 981 printactive(); 982 return 1; 983 } else { 984 warnx("name `%s' not found in current inode directory", name); 985 return 0; 986 } 987 } 988 989 CMDFUNC(focusname) 990 { 991 char *p, *val; 992 993 if (!checkactive()) 994 return 1; 995 996 ocurrent = curinum; 997 998 if (argv[1][0] == '/') { 999 curinum = UFS_ROOTINO; 1000 curinode = ginode(UFS_ROOTINO); 1001 } else { 1002 if (!checkactivedir()) 1003 return 1; 1004 } 1005 for (p = argv[1]; p != NULL;) { 1006 while ((val = strsep(&p, "/")) != NULL && *val == '\0'); 1007 if (val) { 1008 printf("component `%s': ", val); 1009 fflush(stdout); 1010 if (!dolookup(val)) { 1011 curinode = ginode(curinum); 1012 return (1); 1013 } 1014 } 1015 } 1016 return 0; 1017 } 1018 1019 CMDFUNC(ln) 1020 { 1021 ino_t inum; 1022 int rval; 1023 char *cp; 1024 1025 GETINUM(1, inum); 1026 1027 if (!checkactivedir()) 1028 return 1; 1029 rval = makeentry(curinum, inum, argv[2]); 1030 if (rval) 1031 printf("Ino %llu entered as `%s'\n", (unsigned long long)inum, 1032 argv[2]); 1033 else 1034 printf("could not enter name? weird.\n"); 1035 curinode = ginode(curinum); 1036 return rval; 1037 } 1038 1039 CMDFUNC(rm) 1040 { 1041 int rval; 1042 1043 if (!checkactivedir()) 1044 return 1; 1045 rval = changeino(curinum, argv[1], 0); 1046 if (rval & ALTERED) { 1047 printf("Name `%s' removed\n", argv[1]); 1048 return 0; 1049 } else { 1050 printf("could not remove name? weird.\n"); 1051 return 1; 1052 } 1053 } 1054 1055 static long slotcount, desired; 1056 1057 static int 1058 chinumfunc(struct inodesc *idesc) 1059 { 1060 struct direct *dirp = idesc->id_dirp; 1061 1062 if (slotcount++ == desired) { 1063 dirp->d_ino = iswap32(idesc->id_parent); 1064 return STOP | ALTERED | FOUND; 1065 } 1066 return KEEPON; 1067 } 1068 1069 CMDFUNC(chinum) 1070 { 1071 char *cp; 1072 ino_t inum; 1073 struct inodesc idesc; 1074 1075 slotcount = 0; 1076 if (!checkactivedir()) 1077 return 1; 1078 GETINUM(2, inum); 1079 1080 desired = strtol(argv[1], &cp, 0); 1081 if (cp == argv[1] || *cp != '\0' || desired < 0) { 1082 printf("invalid slot number `%s'\n", argv[1]); 1083 return 1; 1084 } 1085 idesc.id_number = curinum; 1086 idesc.id_func = chinumfunc; 1087 idesc.id_fix = IGNORE; 1088 idesc.id_type = DATA; 1089 idesc.id_parent = inum; /* XXX convenient hiding place */ 1090 1091 if (ckinode(curinode, &idesc) & FOUND) 1092 return 0; 1093 else { 1094 warnx("no %sth slot in current directory", argv[1]); 1095 return 1; 1096 } 1097 } 1098 1099 static int 1100 chnamefunc(struct inodesc *idesc) 1101 { 1102 struct direct *dirp = idesc->id_dirp; 1103 struct direct testdir; 1104 1105 if (slotcount++ == desired) { 1106 /* will name fit? */ 1107 testdir.d_namlen = strlen(idesc->id_name); 1108 if (UFS_DIRSIZ(UFS_NEWDIRFMT, &testdir, 0) <= iswap16(dirp->d_reclen)) { 1109 dirp->d_namlen = testdir.d_namlen; 1110 strlcpy(dirp->d_name, idesc->id_name, 1111 sizeof(dirp->d_name)); 1112 return STOP | ALTERED | FOUND; 1113 } else 1114 return STOP | FOUND; /* won't fit, so give up */ 1115 } 1116 return KEEPON; 1117 } 1118 1119 CMDFUNC(chname) 1120 { 1121 int rval; 1122 char *cp; 1123 struct inodesc idesc; 1124 1125 slotcount = 0; 1126 if (!checkactivedir()) 1127 return 1; 1128 1129 desired = strtoul(argv[1], &cp, 0); 1130 if (cp == argv[1] || *cp != '\0') { 1131 printf("invalid slot number `%s'\n", argv[1]); 1132 return 1; 1133 } 1134 idesc.id_number = curinum; 1135 idesc.id_func = chnamefunc; 1136 idesc.id_fix = IGNORE; 1137 idesc.id_type = DATA; 1138 idesc.id_name = argv[2]; 1139 1140 rval = ckinode(curinode, &idesc); 1141 if ((rval & (FOUND | ALTERED)) == (FOUND | ALTERED)) 1142 return 0; 1143 else 1144 if (rval & FOUND) { 1145 warnx("new name `%s' does not fit in slot %s", 1146 argv[2], argv[1]); 1147 return 1; 1148 } else { 1149 warnx("no %sth slot in current directory", argv[1]); 1150 return 1; 1151 } 1152 } 1153 1154 static struct typemap { 1155 const char *typename; 1156 int typebits; 1157 } typenamemap[] = { 1158 { "file", IFREG }, 1159 { "dir", IFDIR }, 1160 { "socket", IFSOCK }, 1161 { "fifo", IFIFO }, 1162 }; 1163 1164 CMDFUNC(newtype) 1165 { 1166 int type; 1167 uint16_t mode; 1168 struct typemap *tp; 1169 1170 if (!checkactive()) 1171 return 1; 1172 mode = iswap16(DIP(curinode, mode)); 1173 type = mode & IFMT; 1174 for (tp = typenamemap; 1175 tp < &typenamemap[sizeof(typenamemap) / sizeof(*typenamemap)]; 1176 tp++) { 1177 if (!strcmp(argv[1], tp->typename)) { 1178 printf("setting type to %s\n", tp->typename); 1179 type = tp->typebits; 1180 break; 1181 } 1182 } 1183 if (tp == &typenamemap[sizeof(typenamemap) / sizeof(*typenamemap)]) { 1184 warnx("type `%s' not known", argv[1]); 1185 warnx("try one of `file', `dir', `socket', `fifo'"); 1186 return 1; 1187 } 1188 DIP_SET(curinode, mode, iswap16((mode & ~IFMT) | type)); 1189 inodirty(); 1190 printactive(); 1191 return 0; 1192 } 1193 1194 CMDFUNC(chmode) 1195 { 1196 long modebits; 1197 char *cp; 1198 uint16_t mode; 1199 1200 if (!checkactive()) 1201 return 1; 1202 1203 modebits = strtol(argv[1], &cp, 8); 1204 if (cp == argv[1] || *cp != '\0') { 1205 warnx("bad modebits `%s'", argv[1]); 1206 return 1; 1207 } 1208 mode = iswap16(DIP(curinode, mode)); 1209 DIP_SET(curinode, mode, iswap16((mode & ~07777) | modebits)); 1210 inodirty(); 1211 printactive(); 1212 return 0; 1213 } 1214 1215 CMDFUNC(chlen) 1216 { 1217 long len; 1218 char *cp; 1219 1220 if (!checkactive()) 1221 return 1; 1222 1223 len = strtol(argv[1], &cp, 0); 1224 if (cp == argv[1] || *cp != '\0' || len < 0) { 1225 warnx("bad length '%s'", argv[1]); 1226 return 1; 1227 } 1228 DIP_SET(curinode, size, iswap64(len)); 1229 inodirty(); 1230 printactive(); 1231 return 0; 1232 } 1233 1234 CMDFUNC(chaflags) 1235 { 1236 u_long flags; 1237 char *cp; 1238 1239 if (!checkactive()) 1240 return 1; 1241 1242 flags = strtoul(argv[1], &cp, 0); 1243 if (cp == argv[1] || *cp != '\0') { 1244 warnx("bad flags `%s'", argv[1]); 1245 return 1; 1246 } 1247 if (flags > UINT_MAX) { 1248 warnx("flags set beyond 32-bit range of field (0x%lx)", 1249 flags); 1250 return (1); 1251 } 1252 DIP_SET(curinode, flags, iswap32(flags)); 1253 inodirty(); 1254 printactive(); 1255 return 0; 1256 } 1257 1258 CMDFUNC(chgen) 1259 { 1260 long gen; 1261 char *cp; 1262 1263 if (!checkactive()) 1264 return 1; 1265 1266 gen = strtol(argv[1], &cp, 0); 1267 if (cp == argv[1] || *cp != '\0') { 1268 warnx("bad gen `%s'", argv[1]); 1269 return 1; 1270 } 1271 if (gen > INT_MAX || gen < INT_MIN) { 1272 warnx("gen set beyond 32-bit range of field (0x%lx)", gen); 1273 return (1); 1274 } 1275 DIP_SET(curinode, gen, iswap32(gen)); 1276 inodirty(); 1277 printactive(); 1278 return 0; 1279 } 1280 1281 CMDFUNC(linkcount) 1282 { 1283 int lcnt; 1284 char *cp; 1285 1286 if (!checkactive()) 1287 return 1; 1288 1289 lcnt = strtol(argv[1], &cp, 0); 1290 if (cp == argv[1] || *cp != '\0') { 1291 warnx("bad link count `%s'", argv[1]); 1292 return 1; 1293 } 1294 if (lcnt > USHRT_MAX || lcnt < 0) { 1295 warnx("max link count is %d", USHRT_MAX); 1296 return 1; 1297 } 1298 DIP_SET(curinode, nlink, iswap16(lcnt)); 1299 inodirty(); 1300 printactive(); 1301 return 0; 1302 } 1303 1304 CMDFUNC(chowner) 1305 { 1306 unsigned long uid; 1307 char *cp; 1308 struct passwd *pwd; 1309 1310 if (!checkactive()) 1311 return 1; 1312 1313 uid = strtoul(argv[1], &cp, 0); 1314 if (cp == argv[1] || *cp != '\0') { 1315 /* try looking up name */ 1316 if ((pwd = getpwnam(argv[1])) != 0) { 1317 uid = pwd->pw_uid; 1318 } else { 1319 warnx("bad uid `%s'", argv[1]); 1320 return 1; 1321 } 1322 } 1323 if (!is_ufs2 && sblock->fs_old_inodefmt < FS_44INODEFMT) 1324 curinode->dp1.di_ouid = iswap32(uid); 1325 else 1326 DIP_SET(curinode, uid, iswap32(uid)); 1327 inodirty(); 1328 printactive(); 1329 return 0; 1330 } 1331 1332 CMDFUNC(chgroup) 1333 { 1334 unsigned long gid; 1335 char *cp; 1336 struct group *grp; 1337 1338 if (!checkactive()) 1339 return 1; 1340 1341 gid = strtoul(argv[1], &cp, 0); 1342 if (cp == argv[1] || *cp != '\0') { 1343 if ((grp = getgrnam(argv[1])) != 0) { 1344 gid = grp->gr_gid; 1345 } else { 1346 warnx("bad gid `%s'", argv[1]); 1347 return 1; 1348 } 1349 } 1350 if (!is_ufs2 && sblock->fs_old_inodefmt < FS_44INODEFMT) 1351 curinode->dp1.di_ogid = iswap32(gid); 1352 else 1353 DIP_SET(curinode, gid, iswap32(gid)); 1354 inodirty(); 1355 printactive(); 1356 return 0; 1357 } 1358 1359 static int 1360 dotime(char *name, int32_t *rsec, int32_t *rnsec) 1361 { 1362 char *p, *val; 1363 struct tm t; 1364 int32_t sec; 1365 int32_t nsec; 1366 p = strchr(name, '.'); 1367 if (p) { 1368 *p = '\0'; 1369 nsec = strtoul(++p, &val, 0); 1370 if (val == p || *val != '\0' || nsec >= 1000000000 || nsec < 0) { 1371 warnx("invalid nanoseconds"); 1372 goto badformat; 1373 } 1374 } else 1375 nsec = 0; 1376 if (strlen(name) != 14) { 1377 badformat: 1378 warnx("date format: YYYYMMDDHHMMSS[.nsec]"); 1379 return 1; 1380 } 1381 for (p = name; *p; p++) 1382 if (*p < '0' || *p > '9') 1383 goto badformat; 1384 1385 p = name; 1386 #define VAL() ((*p++) - '0') 1387 t.tm_year = VAL(); 1388 t.tm_year = VAL() + t.tm_year * 10; 1389 t.tm_year = VAL() + t.tm_year * 10; 1390 t.tm_year = VAL() + t.tm_year * 10 - 1900; 1391 t.tm_mon = VAL(); 1392 t.tm_mon = VAL() + t.tm_mon * 10 - 1; 1393 t.tm_mday = VAL(); 1394 t.tm_mday = VAL() + t.tm_mday * 10; 1395 t.tm_hour = VAL(); 1396 t.tm_hour = VAL() + t.tm_hour * 10; 1397 t.tm_min = VAL(); 1398 t.tm_min = VAL() + t.tm_min * 10; 1399 t.tm_sec = VAL(); 1400 t.tm_sec = VAL() + t.tm_sec * 10; 1401 t.tm_isdst = -1; 1402 1403 sec = mktime(&t); 1404 if (sec == -1) { 1405 warnx("date/time out of range"); 1406 return 1; 1407 } 1408 *rsec = iswap32(sec); 1409 *rnsec = iswap32(nsec); 1410 return 0; 1411 } 1412 1413 CMDFUNC(chmtime) 1414 { 1415 int32_t rsec, nsec; 1416 1417 if (dotime(argv[1], &rsec, &nsec)) 1418 return 1; 1419 DIP_SET(curinode, mtime, rsec); 1420 DIP_SET(curinode, mtimensec, nsec); 1421 inodirty(); 1422 printactive(); 1423 return 0; 1424 } 1425 1426 CMDFUNC(chatime) 1427 { 1428 int32_t rsec, nsec; 1429 1430 if (dotime(argv[1], &rsec, &nsec)) 1431 return 1; 1432 DIP_SET(curinode, atime, rsec); 1433 DIP_SET(curinode, atimensec, nsec); 1434 inodirty(); 1435 printactive(); 1436 return 0; 1437 } 1438 1439 CMDFUNC(chctime) 1440 { 1441 int32_t rsec, nsec; 1442 1443 if (dotime(argv[1], &rsec, &nsec)) 1444 return 1; 1445 DIP_SET(curinode, ctime, rsec); 1446 DIP_SET(curinode, ctimensec, nsec); 1447 inodirty(); 1448 printactive(); 1449 return 0; 1450 } 1451