All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.sf.mmm.util.file.base.FileAccessPermissions Maven / Gradle / Ivy

The newest version!
/* Copyright (c) The m-m-m Team, Licensed under the Apache License, Version 2.0
 * http://www.apache.org/licenses/LICENSE-2.0 */
package net.sf.mmm.util.file.base;

import net.sf.mmm.util.file.api.FileAccessClass;
import net.sf.mmm.util.filter.api.CharFilter;
import net.sf.mmm.util.scanner.base.CharSequenceScanner;

/**
 * This class represents the permissions of a file.
 *
 * @author Joerg Hohwiller (hohwille at users.sourceforge.net)
 * @since 1.0.1
 */
public class FileAccessPermissions implements Cloneable {

  private static final int MASK_MAX = 07777;

  private static final int MASK_USER = 0100;

  private static final int MASK_GROUP = 0010;

  private static final int MASK_OTHERS = 0001;

  private static final int MASK_ALL = 0111;

  /** @see #isReadable(FileAccessClass) */
  public static final int MASK_READABLE = 4;

  /** @see #isWritable(FileAccessClass) */
  public static final int MASK_WRITABLE = 2;

  /** @see #isExecutable(FileAccessClass) */
  public static final int MASK_EXECUTABLE = 1;

  /** @see #isSetuid() */
  public static final int MASK_SETUID = 04000;

  /** @see #isSetgid() */
  public static final int MASK_SETGID = 02000;

  /** @see #isSticky() */
  public static final int MASK_STICKY = 01000;

  private static final int MASK_USER_FLAGS = 00700;

  private static final int MASK_GROUP_FLAGS = 00070;

  private static final int MASK_OTHERS_FLAGS = 00007;

  /** @see #createByUmask(int, boolean) */
  private static final int MASK_FULL_FILE_ACCESS = 0666;

  /** @see #createByUmask(int, boolean) */
  private static final int MASK_FULL_DIRECTORY_ACCESS = 0777;

  private static final int MASK_ALL_EXECUTABLE = 0111;

  private static final int MASK_ALL_WRITABLE = 0222;

  private static final int MASK_ALL_READABLE = 0444;

  private int maskBits;

  /**
   * The constructor.
   */
  public FileAccessPermissions() {

    super();
  }

  /**
   * The constructor.
   *
   * @param mask is the {@link #getMaskBits() mask}.
   */
  public FileAccessPermissions(int mask) {

    super();
    setMaskBits(mask);
  }

  /**
   * This method create a new {@link FileAccessPermissions} instance according to the given
   * umask (user file creation mode mask).
   *
   * @param umask is the umask.
   * @param isDirectory {@code true} if the the {@link FileAccessPermissions} is to be created for a directory,
   *        {@code false} for a regular file.
   * @return the according {@link FileAccessPermissions}.
   */
  public static FileAccessPermissions createByUmask(int umask, boolean isDirectory) {

    int fullAccessMask;
    if (isDirectory) {
      fullAccessMask = MASK_FULL_DIRECTORY_ACCESS;
    } else {
      fullAccessMask = MASK_FULL_FILE_ACCESS;
    }
    int mask = fullAccessMask & ~umask;
    return new FileAccessPermissions(mask);
  }

  /**
   * This method gets the {@link FileAccessPermissions} encoded as a single integer value. The value is in the same
   * format as the octal notation for the command {@code chmod}. Only the last 12 bits of the mask can be set.
   *
   * @return the encoded mask.
   */
  public int getMaskBits() {

    return this.maskBits;
  }

  /**
   * This method sets the bitwise encoded {@link #getMaskBits() mask}.
   *
   * @param mask the mask to set.
   * @throws IllegalArgumentException if the given {@code mask} is negative or greater than {@code 07777} (==
   *         {@code 0xFFF} == {@code 4095}).
   */
  public void setMaskBits(int mask) throws IllegalArgumentException {

    if ((mask > MASK_MAX) || (mask < 0)) {
      throw new IllegalArgumentException("File mode mask out of range: " + mask);
    }
    this.maskBits = mask;
  }

