badblocks.c

Go to the documentation of this file.
00001 /* badblocks - collect bad blocks in a file     Author: Jacob Bunschoten */
00002 
00003 /* Usage "badblocks block_special [Up_to_7_blocks]" */
00004 
00005 /* This program is written to handle BADBLOCKS on a hard or floppy disk.
00006  * The program asks for block_numbers. These numbers can be obtained with
00007  * the program readall, written by A. Tanenbaum.  It then creates a
00008  * file on the disk containing up to 7 bad blocks.
00009  *
00010  * BUG:
00011  *
00012  *      When the zone_size > block_size it can happen that
00013  *      the zone is already allocated. This means some
00014  *      file is using this zone and may use all the blocks including
00015  *      the bad one. This can be cured by inspecting the zone_bitmap
00016  *      (is already done) and change the file if this zone is used.
00017  *      This means that another zone must be allocated and
00018  *      the inode wich claims this zone must be found and changed.
00019  *
00020  */
00021 
00022 #include <sys/types.h>
00023 #include <sys/stat.h>
00024 #include <minix/config.h>
00025 #include <minix/type.h>
00026 #include <fcntl.h>
00027 #include <unistd.h>
00028 #include <dirent.h>
00029 #include <stdlib.h>
00030 
00031 #include "../../servers/fs/const.h"     /* must be included before stdio.h */
00032 #undef printf                   /* so its define of printf can be undone */
00033 #include "../../servers/fs/type.h"
00034 
00035 #include <string.h>
00036 #include <stdio.h>
00037 
00038 #define EXTERN extern
00039 #include "../../servers/fs/super.h"
00040 
00041 _PROTOTYPE(int main, (int argc, char **argv));
00042 _PROTOTYPE(void rw_super, (int flag));
00043 _PROTOTYPE(void get_super, (void));
00044 _PROTOTYPE(void put_super, (void));
00045 _PROTOTYPE(void rw_inode, (struct stat * stat_ptr, int rw_mode));
00046 _PROTOTYPE(void get_inode, (struct stat * stat_ptr));
00047 _PROTOTYPE(void put_inode, (struct stat * stat_ptr));
00048 _PROTOTYPE(long rd_cmdline, (int argc, char *argv[]));
00049 _PROTOTYPE(void modify, (int nr_blocks));
00050 _PROTOTYPE(void save_blk, (block_t blk_num));
00051 _PROTOTYPE(void reset_blks, (void));
00052 _PROTOTYPE(void show_blks, (void));
00053 _PROTOTYPE(int blk_is_used, (block_t blk_num));
00054 _PROTOTYPE(int blk_ok, (block_t num));
00055 _PROTOTYPE(void set_bit, (zone_t num));
00056 _PROTOTYPE(long rd_num, (void));
00057 _PROTOTYPE(int ok, (char *str));
00058 _PROTOTYPE(void done, (int nr));
00059 
00060 /*              Super block table.
00061  *
00062  *      The disk layout is:
00063  *
00064  *      Item        # block
00065  *    boot block      1
00066  *    super block     1
00067  *    inode map     s_imap_blocks
00068  *    zone map      s_zmap_blocks
00069  *    inodes        (s_ninodes + 1 + inodes_per_block - 1)/inodes_per_block
00070  *    unused
00071  *    data zones    (s_nzones - s_firstdatazone) << s_log_zone_size
00072  *
00073  */
00074 
00075 #define OK      0
00076 #define NOT_OK  1
00077 #define QUIT    2
00078 
00079 #define READ    0
00080 #define WRITE   1
00081 
00082 #define HARMLESS        0
00083 #define DIR_CREATED     1
00084 #define DEV_MOUNTED     2
00085 #define FILE_EXISTS     3
00086 #define SUCCESS         4
00087 
00088 #define BYTE         0377
00089 #define BLOCK_SIZE   1024
00090 #define SIZE_OF_INT   (sizeof (int) )
00091 
00092 /* Define V_NR_DZONES as the larger of V1_NR_DZONES and V2_NR_DZONES. */
00093 #if (V1_NR_DZONES > V2_NR_DZONES)
00094 #define V_NR_DZONES V1_NR_DZONES
00095 #define V_SMALLER   V2_NR_DZONES
00096 #else
00097 #define V_NR_DZONES V2_NR_DZONES
00098 #define V_SMALLER   V1_NR_DZONES
00099 #endif
00100 
00101 #if 0
00102 struct super_block {
00103   ino_t s_ninodes;              /* # usable inodes on the minor device */
00104   zone1_t s_nzones;             /* total device size, including bit maps etc */
00105   short s_imap_blocks;          /* # of blocks used by inode bit map */
00106   short s_zmap_blocks;          /* # of blocks used by zone bit map */
00107   zone1_t s_firstdatazone;      /* number of first data zone */
00108   short s_log_zone_size;        /* log2 of blocks/zone */
00109   off_t s_max_size;             /* maximum file size on this device */
00110   short s_magic;                /* magic number to recognize super-blocks */
00111   short s_pad;                  /* try to avoid compiler-dependent padding */
00112   zone_t s_zones;               /* number of zones (replaces s_nzones in V2) */
00113 } super_block;
00114 #endif
00115 
00116  /* ====== globals ======= */
00117 
00118 char *dev_name;
00119 char f_name[] = ".Bad_XXXXXX";
00120 char file_name[50];
00121 char dir_name[] = "/tmpXXXXXX";
00122 
00123 block_t block[V_NR_DZONES + 1]; /* last block contains zero */
00124 int interactive;                /* 1 if interactive (argc == 2) */
00125 int position = 2;               /* next block # is argv[position] */
00126 
00127 FILE *f;
00128 int fd;
00129 int eofseen;                    /* set if '\n' seen */
00130 struct stat stat_buf;
00131 struct super_block *sp, sbs;
00132 int inodes_per_block;
00133 size_t inode_size;
00134 int v1fs = 0, v2fs = 0;                 /* TRUE for V1 file system, FALSE for V2 */
00135 
00136 d1_inode d1inode;               /* declare a V1 disk inode */
00137 d1_inode *ip1;
00138 d2_inode d2inode;               /* declare a V2 disk inode */
00139 d2_inode *ip2;
00140 
00141 
00142  /* ====== super block routines ======= */
00143 
00144 void rw_super(flag)
00145 int flag;
00146 {                               /* read or write a superblock */
00147   int rwd;
00148 
00149   lseek(fd, 0L, SEEK_SET);      /* rewind */
00150   lseek(fd, (long) BLOCK_SIZE, SEEK_SET);       /* seek */
00151 
00152   if (flag == READ)
00153         rwd = read(fd, (char *) sp, SUPER_SIZE);
00154   else
00155         rwd = write(fd, (char *) sp, SUPER_SIZE);
00156   if (rwd != SUPER_SIZE) {      /* ok ? */
00157         printf("Bad %s in get_super() (should be %u is %d)\n",
00158                flag == READ ? "read" : "write",
00159                (unsigned) SUPER_SIZE, rwd);
00160         done(DIR_CREATED);
00161   }
00162 }
00163 
00164 void get_super()
00165  /* Get super_block. global pointer sp is used */
00166 {
00167   rw_super(READ);
00168 
00169   if (sp->s_magic == SUPER_MAGIC) {
00170         /* This is a V1 file system. */
00171         v1fs = 1;               /* file system is not V2 */
00172   } else if (sp->s_magic == SUPER_V2) {
00173         /* This is a V2 file system. */
00174         v2fs = 1;               /* this is a V2 file system */
00175   } else if (sp->s_magic == SUPER_V3) {
00176         v1fs = v2fs = 0;                /* this is a V3 file system */
00177   } else {
00178         /* Neither V1 nor V2 nor V3. */
00179         printf("Bad magic number in super_block (0x%x)\n",
00180                (unsigned) sp->s_magic);
00181         done(DIR_CREATED);
00182   }
00183 }
00184 
00185 
00186 void put_super()
00187 {
00188   rw_super(WRITE);
00189 }
00190 
00191  /* ========== inode routines =========== */
00192 
00193 void rw_inode(stat_ptr, rw_mode)
00194 struct stat *stat_ptr;
00195 int rw_mode;
00196 {
00197   int rwd;
00198   ino_t i_num;
00199   block_t blk, offset;
00200 
00201 
00202   i_num = stat_ptr->st_ino;
00203 
00204   blk = (block_t) (2 + sp->s_imap_blocks + sp->s_zmap_blocks);
00205   blk += (block_t) ((i_num - 1) / inodes_per_block);
00206   blk *= (block_t) (BLOCK_SIZE);/* this block */
00207 
00208   offset = (block_t) ((i_num - 1) % inodes_per_block);
00209   offset *= (block_t) (inode_size);     /* and this offset */
00210 
00211   lseek(fd, (off_t) 0, SEEK_SET);       /* rewind */
00212   lseek(fd, (off_t) (blk + offset), SEEK_SET);  /* seek */
00213 
00214   /* Pointer is at the inode */
00215   if (v1fs) {
00216         /* This is a V1 file system. */
00217         if (rw_mode == READ) {  /* read it */
00218                 rwd = read(fd, (char *) ip1, inode_size);
00219         } else {                /* write it */
00220                 rwd = write(fd, (char *) ip1, inode_size);
00221         }
00222   } else {
00223         /* This is a V2 file system. */
00224         if (rw_mode == READ) {  /* read it */
00225                 rwd = read(fd, (char *) ip2, inode_size);
00226         } else {                /* write it */
00227                 rwd = write(fd, (char *) ip2, inode_size);
00228         }
00229   }
00230 
00231   if (rwd != inode_size) {      /* ok ? */
00232         printf("Bad %s in get_inode()\n", (rw_mode == READ) ? "read" :
00233                "write");
00234         done(DIR_CREATED);
00235   }
00236 }
00237 
00238 void get_inode(stat_ptr)
00239 struct stat *stat_ptr;
00240 {
00241 
00242   int cnt;
00243 
00244   rw_inode(stat_ptr, READ);
00245 
00246   if (v1fs) {
00247         for (cnt = 0; cnt < V1_NR_TZONES; cnt++)
00248                 ip1->d1_zone[cnt] = 0;  /* Just to be safe */
00249   } else {
00250         for (cnt = 0; cnt < V2_NR_TZONES; cnt++)
00251                 ip2->d2_zone[cnt] = 0;  /* Just to be safe */
00252   }
00253 }
00254 
00255 void put_inode(stat_ptr)
00256 struct stat *stat_ptr;
00257 {
00258   rw_inode(stat_ptr, WRITE);
00259 }
00260 
00261 
00262  /* ==============  main program ================= */
00263 int main(argc, argv)
00264 int argc;
00265 char *argv[];
00266 {
00267   int cnt, finished;
00268   block_t blk_nr;
00269   struct stat dev_stat;
00270   FILE *fp;
00271   int block_size;
00272 
00273   sp = &sbs;
00274   ip1 = &d1inode;
00275   ip2 = &d2inode;
00276 
00277   if (argc < 2 || argc > 9) {
00278         fprintf(stderr, "Usage: %s block_special [up_to_7_blocks]\n", argv[0]);
00279         done(HARMLESS);
00280   }
00281   interactive = (argc == 2 ? 1 : 0);
00282 
00283   /* Do some test. */
00284   if (geteuid()) {
00285         printf("Sorry, not in superuser mode \n");
00286         printf("Set_uid bit must be on or you must become super_user\n");
00287         done(HARMLESS);
00288   }
00289   dev_name = argv[1];
00290   mktemp(dir_name);
00291   if (mkdir(dir_name, 0777) == -1) {
00292         fprintf(stderr, "%s is already used in system\n", dir_name);
00293         done(HARMLESS);
00294   }
00295 
00296   /* Mount device. This call may fail. */
00297   mount(dev_name, dir_name, 0);
00298   /* Succes. dev was mounted, try to umount */
00299 
00300   /* Umount device. Playing with the file system while other processes
00301    * have access to this device is asking for trouble */
00302   if (umount(dev_name) == -1) {
00303         printf("Could not umount device %s.\n", dev_name);
00304         done(HARMLESS);
00305   }
00306   mktemp(f_name);
00307   /* Create "/tmpXXXXpid/.BadXXpid" */
00308   strcat(file_name, dir_name);
00309   strcat(file_name, "/");
00310   strcat(file_name, f_name);
00311 
00312   if (mount(dev_name, dir_name, 0) == -1) {     /* this call should work */
00313         fprintf(stderr, "Could not mount device anymore\n");
00314         done(HARMLESS);
00315   }
00316   if (stat(file_name, &stat_buf) != -1) {
00317         printf("File %s already exists\n", file_name);
00318         done(DEV_MOUNTED);
00319   }
00320   if ((fp = fopen(file_name, "w")) == NULL) {
00321         printf("Cannot create file %s\n", file_name);
00322         done(DEV_MOUNTED);
00323   }
00324   chmod(file_name, 0);          /* "useless" file */
00325   if (stat(file_name, &stat_buf) == -1) {
00326         printf("What? Second call from stat failed\n");
00327         done(FILE_EXISTS);
00328   }
00329 
00330   /* Stat buf must be safed. We can now calculate the inode on disk */
00331   fclose(fp);
00332 
00333   /* ===== the badblock file is created ===== */
00334 
00335   if (umount(dev_name) == -1) {
00336         printf("Can not umount device anymore??? \n");
00337         done(DIR_CREATED);
00338   }
00339   if ((fd = open(dev_name, O_RDWR)) == -1) {
00340         printf("Can not open device %s\n", dev_name);
00341         done(DEV_MOUNTED);
00342   }
00343   if (fstat(fd, &dev_stat) == -1) {
00344         printf("fstat on device %s failed\n", dev_name);
00345         done(DEV_MOUNTED);
00346   }
00347   if ((dev_stat.st_mode & S_IFMT) != S_IFBLK) {
00348         printf("Device \"%s\" is not a block_special.\n", dev_name);
00349         done(DEV_MOUNTED);
00350   }
00351   get_super();
00352   if (sp->s_log_zone_size) {
00353         printf("Block_size != zone_size.");
00354         printf("This program can not handle it\n");
00355         done(DIR_CREATED);
00356   }
00357   if(v1fs || v2fs) block_size = 1024;
00358   else block_size = sp->s_block_size;
00359 
00360   /* The number of inodes in a block differs in V1 and V2. */
00361   if (v1fs) {
00362         inodes_per_block = V1_INODES_PER_BLOCK;
00363         inode_size = V1_INODE_SIZE;
00364   } else {
00365         inodes_per_block = V2_INODES_PER_BLOCK(block_size);
00366         inode_size = V2_INODE_SIZE;
00367   }
00368 
00369   get_inode(&stat_buf);
00370 
00371   for (finished = 0; !finished;) {
00372         if (interactive)
00373                 printf("Give up to %d bad block numbers separated by spaces\n",
00374                        V_SMALLER);
00375         reset_blks();
00376         cnt = 0;                /* cnt keep track of the zone's */
00377         while (cnt < V_SMALLER) {
00378                 int tst;
00379 
00380                 if (interactive)
00381                         blk_nr = rd_num();
00382                 else
00383                         blk_nr = rd_cmdline(argc, argv);
00384                 if (blk_nr == -1) break;
00385                 tst = blk_ok(blk_nr);
00386 
00387                 /* Test if this block is free */
00388                 if (tst == OK) {
00389                         cnt++;
00390                         save_blk(blk_nr);
00391                 } else if (tst == QUIT)
00392                         break;
00393         }
00394         if (interactive) show_blks();
00395         if (!cnt) done(FILE_EXISTS);
00396         if (interactive) {
00397                 switch (ok("All these blocks ok <y/n/q> (y:Device will change) ")) {
00398                     case OK:    finished = 1;   break;
00399                     case NOT_OK:
00400                         break;
00401                     case QUIT:  done(FILE_EXISTS);
00402                 }
00403         } else {
00404                 finished = 1;
00405         }
00406   }
00407 
00408   modify(cnt);
00409   close(fd);                    /* free device */
00410   done(SUCCESS);
00411   return(0);
00412 }
00413 
00414 long rd_cmdline(argc, argv)
00415 int argc;
00416 char *argv[];
00417 {
00418   if (position == argc) return(-1);
00419   return(atol(argv[position++]));
00420 }
00421 
00422 
00423 void modify(nr_blocks)
00424 int nr_blocks;
00425 {
00426   int i;
00427 
00428   if (nr_blocks == 0) return;
00429   if (v1fs) {
00430         /* This is a V1 file system. */
00431         for (i = 0; i < nr_blocks; i++) {
00432                 set_bit(block[i]);
00433                 ip1->d1_zone[i] = block[i];
00434         }
00435   } else {
00436         /* This is a V2 file system. */
00437         for (i = 0; i < nr_blocks; i++) {
00438                 set_bit(block[i]);
00439                 ip2->d2_zone[i] = block[i];
00440         }
00441   }
00442   if (v1fs) {
00443         ip1->d1_size = (long) (BLOCK_SIZE * nr_blocks); /* give file size */
00444         ip1->d1_mtime = 0;      /* Who wants a file from 1970? */
00445   } else {
00446         ip2->d2_size = (long) (BLOCK_SIZE * nr_blocks); /* give file size */
00447         ip2->d2_atime = ip2->d2_mtime = ip2->d2_ctime = 0;
00448   }
00449 
00450   put_inode(&stat_buf);         /* save the inode on disk */
00451   put_super();                  /* bit_maps too */
00452 }
00453 
00454 
00455 static blk_cnt = 0;
00456 
00457 void save_blk(blk_num)
00458 block_t blk_num;
00459 {
00460   block[blk_cnt++] = blk_num;
00461 }
00462 
00463 void reset_blks()
00464 {
00465   int i;
00466 
00467   for (i = 0; i <= V_NR_DZONES; i++)
00468         block[i] = 0;           /* Note: Last block_number is set to zero */
00469   blk_cnt = 0;
00470 }
00471 
00472 void show_blks()
00473 {
00474   int i;
00475 
00476   for (i = 0; i < blk_cnt; i++)
00477         printf("Block[%d] = %lu\n", i, (unsigned long) block[i]);
00478 }
00479 
00480 int blk_is_used(blk_num)
00481 block_t blk_num;
00482 {                               /* return TRUE(1) if used */
00483   int i;
00484 
00485   for (i = 0; block[i] && block[i] != blk_num; i++);
00486   return(block[i] != 0) ? 1 : 0;
00487 }
00488 
00489 
00490  /* ===== bitmap handling ======         */
00491 
00492 #define BIT_MAP_SHIFT   13
00493 #define INT_BITS        (SIZE_OF_INT << 3)
00494 
00495 int blk_ok(num)                 /* is this zone free (y/n) */
00496 block_t num;
00497 {
00498   block_t blk_offset;
00499   int rd;
00500   int blk, offset, words, bit, tst_word;
00501   zone_t z_num;
00502 
00503   if (blk_is_used(num)) {
00504         printf("Duplicate block (%lu) given\n", (unsigned long) num);
00505         return NOT_OK;
00506   }
00507 
00508   /* Assumption zone_size == block_size */
00509 
00510   z_num = num - (sp->s_firstdatazone - 1);      /* account offset */
00511 
00512   /* Calculate the word in the bitmap. */
00513   blk = z_num >> BIT_MAP_SHIFT; /* which block */
00514   offset = z_num - (blk << BIT_MAP_SHIFT);      /* offset */
00515   words = z_num / INT_BITS;     /* which word */
00516 
00517   blk_offset = (block_t) (2 + sp->s_imap_blocks);       /* zone map */
00518   blk_offset *= (block_t) BLOCK_SIZE;   /* of course in block */
00519   blk_offset += (block_t) (words * SIZE_OF_INT);        /* offset */
00520 
00521 
00522   lseek(fd, (off_t) 0, SEEK_SET);       /* rewind */
00523   lseek(fd, (off_t) blk_offset, SEEK_SET);      /* set pointer at word */
00524 
00525   rd = read(fd, (char *) &tst_word, SIZE_OF_INT);
00526   if (rd != SIZE_OF_INT) {
00527         printf("Read error in bitmap\n");
00528         done(DIR_CREATED);
00529   }
00530 
00531   /* We have the tst_word, check if bit was off */
00532   bit = offset % INT_BITS;
00533 
00534   if (((tst_word >> bit) & 01) == 0)    /* free */
00535         return OK;
00536   else {
00537         printf("Bad number %lu. ", (unsigned long) num);
00538         printf("This zone (block) is marked in bitmap\n");
00539         return NOT_OK;
00540   }
00541 }
00542 
00543 void set_bit(num)               /* write in the bitmap */
00544 zone_t num;
00545 {
00546   int rwd;
00547   long blk_offset;
00548   int blk, offset, words, tst_word, bit;
00549   unsigned z_num;
00550 
00551   z_num = num - (sp->s_firstdatazone - 1);
00552 
00553   blk = z_num >> BIT_MAP_SHIFT; /* which block */
00554   offset = z_num - (blk << BIT_MAP_SHIFT);      /* offset in block */
00555   words = z_num / INT_BITS;     /* which word */
00556 
00557   blk_offset = (long) (2 + sp->s_imap_blocks);
00558   blk_offset *= (long) BLOCK_SIZE;
00559   blk_offset += (long) (words * SIZE_OF_INT);
00560 
00561 
00562   lseek(fd, (off_t) 0, SEEK_SET);       /* rewind */
00563   lseek(fd, (off_t) blk_offset, SEEK_SET);
00564 
00565   rwd = read(fd, (char *) &tst_word, SIZE_OF_INT);
00566   if (rwd != SIZE_OF_INT) {
00567         printf("Read error in bitmap\n");
00568         done(DEV_MOUNTED);
00569   }
00570   bit = offset % INT_BITS;
00571   if (((tst_word >> bit) & 01) == 0) {  /* free */
00572         lseek(fd, 0L, SEEK_SET);/* rewind */
00573         lseek(fd, (off_t) blk_offset, SEEK_SET);
00574         tst_word |= (1 << bit); /* not free anymore */
00575         rwd = write(fd, (char *) &tst_word, SIZE_OF_INT);
00576         if (rwd != SIZE_OF_INT) {
00577                 printf("Bad write in zone map\n");
00578                 printf("Check file system \n");
00579                 done(DIR_CREATED);
00580         }
00581         return;
00582   }
00583   printf("Bit map indicates that block %lu is in use. Not marked.\n",
00584          (unsigned long) num);
00585 /*  done(DIR_CREATED); */
00586   return;
00587 }
00588 
00589  /* ======= interactive interface ======= */
00590 
00591 long rd_num()
00592 {                               /* read a number from stdin */
00593   long num;
00594   int c;
00595 
00596   if (eofseen) return(-1);
00597   do {
00598         c = getchar();
00599         if (c == EOF || c == '\n') return(-1);
00600   } while (c != '-' && (c < '0' || c > '9'));
00601 
00602   if (c == '-') {
00603         printf("Block numbers must be positive\n");
00604         exit(1);
00605   }
00606   num = 0;
00607   while (c >= '0' && c <= '9') {
00608         num *= 10;
00609         num += c - '0';
00610         c = getchar();
00611         if (c == '\n') eofseen = 1;
00612   }
00613   return num;
00614 }
00615 
00616 
00617 
00618 int ok(str)
00619 char *str;
00620 {
00621   int c;
00622 
00623   for (;;) {
00624         printf("%s", str);
00625         while ((c = getchar()) != EOF &&
00626                c != 'y' && c != 'n' && c != 'q')
00627                 if (c != '\n') printf(" Bad character %c\n", (char) c);
00628         switch (c) {
00629             case EOF:
00630                 return QUIT;
00631             case 'y':
00632                 return OK;
00633             case 'n':
00634                 return NOT_OK;
00635             case 'q':   return QUIT;
00636         }
00637         printf("\n");
00638   }
00639 }
00640 
00641 
00642 void done(nr)
00643 int nr;
00644 {
00645   switch (nr) {
00646       case SUCCESS:
00647       case FILE_EXISTS:
00648         unlink(file_name);
00649       case DEV_MOUNTED:
00650         umount(dev_name);
00651       case DIR_CREATED:
00652         rmdir(dir_name);
00653       case HARMLESS:;
00654   }
00655   sync();
00656   exit(nr == SUCCESS ? 0 : 1);
00657 }

Generated on Fri Apr 14 22:57:04 2006 for minix by  doxygen 1.4.6