
net.oneandone.stool.util.Files Maven / Gradle / Ivy
/**
* Copyright 1&1 Internet AG, https://github.com/1and1/
*
* Licensed 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 net.oneandone.stool.util;
import net.oneandone.sushi.fs.Copy;
import net.oneandone.sushi.fs.ModeException;
import net.oneandone.sushi.fs.Node;
import net.oneandone.sushi.fs.file.FileNode;
import net.oneandone.sushi.fs.filter.Filter;
import net.oneandone.sushi.io.OS;
import net.oneandone.sushi.util.Separator;
import net.oneandone.sushi.util.Substitution;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;
import java.util.Map;
/**
* The method below adjust file permissions.
*
* To think about permissions, it's useful to distinguishes three types for files/directories
* * Source files:
* * group: stool group
* * setgid bit is set for directories
* Almost everything in the stage directory - the only exception is if an application write files into the stage directory.
* Source files include all files generated by the build process (usually in the target directory)
* If the running application can change files with an built-in editor, these files are considered source files,
* even though they were changed by the application
* * Stool files
* * group: stool group
* * directories have the setgid bit set
* * permissions are rw-rw-r--
* everything written by stool - in particular file to configure the running application. All stool files reside in
* the backstage directory, stool does not modify the stage directory.
* * Application files
* * nothing we can guaranty about application files.
* everything written by the running application, e.g. log files; usually, application files are written under
* the backstage directory, but some are accidentally written into the target directory.
*
* Technically important:
* * the setgid bit is used to add the proper group to backstage files
* * when overwriting files in Java, Java changes the content, but not the owner, group or permissions
*/
public final class Files {
/** Creates a directory that's readable for all stool group users. */
public static void createSourceDirectory(PrintWriter log, FileNode dir, String group) throws IOException {
dir.mkdir();
sourceTree(log, dir, group);
}
/** Fixes permissions and group so that all files are stage files. */
public static void sourceTree(PrintWriter log, FileNode dir, String group) throws IOException {
// CAUTION: setPermission doesn't work, see the comment in createBackstageDirectory
exec(log, dir, "chgrp", "-R", group, ".");
exec(log, dir, "sh", "-c", "find . -type d | xargs chmod g+s");
}
public static Node createStoolDirectoryOpt(PrintWriter log, FileNode directory) throws IOException {
if (!directory.isDirectory()) {
createStoolDirectory(log, directory);
}
return directory;
}
/** Creates a directory with mode 2775. Caution - does not work for the stool lib because the group is not set */
public static Node createStoolDirectory(PrintWriter log, FileNode directory) throws IOException {
// The code
// directory.mkdir();
// would inherit the setgid flag from the lib directory, fine. But
// permissions(directory, "rwxrwxr-x");
// would reset setgid. Thus, I have to do it expensively;
exec(log, directory.getParent(), "mkdir", "-m", "2775", directory.getName());
return directory;
}
/**
* Set permissions of a backstage file.
* Assumes the files is owned by the current user (usually because it was just created by us)
* or otherwise already has the proper permissions.
*/
public static Node stoolFile(Node file) throws IOException {
permissions(file, "rw-rw-r--");
return file;
}
/**
* Set permissions of a backstage directory.
* Assumes the directory is owned by the current user (usually because it was just created by us)
* or otherwise already has the proper permissions.
*/
public static FileNode stoolDirectory(PrintWriter log, FileNode dir) throws IOException {
String old;
// TODO: this is expensive, but otherwise, the setgid bit inherited from the lib directory is lost by the previous permissions call.
// see comments in createBackstageDirectory ...
if (OS.CURRENT == OS.MAC) {
old = dir.exec("stat", "-f", "%Op", ".");
// MAC OS returns an addition mode octet, mask it out:
old = Integer.toString(Integer.parseInt(old.trim(), 8) & 07777, 8);
} else {
old = dir.exec("stat", "--format", "%a", ".").trim();
}
if (!old.equals("2775")) {
exec(log, dir, "chmod", "2775", ".");
}
return dir;
}
/**
* CAUTION: assumes that the files is owned by the current user (usually because it was just created by us) or otherwise
* already has the proper permissions.
*/
private static Node stoolExecutable(Node file) throws IOException {
permissions(file, "rwxrwxr-x");
return file;
}
private static void permissions(Node file, String permissions) throws ModeException {
// TODO: if Java overwrites an existing file, ownership and permissions are not changed!
// As a consequence. setPermissions would fail.
// To work-around this, I assume that the original creator of the file has already adjusted permissions;
// or to put it vice versa: if the permissions don't match, the current user is the owner, we can adjust them.
String old;
old = file.getPermissions();
if (!old.equals(permissions)) {
file.setPermissions(permissions);
}
}
/**
* Adjusts permissions for a tree of backstage files. The group is not touched.
*
* CAUTION: assumes that the files is owned by the current user (usually because it was just created by us) or otherwise
* already has the proper permissions.
*/
public static void stoolTree(PrintWriter log, FileNode dir) throws IOException {
stoolDirectory(log, dir);
for (FileNode child : dir.list()) {
if (child.isDirectory()) {
stoolTree(log, child);
} else {
stoolFile(child);
}
}
}
//--
public static void exec(PrintWriter log, FileNode lib, String ... cmd) throws IOException {
log.println("[" + lib + "] " + Separator.SPACE.join(cmd));
lib.execNoOutput(cmd);
}
//-- templates
/** files without keyword substitution */
private static final String[] BINARY_EXTENSIONS = {".keystore", ".war", ".jar", ".gif", ".ico", ".class "};
public static boolean isBinary(String name) {
for (String ext : BINARY_EXTENSIONS) {
if (name.endsWith(ext)) {
return true;
}
}
return false;
}
private static Filter withoutBinary(Filter orig) {
Filter result;
result = new Filter(orig);
for (String ext : BINARY_EXTENSIONS) {
result.exclude("**/*" + ext);
}
return result;
}
//--
private static final Substitution S = new Substitution("${{", "}}", '\\');
public static void template(PrintWriter log, Node src, FileNode dest, Map variables) throws IOException {
Filter selection;
List nodes;
dest.checkDirectory();
// Permissions:
//
// template files are stool files, but some of the directories contain application files
// (e.g. tomcat/conf/Catalina). So we cannot remove the whole directory to create a fresh copy
// (also, we would loose log files by wiping the template) and we cannot simply update permissions
// on all files (via backstageTree), we have to iterate the file actually part of the template.
selection = src.getWorld().filter().includeAll();
nodes = new Copy(src, withoutBinary(selection), false, variables, S).directory(dest);
for (Node node : nodes) {
if (node.isDirectory()) {
stoolDirectory(log, (FileNode) node);
} else {
if (node.getName().endsWith(".sh")) {
stoolExecutable(node);
} else {
stoolFile(node);
}
}
}
for (Node binary : src.find(selection)) {
if (isBinary(binary.getName())) {
Files.stoolFile(binary.copyFile(dest.join(binary.getRelative(src))));
}
}
}
//--
private Files() {
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy