Skip to content

Commit

Permalink
Analyzer: Improve error handling when file paths can't be read despit…
Browse files Browse the repository at this point in the history
…e escalated privileges

Shouldn't happen, but if it happens, we have some fallbacks to turn to.

See #1173
  • Loading branch information
d4rken committed May 24, 2024
1 parent 84b64ee commit 3d69d6c
Show file tree
Hide file tree
Showing 2 changed files with 79 additions and 29 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ package eu.darken.sdmse.common.files

import eu.darken.sdmse.common.coroutine.AppScope
import eu.darken.sdmse.common.coroutine.DispatcherProvider
import eu.darken.sdmse.common.debug.logging.Logging.Priority.WARN
import eu.darken.sdmse.common.debug.logging.asLog
import eu.darken.sdmse.common.debug.logging.log
import eu.darken.sdmse.common.debug.logging.logTag
import eu.darken.sdmse.common.files.local.LocalGateway
import eu.darken.sdmse.common.files.local.LocalPath
Expand All @@ -17,7 +20,6 @@ import okio.IOException
import okio.Sink
import okio.Source
import java.time.Instant
import java.util.*
import javax.inject.Inject
import javax.inject.Singleton

Expand Down Expand Up @@ -76,11 +78,17 @@ class GatewaySwitch @Inject constructor(
val mapped = path.toTargetType(type)
return try {
useGateway(mapped) { lookup(mapped) }
} catch (e: ReadException) {
if (type != Type.AUTO) throw e
} catch (oge: ReadException) {
if (type != Type.AUTO) throw oge
log(TAG, WARN) { "lookup(...): Original lookup failed, try alternative: ${oge.asLog()}" }

val fallback = path.toAlternative()
useGateway(fallback) { lookup(fallback) }
try {
useGateway(fallback) { lookup(fallback) }
} catch (e: ReadException) {
log(TAG, WARN) { "lookup(...): Alternative lookup failed either: ${e.asLog()}" }
throw oge
}
}
}

Expand All @@ -92,16 +100,40 @@ class GatewaySwitch @Inject constructor(
val mapped = path.toTargetType(type)
return try {
useGateway(mapped) { lookupFiles(mapped) }
} catch (e: ReadException) {
if (type != Type.AUTO) throw e
} catch (oge: ReadException) {
if (type != Type.AUTO) throw oge
log(TAG, WARN) { "lookupFiles(...): Original lookup failed, try alternative: ${oge.asLog()}" }

val fallback = path.toAlternative()
useGateway(fallback) { lookupFiles(fallback) }
try {
useGateway(fallback) { lookupFiles(fallback) }
} catch (e: ReadException) {
log(TAG, WARN) { "lookupFiles(...): Alternative lookup failed either: ${e.asLog()}" }
throw oge
}
}
}

override suspend fun lookupFilesExtended(path: APath): Collection<APathLookupExtended<APath>> {
return useGateway(path) { lookupFilesExtended(path) }
return lookupFilesExtended(path, Type.CURRENT)
}

suspend fun lookupFilesExtended(path: APath, type: Type): Collection<APathLookupExtended<APath>> {
val mapped = path.toTargetType(type)
return try {
useGateway(mapped) { lookupFilesExtended(mapped) }
} catch (oge: ReadException) {
if (type != Type.AUTO) throw oge
log(TAG, WARN) { "lookupFilesExtended(...): Original lookup failed, try alternative: ${oge.asLog()}" }

val fallback = path.toAlternative()
try {
useGateway(fallback) { lookupFilesExtended(fallback) }
} catch (e: ReadException) {
log(TAG, WARN) { "lookupFilesExtended(...): Alternative lookup failed either: ${e.asLog()}" }
throw oge
}
}
}

override suspend fun walk(
Expand Down Expand Up @@ -184,8 +216,8 @@ class GatewaySwitch @Inject constructor(
}

private suspend fun APath.toAlternative(): APath = when (this.pathType) {
APath.PathType.LOCAL -> mapper.toSAFPath(this as LocalPath) ?: throw IOException("Can't map $this to SAF")
APath.PathType.SAF -> mapper.toLocalPath(this as SAFPath) ?: throw IOException("Can't map $this to LOCAL")
APath.PathType.LOCAL -> mapper.toSAFPath(this as LocalPath) ?: throw ReadException(this, "Can't map to SAF")
APath.PathType.SAF -> mapper.toLocalPath(this as SAFPath) ?: throw ReadException(this, "Can't map to LOCAL")
APath.PathType.RAW -> throw UnsupportedOperationException("Alternative mapping for RAW not available")
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ import eu.darken.sdmse.common.areas.DataArea
import eu.darken.sdmse.common.ca.toCaString
import eu.darken.sdmse.common.clutter.Marker
import eu.darken.sdmse.common.coroutine.SuspendingLazy
import eu.darken.sdmse.common.debug.logging.Logging.Priority.ERROR
import eu.darken.sdmse.common.debug.logging.Logging.Priority.WARN
import eu.darken.sdmse.common.debug.logging.asLog
import eu.darken.sdmse.common.debug.logging.log
import eu.darken.sdmse.common.debug.logging.logTag
import eu.darken.sdmse.common.files.APath
Expand Down Expand Up @@ -95,15 +97,22 @@ class AppStorageScanner @AssistedInject constructor(
}
?.let { LocalPath.build(it) }
?.let { codeDir ->
when {
useRoot -> codeDir.walkContentItem(gatewaySwitch)
appStorStats != null -> ContentItem.fromInaccessible(
if (useRoot) {
try {
return@let codeDir.walkContentItem(gatewaySwitch)
} catch (e: ReadException) {
log(TAG, ERROR) { "Failed to read $codeDir despire root access? ${e.asLog()}" }
}
}

if (appStorStats != null) {
return@let ContentItem.fromInaccessible(
codeDir,
if (pkg.userHandle == currentUser) appStorStats.appBytes else 0L
)

else -> ContentItem.fromInaccessible(codeDir)
}

ContentItem.fromInaccessible(codeDir)
}

ContentGroup(
Expand Down Expand Up @@ -133,22 +142,31 @@ class AppStorageScanner @AssistedInject constructor(
}
.toSet()

val dataDirPrivs = when {
storage.type != DeviceStorage.Type.PRIMARY -> emptySet()
dataAreas.any { it.type == DataArea.Type.PRIVATE_DATA } -> pkg
.getPrivateDataDirs(dataAreas)
.filter { gatewaySwitch.exists(it, type = GatewaySwitch.Type.CURRENT) }
.map { it.walkContentItem(gatewaySwitch) }

appStorStats != null -> setOfNotNull(pkg.applicationInfo?.dataDir).map {
ContentItem.fromInaccessible(
LocalPath.build(it),
// This is a simplification, because storage stats don't provider more fine grained infos
appStorStats.dataBytes - (dataDirPubs.firstOrNull()?.size ?: 0L)
)
val dataDirPrivs = run {
if (storage.type != DeviceStorage.Type.PRIMARY) return@run emptySet()

if (dataAreas.any { it.type == DataArea.Type.PRIVATE_DATA }) {
try {
return@run pkg
.getPrivateDataDirs(dataAreas)
.filter { gatewaySwitch.exists(it, type = GatewaySwitch.Type.CURRENT) }
.map { it.walkContentItem(gatewaySwitch) }
} catch (e: ReadException) {
log(TAG, ERROR) { "Failed to read private data dirs for $pkg: ${e.asLog()}" }
}
}

if (appStorStats != null) {
return@run setOfNotNull(pkg.applicationInfo?.dataDir).map {
ContentItem.fromInaccessible(
LocalPath.build(it),
// This is a simplification, because storage stats don't provider more fine grained infos
appStorStats.dataBytes - (dataDirPubs.firstOrNull()?.size ?: 0L)
)
}
}

else -> setOfNotNull(pkg.applicationInfo?.dataDir).map {
setOfNotNull(pkg.applicationInfo?.dataDir).map {
ContentItem.fromInaccessible(LocalPath.build(it))
}
}
Expand Down

0 comments on commit 3d69d6c

Please sign in to comment.