Skip to content

Commit 67fdb91

Browse files
uschindlermikemccand
authored andcommitted
Fix WindowsFS to support Windows (#14627)
Windows does not support file keys (the attribute returns null). Therefor WindowsFS cannot be used on Windows. Due to Windows 11 no longer preventing depleting of open files, we need to be able to use WindowsFS on Windows (irony), fix this to fallback to path as key. It should be good enough like an inode. Renaming/moving files is already handles by WindowsFS when it detects a change in key after rename/move. Closes #11920
1 parent 4be27fe commit 67fdb91

File tree

8 files changed

+33
-56
lines changed

8 files changed

+33
-56
lines changed

lucene/CHANGES.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,10 @@ Other
5858

5959
* GITHUB#14516: Move sloppySin into SloppyMath from GeoUtils (Ankit Jain)
6060

61+
* GITHUB#14627, GITHUB#11920: Fix mock filesystem WindowsFS to also work on Windows.
62+
This is required to make tests pass on Windows 11 which no longer has
63+
all limitations of previous Windows versions. (Uwe Schindler)
64+
6165

6266
======================= Lucene 10.2.1 =======================
6367

lucene/core/src/test/org/apache/lucene/index/TestIndexWriter.java

Lines changed: 3 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,6 @@
8989
import org.apache.lucene.store.IndexInput;
9090
import org.apache.lucene.store.IndexOutput;
9191
import org.apache.lucene.store.LockObtainFailedException;
92-
import org.apache.lucene.store.MMapDirectory;
9392
import org.apache.lucene.store.NIOFSDirectory;
9493
import org.apache.lucene.store.NoLockFactory;
9594
import org.apache.lucene.store.SimpleFSLockFactory;
@@ -108,7 +107,6 @@
108107
import org.apache.lucene.tests.util.TestUtil;
109108
import org.apache.lucene.util.Bits;
110109
import org.apache.lucene.util.BytesRef;
111-
import org.apache.lucene.util.Constants;
112110
import org.apache.lucene.util.IOSupplier;
113111
import org.apache.lucene.util.IOUtils;
114112
import org.apache.lucene.util.InfoStream;
@@ -1243,15 +1241,9 @@ public void testDeleteUnusedFiles() throws Exception {
12431241
WindowsFS provider = new WindowsFS(path.getFileSystem());
12441242
Path indexPath = provider.wrapPath(path);
12451243

1246-
// NOTE: on Unix, we cannot use MMapDir, because WindowsFS doesn't see/think it keeps file
1247-
// handles open. Yet, on Windows, we MUST use MMapDir because the Windows OS will in fact
1248-
// prevent file deletion for us, and fails otherwise:
1249-
FSDirectory dir;
1250-
if (Constants.WINDOWS) {
1251-
dir = new MMapDirectory(indexPath);
1252-
} else {
1253-
dir = new NIOFSDirectory(indexPath);
1254-
}
1244+
// NOTE: We cannot use MMapDir, because WindowsFS doesn't see/think it keeps file
1245+
// handles open.
1246+
FSDirectory dir = new NIOFSDirectory(indexPath);
12551247

12561248
MergePolicy mergePolicy = newLogMergePolicy(true);
12571249

@@ -2879,9 +2871,6 @@ public void testCommitImmediatelyAfterNRTReopen() throws Exception {
28792871
}
28802872

28812873
public void testPendingDeleteDVGeneration() throws IOException {
2882-
// irony: currently we don't emulate windows well enough to work on windows!
2883-
assumeFalse("windows is not supported", Constants.WINDOWS);
2884-
28852874
Path path = createTempDir();
28862875

28872876
// Use WindowsFS to prevent open files from being deleted:
@@ -2947,9 +2936,6 @@ public void testPendingDeleteDVGeneration() throws IOException {
29472936
}
29482937

29492938
public void testPendingDeletionsRollbackWithReader() throws IOException {
2950-
// irony: currently we don't emulate Windows well enough to work on Windows!
2951-
assumeFalse("Windows is not supported", Constants.WINDOWS);
2952-
29532939
Path path = createTempDir();
29542940

29552941
// Use WindowsFS to prevent open files from being deleted:
@@ -2986,9 +2972,6 @@ public void testPendingDeletionsRollbackWithReader() throws IOException {
29862972
}
29872973

29882974
public void testWithPendingDeletions() throws Exception {
2989-
// irony: currently we don't emulate Windows well enough to work on Windows!
2990-
assumeFalse("Windows is not supported", Constants.WINDOWS);
2991-
29922975
Path path = createTempDir();
29932976

29942977
// Use WindowsFS to prevent open files from being deleted:
@@ -3038,8 +3021,6 @@ dir, new IndexWriterConfig(new MockAnalyzer(random())).setIndexCommit(indexCommi
30383021

30393022
public void testPendingDeletesAlreadyWrittenFiles() throws IOException {
30403023
Path path = createTempDir();
3041-
// irony: currently we don't emulate Windows well enough to work on Windows!
3042-
assumeFalse("Windows is not supported", Constants.WINDOWS);
30433024

30443025
// Use WindowsFS to prevent open files from being deleted:
30453026
WindowsFS provider = new WindowsFS(path.getFileSystem());

lucene/core/src/test/org/apache/lucene/store/TestFileSwitchDirectory.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@
3636
import org.apache.lucene.tests.store.BaseDirectoryTestCase;
3737
import org.apache.lucene.tests.store.MockDirectoryWrapper;
3838
import org.apache.lucene.tests.util.TestUtil;
39-
import org.apache.lucene.util.Constants;
4039

4140
public class TestFileSwitchDirectory extends BaseDirectoryTestCase {
4241

@@ -172,7 +171,6 @@ protected Directory getDirectory(Path path) throws IOException {
172171
public void testDeleteAndList() throws IOException {
173172
// relies on windows semantics
174173
Path path = createTempDir();
175-
assumeFalse("Irony we seem to not emulate windows well enough", Constants.WINDOWS);
176174
WindowsFS provider = new WindowsFS(path.getFileSystem());
177175
Path indexPath = provider.wrapPath(path);
178176
try (final FileSwitchDirectory dir =

lucene/misc/src/test/org/apache/lucene/misc/store/TestHardLinkCopyDirectoryWrapper.java

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@
3333
import org.apache.lucene.store.NIOFSDirectory;
3434
import org.apache.lucene.tests.mockfile.WindowsFS;
3535
import org.apache.lucene.tests.store.BaseDirectoryTestCase;
36-
import org.apache.lucene.util.Constants;
3736
import org.apache.lucene.util.IOUtils;
3837

3938
// See: https://issues.apache.org/jira/browse/SOLR-12028 Tests cannot remove files on Windows
@@ -104,8 +103,6 @@ public void testCopyHardLinks() throws IOException {
104103
}
105104

106105
public void testRenameWithHardLink() throws Exception {
107-
// irony: currently we don't emulate windows well enough to work on windows!
108-
assumeFalse("windows is not supported", Constants.WINDOWS);
109106
Path path = createTempDir();
110107
WindowsFS provider = new WindowsFS(path.getFileSystem());
111108
Directory dir1 = new NIOFSDirectory(provider.wrapPath(path));

lucene/test-framework/src/java/org/apache/lucene/tests/mockfile/WindowsFS.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,10 @@
2121
import java.nio.file.FileSystem;
2222
import java.nio.file.Files;
2323
import java.nio.file.Path;
24-
import java.nio.file.attribute.BasicFileAttributeView;
2524
import java.nio.file.attribute.BasicFileAttributes;
2625
import java.util.HashMap;
2726
import java.util.Map;
27+
import java.util.Optional;
2828

2929
/**
3030
* FileSystem that (imperfectly) acts like windows.
@@ -55,10 +55,10 @@ public WindowsFS(FileSystem delegate) {
5555

5656
/** Returns file "key" (e.g. inode) for the specified path */
5757
private Object getKey(Path existing) throws IOException {
58-
BasicFileAttributeView view =
59-
Files.getFileAttributeView(existing, BasicFileAttributeView.class);
60-
BasicFileAttributes attributes = view.readAttributes();
61-
return attributes.fileKey();
58+
// the key may be null, e.g. on real Windows!
59+
// in that case we fallback to the file path as key.
60+
return Optional.ofNullable(Files.readAttributes(existing, BasicFileAttributes.class).fileKey())
61+
.orElse(existing);
6262
}
6363

6464
@Override

lucene/test-framework/src/java/org/apache/lucene/tests/mockfile/WindowsPath.java

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,20 +18,17 @@
1818

1919
import java.nio.file.InvalidPathException;
2020
import java.nio.file.Path;
21-
import java.util.Arrays;
22-
import java.util.HashSet;
21+
import java.util.Set;
2322

2423
class WindowsPath extends FilterPath {
2524

26-
static final HashSet<Character> RESERVED_CHARACTERS =
27-
new HashSet<>(Arrays.asList('<', '>', ':', '\"', '\\', '|', '?', '*'));
25+
static final Set<Character> RESERVED_CHARACTERS =
26+
Set.of('<', '>', ':', '\"', '\\', '|', '?', '*');
2827

29-
static final HashSet<String> RESERVED_NAMES =
30-
new HashSet<>(
31-
Arrays.asList(
32-
"CON", "PRN", "AUX", "NUL", "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7",
33-
"COM8", "COM9", "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8",
34-
"LPT9"));
28+
static final Set<String> RESERVED_NAMES =
29+
Set.of(
30+
"CON", "PRN", "AUX", "NUL", "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7",
31+
"COM8", "COM9", "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9");
3532

3633
WindowsPath(Path path, FilterFileSystem fileSystem) {
3734
super(path, fileSystem);

lucene/test-framework/src/java/org/apache/lucene/tests/util/TestRuleTemporaryFilesCleanup.java

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,6 @@
4343
import org.apache.lucene.tests.util.LuceneTestCase.SuppressFileSystems;
4444
import org.apache.lucene.tests.util.LuceneTestCase.SuppressFsync;
4545
import org.apache.lucene.tests.util.LuceneTestCase.SuppressTempFileChecks;
46-
import org.apache.lucene.util.Constants;
4746
import org.apache.lucene.util.IOUtils;
4847

4948
/**
@@ -157,11 +156,8 @@ private FileSystem initializeFileSystem() {
157156
fs = new HandleLimitFS(fs, limit).getFileSystem(null);
158157
}
159158
// windows is currently slow
160-
if (random.nextInt(10) == 0) {
161-
// don't try to emulate windows on windows: they don't get along
162-
if (!Constants.WINDOWS && allowed(avoid, WindowsFS.class)) {
163-
fs = new WindowsFS(fs).getFileSystem(null);
164-
}
159+
if (random.nextInt(10) == 0 && allowed(avoid, WindowsFS.class)) {
160+
fs = new WindowsFS(fs).getFileSystem(null);
165161
}
166162
if (allowed(avoid, ExtrasFS.class)) {
167163
fs = new ExtrasFS(fs, random.nextInt(4) == 0, random.nextBoolean()).getFileSystem(null);

lucene/test-framework/src/test/org/apache/lucene/tests/mockfile/TestWindowsFS.java

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import java.util.Random;
3030
import java.util.concurrent.CyclicBarrier;
3131
import java.util.concurrent.atomic.AtomicBoolean;
32+
import java.util.function.Predicate;
3233
import org.apache.lucene.util.Constants;
3334

3435
/** Basic tests for WindowsFS */
@@ -37,11 +38,6 @@ public class TestWindowsFS extends MockFileSystemTestCase {
3738
@Override
3839
public void setUp() throws Exception {
3940
super.setUp();
40-
// irony: currently we don't emulate windows well enough to work on windows!
41-
// TODO: Can we fork this class and create a new class with only those tests that can run on
42-
// Windows and then check if
43-
// the Lucene WindowsFS error is same as the one OG Windows throws
44-
assumeFalse("windows is not supported", Constants.WINDOWS);
4541
}
4642

4743
@Override
@@ -188,8 +184,12 @@ public void testMove() throws IOException {
188184
}
189185

190186
public void testFileName() {
191-
Character[] reservedCharacters = WindowsPath.RESERVED_CHARACTERS.toArray(new Character[0]);
192-
String[] reservedNames = WindowsPath.RESERVED_NAMES.toArray(new String[0]);
187+
Character[] reservedCharacters =
188+
WindowsPath.RESERVED_CHARACTERS.stream()
189+
.filter(Predicate.not(Character.valueOf('\\')::equals))
190+
.sorted()
191+
.toArray(Character[]::new);
192+
String[] reservedNames = WindowsPath.RESERVED_NAMES.stream().sorted().toArray(String[]::new);
193193
String fileName;
194194
Random r = random();
195195
Path dir = wrap(createTempDir());
@@ -211,7 +211,11 @@ public void testFileName() {
211211
expectThrows(InvalidPathException.class, () -> dir.resolve("foo:bar:tar"));
212212
expectThrows(InvalidPathException.class, () -> dir.resolve("foo?bar"));
213213
expectThrows(InvalidPathException.class, () -> dir.resolve("foo<bar"));
214-
expectThrows(InvalidPathException.class, () -> dir.resolve("foo\\bar"));
214+
if (!Constants.WINDOWS) {
215+
// we need to exclude that test on Windows, because the backslash is resolved before our code
216+
// checks the filename:
217+
expectThrows(InvalidPathException.class, () -> dir.resolve("foo\\bar"));
218+
}
215219
expectThrows(InvalidPathException.class, () -> dir.resolve("foo*bar|tar"));
216220
expectThrows(InvalidPathException.class, () -> dir.resolve("foo|bar?tar"));
217221
}

0 commit comments

Comments
 (0)