1 /* 2 * Copyright (C) 2020 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 /* Utils for target file.*/ 18 package com.android.tradefed.util; 19 20 import com.android.ddmlib.Log; 21 import com.android.tradefed.device.DeviceNotAvailableException; 22 import com.android.tradefed.device.ITestDevice; 23 import com.android.tradefed.log.LogUtil.CLog; 24 import java.util.ArrayList; 25 import java.util.Arrays; 26 import java.util.List; 27 28 public class TargetFileUtils { 29 // 3 permission groups: owner, group, all users 30 private static final int PERMISSION_GROUPS = 3; 31 32 public enum FilePermission { 33 EXECUTE(1), 34 READ(4), 35 WRITE(2); 36 37 private int mPermissionNum; 38 FilePermission(int permissionNum)39 FilePermission(int permissionNum) { 40 mPermissionNum = permissionNum; 41 } 42 getPermissionNum()43 public int getPermissionNum() { 44 return mPermissionNum; 45 } 46 } 47 48 /** 49 * Determines if the permission bits grant the specify permission to any group. 50 * 51 * @param permission The specify permissions. 52 * @param permissionBits The octal permissions string (e.g. 741). 53 * @return True if any owner/group/global has the specify permission. 54 */ hasPermission(FilePermission permission, String permissionBits)55 public static boolean hasPermission(FilePermission permission, String permissionBits) { 56 for (int i = 0; i < PERMISSION_GROUPS; i++) { 57 if (hasPermission(permissionBits, i, permission)) { 58 return true; 59 } 60 } 61 return false; 62 } 63 64 /** 65 * Read the file permission bits of a path. 66 * 67 * @param filepath Path to a file or directory. 68 * @param device The test device. 69 * @return Octal permission bits for the path. 70 */ getPermission(String filepath, ITestDevice device)71 public static String getPermission(String filepath, ITestDevice device) 72 throws DeviceNotAvailableException { 73 CommandResult commandResult = device.executeShellV2Command("stat -c %a " + filepath); 74 if (!CommandStatus.SUCCESS.equals(commandResult.getStatus())) { 75 CLog.logAndDisplay(Log.LogLevel.ERROR, "Get permission error:\nstdout%s\nstderr", 76 commandResult.getStdout(), commandResult.getStderr()); 77 return ""; 78 } 79 return commandResult.getStdout().trim(); 80 } 81 82 /** 83 * Determines if the permission bits grant a permission to a group. 84 * 85 * @param permissionBits The octal permissions string (e.g. 741). 86 * @param groupIndex The index of the group into the permissions string. 87 * (e.g. 0 is owner group). If set to -1, then all groups are 88 * checked. 89 * @param permission The value of the permission. 90 * @return True if the group(s) has read permission. 91 */ hasPermission( String permissionBits, int groupIndex, FilePermission permission)92 private static boolean hasPermission( 93 String permissionBits, int groupIndex, FilePermission permission) { 94 if (groupIndex >= PERMISSION_GROUPS) { 95 throw new RuntimeException(String.format("Invalid group: %s", groupIndex)); 96 } 97 if (permissionBits.length() != PERMISSION_GROUPS) { 98 throw new RuntimeException( 99 String.format("Invalid permission bits: %s", permissionBits.length() + "")); 100 } 101 102 // Define the start/end group index 103 int start = groupIndex; 104 int end = groupIndex + 1; 105 if (groupIndex < 0) { 106 start = 0; 107 end = PERMISSION_GROUPS; 108 } 109 110 for (int i = start; i < end; i++) { 111 try { 112 int perm = Integer.valueOf(permissionBits.charAt(i) + ""); 113 if (perm > 7) { 114 throw new RuntimeException(String.format("Invalid permission bit: %d", perm)); 115 } 116 if ((perm & permission.getPermissionNum()) == 0) { 117 // Return false if any group lacks the permission 118 return false; 119 } 120 } catch (NumberFormatException e) { 121 throw new RuntimeException(String.format( 122 "Permission bits \"%s\" format error, should be three digital number " 123 + "(e.q. 741).", 124 permissionBits)); 125 } 126 } 127 // Return true if no group lacks the permission 128 return true; 129 } 130 131 /** 132 * Helper method which executes a adb shell find command and returns the results as an {@link 133 * ArrayList<String>}. 134 * 135 * @param path The path to search on device. 136 * @param namePattern The file name pattern. 137 * @param options A {@link List} of {@link String} for other options pass to find. 138 * @param device The test device. 139 * @return The result in {@link ArrayList<String>}. 140 * @throws DeviceNotAvailableException if connection with device is lost and cannot be 141 * recovered. 142 */ findFile(String path, String namePattern, List<String> options, ITestDevice device)143 public static ArrayList<String> findFile(String path, String namePattern, List<String> options, 144 ITestDevice device) throws DeviceNotAvailableException { 145 ArrayList<String> findedFiles = new ArrayList<>(); 146 String command = String.format("find %s -name \"%s\"", path, namePattern); 147 if (options != null) { 148 command += " " + String.join(" ", options); 149 } 150 CLog.d("command: %s", command); 151 CommandResult result = device.executeShellV2Command(command); 152 if (!CommandStatus.SUCCESS.equals(result.getStatus())) { 153 CLog.e("Find command: '%s' failed, returned:\nstdout:%s\nstderr:%s", command, 154 result.getStdout(), result.getStderr()); 155 return findedFiles; 156 } 157 findedFiles = new ArrayList<>(Arrays.asList(result.getStdout().split("\n"))); 158 findedFiles.removeIf(s -> s.contentEquals("")); 159 return findedFiles; 160 } 161 162 /** 163 * Check if the permission for a given path is readonly. 164 * 165 * @param filepath Path to a file or directory. 166 * @param device The test device. 167 * @return true if the path is readonly, false otherwise. 168 */ isReadOnly(String filepath, ITestDevice device)169 public static boolean isReadOnly(String filepath, ITestDevice device) 170 throws DeviceNotAvailableException { 171 String permissionBits = getPermission(filepath, device); 172 return (hasPermission(FilePermission.READ, permissionBits) 173 && !hasPermission(FilePermission.WRITE, permissionBits) 174 && !hasPermission(FilePermission.EXECUTE, permissionBits)); 175 } 176 177 /** 178 * Check if the permission for a given path is readwrite. 179 * 180 * @param filepath Path to a file or directory. 181 * @param device The test device. 182 * @return true if the path is readwrite, false otherwise. 183 */ isReadWriteOnly(String filepath, ITestDevice device)184 public static boolean isReadWriteOnly(String filepath, ITestDevice device) 185 throws DeviceNotAvailableException { 186 String permissionBits = getPermission(filepath, device); 187 return (hasPermission(FilePermission.READ, permissionBits) 188 && hasPermission(FilePermission.WRITE, permissionBits) 189 && !hasPermission(FilePermission.EXECUTE, permissionBits)); 190 } 191 } 192