[pacman-dev] [PATCH v2] Extend database upgrade script to handle alpm db version 9
Signed-off-by: Allan McRae
On 18/09/13 18:59, Allan McRae wrote:
Signed-off-by: Allan McRae
--- v2: Use awk to do symlink to actual directory replacement
Any comments on this before it gets committed?
As an example of what changes get made:
diff -Naur local.tmp/glibc-2.18-4/files local/glibc-2.18-4/files --- local.tmp/glibc-2.18-4/files 2013-09-18 18:45:35.747068765 +1000 +++ local/glibc-2.18-4/files 2013-09-18 18:51:29.633643945 +1000 @@ -4,8 +4,8 @@ etc/locale.gen etc/nscd.conf etc/rpc -lib/ -lib/libc.a +usr/lib/ +usr/lib/libc.a usr/ usr/bin/ usr/bin/catchsegv
Note that now usr/lib/ is present twice in the file (may not always be the case) and these entries are out of alphabetical order. Both these are cosmetic to pacman.
scripts/pacman-db-upgrade.sh.in | 59 ++++++++++++++++++++++++++++++++++------- 1 file changed, 49 insertions(+), 10 deletions(-)
diff --git a/scripts/pacman-db-upgrade.sh.in b/scripts/pacman-db-upgrade.sh.in index a1630c5..d6f4169 100644 --- a/scripts/pacman-db-upgrade.sh.in +++ b/scripts/pacman-db-upgrade.sh.in @@ -109,18 +109,57 @@ fi # do not let pacman run while we do this touch "$lockfile"
-# pacman-3.4 to 3.5 upgrade - merge depends into desc -if [[ $(find "$dbroot"/local -name depends) ]]; then - msg "$(gettext "Pre-3.5 database format detected - upgrading...")" - for i in "$dbroot"/local/*; do - if [[ -f "$i"/depends ]]; then - cat "$i"/depends >> "$i"/desc - rm "$i"/depends - fi - done - msg "$(gettext "Done.")" +if [[ -f "${dbroot}"/local/.alpm_db_version ]]; then + db_version=$(cat "${dbroot}"/local/.alpm_db_version) fi
+if [[ -z "$db_version" ]]; then + # pacman-3.4 to 3.5 upgrade - merge depends into desc + if [[ $(find "$dbroot"/local -name depends) ]]; then + msg "$(gettext "Pre-3.5 database format detected - upgrading...")" + for i in "$dbroot"/local/*; do + if [[ -f "$i"/depends ]]; then + cat "$i"/depends >> "$i"/desc + rm "$i"/depends + fi + done + msg "$(gettext "Done.")" + fi + + # pacman 4.1 to 4.2 upgrade - remove directory symlink support + dirlist=() + + unset GREP_OPTIONS + while IFS= read -r dir; do + dirlist+=("/${dir%/}") + done < <(grep -h '/$' "$dbroot"/local/*/files | sort -u) + + mapfile -t dirlist < <(find "${dirlist[@]}" -maxdepth 0 -type l) + + if [[ ${#dirlist[@]} != 0 ]]; then + msg "$(gettext "Pre-4.2 database format detected - upgrading...")" + for dir in "${dirlist[@]}"; do + realdir="$(cd $dir; pwd -P)" + for f in "$dbroot"/local/*/files; do + awk -v "dir=${dir#/}/" -v "realdir=${realdir#/}/" ' + BEGIN { + i = length(dir) + 1 + } + { + if (index($0, dir) == 1) { + printf("%s%s\n", realdir, substr($0, i)) + } else { + print + } + }' $f > $f.tmp + mv $f.tmp $f + done + done + fi +fi + +echo "9" > "$dbroot"/local/.alpm_db_version + # remove the lock file rm -f "$lockfile"
On 09/18/13 at 06:59pm, Allan McRae wrote:
Signed-off-by: Allan McRae
--- v2: Use awk to do symlink to actual directory replacement
As an example of what changes get made:
diff -Naur local.tmp/glibc-2.18-4/files local/glibc-2.18-4/files --- local.tmp/glibc-2.18-4/files 2013-09-18 18:45:35.747068765 +1000 +++ local/glibc-2.18-4/files 2013-09-18 18:51:29.633643945 +1000 @@ -4,8 +4,8 @@ etc/locale.gen etc/nscd.conf etc/rpc -lib/ -lib/libc.a +usr/lib/ +usr/lib/libc.a usr/ usr/bin/ usr/bin/catchsegv
Note that now usr/lib/ is present twice in the file (may not always be the case) and these entries are out of alphabetical order. Both these are cosmetic to pacman.
scripts/pacman-db-upgrade.sh.in | 59 ++++++++++++++++++++++++++++++++++------- 1 file changed, 49 insertions(+), 10 deletions(-)
diff --git a/scripts/pacman-db-upgrade.sh.in b/scripts/pacman-db-upgrade.sh.in index a1630c5..d6f4169 100644 --- a/scripts/pacman-db-upgrade.sh.in +++ b/scripts/pacman-db-upgrade.sh.in @@ -109,18 +109,57 @@ fi # do not let pacman run while we do this touch "$lockfile"
-# pacman-3.4 to 3.5 upgrade - merge depends into desc -if [[ $(find "$dbroot"/local -name depends) ]]; then - msg "$(gettext "Pre-3.5 database format detected - upgrading...")" - for i in "$dbroot"/local/*; do - if [[ -f "$i"/depends ]]; then - cat "$i"/depends >> "$i"/desc - rm "$i"/depends - fi - done - msg "$(gettext "Done.")" +if [[ -f "${dbroot}"/local/.alpm_db_version ]]; then + db_version=$(cat "${dbroot}"/local/.alpm_db_version) fi
+if [[ -z "$db_version" ]]; then + # pacman-3.4 to 3.5 upgrade - merge depends into desc + if [[ $(find "$dbroot"/local -name depends) ]]; then + msg "$(gettext "Pre-3.5 database format detected - upgrading...")" + for i in "$dbroot"/local/*; do + if [[ -f "$i"/depends ]]; then + cat "$i"/depends >> "$i"/desc + rm "$i"/depends + fi + done + msg "$(gettext "Done.")" + fi + + # pacman 4.1 to 4.2 upgrade - remove directory symlink support + dirlist=() + + unset GREP_OPTIONS + while IFS= read -r dir; do + dirlist+=("/${dir%/}") + done < <(grep -h '/$' "$dbroot"/local/*/files | sort -u)
This sort needs to be reversed. If a path contains multiple symlinks, the longer one needs to come first, otherwise awk will replace the shorter symlink and miss the longer one. For example: symlink1/ symlink1/symlink2/ ...
+ + mapfile -t dirlist < <(find "${dirlist[@]}" -maxdepth 0 -type l) + + if [[ ${#dirlist[@]} != 0 ]]; then + msg "$(gettext "Pre-4.2 database format detected - upgrading...")" + for dir in "${dirlist[@]}"; do + realdir="$(cd $dir; pwd -P)" + for f in "$dbroot"/local/*/files; do + awk -v "dir=${dir#/}/" -v "realdir=${realdir#/}/" ' + BEGIN { + i = length(dir) + 1 + } + { + if (index($0, dir) == 1) { + printf("%s%s\n", realdir, substr($0, i)) + } else { + print + } + }' $f > $f.tmp + mv $f.tmp $f
$f will include $dbroot, so it needs to be quoted as well. Depending on the file list, this may leave out parent directories of $realdir or leave behind parent directories of $dir that are no longer relevant. For example: path/ path/to/ path/to/symlink/ ... gives: path/ path/to/ other/path/to/realdir/ ... I don't think the leftover directories will hurt anything, but the missing parent directories will break -Qo.
+ done + done + fi +fi + +echo "9" > "$dbroot"/local/.alpm_db_version + # remove the lock file rm -f "$lockfile"
-- 1.8.4
Original-work-by: Allan McRae
On 03/08/14 15:31, Andrew Gregory wrote:
Original-work-by: Allan McRae
Signed-off-by: Andrew Gregory --- Changes: * set alternate root with PACROOT environment variable * filter results through grep and sort to remove empty lines and duplicates * add parent directories (duplicates filtered out by sort) * verify that the resolved directory is inside our root * replace longer symlinks first * handle conflict between resolved directory and existing filelist entry
This looks fine on first pass to me. Why do you think this is a WIP? Is there something more needing done? A
On 08/04/14 at 03:43pm, Allan McRae wrote:
On 03/08/14 15:31, Andrew Gregory wrote:
Original-work-by: Allan McRae
Signed-off-by: Andrew Gregory --- Changes: * set alternate root with PACROOT environment variable * filter results through grep and sort to remove empty lines and duplicates * add parent directories (duplicates filtered out by sort) * verify that the resolved directory is inside our root * replace longer symlinks first * handle conflict between resolved directory and existing filelist entry
This looks fine on first pass to me. Why do you think this is a WIP? Is there something more needing done?
A
I think it's substantively ready but my bash and awk are not particularly great and probably in need of some cleanup and the PACROOT variable needs a little work. It at least needs to be documented, and I'm considering making it an option rather than an environment variable and making the default dbpath relative to it. apg
On 03/08/14 15:31, Andrew Gregory wrote:
Original-work-by: Allan McRae
Signed-off-by: Andrew Gregory ---
Big thanks for doing this!
Changes: * set alternate root with PACROOT environment variable
As you have pointed out, that needs to be a -r/--root flag and not an environmental variable
* filter results through grep and sort to remove empty lines and duplicates * add parent directories (duplicates filtered out by sort) * verify that the resolved directory is inside our root * replace longer symlinks first * handle conflict between resolved directory and existing filelist entry
scripts/pacman-db-upgrade.sh.in | 92 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 82 insertions(+), 10 deletions(-)
diff --git a/scripts/pacman-db-upgrade.sh.in b/scripts/pacman-db-upgrade.sh.in index b0b0ac8..7ea4077 100644 --- a/scripts/pacman-db-upgrade.sh.in +++ b/scripts/pacman-db-upgrade.sh.in @@ -25,9 +25,17 @@ export TEXTDOMAINDIR='@localedir@'
declare -r myver='@PACKAGE_VERSION@'
+resolve_dir() { + local d="$(cd "$1"; pwd -P)" + [[ $d == */ ]] || d+=/ + printf "%s" "$d" +} +
OK
eval $(awk '/DBPath/ {print $1$2$3}' @sysconfdir@/pacman.conf) dbroot="${DBPath:-@localstatedir@/lib/pacman/}"
+pacroot="$(resolve_dir "${PACROOT:-/}")" + USE_COLOR='y'
m4_include(library/output_format.sh) @@ -112,18 +120,82 @@ fi # do not let pacman run while we do this touch "$lockfile"
-# pacman-3.4 to 3.5 upgrade - merge depends into desc -if [[ $(find "$dbroot"/local -name depends) ]]; then - msg "$(gettext "Pre-3.5 database format detected - upgrading...")" - for i in "$dbroot"/local/*; do - if [[ -f "$i"/depends ]]; then - cat "$i"/depends >> "$i"/desc - rm "$i"/depends - fi - done - msg "$(gettext "Done.")" +if [[ -f "${dbroot}"/local/.alpm_db_version ]]; then + db_version=$(cat "${dbroot}"/local/.alpm_db_version) fi
OK.
+if [[ -z "$db_version" ]]; then + # pacman-3.4 to 3.5 upgrade - merge depends into desc + if [[ $(find "$dbroot"/local -name depends) ]]; then + msg "$(gettext "Pre-3.5 database format detected - upgrading...")" + for i in "$dbroot"/local/*; do + if [[ -f "$i"/depends ]]; then + cat "$i"/depends >> "$i"/desc + rm "$i"/depends + fi + done + msg "$(gettext "Done.")" + fi +
OK
+ # pacman 4.1 to 4.2 upgrade - remove directory symlink support + dirlist=() + + unset GREP_OPTIONS + while IFS= read -r dir; do + dirlist+=("${pacroot}${dir%/}") + done < <(grep -h '/$' "$dbroot"/local/*/files | sort -ru) +
OK
+ mapfile -t dirlist < <(find "${dirlist[@]}" -maxdepth 0 -type l) +
OK
+ if [[ ${#dirlist[@]} != 0 ]]; then
if (( ${#dirlist[@]} ))
+ msg "$(gettext "Pre-4.2 database format detected - upgrading...")" + for dir in "${dirlist[@]}"; do + realdir="$(resolve_dir "$(cd $dir; pwd -P)")" + + # verify realdir is inside root + if [[ ${realdir:0:${#pacroot}} != $pacroot ]]; then + warning "$(gettext "symlink '%s' points outside pacman root, manual repair required")" "$dir" + continue + fi
OK.
+ + # convert to an appropriate form for the replacement + olddir="${dir:${#pacroot}}/" + newdir="${realdir:${#pacroot}}" +
OK
+ # construct the parents of the new directory + parents="" + parent="$(dirname "$newdir")"
use bash: parent=${newdir%/*}
+ while [[ $parent != "." ]]; do
Should that be != '/' ? $ dirname /usr / Anyway, using the bash above this becomes while [[ -n "$parent" ]]; do
+ parents+="$parent/\n" + parent="$(dirname "$parent")" + done +
My awk is suboptimal... So I'll comment my understanding as I go...
+ for f in "$dbroot"/local/*/files; do + awk -v "olddir=$olddir" -v "newdir=$newdir" -v "parents=$parents" '
"parents=${parents[@]}"
+ BEGIN { + i = length(olddir) + 1 + file = substr(newdir, 0, length(newdir) - 1) + } + { + if ($0 == olddir) { + printf("%s", parents) + printf("%s\n", newdir)
OK (replacing old dir with newdir and its parents)
+ } else if ($0 == file) { + # skip
I do not understand what this bit is achieving!
+ } else if (index($0, olddir) == 1) { + printf("%s%s\n", newdir, substr($0, i))
OK ("moving" file in olddir to "newdir")
+ } else { + print
OK (not touching files outside of olddir)
+ } + }' "$f" | grep . | LC_ALL=C sort -u > "$f.tmp"
Why the grep?
+ mv "$f.tmp" "$f" + done + done + fi +fi + +echo "9" > "$dbroot"/local/.alpm_db_version +
OK
# remove the lock file rm -f "$lockfile"
On 08/10/14 at 01:16pm, Allan McRae wrote:
On 03/08/14 15:31, Andrew Gregory wrote:
Original-work-by: Allan McRae
Signed-off-by: Andrew Gregory --- Big thanks for doing this!
Changes: * set alternate root with PACROOT environment variable
As you have pointed out, that needs to be a -r/--root flag and not an environmental variable
* filter results through grep and sort to remove empty lines and duplicates * add parent directories (duplicates filtered out by sort) * verify that the resolved directory is inside our root * replace longer symlinks first * handle conflict between resolved directory and existing filelist entry
scripts/pacman-db-upgrade.sh.in | 92 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 82 insertions(+), 10 deletions(-)
diff --git a/scripts/pacman-db-upgrade.sh.in b/scripts/pacman-db-upgrade.sh.in index b0b0ac8..7ea4077 100644 --- a/scripts/pacman-db-upgrade.sh.in +++ b/scripts/pacman-db-upgrade.sh.in
<snip>
+ # construct the parents of the new directory + parents="" + parent="$(dirname "$newdir")"
use bash: parent=${newdir%/*}
+ while [[ $parent != "." ]]; do
Should that be != '/' ?
$ dirname /usr /
Anyway, using the bash above this becomes while [[ -n "$parent" ]]; do
This won't work. $newdir has a trailing slash that would need to be removed and it's relative to $pacroot, so ${parent%/*} would eventually result in the base directory, not an empty string (which is why dirname eventually gives '.'). If the symlink points to $pacroot, $newdir could even be empty. My attempts to account for all of that in pure bash have been overly convoluted, but I'm open to suggestions.
+ parents+="$parent/\n" + parent="$(dirname "$parent")" + done +
My awk is suboptimal... So I'll comment my understanding as I go...
+ for f in "$dbroot"/local/*/files; do + awk -v "olddir=$olddir" -v "newdir=$newdir" -v "parents=$parents" '
"parents=${parents[@]}"
$parents is actually a string rather than an array because I don't think there's a way to pass an array to awk without mangling something.
+ BEGIN { + i = length(olddir) + 1 + file = substr(newdir, 0, length(newdir) - 1) + } + { + if ($0 == olddir) { + printf("%s", parents) + printf("%s\n", newdir)
OK (replacing old dir with newdir and its parents)
+ } else if ($0 == file) { + # skip
I do not understand what this bit is achieving!
If the newdir already exists in the file list as a file (i.e. somebody replaced a symlink with a directory) this will prevent a duplicate entry. I'll expand that comment so it's clearer.
+ } else if (index($0, olddir) == 1) { + printf("%s%s\n", newdir, substr($0, i))
OK ("moving" file in olddir to "newdir")
+ } else { + print
OK (not touching files outside of olddir)
+ } + }' "$f" | grep . | LC_ALL=C sort -u > "$f.tmp"
Why the grep?
Removes blank lines in case $newdir has no parents or equals $pacroot.
+ mv "$f.tmp" "$f" + done + done + fi +fi + +echo "9" > "$dbroot"/local/.alpm_db_version +
OK
# remove the lock file rm -f "$lockfile"
* convert dbpath from argument to option
* add --config and --root options
* read dbpath and root from config file
* if root is set but not dbpath, dbpath is set relative to root
Signed-off-by: Andrew Gregory
Original-work-by: Allan McRae
participants (2)
-
Allan McRae
-
Andrew Gregory