#ifndef lint static char *sccsid = "@(#)stripe.c 1.1 94/10/31 SMI"; #endif #include #include #include #include #include #include #include #include #include #include #include #include #define DEBUG 1 /* * Data structures to support reading the /etc/stripetab file. */ struct stripe_dev { char *dev_name; }; struct stripe_row { int stripe_width; struct stripe_dev lower_device[MAX_MD_DISKS]; }; struct stripe_table { struct stripe_dev upper_device; int parity_interval; /* interlace */ struct stripe_dev mirror_device; int active_rows; struct stripe_row stripe_rows[MAX_MD_ROWS]; }; #define MAXSTRIPESTR 255 int Aflag = 0; /* Carry out all the striping in /etc/stripetab */ int Nflag = 0; /* Don't carry out the operation */ int Tflag = 0; int Cflag = 0; /* Un-stripe the file system */ int Pflag = 0; /* Perform parity operations */ int fso; int fsi; FILE *stripetab; char *stripename = "/etc/stripetab"; struct md_struct mdinfo; struct stripe_table command_line_table; struct dk_geom g; struct dk_map p; struct dk_info inf; /* * Mainline. Crack command line arguments, and push the correct * levers (but you knew that, didn't you) */ main (argc, argv) int argc; char *argv[]; { int i; int j; char *fsys; #ifndef STANDALONE argc--, argv++; for (j = 0; argc > 0; j++) { if (argv[0][0] == '-') { for (i = 1; argv[0][i] != '\0'; i++) { switch (argv[0][i]) { case 'A': case 'a': Aflag++; break; case 'N': case 'n': Nflag++; break; case 'C': case 'c': Cflag++; break; default: printf("%s: unknown flag\n", &argv[0][i]); usage (); break; } } argc--, argv++; } else break; } if ((Cflag)) { parse_command_line (argc, argv, &command_line_table); fsys = command_line_table.upper_device.dev_name; clear_mdinfo (&mdinfo); mdinfo.md_status = MD_CLEAR; } else { if (argc > 1) { parse_command_line (argc, argv, &command_line_table); fsys = command_line_table.upper_device.dev_name; table_to_mdstruct (&command_line_table, &mdinfo); } else { if (Aflag) { stripe_file (stripename); exit (0); } else { report_file (stripename); exit (0); } } } stripe_the (fsys); } /* * Report on each device mentioned in the stripetab file. */ report_file (name) char *name; { struct stripe_table *table; struct stripe_table *file_line (); char *fsys; while ((table = file_line (name)) != (struct stripe_table *)NULL) { fsys = strdup(table->upper_device.dev_name); report (fsys); printf ("\n"); } } /* * Open a stripe device, check that it's valid, and print out the * striping data. */ report (fsys) char *fsys; { int status; open_for_stripe (fsys); /* * Report on enstripification. */ if ((status = ioctl(fsi, MD_IOCGET, &mdinfo)) >= 0) { } else { perror ("MD_IOCGET"); printf ("Can't perform MD_IOCGET ioctl on fd 0x%x\n", fsi); } dump_mdstruct (&mdinfo); } usage () { fprintf (stderr, "/usr/etc/stripe [-N] rows width spec1 spec2...\n"); fprintf (stderr, " [-C] /* unstripe */\n"); } /* * Read a line from the stripetab file, and parse it. return * NULL if we already read all the lines. */ struct stripe_table * file_line (name) char *name; { char line[MAXSTRIPESTR]; int argc; char **argv; if (stripetab == NULL) { if ((stripetab = fopen(name, "r")) == NULL) { printf ("stripe: can't open %s\n", name); exit (1); } } if (fgets(&line[0], MAXSTRIPESTR, stripetab) != NULL) { parse_line (&line[0], &argc, &argv); parse_command_line (argc, argv, &command_line_table); table_to_mdstruct (&command_line_table, &mdinfo); return (&command_line_table); } return ((struct stripe_table *)NULL); } /* * Read and parse all the lines from the stripetab file, and then * stripe each one. */ stripe_file (name) char *name; { struct stripe_table *table; char *fsys; while ((table = file_line (name)) != (struct stripe_table *)NULL) { fsys = table->upper_device.dev_name; stripe_the (fsys); } } /* * Parse one line from the stripetab file. */ parse_line (line, argc, argv) char *line; int *argc; char ***argv; { int local_argc = 0; char **local_argv_start = (char **)malloc(4 * (MAX_MD_ROWS * (MAX_MD_DISKS + 1))); char **local_argv = local_argv_start; char *p; for (p = line; *p != '\0';) { while (*p == ' ') p++; *local_argv = p; while ((*p != ' ') && (*p != '\n')) p++; *p = '\0'; p++; local_argv++; local_argc++; } *argc = local_argc; *argv = local_argv_start; } /* * Is a file system currently mounted. */ is_mounted (fsys) char *fsys; { char *slash; FILE *mnttab; struct mntent *mntp; char bdevname[MAXPATHLEN]; slash = (char *)rindex(fsys, '/'); if (slash && slash[1] == 'r') { sprintf(bdevname, "/dev/%s", &slash[2]); } else if (*fsys == 'r') { sprintf(bdevname, "/dev/%s", &fsys[1]); } else { strcpy(bdevname, fsys); } mnttab = setmntent(MOUNTED, "r"); while ((mntp = getmntent(mnttab)) != NULL) { if (strcmp(bdevname, mntp->mnt_fsname) == 0) { printf("%s is mounted, can't stripe\n", bdevname); return(1); } } endmntent(mnttab); return (0); } /* * Open a special device, and check that it is capable of being * treated as a striped device. */ open_for_stripe (o_fsys) char *o_fsys; { int status; fso = creat(o_fsys, 0666); if(fso < 0) { perror ("open_for_stripe: creat"); printf("\n%s: cannot create\n", o_fsys); exit(1); } fsi = open(o_fsys, O_RDWR); if(fsi < 0) { perror ("open_for_stripe: open"); printf("\n%s: cannot open\n", o_fsys); exit(1); } /* * Check that we have a stripable file system */ { struct dk_info dkinfo; if ((status = ioctl(fsi, DKIOCINFO, &dkinfo)) >= 0) { switch (dkinfo.dki_ctype) { case DKC_MD: break; default: printf ("Bad disk type 0x%x\n", dkinfo.dki_ctype); exit(1); } } else { perror ("DKIOCINFO"); printf ("Can't perform DKIOCINFO ioctl on fd 0x%x\n", fsi); } } } /* * Check that a file system can be opened for striping, open * it, and carry oout the striping. */ stripe_the(s_fsys) char *s_fsys; { int status; char *fsys; if (!Nflag) { if (is_mounted (s_fsys)) { printf("%s is mounted, can't stripe\n", s_fsys); exit(1); } } fsys = strdup(s_fsys); open_for_stripe (fsys); /* * Carry out the enstripification. */ if ((status = ioctl(fsi, MD_IOCSET, &mdinfo)) >= 0) { } else { perror ("MD_IOCSET"); printf ("Can't perform MD_IOCSET ioctl on fd 0x%x\n", fsi); } #else #endif } /* * Command line format. * * stripe [-N] upper_device #rows * #width lower_device lower_device lower_device * #width lower_device lower_device * [-m another_device ] */ parse_command_line (argc, argv, table) int argc; char *argv[]; struct stripe_table *table; { int rows; int width; struct stripe_row *row; /* * Parse the upper device. * * stripe [-N] upper_device #rows */ table->upper_device.dev_name = argv[0]; if (argc < 4) { printf ("Need \n"); return; } argc--; argv++; if ((table->active_rows = atoi(argv[0])) <= 0) { printf ("Must have at least one active row\n"); exit(1); } argc--; argv++; /* * Parse the #rows lower devices. * * #width lower_device lower_device lower_device * * Each row must have at least two entries. */ for (rows = 0; rows < table->active_rows; rows++) { if (argc < ((table->active_rows - rows) * 2)) { printf ("%d remaining arguments not enough for %d remaining rows\n", argc, table->active_rows - rows); exit(1); } row = &table->stripe_rows[rows]; if((row->stripe_width = atoi(argv[0])) <= 0) { printf ("Row %d must have at least one active disk\n"); exit(1); } argc--; argv++; for (width = 0; width < row->stripe_width; width++) { row->lower_device[width].dev_name = argv[0]; argc--; argv++; } } table->mirror_device.dev_name = (char *)0; table->parity_interval = 0; while (argc) { if ((argv[0][0] == '-') && (argv[0][1] == 'm')) { argc--; argv++; table->mirror_device.dev_name = argv[0]; argc--; argv++; } else { if ((argv[0][0] == '-') && (argv[0][1] == 'p')) { argc--; argv++; table->parity_interval = atoi (argv[0]); Pflag++; argc--; argv++; } else { printf ("parse_command_line: bad option %s\n", argv[0]); exit(1); } } } dump_stripe_entry (table); } /* * Convert a stripe table entry into an md_struct entry suitable * for use in an ioctl. */ table_to_mdstruct (table, mdstruct) struct stripe_table *table; struct md_struct *mdstruct; { int row; struct md_row *mdrow; int width; int cum_blocks = 0; int cum_data_blocks = 0; int disk_size = 0; int size = 0; struct stripe_row *trow; clear_mdinfo (mdstruct); mdstruct->md_status = 0; mdstruct->md_ndisks = 0; mdstruct->md_rows = table->active_rows; if (table->mirror_device.dev_name != (char *)0) { printf ("table_to_mdstruct: mirror device %s\n", table->mirror_device.dev_name); mdstruct->md_mirror = device_of(table->mirror_device.dev_name); } else { printf ("table_to_mdstruct: no mirror device\n"); mdstruct->md_mirror = 0; } mdstruct->md_parity_interval = table->parity_interval; printf ("table_to_mdstruct: md_parity_interval 0x%x\n", mdstruct->md_parity_interval); for (row = 0; row < mdstruct->md_rows; row++) { mdrow = &mdstruct->md_real_row[row]; trow = &table->stripe_rows[row]; mdrow->md_width = trow->stripe_width; mdrow->md_row_disks = trow->stripe_width; for (width = 0; width < mdrow->md_width; width++) { mdstruct->md_ndisks += 1; mdrow->md_real_disks[width] = device_of(trow->lower_device[width].dev_name); size = size_of(trow->lower_device[width].dev_name); if (disk_size == 0) { disk_size = size; } else { if (size < disk_size) { disk_size = size; } } printf ("device %s size %d\n", trow->lower_device[width].dev_name, size_of(trow->lower_device[width].dev_name)); } cum_blocks += mdrow->md_width * disk_size; mdrow->md_blocks = mdrow->md_width * disk_size; mdrow->md_data_blocks = (Pflag != 0) ? mdrow->md_blocks - disk_size : mdrow->md_blocks; cum_data_blocks += mdrow->md_data_blocks; mdrow->md_cum_blocks = cum_blocks; mdrow->md_cum_data_blocks = cum_data_blocks; if (Pflag != 0) printf ("table_to_mdstruct: parity reduces effective size from %d to %d\n", cum_blocks, cum_data_blocks); mdrow->md_start_block = 0; mdrow->md_end_block = disk_size; } } dump_mdstruct (mdstruct) struct md_struct *mdstruct; { int row; int width; printf ("rows %d total disks %d mirror 0x%x\n", mdstruct->md_rows, mdstruct->md_ndisks, mdstruct->md_mirror); for (row = 0; row < mdstruct->md_rows; row++) { printf ("\trow %d width %d disks %d blocks %d cum blocks %d start block %d end block %d\n", row, mdstruct->md_real_row[row].md_width, mdstruct->md_real_row[row].md_row_disks, mdstruct->md_real_row[row].md_blocks, mdstruct->md_real_row[row].md_cum_blocks, mdstruct->md_real_row[row].md_start_block, mdstruct->md_real_row[row].md_end_block); printf ("\t\t"); for (width = 0; width < mdstruct->md_real_row[row].md_width; width++) { printf (" 0x%x ", mdstruct->md_real_row[row].md_real_disks[width]); } printf ("\n"); } } device_of (device_name) char *device_name; { struct stat status; if (stat(device_name, &status) == -1) { perror ("device_of"); printf ("Can't stat '%s'\n", device_name); exit(1); } return (status.st_rdev); } off_t size_of (device_name) char *device_name; { struct stat st; int fd, c, found, nparts; char *rawname(); if (stat(rawname(device_name), &st) == 0) { fd = open(rawname(device_name), 0); if (fd < 0) { perror(device_name); return(0); } if (isdisk(fd)) { if (parts(0, fd) == 0) printf("Not a valid partition.\n"); } else printf("%s: Not a valid disk.\n", device_name); close(fd); if (p.dkl_nblk & 0xf) { return(p.dkl_nblk & ~0xf); } return(p.dkl_nblk); } return (0); } isdisk(fd) { return (ioctl(fd, DKIOCINFO, &inf) == 0 && ioctl(fd, DKIOCGGEOM, &g) == 0); } parts(pa, fd) { int n; if (ioctl(fd, DKIOCGPART, &p) == 0) { return (p.dkl_nblk ? 1 : 0); } else { if (pa) printf("%c: ", pa); printf("can't get partition info\n"); return (0); } } char * rawname(cp) char *cp; { static char rawbuf[MAXPATHLEN]; char *dp = rindex(cp, '/'); if (dp == 0) return (0); *dp = 0; (void)strcpy(rawbuf, cp); *dp = '/'; (void)strcat(rawbuf, "/r"); (void)strcat(rawbuf, dp+1); return (rawbuf); } dump_stripe_entry (table) struct stripe_table *table; { int rows; int width; struct stripe_row *row; printf ("device:\t%s = %d rows\n", table->upper_device.dev_name, table->active_rows); for (rows = 0; rows < table->active_rows; rows++) { row = &table->stripe_rows[rows]; printf ("\trow %d width %d: ", rows, row->stripe_width); for (width = 0; width < row->stripe_width; width++) { printf (" %s ", row->lower_device[width].dev_name); } printf ("\n"); } } clear_mdinfo (mdstruct) struct md_struct *mdstruct; { bzero (mdstruct, sizeof (* mdstruct)); }