bool ext2_read_inode(PEXT2_FILESYS Ext2Sys, ULONG ino, ULONG offset, PVOID Buffer, ULONG size, PULONG dwReturn) { PEXT2_BDL ext2_bdl = NULL; ULONG blocks, i; bool bRet = true; EXT2_INODE ext2_inode; ULONG dwTotal = 0; if (!ext2_load_inode(Ext2Sys, ino, &ext2_inode)) { return false; } blocks = ext2_build_bdl(Ext2Sys, &ext2_inode, offset, size, &ext2_bdl); if (blocks <= 0) return false; for(i = 0; i < blocks; i++) { bRet = NT_SUCCESS(Ext2ReadDisk( Ext2Sys, ext2_bdl[i].Lba, ext2_bdl[i].Length, (PUCHAR)Buffer + ext2_bdl[i].Offset )); if (!bRet) break; dwTotal += ext2_bdl[i].Length; } *dwReturn = dwTotal; if (ext2_bdl) RtlFreeHeap(RtlGetProcessHeap(), 0, ext2_bdl); return bRet; }
int ext2_dirent(fscookie cookie, const char *_path, unsigned index, char **name) { ext2_t *ext2 = (ext2_t *)cookie; inodenum_t diri; struct ext2_inode inode; struct ext2_inode *inodep; int rv; if (strlen(_path) != 0) { rv = ext2_walk(ext2, _path, &ext2->root_inode, &diri, 1); if (rv < 0) return rv; rv = ext2_load_inode(ext2, diri, &inode); if (rv < 0) return rv; inodep = &inode; } else { inodep = &ext2->root_inode; } return ext2_dirent_lookup(ext2, inodep, name, index); }
int ext2_mount(bdev_t *dev, fscookie *cookie) { int err; LTRACEF("dev %p\n", dev); ext2_t *ext2 = malloc(sizeof(ext2_t)); ext2->dev = dev; err = bio_read(dev, &ext2->sb, 1024, sizeof(struct ext2_super_block)); if (err < 0) goto err; endian_swap_superblock(&ext2->sb); /* see if the superblock is good */ if (ext2->sb.s_magic != EXT2_SUPER_MAGIC) { err = -1; return err; } /* calculate group count, rounded up */ ext2->s_group_count = (ext2->sb.s_blocks_count + ext2->sb.s_blocks_per_group - 1) / ext2->sb.s_blocks_per_group; /* print some info */ LTRACEF("rev level %d\n", ext2->sb.s_rev_level); LTRACEF("compat features 0x%x\n", ext2->sb.s_feature_compat); LTRACEF("incompat features 0x%x\n", ext2->sb.s_feature_incompat); LTRACEF("ro compat features 0x%x\n", ext2->sb.s_feature_ro_compat); LTRACEF("block size %d\n", EXT2_BLOCK_SIZE(ext2->sb)); LTRACEF("inode size %d\n", EXT2_INODE_SIZE(ext2->sb)); LTRACEF("block count %d\n", ext2->sb.s_blocks_count); LTRACEF("blocks per group %d\n", ext2->sb.s_blocks_per_group); LTRACEF("group count %d\n", ext2->s_group_count); LTRACEF("inodes per group %d\n", ext2->sb.s_inodes_per_group); /* we only support dynamic revs */ if (ext2->sb.s_rev_level > EXT2_DYNAMIC_REV) { err = -2; return err; } /* make sure it doesn't have any ro features we don't support */ if (ext2->sb.s_feature_ro_compat & ~(EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER|EXT2_FEATURE_RO_COMPAT_LARGE_FILE)) { err = -3; return err; } /* read in all the group descriptors */ ext2->gd = malloc(sizeof(struct ext2_group_desc) * ext2->s_group_count); err = bio_read(ext2->dev, (void *)ext2->gd, (EXT2_BLOCK_SIZE(ext2->sb) == 4096) ? 4096 : 2048, sizeof(struct ext2_group_desc) * ext2->s_group_count); if (err < 0) { err = -4; return err; } int i; for (i=0; i < ext2->s_group_count; i++) { endian_swap_group_desc(&ext2->gd[i]); LTRACEF("group %d:\n", i); LTRACEF("\tblock bitmap %d\n", ext2->gd[i].bg_block_bitmap); LTRACEF("\tinode bitmap %d\n", ext2->gd[i].bg_inode_bitmap); LTRACEF("\tinode table %d\n", ext2->gd[i].bg_inode_table); LTRACEF("\tfree blocks %d\n", ext2->gd[i].bg_free_blocks_count); LTRACEF("\tfree inodes %d\n", ext2->gd[i].bg_free_inodes_count); LTRACEF("\tused dirs %d\n", ext2->gd[i].bg_used_dirs_count); } /* initialize the block cache */ ext2->cache = bcache_create(ext2->dev, EXT2_BLOCK_SIZE(ext2->sb), 4); /* load the first inode */ err = ext2_load_inode(ext2, EXT2_ROOT_INO, &ext2->root_inode); if (err < 0) goto err; // TRACE("successfully mounted volume\n"); *cookie = ext2; return 0; err: LTRACEF("exiting with err code %d\n", err); free(ext2); return err; }
/* note, trashes path */ static int ext2_walk(ext2_t *ext2, char *path, struct ext2_inode *start_inode, inodenum_t *inum, int recurse) { char *ptr; struct ext2_inode inode; struct ext2_inode dir_inode; int err; bool done; LTRACEF("path '%s', start_inode %p, inum %p, recurse %d\n", path, start_inode, inum, recurse); if (recurse > 4) return ERR_RECURSE_TOO_DEEP; /* chew up leading slashes */ ptr = &path[0]; while (*ptr == '/') ptr++; done = false; memcpy(&dir_inode, start_inode, sizeof(struct ext2_inode)); while (!done) { /* process the first component */ char *next_sep = strchr(ptr, '/'); if (next_sep) { /* terminate the next component, giving us a substring */ *next_sep = 0; } else { /* this is the last component */ done = true; } LTRACEF("component '%s', done %d\n", ptr, done); /* do the lookup on this component */ err = ext2_dir_lookup(ext2, &dir_inode, ptr, inum); if (err < 0) return err; nextcomponent: LTRACEF("inum %u\n", *inum); /* load the next inode */ err = ext2_load_inode(ext2, *inum, &inode); if (err < 0) return err; /* is it a symlink? */ if (S_ISLNK(inode.i_mode)) { char link[512]; LTRACEF("hit symlink\n"); err = ext2_read_link(ext2, &inode, link, sizeof(link)); if (err < 0) return err; LTRACEF("symlink read returns %d '%s'\n", err, link); /* recurse, parsing the link */ if (link[0] == '/') { /* link starts with '/', so start over again at the rootfs */ err = ext2_walk(ext2, link, &ext2->root_inode, inum, recurse + 1); } else { err = ext2_walk(ext2, link, &dir_inode, inum, recurse + 1); } LTRACEF("recursive walk returns %d\n", err); if (err < 0) return err; /* if we weren't done with our path parsing, start again with the result of this recurse */ if (!done) { goto nextcomponent; } } else if (S_ISDIR(inode.i_mode)) { /* for the next cycle, point the dir inode at our new directory */ memcpy(&dir_inode, &inode, sizeof(struct ext2_inode)); } else { if (!done) { /* we aren't done and this walked over a nondir, abort */ LTRACEF("not finished and component is nondir\n"); return ERR_NOT_FOUND; } } if (!done) { /* move to the next seperator */ ptr = next_sep + 1; /* consume multiple seperators */ while (*ptr == '/') ptr++; } } return 0; }
bool ext2_add_entry( PEXT2_FILESYS Ext2Sys, ULONG parent, ULONG inode, int filetype, char *name ) { PEXT2_DIR_ENTRY2 dir = NULL, newdir = NULL; EXT2_INODE parent_inode; ULONG dwRet; char *buf; int rec_len; bool bRet = false; rec_len = EXT2_DIR_REC_LEN(strlen(name)); if (!ext2_load_inode(Ext2Sys, parent, &parent_inode)) { return false; } buf = (char *)RtlAllocateHeap(RtlGetProcessHeap(), 0, parent_inode.i_size); if (!ext2_read_inode(Ext2Sys, parent, 0, buf, parent_inode.i_size, &dwRet)) { return false; } dir = (PEXT2_DIR_ENTRY2) buf; while ((char *)dir < buf + parent_inode.i_size) { if ((dir->inode == 0 && dir->rec_len >= rec_len) || (dir->rec_len >= dir->name_len + rec_len) ) { if (dir->inode) { newdir = (PEXT2_DIR_ENTRY2) ((PUCHAR)dir + EXT2_DIR_REC_LEN(dir->name_len)); newdir->rec_len = dir->rec_len - EXT2_DIR_REC_LEN(dir->name_len); dir->rec_len = EXT2_DIR_REC_LEN(dir->name_len); dir = newdir; } dir->file_type = filetype; dir->inode = inode; dir->name_len = strlen(name); memcpy(dir->name, name, strlen(name)); bRet = true; break; } dir = (PEXT2_DIR_ENTRY2) (dir->rec_len + (PUCHAR) dir); } if (bRet) return ext2_write_inode(Ext2Sys, parent, 0, buf, parent_inode.i_size, &dwRet); return bRet; }
bool ext2_write_inode (PEXT2_FILESYS Ext2Sys, ULONG ino, ULONG offset, PVOID Buffer, ULONG size, PULONG dwReturn ) { PEXT2_BDL ext2_bdl = NULL; ULONG blocks, i; bool bRet = true; EXT2_INODE inode; ULONG dwTotal = 0; ULONG dwBlk = 0; ULONG TotalBlks; blocks = (size + offset + Ext2Sys->blocksize - 1) / Ext2Sys->blocksize; if (!ext2_load_inode(Ext2Sys, ino, &inode)) { return false; } TotalBlks = inode.i_blocks / (Ext2Sys->blocksize / SECTOR_SIZE); TotalBlks = Ext2DataBlocks(Ext2Sys, TotalBlks); if (blocks > TotalBlks) { for (i=0; i < (blocks - TotalBlks); i++) { if (ext2_alloc_block(Ext2Sys, 0, &dwBlk) ) { ext2_expand_inode(Ext2Sys, &inode, dwBlk); inode.i_blocks += (Ext2Sys->blocksize/SECTOR_SIZE); } } } blocks = ext2_build_bdl(Ext2Sys, &inode, offset, size, &ext2_bdl); if (blocks <= 0) return false; for(i = 0; i < blocks; i++) { bRet = NT_SUCCESS(Ext2WriteDisk( Ext2Sys, ext2_bdl[i].Lba, ext2_bdl[i].Length, (PUCHAR)Buffer + ext2_bdl[i].Offset )); if (!bRet) { goto errorout; } dwTotal += ext2_bdl[i].Length; } *dwReturn = dwTotal; if (size + offset > inode.i_size) { inode.i_size = size + offset; } ext2_save_inode(Ext2Sys, ino, &inode); errorout: if (ext2_bdl) RtlFreeHeap(RtlGetProcessHeap(), 0, ext2_bdl); return bRet; }