@Test public void testLocalPluginInstallWithBinAndConfig() throws Exception { String pluginName = "plugin-test"; Tuple<Settings, Environment> initialSettings = InternalSettingsPreparer.prepareSettings( ImmutableSettings.settingsBuilder().build(), false); Environment env = initialSettings.v2(); Path binDir = env.homeFile().resolve("bin"); if (!Files.exists(binDir)) { Files.createDirectories(binDir); } Path pluginBinDir = binDir.resolve(pluginName); Path configDir = env.configFile(); if (!Files.exists(configDir)) { Files.createDirectories(configDir); } Path pluginConfigDir = configDir.resolve(pluginName); try { PluginManager pluginManager = pluginManager(getPluginUrlForResource("plugin_with_bin_and_config.zip"), initialSettings); pluginManager.downloadAndExtract(pluginName); Path[] plugins = pluginManager.getListInstalledPlugins(); assertThat(plugins, arrayWithSize(1)); assertDirectoryExists(pluginBinDir); assertDirectoryExists(pluginConfigDir); Path toolFile = pluginBinDir.resolve("tool"); assertFileExists(toolFile); // check that the file is marked executable, without actually checking that we can execute it. PosixFileAttributeView view = Files.getFileAttributeView(toolFile, PosixFileAttributeView.class); // the view might be null, on e.g. windows, there is nothing to check there! if (view != null) { PosixFileAttributes attributes = view.readAttributes(); assertTrue( "unexpected permissions: " + attributes.permissions(), attributes.permissions().contains(PosixFilePermission.OWNER_EXECUTE)); } } finally { // we need to clean up the copied dirs IOUtils.rm(pluginBinDir, pluginConfigDir); } }
public FileAttrs getAttrs() throws IOException { FileAttrs result; BasicFileAttributes attr; DosFileAttributes dosAttr; PosixFileAttributes posixAttr; result = new FileAttrs(); attr = Files.readAttributes(this.path.toPath(), BasicFileAttributes.class); result.setCtime(attr.creationTime().toMillis()); result.setMtime(attr.lastModifiedTime().toMillis()); // result.append("symlink", attr.isSymbolicLink()); //Redundant result.setSize(attr.size()); if (System.getProperty("os.name").startsWith("Windows")) { dosAttr = Files.readAttributes(this.path.toPath(), DosFileAttributes.class); result.setDosArchive(dosAttr.isArchive()); result.setDosHidden(dosAttr.isHidden()); result.setDosReadonly(dosAttr.isReadOnly()); result.setDosSystem(dosAttr.isSystem()); } else { posixAttr = Files.readAttributes(this.path.toPath(), PosixFileAttributes.class); result.setPosixSymlink(this.isSymlink()); if (result.getPosixSymlink()) { result.setLinkTo(Files.readSymbolicLink(this.path.toPath()).toString()); } result.setPosixOwner(posixAttr.owner().getName()); result.setPosixGroup(posixAttr.group().getName()); result.setPosixPermission(PosixFilePermissions.toString(posixAttr.permissions())); } return result; }
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; } } }
static void checkPosixAttributes(PosixFileAttributes attrs1, PosixFileAttributes attrs2) { assertTrue(attrs1.permissions().equals(attrs2.permissions())); assertTrue(attrs1.owner().equals(attrs2.owner())); assertTrue(attrs1.group().equals(attrs2.group())); }
/** * Invoked by readAttributes or sub-classes to add all matching posix attributes to the builder */ final void addRequestedPosixAttributes(PosixFileAttributes attrs, AttributesBuilder builder) { addRequestedBasicAttributes(attrs, builder); if (builder.match(PERMISSIONS_NAME)) builder.add(PERMISSIONS_NAME, attrs.permissions()); if (builder.match(OWNER_NAME)) builder.add(OWNER_NAME, attrs.owner()); if (builder.match(GROUP_NAME)) builder.add(GROUP_NAME, attrs.group()); }
void assertPlugin(String name, Path original, Environment env) throws IOException { Path got = env.pluginsFile().resolve(name); assertTrue("dir " + name + " exists", Files.exists(got)); if (isPosix) { Set<PosixFilePermission> perms = Files.getPosixFilePermissions(got); assertThat( perms, containsInAnyOrder( PosixFilePermission.OWNER_READ, PosixFilePermission.OWNER_WRITE, PosixFilePermission.OWNER_EXECUTE, PosixFilePermission.GROUP_READ, PosixFilePermission.GROUP_EXECUTE, PosixFilePermission.OTHERS_READ, PosixFilePermission.OTHERS_EXECUTE)); } assertTrue("jar was copied", Files.exists(got.resolve("plugin.jar"))); assertFalse("bin was not copied", Files.exists(got.resolve("bin"))); assertFalse("config was not copied", Files.exists(got.resolve("config"))); if (Files.exists(original.resolve("bin"))) { Path binDir = env.binFile().resolve(name); assertTrue("bin dir exists", Files.exists(binDir)); assertTrue("bin is a dir", Files.isDirectory(binDir)); PosixFileAttributes binAttributes = null; if (isPosix) { binAttributes = Files.readAttributes(env.binFile(), PosixFileAttributes.class); } try (DirectoryStream<Path> stream = Files.newDirectoryStream(binDir)) { for (Path file : stream) { assertFalse("not a dir", Files.isDirectory(file)); if (isPosix) { PosixFileAttributes attributes = Files.readAttributes(file, PosixFileAttributes.class); assertEquals(InstallPluginCommand.BIN_FILES_PERMS, attributes.permissions()); } } } } if (Files.exists(original.resolve("config"))) { Path configDir = env.configFile().resolve(name); assertTrue("config dir exists", Files.exists(configDir)); assertTrue("config is a dir", Files.isDirectory(configDir)); UserPrincipal user = null; GroupPrincipal group = null; if (isPosix) { PosixFileAttributes configAttributes = Files.getFileAttributeView(env.configFile(), PosixFileAttributeView.class) .readAttributes(); user = configAttributes.owner(); group = configAttributes.group(); PosixFileAttributes attributes = Files.getFileAttributeView(configDir, PosixFileAttributeView.class).readAttributes(); assertThat(attributes.owner(), equalTo(user)); assertThat(attributes.group(), equalTo(group)); } try (DirectoryStream<Path> stream = Files.newDirectoryStream(configDir)) { for (Path file : stream) { assertFalse("not a dir", Files.isDirectory(file)); if (isPosix) { PosixFileAttributes attributes = Files.readAttributes(file, PosixFileAttributes.class); if (user != null) { assertThat(attributes.owner(), equalTo(user)); } if (group != null) { assertThat(attributes.group(), equalTo(group)); } } } } } assertInstallCleaned(env); }