Loading core/java/com/android/internal/os/AtomicDirectory.java +81 −72 Original line number Original line Diff line number Diff line Loading @@ -17,15 +17,17 @@ package com.android.internal.os; package com.android.internal.os; import android.annotation.NonNull; import android.annotation.NonNull; import android.annotation.Nullable; import android.os.FileUtils; import android.os.FileUtils; import android.system.ErrnoException; import android.system.Os; import android.system.OsConstants; import android.util.ArrayMap; import android.util.ArrayMap; import android.util.Log; import com.android.internal.util.Preconditions; import com.android.internal.util.Preconditions; import libcore.io.IoUtils; import java.io.File; import java.io.File; import java.io.FileDescriptor; import java.io.FileOutputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.IOException; import java.util.Arrays; import java.util.Arrays; Loading @@ -48,12 +50,13 @@ import java.util.Arrays; * backing directory when checking existence, making changes, and deleting. * backing directory when checking existence, making changes, and deleting. */ */ public final class AtomicDirectory { public final class AtomicDirectory { private final @NonNull ArrayMap<File, FileOutputStream> mOpenFiles = new ArrayMap<>(); private static final String LOG_TAG = AtomicDirectory.class.getSimpleName(); private final @NonNull File mBaseDirectory; private final @NonNull File mBaseDirectory; private final @NonNull File mBackupDirectory; private final @NonNull File mBackupDirectory; private int mBaseDirectoryFd = -1; private final @NonNull ArrayMap<File, FileOutputStream> mOpenFiles = new ArrayMap<>(); private int mBackupDirectoryFd = -1; /** /** * Creates a new instance. * Creates a new instance. Loading @@ -67,15 +70,16 @@ public final class AtomicDirectory { } } /** /** * Gets the backup directory if present. This could be useful if you are * Gets the backup directory which may or may not exist. This could be * writing new state to the dir but need to access the last persisted state * useful if you are writing new state to the directory but need to access * at the same time. This means that this call is useful in between * the last persisted state at the same time. This means that this call is * {@link #startWrite()} and {@link #finishWrite()} or {@link #failWrite()}. * useful in between {@link #startWrite()} and {@link #finishWrite()} or * You should not modify the content returned by this method. * {@link #failWrite()}. You should not modify the content returned by this * method. * * * @see #startRead() * @see #startRead() */ */ public @Nullable File getBackupDirectory() { public @NonNull File getBackupDirectory() { return mBackupDirectory; return mBackupDirectory; } } Loading @@ -90,7 +94,8 @@ public final class AtomicDirectory { */ */ public @NonNull File startRead() throws IOException { public @NonNull File startRead() throws IOException { restore(); restore(); return getOrCreateBaseDirectory(); ensureBaseDirectory(); return mBaseDirectory; } } /** /** Loading @@ -99,10 +104,7 @@ public final class AtomicDirectory { * @see #startRead() * @see #startRead() * @see #startWrite() * @see #startWrite() */ */ public void finishRead() { public void finishRead() {} mBaseDirectoryFd = -1; mBackupDirectoryFd = -1; } /** /** * Starts editing this directory. After calling this method you should * Starts editing this directory. After calling this method you should Loading @@ -121,7 +123,8 @@ public final class AtomicDirectory { */ */ public @NonNull File startWrite() throws IOException { public @NonNull File startWrite() throws IOException { backup(); backup(); return getOrCreateBaseDirectory(); ensureBaseDirectory(); return mBaseDirectory; } } /** /** Loading @@ -135,11 +138,11 @@ public final class AtomicDirectory { * @see #closeWrite(FileOutputStream) * @see #closeWrite(FileOutputStream) */ */ public @NonNull FileOutputStream openWrite(@NonNull File file) throws IOException { public @NonNull FileOutputStream openWrite(@NonNull File file) throws IOException { if (file.isDirectory() || !file.getParentFile().equals(getOrCreateBaseDirectory())) { if (file.isDirectory() || !file.getParentFile().equals(mBaseDirectory)) { throw new IllegalArgumentException("Must be a file in " + getOrCreateBaseDirectory()); throw new IllegalArgumentException("Must be a file in " + mBaseDirectory); } } if (mOpenFiles.containsKey(file)) { if (mOpenFiles.containsKey(file)) { throw new IllegalArgumentException("Already open file" + file.getCanonicalPath()); throw new IllegalArgumentException("Already open file " + file.getAbsolutePath()); } } final FileOutputStream destination = new FileOutputStream(file); final FileOutputStream destination = new FileOutputStream(file); mOpenFiles.put(file, destination); mOpenFiles.put(file, destination); Loading @@ -160,7 +163,7 @@ public final class AtomicDirectory { } } mOpenFiles.removeAt(indexOfValue); mOpenFiles.removeAt(indexOfValue); FileUtils.sync(destination); FileUtils.sync(destination); IoUtils.closeQuietly(destination); FileUtils.closeQuietly(destination); } } public void failWrite(@NonNull FileOutputStream destination) { public void failWrite(@NonNull FileOutputStream destination) { Loading @@ -169,7 +172,7 @@ public final class AtomicDirectory { throw new IllegalArgumentException("Unknown file stream " + destination); throw new IllegalArgumentException("Unknown file stream " + destination); } } mOpenFiles.removeAt(indexOfValue); mOpenFiles.removeAt(indexOfValue); IoUtils.closeQuietly(destination); FileUtils.closeQuietly(destination); } } /** /** Loading @@ -177,15 +180,15 @@ public final class AtomicDirectory { * * * @see #startWrite() * @see #startWrite() * * * @throws IllegalStateException is some files are not closed. * @throws IllegalStateException if some files are not closed. */ */ public void finishWrite() { public void finishWrite() { throwIfSomeFilesOpen(); throwIfSomeFilesOpen(); fsyncDirectoryFd(mBaseDirectoryFd); syncDirectory(mBaseDirectory); syncParentDirectory(); deleteDirectory(mBackupDirectory); deleteDirectory(mBackupDirectory); fsyncDirectoryFd(mBackupDirectoryFd); syncParentDirectory(); mBaseDirectoryFd = -1; mBackupDirectoryFd = -1; } } /** /** Loading @@ -195,11 +198,12 @@ public final class AtomicDirectory { */ */ public void failWrite() { public void failWrite() { throwIfSomeFilesOpen(); throwIfSomeFilesOpen(); try{ try{ restore(); restore(); } catch (IOException ignored) {} } catch (IOException e) { mBaseDirectoryFd = -1; Log.e(LOG_TAG, "Failed to restore in failWrite()", e); mBackupDirectoryFd = -1; } } } /** /** Loading @@ -213,29 +217,28 @@ public final class AtomicDirectory { * Deletes this directory. * Deletes this directory. */ */ public void delete() { public void delete() { boolean deleted = false; if (mBaseDirectory.exists()) { if (mBaseDirectory.exists()) { deleteDirectory(mBaseDirectory); deleted |= deleteDirectory(mBaseDirectory); fsyncDirectoryFd(mBaseDirectoryFd); } } if (mBackupDirectory.exists()) { if (mBackupDirectory.exists()) { deleteDirectory(mBackupDirectory); deleted |= deleteDirectory(mBackupDirectory); fsyncDirectoryFd(mBackupDirectoryFd); } if (deleted) { syncParentDirectory(); } } } } private @NonNull File getOrCreateBaseDirectory() throws IOException { private void ensureBaseDirectory() throws IOException { if (!mBaseDirectory.exists()) { if (mBaseDirectory.exists()) { return; } if (!mBaseDirectory.mkdirs()) { if (!mBaseDirectory.mkdirs()) { throw new IOException("Couldn't create directory " + mBaseDirectory); throw new IOException("Failed to create directory " + mBaseDirectory); } } FileUtils.setPermissions(mBaseDirectory.getPath(), FileUtils.setPermissions(mBaseDirectory.getPath(), FileUtils.S_IRWXU | FileUtils.S_IRWXG | FileUtils.S_IXOTH, FileUtils.S_IRWXU | FileUtils.S_IRWXG | FileUtils.S_IXOTH, -1, -1); -1, -1); } if (mBaseDirectoryFd < 0) { mBaseDirectoryFd = getDirectoryFd(mBaseDirectory.getCanonicalPath()); } return mBaseDirectory; } } private void throwIfSomeFilesOpen() { private void throwIfSomeFilesOpen() { Loading @@ -249,50 +252,56 @@ public final class AtomicDirectory { if (!mBaseDirectory.exists()) { if (!mBaseDirectory.exists()) { return; return; } } if (mBaseDirectoryFd < 0) { mBaseDirectoryFd = getDirectoryFd(mBaseDirectory.getCanonicalPath()); } if (mBackupDirectory.exists()) { if (mBackupDirectory.exists()) { deleteDirectory(mBackupDirectory); deleteDirectory(mBackupDirectory); } } if (!mBaseDirectory.renameTo(mBackupDirectory)) { if (!mBaseDirectory.renameTo(mBackupDirectory)) { throw new IOException("Couldn't backup " + mBaseDirectory throw new IOException("Failed to backup " + mBaseDirectory + " to " + mBackupDirectory); + " to " + mBackupDirectory); } } mBackupDirectoryFd = mBaseDirectoryFd; syncParentDirectory(); mBaseDirectoryFd = -1; fsyncDirectoryFd(mBackupDirectoryFd); } } private void restore() throws IOException { private void restore() throws IOException { if (!mBackupDirectory.exists()) { if (!mBackupDirectory.exists()) { return; return; } } if (mBackupDirectoryFd == -1) { mBackupDirectoryFd = getDirectoryFd(mBackupDirectory.getCanonicalPath()); } if (mBaseDirectory.exists()) { if (mBaseDirectory.exists()) { deleteDirectory(mBaseDirectory); deleteDirectory(mBaseDirectory); } } if (!mBackupDirectory.renameTo(mBaseDirectory)) { if (!mBackupDirectory.renameTo(mBaseDirectory)) { throw new IOException("Couldn't restore " + mBackupDirectory throw new IOException("Failed to restore " + mBackupDirectory + " to " + " to " + mBaseDirectory); + mBaseDirectory); } } mBaseDirectoryFd = mBackupDirectoryFd; syncParentDirectory(); mBackupDirectoryFd = -1; fsyncDirectoryFd(mBaseDirectoryFd); } } private static void deleteDirectory(@NonNull File file) { private static boolean deleteDirectory(@NonNull File directory) { final File[] children = file.listFiles(); return FileUtils.deleteContentsAndDir(directory); if (children != null) { for (File child : children) { deleteDirectory(child); } } } file.delete(); private void syncParentDirectory() { syncDirectory(mBaseDirectory.getParentFile()); } } private static native int getDirectoryFd(String path); // Standard Java IO doesn't allow opening a directory (will throw a FileNotFoundException private static native void fsyncDirectoryFd(int fd); // instead), so we have to do it manually. private static void syncDirectory(@NonNull File directory) { String path = directory.getAbsolutePath(); FileDescriptor fd; try { fd = Os.open(path, OsConstants.O_RDONLY, 0); } catch (ErrnoException e) { Log.e(LOG_TAG, "Failed to open " + path, e); return; } try { Os.fsync(fd); } catch (ErrnoException e) { Log.e(LOG_TAG, "Failed to fsync " + path, e); } finally { FileUtils.closeQuietly(fd); } } } } core/jni/Android.bp +0 −1 Original line number Original line Diff line number Diff line Loading @@ -195,7 +195,6 @@ cc_library_shared { "android_content_res_ObbScanner.cpp", "android_content_res_ObbScanner.cpp", "android_content_res_Configuration.cpp", "android_content_res_Configuration.cpp", "android_security_Scrypt.cpp", "android_security_Scrypt.cpp", "com_android_internal_os_AtomicDirectory.cpp", "com_android_internal_os_ClassLoaderFactory.cpp", "com_android_internal_os_ClassLoaderFactory.cpp", "com_android_internal_os_FuseAppLoop.cpp", "com_android_internal_os_FuseAppLoop.cpp", "com_android_internal_os_Zygote.cpp", "com_android_internal_os_Zygote.cpp", Loading core/jni/AndroidRuntime.cpp +0 −2 Original line number Original line Diff line number Diff line Loading @@ -227,7 +227,6 @@ extern int register_android_content_res_Configuration(JNIEnv* env); extern int register_android_animation_PropertyValuesHolder(JNIEnv *env); extern int register_android_animation_PropertyValuesHolder(JNIEnv *env); extern int register_android_security_Scrypt(JNIEnv *env); extern int register_android_security_Scrypt(JNIEnv *env); extern int register_com_android_internal_content_NativeLibraryHelper(JNIEnv *env); extern int register_com_android_internal_content_NativeLibraryHelper(JNIEnv *env); extern int register_com_android_internal_os_AtomicDirectory(JNIEnv *env); extern int register_com_android_internal_os_ClassLoaderFactory(JNIEnv* env); extern int register_com_android_internal_os_ClassLoaderFactory(JNIEnv* env); extern int register_com_android_internal_os_FuseAppLoop(JNIEnv* env); extern int register_com_android_internal_os_FuseAppLoop(JNIEnv* env); extern int register_com_android_internal_os_Zygote(JNIEnv *env); extern int register_com_android_internal_os_Zygote(JNIEnv *env); Loading Loading @@ -1602,7 +1601,6 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_animation_PropertyValuesHolder), REG_JNI(register_android_animation_PropertyValuesHolder), REG_JNI(register_android_security_Scrypt), REG_JNI(register_android_security_Scrypt), REG_JNI(register_com_android_internal_content_NativeLibraryHelper), REG_JNI(register_com_android_internal_content_NativeLibraryHelper), REG_JNI(register_com_android_internal_os_AtomicDirectory), REG_JNI(register_com_android_internal_os_FuseAppLoop), REG_JNI(register_com_android_internal_os_FuseAppLoop), }; }; Loading core/jni/com_android_internal_os_AtomicDirectory.cppdeleted 100644 → 0 +0 −66 Original line number Original line Diff line number Diff line /* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include <nativehelper/ScopedUtfChars.h> #include "jni.h" #include "core_jni_helpers.h" namespace android { static jint com_android_internal_os_AtomicDirectory_getDirectoryFd(JNIEnv* env, jobject /*clazz*/, jstring path) { ScopedUtfChars path8(env, path); if (path8.c_str() == NULL) { ALOGE("Invalid path: %s", path8.c_str()); return -1; } int fd; if ((fd = TEMP_FAILURE_RETRY(open(path8.c_str(), O_DIRECTORY | O_RDONLY | O_CLOEXEC))) == -1) { ALOGE("Cannot open directory %s, error: %s\n", path8.c_str(), strerror(errno)); return -1; } return fd; } static void com_android_internal_os_AtomicDirectory_fsyncDirectoryFd(JNIEnv* env, jobject /*clazz*/, jint fd) { if (TEMP_FAILURE_RETRY(fsync(fd)) == -1) { ALOGE("Cannot fsync directory %d, error: %s\n", fd, strerror(errno)); } } /* * JNI registration. */ static const JNINativeMethod gRegisterMethods[] = { /* name, signature, funcPtr */ { "fsyncDirectoryFd", "(I)V", (void*) com_android_internal_os_AtomicDirectory_fsyncDirectoryFd }, { "getDirectoryFd", "(Ljava/lang/String;)I", (void*) com_android_internal_os_AtomicDirectory_getDirectoryFd }, }; int register_com_android_internal_os_AtomicDirectory(JNIEnv* env) { return RegisterMethodsOrDie(env, "com/android/internal/os/AtomicDirectory", gRegisterMethods, NELEM(gRegisterMethods)); } }; // namespace android Loading
core/java/com/android/internal/os/AtomicDirectory.java +81 −72 Original line number Original line Diff line number Diff line Loading @@ -17,15 +17,17 @@ package com.android.internal.os; package com.android.internal.os; import android.annotation.NonNull; import android.annotation.NonNull; import android.annotation.Nullable; import android.os.FileUtils; import android.os.FileUtils; import android.system.ErrnoException; import android.system.Os; import android.system.OsConstants; import android.util.ArrayMap; import android.util.ArrayMap; import android.util.Log; import com.android.internal.util.Preconditions; import com.android.internal.util.Preconditions; import libcore.io.IoUtils; import java.io.File; import java.io.File; import java.io.FileDescriptor; import java.io.FileOutputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.IOException; import java.util.Arrays; import java.util.Arrays; Loading @@ -48,12 +50,13 @@ import java.util.Arrays; * backing directory when checking existence, making changes, and deleting. * backing directory when checking existence, making changes, and deleting. */ */ public final class AtomicDirectory { public final class AtomicDirectory { private final @NonNull ArrayMap<File, FileOutputStream> mOpenFiles = new ArrayMap<>(); private static final String LOG_TAG = AtomicDirectory.class.getSimpleName(); private final @NonNull File mBaseDirectory; private final @NonNull File mBaseDirectory; private final @NonNull File mBackupDirectory; private final @NonNull File mBackupDirectory; private int mBaseDirectoryFd = -1; private final @NonNull ArrayMap<File, FileOutputStream> mOpenFiles = new ArrayMap<>(); private int mBackupDirectoryFd = -1; /** /** * Creates a new instance. * Creates a new instance. Loading @@ -67,15 +70,16 @@ public final class AtomicDirectory { } } /** /** * Gets the backup directory if present. This could be useful if you are * Gets the backup directory which may or may not exist. This could be * writing new state to the dir but need to access the last persisted state * useful if you are writing new state to the directory but need to access * at the same time. This means that this call is useful in between * the last persisted state at the same time. This means that this call is * {@link #startWrite()} and {@link #finishWrite()} or {@link #failWrite()}. * useful in between {@link #startWrite()} and {@link #finishWrite()} or * You should not modify the content returned by this method. * {@link #failWrite()}. You should not modify the content returned by this * method. * * * @see #startRead() * @see #startRead() */ */ public @Nullable File getBackupDirectory() { public @NonNull File getBackupDirectory() { return mBackupDirectory; return mBackupDirectory; } } Loading @@ -90,7 +94,8 @@ public final class AtomicDirectory { */ */ public @NonNull File startRead() throws IOException { public @NonNull File startRead() throws IOException { restore(); restore(); return getOrCreateBaseDirectory(); ensureBaseDirectory(); return mBaseDirectory; } } /** /** Loading @@ -99,10 +104,7 @@ public final class AtomicDirectory { * @see #startRead() * @see #startRead() * @see #startWrite() * @see #startWrite() */ */ public void finishRead() { public void finishRead() {} mBaseDirectoryFd = -1; mBackupDirectoryFd = -1; } /** /** * Starts editing this directory. After calling this method you should * Starts editing this directory. After calling this method you should Loading @@ -121,7 +123,8 @@ public final class AtomicDirectory { */ */ public @NonNull File startWrite() throws IOException { public @NonNull File startWrite() throws IOException { backup(); backup(); return getOrCreateBaseDirectory(); ensureBaseDirectory(); return mBaseDirectory; } } /** /** Loading @@ -135,11 +138,11 @@ public final class AtomicDirectory { * @see #closeWrite(FileOutputStream) * @see #closeWrite(FileOutputStream) */ */ public @NonNull FileOutputStream openWrite(@NonNull File file) throws IOException { public @NonNull FileOutputStream openWrite(@NonNull File file) throws IOException { if (file.isDirectory() || !file.getParentFile().equals(getOrCreateBaseDirectory())) { if (file.isDirectory() || !file.getParentFile().equals(mBaseDirectory)) { throw new IllegalArgumentException("Must be a file in " + getOrCreateBaseDirectory()); throw new IllegalArgumentException("Must be a file in " + mBaseDirectory); } } if (mOpenFiles.containsKey(file)) { if (mOpenFiles.containsKey(file)) { throw new IllegalArgumentException("Already open file" + file.getCanonicalPath()); throw new IllegalArgumentException("Already open file " + file.getAbsolutePath()); } } final FileOutputStream destination = new FileOutputStream(file); final FileOutputStream destination = new FileOutputStream(file); mOpenFiles.put(file, destination); mOpenFiles.put(file, destination); Loading @@ -160,7 +163,7 @@ public final class AtomicDirectory { } } mOpenFiles.removeAt(indexOfValue); mOpenFiles.removeAt(indexOfValue); FileUtils.sync(destination); FileUtils.sync(destination); IoUtils.closeQuietly(destination); FileUtils.closeQuietly(destination); } } public void failWrite(@NonNull FileOutputStream destination) { public void failWrite(@NonNull FileOutputStream destination) { Loading @@ -169,7 +172,7 @@ public final class AtomicDirectory { throw new IllegalArgumentException("Unknown file stream " + destination); throw new IllegalArgumentException("Unknown file stream " + destination); } } mOpenFiles.removeAt(indexOfValue); mOpenFiles.removeAt(indexOfValue); IoUtils.closeQuietly(destination); FileUtils.closeQuietly(destination); } } /** /** Loading @@ -177,15 +180,15 @@ public final class AtomicDirectory { * * * @see #startWrite() * @see #startWrite() * * * @throws IllegalStateException is some files are not closed. * @throws IllegalStateException if some files are not closed. */ */ public void finishWrite() { public void finishWrite() { throwIfSomeFilesOpen(); throwIfSomeFilesOpen(); fsyncDirectoryFd(mBaseDirectoryFd); syncDirectory(mBaseDirectory); syncParentDirectory(); deleteDirectory(mBackupDirectory); deleteDirectory(mBackupDirectory); fsyncDirectoryFd(mBackupDirectoryFd); syncParentDirectory(); mBaseDirectoryFd = -1; mBackupDirectoryFd = -1; } } /** /** Loading @@ -195,11 +198,12 @@ public final class AtomicDirectory { */ */ public void failWrite() { public void failWrite() { throwIfSomeFilesOpen(); throwIfSomeFilesOpen(); try{ try{ restore(); restore(); } catch (IOException ignored) {} } catch (IOException e) { mBaseDirectoryFd = -1; Log.e(LOG_TAG, "Failed to restore in failWrite()", e); mBackupDirectoryFd = -1; } } } /** /** Loading @@ -213,29 +217,28 @@ public final class AtomicDirectory { * Deletes this directory. * Deletes this directory. */ */ public void delete() { public void delete() { boolean deleted = false; if (mBaseDirectory.exists()) { if (mBaseDirectory.exists()) { deleteDirectory(mBaseDirectory); deleted |= deleteDirectory(mBaseDirectory); fsyncDirectoryFd(mBaseDirectoryFd); } } if (mBackupDirectory.exists()) { if (mBackupDirectory.exists()) { deleteDirectory(mBackupDirectory); deleted |= deleteDirectory(mBackupDirectory); fsyncDirectoryFd(mBackupDirectoryFd); } if (deleted) { syncParentDirectory(); } } } } private @NonNull File getOrCreateBaseDirectory() throws IOException { private void ensureBaseDirectory() throws IOException { if (!mBaseDirectory.exists()) { if (mBaseDirectory.exists()) { return; } if (!mBaseDirectory.mkdirs()) { if (!mBaseDirectory.mkdirs()) { throw new IOException("Couldn't create directory " + mBaseDirectory); throw new IOException("Failed to create directory " + mBaseDirectory); } } FileUtils.setPermissions(mBaseDirectory.getPath(), FileUtils.setPermissions(mBaseDirectory.getPath(), FileUtils.S_IRWXU | FileUtils.S_IRWXG | FileUtils.S_IXOTH, FileUtils.S_IRWXU | FileUtils.S_IRWXG | FileUtils.S_IXOTH, -1, -1); -1, -1); } if (mBaseDirectoryFd < 0) { mBaseDirectoryFd = getDirectoryFd(mBaseDirectory.getCanonicalPath()); } return mBaseDirectory; } } private void throwIfSomeFilesOpen() { private void throwIfSomeFilesOpen() { Loading @@ -249,50 +252,56 @@ public final class AtomicDirectory { if (!mBaseDirectory.exists()) { if (!mBaseDirectory.exists()) { return; return; } } if (mBaseDirectoryFd < 0) { mBaseDirectoryFd = getDirectoryFd(mBaseDirectory.getCanonicalPath()); } if (mBackupDirectory.exists()) { if (mBackupDirectory.exists()) { deleteDirectory(mBackupDirectory); deleteDirectory(mBackupDirectory); } } if (!mBaseDirectory.renameTo(mBackupDirectory)) { if (!mBaseDirectory.renameTo(mBackupDirectory)) { throw new IOException("Couldn't backup " + mBaseDirectory throw new IOException("Failed to backup " + mBaseDirectory + " to " + mBackupDirectory); + " to " + mBackupDirectory); } } mBackupDirectoryFd = mBaseDirectoryFd; syncParentDirectory(); mBaseDirectoryFd = -1; fsyncDirectoryFd(mBackupDirectoryFd); } } private void restore() throws IOException { private void restore() throws IOException { if (!mBackupDirectory.exists()) { if (!mBackupDirectory.exists()) { return; return; } } if (mBackupDirectoryFd == -1) { mBackupDirectoryFd = getDirectoryFd(mBackupDirectory.getCanonicalPath()); } if (mBaseDirectory.exists()) { if (mBaseDirectory.exists()) { deleteDirectory(mBaseDirectory); deleteDirectory(mBaseDirectory); } } if (!mBackupDirectory.renameTo(mBaseDirectory)) { if (!mBackupDirectory.renameTo(mBaseDirectory)) { throw new IOException("Couldn't restore " + mBackupDirectory throw new IOException("Failed to restore " + mBackupDirectory + " to " + " to " + mBaseDirectory); + mBaseDirectory); } } mBaseDirectoryFd = mBackupDirectoryFd; syncParentDirectory(); mBackupDirectoryFd = -1; fsyncDirectoryFd(mBaseDirectoryFd); } } private static void deleteDirectory(@NonNull File file) { private static boolean deleteDirectory(@NonNull File directory) { final File[] children = file.listFiles(); return FileUtils.deleteContentsAndDir(directory); if (children != null) { for (File child : children) { deleteDirectory(child); } } } file.delete(); private void syncParentDirectory() { syncDirectory(mBaseDirectory.getParentFile()); } } private static native int getDirectoryFd(String path); // Standard Java IO doesn't allow opening a directory (will throw a FileNotFoundException private static native void fsyncDirectoryFd(int fd); // instead), so we have to do it manually. private static void syncDirectory(@NonNull File directory) { String path = directory.getAbsolutePath(); FileDescriptor fd; try { fd = Os.open(path, OsConstants.O_RDONLY, 0); } catch (ErrnoException e) { Log.e(LOG_TAG, "Failed to open " + path, e); return; } try { Os.fsync(fd); } catch (ErrnoException e) { Log.e(LOG_TAG, "Failed to fsync " + path, e); } finally { FileUtils.closeQuietly(fd); } } } }
core/jni/Android.bp +0 −1 Original line number Original line Diff line number Diff line Loading @@ -195,7 +195,6 @@ cc_library_shared { "android_content_res_ObbScanner.cpp", "android_content_res_ObbScanner.cpp", "android_content_res_Configuration.cpp", "android_content_res_Configuration.cpp", "android_security_Scrypt.cpp", "android_security_Scrypt.cpp", "com_android_internal_os_AtomicDirectory.cpp", "com_android_internal_os_ClassLoaderFactory.cpp", "com_android_internal_os_ClassLoaderFactory.cpp", "com_android_internal_os_FuseAppLoop.cpp", "com_android_internal_os_FuseAppLoop.cpp", "com_android_internal_os_Zygote.cpp", "com_android_internal_os_Zygote.cpp", Loading
core/jni/AndroidRuntime.cpp +0 −2 Original line number Original line Diff line number Diff line Loading @@ -227,7 +227,6 @@ extern int register_android_content_res_Configuration(JNIEnv* env); extern int register_android_animation_PropertyValuesHolder(JNIEnv *env); extern int register_android_animation_PropertyValuesHolder(JNIEnv *env); extern int register_android_security_Scrypt(JNIEnv *env); extern int register_android_security_Scrypt(JNIEnv *env); extern int register_com_android_internal_content_NativeLibraryHelper(JNIEnv *env); extern int register_com_android_internal_content_NativeLibraryHelper(JNIEnv *env); extern int register_com_android_internal_os_AtomicDirectory(JNIEnv *env); extern int register_com_android_internal_os_ClassLoaderFactory(JNIEnv* env); extern int register_com_android_internal_os_ClassLoaderFactory(JNIEnv* env); extern int register_com_android_internal_os_FuseAppLoop(JNIEnv* env); extern int register_com_android_internal_os_FuseAppLoop(JNIEnv* env); extern int register_com_android_internal_os_Zygote(JNIEnv *env); extern int register_com_android_internal_os_Zygote(JNIEnv *env); Loading Loading @@ -1602,7 +1601,6 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_animation_PropertyValuesHolder), REG_JNI(register_android_animation_PropertyValuesHolder), REG_JNI(register_android_security_Scrypt), REG_JNI(register_android_security_Scrypt), REG_JNI(register_com_android_internal_content_NativeLibraryHelper), REG_JNI(register_com_android_internal_content_NativeLibraryHelper), REG_JNI(register_com_android_internal_os_AtomicDirectory), REG_JNI(register_com_android_internal_os_FuseAppLoop), REG_JNI(register_com_android_internal_os_FuseAppLoop), }; }; Loading
core/jni/com_android_internal_os_AtomicDirectory.cppdeleted 100644 → 0 +0 −66 Original line number Original line Diff line number Diff line /* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include <nativehelper/ScopedUtfChars.h> #include "jni.h" #include "core_jni_helpers.h" namespace android { static jint com_android_internal_os_AtomicDirectory_getDirectoryFd(JNIEnv* env, jobject /*clazz*/, jstring path) { ScopedUtfChars path8(env, path); if (path8.c_str() == NULL) { ALOGE("Invalid path: %s", path8.c_str()); return -1; } int fd; if ((fd = TEMP_FAILURE_RETRY(open(path8.c_str(), O_DIRECTORY | O_RDONLY | O_CLOEXEC))) == -1) { ALOGE("Cannot open directory %s, error: %s\n", path8.c_str(), strerror(errno)); return -1; } return fd; } static void com_android_internal_os_AtomicDirectory_fsyncDirectoryFd(JNIEnv* env, jobject /*clazz*/, jint fd) { if (TEMP_FAILURE_RETRY(fsync(fd)) == -1) { ALOGE("Cannot fsync directory %d, error: %s\n", fd, strerror(errno)); } } /* * JNI registration. */ static const JNINativeMethod gRegisterMethods[] = { /* name, signature, funcPtr */ { "fsyncDirectoryFd", "(I)V", (void*) com_android_internal_os_AtomicDirectory_fsyncDirectoryFd }, { "getDirectoryFd", "(Ljava/lang/String;)I", (void*) com_android_internal_os_AtomicDirectory_getDirectoryFd }, }; int register_com_android_internal_os_AtomicDirectory(JNIEnv* env) { return RegisterMethodsOrDie(env, "com/android/internal/os/AtomicDirectory", gRegisterMethods, NELEM(gRegisterMethods)); } }; // namespace android