diff options
| author | apnadkarni <apnmbx-wits@yahoo.com> | 2023-09-30 16:17:28 (GMT) |
|---|---|---|
| committer | apnadkarni <apnmbx-wits@yahoo.com> | 2023-09-30 16:17:28 (GMT) |
| commit | 5763cd3970d32d646f7a798a69196e70743375b5 (patch) | |
| tree | 732565f7f728328ab17f33b8eca0ea237eb4e8b8 | |
| parent | 1f9a56de2f17443d6639279abb1b25b536c14cfc (diff) | |
| download | tcl-5763cd3970d32d646f7a798a69196e70743375b5.zip tcl-5763cd3970d32d646f7a798a69196e70743375b5.tar.gz tcl-5763cd3970d32d646f7a798a69196e70743375b5.tar.bz2 | |
Start on tests and fixes for file commands for zipfs
| -rw-r--r-- | doc/file.n | 3 | ||||
| -rw-r--r-- | doc/filename.n | 8 | ||||
| -rw-r--r-- | generic/tclIOUtil.c | 24 | ||||
| -rw-r--r-- | generic/tclZipfs.c | 21 | ||||
| -rw-r--r-- | tests/zipfs.test | 171 |
5 files changed, 203 insertions, 24 deletions
@@ -31,6 +31,8 @@ for the file. The time is measured in the standard POSIX fashion as seconds from a fixed starting time (often January 1, 1970). If the file does not exist or its access time cannot be queried or set then an error is generated. On Windows, FAT file systems do not support access time. +On \fBzipfs\fR file systems, access time is mapped to the modification +time. .TP \fBfile attributes \fIname\fR .TP @@ -295,6 +297,7 @@ the file (equivalent to Unix \fBtouch\fR). The time is measured in the standard POSIX fashion as seconds from a fixed starting time (often January 1, 1970). If the file does not exist or its modified time cannot be queried or set then an error is generated. +On \fBzipfs\fR file systems, modification time cannot be explicitly set. .TP \fBfile nativename \fIname\fR . diff --git a/doc/filename.n b/doc/filename.n index 335d8c7..d236b7f 100644 --- a/doc/filename.n +++ b/doc/filename.n @@ -119,6 +119,12 @@ Volume-relative path to a file \fBfoo\fR in the root directory of the current volume. This is not a valid UNC path, so the assumption is that the extra backslashes are superfluous. .RE +.TP +\fBZipfs\fR +On all platforms where \fBzipfs\fR support is enabled, paths within mounted +ZIP archives begin with the string returned by the \fBzipfs root\fR command +and otherwise follow the rules for the Unix paths. +.RE .SH "TILDE SUBSTITUTION" .PP In addition to the file name rules described above, Tcl also supports @@ -171,7 +177,7 @@ or dots with trailing characters .QW .....abc is illegal. .SH "SEE ALSO" -file(n), glob(n) +file(n), glob(n), zipfs(n) .SH KEYWORDS current directory, absolute file name, relative file name, volume-relative file name, portability diff --git a/generic/tclIOUtil.c b/generic/tclIOUtil.c index f665e0f..7bbb9cd 100644 --- a/generic/tclIOUtil.c +++ b/generic/tclIOUtil.c @@ -2281,11 +2281,17 @@ Tcl_FSUtime( * times to use. Should not be modified. */ { const Tcl_Filesystem *fsPtr = Tcl_FSGetFileSystemForPath(pathPtr); + int err; - if (fsPtr != NULL && fsPtr->utimeProc != NULL) { - return fsPtr->utimeProc(pathPtr, tval); + if (fsPtr == NULL) { + err = ENOENT; + } else { + if (fsPtr->utimeProc != NULL) { + return fsPtr->utimeProc(pathPtr, tval); + } + err = ENOTSUP; } - /* TODO: set errno here? Tcl_SetErrno(ENOENT); */ + Tcl_SetErrno(err); return -1; } @@ -4340,11 +4346,17 @@ Tcl_FSCreateDirectory( Tcl_Obj *pathPtr) /* Pathname of directory to create (UTF-8). */ { const Tcl_Filesystem *fsPtr = Tcl_FSGetFileSystemForPath(pathPtr); + int err; - if (fsPtr != NULL && fsPtr->createDirectoryProc != NULL) { - return fsPtr->createDirectoryProc(pathPtr); + if (fsPtr == NULL) { + err = ENOENT; + } else { + if (fsPtr->createDirectoryProc != NULL) { + return fsPtr->createDirectoryProc(pathPtr); + } + err = ENOTSUP; } - Tcl_SetErrno(ENOENT); + Tcl_SetErrno(err); return -1; } diff --git a/generic/tclZipfs.c b/generic/tclZipfs.c index 0cc46a0..23b3f3c 100644 --- a/generic/tclZipfs.c +++ b/generic/tclZipfs.c @@ -1937,6 +1937,9 @@ ZipFSCatalogFilesystem( if (!strcmp(z->name, ZIPFS_VOLUME)) { z->flags |= ZE_F_VOLUME; /* Mark as root volume */ } + Tcl_Time t; + Tcl_GetTime(&t); + z->timestamp = t.sec; z->next = zf->entries; zf->entries = z; } @@ -4696,11 +4699,13 @@ ZipChannelOpen( WriteLock(); z = ZipFSLookup(filename); if (!z) { - Tcl_SetErrno(ENOENT); + Tcl_SetErrno(wr ? ENOTSUP : ENOENT); if (interp) { - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "file not found \"%s\": %s", filename, - Tcl_PosixError(interp))); + Tcl_SetObjResult(interp, + Tcl_ObjPrintf("file \"%s\" not %s: %s", + filename, + wr ? "created" : "found", + Tcl_PosixError(interp))); } goto error; } @@ -5187,7 +5192,7 @@ ZipEntryStat( Tcl_StatBuf *buf) { ZipEntry *z; - int ret = -1; + int ret; ReadLock(); z = ZipFSLookup(path); @@ -5206,8 +5211,14 @@ ZipEntryStat( } else if (ContainsMountPoint(path, -1)) { /* An intermediate dir under which a mount exists */ memset(buf, 0, sizeof(Tcl_StatBuf)); + Tcl_Time t; + Tcl_GetTime(&t); + buf->st_atime = buf->st_mtime = buf->st_ctime = t.sec; buf->st_mode = S_IFDIR | 0555; ret = 0; + } else { + Tcl_SetErrno(ENOENT); + ret = -1; } Unlock(); return ret; diff --git a/tests/zipfs.test b/tests/zipfs.test index 583a4d8..57fb5c9 100644 --- a/tests/zipfs.test +++ b/tests/zipfs.test @@ -975,7 +975,7 @@ namespace eval test_ns_zipfs { testzipfsread stored-ar teststored.zip {} abac-repeat.txt a+ testzipfsread deflate-ar testdeflated2.zip {} abac-repeat.txt a+ - testzipfsread nosuch test.zip "file not found \"//zipfs:/testmount/nosuchfile\": no such file or directory" nosuchfile {} -returnCodes error + testzipfsread enoent test.zip "file \"//zipfs:/testmount/nosuchfile\" not found: no such file or directory" nosuchfile {} -returnCodes error testzipfsread deflate-error broken.zip {decompression error} deflatezliberror {} -returnCodes error testzipfsread bzip2 testbzip2.zip {unsupported compression method} abac-repeat.txt {} -returnCodes error testzipfsread lzma testfile-lzma.zip {unsupported compression method} abac-repeat.txt {} -returnCodes error @@ -1031,10 +1031,10 @@ namespace eval test_ns_zipfs { } -body $body -result $result {*}$args } - testzipfswrite create-w test.zip "file not found \"//zipfs:/testmount/newfile\": no such file or directory" newfile w -returnCodes error - testzipfswrite create-w+ test.zip "file not found \"//zipfs:/testmount/newfile\": no such file or directory" newfile w+ -returnCodes error + testzipfswrite create-w test.zip "file \"//zipfs:/testmount/newfile\" not created: operation not supported" newfile w -returnCodes error + testzipfswrite create-w+ test.zip "file \"//zipfs:/testmount/newfile\" not created: operation not supported" newfile w+ -returnCodes error testzipfswrite create-a test.zip "append mode not supported: permission denied" newfile a -returnCodes error - testzipfswrite create-a+ test.zip "file not found \"//zipfs:/testmount/newfile\": no such file or directory" newfile a+ -returnCodes error + testzipfswrite create-a+ test.zip "file \"//zipfs:/testmount/newfile\" not created: operation not supported" newfile a+ -returnCodes error testzipfswrite store-w teststored.zip "xyz" abac-repeat.txt w testzipfswrite deflate-w testdeflated2.zip "xyz" abac-repeat.txt w testzipfswrite store-w+ teststored.zip "xyz" abac-repeat.txt w+ @@ -1329,7 +1329,7 @@ namespace eval test_ns_zipfs { foreach key {atime ctime mtime} { # ZIP files have no TZ info so zipfs uses mktime which is localtime set time [dict get $stat $key] - if {$time ne "0"} { + if {[regexp {^\d{4}-\d\d-\d\d \d\d:\d\d:\d\d} $time]} { dict set stat $key [clock scan [dict get $stat $key] -format "%Y-%m-%d %H:%M:%S"] } } @@ -1366,25 +1366,25 @@ namespace eval test_ns_zipfs { mount [zippath test.zip] } -cleanup cleanup -body { lsort -stride 2 [file stat $defaultMountPoint] - } -result [fixupstat {atime 0 ctime 0 dev 0 gid 0 ino 0 mode 16749 mtime 0 nlink 0 size 0 type directory uid 0}] + } -result [fixupstat [list atime .* ctime .* dev 0 gid 0 ino 0 mode 16749 mtime .* nlink 0 size 0 type directory uid 0]] -match regexp test zipfs-file-stat-root-mount "Read stat of root" -setup { mount [zippath test.zip] [zipfs root] } -cleanup cleanup -body { lsort -stride 2 [file stat [zipfs root]] - } -result [fixupstat {atime 0 ctime 0 dev 0 gid 0 ino 0 mode 16749 mtime 0 nlink 0 size 0 type directory uid 0}] + } -result [fixupstat {atime .* ctime .* dev 0 gid 0 ino 0 mode 16749 mtime .* nlink 0 size 0 type directory uid 0}] -match regexp test zipfs-file-stat-root-subdir-mount "Read stat of root when mount is subdir" -setup { mount [zippath test.zip] } -cleanup cleanup -body { lsort -stride 2 [file stat [zipfs root]] - } -result [fixupstat {atime 0 ctime 0 dev 0 gid 0 ino 0 mode 16749 mtime 0 nlink 0 size 0 type directory uid 0}] + } -result [fixupstat {atime .* ctime .* dev 0 gid 0 ino 0 mode 16749 mtime .* nlink 0 size 0 type directory uid 0}] -match regexp test zipfs-file-stat-level3 "Stat on a directory that is intermediary in a mount point" -setup { mount [zippath test.zip] [file join $defaultMountPoint mt2] } -cleanup cleanup -body { lsort -stride 2 [file stat $defaultMountPoint] - } -result [fixupstat {atime 0 ctime 0 dev 0 gid 0 ino 0 mode 16749 mtime 0 nlink 0 size 0 type directory uid 0}] + } -result [fixupstat {atime .* ctime .* dev 0 gid 0 ino 0 mode 16749 mtime .* nlink 0 size 0 type directory uid 0}] -match regexp # # glob of zipfs file @@ -1432,9 +1432,13 @@ namespace eval test_ns_zipfs { [list -archive [zippath test.zip] -compsize 0 -crc 0 -mount $defaultMountPoint -offset 0 -permissions 0o555 -uncompsize 0] testzipfsfileattr mezzo [file join $defaultMountPoint a b] {} [file join $defaultMountPoint a b c] -constraints bug-4af110a6a1 - - # - # TODO - file copy, file rename etc. + foreach attr {-uncompsize -compsize -offset -mount -archive -permissions -crc} { + test zipfs-file-attrs-set$attr "Set zipfs file attribute $attr" -setup { + mount [zippath test.zip] + } -cleanup cleanup \ + -body "file attributes [file join $defaultMountPoint test] $attr {}" \ + -result "unsupported operation" -returnCodes error + } # # file normalize @@ -1490,6 +1494,149 @@ namespace eval test_ns_zipfs { 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] + # + # file copy + test zipfs-file-copy-tozip-new {Copy native file to archive} -setup { + mount [zippath test.zip] + } -cleanup { + cleanup + } -body { + file copy [makeFile "" source.tmp] [file join $defaultMountPoint X] + } -result "error copying \"*source.tmp\" to \"[file join $defaultMountPoint X]\": operation not supported" \ + -match glob -returnCodes error + test zipfs-file-copy-tozip-existing {Copy native file to archive} -setup { + mount [zippath test.zip] + } -cleanup { + cleanup + } -body { + file copy [makeFile "newtext" source.tmp] [file join $defaultMountPoint test] + } -result "error copying *: file exists" -match glob -returnCodes error + test zipfs-file-copy-tozip-existing-force {Copy native file to archive} -setup { + mount [zippath test.zip] + } -cleanup { + cleanup + } -body { + file copy -force [makeFile "newtext" source.tmp] [file join $defaultMountPoint test] + } -result "newtext" + test zipfs-file-copy-tozipdir {Copy native file to archive directory} -setup { + mount [zippath test.zip] + } -cleanup { + cleanup + } -body { + file copy [makeFile "" source.tmp] [file join $defaultMountPoint testdir] + } -result "error copying \"*source.tmp\" to \"[file join $defaultMountPoint testdir]/source.tmp\": operation not supported" \ + -match glob -returnCodes error + test zipfs-file-copydir-tozipdir {Copy native dir to archive directory} -setup { + mount [zippath test.zip] + } -cleanup { + cleanup + } -body { + file copy [temporaryDirectory] [file join $defaultMountPoint testdir] + } -result "can't create directory *: operation not supported" \ + -match glob -returnCodes error + test zipfs-file-copy-fromzip-new {Copy archive file to native} -setup { + mount [zippath test.zip] + set dst [file join [temporaryDirectory] dst.tmp] + file delete $dst + } -cleanup { + file delete $dst + cleanup + } -body { + file copy [file join $defaultMountPoint test] $dst + readbin $dst + } -result "test\n" + + + # + # file mkdir + test zipfs-file-mkdir {Make a directory in zip archive} -setup { + mount [zippath test.zip] + } -cleanup { + cleanup + } -body { + file mkdir [file join $defaultMountPoint newdir] + } -result "can't create directory \"[file join $defaultMountPoint newdir]\": operation not supported" -returnCodes error + + # Standard paths for file command tests. Because code paths are different, + # we need tests for... + set targetMountParent $defaultMountPoint; # Parent of mount directory + set targetMount [file join $targetMountParent mt] ; # Mount directory + set targetFile [file join $targetMount test]; # Normal file + set targetDir [file join $targetMount testdir]; # Directory + set targetEnoent [file join $targetMount enoent]; # Non-existing path + + proc testzipfsfile {id cmdargs result args} { + variable targetMount + test zipfs-file-$id "file $id on zipfs" -setup { + zipfs mount [zippath test.zip] $targetMount + } -cleanup cleanup -body { + file {*}$cmdargs + } -result $result {*}$args + } + proc testzipfsenotsup {id cmdargs args} { + testzipfsfile $id $cmdargs "*: operation not supported" -match glob -returnCodes error + } + + # + # file atime + + testzipfsfile atime-get-file [list atime $targetFile] 1065435402 + testzipfsfile atime-get-dir [list atime $targetDir] 1105450434 + testzipfsfile atime-get-mount [list atime $targetMount] {\d+} -match regexp + testzipfsfile atime-get-mezzo [list atime $targetMountParent] {\d+} -match regexp + testzipfsfile atime-get-root [list atime [zipfs root]] {\d+} -match regexp + testzipfsfile atime-get-enoent [list atime $targetEnoent] \ + "could not read \"$targetEnoent\": no such file or directory" -returnCodes error + + set t [clock seconds] + testzipfsenotsup atime-set-file [list atime $targetFile $t] + testzipfsenotsup atime-set-dir [list atime $targetDir $t] + testzipfsenotsup atime-set-mount [list atime $targetMount $t] + testzipfsenotsup atime-set-mezzo [list atime $targetMountParent $t] + testzipfsenotsup atime-set-root [list atime [zipfs root] $t] + testzipfsfile atime-set-enoent [list atime $targetEnoent $t] \ + "could not read \"$targetEnoent\": no such file or directory" -returnCodes error + + # + # file isdirectory + testzipfsfile isdirectory-file [list isdirectory $targetFile] 0 + testzipfsfile isdirectory-dir [list isdirectory $targetDir] 1 + testzipfsfile isdirectory-mount [list isdirectory $targetMount] 1 + testzipfsfile isdirectory-mezzo [list isdirectory $targetMountParent] 1 + testzipfsfile isdirectory-root [list isdirectory [zipfs root]] 1 + testzipfsfile isdirectory-enoent [list isdirectory $targetEnoent] 0 + + # + # file isfile + testzipfsfile isfile-file [list isfile $targetFile] 1 + testzipfsfile isfile-dir [list isfile $targetDir] 0 + testzipfsfile isfile-mount [list isfile $targetMount] 0 + testzipfsfile isfile-mezzo [list isfile $targetMountParent] 0 + testzipfsfile isfile-root [list isfile [zipfs root]] 0 + testzipfsfile isfile-enoent [list isfile $targetEnoent] 0 + + # + # file mtime + + testzipfsfile mtime-get-file [list mtime $targetFile] 1065435402 + testzipfsfile mtime-get-dir [list mtime $targetDir] 1105450434 + testzipfsfile mtime-get-mount [list mtime $targetMount] {\d+} -match regexp + testzipfsfile mtime-get-mezzo [list mtime $targetMountParent] {\d+} -match regexp + testzipfsfile mtime-get-root [list mtime [zipfs root]] {\d+} -match regexp + testzipfsfile mtime-set-enoent [list mtime $targetEnoent $t] \ + "could not read \"$targetEnoent\": no such file or directory" -returnCodes error + + set t [clock seconds] + testzipfsenotsup mtime-set-file [list mtime $targetFile $t] + testzipfsenotsup mtime-set-dir [list mtime $targetDir $t] + testzipfsenotsup mtime-set-mount [list mtime $targetMount $t] + testzipfsenotsup mtime-set-mezzo [list mtime $targetMountParent $t] + testzipfsenotsup mtime-set-root [list mtime [zipfs root] $t] + testzipfsfile mtime-set-enoent [list mtime $targetEnoent $t] \ + "could not read \"$targetEnoent\": no such file or directory" -returnCodes error + + + # TODO - mkkey, mkimg, mkzip, lmkimg, lmkzip testnumargs "zipfs mkkey" "password" "" -constraints zipfs testnumargs "zipfs mkimg" "outfile indir" "?strip? ?password? ?infile?" |