  /**
   * This method determines if this {@link #getMaskBits() mask} is readable for the given {@code fileModeClass}.
   *
   * @param fileModeClass is the class of access ( {@link FileAccessClass#USER}, {@link FileAccessClass#GROUP}, or
   *        {@link FileAccessClass#OTHERS}).
   * @return {@code true} if the mask is readable for the given {@code fileModeClass}, {@code false} otherwise.
   */
  public boolean isReadable(FileAccessClass fileModeClass) {

    return hasFlag(fileModeClass, MASK_READABLE);
  }

  /**
   * This method sets the {@link #isReadable(FileAccessClass) readable flag} of this this {@link #getMaskBits() mask}
   * for ALL {@link FileAccessClass access-classes} to the given value ( {@code readable}).
   *
   * @param readable if {@code true} the mask will be readable, if {@code false} it will NOT be readable.
   */
  public void setReadable(boolean readable) {

    setBits(MASK_ALL_READABLE, readable);
  }

  /**
   * This method sets the {@link #isReadable(FileAccessClass) readable flag} of this this {@link #getMaskBits() mask}
   * for the given {@code fileModeClass} to the given value ( {@code readable}).
   *
   * @param fileModeClass is the class of access ( {@link FileAccessClass#USER}, {@link FileAccessClass#GROUP}, or
   *        {@link FileAccessClass#OTHERS}).
   * @param readable if {@code true} the mask will be readable for the given {@code fileModeClass}, if {@code false} it
   *        will NOT be readable.
   */
  public void setReadable(FileAccessClass fileModeClass, boolean readable) {

    setFlag(fileModeClass, MASK_READABLE, readable);
  }

  /**
   * This method determines if this {@link #getMaskBits() mask} is writable for the given {@code fileModeClass}.
   *
   * @param fileModeClass is the class of access ( {@link FileAccessClass#USER}, {@link FileAccessClass#GROUP}, or
   *        {@link FileAccessClass#OTHERS}).
   * @return {@code true} if the mask is writable for the given {@code fileModeClass}, {@code false} otherwise.
   */
  public boolean isWritable(FileAccessClass fileModeClass) {

    return hasFlag(fileModeClass, MASK_WRITABLE);
  }

  /**
   * This method sets the {@link #isWritable(FileAccessClass) writable flag} of this this {@link #getMaskBits() mask}
   * for ALL {@link FileAccessClass access-classes} to the given value ( {@code writable}).
   *
   * @param writable if {@code true} the mask will be writable, if {@code false} it will NOT be writable.
   */
  public void setWritable(boolean writable) {

    setBits(MASK_ALL_WRITABLE, writable);
  }

  /**
   * This method sets the {@link #isWritable(FileAccessClass) writable flag} of this this {@link #getMaskBits() mask}
   * for the given {@code fileModeClass} to the given value ( {@code writable}).
   *
   * @param fileModeClass is the class of access ( {@link FileAccessClass#USER}, {@link FileAccessClass#GROUP}, or
   *        {@link FileAccessClass#OTHERS}).
   * @param writable if {@code true} the mask will be writable for the given {@code fileModeClass}, if {@code false} it
   *        will NOT be writable.
   */
  public void setWritable(FileAccessClass fileModeClass, boolean writable) {

    setFlag(fileModeClass, MASK_WRITABLE, writable);
  }

  /**
   * This method determines if this {@link #getMaskBits() mask} is executable for the given {@code fileModeClass}.
   *
   * @param fileModeClass is the class of access ( {@link FileAccessClass#USER}, {@link FileAccessClass#GROUP}, or
   *        {@link FileAccessClass#OTHERS}).
   * @return {@code true} if the mask is executable for the given {@code fileModeClass}, {@code false} otherwise.
   */
  public boolean isExecutable(FileAccessClass fileModeClass) {

    return hasFlag(fileModeClass, MASK_EXECUTABLE);
  }

