Подобно тому, как стоит избегать писать свои реализации всякой криптографии и делать что-то со временем, есть еще одна область в которую лучше не лезть: работа с символическими ссылками и файловой системой вообще.

Правильно реализовать работу с символическими ссылками — довольно нетривиальная задача. Хорошую презентацию по этому поводу можно посмотреть тут. Если вкратце, то ссылки могут меняться между системными вызовами, что открывает массу возможностей для повышения привилегий из-за того, что их проверка и действие над ссылкой происходит неатомарно. Даже в системных вызовах Linux не смогли поправить это с первого и даже со второго раза.

А еще символические ссылки ломают иерархию (и связанные с ней проверки) — например, путь с .. уже нельзя нормализовать без запроса к файловой системе. А еще ссылка может ссылаться на другую ссылку, и эти случаи в два раза веселее обрабатывать.

В добавок к этому, я недавно нашел интересное поведение в java. Если создать папку, содержащую юникодные символы, то ее имя по-разному будет нормализовано в зависимости от того, был ли использован метод Files.createDirectory или File.mkdir. Это приводит к тому, что оригинальный путь не совпадает с “реальным” путем к файлу, который только что был создан по этому пути:

package test;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.concurrent.Callable;
import java.util.function.Consumer;
import java.util.function.Function;


public class App {

    @FunctionalInterface
    public interface Action {
        void apply(Path path) throws IOException;
    }

    public static void main(String[] args) throws IOException {
        check("createDirectory", Files::createDirectory);
        check("mkdir", x -> x.toFile().mkdir());
    }

    private static void check(String hint, Action createDir) throws IOException {
        String name = "teŝt files";
        Path root = Path.of(name).toAbsolutePath();
        Path file = root.resolve("file1");

        Files.deleteIfExists(file);
        Files.deleteIfExists(root);
        createDir.apply(root);
        Files.createFile(file);

        Path alternative = file.toRealPath();

        System.out.printf("%s - Equals: %s, isSameFile: %s\n", hint, file.equals(alternative), Files.isSameFile(file, alternative));
    }
}

Вывод:

createDirectory - Equals: true, isSameFile: true
mkdir - Equals: false, isSameFile: true