/* * 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 * * http://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 permissons 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 resource 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. */ 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 resource 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 */ 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}. */ 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}. */ public static FileType of(Resource r) { if (r.isDirectory()) { return FileType.DIR; } return FileType.REGULAR_FILE; } } }