diff options
author | Thomas Bushnell <thomas@gnu.org> | 1997-04-03 23:28:32 +0000 |
---|---|---|
committer | Thomas Bushnell <thomas@gnu.org> | 1997-04-03 23:28:32 +0000 |
commit | 3dd031e1f1571f5b60409c17efe90cc51d51a25c (patch) | |
tree | 3491c55317bb9976413ca314a11d35d2808753f2 /serverboot/ext2_file_io.c | |
parent | 6812102eb13b3c58c79f42f6a6f5c5081f8dfcad (diff) |
Initial Revision
Diffstat (limited to 'serverboot/ext2_file_io.c')
-rw-r--r-- | serverboot/ext2_file_io.c | 1099 |
1 files changed, 1099 insertions, 0 deletions
diff --git a/serverboot/ext2_file_io.c b/serverboot/ext2_file_io.c new file mode 100644 index 00000000..9d743368 --- /dev/null +++ b/serverboot/ext2_file_io.c @@ -0,0 +1,1099 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ +/* + * Stand-alone file reading package. + */ + +#include <device/device_types.h> +#include <device/device.h> + +#include <mach/mach_traps.h> +#include <mach/mach_interface.h> + +#include "file_io.h" +#include "ffs_compat.h" + +void ext2_close_file(); /* forward */ + +/* + * Free file buffers, but don't close file. + */ +static void +free_file_buffers(fp) + register struct file *fp; +{ + register int level; + + /* + * Free the indirect blocks + */ + for (level = 0; level < NIADDR; level++) { + if (fp->f_blk[level] != 0) { + (void) vm_deallocate(mach_task_self(), + fp->f_blk[level], + fp->f_blksize[level]); + fp->f_blk[level] = 0; + } + fp->f_blkno[level] = -1; + } + + /* + * Free the data block + */ + if (fp->f_buf != 0) { + (void) vm_deallocate(mach_task_self(), + fp->f_buf, + fp->f_buf_size); + fp->f_buf = 0; + } + fp->f_buf_blkno = -1; +} + +/* + * Read a new inode into a file structure. + */ +static int +read_inode(inumber, fp) + ino_t inumber; + register struct file *fp; +{ + vm_offset_t buf; + mach_msg_type_number_t buf_size; + register + struct ext2_super_block *fs; + daddr_t disk_block; + kern_return_t rc; + + fs = fp->f_fs; + disk_block = ino2blk(fs, fp->f_gd, inumber); + + rc = device_read(fp->f_dev, + 0, + (recnum_t) fsbtodb(fp->f_fs, disk_block), + (int) EXT2_BLOCK_SIZE(fs), + (char **)&buf, + &buf_size); + if (rc != KERN_SUCCESS) + return (rc); + + { + register struct ext2_inode *dp; + + dp = (struct ext2_inode *)buf; + dp += itoo(fs, inumber); + fp->i_ic = *dp; + fp->f_size = dp->i_size; + } + + (void) vm_deallocate(mach_task_self(), buf, buf_size); + + /* + * Clear out the old buffers + */ + free_file_buffers(fp); + + return (0); +} + +/* + * Given an offset in a file, find the disk block number that + * contains that block. + */ +static int +block_map(fp, file_block, disk_block_p) + struct file *fp; + daddr_t file_block; + daddr_t *disk_block_p; /* out */ +{ + int level; + int idx; + daddr_t ind_block_num; + kern_return_t rc; + + vm_offset_t olddata[NIADDR+1]; + vm_size_t oldsize[NIADDR+1]; + + /* + * Index structure of an inode: + * + * i_db[0..NDADDR-1] hold block numbers for blocks + * 0..NDADDR-1 + * + * i_ib[0] index block 0 is the single indirect + * block + * holds block numbers for blocks + * NDADDR .. NDADDR + NINDIR(fs)-1 + * + * i_ib[1] index block 1 is the double indirect + * block + * holds block numbers for INDEX blocks + * for blocks + * NDADDR + NINDIR(fs) .. + * NDADDR + NINDIR(fs) + NINDIR(fs)**2 - 1 + * + * i_ib[2] index block 2 is the triple indirect + * block + * holds block numbers for double-indirect + * blocks for blocks + * NDADDR + NINDIR(fs) + NINDIR(fs)**2 .. + * NDADDR + NINDIR(fs) + NINDIR(fs)**2 + * + NINDIR(fs)**3 - 1 + */ + + mutex_lock(&fp->f_lock); + + if (file_block < NDADDR) { + /* Direct block. */ + *disk_block_p = fp->i_ic.i_block[file_block]; + mutex_unlock(&fp->f_lock); + return (0); + } + + file_block -= NDADDR; + + /* + * nindir[0] = NINDIR + * nindir[1] = NINDIR**2 + * nindir[2] = NINDIR**3 + * etc + */ + for (level = 0; level < NIADDR; level++) { + if (file_block < fp->f_nindir[level]) + break; + file_block -= fp->f_nindir[level]; + } + if (level == NIADDR) { + /* Block number too high */ + mutex_unlock(&fp->f_lock); + return (FS_NOT_IN_FILE); + } + + ind_block_num = fp->i_ic.i_block[level + NDADDR]; + + /* + * Initialize array of blocks to free. + */ + for (idx = 0; idx < NIADDR; idx++) + oldsize[idx] = 0; + + for (; level >= 0; level--) { + + vm_offset_t data; + mach_msg_type_number_t size; + + if (ind_block_num == 0) + break; + + if (fp->f_blkno[level] == ind_block_num) { + /* + * Cache hit. Just pick up the data. + */ + + data = fp->f_blk[level]; + } + else { + /* + * Drop our lock while doing the read. + * (The f_dev and f_fs fields don`t change.) + */ + mutex_unlock(&fp->f_lock); + + rc = device_read(fp->f_dev, + 0, + (recnum_t) fsbtodb(fp->f_fs, ind_block_num), + EXT2_BLOCK_SIZE(fp->f_fs), + (char **)&data, + &size); + if (rc != KERN_SUCCESS) + return (rc); + + /* + * See if we can cache the data. Need a write lock to + * do this. While we hold the write lock, we can`t do + * *anything* which might block for memory. Otherwise + * a non-privileged thread might deadlock with the + * privileged threads. We can`t block while taking the + * write lock. Otherwise a non-privileged thread + * blocked in the vm_deallocate (while holding a read + * lock) will block a privileged thread. For the same + * reason, we can`t take a read lock and then use + * lock_read_to_write. + */ + + mutex_lock(&fp->f_lock); + + olddata[level] = fp->f_blk[level]; + oldsize[level] = fp->f_blksize[level]; + + fp->f_blkno[level] = ind_block_num; + fp->f_blk[level] = data; + fp->f_blksize[level] = size; + + /* + * Return to holding a read lock, and + * dispose of old data. + */ + + } + + if (level > 0) { + idx = file_block / fp->f_nindir[level-1]; + file_block %= fp->f_nindir[level-1]; + } + else + idx = file_block; + + ind_block_num = ((daddr_t *)data)[idx]; + } + + mutex_unlock(&fp->f_lock); + + /* + * After unlocking the file, free any blocks that + * we need to free. + */ + for (idx = 0; idx < NIADDR; idx++) + if (oldsize[idx] != 0) + (void) vm_deallocate(mach_task_self(), + olddata[idx], + oldsize[idx]); + + *disk_block_p = ind_block_num; + return (0); +} + +/* + * Read a portion of a file into an internal buffer. Return + * the location in the buffer and the amount in the buffer. + */ +static int +buf_read_file(fp, offset, buf_p, size_p) + register struct file *fp; + vm_offset_t offset; + vm_offset_t *buf_p; /* out */ + vm_size_t *size_p; /* out */ +{ + register + struct ext2_super_block *fs; + vm_offset_t off; + register daddr_t file_block; + daddr_t disk_block; + int rc; + vm_offset_t block_size; + + if (offset >= fp->i_ic.i_size) + return (FS_NOT_IN_FILE); + + fs = fp->f_fs; + + off = blkoff(fs, offset); + file_block = lblkno(fs, offset); + block_size = blksize(fs, fp, file_block); + + if (file_block != fp->f_buf_blkno) { + rc = block_map(fp, file_block, &disk_block); + if (rc != 0) + return (rc); + + if (fp->f_buf) + (void)vm_deallocate(mach_task_self(), + fp->f_buf, + fp->f_buf_size); + + if (disk_block == 0) { + (void)vm_allocate(mach_task_self(), + &fp->f_buf, + block_size, + TRUE); + fp->f_buf_size = block_size; + } + else { + rc = device_read(fp->f_dev, + 0, + (recnum_t) fsbtodb(fs, disk_block), + (int) block_size, + (char **) &fp->f_buf, + (mach_msg_type_number_t *)&fp->f_buf_size); + } + if (rc) + return (rc); + + fp->f_buf_blkno = file_block; + } + + /* + * Return address of byte in buffer corresponding to + * offset, and size of remainder of buffer after that + * byte. + */ + *buf_p = fp->f_buf + off; + *size_p = block_size - off; + + /* + * But truncate buffer at end of file. + */ + if (*size_p > fp->i_ic.i_size - offset) + *size_p = fp->i_ic.i_size - offset; + + return (0); +} + +/* + * Search a directory for a name and return its + * i_number. + */ +static int +search_directory(name, fp, inumber_p) + char * name; + register struct file *fp; + ino_t *inumber_p; /* out */ +{ + vm_offset_t buf; + vm_size_t buf_size; + vm_offset_t offset; + register struct ext2_dir_entry *dp; + int length; + kern_return_t rc; + char tmp_name[256]; + + length = strlen(name); + + offset = 0; + while (offset < fp->i_ic.i_size) { + rc = buf_read_file(fp, offset, &buf, &buf_size); + if (rc != KERN_SUCCESS) + return (rc); + + dp = (struct ext2_dir_entry *)buf; + if (dp->inode != 0) { + strncpy (tmp_name, dp->name, dp->name_len); + tmp_name[dp->name_len] = '\0'; + if (dp->name_len == length && + !strcmp(name, tmp_name)) + { + /* found entry */ + *inumber_p = dp->inode; + return (0); + } + } + offset += dp->rec_len; + } + return (FS_NO_ENTRY); +} + +static int +read_fs(dev, fsp, gdp, gd_size_p) + mach_port_t dev; + struct ext2_super_block **fsp; + struct ext2_group_desc **gdp; + vm_size_t *gd_size_p; +{ + register + struct ext2_super_block *fs; + vm_offset_t buf; + vm_offset_t buf2; + mach_msg_type_number_t buf_size; + mach_msg_type_number_t buf2_size; + int error; + int gd_count; + int gd_blocks; + int gd_size; + int gd_location; + int gd_sector; + + /* + * Read the super block + */ + error = device_read(dev, 0, (recnum_t) SBLOCK, SBSIZE, + (char **) &buf, &buf_size); + if (error) + return (error); + + /* + * Check the superblock + */ + fs = (struct ext2_super_block *)buf; + if (fs->s_magic != EXT2_SUPER_MAGIC) { + (void) vm_deallocate(mach_task_self(), buf, buf_size); + return (FS_INVALID_FS); + } + + *fsp = fs; + + /* + * Compute the groups informations + */ + gd_count = (fs->s_blocks_count - fs->s_first_data_block + + fs->s_blocks_per_group - 1) / fs->s_blocks_per_group; + gd_blocks = (gd_count + EXT2_DESC_PER_BLOCK(fs) - 1) / + EXT2_DESC_PER_BLOCK(fs); + gd_size = gd_blocks * EXT2_BLOCK_SIZE(fs); + gd_location = fs->s_first_data_block + 1; + gd_sector = (gd_location * EXT2_BLOCK_SIZE(fs)) / DEV_BSIZE; + + /* + * Read the groups descriptors + */ + error = device_read(dev, 0, (recnum_t) gd_sector, gd_size, + (char **) &buf2, &buf2_size); + if (error) { + (void) vm_deallocate(mach_task_self(), buf, buf_size); + return error; + } + + *gdp = (struct ext2_group_desc *) buf2; + *gd_size_p = gd_size; + + return 0; +} + +static int +mount_fs(fp) + register struct file *fp; +{ + register struct ext2_super_block *fs; + int error; + + error = read_fs(fp->f_dev, &fp->f_fs, &fp->f_gd, &fp->f_gd_size); + if (error) + return (error); + + fs = fp->f_fs; + + /* + * Calculate indirect block levels. + */ + { + register int mult; + register int level; + + mult = 1; + for (level = 0; level < NIADDR; level++) { + mult *= NINDIR(fs); + fp->f_nindir[level] = mult; + } + } + + return (0); +} + +static void +unmount_fs(fp) + register struct file *fp; +{ + if (file_is_structured(fp)) { + (void) vm_deallocate(mach_task_self(), + (vm_offset_t) fp->f_fs, + SBSIZE); + (void) vm_deallocate(mach_task_self(), + (vm_offset_t) fp->f_gd, + fp->f_gd_size); + fp->f_fs = 0; + } +} + +/* + * Open a file. + */ +int +ext2_open_file(master_device_port, path, fp) + mach_port_t master_device_port; + char * path; + struct file *fp; +{ +#define RETURN(code) { rc = (code); goto exit; } + + register char *cp, *component; + register int c; /* char */ + register int rc; + ino_t inumber, parent_inumber; + int nlinks = 0; + + char namebuf[MAXPATHLEN+1]; + + if (path == 0 || *path == '\0') { + return FS_NO_ENTRY; + } + + /* + * Copy name into buffer to allow modifying it. + */ + strcpy(namebuf, path); + + /* + * Look for '/dev/xxx' at start of path, for + * root device. + */ + if (!strprefix(namebuf, "/dev/")) { + printf("no device name\n"); + return FS_NO_ENTRY; + } + + cp = namebuf + 5; /* device */ + component = cp; + while ((c = *cp) != '\0' && c != '/') { + cp++; + } + *cp = '\0'; + + bzero (fp, sizeof (struct file)); + + rc = device_open(master_device_port, + D_READ|D_WRITE, + component, + &fp->f_dev); + if (rc) + return rc; + + if (c == 0) { + fp->f_fs = 0; + goto out_ok; + } + + *cp = c; + + rc = mount_fs(fp); + if (rc) + return rc; + + inumber = (ino_t) ROOTINO; + if ((rc = read_inode(inumber, fp)) != 0) { + printf("can't read root inode\n"); + goto exit; + } + + while (*cp) { + + /* + * Check that current node is a directory. + */ + if ((fp->i_ic.i_mode & IFMT) != IFDIR) + RETURN (FS_NOT_DIRECTORY); + + /* + * Remove extra separators + */ + while (*cp == '/') + cp++; + + /* + * Get next component of path name. + */ + component = cp; + { + register int len = 0; + + while ((c = *cp) != '\0' && c != '/') { + if (len++ > MAXNAMLEN) + RETURN (FS_NAME_TOO_LONG); + if (c & 0200) + RETURN (FS_INVALID_PARAMETER); + cp++; + } + *cp = 0; + } + + /* + * Look up component in current directory. + * Save directory inumber in case we find a + * symbolic link. + */ + parent_inumber = inumber; + rc = search_directory(component, fp, &inumber); + if (rc) { + printf("%s: not found\n", path); + goto exit; + } + *cp = c; + + /* + * Open next component. + */ + if ((rc = read_inode(inumber, fp)) != 0) + goto exit; + + /* + * Check for symbolic link. + */ + if ((fp->i_ic.i_mode & IFMT) == IFLNK) { + + int link_len = fp->i_ic.i_size; + int len; + + len = strlen(cp) + 1; + + if (link_len + len >= MAXPATHLEN - 1) + RETURN (FS_NAME_TOO_LONG); + + if (++nlinks > MAXSYMLINKS) + RETURN (FS_SYMLINK_LOOP); + + ovbcopy(cp, &namebuf[link_len], len); + +#ifdef IC_FASTLINK + if (fp->i_ic.i_blocks == 0) { + bcopy(fp->i_ic.i_block, namebuf, (unsigned) link_len); + } + else +#endif IC_FASTLINK + { + /* + * Read file for symbolic link + */ + vm_offset_t buf; + mach_msg_type_number_t buf_size; + daddr_t disk_block; + register struct ext2_super_block *fs = fp->f_fs; + + (void) block_map(fp, (daddr_t)0, &disk_block); + rc = device_read(fp->f_dev, + 0, + (recnum_t) fsbtodb(fs, disk_block), + (int) blksize(fs, fp, 0), + (char **) &buf, + &buf_size); + if (rc) + goto exit; + + bcopy((char *)buf, namebuf, (unsigned)link_len); + (void) vm_deallocate(mach_task_self(), buf, buf_size); + } + + /* + * If relative pathname, restart at parent directory. + * If absolute pathname, restart at root. + * If pathname begins '/dev/<device>/', + * restart at root of that device. + */ + cp = namebuf; + if (*cp != '/') { + inumber = parent_inumber; + } + else if (!strprefix(cp, "/dev/")) { + inumber = (ino_t)ROOTINO; + } + else { + cp += 5; + component = cp; + while ((c = *cp) != '\0' && c != '/') { + cp++; + } + *cp = '\0'; + + /* + * Unmount current file system and free buffers. + */ + ext2_close_file(fp); + + /* + * Open new root device. + */ + rc = device_open(master_device_port, + D_READ, + component, + &fp->f_dev); + if (rc) + return (rc); + + if (c == 0) { + fp->f_fs = 0; + goto out_ok; + } + + *cp = c; + + rc = mount_fs(fp); + if (rc) + return (rc); + + inumber = (ino_t)ROOTINO; + } + if ((rc = read_inode(inumber, fp)) != 0) + goto exit; + } + } + + /* + * Found terminal component. + */ + out_ok: + mutex_init(&fp->f_lock); + return 0; + + /* + * At error exit, close file to free storage. + */ + exit: + ext2_close_file(fp); + return rc; +} + +/* + * Close file - free all storage used. + */ +void +ext2_close_file(fp) + register struct file *fp; +{ + register int i; + + /* + * Free the disk super-block. + */ + unmount_fs(fp); + + /* + * Free the inode and data buffers. + */ + free_file_buffers(fp); +} + +int +ext2_file_is_directory(struct file *fp) +{ + return (fp->i_ic.i_mode & IFMT) == IFDIR; +} + +int +ext2_file_is_regular(struct file *fp) +{ + return (fp->i_ic.i_mode & IFMT) == IFREG; +} + +/* + * Copy a portion of a file into kernel memory. + * Cross block boundaries when necessary. + */ +int +ext2_read_file(fp, offset, start, size, resid) + register struct file *fp; + vm_offset_t offset; + vm_offset_t start; + vm_size_t size; + vm_size_t *resid; /* out */ +{ + int rc; + register vm_size_t csize; + vm_offset_t buf; + vm_size_t buf_size; + + while (size != 0) { + rc = buf_read_file(fp, offset, &buf, &buf_size); + if (rc) + return (rc); + + csize = size; + if (csize > buf_size) + csize = buf_size; + if (csize == 0) + break; + + bcopy((char *)buf, (char *)start, csize); + + offset += csize; + start += csize; + size -= csize; + } + if (resid) + *resid = size; + + return (0); +} + +/* simple utility: only works for 2^n */ +static int +log2(n) + register unsigned int n; +{ + register int i = 0; + + while ((n & 1) == 0) { + i++; + n >>= 1; + } + return i; +} + +/* + * Make an empty file_direct for a device. + */ +int +ext2_open_file_direct(dev, fdp, is_structured) + mach_port_t dev; + register struct file_direct *fdp; + boolean_t is_structured; +{ + struct ext2_super_block *fs; + struct ext2_group_desc *gd; + vm_size_t gd_size; + int rc; + + if (!is_structured) { + fdp->fd_dev = dev; + fdp->fd_blocks = (daddr_t *) 0; + fdp->fd_bsize = vm_page_size; + fdp->fd_bshift = log2(vm_page_size); + fdp->fd_fsbtodb = 0; /* later */ + fdp->fd_size = 0; /* later */ + return 0; + } + + rc = read_fs(dev, &fs, &gd, &gd_size); + if (rc) + return rc; + + fdp->fd_dev = dev; + fdp->fd_blocks = (daddr_t *) 0; + fdp->fd_size = 0; + fdp->fd_bsize = EXT2_BLOCK_SIZE(fs); + fdp->fd_bshift = log2(fdp->fd_bsize); + fdp->fd_fsbtodb = log2(fdp->fd_bsize / DEV_BSIZE); + + (void) vm_deallocate(mach_task_self(), + (vm_offset_t) fs, + SBSIZE); + (void) vm_deallocate(mach_task_self(), + (vm_offset_t) gd, + gd_size); + + return 0; +} + +/* + * Add blocks from a file to a file_direct. + */ +int +ext2_add_file_direct(fdp, fp) + register struct file_direct *fdp; + register struct file *fp; +{ + register struct ext2_super_block *fs; + long num_blocks, i; + vm_offset_t buffer; + vm_size_t size; + int rc; + + /* the file must be on the same device */ + + if (fdp->fd_dev != fp->f_dev) + return FS_INVALID_FS; + + if (!file_is_structured(fp)) { + int result[DEV_GET_SIZE_COUNT]; + natural_t count; + + count = DEV_GET_SIZE_COUNT; + rc = device_get_status( fdp->fd_dev, DEV_GET_SIZE, + result, &count); + if (rc) + return rc; + fdp->fd_size = result[DEV_GET_SIZE_DEVICE_SIZE] >> fdp->fd_bshift; + fdp->fd_fsbtodb = log2(fdp->fd_bsize/result[DEV_GET_SIZE_RECORD_SIZE]); + return 0; + } + + /* it must hold a file system */ + + fs = fp->f_fs; +/* + if (fdp->fd_bsize != fs->fs_bsize || + fdp->fd_fsbtodb != fs->fs_fsbtodb) +*/ + if (fdp->fd_bsize != EXT2_BLOCK_SIZE(fs)) + return FS_INVALID_FS; + + /* calculate number of blocks in the file, ignoring fragments */ + + num_blocks = lblkno(fs, fp->i_ic.i_size); + + /* allocate memory for a bigger array */ + + size = (num_blocks + fdp->fd_size) * sizeof(daddr_t); + rc = vm_allocate(mach_task_self(), &buffer, size, TRUE); + if (rc != KERN_SUCCESS) + return rc; + + /* lookup new block addresses */ + + for (i = 0; i < num_blocks; i++) { + daddr_t disk_block; + + rc = block_map(fp, (daddr_t) i, &disk_block); + if (rc != 0) { + (void) vm_deallocate(mach_task_self(), buffer, size); + return rc; + } + + ((daddr_t *) buffer)[fdp->fd_size + i] = disk_block; + } + + /* copy old addresses and install the new array */ + + if (fdp->fd_blocks != 0) { + bcopy((char *) fdp->fd_blocks, (char *) buffer, + fdp->fd_size * sizeof(daddr_t)); + + (void) vm_deallocate(mach_task_self(), + (vm_offset_t) fdp->fd_blocks, + (vm_size_t) (fdp->fd_size * sizeof(daddr_t))); + } + fdp->fd_blocks = (daddr_t *) buffer; + fdp->fd_size += num_blocks; + + /* deallocate cached blocks */ + + free_file_buffers(fp); + + return 0; +} + +int +ext2_remove_file_direct(fdp) + struct file_direct *fdp; +{ + if (fdp->fd_blocks) + (void) vm_deallocate(mach_task_self(), + (vm_offset_t) fdp->fd_blocks, + (vm_size_t) (fdp->fd_size * sizeof(daddr_t))); + fdp->fd_blocks = 0; /* sanity */ + /* xxx should lose a ref to fdp->fd_dev here (and elsewhere) xxx */ +} + +/* + * Special read and write routines for default pager. + * Assume that all offsets and sizes are multiples + * of DEV_BSIZE. + */ + +#define fdir_blkoff(fdp, offset) /* offset % fd_bsize */ \ + ((offset) & ((fdp)->fd_bsize - 1)) +#define fdir_lblkno(fdp, offset) /* offset / fd_bsize */ \ + ((offset) >> (fdp)->fd_bshift) + +#define fdir_fsbtodb(fdp, block) /* offset * fd_bsize / DEV_BSIZE */ \ + ((block) << (fdp)->fd_fsbtodb) + +/* + * Read all or part of a data block, and + * return a pointer to the appropriate part. + * Caller must deallocate the block when done. + */ +int +ext2_page_read_file_direct(fdp, offset, size, addr, size_read) + register struct file_direct *fdp; + vm_offset_t offset; + vm_size_t size; + vm_offset_t *addr; /* out */ + mach_msg_type_number_t *size_read; /* out */ +{ + vm_offset_t off; + register daddr_t file_block; + daddr_t disk_block; + + if (offset % DEV_BSIZE != 0 || + size % DEV_BSIZE != 0) + panic("page_read_file_direct"); + + if (offset >= (fdp->fd_size << fdp->fd_bshift)) + return (FS_NOT_IN_FILE); + + off = fdir_blkoff(fdp, offset); + file_block = fdir_lblkno(fdp, offset); + + if (file_is_device(fdp)) { + disk_block = file_block; + } else { + disk_block = fdp->fd_blocks[file_block]; + if (disk_block == 0) + return (FS_NOT_IN_FILE); + } + + if (size > fdp->fd_bsize) + size = fdp->fd_bsize; + + return (device_read(fdp->fd_dev, + 0, + (recnum_t) (fdir_fsbtodb(fdp, disk_block) + btodb(off)), + (int) size, + (char **) addr, + size_read)); +} + +/* + * Write all or part of a data block, and + * return the amount written. + */ +int +ext2_page_write_file_direct(fdp, offset, addr, size, size_written) + register struct file_direct *fdp; + vm_offset_t offset; + vm_offset_t addr; + vm_size_t size; + vm_offset_t *size_written; /* out */ +{ + vm_offset_t off; + register daddr_t file_block; + daddr_t disk_block; + int rc, num_written; + vm_offset_t block_size; + + if (offset % DEV_BSIZE != 0 || + size % DEV_BSIZE != 0) + panic("page_write_file"); + + if (offset >= (fdp->fd_size << fdp->fd_bshift)) + return (FS_NOT_IN_FILE); + + off = fdir_blkoff(fdp, offset); + file_block = fdir_lblkno(fdp, offset); + + if (file_is_device(fdp)) { + disk_block = file_block; + } else { + disk_block = fdp->fd_blocks[file_block]; + if (disk_block == 0) + return (FS_NOT_IN_FILE); + } + + if (size > fdp->fd_bsize) + size = fdp->fd_bsize; + + /* + * Write the data. Wait for completion to keep + * reads from getting ahead of writes and reading + * stale data. + */ + rc = device_write( + fdp->fd_dev, + 0, + (recnum_t) (fdir_fsbtodb(fdp, disk_block) + btodb(off)), + (char *) addr, + size, + &num_written); + *size_written = num_written; + return rc; +} + |