001    /*
002     * CDDL HEADER START
003     *
004     * The contents of this file are subject to the terms of the
005     * Common Development and Distribution License, Version 1.0 only
006     * (the "License").  You may not use this file except in compliance
007     * with the License.
008     *
009     * You can obtain a copy of the license at
010     * trunk/opends/resource/legal-notices/OpenDS.LICENSE
011     * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
012     * See the License for the specific language governing permissions
013     * and limitations under the License.
014     *
015     * When distributing Covered Code, include this CDDL HEADER in each
016     * file and include the License file at
017     * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
018     * add the following below this CDDL HEADER, with the fields enclosed
019     * by brackets "[]" replaced with your own identifying information:
020     *      Portions Copyright [yyyy] [name of copyright owner]
021     *
022     * CDDL HEADER END
023     *
024     *
025     *      Copyright 2006-2008 Sun Microsystems, Inc.
026     */
027    package org.opends.server.types;
028    import org.opends.messages.Message;
029    
030    
031    
032    import java.io.File;
033    import java.io.FileNotFoundException;
034    import java.lang.reflect.Method;
035    
036    import org.opends.server.core.DirectoryServer;
037    
038    import static org.opends.server.loggers.debug.DebugLogger.*;
039    import org.opends.server.loggers.debug.DebugTracer;
040    import static org.opends.messages.UtilityMessages.*;
041    import static org.opends.server.util.StaticUtils.*;
042    
043    
044    
045    /**
046     * This class provides a mechanism for setting file permissions in a
047     * more abstract manner than is provided by the underlying operating
048     * system and/or filesystem.  It uses a traditional UNIX-style rwx/ugo
049     * representation for the permissions and converts them as necessary
050     * to the scheme used by the underlying platform.  It does not provide
051     * any mechanism for getting file permissions, nor does it provide any
052     * way of dealing with file ownership or ACLs.
053     * <BR><BR>
054     * Note that the mechanism used to perform this work on UNIX systems
055     * is based on executing the <CODE>chmod</CODE> command on the
056     * underlying system.  This should be a safe operation because the
057     * Directory Server startup scripts should explicitly specify the PATH
058     * that should be used.  Nevertheless, it is possible to prevent the
059     * server from using the <CODE>Runtime.exec</CODE> method by setting
060     * the <CODE>org.opends.server.DisableExec</CODE> system property with
061     * a value of "true".
062     */
063    @org.opends.server.types.PublicAPI(
064         stability=org.opends.server.types.StabilityLevel.VOLATILE,
065         mayInstantiate=true,
066         mayExtend=false,
067         mayInvoke=true)
068    public class FilePermission
069    {
070      /**
071       * The tracer object for the debug logger.
072       */
073      private static final DebugTracer TRACER = getTracer();
074    
075    
076    
077    
078      /**
079       * The bitmask that should be used for indicating whether a file is
080       * readable by its owner.
081       */
082      public static final int OWNER_READABLE = 0x0100;
083    
084    
085    
086      /**
087       * The bitmask that should be used for indicating whether a file is
088       * writable by its owner.
089       */
090      public static final int OWNER_WRITABLE = 0x0080;
091    
092    
093    
094      /**
095       * The bitmask that should be used for indicating whether a file is
096       * executable by its owner.
097       */
098      public static final int OWNER_EXECUTABLE = 0x0040;
099    
100    
101    
102      /**
103       * The bitmask that should be used for indicating whether a file is
104       * readable by members of its group.
105       */
106      public static final int GROUP_READABLE = 0x0020;
107    
108    
109    
110      /**
111       * The bitmask that should be used for indicating whether a file is
112       * writable by members of its group.
113       */
114      public static final int GROUP_WRITABLE = 0x0010;
115    
116    
117    
118      /**
119       * The bitmask that should be used for indicating whether a file is
120       * executable by members of its group.
121       */
122      public static final int GROUP_EXECUTABLE = 0x0008;
123    
124    
125    
126      /**
127       * The bitmask that should be used for indicating whether a file is
128       * readable by users other than the owner or group members.
129       */
130      public static final int OTHER_READABLE = 0x0004;
131    
132    
133    
134      /**
135       * The bitmask that should be used for indicating whether a file is
136       * writable by users other than the owner or group members.
137       */
138      public static final int OTHER_WRITABLE = 0x0002;
139    
140    
141    
142      /**
143       * The bitmask that should be used for indicating whether a file is
144       * executable by users other than the owner or group members.
145       */
146      public static final int OTHER_EXECUTABLE = 0x0001;
147    
148    
149    
150      // Indicates whether to allow the use of exec for setting file
151      // permissions.
152      private static boolean allowExec;
153    
154      // The method that may be used to specify whether a file is
155      // executable by its owner (and optionally others).
156      private static Method setExecutableMethod;
157    
158      // The method that may be used to specify whether a file is readable
159      // by its owner (and optionally others).
160      private static Method setReadableMethod;
161    
162      // The method that may be used to specify whether a file is wriable
163      // by its owner (and optionally others).
164      private static Method setWritableMethod;
165    
166      // The encoded representation for this file permission.
167      private int encodedPermission;
168    
169    
170    
171      static
172      {
173        // Iterate through the available methods and see if any of the
174        // Java 6 methods for dealing with permissions are available.
175        try
176        {
177          setExecutableMethod = null;
178          setReadableMethod   = null;
179          setWritableMethod   = null;
180    
181          for (Method m : File.class.getMethods())
182          {
183            String  name     = m.getName();
184            Class[] argTypes = m.getParameterTypes();
185    
186            if (name.equals("setExecutable") && (argTypes.length == 2))
187            {
188              setExecutableMethod = m;
189            }
190            else if (name.equals("setReadable") && (argTypes.length == 2))
191            {
192              setReadableMethod = m;
193            }
194            else if (name.equals("setWritable") && (argTypes.length == 2))
195            {
196              setWritableMethod = m;
197            }
198          }
199        }
200        catch (Exception e)
201        {
202          if (debugEnabled())
203          {
204            TRACER.debugCaught(DebugLogLevel.ERROR, e);
205          }
206        }
207    
208    
209        // Determine whether we should disable the ability to execute
210        // commands on the underlying system even if it could provide more
211        // control and capability.
212        allowExec = mayUseExec();
213      }
214    
215    
216    
217      /**
218       * Creates a new file permission object with the provided encoded
219       * representation.
220       *
221       * @param  encodedPermission  The encoded representation for this
222       *                            file permission.
223       */
224      public FilePermission(int encodedPermission)
225      {
226        this.encodedPermission = encodedPermission;
227      }
228    
229    
230    
231      /**
232       * Creates a new file permission with the specified rights for the
233       * file owner.  Users other than the owner will not have any rights.
234       *
235       * @param  ownerReadable    Indicates whether the owner should have
236       *                          the read permission.
237       * @param  ownerWritable    Indicates whether the owner should have
238       *                          the write permission.
239       * @param  ownerExecutable  Indicates whether the owner should have
240       *                          the execute permission.
241       */
242      public FilePermission(boolean ownerReadable, boolean ownerWritable,
243                            boolean ownerExecutable)
244      {
245        encodedPermission = 0x0000;
246    
247        if (ownerReadable)
248        {
249          encodedPermission |= OWNER_READABLE;
250        }
251    
252        if (ownerWritable)
253        {
254          encodedPermission |= OWNER_WRITABLE;
255        }
256    
257        if (ownerExecutable)
258        {
259          encodedPermission |= OWNER_EXECUTABLE;
260        }
261      }
262    
263    
264    
265      /**
266       * Creates a new file permission with the specified rights for the
267       * file owner, group members, and other users.
268       *
269       * @param  ownerReadable    Indicates whether the owner should have
270       *                          the read permission.
271       * @param  ownerWritable    Indicates whether the owner should have
272       *                          the write permission.
273       * @param  ownerExecutable  Indicates whether the owner should have
274       *                          the execute permission.
275       * @param  groupReadable    Indicates whether members of the file's
276       *                          group should have the read permission.
277       * @param  groupWritable    Indicates whether members of the file's
278       *                          group should have the write permission.
279       * @param  groupExecutable  Indicates whether members of the file's
280       *                          group should have the execute
281       *                          permission.
282       * @param  otherReadable    Indicates whether other users should
283       *                          have the read permission.
284       * @param  otherWritable    Indicates whether other users should
285       *                          have the write permission.
286       * @param  otherExecutable  Indicates whether other users should
287       *                          have the execute permission.
288       */
289      public FilePermission(boolean ownerReadable, boolean ownerWritable,
290                            boolean ownerExecutable,
291                            boolean groupReadable, boolean groupWritable,
292                            boolean groupExecutable,
293                            boolean otherReadable, boolean otherWritable,
294                            boolean otherExecutable)
295      {
296        encodedPermission = 0x0000;
297    
298        if (ownerReadable)
299        {
300          encodedPermission |= OWNER_READABLE;
301        }
302    
303        if (ownerWritable)
304        {
305          encodedPermission |= OWNER_WRITABLE;
306        }
307    
308        if (ownerExecutable)
309        {
310          encodedPermission |= OWNER_EXECUTABLE;
311        }
312    
313        if (groupReadable)
314        {
315          encodedPermission |= GROUP_READABLE;
316        }
317    
318        if (groupWritable)
319        {
320          encodedPermission |= GROUP_WRITABLE;
321        }
322    
323        if (groupExecutable)
324        {
325          encodedPermission |= GROUP_EXECUTABLE;
326        }
327    
328        if (otherReadable)
329        {
330          encodedPermission |= OTHER_READABLE;
331        }
332    
333        if (otherWritable)
334        {
335          encodedPermission |= OTHER_WRITABLE;
336        }
337    
338        if (otherExecutable)
339        {
340          encodedPermission |= OTHER_EXECUTABLE;
341        }
342      }
343    
344    
345    
346      /**
347       * Indicates whether this file permission includes the owner read
348       * permission.
349       *
350       * @return  <CODE>true</CODE> if this file permission includes the
351       *          owner read permission, or <CODE>false</CODE> if not.
352       */
353      public boolean isOwnerReadable()
354      {
355        return ((encodedPermission & OWNER_READABLE) == OWNER_READABLE);
356      }
357    
358    
359    
360      /**
361       * Indicates whether this file permission includes the owner write
362       * permission.
363       *
364       * @return  <CODE>true</CODE> if this file permission includes the
365       *          owner write permission, or <CODE>false</CODE> if not.
366       */
367      public boolean isOwnerWritable()
368      {
369        return ((encodedPermission & OWNER_WRITABLE) == OWNER_WRITABLE);
370      }
371    
372    
373    
374      /**
375       * Indicates whether this file permission includes the owner execute
376       * permission.
377       *
378       * @return  <CODE>true</CODE> if this file permission includes the
379       *          owner execute permission, or <CODE>false</CODE> if not.
380       */
381      public boolean isOwnerExecutable()
382      {
383        return ((encodedPermission & OWNER_EXECUTABLE) ==
384                OWNER_EXECUTABLE);
385      }
386    
387    
388    
389      /**
390       * Indicates whether this file permission includes the group read
391       * permission.
392       *
393       * @return  <CODE>true</CODE> if this file permission includes the
394       *          group read permission, or <CODE>false</CODE> if not.
395       */
396      public boolean isGroupReadable()
397      {
398        return ((encodedPermission & GROUP_READABLE) == GROUP_READABLE);
399      }
400    
401    
402    
403      /**
404       * Indicates whether this file permission includes the group write
405       * permission.
406       *
407       * @return  <CODE>true</CODE> if this file permission includes the
408       *          group write permission, or <CODE>false</CODE> if not.
409       */
410      public boolean isGroupWritable()
411      {
412        return ((encodedPermission & GROUP_WRITABLE) == GROUP_WRITABLE);
413      }
414    
415    
416    
417      /**
418       * Indicates whether this file permission includes the group execute
419       * permission.
420       *
421       * @return  <CODE>true</CODE> if this file permission includes the
422       *          group execute permission, or <CODE>false</CODE> if not.
423       */
424      public boolean isGroupExecutable()
425      {
426        return ((encodedPermission & GROUP_EXECUTABLE) ==
427                GROUP_EXECUTABLE);
428      }
429    
430    
431    
432      /**
433       * Indicates whether this file permission includes the other read
434       * permission.
435       *
436       * @return  <CODE>true</CODE> if this file permission includes the
437       *          other read permission, or <CODE>false</CODE> if not.
438       */
439      public boolean isOtherReadable()
440      {
441        return ((encodedPermission & OTHER_READABLE) == OTHER_READABLE);
442      }
443    
444    
445    
446      /**
447       * Indicates whether this file permission includes the other write
448       * permission.
449       *
450       * @return  <CODE>true</CODE> if this file permission includes the
451       *          other write permission, or <CODE>false</CODE> if not.
452       */
453      public boolean isOtherWritable()
454      {
455        return ((encodedPermission & OTHER_WRITABLE) == OTHER_WRITABLE);
456      }
457    
458    
459    
460      /**
461       * Indicates whether this file permission includes the other execute
462       * permission.
463       *
464       * @return  <CODE>true</CODE> if this file permission includes the
465       *          other execute permission, or <CODE>false</CODE> if not.
466       */
467      public boolean isOtherExecutable()
468      {
469        return ((encodedPermission & OTHER_EXECUTABLE) ==
470                OTHER_EXECUTABLE);
471      }
472    
473    
474    
475      /**
476       * Indicates whether the there is a mechanism available for setting
477       * permissions in the underlying filesystem on the current platform.
478       *
479       * @return  <CODE>true</CODE> if there is a mechanism available for
480       *          setting file permissions on the underlying system (e.g.,
481       *          if the server is running in a Java 6 environment, or if
482       *          this is a UNIX-based system and the use of exec is
483       *          allowed), or <CODE>false</CODE> if no such mechanism is
484       *          available.
485       */
486      public static boolean canSetPermissions()
487      {
488        if ((setReadableMethod != null) && (setWritableMethod != null) &&
489            (setExecutableMethod != null))
490        {
491          // It's a Java 6 environment, so we can always use that
492          // mechanism.
493          return true;
494        }
495    
496        OperatingSystem os = DirectoryServer.getOperatingSystem();
497        if (allowExec && (os != null) && OperatingSystem.isUNIXBased(os))
498        {
499          // It's a UNIX-based system and we can exec the chmod utility.
500          return true;
501        }
502    
503        // We have no way to set file permissions on this system.
504        return false;
505      }
506    
507    
508    
509      /**
510       * Attempts to set the given permissions on the specified file.  If
511       * the underlying platform does not allow the full level of
512       * granularity specified in the permissions, then an attempt will be
513       * made to set them as closely as possible to the provided
514       * permissions, erring on the side of security.
515       *
516       * @param  f  The file to which the permissions should be applied.
517       * @param  p  The permissions to apply to the file.
518       *
519       * @return  <CODE>true</CODE> if the permissions (or the nearest
520       *          equivalent) were successfully applied to the specified
521       *          file, or <CODE>false</CODE> if was not possible to set
522       *          the permissions on the current platform.
523       *
524       * @throws  FileNotFoundException  If the specified file does not
525       *                                 exist.
526       *
527       * @throws  DirectoryException  If a problem occurs while trying to
528       *                              set the file permissions.
529       */
530      public static boolean setPermissions(File f, FilePermission p)
531             throws FileNotFoundException, DirectoryException
532      {
533        if (! f.exists())
534        {
535          Message message =
536              ERR_FILEPERM_SET_NO_SUCH_FILE.get(f.getAbsolutePath());
537          throw new FileNotFoundException(message.toString());
538        }
539    
540    
541        // If we're running Java 6, then we'll use the methods that Java
542        // provides.  Even though it's potentially less fine-grained on a
543        // UNIX-based system, it is more efficient and doesn't require an
544        // external process.
545        if ((setReadableMethod != null) && (setWritableMethod != null) &&
546            (setExecutableMethod != null))
547        {
548          return setUsingJava(f, p);
549        }
550    
551    
552        // If it's a UNIX-based system, then try using the chmod command
553        // to set the permissions.  Otherwise (or if that fails), then try
554        // to use the Java 6 API.
555        OperatingSystem os = DirectoryServer.getOperatingSystem();
556        if (allowExec && (os != null) && OperatingSystem.isUNIXBased(os))
557        {
558          return setUsingUNIX(f, p);
559        }
560    
561        // FIXME -- Consider using cacls on Windows.
562    
563    
564        // We have no way to set file permissions on this system.
565        return false;
566      }
567    
568    
569    
570      /**
571       * Attempts to set the specified permissions for the given file
572       * using the UNIX chmod command.
573       *
574       * @param  f  The file to which the permissions should be applied.
575       * @param  p  The permissions to apply to the file.
576       *
577       * @return  <CODE>true</CODE> if the permissions were successfully
578       *          updated, or <CODE>false</CODE> if not.
579       *
580       * @throws  DirectoryException  If an error occurs while trying to
581       *                              execute the chmod command.
582       */
583      private static boolean setUsingUNIX(File f, FilePermission p)
584              throws DirectoryException
585      {
586        String[] arguments =
587        {
588          toUNIXMode(p),
589          f.getAbsolutePath()
590        };
591    
592        int exitCode;
593        try
594        {
595          exitCode = exec("chmod", arguments, null, null, null);
596        }
597        catch (Exception e)
598        {
599          if (debugEnabled())
600          {
601            TRACER.debugCaught(DebugLogLevel.ERROR, e);
602          }
603    
604          Message message = ERR_FILEPERM_CANNOT_EXEC_CHMOD.get(
605              f.getAbsolutePath(), String.valueOf(e));
606          throw new DirectoryException(ResultCode.OTHER, message, e);
607        }
608    
609        return (exitCode == 0);
610      }
611    
612    
613    
614      /**
615       * Attempts to set the specified permissions for the given file
616       * using the Java 6 <CODE>FILE</CODE> API.  Only the "owner" and
617       * "other" permissions will be preserved, since Java doesn't provide
618       * a way to set the group permissions directly.
619       *
620       * @param  f  The file to which the permissions should be applied.
621       * @param  p  The permissions to apply to the file.
622       *
623       * @return  <CODE>true</CODE> if the permissions were successfully
624       *          updated, or <CODE>false</CODE> if not.
625       *
626       * @throws  DirectoryException  If a problem occurs while attempting
627       *                              to update permissions.
628       */
629      private static boolean setUsingJava(File f, FilePermission p)
630              throws DirectoryException
631      {
632        // NOTE:  Due to a very nasty behavior of the Java 6 API, if you
633        //        want to want to grant a permission for the owner but not
634        //        for anyone else, then you *must* remove it for everyone
635        //        first, and then add it only for the owner.  Otherwise,
636        //        the other permissions will be left unchanged and if they
637        //        had it before then they will still have it.
638    
639        boolean anySuccessful   = false;
640        boolean anyFailed       = false;
641        boolean exceptionThrown = false;
642    
643        // Take away read permission from everyone if necessary.
644        if (p.isOwnerReadable() && (! p.isOtherReadable()))
645        {
646          try
647          {
648            Boolean b =
649                 (Boolean) setReadableMethod.invoke(f, false, false);
650            if (b.booleanValue())
651            {
652              anySuccessful = true;
653            }
654            else
655            {
656              if(!DirectoryServer.getOperatingSystem().equals(
657                  OperatingSystem.WINDOWS))
658              {
659                // On Windows platforms, file readability permissions
660                // cannot be set to false. Do not consider this case
661                // a failure. http://java.sun.com/developer/
662                // technicalArticles/J2SE/Desktop/javase6/enhancements/
663                anyFailed = true;
664              }
665            }
666          }
667          catch (Exception e)
668          {
669            if (debugEnabled())
670            {
671              TRACER.debugCaught(DebugLogLevel.ERROR, e);
672            }
673            exceptionThrown = true;
674          }
675        }
676    
677        // Grant the appropriate read permission.
678        try
679        {
680          boolean ownerOnly =
681               (p.isOwnerReadable() != p.isOtherReadable());
682    
683          Boolean b = (Boolean)
684                      setReadableMethod.invoke(f, p.isOwnerReadable(),
685                                               ownerOnly);
686          if (b.booleanValue())
687          {
688            anySuccessful = true;
689          }
690          else
691          {
692            if(!DirectoryServer.getOperatingSystem().equals(
693                OperatingSystem.WINDOWS) || p.isOwnerReadable())
694            {
695              // On Windows platforms, file readabilitys permissions
696              // cannot be set to false. Do not consider this case
697              // a failure. http://java.sun.com/developer/
698              // technicalArticles/J2SE/Desktop/javase6/enhancements/
699              anyFailed = true;
700            }
701          }
702        }
703        catch (Exception e)
704        {
705          if (debugEnabled())
706          {
707            TRACER.debugCaught(DebugLogLevel.ERROR, e);
708          }
709          exceptionThrown = true;
710        }
711    
712    
713        // Take away write permission from everyone if necessary.
714        if (p.isOwnerWritable() && (! p.isOtherWritable()))
715        {
716          try
717          {
718            Boolean b =
719                 (Boolean) setWritableMethod.invoke(f, false, false);
720            if (b.booleanValue())
721            {
722              anySuccessful = true;
723            }
724            else
725            {
726              anyFailed = true;
727            }
728          }
729          catch (Exception e)
730          {
731            if (debugEnabled())
732            {
733              TRACER.debugCaught(DebugLogLevel.ERROR, e);
734            }
735            exceptionThrown = true;
736          }
737        }
738    
739        // Grant the appropriate write permission.
740        try
741        {
742          boolean ownerOnly =
743               (p.isOwnerWritable() != p.isOtherWritable());
744    
745          Boolean b = (Boolean)
746                      setWritableMethod.invoke(f, p.isOwnerWritable(),
747                                               ownerOnly);
748          if (b.booleanValue())
749          {
750            anySuccessful = true;
751          }
752          else
753          {
754            anyFailed = true;
755          }
756        }
757        catch (Exception e)
758        {
759          if (debugEnabled())
760          {
761            TRACER.debugCaught(DebugLogLevel.ERROR, e);
762          }
763          exceptionThrown = true;
764        }
765    
766    
767        // Take away execute permission from everyone if necessary.
768        if (p.isOwnerExecutable() && (! p.isOtherExecutable()))
769        {
770          try
771          {
772            Boolean b =
773                 (Boolean) setExecutableMethod.invoke(f, false, false);
774            if (b.booleanValue())
775            {
776              anySuccessful = true;
777            }
778            else
779            {
780              if(!DirectoryServer.getOperatingSystem().equals(
781                  OperatingSystem.WINDOWS))
782              {
783                // On Windows platforms, file execute permissions
784                // cannot be set to false. Do not consider this case
785                // a failure. http://java.sun.com/developer/
786                // technicalArticles/J2SE/Desktop/javase6/enhancements/
787                anyFailed = true;
788              }
789            }
790          }
791          catch (Exception e)
792          {
793            if (debugEnabled())
794            {
795              TRACER.debugCaught(DebugLogLevel.ERROR, e);
796            }
797            exceptionThrown = true;
798          }
799        }
800    
801        // Grant the appropriate execute permission.
802        try
803        {
804          boolean ownerOnly =
805               (p.isOwnerExecutable() != p.isOtherExecutable());
806    
807          Boolean b = (Boolean)
808                      setExecutableMethod.invoke(f, p.isOwnerExecutable(),
809                                                 ownerOnly);
810          if (b.booleanValue())
811          {
812            anySuccessful = true;
813          }
814          else
815          {
816            if(!DirectoryServer.getOperatingSystem().equals(
817                OperatingSystem.WINDOWS) || p.isOwnerExecutable())
818            {
819              // On Windows platforms, file execute permissions
820              // cannot be set to false. Do not consider this case
821              // a failure. http://java.sun.com/developer/
822              // technicalArticles/J2SE/Desktop/javase6/enhancements/
823              anyFailed = true;
824            }
825          }
826        }
827        catch (Exception e)
828        {
829          if (debugEnabled())
830          {
831            TRACER.debugCaught(DebugLogLevel.ERROR, e);
832          }
833          exceptionThrown = true;
834        }
835    
836    
837        if (exceptionThrown)
838        {
839          // If an exception was thrown, we can't be sure whether or not
840          // any permissions were updated.
841          Message message =
842              ERR_FILEPERM_SET_JAVA_EXCEPTION.get(f.getAbsolutePath());
843          throw new DirectoryException(ResultCode.OTHER, message);
844        }
845        else if (anyFailed)
846        {
847          if (anySuccessful)
848          {
849            // Some of the file permissions may have been altered.
850            Message message = ERR_FILEPERM_SET_JAVA_FAILED_ALTERED.get(
851                f.getAbsolutePath());
852            throw new DirectoryException(ResultCode.OTHER, message);
853          }
854          else
855          {
856            // The file permissions should have been left intact.
857            Message message = ERR_FILEPERM_SET_JAVA_FAILED_UNALTERED.get(
858                f.getAbsolutePath());
859            throw new DirectoryException(ResultCode.OTHER, message);
860          }
861        }
862        else
863        {
864          return anySuccessful;
865        }
866      }
867    
868    
869    
870      /**
871       * Retrieves a three-character string that is the UNIX mode for the
872       * provided file permission.  Each character of the string will be a
873       * numeric digit from zero through seven.
874       *
875       * @param  p  The permission to retrieve as a UNIX mode string.
876       *
877       * @return  The UNIX mode string for the provided permission.
878       */
879      public static String toUNIXMode(FilePermission p)
880      {
881        StringBuilder buffer = new StringBuilder(3);
882        toUNIXMode(buffer, p);
883        return buffer.toString();
884      }
885    
886    
887    
888      /**
889       * Appends a three-character string that is the UNIX mode for the
890       * provided file permission to the given buffer.  Each character of
891       * the string will be anumeric digit from zero through seven.
892       *
893       * @param  buffer  The buffer to which the mode string should be
894       *                 appended.
895       * @param  p       The permission to retrieve as a UNIX mode string.
896       */
897      public static void toUNIXMode(StringBuilder buffer,
898                                    FilePermission p)
899      {
900        byte modeByte = 0x00;
901        if (p.isOwnerReadable())
902        {
903          modeByte |= 0x04;
904        }
905        if (p.isOwnerWritable())
906        {
907          modeByte |= 0x02;
908        }
909        if (p.isOwnerExecutable())
910        {
911          modeByte |= 0x01;
912        }
913        buffer.append(String.valueOf(modeByte));
914    
915        modeByte = 0x00;
916        if (p.isGroupReadable())
917        {
918          modeByte |= 0x04;
919        }
920        if (p.isGroupWritable())
921        {
922          modeByte |= 0x02;
923        }
924        if (p.isGroupExecutable())
925        {
926          modeByte |= 0x01;
927        }
928        buffer.append(String.valueOf(modeByte));
929    
930        modeByte = 0x00;
931        if (p.isOtherReadable())
932        {
933          modeByte |= 0x04;
934        }
935        if (p.isOtherWritable())
936        {
937          modeByte |= 0x02;
938        }
939        if (p.isOtherExecutable())
940        {
941          modeByte |= 0x01;
942        }
943        buffer.append(String.valueOf(modeByte));
944      }
945    
946    
947    
948      /**
949       * Decodes the provided string as a UNIX mode and retrieves the
950       * corresponding file permission.  The mode string must contain
951       * three digits between zero and seven.
952       *
953       * @param  modeString  The string representation of the UNIX mode to
954       *                     decode.
955       *
956       * @return  The file permission that is equivalent to the given UNIX
957       *          mode.
958       *
959       * @throws  DirectoryException  If the provided string is not a
960       *                              valid three-digit UNIX mode.
961       */
962      public static FilePermission decodeUNIXMode(String modeString)
963             throws DirectoryException
964      {
965        if ((modeString == null) || (modeString.length() != 3))
966        {
967          Message message = ERR_FILEPERM_INVALID_UNIX_MODE_STRING.get(
968              String.valueOf(modeString));
969          throw new DirectoryException(ResultCode.OTHER, message);
970        }
971    
972        int encodedPermission = 0x0000;
973        switch (modeString.charAt(0))
974        {
975          case '0':
976            break;
977          case '1':
978            encodedPermission |= OWNER_EXECUTABLE;
979            break;
980          case '2':
981            encodedPermission |= OWNER_WRITABLE;
982            break;
983          case '3':
984            encodedPermission |= OWNER_WRITABLE | OWNER_EXECUTABLE;
985            break;
986          case '4':
987            encodedPermission |= OWNER_READABLE;
988            break;
989          case '5':
990             encodedPermission |= OWNER_READABLE | OWNER_EXECUTABLE;
991            break;
992          case '6':
993            encodedPermission |= OWNER_READABLE | OWNER_WRITABLE;
994            break;
995          case '7':
996            encodedPermission |= OWNER_READABLE | OWNER_WRITABLE |
997                                 OWNER_EXECUTABLE;
998            break;
999          default:
1000          Message message = ERR_FILEPERM_INVALID_UNIX_MODE_STRING.get(
1001              String.valueOf(modeString));
1002          throw new DirectoryException(ResultCode.OTHER, message);
1003        }
1004    
1005        switch (modeString.charAt(1))
1006        {
1007          case '0':
1008            break;
1009          case '1':
1010            encodedPermission |= GROUP_EXECUTABLE;
1011            break;
1012          case '2':
1013            encodedPermission |= GROUP_WRITABLE;
1014            break;
1015          case '3':
1016            encodedPermission |= GROUP_WRITABLE | GROUP_EXECUTABLE;
1017            break;
1018          case '4':
1019            encodedPermission |= GROUP_READABLE;
1020            break;
1021          case '5':
1022             encodedPermission |= GROUP_READABLE | GROUP_EXECUTABLE;
1023            break;
1024          case '6':
1025            encodedPermission |= GROUP_READABLE | GROUP_WRITABLE;
1026            break;
1027          case '7':
1028            encodedPermission |= GROUP_READABLE | GROUP_WRITABLE |
1029                                 GROUP_EXECUTABLE;
1030            break;
1031          default:
1032          Message message = ERR_FILEPERM_INVALID_UNIX_MODE_STRING.get(
1033              String.valueOf(modeString));
1034          throw new DirectoryException(ResultCode.OTHER, message);
1035        }
1036    
1037        switch (modeString.charAt(2))
1038        {
1039          case '0':
1040            break;
1041          case '1':
1042            encodedPermission |= OTHER_EXECUTABLE;
1043            break;
1044          case '2':
1045            encodedPermission |= OTHER_WRITABLE;
1046            break;
1047          case '3':
1048            encodedPermission |= OTHER_WRITABLE | OTHER_EXECUTABLE;
1049            break;
1050          case '4':
1051            encodedPermission |= OTHER_READABLE;
1052            break;
1053          case '5':
1054             encodedPermission |= OTHER_READABLE | OTHER_EXECUTABLE;
1055            break;
1056          case '6':
1057            encodedPermission |= OTHER_READABLE | OTHER_WRITABLE;
1058            break;
1059          case '7':
1060            encodedPermission |= OTHER_READABLE | OTHER_WRITABLE |
1061                                 OTHER_EXECUTABLE;
1062            break;
1063          default:
1064          Message message = ERR_FILEPERM_INVALID_UNIX_MODE_STRING.get(
1065              String.valueOf(modeString));
1066          throw new DirectoryException(ResultCode.OTHER, message);
1067        }
1068    
1069        return new FilePermission(encodedPermission);
1070      }
1071    
1072    
1073    
1074      /**
1075       * Retrieves a string representation of this file permission.
1076       *
1077       * @return  A string representation of this file permission.
1078       */
1079      public String toString()
1080      {
1081        StringBuilder buffer = new StringBuilder();
1082        toString(buffer);
1083        return buffer.toString();
1084      }
1085    
1086    
1087    
1088      /**
1089       * Appends a string representation of this file permission to the
1090       * given buffer.
1091       *
1092       * @param  buffer  The buffer to which the data should be appended.
1093       */
1094      public void toString(StringBuilder buffer)
1095      {
1096        buffer.append("Owner=");
1097    
1098        if (isOwnerReadable())
1099        {
1100          buffer.append("r");
1101        }
1102        if (isOwnerWritable())
1103        {
1104          buffer.append("w");
1105        }
1106        if (isOwnerExecutable())
1107        {
1108          buffer.append("x");
1109        }
1110        buffer.append(", Group=");
1111    
1112        if (isGroupReadable())
1113        {
1114          buffer.append("r");
1115        }
1116        if (isGroupWritable())
1117        {
1118          buffer.append("w");
1119        }
1120        if (isGroupExecutable())
1121        {
1122          buffer.append("x");
1123        }
1124        buffer.append(", Other=");
1125    
1126        if (isOtherReadable())
1127        {
1128          buffer.append("r");
1129        }
1130        if (isOtherWritable())
1131        {
1132          buffer.append("w");
1133        }
1134        if (isOtherExecutable())
1135        {
1136          buffer.append("x");
1137        }
1138      }
1139    }
1140