@Override public void output(int id, String line) { // general check if line contains file if (line.contains(fileName)) { // try to match line exactly try { Matcher permissionMatcher = permissionPattern.matcher(line); if (permissionMatcher.find()) { permissions = convertPermissions(permissionMatcher.group(1)); Log.d(RootCommands.TAG, "Found permissions: " + permissions); } else { Log.d(RootCommands.TAG, "Permissions were not found in ls command!"); } // try to parse for symlink Matcher symlinkMatcher = symlinkPattern.matcher(line); if (symlinkMatcher.find()) { /* * TODO: If symlink points to a file in the same directory the path is not * absolute!!! */ symlink = symlinkMatcher.group(1); Log.d(RootCommands.TAG, "Symlink found: " + symlink); } else { Log.d(RootCommands.TAG, "No symlink found!"); } } catch (Exception e) { Log.e(RootCommands.TAG, "Error with regex!", e); } } }
/** * Copys a file to a destination. Because cp is not available on all android devices, we use dd or * cat. * * @param source example: /data/data/org.adaway/files/hosts * @param destination example: /system/etc/hosts * @param remountAsRw remounts the destination as read/write before writing to it * @param preservePermissions tries to copy file attributes from source to destination, if only * cat is available only permissions are preserved * @return true if it was successfully copied * @throws BrokenBusyboxException * @throws IOException * @throws TimeoutException */ public void copyFile( File source, File destination, boolean remountAsRw, boolean preservePermissions) throws FailedExecuteCommand, FileNotFoundException, IOException { /* * dd can only copy files, but we can not check if the source is a file without invoking * shell commands, because from Java we probably have no read access, thus we only check if * they are ending with trailing slashes */ if (source.isDirectory() || destination.isDirectory()) { throw new FileNotFoundException("dd can only copy files!"); } // remount destination as read/write before copying to it if (remountAsRw) { if (!remount(destination, "RW")) { Log.d( RootCommands.TAG, "Remounting failed! There is probably no need to remount this partition!"); } } // get permissions of source before overwriting String permissions = null; if (preservePermissions) { permissions = getFilePermissions(source.getAbsolutePath()); } try { shell.execCommand("toolbox dd if=" + source + " of=" + destination); } catch (FailedExecuteCommand e) { shell.execCommand("toolbox cat " + source + " > " + destination); } // set back permissions from source to destination if (preservePermissions) { setFilePermissions(destination, permissions); } // remount destination back to read only if (remountAsRw) { if (!remount(destination, "RO")) { Log.d( RootCommands.TAG, "Remounting failed! There is probably no need to remount this partition!"); } } }
public LsCommand(String file) { super("toolbox ls -l " + file); // get only filename: this.fileName = (new File(file)).getName(); Log.d(RootCommands.TAG, "fileName: " + fileName); /** * regex to get pid out of ps line, example: * * <pre> * with busybox: * lrwxrwxrwx 1 root root 15 Aug 13 12:14 dev/stdin -> /proc/self/fd/0 * * with toolbox: * lrwxrwxrwx root root 15 Aug 13 12:14 stdin -> /proc/self/fd/0 * * Regex: * ^.*?(\\S{10}) .* $ * </pre> */ permissionRegex = "^.*?(\\S{10}).*$"; permissionPattern = Pattern.compile(permissionRegex); /** * regex to get symlink * * <pre> * -> /proc/self/fd/0 * ^.*?\\-\\> \\s+ (.*) $ * </pre> */ symlinkRegex = "^.*?\\-\\>\\s+(.*)$"; symlinkPattern = Pattern.compile(symlinkRegex); }
/** * This will return an ArrayList of the class Mount. The class mount contains the following * property's: device mountPoint type flags * * <p>These will provide you with any information you need to work with the mount points. * * @return <code>ArrayList<Mount></code> an ArrayList of the class Mount. * @throws Exception if we cannot return the mount points. */ protected static ArrayList<Mount> getMounts() throws Exception { final String tempFile = "/data/local/RootToolsMounts"; // copy /proc/mounts to tempfile. Directly reading it does not work on 4.3 Shell shell = Shell.startRootShell(); Toolbox tb = new Toolbox(shell); tb.copyFile("/proc/mounts", tempFile, false, false); tb.setFilePermissions(tempFile, "777"); shell.close(); LineNumberReader lnr = null; lnr = new LineNumberReader(new FileReader(tempFile)); String line; ArrayList<Mount> mounts = new ArrayList<Mount>(); while ((line = lnr.readLine()) != null) { Log.d(RootCommands.TAG, line); String[] fields = line.split(" "); mounts.add( new Mount( new File(fields[0]), // device new File(fields[1]), // mountPoint fields[2], // fstype fields[3] // flags )); } lnr.close(); return mounts; }
/** * @param file String that represent the file, including the full path to the file and its name. * @return File permissions as String, for example: 777, returns null on error * @throws IOException * @throws TimeoutException * @throws BrokenBusyboxException */ public String getFilePermissions(String file) throws FailedExecuteCommand, BrokenBusyboxException, IOException { Log.d(RootCommands.TAG, "Checking permissions for " + file); String permissions = null; if (fileExists(file)) { Log.d(RootCommands.TAG, file + " was found."); LsCommand lsCommand = new LsCommand(file); shell.execCommand(lsCommand); permissions = lsCommand.getPermissions(); } return permissions; }
/** * This method can be used to kill a running process * * <p>(commands: ps, kill) * * @param processName name of process to kill * @return <code>true</code> if process was found and killed successfully * @throws IOException * @throws TimeoutException * @throws BrokenBusyboxException */ public boolean killAll(String processName) throws FailedExecuteCommand, TimeoutException, IOException { Log.d(RootCommands.TAG, "Killing process " + processName); PsCommand psCommand = new PsCommand(processName); shell.execCommand(psCommand); // kill processes if (!psCommand.getPids().isEmpty()) { // example: kill -9 1234 1222 5343 SimpleCommand killCommand = new SimpleCommand("toolbox kill -9 " + psCommand.getPidsString()); shell.execCommand(killCommand); return killCommand.getExitCode() == 0; } else { Log.d(RootCommands.TAG, "No pid found! Nothing was killed!"); return false; } }
@Override public void output(int id, String line) { // general check if line contains processName if (line.contains(processName)) { Matcher psMatcher = psPattern.matcher(line); // try to match line exactly try { if (psMatcher.find()) { String pid = psMatcher.group(1); // add to pids list pids.add(pid); Log.d(RootCommands.TAG, "Found pid: " + pid); } else { Log.d(RootCommands.TAG, "Matching in ps command failed!"); } } catch (Exception e) { Log.e(RootCommands.TAG, "Error with regex!", e); } } }
/** * This will tell you how the specified mount is mounted. rw, ro, etc... * * @param path The mount you want to check * @return <code>String</code> What the mount is mounted as. * @throws Exception if we cannot determine how the mount is mounted. */ public String getMountedAs(String path) throws Exception { ArrayList<Mount> mounts = Remounter.getMounts(); if (mounts != null) { for (Mount mount : mounts) { if (path.contains(mount.getMountPoint().getAbsolutePath())) { Log.d(RootCommands.TAG, (String) mount.getFlags().toArray()[0]); return (String) mount.getFlags().toArray()[0]; } } } throw new Exception(); }
/** * This will return a String that represent the symlink for a specified file. * * @param file The path to the file to get the Symlink for. (must have absolute path) * @return A String that represent the symlink for a specified file or null if no symlink exists. * @throws IOException * @throws TimeoutException * @throws BrokenBusyboxException */ public String getSymlink(String file) throws FailedExecuteCommand, TimeoutException, IOException { Log.d(RootCommands.TAG, "Find symlink for " + file); String symlink; LsCommand lsCommand = new LsCommand(file); shell.execCommand(lsCommand); symlink = lsCommand.getSymlink(); return symlink; }
/** * Check if there is enough space on partition where target is located * * @param size size of file to put on partition * @param target path where to put the file * @return true if it will fit on partition of target, false if it will not fit. */ public boolean hasEnoughSpaceOnPartition(String target, long size) { try { // new File(target).getFreeSpace() (API 9) is not working on data partition // get directory without file String directory = new File(target).getParent(); StatFs stat = new StatFs(directory); long blockSize = stat.getBlockSize(); long availableBlocks = stat.getAvailableBlocks(); long availableSpace = availableBlocks * blockSize; Log.i( RootCommands.TAG, "Checking for enough space: Target: " + target + ", directory: " + directory + " size: " + size + ", availableSpace: " + availableSpace); if (size < availableSpace) { return true; } else { Log.e(RootCommands.TAG, "Not enough space on partition!"); return false; } } catch (Exception e) { // if new StatFs(directory) fails catch IllegalArgumentException and just return true as // workaround Log.e(RootCommands.TAG, "Problem while getting available space on partition!", e); return true; } }
private Mount findMountPointRecursive(String file) { try { ArrayList<Mount> mounts = getMounts(); for (File path = new File(file); path != null; ) { for (Mount mount : mounts) { if (mount.getMountPoint().equals(path)) { return mount; } } } return null; } catch (IOException e) { throw new RuntimeException(e); } catch (Exception e) { Log.e(RootCommands.TAG, "Exception", e); } return null; }
public void setFilePermissions(File file, String permissions) throws FailedExecuteCommand { Log.d(RootCommands.TAG, "Set permissions of " + file + " to " + permissions); shell.execCommand("toolbox chmod " + permissions + " \"" + file + "\""); }
/** * This will take a path, which can contain the file name as well, and attempt to remount the * underlying partition. * * <p>For example, passing in the following string: * "/system/bin/some/directory/that/really/would/never/exist" will result in /system ultimately * being remounted. However, keep in mind that the longer the path you supply, the more work this * has to do, and the slower it will run. * * @param file file path * @param mountType mount type: pass in RO (Read only) or RW (Read Write) * @return a <code>boolean</code> which indicates whether or not the partition has been remounted * as specified. */ protected boolean remount(String file, String mountType) { // if the path has a trailing slash get rid of it. if (file.endsWith("/") && !file.equals("/")) { file = file.substring(0, file.lastIndexOf("/")); } // Make sure that what we are trying to remount is in the mount list. boolean foundMount = false; while (!foundMount) { try { for (Mount mount : getMounts()) { Log.d(RootCommands.TAG, mount.getMountPoint().toString()); if (file.equals(mount.getMountPoint().toString())) { foundMount = true; break; } } } catch (Exception e) { Log.e(RootCommands.TAG, "Exception", e); return false; } if (!foundMount) { try { file = (new File(file).getParent()).toString(); } catch (Exception e) { Log.e(RootCommands.TAG, "Exception", e); return false; } } } Mount mountPoint = findMountPointRecursive(file); Log.d( RootCommands.TAG, "Remounting " + mountPoint.getMountPoint().getAbsolutePath() + " as " + mountType.toLowerCase(Locale.US)); final boolean isMountMode = mountPoint.getFlags().contains(mountType.toLowerCase(Locale.US)); if (!isMountMode) { // grab an instance of the internal class try { SimpleCommand command = new SimpleCommand( "busybox mount -o remount," + mountType.toLowerCase(Locale.US) + " " + mountPoint.getDevice().getAbsolutePath() + " " + mountPoint.getMountPoint().getAbsolutePath(), "toolbox mount -o remount," + mountType.toLowerCase(Locale.US) + " " + mountPoint.getDevice().getAbsolutePath() + " " + mountPoint.getMountPoint().getAbsolutePath(), "mount -o remount," + mountType.toLowerCase(Locale.US) + " " + mountPoint.getDevice().getAbsolutePath() + " " + mountPoint.getMountPoint().getAbsolutePath(), "/system/bin/toolbox mount -o remount," + mountType.toLowerCase(Locale.US) + " " + mountPoint.getDevice().getAbsolutePath() + " " + mountPoint.getMountPoint().getAbsolutePath()); // execute on shell shell.add(command).waitForFinish(); } catch (Exception e) { } mountPoint = findMountPointRecursive(file); } if (mountPoint != null) { Log.d(RootCommands.TAG, mountPoint.getFlags() + " AND " + mountType.toLowerCase(Locale.US)); if (mountPoint.getFlags().contains(mountType.toLowerCase(Locale.US))) { Log.d(RootCommands.TAG, mountPoint.getFlags().toString()); return true; } else { Log.d(RootCommands.TAG, mountPoint.getFlags().toString()); } } else { Log.d(RootCommands.TAG, "mountPoint is null"); } return false; }