]> git.ipfire.org Git - thirdparty/util-linux.git/blobdiff - disk-utils/fsck.cramfs.c
Merge branch 'PR/libmount-exec-errors' of github.com:karelzak/util-linux-work
[thirdparty/util-linux.git] / disk-utils / fsck.cramfs.c
index b2a3cc15366f02d9aef2665c095240bc957b11d2..b5d64c36aa6c401ad04f7e7e6b422121c2d7fac7 100644 (file)
@@ -1,4 +1,6 @@
 /*
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
  * cramfsck - check a cramfs file system
  *
  * Copyright (C) 2000-2002 Transmeta Corporation
 #include <errno.h>
 #include <string.h>
 #include <getopt.h>
-#include <utime.h>
 #include <fcntl.h>
 
 /* We don't use our include/crc32.h, but crc32 from zlib!
  *
- * The zlib implemenation performs pre/post-conditioning. The util-linux
+ * The zlib implementation performs pre/post-conditioning. The util-linux
  * imlemenation requires post-conditioning (xor) in the applications.
  */
 #include <zlib.h>
 
+#include <sys/time.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <sys/mman.h>
@@ -86,11 +88,17 @@ static unsigned long start_data = ~0UL;     /* start of the data (256 MB = max) */
 static unsigned long end_data = 0;     /* end of the data */
 
 
-/* Guarantee access to at least 8kB at a time */
-#define ROMBUFFER_BITS 13
-#define ROMBUFFERSIZE  (1 << ROMBUFFER_BITS)
-#define ROMBUFFERMASK  (ROMBUFFERSIZE - 1)
-static char read_buffer[ROMBUFFERSIZE * 2];
+/* Guarantee access to at least 2 * blksize at a time */
+#define CRAMFS_ROMBUFFER_BITS  13
+#define CRAMFS_ROMBUFFERSIZE   (1 << CRAMFS_ROMBUFFER_BITS)
+#define CRAMFS_ROMBUFFERMASK   (CRAMFS_ROMBUFFERSIZE - 1)
+
+/* Defaults, updated in main() according to block size */
+static size_t rombufbits = CRAMFS_ROMBUFFER_BITS;
+static size_t rombufsize = CRAMFS_ROMBUFFERSIZE;
+static size_t rombufmask = CRAMFS_ROMBUFFERMASK;
+
+static char *read_buffer;
 static unsigned long read_buffer_block = ~0UL;
 
 static z_stream stream;
@@ -120,9 +128,9 @@ static void __attribute__((__noreturn__)) usage(void)
        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));
+       fprintf(out, USAGE_HELP_OPTIONS(26));
 
-       printf(USAGE_MAN_TAIL("fsck.cramfs(8)"));
+       fprintf(out, USAGE_MAN_TAIL("fsck.cramfs(8)"));
        exit(FSCK_EX_OK);
 }
 
@@ -131,39 +139,41 @@ static int get_superblock_endianness(uint32_t magic)
        if (magic == CRAMFS_MAGIC) {
                cramfs_is_big_endian = HOST_IS_BIG_ENDIAN;
                return 0;
-       } else if (magic ==
+       }
+
+       if (magic ==
                   u32_toggle_endianness(!HOST_IS_BIG_ENDIAN, CRAMFS_MAGIC)) {
                cramfs_is_big_endian = !HOST_IS_BIG_ENDIAN;
                return 0;
-       } else
-               return -1;
+       }
+
+       return -1;
 }
 
