485 lines
15 KiB
Bash
485 lines
15 KiB
Bash
#! /system/bin/sh
|
|
#
|
|
# Warning: if you want to run this script in cm-recovery change the above to #!/sbin/sh
|
|
#
|
|
# fix_permissions - fixes permissions on Android data directories after upgrade
|
|
# shade@chemlab.org
|
|
#
|
|
# original concept: http://blog.elsdoerfer.name/2009/05/25/android-fix-package-uid-mismatches/
|
|
# implementation by: Cyanogen
|
|
# improved by: ankn, smeat, thenefield, farmatito, rikupw, Kastro
|
|
#
|
|
# v1.1-v1.31r3 - many improvements and concepts from XDA developers.
|
|
# v1.34 through v2.00 - A lot of frustration [by Kastro]
|
|
# v2.01 - Completely rewrote the script for SPEED, thanks for the input farmatito
|
|
# /data/data depth recursion is tweaked;
|
|
# fixed single mode;
|
|
# functions created for modularity;
|
|
# logging can be disabled via CLI for more speed;
|
|
# runtime computation added to end (Runtime: mins secs);
|
|
# progress (current # of total) added to screen;
|
|
# fixed CLI argument parsing, now you can have more than one option!;
|
|
# debug cli option;
|
|
# verbosity can be disabled via CLI option for less noise;;
|
|
# [by Kastro, (XDA: k4str0), twitter;mattcarver]
|
|
# v2.02 - ignore com.htc.resources.apk if it exists and minor code cleanups,
|
|
# fix help text, implement simulated run (-s) [farmatito]
|
|
# v2.03 - fixed chown group ownership output [Kastro]
|
|
# v2.04 - replaced /system/sd with $SD_EXT_DIRECTORY [Firerat]
|
|
VERSION="2.04"
|
|
|
|
# Defaults
|
|
DEBUG=0 # Debug off by default
|
|
LOGGING=1 # Logging on by default
|
|
VERBOSE=1 # Verbose on by default
|
|
|
|
# Messages
|
|
UID_MSG="Changing user ownership for:"
|
|
GID_MSG="Changing group ownership for:"
|
|
PERM_MSG="Changing permissions for:"
|
|
|
|
# Programs needed
|
|
ECHO="busybox echo"
|
|
GREP="busybox grep"
|
|
EGREP="busybox egrep"
|
|
CAT="busybox cat"
|
|
CHOWN="busybox chown"
|
|
CHMOD="busybox chmod"
|
|
MOUNT="busybox mount"
|
|
UMOUNT="busybox umount"
|
|
CUT="busybox cut"
|
|
FIND="busybox find"
|
|
LS="busybox ls"
|
|
TR="busybox tr"
|
|
TEE="busybox tee"
|
|
TEST="busybox test"
|
|
SED="busybox sed"
|
|
RM="busybox rm"
|
|
WC="busybox wc"
|
|
EXPR="busybox expr"
|
|
DATE="busybox date"
|
|
|
|
# Initialise vars
|
|
CODEPATH=""
|
|
LOCALUID=""
|
|
LOCALGID=""
|
|
PACKAGE=""
|
|
REMOVE=0
|
|
NOSYSTEM=0
|
|
ONLY_ONE=""
|
|
SIMULATE=0
|
|
SYSREMOUNT=0
|
|
SYSMOUNT=0
|
|
DATAMOUNT=0
|
|
SYSSDMOUNT=0
|
|
FP_STARTTIME=$( $DATE +"%m-%d-%Y %H:%M:%S" )
|
|
FP_STARTEPOCH=$( $DATE +%s )
|
|
if $TEST "$SD_EXT_DIRECTORY" = ""; then
|
|
#check for mount point, /system/sd included in tests for backward compatibility
|
|
for MP in /sd-ext /system/sd;do
|
|
if $TEST -d $MP; then
|
|
SD_EXT_DIRECTORY=$MP
|
|
break
|
|
fi
|
|
done
|
|
fi
|
|
fp_usage()
|
|
{
|
|
$ECHO "Usage $0 [OPTIONS] [APK_PATH]"
|
|
$ECHO " -d turn on debug"
|
|
$ECHO " -f fix only package APK_PATH"
|
|
$ECHO " -l disable logging for this run (faster)"
|
|
$ECHO " -r remove stale data directories"
|
|
$ECHO " of uninstalled packages while fixing permissions"
|
|
$ECHO " -s simulate only"
|
|
$ECHO " -u check only non-system directories"
|
|
$ECHO " -v disable verbosity for this run (less output)"
|
|
$ECHO " -V print version"
|
|
$ECHO " -h this help"
|
|
}
|
|
|
|
fp_parseargs()
|
|
{
|
|
# Parse options
|
|
while $TEST $# -ne 0; do
|
|
case "$1" in
|
|
-d)
|
|
DEBUG=1
|
|
;;
|
|
-f)
|
|
if $TEST $# -lt 2; then
|
|
$ECHO "$0: missing argument for option $1"
|
|
exit 1
|
|
else
|
|
if $TEST $( $ECHO $2 | $CUT -c1 ) != "-"; then
|
|
ONLY_ONE=$2
|
|
shift;
|
|
else
|
|
$ECHO "$0: missing argument for option $1"
|
|
exit 1
|
|
fi
|
|
fi
|
|
;;
|
|
-r)
|
|
REMOVE=1
|
|
;;
|
|
-s)
|
|
SIMULATE=1
|
|
;;
|
|
-l)
|
|
if $TEST $LOGGING -eq 0; then
|
|
LOGGING=1
|
|
else
|
|
LOGGING=0
|
|
fi
|
|
;;
|
|
-v)
|
|
if $TEST $VERBOSE -eq 0; then
|
|
VERBOSE=1
|
|
else
|
|
VERBOSE=0
|
|
fi
|
|
;;
|
|
-u)
|
|
NOSYSTEM=1
|
|
;;
|
|
-V)
|
|
$ECHO "$0 $VERSION"
|
|
exit 0
|
|
;;
|
|
-h)
|
|
fp_usage
|
|
exit 0
|
|
;;
|
|
-*)
|
|
$ECHO "$0: unknown option $1"
|
|
$ECHO
|
|
fp_usage
|
|
exit 1
|
|
;;
|
|
esac
|
|
shift;
|
|
done
|
|
}
|
|
|
|
fp_print()
|
|
{
|
|
MSG=$@
|
|
if $TEST $LOGGING -eq 1; then
|
|
$ECHO $MSG | $TEE -a $LOG_FILE
|
|
else
|
|
$ECHO $MSG
|
|
fi
|
|
}
|
|
|
|
fp_start()
|
|
{
|
|
if $TEST $SIMULATE -eq 0 ; then
|
|
if $TEST $( $GREP -c " /system " "/proc/mounts" ) -ne 0; then
|
|
DEVICE=$( $GREP " /system " "/proc/mounts" | $CUT -d ' ' -f1 )
|
|
if $TEST $DEBUG -eq 1; then
|
|
fp_print "/system mounted on $DEVICE"
|
|
fi
|
|
if $TEST $( $GREP " /system " "/proc/mounts" | $GREP -c " ro " ) -ne 0; then
|
|
$MOUNT -o remount,rw $DEVICE /system
|
|
SYSREMOUNT=1
|
|
fi
|
|
else
|
|
$MOUNT /system > /dev/null 2>&1
|
|
SYSMOUNT=1
|
|
fi
|
|
|
|
if $TEST $( $GREP -c " /data " "/proc/mounts" ) -eq 0; then
|
|
$MOUNT /data > /dev/null 2>&1
|
|
DATAMOUNT=1
|
|
fi
|
|
|
|
if $TEST -e /dev/block/mmcblk0p2 && $TEST $( $GREP -c " $SD_EXT_DIRECTORY " "/proc/mounts" ) -eq 0; then
|
|
$MOUNT $SD_EXT_DIRECTORY > /dev/null 2>&1
|
|
SYSSDMOUNT=1
|
|
fi
|
|
fi
|
|
if $TEST $( $MOUNT | $GREP -c /sdcard ) -eq 0; then
|
|
LOG_FILE="/data/fix_permissions.log"
|
|
else
|
|
LOG_FILE="/sdcard/fix_permissions.log"
|
|
fi
|
|
if $TEST ! -e "$LOG_FILE"; then
|
|
> $LOG_FILE
|
|
fi
|
|
|
|
fp_print "$0 $VERSION started at $FP_STARTTIME"
|
|
}
|
|
|
|
fp_chown_uid()
|
|
{
|
|
FP_OLDUID=$1
|
|
FP_UID=$2
|
|
FP_FILE=$3
|
|
|
|
#if user ownership doesn't equal then change them
|
|
if $TEST "$FP_OLDUID" != "$FP_UID"; then
|
|
if $TEST $VERBOSE -ne 0; then
|
|
fp_print "$UID_MSG $FP_FILE from '$FP_OLDUID' to '$FP_UID'"
|
|
fi
|
|
if $TEST $SIMULATE -eq 0; then
|
|
$CHOWN $FP_UID "$FP_FILE"
|
|
fi
|
|
fi
|
|
}
|
|
|
|
fp_chown_gid()
|
|
{
|
|
FP_OLDGID=$1
|
|
FP_GID=$2
|
|
FP_FILE=$3
|
|
|
|
#if group ownership doesn't equal then change them
|
|
if $TEST "$FP_OLDGID" != "$FP_GID"; then
|
|
if $TEST $VERBOSE -ne 0; then
|
|
fp_print "$GID_MSG $FP_FILE from '$FP_OLDGID' to '$FP_GID'"
|
|
fi
|
|
if $TEST $SIMULATE -eq 0; then
|
|
$CHOWN :$FP_GID "$FP_FILE"
|
|
fi
|
|
fi
|
|
}
|
|
|
|
fp_chmod()
|
|
{
|
|
FP_OLDPER=$1
|
|
FP_OLDPER=$( $ECHO $FP_OLDPER | cut -c2-10 )
|
|
FP_PERSTR=$2
|
|
FP_PERNUM=$3
|
|
FP_FILE=$4
|
|
|
|
#if the permissions are not equal
|
|
if $TEST "$FP_OLDPER" != "$FP_PERSTR"; then
|
|
if $TEST $VERBOSE -ne 0; then
|
|
fp_print "$PERM_MSG $FP_FILE from '$FP_OLDPER' to '$FP_PERSTR' ($FP_PERNUM)"
|
|
fi
|
|
#change the permissions
|
|
if $TEST $SIMULATE -eq 0; then
|
|
$CHMOD $FP_PERNUM "$FP_FILE"
|
|
fi
|
|
fi
|
|
}
|
|
|
|
fp_all()
|
|
{
|
|
FP_NUMS=$( $CAT /data/system/packages.xml | $EGREP "^<package.*serId" | $GREP -v framework-res.apk | $GREP -v com.htc.resources.apk | $WC -l )
|
|
I=0
|
|
$CAT /data/system/packages.xml | $EGREP "^<package.*serId" | $GREP -v framework-res.apk | $GREP -v com.htc.resources.apk | while read all_line; do
|
|
I=$( $EXPR $I + 1 )
|
|
fp_package "$all_line" $I $FP_NUMS
|
|
done
|
|
}
|
|
|
|
fp_single()
|
|
{
|
|
FP_SFOUND=$( $CAT /data/system/packages.xml | $EGREP "^<package.*serId" | $GREP -v framework-res.apk | $GREP -v com.htc.resources.apk | $GREP -i $ONLY_ONE | wc -l )
|
|
if $TEST $FP_SFOUND -gt 1; then
|
|
fp_print "Cannot perform single operation on $FP_SFOUND matched package(s)."
|
|
elif $TEST $FP_SFOUND = "" -o $FP_SFOUND -eq 0; then
|
|
fp_print "Could not find the package you specified in the packages.xml file."
|
|
else
|
|
FP_SPKG=$( $CAT /data/system/packages.xml | $EGREP "^<package.*serId" | $GREP -v framework-res.apk | $GREP -v com.htc.resources.apk | $GREP -i $ONLY_ONE )
|
|
fp_package "${FP_SPKG}" 1 1
|
|
fi
|
|
}
|
|
|
|
fp_package()
|
|
{
|
|
pkgline=$1
|
|
curnum=$2
|
|
endnum=$3
|
|
CODEPATH=$( $ECHO $pkgline | $SED 's%.* codePath="\(.*\)".*%\1%' | $CUT -d '"' -f1 )
|
|
PACKAGE=$( $ECHO $pkgline | $SED 's%.* name="\(.*\)".*%\1%' | $CUT -d '"' -f1 )
|
|
LOCALUID=$( $ECHO $pkgline | $SED 's%.*serId="\(.*\)".*%\1%' | $CUT -d '"' -f1 )
|
|
LOCALGID=$LOCALUID
|
|
APPDIR=$( $ECHO $CODEPATH | $SED 's%^\(.*\)/.*%\1%' )
|
|
APK=$( $ECHO $CODEPATH | $SED 's%^.*/\(.*\..*\)$%\1%' )
|
|
|
|
#debug
|
|
if $TEST $DEBUG -eq 1; then
|
|
fp_print "CODEPATH: $CODEPATH APPDIR: $APPDIR APK:$APK UID/GID:$LOCALUID:$LOCALGID"
|
|
fi
|
|
|
|
#check for existence of apk
|
|
if $TEST -e $CODEPATH; then
|
|
fp_print "Processing ($curnum of $endnum): $PACKAGE..."
|
|
|
|
#lets get existing permissions of CODEPATH
|
|
OLD_UGD=$( $LS -ln "$CODEPATH" )
|
|
OLD_PER=$( $ECHO $OLD_UGD | $CUT -d ' ' -f1 )
|
|
OLD_UID=$( $ECHO $OLD_UGD | $CUT -d ' ' -f3 )
|
|
OLD_GID=$( $ECHO $OLD_UGD | $CUT -d ' ' -f4 )
|
|
|
|
#apk source dirs
|
|
if $TEST "$APPDIR" = "/system/app"; then
|
|
#skip system apps if set
|
|
if $TEST "$NOSYSTEM" = "1"; then
|
|
fp_print "***SKIPPING SYSTEM APP ($PACKAGE)!"
|
|
return
|
|
fi
|
|
fp_chown_uid $OLD_UID 0 "$CODEPATH"
|
|
fp_chown_gid $OLD_GID 0 "$CODEPATH"
|
|
fp_chmod $OLD_PER "rw-r--r--" 644 "$CODEPATH"
|
|
elif $TEST "$APPDIR" = "/data/app" || $TEST "$APPDIR" = "/sd-ext/app"; then
|
|
fp_chown_uid $OLD_UID 1000 "$CODEPATH"
|
|
fp_chown_gid $OLD_GID 1000 "$CODEPATH"
|
|
fp_chmod $OLD_PER "rw-r--r--" 644 "$CODEPATH"
|
|
elif $TEST "$APPDIR" = "/data/app-private" || $TEST "$APPDIR" = "/sd-ext/app-private"; then
|
|
fp_chown_uid $OLD_UID 1000 "$CODEPATH"
|
|
fp_chown_gid $OLD_GID $LOCALGID "$CODEPATH"
|
|
fp_chmod $OLD_PER "rw-r-----" 640 "$CODEPATH"
|
|
fi
|
|
else
|
|
fp_print "$CODEPATH does not exist ($curnum of $endnum). Reinstall..."
|
|
if $TEST $REMOVE -eq 1; then
|
|
if $TEST -d /data/data/$PACKAGE ; then
|
|
fp_print "Removing stale dir /data/data/$PACKAGE"
|
|
if $TEST $SIMULATE -eq 0 ; then
|
|
$RM -R /data/data/$PACKAGE
|
|
fi
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
#the data/data for the package
|
|
if $TEST -d "/data/data/$PACKAGE"; then
|
|
#find all directories in /data/data/$PACKAGE
|
|
$FIND /data/data/$PACKAGE -type d -exec $LS -ldn {} \; | while read dataline; do
|
|
#get existing permissions of that directory
|
|
OLD_PER=$( $ECHO $dataline | $CUT -d ' ' -f1 )
|
|
OLD_UID=$( $ECHO $dataline | $CUT -d ' ' -f3 )
|
|
OLD_GID=$( $ECHO $dataline | $CUT -d ' ' -f4 )
|
|
FILEDIR=$( $ECHO $dataline | $CUT -d ' ' -f9 )
|
|
FOURDIR=$( $ECHO $FILEDIR | $CUT -d '/' -f5 )
|
|
|
|
#set defaults for iteration
|
|
ISLIB=0
|
|
REVPERM=755
|
|
REVPSTR="rwxr-xr-x"
|
|
REVUID=$LOCALUID
|
|
REVGID=$LOCALGID
|
|
|
|
if $TEST "$FOURDIR" = ""; then
|
|
#package directory, perms:755 owner:$LOCALUID:$LOCALGID
|
|
fp_chmod $OLD_PER "rwxr-xr-x" 755 "$FILEDIR"
|
|
elif $TEST "$FOURDIR" = "lib"; then
|
|
#lib directory, perms:755 owner:1000:1000
|
|
#lib files, perms:755 owner:1000:1000
|
|
ISLIB=1
|
|
REVPERM=755
|
|
REVPSTR="rwxr-xr-x"
|
|
REVUID=1000
|
|
REVGID=1000
|
|
fp_chmod $OLD_PER "rwxr-xr-x" 755 "$FILEDIR"
|
|
elif $TEST "$FOURDIR" = "shared_prefs"; then
|
|
#shared_prefs directories, perms:771 owner:$LOCALUID:$LOCALGID
|
|
#shared_prefs files, perms:660 owner:$LOCALUID:$LOCALGID
|
|
REVPERM=660
|
|
REVPSTR="rw-rw----"
|
|
fp_chmod $OLD_PER "rwxrwx--x" 771 "$FILEDIR"
|
|
elif $TEST "$FOURDIR" = "databases"; then
|
|
#databases directories, perms:771 owner:$LOCALUID:$LOCALGID
|
|
#databases files, perms:660 owner:$LOCALUID:$LOCALGID
|
|
REVPERM=660
|
|
REVPSTR="rw-rw----"
|
|
fp_chmod $OLD_PER "rwxrwx--x" 771 "$FILEDIR"
|
|
elif $TEST "$FOURDIR" = "cache"; then
|
|
#cache directories, perms:771 owner:$LOCALUID:$LOCALGID
|
|
#cache files, perms:600 owner:$LOCALUID:$LOCALGID
|
|
REVPERM=600
|
|
REVPSTR="rw-------"
|
|
fp_chmod $OLD_PER "rwxrwx--x" 771 "$FILEDIR"
|
|
else
|
|
#other directories, perms:771 owner:$LOCALUID:$LOCALGID
|
|
REVPERM=771
|
|
REVPSTR="rwxrwx--x"
|
|
fp_chmod $OLD_PER "rwxrwx--x" 771 "$FILEDIR"
|
|
fi
|
|
|
|
#change ownership of directories matched
|
|
if $TEST "$ISLIB" = "1"; then
|
|
fp_chown_uid $OLD_UID 1000 "$FILEDIR"
|
|
fp_chown_gid $OLD_GID 1000 "$FILEDIR"
|
|
else
|
|
fp_chown_uid $OLD_UID $LOCALUID "$FILEDIR"
|
|
fp_chown_gid $OLD_GID $LOCALGID "$FILEDIR"
|
|
fi
|
|
|
|
#if any files exist in directory with improper permissions reset them
|
|
$FIND $FILEDIR -type f -maxdepth 1 ! -perm $REVPERM -exec $LS -ln {} \; | while read subline; do
|
|
OLD_PER=$( $ECHO $subline | $CUT -d ' ' -f1 )
|
|
SUBFILE=$( $ECHO $subline | $CUT -d ' ' -f9 )
|
|
fp_chmod $OLD_PER $REVPSTR $REVPERM "$SUBFILE"
|
|
done
|
|
|
|
#if any files exist in directory with improper user reset them
|
|
$FIND $FILEDIR -type f -maxdepth 1 ! -user $REVUID -exec $LS -ln {} \; | while read subline; do
|
|
OLD_UID=$( $ECHO $subline | $CUT -d ' ' -f3 )
|
|
SUBFILE=$( $ECHO $subline | $CUT -d ' ' -f9 )
|
|
fp_chown_uid $OLD_UID $REVUID "$SUBFILE"
|
|
done
|
|
|
|
#if any files exist in directory with improper group reset them
|
|
$FIND $FILEDIR -type f -maxdepth 1 ! -group $REVGID -exec $LS -ln {} \; | while read subline; do
|
|
OLD_GID=$( $ECHO $subline | $CUT -d ' ' -f4 )
|
|
SUBFILE=$( $ECHO $subline | $CUT -d ' ' -f9 )
|
|
fp_chown_gid $OLD_GID $REVGID "$SUBFILE"
|
|
done
|
|
done
|
|
fi
|
|
}
|
|
|
|
date_diff()
|
|
{
|
|
if $TEST $# -ne 2; then
|
|
FP_DDM="E"
|
|
FP_DDS="E"
|
|
return
|
|
fi
|
|
FP_DDD=$( $EXPR $2 - $1 )
|
|
FP_DDM=$( $EXPR $FP_DDD / 60 )
|
|
FP_DDS=$( $EXPR $FP_DDD % 60 )
|
|
}
|
|
|
|
fp_end()
|
|
{
|
|
if $TEST $SYSREMOUNT -eq 1; then
|
|
$MOUNT -o remount,ro $DEVICE /system > /dev/null 2>&1
|
|
fi
|
|
|
|
if $TEST $SYSSDMOUNT -eq 1; then
|
|
$UMOUNT $SD_EXT_DIRECTORY > /dev/null 2>&1
|
|
fi
|
|
|
|
if $TEST $SYSMOUNT -eq 1; then
|
|
$UMOUNT /system > /dev/null 2>&1
|
|
fi
|
|
|
|
if $TEST $DATAMOUNT -eq 1; then
|
|
$UMOUNT /data > /dev/null 2>&1
|
|
fi
|
|
|
|
FP_ENDTIME=$( $DATE +"%m-%d-%Y %H:%M:%S" )
|
|
FP_ENDEPOCH=$( $DATE +%s )
|
|
|
|
date_diff $FP_STARTEPOCH $FP_ENDEPOCH
|
|
|
|
fp_print "$0 $VERSION ended at $FP_ENDTIME (Runtime:${FP_DDM}m${FP_DDS}s)"
|
|
}
|
|
|
|
#MAIN SCRIPT
|
|
|
|
fp_parseargs $@
|
|
fp_start
|
|
if $TEST "$ONLY_ONE" != "" -a "$ONLY_ONE" != "0" ; then
|
|
fp_single "$ONLY_ONE"
|
|
else
|
|
fp_all
|
|
fi
|
|
fp_end
|