Migrate to SHA-256 and implicit repo ID (PARTIAL REPO FORMAT CHANGE)
* local, rsync, ssh, sftp repositories are still compatible * gitception/git backend repositories are not compatible and need to be deleted and recreated * Put manifest in a static location, so we don't need #fragment in the URL * Record repository ID for each remote, and warn if it changes. * Use SHA-256 by default but allow reading SHA-224-identified packfiles * The URL #fragment identifies branch to use when using the git backend
This commit is contained in:
parent
4d28d8fe4d
commit
14da0a4d33
2 changed files with 110 additions and 72 deletions
47
README.rst
47
README.rst
|
@ -44,18 +44,15 @@ Quickstart
|
||||||
git remote add cryptremote gcrypt::rsync://example.com:repo
|
git remote add cryptremote gcrypt::rsync://example.com:repo
|
||||||
git push cryptremote master
|
git push cryptremote master
|
||||||
> gcrypt: Setting up new repository
|
> gcrypt: Setting up new repository
|
||||||
> gcrypt: Repository URL is gcrypt::rsync://example.com:repo#KNBr0wKzct52
|
> gcrypt: Repository ID is :SHA256:3a29d035adf234af7e[... ]
|
||||||
> gcrypt: (configuration for cryptremote updated)
|
|
||||||
> [ more lines .. ]
|
> [ more lines .. ]
|
||||||
> To gcrypt::[...]
|
> To gcrypt::[...]
|
||||||
> * [new branch] master -> master
|
> * [new branch] master -> master
|
||||||
|
|
||||||
* Share the updated Repository URL with all participants.
|
(The generated Repository id is not secret, it only exists to ensure
|
||||||
|
that two repositories signed by the same user can be distinguished.
|
||||||
(The generated Repository URL is not secret, it only exists to ensure
|
You will see a warning if the remote repository ID changes, which will
|
||||||
that two repositories signed by the same user can not be maliciously
|
only happen if the remote was re-created or switched out.)
|
||||||
switched around. It incidentally allows multiple repositories to all
|
|
||||||
share location.)
|
|
||||||
|
|
||||||
Design Goals
|
Design Goals
|
||||||
............
|
............
|
||||||
|
@ -98,10 +95,12 @@ Examples
|
||||||
How to use a git backend::
|
How to use a git backend::
|
||||||
|
|
||||||
# notice that the target repo must already exist and its
|
# notice that the target repo must already exist and its
|
||||||
# `master` branch will be overwritten!
|
# `next` branch will be overwritten!
|
||||||
git remote add gitcrypt gcrypt::git@example.com:repo
|
git remote add gitcrypt gcrypt::git@example.com:repo#next
|
||||||
git push gitcrypt HEAD
|
git push gitcrypt HEAD
|
||||||
|
|
||||||
|
The URL fragment (`#next` here) indicates which branch is used.
|
||||||
|
|
||||||
Notes
|
Notes
|
||||||
=====
|
=====
|
||||||
|
|
||||||
|
@ -112,20 +111,20 @@ Repository Format
|
||||||
|
|
||||||
EncSign(X) is sign+encrypt to a PGP key holder
|
EncSign(X) is sign+encrypt to a PGP key holder
|
||||||
Encrypt(K,X) is symmetric encryption
|
Encrypt(K,X) is symmetric encryption
|
||||||
Hash(X) is SHA-224
|
Hash(X) is SHA-256
|
||||||
|
|
||||||
B: branch list
|
B: branch list
|
||||||
L: list of the hash (Hi) and key (Ki) for each packfile
|
L: list of the hash (Hi) and key (Ki) for each packfile
|
||||||
R: Hash(Repository ID)
|
R: repository id
|
||||||
|
|
||||||
Store Manifest as EncSign(B || L || R) in filename R
|
Store Manifest as EncSign(B || L || R)
|
||||||
Store each packfile P as P' = Encrypt(Ki, P) in filename Hi
|
Store each packfile P as P' = Encrypt(Ki, P) in filename Hi
|
||||||
where Hi = Hash(P') and Ki is a random string
|
where Hi = Hash(P') and Ki is a random string
|
||||||
|
|
||||||
To read the repository
|
To read the repository
|
||||||
|
|
||||||
decrypt+verify Manifest using private key -> (B, L, R)
|
decrypt+verify Manifest using private key -> (B, L, R)
|
||||||
verify R matches Hash(Requested Repository ID)
|
warn if R does not match saved repository id for this remote
|
||||||
for Hi, Ki in L:
|
for Hi, Ki in L:
|
||||||
download file Hi from the server -> P'
|
download file Hi from the server -> P'
|
||||||
verify Hash(P') matches Hi
|
verify Hash(P') matches Hi
|
||||||
|
@ -138,14 +137,13 @@ Manifest file
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
$ gpg -d < 5a191cea8c1021a95d813c4007c14f2cc987a40880c2f669430f1916
|
$ gpg -d 91bd0c092128cf2e60e1a608c31e92caf1f9c1595f83f2890ef17c0e4881aa0a
|
||||||
b4a4a39365d19282810c19d0f3f24d04dd2d179f refs/tags/version1
|
542051c7cd152644e4995bda63cc3ddffd635958 refs/heads/next
|
||||||
1d323ddadf4cf1d80fced447e637ab3766b168b7 refs/heads/master
|
3c9e76484c7596eff70b21cbe58408b2774bedad refs/heads/master
|
||||||
pack :SHA224:cfdf36515e0d0820554fe5fd9f00a4bee17bcf88ec8a752d851c46ee \
|
pack :SHA256:f2ad50316fbca42c553810aec3709c24974585ec1b34aae77d5cd4ba67092dc4 z8YoAnFpMlWPIYG8wo1adewd4Fp7Fo3PkI2mND49P1qm
|
||||||
Rc+j8Nv6GOW3mBhWOx6W6jjz3BTX7B6XIJ6RYI+P4TEy
|
pack :SHA256:a6e17bb4c042bdfa8e38856ee6d058d0c0f0c575ace857c4795426492f379584 82+k2cbiUn7i2cW0dgXfyX6wXGpvVaQGj5sF59Y8my5W
|
||||||
pack :SHA224:a43ccd208d3bd2ea582dbd5407cb8ed6e18b150b1da25c806115eaa5 \
|
keep :SHA256:f2ad50316fbca42c553810aec3709c24974585ec1b34aae77d5cd4ba67092dc4 1
|
||||||
UXR3/R7awFCUJWYdzXzrlkk7E2Acxq/Y4EfEcd62AwGG
|
repo :SHA256:ef8e52a7ea96761f713c14caa7190b5f3b55ff87ffe091cab40f7cbe1d3b5b96
|
||||||
repo :SHA224:5a191cea8c1021a95d813c4007c14f2cc987a40880c2f669430f1916 1
|
|
||||||
|
|
||||||
Each item extends until newline, and matches one of the following forms:
|
Each item extends until newline, and matches one of the following forms:
|
||||||
|
|
||||||
|
@ -158,8 +156,8 @@ Each item extends until newline, and matches one of the following forms:
|
||||||
`keep :<hashtype>:<hash> <generation>`
|
`keep :<hashtype>:<hash> <generation>`
|
||||||
Packfile hash and its repack generation
|
Packfile hash and its repack generation
|
||||||
|
|
||||||
`repo :<hashtype>:<hash> <version>`
|
`repo :<hashtype>:<hash>`
|
||||||
The hash of the repository id.
|
The repository id
|
||||||
|
|
||||||
`extn <name> ...`
|
`extn <name> ...`
|
||||||
Extension field, preserved but unused.
|
Extension field, preserved but unused.
|
||||||
|
@ -168,7 +166,6 @@ Each item extends until newline, and matches one of the following forms:
|
||||||
Yet to be Implemented
|
Yet to be Implemented
|
||||||
.....................
|
.....................
|
||||||
|
|
||||||
+ Repacking the remote repository
|
|
||||||
+ Some kind of simple keyring management
|
+ Some kind of simple keyring management
|
||||||
|
|
||||||
See Also
|
See Also
|
||||||
|
|
|
@ -16,9 +16,10 @@ Gref="refs/gcrypt/gitception$GITCEPTION"
|
||||||
Gref_rbranch="refs/heads/master"
|
Gref_rbranch="refs/heads/master"
|
||||||
Repoid=
|
Repoid=
|
||||||
Packkey_bytes=33 # 33 random bytes for passphrase, still compatible if changed
|
Packkey_bytes=33 # 33 random bytes for passphrase, still compatible if changed
|
||||||
Hashtype=SHA224 # incompatible if changed
|
Hashtype=SHA256 # SHA512 SHA384 SHA256 SHA224 supported.
|
||||||
Packpfx="pack :${Hashtype}:"
|
Packpat="pack :*:"
|
||||||
Keeppfx="keep :${Hashtype}:"
|
Manifestfile=91bd0c092128cf2e60e1a608c31e92caf1f9c1595f83f2890ef17c0e4881aa0a
|
||||||
|
Urlfrag=
|
||||||
|
|
||||||
Branchlist=
|
Branchlist=
|
||||||
Packlist=
|
Packlist=
|
||||||
|
@ -68,7 +69,7 @@ splitcolon()
|
||||||
prefix_=${1%%:*}
|
prefix_=${1%%:*}
|
||||||
suffix_=${1#*:}
|
suffix_=${1#*:}
|
||||||
}
|
}
|
||||||
repoidstr() { xecho "repo :${Hashtype}:$Repoid 1"; }
|
repoidstr() { xecho "repo $Repoid"; }
|
||||||
|
|
||||||
## gitception part
|
## gitception part
|
||||||
# Fetch giturl $1, file $2
|
# Fetch giturl $1, file $2
|
||||||
|
@ -77,7 +78,7 @@ gitception_get()
|
||||||
# Take care to preserve FETCH_HEAD
|
# Take care to preserve FETCH_HEAD
|
||||||
local ret_=: obj_id= f_head="$GIT_DIR/FETCH_HEAD"
|
local ret_=: obj_id= f_head="$GIT_DIR/FETCH_HEAD"
|
||||||
[ -e "$f_head" ] && command mv -f "$f_head" "$f_head.$$~" || :
|
[ -e "$f_head" ] && command mv -f "$f_head" "$f_head.$$~" || :
|
||||||
git fetch -q -f "$1" "$Gref_rbranch:$Gref" 2>/dev/tty >/dev/null &&
|
git fetch -q -f "$1" "refs/heads/${Urlfrag:-master}:$Gref" 2>/dev/tty >/dev/null &&
|
||||||
obj_id="$(git ls-tree "$Gref" | xgrep -E '\b'"$2"'$' | awk '{print $3}')" &&
|
obj_id="$(git ls-tree "$Gref" | xgrep -E '\b'"$2"'$' | awk '{print $3}')" &&
|
||||||
isnonnull "$obj_id" && git cat-file blob "$obj_id" && ret_=: ||
|
isnonnull "$obj_id" && git cat-file blob "$obj_id" && ret_=: ||
|
||||||
{ ret_=false && : ; }
|
{ ret_=false && : ; }
|
||||||
|
@ -180,7 +181,8 @@ PUT_FINAL()
|
||||||
then
|
then
|
||||||
:
|
:
|
||||||
else
|
else
|
||||||
git push --quiet -f "${1#gitception://}" "$Gref:$Gref_rbranch"
|
git push --quiet -f "${1#gitception://}" \
|
||||||
|
"$Gref:refs/heads/${Urlfrag:-master}"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -285,14 +287,17 @@ genkey()
|
||||||
gpg --armor --gen-rand 1 "$1"
|
gpg --armor --gen-rand 1 "$1"
|
||||||
}
|
}
|
||||||
|
|
||||||
pack_hash()
|
gpg_hash()
|
||||||
{
|
{
|
||||||
local hash_=
|
local hash_=
|
||||||
hash_=$(gpg --with-colons --print-md "$Hashtype" | tr A-F a-f)
|
hash_=$(gpg --with-colons --print-md "$1" | tr A-F a-f)
|
||||||
hash_=${hash_#:*:}
|
hash_=${hash_#:*:}
|
||||||
xecho "${hash_%:}"
|
xecho "${hash_%:}"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pack_hash() { gpg_hash "$Hashtype"; }
|
||||||
|
|
||||||
|
|
||||||
# Pass the branch/ref by pipe to git
|
# Pass the branch/ref by pipe to git
|
||||||
safe_git_rev_parse()
|
safe_git_rev_parse()
|
||||||
{
|
{
|
||||||
|
@ -306,19 +311,15 @@ make_new_repo()
|
||||||
echo_info "Setting up new repository"
|
echo_info "Setting up new repository"
|
||||||
PUTREPO "$URL"
|
PUTREPO "$URL"
|
||||||
|
|
||||||
# We need a relatively short ID for URL+REPO
|
# Needed assumption: the same user should have no duplicate Repoid
|
||||||
# The manifest will be stored at pack_hash($urlid_)
|
Repoid=":${Hashtype}:$(genkey 64 | pack_hash)"
|
||||||
# Needed assumption: the same user should have no duplicate urlid_
|
|
||||||
# For now, we arbitrarily use 9 random bytes (72 bits)
|
|
||||||
urlid_=$(genkey 9 | tr '+/' '-_')
|
|
||||||
Repoid=$(xecho_n "$urlid_" | pack_hash)
|
|
||||||
iseq "${NAME#gcrypt::}" "$URL" || {
|
iseq "${NAME#gcrypt::}" "$URL" || {
|
||||||
git config "remote.$NAME.url" "gcrypt::$URL#$urlid_"
|
git config "remote.$NAME.gcrypt-id" "$Repoid"
|
||||||
fix_config=1
|
fix_config=1
|
||||||
}
|
}
|
||||||
echo_info "Repository URL is" "gcrypt::$URL#$urlid_"
|
echo_info "Repository ID is $Repoid"
|
||||||
Extension_list=$(xecho "extn comment")
|
Extension_list=$(xecho "extn comment")
|
||||||
isnull "$fix_config" || echo_info "(configuration for $NAME updated)"
|
#isnull "$fix_config" || echo_info "(configuration for $NAME updated)"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -365,7 +366,7 @@ read_config()
|
||||||
|
|
||||||
ensure_connected()
|
ensure_connected()
|
||||||
{
|
{
|
||||||
local manifest_= rcv_repoid= url_id=
|
local manifest_= rcv_repoid= r_name=
|
||||||
|
|
||||||
if isnonnull "$Did_find_repo"
|
if isnonnull "$Did_find_repo"
|
||||||
then
|
then
|
||||||
|
@ -374,24 +375,36 @@ ensure_connected()
|
||||||
Did_find_repo=no
|
Did_find_repo=no
|
||||||
read_config
|
read_config
|
||||||
|
|
||||||
|
iseq "${NAME#gcrypt::}" "$URL" || r_name=$NAME
|
||||||
|
|
||||||
# Fixup ssh:// -> rsync://
|
# Fixup ssh:// -> rsync://
|
||||||
if isurl ssh "$URL"; then
|
if isurl ssh "$URL"; then
|
||||||
URL="rsync://${URL#ssh://}"
|
URL="rsync://${URL#ssh://}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# split out Repo ID from URL
|
# Find the URL fragment
|
||||||
url_id=${URL##*"#"}
|
Urlfrag=${URL##*"#"}
|
||||||
isnoteq "$url_id" "$URL" || {
|
isnoteq "$Urlfrag" "$URL" || Urlfrag=
|
||||||
url_id=${URL##*/"G."}
|
URL=${URL%"#$Urlfrag"}
|
||||||
isnoteq "$url_id" "$URL" || return 0
|
|
||||||
URL=${URL%/"G.$url_id"}
|
# manifestfile -- sha224 hash if we can, else the default location
|
||||||
|
if isurl sftp "$URL" || islocalrepo "$URL" || isurl rsync "$URL"
|
||||||
|
then
|
||||||
|
# not for gitception
|
||||||
|
isnull "$Urlfrag" || Manifestfile=$(xecho_n "$Urlfrag" | gpg_hash SHA224)
|
||||||
|
fi
|
||||||
|
|
||||||
|
Repoid=
|
||||||
|
isnull "$r_name" || {
|
||||||
|
Repoid=$(git config "remote.$r_name.gcrypt-id" || :)
|
||||||
}
|
}
|
||||||
URL=${URL%"#$url_id"}
|
|
||||||
Repoid=$(xecho_n "$url_id" | pack_hash)
|
|
||||||
|
|
||||||
TmpManifest_Enc="$Localdir/tmp_manifest.$$"
|
TmpManifest_Enc="$Localdir/tmp_manifest.$$"
|
||||||
GET "$URL" "$Repoid" "$TmpManifest_Enc" 2>/dev/null ||
|
GET "$URL" "$Manifestfile" "$TmpManifest_Enc" 2>/dev/null || {
|
||||||
echo_die "Repository not found: $url_id at $URL"
|
echo_info "Repository not found: $URL"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
Did_find_repo=yes
|
Did_find_repo=yes
|
||||||
echo_info "Decrypting manifest"
|
echo_info "Decrypting manifest"
|
||||||
|
@ -401,21 +414,50 @@ ensure_connected()
|
||||||
rm -f "$TmpManifest_Enc"
|
rm -f "$TmpManifest_Enc"
|
||||||
|
|
||||||
Branchlist=$(xecho "$manifest_" | xgrep -E '^[0-9a-f]{40} ')
|
Branchlist=$(xecho "$manifest_" | xgrep -E '^[0-9a-f]{40} ')
|
||||||
Packlist=$(xecho "$manifest_" | xgrep "^$Packpfx")
|
Packlist=$(xecho "$manifest_" | xgrep "^pack ")
|
||||||
Keeplist=$(xecho "$manifest_" | xgrep "^keep")
|
Keeplist=$(xecho "$manifest_" | xgrep "^keep ")
|
||||||
Extension_list=$(xecho "$manifest_" | xgrep "^extn ")
|
Extension_list=$(xecho "$manifest_" | xgrep "^extn ")
|
||||||
rcv_repoid=$(xecho "$manifest_" | xgrep "^repo ")
|
rcv_repoid=$(xecho "$manifest_" | xgrep "^repo ")
|
||||||
iseq "$(repoidstr)" "$rcv_repoid" || echo_die "Repository id mismatch!"
|
|
||||||
|
rcv_repoid=${rcv_repoid#repo }
|
||||||
|
rcv_repoid=${rcv_repoid% *}
|
||||||
|
if isnull "$Repoid"
|
||||||
|
then
|
||||||
|
echo_info "Remote repo ID is $rcv_repoid"
|
||||||
|
Repoid=$rcv_repoid
|
||||||
|
elif isnoteq "$rcv_repoid" "$Repoid"
|
||||||
|
then
|
||||||
|
echo_info "WARNING:"
|
||||||
|
echo_info "WARNING: Remote repository ID has changed!"
|
||||||
|
echo_info "WARNING: to $rcv_repoid"
|
||||||
|
echo_info "WARNING:"
|
||||||
|
Repoid=$rcv_repoid
|
||||||
|
else
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
isnull "$r_name" || {
|
||||||
|
git config "remote.$r_name.gcrypt-id" "$rcv_repoid"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fetch_decrypt_pack()
|
fetch_decrypt_pack()
|
||||||
{
|
{
|
||||||
local key_= rcv_id=
|
local key_= rcv_id= htype_= pack_= hfunc_=
|
||||||
GET "$URL" "$1" "$TmpPack_Encrypted" &&
|
splitcolon "${1#pack :}"
|
||||||
rcv_id=$(pack_hash < "$TmpPack_Encrypted") &&
|
htype_=$prefix_
|
||||||
iseq "$rcv_id" "$1" ||
|
pack_=$suffix_
|
||||||
echo_die "Packfile $1 does not match digest!"
|
|
||||||
key_=$(xecho "$Packlist" | grep "$1" | cut -f 3 -d ' ')
|
if isnoteq "$htype_" SHA256 && isnoteq "$htype_" SHA224 &&
|
||||||
|
isnoteq "$htype_" SHA384 && isnoteq "$htype_" SHA512
|
||||||
|
then
|
||||||
|
echo_die "Packline malformed: $1"
|
||||||
|
fi
|
||||||
|
GET "$URL" "$pack_" "$TmpPack_Encrypted" &&
|
||||||
|
rcv_id=$(gpg_hash "$htype_" < "$TmpPack_Encrypted") &&
|
||||||
|
iseq "$rcv_id" "$pack_" ||
|
||||||
|
echo_die "Packfile $pack_ does not match digest!"
|
||||||
|
key_=$(xecho "$Packlist" | grep "$pack_" | cut -f 3 -d ' ')
|
||||||
DECRYPT "$key_" < "$TmpPack_Encrypted"
|
DECRYPT "$key_" < "$TmpPack_Encrypted"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -462,8 +504,8 @@ repack_if_needed()
|
||||||
then
|
then
|
||||||
continue
|
continue
|
||||||
fi
|
fi
|
||||||
pack_=${packline_#"$Packpfx"}
|
pack_=${packline_#$Packpat}
|
||||||
fetch_decrypt_pack "$pack_" |
|
fetch_decrypt_pack "$packline_" |
|
||||||
git index-pack -v --stdin "$Localdir/pack/${pack_}.pack" >/dev/null
|
git index-pack -v --stdin "$Localdir/pack/${pack_}.pack" >/dev/null
|
||||||
done
|
done
|
||||||
key_=$(genkey "$Packkey_bytes")
|
key_=$(genkey "$Packkey_bytes")
|
||||||
|
@ -483,8 +525,8 @@ repack_if_needed()
|
||||||
fi
|
fi
|
||||||
|
|
||||||
pack_id=$(pack_hash < "$TmpPack_Encrypted")
|
pack_id=$(pack_hash < "$TmpPack_Encrypted")
|
||||||
Packlist=$(append "$Packlist" "$Packpfx$pack_id $key_")
|
Packlist=$(append "$Packlist" "pack :${Hashtype}:$pack_id $key_")
|
||||||
Keeplist=$(append "$Keeplist" "$Keeppfx$pack_id 1")
|
Keeplist=$(append "$Keeplist" "keep :${Hashtype}:$pack_id 1")
|
||||||
rm -r -f "$Localdir/pack"
|
rm -r -f "$Localdir/pack"
|
||||||
did_repack=yes
|
did_repack=yes
|
||||||
}
|
}
|
||||||
|
@ -542,11 +584,10 @@ do_fetch()
|
||||||
xecho "$pneed_" | while read packline_
|
xecho "$pneed_" | while read packline_
|
||||||
do
|
do
|
||||||
isnonnull "$packline_" || continue
|
isnonnull "$packline_" || continue
|
||||||
pack_=${packline_#"$Packpfx"}
|
fetch_decrypt_pack "$packline_" |
|
||||||
fetch_decrypt_pack "$pack_" |
|
|
||||||
git index-pack -v --stdin >/dev/null
|
git index-pack -v --stdin >/dev/null
|
||||||
# add to local pack list
|
# add to local pack list
|
||||||
xecho "$Packpfx$pack_" >> "$Localdir/have_packs$GITCEPTION"
|
xecho "${packline_}" >> "$Localdir/have_packs$GITCEPTION"
|
||||||
done
|
done
|
||||||
|
|
||||||
rm -f "$TmpPack_Encrypted"
|
rm -f "$TmpPack_Encrypted"
|
||||||
|
@ -614,7 +655,7 @@ EOF
|
||||||
|
|
||||||
if isnoteq "$did_repack" yes
|
if isnoteq "$did_repack" yes
|
||||||
then
|
then
|
||||||
Packlist=$(append "$Packlist" "$Packpfx$pack_id $key_")
|
Packlist=$(append "$Packlist" "pack :${Hashtype}:$pack_id $key_")
|
||||||
fi
|
fi
|
||||||
# else, repack rewrote Packlist
|
# else, repack rewrote Packlist
|
||||||
|
|
||||||
|
@ -639,14 +680,14 @@ EOF
|
||||||
rm -f "$TmpObjlist"
|
rm -f "$TmpObjlist"
|
||||||
|
|
||||||
# Upload manifest
|
# Upload manifest
|
||||||
PUT "$URL" "$Repoid" "$TmpManifest_Enc"
|
PUT "$URL" "$Manifestfile" "$TmpManifest_Enc"
|
||||||
|
|
||||||
# Delete packs
|
# Delete packs
|
||||||
if isnonnull "$Packlist_delete"; then
|
if isnonnull "$Packlist_delete"; then
|
||||||
REMOVE "$URL" "$(xecho "$Packlist_delete" | while read packline_
|
REMOVE "$URL" "$(xecho "$Packlist_delete" | while read packline_
|
||||||
do
|
do
|
||||||
isnonnull "$packline_" || continue
|
isnonnull "$packline_" || continue
|
||||||
pack_=${packline_#"$Packpfx"}
|
pack_=${packline_#$Packpat}
|
||||||
xecho "$pack_"
|
xecho "$pack_"
|
||||||
done)"
|
done)"
|
||||||
fi
|
fi
|
||||||
|
|
Loading…
Reference in a new issue