-static void test_super(int *start, size_t * length)
+static void test_super(int *start)
 {
        struct stat st;
-
-       /* find the physical size of the file or block device */
-       if (stat(filename, &st) < 0)
-               err(FSCK_EX_ERROR, _("stat of %s failed"), filename);
+       unsigned long long length;
 
        fd = open(filename, O_RDONLY);
        if (fd < 0)
                err(FSCK_EX_ERROR, _("cannot open %s"), filename);
 
+       /* find the physical size of the file or block device */
+       if (fstat(fd, &st) < 0)
+               err(FSCK_EX_ERROR, _("stat of %s failed"), filename);
+
        if (S_ISBLK(st.st_mode)) {
-               unsigned long long bytes;
-               if (blkdev_get_size(fd, &bytes))
+               if (blkdev_get_size(fd, &length))
                        err(FSCK_EX_ERROR,
                            _("ioctl failed: unable to determine device size: %s"),
                            filename);
-               *length = bytes;
        } else if (S_ISREG(st.st_mode))
-               *length = st.st_size;
+               length = st.st_size;
        else
                errx(FSCK_EX_ERROR, _("not a block device or file: %s"), filename);
 
-       if (*length < sizeof(struct cramfs_super))
+       if (length < sizeof(struct cramfs_super))
                errx(FSCK_EX_UNCORRECTED, _("file length too short"));
 
        /* find superblock */
@@ -171,7 +181,7 @@ static void test_super(int *start, size_t * length)
                err(FSCK_EX_ERROR, _("cannot read %s"), filename);
        if (get_superblock_endianness(super.magic) != -1)
                *start = 0;
-       else if (*length >= (PAD_SIZE + sizeof(super))) {
+       else if (length >= (PAD_SIZE + sizeof(super))) {
                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))
@@ -192,16 +202,16 @@ static void test_super(int *start, size_t * length)
                errx(FSCK_EX_ERROR, _("unsupported filesystem features"));
 
        /* What are valid superblock sizes? */
-       if (super.size < sizeof(struct cramfs_super))
+       if (super.size < *start + sizeof(struct cramfs_super))
                errx(FSCK_EX_UNCORRECTED, _("superblock size (%d) too small"),
                     super.size);
 
        if (super.flags & CRAMFS_FLAG_FSID_VERSION_2) {
                if (super.fsid.files == 0)
                        errx(FSCK_EX_UNCORRECTED, _("zero file count"));
-               if (*length < super.size)
+               if (length < super.size)
                        errx(FSCK_EX_UNCORRECTED, _("file length too short"));
-               else if (*length > super.size)
+               else if (length > super.size)
                        warnx(_("file extends past end of filesystem"));
        } else
                warnx(_("old cramfs format"));
@@ -220,24 +230,28 @@ static void test_crc(int start)
        crc = crc32(0L, NULL, 0);
 
        buf =
-           mmap(NULL, start + super.size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
+           mmap(NULL, super.size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
        if (buf == MAP_FAILED) {
                buf =
-                   mmap(NULL, start + super.size, PROT_READ | PROT_WRITE,
+                   mmap(NULL, super.size, PROT_READ | PROT_WRITE,
                         MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
                if (buf != MAP_FAILED) {
-                       if (lseek(fd, start, SEEK_SET) == (off_t) -1)
+                       ssize_t tmp;
+                       if (lseek(fd, 0, SEEK_SET) == (off_t) -1)
                                err(FSCK_EX_ERROR, _("seek on %s failed"), filename);
-                       if (read(fd, (unsigned char *) buf + start, super.size) !=
-                           (ssize_t) super.size)
+                       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, NULL, 0);
-               crc = crc32(crc, (unsigned char *) buf + start, super.size);
-               munmap(buf, start + super.size);
+               crc = crc32(crc, (unsigned char *) buf + start, super.size - start);
+               munmap(buf, super.size);
        } else {
                int retval;
                size_t length = 0;
@@ -291,19 +305,19 @@ static void print_node(char type, struct cramfs_inode *i, char *name)
  */
 static void *romfs_read(unsigned long offset)
 {
-       unsigned int block = offset >> ROMBUFFER_BITS;
+       unsigned int block = offset >> rombufbits;
        if (block != read_buffer_block) {
                ssize_t x;
 
                read_buffer_block = block;
-               if (lseek(fd, block << ROMBUFFER_BITS, SEEK_SET) == (off_t) -1)
+               if (lseek(fd, block << rombufbits, SEEK_SET) == (off_t) -1)
                        warn(_("seek failed"));
 
-               x = read(fd, read_buffer, ROMBUFFERSIZE * 2);
+               x = read(fd, read_buffer, rombufsize * 2);
                if (x < 0)
                        warn(_("read romfs failed"));
        }
-       return read_buffer + (offset & ROMBUFFERMASK);
+       return read_buffer + (offset & rombufmask);
 }
 
 static struct cramfs_inode *cramfs_iget(struct cramfs_inode *i)
@@ -364,7 +378,7 @@ static int uncompress_block(void *src, size_t len)
        return stream.total_out;
 }
 
-#if !HAVE_LCHOWN
+#ifndef HAVE_LCHOWN
 #define lchown chown
 #endif
 
@@ -412,10 +426,9 @@ static void do_uncompress(char *path, int outfd, unsigned long offset,
                curr = next;
        } while (size);
 }
-
 static void change_file_status(char *path, struct cramfs_inode *i)
 {
-       struct utimbuf epoch = { 0, 0 };
+       const struct timeval epoch[] = { {0,0}, {0,0} };
 
        if (euid == 0) {
                if (lchown(path, i->uid, i->gid) < 0)
@@ -423,12 +436,29 @@ static void change_file_status(char *path, struct cramfs_inode *i)
                if (S_ISLNK(i->mode))
                        return;
                if (((S_ISUID | S_ISGID) & i->mode) && chmod(path, i->mode) < 0)
-                       err(FSCK_EX_ERROR, _("chown failed: %s"), path);
+                       err(FSCK_EX_ERROR, _("chmod failed: %s"), path);
        }
        if (S_ISLNK(i->mode))
                return;
-       if (utime(path, &epoch) < 0)
-               err(FSCK_EX_ERROR, _("utime failed: %s"), path);
+       if (utimes(path, epoch) < 0)
+               err(FSCK_EX_ERROR, _("utimes failed: %s"), path);
+}
+
+static int is_dangerous_filename(char *name, int len)
+{
+       return (len == 1 && name[0] == '.') ||
+              (len == 2 && name[0] == '.' && name[1] == '.');
+}
+
+static void __attribute__((__noreturn__))
+       errx_path(const char *mesg, const char *name, size_t namelen)
+{
+       char buf[PATH_MAX] = { 0 };
+
+       namelen = min(namelen, sizeof(buf) - 1);
+       memcpy(buf, name, namelen);
+
+       errx(FSCK_EX_UNCORRECTED, "%s: %s", mesg, buf);
 }
 
 static void do_directory(char *path, struct cramfs_inode *i)
@@ -460,6 +490,7 @@ static void do_directory(char *path, struct cramfs_inode *i)
        }
        while (count > 0) {
                struct cramfs_inode *child = iget(offset);
+               char *name;
                int size;
                int newlen = child->namelen << 2;
 
@@ -467,8 +498,13 @@ static void do_directory(char *path, struct cramfs_inode *i)
                count -= size;
 
                offset += sizeof(struct cramfs_inode);
+               name = romfs_read(offset);
 
-               memcpy(newpath + pathlen, romfs_read(offset), newlen);
+               if (memchr(name, '/', newlen) != NULL)
+                       errx_path(_("illegal filename"), name, newlen);
+               if (*extract_dir != '\0' && is_dangerous_filename(name, newlen))
+                       errx_path(_("dangerous filename"), name, newlen);
+               memcpy(newpath + pathlen, name, newlen);
                newpath[pathlen + newlen] = 0;
                if (newlen == 0)
                        errx(FSCK_EX_UNCORRECTED, _("filename length is zero"));
@@ -641,7 +677,6 @@ int main(int argc, char **argv)
 {
        int c;                  /* for getopt */
        int start = 0;
-       size_t length = 0;
 
        static const struct option longopts[] = {
                {"verbose",   no_argument,       NULL, 'v'},
@@ -656,7 +691,7 @@ int main(int argc, char **argv)
        setlocale(LC_CTYPE, "");
        bindtextdomain(PACKAGE, LOCALEDIR);
        textdomain(PACKAGE);
-       atexit(close_stdout);
+       close_stdout_atexit();
 
        strutils_set_exitcode(FSCK_EX_USAGE);
 
@@ -670,8 +705,7 @@ int main(int argc, char **argv)
                        usage();
                        break;
                case 'V':
-                       printf(UTIL_LINUX_VERSION);
-                       return FSCK_EX_OK;
+                       print_version(FSCK_EX_OK);
                case 'x':
                        opt_extract = 1;
                        if(optarg)
@@ -693,13 +727,24 @@ int main(int argc, char **argv)
        }
        filename = argv[optind];
 
-       test_super(&start, &length);
+       test_super(&start);
        test_crc(start);
 
-       if(opt_extract) {
+       if (opt_extract) {
+               size_t bufsize = 0;
+
                if (blksize == 0)
                        blksize = getpagesize();
+
+               /* re-calculate according to blksize */
+               bufsize = rombufsize = blksize * 2;
+               rombufbits = 0;
+               while (bufsize >>= 1)
+                       rombufbits++;
+               rombufmask = rombufsize - 1;
+
                outbuffer = xmalloc(blksize * 2);
+               read_buffer = xmalloc(rombufsize * 2);
                test_fs(start);
        }