(root)/
util-linux-2.39/
disk-utils/
fsck.cramfs.c
       1  /*
       2   * cramfsck - check a cramfs file system
       3   *
       4   * Copyright (C) 2000-2002 Transmeta Corporation
       5   *               2005 Adrian Bunk
       6   *
       7   * This program is free software; you can redistribute it and/or modify
       8   * it under the terms of the GNU General Public License as published by
       9   * the Free Software Foundation; either version 2 of the License, or
      10   * (at your option) any later version.
      11   *
      12   * This program is distributed in the hope that it will be useful,
      13   * but WITHOUT ANY WARRANTY; without even the implied warranty of
      14   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      15   * GNU General Public License for more details.
      16   *
      17   * You should have received a copy of the GNU General Public License along
      18   * with this program; if not, write to the Free Software Foundation, Inc.,
      19   * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
      20   *
      21   * 1999/12/03: Linus Torvalds (cramfs tester and unarchive program)
      22   * 2000/06/03: Daniel Quinlan (CRC and length checking program)
      23   * 2000/06/04: Daniel Quinlan (merged programs, added options, support
      24   *                            for special files, preserve permissions and
      25   *                            ownership, cramfs superblock v2, bogus mode
      26   *                            test, pathname length test, etc.)
      27   * 2000/06/06: Daniel Quinlan (support for holes, pretty-printing,
      28   *                            symlink size test)
      29   * 2000/07/11: Daniel Quinlan (file length tests, start at offset 0 or 512,
      30   *                            fsck-compatible exit codes)
      31   * 2000/07/15: Daniel Quinlan (initial support for block devices)
      32   * 2002/01/10: Daniel Quinlan (additional checks, test more return codes,
      33   *                            use read if mmap fails, standardize messages)
      34   */
      35  
      36  #include <stdio.h>
      37  #include <stdarg.h>
      38  #include <stdint.h>
      39  #include <unistd.h>
      40  #include <dirent.h>
      41  #include <stdlib.h>
      42  #include <errno.h>
      43  #include <string.h>
      44  #include <getopt.h>
      45  #include <fcntl.h>
      46  
      47  /* We don't use our include/crc32.h, but crc32 from zlib!
      48   *
      49   * The zlib implementation performs pre/post-conditioning. The util-linux
      50   * imlemenation requires post-conditioning (xor) in the applications.
      51   */
      52  #include <zlib.h>
      53  
      54  #include <sys/time.h>
      55  #include <sys/types.h>
      56  #include <sys/stat.h>
      57  #include <sys/mman.h>
      58  
      59  #include "c.h"
      60  #include "cramfs.h"
      61  #include "nls.h"
      62  #include "blkdev.h"
      63  #include "exitcodes.h"
      64  #include "strutils.h"
      65  #include "closestream.h"
      66  
      67  #define XALLOC_EXIT_CODE FSCK_EX_ERROR
      68  #include "xalloc.h"
      69  
      70  static int fd;			/* ROM image file descriptor */
      71  static char *filename;		/* ROM image filename */
      72  static struct cramfs_super super;	/* just find the cramfs superblock once */
      73  static int cramfs_is_big_endian = 0;	/* source is big endian */
      74  static int opt_verbose = 0;	/* 1 = verbose (-v), 2+ = very verbose (-vv) */
      75  static int opt_extract = 0;	/* extract cramfs (-x) */
      76  static char *extract_dir = "";		/* optional extraction directory (-x) */
      77  
      78  #define PAD_SIZE 512
      79  
      80  static uid_t euid;		/* effective UID */
      81  
      82  /* (cramfs_super + start) <= start_dir < end_dir <= start_data <= end_data */
      83  static unsigned long start_dir = ~0UL;	/* start of first non-root inode */
      84  static unsigned long end_dir = 0;	/* end of the directory structure */
      85  static unsigned long start_data = ~0UL;	/* start of the data (256 MB = max) */
      86  static unsigned long end_data = 0;	/* end of the data */
      87  
      88  
      89  /* Guarantee access to at least 2 * blksize at a time */
      90  #define CRAMFS_ROMBUFFER_BITS	13
      91  #define CRAMFS_ROMBUFFERSIZE	(1 << CRAMFS_ROMBUFFER_BITS)
      92  #define CRAMFS_ROMBUFFERMASK	(CRAMFS_ROMBUFFERSIZE - 1)
      93  
      94  /* Defaults, updated in main() according to block size */
      95  static size_t rombufbits = CRAMFS_ROMBUFFER_BITS;
      96  static size_t rombufsize = CRAMFS_ROMBUFFERSIZE;
      97  static size_t rombufmask = CRAMFS_ROMBUFFERMASK;
      98  
      99  static char *read_buffer;
     100  static unsigned long read_buffer_block = ~0UL;
     101  
     102  static z_stream stream;
     103  
     104  /* Prototypes */
     105  static void expand_fs(char *, struct cramfs_inode *);
     106  
     107  static char *outbuffer;
     108  
     109  static size_t blksize = 0;
     110  
     111  static void __attribute__((__noreturn__)) usage(void)
     112  {
     113  	FILE *out = stdout;
     114  
     115  	fputs(USAGE_HEADER, out);
     116  	fprintf(out,
     117  		_(" %s [options] <file>\n"), program_invocation_short_name);
     118  
     119  	fputs(USAGE_SEPARATOR, out);
     120  	fputs(_("Check and repair a compressed ROM filesystem.\n"), out);
     121  
     122  	fputs(USAGE_OPTIONS, out);
     123  	fputs(_(" -a                       for compatibility only, ignored\n"), out);
     124  	fputs(_(" -v, --verbose            be more verbose\n"), out);
     125  	fputs(_(" -y                       for compatibility only, ignored\n"), out);
     126  	fputs(_(" -b, --blocksize <size>   use this blocksize, defaults to page size\n"), out);
     127  	fputs(_("     --extract[=<dir>]    test uncompression, optionally extract into <dir>\n"), out);
     128  	fputs(USAGE_SEPARATOR, out);
     129  	printf(USAGE_HELP_OPTIONS(26));
     130  
     131  	printf(USAGE_MAN_TAIL("fsck.cramfs(8)"));
     132  	exit(FSCK_EX_OK);
     133  }
     134  
     135  static int get_superblock_endianness(uint32_t magic)
     136  {
     137  	if (magic == CRAMFS_MAGIC) {
     138  		cramfs_is_big_endian = HOST_IS_BIG_ENDIAN;
     139  		return 0;
     140  	}
     141  
     142  	if (magic ==
     143  		   u32_toggle_endianness(!HOST_IS_BIG_ENDIAN, CRAMFS_MAGIC)) {
     144  		cramfs_is_big_endian = !HOST_IS_BIG_ENDIAN;
     145  		return 0;
     146  	}
     147  
     148  	return -1;
     149  }
     150  
     151  static void test_super(int *start)
     152  {
     153  	struct stat st;
     154  	unsigned long long length;
     155  
     156  	fd = open(filename, O_RDONLY);
     157  	if (fd < 0)
     158  		err(FSCK_EX_ERROR, _("cannot open %s"), filename);
     159  
     160  	/* find the physical size of the file or block device */
     161  	if (fstat(fd, &st) < 0)
     162  		err(FSCK_EX_ERROR, _("stat of %s failed"), filename);
     163  
     164  	if (S_ISBLK(st.st_mode)) {
     165  		if (blkdev_get_size(fd, &length))
     166  			err(FSCK_EX_ERROR,
     167  			    _("ioctl failed: unable to determine device size: %s"),
     168  			    filename);
     169  	} else if (S_ISREG(st.st_mode))
     170  		length = st.st_size;
     171  	else
     172  		errx(FSCK_EX_ERROR, _("not a block device or file: %s"), filename);
     173  
     174  	if (length < sizeof(struct cramfs_super))
     175  		errx(FSCK_EX_UNCORRECTED, _("file length too short"));
     176  
     177  	/* find superblock */
     178  	if (read(fd, &super, sizeof(super)) != sizeof(super))
     179  		err(FSCK_EX_ERROR, _("cannot read %s"), filename);
     180  	if (get_superblock_endianness(super.magic) != -1)
     181  		*start = 0;
     182  	else if (length >= (PAD_SIZE + sizeof(super))) {
     183  		if (lseek(fd, PAD_SIZE, SEEK_SET) == (off_t) -1)
     184  			err(FSCK_EX_ERROR, _("seek on %s failed"), filename);
     185  		if (read(fd, &super, sizeof(super)) != sizeof(super))
     186  			err(FSCK_EX_ERROR, _("cannot read %s"), filename);
     187  		if (get_superblock_endianness(super.magic) != -1)
     188  			*start = PAD_SIZE;
     189  		else
     190  			errx(FSCK_EX_UNCORRECTED, _("superblock magic not found"));
     191  	} else
     192  		errx(FSCK_EX_UNCORRECTED, _("superblock magic not found"));
     193  
     194  	if (opt_verbose)
     195  		printf(_("cramfs endianness is %s\n"),
     196  		       cramfs_is_big_endian ? _("big") : _("little"));
     197  
     198  	super_toggle_endianness(cramfs_is_big_endian, &super);
     199  	if (super.flags & ~CRAMFS_SUPPORTED_FLAGS)
     200  		errx(FSCK_EX_ERROR, _("unsupported filesystem features"));
     201  
     202  	/* What are valid superblock sizes? */
     203  	if (super.size < *start + sizeof(struct cramfs_super))
     204  		errx(FSCK_EX_UNCORRECTED, _("superblock size (%d) too small"),
     205  		     super.size);
     206  
     207  	if (super.flags & CRAMFS_FLAG_FSID_VERSION_2) {
     208  		if (super.fsid.files == 0)
     209  			errx(FSCK_EX_UNCORRECTED, _("zero file count"));
     210  		if (length < super.size)
     211  			errx(FSCK_EX_UNCORRECTED, _("file length too short"));
     212  		else if (length > super.size)
     213  			warnx(_("file extends past end of filesystem"));
     214  	} else
     215  		warnx(_("old cramfs format"));
     216  }
     217  
     218  static void test_crc(int start)
     219  {
     220  	void *buf;
     221  	uint32_t crc;
     222  
     223  	if (!(super.flags & CRAMFS_FLAG_FSID_VERSION_2)) {
     224  		warnx(_("unable to test CRC: old cramfs format"));
     225  		return;
     226  	}
     227  
     228  	crc = crc32(0L, NULL, 0);
     229  
     230  	buf =
     231  	    mmap(NULL, super.size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
     232  	if (buf == MAP_FAILED) {
     233  		buf =
     234  		    mmap(NULL, super.size, PROT_READ | PROT_WRITE,
     235  			 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
     236  		if (buf != MAP_FAILED) {
     237  			ssize_t tmp;
     238  			if (lseek(fd, 0, SEEK_SET) == (off_t) -1)
     239  				err(FSCK_EX_ERROR, _("seek on %s failed"), filename);
     240  			tmp = read(fd, buf, super.size);
     241  			if (tmp < 0)
     242  				err(FSCK_EX_ERROR, _("cannot read %s"), filename);
     243  			if (tmp != (ssize_t) super.size)
     244  				errx(FSCK_EX_ERROR, _("failed to read %"PRIu32" bytes from file %s"),
     245  					super.size, filename);
     246  		}
     247  	}
     248  	if (buf != MAP_FAILED) {
     249  		((struct cramfs_super *)((unsigned char *) buf + start))->fsid.crc =
     250  		    crc32(0L, NULL, 0);
     251  		crc = crc32(crc, (unsigned char *) buf + start, super.size - start);
     252  		munmap(buf, super.size);
     253  	} else {
     254  		int retval;
     255  		size_t length = 0;
     256  
     257  		buf = xmalloc(4096);
     258  		if (lseek(fd, start, SEEK_SET) == (off_t) -1)
     259  			err(FSCK_EX_ERROR, _("seek on %s failed"), filename);
     260  		for (;;) {
     261  			retval = read(fd, buf, 4096);
     262  			if (retval < 0)
     263  				err(FSCK_EX_ERROR, _("cannot read %s"), filename);
     264  			else if (retval == 0)
     265  				break;
     266  			if (length == 0)
     267  				((struct cramfs_super *)buf)->fsid.crc =
     268  				    crc32(0L, NULL, 0);
     269  			length += retval;
     270  			if (length > (super.size - start)) {
     271  				crc = crc32(crc, buf,
     272  					  retval - (length -
     273  						    (super.size - start)));
     274  				break;
     275  			}
     276  			crc = crc32(crc, buf, retval);
     277  		}
     278  		free(buf);
     279  	}
     280  
     281  	if (crc != super.fsid.crc)
     282  		errx(FSCK_EX_UNCORRECTED, _("crc error"));
     283  }
     284  
     285  static void print_node(char type, struct cramfs_inode *i, char *name)
     286  {
     287  	char info[10];
     288  
     289  	if (S_ISCHR(i->mode) || (S_ISBLK(i->mode)))
     290  		/* major/minor numbers can be as high as 2^12 or 4096 */
     291  		snprintf(info, 10, "%4d,%4d", major(i->size), minor(i->size));
     292  	else
     293  		/* size be as high as 2^24 or 16777216 */
     294  		snprintf(info, 10, "%9d", i->size);
     295  
     296  	printf("%c %04o %s %5d:%-3d %s\n",
     297  	       type, i->mode & ~S_IFMT, info, i->uid, i->gid,
     298  	       !*name && type == 'd' ? "/" : name);
     299  }
     300  
     301  /*
     302   * Create a fake "blocked" access
     303   */
     304  static void *romfs_read(unsigned long offset)
     305  {
     306  	unsigned int block = offset >> rombufbits;
     307  	if (block != read_buffer_block) {
     308  		ssize_t x;
     309  
     310  		read_buffer_block = block;
     311  		if (lseek(fd, block << rombufbits, SEEK_SET) == (off_t) -1)
     312  			warn(_("seek failed"));
     313  
     314  		x = read(fd, read_buffer, rombufsize * 2);
     315  		if (x < 0)
     316  			warn(_("read romfs failed"));
     317  	}
     318  	return read_buffer + (offset & rombufmask);
     319  }
     320  
     321  static struct cramfs_inode *cramfs_iget(struct cramfs_inode *i)
     322  {
     323  	struct cramfs_inode *inode = xmalloc(sizeof(struct cramfs_inode));
     324  
     325  	inode_to_host(cramfs_is_big_endian, i, inode);
     326  	return inode;
     327  }
     328  
     329  static struct cramfs_inode *iget(unsigned int ino)
     330  {
     331  	return cramfs_iget(romfs_read(ino));
     332  }
     333  
     334  static void iput(struct cramfs_inode *inode)
     335  {
     336  	free(inode);
     337  }
     338  
     339  /*
     340   * Return the offset of the root directory
     341   */
     342  static struct cramfs_inode *read_super(void)
     343  {
     344  	struct cramfs_inode *root = cramfs_iget(&super.root);
     345  	unsigned long offset = root->offset << 2;
     346  
     347  	if (!S_ISDIR(root->mode))
     348  		errx(FSCK_EX_UNCORRECTED, _("root inode is not directory"));
     349  	if (!(super.flags & CRAMFS_FLAG_SHIFTED_ROOT_OFFSET) &&
     350  	    ((offset != sizeof(struct cramfs_super)) &&
     351  	     (offset != PAD_SIZE + sizeof(struct cramfs_super)))) {
     352  		errx(FSCK_EX_UNCORRECTED, _("bad root offset (%lu)"), offset);
     353  	}
     354  	return root;
     355  }
     356  
     357  static int uncompress_block(void *src, size_t len)
     358  {
     359  	int err;
     360  
     361  	stream.next_in = src;
     362  	stream.avail_in = len;
     363  
     364  	stream.next_out = (unsigned char *)outbuffer;
     365  	stream.avail_out = blksize * 2;
     366  
     367  	inflateReset(&stream);
     368  
     369  	if (len > blksize * 2)
     370  		errx(FSCK_EX_UNCORRECTED, _("data block too large"));
     371  
     372  	err = inflate(&stream, Z_FINISH);
     373  	if (err != Z_STREAM_END)
     374  		errx(FSCK_EX_UNCORRECTED, _("decompression error: %s"),
     375  		     zError(err));
     376  	return stream.total_out;
     377  }
     378  
     379  #ifndef HAVE_LCHOWN
     380  #define lchown chown
     381  #endif
     382  
     383  static void do_uncompress(char *path, int outfd, unsigned long offset,
     384  			  unsigned long size)
     385  {
     386  	unsigned long curr = offset + 4 * ((size + blksize - 1) / blksize);
     387  
     388  	do {
     389  		unsigned long out = blksize;
     390  		unsigned long next = u32_toggle_endianness(cramfs_is_big_endian,
     391  							   *(uint32_t *)
     392  							   romfs_read(offset));
     393  
     394  		if (next > end_data)
     395  			end_data = next;
     396  
     397  		offset += 4;
     398  		if (curr == next) {
     399  			if (opt_verbose > 1)
     400  				printf(_("  hole at %lu (%zu)\n"), curr,
     401  				       blksize);
     402  			if (size < blksize)
     403  				out = size;
     404  			memset(outbuffer, 0x00, out);
     405  		} else {
     406  			if (opt_verbose > 1)
     407  				printf(_("  uncompressing block at %lu to %lu (%lu)\n"),
     408  				       curr, next, next - curr);
     409  			out = uncompress_block(romfs_read(curr), next - curr);
     410  		}
     411  		if (size >= blksize) {
     412  			if (out != blksize)
     413  				errx(FSCK_EX_UNCORRECTED,
     414  				     _("non-block (%ld) bytes"), out);
     415  		} else {
     416  			if (out != size)
     417  				errx(FSCK_EX_UNCORRECTED,
     418  				     _("non-size (%ld vs %ld) bytes"), out,
     419  				     size);
     420  		}
     421  		size -= out;
     422  		if (*extract_dir != '\0' && write(outfd, outbuffer, out) < 0)
     423  			err(FSCK_EX_ERROR, _("write failed: %s"), path);
     424  		curr = next;
     425  	} while (size);
     426  }
     427  static void change_file_status(char *path, struct cramfs_inode *i)
     428  {
     429  	const struct timeval epoch[] = { {0,0}, {0,0} };
     430  
     431  	if (euid == 0) {
     432  		if (lchown(path, i->uid, i->gid) < 0)
     433  			err(FSCK_EX_ERROR, _("lchown failed: %s"), path);
     434  		if (S_ISLNK(i->mode))
     435  			return;
     436  		if (((S_ISUID | S_ISGID) & i->mode) && chmod(path, i->mode) < 0)
     437  			err(FSCK_EX_ERROR, _("chmod failed: %s"), path);
     438  	}
     439  	if (S_ISLNK(i->mode))
     440  		return;
     441  	if (utimes(path, epoch) < 0)
     442  		err(FSCK_EX_ERROR, _("utimes failed: %s"), path);
     443  }
     444  
     445  static int is_dangerous_filename(char *name, int len)
     446  {
     447  	return (len == 1 && name[0] == '.') ||
     448  	       (len == 2 && name[0] == '.' && name[1] == '.');
     449  }
     450  
     451  static void __attribute__((__noreturn__))
     452  	errx_path(const char *mesg, const char *name, size_t namelen)
     453  {
     454  	char buf[PATH_MAX] = { 0 };
     455  
     456  	namelen = min(namelen, sizeof(buf) - 1);
     457  	memcpy(buf, name, namelen);
     458  
     459  	errx(FSCK_EX_UNCORRECTED, "%s: %s", mesg, buf);
     460  }
     461  
     462  static void do_directory(char *path, struct cramfs_inode *i)
     463  {
     464  	int pathlen = strlen(path);
     465  	int count = i->size;
     466  	unsigned long offset = i->offset << 2;
     467  	char *newpath = xmalloc(pathlen + 256);
     468  
     469  	if (offset == 0 && count != 0)
     470  		errx(FSCK_EX_UNCORRECTED,
     471  		     _("directory inode has zero offset and non-zero size: %s"),
     472  		     path);
     473  
     474  	if (offset != 0 && offset < start_dir)
     475  		start_dir = offset;
     476  
     477  	/* TODO: Do we need to check end_dir for empty case? */
     478  	memcpy(newpath, path, pathlen);
     479  	newpath[pathlen] = '/';
     480  	pathlen++;
     481  	if (opt_verbose)
     482  		print_node('d', i, path);
     483  
     484  	if (*extract_dir != '\0') {
     485  		if (mkdir(path, i->mode) < 0)
     486  			err(FSCK_EX_ERROR, _("mkdir failed: %s"), path);
     487  		change_file_status(path, i);
     488  	}
     489  	while (count > 0) {
     490  		struct cramfs_inode *child = iget(offset);
     491  		char *name;
     492  		int size;
     493  		int newlen = child->namelen << 2;
     494  
     495  		size = sizeof(struct cramfs_inode) + newlen;
     496  		count -= size;
     497  
     498  		offset += sizeof(struct cramfs_inode);
     499  		name = romfs_read(offset);
     500  
     501  		if (memchr(name, '/', newlen) != NULL)
     502  			errx_path(_("illegal filename"), name, newlen);
     503  		if (*extract_dir != '\0' && is_dangerous_filename(name, newlen))
     504  			errx_path(_("dangerous filename"), name, newlen);
     505  		memcpy(newpath + pathlen, name, newlen);
     506  		newpath[pathlen + newlen] = 0;
     507  		if (newlen == 0)
     508  			errx(FSCK_EX_UNCORRECTED, _("filename length is zero"));
     509  		if ((pathlen + newlen) - strlen(newpath) > 3)
     510  			errx(FSCK_EX_UNCORRECTED, _("bad filename length"));
     511  		expand_fs(newpath, child);
     512  
     513  		offset += newlen;
     514  
     515  		if (offset <= start_dir)
     516  			errx(FSCK_EX_UNCORRECTED, _("bad inode offset"));
     517  		if (offset > end_dir)
     518  			end_dir = offset;
     519  		iput(child);	/* free(child) */
     520  	}
     521  	free(newpath);
     522  }
     523  
     524  static void do_file(char *path, struct cramfs_inode *i)
     525  {
     526  	unsigned long offset = i->offset << 2;
     527  	int outfd = 0;
     528  
     529  	if (offset == 0 && i->size != 0)
     530  		errx(FSCK_EX_UNCORRECTED,
     531  		     _("file inode has zero offset and non-zero size"));
     532  	if (i->size == 0 && offset != 0)
     533  		errx(FSCK_EX_UNCORRECTED,
     534  		     _("file inode has zero size and non-zero offset"));
     535  	if (offset != 0 && offset < start_data)
     536  		start_data = offset;
     537  	if (opt_verbose)
     538  		print_node('f', i, path);
     539  	if (*extract_dir != '\0') {
     540  		outfd = open(path, O_WRONLY | O_CREAT | O_TRUNC, i->mode);
     541  		if (outfd < 0)
     542  			err(FSCK_EX_ERROR, _("cannot open %s"), path);
     543  	}
     544  	if (i->size)
     545  		do_uncompress(path, outfd, offset, i->size);
     546  	if ( *extract_dir != '\0') {
     547  		if (close_fd(outfd) != 0)
     548  			err(FSCK_EX_ERROR, _("write failed: %s"), path);
     549  		change_file_status(path, i);
     550  	}
     551  }
     552  
     553  static void do_symlink(char *path, struct cramfs_inode *i)
     554  {
     555  	unsigned long offset = i->offset << 2;
     556  	unsigned long curr = offset + 4;
     557  	unsigned long next =
     558  	    u32_toggle_endianness(cramfs_is_big_endian,
     559  				  *(uint32_t *) romfs_read(offset));
     560  	unsigned long size;
     561  
     562  	if (offset == 0)
     563  		errx(FSCK_EX_UNCORRECTED, _("symbolic link has zero offset"));
     564  	if (i->size == 0)
     565  		errx(FSCK_EX_UNCORRECTED, _("symbolic link has zero size"));
     566  
     567  	if (offset < start_data)
     568  		start_data = offset;
     569  	if (next > end_data)
     570  		end_data = next;
     571  
     572  	size = uncompress_block(romfs_read(curr), next - curr);
     573  	if (size != i->size)
     574  		errx(FSCK_EX_UNCORRECTED, _("size error in symlink: %s"), path);
     575  	outbuffer[size] = 0;
     576  	if (opt_verbose) {
     577  		char *str;
     578  
     579  		xasprintf(&str, "%s -> %s", path, outbuffer);
     580  		print_node('l', i, str);
     581  		if (opt_verbose > 1)
     582  			printf(_("  uncompressing block at %lu to %lu (%lu)\n"),
     583  			       curr, next, next - curr);
     584  		free(str);
     585  	}
     586  	if (*extract_dir != '\0') {
     587  		if (symlink(outbuffer, path) < 0)
     588  			err(FSCK_EX_ERROR, _("symlink failed: %s"), path);
     589  		change_file_status(path, i);
     590  	}
     591  }
     592  
     593  static void do_special_inode(char *path, struct cramfs_inode *i)
     594  {
     595  	dev_t devtype = 0;
     596  	char type;
     597  
     598  	if (i->offset)
     599  		/* no need to shift offset */
     600  		errx(FSCK_EX_UNCORRECTED,
     601  		     _("special file has non-zero offset: %s"), path);
     602  
     603  	if (S_ISCHR(i->mode)) {
     604  		devtype = i->size;
     605  		type = 'c';
     606  	} else if (S_ISBLK(i->mode)) {
     607  		devtype = i->size;
     608  		type = 'b';
     609  	} else if (S_ISFIFO(i->mode)) {
     610  		if (i->size != 0)
     611  			errx(FSCK_EX_UNCORRECTED, _("fifo has non-zero size: %s"),
     612  			     path);
     613  		type = 'p';
     614  	} else if (S_ISSOCK(i->mode)) {
     615  		if (i->size != 0)
     616  			errx(FSCK_EX_UNCORRECTED,
     617  			     _("socket has non-zero size: %s"), path);
     618  		type = 's';
     619  	} else {
     620  		errx(FSCK_EX_UNCORRECTED, _("bogus mode: %s (%o)"), path, i->mode);
     621  		return;		/* not reached */
     622  	}
     623  
     624  	if (opt_verbose)
     625  		print_node(type, i, path);
     626  
     627  	if (*extract_dir != '\0') {
     628  		if (mknod(path, i->mode, devtype) < 0)
     629  			err(FSCK_EX_ERROR, _("mknod failed: %s"), path);
     630  		change_file_status(path, i);
     631  	}
     632  }
     633  
     634  static void expand_fs(char *path, struct cramfs_inode *inode)
     635  {
     636  	if (S_ISDIR(inode->mode))
     637  		do_directory(path, inode);
     638  	else if (S_ISREG(inode->mode))
     639  		do_file(path, inode);
     640  	else if (S_ISLNK(inode->mode))
     641  		do_symlink(path, inode);
     642  	else
     643  		do_special_inode(path, inode);
     644  }
     645  
     646  static void test_fs(int start)
     647  {
     648  	struct cramfs_inode *root;
     649  
     650  	root = read_super();
     651  	umask(0);
     652  	euid = geteuid();
     653  	stream.next_in = NULL;
     654  	stream.avail_in = 0;
     655  	inflateInit(&stream);
     656  	expand_fs(extract_dir, root);
     657  	inflateEnd(&stream);
     658  	if (start_data != ~0UL) {
     659  		if (start_data < (sizeof(struct cramfs_super) + start))
     660  			errx(FSCK_EX_UNCORRECTED,
     661  			     _("directory data start (%lu) < sizeof(struct cramfs_super) + start (%zu)"),
     662  			     start_data, sizeof(struct cramfs_super) + start);
     663  		if (end_dir != start_data)
     664  			errx(FSCK_EX_UNCORRECTED,
     665  			     _("directory data end (%lu) != file data start (%lu)"),
     666  			     end_dir, start_data);
     667  	}
     668  	if (super.flags & CRAMFS_FLAG_FSID_VERSION_2 && end_data > super.size)
     669  		errx(FSCK_EX_UNCORRECTED, _("invalid file data offset"));
     670  
     671  	iput(root);		/* free(root) */
     672  }
     673  
     674  int main(int argc, char **argv)
     675  {
     676  	int c;			/* for getopt */
     677  	int start = 0;
     678  
     679  	static const struct option longopts[] = {
     680  		{"verbose",   no_argument,       NULL, 'v'},
     681  		{"version",   no_argument,       NULL, 'V'},
     682  		{"help",      no_argument,       NULL, 'h'},
     683  		{"blocksize", required_argument, NULL, 'b'},
     684  		{"extract",   optional_argument, NULL, 'x'},
     685  		{NULL, 0, NULL, 0},
     686  	};
     687  
     688  	setlocale(LC_MESSAGES, "");
     689  	setlocale(LC_CTYPE, "");
     690  	bindtextdomain(PACKAGE, LOCALEDIR);
     691  	textdomain(PACKAGE);
     692  	close_stdout_atexit();
     693  
     694  	strutils_set_exitcode(FSCK_EX_USAGE);
     695  
     696  	/* command line options */
     697  	while ((c = getopt_long(argc, argv, "ayvVhb:", longopts, NULL)) != EOF)
     698  		switch (c) {
     699  		case 'a':		/* ignore */
     700  		case 'y':
     701  			break;
     702  		case 'h':
     703  			usage();
     704  			break;
     705  		case 'V':
     706  			print_version(FSCK_EX_OK);
     707  		case 'x':
     708  			opt_extract = 1;
     709  			if(optarg)
     710  				extract_dir = optarg;
     711  			break;
     712  		case 'v':
     713  			opt_verbose++;
     714  			break;
     715  		case 'b':
     716  			blksize = strtou32_or_err(optarg, _("invalid blocksize argument"));
     717  			break;
     718  		default:
     719  			errtryhelp(FSCK_EX_USAGE);
     720  		}
     721  
     722  	if ((argc - optind) != 1){
     723  		warnx(_("bad usage"));
     724  		errtryhelp(FSCK_EX_USAGE);
     725  	}
     726  	filename = argv[optind];
     727  
     728  	test_super(&start);
     729  	test_crc(start);
     730  
     731  	if (opt_extract) {
     732  		size_t bufsize = 0;
     733  
     734  		if (blksize == 0)
     735  			blksize = getpagesize();
     736  
     737  		/* re-calculate according to blksize */
     738  		bufsize = rombufsize = blksize * 2;
     739  		rombufbits = 0;
     740  		while (bufsize >>= 1)
     741  			rombufbits++;
     742  		rombufmask = rombufsize - 1;
     743  
     744  		outbuffer = xmalloc(blksize * 2);
     745  		read_buffer = xmalloc(rombufsize * 2);
     746  		test_fs(start);
     747  	}
     748  
     749  	if (opt_verbose)
     750  		printf(_("%s: OK\n"), filename);
     751  
     752  	exit(FSCK_EX_OK);
     753  }