All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.lucene.tests.mockfile.WindowsFS Maven / Gradle / Ivy

There is a newer version: 7.6.0
Show newest version
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.lucene.tests.mockfile;

import java.io.IOException;
import java.nio.file.CopyOption;
import java.nio.file.FileSystem;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.attribute.BasicFileAttributeView;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.HashMap;
import java.util.Map;

/**
 * FileSystem that (imperfectly) acts like windows.
 *
 * 

Currently this filesystem only prevents deletion of open files. */ public class WindowsFS extends HandleTrackingFS { // This map also supports fileKey -> Path -> counts // which is important to effectively support renames etc. // in the rename case we have to transfer ownership but need to make sure we only transfer // ownership for // the path we rename ie. hardlinks will still resolve to the same key final Map> openFiles = new HashMap<>(); // TODO: try to make this as realistic as possible... it depends e.g. how you // open files, if you map them, etc, if you can delete them (Uwe knows the rules) // TODO: add case-insensitivity /** * Create a new instance, wrapping the delegate filesystem to act like Windows. * * @param delegate delegate filesystem to wrap. */ public WindowsFS(FileSystem delegate) { super("windows://", delegate); } /** Returns file "key" (e.g. inode) for the specified path */ private Object getKey(Path existing) throws IOException { BasicFileAttributeView view = Files.getFileAttributeView(existing, BasicFileAttributeView.class); BasicFileAttributes attributes = view.readAttributes(); return attributes.fileKey(); } @Override protected void onOpen(Path path, Object stream) throws IOException { synchronized (openFiles) { final Object key = getKey(path); // we have to read the key under the lock otherwise me might leak the openFile handle // if we concurrently delete or move this file. Map pathMap = openFiles.computeIfAbsent(key, k -> new HashMap<>()); pathMap.put(path, pathMap.computeIfAbsent(path, p -> 0).intValue() + 1); } } @Override protected void onClose(Path path, Object stream) throws IOException { Object key = getKey(path); // here we can read this outside of the lock synchronized (openFiles) { Map pathMap = openFiles.get(key); assert pathMap != null; assert pathMap.containsKey(path); Integer v = pathMap.get(path); if (v != null) { if (v.intValue() == 1) { pathMap.remove(path); } else { v = Integer.valueOf(v.intValue() - 1); pathMap.put(path, v); } } if (pathMap.isEmpty()) { openFiles.remove(key); } } } private Object getKeyOrNull(Path path) { try { return getKey(path); } catch ( @SuppressWarnings("unused") Exception ignore) { // we don't care if the file doesn't exist } return null; } /** * Checks that it's ok to delete {@code Path}. If the file is still open, it throws * IOException("access denied"). */ private void checkDeleteAccess(Path path) throws IOException { Object key = getKeyOrNull(path); if (key != null) { synchronized (openFiles) { if (openFiles.containsKey(key)) { throw new IOException("access denied: " + path); } } } } @Override public void delete(Path path) throws IOException { synchronized (openFiles) { checkDeleteAccess(path); super.delete(path); } } @Override public void move(Path source, Path target, CopyOption... options) throws IOException { synchronized (openFiles) { checkDeleteAccess(source); Object key = getKeyOrNull(target); super.move(source, target, options); if (key != null) { Object newKey = getKey(target); if (newKey.equals(key) == false) { // we need to transfer ownership here if we have open files on this file since the // getKey() method will // return a different i-node next time we call it with the target path and our onClose // method will // trip an assert Map map = openFiles.get(key); if (map != null) { Integer v = map.remove(target); if (v != null) { Map pathIntegerMap = openFiles.computeIfAbsent(newKey, k -> new HashMap<>()); Integer existingValue = pathIntegerMap.getOrDefault(target, 0); pathIntegerMap.put(target, existingValue + v); } if (map.isEmpty()) { openFiles.remove(key); } } } } } } @Override public boolean deleteIfExists(Path path) throws IOException { synchronized (openFiles) { checkDeleteAccess(path); return super.deleteIfExists(path); } } @Override public FilterPath wrapPath(Path path) { return new WindowsPath(path, fileSystem); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy