public static void main(String[] args) throws Exception {
    boolean followLinks = false;
    boolean printCycles = false;
    int i = 0;
    while (i < (args.length - 1)) {
      switch (args[i]) {
        case "-follow":
          followLinks = true;
          break;
        case "-printCycles":
          printCycles = true;
          break;
        default:
          throw new RuntimeException(args[i] + " not recognized");
      }
      i++;
    }
    Path dir = Paths.get(args[i]);

    Set<FileVisitOption> options = new HashSet<FileVisitOption>();
    if (followLinks) options.add(FileVisitOption.FOLLOW_LINKS);

    final boolean follow = followLinks;
    final boolean reportCycles = printCycles;
    Files.walkFileTree(
        dir,
        options,
        Integer.MAX_VALUE,
        new FileVisitor<Path>() {
          @Override
          public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) {
            System.out.println(dir);
            return FileVisitResult.CONTINUE;
          }

          @Override
          public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
            System.out.println(file);
            return FileVisitResult.CONTINUE;
          }

          @Override
          public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
            if (exc != null) throw exc;
            return FileVisitResult.CONTINUE;
          }

          @Override
          public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException {
            if (follow && (exc instanceof FileSystemLoopException)) {
              if (reportCycles) System.out.println(file);
              return FileVisitResult.CONTINUE;
            } else {
              throw exc;
            }
          }
        });
  }
  // "randomize" the file attributes of the given file.
  static void randomizeAttributes(Path file) throws IOException {
    String os = System.getProperty("os.name");
    boolean isWindows = os.startsWith("Windows");
    boolean isUnix = os.equals("SunOS") || os.equals("Linux");
    boolean isDirectory = isDirectory(file, NOFOLLOW_LINKS);

    if (isUnix) {
      Set<PosixFilePermission> perms = getPosixFilePermissions(file, NOFOLLOW_LINKS);
      PosixFilePermission[] toChange = {
        PosixFilePermission.GROUP_READ,
        PosixFilePermission.GROUP_WRITE,
        PosixFilePermission.GROUP_EXECUTE,
        PosixFilePermission.OTHERS_READ,
        PosixFilePermission.OTHERS_WRITE,
        PosixFilePermission.OTHERS_EXECUTE
      };
      for (PosixFilePermission perm : toChange) {
        if (heads()) {
          perms.add(perm);
        } else {
          perms.remove(perm);
        }
      }
      setPosixFilePermissions(file, perms);
    }

    if (isWindows) {
      DosFileAttributeView view =
          getFileAttributeView(file, DosFileAttributeView.class, NOFOLLOW_LINKS);
      // only set or unset the hidden attribute
      view.setHidden(heads());
    }

    boolean addUserDefinedFileAttributes =
        heads() && getFileStore(file).supportsFileAttributeView("xattr");

    // remove this when copying a direcory copies its named streams
    if (isWindows && isDirectory) addUserDefinedFileAttributes = false;

    if (addUserDefinedFileAttributes) {
      UserDefinedFileAttributeView view =
          getFileAttributeView(file, UserDefinedFileAttributeView.class);
      int n = rand.nextInt(16);
      while (n > 0) {
        byte[] value = new byte[1 + rand.nextInt(100)];
        view.write("user." + Integer.toString(n), ByteBuffer.wrap(value));
        n--;
      }
    }
  }
 // indicates if the siblings of this path should be skipped
 static boolean skip(Path path) {
   Path parent = path.getParent();
   if (parent != null && rand.nextBoolean()) {
     skipped.add(parent);
     return true;
   }
   return false;
 }
