aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/io.c48
-rw-r--r--src/opts.c3
-rw-r--r--src/unalf.133
-rw-r--r--src/unalf.h1
-rw-r--r--src/unalf.rst29
-rw-r--r--src/usage.c1
6 files changed, 94 insertions, 21 deletions
diff --git a/src/io.c b/src/io.c
index 53bc0dd..9a0486d 100644
--- a/src/io.c
+++ b/src/io.c
@@ -28,6 +28,7 @@ static void eof_junk(long pos) {
static void check_hdr_size(const char *name, unsigned long size) {
const char *desc;
+ int fatal = 0;
/* fits on a double-density disk? */
if(size < 184320)
@@ -35,22 +36,51 @@ static void check_hdr_size(const char *name, unsigned long size) {
/* >= 16MB files are impossible because the decrunch algorithm
ignores the high byte of the 4-byte length. */
- if(size >= 16777216L)
+ if(size >= 16777216L) {
desc = "impossibly large (>=16MB)";
+ fatal = !opts.listonly; /* listing the file isn't an error, extracting it is. */
/* >1MB files are possible (e.g. with a hard drive on SpartaDOS X)
but exceedingly rare in the Atari world. */
- else if(size > 1048576L)
+ } else if(size > 1048576L) {
desc = "suspiciously large (>1MB)";
- else
+ } else {
desc = "too large for a floppy disk (>180KB)";
+ }
- fprintf(stderr, "%s: header #%d %s size is %s.\n",
- self, headers_read, name, desc);
+ fprintf(stderr, "%s: %s: header #%d %s size is %s.\n",
+ self, (fatal ? "fatal" : "warning"), headers_read, name, desc);
+ if(fatal) exit(1);
}
-static void sanity_check_header(void) {
- check_hdr_size("original", getquad(alf_hdr_origsize0));
- check_hdr_size("compressed", getquad(alf_hdr_compsize0));
+static void sanity_check_header(long pos) {
+ struct stat s;
+ unsigned long origsize, compsize;
+ int fatal;
+
+ origsize = getquad(alf_hdr_origsize0);
+ compsize = getquad(alf_hdr_compsize0);
+
+ check_hdr_size("original", origsize);
+ check_hdr_size("compressed", compsize);
+
+ pos += 29; /* skip header */
+ if(fstat(fileno(in_file), &s) < 0) {
+ fprintf(stderr, "%s: fatal: fstat on %s ", self, in_filename);
+ perror("failed");
+ return;
+ }
+
+ if(compsize > (s.st_size - pos)) {
+ fatal = !(opts.force || opts.listonly);
+ fprintf(stderr, "%s: %s: compressed size for header #%d is bigger than the rest of the file (truncated?), use -F to override.\n", fatal ? "fatal" : "warning", self, headers_read);
+ exit(1);
+ }
+
+ if(compsize > origsize * 2) {
+ fatal = !(opts.force || opts.listonly);
+ fprintf(stderr, "%s: %s: compressed size for header #%d is over twice the uncompressed size (corrupt?), use -F to override.\n", fatal ? "fatal": "warning", self, headers_read);
+ exit(1);
+ }
}
/* return 1 if a header is read, 0 if not */
@@ -84,7 +114,7 @@ int read_alf_header(void) {
if(h2 < 0x0f) die_arc();
if(h2 == 0x0f) {
headers_read++;
- sanity_check_header();
+ sanity_check_header(read_pos);
return 1; /* signature matches */
}
}
diff --git a/src/opts.c b/src/opts.c
index aca6e83..977a10d 100644
--- a/src/opts.c
+++ b/src/opts.c
@@ -1,6 +1,6 @@
#include "unalf.h"
-#define OPTIONS "aefklLopqtTvVd:x:"
+#define OPTIONS "aefFklLopqtTvVd:x:"
/* uncomment to test exclude/include glob lists */
// #define DEBUG_GLOBS
@@ -38,6 +38,7 @@ void parse_opts(int argc, char * const *argv) {
case 'e': opts.listonly = opts.testonly = 0; break;
case 'k': opts.keepdot++; break;
case 'f': opts.fixjunk++; opts.testonly = 1; opts.listonly = 0; opts.quiet = 1; break;
+ case 'F': opts.force++; break;
case 'l': opts.listonly++; opts.testonly = 0; break;
case 'L': opts.lowercase++; break;
case 'o': opts.overwrite++; break;
diff --git a/src/unalf.1 b/src/unalf.1
index f7d4ff3..2c8054f 100644
--- a/src/unalf.1
+++ b/src/unalf.1
@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
-.TH "UNALF" 1 "2025-12-02" "0.3.0" "Urchlay's Atari 8-bit Tools"
+.TH "UNALF" 1 "2025-12-03" "0.3.0" "Urchlay's Atari 8-bit Tools"
.SH NAME
unalf \- extract Atari 8-bit ALF archives
.\" RST source for unalf(1) man page. Convert with:
@@ -111,6 +111,15 @@ backup is made.
.
.INDENT 0.0
.TP
+.B \-F
+Force \fBunalf\fP to continue after detecting an invalid compressed file
+size in an \fIALF\fP header. This isn\(aqt the default because it can cause
+\fBunalf\fP to get stuck in an infinite loop.
+.UNINDENT
+.\" force uncompressing in case of invalid compressed size.
+.
+.INDENT 0.0
+.TP
.B \-h\fP,\fB \-\-help
Show built\-in help message.
.UNINDENT
@@ -287,6 +296,20 @@ cause these errors.
.TP
.B \fBfatal: compressed data is truncated, EOF before end marker\fP
Self\-explanatory. The ALF file ends before the compressed data does.
+.TP
+.B \fBheader #<n> (compressed|original) size is impossibly large (>16MB)\fP
+\fBunalf\fP can\(aqt handle files of more than 16MB in size. This isn\(aqt a
+bug, it\(aqs just the way it was designed (in 1988, remember). Neither
+\fBLZ.COM\fP nor \fBalf\fP will create files like this, so you \fIprobably\fP
+have a corrupt ALF archive.
+.TP
+.B \fBcompressed size for header #<n> is bigger than the rest of the file (truncated?)\fP
+Normally this means a truncated \fIALF\fP file, or one with a corrupted header.
+You can use the \fB\-F\fP option to turn this error into a non\-fatal warning.
+.TP
+.B \fBcompressed size for header #<n> is over twice the uncompressed size (corrupt?)\fP
+This generally means you have a corrupted \fIALF\fP file.
+You can use the \fB\-F\fP option to turn this error into a non\-fatal warning.
.UNINDENT
.SS Warnings
.sp
@@ -333,11 +356,9 @@ with A\-Z" or "invalid character". The filename will be printed with
any unprintable characters as hex values (e.g. \fB$01\fP).
.TP
.B \fBheader #<n> (compressed|original) size is...\fP
-Followed by "impossibly large", "suspiciously large", or "too large
-to fit on a floppy disk". May indicate a corrupt archive, or someone
-really might have created an ALF file with files this big... though
-"impossibly large" means >=16MB. \fBunalf\fP can\(aqt extract a file
-that big.
+Followed by "suspiciously large" or "too large to fit on a floppy
+disk". May indicate a corrupt archive, or someone really might have
+created an ALF file with files this big.
.TP
.B \fBALF files don\(aqt normally contain other ALF files, are you trying to extract/list multiple ALF files at once?\fP
You gave a \fIwildcard\fP argument that ends with \fI\&.alf\fP\&. This is OK if
diff --git a/src/unalf.h b/src/unalf.h
index 33a7760..4adaeb3 100644
--- a/src/unalf.h
+++ b/src/unalf.h
@@ -80,6 +80,7 @@ typedef struct {
int verbose_list;
int fixjunk;
int ignore_datetime;
+ int force;
} opts_t;
#define MAX_EXCLUDES 256
diff --git a/src/unalf.rst b/src/unalf.rst
index 146e056..069fad1 100644
--- a/src/unalf.rst
+++ b/src/unalf.rst
@@ -89,6 +89,13 @@ OPTIONS
.. fix (remove) junk at EOF.
+-F
+ Force **unalf** to continue after detecting an invalid compressed file
+ size in an *ALF* header. This isn't the default because it can cause
+ **unalf** to get stuck in an infinite loop.
+
+.. force uncompressing in case of invalid compressed size.
+
-h, --help
Show built-in help message.
@@ -242,6 +249,20 @@ Fatal errors
**fatal: compressed data is truncated, EOF before end marker**
Self-explanatory. The ALF file ends before the compressed data does.
+**header #<n> (compressed|original) size is impossibly large (>16MB)**
+ **unalf** can't handle files of more than 16MB in size. This isn't a
+ bug, it's just the way it was designed (in 1988, remember). Neither
+ **LZ.COM** nor **alf** will create files like this, so you *probably*
+ have a corrupt ALF archive.
+
+**compressed size for header #<n> is bigger than the rest of the file (truncated?)**
+ Normally this means a truncated *ALF* file, or one with a corrupted header.
+ You can use the **-F** option to turn this error into a non-fatal warning.
+
+**compressed size for header #<n> is over twice the uncompressed size (corrupt?)**
+ This generally means you have a corrupted *ALF* file.
+ You can use the **-F** option to turn this error into a non-fatal warning.
+
Warnings
--------
@@ -287,11 +308,9 @@ Warnings
any unprintable characters as hex values (e.g. **$01**).
**header #<n> (compressed|original) size is...**
- Followed by "impossibly large", "suspiciously large", or "too large
- to fit on a floppy disk". May indicate a corrupt archive, or someone
- really might have created an ALF file with files this big... though
- "impossibly large" means >=16MB. **unalf** can't extract a file
- that big.
+ Followed by "suspiciously large" or "too large to fit on a floppy
+ disk". May indicate a corrupt archive, or someone really might have
+ created an ALF file with files this big.
**ALF files don't normally contain other ALF files, are you trying to extract/list multiple ALF files at once?**
You gave a *wildcard* argument that ends with *.alf*. This is OK if
diff --git a/src/usage.c b/src/usage.c
index 7615d2e..0f43fca 100644
--- a/src/usage.c
+++ b/src/usage.c
@@ -4,6 +4,7 @@ const char *usage_msg[] = {
" -d: set output directory (created if needed).",
" -e: extract files (redundant; this is the default action).",
" -f: fix (remove) junk at EOF.",
+ " -F: force uncompressing in case of invalid compressed size.",
" -h: show this help message.",
" -k: keep trailing periods (dots) in filenames.",
" -l: list files in archive (filenames only).",