| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * https://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| * |
| */ |
| package org.apache.tools.ant.util; |
| |
| import java.io.IOException; |
| import java.nio.file.Files; |
| import java.nio.file.Path; |
| import java.nio.file.attribute.BasicFileAttributes; |
| import java.nio.file.attribute.PosixFileAttributeView; |
| import java.nio.file.attribute.PosixFilePermission; |
| import java.util.EnumSet; |
| import java.util.Set; |
| import java.util.function.Consumer; |
| import java.util.function.Function; |
| |
| import org.apache.tools.ant.types.Resource; |
| import org.apache.tools.ant.types.resources.ArchiveResource; |
| import org.apache.tools.ant.types.resources.FileProvider; |
| |
| /** |
| * Contains helper methods for dealing with {@link |
| * PosixFilePermission} or the traditional Unix mode representation of |
| * permissions. |
| * |
| * @since Ant 1.10.0 |
| */ |
| public class PermissionUtils { |
| |
| private PermissionUtils() { } |
| |
| /** |
| * Translates a set of permissions into a Unix stat(2) {@code |
| * st_mode} result. |
| * @param permissions the permissions |
| * @param type the file type |
| * @return the "mode" |
| */ |
| public static int modeFromPermissions(Set<PosixFilePermission> permissions, |
| FileType type) { |
| int mode; |
| switch (type) { |
| case SYMLINK: |
| mode = 012; |
| break; |
| case REGULAR_FILE: |
| mode = 010; |
| break; |
| case DIR: |
| mode = 004; |
| break; |
| default: |
| // OTHER could be a character or block device, a socket or a FIFO - so don't set anything |
| mode = 0; |
| break; |
| } |
| mode <<= 3; |
| mode <<= 3; // we don't support sticky, setuid, setgid |
| mode |= modeFromPermissions(permissions, "OWNER"); |
| mode <<= 3; |
| mode |= modeFromPermissions(permissions, "GROUP"); |
| mode <<= 3; |
| mode |= modeFromPermissions(permissions, "OTHERS"); |
| return mode; |
| } |
| |
| /** |
| * Translates a Unix stat(2) {@code st_mode} compatible value into |
| * a set of permissions. |
| * @param mode the "mode" |
| * @return set of permissions |
| */ |
| public static Set<PosixFilePermission> permissionsFromMode(int mode) { |
| Set<PosixFilePermission> permissions = EnumSet.noneOf(PosixFilePermission.class); |
| addPermissions(permissions, "OTHERS", mode); |
| addPermissions(permissions, "GROUP", mode >> 3); |
| addPermissions(permissions, "OWNER", mode >> 6); |
| return permissions; |
| } |
| |
| /** |
| * Sets permissions on a {@link Resource} - doesn't do anything |
| * for unsupported resource types. |
| * |
| * <p>Supported types are:</p> |
| * <ul> |
| * <li>any {@link FileProvider}</li> |
| * <li>{@link ArchiveResource}</li> |
| * </ul> |
| * |
| * @param r the resource to set permissions for |
| * @param permissions the permissions |
| * @param posixNotSupportedCallback optional callback that is |
| * invoked for a file provider resource if the file-system holding |
| * the file doesn't support PosixFilePermissions. The Path |
| * corresponding to the file is passed to the callback. |
| * @throws IOException if something goes wrong |
| */ |
| public static void setPermissions(Resource r, Set<PosixFilePermission> permissions, |
| Consumer<Path> posixNotSupportedCallback) |
| throws IOException { |
| FileProvider f = r.as(FileProvider.class); |
| if (f != null) { |
| Path p = f.getFile().toPath(); |
| PosixFileAttributeView view = |
| Files.getFileAttributeView(p, PosixFileAttributeView.class); |
| if (view != null) { |
| view.setPermissions(permissions); |
| } else if (posixNotSupportedCallback != null) { |
| posixNotSupportedCallback.accept(p); |
| } |
| } else if (r instanceof ArchiveResource) { |
| ((ArchiveResource) r).setMode(modeFromPermissions(permissions, |
| FileType.of(r))); |
| } |
| } |
| |
| /** |
| * Sets permissions of a {@link Resource} - returns an empty set |
| * for unsupported resource types or file systems that don't |
| * support PosixFilePermissions and no fallback has been |
| * provided.. |
| * |
| * <p>Supported types are:</p> |
| * <ul> |
| * <li>any {@link FileProvider}</li> |
| * <li>{@link ArchiveResource}</li> |
| * </ul> |
| * |
| * @param r the resource to read permissions from |
| * @param posixNotSupportedFallback optional fallback function to provide |
| * permissions for file system that don't support |
| * PosixFilePermissions. The Path corresponding to the file is |
| * passed to the callback. |
| * @return the permissions |
| * @throws IOException if something goes wrong |
| */ |
| public static Set<PosixFilePermission> getPermissions(Resource r, |
| Function<Path, Set<PosixFilePermission>> posixNotSupportedFallback) |
| throws IOException { |
| FileProvider f = r.as(FileProvider.class); |
| if (f != null) { |
| Path p = f.getFile().toPath(); |
| PosixFileAttributeView view = |
| Files.getFileAttributeView(p, PosixFileAttributeView.class); |
| if (view != null) { |
| return view.readAttributes().permissions(); |
| } else if (posixNotSupportedFallback != null) { |
| return posixNotSupportedFallback.apply(p); |
| } |
| } else if (r instanceof ArchiveResource) { |
| return permissionsFromMode(((ArchiveResource) r).getMode()); |
| } |
| return EnumSet.noneOf(PosixFilePermission.class); |
| } |
| |
| private static long modeFromPermissions(Set<PosixFilePermission> permissions, |
| String prefix) { |
| long mode = 0; |
| if (permissions.contains(PosixFilePermission.valueOf(prefix + "_READ"))) { |
| mode |= 4; |
| } |
| if (permissions.contains(PosixFilePermission.valueOf(prefix + "_WRITE"))) { |
| mode |= 2; |
| } |
| if (permissions.contains(PosixFilePermission.valueOf(prefix + "_EXECUTE"))) { |
| mode |= 1; |
| } |
| return mode; |
| } |
| |
| private static void addPermissions(Set<PosixFilePermission> permissions, |
| String prefix, long mode) { |
| if ((mode & 1) == 1) { |
| permissions.add(PosixFilePermission.valueOf(prefix + "_EXECUTE")); |
| } |
| if ((mode & 2) == 2) { |
| permissions.add(PosixFilePermission.valueOf(prefix + "_WRITE")); |
| } |
| if ((mode & 4) == 4) { |
| permissions.add(PosixFilePermission.valueOf(prefix + "_READ")); |
| } |
| } |
| |
| /** |
| * The supported types of files, maps to the {@code isFoo} methods |
| * in {@link java.nio.file.attribute.BasicFileAttributes}. |
| */ |
| public enum FileType { |
| /** A regular file. */ |
| REGULAR_FILE, |
| /** A directory. */ |
| DIR, |
| /** A symbolic link. */ |
| SYMLINK, |
| /** Something that is neither a regular file nor a directory nor a symbolic link. */ |
| OTHER; |
| |
| /** |
| * Determines the file type of a {@link Path}. |
| * |
| * @param p Path |
| * @return FileType |
| * @throws IOException if file attributes cannot be read |
| */ |
| public static FileType of(Path p) throws IOException { |
| BasicFileAttributes attrs = |
| Files.readAttributes(p, BasicFileAttributes.class); |
| if (attrs.isRegularFile()) { |
| return FileType.REGULAR_FILE; |
| } else if (attrs.isDirectory()) { |
| return FileType.DIR; |
| } else if (attrs.isSymbolicLink()) { |
| return FileType.SYMLINK; |
| } |
| return FileType.OTHER; |
| } |
| |
| /** |
| * Determines the file type of a {@link Resource}. |
| * |
| * @param r Resource |
| * @return FileType |
| */ |
| public static FileType of(Resource r) { |
| if (r.isDirectory()) { |
| return FileType.DIR; |
| } |
| return FileType.REGULAR_FILE; |
| } |
| } |
| } |