  /**
   * This method sets the {@link #isExecutable(FileAccessClass) executable flag} of this this {@link #getMaskBits()
   * mask} for ALL {@link FileAccessClass access-classes} to the given value ( {@code executable}).
   *
   * @param executable if {@code true} the mask will be executable, if {@code false} it will NOT be executable.
   */
  public void setExecutable(boolean executable) {

    setBits(MASK_ALL_EXECUTABLE, executable);
  }

  /**
   * This method sets the {@link #isExecutable(FileAccessClass) executable flag} of this this {@link #getMaskBits()
   * mask} for the given {@code fileModeClass} to the given value ( {@code executable}).
   *
   * @param fileModeClass is the class of access ( {@link FileAccessClass#USER}, {@link FileAccessClass#GROUP}, or
   *        {@link FileAccessClass#OTHERS}).
   * @param executable if {@code true} the mask will be executable for the given {@code fileModeClass}, if {@code false}
   *        it will NOT be executable.
   */
  public void setExecutable(FileAccessClass fileModeClass, boolean executable) {

    setFlag(fileModeClass, MASK_EXECUTABLE, executable);
  }

  /**
   * This method determines the value of the setuid flag ("set user ID"). If this flag is set and the file is
   * executed, the according process will be started under the user of the file- {@link FileAccessClass#USER owner}
   * instead of the user that performed the execution.
   *
   * @return {@code true} if the flag is set, {@code false} otherwise.
   */
  public boolean isSetuid() {

    return hasBits(MASK_SETUID);
  }

  /**
   * This method sets the {@link #isSetuid() setuid} flag to the given value {@code setuid}.
   *
   * @param setuid - if {@code true} the flag will be set, if {@code false} it will be unset.
   */
  public void setSetuid(boolean setuid) {

    setBits(MASK_SETUID, setuid);
  }

  /**
   * This method determines the value of the setgid flag ("set group ID"). If this flag is set and the file is
   * executed, the according process will be started under the {@link FileAccessClass#GROUP group owning the file}
   * instead of the group of the user that performed the execution.
   *
   * @return {@code true} if the flag is set, {@code false} otherwise.
   */
  public boolean isSetgid() {

    return hasBits(MASK_SETGID);
  }

  /**
   * This method sets the {@link #isSetuid() setuid} flag to the given value {@code setuid}.
   *
   * @param setgid - if {@code true} the flag will be set, if {@code false} it will be unset.
   */
  public void setSetgid(boolean setgid) {

    setBits(MASK_SETGID, setgid);
  }

  /**
   * This method determines the value of the sticky flag. This flag is mostly obsolete and is NOT used on linux
   * systems.
   *
   * @return {@code true} if the flag is set, {@code false} otherwise.
   */
  public boolean isSticky() {

    return hasBits(MASK_STICKY);
  }

  /**
   * This method sets the {@link #isSticky() sticky} flag to the given value {@code sticky}.
   *
   * @param sticky - if {@code true} the flag will be set, if {@code false} it will be unset.
   */
  public void setSticky(boolean sticky) {

    setBits(MASK_STICKY, sticky);
  }

  /**
   * This method shifts the given {@code bitMask} according to the given {@code fileModeClass}.
   *
   * @param fileModeClass is the class of access ( {@link FileAccessClass#USER}, {@link FileAccessClass#GROUP}, or
   *        {@link FileAccessClass#OTHERS}).
   * @param bitMask is the bit-mask to shift.
   * @return the shifted {@code bitMask}.
   */
  private int shiftMask(FileAccessClass fileModeClass, int bitMask) {

    if (fileModeClass == FileAccessClass.USER) {
      return bitMask << 6;
    } else if (fileModeClass == FileAccessClass.GROUP) {
      return bitMask << 3;
    } else if (fileModeClass == FileAccessClass.OTHERS) {
      return bitMask;
    }
    throw new IllegalArgumentException("Illegal FileModeClass: " + fileModeClass);
  }

