source: trunk/minix/commands/simple/badblocks.c@ 9

Last change on this file since 9 was 9, checked in by Mattia Monga, 13 years ago

Minix 3.1.2a

File size: 15.4 KB
Line 
1/* badblocks - collect bad blocks in a file Author: Jacob Bunschoten */
2
3/* Usage "badblocks block_special [Up_to_7_blocks]" */
4
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.
9 *
10 * BUG:
11 *
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.
19 *
20 */
21
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 <dirent.h>
29#include <stdlib.h>
30
31#include "../../servers/fs/const.h" /* must be included before stdio.h */
32#undef printf /* so its define of printf can be undone */
33#include "../../servers/fs/type.h"
34
35#include <string.h>
36#include <stdio.h>
37
38#define EXTERN extern
39#include "../../servers/fs/super.h"
40
41_PROTOTYPE(int main, (int argc, char **argv));
42_PROTOTYPE(void rw_super, (int flag));
43_PROTOTYPE(void get_super, (void));
44_PROTOTYPE(void put_super, (void));
45_PROTOTYPE(void rw_inode, (struct stat * stat_ptr, int rw_mode));
46_PROTOTYPE(void get_inode, (struct stat * stat_ptr));
47_PROTOTYPE(void put_inode, (struct stat * stat_ptr));
48_PROTOTYPE(long rd_cmdline, (int argc, char *argv[]));
49_PROTOTYPE(void modify, (int nr_blocks));
50_PROTOTYPE(void save_blk, (block_t blk_num));
51_PROTOTYPE(void reset_blks, (void));
52_PROTOTYPE(void show_blks, (void));
53_PROTOTYPE(int blk_is_used, (block_t blk_num));
54_PROTOTYPE(int blk_ok, (block_t num));
55_PROTOTYPE(void set_bit, (zone_t num));
56_PROTOTYPE(long rd_num, (void));
57_PROTOTYPE(int ok, (char *str));
58_PROTOTYPE(void done, (int nr));
59
60/* Super block table.
61 *
62 * The disk layout is:
63 *
64 * Item # block
65 * boot block 1
66 * super block 1
67 * inode map s_imap_blocks
68 * zone map s_zmap_blocks
69 * inodes (s_ninodes + 1 + inodes_per_block - 1)/inodes_per_block
70 * unused
71 * data zones (s_nzones - s_firstdatazone) << s_log_zone_size
72 *
73 */
74
75#define OK 0
76#define NOT_OK 1
77#define QUIT 2
78
79#define READ 0
80#define WRITE 1
81
82#define HARMLESS 0
83#define DIR_CREATED 1
84#define DEV_MOUNTED 2
85#define FILE_EXISTS 3
86#define SUCCESS 4
87
88#define BYTE 0377
89#define BLOCK_SIZE 1024
90#define SIZE_OF_INT (sizeof (int) )
91
92/* Define V_NR_DZONES as the larger of V1_NR_DZONES and V2_NR_DZONES. */
93#if (V1_NR_DZONES > V2_NR_DZONES)
94#define V_NR_DZONES V1_NR_DZONES
95#define V_SMALLER V2_NR_DZONES
96#else
97#define V_NR_DZONES V2_NR_DZONES
98#define V_SMALLER V1_NR_DZONES
99#endif
100
101#if 0
102struct super_block {
103 ino_t s_ninodes; /* # usable inodes on the minor device */
104 zone1_t s_nzones; /* total device size, including bit maps etc */
105 short s_imap_blocks; /* # of blocks used by inode bit map */
106 short s_zmap_blocks; /* # of blocks used by zone bit map */
107 zone1_t s_firstdatazone; /* number of first data zone */
108 short s_log_zone_size; /* log2 of blocks/zone */
109 off_t s_max_size; /* maximum file size on this device */
110 short s_magic; /* magic number to recognize super-blocks */
111 short s_pad; /* try to avoid compiler-dependent padding */
112 zone_t s_zones; /* number of zones (replaces s_nzones in V2) */
113} super_block;
114#endif
115
116 /* ====== globals ======= */
117
118char *dev_name;
119char f_name[] = ".Bad_XXXXXX";
120char file_name[50];
121char dir_name[] = "/tmpXXXXXX";
122
123block_t block[V_NR_DZONES + 1]; /* last block contains zero */
124int interactive; /* 1 if interactive (argc == 2) */
125int position = 2; /* next block # is argv[position] */
126
127FILE *f;
128int fd;
129int eofseen; /* set if '\n' seen */
130struct stat stat_buf;
131struct super_block *sp, sbs;
132int inodes_per_block;
133size_t inode_size;
134int v1fs = 0, v2fs = 0; /* TRUE for V1 file system, FALSE for V2 */
135
136d1_inode d1inode; /* declare a V1 disk inode */
137d1_inode *ip1;
138d2_inode d2inode; /* declare a V2 disk inode */
139d2_inode *ip2;
140
141
142 /* ====== super block routines ======= */
143
144void rw_super(flag)
145int flag;
146{ /* read or write a superblock */
147 int rwd;
148
149 lseek(fd, 0L, SEEK_SET); /* rewind */
150 lseek(fd, (long) BLOCK_SIZE, SEEK_SET); /* seek */
151
152 if (flag == READ)
153 rwd = read(fd, (char *) sp, SUPER_SIZE);
154 else
155 rwd = write(fd, (char *) sp, SUPER_SIZE);
156 if (rwd != SUPER_SIZE) { /* ok ? */
157 printf("Bad %s in get_super() (should be %u is %d)\n",
158 flag == READ ? "read" : "write",
159 (unsigned) SUPER_SIZE, rwd);
160 done(DIR_CREATED);
161 }
162}
163
164void get_super()
165 /* Get super_block. global pointer sp is used */
166{
167 rw_super(READ);
168
169 if (sp->s_magic == SUPER_MAGIC) {
170 /* This is a V1 file system. */
171 v1fs = 1; /* file system is not V2 */
172 } else if (sp->s_magic == SUPER_V2) {
173 /* This is a V2 file system. */
174 v2fs = 1; /* this is a V2 file system */
175 } else if (sp->s_magic == SUPER_V3) {
176 v1fs = v2fs = 0; /* this is a V3 file system */
177 } else {
178 /* Neither V1 nor V2 nor V3. */
179 printf("Bad magic number in super_block (0x%x)\n",
180 (unsigned) sp->s_magic);
181 done(DIR_CREATED);
182 }
183}
184
185
186void put_super()
187{
188 rw_super(WRITE);
189}
190
191 /* ========== inode routines =========== */
192
193void rw_inode(stat_ptr, rw_mode)
194struct stat *stat_ptr;
195int rw_mode;
196{
197 int rwd;
198 ino_t i_num;
199 block_t blk, offset;
200
201
202 i_num = stat_ptr->st_ino;
203
204 blk = (block_t) (2 + sp->s_imap_blocks + sp->s_zmap_blocks);
205 blk += (block_t) ((i_num - 1) / inodes_per_block);
206 blk *= (block_t) (BLOCK_SIZE);/* this block */
207
208 offset = (block_t) ((i_num - 1) % inodes_per_block);
209 offset *= (block_t) (inode_size); /* and this offset */
210
211 lseek(fd, (off_t) 0, SEEK_SET); /* rewind */
212 lseek(fd, (off_t) (blk + offset), SEEK_SET); /* seek */
213
214 /* Pointer is at the inode */
215 if (v1fs) {
216 /* This is a V1 file system. */
217 if (rw_mode == READ) { /* read it */
218 rwd = read(fd, (char *) ip1, inode_size);
219 } else { /* write it */
220 rwd = write(fd, (char *) ip1, inode_size);
221 }
222 } else {
223 /* This is a V2 file system. */
224 if (rw_mode == READ) { /* read it */
225 rwd = read(fd, (char *) ip2, inode_size);
226 } else { /* write it */
227 rwd = write(fd, (char *) ip2, inode_size);
228 }
229 }
230
231 if (rwd != inode_size) { /* ok ? */
232 printf("Bad %s in get_inode()\n", (rw_mode == READ) ? "read" :
233 "write");
234 done(DIR_CREATED);
235 }
236}
237
238void get_inode(stat_ptr)
239struct stat *stat_ptr;
240{
241
242 int cnt;
243
244 rw_inode(stat_ptr, READ);
245
246 if (v1fs) {
247 for (cnt = 0; cnt < V1_NR_TZONES; cnt++)
248 ip1->d1_zone[cnt] = 0; /* Just to be safe */
249 } else {
250 for (cnt = 0; cnt < V2_NR_TZONES; cnt++)
251 ip2->d2_zone[cnt] = 0; /* Just to be safe */
252 }
253}
254
255void put_inode(stat_ptr)
256struct stat *stat_ptr;
257{
258 rw_inode(stat_ptr, WRITE);
259}
260
261
262 /* ============== main program ================= */
263int main(argc, argv)
264int argc;
265char *argv[];
266{
267 int cnt, finished;
268 block_t blk_nr;
269 struct stat dev_stat;
270 FILE *fp;
271 int block_size;
272
273 sp = &sbs;
274 ip1 = &d1inode;
275 ip2 = &d2inode;
276
277 if (argc < 2 || argc > 9) {
278 fprintf(stderr, "Usage: %s block_special [up_to_7_blocks]\n", argv[0]);
279 done(HARMLESS);
280 }
281 interactive = (argc == 2 ? 1 : 0);
282
283 /* Do some test. */
284 if (geteuid()) {
285 printf("Sorry, not in superuser mode \n");
286 printf("Set_uid bit must be on or you must become super_user\n");
287 done(HARMLESS);
288 }
289 dev_name = argv[1];
290 mktemp(dir_name);
291 if (mkdir(dir_name, 0777) == -1) {
292 fprintf(stderr, "%s is already used in system\n", dir_name);
293 done(HARMLESS);
294 }
295
296 /* Mount device. This call may fail. */
297 mount(dev_name, dir_name, 0);
298 /* Succes. dev was mounted, try to umount */
299
300 /* Umount device. Playing with the file system while other processes
301 * have access to this device is asking for trouble */
302 if (umount(dev_name) == -1) {
303 printf("Could not umount device %s.\n", dev_name);
304 done(HARMLESS);
305 }
306 mktemp(f_name);
307 /* Create "/tmpXXXXpid/.BadXXpid" */
308 strcat(file_name, dir_name);
309 strcat(file_name, "/");
310 strcat(file_name, f_name);
311
312 if (mount(dev_name, dir_name, 0) == -1) { /* this call should work */
313 fprintf(stderr, "Could not mount device anymore\n");
314 done(HARMLESS);
315 }
316 if (stat(file_name, &stat_buf) != -1) {
317 printf("File %s already exists\n", file_name);
318 done(DEV_MOUNTED);
319 }
320 if ((fp = fopen(file_name, "w")) == NULL) {
321 printf("Cannot create file %s\n", file_name);
322 done(DEV_MOUNTED);
323 }
324 chmod(file_name, 0); /* "useless" file */
325 if (stat(file_name, &stat_buf) == -1) {
326 printf("What? Second call from stat failed\n");
327 done(FILE_EXISTS);
328 }
329
330 /* Stat buf must be safed. We can now calculate the inode on disk */
331 fclose(fp);
332
333 /* ===== the badblock file is created ===== */
334
335 if (umount(dev_name) == -1) {
336 printf("Can not umount device anymore??? \n");
337 done(DIR_CREATED);
338 }
339 if ((fd = open(dev_name, O_RDWR)) == -1) {
340 printf("Can not open device %s\n", dev_name);
341 done(DEV_MOUNTED);
342 }
343 if (fstat(fd, &dev_stat) == -1) {
344 printf("fstat on device %s failed\n", dev_name);
345 done(DEV_MOUNTED);
346 }
347 if ((dev_stat.st_mode & S_IFMT) != S_IFBLK) {
348 printf("Device \"%s\" is not a block_special.\n", dev_name);
349 done(DEV_MOUNTED);
350 }
351 get_super();
352 if (sp->s_log_zone_size) {
353 printf("Block_size != zone_size.");
354 printf("This program can not handle it\n");
355 done(DIR_CREATED);
356 }
357 if(v1fs || v2fs) block_size = 1024;
358 else block_size = sp->s_block_size;
359
360 /* The number of inodes in a block differs in V1 and V2. */
361 if (v1fs) {
362 inodes_per_block = V1_INODES_PER_BLOCK;
363 inode_size = V1_INODE_SIZE;
364 } else {
365 inodes_per_block = V2_INODES_PER_BLOCK(block_size);
366 inode_size = V2_INODE_SIZE;
367 }
368
369 get_inode(&stat_buf);
370
371 for (finished = 0; !finished;) {
372 if (interactive)
373 printf("Give up to %d bad block numbers separated by spaces\n",
374 V_SMALLER);
375 reset_blks();
376 cnt = 0; /* cnt keep track of the zone's */
377 while (cnt < V_SMALLER) {
378 int tst;
379
380 if (interactive)
381 blk_nr = rd_num();
382 else
383 blk_nr = rd_cmdline(argc, argv);
384 if (blk_nr == -1) break;
385 tst = blk_ok(blk_nr);
386
387 /* Test if this block is free */
388 if (tst == OK) {
389 cnt++;
390 save_blk(blk_nr);
391 } else if (tst == QUIT)
392 break;
393 }
394 if (interactive) show_blks();
395 if (!cnt) done(FILE_EXISTS);
396 if (interactive) {
397 switch (ok("All these blocks ok <y/n/q> (y:Device will change) ")) {
398 case OK: finished = 1; break;
399 case NOT_OK:
400 break;
401 case QUIT: done(FILE_EXISTS);
402 }
403 } else {
404 finished = 1;
405 }
406 }
407
408 modify(cnt);
409 close(fd); /* free device */
410 done(SUCCESS);
411 return(0);
412}
413
414long rd_cmdline(argc, argv)
415int argc;
416char *argv[];
417{
418 if (position == argc) return(-1);
419 return(atol(argv[position++]));
420}
421
422
423void modify(nr_blocks)
424int nr_blocks;
425{
426 int i;
427
428 if (nr_blocks == 0) return;
429 if (v1fs) {
430 /* This is a V1 file system. */
431 for (i = 0; i < nr_blocks; i++) {
432 set_bit(block[i]);
433 ip1->d1_zone[i] = block[i];
434 }
435 } else {
436 /* This is a V2 file system. */
437 for (i = 0; i < nr_blocks; i++) {
438 set_bit(block[i]);
439 ip2->d2_zone[i] = block[i];
440 }
441 }
442 if (v1fs) {
443 ip1->d1_size = (long) (BLOCK_SIZE * nr_blocks); /* give file size */
444 ip1->d1_mtime = 0; /* Who wants a file from 1970? */
445 } else {
446 ip2->d2_size = (long) (BLOCK_SIZE * nr_blocks); /* give file size */
447 ip2->d2_atime = ip2->d2_mtime = ip2->d2_ctime = 0;
448 }
449
450 put_inode(&stat_buf); /* save the inode on disk */
451 put_super(); /* bit_maps too */
452}
453
454
455static blk_cnt = 0;
456
457void save_blk(blk_num)
458block_t blk_num;
459{
460 block[blk_cnt++] = blk_num;
461}
462
463void reset_blks()
464{
465 int i;
466
467 for (i = 0; i <= V_NR_DZONES; i++)
468 block[i] = 0; /* Note: Last block_number is set to zero */
469 blk_cnt = 0;
470}
471
472void show_blks()
473{
474 int i;
475
476 for (i = 0; i < blk_cnt; i++)
477 printf("Block[%d] = %lu\n", i, (unsigned long) block[i]);
478}
479
480int blk_is_used(blk_num)
481block_t blk_num;
482{ /* return TRUE(1) if used */
483 int i;
484
485 for (i = 0; block[i] && block[i] != blk_num; i++);
486 return(block[i] != 0) ? 1 : 0;
487}
488
489
490 /* ===== bitmap handling ====== */
491
492#define BIT_MAP_SHIFT 13
493#define INT_BITS (SIZE_OF_INT << 3)
494
495int blk_ok(num) /* is this zone free (y/n) */
496block_t num;
497{
498 block_t blk_offset;
499 int rd;
500 int blk, offset, words, bit, tst_word;
501 zone_t z_num;
502
503 if (blk_is_used(num)) {
504 printf("Duplicate block (%lu) given\n", (unsigned long) num);
505 return NOT_OK;
506 }
507
508 /* Assumption zone_size == block_size */
509
510 z_num = num - (sp->s_firstdatazone - 1); /* account offset */
511
512 /* Calculate the word in the bitmap. */
513 blk = z_num >> BIT_MAP_SHIFT; /* which block */
514 offset = z_num - (blk << BIT_MAP_SHIFT); /* offset */
515 words = z_num / INT_BITS; /* which word */
516
517 blk_offset = (block_t) (2 + sp->s_imap_blocks); /* zone map */
518 blk_offset *= (block_t) BLOCK_SIZE; /* of course in block */
519 blk_offset += (block_t) (words * SIZE_OF_INT); /* offset */
520
521
522 lseek(fd, (off_t) 0, SEEK_SET); /* rewind */
523 lseek(fd, (off_t) blk_offset, SEEK_SET); /* set pointer at word */
524
525 rd = read(fd, (char *) &tst_word, SIZE_OF_INT);
526 if (rd != SIZE_OF_INT) {
527 printf("Read error in bitmap\n");
528 done(DIR_CREATED);
529 }
530
531 /* We have the tst_word, check if bit was off */
532 bit = offset % INT_BITS;
533
534 if (((tst_word >> bit) & 01) == 0) /* free */
535 return OK;
536 else {
537 printf("Bad number %lu. ", (unsigned long) num);
538 printf("This zone (block) is marked in bitmap\n");
539 return NOT_OK;
540 }
541}
542
543void set_bit(num) /* write in the bitmap */
544zone_t num;
545{
546 int rwd;
547 long blk_offset;
548 int blk, offset, words, tst_word, bit;
549 unsigned z_num;
550
551 z_num = num - (sp->s_firstdatazone - 1);
552
553 blk = z_num >> BIT_MAP_SHIFT; /* which block */
554 offset = z_num - (blk << BIT_MAP_SHIFT); /* offset in block */
555 words = z_num / INT_BITS; /* which word */
556
557 blk_offset = (long) (2 + sp->s_imap_blocks);
558 blk_offset *= (long) BLOCK_SIZE;
559 blk_offset += (long) (words * SIZE_OF_INT);
560
561
562 lseek(fd, (off_t) 0, SEEK_SET); /* rewind */
563 lseek(fd, (off_t) blk_offset, SEEK_SET);
564
565 rwd = read(fd, (char *) &tst_word, SIZE_OF_INT);
566 if (rwd != SIZE_OF_INT) {
567 printf("Read error in bitmap\n");
568 done(DEV_MOUNTED);
569 }
570 bit = offset % INT_BITS;
571 if (((tst_word >> bit) & 01) == 0) { /* free */
572 lseek(fd, 0L, SEEK_SET);/* rewind */
573 lseek(fd, (off_t) blk_offset, SEEK_SET);
574 tst_word |= (1 << bit); /* not free anymore */
575 rwd = write(fd, (char *) &tst_word, SIZE_OF_INT);
576 if (rwd != SIZE_OF_INT) {
577 printf("Bad write in zone map\n");
578 printf("Check file system \n");
579 done(DIR_CREATED);
580 }
581 return;
582 }
583 printf("Bit map indicates that block %lu is in use. Not marked.\n",
584 (unsigned long) num);
585/* done(DIR_CREATED); */
586 return;
587}
588
589 /* ======= interactive interface ======= */
590
591long rd_num()
592{ /* read a number from stdin */
593 long num;
594 int c;
595
596 if (eofseen) return(-1);
597 do {
598 c = getchar();
599 if (c == EOF || c == '\n') return(-1);
600 } while (c != '-' && (c < '0' || c > '9'));
601
602 if (c == '-') {
603 printf("Block numbers must be positive\n");
604 exit(1);
605 }
606 num = 0;
607 while (c >= '0' && c <= '9') {
608 num *= 10;
609 num += c - '0';
610 c = getchar();
611 if (c == '\n') eofseen = 1;
612 }
613 return num;
614}
615
616
617
618int ok(str)
619char *str;
620{
621 int c;
622
623 for (;;) {
624 printf("%s", str);
625 while ((c = getchar()) != EOF &&
626 c != 'y' && c != 'n' && c != 'q')
627 if (c != '\n') printf(" Bad character %c\n", (char) c);
628 switch (c) {
629 case EOF:
630 return QUIT;
631 case 'y':
632 return OK;
633 case 'n':
634 return NOT_OK;
635 case 'q': return QUIT;
636 }
637 printf("\n");
638 }
639}
640
641
642void done(nr)
643int nr;
644{
645 switch (nr) {
646 case SUCCESS:
647 case FILE_EXISTS:
648 unlink(file_name);
649 case DEV_MOUNTED:
650 umount(dev_name);
651 case DIR_CREATED:
652 rmdir(dir_name);
653 case HARMLESS:;
654 }
655 sync();
656 exit(nr == SUCCESS ? 0 : 1);
657}
Note: See TracBrowser for help on using the repository browser.