declobber  - a tool for extension rebuilding


Forum: DSL Tips and Tricks
Topic: declobber  - a tool for extension rebuilding
started by: WDef

Posted by WDef on Feb. 03 2007,21:54
Robert has complained that people are sending the occasional badly-made .dsl extension to the repo containing one or more system-clobbering directories.  (An unsubtle hypothetical example might be eg /usr/local/bin, which would wipe out your entire /usr/local/bin tree when the extension is installed).

I suspect these often result from leaving out the --no-recursion tar option when repacking an extension, or from not appropriately trimming a file list for feeding to tar with -T.

Anyway ... I've been using this tool for some time to strip clobber dirs from extensions (similarities to dsl2unc).

Try running it on some of the larger .dsl extensions in the repo - you might get a surprise!

Code Sample

#!/bin/bash

# declobber v0.1  by WDef
# Removes clobber dirs from .dsl extensions

# Just do ./declobber <extension1.dsl extension2.dsl ..>
# Output is extension1_noclobber.dsl etc in the WORK dir

# For livecd use with legacy boot.


#==========================// USER SETTING//===============================

# Put DIR on a hard drive linux partition if insufficient room in /ramdisk/home/dsl
DIR=/home/dsl

#==========================// FNS //========================================

dir_filter(){
# Filter out non-empty dir lines from stdin
while read LM; do
if [ -d "${WORK}/${LM}" ]; then
if [ $(ls -1A ${WORK}/${LM}| wc -l) -eq 0 ]; then
# empty dir - check not already on base system
if [ -e "/KNOPPIX/${LM}" ]; then
# this is a clobberring dir !
echo ${LM} >>${clobber_list}
else
echo ${LM} # OK
fi
else
# this is a clobberring dir !
echo "${LM}" >>${clobber_list}
fi
else
# files, symlinks - ok
echo ${LM}
fi
done
}


#=========================// MAIN //=======================================


. /etc/init.d/dsl-functions # for ANSI colors


if [ -e /etc/sysconfig/unionfs -o ! -e /KNOPPIX/bin/ash ]; then
echo "${RED}declobber is for livecd use with legacy boot option only.${NORMAL}"
exit 1
fi

if [ $EUID -ne 0 ]; then
while true; do
echo "${WHITE}You need to be root to conserve all permissions."
echo -n "Quit? (y/n)${NORMAL} "
read
case $REPLY in
y|y*|Y|Y*) exit 1;;
n|n*|N|N*) break;;
*) echo "Invalid response";;
esac
done
fi


results=/tmp/declobber_results


rm -f ${results} # preclean

CURRENTD="${PWD}"
APP=""


for APP in $*; do

# If no path supplied, assume current working dir

if echo ${APP} | grep -q -v '^/'; then APP="${CURRENTD}/${APP}"; fi

if [ ! -e "${APP}" ]; then
echo "${RED}Can't find ${APP}${NORMAL}" >>${results}
continue
fi

