static void check_root_dir(void) { struct sfs_inode sfi; diskread(&sfi, SFS_ROOT_LOCATION); swapinode(&sfi); switch (sfi.sfi_type) { case SFS_TYPE_DIR: break; case SFS_TYPE_FILE: warnx("Root directory inode is a regular file (fixed)"); goto fix; default: warnx("Root directory inode has invalid type %lu (fixed)", (unsigned long) sfi.sfi_type); fix: setbadness(EXIT_RECOV); sfi.sfi_type = SFS_TYPE_DIR; swapinode(&sfi); diskwrite(&sfi, SFS_ROOT_LOCATION); break; } check_dir(SFS_ROOT_LOCATION, SFS_ROOT_LOCATION, ""); }
static void adjust_filelinks(void) { struct sfs_inode sfi; int i; for (i=0; i<ninodes; i++) { if (inodes[i].linkcount==0) { /* directory */ continue; } diskread(&sfi, inodes[i].ino); swapinode(&sfi); assert(sfi.sfi_type == SFS_TYPE_FILE); if (sfi.sfi_linkcount != inodes[i].linkcount) { warnx("File %lu link count %lu should be %lu (fixed)", (unsigned long) inodes[i].ino, (unsigned long) sfi.sfi_linkcount, (unsigned long) inodes[i].linkcount); sfi.sfi_linkcount = inodes[i].linkcount; setbadness(EXIT_RECOV); swapinode(&sfi); diskwrite(&sfi, inodes[i].ino); } count_files++; } }
static void dirwrite(const struct sfs_inode *sfi, struct sfs_dir *d, int nd) { const unsigned atonce = SFS_BLOCKSIZE/sizeof(struct sfs_dir); unsigned nblocks = SFS_ROUNDUP(nd, atonce) / atonce; unsigned i, j, bad; for (i=0; i<nblocks; i++) { uint32_t block = dobmap(sfi, i); if (block!=0) { for (j=0; j<atonce; j++) { swapdir(&d[i*atonce+j]); } diskwrite(d + i*atonce, block); } else { for (j=bad=0; j<atonce; j++) { if (d[i*atonce+j].sfd_ino != SFS_NOINO || d[i*atonce+j].sfd_name[0] != 0) { bad = 1; } } if (bad) { warnx("Cannot write to missing block in " "sparse directory (ERROR)"); setbadness(EXIT_UNRECOV); } } } }
static void writebitmap(uint32_t fsblocks) { uint32_t nbits = SFS_BITMAPSIZE(fsblocks); uint32_t nblocks = SFS_BITBLOCKS(fsblocks); char *ptr; uint32_t i; if (nblocks > MAXBITBLOCKS) { errx(1, "Filesystem too large " "- increase MAXBITBLOCKS and recompile"); } doallocbit(SFS_SB_LOCATION); doallocbit(SFS_ROOT_LOCATION); for (i=0; i<nblocks; i++) { doallocbit(SFS_MAP_LOCATION+i); } for (i=fsblocks; i<nbits; i++) { doallocbit(i); } for (i=0; i<nblocks; i++) { ptr = bitbuf + i*SFS_BLOCKSIZE; diskwrite(ptr, SFS_MAP_LOCATION+i); } }
void do_multiboot(const char *filename) { uint8_t buf[SECTORSIZE]; elf_header *elfh = (elf_header *) buf; off_t o; if ((diskfd = open(filename, O_RDWR)) < 0) { fprintf(stderr, "%s: %s\n", filename, strerror(errno)); usage(); } readsect(buf, 0); if (elfh->e_magic != ELF_MAGIC) { fprintf(stderr, "%s: not an ELF executable file\n", filename); usage(); } o = elfh->e_phoff + sizeof(elf_program) * elfh->e_phnum; if (o >= 4096 - sizeof(multiboot_header)) { fprintf(stderr, "%s: ELF header too large to accommodate multiboot header\n", filename); usage(); } else if (lseek(diskfd, o, SEEK_SET) != o) { perror("lseek"); usage(); } diskwrite(multiboot_header, sizeof(multiboot_header)); exit(0); }
static void flush(Buffer *b) { if(b->cdirty || b->cnc==0){ if(b->cnc == 0) delblock(b, b->cbi); else diskwrite(disk, &b->bl[b->cbi], b->c, b->cnc); b->cdirty = FALSE; } }
static void writerootdir(void) { struct sfs_inode sfi; bzero((void *)&sfi, sizeof(sfi)); sfi.sfi_size = SWAPL(0); sfi.sfi_type = SWAPS(SFS_TYPE_DIR); sfi.sfi_linkcount = SWAPS(1); diskwrite(&sfi, SFS_ROOT_LOCATION); }
/* * Write out the free block bitmap. */ static void writefreemap(uint32_t fsblocks) { uint32_t freemapblocks; char *ptr; uint32_t i; /* Write out each of the blocks in the free block bitmap. */ freemapblocks = SFS_FREEMAPBLOCKS(fsblocks); for (i=0; i<freemapblocks; i++) { ptr = freemapbuf + i*SFS_BLOCKSIZE; diskwrite(ptr, SFS_FREEMAP_START+i); } }
/* * Write out the root directory inode. */ static void writerootdir(void) { struct sfs_dinode sfi; /* Initialize the dinode */ bzero((void *)&sfi, sizeof(sfi)); sfi.sfi_size = SWAP32(0); sfi.sfi_type = SWAP16(SFS_TYPE_DIR); sfi.sfi_linkcount = SWAP16(1); /* Write it out */ diskwrite(&sfi, SFS_ROOTDIR_INO); }
static void check_sb(void) { struct sfs_super sp; uint32_t i; int schanged=0; diskread(&sp, SFS_SB_LOCATION); swapsb(&sp); if (sp.sp_magic != SFS_MAGIC) { errx(EXIT_UNRECOV, "Not an sfs filesystem"); } assert(nblocks==0); assert(bitblocks==0); nblocks = sp.sp_nblocks; bitblocks = SFS_BITBLOCKS(nblocks); assert(nblocks>0); assert(bitblocks>0); bitmap_init(bitblocks); for (i=nblocks; i<bitblocks*SFS_BLOCKBITS; i++) { bitmap_mark(i, B_PASTEND, 0); } if (checknullstring(sp.sp_volname, sizeof(sp.sp_volname))) { warnx("Volume name not null-terminated (fixed)"); setbadness(EXIT_RECOV); schanged = 1; } if (checkbadstring(sp.sp_volname)) { warnx("Volume name contains illegal characters (fixed)"); setbadness(EXIT_RECOV); schanged = 1; } if (schanged) { swapsb(&sp); diskwrite(&sp, SFS_SB_LOCATION); } bitmap_mark(SFS_SB_LOCATION, B_SUPERBLOCK, 0); for (i=0; i<bitblocks; i++) { bitmap_mark(SFS_MAP_LOCATION+i, B_BITBLOCK, i); } }
static void writesuper(const char *volname, uint32_t nblocks) { struct sfs_super sp; bzero((void *)&sp, sizeof(sp)); if (strlen(volname) >= SFS_VOLNAME_SIZE) { errx(1, "Volume name %s too long", volname); } sp.sp_magic = SWAPL(SFS_MAGIC); sp.sp_nblocks = SWAPL(nblocks); strcpy(sp.sp_volname, volname); diskwrite(&sp, SFS_SB_LOCATION); }
/* * Initialize and write out the superblock. */ static void writesuper(const char *volname, uint32_t nblocks) { struct sfs_superblock sb; /* The cast is required on some outdated host systems. */ bzero((void *)&sb, sizeof(sb)); if (strlen(volname) >= SFS_VOLNAME_SIZE) { errx(1, "Volume name %s too long", volname); } /* Initialize the superblock structure */ sb.sb_magic = SWAP32(SFS_MAGIC); sb.sb_nblocks = SWAP32(nblocks); strcpy(sb.sb_volname, volname); /* and write it out. */ diskwrite(&sb, SFS_SUPER_BLOCK); }
static void check_bitmap(void) { uint8_t bits[SFS_BLOCKSIZE], *found, *tofree, tmp; uint32_t alloccount=0, freecount=0, i, j; int bchanged; for (i=0; i<bitblocks; i++) { diskread(bits, SFS_MAP_LOCATION+i); swapbits(bits); found = bitmapdata + i*SFS_BLOCKSIZE; tofree = tofreedata + i*SFS_BLOCKSIZE; bchanged = 0; for (j=0; j<SFS_BLOCKSIZE; j++) { /* we shouldn't have blocks marked both ways */ assert((found[j] & tofree[j])==0); if (bits[j]==found[j]) { continue; } if (bits[j]==(found[j] | tofree[j])) { bits[j] = found[j]; bchanged = 1; continue; } /* free the ones we're freeing */ bits[j] &= ~tofree[j]; /* are we short any? */ if ((bits[j] & found[j]) != found[j]) { tmp = found[j] & ~bits[j]; alloccount += countbits(tmp); if (tmp != 0) { reportbits(i, j, tmp, "free"); } } /* do we have any extra? */ if ((bits[j] & found[j]) != bits[j]) { tmp = bits[j] & ~found[j]; freecount += countbits(tmp); if (tmp != 0) { reportbits(i, j, tmp, "allocated"); } } bits[j] = found[j]; bchanged = 1; } if (bchanged) { swapbits(bits); diskwrite(bits, SFS_MAP_LOCATION+i); } } if (alloccount > 0) { warnx("%lu blocks erroneously shown free in bitmap (fixed)", (unsigned long) alloccount); setbadness(EXIT_RECOV); } if (freecount > 0) { warnx("%lu blocks erroneously shown used in bitmap (fixed)", (unsigned long) freecount); setbadness(EXIT_RECOV); } }
static int check_dir(uint32_t ino, uint32_t parentino, const char *pathsofar) { struct sfs_inode sfi; struct sfs_dir *direntries; int *sortvector; uint32_t dirsize, ndirentries, maxdirentries, subdircount, i; int ichanged=0, dchanged=0, dotseen=0, dotdotseen=0; diskread(&sfi, ino); swapinode(&sfi); if (remember_dir(ino, pathsofar)) { /* crosslinked dir */ return 1; } bitmap_mark(ino, B_INODE, ino); count_dirs++; if (sfi.sfi_size % sizeof(struct sfs_dir) != 0) { setbadness(EXIT_RECOV); warnx("Directory /%s has illegal size %lu (fixed)", pathsofar, (unsigned long) sfi.sfi_size); sfi.sfi_size = SFS_ROUNDUP(sfi.sfi_size, sizeof(struct sfs_dir)); ichanged = 1; } if (check_inode_blocks(ino, &sfi, 1)) { ichanged = 1; } ndirentries = sfi.sfi_size/sizeof(struct sfs_dir); maxdirentries = SFS_ROUNDUP(ndirentries, SFS_BLOCKSIZE/sizeof(struct sfs_dir)); dirsize = maxdirentries * sizeof(struct sfs_dir); direntries = domalloc(dirsize); sortvector = domalloc(ndirentries * sizeof(int)); dirread(&sfi, direntries, ndirentries); for (i=ndirentries; i<maxdirentries; i++) { direntries[i].sfd_ino = SFS_NOINO; bzero(direntries[i].sfd_name, sizeof(direntries[i].sfd_name)); } for (i=0; i<ndirentries; i++) { if (check_dir_entry(pathsofar, i, &direntries[i])) { dchanged = 1; } sortvector[i] = i; } sortdir(sortvector, direntries, ndirentries); /* don't use ndirentries-1 here in case ndirentries == 0 */ for (i=0; i+1<ndirentries; i++) { struct sfs_dir *d1 = &direntries[sortvector[i]]; struct sfs_dir *d2 = &direntries[sortvector[i+1]]; assert(d1 != d2); if (d1->sfd_ino == SFS_NOINO) { continue; } if (!strcmp(d1->sfd_name, d2->sfd_name)) { if (d1->sfd_ino == d2->sfd_ino) { setbadness(EXIT_RECOV); warnx("Directory /%s: Duplicate entries for " "%s (merged)", pathsofar, d1->sfd_name); d1->sfd_ino = SFS_NOINO; d1->sfd_name[0] = 0; } else { snprintf(d1->sfd_name, sizeof(d1->sfd_name), "FSCK.%lu.%lu", (unsigned long) d1->sfd_ino, (unsigned long) uniquecounter++); setbadness(EXIT_RECOV); warnx("Directory /%s: Duplicate names %s " "(one renamed: %s)", pathsofar, d2->sfd_name, d1->sfd_name); } dchanged = 1; } } for (i=0; i<ndirentries; i++) { if (!strcmp(direntries[i].sfd_name, ".")) { if (direntries[i].sfd_ino != ino) { setbadness(EXIT_RECOV); warnx("Directory /%s: Incorrect `.' entry " "(fixed)", pathsofar); direntries[i].sfd_ino = ino; dchanged = 1; } assert(dotseen==0); /* due to duplicate checking */ dotseen = 1; } else if (!strcmp(direntries[i].sfd_name, "..")) { if (direntries[i].sfd_ino != parentino) { setbadness(EXIT_RECOV); warnx("Directory /%s: Incorrect `..' entry " "(fixed)", pathsofar); direntries[i].sfd_ino = parentino; dchanged = 1; } assert(dotdotseen==0); /* due to duplicate checking */ dotdotseen = 1; } } if (!dotseen) { if (dir_tryadd(direntries, ndirentries, ".", ino)==0) { setbadness(EXIT_RECOV); warnx("Directory /%s: No `.' entry (added)", pathsofar); dchanged = 1; } else if (dir_tryadd(direntries, maxdirentries, ".", ino)==0) { setbadness(EXIT_RECOV); warnx("Directory /%s: No `.' entry (added)", pathsofar); ndirentries++; dchanged = 1; sfi.sfi_size += sizeof(struct sfs_dir); ichanged = 1; } else { setbadness(EXIT_UNRECOV); warnx("Directory /%s: No `.' entry (NOT FIXED)", pathsofar); } } if (!dotdotseen) { if (dir_tryadd(direntries, ndirentries, "..", parentino)==0) { setbadness(EXIT_RECOV); warnx("Directory /%s: No `..' entry (added)", pathsofar); dchanged = 1; } else if (dir_tryadd(direntries, maxdirentries, "..", parentino)==0) { setbadness(EXIT_RECOV); warnx("Directory /%s: No `..' entry (added)", pathsofar); ndirentries++; dchanged = 1; sfi.sfi_size += sizeof(struct sfs_dir); ichanged = 1; } else { setbadness(EXIT_UNRECOV); warnx("Directory /%s: No `..' entry (NOT FIXED)", pathsofar); } } subdircount=0; for (i=0; i<ndirentries; i++) { if (!strcmp(direntries[i].sfd_name, ".")) { /* nothing */ } else if (!strcmp(direntries[i].sfd_name, "..")) { /* nothing */ } else if (direntries[i].sfd_ino == SFS_NOINO) { /* nothing */ } else { char path[strlen(pathsofar)+SFS_NAMELEN+1]; struct sfs_inode subsfi; diskread(&subsfi, direntries[i].sfd_ino); swapinode(&subsfi); snprintf(path, sizeof(path), "%s/%s", pathsofar, direntries[i].sfd_name); switch (subsfi.sfi_type) { case SFS_TYPE_FILE: if (check_inode_blocks(direntries[i].sfd_ino, &subsfi, 0)) { swapinode(&subsfi); diskwrite(&subsfi, direntries[i].sfd_ino); } observe_filelink(direntries[i].sfd_ino); break; case SFS_TYPE_DIR: if (check_dir(direntries[i].sfd_ino, ino, path)) { setbadness(EXIT_RECOV); warnx("Directory /%s: Crosslink to " "other directory (removed)", path); direntries[i].sfd_ino = SFS_NOINO; direntries[i].sfd_name[0] = 0; dchanged = 1; } else { subdircount++; } break; default: setbadness(EXIT_RECOV); warnx("Object /%s: Invalid inode type " "(removed)", path); direntries[i].sfd_ino = SFS_NOINO; direntries[i].sfd_name[0] = 0; dchanged = 1; break; } } } if (sfi.sfi_linkcount != subdircount+2) { setbadness(EXIT_RECOV); warnx("Directory /%s: Link count %lu should be %lu (fixed)", pathsofar, (unsigned long) sfi.sfi_linkcount, (unsigned long) subdircount+2); sfi.sfi_linkcount = subdircount+2; ichanged = 1; } if (dchanged) { dirwrite(&sfi, direntries, ndirentries); } if (ichanged) { swapinode(&sfi); diskwrite(&sfi, ino); } free(direntries); free(sortvector); return 0; }
static void check_indirect_block(uint32_t ino, uint32_t *ientry, uint32_t *blockp, uint32_t nblocks, uint32_t *badcountp, int isdir, int indirection) { uint32_t entries[SFS_DBPERIDB]; uint32_t i, ct; if (*ientry !=0) { diskread(entries, *ientry); swapindir(entries); bitmap_mark(*ientry, B_IBLOCK, ino); } else { for (i=0; i<SFS_DBPERIDB; i++) { entries[i] = 0; } } if (indirection > 1) { for (i=0; i<SFS_DBPERIDB; i++) { check_indirect_block(ino, &entries[i], blockp, nblocks, badcountp, isdir, indirection-1); } } else { assert(indirection==1); for (i=0; i<SFS_DBPERIDB; i++) { if (*blockp < nblocks) { if (entries[i] != 0) { bitmap_mark(entries[i], isdir ? B_DIRDATA : B_DATA, ino); } } else { if (entries[i] != 0) { (*badcountp)++; bitmap_mark(entries[i], isdir ? B_DIRDATA : B_DATA, ino); entries[i] = 0; } } (*blockp)++; } } ct=0; for (i=ct=0; i<SFS_DBPERIDB; i++) { if (entries[i]!=0) ct++; } if (ct==0) { if (*ientry != 0) { (*badcountp)++; bitmap_mark(*ientry, B_TOFREE, 0); *ientry = 0; } } else { assert(*ientry != 0); if (*badcountp > 0) { swapindir(entries); diskwrite(entries, *ientry); } } }
int main(int argc, char *argv[]) { char buf[4096]; char zerobuf[512]; FILE *f; size_t n; size_t nsectors; int i; int bootsector_special = 1; #if defined(_MSDOS) || defined(_WIN32) // As our output file is binary, we must set its file mode to binary. diskfd = _fileno(stdout); _setmode(diskfd, _O_BINARY); #else diskfd = fileno(stdout); #endif // Check for a partition if (argc >= 2 && strcmp(argv[1], "-p") == 0) { if (argc < 3) usage(); if ((diskfd = open(argv[2], O_RDWR)) < 0) { fprintf(stderr, "%s: %s\n", argv[2], strerror(errno)); usage(); } if (find_partition(0, 0, 0) <= 0) { fprintf(stderr, "%s: no JOS partition (type 0x27) found!\n", argv[2]); usage(); } argc -= 2; argv += 2; bootsector_special = 0; } // Check for multiboot option if (argc >= 2 && strcmp(argv[1], "-m") == 0) { if (argc < 3) usage(); do_multiboot(argv[2]); } // Read files if (argc < 2) usage(); // Read boot sector if (bootsector_special) { f = fopencheck(argv[1]); n = fread(buf, 1, 4096, f); if (n > 510) { fprintf(stderr, "%s: boot block too large: %s%u bytes (max 510)\n", argv[1], (n == 4096 ? ">= " : ""), (unsigned) n); usage(); } fclose(f); // Append signature and write modified boot sector memset(buf + n, 0, 510 - n); buf[510] = 0x55; buf[511] = 0xAA; diskwrite(buf, 512); nsectors = 1; argc--; argv++; } else nsectors = 0; // Read any succeeding files, then write them out memset(zerobuf, 0, 512); for (i = 1; i < argc; i++) { size_t pos; char *str; unsigned long skipto_sector; // An argument like "@X" means "skip to sector X". if (argv[i][0] == '@' && isdigit(argv[i][1]) && ((skipto_sector = strtoul(argv[i] + 1, &str, 0)), *str == 0)) { if (nsectors > skipto_sector) { fprintf(stderr, "mkbootdisk: can't skip to sector %u, already at sector %u\n", (unsigned) skipto_sector, (unsigned) nsectors); usage(); } while (nsectors < skipto_sector) { diskwrite(zerobuf, 512); nsectors++; } continue; } // Otherwise, read the file. f = fopencheck(argv[i]); pos = 0; while ((n = fread(buf, 1, 4096, f)) > 0) { diskwrite(buf, n); pos += n; } if (pos % 512 != 0) { diskwrite(zerobuf, 512 - (pos % 512)); pos += 512 - (pos % 512); } nsectors += pos / 512; fclose(f); } // Fill out to 1024 sectors with 0 blocks while (nsectors < 1024) { diskwrite(zerobuf, 512); nsectors++; } return 0; }
void bufinsert(Buffer *b, uint q0, Rune *s, uint n) { uint i, m, t, off; if(q0 > b->nc) panic("internal error: bufinsert"); while(n > 0){ setcache(b, q0); off = q0-b->cq; if(b->cnc+n <= Maxblock){ /* Everything fits in one block. */ t = b->cnc+n; m = n; if(b->bl == nil){ /* allocate */ if(b->cnc != 0) panic("internal error: bufinsert1 cnc!=0"); addblock(b, 0, t); b->cbi = 0; } sizecache(b, t); runemove(b->c+off+m, b->c+off, b->cnc-off); runemove(b->c+off, s, m); b->cnc = t; goto Tail; } /* * We must make a new block. If q0 is at * the very beginning or end of this block, * just make a new block and fill it. */ if(q0==b->cq || q0==b->cq+b->cnc){ if(b->cdirty) flush(b); m = min(n, Maxblock); if(b->bl == nil){ /* allocate */ if(b->cnc != 0) panic("internal error: bufinsert2 cnc!=0"); i = 0; }else{ i = b->cbi; if(q0 > b->cq) i++; } addblock(b, i, m); sizecache(b, m); runemove(b->c, s, m); b->cq = q0; b->cbi = i; b->cnc = m; goto Tail; } /* * Split the block; cut off the right side and * let go of it. */ m = b->cnc-off; if(m > 0){ i = b->cbi+1; addblock(b, i, m); diskwrite(disk, &b->bl[i], b->c+off, m); b->cnc -= m; } /* * Now at end of block. Take as much input * as possible and tack it on end of block. */ m = min(n, Maxblock-b->cnc); sizecache(b, b->cnc+m); runemove(b->c+b->cnc, s, m); b->cnc += m; Tail: b->nc += m; q0 += m; s += m; n -= m; b->cdirty = TRUE; } }