]> git.ipfire.org Git - thirdparty/util-linux.git/blobdiff - disk-utils/fsck.cramfs.c
agetty: fix output of escaped characters
[thirdparty/util-linux.git] / disk-utils / fsck.cramfs.c
index 7ec785000e0e4447ed6e79f0c1ad1ba36d6faf63..0d9bdadf779f25846888b34337d49ac83c6977ee 100644 (file)
@@ -33,9 +33,6 @@
  *                            use read if mmap fails, standardize messages)
  */
 
-/* compile-time options */
-//#define INCLUDE_FS_TESTS     /* include cramfs checking and extraction */
-
 #include <stdio.h>
 #include <stdarg.h>
 #include <stdint.h>
 #include <getopt.h>
 #include <utime.h>
 #include <fcntl.h>
+
+/* We don't use our include/crc32.h, but crc32 from zlib!
+ *
+ * The zlib implementation performs pre/post-conditioning. The util-linux
+ * imlemenation requires post-conditioning (xor) in the applications.
+ */
 #include <zlib.h>
 
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <sys/mman.h>
-#include <sys/sysmacros.h>     /* for major, minor */
 
+#include "c.h"
 #include "cramfs.h"
 #include "nls.h"
 #include "blkdev.h"
-#include "c.h"
 #include "exitcodes.h"
+#include "strutils.h"
 #include "closestream.h"
 
 #define XALLOC_EXIT_CODE FSCK_EX_ERROR
 
 static int fd;                 /* ROM image file descriptor */
 static char *filename;         /* ROM image filename */
-struct cramfs_super super;     /* just find the cramfs superblock once */
+static struct cramfs_super super;      /* just find the cramfs superblock once */
 static int cramfs_is_big_endian = 0;   /* source is big endian */
 static int opt_verbose = 0;    /* 1 = verbose (-v), 2+ = very verbose (-vv) */
-
-char *extract_dir = "";                /* extraction directory (-x) */
+static int opt_extract = 0;    /* extract cramfs (-x) */
+static char *extract_dir = "";         /* optional extraction directory (-x) */
 
 #define PAD_SIZE 512
 
-#ifdef INCLUDE_FS_TESTS
-
-static int opt_extract = 0;    /* extract cramfs (-x) */
-
 static uid_t euid;             /* effective UID */
 
 /* (cramfs_super + start) <= start_dir < end_dir <= start_data <= end_data */
@@ -98,30 +97,33 @@ static z_stream stream;
 
 /* Prototypes */
 static void expand_fs(char *, struct cramfs_inode *);
-#endif /* INCLUDE_FS_TESTS */
 
 static char *outbuffer;
 
-static size_t page_size;
+static size_t blksize = 0;
 
-/* Input status of 0 to print help and exit without an error. */
-static void __attribute__((__noreturn__)) usage(int status)
+static void __attribute__((__noreturn__)) usage(void)
 {
-       FILE *stream = status ? stderr : stdout;
-
-       fputs(USAGE_HEADER, stream);
-       fprintf(stream,
-               _(" %s [options] file\n"), program_invocation_short_name);
-       fputs(USAGE_OPTIONS, stream);
-       fputs(_(" -a                       for compatibility only, ignored\n"), stream);
-       fputs(_(" -v, --verbose            be more verbose\n"), stream);
-       fputs(_(" -x, --destination <dir>  extract into directory\n"), stream);
-       fputs(_(" -y                       for compatibility only, ignored\n"), stream);
-       fputs(USAGE_SEPARATOR, stream);
-       fputs(USAGE_HELP, stream);
-       fputs(USAGE_VERSION, stream);
-       fputs(USAGE_SEPARATOR, stream);
-       exit(status);
+       FILE *out = stdout;
+
+       fputs(USAGE_HEADER, out);
+       fprintf(out,
+               _(" %s [options] <file>\n"), program_invocation_short_name);
+
+       fputs(USAGE_SEPARATOR, out);
+       fputs(_("Check and repair a compressed ROM filesystem.\n"), out);
+
+       fputs(USAGE_OPTIONS, out);
+       fputs(_(" -a                       for compatibility only, ignored\n"), out);
+       fputs(_(" -v, --verbose            be more verbose\n"), out);
+       fputs(_(" -y                       for compatibility only, ignored\n"), out);
+       fputs(_(" -b, --blocksize <size>   use this blocksize, defaults to page size\n"), out);
+       fputs(_("     --extract[=<dir>]    test uncompression, optionally extract into <dir>\n"), out);
+       fputs(USAGE_SEPARATOR, out);
+       printf(USAGE_HELP_OPTIONS(26));
+
+       printf(USAGE_MAN_TAIL("fsck.cramfs(8)"));
+       exit(FSCK_EX_OK);
 }
 
 static int get_superblock_endianness(uint32_t magic)
