159765Sjlemon/*- 259765Sjlemon * Copyright (c) 1999,2000 Jonathan Lemon <jlemon@freebsd.org> 359765Sjlemon * All rights reserved. 459765Sjlemon * 559765Sjlemon * Redistribution and use in source and binary forms, with or without 659765Sjlemon * modification, are permitted provided that the following conditions 759765Sjlemon * are met: 859765Sjlemon * 1. Redistributions of source code must retain the above copyright 959765Sjlemon * notice, this list of conditions and the following disclaimer. 1059765Sjlemon * 2. Redistributions in binary form must reproduce the above copyright 1159765Sjlemon * notice, this list of conditions and the following disclaimer in the 1259765Sjlemon * documentation and/or other materials provided with the distribution. 1359765Sjlemon * 1459765Sjlemon * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1559765Sjlemon * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1659765Sjlemon * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1759765Sjlemon * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1859765Sjlemon * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1959765Sjlemon * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2059765Sjlemon * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2159765Sjlemon * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2259765Sjlemon * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2359765Sjlemon * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2459765Sjlemon * SUCH DAMAGE. 2559765Sjlemon */ 2684221Sdillon 2784221Sdillon#include <sys/cdefs.h> 2884221Sdillon__FBSDID("$FreeBSD: releng/11.0/lib/libstand/ext2fs.c 298230 2016-04-18 23:09:22Z allanjude $"); 2984221Sdillon 3059765Sjlemon/*- 3159765Sjlemon * Copyright (c) 1993 3259765Sjlemon * The Regents of the University of California. All rights reserved. 3359765Sjlemon * 3459765Sjlemon * This code is derived from software contributed to Berkeley by 3559765Sjlemon * The Mach Operating System project at Carnegie-Mellon University. 3659765Sjlemon * 3759765Sjlemon * Redistribution and use in source and binary forms, with or without 3859765Sjlemon * modification, are permitted provided that the following conditions 3959765Sjlemon * are met: 4059765Sjlemon * 1. Redistributions of source code must retain the above copyright 4159765Sjlemon * notice, this list of conditions and the following disclaimer. 4259765Sjlemon * 2. Redistributions in binary form must reproduce the above copyright 4359765Sjlemon * notice, this list of conditions and the following disclaimer in the 4459765Sjlemon * documentation and/or other materials provided with the distribution. 4559765Sjlemon * 3. All advertising materials mentioning features or use of this software 4659765Sjlemon * must display the following acknowledgement: 4759765Sjlemon * This product includes software developed by the University of 4859765Sjlemon * California, Berkeley and its contributors. 4959765Sjlemon * 4. Neither the name of the University nor the names of its contributors 5059765Sjlemon * may be used to endorse or promote products derived from this software 5159765Sjlemon * without specific prior written permission. 5259765Sjlemon * 5359765Sjlemon * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 5459765Sjlemon * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 5559765Sjlemon * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 5659765Sjlemon * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 5759765Sjlemon * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 5859765Sjlemon * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 5959765Sjlemon * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 6059765Sjlemon * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 6159765Sjlemon * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 6259765Sjlemon * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 6359765Sjlemon * SUCH DAMAGE. 6459765Sjlemon * 6559765Sjlemon * 6659765Sjlemon * Copyright (c) 1990, 1991 Carnegie Mellon University 6759765Sjlemon * All Rights Reserved. 6859765Sjlemon * 6959765Sjlemon * Author: David Golub 7059765Sjlemon * 7159765Sjlemon * Permission to use, copy, modify and distribute this software and its 7259765Sjlemon * documentation is hereby granted, provided that both the copyright 7359765Sjlemon * notice and this permission notice appear in all copies of the 7459765Sjlemon * software, derivative works or modified versions, and any portions 7559765Sjlemon * thereof, and that both notices appear in supporting documentation. 7659765Sjlemon * 7759765Sjlemon * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" 7859765Sjlemon * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR 7959765Sjlemon * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. 8059765Sjlemon * 8159765Sjlemon * Carnegie Mellon requests users of this software to return to 8259765Sjlemon * 8359765Sjlemon * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU 8459765Sjlemon * School of Computer Science 8559765Sjlemon * Carnegie Mellon University 8659765Sjlemon * Pittsburgh PA 15213-3890 8759765Sjlemon * 8859765Sjlemon * any improvements or extensions that they make and grant Carnegie the 8959765Sjlemon * rights to redistribute these changes. 9059765Sjlemon */ 9159765Sjlemon 9259765Sjlemon#include <sys/param.h> 9359765Sjlemon#include <sys/time.h> 9459765Sjlemon#include "stand.h" 9559765Sjlemon#include "string.h" 9659765Sjlemon 9759765Sjlemonstatic int ext2fs_open(const char *path, struct open_file *f); 9859765Sjlemonstatic int ext2fs_close(struct open_file *f); 9959765Sjlemonstatic int ext2fs_read(struct open_file *f, void *buf, 10059765Sjlemon size_t size, size_t *resid); 10159765Sjlemonstatic off_t ext2fs_seek(struct open_file *f, off_t offset, int where); 10259765Sjlemonstatic int ext2fs_stat(struct open_file *f, struct stat *sb); 10359765Sjlemonstatic int ext2fs_readdir(struct open_file *f, struct dirent *d); 10459765Sjlemon 10559765Sjlemonstatic int dtmap[] = { DT_UNKNOWN, DT_REG, DT_DIR, DT_CHR, 10659765Sjlemon DT_BLK, DT_FIFO, DT_SOCK, DT_LNK }; 10759765Sjlemon#define EXTFTODT(x) (x) > sizeof(dtmap) / sizeof(dtmap[0]) ? \ 10859765Sjlemon DT_UNKNOWN : dtmap[x] 10959765Sjlemon 11059765Sjlemonstruct fs_ops ext2fs_fsops = { 11159765Sjlemon "ext2fs", 11259765Sjlemon ext2fs_open, 11359765Sjlemon ext2fs_close, 11459765Sjlemon ext2fs_read, 11559765Sjlemon null_write, 11659765Sjlemon ext2fs_seek, 11759765Sjlemon ext2fs_stat, 11859765Sjlemon ext2fs_readdir 11959765Sjlemon}; 12059765Sjlemon 12159765Sjlemon#define EXT2_SBSIZE 1024 12259765Sjlemon#define EXT2_SBLOCK (1024 / DEV_BSIZE) /* block offset of superblock */ 12359765Sjlemon#define EXT2_MAGIC 0xef53 12459765Sjlemon#define EXT2_ROOTINO 2 12559765Sjlemon 12659765Sjlemon#define EXT2_REV0 0 /* original revision of ext2 */ 12759765Sjlemon#define EXT2_R0_ISIZE 128 /* inode size */ 12859765Sjlemon#define EXT2_R0_FIRSTINO 11 /* first inode */ 12959765Sjlemon 13059765Sjlemon#define EXT2_MINBSHIFT 10 /* mininum block shift */ 13159765Sjlemon#define EXT2_MINFSHIFT 10 /* mininum frag shift */ 13259765Sjlemon 13359765Sjlemon#define NDADDR 12 /* # of direct blocks */ 13459765Sjlemon#define NIADDR 3 /* # of indirect blocks */ 13559765Sjlemon 13659765Sjlemon/* 13759765Sjlemon * file system block to disk address 13859765Sjlemon */ 13959765Sjlemon#define fsb_to_db(fs, blk) ((blk) << (fs)->fs_fsbtodb) 14059765Sjlemon 14159765Sjlemon/* 14259765Sjlemon * inode to block group offset 14359765Sjlemon * inode to block group 14459765Sjlemon * inode to disk address 14559765Sjlemon * inode to block offset 14659765Sjlemon */ 14759765Sjlemon#define ino_to_bgo(fs, ino) (((ino) - 1) % (fs)->fs_ipg) 14859765Sjlemon#define ino_to_bg(fs, ino) (((ino) - 1) / (fs)->fs_ipg) 14959765Sjlemon#define ino_to_db(fs, bg, ino) \ 15059765Sjlemon fsb_to_db(fs, ((bg)[ino_to_bg(fs, ino)].bg_inotbl + \ 15159765Sjlemon ino_to_bgo(fs, ino) / (fs)->fs_ipb)) 15259765Sjlemon#define ino_to_bo(fs, ino) (ino_to_bgo(fs, ino) % (fs)->fs_ipb) 15359765Sjlemon 15459765Sjlemon#define nindir(fs) \ 15559765Sjlemon ((fs)->fs_bsize / sizeof(u_int32_t)) 15659765Sjlemon#define lblkno(fs, loc) /* loc / bsize */ \ 15759765Sjlemon ((loc) >> (fs)->fs_bshift) 15859765Sjlemon#define smalllblktosize(fs, blk) /* blk * bsize */ \ 15959765Sjlemon ((blk) << (fs)->fs_bshift) 16059765Sjlemon#define blkoff(fs, loc) /* loc % bsize */ \ 16159765Sjlemon ((loc) & (fs)->fs_bmask) 16259765Sjlemon#define fragroundup(fs, size) /* roundup(size, fsize) */ \ 16359765Sjlemon (((size) + (fs)->fs_fmask) & ~(fs)->fs_fmask) 16459765Sjlemon#define dblksize(fs, dip, lbn) \ 16559765Sjlemon (((lbn) >= NDADDR || (dip)->di_size >= smalllblktosize(fs, (lbn) + 1)) \ 16659765Sjlemon ? (fs)->fs_bsize \ 16759765Sjlemon : (fragroundup(fs, blkoff(fs, (dip)->di_size)))) 16859765Sjlemon 16959765Sjlemon/* 17059765Sjlemon * superblock describing ext2fs 17159765Sjlemon */ 17259765Sjlemonstruct ext2fs_disk { 17359765Sjlemon u_int32_t fd_inodes; /* # of inodes */ 17459765Sjlemon u_int32_t fd_blocks; /* # of blocks */ 17559765Sjlemon u_int32_t fd_resblk; /* # of reserved blocks */ 17659765Sjlemon u_int32_t fd_freeblk; /* # of free blocks */ 17759765Sjlemon u_int32_t fd_freeino; /* # of free inodes */ 17859765Sjlemon u_int32_t fd_firstblk; /* first data block */ 17959765Sjlemon u_int32_t fd_bsize; /* block size */ 18059765Sjlemon u_int32_t fd_fsize; /* frag size */ 18159765Sjlemon u_int32_t fd_bpg; /* blocks per group */ 18259765Sjlemon u_int32_t fd_fpg; /* frags per group */ 18359765Sjlemon u_int32_t fd_ipg; /* inodes per group */ 18459765Sjlemon u_int32_t fd_mtime; /* mount time */ 18559765Sjlemon u_int32_t fd_wtime; /* write time */ 18659765Sjlemon u_int16_t fd_mount; /* # of mounts */ 18759765Sjlemon int16_t fd_maxmount; /* max # of mounts */ 18859765Sjlemon u_int16_t fd_magic; /* magic number */ 18959765Sjlemon u_int16_t fd_state; /* state */ 19059765Sjlemon u_int16_t fd_eflag; /* error flags */ 19159765Sjlemon u_int16_t fd_mnrrev; /* minor revision */ 19259765Sjlemon u_int32_t fd_lastchk; /* last check */ 19359765Sjlemon u_int32_t fd_chkintvl; /* maximum check interval */ 19459765Sjlemon u_int32_t fd_os; /* os */ 19559765Sjlemon u_int32_t fd_revision; /* revision */ 19659765Sjlemon u_int16_t fd_uid; /* uid for reserved blocks */ 19759765Sjlemon u_int16_t fd_gid; /* gid for reserved blocks */ 19859765Sjlemon 19959765Sjlemon u_int32_t fd_firstino; /* first non-reserved inode */ 20059765Sjlemon u_int16_t fd_isize; /* inode size */ 20159765Sjlemon u_int16_t fd_nblkgrp; /* block group # of superblock */ 20259765Sjlemon u_int32_t fd_fcompat; /* compatible features */ 20359765Sjlemon u_int32_t fd_fincompat; /* incompatible features */ 20472093Sasmodai u_int32_t fd_frocompat; /* read-only compatibilties */ 20559765Sjlemon u_int8_t fd_uuid[16]; /* volume uuid */ 20659765Sjlemon char fd_volname[16]; /* volume name */ 20759765Sjlemon char fd_fsmnt[64]; /* name last mounted on */ 20859765Sjlemon u_int32_t fd_bitmap; /* compression bitmap */ 20959765Sjlemon 21059765Sjlemon u_int8_t fd_nblkpa; /* # of blocks to preallocate */ 21159765Sjlemon u_int8_t fd_ndblkpa; /* # of dir blocks to preallocate */ 21259765Sjlemon}; 21359765Sjlemon 21459765Sjlemonstruct ext2fs_core { 21559765Sjlemon int fc_bsize; /* block size */ 21659765Sjlemon int fc_bshift; /* block shift amount */ 21759765Sjlemon int fc_bmask; /* block mask */ 21859765Sjlemon int fc_fsize; /* frag size */ 21959765Sjlemon int fc_fshift; /* frag shift amount */ 22059765Sjlemon int fc_fmask; /* frag mask */ 22159765Sjlemon int fc_isize; /* inode size */ 22259765Sjlemon int fc_imask; /* inode mask */ 22359765Sjlemon int fc_firstino; /* first non-reserved inode */ 22459765Sjlemon int fc_ipb; /* inodes per block */ 22559765Sjlemon int fc_fsbtodb; /* fsb to ds shift */ 22659765Sjlemon}; 22759765Sjlemon 22859765Sjlemonstruct ext2fs { 22959765Sjlemon struct ext2fs_disk fs_fd; 23059765Sjlemon char fs_pad[EXT2_SBSIZE - sizeof(struct ext2fs_disk)]; 23159765Sjlemon struct ext2fs_core fs_fc; 23259765Sjlemon 23359765Sjlemon#define fs_magic fs_fd.fd_magic 23459765Sjlemon#define fs_revision fs_fd.fd_revision 23559765Sjlemon#define fs_blocks fs_fd.fd_blocks 23659765Sjlemon#define fs_firstblk fs_fd.fd_firstblk 23759765Sjlemon#define fs_bpg fs_fd.fd_bpg 23859765Sjlemon#define fs_ipg fs_fd.fd_ipg 23959765Sjlemon 24059765Sjlemon#define fs_bsize fs_fc.fc_bsize 24159765Sjlemon#define fs_bshift fs_fc.fc_bshift 24259765Sjlemon#define fs_bmask fs_fc.fc_bmask 24359765Sjlemon#define fs_fsize fs_fc.fc_fsize 24459765Sjlemon#define fs_fshift fs_fc.fc_fshift 24559765Sjlemon#define fs_fmask fs_fc.fc_fmask 24659765Sjlemon#define fs_isize fs_fc.fc_isize 24759765Sjlemon#define fs_imask fs_fc.fc_imask 24859765Sjlemon#define fs_firstino fs_fc.fc_firstino 24959765Sjlemon#define fs_ipb fs_fc.fc_ipb 25059765Sjlemon#define fs_fsbtodb fs_fc.fc_fsbtodb 25159765Sjlemon}; 25259765Sjlemon 25359765Sjlemonstruct ext2blkgrp { 25459765Sjlemon u_int32_t bg_blkmap; /* block bitmap */ 25559765Sjlemon u_int32_t bg_inomap; /* inode bitmap */ 25659765Sjlemon u_int32_t bg_inotbl; /* inode table */ 25759765Sjlemon u_int16_t bg_nfblk; /* # of free blocks */ 25859765Sjlemon u_int16_t bg_nfino; /* # of free inodes */ 25959765Sjlemon u_int16_t bg_ndirs; /* # of dirs */ 26059765Sjlemon char bg_pad[14]; 26159765Sjlemon}; 26259765Sjlemon 26359765Sjlemonstruct ext2dinode { 26459765Sjlemon u_int16_t di_mode; /* mode */ 26559765Sjlemon u_int16_t di_uid; /* uid */ 26659765Sjlemon u_int32_t di_size; /* byte size */ 26759765Sjlemon u_int32_t di_atime; /* access time */ 26859765Sjlemon u_int32_t di_ctime; /* creation time */ 26959765Sjlemon u_int32_t di_mtime; /* modification time */ 27059765Sjlemon u_int32_t di_dtime; /* deletion time */ 27159765Sjlemon u_int16_t di_gid; /* gid */ 27259765Sjlemon u_int16_t di_nlink; /* link count */ 27359765Sjlemon u_int32_t di_nblk; /* block count */ 27459765Sjlemon u_int32_t di_flags; /* file flags */ 27559765Sjlemon 27659765Sjlemon u_int32_t di_osdep1; /* os dependent stuff */ 27759765Sjlemon 27859765Sjlemon u_int32_t di_db[NDADDR]; /* direct blocks */ 27959765Sjlemon u_int32_t di_ib[NIADDR]; /* indirect blocks */ 28059765Sjlemon u_int32_t di_version; /* version */ 28159765Sjlemon u_int32_t di_facl; /* file acl */ 28259765Sjlemon u_int32_t di_dacl; /* dir acl */ 28359765Sjlemon u_int32_t di_faddr; /* fragment addr */ 28459765Sjlemon 28559765Sjlemon u_int8_t di_frag; /* fragment number */ 28659765Sjlemon u_int8_t di_fsize; /* fragment size */ 28759765Sjlemon 28859765Sjlemon char di_pad[10]; 28959765Sjlemon 29059765Sjlemon#define di_shortlink di_db 29159765Sjlemon}; 29259765Sjlemon 29359765Sjlemon#define EXT2_MAXNAMLEN 255 29459765Sjlemon 29559765Sjlemonstruct ext2dirent { 29659765Sjlemon u_int32_t d_ino; /* inode */ 29759765Sjlemon u_int16_t d_reclen; /* directory entry length */ 29859765Sjlemon u_int8_t d_namlen; /* name length */ 29959765Sjlemon u_int8_t d_type; /* file type */ 30059765Sjlemon char d_name[EXT2_MAXNAMLEN]; 30159765Sjlemon}; 30259765Sjlemon 30359765Sjlemonstruct file { 30459765Sjlemon off_t f_seekp; /* seek pointer */ 30559765Sjlemon struct ext2fs *f_fs; /* pointer to super-block */ 30659765Sjlemon struct ext2blkgrp *f_bg; /* pointer to blkgrp map */ 30759765Sjlemon struct ext2dinode f_di; /* copy of on-disk inode */ 30859765Sjlemon int f_nindir[NIADDR]; /* number of blocks mapped by 30959765Sjlemon indirect block at level i */ 31059765Sjlemon char *f_blk[NIADDR]; /* buffer for indirect block 31159765Sjlemon at level i */ 31259765Sjlemon size_t f_blksize[NIADDR]; /* size of buffer */ 31359765Sjlemon daddr_t f_blkno[NIADDR]; /* disk address of block in 31459765Sjlemon buffer */ 31559765Sjlemon char *f_buf; /* buffer for data block */ 31659765Sjlemon size_t f_buf_size; /* size of data block */ 31759765Sjlemon daddr_t f_buf_blkno; /* block number of data block */ 31859765Sjlemon}; 31959765Sjlemon 32059765Sjlemon/* forward decls */ 32159765Sjlemonstatic int read_inode(ino_t inumber, struct open_file *f); 32259765Sjlemonstatic int block_map(struct open_file *f, daddr_t file_block, 32359765Sjlemon daddr_t *disk_block_p); 32459765Sjlemonstatic int buf_read_file(struct open_file *f, char **buf_p, 32559765Sjlemon size_t *size_p); 32659765Sjlemonstatic int search_directory(char *name, struct open_file *f, 32759765Sjlemon ino_t *inumber_p); 32859765Sjlemon 32959765Sjlemon/* 33059765Sjlemon * Open a file. 33159765Sjlemon */ 33259765Sjlemonstatic int 33359765Sjlemonext2fs_open(const char *upath, struct open_file *f) 33459765Sjlemon{ 33559765Sjlemon struct file *fp; 33659765Sjlemon struct ext2fs *fs; 33759765Sjlemon size_t buf_size; 33859765Sjlemon ino_t inumber, parent_inumber; 33959765Sjlemon int i, len, groups, bg_per_blk, blkgrps, mult; 34059765Sjlemon int nlinks = 0; 34159765Sjlemon int error = 0; 34259765Sjlemon char *cp, *ncp, *path = NULL, *buf = NULL; 34359765Sjlemon char namebuf[MAXPATHLEN+1]; 34459765Sjlemon char c; 34559765Sjlemon 34659765Sjlemon /* allocate file system specific data structure */ 34759765Sjlemon fp = malloc(sizeof(struct file)); 34859765Sjlemon if (fp == NULL) 34959765Sjlemon return (ENOMEM); 35059765Sjlemon bzero(fp, sizeof(struct file)); 35159765Sjlemon f->f_fsdata = (void *)fp; 35259765Sjlemon 35359765Sjlemon /* allocate space and read super block */ 35459765Sjlemon fs = (struct ext2fs *)malloc(sizeof(*fs)); 35559765Sjlemon fp->f_fs = fs; 356276079Sian twiddle(1); 35759765Sjlemon error = (f->f_dev->dv_strategy)(f->f_devdata, F_READ, 358298230Sallanjude EXT2_SBLOCK, 0, EXT2_SBSIZE, (char *)fs, &buf_size); 35959765Sjlemon if (error) 36059765Sjlemon goto out; 36159765Sjlemon 36259765Sjlemon if (buf_size != EXT2_SBSIZE || fs->fs_magic != EXT2_MAGIC) { 36359765Sjlemon error = EINVAL; 36459765Sjlemon goto out; 36559765Sjlemon } 36659765Sjlemon 36759765Sjlemon /* 36859765Sjlemon * compute in-core values for the superblock 36959765Sjlemon */ 37059765Sjlemon fs->fs_bshift = EXT2_MINBSHIFT + fs->fs_fd.fd_bsize; 37159765Sjlemon fs->fs_bsize = 1 << fs->fs_bshift; 37259765Sjlemon fs->fs_bmask = fs->fs_bsize - 1; 37359765Sjlemon 37459765Sjlemon fs->fs_fshift = EXT2_MINFSHIFT + fs->fs_fd.fd_fsize; 37559765Sjlemon fs->fs_fsize = 1 << fs->fs_fshift; 37659765Sjlemon fs->fs_fmask = fs->fs_fsize - 1; 37759765Sjlemon 37859765Sjlemon if (fs->fs_revision == EXT2_REV0) { 37959765Sjlemon fs->fs_isize = EXT2_R0_ISIZE; 38059765Sjlemon fs->fs_firstino = EXT2_R0_FIRSTINO; 38159765Sjlemon } else { 38259765Sjlemon fs->fs_isize = fs->fs_fd.fd_isize; 38359765Sjlemon fs->fs_firstino = fs->fs_fd.fd_firstino; 38459765Sjlemon } 38559765Sjlemon fs->fs_imask = fs->fs_isize - 1; 38659765Sjlemon fs->fs_ipb = fs->fs_bsize / fs->fs_isize; 38759765Sjlemon fs->fs_fsbtodb = (fs->fs_bsize / DEV_BSIZE) - 1; 38859765Sjlemon 38959765Sjlemon /* 39059765Sjlemon * we have to load in the "group descriptors" here 39159765Sjlemon */ 39259765Sjlemon groups = howmany(fs->fs_blocks - fs->fs_firstblk, fs->fs_bpg); 39359765Sjlemon bg_per_blk = fs->fs_bsize / sizeof(struct ext2blkgrp); 39459765Sjlemon blkgrps = howmany(groups, bg_per_blk); 39559765Sjlemon len = blkgrps * fs->fs_bsize; 39659765Sjlemon 39759765Sjlemon fp->f_bg = malloc(len); 398276079Sian twiddle(1); 39959765Sjlemon error = (f->f_dev->dv_strategy)(f->f_devdata, F_READ, 400298230Sallanjude EXT2_SBLOCK + EXT2_SBSIZE / DEV_BSIZE, 0, len, 40159765Sjlemon (char *)fp->f_bg, &buf_size); 40259765Sjlemon if (error) 40359765Sjlemon goto out; 40459765Sjlemon 40559765Sjlemon /* 40659765Sjlemon * XXX 40759765Sjlemon * validation of values? (blocksize, descriptors, etc?) 40859765Sjlemon */ 40959765Sjlemon 41059765Sjlemon /* 41159765Sjlemon * Calculate indirect block levels. 41259765Sjlemon */ 41359765Sjlemon mult = 1; 41459765Sjlemon for (i = 0; i < NIADDR; i++) { 41559765Sjlemon mult *= nindir(fs); 41659765Sjlemon fp->f_nindir[i] = mult; 41759765Sjlemon } 41859765Sjlemon 41959765Sjlemon inumber = EXT2_ROOTINO; 42059765Sjlemon if ((error = read_inode(inumber, f)) != 0) 42159765Sjlemon goto out; 42259765Sjlemon 42359765Sjlemon path = strdup(upath); 42459765Sjlemon if (path == NULL) { 42559765Sjlemon error = ENOMEM; 42659765Sjlemon goto out; 42759765Sjlemon } 42859765Sjlemon cp = path; 42959765Sjlemon while (*cp) { 43059765Sjlemon /* 43159765Sjlemon * Remove extra separators 43259765Sjlemon */ 43359765Sjlemon while (*cp == '/') 43459765Sjlemon cp++; 43559765Sjlemon if (*cp == '\0') 43659765Sjlemon break; 43759765Sjlemon 43859765Sjlemon /* 43959765Sjlemon * Check that current node is a directory. 44059765Sjlemon */ 44159765Sjlemon if (! S_ISDIR(fp->f_di.di_mode)) { 44259765Sjlemon error = ENOTDIR; 44359765Sjlemon goto out; 44459765Sjlemon } 44559765Sjlemon 44659765Sjlemon /* 44759765Sjlemon * Get next component of path name. 44859765Sjlemon */ 44959765Sjlemon len = 0; 45059765Sjlemon 45159765Sjlemon ncp = cp; 45259765Sjlemon while ((c = *cp) != '\0' && c != '/') { 45359765Sjlemon if (++len > EXT2_MAXNAMLEN) { 45459765Sjlemon error = ENOENT; 45559765Sjlemon goto out; 45659765Sjlemon } 45759765Sjlemon cp++; 45859765Sjlemon } 45959765Sjlemon *cp = '\0'; 46059765Sjlemon 46159765Sjlemon /* 46259765Sjlemon * Look up component in current directory. 46359765Sjlemon * Save directory inumber in case we find a 46459765Sjlemon * symbolic link. 46559765Sjlemon */ 46659765Sjlemon parent_inumber = inumber; 46759765Sjlemon error = search_directory(ncp, f, &inumber); 46859765Sjlemon *cp = c; 46959765Sjlemon if (error) 47059765Sjlemon goto out; 47159765Sjlemon 47259765Sjlemon /* 47359765Sjlemon * Open next component. 47459765Sjlemon */ 47559765Sjlemon if ((error = read_inode(inumber, f)) != 0) 47659765Sjlemon goto out; 47759765Sjlemon 47859765Sjlemon /* 47959765Sjlemon * Check for symbolic link. 48059765Sjlemon */ 48159765Sjlemon if (S_ISLNK(fp->f_di.di_mode)) { 48259765Sjlemon int link_len = fp->f_di.di_size; 48359765Sjlemon int len; 48459765Sjlemon 48559765Sjlemon len = strlen(cp); 48659765Sjlemon if (link_len + len > MAXPATHLEN || 48759765Sjlemon ++nlinks > MAXSYMLINKS) { 48859765Sjlemon error = ENOENT; 48959765Sjlemon goto out; 49059765Sjlemon } 49159765Sjlemon 49259765Sjlemon bcopy(cp, &namebuf[link_len], len + 1); 49359765Sjlemon if (fp->f_di.di_nblk == 0) { 49459765Sjlemon bcopy(fp->f_di.di_shortlink, 49559765Sjlemon namebuf, link_len); 49659765Sjlemon } else { 49759765Sjlemon /* 49859765Sjlemon * Read file for symbolic link 49959765Sjlemon */ 50059765Sjlemon struct ext2fs *fs = fp->f_fs; 50159765Sjlemon daddr_t disk_block; 50259765Sjlemon size_t buf_size; 50359765Sjlemon 50459765Sjlemon if (! buf) 50559765Sjlemon buf = malloc(fs->fs_bsize); 50659765Sjlemon error = block_map(f, (daddr_t)0, &disk_block); 50759765Sjlemon if (error) 50859765Sjlemon goto out; 50959765Sjlemon 510276079Sian twiddle(1); 51159765Sjlemon error = (f->f_dev->dv_strategy)(f->f_devdata, 512298230Sallanjude F_READ, fsb_to_db(fs, disk_block), 0, 51359765Sjlemon fs->fs_bsize, buf, &buf_size); 51459765Sjlemon if (error) 51559765Sjlemon goto out; 51659765Sjlemon 51759765Sjlemon bcopy((char *)buf, namebuf, link_len); 51859765Sjlemon } 51959765Sjlemon 52059765Sjlemon /* 52159765Sjlemon * If relative pathname, restart at parent directory. 52259765Sjlemon * If absolute pathname, restart at root. 52359765Sjlemon */ 52459765Sjlemon cp = namebuf; 52559765Sjlemon if (*cp != '/') 52659765Sjlemon inumber = parent_inumber; 52759765Sjlemon else 52859765Sjlemon inumber = (ino_t)EXT2_ROOTINO; 52959765Sjlemon 53059765Sjlemon if ((error = read_inode(inumber, f)) != 0) 53159765Sjlemon goto out; 53259765Sjlemon } 53359765Sjlemon } 53459765Sjlemon 53559765Sjlemon /* 53659765Sjlemon * Found terminal component. 53759765Sjlemon */ 53859765Sjlemon error = 0; 539251561Spfg fp->f_seekp = 0; 54059765Sjlemonout: 54159765Sjlemon if (buf) 54259765Sjlemon free(buf); 54359765Sjlemon if (path) 54459765Sjlemon free(path); 54559765Sjlemon if (error) { 54659765Sjlemon if (fp->f_buf) 54759765Sjlemon free(fp->f_buf); 54859765Sjlemon free(fp->f_fs); 54959765Sjlemon free(fp); 55059765Sjlemon } 55159765Sjlemon return (error); 55259765Sjlemon} 55359765Sjlemon 55459765Sjlemon/* 55559765Sjlemon * Read a new inode into a file structure. 55659765Sjlemon */ 55759765Sjlemonstatic int 55859765Sjlemonread_inode(ino_t inumber, struct open_file *f) 55959765Sjlemon{ 56059765Sjlemon struct file *fp = (struct file *)f->f_fsdata; 56159765Sjlemon struct ext2fs *fs = fp->f_fs; 56259765Sjlemon struct ext2dinode *dp; 56359765Sjlemon char *buf; 56459765Sjlemon size_t rsize; 56559765Sjlemon int level, error = 0; 56659765Sjlemon 56759765Sjlemon /* 56859765Sjlemon * Read inode and save it. 56959765Sjlemon */ 57059765Sjlemon buf = malloc(fs->fs_bsize); 571276079Sian twiddle(1); 57259765Sjlemon error = (f->f_dev->dv_strategy)(f->f_devdata, F_READ, 573298230Sallanjude ino_to_db(fs, fp->f_bg, inumber), 0, fs->fs_bsize, buf, &rsize); 57459765Sjlemon if (error) 57559765Sjlemon goto out; 57659765Sjlemon if (rsize != fs->fs_bsize) { 57759765Sjlemon error = EIO; 57859765Sjlemon goto out; 57959765Sjlemon } 58059765Sjlemon 58159765Sjlemon dp = (struct ext2dinode *)buf; 58259765Sjlemon fp->f_di = dp[ino_to_bo(fs, inumber)]; 58359765Sjlemon 58459765Sjlemon /* clear out old buffers */ 58559765Sjlemon for (level = 0; level < NIADDR; level++) 58659765Sjlemon fp->f_blkno[level] = -1; 58759765Sjlemon fp->f_buf_blkno = -1; 588251561Spfg fp->f_seekp = 0; 58959765Sjlemon 59059765Sjlemonout: 59159765Sjlemon free(buf); 59259765Sjlemon return (error); 59359765Sjlemon} 59459765Sjlemon 59559765Sjlemon/* 59659765Sjlemon * Given an offset in a file, find the disk block number that 59759765Sjlemon * contains that block. 59859765Sjlemon */ 59959765Sjlemonstatic int 60059765Sjlemonblock_map(struct open_file *f, daddr_t file_block, daddr_t *disk_block_p) 60159765Sjlemon{ 60259765Sjlemon struct file *fp = (struct file *)f->f_fsdata; 60359765Sjlemon struct ext2fs *fs = fp->f_fs; 60459765Sjlemon daddr_t ind_block_num; 60596941Siedowse int32_t *ind_p; 60659765Sjlemon int idx, level; 60759765Sjlemon int error; 60859765Sjlemon 60959765Sjlemon /* 61059765Sjlemon * Index structure of an inode: 61159765Sjlemon * 61259765Sjlemon * di_db[0..NDADDR-1] hold block numbers for blocks 61359765Sjlemon * 0..NDADDR-1 61459765Sjlemon * 61559765Sjlemon * di_ib[0] index block 0 is the single indirect block 61659765Sjlemon * holds block numbers for blocks 61759765Sjlemon * NDADDR .. NDADDR + NINDIR(fs)-1 61859765Sjlemon * 61959765Sjlemon * di_ib[1] index block 1 is the double indirect block 62059765Sjlemon * holds block numbers for INDEX blocks for blocks 62159765Sjlemon * NDADDR + NINDIR(fs) .. 62259765Sjlemon * NDADDR + NINDIR(fs) + NINDIR(fs)**2 - 1 62359765Sjlemon * 62459765Sjlemon * di_ib[2] index block 2 is the triple indirect block 62559765Sjlemon * holds block numbers for double-indirect 62659765Sjlemon * blocks for blocks 62759765Sjlemon * NDADDR + NINDIR(fs) + NINDIR(fs)**2 .. 62859765Sjlemon * NDADDR + NINDIR(fs) + NINDIR(fs)**2 62959765Sjlemon * + NINDIR(fs)**3 - 1 63059765Sjlemon */ 63159765Sjlemon 63259765Sjlemon if (file_block < NDADDR) { 63359765Sjlemon /* Direct block. */ 63459765Sjlemon *disk_block_p = fp->f_di.di_db[file_block]; 63559765Sjlemon return (0); 63659765Sjlemon } 63759765Sjlemon 63859765Sjlemon file_block -= NDADDR; 63959765Sjlemon 64059765Sjlemon /* 64159765Sjlemon * nindir[0] = NINDIR 64259765Sjlemon * nindir[1] = NINDIR**2 64359765Sjlemon * nindir[2] = NINDIR**3 64459765Sjlemon * etc 64559765Sjlemon */ 64659765Sjlemon for (level = 0; level < NIADDR; level++) { 64759765Sjlemon if (file_block < fp->f_nindir[level]) 64859765Sjlemon break; 64959765Sjlemon file_block -= fp->f_nindir[level]; 65059765Sjlemon } 65159765Sjlemon if (level == NIADDR) { 65259765Sjlemon /* Block number too high */ 65359765Sjlemon return (EFBIG); 65459765Sjlemon } 65559765Sjlemon 65659765Sjlemon ind_block_num = fp->f_di.di_ib[level]; 65759765Sjlemon 65859765Sjlemon for (; level >= 0; level--) { 65959765Sjlemon if (ind_block_num == 0) { 66059765Sjlemon *disk_block_p = 0; /* missing */ 66159765Sjlemon return (0); 66259765Sjlemon } 66359765Sjlemon 66459765Sjlemon if (fp->f_blkno[level] != ind_block_num) { 66559765Sjlemon if (fp->f_blk[level] == (char *)0) 66659765Sjlemon fp->f_blk[level] = 66759765Sjlemon malloc(fs->fs_bsize); 668276079Sian twiddle(1); 66959765Sjlemon error = (f->f_dev->dv_strategy)(f->f_devdata, F_READ, 670298230Sallanjude fsb_to_db(fp->f_fs, ind_block_num), 0, fs->fs_bsize, 67159765Sjlemon fp->f_blk[level], &fp->f_blksize[level]); 67259765Sjlemon if (error) 67359765Sjlemon return (error); 67459765Sjlemon if (fp->f_blksize[level] != fs->fs_bsize) 67559765Sjlemon return (EIO); 67659765Sjlemon fp->f_blkno[level] = ind_block_num; 67759765Sjlemon } 67859765Sjlemon 67996941Siedowse ind_p = (int32_t *)fp->f_blk[level]; 68059765Sjlemon 68159765Sjlemon if (level > 0) { 68259765Sjlemon idx = file_block / fp->f_nindir[level - 1]; 68359765Sjlemon file_block %= fp->f_nindir[level - 1]; 68459765Sjlemon } else { 68559765Sjlemon idx = file_block; 68659765Sjlemon } 68759765Sjlemon ind_block_num = ind_p[idx]; 68859765Sjlemon } 68959765Sjlemon 69059765Sjlemon *disk_block_p = ind_block_num; 69159765Sjlemon 69259765Sjlemon return (0); 69359765Sjlemon} 69459765Sjlemon 69559765Sjlemon/* 69659765Sjlemon * Read a portion of a file into an internal buffer. Return 69759765Sjlemon * the location in the buffer and the amount in the buffer. 69859765Sjlemon */ 69959765Sjlemonstatic int 70059765Sjlemonbuf_read_file(struct open_file *f, char **buf_p, size_t *size_p) 70159765Sjlemon{ 70259765Sjlemon struct file *fp = (struct file *)f->f_fsdata; 70359765Sjlemon struct ext2fs *fs = fp->f_fs; 70459765Sjlemon long off; 70559765Sjlemon daddr_t file_block; 70659765Sjlemon daddr_t disk_block; 70759765Sjlemon size_t block_size; 70859765Sjlemon int error = 0; 70959765Sjlemon 71059765Sjlemon off = blkoff(fs, fp->f_seekp); 71159765Sjlemon file_block = lblkno(fs, fp->f_seekp); 71259765Sjlemon block_size = dblksize(fs, &fp->f_di, file_block); 71359765Sjlemon 71459765Sjlemon if (file_block != fp->f_buf_blkno) { 71559765Sjlemon error = block_map(f, file_block, &disk_block); 71659765Sjlemon if (error) 71759765Sjlemon goto done; 71859765Sjlemon 71959765Sjlemon if (fp->f_buf == (char *)0) 72059765Sjlemon fp->f_buf = malloc(fs->fs_bsize); 72159765Sjlemon 72259765Sjlemon if (disk_block == 0) { 72359765Sjlemon bzero(fp->f_buf, block_size); 72459765Sjlemon fp->f_buf_size = block_size; 72559765Sjlemon } else { 726276079Sian twiddle(4); 72759765Sjlemon error = (f->f_dev->dv_strategy)(f->f_devdata, F_READ, 728298230Sallanjude fsb_to_db(fs, disk_block), 0, block_size, 72959765Sjlemon fp->f_buf, &fp->f_buf_size); 73059765Sjlemon if (error) 73159765Sjlemon goto done; 73259765Sjlemon } 73359765Sjlemon fp->f_buf_blkno = file_block; 73459765Sjlemon } 73559765Sjlemon 73659765Sjlemon /* 73759765Sjlemon * Return address of byte in buffer corresponding to 73859765Sjlemon * offset, and size of remainder of buffer after that 73959765Sjlemon * byte. 74059765Sjlemon */ 74159765Sjlemon *buf_p = fp->f_buf + off; 74259765Sjlemon *size_p = block_size - off; 74359765Sjlemon 74459765Sjlemon /* 74559765Sjlemon * But truncate buffer at end of file. 74659765Sjlemon */ 74759765Sjlemon if (*size_p > fp->f_di.di_size - fp->f_seekp) 74859765Sjlemon *size_p = fp->f_di.di_size - fp->f_seekp; 74959765Sjlemondone: 75059765Sjlemon return (error); 75159765Sjlemon} 75259765Sjlemon 75359765Sjlemon/* 75459765Sjlemon * Search a directory for a name and return its 75559765Sjlemon * i_number. 75659765Sjlemon */ 75759765Sjlemonstatic int 75859765Sjlemonsearch_directory(char *name, struct open_file *f, ino_t *inumber_p) 75959765Sjlemon{ 76059765Sjlemon struct file *fp = (struct file *)f->f_fsdata; 76159765Sjlemon struct ext2dirent *dp, *edp; 76259765Sjlemon char *buf; 76359765Sjlemon size_t buf_size; 76459765Sjlemon int namlen, length; 76559765Sjlemon int error; 76659765Sjlemon 76759765Sjlemon length = strlen(name); 76859765Sjlemon fp->f_seekp = 0; 76959765Sjlemon while (fp->f_seekp < fp->f_di.di_size) { 77059765Sjlemon error = buf_read_file(f, &buf, &buf_size); 77159765Sjlemon if (error) 77259765Sjlemon return (error); 77359765Sjlemon dp = (struct ext2dirent *)buf; 77459765Sjlemon edp = (struct ext2dirent *)(buf + buf_size); 77559765Sjlemon while (dp < edp) { 77659765Sjlemon if (dp->d_ino == (ino_t)0) 77759765Sjlemon goto next; 77859765Sjlemon namlen = dp->d_namlen; 77959765Sjlemon if (namlen == length && 78059765Sjlemon strncmp(name, dp->d_name, length) == 0) { 78159765Sjlemon /* found entry */ 78259765Sjlemon *inumber_p = dp->d_ino; 78359765Sjlemon return (0); 78459765Sjlemon } 78559765Sjlemon next: 78659765Sjlemon dp = (struct ext2dirent *)((char *)dp + dp->d_reclen); 78759765Sjlemon } 78859765Sjlemon fp->f_seekp += buf_size; 78959765Sjlemon } 79059765Sjlemon return (ENOENT); 79159765Sjlemon} 79259765Sjlemon 79359765Sjlemonstatic int 79459765Sjlemonext2fs_close(struct open_file *f) 79559765Sjlemon{ 79659765Sjlemon struct file *fp = (struct file *)f->f_fsdata; 79759765Sjlemon int level; 79859765Sjlemon 79959765Sjlemon f->f_fsdata = (void *)0; 80059765Sjlemon if (fp == (struct file *)0) 80159765Sjlemon return (0); 80259765Sjlemon 80359765Sjlemon for (level = 0; level < NIADDR; level++) { 80459765Sjlemon if (fp->f_blk[level]) 80559765Sjlemon free(fp->f_blk[level]); 80659765Sjlemon } 80759765Sjlemon if (fp->f_buf) 80859765Sjlemon free(fp->f_buf); 80959765Sjlemon if (fp->f_bg) 81059765Sjlemon free(fp->f_bg); 81159765Sjlemon free(fp->f_fs); 81259765Sjlemon free(fp); 81359765Sjlemon return (0); 81459765Sjlemon} 81559765Sjlemon 81659765Sjlemonstatic int 81759765Sjlemonext2fs_read(struct open_file *f, void *addr, size_t size, size_t *resid) 81859765Sjlemon{ 81959765Sjlemon struct file *fp = (struct file *)f->f_fsdata; 82059765Sjlemon size_t csize, buf_size; 82159765Sjlemon char *buf; 82259765Sjlemon int error = 0; 82359765Sjlemon 82459765Sjlemon while (size != 0) { 82559765Sjlemon if (fp->f_seekp >= fp->f_di.di_size) 82659765Sjlemon break; 82759765Sjlemon 82859765Sjlemon error = buf_read_file(f, &buf, &buf_size); 82959765Sjlemon if (error) 83059765Sjlemon break; 83159765Sjlemon 83259765Sjlemon csize = size; 83359765Sjlemon if (csize > buf_size) 83459765Sjlemon csize = buf_size; 83559765Sjlemon 83659765Sjlemon bcopy(buf, addr, csize); 83759765Sjlemon 83859765Sjlemon fp->f_seekp += csize; 839136093Sstefanf addr = (char *)addr + csize; 84059765Sjlemon size -= csize; 84159765Sjlemon } 84259765Sjlemon if (resid) 84359765Sjlemon *resid = size; 84459765Sjlemon return (error); 84559765Sjlemon} 84659765Sjlemon 84759765Sjlemonstatic off_t 84859765Sjlemonext2fs_seek(struct open_file *f, off_t offset, int where) 84959765Sjlemon{ 85059765Sjlemon struct file *fp = (struct file *)f->f_fsdata; 85159765Sjlemon 85259765Sjlemon switch (where) { 85359765Sjlemon case SEEK_SET: 85459765Sjlemon fp->f_seekp = offset; 85559765Sjlemon break; 85659765Sjlemon case SEEK_CUR: 85759765Sjlemon fp->f_seekp += offset; 85859765Sjlemon break; 85959765Sjlemon case SEEK_END: 86059765Sjlemon fp->f_seekp = fp->f_di.di_size - offset; 86159765Sjlemon break; 86259765Sjlemon default: 863124811Sjhb errno = EINVAL; 86459765Sjlemon return (-1); 86559765Sjlemon } 86659765Sjlemon return (fp->f_seekp); 86759765Sjlemon} 86859765Sjlemon 86959765Sjlemonstatic int 87059765Sjlemonext2fs_stat(struct open_file *f, struct stat *sb) 87159765Sjlemon{ 87259765Sjlemon struct file *fp = (struct file *)f->f_fsdata; 87359765Sjlemon 87459765Sjlemon /* only important stuff */ 87559765Sjlemon sb->st_mode = fp->f_di.di_mode; 87659765Sjlemon sb->st_uid = fp->f_di.di_uid; 87759765Sjlemon sb->st_gid = fp->f_di.di_gid; 87859765Sjlemon sb->st_size = fp->f_di.di_size; 87959765Sjlemon return (0); 88059765Sjlemon} 88159765Sjlemon 88259765Sjlemonstatic int 88359765Sjlemonext2fs_readdir(struct open_file *f, struct dirent *d) 88459765Sjlemon{ 88559765Sjlemon struct file *fp = (struct file *)f->f_fsdata; 88659765Sjlemon struct ext2dirent *ed; 88759765Sjlemon char *buf; 88859765Sjlemon size_t buf_size; 88959765Sjlemon int error; 89059765Sjlemon 89159765Sjlemon /* 89259765Sjlemon * assume that a directory entry will not be split across blocks 89359765Sjlemon */ 89459765Sjlemonagain: 89559765Sjlemon if (fp->f_seekp >= fp->f_di.di_size) 89659765Sjlemon return (ENOENT); 89759765Sjlemon error = buf_read_file(f, &buf, &buf_size); 89859765Sjlemon if (error) 89959765Sjlemon return (error); 90059765Sjlemon ed = (struct ext2dirent *)buf; 90159765Sjlemon fp->f_seekp += ed->d_reclen; 90259765Sjlemon if (ed->d_ino == (ino_t)0) 90359765Sjlemon goto again; 90459765Sjlemon d->d_type = EXTFTODT(ed->d_type); 90559765Sjlemon strncpy(d->d_name, ed->d_name, ed->d_namlen); 90659765Sjlemon d->d_name[ed->d_namlen] = '\0'; 90759765Sjlemon return (0); 90859765Sjlemon} 909