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