Commit | Line | Data |
---|---|---|
31cef89c BJ |
1 | static char *sccsid = "@(#)fsck.c 4.10 (Berkeley) 11/15/80"; |
2 | #include <stdio.h> | |
3 | #include <ctype.h> | |
4 | #include <sys/param.h> | |
5 | #include <sys/filsys.h> | |
6 | #include <sys/dir.h> | |
7 | #include <sys/fblk.h> | |
8 | #include <sys/ino.h> | |
9 | #include <sys/inode.h> | |
10 | #include <sys/stat.h> | |
11 | #include <fstab.h> | |
12 | ||
13 | typedef int (*SIG_TYP)(); | |
14 | ||
15 | #define NDIRECT (BSIZE/sizeof(struct direct)) | |
16 | #define SPERB (BSIZE/sizeof(short)) | |
17 | ||
18 | #define NO 0 | |
19 | #define YES 1 | |
20 | ||
21 | #define MAXDUP 10 /* limit on dup blks (per inode) */ | |
22 | #define MAXBAD 10 /* limit on bad blks (per inode) */ | |
23 | ||
24 | #define STEPSIZE 9 /* default step for freelist spacing */ | |
25 | #define CYLSIZE 400 /* default cyl size for spacing */ | |
26 | #define MAXCYL 500 /* maximum cylinder size */ | |
27 | ||
28 | #define BITSPB 8 /* number bits per byte */ | |
29 | #define BITSHIFT 3 /* log2(BITSPB) */ | |
30 | #define BITMASK 07 /* BITSPB-1 */ | |
31 | #define LSTATE 2 /* bits per inode state */ | |
32 | #define STATEPB (BITSPB/LSTATE) /* inode states per byte */ | |
33 | #define USTATE 0 /* inode not allocated */ | |
34 | #define FSTATE 01 /* inode is file */ | |
35 | #define DSTATE 02 /* inode is directory */ | |
36 | #define CLEAR 03 /* inode is to be cleared */ | |
37 | #define SMASK 03 /* mask for inode state */ | |
38 | ||
39 | typedef struct dinode DINODE; | |
40 | typedef struct direct DIRECT; | |
41 | ||
42 | #define ALLOC ((dp->di_mode & IFMT) != 0) | |
43 | #define DIR ((dp->di_mode & IFMT) == IFDIR) | |
44 | #define REG ((dp->di_mode & IFMT) == IFREG) | |
45 | #define BLK ((dp->di_mode & IFMT) == IFBLK) | |
46 | #define CHR ((dp->di_mode & IFMT) == IFCHR) | |
47 | #define MPC ((dp->di_mode & IFMT) == IFMPC) | |
48 | #define MPB ((dp->di_mode & IFMT) == IFMPB) | |
49 | #define SPECIAL (BLK || CHR || MPC || MPB) | |
50 | ||
51 | #define NINOBLK 11 /* num blks for raw reading */ | |
52 | #define MAXRAW 110 /* largest raw read (in blks) */ | |
53 | daddr_t startib; /* blk num of first in raw area */ | |
54 | unsigned niblk; /* num of blks in raw area */ | |
55 | ||
56 | struct bufarea { | |
57 | struct bufarea *b_next; /* must be first */ | |
58 | daddr_t b_bno; | |
59 | union { | |
60 | char b_buf[BSIZE]; /* buffer space */ | |
61 | short b_lnks[SPERB]; /* link counts */ | |
62 | daddr_t b_indir[NINDIR]; /* indirect block */ | |
63 | struct filsys b_fs; /* super block */ | |
64 | struct fblk b_fb; /* free block */ | |
65 | struct dinode b_dinode[INOPB]; /* inode block */ | |
66 | DIRECT b_dir[NDIRECT]; /* directory */ | |
67 | } b_un; | |
68 | char b_dirty; | |
69 | }; | |
70 | ||
71 | typedef struct bufarea BUFAREA; | |
72 | ||
73 | BUFAREA inoblk; /* inode blocks */ | |
74 | BUFAREA fileblk; /* other blks in filesys */ | |
75 | BUFAREA sblk; /* file system superblock */ | |
76 | BUFAREA *poolhead; /* ptr to first buffer in pool */ | |
77 | ||
78 | #define initbarea(x) (x)->b_dirty = 0;(x)->b_bno = (daddr_t)-1 | |
79 | #define dirty(x) (x)->b_dirty = 1 | |
80 | #define inodirty() inoblk.b_dirty = 1 | |
81 | #define fbdirty() fileblk.b_dirty = 1 | |
82 | #define sbdirty() sblk.b_dirty = 1 | |
83 | ||
84 | #define freeblk fileblk.b_un.b_fb | |
85 | #define dirblk fileblk.b_un | |
86 | #define superblk sblk.b_un.b_fs | |
87 | ||
88 | struct filecntl { | |
89 | int rfdes; | |
90 | int wfdes; | |
91 | int mod; | |
92 | }; | |
93 | ||
94 | struct filecntl dfile; /* file descriptors for filesys */ | |
95 | struct filecntl sfile; /* file descriptors for scratch file */ | |
96 | ||
97 | typedef unsigned MEMSIZE; | |
98 | ||
99 | MEMSIZE memsize; /* amt of memory we got */ | |
100 | #ifdef pdp11 | |
101 | #define MAXDATA ((MEMSIZE)54*1024) | |
102 | #endif | |
103 | #ifdef vax | |
104 | #define MAXDATA ((MEMSIZE)400*1024) | |
105 | #endif | |
106 | ||
107 | #define DUPTBLSIZE 100 /* num of dup blocks to remember */ | |
108 | daddr_t duplist[DUPTBLSIZE]; /* dup block table */ | |
109 | daddr_t *enddup; /* next entry in dup table */ | |
110 | daddr_t *muldup; /* multiple dups part of table */ | |
111 | ||
112 | #define MAXLNCNT 20 /* num zero link cnts to remember */ | |
113 | ino_t badlncnt[MAXLNCNT]; /* table of inos with zero link cnts */ | |
114 | ino_t *badlnp; /* next entry in table */ | |
115 | ||
116 | char sflag; /* salvage free block list */ | |
117 | char csflag; /* salvage free block list (conditional) */ | |
118 | char nflag; /* assume a no response */ | |
119 | char yflag; /* assume a yes response */ | |
120 | char tflag; /* scratch file specified */ | |
121 | char preen; /* just fix normal inconsistencies */ | |
122 | char rplyflag; /* any questions asked? */ | |
123 | char hotroot; /* checking root device */ | |
124 | char rawflg; /* read raw device */ | |
125 | char rmscr; /* remove scratch file when done */ | |
126 | char fixfree; /* corrupted free list */ | |
127 | char *membase; /* base of memory we get */ | |
128 | char *blkmap; /* ptr to primary blk allocation map */ | |
129 | char *freemap; /* ptr to secondary blk allocation map */ | |
130 | char *statemap; /* ptr to inode state table */ | |
131 | char *pathp; /* pointer to pathname position */ | |
132 | char *thisname; /* ptr to current pathname component */ | |
133 | char *srchname; /* name being searched for in dir */ | |
134 | char pathname[200]; | |
135 | char scrfile[80]; | |
136 | char *lfname = "lost+found"; | |
137 | char *checklist = FSTAB; | |
138 | ||
139 | short *lncntp; /* ptr to link count table */ | |
140 | ||
141 | int cylsize; /* num blocks per cylinder */ | |
142 | int stepsize; /* num blocks for spacing purposes */ | |
143 | int badblk; /* num of bad blks seen (per inode) */ | |
144 | int dupblk; /* num of dup blks seen (per inode) */ | |
145 | int (*pfunc)(); /* function to call to chk blk */ | |
146 | ||
147 | ino_t inum; /* inode we are currently working on */ | |
148 | ino_t imax; /* number of inodes */ | |
149 | ino_t parentdir; /* i number of parent directory */ | |
150 | ino_t lastino; /* hiwater mark of inodes */ | |
151 | ino_t lfdir; /* lost & found directory */ | |
152 | ino_t orphan; /* orphaned inode */ | |
153 | ||
154 | off_t filsize; /* num blks seen in file */ | |
155 | off_t maxblk; /* largest logical blk in file */ | |
156 | off_t bmapsz; /* num chars in blkmap */ | |
157 | ||
158 | daddr_t smapblk; /* starting blk of state map */ | |
159 | daddr_t lncntblk; /* starting blk of link cnt table */ | |
160 | daddr_t fmapblk; /* starting blk of free map */ | |
161 | daddr_t n_free; /* number of free blocks */ | |
162 | daddr_t n_blks; /* number of blocks used */ | |
163 | daddr_t n_files; /* number of files seen */ | |
164 | daddr_t fmin; /* block number of the first data block */ | |
165 | daddr_t fmax; /* number of blocks in the volume */ | |
166 | ||
167 | #define howmany(x,y) (((x)+((y)-1))/(y)) | |
168 | #define roundup(x,y) ((((x)+((y)-1))/(y))*(y)) | |
169 | #define outrange(x) (x < fmin || x >= fmax) | |
170 | #define zapino(x) clear((char *)(x),sizeof(DINODE)) | |
171 | ||
172 | #define setlncnt(x) dolncnt(x,0) | |
173 | #define getlncnt() dolncnt(0,1) | |
174 | #define declncnt() dolncnt(0,2) | |
175 | ||
176 | #define setbmap(x) domap(x,0) | |
177 | #define getbmap(x) domap(x,1) | |
178 | #define clrbmap(x) domap(x,2) | |
179 | ||
180 | #define setfmap(x) domap(x,0+4) | |
181 | #define getfmap(x) domap(x,1+4) | |
182 | #define clrfmap(x) domap(x,2+4) | |
183 | ||
184 | #define setstate(x) dostate(x,0) | |
185 | #define getstate() dostate(0,1) | |
186 | ||
187 | #define DATA 1 | |
188 | #define ADDR 0 | |
189 | #define ALTERD 010 | |
190 | #define KEEPON 04 | |
191 | #define SKIP 02 | |
192 | #define STOP 01 | |
193 | ||
194 | int (*signal())(); | |
195 | long lseek(); | |
196 | long time(); | |
197 | DINODE *ginode(); | |
198 | BUFAREA *getblk(); | |
199 | BUFAREA *search(); | |
200 | int dirscan(); | |
201 | int findino(); | |
202 | int catch(); | |
203 | int mkentry(); | |
204 | int chgdd(); | |
205 | int pass1(); | |
206 | int pass1b(); | |
207 | int pass2(); | |
208 | int pass3(); | |
209 | int pass4(); | |
210 | int pass5(); | |
211 | ||
212 | char *devname; | |
213 | ||
214 | main(argc,argv) | |
215 | int argc; | |
216 | char *argv[]; | |
217 | { | |
218 | register FILE *fp; | |
219 | register n; | |
220 | register char *p; | |
221 | char filename[50]; | |
222 | char *sbrk(); | |
223 | ||
224 | sync(); | |
225 | while(--argc > 0 && **++argv == '-') { | |
226 | switch(*++*argv) { | |
227 | case 'p': | |
228 | preen++; | |
229 | break; | |
230 | case 't': | |
231 | case 'T': | |
232 | tflag++; | |
233 | if(**++argv == '-' || --argc <= 0) | |
234 | errexit("Bad -t option\n"); | |
235 | p = scrfile; | |
236 | while(*p++ = **argv) | |
237 | (*argv)++; | |
238 | break; | |
239 | case 's': /* salvage flag */ | |
240 | stype(++*argv); | |
241 | sflag++; | |
242 | break; | |
243 | case 'S': /* conditional salvage */ | |
244 | stype(++*argv); | |
245 | csflag++; | |
246 | break; | |
247 | case 'n': /* default no answer flag */ | |
248 | case 'N': | |
249 | nflag++; | |
250 | yflag = 0; | |
251 | break; | |
252 | case 'y': /* default yes answer flag */ | |
253 | case 'Y': | |
254 | yflag++; | |
255 | nflag = 0; | |
256 | break; | |
257 | default: | |
258 | errexit("%c option?\n",**argv); | |
259 | } | |
260 | } | |
261 | if(nflag && (sflag || csflag)) | |
262 | errexit("Incompatible options: -n and -%s\n",sflag?"s":"S"); | |
263 | if(sflag && csflag) | |
264 | sflag = 0; | |
265 | memsize = (MEMSIZE)sbrk(0); | |
266 | memsize = MAXDATA - memsize - sizeof(int); | |
267 | while(memsize >= 2*sizeof(BUFAREA) && | |
268 | (membase = sbrk(memsize)) == (char *)-1) | |
269 | memsize -= 1024; | |
270 | if(memsize < 2*sizeof(BUFAREA)) | |
271 | errexit("Can't get memory\n"); | |
272 | if (signal(SIGINT, SIG_IGN) != SIG_IGN) | |
273 | signal(SIGINT, catch); | |
274 | if(argc) { /* arg list has file names */ | |
275 | while(argc-- > 0){ | |
276 | hotroot = 0; | |
277 | check(*argv++); | |
278 | } | |
279 | } | |
280 | else { /* use default checklist */ | |
281 | struct fstab *fsp; | |
282 | int pid, passno, anygtr, sumstatus = 0; | |
283 | passno = 1; | |
284 | do { | |
285 | anygtr = 0; | |
286 | if (setfsent() == 0) | |
287 | errexit("Can't open checklist file: %s\n", | |
288 | FSTAB); | |
289 | while ( (fsp = getfsent()) != 0){ | |
290 | if (strcmp(fsp->fs_type, FSTAB_RW) && | |
291 | strcmp(fsp->fs_type, FSTAB_RO)) | |
292 | continue; | |
293 | if (preen == 0 || | |
294 | passno == 1 && fsp->fs_passno == passno) { | |
295 | if (blockcheck(fsp->fs_spec) == NO && | |
296 | preen) | |
297 | exit(8); | |
298 | } else if (fsp->fs_passno > passno) | |
299 | anygtr = 1; | |
300 | else if (fsp->fs_passno == passno) { | |
301 | pid = fork(); | |
302 | if (pid < 0) { | |
303 | perror("fork"); | |
304 | exit(8); | |
305 | } | |
306 | if (pid == 0) | |
307 | if (blockcheck(fsp->fs_spec)==NO) | |
308 | exit(8); | |
309 | else | |
310 | exit(0); | |
311 | } | |
312 | } | |
313 | if (preen) { | |
314 | int status; | |
315 | while (wait(&status) != -1) | |
316 | sumstatus |= status; | |
317 | } | |
318 | passno++; | |
319 | } while (anygtr); | |
320 | if (sumstatus) | |
321 | exit(8); | |
322 | endfsent(); | |
323 | } | |
324 | exit(0); | |
325 | } | |
326 | ||
327 | char *rawname(), *rindex(), *unrawname(); | |
328 | ||
329 | blockcheck(name) | |
330 | char *name; | |
331 | { | |
332 | struct stat stat_slash, stat_block, stat_char; | |
333 | char *raw; | |
334 | int looped = 0; | |
335 | ||
336 | hotroot = 0; | |
337 | if (stat("/", &stat_slash) < 0){ | |
338 | error("Can't stat root\n"); | |
339 | return(NO); | |
340 | } | |
341 | retry: | |
342 | if (stat(name, &stat_block) < 0){ | |
343 | error("Can't stat %s\n", name); | |
344 | return(NO); | |
345 | } | |
346 | if (stat_block.st_mode & S_IFBLK){ | |
347 | raw = rawname(name); | |
348 | if (stat(raw, &stat_char) < 0){ | |
349 | error("Can't stat %s\n", raw); | |
350 | return(NO); | |
351 | } | |
352 | if (stat_char.st_mode & S_IFCHR){ | |
353 | if (stat_slash.st_dev == stat_block.st_rdev) { | |
354 | hotroot++; | |
355 | raw = unrawname(name); | |
356 | } | |
357 | check(raw); | |
358 | return(YES); | |
359 | } else { | |
360 | error("%s is not a character device\n", raw); | |
361 | return(NO); | |
362 | } | |
363 | } else | |
364 | if (stat_block.st_mode & S_IFCHR){ | |
365 | if (looped) { | |
366 | error("Can't make sense out of name %s\n", name); | |
367 | return(NO); | |
368 | } | |
369 | name = unrawname(name); | |
370 | looped++; | |
371 | goto retry; | |
372 | } | |
373 | error("Can't make sense out of name %s\n", name); | |
374 | return(NO); | |
375 | } | |
376 | ||
377 | char * | |
378 | unrawname(cp) | |
379 | char *cp; | |
380 | { | |
381 | char *dp = rindex(cp, '/'); | |
382 | struct stat stb; | |
383 | if (dp == 0) | |
384 | return(cp); | |
385 | if (stat(cp, &stb) < 0) | |
386 | return(cp); | |
387 | if ((stb.st_mode&S_IFMT) != S_IFCHR) | |
388 | return(cp); | |
389 | if (*(dp+1) != 'r') | |
390 | return(cp); | |
391 | strcpy(dp+1, dp+2); | |
392 | return(cp); | |
393 | } | |
394 | ||
395 | char * | |
396 | rawname(cp) | |
397 | char *cp; | |
398 | { | |
399 | static char rawbuf[32]; | |
400 | char *dp = rindex(cp, '/'); | |
401 | ||
402 | if (dp == 0) | |
403 | return (0); | |
404 | *dp = 0; | |
405 | strcpy(rawbuf, cp); | |
406 | *dp = '/'; | |
407 | strcat(rawbuf, "/r"); | |
408 | strcat(rawbuf, dp+1); | |
409 | return (rawbuf); | |
410 | } | |
411 | ||
412 | check(dev) | |
413 | char *dev; | |
414 | { | |
415 | ||
416 | devname = dev; | |
417 | check1(dev); | |
418 | devname = 0; | |
419 | } | |
420 | ||
421 | check1(dev) | |
422 | char *dev; | |
423 | { | |
424 | register DINODE *dp; | |
425 | register n; | |
426 | register ino_t *blp; | |
427 | ino_t savino; | |
428 | daddr_t blk; | |
429 | BUFAREA *bp1, *bp2; | |
430 | ||
431 | if(setup(dev) == NO) | |
432 | return; | |
433 | if (preen==0) { | |
434 | printf("** Checking %s %s", dev, | |
435 | hotroot?"(ROOT FILE SYSTEM)\n":"\n"); | |
436 | printf("** Phase 1 - Check Blocks and Sizes\n"); | |
437 | } | |
438 | pfunc = pass1; | |
439 | for(inum = 1; inum <= imax; inum++) { | |
440 | if((dp = ginode()) == NULL) | |
441 | continue; | |
442 | if(ALLOC) { | |
443 | lastino = inum; | |
444 | if(ftypeok(dp) == NO) { | |
445 | pfatal("UNKNOWN FILE TYPE I=%u",inum); | |
446 | if(reply("CLEAR") == YES) { | |
447 | zapino(dp); | |
448 | inodirty(); | |
449 | } | |
450 | continue; | |
451 | } | |
452 | n_files++; | |
453 | if(setlncnt(dp->di_nlink) <= 0) { | |
454 | if(badlnp < &badlncnt[MAXLNCNT]) | |
455 | *badlnp++ = inum; | |
456 | else { | |
457 | pfatal("LINK COUNT TABLE OVERFLOW"); | |
458 | if(reply("CONTINUE") == NO) | |
459 | errexit(""); | |
460 | } | |
461 | } | |
462 | setstate(DIR ? DSTATE : FSTATE); | |
463 | badblk = dupblk = 0; | |
464 | filsize = 0; | |
465 | maxblk = 0; | |
466 | ckinode(dp,ADDR); | |
467 | if((n = getstate()) == DSTATE || n == FSTATE) | |
468 | sizechk(dp); | |
469 | } | |
470 | else if(dp->di_mode != 0) { | |
471 | pfatal("PARTIALLY ALLOCATED INODE I=%u",inum); | |
472 | if(reply("CLEAR") == YES) { | |
473 | zapino(dp); | |
474 | inodirty(); | |
475 | } | |
476 | } | |
477 | } | |
478 | ||
479 | ||
480 | if(enddup != &duplist[0]) { | |
481 | if (preen) | |
482 | pfatal("INTERNAL ERROR: dups with -p"); | |
483 | printf("** Phase 1b - Rescan For More DUPS\n"); | |
484 | pfunc = pass1b; | |
485 | for(inum = 1; inum <= lastino; inum++) { | |
486 | if(getstate() != USTATE && (dp = ginode()) != NULL) | |
487 | if(ckinode(dp,ADDR) & STOP) | |
488 | break; | |
489 | } | |
490 | } | |
491 | if(rawflg) { | |
492 | if(inoblk.b_dirty) | |
493 | bwrite(&dfile,membase,startib,(int)niblk*BSIZE); | |
494 | inoblk.b_dirty = 0; | |
495 | if(poolhead) { | |
496 | clear(membase,niblk*BSIZE); | |
497 | for(bp1 = poolhead;bp1->b_next;bp1 = bp1->b_next); | |
498 | bp2 = &((BUFAREA *)membase)[(niblk*BSIZE)/sizeof(BUFAREA)]; | |
499 | while(--bp2 >= (BUFAREA *)membase) { | |
500 | initbarea(bp2); | |
501 | bp2->b_next = bp1->b_next; | |
502 | bp1->b_next = bp2; | |
503 | } | |
504 | } | |
505 | rawflg = 0; | |
506 | ||
507 | } | |
508 | ||
509 | ||
510 | if (preen == 0) | |
511 | printf("** Phase 2 - Check Pathnames\n"); | |
512 | inum = ROOTINO; | |
513 | thisname = pathp = pathname; | |
514 | pfunc = pass2; | |
515 | switch(getstate()) { | |
516 | case USTATE: | |
517 | errexit("ROOT INODE UNALLOCATED. TERMINATING.\n"); | |
518 | case FSTATE: | |
519 | pfatal("ROOT INODE NOT DIRECTORY"); | |
520 | if(reply("FIX") == NO || (dp = ginode()) == NULL) | |
521 | errexit(""); | |
522 | dp->di_mode &= ~IFMT; | |
523 | dp->di_mode |= IFDIR; | |
524 | inodirty(); | |
525 | setstate(DSTATE); | |
526 | case DSTATE: | |
527 | descend(); | |
528 | break; | |
529 | case CLEAR: | |
530 | pfatal("DUPS/BAD IN ROOT INODE"); | |
531 | printf("\n"); | |
532 | if(reply("CONTINUE") == NO) | |
533 | errexit(""); | |
534 | setstate(DSTATE); | |
535 | descend(); | |
536 | } | |
537 | ||
538 | ||
539 | if (preen == 0) | |
540 | printf("** Phase 3 - Check Connectivity\n"); | |
541 | for(inum = ROOTINO; inum <= lastino; inum++) { | |
542 | if(getstate() == DSTATE) { | |
543 | pfunc = findino; | |
544 | srchname = ".."; | |
545 | savino = inum; | |
546 | do { | |
547 | orphan = inum; | |
548 | if((dp = ginode()) == NULL) | |
549 | break; | |
550 | filsize = dp->di_size; | |
551 | parentdir = 0; | |
552 | ckinode(dp,DATA); | |
553 | if((inum = parentdir) == 0) | |
554 | break; | |
555 | } while(getstate() == DSTATE); | |
556 | inum = orphan; | |
557 | if(linkup() == YES) { | |
558 | thisname = pathp = pathname; | |
559 | *pathp++ = '?'; | |
560 | pfunc = pass2; | |
561 | descend(); | |
562 | } | |
563 | inum = savino; | |
564 | } | |
565 | } | |
566 | ||
567 | ||
568 | if (preen == 0) | |
569 | printf("** Phase 4 - Check Reference Counts\n"); | |
570 | pfunc = pass4; | |
571 | for(inum = ROOTINO; inum <= lastino; inum++) { | |
572 | switch(getstate()) { | |
573 | case FSTATE: | |
574 | if(n = getlncnt()) | |
575 | adjust((short)n); | |
576 | else { | |
577 | for(blp = badlncnt;blp < badlnp; blp++) | |
578 | if(*blp == inum) { | |
579 | clri("UNREF",YES); | |
580 | break; | |
581 | } | |
582 | } | |
583 | break; | |
584 | case DSTATE: | |
585 | clri("UNREF",YES); | |
586 | break; | |
587 | case CLEAR: | |
588 | clri("BAD/DUP",YES); | |
589 | } | |
590 | } | |
591 | if(imax - n_files != superblk.s_tinode) { | |
592 | pwarn("FREE INODE COUNT WRONG IN SUPERBLK"); | |
593 | if (preen) | |
594 | printf(" (FIXED)\n"); | |
595 | if (preen || reply("FIX") == YES) { | |
596 | superblk.s_tinode = imax - n_files; | |
597 | sbdirty(); | |
598 | } | |
599 | } | |
600 | flush(&dfile,&fileblk); | |
601 | ||
602 | ||
603 | if (preen == 0) | |
604 | printf("** Phase 5 - Check Free List "); | |
605 | if(sflag || (csflag && rplyflag == 0)) { | |
606 | if (preen == 0) | |
607 | printf("(Ignored)\n"); | |
608 | fixfree = 1; | |
609 | } | |
610 | else { | |
611 | if (preen == 0) | |
612 | printf("\n"); | |
613 | if(freemap) | |
614 | copy(blkmap,freemap,(MEMSIZE)bmapsz); | |
615 | else { | |
616 | for(blk = 0; blk < fmapblk; blk++) { | |
617 | bp1 = getblk((BUFAREA *)NULL,blk); | |
618 | bp2 = getblk((BUFAREA *)NULL,blk+fmapblk); | |
619 | copy(bp1->b_un.b_buf,bp2->b_un.b_buf,BSIZE); | |
620 | dirty(bp2); | |
621 | } | |
622 | } | |
623 | badblk = dupblk = 0; | |
624 | freeblk.df_nfree = superblk.s_nfree; | |
625 | for(n = 0; n < NICFREE; n++) | |
626 | freeblk.df_free[n] = superblk.s_free[n]; | |
627 | freechk(); | |
628 | if(badblk) { | |
629 | pfatal("%d BAD BLKS IN FREE LIST",badblk); | |
630 | printf("\n"); | |
631 | } | |
632 | if(dupblk) | |
633 | pwarn("%d DUP BLKS IN FREE LIST\n",dupblk); | |
634 | if(fixfree == 0) { | |
635 | if((n_blks+n_free) != (fmax-fmin)) { | |
636 | pwarn("%ld BLK(S) MISSING\n", | |
637 | fmax-fmin-n_blks-n_free); | |
638 | fixfree = 1; | |
639 | } | |
640 | else if(n_free != superblk.s_tfree) { | |
641 | pwarn("FREE BLK COUNT WRONG IN SUPERBLK"); | |
642 | if (preen) | |
643 | printf(" (FIXED)\n"); | |
644 | if(preen || reply("FIX") == YES) { | |
645 | superblk.s_tfree = n_free; | |
646 | sbdirty(); | |
647 | } | |
648 | } | |
649 | } | |
650 | if(fixfree) { | |
651 | pwarn("BAD FREE LIST"); | |
652 | if (preen) | |
653 | printf(" (SALVAGED)\n"); | |
654 | else if(reply("SALVAGE") == NO) | |
655 | fixfree = 0; | |
656 | } | |
657 | } | |
658 | ||
659 | ||
660 | if(fixfree) { | |
661 | if (preen == 0) | |
662 | printf("** Phase 6 - Salvage Free List\n"); | |
663 | makefree(); | |
664 | n_free = superblk.s_tfree; | |
665 | } | |
666 | ||
667 | ||
668 | pwarn("%ld files %ld blocks %ld free\n", n_files,n_blks,n_free); | |
669 | if(dfile.mod) { | |
670 | time(&superblk.s_time); | |
671 | sbdirty(); | |
672 | } | |
673 | ckfini(); | |
674 | sync(); | |
675 | if(dfile.mod && hotroot) { | |
676 | printf("\n***** BOOT UNIX (NO SYNC!) *****\n"); | |
677 | exit(4); | |
678 | } | |
679 | if(dfile.mod && preen == 0) | |
680 | printf("\n***** FILE SYSTEM WAS MODIFIED *****\n"); | |
681 | } | |
682 | ||
683 | /* VARARGS1 */ | |
684 | error(s1,s2,s3,s4) | |
685 | char *s1; | |
686 | { | |
687 | printf(s1,s2,s3,s4); | |
688 | } | |
689 | ||
690 | /* VARARGS1 */ | |
691 | errexit(s1,s2,s3,s4) | |
692 | char *s1; | |
693 | { | |
694 | error(s1,s2,s3,s4); | |
695 | exit(8); | |
696 | } | |
697 | ||
698 | /* | |
699 | * Pfatal is called when an inconsistency occurs | |
700 | * which should not happen during normal operations. | |
701 | * It prints a message and then dies. | |
702 | * When not preening, this is just a printf. | |
703 | */ | |
704 | pfatal(s,a1,a2,a3) | |
705 | { | |
706 | ||
707 | if (preen) { | |
708 | printf("%s: ", devname); | |
709 | printf(s, a1, a2, a3); | |
710 | printf("\n"); | |
711 | preendie(); | |
712 | } | |
713 | printf(s, a1, a2, a3); | |
714 | } | |
715 | ||
716 | /* | |
717 | * Fatal is called to terminate preening | |
718 | * due to unexplainable inconsistency. | |
719 | */ | |
720 | preendie() | |
721 | { | |
722 | ||
723 | printf("%s: UNEXPECTED INCONSISTENCY; RUN fsck MANUALLY.\n", devname); | |
724 | exit(8); | |
725 | } | |
726 | ||
727 | /* | |
728 | * Pwarn is like printf when not preening, | |
729 | * or a warning (preceded by filename) when preening. | |
730 | */ | |
731 | pwarn(s,a1,a2,a3) | |
732 | { | |
733 | ||
734 | if (preen) | |
735 | printf("%s: ", devname); | |
736 | printf(s, a1, a2, a3); | |
737 | } | |
738 | ||
739 | ckinode(dp,flg) | |
740 | DINODE *dp; | |
741 | register flg; | |
742 | { | |
743 | register daddr_t *ap; | |
744 | register ret; | |
745 | int (*func)(), n; | |
746 | daddr_t iaddrs[NADDR]; | |
747 | ||
748 | if(SPECIAL) | |
749 | return(KEEPON); | |
750 | l3tol(iaddrs,dp->di_addr,NADDR); | |
751 | func = (flg == ADDR) ? pfunc : dirscan; | |
752 | for(ap = iaddrs; ap < &iaddrs[NADDR-3]; ap++) { | |
753 | if(*ap && (ret = (*func)(*ap)) & STOP) | |
754 | return(ret); | |
755 | } | |
756 | for(n = 1; n < 4; n++) { | |
757 | if(*ap && (ret = iblock(*ap,n,flg)) & STOP) | |
758 | return(ret); | |
759 | ap++; | |
760 | } | |
761 | return(KEEPON); | |
762 | } | |
763 | ||
764 | ||
765 | iblock(blk,ilevel,flg) | |
766 | daddr_t blk; | |
767 | register ilevel; | |
768 | { | |
769 | register daddr_t *ap; | |
770 | register n; | |
771 | int (*func)(); | |
772 | BUFAREA ib; | |
773 | ||
774 | if(flg == ADDR) { | |
775 | func = pfunc; | |
776 | if(((n = (*func)(blk)) & KEEPON) == 0) | |
777 | return(n); | |
778 | } | |
779 | else | |
780 | func = dirscan; | |
781 | if(outrange(blk)) /* protect thyself */ | |
782 | return(SKIP); | |
783 | initbarea(&ib); | |
784 | if(getblk(&ib,blk) == NULL) | |
785 | return(SKIP); | |
786 | ilevel--; | |
787 | for(ap = ib.b_un.b_indir; ap < &ib.b_un.b_indir[NINDIR]; ap++) { | |
788 | if(*ap) { | |
789 | if(ilevel > 0) { | |
790 | n = iblock(*ap,ilevel,flg); | |
791 | } | |
792 | else | |
793 | n = (*func)(*ap); | |
794 | if(n & STOP) | |
795 | return(n); | |
796 | } | |
797 | } | |
798 | return(KEEPON); | |
799 | } | |
800 | ||
801 | ||
802 | pass1(blk) | |
803 | daddr_t blk; | |
804 | { | |
805 | register daddr_t *dlp; | |
806 | ||
807 | if(outrange(blk)) { | |
808 | blkerr("BAD",blk); | |
809 | if(++badblk >= MAXBAD) { | |
810 | printf("EXCESSIVE BAD BLKS I=%u",inum); | |
811 | if(reply("CONTINUE") == NO) | |
812 | errexit(""); | |
813 | return(STOP); | |
814 | } | |
815 | return(SKIP); | |
816 | } | |
817 | if(getbmap(blk)) { | |
818 | blkerr("DUP",blk); | |
819 | if(++dupblk >= MAXDUP) { | |
820 | printf("EXCESSIVE DUP BLKS I=%u",inum); | |
821 | if(reply("CONTINUE") == NO) | |
822 | errexit(""); | |
823 | return(STOP); | |
824 | } | |
825 | if(enddup >= &duplist[DUPTBLSIZE]) { | |
826 | printf("DUP TABLE OVERFLOW."); | |
827 | if(reply("CONTINUE") == NO) | |
828 | errexit(""); | |
829 | return(STOP); | |
830 | } | |
831 | for(dlp = duplist; dlp < muldup; dlp++) { | |
832 | if(*dlp == blk) { | |
833 | *enddup++ = blk; | |
834 | break; | |
835 | } | |
836 | } | |
837 | if(dlp >= muldup) { | |
838 | *enddup++ = *muldup; | |
839 | *muldup++ = blk; | |
840 | } | |
841 | } | |
842 | else { | |
843 | n_blks++; | |
844 | setbmap(blk); | |
845 | } | |
846 | filsize++; | |
847 | return(KEEPON); | |
848 | } | |
849 | ||
850 | ||
851 | pass1b(blk) | |
852 | daddr_t blk; | |
853 | { | |
854 | register daddr_t *dlp; | |
855 | ||
856 | if(outrange(blk)) | |
857 | return(SKIP); | |
858 | for(dlp = duplist; dlp < muldup; dlp++) { | |
859 | if(*dlp == blk) { | |
860 | blkerr("DUP",blk); | |
861 | *dlp = *--muldup; | |
862 | *muldup = blk; | |
863 | return(muldup == duplist ? STOP : KEEPON); | |
864 | } | |
865 | } | |
866 | return(KEEPON); | |
867 | } | |
868 | ||
869 | ||
870 | pass2(dirp) | |
871 | register DIRECT *dirp; | |
872 | { | |
873 | register char *p; | |
874 | register n; | |
875 | DINODE *dp; | |
876 | ||
877 | if((inum = dirp->d_ino) == 0) | |
878 | return(KEEPON); | |
879 | thisname = pathp; | |
880 | for(p = dirp->d_name; p < &dirp->d_name[DIRSIZ]; ) | |
881 | if((*pathp++ = *p++) == 0) { | |
882 | --pathp; | |
883 | break; | |
884 | } | |
885 | *pathp = 0; | |
886 | n = NO; | |
887 | if(inum > imax || inum < ROOTINO) | |
888 | n = direrr("I OUT OF RANGE"); | |
889 | else { | |
890 | again: | |
891 | switch(getstate()) { | |
892 | case USTATE: | |
893 | n = direrr("UNALLOCATED"); | |
894 | break; | |
895 | case CLEAR: | |
896 | if((n = direrr("DUP/BAD")) == YES) | |
897 | break; | |
898 | if((dp = ginode()) == NULL) | |
899 | break; | |
900 | setstate(DIR ? DSTATE : FSTATE); | |
901 | goto again; | |
902 | case FSTATE: | |
903 | declncnt(); | |
904 | break; | |
905 | case DSTATE: | |
906 | declncnt(); | |
907 | descend(); | |
908 | } | |
909 | } | |
910 | pathp = thisname; | |
911 | if(n == NO) | |
912 | return(KEEPON); | |
913 | dirp->d_ino = 0; | |
914 | return(KEEPON|ALTERD); | |
915 | } | |
916 | ||
917 | ||
918 | pass4(blk) | |
919 | daddr_t blk; | |
920 | { | |
921 | register daddr_t *dlp; | |
922 | ||
923 | if(outrange(blk)) | |
924 | return(SKIP); | |
925 | if(getbmap(blk)) { | |
926 | for(dlp = duplist; dlp < enddup; dlp++) | |
927 | if(*dlp == blk) { | |
928 | *dlp = *--enddup; | |
929 | return(KEEPON); | |
930 | } | |
931 | clrbmap(blk); | |
932 | n_blks--; | |
933 | } | |
934 | return(KEEPON); | |
935 | } | |
936 | ||
937 | ||
938 | pass5(blk) | |
939 | daddr_t blk; | |
940 | { | |
941 | if(outrange(blk)) { | |
942 | fixfree = 1; | |
943 | if (preen) | |
944 | pfatal("BAD BLOCKS IN FREE LIST."); | |
945 | if(++badblk >= MAXBAD) { | |
946 | printf("EXCESSIVE BAD BLKS IN FREE LIST."); | |
947 | if(reply("CONTINUE") == NO) | |
948 | errexit(""); | |
949 | return(STOP); | |
950 | } | |
951 | return(SKIP); | |
952 | } | |
953 | if(getfmap(blk)) { | |
954 | fixfree = 1; | |
955 | if(++dupblk >= DUPTBLSIZE) { | |
956 | printf("EXCESSIVE DUP BLKS IN FREE LIST."); | |
957 | if(reply("CONTINUE") == NO) | |
958 | errexit(""); | |
959 | return(STOP); | |
960 | } | |
961 | } | |
962 | else { | |
963 | n_free++; | |
964 | setfmap(blk); | |
965 | } | |
966 | return(KEEPON); | |
967 | } | |
968 | ||
969 | ||
970 | blkerr(s,blk) | |
971 | daddr_t blk; | |
972 | char *s; | |
973 | { | |
974 | pfatal("%ld %s I=%u",blk,s,inum); | |
975 | printf("\n"); | |
976 | setstate(CLEAR); /* mark for possible clearing */ | |
977 | } | |
978 | ||
979 | ||
980 | descend() | |
981 | { | |
982 | register DINODE *dp; | |
983 | register char *savname; | |
984 | off_t savsize; | |
985 | ||
986 | setstate(FSTATE); | |
987 | if((dp = ginode()) == NULL) | |
988 | return; | |
989 | savname = thisname; | |
990 | *pathp++ = '/'; | |
991 | savsize = filsize; | |
992 | filsize = dp->di_size; | |
993 | ckinode(dp,DATA); | |
994 | thisname = savname; | |
995 | *--pathp = 0; | |
996 | filsize = savsize; | |
997 | } | |
998 | ||
999 | ||
1000 | dirscan(blk) | |
1001 | daddr_t blk; | |
1002 | { | |
1003 | register DIRECT *dirp; | |
1004 | register char *p1, *p2; | |
1005 | register n; | |
1006 | DIRECT direntry; | |
1007 | ||
1008 | if(outrange(blk)) { | |
1009 | filsize -= BSIZE; | |
1010 | return(SKIP); | |
1011 | } | |
1012 | for(dirp = dirblk.b_dir; dirp < &dirblk.b_dir[NDIRECT] && | |
1013 | filsize > 0; dirp++, filsize -= sizeof(DIRECT)) { | |
1014 | if(getblk(&fileblk,blk) == NULL) { | |
1015 | filsize -= (&dirblk.b_dir[NDIRECT]-dirp)*sizeof(DIRECT); | |
1016 | return(SKIP); | |
1017 | } | |
1018 | p1 = &dirp->d_name[DIRSIZ]; | |
1019 | p2 = &direntry.d_name[DIRSIZ]; | |
1020 | while(p1 > (char *)dirp) | |
1021 | *--p2 = *--p1; | |
1022 | if((n = (*pfunc)(&direntry)) & ALTERD) { | |
1023 | if(getblk(&fileblk,blk) != NULL) { | |
1024 | p1 = &dirp->d_name[DIRSIZ]; | |
1025 | p2 = &direntry.d_name[DIRSIZ]; | |
1026 | while(p1 > (char *)dirp) | |
1027 | *--p1 = *--p2; | |
1028 | fbdirty(); | |
1029 | } | |
1030 | else | |
1031 | n &= ~ALTERD; | |
1032 | } | |
1033 | if(n & STOP) | |
1034 | return(n); | |
1035 | } | |
1036 | return(filsize > 0 ? KEEPON : STOP); | |
1037 | } | |
1038 | ||
1039 | ||
1040 | direrr(s) | |
1041 | char *s; | |
1042 | { | |
1043 | register DINODE *dp; | |
1044 | ||
1045 | pwarn("%s ",s); | |
1046 | pinode(); | |
1047 | printf("\n"); | |
1048 | if((dp = ginode()) != NULL && ftypeok(dp)) | |
1049 | pfatal("%s=%s",DIR?"DIR":"FILE",pathname); | |
1050 | else | |
1051 | pfatal("NAME=%s",pathname); | |
1052 | return(reply("REMOVE")); | |
1053 | } | |
1054 | ||
1055 | ||
1056 | adjust(lcnt) | |
1057 | register short lcnt; | |
1058 | { | |
1059 | register DINODE *dp; | |
1060 | ||
1061 | if((dp = ginode()) == NULL) | |
1062 | return; | |
1063 | if(dp->di_nlink == lcnt) { | |
1064 | if(linkup() == NO) | |
1065 | clri("UNREF",NO); | |
1066 | } | |
1067 | else { | |
1068 | pwarn("LINK COUNT %s", | |
1069 | (lfdir==inum)?lfname:(DIR?"DIR":"FILE")); | |
1070 | pinode(); | |
1071 | printf(" COUNT %d SHOULD BE %d", | |
1072 | dp->di_nlink,dp->di_nlink-lcnt); | |
1073 | if (preen) { | |
1074 | if (lcnt < 0) { | |
1075 | printf("\n"); | |
1076 | preendie(); | |
1077 | } | |
1078 | printf(" (ADJUSTED)\n"); | |
1079 | } | |
1080 | if(preen || reply("ADJUST") == YES) { | |
1081 | dp->di_nlink -= lcnt; | |
1082 | inodirty(); | |
1083 | } | |
1084 | } | |
1085 | } | |
1086 | ||
1087 | ||
1088 | clri(s,flg) | |
1089 | char *s; | |
1090 | { | |
1091 | register DINODE *dp; | |
1092 | ||
1093 | if((dp = ginode()) == NULL) | |
1094 | return; | |
1095 | if(flg == YES) { | |
1096 | pwarn("%s %s",s,DIR?"DIR":"FILE"); | |
1097 | pinode(); | |
1098 | } | |
1099 | if(preen || reply("CLEAR") == YES) { | |
1100 | if (preen) | |
1101 | printf(" (CLEARED)\n"); | |
1102 | n_files--; | |
1103 | pfunc = pass4; | |
1104 | ckinode(dp,ADDR); | |
1105 | zapino(dp); | |
1106 | inodirty(); | |
1107 | } | |
1108 | } | |
1109 | ||
1110 | ||
1111 | setup(dev) | |
1112 | char *dev; | |
1113 | { | |
1114 | register n; | |
1115 | register BUFAREA *bp; | |
1116 | register MEMSIZE msize; | |
1117 | char *mbase; | |
1118 | daddr_t bcnt, nscrblk; | |
1119 | dev_t rootdev; | |
1120 | off_t smapsz, lncntsz, totsz; | |
1121 | struct { | |
1122 | daddr_t tfree; | |
1123 | ino_t tinode; | |
1124 | char fname[6]; | |
1125 | char fpack[6]; | |
1126 | } ustatarea; | |
1127 | struct stat statarea; | |
1128 | ||
1129 | if(stat("/",&statarea) < 0) | |
1130 | errexit("Can't stat root\n"); | |
1131 | rootdev = statarea.st_dev; | |
1132 | if(stat(dev,&statarea) < 0) { | |
1133 | error("Can't stat %s\n",dev); | |
1134 | return(NO); | |
1135 | } | |
1136 | rawflg = 0; | |
1137 | if((statarea.st_mode & S_IFMT) == S_IFBLK) { | |
1138 | if(ustat(statarea.st_rdev, (char *)&ustatarea) >= 0) { | |
1139 | hotroot++; | |
1140 | } | |
1141 | } | |
1142 | else if((statarea.st_mode & S_IFMT) == S_IFCHR) | |
1143 | rawflg++; | |
1144 | else { | |
1145 | if (reply("file is not a block or character device; OK") == NO) | |
1146 | return(NO); | |
1147 | } | |
1148 | if(rootdev == statarea.st_rdev) | |
1149 | hotroot++; | |
1150 | if((dfile.rfdes = open(dev,0)) < 0) { | |
1151 | error("Can't open %s\n",dev); | |
1152 | return(NO); | |
1153 | } | |
1154 | if (preen == 0) | |
1155 | printf("\n%s",dev); | |
1156 | if(nflag || (dfile.wfdes = open(dev,1)) < 0) { | |
1157 | dfile.wfdes = -1; | |
1158 | if (preen) | |
1159 | pfatal("NO WRITE ACCESS"); | |
1160 | printf(" (NO WRITE)"); | |
1161 | } | |
1162 | if (preen == 0) | |
1163 | printf("\n"); | |
1164 | fixfree = 0; | |
1165 | dfile.mod = 0; | |
1166 | n_files = n_blks = n_free = 0; | |
1167 | muldup = enddup = &duplist[0]; | |
1168 | badlnp = &badlncnt[0]; | |
1169 | lfdir = 0; | |
1170 | rplyflag = 0; | |
1171 | initbarea(&sblk); | |
1172 | initbarea(&fileblk); | |
1173 | initbarea(&inoblk); | |
1174 | sfile.wfdes = sfile.rfdes = -1; | |
1175 | rmscr = 0; | |
1176 | if(getblk(&sblk,SUPERB) == NULL) { | |
1177 | ckfini(); | |
1178 | return(NO); | |
1179 | } | |
1180 | imax = ((ino_t)superblk.s_isize - (SUPERB+1)) * INOPB; | |
1181 | fmin = (daddr_t)superblk.s_isize; /* first data blk num */ | |
1182 | fmax = superblk.s_fsize; /* first invalid blk num */ | |
1183 | if(fmin >= fmax || | |
1184 | (imax/INOPB) != ((ino_t)superblk.s_isize-(SUPERB+1))) { | |
1185 | pfatal("Size check: fsize %ld isize %d", | |
1186 | superblk.s_fsize,superblk.s_isize); | |
1187 | printf("\n"); | |
1188 | ckfini(); | |
1189 | return(NO); | |
1190 | } | |
1191 | if (preen == 0) | |
1192 | printf("File System: %.6s Volume: %.6s\n\n", superblk.s_fname, | |
1193 | superblk.s_fpack); | |
1194 | bmapsz = roundup(howmany(fmax,BITSPB),sizeof(*lncntp)); | |
1195 | smapsz = roundup(howmany((long)(imax+1),STATEPB),sizeof(*lncntp)); | |
1196 | lncntsz = (long)(imax+1) * sizeof(*lncntp); | |
1197 | if(bmapsz > smapsz+lncntsz) | |
1198 | smapsz = bmapsz-lncntsz; | |
1199 | totsz = bmapsz+smapsz+lncntsz; | |
1200 | msize = memsize; | |
1201 | mbase = membase; | |
1202 | if(rawflg) { | |
1203 | if(msize < (MEMSIZE)(NINOBLK*BSIZE) + 2*sizeof(BUFAREA)) | |
1204 | rawflg = 0; | |
1205 | else { | |
1206 | msize -= (MEMSIZE)NINOBLK*BSIZE; | |
1207 | mbase += (MEMSIZE)NINOBLK*BSIZE; | |
1208 | niblk = NINOBLK; | |
1209 | startib = fmax; | |
1210 | } | |
1211 | } | |
1212 | clear(mbase,msize); | |
1213 | if((off_t)msize < totsz) { | |
1214 | bmapsz = roundup(bmapsz,BSIZE); | |
1215 | smapsz = roundup(smapsz,BSIZE); | |
1216 | lncntsz = roundup(lncntsz,BSIZE); | |
1217 | nscrblk = (bmapsz+smapsz+lncntsz)>>BSHIFT; | |
1218 | if(tflag == 0) { | |
1219 | printf("\nNEED SCRATCH FILE (%ld BLKS)\n",nscrblk); | |
1220 | do { | |
1221 | printf("ENTER FILENAME: "); | |
1222 | if((n = getline(stdin,scrfile,sizeof(scrfile))) == EOF) | |
1223 | errexit("\n"); | |
1224 | } while(n == 0); | |
1225 | } | |
1226 | if(stat(scrfile,&statarea) < 0 || | |
1227 | (statarea.st_mode & S_IFMT) == S_IFREG) | |
1228 | rmscr++; | |
1229 | if((sfile.wfdes = creat(scrfile,0666)) < 0 || | |
1230 | (sfile.rfdes = open(scrfile,0)) < 0) { | |
1231 | error("Can't create %s\n",scrfile); | |
1232 | ckfini(); | |
1233 | return(NO); | |
1234 | } | |
1235 | bp = &((BUFAREA *)mbase)[(msize/sizeof(BUFAREA))]; | |
1236 | poolhead = NULL; | |
1237 | while(--bp >= (BUFAREA *)mbase) { | |
1238 | initbarea(bp); | |
1239 | bp->b_next = poolhead; | |
1240 | poolhead = bp; | |
1241 | } | |
1242 | bp = poolhead; | |
1243 | for(bcnt = 0; bcnt < nscrblk; bcnt++) { | |
1244 | bp->b_bno = bcnt; | |
1245 | dirty(bp); | |
1246 | flush(&sfile,bp); | |
1247 | } | |
1248 | blkmap = freemap = statemap = (char *) NULL; | |
1249 | lncntp = (short *) NULL; | |
1250 | smapblk = bmapsz / BSIZE; | |
1251 | lncntblk = smapblk + smapsz / BSIZE; | |
1252 | fmapblk = smapblk; | |
1253 | } | |
1254 | else { | |
1255 | if(rawflg && (off_t)msize > totsz+BSIZE) { | |
1256 | niblk += (unsigned)((off_t)msize-totsz)>>BSHIFT; | |
1257 | if(niblk > MAXRAW) | |
1258 | niblk = MAXRAW; | |
1259 | msize = memsize - (niblk*BSIZE); | |
1260 | mbase = membase + (niblk*BSIZE); | |
1261 | } | |
1262 | poolhead = NULL; | |
1263 | blkmap = mbase; | |
1264 | statemap = &mbase[(MEMSIZE)bmapsz]; | |
1265 | freemap = statemap; | |
1266 | lncntp = (short *)&statemap[(MEMSIZE)smapsz]; | |
1267 | } | |
1268 | return(YES); | |
1269 | } | |
1270 | ||
1271 | ||
1272 | DINODE * | |
1273 | ginode() | |
1274 | { | |
1275 | register DINODE *dp; | |
1276 | register char *mbase; | |
1277 | daddr_t iblk; | |
1278 | ||
1279 | if(inum > imax) | |
1280 | return(NULL); | |
1281 | iblk = itod(inum); | |
1282 | if(rawflg) { | |
1283 | mbase = membase; | |
1284 | if(iblk < startib || iblk >= startib+niblk) { | |
1285 | if(inoblk.b_dirty) | |
1286 | bwrite(&dfile,mbase,startib,(int)niblk*BSIZE); | |
1287 | inoblk.b_dirty = 0; | |
1288 | if(bread(&dfile,mbase,iblk,(int)niblk*BSIZE) == NO) { | |
1289 | startib = fmax; | |
1290 | return(NULL); | |
1291 | } | |
1292 | startib = iblk; | |
1293 | } | |
1294 | dp = (DINODE *)&mbase[(unsigned)((iblk-startib)<<BSHIFT)]; | |
1295 | } | |
1296 | else if(getblk(&inoblk,iblk) != NULL) | |
1297 | dp = inoblk.b_un.b_dinode; | |
1298 | else | |
1299 | return(NULL); | |
1300 | return(dp + itoo(inum)); | |
1301 | } | |
1302 | ||
1303 | ||
1304 | ftypeok(dp) | |
1305 | DINODE *dp; | |
1306 | { | |
1307 | switch(dp->di_mode & IFMT) { | |
1308 | case IFDIR: | |
1309 | case IFREG: | |
1310 | case IFBLK: | |
1311 | case IFCHR: | |
1312 | case IFMPC: | |
1313 | case IFMPB: | |
1314 | return(YES); | |
1315 | default: | |
1316 | return(NO); | |
1317 | } | |
1318 | } | |
1319 | ||
1320 | ||
1321 | reply(s) | |
1322 | char *s; | |
1323 | { | |
1324 | char line[80]; | |
1325 | ||
1326 | if (preen) | |
1327 | pfatal("INTERNAL ERROR: GOT TO reply()"); | |
1328 | rplyflag = 1; | |
1329 | printf("\n%s? ",s); | |
1330 | if(nflag || csflag || dfile.wfdes < 0) { | |
1331 | printf(" no\n\n"); | |
1332 | return(NO); | |
1333 | } | |
1334 | if(yflag) { | |
1335 | printf(" yes\n\n"); | |
1336 | return(YES); | |
1337 | } | |
1338 | if(getline(stdin,line,sizeof(line)) == EOF) | |
1339 | errexit("\n"); | |
1340 | printf("\n"); | |
1341 | if(line[0] == 'y' || line[0] == 'Y') | |
1342 | return(YES); | |
1343 | else | |
1344 | return(NO); | |
1345 | } | |
1346 | ||
1347 | ||
1348 | getline(fp,loc,maxlen) | |
1349 | FILE *fp; | |
1350 | char *loc; | |
1351 | { | |
1352 | register n; | |
1353 | register char *p, *lastloc; | |
1354 | ||
1355 | p = loc; | |
1356 | lastloc = &p[maxlen-1]; | |
1357 | while((n = getc(fp)) != '\n') { | |
1358 | if(n == EOF) | |
1359 | return(EOF); | |
1360 | if(!isspace(n) && p < lastloc) | |
1361 | *p++ = n; | |
1362 | } | |
1363 | *p = 0; | |
1364 | return(p - loc); | |
1365 | } | |
1366 | ||
1367 | ||
1368 | stype(p) | |
1369 | register char *p; | |
1370 | { | |
1371 | if(*p == 0) | |
1372 | return; | |
1373 | if (*(p+1) == 0) { | |
1374 | if (*p == '3') { | |
1375 | cylsize = 200; | |
1376 | stepsize = 5; | |
1377 | return; | |
1378 | } | |
1379 | if (*p == '4') { | |
1380 | cylsize = 418; | |
1381 | stepsize = 9; | |
1382 | return; | |
1383 | } | |
1384 | } | |
1385 | cylsize = atoi(p); | |
1386 | while(*p && *p != ':') | |
1387 | p++; | |
1388 | if(*p) | |
1389 | p++; | |
1390 | stepsize = atoi(p); | |
1391 | if(stepsize <= 0 || stepsize > cylsize || | |
1392 | cylsize <= 0 || cylsize > MAXCYL) { | |
1393 | error("Invalid -s argument, defaults assumed\n"); | |
1394 | cylsize = stepsize = 0; | |
1395 | } | |
1396 | } | |
1397 | ||
1398 | ||
1399 | dostate(s,flg) | |
1400 | { | |
1401 | register char *p; | |
1402 | register unsigned byte, shift; | |
1403 | BUFAREA *bp; | |
1404 | ||
1405 | byte = (inum)/STATEPB; | |
1406 | shift = LSTATE * ((inum)%STATEPB); | |
1407 | if(statemap != NULL) { | |
1408 | bp = NULL; | |
1409 | p = &statemap[byte]; | |
1410 | } | |
1411 | else if((bp = getblk((BUFAREA *)NULL,(daddr_t)(smapblk+(byte/BSIZE)))) == NULL) | |
1412 | errexit("Fatal I/O error\n"); | |
1413 | else | |
1414 | p = &bp->b_un.b_buf[byte%BSIZE]; | |
1415 | switch(flg) { | |
1416 | case 0: | |
1417 | *p &= ~(SMASK<<(shift)); | |
1418 | *p |= s<<(shift); | |
1419 | if(bp != NULL) | |
1420 | dirty(bp); | |
1421 | return(s); | |
1422 | case 1: | |
1423 | return((*p>>(shift)) & SMASK); | |
1424 | } | |
1425 | return(USTATE); | |
1426 | } | |
1427 | ||
1428 | ||
1429 | domap(blk,flg) | |
1430 | daddr_t blk; | |
1431 | { | |
1432 | register char *p; | |
1433 | register unsigned n; | |
1434 | register BUFAREA *bp; | |
1435 | off_t byte; | |
1436 | ||
1437 | byte = blk >> BITSHIFT; | |
1438 | n = 1<<((unsigned)(blk & BITMASK)); | |
1439 | if(flg & 04) { | |
1440 | p = freemap; | |
1441 | blk = fmapblk; | |
1442 | } | |
1443 | else { | |
1444 | p = blkmap; | |
1445 | blk = 0; | |
1446 | } | |
1447 | if(p != NULL) { | |
1448 | bp = NULL; | |
1449 | p += (unsigned)byte; | |
1450 | } | |
1451 | else if((bp = getblk((BUFAREA *)NULL,blk+(byte>>BSHIFT))) == NULL) | |
1452 | errexit("Fatal I/O error\n"); | |
1453 | else | |
1454 | p = &bp->b_un.b_buf[(unsigned)(byte&BMASK)]; | |
1455 | switch(flg&03) { | |
1456 | case 0: | |
1457 | *p |= n; | |
1458 | break; | |
1459 | case 1: | |
1460 | n &= *p; | |
1461 | bp = NULL; | |
1462 | break; | |
1463 | case 2: | |
1464 | *p &= ~n; | |
1465 | } | |
1466 | if(bp != NULL) | |
1467 | dirty(bp); | |
1468 | return(n); | |
1469 | } | |
1470 | ||
1471 | ||
1472 | dolncnt(val,flg) | |
1473 | short val; | |
1474 | { | |
1475 | register short *sp; | |
1476 | register BUFAREA *bp; | |
1477 | ||
1478 | if(lncntp != NULL) { | |
1479 | bp = NULL; | |
1480 | sp = &lncntp[inum]; | |
1481 | } | |
1482 | else if((bp = getblk((BUFAREA *)NULL,(daddr_t)(lncntblk+(inum/SPERB)))) == NULL) | |
1483 | errexit("Fatal I/O error\n"); | |
1484 | else | |
1485 | sp = &bp->b_un.b_lnks[inum%SPERB]; | |
1486 | switch(flg) { | |
1487 | case 0: | |
1488 | *sp = val; | |
1489 | break; | |
1490 | case 1: | |
1491 | bp = NULL; | |
1492 | break; | |
1493 | case 2: | |
1494 | (*sp)--; | |
1495 | } | |
1496 | if(bp != NULL) | |
1497 | dirty(bp); | |
1498 | return(*sp); | |
1499 | } | |
1500 | ||
1501 | ||
1502 | BUFAREA * | |
1503 | getblk(bp,blk) | |
1504 | daddr_t blk; | |
1505 | register BUFAREA *bp; | |
1506 | { | |
1507 | register struct filecntl *fcp; | |
1508 | ||
1509 | if(bp == NULL) { | |
1510 | bp = search(blk); | |
1511 | fcp = &sfile; | |
1512 | } | |
1513 | else | |
1514 | fcp = &dfile; | |
1515 | if(bp->b_bno == blk) | |
1516 | return(bp); | |
1517 | flush(fcp,bp); | |
1518 | if(bread(fcp,bp->b_un.b_buf,blk,BSIZE) != NO) { | |
1519 | bp->b_bno = blk; | |
1520 | return(bp); | |
1521 | } | |
1522 | bp->b_bno = (daddr_t)-1; | |
1523 | return(NULL); | |
1524 | } | |
1525 | ||
1526 | ||
1527 | flush(fcp,bp) | |
1528 | struct filecntl *fcp; | |
1529 | register BUFAREA *bp; | |
1530 | { | |
1531 | if(bp->b_dirty) { | |
1532 | bwrite(fcp,bp->b_un.b_buf,bp->b_bno,BSIZE); | |
1533 | } | |
1534 | bp->b_dirty = 0; | |
1535 | } | |
1536 | ||
1537 | ||
1538 | rwerr(s,blk) | |
1539 | char *s; | |
1540 | daddr_t blk; | |
1541 | { | |
1542 | if (preen == 0) | |
1543 | printf("\n"); | |
1544 | pfatal("CAN NOT %s: BLK %ld",s,blk); | |
1545 | if(reply("CONTINUE") == NO) | |
1546 | errexit("Program terminated\n"); | |
1547 | } | |
1548 | ||
1549 | ||
1550 | sizechk(dp) | |
1551 | register DINODE *dp; | |
1552 | { | |
1553 | /* | |
1554 | if (maxblk != howmany(dp->di_size, BSIZE)) | |
1555 | printf("POSSIBLE FILE SIZE ERROR I=%u (%ld,%ld)\n\n", | |
1556 | inum, maxblk, howmany(dp->di_size,BSIZE)); | |
1557 | */ | |
1558 | if(DIR && (dp->di_size % sizeof(DIRECT)) != 0) { | |
1559 | pwarn("DIRECTORY MISALIGNED I=%u\n",inum); | |
1560 | if (preen == 0) | |
1561 | printf("\n"); | |
1562 | } | |
1563 | } | |
1564 | ||
1565 | ||
1566 | ckfini() | |
1567 | { | |
1568 | flush(&dfile,&fileblk); | |
1569 | flush(&dfile,&sblk); | |
1570 | flush(&dfile,&inoblk); | |
1571 | close(dfile.rfdes); | |
1572 | close(dfile.wfdes); | |
1573 | close(sfile.rfdes); | |
1574 | close(sfile.wfdes); | |
1575 | if(rmscr) { | |
1576 | unlink(scrfile); | |
1577 | } | |
1578 | } | |
1579 | ||
1580 | ||
1581 | pinode() | |
1582 | { | |
1583 | register DINODE *dp; | |
1584 | register char *p; | |
1585 | char uidbuf[200]; | |
1586 | char *ctime(); | |
1587 | ||
1588 | printf(" I=%u ",inum); | |
1589 | if((dp = ginode()) == NULL) | |
1590 | return; | |
1591 | printf(" OWNER="); | |
1592 | if(getpw((int)dp->di_uid,uidbuf) == 0) { | |
1593 | for(p = uidbuf; *p != ':'; p++); | |
1594 | *p = 0; | |
1595 | printf("%s ",uidbuf); | |
1596 | } | |
1597 | else { | |
1598 | printf("%d ",dp->di_uid); | |
1599 | } | |
1600 | printf("MODE=%o\n",dp->di_mode); | |
1601 | if (preen) | |
1602 | printf("%s: ", devname); | |
1603 | printf("SIZE=%ld ",dp->di_size); | |
1604 | p = ctime(&dp->di_mtime); | |
1605 | printf("MTIME=%12.12s %4.4s ",p+4,p+20); | |
1606 | } | |
1607 | ||
1608 | ||
1609 | copy(fp,tp,size) | |
1610 | register char *tp, *fp; | |
1611 | MEMSIZE size; | |
1612 | { | |
1613 | while(size--) | |
1614 | *tp++ = *fp++; | |
1615 | } | |
1616 | ||
1617 | ||
1618 | freechk() | |
1619 | { | |
1620 | register daddr_t *ap; | |
1621 | ||
1622 | if(freeblk.df_nfree == 0) | |
1623 | return; | |
1624 | do { | |
1625 | if(freeblk.df_nfree <= 0 || freeblk.df_nfree > NICFREE) { | |
1626 | pfatal("BAD FREEBLK COUNT"); | |
1627 | printf("\n"); | |
1628 | fixfree = 1; | |
1629 | return; | |
1630 | } | |
1631 | ap = &freeblk.df_free[freeblk.df_nfree]; | |
1632 | while(--ap > &freeblk.df_free[0]) { | |
1633 | if(pass5(*ap) == STOP) | |
1634 | return; | |
1635 | } | |
1636 | if(*ap == (daddr_t)0 || pass5(*ap) != KEEPON) | |
1637 | return; | |
1638 | } while(getblk(&fileblk,*ap) != NULL); | |
1639 | } | |
1640 | ||
1641 | ||
1642 | makefree() | |
1643 | { | |
1644 | register i, cyl, step; | |
1645 | int j; | |
1646 | char flg[MAXCYL]; | |
1647 | short addr[MAXCYL]; | |
1648 | daddr_t blk, baseblk; | |
1649 | ||
1650 | superblk.s_nfree = 0; | |
1651 | superblk.s_flock = 0; | |
1652 | superblk.s_fmod = 0; | |
1653 | superblk.s_tfree = 0; | |
1654 | superblk.s_ninode = 0; | |
1655 | superblk.s_ilock = 0; | |
1656 | superblk.s_ronly = 0; | |
1657 | if(cylsize == 0 || stepsize == 0) { | |
1658 | step = superblk.s_dinfo[0]; | |
1659 | cyl = superblk.s_dinfo[1]; | |
1660 | } | |
1661 | else { | |
1662 | step = stepsize; | |
1663 | cyl = cylsize; | |
1664 | } | |
1665 | if(step > cyl || step <= 0 || cyl <= 0 || cyl > MAXCYL) { | |
1666 | error("Default free list spacing assumed\n"); | |
1667 | step = STEPSIZE; | |
1668 | cyl = CYLSIZE; | |
1669 | } | |
1670 | superblk.s_dinfo[0] = step; | |
1671 | superblk.s_dinfo[1] = cyl; | |
1672 | clear(flg,sizeof(flg)); | |
1673 | i = 0; | |
1674 | for(j = 0; j < cyl; j++) { | |
1675 | while(flg[i]) | |
1676 | i = (i + 1) % cyl; | |
1677 | addr[j] = i + 1; | |
1678 | flg[i]++; | |
1679 | i = (i + step) % cyl; | |
1680 | } | |
1681 | baseblk = (daddr_t)roundup(fmax,cyl); | |
1682 | clear((char *)&freeblk,BSIZE); | |
1683 | freeblk.df_nfree++; | |
1684 | for( ; baseblk > 0; baseblk -= cyl) | |
1685 | for(i = 0; i < cyl; i++) { | |
1686 | blk = baseblk - addr[i]; | |
1687 | if(!outrange(blk) && !getbmap(blk)) { | |
1688 | superblk.s_tfree++; | |
1689 | if(freeblk.df_nfree >= NICFREE) { | |
1690 | fbdirty(); | |
1691 | fileblk.b_bno = blk; | |
1692 | flush(&dfile,&fileblk); | |
1693 | clear((char *)&freeblk,BSIZE); | |
1694 | } | |
1695 | freeblk.df_free[freeblk.df_nfree] = blk; | |
1696 | freeblk.df_nfree++; | |
1697 | } | |
1698 | } | |
1699 | superblk.s_nfree = freeblk.df_nfree; | |
1700 | for(i = 0; i < NICFREE; i++) | |
1701 | superblk.s_free[i] = freeblk.df_free[i]; | |
1702 | sbdirty(); | |
1703 | } | |
1704 | ||
1705 | ||
1706 | clear(p,cnt) | |
1707 | register char *p; | |
1708 | MEMSIZE cnt; | |
1709 | { | |
1710 | while(cnt--) | |
1711 | *p++ = 0; | |
1712 | } | |
1713 | ||
1714 | ||
1715 | BUFAREA * | |
1716 | search(blk) | |
1717 | daddr_t blk; | |
1718 | { | |
1719 | register BUFAREA *pbp, *bp; | |
1720 | ||
1721 | for(bp = (BUFAREA *) &poolhead; bp->b_next; ) { | |
1722 | pbp = bp; | |
1723 | bp = pbp->b_next; | |
1724 | if(bp->b_bno == blk) | |
1725 | break; | |
1726 | } | |
1727 | pbp->b_next = bp->b_next; | |
1728 | bp->b_next = poolhead; | |
1729 | poolhead = bp; | |
1730 | return(bp); | |
1731 | } | |
1732 | ||
1733 | ||
1734 | findino(dirp) | |
1735 | register DIRECT *dirp; | |
1736 | { | |
1737 | register char *p1, *p2; | |
1738 | ||
1739 | if(dirp->d_ino == 0) | |
1740 | return(KEEPON); | |
1741 | for(p1 = dirp->d_name,p2 = srchname;*p2++ == *p1; p1++) { | |
1742 | if(*p1 == 0 || p1 == &dirp->d_name[DIRSIZ-1]) { | |
1743 | if(dirp->d_ino >= ROOTINO && dirp->d_ino <= imax) | |
1744 | parentdir = dirp->d_ino; | |
1745 | return(STOP); | |
1746 | } | |
1747 | } | |
1748 | return(KEEPON); | |
1749 | } | |
1750 | ||
1751 | ||
1752 | mkentry(dirp) | |
1753 | register DIRECT *dirp; | |
1754 | { | |
1755 | register ino_t in; | |
1756 | register char *p; | |
1757 | ||
1758 | if(dirp->d_ino) | |
1759 | return(KEEPON); | |
1760 | dirp->d_ino = orphan; | |
1761 | in = orphan; | |
1762 | p = &dirp->d_name[8]; | |
1763 | *--p = 0; | |
1764 | while(p > dirp->d_name) { | |
1765 | *--p = (in % 10) + '0'; | |
1766 | in /= 10; | |
1767 | } | |
1768 | *p = '#'; | |
1769 | return(ALTERD|STOP); | |
1770 | } | |
1771 | ||
1772 | ||
1773 | chgdd(dirp) | |
1774 | register DIRECT *dirp; | |
1775 | { | |
1776 | if(dirp->d_name[0] == '.' && dirp->d_name[1] == '.' && | |
1777 | dirp->d_name[2] == 0) { | |
1778 | dirp->d_ino = lfdir; | |
1779 | return(ALTERD|STOP); | |
1780 | } | |
1781 | return(KEEPON); | |
1782 | } | |
1783 | ||
1784 | ||
1785 | linkup() | |
1786 | { | |
1787 | register DINODE *dp; | |
1788 | register lostdir; | |
1789 | register ino_t pdir; | |
1790 | ||
1791 | if((dp = ginode()) == NULL) | |
1792 | return(NO); | |
1793 | lostdir = DIR; | |
1794 | pdir = parentdir; | |
1795 | pwarn("UNREF %s ",lostdir ? "DIR" : "FILE"); | |
1796 | pinode(); | |
1797 | if (preen && dp->di_size == 0) | |
1798 | return(NO); | |
1799 | if (preen) | |
1800 | printf(" (RECONNECTED)\n"); | |
1801 | else | |
1802 | if (reply("RECONNECT") == NO) | |
1803 | return(NO); | |
1804 | orphan = inum; | |
1805 | if(lfdir == 0) { | |
1806 | inum = ROOTINO; | |
1807 | if((dp = ginode()) == NULL) { | |
1808 | inum = orphan; | |
1809 | return(NO); | |
1810 | } | |
1811 | pfunc = findino; | |
1812 | srchname = lfname; | |
1813 | filsize = dp->di_size; | |
1814 | parentdir = 0; | |
1815 | ckinode(dp,DATA); | |
1816 | inum = orphan; | |
1817 | if((lfdir = parentdir) == 0) { | |
1818 | pfatal("SORRY. NO lost+found DIRECTORY"); | |
1819 | printf("\n\n"); | |
1820 | return(NO); | |
1821 | } | |
1822 | } | |
1823 | inum = lfdir; | |
1824 | if((dp = ginode()) == NULL || !DIR || getstate() != FSTATE) { | |
1825 | inum = orphan; | |
1826 | pfatal("SORRY. NO lost+found DIRECTORY"); | |
1827 | printf("\n\n"); | |
1828 | return(NO); | |
1829 | } | |
1830 | if(dp->di_size & BMASK) { | |
1831 | dp->di_size = roundup(dp->di_size,BSIZE); | |
1832 | inodirty(); | |
1833 | } | |
1834 | filsize = dp->di_size; | |
1835 | inum = orphan; | |
1836 | pfunc = mkentry; | |
1837 | if((ckinode(dp,DATA) & ALTERD) == 0) { | |
1838 | pfatal("SORRY. NO SPACE IN lost+found DIRECTORY"); | |
1839 | printf("\n\n"); | |
1840 | return(NO); | |
1841 | } | |
1842 | declncnt(); | |
1843 | if(lostdir) { | |
1844 | pfunc = chgdd; | |
1845 | dp = ginode(); | |
1846 | filsize = dp->di_size; | |
1847 | ckinode(dp,DATA); | |
1848 | inum = lfdir; | |
1849 | if((dp = ginode()) != NULL) { | |
1850 | dp->di_nlink++; | |
1851 | inodirty(); | |
1852 | setlncnt(getlncnt()+1); | |
1853 | } | |
1854 | inum = orphan; | |
1855 | pwarn("DIR I=%u CONNECTED. ",orphan); | |
1856 | printf("PARENT WAS I=%u\n",pdir); | |
1857 | if (preen == 0) | |
1858 | printf("\n"); | |
1859 | } | |
1860 | return(YES); | |
1861 | } | |
1862 | ||
1863 | ||
1864 | bread(fcp,buf,blk,size) | |
1865 | daddr_t blk; | |
1866 | register struct filecntl *fcp; | |
1867 | register size; | |
1868 | char *buf; | |
1869 | { | |
1870 | if(lseek(fcp->rfdes,blk<<BSHIFT,0) < 0) | |
1871 | rwerr("SEEK",blk); | |
1872 | else if(read(fcp->rfdes,buf,size) == size) | |
1873 | return(YES); | |
1874 | rwerr("READ",blk); | |
1875 | return(NO); | |
1876 | } | |
1877 | ||
1878 | ||
1879 | bwrite(fcp,buf,blk,size) | |
1880 | daddr_t blk; | |
1881 | register struct filecntl *fcp; | |
1882 | register size; | |
1883 | char *buf; | |
1884 | { | |
1885 | if(fcp->wfdes < 0) | |
1886 | return(NO); | |
1887 | if(lseek(fcp->wfdes,blk<<BSHIFT,0) < 0) | |
1888 | rwerr("SEEK",blk); | |
1889 | else if(write(fcp->wfdes,buf,size) == size) { | |
1890 | fcp->mod = 1; | |
1891 | return(YES); | |
1892 | } | |
1893 | rwerr("WRITE",blk); | |
1894 | return(NO); | |
1895 | } | |
1896 | ||
1897 | catch() | |
1898 | { | |
1899 | ckfini(); | |
1900 | exit(12); | |
1901 | } | |
1902 | ||
1903 | ustat(x, s) | |
1904 | char *s; | |
1905 | { | |
1906 | return(-1); | |
1907 | } |