  /**
   * This method sets the flag(s) given by {@code bitMask} of this this {@link #getMaskBits() mask} for the given
   * {@code fileModeClass} to the given value ({@code flag}).
   *
   *
   * @param fileModeClass is the class of access ( {@link FileAccessClass#USER}, {@link FileAccessClass#GROUP}, or
   *        {@link FileAccessClass#OTHERS}).
   * @param bitMask is the bit-mask of the flag(s) to set.
   * @param flag - if {@code true} the flag will be set, if {@code false} it will be unset.
   */
  private void setFlag(FileAccessClass fileModeClass, int bitMask, boolean flag) {

    setBits(shiftMask(fileModeClass, bitMask), flag);
  }

  /**
   * This method determines if the flag(s) given by {@code bitMask} is set in this {@link #getMaskBits() mask} for the
   * given {@code fileModeClass}.
   *
   * @param fileModeClass is the class of access ( {@link FileAccessClass#USER}, {@link FileAccessClass#GROUP}, or
   *        {@link FileAccessClass#OTHERS}).
   * @param bitMask is the bit-mask of the flag(s) to get.
   * @return {@code true} if the flag is set, {@code false} otherwise.
   */
  private boolean hasFlag(FileAccessClass fileModeClass, int bitMask) {

    return hasBits(shiftMask(fileModeClass, bitMask));
  }

  /**
   * This method determines if the flags given by {@code bitMask} are set in this {@link #getMaskBits() mask}.
   *
   * @param bitMask is the bit-mask of the flag(s) to check.
   * @return {@code true} if the flags are set, {@code false} otherwise.
   */
  private boolean hasBits(int bitMask) {

    return ((this.maskBits & bitMask) == bitMask);
  }

  /**
   * This method sets or unsets the flags given by {@code bitMask} in this {@link #getMaskBits() mask} according to the
   * given {@code flag}.
   *
   * @param bitMask is the bit-mask of the flag(s) to set or unset.
   * @param set - if {@code true} the flag(s) will be set, if {@code false} they will be unset.
   */
  private void setBits(int bitMask, boolean set) {

    if (set) {
      this.maskBits = this.maskBits | bitMask;
    } else {
      this.maskBits = this.maskBits & ~bitMask;
    }
  }

