/* * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of Oracle nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ import java.nio.file.*; import java.nio.file.attribute.*; import static java.nio.file.attribute.PosixFilePermission.*; import static java.nio.file.FileVisitResult.*; import java.io.IOException; import java.util.*; /** * Sample code that changes the permissions of files in a similar manner to the * chmod(1) program. */ public class Chmod { /** * Compiles a list of one or more symbolic mode expressions that * may be used to change a set of file permissions. This method is * intended for use where file permissions are required to be changed in * a manner similar to the UNIX chmod program. * *

The {@code exprs} parameter is a comma separated list of expressions * where each takes the form: *

* who operator [permissions] *
* where who is one or more of the characters {@code 'u'}, {@code 'g'}, * {@code 'o'}, or {@code 'a'} meaning the owner (user), group, others, or * all (owner, group, and others) respectively. * *

operator is the character {@code '+'}, {@code '-'}, or {@code * '='} signifying how permissions are to be changed. {@code '+'} means the * permissions are added, {@code '-'} means the permissions are removed, and * {@code '='} means the permissions are assigned absolutely. * *

permissions is a sequence of zero or more of the following: * {@code 'r'} for read permission, {@code 'w'} for write permission, and * {@code 'x'} for execute permission. If permissions is omitted * when assigned absolutely, then the permissions are cleared for * the owner, group, or others as identified by who. When omitted * when adding or removing then the expression is ignored. * *

The following examples demonstrate possible values for the {@code * exprs} parameter: * * * * * * * * * * * * * * * * * * *
{@code u=rw} Sets the owner permissions to be read and write.
{@code ug+w} Sets the owner write and group write permissions.
{@code u+w,o-rwx} Sets the owner write, and removes the others read, others write * and others execute permissions.
{@code o=} Sets the others permission to none (others read, others write and * others execute permissions are removed if set)
* * @param exprs * List of one or more symbolic mode expressions * * @return A {@code Changer} that may be used to changer a set of * file permissions * * @throws IllegalArgumentException * If the value of the {@code exprs} parameter is invalid */ public static Changer compile(String exprs) { // minimum is who and operator (u= for example) if (exprs.length() < 2) throw new IllegalArgumentException("Invalid mode"); // permissions that the changer will add or remove final Set toAdd = new HashSet(); final Set toRemove = new HashSet(); // iterate over each of expression modes for (String expr: exprs.split(",")) { // minimum of who and operator if (expr.length() < 2) throw new IllegalArgumentException("Invalid mode"); int pos = 0; // who boolean u = false; boolean g = false; boolean o = false; boolean done = false; for (;;) { switch (expr.charAt(pos)) { case 'u' : u = true; break; case 'g' : g = true; break; case 'o' : o = true; break; case 'a' : u = true; g = true; o = true; break; default : done = true; } if (done) break; pos++; } if (!u && !g && !o) throw new IllegalArgumentException("Invalid mode"); // get operator and permissions char op = expr.charAt(pos++); String mask = (expr.length() == pos) ? "" : expr.substring(pos); // operator boolean add = (op == '+'); boolean remove = (op == '-'); boolean assign = (op == '='); if (!add && !remove && !assign) throw new IllegalArgumentException("Invalid mode"); // who= means remove all if (assign && mask.length() == 0) { assign = false; remove = true; mask = "rwx"; } // permissions boolean r = false; boolean w = false; boolean x = false; for (int i=0; i change(Set perms) { perms.addAll(toAdd); perms.removeAll(toRemove); return perms; } }; } /** * A task that changes a set of {@link PosixFilePermission} elements. */ public interface Changer { /** * Applies the changes to the given set of permissions. * * @param perms * The set of permissions to change * * @return The {@code perms} parameter */ Set change(Set perms); } /** * Changes the permissions of the file using the given Changer. */ static void chmod(Path file, Changer changer) { try { Set perms = Files.getPosixFilePermissions(file); Files.setPosixFilePermissions(file, changer.change(perms)); } catch (IOException x) { System.err.println(x); } } /** * Changes the permission of each file and directory visited */ static class TreeVisitor implements FileVisitor { private final Changer changer; TreeVisitor(Changer changer) { this.changer = changer; } @Override public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) { chmod(dir, changer); return CONTINUE; } @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) { chmod(file, changer); return CONTINUE; } @Override public FileVisitResult postVisitDirectory(Path dir, IOException exc) { if (exc != null) System.err.println("WARNING: " + exc); return CONTINUE; } @Override public FileVisitResult visitFileFailed(Path file, IOException exc) { System.err.println("WARNING: " + exc); return CONTINUE; } } static void usage() { System.err.println("java Chmod [-R] symbolic-mode-list file..."); System.exit(-1); } public static void main(String[] args) throws IOException { if (args.length < 2) usage(); int argi = 0; int maxDepth = 0; if (args[argi].equals("-R")) { if (args.length < 3) usage(); argi++; maxDepth = Integer.MAX_VALUE; } // compile the symbolic mode expressions Changer changer = compile(args[argi++]); TreeVisitor visitor = new TreeVisitor(changer); Set opts = Collections.emptySet(); while (argi < args.length) { Path file = Paths.get(args[argi]); Files.walkFileTree(file, opts, maxDepth, visitor); argi++; } } }