@@ -143,7 +145,7 @@ static void test_super(int *start, size_t * length)
 
        /* find the physical size of the file or block device */
        if (stat(filename, &st) < 0)
-               err(FSCK_EX_ERROR, _("stat failed %s"), filename);
+               err(FSCK_EX_ERROR, _("stat of %s failed"), filename);
 
        fd = open(filename, O_RDONLY);
        if (fd < 0)
@@ -166,13 +168,14 @@ static void test_super(int *start, size_t * length)
 
        /* find superblock */
        if (read(fd, &super, sizeof(super)) != sizeof(super))
-               err(FSCK_EX_ERROR, _("read failed: %s"), filename);
+               err(FSCK_EX_ERROR, _("cannot read %s"), filename);
        if (get_superblock_endianness(super.magic) != -1)
                *start = 0;
        else if (*length >= (PAD_SIZE + sizeof(super))) {
-               lseek(fd, PAD_SIZE, SEEK_SET);
+               if (lseek(fd, PAD_SIZE, SEEK_SET) == (off_t) -1)
+                       err(FSCK_EX_ERROR, _("seek on %s failed"), filename);
                if (read(fd, &super, sizeof(super)) != sizeof(super))
-                       err(FSCK_EX_ERROR, _("read failed: %s"), filename);
+                       err(FSCK_EX_ERROR, _("cannot read %s"), filename);
                if (get_superblock_endianness(super.magic) != -1)
                        *start = PAD_SIZE;
                else
@@ -188,7 +191,8 @@ static void test_super(int *start, size_t * length)
        if (super.flags & ~CRAMFS_SUPPORTED_FLAGS)
                errx(FSCK_EX_ERROR, _("unsupported filesystem features"));
 
-       if (super.size < page_size)
+       /* What are valid superblock sizes? */
+       if (super.size < *start + sizeof(struct cramfs_super))
                errx(FSCK_EX_UNCORRECTED, _("superblock size (%d) too small"),
                     super.size);
 
@@ -198,10 +202,9 @@ static void test_super(int *start, size_t * length)
                if (*length < super.size)
                        errx(FSCK_EX_UNCORRECTED, _("file length too short"));
                else if (*length > super.size)
-                       fprintf(stderr,
-                               _("warning: file extends past end of filesystem\n"));
+                       warnx(_("file extends past end of filesystem"));
        } else
-               fprintf(stderr, _("warning: old cramfs format\n"));
+               warnx(_("old cramfs format"));
 }
 
 static void test_crc(int start)
@@ -210,14 +213,11 @@ static void test_crc(int start)
        uint32_t crc;
 
        if (!(super.flags & CRAMFS_FLAG_FSID_VERSION_2)) {
-#ifdef INCLUDE_FS_TESTS
+               warnx(_("unable to test CRC: old cramfs format"));
                return;
-#else
-               errx(FSCK_EX_USAGE, _("unable to test CRC: old cramfs format"));
-#endif
        }
 
-       crc = crc32(0L, Z_NULL, 0);
+       crc = crc32(0L, NULL, 0);
 
        buf =
            mmap(NULL, super.size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
@@ -226,14 +226,20 @@ static void test_crc(int start)
                    mmap(NULL, super.size, PROT_READ | PROT_WRITE,
                         MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
                if (buf != MAP_FAILED) {
-                       lseek(fd, 0, SEEK_SET);
-                       if (read(fd, buf, super.size) < 0)
-                               err(FSCK_EX_ERROR, _("read failed: %s"), filename);
+                       ssize_t tmp;
+                       if (lseek(fd, 0, SEEK_SET) == (off_t) -1)
+                               err(FSCK_EX_ERROR, _("seek on %s failed"), filename);
+                       tmp = read(fd, buf, super.size);
+                       if (tmp < 0)
+                               err(FSCK_EX_ERROR, _("cannot read %s"), filename);
+                       if (tmp != (ssize_t) super.size)
+                               errx(FSCK_EX_ERROR, _("failed to read %"PRIu32" bytes from file %s"),
+                                       super.size, filename);
                }
        }
        if (buf != MAP_FAILED) {
                ((struct cramfs_super *)((unsigned char *) buf + start))->fsid.crc =
-                   crc32(0L, Z_NULL, 0);
+                   crc32(0L, NULL, 0);
                crc = crc32(crc, (unsigned char *) buf + start, super.size - start);
                munmap(buf, super.size);
        } else {
@@ -241,16 +247,17 @@ static void test_crc(int start)
                size_t length = 0;
 
                buf = xmalloc(4096);
-               lseek(fd, start, SEEK_SET);
+               if (lseek(fd, start, SEEK_SET) == (off_t) -1)
+                       err(FSCK_EX_ERROR, _("seek on %s failed"), filename);
                for (;;) {
                        retval = read(fd, buf, 4096);
                        if (retval < 0)
-                               err(FSCK_EX_ERROR, _("read failed: %s"), filename);
+                               err(FSCK_EX_ERROR, _("cannot read %s"), filename);
                        else if (retval == 0)
                                break;
                        if (length == 0)
                                ((struct cramfs_super *)buf)->fsid.crc =
-                                   crc32(0L, Z_NULL, 0);
+                                   crc32(0L, NULL, 0);
                        length += retval;
                        if (length > (super.size - start)) {
                                crc = crc32(crc, buf,
@@ -267,7 +274,6 @@ static void test_crc(int start)
                errx(FSCK_EX_UNCORRECTED, _("crc error"));
 }
 
-#ifdef INCLUDE_FS_TESTS
 static void print_node(char type, struct cramfs_inode *i, char *name)
 {
        char info[10];
@@ -291,9 +297,15 @@ static void *romfs_read(unsigned long offset)
 {
        unsigned int block = offset >> ROMBUFFER_BITS;
        if (block != read_buffer_block) {
+               ssize_t x;
+
                read_buffer_block = block;
-               lseek(fd, block << ROMBUFFER_BITS, SEEK_SET);
-               read(fd, read_buffer, ROMBUFFERSIZE * 2);
+               if (lseek(fd, block << ROMBUFFER_BITS, SEEK_SET) == (off_t) -1)
+                       warn(_("seek failed"));
+
+               x = read(fd, read_buffer, ROMBUFFERSIZE * 2);
+               if (x < 0)
+                       warn(_("read romfs failed"));
        }
        return read_buffer + (offset & ROMBUFFERMASK);
 }
@@ -334,7 +346,7 @@ static struct cramfs_inode *read_super(void)
        return root;
 }
 
-static int uncompress_block(void *src, int len)
+static int uncompress_block(void *src, size_t len)
 {
        int err;
 
@@ -342,17 +354,17 @@ static int uncompress_block(void *src, int len)
        stream.avail_in = len;
 
        stream.next_out = (unsigned char *)outbuffer;
-       stream.avail_out = page_size * 2;
+       stream.avail_out = blksize * 2;
 
        inflateReset(&stream);
 
-       if (len > page_size * 2)
+       if (len > blksize * 2)
                errx(FSCK_EX_UNCORRECTED, _("data block too large"));
 
        err = inflate(&stream, Z_FINISH);
        if (err != Z_STREAM_END)
-               errx(FSCK_EX_UNCORRECTED, _("decompression error %p(%d): %s"),
-                    zError(err), src, len);
+               errx(FSCK_EX_UNCORRECTED, _("decompression error: %s"),
+                    zError(err));
        return stream.total_out;
 }
 
@@ -360,13 +372,13 @@ static int uncompress_block(void *src, int len)
 #define lchown chown
 #endif
 
-static void do_uncompress(char *path, int fd, unsigned long offset,
+static void do_uncompress(char *path, int outfd, unsigned long offset,
                          unsigned long size)
 {
-       unsigned long curr = offset + 4 * ((size + page_size - 1) / page_size);
+       unsigned long curr = offset + 4 * ((size + blksize - 1) / blksize);
 
        do {
-               unsigned long out = page_size;
+               unsigned long out = blksize;
                unsigned long next = u32_toggle_endianness(cramfs_is_big_endian,
                                                           *(uint32_t *)
                                                           romfs_read(offset));
@@ -377,19 +389,19 @@ static void do_uncompress(char *path, int fd, unsigned long offset,
                offset += 4;
                if (curr == next) {
                        if (opt_verbose > 1)
-                               printf(_("  hole at %ld (%zd)\n"), curr,
-                                      page_size);
-                       if (size < page_size)
+                               printf(_("  hole at %lu (%zu)\n"), curr,
+                                      blksize);
+                       if (size < blksize)
                                out = size;
                        memset(outbuffer, 0x00, out);
                } else {
                        if (opt_verbose > 1)
-                               printf(_("  uncompressing block at %ld to %ld (%ld)\n"),
+                               printf(_("  uncompressing block at %lu to %lu (%lu)\n"),
                                       curr, next, next - curr);
                        out = uncompress_block(romfs_read(curr), next - curr);
                }
-               if (size >= page_size) {
-                       if (out != page_size)
+               if (size >= blksize) {
+                       if (out != blksize)
                                errx(FSCK_EX_UNCORRECTED,
                                     _("non-block (%ld) bytes"), out);
                } else {
@@ -399,10 +411,8 @@ static void do_uncompress(char *path, int fd, unsigned long offset,
                                     size);
                }
                size -= out;
-               if (opt_extract)
-                       if (write(fd, outbuffer, out) < 0)
-                               err(FSCK_EX_ERROR, _("write failed: %s"),
-                                   path);
+               if (*extract_dir != '\0' && write(outfd, outbuffer, out) < 0)
+                       err(FSCK_EX_ERROR, _("write failed: %s"), path);
                curr = next;
        } while (size);
 }
@@ -447,7 +457,7 @@ static void do_directory(char *path, struct cramfs_inode *i)
        if (opt_verbose)
                print_node('d', i, path);
 
-       if (opt_extract) {
+       if (*extract_dir != '\0') {
                if (mkdir(path, i->mode) < 0)
                        err(FSCK_EX_ERROR, _("mkdir failed: %s"), path);
                change_file_status(path, i);
@@ -484,7 +494,7 @@ static void do_directory(char *path, struct cramfs_inode *i)
 static void do_file(char *path, struct cramfs_inode *i)
 {
        unsigned long offset = i->offset << 2;
-       int fd = 0;
+       int outfd = 0;
 
        if (offset == 0 && i->size != 0)
                errx(FSCK_EX_UNCORRECTED,
@@ -496,15 +506,16 @@ static void do_file(char *path, struct cramfs_inode *i)
                start_data = offset;
        if (opt_verbose)
                print_node('f', i, path);
-       if (opt_extract) {
-               fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, i->mode);
-               if (fd < 0)
+       if (*extract_dir != '\0') {
+               outfd = open(path, O_WRONLY | O_CREAT | O_TRUNC, i->mode);
+               if (outfd < 0)
                        err(FSCK_EX_ERROR, _("cannot open %s"), path);
        }
        if (i->size)
-               do_uncompress(path, fd, offset, i->size);
-       if (opt_extract) {
-               close(fd);
+               do_uncompress(path, outfd, offset, i->size);
+       if ( *extract_dir != '\0') {
+               if (close_fd(outfd) != 0)
+                       err(FSCK_EX_ERROR, _("write failed: %s"), path);
                change_file_status(path, i);
        }
 }
@@ -538,11 +549,11 @@ static void do_symlink(char *path, struct cramfs_inode *i)
                xasprintf(&str, "%s -> %s", path, outbuffer);
                print_node('l', i, str);
                if (opt_verbose > 1)
-                       printf(_("  uncompressing block at %ld to %ld (%ld)\n"),
+                       printf(_("  uncompressing block at %lu to %lu (%lu)\n"),
                               curr, next, next - curr);
                free(str);
        }
-       if (opt_extract) {
+       if (*extract_dir != '\0') {
                if (symlink(outbuffer, path) < 0)
                        err(FSCK_EX_ERROR, _("symlink failed: %s"), path);
                change_file_status(path, i);
@@ -583,7 +594,7 @@ static void do_special_inode(char *path, struct cramfs_inode *i)
        if (opt_verbose)
                print_node(type, i, path);
 
-       if (opt_extract) {
+       if (*extract_dir != '\0') {
                if (mknod(path, i->mode, devtype) < 0)
                        err(FSCK_EX_ERROR, _("mknod failed: %s"), path);
                change_file_status(path, i);
@@ -617,20 +628,18 @@ static void test_fs(int start)
        if (start_data != ~0UL) {
                if (start_data < (sizeof(struct cramfs_super) + start))
                        errx(FSCK_EX_UNCORRECTED,
-                            _("directory data start (%ld) < sizeof(struct cramfs_super) + start (%ld)"),
+                            _("directory data start (%lu) < sizeof(struct cramfs_super) + start (%zu)"),
                             start_data, sizeof(struct cramfs_super) + start);
                if (end_dir != start_data)
                        errx(FSCK_EX_UNCORRECTED,
-                            _("directory data end (%ld) != file data start (%ld)"),
+                            _("directory data end (%lu) != file data start (%lu)"),
                             end_dir, start_data);
        }
-       if (super.flags & CRAMFS_FLAG_FSID_VERSION_2)
-               if (end_data > super.size)
-                       errx(FSCK_EX_UNCORRECTED, _("invalid file data offset"));
+       if (super.flags & CRAMFS_FLAG_FSID_VERSION_2 && end_data > super.size)
+               errx(FSCK_EX_UNCORRECTED, _("invalid file data offset"));
 
        iput(root);             /* free(root) */
 }
-#endif /* INCLUDE_FS_TESTS */
 
 int main(int argc, char **argv)
 {
@@ -639,58 +648,64 @@ int main(int argc, char **argv)
        size_t length = 0;
 
        static const struct option longopts[] = {
-               {"destination", required_argument, 0, 'x'},
-               {"verbose", no_argument, 0, 'v'},
-               {"version", no_argument, 0, 'V'},
-               {"help", no_argument, 0, 'h'},
-               {NULL, no_argument, 0, '0'},
+               {"verbose",   no_argument,       NULL, 'v'},
+               {"version",   no_argument,       NULL, 'V'},
+               {"help",      no_argument,       NULL, 'h'},
+               {"blocksize", required_argument, NULL, 'b'},
+               {"extract",   optional_argument, NULL, 'x'},
+               {NULL, 0, NULL, 0},
        };
 
        setlocale(LC_MESSAGES, "");
+       setlocale(LC_CTYPE, "");
        bindtextdomain(PACKAGE, LOCALEDIR);
        textdomain(PACKAGE);
        atexit(close_stdout);
 
-       page_size = getpagesize();
-
-       outbuffer = xmalloc(page_size * 2);
+       strutils_set_exitcode(FSCK_EX_USAGE);
 
        /* command line options */
-       while ((c = getopt_long(argc, argv, "ayx:vVh", longopts, NULL)) != EOF)
+       while ((c = getopt_long(argc, argv, "ayvVhb:", longopts, NULL)) != EOF)
                switch (c) {
                case 'a':               /* ignore */
                case 'y':
                        break;
                case 'h':
-                       usage(FSCK_EX_OK);
+                       usage();
                        break;
                case 'V':
                        printf(UTIL_LINUX_VERSION);
-                       return EXIT_SUCCESS;
+                       return FSCK_EX_OK;
                case 'x':
-#ifdef INCLUDE_FS_TESTS
                        opt_extract = 1;
-                       extract_dir = optarg;
+                       if(optarg)
+                               extract_dir = optarg;
                        break;
-#else
-                       errx(FSCK_EX_USAGE, _("compiled without -x support"));
-#endif
                case 'v':
                        opt_verbose++;
                        break;
+               case 'b':
+                       blksize = strtou32_or_err(optarg, _("invalid blocksize argument"));
+                       break;
                default:
-                       usage(FSCK_EX_USAGE);
+                       errtryhelp(FSCK_EX_USAGE);
                }
 
-       if ((argc - optind) != 1)
-               usage(FSCK_EX_USAGE);
+       if ((argc - optind) != 1){
+               warnx(_("bad usage"));
+               errtryhelp(FSCK_EX_USAGE);
+       }
        filename = argv[optind];
 
        test_super(&start, &length);
        test_crc(start);
-#ifdef INCLUDE_FS_TESTS
-       test_fs(start);
-#endif
+
+       if(opt_extract) {
+               if (blksize == 0)
+                       blksize = getpagesize();
+               outbuffer = xmalloc(blksize * 2);
+               test_fs(start);
+       }
 
        if (opt_verbose)
                printf(_("%s: OK\n"), filename);