893N/A/*
3909N/A * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
893N/A *
893N/A * Redistribution and use in source and binary forms, with or without
893N/A * modification, are permitted provided that the following conditions
893N/A * are met:
893N/A *
893N/A * - Redistributions of source code must retain the above copyright
893N/A * notice, this list of conditions and the following disclaimer.
893N/A *
893N/A * - Redistributions in binary form must reproduce the above copyright
893N/A * notice, this list of conditions and the following disclaimer in the
893N/A * documentation and/or other materials provided with the distribution.
893N/A *
2362N/A * - Neither the name of Oracle nor the names of its
893N/A * contributors may be used to endorse or promote products derived
893N/A * from this software without specific prior written permission.
893N/A *
893N/A * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
893N/A * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
893N/A * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
893N/A * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
893N/A * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
893N/A * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
893N/A * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
893N/A * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
893N/A * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
893N/A * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
893N/A * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
893N/A */
893N/A
4378N/A/*
4378N/A * This source code is provided to illustrate the usage of a given feature
4378N/A * or technique and has been deliberately simplified. Additional steps
4378N/A * required for a production-quality application, such as security checks,
4378N/A * input validation and proper error handling, might not be present in
4378N/A * this sample code.
4378N/A */
4378N/A
4378N/A
893N/Aimport java.nio.file.*;
893N/Aimport java.nio.file.attribute.*;
893N/Aimport static java.nio.file.attribute.PosixFilePermission.*;
893N/Aimport static java.nio.file.FileVisitResult.*;
893N/Aimport java.io.IOException;
893N/Aimport java.util.*;
893N/A
893N/A/**
893N/A * Sample code that changes the permissions of files in a similar manner to the
893N/A * chmod(1) program.
893N/A */
893N/A
893N/Apublic class Chmod {
893N/A
893N/A /**
893N/A * Compiles a list of one or more <em>symbolic mode expressions</em> that
893N/A * may be used to change a set of file permissions. This method is
893N/A * intended for use where file permissions are required to be changed in
893N/A * a manner similar to the UNIX <i>chmod</i> program.
893N/A *
893N/A * <p> The {@code exprs} parameter is a comma separated list of expressions
893N/A * where each takes the form:
893N/A * <blockquote>
893N/A * <i>who operator</i> [<i>permissions</i>]
893N/A * </blockquote>
893N/A * where <i>who</i> is one or more of the characters {@code 'u'}, {@code 'g'},
893N/A * {@code 'o'}, or {@code 'a'} meaning the owner (user), group, others, or
893N/A * all (owner, group, and others) respectively.
893N/A *
893N/A * <p> <i>operator</i> is the character {@code '+'}, {@code '-'}, or {@code
893N/A * '='} signifying how permissions are to be changed. {@code '+'} means the
893N/A * permissions are added, {@code '-'} means the permissions are removed, and
893N/A * {@code '='} means the permissions are assigned absolutely.
893N/A *
893N/A * <p> <i>permissions</i> is a sequence of zero or more of the following:
893N/A * {@code 'r'} for read permission, {@code 'w'} for write permission, and
893N/A * {@code 'x'} for execute permission. If <i>permissions</i> is omitted
893N/A * when assigned absolutely, then the permissions are cleared for
893N/A * the owner, group, or others as identified by <i>who</i>. When omitted
893N/A * when adding or removing then the expression is ignored.
893N/A *
893N/A * <p> The following examples demonstrate possible values for the {@code
893N/A * exprs} parameter:
893N/A *
893N/A * <table border="0">
893N/A * <tr>
893N/A * <td> {@code u=rw} </td>
893N/A * <td> Sets the owner permissions to be read and write. </td>
893N/A * </tr>
893N/A * <tr>
893N/A * <td> {@code ug+w} </td>
893N/A * <td> Sets the owner write and group write permissions. </td>
893N/A * </tr>
893N/A * <tr>
893N/A * <td> {@code u+w,o-rwx} </td>
893N/A * <td> Sets the owner write, and removes the others read, others write
893N/A * and others execute permissions. </td>
893N/A * </tr>
893N/A * <tr>
893N/A * <td> {@code o=} </td>
893N/A * <td> Sets the others permission to none (others read, others write and
893N/A * others execute permissions are removed if set) </td>
893N/A * </tr>
893N/A * </table>
893N/A *
893N/A * @param exprs
893N/A * List of one or more <em>symbolic mode expressions</em>
893N/A *
893N/A * @return A {@code Changer} that may be used to changer a set of
893N/A * file permissions
893N/A *
893N/A * @throws IllegalArgumentException
893N/A * If the value of the {@code exprs} parameter is invalid
893N/A */
893N/A public static Changer compile(String exprs) {
893N/A // minimum is who and operator (u= for example)
893N/A if (exprs.length() < 2)
893N/A throw new IllegalArgumentException("Invalid mode");
893N/A
893N/A // permissions that the changer will add or remove
893N/A final Set<PosixFilePermission> toAdd = new HashSet<PosixFilePermission>();
893N/A final Set<PosixFilePermission> toRemove = new HashSet<PosixFilePermission>();
893N/A
893N/A // iterate over each of expression modes
893N/A for (String expr: exprs.split(",")) {
893N/A // minimum of who and operator
893N/A if (expr.length() < 2)
893N/A throw new IllegalArgumentException("Invalid mode");
893N/A
893N/A int pos = 0;
893N/A
893N/A // who
893N/A boolean u = false;
893N/A boolean g = false;
893N/A boolean o = false;
893N/A boolean done = false;
893N/A for (;;) {
893N/A switch (expr.charAt(pos)) {
893N/A case 'u' : u = true; break;
893N/A case 'g' : g = true; break;
893N/A case 'o' : o = true; break;
893N/A case 'a' : u = true; g = true; o = true; break;
893N/A default : done = true;
893N/A }
893N/A if (done)
893N/A break;
893N/A pos++;
893N/A }
893N/A if (!u && !g && !o)
893N/A throw new IllegalArgumentException("Invalid mode");
893N/A
893N/A // get operator and permissions
893N/A char op = expr.charAt(pos++);
893N/A String mask = (expr.length() == pos) ? "" : expr.substring(pos);
893N/A
893N/A // operator
893N/A boolean add = (op == '+');
893N/A boolean remove = (op == '-');
893N/A boolean assign = (op == '=');
893N/A if (!add && !remove && !assign)
893N/A throw new IllegalArgumentException("Invalid mode");
893N/A
893N/A // who= means remove all
893N/A if (assign && mask.length() == 0) {
893N/A assign = false;
893N/A remove = true;
893N/A mask = "rwx";
893N/A }
893N/A
893N/A // permissions
893N/A boolean r = false;
893N/A boolean w = false;
893N/A boolean x = false;
893N/A for (int i=0; i<mask.length(); i++) {
893N/A switch (mask.charAt(i)) {
893N/A case 'r' : r = true; break;
893N/A case 'w' : w = true; break;
893N/A case 'x' : x = true; break;
893N/A default:
893N/A throw new IllegalArgumentException("Invalid mode");
893N/A }
893N/A }
893N/A
893N/A // update permissions set
893N/A if (add) {
893N/A if (u) {
893N/A if (r) toAdd.add(OWNER_READ);
893N/A if (w) toAdd.add(OWNER_WRITE);
893N/A if (x) toAdd.add(OWNER_EXECUTE);
893N/A }
893N/A if (g) {
893N/A if (r) toAdd.add(GROUP_READ);
893N/A if (w) toAdd.add(GROUP_WRITE);
893N/A if (x) toAdd.add(GROUP_EXECUTE);
893N/A }
893N/A if (o) {
893N/A if (r) toAdd.add(OTHERS_READ);
893N/A if (w) toAdd.add(OTHERS_WRITE);
893N/A if (x) toAdd.add(OTHERS_EXECUTE);
893N/A }
893N/A }
893N/A if (remove) {
893N/A if (u) {
893N/A if (r) toRemove.add(OWNER_READ);
893N/A if (w) toRemove.add(OWNER_WRITE);
893N/A if (x) toRemove.add(OWNER_EXECUTE);
893N/A }
893N/A if (g) {
893N/A if (r) toRemove.add(GROUP_READ);
893N/A if (w) toRemove.add(GROUP_WRITE);
893N/A if (x) toRemove.add(GROUP_EXECUTE);
893N/A }
893N/A if (o) {
893N/A if (r) toRemove.add(OTHERS_READ);
893N/A if (w) toRemove.add(OTHERS_WRITE);
893N/A if (x) toRemove.add(OTHERS_EXECUTE);
893N/A }
893N/A }
893N/A if (assign) {
893N/A if (u) {
893N/A if (r) toAdd.add(OWNER_READ);
893N/A else toRemove.add(OWNER_READ);
893N/A if (w) toAdd.add(OWNER_WRITE);
893N/A else toRemove.add(OWNER_WRITE);
893N/A if (x) toAdd.add(OWNER_EXECUTE);
893N/A else toRemove.add(OWNER_EXECUTE);
893N/A }
893N/A if (g) {
893N/A if (r) toAdd.add(GROUP_READ);
893N/A else toRemove.add(GROUP_READ);
893N/A if (w) toAdd.add(GROUP_WRITE);
893N/A else toRemove.add(GROUP_WRITE);
893N/A if (x) toAdd.add(GROUP_EXECUTE);
893N/A else toRemove.add(GROUP_EXECUTE);
893N/A }
893N/A if (o) {
893N/A if (r) toAdd.add(OTHERS_READ);
893N/A else toRemove.add(OTHERS_READ);
893N/A if (w) toAdd.add(OTHERS_WRITE);
893N/A else toRemove.add(OTHERS_WRITE);
893N/A if (x) toAdd.add(OTHERS_EXECUTE);
893N/A else toRemove.add(OTHERS_EXECUTE);
893N/A }
893N/A }
893N/A }
893N/A
893N/A // return changer
893N/A return new Changer() {
893N/A @Override
893N/A public Set<PosixFilePermission> change(Set<PosixFilePermission> perms) {
893N/A perms.addAll(toAdd);
893N/A perms.removeAll(toRemove);
893N/A return perms;
893N/A }
893N/A };
893N/A }
893N/A
893N/A /**
893N/A * A task that <i>changes</i> a set of {@link PosixFilePermission} elements.
893N/A */
893N/A public interface Changer {
893N/A /**
893N/A * Applies the changes to the given set of permissions.
893N/A *
893N/A * @param perms
893N/A * The set of permissions to change
893N/A *
893N/A * @return The {@code perms} parameter
893N/A */
893N/A Set<PosixFilePermission> change(Set<PosixFilePermission> perms);
893N/A }
893N/A
893N/A /**
893N/A * Changes the permissions of the file using the given Changer.
893N/A */
3471N/A static void chmod(Path file, Changer changer) {
893N/A try {
3471N/A Set<PosixFilePermission> perms = Files.getPosixFilePermissions(file);
3471N/A Files.setPosixFilePermissions(file, changer.change(perms));
893N/A } catch (IOException x) {
893N/A System.err.println(x);
893N/A }
893N/A }
893N/A
893N/A /**
893N/A * Changes the permission of each file and directory visited
893N/A */
3471N/A static class TreeVisitor implements FileVisitor<Path> {
893N/A private final Changer changer;
893N/A
893N/A TreeVisitor(Changer changer) {
893N/A this.changer = changer;
893N/A }
893N/A
893N/A @Override
3471N/A public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) {
893N/A chmod(dir, changer);
893N/A return CONTINUE;
893N/A }
893N/A
893N/A @Override
3471N/A public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
893N/A chmod(file, changer);
893N/A return CONTINUE;
893N/A }
893N/A
893N/A @Override
3471N/A public FileVisitResult postVisitDirectory(Path dir, IOException exc) {
893N/A if (exc != null)
893N/A System.err.println("WARNING: " + exc);
893N/A return CONTINUE;
893N/A }
893N/A
893N/A @Override
3471N/A public FileVisitResult visitFileFailed(Path file, IOException exc) {
893N/A System.err.println("WARNING: " + exc);
893N/A return CONTINUE;
893N/A }
893N/A }
893N/A
893N/A static void usage() {
893N/A System.err.println("java Chmod [-R] symbolic-mode-list file...");
893N/A System.exit(-1);
893N/A }
893N/A
893N/A public static void main(String[] args) throws IOException {
893N/A if (args.length < 2)
893N/A usage();
893N/A int argi = 0;
893N/A int maxDepth = 0;
893N/A if (args[argi].equals("-R")) {
893N/A if (args.length < 3)
893N/A usage();
893N/A argi++;
893N/A maxDepth = Integer.MAX_VALUE;
893N/A }
893N/A
893N/A // compile the symbolic mode expressions
893N/A Changer changer = compile(args[argi++]);
893N/A TreeVisitor visitor = new TreeVisitor(changer);
893N/A
893N/A Set<FileVisitOption> opts = Collections.emptySet();
893N/A while (argi < args.length) {
893N/A Path file = Paths.get(args[argi]);
893N/A Files.walkFileTree(file, opts, maxDepth, visitor);
893N/A argi++;
893N/A }
893N/A }
893N/A}