summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorapnadkarni <apnmbx-wits@yahoo.com>2023-09-20 06:32:32 (GMT)
committerapnadkarni <apnmbx-wits@yahoo.com>2023-09-20 06:32:32 (GMT)
commitc1d507af302a1330ff03c3507d5bd1c965224064 (patch)
tree1ac896f6837e2749b2e37997aa3bdf7e94c91a89
parentef9ea15d10ceacfb89e94c3f73a3fab9c3601df1 (diff)
downloadtcl-c1d507af302a1330ff03c3507d5bd1c965224064.zip
tcl-c1d507af302a1330ff03c3507d5bd1c965224064.tar.gz
tcl-c1d507af302a1330ff03c3507d5bd1c965224064.tar.bz2
Bug [a288e2003b] - file normalize broken for zipfs.
-rw-r--r--generic/tclInt.h2
-rw-r--r--generic/tclPathObj.c48
-rw-r--r--generic/tclZipfs.c27
-rw-r--r--tests/zipfs.test53
4 files changed, 115 insertions, 15 deletions
diff --git a/generic/tclInt.h b/generic/tclInt.h
index d5a8645..a336a53 100644
--- a/generic/tclInt.h
+++ b/generic/tclInt.h
@@ -3477,7 +3477,7 @@ MODULE_SCOPE void TclErrorStackResetIf(Tcl_Interp *interp,
const char *msg, Tcl_Size length);
/* Tip 430 */
MODULE_SCOPE int TclZipfs_Init(Tcl_Interp *interp);
-
+MODULE_SCOPE int TclIsZipfsPath(const char *path);
MODULE_SCOPE int *TclGetUnicodeFromObj(Tcl_Obj *, int *);
MODULE_SCOPE Tcl_Obj *TclNewUnicodeObj(const int *, int);
diff --git a/generic/tclPathObj.c b/generic/tclPathObj.c
index 1e8f994..0095469 100644
--- a/generic/tclPathObj.c
+++ b/generic/tclPathObj.c
@@ -143,9 +143,17 @@ TclFSNormalizeAbsolutePath(
* directory separator - we can't use '..' to
* remove the volume in a path. */
Tcl_Obj *retVal = NULL;
+ int zipVolumeLen;
dirSep = TclGetString(pathPtr);
- if (tclPlatform == TCL_PLATFORM_WINDOWS) {
+ zipVolumeLen = TclIsZipfsPath(dirSep);
+ if (zipVolumeLen) {
+ /*
+ * NOTE: file normalization for zipfs is very specific to
+ * format of zipfs volume being of the form //xxx:/
+ */
+ dirSep += zipVolumeLen-1; /* Start parse after : */
+ } else if (tclPlatform == TCL_PLATFORM_WINDOWS) {
if ( (dirSep[0] == '/' || dirSep[0] == '\\')
&& (dirSep[1] == '/' || dirSep[1] == '\\')
&& (dirSep[2] == '?')
@@ -246,13 +254,17 @@ TclFSNormalizeAbsolutePath(
Tcl_AppendToObj(retVal, dirSep, 1);
}
if (!first || (tclPlatform == TCL_PLATFORM_UNIX)) {
- linkObj = Tcl_FSLink(retVal, NULL, 0);
+ if (zipVolumeLen) {
+ linkObj = NULL;
+ } else {
+ linkObj = Tcl_FSLink(retVal, NULL, 0);
- /* Safety check in case driver caused sharing */
- if (Tcl_IsShared(retVal)) {
- TclDecrRefCount(retVal);
- retVal = Tcl_DuplicateObj(retVal);
- Tcl_IncrRefCount(retVal);
+ /* Safety check in case driver caused sharing */
+ if (Tcl_IsShared(retVal)) {
+ TclDecrRefCount(retVal);
+ retVal = Tcl_DuplicateObj(retVal);
+ Tcl_IncrRefCount(retVal);
+ }
}
if (linkObj != NULL) {
@@ -322,10 +334,12 @@ TclFSNormalizeAbsolutePath(
/*
* Either way, we now remove the last path element (but
- * not the first character of the path).
+ * not the first character of the path). In the case of
+ * zipfs, make sure not to go beyond the zipfs volume.
*/
- while (--curLen >= 0) {
+ int minLen = zipVolumeLen ? zipVolumeLen - 1 : 0;
+ while (--curLen >= minLen) {
if (IsSeparatorOrNull(linkStr[curLen])) {
if (curLen) {
Tcl_SetObjLength(retVal, curLen);
@@ -384,13 +398,21 @@ TclFSNormalizeAbsolutePath(
/*
* Ensure a windows drive like C:/ has a trailing separator.
+ * Likewise for zipfs volumes.
*/
-
- if (tclPlatform == TCL_PLATFORM_WINDOWS) {
+ if (zipVolumeLen || (tclPlatform == TCL_PLATFORM_WINDOWS)) {
+ int needTrailingSlash = 0;
int len;
const char *path = TclGetStringFromObj(retVal, &len);
-
- if (len == 2 && path[0] != 0 && path[1] == ':') {
+ if (zipVolumeLen) {
+ if (len == (zipVolumeLen - 1))
+ needTrailingSlash = 1;
+ } else {
+ if (len == 2 && path[0] != 0 && path[1] == ':') {
+ needTrailingSlash = 1;
+ }
+ }
+ if (needTrailingSlash) {
if (Tcl_IsShared(retVal)) {
TclDecrRefCount(retVal);
retVal = Tcl_DuplicateObj(retVal);
diff --git a/generic/tclZipfs.c b/generic/tclZipfs.c
index 47b294e..45a2041 100644
--- a/generic/tclZipfs.c
+++ b/generic/tclZipfs.c
@@ -69,7 +69,6 @@
} \
} while (0)
-
#ifdef HAVE_ZLIB
#include "zlib.h"
#include "crypt.h"
@@ -451,6 +450,27 @@ static Tcl_ChannelType ZipChannelType = {
#define ERROR_LENGTH ((size_t) -1)
/*
+ *------------------------------------------------------------------------
+ *
+ * TclIsZipfsPath --
+ *
+ * Checks if the passed path has a zipfs volume prefix.
+ *
+ * Results:
+ * 0 if not a zipfs path
+ * else the length of the zipfs volume prefix
+ *
+ * Side effects:
+ * None.
+ *
+ *------------------------------------------------------------------------
+ */
+int TclIsZipfsPath (const char *path)
+{
+ return strncmp(path, ZIPFS_VOLUME, ZIPFS_VOLUME_LEN) ? 0 : ZIPFS_VOLUME_LEN;
+}
+
+/*
*-------------------------------------------------------------------------
*
* ZipReadInt, ZipReadShort, ZipWriteInt, ZipWriteShort --
@@ -6142,6 +6162,11 @@ TclZipfs_TclLibrary(void)
return NULL;
}
+int TclIsZipfsPath (const char *path)
+{
+ return 0;
+}
+
#endif /* !HAVE_ZLIB */
/*
diff --git a/tests/zipfs.test b/tests/zipfs.test
index f5fb9f9..727adf0 100644
--- a/tests/zipfs.test
+++ b/tests/zipfs.test
@@ -1114,6 +1114,59 @@ namespace eval test_ns_zipfs {
#
# TODO - file copy, file rename etc.
+ #
+ # file normalize
+ proc testzipfsnormalize {id path result {dir {}}} {
+ if {$dir eq ""} {
+ test zipfs-file-normalize-$id "zipfs file normalize $id" -body {
+ file normalize $path
+ } -result $result
+ } else {
+ test zipfs-file-normalize-$id "zipfs file normalize $id" -setup {
+ set cwd [pwd]
+ mount [zippath test.zip] [zipfs root]
+ cd $dir
+ } -cleanup {
+ cd $cwd
+ cleanup
+ } -body {
+ file normalize $path
+ } -result $result
+ }
+ }
+ # The parsing requires all these cases for various code paths
+ # in particular, root, one below root and more than one below root
+ testzipfsnormalize dot-1 [zipfs root] [zipfs root]
+ testzipfsnormalize dot-2 [file join [zipfs root] .] [zipfs root]
+ testzipfsnormalize dot-3 [file join [zipfs root] . .] [zipfs root]
+ testzipfsnormalize dot-4 [file join [zipfs root] a .] [file join [zipfs root] a]
+ testzipfsnormalize dot-5 [file join [zipfs root] a . . .] [file join [zipfs root] a]
+ testzipfsnormalize dot-6 [file join [zipfs root] a b .] [file join [zipfs root] a b]
+ testzipfsnormalize dot-7 [file join [zipfs root] a b . .] [file join [zipfs root] a b]
+
+ testzipfsnormalize dotdot-1 [file join [zipfs root] ..] [zipfs root]
+ testzipfsnormalize dotdot-2 [file join [zipfs root] .. ..] [zipfs root]
+ testzipfsnormalize dotdot-3 [file join [zipfs root] a ..] [zipfs root]
+ testzipfsnormalize dotdot-4 [file join [zipfs root] a .. .. ..] [zipfs root]
+ testzipfsnormalize dotdot-5 [file join [zipfs root] a b ..] [file join [zipfs root] a]
+ testzipfsnormalize dotdot-6 [file join [zipfs root] a b ..] [file join [zipfs root] a]
+ testzipfsnormalize dotdot-7 [file join [zipfs root] a b .. ..] [zipfs root]
+ testzipfsnormalize dotdot-8 [file join [zipfs root] a b .. .. .. ..] [zipfs root]
+
+ testzipfsnormalize relative-1 a [file join [zipfs root] a] [zipfs root]
+ testzipfsnormalize relative-2 . [zipfs root] [zipfs root]
+ testzipfsnormalize relative-3 ./ [zipfs root] [zipfs root]
+ testzipfsnormalize relative-4 ./a [file join [zipfs root] a] [zipfs root]
+ testzipfsnormalize relative-5 ../ [file join [zipfs root]] [zipfs root]
+ testzipfsnormalize relative-6 ../a [file join [zipfs root] a] [zipfs root]
+ testzipfsnormalize relative-7 ../a/ [file join [zipfs root] a] [zipfs root]
+ testzipfsnormalize relative-8 ../.. [zipfs root] [zipfs root]
+ testzipfsnormalize relative-9 dir/a [file join [zipfs root] dir a] [zipfs root]
+ testzipfsnormalize relative-10 dir/dirb/.. [file join [zipfs root] dir] [zipfs root]
+ testzipfsnormalize relative-11 dir/../a [file join [zipfs root] a] [zipfs root]
+ testzipfsnormalize relative-12 dir/../a/ [file join [zipfs root] a] [zipfs root]
+ testzipfsnormalize relative-13 dir/../../../a [file join [zipfs root] a] [zipfs root]
+ testzipfsnormalize relative-14 a [file join [zipfs root] testdir a] [file join [zipfs root] testdir]
# TODO - mkkey, mkimg, mkzip, lmkimg, lmkzip
testnumargs "zipfs mkkey" "password" "" -constraints zipfs