[comp.os.minix] tools/{build.c, init.c, mkfs.c}

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