/** * $Id: $ * $Date: $ * */ package org.xmlsh.posix.commands; import java.io.File; import java.io.IOException; import java.io.PrintWriter; import java.nio.file.FileVisitResult; import java.nio.file.Files; import java.nio.file.LinkOption; import java.nio.file.Path; import java.nio.file.attribute.PosixFileAttributes; import java.nio.file.attribute.PosixFilePermission; import java.nio.file.attribute.PosixFilePermissions; import java.util.ArrayList; import java.util.Date; import java.util.HashSet; import java.util.List; import java.util.Set; import org.xmlsh.core.InvalidArgumentException; import org.xmlsh.core.Options; import org.xmlsh.core.XCommand; import org.xmlsh.core.XValue; import org.xmlsh.posix.commands.chmod.Chmod.Changer; import org.xmlsh.posix.commands.ls.ListVisitor; import org.xmlsh.util.FileUtils; import org.xmlsh.util.IPathTreeVisitor; import org.xmlsh.util.PathMatchOptions; import org.xmlsh.util.UnifiedFileAttributes; import org.xmlsh.util.Util; import org.xmlsh.util.XFile; import java.nio.file.*; import java.nio.file.attribute.*; import static java.nio.file.attribute.PosixFilePermission.*; import static java.nio.file.FileVisitResult.*; import static org.xmlsh.util.UnifiedFileAttributes.MatchFlag.HIDDEN_NAME; import static org.xmlsh.util.UnifiedFileAttributes.MatchFlag.HIDDEN_SYS; import static org.xmlsh.util.UnifiedFileAttributes.MatchFlag.SYSTEM; import java.io.IOException; import java.util.*; public class chmod extends XCommand { // From : http://docs.oracle.com/javase/tutorial/displayCode.html?code=http://docs.oracle.com/javase/tutorial/essential/io/examples/Chmod.java static 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){ if( done && pos == 0 ){ u = true; g = true; o = true; } else 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> posix) { posix = EnumSet.copyOf(posix); posix.addAll(toAdd); posix.removeAll(toRemove); return posix; } }; } /** * 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> set); } /** * Changes the permissions of the file using the given Changer. * @param attrs */ static void chmod(Path file, UnifiedFileAttributes attrs, Changer changer) { FileUtils.changeFilePermissions(file, attrs , changer.change(attrs.getPermissions()) ); } } private boolean bRecurse ; public class ListVisitor implements IPathTreeVisitor { private final Changer changer; public ListVisitor( Changer changer) { this.changer = changer; } @Override public FileVisitResult visitDirectory(Path root, Path directory, UnifiedFileAttributes attrs) throws IOException { Chmod.chmod(directory, attrs, changer); return FileVisitResult.CONTINUE; } @Override public FileVisitResult visitFile(Path root, Path path, UnifiedFileAttributes uattrs) throws IOException { Chmod.chmod(path, uattrs, changer); return FileVisitResult.CONTINUE ; } @Override public FileVisitResult enterDirectory(Path root, Path directory, UnifiedFileAttributes attrs) { return FileVisitResult.CONTINUE; } @Override public FileVisitResult exitDirectory(Path root, Path directory, UnifiedFileAttributes attrs) { return FileVisitResult.CONTINUE; } } @Override public int run(List<XValue> args) throws Exception { Options opts = new Options( "R=recurse"); opts.parse(args,true); // ignore unknown args args = opts.getRemainingArgs(); bRecurse = opts.hasOpt("R"); requires(args.size() > 1 ,"Missing arguments"); String mode = args.remove(0).toString(); // compile the symbolic mode expressions Changer changer = Chmod.compile(mode); for( XValue arg : args){ Path path = this.getPath(arg); FileUtils.walkPathTree(path, bRecurse , new ListVisitor(changer), (new PathMatchOptions() ) ); } return 0; } } // // //Copyright (C) 2008-2014 David A. Lee. // //The contents of this file are subject to the "Simplified BSD License" (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.opensource.org/licenses/bsd-license.php // //Software distributed under the License is distributed on an "AS IS" basis, //WITHOUT WARRANTY OF ANY KIND, either express or implied. //See the License for the specific language governing rights and limitations under the License. // //The Original Code is: all this file. // //The Initial Developer of the Original Code is David A. Lee // //Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved. // //Contributor(s): none. //