1 /* 2 * Copyright (C) 2024 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.net.ct; 18 19 import android.annotation.SuppressLint; 20 21 import java.io.File; 22 import java.io.IOException; 23 24 /** Utility class to manipulate CT directories. */ 25 class DirectoryUtils { 26 makeDir(File dir)27 static void makeDir(File dir) throws IOException { 28 dir.mkdir(); 29 if (!dir.isDirectory()) { 30 throw new IOException("Unable to make directory " + dir.getCanonicalPath()); 31 } 32 setWorldReadable(dir); 33 // Needed for the log list file to be accessible. 34 setWorldExecutable(dir); 35 } 36 37 // CT files and directories are readable by all apps. 38 @SuppressLint("SetWorldReadable") setWorldReadable(File file)39 static void setWorldReadable(File file) throws IOException { 40 if (!file.setReadable(/* readable= */ true, /* ownerOnly= */ false)) { 41 throw new IOException("Failed to set " + file.getCanonicalPath() + " readable"); 42 } 43 } 44 45 // CT directories are executable by all apps, to allow access to the log list by anything on the 46 // device. setWorldExecutable(File file)47 static void setWorldExecutable(File file) throws IOException { 48 if (!file.isDirectory()) { 49 // Only directories need to be marked as executable to allow for access 50 // to the files inside. 51 // See https://www.redhat.com/en/blog/linux-file-permissions-explained for more details. 52 return; 53 } 54 55 if (!file.setExecutable(/* executable= */ true, /* ownerOnly= */ false)) { 56 throw new IOException("Failed to set " + file.getCanonicalPath() + " executable"); 57 } 58 } 59 removeDir(File dir)60 static boolean removeDir(File dir) { 61 return deleteContentsAndDir(dir); 62 } 63 deleteContentsAndDir(File dir)64 private static boolean deleteContentsAndDir(File dir) { 65 if (deleteContents(dir)) { 66 return dir.delete(); 67 } else { 68 return false; 69 } 70 } 71 deleteContents(File dir)72 private static boolean deleteContents(File dir) { 73 File[] files = dir.listFiles(); 74 boolean success = true; 75 if (files != null) { 76 for (File file : files) { 77 if (file.isDirectory()) { 78 success &= deleteContents(file); 79 } 80 if (!file.delete()) { 81 success = false; 82 } 83 } 84 } 85 return success; 86 } 87 } 88