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( 76 Log.LogLevel.ERROR, 77 "Get permission error:\nstdout%s\nstderr", 78 commandResult.getStdout(), 79 commandResult.getStderr()); 80 return ""; 81 } 82 return commandResult.getStdout().trim(); 83 } 84 85 /** 86 * Determines if the permission bits grant a permission to a group. 87 * 88 * @param permissionBits The octal permissions string (e.g. 741). 89 * @param groupIndex The index of the group into the permissions string. (e.g. 0 is owner 90 * group). If set to -1, then all groups are checked. 91 * @param permission The value of the permission. 92 * @return True if the group(s) has read permission. 93 */ hasPermission( String permissionBits, int groupIndex, FilePermission permission)94 private static boolean hasPermission( 95 String permissionBits, int groupIndex, FilePermission permission) { 96 if (groupIndex >= PERMISSION_GROUPS) { 97 throw new RuntimeException(String.format("Invalid group: %s", groupIndex)); 98 } 99 if (permissionBits.length() != PERMISSION_GROUPS) { 100 throw new RuntimeException( 101 String.format("Invalid permission bits: %s", permissionBits.length() + "")); 102 } 103 104 // Define the start/end group index 105 int start = groupIndex; 106 int end = groupIndex + 1; 107 if (groupIndex < 0) { 108 start = 0; 109 end = PERMISSION_GROUPS; 110 } 111 112 for (int i = start; i < end; i++) { 113 try { 114 int perm = Integer.valueOf(permissionBits.charAt(i) + ""); 115 if (perm > 7) { 116 throw new RuntimeException(String.format("Invalid permission bit: %d", perm)); 117 } 118 if ((perm & permission.getPermissionNum()) == 0) { 119 // Return false if any group lacks the permission 120 return false; 121 } 122 } catch (NumberFormatException e) { 123 throw new RuntimeException( 124 String.format( 125 "Permission bits \"%s\" format error, should be three digital" 126 + " number (e.q. 741).", 127 permissionBits)); 128 } 129 } 130 // Return true if no group lacks the permission 131 return true; 132 } 133 134 /** 135 * Helper method which executes a adb shell find command and returns the results as an {@link 136 * ArrayList<String>}. 137 * 138 * @param path The path to search on device. 139 * @param namePattern The file name pattern. 140 * @param options A {@link List} of {@link String} for other options pass to find. 141 * @param device The test device. 142 * @return The result in {@link ArrayList<String>}. 143 * @throws DeviceNotAvailableException if connection with device is lost and cannot be 144 * recovered. 145 */ findFile( String path, String namePattern, List<String> options, ITestDevice device)146 public static ArrayList<String> findFile( 147 String path, String namePattern, List<String> options, ITestDevice device) 148 throws DeviceNotAvailableException { 149 ArrayList<String> findedFiles = new ArrayList<>(); 150 String command = String.format("find %s -name \"%s\"", path, namePattern); 151 if (options != null) { 152 command += " " + String.join(" ", options); 153 } 154 CLog.d("command: %s", command); 155 CommandResult result = device.executeShellV2Command(command); 156 if (!CommandStatus.SUCCESS.equals(result.getStatus())) { 157 CLog.e( 158 "Find command: '%s' failed, returned:\nstdout:%s\nstderr:%s", 159 command, result.getStdout(), result.getStderr()); 160 return findedFiles; 161 } 162 findedFiles = new ArrayList<>(Arrays.asList(result.getStdout().split("\n"))); 163 findedFiles.removeIf(s -> s.contentEquals("")); 164 return findedFiles; 165 } 166 167 /** 168 * Check if the permission for a given path is readonly. 169 * 170 * @param filepath Path to a file or directory. 171 * @param device The test device. 172 * @return true if the path is readonly, false otherwise. 173 */ isReadOnly(String filepath, ITestDevice device)174 public static boolean isReadOnly(String filepath, ITestDevice device) 175 throws DeviceNotAvailableException { 176 String permissionBits = getPermission(filepath, device); 177 return (hasPermission(FilePermission.READ, permissionBits) 178 && !hasPermission(FilePermission.WRITE, permissionBits) 179 && !hasPermission(FilePermission.EXECUTE, permissionBits)); 180 } 181 182 /** 183 * Check if the permission for a given path is readwrite. 184 * 185 * @param filepath Path to a file or directory. 186 * @param device The test device. 187 * @return true if the path is readwrite, false otherwise. 188 */ isReadWriteOnly(String filepath, ITestDevice device)189 public static boolean isReadWriteOnly(String filepath, ITestDevice device) 190 throws DeviceNotAvailableException { 191 String permissionBits = getPermission(filepath, device); 192 return (hasPermission(FilePermission.READ, permissionBits) 193 && hasPermission(FilePermission.WRITE, permissionBits) 194 && !hasPermission(FilePermission.EXECUTE, permissionBits)); 195 } 196 } 197