@@ -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)); | |||||
} | |||||
} |