  /**
   * This method parses the {@code ugoa} prefix indicating which flags to modify.
   * 
    *
  • {@code u} indicates that the flags of the {@link FileAccessClass#USER user} should be changed.
  • *
  • {@code g} indicates that the flags of the {@link FileAccessClass#GROUP group} should be changed.
  • *
  • {@code o} indicates that the flags of the {@link FileAccessClass#OTHERS others} should be changed.
  • *
  • {@code a} indicates that the flags of all {@link FileAccessClass classes} should be changed.
  • *
* * @param parse is the current state of the parser. * @return the bit-mask with the UGO-flags. */ private static int parseUGO(CharSequenceScanner parse) { int ugo = 0; while (true) { char c = parse.forceNext(); if (c == 'u') { ugo = ugo | MASK_USER; } else if (c == 'g') { ugo = ugo | MASK_GROUP; } else if (c == 'o') { ugo = ugo | MASK_OTHERS; } else if (c == 'a') { ugo = MASK_ALL; } else { if (ugo == 0) { // if none of u/g/o/a was specified, then 'a' is the default ugo = MASK_ALL; } if (c != 0) { // we read too far parse.stepBack(); } return ugo; } } } /** * This method parses a symbolic-mode segment from {@code parse}. It applies the {@link #chmod(String) chmod} of that * segment to the given {@code mask} and returns the result. The state of this object remains unchanged. * * @param parse is the current state of the parser. * @param maskBits is the current modifier mask. * @return the changed {@code mask}. */ private static int parseSymbolicMode(CharSequenceScanner parse, int maskBits) { int mask = maskBits; int ugo = parseUGO(parse); char mode = parse.forceNext(); int flags = 0; int template = -1; char c = parse.forceNext(); if (c == 'u') { template = (mask & MASK_USER_FLAGS) >> 6; } else if (c == 'g') { template = (mask & MASK_GROUP_FLAGS) >> 3; } else if (c == 'o') { template = mask & MASK_OTHERS_FLAGS; } else { while (c != 0) { if (c == 'r') { flags = flags | (ugo << 2); } else if (c == 'w') { flags = flags | (ugo << 1); } else if (c == 'x') { flags = flags | ugo; } else if (c == 's') { flags = flags | MASK_SETUID; } else if (c == 'S') { flags = flags | MASK_SETGID; } else if (c == 't') { flags = flags | MASK_STICKY; } else { parse.stepBack(); break; } c = parse.forceNext(); } } if (template != -1) { if ((ugo & 0001) != 0) { flags = template; } if ((ugo & 0010) != 0) { flags = flags | (template << 3); } if ((ugo & 0100) != 0) { flags = flags | (template << 6); } } if (mode == '+') { mask = mask | flags; } else if (mode == '-') { mask = mask & ~flags; } else if (mode == '=') { int clear = ugo | (ugo << 1) | (ugo << 2); mask = mask & ~clear; mask = mask | flags; } else { throw new IllegalArgumentException("op: '" + mode + "'"); } return mask; } /** * This method parses the current state of the {@link #chmod(String) chmod} argument given by {@code parse} in octal * mode. If no digits are detected, {@code parse} remains unchanged and {@code -1} is returned, else all digits are * consumed and the parsed octal-mode is returned. * * @param parse is the current parser state of the the {@link #chmod(String) chmod} argument. * @return the parsed octal-mode or {@code -1} if {@code parse} does NOT point to a digit. * @throws IllegalArgumentException if there are more the 4 digits or non-octal digits (8 or 9). */ private static int parseOctalMode(CharSequenceScanner parse) throws IllegalArgumentException { String octals = parse.readWhile(CharFilter.LATIN_DIGIT_FILTER); if (octals.length() == 0) { return -1; } if (octals.length() > 4) { throw new IllegalArgumentException(); } return Integer.parseInt(octals, 8); } /** * This method allows to change the {@link #getMaskBits() modifiers} by a string expression. It behaves like the GNU * command {@code chmod} (change modifiers).
* Examples:
*
    *
  • {@code 0124}
    * sets the {@link #getMaskBits() modifiers} to {@code --x-w-r--}
  • *
  • {@code u=wx,g+w,o-r}
    * sets the {@link #getMaskBits() modifiers} to {@code -wx?w?-??}
  • *
* * @param chmod is the mode-list argument as supplied to the {@code chmod} command. It can either be a single * octal-mode (up to 4 digits of octal number) or a comma-separated list of symbolic-modes ( * {@code [ugoa]*([-+=]([rwxXst]*|[ugo]))+}). */ public void chmod(String chmod) { CharSequenceScanner parse = new CharSequenceScanner(chmod); if (!parse.hasNext()) { throw new IllegalArgumentException(); } int mask = this.maskBits; try { int octal = parseOctalMode(parse); if (octal == -1) { // no digits are available in parse... while (parse.hasNext()) { mask = parseSymbolicMode(parse, mask); char c = parse.forceNext(); if ((c != ',') && (c != 0)) { throw new IllegalArgumentException("'" + c + "'"); } } } else { mask = octal; char c = parse.forceNext(); if (c != 0) { throw new IllegalArgumentException("'" + c + "'"); } } } catch (IllegalArgumentException e) { throw new IllegalArgumentException("Illegal chmod: " + chmod, e); } this.maskBits = mask; } @Override public int hashCode() { return this.maskBits; } @Override public boolean equals(Object obj) { if (obj == null) { return false; } if (obj == this) { return true; } if (obj.getClass() != getClass()) { return false; } FileAccessPermissions otherMask = (FileAccessPermissions) obj; return (this.maskBits == otherMask.maskBits); } @Override public FileAccessPermissions clone() { try { return (FileAccessPermissions) super.clone(); } catch (CloneNotSupportedException e) { throw new IllegalStateException(e); } } @Override public String toString() { String result = Integer.toString(this.maskBits, 8); int len = result.length(); if (len == 1) { result = "000" + result; } else if (len == 2) { result = "00" + result; } else if (len == 3) { result = "0" + result; } return result; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy