]> source.dussan.org Git - sonarqube.git/commitdiff
Fix regression introduced during previous scanner hardening
authorJulien HENRY <julien.henry@sonarsource.com>
Mon, 7 Aug 2017 16:11:58 +0000 (18:11 +0200)
committerJulien HENRY <julien.henry@sonarsource.com>
Tue, 8 Aug 2017 08:51:13 +0000 (10:51 +0200)
Module workDir is deleted when cleaning root module work dir when they are nested.

sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultInputModule.java
sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ProjectScanContainer.java
sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/WorkDirectoriesInitializer.java [new file with mode: 0644]
sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/WorkDirectoryCleaner.java [deleted file]
sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/WorkDirectoriesInitializerTest.java [new file with mode: 0644]
sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/WorkDirectoryCleanerTest.java [deleted file]

index e5d8a5c060be5455bbc9e0b5f90d1d5cb939c88b..d499e8e0adfd4324f6c2e1c1dff7a3bae0cee74d 100644 (file)
@@ -30,7 +30,6 @@ import java.util.List;
 import java.util.Map;
 import javax.annotation.CheckForNull;
 import javax.annotation.concurrent.Immutable;
-import org.apache.commons.io.FileUtils;
 import org.sonar.api.batch.bootstrap.ProjectDefinition;
 import org.sonar.api.batch.fs.InputModule;
 
@@ -93,11 +92,6 @@ public class DefaultInputModule extends DefaultInputComponent implements InputMo
 
   private static Path initWorkingDir(ProjectDefinition module) {
     File workingDirAsFile = module.getWorkDir();
-    try {
-      FileUtils.forceMkdir(workingDirAsFile);
-    } catch (Exception e) {
-      throw new IllegalStateException("Fail to create working dir: " + workingDirAsFile.getAbsolutePath(), e);
-    }
     return workingDirAsFile.getAbsoluteFile().toPath().normalize();
   }
 