if [ ${APP##*.} != dsl ]; then echo "${RED}${APP} is not a .dsl extension.${NORMAL}" >>${results}; continue; fi

NAME=$(basename ${APP} .dsl)
WORK=${DIR}/$NAME
LIST_IN=${WORK}/list_in
LIST_OUT=${WORK}/list_out
clobber_list=${WORK}/clobber_list

if [ -e "${WORK}" ]; then echo "${RED}${WORK} already exists.${NORMAL}" >>${results}; continue; fi

mkdir ${WORK}
touch "${clobber_list}" # zero count initialised

echo
echo "${WHITE}Removing any nasty clobbering dirs from ${YELLOW}$NAME.dsl ..${NORMAL}"
echo
sleep 1 # comment this out if you're in a hurry

cd ${WORK}
tar -zxpvf $APP
echo -n "${WHITE}Working ..${NORMAL} "
tar -ztf $APP | sort | uniq -u | tee ${LIST_IN} | dir_filter >${LIST_OUT} &
rotdash $!

NEWNAME=${NAME}_noclobber.dsl
tar -T ${LIST_OUT} --no-recursion --numeric-owner -cpf- | gzip -9 >"${NEWNAME}" &
rotdash $!
if [ ! -s "${NEWNAME}" ]; then echo "${RED}Problem making $NAME_noclobber.dsl" >>${results}; continue; fi

# Debug - check that clobbers removed = difference between in and out
DIFF=$(cat ${LIST_IN} ${LIST_OUT} | sort | uniq -u | cat - ${clobber_list} 2>/dev/null | sort | uniq -u | wc -l)
if [ $DIFF -ne 0 ]; then echo "Error. Removed dirs and difference between in/out unequal, exiting .."; exit 1; fi

echo "${GREEN}Now test your new ${YELLOW}${NEWNAME}!${NORMAL}"

CDRS="$(cat ${clobber_list} 2>/dev/null |wc -l)"
CLOBBERS=${CDRS##*[ ]}
echo "Removed ${YELLOW}$CLOBBERS${NORMAL} clobber dir(s) from ${YELLOW}$NAME.dsl${WHITE} --> ${GREEN}${NEWNAME}${NORMAL}" >>${results}
echo "( These are listed in $clobber_list )" >>${results}
cd ${CURRENTD}
done


echo
echo "=========================================================================="
echo
echo "${WHITE}DECLOBBER ${WHITE}RESULTS:${NORMAL}"
echo
cat ${results}
echo
echo "=========================================================================="
echo "${GREEN}Finished.${NORMAL}"

exit 0

Posted by roberts on Feb. 03 2007,23:04
Thanks WDef,

I will add this to the tools section in v3.3RC1

Posted by WDef on Feb. 06 2007,12:43
It's occurred to me that the requirement to run only as livecd legacy boot is probably overkill and only really matters when the extension contains an empty dir that might be on the system.  Also, if other extensions happen to be installed on the system, well, we don't want to clobber those either.

So I might post another version giving this as a warning only when it is required.

Posted by WDef on Feb. 08 2007,00:51
Unsightly bug:

Code Sample
tar -ztf $APP | sort | uniq -u | tee ${LIST_IN} | dir_filter >${LIST_OUT} &


Should read:

Code Sample
find -name \* | sort | uniq -u | tee ${LIST_IN} | dir_filter >${LIST_OUT} &


Apologies.  Cleaned-up version coming.

EDIT: Can't reproduce the alleged-four-in-the-morning bug. Ignoring.

Posted by WDef on Feb. 09 2007,15:26
Code Sample

#!/bin/bash

# declobber v0.2 by  wdef  Feb 9 2007
# Removes clobber dirs from .dsl extensions


# Changes  v0.2
# 1. If used on livecd legacy boot or equivalent, can differentiate between isolated empty clobber dir
# that also occurs on base system and one that is non-base (ie added by user or other extension).
# This is noted in the results for information.  However, isolated empty dirs are infrequent in
# extensions.
# 2. Help and message related to (1) added.
# 3. Added checks for work dir
# 4. Minor changes to output.


#==========================// USER SETTING//===============================

# Put DIR on a hard drive linux partition if insufficient room in /ramdisk/home/dsl
DIR=/home/dsl

#==========================// FNS //========================================

help(){
cat <<"EOF1"
declobber v0.2 - removes clobber directories from damnsmalllinux extensions
by wdef
Usage: declobber <ext1.dsl ext2.dsl ..>
Run as root to preserve all permissions.
Creates a work dir in $DIR
Declobbered extension is output in $DIR
Default DIR=/home/dsl
EOF1
exit
}

check_work_dir(){
# Check work directory looks ok
if [ -z "$DIR" ]; then "${RED}User setting work dir is unset.${NORMAL}"; exit 1; fi
case ${DIR} in
/mnt/*) D=$(echo ${DIR} | awk -F/ '{print "/" $2 "/" $3 }' )
FS=$(grep ${D} /proc/mounts | awk '{print $3}')
if [ -z "$FS" ]; then echo "${RED}$D not mounted.${NORMAL}"; exit 1; fi
case $FS in
ext*|*eiser*);;
*fat*|*dos*) echo "${RED}$DIR on $FS can't preserve perms - use linux filesystem.${NORMAL}"; exit 1;;
ntfs) echo "${RED}Writing to ntfs is not a good idea ${NORMAL}"; exit 1;;
esac;;
/*|/ramdisk/*);;
*) echo "${RED}Err .. where exactly _is_ this $DIR anyway?"${NORMAL}; exit 1;;
esac
if [ ! -d "${DIR}" ] || [ ! -w "${DIR}" ]; then
echo "${RED}$DIR not a dir or not writeable.${NORMAL}"; exit 1
fi
}

not_legacy_live_msg(){
cat <<"EOF2"

This is not a legacy livecd boot.
If your extension contains isolated empty directories that are also found
on the system, declobber cannot be certain if these are clobberring base
system directories or directories added by user/other extensions, or both.
I will note any such dirs in the results.
EOF2
}


checkon_system(){
# Remove empty dirs if already on system
# Make note in results of empty dirs that appear to clobber non-base dirs (eg added by other extensions)
# or if declobber can't tell.

if $LEGCD; then
if [ -e "/${LM}" ]; then
if [ ! -e "/KNOPPIX/${LM}" ]; then
echo ${LM} >>${clobber_list}
echo "Removing empty dir /${LM} - clobbers non-base added directory" >>${results}
else
# this *is* a system clobberring dir !
echo ${LM} >>${clobber_list}
fi
else
echo ${LM} # OK
fi
else
if [ -e "/${LM}" ]; then
echo "Removing empty dir /${LM} - clobbers either base or added directory" >>${results}
echo ${LM} >>${clobber_list}
else
echo ${LM} # OK
fi
fi
}



dir_filter(){
# Filter out non-empty dir lines from stdin
while read LM; do
if [ -d "${WORK}/${LM}" ]; then
if [ $(ls -1A ${WORK}/${LM}| wc -l) -eq 0 ]; then
checkon_system
else
# this *is* a self and/or system clobberring dir !
echo "${LM}" >>${clobber_list}
fi
else
# files, symlinks - ok
echo ${LM}
fi
done
}


#=========================// MAIN //=======================================


. /etc/init.d/dsl-functions # for ANSI colors


if [ "${1}" = -h ] || [ "${1}" = --help ] || [ $# -eq 0 ]; then help; fi

check_work_dir


if [ -e /etc/sysconfig/unionfs -o ! -e /KNOPPIX/bin/ash ]; then
LEGCD=false;
not_legacy_live_msg
else
LEGCD=true;
fi


if [ $EUID -ne 0 ]; then
while true; do
echo "${WHITE}You need to be root to conserve all permissions."
echo -n "Quit? (y/n)${NORMAL} "
read
case $REPLY in
y|y*|Y|Y*) exit 1;;
n|n*|N|N*) break;;
*) echo "Invalid response";;
esac
done
fi


results=/tmp/declobber_results


rm -f ${results} # preclean

CURRENTD="${PWD}"
APP=""


for APP in $*; do

echo ".........................................................................."
echo >>${results}
# If no path supplied, assume current working dir

if echo ${APP} | grep -q -v '^/'; then APP="${CURRENTD}/${APP}"; fi

if [ ! -e "${APP}" ]; then
echo "${RED}Can't find ${APP}${NORMAL}" >>${results}
continue
fi

if [ ${APP##*.} != dsl ]; then echo "${RED}${APP} is not a .dsl extension.${NORMAL}" >>${results}; continue; fi

NAME=$(basename ${APP} .dsl)
WORK=${DIR}/$NAME
LIST_IN=${WORK}/list_in
LIST_OUT=${WORK}/list_out
clobber_list=${WORK}/clobber_list

if [ -e "${WORK}" ]; then echo "${RED}${WORK} already exists.${NORMAL}" >>${results}; continue; fi

mkdir ${WORK}


echo
echo "${WHITE}Removing any nasty clobbering dirs from ${YELLOW}$NAME.dsl ..${NORMAL}"
echo
sleep 1

cd ${WORK}
touch "${clobber_list}" # zero count initialised
tar -zxpvf ${APP}
echo -n "${WHITE}Working ..${NORMAL} "

tar -ztf ${APP} | sort | uniq -u | tee ${LIST_IN} | dir_filter >${LIST_OUT} &
rotdash $!

NEWNAME=${NAME}_noclobber.dsl
tar -T ${LIST_OUT} --no-recursion --numeric-owner -cpf- | gzip -9 >"${NEWNAME}" &
rotdash $!
if [ ! -s "${NEWNAME}" ]; then echo "${RED}Problem making $NAME_noclobber.dsl" >>${results}; continue; fi

# Debug - check that clobbers removed = difference between in and out
DIFF=$(cat ${LIST_IN} ${LIST_OUT} | sort | uniq -u | cat - ${clobber_list} 2>/dev/null | sort | uniq -u | wc -l)
if [ $DIFF -ne 0 ]; then echo "Error. Removed dirs and difference between in/out unequal, exiting .."; exit 1; fi

echo "${GREEN}Now test your new ${YELLOW}${NEWNAME}!${NORMAL}"

CDRS="$(cat ${clobber_list} 2>/dev/null |wc -l)"
CLOBBERS=${CDRS##*[ ]}
echo "Removed ${YELLOW}$CLOBBERS${NORMAL} clobber dir(s) from ${YELLOW}$NAME.dsl${WHITE} --> ${GREEN}${NEWNAME}${NORMAL}" >>${results}
echo "See ${clobber_list}" >>${results}
cd ${CURRENTD}
done


echo
echo "=========================================================================="
echo
echo "${WHITE}DECLOBBER ${WHITE}RESULTS:${NORMAL}"
echo
cat ${results}
echo
echo "=========================================================================="
echo "${GREEN}Finished.${NORMAL}"

exit 0

Posted by WDef on Feb. 10 2007,00:29
Wouldn't make any difference other than for a botched extension containing multiple entries having the same filename and path (possible with tar), but uniq -u should be uniq in the pipe feeding dir_filter:

Code Sample

#!/bin/bash

# declobber v0.2.1 by  wdef  Feb 10 2007
# Removes clobber dirs from .dsl extensions

# Changes v0.2.1
# uniq -u -> uniq in pipe feeding dir_filter

# Changes  v0.2
# 1. If used on livecd legacy boot or equivalent, can differentiate between isolated empty clobber dir
# that also occurs on base system and one that is non-base (ie added by user or other extension).
# This is noted in the results for information.  However, isolated empty dirs are infrequent in
# extensions.
# 2. Help and message related to (1) added.
# 3. Added checks for work dir
# 4. Minor changes to output.


#==========================// USER SETTING//===============================

# Put DIR on a hard drive linux partition if insufficient room in /ramdisk/home/dsl
DIR=/home/dsl

#==========================// FNS //========================================

help(){
cat <<"EOF1"
declobber v0.2.1 - removes clobber directories from damnsmalllinux extensions
by wdef
Usage: declobber <ext1.dsl ext2.dsl ..>
Run as root to preserve all permissions.
Creates a work dir in $DIR
Declobbered extension is output in $DIR
Default DIR=/home/dsl
EOF1
exit
}

check_work_dir(){
# Check work directory looks ok
if [ -z "$DIR" ]; then "${RED}User setting work dir is unset.${NORMAL}"; exit 1; fi
case ${DIR} in
/mnt/*) D=$(echo ${DIR} | awk -F/ '{print "/" $2 "/" $3 }' )
FS=$(grep ${D} /proc/mounts | awk '{print $3}')
if [ -z "$FS" ]; then echo "${RED}$D not mounted.${NORMAL}"; exit 1; fi
case $FS in
ext*|*eiser*);;
*fat*|*dos*) echo "${RED}$DIR on $FS can't preserve perms - use linux filesystem.${NORMAL}"; exit 1;;
ntfs) echo "${RED}Writing to ntfs is not a good idea ${NORMAL}"; exit 1;;
esac;;
/*|/ramdisk/*);;
*) echo "${RED}Err .. where exactly _is_ this $DIR anyway?"${NORMAL}; exit 1;;
esac
if [ ! -d "${DIR}" ] || [ ! -w "${DIR}" ]; then
echo "${RED}$DIR not a dir or not writeable.${NORMAL}"; exit 1
fi
}

not_legacy_live_msg(){
cat <<"EOF2"

This is not a legacy livecd boot.
If your extension contains isolated empty directories that are also found
on the system, declobber cannot be certain if these are clobberring base
system directories or directories added by user/other extensions, or both.
I will note any such dirs in the results.
EOF2
}


checkon_system(){
# Remove empty dirs if already on system
# Make note in results of empty dirs that appear to clobber non-base dirs (eg added by other extensions)
# or if declobber can't tell.

if $LEGCD; then
if [ -e "/${LM}" ]; then
if [ ! -e "/KNOPPIX/${LM}" ]; then
echo ${LM} >>${clobber_list}
echo "Removing empty dir /${LM} - clobbers non-base added directory" >>${results}
else
# this *is* a system clobberring dir !
echo ${LM} >>${clobber_list}
fi
else
echo ${LM} # OK
fi
else
if [ -e "/${LM}" ]; then
echo "Removing empty dir /${LM} - clobbers either base or added directory" >>${results}
echo ${LM} >>${clobber_list}
else
echo ${LM} # OK
fi
fi
}



dir_filter(){
# Filter out non-empty dir lines from stdin
while read LM; do
if [ -d "${WORK}/${LM}" ]; then
if [ $(ls -1A ${WORK}/${LM}| wc -l) -eq 0 ]; then
checkon_system
else
# this *is* a self and/or system clobberring dir !
echo "${LM}" >>${clobber_list}
fi
else
# files, symlinks - ok
echo ${LM}
fi
done
}


#=========================// MAIN //=======================================


. /etc/init.d/dsl-functions # for ANSI colors


if [ "${1}" = -h ] || [ "${1}" = --help ] || [ $# -eq 0 ]; then help; fi

check_work_dir


if [ -e /etc/sysconfig/unionfs -o ! -e /KNOPPIX/bin/ash ]; then
LEGCD=false;
not_legacy_live_msg
else
LEGCD=true;
fi


if [ $EUID -ne 0 ]; then
while true; do
echo "${WHITE}You need to be root to conserve all permissions."
echo -n "Quit? (y/n)${NORMAL} "
read
case $REPLY in
y|y*|Y|Y*) exit 1;;
n|n*|N|N*) break;;
*) echo "Invalid response";;
esac
done
fi


results=/tmp/declobber_results


rm -f ${results} # preclean

CURRENTD="${PWD}"
APP=""


for APP in $*; do

echo ".........................................................................."
echo >>${results}
# If no path supplied, assume current working dir

if echo ${APP} | grep -q -v '^/'; then APP="${CURRENTD}/${APP}"; fi

if [ ! -e "${APP}" ]; then
echo "${RED}Can't find ${APP}${NORMAL}" >>${results}
continue
fi

if [ ${APP##*.} != dsl ]; then echo "${RED}${APP} is not a .dsl extension.${NORMAL}" >>${results}; continue; fi

NAME=$(basename ${APP} .dsl)
WORK=${DIR}/$NAME
LIST_IN=${WORK}/list_in
LIST_OUT=${WORK}/list_out
clobber_list=${WORK}/clobber_list

if [ -e "${WORK}" ]; then echo "${RED}${WORK} already exists.${NORMAL}" >>${results}; continue; fi

mkdir ${WORK}


echo
echo "${WHITE}Removing any nasty clobbering dirs from ${YELLOW}$NAME.dsl ..${NORMAL}"
echo
sleep 1

cd ${WORK}
touch "${clobber_list}" # zero count initialised
tar -zxpvf ${APP}
echo -n "${WHITE}Working ..${NORMAL} "

tar -ztf ${APP} | sort | uniq | tee ${LIST_IN} | dir_filter >${LIST_OUT} &
rotdash $!

NEWNAME=${NAME}_noclobber.dsl
tar -T ${LIST_OUT} --no-recursion --numeric-owner -cpf- | gzip -9 >"${NEWNAME}" &
rotdash $!
if [ ! -s "${NEWNAME}" ]; then echo "${RED}Problem making $NAME_noclobber.dsl" >>${results}; continue; fi

# Debug - check that clobbers removed = difference between in and out
DIFF=$(cat ${LIST_IN} ${LIST_OUT} | sort | uniq -u | cat - ${clobber_list} 2>/dev/null | sort | uniq -u | wc -l)
if [ $DIFF -ne 0 ]; then echo "Error. Removed dirs and difference between in/out unequal, exiting .."; exit 1; fi

echo "${GREEN}Now test your new ${YELLOW}${NEWNAME}!${NORMAL}"

CDRS="$(cat ${clobber_list} 2>/dev/null |wc -l)"
CLOBBERS=${CDRS##*[ ]}
echo "Removed ${YELLOW}$CLOBBERS${NORMAL} clobber dir(s) from ${YELLOW}$NAME.dsl${WHITE} --> ${GREEN}${NEWNAME}${NORMAL}" >>${results}
echo "See ${clobber_list}" >>${results}
cd ${CURRENTD}
done


echo
echo "=========================================================================="
echo
echo "${WHITE}DECLOBBER ${WHITE}RESULTS:${NORMAL}"
echo
cat ${results}
echo
echo "=========================================================================="
echo "${GREEN}Finished.${NORMAL}"

exit 0

Posted by roberts on Feb. 25 2007,22:51
WDef, very impressive work.

One thing I would like to suggest is to add getopts so that one does not need to edit the script to specify output directory for the results.

Here is my suggestion:

Code Sample

DIR=/home/dsl


Be replaced with:
Code Sample

DIR=/home/dsl
while getopts ":o:" opt; do
 case $opt in
 o)
   DIR=$OPTARG
   shift $(($OPTIND - ))
   ;;
 \?) echo 'Usage: declobber.sg [ -o directory ] ex1.dsl ext2.dsl ...'
      exit 1
esac
done


This way the script can be used as

declobber.sh -o /tmp myext.dsl

or even

declobber.sh -o/tmp myext.sh

while still maintaining compatibility without the -o option.

Posted by WDef on Feb. 25 2007,23:08
Thx Robert  - good suggestion, easier configurability!  Will add and repost.
Posted by jpeters on April 04 2007,07:09
I tried this script on gnucash.dsl, and mydsl-loaded the resulting file (gnucash_noclobber.dsl).  Next I attempted to run the exec file cd-ing to the created  /gnucash/usr/bin folder  and typing "sudo ./gnucash" into xterm.  Loading the app failed with the following error:

dsl@box:/ramdisk/home/dsl/gnucash/usr/bin$ sudo ./gnucash                       ERROR: Could not find slib/require.scm in  ("/usr/share/guile" "/usr/share/gnucash/guile-modules" "/usr/share/gnucash/scm" "" "/usr/share/guile/site" "/usr/share/guile/1.6" "/usr/share/guile" ".")

If I mydsl-load the un-declobbered version, the above required files get loaded, and now the app will run.  Maybe  I'm doing this incorrectly.

EDIT:  In comparing the created /usr/share/guile/1.6 files with the KNOPPIX/usr/share/guile/1.6 files, "slibcat" file is missing.

Posted by mikshaw on April 04 2007,14:13
You're running the program "installed" under your home directory rather than under /usr. If the program contains libs or other files that it expects to find in /usr, the program won't work.
Apparently the new package got "/home/dsl/gnucash" included.  From first glance, it looks like the $LIST_OUT is incorrectly including the work directory in the filenames....maybe not, but the work directory is getting in there somehow.

Posted by jpeters on April 04 2007,14:52
Running the app from /Knoppix/usr/bin produces the same results.
Posted by WDef on April 06 2007,18:49
Sorry for the delay getting to this, I've been busy.

declobber produces this error:

Code Sample
tar: usr/share/guile/1.6/ice-9/and-let*.scm usr/share/guile/1.6/ice-9/and-let-star.scm: Cannot stat: No such file or directory
tar: Error exit delayed from previous errors
Error. Removed dirs and difference between in/out unequal, exiting ..


It seems some programmer actually thought it was a good idea to put one of the shell's special characters (wildcard *) into a filename:
/usr/share/guile/1.6/ice-9/and-let*.scm

It look to me like tar may be gagging on this filename when it appears in the list for archiving.

I have no idea what this file ('module') is for, but its comment header states that it is deprecated (no wonder) and that  usr/share/guile/1.6/ice-9/and-let-star.scm should be used instead.

I suggest rebuilding the extension without this file. Try running declobber on that.

Powered by Ikonboard 3.1.2a
Ikonboard © 2001 Jarvis Entertainment Group, Inc.