Как понять, почему FIles.isWritable() возвращает false в Windows

В моем приложении, которое позволяет пользователям время от времени изменять свои музыкальные файлы, у меня возникает проблема, когда приложение не имеет разрешений на изменение файла, но пользователь убежден, что дал ему полные разрешения.

Я обнаружил улучшения Java 7 и написал этот метод для вывода разрешений

public static String displayPermissions(Path path)
{
    StringBuilder sb = new StringBuilder();
    sb.append("File "+path + " permissions\n");
    try
    {
        {
            AclFileAttributeView view = Files.getFileAttributeView(path, AclFileAttributeView.class);
            if (view != null)
            {
                for (AclEntry acl : view.getAcl())
                {
                    sb.append(acl+"\n");
                }
            }
        }

        {
            PosixFileAttributeView view = Files.getFileAttributeView(path, PosixFileAttributeView.class);
            if (view != null)
            {
                PosixFileAttributes pfa = view.readAttributes();
                sb.append(":owner:"+pfa.owner().getName()+":group:"+pfa.group().getName()+":"+PosixFilePermissions.toString(pfa.permissions())+"\n");
            }
        }
    }
    catch(IOException ioe)
    {
        logger.severe("Unable to read permissions for:"+path.toString());
    }
    return sb.toString();
}

но для систем Windows все еще может быть довольно сложно/невозможно понять, почему у них нет разрешений

WARNING: File testdata\test157.dsf permissions
NT AUTHORITY\SYSTEM:READ_DATA/WRITE_DATA/APPEND_DATA/READ_NAMED_ATTRS/WRITE_NAMED_ATTRS/EXECUTE/DELETE_CHILD/READ_ATTRIBUTES/WRITE_ATTRIBUTES/DELETE/READ_ACL/WRITE_ACL/WRITE_OWNER/SYNCHRONIZE:DENY
BUILTIN\Administrators:READ_DATA/WRITE_DATA/APPEND_DATA/READ_NAMED_ATTRS/WRITE_NAMED_ATTRS/EXECUTE/DELETE_CHILD/READ_ATTRIBUTES/WRITE_ATTRIBUTES/DELETE/READ_ACL/WRITE_ACL/WRITE_OWNER/SYNCHRONIZE:DENY
BUILTIN\Administrators:READ_DATA/WRITE_DATA/APPEND_DATA/READ_NAMED_ATTRS/WRITE_NAMED_ATTRS/EXECUTE/DELETE_CHILD/READ_ATTRIBUTES/WRITE_ATTRIBUTES/DELETE/READ_ACL/WRITE_ACL/WRITE_OWNER/SYNCHRONIZE:ALLOW
NT AUTHORITY\SYSTEM:READ_DATA/WRITE_DATA/APPEND_DATA/READ_NAMED_ATTRS/WRITE_NAMED_ATTRS/EXECUTE/DELETE_CHILD/READ_ATTRIBUTES/WRITE_ATTRIBUTES/DELETE/READ_ACL/WRITE_ACL/WRITE_OWNER/SYNCHRONIZE:ALLOW
BUILTIN\Users:READ_DATA/READ_NAMED_ATTRS/EXECUTE/READ_ATTRIBUTES/READ_ACL/SYNCHRONIZE:ALLOW
NT AUTHORITY\Authenticated Users:READ_DATA/WRITE_DATA/APPEND_DATA/READ_NAMED_ATTRS/WRITE_NAMED_ATTRS/EXECUTE/READ_ATTRIBUTES/WRITE_ATTRIBUTES/DELETE/READ_ACL/SYNCHRONIZE:ALLOW

, любые предложения о том, как я могу программно определить причину сбоя isWritable() (или isReadable()).


person Paul Taylor    schedule 29.01.2016    source источник
comment
Что выглядит немного странно. BUILTIN\Administrators имеют DENY и ALLOW одинаковые разрешения.   -  person SubOptimal    schedule 29.01.2016
comment
Ну, это всего лишь пример, но я вручную установил разрешения «Запретить», но не смог удалить существующие разрешения «Разрешить», честно говоря, я нахожу разрешения Windows непонятными.   -  person Paul Taylor    schedule 29.01.2016


Ответы (2)


Я считаю, что путаница возникает из-за того, что эффективное разрешение зависит от разрешений каталога и прав доступа к файлам.

