/* * One 4-byte pointer per block and then the actual blocked * output. The first block does not need an offset pointer, * as it will start immediately after the pointer block; * so the i'th pointer points to the end of the i'th block * (i.e. the start of the (i+1)'th block or past EOF). * * Note that size > 0, as a zero-sized file wouldn't ever * have gotten here in the first place. */ static unsigned int do_compress(char *base, unsigned int offset, char const *name, char *uncompressed, unsigned int size) { unsigned long original_size = size; unsigned long original_offset = offset; unsigned long new_size; unsigned long blocks = (size - 1) / blksize + 1; unsigned long curr = offset + 4 * blocks; int change; total_blocks += blocks; do { unsigned long len = 2 * blksize; unsigned int input = size; int err; if (input > blksize) input = blksize; size -= input; if (!(opt_holes && is_zero (uncompressed, input))) { err = compress_data(base + curr, &len, uncompressed, input, Z_BEST_COMPRESSION); if (err != Z_OK) { die(MKFS_ERROR, 0, "compression error: %s", zError(err)); } curr += len; } uncompressed += input; if (len > blksize*2) { /* (I don't think this can happen with zlib.) */ die(MKFS_ERROR, 0, "AIEEE: block \"compressed\" to > 2*blocklength (%ld)", len); } *(u32 *) (base + offset) = CRAMFS_32(curr); offset += 4; } while (size); curr = (curr + 3) & ~3; new_size = curr - original_offset; /* TODO: Arguably, original_size in these 2 lines should be st_blocks * 512. But if you say that then perhaps administrative data should also be included in both. */ change = new_size - original_size; if (opt_verbose > 1) { printf("%6.2f%% (%+d bytes)\t%s\n", (change * 100) / (double) original_size, change, name); } return curr; }
/* * One 4-byte pointer per block and then the actual blocked * output. The first block does not need an offset pointer, * as it will start immediately after the pointer block; * so the i'th pointer points to the end of the i'th block * (i.e. the start of the (i+1)'th block or past EOF). * * Note that size > 0, as a zero-sized file wouldn't ever * have gotten here in the first place. */ static unsigned int do_compress(char *base, unsigned int offset, struct entry *entry) { unsigned int size = entry->size; unsigned long original_size = size; unsigned long original_offset = offset; unsigned long new_size; unsigned long blocks = (size - 1) / blksize + 1; unsigned long curr = offset + 4 * blocks; int change; char *uncompressed = entry->uncompressed; total_blocks += blocks; do { unsigned long len = 2 * blksize; unsigned int input = size; if (input > blksize) input = blksize; size -= input; if (!is_zero (uncompressed, input)) { compress(base + curr, &len, uncompressed, input); curr += len; } uncompressed += input; if (len > blksize*2) { /* (I don't think this can happen with zlib.) */ error_msg_and_die("AIEEE: block \"compressed\" to > 2*blocklength (%ld)\n", len); } *(u32 *) (base + offset) = CRAMFS_32(curr); offset += 4; } while (size); curr = (curr + 3) & ~3; new_size = curr - original_offset; /* TODO: Arguably, original_size in these 2 lines should be st_blocks * 512. But if you say that then perhaps administrative data should also be included in both. */ change = new_size - original_size; #if 0 if (opt_verbose) { printf("%6.2f%% (%+d bytes)\t%s\n", (change * 100) / (double) original_size, change, entry->name); } #endif return curr; }
static void do_uncompress(char *path, int fd, unsigned long offset, unsigned long size) { unsigned long curr = offset + 4 * ((size + PAGE_CACHE_SIZE - 1) / PAGE_CACHE_SIZE); do { unsigned long out = PAGE_CACHE_SIZE; unsigned long next = CRAMFS_32(*(u32 *) romfs_read(offset)); if (next > end_data) { end_data = next; } offset += 4; if (curr == next) { if (opt_verbose > 1) { printf(" hole at %ld (%d)\n", curr, PAGE_CACHE_SIZE); } if (size < PAGE_CACHE_SIZE) out = size; memset(outbuffer, 0x00, out); } else { if (opt_verbose > 1) { printf(" uncompressing block at %ld to %ld (%ld)\n", curr, next, next - curr); } out = uncompress_block(romfs_read(curr), next - curr); } if (size >= PAGE_CACHE_SIZE) { if (out != PAGE_CACHE_SIZE) { die(FSCK_UNCORRECTED, 0, "non-block (%ld) bytes", out); } } else { if (out != size) { die(FSCK_UNCORRECTED, 0, "non-size (%ld vs %ld) bytes", out, size); } } size -= out; if (opt_extract) { if (write(fd, outbuffer, out) < 0) { die(FSCK_ERROR, 1, "write failed: %s", path); } } curr = next; } while (size); }
static void do_symlink(char *path, struct cramfs_inode *i) { unsigned long offset = i->offset << 2; unsigned long curr = offset + 4; unsigned long next = CRAMFS_32(*(u32 *) romfs_read(offset)); unsigned long size; if (offset == 0) { die(FSCK_UNCORRECTED, 0, "symbolic link has zero offset"); } if (i->size == 0) { die(FSCK_UNCORRECTED, 0, "symbolic link has zero size"); } if (offset < start_data) { start_data = offset; } if (next > end_data) { end_data = next; } size = uncompress_block(romfs_read(curr), next - curr); if (size != i->size) { die(FSCK_UNCORRECTED, 0, "size error in symlink: %s", path); } outbuffer[size] = 0; if (opt_verbose) { char *str; asprintf(&str, "%s -> %s", path, outbuffer); print_node('l', i, str); if (opt_verbose > 1) { printf(" uncompressing block at %ld to %ld (%ld)\n", curr, next, next - curr); } free(str); } if (opt_extract) { if (symlink(outbuffer, path) < 0) { die(FSCK_ERROR, 1, "symlink failed: %s", path); } change_file_status(path, i); } }
/* Returns sizeof(struct cramfs_super), which includes the root inode. */ static unsigned int write_superblock(struct entry *root, char *base, int size) { struct cramfs_super *super = (struct cramfs_super *) base; unsigned int offset = sizeof(struct cramfs_super) + image_length; offset += opt_pad; /* 0 if no padding */ super->magic = CRAMFS_32(CRAMFS_MAGIC); super->flags = CRAMFS_FLAG_FSID_VERSION_2 | CRAMFS_FLAG_SORTED_DIRS; if (opt_holes) super->flags |= CRAMFS_FLAG_HOLES; if (image_length > 0) super->flags |= CRAMFS_FLAG_SHIFTED_ROOT_OFFSET; super->flags |= blksize_bit << CRAMFS_FLAG_BLKSZ_SHIFT; super->flags |= opt_compression << CRAMFS_FLAG_COMP_METHOD_SHIFT; super->flags = CRAMFS_32(super->flags); super->size = CRAMFS_32(size); memcpy(super->signature, CRAMFS_SIGNATURE, sizeof(super->signature)); super->fsid.crc = CRAMFS_32(crc32(0L, Z_NULL, 0)); super->fsid.edition = CRAMFS_32(opt_edition); super->fsid.blocks = CRAMFS_32(total_blocks); super->fsid.files = CRAMFS_32(total_nodes); memset(super->name, 0x00, sizeof(super->name)); if (opt_name) strncpy(super->name, opt_name, sizeof(super->name)); else strncpy(super->name, "Compressed", sizeof(super->name)); super->root.mode = CRAMFS_16(root->mode); super->root.uid = CRAMFS_16(root->uid); super->root.gid = root->gid; super->root.size = CRAMFS_24(root->size); CRAMFS_SET_OFFSET(&(super->root), offset >> 2); return offset; }
static void test_super(int *start, size_t *length) { struct stat st; /* find the physical size of the file or block device */ if (stat(filename, &st) < 0) { die(FSCK_ERROR, 1, "stat failed: %s", filename); } fd = open(filename, O_RDONLY); if (fd < 0) { die(FSCK_ERROR, 1, "open failed: %s", filename); } if (S_ISBLK(st.st_mode)) { if (ioctl(fd, BLKGETSIZE, length) < 0) { die(FSCK_ERROR, 1, "ioctl failed: unable to determine device size: %s", filename); } *length = *length * 512; } else if (S_ISREG(st.st_mode)) { *length = st.st_size; } else { die(FSCK_ERROR, 0, "not a block device or file: %s", filename); } if (*length < sizeof(struct cramfs_super)) { die(FSCK_UNCORRECTED, 0, "file length too short"); } /* find superblock */ if (read(fd, &super, sizeof(super)) != sizeof(super)) { die(FSCK_ERROR, 1, "read failed: %s", filename); } if (super.magic == CRAMFS_MAGIC) { *start = 0; } else if (super.magic == bswap_32(CRAMFS_MAGIC)) { *start = 0; need_swapping = 1; } else if (*length >= (PAD_SIZE + sizeof(super))) { lseek(fd, PAD_SIZE, SEEK_SET); if (read(fd, &super, sizeof(super)) != sizeof(super)) { die(FSCK_ERROR, 1, "read failed: %s", filename); } if (super.magic == CRAMFS_32(CRAMFS_MAGIC)) { *start = PAD_SIZE; } } /* superblock tests */ if (super.magic != CRAMFS_32(CRAMFS_MAGIC)) { die(FSCK_UNCORRECTED, 0, "superblock magic not found"); } if (need_swapping){ super.size = bswap_32(super.size); super.flags = bswap_32(super.flags); super.future = bswap_32(super.future); super.fsid.crc = bswap_32(super.fsid.crc); super.fsid.edition = bswap_32(super.fsid.edition); super.fsid.blocks = bswap_32(super.fsid.blocks); super.fsid.files = bswap_32(super.fsid.files); } if (super.flags & ~CRAMFS_SUPPORTED_FLAGS) { die(FSCK_ERROR, 0, "unsupported filesystem features"); } if (super.size < PAGE_CACHE_SIZE) { die(FSCK_UNCORRECTED, 0, "superblock size (%d) too small", super.size); } if (super.flags & CRAMFS_FLAG_FSID_VERSION_2) { if (super.fsid.files == 0) { die(FSCK_UNCORRECTED, 0, "zero file count"); } if (*length < super.size) { die(FSCK_UNCORRECTED, 0, "file length too short"); } else if (*length > super.size) { fprintf(stderr, "warning: file extends past end of filesystem\n"); } } else { fprintf(stderr, "warning: old cramfs format\n"); } }
int cramfs_init(char *filename) { size_t length = 0; struct stat st; /* find the physical size of the file or block device */ if (stat(filename, &st) < 0) { return FILEERROR; } fd = open(filename, O_RDONLY); if (fd < 0) { return FILEERROR; } if (S_ISBLK(st.st_mode)) { if (ioctl(fd, BLKGETSIZE, length) < 0) { fprintf(stderr, "unable to determine device size: %s\n", filename); return FILEERROR; } length = length * 512; } else if (S_ISREG(st.st_mode)) { length = st.st_size; } else { fprintf(stderr, "not a block device or file: %s\n", filename); return FILEINVALID; } if (length < sizeof(struct cramfs_super)) { fprintf(stderr, "file length too short\n"); return FILEINVALID; } /* find superblock */ if (read(fd, &super, sizeof(super)) != sizeof(super)) { fprintf(stderr, "read failed: %s\n", filename); return FILEINVALID; } if (super.magic == CRAMFS_32(CRAMFS_MAGIC)) { start = 0; } else if (length >= (PAD_SIZE + sizeof(super))) { lseek(fd, PAD_SIZE, SEEK_SET); if (read(fd, &super, sizeof(super)) != sizeof(super)) { fprintf(stderr, "read failed: %s\n", filename); return FILEINVALID; } if (super.magic == CRAMFS_32(CRAMFS_MAGIC)) { start = PAD_SIZE; } } /* superblock tests */ if (super.magic != CRAMFS_32(CRAMFS_MAGIC)) { fprintf(stderr, "superblock magic not found\n"); return FILEINVALID; } #if __BYTE_ORDER == __BIG_ENDIAN super.size = CRAMFS_32(super.size); super.flags = CRAMFS_32(super.flags); super.future = CRAMFS_32(super.future); super.fsid.crc = CRAMFS_32(super.fsid.crc); super.fsid.edition = CRAMFS_32(super.fsid.edition); super.fsid.blocks = CRAMFS_32(super.fsid.blocks); super.fsid.files = CRAMFS_32(super.fsid.files); #endif /* __BYTE_ORDER == __BIG_ENDIAN */ if (super.flags & ~CRAMFS_SUPPORTED_FLAGS) { fprintf(stderr, "unsupported filesystem features\n"); return FILEINVALID; } if (super.size < PAGE_CACHE_SIZE) { fprintf(stderr, "superblock size (%d) too small\n", super.size); return FILEINVALID; } if (super.flags & CRAMFS_FLAG_FSID_VERSION_2) { if (super.fsid.files == 0) { fprintf(stderr, "zero file count\n"); return FILEINVALID; } if (length < super.size) { fprintf(stderr, "file length too short\n"); return FILEINVALID; } else if (length > super.size) { fprintf(stderr, "warning: file extends past end of filesystem\n"); } } else { fprintf(stderr, "warning: old cramfs format\n"); } if (!(super.flags & CRAMFS_FLAG_FSID_VERSION_2)) { fprintf(stderr, "unable to test CRC: old cramfs format\n"); return FILEINVALID; } return 1; }