Home | History | Annotate | Download | only in file
      1 /*
      2  * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions
      6  * are met:
      7  *
      8  *   - Redistributions of source code must retain the above copyright
      9  *     notice, this list of conditions and the following disclaimer.
     10  *
     11  *   - Redistributions in binary form must reproduce the above copyright
     12  *     notice, this list of conditions and the following disclaimer in the
     13  *     documentation and/or other materials provided with the distribution.
     14  *
     15  *   - Neither the name of Oracle nor the names of its
     16  *     contributors may be used to endorse or promote products derived
     17  *     from this software without specific prior written permission.
     18  *
     19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
     20  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
     21  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
     23  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     24  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     25  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
     26  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
     27  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
     28  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
     29  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     30  */
     31 
     32 /*
     33  * This source code is provided to illustrate the usage of a given feature
     34  * or technique and has been deliberately simplified. Additional steps
     35  * required for a production-quality application, such as security checks,
     36  * input validation and proper error handling, might not be present in
     37  * this sample code.
     38  */
     39 
     40 
     41 import java.nio.file.*;
     42 import java.nio.file.attribute.*;
     43 import static java.nio.file.attribute.PosixFilePermission.*;
     44 import static java.nio.file.FileVisitResult.*;
     45 import java.io.IOException;
     46 import java.util.*;
     47 
     48 /**
     49  * Sample code that changes the permissions of files in a similar manner to the
     50  * chmod(1) program.
     51  */
     52 
     53 public class Chmod {
     54 
     55     /**
     56      * Compiles a list of one or more <em>symbolic mode expressions</em> that
     57      * may be used to change a set of file permissions. This method is
     58      * intended for use where file permissions are required to be changed in
     59      * a manner similar to the UNIX <i>chmod</i> program.
     60      *
     61      * <p> The {@code exprs} parameter is a comma separated list of expressions
     62      * where each takes the form:
     63      * <blockquote>
     64      * <i>who operator</i> [<i>permissions</i>]
     65      * </blockquote>
     66      * where <i>who</i> is one or more of the characters {@code 'u'}, {@code 'g'},
     67      * {@code 'o'}, or {@code 'a'} meaning the owner (user), group, others, or
     68      * all (owner, group, and others) respectively.
     69      *
     70      * <p> <i>operator</i> is the character {@code '+'}, {@code '-'}, or {@code
     71      * '='} signifying how permissions are to be changed. {@code '+'} means the
     72      * permissions are added, {@code '-'} means the permissions are removed, and
     73      * {@code '='} means the permissions are assigned absolutely.
     74      *
     75      * <p> <i>permissions</i> is a sequence of zero or more of the following:
     76      * {@code 'r'} for read permission, {@code 'w'} for write permission, and
     77      * {@code 'x'} for execute permission. If <i>permissions</i> is omitted
     78      * when assigned absolutely, then the permissions are cleared for
     79      * the owner, group, or others as identified by <i>who</i>. When omitted
     80      * when adding or removing then the expression is ignored.
     81      *
     82      * <p> The following examples demonstrate possible values for the {@code
     83      * exprs} parameter:
     84      *
     85      * <table border="0">
     86      * <tr>
     87      *   <td> {@code u=rw} </td>
     88      *   <td> Sets the owner permissions to be read and write. </td>
     89      * </tr>
     90      * <tr>
     91      *   <td> {@code ug+w} </td>
     92      *   <td> Sets the owner write and group write permissions. </td>
     93      * </tr>
     94      * <tr>
     95      *   <td> {@code u+w,o-rwx} </td>
     96      *   <td> Sets the owner write, and removes the others read, others write
     97      *     and others execute permissions. </td>
     98      * </tr>
     99      * <tr>
    100      *   <td> {@code o=} </td>
    101      *   <td> Sets the others permission to none (others read, others write and
    102      *     others execute permissions are removed if set) </td>
    103      * </tr>
    104      * </table>
    105      *
    106      * @param   exprs
    107      *          List of one or more <em>symbolic mode expressions</em>
    108      *
    109      * @return  A {@code Changer} that may be used to changer a set of
    110      *          file permissions
    111      *
    112      * @throws  IllegalArgumentException
    113      *          If the value of the {@code exprs} parameter is invalid
    114      */
    115     public static Changer compile(String exprs) {
    116         // minimum is who and operator (u= for example)
    117         if (exprs.length() < 2)
    118             throw new IllegalArgumentException("Invalid mode");
    119 
    120         // permissions that the changer will add or remove
    121         final Set<PosixFilePermission> toAdd = new HashSet<PosixFilePermission>();
    122         final Set<PosixFilePermission> toRemove = new HashSet<PosixFilePermission>();
    123 
    124         // iterate over each of expression modes
    125         for (String expr: exprs.split(",")) {
    126             // minimum of who and operator
    127             if (expr.length() < 2)
    128                 throw new IllegalArgumentException("Invalid mode");
    129 
    130             int pos = 0;
    131 
    132             // who
    133             boolean u = false;
    134             boolean g = false;
    135             boolean o = false;
    136             boolean done = false;
    137             for (;;) {
    138                 switch (expr.charAt(pos)) {
    139                     case 'u' : u = true; break;
    140                     case 'g' : g = true; break;
    141                     case 'o' : o = true; break;
    142                     case 'a' : u = true; g = true; o = true; break;
    143                     default : done = true;
    144                 }
    145                 if (done)
    146                     break;
    147                 pos++;
    148             }
    149             if (!u && !g && !o)
    150                 throw new IllegalArgumentException("Invalid mode");
    151 
    152             // get operator and permissions
    153             char op = expr.charAt(pos++);
    154             String mask = (expr.length() == pos) ? "" : expr.substring(pos);
    155 
    156             // operator
    157             boolean add = (op == '+');
    158             boolean remove = (op == '-');
    159             boolean assign = (op == '=');
    160             if (!add && !remove && !assign)
    161                 throw new IllegalArgumentException("Invalid mode");
    162 
    163             // who= means remove all
    164             if (assign && mask.length() == 0) {
    165                 assign = false;
    166                 remove = true;
    167                 mask = "rwx";
    168             }
    169 
    170             // permissions
    171             boolean r = false;
    172             boolean w = false;
    173             boolean x = false;
    174             for (int i=0; i<mask.length(); i++) {
    175                 switch (mask.charAt(i)) {
    176                     case 'r' : r = true; break;
    177                     case 'w' : w = true; break;
    178                     case 'x' : x = true; break;
    179                     default:
    180                         throw new IllegalArgumentException("Invalid mode");
    181                 }
    182             }
    183 
    184             // update permissions set
    185             if (add) {
    186                 if (u) {
    187                     if (r) toAdd.add(OWNER_READ);
    188                     if (w) toAdd.add(OWNER_WRITE);
    189                     if (x) toAdd.add(OWNER_EXECUTE);
    190                 }
    191                 if (g) {
    192                     if (r) toAdd.add(GROUP_READ);
    193                     if (w) toAdd.add(GROUP_WRITE);
    194                     if (x) toAdd.add(GROUP_EXECUTE);
    195                 }
    196                 if (o) {
    197                     if (r) toAdd.add(OTHERS_READ);
    198                     if (w) toAdd.add(OTHERS_WRITE);
    199                     if (x) toAdd.add(OTHERS_EXECUTE);
    200                 }
    201             }
    202             if (remove) {
    203                 if (u) {
    204                     if (r) toRemove.add(OWNER_READ);
    205                     if (w) toRemove.add(OWNER_WRITE);
    206                     if (x) toRemove.add(OWNER_EXECUTE);
    207                 }
    208                 if (g) {
    209                     if (r) toRemove.add(GROUP_READ);
    210                     if (w) toRemove.add(GROUP_WRITE);
    211                     if (x) toRemove.add(GROUP_EXECUTE);
    212                 }
    213                 if (o) {
    214                     if (r) toRemove.add(OTHERS_READ);
    215                     if (w) toRemove.add(OTHERS_WRITE);
    216                     if (x) toRemove.add(OTHERS_EXECUTE);
    217                 }
    218             }
    219             if (assign) {
    220                 if (u) {
    221                     if (r) toAdd.add(OWNER_READ);
    222                       else toRemove.add(OWNER_READ);
    223                     if (w) toAdd.add(OWNER_WRITE);
    224                       else toRemove.add(OWNER_WRITE);
    225                     if (x) toAdd.add(OWNER_EXECUTE);
    226                       else toRemove.add(OWNER_EXECUTE);
    227                 }
    228                 if (g) {
    229                     if (r) toAdd.add(GROUP_READ);
    230                       else toRemove.add(GROUP_READ);
    231                     if (w) toAdd.add(GROUP_WRITE);
    232                       else toRemove.add(GROUP_WRITE);
    233                     if (x) toAdd.add(GROUP_EXECUTE);
    234                       else toRemove.add(GROUP_EXECUTE);
    235                 }
    236                 if (o) {
    237                     if (r) toAdd.add(OTHERS_READ);
    238                       else toRemove.add(OTHERS_READ);
    239                     if (w) toAdd.add(OTHERS_WRITE);
    240                       else toRemove.add(OTHERS_WRITE);
    241                     if (x) toAdd.add(OTHERS_EXECUTE);
    242                       else toRemove.add(OTHERS_EXECUTE);
    243                 }
    244             }
    245         }
    246 
    247         // return changer
    248         return new Changer() {
    249             @Override
    250             public Set<PosixFilePermission> change(Set<PosixFilePermission> perms) {
    251                 perms.addAll(toAdd);
    252                 perms.removeAll(toRemove);
    253                 return perms;
    254             }
    255         };
    256     }
    257 
    258     /**
    259      * A task that <i>changes</i> a set of {@link PosixFilePermission} elements.
    260      */
    261     public interface Changer {
    262         /**
    263          * Applies the changes to the given set of permissions.
    264          *
    265          * @param   perms
    266          *          The set of permissions to change
    267          *
    268          * @return  The {@code perms} parameter
    269          */
    270         Set<PosixFilePermission> change(Set<PosixFilePermission> perms);
    271     }
    272 
    273     /**
    274      * Changes the permissions of the file using the given Changer.
    275      */
    276     static void chmod(Path file, Changer changer) {
    277         try {
    278             Set<PosixFilePermission> perms = Files.getPosixFilePermissions(file);
    279             Files.setPosixFilePermissions(file, changer.change(perms));
    280         } catch (IOException x) {
    281             System.err.println(x);
    282         }
    283     }
    284 
    285     /**
    286      * Changes the permission of each file and directory visited
    287      */
    288     static class TreeVisitor implements FileVisitor<Path> {
    289         private final Changer changer;
    290 
    291         TreeVisitor(Changer changer) {
    292             this.changer = changer;
    293         }
    294 
    295         @Override
    296         public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) {
    297             chmod(dir, changer);
    298             return CONTINUE;
    299         }
    300 
    301         @Override
    302         public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
    303             chmod(file, changer);
    304             return CONTINUE;
    305         }
    306 
    307         @Override
    308         public FileVisitResult postVisitDirectory(Path dir, IOException exc) {
    309             if (exc != null)
    310                 System.err.println("WARNING: " + exc);
    311             return CONTINUE;
    312         }
    313 
    314         @Override
    315         public FileVisitResult visitFileFailed(Path file, IOException exc) {
    316             System.err.println("WARNING: " + exc);
    317             return CONTINUE;
    318         }
    319     }
    320 
    321     static void usage() {
    322         System.err.println("java Chmod [-R] symbolic-mode-list file...");
    323         System.exit(-1);
    324     }
    325 
    326     public static void main(String[] args) throws IOException {
    327         if (args.length < 2)
    328             usage();
    329         int argi = 0;
    330         int maxDepth = 0;
    331         if (args[argi].equals("-R")) {
    332             if (args.length < 3)
    333                 usage();
    334             argi++;
    335             maxDepth = Integer.MAX_VALUE;
    336         }
    337 
    338         // compile the symbolic mode expressions
    339         Changer changer = compile(args[argi++]);
    340         TreeVisitor visitor = new TreeVisitor(changer);
    341 
    342         Set<FileVisitOption> opts = Collections.emptySet();
    343         while (argi < args.length) {
    344             Path file = Paths.get(args[argi]);
    345             Files.walkFileTree(file, opts, maxDepth, visitor);
    346             argi++;
    347         }
    348     }
    349 }
    350