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; }
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()); } }
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"); }