index 4049d49014e5ece07703614a1ae2e15a87054d8d..d4ff556e8ae80dc9f74069110db444aa24d44f9e 100644 (file)
@@ -108,7 +108,7 @@ public class ProjectScanContainer extends ComponentContainer {
     addBatchExtensions();
     ProjectLock lock = getComponentByType(ProjectLock.class);
     lock.tryLock();
-    getComponentByType(WorkDirectoryCleaner.class).execute();
+    getComponentByType(WorkDirectoriesInitializer.class).execute();
     Settings settings = getComponentByType(Settings.class);
     if (settings != null && settings.getBoolean(CoreProperties.PROFILING_LOG_PROPERTY)) {
       add(PhasesSumUpTimeProfiler.class);
@@ -122,7 +122,7 @@ public class ProjectScanContainer extends ComponentContainer {
     add(
       props,
       ProjectReactorBuilder.class,
-      WorkDirectoryCleaner.class,
+      WorkDirectoriesInitializer.class,
       new MutableProjectReactorProvider(),
       ProjectBuildersExecutor.class,
       ProjectLock.class,
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/WorkDirectoriesInitializer.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/WorkDirectoriesInitializer.java
new file mode 100644 (file)
index 0000000..3ee70e9
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.scanner.scan;
+
+import java.io.IOException;
+import java.nio.file.DirectoryStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Iterator;
+import org.sonar.api.batch.fs.internal.DefaultInputModule;
+import org.sonar.api.batch.fs.internal.InputModuleHierarchy;
+import org.sonar.core.util.FileUtils;
+import org.sonar.home.cache.DirectoryLock;
+
+/**
+ * Clean and create working directories of each module.
+ * Be careful that sub module work dir might be nested in parent working directory.
+ */
+public class WorkDirectoriesInitializer {
+
+  private InputModuleHierarchy moduleHierarchy;
+
+  public WorkDirectoriesInitializer(InputModuleHierarchy moduleHierarchy) {
+    this.moduleHierarchy = moduleHierarchy;
+  }
+
+  public void execute() {
+    cleanAllWorkingDirs(moduleHierarchy.root());
+    mkdirsAllWorkingDirs(moduleHierarchy.root());
+  }
+
+  private void cleanAllWorkingDirs(DefaultInputModule module) {
+    for (DefaultInputModule sub : moduleHierarchy.children(module)) {
+      cleanAllWorkingDirs(sub);
+    }
+    if (Files.exists(module.getWorkDir())) {
+      deleteAllRecursivelyExceptLockFile(module.getWorkDir());
+    }
+  }
+
+  private void mkdirsAllWorkingDirs(DefaultInputModule module) {
+    for (DefaultInputModule sub : moduleHierarchy.children(module)) {
+      mkdirsAllWorkingDirs(sub);
+    }
+    try {
+      Files.createDirectories(module.getWorkDir());
+    } catch (Exception e) {
+      throw new IllegalStateException("Fail to create working dir: " + module.getWorkDir(), e);
+    }
+  }
+
+  private static void deleteAllRecursivelyExceptLockFile(Path dirToDelete) {
+    try (DirectoryStream<Path> stream = list(dirToDelete)) {
+
+      Iterator<Path> it = stream.iterator();
+      while (it.hasNext()) {
+        FileUtils.deleteQuietly(it.next().toFile());
+      }
+    } catch (IOException e) {
+      throw new IllegalStateException("Failed to clean working directory: " + dirToDelete.toString(), e);
+    }
+  }
+
+  private static DirectoryStream<Path> list(Path dir) throws IOException {
+    return Files.newDirectoryStream(dir, entry -> !DirectoryLock.LOCK_FILE_NAME.equals(entry.getFileName().toString()));
+  }
+}
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/WorkDirectoryCleaner.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/WorkDirectoryCleaner.java
deleted file mode 100644 (file)
index 83a9e86..0000000
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2017 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.scanner.scan;
-
-import java.io.IOException;
-import java.nio.file.DirectoryStream;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.util.Iterator;
-import org.sonar.api.batch.fs.internal.InputModuleHierarchy;
-import org.sonar.core.util.FileUtils;
-import org.sonar.home.cache.DirectoryLock;
-
-public class WorkDirectoryCleaner {
-  private final Path workDir;
-
-  public WorkDirectoryCleaner(InputModuleHierarchy moduleHierarchy) {
-    workDir = moduleHierarchy.root().getWorkDir();
-  }
-
-  public void execute() {
-    if (!workDir.toFile().exists()) {
-      return;
-    }
-
-    try (DirectoryStream<Path> stream = list()) {
-
-      Iterator<Path> it = stream.iterator();
-      while (it.hasNext()) {
-        FileUtils.deleteQuietly(it.next().toFile());
-      }
-    } catch (IOException e) {
-      throw new IllegalStateException("Failed to clean working directory: " + workDir.toString(), e);
-    }
-  }
-
-  private DirectoryStream<Path> list() throws IOException {
-    return Files.newDirectoryStream(workDir, entry -> !DirectoryLock.LOCK_FILE_NAME.equals(entry.getFileName().toString()));
-  }
-}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/WorkDirectoriesInitializerTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/WorkDirectoriesInitializerTest.java
new file mode 100644 (file)
index 0000000..3000f93
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.scanner.scan;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Arrays;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.sonar.api.batch.fs.internal.DefaultInputModule;
+import org.sonar.api.batch.fs.internal.InputModuleHierarchy;
+import org.sonar.home.cache.DirectoryLock;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class WorkDirectoriesInitializerTest {
+  private WorkDirectoriesInitializer initializer;
+  @Rule
+  public TemporaryFolder temp = new TemporaryFolder();
+
+  private File rootWorkDir;
+  private File lock;
+  private InputModuleHierarchy hierarchy;
+  private DefaultInputModule root;
+
+  @Before
+  public void setUp() throws IOException {
+    rootWorkDir = temp.newFolder();
+    // create files to clean
+    new File(rootWorkDir, "foo.txt").createNewFile();
+    File newFolder = new File(rootWorkDir, "foo");
+    newFolder.mkdir();
+    File fileInFolder = new File(newFolder, "test");
+    fileInFolder.createNewFile();
+
+    lock = new File(rootWorkDir, DirectoryLock.LOCK_FILE_NAME);
+    lock.createNewFile();
+
+    hierarchy = mock(InputModuleHierarchy.class);
+    root = mock(DefaultInputModule.class);
+    when(hierarchy.root()).thenReturn(root);
+    when(root.getWorkDir()).thenReturn(rootWorkDir.toPath());
+
+    assertThat(rootWorkDir.list().length).isGreaterThan(1);
+    initializer = new WorkDirectoriesInitializer(hierarchy);
+  }
+
+  @Test
+  public void testNonExisting() {
+    temp.delete();
+    initializer.execute();
+  }
+
+  @Test
+  public void testClean() {
+    initializer.execute();
+
+    assertThat(rootWorkDir).exists();
+    assertThat(lock).exists();
+    assertThat(rootWorkDir.list()).containsOnly(DirectoryLock.LOCK_FILE_NAME);
+  }
+
+  @Test
+  public void cleaningRootModuleShouldNotDeleteChildrenWorkDir() throws IOException {
+    DefaultInputModule moduleA = mock(DefaultInputModule.class);
+    when(hierarchy.children(root)).thenReturn(Arrays.asList(moduleA));
+    File moduleAWorkdir = new File(rootWorkDir, "moduleA");
+    when(moduleA.getWorkDir()).thenReturn(moduleAWorkdir.toPath());
+    moduleAWorkdir.mkdir();
+    new File(moduleAWorkdir, "fooA.txt").createNewFile();
+
+    initializer.execute();
+
+    assertThat(rootWorkDir).exists();
+    assertThat(lock).exists();
+    assertThat(rootWorkDir.list()).containsOnly(DirectoryLock.LOCK_FILE_NAME, "moduleA");
+    assertThat(moduleAWorkdir).exists();
+  }
+
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/WorkDirectoryCleanerTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/WorkDirectoryCleanerTest.java
deleted file mode 100644 (file)
index 8288682..0000000
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2017 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.scanner.scan;
-
-import java.io.File;
-import java.io.IOException;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.TemporaryFolder;
-import org.sonar.api.batch.fs.internal.DefaultInputModule;
-import org.sonar.api.batch.fs.internal.InputModuleHierarchy;
-import org.sonar.home.cache.DirectoryLock;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-public class WorkDirectoryCleanerTest {
-  private WorkDirectoryCleaner cleaner;
-  @Rule
-  public TemporaryFolder temp = new TemporaryFolder();
-
-  @Before
-  public void setUp() throws IOException {
-    // create files to clean
-    temp.newFile();
-    File newFolder = temp.newFolder();
-    File fileInFolder = new File(newFolder, "test");
-    fileInFolder.createNewFile();
-
-    File lock = new File(temp.getRoot(), DirectoryLock.LOCK_FILE_NAME);
-    lock.createNewFile();
-
-    // mock project
-    InputModuleHierarchy hierarchy = mock(InputModuleHierarchy.class);
-    DefaultInputModule root = mock(DefaultInputModule.class);
-    when(hierarchy.root()).thenReturn(root);
-    when(root.getWorkDir()).thenReturn(temp.getRoot().toPath());
-
-    assertThat(temp.getRoot().list().length).isGreaterThan(1);
-    cleaner = new WorkDirectoryCleaner(hierarchy);
-  }
-
-  @Test
-  public void testNonExisting() {
-    temp.delete();
-    cleaner.execute();
-  }
-
-  @Test
-  public void testClean() {
-    File lock = new File(temp.getRoot(), DirectoryLock.LOCK_FILE_NAME);
-    cleaner.execute();
-
-    assertThat(temp.getRoot()).exists();
-    assertThat(lock).exists();
-    assertThat(temp.getRoot().list()).containsOnly(DirectoryLock.LOCK_FILE_NAME);
-  }
-
-}