/*
+ * 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>
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;
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);
}
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 */
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))
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"));
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;
*/
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)
return stream.total_out;
}
-#if !HAVE_LCHOWN
+#ifndef HAVE_LCHOWN
#define lchown chown
#endif
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)
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)
}
while (count > 0) {
struct cramfs_inode *child = iget(offset);
+ char *name;
int size;
int newlen = child->namelen << 2;
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"));
{
int c; /* for getopt */
int start = 0;
- size_t length = 0;
static const struct option longopts[] = {
{"verbose", no_argument, NULL, 'v'},
setlocale(LC_CTYPE, "");
bindtextdomain(PACKAGE, LOCALEDIR);
textdomain(PACKAGE);
- atexit(close_stdout);
+ close_stdout_atexit();
strutils_set_exitcode(FSCK_EX_USAGE);
usage();
break;
case 'V':
- printf(UTIL_LINUX_VERSION);
- return FSCK_EX_OK;
+ print_version(FSCK_EX_OK);
case 'x':
opt_extract = 1;
if(optarg)
}
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);
}