From 29da655cee61925bf8b1848e814899fc4f300a13 Mon Sep 17 00:00:00 2001 From: Maxat Mansurov Date: Sat, 25 Mar 2023 21:17:01 +0100 Subject: [PATCH] RUBY-31136 add option to `WslSync` for creating stubs for filtered out files. (cherry picked from commit fdadb26083074b6e510df5c8b66bb3ee13bdb8c3) IJ-CR-105378 GitOrigin-RevId: b5d0d16b1bba3802191775d9400b23205242238f --- bin/win/aarch64/wslhash | Bin 302032 -> 302032 bytes bin/win/amd64/wslhash | Bin 126696 -> 126696 bytes native/WslTools/wslhash.c | 38 +++++--- .../com/intellij/execution/wsl/WslSyncTest.kt | 58 ++++++++++--- .../execution/wsl/sync/FileStorage.kt | 14 +-- .../execution/wsl/sync/LinuxFileStorage.kt | 68 ++++++++++----- .../execution/wsl/sync/WindowsFileStorage.kt | 82 +++++++++++------- .../intellij/execution/wsl/sync/WslSync.kt | 80 ++++++++++------- .../execution/wsl/sync/WslSyncData.kt | 11 +++ 9 files changed, 237 insertions(+), 114 deletions(-) create mode 100644 platform/wsl-impl/src/com/intellij/execution/wsl/sync/WslSyncData.kt diff --git a/bin/win/aarch64/wslhash b/bin/win/aarch64/wslhash index eb6b435e1f5360024cfde8f75a4814073cfe409d..a5a2cd7ddca9aaac54b58c7bf6df3faa282e5d6e 100644 GIT binary patch delta 16424 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 UUdf`ViBP0i>xvng9R* delta 16572 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 HZ?^vrY`0`3 diff --git a/bin/win/amd64/wslhash b/bin/win/amd64/wslhash index aba1d48f8f12c2ef79088fae00bc52aea2dfe8ed..d7fcf1eebe261720e4be0b39de7572ecb3963e22 100644 GIT binary patch delta 13306 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 P#JHjOZ8rKkl^gyylR`!p delta 13326 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 WHqe{CZj>7_X*9JjkG)J4hW`VuR8TSi diff --git a/native/WslTools/wslhash.c b/native/WslTools/wslhash.c index 99246fc383bf..92613c5c3203 100644 --- a/native/WslTools/wslhash.c +++ b/native/WslTools/wslhash.c @@ -25,6 +25,8 @@ // skip the hash calculation step. // -f FILTER // filters the files using the given FILTER. May be specified multiple times. +// -s +// report files for stubbing e.g. files that exists, but were filtered out (explicitly or implicitly). // // Description: // Calculate hashes (unless `-n`) for all files in the given DIR. @@ -47,10 +49,11 @@ // Filters within each OPERATOR group are processed in the order of appearance in the command line. // // Output format: -// [FILE_PATH]:[HASH] +// [FILE_PATH]\0[HASH] // where HASH is little-endian 8 byte (64 bit) integer -// [FILE_PATH];[LINK_LEN][LINK] +// [LINK_PATH]\1[LINK_LEN][LINK] // where LINK_LEN is 4 byte (32 bit) signed int +// [STUB_PATH]\2 //#define WSLHASH_DEBUG 1 #ifdef WSLHASH_DEBUG @@ -69,6 +72,9 @@ #define FLT_NAME_LEN_MAX (1 + FLT_MATCHER_LEN_MAX + FLT_PATTERN_LEN_MAX + 2) // OPERATOR + MATCHER + PATTERN + delims #define FLT_SCAN_FMT "%c:%" STRINGIFY(FLT_MATCHER_LEN_MAX) "s:%" STRINGIFY(FLT_PATTERN_LEN_MAX) "s" +#define FILE_SEPARATOR 0 +#define LINK_SEPARATOR 1 +#define STUB_SEPARATOR 2 struct wslhash_filter_t { char name[FLT_NAME_LEN_MAX + 1]; // full filter name (OPERATOR:MATCHER:PATTERN). @@ -93,6 +99,7 @@ struct wslhash_options_t { size_t includes_len; int skip_hash; + int report_stubs; }; @@ -148,8 +155,8 @@ static int is_dir(const char *path) { return S_ISDIR(stat_info.st_mode); } -static const char * filename(const char* fpath) { - const char* last_slash = strrchr(fpath, '/'); +static const char *filename(const char *fpath) { + const char *last_slash = strrchr(fpath, '/'); return (last_slash != NULL) ? last_slash + 1 : fpath; } @@ -158,16 +165,20 @@ static int process_file(const char *fpath, const struct stat *sb, int tflag, __attribute__((unused)) struct FTW *ftwbuf) { DEBUG_PRINTF("Processing file: %s\n", fpath); if (tflag != FTW_F && tflag != FTW_SL) { - DEBUG_PRINTF("Skipping file: %s\n", fpath); + DEBUG_PRINTF("Skipping: %s\n", fpath); return 0; // Not a file } - if (tflag == FTW_F && !is_filename_ok(filename(fpath))) { - DEBUG_PRINTF("Excluding file: %s\n", fpath); - return 0; - } const char *fpath_relative = fpath + g_options.root_dir_len + 1; // remove first "/" if (tflag == FTW_F) { - printf("%s:", fpath_relative); + if (!is_filename_ok(filename(fpath))) { + DEBUG_PRINTF("Excluding file: %s\n", fpath); + if (g_options.report_stubs) { + printf("%s%c", fpath_relative, STUB_SEPARATOR); + } + return 0; + } + + printf("%s%c", fpath_relative, FILE_SEPARATOR); if (sb->st_size == 0 || g_options.skip_hash) { // No need to calculate hash for empty file fwrite(EMPTY, sizeof(EMPTY), 1, stdout); @@ -196,7 +207,7 @@ process_file(const char *fpath, const struct stat *sb, int tflag, __attribute__( } else { char real_path[PATH_MAX] = {0}; if (realpath(fpath, real_path) != NULL && is_dir(real_path)) { - printf("%s;", fpath_relative); + printf("%s%c", fpath_relative, LINK_SEPARATOR); const int32_t len = (int32_t) strlen(real_path); fwrite(&len, sizeof(int32_t), 1, stdout); fputs(real_path, stdout); @@ -283,8 +294,11 @@ static void parse_filter(const char *arg) { static void parse_args(int argc, char *argv[]) { int c; - while ((c = getopt(argc, argv, "nf:")) != -1) { + while ((c = getopt(argc, argv, "nsf:")) != -1) { switch (c) { + case 's': + g_options.report_stubs = 1; + break; case 'n': g_options.skip_hash = 1; break; diff --git a/platform/platform-tests/testSrc/com/intellij/execution/wsl/WslSyncTest.kt b/platform/platform-tests/testSrc/com/intellij/execution/wsl/WslSyncTest.kt index 58d9ce029e3f..046ed9ecb9f3 100644 --- a/platform/platform-tests/testSrc/com/intellij/execution/wsl/WslSyncTest.kt +++ b/platform/platform-tests/testSrc/com/intellij/execution/wsl/WslSyncTest.kt @@ -2,7 +2,6 @@ package com.intellij.execution.wsl import com.intellij.execution.wsl.sync.* -import com.intellij.execution.wsl.sync.WslHashFilters.Companion.EMPTY_FILTERS import com.intellij.execution.wsl.sync.WslHashFilters.WslHashFiltersBuilder import com.intellij.execution.wsl.sync.WslHashMatcher.Factory.basename import com.intellij.execution.wsl.sync.WslHashMatcher.Factory.extension @@ -64,7 +63,7 @@ class WslSyncTest(private val linToWin: Boolean) { for (source in sources) { storage.createSymLinks(mapOf(Pair(source, target))) } - return storage.getHashesAndLinks(false).second + return storage.calculateSyncData().links } } @@ -90,13 +89,13 @@ class WslSyncTest(private val linToWin: Boolean) { if (linToWin) { val dir = linuxDirRule.dir wslRule.wsl.executeOnWsl(1000, "mkdir", "${dir}/target") - storage = LinuxFileStorage(dir, wslRule.wsl, EMPTY_FILTERS) + storage = LinuxFileStorage(dir, wslRule.wsl) } else { val dir = winDirRule.newDirectoryPath() val targetDir = dir.resolve("target") targetDir.createDirectory() - storage = WindowsFileStorage(dir, wslRule.wsl, EMPTY_FILTERS) + storage = WindowsFileStorage(dir, wslRule.wsl) } val links = createAndGetLinks(storage, FilePathRelativeToDir("target"), *sources) @@ -112,8 +111,8 @@ class WslSyncTest(private val linToWin: Boolean) { val to: FileStorage<*, *> val winRoot = winDirRule.newDirectoryPath() val linRoot = linuxDirRule.dir - val win = WindowsFileStorage(winRoot, wslRule.wsl, EMPTY_FILTERS) - val lin = LinuxFileStorage(linRoot, wslRule.wsl, EMPTY_FILTERS) + val win = WindowsFileStorage(winRoot, wslRule.wsl) + val lin = LinuxFileStorage(linRoot, wslRule.wsl) if (!linToWin) { winRoot.resolve("target dir").createDirectory().resolve("file.txt").createFile() winRoot.resolve("dir_to_ignore").createDirectory() @@ -132,13 +131,13 @@ class WslSyncTest(private val linToWin: Boolean) { } to.createSymLinks(mapOf(Pair(FilePathRelativeToDir("dir_to_ignore/foo"), FilePathRelativeToDir("target dir")))) WslSync.syncWslFolders(linRoot, winRoot, wslRule.wsl, linToWinCopy = linToWin) - val links = to.getHashesAndLinks(false).second + val links = to.calculateSyncData().links for (source in sources) { Assert.assertEquals("target dir", links[source]?.asWindowsPath) } to.createSymLinks(mapOf(Pair(FilePathRelativeToDir("remove_me"), FilePathRelativeToDir("target dir")))) WslSync.syncWslFolders(linRoot, winRoot, wslRule.wsl, linToWinCopy = linToWin) - Assert.assertEquals(null, to.getHashesAndLinks(false).second[FilePathRelativeToDir("remove_me")]) + Assert.assertEquals(null, to.calculateSyncData().links[FilePathRelativeToDir("remove_me")]) for (source in sources) { Assert.assertEquals("target dir", links[source]?.asWindowsPath) } @@ -252,7 +251,8 @@ class WslSyncTest(private val linToWin: Boolean) { .exclude(*extensions("dll", "gz", "zip"), basename("debug"), fullname("idea.log")) - .build() + .build(), + false ) } @@ -266,6 +266,7 @@ class WslSyncTest(private val linToWin: Boolean) { basename("debug"), fullname("idea.log")) .build(), + false ) } @@ -281,12 +282,44 @@ class WslSyncTest(private val linToWin: Boolean) { .include(extension("tar.gz"), fullname("debug.in")) .build(), + false + ) + } + + @Test + fun syncWithExcludesAndStubs() { + doSyncAndAssertFilePresence( + setOf(), + setOf("файл.dll", "файл.gz", "файл.zip", "debug", "debug.out", "idea.log", + "файл.py", "файл.tar.gz", "файл.java", "файл-dll", "ddebug", "debug.in", "idea.log.bck"), + WslHashFiltersBuilder() + .exclude(*extensions("dll", "gz", "zip"), + basename("debug"), + fullname("idea.log")) + .build(), + true + ) + } + + @Test + fun syncWithIncludesAndStubs() { + doSyncAndAssertFilePresence( + setOf(), + setOf("файл.dll", "файл.gz", "файл.zip", "debug", "debug.out", "idea.log", + "файл.py", "файл.tar.gz", "файл.java", "файл-dll", "ddebug", "debug.in", "idea.log.bck"), + WslHashFiltersBuilder() + .include(*extensions("dll", "gz", "zip"), + basename("debug"), + fullname("idea.log")) + .build(), + true ) } private fun doSyncAndAssertFilePresence(fileNamesToIgnore: Set, fileNamesToSync: Set, - filters: WslHashFilters) { + filters: WslHashFilters, + useStubs: Boolean) { val windowsDir = winDirRule.newDirectoryPath() val allFileNames = fileNamesToSync + fileNamesToIgnore @@ -297,7 +330,7 @@ class WslSyncTest(private val linToWin: Boolean) { srcDir.resolve(fileName).writeText("hello $fileName") } - WslSync.syncWslFolders(linuxDirRule.dir, windowsDir, wslRule.wsl, linToWin, filters) + WslSync.syncWslFolders(linuxDirRule.dir, windowsDir, wslRule.wsl, linToWin, filters, useStubs) for (fileName in allFileNames) { val file = dstDir.resolve(fileName) @@ -306,6 +339,9 @@ class WslSyncTest(private val linToWin: Boolean) { } else { Assert.assertTrue("File ${file} must be copied", file.exists()) + if (useStubs && !filters.isFileNameOk(file.fileName.toString())) { + Assert.assertEquals("File ${file} must be stubbed", "", file.readText().trim()) + } } } Assert.assertEquals("Not all files synced", fileNamesToSync.size, dstDir.toFile().list()!!.size) diff --git a/platform/wsl-impl/src/com/intellij/execution/wsl/sync/FileStorage.kt b/platform/wsl-impl/src/com/intellij/execution/wsl/sync/FileStorage.kt index 747776375722..acaa2588ac91 100644 --- a/platform/wsl-impl/src/com/intellij/execution/wsl/sync/FileStorage.kt +++ b/platform/wsl-impl/src/com/intellij/execution/wsl/sync/FileStorage.kt @@ -2,6 +2,7 @@ package com.intellij.execution.wsl.sync import com.intellij.execution.wsl.AbstractWslDistribution +import com.intellij.execution.wsl.sync.WslHashFilters.Companion.EMPTY_FILTERS /** @@ -12,8 +13,7 @@ import com.intellij.execution.wsl.AbstractWslDistribution */ abstract class FileStorage( protected val dir: MyFileType, - protected val distro: AbstractWslDistribution, - protected val filters: WslHashFilters + protected val distro: AbstractWslDistribution ) { /** @@ -22,11 +22,14 @@ abstract class FileStorage( abstract fun createSymLinks(links: Map) /** - * List of [WslHashRecord] (file + hash) and map of `source->target` links. - * [skipHashCalculation] saves time by skipping hash (hence [WslHashRecord.hash] is 0). + * [filters] determine which files to process. + * [skipHash] saves time by skipping hash (hence each [WslHashRecord.hash] in [WslSyncData.hashes] is 0). * Such records can't be used for sync, but only to copy all files + * [useStubs] specifies whether files for stubbing will be reported. */ - abstract fun getHashesAndLinks(skipHashCalculation: Boolean): Pair, Map> + abstract fun calculateSyncData(filters: WslHashFilters = EMPTY_FILTERS, + skipHash: Boolean = false, + useStubs: Boolean = false): WslSyncData /** * is [dir] empty @@ -35,6 +38,7 @@ abstract class FileStorage( abstract fun removeFiles(filesToRemove: Collection) abstract fun createTempFile(): MyFileType abstract fun removeTempFile(file: MyFileType) + abstract fun createStubs(files: Collection) /** * tar [files] and copy to [destTar] diff --git a/platform/wsl-impl/src/com/intellij/execution/wsl/sync/LinuxFileStorage.kt b/platform/wsl-impl/src/com/intellij/execution/wsl/sync/LinuxFileStorage.kt index f26d3623ade2..bc0d936d2d57 100644 --- a/platform/wsl-impl/src/com/intellij/execution/wsl/sync/LinuxFileStorage.kt +++ b/platform/wsl-impl/src/com/intellij/execution/wsl/sync/LinuxFileStorage.kt @@ -6,8 +6,8 @@ import com.intellij.execution.processTools.getBareExecutionResult import com.intellij.execution.processTools.getResultStdoutStr import com.intellij.execution.wsl.* import com.intellij.openapi.diagnostic.Logger +import com.intellij.openapi.util.Ref import com.intellij.util.TimeoutUtil -import com.intellij.util.containers.ContainerUtil.append import com.intellij.util.io.delete import kotlinx.coroutines.runBlocking import java.io.InputStream @@ -19,14 +19,15 @@ import kotlin.io.path.writeText private val LOGGER = Logger.getInstance(LinuxFileStorage::class.java) -class LinuxFileStorage(dir: LinuxFilePath, distro: AbstractWslDistribution, filters: WslHashFilters) - : FileStorage(dir.trimEnd('/') + '/', distro, filters) { +class LinuxFileStorage(dir: LinuxFilePath, distro: AbstractWslDistribution) + : FileStorage(dir.trimEnd('/') + '/', distro) { // Linux side only works with UTF of 7-bit ASCII which is also supported by UTF and WSL doesn't support other charsets - private val CHARSET = Charsets.UTF_8 - private val FILE_SEPARATOR = CHARSET.encode(":").get() - private val LINK_SEPARATOR = CHARSET.encode(";").get() + + private val FILE_SEPARATOR: Byte = 0 + private val LINK_SEPARATOR: Byte = 1 + private val STUB_SEPARATOR: Byte = 2 override fun createSymLinks(links: Map) { val script = createTmpWinFile(distro) @@ -39,27 +40,38 @@ class LinuxFileStorage(dir: LinuxFilePath, distro: AbstractWslDistribution, filt script.first.delete() } - override fun getHashesAndLinks(skipHashCalculation: Boolean): Pair, Map> { - val hashes = ArrayList(AVG_NUM_FILES) - val links = HashMap(AVG_NUM_FILES) + override fun calculateSyncData(filters: WslHashFilters, skipHash: Boolean, useStubs: Boolean): WslSyncData { + val dataRef = Ref() val time = TimeoutUtil.measureExecutionTime { - val wslHashArgs = if (skipHashCalculation) append(filters.toArgs(), "-n", dir) else append(filters.toArgs(), dir) + val wslHashArgs = listOfNotNull(if (skipHash) "-n" else null, + if (useStubs) "-s" else null, + *filters.toArgs().toTypedArray(), + dir) val tool = distro.getTool("wslhash", *wslHashArgs.toTypedArray()) val process = tool.createProcess() process.inputStream.use { - val hashesAndLinks = getHashesInternal(it) - hashes += hashesAndLinks.first - links += hashesAndLinks.second + dataRef.set(calculateSyncDataInternal(it)) } runBlocking { process.getResultStdoutStr() } } LOGGER.info("Linux files calculated in $time") - return Pair(hashes, links) + return dataRef.get() } - override fun createTempFile(): String = distro.runCommand("mktemp", "-u").getOrThrow() + override fun createStubs(files: Collection) { + val script = createTmpWinFile(distro) + try { + val scriptContent = files.joinToString("\n") { "mkdir -p \"$(dirname ${it.escapedWithDir})\" && touch ${it.escapedWithDir}" } + script.first.writeText(scriptContent) + runBlocking { distro.createProcess("sh", script.second).getBareExecutionResult() } + } + finally { + script.first.delete() + } + } + override fun removeLinks(vararg linksToRemove: FilePathRelativeToDir) { this.removeFiles(linksToRemove.asList()) } @@ -113,17 +125,19 @@ class LinuxFileStorage(dir: LinuxFilePath, distro: AbstractWslDistribution, filt } /** - * Read `wslhash` stdout and return map of [file->hash] + * Parse output from `wslhash` and return [WslSyncData]. */ - private fun getHashesInternal(toolStdout: InputStream): Pair, Map> { + private fun calculateSyncDataInternal(toolStdout: InputStream): WslSyncData { val hashes = ArrayList(AVG_NUM_FILES) - val links = HashMap(AVG_NUM_FILES) + val links = mutableMapOf() + val stubs = mutableSetOf() val fileOutput = ByteBuffer.wrap(toolStdout.readAllBytes()).order(ByteOrder.LITTLE_ENDIAN) - // See wslhash.c: format is the following: [file_path]:[hash]. - // Hash is little-endian 8 byte (64 bit) integer - // or [file_path];[link_len][link] where link_len is 4 byte signed int - + // See wslhash.c. + // Output format is the following: + // [file_path]\0[hash], where hash is little-endian 8 byte (64 bit) integer + // [link_path]\1[link_len][link], where link_len is 4 byte signed int + // [stub_path]\2 var fileStarted = 0 val outputLimit = fileOutput.limit() while (fileOutput.position() < outputLimit) { @@ -149,9 +163,17 @@ class LinuxFileStorage(dir: LinuxFilePath, distro: AbstractWslDistribution, filt } fileStarted = prevPos + length } + STUB_SEPARATOR -> { + val prevPos = fileOutput.position() + // 1 = separator + val name = CHARSET.decode(fileOutput.limit(prevPos - 1).position(fileStarted)).toString() + fileOutput.limit(outputLimit).position(prevPos) + stubs += FilePathRelativeToDir(name) + fileStarted = prevPos + } } } - return Pair(hashes, links) + return WslSyncData(hashes, links, stubs) } private val FilePathRelativeToDir.escapedWithDir: String get() = escapePath(dir + asUnixPath) diff --git a/platform/wsl-impl/src/com/intellij/execution/wsl/sync/WindowsFileStorage.kt b/platform/wsl-impl/src/com/intellij/execution/wsl/sync/WindowsFileStorage.kt index 0533a0d3980d..289f119f4848 100644 --- a/platform/wsl-impl/src/com/intellij/execution/wsl/sync/WindowsFileStorage.kt +++ b/platform/wsl-impl/src/com/intellij/execution/wsl/sync/WindowsFileStorage.kt @@ -5,11 +5,11 @@ import com.intellij.execution.configurations.GeneralCommandLine import com.intellij.execution.util.ExecUtil import com.intellij.execution.wsl.AbstractWslDistribution import com.intellij.openapi.diagnostic.Logger -import com.intellij.openapi.diagnostic.thisLogger import com.intellij.openapi.util.io.FileSystemUtil import com.intellij.util.TimeoutUtil import com.intellij.util.io.* import com.intellij.util.system.CpuArch +import net.jpountz.xxhash.XXHash64 import net.jpountz.xxhash.XXHashFactory import java.io.IOException import java.nio.channels.FileChannel @@ -24,27 +24,55 @@ private val LOGGER = Logger.getInstance(WindowsFileStorage::class.java) private class MyFileVisitor(private val filters: WslHashFilters, private val rootDir: Path, - private val processFile: (relativeToDir: FilePathRelativeToDir, file: Path, attrs: BasicFileAttributes) -> Unit) : SimpleFileVisitor() { - private val dirLinksInt: MutableMap = mutableMapOf() - val dirLinks: Map get() = dirLinksInt + private val hashTool: XXHash64, + private val skipHash: Boolean, + private val useStubs: Boolean) : SimpleFileVisitor() { + + private val _hashes: MutableList = ArrayList(AVG_NUM_FILES) + private val _dirLinks: MutableMap = mutableMapOf() + private val _stubs: MutableSet = mutableSetOf() + + val hashes: List get() = _hashes + val dirLinks: Map get() = _dirLinks + val stubs: Set get() = _stubs + override fun postVisitDirectory(dir: Path?, exc: IOException?): FileVisitResult { return super.postVisitDirectory(dir, exc) } override fun visitFile(path: Path, attrs: BasicFileAttributes): FileVisitResult { if (!(attrs.isRegularFile)) return FileVisitResult.CONTINUE - if (!filters.isFileNameOk(path.fileName.toString())) { - return FileVisitResult.CONTINUE - } processFile(FilePathRelativeToDir(rootDir.relativize(path).joinToString("/").lowercase()), path, attrs) return FileVisitResult.CONTINUE } + fun processFile(relativeToDir: FilePathRelativeToDir, file: Path, attrs: BasicFileAttributes) { + if (!filters.isFileNameOk(file.fileName.toString())) { + if (useStubs) { + _stubs.add(relativeToDir) + } + } + else if (skipHash || attrs.size() == 0L) { // Empty file's hash is 0, see wslhash.c + _hashes.add(WslHashRecord(relativeToDir, 0)) + } + else { // Map file and read hash + FileChannel.open(file, StandardOpenOption.READ).use { + val buf = it.map(FileChannel.MapMode.READ_ONLY, 0, attrs.size()) + try { + _hashes.add(WslHashRecord(relativeToDir, hashTool.hash(buf, 0))) // Seed 0 is default, see wslhash.c + } + finally { + ByteBufferUtil.cleanBuffer(buf) // Unmap file: can't overwrite mapped file + } + } + } + } + override fun preVisitDirectory(dir: Path, attrs: BasicFileAttributes): FileVisitResult = if (FileSystemUtil.getAttributes( dir.toFile())?.isSymLink == true) { val target = FileSystemUtil.resolveSymLink(dir.toFile())?.let { rootDir.resolve(it) } if (target != null && target.isDirectory() && target.startsWith(rootDir)) { - dirLinksInt[FilePathRelativeToDir(rootDir.relativize(dir).toString())] = FilePathRelativeToDir(rootDir.relativize(target).toString()) + _dirLinks[FilePathRelativeToDir(rootDir.relativize(dir).toString())] = FilePathRelativeToDir(rootDir.relativize(target).toString()) } FileVisitResult.SKIP_SUBTREE } @@ -54,8 +82,7 @@ private class MyFileVisitor(private val filters: WslHashFilters, } class WindowsFileStorage(dir: Path, - distro: AbstractWslDistribution, - filters: WslHashFilters) : FileStorage(dir, distro, filters) { + distro: AbstractWslDistribution) : FileStorage(dir, distro) { private fun runCommand(vararg commands: String) { val cmd = arrayOf("cmd", "/c", *commands) ExecUtil.execAndGetOutput(GeneralCommandLine(*cmd)).let { @@ -76,36 +103,21 @@ class WindowsFileStorage(dir: Path, } } - override fun getHashesAndLinks(skipHashCalculation: Boolean): Pair, Map> { - val result = ArrayList(AVG_NUM_FILES) + override fun calculateSyncData(filters: WslHashFilters, skipHash: Boolean, useStubs: Boolean): WslSyncData { val arch = System.getProperty("os.arch") val useNativeHash = CpuArch.CURRENT == CpuArch.X86_64 - thisLogger().info("Arch $arch, using native hash: $useNativeHash") + LOGGER.info("Arch $arch, using native hash: $useNativeHash") val hashTool = if (useNativeHash) XXHashFactory.nativeInstance().hash64() else XXHashFactory.safeInstance().hash64() // Native hash can access direct (mapped) buffer a little-bit faster - val visitor = MyFileVisitor(filters, dir) { relativeToDir: FilePathRelativeToDir, file: Path, attrs: BasicFileAttributes -> - if (skipHashCalculation || attrs.size() == 0L) { // Empty file's hash is 0, see wslhash.c - result.add(WslHashRecord(relativeToDir, 0)) - } - else { // Map file and read hash - FileChannel.open(file, StandardOpenOption.READ).use { - val buf = it.map(FileChannel.MapMode.READ_ONLY, 0, attrs.size()) - try { - result.add(WslHashRecord(relativeToDir, hashTool.hash(buf, 0))) // Seed 0 is default, see wslhash.c - } - finally { - ByteBufferUtil.cleanBuffer(buf) // Unmap file: can't overwrite mapped file - } - } - } - } + val visitor = MyFileVisitor(filters, dir, hashTool, skipHash, useStubs) val time = TimeoutUtil.measureExecutionTime { Files.walkFileTree(dir, visitor) } LOGGER.info("Windows files calculated in $time") - return Pair(result, visitor.dirLinks) + return WslSyncData(visitor.hashes, visitor.dirLinks, visitor.stubs) } override fun isEmpty(): Boolean = dir.notExists() || dir.listDirectoryEntries().isEmpty() + override fun removeFiles(filesToRemove: Collection) { if (filesToRemove.isEmpty()) return for (file in filesToRemove) { @@ -117,6 +129,16 @@ class WindowsFileStorage(dir: Path, } override fun createTempFile(): Path = createTmpWinFile(distro).first + + override fun createStubs(files: Collection) { + for (file in files) { + val filePath = dir.resolve(file.asWindowsPath) + if (!filePath.exists()) { + filePath.createFile() + } + } + } + override fun removeLinks(vararg linksToRemove: FilePathRelativeToDir) { for (link in linksToRemove) { runCommand("rmdir", dir.resolve(link.asWindowsPath).toString()) diff --git a/platform/wsl-impl/src/com/intellij/execution/wsl/sync/WslSync.kt b/platform/wsl-impl/src/com/intellij/execution/wsl/sync/WslSync.kt index f53a5df0f1dd..7f161503850f 100644 --- a/platform/wsl-impl/src/com/intellij/execution/wsl/sync/WslSync.kt +++ b/platform/wsl-impl/src/com/intellij/execution/wsl/sync/WslSync.kt @@ -25,36 +25,38 @@ private const val MIN_CHUNK_SIZE = 1000 private val LOGGER = Logger.getInstance(WslSync::class.java) class WslSync private constructor(private val source: FileStorage, - private val dest: FileStorage) { - + private val dest: FileStorage, + private val filters: WslHashFilters, + private val useStubs: Boolean) { companion object { /** - * Makes [windowsDir] reflect [linuxDir] (or vice versa depending on [linToWinCopy]) on [distribution] much like rsync. - * Redundant files deleted, new/changed files copied. - * Set [onlyExtensions] if you only care about certain extensions. - * Direction depends on [linToWinCopy] + * Synchronizes the given [windowsDir] and [linuxDir] (inside [distro]). + * [linToWinCopy] determines the sync direction. + * [filters] allow you to specify which files to include/exclude. + * [useStubs] dictates whether empty stubs should be created for filtered out files. */ @JvmOverloads fun syncWslFolders(linuxDir: String, windowsDir: Path, - distribution: AbstractWslDistribution, + distro: AbstractWslDistribution, linToWinCopy: Boolean = true, - filters: WslHashFilters = EMPTY_FILTERS) { + filters: WslHashFilters = EMPTY_FILTERS, + useStubs: Boolean = false) { LOGGER.info("Sync " + if (linToWinCopy) "$linuxDir -> $windowsDir" else "$windowsDir -> $linuxDir") - val win = WindowsFileStorage(windowsDir, distribution, filters) - val lin = LinuxFileStorage(linuxDir, distribution, filters) + val win = WindowsFileStorage(windowsDir, distro) + val lin = LinuxFileStorage(linuxDir, distro) if (linToWinCopy) { - WslSync(lin, win) + WslSync(lin, win, filters, useStubs) } else { - WslSync(win, lin) + WslSync(win, lin, filters, useStubs) val execFile = windowsDir.resolve("exec.txt") if (execFile.exists()) { // TODO: Support non top level files - for(fileToMarkExec in execFile.readText().split(Regex("\\s+")).map { it.trim() }) { - lin.markExec(fileToMarkExec) + for (fileToMarkExec in execFile.readText().split(Regex("\\s+")).map { it.trim() }) { + lin.markExec(fileToMarkExec) } } } @@ -64,37 +66,48 @@ class WslSync private constructor(private val source: File init { if (dest.isEmpty()) { //Shortcut: no need to sync anything, just copy everything LOGGER.info("Destination folder is empty, will copy all files") - val hashesAndLinks = source.getHashesAndLinks(true) - copyFilesInParallel(hashesAndLinks.first.map { it.file }) - copyAllLinks(hashesAndLinks.second) + val syncData = source.calculateSyncData(filters, true, useStubs) + copyFilesInParallel(syncData.hashes.map { it.file }) + syncLinks(syncData.links) + syncStubs(syncData.stubs) } else { syncFoldersInternal() } } - private fun copyAllLinks(toCreate: Map, - current: Map = emptyMap()) { - val linksToCreate = toCreate.filterNot { current[it.key] == it.value } - val linksToRemove = current.filterNot { toCreate[it.key] == it.value }.keys + private fun syncLinks(sourceLinks: Map, + destStubs: Map = emptyMap()) { + val linksToCreate = sourceLinks.filterNot { destStubs[it.key] == it.value } + val linksToRemove = destStubs.filterNot { sourceLinks[it.key] == it.value }.keys LOGGER.info("Will create ${linksToCreate.size} links and remove ${linksToRemove.size}") dest.removeLinks(*linksToRemove.toTypedArray()) dest.createSymLinks(linksToCreate) } - private fun syncFoldersInternal() { - val sourceHashesFuture = supplyAsync({ - source.getHashesAndLinks(false) - }, ProcessIOExecutorService.INSTANCE) - val destHashesFuture = supplyAsync({ - dest.getHashesAndLinks(false) - }, ProcessIOExecutorService.INSTANCE) + private fun syncStubs(sourceStubs: Set, + destStubs: Set = emptySet()) { + val stubsToCreate = sourceStubs.minus(destStubs) + val stubsToRemove = destStubs.minus(sourceStubs) - val sourceHashAndLinks = sourceHashesFuture.get() - val sourceHashes: MutableMap = sourceHashAndLinks.first.associateBy { it.fileLowerCase }.toMutableMap() - val destHashAndLinks = destHashesFuture.get() - val destHashes: List = destHashAndLinks.first + LOGGER.info("Will create ${stubsToCreate.size} links and remove ${stubsToRemove.size}") + dest.createStubs(stubsToCreate) + dest.removeFiles(stubsToRemove) + } + + private fun syncFoldersInternal() { + val sourceSyncDataFuture = supplyAsync({ + source.calculateSyncData(filters, false, useStubs) + }, ProcessIOExecutorService.INSTANCE) + val destSyncDataFuture = supplyAsync({ + dest.calculateSyncData(filters, false, useStubs) + }, ProcessIOExecutorService.INSTANCE) + + val sourceSyncData = sourceSyncDataFuture.get() + val sourceHashes = sourceSyncData.hashes.associateBy { it.fileLowerCase }.toMutableMap() + val destSyncData = destSyncDataFuture.get() + val destHashes = destSyncData.hashes val destFilesToRemove = ArrayList(AVG_NUM_FILES) for (destRecord in destHashes) { @@ -113,7 +126,8 @@ class WslSync private constructor(private val source: File copyFilesInParallel(sourceHashes.values.map { it.file }) dest.removeFiles(destFilesToRemove) - copyAllLinks(sourceHashAndLinks.second, destHashAndLinks.second) + syncLinks(sourceSyncData.links, destSyncData.links) + syncStubs(sourceSyncData.stubs, destSyncData.stubs) } /** diff --git a/platform/wsl-impl/src/com/intellij/execution/wsl/sync/WslSyncData.kt b/platform/wsl-impl/src/com/intellij/execution/wsl/sync/WslSyncData.kt new file mode 100644 index 000000000000..c3b01e159313 --- /dev/null +++ b/platform/wsl-impl/src/com/intellij/execution/wsl/sync/WslSyncData.kt @@ -0,0 +1,11 @@ +// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package com.intellij.execution.wsl.sync + +/** + * [hashes] is a list of [WslHashRecord] (file + hash) + * [links] is a map of `source->target` links. + * [stubs] is a list of target files to stub. + */ +data class WslSyncData(val hashes: List = listOf(), + val links: Map = mapOf(), + val stubs: Set = setOf())