#include #include "dklabel.h" /* needs to be before fx.h for prototypes */ #include "fx.h" char *bootfile = "/unix"; static struct device_parameters otherparams; extern MENU *cmenu; extern int verbose_sgiinfo; extern MENU drive_menu; extern int nomenus; /* see fx.c, -c option */ void sync_func(void) { #ifdef SMFD_NUMBER if(drivernum == SMFD_NUMBER) { printf("no labels on floppy disks\n"); return; } #endif /* SMFD_NUMBER */ printf("writing label info to %s\n", dksub()); if (update() != 0) scerrwarn("label write failed!\n"); } void show_all_func(void) { if(drivernum == SCSI_NUMBER) { scsi_showparam_func(); scsi_showgeom_func(); } #ifdef SMFD_NUMBER else if(drivernum == SMFD_NUMBER) { /* check so people won't see error messages when they choose * 'all' and they are dealing with the floppy */ scsi_showparam_func(); scsi_showgeom_func(); return; } #endif /* SMFD_NUMBER */ show_pt_func(); show_bi_func(); show_dt_func(); show_sgiinfo_func(); } void create_all_func(void) { #ifdef SMFD_NUMBER if(drivernum == SMFD_NUMBER) /* nothing to do at all, so just return */ return; #endif /* do bootinfo before pt_func, because if bootinfo sets the swap and root partition #'s, and if we are creating everything, we want to use the sgi defaults, not something left over */ create_bi_func(); create_pt_func(); create_sgiinfo_func(); create_dt_func(); } void show_bi_func(void) { char *f; #ifdef SMFD_NUMBER if(drivernum == SMFD_NUMBER) { printf("no bootinfo on floppy disks\n"); return; } #endif /* SMFD_NUMBER */ f = "%15s = %d "; setoff("bootinfo"); printf(f, "root partition", VP(&vh)->vh_rootpt); printf(f, "swap partition", VP(&vh)->vh_swappt); printf("%s = %.16s", "bootfile", VP(&vh)->vh_bootfile); newline(); } void set_bi_func(void) { uint rootpt, swappt; STRBUF s; #ifdef SMFD_NUMBER if(drivernum == SMFD_NUMBER) { printf("no bootinfo on floppy disks\n"); return; } #endif /* SMFD_NUMBER */ argslice(&rootpt, PTNUM_FSROOT, PTNUM_VOLHDR, "root partition"); argslice(&swappt, PTNUM_FSSWAP, PTNUM_VOLHDR, "swap partition"); strncpy(s.c, VP(&vh)->vh_bootfile, BFNAMESIZE); s.c[BFNAMESIZE] = '\0'; argstring(s.c, s.c, "bootfile"); argcheck(); VP(&vh)->vh_rootpt = rootpt; VP(&vh)->vh_swappt = swappt; strncpy(VP(&vh)->vh_bootfile, s.c, BFNAMESIZE); (void)checkparts(); changed = 1; show_bi_func(); } void create_bi_func(void) { #ifdef SMFD_NUMBER if(drivernum == SMFD_NUMBER) { printf("no bootinfo on floppy disks\n"); return; } #endif /* SMFD_NUMBER */ printf("...creating default bootinfo\n"); VP(&vh)->vh_rootpt = PTNUM_FSROOT; VP(&vh)->vh_swappt = PTNUM_FSSWAP; strncpy(VP(&vh)->vh_bootfile, bootfile, BFNAMESIZE); changed = 1; } void readin_bi_func(void) { CBLOCK b; printf("...reading in bootinfo\n"); if( readdvh(VP(&b)) < 0 ) return; bcopy((char *)VP(&b)->vh_bootfile, (char *)VP(&vh)->vh_bootfile, sizeof VP(&b)->vh_bootfile); VP(&vh)->vh_rootpt = VP(&b)->vh_rootpt; VP(&vh)->vh_swappt = VP(&b)->vh_swappt; } void create_dt_func(void) { #ifdef SMFD_NUMBER if(drivernum == SMFD_NUMBER) { printf("no volume header on floppy disks\n"); return; } #endif /* SMFD_NUMBER */ bzero((char *)DT(&vh), sizeof DT(&vh)); printf("...creating default volume directory\n"); changed = 1; } void readin_dt_func(void) { CBLOCK b; printf("...reading in volume directory\n"); if( readdvh(VP(&b)) < 0 ) return; bcopy((char *)DT(&b), (char *)DT(&vh), sizeof DT(&vh)); } /* ix not used, but colprint passes it */ /*ARGSUSED*/ static void desub(int ix, int *t, char *tgt) { register struct volume_directory *dirp; dirp = DT(&vh)+*t; sprintf(tgt, "%2d: %-10.8s block %4d size %7d", *t, dirp->vd_name, dirp->vd_lbn, dirp->vd_nbytes); } void show_dt_func(void) { int dirnums[NVDIR]; register uint i, j; #ifdef SMFD_NUMBER if(drivernum == SMFD_NUMBER) { printf("no volume header on floppy disks\n"); return; } #endif /* SMFD_NUMBER */ j = 0; for( i = 0; i < NVDIR; i++ ) if( *DT(&vh)[i].vd_name != '\0' ) dirnums[j++] = i; setoff("directory entries"); colprint(dirnums, j, sizeof *dirnums, 38, desub); } /* * update the named directory entry with the given data. */ void update_dt(char *name, void *data, int len) { register struct volume_directory *vd; char ls_map[4096]; /* this is pretty ugly; the size sets how much of the vh is even looked at... */ int ls_max = sizeof(ls_map); int slen; /* len in sectors */ int lbn; #ifdef SMFD_NUMBER if(drivernum == SMFD_NUMBER) { printf("no volume header on floppy disks\n"); return; } #endif /* SMFD_NUMBER */ if (!len) { printf("Request for 0 len directory entry!?\n"); /* DHXX*/ return; } if( (vd = findent(name, 1)) == 0 ) { errwarn("can't alloc entry for %s", name); return; } ls_init(ls_map, &ls_max); slen = btos(len); len = stob(slen); /* round it up to a sector size */ if(vd->vd_lbn) map_unbusy(ls_map, ls_max, vd->vd_lbn, slen); if((lbn = map_alloc(ls_map, ls_max, slen)) < 0 ) { map_busy(ls_map, ls_max, lbn, slen); errwarn("can't alloc label space for %s", name); return; } if( gwrite((daddr_t)lbn, data, slen) < 0 ) { /* print this even if EROFS */ errwarn("can't write %s", name); return; } vd->vd_lbn = lbn; /* Success */ vd->vd_nbytes = len; } void ls_init(char *lmap, int *lmax) { register int i; if(*lmax > PT(&vh)[PTNUM_VOLHDR].pt_nblks) *lmax = PT(&vh)[PTNUM_VOLHDR].pt_nblks; map_unbusy(lmap, *lmax, (daddr_t)0, *lmax); /* Busy the first 2 blocks. 2 because we had limited * FAT support that we ended up never using. May find a * use for it in the future... This number must agree with * the code in dvhtool that creates files in the volhdr. * * NOTE: originally the volume_header struct was supposed to be * written in sector 0 of each track in cyl 0 for redundancy. * Due to a bug in dvhtool and fx, this wasn't enforced until * 3.2. The redundant copies have never been used by anything, * and few people knew about them, and NOW (2/92) we find that * we need to put much larger files in the volhdr. That meant * that as of 3.2, we simply busied out the entire first * cylinder (because even though we only wrote 2 single sector * files, if we didn't do this, the copies of the volhdr could * have gotten written out on top of files we put in volhdr * after we wrote the files, thus trashing them. We can't go * out and retroactively increase the size of the volhdr * partition on thousands of machines, so now we are * deliberately going back to NOT making the redundant copies, * and allowing use of the whole volhdr partition (except sector * 0, of course) for files. This big comment is so that no * one will come back in the future and 'fix' this again... * Dave Olson, 2/92 */ map_busy(lmap, *lmax, 0, 2); /* now mark all the blocks used by existing files as in use */ for( i = 0; i < NVDIR; i++ ) map_busy(lmap, *lmax, DT(&vh)[i].vd_lbn, btos(DT(&vh)[i].vd_nbytes)); } /* * find the named entry. optionally create it if nonexistent. */ struct volume_directory * findent(char *name, int flag) { register struct volume_directory *vd, *slot; register int i; slot = 0; for( vd = DT(&vh) , i = NVDIR; --i >= 0; vd++ ) { if( strncmp(name, vd->vd_name, VDNAMESIZE) == 0 ) return vd; if( slot == 0 ) if( *vd->vd_name == '\0' && vd->vd_nbytes == 0 ) slot = vd; } if(flag && slot) { strncpy(slot->vd_name, name, VDNAMESIZE); slot->vd_lbn = 0; changed = 1; return slot; } return 0; } /* * Clear the named entry, if it exists, from the volume directory. * Needed when zapping the badblock list. */ void clearent(char *name) { register struct volume_directory *vd; register int i; int zapping = 0; for( vd = DT(&vh) , i = NVDIR; --i >= 0; vd++ ) { if (!zapping && strncmp(name, vd->vd_name, VDNAMESIZE) == 0 ) zapping = 1; if (zapping) { if (i > 1) bcopy((caddr_t)(vd + 1), (caddr_t)vd, sizeof (struct volume_directory)); else bzero((caddr_t)vd, sizeof (struct volume_directory)); } } } /* * read in the label information. * first we must get the volume header. if that is invalid, * get the drive type and set up defaults. */ void init_label(char *dname) { int mustrewrite = 0; int partsok; #ifdef SMFD_NUMBER if(drivernum == SMFD_NUMBER) { check_dp(dname, 0); return; } #endif /* SMFD_NUMBER */ if( readdvh(&vh) < 0 ) { /* no volhdr of either type */ mustrewrite = 1; check_dp(dname, 0); create_label(); } else { check_dp(dname, 1); if(expert && (partsok=checkparts())) { printf("NOTE: %s existing partitions are" " inconsistent with drive geometry\n", dname); if(partsok < 0) { /* some were out of range default to no, since * this will make it difficult if not impossible * to get their old partitions (and thus their data) * back. */ if(!no("create default partitions (answering yes may" "make it difficult\n\tto retrieve existing" "data from the drive)")) { create_pt_func(); mustrewrite = 1; } /* default to no here also */ if(!no("edit partitions") ){ callfunc(set_pt_func, "partitions"); mustrewrite = 1; } } } } if(mustrewrite) set_vh(VP(&vh)); } /* * initialize drive parameters; if vhvalid, use the volume header. * in the end, we should have 3 consistent pieces of info: * the disk name * the sgi disk type * drive parameters * * the argument is the name of a disk drive. it may be * '' null; ask for drive type * 'other' the literal other; prompt for all info * xxx the full name of a disk drive; get info from tables * * Returns: 0 if existing volume header is OK, * 1 if the volume header must be rewritten to the driver. */ void check_dp(char *dname, int vhvalid) { CBLOCK sgijunk; int goodsgilab = 0; int cantread = 0; switch (drivernum) { #ifdef SMFD_NUMBER case SMFD_NUMBER: /* done just to get name printed */ #endif /* SMFD_NUMBER */ case SCSI_NUMBER: otherparams.dp_flags = DP(&vh)->dp_flags; otherparams.dp_ctq_depth = DP(&vh)->dp_ctq_depth; otherparams.dp_drivecap = DP(&vh)->dp_drivecap; scsiset_dp(&otherparams); scsiset_label(&sgijunk, 1); break; default: printf("Don't know how to set parameters for driver type %d\n", drivernum); return; } if(expert) changed = !vhvalid; if(vhvalid && expert && dpcmp(DP(&vh), &otherparams)) { printf("\ warning: %s disagrees with existing volume header parameters\n", dname); if( yes("show differences") ) showdiff_dp(); /* default to using existing vh; otherwise user can wind up with partitition layout that dosn't match their filesystems. */ if(!yes("use existing volume header") ) changed = 1; } /* always do this; was if mustrewrite || expert; but vh still needs to be valid for exercising, even if not expert. Turns out the equivalent of this must be buried down in the code for hard disks, because it worked for them, but wouldn't work for floppies. Can't see what this would ever hurt, and hopefully we can remove the redundant code in other portions of fx sometime... Olson, 4/83 */ bcopy((char *)&otherparams, (char *)DP(&vh), sizeof otherparams); if (vhvalid && expert) { bzero(&sg, sizeof (struct disk_label)); cantread = readsgilabel (&sg, !expert); /* hardly worth it for scsi, but... */ if(cantread==0 && ((struct disk_label *)&sg)->d_magic == D_MAGIC) goodsgilab = 1; } if (!goodsgilab #ifdef SMFD_NUMBER && drivernum != SMFD_NUMBER #endif /* SMFD_NUMBER */ ) /* don't complain, and don't mark anything as changed. If they * do happen to write the volhdr for some other reason, then * we'll create the sgilabel; see comments at readsgilabel() * for why we don't complain, 4/97. */ bcopy((char *)&sgijunk, (char *)&sg, sizeof(struct disk_label)); } int dpcmp(struct device_parameters *v, struct device_parameters *a) { if(v->dp_secbytes == a->dp_secbytes && (!a->dp_drivecap || v->dp_drivecap == a->dp_drivecap)) return 0; return 1; } void showdiff_dp(void) { register struct device_parameters *v, *a; register char *f; v = DP(&vh); a = &otherparams; # define DD(s, e) if(v->e!=a->e)printf(f,s,v->e,a->e) f = "%15s = volume header %-5d; should be %-5d to match drive\n"; DD("bytes/sec", dp_secbytes); if(v->dp_drivecap) DD("drive capacity", dp_drivecap); # undef DD } /* * make a default volume header based on the current drive parameters. */ void create_label(void) { DP(&vh)->dp_flags = 0; /* make ctq default disabled */ create_bi_func(); create_pt_func(); create_dt_func(); } /* Update mbr from vh */ int update_vh(void) { int error; char b[MAX_BSIZE]; /* gwrite will use the 'actual' secsize */ /* DHXX: must set volume header in driver as well as on disk!! set_vh now has the side-effect of setting the magic # and checksum in vh for us. */ VP(&vh)->vh_magic = 0; error = set_vh(VP(&vh)); if ( ! error) { bzero(b, sizeof b); bcopy((char *)VP(&vh), b, sizeof *VP(&vh)); if (gwrite(0, b, 1) < 0 ) errwarn("can't write volume header on disk"); } return error; } int checkvh(struct volume_header *vhp) { if ((vhp->vh_magic == VHMAGIC && vhchksum((int *)vhp, sizeof *vhp) == 0 )) return 0; bzero(vhp, sizeof *vhp); return -1; } int readdvh(struct volume_header *vhp) { get_vh(vhp); if (checkvh(vhp) < 0 ) return errwarn("invalid label from disk driver, ignored"); return 0; } int readinvh(struct volume_header *vhp) { if(gread(0, vhp, 1) == 0 && checkvh(vhp) == 0 ) { extern unsigned scsi_cap; if(!DP(vhp)->dp_drivecap) { if(!scsi_cap) scsi_readcapacity(&scsi_cap); /* shouldn't happen */ DP(vhp)->dp_drivecap = scsi_cap; } return 0; } return -1; } void lastchance(void) { STRBUF s; sprintf(s.c, "about to destroy data on disk %s! ok", dksub()); if(nomenus) printf("%s\n", s.c); else { banner("WARNING"); if( !yesno(-1, s.c) ) mpop(); } changed = 1; } void optupdate(void) { STRBUF s; CBLOCK b; #ifdef SMFD_NUMBER if(drivernum == SMFD_NUMBER) /* not on floppies */ return; #endif /* SMFD_NUMBER */ /* fetch real label from disk, make sure it's valid */ if( readinvh(VP(&b)) < 0 ) changed = 1; if( changed ) { sprintf(s.c, "\ label info has changed for disk %s. write out changes", dksub()); if( yes(s.c) ) { if (update() != 0) scerrwarn("label write failed!\n"); } } } int update(void) { int error; switch (drivernum) { case SCSI_NUMBER: break; /* nothing to do for badblocks on scsi */ default: printf("Don't know how to update for driver type %d\n", drivernum); return EINVAL; } error = update_vh(); if ( ! error ) { /* Don't allow messing with * sgilabel if nonexpert */ if (expert) update_sgi(); changed = 0; } return error; } void getattribs(int *_n, ITEM *items) { register ITEM *t; register int n, b; STRBUF s; n = *_n; for( t = items; t->name != 0; t++ ) { b = n & t->value; n &= ~t->value; sprintf(s.c, "enable %s", t->name); if( yesno(b, s.c) ) n |= t->value; } *_n = n; } /* bytes to sectors */ uint btos(uint n) { return (n + DP(&vh)->dp_secbytes - 1) / DP(&vh)->dp_secbytes; } void readin_dp(void) { CBLOCK b; if( readdvh(VP(&b)) < 0 ) return; bcopy((char *)DP(&b), (char *)DP(&vh), sizeof *DP(&vh)); } char * dksub(void) { static STRBUF s; if (device_name_specified) sprintf(s.c, "%s", device_name); else { sprintf(s.c, "%s(%d,%d,%d)", driver, ctlrno, driveno, scsilun); #ifdef SMFD_NUMBER if(drivernum == SMFD_NUMBER) { char *type; if(type=(char *)smfd_partname(partnum)) strcat(s.c, type); } #endif /* SMFD_NUMBER */ } return s.c; } /* ----- map routines ----- */ void map_busy(char *map, uint len, uint a, int n) { while(n) { if(a < len ) map[a++] = 1; n--; } } void map_unbusy(char *map, uint len, uint a, int n) { while(n) { if(a < len ) map[a++] = 0; n--; } } map_alloc(char *map, uint len, uint n) { register uint r, f; f = 0; for( r = 0; r < len; r++ ) if( map[r] ) { f = 0; } else { if( ++f >= n ) { r++; while(n) { map[--r] = 1; n--; } return (int)r; } } return -1; } /* ----- geometry routines ----- */ uint stob(uint n) { return n * DP(&vh)->dp_secbytes; } /* round # of blocks to to number of megabytes; since we are getting * close to 4 Gb disks, do in such a way that we don't overflow an * unsigned 32bit value. */ uint mbytes(uint blocks) { blocks += ((1<<19)-1) / DP(&vh)->dp_secbytes; return blocks / ((1<<20) / DP(&vh)->dp_secbytes); } /* return block number corresponding to value in megabytes */ uint mbytetoblk(uint mbyte) { return mbyte * ((1<<20) / DP(&vh)->dp_secbytes); }