coverity appeasement - redundant check
[minix.git] / commands / badblocks / badblocks.c
blob23d745418159b77f7d546d16111085cc7b1bc9ed
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.
10 * BUG:
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>
23 #include <sys/stat.h>
24 #include <minix/config.h>
25 #include <minix/type.h>
26 #include <fcntl.h>
27 #include <unistd.h>
28 #include <sys/mount.h>
29 #include <dirent.h>
30 #include <stdlib.h>
32 #include "mfs/const.h" /* must be included before stdio.h */
33 #undef printf /* so its define of printf can be undone */
34 #include "mfs/type.h"
36 #include <string.h>
37 #include <stdio.h>
39 #define EXTERN extern
40 #include "mfs/super.h"
42 int main(int argc, char **argv);
43 void get_super(void);
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);
51 void show_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);
55 long rd_num(void);
56 int ok(char *str);
57 void done(int nr);
59 /* Super block table.
61 * The disk layout is:
63 * Item # block
64 * boot block 1
65 * super block 1
66 * inode map s_imap_blocks
67 * zone map s_zmap_blocks
68 * inodes (s_ninodes + 1 + inodes_per_block - 1)/inodes_per_block
69 * unused
70 * data zones (s_nzones - s_firstdatazone) << s_log_zone_size
74 #define OK 0
75 #define NOT_OK 1
76 #define QUIT 2
78 #define READ 0
79 #define WRITE 1
81 #define HARMLESS 0
82 #define DIR_CREATED 1
83 #define DEV_MOUNTED 2
84 #define FILE_EXISTS 3
85 #define SUCCESS 4
87 #define BYTE 0377
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
95 #else
96 #define V_NR_DZONES V2_NR_DZONES
97 #define V_SMALLER V1_NR_DZONES
98 #endif
101 /* ====== globals ======= */
103 char *dev_name;
104 char f_name[] = ".Bad_XXXXXX";
105 char file_name[50];
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] */
112 FILE *f;
113 int fd;
114 int eofseen; /* set if '\n' seen */
115 struct stat stat_buf;
116 struct super_block *sp, sbs;
117 int inodes_per_block;
118 size_t inode_size;
119 int v1fs = 0, v2fs = 0; /* TRUE for V1 file system, FALSE for V2 */
121 d1_inode d1inode; /* declare a V1 disk inode */
122 d1_inode *ip1;
123 d2_inode d2inode; /* declare a V2 disk inode */
124 d2_inode *ip2;
127 /* ====== super block routines ======= */
129 void get_super()
130 /* Get super_block. global pointer sp is used */
132 int rd;
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);
140 done(DIR_CREATED);
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 */
151 } else {
152 /* Neither V1 nor V2 nor V3. */
153 printf("Bad magic number in super_block (0x%x)\n",
154 (unsigned) sp->s_magic);
155 done(DIR_CREATED);
160 /* ========== inode routines =========== */
162 void rw_inode(stat_ptr, rw_mode)
163 struct stat *stat_ptr;
164 int rw_mode;
166 int rwd;
167 ino_t i_num;
168 block_t blk, offset;
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 */
183 if (v1fs) {
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);
190 } else {
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" :
201 "write");
202 done(DIR_CREATED);
206 void get_inode(stat_ptr)
207 struct stat *stat_ptr;
210 int cnt;
212 rw_inode(stat_ptr, READ);
214 if (v1fs) {
215 for (cnt = 0; cnt < V1_NR_TZONES; cnt++)
216 ip1->d1_zone[cnt] = 0; /* Just to be safe */
217 } else {
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 ================= */
231 int main(argc, argv)
232 int argc;
233 char *argv[];
235 int cnt, finished;
236 block_t blk_nr;
237 struct stat dev_stat;
238 FILE *fp;
239 int block_size;
241 sp = &sbs;
242 ip1 = &d1inode;
243 ip2 = &d2inode;
245 if (argc < 2 || argc > 9) {
246 fprintf(stderr, "Usage: %s block_special [up_to_7_blocks]\n", argv[0]);
247 done(HARMLESS);
249 interactive = (argc == 2 ? 1 : 0);
251 /* Do some test. */
252 if (geteuid()) {
253 printf("Sorry, not in superuser mode \n");
254 printf("Set_uid bit must be on or you must become super_user\n");
255 done(HARMLESS);
257 dev_name = argv[1];
258 mktemp(dir_name);
259 if (mkdir(dir_name, 0777) == -1) {
260 fprintf(stderr, "%s is already used in system\n", dir_name);
261 done(HARMLESS);
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);
272 done(HARMLESS);
274 mktemp(f_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");
282 done(HARMLESS);
284 if (stat(file_name, &stat_buf) != -1) {
285 printf("File %s already exists\n", file_name);
286 done(DEV_MOUNTED);
288 if ((fp = fopen(file_name, "w")) == NULL) {
289 printf("Cannot create file %s\n", file_name);
290 done(DEV_MOUNTED);
292 chmod(file_name, 0); /* "useless" file */
293 if (stat(file_name, &stat_buf) == -1) {
294 printf("What? Second call from stat failed\n");
295 done(FILE_EXISTS);
298 /* Stat buf must be safed. We can now calculate the inode on disk */
299 fclose(fp);
301 /* ===== the badblock file is created ===== */
303 if (umount(dev_name) == -1) {
304 printf("Can not umount device anymore??? \n");
305 done(DIR_CREATED);
307 if ((fd = open(dev_name, O_RDWR)) == -1) {
308 printf("Can not open device %s\n", dev_name);
309 done(DEV_MOUNTED);
311 if (fstat(fd, &dev_stat) == -1) {
312 printf("fstat on device %s failed\n", dev_name);
313 done(DEV_MOUNTED);
315 if ((dev_stat.st_mode & S_IFMT) != S_IFBLK) {
316 printf("Device \"%s\" is not a block_special.\n", dev_name);
317 done(DEV_MOUNTED);
319 get_super();
320 if (sp->s_log_zone_size) {
321 printf("Block_size != zone_size.");
322 printf("This program can not handle it\n");
323 done(DIR_CREATED);
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. */
329 if (v1fs) {
330 inodes_per_block = V1_INODES_PER_BLOCK;
331 inode_size = V1_INODE_SIZE;
332 } else {
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;
342 else
343 sp->s_firstdatazone = sp->s_firstdatazone_old;
345 get_inode(&stat_buf);
347 for (finished = 0; !finished;) {
348 if (interactive)
349 printf("Give up to %d bad block numbers separated by spaces\n",
350 V_SMALLER);
351 reset_blks();
352 cnt = 0; /* cnt keep track of the zone's */
353 while (cnt < V_SMALLER) {
354 int tst;
356 if (interactive)
357 blk_nr = rd_num();
358 else
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 */
364 if (tst == OK) {
365 cnt++;
366 save_blk(blk_nr);
367 } else if (tst == QUIT)
368 break;
370 if (interactive) show_blks();
371 if (!cnt) done(FILE_EXISTS);
372 if (interactive) {
373 switch (ok("All these blocks ok <y/n/q> (y:Device will change) ")) {
374 case OK: finished = 1; break;
375 case NOT_OK:
376 break;
377 case QUIT: done(FILE_EXISTS);
379 } else {
380 finished = 1;
384 modify(cnt);
385 close(fd); /* free device */
386 done(SUCCESS);
387 return(0);
390 long rd_cmdline(argc, argv)
391 int argc;
392 char *argv[];
394 if (position == argc) return(-1);
395 return(atol(argv[position++]));
399 void modify(nr_blocks)
400 int nr_blocks;
402 int i;
404 if (nr_blocks == 0) return;
405 if (v1fs) {
406 /* This is a V1 file system. */
407 for (i = 0; i < nr_blocks; i++) {
408 set_bit(block[i]);
409 ip1->d1_zone[i] = block[i];
411 } else {
412 /* This is a V2 file system. */
413 for (i = 0; i < nr_blocks; i++) {
414 set_bit(block[i]);
415 ip2->d2_zone[i] = block[i];
418 if (v1fs) {
419 ip1->d1_size = (long) (BLOCK_SIZE * nr_blocks); /* give file size */
420 ip1->d1_mtime = 0; /* Who wants a file from 1970? */
421 } else {
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 */
430 static blk_cnt = 0;
432 void save_blk(blk_num)
433 block_t blk_num;
435 block[blk_cnt++] = blk_num;
438 void reset_blks()
440 int i;
442 for (i = 0; i <= V_NR_DZONES; i++)
443 block[i] = 0; /* Note: Last block_number is set to zero */
444 blk_cnt = 0;
447 void show_blks()
449 int i;
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)
456 block_t blk_num;
457 { /* return TRUE(1) if used */
458 int i;
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) */
471 block_t num;
473 block_t blk_offset;
474 int rd;
475 int blk, offset, words, bit, tst_word;
476 zone_t z_num;
478 if (blk_is_used(num)) {
479 printf("Duplicate block (%lu) given\n", (unsigned long) num);
480 return NOT_OK;
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");
502 done(DIR_CREATED);
505 /* We have the tst_word, check if bit was off */
506 bit = offset % INT_BITS;
508 if (((tst_word >> bit) & 01) == 0) /* free */
509 return OK;
510 else {
511 printf("Bad number %lu. ", (unsigned long) num);
512 printf("This zone (block) is marked in bitmap\n");
513 return NOT_OK;
517 void set_bit(num) /* write in the bitmap */
518 zone_t num;
520 int rwd;
521 long blk_offset;
522 int blk, offset, words, tst_word, bit;
523 unsigned z_num;
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");
541 done(DEV_MOUNTED);
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");
551 done(DIR_CREATED);
553 return;
555 printf("Bit map indicates that block %lu is in use. Not marked.\n",
556 (unsigned long) num);
557 /* done(DIR_CREATED); */
558 return;
561 /* ======= interactive interface ======= */
563 long rd_num()
564 { /* read a number from stdin */
565 long num;
566 int c;
568 if (eofseen) return(-1);
569 do {
570 c = getchar();
571 if (c == EOF || c == '\n') return(-1);
572 } while (c != '-' && (c < '0' || c > '9'));
574 if (c == '-') {
575 printf("Block numbers must be positive\n");
576 exit(1);
578 num = 0;
579 while (c >= '0' && c <= '9') {
580 num *= 10;
581 num += c - '0';
582 c = getchar();
583 if (c == '\n') eofseen = 1;
585 return num;
590 int ok(str)
591 char *str;
593 int c;
595 for (;;) {
596 printf("%s", str);
597 while ((c = getchar()) != EOF &&
598 c != 'y' && c != 'n' && c != 'q')
599 if (c != '\n') printf(" Bad character %c\n", (char) c);
600 switch (c) {
601 case EOF:
602 return QUIT;
603 case 'y':
604 return OK;
605 case 'n':
606 return NOT_OK;
607 case 'q': return QUIT;
609 printf("\n");
614 void done(nr)
615 int nr;
617 switch (nr) {
618 case SUCCESS:
619 case FILE_EXISTS:
620 unlink(file_name);
621 case DEV_MOUNTED:
622 umount(dev_name);
623 case DIR_CREATED:
624 rmdir(dir_name);
625 case HARMLESS:;
627 sync();
628 exit(nr == SUCCESS ? 0 : 1);