123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473 |
- /* 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);
- }
|