| #!/sbin/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="" |
| UID="" |
| GID="" |
| 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 ) |
| UID=$( $ECHO $pkgline | $SED 's%.*serId="\(.*\)".*%\1%' | $CUT -d '"' -f1 ) |
| GID=$UID |
| 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:$UID:$GID" |
| 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 $GID "$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=$UID |
| REVGID=$GID |
| |
| if $TEST "$FOURDIR" = ""; then |
| #package directory, perms:755 owner:$UID:$GID |
| 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:$UID:$GID |
| #shared_prefs files, perms:660 owner:$UID:$GID |
| REVPERM=660 |
| REVPSTR="rw-rw----" |
| fp_chmod $OLD_PER "rwxrwx--x" 771 "$FILEDIR" |
| elif $TEST "$FOURDIR" = "databases"; then |
| #databases directories, perms:771 owner:$UID:$GID |
| #databases files, perms:660 owner:$UID:$GID |
| REVPERM=660 |
| REVPSTR="rw-rw----" |
| fp_chmod $OLD_PER "rwxrwx--x" 771 "$FILEDIR" |
| elif $TEST "$FOURDIR" = "cache"; then |
| #cache directories, perms:771 owner:$UID:$GID |
| #cache files, perms:600 owner:$UID:GID |
| REVPERM=600 |
| REVPSTR="rw-------" |
| fp_chmod $OLD_PER "rwxrwx--x" 771 "$FILEDIR" |
| else |
| #other directories, perms:771 owner:$UID:$GID |
| 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 $UID "$FILEDIR" |
| fp_chown_gid $OLD_GID $GID "$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 |