1 /* badblocks - collect bad blocks in a file Author: Jacob Bunschoten */
3 /* Usage "badblocks block_special [Up_to_7_blocks]" */
5 /* This program is written to handle BADBLOCKS on a hard or floppy disk.
6 * The program asks for block_numbers. These numbers can be obtained with
7 * the program readall, written by A. Tanenbaum. It then creates a
8 * file on the disk containing up to 7 bad blocks.
12 * When the zone_size > block_size it can happen that
13 * the zone is already allocated. This means some
14 * file is using this zone and may use all the blocks including
15 * the bad one. This can be cured by inspecting the zone_bitmap
16 * (is already done) and change the file if this zone is used.
17 * This means that another zone must be allocated and
18 * the inode wich claims this zone must be found and changed.
22 #include <sys/types.h>
24 #include <minix/config.h>
25 #include <minix/type.h>
28 #include <sys/mount.h>
32 #include "mfs/const.h" /* must be included before stdio.h */
33 #undef printf /* so its define of printf can be undone */
40 #include "mfs/super.h"
42 int main(int argc
, char **argv
);
44 void rw_inode(struct stat
* stat_ptr
, int rw_mode
);
45 void get_inode(struct stat
* stat_ptr
);
46 void put_inode(struct stat
* stat_ptr
);
47 long rd_cmdline(int argc
, char *argv
[]);
48 void modify(int nr_blocks
);
49 void save_blk(block_t blk_num
);
50 void reset_blks(void);
52 int blk_is_used(block_t blk_num
);
53 int blk_ok(block_t num
);
54 void set_bit(zone_t num
);
66 * inode map s_imap_blocks
67 * zone map s_zmap_blocks
68 * inodes (s_ninodes + 1 + inodes_per_block - 1)/inodes_per_block
70 * data zones (s_nzones - s_firstdatazone) << s_log_zone_size
88 #define BLOCK_SIZE 1024
89 #define SIZE_OF_INT (sizeof (int) )
91 /* Define V_NR_DZONES as the larger of V1_NR_DZONES and V2_NR_DZONES. */
92 #if (V1_NR_DZONES > V2_NR_DZONES)
93 #define V_NR_DZONES V1_NR_DZONES
94 #define V_SMALLER V2_NR_DZONES
96 #define V_NR_DZONES V2_NR_DZONES
97 #define V_SMALLER V1_NR_DZONES
101 /* ====== globals ======= */
104 char f_name
[] = ".Bad_XXXXXX";
106 char dir_name
[] = "/tmpXXXXXX";
108 block_t block
[V_NR_DZONES
+ 1]; /* last block contains zero */
109 int interactive
; /* 1 if interactive (argc == 2) */
110 int position
= 2; /* next block # is argv[position] */
114 int eofseen
; /* set if '\n' seen */
115 struct stat stat_buf
;
116 struct super_block
*sp
, sbs
;
117 int inodes_per_block
;
119 int v1fs
= 0, v2fs
= 0; /* TRUE for V1 file system, FALSE for V2 */
121 d1_inode d1inode
; /* declare a V1 disk inode */
123 d2_inode d2inode
; /* declare a V2 disk inode */
127 /* ====== super block routines ======= */
130 /* Get super_block. global pointer sp is used */
134 lseek(fd
, (long) BLOCK_SIZE
, SEEK_SET
); /* seek */
136 rd
= read(fd
, (char *) sp
, SUPER_SIZE
);
137 if (rd
!= SUPER_SIZE
) { /* ok ? */
138 printf("Bad read in get_super() (should be %u is %d)\n",
139 (unsigned) SUPER_SIZE
, rd
);
143 if (sp
->s_magic
== SUPER_MAGIC
) {
144 /* This is a V1 file system. */
145 v1fs
= 1; /* file system is not V2 */
146 } else if (sp
->s_magic
== SUPER_V2
) {
147 /* This is a V2 file system. */
148 v2fs
= 1; /* this is a V2 file system */
149 } else if (sp
->s_magic
== SUPER_V3
) {
150 v1fs
= v2fs
= 0; /* this is a V3 file system */
152 /* Neither V1 nor V2 nor V3. */
153 printf("Bad magic number in super_block (0x%x)\n",
154 (unsigned) sp
->s_magic
);
160 /* ========== inode routines =========== */
162 void rw_inode(stat_ptr
, rw_mode
)
163 struct stat
*stat_ptr
;
171 i_num
= stat_ptr
->st_ino
;
173 blk
= (block_t
) (START_BLOCK
+ sp
->s_imap_blocks
+ sp
->s_zmap_blocks
);
174 blk
+= (block_t
) ((i_num
- 1) / inodes_per_block
);
175 blk
*= (block_t
) (BLOCK_SIZE
);/* this block */
177 offset
= (block_t
) ((i_num
- 1) % inodes_per_block
);
178 offset
*= (block_t
) (inode_size
); /* and this offset */
180 lseek(fd
, (off_t
) (blk
+ offset
), SEEK_SET
); /* seek */
182 /* Pointer is at the inode */
184 /* This is a V1 file system. */
185 if (rw_mode
== READ
) { /* read it */
186 rwd
= read(fd
, (char *) ip1
, inode_size
);
187 } else { /* write it */
188 rwd
= write(fd
, (char *) ip1
, inode_size
);
191 /* This is a V2 file system. */
192 if (rw_mode
== READ
) { /* read it */
193 rwd
= read(fd
, (char *) ip2
, inode_size
);
194 } else { /* write it */
195 rwd
= write(fd
, (char *) ip2
, inode_size
);
199 if (rwd
!= inode_size
) { /* ok ? */
200 printf("Bad %s in get_inode()\n", (rw_mode
== READ
) ? "read" :
206 void get_inode(stat_ptr
)
207 struct stat
*stat_ptr
;
212 rw_inode(stat_ptr
, READ
);
215 for (cnt
= 0; cnt
< V1_NR_TZONES
; cnt
++)
216 ip1
->d1_zone
[cnt
] = 0; /* Just to be safe */
218 for (cnt
= 0; cnt
< V2_NR_TZONES
; cnt
++)
219 ip2
->d2_zone
[cnt
] = 0; /* Just to be safe */
223 void put_inode(stat_ptr
)
224 struct stat
*stat_ptr
;
226 rw_inode(stat_ptr
, WRITE
);
230 /* ============== main program ================= */
237 struct stat dev_stat
;
245 if (argc
< 2 || argc
> 9) {
246 fprintf(stderr
, "Usage: %s block_special [up_to_7_blocks]\n", argv
[0]);
249 interactive
= (argc
== 2 ? 1 : 0);
253 printf("Sorry, not in superuser mode \n");
254 printf("Set_uid bit must be on or you must become super_user\n");
259 if (mkdir(dir_name
, 0777) == -1) {
260 fprintf(stderr
, "%s is already used in system\n", dir_name
);
264 /* Mount device. This call may fail. */
265 mount(dev_name
, dir_name
, 0, NULL
, NULL
);
266 /* Succes. dev was mounted, try to umount */
268 /* Umount device. Playing with the file system while other processes
269 * have access to this device is asking for trouble */
270 if (umount(dev_name
) == -1) {
271 printf("Could not umount device %s.\n", dev_name
);
275 /* Create "/tmpXXXXpid/.BadXXpid" */
276 strcat(file_name
, dir_name
);
277 strcat(file_name
, "/");
278 strcat(file_name
, f_name
);
280 if (mount(dev_name
, dir_name
, 0, NULL
, NULL
) == -1) { /* this call should work */
281 fprintf(stderr
, "Could not mount device anymore\n");
284 if (stat(file_name
, &stat_buf
) != -1) {
285 printf("File %s already exists\n", file_name
);
288 if ((fp
= fopen(file_name
, "w")) == NULL
) {
289 printf("Cannot create file %s\n", file_name
);
292 chmod(file_name
, 0); /* "useless" file */
293 if (stat(file_name
, &stat_buf
) == -1) {
294 printf("What? Second call from stat failed\n");
298 /* Stat buf must be safed. We can now calculate the inode on disk */
301 /* ===== the badblock file is created ===== */
303 if (umount(dev_name
) == -1) {
304 printf("Can not umount device anymore??? \n");
307 if ((fd
= open(dev_name
, O_RDWR
)) == -1) {
308 printf("Can not open device %s\n", dev_name
);
311 if (fstat(fd
, &dev_stat
) == -1) {
312 printf("fstat on device %s failed\n", dev_name
);
315 if ((dev_stat
.st_mode
& S_IFMT
) != S_IFBLK
) {
316 printf("Device \"%s\" is not a block_special.\n", dev_name
);
320 if (sp
->s_log_zone_size
) {
321 printf("Block_size != zone_size.");
322 printf("This program can not handle it\n");
325 if(v1fs
|| v2fs
) block_size
= 1024;
326 else block_size
= sp
->s_block_size
;
328 /* The number of inodes in a block differs in V1 and V2. */
330 inodes_per_block
= V1_INODES_PER_BLOCK
;
331 inode_size
= V1_INODE_SIZE
;
333 inodes_per_block
= V2_INODES_PER_BLOCK(block_size
);
334 inode_size
= V2_INODE_SIZE
;
337 /* If the s_firstdatazone_old field is zero, we have to compute the value. */
338 if (sp
->s_firstdatazone_old
== 0)
339 sp
->s_firstdatazone
=
340 START_BLOCK
+ sp
->s_imap_blocks
+ sp
->s_zmap_blocks
+
341 (sp
->s_ninodes
+ inodes_per_block
- 1) / inodes_per_block
;
343 sp
->s_firstdatazone
= sp
->s_firstdatazone_old
;
345 get_inode(&stat_buf
);
347 for (finished
= 0; !finished
;) {
349 printf("Give up to %d bad block numbers separated by spaces\n",
352 cnt
= 0; /* cnt keep track of the zone's */
353 while (cnt
< V_SMALLER
) {
359 blk_nr
= rd_cmdline(argc
, argv
);
360 if (blk_nr
== -1) break;
361 tst
= blk_ok(blk_nr
);
363 /* Test if this block is free */
367 } else if (tst
== QUIT
)
370 if (interactive
) show_blks();
371 if (!cnt
) done(FILE_EXISTS
);
373 switch (ok("All these blocks ok <y/n/q> (y:Device will change) ")) {
374 case OK
: finished
= 1; break;
377 case QUIT
: done(FILE_EXISTS
);
385 close(fd
); /* free device */
390 long rd_cmdline(argc
, argv
)
394 if (position
== argc
) return(-1);
395 return(atol(argv
[position
++]));
399 void modify(nr_blocks
)
404 if (nr_blocks
== 0) return;
406 /* This is a V1 file system. */
407 for (i
= 0; i
< nr_blocks
; i
++) {
409 ip1
->d1_zone
[i
] = block
[i
];
412 /* This is a V2 file system. */
413 for (i
= 0; i
< nr_blocks
; i
++) {
415 ip2
->d2_zone
[i
] = block
[i
];
419 ip1
->d1_size
= (long) (BLOCK_SIZE
* nr_blocks
); /* give file size */
420 ip1
->d1_mtime
= 0; /* Who wants a file from 1970? */
422 ip2
->d2_size
= (long) (BLOCK_SIZE
* nr_blocks
); /* give file size */
423 ip2
->d2_atime
= ip2
->d2_mtime
= ip2
->d2_ctime
= 0;
426 put_inode(&stat_buf
); /* save the inode on disk */
432 void save_blk(blk_num
)
435 block
[blk_cnt
++] = blk_num
;
442 for (i
= 0; i
<= V_NR_DZONES
; i
++)
443 block
[i
] = 0; /* Note: Last block_number is set to zero */
451 for (i
= 0; i
< blk_cnt
; i
++)
452 printf("Block[%d] = %lu\n", i
, (unsigned long) block
[i
]);
455 int blk_is_used(blk_num
)
457 { /* return TRUE(1) if used */
460 for (i
= 0; block
[i
] && block
[i
] != blk_num
; i
++);
461 return(block
[i
] != 0) ? 1 : 0;
465 /* ===== bitmap handling ====== */
467 #define BIT_MAP_SHIFT 13
468 #define INT_BITS (SIZE_OF_INT << 3)
470 int blk_ok(num
) /* is this zone free (y/n) */
475 int blk
, offset
, words
, bit
, tst_word
;
478 if (blk_is_used(num
)) {
479 printf("Duplicate block (%lu) given\n", (unsigned long) num
);
483 /* Assumption zone_size == block_size */
485 z_num
= num
- (sp
->s_firstdatazone
- 1); /* account offset */
487 /* Calculate the word in the bitmap. */
488 blk
= z_num
>> BIT_MAP_SHIFT
; /* which block */
489 offset
= z_num
- (blk
<< BIT_MAP_SHIFT
); /* offset */
490 words
= z_num
/ INT_BITS
; /* which word */
492 blk_offset
= (block_t
) (START_BLOCK
+ sp
->s_imap_blocks
); /* zone map */
493 blk_offset
*= (block_t
) BLOCK_SIZE
; /* of course in block */
494 blk_offset
+= (block_t
) (words
* SIZE_OF_INT
); /* offset */
497 lseek(fd
, (off_t
) blk_offset
, SEEK_SET
); /* set pointer at word */
499 rd
= read(fd
, (char *) &tst_word
, SIZE_OF_INT
);
500 if (rd
!= SIZE_OF_INT
) {
501 printf("Read error in bitmap\n");
505 /* We have the tst_word, check if bit was off */
506 bit
= offset
% INT_BITS
;
508 if (((tst_word
>> bit
) & 01) == 0) /* free */
511 printf("Bad number %lu. ", (unsigned long) num
);
512 printf("This zone (block) is marked in bitmap\n");
517 void set_bit(num
) /* write in the bitmap */
522 int blk
, offset
, words
, tst_word
, bit
;
525 z_num
= num
- (sp
->s_firstdatazone
- 1);
527 blk
= z_num
>> BIT_MAP_SHIFT
; /* which block */
528 offset
= z_num
- (blk
<< BIT_MAP_SHIFT
); /* offset in block */
529 words
= z_num
/ INT_BITS
; /* which word */
531 blk_offset
= (long) (START_BLOCK
+ sp
->s_imap_blocks
);
532 blk_offset
*= (long) BLOCK_SIZE
;
533 blk_offset
+= (long) (words
* SIZE_OF_INT
);
536 lseek(fd
, (off_t
) blk_offset
, SEEK_SET
);
538 rwd
= read(fd
, (char *) &tst_word
, SIZE_OF_INT
);
539 if (rwd
!= SIZE_OF_INT
) {
540 printf("Read error in bitmap\n");
543 bit
= offset
% INT_BITS
;
544 if (((tst_word
>> bit
) & 01) == 0) { /* free */
545 lseek(fd
, (off_t
) blk_offset
, SEEK_SET
);
546 tst_word
|= (1 << bit
); /* not free anymore */
547 rwd
= write(fd
, (char *) &tst_word
, SIZE_OF_INT
);
548 if (rwd
!= SIZE_OF_INT
) {
549 printf("Bad write in zone map\n");
550 printf("Check file system \n");
555 printf("Bit map indicates that block %lu is in use. Not marked.\n",
556 (unsigned long) num
);
557 /* done(DIR_CREATED); */
561 /* ======= interactive interface ======= */
564 { /* read a number from stdin */
568 if (eofseen
) return(-1);
571 if (c
== EOF
|| c
== '\n') return(-1);
572 } while (c
!= '-' && (c
< '0' || c
> '9'));
575 printf("Block numbers must be positive\n");
579 while (c
>= '0' && c
<= '9') {
583 if (c
== '\n') eofseen
= 1;
597 while ((c
= getchar()) != EOF
&&
598 c
!= 'y' && c
!= 'n' && c
!= 'q')
599 if (c
!= '\n') printf(" Bad character %c\n", (char) c
);
607 case 'q': return QUIT
;
628 exit(nr
== SUCCESS
? 0 : 1);