jdk.dio.DevicePermission Maven / Gradle / Ivy
Show all versions of org.openjdk.dio Show documentation
/*
* Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.dio;
import java.security.Permission;
import java.security.PermissionCollection;
import java.util.Enumeration;
import java.util.Vector;
import com.oracle.dio.utils.ExceptionMessage;
import com.oracle.dio.utils.Utils;
import romizer.Local;
/**
* The {@code DevicePermission} abstract class is the superclass of all device permissions.
*
* A {@code DevicePermission} permission has a target name and, optionally, a list of actions.
*
* The target name contains hardware addressing information. It takes the following form:
* ( {controller-spec} ) [ ":" {channel-spec}]
* where {controller-spec} and {channel-spec} are defined as follows:
*
*
* {controller-spec}
* - The
{controller-spec} takes the following form:
* {controller-name-spec} | {controller-number} | "*" | ""
* where {controller-name-spec} and {controller-number} are defined as follows:
*
*
* {controller-name-spec}
* - The
{controller-name-spec} string is the string representation of a controller name as
* may be returned by a call to {@link DeviceConfig.HardwareAddressing#getControllerName
* DeviceConfig.HardwareAddressing.getControllerName}. A controller name is Operating System specific
* such as a device file name on UNIX systems. Occurrences of the semicolon character (
* {@code ":"}) must be escaped with a backslash ({@code "\"}). A {controller-name-spec}
* string that ends with an asterisk ({@code "*"}) is a prefix pattern that matches all the controller
* names starting with the same prefix.
* {controller-number}
* - The
{controller-number} string is the decimal string representation of a controller
* number as may be returned by a call to
* {@link DeviceConfig.HardwareAddressing#getControllerNumber
* DeviceConfig.HardwareAddressing.getControllerNumber}. The characters in the string must all be
* decimal digits.
*
*
* A {controller-spec} specification consisting of the asterisk ({@code "*"}) matches all
* controller names or numbers. A {controller-spec} specification consisting of the empty
* string ({@code ""}) designates an undefined controller name or number that may only be matched by an
* empty string or an asterisk.
* - {channel-spec}
* - The
{channel-spec} takes the following form:
* {channel-desc} | "*" | ""
* where {channel-desc} is defined as follows:
*
*
* {channel-desc}
* - The
{channel-desc} string is device type-specific and must be defined by
* subclasses.
*
*
* A {channel-spec} specification consisting of the asterisk ({@code "*"}) matches all
* channels. A {channel-spec} specification consisting of the empty string ({@code ""})
* designates an undefined channel that may only be matched by an empty string or an asterisk.
*
* The {@code DevicePermission} abstract class treats the {channel-desc} string
* as an opaque string: a {channel-spec} string may therefore only be matched
* by the exact same {channel-spec} string or by the asterisk ({@code "*"}).
*
*
*
* Subclasses of {@code DevicePermission} may defined additional specific target name formats to
* designate devices using their specific hardware addressing information.
*
* The actions to be granted are passed to the constructor in a string containing a list of one or
* more comma-separated keywords. The supported common actions are {@code open} and
* {@code powermanage}. Their meaning is defined as follows:
*
*
* - {@code open}
* - open a device (see {@link DeviceManager#open DeviceManager.open})
* - {@code powermanage}
* - manage the power saving mode of a device (see
* {@link jdk.dio.power.PowerManaged})
*
*
* Additional actions to be granted may be defined by subclasses of
* {@code DevicePermission}.
*
* @see DeviceManager#open DeviceManager.open
* @see jdk.dio.power.PowerManaged
* @since 1.0
*/
@apimarker.API("device-io_1.1")
public abstract class DevicePermission extends Permission {
/**
* The {@code open} action.
*/
public static final String OPEN = "open";
/**
* The {@code powermanage} action.
*/
public static final String POWER_MANAGE = "powermanage";
/**
* Coma-separated action list *
*/
private String myActions;
private String thisDevice;
private String thisChannel;
/**
* Constructs a new {@code DevicePermission} with the specified target name and the implicit
* {@code open} action.
* The target name is normalized so that leading and trailing spaces are removed
* and each occurrence of {controller-number} is represented in its canonical
* decimal representation form (without leading zeros).
*
* @param name the target name (as defined above).
* @throws NullPointerException if {@code name} is {@code null}.
* @throws IllegalArgumentException if {@code name} is not properly formatted.
* @see #getName getName
*/
public DevicePermission(String name) {
// null check
super(name.toString());
String[] ret = Utils.parseDevicePermissionName(name);
thisDevice = ret[0];
thisChannel = ret[1];
myActions = OPEN;
}
/**
* Constructs a new {@code DevicePermission} instance with the specified target name and
* action list.
* The target name is normalized so that leading and trailing spaces are removed
* and each occurrence of {controller-number} is represented in its canonical
* decimal representation form (without leading zeros).
*
* @param name the target name (as defined above).
* @param actions comma-separated list of device operations: {@code open} or {@code powermanage}
* (additional actions may be defined by subclasses).
* @throws NullPointerException if {@code name} is {@code null}.
* @throws IllegalArgumentException
* - if {@code actions} is {@code null}, empty or contains an action other than the
* specified possible actions,
* - if {@code name} is not properly formatted.
*
* @see #getName getName
*/
public DevicePermission(String name, String actions) {
// null check
super(name.toString());
if (null == actions) {
throw new IllegalArgumentException(
ExceptionMessage.format(ExceptionMessage.DEVICE_NULL_ACTIONS)
);
}
String[] ret = Utils.parseDevicePermissionName(name);
thisDevice = ret[0];
thisChannel = ret[1];
myActions = Utils.verifyAndOrderDeviceActions(actions);
}
/**
* Checks two {@code DevicePermission} objects for equality.
* Checks that {@code obj}'s class is the same as this object's class and has the
* same name (as returned by {@link Permission#getName Permission.getName}) and same actions (sorted as per {@link #getActions getActions}) as this object.
*
* @param obj the object to test for equality with this object.
* @return {@code true} if {@code obj}'s class is the same as this object's class and has the same target
* name and actions as this object; {@code false} otherwise.
*/
@Override
public boolean equals(Object obj) {
return equals(this, obj);
}
/**
* Static fucntion for utilization by this class and {@link
* DeviceMgmtPermission}
*
* @param obj1 First object to compare
* @param obj2 Second object to compare
* @return {@code true} if objects are equals, {@code false} otherwise
*/
static boolean equals(Object obj1, Object obj2) {
if (obj1 == obj2) {
return true;
}
if ((obj1 == null) || (obj2 == null) || (obj1.getClass() != obj2.getClass())) {
return false;
}
Permission p1 = (Permission) obj1;
Permission p2 = (Permission) obj2;
return (p1.getName().equals(p2.getName()) && p1.getActions().equals(p2.getActions()));
}
/**
* Returns the list of possible actions in the following order: {@code open}
* or {@code powermanage} (additional actions may be defined by subclasses).
*
* @return comma-separated list of possible actions.
*/
@Override
public String getActions() {
return myActions;
}
/**
* Returns the hash code value for this object. The hash code is calculated
* from this permission's name (as returned by {@link Permission#getName Permission.getName}) and actions (sorted as per {@link #getActions getActions})
* in a way that ensures that {@code permission1.equals(permission2)} implies
* that {@code permission1.hashCode()==permission2.hashCode()} for any two permissions,
* {@code permission1} and {@code permission2}, as required by the general contract of {@link Object#hashCode() Object.hashCode}
* and the contract of {@link Permission#hashCode Permission.hashCode}.
*
* @return a hash code value for this object.
*/
@Override
public int hashCode() {
return (getName() + getActions()).hashCode();
}
/**
* Checks if this object "implies" the specified permission.
*
* More specifically, this method returns {@code true} if:
*
* - {@code permission}'s class is the same as this object's class, and
* - {@code permission}'s actions (as returned by {@link #getActions getActions}) are a proper subset of this object's action list, and
* - {@code permission}'s hardware addressing information or range thereof is included in this
* object's hardware addressing information range; the implementation of this method by
* the {@code DevicePermission} abstract class treats the channel description (
{channel-desc}) string
* as an opaque string: a channel specification ({channel-spec}) string may therefore only be matched
* by the exact same a channel specification string or by the asterisk ({@code "*"}).
*
*
* @param permission the permission to check against.
* @return {@code true} if the specified permission is not {@code null} and is implied by this
* object, {@code false} otherwise.
*/
@Override
public boolean implies(Permission permission) {
if ((permission == null) || (permission.getClass() != getClass())) {
return false;
}
return privateImplies(permission);
}
@Local(WeakDontRenameSubtypes = {"jdk.dio.DevicePermission"})
public String toString() {
return getClass().getName() + " \'" + getName() + "\' " + getActions();
}
boolean privateImplies(Permission permission) {
if (!Utils.implies(getActions(), permission.getActions())) {
return false;
}
String thatDevice = ((DevicePermission) permission).thisDevice;
String thatChannel = ((DevicePermission) permission).thisChannel;
// compare names
if (!"*".equals(thisDevice)) { // if not pure wildcard
if (thisDevice.endsWith("*")) {
// compare "\dev\tty*" and "\dev\*" or "\dev\tty1" and "\dev\*"
if (thisDevice.length() > thatDevice.length() || // wildcard has to be shorter or equals to other name or whildcard
!thatDevice.startsWith(thisDevice.substring(0, thisDevice.length() - 1)) // other name should starts with our wildcard
) {
return false;
}
} else {
if (!thisDevice.equals(thatDevice)) { // other name may not be either wildcard or different name
return false;
}
}
}
if (!"*".equals(thisChannel)) {
// compare channels
if (0 == thisChannel.length() && (0 == thatChannel.length() || "*".equals(thatChannel))) {
//A {channel-spec} specification consisting of the empty string ("") designates an undefined channel
//that may only be matched by an empty string or an asterisk.
return true;
}
// no need to parse as every {channel-spec} limits characters map to either decimal or heximal digits but does not allow to mix them
return thisChannel.equals(thatChannel);
}
//A {channel-spec} specification consisting of the asterisk ("*") matches all channels.
return true;
}
/**
* Returns a new {@code PermissionCollection} for storing {@code DevicePermission} objects.
*
* {@code DevicePermission} objects must be stored in a manner that allows them to be
* inserted into the collection in any order, but that also enables the
* {@link PermissionCollection#implies PermissionCollection.implies} method to be implemented in an efficient (and
* consistent) manner.
*
* For example, assuming a {@code PermissionCollection} object containing
* the two following {@code DevicePermission}s:
*
* -
"adc:1", "powermanage"
* -
"adc:*", "open"
*
*
* when calling the {@code implies} method on that
* {@code PermissionCollection} object with the {@code DevicePermission}:
*
* "adc:1", "open,powermanage",
*
* the {@code implies} method must take into account both the "adc:*"
* and "adc:1" permissions, so the effective permission is
* "open,powermanage", and {@code implies} returns {@code true}. The
* "implies" semantics for {@code DevicePermission}s are handled properly by
* the {@code PermissionCollection} object returned by this method. If a device-specific subclass
* of {@code DevicePermission} defines a different "implies" semantics then
* that subclass must re-implement this method accordingly.
*
*
* @return a new {@code PermissionCollection} suitable for storing {@code DevicePermission}.
*/
@Override
public PermissionCollection newPermissionCollection() {
return new PeripheralPermissionCollection(this.getClass());
}
}
final class LocalPermission extends DevicePermission {
/**
* Comma-separated actions list
*/
private String myActions;
LocalPermission(String name) {
super(name);
myActions = OPEN;
}
LocalPermission(String name, String actionsList) {
//super(name, actionsList);
super(name);
myActions = Utils.verifyAndOrderActions(actionsList, DevicePermission.OPEN + ",data," + DevicePermission.POWER_MANAGE + ",setdirection");
}
@Override
public String getActions() {
return myActions;
}
}
final class PeripheralPermissionCollection extends PermissionCollection {
private final Vector permissions = new Vector<>(6);
/**
* The class to which all PeripheralPermissios in this
* PeripheralPermissionCollection belongs.
*/
private final Class permClazz;
PeripheralPermissionCollection(Class clazz) {
permClazz = clazz;
}
public boolean implies(Permission permission) {
if (!permClazz.isInstance(permission)) {
return false;
}
DevicePermission perm = (DevicePermission) permission;
String[] actionsList = Utils.getActionsList(perm.getActions());
Enumeration search = permissions.elements();
int len = actionsList.length;
if (len <= 1) {
while (search.hasMoreElements()) {
if (search.nextElement().implies(perm)) {
return true;
}
}
} else {
DevicePermission splittedPerm;
boolean implyRes = true;
for (int i = 0; i < len && implyRes; i++) {
boolean res = false;
splittedPerm = new LocalPermission(permission.getName(), actionsList[i]);
search = permissions.elements();
while (search.hasMoreElements()) {
DevicePermission p = search.nextElement();
if (p.privateImplies(splittedPerm)) {
res = true;
break;
}
}
implyRes = implyRes && res;
}
return implyRes;
}
return false;
}
public void add(Permission permission) {
if (!permClazz.isInstance(permission)) {
throw new IllegalArgumentException(
ExceptionMessage.format(ExceptionMessage.DEVICE_INVALID_PERMISSION, permission)
);
}
if (isReadOnly()) {
throw new SecurityException(
ExceptionMessage.format(ExceptionMessage.DEVICE_READONLY_PERMISSION_COLLECTION)
);
}
permissions.addElement((DevicePermission) permission);
}
public Enumeration elements() {
return permissions.elements();
}
}