Expose smb.conf to external storage. (#21)

* Consolidate ShareManager into the unified init process.

* Remove misc.xml

* Expose smb.conf to external storage.

Some clean-up on initialization code. Fix a bug that doesn't restart SMB
client after configuration is updated.

* Use the right TAG.

* Guard a log behind debug flag.
This commit is contained in:
Xu Tan 2017-07-13 17:30:50 -07:00 committed by jon-mann
parent 6b1a97b626
commit b16d629dc5
2 changed files with 86 additions and 41 deletions

View file

@ -45,42 +45,75 @@ class SambaConfiguration implements Iterable<Map.Entry<String, String>> {
private static final String CONF_KEY_VALUE_SEPARATOR = " = "; private static final String CONF_KEY_VALUE_SEPARATOR = " = ";
private final File mHomeFolder; private final File mHomeFolder;
private final File mShareFolder;
private final Map<String, String> mConfigurations = new HashMap<>(); private final Map<String, String> mConfigurations = new HashMap<>();
SambaConfiguration(File homeFolder) { SambaConfiguration(File homeFolder, @Nullable File shareFolder) {
mHomeFolder = homeFolder; mHomeFolder = homeFolder;
if (shareFolder != null && (shareFolder.isDirectory() || shareFolder.mkdirs())) {
mShareFolder = shareFolder;
} else {
Log.w(TAG, "Failed to create share folder. Only default value is supported.");
// Use home folder as the share folder to avoid null checks everywhere.
mShareFolder = mHomeFolder;
}
setHomeEnv(homeFolder.getAbsolutePath()); setHomeEnv(homeFolder.getAbsolutePath());
} }
public void flushAsDefault() { void flushDefault(OnConfigurationChangedListener listener) {
// lmhosts are not used in SambaDocumentsProvider and prioritize bcast because sometimes in home
// settings DNS will resolve unknown domain name to a specific IP for advertisement.
//
// lmhosts -- lmhosts file if existed side by side to smb.conf
// wins -- Windows Internet Name Service
// hosts -- hosts file and DNS resolution
// bcast -- NetBIOS broadcast
addConfiguration("name resolve order", "wins bcast hosts");
// Urge from users to disable SMB1 by default.
addConfiguration("client min protocol", "SMB2");
addConfiguration("client max protocol", "SMB3");
File smbFile = getSmbFile(mHomeFolder); File smbFile = getSmbFile(mHomeFolder);
if (!smbFile.exists()) { if (!smbFile.exists()) {
flush(null); flush(listener);
} }
} }
public synchronized SambaConfiguration addConfiguration(String key, String value) { synchronized SambaConfiguration addConfiguration(String key, String value) {
mConfigurations.put(key, value); mConfigurations.put(key, value);
return this; return this;
} }
public synchronized SambaConfiguration removeConfiguration(String key) { synchronized SambaConfiguration removeConfiguration(String key) {
mConfigurations.remove(key); mConfigurations.remove(key);
return this; return this;
} }
public void load(OnConfigurationChangedListener listener) { boolean syncFromExternal(OnConfigurationChangedListener listener) {
new LoadTask(listener).execute(); final File smbFile = getSmbFile(mHomeFolder);
final File extSmbFile = getExtSmbFile(mShareFolder);
if (extSmbFile.isFile() && extSmbFile.lastModified() > smbFile.lastModified()) {
if (BuildConfig.DEBUG) Log.d(TAG, "Syncing " + SMB_CONF_FILE +
" from external source to internal source.");
new SyncTask(listener).execute(extSmbFile);
return true;
} }
public void flush(@Nullable OnConfigurationChangedListener listener) { return false;
}
private void flush(OnConfigurationChangedListener listener) {
new FlushTask(listener).execute(); new FlushTask(listener).execute();
} }
private synchronized void read() throws IOException { private synchronized void read(File smbFile) throws IOException {
mConfigurations.clear(); mConfigurations.clear();
try (BufferedReader reader = new BufferedReader(new FileReader(getSmbFile(mHomeFolder)))) { try (BufferedReader reader = new BufferedReader(new FileReader(smbFile))) {
String line; String line;
while ((line = reader.readLine()) != null) { while ((line = reader.readLine()) != null) {
String[] conf = line.split(CONF_KEY_VALUE_SEPARATOR); String[] conf = line.split(CONF_KEY_VALUE_SEPARATOR);
@ -113,6 +146,10 @@ class SambaConfiguration implements Iterable<Map.Entry<String, String>> {
return new File(smbFolder, SMB_CONF_FILE); return new File(smbFolder, SMB_CONF_FILE);
} }
private static File getExtSmbFile(File shareFolder) {
return new File(shareFolder, SMB_CONF_FILE);
}
private void setHomeEnv(String absoluteFolder) { private void setHomeEnv(String absoluteFolder) {
try { try {
setEnv(HOME_VAR, absoluteFolder); setEnv(HOME_VAR, absoluteFolder);
@ -128,16 +165,18 @@ class SambaConfiguration implements Iterable<Map.Entry<String, String>> {
return mConfigurations.entrySet().iterator(); return mConfigurations.entrySet().iterator();
} }
private class LoadTask extends BiResultTask<Void, Void, Void> { private class SyncTask extends BiResultTask<File, Void, Void> {
private final OnConfigurationChangedListener mListener; private final OnConfigurationChangedListener mListener;
private LoadTask(OnConfigurationChangedListener listener) { private SyncTask(OnConfigurationChangedListener listener) {
mListener = listener; mListener = listener;
} }
@Override @Override
public Void run(Void... params) throws IOException { public Void run(File... params) throws IOException {
read(); read(params[0]);
write();
return null; return null;
} }
@ -148,9 +187,9 @@ class SambaConfiguration implements Iterable<Map.Entry<String, String>> {
} }
private class FlushTask extends BiResultTask<Void, Void, Void> { private class FlushTask extends BiResultTask<Void, Void, Void> {
private final @Nullable OnConfigurationChangedListener mListener; private final OnConfigurationChangedListener mListener;
private FlushTask(@Nullable OnConfigurationChangedListener listener) { private FlushTask(OnConfigurationChangedListener listener) {
mListener = listener; mListener = listener;
} }
@ -162,11 +201,9 @@ class SambaConfiguration implements Iterable<Map.Entry<String, String>> {
@Override @Override
public void onSucceeded(Void result) { public void onSucceeded(Void result) {
if (mListener != null) {
mListener.onConfigurationChanged(); mListener.onConfigurationChanged();
} }
} }
}
interface OnConfigurationChangedListener { interface OnConfigurationChangedListener {
void onConfigurationChanged(); void onConfigurationChanged();

View file

@ -25,21 +25,22 @@ import android.net.Network;
import android.net.NetworkCapabilities; import android.net.NetworkCapabilities;
import android.net.NetworkRequest; import android.net.NetworkRequest;
import android.util.Log;
import com.google.android.sambadocumentsprovider.SambaConfiguration.OnConfigurationChangedListener; import com.google.android.sambadocumentsprovider.SambaConfiguration.OnConfigurationChangedListener;
import com.google.android.sambadocumentsprovider.cache.DocumentCache; import com.google.android.sambadocumentsprovider.cache.DocumentCache;
import com.google.android.sambadocumentsprovider.nativefacade.CredentialCache; import com.google.android.sambadocumentsprovider.nativefacade.CredentialCache;
import com.google.android.sambadocumentsprovider.nativefacade.SambaMessageLooper; import com.google.android.sambadocumentsprovider.nativefacade.SambaMessageLooper;
import com.google.android.sambadocumentsprovider.nativefacade.SmbFacade; import com.google.android.sambadocumentsprovider.nativefacade.SmbFacade;
import java.io.File;
public class SambaProviderApplication extends Application { public class SambaProviderApplication extends Application {
private static final String TAG = "SambaProviderApplication";
private final DocumentCache mCache = new DocumentCache(); private final DocumentCache mCache = new DocumentCache();
private final TaskManager mTaskManager = new TaskManager(); private final TaskManager mTaskManager = new TaskManager();
private SmbFacade mSambaClient; private SmbFacade mSambaClient;
private CredentialCache mCredentialCache;
private SambaConfiguration mSambaConf;
private ShareManager mShareManager; private ShareManager mShareManager;
@Override @Override
@ -49,10 +50,6 @@ public class SambaProviderApplication extends Application {
init(this); init(this);
} }
public static void init(Context context) {
((SambaProviderApplication) context.getApplicationContext()).initialize(context);
}
private void initialize(Context context) { private void initialize(Context context) {
if (mSambaClient != null) { if (mSambaClient != null) {
// Already initialized. // Already initialized.
@ -62,30 +59,38 @@ public class SambaProviderApplication extends Application {
initializeSambaConf(context); initializeSambaConf(context);
final SambaMessageLooper looper = new SambaMessageLooper(); final SambaMessageLooper looper = new SambaMessageLooper();
mCredentialCache = looper.getCredentialCache(); CredentialCache credentialCache = looper.getCredentialCache();
mSambaClient = looper.getClient(); mSambaClient = looper.getClient();
mShareManager = new ShareManager(context, mCredentialCache); mShareManager = new ShareManager(context, credentialCache);
registerNetworkCallback(context); registerNetworkCallback(context);
} }
private void initializeSambaConf(Context context) { private void initializeSambaConf(Context context) {
mSambaConf = new SambaConfiguration(context.getDir("home", MODE_PRIVATE)); final File home = context.getDir("home", MODE_PRIVATE);
final File share = context.getExternalFilesDir(null);
final SambaConfiguration sambaConf = new SambaConfiguration(home, share);
// lmhosts are not used in SambaDocumentsProvider and prioritize bcast because sometimes in home final OnConfigurationChangedListener listener = new OnConfigurationChangedListener() {
// settings DNS will resolve unknown domain name to a specific IP for advertisement. @Override
// public void onConfigurationChanged() {
// lmhosts -- lmhosts file if existed side by side to smb.conf if (mSambaClient != null) {
// wins -- Windows Internet Name Service mSambaClient.reset();
// hosts -- hosts file and DNS resolution }
// bcast -- NetBIOS broadcast }
mSambaConf.addConfiguration("name resolve order", "wins bcast hosts"); };
// Urge from users to disable SMB1 by default. // Sync from external folder. The reason why we don't use external folder directly as HOME is
mSambaConf.addConfiguration("client min protocol", "SMB2"); // because there are cases where external storage is not ready, and we don't have an external
mSambaConf.addConfiguration("client max protocol", "SMB3"); // folder at all.
mSambaConf.flushAsDefault(); if (sambaConf.syncFromExternal(listener)) {
if (BuildConfig.DEBUG) Log.d(TAG, "Syncing smb.conf from external folder. No need to try "
+ "flushing default config.");
return;
}
sambaConf.flushDefault(listener);
} }
private void registerNetworkCallback(Context context) { private void registerNetworkCallback(Context context) {
@ -102,7 +107,10 @@ public class SambaProviderApplication extends Application {
mSambaClient.reset(); mSambaClient.reset();
} }
}); });
}
public static void init(Context context) {
getApplication(context).initialize(context);
} }
public static ShareManager getServerManager(Context context) { public static ShareManager getServerManager(Context context) {