Skip to content

Commit

Permalink
Merge pull request #1692 from kiwix/feature/macgills/1663-filesystem-…
Browse files Browse the repository at this point in the history
…detection

#1663 Improve FileSystem detection - read from mount and fallback to …
  • Loading branch information
macgills authored Jan 13, 2020
2 parents c4aa23d + cbee2b9 commit e11f421
Show file tree
Hide file tree
Showing 5 changed files with 177 additions and 24 deletions.
13 changes: 13 additions & 0 deletions app/src/main/java/org/kiwix/kiwixmobile/di/modules/KiwixModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,11 @@ import android.content.Context
import android.location.LocationManager
import dagger.Module
import dagger.Provides
import org.kiwix.kiwixmobile.core.utils.SharedPreferenceUtil
import org.kiwix.kiwixmobile.di.KiwixScope
import org.kiwix.kiwixmobile.zim_manager.Fat32Checker
import org.kiwix.kiwixmobile.zim_manager.FileWritingFileSystemChecker
import org.kiwix.kiwixmobile.zim_manager.MountFileSystemChecker

@Module
object KiwixModule {
Expand All @@ -31,4 +35,13 @@ object KiwixModule {
@JvmStatic
internal fun provideLocationManager(context: Context): LocationManager =
context.getSystemService(Context.LOCATION_SERVICE) as LocationManager

@Provides
@KiwixScope
@JvmStatic
internal fun provideFat32Checker(sharedPreferenceUtil: SharedPreferenceUtil): Fat32Checker =
Fat32Checker(
sharedPreferenceUtil,
listOf(MountFileSystemChecker(), FileWritingFileSystemChecker())
)
}
40 changes: 16 additions & 24 deletions app/src/main/java/org/kiwix/kiwixmobile/zim_manager/Fat32Checker.kt
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
package org.kiwix.kiwixmobile.zim_manager

import android.os.FileObserver
import android.util.Log
import io.reactivex.Flowable
import io.reactivex.functions.BiFunction
import io.reactivex.processors.BehaviorProcessor
Expand All @@ -28,11 +27,15 @@ import org.kiwix.kiwixmobile.zim_manager.Fat32Checker.FileSystemState.CanWrite4G
import org.kiwix.kiwixmobile.zim_manager.Fat32Checker.FileSystemState.CannotWrite4GbFile
import org.kiwix.kiwixmobile.zim_manager.Fat32Checker.FileSystemState.NotEnoughSpaceFor4GbFile
import org.kiwix.kiwixmobile.zim_manager.Fat32Checker.FileSystemState.Unknown
import org.kiwix.kiwixmobile.zim_manager.FileSystemCapability.CANNOT_WRITE_4GB
import org.kiwix.kiwixmobile.zim_manager.FileSystemCapability.CAN_WRITE_4GB
import org.kiwix.kiwixmobile.zim_manager.FileSystemCapability.INCONCLUSIVE
import java.io.File
import java.io.RandomAccessFile
import javax.inject.Inject

class Fat32Checker @Inject constructor(sharedPreferenceUtil: SharedPreferenceUtil) {
class Fat32Checker constructor(
sharedPreferenceUtil: SharedPreferenceUtil,
private val fileSystemCheckers: List<FileSystemChecker>
) {
val fileSystemStates: BehaviorProcessor<FileSystemState> = BehaviorProcessor.create()
private var fileObserver: FileObserver? = null
private val requestCheckSystemFileType = BehaviorProcessor.createDefault(Unit)
Expand All @@ -59,10 +62,7 @@ class Fat32Checker @Inject constructor(sharedPreferenceUtil: SharedPreferenceUti

private fun fileObserver(it: String?): FileObserver {
return object : FileObserver(it, MOVED_FROM or DELETE) {
override fun onEvent(
event: Int,
path: String?
) {
override fun onEvent(event: Int, path: String?) {
requestCheckSystemFileType.onNext(Unit)
}
}.apply { startWatching() }
Expand All @@ -77,20 +77,16 @@ class Fat32Checker @Inject constructor(sharedPreferenceUtil: SharedPreferenceUti
}

private fun canCreate4GbFile(storage: String): Boolean {
val path = "$storage/large_file_test.txt"
File(path).deleteIfExists()
try {
RandomAccessFile(path, "rw").use {
it.setLength(FOUR_GIGABYTES_IN_BYTES)
return@canCreate4GbFile true
fileSystemCheckers.forEach {
when (it.checkFilesystemSupports4GbFiles(storage)) {
CAN_WRITE_4GB -> return@canCreate4GbFile true
CANNOT_WRITE_4GB -> return@canCreate4GbFile false
INCONCLUSIVE -> {
/*do nothing*/
}
}
} catch (e: Exception) {
e.printStackTrace()
Log.d("Fat32Checker", e.message)
return false
} finally {
File(path).deleteIfExists()
}
return false
}

companion object {
Expand All @@ -105,7 +101,3 @@ class Fat32Checker @Inject constructor(sharedPreferenceUtil: SharedPreferenceUti
object Unknown : FileSystemState()
}
}

private fun File.deleteIfExists() {
if (exists()) delete()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* Kiwix Android
* Copyright (c) 2020 Kiwix <android.kiwix.org>
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/

package org.kiwix.kiwixmobile.zim_manager

interface FileSystemChecker {
fun checkFilesystemSupports4GbFiles(path: String): FileSystemCapability
}

enum class FileSystemCapability {
CAN_WRITE_4GB,
CANNOT_WRITE_4GB,
INCONCLUSIVE
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* Kiwix Android
* Copyright (c) 2020 Kiwix <android.kiwix.org>
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/

package org.kiwix.kiwixmobile.zim_manager

import android.util.Log
import org.kiwix.kiwixmobile.zim_manager.FileSystemCapability.CANNOT_WRITE_4GB
import org.kiwix.kiwixmobile.zim_manager.FileSystemCapability.CAN_WRITE_4GB
import java.io.File
import java.io.RandomAccessFile

class FileWritingFileSystemChecker : FileSystemChecker {
override fun checkFilesystemSupports4GbFiles(path: String): FileSystemCapability {
with(File("$path/large_file_test.txt")) {
deleteIfExists()
try {
RandomAccessFile(this.path, "rw").use {
it.setLength(Fat32Checker.FOUR_GIGABYTES_IN_BYTES)
return@checkFilesystemSupports4GbFiles CAN_WRITE_4GB
}
} catch (e: Exception) {
e.printStackTrace()
Log.d("Fat32Checker", e.message)
return@checkFilesystemSupports4GbFiles CANNOT_WRITE_4GB
} finally {
deleteIfExists()
}
}
}
}

private fun File.deleteIfExists() {
if (exists()) delete()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/*
* Kiwix Android
* Copyright (c) 2020 Kiwix <android.kiwix.org>
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/

package org.kiwix.kiwixmobile.zim_manager

import org.kiwix.kiwixmobile.zim_manager.FileSystemCapability.CANNOT_WRITE_4GB
import org.kiwix.kiwixmobile.zim_manager.FileSystemCapability.CAN_WRITE_4GB
import org.kiwix.kiwixmobile.zim_manager.FileSystemCapability.INCONCLUSIVE
import java.io.File

class MountFileSystemChecker : FileSystemChecker {
override fun checkFilesystemSupports4GbFiles(path: String) =
recursivelyDetermineFilesystem(mountPoints(), path)

private fun recursivelyDetermineFilesystem(mountPoints: List<MountInfo>, path: String):
FileSystemCapability =
mountPoints.maxBy { it.matchCount(path) }
?.takeIf { it.matchCount(path) > 0 }
?.let {
when {
it.isVirtual -> recursivelyDetermineFilesystem(mountPoints - it, it.device)
it.supports4GBFiles -> CAN_WRITE_4GB
it.doesNotSupport4GBFiles -> CANNOT_WRITE_4GB
else -> INCONCLUSIVE
}
} ?: INCONCLUSIVE

private fun mountPoints() =
File("proc/mounts")
.takeIf(File::exists)
?.readLines()
?.map { MountInfo(it.split(" ")) }
?: emptyList()
}

data class MountInfo(val device: String, val mountPoint: String, val fileSystem: String) {
constructor(split: List<String>) : this(split[0], split[1], split[2])

fun matchCount(storage: String) = storage.split("/")
.zip(mountPoint.split("/"))
.fold(0, { acc, pair ->
if (pair.first == pair.second) acc + 1
else acc
})

val isVirtual = VIRTUAL_FILE_SYSTEMS.contains(fileSystem)
val supports4GBFiles = SUPPORTS_4GB_FILE_SYSTEMS.contains(fileSystem)
val doesNotSupport4GBFiles = DOES_NOT_SUPPORT_4GB_FILE_SYSTEMS.contains(fileSystem)

companion object {
private val VIRTUAL_FILE_SYSTEMS = listOf("fuse", "sdcardfs")
private val SUPPORTS_4GB_FILE_SYSTEMS = listOf("ext4", "exfat")
private val DOES_NOT_SUPPORT_4GB_FILE_SYSTEMS = listOf("fat32", "vfat")
}
}

0 comments on commit e11f421

Please sign in to comment.