ast@cs.vu.nl (Andy Tanenbaum) (10/06/88)
: This is a shar archive. Extract with sh, not csh. : This archive ends with exit, so do not worry about trailing junk. : --------------------------- cut here -------------------------- PATH=/bin:/usr/bin:/usr/ucb echo Extracting 'build.c' sed 's/^X//' > 'build.c' << '+ END-OF-FILE ''build.c' X/* This program takes the previously compiled and linked pieces of the X * operating system, and puts them together to build a boot diskette. X * The files are read and put on the boot diskette in this order: X * X * bootblok: the diskette boot program X * kernel: the operating system kernel X * mm: the memory manager X * fs: the file system X * init: the system initializer X * fsck: the file system checker X * X * The bootblok file goes in sector 0 of the boot diskette. The operating system X * begins directly after it. The kernel, mm, fs, init, and fsck are each X * padded out to a multiple of 16 bytes, and then concatenated into a X * single file beginning 512 bytes into the file. The first byte of sector 1 X * contains executable code for the kernel. There is no header present. X * X * After the boot image has been built, build goes back and makes several X * patches to the image file or diskette: X * X * 1. The last 4 words of the boot block are set as follows: X * Word at 504: Number of sectors to load X * Word at 506: DS value for running fsck X * Word at 508: PC value for starting fsck X * Word at 510: CS value for running fsck X * X * 2. Build writes a table into the first 8 words of the kernel's X * data space. It has 4 entries, the cs and ds values for each X * program. The kernel needs this information to run mm, fs, and X * init. Build also writes the kernel's DS value into address 4 X * of the kernel's TEXT segment, so the kernel can set itself up. X * X * 3. The origin and size of the init program are patched into bytes 4-9 X * of the file system data space. The file system needs this X * information, and expects to find it here. X * X * Build is called by: X * X * build bootblok kernel mm fs init fsck image X * X * to get the resulting image onto the file "image". X */ X X X#define PROGRAMS 5 /* kernel + mm + fs + init + fsck = 5 */ X#define PROG_ORG 1536 /* where does kernel begin in abs mem */ X#define DS_OFFSET 4L /* position of DS written in kernel text seg */ X#define SECTOR_SIZE 512 /* size of buf */ X#define READ_UNIT 512 /* how big a chunk to read in */ X#define KERNEL_D_MAGIC 0x526F /* identifies kernel data space */ X#define FS_D_MAGIC 0xDADA /* identifies fs data space */ X#define CLICK_SHIFT 4 X#define KERN 0 X#define MM 1 X#define FS 2 X#define INIT 3 X#define FSCK 4 X X/* Information about the file header. */ X#define HEADER1 32 /* short form header size */ X#define HEADER2 48 /* long form header size */ X#define SEP_POS 1 /* tells where sep I & D bit is */ X#define HDR_LEN 2 /* tells where header length is */ X#define TEXT_POS 0 /* where is text size in header */ X#define DATA_POS 1 /* where is data size in header */ X#define BSS_POS 2 /* where is bss size in header */ X#define SEP_ID_BIT 0x20 /* bit that tells if file is separate I & D */ X X#ifdef MSDOS X# define BREAD 4 /* value 0 means ASCII read */ X#else X# define BREAD 0 X#endif X Xint image; /* file descriptor used for output file */ Xint cur_sector; /* which 512-byte sector to be written next */ Xint buf_bytes; /* # bytes in buf at present */ Xchar buf[SECTOR_SIZE]; /* buffer for output file */ Xchar zero[SECTOR_SIZE]; /* zeros, for writing bss segment */ X Xlong cum_size; /* Size of kernel+mm+fs+init */ Xlong all_size; /* Size of all 5 programs */ X Xstruct sizes { X unsigned text_size; /* size in bytes */ X unsigned data_size; /* size in bytes */ X unsigned bss_size; /* size in bytes */ X int sep_id; /* 1 if separate, 0 if not */ X} sizes[PROGRAMS]; X Xchar *name[] = {"\nkernel", "mm ", "fs ", "init ", "fsck "}; X Xmain(argc, argv) Xint argc; Xchar *argv[]; X{ X/* Copy the boot block and the 5 programs to the output. */ X X int i; X X if (argc != PROGRAMS+3) pexit("seven file names expected. ", ""); X X IOinit(); /* check for DMAoverrun (DOS) */ X create_image(argv[7]); /* create the output file */ X X /* Go get the boot block and copy it to the output file or diskette. */ X copy1(argv[1]); X X /* Copy the 5 programs to the output file or diskette. */ X for (i = 0; i < PROGRAMS; i++) copy2(i, argv[i+2]); X flush(); X printf(" ----- -----\n"); X#ifdef PCIX X printf("Operating system size %29ld %5lx\n", cum_size, cum_size); X printf("\nTotal size including fsck is %ld.\n", all_size); X#else X printf("Operating system size %29D %5X\n", cum_size, cum_size); X printf("\nTotal size including fsck is %D.\n", all_size); X#endif X X /* Make the three patches to the output file or diskette. */ X patch1(all_size); X patch2(); X patch3(); X exit(0); X} X X X Xcopy1(file_name) Xchar *file_name; X{ X/* Copy the specified file to the output. The file has no header. All the X * bytes are copied, until end-of-file is hit. X */ X X int fd, bytes_read; X char inbuf[READ_UNIT]; X X if ( (fd = open(file_name, BREAD)) < 0) pexit("can't open ",file_name); X X do { X bytes_read = read(fd, inbuf, READ_UNIT); X if (bytes_read < 0) pexit("read error on file ", file_name); X if (bytes_read > 0) wr_out(inbuf, bytes_read); X } while (bytes_read > 0); X flush(); X close(fd); X} X X Xcopy2(num, file_name) Xint num; /* which program is this (0 - 4) */ Xchar *file_name; /* file to open */ X{ X/* Open and read a file, copying it to output. First read the header, X * to get the text, data, and bss sizes. Also see if it is separate I & D. X * write the text, data, and bss to output. The sum of these three pieces X * must be padded upwards to a multiple of 16, if need be. The individual X * pieces need not be multiples of 16 bytes, except for the text size when X * separate I & D is in use. The total size must be less than 64K, even X * when separate I & D space is used. X */ X X int fd, sepid, bytes_read, count; X unsigned text_bytes, data_bytes, bss_bytes, rest, filler; X long tot_bytes; X unsigned left_to_read; X char inbuf[READ_UNIT]; X X if ( (fd = open(file_name, BREAD)) < 0) pexit("can't open ", file_name); X X /* Read the header to see how big the segments are. */ X read_header(fd, &sepid, &text_bytes, &data_bytes, &bss_bytes, file_name); X X /* Pad the total size to a 16-byte multiple, if needed. */ X if (sepid && ((text_bytes % 16) != 0) ) { X pexit("separate I & D but text size not multiple of 16 bytes. File: ", X file_name); X } X tot_bytes = (long)text_bytes + data_bytes + bss_bytes; X rest = tot_bytes % 16; X filler = (rest > 0 ? 16 - rest : 0); X bss_bytes += filler; X tot_bytes += filler; X if (num < FSCK) cum_size += tot_bytes; X all_size += tot_bytes; X X /* Record the size information in the table. */ X sizes[num].text_size = text_bytes; X sizes[num].data_size = data_bytes; X sizes[num].bss_size = bss_bytes; X sizes[num].sep_id = sepid; X X /* Print a message giving the program name and size, except for fsck. */ X if (num < FSCK) { X printf("%s text=%5u data=%5u bss=%5u tot=%5D hex=%5X %s\n", X name[num], text_bytes, data_bytes, bss_bytes, tot_bytes, X tot_bytes, (sizes[num].sep_id ? "Separate I & D" : "")); X } X X X /* Read in the text and data segments, and copy them to output. */ X left_to_read = text_bytes + data_bytes; X while (left_to_read > 0) { X count = (left_to_read < READ_UNIT ? left_to_read : READ_UNIT); X bytes_read = read(fd, inbuf, count); X if (bytes_read < 0) pexit("read error on file ", file_name); X if (bytes_read > 0) wr_out(inbuf, bytes_read); X left_to_read -= count; X } X X /* Write the bss to output. */ X while (bss_bytes > 0) { X count = (bss_bytes < SECTOR_SIZE ? bss_bytes : SECTOR_SIZE); X wr_out(zero, count); X bss_bytes -= count; X } X close(fd); X} X X Xread_header(fd, sepid, text_bytes, data_bytes, bss_bytes, file_name) Xint fd, *sepid; Xunsigned *text_bytes, *data_bytes, *bss_bytes; Xchar *file_name; X{ X/* Read the header and check the magic number. The standard Monix header X * consists of 8 longs, as follows: X * 0: 0x04100301L (combined I & D space) or 0x04200301L (separate I & D) X * 1: 0x00000020L (stripped file) or 0x00000030L (unstripped file) X * 2: size of text segments in bytes X * 3: size of initialized data segment in bytes X * 4: size of bss in bytes X * 5: 0x00000000L X * 6: total memory allocated to program (text, data and stack, combined) X * 7: 0x00000000L X * The longs are represented low-order byte first and high-order byte last. X * The first byte of the header is always 0x01, followed by 0x03. X * The header is followed directly by the text and data segments, whose sizes X * are given in the header. X */ X X long head[12]; X unsigned short hd[4]; X int n, header_len; X X /* Read first 8 bytes of header to get header length. */ X if ((n = read(fd, hd, 8)) != 8) pexit("file header too short: ", file_name); X header_len = hd[HDR_LEN]; X if (header_len != HEADER1 && header_len != HEADER2) X pexit("bad header length. File: ", file_name); X X /* Extract separate I & D bit. */ X *sepid = hd[SEP_POS] & SEP_ID_BIT; X X /* Read the rest of the header and extract the sizes. */ X if ((n = read(fd, head, header_len - 8)) != header_len - 8) X pexit("header too short: ", file_name); X X *text_bytes = (unsigned) head[TEXT_POS]; X *data_bytes = (unsigned) head[DATA_POS]; X *bss_bytes = (unsigned) head[BSS_POS]; X} X X Xwr_out(buffer, bytes) Xchar buffer[READ_UNIT]; Xint bytes; X{ X/* Write some bytes to the output file. This procedure must avoid writes X * that are not entire 512-byte blocks, because when this program runs on X * MS-DOS, the only way it can write the raw diskette is by using the system X * calls for raw block I/O. X */ X X int room, count, count1; X register char *p, *q; X X /* Copy the data to the output buffer. */ X room = SECTOR_SIZE - buf_bytes; X count = (bytes <= room ? bytes : room); X count1 = count; X p = &buf[buf_bytes]; X q = buffer; X while (count--) *p++ = *q++; X X /* See if the buffer is full. */ X buf_bytes += count1; X if (buf_bytes == SECTOR_SIZE) { X /* Write the whole block to the disk. */ X write_block(cur_sector, buf); X clear_buf(); X } X X /* Is there any more data to copy. */ X if (count1 == bytes) return; X bytes -= count1; X buf_bytes = bytes; X p = buf; X while (bytes--) *p++ = *q++; X} X X Xflush() X{ X if (buf_bytes == 0) return; X write_block(cur_sector, buf); X clear_buf(); X} X X Xclear_buf() X{ X register char *p; X X for (p = buf; p < &buf[SECTOR_SIZE]; p++) *p = 0; X buf_bytes = 0; X cur_sector++; X} X X Xpatch1(all_size) Xlong all_size; X{ X/* Put the ip and cs values for fsck in the last two words of the boot blk. X * If fsck is sep I&D we must also provide the ds-value (addr. 506). X * Put in bootblok-offset 504 the number of sectors to load. X */ X X long fsck_org; X unsigned short ip, cs, ds, ubuf[SECTOR_SIZE/2], sectrs; X X if (cum_size % 16 != 0) pexit("MINIX is not multiple of 16 bytes", ""); X fsck_org = PROG_ORG + cum_size; /* where does fsck begin */ X ip = 0; X cs = fsck_org >> CLICK_SHIFT; X if (sizes[FSCK].sep_id) X ds = cs + (sizes[FSCK].text_size >> CLICK_SHIFT); X else X ds = cs; X X /* calc nr of sectors to load (starting at 0) */ X sectrs = (unsigned) (all_size / 512L); X X read_block(0, ubuf); /* read in boot block */ X ubuf[(SECTOR_SIZE/2) - 4] = sectrs + 1; X ubuf[(SECTOR_SIZE/2) - 3] = ds; X ubuf[(SECTOR_SIZE/2) - 2] = ip; X ubuf[(SECTOR_SIZE/2) - 1] = cs; X write_block(0, ubuf); X} X Xpatch2() X{ X/* This program now has information about the sizes of the kernel, mm, fs, and X * init. This information is patched into the kernel as follows. The first 8 X * words of the kernel data space are reserved for a table filled in by build. X * The first 2 words are for kernel, then 2 words for mm, then 2 for fs, and X * finally 2 for init. The first word of each set is the text size in clicks; X * the second is the data+bss size in clicks. If separate I & D is NOT in X * use, the text size is 0, i.e., the whole thing is data. X * X * In addition, the DS value the kernel is to use is computed here, and loaded X * at location 4 in the kernel's text space. It must go in text space because X * when the kernel starts up, only CS is correct. It does not know DS, so it X * can't load DS from data space, but it can load DS from text space. X */ X X int i, j; X unsigned short t, d, b, text_clicks, data_clicks, ds; X long data_offset; X X /* See if the magic number is where it should be in the kernel. */ X data_offset = 512L + (long)sizes[KERN].text_size; /* start of kernel data */ X i = (get_byte(data_offset+1L) << 8) + get_byte(data_offset); X if (i != KERNEL_D_MAGIC) { X pexit("kernel data space: no magic #",""); X } X X for (i = 0; i < PROGRAMS - 1; i++) { X t = sizes[i].text_size; X d = sizes[i].data_size; X b = sizes[i].bss_size; X if (sizes[i].sep_id) { X text_clicks = t >> CLICK_SHIFT; X data_clicks = ((unsigned long)d + b) >> CLICK_SHIFT; X } else { X text_clicks = 0; X data_clicks = ((unsigned long)t + d + b) >> CLICK_SHIFT; X } X put_byte(data_offset + 4*i + 0L, (text_clicks>>0) & 0377); X put_byte(data_offset + 4*i + 1L, (text_clicks>>8) & 0377); X put_byte(data_offset + 4*i + 2L, (data_clicks>>0) & 0377); X put_byte(data_offset + 4*i + 3L, (data_clicks>>8) & 0377); X } X X /* Now write the DS value into word 4 of the kernel text space. */ X if (sizes[KERN].sep_id == 0) X ds = PROG_ORG >> CLICK_SHIFT; /* combined I & D space */ X else X ds = (PROG_ORG + sizes[KERN].text_size) >> CLICK_SHIFT; /* separate */ X put_byte(512L + DS_OFFSET, ds & 0377); X put_byte(512L + DS_OFFSET + 1L, (ds>>8) & 0377); X} X X Xpatch3() X{ X/* Write the origin and text and data sizes of the init program in FS's data X * space. The file system expects to find these 3 words there. X */ X X unsigned short init_text_size, init_data_size, init_buf[SECTOR_SIZE/2], i; X unsigned short w0, w1, w2; X int b0, b1, b2, b3, b4, b5, mag; X long init_org, fs_org, fbase, mm_data; X X init_org = PROG_ORG; X init_org += (long)sizes[KERN].text_size+sizes[KERN].data_size+sizes[KERN].bss_size; X mm_data = init_org - PROG_ORG +512L; /* offset of mm in file */ X mm_data += (long) sizes[MM].text_size; X init_org += (long)sizes[MM].text_size + sizes[MM].data_size + sizes[MM].bss_size; X fs_org = init_org - PROG_ORG + 512L; /* offset of fs-text into file */ X fs_org += (long) sizes[FS].text_size; X init_org += (long)sizes[FS].text_size + sizes[FS].data_size + sizes[FS].bss_size; X init_text_size = sizes[INIT].text_size; X init_data_size = sizes[INIT].data_size + sizes[INIT].bss_size; X init_org = init_org >> CLICK_SHIFT; /* convert to clicks */ X if (sizes[INIT].sep_id == 0) { X init_data_size += init_text_size; X init_text_size = 0; X } X init_text_size = init_text_size >> CLICK_SHIFT; X init_data_size = init_data_size >> CLICK_SHIFT; X X w0 = (unsigned short) init_org; X w1 = init_text_size; X w2 = init_data_size; X b0 = w0 & 0377; X b1 = (w0 >> 8) & 0377; X b2 = w1 & 0377; X b3 = (w1 >> 8) & 0377; X b4 = w2 & 0377; X b5 = (w2 >> 8) & 0377; X X /* Check for appropriate magic numbers. */ X fbase = fs_org; X mag = (get_byte(mm_data+1L) << 8) + get_byte(mm_data+0L); X if (mag != FS_D_MAGIC) pexit("mm data space: no magic #",""); X mag = (get_byte(fbase+1L) << 8) + get_byte(fbase+0L); X if (mag != FS_D_MAGIC) pexit("fs data space: no magic #",""); X X put_byte(fbase+4L, b0); X put_byte(fbase+5L, b1); X put_byte(fbase+6L, b2); X put_byte(fbase+7L, b3); X put_byte(fbase+8L ,b4); X put_byte(fbase+9L, b5); X} X X Xint get_byte(offset) Xlong offset; X{ X/* Fetch one byte from the output file. */ X X char buff[SECTOR_SIZE]; X X read_block( (unsigned) (offset / SECTOR_SIZE), buff); X return(buff[(unsigned) (offset % SECTOR_SIZE)] & 0377); X} X Xput_byte(offset, byte_value) Xlong offset; Xint byte_value; X{ X/* Write one byte into the output file. This is not very efficient, but X * since it is only called to write a few words it is just simpler. X */ X X char buff[SECTOR_SIZE]; X X read_block( (unsigned) (offset/SECTOR_SIZE), buff); X buff[(unsigned) (offset % SECTOR_SIZE)] = byte_value; X write_block( (unsigned)(offset/SECTOR_SIZE), buff); X} X X Xpexit(s1, s2) Xchar *s1, *s2; X{ X printf("Build: %s%s\n", s1, s2); X exit(1); X} X X X X/*=========================================================================== X * The following code is only used in the UNIX version of this program. X *===========================================================================*/ X#ifndef MSDOS Xcreate_image(f) Xchar *f; X{ X/* Create the output file. */ X image = creat(f, 0666); X close(image); X image = open(f, 2); X} X Xread_block(blk, buff) Xint blk; Xchar buff[SECTOR_SIZE]; X{ X lseek(image, (long)SECTOR_SIZE * (long) blk, 0); X if (read(image, buff, SECTOR_SIZE) != SECTOR_SIZE) pexit("block read error", ""); X} X Xwrite_block(blk, buff) Xint blk; Xchar buff[SECTOR_SIZE]; X{ X lseek(image, (long)SECTOR_SIZE * (long) blk, 0); X if (write(image, buff, SECTOR_SIZE) != SECTOR_SIZE) pexit("block write error", ""); X} X XIOinit() {} /* dummy */ X X#else /*MSDOS*/ X/*=========================================================================== X * This is the raw diskette I/O for MSDOS. It uses diskio.asm or biosio.asm X *==========================================================================*/ X X#define MAX_RETRIES 5 X Xchar *buff; Xchar buff1[SECTOR_SIZE]; Xchar buff2[SECTOR_SIZE]; Xint drive; X XIOinit() /* check if no DMAoverrun & assign the buffer */ X{ X if (DMAoverrun(buff1)) X buff = buff2; X else X buff = buff1; X} X X Xread_block (blocknr,user) Xint blocknr; Xchar user[SECTOR_SIZE]; X{ X /* read the requested MINIX-block in core */ X int retries,err,i; X char *p; X X retries = MAX_RETRIES; X do X err = absread (drive, blocknr, buff); X while (err && --retries); X X if (!retries) X dexit ("reading",drive,blocknr,err); X X p=buff; i=SECTOR_SIZE; X while (i--) *(user++) = *(p++); X} X X X Xwrite_block (blocknr,user) Xint blocknr; Xchar user[SECTOR_SIZE]; X{ X /* write the requested MINIX-block to disk */ X int retries,err,i; X char *p; X X p=buff; i=SECTOR_SIZE; X while (i--) *(p++) = *(user++); X X retries = MAX_RETRIES; X do X err = abswrite (drive, blocknr, buff); X while (err && --retries); X X if (!retries) X dexit ("writing",drive,blocknr,err); X} X X X Xdexit (s,drive,sectnum,err) Xint sectnum, err,drive; Xchar *s; X{ extern char *derrtab[]; X printf ("Error %s drive %c, sector: %d, code: %d, %s\n", X s, drive+'A',sectnum, err, derrtab[err] ); X exit (2); X} X X Xcreate_image (s) Xchar *s; X{ X char kbstr[10]; X if (s[1] != ':') pexit ("wrong drive name (dos): ",s); X drive = (s[0] & ~32) - 'A'; X if (drive<0 || drive>32) pexit ("no such drive: ",s); X printf("Put a blank, formatted diskette in drive %s\nHit return when ready",s); X gets (kbstr,10); X puts(""); X} X Xchar *derrtab[14] = { X "no error", X "disk is read-only", X "unknown unit", X "device not ready", X "bad command", X "data error", X "internal error: bad request structure length", X "seek error", X "unknown media type", X "sector not found", X "printer out of paper (??)", X "write fault", X "read error", X "general error" X}; X X X#endif /*MSDOS*/ + END-OF-FILE build.c chmod 'u=rw,g=r,o=r' 'build.c' set `wc -c 'build.c'` count=$1 case $count in 19714) :;; *) echo 'Bad character count in ''build.c' >&2 echo 'Count should be 19714' >&2 esac echo Extracting 'init.c' sed 's/^X//' > 'init.c' << '+ END-OF-FILE ''init.c' X/* This process is the father (mother) of all MINIX user processes. When X * MINIX comes up, this is process 2. It executes the /etc/rc shell file and X * then reads the /etc/ttys file to find out which terminals need a login X * process. The ttys file consists of 3-character lines as follows: X * abc X * where X * a = 0 (line disabled = no shell), 1 (enabled = shell started) X * b = a-r defines UART paramers (baud, bits, parity), 0 for console X * c = line number X * X * The letters a-r correspond to the 18 entries of the uart table below. X * For example, 'a' is 110 baud, 8 bits, no parity; 'b' is 300 baud, 8 bits, X * no parity; 'j' is 2400 baud, 7 bits, even parity; etc. X * X * If the file /usr/adm/wtmp exists and is writable, init (with help from X * login) maintains login accounting used by who(1). X */ X X#include "../h/signal.h" X#include "../h/sgtty.h" X X#define PIDSLOTS 10 X#define NPARAMSETS 18 X#define STACKSIZE 256 X#define DIGIT 8 X#define OFFSET 5 X#define SHELL 1 X#define NOPARAMS -100 X#define WTMPSIZE 8 X Xextern long time(); Xextern long lseek(); X Xstruct uart { X int baud; X int flags; X} uart[NPARAMSETS] = { X B110, BITS8, /* 110 baud, 8 bits, no parity */ X B300, BITS8, /* 300 baud, 8 bits, no parity */ X B1200, BITS8, /* 1200 baud, 8 bits, no parity */ X B2400, BITS8, /* 2400 baud, 8 bits, no parity */ X B4800, BITS8, /* 4800 baud, 8 bits, no parity */ X B9600, BITS8, /* 9600 baud, 8 bits, no parity */ X X B110, BITS7 | EVENP, /* 110 baud, 7 bits, even parity */ X B300, BITS7 | EVENP, /* 300 baud, 7 bits, even parity */ X B1200, BITS7 | EVENP, /* 1200 baud, 7 bits, even parity */ X B2400, BITS7 | EVENP, /* 2400 baud, 7 bits, even parity */ X B4800, BITS7 | EVENP, /* 4800 baud, 7 bits, even parity */ X B9600, BITS7 | EVENP, /* 9600 baud, 7 bits, even parity */ X X B110, BITS7 | ODDP, /* 110 baud, 7 bits, odd parity */ X B300, BITS7 | ODDP, /* 300 baud, 7 bits, odd parity */ X B1200, BITS7 | ODDP, /* 1200 baud, 7 bits, odd parity */ X B2400, BITS7 | ODDP, /* 2400 baud, 7 bits, odd parity */ X B4800, BITS7 | ODDP, /* 4800 baud, 7 bits, odd parity */ X B9600, BITS7 | ODDP /* 9600 baud, 7 bits, odd parity */ X}; X Xchar wtmpfile[] = {"/usr/adm/wtmp"}; Xchar name[] = {"/dev/tty?"}; /* terminal names */ Xint pid[PIDSLOTS]; /* pids of init's own children */ Xint save_params[PIDSLOTS]; Xint pidct; Xextern int errno; X Xchar stack[STACKSIZE]; Xchar *stackpt = &stack[STACKSIZE]; Xchar **environ; /* declaration required by library routines */ X Xstruct sgttyb args; X X Xmain() X{ X char line[10]; /* /etc/ttys lines should be 3 chars */ X int rc, tty, k, status, ttynr, ct, i, params, shell; X X /* Carry out /etc/rc. */ X sync(); /* force buffers out onto RAM disk */ X X /* Execute the /etc/rc file. */ X if (fork()) { X /* Parent just waits. */ X wait(&k); X } else { X /* Child exec's the shell to do the work. */ X if (open("/etc/rc", 0) < 0) exit(-1); X open("/dev/tty0", 1); /* std output */ X open("/dev/tty0", 1); /* std error */ X execn("/bin/sh"); X exit(-2); /* impossible */ X } X X /* Make the /usr/adm/wtmp entry. */ X wtmp("~",""); /* log system reboot */ X X /* Read the /etc/ttys file and fork off login processes. */ X if ( (tty = open("/etc/ttys", 0)) == 0) { X /* Process /etc/ttys file. */ X while ( (ct = read(0, line, 4)) == 4) { X /* Extract and check the 3 characters on each line. */ X shell = line[0] - '0'; /* 0, 1, 2 for disabled, sh, no sh */ X params = line[1] - 'a'; /* selects UART parameters */ X ttynr = line[2] - '0'; /* line number */ X if (shell <= 0 || shell > 1) continue; X if (line[1] == '0') params = NOPARAMS; X else if (params < 0 || params > NPARAMSETS) continue; X if (ttynr < 0 || ttynr > PIDSLOTS) continue; X X save_params[ttynr] = params; X startup(ttynr, params); X } X } else { X tty = open("/dev/tty0", 1); X write(tty, "Init can't open /etc/ttys\n", 26); X while (1) ; /* just hang -- system cannot be started */ X } X close(tty); X X /* All the children have been forked off. Wait for someone to terminate. X * Note that it might be a child, in which case a new login process must be X * spawned off, or it might be somebody's orphan, in which case ignore it. X * First ignore all signals. X */ X for (i = 1; i <= NR_SIGS; i++) signal(i, SIG_IGN); X X while (1) { X sync(); X k = wait(&status); X pidct--; X X /* Search to see which line terminated. */ X for (i = 0; i < PIDSLOTS; i++) { X if (pid[i] == k) { X name[DIGIT] = '0' + i; X wtmp(&name[OFFSET], ""); X startup(i, save_params[i]); X } X } X } X} X X Xstartup(linenr, params) Xint linenr, params; X{ X/* Fork off a process for the indicated line. */ X X int k, n; X X if ( (k = fork()) != 0) { X /* Parent */ X pid[linenr] = k; X pidct++; X } else { X /* Child */ X close(0); /* /etc/ttys may be open */ X name[DIGIT] = '0' + linenr; X if (open(name, 2) != 0) exit(-3); /* standard input */ X if (open(name, 2) != 1) exit(-3); /* standard output */ X if (open(name, 2) != 2) exit(-3); /* standard error */ X X X /* Set line parameters. */ X if (params != NOPARAMS) { X n = ioctl(0, TIOCGETP, &args); /* get parameters */ X args.sg_ispeed = uart[params].baud; X args.sg_ospeed = uart[params].baud; X args.sg_flags = CRMOD | XTABS | ECHO | uart[params].flags; X n = ioctl(0, TIOCSETP, &args); X } X X /* Try to exec login, or in an emergency, exec the shell. */ X execn("/usr/bin/login"); X execn("/bin/login"); X execn("/bin/sh"); /* last resort, if mount of /usr failed */ X execn("/usr/bin/sh"); /* last resort, if mount of /usr failed */ X return; /* impossible */ X } X} X Xwtmp(tty, name) X{ X/* Make an entry in /usr/adm/wtmp. */ X X int i, fd; X long t, time(); X char ttybuff[WTMPSIZE], namebuff[WTMPSIZE]; X X fd = open(wtmpfile, 2); X if (fd < 0) return; /* if wtmp does not exist, no accounting */ X i =lseek(fd, 0L, 2); /* append to file */ X X for (i = 0; i < WTMPSIZE; i++) { X ttybuff[i] = 0; X namebuff[i] = 0; X } X strncpy(ttybuff, tty, 8); X strncpy(namebuff, name, 8); X time(&t); X write(fd, ttybuff, WTMPSIZE); X write(fd, namebuff, WTMPSIZE); X write(fd, &t, sizeof(t)); X close(fd); X} + END-OF-FILE init.c chmod 'u=rw,g=r,o=r' 'init.c' set `wc -c 'init.c'` count=$1 case $count in 6082) :;; *) echo 'Bad character count in ''init.c' >&2 echo 'Count should be 6082' >&2 esac echo Extracting 'mkfs.c' sed 's/^X//' > 'mkfs.c' << '+ END-OF-FILE ''mkfs.c' X/* mkfs - make the MINIX filesystem X * Andy Tanenbaum & Paul Ogilvie, Jun 1986 X * X * This program was initially designed to build a filesystem X * with blocksize = zonesize. During the course of time the X * program is being converted to handle zone_size > blocksize X * but this isn't complete yet. Where routines can handle the X * situation this is mentioned in the comment. X * X * To compile this program for MS-DOS, use: cc -DDOS mkfs.c diskio.asm X * To compile this program for UNIX, use: cc -DUNIX mkfs.c X * To compile this program for MINIX, use: cc mkfs.c X */ X X X#include <minix/const.h> X#include <minix/type.h> X#include <fs/const.h> X#undef EXTERN X#define EXTERN /* get rid of EXTERN by making it null */ X#include <fs/type.h> X#include <fs/super.h> X X#ifdef DOS X#include "/lib/c86/stdio.h" X#else X#include <stdio.h> X#include <sys/types.h> X#include <sys/stat.h> X#endif X X X#ifndef DOS X#ifndef UNIX X#undef printf /* printf is a macro for printk */ X#define UNIX X#endif X#endif X X X X#define INODE_MAP 2 X#define MAX_TOKENS 10 X#define LINE_LEN 200 X#define BIN 2 X#define BINGRP 2 X#define BIT_MAP_SHIFT 13 X#define N_BLOCKS 32000 /* must be multiple of 8 */ X X#ifdef DOS X# define BREAD 4 X# define BWRITE 5 X#else X# define BREAD 0 X# define BWRITE 1 X#endif X X Xint next_zone, next_inode, zone_size, zone_shift=0, zoff, nrblocks,inode_offset, X nrinodes, lct=1, disk, fd, print=0, file=0, override=0, simple=0, dflag; Xint donttest; /* skip test if it fits on medium */ X Xlong current_time, bin_time; Xchar zero[BLOCK_SIZE], *lastp; Xchar umap[(N_BLOCKS+8)/8]; /* bit map tells if block read yet */ Xint zone_map = 3; /* where is zone map? (depends on # inodes) */ X XFILE *proto; Xlong lseek(); Xchar gwarning[] = {65,46,83,46,84,97,110,101,110,98,97,117,109,10}; X X X X/*================================================================ X * mkfs - make filesystem X *===============================================================*/ X Xmain(argc, argv) Xint argc; Xchar *argv[]; X{ X int i, blocks, zones, inodes, mode, usrid, grpid, badusage = 0; X char *token[MAX_TOKENS], line[LINE_LEN]; X FILE *fopen(); X long time(), ls; X struct stat statbuf; X X X /* Get two times, the current time and the mod time of the binary of X * mkfs itself. When the -d flag is used, the later time is put into X * the i_modtimes of all the files. This feature is useful when producing X * a set of file systems, and one wants all the times to be identical. X * First you set the time of the mkfs binary to what you want, then go. X */ X current_time = time(0L); /* time mkfs is being run */ X stat(argv[0], &statbuf); X bin_time = statbuf.st_mtime; /* time when mkfs binary was last modified */ X X /* process parameters and switches */ X if (argc != 3 && argc != 4) badusage = 1; X if (stat(argv[argc - 1], &statbuf) == 0) { X if ( (statbuf.st_mode&S_IFMT) != S_IFREG) badusage = 1; X } X if (badusage) { X write(2, "Usage: mkfs [-ldt] special proto\n", 33); X exit(1); X } X while (--argc) { X switch (argv[argc][0]) { X case '-': while (*++argv[argc]) X switch (*argv[argc]) { X case 'l': case 'L': X print=1; break; X case 'o': case 'O': X override=1; break; X case 'd': case 'D': X current_time = bin_time; dflag=1; break; X case 't': case 'T': X donttest=1; break; X default: X printf ("Bad switch %c, ignored.\n",*argv[argc]); X } X break; X X default : X X /* process proto & special */ X proto = fopen(argv[argc], "r" ); X if (proto != NULL) { X /* Prototype file is readable. */ X getline(line, token); /* skip boot block info. */ X X /* Read the line with the block and inode counts. */ X getline(line, token); X blocks = atoi(token[0]); X if (blocks > N_BLOCKS) pexit("Block count too large"); X inodes = atoi(token[1]); X X /* Process mode line for root directory. */ X getline(line, token); X mode = mode_con(token[0]); X usrid = atoi(token[1]); X grpid = atoi(token[2]); X X } else { X X /* Maybe the prototype file is just a size. Check for that. */ X blocks = atoi(argv[argc]); X if (blocks < 4) pexit("Can't open prototype file"); X X /* Ok, make simple file system of given size, using defaults. */ X inodes = (blocks/3) + 8; /* default is 3 blocks/file */ X mode = 040777; X usrid = BIN; X grpid = BINGRP; X simple = 1; X } X X /* open special */ X argc--; X special(argv[argc]); X X nrblocks = blocks; X nrinodes = inodes; X } /* end switch */ X } /* end while */ X X X X#ifdef UNIX X if (!donttest) { X static short testb[BLOCK_SIZE/sizeof(short)]; X X /* Try writing the last block of partition or diskette. */ X ls = lseek(fd, ((long)blocks - 1L) * BLOCK_SIZE, 0); X testb[0] = 0x3245; X testb[1] = 0x11FF; X if (write(fd, testb, BLOCK_SIZE) != BLOCK_SIZE) X pexit("File system is too big for minor device"); X lseek(fd, ((long)blocks - 1L) * BLOCK_SIZE, 0); X testb[0] = 0; X testb[1] = 0; X i = read(fd, testb, BLOCK_SIZE); X if (i != BLOCK_SIZE || testb[0] != 0x3245 || testb[1] != 0x11FF) X pexit("File system is too big for minor device"); X lseek(fd, ((long)blocks - 1L) * BLOCK_SIZE, 0); X testb[0] = 0; X testb[1] = 0; X if (write(fd, testb, BLOCK_SIZE) != BLOCK_SIZE) X pexit("File system is too big for minor device"); X lseek(fd, 0L, 0); X } X#endif X X /* make the file-system */ X X cache_init(); X put_block (0, zero); /* Write a null boot block. */ X X zone_shift = 0; /* for future use */ X zones = blocks >> zone_shift; X X super(zones, inodes); X X i = alloc_inode(mode, usrid, grpid); X rootdir(i); X if (simple == 0) eat_dir(i); X X if (print) print_fs(); X flush(); X exit (0); X X X} /* end main */ X X X X X/*================================================================ X * super - construct a superblock X *===============================================================*/ X Xsuper(zones, inodes) Xint zones, inodes; X{ X X unsigned int i, inodeblks, initblks, initzones, nrzones, bs; X unsigned int map_size, bit_map_len, b_needed, b_allocated, residual; X long zo; X struct super_block *sup; X char buf[BLOCK_SIZE], *cp; X X sup= (struct super_block *) buf; X X bs = 1 << BIT_MAP_SHIFT; X sup->s_ninodes = inodes; X sup->s_nzones = zones; X sup->s_imap_blocks = (inodes + bs)/bs; X sup->s_zmap_blocks = (zones + bs - 1)/bs; X inode_offset = sup->s_imap_blocks + sup->s_zmap_blocks + 2; X inodeblks = (inodes + INODES_PER_BLOCK - 1)/INODES_PER_BLOCK; X initblks = inode_offset + inodeblks; X initzones = (initblks + (1<<zone_shift) - 1) >> zone_shift; X nrzones = nrblocks >> zone_shift; X sup->s_firstdatazone = (initblks + (1<<zone_shift)-1) >> zone_shift; X zoff = sup->s_firstdatazone - 1; X sup->s_log_zone_size = zone_shift; X sup->s_magic = SUPER_MAGIC; /* identify super blocks */ X zo = 7L + (long) NR_INDIRECTS X + (long) NR_INDIRECTS * NR_INDIRECTS; X sup->s_max_size = zo * BLOCK_SIZE; X zone_size = 1 << zone_shift; /* nr of blocks per zone */ X X for (cp = buf + sizeof(*sup); cp < &buf[BLOCK_SIZE]; cp++) X *cp=0; X put_block (1,buf); X X /* Clear maps and inodes. */ X for (i = 2; i < initblks; i++) X put_block (i, zero); X X next_zone = sup->s_firstdatazone; X next_inode = 1; X X /* Mark all bits beyond the end of the legal inodes and zones as allocated. X * Unfortunately, the coding the bit maps is inconsistent. The rules are: X * For inodes: Every i-node occupies a bit map slot, even i-node 0 X * The first i-node on the disk is i-node 1, not 0 X * For zones: Zone map bit 0 is for the last i-node block on disk X * The first zone available goes with bit 1 in the map X * X * Thus for i-nodes, every i-node, starting at 0 occupies a bit map slot, X * but for zones, only those starting with the final i-node block occupy X * bit slots. This is inconsistent. In retrospect it would might have been X * simpler to have bit 0 of the zone map be zone 0 on the disk. Although X * this would have increased the zone bit map by a few dozen bits, it would X * have prevented a number of bugs in the early days. This is an example of X * what happens when one ignores the maxim: First make it work, then make X * it optimal. For both maps, 0 = available, 1 = in use. X */ X X /* Mark bits beyond end of inodes as allocated. (Fails if >8192 inodes). */ X map_size = 1 << BIT_MAP_SHIFT; X bit_map_len = nrinodes + 1; /* # bits needed in map */ X residual = bit_map_len % (8 * BLOCK_SIZE); X if (residual == 0) residual = 8 * BLOCK_SIZE; X b_needed = (bit_map_len + map_size - 1 ) >> BIT_MAP_SHIFT; X zone_map += b_needed - 1; /* if imap > 1, adjust start of zone map */ X insert_bit(INODE_MAP + b_needed - 1, residual, 8 * BLOCK_SIZE - residual); X X bit_map_len = nrzones - initzones + 1; /* # bits needed in map */ X residual = bit_map_len % (8 * BLOCK_SIZE); X if (residual == 0) residual = 8 * BLOCK_SIZE; X b_needed = (bit_map_len + map_size - 1 ) >> BIT_MAP_SHIFT; X b_allocated = (nrzones + map_size - 1 ) >> BIT_MAP_SHIFT; X insert_bit(zone_map + b_needed - 1, residual, 8 * BLOCK_SIZE - residual); X if (b_needed != b_allocated) { X insert_bit(zone_map + b_allocated - 1, 0, map_size); X } X X insert_bit(zone_map, 0, 1); /* bit zero must always be allocated */ X insert_bit(INODE_MAP, 0, 1); /* inode zero not used but must be allocated */ X X} X X X X X X/*================================================================ X * rootdir - install the root directory X *===============================================================*/ X Xrootdir(inode) Xint inode; X{ X int z; X X z = alloc_zone(); X add_zone (inode, z, 32L, current_time); X enter_dir(inode, ".", inode); X enter_dir(inode, "..", inode); X incr_link(inode); X incr_link(inode); X} X X X X X X/*================================================================ X * eat_dir - recursively install directory X *===============================================================*/ X Xeat_dir(parent) Xint parent; /* parent's inode nr */ X{ X /*Read prototype lines and set up directory. Recurse if need be. */ X char *token[MAX_TOKENS], *p; X char line[LINE_LEN]; X int mode, n, usrid, grpid, z, maj, min, f; X long size; X X while (1) { X getline(line, token); X p = token[0]; X if (*p == '$') return; X p = token[1]; X mode = mode_con(p); X usrid = atoi(token[2]); X grpid = atoi(token[3]); X if (grpid & 0200) write(2, gwarning, 14); X n = alloc_inode(mode, usrid, grpid); X X /* Enter name in directory and update directory's size. */ X enter_dir(parent, token[0], n); X incr_size(parent, 16L); X X /* Check to see if file is directory or special. */ X incr_link(n); X if (*p == 'd') { X /* This is a directory. */ X z = alloc_zone(); /* zone for new directory */ X add_zone(n, z, 32L, current_time); X enter_dir(n, ".", n); X enter_dir(n, "..", parent); X incr_link(parent); X incr_link(n); X eat_dir(n); X } else if (*p == 'b' || *p == 'c') { X /* Special file. */ X maj = atoi(token[4]); X min = atoi(token[5]); X size = 0; X if (token[6]) X size = atoi(token[6]); X size = BLOCK_SIZE * size; X add_zone(n, (maj<<8)|min, size, current_time); X } else { X /* Regular file. Go read it. */ X if ((f=open(token[4],BREAD)) < 0) { X write(2, "Can't open file ", 16); X write(2, token[4], strlen(token[4]) ); X write(2, "\n", 1); X } else X eat_file(n, f); X } X } X X} X X X X/*================================================================ X * eat_file - copy file to MINIX X *===============================================================*/ X X/* zonesize >= blocksize */ Xeat_file(inode, f) Xint inode, f; X{ X int z, ct, i, j, k; X char buf[BLOCK_SIZE]; X long timeval; X extern long file_time(); X X do { X for (i=0, j=0; i < zone_size; i++, j+=ct ) { X for (k = 0; k < BLOCK_SIZE; k++) buf[k] = 0; X if ((ct=read(f,buf, BLOCK_SIZE)) > 0) { X if (i==0) z = alloc_zone(); X put_block ( (z << zone_shift) + i, buf); X } X } X timeval = (dflag ? current_time : file_time(f) ); X if (ct) add_zone (inode, z, (long) j, timeval ); X } while (ct == BLOCK_SIZE); X close(f); X} X X X X X X/*================================================================ X * directory & inode management assist group X *===============================================================*/ X Xenter_dir(parent, name, child) Xint parent, child; /* inode nums */ Xchar *name; X{ X /* enter child in parent directory */ X /* works for dir > 1 block and zone > block */ X int i, j, k, l, b, z, off; X char *p1, *p2; X struct { X short inumb; X char name[14]; X } dir_entry[NR_DIR_ENTRIES]; X X d_inode ino[INODES_PER_BLOCK]; X X X b = ((parent-1) / INODES_PER_BLOCK) + inode_offset; X off = (parent-1) % INODES_PER_BLOCK ; X get_block ( b, ino); X X for ( k=0; k<NR_DZONE_NUM; k++ ) { X z = ino[off].i_zone[k]; X if (z == 0) { X z = alloc_zone(); X ino[off].i_zone[k] = z; X } X for ( l=0; l<zone_size; l++) { X get_block( (z << zone_shift) + l, dir_entry); X for ( i=0; i < NR_DIR_ENTRIES; i++) { X if (dir_entry[i].inumb == 0) { X dir_entry[i].inumb = child; X p1 = name; X p2 = dir_entry[i].name; X j = 14; X while (j--) { X *p2++ = *p1; X if (*p1 != 0) p1++; X } X put_block( (z << zone_shift) + l, dir_entry); X put_block(b, ino); X return; X } X } X } X } X X printf("Directory-inode %d beyond direct blocks. Could not enter %s\n", X parent,name); X pexit("Halt"); X} X X X X Xadd_zone(n, z, bytes, cur_time) Xint n, z; Xlong bytes, cur_time; X{ X /* add zone z to inode n. The file has grown by 'bytes' bytes. */ X X int b, off, indir, i; X zone_nr blk[NR_INDIRECTS]; X d_inode *p; X d_inode inode[INODES_PER_BLOCK]; X X b = ((n-1)/INODES_PER_BLOCK) + inode_offset; X off = (n-1) % INODES_PER_BLOCK; X get_block(b, inode); X p = &inode[off]; X p->i_size += bytes; X p->i_modtime = cur_time; X for (i=0; i < NR_DZONE_NUM; i++) X if (p->i_zone[i] == 0) { X p->i_zone[i] = z; X put_block(b, inode); X return; X } X put_block(b, inode); X X /* File has grown beyond a small file. */ X if (p->i_zone[NR_DZONE_NUM] == 0) p->i_zone[NR_DZONE_NUM] = alloc_zone(); X indir = p->i_zone[NR_DZONE_NUM]; X put_block(b, inode); X b = indir << zone_shift; X get_block(b, blk); X for (i = 0; i < NR_INDIRECTS; i++) X if (blk[i] == 0) { X blk[i] = (zone_nr) z; X put_block(b, blk); X return; X } X pexit("File has grown beyond single indirect"); X} X X X X Xincr_link(n) Xint n; X{ X /* increment the link count to inode n */ X int b, off; X d_inode inode[INODES_PER_BLOCK]; X X b = ((n-1)/INODES_PER_BLOCK) + inode_offset; X off = (n-1) % INODES_PER_BLOCK; X get_block(b, inode); X inode[off].i_nlinks++; X put_block(b, inode); X} X X X X Xincr_size(n,count) Xint n; Xlong count; X{ X /* increment the file-size in inode n */ X int b, off; X d_inode inode[INODES_PER_BLOCK]; X X b = ((n-1)/INODES_PER_BLOCK) + inode_offset; X off = (n-1) % INODES_PER_BLOCK; X get_block(b, inode); X inode[off].i_size += count; X put_block(b, inode); X} X X X X X/*================================================================ X * allocation assist group X *===============================================================*/ X Xint alloc_inode(mode, usrid, grpid) Xint mode, usrid, grpid; X{ X int num, b, off; X d_inode inode[INODES_PER_BLOCK]; X X num = next_inode++; X if (num >= nrinodes) pexit("File system does not have enough inodes"); X b = ((num-1) / INODES_PER_BLOCK) + inode_offset; X off = (num-1) % INODES_PER_BLOCK; X get_block(b, inode); X inode[off].i_mode = mode; X inode[off].i_uid = usrid; X inode[off].i_gid = grpid; X put_block(b, inode); X X /* Set the bit in the bit map. */ X insert_bit(INODE_MAP, num, 1); X return(num); X} X X X X Xint alloc_zone() X{ X /* allocate a new zone */ X /* works for zone > block */ X int b,z,i; X X z = next_zone++; X b = z << zone_shift; X if ( (b+zone_size) > nrblocks) pexit("File system not big enough for all the files"); X for ( i=0; i < zone_size; i++) X put_block ( b+i, zero ); /* give an empty zone */ X insert_bit(zone_map, z - zoff, 1); X return(z); X} X X X X Xinsert_bit(block, bit, count) Xint block, bit, count; X{ X /* insert 'count' bits in the bitmap */ X int w,s, i; X short buf[BLOCK_SIZE/sizeof(short)]; X X get_block(block, buf); X for (i = bit; i < bit + count; i++) { X w = i / (8*sizeof(short)); X s = i % (8*sizeof(short)); X buf[w] |= (1 << s); X } X put_block(block, buf); X} X X X X X/*================================================================ X * proto-file processing assist group X *===============================================================*/ X Xint mode_con(p) Xchar *p; X{ X /* convert string to mode */ X int o1, o2, o3, mode; X char c1, c2, c3; X X c1 = *p++; X c2 = *p++; X c3 = *p++; X o1 = *p++ - '0'; X o2 = *p++ - '0'; X o3 = *p++ - '0'; X mode = (o1 << 6) | (o2 << 3) | o3; X if (c1 == 'd') mode += I_DIRECTORY; X if (c1 == 'b') mode += I_BLOCK_SPECIAL; X if (c1 == 'c') mode += I_CHAR_SPECIAL; X if (c1 == '-') mode += I_REGULAR; X if (c2 == 'u') mode += I_SET_UID_BIT; X if (c3 == 'g') mode += I_SET_GID_BIT; X return(mode); X} X X X Xgetline(line, parse) Xchar *parse[MAX_TOKENS]; Xchar line[LINE_LEN]; X{ X /* read a line and break it up in tokens */ X int k; X char c, *p; X X for (k = 0; k < MAX_TOKENS; k++) parse[k] = 0; X for (k = 0; k < LINE_LEN; k++) line[k] = 0; X k = 0; X parse[0] = 0; X p = line; X while (1) { X *p = fgetc(proto); X if (*p == '\n') lct++; X if (*p <= 0) pexit("Unexpected end-of-file\n"); X if (*p == ' ' || *p == '\t') *p = 0; X if (*p == '\n') {*p++ = 0; *p = '\n'; break;} X p++; X } X X p = line; X lastp = line; X while (1) { X c = *p++; X if (c == '\n') return; X if (c == 0) continue; X parse[k++] = p - 1; X do { X c = *p++; X } while (c != 0 && c != '\n'); X } X} X X X X X/*================================================================ X * other stuff X *===============================================================*/ X X Xlong file_time(f) Xint f; X{ X#ifdef UNIX X struct stat statbuf; X fstat(f, & statbuf); X return (statbuf.st_mtime); X#else /* fstat not supported by DOS */ X return( 0L ); X#endif X} X X Xpexit(s) Xchar *s; X{ X char *s0; X X s0 = s; X while (*s0 != 0) s0++; X write (2,"Error: ", 7); X write (2, s, (int)(s0-s) ); X write(2, "\n", 1); X printf("Line %d being processed when error detected.\n", lct); X flush(); X exit(2); X} X X Xcopy (from, to, count) Xchar *from, *to; Xint count; X{ X while (count--) *to++ = *from++; X} X X Xprint_fs() X{ X X int i, j, k; X d_inode inode[INODES_PER_BLOCK]; X int ibuf[INTS_PER_BLOCK], b; X struct { X short inum; X char name[14]; X } dir[NR_DIR_ENTRIES]; X X X get_block(1, ibuf); X printf("\nSuperblock: "); X for (i= 0; i<8; i++) printf("%06o ",ibuf[i]); X get_block(2, ibuf); X printf("\nInode map: "); X for (i = 0; i < 9; i++) printf("%06o ", ibuf[i]); X get_block(3, ibuf); X printf("\nZone map: "); X for (i = 0; i < 9; i++) printf("%06o ", ibuf[i]); X printf("\n"); X for (b = 4; b < 8; b++) { X get_block(b, inode); X for (i = 0; i < INODES_PER_BLOCK; i++) { X k = INODES_PER_BLOCK * (b - 4) + i + 1; X if (k > nrinodes) break; X if (inode[i].i_mode != 0) { X printf("Inode %2d: mode=",k, inode[i].i_mode); X printf("%06o", inode[i].i_mode); X printf(" uid=%2d gid=%2d size=", X inode[i].i_uid, inode[i].i_gid); X printf("%6ld", inode[i].i_size); X printf(" zone[0]=%d\n", inode[i].i_zone[0]); X } X X if ( (inode[i].i_mode & I_TYPE) == I_DIRECTORY) { X /* This is a directory */ X get_block(inode[i].i_zone[0], dir); X for (j = 0; j < NR_DIR_ENTRIES; j++) X if (dir[j].inum) X printf("\tInode %2d: %s\n",dir[j].inum,dir[j].name); X } X } X } X X printf("%d inodes used. %d zones used.\n",next_inode-1, next_zone); X} X X Xint read_and_set(n) Xint n; X{ X/* The first time a block is read, it returns alls 0s, unless there has X * been a write. This routine checks to see if a block has been accessed. X */ X X int w, s, mask, r; X X w = n/8; X s = n%8; X mask = 1 << s; X r = (umap[w] & mask ? 1 : 0); X umap[w] |= mask; X return(r); X} X X X X/*================================================================ X * get_block & put_block for MS-DOS X *===============================================================*/ X X#ifdef DOS X X/* X * These are the get_block and put_block routines X * when compiling & running mkfs.c under MS-DOS. X * X * It requires the (asembler) routines absread & abswrite X * from the file diskio.asm. Since these routines just do X * as they are told (read & write the sector specified), X * a local cache is used to minimize the i/o-overhead for X * frequently used blocks. X * X * The global variable "file" determines whether the output X * is to a disk-device or to a binary file. X */ X X X#define PH_SECTSIZE 512 /* size of a physical disk-sector */ X X Xchar *derrtab[14] = { X "no error", X "disk is read-only", X "unknown unit", X "device not ready", X "bad command", X "data error", X "internal error: bad request structure length", X "seek error", X "unknown media type", X "sector not found", X "printer out of paper (??)", X "write fault", X "read error", X "general error" X}; X X#define CACHE_SIZE 20 /* 20 block-buffers */ X X Xstruct cache { X char blockbuf[BLOCK_SIZE]; X int blocknum; X int dirty; X int usecnt; X} cache[CACHE_SIZE]; X X X X Xspecial (string) Xchar *string; X{ X X if (string[1] == ':' && string[2]==0) { X /* format: d: or d:fname */ X disk = (string[0] & ~32) - 'A'; X if (disk>1 && !override) /* safety precaution */ X pexit ("Bad drive specifier for special"); X } X else { X file=1; X if ((fd = creat(string,BWRITE)) == 0) X pexit ("Can't open special file"); X } X} X X X Xget_block(n, buf) Xint n; Xchar buf[BLOCK_SIZE]; X{ X /* get a block to the user */ X struct cache *bp,*fp; X X /* First access returns a zero block */ X if (read_and_set(n) == 0) { X copy(zero, buf, BLOCK_SIZE); X return; X } X X /* look for block in cache */ X fp=0; X for (bp=cache; bp<&cache[CACHE_SIZE]; bp++) { X if (bp->blocknum==n) { X copy (bp,buf,BLOCK_SIZE); X bp->usecnt++; X return; X } X /* remember clean block */ X if (bp->dirty == 0) X if (fp) {if (fp->usecnt > bp->usecnt) fp=bp;} X else fp=bp; X } X X /* block not in cache, get it */ X if (!fp) { X /* no clean buf, flush one */ X for (bp=cache,fp=cache; bp<&cache[CACHE_SIZE]; bp++) X if (fp->usecnt > bp->usecnt) fp=bp; X mx_write (fp->blocknum, fp); X } X X mx_read (n, fp); X fp->dirty=0; X fp->usecnt=0; X fp->blocknum=n; X copy (fp, buf, BLOCK_SIZE); X} X X X Xput_block(n, buf) Xint n; Xchar buf[BLOCK_SIZE]; X{ X /* Accept block from user */ X struct cache *fp, *bp; X X read_and_set(n); X X /* look for block in cache */ X fp=0; X for (bp=cache; bp<&cache[CACHE_SIZE]; bp++) { X if (bp->blocknum==n) { X copy (buf,bp,BLOCK_SIZE); X bp->dirty=1; X return; X } X /* remember clean block */ X if (bp->dirty == 0) X if (fp) {if (fp->usecnt > bp->usecnt) fp=bp;} X else fp=bp; X } X X /* block not in cache */ X if (!fp) { X /* no clean buf, flush one */ X for (bp=cache,fp=cache; bp<&cache[CACHE_SIZE]; bp++) X if (fp->usecnt > bp->usecnt) fp=bp; X mx_write (fp->blocknum, fp); X } X X fp->dirty=1; X fp->usecnt=1; X fp->blocknum=n; X copy (buf,fp,BLOCK_SIZE); X} X X X Xcache_init() X{ X struct cache *bp; X for (bp=cache; bp < &cache[CACHE_SIZE]; bp++) bp->blocknum = -1; X} X X X Xflush () X{ X /* flush all dirty blocks to disk */ X struct cache *bp; X X for (bp=cache; bp<&cache[CACHE_SIZE]; bp++) X if (bp->dirty) { X mx_write (bp->blocknum, bp); X bp->dirty=0; X } X} X X X X/*================================================================== X * hard read & write etc. X *=================================================================*/ X X#define MAX_RETRIES 5 X X Xmx_read (blocknr,buf) Xint blocknr; Xchar buf[BLOCK_SIZE]; X{ X X /* read the requested MINIX-block in core */ X char (*bp)[PH_SECTSIZE]; X int sectnum,retries,err; X X if (file) { X lseek (fd, (long) blocknr * BLOCK_SIZE, 0); X if (read (fd, buf, BLOCK_SIZE) != BLOCK_SIZE) X pexit ("mx_read: error reading file"); X } X else { X sectnum = blocknr * (BLOCK_SIZE / PH_SECTSIZE); X for (bp=buf; bp<&buf[BLOCK_SIZE]; bp++) { X retries = MAX_RETRIES; X do X err=absread (disk,sectnum,bp); X while (err && --retries); X X if (retries) { X sectnum++; X } else { X dexit ("mx_read",sectnum,err); X } X } X } X} X X X Xmx_write (blocknr,buf) Xint blocknr; Xchar buf[BLOCK_SIZE]; X{ X /* write the MINIX-block to disk */ X char (*bp)[PH_SECTSIZE]; X int retries,sectnum,err; X X if (file) { X lseek (fd, blocknr * BLOCK_SIZE, 0); X if (write (fd, buf, BLOCK_SIZE) != BLOCK_SIZE) { X pexit ("mx_write: error writing file"); X } X } X else { X sectnum = blocknr * (BLOCK_SIZE / PH_SECTSIZE); X for (bp=buf; bp<&buf[BLOCK_SIZE]; bp++) { X retries = MAX_RETRIES; X do { X err=abswrite (disk,sectnum,bp); X } while (err && --retries); X X if (retries) { X sectnum++; X } else { X dexit ("mx_write",sectnum,err); X } X } X } X} X X Xdexit (s,sectnum,err) Xint sectnum, err; Xchar *s; X{ X printf ("Error: %s, sector: %d, code: %d, meaning: %s\n", X s, sectnum, err, derrtab[err] ); X exit (2); X} X#endif X X/*================================================================ X * get_block & put_block for UNIX X *===============================================================*/ X X#ifdef UNIX X Xspecial (string) Xchar *string; X{ X fd = creat(string, 0777); X close(fd); X fd = open(string, 2); X if (fd < 0) pexit("Can't open special file"); X} X X X X X Xget_block(n, buf) Xint n; Xchar buf[BLOCK_SIZE]; X{ X/* Read a block. */ X X int k; X X /* First access returns a zero block */ X if (read_and_set(n) == 0) { X copy(zero, buf, BLOCK_SIZE); X return; X } X X lseek(fd, (long) n*BLOCK_SIZE, 0); X k = read(fd, buf, BLOCK_SIZE); X if (k != BLOCK_SIZE) { X pexit("get_block couldn't read"); X } X} X X X Xput_block(n, buf) Xint n; Xchar buf[BLOCK_SIZE]; X{ X/* Write a block. */ X X read_and_set(n); X X if (lseek(fd, (long)n*BLOCK_SIZE, 0) < 0L) { X pexit("put_block couldn't seek"); X } X if (write(fd, buf, BLOCK_SIZE) != BLOCK_SIZE) { X pexit("put_block couldn't write"); X } X} X X X/* dummy routines to keep source file clean from #ifdefs */ X Xflush() X{ X return; X} X X X Xcache_init() X{ X return; X} X X#endif X + END-OF-FILE mkfs.c chmod 'u=rw,g=r,o=r' 'mkfs.c' set `wc -c 'mkfs.c'` count=$1 case $count in 25884) :;; *) echo 'Bad character count in ''mkfs.c' >&2 echo 'Count should be 25884' >&2 esac exit 0