/* diskio.c -- C routines for disk i/o in tertiary boot programs. * * La Monte H. Yarroll <piggy@mwc.com>, September 1991 */ #include <sys/types.h> #include <canon.h> #include <sys/filsys.h> #include <sys/ino.h> #include <sys/inode.h> #include <sys/dir.h> #include <sys/buf.h> #include <sys/stat.h> #include "tboot.h" /* Aligning bread. * Reads 1 block into an arbitrary buffer. The assembly language * routine bread() needs a buffer aligned on a 4K boundary. */ char bufspace[FOURK+BLOCK]; char *lbuf = NULL; /* Buffer for bread. */ BUF * bread(blockno) daddr_t blockno; /* Block number. */ { BUF *retval = NULL; /* If not already aligned, align buffer on a 4K boundary. */ if (NULL == lbuf) { lbuf = bufspace + FOURK; lbuf = (char *) (((unsigned short) lbuf) & FOURKBOUNDRY); } sanity_check("bread() entry"); /* * Get a buffer to cache this in. */ retval = bclaim(blockno); sanity_check("bread() returning from bclaim()"); /* * If the buffer has the right block number, * we need not go further. */ if ((THE_DEV == retval->b_dev) && (blockno == retval->b_bno)) { return(retval); } /* Read the block, ignoring * the return value. */ sanity_check("bread() to _bread()"); _bread(blockno, lbuf); sanity_check("bread() from _bread()"); /* Copy to the unaligned buffer. */ sanity_check("bread() to memcpy()"); memcpy((uint16) (retval->b_paddr), lbuf, BLOCK); sanity_check("bread() from memcpy()"); retval->b_dev = THE_DEV; /* Associate it with a device, */ retval->b_bno = blockno; /* and block number. */ sanity_check("bread() about to return"); /* Return the buffer we just filled in. */ return(retval); } /* bread() */ /* * Inode OPEN: Load the inode for a file into memory. * iopen(struct inode *ip, * ino_t inode_number) * */ int iopen(meminode, inode_number) struct inode *meminode; ino_t inode_number; { BUF *bp; char *my_block; daddr_t blockno; /* Physical block the inode lives on. */ uint16 offset; /* Offset within that block for inode. */ struct dinode *diskinode; /* Pointer into buffer for disk * inode structure. */ /* Read the super block to check the inode_number. * bp = bread(1L); * WRITE ME */ /* Convert from inode number to block number. */ /* This code fragment should (but doesn't) examine the * bad block list, to skip over bad blocks in the free list. * * I'm no longer certain of the preceeding comment... :-( */ blockno = ((inode_number - 1) / INODES_PER_BLOCK) + 2; offset = ((inode_number - 1) % INODES_PER_BLOCK) * sizeof(struct dinode); /* Get the inode off disk. */ bp = bread(blockno); /* Point at the actual disk block. */ my_block = (char *) bp->b_paddr; /* Pick out the particular inode we want. */ diskinode = (struct dinode *) (my_block + offset); meminode->i_dev = THE_DEV; /* We're making something up. */ meminode->i_ino = inode_number; /* Inode index */ meminode->i_refc = diskinode->di_nlink; meminode->i_gate[0] = 0; /* Gate (unused) */ meminode->i_gate[1] = 0; /* Gate (unused) */ meminode->i_flag = 0; /* Flags (unused) */ meminode->i_mode = diskinode->di_mode; meminode->i_nlink = diskinode->di_nlink; meminode->i_uid = diskinode->di_uid; meminode->i_gid = diskinode->di_gid; meminode->i_size = diskinode->di_size; meminode->i_atime = diskinode->di_atime; meminode->i_mtime = diskinode->di_mtime; meminode->i_ctime = diskinode->di_ctime; meminode->i_lrt = GREATEST( /* Last reference time */ meminode->i_atime, meminode->i_mtime, meminode->i_ctime); /* Disk addresses */ /* Copy all block numbers (10 direct, 3 indirects). */ l3tol(meminode->i_a.i_addr, diskinode->di_a.di_addb, NADDR); /* Translate cannonical form to internal form. */ canshort(meminode->i_nlink); /* Reference count */ canshort(meminode->i_mode); /* Mode and type */ canshort(meminode->i_nlink); /* Number of links */ canshort(meminode->i_uid); /* Owner's user id */ canshort(meminode->i_gid); /* Owner's group id */ cansize(meminode->i_size); /* Size of file in bytes */ cantime(meminode->i_atime); /* Last access time */ cantime(meminode->i_mtime); /* Last modify time */ cantime(meminode->i_ctime); /* Creation time */ sanity_check("iopen() calling brelease()"); brelease(bp); return(1); } /* iopen() */ /* Convert a filename to an inode number. Returns inode number 0 on * failure. */ ino_t namei(filename) char *filename; { fsize_t i; struct direct dirent; struct inode dir; iopen(&dir, (ino_t) 2); /* Open the root directory. */ /* Read each directory entry one at a time. */ for (i = 0; i < dir.i_size; i += sizeof(struct direct)) { iread(&dir, (char *) &dirent, (fsize_t) i, (uint16) sizeof(struct direct)); /* Canonicalize it. */ canino(dirent.d_ino); if (0 == strncmp(filename, dirent.d_name, DIRSIZ)){ return(dirent.d_ino); } } return((ino_t) 0); } /* namei() */ /* * Inode READ: Load a local buffer from a file. * iread(struct inode *ip, * char *buffer, * fsize_t offset, * uint16 lenarg); */ void iread(ip, buffer, offset, lenarg) struct inode *ip; /* Read from this file, */ char *buffer; /* into this buffer, */ fsize_t offset; /* from here in the file, */ uint16 lenarg; /* for this many bytes. */ { extern uint16 myds; /* Contents of ds register. */ ifread(ip, myds, buffer, offset, (fsize_t) lenarg); } /* iread() */ /* * Inode to Far READ: Load an arbitrary length from a file into a far address. * ifread(struct inode *ip, * uint16 toseg, * uint16 tooffset, * fsize_t offset, * fsize_t length); */ void ifread(ip, toseg, tooffset, offset, lenarg) struct inode *ip; /* Read from this file, */ uint16 toseg; /* into this far buffer, */ uint16 tooffset; fsize_t offset; /* from here in the file, */ fsize_t lenarg; /* for this many bytes. */ { BUF *bp; char *my_block; daddr_t fblockno; /* Block number relative to file. */ daddr_t pblockno; /* Block number relative to disk. */ uint16 blockoffset; /* How far into buffer to start or end. */ uint16 blocklen; /* Size of first fractional block. */ long length; extern uint16 myds; /* Contents of register ds. */ length = (int32) lenarg; /* We need something that can go negative. */ /* Calculate the first block of the requested file segment. */ fblockno = (daddr_t) (offset / (fsize_t) BLOCK); /* Look up the physical block number. */ pblockno = vmap(ip, fblockno); /* Calculate starting offset within that block. */ blockoffset = (uint16) (offset % (fsize_t) BLOCK); blocklen = (uint16) LESSER((int32) (BLOCK - blockoffset), length); /* Read the first fraction of a block. */ bp = bread(pblockno); /* Point at the actual data. */ my_block = (char *) bp->b_paddr; /* Copy it in place. */ ffcopy(tooffset, toseg, &(my_block[blockoffset]), myds, blocklen); sanity_check("ifread() calling the first brelease()"); brelease(bp); /* Don't bother with the rest if there is nothing more to read. */ if (blocklen == (uint16) length) { return; } ++fblockno; pblockno = vmap(ip, fblockno); length -= (int32) blocklen; seginc(&tooffset, &toseg, blocklen); pac_init(); /* Turn on the pacifier. */ /* Read the middle blocks. */ while (length - (int32) BLOCK > 0L) { sanity_check("ifread() to middle bread()"); bp = bread(pblockno); sanity_check("ifread() from middle bread()"); /* Point at the actual data. */ my_block = (char *) bp->b_paddr; sanity_check("ifread() to middle ffcopy()"); ffcopy(tooffset, toseg, my_block, myds, BLOCK); sanity_check("ifread() from middle ffcopy()"); sanity_check("ifread() to middle brelease()"); brelease(bp); sanity_check("ifread() from middle brelease()"); pacifier(); /* Put something reassuring on the screen. */ ++fblockno; sanity_check("ifread() to middle vmap()"); pblockno = vmap(ip, fblockno); sanity_check("ifread() to middle vmap()"); length -= (int32) BLOCK; sanity_check("ifread() to middle seginc()"); seginc(&tooffset, &toseg, BLOCK); sanity_check("ifread() from middle seginc()"); } pac_cleanup(); /* Turn off the pacifier. */ /* Read the last fraction of a block. */ if (length > 0L) { /* ASSERTION: length < BLOCK */ bp = bread(pblockno); /* Point at the actual data. */ my_block = (char *) bp->b_paddr; ffcopy(tooffset, toseg, my_block, myds, (uint16)length); sanity_check("ifread() calling the last brelease()"); brelease(bp); } } /* ifread() */ /* Aligning xbread. * Disk addresses are relative to the start of the disk, rather than * the start of the partition. * Reads 1 block into an arbitrary buffer. The assembly language * routine xbread() needs a buffer aligned on a 4K boundary. */ BUF * xbread(blockno) daddr_t blockno; /* Block number. */ { BUF *retval = NULL; /* If not already aligned, align buffer on a 4K boundary. */ if (NULL == lbuf) { lbuf = bufspace + FOURK; lbuf = (char *) (((unsigned short) lbuf) & FOURKBOUNDRY); } sanity_check("xbread() entry"); /* * Get a buffer to cache this in. */ retval = bclaim(blockno); sanity_check("xbread() returning from bclaim()"); /* * If the buffer has the right block number, * we need not go further. */ if ((THE_XDEV == retval->b_dev) && (blockno == retval->b_bno)) { return(retval); } /* Read the block, ignoring * the return value. */ sanity_check("xbread() to _xbread()"); _xbread(blockno, lbuf); sanity_check("xbread() from _xbread()"); /* Copy to the unaligned buffer. */ sanity_check("xbread() to memcpy()"); memcpy((uint16) (retval->b_paddr), lbuf, BLOCK); sanity_check("xbread() from memcpy()"); retval->b_dev = THE_XDEV; /* Associate it with a device, */ retval->b_bno = blockno; /* and block number. */ sanity_check("xbread() about to return"); /* Return the buffer we just filled in. */ return(retval); } /* xbread() */