| /* |
| * This file is subject to the terms and conditions of the GNU General Public |
| * License. See the file "COPYING" in the main directory of this archive |
| * for more details. |
| * |
| * Copyright (C) Hewlett-Packard (Paul Bame) paul_bame@hp.com |
| */ |
| /* |
| * This is a set of functions that provides minimal filesystem |
| * functionality to the Linux bootstrapper. All we can do is |
| * open and read files... but that's all we need 8-) |
| * |
| * This file has been ported from the DEC 32-bit Linux version |
| * by David Mosberger (davidm@cs.arizona.edu). |
| */ |
| |
| /* The following defines are necessary to satisfy ext2_fs.h and |
| * things it pulls in. |
| */ |
| typedef unsigned int u32; |
| typedef unsigned char u8; |
| #define GFP_KERNEL 0 |
| |
| #include <linux/stat.h> |
| #include <linux/types.h> |
| #include <linux/ext2_fs.h> |
| #include "bootloader.h" |
| |
| #define MAX_OPEN_FILES 5 |
| |
| extern struct bootfs ext2fs; |
| |
| static struct ext2_super_block sb; |
| static struct ext2_group_desc *gds; |
| static struct ext2_inode *root_inode = NULL; |
| static int ngroups = 0; |
| static int directlim; /* Maximum direct blkno */ |
| static int ind1lim; /* Maximum single-indir blkno */ |
| static int ind2lim; /* Maximum double-indir blkno */ |
| static int ptrs_per_blk; /* ptrs/indirect block */ |
| static char blkbuf[EXT2_MAX_BLOCK_SIZE]; |
| static int cached_iblkno = -1; |
| static char iblkbuf[EXT2_MAX_BLOCK_SIZE]; |
| static int cached_diblkno = -1; |
| static char diblkbuf[EXT2_MAX_BLOCK_SIZE]; |
| static long dev = -1; |
| static long partition_offset; |
| |
| struct ext2_inode *fd2inode[MAX_FD]; |
| |
| static struct inode_table_entry { |
| struct ext2_inode inode; |
| int inumber; |
| int free; |
| unsigned short old_mode; |
| } inode_table[MAX_OPEN_FILES]; |
| |
| /* to work with existing palo I/O */ |
| #define cons_read(dev, addr, length, seekptr) \ |
| seekread(dev, (void *)addr, length, seekptr) |
| |
| #define inplace(xform, var) (var) = xform((var)) |
| |
| #undef DEBUG |
| #define Debug 0 |
| |
| |
| static void swapsb(struct ext2_super_block *sb) |
| { |
| inplace(__le32_to_cpu, sb->s_inodes_count); |
| inplace(__le32_to_cpu, sb->s_blocks_count); |
| inplace(__le32_to_cpu, sb->s_r_blocks_count); |
| inplace(__le32_to_cpu, sb->s_free_blocks_count); |
| inplace(__le32_to_cpu, sb->s_free_inodes_count); |
| inplace(__le32_to_cpu, sb->s_first_data_block); |
| inplace(__le32_to_cpu, sb->s_log_block_size); |
| inplace(__le32_to_cpu, sb->s_log_frag_size); |
| inplace(__le32_to_cpu, sb->s_blocks_per_group); |
| inplace(__le32_to_cpu, sb->s_frags_per_group); |
| inplace(__le32_to_cpu, sb->s_inodes_per_group); |
| inplace(__le32_to_cpu, sb->s_mtime); |
| inplace(__le32_to_cpu, sb->s_wtime); |
| |
| inplace(__le16_to_cpu, sb->s_mnt_count); |
| inplace(__le16_to_cpu, sb->s_max_mnt_count); |
| inplace(__le16_to_cpu, sb->s_magic); |
| inplace(__le16_to_cpu, sb->s_state); |
| inplace(__le16_to_cpu, sb->s_errors); |
| inplace(__le16_to_cpu, sb->s_minor_rev_level); |
| |
| inplace(__le32_to_cpu, sb->s_lastcheck); |
| inplace(__le32_to_cpu, sb->s_checkinterval); |
| inplace(__le32_to_cpu, sb->s_creator_os); |
| inplace(__le32_to_cpu, sb->s_rev_level); |
| |
| inplace(__le16_to_cpu, sb->s_def_resuid); |
| inplace(__le16_to_cpu, sb->s_def_resgid); |
| |
| inplace(__le32_to_cpu, sb->s_first_ino); |
| |
| inplace(__le16_to_cpu, sb->s_inode_size); |
| inplace(__le16_to_cpu, sb->s_block_group_nr); |
| |
| inplace(__le32_to_cpu, sb->s_feature_compat); |
| inplace(__le32_to_cpu, sb->s_feature_incompat); |
| inplace(__le32_to_cpu, sb->s_feature_ro_compat); |
| inplace(__le32_to_cpu, sb->s_algorithm_usage_bitmap); |
| |
| /* whew! */ |
| } |
| |
| static void swapgrp(struct ext2_group_desc *g) |
| { |
| inplace(__le32_to_cpu, g->bg_block_bitmap); |
| inplace(__le32_to_cpu, g->bg_inode_bitmap); |
| inplace(__le32_to_cpu, g->bg_inode_table); |
| |
| inplace(__le16_to_cpu, g->bg_free_blocks_count); |
| inplace(__le16_to_cpu, g->bg_free_inodes_count); |
| inplace(__le16_to_cpu, g->bg_used_dirs_count); |
| |
| } |
| |
| static void swapino(struct ext2_inode *i) |
| { |
| int n; |
| |
| inplace(__le16_to_cpu, i->i_mode); |
| inplace(__le16_to_cpu, i->i_uid); |
| inplace(__le32_to_cpu, i->i_size); |
| inplace(__le32_to_cpu, i->i_atime); |
| inplace(__le32_to_cpu, i->i_ctime); |
| inplace(__le32_to_cpu, i->i_mtime); |
| inplace(__le32_to_cpu, i->i_dtime); |
| inplace(__le16_to_cpu, i->i_gid); |
| inplace(__le16_to_cpu, i->i_links_count); |
| inplace(__le32_to_cpu, i->i_blocks); |
| inplace(__le32_to_cpu, i->i_flags); |
| /* Don't know how to handle the unions right now -- ignore them */ |
| #if 0 |
| union { |
| struct { |
| __u32 l_i_reserved1; |
| } linux1; |
| struct { |
| __u32 h_i_translator; |
| } hurd1; |
| struct { |
| __u32 m_i_reserved1; |
| } masix1; |
| } osd1; /* OS dependent 1 */ |
| #endif |
| for (n = 0; n < EXT2_N_BLOCKS; n++) { |
| inplace(__le32_to_cpu, i->i_block[n]); |
| } |
| inplace(__le32_to_cpu, i->i_generation); |
| inplace(__le32_to_cpu, i->i_file_acl); |
| inplace(__le32_to_cpu, i->i_dir_acl); |
| inplace(__le32_to_cpu, i->i_faddr); |
| #if 0 |
| union { |
| struct { |
| __u8 l_i_frag; /* Fragment number */ |
| __u8 l_i_fsize; /* Fragment size */ |
| __u16 i_pad1; |
| __u32 l_i_reserved2[2]; |
| } linux2; |
| struct { |
| __u8 h_i_frag; /* Fragment number */ |
| __u8 h_i_fsize; /* Fragment size */ |
| __u16 h_i_mode_high; |
| __u16 h_i_uid_high; |
| __u16 h_i_gid_high; |
| __u32 h_i_author; |
| } hurd2; |
| struct { |
| __u8 m_i_frag; /* Fragment number */ |
| __u8 m_i_fsize; /* Fragment size */ |
| __u16 m_pad1; |
| __u32 m_i_reserved2[2]; |
| } masix2; |
| } osd2; /* OS dependent 2 */ |
| #endif |
| } |
| |
| static void swapde(struct ext2_dir_entry_2 *de) |
| { |
| if (Debug) printf("swapde: 0x%x 0x%x / ", |
| de->inode, de->rec_len); |
| inplace(__le32_to_cpu, de->inode); |
| inplace(__le16_to_cpu, de->rec_len); |
| if (Debug) printf("0x%x 0x%x %d %d %.*s\n", |
| de->inode, de->rec_len, |
| de->name_len, de->file_type, |
| de->name_len, de->name); |
| } |
| |
| /* |
| * Initialize an ext2 partition starting at offset P_OFFSET; this is |
| * sort-of the same idea as "mounting" it. Read in the relevant |
| * control structures and make them available to the user. Returns 0 |
| * if successful, -1 on failure. |
| */ |
| int ext2_mount(long cons_dev, long p_offset, long quiet) |
| { |
| long sb_block = 1; |
| long sb_offset; |
| int i; |
| |
| dev = cons_dev; |
| partition_offset = p_offset; |
| |
| /* initialize the inode table */ |
| for (i = 0; i < MAX_OPEN_FILES; i++) { |
| inode_table[i].free = 1; |
| inode_table[i].inumber = 0; |
| } |
| /* clear the root inode pointer (very important!) */ |
| root_inode = NULL; |
| |
| /* read in the first superblock */ |
| sb_offset = sb_block * EXT2_MIN_BLOCK_SIZE; |
| if (cons_read(dev, (char *)&sb, sizeof(sb), partition_offset + sb_offset) |
| != sizeof(sb)) |
| { |
| printf("ext2 sb read failed\n"); |
| return -1; |
| } |
| |
| if (__le16_to_cpu(sb.s_magic) != EXT2_SUPER_MAGIC) { |
| if (!quiet) { |
| printf("ext2_init: bad magic 0x%x\n", sb.s_magic); |
| } |
| return -1; |
| } |
| |
| swapsb(&sb); |
| |
| ngroups = (sb.s_blocks_count - |
| sb.s_first_data_block + |
| EXT2_BLOCKS_PER_GROUP(&sb) - 1) |
| / EXT2_BLOCKS_PER_GROUP(&sb); |
| |
| gds = (struct ext2_group_desc *) |
| malloc((size_t)(ngroups * sizeof(struct ext2_group_desc))); |
| |
| ext2fs.blocksize = EXT2_BLOCK_SIZE(&sb); |
| if (Debug) printf("ext2 block size %d\n", ext2fs.blocksize); |
| |
| /* read in the group descriptors (immediately follows superblock) */ |
| cons_read(dev, gds, ngroups * sizeof(struct ext2_group_desc), |
| partition_offset + |
| ext2fs.blocksize * (EXT2_MIN_BLOCK_SIZE/ext2fs.blocksize + 1)); |
| for (i = 0; i < ngroups; i++) |
| { |
| swapgrp(&gds[i]); |
| } |
| /* |
| * Calculate direct/indirect block limits for this file system |
| * (blocksize dependent): |
| */ |
| ext2fs.blocksize = EXT2_BLOCK_SIZE(&sb); |
| if (Debug) printf("ext2 block size %d\n", ext2fs.blocksize); |
| |
| directlim = EXT2_NDIR_BLOCKS - 1; |
| ptrs_per_blk = ext2fs.blocksize/sizeof(unsigned int); |
| ind1lim = ptrs_per_blk + directlim; |
| ind2lim = (ptrs_per_blk * ptrs_per_blk) + directlim; |
| |
| return 0; |
| } |
| |
| static void printinode(const char *s, struct ext2_inode *ip) |
| { |
| int i; |
| |
| printf("%s: inode %p mode 0%o uid %d gid %d size %d\n", |
| s, ip, ip->i_mode, ip->i_uid, ip->i_gid, ip->i_size); |
| |
| for (i = 0; i < EXT2_NDIR_BLOCKS; i++) |
| { |
| printf("\tblock %d at %d (0x%x)\n", i, ip->i_block[i], |
| ip->i_block[i]); |
| } |
| } |
| |
| /* |
| * Read the specified inode from the disk and return it to the user. |
| * Returns NULL if the inode can't be read... |
| */ |
| static struct ext2_inode *ext2_iget(int ino) |
| { |
| int i; |
| struct ext2_inode *ip; |
| struct inode_table_entry *itp = 0; |
| int group; |
| long offset; |
| |
| |
| ip = 0; |
| for (i = 0; i < MAX_OPEN_FILES; i++) { |
| #ifdef DEBUG |
| printf("ext2_iget: looping, entry %d inode %d free %d\n", |
| i, inode_table[i].inumber, inode_table[i].free); |
| #endif |
| if (inode_table[i].free) { |
| itp = &inode_table[i]; |
| ip = &itp->inode; |
| break; |
| } |
| } |
| if (!ip) { |
| printf("ext2_iget: no free inodes\n"); |
| return NULL; |
| } |
| |
| group = (ino-1) / sb.s_inodes_per_group; |
| #ifdef DEBUG |
| printf("group is %d\n", group); |
| #endif |
| offset = partition_offset |
| + ((long) gds[group].bg_inode_table * (long)ext2fs.blocksize) |
| + (((ino - 1) % EXT2_INODES_PER_GROUP(&sb)) |
| * EXT2_INODE_SIZE(&sb)); |
| #ifdef DEBUG |
| printf("ext2_iget: reading %ld bytes at offset %ld " |
| "(%ld + (%d * %d) + ((%d) %% %d) * %d) " |
| "(inode %d -> table %d)\n", |
| sizeof(struct ext2_inode), offset, partition_offset, |
| gds[group].bg_inode_table, ext2fs.blocksize, |
| ino - 1, EXT2_INODES_PER_GROUP(&sb), EXT2_INODE_SIZE(&sb), |
| ino, (int) (itp - inode_table)); |
| #endif |
| if (cons_read(dev, ip, sizeof(struct ext2_inode), offset) |
| != sizeof(struct ext2_inode)) |
| { |
| printf("ext2_iget: read error\n"); |
| return NULL; |
| } |
| |
| swapino(ip); |
| if (Debug) printinode("iget", ip); |
| |
| itp->free = 0; |
| itp->inumber = ino; |
| itp->old_mode = ip->i_mode; |
| |
| return ip; |
| } |
| |
| |
| /* |
| * Release our hold on an inode. Since this is a read-only application, |
| * don't worry about putting back any changes... |
| */ |
| static void ext2_iput(struct ext2_inode *ip) |
| { |
| struct inode_table_entry *itp; |
| |
| /* Find and free the inode table slot we used... */ |
| itp = (struct inode_table_entry *)ip; |
| |
| #ifdef DEBUG |
| printf("ext2_iput: inode %d table %d\n", itp->inumber, |
| (int) (itp - inode_table)); |
| #endif |
| itp->inumber = 0; |
| itp->free = 1; |
| } |
| |
| |
| /* |
| * Map a block offset into a file into an absolute block number. |
| * (traverse the indirect blocks if necessary). Note: Double-indirect |
| * blocks allow us to map over 64Mb on a 1k file system. Therefore, for |
| * our purposes, we will NOT bother with triple indirect blocks. |
| * |
| * The "allocate" argument is set if we want to *allocate* a block |
| * and we don't already have one allocated. |
| */ |
| static int ext2_blkno(struct ext2_inode *ip, int blkoff) |
| { |
| unsigned int *lp; |
| unsigned int *ilp; |
| unsigned int *dlp; |
| int blkno; |
| int iblkno; |
| int diblkno; |
| unsigned long offset; |
| |
| ilp = (unsigned int *)iblkbuf; |
| dlp = (unsigned int *)diblkbuf; |
| lp = (unsigned int *)blkbuf; |
| |
| /* If it's a direct block, it's easy! */ |
| if (blkoff <= directlim) { |
| return ip->i_block[blkoff]; |
| } |
| |
| /* Is it a single-indirect? */ |
| if (blkoff <= ind1lim) { |
| iblkno = ip->i_block[EXT2_IND_BLOCK]; |
| |
| if (iblkno == 0) { |
| return 0; |
| } |
| |
| /* Read the indirect block */ |
| if (cached_iblkno != iblkno) { |
| offset = partition_offset + (long)iblkno * (long)ext2fs.blocksize; |
| if (cons_read(dev, iblkbuf, ext2fs.blocksize, offset) |
| != ext2fs.blocksize) |
| { |
| printf("ext2_blkno: error on iblk read\n"); |
| return 0; |
| } |
| cached_iblkno = iblkno; |
| } |
| |
| blkno = ilp[blkoff-(directlim+1)]; |
| inplace(__le32_to_cpu, blkno); |
| |
| return blkno; |
| } |
| |
| /* Is it a double-indirect? */ |
| if (blkoff <= ind2lim) { |
| /* Find the double-indirect block */ |
| diblkno = ip->i_block[EXT2_DIND_BLOCK]; |
| |
| if (diblkno == 0) { |
| return 0; |
| } |
| |
| /* Read in the double-indirect block */ |
| if (cached_diblkno != diblkno) { |
| offset = partition_offset + (long) diblkno * (long) ext2fs.blocksize; |
| if (cons_read(dev, diblkbuf, ext2fs.blocksize, offset) |
| != ext2fs.blocksize) |
| { |
| printf("ext2_blkno: err reading dindr blk\n"); |
| return 0; |
| } |
| cached_diblkno = diblkno; |
| } |
| |
| /* Find the single-indirect block pointer ... */ |
| iblkno = dlp[(blkoff - (ind1lim+1)) / ptrs_per_blk]; |
| inplace(__le32_to_cpu, iblkno); |
| |
| if (iblkno == 0) { |
| return 0; |
| } |
| |
| /* Read the indirect block */ |
| |
| if (cached_iblkno != iblkno) { |
| offset = partition_offset + (long) iblkno * (long) ext2fs.blocksize; |
| if (cons_read(dev, iblkbuf, ext2fs.blocksize, offset) |
| != ext2fs.blocksize) |
| { |
| printf("ext2_blkno: err on iblk read\n"); |
| return 0; |
| } |
| cached_iblkno = iblkno; |
| } |
| |
| /* Find the block itself. */ |
| blkno = ilp[(blkoff-(ind1lim+1)) % ptrs_per_blk]; |
| inplace(__le32_to_cpu, blkno); |
| return blkno; |
| } |
| |
| if (blkoff > ind2lim) { |
| printf("ext2_blkno: block number too large: %d\n", blkoff); |
| return 0; |
| } |
| return -1; |
| } |
| |
| static int print_ext2_blkno(struct ext2_inode *ip, int blkoff) |
| { |
| int r = ext2_blkno(ip, blkoff); |
| if (0) printf("ext2_blkno(%p, blk %d) ==> %d\n", |
| ip, blkoff, r); |
| |
| return r; |
| } |
| |
| |
| static int ext2_breadi(struct ext2_inode *ip, long blkno, long nblks, |
| char *buffer) |
| { |
| long dev_blkno, ncontig, offset, nbytes, tot_bytes; |
| |
| if (Debug) printf("ext2_breadi(%p, %ld, %ld, %p)\n", |
| ip, blkno, nblks, buffer); |
| |
| tot_bytes = 0; |
| if ((blkno+nblks)*ext2fs.blocksize > ip->i_size) |
| nblks = (ip->i_size + ext2fs.blocksize) / ext2fs.blocksize - blkno; |
| |
| if (Debug) printf("nblks = %ld\n", nblks); |
| while (nblks) { |
| /* |
| * Contiguous reads are a lot faster, so we try to group |
| * as many blocks as possible: |
| */ |
| ncontig = 0; nbytes = 0; |
| dev_blkno = print_ext2_blkno(ip, blkno); |
| if (Debug) printf("dev_blkno = %ld\n", dev_blkno); |
| do { |
| ++blkno; ++ncontig; --nblks; |
| nbytes += ext2fs.blocksize; |
| } while (nblks && |
| print_ext2_blkno(ip, blkno) == dev_blkno + ncontig); |
| |
| if (Debug) printf("dev_blkno = %ld\n", dev_blkno); |
| |
| if (dev_blkno == 0) { |
| /* This is a "hole" */ |
| memset(buffer, 0, nbytes); |
| } else { |
| /* Read it for real */ |
| offset = partition_offset + (long) dev_blkno* (long) ext2fs.blocksize; |
| #ifdef DEBUG |
| printf("ext2_bread: reading %ld bytes at offset %ld\n", |
| nbytes, offset); |
| #endif |
| if (cons_read(dev, buffer, nbytes, offset) |
| != nbytes) |
| { |
| printf("ext2_bread: read error\n"); |
| return -1; |
| } |
| |
| if (Debug) blockprint(offset, buffer, 128); |
| } |
| buffer += nbytes; |
| tot_bytes += nbytes; |
| } |
| return tot_bytes; |
| } |
| |
| static struct ext2_dir_entry_2 *ext2_readdiri(struct ext2_inode *dir_inode, |
| int rewind) |
| { |
| struct ext2_dir_entry_2 *dp; |
| static int diroffset = 0, blockoffset = 0; |
| |
| /* Reading a different directory, invalidate previous state */ |
| if (rewind) { |
| diroffset = 0; |
| blockoffset = 0; |
| /* read first block */ |
| if (ext2_breadi(dir_inode, 0, 1, blkbuf) < 0) |
| return NULL; |
| } |
| |
| #ifdef DEBUG |
| printf("ext2_readdiri: blkoffset %d diroffset %d len %d\n", |
| blockoffset, diroffset, dir_inode->i_size); |
| #endif |
| if (blockoffset >= ext2fs.blocksize) { |
| diroffset += ext2fs.blocksize; |
| if (diroffset >= dir_inode->i_size) |
| return NULL; |
| #ifdef DEBUG |
| printf("ext2_readdiri: reading block at %d\n", |
| diroffset); |
| #endif |
| /* assume that this will read the whole block */ |
| if (ext2_breadi(dir_inode, |
| diroffset / ext2fs.blocksize, |
| 1, blkbuf) < 0) |
| return NULL; |
| blockoffset = 0; |
| } |
| |
| dp = (struct ext2_dir_entry_2 *) (blkbuf + blockoffset); |
| swapde(dp); |
| blockoffset += dp->rec_len; |
| #ifdef DEBUG |
| printf("ext2_readdiri: returning %p = %.*s\n", dp, dp->name_len, dp->name); |
| #endif |
| return dp; |
| } |
| |
| static struct ext2_inode *ext2_namei(const char *name) |
| { |
| char namebuf[256]; |
| char *component; |
| struct ext2_inode *dir_inode; |
| struct ext2_dir_entry_2 *dp; |
| int next_ino; |
| |
| /* squirrel away a copy of "namebuf" that we can modify: */ |
| strcpy(namebuf, name); |
| |
| /* start at the root: */ |
| if (!root_inode) |
| root_inode = ext2_iget(EXT2_ROOT_INO); |
| dir_inode = root_inode; |
| if (!dir_inode) |
| return NULL; |
| |
| component = strtok(namebuf, "/"); |
| while (component) { |
| int component_length; |
| int rewind = 0; |
| /* |
| * Search for the specified component in the current |
| * directory inode. |
| */ |
| next_ino = -1; |
| component_length = strlen(component); |
| |
| /* rewind the first time through */ |
| while ((dp = ext2_readdiri(dir_inode, !rewind++))) { |
| if ((dp->name_len == component_length) && |
| (strncmp(component, dp->name, |
| component_length) == 0)) |
| { |
| /* Found it! */ |
| #ifdef DEBUG |
| printf("ext2_namei: found entry %s\n", |
| component); |
| #endif |
| next_ino = dp->inode; |
| break; |
| } |
| #ifdef DEBUG |
| printf("ext2_namei: looping\n"); |
| #endif |
| } |
| |
| #ifdef DEBUG |
| printf("ext2_namei: next_ino = %d\n", next_ino); |
| #endif |
| |
| /* |
| * At this point, we're done with this directory whether |
| * we've succeeded or failed... |
| */ |
| if (dir_inode != root_inode) |
| ext2_iput(dir_inode); |
| |
| /* |
| * If next_ino is negative, then we've failed (gone |
| * all the way through without finding anything) |
| */ |
| if (next_ino < 0) { |
| return NULL; |
| } |
| |
| /* |
| * Otherwise, we can get this inode and find the next |
| * component string... |
| */ |
| dir_inode = ext2_iget(next_ino); |
| if (!dir_inode) |
| return NULL; |
| |
| component = strtok(NULL, "/"); |
| } |
| |
| /* |
| * If we get here, then we got through all the components. |
| * Whatever we got must match up with the last one. |
| */ |
| return dir_inode; |
| } |
| |
| |
| /* |
| * Read block number "blkno" from the specified file. |
| */ |
| static int ext2_bread(int fd, long blkno, long nblks, char *buffer) |
| { |
| struct ext2_inode * ip; |
| ip = &inode_table[fd].inode; |
| return ext2_breadi(ip, blkno, nblks, buffer); |
| } |
| |
| /* |
| * Note: don't mix any kind of file lookup or other I/O with this or |
| * you will lose horribly (as it reuses blkbuf) |
| */ |
| const char * ext2_readdir(int fd, int rewind) |
| { |
| struct ext2_inode * ip = fd2inode[fd]; |
| struct ext2_dir_entry_2 * ent; |
| if (!S_ISDIR(ip->i_mode)) { |
| printf("fd %d (inode %d) is not a directory (mode %x)\n", |
| fd, inode_table[fd].inumber, ip->i_mode); |
| return NULL; |
| } |
| ent = ext2_readdiri(ip, rewind); |
| if (ent) { |
| ent->name[ent->name_len] = '\0'; |
| return ent->name; |
| } else { |
| return NULL; |
| } |
| } |
| |
| int ext2_filesize(int fd) |
| { |
| struct ext2_inode * ip = fd2inode[fd]; |
| |
| return ip->i_size; |
| } |
| |
| #if 0 |
| static int ext2_fstat(int fd, struct stat* buf) |
| { |
| struct ext2_inode * ip = &inode_table[fd].inode; |
| |
| if (fd >= MAX_OPEN_FILES) |
| return -1; |
| memset(buf, 0, sizeof(struct stat)); |
| /* fill in relevant fields */ |
| buf->st_ino = inode_table[fd].inumber; |
| buf->st_mode = ip->i_mode; |
| buf->st_flags = ip->i_flags; |
| buf->st_nlink = ip->i_links_count; |
| buf->st_uid = ip->i_uid; |
| buf->st_gid = ip->i_gid; |
| buf->st_size = ip->i_size; |
| buf->st_blocks = ip->i_blocks; |
| buf->st_atime = ip->i_atime; |
| buf->st_mtime = ip->i_mtime; |
| buf->st_ctime = ip->i_ctime; |
| |
| return 0; /* NOTHING CAN GO WROGN! */ |
| } |
| #endif |
| |
| static struct ext2_inode * ext2_follow_link(struct ext2_inode * from, |
| const char * base) |
| { |
| char *linkto; |
| |
| if (from->i_blocks) { |
| linkto = blkbuf; |
| if (ext2_breadi(from, 0, 1, blkbuf) == -1) |
| return NULL; |
| #ifdef DEBUG |
| printf("long link!\n"); |
| #endif |
| } else { |
| int n; |
| for (n = 0; n < EXT2_N_BLOCKS; n++) { |
| inplace(__le32_to_cpu, from->i_block[n]); |
| } |
| linkto = (char*)from->i_block; |
| } |
| #ifdef DEBUG |
| printf("symlink to %s\n", linkto); |
| #endif |
| |
| /* Resolve relative links */ |
| if (linkto[0] != '/') { |
| char *end = strrchr(base, '/'); |
| if (end) { |
| char fullname[(end - base + 1) + strlen(linkto) + 1]; |
| strncpy(fullname, base, end - base + 1); |
| fullname[end - base + 1] = '\0'; |
| strcat(fullname, linkto); |
| #ifdef DEBUG |
| printf("resolved to %s\n", fullname); |
| #endif |
| return ext2_namei(fullname); |
| } else { |
| /* Assume it's in the root */ |
| return ext2_namei(linkto); |
| } |
| } else { |
| return ext2_namei(linkto); |
| } |
| } |
| |
| static int ext2_read(int fd, char *buf, unsigned count, unsigned devaddr) |
| { |
| struct ext2_inode * ip; |
| ip = fd2inode[fd]; |
| |
| if (Debug) printf("ext2_read(%d, 0x%p, %d, %d)\n", |
| fd, buf, count, devaddr); |
| |
| return ext2_breadi(ip, |
| devaddr / ext2fs.blocksize, |
| count / ext2fs.blocksize, |
| buf); |
| } |
| |
| static void ext2_describe(int fd, int *bufalign, |
| int *blocksize) |
| { |
| describe(dev, bufalign, blocksize); |
| if (blocksize != 0) |
| *blocksize = ext2fs.blocksize; |
| } |
| |
| int ext2_open(const char *filename) |
| { |
| /* |
| * Unix-like open routine. Returns a small integer (actually |
| * an index into the inode table... |
| */ |
| struct ext2_inode * ip; |
| int fd = -1; |
| |
| ip = ext2_namei(filename); |
| if (ip) { |
| while (S_ISLNK(ip->i_mode)) { |
| ip = ext2_follow_link(ip, filename); |
| if (!ip) return -1; |
| } |
| fd = fileio_open(ext2_describe, ext2_read); |
| if (fd >= 0) |
| { |
| fd2inode[fd] = ip; |
| } |
| } |
| |
| return fd; |
| } |
| |
| |
| void ext2_close(int fd) |
| { |
| /* blah, hack, don't close the root inode ever */ |
| if (&inode_table[fd].inode != root_inode) |
| ext2_iput(&inode_table[fd].inode); |
| } |
| |
| |
| struct bootfs ext2fs = { |
| .fs_type = 0, |
| .blocksize = 0, |
| .mount = ext2_mount, |
| .open = ext2_open, |
| .bread = ext2_bread, |
| .close = ext2_close, |
| .readdir = ext2_readdir, |
| /* .fstat = ext2_fstat */ |
| }; |
| /* $Id: ext2.c,v 1.5 2000/09/21 16:47:48 bame Exp $ */ |