Exemple #4
0
  private void copyBinDirectory(
      Path sourcePluginBinDirectory,
      Path destPluginBinDirectory,
      String pluginName,
      Terminal terminal)
      throws IOException {
    boolean canCopyFromSource =
        Files.exists(sourcePluginBinDirectory)
            && Files.isReadable(sourcePluginBinDirectory)
            && Files.isDirectory(sourcePluginBinDirectory);
    if (canCopyFromSource) {
      terminal.println(VERBOSE, "Found bin, moving to %s", destPluginBinDirectory.toAbsolutePath());
      if (Files.exists(destPluginBinDirectory)) {
        IOUtils.rm(destPluginBinDirectory);
      }
      try {
        Files.createDirectories(destPluginBinDirectory.getParent());
        FileSystemUtils.move(sourcePluginBinDirectory, destPluginBinDirectory);
      } catch (IOException e) {
        throw new IOException(
            "Could not move [" + sourcePluginBinDirectory + "] to [" + destPluginBinDirectory + "]",
            e);
      }
      if (Environment.getFileStore(destPluginBinDirectory)
          .supportsFileAttributeView(PosixFileAttributeView.class)) {
        final PosixFileAttributes parentDirAttributes =
            Files.getFileAttributeView(
                    destPluginBinDirectory.getParent(), PosixFileAttributeView.class)
                .readAttributes();
        // copy permissions from parent bin directory
        final Set<PosixFilePermission> filePermissions = new HashSet<>();
        for (PosixFilePermission posixFilePermission : parentDirAttributes.permissions()) {
          switch (posixFilePermission) {
            case OWNER_EXECUTE:
            case GROUP_EXECUTE:
            case OTHERS_EXECUTE:
              break;
            default:
              filePermissions.add(posixFilePermission);
          }
        }
        // add file execute permissions to existing perms, so execution will work.
        filePermissions.add(PosixFilePermission.OWNER_EXECUTE);
        filePermissions.add(PosixFilePermission.GROUP_EXECUTE);
        filePermissions.add(PosixFilePermission.OTHERS_EXECUTE);
        Files.walkFileTree(
            destPluginBinDirectory,
            new SimpleFileVisitor<Path>() {
              @Override
              public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
                  throws IOException {
                if (attrs.isRegularFile()) {
                  setPosixFileAttributes(
                      file,
                      parentDirAttributes.owner(),
                      parentDirAttributes.group(),
                      filePermissions);
                }
                return FileVisitResult.CONTINUE;
              }

              @Override
              public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs)
                  throws IOException {
                setPosixFileAttributes(
                    dir,
                    parentDirAttributes.owner(),
                    parentDirAttributes.group(),
                    parentDirAttributes.permissions());
                return FileVisitResult.CONTINUE;
              }
            });
      } else {
        terminal.println(
            VERBOSE, "Skipping posix permissions - filestore doesn't support posix permission");
      }
      terminal.println(
          VERBOSE, "Installed %s into %s", pluginName, destPluginBinDirectory.toAbsolutePath());
    }
  }
