| @@ -0,0 +1,207 @@ | |||
| /* | |||
| * 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.PosixFilePermission; | |||
| import java.util.EnumSet; | |||
| import java.util.Set; | |||
| 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 | |||
| */ | |||
| public static void setPermissions(Resource r, Set<PosixFilePermission> permissions) | |||
| throws IOException { | |||
| FileProvider f = r.as(FileProvider.class); | |||
| if (f != null) { | |||
| Files.setPosixFilePermissions(f.getFile().toPath(), permissions); | |||
| } else if (r instanceof ArchiveResource) { | |||
| ((ArchiveResource) r).setMode(modeFromPermissions(permissions, | |||
| FileType.of(r))); | |||
| } | |||
| } | |||
| /** | |||
| * Sets permissions of a {@link Resource} - doesn't returns an | |||
| * empty set 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 read permissions from | |||
| * @return the permissions | |||
| */ | |||
| public static Set<PosixFilePermission> getPermissions(Resource r) throws IOException { | |||
| FileProvider f = r.as(FileProvider.class); | |||
| if (f != null) { | |||
| return Files.getPosixFilePermissions(f.getFile().toPath()); | |||
| } 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; | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,155 @@ | |||
| /* | |||
| * 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 static org.junit.Assert.assertEquals; | |||
| import java.io.File; | |||
| import java.io.FileOutputStream; | |||
| import java.io.IOException; | |||
| import java.nio.file.attribute.PosixFilePermission; | |||
| import java.util.EnumSet; | |||
| import java.util.Set; | |||
| import org.apache.tools.ant.types.resources.FileResource; | |||
| import org.apache.tools.ant.types.resources.TarResource; | |||
| import org.apache.tools.ant.types.resources.ZipResource; | |||
| import org.apache.tools.tar.TarEntry; | |||
| import org.apache.tools.tar.TarOutputStream; | |||
| import org.apache.tools.zip.ZipEntry; | |||
| import org.apache.tools.zip.ZipOutputStream; | |||
| import org.junit.Test; | |||
| public class PermissionUtilsTest { | |||
| @Test | |||
| public void modeFromPermissionsReturnsExpectedResult() { | |||
| int mode = PermissionUtils.modeFromPermissions(EnumSet.of(PosixFilePermission.OWNER_READ, | |||
| PosixFilePermission.OWNER_WRITE, | |||
| PosixFilePermission.OWNER_EXECUTE), | |||
| PermissionUtils.FileType.REGULAR_FILE); | |||
| assertEquals("100700", Integer.toString(mode, 8)); | |||
| } | |||
| @Test | |||
| public void permissionsFromModeReturnsExpectedResult() { | |||
| Set<PosixFilePermission> s = PermissionUtils.permissionsFromMode(0100753); | |||
| assertEquals(EnumSet.of(PosixFilePermission.OWNER_READ, | |||
| PosixFilePermission.OWNER_WRITE, | |||
| PosixFilePermission.OWNER_EXECUTE, | |||
| PosixFilePermission.GROUP_READ, | |||
| PosixFilePermission.GROUP_EXECUTE, | |||
| PosixFilePermission.OTHERS_WRITE, | |||
| PosixFilePermission.OTHERS_EXECUTE), | |||
| s); | |||
| } | |||
| @Test | |||
| public void detectsFileTypeOfRegularFileFromPath() throws IOException { | |||
| File f = File.createTempFile("ant", ".tst"); | |||
| f.deleteOnExit(); | |||
| assertEquals(PermissionUtils.FileType.REGULAR_FILE, | |||
| PermissionUtils.FileType.of(f.toPath())); | |||
| } | |||
| @Test | |||
| public void detectsFileTypeOfRegularFileFromResource() throws IOException { | |||
| File f = File.createTempFile("ant", ".tst"); | |||
| f.deleteOnExit(); | |||
| assertEquals(PermissionUtils.FileType.REGULAR_FILE, | |||
| PermissionUtils.FileType.of(new FileResource(f))); | |||
| } | |||
| @Test | |||
| public void detectsFileTypeOfDirectoryFromPath() throws IOException { | |||
| File f = File.createTempFile("ant", ".dir"); | |||
| f.delete(); | |||
| f.mkdirs(); | |||
| f.deleteOnExit(); | |||
| assertEquals(PermissionUtils.FileType.DIR, | |||
| PermissionUtils.FileType.of(f.toPath())); | |||
| } | |||
| @Test | |||
| public void detectsFileTypeOfDirectoryFromResource() throws IOException { | |||
| File f = File.createTempFile("ant", ".tst"); | |||
| f.delete(); | |||
| f.mkdirs(); | |||
| f.deleteOnExit(); | |||
| assertEquals(PermissionUtils.FileType.DIR, | |||
| PermissionUtils.FileType.of(new FileResource(f))); | |||
| } | |||
| @Test | |||
| public void getSetPermissionsWorksForFiles() throws IOException { | |||
| File f = File.createTempFile("ant", ".tst"); | |||
| f.deleteOnExit(); | |||
| Set<PosixFilePermission> s = | |||
| EnumSet.of(PosixFilePermission.OWNER_READ, | |||
| PosixFilePermission.OWNER_WRITE, | |||
| PosixFilePermission.OWNER_EXECUTE, | |||
| PosixFilePermission.GROUP_READ); | |||
| PermissionUtils.setPermissions(new FileResource(f), s); | |||
| assertEquals(s, PermissionUtils.getPermissions(new FileResource(f))); | |||
| } | |||
| @Test | |||
| public void getSetPermissionsWorksForZipResources() throws IOException { | |||
| File f = File.createTempFile("ant", ".zip"); | |||
| f.deleteOnExit(); | |||
| try (ZipOutputStream os = new ZipOutputStream(f)) { | |||
| ZipEntry e = new ZipEntry("foo"); | |||
| os.putNextEntry(e); | |||
| os.closeEntry(); | |||
| } | |||
| ZipResource r = new ZipResource(); | |||
| r.setName("foo"); | |||
| r.setArchive(f); | |||
| Set<PosixFilePermission> s = | |||
| EnumSet.of(PosixFilePermission.OWNER_READ, | |||
| PosixFilePermission.OWNER_WRITE, | |||
| PosixFilePermission.OWNER_EXECUTE, | |||
| PosixFilePermission.GROUP_READ); | |||
| PermissionUtils.setPermissions(r, s); | |||
| assertEquals(s, PermissionUtils.getPermissions(r)); | |||
| } | |||
| @Test | |||
| public void getSetPermissionsWorksForTarResources() throws IOException { | |||
| File f = File.createTempFile("ant", ".zip"); | |||
| f.deleteOnExit(); | |||
| try (TarOutputStream os = new TarOutputStream(new FileOutputStream(f))) { | |||
| TarEntry e = new TarEntry("foo"); | |||
| os.putNextEntry(e); | |||
| os.closeEntry(); | |||
| } | |||
| TarResource r = new TarResource(); | |||
| r.setName("foo"); | |||
| r.setArchive(f); | |||
| Set<PosixFilePermission> s = | |||
| EnumSet.of(PosixFilePermission.OWNER_READ, | |||
| PosixFilePermission.OWNER_WRITE, | |||
| PosixFilePermission.OWNER_EXECUTE, | |||
| PosixFilePermission.GROUP_READ); | |||
| PermissionUtils.setPermissions(r, s); | |||
| assertEquals(s, PermissionUtils.getPermissions(r)); | |||
| } | |||
| } | |||