@@ -345,10 +345,16 @@ proc _localpath {package hostname file} { set homedir [::appfsd::get_homedir] set dir [file join $homedir .appfs "./${package}@${hostname}" "./${file}"] return $dir } + + proc _whiteoutpath {package hostname file} { + set homedir [::appfsd::get_homedir] + set dir [file join $homedir .appfs "./${package}@${hostname}" ".APPFS.WHITEOUT" "./${file}.APPFS.WHITEOUT"] + return $dir + } proc _parsepath {path} { set path [string trim $path "/"] set path [split $path "/"] set pathlen [llength $path] @@ -456,21 +462,26 @@ set retval [::appfs::db eval {SELECT DISTINCT file_name FROM files WHERE package_sha1 = $pathinfo(package_sha1) AND file_directory = $pathinfo(file);}] if {[info exists pathinfo(package)] && [info exists pathinfo(hostname)] && [info exists pathinfo(file)]} { set dir [_localpath $pathinfo(package) $pathinfo(hostname) $pathinfo(file)] + set whiteoutdir [string range [_whiteoutpath $pathinfo(package) $pathinfo(hostname) $pathinfo(file)] 0 end-15] + + foreach file [glob -nocomplain -tails -directory $whiteoutdir {{.,}*.APPFS.WHITEOUT}] { + set remove [string range $file 0 end-15] + set idx [lsearch -exact $retval $remove] + if {$idx != -1} { + set retval [lreplace $retval $idx $idx] + } + } + foreach file [glob -nocomplain -tails -directory $dir -types {d f l} {{.,}*}] { if {$file == "." || $file == ".."} { continue } - if {[string match "*.APPFS.WHITEOUT" $file]} { - set remove [string range $file 0 end-15] - set idx [lsearch -exact $retval $remove] - if {$idx != -1} { - set retval [lreplace $retval $idx $idx] - } + if {$file == ".APPFS.WHITEOUT"} { continue } if {[lsearch -exact $retval $file] != -1} { continue @@ -542,46 +553,53 @@ } "files" { set retval(packaged) 1 set localpath [_localpath $pathinfo(package) $pathinfo(hostname) $pathinfo(file)] + set whiteoutpath [_whiteoutpath $pathinfo(package) $pathinfo(hostname) $pathinfo(file)] set retval(localpath) $localpath - - if {![file exists "${localpath}.APPFS.WHITEOUT"]} { - if {[file exists $localpath]} { - set retval(is_localfile) 1 - catch { - file lstat $localpath localpathinfo - set retval(time) $localpathinfo(mtime) - - switch -- $localpathinfo(type) { - "directory" { - set retval(type) "directory" - set retval(childcount) 2 - } - "file" { - set retval(type) "file" - set retval(size) $localpathinfo(size) - if {[file executable $localpath]} { - set retval(perms) "x" - } else { - set retval(perms) "" - } - } - "link" { - set retval(type) "symlink" - set retval(source) [file readlink $localpath] - } - } - } err - } else { + set retval(whiteoutpath) $whiteoutpath + + if {[file exists $localpath]} { + set retval(is_localfile) 1 + catch { + file lstat $localpath localpathinfo + set retval(time) $localpathinfo(mtime) + + switch -- $localpathinfo(type) { + "directory" { + set retval(type) "directory" + set retval(childcount) 2 + } + "file" { + set retval(type) "file" + set retval(size) $localpathinfo(size) + if {[file executable $localpath]} { + set retval(perms) "x" + } else { + set retval(perms) "" + } + } + "link" { + set retval(type) "symlink" + set retval(source) [file readlink $localpath] + } + } + } err + } else { + if {![file exists $whiteoutpath]} { set retval(is_remotefile) 1 set work [split $pathinfo(file) "/"] set directory [join [lrange $work 0 end-1] "/"] set file [lindex $work end] + + if {$directory == "" && $file == ""} { + array set retval [list type directory childcount 2] + } + ::appfs::db eval {SELECT type, time, source, size, perms FROM files WHERE package_sha1 = $pathinfo(package_sha1) AND file_directory = $directory AND file_name = $file;} retval {} unset -nocomplain retval(*) } } @@ -603,12 +621,10 @@ } set localpath [_localpath $pathinfo(package) $pathinfo(hostname) $pathinfo(file)] if {$mode == "create"} { - file delete -- "${localpath}.APPFS.WHITEOUT" - return $localpath } if {[file exists $localpath]} { return $localpath @@ -652,10 +668,22 @@ return $localpath } return $localcachefile } + + proc localpath {path} { + array set pathinfo [_parsepath $path] + + if {$pathinfo(_type) != "files"} { + return -code error "invalid type" + } + + set localpath [_localpath $pathinfo(package) $pathinfo(hostname) $pathinfo(file)] + + return $localpath + } proc exists {path} { catch { set info [getattr $path] } err @@ -669,36 +697,26 @@ } return $info } - proc prepare_to_create {path} { - if {[exists $path] != ""} { - return -code error "File already exists" + proc prepare_to_create {path {must_not_exist 1}} { + if {$must_not_exist} { + if {[exists $path] != ""} { + return -code error "File already exists" + } } - set filename [openpath $path "create"] + set filename [localpath $path] set dirname [file dirname $filename] file mkdir $dirname return $filename } - proc localpath {path} { - array set pathinfo [_parsepath $path] - - if {$pathinfo(_type) != "files"} { - return -code error "invalid type" - } - - set localpath [_localpath $pathinfo(package) $pathinfo(hostname) $pathinfo(file)] - - return $localpath - } - proc unlinkpath {path} { array set pathattrs [exists $path] if {![info exists pathattrs(packaged)]} { return -code error "invalid type" @@ -708,35 +726,36 @@ set whiteout 0 set isdirectory 0 if {[info exists pathattrs(is_localfile)]} { if {[file isdirectory $localpath]} { - set isdirectory 1 set whiteout 1 - } else { - file delete -force -- $localpath + + set isdirectory 1 + set children [getchildren $path] } + file delete -force -- $localpath } elseif {[info exists pathattrs(is_remotefile)]} { if {$pathattrs(type) == "directory"} { set isdirectory 1 + set children [getchildren $path] } set whiteout 1 } else { return -code error "Unknown if file is remote or local !?" } if {$isdirectory} { - set children [getchildren $path] if {$children != [list]} { return -code error "Asked to delete non-empty directory" } } if {$whiteout} { - set whiteoutfile "${localpath}.APPFS.WHITEOUT" + set whiteoutfile $pathattrs(whiteoutpath) set whiteoutdir [file dirname $whiteoutfile] file mkdir $whiteoutdir close [open $whiteoutfile w] } } }