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 final File mHomeFolder;
private final File mShareFolder;
private final Map<String, String> mConfigurations = new HashMap<>();
SambaConfiguration(File homeFolder) {
SambaConfiguration(File homeFolder, @Nullable File shareFolder) {
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());
}
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);
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);
return this;
}
public synchronized SambaConfiguration removeConfiguration(String key) {
synchronized SambaConfiguration removeConfiguration(String key) {
mConfigurations.remove(key);
return this;
}
public void load(OnConfigurationChangedListener listener) {
new LoadTask(listener).execute();
boolean syncFromExternal(OnConfigurationChangedListener listener) {
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();
}
private synchronized void read() throws IOException {
private synchronized void read(File smbFile) throws IOException {
mConfigurations.clear();
try (BufferedReader reader = new BufferedReader(new FileReader(getSmbFile(mHomeFolder)))) {
try (BufferedReader reader = new BufferedReader(new FileReader(smbFile))) {
String line;
while ((line = reader.readLine()) != null) {
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);
}
private static File getExtSmbFile(File shareFolder) {
return new File(shareFolder, SMB_CONF_FILE);
}
private void setHomeEnv(String absoluteFolder) {
try {
setEnv(HOME_VAR, absoluteFolder);
@ -128,16 +165,18 @@ class SambaConfiguration implements Iterable<Map.Entry<String, String>> {
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 LoadTask(OnConfigurationChangedListener listener) {
private SyncTask(OnConfigurationChangedListener listener) {
mListener = listener;
}
@Override
public Void run(Void... params) throws IOException {
read();
public Void run(File... params) throws IOException {
read(params[0]);
write();
return null;
}
@ -148,9 +187,9 @@ class SambaConfiguration implements Iterable<Map.Entry<String, String>> {
}
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;
}
@ -162,11 +201,9 @@ class SambaConfiguration implements Iterable<Map.Entry<String, String>> {
@Override
public void onSucceeded(Void result) {
if (mListener != null) {
mListener.onConfigurationChanged();
}
}
}
interface OnConfigurationChangedListener {
void onConfigurationChanged();

View file

@ -25,21 +25,22 @@ import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
import android.util.Log;
import com.google.android.sambadocumentsprovider.SambaConfiguration.OnConfigurationChangedListener;
import com.google.android.sambadocumentsprovider.cache.DocumentCache;
import com.google.android.sambadocumentsprovider.nativefacade.CredentialCache;
import com.google.android.sambadocumentsprovider.nativefacade.SambaMessageLooper;
import com.google.android.sambadocumentsprovider.nativefacade.SmbFacade;
import java.io.File;
public class SambaProviderApplication extends Application {
private static final String TAG = "SambaProviderApplication";
private final DocumentCache mCache = new DocumentCache();
private final TaskManager mTaskManager = new TaskManager();
private SmbFacade mSambaClient;
private CredentialCache mCredentialCache;
private SambaConfiguration mSambaConf;
private ShareManager mShareManager;
@Override
@ -49,10 +50,6 @@ public class SambaProviderApplication extends Application {
init(this);
}
public static void init(Context context) {
((SambaProviderApplication) context.getApplicationContext()).initialize(context);
}
private void initialize(Context context) {
if (mSambaClient != null) {
// Already initialized.
@ -62,30 +59,38 @@ public class SambaProviderApplication extends Application {
initializeSambaConf(context);
final SambaMessageLooper looper = new SambaMessageLooper();
mCredentialCache = looper.getCredentialCache();
CredentialCache credentialCache = looper.getCredentialCache();
mSambaClient = looper.getClient();
mShareManager = new ShareManager(context, mCredentialCache);
mShareManager = new ShareManager(context, credentialCache);
registerNetworkCallback(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
// 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
mSambaConf.addConfiguration("name resolve order", "wins bcast hosts");
final OnConfigurationChangedListener listener = new OnConfigurationChangedListener() {
@Override
public void onConfigurationChanged() {
if (mSambaClient != null) {
mSambaClient.reset();
}
}
};
// Urge from users to disable SMB1 by default.
mSambaConf.addConfiguration("client min protocol", "SMB2");
mSambaConf.addConfiguration("client max protocol", "SMB3");
mSambaConf.flushAsDefault();
// Sync from external folder. The reason why we don't use external folder directly as HOME is
// because there are cases where external storage is not ready, and we don't have an external
// folder at all.
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) {
@ -102,7 +107,10 @@ public class SambaProviderApplication extends Application {
mSambaClient.reset();
}
});
}
public static void init(Context context) {
getApplication(context).initialize(context);
}
public static ShareManager getServerManager(Context context) {