Exemple #5
0
  private void extract(PluginHandle pluginHandle, Terminal terminal, Path pluginFile)
      throws IOException {
    // unzip plugin to a staging temp dir, named for the plugin
    Path tmp = Files.createTempDirectory(environment.tmpFile(), null);
    Path root = tmp.resolve(pluginHandle.name);
    unzipPlugin(pluginFile, root);

    // find the actual root (in case its unzipped with extra directory wrapping)
    root = findPluginRoot(root);

    // read and validate the plugin descriptor
    PluginInfo info = PluginInfo.readFromProperties(root);
    terminal.println(VERBOSE, "%s", info);

    // update name in handle based on 'name' property found in descriptor file
    pluginHandle = new PluginHandle(info.getName(), pluginHandle.version, pluginHandle.user);
    final Path extractLocation = pluginHandle.extractedDir(environment);
    if (Files.exists(extractLocation)) {
      throw new IOException(
          "plugin directory "
              + extractLocation.toAbsolutePath()
              + " already exists. To update the plugin, uninstall it first using 'remove "
              + pluginHandle.name
              + "' command");
    }

    // check for jar hell before any copying
    if (info.isJvm()) {
      jarHellCheck(root, info.isIsolated());
    }

    // install plugin
    FileSystemUtils.copyDirectoryRecursively(root, extractLocation);
    terminal.println("Installed %s into %s", pluginHandle.name, extractLocation.toAbsolutePath());

    // cleanup
    tryToDeletePath(terminal, tmp, pluginFile);

    // take care of bin/ by moving and applying permissions if needed
    Path sourcePluginBinDirectory = extractLocation.resolve("bin");
    Path destPluginBinDirectory = pluginHandle.binDir(environment);
    boolean needToCopyBinDirectory = Files.exists(sourcePluginBinDirectory);
    if (needToCopyBinDirectory) {
      if (Files.exists(destPluginBinDirectory) && !Files.isDirectory(destPluginBinDirectory)) {
        tryToDeletePath(terminal, extractLocation);
        throw new IOException(
            "plugin bin directory " + destPluginBinDirectory + " is not a directory");
      }

      try {
        copyBinDirectory(
            sourcePluginBinDirectory, destPluginBinDirectory, pluginHandle.name, terminal);
      } catch (IOException e) {
        // rollback and remove potentially before installed leftovers
        terminal.printError(
            "Error copying bin directory [%s] to [%s], cleaning up, reason: %s",
            sourcePluginBinDirectory, destPluginBinDirectory, ExceptionsHelper.detailedMessage(e));
        tryToDeletePath(terminal, extractLocation, pluginHandle.binDir(environment));
        throw e;
      }
    }

    Path sourceConfigDirectory = extractLocation.resolve("config");
    Path destConfigDirectory = pluginHandle.configDir(environment);
    boolean needToCopyConfigDirectory = Files.exists(sourceConfigDirectory);
    if (needToCopyConfigDirectory) {
      if (Files.exists(destConfigDirectory) && !Files.isDirectory(destConfigDirectory)) {
        tryToDeletePath(terminal, extractLocation, destPluginBinDirectory);
        throw new IOException(
            "plugin config directory " + destConfigDirectory + " is not a directory");
      }

      try {
        terminal.println(
            VERBOSE, "Found config, moving to %s", destConfigDirectory.toAbsolutePath());
        moveFilesWithoutOverwriting(sourceConfigDirectory, destConfigDirectory, ".new");

        if (Environment.getFileStore(destConfigDirectory)
            .supportsFileAttributeView(PosixFileAttributeView.class)) {
          // We copy owner, group and permissions from the parent ES_CONFIG directory, assuming they
          // were properly set depending
          // on how es was installed in the first place: can be root:elasticsearch (750) if es was
          // installed from rpm/deb packages
          // or most likely elasticsearch:elasticsearch if installed from tar/zip. As for
          // permissions we don't rely on umask.
          final PosixFileAttributes parentDirAttributes =
              Files.getFileAttributeView(
                      destConfigDirectory.getParent(), PosixFileAttributeView.class)
                  .readAttributes();
          // for files though, we make sure not to copy execute permissions from the parent dir and
          // leave them untouched
          final Set<PosixFilePermission> baseFilePermissions = new HashSet<>();
          for (PosixFilePermission posixFilePermission : parentDirAttributes.permissions()) {
            switch (posixFilePermission) {
              case OWNER_EXECUTE:
              case GROUP_EXECUTE:
              case OTHERS_EXECUTE:
                break;
              default:
                baseFilePermissions.add(posixFilePermission);
            }
          }
          Files.walkFileTree(
              destConfigDirectory,
              new SimpleFileVisitor<Path>() {
                @Override
                public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
                    throws IOException {
                  if (attrs.isRegularFile()) {
                    Set<PosixFilePermission> newFilePermissions =
                        new HashSet<>(baseFilePermissions);
                    Set<PosixFilePermission> currentFilePermissions =
                        Files.getPosixFilePermissions(file);
                    for (PosixFilePermission posixFilePermission : currentFilePermissions) {
                      switch (posixFilePermission) {
                        case OWNER_EXECUTE:
                        case GROUP_EXECUTE:
                        case OTHERS_EXECUTE:
                          newFilePermissions.add(posixFilePermission);
                      }
                    }
                    setPosixFileAttributes(
                        file,
                        parentDirAttributes.owner(),
                        parentDirAttributes.group(),
                        newFilePermissions);
                  }
                  return FileVisitResult.CONTINUE;
                }

                @Override
                public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs)
                    throws IOException {
                  setPosixFileAttributes(
                      dir,
                      parentDirAttributes.owner(),
                      parentDirAttributes.group(),
                      parentDirAttributes.permissions());
                  return FileVisitResult.CONTINUE;
                }
              });
        } else {
          terminal.println(
              VERBOSE, "Skipping posix permissions - filestore doesn't support posix permission");
        }

        terminal.println(
            VERBOSE,
            "Installed %s into %s",
            pluginHandle.name,
            destConfigDirectory.toAbsolutePath());
      } catch (IOException e) {
        terminal.printError(
            "Error copying config directory [%s] to [%s], cleaning up, reason: %s",
            sourceConfigDirectory, destConfigDirectory, ExceptionsHelper.detailedMessage(e));
        tryToDeletePath(terminal, extractLocation, destPluginBinDirectory, destConfigDirectory);
        throw e;
      }
    }
  }
  /** Encode the ACL to the given buffer */
  private static void encode(List<AclEntry> acl, long address) {
    long offset = address;
    for (AclEntry ace : acl) {
      int flags = 0;

      // map UserPrincipal to uid and flags
      UserPrincipal who = ace.principal();
      if (!(who instanceof UnixUserPrincipals.User)) throw new ProviderMismatchException();
      UnixUserPrincipals.User user = (UnixUserPrincipals.User) who;
      int uid;
      if (user.isSpecial()) {
        uid = -1;
        if (who == UnixUserPrincipals.SPECIAL_OWNER) flags |= ACE_OWNER;
        else if (who == UnixUserPrincipals.SPECIAL_GROUP)
          flags |= (ACE_GROUP | ACE_IDENTIFIER_GROUP);
        else if (who == UnixUserPrincipals.SPECIAL_EVERYONE) flags |= ACE_EVERYONE;
        else throw new AssertionError("Unable to map special identifier");
      } else {
        if (user instanceof UnixUserPrincipals.Group) {
          uid = user.gid();
          flags |= ACE_IDENTIFIER_GROUP;
        } else {
          uid = user.uid();
        }
      }

      // map ACE type
      int type;
      switch (ace.type()) {
        case ALLOW:
          type = ACE_ACCESS_ALLOWED_ACE_TYPE;
          break;
        case DENY:
          type = ACE_ACCESS_DENIED_ACE_TYPE;
          break;
        case AUDIT:
          type = ACE_SYSTEM_AUDIT_ACE_TYPE;
          break;
        case ALARM:
          type = ACE_SYSTEM_ALARM_ACE_TYPE;
          break;
        default:
          throw new AssertionError("Unable to map ACE type");
      }

      // map permissions
      Set<AclEntryPermission> aceMask = ace.permissions();
      int mask = 0;
      if (aceMask.contains(AclEntryPermission.READ_DATA)) mask |= ACE_READ_DATA;
      if (aceMask.contains(AclEntryPermission.WRITE_DATA)) mask |= ACE_WRITE_DATA;
      if (aceMask.contains(AclEntryPermission.APPEND_DATA)) mask |= ACE_APPEND_DATA;
      if (aceMask.contains(AclEntryPermission.READ_NAMED_ATTRS)) mask |= ACE_READ_NAMED_ATTRS;
      if (aceMask.contains(AclEntryPermission.WRITE_NAMED_ATTRS)) mask |= ACE_WRITE_NAMED_ATTRS;
      if (aceMask.contains(AclEntryPermission.EXECUTE)) mask |= ACE_EXECUTE;
      if (aceMask.contains(AclEntryPermission.DELETE_CHILD)) mask |= ACE_DELETE_CHILD;
      if (aceMask.contains(AclEntryPermission.READ_ATTRIBUTES)) mask |= ACE_READ_ATTRIBUTES;
      if (aceMask.contains(AclEntryPermission.WRITE_ATTRIBUTES)) mask |= ACE_WRITE_ATTRIBUTES;
      if (aceMask.contains(AclEntryPermission.DELETE)) mask |= ACE_DELETE;
      if (aceMask.contains(AclEntryPermission.READ_ACL)) mask |= ACE_READ_ACL;
      if (aceMask.contains(AclEntryPermission.WRITE_ACL)) mask |= ACE_WRITE_ACL;
      if (aceMask.contains(AclEntryPermission.WRITE_OWNER)) mask |= ACE_WRITE_OWNER;
      if (aceMask.contains(AclEntryPermission.SYNCHRONIZE)) mask |= ACE_SYNCHRONIZE;

      // FIXME - it would be desirable to know here if the file is a
      // directory or not. Solaris returns EINVAL if an ACE has a directory
      // -only flag and the file is not a directory.
      Set<AclEntryFlag> aceFlags = ace.flags();
      if (aceFlags.contains(AclEntryFlag.FILE_INHERIT)) flags |= ACE_FILE_INHERIT_ACE;
      if (aceFlags.contains(AclEntryFlag.DIRECTORY_INHERIT)) flags |= ACE_DIRECTORY_INHERIT_ACE;
      if (aceFlags.contains(AclEntryFlag.NO_PROPAGATE_INHERIT))
        flags |= ACE_NO_PROPAGATE_INHERIT_ACE;
      if (aceFlags.contains(AclEntryFlag.INHERIT_ONLY)) flags |= ACE_INHERIT_ONLY_ACE;

      unsafe.putInt(offset + OFFSETOF_UID, uid);
      unsafe.putInt(offset + OFFSETOF_MASK, mask);
      unsafe.putShort(offset + OFFSETOF_FLAGS, (short) flags);
      unsafe.putShort(offset + OFFSETOF_TYPE, (short) type);

      offset += SIZEOF_ACE_T;
    }
  }
  /** Decode the buffer, returning an ACL */
  private static List<AclEntry> decode(long address, int n) {
    ArrayList<AclEntry> acl = new ArrayList<>(n);
    for (int i = 0; i < n; i++) {
      long offset = address + i * SIZEOF_ACE_T;

      int uid = unsafe.getInt(offset + OFFSETOF_UID);
      int mask = unsafe.getInt(offset + OFFSETOF_MASK);
      int flags = (int) unsafe.getShort(offset + OFFSETOF_FLAGS);
      int type = (int) unsafe.getShort(offset + OFFSETOF_TYPE);

      // map uid and flags to UserPrincipal
      UnixUserPrincipals.User who = null;
      if ((flags & ACE_OWNER) > 0) {
        who = UnixUserPrincipals.SPECIAL_OWNER;
      } else if ((flags & ACE_GROUP) > 0) {
        who = UnixUserPrincipals.SPECIAL_GROUP;
      } else if ((flags & ACE_EVERYONE) > 0) {
        who = UnixUserPrincipals.SPECIAL_EVERYONE;
      } else if ((flags & ACE_IDENTIFIER_GROUP) > 0) {
        who = UnixUserPrincipals.fromGid(uid);
      } else {
        who = UnixUserPrincipals.fromUid(uid);
      }

      AclEntryType aceType = null;
      switch (type) {
        case ACE_ACCESS_ALLOWED_ACE_TYPE:
          aceType = AclEntryType.ALLOW;
          break;
        case ACE_ACCESS_DENIED_ACE_TYPE:
          aceType = AclEntryType.DENY;
          break;
        case ACE_SYSTEM_AUDIT_ACE_TYPE:
          aceType = AclEntryType.AUDIT;
          break;
        case ACE_SYSTEM_ALARM_ACE_TYPE:
          aceType = AclEntryType.ALARM;
          break;
        default:
          assert false;
      }

      Set<AclEntryPermission> aceMask = EnumSet.noneOf(AclEntryPermission.class);
      if ((mask & ACE_READ_DATA) > 0) aceMask.add(AclEntryPermission.READ_DATA);
      if ((mask & ACE_WRITE_DATA) > 0) aceMask.add(AclEntryPermission.WRITE_DATA);
      if ((mask & ACE_APPEND_DATA) > 0) aceMask.add(AclEntryPermission.APPEND_DATA);
      if ((mask & ACE_READ_NAMED_ATTRS) > 0) aceMask.add(AclEntryPermission.READ_NAMED_ATTRS);
      if ((mask & ACE_WRITE_NAMED_ATTRS) > 0) aceMask.add(AclEntryPermission.WRITE_NAMED_ATTRS);
      if ((mask & ACE_EXECUTE) > 0) aceMask.add(AclEntryPermission.EXECUTE);
      if ((mask & ACE_DELETE_CHILD) > 0) aceMask.add(AclEntryPermission.DELETE_CHILD);
      if ((mask & ACE_READ_ATTRIBUTES) > 0) aceMask.add(AclEntryPermission.READ_ATTRIBUTES);
      if ((mask & ACE_WRITE_ATTRIBUTES) > 0) aceMask.add(AclEntryPermission.WRITE_ATTRIBUTES);
      if ((mask & ACE_DELETE) > 0) aceMask.add(AclEntryPermission.DELETE);
      if ((mask & ACE_READ_ACL) > 0) aceMask.add(AclEntryPermission.READ_ACL);
      if ((mask & ACE_WRITE_ACL) > 0) aceMask.add(AclEntryPermission.WRITE_ACL);
      if ((mask & ACE_WRITE_OWNER) > 0) aceMask.add(AclEntryPermission.WRITE_OWNER);
      if ((mask & ACE_SYNCHRONIZE) > 0) aceMask.add(AclEntryPermission.SYNCHRONIZE);

      Set<AclEntryFlag> aceFlags = EnumSet.noneOf(AclEntryFlag.class);
      if ((flags & ACE_FILE_INHERIT_ACE) > 0) aceFlags.add(AclEntryFlag.FILE_INHERIT);
      if ((flags & ACE_DIRECTORY_INHERIT_ACE) > 0) aceFlags.add(AclEntryFlag.DIRECTORY_INHERIT);
      if ((flags & ACE_NO_PROPAGATE_INHERIT_ACE) > 0)
        aceFlags.add(AclEntryFlag.NO_PROPAGATE_INHERIT);
      if ((flags & ACE_INHERIT_ONLY_ACE) > 0) aceFlags.add(AclEntryFlag.INHERIT_ONLY);

      // build the ACL entry and add it to the list
      AclEntry ace =
          AclEntry.newBuilder()
              .setType(aceType)
              .setPrincipal(who)
              .setPermissions(aceMask)
              .setFlags(aceFlags)
              .build();
      acl.add(ace);
    }

    return acl;
  }
 // check if this path's directory has been skipped
 static void check(Path path) {
   if (skipped.contains(path.getParent()))
     throw new RuntimeException(path + " should not have been visited");
 }