From 5af60f755ee707ce7b7f7fe5d2724522e863c890 Mon Sep 17 00:00:00 2001 From: Konstantin Kushnir Date: Sun, 26 May 2024 19:34:05 +0000 Subject: [PATCH] Add the ability to detect truncated archives --- ChangeLog | 1 + generic/pages.c | 231 ++++++++++++++++++++++++++++++++++++++++++- generic/pages.h | 4 + generic/pagesCompr.c | 3 + scripts/pages.tcl | 54 ++++++++++ tests/pages.test | 134 ++++++++++++++++++++++--- tests/vfs.test | 4 +- 7 files changed, 410 insertions(+), 21 deletions(-) diff --git a/ChangeLog b/ChangeLog index db3b341..769a071 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,7 @@ 2024-05-26 Konstantin Kushnir * Add tests for writer * Fix build without c-pages, but with c-fsindex + * Add the ability to detect truncated archives 2024-05-25 Konstantin Kushnir * Add support for writer in C diff --git a/generic/pages.c b/generic/pages.c index d3595cf..e290290 100644 --- a/generic/pages.c +++ b/generic/pages.c @@ -11,6 +11,11 @@ #define COOKFS_SUFFIX_BYTES 16 +// read by 512kb chunks +#define COOKFS_SEARCH_STAMP_CHUNK 524288 +// max read 10mb +#define COOKFS_SEARCH_STAMP_MAX_READ 10485760 + #define COOKFS_PAGES_ERRORMSG "Unable to create Cookfs object" /* declarations of static and/or internal functions */ @@ -19,6 +24,7 @@ static void CookfsPagesPageCacheMoveToTop(Cookfs_Pages *p, int index); static int CookfsReadIndex(Tcl_Interp *interp, Cookfs_Pages *p); static void CookfsPagesPageExtendIfNeeded(Cookfs_Pages *p, int count); static void CookfsTruncateFileIfNeeded(Cookfs_Pages *p, Tcl_WideInt targetOffset); +static Tcl_WideInt Cookfs_PageSearchStamp(Cookfs_Pages *p); static const char *const pagehashNames[] = { "md5", "crc32", NULL }; @@ -232,16 +238,24 @@ Cookfs_Pages *Cookfs_PagesInit(Tcl_Interp *interp, Tcl_Obj *fileName, int fileRe } /* initialize structure */ + rc->isFirstWrite = 0; rc->useFoffset = useFoffset; rc->foffset = foffset; rc->fileReadOnly = fileReadOnly; rc->fileCompression = fileCompression; rc->alwaysCompress = 0; - if (fileSignature != NULL) - memcpy(rc->fileSignature, fileSignature, 7); - else - memcpy(rc->fileSignature, "CFS0002", 7); - + if (fileSignature != NULL) { + memcpy(rc->fileSignature, fileSignature, 7); + } else { + // Split the signature into 2 strings so we don't find that whole string + // when searching for the signature + memcpy(rc->fileSignature, "CFS", 3); + memcpy(rc->fileSignature + 3, "0002", 4); + } + // Split the stamp into 2 strings so we don't find that whole string + // when searching for the stamp + memcpy(rc->fileStamp, "CFS", 3); + memcpy(rc->fileStamp + 3, "S002", 4); /* initialize parameters */ rc->fileLastOp = COOKFS_LASTOP_UNKNOWN; @@ -346,12 +360,24 @@ Cookfs_Pages *Cookfs_PagesInit(Tcl_Interp *interp, Tcl_Obj *fileName, int fileRe /* read index or fail */ if (!CookfsReadIndex(interp, rc)) { if (rc->fileReadOnly) { + // Detecting a corrupted file only if no endoffset is specified and we + // tried to automatically detect the contents of the archive. + if (!useFoffset) { + Tcl_WideInt expectedSize = Cookfs_PageSearchStamp(rc); + if (expectedSize != -1) { + Tcl_SetObjResult(interp, Tcl_ObjPrintf("The archive \"%s\"" + " appears to be corrupted or truncated. Expected" + " archive size is %" TCL_LL_MODIFIER "d bytes or" + " larger.", Tcl_GetString(fileName), expectedSize)); + } + } rc->pagesUptodate = 1; rc->indexChanged = 0; rc->shouldTruncate = 0; Cookfs_PagesFini(rc); return NULL; } else { + rc->isFirstWrite = 1; rc->dataInitialOffset = Tcl_Seek(rc->fileChannel, 0, SEEK_END); rc->dataAllPagesSize = 0; rc->dataNumPages = 0; @@ -416,6 +442,9 @@ Tcl_WideInt Cookfs_PagesClose(Cookfs_Pages *p) { Cookfs_AsyncCompressFinalize(p); Cookfs_AsyncDecompressFinalize(p); + // Add initial stamp if needed + Cookfs_PageAddStamp(p, 0); + /* seek to proper position */ Cookfs_SeekToPage(p, p->dataNumPages); @@ -461,6 +490,9 @@ Tcl_WideInt Cookfs_PagesClose(Cookfs_Pages *p) { CookfsTruncateFileIfNeeded(p, p->foffset); + // Add final stamp if needed + Cookfs_PageAddStamp(p, p->foffset); + } /* close file channel */ @@ -561,6 +593,195 @@ void Cookfs_PagesFini(Cookfs_Pages *p) { Tcl_Free((void *) p); } +/* + *---------------------------------------------------------------------- + * + * Cookfs_PageSearchStamp -- + * + * Trying to find the cookfs stamp that should be located in front of the archive + * + * Results: + * Expected file size if the stamp is found, or -1 otherwise + * + * Side effects: + * Changing the current offset in the channel + * + *---------------------------------------------------------------------- + */ + +static Tcl_WideInt Cookfs_PageSearchStamp(Cookfs_Pages *p) { + + CookfsLog(printf("Cookfs_PageSearchStamp: enter")); + + unsigned char *buf = NULL; + Tcl_WideInt size = -1; + + buf = ckalloc(COOKFS_SEARCH_STAMP_CHUNK); + if (buf == NULL) { + CookfsLog(printf("Cookfs_PageSearchStamp: failed to alloc")); + goto error; + } + + if (Tcl_Seek(p->fileChannel, 0, SEEK_SET) == -1) { + CookfsLog(printf("Cookfs_PageSearchStamp: failed to seek")); + goto error; + } + + Tcl_WideInt read = 0; + int bufSize = 0; + int i; + + while (!Tcl_Eof(p->fileChannel) && (read < COOKFS_SEARCH_STAMP_MAX_READ)) { + + int wantToRead = COOKFS_SEARCH_STAMP_CHUNK - bufSize; + + if ((wantToRead + read) > COOKFS_SEARCH_STAMP_MAX_READ) { + wantToRead = COOKFS_SEARCH_STAMP_MAX_READ - read; + } + + CookfsLog(printf("Cookfs_PageSearchStamp: try to read %d bytes", + wantToRead)); + + int readCount = Tcl_Read(p->fileChannel, (char *)buf + bufSize, + wantToRead); + + if (!readCount) { + CookfsLog(printf("Cookfs_PageSearchStamp: got zero bytes," + " continue")); + continue; + } + + // A negative value of bytes read indicates an error. Stop processing + // in this case. + if (readCount < 0) { + goto error; + } + + CookfsLog(printf("Cookfs_PageSearchStamp: got %d bytes", readCount)); + + read += readCount; + bufSize += readCount; + + // Do not look for the last 20 bytes, as a situation may arise where + // the stamp byte is at the very end of the buffer and the WideInt + // that should come after the stamp is not read. + int bytesToLookup = bufSize - 20; + + for (i = 0; i < bytesToLookup; i++) { + if (buf[i] != p->fileStamp[0]) { + continue; + } + if (memcmp(&buf[i], p->fileStamp, COOKFS_SIGNATURE_LENGTH) != 0) { + continue; + } + goto found; + } + + CookfsLog(printf("Cookfs_PageSearchStamp: stamp is not found yet")); + + // Leave the last 20 bytes in the buffer + if (bufSize > 20) { + memcpy(buf, &buf[bufSize - 20], 20); + bufSize = 20; + } + + } + + CookfsLog(printf("Cookfs_PageSearchStamp: read total %ld bytes and" + " could not find the stamp", read)); + + goto error; + +found: + + + Cookfs_Binary2WideInt(&buf[i + COOKFS_SIGNATURE_LENGTH], &size, 1); + CookfsLog(printf("Cookfs_PageSearchStamp: return the size: %ld", size)); + +error: + + if (buf != NULL) { + ckfree(buf); + } + + return size; + +} + +/* + *---------------------------------------------------------------------- + * + * Cookfs_PageAddStamp -- + * + * Adds a stamp before archive + * + * Results: + * TCL_OK on success or TCL_ERROR on failure + * + * Side effects: + * Sets the current offset in the channel immediately following the stamp + * + *---------------------------------------------------------------------- + */ + +int Cookfs_PageAddStamp(Cookfs_Pages *p, Tcl_WideInt size) { + + CookfsLog(printf("Cookfs_PageAddStamp: enter, size: %ld", size)); + + unsigned char sizeBin[8]; // 64-bit WideInt + Cookfs_WideInt2Binary(&size, sizeBin, 1); + + if (size == 0) { + if (!p->isFirstWrite) { + CookfsLog(printf("Cookfs_PageAddStamp: return:" + " is not the first write")); + return TCL_OK; + } + CookfsLog(printf("Cookfs_PageAddStamp: write initial stamp")); + if (Tcl_Seek(p->fileChannel, 0, SEEK_END) == -1) { + CookfsLog(printf("Cookfs_PageAddStamp: return error," + " failed to seek")); + return TCL_ERROR; + } + if (Tcl_Write(p->fileChannel, p->fileStamp, COOKFS_SIGNATURE_LENGTH) + != COOKFS_SIGNATURE_LENGTH) + { + CookfsLog(printf("Cookfs_PageAddStamp: return error," + " failed to write signature")); + return TCL_ERROR; + } + if (Tcl_Write(p->fileChannel, (const char *)sizeBin, 8) != 8) + { + CookfsLog(printf("Cookfs_PageAddStamp: return error," + " failed to write size")); + return TCL_ERROR; + } + p->dataInitialOffset += COOKFS_SIGNATURE_LENGTH + 8; // 7+8 = 15 + p->isFirstWrite = 0; + // We're already in position for the next file write + p->fileLastOp = COOKFS_LASTOP_WRITE; + } else { + CookfsLog(printf("Cookfs_PageAddStamp: write final stamp")); + if (Tcl_Seek(p->fileChannel, p->dataInitialOffset - 8, SEEK_SET) + == -1) + { + CookfsLog(printf("Cookfs_PageAddStamp: return error," + " failed to seek")); + return TCL_ERROR; + } + if (Tcl_Write(p->fileChannel, (const char *)sizeBin, 8) != 8) + { + CookfsLog(printf("Cookfs_PageAddStamp: return error," + " failed to write size")); + return TCL_ERROR; + } + } + + CookfsLog(printf("Cookfs_PageAddStamp: ok")); + return TCL_OK; + +} + /* *---------------------------------------------------------------------- * diff --git a/generic/pages.h b/generic/pages.h index e35847c..3466ba3 100644 --- a/generic/pages.h +++ b/generic/pages.h @@ -84,6 +84,8 @@ typedef struct Cookfs_Pages { int fileCompression; int fileCompressionLevel; char fileSignature[COOKFS_SIGNATURE_LENGTH]; + int isFirstWrite; + char fileStamp[COOKFS_SIGNATURE_LENGTH]; Tcl_Channel fileChannel; int fileLastOp; int useFoffset; @@ -176,6 +178,8 @@ Tcl_Obj *Cookfs_PagesGetHashAsObj(Cookfs_Pages *p); int Cookfs_PagesSetHashByObj(Cookfs_Pages *p, Tcl_Obj *pagehash, Tcl_Interp *interp); +int Cookfs_PageAddStamp(Cookfs_Pages *p, Tcl_WideInt size); + #endif /* COOKFS_USECPAGES */ #endif /* COOKFS_PAGES_H */ diff --git a/generic/pagesCompr.c b/generic/pagesCompr.c index d30a074..5d2d23f 100644 --- a/generic/pagesCompr.c +++ b/generic/pagesCompr.c @@ -518,6 +518,9 @@ void Cookfs_SeekToPage(Cookfs_Pages *p, int idx) { int Cookfs_WritePage(Cookfs_Pages *p, int idx, unsigned char *bytes, int origSize, Tcl_Obj *compressedData) { int size = -1; + // Add initial stamp if needed + Cookfs_PageAddStamp(p, 0); + /* if last operation was not write, we need to seek * to make sure we're at location where we should be writing */ if ((idx >= 0) && (p->fileLastOp != COOKFS_LASTOP_WRITE)) { diff --git a/scripts/pages.tcl b/scripts/pages.tcl index 62f5cd8..d65c668 100644 --- a/scripts/pages.tcl +++ b/scripts/pages.tcl @@ -21,6 +21,7 @@ proc cookfs::tcl::pages {args} { set name ::cookfs::tcl::pages::handle[incr pages::pageshandleIdx] upvar #0 $name c array set c { + firstwrite 0 readonly 0 cachelist {} compression zlib @@ -32,6 +33,7 @@ proc cookfs::tcl::pages {args} { indexdata "" endoffset "" cfsname "CFS0002" + cfsstamp "CFSS002" lastop read hash crc32 alwayscompress 0 @@ -150,9 +152,17 @@ proc cookfs::tcl::pages {args} { set c(indexChanged) 0 } else { if {$c(readonly)} { + if { [catch { pages::searchstamp $name } size] } { + set size -1 + } catch {close $c(fh)} + if { $size > 0 } { + set msg "The archive \"[lindex $args 0]\" appears to be corrupted\ + or truncated. Expected archive size is $size bytes or larger." + } error $msg $msg } + set c(firstwrite) 1 set c(startoffset) $c(endoffset) set c(haschanged) 1 set c(indexChanged) 1 @@ -351,6 +361,47 @@ proc cookfs::tcl::pages::readIndex {name msgVariable} { return 1 } +proc cookfs::tcl::pages::searchstamp {name} { + upvar #0 $name c + set buffer "" + # read by 512kb chunks + set chunk 524288 + # max read 10mb + set maxread 10485760 + set read 0 + seek $c(fh) 0 start + while { ![eof $c(fh)] && $read < $maxread } { + set initialbufsize [string length $buffer] + append buffer [read $c(fh) $chunk] + incr read [expr { [string length $buffer] - $initialbufsize }] + if { [set pos [string first $c(cfsstamp) $buffer]] == -1 } { + set buffer [string range $buffer end-20 end] + continue + } + incr pos [string length $c(cfsstamp)] + binary scan [string range $buffer $pos $pos+8] W size + return $size + } + return -1 +} + +proc cookfs::tcl::pages::addstamp {name size} { + upvar #0 $name c + set stamp "$c(cfsstamp)[binary format W $size]" + if { $size == 0 } { + if { !$c(firstwrite) } { + return + } + seek $c(fh) 0 end + puts -nonewline $c(fh) $stamp + incr c(startoffset) [string length $stamp] + set c(firstwrite) 0 + } else { + seek $c(fh) [expr { $c(startoffset) - [string length $stamp] }] start + puts -nonewline $c(fh) $stamp + } +} + proc cookfs::tcl::pages::pagewrite {name contents} { upvar #0 $name c @@ -363,6 +414,7 @@ proc cookfs::tcl::pages::pagewrite {name contents} { seek $c(fh) 0 end } if {[catch { + addstamp $name 0 puts -nonewline $c(fh) $contents }]} { error "Unable to add page" @@ -416,6 +468,7 @@ proc cookfs::tcl::pages::cleanup {name} { asyncDecompressFinalize $name if {$c(haschanged) || $c(indexChanged)} { + addstamp $name 0 set offset $c(startoffset) foreach i $c(idx.sizelist) { incr offset $i @@ -432,6 +485,7 @@ proc cookfs::tcl::pages::cleanup {name} { if {$eo < $c(endoffset)} { catch {chan truncate $c(fh)} } + addstamp $name $eo set c(endoffset) $eo set c(haschanged) 0 set c(indexChanged) 0 diff --git a/tests/pages.test b/tests/pages.test index 34e37de..0715cb2 100644 --- a/tests/pages.test +++ b/tests/pages.test @@ -67,7 +67,7 @@ tcltest::test cookfsPages-1.5 "Check dataoffset command reporting properly for e } -cleanup { $pg delete file delete -force $file -} -returnCodes {ok} -result 0,0,5 +} -returnCodes {ok} -result 15,15,20 tcltest::test cookfsPages-1.6 "Check dataoffset command reporting properly for files with prefix" -setup { set file [tcltest::makeFile {} pages.cfs] @@ -86,7 +86,7 @@ tcltest::test cookfsPages-1.6 "Check dataoffset command reporting properly for f } -cleanup { $pg delete file delete -force $file -} -returnCodes {ok} -result 4096,4096,4101 +} -returnCodes {ok} -result 4111,4111,4116 tcltest::test cookfsPages-1.7 "Check filesize command reporting properly for empty archives" -setup { set file [tcltest::makeFile {} pages.cfs] @@ -100,8 +100,8 @@ tcltest::test cookfsPages-1.7 "Check filesize command reporting properly for emp } -cleanup { $pg delete file delete -force $file - # -result is 4097 - 4096+1 byte for page prefix -} -returnCodes {ok} -result 4097 + # -result is 4108 - 4096+1 byte for page prefix+15 bytes for stamp +} -returnCodes {ok} -result 4112 tcltest::test cookfsPages-1.8 "Check filesize command reporting properly for files with prefix" -setup { set file [tcltest::makeFile {} pages.cfs] @@ -117,8 +117,8 @@ tcltest::test cookfsPages-1.8 "Check filesize command reporting properly for fil } -cleanup { $pg delete file delete -force $file - # -result is 8193 - 4096+4096+1 byte for page prefix -} -returnCodes {ok} -result 8193 + # -result is 8208 - 4096+4096+1 byte for page prefix+15 bytes for stamp +} -returnCodes {ok} -result 8208 tcltest::test cookfsPages-2.1 "Test that different pages get different indexes" -setup { set file [tcltest::makeFile {} pages.cfs] @@ -416,7 +416,7 @@ tcltest::test cookfsPages-5.2.1 "Check searching for end of archive if endoffset } -cleanup { $pg delete file delete -force $file -} -returnCodes {ok} -result 0 +} -returnCodes {ok} -result 15; # 15 is the size of stamp tcltest::test cookfsPages-5.2.2 "Check searching for end of archive if endoffset not specified for small file, 65500 bytes of garbage at the end of the file" -setup { set file [tcltest::makeFile {} pages.cfs] @@ -438,7 +438,7 @@ tcltest::test cookfsPages-5.2.2 "Check searching for end of archive if endoffset } -cleanup { $pg delete file delete -force $file -} -returnCodes {ok} -result 0 +} -returnCodes {ok} -result 15; # 15 is the size of stamp tcltest::test cookfsPages-5.3.1 "Check searching for end of archive if endoffset not specified for large file, 256 bytes of garbage at the end of the file" -setup { set file [tcltest::makeFile {} pages.cfs] @@ -460,7 +460,7 @@ tcltest::test cookfsPages-5.3.1 "Check searching for end of archive if endoffset } -cleanup { $pg delete file delete -force $file -} -returnCodes {ok} -result 0 +} -returnCodes {ok} -result 15; # 15 is the size of stamp tcltest::test cookfsPages-5.3.2 "Check searching for end of archive if endoffset not specified for large file, 65500 bytes of garbage at the end of the file" -setup { set file [tcltest::makeFile {} pages.cfs] @@ -482,7 +482,7 @@ tcltest::test cookfsPages-5.3.2 "Check searching for end of archive if endoffset } -cleanup { $pg delete file delete -force $file -} -returnCodes {ok} -result 0 +} -returnCodes {ok} -result 15; # 15 is the size of stamp tcltest::test cookfsPages-6.1 "Test multiple compression algorithms" -setup { set file [tcltest::makeFile {} pages.cfs] @@ -705,7 +705,7 @@ tcltest::test cookfsPages-8.4 "Test returning offset when overwriting appended d file delete -force $file } -returnCodes {ok} -result 1025 -tcltest::test cookfsPages-9.1 "Test only changing index information" -setup { +tcltest::test cookfsPages-9.1.1 "Test only changing index information" -setup { set file [tcltest::makeFile {TESTTEST} pages.cfs] } -body { set pg [cookfs::pages $file] @@ -725,6 +725,28 @@ tcltest::test cookfsPages-9.1 "Test only changing index information" -setup { file delete -force $file } -returnCodes {ok} -result IDX1 +tcltest::test cookfsPages-9.1.2 "Test only changing index information without pages" -setup { + set file [tcltest::makeFile {TESTTEST} pages.cfs] +} -body { + set result [list] + + set pg [cookfs::pages $file] + $pg index IDX0 + set offset [$pg close] + $pg delete + + set pg [cookfs::pages $file] + lappend result [$pg index] + $pg index IDX1 + $pg delete + + set pg [cookfs::pages $file] + lappend result [$pg index] +} -cleanup { + catch {$pg delete} + file delete -force $file +} -returnCodes {ok} -result {IDX0 IDX1} + tcltest::test cookfsPages-10.1 "Test crc32 checksum algorithm" -setup { set file [tcltest::makeFile {TESTTEST} pages.cfs] } -body { @@ -906,8 +928,8 @@ tcltest::test cookfsPages-13.1 "Error message when index not found" -setup { file delete -force $file } -returnCodes {error} -match glob -result {Unable to create Cookfs object: index not found} -tcltest::test cookfsPages-13.2 "Error message when invalid signature found" -setup { - set file [tcltest::makeFile {} pages.cfs] +tcltest::test cookfsPages-13.2.1 "Error message when invalid signature found" -setup { + set file [tcltest::makeBinFile {} pages.cfs] set pg [cookfs::pages -compression none $file] $pg add [string repeat TEST 32] $pg delete @@ -926,15 +948,99 @@ tcltest::test cookfsPages-13.2 "Error message when invalid signature found" -set } -cleanup { catch {$pg delete} file delete -force $file +} -returnCodes {error} -match glob -result {The archive "*" appears to be corrupted or truncated. Expected archive size is 180 bytes or larger.} + +tcltest::test cookfsPages-13.2.2 "Error message for truncated file with 100 bytes before archive" -setup { + set file [tcltest::makeFile {} pages.cfs] + set fp [open $file wb] + seek $fp 99 start + puts -nonewline $fp x + close $fp + set pg [cookfs::pages -compression none $file] + $pg add [string repeat x 1024] + $pg delete + + set fh [open $file rb+] + chan truncate $fh 128 + close $fh +} -body { + set pg [cookfs::pages -readonly $file] +} -cleanup { + catch {$pg delete} + file delete -force $file +} -returnCodes {error} -match glob -result {The archive "*" appears to be corrupted or truncated. Expected archive size is 1176 bytes or larger.} + +tcltest::test cookfsPages-13.2.3 "Error message for truncated file with 9MB bytes before archive" -setup { + set file [tcltest::makeFile {} pages.cfs] + set fp [open $file wb] + seek $fp [expr { 1024 * 1024 * 9 - 1}] start + puts -nonewline $fp x + close $fp + set pg [cookfs::pages -compression none $file] + $pg add [string repeat x 1024] + $pg delete + + set fh [open $file rb+] + chan truncate $fh [expr { 1024 * 1024 * 9 + 128 }] + close $fh +} -body { + set pg [cookfs::pages -readonly $file] +} -cleanup { + catch {$pg delete} + file delete -force $file +} -returnCodes {error} -match glob -result {The archive "*" appears to be corrupted or truncated. Expected archive size is 9438260 bytes or larger.} + +# We are looking for no more than 10 mb, so this test should not find the stamp +# and give a generic "invalid file signature" error message. +tcltest::test cookfsPages-13.2.4 "Error message for truncated file with 10MB bytes before archive" -setup { + set file [tcltest::makeFile {} pages.cfs] + set fp [open $file wb] + seek $fp [expr { 1024 * 1024 * 10 - 1}] start + puts -nonewline $fp x + close $fp + set pg [cookfs::pages -compression none $file] + $pg add [string repeat x 1024] + $pg delete + + set fh [open $file rb+] + chan truncate $fh [expr { 1024 * 1024 * 10 + 128 }] + close $fh +} -body { + set pg [cookfs::pages -readonly $file] +} -cleanup { + catch {$pg delete} + file delete -force $file } -returnCodes {error} -match glob -result {Unable to create Cookfs object: invalid file signature} +tcltest::test cookfsPages-13.2.5 "Error message for truncated file with 1024 bytes before archive, without pages and updated fsindex" -setup { + set file [tcltest::makeFile {} pages.cfs] + set fp [open $file wb] + seek $fp 1023 start + puts -nonewline $fp x + close $fp + set pg [cookfs::pages -compression none $file] + $pg index [string repeat x 1024] + $pg delete + + set fh [open $file rb+] + chan truncate $fh [expr { 1024 + 128 }] + close $fh +} -body { + set pg [cookfs::pages -readonly $file] +} -cleanup { + catch {$pg delete} + file delete -force $file +} -returnCodes {error} -match glob -result {The archive "*" appears to be corrupted or truncated. Expected archive size is 2080 bytes or larger.} + tcltest::test cookfsPages-13.3 "Error message when unable to seek to pages" -setup { set file [tcltest::makeFile {} pages.cfs] set pg [cookfs::pages -compression none $file] $pg add 1 $pg add 2 $pg add 3 - set size [file size $file] + # [file size $file] should not be used here because the page channel + # may be buffered, and the above pages are not yet written to disk + set size [$pg filesize] $pg delete set fh [open $file r] diff --git a/tests/vfs.test b/tests/vfs.test index 4aef5b6..c0cf9f3 100644 --- a/tests/vfs.test +++ b/tests/vfs.test @@ -422,9 +422,9 @@ tcltest::test cookfsVfs-7.1 "Test filesize command" -setup { } -cleanup { catch {vfs::unmount $file} tcltest::removeFile $file -} -returnCodes {ok} -result 69634 +} -returnCodes {ok} -result 69649 -tcltest::test cookfsVfs-7.2 "Test filesize command" -setup { +tcltest::test cookfsVfs-7.2 "Test smallfilebuffersize command" -setup { set file [tcltest::makeFile {} pages.cfs] # make sure no additional data is written to file set fh [open $file w]