You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
350 lines
13 KiB
350 lines
13 KiB
/*
|
|
* Copyright (c) 2008, 2011, 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.
|
|
*/
|
|
|
|
/*
|
|
* This source code is provided to illustrate the usage of a given feature
|
|
* or technique and has been deliberately simplified. Additional steps
|
|
* required for a production-quality application, such as security checks,
|
|
* input validation and proper error handling, might not be present in
|
|
* this sample code.
|
|
*/
|
|
|
|
|
|
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 <em>symbolic mode expressions</em> 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 <i>chmod</i> program.
|
|
*
|
|
* <p> The {@code exprs} parameter is a comma separated list of expressions
|
|
* where each takes the form:
|
|
* <blockquote>
|
|
* <i>who operator</i> [<i>permissions</i>]
|
|
* </blockquote>
|
|
* where <i>who</i> 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.
|
|
*
|
|
* <p> <i>operator</i> 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.
|
|
*
|
|
* <p> <i>permissions</i> 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 <i>permissions</i> is omitted
|
|
* when assigned absolutely, then the permissions are cleared for
|
|
* the owner, group, or others as identified by <i>who</i>. When omitted
|
|
* when adding or removing then the expression is ignored.
|
|
*
|
|
* <p> The following examples demonstrate possible values for the {@code
|
|
* exprs} parameter:
|
|
*
|
|
* <table border="0">
|
|
* <tr>
|
|
* <td> {@code u=rw} </td>
|
|
* <td> Sets the owner permissions to be read and write. </td>
|
|
* </tr>
|
|
* <tr>
|
|
* <td> {@code ug+w} </td>
|
|
* <td> Sets the owner write and group write permissions. </td>
|
|
* </tr>
|
|
* <tr>
|
|
* <td> {@code u+w,o-rwx} </td>
|
|
* <td> Sets the owner write, and removes the others read, others write
|
|
* and others execute permissions. </td>
|
|
* </tr>
|
|
* <tr>
|
|
* <td> {@code o=} </td>
|
|
* <td> Sets the others permission to none (others read, others write and
|
|
* others execute permissions are removed if set) </td>
|
|
* </tr>
|
|
* </table>
|
|
*
|
|
* @param exprs
|
|
* List of one or more <em>symbolic mode expressions</em>
|
|
*
|
|
* @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<PosixFilePermission> toAdd = new HashSet<PosixFilePermission>();
|
|
final Set<PosixFilePermission> toRemove = new HashSet<PosixFilePermission>();
|
|
|
|
// 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<mask.length(); i++) {
|
|
switch (mask.charAt(i)) {
|
|
case 'r' : r = true; break;
|
|
case 'w' : w = true; break;
|
|
case 'x' : x = true; break;
|
|
default:
|
|
throw new IllegalArgumentException("Invalid mode");
|
|
}
|
|
}
|
|
|
|
// update permissions set
|
|
if (add) {
|
|
if (u) {
|
|
if (r) toAdd.add(OWNER_READ);
|
|
if (w) toAdd.add(OWNER_WRITE);
|
|
if (x) toAdd.add(OWNER_EXECUTE);
|
|
}
|
|
if (g) {
|
|
if (r) toAdd.add(GROUP_READ);
|
|
if (w) toAdd.add(GROUP_WRITE);
|
|
if (x) toAdd.add(GROUP_EXECUTE);
|
|
}
|
|
if (o) {
|
|
if (r) toAdd.add(OTHERS_READ);
|
|
if (w) toAdd.add(OTHERS_WRITE);
|
|
if (x) toAdd.add(OTHERS_EXECUTE);
|
|
}
|
|
}
|
|
if (remove) {
|
|
if (u) {
|
|
if (r) toRemove.add(OWNER_READ);
|
|
if (w) toRemove.add(OWNER_WRITE);
|
|
if (x) toRemove.add(OWNER_EXECUTE);
|
|
}
|
|
if (g) {
|
|
if (r) toRemove.add(GROUP_READ);
|
|
if (w) toRemove.add(GROUP_WRITE);
|
|
if (x) toRemove.add(GROUP_EXECUTE);
|
|
}
|
|
if (o) {
|
|
if (r) toRemove.add(OTHERS_READ);
|
|
if (w) toRemove.add(OTHERS_WRITE);
|
|
if (x) toRemove.add(OTHERS_EXECUTE);
|
|
}
|
|
}
|
|
if (assign) {
|
|
if (u) {
|
|
if (r) toAdd.add(OWNER_READ);
|
|
else toRemove.add(OWNER_READ);
|
|
if (w) toAdd.add(OWNER_WRITE);
|
|
else toRemove.add(OWNER_WRITE);
|
|
if (x) toAdd.add(OWNER_EXECUTE);
|
|
else toRemove.add(OWNER_EXECUTE);
|
|
}
|
|
if (g) {
|
|
if (r) toAdd.add(GROUP_READ);
|
|
else toRemove.add(GROUP_READ);
|
|
if (w) toAdd.add(GROUP_WRITE);
|
|
else toRemove.add(GROUP_WRITE);
|
|
if (x) toAdd.add(GROUP_EXECUTE);
|
|
else toRemove.add(GROUP_EXECUTE);
|
|
}
|
|
if (o) {
|
|
if (r) toAdd.add(OTHERS_READ);
|
|
else toRemove.add(OTHERS_READ);
|
|
if (w) toAdd.add(OTHERS_WRITE);
|
|
else toRemove.add(OTHERS_WRITE);
|
|
if (x) toAdd.add(OTHERS_EXECUTE);
|
|
else toRemove.add(OTHERS_EXECUTE);
|
|
}
|
|
}
|
|
}
|
|
|
|
// return changer
|
|
return new Changer() {
|
|
@Override
|
|
public Set<PosixFilePermission> change(Set<PosixFilePermission> perms) {
|
|
perms.addAll(toAdd);
|
|
perms.removeAll(toRemove);
|
|
return perms;
|
|
}
|
|
};
|
|
}
|
|
|
|
/**
|
|
* A task that <i>changes</i> 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<PosixFilePermission> change(Set<PosixFilePermission> perms);
|
|
}
|
|
|
|
/**
|
|
* Changes the permissions of the file using the given Changer.
|
|
*/
|
|
static void chmod(Path file, Changer changer) {
|
|
try {
|
|
Set<PosixFilePermission> 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<Path> {
|
|
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<FileVisitOption> opts = Collections.emptySet();
|
|
while (argi < args.length) {
|
|
Path file = Paths.get(args[argi]);
|
|
Files.walkFileTree(file, opts, maxDepth, visitor);
|
|
argi++;
|
|
}
|
|
}
|
|
}
|