Если у пользователя есть, например. разрешение Write для файла, но не разрешение Modify для содержащего каталога, у него фактически нет разрешения на запись в этот файл. См. полную матрицу разрешений для файлов и папок.

Чтобы выполнить проверку в Java, вы можете использовать FileSystemProvider.checkAccess для проверки разрешения

Предположим, что следующие файлы и разрешения (все разрешения за пределами области удалены, чтобы сделать их более понятными). Разрешения были получены с помощью инструмента icacls.

c:\ BUILTIN\Users:(OI)(CI)(RX) - read + execute permission

c:\bar BUILTIN\Users:(RX) - read + execute permission

c:\foo BUILTIN\Users:(F) - full permission

Образец фрагмента для демонстрации.

public class AccessCheckDemo {
    public static void main(String[] args) throws IOException {
        String[] files = {"c:/foo", "c:/bar"};
        for (String file : files) {
            Path path = Paths.get(file);
            System.out.println("check " + path);
            System.out.println("file      Files.isWritable: "
                    + Files.isWritable(path));
            System.out.println("directory Files.isWritable: "
                    + Files.isWritable(path.getParent()));
            System.out.println();
        }
    }
}

выход

check c:\foo
file      Files.isWritable: true
directory Files.isWritable: false

check c:\bar
file      Files.isWritable: true
directory Files.isWritable: false

Даже у BUILTIN\Users есть full permissions в файле c:\foo, они не могут писать в этот файл, так как разрешение каталога c:\ не разрешает этого (только разрешение на чтение + выполнение для этой группы на c:\).

Как уже упоминал badsamaritan (JDK-7190897), это не работает должным образом в Ява 7.

person SubOptimal    schedule 01.02.2016
comment
Это интересно, я не использовал этот метод, но я уже использую Files.isReadable(), Files.isWritable(), которые, похоже, все равно его используют. Но из приведенного выше примера кажется, что вы говорите, что он может вернуть true для isWritable() для файла, даже если файл может быть недоступен для записи, потому что у вас нет достаточных разрешений в содержащей папке. Я хочу, чтобы он возвращал false, если у вас на самом деле нет разрешений на его изменение, но не имеете разрешения на запись для папки, просто это означает, что вам нужно создавать новые файлы в папке, а не редактировать файлы в папке? Кстати, я уже использую Java 8. - person Paul Taylor; 01.02.2016
comment
@PaulTaylor Вы правы, Files.isReadable() и Files.isWritable() являются оболочкой метода FileSystemProvider.checkAccess(). Если файловая система использует ACL, вам нужно читать их сверху вниз. Используется первая совпадающая запись. Если у пользователя нет разрешения на запись в файл в определенном каталоге, то разрешение на содержащийся файл не проверяется. Это отличается, например, от схемы пользователя/группы/другого Linux по умолчанию. Если у пользователя нет прав на запись в каталог, это означает, что он не может создавать/удалять файлы. Но он по-прежнему может изменять файлы, если разрешение позволяет это. - person SubOptimal; 02.02.2016

любые предложения о том, как я могу программно определить причину сбоя isWritable() (или isReadable())

Может быть, это настоящая ошибка, а не ваша вина:

https://bugs.openjdk.java.net/browse/JDK-8034863

or

http://bugs.java.com/view_bug.do?bug_id=7190897


Говоря о разрешениях Windows, стоит упомянуть, что они действительно немного странные (даже Microsoft это признает): DENY имеет более высокий приоритет, чем ALLOW, поэтому, если вы разрешаете некоторым пользователям читать определенный файл, а также запрещаете всем разрешения, этот запрет «переопределяет ' все и никто (включая владельца) не может прочитать этот файл. Вот почему вы не часто видите разрешение DENY, выбранное в ACL.

person badsamaritan    schedule 01.02.2016
comment
Спасибо, это очень полезный момент о DENY, а вторая ошибка очень интересна, поскольку я не обновлялся до версии java 8 с исправлением ошибок, и это вполне может исправить некоторые проблемы. Но на самом деле мой вопрос был не «почему у меня нет разрешения», а «учитывая набор разрешений Windows, как мне их декодировать, чтобы предоставить эффективные разрешения для пользователя». - person Paul Taylor; 01.02.2016
comment
Хорошо, я думал, что ответил на другую половину вопроса. В любом случае, удачи в этой теме :) - person badsamaritan; 01.02.2016