diff options
author | Waldemar Brodkorb <wbx@openadk.org> | 2017-12-31 18:47:16 +0100 |
---|---|---|
committer | Waldemar Brodkorb <wbx@openadk.org> | 2017-12-31 18:47:25 +0100 |
commit | 3a96085b999220c4da0c5ef7d1f7ba26b9ddfb98 (patch) | |
tree | 77f1445aae2e6be5135594e95986b3278bbc061c /package/aboot/src/tools | |
parent | cc28479164b8dc8afd4310716da32f16022f5974 (diff) |
dec-multia: make netboot possible, add aboot bootloader
Diffstat (limited to 'package/aboot/src/tools')
-rw-r--r-- | package/aboot/src/tools/.cvsignore | 5 | ||||
-rw-r--r-- | package/aboot/src/tools/Makefile | 22 | ||||
-rw-r--r-- | package/aboot/src/tools/abootconf.c | 125 | ||||
-rw-r--r-- | package/aboot/src/tools/bio.c | 178 | ||||
-rw-r--r-- | package/aboot/src/tools/bio.h | 4 | ||||
-rw-r--r-- | package/aboot/src/tools/e2lib.c | 1473 | ||||
-rw-r--r-- | package/aboot/src/tools/e2lib.h | 332 | ||||
-rw-r--r-- | package/aboot/src/tools/e2writeboot.c | 254 | ||||
-rw-r--r-- | package/aboot/src/tools/elfencap.c | 61 | ||||
-rw-r--r-- | package/aboot/src/tools/isomarkboot.c | 239 | ||||
-rw-r--r-- | package/aboot/src/tools/objstrip.c | 260 |
11 files changed, 2953 insertions, 0 deletions
diff --git a/package/aboot/src/tools/.cvsignore b/package/aboot/src/tools/.cvsignore new file mode 100644 index 000000000..2197455e0 --- /dev/null +++ b/package/aboot/src/tools/.cvsignore @@ -0,0 +1,5 @@ +abootconf +e2writeboot +elfencap +isomarkboot +objstrip diff --git a/package/aboot/src/tools/Makefile b/package/aboot/src/tools/Makefile new file mode 100644 index 000000000..740b9cb9d --- /dev/null +++ b/package/aboot/src/tools/Makefile @@ -0,0 +1,22 @@ +ifndef ($(CC)) +CC = gcc +endif +override CFLAGS = -g -O2 -Wall -I. -I../include $(CPPFLAGS) +override LDFLAGS = -g +override PGMS += e2writeboot abootconf elfencap objstrip + +EXEC_PREFIX = /usr + +all: $(PGMS) + +install: $(PGMS) + install -c -o root -g root -m 755 $(PGMS) $(EXEC_PREFIX)/bin + +clean: + rm -f *~ *.o *.a core $(PGMS) + +isomarkboot: isomarkboot.o ../lib/isolib.o +e2writeboot: e2writeboot.o e2lib.o bio.o + +e2writeboot.o: e2lib.h +e2lib.o: e2lib.h diff --git a/package/aboot/src/tools/abootconf.c b/package/aboot/src/tools/abootconf.c new file mode 100644 index 000000000..0d0ae075c --- /dev/null +++ b/package/aboot/src/tools/abootconf.c @@ -0,0 +1,125 @@ +/* + * abootconf.c + * + * This file is part of aboot, the SRM bootloader for Linux/Alpha + * Copyright (C) 1996 Linus Torvalds, David Mosberger, and Michael Schwingen. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include <sys/fcntl.h> + +#include <config.h> + + +const char * prog_name; + +int +main (int argc, char ** argv) +{ + unsigned long sector[512 / sizeof(unsigned long)]; + off_t aboot_pos; + size_t nbytes; + long part = -1; + int disk, i; + + prog_name = argv[0]; + + if (argc != 2 && argc != 3) { + fprintf(stderr, "usage: %s device [partition]\n", prog_name); + exit(1); + } + + if (argc > 2) { + errno = 0; + part = strtol(argv[2], 0, 10); + if (errno == ERANGE) { + fprintf(stderr, "%s: bad partition number %s\n", + prog_name, argv[2]); + exit(1); + } + } + + disk = open(argv[1], part < 0 ? O_RDONLY : O_RDWR); + if (disk < 0) { + perror(argv[1]); + exit(1); + } + + nbytes = read(disk, sector, sizeof(sector)); + if (nbytes != sizeof(sector)) { + if ((long) nbytes < 0) { + perror("read"); + } else { + fprintf(stderr, "%s: short read\n", prog_name); + } + exit(1); + } + + aboot_pos = sector[61] * 512; + + if (lseek(disk, aboot_pos, SEEK_SET) != aboot_pos) { + perror("lseek"); + exit(1); + } + + nbytes = read(disk, sector, sizeof(sector)); + if (nbytes != sizeof(sector)) { + if ((long) nbytes < 0) { + perror("read"); + } else { + fprintf(stderr, "%s: short read\n", prog_name); + } + exit(1); + } + + for (i = 0; i < (int) (sizeof(sector)/sizeof(sector[0])); ++i) { + if (sector[i] == ABOOT_MAGIC) + break; + } + if (i >= (int) (sizeof(sector)/sizeof(sector[0]))) { + fprintf(stderr, "%s: could not find aboot on disk %s\n", + prog_name, argv[1]); + exit(1); + } + + if (part < 0) { + printf("aboot.conf partition currently set to %ld\n", + sector[i + 1]); + exit(0); + } + + if (lseek(disk, aboot_pos, SEEK_SET) != aboot_pos) { + perror("lseek"); + exit(1); + } + + sector[i + 1] = atoi(argv[2]); + + nbytes = write(disk, sector, sizeof(sector)); + if (nbytes != sizeof(sector)) { + if ((long) nbytes < 0) { + perror("write"); + } else { + fprintf(stderr, "%s: short write\n", prog_name); + } + exit(1); + } + return 0; +} diff --git a/package/aboot/src/tools/bio.c b/package/aboot/src/tools/bio.c new file mode 100644 index 000000000..c82161e62 --- /dev/null +++ b/package/aboot/src/tools/bio.c @@ -0,0 +1,178 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +/* Buffered I/O functions. By cacheing the most recently used blocks, + * we can cut WAAY down on disk traffic... + */ + +static int bio_fd = -1; +static int bio_counter = 0; +static int bio_blocksize = 0; + +struct bio_buf { + int blkno; + int last_access; + int dirty; + char * data; +}; + +#define NBUFS 32 +struct bio_buf buflist[NBUFS]; + +/* initialize the buffer cache. Blow away anything that may + * have been previously cached... + */ +void +binit(int fd, int blocksize) +{ + int i; + + bio_fd = fd; + bio_blocksize = blocksize; + + for(i = 0; i < NBUFS; i++) { + buflist[i].blkno = 0; + if(buflist[i].data) { + free(buflist[i].data); + } + buflist[i].data = 0; + buflist[i].last_access = 0; + buflist[i].dirty = 0; + } +} + +/* Flush out any dirty blocks */ +void +bflush(void) +{ + int i; + + for(i = 0; i < NBUFS; i++) { + if(buflist[i].dirty) { +#ifdef BIO_DEBUG + printf("bflush: writing block %d\n", buflist[i].blkno); +#endif + lseek(bio_fd, buflist[i].blkno * bio_blocksize, 0); + write(bio_fd, buflist[i].data, bio_blocksize); + buflist[i].dirty = 0; + } + } +} + +/* Read a block. */ +void +bread(int blkno, void * blkbuf) +{ + int i; + int lowcount; + int lowcount_buf; + + /* First, see if the block is already in memory... */ + for(i = 0; i < NBUFS; i++) { + if(buflist[i].blkno == blkno) { + /* Got it! Bump the access count and return. */ + buflist[i].last_access = ++bio_counter; +#ifdef BIO_DEBUG + printf("bread: buffer hit on block %d\n", blkno); +#endif + memcpy(blkbuf, buflist[i].data, bio_blocksize); + return; + } + } + + /* Not in memory; need to find a buffer and read it in. */ + lowcount = buflist[0].last_access; + lowcount_buf = 0; + for(i = 1; i < NBUFS; i++) { + if(buflist[i].last_access < lowcount) { + lowcount = buflist[i].last_access; + lowcount_buf = i; + } + } + + /* If the buffer is dirty, we need to write it out... */ + if(buflist[lowcount_buf].dirty) { +#ifdef BIO_DEBUG + printf("bread: recycling dirty buffer %d for block %d\n", + lowcount_buf, buflist[lowcount_buf].blkno); +#endif + lseek(bio_fd, buflist[lowcount_buf].blkno * bio_blocksize, 0); + write(bio_fd, buflist[lowcount_buf].data, bio_blocksize); + buflist[lowcount_buf].dirty = 0; + } + +#ifdef BIO_DEBUG + printf("bread: Using buffer %d for block %d\n", lowcount_buf, blkno); +#endif + + buflist[lowcount_buf].blkno = blkno; + if(!buflist[lowcount_buf].data) { + buflist[lowcount_buf].data = (char *)malloc(bio_blocksize); + } + lseek(bio_fd, blkno * bio_blocksize, 0); + if(read(bio_fd,buflist[lowcount_buf].data,bio_blocksize)!=bio_blocksize) { + perror("bread: I/O error"); + } + + buflist[lowcount_buf].last_access = ++bio_counter; + memcpy(blkbuf, buflist[lowcount_buf].data, bio_blocksize); +} + + +/* Write a block */ +void +bwrite(int blkno, void * blkbuf) +{ + int i; + int lowcount; + int lowcount_buf; + + /* First, see if the block is already in memory... */ + for(i = 0; i < NBUFS; i++) { + if(buflist[i].blkno == blkno) { + /* Got it! Bump the access count and return. */ +#ifdef BIO_DEBUG + printf("bwrite: buffer hit on block %d\n", blkno); +#endif + buflist[i].last_access = ++bio_counter; + memcpy(buflist[i].data, blkbuf, bio_blocksize); + buflist[i].dirty = 1; + return; + } + } + + /* Not in memory; need to find a buffer and stuff it. */ + lowcount = buflist[0].last_access; + lowcount_buf = 0; + for(i = 1; i < NBUFS; i++) { + if(buflist[i].last_access < lowcount) { + lowcount = buflist[i].last_access; + lowcount_buf = i; + } + } + + /* If the buffer is dirty, we need to write it out... */ + if(buflist[lowcount_buf].dirty) { +#ifdef BIO_DEBUG + printf("bwrite: recycling dirty buffer %d for block %d\n", + lowcount_buf, buflist[lowcount_buf].blkno); +#endif + lseek(bio_fd, buflist[lowcount_buf].blkno * bio_blocksize, 0); + write(bio_fd, buflist[lowcount_buf].data, bio_blocksize); + buflist[lowcount_buf].dirty = 0; + } + +#ifdef BIO_DEBUG + printf("bwrite: Using buffer %d for block %d\n", lowcount_buf, blkno); +#endif + + buflist[lowcount_buf].blkno = blkno; + if(!buflist[lowcount_buf].data) { + buflist[lowcount_buf].data = (char *)malloc(bio_blocksize); + } + buflist[lowcount_buf].last_access = ++bio_counter; + memcpy(buflist[lowcount_buf].data, blkbuf, bio_blocksize); + buflist[lowcount_buf].dirty = 1; +} diff --git a/package/aboot/src/tools/bio.h b/package/aboot/src/tools/bio.h new file mode 100644 index 000000000..398590360 --- /dev/null +++ b/package/aboot/src/tools/bio.h @@ -0,0 +1,4 @@ +extern void binit (int fd, int blocksize); +extern void bflush (void); +extern void bread (int blkno, void * blkbuf); +extern void bwrite (int blkno, void * blkbuf); diff --git a/package/aboot/src/tools/e2lib.c b/package/aboot/src/tools/e2lib.c new file mode 100644 index 000000000..56e4b66ef --- /dev/null +++ b/package/aboot/src/tools/e2lib.c @@ -0,0 +1,1473 @@ +/* This is a library of functions that allow user-level programs to + * read and manipulate ext2 file systems. For convenience sake, + * this library maintains a lot of state information in static + * variables; therefore, it's not reentrant. We don't care for + * our applications 8-) + */ + +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <sys/types.h> +#include <sys/stat.h> + +#include <bio.h> +#include <e2lib.h> + + +#define MAX_OPEN_FILES 8 + +int fd = -1; +struct ext2_super_block sb; +struct ext2_group_desc *gds; +int ngroups = 0; +int blocksize; /* Block size of this fs */ +int directlim; /* Maximum direct blkno */ +int ind1lim; /* Maximum single-indir blkno */ +int ind2lim; /* Maximum double-indir blkno */ +int ptrs_per_blk; /* ptrs/indirect block */ +char filename[256]; +int readonly; /* Is this FS read-only? */ +int verbose = 0; +int big_endian = 0; + +static void ext2_ifree(int ino); +static void ext2_free_indirect(int indirect_blkno, int level); + + +struct inode_table_entry { + struct ext2_inode inode; + int inumber; + int free; + unsigned short old_mode; +} inode_table[MAX_OPEN_FILES]; + +/* Utility functions to byte-swap 16 and 32 bit quantities... */ + +unsigned short +swap16 (unsigned short s) +{ + return((unsigned short)( ((s << 8) & 0xff00) | ((s >> 8) & 0x00ff))); +} + +unsigned int +swap32 (unsigned int i) +{ + return((unsigned int)( + ((i << 24) & 0xff000000) | + ((i << 8) & 0x00ff0000) | + ((i >> 8) & 0x0000ff00) | + ((i >> 24) & 0x000000ff)) ); +} + +void +ext2_swap_sb (struct ext2_super_block *sb) +{ + sb->s_inodes_count = swap32(sb->s_inodes_count); + sb->s_blocks_count = swap32(sb->s_blocks_count); + sb->s_r_blocks_count = swap32(sb->s_r_blocks_count); + sb->s_free_blocks_count = swap32(sb->s_free_blocks_count); + sb->s_free_inodes_count = swap32(sb->s_free_inodes_count); + sb->s_first_data_block = swap32(sb->s_first_data_block); + sb->s_log_block_size = swap32(sb->s_log_block_size); + sb->s_log_frag_size = swap32(sb->s_log_frag_size); + sb->s_blocks_per_group = swap32(sb->s_blocks_per_group); + sb->s_frags_per_group = swap32(sb->s_frags_per_group); + sb->s_inodes_per_group = swap32(sb->s_inodes_per_group); + sb->s_mtime = swap32(sb->s_mtime); + sb->s_wtime = swap32(sb->s_wtime); + sb->s_mnt_count = swap16(sb->s_mnt_count); + sb->s_max_mnt_count = swap16(sb->s_max_mnt_count); + sb->s_magic = swap16(sb->s_magic); + sb->s_state = swap16(sb->s_state); + sb->s_errors = swap16(sb->s_errors); + sb->s_pad = swap16(sb->s_pad); + sb->s_lastcheck = swap32(sb->s_lastcheck); + sb->s_checkinterval = swap32(sb->s_checkinterval); +} + +void +ext2_swap_gd (struct ext2_group_desc *gd) +{ + gd->bg_block_bitmap = swap32(gd->bg_block_bitmap); + gd->bg_inode_bitmap = swap32(gd->bg_inode_bitmap); + gd->bg_inode_table = swap32(gd->bg_inode_table); + gd->bg_free_blocks_count = swap16(gd->bg_free_blocks_count); + gd->bg_free_inodes_count = swap16(gd->bg_free_inodes_count); + gd->bg_used_dirs_count = swap16(gd->bg_used_dirs_count); + gd->bg_pad = swap16(gd->bg_pad); +} + +void +ext2_swap_inode (struct ext2_inode *ip) +{ + int i; + + ip->i_mode = swap16(ip->i_mode); + ip->i_uid = swap16(ip->i_uid); + ip->i_size = swap32(ip->i_size); + ip->i_atime = swap32(ip->i_atime); + ip->i_ctime = swap32(ip->i_ctime); + ip->i_mtime = swap32(ip->i_mtime); + ip->i_dtime = swap32(ip->i_dtime); + ip->i_gid = swap16(ip->i_gid); + ip->i_links_count = swap16(ip->i_links_count); + ip->i_blocks = swap32(ip->i_blocks); + ip->i_flags = swap32(ip->i_flags); + ip->i_reserved1 = swap32(ip->i_reserved1); + for(i = 0; i < EXT2_N_BLOCKS; i++) { + ip->i_block[i] = swap32(ip->i_block[i]); + } + ip->i_version = swap32(ip->i_version); + ip->i_file_acl = swap32(ip->i_file_acl); + ip->i_dir_acl = swap32(ip->i_dir_acl); + ip->i_faddr = swap32(ip->i_faddr); + ip->i_pad1 = swap16(ip->i_pad1); +} + + + +/* Initialize an ext2 filesystem; 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_init (char * name, int access) +{ + int i; + + /* Initialize the inode table */ + for(i = 0; i < MAX_OPEN_FILES; i++) { + inode_table[i].free = 1; + inode_table[i].inumber = 0; + } + + if((access != O_RDONLY) && (access != O_RDWR)) { + fprintf(stderr, + "ext2_init: Access must be O_RDONLY or O_RDWR, not %d\n", + access); + return(-1); + } + + /* Open the device/file */ + fd = open(name, access); + if(fd < 0) { + perror(filename); + return(-1); + } + + if(access == O_RDONLY) { + readonly = 1; + } + + /* Read in the first superblock */ + lseek(fd, EXT2_MIN_BLOCK_SIZE, SEEK_SET); + if(read(fd, &sb, sizeof(sb)) != sizeof(sb)) { + perror("ext2 sb read"); + close(fd); + return(-1); + } + + if((sb.s_magic != EXT2_SUPER_MAGIC) && (sb.s_magic != EXT2_SUPER_BIGMAGIC)) { + fprintf(stderr, "ext2 bad magic 0x%x\n", sb.s_magic); + close(fd); + return(-1); + } + + if(sb.s_magic == EXT2_SUPER_BIGMAGIC) { + big_endian = 1; + + /* Byte-swap the fields in the superblock... */ + ext2_swap_sb(&sb); + } + + if(sb.s_first_data_block != 1) { + fprintf(stderr, + "Brain-damaged utils can't deal with a filesystem\nwhere s_first_data_block != 1.\nRe-initialize the filesystem\n"); + close(fd); + return(-1); + } + + ngroups = (sb.s_blocks_count+sb.s_blocks_per_group-1)/sb.s_blocks_per_group; + gds = (struct ext2_group_desc *) + malloc((size_t)(ngroups * sizeof(struct ext2_group_desc))); + + /* Read in the group descriptors (immediately follows superblock) */ + if ((size_t) read(fd, gds, ngroups * sizeof(struct ext2_group_desc)) + != (ngroups * sizeof(struct ext2_group_desc))) + { + perror("ext2_init: group desc read error"); + return(-1); + } + + if(big_endian) { + for(i = 0; i < ngroups; i++) { + ext2_swap_gd(&(gds[i])); + } + } + + strcpy(filename, name); + + /* Calculate direct/indirect block limits for this file system + * (blocksize dependent) + */ + blocksize = EXT2_BLOCK_SIZE(&sb); + directlim = EXT2_NDIR_BLOCKS - 1; + ptrs_per_blk = blocksize/sizeof(unsigned int); + ind1lim = ptrs_per_blk + directlim; + ind2lim = (ptrs_per_blk * ptrs_per_blk) + directlim; + + if(getenv("EXT2_VERBOSE")) { + verbose = 1; + } + + binit(fd, blocksize); + + if(verbose) { + printf("Initialized filesystem %s\n", filename); + printf(" %d blocks (%dKb), %d free (%dKb)\n", + sb.s_blocks_count, (sb.s_blocks_count * blocksize)/1024, + sb.s_free_blocks_count, + (sb.s_free_blocks_count * blocksize)/1024); + printf(" %d inodes, %d free\n", + sb.s_inodes_count, sb.s_free_inodes_count); + printf(" %d groups, %d blocks/group\n", + ngroups, sb.s_blocks_per_group); + } + + return(0); +} + +int +ext2_blocksize (void) +{ + return blocksize; +} + +int +ext2_total_blocks (void) +{ + return sb.s_blocks_count; +} + +int +ext2_free_blocks (void) +{ + return sb.s_free_blocks_count; +} + +int +ext2_total_inodes (void) +{ + return sb.s_inodes_count; +} + +int +ext2_free_inodes (void) +{ + return sb.s_free_inodes_count; +} + +/* Call this when we're all done with the file system. This will write + * back any superblock and group changes to the file system. + */ +void +ext2_close (void) +{ + int i; + int errors = 0; + int blocks_per_group = sb.s_blocks_per_group; + + if(!readonly) { + + if(big_endian) { + ext2_swap_sb(&sb); + for(i = 0; i < ngroups; i++) { + ext2_swap_gd(&(gds[i])); + } + } + + for(i = 0; i < ngroups; i++) { + lseek(fd, ((i*blocks_per_group)+1)*blocksize, SEEK_SET); + if(write(fd, &sb, sizeof(sb)) != sizeof(sb)) { + perror("sb write"); + errors = 1; + } + + if ((size_t) write(fd, gds, ngroups*sizeof(struct ext2_group_desc)) + != ngroups*sizeof(struct ext2_group_desc)) + { + perror("gds write"); + errors = 1; + } + + bflush(); + } + } + + close(fd); + if(errors) { + fprintf(stderr, "Errors encountered while updating %s\n", filename); + fprintf(stderr, "e2fsck is STRONGLY recommended!\n"); + } +} + + + +/* Read the specified inode from the disk and return it to the user. + * Returns NULL if the inode can't be read... + */ +struct ext2_inode * +ext2_iget (int ino) +{ + int i; + struct ext2_inode * ip = NULL; + struct inode_table_entry * itp = NULL; + int group; + int blkoffset; + int byteoffset; + char inobuf[EXT2_MAX_BLOCK_SIZE]; + + for(i = 0; i < MAX_OPEN_FILES; i++) { + if(inode_table[i].free) { + itp = &(inode_table[i]); + ip = &(itp->inode); + break; + } + } + if(!ip) { + fprintf(stderr, "ext2_iget: no free inodes\n"); + return(NULL); + } + + group = ino / sb.s_inodes_per_group; + blkoffset = (gds[group].bg_inode_table * blocksize); + byteoffset = ((ino-1) % sb.s_inodes_per_group) * sizeof(struct ext2_inode); + blkoffset += ((byteoffset / blocksize) * blocksize); + byteoffset = (byteoffset % blocksize); + bread(blkoffset/blocksize, inobuf); + + memcpy(ip, &(inobuf[byteoffset]), sizeof(struct ext2_inode)); + + if(big_endian) { + ext2_swap_inode(ip); + } + + /* Yes, this is ugly, but it makes iput SOOO much easier 8-) */ + itp->free = 0; + itp->inumber = ino; + itp->old_mode = ip->i_mode; + + return(ip); +} + +/* Put the specified inode back on the disk where it came from. */ +void +ext2_iput (struct ext2_inode *ip) +{ + int group; + int blkoffset; + int byteoffset; + int ino; + struct inode_table_entry *itp; + char inobuf[EXT2_MAX_BLOCK_SIZE]; + int inode_mode; + + itp = (struct inode_table_entry *)ip; + ino = itp->inumber; + + if(ip->i_links_count == 0) { + ext2_ifree(itp->inumber); + } + + itp->inumber = 0; + + if(!readonly) { + group = ino / sb.s_inodes_per_group; + blkoffset = (gds[group].bg_inode_table * blocksize); + byteoffset = ((ino-1) % sb.s_inodes_per_group) * sizeof(struct ext2_inode); + blkoffset += (byteoffset / blocksize) * blocksize; + byteoffset = byteoffset % blocksize; + + inode_mode = ip->i_mode; + bread(blkoffset/blocksize, inobuf); + if(big_endian) { + ext2_swap_inode(ip); + } + memcpy(&(inobuf[byteoffset]), ip, sizeof(struct ext2_inode)); + bwrite(blkoffset/blocksize, inobuf); + + if(S_ISDIR(itp->old_mode) && !S_ISDIR(inode_mode)) { + /* We deleted a directory */ + gds[group].bg_used_dirs_count--; + } + if(!S_ISDIR(itp->old_mode) && S_ISDIR(inode_mode)) { + /* We created a directory */ + gds[group].bg_used_dirs_count++; + } + } + + itp->free = 1; +} + +#define BITS_PER_LONG (8*sizeof(int)) + +static int +find_first_zero_bit (unsigned int * addr, unsigned size) +{ + unsigned lwsize; + unsigned int *ap = (unsigned int *)addr; + unsigned int mask; + unsigned int longword, bit; + unsigned int lwval; + + if (!size) + return 0; + + /* Convert "size" to a whole number of longwords... */ + lwsize = (size + BITS_PER_LONG - 1) >> 5; + for (longword = 0; longword < lwsize; longword++, ap++) { + if(*ap != 0xffffffff) { + lwval = big_endian ? swap32(*ap) : *ap; + + for (bit = 0, mask = 1; bit < BITS_PER_LONG; bit++, mask <<= 1) + { + if ((lwval & mask) == 0) { + return (longword*BITS_PER_LONG) + bit; + } + } + } + } + return size; + +} + +static void +set_bit (unsigned int *addr, int bitno) +{ + if(big_endian) { + int lwval; + lwval = swap32(addr[bitno/BITS_PER_LONG]); + lwval |= (1 << (bitno % BITS_PER_LONG)); + addr[bitno/BITS_PER_LONG] = swap32(lwval); + } + else { + addr[bitno / BITS_PER_LONG] |= (1 << (bitno % BITS_PER_LONG)); + } +} + +static void +clear_bit (unsigned int *addr, int bitno) +{ + if(big_endian) { + int lwval; + lwval = swap32(addr[bitno/BITS_PER_LONG]); + lwval &= ~((unsigned int)(1 << (bitno % BITS_PER_LONG))); + addr[bitno/BITS_PER_LONG] = swap32(lwval); + } + else { + addr[bitno / BITS_PER_LONG] &= + ~((unsigned int)(1 << (bitno % BITS_PER_LONG))); + } +} + + +/* Allocate a block from the file system. Brain-damaged implementation; + * doesn't even TRY to do load-balancing among groups... just grabs the + * first block it can find... + */ +int +ext2_balloc (void) +{ + unsigned int blk, blockmap[256]; + int i; + + if(readonly) { + fprintf(stderr, "ext2_balloc: readonly filesystem\n"); + return(0); + } + + for(i = 0; i < ngroups; i++) { + if(gds[i].bg_free_blocks_count > 0) { + bread(gds[i].bg_block_bitmap, blockmap); + blk = find_first_zero_bit(blockmap, sb.s_blocks_per_group); + if (blk == 0 || blk == sb.s_blocks_per_group) { + fprintf(stderr, + "group %d has %d blocks free but none in bitmap?\n", + i, gds[i].bg_free_blocks_count); + continue; + } + set_bit(blockmap, blk); + bwrite(gds[i].bg_block_bitmap, blockmap); + gds[i].bg_free_blocks_count--; + sb.s_free_blocks_count--; + blk = blk + (i*sb.s_blocks_per_group)+1; + + if(blk == 0) { + fprintf(stderr, "ext2_balloc: blk == 0?\n"); + } + return(blk); + } + } + + if(verbose) { + printf("ext2_balloc: can't find a free block\n"); + } + return(0); +} + +/* Deallocate a block */ +void +ext2_bfree (int blk) +{ + int i; + unsigned int blockmap[256]; + + /* Find which group this block is in */ + i = (blk-1) / sb.s_blocks_per_group; + + /* Read the block map */ + bread(gds[i].bg_block_bitmap, blockmap); + + /* Clear the appropriate bit */ + clear_bit(blockmap, (blk-1) % sb.s_blocks_per_group); + + /* Write the block map back out */ + bwrite(gds[i].bg_block_bitmap, blockmap); + + /* Update free block counts. */ + gds[i].bg_free_blocks_count++; + sb.s_free_blocks_count++; + +} + +/* Allocate a contiguous range of blocks. This is used ONLY for + * initializing the bootstrapper. It uses a simple-minded algorithm + * that works best on a clean or nearly clean file system... we + * chunk through the bitmap a longword at a time. Only if the whole + * longword indicates free blocks do we use it. On a 32-bit system, + * this means we allocate blocks only in units of 32. + */ +int +ext2_contiguous_balloc (int nblocks) +{ + int i, j; + int firstlong, lastlong; + int longs_needed; + int longs_per_group; + int blk; + unsigned int blockmap[256]; + + if(readonly) { + fprintf(stderr, "ext2_contiguous_balloc: readonly filesystem\n"); + return(0); + } + + /* Compute how many longwords we need to fulfill this request */ + longs_needed = (nblocks + BITS_PER_LONG - 1) / BITS_PER_LONG; + longs_per_group = sb.s_blocks_per_group/BITS_PER_LONG; + + for(i = 0; i < ngroups; i++) { + /* Don't even bother if this group doesn't have enough blocks! */ + if(gds[i].bg_free_blocks_count >= nblocks) { + + /* Get the block map. */ + bread(gds[i].bg_block_bitmap, blockmap); + + /* Find a run of blocks */ + firstlong = 0; + + do { + for(; firstlong < longs_per_group; firstlong++) { + if(blockmap[firstlong] == 0) break; + } + + if(firstlong == longs_per_group) { + /* No such thing in this group; try another! */ + break; + } + + for(lastlong = firstlong; lastlong < longs_per_group; + lastlong++) { + if(blockmap[lastlong] != 0) break; + } + + if((lastlong-firstlong) < longs_needed) { + firstlong = lastlong; + } + } while((lastlong-firstlong) < longs_needed); + + /* If we got all the way through the block map, + * try another group. + */ + if(firstlong == longs_per_group) { + continue; + } + + /* If we get here, then we know that we have a run + * that will fit our allocation. Allocate the *actual* + * blocks that we need! + */ + blk = firstlong * BITS_PER_LONG; + for(j = 0; j < nblocks; j++) { + set_bit(blockmap, blk+j); + } + + bwrite(gds[i].bg_block_bitmap, blockmap); + gds[i].bg_free_blocks_count -= nblocks; + sb.s_free_blocks_count -= nblocks; + blk = blk + (i*sb.s_blocks_per_group)+1; + + if(verbose) { + printf("ext2_contiguous_balloc: allocated %d blks @%d\n", + nblocks, blk); + } + return(blk); + } + } + + if(verbose) { + printf("ext2_contiguous_balloc: can't find %d contiguous free blocks\n", nblocks); + } + return(0); +} + + +/* Pre-allocate contiguous blocks to the specified inode. Note that the + * DATA blocks must be contiguous; indirect blocks can come from anywhere. + * This is for the benefit of the bootstrap loader. + * If successful, this routine returns the block number of the first + * data block of the file. Otherwise, it returns -1. + */ +int +ext2_fill_contiguous (struct ext2_inode * ip, int nblocks) +{ + int iblkno = 0; + int firstblock; + int i; + unsigned int *lp = NULL; + char blkbuf[EXT2_MAX_BLOCK_SIZE]; + + /* For simplicity's sake, we only allow single indirection + * here. We shouldn't need more than this anyway! + */ + if(nblocks > ind1lim) { + fprintf(stderr, + "ext2_fill_contiguous: file size too big (%d); cannot exceed %d\n", + nblocks, ind1lim); + return(-1); + } + + /* First, try to allocate the data blocks */ + firstblock = ext2_contiguous_balloc(nblocks); + + if(firstblock == 0) { + fprintf(stderr, + "ext2_fill_contiguous: Cannot allocate %d contiguous blocks\n", nblocks); + return(-1); + } + + ip->i_blocks = nblocks * (blocksize/512); + + /* If we need the indirect block, then allocate it now. */ + if(nblocks > directlim) { + iblkno = ext2_balloc(); + if(iblkno == 0) { + /* Should rarely happen! */ + fprintf(stderr, + "ext2_fill_contiguous: cannot allocate indirect block\n"); + for(i = 0; i < nblocks; i++) { + ext2_bfree(i); + } + return(-1); + } + ip->i_blocks += (blocksize/512); + /* Point to indirect block buffer, in case we need it! */ + lp = (unsigned int *)blkbuf; + + for(i = 0; i < ptrs_per_blk; i++) { + lp[i] = 0; + } + + ip->i_block[EXT2_IND_BLOCK] = iblkno; + } + + /* All set... let's roll! */ + ip->i_size = nblocks * blocksize; + + for(i = 0; i < nblocks; i++) { + if(i < EXT2_NDIR_BLOCKS) { + ip->i_block[i] = firstblock+i; + } + else { + *lp++ = big_endian ? swap32(firstblock+i) : firstblock+i; + } + } + + /* Write back the indirect block if necessary... */ + if(iblkno) { + bwrite(iblkno, blkbuf); + } + + return(firstblock); +} + +/* Write out a boot block for this file system. The caller + * should have instantiated the block. + */ +void +ext2_write_bootblock (char *bb) +{ + bwrite(0, bb); +} + + +/* Allocate an inode from the file system. Brain-damaged implementation; + * doesn't even TRY to do load-balancing among groups... just grabs the + * first inode it can find... + */ +int +ext2_ialloc (void) +{ + unsigned int inodemap[256]; + int i, ino; + + if(readonly) { + return(0); + } + for(i = 0; i < ngroups; i++) { + if(gds[i].bg_free_inodes_count > 4) { + /* leave a few inodes in each group for slop... */ + bread(gds[i].bg_inode_bitmap, inodemap); + + ino = find_first_zero_bit(inodemap, sb.s_inodes_per_group); + if (ino == 0 || (unsigned) ino == sb.s_inodes_per_group) { + fprintf(stderr, + "group %d has %d inodes free but none in bitmap?\n", + i, gds[i].bg_free_inodes_count); + continue; + } + set_bit(inodemap, ino); + bwrite(gds[i].bg_inode_bitmap, inodemap); + gds[i].bg_free_inodes_count--; + sb.s_free_inodes_count--; + ino = ino + (i*sb.s_inodes_per_group) + 1; + return ino; + } + } + return 0; +} + +/* Deallocate an inode */ +static void +ext2_ifree (int ino) +{ + int i; + unsigned int inodemap[256]; + + /* Find which group this inode is in */ + i = (ino-1) / sb.s_inodes_per_group; + + /* Read the inode map */ + bread(gds[i].bg_inode_bitmap, inodemap); + + /* Clear the appropriate bit */ + clear_bit(inodemap, (ino-1) % sb.s_inodes_per_group); + + /* Write the inode map back out */ + bwrite(gds[i].bg_inode_bitmap, inodemap); + + /* Update free inode counts. */ + gds[i].bg_free_inodes_count++; + sb.s_free_inodes_count++; +} + +/* 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. + */ +int +ext2_blkno (struct ext2_inode *ip, int blkoff, int allocate) +{ + unsigned int *lp; + int blkno; + int iblkno; + int diblkno; + char blkbuf[EXT2_MAX_BLOCK_SIZE]; + + if(allocate && readonly) { + fprintf(stderr, "ext2_blkno: Cannot allocate on a readonly file system!\n"); + return(0); + } + + lp = (unsigned int *)blkbuf; + + /* If it's a direct block, it's easy! */ + if(blkoff <= directlim) { + if((ip->i_block[blkoff] == 0) && allocate) { + ip->i_block[blkoff] = ext2_balloc(); + if(verbose) { + printf("Allocated data block %d\n", ip->i_block[blkoff]); + } + ip->i_blocks += (blocksize / 512); + } + return(ip->i_block[blkoff]); + } + + /* Is it a single-indirect? */ + if(blkoff <= ind1lim) { + iblkno = ip->i_block[EXT2_IND_BLOCK]; + if((iblkno == 0) && allocate) { + /* No indirect block and we need one, so we allocate + * one, zero it, and write it out. + */ + iblkno = ext2_balloc(); + if(iblkno == 0) { + return(0); + } + ip->i_block[EXT2_IND_BLOCK] = iblkno; + if(verbose) { + printf("Allocated indirect block %d\n", iblkno); + } + ip->i_blocks += (blocksize / 512); + memset(blkbuf, 0, blocksize); + bwrite(iblkno, blkbuf); + } + + if(iblkno == 0) { + return(0); + } + + /* Read the indirect block */ + bread(iblkno, blkbuf); + + if(big_endian) { + blkno = swap32(lp[blkoff-(directlim+1)]); + } + else { + blkno = lp[blkoff-(directlim+1)]; + } + if((blkno == 0) && allocate) { + /* No block allocated but we need one. */ + if(big_endian) { + blkno = ext2_balloc(); + lp[blkoff-(directlim+1)] = swap32(blkno); + } + else { + blkno = lp[blkoff-(directlim+1)] = ext2_balloc(); + } + if(blkno == 0) { + return(0); + } + ip->i_blocks += (blocksize / 512); + if(verbose) { + printf("Allocated data block %d\n", blkno); + } + bwrite(iblkno, blkbuf); + } + 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) && allocate) { + /* No double-indirect block and we need one. Allocate one, + * fill it with zeros, and write it out. + */ + diblkno = ext2_balloc(); + if(diblkno == 0) { + return(0); + } + ip->i_blocks += (blocksize / 512); + if(verbose) { + printf("Allocated double-indirect block %d\n", diblkno); + } + memset(blkbuf, 0, blocksize); + bwrite(diblkno, blkbuf); + ip->i_block[EXT2_DIND_BLOCK] = diblkno; + } + + if(diblkno == 0) { + return(0); + } + + /* Read in the double-indirect block */ + bread(diblkno, blkbuf); + + /* Find the single-indirect block pointer ... */ + iblkno = lp[(blkoff - (ind1lim+1)) / ptrs_per_blk]; + if(big_endian) { + iblkno = swap32(iblkno); + } + + if((iblkno == 0) && allocate) { + /* No indirect block and we need one, so we allocate + * one, zero it, and write it out. + */ + iblkno = ext2_balloc(); + if(iblkno == 0) { + return(0); + } + ip->i_blocks += (blocksize / 512); + if(verbose) { + printf("Allocated single-indirect block %d\n", iblkno); + } + lp[(blkoff-(ind1lim+1)) / ptrs_per_blk] = big_endian ? swap32(iblkno) : iblkno; + bwrite(diblkno, blkbuf); + + memset(blkbuf, 0, blocksize); + bwrite(iblkno, blkbuf); + } + + if(iblkno == 0) { + return(0); + } + + + /* Read the indirect block */ + bread(iblkno, blkbuf); + + /* Find the block itself. */ + blkno = lp[(blkoff-(ind1lim+1)) % ptrs_per_blk]; + if(big_endian) { + blkno = swap32(blkno); + } + if((blkno == 0) && allocate) { + /* No block allocated but we need one. */ + if(big_endian) { + blkno = ext2_balloc(); + lp[(blkoff-(ind1lim+1)) % ptrs_per_blk] = swap32(blkno); + } + else { + blkno = lp[(blkoff-(ind1lim+1)) % ptrs_per_blk] = ext2_balloc(); + } + ip->i_blocks += (blocksize / 512); + if(verbose) { + printf("Allocated data block %d\n", blkno); + } + bwrite(iblkno, blkbuf); + } + return(blkno); + } + + if(blkoff > ind2lim) { + fprintf(stderr, "ext2_blkno: block number too large: %d\n", blkoff); + return(0); + } + return 0; +} + + + + +/* Read block number "blkno" from the specified file */ +void +ext2_bread (struct ext2_inode *ip, int blkno, char * buffer) +{ + int dev_blkno; + + dev_blkno = ext2_blkno(ip, blkno, 0); + if(dev_blkno == 0) { + /* This is a "hole" */ + memset(buffer, 0, blocksize); + } + else { + /* Read it for real */ + bread(dev_blkno, buffer); + } +} + +/* Write block number "blkno" to the specified file */ +void +ext2_bwrite (struct ext2_inode *ip, int blkno, char * buffer) +{ + int dev_blkno; + + if(readonly) { + fprintf(stderr, "ext2_bwrite: Cannot write to a readonly filesystem!\n"); + return; + } + + dev_blkno = ext2_blkno(ip, blkno, 1); + if(dev_blkno == 0) { + fprintf(stderr, "%s: No space on ext2 device\n", filename); + } + else { + /* Write it for real */ + bwrite(dev_blkno, buffer); + } +} + +/* More convenient forms of ext2_bread/ext2_bwrite. These allow arbitrary + * data alignment and buffer sizes... + */ +int +ext2_seek_and_read (struct ext2_inode *ip, int offset, char *buffer, int count) +{ + int blkno; + int blkoffset; + int bytesleft; + int nread; + int iosize; + char *bufptr; + char blkbuf[EXT2_MAX_BLOCK_SIZE]; + + bufptr = buffer; + bytesleft = count; + nread = 0; + blkno = offset / blocksize; + blkoffset = offset % blocksize; + + while(bytesleft > 0) { + iosize = ((blocksize-blkoffset) > bytesleft) ? + bytesleft : (blocksize-blkoffset); + if((blkoffset == 0) && (iosize == blocksize)) { + ext2_bread(ip, blkno, bufptr); + } + else { + ext2_bread(ip, blkno, blkbuf); + memcpy(bufptr, blkbuf+blkoffset, iosize); + } + bytesleft -= iosize; + bufptr += iosize; + nread += iosize; + blkno++; + blkoffset = 0; + } + return(nread); +} + +int +ext2_seek_and_write (struct ext2_inode *ip, int offset, char *buffer, int count) +{ + int blkno; + int blkoffset; + int bytesleft; + int nwritten; + int iosize; + char *bufptr; + char blkbuf[EXT2_MAX_BLOCK_SIZE]; + + bufptr = buffer; + bytesleft = count; + nwritten = 0; + blkno = offset / blocksize; + blkoffset = offset % blocksize; + + while(bytesleft > 0) { + iosize = ((blocksize-blkoffset) > bytesleft) ? + bytesleft : (blocksize-blkoffset); + if((blkoffset == 0) && (iosize == blocksize)) { + ext2_bwrite(ip, blkno, bufptr); + } + else { + ext2_bread(ip, blkno, blkbuf); + memcpy(blkbuf+blkoffset, bufptr, iosize); + ext2_bwrite(ip, blkno, blkbuf); + } + bytesleft -= iosize; + bufptr += iosize; + nwritten += iosize; + blkno++; + blkoffset = 0; + } + return(nwritten); +} + +struct ext2_inode * +ext2_namei (char *name) +{ + char namebuf[256]; + char dirbuf[EXT2_MAX_BLOCK_SIZE]; + char * component; + struct ext2_inode * dir_inode; + struct ext2_dir_entry *dp; + int next_ino; + + /* Squirrel away a copy of "namebuf" that we can molest */ + strcpy(namebuf, name); + + /* Start at the root... */ + dir_inode = ext2_iget(EXT2_ROOT_INO); + + component = strtok(namebuf, "/"); + while(component) { + unsigned diroffset; + int component_length, blockoffset; + + /* Search for the specified component in the current directory + * inode. + */ + + next_ino = -1; + + component_length = strlen(component); + diroffset = 0; + while (diroffset < dir_inode->i_size) { + blockoffset = 0; + ext2_bread(dir_inode, diroffset / blocksize, dirbuf); + while (blockoffset < blocksize) { + int namelen; + + dp = (struct ext2_dir_entry *)(dirbuf+blockoffset); + namelen = big_endian ? swap16(dp->name_len) : dp->name_len; + if((namelen == component_length) && + (strncmp(component, dp->name, component_length) == 0)) { + /* Found it! */ + next_ino = big_endian ? swap32(dp->inode) : dp->inode; + break; + } + /* Go to next entry in this block */ + blockoffset += (big_endian ? swap16(dp->rec_len) : dp->rec_len); + } + if(next_ino >= 0) { + break; + } + + /* If we got here, then we didn't find the component. + * Try the next block in this directory... + */ + diroffset += blocksize; + } + + /* At this point, we're done with this directory whether + * we've succeeded or failed... + */ + 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); + + 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); +} + +/* Create a new entry in the specified directory with the specified + * name/inumber pair. This routine ASSUMES that the specified + * entry does not already exist! Therefore, we MUST use namei + * first to try and find the entry... + */ + +void +ext2_mknod (struct ext2_inode *dip, char * name, int ino) +{ + unsigned diroffset; + int blockoffset, namelen, new_reclen; + struct ext2_dir_entry *dp; + struct ext2_dir_entry *entry_dp; + char dirbuf[EXT2_MAX_BLOCK_SIZE]; + int dp_inode, dp_reclen, dp_namelen; + + namelen = strlen(name); + + /* Look for an empty directory entry that can hold this + * item. + */ + diroffset = 0; + entry_dp = NULL; + while (diroffset < dip->i_size) { + blockoffset = 0; + ext2_bread(dip, diroffset / blocksize, dirbuf); + while(blockoffset < blocksize) { + + dp = (struct ext2_dir_entry *)(dirbuf+blockoffset); + dp_inode = big_endian ? swap32(dp->inode) : dp->inode; + dp_reclen = big_endian ? swap16(dp->rec_len) : dp->rec_len; + dp_namelen = big_endian ? swap16(dp->name_len) : dp->name_len; + + if((dp_inode == 0) && (dp_reclen >= EXT2_DIR_REC_LEN(namelen))) { + /* Found an *empty* entry that can hold this name. */ + entry_dp = dp; + break; + } + + /* If this entry is in use, see if it has space at the end + * to hold the new entry anyway... + */ + if((dp_inode != 0) && + ((dp_reclen - EXT2_DIR_REC_LEN(dp_namelen)) + >= EXT2_DIR_REC_LEN(namelen))) { + + new_reclen = dp_reclen - EXT2_DIR_REC_LEN(dp_namelen); + + /* Chop the in-use entry down to size */ + if(big_endian) { + dp_reclen = EXT2_DIR_REC_LEN(swap16(dp->name_len)); + } + else { + dp_reclen = EXT2_DIR_REC_LEN(dp->name_len); + } + dp->rec_len = big_endian ? swap16(dp_reclen) : dp_reclen; + + /* Point entry_dp to the end of this entry */ + entry_dp = (struct ext2_dir_entry *)((char*)dp + dp_reclen); + + /* Set the record length for this entry */ + entry_dp->rec_len = big_endian ? swap16(new_reclen) : new_reclen; + + /* all set! */ + break; + } + + /* No luck yet... go to next entry in this block */ + blockoffset += dp_reclen; + } + if(entry_dp != NULL) { + break; + } + + /* If we got here, then we didn't find the component. + * Try the next block in this directory... + */ + diroffset += blocksize; + } + + /* By the time we get here, one of two things has happened: + * + * If entry_dp is non-NULL, then entry_dp points to the + * place in dirbuf where the entry lives, and diroffset + * is the directory offset of the beginning of dirbuf. + * + * If entry_dp is NULL, then we couldn't find an entry, + * so we need to add a block to the directory file for + * this entry... + */ + if(entry_dp) { + entry_dp->inode = big_endian ? swap32(ino) : ino; + entry_dp->name_len = big_endian ? swap16(namelen) : namelen; + strncpy(entry_dp->name, name, namelen); + ext2_bwrite(dip, diroffset/blocksize, dirbuf); + } + else { + entry_dp = (struct ext2_dir_entry *)dirbuf; + entry_dp->inode = big_endian ? swap32(ino) : ino; + entry_dp->name_len = big_endian ? swap16(namelen) : namelen; + strncpy(entry_dp->name, name, namelen); + entry_dp->rec_len = big_endian ? swap16(blocksize) : blocksize; + ext2_bwrite(dip, dip->i_size/blocksize, dirbuf); + dip->i_size += blocksize; + } +} + +/* This is a close cousin to namei, only it *removes* the entry + * in addition to finding it. This routine assumes that the specified + * entry has already been found... + */ +void +ext2_remove_entry (char *name) +{ + char namebuf[256]; + char dirbuf[EXT2_MAX_BLOCK_SIZE]; + char * component; + struct ext2_inode * dir_inode; + struct ext2_dir_entry *dp; + int next_ino; + int dp_inode, dp_reclen, dp_namelen; + + /* Squirrel away a copy of "namebuf" that we can molest */ + strcpy(namebuf, name); + + /* Start at the root... */ + dir_inode = ext2_iget(EXT2_ROOT_INO); + + component = strtok(namebuf, "/"); + while(component) { + unsigned diroffset; + int blockoffset, component_length; + char *next_component; + struct ext2_dir_entry * pdp; + + /* Search for the specified component in the current directory + * inode. + */ + + next_component = NULL; + pdp = NULL; + next_ino = -1; + + component_length = strlen(component); + diroffset = 0; + while (diroffset < dir_inode->i_size) { + blockoffset = 0; + ext2_bread(dir_inode, diroffset / blocksize, dirbuf); + while(blockoffset < blocksize) { + dp = (struct ext2_dir_entry *)(dirbuf+blockoffset); + dp_inode = big_endian ? swap32(dp->inode) : dp->inode; + dp_reclen = big_endian ? swap16(dp->rec_len) : dp->rec_len; + dp_namelen = big_endian ? swap16(dp->name_len) : dp->name_len; + + if((dp_namelen == component_length) && + (strncmp(component, dp->name, component_length) == 0)) { + /* Found it! */ + next_component = strtok(NULL, "/"); + if(next_component == NULL) { + /* We've found the entry that needs to be + * zapped. If it's at the beginning of the + * block, then zap it. Otherwise, coalesce + * it with the previous entry. + */ + if(pdp) { + if(big_endian) { + pdp->rec_len = + swap16(swap16(pdp->rec_len)+dp_reclen); + } + else { + pdp->rec_len += dp_reclen; + } + } + else { + dp->inode = 0; + dp->name_len = 0; + } + ext2_bwrite(dir_inode, diroffset / blocksize, dirbuf); + return; + } + next_ino = dp_inode; + break; + } + /* Go to next entry in this block */ + pdp = dp; + blockoffset += dp_reclen; + } + if(next_ino >= 0) { + break; + } + + /* If we got here, then we didn't find the component. + * Try the next block in this directory... + */ + diroffset += blocksize; + } + + /* At this point, we're done with this directory whether + * we've succeeded or failed... + */ + 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; + } + + /* Otherwise, we can get this inode and find the next + * component string... + */ + dir_inode = ext2_iget(next_ino); + + component = next_component; + } + + ext2_iput(dir_inode); +} + + +void +ext2_truncate (struct ext2_inode *ip) +{ + int i; + + /* Deallocate all blocks associated with a particular file + * and set its size to zero. + */ + + /* Direct blocks */ + for(i = 0; i < EXT2_NDIR_BLOCKS; i++) { + if(ip->i_block[i]) { + ext2_bfree(ip->i_block[i]); + ip->i_block[i] = 0; + } + } + + /* First-level indirect blocks */ + if(ip->i_block[EXT2_IND_BLOCK]) { + ext2_free_indirect(ip->i_block[EXT2_IND_BLOCK], 0); + ip->i_block[EXT2_IND_BLOCK] = 0; + } + + /* Second-level indirect blocks */ + if(ip->i_block[EXT2_DIND_BLOCK]) { + ext2_free_indirect(ip->i_block[EXT2_DIND_BLOCK], 1); + ip->i_block[EXT2_DIND_BLOCK] = 0; + } + + /* Third-level indirect blocks */ + if(ip->i_block[EXT2_TIND_BLOCK]) { + ext2_free_indirect(ip->i_block[EXT2_TIND_BLOCK], 2); + ip->i_block[EXT2_TIND_BLOCK] = 0; + } + + ip->i_size = 0; +} + +/* Recursive routine to free an indirect chain */ +static void +ext2_free_indirect (int indirect_blkno, int level) +{ + int i, indirect_block[EXT2_MAX_BLOCK_SIZE/4]; + + /* Read the specified indirect block */ + bread(indirect_blkno, indirect_block); + + for(i = 0; i < ptrs_per_blk; i++) { + if(level == 0) { + /* These are pointers to data blocks; just free them up */ + if(indirect_block[i]) { + if(big_endian) { + ext2_bfree(swap32(indirect_block[i])); + } + else { + ext2_bfree(indirect_block[i]); + } + indirect_block[i] = 0; + } + } + else { + /* These are pointers to *indirect* blocks. Go down the chain */ + if(indirect_block[i]) { + if(big_endian) { + ext2_free_indirect(swap32(indirect_block[i]), level-1); + } + else { + ext2_free_indirect(indirect_block[i], level-1); + } + indirect_block[i] = 0; + } + } + } + ext2_bfree(indirect_blkno); +} + +int +ext2_get_inumber (struct ext2_inode *ip) +{ + struct inode_table_entry *itp; + + itp = (struct inode_table_entry *)ip; + return(itp->inumber); +} diff --git a/package/aboot/src/tools/e2lib.h b/package/aboot/src/tools/e2lib.h new file mode 100644 index 000000000..7c94ce270 --- /dev/null +++ b/package/aboot/src/tools/e2lib.h @@ -0,0 +1,332 @@ +#ifndef EXT2_LIB_H +#define EXT2_LIB_H + +#include <fcntl.h> + + +/* Definitions cribbed from ext2_fs.h, modified so's to be 64-bit clean + * when cross-compiling on Alpha + */ + +/* + * The second extended filesystem constants/structures + */ + +/* + * Define EXT2FS_DEBUG to produce debug messages + */ +#undef EXT2FS_DEBUG + +/* + * Define EXT2FS_DEBUG_CACHE to produce cache debug messages + */ +#undef EXT2FS_DEBUG_CACHE + +/* + * Define EXT2FS_CHECK_CACHE to add some checks to the name cache code + */ +#undef EXT2FS_CHECK_CACHE + +/* + * Define EXT2FS_PRE_02B_COMPAT to convert ext 2 fs prior to 0.2b + */ +#undef EXT2FS_PRE_02B_COMPAT + +/* + * Define DONT_USE_DCACHE to inhibit the directory cache + */ +#define DONT_USE_DCACHE + +/* + * Define EXT2_PREALLOCATE to preallocate data blocks for expanding files + */ +#define EXT2_PREALLOCATE + +/* + * The second extended file system version + */ +#define EXT2FS_DATE "94/03/10" +#define EXT2FS_VERSION "0.5" + + +/* + * Special inodes numbers + */ +#define EXT2_BAD_INO 1 /* Bad blocks inode */ +#define EXT2_ROOT_INO 2 /* Root inode */ +#define EXT2_ACL_IDX_INO 3 /* ACL inode */ +#define EXT2_ACL_DATA_INO 4 /* ACL inode */ +#define EXT2_BOOT_LOADER_INO 5 /* Boot loader inode */ +#define EXT2_UNDEL_DIR_INO 6 /* Undelete directory inode */ +#define EXT2_FIRST_INO 11 /* First non reserved inode */ + +/* + * The second extended file system magic number + */ +#define EXT2_PRE_02B_MAGIC 0xEF51 +#define EXT2_SUPER_MAGIC 0xEF53 +#define EXT2_SUPER_BIGMAGIC 0x53EF /* Accessing on big-endian system... */ + +/* + * Maximal count of links to a file + */ +#define EXT2_LINK_MAX 32000 + +/* + * Macro-instructions used to manage several block sizes + */ +#define EXT2_MIN_BLOCK_SIZE 1024 +#define EXT2_MAX_BLOCK_SIZE 4096 +#define EXT2_MIN_BLOCK_LOG_SIZE 10 +# define EXT2_BLOCK_SIZE(s) (EXT2_MIN_BLOCK_SIZE << (s)->s_log_block_size) +#define EXT2_ACLE_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s) / sizeof (struct ext2_acl_entry)) +#define EXT2_ADDR_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s) / sizeof (unsigned int)) +# define EXT2_BLOCK_SIZE_BITS(s) ((s)->s_log_block_size + 10) +#define EXT2_INODES_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s) / sizeof (struct ext2_inode)) + +/* + * Macro-instructions used to manage fragments + */ +#define EXT2_MIN_FRAG_SIZE 1024 +#define EXT2_MAX_FRAG_SIZE 4096 +#define EXT2_MIN_FRAG_LOG_SIZE 10 +# define EXT2_FRAG_SIZE(s) (EXT2_MIN_FRAG_SIZE << (s)->s_log_frag_size) +# define EXT2_FRAGS_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s) / EXT2_FRAG_SIZE(s)) + +/* + * ACL structures + */ +struct ext2_acl_header /* Header of Access Control Lists */ +{ + unsigned int aclh_size; + unsigned int aclh_file_count; + unsigned int aclh_acle_count; + unsigned int aclh_first_acle; +}; + +struct ext2_acl_entry /* Access Control List Entry */ +{ + unsigned int acle_size; + unsigned short acle_perms; /* Access permissions */ + unsigned short acle_type; /* Type of entry */ + unsigned short acle_tag; /* User or group identity */ + unsigned short acle_pad1; + unsigned int acle_next; /* Pointer on next entry for the */ + /* same inode or on next free entry */ +}; + +/* + * Structure of a blocks group descriptor + */ +struct ext2_old_group_desc +{ + unsigned int bg_block_bitmap; /* Blocks bitmap block */ + unsigned int bg_inode_bitmap; /* Inodes bitmap block */ + unsigned int bg_inode_table; /* Inodes table block */ + unsigned short bg_free_blocks_count; /* Free blocks count */ + unsigned short bg_free_inodes_count; /* Free inodes count */ +}; + +struct ext2_group_desc +{ + unsigned int bg_block_bitmap; /* Blocks bitmap block */ + unsigned int bg_inode_bitmap; /* Inodes bitmap block */ + unsigned int bg_inode_table; /* Inodes table block */ + unsigned short bg_free_blocks_count; /* Free blocks count */ + unsigned short bg_free_inodes_count; /* Free inodes count */ + unsigned short bg_used_dirs_count; /* Directories count */ + unsigned short bg_pad; + unsigned int bg_reserved[3]; +}; + +/* + * Macro-instructions used to manage group descriptors + */ +# define EXT2_BLOCKS_PER_GROUP(s) ((s)->s_blocks_per_group) +# define EXT2_DESC_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s) / sizeof (struct ext2_group_desc)) +# define EXT2_INODES_PER_GROUP(s) ((s)->s_inodes_per_group) + +/* + * Constants relative to the data blocks + */ +#define EXT2_NDIR_BLOCKS 12 +#define EXT2_IND_BLOCK EXT2_NDIR_BLOCKS +#define EXT2_DIND_BLOCK (EXT2_IND_BLOCK + 1) +#define EXT2_TIND_BLOCK (EXT2_DIND_BLOCK + 1) +#define EXT2_N_BLOCKS (EXT2_TIND_BLOCK + 1) + +/* + * Inode flags + */ +#define EXT2_SECRM_FL 0x0001 /* Secure deletion */ +#define EXT2_UNRM_FL 0x0002 /* Undelete */ +#define EXT2_COMPR_FL 0x0004 /* Compress file */ +#define EXT2_SYNC_FL 0x0008 /* Synchronous updates */ + +/* + * ioctl commands + */ +#define EXT2_IOC_GETFLAGS _IOR('f', 1, int) +#define EXT2_IOC_SETFLAGS _IOW('f', 2, int) +#define EXT2_IOC_GETVERSION _IOR('v', 1, int) +#define EXT2_IOC_SETVERSION _IOW('v', 2, int) + +/* + * Structure of an inode on the disk + */ +struct ext2_inode { + unsigned short i_mode; /* File mode */ + unsigned short i_uid; /* Owner Uid */ + unsigned int i_size; /* Size in bytes */ + unsigned int i_atime; /* Access time */ + unsigned int i_ctime; /* Creation time */ + unsigned int i_mtime; /* Modification time */ + unsigned int i_dtime; /* Deletion Time */ + unsigned short i_gid; /* Group Id */ + unsigned short i_links_count; /* Links count */ + unsigned int i_blocks; /* Blocks count */ + unsigned int i_flags; /* File flags */ + unsigned int i_reserved1; + unsigned int i_block[EXT2_N_BLOCKS];/* Pointers to blocks */ + unsigned int i_version; /* File version (for NFS) */ + unsigned int i_file_acl; /* File ACL */ + unsigned int i_dir_acl; /* Directory ACL */ + unsigned int i_faddr; /* Fragment address */ + unsigned char i_frag; /* Fragment number */ + unsigned char i_fsize; /* Fragment size */ + unsigned short i_pad1; + unsigned int i_reserved2[2]; +}; + +/* + * File system states + */ +#define EXT2_VALID_FS 0x0001 /* Unmounted cleany */ +#define EXT2_ERROR_FS 0x0002 /* Errors detected */ + +/* + * Mount flags + */ +#define EXT2_MOUNT_CHECK_NORMAL 0x0001 /* Do some more checks */ +#define EXT2_MOUNT_CHECK_STRICT 0x0002 /* Do again more checks */ +#define EXT2_MOUNT_CHECK (EXT2_MOUNT_CHECK_NORMAL | \ + EXT2_MOUNT_CHECK_STRICT) +#define EXT2_MOUNT_GRPID 0x0004 /* Create files with directory's group */ +#define EXT2_MOUNT_DEBUG 0x0008 /* Some debugging messages */ +#define EXT2_MOUNT_ERRORS_CONT 0x0010 /* Continue on errors */ +#define EXT2_MOUNT_ERRORS_RO 0x0020 /* Remount fs ro on errors */ +#define EXT2_MOUNT_ERRORS_PANIC 0x0040 /* Panic on errors */ + +#define clear_opt(o, opt) o &= ~EXT2_MOUNT_##opt +#define set_opt(o, opt) o |= EXT2_MOUNT_##opt +#define test_opt(sb, opt) ((sb)->u.ext2_sb.s_mount_opt & \ + EXT2_MOUNT_##opt) +/* + * Maximal mount counts between two filesystem checks + */ +#define EXT2_DFL_MAX_MNT_COUNT 20 /* Allow 20 mounts */ +#define EXT2_DFL_CHECKINTERVAL 0 /* Don't use interval check */ + +/* + * Behaviour when detecting errors + */ +#define EXT2_ERRORS_CONTINUE 1 /* Continue execution */ +#define EXT2_ERRORS_RO 2 /* Remount fs read-only */ +#define EXT2_ERRORS_PANIC 3 /* Panic */ +#define EXT2_ERRORS_DEFAULT EXT2_ERRORS_CONTINUE + +/* + * Structure of the super block + */ +struct ext2_super_block { + unsigned int s_inodes_count; /* 0: Inodes count */ + unsigned int s_blocks_count; /* 4: Blocks count */ + unsigned int s_r_blocks_count;/* 8: Reserved blocks count */ + unsigned int s_free_blocks_count;/* 12: Free blocks count */ + unsigned int s_free_inodes_count;/* 16: Free inodes count */ + unsigned int s_first_data_block;/* 20: First Data Block */ + unsigned int s_log_block_size;/* 24: Block size */ + int s_log_frag_size; /* 28: Fragment size */ + unsigned int s_blocks_per_group;/* 32: # Blocks per group */ + unsigned int s_frags_per_group;/* 36: # Fragments per group */ + unsigned int s_inodes_per_group;/* 40: # Inodes per group */ + unsigned int s_mtime; /* 44: Mount time */ + unsigned int s_wtime; /* 48: Write time */ + unsigned short s_mnt_count; /* 52: Mount count */ + short s_max_mnt_count; /* 54: Maximal mount count */ + unsigned short s_magic; /* 56: Magic signature */ + unsigned short s_state; /* 58: File system state */ + unsigned short s_errors; /* 60: Behaviour when detecting errors */ + unsigned short s_pad; /* 62: */ + unsigned int s_lastcheck; /* 64: time of last check */ + unsigned int s_checkinterval; /* 68: max. time between checks */ + unsigned int s_reserved[238]; /* 72: Padding to the end of the block */ +}; + +/* + * Structure of a directory entry + */ +#define EXT2_NAME_LEN 255 + +struct ext2_dir_entry { + unsigned int inode; /* Inode number */ + unsigned short rec_len; /* Directory entry length */ + unsigned short name_len; /* Name length */ + char name[EXT2_NAME_LEN]; /* File name */ +}; + +/* + * EXT2_DIR_PAD defines the directory entries boundaries + * + * NOTE: It must be a multiple of 4 + */ +#define EXT2_DIR_PAD 4 +#define EXT2_DIR_ROUND (EXT2_DIR_PAD - 1) +#define EXT2_DIR_REC_LEN(name_len) (((name_len) + 8 + EXT2_DIR_ROUND) & \ + ~EXT2_DIR_ROUND) + + +/* These definitions are cribbed from other file system include files, so that + * we can take a stab at identifying non-ext2 file systems as well... + */ +/* + * minix super-block data on disk + */ +struct minix_super_block { + unsigned short s_ninodes; + unsigned short s_nzones; + unsigned short s_imap_blocks; + unsigned short s_zmap_blocks; + unsigned short s_firstdatazone; + unsigned short s_log_zone_size; + unsigned int s_max_size; + unsigned short s_magic; + unsigned short s_state; +}; + +#define MINIX_SUPER_MAGIC 0x137F /* original minix fs */ +#define MINIX_SUPER_MAGIC2 0x138F /* minix fs, 30 char names */ +#define NEW_MINIX_SUPER_MAGIC 0x2468 /* minix V2 - not implemented */ + +extern int ext2_init(char * name, int access); +extern void ext2_close(); +extern struct ext2_inode * ext2_iget(int ino); +extern void ext2_iput(struct ext2_inode *ip); +extern int ext2_balloc(void); +extern int ext2_ialloc(void); +extern int ext2_blocksize(void); +extern int ext2_blkno(struct ext2_inode *ip, int blkoff, + int allocate); +extern void ext2_bread(struct ext2_inode *ip, int blkno, + char * buffer); +extern void ext2_bwrite(struct ext2_inode *ip, int blkno, + char * buffer); +extern struct ext2_inode * ext2_namei(char * name); +extern void ext2_truncate(struct ext2_inode *ip); +extern void ext2_mknod(struct ext2_inode *dip, + char * name, int ino); +extern int ext2_fill_contiguous(struct ext2_inode * ip, + int nblocks); +extern void ext2_write_bootblock(char *bb); + +#endif /* EXT2_LIB_H */ diff --git a/package/aboot/src/tools/e2writeboot.c b/package/aboot/src/tools/e2writeboot.c new file mode 100644 index 000000000..58cbec1b7 --- /dev/null +++ b/package/aboot/src/tools/e2writeboot.c @@ -0,0 +1,254 @@ +/* Program to write a primary bootstrap file to a LINUX ext2 + * file system. + * + * Usage: e2writeboot fs-image bootfile + * + * It is assumed that the "bootfile" is a COFF executable with text, + * data, and bss contiguous. + */ + +#include <grp.h> +#include <pwd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> + +#include <sys/stat.h> + +#include <e2lib.h> + +#if defined(__linux__) +# include <sys/types.h> +#elif defined(__alpha__) && defined(__osf1__) + typedef unsigned long u_int64_t; +#elif defined(__GNUC__) + typedef unsigned long long u_int64_t; +#endif + +struct boot_block { + u_int64_t vax_code[17]; + u_int64_t reserved[43]; + u_int64_t count; + u_int64_t lbn; + u_int64_t flags; + u_int64_t chk_sum; +}; + +#define CONSOLE_BLOCK_SIZE 512 + +extern int big_endian; +extern unsigned short swap16(); +extern unsigned int swap32(); + + +int +main(int argc, char ** argv) +{ + char fsname[512]; + char iobuf[1024]; + char namebuf[EXT2_NAME_LEN+1]; + struct ext2_inode * ip; + int filesize; + int infile; + int blkno; + int bootstrap_size; + int i; + int bs_start; + char *bsbuf; + int blocksize; + struct boot_block *bbp; + u_int64_t *lbp, checksum; + struct stat st; + + if (argc != 3) { + printf("Usage: %s ext2-fs input-file\n", argv[0]); + exit(1); + } + + strcpy(fsname, argv[1]); + strcpy(namebuf, "/linuxboot"); + + + /* "Open" the file system */ + if (ext2_init(fsname, O_RDWR) < 0) { + exit(1); + } + + /* Open the input file */ + infile = open(argv[2], 0); + if (infile < 0) { + perror(argv[2]); + ext2_close(); + exit(1); + } + + /* Figure out just how much data the input file is going + * to require us to put out. (text+data+bss). + */ + if (fstat(infile, &st) == -1) { + perror("fstat"); + ext2_close(); + exit(1); + } + blocksize = ext2_blocksize(); + bootstrap_size = st.st_size; + printf("bootstrap_size: %d -> ", bootstrap_size); + bootstrap_size += blocksize-1; + bootstrap_size &= ~(blocksize-1); + printf("%d\n", bootstrap_size); + + /* Allocate a buffer to hold the entire bootstrap, then read + * in the text+data segments. + */ + bsbuf = (char *)malloc(bootstrap_size); + memset(bsbuf, 0, bootstrap_size); + read(infile, bsbuf, bootstrap_size); + close(infile); + + /* Get the inode for the file we want to create */ + ip = ext2_namei(namebuf); + if(ip) { + /* This file exists. Make sure it's a regular file, then + * truncate it. + */ + if(!S_ISREG(ip->i_mode)) { + printf("%s: Not a regular file. Must remove it first.\n", namebuf); + ext2_iput(ip); + ext2_close(); + exit(1); + } + + printf("Using existing file %s\n", namebuf); + ext2_truncate(ip); + } + else { + /* Doesn't exist. Must get the parent directory's inode. */ + char dirname[EXT2_NAME_LEN+1]; + char filename[EXT2_NAME_LEN+1]; + struct ext2_inode *dip; + int inumber; + char *cp; + int i; + + strcpy(dirname, namebuf); + cp = strrchr(dirname, '/'); + if(cp) { + *cp = '\0'; + strcpy(filename, cp+1); + } + else { + strcpy(filename, dirname); + strcpy(dirname, "/"); + } + + dip = ext2_namei(dirname); + if(!dip) { + printf("Directory %s does not exist\n", dirname); + ext2_close(); + exit(1); + } + + printf("Creating new file %s in directory %s\n", filename, dirname); + + /* Get an inode for the file */ + inumber = ext2_ialloc(); + ip = ext2_iget(inumber); + + if(!ip) { + printf("PANIC! ip == NULL\n"); + exit(1); + } + + /* Create the directory entry */ + ext2_mknod(dip, filename, inumber); + + /* We're done with the directory for now... */ + ext2_iput(dip); + + /* Set certain fields in the inode (we're not going to get + * fancy here... just choose a safe set of defaults...) + */ + ip->i_mode = 0550 | S_IFREG; /* Regular file, r-xr-x--- */ + ip->i_uid = 0; /* Owned by root */ + ip->i_gid = 0; /* Group is system */ + ip->i_size = 0; + ip->i_atime = ip->i_ctime = ip->i_mtime = time(0); + ip->i_dtime = 0; + ip->i_links_count = 1; + ip->i_blocks = 0; + ip->i_flags = 0; /* Nothing special */ + for(i = 0; i < EXT2_N_BLOCKS; i++) { + ip->i_block[i] = 0; + } + ip->i_version = 0; + ip->i_file_acl = 0; + ip->i_frag = 0; + ip->i_fsize = 0; + ip->i_reserved1 = ip->i_pad1 = ip->i_reserved2[0] = 0; + + } + + /* At this point we have an inode for an empty regular file. + * Fill it up! + */ + + bs_start = ext2_fill_contiguous(ip, bootstrap_size/blocksize); + if(bs_start <= 0) { + printf("Cannot allocate blocks for %s... goodbye!\n", argv[2]); + ext2_close(); + exit(1); + } + + /* Write what we've got out to the file */ + filesize = bootstrap_size; + blkno = 0; + while(filesize > 0) { + ext2_bwrite(ip, blkno, bsbuf+(blkno*blocksize)); + blkno++; + filesize -= blocksize; + } + + ip->i_size = bootstrap_size; + ip->i_mtime = time(0); + + + /* Prepare and write out a bootblock */ + memset(iobuf, 0, blocksize); + bbp = (struct boot_block *)iobuf; + + bbp->count = bootstrap_size / CONSOLE_BLOCK_SIZE; + bbp->lbn = (bs_start * blocksize) / CONSOLE_BLOCK_SIZE; + bbp->flags = 0; + + /* Compute the checksum */ + checksum = 0; + lbp = (u_int64_t*) bbp; + for (i = 0; i < CONSOLE_BLOCK_SIZE/8; ++i) { + checksum += lbp[i]; + } + bbp->chk_sum = checksum; + + if(big_endian) { + /* Need to flip the bootblock fields so they come out + * right on disk... + */ + bbp->count = (((u_int64_t) swap32(bbp->count & 0xffffffff) << 32) + | swap32(bbp->count >> 32)); + bbp->lbn = (((u_int64_t) swap32(bbp->lbn & 0xffffffff) << 32) + | swap32(bbp->lbn >> 32)); + bbp->flags = (((u_int64_t) swap32(bbp->flags & 0xffffffff) << 32) + | swap32(bbp->flags >> 32)); + bbp->chk_sum = (((u_int64_t) swap32(bbp->chk_sum & 0xffffffff) << 32) + | swap32(bbp->chk_sum >> 32)); + } + + ext2_write_bootblock((char *) bbp); + + ext2_iput(ip); + ext2_close(); + + printf("%d bytes written to %s\n", bootstrap_size, namebuf); + return 0; +} diff --git a/package/aboot/src/tools/elfencap.c b/package/aboot/src/tools/elfencap.c new file mode 100644 index 000000000..3e5f4b4b9 --- /dev/null +++ b/package/aboot/src/tools/elfencap.c @@ -0,0 +1,61 @@ +#include <fcntl.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +#include <sys/stat.h> + +#include <linux/elf.h> + + +int +main (int argc, char ** argv) +{ + int ifd; + ssize_t n; + char buf[8192]; + struct stat st; + struct { + struct elf64_hdr ehdr; + struct elf64_phdr phdr; + } h; + + ifd = open(argv[1], O_RDONLY); + if (ifd < 0) { + perror(argv[1]); + return 1; + } + + if (fstat(ifd, &st) < 0) { + perror(argv[1]); + return 1; + } + + memset(&h, 0, sizeof(h)); + + h.ehdr.e_ident[0] = 0x7f; + strcpy(h.ehdr.e_ident + 1, "ELF"); + h.ehdr.e_ident[EI_CLASS] = ELFCLASS64; + h.ehdr.e_ident[EI_DATA] = ELFDATA2LSB; + h.ehdr.e_ident[EI_VERSION] = EV_CURRENT; + h.ehdr.e_type = ET_EXEC; + h.ehdr.e_machine = EM_ALPHA; + h.ehdr.e_version = EV_CURRENT; + h.ehdr.e_entry = 0xfffffc0000310000; + h.ehdr.e_phnum = 1; + h.ehdr.e_phoff = (char *) &h.phdr - (char *) &h; + h.phdr.p_vaddr = 0xfffffc0000310000; + h.phdr.p_offset = sizeof(h); + h.phdr.p_filesz = st.st_size; + h.phdr.p_memsz = h.phdr.p_filesz; + + write(1, &h, sizeof(h)); + + while ((n = read(ifd, buf, sizeof(buf))) > 0) { + if (write(1, buf, n) != n) { + perror("short write"); + return 1; + } + } + return 0; +} diff --git a/package/aboot/src/tools/isomarkboot.c b/package/aboot/src/tools/isomarkboot.c new file mode 100644 index 000000000..ba01009fa --- /dev/null +++ b/package/aboot/src/tools/isomarkboot.c @@ -0,0 +1,239 @@ +/* + * isomarkboot.c + * + * This file is part of aboot, the SRM bootloader for Linux/Alpha + * Copyright (C) 1996 David Mosberger. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +/* + * Making an ISO9660 filesystem bootable is straight-forward since all + * files are contiguous. + */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <asm/fcntl.h> + +#include <config.h> +#include <isolib.h> +#include <iso.h> + +const char * prog_name; + +static int disk; + +void +__memcpy (void * dest, const void * src, size_t n) +{ + memcpy (dest, src, n); +} + +long +iso_dev_read (void * buf, long offset, long size) +{ + if (lseek(disk, offset, SEEK_SET) != offset) { + perror("lseek"); + return -1; + } + return read(disk, buf, size); +} + +/* Write a 64-bit quantity out into memory in LITTLE ENDIAN order */ +static void write_64 (unsigned char* out, unsigned long long in) +{ + out[0] = in & 0xFF; + out[1] = (in >> 8) & 0xFF; + out[2] = (in >> 16) & 0xFF; + out[3] = (in >> 24) & 0xFF; + out[4] = (in >> 32) & 0xFF; + out[5] = (in >> 40) & 0xFF; + out[6] = (in >> 48) & 0xFF; + out[7] = (in >> 56) & 0xFF; +} + +/* Read in a 64-bit LITTLE ENDIAN quantity */ +static unsigned long long read_64 (unsigned char *in) +{ + unsigned long long result = 0; + + result |= (unsigned long long) in[0]; + result |= (unsigned long long) in[1] << 8; + result |= (unsigned long long) in[2] << 16; + result |= (unsigned long long) in[3] << 24; + result |= (unsigned long long) in[4] << 32; + result |= (unsigned long long) in[5] << 40; + result |= (unsigned long long) in[6] << 48; + result |= (unsigned long long) in[7] << 56; + + return result; +} + +int +main (int argc, char ** argv) +{ + u_int64_t sector[512 / 8], sum; + struct iso_primary_descriptor vol_desc; + size_t nbytes, aboot_size; + off_t aboot_pos; + int i, aboot_fd; + int rootbin_fd; + off_t rootbin_pos; + char root_start[100]; + + prog_name = argv[0]; + + if (argc < 3 || argc > 4) { + fprintf(stderr, "usage: %s filesys path [root.bin]\n", prog_name); + exit(1); + } + disk = open(argv[1], O_RDWR); + if (disk < 0) { + perror(argv[1]); + exit(1); + } + + if (iso_read_super (0, 0) < 0) { + fprintf(stderr, "%s: cannot mount\n", argv[1]); + exit(1); + } + + aboot_fd = iso_open(argv[2]); + if (aboot_fd < 0) { + fprintf(stderr, "%s: file not found\n", argv[2]); + exit(1); + } + + { + struct stat buf; + iso_fstat(aboot_fd, &buf); + aboot_size = buf.st_size; + } + + aboot_pos = iso_map (aboot_fd, 0); + + printf("%s: %s is at offset %ld and is %lu bytes long\n", + prog_name, argv[2], aboot_pos, aboot_size); + + if (lseek(disk, 0, SEEK_SET) != 0) { + perror("lseek"); + return -1; + } + + nbytes = read(disk, sector, sizeof(sector)); + if (nbytes != sizeof(sector)) { + if ((long) nbytes < 0) { + perror("read"); + } else { + fprintf(stderr, "%s: short read\n", prog_name); + } + exit(1); + } + + strcpy((char *) sector, "Linux/Alpha aboot for ISO filesystem."); + write_64 ((unsigned char *) §or[60], aboot_size / 512);/* sector count */ + write_64 ((unsigned char *) §or[61], aboot_pos / 512); /* starting LBM */ + write_64 ((unsigned char *) §or[62], 0); /* flags */ + + /* update checksum: */ + sum = 0; + for (i = 0; i < 63; i++) + sum += read_64 ((unsigned char *) §or[i]); + + write_64 ((unsigned char *) §or[63], sum); + + if (lseek(disk, 0, SEEK_SET) != 0) { + perror("lseek"); + return -1; + } + + nbytes = write(disk, sector, sizeof(sector)); + if (nbytes != sizeof(sector)) { + if ((long) nbytes < 0) { + perror("write"); + } else { + fprintf(stderr, "%s: short write\n", prog_name); + } + exit(1); + } + + if (argc < 4) + return 0; + + rootbin_fd = iso_open(argv[3]); + if (rootbin_fd < 0) { + fprintf(stderr, "%s: file not found\n", argv[3]); + exit(1); + } + + rootbin_pos = iso_map (rootbin_fd, 0); + iso_close(rootbin_fd); + + { + struct stat buf; + iso_fstat(rootbin_fd, &buf); + printf("%s: %s is at offset %ld and is %lu bytes long\n", + prog_name, argv[3], rootbin_pos, buf.st_size); + } + + + if (lseek(disk, 16*2048, SEEK_SET) != 16*2048) { + perror("lseek"); + return -1; + } + nbytes = read(disk, &vol_desc, sizeof(vol_desc)); + if (nbytes != sizeof(vol_desc)) { + if ((long) nbytes < 0) { + perror("read"); + } else { + fprintf(stderr, "%s: short read\n", prog_name); + } + exit(1); + } + + if (strncmp (vol_desc.id, ISO_STANDARD_ID, sizeof vol_desc.id) != 0) { + fprintf(stderr,"first volume descriptor has not an ISO_STANDARD_ID!!\n"); + exit(1); + } + if (isonum_711 (vol_desc.type) != ISO_VD_PRIMARY) { + fprintf(stderr,"first volume descriptor is not a primary one!!\n"); + exit(1); + } + if (rootbin_pos & 2047) { + fprintf(stderr,"erreur:rootbin_pos=%ld is not at a isoblock boundary\n",rootbin_pos); + exit(1); + } + sprintf(root_start,"ROOT START=%ld ",rootbin_pos/2048); + printf("writing %s in application_data of first volume descriptor\n", root_start); + memcpy(vol_desc.application_data,root_start,strlen(root_start)); + if (lseek(disk, 16*2048, SEEK_SET) != 16*2048) { + perror("lseek"); + return -1; + } + + nbytes = write(disk, &vol_desc, sizeof(vol_desc)); + if (nbytes != sizeof(vol_desc)) { + if ((long) nbytes < 0) { + perror("write"); + } else { + fprintf(stderr, "%s: short write\n", prog_name); + } + exit(1); + } + + return 0; +} diff --git a/package/aboot/src/tools/objstrip.c b/package/aboot/src/tools/objstrip.c new file mode 100644 index 000000000..19612644b --- /dev/null +++ b/package/aboot/src/tools/objstrip.c @@ -0,0 +1,260 @@ +/* + * arch/alpha/boot/tools/objstrip.c + * + * Strip the object file headers/trailers from an executable (ELF). + * + * Copyright (C) 1996 David Mosberger-Tang. + */ +/* + * Converts an ELF object file into a bootable file. The + * object file must be a OMAGIC file (i.e., data and bss follow immediatly + * behind the text). See DEC "Assembly Language Programmer's Guide" + * documentation for details. The SRM boot process is documented in + * the Alpha AXP Architecture Reference Manual, Second Edition by + * Richard L. Sites and Richard T. Witek. + */ +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> + +#include <sys/fcntl.h> +#include <sys/stat.h> +#include <sys/types.h> + +#include <a.out.h> +#include <linux/coff.h> +#include <linux/param.h> +#include <string.h> + +#ifdef __ELF__ +# include <linux/elf.h> +# include <linux/version.h> +# if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24) +# define elf_check_arch(x) ((x)->e_machine == EM_ALPHA) +# endif +# if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) +# define aboot_elf_check_arch(e) elf_check_arch(e) +# else +# define aboot_elf_check_arch(e) elf_check_arch(e->e_machine) +# endif +#endif + +/* bootfile size must be multiple of BLOCK_SIZE: */ +#define BLOCK_SIZE 512 + +const char * prog_name; + + +void +usage (void) +{ + fprintf(stderr, + "usage: %s [-v] -p file primary\n" + " %s [-vb] file [secondary]\n", prog_name, prog_name); + exit(1); +} + + +int +main (int argc, char *argv[]) +{ + size_t nwritten, tocopy, n, mem_size, fil_size, pad = 0; + int fd, ofd, i, j, verbose = 0, primary = 0; + char buf[8192], *inname; + struct exec * aout; /* includes file & aout header */ + long offset; +#ifdef __ELF__ + struct elf64_hdr *elf; + struct elf64_phdr *elf_phdr; /* program header */ + unsigned long long e_entry; +#endif + + prog_name = argv[0]; + + for (i = 1; i < argc && argv[i][0] == '-'; ++i) { + for (j = 1; argv[i][j]; ++j) { + switch (argv[i][j]) { + case 'v': + verbose = ~verbose; + break; + + case 'b': + pad = BLOCK_SIZE; + break; + + case 'p': + primary = 1; /* make primary bootblock */ + break; + } + } + } + + if (i >= argc) { + usage(); + } + inname = argv[i++]; + + fd = open(inname, O_RDONLY); + if (fd == -1) { + perror("open"); + exit(1); + } + + ofd = 1; + if (i < argc) { + ofd = open(argv[i++], O_WRONLY | O_CREAT | O_TRUNC, 0666); + if (fd == -1) { + perror("open"); + exit(1); + } + } + + if (primary) { + /* generate bootblock for primary loader */ + + unsigned long bb[64], sum = 0; + struct stat st; + off_t size; + int i; + + if (ofd == 1) { + usage(); + } + + if (fstat(fd, &st) == -1) { + perror("fstat"); + exit(1); + } + + size = (st.st_size + BLOCK_SIZE - 1) & ~(BLOCK_SIZE - 1); + memset(bb, 0, sizeof(bb)); + strcpy((char *) bb, "Linux SRM bootblock"); + bb[60] = size / BLOCK_SIZE; /* count */ + bb[61] = 1; /* starting sector # */ + bb[62] = 0; /* flags---must be 0 */ + for (i = 0; i < 63; ++i) { + sum += bb[i]; + } + bb[63] = sum; + if (write(ofd, bb, sizeof(bb)) != sizeof(bb)) { + perror("boot-block write"); + exit(1); + } + printf("%lu\n", size); + return 0; + } + + /* read and inspect exec header: */ + + if (read(fd, buf, sizeof(buf)) < 0) { + perror("read"); + exit(1); + } + +#ifdef __ELF__ + elf = (struct elf64_hdr *) buf; + + if (elf->e_ident[0] == 0x7f && strncmp(elf->e_ident + 1, "ELF", 3) == 0) { + if (elf->e_type != ET_EXEC) { + fprintf(stderr, "%s: %s is not an ELF executable\n", + prog_name, inname); + exit(1); + } + if (!aboot_elf_check_arch(elf)) { + fprintf(stderr, "%s: is not for this processor (e_machine=%d)\n", + prog_name, elf->e_machine); + exit(1); + } + if (elf->e_phnum != 1) { + fprintf(stderr, + "%s: %d program headers (forgot to link with -N?)\n", + prog_name, elf->e_phnum); + } + + e_entry = elf->e_entry; + + lseek(fd, elf->e_phoff, SEEK_SET); + if (read(fd, buf, sizeof(*elf_phdr)) != sizeof(*elf_phdr)) { + perror("read"); + exit(1); + } + + elf_phdr = (struct elf64_phdr *) buf; + offset = elf_phdr->p_offset; + mem_size = elf_phdr->p_memsz; + fil_size = elf_phdr->p_filesz; + + /* work around ELF bug: */ + if (elf_phdr->p_vaddr < e_entry) { + unsigned long delta = e_entry - elf_phdr->p_vaddr; + offset += delta; + mem_size -= delta; + fil_size -= delta; + elf_phdr->p_vaddr += delta; + } + + if (verbose) { + fprintf(stderr, "%s: extracting %#016lx-%#016lx (at %lx)\n", + prog_name, (long) elf_phdr->p_vaddr, + elf_phdr->p_vaddr + fil_size, offset); + } + } +#endif + + if (lseek(fd, offset, SEEK_SET) != offset) { + perror("lseek"); + exit(1); + } + + if (verbose) { + fprintf(stderr, "%s: copying %lu byte from %s\n", + prog_name, (unsigned long) fil_size, inname); + } + + tocopy = fil_size; + while (tocopy > 0) { + n = tocopy; + if (n > sizeof(buf)) { + n = sizeof(buf); + } + tocopy -= n; + if ((size_t) read(fd, buf, n) != n) { + perror("read"); + exit(1); + } + do { + nwritten = write(ofd, buf, n); + if ((ssize_t) nwritten == -1) { + perror("write"); + exit(1); + } + n -= nwritten; + } while (n > 0); + } + + if (pad) { + mem_size = ((mem_size + pad - 1) / pad) * pad; + } + + tocopy = mem_size - fil_size; + if (tocopy > 0) { + fprintf(stderr, + "%s: zero-filling bss and aligning to %lu with %lu bytes\n", + prog_name, pad, (unsigned long) tocopy); + + memset(buf, 0x00, sizeof(buf)); + do { + n = tocopy; + if (n > sizeof(buf)) { + n = sizeof(buf); + } + nwritten = write(ofd, buf, n); + if ((ssize_t) nwritten == -1) { + perror("write"); + exit(1); + } + tocopy -= nwritten; + } while (tocopy > 0); + } + return 0; +} |