move to debhelper compat 13, hopefully fixes cross-building
[debian/cpmtools] / fsck.cpm.c
1 /* #includes */ /*{{{C}}}*//*{{{*/
2 #include "config.h"
3
4 #include <assert.h>
5 #include <ctype.h>
6 #include <stdio.h>
7 #include <string.h>
8 #include <stdlib.h>
9 #include <errno.h>
10
11 #include "getopt_.h"
12 #include "cpmdir.h"
13 #include "cpmfs.h"
14 /*}}}*/
15 /* #defines */ /*{{{*/
16 /* your favourite password *:-) */
17
18 #define T0 'G'
19 #define T1 'E'
20 #define T2 'H'
21 #define T3 'E'
22 #define T4 'I'
23 #define T5 'M'
24 #define T6 ' '
25 #define T7 ' '
26
27 #define PB ((char)(T0+T1+T2+T3+T4+T5+T6+T7))
28 #define P0 ((char)(T7^PB))
29 #define P1 ((char)(T6^PB))
30 #define P2 ((char)(T5^PB))
31 #define P3 ((char)(T4^PB))
32 #define P4 ((char)(T3^PB))
33 #define P5 ((char)(T2^PB))
34 #define P6 ((char)(T1^PB))
35 #define P7 ((char)(T0^PB))
36 /*}}}*/
37
38 /* types */ /*{{{*/
39 enum Result { OK=0, MODIFIED=1, BROKEN=2 };
40 /*}}}*/
41 /* variables */ /*{{{*/
42 static int norepair=0;
43 /*}}}*/
44
45 /* bcdCheck -- check format and range of BCD digit */ /*{{{*/
46 static int bcdCheck(int n, int max, const char *msg, const char *unit, int extent1, int extent2)
47 {
48   if (((n>>4)&0xf)>10 || (n&0xf)>10 || (((n>>4)&0xf)*10+(n&0xf))>=max)
49   {
50     printf("Error: Bad %s %s (extent=%d/%d, %s=%02x)\n",msg,unit,extent1,extent2,unit,n&0xff);
51     return -1;
52   }
53   else return 0;
54 }
55 /*}}}*/
56 /* pwdCheck -- check password */ /*{{{*/
57 static int pwdCheck(int extent, const char *pwd, char decode)
58 {
59   char c;
60   int i;
61
62   for (i=0; i<8; ++i) if ((c=((char)(pwd[7-i]^decode)))<' ' || c&0x80)
63   {
64     printf("Error: non-printable character in password (extent=%d, password=",extent);
65     for (i=0; i<8; ++i)
66     {
67       c=pwd[7-i]^decode;
68       if (c<' ' || c&0x80)
69       {
70         putchar('\\'); putchar('0'+((c>>6)&0x01));
71         putchar('0'+((c>>3)&0x03));
72         putchar('0'+(c&0x03));
73       }
74       else putchar(c);
75     }
76     printf(")\n");
77     return -1;
78   }
79   return 0;
80 }
81 /*}}}*/
82 /* ask -- ask user and return answer */ /*{{{*/
83 static int ask(const char *msg)
84 {
85   while (1)
86   {
87     char buf[80];
88
89     if (norepair) return 0;
90     printf("%s [Y]? ",msg); fflush(stdout);
91     if (fgets(buf,sizeof(buf),stdin)==(char*)0) exit(1);
92     switch (toupper(buf[0]))
93     {
94       case '\n':
95       case 'Y': return 1;
96       case 'N': return 0;
97     }
98   }
99 }
100 /*}}}*/
101 /* prfile -- print file name */ /*{{{*/
102 static char *prfile(struct cpmSuperBlock *sb, int extent)
103 {
104   struct PhysDirectoryEntry *dir;
105   static char name[80];
106   char *s=name;
107   int i;
108   char c;
109
110   dir=sb->dir+extent;
111   for (i=0; i<8; ++i)
112   {
113     c=dir->name[i];
114     if ((c&0x7f)<' ')
115     {
116       *s++='\\'; *s++=('0'+((c>>6)&0x01));
117       *s++=('0'+((c>>3)&0x03));
118       *s++=('0'+(c&0x03));
119     }
120     else *s++=(c&0x7f);
121   }
122   *s++='.';
123   for (i=0; i<3; ++i)
124   {
125     c=dir->ext[i];
126     if ((c&0x7f)<' ')
127     {
128       *s++='\\'; *s++=('0'+((c>>6)&0x01));
129       *s++=('0'+((c>>3)&0x03));
130       *s++=('0'+(c&0x03));
131     }
132     else *s++=(c&0x7f);
133   }
134   *s='\0';
135   return name;
136 }
137 /*}}}*/
138 /* fsck -- file system check */ /*{{{*/
139 static int fsck(struct cpmInode *root, const char *image)
140 {
141   /* variables */ /*{{{*/
142   enum Result ret=OK;
143   int extent,extent2;
144   struct PhysDirectoryEntry *dir,*dir2;
145   struct cpmSuperBlock *sb=root->sb;
146   /*}}}*/
147
148   /* Phase 1: check extent fields */ /*{{{*/
149   printf("Phase 1: check extent fields\n");
150   for (extent=0; extent<sb->maxdir; ++extent)
151   {
152     char *status;
153     int usedBlocks=0;
154
155     dir=sb->dir+extent;
156     status=&dir->status;
157     if (*status>=0 && *status<=(sb->type==CPMFS_P2DOS ? 31 : 15)) /* directory entry */ /*{{{*/
158     {
159       /* check name and extension */ /*{{{*/
160       {
161         int i;
162         char *c;
163
164         for (i=0; i<8; ++i)
165         {
166           c=&(dir->name[i]);
167           if (!ISFILECHAR(i,*c&0x7f) || islower(*c&0x7f))
168           {
169             printf("Error: Bad name (extent=%d, name=\"%s\", position=%d)\n",extent,prfile(sb,extent),i);
170             if (ask("Remove file"))
171             {
172               *status=(char)0xE5;
173               ret|=MODIFIED;
174               break;
175             }
176             else ret|=BROKEN;
177           }
178         }
179         if (*status==(char)0xe5) continue;
180         for (i=0; i<3; ++i)
181         {
182           c=&(dir->ext[i]);
183           if (!ISFILECHAR(1,*c&0x7f) || islower(*c&0x7f))
184           {
185             printf("Error: Bad name (extent=%d, name=\"%s\", position=%d)\n",extent,prfile(sb,extent),i);
186             if (ask("Remove file"))
187             {
188               *status=(char)0xE5;
189               ret|=MODIFIED;
190               break;
191             }
192             else ret|=BROKEN;
193           }
194         }
195         if (*status==(char)0xe5) continue;
196       }
197       /*}}}*/
198       /* check extent number */ /*{{{*/
199       if ((dir->extnol&0xff)>0x1f)
200       {
201         printf("Error: Bad lower bits of extent number (extent=%d, name=\"%s\", low bits=%d)\n",extent,prfile(sb,extent),dir->extnol&0xff);
202         if (ask("Remove file"))
203         {
204           *status=(char)0xE5;
205           ret|=MODIFIED;
206         }
207         else ret|=BROKEN;
208       }
209       if (*status==(char)0xe5) continue;
210       if ((dir->extnoh&0xff)>0x3f)
211       {
212         printf("Error: Bad higher bits of extent number (extent=%d, name=\"%s\", high bits=%d)\n",extent,prfile(sb,extent),dir->extnoh&0xff);
213         if (ask("Remove file"))
214         {
215           *status=(char)0xE5;
216           ret|=MODIFIED;
217         }
218         else ret|=BROKEN;
219       }
220       if (*status==(char)0xe5) continue;
221       /*}}}*/
222       /* check last record byte count */ /*{{{*/
223       if ((dir->lrc&0xff)>128)
224       {
225         printf("Error: Bad last record byte count (extent=%d, name=\"%s\", lrc=%d)\n",extent,prfile(sb,extent),dir->lrc&0xff);
226         if (ask("Clear last record byte count"))
227         {
228           dir->lrc=(char)0;
229           ret|=MODIFIED;
230         }
231         else ret|=BROKEN;
232       }
233       if (*status==(char)0xe5) continue;
234       /*}}}*/
235       /* check block number range */ /*{{{*/
236       {
237         int block,min,max,i;
238
239         min=sb->dirblks;
240         max=sb->size;
241         for (i=0; i<16; ++i)
242         {
243           block=dir->pointers[i]&0xff;
244           if (sb->size>256) block+=(dir->pointers[++i]&0xff)<<8;
245           if (block>0)
246           {
247             ++usedBlocks;
248             if (block<min || block>=max)
249             {
250               printf("Error: Bad block number (extent=%d, name=\"%s\", block=%d)\n",extent,prfile(sb,extent),block);
251               if (ask("Remove file"))
252               {
253                 *status=(char)0xE5;
254                 ret|=MODIFIED;
255                 break;
256               }
257               else ret|=BROKEN;
258             }
259           }
260         }
261         if (*status==(char)0xe5) continue;
262       }
263       /*}}}*/
264       /* check number of used blocks ? */ /*{{{*/
265       /*}}}*/
266       /* check record count */ /*{{{*/
267       {
268         int i,min,max,recordsInBlocks,used=0;
269
270         min=(dir->extnol%sb->extents)*16/sb->extents;
271         max=((dir->extnol%sb->extents)+1)*16/sb->extents;
272         assert(min<max);
273         for (i=min; i<max; ++i)
274         {
275         /* [JCE] Rewritten because the previous implementation didn't work
276          *       properly with Visual C++ */
277           if (dir->pointers[i] || (sb->size>256 && dir->pointers[i+1])) ++used;
278           if (sb->size>256) ++i;
279         }
280         recordsInBlocks=(((unsigned char)dir->blkcnt)*128+sb->blksiz-1)/sb->blksiz;
281         if (recordsInBlocks!=used)
282         {
283           printf("Error: Bad record count (extent=%d, name=\"%s\", record count=%d)\n",extent,prfile(sb,extent),dir->blkcnt&0xff);
284           if (ask("Remove file"))
285           {
286             *status=(char)0xE5;
287             ret|=MODIFIED;
288           }
289           else ret|=BROKEN;
290         }
291         if (*status==(char)0xe5) continue;
292       }
293       /*}}}*/
294       /* check for too large .com files */ /*{{{*/
295       if (((EXTENT(dir->extnol,dir->extnoh)==3 && dir->blkcnt>=126) || EXTENT(dir->extnol,dir->extnoh)>=4) && (dir->ext[0]&0x7f)=='C' && (dir->ext[1]&0x7f)=='O' && (dir->ext[2]&0x7f)=='M')
296       {
297         printf("Warning: Oversized .COM file (extent=%d, name=\"%s\")\n",extent,prfile(sb,extent));
298       }
299       /*}}}*/
300     }
301     /*}}}*/
302     else if ((sb->type==CPMFS_P2DOS || sb->type==CPMFS_DR3) && *status==33) /* check time stamps ? */ /*{{{*/
303     {
304       unsigned long created,modified;
305       char s;
306
307       if ((s=sb->dir[extent2=(extent&~3)].status)>=0 && s<=(sb->type==CPMFS_P2DOS ? 31 : 15)) /* time stamps for first of the three extents */ /*{{{*/
308       {
309         bcdCheck(dir->name[2],24,sb->cnotatime ? "creation date" : "access date","hour",extent,extent2);
310         bcdCheck(dir->name[3],60,sb->cnotatime ? "creation date" : "access date","minute",extent,extent2);
311         bcdCheck(dir->name[6],24,"modification date","hour",extent,extent2);
312         bcdCheck(dir->name[7],60,"modification date","minute",extent,extent2);
313         created=(dir->name[4]+(dir->name[1]<<8))*(0x60*0x60)+dir->name[2]*0x60+dir->name[3];
314         modified=(dir->name[0]+(dir->name[5]<<8))*(0x60*0x60)+dir->name[6]*0x60+dir->name[7];
315         if (sb->cnotatime && modified<created)
316         {
317           printf("Warning: Modification date earlier than creation date (extent=%d/%d)\n",extent,extent2);
318         }
319       }
320       /*}}}*/
321       if ((s=sb->dir[extent2=(extent&~3)+1].status)>=0 && s<=(sb->type==CPMFS_P2DOS ? 31 : 15)) /* time stamps for second */ /*{{{*/
322       {
323         bcdCheck(dir->lrc,24,sb->cnotatime ? "creation date" : "access date","hour",extent,extent2);
324         bcdCheck(dir->extnoh,60,sb->cnotatime ? "creation date" : "access date","minute",extent,extent2);
325         bcdCheck(dir->pointers[1],24,"modification date","hour",extent,extent2);
326         bcdCheck(dir->pointers[2],60,"modification date","minute",extent,extent2);
327         created=(dir->ext[2]+(dir->extnol<<8))*(0x60*0x60)+dir->lrc*0x60+dir->extnoh;
328         modified=(dir->blkcnt+(dir->pointers[0]<<8))*(0x60*0x60)+dir->pointers[1]*0x60+dir->pointers[2];
329         if (sb->cnotatime && modified<created)
330         {
331           printf("Warning: Modification date earlier than creation date (extent=%d/%d)\n",extent,extent2);
332         }
333       }
334       /*}}}*/
335       if ((s=sb->dir[extent2=(extent&~3)+2].status)>=0 && s<=(sb->type==CPMFS_P2DOS ? 31 : 15)) /* time stamps for third */ /*{{{*/
336       {
337         bcdCheck(dir->pointers[7],24,sb->cnotatime ? "creation date" : "access date","hour",extent,extent2);
338         bcdCheck(dir->pointers[8],60,sb->cnotatime ? "creation date" : "access date","minute",extent,extent2);
339         bcdCheck(dir->pointers[11],24,"modification date","hour",extent,extent2);
340         bcdCheck(dir->pointers[12],60,"modification date","minute",extent,extent2);
341         created=(dir->pointers[5]+(dir->pointers[6]<<8))*(0x60*0x60)+dir->pointers[7]*0x60+dir->pointers[8];
342         modified=(dir->pointers[9]+(dir->pointers[10]<<8))*(0x60*0x60)+dir->pointers[11]*0x60+dir->pointers[12];
343         if (sb->cnotatime && modified<created)
344         {
345           printf("Warning: Modification date earlier than creation date (extent=%d/%d)\n",extent,extent2);
346         }
347       }
348       /*}}}*/
349     }
350     /*}}}*/
351     else if (sb->type==CPMFS_DR3 && *status==32) /* disc label */ /*{{{*/
352     {
353       unsigned long created,modified;
354
355       bcdCheck(dir->pointers[10],24,sb->cnotatime ? "creation date" : "access date","hour",extent,extent);
356       bcdCheck(dir->pointers[11],60,sb->cnotatime ? "creation date" : "access date","minute",extent,extent);
357       bcdCheck(dir->pointers[14],24,"modification date","hour",extent,extent);
358       bcdCheck(dir->pointers[15],60,"modification date","minute",extent,extent);
359       created=(dir->pointers[8]+(dir->pointers[9]<<8))*(0x60*0x60)+dir->pointers[10]*0x60+dir->pointers[11];
360       modified=(dir->pointers[12]+(dir->pointers[13]<<8))*(0x60*0x60)+dir->pointers[14]*0x60+dir->pointers[15];
361       if (sb->cnotatime && modified<created)
362       {
363         printf("Warning: Label modification date earlier than creation date (extent=%d)\n",extent);
364       }
365       if (dir->extnol&0x40 && dir->extnol&0x10)
366       {
367         printf("Error: Bit 4 and 6 can only be exclusively be set (extent=%d, label byte=0x%02x)\n",extent,(unsigned char)dir->extnol);
368         if (ask("Time stamp on creation"))
369         {
370           dir->extnol&=~0x40;
371           ret|=MODIFIED;
372         }
373         else if (ask("Time stamp on access"))
374         {
375           dir->extnol&=~0x10;
376           ret|=MODIFIED;
377         }
378         else ret|=BROKEN;
379       }
380       if (dir->extnol&0x80 && pwdCheck(extent,dir->pointers,dir->lrc))
381       {
382         char msg[80];
383
384         sprintf(msg,"Set password to %c%c%c%c%c%c%c%c",T0,T1,T2,T3,T4,T5,T6,T7);
385         if (ask(msg))
386         {
387           dir->pointers[0]=P0;
388           dir->pointers[1]=P1;
389           dir->pointers[2]=P2;
390           dir->pointers[3]=P3;
391           dir->pointers[4]=P4;
392           dir->pointers[5]=P5;
393           dir->pointers[6]=P6;
394           dir->pointers[7]=P7;
395           dir->lrc=PB;
396           ret|=MODIFIED;
397         }
398         else ret|=BROKEN;
399       }
400     }
401     /*}}}*/
402     else if (sb->type==CPMFS_DR3 && *status>=16 && *status<=31) /* password */ /*{{{*/
403     {
404       /* check name and extension */ /*{{{*/
405       {
406         int i;
407         char *c;
408
409         for (i=0; i<8; ++i)
410         {
411           c=&(dir->name[i]);
412           if (!ISFILECHAR(i,*c&0x7f) || islower(*c&0x7f))
413           {
414             printf("Error: Bad name (extent=%d, name=\"%s\", position=%d)\n",extent,prfile(sb,extent),i);
415             if (ask("Clear password entry"))
416             {
417               *status=(char)0xE5;
418               ret|=MODIFIED;
419               break;
420             }
421             else ret|=BROKEN;
422           }
423         }
424         if (*status==(char)0xe5) continue;
425         for (i=0; i<3; ++i)
426         {
427           c=&(dir->ext[i]);
428           if (!ISFILECHAR(1,*c&0x7f) || islower(*c&0x7f))
429           {
430             printf("Error: Bad name (extent=%d, name=\"%s\", position=%d)\n",extent,prfile(sb,extent),i);
431             if (ask("Clear password entry"))
432             {
433               *status=(char)0xE5;
434               ret|=MODIFIED;
435               break;
436             }
437             else ret|=BROKEN;
438           }
439         }
440         if (*status==(char)0xe5) continue;
441       }
442       /*}}}*/
443       /* check password */ /*{{{*/
444       if (dir->extnol&(0x80|0x40|0x20) && pwdCheck(extent,dir->pointers,dir->lrc))
445       {
446         char msg[80];
447
448         sprintf(msg,"Set password to %c%c%c%c%c%c%c%c",T0,T1,T2,T3,T4,T5,T6,T7);
449         if (ask(msg))
450         {
451           dir->pointers[0]=P0;
452           dir->pointers[1]=P1;
453           dir->pointers[2]=P2;
454           dir->pointers[3]=P3;
455           dir->pointers[4]=P4;
456           dir->pointers[5]=P5;
457           dir->pointers[6]=P6;
458           dir->pointers[7]=P7;
459           dir->lrc=PB;
460           ret|=MODIFIED;
461         }
462         else ret|=BROKEN;
463       }
464       /*}}}*/
465     }
466     /*}}}*/
467     else if (*status!=(char)0xe5) /* bad status */ /*{{{*/
468     {
469       printf("Error: Bad status (extent=%d, name=\"%s\", status=0x%02x)\n",extent,prfile(sb,extent),*status&0xff);
470       if (ask("Clear entry"))
471       {
472         *status=(char)0xE5;
473         ret|=MODIFIED;
474       }
475       else ret|=BROKEN;
476       continue;
477     }
478     /*}}}*/
479   }
480   /*}}}*/
481   /* Phase 2: check extent connectivity */ /*{{{*/
482   printf("Phase 2: check extent connectivity\n");
483   /* check multiple allocated blocks */ /*{{{*/
484   for (extent=0; extent<sb->maxdir; ++extent) if ((dir=sb->dir+extent)->status>=0 && dir->status<=(sb->type==CPMFS_P2DOS ? 31 : 15))
485   {
486     int i,j,block,block2;
487
488     for (i=0; i<16; ++i)
489     {
490       block=dir->pointers[i]&0xff;
491       if (sb->size>256) block+=(dir->pointers[++i]&0xff)<<8;
492       for (extent2=0; extent2<sb->maxdir; ++extent2) if ((dir2=sb->dir+extent2)->status>=0 && dir2->status<=(sb->type==CPMFS_P2DOS ? 31 : 15))
493       {
494         for (j=0; j<16; ++j)
495         {
496           block2=dir2->pointers[j]&0xff;
497           if (sb->size>256) block2+=(dir2->pointers[++j]&0xff)<<8;
498           if (block!=0 && block2!=0 && block==block2 && !(extent==extent2 && i==j))
499           {
500             printf("Error: Multiple allocated block (extent=%d,%d, name=\"%s\"",extent,extent2,prfile(sb,extent));
501             printf(",\"%s\" block=%d)\n",prfile(sb,extent2),block);
502             ret|=BROKEN;
503           }
504         }
505       }
506     }
507   }
508   /*}}}*/
509   /* check multiple extents */ /*{{{*/
510   for (extent=0; extent<sb->maxdir; ++extent) if ((dir=sb->dir+extent)->status>=0 && dir->status<=(sb->type==CPMFS_P2DOS ? 31 : 15))
511   {
512     for (extent2=0; extent2<sb->maxdir; ++extent2) if ((dir2=sb->dir+extent2)->status>=0 && dir2->status<=(sb->type==CPMFS_P2DOS ? 31 : 15))
513     {
514       if (extent!=extent2 && EXTENT(dir->extnol,dir->extnoh)==EXTENT(dir2->extnol,dir2->extnoh) && dir->status==dir2->status)
515       {
516         int i;
517
518         for (i=0; i<8 && (dir->name[i]&0x7f)==(dir2->name[i]&0x7f); ++i);
519         if (i==8)
520         {
521           for (i=0; i<3 && (dir->ext[i]&0x7f)==(dir2->ext[i]&0x7f); ++i);
522           if (i==3)
523           {
524             printf("Error: Duplicate extent (extent=%d,%d)\n",extent,extent2);
525             ret|=BROKEN;
526           }
527         }
528       }
529     }
530   }
531   /*}}}*/
532   /*}}}*/
533   if (ret==0) /* print statistics */ /*{{{*/
534   {
535     struct cpmStatFS statfsbuf;
536     int fragmented=0,borders=0;
537
538     cpmStatFS(root,&statfsbuf);
539     for (extent=0; extent<sb->maxdir; ++extent) if ((dir=sb->dir+extent)->status>=0 && dir->status<=(sb->type==CPMFS_P2DOS ? 31 : 15))
540     {
541       int i,block,previous=-1;
542
543       for (i=0; i<16; ++i)
544       {
545         block=dir->pointers[i]&0xff;
546         if (sb->size>256) block+=(dir->pointers[++i]&0xff)<<8;
547         if (previous!=-1)
548         {
549           if (block!=0 && block!=(previous+1)) ++fragmented;
550           ++borders;
551         }
552         previous=block;
553       }
554     }
555     fragmented=(borders ? (1000*fragmented)/borders : 0);
556     printf("%s: %ld/%ld files (%d.%d%% non-contigous), %ld/%ld blocks\n",image,statfsbuf.f_files-statfsbuf.f_ffree,statfsbuf.f_files,fragmented/10,fragmented%10,statfsbuf.f_blocks-statfsbuf.f_bfree,statfsbuf.f_blocks);
557   }
558   /*}}}*/
559   return ret;
560 }
561 /*}}}*/
562
563 const char cmd[]="fsck.cpm";
564
565 /* main */ /*{{{*/
566 int main(int argc, char *argv[])
567 {
568   const char *err;
569   const char *image;
570   const char *format;
571   const char *devopts=NULL;
572   int uppercase=0;
573   int c,usage=0;
574   struct cpmSuperBlock sb;
575   struct cpmInode root;
576   enum Result ret;
577
578   if (!(format=getenv("CPMTOOLSFMT"))) format=FORMAT;
579   while ((c=getopt(argc,argv,"T:f:nuh?"))!=EOF) switch(c)
580   {
581     case 'f': format=optarg; break;
582     case 'T': devopts=optarg; break;
583     case 'n': norepair=1; break;
584     case 'u': uppercase=1; break;
585     case 'h':
586     case '?': usage=1; break;
587   }
588
589   if (optind!=(argc-1)) usage=1;
590   else image=argv[optind++];
591
592   if (usage)
593   {
594     fprintf(stderr,"Usage: %s [-f format] [-n] image\n",cmd);
595     exit(1);
596   }
597   if ((err=Device_open(&sb.dev, image, (norepair ? O_RDONLY : O_RDWR), devopts)))
598   {
599     if ((err=Device_open(&sb.dev, image,O_RDONLY, devopts)))
600     {
601       fprintf(stderr,"%s: cannot open %s: %s\n",cmd,image,err);
602       exit(1);
603     }
604     else
605     {
606       fprintf(stderr,"%s: cannot open %s for writing, no repair possible\n",cmd,image);
607     }
608   }
609   if (cpmReadSuper(&sb,&root,format,uppercase)==-1)
610   {
611     fprintf(stderr,"%s: cannot read superblock (%s)\n",cmd,boo);
612     exit(1);
613   }
614   ret=fsck(&root,image);
615   if (ret&MODIFIED)
616   {
617     if (cpmSync(&sb)==-1)
618     {
619       fprintf(stderr,"%s: write error on %s: %s\n",cmd,image,strerror(errno));
620       ret|=BROKEN;
621     }
622     fprintf(stderr,"%s: FILE SYSTEM ON %s MODIFIED",cmd,image);
623     if (ret&BROKEN) fprintf(stderr,", PLEASE CHECK AGAIN");
624     fprintf(stderr,"\n");
625   }
626   cpmUmount(&sb);
627   if (ret&BROKEN) return 2;
628   else return 0;
629 }
630 /*}}}*/